cloving 1.3.0 → 1.4.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 (396) hide show
  1. package/README.md +235 -206
  2. package/dist/src/agent-harness-shell.d.ts +2 -1
  3. package/dist/src/agent-harness-shell.js +8 -5
  4. package/dist/src/authority/command-wait-checks.d.ts +8 -0
  5. package/dist/src/authority/command-wait-checks.js +88 -0
  6. package/dist/src/authority/command-wait-results.d.ts +15 -0
  7. package/dist/src/authority/command-wait-results.js +73 -0
  8. package/dist/src/authority/command-wait-view.d.ts +21 -0
  9. package/dist/src/authority/command-wait-view.js +34 -0
  10. package/dist/src/authority/command-waits.d.ts +39 -0
  11. package/dist/src/authority/command-waits.js +97 -0
  12. package/dist/src/authority/commands.js +22 -1
  13. package/dist/src/authority/dispatch-decision.d.ts +8 -0
  14. package/dist/src/authority/dispatch-decision.js +26 -1
  15. package/dist/src/authority/effect-requests.js +6 -3
  16. package/dist/src/authority/effect-results.js +4 -2
  17. package/dist/src/authority/history-state.d.ts +2 -0
  18. package/dist/src/authority/history-state.js +13 -1
  19. package/dist/src/authority/instants.d.ts +1 -0
  20. package/dist/src/authority/instants.js +21 -0
  21. package/dist/src/authority/reduction-state.d.ts +2 -0
  22. package/dist/src/authority/reduction-state.js +5 -0
  23. package/dist/src/authority/reduction.js +24 -0
  24. package/dist/src/authority/status-input.d.ts +2 -0
  25. package/dist/src/authority/status.d.ts +2 -0
  26. package/dist/src/authority/status.js +1 -0
  27. package/dist/src/authority/stop-sequence.d.ts +1 -0
  28. package/dist/src/authority/stop-sequence.js +2 -1
  29. package/dist/src/authority/transitions.d.ts +6 -4
  30. package/dist/src/authority/transitions.js +7 -0
  31. package/dist/src/broker/actor/authority-queries.d.ts +2 -1
  32. package/dist/src/broker/actor/authority-queries.js +14 -5
  33. package/dist/src/broker/actor/authority-receipts.js +1 -1
  34. package/dist/src/broker/actor/authority-settings.js +2 -2
  35. package/dist/src/broker/actor/authority-upgrades.d.ts +19 -2
  36. package/dist/src/broker/actor/authority-upgrades.js +7 -2
  37. package/dist/src/broker/actor/delivery-intents.js +2 -0
  38. package/dist/src/broker/actor/effect-delivery.d.ts +1 -1
  39. package/dist/src/broker/actor/effect-delivery.js +15 -2
  40. package/dist/src/broker/actor/effect-plan.js +11 -2
  41. package/dist/src/broker/actor/effect-queries.d.ts +4 -0
  42. package/dist/src/broker/actor/effect-queries.js +9 -1
  43. package/dist/src/broker/actor/effect-turn.js +2 -0
  44. package/dist/src/broker/actor/stop-plan.js +9 -3
  45. package/dist/src/broker/actor/stop-turn.d.ts +2 -0
  46. package/dist/src/broker/actor/stop-turn.js +24 -11
  47. package/dist/src/broker/actor/turn-draft.d.ts +1 -0
  48. package/dist/src/broker/actor/turn-draft.js +4 -0
  49. package/dist/src/broker/actor/turn-state.js +5 -1
  50. package/dist/src/broker/authority-admission.js +60 -0
  51. package/dist/src/broker/authority-restore.d.ts +2 -0
  52. package/dist/src/broker/authority-restore.js +35 -1
  53. package/dist/src/broker/command-wait-restore.d.ts +31 -0
  54. package/dist/src/broker/command-wait-restore.js +86 -0
  55. package/dist/src/broker/decision-restore.d.ts +1 -1
  56. package/dist/src/broker/decision-restore.js +1 -0
  57. package/dist/src/broker/effect-upgrade.d.ts +2 -0
  58. package/dist/src/broker/effect-upgrade.js +8 -2
  59. package/dist/src/cli/arguments.d.ts +13 -0
  60. package/dist/src/cli/arguments.js +42 -3
  61. package/dist/src/cli/chat-stores.d.ts +2 -0
  62. package/dist/src/cli/chat-stores.js +113 -0
  63. package/dist/src/cli/client.d.ts +4 -1
  64. package/dist/src/cli/client.js +9 -3
  65. package/dist/src/cli/command-requests.d.ts +17 -0
  66. package/dist/src/cli/command-requests.js +43 -0
  67. package/dist/src/cli/command-waits.d.ts +9 -0
  68. package/dist/src/cli/command-waits.js +51 -0
  69. package/dist/src/cli/commands.js +14 -41
  70. package/dist/src/cli/config-file.d.ts +2 -2
  71. package/dist/src/cli/config-file.js +4 -2
  72. package/dist/src/cli/doctor-workflows.d.ts +5 -2
  73. package/dist/src/cli/doctor-workflows.js +12 -7
  74. package/dist/src/cli/doctor.js +6 -9
  75. package/dist/src/cli/harness-probes.d.ts +2 -0
  76. package/dist/src/cli/harness-probes.js +1 -1
  77. package/dist/src/cli/harness-processes.d.ts +6 -0
  78. package/dist/src/cli/harness-processes.js +36 -0
  79. package/dist/src/cli/init-command.d.ts +9 -0
  80. package/dist/src/cli/init-command.js +52 -0
  81. package/dist/src/cli/init-prompt-command.d.ts +10 -0
  82. package/dist/src/cli/init-prompt-command.js +145 -0
  83. package/dist/src/cli/init-prompt.d.ts +35 -0
  84. package/dist/src/cli/init-prompt.js +74 -0
  85. package/dist/src/cli/init-selection.d.ts +13 -0
  86. package/dist/src/cli/init-selection.js +75 -0
  87. package/dist/src/cli/main.d.ts +2 -0
  88. package/dist/src/cli/main.js +37 -7
  89. package/dist/src/cli/mcp-server.d.ts +1 -0
  90. package/dist/src/cli/mcp-server.js +156 -0
  91. package/dist/src/cli/mcp-tools.d.ts +15 -0
  92. package/dist/src/cli/mcp-tools.js +80 -0
  93. package/dist/src/cli/render.d.ts +1 -1
  94. package/dist/src/cli/render.js +17 -30
  95. package/dist/src/cli/report-ci-jobs.d.ts +23 -0
  96. package/dist/src/cli/report-ci-jobs.js +103 -0
  97. package/dist/src/cli/report-ci-usage.d.ts +9 -0
  98. package/dist/src/cli/report-ci-usage.js +185 -0
  99. package/dist/src/cli/report-ci.d.ts +13 -0
  100. package/dist/src/cli/report-ci.js +74 -0
  101. package/dist/src/cli/report-closures.d.ts +6 -0
  102. package/dist/src/cli/report-closures.js +97 -0
  103. package/dist/src/cli/report-flow.d.ts +7 -0
  104. package/dist/src/cli/report-flow.js +122 -0
  105. package/dist/src/cli/report-github-reader.d.ts +29 -0
  106. package/dist/src/cli/report-github-reader.js +157 -0
  107. package/dist/src/cli/report-github.d.ts +15 -8
  108. package/dist/src/cli/report-github.js +131 -150
  109. package/dist/src/cli/report-operator.d.ts +3 -0
  110. package/dist/src/cli/report-operator.js +40 -0
  111. package/dist/src/cli/report-ready.d.ts +3 -0
  112. package/dist/src/cli/report-ready.js +96 -0
  113. package/dist/src/cli/report-text.d.ts +9 -0
  114. package/dist/src/cli/report-text.js +33 -0
  115. package/dist/src/cli/report.d.ts +1 -0
  116. package/dist/src/cli/report.js +104 -27
  117. package/dist/src/cli/runtime-arguments.d.ts +2 -1
  118. package/dist/src/cli/runtime-arguments.js +7 -2
  119. package/dist/src/cli/schedule-arguments.d.ts +14 -0
  120. package/dist/src/cli/schedule-arguments.js +26 -0
  121. package/dist/src/cli/schedule-render.d.ts +22 -0
  122. package/dist/src/cli/schedule-render.js +73 -0
  123. package/dist/src/cli/terminal-text.d.ts +3 -0
  124. package/dist/src/cli/terminal-text.js +28 -0
  125. package/dist/src/cli/usage-render.d.ts +1 -0
  126. package/dist/src/cli/usage-render.js +28 -1
  127. package/dist/src/cli/usage.d.ts +1 -1
  128. package/dist/src/cli/usage.js +50 -7
  129. package/dist/src/cli/watch-actions.js +2 -2
  130. package/dist/src/cli/watch-board-view.js +19 -2
  131. package/dist/src/cli/watch-board.d.ts +4 -2
  132. package/dist/src/cli/watch-board.js +53 -43
  133. package/dist/src/cli/watch-card-forms.d.ts +1 -1
  134. package/dist/src/cli/watch-card-forms.js +11 -10
  135. package/dist/src/cli/watch-client.d.ts +1 -1
  136. package/dist/src/cli/watch-client.js +2 -11
  137. package/dist/src/cli/watch-header-view.d.ts +0 -5
  138. package/dist/src/cli/watch-header-view.js +8 -20
  139. package/dist/src/cli/watch-model.d.ts +4 -1
  140. package/dist/src/cli/watch-model.js +17 -11
  141. package/dist/src/cli/watch-page.js +12 -14
  142. package/dist/src/cli/watch-phases.d.ts +12 -3
  143. package/dist/src/cli/watch-phases.js +68 -11
  144. package/dist/src/cli/watch-pr-board.js +1 -1
  145. package/dist/src/cli/watch-pr-command.d.ts +1 -0
  146. package/dist/src/cli/watch-pull-request.js +18 -8
  147. package/dist/src/cli/watch-reads.d.ts +2 -0
  148. package/dist/src/cli/watch-reads.js +17 -16
  149. package/dist/src/cli/watch-render.d.ts +14 -0
  150. package/dist/src/cli/watch-run-input.d.ts +0 -1
  151. package/dist/src/cli/watch-run-input.js +0 -13
  152. package/dist/src/cli/watch-server.js +6 -6
  153. package/dist/src/cli/watch-view.d.ts +0 -3
  154. package/dist/src/cli/watch-view.js +13 -13
  155. package/dist/src/cli.js +59 -5
  156. package/dist/src/command-wait.d.ts +4 -0
  157. package/dist/src/command-wait.js +45 -0
  158. package/dist/src/configuration-change.d.ts +7 -0
  159. package/dist/src/configuration-change.js +14 -0
  160. package/dist/src/configuration-schema.d.ts +9 -0
  161. package/dist/src/configuration-schema.js +6 -2
  162. package/dist/src/daemon/attachment-file.d.ts +6 -0
  163. package/dist/src/daemon/attachment-file.js +86 -0
  164. package/dist/src/daemon/attachments.d.ts +14 -0
  165. package/dist/src/daemon/attachments.js +17 -0
  166. package/dist/src/daemon/claude-recovery.d.ts +50 -0
  167. package/dist/src/daemon/claude-recovery.js +204 -0
  168. package/dist/src/daemon/claude-stream.d.ts +27 -0
  169. package/dist/src/daemon/claude-stream.js +63 -0
  170. package/dist/src/daemon/claude-turn.d.ts +11 -25
  171. package/dist/src/daemon/claude-turn.js +64 -81
  172. package/dist/src/daemon/claude-worker.d.ts +2 -1
  173. package/dist/src/daemon/claude-worker.js +124 -122
  174. package/dist/src/daemon/daemon-adapters.d.ts +2 -0
  175. package/dist/src/daemon/daemon-adapters.js +26 -2
  176. package/dist/src/daemon/daemon-loops.d.ts +3 -1
  177. package/dist/src/daemon/daemon-loops.js +3 -1
  178. package/dist/src/daemon/daemon.js +14 -6
  179. package/dist/src/daemon/github-attach.d.ts +2 -0
  180. package/dist/src/daemon/github-attach.js +149 -0
  181. package/dist/src/daemon/github-ci-filters.d.ts +6 -0
  182. package/dist/src/daemon/github-ci-filters.js +138 -0
  183. package/dist/src/daemon/github-ci-workflows.d.ts +10 -0
  184. package/dist/src/daemon/github-ci-workflows.js +148 -0
  185. package/dist/src/daemon/github-ci.js +22 -4
  186. package/dist/src/daemon/github-issue-methods.d.ts +2 -0
  187. package/dist/src/daemon/github-issue-methods.js +37 -2
  188. package/dist/src/daemon/github-merge-call.d.ts +2 -1
  189. package/dist/src/daemon/github-merge-call.js +10 -3
  190. package/dist/src/daemon/github-merge.js +52 -24
  191. package/dist/src/daemon/github-methods.js +12 -2
  192. package/dist/src/daemon/github-operation.d.ts +4 -0
  193. package/dist/src/daemon/github-operation.js +6 -0
  194. package/dist/src/daemon/github-worker.d.ts +4 -0
  195. package/dist/src/daemon/github-worker.js +6 -0
  196. package/dist/src/daemon/issue-queue.d.ts +1 -1
  197. package/dist/src/daemon/issue-queue.js +16 -11
  198. package/dist/src/daemon/notify-events.d.ts +6 -0
  199. package/dist/src/daemon/notify-events.js +84 -0
  200. package/dist/src/daemon/notify-journal.js +1 -1
  201. package/dist/src/daemon/notify-message.d.ts +4 -1
  202. package/dist/src/daemon/notify-message.js +36 -0
  203. package/dist/src/daemon/notify.d.ts +1 -1
  204. package/dist/src/daemon/notify.js +5 -77
  205. package/dist/src/daemon/operation-journal.d.ts +1 -0
  206. package/dist/src/daemon/queue-status.d.ts +9 -3
  207. package/dist/src/daemon/queue-status.js +17 -4
  208. package/dist/src/daemon/resume-index-lock.d.ts +13 -0
  209. package/dist/src/daemon/resume-index-lock.js +160 -0
  210. package/dist/src/daemon/resume-worktree.d.ts +7 -0
  211. package/dist/src/daemon/resume-worktree.js +29 -0
  212. package/dist/src/daemon/retrying-turn.d.ts +2 -12
  213. package/dist/src/daemon/retrying-turn.js +98 -172
  214. package/dist/src/daemon/schedule-control.d.ts +26 -0
  215. package/dist/src/daemon/schedule-control.js +110 -0
  216. package/dist/src/daemon/schedule-journal.d.ts +2 -15
  217. package/dist/src/daemon/schedule-journal.js +67 -4
  218. package/dist/src/daemon/ticket-registration.d.ts +2 -7
  219. package/dist/src/daemon/ticket-registration.js +15 -81
  220. package/dist/src/daemon/turn-attempt-journal.d.ts +38 -0
  221. package/dist/src/daemon/turn-attempt-journal.js +136 -0
  222. package/dist/src/daemon/turn-attempts.d.ts +47 -0
  223. package/dist/src/daemon/turn-attempts.js +166 -0
  224. package/dist/src/daemon/turn-report.d.ts +8 -1
  225. package/dist/src/daemon/turn-report.js +39 -0
  226. package/dist/src/daemon/work-tree.d.ts +9 -0
  227. package/dist/src/daemon/work-tree.js +20 -0
  228. package/dist/src/daemon/worker-profile.d.ts +8 -0
  229. package/dist/src/daemon/worker-profile.js +40 -0
  230. package/dist/src/daemon/workflow-schedules.d.ts +3 -0
  231. package/dist/src/daemon/workflow-schedules.js +98 -45
  232. package/dist/src/daemon/worktree-processes.d.ts +9 -0
  233. package/dist/src/daemon/worktree-processes.js +151 -0
  234. package/dist/src/doctor-report.d.ts +3 -5
  235. package/dist/src/doctor-report.js +6 -13
  236. package/dist/src/harness-start.d.ts +15 -0
  237. package/dist/src/harness-start.js +37 -0
  238. package/dist/src/host/command-wait.d.ts +38 -0
  239. package/dist/src/host/command-wait.js +115 -0
  240. package/dist/src/host/start-delivery.d.ts +5 -7
  241. package/dist/src/host/start-delivery.js +3 -15
  242. package/dist/src/host/step-bridge-host.d.ts +2 -0
  243. package/dist/src/host/step-bridge-host.js +21 -0
  244. package/dist/src/operation-identity.d.ts +1 -0
  245. package/dist/src/operation-identity.js +4 -0
  246. package/dist/src/private-config.schema.json +40 -0
  247. package/dist/src/project-config.schema.json +40 -0
  248. package/dist/src/project-settings.d.ts +2 -2
  249. package/dist/src/project-settings.js +3 -1
  250. package/dist/src/queue-spend.d.ts +4 -0
  251. package/dist/src/queue-spend.js +21 -0
  252. package/dist/src/relay/relay-service.d.ts +3 -0
  253. package/dist/src/relay/relay-service.js +5 -3
  254. package/dist/src/report-ci.d.ts +164 -0
  255. package/dist/src/report-ci.js +32 -0
  256. package/dist/src/report-effects.d.ts +15 -0
  257. package/dist/src/report-effects.js +212 -0
  258. package/dist/src/report-flow-types.d.ts +38 -0
  259. package/dist/src/report-flow-types.js +7 -0
  260. package/dist/src/report-flow.d.ts +43 -0
  261. package/dist/src/report-flow.js +193 -0
  262. package/dist/src/report-operator.d.ts +67 -0
  263. package/dist/src/report-operator.js +25 -0
  264. package/dist/src/report-quality.d.ts +221 -0
  265. package/dist/src/report-quality.js +94 -0
  266. package/dist/src/report-ready.d.ts +23 -0
  267. package/dist/src/report-ready.js +117 -0
  268. package/dist/src/report-statistics.d.ts +2 -1
  269. package/dist/src/report-statistics.js +40 -4
  270. package/dist/src/report.d.ts +588 -47
  271. package/dist/src/report.js +156 -41
  272. package/dist/src/schedule-progress.d.ts +33 -0
  273. package/dist/src/schedule-progress.js +45 -0
  274. package/dist/src/service/active-tickets.js +6 -6
  275. package/dist/src/service/command-waits.d.ts +34 -0
  276. package/dist/src/service/command-waits.js +86 -0
  277. package/dist/src/service/expiry.js +2 -0
  278. package/dist/src/service/gateway.js +3 -1
  279. package/dist/src/service/queue-spend.d.ts +6 -0
  280. package/dist/src/service/queue-spend.js +68 -0
  281. package/dist/src/service/replies.d.ts +8 -1
  282. package/dist/src/service/replies.js +11 -0
  283. package/dist/src/service/run-handlers.js +22 -2
  284. package/dist/src/service/run-metadata.d.ts +15 -1
  285. package/dist/src/service/run-metadata.js +93 -8
  286. package/dist/src/service/service.d.ts +4 -0
  287. package/dist/src/service/service.js +8 -8
  288. package/dist/src/service/submission-handler.js +18 -4
  289. package/dist/src/service/worker-port.js +9 -1
  290. package/dist/src/service/worker-totals.d.ts +2 -0
  291. package/dist/src/service/worker-totals.js +1 -1
  292. package/dist/src/step-bridge.d.ts +20 -0
  293. package/dist/src/steps/command-wait.d.ts +12 -0
  294. package/dist/src/steps/command-wait.js +26 -0
  295. package/dist/src/steps/open-decision-wait.d.ts +2 -0
  296. package/dist/src/steps/{decision-gate.js → open-decision-wait.js} +0 -7
  297. package/dist/src/steps/review-by-person.js +4 -1
  298. package/dist/src/steps/wait-for-command.d.ts +3 -0
  299. package/dist/src/steps/wait-for-command.js +17 -0
  300. package/dist/src/steps/wait-for-decision.js +1 -1
  301. package/dist/src/storage/command-wait-rows.d.ts +30 -0
  302. package/dist/src/storage/command-wait-rows.js +96 -0
  303. package/dist/src/storage/decision-snapshots.d.ts +8 -1
  304. package/dist/src/storage/decision-snapshots.js +15 -1
  305. package/dist/src/storage/domain-changes.d.ts +2 -0
  306. package/dist/src/storage/domain-changes.js +3 -0
  307. package/dist/src/storage/domain-schema.d.ts +13 -1
  308. package/dist/src/storage/domain-schema.js +31 -4
  309. package/dist/src/storage/effect-schema.d.ts +1 -0
  310. package/dist/src/storage/effect-schema.js +5 -0
  311. package/dist/src/testing-requests.d.ts +15 -0
  312. package/dist/src/testing-requests.js +93 -0
  313. package/dist/src/testing.d.ts +4 -10
  314. package/dist/src/testing.js +33 -86
  315. package/dist/src/worker-usage.d.ts +1 -0
  316. package/dist/src/worker-usage.js +3 -0
  317. package/dist/src/workflow-candidate.d.ts +1 -0
  318. package/dist/src/workflow-candidate.js +12 -1
  319. package/dist/src/workflow-contracts.d.ts +1 -0
  320. package/dist/src/workflow-contracts.js +9 -2
  321. package/dist/src/workflow-discovery.d.ts +2 -13
  322. package/dist/src/workflow-discovery.js +1 -13
  323. package/dist/src/workflow-refusals.js +0 -1
  324. package/dist/src/workflows.d.ts +1 -1
  325. package/dist/src/workflows.js +1 -1
  326. package/dist/tools/red-green-command.d.ts +1 -0
  327. package/dist/tools/red-green-command.js +174 -0
  328. package/dist/tools/red-green-process.d.ts +10 -0
  329. package/dist/tools/red-green-process.js +59 -0
  330. package/dist/tools/self-host-processes.d.ts +2 -0
  331. package/dist/tools/self-host-processes.js +32 -13
  332. package/dist/tools/self-host-project.js +13 -78
  333. package/dist/tools/self-host-runtime.js +1 -1
  334. package/dist/tools/self-host.js +42 -10
  335. package/dist/tools/template-command.d.ts +1 -0
  336. package/dist/tools/template-command.js +160 -0
  337. package/dist/tools/template-copy.d.ts +11 -0
  338. package/dist/tools/template-copy.js +130 -0
  339. package/dist/tools/template-files.d.ts +17 -0
  340. package/dist/tools/template-files.js +74 -0
  341. package/dist/tools/workflow-bundles.js +1 -0
  342. package/dist/tools/workflow-candidate-counts.js +2 -2
  343. package/dist/tools/workflow-candidate-shape.d.ts +3 -0
  344. package/dist/tools/workflow-candidate-shape.js +90 -0
  345. package/dist/tools/workflow-candidate-source.js +15 -1
  346. package/dist/tools/workflow-candidate-syntax.d.ts +2 -2
  347. package/dist/tools/workflow-candidate-syntax.js +24 -94
  348. package/dist/tools/workflow-command.js +1 -2
  349. package/dist/tools/workflow-folders.d.ts +2 -3
  350. package/dist/tools/workflow-folders.js +1 -1
  351. package/package.json +16 -8
  352. package/templates/develop/README.md +12 -0
  353. package/templates/develop/develop/flow-tests.ts +37 -0
  354. package/templates/develop/develop/pull.ts +5 -1
  355. package/templates/develop/develop/refusal-tests.ts +77 -0
  356. package/templates/develop/develop/turn.ts +2 -1
  357. package/templates/idea-to-issue/README.md +27 -0
  358. package/templates/idea-to-issue/idea-to-issue/effects.ts +52 -0
  359. package/templates/idea-to-issue/idea-to-issue/leave.ts +10 -0
  360. package/templates/idea-to-issue/idea-to-issue/next.ts +29 -0
  361. package/templates/idea-to-issue/idea-to-issue/post-person.ts +18 -0
  362. package/templates/idea-to-issue/idea-to-issue/post.ts +50 -0
  363. package/templates/idea-to-issue/idea-to-issue/queue.ts +27 -0
  364. package/templates/idea-to-issue/idea-to-issue/review.ts +42 -0
  365. package/templates/idea-to-issue/idea-to-issue/scenarios.ts +50 -0
  366. package/templates/idea-to-issue/idea-to-issue/turn.ts +40 -0
  367. package/templates/idea-to-issue/idea-to-issue.declaration.json +16 -0
  368. package/templates/idea-to-issue/idea-to-issue.test.ts +79 -0
  369. package/templates/idea-to-issue/idea-to-issue.ts +49 -0
  370. package/templates/idea-to-issue/template.json +81 -0
  371. package/templates/review-day/README.md +97 -0
  372. package/templates/review-day/review-day/critique.ts +58 -0
  373. package/templates/review-day/review-day/decision.ts +29 -0
  374. package/templates/review-day/review-day/facts.ts +69 -0
  375. package/templates/review-day/review-day/label.ts +29 -0
  376. package/templates/review-day/review-day/next.ts +28 -0
  377. package/templates/review-day/review-day/post.ts +27 -0
  378. package/templates/review-day/review-day/report.ts +98 -0
  379. package/templates/review-day/review-day/rules.ts +12 -0
  380. package/templates/review-day/review-day/scenarios.ts +76 -0
  381. package/templates/review-day/review-day/turn.ts +73 -0
  382. package/templates/review-day/review-day.declaration.json +16 -0
  383. package/templates/review-day/review-day.test.ts +362 -0
  384. package/templates/review-day/review-day.ts +37 -0
  385. package/templates/review-day/template.json +56 -0
  386. package/dist/src/cli/watch-start-view.d.ts +0 -18
  387. package/dist/src/cli/watch-start-view.js +0 -77
  388. package/dist/src/steps/decision-gate.d.ts +0 -3
  389. package/dist/workflows/develop-prd.d.ts +0 -7
  390. package/dist/workflows/develop-prd.js +0 -165
  391. package/dist/workflows/develop-ticket.d.ts +0 -9
  392. package/dist/workflows/develop-ticket.js +0 -207
  393. package/dist/workflows/issue-labels.d.ts +0 -16
  394. package/dist/workflows/issue-labels.js +0 -70
  395. package/dist/workflows/ticket-prompts.d.ts +0 -35
  396. package/dist/workflows/ticket-prompts.js +0 -147
@@ -6,75 +6,18 @@ import { codexStream } from "./codex-stream.js";
6
6
  import { cursorStream } from "./cursor-stream.js";
7
7
  import { opencodeStream } from "./opencode-stream.js";
8
8
  import { textOutput } from "./text-output.js";
9
- import { grokResultFields, grokUsageWarnings } from "../grok-result.js";
9
+ import { grokUsageWarnings } from "../grok-result.js";
10
10
  import { geminiStream } from "./gemini-stream.js";
11
- import { priceOf, modelUsageOf, usageOf, } from "../worker-usage.js";
11
+ import { parsed, textOf, streamResult, } from "./claude-stream.js";
12
+ import { usageOf } from "../worker-usage.js";
13
+ export { streamResult, capacityRefusal, retryKind } from "./claude-stream.js";
12
14
  import { commandModel } from "../token-prices.js";
13
15
  import { shellTurnScript } from "../agent-harness-shell.js";
14
- function parsed(line) {
15
- try {
16
- const value = JSON.parse(line);
17
- return value;
18
- }
19
- catch {
20
- return undefined;
21
- }
22
- }
23
- function numberOf(value) {
24
- return typeof value === "number" ? value : null;
25
- }
26
- function textOf(value) {
27
- return typeof value === "string" ? value : null;
28
- }
29
- function networkFailure(result) {
30
- return (result?.failed === true &&
31
- result.text.startsWith("API Error: Can't reach the API server"));
32
- }
33
- export function capacityRefusal(result) {
34
- if (networkFailure(result))
35
- return false;
36
- return (result?.failed === true &&
37
- ["success", "error_during_execution"].includes(result.subtype ?? "error_during_execution") &&
38
- (result.text ===
39
- "Selected model is at capacity. Please try a different model." ||
40
- result.errorType === "overloaded"));
41
- }
42
- export function retryKind(result) {
43
- if (networkFailure(result))
44
- return "network";
45
- return capacityRefusal(result) ? "capacity" : null;
46
- }
47
16
  export function succeeded(end) {
48
17
  return (end.reason === undefined &&
49
18
  end.exitCode === 0 &&
50
19
  end.result?.failed === false);
51
20
  }
52
- export function streamResult(line, worker) {
53
- if (!line.includes('"result"'))
54
- return undefined;
55
- const record = parsed(line);
56
- const field = (name) => Reflect.get(Object(record), name);
57
- if (field("type") !== "result")
58
- return undefined;
59
- const models = modelUsageOf(field("models"));
60
- return {
61
- ...(models === undefined ? {} : { models }),
62
- ...(typeof field("subtype") === "string"
63
- ? { subtype: String(field("subtype")) }
64
- : {}),
65
- ...(typeof field("error") === "string"
66
- ? { errorType: String(field("error")) }
67
- : {}),
68
- text: textOf(field("result")) ?? "",
69
- failed: field("is_error") === true,
70
- costUsd: priceOf(field("total_cost_usd")),
71
- usage: usageOf(field("usage")),
72
- model: textOf(field("model")),
73
- sessionId: textOf(field("session_id")),
74
- turns: numberOf(field("num_turns")),
75
- ...(worker === "grok" ? grokResultFields(record) : {}),
76
- };
77
- }
78
21
  function ended(stream) {
79
22
  return new Promise((settle) => {
80
23
  stream.end(settle);
@@ -129,33 +72,65 @@ function spawnTurn(start) {
129
72
  throw error;
130
73
  }
131
74
  }
75
+ function streamMetadata(stream, event) {
76
+ if (typeof event !== "object" || event === null)
77
+ return false;
78
+ const field = (name) => Reflect.get(event, name);
79
+ const type = field("type");
80
+ const sessionId = textOf(field("session_id"));
81
+ const discovered = stream.sessionId === null && sessionId !== null;
82
+ if (discovered)
83
+ stream.sessionId = sessionId;
84
+ if (type === "assistant" && field("parent_tool_use_id") == null)
85
+ stream.errorType = textOf(field("error"));
86
+ if (type === "system" || type === "result")
87
+ stream.model = textOf(field("model")) ?? stream.model;
88
+ return discovered;
89
+ }
132
90
  function receivedLines(stream, input) {
133
91
  const { lines, worker } = input;
134
92
  for (const line of lines) {
135
- const event = parsed(line);
136
- const field = (name) => Reflect.get(Object(event), name);
137
- const type = field("type");
138
- if (type === "assistant" && field("parent_tool_use_id") == null)
139
- stream.errorType = textOf(field("error"));
140
- if (type === "system" || type === "result")
141
- stream.model = textOf(field("model")) ?? stream.model;
93
+ const discovered = streamMetadata(stream, parsed(line));
142
94
  const result = streamResult(line, worker);
143
95
  if (result !== undefined) {
144
96
  const errorType = result.errorType ?? stream.errorType;
145
97
  stream.result = {
146
98
  ...result,
99
+ sessionId: result.sessionId ?? stream.sessionId,
147
100
  ...(errorType === null ? {} : { errorType }),
148
101
  };
149
102
  }
103
+ if (discovered || result !== undefined) {
104
+ saveProgress(stream, input);
105
+ }
150
106
  }
151
107
  }
108
+ function saveProgress(stream, input) {
109
+ const progress = { sessionId: stream.sessionId, result: stream.result };
110
+ stream.saved = stream.saved
111
+ .then(() => {
112
+ if (stream.failure === undefined)
113
+ return input.progress?.(progress);
114
+ return undefined;
115
+ })
116
+ .catch((error) => {
117
+ stream.failure = { error };
118
+ input.failed();
119
+ });
120
+ }
152
121
  function activityOutput(start) {
153
122
  const output = createWriteStream(join(start.activityFolder ?? start.folder, "turn.jsonl"), { flags: start.activityFolder === undefined ? "w" : "a" });
154
123
  if (start.activityFolder !== undefined)
155
124
  output.write("\n");
156
125
  return output;
157
126
  }
158
- function streamAdapter(worker) {
127
+ function streamAdapter(worker, stderr) {
128
+ if (worker === "opencode") {
129
+ const translated = opencodeStream();
130
+ stderr.setEncoding("utf8");
131
+ stderr.on("data", translated.stderr);
132
+ return translated;
133
+ }
159
134
  if (worker === "codex")
160
135
  return codexStream();
161
136
  if (worker === "cursor-agent")
@@ -164,26 +139,31 @@ function streamAdapter(worker) {
164
139
  return geminiStream();
165
140
  return undefined;
166
141
  }
167
- function turnOutput(start, child) {
168
- const output = activityOutput(start);
169
- if (start.output === "text")
170
- return textOutput(child, { output, limit: start.resultBytes ?? 48_000 });
171
- const translated = start.worker === "opencode" ? opencodeStream() : undefined;
172
- const adapter = translated ?? streamAdapter(start.worker);
173
- const { stdout, stderr } = child;
174
- if (translated !== undefined) {
175
- stderr.setEncoding("utf8");
176
- stderr.on("data", translated.stderr);
177
- }
178
- const stream = {
142
+ function streamState() {
143
+ return {
144
+ failure: undefined,
145
+ sessionId: null,
146
+ saved: Promise.resolve(),
179
147
  pending: "",
180
148
  result: undefined,
181
149
  model: null,
182
150
  errorType: null,
183
151
  };
152
+ }
153
+ function turnOutput(start, child) {
154
+ const output = activityOutput(start);
155
+ if (start.output === "text")
156
+ return textOutput(child, { output, limit: start.resultBytes ?? 48_000 });
157
+ const { stdout, stderr } = child;
158
+ const adapter = streamAdapter(start.worker, stderr);
159
+ const stream = streamState();
184
160
  const received = (lines) => {
185
161
  receivedLines(stream, {
186
162
  lines,
163
+ progress: start.progress,
164
+ failed: () => {
165
+ signalTurn({ child, start }, "SIGKILL");
166
+ },
187
167
  ...(start.worker === undefined ? {} : { worker: start.worker }),
188
168
  });
189
169
  };
@@ -212,6 +192,9 @@ function turnOutput(start, child) {
212
192
  await ended(output);
213
193
  if (adapter === undefined)
214
194
  received([stream.pending]);
195
+ await stream.saved;
196
+ if (stream.failure !== undefined)
197
+ throw stream.failure.error;
215
198
  const result = stream.result;
216
199
  return result === undefined
217
200
  ? undefined
@@ -1,6 +1,6 @@
1
1
  import { type HarnessSettings } from "../harness-defaults.ts";
2
2
  import { type TurnProfiles } from "../turn-profile.ts";
3
- import type { Delivery } from "../broker/actor/delivery-intents.ts";
3
+ import type { Delivery, DeliveryIntent } from "../broker/actor/delivery-intents.ts";
4
4
  import { type RetrySettings } from "./retrying-turn.ts";
5
5
  import { type WorkerReport } from "./operation-journal.ts";
6
6
  export interface ClaudeWorkerSettings extends RetrySettings, HarnessSettings {
@@ -15,6 +15,7 @@ export interface ClaudeWorkerSettings extends RetrySettings, HarnessSettings {
15
15
  readonly stopMilliseconds?: number;
16
16
  }
17
17
  export interface ClaudeWorker extends Delivery {
18
+ readonly recover: (intent: DeliveryIntent) => Promise<void>;
18
19
  readonly harness: (operationId: string) => Promise<string | undefined>;
19
20
  readonly stop: NonNullable<Delivery["stop"]>;
20
21
  readonly close: () => Promise<void>;
@@ -1,94 +1,111 @@
1
- import { join } from "node:path";
2
1
  import { turnHarness } from "../harness-defaults.js";
2
+ import { attachmentEnvironment } from "./attachments.js";
3
3
  import { profileCommand, } from "../turn-profile.js";
4
- import { succeeded } from "./claude-turn.js";
5
- import { turnReport } from "./turn-report.js";
4
+ import {} from "./claude-turn.js";
5
+ import { turnOutcome } from "./turn-report.js";
6
6
  import { startRetriedTurn } from "./retrying-turn.js";
7
- import { interrupted, located, operationKey, readRecord as read, replied, reportOf, writeRecord as written, } from "./operation-journal.js";
8
- import { treeFailure, workTree } from "./work-tree.js";
7
+ import { located, readRecord as read, replied, reportOf, writeRecord as written, } from "./operation-journal.js";
8
+ import { firstTurn, treeFailure, workTree, } from "./work-tree.js";
9
+ import { recordedHarness, interruptedReply, processAlive, uncertainProcess, resumePlan, recordedField, resumableHarness, recoverTurn, turnIntent, } from "./claude-recovery.js";
10
+ import { selectedProfile } from "./worker-profile.js";
9
11
  export const claudeLimits = {
10
12
  summaryCharacters: 16_000,
11
13
  resultBytes: 48_000,
12
- stopMilliseconds: 2_000,
14
+ stopMilliseconds: 15_000,
13
15
  };
14
16
  const worker = "claude";
15
- async function recordedHarness(journal, operationId) {
16
- const { folder } = located(journal, operationId);
17
- const started = await read(folder, "started.json").catch(() => undefined);
18
- const harness = Reflect.get(Object(started), "harness");
19
- return typeof harness === "string" ? harness : undefined;
20
- }
21
- function alive(processId) {
22
- if (typeof processId !== "number")
23
- return false;
24
- try {
25
- process.kill(processId, 0);
26
- return true;
27
- }
28
- catch (error) {
29
- return Reflect.get(Object(error), "code") === "EPERM";
30
- }
17
+ function reconciled(parts, call) {
18
+ const { intent } = call;
19
+ const { key } = located(parts.settings.journal, intent.operationId);
20
+ const pending = parts.reconciling.get(key);
21
+ if (pending !== undefined)
22
+ return pending;
23
+ const reconciliation = reconcileOperation(parts, call).finally(() => {
24
+ if (parts.reconciling.get(key) === reconciliation)
25
+ parts.reconciling.delete(key);
26
+ });
27
+ parts.reconciling.set(key, reconciliation);
28
+ return reconciliation;
31
29
  }
32
- async function reconciled(parts, intent) {
30
+ async function reconcileOperation(parts, call) {
31
+ const { intent, signal } = call;
33
32
  const { key, folder } = located(parts.settings.journal, intent.operationId);
34
33
  const result = await read(folder, "result.json");
35
- if (result !== undefined)
34
+ if (result !== undefined &&
35
+ (recordedField(result, "stoppedBy") !== "daemon-shutdown" ||
36
+ parts.stopping.signal.aborted))
36
37
  return replied({ receipt: key, worker }, result);
37
38
  if (parts.active.has(key))
38
39
  return { kind: "accepted", receipt: key, worker };
39
40
  const started = await read(folder, "started.json");
40
41
  if (started === undefined)
41
42
  return { kind: "absent" };
42
- if (alive(Reflect.get(Object(started), "processId")))
43
+ if (await uncertainProcess(started))
43
44
  return { kind: "unknown", evidence: `claude-turn:${key}` };
44
- const ending = interrupted("claude-turn-interrupted");
45
- await written(folder, { name: "result.json", value: ending });
46
- return replied({ receipt: key, worker }, ending);
45
+ return resumed(parts, { intent, key, folder, started, signal });
47
46
  }
48
- async function outcome(running, end) {
49
- const endedAt = new Date().toISOString();
50
- const { result } = end;
51
- const report = result === undefined
52
- ? {}
53
- : await turnReport({
54
- tree: running.tree,
55
- harness: running.harness,
56
- limits: claudeLimits,
57
- }, result);
58
- const { text, reason } = report;
59
- if (succeeded(end) && reason === undefined && text !== undefined)
60
- return { outcome: "completed", result: text, endedAt };
61
- if (running.stopping.requested)
62
- return { outcome: "stopped", endedAt };
63
- return {
64
- outcome: "failed",
65
- reason: end.reason ?? reason ?? "claude-turn-failed",
66
- ...(end.detail === undefined ? {} : { detail: end.detail }),
67
- ...(text === undefined ? {} : { result: text }),
68
- endedAt,
69
- };
47
+ async function resumed(parts, request) {
48
+ const { intent, started, key, folder, signal } = request;
49
+ const aborted = () => signal.aborted;
50
+ if (!turnIntent(intent))
51
+ return interruptedReply(request, "claude-turn-interrupted");
52
+ if (aborted())
53
+ return failure("claude-start-aborted");
54
+ const plan = await resumePlan(parts.settings, {
55
+ folder,
56
+ started,
57
+ intent,
58
+ signal,
59
+ });
60
+ if (aborted())
61
+ return failure("claude-start-aborted");
62
+ if (typeof plan === "string")
63
+ return interruptedReply(request, plan);
64
+ const prompt = await parts.settings.document(intent.payload);
65
+ if (aborted())
66
+ return failure("claude-start-aborted");
67
+ if (prompt === undefined)
68
+ return interruptedReply(request, "claude-turn-interrupted");
69
+ await begun(parts, {
70
+ key,
71
+ folder,
72
+ intent,
73
+ ...plan,
74
+ signal,
75
+ prompt,
76
+ });
77
+ return { kind: "accepted", receipt: key, worker };
70
78
  }
71
79
  async function concluded(parts, running) {
72
80
  const { intent, key } = running;
73
- const recorded = await outcome(running, await running.turn.ended);
81
+ const recorded = await turnOutcome({ ...running, limits: claudeLimits }, await running.turn.ended);
74
82
  await written(running.folder, { name: "result.json", value: recorded });
83
+ if (recorded.stoppedBy === "daemon-shutdown")
84
+ return;
75
85
  await parts.settings.report(reportOf({ intent, commandId: `claude-turn:${key}`, worker }, recorded));
76
86
  }
77
87
  async function begun(parts, start) {
78
88
  const { intent, key, folder, tree, prompt } = start;
79
89
  const { harness } = start;
80
90
  const stopping = { requested: false, controller: new AbortController() };
91
+ const cancel = () => {
92
+ stopping.controller.abort();
93
+ };
94
+ start.signal.addEventListener("abort", cancel, { once: true });
95
+ if (start.signal.aborted)
96
+ cancel();
81
97
  const turn = startRetriedTurn({
82
98
  ...harness,
83
99
  resultBytes: claudeLimits.resultBytes,
84
100
  command: profileCommand(harness.command, start.profile),
85
- environment: start.profile.env ?? {},
101
+ environment: await attachmentEnvironment(parts.settings, start),
86
102
  turnId: `${intent.runId}-${key}`,
87
103
  directory: tree.path,
88
104
  folder,
89
105
  prompt,
90
106
  }, {
91
107
  ...parts.settings,
108
+ ...(start.resume === undefined ? {} : { resume: start.resume }),
92
109
  stopping: stopping.controller,
93
110
  metadata: {
94
111
  harness: harness.worker,
@@ -100,6 +117,7 @@ async function begun(parts, start) {
100
117
  },
101
118
  });
102
119
  parts.active.set(key, {
120
+ worker: harness.worker,
103
121
  turn,
104
122
  stopping,
105
123
  concluded: turn
@@ -107,74 +125,13 @@ async function begun(parts, start) {
107
125
  .catch(() => undefined)
108
126
  .finally(() => parts.active.delete(key)),
109
127
  });
110
- await turn;
111
- }
112
- async function firstTurn(settings, opening) {
113
- const { tree, intent } = opening;
114
- const { target, runId } = intent;
115
- const folder = target.startsWith("branch:")
116
- ? join(settings.journal, "runs", operationKey(runId), "targets")
117
- : join(settings.journal, "targets");
118
- const name = `${operationKey(target)}.json`;
119
- const startCommit = Reflect.get(Object(await read(folder, name)), "startCommit");
120
- if (typeof startCommit === "string")
121
- return { ...tree, startCommit };
122
- await written(folder, {
123
- name,
124
- value: { target, startCommit: tree.startCommit },
128
+ await turn.finally(() => {
129
+ start.signal.removeEventListener("abort", cancel);
125
130
  });
126
- return tree;
127
- }
128
- function turnIntent(intent) {
129
- return (intent.capability === worker &&
130
- (intent.method === "run-turn" ||
131
- /^run-turn:[A-Za-z0-9_-]{1,119}$/.test(intent.method ?? "")) &&
132
- intent.target !== undefined &&
133
- intent.payload !== undefined &&
134
- intent.leaseGeneration !== undefined);
135
131
  }
136
132
  function failure(reason) {
137
133
  return { kind: "failed", reason };
138
134
  }
139
- async function runWorkflow(settings, runId) {
140
- try {
141
- return await settings.workflowId?.(runId);
142
- }
143
- catch {
144
- return undefined;
145
- }
146
- }
147
- function unsupportedProfile(profile, harness) {
148
- if (harness.output === "text" && profile.model !== undefined)
149
- return "profile-model-not-supported";
150
- if (harness.worker === "claude")
151
- return undefined;
152
- return [
153
- profile.allowedTools,
154
- profile.permissionMode,
155
- profile.maxBudgetUsd,
156
- ].some((value) => value !== undefined)
157
- ? "profile-needs-claude"
158
- : undefined;
159
- }
160
- async function selectedProfile(settings, intent) {
161
- if (intent.method === "run-turn")
162
- return {};
163
- const name = intent.method.slice("run-turn:".length);
164
- const profiles = settings.profiles ?? {};
165
- const profile = Object.hasOwn(profiles, name) ? profiles[name] : undefined;
166
- if (profile === undefined)
167
- return "unknown-profile";
168
- const unsupported = unsupportedProfile(profile, turnHarness(settings, profile));
169
- if (unsupported !== undefined)
170
- return unsupported;
171
- if (profile.workflows === undefined)
172
- return profile;
173
- const workflowId = await runWorkflow(settings, intent.runId);
174
- if (workflowId === undefined || !profile.workflows.includes(workflowId))
175
- return "profile-not-allowed";
176
- return profile;
177
- }
178
135
  async function delivered(parts, call) {
179
136
  const { intent, signal } = call;
180
137
  const { settings } = parts;
@@ -183,7 +140,7 @@ async function delivered(parts, call) {
183
140
  return failure("unsupported-effect");
184
141
  if (aborted())
185
142
  return failure("claude-start-aborted");
186
- const known = await reconciled(parts, intent);
143
+ const known = await reconciled(parts, call);
187
144
  if (known.kind !== "absent")
188
145
  return known;
189
146
  const profile = await selectedProfile(settings, intent);
@@ -213,6 +170,7 @@ async function delivered(parts, call) {
213
170
  prompt,
214
171
  profile,
215
172
  harness: turnHarness(settings, profile),
173
+ signal,
216
174
  });
217
175
  return { receipt: place.key, worker };
218
176
  }
@@ -224,11 +182,44 @@ async function halted(parts, active) {
224
182
  await active.concluded;
225
183
  }
226
184
  async function stopped(parts, intent) {
227
- const { key } = located(parts.settings.journal, intent.operationId);
185
+ const { key, folder } = located(parts.settings.journal, intent.operationId);
228
186
  const active = parts.active.get(key);
229
- if (active !== undefined)
187
+ if (active !== undefined) {
188
+ delete active.stopping.stoppedBy;
230
189
  await halted(parts, active);
231
- return reconciled(parts, intent);
190
+ }
191
+ const result = await read(folder, "result.json");
192
+ if (result !== undefined &&
193
+ recordedField(result, "stoppedBy") !== "daemon-shutdown")
194
+ return replied({ receipt: key, worker }, result);
195
+ const started = await read(folder, "started.json");
196
+ if (started === undefined)
197
+ return { kind: "absent" };
198
+ if (!resumableHarness(recordedField(started, "harness")))
199
+ return reconcileOperation(parts, { intent, signal: parts.stopping.signal });
200
+ if (processAlive(recordedField(started, "processId")))
201
+ return { kind: "unknown", evidence: `claude-turn:${key}` };
202
+ if (!turnIntent(intent))
203
+ return failure("unsupported-effect");
204
+ const recorded = {
205
+ outcome: "stopped",
206
+ endedAt: new Date().toISOString(),
207
+ };
208
+ await written(folder, { name: "result.json", value: recorded });
209
+ await parts.settings.report(reportOf({ intent, commandId: `claude-turn:${key}`, worker }, recorded));
210
+ return replied({ receipt: key, worker }, recorded);
211
+ }
212
+ function stoppingOperation(parts, intent) {
213
+ const { key } = located(parts.settings.journal, intent.operationId);
214
+ const pending = parts.reconciling.get(key);
215
+ const stop = () => stopped(parts, intent);
216
+ const stopping = pending === undefined ? stop() : pending.then(stop, stop);
217
+ const settled = stopping.finally(() => {
218
+ if (parts.reconciling.get(key) === settled)
219
+ parts.reconciling.delete(key);
220
+ });
221
+ parts.reconciling.set(key, settled);
222
+ return settled;
232
223
  }
233
224
  export function claudeWorker(settings) {
234
225
  if (settings.command.length === 0)
@@ -239,22 +230,33 @@ export function claudeWorker(settings) {
239
230
  const parts = {
240
231
  settings,
241
232
  active: new Map(),
233
+ reconciling: new Map(),
242
234
  stopping: new AbortController(),
243
235
  };
244
236
  const guarded = (intent, run) => intent.method === "run-turn:analyze-chats"
245
237
  ? Promise.resolve(failure("unsupported-effect"))
246
238
  : run();
247
239
  return {
240
+ recover: (intent) => turnIntent(intent)
241
+ ? recoverTurn({ ...settings, intent, signal: parts.stopping.signal }, () => reconciled(parts, { intent, signal: parts.stopping.signal }))
242
+ : Promise.resolve(),
248
243
  harness: (operationId) => recordedHarness(settings.journal, operationId),
249
244
  deliver: (intent, signal) => guarded(intent, () => delivered(parts, {
250
245
  intent,
251
246
  signal: AbortSignal.any([signal, parts.stopping.signal]),
252
247
  })),
253
- reconcile: (intent) => guarded(intent, () => reconciled(parts, intent)),
254
- stop: (intent) => guarded(intent, () => stopped(parts, intent)),
248
+ reconcile: (intent, signal) => guarded(intent, () => reconciled(parts, {
249
+ intent,
250
+ signal: AbortSignal.any([signal, parts.stopping.signal]),
251
+ })),
252
+ stop: (intent) => guarded(intent, () => stoppingOperation(parts, intent)),
255
253
  close: async () => {
256
254
  parts.stopping.abort();
257
- await Promise.all([...parts.active.values()].map((active) => halted(parts, active)));
255
+ await Promise.all([...parts.active.values()].map((active) => {
256
+ if (resumableHarness(active.worker) && !active.stopping.requested)
257
+ active.stopping.stoppedBy = "daemon-shutdown";
258
+ return halted(parts, active);
259
+ }));
258
260
  },
259
261
  };
260
262
  }
@@ -37,6 +37,7 @@ export interface DaemonSettings extends HarnessSettings {
37
37
  readonly scheduleClock?: () => number;
38
38
  readonly schedules?: unknown;
39
39
  readonly queueLanes?: number;
40
+ readonly queueDailySpendLimitUsd?: number;
40
41
  readonly resetQueue?: boolean;
41
42
  readonly githubCommand?: readonly string[];
42
43
  readonly requiredChecks?: readonly string[] | null;
@@ -56,3 +57,4 @@ export declare function adapters(settings: DaemonSettings, options: {
56
57
  readonly generations: ReturnType<typeof workflowGenerations>;
57
58
  }): Adapters;
58
59
  export declare function recoverCommands(commands: CommandWorker, ports: HostPorts): Promise<void>;
60
+ export declare function recoverClaude(worker: Pick<ClaudeWorker, "harness" | "recover">, ports: Pick<HostPorts, "state" | "operations">): Promise<void>;
@@ -6,11 +6,13 @@ import { claudeWorker } from "./claude-worker.js";
6
6
  import { githubWorker } from "./github-worker.js";
7
7
  import { commandWorker } from "./command-worker.js";
8
8
  import { handoffWorker } from "./handoff-worker.js";
9
+ import { resumableHarness } from "./claude-recovery.js";
9
10
  function githubOf(settings, given) {
10
11
  const { directory, repository, checkCommand } = settings;
11
12
  return githubWorker({
12
13
  document: async (digest) => (await given).document(digest),
13
14
  report: async (effect) => (await given).reportEffect(effect),
15
+ observe: async (observation) => (await given).submit({ kind: "report-observation", ...observation }),
14
16
  operationStatus: async (operationId) => (await (await given).operation(operationId))?.status,
15
17
  runStatus: async (runId) => (await (await given).state()).runs[runId]?.status,
16
18
  repository,
@@ -81,11 +83,33 @@ function handoffOf(settings, given) {
81
83
  });
82
84
  }
83
85
  export async function recoverCommands(commands, ports) {
86
+ await commands.recover(await recoveryCalls(ports, "command"));
87
+ }
88
+ export async function recoverClaude(worker, ports) {
89
+ for (const intent of await recoveryCalls(ports, "claude")) {
90
+ if (!resumableHarness(await worker.harness(intent.operationId)))
91
+ continue;
92
+ if (!(await stillRecoverable(ports, intent)))
93
+ continue;
94
+ await worker.recover(intent);
95
+ }
96
+ }
97
+ async function stillRecoverable(ports, intent) {
98
+ const operation = (await ports.operations(intent.runId)).find((current) => current.operationId === intent.operationId);
99
+ const run = (await ports.state()).runs[intent.runId];
100
+ return (run?.status === "running" &&
101
+ operation !== undefined &&
102
+ operation.leaseGeneration === intent.leaseGeneration &&
103
+ ["running", "recovering", "dispatch-pending"].includes(operation.status));
104
+ }
105
+ async function recoveryCalls(ports, capability) {
84
106
  const state = await ports.state();
85
107
  const calls = [];
86
108
  for (const run of Object.values(state.runs)) {
109
+ if (capability === "claude" && run.status !== "running")
110
+ continue;
87
111
  for (const operation of await ports.operations(run.runId)) {
88
- if (operation.capability !== "command" ||
112
+ if (operation.capability !== capability ||
89
113
  operation.leaseGeneration === null ||
90
114
  !["running", "recovering", "dispatch-pending"].includes(operation.status))
91
115
  continue;
@@ -98,5 +122,5 @@ export async function recoverCommands(commands, ports) {
98
122
  });
99
123
  }
100
124
  }
101
- await commands.recover(calls);
125
+ return calls;
102
126
  }
@@ -1,11 +1,13 @@
1
+ import type { scheduleConnection } from "./schedule-control.ts";
1
2
  import { workflowSchedules } from "./workflow-schedules.ts";
2
3
  interface LoopSettings extends ParametersOfSchedules {
4
+ readonly scheduleConnection?: ReturnType<typeof scheduleConnection>;
3
5
  readonly notifyCommand?: readonly string[];
4
6
  readonly notifyMilliseconds?: number;
5
7
  readonly queueMilliseconds?: number;
6
8
  readonly queueLanes?: number;
7
9
  readonly resetQueue?: boolean;
8
- readonly queueWorkflowId: string;
10
+ readonly queueWorkflowId: string | undefined;
9
11
  }
10
12
  type ParametersOfSchedules = Parameters<typeof workflowSchedules>[0];
11
13
  export declare function daemonLoops(settings: LoopSettings): Promise<{