Skip to main content

Module connection

Module connection 

Source
Expand description

ACP connection adapter over the agent-client-protocol crate.

The crate frames JSON-RPC over stdio itself (newline-delimited JSON) and drives a connection through a closure-based API: Client.builder()...connect_with(transport, async |cx| { ... }). That closure owns the connection for as long as it runs — once it returns, the whole connection (including its background dispatch loop) shuts down. That doesn’t fit the shape Tasks 4/5/7 need: a Connection handle with independent async methods (initialize, new_session, prompt) callable at any time from any task.

This module bridges the two shapes: Connection::spawn starts a background “driver” task that runs the crate’s connect_with closure as a command loop for the connection’s whole lifetime, and the public methods send commands into that loop over a channel and await the reply.

§Threading

agent-client-protocol builds its connection state on futures::channel::mpsc (not tokio) and requires spawned work to be Send + 'static (see the crate’s internal Task::new bound). That makes the whole stack executor-agnostic and Send, so the driver task runs on a plain tokio::spawn — no dedicated thread or LocalSet is required.

Connection::spawn takes generic tokio AsyncRead/AsyncWrite halves and adapts them to the futures::io traits the crate’s ByteStreams transport expects via tokio_util::compat.

§Notifications

session/update notifications are handled once, connection-wide, via a single on_receive_notification handler registered on the builder (the crate does not scope notification handlers per-session at this layer). Every notification the agent sends for the life of the connection is forwarded to the mpsc::UnboundedReceiver<SessionNotification> returned by Connection::spawn; callers that run multiple sessions on one connection must filter by SessionNotification::session_id themselves. The channel is unbounded (not bounded) because on_receive_notification runs inside the crate’s single dispatch loop — see the comment at the channel construction site in Connection::spawn for why a bounded channel would risk deadlocking every in-flight request.

Structs§

Connection
A handle to a live ACP connection driven by the agent-client-protocol crate.
PermissionRequest
AU‑4: an agent→client session/request_permission request routed out to the supervisor, carrying the [Responder] it must answer. The supervisor classifies the tool call (auto-allow reads / review file edits) and calls responder.respond(...) — possibly much later, after a diff verdict. The [Responder] is Send + 'static (its send_fn is a boxed FnOnce + Send), so answering off the connection’s dispatch loop is sound.
PermissionRequestPayload
Re-exported so the supervisor (AU‑4) can inspect the permission request + build the response without depending on agent-client-protocol directly. Request for user permission to execute a tool call.
SessionId
A lattice-local session identifier.
SessionNotification
Re-exported so callers (Task 6) can match on session/update payloads without depending on agent-client-protocol directly. Notification containing a session update from the agent.