refactor(tools): tagged render-intent union for tool-call presentation

Replace the "bag of optional fields" tool-presentation types
(ToolCallPresentation / ToolResultPresentation / ToolTerminal) with a
card-tagged discriminated union — the standing FIXME(tool-presentation).
A tool declares one render intent per call/result and the ACP bridge
switches on `card`:

  ToolCallView   = generic | terminal | diff
  ToolResultView = generic | terminal

The `diff` card is new: fs write/edit now emit an ACP {type:'diff'}
content block (an editor's inline diff), which the old shapes could not
express. The bridge also relativizes a file card's title against the
session cwd (mirroring claude-agent-acp's toDisplayPath) while keeping
locations/diff paths raw, and derives the no-capability fenced console
fallback from a terminal result's output. read gains the window-in-title
(`Read foo.txt (5 - 8)`) and an always-set location line, matching the
reference adapter field-for-field.

Migrates all three producer families (tool-fs, tool-bash, tool-todo) and
the sole consumer (the ACP bridge) together — the source does not compile
piecewise. Adds snapshot coverage for the terminal _meta path (a new
capability-advertising scenario) and re-records the fs goldens to show the
diff cards. Applied-hunk (result-time, context-line) diffs need a new
result/event shape and are a follow-up.

RFC: docs/rfc/implemented/architecture/2026-07-02-tool-render-intent-union.md
This commit is contained in:
Tianyi Cui
2026-07-03 02:04:03 +08:00
parent 490fe002a1
commit 1a57d67058
35 changed files with 1015 additions and 450 deletions
+2 -2
View File
@@ -99,7 +99,7 @@ Tool-call presentation is **owned by each tool** (`presentCall` / `presentResult
| `ToolCallKind` mapping | S | ✅ | ✅ | ✅ | `execute`/`read`/`edit`/`other` inferred from the tool; richer mapping possible. |
| `ToolCallStatus` | S | ✅ | ✅ | ✅ | `in_progress` → `completed`/`failed`. |
| `content` blocks | S | ✅ | ✅ | ✅ | Text content; the description renders above the card. |
| `diff` content | S | ❌ | ✅ | ✅ | No structured diff rendering for edits (would need a diffing edit tool + presenter). |
| `diff` content | S | ✅ | ✅ | ✅ | The `write`/`edit` tools declare a `diff` render intent (`presentCall` → `{ card: 'diff' }`); the bridge emits `{ type: 'diff', path, oldText, newText }` content blocks (call-time, args-derived — applied-hunk diffs are a follow-up). |
| `terminal` content | S | ✅ | ✅ | ✅ | Via the Zed `_meta` terminal convention (see below), not the spec `terminal/*` sub-protocol. |
| `locations` (follow-along) | S | ✅ | ✅ | ✅ | The `read`/`write`/`edit` tools emit `{ path, line? }` file-location hints via `presentCall`. |
| `rawInput` | S | ✅ | ⚠️ | ✅ | Parsed tool args surfaced as `rawInput`. |
@@ -147,7 +147,7 @@ Ranked by how commonly the reference adapters ship them and how much UX they unl
5. **Slash commands** (`available_commands_update`).
6. **MCP passthrough** (`mcpServers` on `session/new` + `mcpCapabilities`).
7. **Richer prompt content** — image / embedded `resource` blocks (needs a multimodal model path).
8. **Diff tool rendering** — structured `diff` content for edit tools (the `locations` follow-along hint already ships on `read`/`write`/`edit`).
8. **Applied-hunk diff rendering** — the `write`/`edit` diff cards ship (call-time, args-derived: whole `old_string`→`new_string`). Result-time structured-patch hunks with surrounding context (what `claude-agent-acp` derives from a PostToolUse hook) need a new result/event shape carrying the patch — a follow-up.
9. **Usage reporting** (`usage_update`) — the harness already records token usage internally (on `assistant/message`).
10. **Editor filesystem delegation** (`fs/read_text_file` / `fs/write_text_file`) — lets the agent see unsaved buffers; lower priority since the harness has direct disk access.