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,145 @@
1
+ import { createHash, verify } from "node:crypto";
2
+ import { record } from "../protocol.js";
3
+ import { publicKeyObject } from "./pairing-checks.js";
4
+ const textNames = [
5
+ "daemonId",
6
+ "deviceId",
7
+ "decisionId",
8
+ "command",
9
+ "subjectDigest",
10
+ "promptDigest",
11
+ "commandId",
12
+ "signedAt",
13
+ "keyClass",
14
+ ];
15
+ const countNames = ["expectedRevision", "generation"];
16
+ const commands = new Set(["answer", "approve", "deny"]);
17
+ const keyClasses = new Set(["answer", "approval"]);
18
+ const ageLimitMilliseconds = 10 * 60_000;
19
+ function payloadFields(payload) {
20
+ try {
21
+ return record(JSON.parse(payload));
22
+ }
23
+ catch {
24
+ return undefined;
25
+ }
26
+ }
27
+ function optionalText(value) {
28
+ return value === null || typeof value === "string";
29
+ }
30
+ function wellFormed(fields) {
31
+ return (fields["version"] === 1 &&
32
+ fields["kind"] === "answer" &&
33
+ textNames.every((name) => typeof fields[name] === "string") &&
34
+ countNames.every((name) => {
35
+ const value = fields[name];
36
+ return Number.isSafeInteger(value) && Number(value) >= 1;
37
+ }) &&
38
+ commands.has(String(fields["command"])) &&
39
+ keyClasses.has(String(fields["keyClass"])) &&
40
+ !Number.isNaN(Date.parse(String(fields["signedAt"]))));
41
+ }
42
+ function parsedAnswer(message) {
43
+ const fields = payloadFields(message.payload);
44
+ if (fields === undefined || !wellFormed(fields))
45
+ return undefined;
46
+ const text = fields["text"] ?? null;
47
+ const textDigest = fields["textDigest"] ?? null;
48
+ if (!optionalText(text) || !optionalText(textDigest))
49
+ return undefined;
50
+ const value = (name) => String(fields[name]);
51
+ return {
52
+ daemonId: value("daemonId"),
53
+ deviceId: value("deviceId"),
54
+ decisionId: value("decisionId"),
55
+ command: value("command"),
56
+ text,
57
+ textDigest,
58
+ expectedRevision: Number(fields["expectedRevision"]),
59
+ generation: Number(fields["generation"]),
60
+ subjectDigest: value("subjectDigest"),
61
+ promptDigest: value("promptDigest"),
62
+ commandId: value("commandId"),
63
+ signedAt: Date.parse(value("signedAt")),
64
+ keyClass: value("keyClass"),
65
+ };
66
+ }
67
+ function deviceRefusal(device) {
68
+ if (device === undefined)
69
+ return "device-unknown";
70
+ if (device.removedAt !== null)
71
+ return "device-revoked";
72
+ if (device.state !== "connected")
73
+ return "device-not-connected";
74
+ return undefined;
75
+ }
76
+ function signatureValid(checking) {
77
+ const { answer, device, message } = checking;
78
+ const key = publicKeyObject(answer.keyClass === "approval"
79
+ ? device.approvalPublicKey
80
+ : device.answerPublicKey);
81
+ if (key === undefined)
82
+ return false;
83
+ try {
84
+ return verify("sha256", Buffer.from(message.payload), { key, dsaEncoding: "ieee-p1363" }, Buffer.from(message.signature, "base64url"));
85
+ }
86
+ catch {
87
+ return false;
88
+ }
89
+ }
90
+ function digestOf(text) {
91
+ return `sha256:${createHash("sha256").update(text).digest("hex")}`;
92
+ }
93
+ const checks = [
94
+ (checking) => (signatureValid(checking) ? undefined : "signature-invalid"),
95
+ ({ answer }) => answer.command !== "answer" && answer.keyClass !== "approval"
96
+ ? "approval-key-required"
97
+ : undefined,
98
+ ({ answer, scope }) => Math.abs(scope.now - answer.signedAt) > ageLimitMilliseconds
99
+ ? "answer-too-old"
100
+ : undefined,
101
+ ({ answer, scope }) => scope.sentPromptDigest === answer.promptDigest
102
+ ? undefined
103
+ : "prompt-changed",
104
+ ({ answer }) => {
105
+ const expected = answer.text === null ? null : digestOf(answer.text);
106
+ return answer.textDigest === expected ? undefined : "answer-invalid";
107
+ },
108
+ ];
109
+ function decisionParameters(answer) {
110
+ return {
111
+ command: answer.command,
112
+ reference: answer.decisionId,
113
+ ...(answer.text === null ? {} : { text: answer.text }),
114
+ expectedRevision: answer.expectedRevision,
115
+ generation: answer.generation,
116
+ subjectDigest: answer.subjectDigest,
117
+ commandId: answer.commandId,
118
+ actor: `device:${answer.deviceId}`,
119
+ };
120
+ }
121
+ export function answerDecisionId(message) {
122
+ const decisionId = payloadFields(message.payload)?.["decisionId"];
123
+ return typeof decisionId === "string" ? decisionId : null;
124
+ }
125
+ export function answerVerdict(message, scope) {
126
+ const answer = parsedAnswer(message);
127
+ if (answer === undefined ||
128
+ answer.daemonId !== scope.daemonId ||
129
+ answer.deviceId !== scope.routedDeviceId)
130
+ return { kind: "refused", code: "answer-invalid", decisionId: null };
131
+ const device = scope.devices.find((item) => item.deviceId === answer.deviceId);
132
+ const refusal = deviceRefusal(device) ??
133
+ (device === undefined
134
+ ? undefined
135
+ : checks
136
+ .map((check) => check({ answer, device, message, scope }))
137
+ .find((code) => code !== undefined));
138
+ if (refusal !== undefined)
139
+ return { kind: "refused", code: refusal, decisionId: answer.decisionId };
140
+ return {
141
+ kind: "decision",
142
+ decisionId: answer.decisionId,
143
+ parameters: decisionParameters(answer),
144
+ };
145
+ }
@@ -0,0 +1,15 @@
1
+ import { type CheckContext } from "./sealed-post.ts";
2
+ export interface InboxReading {
3
+ readonly waitSeconds: number;
4
+ readonly signal: AbortSignal;
5
+ }
6
+ export interface InboxTiming {
7
+ readonly waitSeconds: number;
8
+ readonly retryMilliseconds: number;
9
+ }
10
+ export declare const inboxTiming: InboxTiming;
11
+ export declare function readAnswers(context: CheckContext, reading: InboxReading): Promise<boolean>;
12
+ export declare function runInbox(context: CheckContext, running: {
13
+ readonly signal: AbortSignal;
14
+ readonly timing: InboxTiming;
15
+ }): Promise<void>;
@@ -0,0 +1,173 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import { record } from "../protocol.js";
3
+ import { answerDecisionId, answerVerdict, } from "./answer-check.js";
4
+ import { receiptEnvelope } from "./decision-envelopes.js";
5
+ import { envelopeAad, relayInfo, unpadPlaintext } from "./envelope.js";
6
+ import { envelopesBody } from "./envelope-post.js";
7
+ import { openFrom } from "./hpke.js";
8
+ import { relayRequest } from "./relay-http.js";
9
+ import { storedRegistration } from "./relay-registration.js";
10
+ import { envelopeExpiry, sealedPost, } from "./sealed-post.js";
11
+ export const inboxTiming = {
12
+ waitSeconds: 25,
13
+ retryMilliseconds: 5000,
14
+ };
15
+ function inboxItem(value) {
16
+ const fields = record(value);
17
+ const answerId = fields?.["answer_id"];
18
+ const deviceId = fields?.["device_id"];
19
+ const ciphertext = fields?.["ciphertext"];
20
+ const sequence = fields?.["sequence"];
21
+ if (typeof answerId !== "string" ||
22
+ typeof deviceId !== "string" ||
23
+ typeof ciphertext !== "string" ||
24
+ typeof sequence !== "number")
25
+ return [];
26
+ return [{ answerId, deviceId, ciphertext, sequence }];
27
+ }
28
+ function inboxItems(body) {
29
+ const answers = record(body)?.["answers"];
30
+ if (!Array.isArray(answers))
31
+ return [];
32
+ return answers
33
+ .flatMap((item) => inboxItem(item))
34
+ .sort((left, right) => left.sequence - right.sequence);
35
+ }
36
+ function openedAnswer(answering) {
37
+ const { registration, item } = answering;
38
+ try {
39
+ const padded = openFrom(registration.keys.agreementPrivateKey, {
40
+ info: relayInfo,
41
+ aad: envelopeAad({
42
+ daemonId: registration.link.daemonId,
43
+ deviceId: item.deviceId,
44
+ kind: "answer",
45
+ }),
46
+ sealed: Buffer.from(item.ciphertext, "base64url"),
47
+ });
48
+ const message = record(JSON.parse(unpadPlaintext(padded).toString("utf8")));
49
+ const payload = message?.["payload"];
50
+ const signature = message?.["signature"];
51
+ if (typeof payload !== "string" || typeof signature !== "string")
52
+ return undefined;
53
+ return { payload, signature };
54
+ }
55
+ catch {
56
+ return undefined;
57
+ }
58
+ }
59
+ async function verdictOf(context, answering) {
60
+ const message = openedAnswer(answering);
61
+ if (message === undefined)
62
+ return { kind: "refused", code: "answer-invalid", decisionId: null };
63
+ const decisionId = answerDecisionId(message);
64
+ return answerVerdict(message, {
65
+ daemonId: answering.registration.link.daemonId,
66
+ routedDeviceId: answering.item.deviceId,
67
+ devices: await context.store.devices(),
68
+ now: context.now(),
69
+ sentPromptDigest: decisionId === null
70
+ ? null
71
+ : await context.store.delivery.promptDigest(decisionId),
72
+ });
73
+ }
74
+ function receiptBody(base, answered) {
75
+ if (answered.kind === "result")
76
+ return {
77
+ ...base,
78
+ code: String(answered.result["kind"]),
79
+ outcome: answered.result["decision"] ?? null,
80
+ receipt: answered.result["receipt"] ?? null,
81
+ };
82
+ const code = answered.kind === "refused" ? answered.code : answered.error.code;
83
+ return { ...base, code, outcome: null, receipt: null };
84
+ }
85
+ async function receiptWork(context, answering) {
86
+ const { registration, item } = answering;
87
+ const verdict = await verdictOf(context, answering);
88
+ const answered = verdict.kind === "decision"
89
+ ? await context.control({
90
+ operation: "decision",
91
+ parameters: verdict.parameters,
92
+ })
93
+ : verdict;
94
+ const receipt = receiptBody({ answerId: item.answerId, decisionId: verdict.decisionId }, answered);
95
+ const device = (await context.store.devices()).find((known) => known.deviceId === item.deviceId);
96
+ const issued = context.now();
97
+ const envelope = device === undefined
98
+ ? null
99
+ : await sealedPost(context, {
100
+ registration,
101
+ device,
102
+ decisionId: verdict.decisionId ?? item.answerId,
103
+ issuedAt: new Date(issued).toISOString(),
104
+ expiresAt: envelopeExpiry(null, issued),
105
+ build: (target) => receiptEnvelope({ receipt, target }),
106
+ });
107
+ return {
108
+ answerId: item.answerId,
109
+ sequence: item.sequence,
110
+ deviceId: item.deviceId,
111
+ envelope,
112
+ };
113
+ }
114
+ async function handled(context, answering) {
115
+ const { item } = answering;
116
+ const { link } = answering.registration;
117
+ const work = (await context.store.inbox.work(item.answerId)) ??
118
+ (await context.store.inbox.record(await receiptWork(context, answering)));
119
+ if (work.envelope !== null) {
120
+ const posted = await relayRequest(link.origin, {
121
+ method: "POST",
122
+ path: "/api/v1/relay/envelopes",
123
+ token: link.token,
124
+ body: envelopesBody(work.deviceId, [work.envelope]),
125
+ });
126
+ if (posted.status !== 201)
127
+ return false;
128
+ }
129
+ const acknowledged = await relayRequest(link.origin, {
130
+ method: "POST",
131
+ path: "/api/v1/relay/inbox/acks",
132
+ token: link.token,
133
+ body: { answer_ids: [item.answerId] },
134
+ });
135
+ if (acknowledged.status !== 200)
136
+ return false;
137
+ await context.store.inbox.complete({
138
+ answerId: item.answerId,
139
+ sequence: item.sequence,
140
+ });
141
+ return true;
142
+ }
143
+ export async function readAnswers(context, reading) {
144
+ const registration = await storedRegistration(context.phoneDirectory);
145
+ if (registration === undefined)
146
+ return false;
147
+ const { link } = registration;
148
+ const cursor = await context.store.inbox.cursor();
149
+ const reply = await relayRequest(link.origin, {
150
+ method: "GET",
151
+ path: `/api/v1/relay/inbox?after=${String(cursor)}&wait=${String(reading.waitSeconds)}`,
152
+ token: link.token,
153
+ timeoutMilliseconds: (reading.waitSeconds + 10) * 1000,
154
+ signal: reading.signal,
155
+ });
156
+ if (reply.status !== 200)
157
+ throw new Error(`inbox-failed: cloving.ai answered ${String(reply.status)}.`);
158
+ for (const item of inboxItems(reply.body))
159
+ if (!(await handled(context, { registration, item })))
160
+ return false;
161
+ return true;
162
+ }
163
+ export async function runInbox(context, running) {
164
+ const { signal, timing } = running;
165
+ while (!signal.aborted) {
166
+ const polled = await readAnswers(context, {
167
+ waitSeconds: timing.waitSeconds,
168
+ signal,
169
+ }).catch(() => false);
170
+ if (!polled)
171
+ await delay(timing.retryMilliseconds, undefined, { signal }).catch(() => undefined);
172
+ }
173
+ }
@@ -0,0 +1,16 @@
1
+ import { type KeyObject } from "node:crypto";
2
+ export interface DaemonKeys {
3
+ readonly agreementPrivateKey: string;
4
+ readonly agreementPublicKey: string;
5
+ readonly signingPrivateKey: string;
6
+ readonly signingPublicKey: string;
7
+ }
8
+ export declare function publicPoint(key: KeyObject): string;
9
+ export declare function newDaemonKeys(): DaemonKeys;
10
+ export declare function parseDaemonKeys(text: string | undefined): DaemonKeys | undefined;
11
+ export declare function keyFingerprint(keys: DaemonKeys): Buffer;
12
+ export declare function pairingIdentity(secret: Buffer): string;
13
+ export declare function pairingLink(origin: string, values: {
14
+ readonly secret: Buffer;
15
+ readonly keys: DaemonKeys;
16
+ }): string;
@@ -0,0 +1,75 @@
1
+ import { createHash, generateKeyPairSync } from "node:crypto";
2
+ import { canonicalJson } from "../phone/canonical-json.js";
3
+ const fields = [
4
+ "agreementPrivateKey",
5
+ "agreementPublicKey",
6
+ "signingPrivateKey",
7
+ "signingPublicKey",
8
+ ];
9
+ export function publicPoint(key) {
10
+ const jwk = key.export({ format: "jwk" });
11
+ return Buffer.concat([
12
+ Buffer.from([4]),
13
+ Buffer.from(String(jwk.x), "base64url"),
14
+ Buffer.from(String(jwk.y), "base64url"),
15
+ ]).toString("base64url");
16
+ }
17
+ function pair() {
18
+ const { privateKey, publicKey } = generateKeyPairSync("ec", {
19
+ namedCurve: "prime256v1",
20
+ });
21
+ return {
22
+ privateKey: privateKey
23
+ .export({ format: "der", type: "pkcs8" })
24
+ .toString("base64url"),
25
+ publicKey: publicPoint(publicKey),
26
+ };
27
+ }
28
+ export function newDaemonKeys() {
29
+ const agreement = pair();
30
+ const signing = pair();
31
+ return {
32
+ agreementPrivateKey: agreement.privateKey,
33
+ agreementPublicKey: agreement.publicKey,
34
+ signingPrivateKey: signing.privateKey,
35
+ signingPublicKey: signing.publicKey,
36
+ };
37
+ }
38
+ export function parseDaemonKeys(text) {
39
+ if (text === undefined)
40
+ return undefined;
41
+ const value = JSON.parse(text);
42
+ if (typeof value !== "object" || value === null)
43
+ return undefined;
44
+ const complete = fields.every((name) => {
45
+ const field = Reflect.get(value, name);
46
+ return typeof field === "string" && field !== "";
47
+ });
48
+ if (!complete)
49
+ return undefined;
50
+ return {
51
+ agreementPrivateKey: String(Reflect.get(value, "agreementPrivateKey")),
52
+ agreementPublicKey: String(Reflect.get(value, "agreementPublicKey")),
53
+ signingPrivateKey: String(Reflect.get(value, "signingPrivateKey")),
54
+ signingPublicKey: String(Reflect.get(value, "signingPublicKey")),
55
+ };
56
+ }
57
+ export function keyFingerprint(keys) {
58
+ const text = canonicalJson({
59
+ agreementKey: keys.agreementPublicKey,
60
+ signingKey: keys.signingPublicKey,
61
+ });
62
+ return createHash("sha256").update(text).digest().subarray(0, 16);
63
+ }
64
+ export function pairingIdentity(secret) {
65
+ return createHash("sha256")
66
+ .update("cloving-pairing-id|")
67
+ .update(secret)
68
+ .digest()
69
+ .subarray(0, 16)
70
+ .toString("base64url");
71
+ }
72
+ export function pairingLink(origin, values) {
73
+ const payload = Buffer.concat([values.secret, keyFingerprint(values.keys)]);
74
+ return `${origin}/pair#v1.${payload.toString("base64url")}`;
75
+ }
@@ -0,0 +1,3 @@
1
+ import { type CheckContext } from "./sealed-post.ts";
2
+ export type { CheckContext } from "./sealed-post.ts";
3
+ export declare function checkDecisions(context: CheckContext): Promise<void>;
@@ -0,0 +1,77 @@
1
+ import { record } from "../protocol.js";
2
+ import { closeDecisions } from "./decision-close.js";
3
+ import { fullEnvelope, previewEnvelope, promptDigest, } from "./decision-envelopes.js";
4
+ import { storedRegistration } from "./relay-registration.js";
5
+ import { envelopeExpiry, resultOf, rows, sealedPost, } from "./sealed-post.js";
6
+ async function devicePosts(context, plan) {
7
+ const { decision, workflowId } = plan;
8
+ const issued = context.now();
9
+ const posts = [];
10
+ for (const build of [previewEnvelope, fullEnvelope])
11
+ posts.push(await sealedPost(context, {
12
+ registration: plan.registration,
13
+ device: plan.device,
14
+ decisionId: String(decision["decisionId"]),
15
+ issuedAt: new Date(issued).toISOString(),
16
+ expiresAt: envelopeExpiry(decision["expiresAt"], issued),
17
+ build: (target) => build({ decision, workflowId, target }),
18
+ }));
19
+ return posts;
20
+ }
21
+ async function workflowOf(context, lookup) {
22
+ const known = lookup.known.get(lookup.runId);
23
+ if (known !== undefined)
24
+ return known;
25
+ const status = await resultOf(context, {
26
+ operation: "status",
27
+ parameters: { runId: lookup.runId },
28
+ });
29
+ const workflowId = record(status?.["run"])?.["workflowId"];
30
+ if (typeof workflowId !== "string")
31
+ return undefined;
32
+ lookup.known.set(lookup.runId, workflowId);
33
+ return workflowId;
34
+ }
35
+ async function offerDecision(context, plan) {
36
+ const decisionId = String(plan.decision["decisionId"]);
37
+ const delivered = new Set((await context.store.delivery.list(decisionId))
38
+ .filter((item) => item.topic === "decision")
39
+ .map((item) => item.deviceId));
40
+ for (const device of plan.devices.filter((item) => !delivered.has(item.deviceId)))
41
+ await context.store.delivery.offer(decisionId, {
42
+ runId: String(plan.decision["runId"]),
43
+ promptDigest: promptDigest(String(plan.decision["prompt"])),
44
+ deviceId: device.deviceId,
45
+ envelopes: await devicePosts(context, { ...plan, device }),
46
+ });
47
+ }
48
+ export async function checkDecisions(context) {
49
+ const registration = await storedRegistration(context.phoneDirectory);
50
+ if (registration === undefined)
51
+ return;
52
+ const questions = await resultOf(context, {
53
+ operation: "questions",
54
+ parameters: {},
55
+ });
56
+ if (questions === undefined)
57
+ return;
58
+ const decisions = rows(questions["decisions"]);
59
+ const devices = (await context.store.devices()).filter((device) => device.state === "connected" && device.removedAt === null);
60
+ const known = new Map();
61
+ for (const decision of devices.length === 0 ? [] : decisions) {
62
+ const runId = String(decision["runId"]);
63
+ const workflowId = await workflowOf(context, { runId, known });
64
+ if (workflowId !== undefined)
65
+ await offerDecision(context, {
66
+ decision,
67
+ workflowId,
68
+ registration,
69
+ devices,
70
+ });
71
+ }
72
+ await closeDecisions(context, {
73
+ registration,
74
+ pending: new Set(decisions.map((decision) => decision["decisionId"])),
75
+ });
76
+ await context.store.delivery.sweep();
77
+ }
@@ -0,0 +1,6 @@
1
+ import type { Registration } from "./relay-registration.ts";
2
+ import { type CheckContext } from "./sealed-post.ts";
3
+ export declare function closeDecisions(context: CheckContext, scope: {
4
+ readonly registration: Registration;
5
+ readonly pending: ReadonlySet<unknown>;
6
+ }): Promise<void>;
@@ -0,0 +1,72 @@
1
+ import { record } from "../protocol.js";
2
+ import { closedEnvelope } from "./decision-envelopes.js";
3
+ import { envelopeExpiry, rows, sealedPost, } from "./sealed-post.js";
4
+ const outcomes = new Set([
5
+ "answered",
6
+ "approved",
7
+ "denied",
8
+ "expired",
9
+ ]);
10
+ async function runOf(context, runId) {
11
+ const reply = await context.control({
12
+ operation: "status",
13
+ parameters: { runId },
14
+ });
15
+ if (reply.kind === "result")
16
+ return { run: record(reply.result["run"]) };
17
+ return reply.error.code === "unknown-run" ? { run: undefined } : undefined;
18
+ }
19
+ function endingFrom(open, run) {
20
+ const decided = rows(run?.["decisions"]).find((item) => item["decisionId"] === open.decisionId);
21
+ const outcome = decided?.["decision"];
22
+ const actor = decided?.["actor"];
23
+ return {
24
+ decisionId: open.decisionId,
25
+ outcome: typeof outcome === "string" && outcomes.has(outcome)
26
+ ? outcome
27
+ : "unknown",
28
+ actor: typeof actor === "string" ? actor : null,
29
+ };
30
+ }
31
+ async function endingOf(context, open) {
32
+ const found = await runOf(context, open.runId);
33
+ return found === undefined ? undefined : endingFrom(open, found.run);
34
+ }
35
+ async function closeDecision(context, scope) {
36
+ const { decisionId } = scope.open;
37
+ const ending = await endingOf(context, scope.open);
38
+ if (ending === undefined)
39
+ return;
40
+ const reached = new Set((await context.store.delivery.list(decisionId))
41
+ .filter((item) => item.topic === "decision" &&
42
+ (item.state === "sent" || item.state === "pending"))
43
+ .map((item) => item.deviceId));
44
+ const devices = (await context.store.devices()).filter((device) => reached.has(device.deviceId) &&
45
+ device.state === "connected" &&
46
+ device.removedAt === null);
47
+ const issued = context.now();
48
+ const closing = [];
49
+ for (const device of devices) {
50
+ const post = await sealedPost(context, {
51
+ registration: scope.registration,
52
+ device,
53
+ decisionId,
54
+ issuedAt: new Date(issued).toISOString(),
55
+ expiresAt: envelopeExpiry(null, issued),
56
+ build: (target) => closedEnvelope({ ending, target }),
57
+ });
58
+ closing.push({ deviceId: device.deviceId, envelopes: [post] });
59
+ }
60
+ await context.store.delivery.close(decisionId, {
61
+ devices: closing,
62
+ at: issued,
63
+ });
64
+ }
65
+ export async function closeDecisions(context, scope) {
66
+ for (const open of await context.store.delivery.open())
67
+ if (!scope.pending.has(open.decisionId))
68
+ await closeDecision(context, {
69
+ registration: scope.registration,
70
+ open,
71
+ });
72
+ }
@@ -0,0 +1,29 @@
1
+ import type { Fields } from "../protocol.ts";
2
+ import { type Envelope } from "./envelope.ts";
3
+ export interface EnvelopeTarget {
4
+ readonly daemonId: string;
5
+ readonly deviceId: string;
6
+ readonly sequence: number;
7
+ readonly issuedAt: string;
8
+ readonly expiresAt: string;
9
+ }
10
+ export interface DecisionSource {
11
+ readonly decision: Fields;
12
+ readonly workflowId: string;
13
+ readonly target: EnvelopeTarget;
14
+ }
15
+ export declare function promptDigest(prompt: string): string;
16
+ export declare function receiptEnvelope(source: {
17
+ readonly receipt: Fields;
18
+ readonly target: EnvelopeTarget;
19
+ }): Envelope;
20
+ export declare function closedEnvelope(source: {
21
+ readonly ending: {
22
+ readonly decisionId: string;
23
+ readonly outcome: string;
24
+ readonly actor: string | null;
25
+ };
26
+ readonly target: EnvelopeTarget;
27
+ }): Envelope;
28
+ export declare function previewEnvelope(source: DecisionSource): Envelope;
29
+ export declare function fullEnvelope(source: DecisionSource): Envelope;