pub struct Agent { /* private fields */ }Expand description
A stateful agent: configuration, a model transport, a tool set, and the
running conversation. Drive it with Agent::send.
Implementations§
Source§impl Agent
impl Agent
Sourcepub fn new(config: Config) -> Result<Agent, Error>
pub fn new(config: Config) -> Result<Agent, Error>
Build an agent backed by an OpenAI-compatible endpoint (OpenRouter by
default). The API key is taken from Config::api_key, then
Config::api_key_cmd (P4: a credential-helper command, run via the
shell — see run_api_key_cmd), then the configured environment
variable (Config::api_key_env).
Sourcepub fn with_provider(config: Config, provider: Box<dyn Provider>) -> Agent
pub fn with_provider(config: Config, provider: Box<dyn Provider>) -> Agent
Build an agent with an explicit provider and the built-in tools. Handy for tests (inject a mock provider) or custom transports.
Sourcepub fn with_parts(
config: Config,
provider: Box<dyn Provider>,
registry: ToolRegistry,
) -> Agent
pub fn with_parts( config: Config, provider: Box<dyn Provider>, registry: ToolRegistry, ) -> Agent
Build an agent from all three parts.
Sourcepub fn provider_arc(&self) -> Arc<dyn Provider> ⓘ
pub fn provider_arc(&self) -> Arc<dyn Provider> ⓘ
A handle to this agent’s model transport, for sharing with subagents.
Sourcepub fn config(&self) -> &Config
pub fn config(&self) -> &Config
Read-only access to this agent’s resolved Config — e.g. so a
caller (crates/cli’s attach_mcp) can consult
Config::module_registry/Config::module_activation AFTER
construction without having to separately thread the config through
every call site that builds an Agent and later needs it again.
Same trust boundary as every other already-public Agent accessor
(history, provider_arc) — the caller is the same process that
built this Config in the first place, not a new exposure surface.
Sourcepub fn checkpoint_observer(&self) -> Option<&CheckpointObserver>
pub fn checkpoint_observer(&self) -> Option<&CheckpointObserver>
P5-9 (§2 module 20 checkpoint): this agent’s checkpoint engine, if
Config::checkpoint_enabled is true and the shadow store opened
successfully — None otherwise (the default-off case, or a
graceful-degrade after an I/O failure). A caller (CLI/TUI/embedder)
uses this to list/turn_diff/restore WITHOUT re-deriving the
shadow-store root itself. Deliberately named checkpoint_observer,
not checkpoint — Self::checkpoint already names the unrelated
in-memory conversation-position marker (see that method’s doc
comment).
Sourcepub fn lsp_manager(&self) -> Option<&LspManager>
pub fn lsp_manager(&self) -> Option<&LspManager>
P5-11 (§2 module 28 lsp): this agent’s LSP server registry, if
Config::lsp_enabled is true — None otherwise (the default-off
case). impl Drop for Agent already covers production teardown via
crate::lsp::LspManager::kill_all_sync (a real, group-killing OS
process kill — see crate::lsp’s module doc). This accessor exists
for an OPTIONAL caller (CLI/TUI/embedder) that manages its own
Agent lifecycle and additionally wants to reach
crate::lsp::LspManager::shutdown_all for a graceful LSP
shutdown/exit handshake BEFORE dropping the agent — nothing
calls shutdown_all automatically today.
Sourcepub async fn run_subagent(
&self,
system: impl Into<String>,
task: impl Into<String>,
) -> Result<String, Error>
pub async fn run_subagent( &self, system: impl Into<String>, task: impl Into<String>, ) -> Result<String, Error>
Spawn a subagent that shares this agent’s model transport, runs task
to completion with its own fresh conversation (seeded with system), and
returns its final answer. The analog of Agent / spawn_agent.
Sourcepub fn with_provider_arc(config: Config, provider: Arc<dyn Provider>) -> Agent
pub fn with_provider_arc(config: Config, provider: Arc<dyn Provider>) -> Agent
Like Self::with_provider but sharing an existing transport handle.
Sourcepub fn run_in_background(
self,
prompt: impl Into<String>,
) -> JoinHandle<(Agent, Result<String, Error>)> ⓘ
pub fn run_in_background( self, prompt: impl Into<String>, ) -> JoinHandle<(Agent, Result<String, Error>)> ⓘ
Run a prompt on a background task, returning a handle that resolves to the final answer (and the agent, so the caller can continue it). The analog of background/async agent runs.
Sourcepub fn resume(config: Config, session: Session) -> Result<Agent, Error>
pub fn resume(config: Config, session: Session) -> Result<Agent, Error>
Build an agent and seed it with a previously-recorded Session so it
can continue where Claude Code or Codex left off.
Sourcepub fn resume_recorded(
config: Config,
session: Session,
sidecar_path: &Path,
) -> Result<Agent, Error>
pub fn resume_recorded( config: Config, session: Session, sidecar_path: &Path, ) -> Result<Agent, Error>
Like Self::resume, but also begins recording (A2/A3): a fresh
native-v2 sidecar is created at sidecar_path from session (header +
session.raw verbatim — the imported prefix’s own fidelity), and every
subsequent turn this agent produces is appended to it at full fidelity,
independent of whatever cap_tool_output/maybe_compact (D6) do to
history.
Invariant this establishes ONLY once Self::set_reduction_policy is
also called (the D6/A7 supersession gate, Self::run_loop): at any
instant, Session::from_native_str(sidecar).messages equals
session.messages (the imported prefix) followed by every message
appended since — i.e. self.history()[1..] (history[0] is this
agent’s own system prompt, per Self::load_session; it is never
part of session and is never written to the sidecar). Recording
alone (no policy) leaves the gate off: cap_tool_output still runs on
oversized tool results, and history can diverge from the sidecar for
them — honestly, via the notice’s “full output in session sidecar”
label, never silently.
Sourcepub fn set_recorder(&mut self, w: SidecarWriter)
pub fn set_recorder(&mut self, w: SidecarWriter)
Install (or replace) this agent’s sidecar recorder (A3).
Sourcepub fn set_reduction_policy(&mut self, policy: ReductionPolicy)
pub fn set_reduction_policy(&mut self, policy: ReductionPolicy)
Install (or replace) this agent’s reduction policy (A5/A7/A10). Once
set, every provider request is built from a projected view of
history[1..] (reduce::project_messages) rather than history
verbatim — history itself is never shrunk or mutated by this; only
the request view does.
Sourcepub fn reduction_policy(&self) -> Option<&ReductionPolicy>
pub fn reduction_policy(&self) -> Option<&ReductionPolicy>
This agent’s reduction policy, if one is installed.
Sourcepub fn set_schema_tier(&mut self, tier: SchemaTier)
pub fn set_schema_tier(&mut self, tier: SchemaTier)
Change the global tool-schema tier (TR-8/T5) mid-session. Takes effect
starting with the NEXT request this agent builds. Under
CachePlan::ImportedPrefix, the first request built after a change
is flagged as a cache-bust event and its cache-control annotation is
skipped for that one request (see [provider::tier_change_is_cache_bust],
consulted in Self::build_request_messages) — normal annotation
resumes on the next request if the tier doesn’t change again.
Sourcepub fn set_tool_schema_tier(
&mut self,
name: impl Into<String>,
tier: SchemaTier,
)
pub fn set_tool_schema_tier( &mut self, name: impl Into<String>, tier: SchemaTier, )
Override the schema tier for a single tool (TR-8/T5) mid-session, same
cache-bust interaction as Self::set_schema_tier.
Sourcepub fn set_span_summarizer(
&mut self,
summarizer: impl SpanSummarizer + Send + Sync + 'static,
)
pub fn set_span_summarizer( &mut self, summarizer: impl SpanSummarizer + Send + Sync + 'static, )
Install (or replace) this agent’s TR-7 span summarizer — the
injectable side-call Self::build_request_messages uses to turn an
A10 TurnsCleared span into an LLM-written summary paragraph when
policy.summarize_cleared_turns is on. Installing one alone changes
nothing: ReductionPolicy::summarize_cleared_turns (off by
default) is the actual gate, so tests/callers that want the
deterministic stub can simply never call this.
Sourcepub fn prepare_cleared_turns_summary(
&self,
msgs: &[ChatMessage],
policy: &ReductionPolicy,
prior: &ReductionLog,
) -> Option<PreparedClearSummary>
pub fn prepare_cleared_turns_summary( &self, msgs: &[ChatMessage], policy: &ReductionPolicy, prior: &ReductionLog, ) -> Option<PreparedClearSummary>
Prepare TR-7 metadata with this agent’s installed summarizer for a
projection performed by an outer driver before session history/log
are loaded (the CLI foreign-resume preflight). None preserves the
deterministic fallback when the gate is off, no summarizer exists,
the span is below the cost floor, or the side-call fails.
Sourcepub fn set_event_sink(&mut self, sink: Box<dyn Fn(AgentEvent) + Send + Sync>)
pub fn set_event_sink(&mut self, sink: Box<dyn Fn(AgentEvent) + Send + Sync>)
P5-4: install (or replace) this agent’s crate::EventSink AFTER
construction — Config::event_sink is otherwise only set at
Config-build time (before Agent::new), which is too early for a
tui embedder that only knows it’s activating (and needs to
replace whatever print-mode/REPL sink was already installed with
one that feeds its own render loop instead of writing straight to
stdout) once it already holds a live Agent. Mirrors [Self:: set_permissions_approval_handler]’s “installing one alone changes
nothing beyond what already consults Config::event_sink” pattern
— this is a plain replacement, not a new activation gate.
Sourcepub fn set_permissions_approval_handler(
&mut self,
handler: impl PermissionsApprovalHandler + 'static,
)
pub fn set_permissions_approval_handler( &mut self, handler: impl PermissionsApprovalHandler + 'static, )
P5-1: install (or replace) this agent’s permissions-engine approval
handler — see crate::permissions::PermissionsApprovalHandler.
This is the non-interactive decision seam a CLI/TUI/SDK embedder
implements for the Ask-tier prompt; the TUI’s actual interactive
UI is a separate module (P5 row 4), not built here. Installing one
alone changes nothing: Config::permissions_enabled (off by
default) is the actual gate — with no handler installed, every
Ask-tier decision denies (fail-closed, see that trait’s doc
comment).
P5-10 (§2 module 12, escalation = "ask"): the SAME handler also
backs a sandbox-unenforceable ask decision
(crate::sandbox::decide_fs‘s approval parameter) — one installed
seam serves both permissions.rules’ Ask tier and
permissions.sandbox’s escalation = "ask", rather than requiring
an embedder to install two near-identical handlers. Kept in sync on
self.ctx (not just self.permissions_approval_handler) because
BashTool::execute/PersistentShellTool::execute only ever see
&ToolContext, never &Agent — see ToolContext:: sandbox_approval_handler’s doc comment.
Sourcepub fn set_legacy_approval_handler(
&mut self,
handler: Box<dyn Fn(&ToolCall) -> bool + Send + Sync>,
)
pub fn set_legacy_approval_handler( &mut self, handler: Box<dyn Fn(&ToolCall) -> bool + Send + Sync>, )
Install the compatibility approval seam used when the composable
permissions engine is disabled. SDK-owned interactive frontends call
this alongside Self::set_permissions_approval_handler so the same
authenticated request channel works under either policy engine; the
selected engine remains entirely a configuration decision.
Sourcepub fn set_subagent_store(
&mut self,
store: Arc<SessionStore>,
session_name: impl Into<String>,
)
pub fn set_subagent_store( &mut self, store: Arc<SessionStore>, session_name: impl Into<String>, )
P5-3 (§2 module 9 D5 “subagent transcripts… persisted + linked”):
install a crate::store::SessionStore (+ this agent’s own session
name in it) so spawn_subagent persists each child’s transcript
(via crate::store::SessionStore::save_subagent_transcript) and
lineage record (via
crate::store::SessionStore::save_subagent_lineage) once the
child finishes. Installing one alone changes nothing about whether
spawning WORKS — Config::subagents_enabled is the actual gate;
this only controls whether a completed spawn’s transcript additionally
lands on disk.
Sourcepub fn set_claude_runtime_manifest(&mut self, manifest: ClaudeRuntimeManifest)
pub fn set_claude_runtime_manifest(&mut self, manifest: ClaudeRuntimeManifest)
Seed the Claude runtime manifest reconstructed during resume.
Installing state enables the matching Claude runtime tool schemas so a disk-reloaded continuation does not lose that vocabulary, but never starts a timer by itself. The supplied execution posture is preserved: an embedding scheduler may deliberately activate before installing it.
Sourcepub fn restore_claude_project_agents(&mut self) -> Result<usize, Error>
pub fn restore_claude_project_agents(&mut self) -> Result<usize, Error>
Reinstall project-scoped Claude named-agent definitions when a
Supercode continuation carrying a Claude runtime manifest is reopened
from disk. The manifest is the durable capability marker; definitions
themselves remain authoritative in <cwd>/.claude/agents/*.md.
Sourcepub fn claude_runtime_manifest(&self) -> Option<&ClaudeRuntimeManifest>
pub fn claude_runtime_manifest(&self) -> Option<&ClaudeRuntimeManifest>
Current imported Claude runtime state, including paused mutations made
by Cron*/ScheduleWakeup, for persistence by the embedding loop.
Sourcepub fn claude_runtime_manifest_mut(
&mut self,
) -> Option<&mut ClaudeRuntimeManifest>
pub fn claude_runtime_manifest_mut( &mut self, ) -> Option<&mut ClaudeRuntimeManifest>
Mutable access for an embedding scheduler driver to atomically claim due events and persist the resulting manifest. Merely borrowing this state does not start a timer; execution remains the driver’s explicit responsibility.
Sourcepub fn set_child_approval_handler_factory(
&mut self,
factory: impl Fn(String, Arc<Mutex<Vec<QueuedApproval>>>) -> Arc<dyn PermissionsApprovalHandler> + Send + Sync + 'static,
)
pub fn set_child_approval_handler_factory( &mut self, factory: impl Fn(String, Arc<Mutex<Vec<QueuedApproval>>>) -> Arc<dyn PermissionsApprovalHandler> + Send + Sync + 'static, )
P5-4 (tui, closes the P5-3 §2.2 C6 deferred chain): install a
factory this agent’s Self::run_spawn_subagent calls (with the
fresh child’s own id and this agent’s shared
Self::pending_child_approvals queue) to build the
PermissionsApprovalHandler a background_prompts = "parent"
child gets, INSTEAD of the default
crate::subagents::ParentQueueApprovalHandler. Installing one
alone changes nothing about whether background spawning works —
Config::subagents_background_prompts being
crate::subagents::BackgroundPromptsPolicy::Parent is the actual
gate that reaches this factory at all; a Parent-policy child
spawned before this is installed (or on an agent that never installs
it) still gets the immediate-deny default, unchanged.
Security note. The factory only controls WHICH handler answers
an Ask-tier request — it can never widen what gets asked in the
first place: crate::permissions::approval::resolve_ask only
calls a handler’s ask when the rule engine has already resolved
the call to Ask (Deny short-circuits before any handler is
consulted; Allow never needs one), so a parent’s “allow” answer
here can only grant what the policy already routed to a prompt —
never override a Deny the engine already decided.
Sourcepub fn pending_child_approvals(&self) -> Vec<QueuedApproval>
pub fn pending_child_approvals(&self) -> Vec<QueuedApproval>
P5-3 (§2.2 C6 “parent-surfaced queue”): every approval request a
background_prompts = "parent" child has raised so far, oldest
first — a read-only audit view, not a mutable queue the caller
answers. Under P5-3’s own default handler (no P5-4 TUI factory
installed) every entry here WAS already resolved Deny (a
background call can’t wait for an answer with no handler
installed) — but once a crate::tui::TuiChildApprovalHandler
factory is installed (P5-4,
Self::set_child_approval_handler_factory), the underlying call
genuinely blocks and may resolve Allow/AllowForSession; this
method still records the SAME entry for the audit trail either
way, so “queued here” no longer implies “was denied” in general —
see crate::subagents::QueuedApproval’s doc comment.
Sourcepub fn set_session_titler(
&mut self,
titler: impl SessionTitler + Send + Sync + 'static,
)
pub fn set_session_titler( &mut self, titler: impl SessionTitler + Send + Sync + 'static, )
P4b: install (or replace) this agent’s auto-title side-call — see
crate::session_title::SessionTitler. Installing one alone changes
nothing: Config::auto_title (off by default) is the actual gate a
caller should consult before calling Self::auto_title.
Sourcepub fn auto_title(&self) -> Option<String>
pub fn auto_title(&self) -> Option<String>
P4b: produce a title for this agent’s current conversation via the
installed Self::set_session_titler side-call. Returns None (never
panics, never blocks longer than the titler itself does) if no
titler is installed, or the side-call itself declined (see
crate::session_title::auto_title). Does NOT consult
Config::auto_title itself — that gate is the caller’s
responsibility, matching Self::span_summarizer’s precedent of
keeping the mechanism and the policy gate separate.
Sourcepub fn usage_records(&self) -> &[UsageRecord]
pub fn usage_records(&self) -> &[UsageRecord]
P4b (§1.6, catalog §4a “persisted per-turn usage records”): every
crate::usage_log::UsageRecord this agent has accumulated so far.
Sourcepub fn save_usage_log(
&self,
store: &SessionStore,
name: &str,
) -> Result<(), Error>
pub fn save_usage_log( &self, store: &SessionStore, name: &str, ) -> Result<(), Error>
P4b: persist this agent’s accumulated usage log to store under
name — a thin wrapper over crate::store::SessionStore::save_usage_log
so callers don’t need to import both types.
Sourcepub fn queue_steer(&self, message: impl Into<String>)
pub fn queue_steer(&self, message: impl Into<String>)
P4b (§1.7, pi§3 semantics): queue a mid-turn steering message —
delivered “after current tool calls” (pi’s phrasing): at the top of
Self::run_loop’s NEXT iteration, before the next model request is
built, regardless of whether this turn is still mid-flight with
pending tool calls. Drained per Config::steering_mode.
Sourcepub fn queue_follow_up(&mut self, message: impl Into<String>)
pub fn queue_follow_up(&mut self, message: impl Into<String>)
P4b: queue a follow-up message — delivered “at idle” (pi’s phrasing):
only once Self::run_loop would otherwise return a final answer
(no more tool calls pending). Drained per Config::follow_up_mode.
Sourcepub fn queued_steer_count(&self) -> usize
pub fn queued_steer_count(&self) -> usize
P4b: how many steering messages are currently queued (mid-turn + follow-up combined) — mostly for tests/diagnostics.
Sourcepub fn reduction_log(&self) -> &ReductionLog
pub fn reduction_log(&self) -> &ReductionLog
The accumulating reduction log (A5) — every reduction applied to any
projected request view so far. Combined with a full-fidelity sidecar
Session, this is enough to reduce::invert any projected view back to
the exact original.
Sourcepub fn set_context_limit(&mut self, limit: u64)
pub fn set_context_limit(&mut self, limit: u64)
PARITY-18 D4 — arm the per-send context guard: Self::run_loop
will refuse (via Error::ContextLimitExceeded) to build and issue
ANY request — the first or any later turn — whose
crate::tokens::context_guard verdict is “does not fit” against
limit. Call this once the target model’s context-window size is
known (resume --reduced’s preflight already computes it). Leaving
this unset (the default) is a no-op: no guard runs, exactly today’s
pre-PARITY-18 behavior.
Sourcepub fn context_limit(&self) -> Option<u64>
pub fn context_limit(&self) -> Option<u64>
This agent’s armed context limit, if Self::set_context_limit has
been called.
Sourcepub fn model(&self) -> &str
pub fn model(&self) -> &str
The model identifier this agent sends on its next request
(Config::model, as of construction/resume or the last
Self::set_model call).
Sourcepub fn set_model(&mut self, model: impl Into<String>)
pub fn set_model(&mut self, model: impl Into<String>)
UX-30 dev/02 — switch the model this agent sends, starting with the
NEXT request it builds (and every one after, until changed again).
Self::run_loop reads self.config.model fresh on every request
(see its ChatRequest construction), so this alone is enough —
there is no cached/baked-in copy anywhere else to also update.
Takes effect immediately; safe to call only between turns (the
REPL’s /model picker runs at the prompt, never mid-turn). Touches
nothing else: history, the sidecar, and reduction state are exactly
as untouched as Self::set_schema_tier leaves them for a
mid-session tier change.
P4c-review note: this is the LOW-LEVEL primitive — it swaps
Config::model and nothing else. It does NOT run dep 8’s
reasoning-artifact filter
(reduce::rehydrate::filter_reasoning_artifacts) and does NOT
create a crate::model_change::ModelChangeRecord, so calling it
directly for a mid-session handoff between two DIFFERENT models
leaves model-A’s reasoning artifacts in history for model-B to
inherit. Self::switch_model is the safe superset — gated by
Config::model_switch_allow_switch, it filters and records the
switch before delegating to this method — and is what callers
performing a governed mid-session model switch should use instead.
Sourcepub fn switch_model(&mut self, model: impl Into<String>)
pub fn switch_model(&mut self, model: impl Into<String>)
P4c (§1.10/§3.1 core.model_switch.allow_switch, D9 row, dep 8,
design’s “core NEW-significant” item): the mid-session model
switch — a superset of Self::set_model gated by
Config::model_switch_allow_switch.
allow_switch = false (the default): EXACTLY Self::set_model
— same single field write, nothing else touched, no
crate::model_change::ModelChangeRecord created. Byte-identical to
calling set_model directly.
allow_switch = true: additionally, before the swap takes
effect, runs reduce::rehydrate::filter_reasoning_artifacts over
Self::history — model-A’s reasoning/thinking artifacts (any
crate::message::ChatMessage::metadata key in
reduce::rehydrate::REASONING_METADATA_KEYS, any content_parts
block whose "type" is in
reduce::rehydrate::REASONING_CONTENT_PART_TYPES) are stripped
BEFORE model-B ever builds a request from this history — then
appends a typed, translatable crate::model_change::ModelChangeRecord
to Self::model_change_records (persist it via
Self::save_model_change_log). A switch TO the current model
(model == Self::model()) is treated as a no-op — still exactly
set_model’s mechanics, no record for a switch that didn’t actually
change anything (and nothing to filter FOR, since there was no
handoff).
Sourcepub fn model_change_records(&self) -> &[ModelChangeRecord]
pub fn model_change_records(&self) -> &[ModelChangeRecord]
P4c: every crate::model_change::ModelChangeRecord this agent has
accumulated so far (via Self::switch_model with allow_switch
on). Empty when the knob is off or no switch has happened yet.
Sourcepub fn save_model_change_log(
&self,
store: &SessionStore,
name: &str,
) -> Result<(), Error>
pub fn save_model_change_log( &self, store: &SessionStore, name: &str, ) -> Result<(), Error>
P4c: persist this agent’s accumulated model-change log to store
under name — the crate::model_change::ModelChangeRecord analog
of Self::save_usage_log.
Sourcepub fn git_metadata(&self) -> Option<&GitMetadataRecord>
pub fn git_metadata(&self) -> Option<&GitMetadataRecord>
P4e (§1.6/§3.1 core.session.git_metadata, catalog:331): this
agent’s captured git provenance, if Config::session_git_metadata
was on at construction and the best-effort probe found a repo.
Sourcepub fn save_git_metadata(
&self,
store: &SessionStore,
name: &str,
) -> Result<(), Error>
pub fn save_git_metadata( &self, store: &SessionStore, name: &str, ) -> Result<(), Error>
P4e: persist this agent’s captured git metadata to store under
name — a thin wrapper over
crate::store::SessionStore::save_git_metadata, the
crate::git_metadata::GitMetadataRecord analog of
Self::save_usage_log. A no-op (Ok(()), nothing written) when
Self::git_metadata is None.
Sourcepub fn session_persist(&self) -> bool
pub fn session_persist(&self) -> bool
P4e DEFECT-FIX (independent Fable-5 review of P4e: core.session.persist
had a Config field and CLI plumbing at ConfigProfile → Config but
no consumer at all): whether a CLI caller’s session-store save sites
(persist_session, persist_full_view) should actually write to
disk. true (the default) is byte-identical to pre-fix behavior —
every session persists. false makes a session ephemeral: it runs
exactly as before, but no <name>.jsonl/sidecar family is ever
written for it. A plain getter, same posture as Self::model —
this crate itself never reads or enforces it; the CLI’s save sites do.
Sourcepub fn session_name(&self) -> Option<&str>
pub fn session_name(&self) -> Option<&str>
P4e DEFECT-FIX (independent Fable-5 review of P4e: core.session.name
had a Config field and CLI plumbing but no consumer): the
caller-configured session name, if [core.session] name was set.
None (the default) leaves session naming exactly as before —
mint_session_name’s auto-generated <tag>-<adjective>-<noun> shape.
A plain getter, same posture as Self::session_persist.
Sourcepub fn request_issued(&self) -> bool
pub fn request_issued(&self) -> bool
PARITY-18 D3 — whether this agent has actually issued at least one
live request to its Provider so far (set the instant
Self::run_loop reaches its real send site, regardless of whether
that call then succeeds or fails). Callers should report
“request sent” from THIS, never from having merely passed the
context guard or having called Self::send — either of those can
happen with zero requests actually issued (a guard refusal, an
interactive session quit before any turn completes).
Sourcepub fn cache_established(&self) -> bool
pub fn cache_established(&self) -> bool
P5-2 (§2.2 C2): whether this agent currently considers its
CachePlan::ImportedPrefix cache entry warm — mirrors
Self::request_issued’s read-only-observability precedent, so a
caller (or a test) can confirm Self::register_tool’s C2
invalidation actually took effect without reaching into private
state.
Sourcepub fn imported_prefix_len(&self) -> Option<usize>
pub fn imported_prefix_len(&self) -> Option<usize>
B7: length of the imported-prefix protected by CachePlan::ImportedPrefix
(this agent’s own system message plus every message of a
previously-imported session), set by Self::load_session. None
until a session has been loaded.
Sourcepub fn set_reduction_log(&mut self, log: ReductionLog)
pub fn set_reduction_log(&mut self, log: ReductionLog)
Replace this agent’s accumulating reduction log (C4: /expand//reduce
mutate the log directly via reduce::invert_one/reduce::project_messages
and must feed the result back here so the next request build or
persist sees the updated state instead of silently recomputing from an
empty log). Also lets a caller (resume_cmd, C1) seed the log with the
initial projection it already computed for the entry banner, so
reduction_log() reflects reality even before this agent’s first
send() (which is otherwise the only place build_request_messages
populates it).
Sourcepub fn load_session(&mut self, session: Session)
pub fn load_session(&mut self, session: Session)
Replace the conversation with a loaded session, keeping this agent’s own system prompt at the front. The session’s own system/developer turns are preserved after it for context.
Sourcepub fn save_transcript(&self, path: impl AsRef<Path>) -> Result<(), Error>
pub fn save_transcript(&self, path: impl AsRef<Path>) -> Result<(), Error>
Persist the live conversation to path as JSONL (one ChatMessage
per line) so the session can be resumed later — supercode’s own sessions
become first-class, resumable artifacts.
Sourcepub fn load_transcript(&mut self, path: impl AsRef<Path>) -> Result<(), Error>
pub fn load_transcript(&mut self, path: impl AsRef<Path>) -> Result<(), Error>
Restore a conversation previously written with Self::save_transcript,
replacing the current history.
Sourcepub fn checkpoint(&self) -> usize
pub fn checkpoint(&self) -> usize
Take a checkpoint of the current conversation position. Pass it to
Self::rewind_to to discard everything sent since (the rewind/undo
analog of fork/checkpoint).
Sourcepub fn rewind_to(&mut self, checkpoint: usize)
pub fn rewind_to(&mut self, checkpoint: usize)
Rewind the conversation to a Self::checkpoint, discarding later turns.
Sourcepub async fn send_with_files(
&mut self,
text: impl Into<String>,
files: &[PathBuf],
) -> Result<String, Error>
pub async fn send_with_files( &mut self, text: impl Into<String>, files: &[PathBuf], ) -> Result<String, Error>
Send a message with file inputs attached — the --file / -i analog.
Each file’s contents are injected into the prompt: UTF-8 text inline,
binary (e.g. images) noted with a size marker. (Native image vision
would additionally require multimodal content parts.)
Sourcepub async fn send_with_images(
&mut self,
text: impl Into<String>,
image_urls: &[String],
) -> Result<String, Error>
pub async fn send_with_images( &mut self, text: impl Into<String>, image_urls: &[String], ) -> Result<String, Error>
Send a message with image inputs to a vision model — the -i/--image
analog. image_urls may be https://… links or data:image/…;base64,…
URLs; they’re attached as multimodal image_url content parts.
Sourcepub fn expand_prompt(&self, input: &str) -> String
pub fn expand_prompt(&self, input: &str) -> String
Expand a /<name> <args> slash command against the registered prompt
templates ({args} is replaced with the trailing text). Non-matching
input is returned unchanged.
Sourcepub async fn expand_prompt_async(&self, input: &str) -> String
pub async fn expand_prompt_async(&self, input: &str) -> String
P5-2 (§2 module 15 D7 row 4 “prompts-as-commands”): like
Self::expand_prompt, but also consults MCP-server-sourced
prompts registered via Self::register_mcp_prompt when the local
Config::prompts table has no match — a live prompts/get
round-trip, which is why this is async and Self::expand_prompt
itself stays synchronous (its public sync signature is unchanged,
for every existing caller that doesn’t need MCP prompts).
Argument mapping (a scope decision, not a protocol requirement —
the MCP spec leaves “how does free CLI text become named prompt
arguments” to the client): a prompt with zero or one declared
arguments gets the whole trailing text (empty string if the prompt
takes no arguments and none was given); a prompt with two or more
declared arguments expects key=value pairs, whitespace-separated
(/mcp__server__prompt lang=rust topic=async) — an unparseable pair
(no =) is simply skipped, never a hard error (matches this
method’s “non-matching input passes through” fail-open posture for
the LOCAL-prompt case above).
Sourcepub fn register_mcp_prompt(
&mut self,
command_name: impl Into<String>,
source: impl SdkPromptSource + 'static,
)
pub fn register_mcp_prompt( &mut self, command_name: impl Into<String>, source: impl SdkPromptSource + 'static, )
P5-2 (§2 module 15 D7 row 4): register an MCP server’s prompt as a
slash-command source — command_name MUST already be the
namespaced mcp__<server>__<prompt> form
(crate::mcp::McpServerHandle::prompts produces exactly that
shape); this method does not re-namespace or validate it, so a
caller that hands it a bare name defeats the collision protection
crate::mcp::McpPromptSource’s doc comment describes. Overwrites
any prior registration under the same command name (re-attaching
the same server replaces its own earlier prompt list; this can
never touch a NON-mcp__-prefixed key, i.e. never a local
Config::prompts entry).
Sourcepub fn append_system_note(&mut self, text: &str)
pub fn append_system_note(&mut self, text: &str)
P5-2 (§2 module 15 D7 row 5 “instructions”): fold an MCP server’s
initialize-time instructions (or any other free-text note) into
this agent’s system message — the context-assembly site every other
core.*/capabilities.* prompt-section append already uses
(Self::with_parts), except this one fires AFTER construction
(attaching MCP servers happens once the agent already exists — see
crates/cli/src/main.rs’s attach_mcp). A no-op if history is
somehow empty or its first message isn’t a system message (never
true for an Agent built via Self::new/Self::with_parts, but
checked rather than assumed).
Sourcepub fn maybe_compact(&mut self) -> bool
pub fn maybe_compact(&mut self) -> bool
Compact the conversation if it has grown past the configured threshold.
Re-founded (A10): with a ReductionPolicy installed
(Self::set_reduction_policy), this no longer touches self.history
at all. It derives policy.clear_turns_older_than from
compact_after_messages so the next projected request view
(reduce::project_messages, built in Self::run_loop) collapses the
old turns into one reversible TurnsCleared stub instead —
history() and the sidecar keep every message forever; only the view
shrinks. Returns whether the live (unreduced) history currently
exceeds the threshold, i.e. whether a clearing will actually be
visible in the next projected view.
Legacy path (no policy) — LOSSY, kept only for byte-identical
backward compatibility (D6): destructively rewrites self.history,
permanently discarding the dropped middle turns (replaced by a single
non-reversible summary marker that becomes their SOLE remaining copy —
exactly the lossy compaction this reduction layer differentiates
against). Once a sidecar/recorder or a ReductionPolicy is in play,
prefer installing a policy so this method takes the re-founded path
above instead.
Sourcepub fn register_tool(&mut self, tool: impl Tool + 'static)
pub fn register_tool(&mut self, tool: impl Tool + 'static)
Register an additional tool (e.g. your own capability).
P5-2 (§2.2 C2 “connect invalidates cache prefix”): registering a
tool AFTER this agent has already issued a request
(Self::request_issued) changes the tools schema every
subsequent request carries — the exact prefix-churn shape C2
describes, MCP-sourced or not. Resets Self::cache_established so
the next cache-warmth check (provider::cache_cold_reason) doesn’t
wrongly assume the entry is still warm. A no-op call before the
first request (the common case: attach_mcp registers tools once at
startup, before any turn runs) changes nothing — byte-identical to
today.
Sourcepub fn history(&self) -> &[ChatMessage]
pub fn history(&self) -> &[ChatMessage]
The current conversation, including the system prompt.
Sourcepub async fn send(
&mut self,
user_input: impl Into<String>,
) -> Result<String, Error>
pub async fn send( &mut self, user_input: impl Into<String>, ) -> Result<String, Error>
Send a user message and run the loop until the model produces a final answer (text with no tool calls) or the iteration budget is exhausted.
Sourcepub fn tool_schemas(&self) -> Vec<ToolSchema>
pub fn tool_schemas(&self) -> Vec<ToolSchema>
The tool-schema array this agent would advertise on its NEXT
request, exactly as Self::run_loop computes it. Public
(PARITY-18 D1) so a caller can measure the real request-token cost
of an agent’s tool surface — including the current
crate::config::ToolAdvertising mode’s core/deferred split and
the synthetic tool_search/expand_reduction/sidecar_search
schemas — BEFORE ever calling Self::send, e.g. for a preflight
context-guard check.
Sourcepub fn turn_count(&self) -> usize
pub fn turn_count(&self) -> usize
Number of non-system messages exchanged so far.
Sourcepub fn total_output_tokens(&self) -> u64
pub fn total_output_tokens(&self) -> u64
Cumulative output (completion) tokens reported by the provider across
every send on this agent. Zero if the provider reports no usage.
Trait Implementations§
Source§impl Drop for Agent
P5-3 safety hardening (Fable-5 review, MEDIUM-LOW “orphaned billed
spend”): a dropped parent must not leave a detached background child
running against a REAL provider. Without this, a parent dropped
mid-run (the caller’s own process exits the scope, panics, or simply
stops polling) leaves every still-running BackgroundSubagent::handle
as an orphaned tokio::spawn task: nothing had ever awaited or
aborted it, so it runs to its own (max_iterations-bounded)
completion regardless — bounded but real provider spend nobody is
paying attention to.
impl Drop for Agent
P5-3 safety hardening (Fable-5 review, MEDIUM-LOW “orphaned billed
spend”): a dropped parent must not leave a detached background child
running against a REAL provider. Without this, a parent dropped
mid-run (the caller’s own process exits the scope, panics, or simply
stops polling) leaves every still-running BackgroundSubagent::handle
as an orphaned tokio::spawn task: nothing had ever awaited or
aborted it, so it runs to its own (max_iterations-bounded)
completion regardless — bounded but real provider spend nobody is
paying attention to.
.abort() on a tokio::task::JoinHandle is safe to call
unconditionally, including on an ALREADY-finished task (a documented
no-op there — see tokio’s JoinHandle::abort docs) — so this never
needs to distinguish “still running” from “already done”; a background
child that already finished and is merely awaiting a subagent_status
reap is untouched in practice (aborting a finished task changes
nothing observable). For a task still mid-flight, tokio cancels it at
its next .await point, which drops that future in place — including
the _guard: ConcurrencyGuard moved into it at spawn time (see
Self::run_spawn_subagent’s tokio::spawn body) — so the
concurrency-gauge slot is released exactly the same way a normal
completion releases it (ConcurrencyGuard’s own Drop, in
crate::subagents). No separate cleanup call site to forget.
Deliberately does NOT touch [Self::pending_child_approvals] or
Self::subagent_store — this is purely “stop burning provider
calls on behalf of a caller who’s gone”, not a transcript-persistence
path (a child aborted mid-flight has no finished result to persist;
see this build’s named residual on abort-time transcript loss).