fix: send only stdout delta in Bash terminal output to prevent replay#14
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements delta deduplication for terminal output updates by tracking the last-sent stdout snapshot per callId and emitting only the new suffix. This prevents long-running commands from replaying their entire output multiple times. The feedback highlights a critical edge case: when the stdout tail buffer rotates, the current prefix check fails and falls back to sending the full snapshot, causing output duplication. It is recommended to resolve this by finding the overlap between the consecutive snapshots and to add a corresponding regression test.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| // Only the suffix past the previously-sent snapshot is new output. Guard | ||
| // against a non-monotonic snapshot (tail rotated) by falling back to the full | ||
| // value when the previous snapshot isn't a prefix of the current one. | ||
| const delta = lastSent && full.startsWith(lastSent) ? full.slice(lastSent.length) : full; |
There was a problem hiding this comment.
When the stdout tail buffer rotates (i.e., older output is discarded from the beginning of the cumulative snapshot), full.startsWith(lastSent) will be false. In this case, falling back to the full full snapshot causes the remaining overlapping part of the buffer to be replayed and duplicated in the terminal. Since long-running commands are exactly the ones that will trigger tail rotation, this defeats the purpose of preventing replay.
We can resolve this by finding the longest suffix of lastSent that matches a prefix of full (the overlap) and sending only the remaining suffix of full as the delta.
let delta = full;
if (lastSent) {
if (full.startsWith(lastSent)) {
delta = full.slice(lastSent.length);
} else {
const maxOverlap = Math.min(lastSent.length, full.length);
for (let i = maxOverlap; i > 0; i--) {
if (lastSent.endsWith(full.slice(0, i))) {
delta = full.slice(i);
break;
}
}
}
}
Problem
Long-running Bash commands display duplicated output. The longer the command runs, the worse the duplication.
Root cause is a semantic mismatch:
stdoutTail(progress events) and resultcontentare cumulative snapshots — each carries the full tail of stdout so far.terminal_output.datais append semantics — the client writes each value as new bytes to the terminal.dispatchTerminalUpdatepassed the full snapshot intodataevery time, so N progress events replayed the output N times, and the final result event appended it once more.Fix
Add
terminalSentData: Map<callId, string>onZcodeAcpServerto track the last-sent snapshot per Bash call.dispatchTerminalUpdatenow:full.slice(lastSent.length))Test
`