Context passed to tools and dynamic prompts during agent execution.
The RunContext provides access to:
- Dependencies (deps) - User-provided data like database connections
- Retry count - Number of times this tool has been retried
- Usage information - Token and request counts so far
Example with Tool
defmodule MyTools do
def search_database(ctx, query) do
# Access database from dependencies
ctx.deps.database
|> Database.search(query)
|> format_results()
end
end
# Pass deps when running agent
deps = %{database: MyApp.Database}
{:ok, result} = Agent.run(agent, "Search for users", deps: deps)The struct itself is plain data — build one directly to call a tool outside the agent loop, or to assert on what a tool would see:
iex> ctx = RunContext.new(%{database: MyApp.Database, api_key: "secret"})
iex> ctx.deps.api_key
"secret"
iex> {ctx.retry, ctx.approval_gated?, ctx.usage.total_tokens}
{0, false, 0}
Summary
Types
Decision returned by an approval handler for a single tool call.
Called before an approval-gated tool runs. Receives the same
%{name:, id:, arguments:, tool:} shape the agent runner passes its handler,
so one handler works for both entry points (id is nil outside the runner,
which is the only place a provider tool-call id exists).
Functions
Create a new run context with dependencies.
Types
@type approval_decision() :: :approve | :reject | {:edit, map()}
Decision returned by an approval handler for a single tool call.
@type approval_handler() :: (map() -> approval_decision())
Called before an approval-gated tool runs. Receives the same
%{name:, id:, arguments:, tool:} shape the agent runner passes its handler,
so one handler works for both entry points (id is nil outside the runner,
which is the only place a provider tool-call id exists).
@type t(deps) :: %Nous.RunContext{ approval_gated?: boolean(), approval_handler: approval_handler() | nil, deps: deps, retry: non_neg_integer(), sandbox: Nous.Sandbox.Policy.t() | nil, usage: Nous.Usage.t() }
Functions
Create a new run context with dependencies.
Options
:retry- Current retry count (default: 0):usage- Current usage information (default: empty Usage):approval_handler- Called before a tool withrequires_approval: trueruns. Without one, such tools are rejected (seeNous.ToolExecutor).:approval_gated?- Set by a caller that has ALREADY run its own approval pipeline (the agent runner does), soNous.ToolExecutordoes not prompt a second time. Defaults tofalse— i.e. ungated.:sandbox- OptionalNous.Sandbox.Policyfor this session. Tools that spawn subprocesses pass the context toNous.Sandbox.Policy.resolve/2, which treats this field as the session-level override: it wins overdeps[:workspace_root]and over the:sandbox_modeapplication setting.nil(the default) means unset — resolution falls back to app config.
Examples
iex> ctx = RunContext.new(%{database: MyApp.Database, api_key: "secret"})
iex> ctx.deps.database
MyApp.DatabaseAn approval-gated context carries the handler the tool executor consults
before running a requires_approval: true tool:
iex> handler = fn %{name: name} -> if name == "delete_all", do: :reject, else: :approve end
iex> ctx = RunContext.new(%{}, retry: 2, approval_handler: handler)
iex> {ctx.retry, ctx.approval_handler.(%{name: "delete_all"}), ctx.approval_gated?}
{2, :reject, false}