Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions specs/GH7326/product.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Product Spec: Support for Agent Client Protocol (ACP)

**Issue:** [warpdotdev/warp#7326](https://github.com/warpdotdev/warp/issues/7326)
**Figma:** none provided

## Summary

Warp should support the Agent Client Protocol (ACP) as a client, allowing any
ACP-compatible coding agent (e.g. opencode, Kimi CLI, Mistral's Devstral, custom
agents) to run inside Warp's native agent UX — blocks, diff view, file tree, and
conversation history — without requiring a custom Warp integration per agent.

## Problem

Today, each third-party coding agent (Claude Code, Codex, Gemini CLI) requires a
dedicated harness in Warp. Adding a new agent means writing new integration code.
Meanwhile, ACP is an emerging open standard (backed by Zed, JetBrains, and a
growing ecosystem) that defines a standard JSON-RPC-over-stdio protocol for
agent-editor communication — analogous to how LSP standardized language server
integration. Without ACP support, Warp users cannot run the growing number of
ACP-native agents inside Warp's UI, and must fall back to raw TUI output.

## Goals

- Any ACP-compatible agent can be launched inside Warp and uses Warp's native
agent UI (conversation blocks, diff view, file tree, tool call rendering).
- Users can configure ACP agents the same way they configure existing harnesses
(via the settings UI and/or the Warp config file).
- Warp passes its MCP servers to the ACP agent session so existing tool context
is available.
- The ACP harness validates that the target agent CLI is installed before launch
and surfaces a helpful install link if not.

## Non-goals

- Warp as an ACP *server* (other IDEs using Warp's agent) — this is a follow-up.
- Supporting ACP agents that require remote/HTTP transport (stdio only in v1).
- Replacing existing dedicated harnesses (Claude Code, Codex, Gemini) — those
remain as-is for agents that benefit from custom integrations.

## User experience

### Configuring an ACP agent

1. User opens Settings → Agents → Third-party CLI agents.
2. A new "ACP agents" section lists any configured ACP agents alongside existing
harnesses.
3. User clicks "Add ACP agent" and enters:
- **Name** (e.g. "opencode", "Kimi")
- **Command** (e.g. `opencode`, `kimi`)
- **Args** (optional, e.g. `--model gpt-4o`)
4. Warp validates the command exists on PATH. If not, shows an error with a link
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] [SECURITY] The spec lets users configure arbitrary commands and args but does not define the execution trust boundary; require argv-based execution rather than shell interpolation, PATH/absolute-path resolution rules, and whether synced/shared config can cause another machine to execute a command.

to the agent's install docs (if known).
5. The configured agent appears in the harness selector dropdown in the agent
input footer.

### Launching an ACP agent session

1. User selects an ACP agent from the harness selector and sends a prompt.
2. Warp spawns the agent process over stdio and initiates the ACP handshake
(initialize → initialized).
3. The agent's responses render in Warp's native conversation UI:
- Text responses appear as agent message blocks.
- File edits surface in Warp's diff view for review.
- Tool calls (if passed via MCP) render as tool call blocks.
4. The session persists. Session resumption might be out of scope for v1.

### Edge cases

- **Agent not installed:** Warp shows an inline error with install instructions
before attempting to spawn the process.
- **Agent crashes mid-session:** Warp surfaces an error block and marks the
conversation as ended, matching behavior of existing harnesses.
- **ACP handshake fails:** Warp shows a descriptive error (e.g. "Agent did not
respond to initialize within 10 seconds") rather than hanging.
- **Agent sends unsupported capability:** Warp ignores unknown capability fields
gracefully (forward-compatible).
- **No MCP servers configured:** ACP session launches without MCP context;
no error is shown (same as existing harness behavior).

## Success criteria

1. An ACP agent configured with a valid command launches successfully and
produces a conversation block in Warp's UI.
2. A file edit proposed by the agent appears in Warp's diff view.
3. If the command is not on PATH, Warp shows an error before spawning.
4. If the ACP handshake times out, Warp surfaces a descriptive error.
5. Configured ACP agents appear in the harness selector alongside Claude Code,
Codex, and Gemini.
6. MCP servers configured in Warp are passed to the ACP agent session.
7. Conversation history for ACP sessions persists and is viewable.
Comment thread
nigelpurvis marked this conversation as resolved.
Outdated

## Open questions

1. How should Warp handle ACP agents that advertise capabilities Warp doesn't
yet render (e.g. multi-file context beyond diff view)?
2. Should there be a curated list of known ACP agents with pre-filled commands
and install URLs (similar to how Codex and Gemini are pre-configured)?

206 changes: 206 additions & 0 deletions specs/GH7326/tech.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
# Tech Spec: Support for Agent Client Protocol (ACP)

**Issue:** [warpdotdev/warp#7326](https://github.com/warpdotdev/warp/issues/7326)
**Product spec:** `specs/GH7326/product.md`

## Context

Warp currently supports third-party coding agents through a `ThirdPartyHarness`
trait defined in `app/src/ai/agent_sdk/driver/harness/mod.rs`. Each supported
agent (Claude Code, Gemini) implements this trait in its own file and is
registered as a variant of the `Harness` enum in `crates/warp_cli/src/agent.rs`
and `CLIAgent` enum in `app/src/terminal/cli_agent.rs`.

ACP (Agent Client Protocol) is a standard JSON-RPC-over-stdio protocol for
agent-editor communication. Rather than adding a new per-agent harness for every
ACP-compatible CLI, a single `AcpHarness` implementation covers all of them.
The user supplies the command (e.g. `opencode`, `kimi`) and Warp handles the
protocol layer generically.

### Relevant files

- `crates/warp_cli/src/agent.rs:123` — `pub enum Harness` — needs new `Acp` variant
- `app/src/terminal/cli_agent.rs:108` — `pub enum CLIAgent` — needs new `Acp` variant with user-configurable command
- `app/src/ai/agent_sdk/driver/harness/mod.rs:57` — `ThirdPartyHarness` trait definition — the contract `AcpHarness` must implement
- `app/src/ai/agent_sdk/driver/harness/gemini.rs` — `GeminiHarness` — closest structural analog to `AcpHarness`
- `app/src/ai/agent_sdk/driver/harness/mod.rs:133` — `HarnessKind` enum — may need `Acp` variant
- `app/src/terminal/view/ambient_agent/harness_selector.rs:61` — harness dropdown — needs ACP entry
- `app/src/server/server_api/harness_support.rs` — server-side harness registration — needs ACP format slug

## Proposed changes

### 1. `crates/warp_cli/src/agent.rs`

Add a new `Acp` variant to `pub enum Harness`:

```rust
/// Delegate to any ACP-compatible agent CLI (user-configured command).
#[value(name = "acp")]
Acp,
```

Place between `OpenCode` and the `Unknown` fallback variant.

### 2. `app/src/terminal/cli_agent.rs`

Add `Acp` to `pub enum CLIAgent`. Unlike other variants whose commands are
hardcoded, `CLIAgent::Acp` carries the user-supplied command string:

```rust
/// An ACP-compatible agent with a user-configured command.
Acp(String),
Comment thread
nigelpurvis marked this conversation as resolved.
Outdated
```

Update `command_prefix()`, `display_name()`, and `icon()` match arms:

```rust
// command_prefix
CLIAgent::Acp(cmd) => cmd.as_str(),
Comment thread
nigelpurvis marked this conversation as resolved.
Outdated

// display_name
CLIAgent::Acp(cmd) => cmd.as_str(), // show the actual command as the name

// icon — no dedicated icon yet; fall through to None
CLIAgent::Acp(_) => None,
```

### 3. New file: `app/src/ai/agent_sdk/driver/harness/acp.rs`

Create `AcpHarness` implementing `ThirdPartyHarness`. The struct carries the
user-configured command and optional args:

```rust
pub(crate) struct AcpHarness {
command: String,
args: Vec<String>,
}
```

Key `ThirdPartyHarness` method implementations:

- `harness()` → `Harness::Acp`
- `cli_agent()` → `CLIAgent::Acp(self.command.clone())`
- `validate()` → call `validate_cli_installed(&self.command, self.install_docs_url())`
- `install_docs_url()` → `None` (unknown for arbitrary agents; follow-up can add a registry)
- `prepare_environment_config()` → write ACP session config if needed; for v1 this may be a no-op or minimal JSON config file with MCP server list
- `build_command()` → spawn `self.command` with `self.args`, plus the stdio transport flags required by ACP (`--acp` or similar, depending on agent)
Comment thread
nigelpurvis marked this conversation as resolved.
Outdated
Comment thread
nigelpurvis marked this conversation as resolved.
Outdated

The ACP protocol handshake (JSON-RPC `initialize` / `initialized` exchange over
stdio) is handled in a new `run_acp_session()` function within this file, called
from `run()`. The initialize request must include:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] The core implementation path is underspecified: existing third-party harness runners launch terminal commands, while ACP needs a bidirectional JSON-RPC stdio process and an adapter into Warp's native conversation, diff, and tool-call UI.


```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"clientInfo": { "name": "warp", "version": "<warp_version>" },
"capabilities": {}
}
Comment thread
nigelpurvis marked this conversation as resolved.
}
```

Warp then reads the `InitializeResult` from the agent's stdout, sends
`initialized`, and transitions to the active session loop.

**Timeout:** If no `InitializeResult` arrives within 10 seconds, `run()` returns
`AgentDriverError::HarnessConfigSetupFailed` with a descriptive message.

**MCP passthrough:** After initialization, if Warp has configured MCP servers,
they are passed to the agent via the ACP session configuration per the protocol
spec before the first prompt is sent.
Comment thread
nigelpurvis marked this conversation as resolved.
Outdated
Comment thread
nigelpurvis marked this conversation as resolved.
Outdated

### 4. `app/src/ai/agent_sdk/driver/harness/mod.rs`

- Add `mod acp;` and `pub(crate) use acp::AcpHarness;`
- Add `Acp` variant to `HarnessKind` (line 133) if this enum is exhaustively matched anywhere
- Register `AcpHarness` in the harness dispatch logic alongside `ClaudeHarness` and `GeminiHarness`

### 5. `app/src/terminal/view/ambient_agent/harness_selector.rs`

Add `Harness::Acp` to the harness selector dropdown. For user-configured ACP
agents, each appears as a separate entry using its display name (the command
string). This requires reading the list of configured ACP agents from settings
and generating one selector entry per agent.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] The settings/config story is incomplete; define the schema, persistence/sync behavior, stable identity for multiple ACP agents, and how selector state serializes a specific configured agent rather than only Harness::Acp.


### 6. `app/src/server/server_api/harness_support.rs`

Add an `"acp"` format slug for ACP conversations, used when creating a
conversation record on the server. This follows the pattern of `"gemini_cli"`
for Gemini.

## Data flow

```
User selects ACP agent + sends prompt
→ AcpHarness::validate() checks command on PATH
→ AcpHarness::build_command() spawns process over stdio
→ run_acp_session() sends initialize, awaits InitializeResult (10s timeout)
→ sends initialized notification
→ passes MCP servers to session config (if any)
→ enters prompt/response loop:
Warp sends prompt as ACP session/message
Agent streams response chunks
Warp renders chunks as conversation blocks
File edits → diff view
Tool calls → tool call blocks
Comment thread
nigelpurvis marked this conversation as resolved.
Outdated
→ on agent exit or crash → conversation marked ended
```

## Tradeoffs

- **ACP agent config storage:** `CLIAgent::Acp(String)` conflicts with
`CLIAgent`'s current `&'static str` return and `Copy` constraints.
The preferred approach is likely storing ACP agent configuration
(name, command, args) in a dedicated settings model (e.g.
`AcpAgentConfig` struct in the AI settings) and keeping `CLIAgent`
as a simple fieldless `Acp` variant. The harness selector would then
read from that settings model. **Open question for Warp team: what is
the preferred home for per-agent ACP config?**
- **Stdio-only in v1:** Excludes ACP agents that communicate over HTTP. This
covers the majority of current ACP agents (opencode, Kimi, Gemini, Codex all
support stdio) and avoids auth complexity for v1.

## Testing and validation

Invariant-to-test mapping (from `product.md` success criteria):

1. **Unit test in `acp.rs`:** `AcpHarness::validate()` returns `Ok(())` for a
command on PATH and `Err(AgentDriverError::CliNotInstalled)` for a missing
command.
2. **Unit test:** `run_acp_session()` returns a timeout error if the mock agent
process does not send `InitializeResult` within the deadline.
3. **Integration test under `crates/integration/`:** Spawn a minimal ACP echo
agent (a small test binary that implements initialize/initialized and
returns a static response), send a prompt, and assert a conversation block
is produced.
4. **Manual:** Configure `opencode` as an ACP agent, send a prompt, confirm
response renders as a Warp conversation block.
5. **Manual:** Confirm `opencode` appears in the harness selector alongside
Claude Code, Codex, and Gemini.
6. **Regression:** `cargo nextest run` passes across existing harness tests
(`local_harness_launch_tests.rs`, `cli_agent_tests.rs`,
`mod_test.rs` in `driver/harness/`).

## Risks and mitigations

- **ACP spec churn:** The ACP spec is still evolving. Mitigation: pin to the
stable `initialize`/`initialized`/`session/message` core and treat unknown
fields as ignored (forward-compatible deserialization with `#[serde(flatten)]`
or `deny_unknown_fields = false`).
- **CLIAgent enum serialization:** Adding `Acp(String)` changes the shape of a
serialized enum. Mitigation: verify that `CLIAgent` is not persisted to disk
or sent over the network in a way that would break existing stored data; if it
is, add a migration.
- **No icon for ACP agents:** The harness selector will show ACP agents without
a logo. Mitigation: acceptable for v1; a generic "agent" icon can be added as
a follow-up.

## Follow-ups

- Session resumption for ACP agents (requires ACP `loadSession` capability).
- A curated registry of known ACP agents with pre-filled commands and install URLs.
- Warp as ACP server (exposing Warp Agent to external IDEs like Zed).
- Per-agent icons for known ACP CLIs.
- HTTP transport support for remote ACP agents.