Skip to content

Runtime-provided tools

A runtime-provided tool is an entry in the agent's tool inventory with no artifact behind it. The runtime writes its murmur.yaml into tools/<name>/ in the session's runtime directory while the session is staged, and answers the call itself.

The model sees no difference. A runtime-provided tool has the same manifest shape, appears in the same inventory, and is called the same way as a tool artifact. What differs is where it comes from and what decides whether it may run.

The twelve

Each one appears only when the declaration in the second column is present.

Tool Gated on Does
One per shell binary capabilities.shell.allow Runs that binary as a subprocess in the accessible workdir
share-file exports.peer_files Mints a peer-file handle for one file under the declared export root
fetch-peer-file capabilities.peer_fetch Redeems a handle a peer sent and stores the file in this capsule's workdir
delegate-task capabilities.spawn.allow Hands one task to one sub-capsule, named by the bare part of its spawn.allow entry, and returns as soon as it is running and holding it; once the turn ends, the task waits and continues with the outcome — see The delegation tool
submit-plan capabilities.plan.submit Runs one plan of steps against this session's own tools and returns every step's result — see Plans
switch-driver control.agent_settings: [inference.driver] Selects the driver choice the agent's next inference call is served by — see switch-driver
call-member A reachability rule in the formation's roster.yaml that lets this member call another Hands one task to another formation member and returns at once, started or busy; the answer arrives later in the same task — see call-member
end-without-answer The same rule as call-member Ends a task another formation member sent without an answer, and the runtime tells that member none came — see end-without-answer
pull-artifact capabilities.install, under transport: http Installs a skill or tool into the running session, usable from the next turn, or lists what the capsule may pull — see pull-artifact
remove-artifact The same grant as pull-artifact Takes out an artifact the session pulled — see remove-artifact
session-status An inference: block: every agent capsule, on both transports Reports the time the session has run and has left, its turns, context, tokens and spend — see session-status
background-command An inference: block, a non-empty capabilities.shell.allow and a lifecycle.shell_grace_secs, on both transports Reports on, waits for or stops a shell command that moved to the background, by its work id — see background-command

Every one of their manifests carries version: 0.0.0, runtime: tool and implementation: native. Nothing was fetched, so nothing is version-pinned, nothing is hash-verified, and no entry appears for them in murmur.lock or in mur list.

The grant is the tool's existence

share-file, fetch-peer-file, delegate-task, submit-plan, switch-driver, call-member, end-without-answer, pull-artifact, remove-artifact, session-status and background-command are answered before the tool allowlist is consulted. The allowlist governs which tool artifacts may run; it has no say over these eleven.

session-status needs no grant: staging writes its manifest for every agent capsule and for no script capsule.

The gate is whether the manifest file was written at all. With the grant absent, staging writes nothing under tools/<name>/, so:

  • The tool is absent from the inventory the model is sent.
  • It is absent from session_start's tools_declared in trace.jsonl.
  • A call naming it anyway is refused with a message naming the declaration that is missing.

So capabilities.spawn.allow decides whether delegate-task exists, rather than whether a call to it succeeds. The same holds for the two peer-handoff tools, for submit-plan, for switch-driver, for call-member and end-without-answer, for pull-artifact and remove-artifact, for background-command, and for their grants.

capabilities.plan.submit also decides whether the model is told when to plan: the runtime's plan guidance is part of the system prompt exactly when the tool exists.

Shell binaries are gated the same way — one manifest per name in capabilities.shell.allow — and are additionally checked against that list again at dispatch.

Reserved names

share-file, fetch-peer-file, delegate-task, submit-plan, switch-driver, call-member, end-without-answer, pull-artifact, remove-artifact, session-status and background-command are reserved. The check is made on an artifact's tool name, the part of its full name after the /, so a capsule declaring murmur/delegate-task or ana/pull-artifact is refused at staging, before any artifact is pulled, with E-CAP-013. The same refusal covers an in-session pull of such a name, by manage.pull() or by pull-artifact.

A name is reserved whether or not the capsule declares the grant that would provide the tool, so adding or removing a capability never changes whether a manifest is accepted.

Shell binary names are not reserved. They come from capabilities.shell.allow, which is the operator's own list, so there is no fixed set to reserve — and a capsule may declare both an artifact whose bare name is bash, such as local/bash, and bash in its shell allowlist. Staging writes the artifact's manifest first and the shell manifest yields to it, so the inventory describes the artifact.

Dispatch resolves such a pair by precedence: a native artifact answers ahead of the shell binary, and the shell binary answers ahead of a WASM artifact of the same name.

session-status

session-status takes no input ({"type":"object","properties":{}}) and returns one JSON object, measured at the moment of the call:

{
  "time":    {"elapsed_secs": 1834, "task_elapsed_secs": 1790,
              "deadline": {"at": "2026-10-20T14:00:00Z", "left_secs": 5366}},
  "turns":   {"used": 42, "cap": "none"},
  "keep_working": {"used": 3, "left": 17},
  "done_check": {"used": 1, "left": 2},
  "context": {"used_tokens": 61234, "window_tokens": 200000, "budget_tokens": 191808,
              "budget_basis": "context_window_less_output_cap", "measured_by": "estimate"},
  "tokens":  {"input": 812345, "output": 23456, "cached": 600000, "cache_write": 10000,
              "unreported_calls": 0},
  "spend":   {"tokens": 835801, "ceiling": "none", "machine": "none"}
}

A limit that is not set reads "none". A count the runtime does not have reads "unknown". Seconds are whole, rounded down.

Field Value "none" / "unknown"
time.elapsed_secs Seconds since the session started —
time.task_elapsed_secs Seconds since the running task's task_start "unknown" outside a task
time.deadline.at When the runtime ends the session, RFC 3339 UTC: the session's effective deadline, the earliest of its own --deadline and the one it inherited from its formation or its parent time.deadline is "none" when the session has no deadline
time.deadline.left_secs Seconds until at; 0 once it has passed —
turns.used Inference calls the running task has made, across its reopens and continuations, a hook's run-inference included: the count inference.max_turns is checked against —
turns.cap inference.max_turns "none" with no cap
keep_working.used Messages lifecycle.keep_working has sent the running task, across its reopens; 0 at each task's start keep_working is "none" when the capsule does not set the option
keep_working.left lifecycle.keep_working.limit less used —
done_check.used Times lifecycle.done_check has reopened the running task because its command failed; 0 at each task's start done_check is "none" when the capsule does not set the option
done_check.left lifecycle.done_check.limit less used —
context.used_tokens The conversation's size as the compaction check last counted it context is "unknown" before the first turn, and always under transport: process
context.window_tokens The context window of the driver choice that served the last call "none" when the choice has none
context.budget_tokens The input budget that check compared it with: the input ceiling, or the window less the output cap the last call sent, whichever is smaller. Compaction fires at inference.compaction.threshold of it "none" when the choice has no window
context.budget_basis Which limit set budget_tokens: "max_input_tokens" (the driver's input ceiling) or "context_window_less_output_cap" "none" when the choice has no window
context.measured_by "actual": the provider's own count of the last call, its request plus its completion. "estimate": the runtime's own count, when that call reported none. The same values as the compaction record's tokens_before_source —
quota Under transport: process, the latest subscription window per name the harness has reported, in the order the names were first reported: [{"name": "five-hour", "used": 0.42, "resets_at": "2026-10-20T15:00:00Z", "window_minutes": 300, "state": "ok"}] Absent until the harness reports a window, and always under transport: http
quota[].name The window's name, as the driver documents it —
quota[].used Fraction of the window used, 0 to 1 Absent when the harness did not report it
quota[].resets_at When the window resets, RFC 3339 UTC to the second Absent when not reported
quota[].window_minutes The window's length in minutes Absent when not reported
quota[].state "ok", "warning" or "exhausted", as the harness reports it Absent when not reported
tokens.input, tokens.output, tokens.cached, tokens.cache_write Sums of what the provider reported over the session's inference records so far "unknown" for a member no record carried
tokens.unreported_calls inference records that carried no count —
spend.tokens Tokens the spend ceilings count for this session —
spend.ceiling inference.max_session_tokens "none" with no ceiling
spend.machine {"tokens_today": …, "ceiling": …}: this machine's day total against spend.machine_tokens_per_day "none" when the machine ceiling does not cover the session

No money amount is kept on either transport: spend is counted in tokens.

The two transports differ in where the numbers come from:

Field transport: http transport: process
turns.used Every inference call Turns the harness has closed
context An object "unknown": the harness keeps its own context
tokens The provider's usage on each call The harness's own counts for each closed turn
quota Absent The harness's latest windows, once it has reported one

The runtime reports quota and never acts on it: no window, however exhausted, stops, slows or warns about the session.

Each call writes one session_status record carrying the object returned.

The deadline notice

With a deadline, own or inherited, the agent is told once that the session is about to end. The first tool result the runtime hands the model once no more than lifecycle.deadline_notice_secs are left carries one line after its fence:

Session deadline: the runtime ends this session at 2026-10-20T14:00:00Z, in 297 seconds.
Aspect Behaviour
Margin lifecycle.deadline_notice_secs; default five minutes, or a tenth of the deadline when that is shorter, and at least one second. 0 sends none
Carried by The first tool result dispatched inside the margin, on either transport — under transport: process, the bridged tool result. A tool that ran and reported failure carries it; a refused call, or one the runtime could not run, does not
How often Once per session
A turn with no tool call Gets no notice; the deadline still ends the session
Trace One deadline_notice record

Nothing else the runtime adds to the conversation changes. What happens when the deadline is reached is described under The deadline.

background-command

A shell command that runs past lifecycle.shell_grace_secs moves to the background, and its shell call returns a work id of the form wrk_<hex>. The tool is offered only to a capsule that sets lifecycle.shell_grace_secs; without it no command moves to the background on a clock. background-command takes that id:

{"type":"object","properties":{"operation":{"type":"string","enum":["status","wait","stop"]},"work_id":{"type":"string"},"wait_secs":{"type":"integer","minimum":0}},"required":["operation","work_id"]}
operation Behaviour
status Returns at once
wait Returns when the command exits or after wait_secs seconds, whichever is first. wait_secs defaults to 600; a larger value is taken as 600, and 0 returns at once. A cancel of the task ends the wait at once, recorded as phase background_wait
stop Sends SIGTERM to the command's process group, SIGKILL 2 seconds later if the command has not exited, then waits up to 3 seconds more for it to finish. A command that already exited is sent nothing

A stop reaches the command and every process in its process group. A process that left the group, such as one started with setsid, is not reached; the session's end kills it.

Each operation returns the same text, Passed whatever the command's exit code:

work_id: wrk_0199a3f1c2d47e8ab5c6d7e8f9a0b1c2
state: exited
elapsed_secs: 312
waited_secs: 41
exit_code: 101
status: error
output: /home/dev/.murmur/records/home/dev/proj/ses_01k4m2y7xq8f9v3n5r7t9w1c2e/logs/wrk_0199a3f1c2d47e8ab5c6d7e8f9a0b1c2.log
output_bytes: 48213
output_shown: last 16384 bytes

<the last 16384 bytes of output>
Line Value Present
work_id The work id Always
state running, exited, or stopped for a command that ended after a stop reached it Always
elapsed_secs Whole seconds from the command's start to now, or to its exit Always
waited_secs Whole seconds this call waited wait only
exit_code The exit code; 128 + signal for a signal kill exited and stopped
status ok for a zero exit with no resource limit and no wait error, error otherwise exited and stopped
wait_error A failure in waiting for the command, or in writing its log When there was one
output The absolute path of the command's log in the session's records directory, which holds all of its output Always
output_bytes Bytes of output in the log so far Always
output_shown all, or last 16384 bytes when the output is longer Always

The output, both streams in the order read, follows a blank line. A resource limit the command was stopped by, and processes left holding its output, are reported in a note after the untrusted fence, as on a foreground shell result. A stopped command has no resource limit attributed.

A work id is valid for the rest of the session that issued it. An id from another session, a resumed one included, is unknown in this one. A command still running when the session ends is killed with its process group, on every host. A task that ends while commands are still running names them in runningCommands and on its task_end record.

Under task_acceptance: queue with after_task: sleep, a command that exits also arrives as a completion task, unless background-command already returned its exit.

Refused input Message
An id this session never issued no background command with work id `<id>` in this session
An id not of the form wrk_<hex> Names the value and the form
No work_id Names work_id
An operation outside status, wait, stop Names the value and the accepted operations
A wait_secs that is not a whole number of seconds Names the value

A refused call writes no record. Each answered call writes one background_command record.

switch-driver

Aspect Behaviour
Input {"driver": "<choice name>"}. The schema lists every declared choice
Description Lists every choice with its model and driver
Accepted The next agent-loop inference call, and every one after it, is served by the choice. The tool result names the previous and the new choice
Refused Returned to the model as a tool error, with the same checks and messages a controller gets: an undeclared name, or a choice whose credential cannot produce a value now. Nothing switches
Policy The call passes the same on-tool-call decision point as every tool call, so a policy hook can deny it
Plans Not callable from a plan step
Trace control_change or control_refused with principal: "agent" and no token_id; control_applied at the first call that uses the choice

The runtime answers the call in-process: it presents no credential and never reaches the control surface or its token. What a switch carries across is in Switching drivers.

pull-artifact

pull-artifact exists for a capsule that declares capabilities.install and runs the http agent loop. A capsule under transport: process is refused at load, because the harness owns its tool list.

Aspect Behaviour
Input {"artifact": "<value>"}, required. <scope>/<name> pulls the newest version that fits this mur; <scope>/<name>@<version> pulls that version; a pattern ending in * lists instead of pulling
Description Lists the capsule's install.skill and install.tool entries, or none, and its install.from. Says a pattern lists instead of pulling, that the artifact is usable from the next turn, and that a pulled artifact was not vetted by the operator, so its text and results are untrusted
Checked The grant by tier and pattern, never a hook or a driver, the payload's hash, its WIT level, the scope's owner id against murmur.lock, and an existing pin. A refusal writes nothing
Found in The project store, then the global store. With install.from: [stores, sources], a version no store holds is fetched from the scope's GitHub source by the runtime itself and written to no store
Accepted passed; the artifact is in the tool array from the next turn, whatever inference.tool_refresh says. A tool joins the dispatch allowlist
Refused Returned to the model as a tool error opening with one token — see Refusals
Policy The call passes the same on-tool-call decision point as every tool call, so a policy hook can deny it
Plans Not callable from a plan step
Trace artifact_pulled per pull; tools_refreshed with trigger: "agent" before the next inference call

A successful pull's data, fenced under tool:pull-artifact, is followed by the runtime's note outside the fence:

<untrusted-content source=tool:pull-artifact>
{"artifact":"murmur/tool-edit","fetched_from":"global store","runtime":"tool","tool_name":"tool-edit","version":"1.0.0"}
</untrusted-content>
tool-edit is in your tool list from your next turn.
Key Value
artifact The full name
version The version pulled
runtime skill or tool
tool_name The name the model calls it by
fetched_from project store, global store or github:<owner>/<repo>
notes Present when a place could not be consulted and the pull went ahead without it

A pattern returns, under the same fence and with no note:

Key Value
pattern The pattern asked for
matches At most 50, sorted by name: artifact, runtime, description (when one is recorded), versions (newest first), newest_fitting (when one fits), held_in
omitted How many more matched
notes Why a place could not be read

A match is an artifact the pattern names that an entry of its own tier's list also names, held in an allowed place. A pattern that reaches no granted name is refused not-granted:.

Refusals

Token When
not-granted: No capabilities.install; not a full name; outside the grant; a hook, a driver, or a role that disagrees with its payload; a tool name another artifact of the session holds
not-found: No allowed place holds the name or version
does-not-fit: The payload does not fit the WIT level this mur serves, carrying E-WIT-LEVEL-001 and naming the newest version that fits
not-fetched: The source could not be read, failed a check, or answers for an owner murmur.lock does not record for the scope (E-REG-010)

A pin of the same name at another version or digest is refused with murmur.lock conflict for '<name>': ….

remove-artifact

Aspect Behaviour
Input {"artifact": "<value>"}, required: the full name or the tool name
Accepted passed, data {"artifact": "<full name>", "removed": true}, then the note <tool name> is gone from your tool list from your next turn; a call to it is refused. The lock entry, tools/<tool name>/ and the allowlist entry are gone
Not installed passed, data {"artifact": "<name>", "removed": false}, no note
Refused A tool error opening not-removable: for a hook, a driver, an artifact murmur.yaml declares, or a pin with origin: operator; not-granted: with no capabilities.install
In-flight calls The agent loop runs a turn's calls one at a time, in order: a call listed before the removal completes, one listed after it is refused with tool '<name>' was removed from this session; it can no longer be called
Policy The same on-tool-call decision point as every tool call
Plans Not callable from a plan step
Trace No event of its own; tools_refreshed with trigger: "agent" and the name in removed before the next inference call

call-member

call-member exists for a member of a formation launched by mur run --roster when roster.yaml lets it call at least one other member. A member the roster lets call nobody, and a session in no formation, has no such tool.

Aspect Behaviour
Input {"member": "<name>", "task": "<text>"}, both required. The schema's member is an enum of the members this one may call, in roster order
Description Tells the model that task is the whole of what the member is told, so any file content it needs goes in the task text; that the call returns at once with a call id, started when the member holds the task or busy when it has no room, in which case the runtime keeps offering the task until the member takes it or the call's deadline passes; that the answer arrives in the conversation, naming the call id, after the turn ends; and that a second call to a member before its answer has arrived is refused
Sent One SendMessage to the member's door, carrying task as one text part, the formation token, and the calling task's trust class with origin peer. The model never sees the door's address or the token
Egress The member's door is checked against this capsule's own capabilities.network.allow, which must list localhost — see Giving a member work. Without it, every call fails and staging prints W-RUN-008
Trace One member_call_busy per offer the member turns away busy, member_call_start when the member holds the task, and one member_call per call once it is accounted for

What a call returns

A call ends its tool call in one of four ways:

Result When data
passed, summary Called <member>: started The member's door answered with a task id and a state that is not terminal {"call_id": "mcl_…", "member": "<name>", "status": "started", "task_id": "<the member's task id>"}
passed, summary Called <member>: busy The member's door answered TASK_STATE_REJECTED with the status message task rejected: capsule is busy. The runtime keeps offering the task — see A busy member {"call_id": "mcl_…", "member": "<name>", "status": "busy", "output": "<member> is busy with other work and did not take the task"}
failed, summary Called <member>: rejected The member's door answered TASK_STATE_REJECTED for any other reason, such as its session closing, which call-member reports as rejected {"call_id": "mcl_…", "member": "<name>", "status": "rejected", "output": "<member> did not take the task: <reason>"}
failed, summary Called <member>: failed The call was refused, the door could not be reached, the door answered an error status or a JSON-RPC error, or the member answered with another terminal state {"call_id": "mcl_…", "member": "<name>", "status": "failed", "output": "<why>"}

data reaches the model fenced under tool:call-member. Each result adds the runtime's own note on the line after the closing marker, outside the fence:

<untrusted-content source=tool:call-member>
{"call_id":"mcl_01a1…","member":"worker","status":"started","task_id":"tsk_…"}
</untrusted-content>
[call-member] worker is now working on call mcl_01a1…. Its answer is not in this result and no tool fetches it: the runtime adds it to this conversation after you end your turn. Unless you still have work to hand to a different member, end your turn now by replying without calling a tool. Calling worker again before its answer arrives is refused.
Result Note
started [call-member] <member> is now working on call <call_id>. Its answer is not in this result and no tool fetches it: the runtime adds it to this conversation after you end your turn. Unless you still have work to hand to a different member, end your turn now by replying without calling a tool. Calling <member> again before its answer arrives is refused.
busy [call-member] <member> is busy with other work and has not taken call <call_id> yet. The runtime keeps offering it the task for up to <N>s and adds <member>'s answer, or word that it stayed busy, to this conversation after you end your turn. Unless you still have work to hand to a different member, end your turn now by replying without calling a tool. Calling <member> again before then is refused. <N> is the call's deadline in seconds
rejected, failed [call-member] Call <call_id> to <member> ended <status>, with no answer from <member>. Do not present an answer of your own as <member>'s. For a task another formation member sent, followed by If you have no answer to give without <member>, call end-without-answer with the reason: the runtime then tells <caller> plainly that you gave none.

A door's TASK_STATE_REJECTED answer reads as one sentence:

The door's status message output
task rejected: capsule is busy <member> is busy with other work and did not take the task
Any other <member> did not take the task: <message>, with a leading task rejected: dropped
None <member> did not take the task

A busy member

A busy member's call is outstanding from the moment the tool call returns, and the runtime, not the model, offers the member the task again:

  1. Each offer is a new SendMessage carrying the same task text.
  2. The second offer comes 1 second after the first refusal. Each later wait doubles, up to 8 seconds, plus up to 250 ms of jitter.
  3. Each offer the member turns away busy writes its own member_call_busy.

The call then ends in one of these ways:

What happens The call
The member takes the task Writes member_call_start and runs as a started call does, against the same deadline
The member answers TASK_STATE_REJECTED for another reason, or another terminal state Ends rejected or failed respectively, with the sentence What a call returns gives
Two offers in a row get no answer Ends unreachable
lifecycle.delegation_deadline_secs passes while the member is still busy Ends rejected, never held: <member> stayed busy with other work for the whole <N>s this call may wait and never took the task; it was offered the task <k> times. Nothing was done on it.
The calling task ends between offers Ends abandoned, never held: the calling task ended before <member> took the task; <member> was busy and was never handed it
The calling task ends while an offer is on its way, and the member takes the task Ends abandoned with the member's task id: the calling task ended just after <member> took the task; a cancel was sent to <member>. The runtime sends that task CancelTask
The calling task ends while an offer is on its way, and the member turns it away busy Writes that offer's member_call_busy, then ends as when the calling task ends between offers
The calling task ends while an offer is on its way, and the member does not answer within 6 seconds Ends abandoned: the calling task ended while an offer to <member> was in flight; <member> may hold the task

A calling task that ends while an offer is on its way waits up to 6 seconds for the member's answer to that offer before it ends. No offer is sent after the calling task ends.

Only a refusal with exactly the message task rejected: capsule is busy is offered again; any other refusal ends the call in the same turn.

Four calls are refused as a tool error, with nothing sent and nothing recorded as a member call:

Call Error
A member the roster does not let this capsule call Names the members it may call
A call made while no task is running Says the tool is answered only while a task runs
A call made after end-without-answer was accepted in this task 'call-member' makes no call from a task that is ending without an answer.
A member that already holds a call from this task whose answer has not been delivered One of the two texts below, naming the earlier call
The earlier call's answer Error text
Has not arrived <member> has not yet answered call <call_id> from this task, so nothing was sent. Its answer reaches you only after you end your turn: end your turn now by replying without calling a tool. Once that answer has arrived you may call <member> again with new work.
Has arrived and waits for the turn to end <member> has already answered call <call_id>, so nothing was sent. The answer reaches you as soon as you end your turn: end your turn now by replying without calling a tool.

Once its answer has been delivered, a member can be called again with new work. Calls to different members in one turn never refuse each other.

How the answer arrives

  1. The model ends its turn with a turn still left.
  2. The task waits until every call it made has ended. While any call is outstanding, the task's stream shows a TASK_STATE_WORKING status waiting on call-member: <n> call(s) outstanding: once when the wait starts, and again with the new count each time an answer arrives while others are still out. When every answer arrived during the turn, the task does not wait and shows no such status.
  3. Once every call has ended, the task continues with one new user message holding every answer. The stream shows continuing with <n> member answer(s), and the trace records one task_continued.
  4. The model reads the answers and answers in turn, or calls again.

One round of calls costs one continuation, however far apart the answers arrive. The wait is bounded by each call's own deadline, lifecycle.delegation_deadline_secs: a member that never answers holds the round until its call ends timed_out.

Each answer in that message is one header line naming the call id, the member and how the call ended, then the member's output fenced under member:<name>, whatever the calling task's trust. The message ends with the runtime's own lines, outside every fence:

[call-member] call mcl_01a1… to worker ended completed:
<untrusted-content source=member:worker>
WORKER-0123
</untrusted-content>

[call-member] call mcl_01a2… to critic ended timed_out, with no answer from critic:
<untrusted-content source=member:critic>
critic did not answer within 600s. …
</untrusted-content>

[call-member] No answer came from: critic (call mcl_01a2…, timed_out). What you asked of them has not been done by them: do not present an answer of your own as theirs.

[call-member] Every call this task made has ended. Answer the task with the answers you have and say plainly which part has no answer, or call a member again if another attempt could succeed.
Ending Header
completed [call-member] call <call_id> to <member> ended completed:
Any other [call-member] call <call_id> to <member> ended <status>, with no answer from <member>:

A call ends no_answer when the member ended its task with end-without-answer: its task is TASK_STATE_FAILED and its GetTask result carries metadata.murmur.noAnswer: true. A member's GetTask result may also name, in metadata.murmur.noAnswerBelow, the members further down that gave it no answer. The runtime reads those names only for a completed or no_answer call, keeps at most 8, and keeps only a roster member name with a status other than completed.

A member has no answer when its latest call in this task did not end completed, whether that call ended in its own tool call or in a continuation. Each such member is named, with that call's id and status, in one line after every fence:

[call-member] No answer came from: <member> (call <call_id>, <status>), <member> (call <call_id>, <status>). What you asked of them has not been done by them: do not present an answer of your own as theirs.

A member that reported members further down with no answer is named in the further-down form:

<member> (call <call_id>, <status>; further down, no answer came from <name> (<status>), <name> (<status>))

A member whose latest call ended completed while it reported members further down with no answer has answered with a gap. Its answer is fenced as usual, and it gets a line of its own after the no-answer line:

[call-member] <member> (call <call_id>) answered without an answer from <name> (<status>), <name> (<status>): any part of its answer that stands in for theirs is <member>'s own, not theirs.

A later call to the same member that completes with nothing missing below it removes it from both lines. The message's last line:

Members with no answer, or with a gap Last line
None [call-member] Every call this task made has ended, and the answers are above. Answer the task with them now; call a member again only to give it new work.
One or more [call-member] Every call this task made has ended. Answer the task with the answers you have and say plainly which part has no answer, or call a member again if another attempt could succeed.

For a task another formation member sent, the second line continues with If you have no answer to give, call end-without-answer with the reason instead: the runtime then tells <caller> plainly that you gave none. The entry member's task has no formation caller, so its lines never name the tool.

Ending Output
completed The member's answer: its task's response artifact
no_answer The member's reason, its task's status message, or <member> ended its task without an answer
failed, canceled, rejected The member's task's status message, or for a member that stayed busy, the runtime's sentence from A busy member
timed_out The member did not answer within lifecycle.delegation_deadline_secs; it was not cancelled and may still be working
unreachable The member's door stopped answering

Under transport: process, the harness calls call-member through the runtime's loopback tool server, and the message holding the answers reaches the harness as the prompt of a resume of its same session.

Output is cut at 64 KiB. Continuing with answers is not a reopen: it spends no lifecycle.max_task_reopens, writes no task_reopened, and runs before the task's on-task-end hooks. Its turns count against inference.max_turns.

The task waits for answers only when the model ends a turn with a turn still left. Every other ending accounts for the task's calls at once, and the task ends as its attempt ended:

The attempt Calls still outstanding Answers that arrived
The model ends a turn with a turn left Waited for, as above Delivered in the continuation
The model ends its last allowed turn Not waited for: each is recorded abandoned Recorded delivered: false
Fails, runs out of turns, is cancelled, or is stopped by SIGTERM or its formation ending Each is recorded abandoned Recorded delivered: false

A task cancelled while it waits ends TASK_STATE_CANCELED. A task with delegate-task delegations outstanding as well waits for both kinds together and continues once neither has anything outstanding, with every answer and every delegation outcome in one message, answers first — see How the outcome arrives.

Bound Value
How long a call may take, offering a busy member the task and waiting for its answer together lifecycle.delegation_deadline_secs, default 600 seconds, or MURMUR_DELEGATION_TIMEOUT_SECS — the bound delegations use
How often the member's task is read Every 500 ms, through the member's GetTask
Unreachable Two reads, or two offers, in a row that get no answer

The runtime cancels a member's task only when the member took it just after the calling task ended, as the busy-member table shows. A call that times out, or is abandoned while the member works on the task, leaves the member's task running until it finishes or the formation ends.

end-without-answer

end-without-answer exists exactly where call-member does. It ends the running task without an answer, when the member has none to give — usually because a member it called gave it none. The runtime then tells the member that sent the task, in its own words, that no answer came.

Aspect Behaviour
Input {"reason": "<text>"}, required. The reason is the task's status message
Description Tells the model to use it instead of replying when it has no answer to give, usually because a member it called gave it none; that reason is passed to the member that sent the task; that the runtime tells that member plainly no answer came and names each member called that gave none; and that it is refused while a call is still out and for a task no formation member sent
Accepted passed, summary Ending task without an answer, data {"status": "ending", "caller": "<caller>"}, then the note [end-without-answer] This task ends without an answer once this turn's tool calls finish, and <caller> is told you gave none. The other tool calls of the same turn still run
Policy The call passes the same on-tool-call decision point as every tool call
Plans Not callable from a plan step

It is refused as a tool error, checked in this order:

Case Error text
The session is no formation member with a callee 'end-without-answer' is answered only for a formation member that roster.yaml lets call another; this session is not one
No task is running 'end-without-answer' is answered only while a task runs; this session is running none
No formation member sent the task, as for the entry member's task 'end-without-answer' ends only a task another formation member sent; no formation member sent this one. Reply in text, saying plainly which part has no answer.
A call this task made is being sent, is outstanding, or has an answer not yet delivered Call <call_id> to <member> is still out; its answer arrives after you end your turn. Call end-without-answer only when no call is left to wait for.
reason is missing or blank 'end-without-answer' needs a reason: say in a sentence why you have no answer.
The task has already accepted one This task is already ending without an answer.

How the task ends

Once the turn's tool calls finish, the attempt ends with no further inference call. Under transport: process, an attempt whose harness kept going after the call and finished ends the same way.

Surface What it shows
A2A state TASK_STATE_FAILED, with the reason as its status message
GetTask metadata.murmur.noAnswer: true, and metadata.murmur.noAnswerBelow naming each member further down this task had no answer from
out/result.txt in the session's records directory no answer: <reason>
task_end.exit_status and the on-task-end exit status no_answer
task_failed None is written
The caller's call Ends no_answer — see How the answer arrives
The launch A no_answer task never decides session_end.exit_status or the launch's exit code

An on-task-end hook may reopen the task; the reopened attempt may answer, or end without an answer again. A caller that is not a formation member reads an ordinary TASK_STATE_FAILED task with a status message.

A task that answers in text while a member it called gave no answer completes as usual. Its GetTask result carries metadata.murmur.noAnswerBelow naming those members, and its caller reads its answer with a gap line.