- 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.
- 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.
- Added the `enforceSeenLines` option to hashline `PatcherOptions` (defaults `true`); the seen-line guard in `Patcher` now runs only when enabled.
- Added the `edit.enforceSeenLines` coding-agent setting (default off) and wired it through `edit/hashline/execute.ts` into the `Patcher`.
- Stopped `file-snapshot-store` excluding column-clipped (>512-char) lines from a snapshot's seen set, so single-line edits on long lines apply without a full-width re-read.
- Updated `seen-line-guard` tests and the hashline/coding-agent changelogs.
- Introduced an automated retry recovery system to track, manage, and persist recovered error states within agent sessions.
- Enabled compact transcript rendering for recovered auto-retry errors by removing heuristic commit machinery.
- Improved raw read tracking and provenance in the ReadTool to support refined file snapshot recording and hashline editing.
- Excluded recovered assistant messages from default model context and updated event controllers to handle retry recovery life cycles.
Codex reviewer flagged that the ranged-read fallback recommended by
the patcher's over-cap reveal path (`path:N-M`) itself applies the
512-column cap and still records the displayed line numbers via
`recordSeenLinesFromBody`. On a minified wide anchor line, `read
file:N` shows only the clipped prefix but the line lands in the
tag's seenLines — a subsequent edit anchored at N then slips past
the seen-line guard.
- `packages/coding-agent/src/edit/file-snapshot-store.ts`:
`recordSeenLinesFromBody` grows an optional `excludedLines` set;
parsed line numbers matching it are filtered before recording.
- `packages/coding-agent/src/tools/read.ts`:
`#readLocalFileMultiRange` and the single-range disk path build a
`clippedLines` set alongside `columnTruncated` for both direct-range
lines and `buildLineEntriesWithBlockContext` context lines, then
pass it into `recordSeenLinesFromBody`.
- `packages/coding-agent/src/tools/grep.ts`:
same wiring for the match line via `match.truncated`, plus a
conservative length+`...`-marker heuristic for context lines
(native `crates/pi-natives/src/grep.rs` `truncate_line` doesn't
propagate a per-line flag on `contextBefore`/`contextAfter`; a
proper native-side flag is a follow-up).
- `packages/coding-agent/test/edit/seen-line-guard.test.ts`:
new case reads a 4KB single line and asserts the clipped line
number stays out of `seenLines` and the edit against it still
rejects with the seen-line guard.
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
- Replaced `grep`, `glob`, and `ast_grep` `paths` inputs with optional single `path` strings while preserving default workspace-root behavior.
- Added shared `toPathList` normalization for legacy arrays and JSON-encoded arrays across tool execution and TUI renderers.
- Updated prompts, fixtures, shims, transcript summaries, and tests to send and display the new `path` argument.
- Updated collab-web search tool cards to read `path` while falling back to legacy `paths` for historical transcripts.
- Recorded the contiguous coding-agent changelog run for the tool-path breaking change and adjacent TTS entries.
PR #3648 review (codex): the first iteration emitted every envelope
path alongside one combined digest, so a multi-file payload that added
`: any` to a README.md hunk and merely touched src/ok.ts would surface
a *.ts path in the TTSR match context and trip the bundled
tool:edit(*.ts) ts-no-any rule on text that belonged to the Markdown
hunk — aborting valid edits under interruptMode:always.
Add a per-file matcherEntries(args) hook on AgentTool / EditStreaming-
Strategy returning [{ path, digest }] entries, one per touched file
(same-path sections/hunks merged):
- replace / patch: one entry from the top-level path + matcherDigest
- hashline: regex-split by [path#TAG] section, body added-lines per
entry (tolerant of streaming partial payloads)
- apply_patch: expandApplyPatchToPreviewEntries grouped by path
AgentSession.#checkTtsrStream / #checkTtsrAstStream now prefer
matcherEntries and iterate per-file with isolated filePaths + streamKey,
so each file's buffer and repeat-tracking are independent. Tools
without matcherEntries keep the existing combined matcherDigest +
matcherPaths path.
AgentSession's TTSR match context only scanned top-level path/paths
arguments, so hashline and apply_patch edit streams (whose only target
path lives inside the wire payload — section headers or envelope
markers — not as a top-level argument) arrived without any filePaths
and silently skipped path-scoped rules like the bundled ts-no-any
(scope: tool:edit(*.ts)).
Add an optional AgentTool.matcherPaths(args) hook, companion to the
existing matcherDigest(args), so tools whose wire grammar embeds paths
can surface them. Implement on each edit streaming strategy:
- replace / patch: top-level path
- hashline: parse [path#TAG] (and tag-less [path]) section headers
tolerant of streaming partial payloads
- apply_patch: parse *** Add/Update/Delete File: markers, also tolerant
of pre-End-Patch buffers
AgentSession.#getTtsrToolMatchContext consults tool.matcherPaths first,
normalising its output through the existing path-candidate helper, and
falls back to the generic top-level argument scan for tools that don't
implement it.
Fixes#3646
- Renamed the `find` and `search` tools to `glob` and `grep` respectively across the codebase to improve command clarity.
- Implemented full-stack support for the renamed tools, including CLI arguments, system prompts, SDK exports, and tool registration.
- Added automated migration logic in `settings` to transform legacy `find` and `search` configuration keys to their new equivalents.
- Updated the `collab-web` renderer registry to ensure backwards compatibility with legacy tool outputs.
Migrate 203 test files (356 call sites) from fs.rm/fs.rmSync to
removeWithRetries/removeSyncWithRetries to reduce EBUSY test failures
on Windows. removeWithRetries is now exported from @oh-my-pi/pi-utils.
The migration uses a regex-based approach that:
- Replaces fs.rm(path, { recursive, force }) → removeWithRetries(path)
- Replaces fs.rmSync(path, { recursive, force }) → removeSyncWithRetries(path)
- Replaces fs.rm(path) → removeWithRetries(path) (no options)
- Skips fs.rm/fs.rmSync inside template literals (bun --eval scripts)
- Adds imports to existing @oh-my-pi/pi-utils import or creates new one
- Removes unused fs imports where fs.rm was the only fs usage (4 files)
- Standardized elision markers across all tool outputs and filters to use cohesive `[...N [type] elided...]`, `[...Nln elided...]`, and `[...xB elided...]` syntax.
- Updated documentation, prompts, and test expectations to reflect the unified elision format.
- Improved transcript viewer robustness by preventing content aliasing through path-inclusive signature hashing.
- Added logic to clear stale transcript content when associated session files are deleted, accompanied by verifying test cases.
- Migrated all wire protocol, schema definitions, and tools validation from Zod to ArkType across multiple packages.
- Updated extension runtimes, custom tools loader, and TypeBox compatibility shim to expose and use ArkType instances.
- Added a comprehensive ArkType migration guide, validation parity tests, and helper utilities.
- Removed redundant PDF asset routing and parsing implementations from the read tool.
Merge displayed bridge-backed range and multi-range read lines into the existing hashline snapshot provenance so INS.POST anchors pass visible-line validation after ACP reads.
Fixes#2773
- 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.