Editor Compatibility
One Core, Many Surfaces
An agent harness is not a CLI. It is a core runtime — conversation loop, tool registry, permission model, configuration — presented through interface surfaces. Build the core as a library with no I/O. Build each surface as a thin translation layer between the core’s event stream and whatever medium the user is working in.
This separation is not optional for any agent that intends to reach developers where they work: terminal, VS Code, JetBrains, Neovim, web. An agent that lives in only one of these is an agent most developers cannot use.
The Interface Matrix
Each surface has distinct constraints on transport, input handling, output rendering, and lifecycle.
| Surface | Transport | Input | Output | Lifecycle |
|---|---|---|---|---|
| CLI / REPL | stdin/stdout | Line editing, signal handling (Ctrl+C/D) | Streaming markdown, syntax highlighting | Process lifetime = session lifetime |
| VS Code | JSON-RPC over stdio or WebSocket | Extension API, editor selections, file context | Webview panels, inline annotations, decorations | Extension host; survives restart via state serialization |
| JetBrains | HTTP/WebSocket or stdio | Plugin API, PSI tree context, selections | Tool windows, editor inlays, balloons | JVM plugin lifecycle; handles project open/close |
| Neovim | stdio or TCP (Lua RPC) | Lua callbacks, buffer context, visual selections | Floating windows, virtual text, quickfix | Plugin lazy-loaded on demand |
| Web | HTTP/SSE or WebSocket | REST / WebSocket messages | JSON event stream rendered by frontend | Stateless server; session state externalized |
The Harness Event Stream
The core emits a single, interface-agnostic event stream. Every surface consumes it and translates into native presentation.
graph LR
subgraph core ["Agent Core"]
CL["Conversation Loop"]
TR["Tool Registry"]
PM["Permission Model"]
end
ES["Event Stream"]
subgraph surfaces ["Interface Surfaces"]
CLI["CLI / REPL"]
VSC["VS Code Extension"]
JB["JetBrains Plugin"]
WEB["Web Server"]
end
core --> ES
ES --> CLI
ES --> VSC
ES --> JB
ES --> WEB
Illustrative Event Vocabulary
The shape below is pedagogical, not a spec. Anthropic’s Messages API streams content_block_start/delta, message_delta, message_stop, tool_use blocks. ACP defines its own JSON-RPC method set (session/prompt, session/update, …). OpenAI’s Responses API has yet another shape. The architectural principle — one core, many surfaces, a single event vocabulary between — holds even though no standard names the events.
| Event | Payload | CLI renders as | IDE renders as | Web emits as |
|---|---|---|---|---|
MessageStart | { role, turn_id } | Newline + role prefix | New chat bubble | SSE message_start |
TokenDelta | { text } | Append to terminal | Append to webview | SSE token_delta |
ToolCallRequest | { tool, args } | Print tool + args | Inline annotation | JSON tool_request |
PermissionPrompt | { tool, args, risk } | Blocking readline (y/n) | Modal dialog | JSON awaiting frontend confirm |
ToolCallResult | { tool, status, content } | Print result, highlighted | Collapsible chat panel | SSE tool_result |
Error | { code, message } | stderr in red | Notification balloon | SSE error |
The key principle: the core never imports terminal, editor, or HTTP libraries. It emits events; surfaces translate.
Permission Prompts Across Surfaces
Permission handling is the hardest interface problem. The core must not block on a prompt. It emits an async event and awaits an async response. The surface decides how to collect the decision: CLI uses blocking readline; IDE uses a modal dialog; web uses an HTTP round-trip.
sequenceDiagram
participant C as Agent Core
participant S as Interface Surface
C->>S: PermissionPrompt { tool: "bash", args: "rm -rf dist/", risk: "destructive" }
Note over S: CLI: blocking readline<br/>IDE: modal dialog<br/>Web: async HTTP
alt User approves
S->>C: PermissionResponse { approved: true }
C->>C: Execute tool
else User denies
S->>C: PermissionResponse { approved: false }
C->>C: Return denial to model
end
Timeouts and batch approvals (“approve all bash(git *) for this session”) are surface-specific. The core sees individual approve/deny; the surface manages the UX.
Core Extraction Pattern
The architectural pattern is core extraction: agent logic as a library with no I/O dependencies; each interface as a thin shell wiring the library to its environment.
agent-core/ # Library — no I/O, no UI
conversation.rs # Agentic loop, tool dispatch
tools.rs # Tool registry, execution
permissions.rs # Permission model, approval flow
config.rs # Configuration loading, merging
events.rs # Event type definitions
agent-cli/ # Binary — depends on agent-core
agent-vscode/ # Extension — depends on agent-core (subprocess/WASM)
agent-jetbrains/ # Plugin — depends on agent-core (subprocess/HTTP)
agent-server/ # HTTP server — depends on agent-core
The litmus test: can you test the core without an interface? A mock surface that records events and replays permission responses should be enough to drive the full conversation loop. If your tests require a terminal emulator or a browser, the abstraction is leaking.
Current Cross-Platform Adoption (May 2026)
| Platform | Reach |
|---|---|
| Claude Code | First-party VS Code extension (5.2M installs) + JetBrains plugin (beta, IntelliJ/PyCharm/WebStorm/etc.) + community coder/claudecode.nvim (WebSocket MCP). |
| OpenAI Codex | First-party VS Code extension (4.9M installs) + native JetBrains integration since Jan 2026 (IntelliJ 2025.3+, PyCharm, WebStorm, Rider). |
| Gemini CLI | VS Code Companion extension + JetBrains via ACP + Neovim via ACP (CodeCompanion, avante.nvim). |
| Cursor / Windsurf | Single-surface VS Code forks. No JetBrains build, no Neovim plugin. |
| Aider / others | Terminal-only. No first-party IDE story. |
Agents that extract the core early (Claude Code, Codex, Gemini CLI) expand to new surfaces without rewriting agent logic. Agents that embed logic in the interface (Cursor, Windsurf) are locked to a single editor fork.
Agent Client Protocol (ACP)
The emerging cross-editor standard is the Agent Client Protocol (ACP), originated by Zed and backed by JetBrains since their Oct 2025 partnership. ACP is to agent-to-IDE communication what LSP is to language servers: a JSON-RPC method set (session/prompt, session/update, …) any editor can speak to any conforming agent.
- Reference implementation: Gemini CLI.
- Adoption: 25+ agents implement ACP — Auggie, Hermes, and others — making them addressable from Zed, JetBrains, Neovim, and Emacs through one protocol.
- Shape: JSON-RPC, bidirectional, LSP-like layering but scoped to agent conversations (sessions, prompts, tool calls, permission requests) instead of code intelligence.
If you are building a new agent and want IDE reach without writing N native extensions, ACP is the lowest-cost path to plurality.
State Synchronization Across Surfaces
When the same core serves multiple surfaces at once (CLI plus VS Code), session state must be synchronized or explicitly isolated.
| Strategy | How it works | Tradeoff |
|---|---|---|
| Exclusive sessions | Each surface owns its session. No sharing. | Simple; users lose continuity when switching. |
| Shared server | Local daemon runs the core; CLI and IDE are clients. | Full continuity; adds a daemon to manage. |
| State file locking | Session state on disk; surfaces acquire a lock. | Works offline; lock contention possible. |
| Cloud sync | State in a remote service; all surfaces read/write via API. | Works across machines; needs network. |
The shared-server pattern is emerging as dominant for local development. One agent-server process owns sessions; CLI and IDE extension connect as clients. This avoids duplicate model calls and keeps a tool call initiated in VS Code visible in the terminal — what users expect once they have the agent in more than one place.