feat(sdk,kernel): f.agent resume continues a recorded CLI session
This is the session-resume piece of the Babysitter owner plan, approved by the lead as "the heart of Khaliq's knows the original intent ask". A fixer woken again on the same PR, in the same reused sandbox (cloud#4356 + flows#635), continues its previous agent session instead of starting cold. The feature itself is generic f.agent + kernel; Babysitter adoption is a follow-up.
API
const first = await f.agent("fix", { task });
await f.agent("fix-again", { task: next, resume: first.sessionId });
AgentResult.sessionId: the session the CLI ran as (Claude's session id, Codex'sthread.startedid).- It's read from the journaled transcript evidence (
trajectory_tail.transcript.result.session_id), so it is also reported for agents that answer with a JSON object. - It's only offered when it passes the resume rule, so a bounded or truncated label never is.
- It's read from the journaled transcript evidence (
resume(alsoresume:on a YAML agent step):- Claude runs
-p --resume <id>; Codex runsexec resume … <id>with the same flags. - A resume to any other CLI, or over
transport: "relay", is refused rather than run cold.
- Claude runs
- Validation: one lexical rule, identical in TS and Rust and tested against
testdata/agent-resume-cases.json: 1–200 of[A-Za-z0-9._:-], starting with a letter or digit, so an id can never be parsed as a CLI flag. - Kernel:
resumejoins the closedSTEP_AGENT_FIELDS;- its shape is checked before serde (
null/number refused, not read as absence); - a malformed id is
InvalidAgentResume; skip_serializing_if, so every existing spec keeps its bytes and hash.
Checked against the real CLIs first (evidence/agent-resume/05-real-cli-resume.txt)
# Missing session, real Claude CLI (2.x on this host):
$ claude -p --resume 00000000-0000-4000-8000-0000000000aa "hi" --output-format json
No conversation found with session ID: 00000000-0000-4000-8000-0000000000aa
exit=1
# Resume from a different directory than the session was created in:
$ (cd A && claude -p "Remember the code word PELICAN-42. Reply only with OK." --output-format json)
A session: 1908508f-989e-4159-82b0-6a676c3d74d3 result: OK
$ (cd B && claude -p --resume 1908508f-989e-4159-82b0-6a676c3d74d3 "What was the code word? Reply with just it." --output-format json)
B session: 1908508f-989e-4159-82b0-6a676c3d74d3 result: 'PELICAN-42' error: False
Cloud's bootstrap does not override CLAUDE_CONFIG_DIR/CODEX_HOME, so sessions live under $HOME and survive in a reused box.
Gate edits (please review)
tests/spec-parity.test.ts:agent-resumeis added to the fixture list. It extends the gate and loosens nothing. It caught the YAML path silently dropping the field, which is fixed.packages/schema/flows.schema.jsonis regenerated byscripts/generate-json-schema.mjs, asschema-publish.ymlrequires.
Evidence (evidence/agent-resume/, unedited)
Red, with packages/sdk/src and packages/surface/src at main via git stash:
# packages/sdk/src and packages/surface/src at origin/main (22cd1836) via git stash; tests and testdata from this branch.
$ (cd packages/sdk && npx vitest run tests/agent-resume.test.ts tests/spec-parity.test.ts)
× spec parity: one dialect at the SDK<->kernel boundary > compiles agent-resume to the pinned canonical JSON 5ms
× spec parity: one dialect at the SDK<->kernel boundary > hashes agent-resume to the pinned spec_hash 1ms
× spec parity: one dialect at the SDK<->kernel boundary > round-trips agent-resume across the kernel dialect 1ms
Error: Failed to load url ../src/agent-resume.js (resolved id: ../src/agent-resume.js) in /home/khaliqgant/Projects/AgentWorkforce/flows-worktrees/agent-resume/packages/sdk/tests/agent-resume.test.ts. Does the file exist?
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 3 ⎯⎯⎯⎯⎯⎯⎯
CompileError: spec compile failed:
CompileError: spec compile failed:
CompileError: spec compile failed:
Test Files 2 failed (2)
Tests 3 failed | 46 passed (49)
Green:
$ (cd packages/sdk && npx vitest run tests/agent-resume.test.ts tests/spec-parity.test.ts)
Test Files 2 passed (2)
Tests 75 passed (75)
$ (cd kernel && cargo test -p relayflowd-core)
test result: ok. 86 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.71s
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
$ node scripts/generate-json-schema.mjs && git diff --stat -- packages/schema
Generated packages/schema/flows.schema.json (74 definitions)
packages/schema/flows.schema.json | 10 ++++++++++
1 file changed, 10 insertions(+)
$ (cd packages/schema && bun run test)
86 pass
0 fail
Full SDK suite (02: typecheck, build and test typecheck pass):
# RELAYFLOWD_BIN points at this branch's kernel build (kernel/target/debug/relayflowd); the ambient value named a missing toolchain binary.
$ (cd packages/sdk && RELAYFLOWD_BIN=$PWD/../../kernel/target/debug/relayflowd npx vitest run)
× durable authored root > journals exact source and Surface authority and completes the admitted root 5069ms
× durable authored root > resumes past an answered question: the wait is closed, the body re-runs and completes 5005ms
× hosted extension hostile protocol > rejects an import-time different PR frame with zero adapter calls 1543ms
× flows check --watch > watches transitive relative use imports, cycles, and nearest config changes 5118ms
× flows check --watch > refreshes the import graph and notices missing imports being created 6406ms
× flows check --watch > detects a nearer config appearing and falls back after it is deleted 5822ms
× built flows CLI against live relayflowd > twenty-six-step reuses 25 durable completions after editing the failed final step 8098ms
⎯⎯⎯⎯⎯⎯⎯ Failed Tests 7 ⎯⎯⎯⎯⎯⎯⎯
Test Files 6 failed | 267 passed | 1 skipped (274)
Tests 7 failed | 4166 passed | 20 skipped (4193)
# The 7 failures above are 5s timeouts under full-suite load; the same files in isolation:
$ (cd packages/sdk && RELAYFLOWD_BIN=... npx vitest run tests/cli-watch.test.ts tests/authored-root.test.ts tests/hosted-extension-protocol.test.ts tests/live-kernel.test.ts tests/flow-executor-chain.test.ts tests/mcp.test.ts )
Test Files 6 passed (6)
Tests 156 passed (156)
- The 7 full-run failures are 5s timeouts under load; the same 6 files pass in isolation (156/156).
RELAYFLOWD_BINpoints at this branch's kernel build, because the ambient value named a missing binary.cargo fmt --checkandclippy -D warningsreport findings only in files this PR doesn't touch. Clippy reports 8 errors on cleanmainwith the local toolchain, too. My new hunks are formatted; I did not reformat unrelated code.
Not in this PR
Babysitter adoption. The fixer has to persist {cli, sessionId} beside its PR checkout cache and pass resume only when that cache was restored on the same box. A missing session fails the step: Claude exits 1 with "No conversation found". The adoption also needs an SDK release that the sandbox runs.
🤖 Generated with Claude Code
[!NOTE]
Medium Risk
Changes agent-step execution and spec hashing for flows that declare resume; mis-resume or missing CLI session stores fail steps at runtime rather than silently starting new sessions.
Overview
Adds resume on agent steps and AgentResult.sessionId so a later f.agent (or YAML agent step) can continue the same Claude or Codex conversation instead of starting cold.
Spec and validation: resume is a new optional agent field in the SDK, JSON schema, and kernel (STEP_AGENT_FIELDS, spec hashing when set). Both sides enforce the same lexical session-id rule (shared testdata/agent-resume-cases.json); invalid ids fail closed, and resume with transport: "relay" is rejected.
Execution: Direct-spawn workers pass resume into Claude (-p --resume) and Codex (exec resume); other CLIs or relay dispatch refuse the step rather than run without the session. Codex transcripts now surface thread.started as session_id; authored f.agent reads journaled transcript evidence and returns sessionId when it passes validation (including JSON-object outputs via onCompleted).
Docs: docs/SURFACE.md documents the resume workflow and constraints.
Reviewed by Cursor Bugbot for commit 146ba8dfb625ee4c6ecede5ecb08bf52aca23b99. Bugbot is set up for automated code reviews on this repo. Configure here.
Agent Relay sessions
claudesession497569fc· opened viagh pr create· last active 2026-10-09