cloving 1.2.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 (518) hide show
  1. package/README.md +525 -185
  2. package/dist/src/agent-harness-shell.d.ts +12 -0
  3. package/dist/src/agent-harness-shell.js +90 -0
  4. package/dist/src/agent-harness-versions.d.ts +8 -0
  5. package/dist/src/agent-harness-versions.js +49 -0
  6. package/dist/src/agent-harnesses.d.ts +4 -0
  7. package/dist/src/agent-harnesses.js +13 -0
  8. package/dist/src/authority/commands.d.ts +1 -0
  9. package/dist/src/authority/commands.js +24 -4
  10. package/dist/src/authority/gate-reduction.d.ts +1 -0
  11. package/dist/src/authority/gate-reduction.js +18 -3
  12. package/dist/src/authority/gates.d.ts +2 -1
  13. package/dist/src/authority/gates.js +5 -1
  14. package/dist/src/authority/operations.js +1 -1
  15. package/dist/src/authority/reduction.d.ts +1 -1
  16. package/dist/src/authority/reduction.js +6 -1
  17. package/dist/src/authority/status-input.d.ts +1 -1
  18. package/dist/src/authority/status.d.ts +1 -0
  19. package/dist/src/authority/status.js +27 -3
  20. package/dist/src/authority/transitions.d.ts +5 -5
  21. package/dist/src/authority/transitions.js +6 -0
  22. package/dist/src/broker/actor/authority-delivery.js +1 -0
  23. package/dist/src/broker/actor/authority-queries.d.ts +7 -1
  24. package/dist/src/broker/actor/authority-queries.js +17 -2
  25. package/dist/src/broker/actor/authority-receipts.js +11 -1
  26. package/dist/src/broker/actor/authority-session.js +2 -2
  27. package/dist/src/broker/actor/authority-settings.js +4 -0
  28. package/dist/src/broker/actor/delivery-intents.d.ts +2 -1
  29. package/dist/src/broker/actor/delivery-intents.js +4 -4
  30. package/dist/src/broker/actor/effect-delivery.js +4 -1
  31. package/dist/src/broker/actor/effect-queries.d.ts +1 -0
  32. package/dist/src/broker/actor/effect-queries.js +7 -0
  33. package/dist/src/broker/actor/pin-check.js +1 -1
  34. package/dist/src/broker/actor/stop-plan.js +1 -1
  35. package/dist/src/broker/actor/turn-state.js +1 -1
  36. package/dist/src/broker/authority-admission.js +22 -9
  37. package/dist/src/broker/authority-restore.js +1 -0
  38. package/dist/src/broker/decision-restore.js +4 -4
  39. package/dist/src/broker/socket-address.d.ts +3 -1
  40. package/dist/src/broker/socket-address.js +2 -2
  41. package/dist/src/cli/arguments.d.ts +11 -13
  42. package/dist/src/cli/arguments.js +37 -107
  43. package/dist/src/cli/command-effects.d.ts +7 -0
  44. package/dist/src/cli/command-effects.js +39 -0
  45. package/dist/src/cli/commands.js +11 -4
  46. package/dist/src/cli/config-arguments.d.ts +11 -0
  47. package/dist/src/cli/config-arguments.js +59 -0
  48. package/dist/src/cli/config-command.d.ts +2 -0
  49. package/dist/src/cli/config-command.js +105 -0
  50. package/dist/src/cli/config-file.d.ts +3 -0
  51. package/dist/src/cli/config-file.js +50 -0
  52. package/dist/src/cli/config-lock.d.ts +7 -0
  53. package/dist/src/cli/config-lock.js +61 -0
  54. package/dist/src/cli/config-notice.d.ts +5 -0
  55. package/dist/src/cli/config-notice.js +46 -0
  56. package/dist/src/cli/doctor-github.d.ts +9 -0
  57. package/dist/src/cli/doctor-github.js +138 -0
  58. package/dist/src/cli/doctor-harness.d.ts +13 -0
  59. package/dist/src/cli/doctor-harness.js +242 -0
  60. package/dist/src/cli/doctor-local.d.ts +18 -0
  61. package/dist/src/cli/doctor-local.js +77 -0
  62. package/dist/src/cli/doctor-runtime.d.ts +15 -0
  63. package/dist/src/cli/doctor-runtime.js +176 -0
  64. package/dist/src/cli/doctor-service.d.ts +1 -0
  65. package/dist/src/cli/doctor-service.js +11 -0
  66. package/dist/src/cli/doctor-workflows.d.ts +3 -0
  67. package/dist/src/cli/doctor-workflows.js +45 -0
  68. package/dist/src/cli/doctor.d.ts +15 -0
  69. package/dist/src/cli/doctor.js +190 -0
  70. package/dist/src/cli/github-ci-progress.d.ts +8 -0
  71. package/dist/src/cli/github-ci-progress.js +83 -0
  72. package/dist/src/cli/github-retry.d.ts +6 -0
  73. package/dist/src/cli/github-retry.js +47 -0
  74. package/dist/src/cli/harness-probes.d.ts +22 -0
  75. package/dist/src/cli/harness-probes.js +183 -0
  76. package/dist/src/cli/main.d.ts +7 -1
  77. package/dist/src/cli/main.js +39 -61
  78. package/dist/src/cli/phone-arguments.d.ts +14 -0
  79. package/dist/src/cli/phone-arguments.js +37 -0
  80. package/dist/src/cli/phone-client.d.ts +2 -0
  81. package/dist/src/cli/phone-client.js +32 -0
  82. package/dist/src/cli/phone-command.d.ts +13 -0
  83. package/dist/src/cli/phone-command.js +101 -0
  84. package/dist/src/cli/probe-command.d.ts +36 -0
  85. package/dist/src/cli/probe-command.js +59 -0
  86. package/dist/src/cli/project-settings.d.ts +4 -0
  87. package/dist/src/cli/project-settings.js +33 -0
  88. package/dist/src/cli/render.d.ts +2 -1
  89. package/dist/src/cli/render.js +44 -28
  90. package/dist/src/cli/report-github.d.ts +18 -0
  91. package/dist/src/cli/report-github.js +200 -0
  92. package/dist/src/cli/report.d.ts +17 -0
  93. package/dist/src/cli/report.js +192 -0
  94. package/dist/src/cli/repository-arguments.d.ts +5 -0
  95. package/dist/src/cli/repository-arguments.js +34 -0
  96. package/dist/src/cli/runtime-arguments.d.ts +4 -0
  97. package/dist/src/cli/runtime-arguments.js +15 -0
  98. package/dist/src/cli/runtime-directory.d.ts +6 -2
  99. package/dist/src/cli/runtime-directory.js +72 -14
  100. package/dist/src/cli/toolchain.d.ts +2 -0
  101. package/dist/src/cli/toolchain.js +22 -2
  102. package/dist/src/cli/usage-render.d.ts +5 -0
  103. package/dist/src/cli/usage-render.js +40 -1
  104. package/dist/src/cli/usage.d.ts +3 -3
  105. package/dist/src/cli/usage.js +70 -44
  106. package/dist/src/cli/watch-actions.d.ts +2 -0
  107. package/dist/src/cli/watch-actions.js +4 -4
  108. package/dist/src/cli/watch-board-view.d.ts +1 -1
  109. package/dist/src/cli/watch-board-view.js +8 -2
  110. package/dist/src/cli/watch-board.js +23 -10
  111. package/dist/src/cli/watch-card-forms.js +2 -2
  112. package/dist/src/cli/watch-client.d.ts +1 -1
  113. package/dist/src/cli/watch-client.js +7 -2
  114. package/dist/src/cli/watch-header-view.d.ts +27 -0
  115. package/dist/src/cli/watch-header-view.js +116 -0
  116. package/dist/src/cli/watch-model.js +17 -4
  117. package/dist/src/cli/watch-page.js +48 -12
  118. package/dist/src/cli/watch-phone-client.d.ts +1 -0
  119. package/dist/src/cli/watch-phone-client.js +84 -0
  120. package/dist/src/cli/watch-phone-view.d.ts +56 -0
  121. package/dist/src/cli/watch-phone-view.js +113 -0
  122. package/dist/src/cli/watch-phone.d.ts +5 -0
  123. package/dist/src/cli/watch-phone.js +53 -0
  124. package/dist/src/cli/watch-pull-request.d.ts +3 -1
  125. package/dist/src/cli/watch-pull-request.js +4 -0
  126. package/dist/src/cli/watch-render.d.ts +9 -1
  127. package/dist/src/cli/watch-server.js +23 -5
  128. package/dist/src/cli/watch-turn.d.ts +4 -1
  129. package/dist/src/cli/watch-turn.js +37 -1
  130. package/dist/src/cli/watch-usage.js +5 -1
  131. package/dist/src/cli/watch-view.d.ts +6 -20
  132. package/dist/src/cli/watch-view.js +14 -106
  133. package/dist/src/cli.js +106 -23
  134. package/dist/src/command-settings.d.ts +16 -0
  135. package/dist/src/command-settings.js +42 -0
  136. package/dist/src/configuration-change.d.ts +11 -0
  137. package/dist/src/configuration-change.js +62 -0
  138. package/dist/src/configuration-display.d.ts +21 -0
  139. package/dist/src/configuration-display.js +69 -0
  140. package/dist/src/configuration-schema.d.ts +49 -0
  141. package/dist/src/configuration-schema.js +159 -0
  142. package/dist/src/daemon/claude-turn.d.ts +19 -5
  143. package/dist/src/daemon/claude-turn.js +116 -53
  144. package/dist/src/daemon/claude-worker.d.ts +3 -3
  145. package/dist/src/daemon/claude-worker.js +65 -76
  146. package/dist/src/daemon/command-journal.d.ts +5 -0
  147. package/dist/src/daemon/command-journal.js +50 -0
  148. package/dist/src/daemon/command-process.d.ts +28 -0
  149. package/dist/src/daemon/command-process.js +164 -0
  150. package/dist/src/daemon/command-worker.d.ts +19 -0
  151. package/dist/src/daemon/command-worker.js +182 -0
  152. package/dist/src/daemon/cursor-stream.d.ts +4 -0
  153. package/dist/src/daemon/cursor-stream.js +154 -0
  154. package/dist/src/daemon/daemon-adapters.d.ts +58 -0
  155. package/dist/src/daemon/daemon-adapters.js +102 -0
  156. package/dist/src/daemon/daemon-loops.d.ts +14 -0
  157. package/dist/src/daemon/daemon-loops.js +39 -0
  158. package/dist/src/daemon/daemon.d.ts +2 -33
  159. package/dist/src/daemon/daemon.js +126 -132
  160. package/dist/src/daemon/declared-input.d.ts +3 -0
  161. package/dist/src/daemon/declared-input.js +9 -0
  162. package/dist/src/daemon/delivery-router.js +1 -1
  163. package/dist/src/daemon/gemini-stream.d.ts +4 -0
  164. package/dist/src/daemon/gemini-stream.js +129 -0
  165. package/dist/src/daemon/github-ci-rerun.d.ts +26 -0
  166. package/dist/src/daemon/github-ci-rerun.js +92 -0
  167. package/dist/src/daemon/github-ci.js +89 -8
  168. package/dist/src/daemon/github-merge-call.d.ts +2 -0
  169. package/dist/src/daemon/github-merge.js +7 -3
  170. package/dist/src/daemon/github-methods.js +10 -0
  171. package/dist/src/daemon/github-operation.d.ts +1 -0
  172. package/dist/src/daemon/github-retry.js +8 -44
  173. package/dist/src/daemon/issue-queue.d.ts +1 -0
  174. package/dist/src/daemon/issue-queue.js +3 -26
  175. package/dist/src/daemon/notify.js +2 -1
  176. package/dist/src/daemon/opencode-stream.d.ts +5 -0
  177. package/dist/src/daemon/opencode-stream.js +87 -0
  178. package/dist/src/daemon/queue-status.d.ts +4 -0
  179. package/dist/src/daemon/queue-status.js +38 -0
  180. package/dist/src/daemon/retry-worktree.d.ts +18 -0
  181. package/dist/src/daemon/retry-worktree.js +139 -0
  182. package/dist/src/daemon/retrying-turn.d.ts +2 -0
  183. package/dist/src/daemon/retrying-turn.js +108 -30
  184. package/dist/src/daemon/schedule-journal.d.ts +19 -0
  185. package/dist/src/daemon/schedule-journal.js +57 -0
  186. package/dist/src/daemon/text-output.d.ts +18 -0
  187. package/dist/src/daemon/text-output.js +44 -0
  188. package/dist/src/daemon/ticket-registration.d.ts +5 -7
  189. package/dist/src/daemon/ticket-registration.js +85 -40
  190. package/dist/src/daemon/turn-command.d.ts +10 -0
  191. package/dist/src/daemon/turn-command.js +33 -0
  192. package/dist/src/daemon/turn-report.d.ts +18 -0
  193. package/dist/src/daemon/turn-report.js +79 -0
  194. package/dist/src/daemon/work-tree.d.ts +2 -1
  195. package/dist/src/daemon/work-tree.js +15 -12
  196. package/dist/src/daemon/workflow-generations.d.ts +32 -0
  197. package/dist/src/daemon/workflow-generations.js +57 -0
  198. package/dist/src/daemon/workflow-replay.d.ts +16 -0
  199. package/dist/src/daemon/workflow-replay.js +58 -0
  200. package/dist/src/daemon/workflow-schedules.d.ts +27 -0
  201. package/dist/src/daemon/workflow-schedules.js +185 -0
  202. package/dist/src/decision-wait.d.ts +1 -0
  203. package/dist/src/decision-wait.js +3 -0
  204. package/dist/src/delivery-backoff.d.ts +1 -0
  205. package/dist/src/delivery-backoff.js +3 -0
  206. package/dist/src/doctor-report.d.ts +24 -0
  207. package/dist/src/doctor-report.js +70 -0
  208. package/dist/src/execution-pin.d.ts +5 -0
  209. package/dist/src/execution-pin.js +67 -4
  210. package/dist/src/grok-result.d.ts +12 -0
  211. package/dist/src/grok-result.js +61 -0
  212. package/dist/src/harness-defaults.d.ts +35 -0
  213. package/dist/src/harness-defaults.js +121 -0
  214. package/dist/src/host/decision-resume.d.ts +35 -0
  215. package/dist/src/host/decision-resume.js +86 -0
  216. package/dist/src/host/gate-bridge.d.ts +48 -0
  217. package/dist/src/host/gate-bridge.js +124 -0
  218. package/dist/src/host/run-wait.d.ts +26 -0
  219. package/dist/src/host/run-wait.js +37 -0
  220. package/dist/src/host/start-delivery.d.ts +1 -1
  221. package/dist/src/host/start-delivery.js +10 -3
  222. package/dist/src/host/step-bridge-host.d.ts +6 -20
  223. package/dist/src/host/step-bridge-host.js +44 -58
  224. package/dist/src/merge-request.d.ts +19 -0
  225. package/dist/src/merge-request.js +32 -0
  226. package/dist/src/phone/canonical-json.d.ts +1 -0
  227. package/dist/src/phone/canonical-json.js +16 -0
  228. package/dist/src/phone/phone-link.d.ts +7 -0
  229. package/dist/src/phone/phone-link.js +21 -0
  230. package/dist/src/phone/qr-data.d.ts +8 -0
  231. package/dist/src/phone/qr-data.js +133 -0
  232. package/dist/src/phone/qr-matrix.d.ts +6 -0
  233. package/dist/src/phone/qr-matrix.js +175 -0
  234. package/dist/src/phone/qr-penalty.d.ts +3 -0
  235. package/dist/src/phone/qr-penalty.js +67 -0
  236. package/dist/src/phone/qr-picture.d.ts +5 -0
  237. package/dist/src/phone/qr-picture.js +24 -0
  238. package/dist/src/private-config.schema.json +263 -0
  239. package/dist/src/project-config.schema.json +65 -0
  240. package/dist/src/project-settings.d.ts +19 -0
  241. package/dist/src/project-settings.js +131 -0
  242. package/dist/src/relay/answer-check.d.ts +24 -0
  243. package/dist/src/relay/answer-check.js +145 -0
  244. package/dist/src/relay/answer-inbox.d.ts +15 -0
  245. package/dist/src/relay/answer-inbox.js +173 -0
  246. package/dist/src/relay/daemon-keys.d.ts +16 -0
  247. package/dist/src/relay/daemon-keys.js +75 -0
  248. package/dist/src/relay/decision-check.d.ts +3 -0
  249. package/dist/src/relay/decision-check.js +77 -0
  250. package/dist/src/relay/decision-close.d.ts +6 -0
  251. package/dist/src/relay/decision-close.js +72 -0
  252. package/dist/src/relay/decision-envelopes.d.ts +29 -0
  253. package/dist/src/relay/decision-envelopes.js +93 -0
  254. package/dist/src/relay/decision-watch.d.ts +14 -0
  255. package/dist/src/relay/decision-watch.js +68 -0
  256. package/dist/src/relay/envelope-post.d.ts +8 -0
  257. package/dist/src/relay/envelope-post.js +71 -0
  258. package/dist/src/relay/envelope.d.ts +28 -0
  259. package/dist/src/relay/envelope.js +53 -0
  260. package/dist/src/relay/hpke.d.ts +25 -0
  261. package/dist/src/relay/hpke.js +108 -0
  262. package/dist/src/relay/pairing-checks.d.ts +42 -0
  263. package/dist/src/relay/pairing-checks.js +172 -0
  264. package/dist/src/relay/pairing-follow.d.ts +8 -0
  265. package/dist/src/relay/pairing-follow.js +95 -0
  266. package/dist/src/relay/pairing-session.d.ts +29 -0
  267. package/dist/src/relay/pairing-session.js +87 -0
  268. package/dist/src/relay/phone-delivery.d.ts +51 -0
  269. package/dist/src/relay/phone-delivery.js +139 -0
  270. package/dist/src/relay/phone-devices.d.ts +36 -0
  271. package/dist/src/relay/phone-devices.js +46 -0
  272. package/dist/src/relay/phone-inbox.d.ts +18 -0
  273. package/dist/src/relay/phone-inbox.js +17 -0
  274. package/dist/src/relay/phone-operations.d.ts +18 -0
  275. package/dist/src/relay/phone-operations.js +121 -0
  276. package/dist/src/relay/phone-outbox.d.ts +21 -0
  277. package/dist/src/relay/phone-outbox.js +25 -0
  278. package/dist/src/relay/phone-socket.d.ts +6 -0
  279. package/dist/src/relay/phone-socket.js +63 -0
  280. package/dist/src/relay/relay-control.d.ts +7 -0
  281. package/dist/src/relay/relay-control.js +24 -0
  282. package/dist/src/relay/relay-files.d.ts +2 -0
  283. package/dist/src/relay/relay-files.js +23 -0
  284. package/dist/src/relay/relay-heartbeat.d.ts +17 -0
  285. package/dist/src/relay/relay-heartbeat.js +81 -0
  286. package/dist/src/relay/relay-http.d.ts +14 -0
  287. package/dist/src/relay/relay-http.js +63 -0
  288. package/dist/src/relay/relay-registration.d.ts +15 -0
  289. package/dist/src/relay/relay-registration.js +63 -0
  290. package/dist/src/relay/relay-service.d.ts +25 -0
  291. package/dist/src/relay/relay-service.js +216 -0
  292. package/dist/src/relay/relay-store.d.ts +48 -0
  293. package/dist/src/relay/relay-store.js +131 -0
  294. package/dist/src/relay/sealed-post.d.ts +25 -0
  295. package/dist/src/relay/sealed-post.js +52 -0
  296. package/dist/src/relay.d.ts +1 -0
  297. package/dist/src/relay.js +8 -0
  298. package/dist/src/report-statistics.d.ts +50 -0
  299. package/dist/src/report-statistics.js +70 -0
  300. package/dist/src/report.d.ts +171 -0
  301. package/dist/src/report.js +119 -0
  302. package/dist/src/repository-identity.d.ts +4 -0
  303. package/dist/src/repository-identity.js +4 -0
  304. package/dist/src/review-subject.d.ts +41 -0
  305. package/dist/src/review-subject.js +91 -0
  306. package/dist/src/run-wait.d.ts +23 -0
  307. package/dist/src/run-wait.js +20 -0
  308. package/dist/src/service/active-tickets.d.ts +4 -0
  309. package/dist/src/service/active-tickets.js +49 -0
  310. package/dist/src/service/command-effects.d.ts +4 -0
  311. package/dist/src/service/command-effects.js +16 -0
  312. package/dist/src/service/decision-handler.d.ts +4 -0
  313. package/dist/src/service/decision-handler.js +39 -4
  314. package/dist/src/service/gateway.js +18 -3
  315. package/dist/src/service/registry.d.ts +16 -3
  316. package/dist/src/service/registry.js +34 -3
  317. package/dist/src/service/replies.d.ts +9 -1
  318. package/dist/src/service/replies.js +12 -0
  319. package/dist/src/service/run-handlers.js +14 -55
  320. package/dist/src/service/run-metadata.d.ts +8 -0
  321. package/dist/src/service/run-metadata.js +53 -0
  322. package/dist/src/service/run-waits.d.ts +3 -0
  323. package/dist/src/service/run-waits.js +41 -0
  324. package/dist/src/service/service.d.ts +18 -2
  325. package/dist/src/service/service.js +23 -7
  326. package/dist/src/service/submission-handler.d.ts +0 -1
  327. package/dist/src/service/submission-handler.js +6 -34
  328. package/dist/src/service/worker-port.d.ts +1 -0
  329. package/dist/src/service/worker-totals.js +8 -23
  330. package/dist/src/step-bridge.d.ts +23 -0
  331. package/dist/src/steps/decision-gate.d.ts +2 -1
  332. package/dist/src/steps/decision-gate.js +7 -0
  333. package/dist/src/steps/merge-by-person.d.ts +2 -0
  334. package/dist/src/steps/merge-by-person.js +18 -0
  335. package/dist/src/steps/merge-when-green.d.ts +2 -0
  336. package/dist/src/steps/merge-when-green.js +62 -0
  337. package/dist/src/steps/review-by-agent.d.ts +5 -0
  338. package/dist/src/steps/review-by-agent.js +124 -0
  339. package/dist/src/steps/review-by-person.d.ts +4 -0
  340. package/dist/src/steps/review-by-person.js +16 -0
  341. package/dist/src/steps/review-subject.d.ts +2 -0
  342. package/dist/src/steps/review-subject.js +11 -0
  343. package/dist/src/steps/run-command.d.ts +2 -0
  344. package/dist/src/steps/run-command.js +15 -0
  345. package/dist/src/steps/sleep.d.ts +3 -0
  346. package/dist/src/steps/sleep.js +19 -0
  347. package/dist/src/steps/wait-for-decision.d.ts +2 -0
  348. package/dist/src/steps/wait-for-decision.js +71 -0
  349. package/dist/src/steps/wait-for-run.d.ts +5 -0
  350. package/dist/src/steps/wait-for-run.js +9 -0
  351. package/dist/src/storage/decision-snapshots.d.ts +1 -0
  352. package/dist/src/storage/decision-snapshots.js +7 -1
  353. package/dist/src/submission.d.ts +4 -0
  354. package/dist/src/submission.js +6 -2
  355. package/dist/src/testing.d.ts +29 -0
  356. package/dist/src/testing.js +264 -0
  357. package/dist/src/ticket-status-stream.d.ts +9 -0
  358. package/dist/src/ticket-status-stream.js +94 -0
  359. package/dist/src/token-prices.d.ts +12 -4
  360. package/dist/src/token-prices.js +15 -2
  361. package/dist/src/turn-profile.d.ts +1 -0
  362. package/dist/src/turn-profile.js +8 -1
  363. package/dist/src/worker-usage.d.ts +16 -0
  364. package/dist/src/worker-usage.js +57 -0
  365. package/dist/src/workflow-admission.d.ts +5 -0
  366. package/dist/src/workflow-candidate.d.ts +7 -2
  367. package/dist/src/workflow-candidate.js +47 -5
  368. package/dist/src/workflow-declaration.d.ts +21 -3
  369. package/dist/src/workflow-declaration.js +77 -9
  370. package/dist/src/workflow-discovery.d.ts +2 -1
  371. package/dist/src/workflow-discovery.js +4 -1
  372. package/dist/src/workflow-refusals.js +3 -0
  373. package/dist/src/workflow-schedule.d.ts +22 -0
  374. package/dist/src/workflow-schedule.js +53 -0
  375. package/dist/src/workflows.d.ts +11 -1
  376. package/dist/src/workflows.js +8 -0
  377. package/dist/tools/admit-workflow.d.ts +1 -0
  378. package/dist/tools/admit-workflow.js +18 -5
  379. package/dist/tools/doctor-adoption.d.ts +1 -0
  380. package/dist/tools/doctor-adoption.js +11 -0
  381. package/dist/tools/project-configuration.d.ts +4 -0
  382. package/dist/tools/project-configuration.js +47 -0
  383. package/dist/tools/self-host-processes.d.ts +1 -0
  384. package/dist/tools/self-host-processes.js +33 -22
  385. package/dist/tools/self-host-project.d.ts +4 -0
  386. package/dist/tools/self-host-project.js +61 -14
  387. package/dist/tools/self-host-runtime.d.ts +16 -0
  388. package/dist/tools/self-host-runtime.js +60 -0
  389. package/dist/tools/self-host-workflows.d.ts +1 -0
  390. package/dist/tools/self-host-workflows.js +36 -1
  391. package/dist/tools/self-host.js +86 -61
  392. package/dist/tools/workflow-candidate-counts.d.ts +6 -2
  393. package/dist/tools/workflow-candidate-counts.js +31 -16
  394. package/dist/tools/workflow-candidate-fields.d.ts +5 -0
  395. package/dist/tools/workflow-candidate-fields.js +24 -0
  396. package/dist/tools/workflow-candidate-helper-counts.d.ts +10 -0
  397. package/dist/tools/workflow-candidate-helper-counts.js +129 -0
  398. package/dist/tools/workflow-candidate-program.d.ts +2 -0
  399. package/dist/tools/workflow-candidate-program.js +36 -8
  400. package/dist/tools/workflow-candidate-source.js +77 -15
  401. package/dist/tools/workflow-candidate-syntax.d.ts +8 -2
  402. package/dist/tools/workflow-candidate-syntax.js +70 -26
  403. package/dist/tools/workflow-command.d.ts +1 -0
  404. package/dist/tools/workflow-command.js +143 -0
  405. package/dist/tools/workflow-folder-capture.d.ts +1 -0
  406. package/dist/tools/workflow-folder-capture.js +47 -2
  407. package/dist/tools/workflow-folders.d.ts +17 -1
  408. package/dist/tools/workflow-folders.js +59 -6
  409. package/dist/tools/workflow-test-hook.d.ts +7 -0
  410. package/dist/tools/workflow-test-hook.js +16 -0
  411. package/dist/tools/workflow-test-loader.d.ts +1 -0
  412. package/dist/tools/workflow-test-loader.js +10 -0
  413. package/dist/workflows/develop-ticket.js +1 -1
  414. package/dist/workflows/ticket-prompts.js +5 -1
  415. package/package.json +46 -4
  416. package/templates/develop/README.md +78 -0
  417. package/templates/develop/develop/accept.ts +11 -0
  418. package/templates/develop/develop/flow-tests.ts +357 -0
  419. package/templates/develop/develop/local.ts +40 -0
  420. package/templates/develop/develop/option-tests.ts +174 -0
  421. package/templates/develop/develop/partial-choice.ts +29 -0
  422. package/templates/develop/develop/partial.ts +28 -0
  423. package/templates/develop/develop/pull.ts +152 -0
  424. package/templates/develop/develop/queue-partial.ts +39 -0
  425. package/templates/develop/develop/refusal-tests.ts +174 -0
  426. package/templates/develop/develop/report.ts +156 -0
  427. package/templates/develop/develop/round.ts +40 -0
  428. package/templates/develop/develop/scenarios.ts +87 -0
  429. package/templates/develop/develop/turn.ts +67 -0
  430. package/templates/develop/develop.declaration.json +24 -0
  431. package/templates/develop/develop.test.ts +287 -0
  432. package/templates/develop/develop.ts +30 -0
  433. package/templates/develop/template.json +153 -0
  434. package/dist/src/chats/analysis-batches.d.ts +0 -42
  435. package/dist/src/chats/analysis-batches.js +0 -97
  436. package/dist/src/chats/analysis-checkpoint.d.ts +0 -29
  437. package/dist/src/chats/analysis-checkpoint.js +0 -31
  438. package/dist/src/chats/analysis-contract.d.ts +0 -80
  439. package/dist/src/chats/analysis-contract.js +0 -16
  440. package/dist/src/chats/analysis-data.d.ts +0 -3
  441. package/dist/src/chats/analysis-data.js +0 -85
  442. package/dist/src/chats/analysis-input.d.ts +0 -18
  443. package/dist/src/chats/analysis-input.js +0 -97
  444. package/dist/src/chats/analysis-links.d.ts +0 -2
  445. package/dist/src/chats/analysis-links.js +0 -25
  446. package/dist/src/chats/analysis-merge.d.ts +0 -16
  447. package/dist/src/chats/analysis-merge.js +0 -134
  448. package/dist/src/chats/analysis-prompt.d.ts +0 -18
  449. package/dist/src/chats/analysis-prompt.js +0 -42
  450. package/dist/src/chats/analysis-proposal.d.ts +0 -4
  451. package/dist/src/chats/analysis-proposal.js +0 -41
  452. package/dist/src/chats/analysis-provenance.d.ts +0 -4
  453. package/dist/src/chats/analysis-provenance.js +0 -99
  454. package/dist/src/chats/analysis-shape.d.ts +0 -4
  455. package/dist/src/chats/analysis-shape.js +0 -235
  456. package/dist/src/chats/analysis.d.ts +0 -3
  457. package/dist/src/chats/analysis.js +0 -42
  458. package/dist/src/chats/corpus-input.d.ts +0 -5
  459. package/dist/src/chats/corpus-input.js +0 -288
  460. package/dist/src/chats/destination.d.ts +0 -4
  461. package/dist/src/chats/destination.js +0 -96
  462. package/dist/src/chats/import.d.ts +0 -15
  463. package/dist/src/chats/import.js +0 -243
  464. package/dist/src/chats/input-file.d.ts +0 -3
  465. package/dist/src/chats/input-file.js +0 -32
  466. package/dist/src/chats/normalize.d.ts +0 -29
  467. package/dist/src/chats/normalize.js +0 -155
  468. package/dist/src/chats/previous-import.d.ts +0 -5
  469. package/dist/src/chats/previous-import.js +0 -34
  470. package/dist/src/chats/reader.d.ts +0 -21
  471. package/dist/src/chats/reader.js +0 -118
  472. package/dist/src/chats/redact-import.d.ts +0 -14
  473. package/dist/src/chats/redact-import.js +0 -141
  474. package/dist/src/chats/redaction.d.ts +0 -41
  475. package/dist/src/chats/redaction.js +0 -161
  476. package/dist/src/chats/reference-mapping.d.ts +0 -16
  477. package/dist/src/chats/reference-mapping.js +0 -153
  478. package/dist/src/chats/revision-input.d.ts +0 -3
  479. package/dist/src/chats/revision-input.js +0 -180
  480. package/dist/src/chats/revision.d.ts +0 -48
  481. package/dist/src/chats/revision.js +0 -94
  482. package/dist/src/chats/selection.d.ts +0 -25
  483. package/dist/src/chats/selection.js +0 -109
  484. package/dist/src/chats/source.d.ts +0 -23
  485. package/dist/src/chats/source.js +0 -135
  486. package/dist/src/chats/structure.d.ts +0 -57
  487. package/dist/src/chats/structure.js +0 -216
  488. package/dist/src/chats/workflow-generation-projection.d.ts +0 -8
  489. package/dist/src/chats/workflow-generation-projection.js +0 -75
  490. package/dist/src/chats/workflow-generation-source.d.ts +0 -25
  491. package/dist/src/chats/workflow-generation-source.js +0 -124
  492. package/dist/src/chats/workflow-generation-text.d.ts +0 -23
  493. package/dist/src/chats/workflow-generation-text.js +0 -74
  494. package/dist/src/chats/workflow-generation.d.ts +0 -69
  495. package/dist/src/chats/workflow-generation.js +0 -142
  496. package/dist/src/daemon/analysis-launch.d.ts +0 -5
  497. package/dist/src/daemon/analysis-launch.js +0 -81
  498. package/dist/src/daemon/analysis-token.d.ts +0 -6
  499. package/dist/src/daemon/analysis-token.js +0 -88
  500. package/dist/src/daemon/analysis-worker.d.ts +0 -20
  501. package/dist/src/daemon/analysis-worker.js +0 -271
  502. package/dist/src/daemon/chats-batch-binding.d.ts +0 -23
  503. package/dist/src/daemon/chats-batch-binding.js +0 -99
  504. package/dist/src/daemon/chats-capture.d.ts +0 -18
  505. package/dist/src/daemon/chats-capture.js +0 -194
  506. package/dist/src/daemon/chats-checkpoints.d.ts +0 -41
  507. package/dist/src/daemon/chats-checkpoints.js +0 -186
  508. package/dist/src/daemon/chats-files.d.ts +0 -42
  509. package/dist/src/daemon/chats-files.js +0 -252
  510. package/dist/src/daemon/chats-follow-up.d.ts +0 -38
  511. package/dist/src/daemon/chats-follow-up.js +0 -180
  512. package/dist/src/daemon/chats-question.d.ts +0 -15
  513. package/dist/src/daemon/chats-question.js +0 -93
  514. package/dist/src/daemon/chats-worker.d.ts +0 -13
  515. package/dist/src/daemon/chats-worker.js +0 -201
  516. package/dist/workflows/analyze-chats.d.ts +0 -15
  517. package/dist/workflows/analyze-chats.declaration.json +0 -22
  518. package/dist/workflows/analyze-chats.js +0 -250
@@ -1,10 +1,16 @@
1
+ import { turnCommand } from "./turn-command.js";
1
2
  import { spawn } from "node:child_process";
2
3
  import { createWriteStream, mkdtempSync, rmSync } from "node:fs";
3
4
  import { join } from "node:path";
4
5
  import { codexStream } from "./codex-stream.js";
5
- import { priceOf, usageOf, } from "../worker-usage.js";
6
+ import { cursorStream } from "./cursor-stream.js";
7
+ import { opencodeStream } from "./opencode-stream.js";
8
+ import { textOutput } from "./text-output.js";
9
+ import { grokResultFields, grokUsageWarnings } from "../grok-result.js";
10
+ import { geminiStream } from "./gemini-stream.js";
11
+ import { priceOf, modelUsageOf, usageOf, } from "../worker-usage.js";
6
12
  import { commandModel } from "../token-prices.js";
7
- import { analysisCommand, analysisEnvironment } from "./analysis-launch.js";
13
+ import { shellTurnScript } from "../agent-harness-shell.js";
8
14
  function parsed(line) {
9
15
  try {
10
16
  const value = JSON.parse(line);
@@ -20,21 +26,39 @@ function numberOf(value) {
20
26
  function textOf(value) {
21
27
  return typeof value === "string" ? value : null;
22
28
  }
29
+ function networkFailure(result) {
30
+ return (result?.failed === true &&
31
+ result.text.startsWith("API Error: Can't reach the API server"));
32
+ }
23
33
  export function capacityRefusal(result) {
34
+ if (networkFailure(result))
35
+ return false;
24
36
  return (result?.failed === true &&
25
37
  ["success", "error_during_execution"].includes(result.subtype ?? "error_during_execution") &&
26
38
  (result.text ===
27
39
  "Selected model is at capacity. Please try a different model." ||
28
40
  result.errorType === "overloaded"));
29
41
  }
30
- export function streamResult(line) {
42
+ export function retryKind(result) {
43
+ if (networkFailure(result))
44
+ return "network";
45
+ return capacityRefusal(result) ? "capacity" : null;
46
+ }
47
+ export function succeeded(end) {
48
+ return (end.reason === undefined &&
49
+ end.exitCode === 0 &&
50
+ end.result?.failed === false);
51
+ }
52
+ export function streamResult(line, worker) {
31
53
  if (!line.includes('"result"'))
32
54
  return undefined;
33
55
  const record = parsed(line);
34
56
  const field = (name) => Reflect.get(Object(record), name);
35
57
  if (field("type") !== "result")
36
58
  return undefined;
59
+ const models = modelUsageOf(field("models"));
37
60
  return {
61
+ ...(models === undefined ? {} : { models }),
38
62
  ...(typeof field("subtype") === "string"
39
63
  ? { subtype: String(field("subtype")) }
40
64
  : {}),
@@ -48,6 +72,7 @@ export function streamResult(line) {
48
72
  model: textOf(field("model")),
49
73
  sessionId: textOf(field("session_id")),
50
74
  turns: numberOf(field("num_turns")),
75
+ ...(worker === "grok" ? grokResultFields(record) : {}),
51
76
  };
52
77
  }
53
78
  function ended(stream) {
@@ -55,12 +80,7 @@ function ended(stream) {
55
80
  stream.end(settle);
56
81
  });
57
82
  }
58
- function turnEnvironment(configuration, start) {
59
- if (start.mode === "analysis")
60
- return {
61
- ...analysisEnvironment(start.folder),
62
- CLAUDE_CODE_OAUTH_TOKEN: start.analysisToken,
63
- };
83
+ export function turnEnvironment(configuration, start) {
64
84
  const excluded = new Set([
65
85
  "GH_TOKEN",
66
86
  "GITHUB_TOKEN",
@@ -75,25 +95,42 @@ function turnEnvironment(configuration, start) {
75
95
  if (start.turnId !== undefined)
76
96
  environment["CLOVING_TURN_ID"] = start.turnId;
77
97
  environment["GH_CONFIG_DIR"] = configuration;
98
+ if (start.worker === "opencode")
99
+ environment["OPENCODE_PERMISSION"] = '{"*":"allow"}';
78
100
  return environment;
79
101
  }
80
102
  function spawnTurn(start) {
81
103
  const configuration = mkdtempSync(join(start.folder, "gh-config-"));
82
- const [executable = "", ...fixed] = start.mode === "analysis" ? analysisCommand() : start.command;
104
+ const { parts, standardInput } = turnCommand(start);
105
+ const command = start.shell === undefined
106
+ ? parts
107
+ : [
108
+ start.shell,
109
+ "-l",
110
+ "-i",
111
+ "-c",
112
+ shellTurnScript,
113
+ "cloving-harness",
114
+ configuration,
115
+ ...parts,
116
+ ];
117
+ const [executable = "", ...fixed] = command;
83
118
  try {
84
119
  const child = spawn(executable, fixed, {
85
120
  cwd: start.directory,
86
121
  env: turnEnvironment(configuration, start),
87
122
  stdio: ["pipe", "pipe", "pipe"],
123
+ detached: start.shell !== undefined,
88
124
  });
89
- return { child, configuration };
125
+ return { child, configuration, standardInput };
90
126
  }
91
127
  catch (error) {
92
128
  rmSync(configuration, { recursive: true, force: true });
93
129
  throw error;
94
130
  }
95
131
  }
96
- function receivedLines(stream, lines) {
132
+ function receivedLines(stream, input) {
133
+ const { lines, worker } = input;
97
134
  for (const line of lines) {
98
135
  const event = parsed(line);
99
136
  const field = (name) => Reflect.get(Object(event), name);
@@ -102,7 +139,7 @@ function receivedLines(stream, lines) {
102
139
  stream.errorType = textOf(field("error"));
103
140
  if (type === "system" || type === "result")
104
141
  stream.model = textOf(field("model")) ?? stream.model;
105
- const result = streamResult(line);
142
+ const result = streamResult(line, worker);
106
143
  if (result !== undefined) {
107
144
  const errorType = result.errorType ?? stream.errorType;
108
145
  stream.result = {
@@ -113,16 +150,31 @@ function receivedLines(stream, lines) {
113
150
  }
114
151
  }
115
152
  function activityOutput(start) {
116
- if (start.mode === "analysis")
117
- return undefined;
118
153
  const output = createWriteStream(join(start.activityFolder ?? start.folder, "turn.jsonl"), { flags: start.activityFolder === undefined ? "w" : "a" });
119
154
  if (start.activityFolder !== undefined)
120
155
  output.write("\n");
121
156
  return output;
122
157
  }
123
- function turnOutput(start, stdout) {
158
+ function streamAdapter(worker) {
159
+ if (worker === "codex")
160
+ return codexStream();
161
+ if (worker === "cursor-agent")
162
+ return cursorStream();
163
+ if (worker === "gemini")
164
+ return geminiStream();
165
+ return undefined;
166
+ }
167
+ function turnOutput(start, child) {
124
168
  const output = activityOutput(start);
125
- const codex = start.worker === "codex" ? codexStream() : undefined;
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
+ }
126
178
  const stream = {
127
179
  pending: "",
128
180
  result: undefined,
@@ -130,66 +182,72 @@ function turnOutput(start, stdout) {
130
182
  errorType: null,
131
183
  };
132
184
  const received = (lines) => {
133
- receivedLines(stream, lines);
185
+ receivedLines(stream, {
186
+ lines,
187
+ ...(start.worker === undefined ? {} : { worker: start.worker }),
188
+ });
134
189
  };
135
190
  const written = (lines) => {
136
191
  for (const line of lines)
137
- output?.write(`${line}\n`);
192
+ output.write(`${line}\n`);
138
193
  received(lines);
139
194
  };
140
195
  stdout.setEncoding("utf8");
141
196
  stdout.on("data", (chunk) => {
142
- if (codex === undefined)
143
- output?.write(chunk);
197
+ if (adapter === undefined)
198
+ output.write(chunk);
144
199
  const lines = `${stream.pending}${chunk}`.split("\n");
145
200
  stream.pending = lines.pop() ?? "";
146
- if (codex !== undefined) {
147
- written(lines.flatMap(codex.line));
201
+ if (adapter !== undefined) {
202
+ written(lines.flatMap(adapter.line));
148
203
  return;
149
204
  }
150
205
  received(lines);
151
206
  });
152
207
  return async (exitCode) => {
153
- if (codex !== undefined) {
154
- written(codex.line(stream.pending));
155
- written([codex.result(exitCode)]);
208
+ if (adapter !== undefined) {
209
+ written(adapter.line(stream.pending));
210
+ written([adapter.result(exitCode)]);
156
211
  }
157
- if (output !== undefined)
158
- await ended(output);
159
- if (codex === undefined)
212
+ await ended(output);
213
+ if (adapter === undefined)
160
214
  received([stream.pending]);
161
215
  const result = stream.result;
162
216
  return result === undefined
163
217
  ? undefined
164
218
  : {
165
219
  ...result,
166
- model: result.model ??
167
- stream.model ??
168
- commandModel(start.mode === "analysis" ? analysisCommand() : start.command),
220
+ model: result.model ?? stream.model ?? commandModel(start.command),
169
221
  };
170
222
  };
171
223
  }
172
- function withoutAnalysisToken(start, result) {
173
- const token = start.mode === "analysis" ? start.analysisToken : undefined;
174
- if (token === undefined || result === undefined)
175
- return result;
176
- const redact = (value) => value === null ? null : value.replaceAll(token, "[redacted]");
177
- return {
178
- ...result,
179
- text: redact(result.text) ?? "",
180
- model: redact(result.model),
181
- sessionId: redact(result.sessionId),
182
- };
224
+ function signalTurn(turn, signal) {
225
+ const { child, start } = turn;
226
+ if (start.shell === undefined || child.pid === undefined) {
227
+ child.kill(signal);
228
+ return;
229
+ }
230
+ try {
231
+ process.kill(-child.pid, signal);
232
+ }
233
+ catch (error) {
234
+ const code = Reflect.get(Object(error), "code");
235
+ if (code !== "ESRCH" && code !== "EPERM")
236
+ throw error;
237
+ child.kill(signal);
238
+ }
183
239
  }
184
240
  export function startTurn(start) {
185
- const { child, configuration } = spawnTurn(start);
186
- const output = turnOutput(start, child.stdout);
187
- if (start.mode === "analysis")
188
- child.stderr.resume();
189
- else
190
- child.stderr.pipe(createWriteStream(join(start.folder, "turn.stderr.log")));
241
+ const { child, configuration, standardInput } = spawnTurn(start);
242
+ const output = turnOutput(start, child);
243
+ const warnings = grokUsageWarnings();
244
+ if (start.worker === "grok") {
245
+ child.stderr.setEncoding("utf8");
246
+ child.stderr.on("data", warnings.write);
247
+ }
248
+ child.stderr.pipe(createWriteStream(join(start.folder, "turn.stderr.log")));
191
249
  child.stdin.on("error", () => undefined);
192
- child.stdin.end(start.prompt);
250
+ child.stdin.end(standardInput);
193
251
  const closed = new Promise((settle) => {
194
252
  child.once("error", () => undefined);
195
253
  child.once("close", settle);
@@ -200,17 +258,22 @@ export function startTurn(start) {
200
258
  ended: closed
201
259
  .then(async (exitCode) => {
202
260
  timers.forEach(clearTimeout);
261
+ const result = await output(exitCode);
203
262
  return {
204
263
  exitCode,
205
- result: withoutAnalysisToken(start, await output(exitCode)),
264
+ result: result !== undefined && warnings.incomplete()
265
+ ? { ...result, usage: usageOf(null), costUsd: null }
266
+ : result,
206
267
  };
207
268
  })
208
269
  .finally(() => {
209
270
  rmSync(configuration, { recursive: true, force: true });
210
271
  }),
211
272
  stop: (graceMilliseconds) => {
212
- child.kill("SIGTERM");
213
- timers.push(setTimeout(() => child.kill("SIGKILL"), graceMilliseconds).unref());
273
+ signalTurn({ child, start }, "SIGTERM");
274
+ timers.push(setTimeout(() => {
275
+ signalTurn({ child, start }, "SIGKILL");
276
+ }, graceMilliseconds).unref());
214
277
  },
215
278
  };
216
279
  }
@@ -1,10 +1,9 @@
1
+ import { type HarnessSettings } from "../harness-defaults.ts";
1
2
  import { type TurnProfiles } from "../turn-profile.ts";
2
3
  import type { Delivery } from "../broker/actor/delivery-intents.ts";
3
- import { type Worker } from "./claude-turn.ts";
4
4
  import { type RetrySettings } from "./retrying-turn.ts";
5
5
  import { type WorkerReport } from "./operation-journal.ts";
6
- export interface ClaudeWorkerSettings extends RetrySettings {
7
- readonly worker?: Worker;
6
+ export interface ClaudeWorkerSettings extends RetrySettings, HarnessSettings {
8
7
  readonly profiles?: TurnProfiles;
9
8
  readonly workflowId?: (runId: string) => Promise<string | undefined>;
10
9
  readonly repository: string;
@@ -16,6 +15,7 @@ export interface ClaudeWorkerSettings extends RetrySettings {
16
15
  readonly stopMilliseconds?: number;
17
16
  }
18
17
  export interface ClaudeWorker extends Delivery {
18
+ readonly harness: (operationId: string) => Promise<string | undefined>;
19
19
  readonly stop: NonNullable<Delivery["stop"]>;
20
20
  readonly close: () => Promise<void>;
21
21
  }
@@ -1,19 +1,23 @@
1
1
  import { join } from "node:path";
2
- import { ticketStatus } from "../ticket-status.js";
3
- import { sumPrices } from "../worker-usage.js";
4
- import { analysisWorker } from "./analysis-worker.js";
5
- import { analysisMethod } from "./analysis-launch.js";
2
+ import { turnHarness } from "../harness-defaults.js";
6
3
  import { profileCommand, } from "../turn-profile.js";
7
- import {} from "./claude-turn.js";
4
+ import { succeeded } from "./claude-turn.js";
5
+ import { turnReport } from "./turn-report.js";
8
6
  import { startRetriedTurn } from "./retrying-turn.js";
9
7
  import { interrupted, located, operationKey, readRecord as read, replied, reportOf, writeRecord as written, } from "./operation-journal.js";
10
- import { changes, treeFailure, workTree } from "./work-tree.js";
8
+ import { treeFailure, workTree } from "./work-tree.js";
11
9
  export const claudeLimits = {
12
10
  summaryCharacters: 16_000,
13
11
  resultBytes: 48_000,
14
12
  stopMilliseconds: 2_000,
15
13
  };
16
14
  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
+ }
17
21
  function alive(processId) {
18
22
  if (typeof processId !== "number")
19
23
  return false;
@@ -41,58 +45,25 @@ async function reconciled(parts, intent) {
41
45
  await written(folder, { name: "result.json", value: ending });
42
46
  return replied({ receipt: key, worker }, ending);
43
47
  }
44
- async function resultText(tree, result) {
45
- const fields = {
46
- ticketStatus: ticketStatus(result.text),
47
- summary: result.text.slice(0, claudeLimits.summaryCharacters),
48
- ...(await changes(tree)),
49
- worktree: tree.path,
50
- costUsd: result.costUsd,
51
- usage: result.usage,
52
- model: result.model,
53
- ...(result.attempts === undefined ? {} : { attempts: result.attempts }),
54
- estimatedCostUsd: sumPrices((result.attempts ?? []).map((attempt) => attempt.estimatedCostUsd)),
55
- sessionId: result.sessionId,
56
- turns: result.turns,
57
- };
58
- const text = JSON.stringify(fields);
59
- const excess = Buffer.byteLength(text) - claudeLimits.resultBytes;
60
- if (excess <= 0)
61
- return { text };
62
- const kept = Math.max(0, fields.summary.length - excess);
63
- const shortened = JSON.stringify({
64
- ...fields,
65
- summary: fields.summary.slice(0, kept),
66
- });
67
- if (Buffer.byteLength(shortened) <= claudeLimits.resultBytes)
68
- return { text: shortened };
69
- const metadata = JSON.stringify({
70
- ...fields,
71
- ticketStatus: undefined,
72
- summary: "",
73
- });
74
- return {
75
- reason: "claude-result-too-large",
76
- ...(Buffer.byteLength(metadata) <= claudeLimits.resultBytes
77
- ? { text: metadata }
78
- : {}),
79
- };
80
- }
81
48
  async function outcome(running, end) {
82
49
  const endedAt = new Date().toISOString();
83
50
  const { result } = end;
84
- const report = result === undefined ? {} : await resultText(running.tree, result);
51
+ const report = result === undefined
52
+ ? {}
53
+ : await turnReport({
54
+ tree: running.tree,
55
+ harness: running.harness,
56
+ limits: claudeLimits,
57
+ }, result);
85
58
  const { text, reason } = report;
86
- if (reason === undefined &&
87
- text !== undefined &&
88
- end.exitCode === 0 &&
89
- result?.failed === false)
59
+ if (succeeded(end) && reason === undefined && text !== undefined)
90
60
  return { outcome: "completed", result: text, endedAt };
91
61
  if (running.stopping.requested)
92
62
  return { outcome: "stopped", endedAt };
93
63
  return {
94
64
  outcome: "failed",
95
- reason: reason ?? "claude-turn-failed",
65
+ reason: end.reason ?? reason ?? "claude-turn-failed",
66
+ ...(end.detail === undefined ? {} : { detail: end.detail }),
96
67
  ...(text === undefined ? {} : { result: text }),
97
68
  endedAt,
98
69
  };
@@ -105,9 +76,12 @@ async function concluded(parts, running) {
105
76
  }
106
77
  async function begun(parts, start) {
107
78
  const { intent, key, folder, tree, prompt } = start;
79
+ const { harness } = start;
80
+ const stopping = { requested: false, controller: new AbortController() };
108
81
  const turn = startRetriedTurn({
109
- worker: parts.settings.worker ?? "claude",
110
- command: profileCommand(parts.settings.command, start.profile),
82
+ ...harness,
83
+ resultBytes: claudeLimits.resultBytes,
84
+ command: profileCommand(harness.command, start.profile),
111
85
  environment: start.profile.env ?? {},
112
86
  turnId: `${intent.runId}-${key}`,
113
87
  directory: tree.path,
@@ -115,7 +89,9 @@ async function begun(parts, start) {
115
89
  prompt,
116
90
  }, {
117
91
  ...parts.settings,
92
+ stopping: stopping.controller,
118
93
  metadata: {
94
+ harness: harness.worker,
119
95
  operationId: intent.operationId,
120
96
  target: intent.target,
121
97
  branch: tree.branch,
@@ -123,7 +99,6 @@ async function begun(parts, start) {
123
99
  startCommit: tree.startCommit,
124
100
  },
125
101
  });
126
- const stopping = { requested: false };
127
102
  parts.active.set(key, {
128
103
  turn,
129
104
  stopping,
@@ -169,16 +144,30 @@ async function runWorkflow(settings, runId) {
169
144
  return undefined;
170
145
  }
171
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
+ }
172
160
  async function selectedProfile(settings, intent) {
173
161
  if (intent.method === "run-turn")
174
162
  return {};
175
- if (settings.worker === "codex")
176
- return "profile-needs-claude";
177
163
  const name = intent.method.slice("run-turn:".length);
178
164
  const profiles = settings.profiles ?? {};
179
165
  const profile = Object.hasOwn(profiles, name) ? profiles[name] : undefined;
180
166
  if (profile === undefined)
181
167
  return "unknown-profile";
168
+ const unsupported = unsupportedProfile(profile, turnHarness(settings, profile));
169
+ if (unsupported !== undefined)
170
+ return unsupported;
182
171
  if (profile.workflows === undefined)
183
172
  return profile;
184
173
  const workflowId = await runWorkflow(settings, intent.runId);
@@ -217,11 +206,19 @@ async function delivered(parts, call) {
217
206
  const first = await firstTurn(settings, { tree, intent });
218
207
  if (aborted())
219
208
  return failure("claude-start-aborted");
220
- await begun(parts, { ...place, intent, tree: first, prompt, profile });
209
+ await begun(parts, {
210
+ ...place,
211
+ intent,
212
+ tree: first,
213
+ prompt,
214
+ profile,
215
+ harness: turnHarness(settings, profile),
216
+ });
221
217
  return { receipt: place.key, worker };
222
218
  }
223
219
  async function halted(parts, active) {
224
220
  active.stopping.requested = true;
221
+ active.stopping.controller.abort();
225
222
  const turn = await active.turn.catch(() => undefined);
226
223
  turn?.stop(parts.settings.stopMilliseconds ?? claudeLimits.stopMilliseconds);
227
224
  await active.concluded;
@@ -236,35 +233,27 @@ async function stopped(parts, intent) {
236
233
  export function claudeWorker(settings) {
237
234
  if (settings.command.length === 0)
238
235
  throw new Error("invalid-claude-command");
236
+ turnHarness(settings);
237
+ for (const profile of Object.values(settings.profiles ?? {}))
238
+ turnHarness(settings, profile);
239
239
  const parts = {
240
240
  settings,
241
241
  active: new Map(),
242
242
  stopping: new AbortController(),
243
243
  };
244
- const analysis = analysisWorker(settings);
245
- const guarded = async (intent, run) => {
246
- if (!turnIntent(intent) ||
247
- Object.hasOwn(settings.profiles ?? {}, "analyze-chats") ||
248
- (await runWorkflow(settings, intent.runId)) !== "analyze-chats")
249
- return failure("analysis-worker-unavailable");
250
- return run();
251
- };
244
+ const guarded = (intent, run) => intent.method === "run-turn:analyze-chats"
245
+ ? Promise.resolve(failure("unsupported-effect"))
246
+ : run();
252
247
  return {
253
- deliver: (intent, signal) => intent.method === analysisMethod
254
- ? guarded(intent, () => analysis.deliver(intent, signal))
255
- : delivered(parts, {
256
- intent,
257
- signal: AbortSignal.any([signal, parts.stopping.signal]),
258
- }),
259
- reconcile: (intent, signal) => intent.method === analysisMethod
260
- ? guarded(intent, () => analysis.reconcile(intent, signal))
261
- : reconciled(parts, intent),
262
- stop: (intent, signal) => intent.method === analysisMethod
263
- ? guarded(intent, () => analysis.stop(intent, signal))
264
- : stopped(parts, intent),
248
+ harness: (operationId) => recordedHarness(settings.journal, operationId),
249
+ deliver: (intent, signal) => guarded(intent, () => delivered(parts, {
250
+ intent,
251
+ signal: AbortSignal.any([signal, parts.stopping.signal]),
252
+ })),
253
+ reconcile: (intent) => guarded(intent, () => reconciled(parts, intent)),
254
+ stop: (intent) => guarded(intent, () => stopped(parts, intent)),
265
255
  close: async () => {
266
256
  parts.stopping.abort();
267
- await analysis.close();
268
257
  await Promise.all([...parts.active.values()].map((active) => halted(parts, active)));
269
258
  },
270
259
  };
@@ -0,0 +1,5 @@
1
+ import type { CommandEffects } from "../command-settings.ts";
2
+ export declare function claimCommand(folder: string): Promise<boolean>;
3
+ export declare function commandStarted(folder: string): Promise<boolean>;
4
+ export declare function recordCommandResult(folder: string, value: unknown): Promise<boolean>;
5
+ export declare function commandEffects(journal: string): CommandEffects;
@@ -0,0 +1,50 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { link, mkdir, rm, stat, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { located, readRecord } from "./operation-journal.js";
5
+ import { record } from "../protocol.js";
6
+ function alreadyExists(error) {
7
+ if (Reflect.get(Object(error), "code") === "EEXIST")
8
+ return false;
9
+ throw error;
10
+ }
11
+ export async function claimCommand(folder) {
12
+ await mkdir(dirname(folder), { recursive: true, mode: 0o700 });
13
+ return mkdir(folder, { mode: 0o700 }).then(() => true, alreadyExists);
14
+ }
15
+ export async function commandStarted(folder) {
16
+ return stat(folder).then(() => true, (error) => {
17
+ if (Reflect.get(Object(error), "code") === "ENOENT")
18
+ return false;
19
+ throw error;
20
+ });
21
+ }
22
+ export async function recordCommandResult(folder, value) {
23
+ const staged = join(folder, `${randomUUID()}.staged`);
24
+ await writeFile(staged, JSON.stringify(value), { mode: 0o600, flag: "wx" });
25
+ try {
26
+ return await link(staged, join(folder, "result.json")).then(() => true, alreadyExists);
27
+ }
28
+ finally {
29
+ await rm(staged);
30
+ }
31
+ }
32
+ export function commandEffects(journal) {
33
+ const completed = new Map();
34
+ return (effects) => Promise.all(effects.map(async (effect) => {
35
+ const { key, folder } = located(journal, effect.operationId);
36
+ const cached = completed.get(key);
37
+ if (cached !== undefined)
38
+ return { ...effect, ...cached };
39
+ const result = record(await readRecord(folder, "result.json"));
40
+ const exitCode = result?.["exitCode"];
41
+ const duration = result?.["durationMilliseconds"];
42
+ const metadata = {
43
+ exitCode: typeof exitCode === "number" ? exitCode : null,
44
+ durationMilliseconds: typeof duration === "number" ? duration : null,
45
+ };
46
+ if (result !== undefined)
47
+ completed.set(key, metadata);
48
+ return { ...effect, ...metadata };
49
+ }));
50
+ }
@@ -0,0 +1,28 @@
1
+ export interface CommandStart {
2
+ readonly command: readonly string[];
3
+ readonly directory: string;
4
+ readonly folder: string;
5
+ readonly payload: string;
6
+ readonly runId: string;
7
+ readonly operationId: string;
8
+ readonly seconds: number;
9
+ readonly deadline?: string;
10
+ }
11
+ export interface CommandEnd {
12
+ readonly stdout: string;
13
+ readonly exitCode: number | null;
14
+ readonly durationMilliseconds: number;
15
+ readonly reason?: string;
16
+ }
17
+ export interface RunningCommand {
18
+ readonly ended: Promise<CommandEnd>;
19
+ readonly stop: () => void;
20
+ }
21
+ export interface CommandClock {
22
+ readonly now: () => number;
23
+ readonly timer: (wait: {
24
+ readonly milliseconds: number;
25
+ readonly expired: () => void;
26
+ }) => () => void;
27
+ }
28
+ export declare function startCommand(start: CommandStart, clock?: CommandClock): RunningCommand;