30 Commits

Author SHA1 Message Date
can1357 8fdb6a4197 fix(edit): dropped compact seen-line retry tokens (#8461)
Reverted the retry-token continuation flow (merge 34628528bf and prompt
follow-up 446e745bf4): seen-line rejections resend the full patch again.
2026-08-16 07:58:55 +02:00
Kigbnajd 705442dbde fix(edit): preserve guard after transformed writes 2026-08-13 21:33:22 +02:00
Kigbnajd dadaceaa6f fix(edit): add compact seen-line retries 2026-08-13 20:15:18 +02:00
metaphorics 3802d2dd80 chore(ts): enforce noImplicitOverride 2026-08-05 02:49:01 +09:00
can1357 5ea583e413 feat: replaced legacy editing commands with unified put and cut syntax
- 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.
2026-07-31 00:19:52 +02:00
can1357 e05f229f43 refactor: standardized editing syntax by removing copy and delete operations
- Removed copy and delete operations across tokenizer, parser, grammar, and clipboard logic.
- Standardized line-editing operations and block resolvers to use cut exclusively.
- Updated documentation, prompts, and test suites to reflect the removal of copy and delete syntax.
2026-07-30 07:42:48 +02:00
can1357 f2251b7d59 chore: normalize changelogs and formatting after merging open fixes 2026-07-29 23:09:40 +02:00
Márton Danóczy 7708f372b5 fix(hashline,coding-agent): key snapshot tag on actually-persisted content after ACP bridge writes
Root cause of the reported "edit tool silently reformats the whole
file" corruption: fs/write_text_file has no verbatim guarantee. When
an ACP client (e.g. Zed with format_on_save: on) reformats a buffer
on save, routeWriteThroughBridge reported the pre-write content as
successfully written, and Patcher.commit keyed the returned snapshot
tag on that same pre-write text instead of what actually landed on
disk. The next edit anchored on that tag then resolved hunks against
a baseline the file had already drifted away from, which is what
produced whole-file "corruption" from single-line hunks -- reproduced
live in this session against real Swift/JSON/TypeScript files with
Zed as the ACP client.

- routeWriteThroughBridge reads the file back after the bridge write
  and returns the verified content plus a drift flag (best-effort:
  ACP defines no ordering between the client acking the write and its
  own async format-on-save settling, so this degrades gracefully to
  the old stale-tag-on-next-read failure mode, never to corruption).
- HashlineFilesystem.writeText propagates that verified content in
  view-space (the same space readText returns -- e.g. a notebook's
  editable cell text, not its raw JSON), not storage-space, so tag
  validation on the next edit compares like with like.
- Patcher.commit keys fileHash/header/snapshot on the verified
  post-write content (normalized, so BOM/line-ending restoration never
  produces a false "drift") when it diverges from what was sent, and
  appends a warning naming the drift -- but deliberately leaves the
  returned `after` (and therefore the model-visible diff) scoped to
  the intended hunk. Diffing against the full drifted file would
  balloon the tool response to span every reformatted line (measured
  ~6.8x inflation on a 245-line file with one touched line); the
  warning is the correct O(1) channel for "your editor reformatted
  this," not an O(file-size) diff.
- write.ts keys its own snapshot header on the verified bridge content
  too (no diff-size concern there since write always replaces the
  whole file).

Caught via code review (dispatched against the first pass of this
fix): a naive "just use the verified content everywhere" fix broke
.ipynb editing outright (write-space vs read-space content mismatch,
tag invalid on every notebook edit) and would have inflated every
drifted edit response by ~6.8x. Both are now covered by regression
tests that fail against the pre-fix code and pass against this one.

(cherry picked from commit 35ab80e43be5800b2f48728e4400eb9fd7f7f7d2)
2026-07-29 23:08:38 +02:00
can1357 1301d5b07a Merge remote-tracking branch 'origin/farm/f5b6b32e/fix-edit-anchor-fresh-read' 2026-07-02 23:43:10 +02:00
can1357 0059aed4f0 feat(hashline): enabled content hash matching to resolve tag collisions
- Simplified match logic to rely exclusively on content hash equality.
- Removed strict validation that rejected colliding snapshot tags.
- Updated recovery behavior to resolve collisions to the most-recently recorded snapshot.
- Refactored tests to expect successful preview and patching despite tag ambiguity.
2026-07-02 22:45:29 +02:00
roboomp 367a002e48 fix(hashline): column-cap revealed anchor lines and gate merge on width truncation
Codex reviewer flagged that the per-reveal cap only limits the line
count, not each line's width. A minified-bundle-style wide line
(megabytes on one row) would be stored verbatim in the RevealedLine
and formatted straight into the thrown edit error — bypassing the
column-truncation read/search already apply to long lines and dumping
that much content into the tool result, TUI, and model context.

assertSeenLines now clips each revealed line at
SEEN_LINE_REVEAL_MAX_COLUMNS (512, matching search's DEFAULT_MAX_COLUMN)
with a trailing ellipsis marker and treats any clip as truncated. The
existing truncated-gated merge keeps the guard closed on clipped
reveals so the model cannot land an edit having only seen the first
512 chars of a wide line, and the message keeps the range-re-read
guidance.
2026-07-02 08:20:47 +00:00
roboomp f42fa38f7c fix(hashline): do not merge revealed seen-lines on the truncated path
Codex reviewer flagged that when an anchor range exceeds
`SEEN_LINE_REVEAL_CAP`, merging the revealed prefix into `seenLines`
lets a model split a blind over-cap edit into two <=cap-line retries
and slip past the range-re-read gate: attempt 1 reveals+merges lines
100-139, attempt 2 reveals+merges the 140-159 tail, attempt 3 applies
— all without a single range read.

The merge is now gated on `truncated === false`: only a reveal that
covered EVERY unseen anchor line joins `seenLines`. Truncated reveals
keep the range-re-read guidance and the reveal window stays anchored
at the head across retries, so the same over-cap patch keeps rejecting
until the model actually re-reads the range.
2026-07-02 08:11:16 +00:00
roboomp 72a30af57c fix(hashline): reveal seen-line-guard content and merge into snapshot on reject
Structural-summary reads (default for parseable code >100 lines) mint a
`[path#tag]` that only marks declaration/boundary lines as displayed;
edits anchored inside an elided body then hit `#assertSeenLines` in
`packages/hashline/src/patcher.ts` and reject with "never displayed
(it showed a partial range, a search hit, or a folded summary)". The
existing message pointed at a range re-read, but that made every such
recovery a three-turn round-trip (edit-fail → range read → edit-retry)
and models frequently retried the same edit instead of following the
hint — 5-8 out of 10 edits failed for the reporter.

The rejection now:
- Inlines the actual file content at the unseen anchor lines, from
  `matchedSnapshot.text` (which by definition equals the live normalized
  content on the no-drift path), up to `SEEN_LINE_REVEAL_CAP` (40) lines.
- Merges the revealed lines into the snapshot's `seenLines` set, so a
  straight retry with the same `[path#tag]` header succeeds without a
  follow-up read. The content is inside the error the model receives,
  which is the proof it has now seen those lines.
- For anchor ranges over the cap, only the revealed prefix is merged;
  the message keeps the range-re-read guidance for the remainder so
  runaway blind edits don't sneak past.

Fixes #4224
2026-07-02 08:05:12 +00:00
can1357 efee86afe5 fix(hashline): reject ambiguous colliding snapshot tags 2026-07-01 21:53:16 +02:00
roboomp b081c8abaf fix(hashline): guarded against 16-bit snapshot tag collisions
The visible 4-hex hashline tag is the low 16 bits of a non-cryptographic hash, so two genuinely different file states can collide. Snapshot storage keyed dedup on the hash alone, fusing distinct texts (and their seenLines) into one stored entry. The patcher treated `computeFileHash(live) === expected` as an exact live match and took the no-drift path — applying line-anchored edits directly to unrelated live content and bypassing recovery.

Keeps the visible tag format for backward compatibility and widens identity at the two junctures where it matters:

- Store dedup now compares (hash, text). Distinct texts with the same 4-hex tag are retained as separate versions with independent seenLines; identical repeated reads still fuse as before.

- New abstract SnapshotStore.byContent(path, text) disambiguates collisions by content.

- Patcher requires the stored snapshot for (path, expected) to equal live text before taking the no-drift path. On collision it falls through to Recovery, whose line-precise 3-way merge fails cleanly on colliding-but-different content and raises MismatchError. When no snapshot is retained for the tag, behavior is unchanged (external mint / aged out).

- #assertSeenLines now consults the exact matched snapshot, so seen-line validation cannot read a collider's provenance.

Regression tests added to both snapshots.test.ts and patcher.test.ts covering the concrete `1D84` pair from the reporter.

Fixes #4075
2026-07-01 07:08:13 +00:00
roboomp d7c6472824 fix(edit): preserved hashline utf-8 bom
Added binary BOM detection to the hashline patcher and filesystem adapters so edits restore BOM bytes when text decoding hides U+FEFF.

Fixes #3867
2026-06-30 05:48:37 +00:00
can1357 55b02a568d feat(coding-agent): improved local artifact sandbox handling and path recovery
- Enabled tag-based path recovery for artifacts within the session `local://` sandbox.
- Restructured `hashline` path recovery logic to ensure preflight validation runs on resolved paths.
- Prevented ACP editor buffer routing for session `local://` sandbox artifacts.
- Added comprehensive tests covering sandbox target identification and recovery for bare file names.
2026-06-26 16:39:48 +02:00
can1357 c46216742b feat(hashline): implemented tag-based patch path recovery
- Automatically rebind edits to the correct file when an authored path does not exist but the filename and snapshot tag uniquely match a file read earlier in the session.
- Prevent path recovery for paths that would escalate write privileges, ensuring compatibility with read-only internal URL targets.
- Surface warning messages to the model and user upon successful path recovery to encourage correct future path usage.
2026-06-25 13:14:05 +02:00
can1357 8778554426 ux(hashline): improved unseen lines error message with specific read instruction
- Clarified the message instructing users or models on how to handle unseen lines.
- Added specific instructions recommending a ranged read to bypass summarization and mint a fresh tag.
2026-06-17 23:45:47 +02:00
can1357 d6c51fe69a feat: hashline .= as seperator 2026-06-15 10:46:45 +02:00
can1357 ba82ed6e59 test: update hashline tests 2026-06-15 07:47:54 +02:00
can1357 907bc9979e feat: renamed opcodes to simplified variants
- Renamed line and block patch op verbs to XCHG, DEL, and INS in parsing and formatting.
- Updated grammar and tokenizer to support XCHG.BLK, DEL.BLK, and INS.PRE/POST/HEAD/TAIL forms.
- Updated diagnostics, docs, prompts, tests, and changelog to use XCHG/DEL/INS-based operators.
- Expanded session-stats parsing to normalize legacy op aliases to compact IDs.
2026-06-15 07:33:24 +02:00
can1357 a655953e7a fix(hashline): hardened hashline editing with seen-line and block-anchor validation
- Tracked seen-line provenance in snapshots and propagated it from read/search/ast-grep rows.
- Rejected hashline edits on unseen lines before patching, throwing unseen-line errors.
- Rejected single-line block anchors in strict mode and dropped them in unresolved lenient mode.
- Trimmed one-sided keeper-echo duplicates during multi-line replacements with warning output.
2026-06-15 04:25:33 +02:00
can1357 860eef3f2f refactor(hashline): switched header syntax to bracketed [path#tag]
- Replaced `¶path#hash` prefix with `[path#hash]` delimiters across parser, tokenizer, and grammar.
- Updated prompts, docs, and recovery paths to the new bracketed form.
2026-06-06 15:38:08 +02:00
can1357 32f07833fe feat(hashline): made snapshot tag mandatory and added head/tail drift warning
- Made `#TAG` required on every file section; removed the hashless-header grammar form.
- Head/tail inserts with a stale tag now apply and emit `HEADTAIL_DRIFT_WARNING` instead of hard-failing.
- Anchored edits and missing files now surface write-tool guidance in error messages.
- Updated prompt docs to document no-hashless-form rule and stale-tag recovery guidance.
2026-05-30 12:37:17 +02:00
can1357 01c34db450 feat(hashline): added full-file hash snapshots with 4-hex tags
- Replaced snapshot internals with full-file records and removed contiguous/sparse snapshot APIs.
- Added file-hash normalization, computed `computeFileHash`, and updated grammar/messages to 4-hex tags.
- Simplified recovery by checking whole-file hashes first, then applying merge-replay fallback after mismatches.
- Updated coding-agent tools to use `record`/`recordFileSnapshot` and skip hash headers for unsnapshotted large files.
- Expanded patcher and snapshot tests to verify 4-hex anchors, hash deduplication, and cache-capped behavior.
2026-05-29 18:12:08 +02:00
can1357 2d7cd6d2de feat(hashline)!: migrated to verb-based v4 syntax for edits
- Replaced bare `A B` range headers with `replace N..M:`, `delete N..M`, `insert before N:`, `insert after N:`, `insert head:`, and `insert tail:`.
- Removed `&A..B` repeat rows; insert-before/after ops now express the same intent explicitly.
- Empty replace bodies now error instead of deleting; `delete` is the canonical deletion op.
- Updated grammar, prompt, docs, tests, and all call sites to the new syntax.
2026-05-29 17:36:17 +02:00
can1357 037bf15b53 feat(hashline): distinguished mismatch diagnostics for drifted vs unrecognized hashes
- Added a `hashRecognized` field to `MismatchDetails` and `MismatchError`, defaulting it to `true` for compatibility.
- Updated stale mismatch rejection messaging to distinguish drifted hashes from session-absent hashes with explicit guidance.
- Propagated `hashRecognized: snapshot !== null` in `Patcher` and added tests for both mismatch branches.
2026-05-29 06:42:47 +02:00
can1357 7dd00c015b feat: hashline improvements for spark
- Redesigned hashline patch syntax from anchor-based (`A-B:`) to hunk-header format (`@@ A..B @@`) with unified-diff compatibility.
- Removed `autoDropPureInsertDuplicates` option and simplified apply behavior to preserve duplicated boundary and context lines.
- Changed repeat operator from `^A-B` to `&A..B` and range separator from `-` to `..` for consistency with hunk-header syntax.
- Added image resizing and dimension notes to eval tool output; improved write tool hashline header sanitation for legacy formats.
- Removed 521 lines of boundary-duplicate absorption code and simplified parser to auto-convert bare body rows and unified-diff contamination.
2026-05-28 03:06:52 +02:00
can1357 7c64576524 feat(hashline): replaced file-hash anchors with opaque snapshot-store tags
- Replaced 4-hex content-derived file hashes with 3-hex opaque tags minted by InMemorySnapshotStore, making tags session-bound pointers rather than content fingerprints.
- Removed lru-cache dependency; replaced LRU-bounded per-path rings with a flat 4096-slot global ring using a scrambled permutation to prevent LLM tag extrapolation.
- Made SnapshotStore required in Patcher (was optional); tag resolution now drives stale-anchor detection instead of recomputing hashes at apply time.
- Changed literal payload sigil from `|` to `+` and accepted `^A` shorthand for `^A-A`; added lenient recovery for bare bodies, lone `-` rows, and overlapping bare/concrete block pairs.
2026-05-28 01:00:23 +02:00