feat(agents): Dispatch and supervise coding agents over ACP (#72)

## What

An Agents feature: hand a task to a coding agent and follow it from the IDE. Agents speak the [Agent Client Protocol](https://agentclientprotocol.com). Built in: Claude Code, Codex, Gemini CLI, GitHub Copilot, Cursor, OpenCode, Qwen Code, Kimi CLI, Goose, Auggie, Kiro, Mistral Vibe. You can add your own in `<config>/agents/<id>.toml` (`name`, `command`, `args`, `[env]`). An agent whose command isn't on `PATH` is listed under "Not installed".

- **New Task page.** An editor page with a prompt box. You pick the agent, model, mode and thinking effort, and whether it runs in a new worktree (from a chosen branch) or in the checkout.
- **Agents tool window.** Tasks are grouped by what they need: Needs Input, Failed, Running, Ready to Review, Archived. It also shows a stripe badge, a status bar item and notifications.
- **Session tab.**
  - The conversation: Markdown, thinking, tool calls with diff stats, the plan.
  - Permission requests appear as an editor banner.
  - The changed files are compared against the task's base commit and open in the diff viewer.
  - A context-usage meter.
- **Composer.**
  - `@` references files from the project index. A referenced file's text is sent when the agent accepts embedded context, otherwise a `resource_link`.
  - `/` lists the agent's commands; ↑/↓ recall earlier messages.
  - Images can be pasted, dropped or picked; Shift+Tab cycles the mode.
  - While the agent works, Enter queues the message and ⌘↵ interrupts the turn and sends it now. Queued messages can be sent now, edited or removed.
- **Send to Agent.** In the editor and Project view context menus. It adds `@file:10-20` references to the message you typed last.
- Landing the work is left to the existing Git features: the worktree is a normal branch.

## How

- **`crates/ide-agent`**: one ACP agent process per task, running in the task's directory.
  - It folds `session/update` into a timeline and answers `session/request_permission`.
  - It tracks the session's settings: `configOptions`, falling back to the older `modes` / `models` fields. The user's choices are applied again on every new or reloaded session.
  - Tasks are saved as JSON per task. Sessions resume with `session/load` without duplicating history, and old task files are upgraded when read.
- **`ide-jsonrpc`**: adds newline-delimited framing (ACP) next to Content-Length (LSP).
- **`ide-vcs`**: adds `numstat_with_worktree` for the changes list.
- **Editor**:
  - The Switcher (⌃⇥) lists every open editor, as IDEA does, not just files.
  - `EditorKindContribution.restorable` lets a kind's tabs be saved with the editor session, so agent tabs survive reloads.
- **UI**: `Select` popups now size to fit short option details (they used to cut them off).

## Testing

- `cargo test -p ide-agent` (14 tests). They use an in-memory ACP agent and cover permissions, restart with `session/load`, settings, the queue with send-now and stop, and prompt blocks.
- There is also an ignored end-to-end test against the real Claude Code adapter: `OPENIDEA_AGENT_E2E=1 cargo test -p ide-agent real_claude -- --ignored`.
- `pnpm vitest run`: 2052 tests.
- `cargo clippy -p ide-agent -p openidea --all-targets -- -D warnings` and typecheck are clean.
- Driven in the running app with Claude Code:
  - A task in a worktree, with permission answers, changes listed and the diff opened.
  - A follow-up after restarting the IDE.
  - `@` reference with embedded content, `/` commands, Shift+Tab mode changes.
  - A queued message and send-now, an image prompt, Send to Agent from a selection.
  - The Switcher and restored tabs after a reload.

Not covered yet: only Claude Code was driven end to end. The other agents go through the same ACP path but weren't run here.


Merged on the maintainer's go-ahead. Merge with main: kept both sides in Cargo.toml (ide-agent + ide-clipboard), the feature registry (agents + vim) and zh.json (no duplicate keys). After the merge: typecheck clean, full vitest 268 files / 2247 tests passed, ide-agent/ide-jsonrpc/ide-vcs 144 passed, bindings unchanged after regeneration.
Цей коміт міститься в:
l0ng-ai authored and GitHub committed 2026-10-27 14:44:28 +08:00
1 джерело b4742b730b
коміт 2eaa6cb0c7
75 files changed
+6377 -63

No files matched your search

+3
Переглянути файл
@@ -298,6 +298,9 @@ export const intellijMacos: Record<string, string[]> = {
"http.runAll": [],
"db.execute": ["Meta+Enter"],
"db.showToolWindow": [],
"agents.newTask": ["Meta+Shift+I"],
"agents.showToolWindow": [],
"agents.sendToAgent": [],
// Database tool window (when the tree has focus; `db.openConsole` anywhere).
"db.openConsole": ["Meta+Shift+L"],
"db.newDataSource": [],
+3
Переглянути файл
@@ -295,6 +295,9 @@ export const intellijWindows: Record<string, string[]> = {
"http.runAll": [],
"db.execute": ["Ctrl+Enter"],
"db.showToolWindow": [],
"agents.newTask": ["Ctrl+Alt+Shift+I"],
"agents.showToolWindow": [],
"agents.sendToAgent": [],
// Database tool window (when the tree has focus; `db.openConsole` anywhere).
"db.openConsole": ["Ctrl+Shift+Q"],
"db.newDataSource": [],
+4
Переглянути файл
@@ -322,6 +322,10 @@ export const vscodeMacos: Record<string, string[]> = {
"find.popup.previousResult": ["Meta+Shift+G"],
"find.popup.openResult": [],
"find.popup.recentSearches": ["Alt+Down"],
// Agents (VS Code's chat: ⇧⌘I)
"agents.newTask": ["Meta+Shift+I"],
"agents.showToolWindow": [],
"agents.sendToAgent": [],
// Search fields (find bar, Find in Files): last, so they win over editor keys while one is focused.
"find.field.newLine": ["Meta+Shift+Enter"],
"find.field.recentSearches": ["Alt+Down"],
+4
Переглянути файл
@@ -59,4 +59,8 @@ export const vscodeWindows: Record<string, string[]> = {
"markdown.editorOnly": [],
"markdown.editorAndPreview": ["Ctrl+K V"],
"markdown.previewOnly": ["Ctrl+Shift+V"],
// Agents (VS Code's chat: Ctrl+Shift+I)
"agents.newTask": ["Ctrl+Shift+I"],
"agents.showToolWindow": [],
"agents.sendToAgent": [],
};