issue-flow 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/README.md +79 -274
  2. package/dist/agent-OX3KPHE4.js +30 -0
  3. package/dist/{analyze-OEET7KGE.js → analyze-WIND2SEB.js} +31 -16
  4. package/dist/analyze-WIND2SEB.js.map +1 -0
  5. package/dist/{apply-ZIA3C52S.js → apply-JNOI2LR4.js} +18 -7
  6. package/dist/{apply-ZIA3C52S.js.map → apply-JNOI2LR4.js.map} +1 -1
  7. package/dist/bench-B4VZON7E.js +910 -0
  8. package/dist/bench-B4VZON7E.js.map +1 -0
  9. package/dist/{chunk-FADTCQGM.js → chunk-2IQ6M4ZE.js} +94 -32
  10. package/dist/chunk-2IQ6M4ZE.js.map +1 -0
  11. package/dist/chunk-3BWZO4YI.js +141 -0
  12. package/dist/chunk-3BWZO4YI.js.map +1 -0
  13. package/dist/chunk-3WFZETIM.js +163 -0
  14. package/dist/chunk-3WFZETIM.js.map +1 -0
  15. package/dist/chunk-67YIU2VB.js +362 -0
  16. package/dist/chunk-67YIU2VB.js.map +1 -0
  17. package/dist/{chunk-QCPGHBSZ.js → chunk-6BENHR2I.js} +63 -21
  18. package/dist/chunk-6BENHR2I.js.map +1 -0
  19. package/dist/{chunk-BYZDPR7N.js → chunk-6QYMSNVA.js} +194 -235
  20. package/dist/chunk-6QYMSNVA.js.map +1 -0
  21. package/dist/{chunk-Y5ZDJ4PJ.js → chunk-76MMPJ53.js} +20 -10
  22. package/dist/chunk-76MMPJ53.js.map +1 -0
  23. package/dist/chunk-7Z4Y3VKM.js +26 -0
  24. package/dist/chunk-7Z4Y3VKM.js.map +1 -0
  25. package/dist/{chunk-3QHK4YQA.js → chunk-B72RQZBV.js} +346 -119
  26. package/dist/chunk-B72RQZBV.js.map +1 -0
  27. package/dist/chunk-BEZ5BA5U.js +625 -0
  28. package/dist/chunk-BEZ5BA5U.js.map +1 -0
  29. package/dist/chunk-BRCNQXYQ.js +74 -0
  30. package/dist/chunk-BRCNQXYQ.js.map +1 -0
  31. package/dist/chunk-CGKLE6WR.js +1203 -0
  32. package/dist/chunk-CGKLE6WR.js.map +1 -0
  33. package/dist/chunk-CRGIQFUQ.js +83 -0
  34. package/dist/chunk-CRGIQFUQ.js.map +1 -0
  35. package/dist/{chunk-NS6R37II.js → chunk-F6TOYSYY.js} +141 -12
  36. package/dist/chunk-F6TOYSYY.js.map +1 -0
  37. package/dist/chunk-G5EK2XRN.js +25 -0
  38. package/dist/chunk-G5EK2XRN.js.map +1 -0
  39. package/dist/chunk-HAKDLR6N.js +134 -0
  40. package/dist/chunk-HAKDLR6N.js.map +1 -0
  41. package/dist/chunk-J3SOTUVI.js +298 -0
  42. package/dist/chunk-J3SOTUVI.js.map +1 -0
  43. package/dist/chunk-JN6D3MY6.js +30 -0
  44. package/dist/chunk-JN6D3MY6.js.map +1 -0
  45. package/dist/{chunk-LAFNLLQP.js → chunk-KMRFRXIT.js} +5 -5
  46. package/dist/chunk-KT7JKOXB.js +72 -0
  47. package/dist/chunk-KT7JKOXB.js.map +1 -0
  48. package/dist/chunk-L6HSKTQE.js +77 -0
  49. package/dist/chunk-L6HSKTQE.js.map +1 -0
  50. package/dist/chunk-LBGHI4S2.js +1264 -0
  51. package/dist/chunk-LBGHI4S2.js.map +1 -0
  52. package/dist/chunk-LEW4QM74.js +180 -0
  53. package/dist/chunk-LEW4QM74.js.map +1 -0
  54. package/dist/{chunk-FJP3UQGD.js → chunk-LFEQIZLB.js} +115 -20
  55. package/dist/chunk-LFEQIZLB.js.map +1 -0
  56. package/dist/chunk-OOVG2KBY.js +77 -0
  57. package/dist/chunk-OOVG2KBY.js.map +1 -0
  58. package/dist/{chunk-56KS3TFQ.js → chunk-OR43364P.js} +115 -160
  59. package/dist/chunk-OR43364P.js.map +1 -0
  60. package/dist/chunk-P3LRUNC6.js +16 -0
  61. package/dist/chunk-P3LRUNC6.js.map +1 -0
  62. package/dist/{chunk-TSOLWK7Y.js → chunk-PBTA3QQX.js} +8 -7
  63. package/dist/{chunk-TSOLWK7Y.js.map → chunk-PBTA3QQX.js.map} +1 -1
  64. package/dist/{chunk-MJULIKQU.js → chunk-PHW2YV6N.js} +23 -47
  65. package/dist/chunk-PHW2YV6N.js.map +1 -0
  66. package/dist/{chunk-73ATPIYX.js → chunk-PZTWU277.js} +36 -71
  67. package/dist/chunk-PZTWU277.js.map +1 -0
  68. package/dist/{chunk-M5UCXTIT.js → chunk-QD4VOILZ.js} +22 -18
  69. package/dist/chunk-QD4VOILZ.js.map +1 -0
  70. package/dist/chunk-QJ5UZAH5.js +2718 -0
  71. package/dist/chunk-QJ5UZAH5.js.map +1 -0
  72. package/dist/chunk-QK7WUO7A.js +283 -0
  73. package/dist/chunk-QK7WUO7A.js.map +1 -0
  74. package/dist/{chunk-A4SLHOW7.js → chunk-QZVKRDJY.js} +27 -19
  75. package/dist/chunk-QZVKRDJY.js.map +1 -0
  76. package/dist/chunk-REO3PXXO.js +71 -0
  77. package/dist/chunk-REO3PXXO.js.map +1 -0
  78. package/dist/{chunk-EZATO3DW.js → chunk-RVO7L4FX.js} +594 -17
  79. package/dist/chunk-RVO7L4FX.js.map +1 -0
  80. package/dist/{chunk-XQUSIEKA.js → chunk-SZMHCEYV.js} +63 -14
  81. package/dist/chunk-SZMHCEYV.js.map +1 -0
  82. package/dist/chunk-TYRNTSPJ.js +243 -0
  83. package/dist/chunk-TYRNTSPJ.js.map +1 -0
  84. package/dist/chunk-UJ3L2MS7.js +238 -0
  85. package/dist/chunk-UJ3L2MS7.js.map +1 -0
  86. package/dist/chunk-V7MO5EDS.js +120 -0
  87. package/dist/chunk-V7MO5EDS.js.map +1 -0
  88. package/dist/chunk-WB2X37MO.js +415 -0
  89. package/dist/chunk-WB2X37MO.js.map +1 -0
  90. package/dist/{chunk-NTMMF5UD.js → chunk-X44FKNKP.js} +525 -252
  91. package/dist/chunk-X44FKNKP.js.map +1 -0
  92. package/dist/chunk-XEYH6ZZI.js +289 -0
  93. package/dist/chunk-XEYH6ZZI.js.map +1 -0
  94. package/dist/cli.js +267 -44
  95. package/dist/cli.js.map +1 -1
  96. package/dist/{config-PRJGWW7J.js → config-HMFMCY2M.js} +31 -5
  97. package/dist/contract-JH2WBIO6.js +8 -0
  98. package/dist/conventions-NJF2OPVQ.js +146 -0
  99. package/dist/conventions-NJF2OPVQ.js.map +1 -0
  100. package/dist/diagnostics-THH42ZMP.js +23 -0
  101. package/dist/execute-KFNNZZHI.js +35 -0
  102. package/dist/{generate-EGB4KV2B.js → generate-FW5C6NP7.js} +37 -20
  103. package/dist/{generate-EGB4KV2B.js.map → generate-FW5C6NP7.js.map} +1 -1
  104. package/dist/git-W72QQ6GT.js +33 -0
  105. package/dist/init-HTN4V67Q.js +24 -0
  106. package/dist/{operations-BYT626KQ.js → operations-BCFOBH42.js} +27 -15
  107. package/dist/{operations-BYT626KQ.js.map → operations-BCFOBH42.js.map} +1 -1
  108. package/dist/permissions-4AKNQZHK.js +18 -0
  109. package/dist/plan-SIZLGA4V.js +34 -0
  110. package/dist/{policy-6S4GFC76.js → policy-DT6Q6ZVM.js} +20 -6
  111. package/dist/policy-DT6Q6ZVM.js.map +1 -0
  112. package/dist/pr-OMWCGUOW.js +39 -0
  113. package/dist/pr-review-N24CZPKR.js +32 -0
  114. package/dist/pr-review-N24CZPKR.js.map +1 -0
  115. package/dist/prd-5OFTZAHJ.js +33 -0
  116. package/dist/prd-5OFTZAHJ.js.map +1 -0
  117. package/dist/ps-RFORTCUD.js +93 -0
  118. package/dist/ps-RFORTCUD.js.map +1 -0
  119. package/dist/recorder-KCCPMKQG.js +32 -0
  120. package/dist/recorder-KCCPMKQG.js.map +1 -0
  121. package/dist/registry-GLPXOMFG.js +15 -0
  122. package/dist/registry-GLPXOMFG.js.map +1 -0
  123. package/dist/{resume-PPCNQVVV.js → resume-M5RLKP4I.js} +55 -34
  124. package/dist/{resume-PPCNQVVV.js.map → resume-M5RLKP4I.js.map} +1 -1
  125. package/dist/review-YM4JAABY.js +35 -0
  126. package/dist/review-YM4JAABY.js.map +1 -0
  127. package/dist/routing-NNT5WKGX.js +94 -0
  128. package/dist/routing-NNT5WKGX.js.map +1 -0
  129. package/dist/run-PZUKHPQK.js +54 -0
  130. package/dist/run-PZUKHPQK.js.map +1 -0
  131. package/dist/session-publisher-T7EI3CIP.js +11 -0
  132. package/dist/session-publisher-T7EI3CIP.js.map +1 -0
  133. package/dist/{state-manager-2EDVDBAR.js → state-manager-GQH32C44.js} +4 -2
  134. package/dist/state-manager-GQH32C44.js.map +1 -0
  135. package/dist/usage-V2CHE5PY.js +177 -0
  136. package/dist/usage-V2CHE5PY.js.map +1 -0
  137. package/dist/{web-EF5WIIGG.js → web-EA3KGNFN.js} +45 -30
  138. package/dist/web-EA3KGNFN.js.map +1 -0
  139. package/package.json +6 -2
  140. package/prompts/execute.md +7 -8
  141. package/prompts/plan.md +4 -5
  142. package/prompts/pr.md +2 -2
  143. package/web/AGENTS.md +152 -13
  144. package/web/public/app.css +380 -109
  145. package/web/public/app.js +525 -25
  146. package/web/public/index.html +68 -1
  147. package/dist/analyze-OEET7KGE.js.map +0 -1
  148. package/dist/chunk-3QHK4YQA.js.map +0 -1
  149. package/dist/chunk-42PJFMAR.js +0 -605
  150. package/dist/chunk-42PJFMAR.js.map +0 -1
  151. package/dist/chunk-4JSOYE4G.js +0 -368
  152. package/dist/chunk-4JSOYE4G.js.map +0 -1
  153. package/dist/chunk-56KS3TFQ.js.map +0 -1
  154. package/dist/chunk-73ATPIYX.js.map +0 -1
  155. package/dist/chunk-A4SLHOW7.js.map +0 -1
  156. package/dist/chunk-BAUPQXPE.js +0 -173
  157. package/dist/chunk-BAUPQXPE.js.map +0 -1
  158. package/dist/chunk-BYZDPR7N.js.map +0 -1
  159. package/dist/chunk-CD5ULRVC.js +0 -615
  160. package/dist/chunk-CD5ULRVC.js.map +0 -1
  161. package/dist/chunk-DIDUVL6O.js +0 -1365
  162. package/dist/chunk-DIDUVL6O.js.map +0 -1
  163. package/dist/chunk-EZATO3DW.js.map +0 -1
  164. package/dist/chunk-FADTCQGM.js.map +0 -1
  165. package/dist/chunk-FJP3UQGD.js.map +0 -1
  166. package/dist/chunk-LWRSMTTZ.js +0 -18
  167. package/dist/chunk-LWRSMTTZ.js.map +0 -1
  168. package/dist/chunk-M5UCXTIT.js.map +0 -1
  169. package/dist/chunk-M66SJHQQ.js +0 -206
  170. package/dist/chunk-M66SJHQQ.js.map +0 -1
  171. package/dist/chunk-MJULIKQU.js.map +0 -1
  172. package/dist/chunk-NS6R37II.js.map +0 -1
  173. package/dist/chunk-NTMMF5UD.js.map +0 -1
  174. package/dist/chunk-QCPGHBSZ.js.map +0 -1
  175. package/dist/chunk-XQUSIEKA.js.map +0 -1
  176. package/dist/chunk-Y5ZDJ4PJ.js.map +0 -1
  177. package/dist/execute-XBNVL753.js +0 -18
  178. package/dist/init-AJHCXJBP.js +0 -10
  179. package/dist/plan-QOZ3RIO6.js +0 -21
  180. package/dist/policy-6S4GFC76.js.map +0 -1
  181. package/dist/pr-EBBHD2ZX.js +0 -26
  182. package/dist/pr-review-VVMW5PO4.js +0 -20
  183. package/dist/prd-JQSZF7PR.js +0 -21
  184. package/dist/review-3F6MARWH.js +0 -19
  185. package/dist/run-RXE5GDQO.js +0 -36
  186. package/dist/web-EF5WIIGG.js.map +0 -1
  187. /package/dist/{config-PRJGWW7J.js.map → agent-OX3KPHE4.js.map} +0 -0
  188. /package/dist/{chunk-LAFNLLQP.js.map → chunk-KMRFRXIT.js.map} +0 -0
  189. /package/dist/{execute-XBNVL753.js.map → config-HMFMCY2M.js.map} +0 -0
  190. /package/dist/{init-AJHCXJBP.js.map → contract-JH2WBIO6.js.map} +0 -0
  191. /package/dist/{plan-QOZ3RIO6.js.map → diagnostics-THH42ZMP.js.map} +0 -0
  192. /package/dist/{pr-EBBHD2ZX.js.map → execute-KFNNZZHI.js.map} +0 -0
  193. /package/dist/{pr-review-VVMW5PO4.js.map → git-W72QQ6GT.js.map} +0 -0
  194. /package/dist/{prd-JQSZF7PR.js.map → init-HTN4V67Q.js.map} +0 -0
  195. /package/dist/{review-3F6MARWH.js.map → permissions-4AKNQZHK.js.map} +0 -0
  196. /package/dist/{run-RXE5GDQO.js.map → plan-SIZLGA4V.js.map} +0 -0
  197. /package/dist/{state-manager-2EDVDBAR.js.map → pr-OMWCGUOW.js.map} +0 -0
@@ -1,14 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- isoNow
4
- } from "./chunk-CD5ULRVC.js";
3
+ setTelemetrySessionId
4
+ } from "./chunk-P3LRUNC6.js";
5
5
 
6
6
  // src/core/session-state.ts
7
- import { mkdir, rename, writeFile } from "fs/promises";
7
+ import { mkdir, rename, utimes, writeFile } from "fs/promises";
8
8
  import { dirname } from "path";
9
9
  import { stripVTControlCharacters } from "util";
10
10
  var DEFAULT_LOG_LIMIT = 200;
11
11
  var DEFAULT_THROTTLE_MS = 1e3;
12
+ var DEFAULT_SESSION_HEARTBEAT_MS = 1e4;
12
13
  function emptyUsage() {
13
14
  return {
14
15
  inputTokens: null,
@@ -18,6 +19,16 @@ function emptyUsage() {
18
19
  costUsd: null
19
20
  };
20
21
  }
22
+ function emptyPhaseTiming() {
23
+ return {
24
+ harnessExecutionMs: null,
25
+ orchestrationOverheadMs: null,
26
+ harnessStartupMs: null,
27
+ ttftMs: null,
28
+ attemptCount: null,
29
+ retryDurationMs: null
30
+ };
31
+ }
21
32
  function emptyMetrics() {
22
33
  return {
23
34
  totalInputTokens: null,
@@ -53,7 +64,24 @@ function createInitialSnapshot() {
53
64
  stories: [],
54
65
  metrics: emptyMetrics(),
55
66
  execution: { iteration: 0, retries: 0, correctionCycle: 0, maxCorrectionCycles: null },
56
- git: { branch: null, baseBranch: null, commits: [] },
67
+ executions: [],
68
+ processLogs: [],
69
+ configuration: null,
70
+ resilience: {
71
+ attempt: 0,
72
+ provider: null,
73
+ model: null,
74
+ lastFailureKind: null,
75
+ cooldownUntil: null,
76
+ lastActivityAt: null
77
+ },
78
+ git: {
79
+ branch: null,
80
+ baseBranch: null,
81
+ branchCreated: null,
82
+ startCommit: null,
83
+ commits: []
84
+ },
57
85
  repository: { name: null, remoteUrl: null, branch: null, headCommit: null, root: null },
58
86
  pullRequests: [],
59
87
  logs: [],
@@ -61,7 +89,8 @@ function createInitialSnapshot() {
61
89
  warnings: [],
62
90
  lastError: null,
63
91
  nextSteps: [],
64
- environment: null
92
+ environment: null,
93
+ verification: null
65
94
  };
66
95
  }
67
96
  function accumulate(current, delta) {
@@ -140,6 +169,16 @@ function reduceSessionEvent(snapshot, event, options) {
140
169
  function isTerminalStage(stage) {
141
170
  return stage === "done" || stage === "failed";
142
171
  }
172
+ function transitionStory(story, stage, at, detail) {
173
+ if (story.stage === stage && story.stageDetail === detail) return story;
174
+ return {
175
+ ...story,
176
+ stage,
177
+ stageSince: at,
178
+ stageDetail: detail,
179
+ history: [...story.history, { at, stage, detail }]
180
+ };
181
+ }
143
182
  function deriveStageOnStoriesUpdate(story, before, at) {
144
183
  if (!story.passes) {
145
184
  return {
@@ -156,6 +195,7 @@ function deriveStageOnStoriesUpdate(story, before, at) {
156
195
  function applyEvent(snapshot, event, options) {
157
196
  switch (event.type) {
158
197
  case "session:start": {
198
+ setTelemetrySessionId(event.sessionId);
159
199
  const initial = createInitialSnapshot();
160
200
  return {
161
201
  ...initial,
@@ -174,15 +214,28 @@ function applyEvent(snapshot, event, options) {
174
214
  endedAt: null,
175
215
  durationSeconds: null,
176
216
  error: null,
217
+ ...emptyPhaseTiming(),
177
218
  ...emptyUsage()
178
219
  })),
179
- git: { branch: event.branch ?? null, baseBranch: event.baseBranch ?? null, commits: [] },
220
+ git: {
221
+ branch: event.branch ?? null,
222
+ baseBranch: event.baseBranch ?? null,
223
+ branchCreated: event.branchCreated ?? null,
224
+ startCommit: event.startCommit ?? null,
225
+ commits: []
226
+ },
180
227
  // The branch is the one piece of repository identity the session
181
228
  // already knows here; the rest waits for publishGitState. Seeding it
182
229
  // keeps git.branch and repository.branch consistent for a poll that
183
230
  // lands before the first git:update.
184
231
  repository: { ...initial.repository, branch: event.branch ?? null },
185
- environment: event.environment ?? null
232
+ configuration: event.configuration ?? null,
233
+ environment: event.environment ? {
234
+ node: event.environment.node,
235
+ platform: event.environment.platform,
236
+ agent: event.environment.agent ?? null,
237
+ model: event.environment.model ?? null
238
+ } : null
186
239
  };
187
240
  }
188
241
  case "issue:update":
@@ -220,11 +273,12 @@ function applyEvent(snapshot, event, options) {
220
273
  endedAt: null,
221
274
  durationSeconds: null,
222
275
  error: null,
276
+ ...emptyPhaseTiming(),
223
277
  ...emptyUsage()
224
278
  }
225
279
  ];
226
280
  const stories = event.phase === "review" ? snapshot.stories.map(
227
- (story) => story.passes ? { ...story, stage: "in_review", stageSince: event.at, stageDetail: null } : story
281
+ (story) => story.passes ? transitionStory(story, "in_review", event.at, null) : story
228
282
  ) : snapshot.stories;
229
283
  return {
230
284
  ...snapshot,
@@ -241,14 +295,20 @@ function applyEvent(snapshot, event, options) {
241
295
  status: event.success ? "completed" : "failed",
242
296
  endedAt: event.at,
243
297
  durationSeconds: secondsBetween(p.startedAt, event.at),
244
- error: event.error ? stripVTControlCharacters(event.error) : null
298
+ error: event.error ? stripVTControlCharacters(event.error) : null,
299
+ harnessExecutionMs: event.harnessExecutionMs ?? p.harnessExecutionMs,
300
+ orchestrationOverheadMs: event.orchestrationOverheadMs ?? p.orchestrationOverheadMs,
301
+ harnessStartupMs: event.harnessStartupMs ?? p.harnessStartupMs,
302
+ ttftMs: event.ttftMs ?? p.ttftMs,
303
+ attemptCount: event.attemptCount ?? p.attemptCount,
304
+ retryDurationMs: event.retryDurationMs ?? p.retryDurationMs
245
305
  } : p
246
306
  );
247
307
  const phasesCompleted = phases.filter((p) => p.status === "completed").length;
248
308
  const stories = !event.success ? snapshot.stories.map(
249
- (story) => isTerminalStage(story.stage) ? story : { ...story, stage: "failed", stageSince: event.at, stageDetail: null }
309
+ (story) => isTerminalStage(story.stage) ? story : transitionStory(story, "failed", event.at, null)
250
310
  ) : event.phase === "review" ? snapshot.stories.map(
251
- (story) => story.passes ? { ...story, stage: "done", stageSince: event.at, stageDetail: null } : story
311
+ (story) => story.passes ? transitionStory(story, "done", event.at, null) : story
252
312
  ) : snapshot.stories;
253
313
  return {
254
314
  ...snapshot,
@@ -268,9 +328,9 @@ function applyEvent(snapshot, event, options) {
268
328
  const stories = snapshot.stories.map((story) => {
269
329
  if (story.passes) return story;
270
330
  if (story.id === storyId) {
271
- return story.stage === "executing" ? story : { ...story, stage: "executing", stageSince: event.at, stageDetail: null };
331
+ return transitionStory(story, "executing", event.at, null);
272
332
  }
273
- return story.stage === "pending" ? story : { ...story, stage: "pending", stageSince: event.at, stageDetail: null };
333
+ return transitionStory(story, "pending", event.at, null);
274
334
  });
275
335
  return {
276
336
  ...snapshot,
@@ -291,11 +351,56 @@ function applyEvent(snapshot, event, options) {
291
351
  ...snapshot,
292
352
  execution: { ...snapshot.execution, retries: snapshot.execution.retries + 1 }
293
353
  };
354
+ case "agent:attempt":
355
+ return {
356
+ ...snapshot,
357
+ resilience: {
358
+ ...snapshot.resilience,
359
+ attempt: event.attempt,
360
+ provider: event.provider,
361
+ model: event.model ?? null,
362
+ lastActivityAt: event.at
363
+ }
364
+ };
365
+ case "failover":
366
+ return {
367
+ ...snapshot,
368
+ resilience: {
369
+ ...snapshot.resilience,
370
+ provider: event.to,
371
+ lastFailureKind: event.reason,
372
+ cooldownUntil: event.cooldownUntil ?? null,
373
+ lastActivityAt: event.at
374
+ }
375
+ };
376
+ case "agent:result":
377
+ return {
378
+ ...snapshot,
379
+ resilience: {
380
+ ...snapshot.resilience,
381
+ provider: event.provider,
382
+ lastFailureKind: event.failureKind ?? snapshot.resilience.lastFailureKind,
383
+ cooldownUntil: event.cooldownUntil ?? null,
384
+ lastActivityAt: event.at
385
+ }
386
+ };
387
+ case "agent:activity":
388
+ return {
389
+ ...snapshot,
390
+ resilience: {
391
+ ...snapshot.resilience,
392
+ provider: event.provider,
393
+ lastActivityAt: event.at
394
+ }
395
+ };
294
396
  case "stories:update": {
295
397
  const previous = new Map(snapshot.stories.map((story) => [story.id, story]));
296
398
  const stories = event.stories.map((story) => {
297
399
  const before = previous.get(story.id);
298
400
  const completedAt = !story.passes ? null : before && !before.passes ? event.at : before?.completedAt ?? null;
401
+ const stage = deriveStageOnStoriesUpdate(story, before, event.at);
402
+ const history = before?.history ?? [];
403
+ const stageChanged = before !== void 0 && before.stage !== stage.stage;
299
404
  return {
300
405
  id: story.id,
301
406
  title: story.title,
@@ -318,7 +423,8 @@ function applyEvent(snapshot, event, options) {
318
423
  cacheReadTokens: before?.cacheReadTokens ?? null,
319
424
  cacheCreationTokens: before?.cacheCreationTokens ?? null,
320
425
  costUsd: before?.costUsd ?? null,
321
- ...deriveStageOnStoriesUpdate(story, before, event.at)
426
+ ...stage,
427
+ history: stageChanged ? [...history, { at: event.at, stage: stage.stage, detail: stage.stageDetail }] : history
322
428
  };
323
429
  });
324
430
  return {
@@ -337,7 +443,11 @@ function applyEvent(snapshot, event, options) {
337
443
  const detail = event.detail ?? null;
338
444
  const current = snapshot.currentActivity;
339
445
  const since = current && current.story === story && current.tool === tool && current.detail === detail ? current.since : event.at;
340
- return { ...snapshot, currentActivity: { story, tool, detail, since } };
446
+ return {
447
+ ...snapshot,
448
+ currentActivity: { story, tool, detail, since },
449
+ resilience: { ...snapshot.resilience, lastActivityAt: event.at }
450
+ };
341
451
  }
342
452
  case "log": {
343
453
  const limit = options?.logLimit ?? DEFAULT_LOG_LIMIT;
@@ -349,12 +459,36 @@ function applyEvent(snapshot, event, options) {
349
459
  const logs = [...snapshot.logs, entry].slice(-Math.max(1, limit));
350
460
  return { ...snapshot, logs };
351
461
  }
462
+ case "process:output": {
463
+ const limit = options?.logLimit ?? DEFAULT_LOG_LIMIT;
464
+ const entry = {
465
+ at: event.at,
466
+ phase: event.phase,
467
+ executionId: event.executionId,
468
+ provider: event.provider,
469
+ stream: event.stream,
470
+ message: stripVTControlCharacters(event.message)
471
+ };
472
+ return {
473
+ ...snapshot,
474
+ processLogs: [...snapshot.processLogs, entry].slice(-Math.max(1, limit))
475
+ };
476
+ }
477
+ case "execution:update": {
478
+ const index = snapshot.executions.findIndex((entry) => entry.id === event.execution.id);
479
+ const executions = [...snapshot.executions];
480
+ if (index === -1) executions.push(event.execution);
481
+ else executions[index] = event.execution;
482
+ return { ...snapshot, executions };
483
+ }
352
484
  case "git:update":
353
485
  return {
354
486
  ...snapshot,
355
487
  git: {
356
488
  branch: event.branch ?? snapshot.git.branch,
357
489
  baseBranch: event.baseBranch ?? snapshot.git.baseBranch,
490
+ branchCreated: reported(event.branchCreated, snapshot.git.branchCreated),
491
+ startCommit: snapshot.git.startCommit,
358
492
  commits: event.commits ?? snapshot.git.commits
359
493
  },
360
494
  repository: {
@@ -371,7 +505,7 @@ function applyEvent(snapshot, event, options) {
371
505
  case "correction:cycle": {
372
506
  const stageDetail = `Cycle ${event.cycle}/${event.maxCycles}`;
373
507
  const stories = snapshot.stories.map(
374
- (story) => story.passes ? { ...story, stage: "in_correction", stageSince: event.at, stageDetail } : story
508
+ (story) => story.passes ? transitionStory(story, "in_correction", event.at, stageDetail) : story
375
509
  );
376
510
  return {
377
511
  ...snapshot,
@@ -421,6 +555,15 @@ function applyEvent(snapshot, event, options) {
421
555
  }
422
556
  };
423
557
  }
558
+ case "verify:end":
559
+ return {
560
+ ...snapshot,
561
+ verification: {
562
+ verdict: event.verdict,
563
+ level: event.level,
564
+ independence: event.independence
565
+ }
566
+ };
424
567
  case "session:end":
425
568
  return {
426
569
  ...snapshot,
@@ -435,7 +578,7 @@ function applyEvent(snapshot, event, options) {
435
578
  stories: snapshot.stories.map((story) => {
436
579
  if (isTerminalStage(story.stage)) return story;
437
580
  const stage = event.status === "completed" && story.passes ? "done" : "failed";
438
- return { ...story, stage, stageSince: event.at, stageDetail: null };
581
+ return transitionStory(story, stage, event.at, null);
439
582
  }),
440
583
  lastError: event.error ? { message: stripVTControlCharacters(event.error), at: event.at } : snapshot.lastError
441
584
  };
@@ -510,6 +653,7 @@ var FilePublisher = class extends MemoryPublisher {
510
653
  filePath;
511
654
  throttleMs;
512
655
  timer = null;
656
+ heartbeatTimer = null;
513
657
  lastWriteStartedAt = 0;
514
658
  lastWrittenVersion = 0;
515
659
  writeChain = Promise.resolve();
@@ -518,10 +662,15 @@ var FilePublisher = class extends MemoryPublisher {
518
662
  super(options);
519
663
  this.filePath = filePath;
520
664
  this.throttleMs = options.throttleMs ?? DEFAULT_THROTTLE_MS;
665
+ const heartbeatMs = options.heartbeatMs ?? DEFAULT_SESSION_HEARTBEAT_MS;
666
+ if (heartbeatMs > 0) {
667
+ this.heartbeatTimer = setInterval(() => this.enqueueHeartbeat(), heartbeatMs);
668
+ this.heartbeatTimer.unref();
669
+ }
521
670
  }
522
671
  afterPublish(event) {
523
672
  if (this.closed) return;
524
- const terminal = event.type === "phase:end" || event.type === "session:end";
673
+ const terminal = event.type === "phase:end" || event.type === "session:end" || event.type === "verify:end";
525
674
  this.scheduleWrite(terminal);
526
675
  }
527
676
  scheduleWrite(force) {
@@ -560,6 +709,24 @@ var FilePublisher = class extends MemoryPublisher {
560
709
  }
561
710
  });
562
711
  }
712
+ /**
713
+ * Keep directory-based discovery alive without changing snapshot content or
714
+ * its content-derived ETag. The write chain serializes the touch with atomic
715
+ * snapshot replacement, and no heartbeat is attempted before the first file
716
+ * has been written successfully.
717
+ */
718
+ enqueueHeartbeat() {
719
+ if (this.closed) return;
720
+ this.writeChain = this.writeChain.then(async () => {
721
+ if (this.closed || this.lastWrittenVersion === 0) return;
722
+ const now = /* @__PURE__ */ new Date();
723
+ try {
724
+ await utimes(this.filePath, now, now);
725
+ } catch (err) {
726
+ this.warnOnce(err);
727
+ }
728
+ });
729
+ }
563
730
  async flush() {
564
731
  if (this.timer !== null) {
565
732
  clearTimeout(this.timer);
@@ -571,8 +738,12 @@ var FilePublisher = class extends MemoryPublisher {
571
738
  await this.writeChain;
572
739
  }
573
740
  async close() {
574
- await this.flush();
741
+ if (this.heartbeatTimer !== null) {
742
+ clearInterval(this.heartbeatTimer);
743
+ this.heartbeatTimer = null;
744
+ }
575
745
  this.closed = true;
746
+ await this.flush();
576
747
  }
577
748
  };
578
749
  async function atomicWriteFile(path, content) {
@@ -592,225 +763,13 @@ function getSessionPublisher() {
592
763
  return _publisher;
593
764
  }
594
765
 
595
- // src/core/verbose.ts
596
- var _verbose = false;
597
- function setVerbose(enabled) {
598
- _verbose = enabled;
599
- }
600
- function isVerbose() {
601
- return _verbose;
602
- }
603
- var _onOutput;
604
- function setOutputCallback(callback) {
605
- _onOutput = callback;
606
- }
607
- function getOutputCallback() {
608
- return _onOutput;
609
- }
610
- var _onStoryUpdate;
611
- function setStoryUpdateCallback(callback) {
612
- _onStoryUpdate = callback;
613
- }
614
- function getStoryUpdateCallback() {
615
- return _onStoryUpdate;
616
- }
617
- var _onStoryStage;
618
- function setStoryStageCallback(callback) {
619
- _onStoryStage = callback;
620
- }
621
- function getStoryStageCallback() {
622
- return _onStoryStage;
623
- }
624
- var _globalTimeout;
625
- function setGlobalTimeout(ms) {
626
- _globalTimeout = ms;
627
- }
628
- function getGlobalTimeout() {
629
- return _globalTimeout;
630
- }
631
- var _inactivityTimeout;
632
- function setInactivityTimeout(ms) {
633
- _inactivityTimeout = ms;
634
- }
635
- function getInactivityTimeout() {
636
- return _inactivityTimeout;
637
- }
638
-
639
- // src/ui/logger.ts
640
- import chalk from "chalk";
641
- import ora from "ora";
642
- function useUnicode() {
643
- if (process.env.NO_COLOR != null && process.env.NO_COLOR !== "") return false;
644
- if (!process.stdout.isTTY) return false;
645
- return true;
646
- }
647
- function useColor() {
648
- if (process.env.NO_COLOR != null && process.env.NO_COLOR !== "") return false;
649
- if (!process.stdout.isTTY) return false;
650
- return true;
651
- }
652
- function getIcons() {
653
- if (useUnicode()) {
654
- return {
655
- success: "\u2713",
656
- fail: "\u2717",
657
- pending: "\u23F3",
658
- retry: "\u21BB",
659
- warn: "\u26A0",
660
- start: "\u25B6",
661
- end: "\u25A0",
662
- notReached: "\u25CB",
663
- tool: "\u25B8",
664
- connector: "\u2502"
665
- };
666
- }
667
- return {
668
- success: "[OK]",
669
- fail: "[FAIL]",
670
- pending: "[...]",
671
- retry: "[RETRY]",
672
- warn: "[WARN]",
673
- start: "[START]",
674
- end: "[END]",
675
- notReached: "[ ]",
676
- tool: ">",
677
- connector: "|"
678
- };
679
- }
680
- function getTermWidth() {
681
- return process.stdout.columns ?? 80;
682
- }
683
- function emit(line, level = "info") {
684
- getSessionPublisher().publish({ type: "log", at: isoNow(), level, message: line });
685
- const cb = getOutputCallback();
686
- if (cb) {
687
- cb(line);
688
- } else {
689
- console.log(line);
690
- }
691
- }
692
- function printSuccess(message) {
693
- const icons = getIcons();
694
- if (useColor()) {
695
- emit(chalk.green(`${icons.success} ${message}`));
696
- } else {
697
- emit(`${icons.success} ${message}`);
698
- }
699
- }
700
- function printError(message) {
701
- const icons = getIcons();
702
- if (useColor()) {
703
- emit(chalk.red(`${icons.fail} ${message}`), "error");
704
- } else {
705
- emit(`${icons.fail} ${message}`, "error");
706
- }
707
- }
708
- function printWarning(message) {
709
- const icons = getIcons();
710
- if (useColor()) {
711
- emit(chalk.yellow(`${icons.warn} ${message}`), "warn");
712
- } else {
713
- emit(`${icons.warn} ${message}`, "warn");
714
- }
715
- }
716
- function printRetry(message) {
717
- const icons = getIcons();
718
- if (useColor()) {
719
- emit(chalk.yellow(`${icons.retry} ${message}`), "warn");
720
- } else {
721
- emit(`${icons.retry} ${message}`, "warn");
722
- }
723
- }
724
- function printInfo(message) {
725
- const icons = getIcons();
726
- if (useColor()) {
727
- emit(chalk.blue(`${icons.start} ${message}`));
728
- } else {
729
- emit(`${icons.start} ${message}`);
730
- }
731
- }
732
- function createSpinner(message) {
733
- return ora({
734
- text: message,
735
- color: "blue",
736
- spinner: "dots"
737
- });
738
- }
739
- function formatDuration(totalSeconds) {
740
- const hours = Math.floor(totalSeconds / 3600);
741
- const mins = Math.floor(totalSeconds % 3600 / 60);
742
- const secs = totalSeconds % 60;
743
- if (hours > 0) {
744
- return `${hours}h ${mins}m ${secs}s`;
745
- }
746
- if (mins > 0) {
747
- return `${mins}m ${secs}s`;
748
- }
749
- return `${secs}s`;
750
- }
751
- var ElapsedTimer = class {
752
- constructor(onTick) {
753
- this.onTick = onTick;
754
- this.startTime = Date.now();
755
- }
756
- onTick;
757
- startTime;
758
- intervalId = null;
759
- cleanup = () => this.stop();
760
- start() {
761
- this.startTime = Date.now();
762
- this.intervalId = setInterval(() => {
763
- const seconds = Math.floor((Date.now() - this.startTime) / 1e3);
764
- this.onTick(formatDuration(seconds));
765
- }, 1e3);
766
- if (this.intervalId && typeof this.intervalId === "object" && "unref" in this.intervalId) {
767
- this.intervalId.unref();
768
- }
769
- process.on("exit", this.cleanup);
770
- return this;
771
- }
772
- getElapsedSeconds() {
773
- return Math.floor((Date.now() - this.startTime) / 1e3);
774
- }
775
- stop() {
776
- if (this.intervalId) {
777
- clearInterval(this.intervalId);
778
- this.intervalId = null;
779
- }
780
- process.removeListener("exit", this.cleanup);
781
- return this.getElapsedSeconds();
782
- }
783
- };
784
-
785
766
  export {
767
+ createInitialSnapshot,
768
+ reduceSessionEvent,
786
769
  NullPublisher,
787
770
  MemoryPublisher,
788
771
  FilePublisher,
789
772
  setSessionPublisher,
790
- getSessionPublisher,
791
- setVerbose,
792
- isVerbose,
793
- setOutputCallback,
794
- getOutputCallback,
795
- setStoryUpdateCallback,
796
- getStoryUpdateCallback,
797
- setStoryStageCallback,
798
- getStoryStageCallback,
799
- setGlobalTimeout,
800
- getGlobalTimeout,
801
- setInactivityTimeout,
802
- getInactivityTimeout,
803
- useUnicode,
804
- useColor,
805
- getIcons,
806
- getTermWidth,
807
- printSuccess,
808
- printError,
809
- printWarning,
810
- printRetry,
811
- printInfo,
812
- createSpinner,
813
- formatDuration,
814
- ElapsedTimer
773
+ getSessionPublisher
815
774
  };
816
- //# sourceMappingURL=chunk-BYZDPR7N.js.map
775
+ //# sourceMappingURL=chunk-6QYMSNVA.js.map