- Replaced legacy `SWAP`, `INS`, and `PASTE` commands with unified `PUT` and `CUT` hunks across parser, grammar, tokenizer, and test suites.
- Added support for named registers and span paste operations in clipboard and block execution logic.
- Implemented indentation repair and enhanced gap locator formatting for improved patch resilience.
- Updated documentation, system prompts, and session analysis scripts to reflect the new syntax and header shapes.
The hashline multi-section path in `executeHashlineSingle` returned
`perFileResults: rendered.map(r => r.perFileResult)` directly. Each
per-section result had already been individually pruned by
`renderSection`, but the whole array bypassed the shared aggregate
budget added in 3987969 — a single hashline payload touching many files
with sub-32 KB snapshots each could still serialize unbounded snapshot
bytes into one session JSONL line.
Wrap the multi-section return in `pruneOversizedEditSnapshots`, which
delegates to `capPerFileSnapshots` and enforces the shared cap walking
left-to-right; early sections keep their ACP diff visualization, later
sections in a many-file batch degrade to text-only.
End-to-end regression test seeds five real on-disk files (~10 KB
combined snapshots each), runs a multi-section hashline SWAP via
`executeHashlineSingle`, and asserts the aggregate result holds the
cumulative kept snapshot bytes under `MAX_EDIT_SNAPSHOT_TEXT_CHARS`
with at least one section pruned.
The per-entry 32 KB cap let a many-file batch (apply_patch / hashline
touching N files) accumulate unbounded snapshot bytes because each
`perFileResults` entry was checked independently. 100 files × 30 KB
each kept everything (~3 MB) even though the whole array still
serializes into one session JSONL line.
`capPerFileSnapshots` walks entries left-to-right with one shared
`MAX_EDIT_SNAPSHOT_TEXT_CHARS` budget. Each per-entry payload is still
capped individually by `pruneSnapshot`; if an entry's surviving bytes
would push the running aggregate past the cap, the entry is stripped
and stamped with `snapshotsPruned: true`. Early entries keep their ACP
diff visualization; later entries in a large batch degrade to text-only
exactly like over-sized single edits.
Regression test exercises five equal-size entries that each fit the
per-entry budget but bust it cumulatively, asserting only the first two
keep snapshots and the trailing three carry the pruned marker.
When a multi-entry single-path edit prunes the first entry's snapshots
(large pre-image) and keeps a later entry's snapshots (file shrunk
between entries), the aggregator at `executeSinglePathEntries` recorded
the later entry's small `oldText`/`newText` as the whole-file
transition. ACP clients would then render a misleading partial diff
instead of degrading to text-only for the over-budget edit.
Add an explicit `snapshotsPruned` marker on `EditToolDetails` /
`EditToolPerFileResult`, set by `pruneSnapshot` whenever it strips a
payload. `executeSinglePathEntries` tracks the flag across child
results and suppresses aggregate `oldText`/`newText` (re-stamping the
marker on the aggregate) the moment any child was pruned;
`executeApplyPatchPerFile` propagates the flag onto each per-file entry.
Regression test exercises the exact scenario the reviewer raised on
#3787: replace mode where entry 1 collapses a >1 MB file to a single
line (pruned) and entry 2 trivially renames the now-tiny result; the
aggregate result now carries `snapshotsPruned: true` with both
snapshot fields omitted.
Edit-tool results carried the full pre/post file content in
`details.oldText` / `details.newText`. For large files this bloated each
per-turn JSONL line by hundreds of KB even though the snapshots are
never sent to the LLM (provider serializers send only `content`) and
only consumed by the ACP event mapper for diff visualization.
Add `pruneOversizedEditSnapshots` and apply it at every site that
constructs an `EditToolDetails` / `EditToolPerFileResult`:
`executePatchSingle`, `executeReplaceSingle`, hashline `renderSection`
(delete + update branches), and both aggregators in `edit/index.ts`.
When combined `oldText` + `newText` exceeds 32 KB the helper returns a
shallow copy with both fields omitted; smaller edits pass through
unchanged. The diff, path, firstChangedLine, op, move, and diagnostics
fields are preserved, and ACP returns no diff content for over-budget
files (the text content still flows — graceful degradation).
Fixes#3786