pub struct Request { /* private fields */ }Expand description
A run, described but not yet started.
Built fluently and then handed to crate::run or crate::stream:
use agent_abstraction::{Agent, Permission, Request};
let request = Request::new(Agent::Claude, "summarize this repo")
.model("sonnet")
.permission(Permission::ReadOnly);Implementations§
Source§impl Request
impl Request
Sourcepub fn new(agent: Agent, prompt: impl Into<String>) -> Self
pub fn new(agent: Agent, prompt: impl Into<String>) -> Self
A request for agent with prompt.
Defaults are deliberately conservative: Permission::ReadOnly,
EnvPolicy::Minimal, and the agent’s structured output format. Widen
them explicitly.
Sourcepub fn command(agent: Agent, command: &Command) -> Self
pub fn command(agent: Agent, command: &Command) -> Self
A run that carries a slash command instead of a prompt.
The agent’s own verbs, addressed as values: /compact summarises a
conversation that has grown too long to think in, /clear discards it.
See crate::Command.
Pair it with Request::session or Request::resume. A command with
no conversation behind it has nothing to act on: /compact on a fresh
session is refused, and says so.
§A command is a turn, not an interruption
Deliberately a constructor rather than something crate::Run::send
delivers mid-turn. Verified against claude 2.1.212: a command injected
into a running turn emits its own result record after the turn’s,
which overwrites the outcome — the answer’s text becomes the
compaction’s empty string and the turn’s usage becomes the compaction’s
zeroes. As its own run the same command produces one clean terminal.
§Reading the result
The outcome’s text is empty and num_turns is zero, because a
compaction generates no answer. Neither is a failure, and neither is a
refusal: crate::Event::Compaction carries whether it worked, so this
wants crate::stream rather than crate::run.
Claude only. No other agent has a command vocabulary, so both refuse before spawning.
Sourcepub fn bin(self, bin: impl Into<String>) -> Self
pub fn bin(self, bin: impl Into<String>) -> Self
Override the binary. Defaults to the agent’s own name on PATH.
Sourcepub fn system(self, system: impl Into<String>) -> Self
pub fn system(self, system: impl Into<String>) -> Self
A system prompt. Delivered by flag where the agent has one and prepended to the prompt where it does not. It is never dropped.
Sourcepub fn model(self, model: impl Into<String>) -> Self
pub fn model(self, model: impl Into<String>) -> Self
Pin the model. Passed through verbatim; this crate does not validate model names, so an unknown one surfaces as the agent’s own error.
Sourcepub fn effort(self, effort: impl Into<String>) -> Self
pub fn effort(self, effort: impl Into<String>) -> Self
Set the reasoning effort level.
Passed through verbatim, exactly like Request::model and for the same
reason: the accepted set belongs to the provider, differs between agents,
and has already grown once. crate::Model::efforts lists what each
model is known to take, and nothing here validates against it.
Delivered as --effort on Claude and Copilot, and as
-c model_reasoning_effort=<level> on Codex, which has no flag for it.
Sourcepub fn thinking(self, enabled: bool) -> Self
pub fn thinking(self, enabled: bool) -> Self
Turn the model’s reasoning (“thinking”) on or off for this run.
Left unset the agent keeps its own default, which for Claude is adaptive
thinking. thinking(false) disables it; thinking(true) is the same as
leaving it unset and exists so a caller driven by a UI toggle can pass
the switch through without branching.
§Only Claude has a lever here
Delivered as MAX_THINKING_TOKENS=0 in the child’s environment, which is
exactly the switch the claude CLI reads to decide whether to send a
thinking block to the API (verified against claude 2.1.212: the gate is
MAX_THINKING_TOKENS > 0, so 0 omits the block). It rides the
environment rather than an argument because the CLI exposes no flag for
it, and it wins over EnvPolicy the same way an explicit
Request::env does.
Codex and Copilot have no equivalent off switch, so thinking(false) is
a no-op for them rather than a silent lie. Their reasoning is steered by
Request::effort instead.
Sourcepub fn interactive(self) -> Self
pub fn interactive(self) -> Self
Keep the input channel open for the turn, so the caller can send more.
Without this a run takes one prompt and that is the whole conversation.
With it, crate::Run::send delivers another message while the agent is
still working, which is what lets a chat UI accept a correction the
moment a user types it rather than making them wait for the turn to end.
The agent takes the message at its next step boundary, not mid-token. Verified against claude 2.1.212 and codex-cli 0.145.0.
Claude and Codex support this. Codex switches from exec to app-server
for the interactive turn. Copilot is crate::Error::Unsupported.
Sourcepub fn approvals(self) -> Self
pub fn approvals(self) -> Self
Route gated tool calls to the caller for a decision, instead of letting the posture answer them.
Every Permission resolves the approval question up front, which is
what lets a headless run finish unattended. This asks instead: a gated
call arrives as crate::Event::ApprovalRequest and the run waits,
mid-turn, until crate::Run::respond answers it.
Two constraints, both raised before spawning rather than met as a hang:
this needs crate::stream, since crate::run yields no events for
anyone to answer. Claude and Codex expose approval callbacks; Copilot
does not and is crate::Error::Unsupported.
Permission still applies to everything the agent does not ask about.
Agents may allow read-only commands without asking, so the absence of a
question is not proof that nothing ran.
Sourcepub fn permission(self, permission: Permission) -> Self
pub fn permission(self, permission: Permission) -> Self
Set the permission posture.
Sourcepub fn format(self, format: Format) -> Self
pub fn format(self, format: Format) -> Self
Pin the output format. Left unset, a run picks the agent’s structured format, which is also the one that carries a session id.
Sourcepub fn add_dir(self, dir: impl Into<PathBuf>) -> Self
pub fn add_dir(self, dir: impl Into<PathBuf>) -> Self
Add another working root beside Request::cwd.
Claude and codex exec receive --add-dir. Interactive Codex runs use
the same path as an app-server runtime root and workspace-write root, so
the access described here survives transport changes without a caller
assembling provider-specific arguments.
Sourcepub fn env(self, key: impl Into<String>, value: impl Into<String>) -> Self
pub fn env(self, key: impl Into<String>, value: impl Into<String>) -> Self
Set an environment variable for the child. Repeatable.
Sourcepub fn env_policy(self, policy: EnvPolicy) -> Self
pub fn env_policy(self, policy: EnvPolicy) -> Self
Choose which of the host’s environment variables reach the agent.
Defaults to EnvPolicy::Minimal, which passes through only what the
selected agent needs. Reach for EnvPolicy::Inherit when the host
holds nothing the agent should not see, or when something this crate
does not know about has to reach the CLI.
let request = Request::new(Agent::Claude, "review this")
.env_policy(EnvPolicy::Inherit);Sourcepub fn timeout(self, timeout: Duration) -> Self
pub fn timeout(self, timeout: Duration) -> Self
Kill the run if it has not finished within timeout.
Sourcepub fn unchecked_args<I, S>(self, args: I) -> Self
pub fn unchecked_args<I, S>(self, args: I) -> Self
Append raw arguments after everything this crate builds.
The escape hatch for agent-specific flags with no unified spelling.
This voids the crate’s guarantees. Arguments land after the generated
ones, so they can contradict Request::permission, redirect the output
format the parser expects, or point the run at a different session.
Codex’s -c key=value in particular can rewrite sandbox and approval
policy for the invocation. Nothing here is validated, and a security
review of the permission posture means little without also reviewing
whatever is passed here.
Arguments are passed straight to the binary without a shell.
Sourcepub fn schema(self, schema: impl Into<String>) -> Self
pub fn schema(self, schema: impl Into<String>) -> Self
Constrain the answer to a JSON Schema.
The agent is asked to return a value conforming to schema, which
Outcome::structured then carries already parsed. Useful when the
answer is data rather than prose: a set of review findings, an
extraction, a classification. Reading it beats parsing prose, which is
a guess about formatting the model never promised.
The two CLIs that support this take it differently, and the difference
is hidden: Claude accepts the schema inline, Codex reads it from a file
this crate writes for the run and removes afterwards. Copilot 1.0.78
has no schema support, so asking is crate::Error::Unsupported
rather than a prose answer presented as data.
The schema is passed through unvalidated; a malformed one surfaces as the agent’s own error.
§Write the schema strictly
Codex sends it to OpenAI’s structured-output API, which rejects anything
permissive. Every object needs "additionalProperties": false and every
property listed in required, or the request fails with a 400 before
the model runs:
'additionalProperties' is required to be supplied and to be falseClaude is more forgiving, so a schema that works there can still fail on Codex. Writing to the stricter rule keeps one schema usable for both.
Sourcepub fn resume(self, id: impl Into<String>) -> Self
pub fn resume(self, id: impl Into<String>) -> Self
Continue an earlier conversation by its native id, bypassing the session
store. Prefer Request::session unless you are tracking ids yourself.
Sourcepub fn session_id(self, id: impl Into<String>) -> Self
pub fn session_id(self, id: impl Into<String>) -> Self
Start a new conversation under an id you choose, rather than one the agent picks.
Useful when a host already has its own identifier for a thread and wants the agent’s session to match it, with no mapping table in between. The id is known before the process starts, so the association survives a run that dies mid-turn.
Only Claude and Copilot accept an assigned id
(SessionSupport::Minted). Codex reveals its thread_id only in its
own output, so this is crate::Error::Unsupported for it, raised when
the argv is built rather than silently starting an unrelated session.
Both CLIs require a valid UUID here; this crate passes the string through without checking, so a non-UUID surfaces as the agent’s own error.
Sourcepub fn session(
self,
store: &SessionStore,
project: impl AsRef<Path>,
name: impl Into<String>,
fork: bool,
) -> Result<Self>
pub fn session( self, store: &SessionStore, project: impl AsRef<Path>, name: impl Into<String>, fork: bool, ) -> Result<Self>
Attach this run to a caller-owned session name.
The store decides whether this turn creates, continues, or forks, and the
binding is written back once the run yields an id. fork branches a new
conversation off the stored one instead of appending to it.
The store is cloned into the request so the run can write the binding
back without borrowing it. That clone is a PathBuf, not the sessions
themselves: records are read and written on demand and never held in
memory, so this stays cheap however many sessions exist.
§Errors
crate::Error::SessionConflict if the name belongs to another agent,
or crate::Error::Unsupported if this agent cannot fork or has no
session id at all.
Sourcepub fn effective_format(&self) -> Format
pub fn effective_format(&self) -> Format
The format this request will actually use.
Sourcepub fn session_phase(&self) -> Option<Phase>
pub fn session_phase(&self) -> Option<Phase>
Whether this turn opens, continues, or branches its named session.
None when the request is not attached to one.
Known before the run starts, so a UI can label the turn up front.