|
| 1 | +# Experimental Contrib Modules |
| 2 | + |
| 3 | +> The helpers under `acp.contrib` capture patterns we observed in reference integrations such as Toad and kimi-cli. Every API here is experimental and may change without notice. |
| 4 | +
|
| 5 | +## SessionAccumulator |
| 6 | + |
| 7 | +Module: `acp.contrib.session_state` |
| 8 | + |
| 9 | +UI surfaces like Toad need a live, merged view of the latest tool calls, plan entries, and message stream. The core SDK only emits raw `SessionNotification` payloads, so applications usually end up writing their own state layer. `SessionAccumulator` offers that cache out of the box. |
| 10 | + |
| 11 | +Capabilities: |
| 12 | + |
| 13 | +- `SessionAccumulator.apply(notification)` merges `tool_call` and `tool_call_update` events, backfilling a missing start message when necessary. |
| 14 | +- Each call to `snapshot()` returns an immutable `SessionSnapshot` (Pydantic model) containing the active plan, current mode ID, available commands, and historical user/agent/thought chunks. |
| 15 | +- `subscribe(callback)` wires a lightweight observer that receives every new snapshot, making it easy to refresh UI widgets. |
| 16 | +- Automatic reset when a different session ID arrives (configurable via `auto_reset_on_session_change`). |
| 17 | + |
| 18 | +> Integration tip: create one accumulator per UI controller. Feed every `SessionNotification` through it, then render from `snapshot.tool_calls` or `snapshot.user_messages` instead of mutating state manually. |
| 19 | +
|
| 20 | +## ToolCallTracker & PermissionBroker |
| 21 | + |
| 22 | +Modules: `acp.contrib.tool_calls` and `acp.contrib.permissions` |
| 23 | + |
| 24 | +Agent-side runtimes (for example kimi-cli) are responsible for synthesising tool call IDs, streaming argument fragments, and formatting permission prompts. Managing bare Pydantic models quickly devolves into boilerplate; these helpers centralise the bookkeeping. |
| 25 | + |
| 26 | +- `ToolCallTracker.start()/progress()/append_stream_text()` manages tool call state and emits canonical `ToolCallStart` / `ToolCallProgress` messages. The tracker also exposes `view()` (immutable `TrackedToolCallView`) and `tool_call_model()` for logging or permission prompts. |
| 27 | +- `PermissionBroker.request_for()` wraps `requestPermission` RPCs. It reuses the tracker’s state (or an explicit `ToolCall`), applies optional extra content, and defaults to a standard Approve / Approve for session / Reject option set. |
| 28 | +- `default_permission_options()` exposes that canonical option triple so applications can customise or extend it. |
| 29 | + |
| 30 | +> Integration tip: keep a single tracker alongside your agent loop. Emit tool call notifications through it, and hand the tracker to `PermissionBroker` so permission prompts stay in sync with the latest call state. |
| 31 | +
|
| 32 | +## Design Guardrails |
| 33 | + |
| 34 | +To stay aligned with the ACP schema, the contrib layer follows a few rules: |
| 35 | + |
| 36 | +- Protocol types continue to live in `acp.schema`. Contrib code always copies them via `.model_copy(deep=True)` to avoid mutating shared instances. |
| 37 | +- Helpers are opt-in; the core package never imports them implicitly and imposes no UI or agent framework assumptions. |
| 38 | +- Implementations focus on the common pain points (tool call aggregation, permission requests) while leaving business-specific policy to the application. |
| 39 | + |
| 40 | +Try the contrib modules in your agent or client, and open an issue/PR with feedback so we can decide which pieces should graduate into the stable surface. |
0 commit comments