From efe92120c74729a8b0775f57bd844d199c508035 Mon Sep 17 00:00:00 2001 From: Guilherme Beira Date: Mon, 15 Jun 2026 10:04:44 -0300 Subject: [PATCH] refactor(acp): rename crate iii-acp -> acp --- acp/Cargo.lock | 32 +++++++++++++++--------------- acp/Cargo.toml | 6 +++--- acp/README.md | 46 +++++++++++++++++++++---------------------- acp/iii.worker.yaml | 2 +- acp/src/handler.rs | 12 +++++------ acp/src/main.rs | 14 ++++++------- acp/src/session.rs | 2 +- acp/tests/protocol.rs | 20 +++++++++---------- 8 files changed, 67 insertions(+), 67 deletions(-) diff --git a/acp/Cargo.lock b/acp/Cargo.lock index 1e7bdf44..414b5408 100644 --- a/acp/Cargo.lock +++ b/acp/Cargo.lock @@ -2,6 +2,22 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "acp" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "dashmap", + "iii-sdk", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "uuid", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -730,22 +746,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "iii-acp" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "dashmap", - "iii-sdk", - "serde", - "serde_json", - "tokio", - "tracing", - "tracing-subscriber", - "uuid", -] - [[package]] name = "iii-observability" version = "0.16.0-next.2" diff --git a/acp/Cargo.toml b/acp/Cargo.toml index a426c9b8..b3972392 100644 --- a/acp/Cargo.toml +++ b/acp/Cargo.toml @@ -1,7 +1,7 @@ [workspace] [package] -name = "iii-acp" +name = "acp" version = "0.1.0" edition = "2024" description = "Agent Client Protocol worker for iii-engine" @@ -15,11 +15,11 @@ categories = ["command-line-utilities"] publish = false [lib] -name = "iii_acp" +name = "acp" path = "src/lib.rs" [[bin]] -name = "iii-acp" +name = "acp" path = "src/main.rs" [dependencies] diff --git a/acp/README.md b/acp/README.md index dbc0128f..0b1ffeaa 100644 --- a/acp/README.md +++ b/acp/README.md @@ -2,9 +2,9 @@ **iii as a first-class agent in any ACP-speaking editor.** -`iii-acp` is a stdio JSON-RPC adapter that exposes the iii engine — and every +`acp` is a stdio JSON-RPC adapter that exposes the iii engine — and every brain worker on it — through the [Agent Client Protocol](https://agentclientprotocol.com). -Editors and clients that already speak ACP launch `iii-acp` as a subprocess +Editors and clients that already speak ACP launch `acp` as a subprocess and drive it through their native agent UI. No editor plugin, no fork, no bespoke per-client integration. @@ -27,14 +27,14 @@ others don't. | **MCP server** (`iii-mcp`) | Exposes iii functions as **tools** to an external agent | You're already running Claude Code / Cursor / etc. and want to give it iii tools | | **Skill bundles** | Curated prompts + tools loaded into an agent host | You're inside a skill-aware host (Claude Code, Cursor) and want a preset toolset | | **Agent workers** (`turn-orchestrator`, `agent`, `coding`, …) | The brain itself — registers `run::start_and_wait`, runs LLM turns, executes tools | You're calling iii from your own code (`iii.trigger("run::start_and_wait", …)`) or backend automation | -| **`iii-acp` (this worker)** | Editor → iii. Translates ACP `session/*` JSON-RPC into the canonical iii brain contract; turns iii's `agent::events` stream into ACP `session/update` notifications | You want iii to **be** the agent in an editor users already opened today | +| **`acp` (this worker)** | Editor → iii. Translates ACP `session/*` JSON-RPC into the canonical iii brain contract; turns iii's `agent::events` stream into ACP `session/update` notifications | You want iii to **be** the agent in an editor users already opened today | ACP is the **north** edge of the stack. MCP is the **south** edge. They coexist: ``` Editor (Zed, VS Code, Neovim, …) - ↓ ACP ─ session/prompt, session/update ← iii-acp + ↓ ACP ─ session/prompt, session/update ← acp iii engine + brain workers (turn-orchestrator, provider-router, guardrails, llm-budget, audit-log, dlp-scrubber, policy-denylist, …) @@ -54,13 +54,13 @@ What iii brings to an editor session that a vanilla agent host doesn't: ## Supported clients -ACP is an open spec. Any client that speaks it works with `iii-acp`. As of +ACP is an open spec. Any client that speaks it works with `acp`. As of this writing the public client list ([agentclientprotocol.com/get-started/clients](https://agentclientprotocol.com/get-started/clients)) includes: **Editors / IDEs** -| Client | How to wire iii-acp | +| Client | How to wire acp | |---|---| | [Zed](https://zed.dev) | `agent_servers` block in `~/.config/zed/settings.json` (snippet below) | | Visual Studio Code | ACP Client extension | @@ -80,15 +80,15 @@ includes: (via `sidequery/duckdb-acp`), `marimo`, `Agmente` (iOS), `Ferngeist` (Android), `Happy`, `Mobvibe` (mobile), `OpenACP` (Telegram/Discord/Slack), and others. -Setup pattern is the same everywhere: point the client at the `iii-acp` +Setup pattern is the same everywhere: point the client at the `acp` binary, set the `IIIACP_*` env vars below. ## Prerequisites -`iii-acp` needs an iii engine plus a brain. Minimum stack: +`acp` needs an iii engine plus a brain. Minimum stack: ```bash -# 1. Engine builtins iii-acp uses directly. iii-state holds session +# 1. Engine builtins acp uses directly. iii-state holds session # records + history; iii-stream carries the agent::events tape; # iii-queue backs durable cancel topics. iii worker add iii-state iii-stream iii-queue @@ -132,12 +132,12 @@ the stack is healthy. ## Spawn -`iii-acp` is a stdio agent. The client launches it as a subprocess and +`acp` is a stdio agent. The client launches it as a subprocess and exchanges JSON-RPC frames over stdin/stdout. **stderr is reserved for logs; stdout is reserved for ACP frames.** ```bash -iii-acp --use-canonical-brain --model claude-sonnet-4-5-20250929 --provider anthropic +acp --use-canonical-brain --model claude-sonnet-4-5-20250929 --provider anthropic ``` ## Configuration @@ -162,9 +162,9 @@ iii-acp --use-canonical-brain --model claude-sonnet-4-5-20250929 --provider anth ```jsonc { "agent_servers": { - "iii-acp": { + "acp": { "type": "custom", - "command": "/path/to/iii-acp", + "command": "/path/to/acp", "args": [], "env": { "IIIACP_ENGINE_URL": "ws://localhost:49134", @@ -178,11 +178,11 @@ iii-acp --use-canonical-brain --model claude-sonnet-4-5-20250929 --provider anth } ``` -Restart Zed → Agent panel → `+` → pick **iii-acp** → type a prompt. +Restart Zed → Agent panel → `+` → pick **acp** → type a prompt. ### VS Code -Install the ACP Client extension. Add `iii-acp` as a custom agent in the +Install the ACP Client extension. Add `acp` as a custom agent in the extension's settings, pointing `command` at the binary and replicating the `env` block above. @@ -195,12 +195,12 @@ the same env vars work. ### JetBrains / Emacs / Obsidian / Unity / Chrome Same pattern: client config takes a command path and env map. Point at -`iii-acp` with the env vars above. The protocol is the same on all sides. +`acp` with the env vars above. The protocol is the same on all sides. ### CLIs (`acpx`, `Nori CLI`, …) Most CLI ACP clients accept `--agent ` or a config file. Point them -at `iii-acp` directly. +at `acp` directly. ## Methods @@ -232,11 +232,11 @@ include those slots in `SessionCapabilities` yet — clients that try them get a successful response; clients that don't try are unaffected. Both methods persist their values on the session record (`mode: Option` and `config_options: Map`). Brains opt in to honoring them -on the next `session/prompt` turn; iii-acp just stores. +on the next `session/prompt` turn; acp just stores. ## Brain contract -iii-acp talks to the canonical iii brain shape used by `turn-orchestrator` +acp talks to the canonical iii brain shape used by `turn-orchestrator` and every provider worker. Any function with this input contract drops in as a brain — no adapter required. @@ -269,12 +269,12 @@ as a brain — no adapter required. } ``` -iii-acp picks the ACP `stopReason` from the final assistant message's +acp picks the ACP `stopReason` from the final assistant message's `stop_reason` field (`end` → `end_turn`, `length` → `max_tokens`, `aborted` → `cancelled`, `error` → `refusal`). **Streaming.** The brain emits `AgentEvent` frames into `agent::events` -(group_id = session_id). iii-acp registers one stream subscriber per +(group_id = session_id). acp registers one stream subscriber per connection at startup and translates each event: | `AgentEvent` | ACP `sessionUpdate` | @@ -287,7 +287,7 @@ connection at startup and translates each event: | other | dropped | This is the same stream `context-compaction` and every provider worker -already subscribe to. **No bespoke iii-acp publish protocol.** +already subscribe to. **No bespoke acp publish protocol.** ## State layout @@ -307,7 +307,7 @@ Streaming wire: `agent::events` (per-session events), per-connection topic ```bash echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{},"clientInfo":{"name":"demo","version":"0"}}}' \ - | iii-acp --use-canonical-brain --model claude-sonnet-4-5-20250929 --provider anthropic + | acp --use-canonical-brain --model claude-sonnet-4-5-20250929 --provider anthropic ``` Replies stream on stdout, one JSON frame per line. diff --git a/acp/iii.worker.yaml b/acp/iii.worker.yaml index 1f01d922..db871e40 100644 --- a/acp/iii.worker.yaml +++ b/acp/iii.worker.yaml @@ -3,5 +3,5 @@ name: acp language: rust deploy: binary manifest: Cargo.toml -bin: iii-acp +bin: acp description: Agent Client Protocol surface — stdio JSON-RPC, exposes iii agents as ACP sessions diff --git a/acp/src/handler.rs b/acp/src/handler.rs index f930c8fc..59d7e663 100644 --- a/acp/src/handler.rs +++ b/acp/src/handler.rs @@ -20,7 +20,7 @@ use crate::types::{ // Canonical iii brain function. Any worker exposing this id with the // turn-orchestrator wire shape (session_id, messages, model, ...) can -// drive iii-acp without an adapter. +// drive acp without an adapter. pub const DEFAULT_BRAIN_FN: &str = "run::start_and_wait"; const BRAIN_TIMEOUT_MS: u64 = 600_000; @@ -72,7 +72,7 @@ pub struct AcpHandler { brain_system_prompt: Option, // Session ids owned by this connection. The agent::events stream // subscriber filters by this set so we don't forward events for - // sessions another iii-acp subprocess owns. Also written by + // sessions another acp subprocess owns. Also written by // session/new and session/close so close cleans up. owned_sessions: Arc>, // Trigger + function guards. Dropping them tears the registration @@ -215,7 +215,7 @@ impl AcpHandler { } }, "agentInfo": { - "name": "iii-acp", + "name": "acp", "title": "iii Agent", "version": env!("CARGO_PKG_VERSION") } @@ -322,7 +322,7 @@ impl AcpHandler { if self.brain_fn.is_some() && !self.event_subscriber_healthy { return Err(( INTERNAL_ERROR, - "iii-acp: agent::events stream subscriber failed to register at startup; \ + "acp: agent::events stream subscriber failed to register at startup; \ external brain updates would not reach the editor. Check engine logs and \ ensure `iii-stream` worker is active before retrying." .to_string(), @@ -663,9 +663,9 @@ async fn write_notification(outbound: &Outbound, seq: &AtomicU64, method: &str, // hold them; dropping either tears the registration down. // // The same stream is used by `turn-orchestrator`, every provider worker, -// `context-compaction`, etc. iii-acp filters frames by group_id (the +// `context-compaction`, etc. acp filters frames by group_id (the // session_id) against the per-process owned_sessions set so multiple -// iii-acp subprocesses don't fight over the same events. +// acp subprocesses don't fight over the same events. fn register_event_subscriber( iii: &III, conn_id: &str, diff --git a/acp/src/main.rs b/acp/src/main.rs index 79fe8ab7..6e3618c3 100644 --- a/acp/src/main.rs +++ b/acp/src/main.rs @@ -1,13 +1,13 @@ use std::sync::Arc; use clap::Parser; -use iii_acp::handler::{AcpHandler, BrainConfig, DEFAULT_BRAIN_FN}; -use iii_acp::transport; +use acp::handler::{AcpHandler, BrainConfig, DEFAULT_BRAIN_FN}; +use acp::transport; use iii_sdk::{InitOptions, register_worker}; use tracing_subscriber::{EnvFilter, fmt, prelude::*}; #[derive(Parser, Debug)] -#[command(name = "iii-acp")] +#[command(name = "acp")] #[command(version)] #[command(about = "Agent Client Protocol worker for iii-engine")] struct Args { @@ -37,7 +37,7 @@ struct Args { #[arg( long, env = "IIIACP_USE_CANONICAL_BRAIN", - help = "Shortcut for --brain-fn run::start_and_wait. Wires iii-acp \ + help = "Shortcut for --brain-fn run::start_and_wait. Wires acp \ straight to turn-orchestrator. Ignored if --brain-fn is \ already set." )] @@ -84,9 +84,9 @@ async fn main() -> anyhow::Result<()> { // Honor RUST_LOG when set (gives operators per-module control); fall // back to the --debug switch otherwise. let fallback = if args.debug { - "iii_acp=debug,iii_sdk=debug" + "acp=debug,iii_sdk=debug" } else { - "iii_acp=info,iii_sdk=warn" + "acp=info,iii_sdk=warn" }; let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(fallback)); @@ -95,7 +95,7 @@ async fn main() -> anyhow::Result<()> { .with(filter) .init(); - tracing::info!(version = env!("CARGO_PKG_VERSION"), "starting iii-acp"); + tracing::info!(version = env!("CARGO_PKG_VERSION"), "starting acp"); let mut init_opts = InitOptions::default(); if let Some(tag) = args.rbac_tag.as_ref() { diff --git a/acp/src/session.rs b/acp/src/session.rs index bd613bf9..4e29b259 100644 --- a/acp/src/session.rs +++ b/acp/src/session.rs @@ -28,7 +28,7 @@ pub fn session_history_key(session_id: &str) -> String { // Streaming wire = the iii ecosystem's `agent::events` stream. No // per-connection topic exists. Brains (turn-orchestrator and any // drop-in replacement) emit AgentEvent frames into that stream with -// group_id = session_id; iii-acp subscribes once and routes by group. +// group_id = session_id; acp subscribes once and routes by group. pub const AGENT_EVENTS_STREAM: &str = "agent::events"; pub fn cancel_topic(conn_id: &str, session_id: &str) -> String { diff --git a/acp/tests/protocol.rs b/acp/tests/protocol.rs index 2ffeb5cd..e9859b50 100644 --- a/acp/tests/protocol.rs +++ b/acp/tests/protocol.rs @@ -7,7 +7,7 @@ use serde_json::json; #[test] fn jsonrpc_response_success_serializes_with_required_fields() { - let r = iii_acp::types::JsonRpcResponse::success(Some(json!(1)), json!({ "ok": true })); + let r = acp::types::JsonRpcResponse::success(Some(json!(1)), json!({ "ok": true })); let v = serde_json::to_value(&r).unwrap(); assert_eq!(v["jsonrpc"], "2.0"); assert_eq!(v["id"], 1); @@ -17,7 +17,7 @@ fn jsonrpc_response_success_serializes_with_required_fields() { #[test] fn jsonrpc_response_error_omits_result() { - let r = iii_acp::types::JsonRpcResponse::error(Some(json!("abc")), -32601, "missing"); + let r = acp::types::JsonRpcResponse::error(Some(json!("abc")), -32601, "missing"); let v = serde_json::to_value(&r).unwrap(); assert_eq!(v["jsonrpc"], "2.0"); assert_eq!(v["id"], "abc"); @@ -29,7 +29,7 @@ fn jsonrpc_response_error_omits_result() { #[test] fn session_new_params_accepts_minimal() { let raw = json!({ "cwd": "/tmp" }); - let p: iii_acp::types::SessionNewParams = serde_json::from_value(raw).unwrap(); + let p: acp::types::SessionNewParams = serde_json::from_value(raw).unwrap(); assert_eq!(p.cwd, "/tmp"); assert!(p.mcp_servers.is_empty()); } @@ -42,7 +42,7 @@ fn session_new_params_passes_through_stdio_mcp_server() { { "name": "fs", "command": "/bin/foo", "args": ["--stdio"] } ] }); - let p: iii_acp::types::SessionNewParams = serde_json::from_value(raw).unwrap(); + let p: acp::types::SessionNewParams = serde_json::from_value(raw).unwrap(); assert_eq!(p.mcp_servers.len(), 1); assert_eq!(p.mcp_servers[0]["name"], "fs"); assert_eq!(p.mcp_servers[0]["command"], "/bin/foo"); @@ -56,7 +56,7 @@ fn session_new_params_passes_through_http_mcp_server() { { "type": "http", "name": "remote", "url": "https://example.com/mcp" } ] }); - let p: iii_acp::types::SessionNewParams = serde_json::from_value(raw).unwrap(); + let p: acp::types::SessionNewParams = serde_json::from_value(raw).unwrap(); assert_eq!(p.mcp_servers.len(), 1); assert_eq!(p.mcp_servers[0]["type"], "http"); assert_eq!(p.mcp_servers[0]["url"], "https://example.com/mcp"); @@ -68,14 +68,14 @@ fn session_prompt_params_round_trips() { "sessionId": "sess_abc", "prompt": [{ "type": "text", "text": "hi" }] }); - let p: iii_acp::types::SessionPromptParams = serde_json::from_value(raw).unwrap(); + let p: acp::types::SessionPromptParams = serde_json::from_value(raw).unwrap(); assert_eq!(p.session_id, "sess_abc"); assert_eq!(p.prompt.len(), 1); } #[test] fn parse_returns_error_on_missing_params() { - let r: Result = iii_acp::types::parse(None); + let r: Result = acp::types::parse(None); assert!(r.is_err()); } @@ -86,7 +86,7 @@ fn session_resume_params_round_trips() { "cwd": "/home/user/project", "mcpServers": [] }); - let p: iii_acp::types::SessionResumeParams = serde_json::from_value(raw).unwrap(); + let p: acp::types::SessionResumeParams = serde_json::from_value(raw).unwrap(); assert_eq!(p.session_id, "sess_abc"); assert_eq!(p.cwd, "/home/user/project"); assert!(p.mcp_servers.is_empty()); @@ -95,7 +95,7 @@ fn session_resume_params_round_trips() { #[test] fn session_set_mode_params_round_trips() { let raw = json!({ "sessionId": "sess_x", "modeId": "code" }); - let p: iii_acp::types::SessionSetModeParams = serde_json::from_value(raw).unwrap(); + let p: acp::types::SessionSetModeParams = serde_json::from_value(raw).unwrap(); assert_eq!(p.session_id, "sess_x"); assert_eq!(p.mode_id, "code"); } @@ -107,7 +107,7 @@ fn session_set_config_option_params_round_trips() { "configId": "thinking_level", "value": "high" }); - let p: iii_acp::types::SessionSetConfigOptionParams = serde_json::from_value(raw).unwrap(); + let p: acp::types::SessionSetConfigOptionParams = serde_json::from_value(raw).unwrap(); assert_eq!(p.session_id, "sess_x"); assert_eq!(p.config_id, "thinking_level"); assert_eq!(p.value, json!("high"));