Skip to content

feat(session): safe session reads and copies for workflows, drivers, scorers, and benchmarks (#593 follow-up) - #1135

Merged
jeff-r2026 merged 25 commits into
Tencent:mainfrom
SaulMoro:feat/agent-sessions-593
Oct 15, 2026
Merged

jeff-r2026 merged 25 commits into
Tencent:mainfrom
SaulMoro:feat/agent-sessions-593

Conversation

@SaulMoro

@SaulMoro SaulMoro commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Follow-up to #593: fixes its review findings and gives every caller that reads or copies a member's agent session one safe way to do it. Related: #1080 (session continuation), #1085 (scorers), #1133 (multi-machine index conflicts, not covered here).

Summary

One module, AgentSession, sits in front of the session adapters. session migrate, session push --all, and later the workflow local driver go through it.

AgentSession
  platformForTool(tool)                      'claude' → 'claude-code', 'opencode' → 'opencode'
  list({ platforms?, cwd?, updatedSince? })  → { sessions: refs find reads as listed, failures per tool }
  find(ref, { scrub = true })                → session | no_adapter | tool_unavailable | not_found | unreadable
  copy({ from, to: { platform, cwd } })      → { sessionId, reused, fidelity } | failure
  transcript(session)                        → one entry per message, tool calls and results included

Who calls it:

Consumer Uses In this PR
session migrate, session push --all (members) list, find, copy yes
Workflow local driver (#1080): continue a step's session in another tool copy, platformForTool no, next
External driver without its own session transfer teamai session migrate --json yes
Scorer inside a workflow step (#1085): grade the session the judged step left find (redacted by default), transcript read side only
Scorer outside workflows: grade a sample of the team's finished sessions, whatever tool ran them list, find, transcript read side only
Benchmark: turn a past session into a task (first prompt, workspace, starting commit), replay it on other tools and models, grade each run list, find, transcript, git state at start read side only; the runs are workflow steps, no runner here
Dashboard session view list, find, transcript no

transcript gives one text shape for every tool, with tool results apart from what the member and the agent wrote, for a judge's prompt. A scorer that counts tool calls reads them from session.messages, which has the same shape for every tool; tokens, interruptions, and corrections are already in the dashboard events under the same session id.

A session file records its workspace and time but not the commit it started from (Claude Code records only the branch), and that cannot be rebuilt later. The session start hook now records the commit and whether tracked files had changes, so a benchmark can replay a session on the code it ran against.

A copy never overwrites:

 copy(from, to)
-  target exists → overwrite (Cursor deletes every bubble, CodeBuddy IDE every message file)
+  target or a legacy copy exists → { sessionId, reused: true }, nothing written
 deriveTargetSessionId
-  reuses a UUID source id; cwd only for global-key stores; realpathSync
+  always derived; cwd for every platform; realpathSync.native
 rollback
-  deleteSession(id) in every workspace
+  only what this call created, in the target workspace

Changes, one commit each:

  • Every target id is derived from the target workspace
  • Copy a session once and report the copy that already exists
  • session migrate --json with tagged failures
  • Read the session the caller listed
  • Serialize writes to the team repo session index
  • Cursor keeps the images a member pasted
  • Search finds text in tool output
  • Migrate a session from OpenCode
  • Migrate a session to OpenCode
  • Codex and OpenCode on Windows
  • List the sessions of every tool through AgentSession
  • Record the git state a session started from
  • Session stores follow a relocated Claude Code or Codex root
  • List a Claude Code session under the workspace it recorded (the listing decoded the folder name, which fails on -, _, . or a space)
  • CI runs the OpenCode 2 E2Es: session migrate into OpenCode, and the OpenCode 2 case of the hooks E2E, which was opt-in before (pinned @opencode/cli 2.0.29 checked against its sha256)
  • A Cursor copy whose transcript was deleted but whose conversation is still in Cursor's store counts as existing, and session rollback removes it from the store
  • A session whose first lines do not parse is listed and reported unreadable, not not_found, and is dated by its file rather than now
  • On Windows, session migrate -t cursor|workbuddy is refused once, before anything is listed
  • Two commands copying one session into the same workspace and tool write it once: the check, the write and the rollback hold a lock on that copy; a copy still blocked after ten seconds fails with copy_busy
  • An archived Codex copy is read back and rolled back, as it is reused
  • Search indexes each tool result whole; the transcript a scorer reads keeps its 2,000-character cut
  • A batch over sessions finishes the sessions it can read:
migrate --all, its --push archive, push --all
  a session cannot be read or copied     → name it, count it, go on with the rest; exit 1
  the archive write fails or is locked   → stop before the commit, so no half-written entry is published; exit 1

The workflow engine does not change, and AgentSession is not part of the driver contract. A driver can keep and restore sessions its own way: a host that runs several agents, such as T3 Code or Paseo, may store conversations and move them between its agents with its own mechanism. The driver that runs a step picks how a session continues, in this order:

  1. the same tool on the same machine: resume by id;
  2. the driver's own mechanism, when it has one (preferred over TeamAI's copy);
  3. AgentSession.copy: what the local driver does, or teamai session migrate --json for what a driver's own mechanism does not cover;
  4. a fresh session.

Whatever the route, the driver reports the tool and its native session id; TeamAI needs nothing else. AgentSession reads only each tool's own session store, so a conversation that lives only in a driver's store is invisible to it; a driver with its own handle reports that separately. Importing such a conversation (as the session format the team repo archive already stores) is left for when a driver needs it. The note on #1080 has the rule.

Evidence

End to end, built at 104fec6. A Claude Code session started from T3 Code, found through its thread's resume id, is copied to Codex in another workspace. The run uses an isolated HOME holding a copy of that session file, so the real ~/.claude and ~/.codex are untouched.

$ teamai session migrate 073c1c92-… -s claude-code -t codex --cwd <teamai-cli checkout> --target-cwd $HOME/work/target --json
{"data":{"sessionId":"7f073b61-a1a1-7e78-930b-d96135fd0f60","cwd":"…/work/target","reused":false,
 "fidelity":{"score":1,"mode":"A","totalMessages":3,…},"filePath":"$HOME/.codex/sessions/2026/09/15/rollout-…-7f073b61-….jsonl"}}   exit 0
$ teamai session migrate <same arguments> --json
{"data":{"sessionId":"7f073b61-a1a1-7e78-930b-d96135fd0f60","cwd":"…/work/target","reused":true,…}}   exit 0
$ cd $HOME/work/target && codex exec resume 7f073b61-… "Answer in one line: which pull request number did I mention earlier in this conversation?"
Mencionaste el PR #567.

The source session asked about #567, so Codex answers from the copied context.

Before and after, per change. Every After cell is a real-CLI run. Before is main: run for the index lock, read from the code for the other rows. The concurrent copy, Codex archive and build-log rows fix gaps in this branch, so their Before is this branch before the fix: read from the code for the concurrent copy, run for the other two.

Change Before (main) After
Serialize index writes two concurrent session push into one team clone lost a session 6 times out of 6 both sessions kept 7 times out of 7, no lock files in the team repo
Copy once a second migrate rewrote the copy, losing a turn added there "already migrated as …, nothing written"; the later turn stays
Search tool output text that only appears in a tool result is not found found; ranking title 2.6 > conversation 0.7 > tool output 0.2
Error at the end of a 10 KB build log, session search ZZTOP this branch before the fix: "No results found." found, Score: 0.029 (one decimal printed 0.0 before), the snippet ends error TS2304: Cannot find name ZZTOP
Relocated tool root a copy into Claude Code landed in ~/.claude/projects, and claude --resume with CLAUDE_CONFIG_DIR said "No conversation found" the copy lands under the recorded root and claude --resume finds it; session rollback removes it
Migrate into OpenCode not supported the copy shows in opencode session list, a repeat is reused, and opencode run --session recalls a keyword from it (OpenCode 2.0.29)
OpenCode CLI missing or OpenCode 1 — --json prints tool_unavailable, exit 1, nothing written
Cursor copy left only in Cursor's store (transcript deleted) a second migrate wrote over it reused: true, state.vscdb byte-identical; session rollback removes the store rows and the next migrate writes a fresh copy
Claude Code session whose first lines do not parse migrate --json: not_found unreadable, with the parse error, exit 1
Two session migrate of one session into Codex at once both could write; a failing one's rollback could delete the other's copy one writes, the other prints "already migrated", 1 rollout; with the lock held for 60 s, text and --json exit 1 with copy_busy and nothing written
Three Claude Code sessions, the middle one damaged: migrate --all -t codebuddy, then push --all this branch before the fix, read from the code: migrate stopped at the damaged one and copied nothing after it; push committed nothing 2 session(s) migrated, 1 failed., exit 1 (--dry-run the same); push commits the other two and prints 1 session(s) could not be read and were not archived., exit 1
Codex copy moved to archived_sessions, then migrate --push and session rollback "already migrated", then Codex session not found, exit 1; rollback "Session not found" pushed (exit 0); rollback removes the archived rollout

Checks at 114dc5d (main merged in again at 01e9549, bringing #1141 and #1117): tsc and lint pass. The unit suite and the e2e suite (run at 01e9549; 114dc5d only adds a unit test) pass, with three exceptions: files that hit spawn git EAGAIN or time out on a loaded machine, which pass when rerun; gitlab-clone-realspawn, which also times out locally on main; and source-freshness-recall (/private/var vs /var on macOS, fails on main too). Every _index.json write still goes through writeIndex, which leaves an unchanged index byte-identical (#1141), and every command that reaches it holds the session index lock; a new test runs session pull and session pull --all twice through that lock and checks the second leaves _index.json byte-identical. Real-CLI run at 114dc5d with an isolated HOME: session platforms lists Codex as installed with only ~/.codex present; session push --all of three Claude Code sessions, the middle one damaged, archives and pushes the other two and exits 1; two session pull --all runs leave _index.json with the same SHA-256 and the team checkout clean. At 55df15f: 62 session, dashboard and hook test files (1453 tests) pass with a TMPDIR that contains _ and a space, as the macOS runners have. commands-reference passes, and the OpenCode migrate E2E passes against OpenCode 2.0.29.

Not verified:

  • Windows (no Windows CI).
  • The real Cursor, WorkBuddy, and CodeBuddy clients: their adapters are covered by unit tests only.
  • codex exec resume under a relocated CODEX_HOME: only a stand-in CLI was used.
  • The gitlab and github providers: left to CI.

Merge Danger

Door: two-way

Re-running session migrate no longer overwrites the target; it reports the existing copy. Copies made before this change are recognized under their old ids.

Naming a source tool that is not installed (session migrate -s codex, session push --source codex --all) now fails with tool_unavailable and exit 1, where it printed "No sessions found" and exited 0.

Blast Radius: sessions

session migrate, session push, session search, every session adapter, and the session start hook (one git call per session start). Windows is not verified: there is no Windows CI.

Cursor has no image storage. The transcript and composer writers now write the shared image placeholder for a pasted image instead of dropping it, so an image-only user message still appears and the written session matches the fidelity report.
Two session pushes at once could both pick the same free archive name and
drop one session and its index entry. push, migrate --push and pull now
hold a lock per team repo archive around each save and each index rebuild.
The lock lives under ~/.teamai/locks, never inside the team repo.
A migrated session never reuses the source id in the target tool. Reusing
a source UUID wrote the copy over whatever already held that id, and
Claude Code resumes by id across project directories, so the target
workspace is now part of every platform's id, Claude Code and CodeBuddy
included. The workspace is hashed as its native real path, so /tmp/x and
/private/tmp/x, or two letter cases on macOS, name one target. A target
workspace that is not absolute is refused before anything is written.

Ids already derived for correctly spelled workspaces on Cursor, Codex,
WorkBuddy and CodeBuddy IDE do not change. Copies from earlier releases
of a UUID source, or Claude Code and CodeBuddy copies, carry other ids.
session migrate now goes through AgentSession.copy. Before writing, copy
looks the target up by id across the target tool's storage, including ids
an older TeamAI wrote in that workspace. When a copy exists, readable or
not, it prints 'already migrated as <id>' and writes nothing, so turns
added to the copy survive. session resume runs the same check and prints
'already restored as <id>'. A failed write removes only the target that
call created, in the target workspace. JSONL transcripts are written
through a temp file and a rename.
AgentSession.transcript renders a session as one entry per message:
`text` holds what the member and the agent wrote, with tool calls and
their input as JSON, and `toolOutput` holds the tool results, each
truncated to 2,000 characters. Session search indexes it in place of
its own extractor, so a query finds an error a command printed, and
nested tool input no longer indexes as "[object Object]".

Search scores title, conversation and tool output as separate BM25
fields weighted 3x, 1x and 0.1x, so a session that names the query in
its title or conversation ranks above one that only repeats it in a
long tool dump. Snippets are collapsed to one line.
session migrate --json prints a single { data } | { error } line on stdout,
the shape the workflow commands use, and sends progress to stderr. data
carries the target session id, its workspace, reused, and the fidelity
report. Expected failures keep their names (no_adapter, tool_unavailable,
not_found, unreadable, invalid_workspace, write_failed); a held archive
lock is busy. --json never prompts: a missing id, source or target fails
with prompt_refused, --push needs -y or --scrub, and --all is refused.
Adapters read by { sessionId, projectPath?, filePath? }. Callers that enumerate pass the file they listed, so push --all no longer archives one copy twice when an id lives in two workspaces. Codex honors the workspace and returns the full id for a suffix; Cursor and WorkBuddy fall back to the id. AgentSession.find redacts by default; push and migrate --push pass their --scrub choice. The migrate preview reads the source through AgentSession.find, so --json prints not_found or unreadable instead of unexpected_error.
@SaulMoro SaulMoro changed the title feat(session): safe agent session copies for workflows and drivers (#593 follow-up) feat(session): safe session reads and copies for workflows, drivers, scorers, and benchmarks (#593 follow-up) Oct 14, 2026
…here

Codex is found with probeBinary and run through cross-spawn, so the npm
codex.cmd shim on Windows is found and started. findSqlite3 looks for
sqlite3.exe on Windows, and Cursor uses the same lookup.

On Windows, Cursor and WorkBuddy list a copy only after the sqlite3 CLI
registers it, so migrate and resume refuse them as targets with
no_adapter and reason "unsupported on win32" instead of writing a copy
the tool never shows. The no_adapter message now has one home.

A Codex rollback that cannot find sqlite3 while Codex's databases exist
now fails and keeps the rollout, instead of reporting success and
leaving the thread listed.

Workspace ids treat c:\ and C:\, and / and \, as one Windows path.
Windows is not verified: there is no Windows CI.
…rted on

A session_start event in a git work tree now carries gitHead and gitDirty (tracked changes only; untracked files are not listed). Each start records its own state; collectSession reports the earliest as gitAtStart. Outside git, with no commits, with no cwd, or when git fails or takes over 2 s, both fields are absent and the event is still written. The monthly log and session save --push are unchanged.
OpenCode becomes a source platform through its CLI: the adapter lists
sessions with `opencode session list --format json`, reads one with
`opencode session export`, and deletes with `opencode session delete`,
always with `--standalone` so no background service is left running. The
CLI is found with probeBinary and run through cross-spawn; a missing CLI or
OpenCode 1 (no `session export`) is reported as tool_unavailable by
AgentSession.find and by `session migrate --json`.

An export carries user text and inline image files, and assistant text,
reasoning and tool parts with their input and result. Tool calls and their
results become separate turns, as Claude Code stores them, so search and
the copy see tool output. OpenCode joins the fidelity tables (thinking,
images, its tool names).

AgentSession.platformForTool maps a workflow tool name to its session
platform (`claude` to claude-code, `opencode` to opencode).

Writing OpenCode sessions is not supported yet.
AgentSession.list({ platforms?, cwd?, updatedSince? }) returns every listed
session as a ref find reads as is, newest first, plus one failure per tool
that could not be listed (no_adapter, tool_unavailable, or the new
list_failed), so one broken tool does not hide the others. session migrate
and session push list through it, and migrate --json reports list_failed in
its envelope.

OpenCode's CLI lists one workspace at a time, so listing it without a
workspace is list_failed instead of the current directory passed off as all
of them, and its listed sessions carry no message count or size rather than 0.
A named source that is not installed now fails with tool_unavailable instead
of "No sessions found".
OpenCode becomes a target platform: the adapter writes the session in the
shape `opencode session export` prints and imports it with
`opencode session import --directory <workspace>`, through the same probed
CLI and `--standalone` as its reads.

OpenCode orders sessions and messages by the time encoded in their ids and
refuses an id it already has, so copies keep its id shape: `ses_` plus the
time part of an OpenCode source, or the source's creation time encoded the
way OpenCode 2 encodes it, plus a suffix derived from the target workspace
and the source id. Every message gets a new `msg_` id that sorts in message
order, so two copies of one source never share one. Tool results go back
into the tool part of the call they answer, and assistant messages carry
`time.completed`, without which OpenCode drops them on import.

A copy into a second workspace imports, and a repeat into the same
workspace is reused. OpenCode refuses a workspace that does not exist, so
such a copy fails with the path instead of writing.
The listing took the workspace from the project directory name, which decodes only when the path has no '-', '_', '.' or space; otherwise it returned the encoded name. It now takes the absolute cwd the session's records carry, as reading the session already does, and falls back to the decoded name.
npm's opencode-ai is OpenCode 1, so the E2E that migrates a session into OpenCode, and the OpenCode 2 case of the hooks E2E, skipped in CI. Both full-suite E2E jobs now fetch the @opencode/cli-linux-x64 2.0.29 tarball from the npm registry, check it against a pinned sha256, put its bin on PATH and set TEAMAI_OPENCODE_V2_BIN to it; no install script runs. Under CI the migrate test now fails instead of skipping when no OpenCode 2 is on PATH.
A member who relocated Claude Code (CLAUDE_CONFIG_DIR) or Codex
(CODEX_HOME) and recorded it with `teamai init` had their sessions
looked up in ~/.claude/projects and ~/.codex/sessions: sources were not
found, and copies landed where `claude --resume` never looks.

AgentSession, `session resume`, `session rollback` and
`session platforms` now resolve the recorded `toolRoots` for the
workspace at hand and pass them through the adapter registry to the
store paths. A run over many sessions (migrate, push) resolves each
workspace once. Nothing recorded keeps the default paths; the variants
keep their fixed ones. The Codex CLI that indexes a copy gets CODEX_HOME
set to the recorded root, as it already did for the default one.

Looking up the recorded roots is a pure read: it never migrates or rewrites the member's config, also under --dry-run.
- session migrate into OpenCode reports tool_unavailable, not
  unexpected_error, when the opencode CLI is missing or is OpenCode 1.
- An OpenCode export that fails for another reason than "Session not
  found" is tool_unavailable (existence unknown, nothing written), not a
  reused copy.
- OpenCode listings keep only the workspace asked for: inside a git repo
  its CLI lists every directory of the repo.
- AgentSession.copy and session resume write through one writeCopyOnce
  path, so a restore whose write fails is rolled back and a retry writes.
- session_start events carry gitAtStart (HeadState from utils/git)
  instead of gitHead and gitDirty; SessionSummary.gitAtStart uses it.
- AgentSessionListed derives from SessionMeta, the OpenCode tool error
  is parsed with zod, and noAdapter takes the OS as a parameter.
- The session contract suites take their platforms from the adapter
  registry; OpenCode joins the find suite.
- Docs: who picks how a session continues, tool_unavailable covers the
  source and the target, and the pinned OpenCode 2 in the CI E2E jobs.
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Make the existence check and copy write atomic — src/session-flow/agent-session.ts:267
    Two concurrent migrations of the same source into the same workspace can both find no target and enter writeSession. The later write overwrites the first copy (writeJsonl renames over the destination; Cursor/CodeBuddy IDE replace stored messages). If either write fails, its cleanup can also delete the other call’s successful copy. Serialize the check/write/cleanup for each target, or use exclusive creation with ownership-aware cleanup.

  • [P2 non-blocking] Handle archived Codex copies consistently when reporting reuse — src/session-flow/adapters/codex.ts:602
    existingSessionId now recognizes rollouts in archived_sessions, but readSession and deleteSession still search only the active sessions directory. After archiving a migrated Codex copy, repeating migrate --push reports reuse and then fails to read it for the archive. The suggested session rollback also reports “not found,” so it cannot refresh that copy. Read/delete the archived rollout too, or restore it before returning reuse.

Testing: The PR description provides sufficient real-CLI evidence at 55df15f4, including migration, repeat-copy reuse, and Codex continuation. No additional provider/agent matrix is required. I did not run, build, or install PR code.

…ows refusal

- Cursor: a composer whose transcript was deleted but whose rows remain in
  Cursor's store now counts as existing, so a copy is reused instead of
  rewriting its bubbles. A store that cannot be read fails the copy as
  tool_unavailable.
- Claude Code, CodeBuddy, Cursor and WorkBuddy list a session whose first
  lines do not parse, under a placeholder title, so `session migrate <id>`
  reports unreadable instead of not_found.
- `session migrate` refuses a target it cannot write once, before listing or
  previewing anything (text and --json).
- Pin that the Cursor adapter keeps raw reasoning as <thinking> text in the
  transcript and out of the bubbles, and fix the comment that said otherwise.
…-only Cursor copies

- Claude Code and CodeBuddy date a listed session with no readable timestamp
  by its file's mtime instead of now, so a damaged session does not sort as
  the newest one.
- Cursor's deleteSession removes a composer that is only in Cursor's store
  (in that workspace when one is given), so the rollback a reused copy points
  to works and the next migrate writes a fresh copy.
Two commands copying the same session into the same workspace could both
see no copy and both write it, and a failing one's rollback could delete
the other's good copy. writeCopyOnce now runs the check, the write and the
rollback under a lock on that one copy, keyed by the target platform and
the derived id, under ~/.teamai/locks/. A second command waits and then
reuses the first copy; one still blocked after ten seconds fails with
copy_busy and says to run again when the other command finishes.

Codex reuse already counted rollouts in archived_sessions, but reading and
deleting looked only in sessions/, so migrate --push failed to read an
archived copy back and rollback reported it not found. The read and delete
lookup now searches archived rollouts too, with the same workspace scoping.
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Make the existence check, write, and cleanup atomic — src/session-flow/agent-session.ts:267
    The earlier finding remains unresolved. Two concurrent migrations of the same source into the same workspace can both observe no target and write it. The later write overwrites the first; a failed call’s cleanup can also delete the other call’s successful copy. Serialize the operation per target across processes, or use exclusive creation with ownership-aware cleanup.

  • [P2 non-blocking] Read and delete archived Codex copies when reporting reuse — src/session-flow/adapters/codex.ts:602
    The earlier finding remains unresolved. existingSessionId recognizes archived_sessions, but read/delete lookup still searches active sessions only. After archiving a migrated copy, repeating migrate --push reports reuse but cannot read it for the archive; the suggested rollback cannot remove it either. Include archived rollouts in read/delete lookup.

  • [P2 non-blocking] Index complete tool output rather than the truncated transcript — src/session-flow/agent-session.ts:418
    Search now indexes AgentSession.transcript, which discards everything after the first 2,000 characters of each tool result. An error appearing near the end of a build log is therefore unsearchable unless repeated elsewhere. Keep truncation for scorer prompts/display, but index the complete output.

Testing: The description provides sufficient real-CLI evidence, including migration, repeat-copy reuse, and Codex continuation. Its checks name the older commit 55df15f4; that is a note, not a blocker. No additional provider/agent matrix is required. I only read the scoped diff; I did not run, build, or install PR code.

@github-actions

Copy link
Copy Markdown
  • [P2 non-blocking] Index complete tool output, not the truncated transcript — src/session-flow/agent-session.ts:471
    The earlier finding remains unresolved. Search indexes AgentSession.transcript, which retains only the first 2,000 characters of each tool result. An error occurring later in a build log remains unsearchable unless repeated elsewhere. Keep truncation for scorer prompts/display, but index the complete output.

Resolved: The per-target lock now covers the existence check, write, and cleanup. Codex read/delete lookup now includes archived rollouts. Neither earlier finding is raised again.

Testing: The PR description supplies sufficient real-CLI evidence, including migration, repeat-copy reuse, and Codex continuation. The record names the older commit 55df15f4; that is a note, not a blocker. No additional provider/agent matrix is required.

I only read the scoped diff; I did not run, build, or install PR code.

AgentSession.transcript keeps the first 2,000 characters of each tool
result, which suits a scorer's prompt. Search indexed that transcript, so
an error at the end of a long build log could not be found. Search now
indexes each message's full tool output; the transcript stays truncated.
@github-actions

Copy link
Copy Markdown

No new findings I’m confident are real in the scoped diff.

Resolved: All three earlier findings are fixed: the copy lock covers check/write/cleanup, Codex read/delete includes archived rollouts, and search indexes complete tool output rather than the truncated transcript.

Testing: The PR description documents sufficient built, real-CLI verification, including migration, repeat-copy reuse, and Codex continuation. The record names older commit 55df15f4; that is a note, not a blocker. No additional provider × agent matrix is required.

I only read the scoped diff and trusted base review criteria; I did not run, build, or install PR code.

A session that matches only in tool output scores well under 0.1, so
`session search` printed it as `Score: 0.0`. It now prints two
significant digits, for example 0.029 or 2.6.
@github-actions

Copy link
Copy Markdown
  • [P2 non-blocking] Continue batch migration after an unreadable session — src/session-flow/session-cmd.ts:678
    Damaged transcripts are now included in listings, but a failed AgentSession.find throws out of the entire migration loop. With migrate --all, one damaged session prevents all later healthy sessions from being copied. Report the failure, increment failed, and continue, preserving the nonzero exit status.

Resolved: All three earlier findings remain fixed: the copy lock covers check/write/cleanup, Codex read/delete includes archived rollouts, and search indexes complete tool output.

Testing: The description supplies sufficient built, real-CLI evidence, including migration, repeat-copy reuse, and Codex continuation. The end-to-end record names an older commit; that is not blocking. No additional provider × agent matrix is required.

I only read the scoped diff and trusted base criteria; I did not run, build, or install PR code.

migrate --all, its --push archive, and push --all stopped at the first
session they could not read. Each now names it, counts it, carries on
with the others, and exits 1. A failed archive write still stops before
the commit, so no half-written entry is published.
@github-actions

Copy link
Copy Markdown

No new findings I’m confident are real in the scoped diff.

Resolved: All earlier findings are fixed: copy locking covers check/write/cleanup, Codex read/delete includes archived rollouts, search indexes complete tool output, and batch migration skips unreadable sessions while continuing with healthy ones and exiting nonzero.

Testing: The PR description supplies sufficient built, real-CLI evidence, including migration, repeat-copy reuse, Codex continuation, and damaged-session batch verification at a810a8cb. No additional provider × agent matrix is required.

I only read the scoped diff and trusted base review criteria; I did not run, build, or install PR code.

@github-actions

Copy link
Copy Markdown

No new findings I’m confident are real in the scoped diff.

Resolved: All earlier findings remain fixed: copy locking covers check/write/cleanup, Codex read/delete includes archived rollouts, search indexes complete tool output, and batch migration continues past unreadable sessions with a nonzero exit status.

Testing: The description documents sufficient built, real-CLI verification, including migration, repeat-copy reuse, Codex continuation, and damaged-session batch verification at 4de75f58. No additional provider × agent matrix is required.

I only read the scoped diff and trusted base review criteria; I did not run, build, or install PR code.

@github-actions

Copy link
Copy Markdown

No new findings I’m confident are real in the scoped diff.

Resolved: All earlier findings remain fixed: copy locking covers check/write/cleanup, Codex read/delete includes archived rollouts, search indexes complete tool output, and batch migration continues past unreadable sessions with a nonzero exit status.

Testing: The PR description documents sufficient built, real-CLI verification, including checks at 114dc5d2. The explicitly deferred provider and client coverage is not blocking; no full provider × agent matrix is required.

I only read the scoped diff and trusted base review criteria. I did not run, build, or install PR code.

@jeff-r2026
jeff-r2026 merged commit 0fff828 into Tencent:main Oct 15, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants