agent_client_protocol/concepts/callbacks.rs
1//! Handling incoming messages with `on_receive_*` callbacks.
2//!
3//! So far we've seen how to *send* messages. But ACP is bidirectional - the
4//! remote peer can also send messages to you. Use callbacks to handle them.
5//!
6//! # Handling Requests
7//!
8//! Use `on_receive_request` to handle incoming requests that expect a response:
9//!
10//! ```
11//! # use agent_client_protocol::{Client, Agent, ConnectTo};
12//! # use agent_client_protocol_test::{ValidateRequest, ValidateResponse};
13//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
14//! Client.builder()
15//! .on_receive_request(async |req: ValidateRequest, responder, cx| {
16//! // Process the request
17//! let is_valid = req.data.len() > 0;
18//!
19//! // Send the response
20//! responder.respond(ValidateResponse { is_valid, error: None })
21//! }, agent_client_protocol::on_receive_request!())
22//! .connect_with(transport, async |cx| { Ok(()) })
23//! .await?;
24//! # Ok(())
25//! # }
26//! ```
27//!
28//! Your callback receives three arguments:
29//! - The request payload (e.g., `PermissionRequest`)
30//! - A [`Responder`] for sending the response
31//! - A [`ConnectionTo`] for sending other messages
32//!
33//! # Handling Notifications
34//!
35//! Use `on_receive_notification` for fire-and-forget messages that don't need
36//! a response:
37//!
38//! ```
39//! # use agent_client_protocol::{Client, Agent, ConnectTo};
40//! # use agent_client_protocol_test::StatusUpdate;
41//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
42//! Client.builder()
43//! .on_receive_notification(async |notif: StatusUpdate, cx| {
44//! println!("Status: {}", notif.message);
45//! Ok(())
46//! }, agent_client_protocol::on_receive_notification!())
47//! # .connect_with(transport, async |_| Ok(())).await?;
48//! # Ok(())
49//! # }
50//! ```
51//!
52//! # The Request Context
53//!
54//! The [`Responder`] lets you send a response to the request:
55//!
56//! ```
57//! # use agent_client_protocol::{Client, Agent, ConnectTo};
58//! # use agent_client_protocol_test::{MyRequest, MyResponse};
59//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
60//! # Client.builder()
61//! # .on_receive_request(async |req: MyRequest, responder, cx| {
62//! // Send a successful response
63//! responder.respond(MyResponse { status: "ok".into() })?;
64//! # Ok(())
65//! # }, agent_client_protocol::on_receive_request!())
66//! # .connect_with(transport, async |_| Ok(())).await?;
67//! # Ok(())
68//! # }
69//! ```
70//!
71//! Or send an error:
72//!
73//! ```
74//! # use agent_client_protocol::{Client, Agent, ConnectTo};
75//! # use agent_client_protocol_test::{MyRequest, MyResponse};
76//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
77//! # Client.builder()
78//! # .on_receive_request(async |req: MyRequest, responder, cx| {
79//! responder.respond_with_error(agent_client_protocol::Error::invalid_params())?;
80//! # Ok(())
81//! # }, agent_client_protocol::on_receive_request!())
82//! # .connect_with(transport, async |_| Ok(())).await?;
83//! # Ok(())
84//! # }
85//! ```
86//!
87//! You must send exactly one response per request. If your callback returns
88//! without responding, an error response is sent automatically.
89//!
90//! # Multiple Handlers
91//!
92//! You can register multiple handlers. They're tried in order until one
93//! handles the message:
94//!
95//! ```
96//! # use agent_client_protocol::{Client, Agent, ConnectTo};
97//! # use agent_client_protocol_test::{ValidateRequest, ValidateResponse, ExecuteRequest, ExecuteResponse};
98//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
99//! Client.builder()
100//! .on_receive_request(async |req: ValidateRequest, responder, cx| {
101//! // Handle validation requests
102//! responder.respond(ValidateResponse { is_valid: true, error: None })
103//! }, agent_client_protocol::on_receive_request!())
104//! .on_receive_request(async |req: ExecuteRequest, responder, cx| {
105//! // Handle execution requests
106//! responder.respond(ExecuteResponse { result: "done".into() })
107//! }, agent_client_protocol::on_receive_request!())
108//! # .connect_with(transport, async |_| Ok(())).await?;
109//! # Ok(())
110//! # }
111//! ```
112//!
113//! # Ordering Guarantees
114//!
115//! Callbacks run inside the dispatch loop and block further message processing
116//! until they complete. This gives you ordering guarantees but also means you
117//! need to be careful about deadlocks.
118//!
119//! See [Ordering](super::ordering) for the full details.
120//!
121//! # Next Steps
122//!
123//! - [Explicit Peers](super::peers) - Use `_from` variants to specify the source peer
124//! - [Ordering](super::ordering) - Understand dispatch loop semantics
125//!
126//! [`Responder`]: crate::Responder
127//! [`ConnectionTo`]: crate::ConnectionTo