Skip to main content

Agent

Struct Agent 

Source
pub struct Agent { /* private fields */ }
Expand description

Stable, ergonomic entry point for agent execution.

Wraps a bamboo_engine::Agent (which owns the shared runtime) plus the instruction / tool policy / model configured at build time. Clone is cheap.

Implementations§

Source§

impl Agent

Source

pub fn builder() -> AgentBuilder

Return a new ergonomic builder.

Source

pub fn from_runtime(inner: Agent) -> Agent

Wrap an existing engine Agent with no extra role configuration.

Source

pub async fn run( &self, session: &mut Session, input: impl Into<String>, ) -> Result<(), AgentError>

Run the agent loop on session with the given input, draining events internally until completion.

The configured instruction + model are applied to the session before execution; the tool set was fixed on the agent’s executor at build time.

NOTE: this variant discards every AgentEvent (tool calls, tokens, intermediate errors) — you only get the final Result. To observe the run, use run_stream instead. To cancel a blocking run from another task, use run_with_cancel.

Source

pub async fn run_with_cancel( &self, session: &mut Session, input: impl Into<String>, cancel_token: CancellationToken, ) -> Result<(), AgentError>

Like run but driven by a caller-owned CancellationToken: cancelling the token from another task stops the loop at the next check point. Events are still discarded (see run).

Source

pub async fn run_session(&self, session: &mut Session) -> Result<(), AgentError>

Run the agent loop on session exactly as it stands — i.e. on a caller-provided message list — without appending a new turn. The last User message already in the session drives execution.

This is how you pass a full conversation / message list: build the session from your messages, then run it.

let mut session = Session::new("s1", "claude-sonnet-4-6");
session.add_message(Message::user("hi"));
session.add_message(Message::assistant("hello!", None));
session.add_message(Message::user("now summarize our chat"));
agent.run_session(&mut session).await?; // no extra input appended
Source

pub async fn run_session_with_cancel( &self, session: &mut Session, cancel_token: CancellationToken, ) -> Result<(), AgentError>

Like run_session but driven by a caller-owned CancellationToken, so a blocking run can be cancelled from another task. Events are still discarded (see run).

Source

pub fn run_stream( &self, session: Session, input: impl Into<String>, ) -> Receiver<AgentEvent>

Append input as a new user turn, then stream the run’s AgentEvents. The execution runs on a background task; the caller drives it by reading from the returned receiver until it closes.

Source

pub fn run_stream_cancellable( &self, session: Session, input: impl Into<String>, ) -> (Receiver<AgentEvent>, CancellationToken)

Like run_stream, but also returns a CancellationToken for the run: call token.cancel() to stop the loop at the next check point. Dropping the receiver does NOT cancel the run, so this is the way to interrupt a streaming agent.

Source

pub fn run_stream_session(&self, session: Session) -> Receiver<AgentEvent>

Stream the run’s AgentEvents for a caller-provided message list, without appending a new turn (the last User message drives execution).

Source

pub fn run_stream_session_cancellable( &self, session: Session, ) -> (Receiver<AgentEvent>, CancellationToken)

Like run_stream_session but also returns the run’s CancellationToken so the caller can interrupt it.

Source

pub fn run_stream_session_with_cancel( &self, session: Session, cancel_token: CancellationToken, ) -> Receiver<AgentEvent>

Stream a caller-provided message list under a caller-owned CancellationToken. The shared entry point the other run_stream* helpers funnel into.

Source

pub async fn execute( &self, session: &mut Session, request: ExecuteRequest, ) -> Result<(), AgentError>

Escape hatch for full per-request control: run a fully-specified ExecuteRequest (split fast/background/summarization models, provider handle, skill selection, custom event channel, cancellation token, …) on session via the single canonical engine execution path — the same path run / run_stream funnel into.

Unlike run/run_stream, this does NOT apply the builder’s configured instruction or model: the caller owns the request entirely. Build it with ExecuteRequestBuilder.

let (tx, _rx) = tokio::sync::mpsc::channel(256);
let req = ExecuteRequestBuilder::new("investigate X", tx, Default::default())
    .model("claude-sonnet-4-6")
    .build();
agent.execute(&mut session, req).await?;
Source

pub fn storage(&self) -> &Arc<dyn Storage>

Access the shared storage backend.

Source

pub fn persistence(&self) -> &Arc<dyn RuntimeSessionPersistence>

Access the runtime persistence adapter.

Source

pub async fn answer( &self, session_id: impl Into<String>, response: impl Into<String>, ) -> Result<AnswerOutcome, SdkError>

Answer a suspended session’s pending question — a conclusion_with_options clarification OR a permission-approval prompt (NeedClarification / ToolApprovalRequested events; both suspend via the same session.pending_question mechanism, per bamboo_engine::session_app::respond). This is the in-process equivalent of the HTTP POST /api/v1/sessions/{id}/respond endpoint — same use case function (submit_pending_response), so behavior (validation, plan-mode transitions, permission-grant extraction) matches exactly.

response must be one of the pending question’s options unless it allow_customs a free-form answer (returns SdkError::InvalidResponse otherwise). Loads the session by ID from storage, so the run must have already suspended (and thus persisted) before calling this — the session is NOT taken from an in-memory handle.

If this Agent was built with AgentBuilder::permission_checker, any permission grants implied by an approved permission prompt are applied to it automatically (mirroring what the HTTP handler does for state.permission_checker), so the resumed re-attempt of the gated operation passes the checker without prompting again.

After answering, resume execution with resume / resume_stream on the returned AnswerOutcome::session — or use answer_and_resume_stream to do both in one call.

Like the HTTP server’s /respond handler, approving a gated tool call here also re-executes it for real once the run resumes: answer (via submit_pending_response) stamps the returned session’s metadata with PERMISSION_REEXECUTE_METADATA_KEY, and the next call into resume/resume_stream/run* re-runs the originally-gated tool call against the agent’s tool executor and overwrites the synthetic “Selected response: Approve” placeholder with the operation’s genuine output before the loop continues — see reexecute_approved_tool_if_pending.

NOTE: ChildApprovalRequested (an out-of-process sub-agent worker’s gated tool, proxied over the actor protocol) is a SEPARATE mechanism from pending_question/respond and is not covered by this method — use answer_child_approval instead.

Source

pub async fn resume(&self, session: &mut Session) -> Result<(), AgentError>

Resume execution on session — i.e. continue the agent loop from its current state (e.g. the tool result answer just appended) WITHOUT appending a new user turn, draining events internally until completion. An alias for run_session that documents intent at the call site: the engine’s execution entry point always resumes from whatever is already in session.messages (run/run_stream are the ones that append a fresh turn first).

Source

pub async fn resume_with_cancel( &self, session: &mut Session, cancel_token: CancellationToken, ) -> Result<(), AgentError>

Like resume, but driven by a caller-owned CancellationToken.

Source

pub fn resume_stream(&self, session: Session) -> Receiver<AgentEvent>

Like resume, but streams AgentEvents instead of draining them. An alias for run_stream_session.

Source

pub fn resume_stream_cancellable( &self, session: Session, ) -> (Receiver<AgentEvent>, CancellationToken)

Like resume_stream, but also returns a CancellationToken for the resumed run.

Source

pub async fn answer_and_resume_stream( &self, session_id: impl Into<String>, response: impl Into<String>, ) -> Result<Receiver<AgentEvent>, SdkError>

Convenience: answer a pending question, then immediately resume_stream on the resulting session — the common “ask → answer → resume” flow in one call.

Source

pub fn answer_child_approval( &self, child_session_id: impl AsRef<str>, request_id: impl AsRef<str>, approved: bool, ) -> bool

Answer an out-of-process child sub-agent’s gated-tool approval request (an AgentEvent::ChildApprovalRequested surfaced on a run’s event stream — e.g. from run_stream/ resume_stream) — the in-process equivalent of the HTTP POST /api/v1/child-approval/{child_session_id} endpoint (see bamboo_server::handlers::agent::child_approval).

This is a SEPARATE mechanism from answer/ pending_question: a child sub-agent worker running out-of-process (over the actor protocol, e.g. a broker worker) that hits a gated tool escalates the approval request UP to this process rather than suspending its own pending_question, and the engine tracks it in a process-global pending-approval registry (bamboo_engine::external_agents::live) keyed by (child_session_id, request_id) — both taken verbatim from the surfaced event. There is no session to load/save here: this only delivers the decision over the child’s live connection (or fails it closed if the child already disconnected or the request already resolved/timed out).

Returns true if the decision was delivered to a genuinely-pending request, false if request_id is unknown, was already answered, timed out, or the child is no longer live — mirroring the HTTP handler’s 200-vs-404 distinction. A false result does not need cleanup on the caller’s part: the request is either already resolved or has moved on.

§Boundary

This method only covers the TOP-orchestrator, human-in-the-loop leg of child approval (the leg that surfaces ChildApprovalRequested at all). It requires the agent to actually be driving an out-of-process child — i.e. the caller has wired the engine’s external_agents actor transport (broker/worker) — which AgentBuilder::with_defaults_for_data_dir does NOT assemble; that machinery is a separate, opt-in subsystem. Calling this without a live child matching child_session_id/request_id simply returns false (no panic, no error) — the same as an unmatched HTTP POST.

Source

pub async fn list_sessions(&self) -> Result<Vec<SessionIndexEntry>, SdkError>

List every session in the data directory, most-recently-updated first.

Only available when this Agent was built via AgentBuilder::with_defaults_for_data_dir (which assembles the concrete session-index handle this needs) — returns SdkError::Unsupported otherwise.

Source

pub async fn get_session( &self, session_id: &str, ) -> Result<Option<Session>, SdkError>

Load a session by ID (its full message history + runtime state), or Ok(None) if it doesn’t exist.

Source

pub async fn session_history( &self, session_id: &str, ) -> Result<Vec<Message>, SdkError>

The message history for a session, or SdkError::SessionNotFound if it doesn’t exist.

Source

pub async fn delete_session(&self, session_id: &str) -> Result<bool, SdkError>

Delete a session. Returns true if a session was actually deleted.

Trait Implementations§

Source§

impl Clone for Agent

Source§

fn clone(&self) -> Agent

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

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

Performs copy-assignment from source. Read more
Source§

impl SessionAccess for Agent

SessionAccess for Agent, backed by Agent::storage (reads) and Agent::persistence (writes) — no separate cache tier, unlike the server’s cache+storage+persistence-backed SessionRepository, since the SDK has no cross-request cache to keep coherent. This is what lets Agent::answer call the same bamboo_engine::session_app::respond use-case function the HTTP /respond handler calls on AppState.

Source§

fn load_session<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<Option<Session>, SessionLoadError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Agent: 'async_trait,

Load a session by ID (from cache or storage).
Source§

fn load_or_create<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, id: &'life1 str, model: &'life2 str, ) -> Pin<Box<dyn Future<Output = Result<Session, SessionLoadError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Agent: 'async_trait,

Load an existing session or create a new one with the given model.
Source§

fn load_merged<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 str, ) -> Pin<Box<dyn Future<Output = Result<Option<Session>, SessionLoadError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Agent: 'async_trait,

Load a session, merging memory and storage using a preference heuristic. Read more
Source§

fn save_session<'life0, 'life1, 'async_trait>( &'life0 self, session: &'life1 mut Session, ) -> Pin<Box<dyn Future<Output = Result<(), SessionSaveError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Agent: 'async_trait,

Save a session to persistent storage only. Read more
Source§

fn save_and_cache<'life0, 'life1, 'async_trait>( &'life0 self, session: &'life1 mut Session, ) -> Pin<Box<dyn Future<Output = Result<(), SessionSaveError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Agent: 'async_trait,

Save a session to persistent storage and update the in-memory cache. Read more

Auto Trait Implementations§

§

impl !RefUnwindSafe for Agent

§

impl !UnwindSafe for Agent

§

impl Freeze 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> CloneToUninit for T
where T: Clone,

Source§

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

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

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

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

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

fn in_current_span(self) -> Instrumented<Self>

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

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

Source§

fn into(self) -> U

Calls U::from(self).

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

Source§

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

Source§

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

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

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

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

impl<T> 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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

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

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

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

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

Source§

type Error = 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