Five bridges adapt existing app code to the runtime seams, each unit-
tested against injected fakes:
- RealAgentResolver: loadAgent + dynamic builders -> immutable
ResolvedAgent. Model precedence override > agent > app default; tool
descriptors from BuiltinTools (zod -> JSON schema) and MCP
attachments (toolId schemes builtin:/mcp:server:tool); ask-human as
the async requiresHuman tool. System prompt composed via
composeSystemInstructions — extracted verbatim from streamAgent so
old and new runtimes share one implementation — driven by the new
opaque RequestedAgent.overrides.composition (session-sticky inputs
preserve provider prefix caching; per-message context stays on
UserMessage.userMessageContext as today).
- RealModelRegistry: models.json -> live AI SDK model; one streamText
step (stopWhen: stepCountIs(1)) normalized into deltas, durable step
events, and the completed assistant message.
- RealToolRegistry: execTool dispatch (builtins + MCP) with per-call
abortRegistry bracketing and tool-output-stream -> durable progress.
- RealPermissionChecker: getToolPermissionMetadata rules (command
allowlist, workspace file boundaries); session grants deferred.
- RealPermissionClassifier: classifyToolPermissions with conversation
context now threaded through the classifier batch.
Interface refinements (doc updated): ToolExecutionContext gains
turnId/toolCallId; classifier takes a batch with turnId + messages;
TurnRuntime dep renamed bus -> lifecycleBus and SessionsImpl bus ->
sessionBus for strict-PROXY DI. Container registers the whole stack
(FS repos rooted at WorkDir/storage); the legacy agentRuntime
registration became lazy to survive the new import-order cycle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The TurnRuntime per turn-runtime-design.md: constructor-injected,
container-ignorant, no per-turn state — every advanceTurn reconstructs
from the JSONL log, so normal execution and crash recovery share one
path.
- runtime.ts: createTurn/advanceTurn/getTurn; durable-barrier appends
before side effects; sequential sync tools with progress; async tool
exposure + durable suspension; permission pipeline (checker →
optional classifier batch → human/deny fallback, fail-closed on
checker errors); cancellation with synthetic results; recovery that
re-issues interrupted model calls and continues after interrupted
sync tools; model-call-limit exhaustion with machine-readable code.
- fs-repo.ts: date-partitioned append-only JSONL, strict line
validation, path-traversal rejection, per-turn in-process locking.
- stream.ts: hot execution stream — buffers before the consumer
attaches, outcome independent of consumption, close-drops-events.
- context-resolver.ts: chain-walking materialization of context
references with cycle detection.
- Seam interfaces for stage-4 bridges: model/tool registries,
permission checker/classifier, agent resolver, lifecycle bus, clock.
- shared reducer: track classification failures durably so failed
classifications are never re-run.
67 new tests: all nine §26 end-to-end scenarios (mocked deps,
recovery boundaries seeded directly) plus repo/stream/resolver suites.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Durable contracts for the new turn/session runtime, shared by core and
renderer:
- turns.ts: zod schemas for all 16 durable turn events, TurnContext
(previousTurnId ref | inline messages), ModelRequest with contextRef
and current-turn-only messages, ephemeral delta types, reduceTurn
enforcing every spec invariant, and pure derivations
(deriveTurnStatus, turnTranscript, outstanding work).
- sessions.ts: session_created/turn_appended/title_changed schemas,
reduceSession, and the SessionIndexEntry projection.
- vitest wiring for packages/shared (config, build-excluded tests);
92 tests covering happy paths, recovery-shaped histories, and one
named test per corruption invariant.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>