1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Establishing connections using role types and connection builders.
//!
//! To communicate over ACP, you need to establish a connection. This involves
//! choosing a **role type** that matches your role and using a **connection builder**
//! to configure and run the connection.
//!
//! # Choosing a Role Type
//!
//! Your role type determines what messages you can send and who you can send them to.
//! Choose based on what you're building:
//!
//! | You are building... | Use this role type |
//! |---------------------|-------------------|
//! | A client that talks to an agent | [`Client`] |
//! | An agent that responds to clients | [`Agent`] |
//! | A proxy in a conductor chain | [`Proxy`] |
//!
//! # The Connection Builder Pattern
//!
//! Every role type has a `builder()` method that returns a connection builder.
//! The builder lets you configure handlers, then connect to a transport:
//!
//! ```
//! # use agent_client_protocol::{Client, Agent, ConnectTo};
//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
//! Client.builder()
//! .name("my-client")
//! .connect_with(transport, async |cx| {
//! // Use `cx` to send requests and handle responses
//! Ok(())
//! })
//! .await?;
//! # Ok(())
//! # }
//! ```
//!
//! # The Connection Context
//!
//! Inside `connect_with`, you receive a [`ConnectionTo`] (connection context) that
//! lets you interact with the remote peer:
//!
//! ```
//! # use agent_client_protocol::{Client, Agent, ConnectTo};
//! # use agent_client_protocol::schema::{ProtocolVersion, v1::InitializeRequest};
//! # use agent_client_protocol_test::StatusUpdate;
//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
//! # Client.builder().connect_with(transport, async |cx| {
//! // Send a request and wait for the response
//! let response = cx.send_request(InitializeRequest::new(ProtocolVersion::V1))
//! .block_task()
//! .await?;
//!
//! // Send a notification (fire-and-forget)
//! cx.send_notification(StatusUpdate { message: "hello".into() })?;
//! # Ok(())
//! # }).await?;
//! # Ok(())
//! # }
//! ```
//!
//! # Sending Requests
//!
//! When you call `send_request()`, you get back a [`SentRequest`] that represents
//! the pending response. You have two main ways to handle it:
//!
//! ## Option 1: Block and wait
//!
//! Use `block_task()` when you need the response before continuing:
//!
//! ```
//! # use agent_client_protocol::{Client, Agent, ConnectTo};
//! # use agent_client_protocol_test::MyRequest;
//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
//! # Client.builder().connect_with(transport, async |cx| {
//! let response = cx.send_request(MyRequest {})
//! .block_task()
//! .await?;
//! // Use response here
//! # Ok(())
//! # }).await?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Option 2: Schedule a callback
//!
//! Use `on_receiving_result()` when you want to handle the response asynchronously:
//!
//! ```
//! # use agent_client_protocol::{Client, Agent, ConnectTo};
//! # use agent_client_protocol_test::MyRequest;
//! # async fn example(transport: impl ConnectTo<Client>) -> Result<(), agent_client_protocol::Error> {
//! # Client.builder().connect_with(transport, async |cx| {
//! cx.send_request(MyRequest {})
//! .on_receiving_result(async |result| {
//! match result {
//! Ok(response) => { /* handle success */ }
//! Err(error) => { /* handle error */ }
//! }
//! Ok(())
//! })?;
//! // Continues immediately, callback runs when response arrives
//! # Ok(())
//! # }).await?;
//! # Ok(())
//! # }
//! ```
//!
//! See [Ordering](super::ordering) for important details about how these differ.
//!
//! ## Dropping a `SentRequest`
//!
//! By default, dropping a [`SentRequest`] without consuming it simply discards
//! the response when it arrives. When the `unstable_cancel_request` feature is
//! enabled, dropping an unconsumed [`SentRequest`] additionally sends a
//! `$/cancel_request` notification asking the peer to cancel the request. For a
//! request whose eventual response should be ignored, but which should keep
//! running on the peer, call [`SentRequest::detach`] instead. If no response is
//! expected at all, use a notification. See the request cancellation chapter
//! (`concepts::cancellation`, feature-gated) for details.
//!
//! # Next Steps
//!
//! - [Sessions](super::sessions) - Create multi-turn conversations
//! - [Callbacks](super::callbacks) - Handle incoming requests from the remote peer
//!
//! [`Client`]: crate::Client
//! [`Agent`]: crate::Agent
//! [`Proxy`]: crate::Proxy
//! [`ConnectionTo`]: crate::ConnectionTo
//! [`SentRequest`]: crate::SentRequest
//! [`SentRequest::detach`]: crate::SentRequest::detach