# `Nous.CodeMode`
[🔗](https://github.com/nyo16/nous/blob/v0.17.1/lib/nous/code_mode.ex#L1)

Mode resolution and the visible tool set for Code Mode.

Code Mode lets the model write a *program* that calls tools — looping,
branching, fanning out — in one round trip, instead of emitting a chain of
individual tool calls. The program runs on a `Nous.CodeRuntime` provider; the
tools it may call are the same ones a direct call would reach, through the
same permission policy.

## Modes

  * `:native` — no Code Mode. The model sees the tools and calls them
    directly. This is what every existing agent does.
  * `:code` — the model sees **only** `run_code`. Tools are reachable from
    inside a program and nowhere else.
  * `:both` — the model sees the tools *and* `run_code`, and picks per turn.
    A single `file_read` is already ideal as a native call; a ten-step search
    is not.

Resolution order: the agent's `:code_mode` field, then
`config :nous, :code_mode`, then `:both`. As with
`Nous.Sandbox.Policy.default_mode/0`, a configured value may be a string (it
often comes from an env var) and is matched against a literal whitelist —
never `String.to_atom/1`.

## Degrading without a runtime

`:both` needs a runtime to be worth advertising: with none configured it
behaves as `:native` rather than showing the model a `run_code` that can only
fail, since the native path is right there. `:code` is *not* degraded — an
operator who asked for code mode gets `run_code`, and calling it without a
provider returns an actionable error (see `Nous.Tools.RunCode`) instead of
silently reverting to a mode they turned off.

Configure a provider with:

    config :nous, :code_runtime, {Nous.CodeRuntime.JS, timeout_ms: 30_000}

`Nous.CodeRuntime.JS` ships here and needs the optional `:tyrex` dependency;
any module implementing `Nous.CodeRuntime` works in its place.

## `run_code` sits outside the restriction layers

`visible_tools/4` injects `run_code` **after** `Nous.Permissions.filter_tools/2`
has run. A restriction that denies every tool still leaves Code Mode's entry
point reachable — otherwise a deny-all policy would not restrict the agent,
it would mute it. That is not a hole: `run_code` still dispatches through the
whole pipeline, so a pre-tool hook or a permission plugin can inspect the
program text before it executes, and every tool the program calls is filtered
by the same policy through `bindings/4`.

## One permission mechanism

`bindings/4` derives the program's callable surface from
`Nous.Permissions.filter_tools/2`. A granted tool becomes a real closure,
already scoped to the caller; a denied one becomes a stub that returns an
error naming the tool. Both appear, so a program that calls a denied tool
reads a comprehensible refusal instead of crashing on an undefined function —
and there is exactly one place that decides what may run.

# `dispatch`

```elixir
@type dispatch() :: (Nous.Tool.t(), map(), Nous.RunContext.t() -&gt;
                 {:ok, term()} | {:error, term()})
```

A tool dispatch: what actually runs a sub-call made from inside a program.

Sub-call scheduling, ordering and session-event logging live behind this
function, which is why `bindings/4` takes it as an option instead of calling
the executor directly.

# `mode`

```elixir
@type mode() :: :native | :code | :both
```

How the model reaches tools this run.

# `await_timeout_ms`

```elixir
@spec await_timeout_ms() :: pos_integer()
```

How long one `run_code` call may take, in milliseconds.

This is the caller's patience, not a budget the program can ask for: real
budgets — wall clock, memory, instruction count — are provider configuration,
validated when the provider is configured.

# `bindings`

```elixir
@spec bindings(
  [Nous.Tool.t()],
  Nous.Permissions.Policy.t() | nil,
  Nous.RunContext.t(),
  keyword()
) :: [
  Nous.CodeRuntime.Binding.t()
]
```

The bindings a program is given: one global, one function per tool.

Granted tools — those that survive `Nous.Permissions.filter_tools/2` — get a
closure that dispatches the real tool with the caller's context. Denied tools
get a stub returning an error naming the tool. Both are present, so the
program never hits an undefined function and never gets to distinguish
"denied" from "failed" by the shape of what it caught.

## Options

  * `:dispatch` — a `t:dispatch/0` replacing the default, `direct_dispatch/3`.
    This is where a sub-call scheduler plugs in: serialisation,
    `max_parallel`, argument snapshots and session-event logging all live
    behind it.

`direct_dispatch/3` is the default because it is the only thing a caller who
owns no `Nous.Agent.Context` can honestly do — but it is *not* what the
shipped Code Mode path uses. `Nous.Tools.RunCode` starts a
`Nous.CodeMode.Scheduler` for each run and passes
`Nous.CodeMode.Scheduler.dispatch_fun/1` here, so a real program's sub-calls
are ordered by one lane and audited into the session log. Calling `bindings/4`
with no `:dispatch` gets the unscheduled path: right for a test or a direct
caller, wrong for a run.

# `collapse_message`

```elixir
@spec collapse_message(String.t()) :: String.t()
```

What the model is told when a call collapses. Names the way back in, so the
next turn is a `run_code` call rather than the same mistake.

# `collapsed?`

```elixir
@spec collapsed?(mode(), String.t(), map()) :: boolean()
```

Whether a model-direct call to `tool_name` collapses to "unknown tool".

Under `mode: :code` the model was shown exactly one tool, so a call to any
other one — hallucinated, or replayed from a transcript recorded in another
mode — can only fail. It is refused here, *before* pre-tool hooks run: a
guard must never be asked to approve a call that cannot execute, because an
approval it grants is a decision about nothing.

A sub-dispatch made from inside a program carries a parent token (see
`tag_transport/2`) marking it as transport rather than model, and is let
through.

# `default_mode`

```elixir
@spec default_mode() :: mode()
```

The application-wide default mode.

# `direct_dispatch`

```elixir
@spec direct_dispatch(Nous.Tool.t(), map(), Nous.RunContext.t()) ::
  {:ok, term()} | {:error, map()}
```

Run one sub-call directly, with no scheduling in front of it.

This is the default `t:dispatch/0`, and the one `Nous.CodeMode.Scheduler` sits
on top of in the shipped `run_code` path rather than calling
`Nous.ToolExecutor.execute/3` itself: the executor has a **three**-shape
return (`{:ok, result}`, `{:ok, result, %ContextUpdate{}}`,
`{:error, reason}`) and a dispatch must hand back two. Folding that here, in
one place, is what keeps a tool that returns a context update from looking
like a contract breach to everything downstream.

The context update is dropped, loudly. There is no agent context at this
depth to merge it into — a `%Nous.RunContext{}` is not one — so the honest
options are "drop it and say so" or "lie". That is about a *tool's* deps
update: applying those from concurrent sub-calls into a context the runner
also owns is the two-writers hazard. The scheduler's own bookkeeping events
are a different thing and do reach the session log, as `:log_event`
operations on the update `Nous.Tools.RunCode` returns.

# `language`

```elixir
@spec language() :: Nous.CodeMode.Sdk.language()
```

The language the configured provider runs, as an SDK language.

Falls back to `:javascript` when nothing is configured or the provider names
a language this repo cannot generate an SDK for — a provider must not be able
to break tool visibility by reporting an exotic language.

# `modes`

```elixir
@spec modes() :: [mode()]
```

The three modes.

# `new`

```elixir
@spec new(term()) :: mode()
```

Normalise a mode given as an atom or a string.

Raises `ArgumentError` on anything else, at the point of configuration rather
than mid-run.

## Examples

    iex> Nous.CodeMode.new(:code)
    :code

    iex> Nous.CodeMode.new("both")
    :both

# `new_parent_token`

```elixir
@spec new_parent_token() :: String.t()
```

Mint a parent token for one `run_code` execution.

# `resolve`

```elixir
@spec resolve(map() | nil) :: mode()
```

The mode for an agent: its own `:code_mode`, else `default_mode/0`.

Takes anything with a `:code_mode` key, so it works on an agent, a plain map
in a test, or `nil`.

## Examples

    iex> Nous.CodeMode.resolve(%{code_mode: :code})
    :code

    iex> Nous.CodeMode.resolve(nil)
    :both

# `run_code_name`

```elixir
@spec run_code_name() :: String.t()
```

The name of the transport tool.

# `run_code_tool`

```elixir
@spec run_code_tool([Nous.Tool.t()], [Nous.Tool.t()], keyword()) :: Nous.Tool.t()
```

Build the `run_code` tool for this request.

The generated SDK rides in the tool's description, which is where the model
reads it: under `mode: :code` it is the only declaration of the tools that
reaches the prompt at all. That also makes `Nous.CodeMode.Sdk`'s byte
stability load-bearing — this string is part of the prompt prefix of every
request in the run.

`retries: 0` is deliberate. A program has side effects by the time it fails;
re-running it because the model's code threw would repeat them.

# `runtime`

```elixir
@spec runtime() :: {:ok, {module(), term()}} | {:error, String.t()}
```

The configured runtime provider, or an error message explaining what to do.

Accepts `{module, config}` or a bare module (config defaults to `[]`), and
checks the module actually implements the behaviour, so a typo is reported
here rather than as an `UndefinedFunctionError` inside a tool call.

# `runtime_configured?`

```elixir
@spec runtime_configured?() :: boolean()
```

Whether a usable runtime provider is configured.

# `tag_transport`

```elixir
@spec tag_transport(map(), String.t()) :: map()
```

Tag a synthesised tool call as transport, so `collapsed?/3` lets it through.

# `transport_call?`

```elixir
@spec transport_call?(term()) :: boolean()
```

Whether a tool call was synthesised by a running program rather than sent by
the model.

# `visible_tools`

```elixir
@spec visible_tools(mode(), [Nous.Tool.t()], [Nous.Tool.t()], keyword()) :: [
  Nous.Tool.t()
]
```

The tool set the model sees, given `mode`.

`all_tools` is the set *before* the permission policy ran and `granted` the
set after; both are needed because the SDK declares what the program may call
(granted) while its bindings also stub what it may not (all).

Injection happens here, after the filter, on purpose — see the moduledoc.

## Options

  * `:policy` — the `Nous.Permissions.Policy` in force, carried into the
    bindings so the program is filtered by the same mechanism.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
