Protocol
The tool reference gives the signatures; this page covers call order, errors and warnings, trace tokens, reference delivery, and dispatch on the wire.
Call order and authentication
Three tools are callable cold — discover, list_workflows, and health_check. Everything else requires the session_index issued by start_session, and a few ordering rules carry the whole protocol:
start_sessionreturnsclient.workflow.initialActivitywhen it embeds a unique catalog match. Otherwiseget_workflowis the source ofinitialActivity— the firstnext_activitycall needs it, so the sequencestart_session → get_workflow → next_activityis fixed on that path.next_activitymoves;get_activityreads. The orchestrator advances the session and gets back only the new activity's id; the worker then loads the full definition. Each activity declares itsexits; a later call names the exit taken, and the server enters the destination the workflow graph binds to it.- An active checkpoint locks the session. Between
yield_checkpointandrespond_checkpoint, navigation and content reads (next_activity,get_workflow,get_activity,get_technique,get_resource,get_trace) are refused.present_checkpointandrespond_checkpointresolve the pause;get_workflow_statusandinspect_sessionstay open for diagnosis,record_usagefor work already done, anddispatch_childso a paused run can still open a child workflow.respond_checkpointwith a user choice is additionally rejected if it arrives less than three seconds after the yield — a response that fast did not involve a person. - Replays are safe. Yielding a checkpoint that was already resolved returns the cached response instead of pausing again, so a worker resumed with stale context cannot double-ask.
get_activityrequirescontext_tokens. The worker declares its own context window in tokens on every activity fetch. The server derives an eager step-technique bundling budget from it (availability headroom × a token→char factor, both server config) and inlines the step-bound techniques whose gate answers true, where they fit. It is per-agent and per-call — never stored on the session, never defaulted. Omittingcontext_tokensis a validation error. See eager bundling and Resolution.
Errors: the call failed
Errors mean the call did not take effect. The useful distinction for an agent is what to do next:
| Error | Meaning | Sensible reaction |
|---|---|---|
| Parameter validation failure | The call violated the tool's schema; it never reached a handler. | Fix the call — never retry verbatim. |
WorkflowNotFoundError, ActivityNotFoundError, TechniqueNotFoundError, ResourceNotFoundError | A definition referenced by id does not exist in the mounted corpus. | Re-check the reference against list_workflows / the activity's declared steps; a typo is far likelier than a missing file. |
SessionStoreError: NOT_FOUND / INVALID_INDEX | No session behind that index, or the token isn't a session index at all. Where a planning folder's session.json could not be read, the error names that folder. | Resume by planning-folder slug via start_session rather than guessing indexes; for a named unreadable folder, restore it from its last commit or start a fresh session. |
SessionStoreError: SEAL_MISMATCH | The state file is not what the server last sealed — edited outside the server, or sealed under another signing key. | Stop and surface to the user; the server will not act on state it cannot vouch for. |
SessionStoreError: SESSION_INVALID | The state file cannot be read as a session: it is not JSON, or the session schema rejects it. | Restore the folder from its last commit, or start a fresh session. |
SessionStoreError: SESSION_OUTDATED | The state file predates the frontier and holds no position to resume from. | Start a fresh session. |
SessionStoreError: STALE_WRITE | Another call recorded against the session while this one was in flight; a parent and its children share one file, so they count as one session. Nothing was written. | Call again with the same arguments — the repeat applies the change to the state as it now stands. Keep one call in flight per session. |
SessionStoreError: FOLDER_OCCUPIED | The planning folder already holds a run. Nothing was written. | Pass the returned session_index to continue that run, or a distinct planning_folder to open another. |
SessionStoreError: COLLISION / WORKSPACE_INVALID | Two folders derived the same index, or the workspace path is unusable. | Both are setup problems for a human, not something to retry around. |
| Checkpoint gate refusals | Navigation attempted while a checkpoint is active, or a response arrived too fast. | Resolve the checkpoint properly; the gate is the feature. |
Warnings: the call succeeded, and left evidence
Validation that requires interpretation never blocks — it lands in _meta.validation as { status, warnings } on the successful response, and is recorded in the trace. Warnings an orchestrator should expect to read:
| Warning | What it signals |
|---|---|
| Missing / unexpected steps in manifest | The reported step_manifest doesn't match the activity's declared steps — work was skipped, invented, or renamed. |
| Step order mismatch, empty step output | Steps reported out of declared order, or a step claimed complete with nothing to show. |
| Technique not fetched | A manifested technique step has no technique_fetched or technique_bundled event — the step was summarized from memory, not executed from its definition. |
| Workflow version drift | The definition changed on disk since start_session; the run continues under the new definition. |
Why deviations warn instead of block follows the same rule — see the advisory layers on the fidelity page.
The trace
Each call that carries a session_index, apart from the read-only diagnostics get_trace, get_workflow_status and inspect_session, appends an event — tool, timing, outcome, workflow, activity, agent, and any validation warnings — to an in-process, per-session buffer (up to 1,000 sessions; the oldest is evicted beyond that). Each next_activity response additionally carries _meta.trace_token: the events since the previous transition, HMAC-signed into a compact token. The tokens are the durable half of the design — an orchestrator that accumulates them can hand the set to get_trace and reconstruct a tamper-evident account of the run even after the in-memory buffer is gone. Calling get_trace with no tokens returns the live buffer for the session.
Reference delivery
Sessions run in one of two delivery modes, recorded as contextMode. In fresh mode (the default) a first delivery to a context is full — right for a dispatched worker, whose context starts empty. A context returning under the identity its dispatch minted still receives markers for the blocks every activity repeats, and opts into reference delivery per call for the rest. In persistent mode the server keeps a per-agent ledger of content hashes and replaces anything already delivered with a short marker:
persistent mode. The ledger is keyed per agent and per channel (technique bundles, rules, individual techniques, resources), so an orchestrator and a worker on the same session have independent delivery state. An agent that has lost its context can force re-delivery with get_activity { bundle: "full" }, get_technique { full: true }, or get_resource { full: true }.The marker is a contract, not a hint: delivery: "unchanged" asserts the content is byte-identical to what this agent already received, which the server can promise because payload composition is deterministic. If your context no longer contains the earlier copy, ask for full delivery rather than proceeding from a summary.
Eager step-technique bundling
Every get_activity call automatically inlines small step-bound techniques whose gate answers true under a step_techniques map — there is no per-activity opt-in. The worker supplies context_tokens; the server computes a cumulative per-activity character budget:
context_tokens × headroomFraction × charsPerToken — defaults 0.80 and 4, overridable via BUNDLE_HEADROOM_FRACTION and BUNDLE_CHARS_PER_TOKEN. The worker bundle the activity rides in — its role rules and contract techniques — is counted first; technique steps with no gate, or a gate that answers true, are then inlined in document order until the next would overflow what remains; a step whose gate (when/condition on the step or an enclosing loop) answers false or has no answer at delivery, and an oversized technique, stay lazy via get_technique { step_id }. A per-activity bundleTechniques.maxChars caps any single technique; maxChars: 0 opts the activity out entirely. Under reference delivery the bodies of resources the inlined techniques link ride a resources map; otherwise their ids ride resource_refs for get_resource.
Bundled entries share the technique:<resolvedId> ledger key with get_technique, so in persistent context mode a bundled delivery collapses a later step-bound refetch to an unchanged marker. Each bundled step is recorded as a technique_bundled event and counts toward manifest fidelity checks alongside technique_fetched.
Enforcement notes and provenance
The server is a state ledger and payload composer, not an executor. Some constructs are agent-interpreted — step ordering, condition evaluation, actions[] verbs, protocol execution — and the server never checks them. Because a payload-only reader cannot see this classification, get_activity surfaces an enforcement_notes block (also in _meta) when the current activity contains agent-interpreted constructs: an actions note when a step is kind:action or carries an actions[] list; an auto_advance note when a checkpoint declares autoAdvanceMs. An activity without these constructs carries no block.
A step-bound get_technique fetch annotates the binding seam: each of the technique's own inputs carries a source: line stating where its value comes from (step-binding value, workflow variable, prior step output, declared default, or UNRESOLVED); remapped outputs carry a destination: line. Contract-inherited inputs are annotated only when the resolution says something the block note does not. Classification is purely static — same corpus and step always produce byte-identical annotations, which is what makes reference delivery safe.
Dispatch in practice
A typical work package starts before any planning folder exists: the meta session created by start_session for workflow selection is transient, living in the system temp directory. The first dispatch_child that commits to real work does three things at once — promotes the transient session into the engineering planning root (typically artifacts/planning/<slug>/, or .engineering/artifacts/planning/<slug>/ in legacy single-root mode; slug from your request or a dated fallback), creates the child session embedded in the parent's file, and returns the child's own session_index for the worker. From there:
get_workflow_statusis the orchestrator's poll — status, the activities in flight and completed, the last checkpoint answered, and the variable bag, without touching execution.- A child reaching its terminal activity flips its entry in the parent's
triggeredWorkflowstocompleted; the parent discovers this on its next status read rather than by callback. - Because children are embedded, not separate files, resuming the parent by folder name resumes the entire tree.
Next
Signatures for every tool are on the tool reference; server-side mechanics are in request lifecycle.