ai-whisper 0.5.5 → 0.5.7

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 (181) hide show
  1. package/README.md +6 -5
  2. package/dist/bin/broker-daemon.js +205 -30
  3. package/dist/bin/companion-agent.js +600 -78
  4. package/dist/bin/relay-monitor.js +218 -32
  5. package/dist/bin/turn-event-shim.js +85 -0
  6. package/dist/bin/whisper.js +1480 -134
  7. package/package.json +6 -5
  8. package/skills/ai-whisper-plan-execution/SKILL.md +65 -0
  9. package/dist/.tsbuildinfo +0 -1
  10. package/dist/bin/broker-daemon.d.ts +0 -3
  11. package/dist/bin/broker-daemon.d.ts.map +0 -1
  12. package/dist/bin/companion-agent.d.ts +0 -3
  13. package/dist/bin/companion-agent.d.ts.map +0 -1
  14. package/dist/bin/relay-monitor.d.ts +0 -3
  15. package/dist/bin/relay-monitor.d.ts.map +0 -1
  16. package/dist/bin/whisper.d.ts +0 -3
  17. package/dist/bin/whisper.d.ts.map +0 -1
  18. package/dist/commands/collab/dashboard.d.ts +0 -18
  19. package/dist/commands/collab/dashboard.d.ts.map +0 -1
  20. package/dist/commands/collab/inspect.d.ts +0 -23
  21. package/dist/commands/collab/inspect.d.ts.map +0 -1
  22. package/dist/commands/collab/mount.d.ts +0 -55
  23. package/dist/commands/collab/mount.d.ts.map +0 -1
  24. package/dist/commands/collab/reconnect.d.ts +0 -45
  25. package/dist/commands/collab/reconnect.d.ts.map +0 -1
  26. package/dist/commands/collab/recover.d.ts +0 -33
  27. package/dist/commands/collab/recover.d.ts.map +0 -1
  28. package/dist/commands/collab/relay-monitor.d.ts +0 -21
  29. package/dist/commands/collab/relay-monitor.d.ts.map +0 -1
  30. package/dist/commands/collab/start.d.ts +0 -53
  31. package/dist/commands/collab/start.d.ts.map +0 -1
  32. package/dist/commands/collab/status.d.ts +0 -6
  33. package/dist/commands/collab/status.d.ts.map +0 -1
  34. package/dist/commands/collab/stop.d.ts +0 -13
  35. package/dist/commands/collab/stop.d.ts.map +0 -1
  36. package/dist/commands/collab/tell.d.ts +0 -27
  37. package/dist/commands/collab/tell.d.ts.map +0 -1
  38. package/dist/commands/skill/install.d.ts +0 -13
  39. package/dist/commands/skill/install.d.ts.map +0 -1
  40. package/dist/commands/workflow/cancel.d.ts +0 -14
  41. package/dist/commands/workflow/cancel.d.ts.map +0 -1
  42. package/dist/commands/workflow/inspect.d.ts +0 -41
  43. package/dist/commands/workflow/inspect.d.ts.map +0 -1
  44. package/dist/commands/workflow/list.d.ts +0 -28
  45. package/dist/commands/workflow/list.d.ts.map +0 -1
  46. package/dist/commands/workflow/pause.d.ts +0 -14
  47. package/dist/commands/workflow/pause.d.ts.map +0 -1
  48. package/dist/commands/workflow/resume.d.ts +0 -17
  49. package/dist/commands/workflow/resume.d.ts.map +0 -1
  50. package/dist/commands/workflow/start.d.ts +0 -66
  51. package/dist/commands/workflow/start.d.ts.map +0 -1
  52. package/dist/commands/workflow/types.d.ts +0 -2
  53. package/dist/commands/workflow/types.d.ts.map +0 -1
  54. package/dist/create-cli.d.ts +0 -4
  55. package/dist/create-cli.d.ts.map +0 -1
  56. package/dist/index.d.ts +0 -5
  57. package/dist/index.d.ts.map +0 -1
  58. package/dist/runtime/action-inference.d.ts +0 -3
  59. package/dist/runtime/action-inference.d.ts.map +0 -1
  60. package/dist/runtime/artifact-input.d.ts +0 -2
  61. package/dist/runtime/artifact-input.d.ts.map +0 -1
  62. package/dist/runtime/assistant-turn-capture.d.ts +0 -14
  63. package/dist/runtime/assistant-turn-capture.d.ts.map +0 -1
  64. package/dist/runtime/bracketed-paste-detector.d.ts +0 -16
  65. package/dist/runtime/bracketed-paste-detector.d.ts.map +0 -1
  66. package/dist/runtime/broker-artifact-service.d.ts +0 -48
  67. package/dist/runtime/broker-artifact-service.d.ts.map +0 -1
  68. package/dist/runtime/broker-connect.d.ts +0 -9
  69. package/dist/runtime/broker-connect.d.ts.map +0 -1
  70. package/dist/runtime/broker-daemon.d.ts +0 -25
  71. package/dist/runtime/broker-daemon.d.ts.map +0 -1
  72. package/dist/runtime/busy-indicator.d.ts +0 -11
  73. package/dist/runtime/busy-indicator.d.ts.map +0 -1
  74. package/dist/runtime/capture-handback-text.d.ts +0 -56
  75. package/dist/runtime/capture-handback-text.d.ts.map +0 -1
  76. package/dist/runtime/clipboard-change-count.d.ts +0 -12
  77. package/dist/runtime/clipboard-change-count.d.ts.map +0 -1
  78. package/dist/runtime/clipboard-handback-capture.d.ts +0 -13
  79. package/dist/runtime/clipboard-handback-capture.d.ts.map +0 -1
  80. package/dist/runtime/collab-resolver.d.ts +0 -36
  81. package/dist/runtime/collab-resolver.d.ts.map +0 -1
  82. package/dist/runtime/companion-agent-loop.d.ts +0 -13
  83. package/dist/runtime/companion-agent-loop.d.ts.map +0 -1
  84. package/dist/runtime/context-injector.d.ts +0 -20
  85. package/dist/runtime/context-injector.d.ts.map +0 -1
  86. package/dist/runtime/context-policy.d.ts +0 -3
  87. package/dist/runtime/context-policy.d.ts.map +0 -1
  88. package/dist/runtime/current-tty.d.ts +0 -2
  89. package/dist/runtime/current-tty.d.ts.map +0 -1
  90. package/dist/runtime/dashboard-glyph.d.ts +0 -12
  91. package/dist/runtime/dashboard-glyph.d.ts.map +0 -1
  92. package/dist/runtime/dashboard-state.d.ts +0 -183
  93. package/dist/runtime/dashboard-state.d.ts.map +0 -1
  94. package/dist/runtime/dashboard-view.d.ts +0 -34
  95. package/dist/runtime/dashboard-view.d.ts.map +0 -1
  96. package/dist/runtime/dashboard.d.ts +0 -46
  97. package/dist/runtime/dashboard.d.ts.map +0 -1
  98. package/dist/runtime/evaluator-config.d.ts +0 -21
  99. package/dist/runtime/evaluator-config.d.ts.map +0 -1
  100. package/dist/runtime/evaluator-observer.d.ts +0 -8
  101. package/dist/runtime/evaluator-observer.d.ts.map +0 -1
  102. package/dist/runtime/id-factory.d.ts +0 -6
  103. package/dist/runtime/id-factory.d.ts.map +0 -1
  104. package/dist/runtime/launcher.d.ts +0 -39
  105. package/dist/runtime/launcher.d.ts.map +0 -1
  106. package/dist/runtime/live-session-broker-executor.d.ts +0 -8
  107. package/dist/runtime/live-session-broker-executor.d.ts.map +0 -1
  108. package/dist/runtime/live-session.d.ts +0 -33
  109. package/dist/runtime/live-session.d.ts.map +0 -1
  110. package/dist/runtime/load-dot-env.d.ts +0 -6
  111. package/dist/runtime/load-dot-env.d.ts.map +0 -1
  112. package/dist/runtime/local-multiline-composer.d.ts +0 -23
  113. package/dist/runtime/local-multiline-composer.d.ts.map +0 -1
  114. package/dist/runtime/mount-session-main.d.ts +0 -67
  115. package/dist/runtime/mount-session-main.d.ts.map +0 -1
  116. package/dist/runtime/mounted-turn-owned-relay.d.ts +0 -170
  117. package/dist/runtime/mounted-turn-owned-relay.d.ts.map +0 -1
  118. package/dist/runtime/on-demand-processing.d.ts +0 -23
  119. package/dist/runtime/on-demand-processing.d.ts.map +0 -1
  120. package/dist/runtime/operator-inspect-captures.d.ts +0 -6
  121. package/dist/runtime/operator-inspect-captures.d.ts.map +0 -1
  122. package/dist/runtime/operator-inspect-verdicts.d.ts +0 -6
  123. package/dist/runtime/operator-inspect-verdicts.d.ts.map +0 -1
  124. package/dist/runtime/operator-inspect.d.ts +0 -138
  125. package/dist/runtime/operator-inspect.d.ts.map +0 -1
  126. package/dist/runtime/paths.d.ts +0 -2
  127. package/dist/runtime/paths.d.ts.map +0 -1
  128. package/dist/runtime/port-allocator.d.ts +0 -7
  129. package/dist/runtime/port-allocator.d.ts.map +0 -1
  130. package/dist/runtime/port-utils.d.ts +0 -3
  131. package/dist/runtime/port-utils.d.ts.map +0 -1
  132. package/dist/runtime/process-start-time.d.ts +0 -7
  133. package/dist/runtime/process-start-time.d.ts.map +0 -1
  134. package/dist/runtime/provider-submit-strategy.d.ts +0 -14
  135. package/dist/runtime/provider-submit-strategy.d.ts.map +0 -1
  136. package/dist/runtime/providers.d.ts +0 -18
  137. package/dist/runtime/providers.d.ts.map +0 -1
  138. package/dist/runtime/record-evaluator-status.d.ts +0 -9
  139. package/dist/runtime/record-evaluator-status.d.ts.map +0 -1
  140. package/dist/runtime/recovery-guard.d.ts +0 -12
  141. package/dist/runtime/recovery-guard.d.ts.map +0 -1
  142. package/dist/runtime/relay-directive.d.ts +0 -4
  143. package/dist/runtime/relay-directive.d.ts.map +0 -1
  144. package/dist/runtime/relay-line-buffer.d.ts +0 -22
  145. package/dist/runtime/relay-line-buffer.d.ts.map +0 -1
  146. package/dist/runtime/relay-monitor.d.ts +0 -26
  147. package/dist/runtime/relay-monitor.d.ts.map +0 -1
  148. package/dist/runtime/relay-orchestrator-evaluator.d.ts +0 -160
  149. package/dist/runtime/relay-orchestrator-evaluator.d.ts.map +0 -1
  150. package/dist/runtime/relay-orchestrator.d.ts +0 -116
  151. package/dist/runtime/relay-orchestrator.d.ts.map +0 -1
  152. package/dist/runtime/relay-pane-writer.d.ts +0 -28
  153. package/dist/runtime/relay-pane-writer.d.ts.map +0 -1
  154. package/dist/runtime/relay-service.d.ts +0 -76
  155. package/dist/runtime/relay-service.d.ts.map +0 -1
  156. package/dist/runtime/relay-view-input.d.ts +0 -17
  157. package/dist/runtime/relay-view-input.d.ts.map +0 -1
  158. package/dist/runtime/relay-view-state.d.ts +0 -87
  159. package/dist/runtime/relay-view-state.d.ts.map +0 -1
  160. package/dist/runtime/relay-view.d.ts +0 -16
  161. package/dist/runtime/relay-view.d.ts.map +0 -1
  162. package/dist/runtime/reply-wait.d.ts +0 -21
  163. package/dist/runtime/reply-wait.d.ts.map +0 -1
  164. package/dist/runtime/runtime-debug-log.d.ts +0 -7
  165. package/dist/runtime/runtime-debug-log.d.ts.map +0 -1
  166. package/dist/runtime/state-root.d.ts +0 -3
  167. package/dist/runtime/state-root.d.ts.map +0 -1
  168. package/dist/runtime/terminal-input-normalizer.d.ts +0 -11
  169. package/dist/runtime/terminal-input-normalizer.d.ts.map +0 -1
  170. package/dist/runtime/theme.d.ts +0 -16
  171. package/dist/runtime/theme.d.ts.map +0 -1
  172. package/dist/runtime/version-check.d.ts +0 -17
  173. package/dist/runtime/version-check.d.ts.map +0 -1
  174. package/dist/runtime/wait-for-broker-ready.d.ts +0 -13
  175. package/dist/runtime/wait-for-broker-ready.d.ts.map +0 -1
  176. package/dist/runtime/workspace-id.d.ts +0 -3
  177. package/dist/runtime/workspace-id.d.ts.map +0 -1
  178. package/dist/skills/ai-whisper-bugfix/SKILL.md +0 -163
  179. package/dist/skills/ai-whisper-code-review/SKILL.md +0 -79
  180. package/dist/skills/ai-whisper-ralph/SKILL.md +0 -163
  181. 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.
@@ -79,7 +80,7 @@ pnpm install
79
80
  pnpm build
80
81
  ```
81
82
 
82
- Install the bundled agent skills once (they let the agents verify, kick off, and report on workflows). This also installs `ai-whisper-code-review`, the skill workflow code-review handoffs use to evaluate agent-written code:
83
+ Install the bundled agent skills once (they let the agents verify, kick off, and report on workflows). This also installs `ai-whisper-code-review`, the skill workflow code-review handoffs use to evaluate agent-written code, and `ai-whisper-plan-execution`, the skill plan-execution handoffs use to structure how the implementer executes an approved plan:
83
84
 
84
85
  ```bash
85
86
  whisper skill install
@@ -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);
@@ -2124,8 +2215,10 @@ Non-blocking risks:
2124
2215
  - <risk that does NOT block this gate, or "None.">
2125
2216
  --- end protocol ---`;
2126
2217
  var CODE_REVIEW_SKILL_GUIDANCE = "Use the ai-whisper-code-review skill to evaluate the delivered code. The workflow review protocol below controls your output format and evaluator semantics; the skill controls how you inspect code and decide which code-quality issues are blocking.\n\n";
2218
+ var PLAN_EXECUTION_SKILL_GUIDANCE = "Use the ai-whisper-plan-execution skill to structure HOW you execute this plan: subagent fan-out with model allocation where your harness supports it, disciplined inline execution otherwise. The handback contract above remains authoritative \u2014 settle all delegated work before handing back, and state which execution mode you used.";
2127
2219
  var SDD_SPEC_REVIEW = "Review the spec at {specPath}. This is an autonomous workflow with no human in the loop.\n\n" + WORKFLOW_REVIEW_PROTOCOL;
2128
2220
  var SDD_CODE_REVIEW = "Review the implementer's changes for this phase \u2014 the commits in {commitRange}. The upper bound is a LIVE `HEAD`: resolve it against the current repository at review time and INCLUDE any commits added during this review round (e.g. fixes for your prior findings); do not pin the review to an earlier tip. Verify against the spec's acceptance criteria and run the project's verification/tests. This is an autonomous workflow with no human in the loop.\n\n" + CODE_REVIEW_SKILL_GUIDANCE + WORKFLOW_REVIEW_PROTOCOL;
2221
+ var SDD_PLAN_EXECUTION = "Execute the plan at {planPath} now: make all changes it specifies, run the verification command the plan or spec defines and ensure it passes, then commit. This is an autonomous workflow \u2014 no human will respond. Do the work yourself; never ask for confirmation, permission, or clarification. Hand back the commit SHAs and the verification output, plus a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters \u2014 never hand back only a single word.\n\n" + PLAN_EXECUTION_SKILL_GUIDANCE;
2129
2222
  var SPEC_DRIVEN_DEVELOPMENT = {
2130
2223
  type: "spec-driven-development",
2131
2224
  displayName: "Spec-Driven Development",
@@ -2170,9 +2263,9 @@ var SPEC_DRIVEN_DEVELOPMENT = {
2170
2263
  reviewerRole: null,
2171
2264
  maxRounds: 1,
2172
2265
  initialHandoffStep: "execute",
2173
- kickoffTemplate: "Execute the plan at {planPath} now: make all changes it specifies, run the verification command the plan or spec defines and ensure it passes, then commit. This is an autonomous workflow \u2014 no human will respond. Do the work yourself; never ask for confirmation, permission, or clarification. Hand back the commit SHAs and the verification output, plus a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters \u2014 never hand back only a single word.",
2266
+ kickoffTemplate: SDD_PLAN_EXECUTION,
2174
2267
  stepTemplates: {
2175
- execute: "Execute the plan at {planPath} now: make all changes it specifies, run the verification command the plan or spec defines and ensure it passes, then commit. This is an autonomous workflow \u2014 no human will respond. Do the work yourself; never ask for confirmation, permission, or clarification. Hand back the commit SHAs and the verification output, plus a 1-2 sentence summary; your reply must be at least two sentences, well over 100 characters \u2014 never hand back only a single word."
2268
+ execute: SDD_PLAN_EXECUTION
2176
2269
  },
2177
2270
  evaluatorPromptKey: "execution-gate",
2178
2271
  artifactOut: { kind: "commit-range" }
@@ -2375,11 +2468,11 @@ function derivePlanPath(specPath, dateIso) {
2375
2468
  if (!/^\d{4}-\d{2}-\d{2}/.test(dateIso)) {
2376
2469
  throw new Error(`derivePlanPath: dateIso must start with YYYY-MM-DD, got "${dateIso}"`);
2377
2470
  }
2378
- const basename2 = specPath.split("/").pop() ?? "";
2379
- if (!basename2.endsWith("-design.md")) {
2471
+ const basename3 = specPath.split("/").pop() ?? "";
2472
+ if (!basename3.endsWith("-design.md")) {
2380
2473
  throw new Error(`derivePlanPath: specPath must end with "-design.md", got "${specPath}"`);
2381
2474
  }
2382
- const withoutExt = basename2.replace(/\.md$/, "");
2475
+ const withoutExt = basename3.replace(/\.md$/, "");
2383
2476
  const slug = withoutExt.replace(/-design$/, "").replace(/^\d{4}-\d{2}-\d{2}-/, "");
2384
2477
  const date = dateIso.slice(0, 10);
2385
2478
  return `docs/superpowers/plans/${date}-${slug}.md`;
@@ -4170,6 +4263,15 @@ function createControlService(db, events) {
4170
4263
  sweepEvaluatorDiagnostics(input) {
4171
4264
  return deleteEvaluatorDiagnosticsOlderThan(db, input.cutoffIso);
4172
4265
  },
4266
+ recordTurnEventDiagnostic(input) {
4267
+ return insertTurnEventDiagnostic(db, input);
4268
+ },
4269
+ listTurnEventDiagnosticsByCollab(collabId2, limit) {
4270
+ return listTurnEventDiagnosticsByCollab(db, collabId2, limit);
4271
+ },
4272
+ sweepTurnEventDiagnostics(input) {
4273
+ return deleteTurnEventDiagnosticsOlderThan(db, input.cutoffIso);
4274
+ },
4173
4275
  cleanupOrchestration(input) {
4174
4276
  return cleanupOrchestrationOnShutdownTxn(db, input);
4175
4277
  }
@@ -4310,7 +4412,7 @@ function sweepStaleCaptureLease(db, options = {}) {
4310
4412
  }
4311
4413
 
4312
4414
  // ../broker/dist/storage/apply-migrations.js
4313
- var CURRENT_SCHEMA_VERSION = 5;
4415
+ var CURRENT_SCHEMA_VERSION = 6;
4314
4416
  var initMigrationSql = `
4315
4417
  CREATE TABLE IF NOT EXISTS broker_state (
4316
4418
  id INTEGER PRIMARY KEY CHECK (id = 1),
@@ -4825,6 +4927,37 @@ function runMigrationBody(db) {
4825
4927
  if (!brokerDaemonColumns.some((column) => column.name === "evaluator_status")) {
4826
4928
  db.exec("ALTER TABLE broker_daemon ADD COLUMN evaluator_status TEXT");
4827
4929
  }
4930
+ db.exec(`
4931
+ CREATE TABLE IF NOT EXISTS relay_turn_event_diagnostics (
4932
+ event_id TEXT PRIMARY KEY,
4933
+ received_at TEXT NOT NULL,
4934
+ provider TEXT NOT NULL,
4935
+ workspace_id TEXT NOT NULL,
4936
+ cwd TEXT NOT NULL,
4937
+ session_or_thread_id TEXT,
4938
+ turn_id TEXT,
4939
+ workflow_active INTEGER NOT NULL DEFAULT 0,
4940
+ collab_id TEXT,
4941
+ workflow_id TEXT,
4942
+ chain_id TEXT,
4943
+ handoff_id TEXT,
4944
+ input_correlated INTEGER,
4945
+ containment_score REAL,
4946
+ fidelity_verdict TEXT NOT NULL DEFAULT 'n/a',
4947
+ defer_count INTEGER NOT NULL DEFAULT 0,
4948
+ action TEXT NOT NULL,
4949
+ message_len INTEGER NOT NULL DEFAULT 0,
4950
+ message_sample TEXT
4951
+ );
4952
+ CREATE INDEX IF NOT EXISTS idx_relay_turn_event_diag_collab_received
4953
+ ON relay_turn_event_diagnostics (collab_id, received_at DESC);
4954
+ CREATE INDEX IF NOT EXISTS idx_relay_turn_event_diag_handoff
4955
+ ON relay_turn_event_diagnostics (handoff_id);
4956
+ CREATE INDEX IF NOT EXISTS idx_relay_turn_event_diag_action
4957
+ ON relay_turn_event_diagnostics (action);
4958
+ CREATE INDEX IF NOT EXISTS idx_relay_turn_event_diag_received
4959
+ ON relay_turn_event_diagnostics (received_at);
4960
+ `);
4828
4961
  }
4829
4962
 
4830
4963
  // ../broker/dist/storage/open-database.js
@@ -4975,8 +5108,12 @@ function createDaemonHeartbeat(input) {
4975
5108
  }
4976
5109
 
4977
5110
  // ../broker/dist/runtime/diagnostics-sweep.js
5111
+ import { readdirSync, unlinkSync, statSync as statSync2 } from "node:fs";
5112
+ import { join as join2 } from "node:path";
4978
5113
  var DEFAULT_INTERVAL_MS = 60 * 60 * 1e3;
4979
5114
  var DEFAULT_RETENTION_DAYS = 30;
5115
+ var DEFAULT_EVENT_LOG_RETENTION_DAYS = 3;
5116
+ var TURN_EVENT_FILE_RE = /^turn-events-(\d{4}-\d{2}-\d{2})\.jsonl$/;
4980
5117
  function resolveIntervalMs(override) {
4981
5118
  if (override !== void 0)
4982
5119
  return override;
@@ -4993,6 +5130,38 @@ function resolveRetentionDays(override) {
4993
5130
  return Number(envVal);
4994
5131
  return DEFAULT_RETENTION_DAYS;
4995
5132
  }
5133
+ function resolveEventLogRetentionDays(override) {
5134
+ if (override !== void 0)
5135
+ return override;
5136
+ const raw = process.env["AI_WHISPER_EVENT_LOG_RETENTION_DAYS"];
5137
+ if (raw && Number.isFinite(Number(raw)) && Number(raw) > 0)
5138
+ return Number(raw);
5139
+ return DEFAULT_EVENT_LOG_RETENTION_DAYS;
5140
+ }
5141
+ function sweepTurnEventLogs(logsDir, cutoffDate) {
5142
+ let entries;
5143
+ try {
5144
+ entries = readdirSync(logsDir);
5145
+ } catch {
5146
+ return [];
5147
+ }
5148
+ const removed = [];
5149
+ for (const name of entries) {
5150
+ const m = TURN_EVENT_FILE_RE.exec(name);
5151
+ if (!m)
5152
+ continue;
5153
+ if (m[1] < cutoffDate) {
5154
+ const full = join2(logsDir, name);
5155
+ try {
5156
+ statSync2(full);
5157
+ unlinkSync(full);
5158
+ removed.push(full);
5159
+ } catch {
5160
+ }
5161
+ }
5162
+ }
5163
+ return removed;
5164
+ }
4996
5165
  function createDiagnosticsSweep(deps) {
4997
5166
  const intervalMs = resolveIntervalMs(deps.intervalMs);
4998
5167
  const retentionDays = resolveRetentionDays(deps.retentionDays);
@@ -5002,6 +5171,12 @@ function createDiagnosticsSweep(deps) {
5002
5171
  const cutoffIso = cutoff.toISOString();
5003
5172
  deps.broker.control.sweepCaptureDiagnostics({ cutoffIso });
5004
5173
  deps.broker.control.sweepEvaluatorDiagnostics({ cutoffIso });
5174
+ const eventRetentionDays = resolveEventLogRetentionDays(deps.eventLogRetentionDays);
5175
+ const eventCutoff = new Date(Date.now() - eventRetentionDays * 864e5);
5176
+ deps.broker.control.sweepTurnEventDiagnostics({ cutoffIso: eventCutoff.toISOString() });
5177
+ if (deps.logsDir) {
5178
+ sweepTurnEventLogs(deps.logsDir, eventCutoff.toISOString().slice(0, 10));
5179
+ }
5005
5180
  }
5006
5181
  return {
5007
5182
  start() {
@@ -5020,15 +5195,15 @@ function createDiagnosticsSweep(deps) {
5020
5195
 
5021
5196
  // ../broker/dist/runtime/ralph-setup.js
5022
5197
  import { mkdirSync, writeFileSync, existsSync as existsSync2 } from "node:fs";
5023
- import { join as join2 } from "node:path";
5198
+ import { join as join3 } from "node:path";
5024
5199
  function ensureRalphWorkspace(workspaceRoot, workflowId) {
5025
5200
  const dir = ralphRunDir(workspaceRoot, workflowId);
5026
5201
  mkdirSync(dir, { recursive: true });
5027
- const ignorePath = join2(workspaceRoot, ".ai-whisper", ".gitignore");
5202
+ const ignorePath = join3(workspaceRoot, ".ai-whisper", ".gitignore");
5028
5203
  if (!existsSync2(ignorePath)) {
5029
5204
  writeFileSync(ignorePath, "*\n");
5030
5205
  }
5031
- const ralphIgnorePath = join2(workspaceRoot, ".ai-whisper", "ralph", ".gitignore");
5206
+ const ralphIgnorePath = join3(workspaceRoot, ".ai-whisper", "ralph", ".gitignore");
5032
5207
  if (!existsSync2(ralphIgnorePath)) {
5033
5208
  writeFileSync(ralphIgnorePath, "*\n");
5034
5209
  }
@@ -5037,15 +5212,15 @@ function ensureRalphWorkspace(workspaceRoot, workflowId) {
5037
5212
 
5038
5213
  // ../broker/dist/runtime/bugfix-setup.js
5039
5214
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "node:fs";
5040
- import { join as join3 } from "node:path";
5215
+ import { join as join4 } from "node:path";
5041
5216
  function ensureBugfixWorkspace(workspaceRoot, workflowId) {
5042
5217
  const dir = bugfixRunDir(workspaceRoot, workflowId);
5043
5218
  mkdirSync2(dir, { recursive: true });
5044
- const ignorePath = join3(workspaceRoot, ".ai-whisper", ".gitignore");
5219
+ const ignorePath = join4(workspaceRoot, ".ai-whisper", ".gitignore");
5045
5220
  if (!existsSync3(ignorePath)) {
5046
5221
  writeFileSync2(ignorePath, "*\n");
5047
5222
  }
5048
- const bugfixIgnorePath = join3(workspaceRoot, ".ai-whisper", "bugfix", ".gitignore");
5223
+ const bugfixIgnorePath = join4(workspaceRoot, ".ai-whisper", "bugfix", ".gitignore");
5049
5224
  if (!existsSync3(bugfixIgnorePath)) {
5050
5225
  writeFileSync2(bugfixIgnorePath, "*\n");
5051
5226
  }
@@ -6099,8 +6274,8 @@ function writeOwnPidToBrokerDaemon(db, input) {
6099
6274
  }
6100
6275
 
6101
6276
  // src/runtime/evaluator-config.ts
6102
- import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
6103
- import { join as join4 } from "node:path";
6277
+ import { readFileSync as readFileSync2, statSync as statSync3 } from "node:fs";
6278
+ import { join as join5 } from "node:path";
6104
6279
 
6105
6280
  // src/runtime/state-root.ts
6106
6281
  import os from "node:os";
@@ -6131,7 +6306,7 @@ function warnIfLoosePerms(path2) {
6131
6306
  if (process.platform === "win32") return;
6132
6307
  let mode;
6133
6308
  try {
6134
- mode = statSync2(path2).mode;
6309
+ mode = statSync3(path2).mode;
6135
6310
  } catch {
6136
6311
  return;
6137
6312
  }
@@ -6159,15 +6334,15 @@ function loadEvaluatorConfig() {
6159
6334
  const root = getStateRoot();
6160
6335
  let dotenv = {};
6161
6336
  try {
6162
- dotenv = parseDotEnv(readFileSync2(join4(root, ".env"), "utf8"));
6337
+ dotenv = parseDotEnv(readFileSync2(join5(root, ".env"), "utf8"));
6163
6338
  } catch (err) {
6164
6339
  if (err.code !== "ENOENT") throw err;
6165
6340
  }
6166
6341
  const envGet = (k) => process.env[k] ?? dotenv[k];
6167
- const authPath = join4(root, "auth.json");
6342
+ const authPath = join5(root, "auth.json");
6168
6343
  warnIfLoosePerms(authPath);
6169
6344
  const auth = readJsonFile(authPath, "auth.json");
6170
- const config = readJsonFile(join4(root, "config.json"), "config.json");
6345
+ const config = readJsonFile(join5(root, "config.json"), "config.json");
6171
6346
  const evalCfg = config?.evaluator ?? {};
6172
6347
  const evalOllama = evalCfg.ollama ?? {};
6173
6348
  const provider = (envGet("AI_WHISPER_EVALUATOR_PROVIDER") ?? evalCfg.provider) === "ollama" ? "ollama" : "anthropic";