8 KiB
V2 Session Contract
Status: Current semantic overview. Protocol owns public operations, Schema owns public shapes and durable events, and Core owns execution and persistence behavior. CONTEXT.md defines the canonical terms used here.
Prompt Admission Precedes Execution
SessionV2.prompt(...) records one durable session.input.admitted fact and one session_pending row before advisory execution begins. Pending input remains outside model-visible Session History until promotion. The promotion transaction publishes session.input.promoted, projects the visible message, and consumes the pending row atomically.
Reusing a Session ID adopts the existing Session. Reusing a prompt message ID reconciles an exact retry only when Session, prompt, and delivery mode match; conflicting reuse fails. A retry of an already-promoted input reconciles against projected history and its durable admission event.
resume controls scheduling, not durability:
- Omitted or
truerecords the input, then schedulesSessionExecution.wake(sessionID). falserecords the input without scheduling execution.
Delivery is explicit:
steeris the default. Steers promote together at the next Safe Step Boundary while the current Session Drain still requires continuation.queueremains pending while the Session can continue. When the Session would otherwise become idle, one queued input promotes; the runner then reevaluates continuation before promoting another.
Promoting new user input resets the selected agent's step allowance. A batch of steers resets it once.
Manual compaction uses the same pending store as one coalesced barrier. The barrier blocks later input promotion until compaction ends or fails, then is consumed.
Execution Is Process-Local
SessionExecution is process-global and keyed only by Session ID. At drain start it loads the Session, enters its Location through LocationServiceMap, and invokes the Location-scoped runner. The runner, model resolution, tools, permissions, plugins, and filesystem remain Location-scoped.
SessionRunCoordinator provides the local ownership rules:
- Explicit resumes join the active execution for the same Session.
- Repeated wakes coalesce into one follow-up drain.
- Different Sessions run concurrently.
- Interruption stops locally owned execution without deleting pending input.
The public interrupt operation verifies that the durable Session exists. An unknown Session fails with SessionNotFoundError; a known Session that is idle, settled, or not locally owned is a no-op.
sessions.active() snapshots foreground drains currently owned by this process. Durable execution events are historical observations, not liveness or ownership records.
The managed server provides graceful restart continuity through private Session suspension. Shutdown marks active Sessions before interrupting them; the next managed server atomically consumes each suspension and schedules at most one resume. Hard-crash recovery and exactly-once provider or tool execution remain out of scope. See Managed restart continuation.
One Step Owns One Logical LLM Call
Before each Step, the runner reloads Session History, resolves the selected agent and model, prepares instructions, and materializes tools. Most Steps make one Physical Attempt; overflow-triggered compaction recovery may rebuild the same Step for one additional provider request.
Each complete local tool call is durable before side effects begin. Local calls start eagerly and may run concurrently, but settlement publication remains serialized. Every local and hosted call reaches durable success or failure before the Step publishes its single terminal ended or failed event.
Tool calls belong to their assistant message. callID is unique only within that Step, so durable tool events also carry assistantMessageID.
Before runStep assembles its provider request, orphan reconciliation fails tool calls still projected as streaming or running from an earlier process. It preserves the original assistant attribution and never replays ambiguous side effects.
After local settlement, continuation reloads projected history and begins a new Step. The runner never delegates orchestration to an in-memory tool loop.
Retry Is Narrow And Observable
Core retries typed rate-limit, provider-internal, and transport failures only before durable assistant content, tool-call, tool-output, or tool-execution evidence exists. The initial request plus at most four retries use exponential backoff, increased when the provider supplies a longer retry delay.
Each retry attempt is a distinct Step, consumes the selected agent's allowance, and reuses the assistant message ID while no durable output exists. session.retry.scheduled records the next attempt and absolute retry time. A later Step start or terminal failure clears projected retry state. Surviving retry history never triggers post-crash recovery by itself.
A normalized content-filter finish fails the Step. Any partial streamed content remains visible.
Instructions Are Session-Owned
InstructionCheckpoint stores the exact instruction baseline last shown to the model and an Instructions.Applied record for each source. The runner explicitly combines built-ins, ambient discovery, selected-agent skill guidance, references, MCP guidance, and API-managed instruction entries. There is no instruction registry.
The first complete observation establishes the baseline before input promotion. Later changes publish chronological session.instructions.updated messages and advance applied state atomically. An unavailable source preserves the model's previous belief; it blocks only a Session that has never established a complete baseline.
Completed compaction rebaselines instructions. Session movement and committed revert reset the checkpoint. Model selection affects request assembly but is not itself an instruction source.
Compaction Rebuilds Active History
Before each Step, the runner estimates the complete model-visible request against the selected model's context window and reserved output headroom. When compaction is enabled, model limits are known, and enough older Session History is available, the runner may store a structured rolling summary plus bounded recent context instead of sending an over-budget request.
The full transcript remains durable. Active model history after the checkpoint contains the summary and retained recent context; provider-native continuation state does not cross the compaction boundary.
If the provider reports context overflow before durable assistant output or tool execution, the runner may perform one overflow-triggered compaction and rebuild the same logical Step. A second overflow or any overflow after durable output is terminal.
Durable Events Are Session-Scoped
sessions.log({ sessionID, after?, follow? }) verifies the Session and reads public durable Session events after an exclusive aggregate sequence. With follow: true, it subscribes before replay and emits one synchronization marker at the captured watermark before live durable events continue.
Live-only text, reasoning, tool-input, and compaction deltas are intentionally absent from replay. The instance-wide live event stream has different schemas and no replay guarantee.
There is no separate finite Session-history endpoint. Request/response consumers use authoritative Session projections such as messages, pending input, and context; replay consumers use the durable log.
Recovery Boundaries Stay Explicit
An advisory wake does not infer that ambiguous provider work is safe to retry after input promotion. Explicit resume may continue from durable projected history, but automatic hard-crash continuation requires a separate design covering provider-dispatch ambiguity, tool idempotency, retry budgets, and future clustered ownership.
Event replay ownership is separate from Session execution ownership. Local execution remains process-owned until clustering introduces an explicit placement and fencing protocol.