@kenkaiiii/ggcoder 4.14.2 → 4.15.0

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 (170) hide show
  1. package/dist/core/agent-session.d.ts.map +1 -1
  2. package/dist/core/agent-session.js +8 -2
  3. package/dist/core/agent-session.js.map +1 -1
  4. package/dist/core/code-retrieval.d.ts +13 -0
  5. package/dist/core/code-retrieval.d.ts.map +1 -0
  6. package/dist/core/code-retrieval.js +87 -0
  7. package/dist/core/code-retrieval.js.map +1 -0
  8. package/dist/core/hashline-edit-benchmark.d.ts +55 -0
  9. package/dist/core/hashline-edit-benchmark.d.ts.map +1 -0
  10. package/dist/core/hashline-edit-benchmark.js +342 -0
  11. package/dist/core/hashline-edit-benchmark.js.map +1 -0
  12. package/dist/core/hashline-edit-benchmark.test.d.ts +2 -0
  13. package/dist/core/hashline-edit-benchmark.test.d.ts.map +1 -0
  14. package/dist/core/hashline-edit-benchmark.test.js +141 -0
  15. package/dist/core/hashline-edit-benchmark.test.js.map +1 -0
  16. package/dist/core/hashline.d.ts +50 -0
  17. package/dist/core/hashline.d.ts.map +1 -0
  18. package/dist/core/hashline.js +76 -0
  19. package/dist/core/hashline.js.map +1 -0
  20. package/dist/core/semantic-search-benchmark.d.ts +37 -0
  21. package/dist/core/semantic-search-benchmark.d.ts.map +1 -0
  22. package/dist/core/semantic-search-benchmark.js +211 -0
  23. package/dist/core/semantic-search-benchmark.js.map +1 -0
  24. package/dist/core/semantic-search-benchmark.test.d.ts +2 -0
  25. package/dist/core/semantic-search-benchmark.test.d.ts.map +1 -0
  26. package/dist/core/semantic-search-benchmark.test.js +89 -0
  27. package/dist/core/semantic-search-benchmark.test.js.map +1 -0
  28. package/dist/core/steering.d.ts +25 -0
  29. package/dist/core/steering.d.ts.map +1 -0
  30. package/dist/core/steering.js +29 -0
  31. package/dist/core/steering.js.map +1 -0
  32. package/dist/tools/edit.d.ts +6 -0
  33. package/dist/tools/edit.d.ts.map +1 -1
  34. package/dist/tools/edit.js +34 -2
  35. package/dist/tools/edit.js.map +1 -1
  36. package/dist/tools/edit.test.js +112 -0
  37. package/dist/tools/edit.test.js.map +1 -1
  38. package/dist/tools/index.d.ts +1 -0
  39. package/dist/tools/index.d.ts.map +1 -1
  40. package/dist/tools/index.js +3 -0
  41. package/dist/tools/index.js.map +1 -1
  42. package/dist/tools/prompt-hints.d.ts.map +1 -1
  43. package/dist/tools/prompt-hints.js +8 -0
  44. package/dist/tools/prompt-hints.js.map +1 -1
  45. package/dist/tools/read.d.ts +1 -0
  46. package/dist/tools/read.d.ts.map +1 -1
  47. package/dist/tools/read.js +14 -3
  48. package/dist/tools/read.js.map +1 -1
  49. package/dist/tools/read.test.js +40 -0
  50. package/dist/tools/read.test.js.map +1 -1
  51. package/dist/tools/search-code.d.ts +11 -0
  52. package/dist/tools/search-code.d.ts.map +1 -0
  53. package/dist/tools/search-code.js +95 -0
  54. package/dist/tools/search-code.js.map +1 -0
  55. package/dist/tools/search-code.test.d.ts +2 -0
  56. package/dist/tools/search-code.test.d.ts.map +1 -0
  57. package/dist/tools/search-code.test.js +77 -0
  58. package/dist/tools/search-code.test.js.map +1 -0
  59. package/dist/ui/App.d.ts +2 -1
  60. package/dist/ui/App.d.ts.map +1 -1
  61. package/dist/ui/components/ActivityIndicator.d.ts +1 -1
  62. package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
  63. package/dist/ui/components/AnimationContext.d.ts +1 -1
  64. package/dist/ui/components/AnimationContext.d.ts.map +1 -1
  65. package/dist/ui/components/AssistantMessage.d.ts +1 -1
  66. package/dist/ui/components/AssistantMessage.d.ts.map +1 -1
  67. package/dist/ui/components/BackgroundTasksBar.d.ts +2 -1
  68. package/dist/ui/components/BackgroundTasksBar.d.ts.map +1 -1
  69. package/dist/ui/components/Banner.d.ts +2 -1
  70. package/dist/ui/components/Banner.d.ts.map +1 -1
  71. package/dist/ui/components/ChatFooterPane.d.ts +2 -1
  72. package/dist/ui/components/ChatFooterPane.d.ts.map +1 -1
  73. package/dist/ui/components/ChatInputStack.d.ts +1 -1
  74. package/dist/ui/components/ChatInputStack.d.ts.map +1 -1
  75. package/dist/ui/components/ChatLayout.d.ts +4 -4
  76. package/dist/ui/components/ChatLayout.d.ts.map +1 -1
  77. package/dist/ui/components/ChatLivePane.d.ts +1 -1
  78. package/dist/ui/components/ChatLivePane.d.ts.map +1 -1
  79. package/dist/ui/components/ChatScreen.d.ts +1 -1
  80. package/dist/ui/components/ChatScreen.d.ts.map +1 -1
  81. package/dist/ui/components/ChatStatusRow.d.ts +1 -1
  82. package/dist/ui/components/ChatStatusRow.d.ts.map +1 -1
  83. package/dist/ui/components/CompactionNotice.d.ts +3 -2
  84. package/dist/ui/components/CompactionNotice.d.ts.map +1 -1
  85. package/dist/ui/components/DiffFrame.d.ts +1 -1
  86. package/dist/ui/components/DiffFrame.d.ts.map +1 -1
  87. package/dist/ui/components/DiffView.d.ts +2 -1
  88. package/dist/ui/components/DiffView.d.ts.map +1 -1
  89. package/dist/ui/components/Footer.d.ts +2 -1
  90. package/dist/ui/components/Footer.d.ts.map +1 -1
  91. package/dist/ui/components/FooterStatusRow.d.ts +2 -1
  92. package/dist/ui/components/FooterStatusRow.d.ts.map +1 -1
  93. package/dist/ui/components/FullScreenOverlayRouter.d.ts +2 -1
  94. package/dist/ui/components/FullScreenOverlayRouter.d.ts.map +1 -1
  95. package/dist/ui/components/IdealHookMessage.d.ts +1 -1
  96. package/dist/ui/components/IdealHookMessage.d.ts.map +1 -1
  97. package/dist/ui/components/InputArea.d.ts +1 -1
  98. package/dist/ui/components/InputArea.d.ts.map +1 -1
  99. package/dist/ui/components/LiveToolPanel.d.ts +2 -1
  100. package/dist/ui/components/LiveToolPanel.d.ts.map +1 -1
  101. package/dist/ui/components/Markdown.d.ts +2 -2
  102. package/dist/ui/components/Markdown.d.ts.map +1 -1
  103. package/dist/ui/components/ModelSelector.d.ts +2 -1
  104. package/dist/ui/components/ModelSelector.d.ts.map +1 -1
  105. package/dist/ui/components/Overlay.d.ts +1 -1
  106. package/dist/ui/components/Overlay.d.ts.map +1 -1
  107. package/dist/ui/components/PixelOverlay.d.ts +2 -1
  108. package/dist/ui/components/PixelOverlay.d.ts.map +1 -1
  109. package/dist/ui/components/PlanApproval.d.ts +2 -1
  110. package/dist/ui/components/PlanApproval.d.ts.map +1 -1
  111. package/dist/ui/components/PlanBanner.d.ts +2 -1
  112. package/dist/ui/components/PlanBanner.d.ts.map +1 -1
  113. package/dist/ui/components/PlanModeLogo.d.ts +2 -1
  114. package/dist/ui/components/PlanModeLogo.d.ts.map +1 -1
  115. package/dist/ui/components/PlanOverlay.d.ts +3 -2
  116. package/dist/ui/components/PlanOverlay.d.ts.map +1 -1
  117. package/dist/ui/components/PlanProgress.d.ts +2 -1
  118. package/dist/ui/components/PlanProgress.d.ts.map +1 -1
  119. package/dist/ui/components/QueueIndicator.d.ts +2 -1
  120. package/dist/ui/components/QueueIndicator.d.ts.map +1 -1
  121. package/dist/ui/components/RewindOverlay.d.ts +2 -1
  122. package/dist/ui/components/RewindOverlay.d.ts.map +1 -1
  123. package/dist/ui/components/SelectList.d.ts +2 -1
  124. package/dist/ui/components/SelectList.d.ts.map +1 -1
  125. package/dist/ui/components/ServerToolExecution.d.ts +2 -1
  126. package/dist/ui/components/ServerToolExecution.d.ts.map +1 -1
  127. package/dist/ui/components/SessionSelector.d.ts +2 -1
  128. package/dist/ui/components/SessionSelector.d.ts.map +1 -1
  129. package/dist/ui/components/SessionSummary.d.ts +2 -1
  130. package/dist/ui/components/SessionSummary.d.ts.map +1 -1
  131. package/dist/ui/components/SettingsSelector.d.ts +2 -1
  132. package/dist/ui/components/SettingsSelector.d.ts.map +1 -1
  133. package/dist/ui/components/SkillsOverlay.d.ts +2 -1
  134. package/dist/ui/components/SkillsOverlay.d.ts.map +1 -1
  135. package/dist/ui/components/SlashCommandMenu.d.ts +2 -1
  136. package/dist/ui/components/SlashCommandMenu.d.ts.map +1 -1
  137. package/dist/ui/components/SlashStyledSelectList.d.ts +2 -1
  138. package/dist/ui/components/SlashStyledSelectList.d.ts.map +1 -1
  139. package/dist/ui/components/Spinner.d.ts +2 -1
  140. package/dist/ui/components/Spinner.d.ts.map +1 -1
  141. package/dist/ui/components/StreamingArea.d.ts +1 -1
  142. package/dist/ui/components/StreamingArea.d.ts.map +1 -1
  143. package/dist/ui/components/SubAgentPanel.d.ts +2 -1
  144. package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
  145. package/dist/ui/components/TaskPickerMenu.d.ts +2 -1
  146. package/dist/ui/components/TaskPickerMenu.d.ts.map +1 -1
  147. package/dist/ui/components/ThemeSelector.d.ts +2 -1
  148. package/dist/ui/components/ThemeSelector.d.ts.map +1 -1
  149. package/dist/ui/components/ThinkingBlock.d.ts +1 -1
  150. package/dist/ui/components/ThinkingBlock.d.ts.map +1 -1
  151. package/dist/ui/components/ToolExecution.d.ts +2 -1
  152. package/dist/ui/components/ToolExecution.d.ts.map +1 -1
  153. package/dist/ui/components/ToolGroupExecution.d.ts +2 -1
  154. package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -1
  155. package/dist/ui/components/TranscriptViewport.d.ts +1 -1
  156. package/dist/ui/components/TranscriptViewport.d.ts.map +1 -1
  157. package/dist/ui/components/UserMessage.d.ts +2 -1
  158. package/dist/ui/components/UserMessage.d.ts.map +1 -1
  159. package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
  160. package/dist/ui/hooks/useAgentLoop.js +5 -1
  161. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  162. package/dist/ui/transcript/MiscRows.d.ts +8 -7
  163. package/dist/ui/transcript/MiscRows.d.ts.map +1 -1
  164. package/dist/ui/transcript/StatusRow.d.ts +1 -1
  165. package/dist/ui/transcript/StatusRow.d.ts.map +1 -1
  166. package/dist/ui/transcript/ToolRows.d.ts +7 -6
  167. package/dist/ui/transcript/ToolRows.d.ts.map +1 -1
  168. package/dist/ui/transcript/TranscriptItemFrame.d.ts +1 -1
  169. package/dist/ui/transcript/TranscriptItemFrame.d.ts.map +1 -1
  170. package/package.json +6 -4
@@ -0,0 +1,342 @@
1
+ /**
2
+ * Hashline (hash-anchored edits) vs current string-match edit — real-API
3
+ * measurement of whether Feature #2 is worth building.
4
+ *
5
+ * Two strategies produce the SAME edit against the SAME file, measured against
6
+ * live gpt-5.5:
7
+ *
8
+ * BASELINE (what ggcoder does today): the model emits { old_text, new_text }
9
+ * edits where old_text must be copied VERBATIM from the file with enough
10
+ * surrounding context to match uniquely (this is exactly our edit tool's
11
+ * contract — see tools/edit.ts). The reproduced context is what costs output
12
+ * tokens, and a non-unique / drifted old_text is what causes apply failures.
13
+ *
14
+ * HASHLINE (the proposed feature): every line is shown with a short content-
15
+ * hash anchor (`a3f1│<line>`). The model references anchors instead of
16
+ * reproducing text — it emits { from, to, lines } where from/to are anchors.
17
+ * Anchors are unique by construction, so an edit either resolves exactly or is
18
+ * rejected (never silently corrupts), and the model writes far fewer tokens.
19
+ *
20
+ * We measure, per task: model OUTPUT tokens (the headline -61% claim), whether
21
+ * the edit applied cleanly + produced the correct file, and an anchor-uniqueness
22
+ * / safety check. Edits are graded deterministically — no second model needed.
23
+ *
24
+ * Usage:
25
+ * npx tsx src/core/hashline-edit-benchmark.ts
26
+ *
27
+ * Env overrides:
28
+ * GG_HL_PROVIDER / GG_HL_MODEL (default openai / gpt-5.5)
29
+ * GG_HL_REPEAT (runs per task, default 1 — raise to average noise)
30
+ */
31
+ import { stream } from "@kenkaiiii/gg-ai";
32
+ import { AuthStorage } from "./auth-storage.js";
33
+ import { anchorFile } from "./hashline.js";
34
+ export function genFile(lines) {
35
+ const head = [
36
+ `import { EventEmitter } from "node:events";`,
37
+ `import { performance } from "node:perf_hooks";`,
38
+ ``,
39
+ `/** Auto-generated module with ${lines} lines for benchmarking. */`,
40
+ `export interface Config {`,
41
+ ` retries: number;`,
42
+ ` timeoutMs: number;`,
43
+ ` label: string;`,
44
+ `}`,
45
+ ``,
46
+ `const DEFAULT_TIMEOUT = 3000;`,
47
+ ``,
48
+ ];
49
+ const body = [];
50
+ let n = 0;
51
+ while (head.length + body.length < lines - 12) {
52
+ body.push(`export function task${n}(x: number): number {`, ` // step ${n}`, ` const y = x * ${n + 1} + DEFAULT_TIMEOUT;`, ` return y - ${n};`, `}`, ``);
53
+ n++;
54
+ }
55
+ const tail = [
56
+ `export function computeTimeout(cfg: Config): number {`,
57
+ ` return cfg.timeoutMs > 0 ? cfg.timeoutMs : DEFAULT_TIMEOUT;`,
58
+ `}`,
59
+ ``,
60
+ `export const SENTINEL_TAIL = "anchor_${lines}_end";`,
61
+ ``,
62
+ ];
63
+ return [...head, ...body, ...tail].join("\n");
64
+ }
65
+ export function buildTasks() {
66
+ const sizes = [
67
+ { name: "small", lines: 40 },
68
+ { name: "medium", lines: 160 },
69
+ { name: "large", lines: 420 },
70
+ ];
71
+ return sizes.map((s) => ({
72
+ name: s.name,
73
+ approxLines: s.lines,
74
+ file: genFile(s.lines),
75
+ instruction: "Change `computeTimeout` so that when `cfg.retries` is greater than 0 it returns " +
76
+ "`cfg.timeoutMs * cfg.retries` (capped at 30000), otherwise it keeps the existing behaviour. " +
77
+ "Also add a one-line JSDoc comment above the function describing it.",
78
+ mustContain: ["cfg.timeoutMs * cfg.retries", "30000", "computeTimeout"],
79
+ mustPreserve: ["DEFAULT_TIMEOUT = 3000", `SENTINEL_TAIL = "anchor_${s.lines}_end"`, "task0"],
80
+ }));
81
+ }
82
+ // ── Hashline anchoring lives in ./hashline.ts (shared with the read/edit tools) ──
83
+ // ── Prompts ─────────────────────────────────────────────────
84
+ function baselinePrompt(file, instruction) {
85
+ return [
86
+ {
87
+ role: "system",
88
+ content: "You are a coding agent's edit engine. Apply the edit by emitting search/replace " +
89
+ "operations. Output ONLY raw JSON, no markdown fence, in this shape:\n" +
90
+ `{"edits":[{"old_text":"<verbatim slice from the file>","new_text":"<replacement>"}]}\n` +
91
+ "RULES: old_text MUST be copied verbatim from the file, including enough surrounding " +
92
+ "context lines that it matches EXACTLY ONE location. Do not paraphrase. Do not explain.",
93
+ },
94
+ { role: "user", content: `FILE:\n\`\`\`ts\n${file}\n\`\`\`\n\nEDIT: ${instruction}` },
95
+ ];
96
+ }
97
+ function hashlinePrompt(rendered, instruction) {
98
+ return [
99
+ {
100
+ role: "system",
101
+ content: "You are a coding agent's edit engine. The file is shown with a unique 4-char anchor " +
102
+ "before each line as `anchor│code`. The anchors are NOT part of the file. Apply the edit " +
103
+ "by referencing anchors instead of retyping code. Output ONLY raw JSON, no markdown fence:\n" +
104
+ `{"edits":[{"from":"<anchor>","to":"<anchor>","lines":["<new line 1>","<new line 2>"]}]}\n` +
105
+ "Each edit REPLACES the inclusive span of lines from anchor `from` to anchor `to` with the " +
106
+ "`lines` array (write the full replacement lines, with correct indentation; never include the " +
107
+ "anchor prefixes). For a single-line change, set from === to. To ADD new lines (e.g. a JSDoc " +
108
+ "comment above a function), pick the existing line you are augmenting as both from and to, and " +
109
+ "put [new line(s), ...that original line] in `lines`. Every `from`/`to` MUST be an anchor that " +
110
+ "appears in the file. Do not explain.",
111
+ },
112
+ { role: "user", content: `FILE (anchored):\n${rendered}\n\nEDIT: ${instruction}` },
113
+ ];
114
+ }
115
+ export function stripFence(s) {
116
+ return s
117
+ .trim()
118
+ .replace(/^```[a-z]*\n?/i, "")
119
+ .replace(/\n?```$/i, "")
120
+ .trim();
121
+ }
122
+ export function checkAnchors(file, task) {
123
+ for (const s of task.mustContain)
124
+ if (!file.includes(s))
125
+ return false;
126
+ for (const s of task.mustPreserve)
127
+ if (!file.includes(s))
128
+ return false;
129
+ return true;
130
+ }
131
+ export function applyBaseline(raw, task) {
132
+ let parsed;
133
+ try {
134
+ parsed = JSON.parse(stripFence(raw));
135
+ }
136
+ catch {
137
+ return { applied: false, correct: false, ambiguousEdits: 0, parsedEdits: 0 };
138
+ }
139
+ const edits = parsed.edits ?? [];
140
+ let file = task.file;
141
+ let applied = edits.length > 0;
142
+ let ambiguous = 0;
143
+ for (const e of edits) {
144
+ const occ = file.split(e.old_text).length - 1;
145
+ if (occ === 1) {
146
+ file = file.replace(e.old_text, e.new_text);
147
+ }
148
+ else {
149
+ // 0 = not found (drift/paraphrase), >1 = non-unique → our real tool rejects both.
150
+ ambiguous++;
151
+ applied = false;
152
+ }
153
+ }
154
+ return {
155
+ applied,
156
+ correct: applied && checkAnchors(file, task),
157
+ ambiguousEdits: ambiguous,
158
+ parsedEdits: edits.length,
159
+ };
160
+ }
161
+ export function applyHashline(raw, task, anchored) {
162
+ let parsed;
163
+ try {
164
+ parsed = JSON.parse(stripFence(raw));
165
+ }
166
+ catch {
167
+ return { applied: false, correct: false, ambiguousEdits: 0, parsedEdits: 0 };
168
+ }
169
+ const edits = parsed.edits ?? [];
170
+ // Resolve every anchor first; reject the whole patch if any is unresolvable
171
+ // (this is the corruption-avoidance property — anchors must hit exactly once).
172
+ let ambiguous = 0;
173
+ const resolved = [];
174
+ for (const e of edits) {
175
+ const from = anchored.anchorToIndex.get(e.from);
176
+ const to = anchored.anchorToIndex.get(e.to);
177
+ if (from === undefined || to === undefined || from > to) {
178
+ ambiguous++;
179
+ continue;
180
+ }
181
+ resolved.push({ from, to, lines: e.lines ?? [] });
182
+ }
183
+ if (resolved.length !== edits.length || edits.length === 0) {
184
+ return { applied: false, correct: false, ambiguousEdits: ambiguous, parsedEdits: edits.length };
185
+ }
186
+ // Apply bottom-up so earlier indices stay valid.
187
+ resolved.sort((a, b) => b.from - a.from);
188
+ const out = [...anchored.lines];
189
+ for (const r of resolved)
190
+ out.splice(r.from, r.to - r.from + 1, ...r.lines);
191
+ const file = out.join("\n");
192
+ return {
193
+ applied: true,
194
+ correct: checkAnchors(file, task),
195
+ ambiguousEdits: ambiguous,
196
+ parsedEdits: edits.length,
197
+ };
198
+ }
199
+ function sleep(ms) {
200
+ return new Promise((r) => setTimeout(r, ms));
201
+ }
202
+ async function call(provider, model, c, messages, maxTokens) {
203
+ let lastErr;
204
+ for (let attempt = 0; attempt < 4; attempt++) {
205
+ try {
206
+ return await callOnce(provider, model, c, messages, maxTokens);
207
+ }
208
+ catch (err) {
209
+ lastErr = err;
210
+ await sleep(2000 * (attempt + 1));
211
+ }
212
+ }
213
+ throw lastErr;
214
+ }
215
+ async function callOnce(provider, model, c, messages, maxTokens) {
216
+ const start = Date.now();
217
+ let text = "";
218
+ const result = stream({
219
+ provider: provider,
220
+ model,
221
+ messages,
222
+ maxTokens,
223
+ apiKey: c.apiKey,
224
+ baseUrl: c.baseUrl,
225
+ accountId: c.accountId,
226
+ });
227
+ for await (const event of result) {
228
+ if (event.type === "text_delta")
229
+ text += event.text;
230
+ }
231
+ const response = await result.response;
232
+ const content = response.message.content;
233
+ const finalText = typeof content === "string"
234
+ ? content
235
+ : content
236
+ .filter((p) => p.type === "text")
237
+ .map((p) => p.text ?? "")
238
+ .join("");
239
+ return {
240
+ text: finalText || text,
241
+ outputTokens: response.usage.outputTokens,
242
+ inputTokens: response.usage.inputTokens,
243
+ wallMs: Date.now() - start,
244
+ };
245
+ }
246
+ async function main() {
247
+ const provider = process.env.GG_HL_PROVIDER ?? "openai";
248
+ const model = process.env.GG_HL_MODEL ?? "gpt-5.5";
249
+ const repeat = Math.max(1, parseInt(process.env.GG_HL_REPEAT ?? "1", 10));
250
+ const auth = new AuthStorage();
251
+ await auth.load();
252
+ const cr = await auth.resolveCredentials(provider);
253
+ const creds = { apiKey: cr.accessToken, baseUrl: cr.baseUrl, accountId: cr.accountId };
254
+ console.log(`\n🔗 Hashline edit benchmark — ${provider}/${model} (repeat ${repeat})\n`);
255
+ const tasks = buildTasks();
256
+ const rows = [];
257
+ for (const task of tasks) {
258
+ const anchored = anchorFile(task.file);
259
+ const agg = {
260
+ task: task.name,
261
+ lines: task.approxLines,
262
+ baseOutTok: 0,
263
+ baseInTok: 0,
264
+ baseOk: true,
265
+ baseAmbiguous: 0,
266
+ hlOutTok: 0,
267
+ hlInTok: 0,
268
+ hlOk: true,
269
+ hlAmbiguous: 0,
270
+ };
271
+ let baseOkCount = 0;
272
+ let hlOkCount = 0;
273
+ for (let r = 0; r < repeat; r++) {
274
+ process.stdout.write(`▶ ${task.name} (${task.approxLines} lines) run ${r + 1}/${repeat}\n`);
275
+ await sleep(1500);
276
+ const base = await call(provider, model, creds, baselinePrompt(task.file, task.instruction), 4096);
277
+ const baseOut = applyBaseline(base.text, task);
278
+ agg.baseOutTok += base.outputTokens;
279
+ agg.baseInTok += base.inputTokens;
280
+ agg.baseAmbiguous += baseOut.ambiguousEdits;
281
+ if (baseOut.correct)
282
+ baseOkCount++;
283
+ process.stdout.write(` baseline: ${base.outputTokens} out tok | ${baseOut.correct ? "OK" : "FAIL"}` +
284
+ `${baseOut.ambiguousEdits ? ` (${baseOut.ambiguousEdits} non-unique)` : ""}\n`);
285
+ await sleep(1500);
286
+ const hl = await call(provider, model, creds, hashlinePrompt(anchored.rendered, task.instruction), 2048);
287
+ const hlOut = applyHashline(hl.text, task, anchored);
288
+ agg.hlOutTok += hl.outputTokens;
289
+ agg.hlInTok += hl.inputTokens;
290
+ agg.hlAmbiguous += hlOut.ambiguousEdits;
291
+ if (hlOut.correct)
292
+ hlOkCount++;
293
+ process.stdout.write(` hashline: ${hl.outputTokens} out tok | ${hlOut.correct ? "OK" : "FAIL"}` +
294
+ `${hlOut.ambiguousEdits ? ` (${hlOut.ambiguousEdits} unresolved)` : ""}\n\n`);
295
+ }
296
+ agg.baseOutTok = Math.round(agg.baseOutTok / repeat);
297
+ agg.baseInTok = Math.round(agg.baseInTok / repeat);
298
+ agg.hlOutTok = Math.round(agg.hlOutTok / repeat);
299
+ agg.hlInTok = Math.round(agg.hlInTok / repeat);
300
+ agg.baseOk = baseOkCount === repeat;
301
+ agg.hlOk = hlOkCount === repeat;
302
+ rows.push(agg);
303
+ }
304
+ // ── Report ──
305
+ console.log("══════════════════════ RESULTS ══════════════════════\n");
306
+ console.log("Task Lines | base out-tok | hashline out-tok | Δ out | in-tok base/hl | OK base/hl");
307
+ for (const r of rows) {
308
+ const delta = r.baseOutTok > 0 ? ((r.baseOutTok - r.hlOutTok) / r.baseOutTok) * 100 : 0;
309
+ console.log(`${r.task.padEnd(7)} ${String(r.lines).padStart(4)} | ` +
310
+ `${String(r.baseOutTok).padStart(12)} | ${String(r.hlOutTok).padStart(16)} | ` +
311
+ `${((delta >= 0 ? "-" : "+") + Math.abs(delta).toFixed(0) + "%").padStart(7)} | ` +
312
+ `${String(r.baseInTok).padStart(6)}/${String(r.hlInTok).padStart(6)} | ` +
313
+ `${r.baseOk ? "OK" : "FAIL"}/${r.hlOk ? "OK" : "FAIL"}`);
314
+ }
315
+ const sum = (f) => rows.reduce((s, r) => s + f(r), 0);
316
+ const baseOut = sum((r) => r.baseOutTok);
317
+ const hlOut = sum((r) => r.hlOutTok);
318
+ const baseIn = sum((r) => r.baseInTok);
319
+ const hlIn = sum((r) => r.hlInTok);
320
+ console.log(`\nOutput tokens: baseline ${baseOut} vs hashline ${hlOut} ` +
321
+ `→ ${baseOut > hlOut ? `${(((baseOut - hlOut) / baseOut) * 100).toFixed(0)}% fewer` : "no win"} ` +
322
+ `(claim from oh-my-pi: ~61% fewer)`);
323
+ console.log(`Input tokens: baseline ${baseIn} vs hashline ${hlIn} ` +
324
+ `(hashline adds the anchor column: ${hlIn > baseIn ? `+${(((hlIn - baseIn) / baseIn) * 100).toFixed(0)}% input` : "no overhead"})`);
325
+ console.log(`Correctness: baseline ${rows.filter((r) => r.baseOk).length}/${rows.length}, ` +
326
+ `hashline ${rows.filter((r) => r.hlOk).length}/${rows.length}`);
327
+ console.log(`Locator misses: baseline ${sum((r) => r.baseAmbiguous)} non-unique/not-found, ` +
328
+ `hashline ${sum((r) => r.hlAmbiguous)} unresolved (rejected before corruption)`);
329
+ console.log(`\nVerdict: hashline wins if output-token drop is large AND correctness ≥ baseline. ` +
330
+ `Net input overhead from the anchor column is the cost to weigh against it.\n`);
331
+ }
332
+ // Run when executed directly (not when imported by tests).
333
+ const isDirectRun = process.argv[1]?.endsWith("hashline-edit-benchmark.ts") ||
334
+ process.argv[1]?.endsWith("hashline-edit-benchmark.js") ||
335
+ process.argv[1]?.endsWith("hashline-edit-benchmark");
336
+ if (isDirectRun) {
337
+ main().catch((err) => {
338
+ console.error("Benchmark failed:", err);
339
+ process.exit(1);
340
+ });
341
+ }
342
+ //# sourceMappingURL=hashline-edit-benchmark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashline-edit-benchmark.js","sourceRoot":"","sources":["../../src/core/hashline-edit-benchmark.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hashline-edit-benchmark.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashline-edit-benchmark.test.d.ts","sourceRoot":"","sources":["../../src/core/hashline-edit-benchmark.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,141 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { lineHash, anchorFile } from "./hashline.js";
3
+ import { applyBaseline, applyHashline, checkAnchors, stripFence, genFile, buildTasks, } from "./hashline-edit-benchmark.js";
4
+ /**
5
+ * These tests prove the DETERMINISTIC half of the hashline benchmark — the
6
+ * apply + grading logic that decides OK / FAIL / corruption. The live-model
7
+ * numbers are only trustworthy if this logic is correct, so we exercise it from
8
+ * every angle: anchor uniqueness, corruption-avoidance, multi-edit ordering,
9
+ * malformed input, and the string-match baseline's ambiguity handling.
10
+ */
11
+ /** Build a minimal EditTask around a hand-written file for precise assertions. */
12
+ function taskFor(file, mustContain, mustPreserve) {
13
+ return {
14
+ name: "t",
15
+ approxLines: file.split("\n").length,
16
+ file,
17
+ instruction: "",
18
+ mustContain,
19
+ mustPreserve,
20
+ };
21
+ }
22
+ const FN_FILE = [
23
+ "export function computeTimeout(cfg: Config): number {",
24
+ " return cfg.timeoutMs > 0 ? cfg.timeoutMs : DEFAULT_TIMEOUT;",
25
+ "}",
26
+ ].join("\n");
27
+ /** The anchor for a given 0-based line index of a file. */
28
+ function anchorAt(file, index) {
29
+ return lineHash(file.split("\n")[index], index);
30
+ }
31
+ describe("hashline anchoring", () => {
32
+ it("1. folds line position into the hash so identical lines get distinct anchors", () => {
33
+ // Two identical lines at different positions must NOT collide.
34
+ expect(lineHash(" return x;", 3)).not.toBe(lineHash(" return x;", 9));
35
+ // Same content + same position is stable (deterministic).
36
+ expect(lineHash(" return x;", 3)).toBe(lineHash(" return x;", 3));
37
+ });
38
+ it("2. gives every line a unique resolvable anchor — even repeated blank lines", () => {
39
+ const file = ["a", "", "b", "", "c"].join("\n"); // two blank lines
40
+ const a = anchorFile(file);
41
+ expect(a.ambiguous.size).toBe(0);
42
+ expect(a.anchorToIndex.size).toBe(5);
43
+ expect([...a.anchorToIndex.values()].sort((x, y) => x - y)).toEqual([0, 1, 2, 3, 4]);
44
+ });
45
+ it("3. renders as `anchor│line` and the line column reconstructs the file", () => {
46
+ const a = anchorFile(FN_FILE);
47
+ const stripped = a.rendered
48
+ .split("\n")
49
+ .map((l) => l.slice(l.indexOf("│") + 1))
50
+ .join("\n");
51
+ expect(stripped).toBe(FN_FILE);
52
+ });
53
+ });
54
+ describe("applyHashline (proposed)", () => {
55
+ it("4. applies a valid single-line edit and grades it correct", () => {
56
+ const anchored = anchorFile(FN_FILE);
57
+ const anchor = anchorAt(FN_FILE, 1); // the `return ...` line
58
+ const raw = JSON.stringify({
59
+ edits: [
60
+ {
61
+ from: anchor,
62
+ to: anchor,
63
+ lines: [
64
+ " return cfg.retries > 0 ? Math.min(cfg.timeoutMs * cfg.retries, 30000) : (cfg.timeoutMs > 0 ? cfg.timeoutMs : DEFAULT_TIMEOUT);",
65
+ ],
66
+ },
67
+ ],
68
+ });
69
+ const task = taskFor(FN_FILE, ["cfg.timeoutMs * cfg.retries", "30000"], ["computeTimeout", "DEFAULT_TIMEOUT"]);
70
+ const out = applyHashline(raw, task, anchored);
71
+ expect(out.applied).toBe(true);
72
+ expect(out.correct).toBe(true);
73
+ expect(out.ambiguousEdits).toBe(0);
74
+ });
75
+ it("5. rejects an edit whose anchor is not in the file — never corrupts", () => {
76
+ const anchored = anchorFile(FN_FILE);
77
+ const raw = JSON.stringify({ edits: [{ from: "0000", to: "0000", lines: ["// hijacked"] }] });
78
+ const task = taskFor(FN_FILE, [], ["computeTimeout"]);
79
+ const out = applyHashline(raw, task, anchored);
80
+ expect(out.applied).toBe(false);
81
+ expect(out.correct).toBe(false);
82
+ expect(out.ambiguousEdits).toBe(1);
83
+ });
84
+ it("6. rejects a reversed range (from after to)", () => {
85
+ const anchored = anchorFile(FN_FILE);
86
+ const raw = JSON.stringify({
87
+ edits: [{ from: anchorAt(FN_FILE, 2), to: anchorAt(FN_FILE, 0), lines: ["x"] }],
88
+ });
89
+ const out = applyHashline(raw, taskFor(FN_FILE, [], []), anchored);
90
+ expect(out.applied).toBe(false);
91
+ });
92
+ it("7. applies multiple edits bottom-up so earlier indices stay valid", () => {
93
+ const file = ["const a = 1;", "const b = 2;", "const c = 3;"].join("\n");
94
+ const anchored = anchorFile(file);
95
+ const raw = JSON.stringify({
96
+ edits: [
97
+ { from: anchorAt(file, 0), to: anchorAt(file, 0), lines: ["const a = 10;"] },
98
+ { from: anchorAt(file, 2), to: anchorAt(file, 2), lines: ["const c = 30;"] },
99
+ ],
100
+ });
101
+ const out = applyHashline(raw, taskFor(file, ["const a = 10;", "const c = 30;"], ["const b = 2;"]), anchored);
102
+ expect(out.applied).toBe(true);
103
+ expect(out.correct).toBe(true);
104
+ });
105
+ it("8. returns a clean failure on malformed JSON instead of throwing", () => {
106
+ const anchored = anchorFile(FN_FILE);
107
+ expect(() => applyHashline("not json {", taskFor(FN_FILE, [], []), anchored)).not.toThrow();
108
+ const out = applyHashline("not json {", taskFor(FN_FILE, [], []), anchored);
109
+ expect(out.applied).toBe(false);
110
+ expect(out.parsedEdits).toBe(0);
111
+ });
112
+ });
113
+ describe("applyBaseline (current string-match) + helpers", () => {
114
+ it("9. applies a unique old_text but rejects a non-unique one", () => {
115
+ const unique = taskFor("const x = 1;\nconst y = 2;\n", ["const x = 100;"], ["const y = 2;"]);
116
+ const okRaw = JSON.stringify({
117
+ edits: [{ old_text: "const x = 1;", new_text: "const x = 100;" }],
118
+ });
119
+ expect(applyBaseline(okRaw, unique).applied).toBe(true);
120
+ // Same literal appears twice → the real edit tool refuses; so must we.
121
+ const dup = taskFor("const x = 1;\nconst y = 2;\nconst x = 1;\n", [], []);
122
+ const dupRaw = JSON.stringify({
123
+ edits: [{ old_text: "const x = 1;", new_text: "const x = 9;" }],
124
+ });
125
+ const out = applyBaseline(dupRaw, dup);
126
+ expect(out.applied).toBe(false);
127
+ expect(out.ambiguousEdits).toBe(1);
128
+ });
129
+ it("10. flags not-found old_text, strips code fences, and grades generated tasks", () => {
130
+ // Paraphrased / drifted old_text that isn't in the file → rejected.
131
+ const notFound = JSON.stringify({ edits: [{ old_text: "const z = 0;", new_text: "x" }] });
132
+ expect(applyBaseline(notFound, taskFor("const a = 1;\n", [], [])).applied).toBe(false);
133
+ // stripFence removes ```ts ... ``` wrappers the model sometimes adds.
134
+ expect(stripFence('```ts\n{"edits":[]}\n```')).toBe('{"edits":[]}');
135
+ // The generator + grader agree: a freshly generated file preserves its anchors.
136
+ const task = buildTasks()[0];
137
+ expect(genFile(task.approxLines)).toContain("computeTimeout");
138
+ expect(checkAnchors(task.file, taskFor(task.file, [], task.mustPreserve))).toBe(true);
139
+ });
140
+ });
141
+ //# sourceMappingURL=hashline-edit-benchmark.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashline-edit-benchmark.test.js","sourceRoot":"","sources":["../../src/core/hashline-edit-benchmark.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 4-hex-char anchor for a line. Position is folded into the hash so anchors are
3
+ * UNIQUE by construction (blank lines and repeated lines no longer collide).
4
+ * Resolution stays O(1) via a lookup map. `index` is the 0-based line index.
5
+ */
6
+ export declare function lineHash(line: string, index: number): string;
7
+ /** File rendered with `anchor│line` prefixes for the model to read. */
8
+ export declare function renderWithAnchors(file: string): string;
9
+ export interface AnchoredFile {
10
+ /** File rendered with `anchor│line` prefixes for the model to read. */
11
+ rendered: string;
12
+ /** anchor → line index (0-based). Only UNIQUE anchors are resolvable. */
13
+ anchorToIndex: Map<string, number>;
14
+ /** anchors that collided (ambiguous → unresolvable, like a stale-file reject). */
15
+ ambiguous: Set<string>;
16
+ lines: string[];
17
+ }
18
+ export declare function anchorFile(file: string): AnchoredFile;
19
+ /**
20
+ * True when the line at `index` (0-based) still hashes to `hash`. Out-of-range
21
+ * indices return false. This is the staleness gate the edit tool uses.
22
+ */
23
+ export declare function verifyAnchor(lines: string[], index: number, hash: string): boolean;
24
+ /** Optional per-edit anchor guard: pin both endpoints of an edit by line+hash. */
25
+ export interface EditAnchor {
26
+ /** 1-based line number of the first line of the edited span. */
27
+ start_line: number;
28
+ /** Content anchor of the first line (from a `read` with `anchors: true`). */
29
+ start_hash: string;
30
+ /** 1-based line number of the last line of the edited span. */
31
+ end_line: number;
32
+ /** Content anchor of the last line. */
33
+ end_hash: string;
34
+ }
35
+ export type AnchorFailure = "out_of_range" | "hash_mismatch" | "reversed";
36
+ export interface AnchorResolution {
37
+ ok: boolean;
38
+ /** 0-based index of the first line (only when ok). */
39
+ startIndex?: number;
40
+ /** 0-based index of the last line (only when ok). */
41
+ endIndex?: number;
42
+ reason?: AnchorFailure;
43
+ }
44
+ /**
45
+ * Resolve an anchor against the current file lines (0-based). Rejects the edit
46
+ * if either endpoint is out of range, the range is reversed, or either hash no
47
+ * longer matches — the corruption-avoidance property.
48
+ */
49
+ export declare function resolveAnchoredEdit(lines: string[], anchor: EditAnchor): AnchorResolution;
50
+ //# sourceMappingURL=hashline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hashline.d.ts","sourceRoot":"","sources":["../../src/core/hashline.ts"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,uEAAuE;AACvE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKtD;AAED,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,kFAAkF;IAClF,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAiBrD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGlF;AAED,kFAAkF;AAClF,MAAM,WAAW,UAAU;IACzB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,eAAe,GAAG,UAAU,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAgBzF"}