ai-whisper 0.5.4 → 0.5.6

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 (180) hide show
  1. package/README.md +5 -4
  2. package/dist/bin/broker-daemon.js +201 -28
  3. package/dist/bin/companion-agent.js +596 -76
  4. package/dist/bin/relay-monitor.js +214 -30
  5. package/dist/bin/turn-event-shim.js +85 -0
  6. package/dist/bin/whisper.js +1475 -131
  7. package/package.json +6 -4
  8. package/dist/.tsbuildinfo +0 -1
  9. package/dist/bin/broker-daemon.d.ts +0 -3
  10. package/dist/bin/broker-daemon.d.ts.map +0 -1
  11. package/dist/bin/companion-agent.d.ts +0 -3
  12. package/dist/bin/companion-agent.d.ts.map +0 -1
  13. package/dist/bin/relay-monitor.d.ts +0 -3
  14. package/dist/bin/relay-monitor.d.ts.map +0 -1
  15. package/dist/bin/whisper.d.ts +0 -3
  16. package/dist/bin/whisper.d.ts.map +0 -1
  17. package/dist/commands/collab/dashboard.d.ts +0 -18
  18. package/dist/commands/collab/dashboard.d.ts.map +0 -1
  19. package/dist/commands/collab/inspect.d.ts +0 -23
  20. package/dist/commands/collab/inspect.d.ts.map +0 -1
  21. package/dist/commands/collab/mount.d.ts +0 -55
  22. package/dist/commands/collab/mount.d.ts.map +0 -1
  23. package/dist/commands/collab/reconnect.d.ts +0 -45
  24. package/dist/commands/collab/reconnect.d.ts.map +0 -1
  25. package/dist/commands/collab/recover.d.ts +0 -33
  26. package/dist/commands/collab/recover.d.ts.map +0 -1
  27. package/dist/commands/collab/relay-monitor.d.ts +0 -21
  28. package/dist/commands/collab/relay-monitor.d.ts.map +0 -1
  29. package/dist/commands/collab/start.d.ts +0 -53
  30. package/dist/commands/collab/start.d.ts.map +0 -1
  31. package/dist/commands/collab/status.d.ts +0 -6
  32. package/dist/commands/collab/status.d.ts.map +0 -1
  33. package/dist/commands/collab/stop.d.ts +0 -13
  34. package/dist/commands/collab/stop.d.ts.map +0 -1
  35. package/dist/commands/collab/tell.d.ts +0 -27
  36. package/dist/commands/collab/tell.d.ts.map +0 -1
  37. package/dist/commands/skill/install.d.ts +0 -13
  38. package/dist/commands/skill/install.d.ts.map +0 -1
  39. package/dist/commands/workflow/cancel.d.ts +0 -14
  40. package/dist/commands/workflow/cancel.d.ts.map +0 -1
  41. package/dist/commands/workflow/inspect.d.ts +0 -41
  42. package/dist/commands/workflow/inspect.d.ts.map +0 -1
  43. package/dist/commands/workflow/list.d.ts +0 -28
  44. package/dist/commands/workflow/list.d.ts.map +0 -1
  45. package/dist/commands/workflow/pause.d.ts +0 -14
  46. package/dist/commands/workflow/pause.d.ts.map +0 -1
  47. package/dist/commands/workflow/resume.d.ts +0 -17
  48. package/dist/commands/workflow/resume.d.ts.map +0 -1
  49. package/dist/commands/workflow/start.d.ts +0 -66
  50. package/dist/commands/workflow/start.d.ts.map +0 -1
  51. package/dist/commands/workflow/types.d.ts +0 -2
  52. package/dist/commands/workflow/types.d.ts.map +0 -1
  53. package/dist/create-cli.d.ts +0 -4
  54. package/dist/create-cli.d.ts.map +0 -1
  55. package/dist/index.d.ts +0 -5
  56. package/dist/index.d.ts.map +0 -1
  57. package/dist/runtime/action-inference.d.ts +0 -3
  58. package/dist/runtime/action-inference.d.ts.map +0 -1
  59. package/dist/runtime/artifact-input.d.ts +0 -2
  60. package/dist/runtime/artifact-input.d.ts.map +0 -1
  61. package/dist/runtime/assistant-turn-capture.d.ts +0 -14
  62. package/dist/runtime/assistant-turn-capture.d.ts.map +0 -1
  63. package/dist/runtime/bracketed-paste-detector.d.ts +0 -16
  64. package/dist/runtime/bracketed-paste-detector.d.ts.map +0 -1
  65. package/dist/runtime/broker-artifact-service.d.ts +0 -48
  66. package/dist/runtime/broker-artifact-service.d.ts.map +0 -1
  67. package/dist/runtime/broker-connect.d.ts +0 -9
  68. package/dist/runtime/broker-connect.d.ts.map +0 -1
  69. package/dist/runtime/broker-daemon.d.ts +0 -25
  70. package/dist/runtime/broker-daemon.d.ts.map +0 -1
  71. package/dist/runtime/busy-indicator.d.ts +0 -11
  72. package/dist/runtime/busy-indicator.d.ts.map +0 -1
  73. package/dist/runtime/capture-handback-text.d.ts +0 -56
  74. package/dist/runtime/capture-handback-text.d.ts.map +0 -1
  75. package/dist/runtime/clipboard-change-count.d.ts +0 -12
  76. package/dist/runtime/clipboard-change-count.d.ts.map +0 -1
  77. package/dist/runtime/clipboard-handback-capture.d.ts +0 -13
  78. package/dist/runtime/clipboard-handback-capture.d.ts.map +0 -1
  79. package/dist/runtime/collab-resolver.d.ts +0 -36
  80. package/dist/runtime/collab-resolver.d.ts.map +0 -1
  81. package/dist/runtime/companion-agent-loop.d.ts +0 -13
  82. package/dist/runtime/companion-agent-loop.d.ts.map +0 -1
  83. package/dist/runtime/context-injector.d.ts +0 -20
  84. package/dist/runtime/context-injector.d.ts.map +0 -1
  85. package/dist/runtime/context-policy.d.ts +0 -3
  86. package/dist/runtime/context-policy.d.ts.map +0 -1
  87. package/dist/runtime/current-tty.d.ts +0 -2
  88. package/dist/runtime/current-tty.d.ts.map +0 -1
  89. package/dist/runtime/dashboard-glyph.d.ts +0 -12
  90. package/dist/runtime/dashboard-glyph.d.ts.map +0 -1
  91. package/dist/runtime/dashboard-state.d.ts +0 -183
  92. package/dist/runtime/dashboard-state.d.ts.map +0 -1
  93. package/dist/runtime/dashboard-view.d.ts +0 -34
  94. package/dist/runtime/dashboard-view.d.ts.map +0 -1
  95. package/dist/runtime/dashboard.d.ts +0 -46
  96. package/dist/runtime/dashboard.d.ts.map +0 -1
  97. package/dist/runtime/evaluator-config.d.ts +0 -21
  98. package/dist/runtime/evaluator-config.d.ts.map +0 -1
  99. package/dist/runtime/evaluator-observer.d.ts +0 -8
  100. package/dist/runtime/evaluator-observer.d.ts.map +0 -1
  101. package/dist/runtime/id-factory.d.ts +0 -6
  102. package/dist/runtime/id-factory.d.ts.map +0 -1
  103. package/dist/runtime/launcher.d.ts +0 -39
  104. package/dist/runtime/launcher.d.ts.map +0 -1
  105. package/dist/runtime/live-session-broker-executor.d.ts +0 -8
  106. package/dist/runtime/live-session-broker-executor.d.ts.map +0 -1
  107. package/dist/runtime/live-session.d.ts +0 -33
  108. package/dist/runtime/live-session.d.ts.map +0 -1
  109. package/dist/runtime/load-dot-env.d.ts +0 -6
  110. package/dist/runtime/load-dot-env.d.ts.map +0 -1
  111. package/dist/runtime/local-multiline-composer.d.ts +0 -23
  112. package/dist/runtime/local-multiline-composer.d.ts.map +0 -1
  113. package/dist/runtime/mount-session-main.d.ts +0 -67
  114. package/dist/runtime/mount-session-main.d.ts.map +0 -1
  115. package/dist/runtime/mounted-turn-owned-relay.d.ts +0 -170
  116. package/dist/runtime/mounted-turn-owned-relay.d.ts.map +0 -1
  117. package/dist/runtime/on-demand-processing.d.ts +0 -23
  118. package/dist/runtime/on-demand-processing.d.ts.map +0 -1
  119. package/dist/runtime/operator-inspect-captures.d.ts +0 -6
  120. package/dist/runtime/operator-inspect-captures.d.ts.map +0 -1
  121. package/dist/runtime/operator-inspect-verdicts.d.ts +0 -6
  122. package/dist/runtime/operator-inspect-verdicts.d.ts.map +0 -1
  123. package/dist/runtime/operator-inspect.d.ts +0 -138
  124. package/dist/runtime/operator-inspect.d.ts.map +0 -1
  125. package/dist/runtime/paths.d.ts +0 -2
  126. package/dist/runtime/paths.d.ts.map +0 -1
  127. package/dist/runtime/port-allocator.d.ts +0 -7
  128. package/dist/runtime/port-allocator.d.ts.map +0 -1
  129. package/dist/runtime/port-utils.d.ts +0 -3
  130. package/dist/runtime/port-utils.d.ts.map +0 -1
  131. package/dist/runtime/process-start-time.d.ts +0 -7
  132. package/dist/runtime/process-start-time.d.ts.map +0 -1
  133. package/dist/runtime/provider-submit-strategy.d.ts +0 -14
  134. package/dist/runtime/provider-submit-strategy.d.ts.map +0 -1
  135. package/dist/runtime/providers.d.ts +0 -18
  136. package/dist/runtime/providers.d.ts.map +0 -1
  137. package/dist/runtime/record-evaluator-status.d.ts +0 -9
  138. package/dist/runtime/record-evaluator-status.d.ts.map +0 -1
  139. package/dist/runtime/recovery-guard.d.ts +0 -12
  140. package/dist/runtime/recovery-guard.d.ts.map +0 -1
  141. package/dist/runtime/relay-directive.d.ts +0 -4
  142. package/dist/runtime/relay-directive.d.ts.map +0 -1
  143. package/dist/runtime/relay-line-buffer.d.ts +0 -22
  144. package/dist/runtime/relay-line-buffer.d.ts.map +0 -1
  145. package/dist/runtime/relay-monitor.d.ts +0 -26
  146. package/dist/runtime/relay-monitor.d.ts.map +0 -1
  147. package/dist/runtime/relay-orchestrator-evaluator.d.ts +0 -160
  148. package/dist/runtime/relay-orchestrator-evaluator.d.ts.map +0 -1
  149. package/dist/runtime/relay-orchestrator.d.ts +0 -116
  150. package/dist/runtime/relay-orchestrator.d.ts.map +0 -1
  151. package/dist/runtime/relay-pane-writer.d.ts +0 -28
  152. package/dist/runtime/relay-pane-writer.d.ts.map +0 -1
  153. package/dist/runtime/relay-service.d.ts +0 -76
  154. package/dist/runtime/relay-service.d.ts.map +0 -1
  155. package/dist/runtime/relay-view-input.d.ts +0 -17
  156. package/dist/runtime/relay-view-input.d.ts.map +0 -1
  157. package/dist/runtime/relay-view-state.d.ts +0 -87
  158. package/dist/runtime/relay-view-state.d.ts.map +0 -1
  159. package/dist/runtime/relay-view.d.ts +0 -16
  160. package/dist/runtime/relay-view.d.ts.map +0 -1
  161. package/dist/runtime/reply-wait.d.ts +0 -21
  162. package/dist/runtime/reply-wait.d.ts.map +0 -1
  163. package/dist/runtime/runtime-debug-log.d.ts +0 -7
  164. package/dist/runtime/runtime-debug-log.d.ts.map +0 -1
  165. package/dist/runtime/state-root.d.ts +0 -3
  166. package/dist/runtime/state-root.d.ts.map +0 -1
  167. package/dist/runtime/terminal-input-normalizer.d.ts +0 -11
  168. package/dist/runtime/terminal-input-normalizer.d.ts.map +0 -1
  169. package/dist/runtime/theme.d.ts +0 -16
  170. package/dist/runtime/theme.d.ts.map +0 -1
  171. package/dist/runtime/version-check.d.ts +0 -17
  172. package/dist/runtime/version-check.d.ts.map +0 -1
  173. package/dist/runtime/wait-for-broker-ready.d.ts +0 -13
  174. package/dist/runtime/wait-for-broker-ready.d.ts.map +0 -1
  175. package/dist/runtime/workspace-id.d.ts +0 -3
  176. package/dist/runtime/workspace-id.d.ts.map +0 -1
  177. package/dist/skills/ai-whisper-bugfix/SKILL.md +0 -163
  178. package/dist/skills/ai-whisper-code-review/SKILL.md +0 -79
  179. package/dist/skills/ai-whisper-ralph/SKILL.md +0 -163
  180. package/dist/skills/ai-whisper-sdd/SKILL.md +0 -155
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ai-whisper
2
2
 
3
- ai-whisper turns two coding agents — Claude and Codex — into a terminal-native pair that hand work back and forth under a single baton, so one agent implements while the other reviews, and a structured workflow drives the loop to a finished, reviewed deliverable without a human babysitting every round.
3
+ ai-whisper pairs two coding agents — mount any two of Claude, Codex, and ezio — into a terminal-native pair that hand work back and forth under a single baton, so one agent implements while the other reviews, and a structured workflow drives the loop to a finished, reviewed deliverable without a human babysitting every round.
4
4
 
5
5
  ## Magic moment
6
6
 
@@ -52,10 +52,11 @@ It is **not** for:
52
52
 
53
53
  ## Prerequisites
54
54
 
55
- ai-whisper drives the *real* Claude and Codex CLIs, so you need both installed and authenticated first:
55
+ You pair any two of three agents — `claude`, `codex`, and `ezio`. ai-whisper drives the *real* Claude and Codex CLIs, so install and authenticate whichever of those two you plan to mount first; `ezio` is protocol-native and ships with ai-whisper, so it needs no separate CLI.
56
56
 
57
57
  - **[Claude Code CLI](https://claude.com/claude-code)** — the `claude` command, signed in.
58
58
  - **[Codex CLI](https://github.com/openai/codex)** — the `codex` command, signed in.
59
+ - **ezio** *(optional)* — bundled with ai-whisper; mount it with `whisper collab mount ezio`, no separate install.
59
60
  - **Node.js 22+**.
60
61
  - **An LLM evaluator with credentials** — workflows are gated by it and refuse to start without it. See [Evaluator configuration](docs/evaluator-configuration.md).
61
62
  - **tmux** *(optional)* — only for `whisper collab start`, which auto-arranges both agents into panes. The mount flow below does not need it.
@@ -110,9 +111,9 @@ A run that stops short usually **escalates** — it does not crash. When the eva
110
111
 
111
112
  ## Core concepts
112
113
 
113
- ai-whisper is **not a swarm**. The agents never type at once — work moves by a single baton, one owner at a time. Mounted sessions are *real* Claude and Codex sessions in your terminal, and those sessions are the source of truth. Autonomy is supervised: every handoff, verdict, and round is inspectable, and runs are resumable rather than fire-and-forget. Work is organized as structured workflows — explicit loops and state transitions, not a free-form chat.
114
+ ai-whisper is **not a swarm**. The agents never type at once — work moves by a single baton, one owner at a time. Mounted sessions are *real* agent sessions in your terminal — Claude or Codex CLIs, or ezio — and those sessions are the source of truth. Autonomy is supervised: every handoff, verdict, and round is inspectable, and runs are resumable rather than fire-and-forget. Work is organized as structured workflows — explicit loops and state transitions, not a free-form chat.
114
115
 
115
- Claude and Codex are supported today; the architecture is provider-agnostic by design, so other coding-agent CLIs can be added behind the same relay.
116
+ Claude, Codex, and ezio are supported today — you mount any two of them; the architecture is provider-agnostic by design, so other coding-agent CLIs can be added behind the same relay.
116
117
 
117
118
  For the full mental model, read [Concepts](docs/concepts.md).
118
119
 
@@ -106,6 +106,26 @@ var attachTargetModes = [
106
106
  "mount_current_tty"
107
107
  ];
108
108
 
109
+ // ../shared/dist/repo-relative-path.js
110
+ import { basename, isAbsolute, relative } from "node:path";
111
+ function repoRelativePath(absPath, root) {
112
+ if (!absPath)
113
+ return "";
114
+ if (!root || !isAbsolute(absPath) || !isAbsolute(root))
115
+ return absPath;
116
+ const rel = relative(root, absPath);
117
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel))
118
+ return absPath;
119
+ return rel;
120
+ }
121
+ function displayArtifactPath(absPath, root) {
122
+ const trimmed = (absPath ?? "").trim();
123
+ if (!trimmed)
124
+ return null;
125
+ const rel = repoRelativePath(trimmed, root).trim();
126
+ return rel || basename(trimmed).trim() || null;
127
+ }
128
+
109
129
  // ../shared/dist/broker-status.js
110
130
  import { z as z3 } from "zod";
111
131
  var brokerStatusSchema = z3.object({
@@ -1255,8 +1275,78 @@ function deleteEvaluatorDiagnosticsOlderThan(db, cutoffIso) {
1255
1275
  return result.changes;
1256
1276
  }
1257
1277
 
1278
+ // ../broker/dist/storage/repositories/relay-turn-event-diagnostics-repository.js
1279
+ var insertSeq = 0;
1280
+ function insertTurnEventDiagnostic(db, input) {
1281
+ const eventId = `tevt_${input.receivedAt.replace(/[^0-9]/g, "")}_${(input.handoffId ?? input.workspaceId).slice(-8)}_${input.action}_${process.pid}_${(insertSeq++).toString(36)}`;
1282
+ db.prepare(`INSERT INTO relay_turn_event_diagnostics (
1283
+ event_id, received_at, provider, workspace_id, cwd,
1284
+ session_or_thread_id, turn_id, workflow_active, collab_id, workflow_id,
1285
+ chain_id, handoff_id, input_correlated, containment_score,
1286
+ fidelity_verdict, defer_count, action, message_len, message_sample
1287
+ ) VALUES (
1288
+ @eventId, @receivedAt, @provider, @workspaceId, @cwd,
1289
+ @sessionOrThreadId, @turnId, @workflowActive, @collabId, @workflowId,
1290
+ @chainId, @handoffId, @inputCorrelated, @containmentScore,
1291
+ @fidelityVerdict, @deferCount, @action, @messageLen, @messageSample
1292
+ )`).run({
1293
+ eventId,
1294
+ receivedAt: input.receivedAt,
1295
+ provider: input.provider,
1296
+ workspaceId: input.workspaceId,
1297
+ cwd: input.cwd,
1298
+ sessionOrThreadId: input.sessionOrThreadId,
1299
+ turnId: input.turnId,
1300
+ workflowActive: input.workflowActive ? 1 : 0,
1301
+ collabId: input.collabId,
1302
+ workflowId: input.workflowId,
1303
+ chainId: input.chainId,
1304
+ handoffId: input.handoffId,
1305
+ inputCorrelated: input.inputCorrelated === null ? null : input.inputCorrelated ? 1 : 0,
1306
+ containmentScore: input.containmentScore,
1307
+ fidelityVerdict: input.fidelityVerdict,
1308
+ deferCount: input.deferCount,
1309
+ action: input.action,
1310
+ messageLen: input.messageLen,
1311
+ messageSample: input.messageSample
1312
+ });
1313
+ return { eventId };
1314
+ }
1315
+ function rowToRecord4(r) {
1316
+ return {
1317
+ eventId: r.event_id,
1318
+ receivedAt: r.received_at,
1319
+ provider: r.provider,
1320
+ workspaceId: r.workspace_id,
1321
+ cwd: r.cwd,
1322
+ sessionOrThreadId: r.session_or_thread_id,
1323
+ turnId: r.turn_id,
1324
+ workflowActive: r.workflow_active === 1,
1325
+ collabId: r.collab_id,
1326
+ workflowId: r.workflow_id,
1327
+ chainId: r.chain_id,
1328
+ handoffId: r.handoff_id,
1329
+ inputCorrelated: r.input_correlated === null ? null : r.input_correlated === 1,
1330
+ containmentScore: r.containment_score,
1331
+ fidelityVerdict: r.fidelity_verdict,
1332
+ deferCount: r.defer_count,
1333
+ action: r.action,
1334
+ messageLen: r.message_len,
1335
+ messageSample: r.message_sample
1336
+ };
1337
+ }
1338
+ function listTurnEventDiagnosticsByCollab(db, collabId2, limit) {
1339
+ const sql = "SELECT * FROM relay_turn_event_diagnostics WHERE collab_id = ? ORDER BY received_at DESC" + (limit !== null ? " LIMIT ?" : "");
1340
+ const rows = limit !== null ? db.prepare(sql).all(collabId2, limit) : db.prepare(sql).all(collabId2);
1341
+ return rows.map(rowToRecord4);
1342
+ }
1343
+ function deleteTurnEventDiagnosticsOlderThan(db, cutoffIso) {
1344
+ const info = db.prepare("DELETE FROM relay_turn_event_diagnostics WHERE received_at < ?").run(cutoffIso);
1345
+ return info.changes;
1346
+ }
1347
+
1258
1348
  // ../broker/dist/storage/repositories/relay-handoff-repository.js
1259
- function rowToRecord4(row) {
1349
+ function rowToRecord5(row) {
1260
1350
  return {
1261
1351
  handoffId: row.handoff_id,
1262
1352
  collabId: row.collab_id,
@@ -1293,7 +1383,7 @@ function queryRelayHandoff(db, handoffId) {
1293
1383
  FROM relay_handoff h
1294
1384
  JOIN collab c ON c.collab_id = h.collab_id
1295
1385
  WHERE h.handoff_id = ?`).get(handoffId);
1296
- return row ? rowToRecord4(row) : null;
1386
+ return row ? rowToRecord5(row) : null;
1297
1387
  }
1298
1388
  function createRelayHandoffTxn(db, input) {
1299
1389
  return db.transaction(() => {
@@ -1445,7 +1535,7 @@ function queryLatestHandedBackHandoff(db, collabId2) {
1445
1535
  WHERE h.collab_id = ? AND h.status = 'handed_back'
1446
1536
  ORDER BY h.resolved_at DESC
1447
1537
  LIMIT 1`).get(collabId2);
1448
- return row ? rowToRecord4(row) : null;
1538
+ return row ? rowToRecord5(row) : null;
1449
1539
  }
1450
1540
  function failRelayHandoffOnDisconnectTxn(db, input) {
1451
1541
  db.transaction(() => {
@@ -1516,7 +1606,7 @@ function listRelayHandoffsPendingOrchestration(db, collabId2) {
1516
1606
  AND h.status = 'handed_back'
1517
1607
  AND h.orchestrator_status = 'idle'
1518
1608
  AND (w.status IS NULL OR w.status <> 'paused')`).all(collabId2);
1519
- return rows.map(rowToRecord4);
1609
+ return rows.map(rowToRecord5);
1520
1610
  }
1521
1611
  function createLoopRelayHandoffTxn(db, input) {
1522
1612
  return db.transaction(() => {
@@ -1712,7 +1802,7 @@ function getHandoffWithWorkflowMetaById(db, handoffId) {
1712
1802
  WHERE h.handoff_id = ?`).get(handoffId);
1713
1803
  if (!row)
1714
1804
  return null;
1715
- const base = rowToRecord4(row);
1805
+ const base = rowToRecord5(row);
1716
1806
  return {
1717
1807
  ...base,
1718
1808
  handoffStep: row.handoff_step,
@@ -1811,7 +1901,7 @@ function cleanupOrchestrationOnShutdownTxn(db, input) {
1811
1901
  }
1812
1902
 
1813
1903
  // ../broker/dist/storage/repositories/dashboard-repository.js
1814
- import { basename } from "node:path";
1904
+ import { basename as basename2 } from "node:path";
1815
1905
  function listActiveCollabSummaries(db, input) {
1816
1906
  const nowMs = Date.parse(input.now ?? (/* @__PURE__ */ new Date()).toISOString());
1817
1907
  const base = Number.isFinite(nowMs) ? nowMs : Date.now();
@@ -1855,7 +1945,7 @@ function buildCollabSummary(db, collabId2) {
1855
1945
  FROM collab WHERE collab_id = ?`).get(e.collabId);
1856
1946
  const wf = db.prepare(`SELECT workflow_id AS workflowId, workflow_type AS workflowType,
1857
1947
  name, status, current_phase_index AS currentPhaseIndex,
1858
- created_at AS createdAt
1948
+ created_at AS createdAt, spec_path AS specPath
1859
1949
  FROM workflows WHERE collab_id = ?
1860
1950
  ORDER BY (status = 'running') DESC, created_at DESC
1861
1951
  LIMIT 1`).get(e.collabId);
@@ -1898,7 +1988,7 @@ function buildCollabSummary(db, collabId2) {
1898
1988
  ) ranked
1899
1989
  WHERE rn = 1
1900
1990
  ORDER BY agentType ASC`).all(e.collabId);
1901
- const label = wf?.name && wf.name.trim() || collab2?.displayName && collab2.displayName.trim() || (collab2?.workspaceRoot ? basename(collab2.workspaceRoot) : "") || e.collabId.slice(0, 12);
1991
+ const label = wf?.name && wf.name.trim() || collab2?.displayName && collab2.displayName.trim() || (collab2?.workspaceRoot ? basename2(collab2.workspaceRoot) : "") || e.collabId.slice(0, 12);
1902
1992
  const runLastActRow = wf ? db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
1903
1993
  FROM relay_handoff
1904
1994
  WHERE collab_id = ? AND workflow_id = ?`).get(e.collabId, wf.workflowId) : db.prepare(`SELECT COALESCE(MAX(last_activity_at), '') AS lastAct
@@ -1924,6 +2014,7 @@ function buildCollabSummary(db, collabId2) {
1924
2014
  },
1925
2015
  sessions,
1926
2016
  workflowCreatedAt: wf?.createdAt ?? null,
2017
+ specPath: wf ? displayArtifactPath(wf.specPath, collab2?.workspaceRoot ?? "") : null,
1927
2018
  lastActivityAt: runLastAct
1928
2019
  };
1929
2020
  }
@@ -1966,7 +2057,7 @@ function listRunCostRows(db, input) {
1966
2057
  import { randomUUID } from "node:crypto";
1967
2058
 
1968
2059
  // ../broker/dist/storage/repositories/workflow-phase-repository.js
1969
- function rowToRecord5(row) {
2060
+ function rowToRecord6(row) {
1970
2061
  return {
1971
2062
  phaseRunId: row.phase_run_id,
1972
2063
  workflowId: row.workflow_id,
@@ -1985,7 +2076,7 @@ function insertWorkflowPhaseRun(db, input) {
1985
2076
  }
1986
2077
  function listPhaseRunsForWorkflow(db, workflowId) {
1987
2078
  const rows = db.prepare("SELECT * FROM workflow_phases WHERE workflow_id = ? ORDER BY started_at ASC").all(workflowId);
1988
- return rows.map(rowToRecord5);
2079
+ return rows.map(rowToRecord6);
1989
2080
  }
1990
2081
  function closeWorkflowPhaseRun(db, input) {
1991
2082
  db.prepare("UPDATE workflow_phases SET ended_at = ?, outcome = ? WHERE phase_run_id = ?").run(input.now, input.outcome, input.phaseRunId);
@@ -1998,7 +2089,7 @@ function hasOpenPhaseRunForIndex(db, input) {
1998
2089
  }
1999
2090
 
2000
2091
  // ../broker/dist/storage/repositories/relay-chain-repository.js
2001
- function rowToRecord6(row) {
2092
+ function rowToRecord7(row) {
2002
2093
  return {
2003
2094
  chainId: row.chain_id,
2004
2095
  collabId: row.collab_id,
@@ -2018,7 +2109,7 @@ function insertRelayChain(db, input) {
2018
2109
  }
2019
2110
  function getRelayChain(db, chainId) {
2020
2111
  const row = db.prepare("SELECT * FROM relay_chains WHERE chain_id = ?").get(chainId);
2021
- return row ? rowToRecord6(row) : null;
2112
+ return row ? rowToRecord7(row) : null;
2022
2113
  }
2023
2114
  function incrementChainRound(db, input) {
2024
2115
  db.prepare("UPDATE relay_chains SET current_round = current_round + 1, updated_at = ? WHERE chain_id = ?").run(input.now, input.chainId);
@@ -2375,11 +2466,11 @@ function derivePlanPath(specPath, dateIso) {
2375
2466
  if (!/^\d{4}-\d{2}-\d{2}/.test(dateIso)) {
2376
2467
  throw new Error(`derivePlanPath: dateIso must start with YYYY-MM-DD, got "${dateIso}"`);
2377
2468
  }
2378
- const basename2 = specPath.split("/").pop() ?? "";
2379
- if (!basename2.endsWith("-design.md")) {
2469
+ const basename3 = specPath.split("/").pop() ?? "";
2470
+ if (!basename3.endsWith("-design.md")) {
2380
2471
  throw new Error(`derivePlanPath: specPath must end with "-design.md", got "${specPath}"`);
2381
2472
  }
2382
- const withoutExt = basename2.replace(/\.md$/, "");
2473
+ const withoutExt = basename3.replace(/\.md$/, "");
2383
2474
  const slug = withoutExt.replace(/-design$/, "").replace(/^\d{4}-\d{2}-\d{2}-/, "");
2384
2475
  const date = dateIso.slice(0, 10);
2385
2476
  return `docs/superpowers/plans/${date}-${slug}.md`;
@@ -4170,6 +4261,15 @@ function createControlService(db, events) {
4170
4261
  sweepEvaluatorDiagnostics(input) {
4171
4262
  return deleteEvaluatorDiagnosticsOlderThan(db, input.cutoffIso);
4172
4263
  },
4264
+ recordTurnEventDiagnostic(input) {
4265
+ return insertTurnEventDiagnostic(db, input);
4266
+ },
4267
+ listTurnEventDiagnosticsByCollab(collabId2, limit) {
4268
+ return listTurnEventDiagnosticsByCollab(db, collabId2, limit);
4269
+ },
4270
+ sweepTurnEventDiagnostics(input) {
4271
+ return deleteTurnEventDiagnosticsOlderThan(db, input.cutoffIso);
4272
+ },
4173
4273
  cleanupOrchestration(input) {
4174
4274
  return cleanupOrchestrationOnShutdownTxn(db, input);
4175
4275
  }
@@ -4310,7 +4410,7 @@ function sweepStaleCaptureLease(db, options = {}) {
4310
4410
  }
4311
4411
 
4312
4412
  // ../broker/dist/storage/apply-migrations.js
4313
- var CURRENT_SCHEMA_VERSION = 5;
4413
+ var CURRENT_SCHEMA_VERSION = 6;
4314
4414
  var initMigrationSql = `
4315
4415
  CREATE TABLE IF NOT EXISTS broker_state (
4316
4416
  id INTEGER PRIMARY KEY CHECK (id = 1),
@@ -4825,6 +4925,37 @@ function runMigrationBody(db) {
4825
4925
  if (!brokerDaemonColumns.some((column) => column.name === "evaluator_status")) {
4826
4926
  db.exec("ALTER TABLE broker_daemon ADD COLUMN evaluator_status TEXT");
4827
4927
  }
4928
+ db.exec(`
4929
+ CREATE TABLE IF NOT EXISTS relay_turn_event_diagnostics (
4930
+ event_id TEXT PRIMARY KEY,
4931
+ received_at TEXT NOT NULL,
4932
+ provider TEXT NOT NULL,
4933
+ workspace_id TEXT NOT NULL,
4934
+ cwd TEXT NOT NULL,
4935
+ session_or_thread_id TEXT,
4936
+ turn_id TEXT,
4937
+ workflow_active INTEGER NOT NULL DEFAULT 0,
4938
+ collab_id TEXT,
4939
+ workflow_id TEXT,
4940
+ chain_id TEXT,
4941
+ handoff_id TEXT,
4942
+ input_correlated INTEGER,
4943
+ containment_score REAL,
4944
+ fidelity_verdict TEXT NOT NULL DEFAULT 'n/a',
4945
+ defer_count INTEGER NOT NULL DEFAULT 0,
4946
+ action TEXT NOT NULL,
4947
+ message_len INTEGER NOT NULL DEFAULT 0,
4948
+ message_sample TEXT
4949
+ );
4950
+ CREATE INDEX IF NOT EXISTS idx_relay_turn_event_diag_collab_received
4951
+ ON relay_turn_event_diagnostics (collab_id, received_at DESC);
4952
+ CREATE INDEX IF NOT EXISTS idx_relay_turn_event_diag_handoff
4953
+ ON relay_turn_event_diagnostics (handoff_id);
4954
+ CREATE INDEX IF NOT EXISTS idx_relay_turn_event_diag_action
4955
+ ON relay_turn_event_diagnostics (action);
4956
+ CREATE INDEX IF NOT EXISTS idx_relay_turn_event_diag_received
4957
+ ON relay_turn_event_diagnostics (received_at);
4958
+ `);
4828
4959
  }
4829
4960
 
4830
4961
  // ../broker/dist/storage/open-database.js
@@ -4975,8 +5106,12 @@ function createDaemonHeartbeat(input) {
4975
5106
  }
4976
5107
 
4977
5108
  // ../broker/dist/runtime/diagnostics-sweep.js
5109
+ import { readdirSync, unlinkSync, statSync as statSync2 } from "node:fs";
5110
+ import { join as join2 } from "node:path";
4978
5111
  var DEFAULT_INTERVAL_MS = 60 * 60 * 1e3;
4979
5112
  var DEFAULT_RETENTION_DAYS = 30;
5113
+ var DEFAULT_EVENT_LOG_RETENTION_DAYS = 3;
5114
+ var TURN_EVENT_FILE_RE = /^turn-events-(\d{4}-\d{2}-\d{2})\.jsonl$/;
4980
5115
  function resolveIntervalMs(override) {
4981
5116
  if (override !== void 0)
4982
5117
  return override;
@@ -4993,6 +5128,38 @@ function resolveRetentionDays(override) {
4993
5128
  return Number(envVal);
4994
5129
  return DEFAULT_RETENTION_DAYS;
4995
5130
  }
5131
+ function resolveEventLogRetentionDays(override) {
5132
+ if (override !== void 0)
5133
+ return override;
5134
+ const raw = process.env["AI_WHISPER_EVENT_LOG_RETENTION_DAYS"];
5135
+ if (raw && Number.isFinite(Number(raw)) && Number(raw) > 0)
5136
+ return Number(raw);
5137
+ return DEFAULT_EVENT_LOG_RETENTION_DAYS;
5138
+ }
5139
+ function sweepTurnEventLogs(logsDir, cutoffDate) {
5140
+ let entries;
5141
+ try {
5142
+ entries = readdirSync(logsDir);
5143
+ } catch {
5144
+ return [];
5145
+ }
5146
+ const removed = [];
5147
+ for (const name of entries) {
5148
+ const m = TURN_EVENT_FILE_RE.exec(name);
5149
+ if (!m)
5150
+ continue;
5151
+ if (m[1] < cutoffDate) {
5152
+ const full = join2(logsDir, name);
5153
+ try {
5154
+ statSync2(full);
5155
+ unlinkSync(full);
5156
+ removed.push(full);
5157
+ } catch {
5158
+ }
5159
+ }
5160
+ }
5161
+ return removed;
5162
+ }
4996
5163
  function createDiagnosticsSweep(deps) {
4997
5164
  const intervalMs = resolveIntervalMs(deps.intervalMs);
4998
5165
  const retentionDays = resolveRetentionDays(deps.retentionDays);
@@ -5002,6 +5169,12 @@ function createDiagnosticsSweep(deps) {
5002
5169
  const cutoffIso = cutoff.toISOString();
5003
5170
  deps.broker.control.sweepCaptureDiagnostics({ cutoffIso });
5004
5171
  deps.broker.control.sweepEvaluatorDiagnostics({ cutoffIso });
5172
+ const eventRetentionDays = resolveEventLogRetentionDays(deps.eventLogRetentionDays);
5173
+ const eventCutoff = new Date(Date.now() - eventRetentionDays * 864e5);
5174
+ deps.broker.control.sweepTurnEventDiagnostics({ cutoffIso: eventCutoff.toISOString() });
5175
+ if (deps.logsDir) {
5176
+ sweepTurnEventLogs(deps.logsDir, eventCutoff.toISOString().slice(0, 10));
5177
+ }
5005
5178
  }
5006
5179
  return {
5007
5180
  start() {
@@ -5020,15 +5193,15 @@ function createDiagnosticsSweep(deps) {
5020
5193
 
5021
5194
  // ../broker/dist/runtime/ralph-setup.js
5022
5195
  import { mkdirSync, writeFileSync, existsSync as existsSync2 } from "node:fs";
5023
- import { join as join2 } from "node:path";
5196
+ import { join as join3 } from "node:path";
5024
5197
  function ensureRalphWorkspace(workspaceRoot, workflowId) {
5025
5198
  const dir = ralphRunDir(workspaceRoot, workflowId);
5026
5199
  mkdirSync(dir, { recursive: true });
5027
- const ignorePath = join2(workspaceRoot, ".ai-whisper", ".gitignore");
5200
+ const ignorePath = join3(workspaceRoot, ".ai-whisper", ".gitignore");
5028
5201
  if (!existsSync2(ignorePath)) {
5029
5202
  writeFileSync(ignorePath, "*\n");
5030
5203
  }
5031
- const ralphIgnorePath = join2(workspaceRoot, ".ai-whisper", "ralph", ".gitignore");
5204
+ const ralphIgnorePath = join3(workspaceRoot, ".ai-whisper", "ralph", ".gitignore");
5032
5205
  if (!existsSync2(ralphIgnorePath)) {
5033
5206
  writeFileSync(ralphIgnorePath, "*\n");
5034
5207
  }
@@ -5037,15 +5210,15 @@ function ensureRalphWorkspace(workspaceRoot, workflowId) {
5037
5210
 
5038
5211
  // ../broker/dist/runtime/bugfix-setup.js
5039
5212
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "node:fs";
5040
- import { join as join3 } from "node:path";
5213
+ import { join as join4 } from "node:path";
5041
5214
  function ensureBugfixWorkspace(workspaceRoot, workflowId) {
5042
5215
  const dir = bugfixRunDir(workspaceRoot, workflowId);
5043
5216
  mkdirSync2(dir, { recursive: true });
5044
- const ignorePath = join3(workspaceRoot, ".ai-whisper", ".gitignore");
5217
+ const ignorePath = join4(workspaceRoot, ".ai-whisper", ".gitignore");
5045
5218
  if (!existsSync3(ignorePath)) {
5046
5219
  writeFileSync2(ignorePath, "*\n");
5047
5220
  }
5048
- const bugfixIgnorePath = join3(workspaceRoot, ".ai-whisper", "bugfix", ".gitignore");
5221
+ const bugfixIgnorePath = join4(workspaceRoot, ".ai-whisper", "bugfix", ".gitignore");
5049
5222
  if (!existsSync3(bugfixIgnorePath)) {
5050
5223
  writeFileSync2(bugfixIgnorePath, "*\n");
5051
5224
  }
@@ -6099,8 +6272,8 @@ function writeOwnPidToBrokerDaemon(db, input) {
6099
6272
  }
6100
6273
 
6101
6274
  // src/runtime/evaluator-config.ts
6102
- import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
6103
- import { join as join4 } from "node:path";
6275
+ import { readFileSync as readFileSync2, statSync as statSync3 } from "node:fs";
6276
+ import { join as join5 } from "node:path";
6104
6277
 
6105
6278
  // src/runtime/state-root.ts
6106
6279
  import os from "node:os";
@@ -6131,7 +6304,7 @@ function warnIfLoosePerms(path2) {
6131
6304
  if (process.platform === "win32") return;
6132
6305
  let mode;
6133
6306
  try {
6134
- mode = statSync2(path2).mode;
6307
+ mode = statSync3(path2).mode;
6135
6308
  } catch {
6136
6309
  return;
6137
6310
  }
@@ -6159,15 +6332,15 @@ function loadEvaluatorConfig() {
6159
6332
  const root = getStateRoot();
6160
6333
  let dotenv = {};
6161
6334
  try {
6162
- dotenv = parseDotEnv(readFileSync2(join4(root, ".env"), "utf8"));
6335
+ dotenv = parseDotEnv(readFileSync2(join5(root, ".env"), "utf8"));
6163
6336
  } catch (err) {
6164
6337
  if (err.code !== "ENOENT") throw err;
6165
6338
  }
6166
6339
  const envGet = (k) => process.env[k] ?? dotenv[k];
6167
- const authPath = join4(root, "auth.json");
6340
+ const authPath = join5(root, "auth.json");
6168
6341
  warnIfLoosePerms(authPath);
6169
6342
  const auth = readJsonFile(authPath, "auth.json");
6170
- const config = readJsonFile(join4(root, "config.json"), "config.json");
6343
+ const config = readJsonFile(join5(root, "config.json"), "config.json");
6171
6344
  const evalCfg = config?.evaluator ?? {};
6172
6345
  const evalOllama = evalCfg.ollama ?? {};
6173
6346
  const provider = (envGet("AI_WHISPER_EVALUATOR_PROVIDER") ?? evalCfg.provider) === "ollama" ? "ollama" : "anthropic";