Skip to main content

SubagentHandle

Struct SubagentHandle 

Source
pub struct SubagentHandle<'a, S> { /* private fields */ }
Expand description

One open subagent invocation.

Created by RunContext::subagent. SUBAGENT_STARTED has already gone out; Drop emits SUBAGENT_FINISHED with a success outcome, including on the early return that a ? produces.

A subagent is a scope, like a step: everything emitted through the handle comes out attributed to it. The handle therefore dereferences to the run context — messages, tool calls, reasoning, steps and nested subagents all open through it, and every event they produce carries this invocation’s subagentRunId without the agent saying so:

{
    let mut researcher = ctx.subagent("researcher")?;
    researcher.say("Three sources found.")?;   // attributed, through Deref
    researcher.finish_with(serde_json::json!({ "sources": 3 }))?;
}
ctx.say("Thanks.")?;                            // the parent's own, untagged

let events = events.drain();
assert_eq!(events[0].event_type(), EventType::SubagentStarted);
assert_eq!(events[1].subagent_run_id().map(|id| id.as_str()), Some("r-sub-1"));
assert_eq!(events[4].event_type(), EventType::SubagentFinished);
assert_eq!(events[5].subagent_run_id(), None);

Nesting is automatic: a subagent opened through a handle gets the handle’s id as its parentSubagentRunId. Two subagents cannot be open at once through handles — the second subagent() is a borrow-check error, as everything overlapping is here. For subagents that genuinely stream concurrently, tag events yourself and emit them interleaved; see the module docs.

§Ending it

The terminator names the subagent it closes and is not itself attributed to it, so every method here restores the enclosing attribution before emitting. Drop cannot tell success from failure: on the error path you care about, call fail — or suspend when the run is about to pause on an interrupt the subagent raised.

Implementations§

Source§

impl<'a, S> SubagentHandle<'a, S>

Source

pub fn id(&self) -> &SubagentRunId

The id every event emitted through this handle carries.

Source

pub fn name(&self) -> &str

The subagent’s declared name, as announced.

Source

pub fn finish(self) -> Result<()>

Emits SUBAGENT_FINISHED with a success outcome and consumes the handle.

Only worth calling over letting the handle drop when you want to see the error: Drop cannot report one.

Source

pub fn finish_with(self, result: impl Into<Value>) -> Result<()>

Emits SUBAGENT_FINISHED carrying a completion payload — the subagent’s counterpart of RUN_FINISHED.result.

Source

pub fn suspend(self, interrupt_ids: impl Into<Vec<String>>) -> Result<()>

Emits SUBAGENT_FINISHED with a suspended outcome: the subagent is waiting on interrupt_ids, which the run is about to return in an interrupt outcome.

Build each interrupt with Interrupt::with_subagent_run_id so a client can render it inside this subagent’s group, and announce the same id again on the resuming run to continue the invocation.

Source

pub fn fail(self, message: impl Into<String>) -> Result<()>

Emits SUBAGENT_ERROR and consumes the handle.

Source

pub fn fail_with_code( self, message: impl Into<String>, code: impl Into<String>, ) -> Result<()>

Emits SUBAGENT_ERROR with a machine-readable code.

Methods from Deref<Target = RunContext<S>>§

Source

pub fn state(&self) -> &S

The typed state, as of the last publish.

Source

pub fn state_mut(&mut self) -> &mut S

The typed state, mutably. Nothing is emitted until you call publish_state.

Source

pub fn set_state(&mut self, state: &S) -> Result<()>

Replaces the state and publishes the change.

The first publish of a run is a STATE_SNAPSHOT; later ones are a STATE_DELTA unless the patch would be no smaller than the snapshot. See StateManager.

Source

pub fn update_state(&mut self, update: impl FnOnce(&mut S)) -> Result<()>

Mutates the state in place and publishes the change.

#[derive(Default, Serialize, Deserialize)]
struct Draft { revision: u32 }

ctx.update_state(|draft| draft.revision += 1)?;
assert_eq!(ctx.state().revision, 1);
Source

pub fn publish_state(&mut self) -> Result<()>

Publishes whatever state_mut left behind.

A no-op when nothing changed since the last publish.

Source

pub fn input(&self) -> &RunAgentInput

The whole request, for anything the accessors do not cover.

Source

pub fn thread_id(&self) -> &ThreadId

The conversation this run belongs to.

Source

pub fn run_id(&self) -> &RunId

This run’s id.

Source

pub fn parent_run_id(&self) -> Option<&RunId>

The run that spawned this one, for nested agents.

Source

pub fn messages(&self) -> &[Message]

Conversation history, oldest first.

Source

pub fn last_user_text(&self) -> Option<String>

What the user said last, as text.

The turn an agent is almost always answering. Non-text parts of a multimodal message are dropped — see UserContent::to_text; reach into RunContext::messages directly if the images matter.

None when the history holds no user message at all, which is distinct from a user who sent an empty one.

let mut input = RunAgentInput::new("thread-1", "run-1");
input.messages = vec![Message::user("msg-1", "add milk")];
let (ctx, _events) = RunContext::<()>::new(input)?;

assert_eq!(ctx.last_user_text().as_deref(), Some("add milk"));
Source

pub fn tools(&self) -> &[Tool]

Tools the client is offering for this run.

Source

pub fn tool(&self, name: &str) -> Option<&Tool>

One offered tool by name.

Source

pub fn context(&self) -> &[Context]

Ambient context entries.

Source

pub fn forwarded_props(&self) -> &Value

Arbitrary passthrough properties, opaque to the protocol.

Source

pub fn resume(&self) -> &[ResumeEntry]

Answers to the interrupts a previous run paused on.

Empty unless this request resumes a paused run.

Source

pub fn resume_for(&self, interrupt_id: &str) -> Option<&ResumeEntry>

The answer to one interrupt, by its Interrupt::id.

Source

pub fn is_resume(&self) -> bool

Whether this request resumes a paused run.

Source

pub fn is_cancelled(&self) -> bool

Whether the run has been cancelled.

Source

pub fn check_cancelled(&self) -> Result<()>

Error::Cancelled once the run is cancelled, for use with ?.

Source

pub fn cancel_token(&self) -> CancellationToken

A handle a transport can trip on client disconnect.

Source

pub fn cancelled(&self) -> Cancelled

Resolves once the run is cancelled.

Source

pub fn until_cancelled<F: Future>( &self, future: F, ) -> impl Future<Output = Option<F::Output>>

Races future against cancellation, returning None if cancellation won.

The way to make a long model call interruptible:

let answer = ctx
    .until_cancelled(async { "the model's reply" })
    .await
    .ok_or(Error::Cancelled)?;
assert_eq!(answer, "the model's reply");

Deliberately not an async fn: that would capture &self in the returned future, and a future holding a borrow of the run context is only Send if the context is Sync — which it is not, since a transformer only has to be Send.

Source

pub fn emit(&mut self, event: Event) -> Result<()>

Emits an event as-is — the escape hatch under the typed emitters.

Everything the handles emit goes through here, so a raw event is transformed and verified like any other.

Source

pub fn new_message_id(&mut self) -> MessageId

A fresh message id, unique within the run.

Derived from the run id and a counter rather than a UUID: the protocol asks for opaque strings, this crate takes no uuid dependency, and a deterministic id makes a recorded stream diffable. Pass your own id to message_with_id when you need one.

Source

pub fn new_tool_call_id(&mut self) -> ToolCallId

A fresh tool call id, unique within the run.

Source

pub fn new_subagent_run_id(&mut self) -> SubagentRunId

A fresh subagent invocation id, unique within the run.

Derived like the others, from the run id and a counter — so, like message ids, it is unique across runs only while run ids are. A resuming run that continues a suspended subagent should reuse the suspended id instead — see subagent_with.

Source

pub fn subagent_run_id(&self) -> Option<&SubagentRunId>

The subagent everything emitted right now is attributed to — None outside any subagent scope.

Source

pub fn subagent( &mut self, name: impl Into<String>, ) -> Result<SubagentHandle<'_, S>>

Announces a subagent under a fresh id and scopes everything emitted through the returned handle to it — SUBAGENT_STARTED now, SUBAGENT_FINISHED when the handle drops.

name is the subagent’s reusable type or name, for display; the id is this invocation’s alone. See SubagentHandle.

Source

pub fn subagent_with( &mut self, started: SubagentStartedEvent, ) -> Result<SubagentHandle<'_, S>>

The same, from an announcement you built — for a description, an explicit id, or the agents-as-tools links.

A parent_subagent_run_id left absent is filled from the enclosing scope, so nesting needs no help. An explicit id is how a resuming run continues a subagent that suspended: announce the id the suspended invocation had, and a client transitions its group from waiting back to running rather than drawing a second one.

let mut call = ctx.tool_call("task")?;
call.args(r#"{"brief":"find sources"}"#)?;
let (call_id, result_id) = (call.id().clone(), call.result_message_id().clone());
call.end()?;                                   // the client sees the call close…

let announce = SubagentStartedEvent::new("researcher-7", "researcher")
    .with_parent_tool_call(call_id.clone());
let mut researcher = ctx.subagent_with(announce)?;
researcher.say("Three sources found.")?;       // …then the subagent it spawned…
researcher.finish()?;

ctx.emit(Event::tool_call_result(result_id, call_id, "3 sources"))?;  // …then its result
let types: Vec<_> = events.drain().iter().map(Event::event_type).collect();
assert_eq!(types[3], EventType::SubagentStarted);
assert_eq!(types[7], EventType::SubagentFinished);
assert_eq!(types[8], EventType::ToolCallResult);
Source

pub fn assistant_message(&mut self) -> Result<MessageHandle<'_, S>>

Opens an assistant message under a fresh id — TEXT_MESSAGE_START.

Source

pub fn message(&mut self, role: TextMessageRole) -> Result<MessageHandle<'_, S>>

Opens a message with the given role under a fresh id.

Source

pub fn message_with_id( &mut self, id: impl Into<MessageId>, role: TextMessageRole, ) -> Result<MessageHandle<'_, S>>

Opens a message under an id you choose.

Source

pub fn say(&mut self, text: impl Into<String>) -> Result<MessageId>

Emits a whole assistant message — start, content, end — and returns its id.

Source

pub fn reasoning(&mut self) -> Result<ReasoningHandle<'_, S>>

Opens a reasoning block under a fresh id — REASONING_START.

Source

pub fn reasoning_with_id( &mut self, id: impl Into<MessageId>, ) -> Result<ReasoningHandle<'_, S>>

Opens a reasoning block under an id you choose.

Source

pub fn think(&mut self, text: impl Into<String>) -> Result<MessageId>

Emits a whole reasoning block in one call and returns its id.

Source

pub fn tool_call(&mut self, name: &str) -> Result<ToolCallHandle<'_, S>>

Opens a call to name under a fresh id — TOOL_CALL_START.

Source

pub fn tool_call_with_id( &mut self, id: impl Into<ToolCallId>, name: &str, ) -> Result<ToolCallHandle<'_, S>>

Opens a call to name under an id you choose.

Source

pub fn step(&mut self, name: impl Into<StepName>) -> Result<StepGuard<'_, S>>

Opens a named step — STEP_STARTED.

The returned guard dereferences to this context, and emits STEP_FINISHED when it drops.

Trait Implementations§

Source§

impl<'a, S: Debug> Debug for SubagentHandle<'a, S>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<S> Deref for SubagentHandle<'_, S>

Source§

type Target = RunContext<S>

The resulting type after dereferencing.
Source§

fn deref(&self) -> &Self::Target

Dereferences the value.
Source§

impl<S> DerefMut for SubagentHandle<'_, S>

Source§

fn deref_mut(&mut self) -> &mut Self::Target

Mutably dereferences the value.
Source§

impl<S> Drop for SubagentHandle<'_, S>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl<'a, S> !RefUnwindSafe for SubagentHandle<'a, S>

§

impl<'a, S> !Sync for SubagentHandle<'a, S>

§

impl<'a, S> !UnwindSafe for SubagentHandle<'a, S>

§

impl<'a, S> Freeze for SubagentHandle<'a, S>

§

impl<'a, S> Send for SubagentHandle<'a, S>
where S: Send,

§

impl<'a, S> Unpin for SubagentHandle<'a, S>

§

impl<'a, S> UnsafeUnpin for SubagentHandle<'a, S>

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<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

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

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

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.

§

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

§

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
§

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<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
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.
§

impl<T> WithSubscriber for T

§

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
§

fn with_current_subscriber(self) -> WithDispatch<Self>

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