Skip to main content

Agent

Struct Agent 

Source
pub struct Agent { /* private fields */ }
Expand description
Stable since 0.63.0
Agent runtime.

Manages provider, tool registry, state, and compaction, providing an agentic loop for prompt execution, model switching, tool calls, and fallback.

Supports session continuation, tokio-native event streaming, and deferred model switching (changes are queued while a loop is running and applied after it completes).

Implementations§

Source§

impl Agent

Source

pub fn new( provider: Arc<dyn Provider>, config: AgentConfig, tools: Arc<ToolRegistry>, ) -> Agent

Create a new agent with the given provider, config, and tool registry.

Uses the global oxicode_ai::get_provider() / resolve_model_from_id() for model switching. For isolated instances, use new_with_resolver.

Source

pub fn new_with_resolver( provider: Arc<dyn Provider>, config: AgentConfig, tools: Arc<ToolRegistry>, resolver: Arc<dyn ProviderResolver>, ) -> Agent

Create an agent with a custom provider/model resolver.

This is the preferred constructor for SDK usage where provider and model registries must be isolated from global state.

Source

pub fn new_with_compactor( provider: Arc<dyn Provider>, config: AgentConfig, tools: Arc<ToolRegistry>, resolver: Arc<dyn ProviderResolver>, custom_compactor: Option<Arc<dyn Compactor>>, ) -> Agent

Create an agent with a custom provider/model resolver and a custom compactor that replaces the default LLM compactor.

The compactor is threaded into every per-run AgentLoop (via AgentLoopConfig.compactor) — see crate::agent_loop::config::AgentLoopConfig::compactor for the replace semantics. oxicode-sdk’s AgentBuilder::with_compactor uses this constructor.

Source

pub fn new_empty(provider: Arc<dyn Provider>, config: AgentConfig) -> Agent

Create an agent with an empty tool registry.

Source

pub fn model_id(&self) -> String

Get the current model ID

Source

pub fn get_config(&self) -> AgentConfig

Get the agent configuration (full clone)

Source

pub fn todo_provider(&self) -> Option<Arc<dyn TodoStateProvider>>

Get a cheap clone of the configured todo state provider, if any. Used by hosts (e.g. the TUI) to observe todo phase changes without cloning the full AgentConfig.

Source

pub fn resolver(&self) -> &Arc<dyn ProviderResolver>

Get a reference to the provider resolver.

Source

pub fn switch_model(&self, model_id: &str) -> Result<(), Error>

Switch the model used for future LLM calls.

Switch model mid-conversation.

If the agent is currently running, the switch is deferred: the new model and provider are stored in pending_model_switch and applied automatically when the current loop finishes. This ensures the running loop completes with a consistent provider/model without interruption.

If the agent is idle, the switch takes effect immediately.

If the new model uses a different provider API, the conversation history is automatically transformed for cross-provider compatibility (e.g. thinking blocks are converted to <thinking> tags).

§Arguments
  • model_id - New model ID in provider/model format
§Returns

Ok(()) on success, or an error if the model/provider is unknown

§Credentials

The new provider is constructed via ProviderResolver::resolve_provider, which is the single credential authority — the wired AuthProvider port (sync fast-path) supplies the API key. The old api_key parameter was removed in 0.55.0; see issues #39 and #40.

Source

pub fn switch_to_model(&self, model: &Model) -> Result<(), Error>

Switch the model using a pre-resolved Model object.

This is useful when the caller has already looked up the model and optionally created the provider.

Like switch_model, if the agent is currently running, the switch is deferred until the current loop completes.

§Credentials

The new provider is constructed via ProviderResolver::resolve_provider, the single credential authority (sync AuthProvider fast-path). The old api_key parameter was removed in 0.55.0; see issues #39/#40.

Source

pub fn refresh_credentials(&self) -> Result<(), Error>

Refresh credentials by re-resolving the current provider via the resolver.

After the resolver-centric credential model (0.55.0), the provider instance is the single source of truth for API keys. To pick up credential changes — e.g. the user updated their auth store via the TUI overlay — call this to re-resolve the current provider and swap it in. The resolver consults the wired AuthProvider port on every call, so updates are reflected without rebuilding the engine.

Returns Ok(()) if a fresh provider was resolved and swapped, or an error if the resolver could not produce a provider (the existing provider is left untouched on error). Replaces the deprecated refresh_api_key(&self, api_key) from pre-0.55.0; see issues #39/#40.

Source

pub fn tools(&self) -> Arc<ToolRegistry>

Get a handle to the tool registry.

Source

pub fn state(&self) -> AgentState

Get a snapshot of the current agent state.

Source

pub fn update_state(&self, f: impl FnOnce(&mut AgentState))

Update agent state in-place. Used by compaction to replace messages.

Source

pub fn reset(&self)

Reset agent state for a new conversation

Source

pub fn add_tool<T>(&self, tool: T)
where T: AgentTool + 'static,

Register a tool that the agent can invoke during a run.

Source

pub fn set_system_prompt(&self, prompt: String)

Update the system prompt for future interactions.

Source

pub fn compaction_manager(&self) -> &CompactionManager

Get the compaction manager

Source

pub fn set_compaction_strategy(&self, strategy: CompactionStrategy)

Update the compaction strategy for future runs.

The strategy is read fresh from the config at the start of each run (see run_with_channel_inner), so this takes effect on the next agent turn — never mid-run. Pair with compaction_manager() for manual compaction, which is unaffected by the strategy.

Source

pub fn compaction_strategy(&self) -> CompactionStrategy

Get the compaction strategy that will be used on the next run.

This reads from inner.config (mutable via set_compaction_strategy), not from the compaction_manager field (which retains its construction-time strategy). The agent loop reads from config fresh each run, so this is the authoritative value.

Source

pub async fn run( &self, prompt: String, ) -> Result<(Response, Vec<AgentEvent>), Error>

Run the agent with a prompt, collecting all events into a vector.

Convenience wrapper around run_with_channel that gathers every AgentEvent produced during the run.

Source

pub async fn run_with_channel( &self, prompt: String, tx: Sender<AgentEvent>, ) -> Result<Response, Error>

Run the agent, delivering events through the provided channel.

Delegates to the agent loop which implements the same 2-level agentic loop matching pi-mono’s architecture:

AgentLoop.run_messages()
  Outer loop (follow-up messages):
    Inner loop (tool calls + steering):
      1. Inject pending messages (steering)
      2. Compaction check
      3. Stream LLM response (with accumulated partial messages)
      4. Execute tool calls if any
      5. Emit turn_end
      6. Check shouldStopAfterTurn
      7. Poll steering messages
    Check follow-up messages
    Exit
Source

pub async fn run_with_channel_message( &self, prompt: Message, tx: Sender<AgentEvent>, ) -> Result<Response, Error>

Run with an explicit user Message (supports image content blocks). Used by RPC prompt with images. The running-guard logic lives here; run_with_channel delegates after converting its String prompt into a text-only user message.

Source

pub fn set_hooks(&self, hooks: AgentHooks)

Set hooks for the agent loop.

Source

pub fn add_observability_dispatch( &self, f: impl Fn(AgentEvent) + Send + Sync + 'static, )

Register a side-dispatch closure called for every AgentEvent emitted by run, run_with_channel, run_streaming, run_tokio_stream, and continue_with.

Multiple calls stack: every registered closure is invoked on every event. Closures run synchronously on the agent-loop emit thread, so they must be cheap and non-blocking. Long work should be spawned off (e.g. tokio::spawn) by the closure itself.

Used by oxicode-sdk to bridge observability types (Tracer, CostTracker, AuditLog, Authorizer / AccessGate) into the runtime without leaking those types into oxicode-agent.

§Example
agent.add_observability_dispatch(|event| match event {
    AgentEvent::TurnStart { turn_number } => {
        // open a span
    }
    AgentEvent::Usage { input_tokens, output_tokens } => {
        // record cost
    }
    _ => {}
});
Source

pub fn cancel(&self)

Request cancellation of the current agent run.

Sets a shared cancel_flag that is propagated to the AgentLoop’s external_stop on every event AND polled every ~500ms by the streaming loop’s periodic check. This ensures cancellation is detected quickly even when the provider stream is completely hung (no events arriving).

Source

pub fn set_auto_retry(&self, enabled: bool)

Toggle auto-retry at runtime (affects the next retry decision in an active run; does not interrupt an in-progress retry sleep — use Self::cancel_auto_retry for that).

Source

pub fn cancel_auto_retry(&self)

Abort any in-progress auto-retry wait immediately. The running turn ends without retrying the error.

Source

pub fn reset_cancel(&self)

Reset the cancellation flag before starting a new run.

Source

pub async fn run_streaming<F>( &self, prompt: String, on_event: F, ) -> Result<Response, Error>
where F: FnMut(AgentEvent) + Send,

Run the agent, invoking on_event for each AgentEvent produced.

Blocking convenience wrapper suitable for callers that prefer a callback-based API over a channel.

Source

pub fn export_state(&self) -> Result<Value, Error>

Export the agent state as a JSON value.

The serialized state includes conversation messages, token counts, iteration progress, and stop reason. Use import_state to restore.

Source

pub fn import_state(&self, value: Value) -> Result<(), Error>

Import agent state from a JSON value.

Restores conversation history, token counts, and iteration progress. Typically used together with export_state for session persistence.

Source

pub async fn continue_with( &self, prompt: String, ) -> Result<(Response, Vec<AgentEvent>), Error>

Continue the current session with a new prompt.

Unlike run(), which can be used on a fresh agent, continue_with preserves the existing conversation state and appends the new prompt. This enables multi-turn interactions within the same session.

Source

pub async fn run_tokio_stream( &self, prompt: String, ) -> Result<(Receiver<AgentEvent>, JoinHandle<Result<Response, Error>>), Error>

Run the agent with tokio-native event streaming.

Returns a tokio::sync::mpsc::Receiver for events and a JoinHandle for the response. This is the preferred API for async runtimes (WebSocket/SSE gateways, tokio-based servers).

§Example
let (rx, handle) = agent.run_tokio_stream("Explain Rust".into()).await?;
while let Some(event) = rx.recv().await {
    println!("Event: {:?}", event.type_name());
}
let response = handle.await??;

Auto Trait Implementations§

§

impl !Freeze for Agent

§

impl !RefUnwindSafe for Agent

§

impl !UnwindSafe for Agent

§

impl Send for Agent

§

impl Sync for Agent

§

impl Unpin for Agent

§

impl UnsafeUnpin for Agent

Blanket Implementations§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

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

Source§

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

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

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

Source§

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

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

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

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

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

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

Performs the conversion.
Source§

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

Source§

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

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

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

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

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

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

fn with_current_subscriber(self) -> WithDispatch<Self>

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