feat(session): cross-platform session migration and team session archive - #593
lurkacai0831 wants to merge 8 commits into
Conversation
- new codebuddy-ide adapter: list/read/write/delete for the IDE sidebar history store (previously write-only via the cli adapter's implicit double-write, so IDE sessions could neither be listed nor migrated) - cli adapter no longer writes/deletes the IDE store; read/write symmetry - detect both on-disk history layouts (default instance stores history one level higher) so its conversations are no longer skipped - reuse 32-hex conversation ids as-is instead of hashing twice, fixing rollback reporting success while leaving sessions in place - encode cli project dirs with CodeBuddy's own rule (spaces kept), so workspaces like 'teamai cli' can be listed and read - resolve symlinks before hashing workspaces (/tmp vs /private/tmp) - shared title cleaning: skip <system-reminder>-style injected first messages instead of leaking prompt text into listings - user-facing errors and test assertions in English
…rectness fixes M1 correctness: - migrate --push re-reads exactly the migrated target session ids instead of 'the N most recent', which could push unrelated sessions - meta no longer lies: fidelityScore uses the real preview score and createdAt records the session's own creation time, not push time - resume failures hint that the session may be archived under another project identity (search --all / --cwd) - empty pushes no longer report success when nothing was committed - 14 Chinese user-facing strings converted to English M2 user-repo capability: - SyncManager: listAllRepoIdentities() reverse-maps canonical identities from each repo's _index.json; listSessionsAcrossRepos() merges entries - list --all / pull --all / search --all give the cross-project view (search --all previously had a dead loop and never searched other repos) - push --all archives every workspace of one platform (--source), confirming before pushing more than 5 sessions (-y skips) - archive key derives from the session's native cwd instead of the directory the command ran in; unknowable workspaces (codebuddy-ide md5 placeholders) archive under _unattributed with a warning - claude-code / codebuddy / cursor readSession recover the native cwd from the first JSONL record instead of lossy directory-name decoding - pushing the same session twice updates the entry instead of creating _1 duplicates (dedup key: origin sessionId + author)
…red fixes Tests (29 new cases): - session-sync.test.ts: identity reverse-mapping, cross-repo listing, dedup by origin sessionId, index rebuild round-trip - session-cmd.test.ts: list/pull/search --all, push --all confirmation, native archive key, English output assertions Docs: - usage-guide (en/zh): new Session Sync & Migration section - README (en/zh): capability row and command cheat-sheet entries - CHANGELOG: M1/M2 entries plus the fixes below Fixes found by the real-CLI E2E run: - encode symlink-resolved cwds into project directory names for claude-code / codebuddy / cursor / workbuddy (writing /tmp/x used to create a directory listing from /private/tmp/x could never see) - keep the local commit and print a warning when the remote push fails instead of crashing after a successful save - apply the shared injected-title cleaning to claude-code / workbuddy / cursor (their first 'user message' is often a system-reminder wrapper, which used to become the archived session name)
Claude Code's /resume picker shows the bare session id (e.g. 824ff784) for sessions without a type:"summary" record, so every migrated session appeared untitled. Carry the IR session title — already cleaned of injected wrappers by the source adapter — into the target JSONL.
…ommands From the three-way QA sweep (adapters / command layer / fidelity): P0 crashes fixed (found by real-CLI execution): - git add/commit/pull in a non-git or missing --repo-root dumped a full stack trace with internal paths; now a one-line error + exit 1 - concurrent pushes hitting git index.lock crashed the same way - pull with no origin remote / nonexistent repo root reported a misleading ENOENT instead of the actual cause Correctness: - session archive dedup key now includes platform: a session pushed as codebuddy and re-archived after migrating to claude-code are two artifacts, not an update of each other - codex keeps per-message timestamps (read response_item.timestamp, stamp records with the message's own time) — roundtrips no longer collapse the timeline - codex session lookup matches whole ids; a 4-char prefix could resolve to someone else's session file - cursor writeSession is idempotent again (malformed UUID regex minted a new id per write, piling up copies) - claude-code readSession honors the type:"summary" record it writes - workbuddy/claude-code/cursor titles skip injected ai-title/name wrappers and tool-output snippets; extractMeta no longer stops at the first injected block (real question after a system-reminder wrapper becomes the title) - codex writeSession survives an invalid session.createdAt instead of crashing with RangeError - a corrupted sessions/**/_index.json warns with the rebuild command instead of silently emptying the dedup key Robustness: - interactive prompts treat EOF like "n" (Cancelled., exit 0) instead of a silent success; --limit rejects non-positive values; a closed output pipe exits cleanly instead of an EPIPE stack - remote push failures report git's actual fatal line, keeping the local commit Docs: - design doc gains a Known limitations section (fidelityScore is a proxy metric; codex splitting; sessionId is platform-native; flattenDag) - src/__tests__/fidelity-sweep.test.ts joins the suite as the fidelity regression tool (roundtrip matrix over 5 platform routes)
|
Reviewed the full branch (type-check + 50/50 tests green, built, smoke-tested, merges cleanly onto 1. Delete 2. 3. Archived sessions are unscrubbed — needs gating before merge. Unlike |
Closes #587
Adds two layers to
teamai session: cross-platform session migration (move a full conversation between AI tools, previewable and undoable) and a team session archive (archive into the team repo, search, restore).What ships
Migration
session platformssession migrate <id> -s <src> -t <dst>--allfor the recent fewsession rollback <id> --platform <dst>Team archive
session push --source <agent>--allcovers every workspace of that agentsession pullsession list/list --all--alladds a SOURCE column)session search <kw> --allsession resume <name> --platform <agent>Platforms:
claude-code,codebuddy(CLI),codebuddy-ide(IDE sidebar),codex,cursor,workbuddy, plus theclaude-internal/tclaude/codex-internal/tcodexvariants.Design notes
sessions/repos/<repo>/<author>/is keyed on the git identity of the session's own working directory (recovered from the JSONL record where needed); unknowable workspaces (codebuddy-ide md5 placeholders) land in_unattributedwith an English warning. Runningpushfrom another directory still archives under the right project.rollback, nobody dares migrate anything real.--all— a project team repo shows only its own sessions; a personal repo gets the cross-project view.session save(scrubbed summary → digest) rather than replacing it: summary for trends, full session for resuming.Known limitations are documented in
docs/designs/session-user-repo-sync.md— notably thatfidelityScoreis a proxy for IR-block degradation, not a byte-equality guarantee.Test plan (all executed against the built CLI)
npx tsc --noEmit→ 0 errors ✅npx vitest run src/__tests__/session-{sync,cmd}.test.ts src/__tests__/{codebuddy-ide-adapter,session-title,fidelity-sweep}.test.ts→ 50/50 passed ✅npm run build→ success (ESM 1.72 MB) ✅session platforms→ all 6 platforms listed,codebuddy+codebuddy-ideboth✓ installed✅Fidelity: 100.0%, target id printed;claude --resume <id>picks it up with the session title ✅session rollback→ only the target copy removed, source session intact ✅sessions/repos/github.com_org_beta/andsessions/repos/gitlab.com_team_alpha/✅_unattributedwith an English warning ✅session list --all→ both projects listed with their SOURCE identity;session listfrom a project shows only that project's ✅session search <kw> --all→ hits sessions across two repo identities ✅xxx_1duplicate ✅session resumefrom the wrong project → English error namingsearch --all/--cwd, exit 1 ✅session pushinto a non-git repo root / no remote / concurrentindex.lock→ one-line English error + exit 1, no stack trace ✅npx vitest run→ failed files identical to the pre-change baseline (hook-handlers,dashboard-collector,recall-scope-isolation,contribute-self-learnings,push-team-config); none of them importsession-flow✅fidelity-sweep.test.tsroundtrips 5 platform routes and asserts message count/role/text/thinking/tool pairing/timestamps ✅Evidence: a real session relay — CodeBuddy IDE → claude-code (2661 messages / 47.5 MB)
The screenshots below are one continuous relay, all taken from the actual run. They live on the
session-migration-evidencebranch of this fork (kept out of the PR diff).Step 1 — the session lives in CodeBuddy IDE
Everyday work happens in the IDE. The migration does not require leaving it: the in-IDE agent provides and runs the exact commands, from enabling the new CLI to
migrate/rollback/--allvariants.Step 2 — run the migration
Fidelity: 100.0% (Mode A)andPreserved: 5816/5816 blocks.tool_not_in_targetwarnings are expected: this session used CodeBuddy-specific tools (team_create,send_message,ask_followup_question, …) with no same-named counterpart in claude-code. Their inputs and results are preserved as text blocks — readable after resume, but not replayable as tool calls.~/.claude/projects/-Users-...-teamai-cli/<uuid>.jsonl).Step 3 — resume in Claude Code: the relay completes
Claude Code's
/resumepicker, searched for "teamai cli": the migrated session appears under its original title ("完整的分析一下 seeeionflow ts版本的能力和 tea · 1 minute ago · 21.2 MB"). Selecting it continues the conversation with the full history visible — this is the relay completing. Before thetype:"summary"record fix, this picker showed a bare session id (824ff784).Same session, before and after
/resume)conversations[].nametype:"summary"recordNotes on the numbers:
extra/requestsmetadata; Claude records are flatter).tool_not_in_targetwarnings degrade CodeBuddy-only tools to text blocks: content preserved, tool structure not replayable.Notes for reviewers
origin/main(8ea0612) — rebased, CHANGELOG conflicts resolved by keeping both sides.--all, (3) the learnings loop.session save), so anything archived is team-readable. If that needs redaction or an opt-in gate, say so and I will add it before this lands.