Map: exarch / tools
ral is exarch’s tool. Every other harness affordance — spawning,
messaging, cancelling, scheduling, replying, and reading a reply — is a ral
builtin, not a provider-advertised Tool, reached by writing ral inside
ral itself, per the
agent-tool-to-exarch-builtin
migration; see builtins for the verbs and
engine-protocol for the desk they speak
through.
Two files hold the seam that makes this parametric. provider/tools.rs is the
wire half: a ToolSpec is a static record — name, description, schema fn —
(RAL, THINKING; RAL_TOOL names the one every agent is offered), and
Toolset is a Copy slice of them: Toolset::offered(thinking) is ral
alone or ral plus thinking; Toolset::default() is empty (--chat).
agent/tools.rs is the dispatch half: dispatch matches a name to its
module. The fleet’s Launch carries one Toolset, and both provider.complete
(which puts Toolset::wire() on the request) and Avatar::invoke (which asks
Toolset::contains before it dispatches) read it, so what was advertised and
what is recognised cannot disagree; an unadvertised name earns unknown tool and a Forensic::Error.
Every fork and desk spawn inherits its parent’s set verbatim. Malformed input is
answered through the shared required_str/input_error (agent/tools.rs), in the same words
for every tool; a missing field is answered with the fields the call did carry, so the model
can see what it sent.
ral(agent/tools/ral.rs) — the one call that crosses the provider boundary: evaluate ral source against the session shell, synchronously, throughAvatar::raland `run_shell`. Its input is a requiredcommand(the ral source) and a required one-linedescription(shown on the rail; the fullcommandopens in the collapsible tool-call block; oversize descriptions are truncated, never rejected). An optionaltimeout_secsbounds the call, defaulting toCALL_TIMEOUT_SECS(60s) — a default, not a cap: raise it for a command known to run long, rather than spawn-then-wait, and keepspawnfor work the model can overlap. A call the wall cuts short keeps what landed before it, so its stderr carries the engine’s span, the remedy, an audit of what already stands, and thespawned workers still running past the wall, each named#id (title)soawait !{worker N}reaches it whether or not its binding landed (shell-eval). Every accepted or malformed call emits aDisplay::ToolCallfollowed by aDisplay::Verdictaddressed by the call’sBlockId, so a verdict never has to search backward for its tool row.Verdict::of(content, failed)is the result’s line count andfailed— rejected, raised, nonzero, or severed — which the view fold settles onto the call, and the TUI draws a failed call’s script in solid red; the result text itself is recorded once, inProtocol::ToolResults. Malformed JSON uses<invalid input>as the call label but still gets a paired diagnostic verdict; if the call row itself cannot be appended, the seam reports a transient fault and cannot invent a verdict target.thinking(agent/tools/thinking.rs) — offered only under the hidden--thinking-toolflag: a relay whose one field,thought, is recorded as a singleDisplay::Thinking— the same lane the provider’s own reasoning commits on, so a thought wears the∴rail, the drained ink, and the/thinkingdial (newline-terminated, since the view fold grows one thinking block by every thinking record that follows) — and answered with the barerelayed. Nothing else happens — no call row, no model-view twin — so a model may narrate between calls without ending its turn. A record failure is a seam fault, not a tool error: the model still gets its acknowledgement.agent/spawn.rs— not a tool module but the spawn spine every launch shares:spawn_async,AsyncSpawn,SpawnedChild. The attended roots (main, a resumed one, a/newor/branchroot hatched byspawn_root) and the desk’sexarch-agents `starthandler build on it — either arm of it, in-process or across a wire — so a root and a harness-spawned child run one mechanism (agents, agent).
The harness verbs are answered by the ExarchDesk (exarch/src/agent/desk.rs),
installed per ral call and reached through shell.enquire(...) from the
builtin’s body; acting verbs emit Display::HarnessCall/Forensic::HarnessResult and, on
the arm where the act genuinely landed, file it in the call’s act ledger for the
audit every raise owes the model (shell-eval).
They are rendered as acts — verb, subject, payload rows that never fold into
an observation run
(harness-calls-are-acts; spawns
additionally derive a child tab) — while listings stay silent since
their value is the returned record, the whole read-only
`exarch-transcript family among them. There is no Gate/tools_for axis any
more — a fresh model never even sees a verb the desk would certainly refuse:
agents is dropped from the per-agent builtin index when the agent neither
spawns nor returns (its `reply needs only a returning role, so a fuelless
returning leaf keeps the verb), and the
self-wakeup family when the agent lacks the schedule grant (agent/index.rs’s
BuiltinIndex, resolved once against the boot shell), while authority
itself is still enforced only at the desk, never by omission.