Skip to main content

Module spawn

Module spawn 

Source
Expand description

spawn — the model’s only route to a command and to a subagent.

ADR-0016. Two doors existed for do something I cannot do by thinking — mentra’s shell builtin and its task intrinsic — and they were governed as if they were unrelated: two side-effect levels, two names at basis’s ApprovalGate, and therefore two rule namespaces for what an operator thinks of as one question. This is that one question, with one name on it.

§The shape the model sees

One string. A leading ! means run this; anything else is a task for a subagent. A prompt that genuinely starts with ! is written !!. The prefix is surface: it is read exactly once, at the boundary, and everything after that dispatches on the typed mode — which is why the sugar costs no type confusion between the approver, the rule store and the audit trail.

A command may also say where: !@<target> <command> names an executor to run it on (ADR-0021) — though nothing currently registers one; see docs/targets.md. One more fact on the same act rather than a second tool, because a second tool would be a second name at the gate and a second rule namespace — the two doors ADR-0016 closed, rebuilt by hand. The prefix is read by the same parser, in the same pass, and the target it leaves behind is a typed field like the mode beside it.

§What the approver sees

ToolExecutor::authorization_preview is overridden rather than left at its default, which merely restates the static descriptor. A command reports ToolSideEffectLevel::Process and a delegation reports ToolSideEffectLevel::LocalState; both are consequential, so is_consequential never waves either through under the reads-are-never-asked rule. The preview’s parser owns {mode, body, target}; after the tool context resolves the working directory, structured_input carries {mode, body, cwd, target} — the thing an approver renders, and the thing mentra globs a remembered rule’s pattern against (RuleStore::matching_rule). A RuleKey { tool_name: "spawn", pattern } is therefore a command allowlist expressible as data. A delegation whose child policy (ChildSpec, D4) overrode something adds a child key describing what the child will be — roster, model, whether the system prompt is replaced — and only then, so the four-key shape every existing rule was written against is untouched on a runtime with no policy.

Order matters and is mentra’s, not basis’s: the orchestrator builds this preview and puts it to the authorizer before it calls execute_mut (ToolRuntime::execute_registered_tool), so nothing here runs ahead of the answer. A preview that returns Err is a refusal in its own right, reaching the model as Tool execution denied: … without the approver — or the rule store — ever being consulted. That is where the depth guard lives, because a structural floor is not a thing an allow-rule should be able to lift.

§What this is not

Approval is policy, not confinement (ADR-0013). An approved command runs with the full authority of this process, and --no-shell remains the only thing that shuts commands off entirely: it sets RuntimePolicy::allow_shell_commands(false), and mentra’s policy refuses on the same path spawn calls — after this tool has been authorized, before anything executes.

Structs§

ChildContext
What spawn knows about a delegation when it consults the child policy.
ChildSpec
Which of a child’s three inherited facts a policy overrides.
ReachPolicy
What a host allows reach to be, and what it means when the model says nothing (ADR-0037 §3).
SpawnInput
Every fact the spawn input parser owns: mode, normalized body, and optional target — where a command runs, or which remote agent a task is for. The working directory is not model input; the tool context resolves it later when building the authorization preview.
SpawnTool
basis’s own tool, and the first tool basis registers on a runtime it builds.

Enums§

Reach
Which agents @<name> may reach.
SpawnMode
Which of the two acts a call turned out to be.

Constants§

DEFAULT_DELEGATION_DEPTH
How many levels of delegation spawn will start, absent a RuntimeBuilder::with_delegation_depth override.
SPAWN
The name the model calls, an operator writes in a rule, and a .basis/hooks.json entry matches on.
SPAWN_INPUT_FIELD
The field the model fills in. One string, because the tool takes one thing.
SPAWN_REACH_FIELD
The one optional field beside it: which agents @<name> may reach (ADR-0037 §3). Typed, never read out of the string.

Functions§

classify_spawn_input
Classifies the lexical spawn mode without validating that the body is executable.
parse_spawn_input
Reads a tool call’s JSON input into a typed call, or says why it could not.