Skip to main content
Draft — not yet ratified. This document is published for review. It describes the intended 1.0 behaviour, its wording is not final, and nothing here is covered by a compatibility promise until a version is frozen. Do not cite it as a stable reference.
An agent may delegate part of a run to another agent. Everything the delegate produces travels in the same stream, so a consumer needs to know which work belongs to whom without replaying the run.

Attribution

subagentRunId identifies one invocation of a subagent. Events the subagent produces carry it; events the parent agent produces do not. An absent subagentRunId means the parent agent, and MUST NOT be spelled as null. An id identifies an invocation, not an agent. A producer MUST NOT reuse a subagentRunId for a second invocation within a run, even of the same subagent — the second invocation gets its own id. An id MAY reappear in a later run, when a suspended invocation is continued. Which events can carry attribution is a structural question, and the schema answers it. Read a tag on a standalone event — STEP_*, CUSTOM, RAW, the state events — as provenance: it records who produced the event, not that the producer owns a private copy of what the event touches.

Lifecycle

SUBAGENT_STARTED announces an invocation, SUBAGENT_FINISHED closes it, and SUBAGENT_ERROR reports that it failed. All three carry the subagentRunId they concern, and a producer MUST NOT omit it.
  • A producer that announces an invocation MUST do so before any event attributed to it.
  • A producer MUST NOT announce an invocation whose id is already active.
  • A producer MUST NOT reuse the id of an invocation that has already finished in this run.
  • Every invocation a producer announces MUST be closed, by SUBAGENT_FINISHED or SUBAGENT_ERROR, before the run finishes.
A producer MAY attribute events without announcing anything. Such a stream tells a consumer which work belongs together but not when an invocation began or ended, and a consumer MUST accept it: subagentRunId is meaningful on its own, and requiring the lifecycle events would make grouping unavailable to producers that cannot report it. A producer that can report the lifecycle SHOULD, since a consumer can then show an invocation as running rather than inferring it from the events that happen to arrive. SUBAGENT_ERROR ends the invocation, not the run. A parent agent MAY handle a failed subagent and continue; that is why it is not RUN_ERROR. SUBAGENT_FINISHED reports why the segment ended. An absent outcome means success. A suspended outcome means the invocation is paused awaiting outside input — terminal for this stream but not for the invocation, which a later run MAY continue under the same id. A producer MUST NOT send an outcome value the schema does not describe, and MUST NOT attach interrupt ids to a success outcome.

Ownership

Attribution is not advisory. Once an entity is opened under an owner, every event continuing it MUST agree about that owner.
  • A TEXT_MESSAGE_CONTENT or TEXT_MESSAGE_END MUST NOT carry a subagentRunId that disagrees with the TEXT_MESSAGE_START that opened the message. It MAY omit the tag: an untagged continuation continues whatever the opener owns, and a producer that tags only the opener is still conformant.
  • The same holds for reasoning messages, for tool calls against their TOOL_CALL_START, and for activity messages against the snapshot that opened them.
  • A STEP_FINISHED MUST carry the same attribution as the STEP_STARTED it closes. Steps are per-owner: a parent and a subagent MAY each have a step of the same name open at once, and each closes its own.
A consumer MUST reject a continuation whose tag disagrees with its opener. Accepting it would append one producer’s content into another producer’s message, which no consumer can detect afterwards. A continuation carrying no tag is not a disagreement and MUST be accepted. A tool call inherits the attribution of the message that held it. A producer MUST NOT attribute a tool call to one owner while the message carrying it is attributed to another.

Nesting and parallelism

A subagent MAY itself delegate. parentSubagentRunId on SUBAGENT_STARTED names the invocation that spawned this one; absent means the parent agent spawned it directly. A producer MUST NOT name a parent invocation that has not been announced in this run. Invocations MAY run in parallel, and their events MAY interleave arbitrarily. Each invocation’s entities are tracked separately, so two subagents MAY stream messages at the same time. A consumer MUST NOT assume that one invocation’s events are contiguous, and MUST NOT close one invocation’s entities because another invocation ended.

State

State is run-scoped. A STATE_SNAPSHOT or STATE_DELTA a subagent sends updates the run’s state exactly as one from the parent agent does, and a consumer MUST apply it that way. subagentRunId on a state event is provenance: it records which invocation produced the update. It MUST NOT be read as the subagent owning a private state of its own, and a consumer MUST NOT withhold the update from the run’s state because a subagent sent it. There is no per-subagent state in this protocol.

Termination

A run MUST NOT finish while an invocation is still active. A producer that reaches the end of its work with an invocation open has a bug: either the invocation ended and was not reported, or it did not end and the run is not over. RUN_ERROR ends everything. A consumer MUST treat every open invocation as abandoned when a run errors, and MUST NOT expect a closing event for any of them. When a run finishes with an interrupt outcome, the interrupts it carries MAY be attributed. An interrupt tagged with a subagentRunId was raised inside that invocation; an untagged one belongs to the parent agent. An invocation suspended because a descendant interrupted owns no interrupt of its own, so a suspended outcome MAY carry no interrupt ids at all.