Skip to main content

SessionBuilder

Struct SessionBuilder 

Source
pub struct SessionBuilder<Counterpart, Run: RunWithConnectionTo<Counterpart> = NullRun, BlockState: SessionBlockState = NonBlocking>
where Counterpart: HasPeer<Agent>,
{ /* private fields */ }
Expand description

Session builder for a new session request. Allows you to add MCP servers or set other details for this session.

The BlockState type parameter tracks whether blocking methods are available:

Implementations§

Source§

impl<Counterpart, R, BlockState> SessionBuilder<Counterpart, R, BlockState>
where Counterpart: HasPeer<Agent>, R: RunWithConnectionTo<Counterpart>, BlockState: SessionBlockState,

Source

pub fn with_mcp_server<McpRun>( self, mcp_server: McpServer<Counterpart, McpRun>, ) -> Result<SessionBuilder<Counterpart, ChainRun<R, McpRun>, BlockState>, Error>
where McpRun: RunWithConnectionTo<Counterpart>,

Available on crate feature unstable_mcp_over_acp only.

Attach an MCP server to this new session.

Source

pub fn on_session_start<F, Fut>(self, op: F) -> Result<(), Error>
where R: 'static, F: FnOnce(ActiveSession<'static, Counterpart>) -> Fut + Send + 'static, Fut: Future<Output = Result<(), Error>> + Send,

Spawn a task that runs the provided closure once the session starts.

Unlike start_session, this method returns immediately without blocking the current task. The session handshake and closure execution happen in a spawned background task.

The closure receives an ActiveSession<'static, _> and runs in a spawned task. If it returns an error, the error propagates to the connection’s task handling.

§Example
cx.build_session_cwd()?
    .with_mcp_server(mcp)?
    .on_session_start(async |mut session| {
        // Do something with the session
        session.send_prompt("Hello")?;
        let response = session.read_to_string().await?;
        Ok(())
    })?;
// Returns immediately, session runs in background
§Ordering

Session runners are scheduled and routing setup is installed before the dispatch loop processes the next message when the session response is routed during its original dispatch. No user callback code runs under that ordering guarantee: the callback is invoked in a spawned task, so it may wait for later session traffic without deadlocking the connection. A response interceptor that retains the response and routes it later cannot retroactively order session setup before messages the dispatch loop has already processed.

Source

pub fn on_proxy_session_start<F, Fut>( self, responder: Responder<NewSessionResponse>, op: F, ) -> Result<(), Error>
where F: FnOnce(SessionId) -> Fut + Send + 'static, Fut: Future<Output = Result<(), Error>> + Send, Counterpart: HasPeer<Client>, R: 'static,

Spawn a proxy session and run a closure with the session ID.

A proxy session starts the session with the agent and then automatically proxies all session updates (prompts, tool calls, etc.) from the agent back to the client. You don’t need to handle any messages yourself - the proxy takes care of forwarding everything. This is useful when you want to inject and/or filter prompts coming from the client but otherwise not be involved in the session.

Unlike start_session_proxy, this method returns immediately without blocking the current task. The session handshake, client response, and proxy setup all happen in a spawned background task.

The closure receives the SessionId once the session is established. Use it for logging or eventual tracking; it runs concurrently with later connection traffic. Register ID-independent state that later handlers must observe before calling this helper. For ID-keyed bookkeeping, install a gate or placeholder first, make later handlers await it, and populate it from the closure.

§Example
Proxy.builder()
    .on_receive_request_from(Client, async |request: NewSessionRequest, responder, cx| {
        let mcp = McpServer::<Conductor, _>::builder("tools").build();
        cx.build_session_from(request)
            .with_mcp_server(mcp)?
            .on_proxy_session_start(responder, async |session_id| {
                // Session started
                Ok(())
            })
    }, agent_client_protocol::on_receive_request!())
    .connect_to(transport)
    .await?;
§Ordering

The client response is queued, proxy routing is installed, and session runners are scheduled before the dispatch loop processes the next message when the session response is routed during its original dispatch. This is a local ordering guarantee, not a guarantee that the response reaches the client before later wire traffic. No user callback code runs under the barrier: the callback is invoked in a spawned task, so it may wait for later connection traffic. A response interceptor that retains the response and routes it later cannot retroactively order this setup before messages the loop already processed.

Source§

impl<Counterpart, R> SessionBuilder<Counterpart, R, NonBlocking>
where Counterpart: HasPeer<Agent>, R: RunWithConnectionTo<Counterpart>,

Source

pub fn block_task(self) -> SessionBuilder<Counterpart, R, Blocking>

Mark this session builder as being able to block the current task.

After calling this, you can use run_until or start_session which block the current task.

This should not be used from inside a message handler like Builder::on_receive_request or HandleDispatchFrom implementations.

Source§

impl<Counterpart, R> SessionBuilder<Counterpart, R, Blocking>
where Counterpart: HasPeer<Agent>, R: RunWithConnectionTo<Counterpart>,

Source

pub async fn run_until<T>( self, op: impl for<'runner> AsyncFnOnce(ActiveSession<'runner, Counterpart>) -> Result<T, Error>, ) -> Result<T, Error>

Run this session synchronously. The current task will be blocked and op will be executed with the active session information. This is useful when you have MCP servers that are borrowed from your local stack frame.

The ActiveSession passed to op has a non-'static lifetime, which prevents calling ActiveSession::proxy_remaining_messages (since the session’s background runners would terminate when op returns).

Requires calling block_task first.

Source

pub async fn start_session( self, ) -> Result<ActiveSession<'static, Counterpart>, Error>
where R: 'static,

Send the request to create the session and return a handle. This is an alternative to Self::run_until that avoids rightward drift but at the cost of requiring MCP servers that are Send and don’t access data from the surrounding scope.

Returns an ActiveSession<'static, _> because the session’s runners are spawned into background tasks that live for the connection lifetime.

Requires calling block_task first.

Source

pub async fn start_session_proxy( self, responder: Responder<NewSessionResponse>, ) -> Result<SessionId, Error>
where Counterpart: HasPeer<Client>, R: 'static,

Start a proxy session that forwards all messages between client and agent.

A proxy session starts the session with the agent and then automatically proxies all session updates (prompts, tool calls, etc.) from the agent back to the client. You don’t need to handle any messages yourself - the proxy takes care of forwarding everything. This is useful when you want to inject and/or filter prompts coming from the client but otherwise not be involved in the session.

This is a convenience method that combines start_session, responding to the client, and ActiveSession::proxy_remaining_messages.

For more control (e.g., to send some messages before proxying), use start_session instead and call proxy_remaining_messages manually.

Requires calling block_task first.

Trait Implementations§

Source§

impl<Counterpart, Run: Debug + RunWithConnectionTo<Counterpart>, BlockState: Debug + SessionBlockState> Debug for SessionBuilder<Counterpart, Run, BlockState>
where Counterpart: HasPeer<Agent> + Debug,

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<Counterpart, Run = NullRun, BlockState = NonBlocking> !RefUnwindSafe for SessionBuilder<Counterpart, Run, BlockState>

§

impl<Counterpart, Run = NullRun, BlockState = NonBlocking> !UnwindSafe for SessionBuilder<Counterpart, Run, BlockState>

§

impl<Counterpart, Run, BlockState> Freeze for SessionBuilder<Counterpart, Run, BlockState>
where Run: Freeze, Counterpart: Freeze,

§

impl<Counterpart, Run, BlockState> Send for SessionBuilder<Counterpart, Run, BlockState>

§

impl<Counterpart, Run, BlockState> Sync for SessionBuilder<Counterpart, Run, BlockState>
where Run: Sync,

§

impl<Counterpart, Run, BlockState> Unpin for SessionBuilder<Counterpart, Run, BlockState>
where Run: Unpin, Counterpart: Unpin, BlockState: Unpin,

§

impl<Counterpart, Run, BlockState> UnsafeUnpin for SessionBuilder<Counterpart, Run, BlockState>
where Run: UnsafeUnpin, Counterpart: UnsafeUnpin,

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.

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

Source§

fn into_maybe_undefined(self) -> MaybeUndefined<T>

Converts this value into a three-state builder argument.
Source§

impl<T> IntoOption<T> for T

Source§

fn into_option(self) -> Option<T>

Converts this value into an optional builder argument.
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<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