@esso0428/pi-subagents 0.15.13 → 0.15.15

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 (41) hide show
  1. package/CHANGELOG.md +6 -31
  2. package/dist/ui/agent-widget.d.ts +8 -0
  3. package/dist/ui/agent-widget.d.ts.map +1 -1
  4. package/dist/ui/agent-widget.js +40 -12
  5. package/dist/ui/agent-widget.js.map +1 -1
  6. package/dist/ui/ccstyle/diff/diff-renderer.d.ts.map +1 -1
  7. package/dist/ui/ccstyle/diff/diff-renderer.js +2 -4
  8. package/dist/ui/ccstyle/diff/diff-renderer.js.map +1 -1
  9. package/dist/ui/ccstyle/tool-renderer.d.ts.map +1 -1
  10. package/dist/ui/ccstyle/tool-renderer.js +17 -11
  11. package/dist/ui/ccstyle/tool-renderer.js.map +1 -1
  12. package/dist/ui/ccstyle/tool-result.d.ts +22 -10
  13. package/dist/ui/ccstyle/tool-result.d.ts.map +1 -1
  14. package/dist/ui/ccstyle/tool-result.js +160 -73
  15. package/dist/ui/ccstyle/tool-result.js.map +1 -1
  16. package/dist/ui/conversation-timeline.d.ts +16 -9
  17. package/dist/ui/conversation-timeline.d.ts.map +1 -1
  18. package/dist/ui/conversation-timeline.js +85 -61
  19. package/dist/ui/conversation-timeline.js.map +1 -1
  20. package/dist/ui/conversation-viewer.d.ts +4 -1
  21. package/dist/ui/conversation-viewer.d.ts.map +1 -1
  22. package/dist/ui/conversation-viewer.js +107 -110
  23. package/dist/ui/conversation-viewer.js.map +1 -1
  24. package/dist/write-execution.d.ts +0 -2
  25. package/dist/write-execution.d.ts.map +1 -1
  26. package/dist/write-execution.js +1 -5
  27. package/dist/write-execution.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/ui/agent-widget.ts +38 -12
  30. package/src/ui/ccstyle/diff/diff-renderer.ts +2 -7
  31. package/src/ui/ccstyle/tool-renderer.ts +23 -24
  32. package/src/ui/ccstyle/tool-result.ts +167 -107
  33. package/src/ui/conversation-timeline.ts +103 -62
  34. package/src/ui/conversation-viewer.ts +119 -124
  35. package/src/write-execution.ts +1 -7
  36. package/test/ui/agent-widget.test.ts +123 -0
  37. package/test/ui/conversation-diff.test.ts +1 -28
  38. package/test/ui/conversation-viewer.test.ts +181 -112
  39. package/test/write-execution.test.ts +0 -2
  40. package/.pi/tasks/tasks-01a09263-0ac6-77be-97f5-664b3ffea76b.json +0 -53
  41. package/.pi/workflows/index.json +0 -5
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, it, vi } from "vitest";
2
+ import type { TuiMouseEvent } from "@earendil-works/pi-tui";
2
3
  import { ConversationViewer, createStaticConversationSource } from "../../src/ui/conversation-viewer.js";
3
4
 
4
5
  const theme = {
@@ -28,6 +29,28 @@ function tui() {
28
29
  } as any;
29
30
  }
30
31
 
32
+ function mouse(type: TuiMouseEvent["type"], y: number, overrides: Partial<TuiMouseEvent> = {}): TuiMouseEvent {
33
+ return {
34
+ type,
35
+ button: "left",
36
+ x: 2,
37
+ y,
38
+ screenX: 2,
39
+ screenY: y,
40
+ width: 120,
41
+ height: 30,
42
+ shift: false,
43
+ alt: false,
44
+ ctrl: false,
45
+ ...overrides,
46
+ };
47
+ }
48
+
49
+ // The test record has the default invocation row, so timeline line 0 is viewer row 4.
50
+ function viewerY(timelineLine: number): number {
51
+ return 4 + timelineLine;
52
+ }
53
+
31
54
  describe("ConversationViewer", () => {
32
55
  it("renders assistant fenced code through the Markdown renderer", () => {
33
56
  const ui = tui();
@@ -85,8 +108,6 @@ describe("ConversationViewer", () => {
85
108
  expect(liveFooter).toContain("j/k scroll · J/K messages · [/] tools · w preview · g/G t M o O /n N q");
86
109
  expect(liveFooter).toContain("e steer");
87
110
  expect(liveFooter).toContain("x stop");
88
- live.handleInput("\r");
89
- expect(live.render(120).join("\n")).not.toContain("Enter send");
90
111
  live.handleInput("e");
91
112
  expect(live.render(120).join("\n")).toContain("Enter send");
92
113
  live.handleInput("\x1b");
@@ -152,78 +173,62 @@ describe("ConversationViewer", () => {
152
173
  vi.fn(),
153
174
  );
154
175
 
155
- const initial = viewer.render(120).join("\n");
156
- expect(initial).not.toContain("six");
157
- expect(initial).not.toContain("READ");
176
+ expect(viewer.render(120).join("\n")).not.toContain("six");
177
+ viewer.handleInput("\t");
158
178
  viewer.handleInput("\r");
159
179
  expect(viewer.render(120).join("\n")).toContain("six");
160
180
  });
161
181
 
162
- it("keeps J/K on user and assistant blocks while j/k remain line scrolling", () => {
182
+ it("toggles mouse expansion only on the normalized click event", () => {
163
183
  const viewer = new ConversationViewer(
164
184
  tui(),
165
185
  createStaticConversationSource([
166
- { role: "assistant", content: [{ type: "text", text: "first" }, { type: "toolCall", name: "read", arguments: { path: "x" } }] },
167
- { role: "toolResult", toolName: "read", content: [{ type: "text", text: "tool output" }] },
168
- { role: "assistant", content: [{ type: "text", text: "second" }] },
186
+ { role: "assistant", content: [{ type: "toolCall", name: "read", arguments: { path: "x" } }] },
187
+ { role: "toolResult", toolName: "read", content: [{ type: "text", text: "one\ntwo\nthree\nfour\nfive\nsix" }] },
169
188
  ] as any),
170
189
  record(),
171
190
  undefined,
172
191
  theme,
173
192
  vi.fn(),
174
193
  );
175
- viewer.render(120);
176
- viewer.handleInput("J");
177
- expect((viewer as any).currentBlock().role).toBe("assistant");
178
- expect((viewer as any).currentBlock().markdown).toBe("second");
179
- viewer.handleInput("K");
180
- expect((viewer as any).currentBlock().markdown).toBe("first");
194
+ expect(viewer.render(120).join("\n")).not.toContain("six");
195
+ const span = (viewer as any).timeline.getSnapshot().toolSpans[0];
196
+ expect(span.clickStartLine).toEqual(expect.any(Number));
197
+ const hintY = viewerY(span.clickStartLine);
198
+ expect(viewer.handleMouse(mouse("press", hintY))?.handled).toBe(true);
199
+ expect(viewer.render(120).join("\n")).not.toContain("six");
200
+ viewer.handleMouse(mouse("click", hintY, { clickCount: 1 }));
201
+ expect(viewer.render(120).join("\n")).toContain("six");
181
202
  });
182
203
 
183
- it("focuses nearby tools with [ and ] without changing line-scroll keys", () => {
204
+ it("focuses USER and ASSISTANT blocks from their rendered spans", () => {
184
205
  const viewer = new ConversationViewer(
185
206
  tui(),
186
207
  createStaticConversationSource([
187
- { role: "assistant", content: [{ type: "toolCall", name: "read", arguments: { path: "first" } }] },
188
- { role: "toolResult", toolName: "read", content: [{ type: "text", text: "first output" }] },
189
- { role: "assistant", content: [{ type: "text", text: "between" }, { type: "toolCall", name: "read", arguments: { path: "second" } }] },
190
- { role: "toolResult", toolName: "read", content: [{ type: "text", text: "second output" }] },
208
+ { role: "user", content: "user text" },
209
+ { role: "assistant", content: [{ type: "text", text: "assistant text" }] },
191
210
  ] as any),
192
211
  record(),
193
212
  undefined,
194
213
  theme,
195
214
  vi.fn(),
196
215
  );
197
- viewer.render(120);
198
- const timeline = (viewer as any).timeline;
199
- const ids = timeline.getSnapshot().interactiveToolIds;
200
- expect(ids).toHaveLength(2);
201
216
 
202
- viewer.handleInput("]");
203
- expect(timeline.getFocusedToolCallId()).toBe(ids[0]);
204
217
  viewer.render(120);
205
- expect((viewer as any).currentBlock().toolCallId).toBe(ids[0]);
206
- viewer.handleInput("]");
207
- expect(timeline.getFocusedToolCallId()).toBe(ids[1]);
208
- viewer.render(120);
209
- expect((viewer as any).currentBlock().toolCallId).toBe(ids[1]);
210
- viewer.handleInput("[");
211
- expect(timeline.getFocusedToolCallId()).toBe(ids[0]);
218
+ const timeline = (viewer as any).timeline;
219
+ const spans = timeline.getSnapshot().blockSpans;
220
+ expect(spans).toHaveLength(2);
221
+ viewer.handleMouse(mouse("click", viewerY(spans[1].startLine), { clickCount: 1 }));
222
+ expect((viewer as any).selectedMessageIdx).toBe(1);
223
+ expect(timeline.getFocusedToolCallId()).toBeUndefined();
224
+ viewer.handleMouse(mouse("click", viewerY(spans[0].startLine), { clickCount: 1 }));
225
+ expect((viewer as any).selectedMessageIdx).toBe(0);
226
+ expect(timeline.getFocusedToolCallId()).toBeUndefined();
212
227
  });
213
228
 
214
- it("opens a complete read-only tool preview with w for the focused tool", () => {
215
- const ui = tui();
216
- const custom = vi.fn((factory: any) => {
217
- const preview = factory(ui, theme, undefined, vi.fn());
218
- const rendered = preview.render(100).join("\\n");
219
- expect(rendered).toContain("Input");
220
- expect(rendered).toContain("Output");
221
- expect(rendered).toContain("one");
222
- expect(rendered).toContain("six");
223
- return Promise.resolve(undefined);
224
- });
229
+ it("focuses normal tool rows without expanding, while hint clicks expand", () => {
225
230
  const viewer = new ConversationViewer(
226
- ui,
231
+ tui(),
227
232
  createStaticConversationSource([
228
233
  { role: "assistant", content: [{ type: "toolCall", name: "read", arguments: { path: "x" } }] },
229
234
  { role: "toolResult", toolName: "read", content: [{ type: "text", text: "one\ntwo\nthree\nfour\nfive\nsix" }] },
@@ -232,81 +237,92 @@ describe("ConversationViewer", () => {
232
237
  undefined,
233
238
  theme,
234
239
  vi.fn(),
235
- undefined,
236
- undefined,
237
- undefined,
238
- { pi: {} as any, ctx: { ui: { custom } } as any, readOnly: true },
239
240
  );
240
- viewer.render(120);
241
- viewer.handleInput("]");
242
- viewer.handleInput("w");
243
- expect(custom).toHaveBeenCalledWith(expect.any(Function), expect.objectContaining({ overlay: true }));
241
+
242
+ expect(viewer.render(120).join("\n")).not.toContain("six");
243
+ const timeline = (viewer as any).timeline;
244
+ const initialSpan = timeline.getSnapshot().toolSpans[0];
245
+ const toolId = initialSpan.toolCallId;
246
+ const hintLine = initialSpan.clickStartLine as number;
247
+ const normalOutputLine = hintLine - 1;
248
+
249
+ const press = viewer.handleMouse(mouse("press", viewerY(hintLine)));
250
+ expect(press).toMatchObject({ handled: true, capture: true });
251
+ expect(timeline.getFocusedToolCallId()).toBeUndefined();
252
+ expect(viewer.render(120).join("\n")).not.toContain("six");
253
+
254
+ viewer.handleMouse(mouse("click", viewerY(normalOutputLine), { clickCount: 1 }));
255
+ expect(timeline.getFocusedToolCallId()).toBe(toolId);
256
+ expect((viewer as any).selectedMessageIdx).toBe(initialSpan.blockIndex);
257
+ expect(viewer.render(120).join("\n")).not.toContain("six");
258
+
259
+ const collapsedSpan = timeline.getSnapshot().toolSpans[0];
260
+ viewer.handleMouse(mouse("click", viewerY(collapsedSpan.clickStartLine as number), { clickCount: 1 }));
261
+ expect(timeline.getFocusedToolCallId()).toBe(toolId);
262
+ expect(viewer.render(120).join("\n")).toContain("six");
244
263
  });
245
264
 
246
- it("uses a distinct focus rail for tool cards", () => {
247
- const railTheme = {
248
- fg: (name: string, text: string) => `<${name}>${text}</${name}>`,
249
- bold: (text: string) => `<bold>${text}</bold>`,
250
- };
265
+ it("synchronizes J/K message focus and anchors [/] around the selected message", () => {
251
266
  const viewer = new ConversationViewer(
252
267
  tui(),
253
268
  createStaticConversationSource([
254
- { role: "assistant", content: [{ type: "toolCall", name: "read", arguments: { path: "x" } }] },
255
- { role: "toolResult", toolName: "read", content: [{ type: "text", text: "done" }] },
269
+ { role: "user", content: "user one" },
270
+ { role: "assistant", content: [{ type: "text", text: "assistant one" }] },
271
+ { role: "assistant", content: [{ type: "toolCall", id: "tool-1", name: "read", arguments: { path: "one" } }] },
272
+ { role: "toolResult", toolCallId: "tool-1", toolName: "read", content: [{ type: "text", text: "one" }] },
273
+ { role: "assistant", content: [{ type: "text", text: "assistant two" }] },
274
+ { role: "assistant", content: [{ type: "toolCall", id: "tool-2", name: "read", arguments: { path: "two" } }] },
275
+ { role: "toolResult", toolCallId: "tool-2", toolName: "read", content: [{ type: "text", text: "two" }] },
256
276
  ] as any),
257
277
  record(),
258
278
  undefined,
259
- railTheme,
260
- vi.fn(),
261
- );
262
- expect(viewer.render(120).join("\\n")).toContain("<toolTitle>▌</toolTitle>");
263
- });
264
-
265
- it("scrolls the transcript with a normalized mouse wheel event", () => {
266
- const longText = Array.from({ length: 80 }, (_, index) => `line-${index}`).join("\n");
267
- const viewer = new ConversationViewer(
268
- tui(),
269
- createStaticConversationSource([{ role: "assistant", content: [{ type: "text", text: longText }] }] as any),
270
- record(),
271
- undefined,
272
279
  theme,
273
280
  vi.fn(),
274
281
  );
282
+
275
283
  viewer.render(120);
276
- const before = (viewer as any).scrollOffset;
277
- const event = { type: "wheel", button: "none", x: 2, y: 5, screenX: 2, screenY: 5, width: 120, height: 30, shift: false, alt: false, ctrl: false, wheelDelta: -3 };
278
- expect(viewer.handleMouse(event as any)?.handled).toBe(true);
279
- expect((viewer as any).scrollOffset).toBeLessThan(before);
284
+ const timeline = (viewer as any).timeline;
285
+ const snapshot = timeline.getSnapshot();
286
+ viewer.handleInput("]");
287
+ expect((viewer as any).selectedMessageIdx).toBe(snapshot.toolSpans[0].blockIndex);
288
+ expect(timeline.getFocusedToolCallId()).toBe("tool-1");
289
+
290
+ viewer.handleInput("J");
291
+ expect((viewer as any).selectedMessageIdx).toBe(3);
292
+ expect(timeline.getFocusedToolCallId()).toBeUndefined();
293
+ viewer.handleInput("]");
294
+ expect((viewer as any).selectedMessageIdx).toBe(snapshot.toolSpans[1].blockIndex);
295
+ expect(timeline.getFocusedToolCallId()).toBe("tool-2");
296
+
297
+ viewer.handleInput("K");
298
+ expect((viewer as any).selectedMessageIdx).toBe(3);
299
+ expect(timeline.getFocusedToolCallId()).toBeUndefined();
300
+ viewer.handleInput("[");
301
+ expect((viewer as any).selectedMessageIdx).toBe(snapshot.toolSpans[0].blockIndex);
302
+ expect(timeline.getFocusedToolCallId()).toBe("tool-1");
280
303
  });
281
304
 
282
- it("expands on the first click and collapses only on the second click", () => {
283
- const viewer = new ConversationViewer(
284
- tui(),
285
- createStaticConversationSource([
286
- { role: "assistant", content: [{ type: "toolCall", name: "read", arguments: { path: "x" } }] },
287
- { role: "toolResult", toolName: "read", content: [{ type: "text", text: "one\ntwo\nthree\nfour\nfive\nsix" }] },
288
- ] as any),
289
- record(),
290
- undefined,
291
- theme,
292
- vi.fn(),
293
- );
294
- expect(viewer.render(120).join("\n")).not.toContain("six");
295
- const span = (viewer as any).timeline.getSnapshot().toolSpans[0];
296
- expect(span.clickStartLine).toEqual(expect.any(Number));
297
- const hintY = 4 + span.clickStartLine;
298
- const mouse = { button: "left", x: 1, screenX: 1, screenY: hintY, width: 120, height: 30, shift: false, alt: false, ctrl: false };
299
- expect(viewer.handleMouse({ ...mouse, type: "press", y: hintY } as any)?.handled).toBe(true);
300
- expect(viewer.render(120).join("\n")).not.toContain("six");
301
- viewer.handleMouse({ ...mouse, type: "click", y: hintY, clickCount: 1 } as any);
302
- expect(viewer.render(120).join("\n")).toContain("six");
303
- viewer.handleMouse({ ...mouse, type: "click", y: hintY, clickCount: 1 } as any);
304
- expect(viewer.render(120).join("\n")).toContain("six");
305
- viewer.handleMouse({ ...mouse, type: "click", y: hintY, clickCount: 2 } as any);
306
- expect(viewer.render(120).join("\n")).not.toContain("six");
305
+ it("uses the first or last tool for initial [/], before an explicit focus exists", () => {
306
+ const source = createStaticConversationSource([
307
+ { role: "assistant", content: [{ type: "toolCall", id: "tool-1", name: "read", arguments: { path: "one" } }] },
308
+ { role: "toolResult", toolCallId: "tool-1", toolName: "read", content: [{ type: "text", text: "one" }] },
309
+ { role: "assistant", content: [{ type: "toolCall", id: "tool-2", name: "read", arguments: { path: "two" } }] },
310
+ { role: "toolResult", toolCallId: "tool-2", toolName: "read", content: [{ type: "text", text: "two" }] },
311
+ ] as any);
312
+ const makeViewer = () => new ConversationViewer(tui(), source, record(), undefined, theme, vi.fn());
313
+
314
+ const left = makeViewer();
315
+ left.render(120);
316
+ left.handleInput("[");
317
+ expect((left as any).timeline.getFocusedToolCallId()).toBe("tool-2");
318
+
319
+ const right = makeViewer();
320
+ right.render(120);
321
+ right.handleInput("]");
322
+ expect((right as any).timeline.getFocusedToolCallId()).toBe("tool-1");
307
323
  });
308
324
 
309
- it("highlights the tool hint on hover and closes from the viewer [esc] button", () => {
325
+ it("highlights the collapsed tool hint and the viewer close control independently", () => {
310
326
  const semanticTheme = {
311
327
  fg: (name: string, text: string) => `<${name}>${text}</${name}>`,
312
328
  bold: (text: string) => `<bold>${text}</bold>`,
@@ -325,21 +341,74 @@ describe("ConversationViewer", () => {
325
341
  );
326
342
  const initial = viewer.render(120).join("\n");
327
343
  const span = (viewer as any).timeline.getSnapshot().toolSpans[0];
328
- expect(span.clickStartLine).toEqual(expect.any(Number));
329
- const hintY = 4 + span.clickStartLine;
344
+ const hintY = viewerY(span.clickStartLine);
330
345
  expect(initial).not.toContain("<text>click to show more</text>");
331
- viewer.handleMouse({ type: "move", button: "none", x: 2, y: hintY, screenX: 2, screenY: hintY, width: 120, height: 30, shift: false, alt: false, ctrl: false } as any);
346
+ viewer.handleMouse(mouse("move", hintY, { button: "none" }));
332
347
  expect(viewer.render(120).join("\n")).toContain("<text>click to show more</text>");
333
-
334
- expect(viewer.render(120).join("\n")).toContain("[esc]");
335
- viewer.handleMouse({ type: "move", button: "none", x: 114, y: 1, screenX: 114, screenY: 1, width: 120, height: 30, shift: false, alt: false, ctrl: false } as any);
348
+ viewer.handleMouse(mouse("move", 1, { button: "none", x: 114, screenX: 114, screenY: 1 }));
336
349
  expect(viewer.render(120).join("\n")).toContain("<text><bold>[esc]</bold></text>");
337
- const close = { button: "left", x: 114, screenX: 114, screenY: 1, y: 1, width: 120, height: 30, shift: false, alt: false, ctrl: false };
338
- expect(viewer.handleMouse({ ...close, type: "press" } as any)?.handled).toBe(true);
339
- expect(viewer.handleMouse({ ...close, type: "click", clickCount: 1 } as any)?.handled).toBe(true);
350
+ expect(viewer.handleMouse(mouse("press", 1, { x: 114, screenX: 114 }))?.handled).toBe(true);
351
+ expect(viewer.handleMouse(mouse("click", 1, { x: 114, screenX: 114, clickCount: 1 }))?.handled).toBe(true);
340
352
  expect(done).toHaveBeenCalledTimes(1);
341
353
  });
342
354
 
355
+ it("focuses nearby tools with [ and ] and opens a complete read-only preview with w", () => {
356
+ const ui = tui();
357
+ let preview: any;
358
+ const previewDone = vi.fn();
359
+ const custom = vi.fn((factory: any) => {
360
+ preview = factory(ui, theme, undefined, previewDone);
361
+ const initial = preview.render(100).join("\\n");
362
+ expect(initial).toContain("Input");
363
+ expect(initial).toContain("Output");
364
+ expect(initial).toContain("line 1");
365
+ expect(initial).not.toContain("line 40");
366
+ preview.handleInput("G");
367
+ expect(preview.render(100).join("\\n")).toContain("line 40");
368
+ preview.handleInput("k");
369
+ preview.handleInput("j");
370
+ preview.handleInput("g");
371
+ preview.handleInput("g");
372
+ expect(preview.render(100).join("\\n")).toContain("line 1");
373
+ preview.handleInput("q");
374
+ expect(previewDone).toHaveBeenCalledTimes(1);
375
+ return Promise.resolve(undefined);
376
+ });
377
+ const viewer = new ConversationViewer(
378
+ ui,
379
+ createStaticConversationSource([
380
+ { role: "assistant", content: [{ type: "toolCall", name: "read", arguments: { path: "first" } }] },
381
+ { role: "toolResult", toolName: "read", content: [{ type: "text", text: Array.from({ length: 40 }, (_, index) => `line ${index + 1}`).join("\n") }] },
382
+ { role: "assistant", content: [{ type: "toolCall", name: "read", arguments: { path: "second" } }] },
383
+ { role: "toolResult", toolName: "read", content: [{ type: "text", text: "second output" }] },
384
+ ] as any),
385
+ record(),
386
+ undefined,
387
+ theme,
388
+ vi.fn(),
389
+ undefined,
390
+ undefined,
391
+ undefined,
392
+ { pi: {} as any, ctx: { ui: { custom } } as any, readOnly: true },
393
+ );
394
+ viewer.render(120);
395
+ const timeline = (viewer as any).timeline;
396
+ const ids = timeline.getSnapshot().interactiveToolIds;
397
+ viewer.handleInput("]");
398
+ expect(timeline.getFocusedToolCallId()).toBe(ids[0]);
399
+ viewer.render(120);
400
+ viewer.handleInput("w");
401
+ expect(custom).toHaveBeenCalledWith(expect.any(Function), expect.objectContaining({ overlay: true }));
402
+ preview.handleInput("G");
403
+ preview.handleInput("g");
404
+ preview.handleInput("g");
405
+ preview.handleInput("q");
406
+ viewer.handleInput("]");
407
+ expect(timeline.getFocusedToolCallId()).toBe(ids[1]);
408
+ viewer.handleInput("[");
409
+ expect(timeline.getFocusedToolCallId()).toBe(ids[0]);
410
+ });
411
+
343
412
  it("keeps historical viewers read-only and closes on Escape", () => {
344
413
  const ui = tui();
345
414
  const steer = vi.fn();
@@ -31,7 +31,6 @@ describe("executeWriteWithMetadata", () => {
31
31
 
32
32
  expect(await readFile(join(cwd, "nested/file.txt"), "utf8")).toBe("new\n");
33
33
  expect(result.content[0]?.text).toContain("Successfully wrote to nested/file.txt");
34
- expect(result.details?.fileExistedBeforeWrite).toBe(false);
35
34
  expect(result.details?.diff).toContain("+1 new");
36
35
  expect(result.details?.patch).toContain("+new");
37
36
  expect(store.get("call-new")?.diff).toBe(result.details?.diff);
@@ -49,7 +48,6 @@ describe("executeWriteWithMetadata", () => {
49
48
 
50
49
  await writeFile(join(cwd, "file.txt"), "later\n", "utf8");
51
50
 
52
- expect(persisted.fileExistedBeforeWrite).toBe(true);
53
51
  expect(persisted.diff).toContain("-1 old");
54
52
  expect(persisted.diff).toContain("+1 new");
55
53
  expect(persisted.patch).toContain("-old");
@@ -1,53 +0,0 @@
1
- {
2
- "nextId": 4,
3
- "tasks": [
4
- {
5
- "id": "1",
6
- "subject": "Analyze viewer interaction boundaries",
7
- "description": "Inspect current ConversationViewer/ConversationTimeline/tool renderer and Pi mouse semantics. Identify the smallest safe design for press/release/click, viewer-local hover target styling, clickable [esc], and complete expanded output. Read-only; do not edit.",
8
- "status": "in_progress",
9
- "activeForm": "Analyzing viewer interaction boundaries",
10
- "metadata": {
11
- "agentType": "oracle"
12
- },
13
- "blocks": [],
14
- "blockedBy": [],
15
- "createdAt": 1789162453256,
16
- "updatedAt": 1789162461551,
17
- "owner": "orchestrator"
18
- },
19
- {
20
- "id": "2",
21
- "subject": "Implement viewer mouse behavior",
22
- "description": "After inspecting current source, implement the requested ConversationViewer behavior in source and focused regression tests: press/release never toggle, click-target expansion/collapse, hover styling, clickable top-right [esc], Esc key close, complete expanded output with usable scrolling, preserving Enter and J/K/j/k. Modify source/tests only; do not run git/npm install/npm publish.",
23
- "status": "in_progress",
24
- "activeForm": "Implementing viewer mouse behavior",
25
- "metadata": {
26
- "agentType": "fixer"
27
- },
28
- "blocks": [
29
- "3"
30
- ],
31
- "blockedBy": [],
32
- "createdAt": 1789162453263,
33
- "updatedAt": 1789162461564,
34
- "owner": "orchestrator"
35
- },
36
- {
37
- "id": "3",
38
- "subject": "Validate viewer regressions",
39
- "description": "Run the focused ConversationViewer test command after implementation, inspect changed source/tests for correctness and scope, and report failures or evidence. Do not run git/npm install/npm publish.",
40
- "status": "pending",
41
- "activeForm": "Validating viewer regressions",
42
- "metadata": {
43
- "agentType": "build"
44
- },
45
- "blocks": [],
46
- "blockedBy": [
47
- "2"
48
- ],
49
- "createdAt": 1789162453268,
50
- "updatedAt": 1789162461564
51
- }
52
- ]
53
- }
@@ -1,5 +0,0 @@
1
- {
2
- "schemaVersion": 1,
3
- "updatedAt": "2026-09-11T21:34:35.211Z",
4
- "runs": []
5
- }