Agent Runtime Pack
The Agent Runtime Pack is the pack-scoped runtime for governed agent execution. Its manifest-registered
tool surface is agent-runtime:execute-turn plus six remote-callable remote-control tools
(resume_workflow, pause_turn, abort_turn, elevate_autonomy, lower_autonomy,
approve_inflight), alongside library helpers for long-running agent-session orchestration.
What The Pack Adds
Section titled “What The Pack Adds”| Surface | Purpose |
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| agent-runtime:execute-turn | Executes one governed model turn and returns native ordered content, exact tool calls, and terminal state |
| runGovernedAgentLoop() | Host helper for a multi-turn loop that keeps real tool calls in the kernel enforcement path |
| startGovernedAgentSession() / resumeGovernedAgentSession() | Persist and resume linear agent-session state |
| startGovernedAgentWorkflow() / resumeGovernedAgentWorkflow() | Persist and resume branching, joins, and scheduled wakeups inside the same agent-session task model |
| createHostContextMessages() / createApprovalResolutionMessage() | Build host-supplied messages without depending on another pack’s storage internals |
The pack exports provider adapters, capability augmentation, and a policy factory, but those are internal pack plumbing rather than a separate public tool surface.
Task Model
Section titled “Task Model”The pack owns one task type:
Scheduled wakeups, resumed sessions, and workflow branches all remain agent-session executions.
The pack does not introduce a second execution class for routines or workflows.
Runtime Boundary
Section titled “Runtime Boundary”| Layer | Owns |
| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Kernel | Host-authenticated caller binding, persisted authority projection, policy/scope enforcement, physical confinement, approvals, and immutable evidence |
| Agent Runtime Pack | Turn normalization, provider adapter behavior, agent-session state, branch/join readiness, and scheduled wakeups |
| Host | Opaque caller authentication, strict selector binding, external context, exact tool allowlisting, loop entry/exit, and approval timing |
The pack receives a sealed AuthenticatedExecutionRuntime, not a raw ToolHost or
caller-constructed execution context. Every operation uses an ExecutionSelector containing
exactly task_id, run_id, and session_id; the kernel resolves the principal, workspace, lane,
scopes, pinned pack configuration, and execution-policy profile from persisted trusted state.
Governed Turn Contract
Section titled “Governed Turn Contract”Every agent-runtime:execute-turn response uses the same normalized output shape:
content_blocks is the canonical ordered conversation representation. It keeps text, reasoning,
opaque provider state, tool calls, and tool results distinct instead of flattening them into one
string. Empty content_blocks is a valid empty reply. requested_tools contains every native
provider call in provider order. Only native OpenAI-compatible tool_calls or
Messages-compatible tool_use blocks create executable requests.
Assistant text is always ordinary untrusted content, including valid JSON text. The adapter never
parses it as status, policy authority, or requested-tool metadata. Native tool-only responses need
no synthetic text envelope and remain valid with empty assistant_message. The deprecated
intent_catalog input is accepted for compatibility but ignored; model-authored labels can only
appear as optional untrusted_model_label telemetry and never select an execution policy profile,
allowed tools, approvals, or protected metadata. Messages-compatible requests include the native
API’s x-api-key, pinned anthropic-version, and positive max_tokens fields.
The kernel still governs every real tool call. The orchestration helper requires each requested
tool to exist in the exact host-provided catalog before it executes any request in the batch. It
appends the assistant call turn before its results, executes multiple calls deterministically, and
sends each result back with the exact provider-issued call_id. A missing ID is invalid output,
never an invitation to create a replacement ID.
The complete call list is validated before execution: IDs must be unique, and ordered call blocks
must match every request’s ID, name, and input. Batch budget checks are atomic. Rejected batches,
thrown calls, and unexecuted remainder calls still receive paired exact-ID results so provider
history remains valid. Multiple approval requests are persisted together and a partial approval
continuation cannot erase unresolved requests. Resolutions require a nonempty request_id, a
boolean approved, and a nonempty approved_by; malformed and duplicate resolutions fail before
persisted state changes.
This matches the provider pairing rules: Chat Completions history uses the exact tool_calls[].id
as tool_call_id; Responses history preserves each exact call_id and emits one
function_call_output item per call; Messages-compatible history uses the exact tool_use.id as
tool_use_id. See the official OpenAI function-calling
guide and Anthropic tool-use
guide.
Execution Ownership & Caller Graphs
Section titled “Execution Ownership & Caller Graphs”Every tool-related content block (tool_call, tool_result, and the opaque fallback for
unrecognized provider items) declares an execution_owner:
| Owner | Meaning |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| client | An arbitrary governed tool the host executes via the authenticated execution runtime, subject to trusted authority and approval |
| kernel | A reserved agent-runtime:* remote-control tool (see Remote Controls) — a kernel-implemented lifecycle operation, never a user-registered tool |
| provider | The provider itself executes and owns the call (Anthropic server_tool_use, an OpenAI Responses hosted tool item, or any unrecognized future block kind) |
requested_tools only ever contains client- and kernel-owned calls — the exact set that
crossed the provider boundary into the governed execution path. provider-owned calls are never
executed or emulated locally; they round-trip byte-for-byte (see below) so the provider can
continue its own state.
Blocks also preserve, when the provider supplies them, our internal caller_id/container_id/
source_order fields. These are normalized names — the actual wire shape differs per provider and
is read from exactly where each provider puts it, never fabricated:
caller_id— the parent/caller item id for a nested or programmatic tool-call graph.- Anthropic nests it on the
tool_useblock itself:"caller": {"type": "direct"}for a direct model call, or"caller": {"type": "code_execution_20260120", "tool_id": "srvtoolu_..."}when the call was issued from inside a runningcode_executionblock —tool_idbecomescaller_id. See Programmatic tool calling. - OpenAI nests it on the item:
"caller": {"type": "program", "caller_id": "call_prog_123"}. Thatcaller_idmatches the callingprogramitem’scall_id, not itsid— so the caller-graph node identity for every OpenAI item is itscall_idwhen present, falling back toidonly for items (mostly hosted tool calls) that have nocall_id. See Programmatic tool calling. - A
caller_idmust resolve to an item already seen earlier in the same turn; an unresolved, self-referencing, or out-of-order caller edge fails the turn closed with a stablePROVIDER_CALLER_GRAPH_*error rather than silently dropping or fabricating a correlation.
- Anthropic nests it on the
container_id— an opaque provider container/resume token (for example a code-execution container), preserved unchanged across turns.- Anthropic: the container is top-level on the Message response —
{"content": [...], "container": {"id": "container_xyz", "expires_at": "..."}, "stop_reason": ...}— not per-block. It applies to the whole turn, so every provider-owned block in that turn is tagged with it. On the next request it is required as a top-level string field —{"container": "container_xyz", ...}— whenever a programmatic tool call is still pending; the pack resends the most recently observed container id automatically on every continuation. - OpenAI’s confirmed container-reuse mechanism is a tool-config field
(
tools: [{"type": "code_interpreter", "container": "cntr_..."}]), not a per-item response field. The pack still preserves a per-itemcontainer_id/container.idwhen a hosted item happens to carry one (a defensive, non-fabricating pass-through), but this is not itself a confirmed OpenAI wire contract the way the Anthropic top-levelcontaineris.
- Anthropic: the container is top-level on the Message response —
source_order— the 0-based ordinal fixing the item’s position in the provider’s original sequence, independent of any later filtering.
A reserved kernel tool must always be called directly — a kernel-named call arriving with a
non-direct caller (nested inside a code_execution/program run) is contradictory ownership and
fails closed with PROVIDER_AMBIGUOUS_TOOL_OWNERSHIP before any execution is attempted.
The preserved caller graph is bounded: depth and fan-out both have configurable safety limits
(max_caller_graph_depth, max_caller_graph_fan_out, defaulting to 8 and 32). Exceeding either
limit — or a graph shape that cannot be resolved unambiguously — fails the turn closed with one of
PROVIDER_AMBIGUOUS_TOOL_OWNERSHIP, PROVIDER_CALLER_GRAPH_CYCLE,
PROVIDER_CALLER_GRAPH_DEPTH_EXCEEDED, PROVIDER_CALLER_GRAPH_FAN_OUT_EXCEEDED, or
PROVIDER_CALLER_GRAPH_UNKNOWN_CALLER before any execution is attempted.
Provider-owned round trip
Section titled “Provider-owned round trip”A provider-owned tool_call/tool_result block carries the exact original provider block under
metadata.provider_raw_block. When the pack resends history, a provider-owned block is emitted
verbatim from that raw block instead of being rewritten into the generic client tool_use/
tool_result shape — the same idiom already used for Anthropic thinking/redacted_thinking
blocks. This is what lets an Anthropic server_tool_use call and its web_search_tool_result (or
any ${tool_name}_tool_result) survive a full round trip without the kernel ever executing or
rewriting provider-owned state.
Pause and continuation
Section titled “Pause and continuation”Anthropic can pause a long-running, provider-owned turn with stop_reason: "pause_turn" and
expects the exact content resent unmodified to continue. The pack maps this to
terminal_state: "paused" — never a phantom complete — and the governed loop persists the
turn as suspended (not completed), appending the exact assistant content so a later resume
call (resumeGovernedAgentSession / resumeGovernedAgentWorkflow) can continue it.
Deliberately unsupported
Section titled “Deliberately unsupported”- The pack never emulates a provider-hosted tool locally, and never exposes a generic proxy that lets the kernel invoke provider-owned tools on the provider’s behalf.
- Depth/fan-out bounds and caller-edge resolution are a safety net, not a full graph query API — the pack does not expose ancestor/descendant traversal helpers.
- Provider “container” staleness/expiry is not tracked;
container_idis preserved faithfully as opaque provider state, but the pack does not validate whether a given container is still live on the provider’s side.
Terminal Outcomes
Section titled “Terminal Outcomes”The provider adapter uses bounded incremental parsing for response bytes, stream bytes, events, snapshots, blocks, calls, and tool arguments. Delta snapshots do not copy the growing transcript. After a terminal marker, the adapter drains until natural EOF or one absolute configured deadline, rejects every non-empty post-terminal chunk observed before that deadline, and then cancels a reader that did not close. Cancellation is best-effort and never blocks the bounded return path. Bytes sent only after the deadline are not observable. Cancellation remains active after response headers.
The provider adapter emits one normalized adapter outcome per invocation: completed, cancelled,
provider_error, malformed_stream, or client_disconnect. Partial stream chunks can produce
progress snapshots, but cannot produce a second terminal event or a phantom tool call. Chat
Completions streams are malformed without a finish reason before [DONE]. Responses streams must
end in exactly one response.completed, response.incomplete, response.failed,
response.cancelled, or error event, with provider failure details preserved as bounded
diagnostics. Messages streams are incomplete unless every started content block is stopped and
message_delta supplies a stop reason before message_stop. Tool turns require a successful
provider terminal reason. terminal_state maps Chat Completions stop and Messages
end_turn/stop_sequence to complete, length/max_tokens to incomplete,
content_filter/refusal to refused, Messages pause_turn to paused (see
Pause and continuation), and unrecognized future reasons to unknown.
Only tool_request is runnable; paused is resumable, never a phantom completion.
Responses text and function-argument deltas must reach their matching native done substages before the output item completes. Duplicate or late deltas/done events are rejected. Incomplete calls stay opaque and non-runnable, while item, call, argument-byte, and JSON-structure limits still apply.
Messages-compatible thinking and redacted_thinking blocks are returned complete and unmodified
when history is sent back for tool use. Chat Completions reasoning fragments retain their index and
assembled signature. Responses requests use store: false; reasoning items preserve ordered
provider identity, optional provider status, and encrypted content so complete stateless history
can replay provider-owned reasoning without interpreting it as application authority. Function
tools use strict: false because the runtime accepts governed schemas beyond OpenAI’s strict
structured-output subset. See OpenAI’s reasoning continuation
guide
and Responses streaming migration
guide.
Current Limits
Section titled “Current Limits”- The manifest-registered tool surface is intentionally small: one turn tool plus six remote-control tools, alongside pack library helpers (not separate manifest tools).
- OpenAI protocol selection is explicit:
openai_compatibleis Chat Completions-style andopenai_responsesis Responses typed-item/SSE. The adapter never infers protocol from a URL. - The Messages-compatible and Responses adapters preserve client-, kernel-, and provider-owned tool calls and their caller graph (see Execution Ownership & Caller Graphs) but never execute or emulate provider-owned work locally, and do not track provider container staleness/expiry.
- Tool access is controlled by the persisted execution-policy profile, kernel scope/policy projection, and an exact host catalog; the model never gets direct access to undeclared tools.
- Workflow state is committed through the RunAggregate-backed workflow authority (a fenced
record_effecttransition plus a content-addressed payload sink), while the durable audit trail remains in kernel evidence files. The legacy.agent-runtime/workflow/directory is read-only, used only for one-way migration into the RunAggregate authority. - New workflow writes use schema v3. The bounded v1/v2 read path migrates persisted envelope fields to ordered native blocks without interpreting assistant text as control data.