diff --git a/frontends/desktop/static/app.js b/frontends/desktop/static/app.js index 1bacce26..1b8cc113 100644 --- a/frontends/desktop/static/app.js +++ b/frontends/desktop/static/app.js @@ -461,6 +461,7 @@ const I18N = { 'sys.memoryImported': '记忆已导入', 'err.memoryImport': '导入记忆失败', 'sys.memoryImportBackup': '原记忆已备份至', + 'sys.memorySessions': '会话', 'sys.memoryPickTitle': '选择 GenericAgent 根目录(包含 memory 与 temp 的目录)', 'sys.memoryImportPrompt': '请输入 GenericAgent 根目录的完整路径(包含 memory 与 temp 的目录,而非 memory 文件夹本身):', 'sys.mykeyExported': '模型配置已导出', @@ -639,6 +640,7 @@ const I18N = { 'sys.memoryImported': 'Memory imported', 'err.memoryImport': 'Failed to import memory', 'sys.memoryImportBackup': 'Previous memory backed up to', + 'sys.memorySessions': 'sessions', 'sys.memoryPickTitle': 'Select the GenericAgent root directory (the folder containing memory and temp)', 'sys.memoryImportPrompt': 'Enter the full path of the GenericAgent root directory (the folder containing memory and temp, not the memory folder itself):', 'sys.mykeyExported': 'Model config exported', @@ -1030,7 +1032,11 @@ async function importMemoryFromDir() { if (!res || res.ok === false) throw new Error((res && res.error) || 'import failed'); const detail = `memory: ${res.memoryCopied}, model_responses: ${res.responsesCopied}` + (res.responsesSkipped ? ` (skip ${res.responsesSkipped})` : '') + + `, ${t('sys.memorySessions')}: ${res.sessionsAdded || 0}` + (res.backupDir ? `\n${t('sys.memoryImportBackup')}: ${res.backupDir}` : ''); + if (res.sessionsAdded) { + try { await loadSessions(); } catch (_) {} + } showChanToast(t('sys.memoryImported'), detail, 'ok'); } bindClick('import-memory-btn', async (e) => { diff --git a/frontends/desktop/static/index.html b/frontends/desktop/static/index.html index 9199192a..d38e77eb 100644 --- a/frontends/desktop/static/index.html +++ b/frontends/desktop/static/index.html @@ -678,6 +678,6 @@ - + diff --git a/frontends/desktop_bridge.py b/frontends/desktop_bridge.py index e535846b..a15304be 100644 --- a/frontends/desktop_bridge.py +++ b/frontends/desktop_bridge.py @@ -188,6 +188,49 @@ class AgentManager: except Exception as e: print(f"[bridge] load sessions failed: {e}", file=sys.stderr) + def import_sessions(self, source_dir: str) -> dict: + """把 source_dir/temp/desktop_sessions.json 合并进当前会话列表(按 id 去重)。 + + 既更新内存(立即在侧边栏可见)又落盘。返回新增/跳过计数。 + """ + src = Path(source_dir).expanduser().resolve() + src_file = src / "temp" / "desktop_sessions.json" + if not src_file.is_file(): + return {"sessionsAdded": 0, "sessionsSkipped": 0, "sessionsFileFound": False} + try: + arr = json.loads(src_file.read_text(encoding="utf-8")) + if not isinstance(arr, list): + raise ValueError("desktop_sessions.json is not a list") + except Exception as e: + raise ValueError(f"cannot read desktop_sessions.json: {e}") + added = 0 + skipped = 0 + with self.lock: + for item in arr: + sid = item.get("id") + if not sid or sid in self.sessions: + skipped += 1 + continue + msgs = item.get("messages", []) + sess = Session(id=sid, title=item.get("title", "New chat"), + cwd=item.get("cwd", self.ga_root), + created_at=item.get("created_at", time.time()), + updated_at=item.get("updated_at", time.time()), + messages=msgs, + msg_seq=item.get("msg_seq", 0), + pinned=item.get("pinned", False), + untitled=item.get("untitled", True), + plan_scan_baseline=_load_plan_baseline(item, msgs), + plan_path=_sanitize_desktop_plan_path(sid, item.get("plan_path") or ""), + status="idle", agent=None, + llm_history=item.get("llm_history"), + llm_no=item.get("llm_no")) + self.sessions[sid] = sess + added += 1 + if added: + self._persist() + return {"sessionsAdded": added, "sessionsSkipped": skipped, "sessionsFileFound": True} + def _mykey_file(self) -> Path: p = Path(self.ga_root) / "mykey.py" if not p.exists(): @@ -1812,6 +1855,7 @@ async def memory_import_handler(request): return json_ok({"ok": False, "error": "missing_sourceDir"}, status=400) try: result = _import_memory_from(source_dir, manager.ga_root) + result.update(manager.import_sessions(source_dir)) except Exception as e: return json_ok({"ok": False, "error": str(e)}, status=400) return json_ok(result)