Code Mode's transport tool: runs a model-authored program that calls tools.
One run_code call replaces a chain of individual tool calls — the program
loops, branches and fans out in a single round trip, and only what it logs or
returns re-enters the conversation. The tools it may call are declared in the
SDK carried in this tool's description (see Nous.CodeMode.Sdk), and are
reached through bindings derived from the same permission policy that governs
a direct call (Nous.CodeMode.bindings/4).
The runtime provider
Nous.CodeRuntime.JS ships in this repository and runs programs in an
embedded V8 isolate. It needs the optional :tyrex dependency:
{:tyrex, "~> 0.4"}
config :nous, :code_runtime, {Nous.CodeRuntime.JS, timeout_ms: 30_000}Any other provider behind Nous.CodeRuntime works the same way. With none
configured this tool returns a clear error naming the configuration it needs;
it does not crash, and it does not pretend to have run anything. mode: :both
quietly behaves as :native when nothing is configured, so that error is only
reachable when an operator explicitly asked for mode: :code.
Approval is per sub-call, not per program
Approving a run_code call approves running that program. It does not
approve whatever the program then decides to call: each sub-call to a tool with
requires_approval: true consults the approval handler on its own, with the
real tool name and the real arguments. With no handler in the context such a
tool is refused rather than run — the same default-deny Nous.ToolExecutor
applies at every other entry point.
Result shape
A run that finishes returns %{logs: [String.t()], result: json}. A run that
fails returns %{logs: [String.t()], error: %{kind: String.t(), message: String.t()}} — failure is data, not a tool error, because a program that
throws is the normal case: the model reads the failure and writes a better
program. {:error, _} is reserved for a call that never ran at all — no
provider, a missing description, a request the seam refused.
Logs are returned whether the run succeeded or failed, so a program killed by its deadline still reports what it managed to say.
Sub-calls go through one lane
Every tool call the program makes is submitted to a Nous.CodeMode.Scheduler
started for this run and torn down with it: one driver lane with a
max_parallel ceiling, an exclusivity barrier for tools that do not declare
themselves concurrency_safe?/1, and two independent argument snapshots per
call. The bindings never reach Nous.ToolExecutor on their own.
The scheduler records each sub-dispatch as a bookkeeping :tool_call event the
moment it starts. A tool is handed a %Nous.RunContext{}, which carries no
session log, so those events leave here as :log_event operations on a
Nous.Tool.ContextUpdate — this tool's third return shape — and the agent
runner appends them to the real Nous.Agent.Context. Being bookkeeping, they
project to no message: a program making forty tool calls adds forty audit
records and nothing whatsoever to what the model reads.
Approval and audit
description is required and must be non-empty. It is what a human reads in
an approval prompt and what an audit log records; an empty one turns both into
a shrug, so it is rejected. The tool's own requires_approval is false —
gate it through Nous.Permissions (it is an :execute-category tool) rather
than here, because every tool the program calls is also gated, and a second
unconditional prompt in front of the first buys nothing.
Summary
Functions
Return the full tool schema definition for introspection.
Run a program, with the tool set and dispatch this call is scoped to.
Functions
@spec __tool_schema__() :: map()
Return the full tool schema definition for introspection.
Includes parameter declarations, category, and tags.
@spec run(Nous.RunContext.t(), map(), keyword()) :: {:ok, map()} | {:ok, map(), Nous.Tool.ContextUpdate.t()} | {:error, String.t()}
Run a program, with the tool set and dispatch this call is scoped to.
Nous.CodeMode.run_code_tool/3 builds the closure that supplies opts; the
bare execute/2 above is the same call with none, which is what a direct
Nous.ToolExecutor.execute/3 outside the runner gets.
Returns {:ok, output, %Nous.Tool.ContextUpdate{}} when the program made at
least one sub-call, carrying one :log_event operation per sub-dispatch in
submission order; a program that called nothing returns the plain {:ok, output}, because an update with no operations is noise.
Options
:tools— every tool in scope before the permission policy filtered it. Granted ones become real closures, denied ones error stubs.:policy— theNous.Permissions.Policythat decides which is which.:dispatch— aNous.CodeMode.dispatch/0the scheduler runs sub-calls through; defaults toNous.CodeMode.direct_dispatch/3. It replaces what the lane calls, never the lane itself.:call_id— the model's tool-call id, so each sub-dispatch event correlates back to this call. Defaults to a minted per-run token.:runtime—{module, config}overridingconfig :nous, :code_runtime.