Stateful session reuse — warm CLI-agent bindings + session tree ingestion (T333)
By default every CLI worker cold-spawns a brand-new session for every task. With warm bindings
enabled the adapter records the session ID returned by the first successful dispatch, then passes
it to subsequent dispatches via the CLI's own resume flag (--resume <id> for Claude Code,
-s <id> for OpenCode). A failed warm-resume automatically invalidates the cached handle and
falls back to a cold spawn — callers never need to handle the distinction.
The session tree option (captureSessionTree) additionally parses parent/child session topology
from the CLI's streaming output and writes it into WorkerRun.cliSessionTree so judges can
reason over the full agent conversation graph, not just the flattened final message.
Enable on the Claude Code factory:
import { makeClaudeAdapter } from './src/adapters/CliAgentAdapter.ts';
const adapter = makeClaudeAdapter({
warmSession: true, // resume prior session on next dispatch; cold-spawn fallback if cache miss or error
captureSessionTree: true, // parse parent/child session nodes from stream-json output
});Enable on the OpenCode factory:
import { makeOpenCodeAdapter } from './src/adapters/CliAgentAdapter.ts';
const adapter = makeOpenCodeAdapter({
warmSession: true,
captureSessionTree: true,
});Reading session topology after a run:
const run = await store.getRun(runId);
for (const step of run.steps) {
for (const worker of step.workers) {
if (worker.cliResumedSessionId) {
console.log('resumed from', worker.cliResumedSessionId);
}
if (worker.cliSessionTree) {
// Array of CliSessionNode — each has sessionId, optional parentSessionId, role, summary
for (const node of worker.cliSessionTree) {
console.log(node.role, node.sessionId, '->', node.parentSessionId ?? 'root');
}
}
}
}Warm/cold lifecycle:
- First dispatch: cold spawn, session ID extracted from stdout and stored in the in-process cache.
- Subsequent dispatches: resume args injected;
WorkerRun.cliResumedSessionIdrecords which prior session was resumed. - Non-zero exit or spawn error on a warm attempt: cache entry invalidated, next dispatch cold-spawns automatically.
- The cache is in-process only (discarded on exit). Cross-process reuse requires an external session store.
Session tree shape (extends WorkerRun additively, existing fields unchanged):
interface CliSessionNode {
sessionId: string;
parentSessionId?: string; // absent on root nodes
role: 'parent' | 'child';
summary?: string;
stepCount?: number;
durationMs?: number;
}Backward compatibility — warmSession and captureSessionTree default to false.
Existing factory calls, cold-spawn adapters, and all judges are unaffected.
Result projection — configurable worker output fidelity (T328)
By default every worker returns a full Result: the complete output text, usage stats, and any raw provider payload. When a judge or downstream step only needs a summary (e.g. an LLM judge that...
Pluggable execution backends — run workers in Docker or on a remote host (T332)
By default every isolate-step worker provisions a local git worktree and executes its CLI or loop work on the local host. The execution backend abstraction decouples WHERE the worker runs from...