pub struct Agent { /* private fields */ }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_journal(&mut self, journal: SessionJournal)
pub fn set_journal(&mut self, journal: SessionJournal)
Install (or replace) this agent’s append-only session journal — the durable, flush-per-record log of every message it produces plus every queue/rewind/plan operation performed on it. Installing one alone changes nothing about the conversation; it only makes the session survive a crash mid-turn.
Sourcepub fn has_journal(&self) -> bool
pub fn has_journal(&self) -> bool
Whether an append-only journal is installed.
Sourcepub fn journal_checkpoint(&self, messages: usize)
pub fn journal_checkpoint(&self, messages: usize)
Declare the durable view caught up: <name>.jsonl now holds
messages messages and every journal record before this point is
already in it. Everything journaled AFTER the last such record is
exactly what a crash would have lost — see
crate::session_journal::JournalState::unpersisted.
Sourcepub fn journal_usage(&self, record: &UsageRecord)
pub fn journal_usage(&self, record: &UsageRecord)
BP-13: record one per-turn usage entry in the append-only journal.
Sourcepub fn journal_model_change(&self, record: &ModelChangeRecord)
pub fn journal_model_change(&self, record: &ModelChangeRecord)
BP-13: record one mid-session model change in the append-only journal — the ONE persisted home for a routing record (BP-8’s journal), never a second file.
Sourcepub fn session_tree(&self) -> Option<&SessionTree>
pub fn session_tree(&self) -> Option<&SessionTree>
BP-8 (catalog:151): this session’s conversation tree, when the module is on.
Sourcepub fn set_session_tree(&mut self, tree: SessionTree)
pub fn set_session_tree(&mut self, tree: SessionTree)
Install a tree loaded from the store (a resume), replacing whatever
this agent built. A no-op when the module is off — a session whose
preset does not enable session_tree must not acquire one through
the back door of an old sidecar.
Sourcepub fn rebuild_session_tree_from_history(&mut self)
pub fn rebuild_session_tree_from_history(&mut self)
BP-8: rebuild the tree from the current linear history — used after
a resume that loaded a transcript but had no .tree.json to restore
(every session recorded before the module was on).
Sourcepub fn rewind_conversation(&mut self, keep: usize) -> RewindOutcome
pub fn rewind_conversation(&mut self, keep: usize) -> RewindOutcome
BP-8 (catalog:152 “Rewind/rollback conversation”): move THIS
conversation back to an earlier point — the whole row, not the
last-exchange special case Self::rewind_to serves and not
sessions fork --at, which makes a different session.
keep is a message count (index into history), so keep = 1
leaves only the system message. Three things happen, in this order:
- the removed tail is pushed onto an undo stack, so
Self::undo_rewindcan put it back; - a
crate::session_journal::JournalOp::Rewindrecord is APPENDED — nothing is deleted from disk, so the rewound-away messages remain recoverable from the log; - when the tree module is on, the active branch’s leaf moves to the
node at
keep, and the old leaf is preserved under a fresh sibling branch — the next message appended forks there rather than overwriting.
Returns what it did. Rewinding to a point at or past the end is a
no-op with removed = 0, never an error.
Sourcepub fn undo_rewind(&mut self) -> bool
pub fn undo_rewind(&mut self) -> bool
BP-8: invert the most recent Self::rewind_conversation — the
messages come back, and the inversion is itself an appended journal
record. false when there is nothing to undo.
Sourcepub fn append_recovered_messages(&mut self, messages: &[ChatMessage])
pub fn append_recovered_messages(&mut self, messages: &[ChatMessage])
BP-8 (catalog:150): append messages recovered from the journal after a crash — they were already recorded, so this deliberately does NOT re-journal them; it puts the live conversation back where the interrupted process left it.
Sourcepub fn undoable_rewinds(&self) -> usize
pub fn undoable_rewinds(&self) -> usize
BP-8: how many rewinds are currently undoable.
Sourcepub fn restore_rewind_undo(&mut self, stack: Vec<Vec<ChatMessage>>)
pub fn restore_rewind_undo(&mut self, stack: Vec<Vec<ChatMessage>>)
BP-8: restore the undo stack a previous process left in the journal,
so /rewind undo works across a restart.
Sourcepub fn restore_queues(&mut self, steer: &[String], follow_up: &[String])
pub fn restore_queues(&mut self, steer: &[String], follow_up: &[String])
BP-8 (catalog:154 “Queued-prompt persistence”): re-queue pending inputs recovered from the journal WITHOUT re-recording them — they are already in the log, and journaling them again would double them on the next restart.
Sourcepub fn plan(&self) -> Vec<PlanEntry>
pub fn plan(&self) -> Vec<PlanEntry>
BP-8 (catalog:156 “Todos/plan persisted per session”): the session’s
current update_plan checklist.
Sourcepub fn set_plan(&mut self, steps: Vec<PlanEntry>)
pub fn set_plan(&mut self, steps: Vec<PlanEntry>)
BP-8: restore a plan read back from the store on resume. Marked as already-journaled, so a resume that changes nothing writes nothing.
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 set_span_summarizer_arc(
&mut self,
summarizer: Arc<dyn SpanSummarizer + Send + Sync>,
)
pub fn set_span_summarizer_arc( &mut self, summarizer: Arc<dyn SpanSummarizer + Send + Sync>, )
Install an already-shared summarizer — same seam as
Self::set_span_summarizer, for callers (and tests) that need to
keep their own handle on it.
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 permissions_approval_cache(&self) -> &ApprovalCache
pub fn permissions_approval_cache(&self) -> &ApprovalCache
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.
BP-10 (catalog row “Session approval caching”): this agent’s
approval cache — the door an embedder/TUI uses to inspect or REVOKE
remembered grants (ApprovalCache::clear forgets every one, in
memory and on disk, and the next matching call asks again). Also
how a test proves a grant really did survive the process:
store_path() names the file a second agent reads back.
pub fn set_permissions_approval_handler( &mut self, handler: impl PermissionsApprovalHandler + 'static, )
Sourcepub fn set_user_question_handler(
&mut self,
handler: Arc<dyn McpElicitationHandler>,
)
pub fn set_user_question_handler( &mut self, handler: Arc<dyn McpElicitationHandler>, )
BP-3 (§2 module 6 tools.question): install the door ask_user
asks the human through — the elicitation/create handler the design
names as the module’s protocol side. SDK-owned frontends pass the
broker-backed handler (crate::server::FrontendRequestBridge:: elicitation_handler), which is what makes the question a real
frontend request the turn waits on. None (the default, nothing
installed) leaves the tool deny-default: it reports that nobody can
be asked instead of blocking.
Installing one alone changes nothing about whether the tool EXISTS —
[capabilities.tools_question] is that gate, applied by
crate::tools::ToolRegistry::from_config.
Sourcepub fn plan_mode(&self) -> &Arc<PlanModeState> ⓘ
pub fn plan_mode(&self) -> &Arc<PlanModeState> ⓘ
BP-3 (§2 module 8): the shared plan-mode state, so a frontend (the
REPL’s /plan, a TUI toggle) can enter or leave the read-only
research phase the same tools and permission gate see.
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 turn_records(&self) -> &[TurnRecord]
pub fn turn_records(&self) -> &[TurnRecord]
BP-7 (catalog §4a “Turn/step bracketing records”): every
crate::turn_record::TurnRecord this agent has accumulated —
the context/usage/finish brackets of each model round-trip plus the
retry, abort, effort and goal markers between them.
Sourcepub fn save_turn_records(
&self,
store: &SessionStore,
name: &str,
) -> Result<(), Error>
pub fn save_turn_records( &self, store: &SessionStore, name: &str, ) -> Result<(), Error>
BP-7: persist the marker log to store under name
(<name>.events.jsonl), the same thin-wrapper shape
Self::save_usage_log has.
Sourcepub fn total_cost_usd(&self) -> f64
pub fn total_cost_usd(&self) -> f64
BP-7 (catalog §4a “Per-turn cost/usage accounting”): dollars this
agent has spent so far. 0.0 when the model is unpriceable — read
Self::model_priced to tell “free” from “unknown”.
Sourcepub fn model_priced(&self) -> bool
pub fn model_priced(&self) -> bool
BP-7: whether this build can price this agent’s model, i.e. whether
Self::total_cost_usd is a real figure rather than a floor.
Sourcepub fn total_steps(&self) -> usize
pub fn total_steps(&self) -> usize
BP-7 (catalog §4a “Turn/budget caps”): tool calls this agent has
executed so far — the counter Config::max_steps bounds.
Sourcepub fn note_abort(&mut self, source: &str)
pub fn note_abort(&mut self, source: &str)
BP-7 (catalog §4a “Interrupt/abort with state preserved”): record that the in-flight turn was interrupted.
Called by whoever owns the cancellation (the CLI’s Ctrl-C race), NOT
by the loop itself: a cancelled send future is dropped mid-await,
so the loop never runs another line. The partial work already
appended to the transcript stands; this marker is what makes the
interruption a persisted FACT — the residue the ledger row named —
rather than something a reader has to infer from a dangling tool
call on reload. Emits AgentEvent::TurnAborted as the live
counterpart.
Sourcepub fn set_goal(&mut self, objective: impl Into<String>) -> bool
pub fn set_goal(&mut self, objective: impl Into<String>) -> bool
Set (or revise) this session’s standing objective.
Returns false, changing nothing, when capabilities.todos.goals
is off — the module gate, not a silent success. A goal restates
itself at the tail of every request until Self::clear_goal, and
each change appends a goal marker to the turn-record log.
Sourcepub fn goal(&self) -> Option<&GoalRecord>
pub fn goal(&self) -> Option<&GoalRecord>
This session’s standing objective, if one is set.
Sourcepub fn clear_goal(&mut self) -> bool
pub fn clear_goal(&mut self) -> bool
Drop the standing objective. true when there was one to drop.
Sourcepub fn save_goal(&self, store: &SessionStore, name: &str) -> Result<(), Error>
pub fn save_goal(&self, store: &SessionStore, name: &str) -> Result<(), Error>
BP-7: persist (or, when cleared, remove) the standing objective
beside the session — same thin-wrapper shape as
Self::save_usage_log.
Sourcepub fn restore_goal(&mut self, goal: Option<GoalRecord>)
pub fn restore_goal(&mut self, goal: Option<GoalRecord>)
BP-7: adopt a goal loaded from the store (a resumed session picks up exactly where it left off). Bypasses the module gate on purpose: a goal already persisted is data to restore, not a new capability being turned on, and dropping it silently would lose session state.
Sourcepub fn effort(&self) -> Option<&str>
pub fn effort(&self) -> Option<&str>
The reasoning-effort level in force for the NEXT request, or None
when extended thinking is off.
Sourcepub fn set_effort(&mut self, effort: Option<String>) -> Option<String>
pub fn set_effort(&mut self, effort: Option<String>) -> Option<String>
Change the reasoning-effort level mid-session.
Some(level) sets the level; None turns extended thinking OFF —
the on/off toggle the ledger row named as distinct from the level.
run_loop reads self.config.effort fresh when it builds each
ChatRequest, so this takes effect on the very next request with no
other copy to update (the same contract Self::set_model has).
The change is appended to the turn-record log as an effort marker,
the extended-thinking analog of the model_change log.
Returns the PREVIOUS setting.
Sourcepub fn review_prompt(&self, args: &str) -> Option<String>
pub fn review_prompt(&self, args: &str) -> Option<String>
The purpose-built review turn’s prompt: the code-review template
from Config::prompts with {args} replaced by args.
None when the resolved config carries no code-review template —
the preset decides whether this harness has a review mode, and the
template IS the report format (both parity presets pin one).
Sourcepub async fn review(&mut self, args: &str) -> Result<String, Error>
pub async fn review(&mut self, args: &str) -> Result<String, Error>
Run the review turn: an ordinary Self::send of
Self::review_prompt, so the review’s request, tools, transcript
and records are the session’s own — a purpose-built TURN, not a
second agent.
Sourcepub async fn side_question(&self, question: &str) -> Result<String, Error>
pub async fn side_question(&self, question: &str) -> Result<String, Error>
Answer question over this session’s FULL current context without
recording anything.
Three properties, all load-bearing and all asserted by this build’s
tests: the request carries the whole conversation as the next turn
would see it; it advertises NO tools, so the model can only answer;
and neither history, the sidecar recorder, the usage log nor the
turn-record log is touched — &self, not &mut self, is the type
system saying so. cc’s /btw and cx’s /side.
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 record_model_change(
&mut self,
from: &str,
to: &str,
reason: Option<&str>,
)
pub fn record_model_change( &mut self, from: &str, to: &str, reason: Option<&str>, )
BP-13 — the ONE place a mid-session model change is performed and
recorded, shared by Self::switch_model (a user asked) and the
run loop’s fallback pass (a provider failed).
It does four things, in this order, and nothing else: strips model-A
reasoning artifacts out of the live history (dep 8 — model B must
never inherit them), moves Config::model, appends the typed
crate::model_change::ModelChangeRecord, and writes that same
record into the append-only session journal (BP-8) — which is where
every persisted routing record lives; there is no second file. The
change is also EMITTED, so a surface that renders events shows the
switch instead of silently answering as a different model.
Sourcepub fn set_service_tier(&mut self, tier: Option<String>)
pub fn set_service_tier(&mut self, tier: Option<String>)
BP-13 (catalog D9 “Fast mode / service tiers”): set or clear the
session-level service-tier override. Some(tier) WINS over the
[capabilities.model_catalog] service_tier rule for every
subsequent request (it is the live toggle the user just pulled);
None puts the configured rule back in charge. Takes effect on the
next request the loop builds, like Self::set_model.
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.
BP-6 additionally resolves SKILL.md invocations here, after the
template table misses: /skill:name args (pi§2 “Skill commands”),
/name args when the config follows Claude Code (cc§7: “a SKILL.md
in a directory = a /name command”), and $slug mentions (cx§7).
$ARGUMENTS in the body is replaced with the trailing text.
Sourcepub fn skills(&self) -> &[LoopSkill]
pub fn skills(&self) -> &[LoopSkill]
The SKILL.md packages this agent discovered (frontmatter only) — the
exact set its prompt index lists and its skill tool can load.
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 refresh_env_context(&mut self) -> bool
pub fn refresh_env_context(&mut self) -> bool
BP-4 (catalog:90, cx§2 <environment_context> “re-emitted on
change”): re-derive the # Environment block and, if anything in it
moved — cwd, the approval/sandbox policy, the git branch or its
dirty state, the date — replace the stale copy in the system message
with the fresh one. Returns whether the block changed.
A no-op (and free — no git subprocess) when core.env_context is
off, which is the default and every non-parity config. Replacing in
place rather than appending a second block is deliberate: two
# Environment sections disagreeing about cwd is worse context than
one stale one, and the system message is re-sent on every request,
so the rewrite IS the re-emission the model sees.
Sourcepub fn model_input(&mut self) -> ChatRequest
pub fn model_input(&mut self) -> ChatRequest
BP-5 (catalog D2 “Prompt-input debugging”: render the exact
model-visible input for inspection; cx§2 codex debug prompt-input,
which “renders the exact model-visible input list as JSON”): the
request this agent would send next.
Built by the SAME two calls the loop makes
(Self::build_request_messages, Self::tool_schemas) and
assembled by the SAME Self::chat_request — it is the real
request, not a reconstruction of one. &mut self because
build_request_messages is: rendering the input is exactly as
stateful as building it for a send.
Sourcepub async fn model_input_for(&mut self, prompt: &str) -> ChatRequest
pub async fn model_input_for(&mut self, prompt: &str) -> ChatRequest
BP-5: Self::model_input for a turn that has not been sent —
prompt is expanded exactly as Self::send would expand it
(slash templates, skills, @path mentions, MCP prompts) and appended
to the conversation IN MEMORY, then the request is rendered.
Deliberately not recorded: this door inspects an input, it does not take a turn. Nothing is written to the session store, no journal entry is made, and no request is issued.
Sourcepub fn render_model_input(req: &ChatRequest) -> Value
pub fn render_model_input(req: &ChatRequest) -> Value
BP-5: a ChatRequest as the JSON a human (or jq) inspects — the
system prompt, every message in order, and every advertised tool
schema, plus the sampling controls that travel with them.
Sourcepub fn base_prompt(&self) -> &str
pub fn base_prompt(&self) -> &str
BP-5 (catalog D2 “Per-model-family base-prompt selection”): the base system prompt currently in force for this agent’s model.
Sourcepub fn inject_context_block(
&mut self,
name: impl Into<String>,
content: impl Into<String>,
) -> bool
pub fn inject_context_block( &mut self, name: impl Into<String>, content: impl Into<String>, ) -> bool
BP-4 (catalog:91 “Synthetic context-injection blocks”): splice one
named ambient block into the live context — the seam a hook’s
additionalContext, a frontend nudge or an orchestrator’s brief
enters through, mid-session, after construction.
Requires core.context_injections (returns false otherwise): the
gate governs the whole registry, not just its startup half. The
block is appended to the system message and remembered, so it is
carried by every later request and re-rendered by
crate::context_injection::assemble wherever the prompt is
rebuilt.
Sourcepub fn spliced_context_blocks(&self) -> &[ContextInjectionBlock]
pub fn spliced_context_blocks(&self) -> &[ContextInjectionBlock]
The blocks spliced in since construction — see
Self::inject_context_block.
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 compact_now(&mut self, focus: Option<&str>) -> bool
pub fn compact_now(&mut self, focus: Option<&str>) -> bool
BP-4 (catalog:98 “Manual compact with focus instructions”, cc§2 /
cx§2 /compact [instructions]): compact NOW, regardless of whether
either automatic trigger has fired — the mechanism behind the REPL’s
/compact [focus].
focus is this invocation’s steering text: it overrides the standing
core.compaction.focus_instructions for this compaction only, is
carried into the SUMMARIZER’s input (so the model-written summary
preserves what the user asked for), and is stated on the marker. An
empty/whitespace focus falls back to the configured standing value,
which is what a bare /compact means.
Returns whether anything was compacted (false when the history is
already at or below the keep-window, or when a ReductionPolicy is
installed — under a policy the reversible A10 path owns clearing, and
a manual compact would be the lossy one).
Sourcepub fn new_context(
&mut self,
objective: &str,
keep_recent: Option<usize>,
) -> usize
pub fn new_context( &mut self, objective: &str, keep_recent: Option<usize>, ) -> usize
BP-4 (catalog:106 “Handoff (fresh objective + curated keep-set)”,
cx§1 new_context): reset the live working view to a fresh
objective plus a curated keep-set, in-session.
The new view is: the system prompt (plus any imported prefix a
CachePlan::ImportedPrefix config protects — same clamp compaction
uses), then a handoff marker stating the objective and what was set
aside, then the most recent keep_recent messages (None = the
token-budget-derived count core.compaction.keep_recent_tokens
already governs, so the keep-set is curated by the same budget the
rest of the compaction machinery uses, not by a magic number). The
keep-set never begins on a tool result, so no tool message is left
orphaned from its originating assistant turn.
Returns how many messages were set aside. Like compaction, the marker is PERSISTED through the recorder, so a resumed session can see where the handoff happened; and like compaction, the set-aside messages remain in the transcript sidecar whenever one is attached.
Scope note: this is the in-session new_context mechanism, NOT
Config::handoff_enabled’s reversible ReductionLog snapshot (the
offline supercode handoff projection) — that one is the reduction
module’s, and stays there.
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 context_usage(&self) -> ContextUsage
pub fn context_usage(&self) -> ContextUsage
BP-4 (catalog:109 “Context-usage introspection”, cc§2 /context
grid, cx§8 /status + get_context_remaining): the LIVE
context-window accounting for this session — the same numbers
resume --dry-run’s preflight already computes
(tokens::estimate_request_tokens / tokens::context_guard), read
out mid-session instead of only before one.
Pure: it projects the request view exactly as
Self::guard_candidate_message does (reduction stubs included,
cache annotation included) without mutating the reduction log, so
asking “how full am I?” can never change what the next request
carries.
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 child_config_for_agent_type(&self, agent_type: &str) -> Option<Config>
pub fn child_config_for_agent_type(&self, agent_type: &str) -> Option<Config>
BP-7 (catalog §4a “Named agent definitions as data”): the child
Config a spawn_subagent of agent_type would build — the
resolved posture a named definition actually produces, including
its crate::subagents::AgentPermissions bundle applied through
the tightening-only rules. None when no definition of that name
is registered or discovered.
Exposed so a caller (and this build’s tests) can ask what a named agent WOULD run as without spawning it and paying for a turn.
Sourcepub fn reaped_subagent_ids(&self) -> Vec<String>
pub fn reaped_subagent_ids(&self) -> Vec<String>
BP-7 (catalog §4a “Background subagents + resume”): the ids of
children that have finished and been reaped, and can therefore be
continued with [SUBAGENT_RESUME].
Sourcepub fn set_lifecycle_hook(
&mut self,
hook: Box<dyn Fn(&LifecycleEvent) + Send + Sync>,
)
pub fn set_lifecycle_hook( &mut self, hook: Box<dyn Fn(&LifecycleEvent) + Send + Sync>, )
Installs the lifecycle observer (compaction and subagent boundaries).
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).