Kernel operations¶
The runtime exposes a small set of operations. Subagents, recovery, and debug are combinations of those operations, not separate engines.
Operations¶
| Operation | Meaning |
|---|---|
| Start / stop | Materialize a spec into a running agent; tear it down |
| Step | Current state + input → next state, through governance, written to the trace |
| Spawn | Start a child agent with a parent id |
| Snapshot | Cheap in-memory picture of the run (kernel, working memory, spec revision) |
| Persist | Write that picture to disk |
| Navigate | Move a debug cursor on the snapshot tree; does not change the live run until promote |
| Pause / resume / steer / undo | Control. Pause blocks the next user turn |
| Spec revision | Recorded change to the live spec (disable a tool, switch a pattern) |
Plugin code, an LLM tool advertisement, and an admin caller (ControlContract)
call the same functions. Multi-host attach is a library control_protocol
plugin (JSON-lines unix/TCP). A2A is how people talk to an agent. Two A2A
connections with no contextId are two sessions. The first response returns
the session id as A2A contextId; a later connection that sends that id
attaches to the same session. Concurrent attachers share one TurnInputQueue.
Inbound user turns are a subset of ControlContract: A2A message/send is
send_message (queue a turn at the tail). A2A has no steer RPC. steer is
control-protocol only. AgentCommContract.send is outbound peer send after
delegation, not inbound user input. UserIOContract is progress egress.
input-required is HITL, not a splice.
Concurrent queue¶
All queue mutations are lock-linearized per session:
| Verb | Concurrent rule |
|---|---|
send_message |
A2A Send Message (message/send). Always succeeds. Tail only, action="turn". Additional input on a non-terminal working task waits in the queue. Never preempts. |
enqueue_input |
Control-protocol queue mutation. Default at="tail"; at="head" or at=<index> from inspect_queue ([0, len]). May set action="steer" for a later cycle. Never conflicts. |
cancel_queued / drop |
CAS on revision. Gone id → QueueItemGone. |
reorder_queue / move |
CAS on revision. Must list every live id once. |
set_queued_action (queue → steer) |
CAS on revision. Changes a still-queued item. |
steer (preempt) |
Keep streamed tokens, stop the rest of this decode, continue. The decode is preempted — not a finished client response. Unavailable on A2A. |
steer (replace) |
Discard streamed tokens, stop this decode, start a new exclusive turn. Unavailable on A2A. |
steer (after) |
Do not interrupt. Queue text to the front (at="head") and wait for the current generation to finish. enqueue is an alias. |
cancel_inflight |
A2A Cancel Task (tasks/cancel). Drops the remainder of the decode; no follow-up cycle. Success is not guaranteed. |
inspect_queue returns the CAS token. Callers retry on QueueConflict.
There is no last-write-wins. Turns drain FIFO under the same lock, so a
drop cannot race a pop.
LLM primitives¶
These control verbs are not Mealy ticks. They close or continue an
LLM_CALL already in M_model=CALLING:
| Verb | LLM | M_model |
Reply |
|---|---|---|---|
steer (preempt) |
Cancel the streamed ainvoke between tokens. Keep append_partial prefix in working memory. New completion after OperatorSteerReceived (operator text in context). |
CALLING → IDLE (abort, not DONE) → CALLING | Prefix is not a client stop. Continuation is a new assistant message. |
steer (replace) |
Cancel ainvoke, discard partials. New UserInputReceived turn. |
CALLING → IDLE, M_dp IDLE |
No prefix in history. Fresh user turn. |
steer (after) |
Do not touch the in-flight call. | unchanged | Current decode finishes normally (stop), then the queued turn runs. |
cancel_inflight |
Cancel ainvoke, drop remainder. |
CALLING → ERROR/IDLE via cancelled return | Error/cancel client response. No follow-up. |
Preempt needs a cancellable decode. ainvoke uses achat_completion_stream when the provider has it, so cancel lands between chunks even if the spec did not set stream: true for UI. Without a stream iterator, cancel still aborts the one-shot await and the prefix is empty.
finish_reason=preempted / replaced on EngineIoReturn means that decode ended, not that the agent finished answering. Evaluate does not emit EmitClientResponse for those.
mas-ctl chat and mas-ctl serve advertise each session id (A2A
contextId) in $XDG_RUNTIME_DIR/mas-ctl, else /var/run/mas-ctl, else
the process temp dir (MAS_CONTROL_DIR overrides). mas-ctl control attach
SESSION_ID uses that default. Pass --no-control to skip advertising.
Gdb-like debug scripts are a runtime plugin (debug_script).
They observe tool-call / tool-result so the process can act as a debugger
(evaluate_egress always ALLOW). Enable from workspace config.yaml
plugins: / lab.enable_plugins. spec.debug holds script_file or
inline script. Load also with mas-ctl chat --debug-script @file.
break tool_call / break tool_result … if first, then checkpoint,
info checkpoints, info working_memory, continue.
Breakpoint hits emit native telemetry
(category: debug.breakpoint); checkpoint / pause / resume go
through ControlContract.
Checkpoint storage is a library checkpoint_store plugin
(memory, disk, hybrid) behind persist. See
Session checkpoints.
Workspace config.yaml plugins: and lab.enable_plugins turn on gdb and
checkpoint stores. Off unless listed. spec.observability and
spec.governance stay on the agent.
mas-ctl control scripts (pause, persist --auto-stop, steer) use the
same language whether chained on the CLI, passed as -e, or --data @file.
Snapshot/persist require a paused session unless --auto-stop.
Layers¶
- Kernel operations — the table above.
- Boundary slots — closed envelope/spec types (
governance,llm_provider,tool_provider, …). A new slot is a kernel change. - Harness compositions — named combinations whose leaves are
operations or slots (
react,subagents,recovery,detective,whatif,plan_mode,evolution). Seemas.runtime.harness. - Library / product — skills, related filesystem adapters, execute
sandboxes, lab steps. These are not envelope slots and are not stored
in kernel product state
Q.
Tool execution after ALLOW may run inside an ExecuteSandbox (workdir
or a host jail). Coding-agent workspaces are snapshotted as related
state (fingerprint + locator; git or directory-copy adapters). See
snapshots.
Copy-on-write¶
Python updates kernel state in place. A snapshot copies kernel product
state into a frozen CowKernel and freeze-shares working memory. Later
writes do not leak into the frozen node. Restore installs a writable copy.