cloving 1.1.0 → 1.3.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 (568) hide show
  1. package/README.md +584 -144
  2. package/assets/watch-fonts/fira-code-OFL.md +93 -0
  3. package/assets/watch-fonts/fira-code-latin-400-normal.woff2 +0 -0
  4. package/assets/watch-fonts/fira-code-latin-500-normal.woff2 +0 -0
  5. package/assets/watch-fonts/heebo-OFL.md +93 -0
  6. package/assets/watch-fonts/heebo-latin-500-normal.woff2 +0 -0
  7. package/assets/watch-fonts/heebo-latin-700-normal.woff2 +0 -0
  8. package/assets/watch-fonts/heebo-latin-800-normal.woff2 +0 -0
  9. package/assets/watch-fonts/manifest.json +112 -0
  10. package/assets/watch-fonts/space-grotesk-OFL.md +93 -0
  11. package/assets/watch-fonts/space-grotesk-latin-400-normal.woff2 +0 -0
  12. package/assets/watch-fonts/space-grotesk-latin-500-normal.woff2 +0 -0
  13. package/assets/watch-fonts/space-grotesk-latin-600-normal.woff2 +0 -0
  14. package/assets/watch-fonts/space-grotesk-latin-700-normal.woff2 +0 -0
  15. package/dist/src/agent-harness-shell.d.ts +12 -0
  16. package/dist/src/agent-harness-shell.js +90 -0
  17. package/dist/src/agent-harness-versions.d.ts +8 -0
  18. package/dist/src/agent-harness-versions.js +49 -0
  19. package/dist/src/agent-harnesses.d.ts +4 -0
  20. package/dist/src/agent-harnesses.js +13 -0
  21. package/dist/src/authority/commands.d.ts +2 -0
  22. package/dist/src/authority/commands.js +49 -8
  23. package/dist/src/authority/dispatch-decision.d.ts +1 -0
  24. package/dist/src/authority/dispatch-decision.js +7 -4
  25. package/dist/src/authority/events.js +2 -2
  26. package/dist/src/authority/gate-reduction.d.ts +1 -0
  27. package/dist/src/authority/gate-reduction.js +18 -3
  28. package/dist/src/authority/gates.d.ts +2 -1
  29. package/dist/src/authority/gates.js +5 -1
  30. package/dist/src/authority/handoff-chain.d.ts +24 -0
  31. package/dist/src/authority/handoff-chain.js +46 -0
  32. package/dist/src/authority/operations.js +1 -1
  33. package/dist/src/authority/reconciliation.d.ts +1 -0
  34. package/dist/src/authority/reconciliation.js +18 -11
  35. package/dist/src/authority/reduction.d.ts +1 -1
  36. package/dist/src/authority/reduction.js +6 -1
  37. package/dist/src/authority/status-input.d.ts +1 -1
  38. package/dist/src/authority/status.d.ts +1 -0
  39. package/dist/src/authority/status.js +27 -3
  40. package/dist/src/authority/transitions.d.ts +5 -5
  41. package/dist/src/authority/transitions.js +9 -0
  42. package/dist/src/broker/actor/authority-delivery.js +1 -0
  43. package/dist/src/broker/actor/authority-queries.d.ts +11 -1
  44. package/dist/src/broker/actor/authority-queries.js +67 -3
  45. package/dist/src/broker/actor/authority-receipts.js +11 -1
  46. package/dist/src/broker/actor/authority-session.js +2 -2
  47. package/dist/src/broker/actor/authority-settings.js +4 -0
  48. package/dist/src/broker/actor/delivery-intents.d.ts +3 -1
  49. package/dist/src/broker/actor/delivery-intents.js +4 -4
  50. package/dist/src/broker/actor/effect-delivery.js +13 -1
  51. package/dist/src/broker/actor/effect-queries.d.ts +1 -0
  52. package/dist/src/broker/actor/effect-queries.js +7 -0
  53. package/dist/src/broker/actor/pin-check.js +1 -1
  54. package/dist/src/broker/actor/stop-plan.js +1 -1
  55. package/dist/src/broker/actor/turn-state.js +1 -1
  56. package/dist/src/broker/authority-admission.js +26 -5
  57. package/dist/src/broker/authority-restore.js +1 -0
  58. package/dist/src/broker/decision-restore.js +4 -4
  59. package/dist/src/broker/socket-address.d.ts +3 -1
  60. package/dist/src/broker/socket-address.js +2 -2
  61. package/dist/src/cli/arguments.d.ts +11 -13
  62. package/dist/src/cli/arguments.js +37 -107
  63. package/dist/src/cli/command-effects.d.ts +7 -0
  64. package/dist/src/cli/command-effects.js +39 -0
  65. package/dist/src/cli/commands.js +11 -4
  66. package/dist/src/cli/config-arguments.d.ts +11 -0
  67. package/dist/src/cli/config-arguments.js +59 -0
  68. package/dist/src/cli/config-command.d.ts +2 -0
  69. package/dist/src/cli/config-command.js +105 -0
  70. package/dist/src/cli/config-file.d.ts +3 -0
  71. package/dist/src/cli/config-file.js +50 -0
  72. package/dist/src/cli/config-lock.d.ts +7 -0
  73. package/dist/src/cli/config-lock.js +61 -0
  74. package/dist/src/cli/config-notice.d.ts +5 -0
  75. package/dist/src/cli/config-notice.js +46 -0
  76. package/dist/src/cli/doctor-github.d.ts +9 -0
  77. package/dist/src/cli/doctor-github.js +138 -0
  78. package/dist/src/cli/doctor-harness.d.ts +13 -0
  79. package/dist/src/cli/doctor-harness.js +242 -0
  80. package/dist/src/cli/doctor-local.d.ts +18 -0
  81. package/dist/src/cli/doctor-local.js +77 -0
  82. package/dist/src/cli/doctor-runtime.d.ts +15 -0
  83. package/dist/src/cli/doctor-runtime.js +176 -0
  84. package/dist/src/cli/doctor-service.d.ts +1 -0
  85. package/dist/src/cli/doctor-service.js +11 -0
  86. package/dist/src/cli/doctor-workflows.d.ts +3 -0
  87. package/dist/src/cli/doctor-workflows.js +45 -0
  88. package/dist/src/cli/doctor.d.ts +15 -0
  89. package/dist/src/cli/doctor.js +190 -0
  90. package/dist/src/cli/github-ci-progress.d.ts +8 -0
  91. package/dist/src/cli/github-ci-progress.js +83 -0
  92. package/dist/src/cli/github-retry.d.ts +6 -0
  93. package/dist/src/cli/github-retry.js +47 -0
  94. package/dist/src/cli/harness-probes.d.ts +22 -0
  95. package/dist/src/cli/harness-probes.js +183 -0
  96. package/dist/src/cli/main.d.ts +7 -1
  97. package/dist/src/cli/main.js +39 -61
  98. package/dist/src/cli/phone-arguments.d.ts +14 -0
  99. package/dist/src/cli/phone-arguments.js +37 -0
  100. package/dist/src/cli/phone-client.d.ts +2 -0
  101. package/dist/src/cli/phone-client.js +32 -0
  102. package/dist/src/cli/phone-command.d.ts +13 -0
  103. package/dist/src/cli/phone-command.js +101 -0
  104. package/dist/src/cli/probe-command.d.ts +36 -0
  105. package/dist/src/cli/probe-command.js +59 -0
  106. package/dist/src/cli/project-settings.d.ts +4 -0
  107. package/dist/src/cli/project-settings.js +33 -0
  108. package/dist/src/cli/render.d.ts +3 -1
  109. package/dist/src/cli/render.js +102 -39
  110. package/dist/src/cli/report-github.d.ts +18 -0
  111. package/dist/src/cli/report-github.js +200 -0
  112. package/dist/src/cli/report.d.ts +17 -0
  113. package/dist/src/cli/report.js +192 -0
  114. package/dist/src/cli/repository-arguments.d.ts +5 -0
  115. package/dist/src/cli/repository-arguments.js +34 -0
  116. package/dist/src/cli/runtime-arguments.d.ts +4 -0
  117. package/dist/src/cli/runtime-arguments.js +15 -0
  118. package/dist/src/cli/runtime-directory.d.ts +6 -2
  119. package/dist/src/cli/runtime-directory.js +72 -14
  120. package/dist/src/cli/toolchain.d.ts +2 -0
  121. package/dist/src/cli/toolchain.js +22 -2
  122. package/dist/src/cli/usage-render.d.ts +5 -0
  123. package/dist/src/cli/usage-render.js +40 -1
  124. package/dist/src/cli/usage.d.ts +3 -3
  125. package/dist/src/cli/usage.js +74 -45
  126. package/dist/src/cli/watch-actions.d.ts +2 -0
  127. package/dist/src/cli/watch-actions.js +4 -4
  128. package/dist/src/cli/watch-board-view.d.ts +12 -1
  129. package/dist/src/cli/watch-board-view.js +64 -12
  130. package/dist/src/cli/watch-board.d.ts +7 -1
  131. package/dist/src/cli/watch-board.js +61 -16
  132. package/dist/src/cli/watch-card-forms.js +2 -2
  133. package/dist/src/cli/watch-client.d.ts +1 -1
  134. package/dist/src/cli/watch-client.js +12 -3
  135. package/dist/src/cli/watch-header-view.d.ts +27 -0
  136. package/dist/src/cli/watch-header-view.js +116 -0
  137. package/dist/src/cli/watch-model.d.ts +10 -4
  138. package/dist/src/cli/watch-model.js +49 -59
  139. package/dist/src/cli/watch-page.js +67 -14
  140. package/dist/src/cli/watch-phases.js +3 -3
  141. package/dist/src/cli/watch-phone-client.d.ts +1 -0
  142. package/dist/src/cli/watch-phone-client.js +84 -0
  143. package/dist/src/cli/watch-phone-view.d.ts +56 -0
  144. package/dist/src/cli/watch-phone-view.js +113 -0
  145. package/dist/src/cli/watch-phone.d.ts +5 -0
  146. package/dist/src/cli/watch-phone.js +53 -0
  147. package/dist/src/cli/watch-pr-command.d.ts +5 -2
  148. package/dist/src/cli/watch-pr-command.js +41 -4
  149. package/dist/src/cli/watch-pr-identity.d.ts +14 -2
  150. package/dist/src/cli/watch-pr-identity.js +25 -0
  151. package/dist/src/cli/watch-pr-observer.d.ts +1 -0
  152. package/dist/src/cli/watch-pr-observer.js +48 -6
  153. package/dist/src/cli/watch-pull-request.d.ts +12 -0
  154. package/dist/src/cli/watch-pull-request.js +134 -0
  155. package/dist/src/cli/watch-reads.d.ts +7 -1
  156. package/dist/src/cli/watch-reads.js +42 -20
  157. package/dist/src/cli/watch-render.d.ts +23 -2
  158. package/dist/src/cli/watch-render.js +2 -1
  159. package/dist/src/cli/watch-server.js +56 -7
  160. package/dist/src/cli/watch-turn.d.ts +4 -1
  161. package/dist/src/cli/watch-turn.js +38 -2
  162. package/dist/src/cli/watch-usage.js +5 -1
  163. package/dist/src/cli/watch-view.d.ts +10 -17
  164. package/dist/src/cli/watch-view.js +33 -77
  165. package/dist/src/cli.js +106 -23
  166. package/dist/src/command-settings.d.ts +16 -0
  167. package/dist/src/command-settings.js +42 -0
  168. package/dist/src/configuration-change.d.ts +11 -0
  169. package/dist/src/configuration-change.js +62 -0
  170. package/dist/src/configuration-display.d.ts +21 -0
  171. package/dist/src/configuration-display.js +69 -0
  172. package/dist/src/configuration-schema.d.ts +49 -0
  173. package/dist/src/configuration-schema.js +159 -0
  174. package/dist/src/daemon/claude-turn.d.ts +20 -5
  175. package/dist/src/daemon/claude-turn.js +116 -53
  176. package/dist/src/daemon/claude-worker.d.ts +3 -5
  177. package/dist/src/daemon/claude-worker.js +64 -80
  178. package/dist/src/daemon/command-journal.d.ts +5 -0
  179. package/dist/src/daemon/command-journal.js +50 -0
  180. package/dist/src/daemon/command-process.d.ts +28 -0
  181. package/dist/src/daemon/command-process.js +164 -0
  182. package/dist/src/daemon/command-worker.d.ts +19 -0
  183. package/dist/src/daemon/command-worker.js +182 -0
  184. package/dist/src/daemon/cursor-stream.d.ts +4 -0
  185. package/dist/src/daemon/cursor-stream.js +154 -0
  186. package/dist/src/daemon/daemon-adapters.d.ts +58 -0
  187. package/dist/src/daemon/daemon-adapters.js +102 -0
  188. package/dist/src/daemon/daemon-loops.d.ts +14 -0
  189. package/dist/src/daemon/daemon-loops.js +39 -0
  190. package/dist/src/daemon/daemon.d.ts +3 -30
  191. package/dist/src/daemon/daemon.js +132 -110
  192. package/dist/src/daemon/declared-input.d.ts +3 -0
  193. package/dist/src/daemon/declared-input.js +9 -0
  194. package/dist/src/daemon/delivery-router.js +7 -1
  195. package/dist/src/daemon/gemini-stream.d.ts +4 -0
  196. package/dist/src/daemon/gemini-stream.js +129 -0
  197. package/dist/src/daemon/github-calls.d.ts +4 -0
  198. package/dist/src/daemon/github-calls.js +16 -7
  199. package/dist/src/daemon/github-ci-rerun.d.ts +26 -0
  200. package/dist/src/daemon/github-ci-rerun.js +92 -0
  201. package/dist/src/daemon/github-ci.d.ts +6 -0
  202. package/dist/src/daemon/github-ci.js +251 -0
  203. package/dist/src/daemon/github-merge-call.d.ts +16 -0
  204. package/dist/src/daemon/github-merge-call.js +73 -0
  205. package/dist/src/daemon/github-merge.d.ts +3 -5
  206. package/dist/src/daemon/github-merge.js +59 -98
  207. package/dist/src/daemon/github-methods.d.ts +28 -0
  208. package/dist/src/daemon/github-methods.js +90 -16
  209. package/dist/src/daemon/github-operation.d.ts +3 -0
  210. package/dist/src/daemon/github-retry.d.ts +6 -0
  211. package/dist/src/daemon/github-retry.js +13 -0
  212. package/dist/src/daemon/github-ticket-status.js +27 -1
  213. package/dist/src/daemon/github-worker.js +2 -1
  214. package/dist/src/daemon/handoff-worker.d.ts +10 -0
  215. package/dist/src/daemon/handoff-worker.js +232 -0
  216. package/dist/src/daemon/issue-queue.d.ts +2 -0
  217. package/dist/src/daemon/issue-queue.js +92 -63
  218. package/dist/src/daemon/notify-journal.d.ts +18 -0
  219. package/dist/src/daemon/notify-journal.js +108 -0
  220. package/dist/src/daemon/notify-message.js +2 -1
  221. package/dist/src/daemon/notify.js +177 -37
  222. package/dist/src/daemon/opencode-stream.d.ts +5 -0
  223. package/dist/src/daemon/opencode-stream.js +87 -0
  224. package/dist/src/daemon/queue-status.d.ts +4 -0
  225. package/dist/src/daemon/queue-status.js +38 -0
  226. package/dist/src/daemon/retry-worktree.d.ts +18 -0
  227. package/dist/src/daemon/retry-worktree.js +139 -0
  228. package/dist/src/daemon/retrying-turn.d.ts +5 -0
  229. package/dist/src/daemon/retrying-turn.js +145 -21
  230. package/dist/src/daemon/schedule-journal.d.ts +19 -0
  231. package/dist/src/daemon/schedule-journal.js +57 -0
  232. package/dist/src/daemon/text-output.d.ts +18 -0
  233. package/dist/src/daemon/text-output.js +44 -0
  234. package/dist/src/daemon/ticket-dependency-waits.d.ts +18 -0
  235. package/dist/src/daemon/ticket-dependency-waits.js +60 -0
  236. package/dist/src/daemon/ticket-registration.d.ts +10 -8
  237. package/dist/src/daemon/ticket-registration.js +146 -47
  238. package/dist/src/daemon/ticket-source.d.ts +4 -1
  239. package/dist/src/daemon/ticket-source.js +8 -2
  240. package/dist/src/daemon/turn-command.d.ts +10 -0
  241. package/dist/src/daemon/turn-command.js +33 -0
  242. package/dist/src/daemon/turn-report.d.ts +18 -0
  243. package/dist/src/daemon/turn-report.js +79 -0
  244. package/dist/src/daemon/work-tree.d.ts +2 -1
  245. package/dist/src/daemon/work-tree.js +15 -12
  246. package/dist/src/daemon/workflow-generations.d.ts +32 -0
  247. package/dist/src/daemon/workflow-generations.js +57 -0
  248. package/dist/src/daemon/workflow-replay.d.ts +16 -0
  249. package/dist/src/daemon/workflow-replay.js +58 -0
  250. package/dist/src/daemon/workflow-schedules.d.ts +27 -0
  251. package/dist/src/daemon/workflow-schedules.js +185 -0
  252. package/dist/src/decision-visibility.d.ts +2 -0
  253. package/dist/src/decision-visibility.js +10 -0
  254. package/dist/src/decision-wait.d.ts +1 -0
  255. package/dist/src/decision-wait.js +3 -0
  256. package/dist/src/delivery-backoff.d.ts +1 -0
  257. package/dist/src/delivery-backoff.js +3 -0
  258. package/dist/src/doctor-report.d.ts +24 -0
  259. package/dist/src/doctor-report.js +70 -0
  260. package/dist/src/execution-pin.d.ts +9 -0
  261. package/dist/src/execution-pin.js +86 -2
  262. package/dist/src/grok-result.d.ts +12 -0
  263. package/dist/src/grok-result.js +61 -0
  264. package/dist/src/harness-defaults.d.ts +35 -0
  265. package/dist/src/harness-defaults.js +121 -0
  266. package/dist/src/host/decision-resume.d.ts +35 -0
  267. package/dist/src/host/decision-resume.js +86 -0
  268. package/dist/src/host/gate-bridge.d.ts +48 -0
  269. package/dist/src/host/gate-bridge.js +124 -0
  270. package/dist/src/host/run-wait.d.ts +26 -0
  271. package/dist/src/host/run-wait.js +37 -0
  272. package/dist/src/host/start-delivery.d.ts +3 -2
  273. package/dist/src/host/start-delivery.js +11 -4
  274. package/dist/src/host/step-bridge-host.d.ts +6 -19
  275. package/dist/src/host/step-bridge-host.js +57 -55
  276. package/dist/src/host/world-reconciliation.d.ts +1 -0
  277. package/dist/src/host/world-reconciliation.js +3 -0
  278. package/dist/src/host/world-runs.js +5 -0
  279. package/dist/src/issue-identity.d.ts +6 -0
  280. package/dist/src/issue-identity.js +23 -0
  281. package/dist/src/merge-request.d.ts +19 -0
  282. package/dist/src/merge-request.js +32 -0
  283. package/dist/src/phone/canonical-json.d.ts +1 -0
  284. package/dist/src/phone/canonical-json.js +16 -0
  285. package/dist/src/phone/phone-link.d.ts +7 -0
  286. package/dist/src/phone/phone-link.js +21 -0
  287. package/dist/src/phone/qr-data.d.ts +8 -0
  288. package/dist/src/phone/qr-data.js +133 -0
  289. package/dist/src/phone/qr-matrix.d.ts +6 -0
  290. package/dist/src/phone/qr-matrix.js +175 -0
  291. package/dist/src/phone/qr-penalty.d.ts +3 -0
  292. package/dist/src/phone/qr-penalty.js +67 -0
  293. package/dist/src/phone/qr-picture.d.ts +5 -0
  294. package/dist/src/phone/qr-picture.js +24 -0
  295. package/dist/src/private-config.schema.json +263 -0
  296. package/dist/src/project-config.schema.json +65 -0
  297. package/dist/src/project-settings.d.ts +19 -0
  298. package/dist/src/project-settings.js +131 -0
  299. package/dist/src/relay/answer-check.d.ts +24 -0
  300. package/dist/src/relay/answer-check.js +145 -0
  301. package/dist/src/relay/answer-inbox.d.ts +15 -0
  302. package/dist/src/relay/answer-inbox.js +173 -0
  303. package/dist/src/relay/daemon-keys.d.ts +16 -0
  304. package/dist/src/relay/daemon-keys.js +75 -0
  305. package/dist/src/relay/decision-check.d.ts +3 -0
  306. package/dist/src/relay/decision-check.js +77 -0
  307. package/dist/src/relay/decision-close.d.ts +6 -0
  308. package/dist/src/relay/decision-close.js +72 -0
  309. package/dist/src/relay/decision-envelopes.d.ts +29 -0
  310. package/dist/src/relay/decision-envelopes.js +93 -0
  311. package/dist/src/relay/decision-watch.d.ts +14 -0
  312. package/dist/src/relay/decision-watch.js +68 -0
  313. package/dist/src/relay/envelope-post.d.ts +8 -0
  314. package/dist/src/relay/envelope-post.js +71 -0
  315. package/dist/src/relay/envelope.d.ts +28 -0
  316. package/dist/src/relay/envelope.js +53 -0
  317. package/dist/src/relay/hpke.d.ts +25 -0
  318. package/dist/src/relay/hpke.js +108 -0
  319. package/dist/src/relay/pairing-checks.d.ts +42 -0
  320. package/dist/src/relay/pairing-checks.js +172 -0
  321. package/dist/src/relay/pairing-follow.d.ts +8 -0
  322. package/dist/src/relay/pairing-follow.js +95 -0
  323. package/dist/src/relay/pairing-session.d.ts +29 -0
  324. package/dist/src/relay/pairing-session.js +87 -0
  325. package/dist/src/relay/phone-delivery.d.ts +51 -0
  326. package/dist/src/relay/phone-delivery.js +139 -0
  327. package/dist/src/relay/phone-devices.d.ts +36 -0
  328. package/dist/src/relay/phone-devices.js +46 -0
  329. package/dist/src/relay/phone-inbox.d.ts +18 -0
  330. package/dist/src/relay/phone-inbox.js +17 -0
  331. package/dist/src/relay/phone-operations.d.ts +18 -0
  332. package/dist/src/relay/phone-operations.js +121 -0
  333. package/dist/src/relay/phone-outbox.d.ts +21 -0
  334. package/dist/src/relay/phone-outbox.js +25 -0
  335. package/dist/src/relay/phone-socket.d.ts +6 -0
  336. package/dist/src/relay/phone-socket.js +63 -0
  337. package/dist/src/relay/relay-control.d.ts +7 -0
  338. package/dist/src/relay/relay-control.js +24 -0
  339. package/dist/src/relay/relay-files.d.ts +2 -0
  340. package/dist/src/relay/relay-files.js +23 -0
  341. package/dist/src/relay/relay-heartbeat.d.ts +17 -0
  342. package/dist/src/relay/relay-heartbeat.js +81 -0
  343. package/dist/src/relay/relay-http.d.ts +14 -0
  344. package/dist/src/relay/relay-http.js +63 -0
  345. package/dist/src/relay/relay-registration.d.ts +15 -0
  346. package/dist/src/relay/relay-registration.js +63 -0
  347. package/dist/src/relay/relay-service.d.ts +25 -0
  348. package/dist/src/relay/relay-service.js +216 -0
  349. package/dist/src/relay/relay-store.d.ts +48 -0
  350. package/dist/src/relay/relay-store.js +131 -0
  351. package/dist/src/relay/sealed-post.d.ts +25 -0
  352. package/dist/src/relay/sealed-post.js +52 -0
  353. package/dist/src/relay.d.ts +1 -0
  354. package/dist/src/relay.js +8 -0
  355. package/dist/src/report-statistics.d.ts +50 -0
  356. package/dist/src/report-statistics.js +70 -0
  357. package/dist/src/report.d.ts +171 -0
  358. package/dist/src/report.js +119 -0
  359. package/dist/src/repository-identity.d.ts +4 -0
  360. package/dist/src/repository-identity.js +4 -0
  361. package/dist/src/review-subject.d.ts +41 -0
  362. package/dist/src/review-subject.js +91 -0
  363. package/dist/src/run-wait.d.ts +23 -0
  364. package/dist/src/run-wait.js +20 -0
  365. package/dist/src/service/active-tickets.d.ts +4 -0
  366. package/dist/src/service/active-tickets.js +49 -0
  367. package/dist/src/service/command-effects.d.ts +4 -0
  368. package/dist/src/service/command-effects.js +16 -0
  369. package/dist/src/service/decision-handler.d.ts +4 -0
  370. package/dist/src/service/decision-handler.js +39 -4
  371. package/dist/src/service/gateway.js +18 -3
  372. package/dist/src/service/queue-runs.d.ts +3 -0
  373. package/dist/src/service/queue-runs.js +39 -0
  374. package/dist/src/service/registry.d.ts +22 -3
  375. package/dist/src/service/registry.js +51 -8
  376. package/dist/src/service/replies.d.ts +10 -0
  377. package/dist/src/service/replies.js +12 -0
  378. package/dist/src/service/run-handlers.js +45 -54
  379. package/dist/src/service/run-metadata.d.ts +8 -0
  380. package/dist/src/service/run-metadata.js +53 -0
  381. package/dist/src/service/run-waits.d.ts +3 -0
  382. package/dist/src/service/run-waits.js +41 -0
  383. package/dist/src/service/service.d.ts +21 -2
  384. package/dist/src/service/service.js +23 -5
  385. package/dist/src/service/submission-handler.js +70 -19
  386. package/dist/src/service/worker-port.d.ts +1 -0
  387. package/dist/src/service/worker-totals.js +10 -1
  388. package/dist/src/step-bridge.d.ts +32 -0
  389. package/dist/src/steps/decision-gate.d.ts +2 -1
  390. package/dist/src/steps/decision-gate.js +7 -0
  391. package/dist/src/steps/handoff.d.ts +2 -0
  392. package/dist/src/steps/handoff.js +13 -0
  393. package/dist/src/steps/merge-by-person.d.ts +2 -0
  394. package/dist/src/steps/merge-by-person.js +18 -0
  395. package/dist/src/steps/merge-when-green.d.ts +2 -0
  396. package/dist/src/steps/merge-when-green.js +62 -0
  397. package/dist/src/steps/review-by-agent.d.ts +5 -0
  398. package/dist/src/steps/review-by-agent.js +124 -0
  399. package/dist/src/steps/review-by-person.d.ts +4 -0
  400. package/dist/src/steps/review-by-person.js +16 -0
  401. package/dist/src/steps/review-subject.d.ts +2 -0
  402. package/dist/src/steps/review-subject.js +11 -0
  403. package/dist/src/steps/run-command.d.ts +2 -0
  404. package/dist/src/steps/run-command.js +15 -0
  405. package/dist/src/steps/sleep.d.ts +3 -0
  406. package/dist/src/steps/sleep.js +19 -0
  407. package/dist/src/steps/wait-for-decision.d.ts +2 -0
  408. package/dist/src/steps/wait-for-decision.js +71 -0
  409. package/dist/src/steps/wait-for-run.d.ts +5 -0
  410. package/dist/src/steps/wait-for-run.js +9 -0
  411. package/dist/src/storage/decision-snapshots.d.ts +1 -0
  412. package/dist/src/storage/decision-snapshots.js +7 -1
  413. package/dist/src/submission.d.ts +4 -0
  414. package/dist/src/submission.js +6 -2
  415. package/dist/src/testing.d.ts +29 -0
  416. package/dist/src/testing.js +264 -0
  417. package/dist/src/ticket-dependencies.d.ts +1 -0
  418. package/dist/src/ticket-dependencies.js +15 -0
  419. package/dist/src/ticket-status-stream.d.ts +9 -0
  420. package/dist/src/ticket-status-stream.js +94 -0
  421. package/dist/src/ticket-status.js +7 -4
  422. package/dist/src/token-prices.d.ts +14 -4
  423. package/dist/src/token-prices.js +56 -11
  424. package/dist/src/turn-profile.d.ts +1 -0
  425. package/dist/src/turn-profile.js +8 -1
  426. package/dist/src/worker-usage.d.ts +25 -0
  427. package/dist/src/worker-usage.js +58 -1
  428. package/dist/src/workflow-admission.d.ts +6 -0
  429. package/dist/src/workflow-candidate.d.ts +7 -2
  430. package/dist/src/workflow-candidate.js +48 -5
  431. package/dist/src/workflow-contracts.d.ts +2 -0
  432. package/dist/src/workflow-contracts.js +8 -3
  433. package/dist/src/workflow-declaration.d.ts +31 -2
  434. package/dist/src/workflow-declaration.js +140 -16
  435. package/dist/src/workflow-discovery.d.ts +19 -0
  436. package/dist/src/workflow-discovery.js +36 -0
  437. package/dist/src/workflow-refusals.d.ts +6 -0
  438. package/dist/src/workflow-refusals.js +42 -0
  439. package/dist/src/workflow-schedule.d.ts +22 -0
  440. package/dist/src/workflow-schedule.js +53 -0
  441. package/dist/src/workflows.d.ts +12 -1
  442. package/dist/src/workflows.js +9 -0
  443. package/dist/tools/admit-workflow.d.ts +14 -0
  444. package/dist/tools/admit-workflow.js +107 -0
  445. package/dist/tools/doctor-adoption.d.ts +1 -0
  446. package/dist/tools/doctor-adoption.js +11 -0
  447. package/dist/tools/project-configuration.d.ts +4 -0
  448. package/dist/tools/project-configuration.js +47 -0
  449. package/dist/tools/self-host-processes.d.ts +1 -0
  450. package/dist/tools/self-host-processes.js +33 -22
  451. package/dist/tools/self-host-project.d.ts +21 -0
  452. package/dist/tools/self-host-project.js +224 -0
  453. package/dist/tools/self-host-runtime.d.ts +16 -0
  454. package/dist/tools/self-host-runtime.js +60 -0
  455. package/dist/tools/self-host-workflows.d.ts +1 -0
  456. package/dist/tools/self-host-workflows.js +36 -1
  457. package/dist/tools/self-host.js +106 -135
  458. package/dist/tools/workflow-bundle-dependencies.d.ts +5 -0
  459. package/dist/tools/workflow-bundle-dependencies.js +93 -0
  460. package/dist/tools/workflow-bundles.d.ts +20 -0
  461. package/dist/tools/workflow-bundles.js +173 -0
  462. package/dist/tools/workflow-candidate-counts.d.ts +12 -0
  463. package/dist/tools/workflow-candidate-counts.js +266 -0
  464. package/dist/tools/workflow-candidate-fields.d.ts +5 -0
  465. package/dist/tools/workflow-candidate-fields.js +24 -0
  466. package/dist/tools/workflow-candidate-helper-counts.d.ts +10 -0
  467. package/dist/tools/workflow-candidate-helper-counts.js +129 -0
  468. package/dist/tools/workflow-candidate-program.d.ts +13 -0
  469. package/dist/tools/workflow-candidate-program.js +106 -0
  470. package/dist/tools/workflow-candidate-source.d.ts +6 -0
  471. package/dist/tools/workflow-candidate-source.js +169 -0
  472. package/dist/tools/workflow-candidate-syntax.d.ts +21 -0
  473. package/dist/tools/workflow-candidate-syntax.js +263 -0
  474. package/dist/tools/workflow-command.d.ts +1 -0
  475. package/dist/tools/workflow-command.js +143 -0
  476. package/dist/tools/workflow-folder-capture.d.ts +18 -0
  477. package/dist/tools/workflow-folder-capture.js +261 -0
  478. package/dist/tools/workflow-folders.d.ts +49 -0
  479. package/dist/tools/workflow-folders.js +203 -0
  480. package/dist/tools/workflow-source-effects.d.ts +10 -0
  481. package/dist/tools/workflow-source-effects.js +213 -0
  482. package/dist/tools/workflow-test-hook.d.ts +7 -0
  483. package/dist/tools/workflow-test-hook.js +16 -0
  484. package/dist/tools/workflow-test-loader.d.ts +1 -0
  485. package/dist/tools/workflow-test-loader.js +10 -0
  486. package/dist/workflows/develop-prd.js +71 -3
  487. package/dist/workflows/develop-ticket.js +6 -1
  488. package/dist/workflows/ticket-prompts.js +17 -3
  489. package/package.json +66 -6
  490. package/templates/develop/README.md +78 -0
  491. package/templates/develop/develop/accept.ts +11 -0
  492. package/templates/develop/develop/flow-tests.ts +357 -0
  493. package/templates/develop/develop/local.ts +40 -0
  494. package/templates/develop/develop/option-tests.ts +174 -0
  495. package/templates/develop/develop/partial-choice.ts +29 -0
  496. package/templates/develop/develop/partial.ts +28 -0
  497. package/templates/develop/develop/pull.ts +152 -0
  498. package/templates/develop/develop/queue-partial.ts +39 -0
  499. package/templates/develop/develop/refusal-tests.ts +174 -0
  500. package/templates/develop/develop/report.ts +156 -0
  501. package/templates/develop/develop/round.ts +40 -0
  502. package/templates/develop/develop/scenarios.ts +87 -0
  503. package/templates/develop/develop/turn.ts +67 -0
  504. package/templates/develop/develop.declaration.json +24 -0
  505. package/templates/develop/develop.test.ts +287 -0
  506. package/templates/develop/develop.ts +30 -0
  507. package/templates/develop/template.json +153 -0
  508. package/dist/src/chats/analysis-batches.d.ts +0 -24
  509. package/dist/src/chats/analysis-batches.js +0 -83
  510. package/dist/src/chats/analysis-contract.d.ts +0 -80
  511. package/dist/src/chats/analysis-contract.js +0 -16
  512. package/dist/src/chats/analysis-data.d.ts +0 -3
  513. package/dist/src/chats/analysis-data.js +0 -85
  514. package/dist/src/chats/analysis-input.d.ts +0 -12
  515. package/dist/src/chats/analysis-input.js +0 -83
  516. package/dist/src/chats/analysis-links.d.ts +0 -2
  517. package/dist/src/chats/analysis-links.js +0 -25
  518. package/dist/src/chats/analysis-prompt.d.ts +0 -10
  519. package/dist/src/chats/analysis-prompt.js +0 -16
  520. package/dist/src/chats/analysis-proposal.d.ts +0 -2
  521. package/dist/src/chats/analysis-proposal.js +0 -32
  522. package/dist/src/chats/analysis-provenance.d.ts +0 -4
  523. package/dist/src/chats/analysis-provenance.js +0 -99
  524. package/dist/src/chats/analysis-shape.d.ts +0 -3
  525. package/dist/src/chats/analysis-shape.js +0 -228
  526. package/dist/src/chats/analysis.d.ts +0 -3
  527. package/dist/src/chats/analysis.js +0 -42
  528. package/dist/src/chats/corpus-input.d.ts +0 -5
  529. package/dist/src/chats/corpus-input.js +0 -288
  530. package/dist/src/chats/destination.d.ts +0 -4
  531. package/dist/src/chats/destination.js +0 -96
  532. package/dist/src/chats/import.d.ts +0 -15
  533. package/dist/src/chats/import.js +0 -243
  534. package/dist/src/chats/input-file.d.ts +0 -3
  535. package/dist/src/chats/input-file.js +0 -32
  536. package/dist/src/chats/normalize.d.ts +0 -29
  537. package/dist/src/chats/normalize.js +0 -155
  538. package/dist/src/chats/previous-import.d.ts +0 -5
  539. package/dist/src/chats/previous-import.js +0 -34
  540. package/dist/src/chats/reader.d.ts +0 -21
  541. package/dist/src/chats/reader.js +0 -118
  542. package/dist/src/chats/redact-import.d.ts +0 -14
  543. package/dist/src/chats/redact-import.js +0 -141
  544. package/dist/src/chats/redaction.d.ts +0 -41
  545. package/dist/src/chats/redaction.js +0 -161
  546. package/dist/src/chats/revision-input.d.ts +0 -3
  547. package/dist/src/chats/revision-input.js +0 -180
  548. package/dist/src/chats/revision.d.ts +0 -48
  549. package/dist/src/chats/revision.js +0 -94
  550. package/dist/src/chats/selection.d.ts +0 -25
  551. package/dist/src/chats/selection.js +0 -109
  552. package/dist/src/chats/source.d.ts +0 -23
  553. package/dist/src/chats/source.js +0 -135
  554. package/dist/src/chats/structure.d.ts +0 -57
  555. package/dist/src/chats/structure.js +0 -216
  556. package/dist/src/daemon/analysis-launch.d.ts +0 -5
  557. package/dist/src/daemon/analysis-launch.js +0 -81
  558. package/dist/src/daemon/analysis-token.d.ts +0 -6
  559. package/dist/src/daemon/analysis-token.js +0 -88
  560. package/dist/src/daemon/analysis-worker.d.ts +0 -22
  561. package/dist/src/daemon/analysis-worker.js +0 -219
  562. package/dist/src/daemon/chats-files.d.ts +0 -15
  563. package/dist/src/daemon/chats-files.js +0 -113
  564. package/dist/src/daemon/chats-worker.d.ts +0 -12
  565. package/dist/src/daemon/chats-worker.js +0 -200
  566. package/dist/workflows/analyze-chats.d.ts +0 -10
  567. package/dist/workflows/analyze-chats.declaration.json +0 -9
  568. package/dist/workflows/analyze-chats.js +0 -73
@@ -0,0 +1,232 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import { send } from "../cli/client.js";
3
+ import { record } from "../protocol.js";
4
+ import { runIdentity } from "../service/submission-handler.js";
5
+ import { located, readRecord, writeRecord } from "./operation-journal.js";
6
+ const attempts = 10;
7
+ const uncertain = [
8
+ "outcome-unknown",
9
+ "service-unavailable",
10
+ "service-busy",
11
+ "storage-failure",
12
+ "authority-unavailable",
13
+ "resolver-timeout",
14
+ "resolver-failure",
15
+ ];
16
+ function own(intent) {
17
+ return (intent.kind === "protected-effect" &&
18
+ intent.capability === "handoff" &&
19
+ intent.method === "run" &&
20
+ intent.target !== undefined &&
21
+ intent.payload !== undefined &&
22
+ intent.leaseGeneration !== undefined);
23
+ }
24
+ function completed(runId) {
25
+ return {
26
+ kind: "completed",
27
+ worker: "handoff",
28
+ receipt: runId,
29
+ result: runId,
30
+ };
31
+ }
32
+ async function saved(folder) {
33
+ const stored = record(await readRecord(folder, "result.json"));
34
+ if (stored === undefined)
35
+ return undefined;
36
+ if (stored["kind"] === "completed" && typeof stored["result"] === "string")
37
+ return completed(stored["result"]);
38
+ if (stored["kind"] === "failed" && typeof stored["reason"] === "string")
39
+ return { kind: "failed", reason: stored["reason"] };
40
+ throw new Error("invalid-handoff-journal");
41
+ }
42
+ async function retained(folder, outcome) {
43
+ if (["completed", "failed"].includes(outcome.kind))
44
+ await writeRecord(folder, {
45
+ name: "result.json",
46
+ value: outcome,
47
+ permissions: "private",
48
+ });
49
+ return outcome;
50
+ }
51
+ function outcome(reply) {
52
+ if (reply.kind !== "error") {
53
+ const runId = reply.result["runId"];
54
+ return reply.result["kind"] === "run-admitted" && typeof runId === "string"
55
+ ? completed(runId)
56
+ : { kind: "unknown" };
57
+ }
58
+ if (reply.error.code === "workflow-active")
59
+ return undefined;
60
+ if (reply.error.code === "chain-limit")
61
+ return { kind: "failed", reason: "chain-limit" };
62
+ return uncertain.includes(reply.error.code)
63
+ ? { kind: "unknown" }
64
+ : { kind: "failed", reason: "handoff-input-refused" };
65
+ }
66
+ async function input(settings, intent) {
67
+ const text = await settings.document(intent.payload);
68
+ try {
69
+ return record(JSON.parse(text ?? "null"));
70
+ }
71
+ catch {
72
+ return undefined;
73
+ }
74
+ }
75
+ async function submit(settings, call) {
76
+ const { intent, signal } = call;
77
+ if (!own(intent))
78
+ return { kind: "failed", reason: "unsupported-effect" };
79
+ const { folder } = located(settings.journal, intent.operationId);
80
+ const known = await saved(folder);
81
+ if (known !== undefined)
82
+ return known;
83
+ const submission = await input(settings, intent);
84
+ if (submission === undefined)
85
+ return retained(folder, {
86
+ kind: "failed",
87
+ reason: "handoff-input-refused",
88
+ });
89
+ const previous = await busyAttempts(folder);
90
+ for (let busy = previous; busy < attempts; busy += 1) {
91
+ if (!(await allowed(settings, { intent, signal })))
92
+ return { kind: "unknown" };
93
+ const reply = await submitted(settings, {
94
+ intent,
95
+ submission,
96
+ signal,
97
+ folder,
98
+ });
99
+ if (reply === undefined)
100
+ return { kind: "unknown" };
101
+ const result = outcome(reply);
102
+ if (result !== undefined)
103
+ return retained(folder, result);
104
+ await writeRecord(folder, {
105
+ name: "attempts.json",
106
+ value: { busy: busy + 1 },
107
+ permissions: "private",
108
+ });
109
+ if (busy + 1 < attempts)
110
+ await delay(settings.retryMilliseconds ?? 1000, undefined, {
111
+ signal,
112
+ }).catch(() => undefined);
113
+ }
114
+ return retained(folder, { kind: "failed", reason: "handoff-start-timeout" });
115
+ }
116
+ async function submitted(settings, call) {
117
+ const { intent, submission, folder, signal } = call;
118
+ const marker = (status) => writeRecord(folder, {
119
+ name: "submission.json",
120
+ value: { processId: process.pid, pending: status === "pending" },
121
+ permissions: "private",
122
+ });
123
+ await marker("pending");
124
+ if (!(await allowed(settings, { intent, signal }))) {
125
+ await marker("settled");
126
+ return undefined;
127
+ }
128
+ const reply = await send(settings.runtimeDirectory, {
129
+ operation: "run",
130
+ parameters: {
131
+ workflowId: intent.target,
132
+ source: "input",
133
+ submission,
134
+ commandId: `handoff:${intent.operationId}`,
135
+ },
136
+ });
137
+ if (reply.kind !== "error" || reply.error.code !== "outcome-unknown")
138
+ await marker("settled");
139
+ return reply;
140
+ }
141
+ async function allowed(settings, call) {
142
+ try {
143
+ call.signal.throwIfAborted();
144
+ const status = await settings.runStatus(call.intent.runId);
145
+ call.signal.throwIfAborted();
146
+ return status === "running";
147
+ }
148
+ catch {
149
+ return false;
150
+ }
151
+ }
152
+ async function inFlight(folder) {
153
+ const marker = record(await readRecord(folder, "submission.json"));
154
+ if (marker?.["pending"] !== true)
155
+ return false;
156
+ const pid = marker["processId"];
157
+ if (typeof pid !== "number" || !Number.isSafeInteger(pid) || pid <= 0)
158
+ throw new Error("invalid-handoff-journal");
159
+ try {
160
+ process.kill(pid, 0);
161
+ return true;
162
+ }
163
+ catch (error) {
164
+ return !(error instanceof Error &&
165
+ "code" in error &&
166
+ error.code === "ESRCH");
167
+ }
168
+ }
169
+ async function busyAttempts(folder) {
170
+ const previous = record(await readRecord(folder, "attempts.json"))?.["busy"] ?? 0;
171
+ if (typeof previous !== "number" ||
172
+ !Number.isSafeInteger(previous) ||
173
+ previous < 0 ||
174
+ previous > attempts)
175
+ throw new Error("invalid-handoff-journal");
176
+ return previous;
177
+ }
178
+ async function observed(settings, intent) {
179
+ if (!own(intent))
180
+ return { kind: "failed", reason: "unsupported-effect" };
181
+ const known = await saved(located(settings.journal, intent.operationId).folder);
182
+ if (known !== undefined)
183
+ return known;
184
+ const reply = await send(settings.runtimeDirectory, {
185
+ operation: "status",
186
+ parameters: { runId: runIdentity(`handoff:${intent.operationId}`) },
187
+ });
188
+ if (reply.kind === "error")
189
+ return reply.error.code === "unknown-run"
190
+ ? {
191
+ kind: (await inFlight(located(settings.journal, intent.operationId).folder))
192
+ ? "unknown"
193
+ : "absent",
194
+ }
195
+ : { kind: "unknown" };
196
+ const runId = record(reply.result["run"])?.["runId"];
197
+ return typeof runId === "string" ? completed(runId) : { kind: "unknown" };
198
+ }
199
+ export function handoffWorker(settings) {
200
+ const pending = new Map();
201
+ const deliver = (intent, signal) => {
202
+ const known = pending.get(intent.operationId);
203
+ if (known !== undefined)
204
+ return known.result;
205
+ const stopping = new AbortController();
206
+ const result = submit(settings, {
207
+ intent,
208
+ signal: AbortSignal.any([signal, stopping.signal]),
209
+ }).finally(() => pending.delete(intent.operationId));
210
+ pending.set(intent.operationId, { stopping, result });
211
+ return result;
212
+ };
213
+ return {
214
+ deliver,
215
+ reconcile: (intent) => observed(settings, intent),
216
+ retry: async (intent, signal) => {
217
+ if (!own(intent))
218
+ return { kind: "failed", reason: "unsupported-effect" };
219
+ const { folder } = located(settings.journal, intent.operationId);
220
+ if (!(await inFlight(folder)))
221
+ return { kind: "unknown" };
222
+ return deliver(intent, signal);
223
+ },
224
+ stop: async (intent) => {
225
+ const active = pending.get(intent.operationId);
226
+ active?.stopping.abort();
227
+ await active?.result;
228
+ const known = await observed(settings, intent);
229
+ return known.kind === "absent" ? { kind: "stopped" } : known;
230
+ },
231
+ };
232
+ }
@@ -2,7 +2,9 @@ interface QueueSettings {
2
2
  readonly directory: string;
3
3
  readonly runtimeDirectory: string;
4
4
  readonly intervalMilliseconds: number;
5
+ readonly lanes?: number;
5
6
  readonly resetQueue?: boolean;
7
+ readonly workflowId?: string;
6
8
  }
7
9
  export declare function issueQueue(settings: QueueSettings): {
8
10
  readonly stop: () => Promise<void>;
@@ -1,3 +1,4 @@
1
+ import { activeQueueRuns } from "./queue-status.js";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import { readFile, rename, rm, writeFile } from "node:fs/promises";
3
4
  import { join } from "node:path";
@@ -76,6 +77,34 @@ async function recover(settings, pending) {
76
77
  await pending.save();
77
78
  }
78
79
  }
80
+ async function trackedRuns(runtimeDirectory, commands) {
81
+ if (commands.length === 0)
82
+ return [];
83
+ if (commands.length === 1) {
84
+ const commandId = String(commands[0]);
85
+ const run = await runStatus(runtimeDirectory, commandId);
86
+ if (run === undefined)
87
+ throw new Error("queue-run-missing");
88
+ return [{ commandId, run }];
89
+ }
90
+ const reply = await send(runtimeDirectory, {
91
+ operation: "status",
92
+ parameters: { runIds: commands.map(runIdentity) },
93
+ });
94
+ if (reply.kind === "error")
95
+ throw new Error(reply.error.code);
96
+ const runs = reply.result["runs"];
97
+ if (!Array.isArray(runs))
98
+ throw new Error("invalid-run-list");
99
+ return commands.map((commandId) => {
100
+ const run = runs
101
+ .map(record)
102
+ .find((item) => item?.["runId"] === runIdentity(commandId));
103
+ if (run === undefined)
104
+ throw new Error("queue-run-missing");
105
+ return { commandId, run };
106
+ });
107
+ }
79
108
  function handedOff(run) {
80
109
  const outcome = record(run["outcome"]);
81
110
  return (run["state"] === "completed" &&
@@ -88,55 +117,36 @@ function checkFailureLimit(pending) {
88
117
  if (pending.failures.length >= 3)
89
118
  throw new QueueFailureLimit(`3 runs in a row ended without handoff: ${pending.failures.join(", ")}`);
90
119
  }
120
+ async function retired(pending, ending) {
121
+ const { commandId, run } = ending;
122
+ pending.commands.delete(commandId);
123
+ if (handedOff(run))
124
+ pending.failures.length = 0;
125
+ else
126
+ pending.failures.push(runIdentity(commandId));
127
+ await pending.save();
128
+ checkFailureLimit(pending);
129
+ if (handedOff(run))
130
+ return;
131
+ const outcome = record(run["outcome"]);
132
+ process.stderr.write(`Queue run ended ${String(outcome?.["kind"] ?? run["state"])}${outcome?.["issueUpdate"] === "failed" ? "; issueUpdate: failed" : ""}: ${runIdentity(commandId)}; continuing\n`);
133
+ }
91
134
  async function completed(settings, pending) {
92
135
  checkFailureLimit(pending);
93
- let pendingRun = false;
136
+ let pendingRuns = 0;
137
+ const endings = [];
94
138
  const tracked = [...pending.commands].filter((commandId) => commandId !== pending.retryCommandId);
95
- for (const commandId of tracked) {
96
- const run = await runStatus(settings.runtimeDirectory, commandId);
97
- if (run === undefined)
98
- throw new Error("queue-run-missing");
99
- if (!terminal(run)) {
100
- pendingRun = true;
101
- continue;
102
- }
103
- pending.commands.delete(commandId);
104
- if (handedOff(run))
105
- pending.failures.length = 0;
139
+ for (const { commandId, run } of await trackedRuns(settings.runtimeDirectory, tracked)) {
140
+ if (terminal(run))
141
+ endings.push({ commandId, run });
106
142
  else
107
- pending.failures.push(runIdentity(commandId));
108
- await pending.save();
109
- checkFailureLimit(pending);
110
- if (!handedOff(run)) {
111
- const outcome = record(run["outcome"]);
112
- process.stderr.write(`Queue run ended ${String(outcome?.["kind"] ?? run["state"])}${outcome?.["issueUpdate"] === "failed" ? "; issueUpdate: failed" : ""}: ${runIdentity(commandId)}; continuing\n`);
113
- }
143
+ pendingRuns += 1;
114
144
  }
115
- return pendingRun;
116
- }
117
- async function active(runtimeDirectory) {
118
- let after = "";
119
- do {
120
- const reply = await send(runtimeDirectory, {
121
- operation: "status",
122
- parameters: { after, limit: 500 },
123
- });
124
- if (reply.kind === "error")
125
- throw new Error(reply.error.code);
126
- const runs = reply.result["runs"];
127
- if (!Array.isArray(runs))
128
- throw new Error("invalid-run-list");
129
- if (runs.some((value) => {
130
- const run = record(value);
131
- return run?.["workflowId"] === "develop-issue" && !terminal(run);
132
- }))
133
- return true;
134
- const next = reply.result["next"];
135
- if (next !== null && (typeof next !== "string" || next <= after))
136
- throw new Error("invalid-run-page");
137
- after = next;
138
- } while (after !== null);
139
- return false;
145
+ endings.sort((first, second) => Number(first.run["terminalSequence"] ?? first.run["latestSequence"] ?? 0) -
146
+ Number(second.run["terminalSequence"] ?? second.run["latestSequence"] ?? 0));
147
+ for (const ending of endings)
148
+ await retired(pending, ending);
149
+ return pendingRuns;
140
150
  }
141
151
  async function submit(settings, pending) {
142
152
  const commandId = pending.retryCommandId ?? randomUUID();
@@ -145,7 +155,7 @@ async function submit(settings, pending) {
145
155
  const reply = await send(settings.runtimeDirectory, {
146
156
  operation: "run",
147
157
  parameters: {
148
- workflowId: "develop-issue",
158
+ workflowId: settings.workflowId ?? "develop-issue",
149
159
  source: "ticket",
150
160
  submission: { ticket: "next" },
151
161
  commandId,
@@ -163,19 +173,38 @@ async function submit(settings, pending) {
163
173
  process.stderr.write(`Queue ${reply.error.code}: command ${commandId}; will retry\n`);
164
174
  return;
165
175
  }
166
- if (!["no-ready-ticket", "workflow-active"].includes(reply.error.code))
176
+ if (![
177
+ "no-ready-ticket",
178
+ "workflow-active",
179
+ "ticket-active",
180
+ "ticket-dependency-open",
181
+ ].includes(reply.error.code))
167
182
  throw new Error(reply.error.code);
168
183
  pending.commands.delete(commandId);
169
184
  await pending.save();
170
185
  }
186
+ async function pendingUpdate(directory) {
187
+ return readFile(join(directory, "pending-self-update"), "utf8").catch((error) => {
188
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
189
+ return undefined;
190
+ throw error;
191
+ });
192
+ }
193
+ async function holdStarted(pending, observed) {
194
+ const { mark, activeRuns } = observed;
195
+ const timestamp = Number(mark);
196
+ if (!Number.isFinite(timestamp) || timestamp <= 0)
197
+ throw new Error("invalid-pending-self-update");
198
+ if (pending.selfUpdateHold?.mark !== mark || activeRuns > 0) {
199
+ pending.selfUpdateHold = { mark, startedAt: Date.now() };
200
+ await pending.save();
201
+ }
202
+ return pending.selfUpdateHold.startedAt;
203
+ }
171
204
  function updateHold(directory, pending) {
172
205
  let notice;
173
- return async () => {
174
- const mark = await readFile(join(directory, "pending-self-update"), "utf8").catch((error) => {
175
- if (error instanceof Error && "code" in error && error.code === "ENOENT")
176
- return undefined;
177
- throw error;
178
- });
206
+ return async (activeRuns = 0) => {
207
+ const mark = await pendingUpdate(directory);
179
208
  if (mark === undefined && pending.selfUpdateHold !== undefined) {
180
209
  pending.selfUpdateHold = undefined;
181
210
  await pending.save();
@@ -184,14 +213,8 @@ function updateHold(directory, pending) {
184
213
  notice = undefined;
185
214
  return false;
186
215
  }
187
- const timestamp = Number(mark);
188
- if (!Number.isFinite(timestamp) || timestamp <= 0)
189
- throw new Error("invalid-pending-self-update");
190
- if (pending.selfUpdateHold?.mark !== mark) {
191
- pending.selfUpdateHold = { mark, startedAt: Date.now() };
192
- await pending.save();
193
- }
194
- const holding = Date.now() - pending.selfUpdateHold.startedAt < 30 * 60 * 1000;
216
+ const startedAt = await holdStarted(pending, { mark, activeRuns });
217
+ const holding = activeRuns > 0 || Date.now() - startedAt < 30 * 60 * 1000;
195
218
  const line = holding
196
219
  ? "Queue holding for pending self-update\n"
197
220
  : "Queue continuing after pending self-update time limit\n";
@@ -227,11 +250,17 @@ async function cycle(settings, signal) {
227
250
  await delay(settings.intervalMilliseconds, undefined, { signal }).catch(() => undefined);
228
251
  if (stopped())
229
252
  return;
230
- if (await completed(settings, pending))
253
+ const pendingRuns = await completed(settings, pending);
254
+ const lanes = settings.lanes ?? 1;
255
+ if (pendingRuns >= lanes) {
256
+ await holding(pendingRuns);
231
257
  continue;
232
- if (await active(settings.runtimeDirectory))
258
+ }
259
+ const activeRuns = await activeQueueRuns(settings);
260
+ const held = await holding(activeRuns);
261
+ if (activeRuns >= lanes || activeRuns < pendingRuns)
233
262
  continue;
234
- if (await holding())
263
+ if (held)
235
264
  continue;
236
265
  if (stopped())
237
266
  return;
@@ -0,0 +1,18 @@
1
+ import type { Notification } from "./notify-message.ts";
2
+ export interface DeliveryEntry {
3
+ readonly key: string;
4
+ readonly message: Notification | null;
5
+ readonly state: "pending" | "sent" | "failed";
6
+ readonly attempts: number;
7
+ readonly lastAttemptAt: number | null;
8
+ readonly lastFailure: string | null;
9
+ }
10
+ export interface RecordedNotifications {
11
+ readonly entries: Map<string, DeliveryEntry>;
12
+ readonly retiredKeys: Set<string>;
13
+ }
14
+ export declare function recordedNotifications(file: string): Promise<RecordedNotifications | undefined>;
15
+ export declare function saveNotifications(file: string, journal: {
16
+ readonly entries: ReadonlyMap<string, DeliveryEntry>;
17
+ readonly retiredKeys: ReadonlySet<string>;
18
+ }): Promise<void>;
@@ -0,0 +1,108 @@
1
+ import { open, readFile, rename } from "node:fs/promises";
2
+ import { record } from "../protocol.js";
3
+ export async function recordedNotifications(file) {
4
+ const text = await readFile(file, "utf8").catch((error) => {
5
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
6
+ return undefined;
7
+ throw error;
8
+ });
9
+ if (text === undefined)
10
+ return undefined;
11
+ const value = JSON.parse(text);
12
+ const journal = record(value);
13
+ if (journal?.["version"] === 1 && Array.isArray(journal["entries"])) {
14
+ const entries = journal["entries"].map(deliveryEntry);
15
+ const found = new Map(entries.map((entry) => [entry.key, entry]));
16
+ if (found.size !== entries.length)
17
+ throw new Error("invalid-notify-record");
18
+ return {
19
+ entries: found,
20
+ retiredKeys: recordedKeys(journal["retiredKeys"] ?? []),
21
+ };
22
+ }
23
+ const keys = recordedKeys(value);
24
+ return {
25
+ retiredKeys: new Set(),
26
+ entries: new Map([...keys].map((key) => [
27
+ key,
28
+ {
29
+ key,
30
+ message: null,
31
+ state: "sent",
32
+ attempts: 0,
33
+ lastAttemptAt: Date.now(),
34
+ lastFailure: null,
35
+ },
36
+ ])),
37
+ };
38
+ }
39
+ function recordedKeys(value) {
40
+ if (!Array.isArray(value) ||
41
+ !value.every((key) => typeof key === "string"))
42
+ throw new Error("invalid-notify-record");
43
+ return new Set(value.map(String));
44
+ }
45
+ function notification(value) {
46
+ const message = record(value);
47
+ if (message === undefined)
48
+ return false;
49
+ return (["decision", "blocked", "failed"].includes(String(message["event"])) &&
50
+ ["event", "runId", "workflowId", "subject", "body"].every((field) => typeof message[field] === "string"));
51
+ }
52
+ function deliveryState(value) {
53
+ return value === "pending" || value === "sent" || value === "failed";
54
+ }
55
+ function attemptCount(value) {
56
+ return (typeof value === "number" &&
57
+ Number.isInteger(value) &&
58
+ value >= 0 &&
59
+ value <= 5);
60
+ }
61
+ function attemptTime(value) {
62
+ return (value === null ||
63
+ (typeof value === "number" && Number.isSafeInteger(value) && value >= 0));
64
+ }
65
+ function deliveryEntry(value) {
66
+ const { key, message, state, attempts, lastAttemptAt, lastFailure } = record(value) ?? {};
67
+ if (typeof key !== "string" ||
68
+ !deliveryState(state) ||
69
+ !attemptCount(attempts))
70
+ throw new Error("invalid-notify-record");
71
+ if (!attemptTime(lastAttemptAt) ||
72
+ !(lastFailure === null || typeof lastFailure === "string"))
73
+ throw new Error("invalid-notify-record");
74
+ const content = { message, state };
75
+ if (!entryMessage(content))
76
+ throw new Error("invalid-notify-record");
77
+ return {
78
+ key,
79
+ message: content.message,
80
+ state,
81
+ attempts,
82
+ lastAttemptAt,
83
+ lastFailure,
84
+ };
85
+ }
86
+ function entryMessage(entry) {
87
+ return entry.message === null
88
+ ? entry.state === "sent"
89
+ : notification(entry.message);
90
+ }
91
+ export async function saveNotifications(file, journal) {
92
+ const temporary = await open(`${file}.tmp`, "w", 0o600);
93
+ try {
94
+ await temporary.chmod(0o600);
95
+ await temporary.writeFile(JSON.stringify({
96
+ version: 1,
97
+ entries: [...journal.entries.values()],
98
+ ...(journal.retiredKeys.size > 0
99
+ ? { retiredKeys: [...journal.retiredKeys] }
100
+ : {}),
101
+ }) + "\n");
102
+ await temporary.sync();
103
+ }
104
+ finally {
105
+ await temporary.close();
106
+ }
107
+ await rename(`${file}.tmp`, file);
108
+ }
@@ -1,3 +1,4 @@
1
+ import { operationalDecision } from "../decision-visibility.js";
1
2
  import { decisionLines, runStatus } from "../cli/render.js";
2
3
  import { record } from "../protocol.js";
3
4
  function body(text, commands) {
@@ -17,7 +18,7 @@ function message(run, content) {
17
18
  };
18
19
  }
19
20
  export function decisionNotification(run, decision) {
20
- const lines = decisionLines(decision);
21
+ const lines = decisionLines(operationalDecision(decision, String(run["workflowId"])));
21
22
  const allowed = decision["allowed"];
22
23
  const count = Array.isArray(allowed) ? allowed.length : 0;
23
24
  const split = lines.length - count;