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
impl Agent
Sourcepub fn builder() -> AgentBuilder
pub fn builder() -> AgentBuilder
Return a new ergonomic builder.
Sourcepub fn from_runtime(inner: Agent) -> Agent
pub fn from_runtime(inner: Agent) -> Agent
Wrap an existing engine Agent with no extra
role configuration.
Sourcepub async fn run(
&self,
session: &mut Session,
input: impl Into<String>,
) -> Result<(), AgentError>
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.
Sourcepub async fn run_with_cancel(
&self,
session: &mut Session,
input: impl Into<String>,
cancel_token: CancellationToken,
) -> Result<(), AgentError>
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).
Sourcepub async fn run_session(&self, session: &mut Session) -> Result<(), AgentError>
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 appendedSourcepub async fn run_session_with_cancel(
&self,
session: &mut Session,
cancel_token: CancellationToken,
) -> Result<(), AgentError>
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).
Sourcepub fn run_stream(
&self,
session: Session,
input: impl Into<String>,
) -> Receiver<AgentEvent>
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.
Sourcepub fn run_stream_cancellable(
&self,
session: Session,
input: impl Into<String>,
) -> (Receiver<AgentEvent>, CancellationToken)
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.
Sourcepub fn run_stream_session(&self, session: Session) -> Receiver<AgentEvent>
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).
Sourcepub fn run_stream_session_cancellable(
&self,
session: Session,
) -> (Receiver<AgentEvent>, CancellationToken)
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.
Sourcepub fn run_stream_session_with_cancel(
&self,
session: Session,
cancel_token: CancellationToken,
) -> Receiver<AgentEvent>
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.
Sourcepub async fn execute(
&self,
session: &mut Session,
request: ExecuteRequest,
) -> Result<(), AgentError>
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?;Sourcepub fn persistence(&self) -> &Arc<dyn RuntimeSessionPersistence> ⓘ
pub fn persistence(&self) -> &Arc<dyn RuntimeSessionPersistence> ⓘ
Access the runtime persistence adapter.
Sourcepub async fn answer(
&self,
session_id: impl Into<String>,
response: impl Into<String>,
) -> Result<AnswerOutcome, SdkError>
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.
Sourcepub async fn resume(&self, session: &mut Session) -> Result<(), AgentError>
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).
Sourcepub async fn resume_with_cancel(
&self,
session: &mut Session,
cancel_token: CancellationToken,
) -> Result<(), AgentError>
pub async fn resume_with_cancel( &self, session: &mut Session, cancel_token: CancellationToken, ) -> Result<(), AgentError>
Like resume, but driven by a caller-owned
CancellationToken.
Sourcepub fn resume_stream(&self, session: Session) -> Receiver<AgentEvent>
pub fn resume_stream(&self, session: Session) -> Receiver<AgentEvent>
Like resume, but streams AgentEvents instead of
draining them. An alias for
run_stream_session.
Sourcepub fn resume_stream_cancellable(
&self,
session: Session,
) -> (Receiver<AgentEvent>, CancellationToken)
pub fn resume_stream_cancellable( &self, session: Session, ) -> (Receiver<AgentEvent>, CancellationToken)
Like resume_stream, but also returns a
CancellationToken for the resumed run.
Sourcepub async fn answer_and_resume_stream(
&self,
session_id: impl Into<String>,
response: impl Into<String>,
) -> Result<Receiver<AgentEvent>, SdkError>
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.
Sourcepub fn answer_child_approval(
&self,
child_session_id: impl AsRef<str>,
request_id: impl AsRef<str>,
approved: bool,
) -> bool
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.
Sourcepub async fn list_sessions(&self) -> Result<Vec<SessionIndexEntry>, SdkError>
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.
Sourcepub async fn get_session(
&self,
session_id: &str,
) -> Result<Option<Session>, SdkError>
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.
Sourcepub async fn session_history(
&self,
session_id: &str,
) -> Result<Vec<Message>, SdkError>
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.
Trait Implementations§
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.
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.