You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ACP: completed tool_call_update diff content is derived from tool input schema, not result metadata — write/patch and plugin edit tools never emit diffs #52636
So the diff content block is emitted only when the tool's input schema happens to have top-level oldString/newString. Consequences:
The built-in write tool ({filePath, content}) never emits a diff content block.
The built-in patch tool ({path, patchText}) never emits one either (v1 had the same gap for apply_patch, fixed in [FEATURE]: ACP: Send diff on apply_patch #30913 — this regressed with the v2 rewrite).
Any plugin that replaces/wraps edit with a different schema (e.g. a batch edits: [{oldString, newString, ...}] array) never emits one.
Clients that render per-turn diffs solely from that block show nothing. Zed is affected this way — its inline diff cards come from the diff content (also confirmed in the discussion on #52466; client-side context in zed-industries/zed#54918).
The data is already there: tools report their changes in result metadata as typed FileDiff.Info ({file, patch, additions, deletions, status} — packages/schema/src/file-diff.ts), and the ACP layer already consumes that field elsewhere (permission.tssyncEditedFiles reads metadata.files). Only completedToolUpdate ignores it and guesses from the input schema instead.
Suggested fix: derive the diff content from result metadata (metadata.files[] / unified patch) rather than the input schema. The unified-patch → per-file oldText/newText conversion already exists in permission.tspatchPreviews (Patch.parse + Patch.derive) and could be reused here.
Plugins
@cortexkit/aft-opencode 0.58.0 (reproduced with and without it — the bug is not plugin-specific, but the plugin's batch-edit schema makes it visible for edit too)
Run opencode acp with Zed (or any ACP client that renders diff content).
2a. Prompt the agent to use write to create/modify a file → the completed tool_call_update contains only a text item, no {type: "diff"} block → no per-turn diff in Zed.
2b. With a plugin registering an edit tool whose schema is {path, edits: [{oldString, newString}]} (instead of top-level fields), the same happens for edits.
For contrast, the built-in edit with top-level oldString/newString does emit the diff block — confirming the mapping depends on the input schema rather than the actual change.
Verified by capturing the ACP traffic directly: completed update content is ['content', 'diff'] with the built-in edit, and ['content'] for write and for the plugin edit, while rawOutput.metadata contains the full unified patch in all three cases.
Built-in edit/write/patch already report their changes in result metadata as metadata.files[] (FileDiff.Info[] with full patches) — no tool-side change needed for them. Fixing completedToolUpdate to derive diff content from that field restores diffs for all three at once.
The wrinkle is plugin tools: tool metadata is not documented as a contract, so plugins invent their own fields. The plugin I reproduced this with (a batch-edit edit replacement) reports diffs as metadata.diff (unified patch string) + metadata.filediff (array with patches) instead of metadata.files[] — so a fix reading only metadata.files[] would still leave plugin tools without diffs.
Suggested as two parts:
ACP layer derives diff content from metadata.files[] (fixes the built-in tools).
Formalize files: FileDiff.Info[] as the documented tool-result metadata contract for reporting file changes (or expose a helper on the Tool API for it), so plugin authors can comply. Until plugins adopt it, their tools would still lack diff content — but that becomes plugin conformance rather than an ACP bug.
Thanks for the detailed report! Starting in 2.0.25, ACP builds completed tool diffs from file-change metadata, restoring write and patch diffs without depending on the tool's input schema; plugin tools should report changes through metadata.files too. Update with opencode upgrade or npm i -g @opencode/cli@latest (desktop: update the app). Please comment or reopen if this still happens on that version or newer.
Activity
github-actions commentedon Oct 2, 2026
This issue might overlap with a previously reported ACP diff issue. Please check:
apply_patch; this issue reports the same missing diff for patch tools in v2, following the earlier v1 fix.gaojunran commentedon Oct 2, 2026
Follow-up clarifying scope (verified empirically by capturing ACP traffic + reading tool results):
Built-in
edit/write/patchalready report their changes in result metadata asmetadata.files[](FileDiff.Info[]with full patches) — no tool-side change needed for them. FixingcompletedToolUpdateto derive diff content from that field restores diffs for all three at once.The wrinkle is plugin tools: tool metadata is not documented as a contract, so plugins invent their own fields. The plugin I reproduced this with (a batch-edit
editreplacement) reports diffs asmetadata.diff(unified patch string) +metadata.filediff(array with patches) instead ofmetadata.files[]— so a fix reading onlymetadata.files[]would still leave plugin tools without diffs.Suggested as two parts:
metadata.files[](fixes the built-in tools).files: FileDiff.Info[]as the documented tool-result metadata contract for reporting file changes (or expose a helper on the Tool API for it), so plugin authors can comply. Until plugins adopt it, their tools would still lack diff content — but that becomes plugin conformance rather than an ACP bug.neriousy commentedon Oct 20, 2026
Thanks for the detailed report! Starting in 2.0.25, ACP builds completed tool diffs from file-change metadata, restoring write and patch diffs without depending on the tool's input schema; plugin tools should report changes through
metadata.filestoo. Update withopencode upgradeornpm i -g @opencode/cli@latest(desktop: update the app). Please comment or reopen if this still happens on that version or newer.