@bastani/atomic 0.9.14-alpha.2 → 0.9.14-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/CHANGELOG.md +6 -0
  4. package/dist/builtin/mcp/package.json +2 -2
  5. package/dist/builtin/subagents/CHANGELOG.md +6 -0
  6. package/dist/builtin/subagents/agents/code-simplifier.md +1 -1
  7. package/dist/builtin/subagents/agents/codebase-analyzer.md +1 -1
  8. package/dist/builtin/subagents/agents/codebase-locator.md +1 -1
  9. package/dist/builtin/subagents/agents/codebase-online-researcher.md +1 -1
  10. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +1 -1
  11. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +1 -1
  12. package/dist/builtin/subagents/agents/codebase-research-locator.md +1 -1
  13. package/dist/builtin/subagents/agents/debugger.md +1 -1
  14. package/dist/builtin/subagents/agents/worker.md +1 -1
  15. package/dist/builtin/subagents/package.json +1 -1
  16. package/dist/builtin/web-access/CHANGELOG.md +16 -0
  17. package/dist/builtin/web-access/README.md +3 -6
  18. package/dist/builtin/web-access/index.ts +1 -2
  19. package/dist/builtin/web-access/package.json +2 -2
  20. package/dist/builtin/web-access/web-search-config.ts +2 -8
  21. package/dist/builtin/web-access/web-search-tool.ts +2 -7
  22. package/dist/builtin/web-access/web-search-workflow.ts +10 -0
  23. package/dist/builtin/workflows/CHANGELOG.md +18 -0
  24. package/dist/builtin/workflows/README.md +1 -1
  25. package/dist/builtin/workflows/builtin/goal-models.ts +4 -2
  26. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +2 -1
  27. package/dist/builtin/workflows/builtin/ralph-models.ts +10 -5
  28. package/dist/builtin/workflows/package.json +1 -1
  29. package/dist/builtin/workflows/src/extension/index.bundle.mjs +125 -66
  30. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +32 -4
  31. package/dist/builtin/workflows/src/runs/foreground/executor-prompt-nodes.ts +3 -9
  32. package/dist/builtin/workflows/src/runs/foreground/executor-stage-call.ts +27 -20
  33. package/dist/builtin/workflows/src/runs/foreground/executor.ts +4 -0
  34. package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +6 -1
  35. package/dist/builtin/workflows/src/shared/authoring-contract-ui.ts +6 -1
  36. package/dist/builtin/workflows/src/tui/graph-theme.ts +11 -0
  37. package/dist/builtin/workflows/src/tui/graph-view-render.ts +19 -10
  38. package/dist/builtin/workflows/src/tui/stage-chat-view-custom-ui.ts +7 -8
  39. package/dist/builtin/workflows/src/tui/tool-detail.ts +45 -26
  40. package/dist/core/tools/ask-user-question/ask-user-question.d.ts +3 -1
  41. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  42. package/dist/core/tools/ask-user-question/ask-user-question.js +2 -1
  43. package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
  44. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts +1 -0
  45. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts.map +1 -1
  46. package/dist/core/tools/ask-user-question/state/build-questionnaire.js +1 -0
  47. package/dist/core/tools/ask-user-question/state/build-questionnaire.js.map +1 -1
  48. package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -1
  49. package/dist/core/tools/ask-user-question/state/key-router.js +3 -1
  50. package/dist/core/tools/ask-user-question/state/key-router.js.map +1 -1
  51. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts +3 -0
  52. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  53. package/dist/core/tools/ask-user-question/state/questionnaire-session.js +3 -0
  54. package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
  55. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts +1 -0
  56. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  57. package/dist/core/tools/ask-user-question/state/state-reducer.js +3 -1
  58. package/dist/core/tools/ask-user-question/state/state-reducer.js.map +1 -1
  59. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts +2 -0
  60. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -1
  61. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js +1 -0
  62. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js.map +1 -1
  63. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts +3 -0
  64. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  65. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +2 -1
  66. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
  67. package/dist/core/tools/edit-batch.d.ts +33 -0
  68. package/dist/core/tools/edit-batch.d.ts.map +1 -0
  69. package/dist/core/tools/edit-batch.js +89 -0
  70. package/dist/core/tools/edit-batch.js.map +1 -0
  71. package/dist/core/tools/edit.d.ts +1 -1
  72. package/dist/core/tools/edit.d.ts.map +1 -1
  73. package/dist/core/tools/edit.js +98 -42
  74. package/dist/core/tools/edit.js.map +1 -1
  75. package/docs/providers.md +1 -1
  76. package/docs/tools.md +1 -1
  77. package/docs/workflows.md +4 -4
  78. package/npm-shrinkwrap.json +65 -50
  79. package/package.json +5 -5
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Coalesce in-flight hashline `edit` calls that target the same snapshot.
3
+ *
4
+ * Models often emit several parallel `edit` calls against one `[path#TAG]`.
5
+ * Those hunks were authored against the original snapshot, so they must apply
6
+ * as one batch — the same merge `Patch.parse` already does inside a single
7
+ * call. The first writer to hold the file mutation lock absorbs every
8
+ * compatible sibling that has already announced and applies them together.
9
+ */
10
+ export declare function parallelEditBatchWarning(count: number): string;
11
+ export declare class EditBatchEntry<T> {
12
+ #private;
13
+ readonly input: string;
14
+ readonly paths: ReadonlyMap<string, string>;
15
+ readonly signal: AbortSignal | undefined;
16
+ readonly promise: Promise<T>;
17
+ constructor(input: string, paths: ReadonlyMap<string, string>, signal?: AbortSignal);
18
+ get settled(): boolean;
19
+ resolve(value: T): void;
20
+ reject(reason: unknown): void;
21
+ }
22
+ export declare class EditBatchCoordinator<T> {
23
+ #private;
24
+ announce(input: string, paths: ReadonlyMap<string, string>, signal?: AbortSignal): EditBatchEntry<T>;
25
+ /**
26
+ * Claim `leader` plus every pending sibling whose sections are a subset of
27
+ * the leader's locked `(path → tag)` set. Aborted siblings are rejected and
28
+ * dropped instead of being absorbed.
29
+ */
30
+ takeCompatible(leader: EditBatchEntry<T>): EditBatchEntry<T>[];
31
+ drop(entry: EditBatchEntry<T>): void;
32
+ }
33
+ //# sourceMappingURL=edit-batch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-batch.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit-batch.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,qBAAa,cAAc,CAAC,CAAC;;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAK7B,YAAY,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,EAYlF;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAItB;IAED,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAI5B;CACD;AAED,qBAAa,oBAAoB,CAAC,CAAC;;IAGlC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC,CAAC,CAAC,CAInG;IAED;;;;OAIG;IACH,cAAc,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,CAc7D;IAED,IAAI,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI,CAEnC;CACD"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Coalesce in-flight hashline `edit` calls that target the same snapshot.
3
+ *
4
+ * Models often emit several parallel `edit` calls against one `[path#TAG]`.
5
+ * Those hunks were authored against the original snapshot, so they must apply
6
+ * as one batch — the same merge `Patch.parse` already does inside a single
7
+ * call. The first writer to hold the file mutation lock absorbs every
8
+ * compatible sibling that has already announced and applies them together.
9
+ */
10
+ export function parallelEditBatchWarning(count) {
11
+ return `Applied ${count} parallel edit calls as one snapshot-anchored batch.`;
12
+ }
13
+ export class EditBatchEntry {
14
+ #resolve;
15
+ #reject;
16
+ #settled = false;
17
+ constructor(input, paths, signal) {
18
+ this.input = input;
19
+ this.paths = paths;
20
+ this.signal = signal;
21
+ this.promise = new Promise((resolve, reject) => {
22
+ this.#resolve = resolve;
23
+ this.#reject = reject;
24
+ });
25
+ // A follower may still be waiting on the file lock when the leader
26
+ // rejects the group. Swallow here so that rejection is not "unhandled"
27
+ // before the follower awaits the same promise.
28
+ this.promise.catch(() => undefined);
29
+ }
30
+ get settled() {
31
+ return this.#settled;
32
+ }
33
+ resolve(value) {
34
+ if (this.#settled)
35
+ return;
36
+ this.#settled = true;
37
+ this.#resolve(value);
38
+ }
39
+ reject(reason) {
40
+ if (this.#settled)
41
+ return;
42
+ this.#settled = true;
43
+ this.#reject(reason);
44
+ }
45
+ }
46
+ export class EditBatchCoordinator {
47
+ #pending = new Set();
48
+ announce(input, paths, signal) {
49
+ const entry = new EditBatchEntry(input, paths, signal);
50
+ this.#pending.add(entry);
51
+ return entry;
52
+ }
53
+ /**
54
+ * Claim `leader` plus every pending sibling whose sections are a subset of
55
+ * the leader's locked `(path → tag)` set. Aborted siblings are rejected and
56
+ * dropped instead of being absorbed.
57
+ */
58
+ takeCompatible(leader) {
59
+ const group = [leader];
60
+ this.#pending.delete(leader);
61
+ for (const other of [...this.#pending]) {
62
+ if (other.signal?.aborted) {
63
+ this.#pending.delete(other);
64
+ other.reject(new Error("Operation aborted"));
65
+ continue;
66
+ }
67
+ if (!isSubsetWithMatchingTags(other.paths, leader.paths))
68
+ continue;
69
+ this.#pending.delete(other);
70
+ group.push(other);
71
+ }
72
+ return group;
73
+ }
74
+ drop(entry) {
75
+ this.#pending.delete(entry);
76
+ }
77
+ }
78
+ function isSubsetWithMatchingTags(candidate, locked) {
79
+ if (candidate.size === 0)
80
+ return false;
81
+ for (const [path, tag] of candidate) {
82
+ if (tag.length === 0)
83
+ return false;
84
+ if (locked.get(path) !== tag)
85
+ return false;
86
+ }
87
+ return true;
88
+ }
89
+ //# sourceMappingURL=edit-batch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-batch.js","sourceRoot":"","sources":["../../../src/core/tools/edit-batch.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Coalesce in-flight hashline `edit` calls that target the same snapshot.\n *\n * Models often emit several parallel `edit` calls against one `[path#TAG]`.\n * Those hunks were authored against the original snapshot, so they must apply\n * as one batch — the same merge `Patch.parse` already does inside a single\n * call. The first writer to hold the file mutation lock absorbs every\n * compatible sibling that has already announced and applies them together.\n */\nexport function parallelEditBatchWarning(count: number): string {\n\treturn `Applied ${count} parallel edit calls as one snapshot-anchored batch.`;\n}\n\nexport class EditBatchEntry<T> {\n\treadonly input: string;\n\treadonly paths: ReadonlyMap<string, string>;\n\treadonly signal: AbortSignal | undefined;\n\treadonly promise: Promise<T>;\n\t#resolve!: (value: T) => void;\n\t#reject!: (reason: unknown) => void;\n\t#settled = false;\n\n\tconstructor(input: string, paths: ReadonlyMap<string, string>, signal?: AbortSignal) {\n\t\tthis.input = input;\n\t\tthis.paths = paths;\n\t\tthis.signal = signal;\n\t\tthis.promise = new Promise<T>((resolve, reject) => {\n\t\t\tthis.#resolve = resolve;\n\t\t\tthis.#reject = reject;\n\t\t});\n\t\t// A follower may still be waiting on the file lock when the leader\n\t\t// rejects the group. Swallow here so that rejection is not \"unhandled\"\n\t\t// before the follower awaits the same promise.\n\t\tthis.promise.catch(() => undefined);\n\t}\n\n\tget settled(): boolean {\n\t\treturn this.#settled;\n\t}\n\n\tresolve(value: T): void {\n\t\tif (this.#settled) return;\n\t\tthis.#settled = true;\n\t\tthis.#resolve(value);\n\t}\n\n\treject(reason: unknown): void {\n\t\tif (this.#settled) return;\n\t\tthis.#settled = true;\n\t\tthis.#reject(reason);\n\t}\n}\n\nexport class EditBatchCoordinator<T> {\n\treadonly #pending = new Set<EditBatchEntry<T>>();\n\n\tannounce(input: string, paths: ReadonlyMap<string, string>, signal?: AbortSignal): EditBatchEntry<T> {\n\t\tconst entry = new EditBatchEntry<T>(input, paths, signal);\n\t\tthis.#pending.add(entry);\n\t\treturn entry;\n\t}\n\n\t/**\n\t * Claim `leader` plus every pending sibling whose sections are a subset of\n\t * the leader's locked `(path → tag)` set. Aborted siblings are rejected and\n\t * dropped instead of being absorbed.\n\t */\n\ttakeCompatible(leader: EditBatchEntry<T>): EditBatchEntry<T>[] {\n\t\tconst group = [leader];\n\t\tthis.#pending.delete(leader);\n\t\tfor (const other of [...this.#pending]) {\n\t\t\tif (other.signal?.aborted) {\n\t\t\t\tthis.#pending.delete(other);\n\t\t\t\tother.reject(new Error(\"Operation aborted\"));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!isSubsetWithMatchingTags(other.paths, leader.paths)) continue;\n\t\t\tthis.#pending.delete(other);\n\t\t\tgroup.push(other);\n\t\t}\n\t\treturn group;\n\t}\n\n\tdrop(entry: EditBatchEntry<T>): void {\n\t\tthis.#pending.delete(entry);\n\t}\n}\n\nfunction isSubsetWithMatchingTags(\n\tcandidate: ReadonlyMap<string, string>,\n\tlocked: ReadonlyMap<string, string>,\n): boolean {\n\tif (candidate.size === 0) return false;\n\tfor (const [path, tag] of candidate) {\n\t\tif (tag.length === 0) return false;\n\t\tif (locked.get(path) !== tag) return false;\n\t}\n\treturn true;\n}\n"]}
@@ -7,7 +7,7 @@ declare const editSchema: Type.TObject<{
7
7
  }>;
8
8
  export declare const editToolSystemPromptContribution: Readonly<{
9
9
  readonly snippet: "Apply source edits with hashline patch input";
10
- readonly guidelines: readonly ["hashline edit format: a header ending in ':' is followed by '+'TEXT body rows; 'delete' has no body. Every section starts with [PATH#TAG]; TAG is REQUIRED (the 4-hex snapshot tag from your latest read/search) — there is no hashless form. Use the write tool to create new files.", "Ops: 'replace N..M:' replaces original lines N..M (INCLUSIVE — line M is consumed); 'replace block N:' replaces the whole syntactic block that BEGINS on line N (Atomic resolves the closing line with a brace/indent heuristic; point N at the opener); 'delete N..M' / 'delete block N' delete (no body); 'insert before N:' / 'insert after N:' insert relative to a line; 'insert after block N:' inserts after the END of the block beginning on N; 'insert head:' / 'insert tail:' insert at file start/end. Single line: 'replace N..N:' / 'delete N'. The range is the ORIGINAL lines you touch; body length is irrelevant.", "Body rows appear only under a ':' header. Every row is '+TEXT' (adds a literal line, leading whitespace kept; '+' alone adds a blank line). There is NO other body row kind — never write '-old' or a bare/context line. To keep a line, leave it out of every range. For a literal line starting with '-' or '+', prefix it: '+-x', '++x'.", "Numbers refer to the ORIGINAL file and do not shift as hunks apply; they die with the call — every applied edit mints a fresh #TAG and renumbers, so anchor the next edit on the edit response or a fresh read. Ranges are TIGHT: cover ONLY lines whose content changes; a stale wide range shreds everything it spans. Pure additions use 'insert', never a widened 'replace'. Whole construct → 'replace block N'; lines inside it → 'replace N..M'.", "On a stale-tag rejection or any surprising result: STOP and re-read before further edits. Never start or end a range mid-expression/mid-block, and never span a hunk across an elided ('…') region — read it first. Never use edit to reformat/restyle code; run the project formatter instead."];
10
+ readonly guidelines: readonly ["hashline edit format: a header ending in ':' is followed by '+'TEXT body rows; 'delete' has no body. Every section starts with [PATH#TAG]; TAG is REQUIRED (the 4-hex snapshot tag from your latest read/search) — there is no hashless form. Use the write tool to create new files.", "Ops: 'replace N..M:' replaces original lines N..M (INCLUSIVE — line M is consumed); 'replace block N:' replaces the whole syntactic block that BEGINS on line N (Atomic resolves the closing line with a brace/indent heuristic; point N at the opener); 'delete N..M' / 'delete block N' delete (no body); 'insert before N:' / 'insert after N:' insert relative to a line; 'insert after block N:' inserts after the END of the block beginning on N; 'insert head:' / 'insert tail:' insert at file start/end. Single line: 'replace N..N:' / 'delete N'. The range is the ORIGINAL lines you touch; body length is irrelevant.", "Body rows appear only under a ':' header. Every row is '+TEXT' (adds a literal line, leading whitespace kept; '+' alone adds a blank line). There is NO other body row kind — never write '-old' or a bare/context line. To keep a line, leave it out of every range. For a literal line starting with '-' or '+', prefix it: '+-x', '++x'.", "Numbers refer to the ORIGINAL file and do not shift as hunks apply; they die with the call — every applied edit mints a fresh #TAG and renumbers, so anchor the next edit on the edit response or a fresh read. Parallel edit calls that share a [path#TAG] are applied as one snapshot batch. Ranges are TIGHT: cover ONLY lines whose content changes; a stale wide range shreds everything it spans. Pure additions use 'insert', never a widened 'replace'. Whole construct → 'replace block N'; lines inside it → 'replace N..M'.", "On a stale-tag rejection or any surprising result: STOP and re-read before further edits. Never start or end a range mid-expression/mid-block, and never span a hunk across an elided ('…') region — read it first. Never use edit to reformat/restyle code; run the project formatter instead."];
11
11
  }>;
12
12
  export type EditToolInput = Static<typeof editSchema>;
13
13
  export interface EditToolDetails {
@@ -1 +1 @@
1
- {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAI/D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAI7D,OAAO,EAGN,KAAK,qBAAqB,EAE1B,MAAM,eAAe,CAAC;AAevB,QAAA,MAAM,UAAU;;EAQf,CAAC;AACF,eAAO,MAAM,gCAAgC;sBACnC,8CAA8C;;EAQ7C,CAAC;AAEZ,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAQD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,aAAa,CAAC,EAAE,qBAAqB,CAAC;CACtC;AAoID,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAmFhE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG"}
1
+ {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAI/D,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAK7D,OAAO,EAGN,KAAK,qBAAqB,EAE1B,MAAM,eAAe,CAAC;AAgBvB,QAAA,MAAM,UAAU;;EAQf,CAAC;AACF,eAAO,MAAM,gCAAgC;sBACnC,8CAA8C;;EAQ7C,CAAC;AAEZ,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,UAAU,CAAC,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAQD,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,aAAa,CAAC,EAAE,qBAAqB,CAAC;CACtC;AAoID,wBAAgB,wBAAwB,CACvC,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,eAAe,GACvB,cAAc,CAAC,OAAO,UAAU,EAAE,eAAe,GAAG,SAAS,CAAC,CAwIhE;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAEnG"}
@@ -5,10 +5,11 @@ import { Type } from "typebox";
5
5
  import { renderDiff } from "../../modes/interactive/components/diff.js";
6
6
  import { experimentalToolSamplingProperty } from "../experimental.js";
7
7
  import { nativeBlockResolver } from "./block-resolver.js";
8
+ import { EditBatchCoordinator, parallelEditBatchWarning } from "./edit-batch.js";
8
9
  import { generateDiffString, generateUnifiedPatch, normalizeToLF, stripBom } from "./edit-diff.js";
9
10
  import { withFileMutationQueue } from "./file-mutation-queue.js";
10
11
  import { createHashlineSnapshotStore, formatCompactHashlineEditResult, recordHashlineSnapshot, } from "./hashline.js";
11
- import { Filesystem, Patch, Patcher, } from "./hashline-engine/index.js";
12
+ import { Filesystem, missingSnapshotTagMessage, Patch, Patcher, } from "./hashline-engine/index.js";
12
13
  import { isNotebookPath, readEditableNotebookText, serializeEditedNotebookText } from "./notebook.js";
13
14
  import { resolveReadPath } from "./path-utils.js";
14
15
  import { renderToolPath } from "./render-utils.js";
@@ -25,7 +26,7 @@ export const editToolSystemPromptContribution = Object.freeze({
25
26
  "hashline edit format: a header ending in ':' is followed by '+'TEXT body rows; 'delete' has no body. Every section starts with [PATH#TAG]; TAG is REQUIRED (the 4-hex snapshot tag from your latest read/search) — there is no hashless form. Use the write tool to create new files.",
26
27
  "Ops: 'replace N..M:' replaces original lines N..M (INCLUSIVE — line M is consumed); 'replace block N:' replaces the whole syntactic block that BEGINS on line N (Atomic resolves the closing line with a brace/indent heuristic; point N at the opener); 'delete N..M' / 'delete block N' delete (no body); 'insert before N:' / 'insert after N:' insert relative to a line; 'insert after block N:' inserts after the END of the block beginning on N; 'insert head:' / 'insert tail:' insert at file start/end. Single line: 'replace N..N:' / 'delete N'. The range is the ORIGINAL lines you touch; body length is irrelevant.",
27
28
  "Body rows appear only under a ':' header. Every row is '+TEXT' (adds a literal line, leading whitespace kept; '+' alone adds a blank line). There is NO other body row kind — never write '-old' or a bare/context line. To keep a line, leave it out of every range. For a literal line starting with '-' or '+', prefix it: '+-x', '++x'.",
28
- "Numbers refer to the ORIGINAL file and do not shift as hunks apply; they die with the call — every applied edit mints a fresh #TAG and renumbers, so anchor the next edit on the edit response or a fresh read. Ranges are TIGHT: cover ONLY lines whose content changes; a stale wide range shreds everything it spans. Pure additions use 'insert', never a widened 'replace'. Whole construct → 'replace block N'; lines inside it → 'replace N..M'.",
29
+ "Numbers refer to the ORIGINAL file and do not shift as hunks apply; they die with the call — every applied edit mints a fresh #TAG and renumbers, so anchor the next edit on the edit response or a fresh read. Parallel edit calls that share a [path#TAG] are applied as one snapshot batch. Ranges are TIGHT: cover ONLY lines whose content changes; a stale wide range shreds everything it spans. Pure additions use 'insert', never a widened 'replace'. Whole construct → 'replace block N'; lines inside it → 'replace N..M'.",
29
30
  "On a stale-tag rejection or any surprising result: STOP and re-read before further edits. Never start or end a range mid-expression/mid-block, and never span a hunk across an elided ('…') region — read it first. Never use edit to reformat/restyle code; run the project formatter instead.",
30
31
  ]),
31
32
  });
@@ -143,6 +144,69 @@ export function createEditToolDefinition(cwd, options) {
143
144
  const fs = new EditFilesystem(cwd, ops);
144
145
  const patcher = new Patcher({ fs, snapshots: hashlineStore.snapshots, blockResolver: nativeBlockResolver });
145
146
  const noopCounts = new Map();
147
+ const batcher = new EditBatchCoordinator();
148
+ async function applySiblingEdits(siblings, applySignal) {
149
+ const merged = siblings.length === 1
150
+ ? Patch.parse(siblings[0].input, { cwd })
151
+ : Patch.parse(siblings.map((sibling) => sibling.input).join("\n"), { cwd });
152
+ const prepared = [];
153
+ for (const section of merged.sections) {
154
+ throwIfAborted(applySignal);
155
+ prepared.push(await patcher.prepare(section));
156
+ }
157
+ assertUniquePreparedPaths(prepared);
158
+ const noops = prepared.filter((item) => item.isNoop);
159
+ if (noops.length > 0) {
160
+ if (noops.length !== prepared.length)
161
+ throw new Error(`Hashline edit for ${noops[0].section.path} did not change the file.`);
162
+ const key = prepared.map((item) => `${item.canonicalPath}\0${item.applyResult.text}`).join("\0\0");
163
+ const count = (noopCounts.get(key) ?? 0) + 1;
164
+ noopCounts.set(key, count);
165
+ if (count >= 3)
166
+ throw new Error(`STOP. ${formatNoopMessage(prepared[0].section.path, count)}`);
167
+ return {
168
+ content: [{ type: "text", text: formatNoopMessage(prepared[0].section.path, count) }],
169
+ details: { diff: "", patch: "" },
170
+ };
171
+ }
172
+ for (const item of prepared) {
173
+ throwIfAborted(applySignal);
174
+ if (normalizeToLF(stripBom(await fs.readText(item.section.path)).text) !== item.normalized)
175
+ throw new Error(`Stale hashline tag for ${item.section.path}: file content changed before write. Re-read before editing.`);
176
+ }
177
+ const outputs = [];
178
+ let combinedDiff = "", combinedPatch = "";
179
+ let firstChangedLine;
180
+ const batchNote = siblings.length > 1 ? [parallelEditBatchWarning(siblings.length)] : [];
181
+ for (let index = 0; index < prepared.length; index++) {
182
+ const item = prepared[index];
183
+ throwIfAborted(applySignal);
184
+ let result;
185
+ try {
186
+ result = await patcher.commit(item);
187
+ }
188
+ catch (error) {
189
+ const written = prepared.slice(0, index).map((entry) => entry.section.path);
190
+ const notWritten = prepared.slice(index + 1).map((entry) => entry.section.path);
191
+ const message = error instanceof Error ? error.message : String(error);
192
+ throw new Error(`Failed to write ${item.section.path}: ${message}` +
193
+ (written.length > 0 ? ` Sections already written: ${written.join(", ")}.` : "") +
194
+ (notWritten.length > 0 ? ` Sections not written: ${notWritten.join(", ")}.` : ""), { cause: error });
195
+ }
196
+ throwIfAborted(applySignal);
197
+ invalidateNativeSearchCache(result.canonicalPath);
198
+ const snapshot = recordHashlineSnapshot(result.canonicalPath, cwd, result.after, hashlineStore);
199
+ const diffResult = generateDiffString(result.before, result.after);
200
+ combinedDiff += `${combinedDiff ? "\n" : ""}${diffResult.diff}`;
201
+ combinedPatch += `${combinedPatch ? "\n" : ""}${generateUnifiedPatch(result.path, result.before, result.after)}`;
202
+ firstChangedLine ??= diffResult.firstChangedLine;
203
+ outputs.push(formatCompactHashlineEditResult(snapshot, diffResult, [...batchNote, ...blockMessages(result)]));
204
+ }
205
+ return {
206
+ content: [{ type: "text", text: outputs.join("\n\n") }],
207
+ details: { diff: combinedDiff, patch: combinedPatch, firstChangedLine },
208
+ };
209
+ }
146
210
  return {
147
211
  name: "edit",
148
212
  label: "edit",
@@ -155,49 +219,41 @@ export function createEditToolDefinition(cwd, options) {
155
219
  if (typeof input.input !== "string" || input.input.trim() === "")
156
220
  throw new Error("edit input must be a non-empty hashline script with [PATH#TAG] sections.");
157
221
  const patch = Patch.parse(input.input, { cwd });
158
- const prepared = [];
222
+ const paths = new Map();
159
223
  for (const section of patch.sections) {
160
- throwIfAborted(signal);
161
- prepared.push(await patcher.prepare(section));
162
- }
163
- assertUniquePreparedPaths(prepared);
164
- const noops = prepared.filter((item) => item.isNoop);
165
- if (noops.length > 0) {
166
- if (noops.length !== prepared.length)
167
- throw new Error(`Hashline edit for ${noops[0].section.path} did not change the file.`);
168
- const key = prepared.map((item) => `${item.canonicalPath}\0${item.applyResult.text}`).join("\0\0");
169
- const count = (noopCounts.get(key) ?? 0) + 1;
170
- noopCounts.set(key, count);
171
- if (count >= 3)
172
- throw new Error(`STOP. ${formatNoopMessage(prepared[0].section.path, count)}`);
173
- return {
174
- content: [{ type: "text", text: formatNoopMessage(prepared[0].section.path, count) }],
175
- details: { diff: "", patch: "" },
176
- };
224
+ if (section.fileHash === undefined)
225
+ throw new Error(missingSnapshotTagMessage(section.path));
226
+ paths.set(section.path, section.fileHash);
177
227
  }
178
- return withFileMutationQueues(prepared.map((item) => item.canonicalPath), async () => {
179
- for (const item of prepared)
180
- if (normalizeToLF(stripBom(await fs.readText(item.section.path)).text) !== item.normalized)
181
- throw new Error(`Stale hashline tag for ${item.section.path}: file content changed before write. Re-read before editing.`);
182
- const applyResult = await patcher.apply(patch);
183
- const outputs = [];
184
- let combinedDiff = "", combinedPatch = "";
185
- let firstChangedLine;
186
- for (const result of applyResult.sections) {
228
+ const entry = batcher.announce(input.input, paths, signal);
229
+ try {
230
+ return await withFileMutationQueues([...paths.keys()].map((sectionPath) => fs.canonicalPath(sectionPath)), async () => {
231
+ if (entry.settled)
232
+ return await entry.promise;
187
233
  throwIfAborted(signal);
188
- invalidateNativeSearchCache(result.canonicalPath);
189
- const snapshot = recordHashlineSnapshot(result.canonicalPath, cwd, result.after, hashlineStore);
190
- const diffResult = generateDiffString(result.before, result.after);
191
- combinedDiff += `${combinedDiff ? "\n" : ""}${diffResult.diff}`;
192
- combinedPatch += `${combinedPatch ? "\n" : ""}${generateUnifiedPatch(result.path, result.before, result.after)}`;
193
- firstChangedLine ??= diffResult.firstChangedLine;
194
- outputs.push(formatCompactHashlineEditResult(snapshot, diffResult, blockMessages(result)));
195
- }
196
- return {
197
- content: [{ type: "text", text: outputs.join("\n\n") }],
198
- details: { diff: combinedDiff, patch: combinedPatch, firstChangedLine },
199
- };
200
- });
234
+ const siblings = batcher.takeCompatible(entry);
235
+ try {
236
+ const result = await applySiblingEdits(siblings, signal);
237
+ for (const sibling of siblings)
238
+ sibling.resolve(result);
239
+ return result;
240
+ }
241
+ catch (error) {
242
+ for (const sibling of siblings)
243
+ sibling.reject(error);
244
+ throw error;
245
+ }
246
+ });
247
+ }
248
+ catch (error) {
249
+ if (entry.settled)
250
+ return await entry.promise;
251
+ entry.reject(error);
252
+ throw error;
253
+ }
254
+ finally {
255
+ batcher.drop(entry);
256
+ }
201
257
  },
202
258
  renderCall(args, theme, context) {
203
259
  const text = context.lastComponent ?? new Text("", 0, 0);
@@ -1 +1 @@
1
- {"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Container, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport { constants } from \"fs\";\nimport { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from \"fs/promises\";\nimport { type Static, Type } from \"typebox\";\nimport { renderDiff } from \"../../modes/interactive/components/diff.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { experimentalToolSamplingProperty } from \"../experimental.ts\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { nativeBlockResolver } from \"./block-resolver.ts\";\nimport { generateDiffString, generateUnifiedPatch, normalizeToLF, stripBom } from \"./edit-diff.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport {\n\tcreateHashlineSnapshotStore,\n\tformatCompactHashlineEditResult,\n\ttype HashlineSnapshotStore,\n\trecordHashlineSnapshot,\n} from \"./hashline.ts\";\nimport {\n\tFilesystem,\n\tPatch,\n\tPatcher,\n\ttype PatchSectionResult,\n\ttype PreparedSection,\n\ttype WriteResult,\n} from \"./hashline-engine/index.ts\";\nimport { isNotebookPath, readEditableNotebookText, serializeEditedNotebookText } from \"./notebook.ts\";\nimport { resolveReadPath } from \"./path-utils.ts\";\nimport { renderToolPath } from \"./render-utils.ts\";\nimport { invalidateNativeSearchCache } from \"./search-native.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst editSchema = Type.Object(\n\t{\n\t\tinput: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"One or more hashline file sections. Must start with [PATH#TAG]; tag comes from the latest read, search, write, or successful edit output.\",\n\t\t}),\n\t},\n\t{ additionalProperties: false },\n);\nexport const editToolSystemPromptContribution = Object.freeze({\n\tsnippet: \"Apply source edits with hashline patch input\",\n\tguidelines: Object.freeze([\n\t\t\"hashline edit format: a header ending in ':' is followed by '+'TEXT body rows; 'delete' has no body. Every section starts with [PATH#TAG]; TAG is REQUIRED (the 4-hex snapshot tag from your latest read/search) — there is no hashless form. Use the write tool to create new files.\",\n\t\t\"Ops: 'replace N..M:' replaces original lines N..M (INCLUSIVE — line M is consumed); 'replace block N:' replaces the whole syntactic block that BEGINS on line N (Atomic resolves the closing line with a brace/indent heuristic; point N at the opener); 'delete N..M' / 'delete block N' delete (no body); 'insert before N:' / 'insert after N:' insert relative to a line; 'insert after block N:' inserts after the END of the block beginning on N; 'insert head:' / 'insert tail:' insert at file start/end. Single line: 'replace N..N:' / 'delete N'. The range is the ORIGINAL lines you touch; body length is irrelevant.\",\n\t\t\"Body rows appear only under a ':' header. Every row is '+TEXT' (adds a literal line, leading whitespace kept; '+' alone adds a blank line). There is NO other body row kind — never write '-old' or a bare/context line. To keep a line, leave it out of every range. For a literal line starting with '-' or '+', prefix it: '+-x', '++x'.\",\n\t\t\"Numbers refer to the ORIGINAL file and do not shift as hunks apply; they die with the call — every applied edit mints a fresh #TAG and renumbers, so anchor the next edit on the edit response or a fresh read. Ranges are TIGHT: cover ONLY lines whose content changes; a stale wide range shreds everything it spans. Pure additions use 'insert', never a widened 'replace'. Whole construct → 'replace block N'; lines inside it → 'replace N..M'.\",\n\t\t\"On a stale-tag rejection or any surprising result: STOP and re-read before further edits. Never start or end a range mid-expression/mid-block, and never span a hunk across an elided ('…') region — read it first. Never use edit to reformat/restyle code; run the project formatter instead.\",\n\t] as const),\n} as const);\n\nexport type EditToolInput = Static<typeof editSchema>;\n\nexport interface EditToolDetails {\n\tdiff: string;\n\tpatch: string;\n\tfirstChangedLine?: number;\n}\n\nexport interface EditOperations {\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\taccess: (absolutePath: string) => Promise<void>;\n}\n\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => fsReadFile(path),\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\n\nexport interface EditToolOptions {\n\toperations?: EditOperations;\n\thashlineStore?: HashlineSnapshotStore;\n}\n\ntype EditToolResultLike = {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tdetails?: EditToolDetails;\n};\n\nclass EditFilesystem extends Filesystem {\n\tprivate readonly cwd: string;\n\tprivate readonly operations: EditOperations;\n\tconstructor(cwd: string, operations: EditOperations) {\n\t\tsuper();\n\t\tthis.cwd = cwd;\n\t\tthis.operations = operations;\n\t}\n\n\tcanonicalPath(path: string): string {\n\t\treturn resolveReadPath(path, this.cwd);\n\t}\n\n\tasync preflightWrite(path: string): Promise<void> {\n\t\tconst absolutePath = this.canonicalPath(path);\n\t\ttry {\n\t\t\tawait this.operations.access(absolutePath);\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error && \"code\" in error ? `Error code: ${error.code}` : String(error);\n\t\t\tthrow new Error(`Could not edit file: ${path}. ${message}.`);\n\t\t}\n\t}\n\n\tasync readText(path: string): Promise<string> {\n\t\tconst absolutePath = this.canonicalPath(path);\n\t\treturn isNotebookPath(absolutePath)\n\t\t\t? readEditableNotebookText(absolutePath, path)\n\t\t\t: (await this.operations.readFile(absolutePath)).toString(\"utf-8\");\n\t}\n\n\tasync writeText(path: string, content: string): Promise<WriteResult> {\n\t\tconst absolutePath = this.canonicalPath(path);\n\t\tconst persisted = isNotebookPath(absolutePath)\n\t\t\t? serializeEditedNotebookText(absolutePath, path, normalizeToLF(stripBom(content).text))\n\t\t\t: content;\n\t\tawait this.operations.writeFile(absolutePath, persisted);\n\t\treturn { text: persisted };\n\t}\n}\n\nfunction isFourDigitHexTag(value: string): boolean {\n\treturn (\n\t\tvalue.length === 4 &&\n\t\t[...value].every(\n\t\t\t(char) => (char >= \"0\" && char <= \"9\") || (char >= \"a\" && char <= \"f\") || (char >= \"A\" && char <= \"F\"),\n\t\t)\n\t);\n}\n\nfunction extractFirstHeaderPath(input: string | undefined): string | undefined {\n\tif (!input) return undefined;\n\tfor (const line of input.split(\"\\n\")) {\n\t\tconst trimmed = line.trimStart();\n\t\tif (!trimmed.startsWith(\"[\")) continue;\n\t\tconst hashIndex = trimmed.indexOf(\"#\", 1);\n\t\tconst closeIndex = hashIndex >= 0 ? trimmed.indexOf(\"]\", hashIndex + 1) : -1;\n\t\tif (hashIndex <= 1 || closeIndex !== hashIndex + 5) continue;\n\t\tconst tag = trimmed.slice(hashIndex + 1, closeIndex);\n\t\tif (isFourDigitHexTag(tag)) return trimmed.slice(1, hashIndex);\n\t}\n\treturn undefined;\n}\n\nfunction formatEditCall(args: unknown, theme: Theme, cwd: string): string {\n\tconst input = args && typeof args === \"object\" && \"input\" in args ? (args as { input?: unknown }).input : undefined;\n\tconst pathDisplay = renderToolPath(\n\t\textractFirstHeaderPath(typeof input === \"string\" ? input : undefined) ?? null,\n\t\ttheme,\n\t\tcwd,\n\t);\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"edit\"))} ${pathDisplay}`;\n}\n\nfunction formatEditResult(result: EditToolResultLike, theme: Theme, isError: boolean): string | undefined {\n\tif (isError) {\n\t\tconst errorText = result.content\n\t\t\t.filter((c) => c.type === \"text\")\n\t\t\t.map((c) => c.text || \"\")\n\t\t\t.join(\"\\n\");\n\t\treturn errorText ? theme.fg(\"error\", errorText) : undefined;\n\t}\n\treturn result.details?.diff ? renderDiff(result.details.diff) : undefined;\n}\n\nasync function withFileMutationQueues<T>(filePaths: readonly string[], fn: () => Promise<T>): Promise<T> {\n\tconst sorted = [...new Set(filePaths)].sort();\n\tconst run = (index: number): Promise<T> => {\n\t\tconst filePath = sorted[index];\n\t\treturn filePath ? withFileMutationQueue(filePath, () => run(index + 1)) : fn();\n\t};\n\treturn run(0);\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n}\n\nfunction formatNoopMessage(path: string, count: number): string {\n\treturn `Edits to ${path} parsed and applied cleanly, but produced no change: your body row(s) are byte-identical to the file at the targeted lines. The bug is somewhere else — re-read the file before issuing another edit. Do NOT widen the payload or add lines; verify the anchor first.${count > 1 ? `\\nNo-op count for this identical payload: ${count}.` : \"\"}`;\n}\n\nfunction blockMessages(item: PreparedSection | PatchSectionResult): string[] {\n\tconst warnings =\n\t\t\"parseWarnings\" in item ? [...item.parseWarnings, ...(item.applyResult.warnings ?? [])] : item.warnings;\n\tconst resolutions =\n\t\t(\"applyResult\" in item ? item.applyResult.blockResolutions : item.blockResolutions)?.map((resolution) => {\n\t\t\tconst verb = resolution.op === \"insert_after\" ? \"insert after block\" : `${resolution.op} block`;\n\t\t\tconst lands = resolution.op === \"insert_after\" ? `; body lands after line ${resolution.end}` : \"\";\n\t\t\treturn `${verb} ${resolution.anchorLine} → resolved lines ${resolution.start}-${resolution.end} (${resolution.end - resolution.start + 1} lines)${lands}`;\n\t\t}) ?? [];\n\treturn [...warnings, ...resolutions];\n}\n\nfunction assertUniquePreparedPaths(prepared: readonly PreparedSection[]): void {\n\tconst seen = new Map<string, string>();\n\tfor (const entry of prepared) {\n\t\tconst previous = seen.get(entry.canonicalPath);\n\t\tif (previous)\n\t\t\tthrow new Error(\n\t\t\t\t`Multiple hashline sections resolve to the same file (${previous} and ${entry.section.path}). Merge their ops under one header before applying.`,\n\t\t\t);\n\t\tseen.set(entry.canonicalPath, entry.section.path);\n\t}\n}\n\nexport function createEditToolDefinition(\n\tcwd: string,\n\toptions?: EditToolOptions,\n): ToolDefinition<typeof editSchema, EditToolDetails | undefined> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\tconst fs = new EditFilesystem(cwd, ops);\n\tconst patcher = new Patcher({ fs, snapshots: hashlineStore.snapshots, blockResolver: nativeBlockResolver });\n\tconst noopCounts = new Map<string, number>();\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit existing files with the hashline patch language: each section starts with [PATH#TAG] (TAG is the 4-hex snapshot tag from your latest read/search), then hunk headers (replace N..M:, replace block N:, delete N..M, delete block N, insert before|after N:, insert after block N:, insert head:, insert tail:) followed by +TEXT body rows. Numbers refer to the original file. Use the write tool to create new files.\",\n\t\tpromptSnippet: editToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: [...editToolSystemPromptContribution.guidelines],\n\t\t...experimentalToolSamplingProperty(),\n\t\tparameters: editSchema,\n\t\tasync execute(_toolCallId, input: EditToolInput, signal?: AbortSignal) {\n\t\t\tif (typeof input.input !== \"string\" || input.input.trim() === \"\")\n\t\t\t\tthrow new Error(\"edit input must be a non-empty hashline script with [PATH#TAG] sections.\");\n\t\t\tconst patch = Patch.parse(input.input, { cwd });\n\t\t\tconst prepared: PreparedSection[] = [];\n\t\t\tfor (const section of patch.sections) {\n\t\t\t\tthrowIfAborted(signal);\n\t\t\t\tprepared.push(await patcher.prepare(section));\n\t\t\t}\n\t\t\tassertUniquePreparedPaths(prepared);\n\t\t\tconst noops = prepared.filter((item) => item.isNoop);\n\t\t\tif (noops.length > 0) {\n\t\t\t\tif (noops.length !== prepared.length)\n\t\t\t\t\tthrow new Error(`Hashline edit for ${noops[0]!.section.path} did not change the file.`);\n\t\t\t\tconst key = prepared.map((item) => `${item.canonicalPath}\\0${item.applyResult.text}`).join(\"\\0\\0\");\n\t\t\t\tconst count = (noopCounts.get(key) ?? 0) + 1;\n\t\t\t\tnoopCounts.set(key, count);\n\t\t\t\tif (count >= 3) throw new Error(`STOP. ${formatNoopMessage(prepared[0]!.section.path, count)}`);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: formatNoopMessage(prepared[0]!.section.path, count) }],\n\t\t\t\t\tdetails: { diff: \"\", patch: \"\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn withFileMutationQueues(\n\t\t\t\tprepared.map((item) => item.canonicalPath),\n\t\t\t\tasync () => {\n\t\t\t\t\tfor (const item of prepared)\n\t\t\t\t\t\tif (normalizeToLF(stripBom(await fs.readText(item.section.path)).text) !== item.normalized)\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t`Stale hashline tag for ${item.section.path}: file content changed before write. Re-read before editing.`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\tconst applyResult = await patcher.apply(patch);\n\t\t\t\t\tconst outputs: string[] = [];\n\t\t\t\t\tlet combinedDiff = \"\",\n\t\t\t\t\t\tcombinedPatch = \"\";\n\t\t\t\t\tlet firstChangedLine: number | undefined;\n\t\t\t\t\tfor (const result of applyResult.sections) {\n\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\tinvalidateNativeSearchCache(result.canonicalPath);\n\t\t\t\t\t\tconst snapshot = recordHashlineSnapshot(result.canonicalPath, cwd, result.after, hashlineStore);\n\t\t\t\t\t\tconst diffResult = generateDiffString(result.before, result.after);\n\t\t\t\t\t\tcombinedDiff += `${combinedDiff ? \"\\n\" : \"\"}${diffResult.diff}`;\n\t\t\t\t\t\tcombinedPatch += `${combinedPatch ? \"\\n\" : \"\"}${generateUnifiedPatch(result.path, result.before, result.after)}`;\n\t\t\t\t\t\tfirstChangedLine ??= diffResult.firstChangedLine;\n\t\t\t\t\t\toutputs.push(formatCompactHashlineEditResult(snapshot, diffResult, blockMessages(result)));\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: outputs.join(\"\\n\\n\") }],\n\t\t\t\t\t\tdetails: { diff: combinedDiff, patch: combinedPatch, firstChangedLine },\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatEditCall(args, theme, context.cwd));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatEditResult(result as EditToolResultLike, theme, context.isError);\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tcomponent.clear();\n\t\t\tif (!output) return component;\n\t\t\tcomponent.addChild(new Spacer(1));\n\t\t\tcomponent.addChild(new Text(output, 1, 0));\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\treturn wrapToolDefinition(createEditToolDefinition(cwd, options));\n}\n"]}
1
+ {"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/core/tools/edit.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Container, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport { constants } from \"fs\";\nimport { access as fsAccess, readFile as fsReadFile, writeFile as fsWriteFile } from \"fs/promises\";\nimport { type Static, Type } from \"typebox\";\nimport { renderDiff } from \"../../modes/interactive/components/diff.ts\";\nimport type { Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { experimentalToolSamplingProperty } from \"../experimental.ts\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { nativeBlockResolver } from \"./block-resolver.ts\";\nimport { EditBatchCoordinator, parallelEditBatchWarning } from \"./edit-batch.ts\";\nimport { generateDiffString, generateUnifiedPatch, normalizeToLF, stripBom } from \"./edit-diff.ts\";\nimport { withFileMutationQueue } from \"./file-mutation-queue.ts\";\nimport {\n\tcreateHashlineSnapshotStore,\n\tformatCompactHashlineEditResult,\n\ttype HashlineSnapshotStore,\n\trecordHashlineSnapshot,\n} from \"./hashline.ts\";\nimport {\n\tFilesystem,\n\tmissingSnapshotTagMessage,\n\tPatch,\n\tPatcher,\n\ttype PatchSectionResult,\n\ttype PreparedSection,\n\ttype WriteResult,\n} from \"./hashline-engine/index.ts\";\nimport { isNotebookPath, readEditableNotebookText, serializeEditedNotebookText } from \"./notebook.ts\";\nimport { resolveReadPath } from \"./path-utils.ts\";\nimport { renderToolPath } from \"./render-utils.ts\";\nimport { invalidateNativeSearchCache } from \"./search-native.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\n\nconst editSchema = Type.Object(\n\t{\n\t\tinput: Type.String({\n\t\t\tdescription:\n\t\t\t\t\"One or more hashline file sections. Must start with [PATH#TAG]; tag comes from the latest read, search, write, or successful edit output.\",\n\t\t}),\n\t},\n\t{ additionalProperties: false },\n);\nexport const editToolSystemPromptContribution = Object.freeze({\n\tsnippet: \"Apply source edits with hashline patch input\",\n\tguidelines: Object.freeze([\n\t\t\"hashline edit format: a header ending in ':' is followed by '+'TEXT body rows; 'delete' has no body. Every section starts with [PATH#TAG]; TAG is REQUIRED (the 4-hex snapshot tag from your latest read/search) — there is no hashless form. Use the write tool to create new files.\",\n\t\t\"Ops: 'replace N..M:' replaces original lines N..M (INCLUSIVE — line M is consumed); 'replace block N:' replaces the whole syntactic block that BEGINS on line N (Atomic resolves the closing line with a brace/indent heuristic; point N at the opener); 'delete N..M' / 'delete block N' delete (no body); 'insert before N:' / 'insert after N:' insert relative to a line; 'insert after block N:' inserts after the END of the block beginning on N; 'insert head:' / 'insert tail:' insert at file start/end. Single line: 'replace N..N:' / 'delete N'. The range is the ORIGINAL lines you touch; body length is irrelevant.\",\n\t\t\"Body rows appear only under a ':' header. Every row is '+TEXT' (adds a literal line, leading whitespace kept; '+' alone adds a blank line). There is NO other body row kind — never write '-old' or a bare/context line. To keep a line, leave it out of every range. For a literal line starting with '-' or '+', prefix it: '+-x', '++x'.\",\n\t\t\"Numbers refer to the ORIGINAL file and do not shift as hunks apply; they die with the call — every applied edit mints a fresh #TAG and renumbers, so anchor the next edit on the edit response or a fresh read. Parallel edit calls that share a [path#TAG] are applied as one snapshot batch. Ranges are TIGHT: cover ONLY lines whose content changes; a stale wide range shreds everything it spans. Pure additions use 'insert', never a widened 'replace'. Whole construct → 'replace block N'; lines inside it → 'replace N..M'.\",\n\t\t\"On a stale-tag rejection or any surprising result: STOP and re-read before further edits. Never start or end a range mid-expression/mid-block, and never span a hunk across an elided ('…') region — read it first. Never use edit to reformat/restyle code; run the project formatter instead.\",\n\t] as const),\n} as const);\n\nexport type EditToolInput = Static<typeof editSchema>;\n\nexport interface EditToolDetails {\n\tdiff: string;\n\tpatch: string;\n\tfirstChangedLine?: number;\n}\n\nexport interface EditOperations {\n\treadFile: (absolutePath: string) => Promise<Buffer>;\n\twriteFile: (absolutePath: string, content: string) => Promise<void>;\n\taccess: (absolutePath: string) => Promise<void>;\n}\n\nconst defaultEditOperations: EditOperations = {\n\treadFile: (path) => fsReadFile(path),\n\twriteFile: (path, content) => fsWriteFile(path, content, \"utf-8\"),\n\taccess: (path) => fsAccess(path, constants.R_OK | constants.W_OK),\n};\n\nexport interface EditToolOptions {\n\toperations?: EditOperations;\n\thashlineStore?: HashlineSnapshotStore;\n}\n\ntype EditToolResultLike = {\n\tcontent: Array<{ type: \"text\"; text: string }>;\n\tdetails: EditToolDetails | undefined;\n};\n\nclass EditFilesystem extends Filesystem {\n\tprivate readonly cwd: string;\n\tprivate readonly operations: EditOperations;\n\tconstructor(cwd: string, operations: EditOperations) {\n\t\tsuper();\n\t\tthis.cwd = cwd;\n\t\tthis.operations = operations;\n\t}\n\n\tcanonicalPath(path: string): string {\n\t\treturn resolveReadPath(path, this.cwd);\n\t}\n\n\tasync preflightWrite(path: string): Promise<void> {\n\t\tconst absolutePath = this.canonicalPath(path);\n\t\ttry {\n\t\t\tawait this.operations.access(absolutePath);\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error && \"code\" in error ? `Error code: ${error.code}` : String(error);\n\t\t\tthrow new Error(`Could not edit file: ${path}. ${message}.`);\n\t\t}\n\t}\n\n\tasync readText(path: string): Promise<string> {\n\t\tconst absolutePath = this.canonicalPath(path);\n\t\treturn isNotebookPath(absolutePath)\n\t\t\t? readEditableNotebookText(absolutePath, path)\n\t\t\t: (await this.operations.readFile(absolutePath)).toString(\"utf-8\");\n\t}\n\n\tasync writeText(path: string, content: string): Promise<WriteResult> {\n\t\tconst absolutePath = this.canonicalPath(path);\n\t\tconst persisted = isNotebookPath(absolutePath)\n\t\t\t? serializeEditedNotebookText(absolutePath, path, normalizeToLF(stripBom(content).text))\n\t\t\t: content;\n\t\tawait this.operations.writeFile(absolutePath, persisted);\n\t\treturn { text: persisted };\n\t}\n}\n\nfunction isFourDigitHexTag(value: string): boolean {\n\treturn (\n\t\tvalue.length === 4 &&\n\t\t[...value].every(\n\t\t\t(char) => (char >= \"0\" && char <= \"9\") || (char >= \"a\" && char <= \"f\") || (char >= \"A\" && char <= \"F\"),\n\t\t)\n\t);\n}\n\nfunction extractFirstHeaderPath(input: string | undefined): string | undefined {\n\tif (!input) return undefined;\n\tfor (const line of input.split(\"\\n\")) {\n\t\tconst trimmed = line.trimStart();\n\t\tif (!trimmed.startsWith(\"[\")) continue;\n\t\tconst hashIndex = trimmed.indexOf(\"#\", 1);\n\t\tconst closeIndex = hashIndex >= 0 ? trimmed.indexOf(\"]\", hashIndex + 1) : -1;\n\t\tif (hashIndex <= 1 || closeIndex !== hashIndex + 5) continue;\n\t\tconst tag = trimmed.slice(hashIndex + 1, closeIndex);\n\t\tif (isFourDigitHexTag(tag)) return trimmed.slice(1, hashIndex);\n\t}\n\treturn undefined;\n}\n\nfunction formatEditCall(args: unknown, theme: Theme, cwd: string): string {\n\tconst input = args && typeof args === \"object\" && \"input\" in args ? (args as { input?: unknown }).input : undefined;\n\tconst pathDisplay = renderToolPath(\n\t\textractFirstHeaderPath(typeof input === \"string\" ? input : undefined) ?? null,\n\t\ttheme,\n\t\tcwd,\n\t);\n\treturn `${theme.fg(\"toolTitle\", theme.bold(\"edit\"))} ${pathDisplay}`;\n}\n\nfunction formatEditResult(result: EditToolResultLike, theme: Theme, isError: boolean): string | undefined {\n\tif (isError) {\n\t\tconst errorText = result.content\n\t\t\t.filter((c) => c.type === \"text\")\n\t\t\t.map((c) => c.text || \"\")\n\t\t\t.join(\"\\n\");\n\t\treturn errorText ? theme.fg(\"error\", errorText) : undefined;\n\t}\n\treturn result.details?.diff ? renderDiff(result.details.diff) : undefined;\n}\n\nasync function withFileMutationQueues<T>(filePaths: readonly string[], fn: () => Promise<T>): Promise<T> {\n\tconst sorted = [...new Set(filePaths)].sort();\n\tconst run = (index: number): Promise<T> => {\n\t\tconst filePath = sorted[index];\n\t\treturn filePath ? withFileMutationQueue(filePath, () => run(index + 1)) : fn();\n\t};\n\treturn run(0);\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n\tif (signal?.aborted) throw new Error(\"Operation aborted\");\n}\n\nfunction formatNoopMessage(path: string, count: number): string {\n\treturn `Edits to ${path} parsed and applied cleanly, but produced no change: your body row(s) are byte-identical to the file at the targeted lines. The bug is somewhere else — re-read the file before issuing another edit. Do NOT widen the payload or add lines; verify the anchor first.${count > 1 ? `\\nNo-op count for this identical payload: ${count}.` : \"\"}`;\n}\n\nfunction blockMessages(item: PreparedSection | PatchSectionResult): string[] {\n\tconst warnings =\n\t\t\"parseWarnings\" in item ? [...item.parseWarnings, ...(item.applyResult.warnings ?? [])] : item.warnings;\n\tconst resolutions =\n\t\t(\"applyResult\" in item ? item.applyResult.blockResolutions : item.blockResolutions)?.map((resolution) => {\n\t\t\tconst verb = resolution.op === \"insert_after\" ? \"insert after block\" : `${resolution.op} block`;\n\t\t\tconst lands = resolution.op === \"insert_after\" ? `; body lands after line ${resolution.end}` : \"\";\n\t\t\treturn `${verb} ${resolution.anchorLine} → resolved lines ${resolution.start}-${resolution.end} (${resolution.end - resolution.start + 1} lines)${lands}`;\n\t\t}) ?? [];\n\treturn [...warnings, ...resolutions];\n}\n\nfunction assertUniquePreparedPaths(prepared: readonly PreparedSection[]): void {\n\tconst seen = new Map<string, string>();\n\tfor (const entry of prepared) {\n\t\tconst previous = seen.get(entry.canonicalPath);\n\t\tif (previous)\n\t\t\tthrow new Error(\n\t\t\t\t`Multiple hashline sections resolve to the same file (${previous} and ${entry.section.path}). Merge their ops under one header before applying.`,\n\t\t\t);\n\t\tseen.set(entry.canonicalPath, entry.section.path);\n\t}\n}\n\nexport function createEditToolDefinition(\n\tcwd: string,\n\toptions?: EditToolOptions,\n): ToolDefinition<typeof editSchema, EditToolDetails | undefined> {\n\tconst ops = options?.operations ?? defaultEditOperations;\n\tconst hashlineStore = options?.hashlineStore ?? createHashlineSnapshotStore();\n\tconst fs = new EditFilesystem(cwd, ops);\n\tconst patcher = new Patcher({ fs, snapshots: hashlineStore.snapshots, blockResolver: nativeBlockResolver });\n\tconst noopCounts = new Map<string, number>();\n\tconst batcher = new EditBatchCoordinator<EditToolResultLike>();\n\tasync function applySiblingEdits(\n\t\tsiblings: readonly { input: string }[],\n\t\tapplySignal?: AbortSignal,\n\t): Promise<EditToolResultLike> {\n\t\tconst merged =\n\t\t\tsiblings.length === 1\n\t\t\t\t? Patch.parse(siblings[0]!.input, { cwd })\n\t\t\t\t: Patch.parse(siblings.map((sibling) => sibling.input).join(\"\\n\"), { cwd });\n\t\tconst prepared: PreparedSection[] = [];\n\t\tfor (const section of merged.sections) {\n\t\t\tthrowIfAborted(applySignal);\n\t\t\tprepared.push(await patcher.prepare(section));\n\t\t}\n\t\tassertUniquePreparedPaths(prepared);\n\t\tconst noops = prepared.filter((item) => item.isNoop);\n\t\tif (noops.length > 0) {\n\t\t\tif (noops.length !== prepared.length)\n\t\t\t\tthrow new Error(`Hashline edit for ${noops[0]!.section.path} did not change the file.`);\n\t\t\tconst key = prepared.map((item) => `${item.canonicalPath}\\0${item.applyResult.text}`).join(\"\\0\\0\");\n\t\t\tconst count = (noopCounts.get(key) ?? 0) + 1;\n\t\t\tnoopCounts.set(key, count);\n\t\t\tif (count >= 3) throw new Error(`STOP. ${formatNoopMessage(prepared[0]!.section.path, count)}`);\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: formatNoopMessage(prepared[0]!.section.path, count) }],\n\t\t\t\tdetails: { diff: \"\", patch: \"\" },\n\t\t\t};\n\t\t}\n\t\tfor (const item of prepared) {\n\t\t\tthrowIfAborted(applySignal);\n\t\t\tif (normalizeToLF(stripBom(await fs.readText(item.section.path)).text) !== item.normalized)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Stale hashline tag for ${item.section.path}: file content changed before write. Re-read before editing.`,\n\t\t\t\t);\n\t\t}\n\t\tconst outputs: string[] = [];\n\t\tlet combinedDiff = \"\",\n\t\t\tcombinedPatch = \"\";\n\t\tlet firstChangedLine: number | undefined;\n\t\tconst batchNote = siblings.length > 1 ? [parallelEditBatchWarning(siblings.length)] : [];\n\t\tfor (let index = 0; index < prepared.length; index++) {\n\t\t\tconst item = prepared[index]!;\n\t\t\tthrowIfAborted(applySignal);\n\t\t\tlet result: PatchSectionResult;\n\t\t\ttry {\n\t\t\t\tresult = await patcher.commit(item);\n\t\t\t} catch (error) {\n\t\t\t\tconst written = prepared.slice(0, index).map((entry) => entry.section.path);\n\t\t\t\tconst notWritten = prepared.slice(index + 1).map((entry) => entry.section.path);\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to write ${item.section.path}: ${message}` +\n\t\t\t\t\t\t(written.length > 0 ? ` Sections already written: ${written.join(\", \")}.` : \"\") +\n\t\t\t\t\t\t(notWritten.length > 0 ? ` Sections not written: ${notWritten.join(\", \")}.` : \"\"),\n\t\t\t\t\t{ cause: error },\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrowIfAborted(applySignal);\n\t\t\tinvalidateNativeSearchCache(result.canonicalPath);\n\t\t\tconst snapshot = recordHashlineSnapshot(result.canonicalPath, cwd, result.after, hashlineStore);\n\t\t\tconst diffResult = generateDiffString(result.before, result.after);\n\t\t\tcombinedDiff += `${combinedDiff ? \"\\n\" : \"\"}${diffResult.diff}`;\n\t\t\tcombinedPatch += `${combinedPatch ? \"\\n\" : \"\"}${generateUnifiedPatch(result.path, result.before, result.after)}`;\n\t\t\tfirstChangedLine ??= diffResult.firstChangedLine;\n\t\t\toutputs.push(formatCompactHashlineEditResult(snapshot, diffResult, [...batchNote, ...blockMessages(result)]));\n\t\t}\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: outputs.join(\"\\n\\n\") }],\n\t\t\tdetails: { diff: combinedDiff, patch: combinedPatch, firstChangedLine },\n\t\t};\n\t}\n\treturn {\n\t\tname: \"edit\",\n\t\tlabel: \"edit\",\n\t\tdescription:\n\t\t\t\"Edit existing files with the hashline patch language: each section starts with [PATH#TAG] (TAG is the 4-hex snapshot tag from your latest read/search), then hunk headers (replace N..M:, replace block N:, delete N..M, delete block N, insert before|after N:, insert after block N:, insert head:, insert tail:) followed by +TEXT body rows. Numbers refer to the original file. Use the write tool to create new files.\",\n\t\tpromptSnippet: editToolSystemPromptContribution.snippet,\n\t\tpromptGuidelines: [...editToolSystemPromptContribution.guidelines],\n\t\t...experimentalToolSamplingProperty(),\n\t\tparameters: editSchema,\n\t\tasync execute(_toolCallId, input: EditToolInput, signal?: AbortSignal) {\n\t\t\tif (typeof input.input !== \"string\" || input.input.trim() === \"\")\n\t\t\t\tthrow new Error(\"edit input must be a non-empty hashline script with [PATH#TAG] sections.\");\n\t\t\tconst patch = Patch.parse(input.input, { cwd });\n\t\t\tconst paths = new Map<string, string>();\n\t\t\tfor (const section of patch.sections) {\n\t\t\t\tif (section.fileHash === undefined) throw new Error(missingSnapshotTagMessage(section.path));\n\t\t\t\tpaths.set(section.path, section.fileHash);\n\t\t\t}\n\t\t\tconst entry = batcher.announce(input.input, paths, signal);\n\t\t\ttry {\n\t\t\t\treturn await withFileMutationQueues(\n\t\t\t\t\t[...paths.keys()].map((sectionPath) => fs.canonicalPath(sectionPath)),\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tif (entry.settled) return await entry.promise;\n\t\t\t\t\t\tthrowIfAborted(signal);\n\t\t\t\t\t\tconst siblings = batcher.takeCompatible(entry);\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst result = await applySiblingEdits(siblings, signal);\n\t\t\t\t\t\t\tfor (const sibling of siblings) sibling.resolve(result);\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tfor (const sibling of siblings) sibling.reject(error);\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tif (entry.settled) return await entry.promise;\n\t\t\t\tentry.reject(error);\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tbatcher.drop(entry);\n\t\t\t}\n\t\t},\n\t\trenderCall(args, theme, context) {\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatEditCall(args, theme, context.cwd));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, _options, theme, context) {\n\t\t\tconst output = formatEditResult(result as EditToolResultLike, theme, context.isError);\n\t\t\tconst component = (context.lastComponent as Container | undefined) ?? new Container();\n\t\t\tcomponent.clear();\n\t\t\tif (!output) return component;\n\t\t\tcomponent.addChild(new Spacer(1));\n\t\t\tcomponent.addChild(new Text(output, 1, 0));\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nexport function createEditTool(cwd: string, options?: EditToolOptions): AgentTool<typeof editSchema> {\n\treturn wrapToolDefinition(createEditToolDefinition(cwd, options));\n}\n"]}
package/docs/providers.md CHANGED
@@ -89,7 +89,7 @@ Business and enterprise tokens sent to the individual host return `421 Misdirect
89
89
 
90
90
  Run `/login xai`, then select **Use a subscription**. `XAI_API_KEY` remains available through **Use an API key**.
91
91
 
92
- Atomic defaults xAI sessions to `grok-4.6`. Built-in workflow and subagent fallback chains use `xai/grok-4.6:high` and `openrouter/x-ai/grok-4.6`; GitHub Copilot also exposes Grok 4.6 when the account's model policy enables it. Network-backed catalogs refresh and cache these newer entries independently of the bundled catalog snapshot.
92
+ Atomic defaults xAI sessions to `grok-4.6`. Built-in workflow and subagent fallback chains use `xai/grok-4.6:xhigh`, `github-copilot/grok-4.6:xhigh`, and `openrouter/x-ai/grok-4.6`; GitHub Copilot also exposes Grok 4.6 when the account's model policy enables it. Network-backed catalogs refresh and cache these newer entries independently of the bundled catalog snapshot.
93
93
 
94
94
  ### Radius
95
95
 
package/docs/tools.md CHANGED
@@ -22,7 +22,7 @@ insert tail:
22
22
  +// done
23
23
  ```
24
24
 
25
- Supported hashline operations include `replace N..M:`, `replace block N:`, `delete N..M`, `delete block N`, `insert before N:`, `insert after N:`, `insert after block N:`, `insert head:`, and `insert tail:`. Safe lenient variants such as `replace N`, `replace N-M:`, `replace N M:`, `replace N…M:`, bare body rows, and `*** Begin Patch` envelopes are accepted. Bare body rows are auto-prefixed and reported as warnings. `*** Abort` stops parsing the remaining input, while apply-patch sentinels, `@@` hunk headers, bare numeric hunk headers, `delete` bodies, empty `replace`/`insert` bodies, and `-` diff rows are rejected with guidance instead of silently deleting content. Line numbers refer to the original tagged snapshot and do not shift within a call.
25
+ Supported hashline operations include `replace N..M:`, `replace block N:`, `delete N..M`, `delete block N`, `insert before N:`, `insert after N:`, `insert after block N:`, `insert head:`, and `insert tail:`. Safe lenient variants such as `replace N`, `replace N-M:`, `replace N M:`, `replace N…M:`, bare body rows, and `*** Begin Patch` envelopes are accepted. Bare body rows are auto-prefixed and reported as warnings. `*** Abort` stops parsing the remaining input, while apply-patch sentinels, `@@` hunk headers, bare numeric hunk headers, `delete` bodies, empty `replace`/`insert` bodies, and `-` diff rows are rejected with guidance instead of silently deleting content. Line numbers refer to the original tagged snapshot and do not shift within a call. Parallel `edit` calls that share a `[path#TAG]` are applied as one snapshot-anchored batch (the same merge as multiple hunks under one header), so a later sibling does not fail just because the first write minted a new tag. A follow-up `edit` that arrives after that batch has already committed still uses snapshot recovery for provably non-overlapping drift.
26
26
 
27
27
  Before writing, Atomic verifies the current file against the tagged snapshot. If the file drifted, `edit` first attempts a snapshot-based recovery for provably non-overlapping external changes and appends a warning when it preserves those changes; unknown tags, overlapping stale edits, and unrecoverable drift fail clearly with the current file hash (and anchor context for drifted files) and leave the file unchanged. Byte-identical no-op edits return a no-op warning without writing, and repeated identical no-ops escalate to an error to stop looped retries. Hashline snapshots are scoped to the active tool/session store, so tags emitted in another session or stale context do not authorize edits. One `edit` input may contain multiple `[PATH#TAG]` sections; Atomic preflights every section before writing, but this is preflight atomicity rather than transactional rollback, so a mid-batch filesystem write failure can leave earlier sections already written. Each successful `write` or `edit` returns a fresh tag for follow-up edits; hashline edit success output is compact and includes the refreshed header plus block-resolution/change metadata while the full diff remains in tool details. Plain `write` success output is likewise compact (`[path#TAG]` plus a byte-count summary), not a full reprint of the file. `write` strips copied hashline headers and `LINE:`/`*LINE:` display prefixes only when the pasted content matches a known current-store snapshot and notes when stripping occurred; complete copied output preserves whether that snapshot had a terminal newline. Literal or unknown hashline-looking content is preserved instead of being stripped.
28
28
 
package/docs/workflows.md CHANGED
@@ -2240,7 +2240,7 @@ ctx.ui.custom<T>(
2240
2240
  ): Promise<T>;
2241
2241
  ```
2242
2242
 
2243
- Builds a custom TUI component and resolves with the value passed to `done(value)`. Workflow graph hosts reject `overlay: true`; `label` is display-only and defaults to `"Custom TUI prompt"`, while `replayIdentity` should change when widget semantics change and must not contain secrets.
2243
+ Builds a custom TUI component and resolves with the value passed to `done(value)`. `overlay: true` is accepted: the attached stage chat mounts it on the same custom-UI slot as an inline widget, which keeps the stage transcript visible and scrollable behind the question, and `overlayOptions` stays advisory metadata there. `label` is display-only and defaults to `"Custom TUI prompt"`, while `replayIdentity` should change when widget semantics change and must not contain secrets.
2244
2244
 
2245
2245
  See [Lifecycle Notices and Human Input](#lifecycle-notices-and-human-input) for replay identity, answer routing, and interactive-only constraints.
2246
2246
 
@@ -3261,7 +3261,7 @@ When a workflow needs human input, answer in the graph viewer or attached stage
3261
3261
 
3262
3262
  Agents can answer primitive and structured pending prompts programmatically with `workflow({ action: "send", delivery: "answer", ... })` only while the root workflow is nonterminal; use `promptId` when it is present in the stage details, and provide answer content with `response`, `text`, or `message`. Arbitrary custom TUI widget prompts intentionally refuse this path in iteration 1 because a generic `T` cannot be reconstructed safely from a non-TUI payload.
3263
3263
 
3264
- `ctx.ui.custom<T>(factory, options?)` reuses Atomic's TUI component path: the factory receives the same real `(tui, theme, keybindings, done)` types as extension `ctx.ui.custom`, and the workflow resumes with the value passed to `done(value)`. Use `options.label` for a safe display-only graph/status label and `options.replayIdentity` when widget semantics can change without the callsite changing. Do not put secrets in labels or replay identities; only a hash of the identity is stored, and label text is not part of replay identity. Inline connected rendering is supported; `overlay: true` is rejected clearly because nested workflow graph overlays are not safely supported yet.
3264
+ `ctx.ui.custom<T>(factory, options?)` reuses Atomic's TUI component path: the factory receives the same real `(tui, theme, keybindings, done)` types as extension `ctx.ui.custom`, and the workflow resumes with the value passed to `done(value)`. Use `options.label` for a safe display-only graph/status label and `options.replayIdentity` when widget semantics can change without the callsite changing. Do not put secrets in labels or replay identities; only a hash of the identity is stored, and label text is not part of replay identity. Both inline connected rendering and `overlay: true` mount in the graph viewer's attached stage chat: overlay is a placement hint rather than a capability request, so an in-stage `ask_user_question` — which always asks for an overlay — mounts, takes focus, and resolves like any other custom prompt. There is no nested host overlay above the graph chrome; the widget occupies the stage-chat custom-UI slot and `overlayOptions` / `onHandle` are not consumed there.
3265
3265
 
3266
3266
  Prompt answers are replayable only while the source run remains in the live in-memory store. `StageSnapshot.promptAnswerState` is snapshot-safe metadata for continuation: `available` means a matching live answer can be replayed, `unavailable` means the matching prompt node exists but its private answer was purged, and `ambiguous` means multiple matching prompt nodes exist so Atomic asks again. The raw answer lives in a private `PromptAnswerRecord` ledger, is never written to snapshots or persistence, and remains resident in memory until the answer is cleared, the run is removed, or the store is cleared.
3267
3267
 
@@ -3271,7 +3271,7 @@ If the user answers a human-in-the-loop prompt in the workflow UI or stage UI br
3271
3271
 
3272
3272
  When an interactive, non-schema workflow stage calls `ask_user_question`, Atomic waits for the stage's assistant turn to finish and then brokers the deterministic readiness question **“Are you ready to move on to the next stage?”**. This includes typed or freeform questionnaire answers reported as `details.answers[].kind === "chat"`: the assistant first gives its normal conversational response, then the stage becomes `awaiting_input` with `inputRequest.kind: "readiness_gate"` in workflow status and graph surfaces.
3273
3273
 
3274
- In this chat-answer flow, choosing the ready option completes the stage and releases dependent stages. Choosing the not-ready option keeps the stage open for a genuine stage-chat turn and brokers readiness again after that turn. A chat answer is never treated as an invisible stay decision.
3274
+ In this chat-answer flow, choosing the ready option completes the stage and releases dependent stages. Choosing the not-ready option keeps the stage open for a genuine stage-chat turn and brokers readiness again after that turn. A chat answer is never treated as an invisible stay decision. On the readiness gate, **Type something.** sends the typed text as the next stage-chat message (empty or whitespace-only text cannot be submitted). **Chat about this** is a plain option — it does not open an inline editor — and stays by sending `The user would like to chat more about this`.
3275
3275
 
3276
3276
  The readiness prompt can be answered in the attached stage UI or with `workflow({ action: "send", delivery: "answer", ... })`. Ordinary structured-option answers retain their existing readiness behavior. A schema-backed stage that has successfully finalized through `structured_output` is terminal and does not reopen this readiness gate.
3277
3277
 
@@ -3314,7 +3314,7 @@ Repeated, sibling, sequential, parallel, and multi-level child calls keep indepe
3314
3314
 
3315
3315
  ### `ctx.tool` — durable cached tool execution
3316
3316
 
3317
- The `ctx.tool(name, args, fn, options?)` primitive runs arbitrary TypeScript code as a first-class durable graph node and caches the result durably. The node is non-attachable and has no stage chat controls, and its graph card body is the constant `durable tool` in every state — status, timing, and dependency rows keep their own rows, and the card does not preview the result or error. In the graph viewer, focusing the node and pressing Enter, clicking it, or choosing it from the switcher opens a read-only host-style operator card from the snapshot: a status-tinted shaded rectangle with a `$ <tool-name>` call header, an optional short argument summary, and the result or error as its body. It is collapsed by default and wraps the fully bounded result or error before showing its last visual rows, with `... (N earlier lines, ctrl+o Expand)` above the tail when the action is bound; the configured `app.tools.expand` action (`ctrl+o` by default) toggles the full bounded result or error and then a muted callback-source block when source exists. The graph statusline advertises the resolved expand key with `expand` or `collapse` alongside return-to-graph and scroll hints, including remapped keys, and omits that segment entirely when the action is unbound. The footer says `Took` for settled calls or `Elapsed` for running calls, using milliseconds below one second and the existing human duration above it, with cached/replayed markers kept as a quiet suffix. The operator surface has no ARGS/RESULT/SOURCE/TIMING/MARKERS debug table and does not expose raw clock fields. Source capture uses `fn.toString()` at registration without re-executing the callback or reading a file. `↑`/`↓`, `PageUp`/`PageDown`, `Home`/`End`, the wheel, and the scrollbar all scroll the block, so a long payload stays readable on a keyboard-only session or a terminal without mouse reporting; Escape or `ctrl+x` returns to the graph. The message block is read-only and never offers chat attachment, steering, interrupt, or resume. Bounded payloads remain width-safe and mark truncation explicitly with `… [truncated]`; source tabs expand and control bytes become `\xNN`, while cyclic payloads, throwing `toJSON`, or throwing property getters render `<cycle>`, `<unserializable>`, or `<unreadable>` instead of crashing the view. The same cap applies to what the live run snapshot retains for a tool node, while durable checkpoints keep the exact output, raw-args `argsHash`, and replay behavior unchanged.
3317
+ The `ctx.tool(name, args, fn, options?)` primitive runs arbitrary TypeScript code as a first-class durable graph node and caches the result durably. The node is non-attachable and has no stage chat controls, and its graph card body is the constant `durable tool` in every state — status, timing, and dependency rows keep their own rows, and the card does not preview the result or error. In the graph viewer, focusing the node and pressing Enter, clicking it, or choosing it from the switcher opens a read-only host-style operator card from the snapshot: a status-tinted shaded rectangle with the same inner padding and header/body gap as the main-chat tool block, inset from the orchestrator header and footer bars, a `$ <tool-name>` call header, an optional short argument summary, and the result or error as its body. It is collapsed by default and wraps the fully bounded result or error before showing its last visual rows, with `... (N earlier lines, ctrl+o Expand)` above the tail when the action is bound; the configured `app.tools.expand` action (`ctrl+o` by default) toggles the full bounded result or error and then a muted callback-source block when source exists. The graph statusline advertises the resolved expand key with `expand` or `collapse` alongside return-to-graph and scroll hints, including remapped keys, and omits that segment entirely when the action is unbound. The footer says `Took` for settled calls or `Elapsed` for running calls, using the same second-resolution duration as the main-chat tool block, with cached/replayed markers kept as a quiet suffix. The operator surface has no ARGS/RESULT/SOURCE/TIMING/MARKERS debug table and does not expose raw clock fields. Source capture uses `fn.toString()` at registration without re-executing the callback or reading a file. `↑`/`↓`, `PageUp`/`PageDown`, `Home`/`End`, the wheel, and the scrollbar all scroll the block, so a long payload stays readable on a keyboard-only session or a terminal without mouse reporting; Escape or `ctrl+x` returns to the graph. The message block is read-only and never offers chat attachment, steering, interrupt, or resume. Bounded payloads remain width-safe and mark truncation explicitly with `… [truncated]`; source tabs expand and control bytes become `\xNN`, while cyclic payloads, throwing `toJSON`, or throwing property getters render `<cycle>`, `<unserializable>`, or `<unreadable>` instead of crashing the view. The same cap applies to what the live run snapshot retains for a tool node, while durable checkpoints keep the exact output, raw-args `argsHash`, and replay behavior unchanged.
3318
3318
 
3319
3319
  When the workflow body fulfills but one or more admitted tool calls failed, Atomic promotes the first observed failure to the terminal run failure, regardless of admission order, and persists that selected tool-node identity for status inspection and lifecycle output. A direct uncaught `await ctx.tool(...)` rejection keeps the original error and persists its failed-node link through session and durable restore. First-event arbitration also preserves the selected node when concurrent failures throw the same object or primitive; unrelated later stage or body errors do not inherit a caught tool's origin. Tool admission remains open while author code can catch a failure and continue. Once the body settles and failure has won before any real cancellation, Atomic closes admission, cancels remaining non-failed tool nodes, waits for observed failed nodes to finish publication, and publishes the failed root without waiting for callbacks that ignore cancellation.
3320
3320