Skip to main content

RunContext

Struct RunContext 

Source
pub struct RunContext {
    pub tools: Arc<ToolCtx>,
    pub approver: Arc<dyn Approver>,
    pub budget: Budget,
    pub cancel: Option<CancellationToken>,
    pub phase: Phase,
    pub homeostat: Option<Homeostat>,
    pub compact_at_tokens: Option<u64>,
    pub queued_input: Option<Arc<Mutex<VecDeque<String>>>>,
    pub withheld: Arc<[String]>,
    pub hooks: Arc<HookSet>,
    pub outbox: Option<Arc<OutboxRoute>>,
    pub mailbox: Option<Arc<MailboxRoute>>,
}
Expand description

What the loop consults that is properly per-run rather than per-agent: what tools may touch, who approves the ones that aren’t read-only, and what this particular run is allowed to spend.

All three used to be fixed when the Agent was built, which is fine for a REPL and wrong for anything fanning out: an eval case that writes files needs its own copy of the fixture and permission to write to it, while the case running beside it needs neither, and a task that genuinely takes twenty steps should say so rather than depending on a global flag. Bundling them keeps the decisions together — a private workspace nobody is allowed to write to is not a sandbox, it is a confusing denial.

Fields§

§tools: Arc<ToolCtx>§approver: Arc<dyn Approver>§budget: Budget§cancel: Option<CancellationToken>

Cancels this run. None means it cannot be interrupted.

Opt-in rather than always-on, because making a run cancellable changes how the request is made: the loop has to stream in order to keep the half-written turn it was cancelled in the middle of. A batch worker that nobody can interrupt should not silently switch transports.

Sharing one token across several runs is a feature — that is how a whole batch is cancelled at once.

§phase: Phase

Which tools this run may see at all. See Phase.

§homeostat: Option<Homeostat>

Conditions sampled when this run began — see Homeostat.

Opt-in for the same shape of reason cancel is: sampling walks five stores, and more importantly mecha eval and the replay probes must not read live machine state. A scorecard that varies with how busy the box was is not a scorecard, and a replayed arm that samples today’s backlog measures the afternoon rather than the change. So a front-end that records sessions turns this on; a harness that reconstructs a run reads what was recorded.

§compact_at_tokens: Option<u64>

Compaction threshold for this run, overriding the agent’s own.

Here rather than only in AgentConfig for the same reason the budget and the jail are: one agent serves many runs, and a case that means to exercise compaction cannot ask every other case to compact too.

§queued_input: Option<Arc<Mutex<VecDeque<String>>>>

Text the user typed while the agent was working — steering, as distinct from stopping it.

Drained at the top of each turn and folded into the message that already carries the tool results, so the model sees “here is what your tools returned, and also: actually, focus on X” as one user turn and carries on working. The run is never stopped and restarted, and no context is lost.

That placement is not a detail. Between an assistant’s tool_use and its results there is no valid place to put a user message — the API requires a result for every call — so the first legal opening is the results message itself, and taking it is what makes steering mid-run possible at all rather than merely queued until the run ends.

The cost is latency: a steer waits for the in-flight model call and the tools it asked for. Interrupting sooner would mean discarding a turn the user already paid for.

§withheld: Arc<[String]>

Tools this run may not dispatch, whatever the registry holds.

A narrowing that belongs to one run rather than to the agent. The existing restriction (Tool::narrows_surface_to, which skills use) lives on the registry, which is right when one agent serves one conversation and wrong the moment one agent serves many: a web process holds a single Arc<Agent> and a Conversation per session, so a registry-level narrowing for one session narrows every other session with it.

The case that needed it is D6 — the agent may not close its own task — which a spawned child enforces by taking kg_task_update off its own private registry. A task conversation inside a shared-agent process has no private registry to take it off, so without this the model working a task would be handed the tool that closes it: a lane promoting itself, which is ladder.rs’s oldest rule.

A denylist, deliberately, where the skill restriction is an allowlist. They compose without either having to know about the other, and they fail in the same safe direction: an allowlist that forgets a tool makes it unreachable, and a denylist that forgets one leaves it reachable — so the harness names what must never be called and the skill names what may be.

§hooks: Arc<HookSet>

Lifecycle hooks. pre_tool runs after the interlock and before the approver — mechanical policy is cheaper than an interruption, and a hook cannot be talked into clicking yes. Empty by default and free.

§outbox: Option<Arc<OutboxRoute>>

Outbox routing: tools whose calls are staged for the user’s review instead of executed. None (the default) routes nothing. See crate::outbox.

§mailbox: Option<Arc<MailboxRoute>>

This run’s inter-agent messaging context: attached whenever messaging is enabled, so every dispatch can stamp the turn’s taint for message_send. Whether inbound mail is delivered is the route’s own deliver flag — the receiving side’s accept decision, made where the route is built and never inside the loop. See crate::mailbox.

Implementations§

Source§

impl RunContext

Source

pub fn new(tools: ToolCtx, approver: Arc<dyn Approver>) -> Self

Source

pub fn sandboxed( &self, workspace: impl Into<PathBuf>, approver: Arc<dyn Approver>, ) -> Self

Same policy, different root and approver — the sandboxed-run shape.

Source

pub fn with_homeostat(self) -> Self

Sample the conditions this run starts under.

Opt-in: see the field. A front-end that records sessions calls this; eval and the replay probes must not.

Source

pub fn with_budget(self, budget: Budget) -> Self

Source

pub fn with_phase(self, phase: Phase) -> Self

Make this run interruptible. Cancelling the token stops it at the next safe point, keeping whatever it had already produced. Run in phase, hiding whatever it does not permit.

Source

pub fn with_compact_at(self, limit: Option<u64>) -> Self

Compact this run at limit reported prompt tokens, whatever the agent is configured for.

Source

pub fn with_cancel(self, token: CancellationToken) -> Self

Source

pub fn with_hooks(self, hooks: Arc<HookSet>) -> Self

Source

pub fn with_outbox(self, route: Arc<OutboxRoute>) -> Self

Source

pub fn with_mailbox(self, route: Arc<MailboxRoute>) -> Self

Deliver this run’s inter-agent mail at turn boundaries.

Source

pub fn with_queued_input(self, queue: Arc<Mutex<VecDeque<String>>>) -> Self

Attach a queue the caller can push into while the run is in flight.

Source

pub fn withholding(self, names: impl IntoIterator<Item = String>) -> Self

Withhold tools from this run’s dispatch. See RunContext::withheld.

Source

pub fn is_withheld(&self, name: &str) -> bool

Is this name out of reach for this run?

Matched on the registered name and on a bare suffix, the way setup::find_tool resolves one: a deployment with prefix_tools on registers graph__kg_task_update, and a withholding that silently stopped applying there is a control that reads as enforced and is not.

Source

pub fn cancelled(&self) -> bool

Trait Implementations§

Source§

impl Clone for RunContext

Source§

fn clone(&self) -> RunContext

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more