Skip to content

Commit 155846b

Browse files
committed
docs: improve sacp crate documentation for newcomers
- Reframe docs around client/proxy/agent use cases (client most common) - Replace UntypedLink quick start with ClientToAgent using session builder - Add build_session_cwd() convenience method for current working directory - Add running_proxies_with_conductor cookbook section explaining conductor requirement - Add building_an_agent cookbook section with initialization, sessions, prompts - Update connecting_as_client to use session builder pattern - Simplify peers section to focus on ClientPeer/AgentPeer (conductor/proxy are internal) - Update README.md to match new structure
1 parent 55a288c commit 155846b

File tree

5 files changed

+662
-199
lines changed

5 files changed

+662
-199
lines changed

commit-messages.txt

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Improved commit messages for e8bbda18b3e2b9484a15dfab0cc4a7ffa30d4710..HEAD
2+
# Use: git rebase -i e8bbda18b3e2b9484a15dfab0cc4a7ffa30d4710
3+
# Then for each commit, copy the corresponding message below.
4+
5+
================================================================================
6+
COMMIT 1: bae4a8f - refactor!: rename roles to FooRole
7+
================================================================================
8+
9+
refactor!: rename link endpoint types from Foo to FooRole
10+
11+
Rename the endpoint marker types to clarify they represent roles in a connection:
12+
- Client -> ClientRole
13+
- Agent -> AgentRole
14+
- Conductor -> ConductorRole
15+
- Untyped -> UntypedRole
16+
17+
This is preparation for separating the concepts of "role" (who I am) from
18+
"peer" (who I'm talking to) and "link" (the relationship between two sides).
19+
20+
BREAKING CHANGE: All references to Client, Agent, Conductor, Untyped as
21+
peer types must be updated to ClientRole, AgentRole, etc.
22+
23+
================================================================================
24+
COMMIT 2: f9b9a5a - refactor!: remove local role
25+
================================================================================
26+
27+
refactor!: remove LocalRole from JrLink trait
28+
29+
The LocalRole associated type on JrLink was redundant - it captured "who am I"
30+
but that information is already implicit in the link type itself (e.g.,
31+
ClientToAgent implies the local side is a client).
32+
33+
This simplifies the trait and removes unnecessary type parameters from
34+
connection handling code.
35+
36+
BREAKING CHANGE: JrLink no longer has a LocalRole associated type.
37+
38+
================================================================================
39+
COMMIT 3: 4de0fec - refactor!: move RemotePeer to DefaultPeer
40+
================================================================================
41+
42+
refactor!: replace RemotePeer with HasDefaultPeer::DefaultPeer
43+
44+
Move the "default peer" concept from JrLink::RemotePeer to a separate
45+
HasDefaultPeer trait with DefaultPeer associated type.
46+
47+
This separation is important because:
48+
- Not all links have a single default peer (e.g., ProxyToConductor can
49+
send to both ClientPeer and AgentPeer)
50+
- Links that DO have a default peer now explicitly implement HasDefaultPeer
51+
- The HasPeer<P> trait declares which peers a link can communicate with
52+
53+
BREAKING CHANGE: JrLink::RemotePeer removed; implement HasDefaultPeer instead.
54+
55+
================================================================================
56+
COMMIT 4: f3815bc - refactor!: rename Role to Peer
57+
================================================================================
58+
59+
refactor!: rename FooRole types to FooPeer
60+
61+
Complete the terminology shift from "role" to "peer":
62+
- ClientRole -> ClientPeer
63+
- AgentRole -> AgentPeer
64+
- ConductorRole -> ConductorPeer
65+
- UntypedRole -> UntypedPeer
66+
- JrRole trait -> JrPeer trait
67+
68+
"Peer" better captures what these types represent: the logical destination
69+
for messages. A peer is "who you're talking to", not "who you are".
70+
71+
BREAKING CHANGE: All FooRole types renamed to FooPeer.
72+
73+
================================================================================
74+
COMMIT 5: 24ec6c4 - refactor!: s/role/peer
75+
================================================================================
76+
77+
refactor!: update module and documentation references from role to peer
78+
79+
Rename the role module to peer module and update all references:
80+
- sacp::role:: -> sacp::peer::
81+
- role.rs -> peer.rs
82+
- Doc comments updated to use "peer" terminology
83+
84+
Also updates documentation to use clearer language:
85+
- "fulfill their role" -> "fulfill their responsibilities"
86+
- "role-agnostic" -> "link-agnostic"
87+
88+
BREAKING CHANGE: sacp::role module renamed to sacp::peer.
89+
90+
================================================================================
91+
COMMIT 6: 1689112 - refactor!: split peer/link modules
92+
================================================================================
93+
94+
refactor!: split peer.rs into separate peer and link modules
95+
96+
The peer module was conflating two distinct concepts:
97+
- Peers: logical message destinations (ClientPeer, AgentPeer, etc.)
98+
- Links: directional connection types (ClientToAgent, AgentToClient, etc.)
99+
100+
Now split into:
101+
- peer.rs: JrPeer trait and peer types (ClientPeer, AgentPeer, ConductorPeer)
102+
- link.rs: JrLink trait, HasDefaultPeer, HasPeer, RemoteStyle, and all
103+
link types (ClientToAgent, AgentToClient, ProxyToConductor, etc.)
104+
105+
This clarifies the mental model:
106+
- Links capture the *relationship* between two sides
107+
- Peers identify *who* you're talking to
108+
109+
BREAKING CHANGE: Link types moved from sacp::peer to sacp::link.
110+
111+
================================================================================
112+
COMMIT 7: 48390ea - refactor!: more comments, introduce ProxyPeer
113+
================================================================================
114+
115+
refactor!: add ProxyPeer and improve link documentation
116+
117+
Introduce ProxyPeer as a distinct peer type for proxy connections, used by
118+
ConductorToProxy. Previously this incorrectly used AgentPeer as the default.
119+
120+
Also adds extensive documentation to link types explaining:
121+
- When each link type should be used
122+
- Which link types are conductor-internal vs. general purpose
123+
- The relationship between ConductorToProxy and ClientToAgent
124+
- How SuccessorMessage routing works
125+
126+
Key documentation additions:
127+
- ClientToAgent: "Use this when attempting to issue prompts or requests"
128+
- AgentToClient: "This is used when implementing agents"
129+
- ConductorTo* links: "Only meant to be used by the conductor itself"

src/sacp/README.md

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,66 @@
11
# sacp -- the Symposium Agent Client Protocol (ACP) SDK
22

3-
**sacp** is a Rust SDK for building agents and editors using the [Agent-Client Protocol (ACP)](https://agentclientprotocol.com/). It makes it easy to build ACP editors and clients -- or, indeed, any JSON-RPC-based application.
3+
**sacp** is a Rust SDK for building [Agent-Client Protocol (ACP)](https://agentclientprotocol.com/) applications.
4+
ACP is a protocol for communication between AI agents and their clients (IDEs, CLIs, etc.),
5+
enabling features like tool use, permission requests, and streaming responses.
46

5-
## Quick Start
7+
## What can you build with sacp?
68

7-
Building an ACP agent is straightforward with sacp's type-safe API:
9+
- **Clients** that talk to ACP agents (like building your own Claude Code interface)
10+
- **Proxies** that add capabilities to existing agents (like adding custom tools via MCP)
11+
- **Agents** that respond to prompts with AI-powered responses
12+
13+
## Quick Start: Connecting to an Agent
14+
15+
The most common use case is connecting to an existing ACP agent as a client:
816

917
```rust
10-
// Start by creating an agent talking on stdout/stdin
11-
JrConnection::new(
12-
tokio::io::stdout().compat_write(),
13-
tokio::io::stdin().compat(),
14-
)
15-
.name("my-agent") // Give it a name for logging purposes
16-
.on_receive_request(async move |initialize: InitializeRequest, request_cx| {
17-
// Create one or more request handlers -- these are attempted in order.
18-
// You can do anything you want in here, but you should eventually
19-
// respond to the request with `request_cx.respond(...)`:
20-
request_cx.respond(InitializeResponse {
21-
protocol_version: initialize.protocol_version,
22-
agent_capabilities: AgentCapabilities::default(),
23-
auth_methods: Default::default(),
24-
agent_info: Default::default(),
25-
meta: Default::default(),
18+
use sacp::ClientToAgent;
19+
use sacp::schema::{InitializeRequest, VERSION as PROTOCOL_VERSION};
20+
21+
ClientToAgent::builder()
22+
.name("my-client")
23+
.run_until(transport, async |cx| {
24+
// Initialize the connection
25+
cx.send_request(InitializeRequest {
26+
protocol_version: PROTOCOL_VERSION,
27+
client_capabilities: Default::default(),
28+
client_info: Default::default(),
29+
meta: None,
30+
}).block_task().await?;
31+
32+
// Create a session and send a prompt
33+
cx.build_session_cwd()?
34+
.block_task()
35+
.run_until(async |mut session| {
36+
session.send_prompt("What is 2 + 2?")?;
37+
let response = session.read_to_string().await?;
38+
println!("{}", response);
39+
Ok(())
40+
})
41+
.await
2642
})
27-
})
28-
.on_receive_message(async move |message: MessageAndCx<UntypedMessage, UntypedMessage>| {
29-
// You can also handle any kind of message:
30-
message.respond_with_error(sacp::util::internal_error("TODO"))
31-
})
32-
.serve() // Finally, start the server (or use `run_until`)
33-
.await
43+
.await
3444
```
3545

36-
## Learning more
46+
## Learning More
47+
48+
See the [crate documentation](https://docs.rs/sacp) for:
3749

38-
You can learn more in the [docs for `JrConnection`](https://docs.rs/symposium-acp/latest/symposium_acp/jr_connection/struct.JrConnection.html) or on our [Github Pages](https://github.com/symposium-acp/symposium-acp) site.
50+
- **[Cookbook](https://docs.rs/sacp/latest/sacp/cookbook/)** - Patterns for building clients, proxies, and agents
51+
- **[Examples](https://github.com/symposium-dev/symposium-acp/tree/main/src/sacp/examples)** - Working code you can run
3952

40-
You may also enjoy looking at some of these examples:
53+
You may also enjoy looking at:
4154

42-
- **[`simple_agent.rs`](examples/simple_agent.rs)** - Minimal agent implementation
4355
- **[`yolo_one_shot_client.rs`](examples/yolo_one_shot_client.rs)** - Complete client that spawns an agent and sends a prompt
44-
- **[`elizacp`](https://crates.io/crates/elizacp)** - Full working agent with session management (also useful for testing)
45-
- **[`sacp-conductor`](https://crates.io/crates/sacp-conductor)** - The "conductor" is an ACP agent that composes [proxies](https://crates.io/crates/sacp-conductor) components with a final agent.
56+
- **[`elizacp`](https://crates.io/crates/elizacp)** - Full working agent with session management
57+
- **[`sacp-conductor`](https://crates.io/crates/sacp-conductor)** - Orchestrates proxy chains with a final agent
4658

4759
## Related Crates
4860

49-
- **[sacp-proxy](../sacp-proxy/)** - Framework for building ACP proxies that extend agent behavior
50-
- **[sacp-tokio](../sacp-tokio/)** - Tokio-specific utilities (process spawning, connection management)
51-
- **[sacp-conductor](../sacp-conductor/)** - Binary for orchestrating proxy chains
61+
- **[sacp-tokio](../sacp-tokio/)** - Tokio utilities for spawning agent processes
62+
- **[sacp-proxy](../sacp-proxy/)** - Framework for building proxy components
63+
- **[sacp-conductor](../sacp-conductor/)** - Binary for running proxy chains
5264

5365
## License
5466

0 commit comments

Comments
 (0)