From 87a3b6ca03471e17cfe07eff9d2a2318177ded13 Mon Sep 17 00:00:00 2001 From: can1357 Date: Tue, 7 Apr 2026 05:50:54 +0200 Subject: [PATCH] refactor(patch): restructured chunk edit schema to use explicit op enum and anchor field - Refactored chunk edit schema to use explicit `op` enum field (replace, delete, append, prepend, after, before) instead of multiple boolean/string flags. - Renamed chunk edit parameters from `after`/`before` field names to `anchor` for sibling-relative insert operations. - Simplified chunk edit validation logic by consolidating nested conditionals into switch statement on operation type. - Fixed `log_experiment` to correctly identify run-modified files by removing pre-run dirty path filtering. - Simplified chunk edit and read prompt documentation to emphasize read-first workflow with consolidated examples. --- packages/coding-agent/CHANGELOG.md | 10 ++ .../src/autoresearch/tools/log-experiment.ts | 8 +- packages/coding-agent/src/patch/index.ts | 169 ++++++++---------- packages/coding-agent/src/patch/shared.ts | 38 ++-- .../src/prompts/tools/chunk-edit.md | 143 +++------------ .../src/prompts/tools/read-chunk.md | 53 +----- .../test/autoresearch-tools.test.ts | 95 ++++++++++ .../test/tools/chunk-mode.test.ts | 8 +- 8 files changed, 235 insertions(+), 289 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 7ed60a5f4..49d26f3d5 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -21,6 +21,15 @@ ### Changed +- Simplified chunk edit schema to use explicit `op` field (replace, delete, append, prepend, after, before) instead of separate boolean flags for improved clarity +- Updated chunk edit prompt documentation to consolidate operation guidance with streamlined rules, ops reference table, and simplified examples +- Refactored chunk edit preview formatting to use `op` field and `anchor` parameter for sibling-relative inserts (after/before operations) +- Renamed chunk edit parameter from `after`/`before` field names to use `anchor` field for specifying named children in sibling-relative insert operations +- Simplified chunk read and edit prompt documentation to reduce verbosity and focus on essential guidance +- Refactored chunk edit schema to use explicit `op` field (replace, delete, append, prepend, after, before) instead of boolean flags for improved clarity and consistency +- Updated chunk edit prompt documentation to simplify operation guidance with consolidated rules, ops reference, and streamlined examples +- Changed chunk edit preview formatting to use `op` field and `anchor` parameter for sibling-relative inserts (after/before operations) +- Renamed chunk edit parameter from `after`/`before` field names to use `anchor` field for specifying named children in sibling-relative insert operations - Updated streaming edit preview to display chunk edits alongside hashline edits with operation-specific formatting - Improved chunk edit preview labels to show operation type (delete, append, prepend, insert, replace) with target and line ranges - Simplified chunk read path parsing to extract only the selector string without redundant CRC field @@ -72,6 +81,7 @@ ### Fixed +- Fixed `log_experiment` to correctly identify run-modified files by removing pre-run dirty path filtering, ensuring all uncommitted changes are properly validated - Fixed chunk edit operations to extract and use embedded CRC checksums when models write selectors like `fn_foo#ABCD` instead of separate `sel` and `crc` fields - Fixed shell command error handling to properly check for errors before processing chunks in bash executor and config resolution - Fixed `log_experiment` to correctly identify and revert only files modified by the run, leaving pre-existing uncommitted changes intact diff --git a/packages/coding-agent/src/autoresearch/tools/log-experiment.ts b/packages/coding-agent/src/autoresearch/tools/log-experiment.ts index 5b894b116..18cd682bb 100644 --- a/packages/coding-agent/src/autoresearch/tools/log-experiment.ts +++ b/packages/coding-agent/src/autoresearch/tools/log-experiment.ts @@ -209,7 +209,7 @@ export function createLogExperimentTool( const preRunDirtyPaths = pendingRun.preRunDirtyPaths; let keepScopeValidation: { committablePaths: string[] } | undefined; if (params.status === "keep") { - const scopeValidation = await validateKeepPaths(options, workDir, state, preRunDirtyPaths); + const scopeValidation = await validateKeepPaths(options, workDir, state); if (typeof scopeValidation === "string") { return { content: [{ type: "text", text: `Error: ${scopeValidation}` }], @@ -605,7 +605,6 @@ async function validateKeepPaths( options: AutoresearchToolFactoryOptions, workDir: string, state: ExperimentState, - preRunDirtyPaths: string[], ): Promise<{ committablePaths: string[] } | string> { if (state.scopePaths.length === 0) { return "Files in Scope is empty for the current segment. Re-run init_experiment after fixing autoresearch.md."; @@ -624,7 +623,6 @@ async function validateKeepPaths( } const workDirPrefix = await readGitWorkDirPrefix(options, workDir); - const preRunSet = new Set(preRunDirtyPaths); const committablePaths: string[] = []; for (const entry of parseWorkDirDirtyPathsWithStatus(statusText, workDirPrefix)) { if (isAutoresearchLocalStatePath(entry.path)) { @@ -634,10 +632,6 @@ async function validateKeepPaths( committablePaths.push(entry.path); continue; } - // Skip files that were already dirty before the run - if (preRunSet.has(entry.path)) { - continue; - } if (state.offLimits.some(spec => pathMatchesContractPath(entry.path, spec))) { return `cannot keep this run because ${entry.path} is listed under Off Limits in autoresearch.md`; } diff --git a/packages/coding-agent/src/patch/index.ts b/packages/coding-agent/src/patch/index.ts index 02748c2e3..460971e9e 100644 --- a/packages/coding-agent/src/patch/index.ts +++ b/packages/coding-agent/src/patch/index.ts @@ -102,49 +102,30 @@ const patchEditSchema = Type.Object({ diff: Type.Optional(Type.String({ description: "Diff hunks (update) or full content (create)" })), }); -const pTarget = { - target: Type.String({ description: "Chunk selector path with optional #CRC suffix copied from read output" }), -} as const; +const CHUNK_OP_VALUES = ["replace", "delete", "append", "prepend", "after", "before"] as const; -const pContent = { - content: Type.Optional( - Type.Union([Type.String(), Type.Array(Type.String())], { description: "Replacement content" }), - ), -} as const; - -const pDelete = { - delete: Type.Optional(Type.Boolean({ description: "Delete the target chunk when true" })), -} as const; - -const pPosition = { - append: Type.Optional(Type.Boolean({ description: "Insert content as the last child of target" })), - prepend: Type.Optional(Type.Boolean({ description: "Insert content as the first child of target" })), - after: Type.Optional(Type.String({ description: "Insert content after this named child within target" })), - before: Type.Optional(Type.String({ description: "Insert content before this named child within target" })), -} as const; - -const pRangesOpt = { - line: Type.Optional( - Type.Integer({ +const chunkToolEditSchema = Type.Object({ + target: Type.String({ + description: + "Chunk path from read output, with #CRC suffix for replace/delete (e.g. 'class_X.fn_y#A14F'). Use parent path without #CRC for insert ops.", + }), + op: Type.Optional( + StringEnum(CHUNK_OP_VALUES, { description: - "Start line for line-scoped replace (1-indexed file line from read gutter). Omit for whole-chunk replace.", + "Edit op (default: replace). 'delete' removes target. 'append'/'prepend' insert as last/first child. 'after'/'before' insert at sibling position; require 'anchor'.", }), ), - end_line: Type.Optional( - Type.Integer({ description: "End line for line-scoped replace. Omit for single-line edit." }), + content: Type.Optional( + Type.String({ description: "New content (required for replace/append/prepend/after/before)." }), ), -} as const; - -const chunkToolEditSchema = Type.Object( - { - ...pTarget, - ...pContent, - ...pDelete, - ...pPosition, - ...pRangesOpt, - }, - { additionalProperties: false }, -); + line: Type.Optional( + Type.Integer({ description: "Absolute file line for line-scoped replace (omit for whole-chunk replace)." }), + ), + end_line: Type.Optional(Type.Integer({ description: "End line (inclusive) for replacing a line range." })), + anchor: Type.Optional( + Type.String({ description: "Named child to insert relative to (required for op=after/before)." }), + ), +}); const chunkEditParamsSchema = Type.Object( { @@ -584,75 +565,75 @@ export class EditTool implements AgentTool { for (const edit of edits) { const { selector, crc } = parseChunkTarget(edit.target); + const op = edit.op ?? "replace"; const hasContent = edit.content !== undefined; - const positionModes = - Number(Boolean(edit.append)) + - Number(Boolean(edit.prepend)) + - Number(edit.after !== undefined) + - Number(edit.before !== undefined); + const content = flattenContent(edit.content); - if (positionModes > 1) { - throw new Error(`Edit target ${describeChunkTarget(selector)} must choose only one insertion mode.`); - } + switch (op) { + case "delete": { + if (hasContent || edit.line !== undefined || edit.end_line !== undefined) { + throw new Error( + `Delete edit on ${describeChunkTarget(selector)} cannot include content or line ranges.`, + ); + } + normalizedOperations.push({ + op: "delete", + sel: selector, + crc: await assertChecksum("delete", crc, selector), + }); + break; + } - if (edit.delete) { - if (hasContent || edit.line !== undefined || edit.end_line !== undefined || positionModes > 0) { - throw new Error( - `Delete edit on ${describeChunkTarget(selector)} cannot include content, line ranges, or insert positions.`, - ); - } - normalizedOperations.push({ - op: "delete", - sel: selector, - crc: await assertChecksum("delete", crc, selector), - }); - continue; - } - - if (positionModes > 0) { - if (edit.line !== undefined || edit.end_line !== undefined) { - throw new Error(`Insert edit on ${describeChunkTarget(selector)} cannot include line ranges.`); - } - if (!hasContent) { - throw new Error(`Content required for insert edit on ${describeChunkTarget(selector)}.`); - } - const content = flattenContent(edit.content); - if (edit.append) { + case "append": { + if (!hasContent) throw new Error(`Content required for append on ${describeChunkTarget(selector)}.`); normalizedOperations.push({ op: "append_child", sel: selector, content }); - continue; + break; } - if (edit.prepend) { + case "prepend": { + if (!hasContent) throw new Error(`Content required for prepend on ${describeChunkTarget(selector)}.`); normalizedOperations.push({ op: "prepend_child", sel: selector, content }); - continue; + break; } - if (edit.after !== undefined) { + + case "after": { + if (!hasContent) + throw new Error(`Content required for after-insert on ${describeChunkTarget(selector)}.`); + if (!edit.anchor) + throw new Error(`'anchor' required for op=after on ${describeChunkTarget(selector)}.`); normalizedOperations.push({ op: "append_sibling", - sel: joinChunkPath(selector, edit.after), + sel: joinChunkPath(selector, edit.anchor), content, }); - continue; + break; + } + case "before": { + if (!hasContent) + throw new Error(`Content required for before-insert on ${describeChunkTarget(selector)}.`); + if (!edit.anchor) + throw new Error(`'anchor' required for op=before on ${describeChunkTarget(selector)}.`); + normalizedOperations.push({ + op: "prepend_sibling", + sel: joinChunkPath(selector, edit.anchor), + content, + }); + break; + } + default: { + if (!hasContent) { + throw new Error(`Content required for replace edit on ${describeChunkTarget(selector)}.`); + } + normalizedOperations.push({ + op: "replace", + sel: selector, + crc: await assertChecksum("replace", crc, selector), + content, + line: edit.line, + endLine: edit.end_line, + }); + break; } - normalizedOperations.push({ - op: "prepend_sibling", - sel: joinChunkPath(selector, edit.before ?? ""), - content, - }); - continue; } - - if (!hasContent) { - throw new Error(`Content required for replace edit on ${describeChunkTarget(selector)}.`); - } - - normalizedOperations.push({ - op: "replace", - sel: selector, - crc: await assertChecksum("replace", crc, selector), - content: flattenContent(edit.content), - line: edit.line, - endLine: edit.end_line, - }); } const chunkResult = applyChunkEdits({ diff --git a/packages/coding-agent/src/patch/shared.ts b/packages/coding-agent/src/patch/shared.ts index 29653a261..c16106714 100644 --- a/packages/coding-agent/src/patch/shared.ts +++ b/packages/coding-agent/src/patch/shared.ts @@ -198,27 +198,27 @@ function formatStreamingHashlineEdits(edits: Partial +- **MUST** `read` first. NEVER invent chunk names or CRCs — copy them from the latest read output or edit response. +- `target` **MUST** be the **fully-qualified** path (e.g. `class_X.fn_y.if_2`, not `if_2`), ending with `#CRC` for replace/delete. +- Prefer `line`/`end_line` (absolute file line numbers from the read gutter) for small fixes over whole-chunk replace. +- `content` must include the destination block's inner indentation. +- Successful edits return refreshed anchors — use them for follow-ups, don't re-read just for new CRCs. + -Successful edit responses include the updated chunk tree with checksums. Do not re-read just to refresh checksums unless the file changed externally. + +|op|fields|effect| +|---|---|---| +|`replace` (default)|`target#CRC`, `content`, opt. `line`/`end_line`|rewrite chunk or a line range within it| +|`delete`|`target#CRC`|remove chunk| +|`append` / `prepend`|`target`, `content`|insert as last/first child of target| +|`after` / `before`|`target`, `anchor` (child name), `content`|insert at sibling position| -**Checksum scope:** Each chunk has its own CRC over its source span. Editing non-overlapping lines elsewhere does not change unrelated chunks' checksums. +For file-root edits, `target` is the file CRC alone (e.g. `"#VSKB"`). + - -**Choosing the right edit shape:** -- To rewrite an entire chunk → `{ "target": "chunk#CRC", "content": "…" }` -- To fix a single line → add `"line": 13` -- To fix a contiguous range → add `"line": 13, "end_line": 17` -- To delete a chunk → `{ "target": "chunk#CRC", "delete": true }` -- To append/prepend inside a container → add `"append": true` or `"prepend": true` -- To insert relative to a named child → use `"after": "child_name"` or `"before": "child_name"` on the parent target - -|shape|effect| -|---|---| -|`{ "target": "chunk#CRC", "content": "…" }`|replace the target chunk| -|`{ "target": "chunk#CRC", "line": 13, "content": "…" }`|replace one line within the target chunk| -|`{ "target": "chunk#CRC", "line": 13, "end_line": 17, "content": "…" }`|replace an inclusive line range within the target chunk| -|`{ "target": "chunk#CRC", "delete": true }`|delete the target chunk| -|`{ "target": "chunk", "append": true, "content": "…" }`|append as last child of the target chunk| -|`{ "target": "chunk", "prepend": true, "content": "…" }`|prepend as first child of the target chunk| -|`{ "target": "parent", "after": "child", "content": "…" }`|insert after the named child within `parent`| -|`{ "target": "parent", "before": "child", "content": "…" }`|insert before the named child within `parent`| -- `line`/`end_line` are **absolute file line numbers** from the `read` gutter. `line` alone = single line. `line` + `end_line` = inclusive range. `line` with `end_line` = `line`-1 = zero-width insert. -- `path` is always just the file path. Do not embed `:chunk` selectors in `path`. -- `target` is the chunk path, optionally followed by `#CRC` copied from the anchor. Example: `"class_Server.fn_start#HTST"`. -- Insert edits usually omit the checksum because they do not rewrite the target chunk itself. -- For file-root replace/delete, use the file header checksum as `"target": "#VSKB"`. -- Content must already include the intended inner indentation for the destination block. -- Batch edits observe earlier edits. If edit 1 changes checksum/span/path, edit 2 must use the post-edit anchor values. -- `replace`/`delete` include leading comments/attributes attached to the chunk. - - - -All examples reference this `read` output: + +Given read output: ``` | server.ts·40L·ts·#VSKB - 5| class Server { - | {{anchor "class_Server" "XKQZ"}} 12| start(): void { | {{anchor "fn_start" "HTST"}} 13| log("booting on " + this.port); -14| for (let i = 0; i < MAX_RETRIES; i++) { -15| this.tryBind(); -16| } -17| } -19| private tryBind(): boolean { - | {{anchor "fn_tryBind" "VNWR"}} -20| // TODO: add backoff -21| return bind(this.port); -22| } +14| this.tryBind(); +15| } ``` - -``` -"path": "server.ts", -"edits": [ - { - "target": "{{sel "class_Server.fn_start"}}#HTST", - "content": "start(): void {\n log(\"starting\");\n this.tryBind();\n}" - } -] +Fix the typo on line 13: +```json +{"path":"server.ts","edits":[{"target":"{{sel "class_Server.fn_start"}}#HTST","line":13,"content":" warn(\"booting on \" + this.port);"}]} ``` - - -``` -"path": "server.ts", -"edits": [ - { - "target": "{{sel "class_Server.fn_start"}}#HTST", - "line": 13, - "content": " warn(\"booting on \" + this.port);" - } -] -``` - - - -``` -"path": "server.ts", -"edits": [ - { - "target": "{{sel "class_Server.fn_tryBind"}}#VNWR", - "delete": true - } -] -``` - - - -``` -"path": "server.ts", -"edits": [ - { - "target": "{{sel "class_Server"}}", - "append": true, - "content": "status(): string {\n return \"ok\";\n}" - } -] -``` - - - -``` -"path": "server.ts", -"edits": [ - { - "target": "{{sel "class_Server"}}", - "after": "fn_start", - "content": "reset(): void {\n this.port = 0;\n}" - } -] -``` - - - - -- **MUST** include `path` in every edit call. -- **MUST** read the latest chunk output before editing. -- **MUST** include a `#CRC` suffix in `target` for every replace or delete edit. -- **MUST** use updated chunk output from the edit response for follow-up edits. -- **MUST** use the smallest correct target; do not rewrite siblings unnecessarily. -- **MUST** use the **fully qualified** chunk path. Copy the complete path shown in read output — e.g. `fn_handleTerraform.try.if_2`, never just `if_2`. Short/leaf names are ambiguous. -- For line-scoped replace edits, use file line numbers from the `read` gutter. -- Multiple line-scoped edits on the same chunk in one batch are fine — the engine auto-updates the checksum between operations. - - diff --git a/packages/coding-agent/src/prompts/tools/read-chunk.md b/packages/coding-agent/src/prompts/tools/read-chunk.md index a77509c2c..2522bcf76 100644 --- a/packages/coding-agent/src/prompts/tools/read-chunk.md +++ b/packages/coding-agent/src/prompts/tools/read-chunk.md @@ -1,55 +1,16 @@ Reads files using syntax-aware chunks. -## Parameters -- `path` -- file path or URL; may include `:selector` suffix as an alternative to `sel` -- `sel` -- optional selector (see table below) -- `timeout` -- seconds, for URLs only +- `path` — file path or URL; may include `:selector` suffix +- `sel` — optional selector: `class_Foo`, `class_Foo.fn_bar`, `L50`, `L50-L120`, or `raw` +- `timeout` — seconds, for URLs only -## Selectors +Each anchor `[name#CCCC]` in the output is a chunk ID. Copy `name#CCCC` into the edit tool's `target` field. +Line numbers in the gutter are absolute — use them for `line`/`end_line` in edits. -|Input|Behavior| -|---|---| -|_(omitted)_|Render the file root chunk| -|`class_Foo`|Read a chunk by path| -|`class_Foo.fn_bar`|Read a nested chunk path| -|`L50` or `L50-L120`|Absolute file line range| -|`raw`|Read full raw file content (no chunk rendering)| - -Each anchor line shows `[name#CCCC]` — `#CCCC` is the edit checksum. Copy it when editing with `chunk-edit`. - -If `path:chunk` and `sel` are both provided, `sel` wins. Missing chunk paths return `[Chunk not found]`. - -Code rows use **absolute file line numbers** in the gutter. `chunk-edit` `line`/`end_line` use those same numbers. - -## Examples - -`read(path="src/math.ts")` - -```text - | src/math.ts·120L·ts·#A744 - | - 5| export function sum(values: readonly number[]): number { - | {{anchor "fn_sum" "3286"}} - 6| return values.reduce((total, value) => total + value, 0); - 7| } -10| export class Calculator { - | {{anchor "class_Calculator" "5D36"}} -11| multiply(left: number, right: number): number { - | {{anchor "fn_multiply" "B592"}} -12| return left * right; -13| } -14| } -``` - -## Language Support - -Chunk trees: JavaScript, TypeScript, TSX, Python, Rust, Go. Others use blank-line fallback. +Chunk trees: JS, TS, TSX, Python, Rust, Go. Others use blank-line fallback. -- **MUST** use `read` instead of shell commands for file reading. -- **MUST** copy the current checksum before editing a chunk with `chunk-edit`. -- **MUST NOT** assume chunk names; always read the current output first. +- **MUST** `read` before editing — never invent chunk names or CRCs. - diff --git a/packages/coding-agent/test/autoresearch-tools.test.ts b/packages/coding-agent/test/autoresearch-tools.test.ts index 97761f9e1..e1699cb5f 100644 --- a/packages/coding-agent/test/autoresearch-tools.test.ts +++ b/packages/coding-agent/test/autoresearch-tools.test.ts @@ -621,6 +621,101 @@ describe("autoresearch tools", () => { expect(runJson.status).toBe("keep"); }); + it("commits in-scope changes when those paths were already dirty before the run", async () => { + const dir = makeTempDir(); + tempDirs.push(dir); + writeAutoresearchWorkspace(dir, { + contract: [ + "# Autoresearch", + "", + "## Benchmark", + "- command: bash autoresearch.sh", + "- primary metric: runtime_ms", + "- metric unit: ms", + "- direction: lower", + "", + "## Files in Scope", + "- src/in-scope.ts", + "", + "## Off Limits", + "", + "## Constraints", + "- keep behavior stable", + "", + ].join("\n"), + }); + fs.mkdirSync(path.join(dir, "src"), { recursive: true }); + fs.writeFileSync(path.join(dir, "src", "in-scope.ts"), "export const value = 1;\n"); + + await $`git init`.cwd(dir).quiet(); + await $`git config user.email test@example.com`.cwd(dir).quiet(); + await $`git config user.name Test User`.cwd(dir).quiet(); + await $`git add .`.cwd(dir).quiet(); + await $`git commit -m initial`.cwd(dir).quiet(); + await $`git checkout -b autoresearch/test-keep-prerun-dirty`.cwd(dir).quiet(); + + fs.writeFileSync(path.join(dir, "src", "in-scope.ts"), "export const value = 3;\n"); + fs.writeFileSync(path.join(dir, "autoresearch.program.md"), "# Strategy\n\n- focus on in-scope edits\n"); + await Bun.write( + path.join(dir, ".autoresearch", "runs", "0001", "run.json"), + JSON.stringify({ + command: "bash autoresearch.sh", + exitCode: 0, + parsedMetrics: { runtime_ms: 9 }, + parsedPrimary: 9, + runNumber: 1, + }), + ); + + const runtime = createSessionRuntime(); + runtime.state.metricName = "runtime_ms"; + runtime.state.metricUnit = "ms"; + runtime.state.scopePaths = ["src/in-scope.ts"]; + runtime.state.constraints = ["keep behavior stable"]; + const runDirectory = path.join(dir, ".autoresearch", "runs", "0001"); + runtime.lastRunArtifactDir = runDirectory; + runtime.lastRunNumber = 1; + runtime.lastRunDuration = 1.2; + runtime.lastRunSummary = { + checksDurationSeconds: 0, + checksPass: null, + checksTimedOut: false, + command: "bash autoresearch.sh", + durationSeconds: 1.2, + parsedAsi: null, + parsedMetrics: { runtime_ms: 9 }, + parsedPrimary: 9, + passed: true, + preRunDirtyPaths: ["src/in-scope.ts"], + runDirectory, + runNumber: 1, + }; + + const tool = createLogExperimentTool({ + dashboard: createDashboardStub(), + getRuntime: () => runtime, + pi: createGitApi(), + }); + const result = await tool.execute( + "call-keep-prerun-dirty", + { + commit: "initial", + metric: 9, + status: "keep", + description: "Improve in scope after prior WIP", + asi: { hypothesis: "refine further" }, + }, + undefined, + undefined, + createContext(dir), + ); + + expect(firstTextBlockText(result.content)).toContain("Logged run #1: keep"); + const committedPaths = await $`git show --name-only --pretty=format: HEAD`.cwd(dir).text(); + expect(committedPaths).toContain("src/in-scope.ts"); + expect(committedPaths).toContain("autoresearch.program.md"); + }); + it("rejects keep when an out-of-scope file is dirty", async () => { const dir = makeTempDir(); tempDirs.push(dir); diff --git a/packages/coding-agent/test/tools/chunk-mode.test.ts b/packages/coding-agent/test/tools/chunk-mode.test.ts index 979d56141..5a84e8e2e 100644 --- a/packages/coding-agent/test/tools/chunk-mode.test.ts +++ b/packages/coding-agent/test/tools/chunk-mode.test.ts @@ -296,7 +296,7 @@ describe("chunk mode tools", () => { await editTool.execute("chunk-edit-string-content", { path: filePath, - edits: [{ target: "class_Server", append: true, content: 'status(): string {\n return "ok";\n}\n' }], + edits: [{ target: "class_Server", op: "append", content: 'status(): string {\n return "ok";\n}\n' }], } as never); const updatedSource = await Bun.file(filePath).text(); @@ -415,8 +415,8 @@ describe("chunk mode tools", () => { editTool.execute("chunk-edit-batch-rollback", { path: filePath, edits: [ - { target: "class_Server", append: true, content: ' status(): string {\n return "ok";\n }' }, - { target: "class_Server.fn_handleError#ZZZZ", delete: true }, + { target: "class_Server", op: "append", content: ' status(): string {\n return "ok";\n }' }, + { target: "class_Server.fn_handleError#ZZZZ", op: "delete" }, ], }), ).rejects.toThrow(/No changes were saved/); @@ -491,7 +491,7 @@ describe("chunk mode tools", () => { line: 3, end_line: 3, content: " let total = 1;", - } as never, + }, ], }), ).rejects.toThrow(/Checksum required/);