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,8 @@
1
+ import type { OpenPairing, PairingSessions } from "./pairing-session.ts";
2
+ import type { RelayStore } from "./relay-store.ts";
3
+ export interface FollowContext {
4
+ readonly store: RelayStore;
5
+ readonly sessions: PairingSessions;
6
+ }
7
+ export declare function denied(open: OpenPairing): Promise<void>;
8
+ export declare function followPairing(context: FollowContext, open: OpenPairing): Promise<void>;
@@ -0,0 +1,95 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import { checkedRequest, checkValid, } from "./pairing-checks.js";
3
+ import { relayRequest } from "./relay-http.js";
4
+ function field(body, name) {
5
+ return typeof body === "object" && body !== null
6
+ ? Reflect.get(body, name)
7
+ : undefined;
8
+ }
9
+ export async function denied(open) {
10
+ await relayRequest(open.registration.link.origin, {
11
+ method: "POST",
12
+ path: `/api/v1/relay/pairings/${open.pairingId}/deny`,
13
+ token: open.registration.link.token,
14
+ }).catch(() => undefined);
15
+ }
16
+ async function revoked(store, deviceId) {
17
+ return (await store.devices()).some((device) => device.deviceId === deviceId && device.removedAt !== null);
18
+ }
19
+ async function refusalOf(context, checked) {
20
+ if (checked.kind === "refused")
21
+ return checked.code;
22
+ const removed = await revoked(context.store, checked.request.deviceId);
23
+ return removed ? "pairing-device-revoked" : null;
24
+ }
25
+ async function onRequest(context, exchange) {
26
+ const { open } = exchange;
27
+ if (open.stage !== "open")
28
+ return;
29
+ const checked = checkedRequest(open.secret, field(exchange.body, "request"));
30
+ const code = await refusalOf(context, checked);
31
+ if (code !== null || checked.kind === "refused") {
32
+ await denied(open);
33
+ context.sessions.end(open, code);
34
+ return;
35
+ }
36
+ open.request = checked.request;
37
+ open.stage = "requested";
38
+ }
39
+ async function onCheck(context, exchange) {
40
+ const { open } = exchange;
41
+ const { request, nonce } = open;
42
+ if (open.stage !== "checking" || request === undefined || nonce === undefined)
43
+ return;
44
+ const valid = checkValid(field(exchange.body, "check"), {
45
+ daemonId: open.registration.link.daemonId,
46
+ deviceId: request.deviceId,
47
+ nonce,
48
+ answerPublicKey: request.answerPublicKey,
49
+ now: Date.now(),
50
+ });
51
+ if (valid)
52
+ await context.store.connect(request.deviceId);
53
+ context.sessions.end(open, valid ? null : "pairing-check-invalid");
54
+ }
55
+ function onEnd(context, exchange) {
56
+ const { open } = exchange;
57
+ const expired = field(exchange.body, "stage") === "expired";
58
+ context.sessions.end(open, expired && open.stage !== "checking" ? "pairing-expired" : null);
59
+ return Promise.resolve();
60
+ }
61
+ const handlers = {
62
+ requested: onRequest,
63
+ checked: onCheck,
64
+ denied: onEnd,
65
+ expired: onEnd,
66
+ };
67
+ async function polled(open, stage) {
68
+ try {
69
+ return await relayRequest(open.registration.link.origin, {
70
+ method: "GET",
71
+ path: `/api/v1/relay/pairings/${open.pairingId}?wait=25&stage=${stage}`,
72
+ token: open.registration.link.token,
73
+ signal: open.stop.signal,
74
+ timeoutMilliseconds: 40_000,
75
+ });
76
+ }
77
+ catch {
78
+ await delay(1000, undefined, { signal: open.stop.signal }).catch(() => undefined);
79
+ return undefined;
80
+ }
81
+ }
82
+ export async function followPairing(context, open) {
83
+ let stage = "open";
84
+ while (context.sessions.current() === open) {
85
+ const reply = await polled(open, stage);
86
+ if (reply === undefined)
87
+ continue;
88
+ if (reply.status !== 200) {
89
+ context.sessions.end(open, "pairing-expired");
90
+ return;
91
+ }
92
+ stage = String(field(reply.body, "stage"));
93
+ await handlers[stage]?.(context, { open, body: reply.body });
94
+ }
95
+ }
@@ -0,0 +1,29 @@
1
+ import type { PairingRequest } from "./pairing-checks.ts";
2
+ import type { Registration } from "./relay-registration.ts";
3
+ export interface OpenPairing {
4
+ readonly pairingId: string;
5
+ readonly secret: Buffer;
6
+ readonly link: string;
7
+ readonly expiresAt: string;
8
+ readonly registration: Registration;
9
+ readonly stop: AbortController;
10
+ stage: "open" | "requested" | "checking";
11
+ request: PairingRequest | undefined;
12
+ nonce: string | undefined;
13
+ }
14
+ export declare class RegistrationEnded extends Error {
15
+ constructor();
16
+ }
17
+ export interface PairingSessions {
18
+ readonly current: () => OpenPairing | undefined;
19
+ readonly refusal: () => string | null;
20
+ readonly generation: () => number;
21
+ readonly start: (registration: Registration, startedIn: number) => Promise<OpenPairing>;
22
+ readonly end: (open: OpenPairing, refusal: string | null) => void;
23
+ readonly cancel: () => void;
24
+ }
25
+ export interface SessionSettings {
26
+ readonly secret: () => Buffer;
27
+ readonly follow: (open: OpenPairing) => Promise<void>;
28
+ }
29
+ export declare function pairingSessions(settings: SessionSettings): PairingSessions;
@@ -0,0 +1,87 @@
1
+ import { pairingIdentity, pairingLink } from "./daemon-keys.js";
2
+ import { relayRequest } from "./relay-http.js";
3
+ export class RegistrationEnded extends Error {
4
+ constructor() {
5
+ super("registration-ended: cloving.ai does not know this computer.");
6
+ }
7
+ }
8
+ function expiry(body) {
9
+ const value = typeof body === "object" && body !== null
10
+ ? Reflect.get(body, "expires_at")
11
+ : undefined;
12
+ if (typeof value !== "string" || Number.isNaN(Date.parse(value)))
13
+ throw new Error("pairing-invalid: cloving.ai sent no expiry.");
14
+ return value;
15
+ }
16
+ async function created(registration, secret) {
17
+ const pairingId = pairingIdentity(secret);
18
+ const reply = await relayRequest(registration.link.origin, {
19
+ method: "POST",
20
+ path: "/api/v1/relay/pairings",
21
+ token: registration.link.token,
22
+ body: { pairing_id: pairingId },
23
+ });
24
+ if (reply.status === 401)
25
+ throw new RegistrationEnded();
26
+ if (reply.status !== 201)
27
+ throw new Error(`pairing-failed: cloving.ai answered ${String(reply.status)}.`);
28
+ return {
29
+ pairingId,
30
+ secret,
31
+ expiresAt: expiry(reply.body),
32
+ registration,
33
+ stop: new AbortController(),
34
+ stage: "open",
35
+ request: undefined,
36
+ nonce: undefined,
37
+ link: pairingLink(registration.link.origin, {
38
+ secret,
39
+ keys: registration.keys,
40
+ }),
41
+ };
42
+ }
43
+ function cancelled() {
44
+ return new Error("pairing-cancelled: The pairing was cancelled.");
45
+ }
46
+ export function pairingSessions(settings) {
47
+ let open;
48
+ let refusal = null;
49
+ let timer;
50
+ let generation = 0;
51
+ const end = (ended, code) => {
52
+ if (open !== ended)
53
+ return;
54
+ clearTimeout(timer);
55
+ ended.stop.abort();
56
+ open = undefined;
57
+ refusal = code;
58
+ };
59
+ return {
60
+ current: () => open,
61
+ refusal: () => refusal,
62
+ end,
63
+ cancel: () => {
64
+ generation += 1;
65
+ if (open !== undefined)
66
+ end(open, null);
67
+ },
68
+ generation: () => generation,
69
+ start: async (registration, startedIn) => {
70
+ if (startedIn !== generation)
71
+ throw cancelled();
72
+ const started = await created(registration, settings.secret());
73
+ if (startedIn !== generation)
74
+ throw cancelled();
75
+ open = started;
76
+ refusal = null;
77
+ timer = setTimeout(() => {
78
+ end(started, started.stage === "requested" ? "pairing-expired" : null);
79
+ }, Date.parse(started.expiresAt) - Date.now());
80
+ timer.unref();
81
+ settings.follow(started).catch(() => {
82
+ end(started, "pairing-failed");
83
+ });
84
+ return started;
85
+ },
86
+ };
87
+ }
@@ -0,0 +1,51 @@
1
+ import { Actor, type EffectSuccessPayload } from "solid-objects";
2
+ export type PostedEnvelope = {
3
+ readonly envelopeId: string;
4
+ readonly kind: string;
5
+ readonly ciphertext: string;
6
+ readonly expiresAt: string;
7
+ readonly push: string;
8
+ readonly collapseId: string | null;
9
+ };
10
+ export type DeliveryTopic = "decision" | "closed";
11
+ export type DeviceDelivery = {
12
+ readonly deviceId: string;
13
+ readonly topic: DeliveryTopic;
14
+ readonly state: "pending" | "sent" | "failed" | "cancelled";
15
+ readonly attempts: number;
16
+ readonly lastAttemptAt: number | null;
17
+ readonly lastFailure: string | null;
18
+ readonly envelopes: readonly PostedEnvelope[];
19
+ };
20
+ export type DeviceEnvelopes = {
21
+ readonly deviceId: string;
22
+ readonly envelopes: readonly PostedEnvelope[];
23
+ };
24
+ export type PostOutcome = {
25
+ readonly at: number;
26
+ readonly failure: string | null;
27
+ };
28
+ export declare const postEnvelopesEffect = "phone_post_envelopes";
29
+ export declare const deviceRemoved = "device-removed";
30
+ export declare class PhoneDelivery extends Actor {
31
+ static readonly actorType = "PhoneDelivery";
32
+ deliveries: DeviceDelivery[];
33
+ closedAt: number | null;
34
+ promptDigest: string | null;
35
+ offer(device: DeviceEnvelopes & {
36
+ readonly promptDigest: string;
37
+ }): boolean;
38
+ close({ devices, at, }: {
39
+ readonly devices: readonly DeviceEnvelopes[];
40
+ readonly at: number;
41
+ }): boolean;
42
+ posted({ arguments: request, result, }: EffectSuccessPayload<{
43
+ deviceId: string;
44
+ topic: DeliveryTopic;
45
+ }, PostOutcome>): void;
46
+ retry(key: {
47
+ readonly deviceId: string;
48
+ readonly topic: DeliveryTopic;
49
+ }): void;
50
+ retire(): void;
51
+ }
@@ -0,0 +1,139 @@
1
+ import { Actor } from "solid-objects";
2
+ import { deliveryBackoffMilliseconds } from "../delivery-backoff.js";
3
+ import { outboxIdentity, PhoneOutbox } from "./phone-outbox.js";
4
+ export const postEnvelopesEffect = "phone_post_envelopes";
5
+ export const deviceRemoved = "device-removed";
6
+ const attemptLimit = deliveryBackoffMilliseconds.length;
7
+ const retentionMilliseconds = 7 * 86_400_000;
8
+ function same(item, key) {
9
+ return item.deviceId === key.deviceId && item.topic === key.topic;
10
+ }
11
+ function replaced(deliveries, delivery) {
12
+ return deliveries.map((item) => (same(item, delivery) ? delivery : item));
13
+ }
14
+ function ended(delivery) {
15
+ if (delivery.state === "sent" || delivery.state === "cancelled")
16
+ return true;
17
+ return (delivery.state === "failed" &&
18
+ (delivery.attempts >= attemptLimit ||
19
+ delivery.lastFailure === deviceRemoved));
20
+ }
21
+ function started(topic, device) {
22
+ return {
23
+ ...device,
24
+ topic,
25
+ state: "pending",
26
+ attempts: 1,
27
+ lastAttemptAt: null,
28
+ lastFailure: null,
29
+ };
30
+ }
31
+ function settled(delivery, result) {
32
+ if (result.failure === null)
33
+ return {
34
+ ...delivery,
35
+ state: "sent",
36
+ lastAttemptAt: result.at,
37
+ lastFailure: null,
38
+ envelopes: [],
39
+ };
40
+ return {
41
+ ...delivery,
42
+ state: "failed",
43
+ lastAttemptAt: result.at,
44
+ lastFailure: result.failure,
45
+ };
46
+ }
47
+ function afterClose(delivery, closedAt) {
48
+ const stopped = delivery.topic === "decision" &&
49
+ delivery.state === "failed" &&
50
+ closedAt !== null;
51
+ return stopped ? { ...delivery, state: "cancelled" } : delivery;
52
+ }
53
+ export class PhoneDelivery extends Actor {
54
+ static actorType = "PhoneDelivery";
55
+ deliveries = [];
56
+ closedAt = null;
57
+ promptDigest = null;
58
+ offer(device) {
59
+ const known = this.deliveries.some((item) => same(item, { deviceId: device.deviceId, topic: "decision" }));
60
+ if (known || this.closedAt !== null)
61
+ return false;
62
+ const delivery = started("decision", {
63
+ deviceId: device.deviceId,
64
+ envelopes: device.envelopes,
65
+ });
66
+ this.promptDigest ??= device.promptDigest;
67
+ this.deliveries = [...this.deliveries, delivery];
68
+ this.emit(postEnvelopesEffect, {
69
+ arguments: {
70
+ deviceId: delivery.deviceId,
71
+ envelopes: delivery.envelopes,
72
+ topic: delivery.topic,
73
+ },
74
+ onSuccess: "posted",
75
+ });
76
+ return true;
77
+ }
78
+ close({ devices, at, }) {
79
+ if (this.closedAt !== null)
80
+ return false;
81
+ this.closedAt = at;
82
+ const closing = devices.map((device) => started("closed", device));
83
+ this.deliveries = [
84
+ ...this.deliveries.map((item) => item.state === "failed" ? { ...item, state: "cancelled" } : item),
85
+ ...closing,
86
+ ];
87
+ for (const device of devices)
88
+ this.emit(postEnvelopesEffect, {
89
+ arguments: { ...device, topic: "closed" },
90
+ onSuccess: "posted",
91
+ });
92
+ if (closing.length === 0)
93
+ this.schedule({ at: new Date(at + retentionMilliseconds) }).retire();
94
+ return true;
95
+ }
96
+ posted({ arguments: request, result, }) {
97
+ const delivery = this.deliveries.find((item) => same(item, request) && item.state === "pending");
98
+ if (delivery === undefined)
99
+ return;
100
+ const updated = afterClose(settled(delivery, result), this.closedAt);
101
+ this.deliveries = replaced(this.deliveries, updated);
102
+ if (!ended(updated)) {
103
+ const wait = deliveryBackoffMilliseconds[updated.attempts] ?? 0;
104
+ this.schedule({
105
+ at: new Date(result.at + wait),
106
+ key: `${updated.topic}:${updated.deviceId}`,
107
+ }).retry({ deviceId: updated.deviceId, topic: updated.topic });
108
+ return;
109
+ }
110
+ const closing = this.deliveries.filter((item) => item.topic === "closed");
111
+ if (updated.topic === "closed" && closing.every(ended))
112
+ this.schedule({
113
+ at: new Date(result.at + retentionMilliseconds),
114
+ }).retire();
115
+ }
116
+ retry(key) {
117
+ const delivery = this.deliveries.find((item) => same(item, key) && item.state === "failed");
118
+ if (delivery === undefined || ended(delivery))
119
+ return;
120
+ this.deliveries = replaced(this.deliveries, {
121
+ ...delivery,
122
+ state: "pending",
123
+ attempts: delivery.attempts + 1,
124
+ });
125
+ this.emit(postEnvelopesEffect, {
126
+ arguments: {
127
+ deviceId: delivery.deviceId,
128
+ topic: delivery.topic,
129
+ envelopes: delivery.envelopes,
130
+ },
131
+ onSuccess: "posted",
132
+ });
133
+ }
134
+ retire() {
135
+ this.sendTo(PhoneOutbox.ref(outboxIdentity)).retire({
136
+ decisionId: this.actorId,
137
+ });
138
+ }
139
+ }
@@ -0,0 +1,36 @@
1
+ import { Actor } from "solid-objects";
2
+ export type PhoneDevice = {
3
+ readonly deviceId: string;
4
+ readonly name: string;
5
+ readonly platform: string;
6
+ readonly agreementPublicKey: string;
7
+ readonly answerPublicKey: string;
8
+ readonly approvalPublicKey: string;
9
+ readonly pairedAt: string;
10
+ readonly state: "checking" | "connected";
11
+ readonly removedAt: string | null;
12
+ };
13
+ export declare class PhoneDevices extends Actor {
14
+ static readonly actorType = "PhoneDevices";
15
+ devices: PhoneDevice[];
16
+ sequences: {
17
+ readonly deviceId: string;
18
+ readonly sequence: number;
19
+ }[];
20
+ add({ device }: {
21
+ readonly device: PhoneDevice;
22
+ }): PhoneDevice;
23
+ nextSequence({ deviceId }: {
24
+ readonly deviceId: string;
25
+ }): number;
26
+ connect({ deviceId }: {
27
+ readonly deviceId: string;
28
+ }): boolean;
29
+ remove({ deviceId, removedAt, }: {
30
+ readonly deviceId: string;
31
+ readonly removedAt: string;
32
+ }): boolean;
33
+ removeAll({ removedAt }: {
34
+ readonly removedAt: string;
35
+ }): number;
36
+ }
@@ -0,0 +1,46 @@
1
+ import { Actor } from "solid-objects";
2
+ export class PhoneDevices extends Actor {
3
+ static actorType = "PhoneDevices";
4
+ devices = [];
5
+ sequences = [];
6
+ add({ device }) {
7
+ const removed = this.devices.some((item) => item.deviceId === device.deviceId && item.removedAt !== null);
8
+ if (removed)
9
+ this.reject("pairing_device_revoked", {
10
+ message: "This phone was removed. Connect it again as a new phone.",
11
+ });
12
+ this.devices = [
13
+ ...this.devices.filter((item) => item.deviceId !== device.deviceId),
14
+ device,
15
+ ];
16
+ return device;
17
+ }
18
+ nextSequence({ deviceId }) {
19
+ const sequence = (this.sequences.find((item) => item.deviceId === deviceId)?.sequence ??
20
+ 0) + 1;
21
+ this.sequences = [
22
+ ...this.sequences.filter((item) => item.deviceId !== deviceId),
23
+ { deviceId, sequence },
24
+ ];
25
+ return sequence;
26
+ }
27
+ connect({ deviceId }) {
28
+ const found = this.devices.some((item) => item.deviceId === deviceId && item.removedAt === null);
29
+ this.devices = this.devices.map((item) => item.deviceId === deviceId && item.removedAt === null
30
+ ? { ...item, state: "connected" }
31
+ : item);
32
+ return found;
33
+ }
34
+ remove({ deviceId, removedAt, }) {
35
+ const found = this.devices.some((item) => item.deviceId === deviceId && item.removedAt === null);
36
+ this.devices = this.devices.map((item) => item.deviceId === deviceId && item.removedAt === null
37
+ ? { ...item, removedAt }
38
+ : item);
39
+ return found;
40
+ }
41
+ removeAll({ removedAt }) {
42
+ const count = this.devices.filter((item) => item.removedAt === null).length;
43
+ this.devices = this.devices.map((item) => item.removedAt === null ? { ...item, removedAt } : item);
44
+ return count;
45
+ }
46
+ }
@@ -0,0 +1,18 @@
1
+ import { Actor } from "solid-objects";
2
+ import type { PostedEnvelope } from "./phone-delivery.ts";
3
+ export type InboxWork = {
4
+ readonly answerId: string;
5
+ readonly sequence: number;
6
+ readonly deviceId: string;
7
+ readonly envelope: PostedEnvelope | null;
8
+ };
9
+ export declare class PhoneInbox extends Actor {
10
+ static readonly actorType = "PhoneInbox";
11
+ cursor: number;
12
+ work: InboxWork[];
13
+ record(work: InboxWork): InboxWork;
14
+ complete({ answerId, sequence, }: {
15
+ readonly answerId: string;
16
+ readonly sequence: number;
17
+ }): void;
18
+ }
@@ -0,0 +1,17 @@
1
+ import { Actor } from "solid-objects";
2
+ export class PhoneInbox extends Actor {
3
+ static actorType = "PhoneInbox";
4
+ cursor = 0;
5
+ work = [];
6
+ record(work) {
7
+ const known = this.work.find((item) => item.answerId === work.answerId);
8
+ if (known !== undefined)
9
+ return known;
10
+ this.work = [...this.work, work];
11
+ return work;
12
+ }
13
+ complete({ answerId, sequence, }) {
14
+ this.work = this.work.filter((item) => item.answerId !== answerId);
15
+ this.cursor = Math.max(this.cursor, sequence);
16
+ }
17
+ }
@@ -0,0 +1,18 @@
1
+ import type { Fields, Reply } from "../protocol.ts";
2
+ import type { PairingSessions } from "./pairing-session.ts";
3
+ import { type RegistrationContext } from "./relay-registration.ts";
4
+ import type { RelayStore } from "./relay-store.ts";
5
+ export interface OperationState {
6
+ readonly context: RegistrationContext;
7
+ readonly store: RelayStore;
8
+ readonly sessions: PairingSessions;
9
+ readonly registration: {
10
+ lost: boolean;
11
+ };
12
+ }
13
+ export declare function result(fields: Fields): Reply;
14
+ export declare function refused(code: string, detail: string): Reply;
15
+ export declare function pairAccept(relay: OperationState, parameters: Fields): Promise<Reply>;
16
+ export declare function pairDeny(relay: OperationState, parameters: Fields): Promise<Reply>;
17
+ export declare function phoneRemove(relay: OperationState, parameters: Fields): Promise<Reply>;
18
+ export declare function phoneReset(relay: OperationState): Promise<Reply>;
@@ -0,0 +1,121 @@
1
+ import { rm } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { signedReceipt } from "./pairing-checks.js";
4
+ import { denied } from "./pairing-follow.js";
5
+ import { relayRequest } from "./relay-http.js";
6
+ import { readLink } from "./relay-registration.js";
7
+ export function result(fields) {
8
+ return { kind: "result", result: fields };
9
+ }
10
+ export function refused(code, detail) {
11
+ return { kind: "error", error: { code, detail } };
12
+ }
13
+ function requestedPairing(relay) {
14
+ const open = relay.sessions.current();
15
+ return open?.stage === "requested" && open.request !== undefined
16
+ ? open
17
+ : undefined;
18
+ }
19
+ const noRequest = refused("pairing-not-requested", "No phone waits for an answer. Click Connect a phone and scan the code again.");
20
+ const changed = refused("pairing-changed", "The pairing changed. Check the name and the code on the watch page again.");
21
+ const acceptances = new WeakMap();
22
+ export function pairAccept(relay, parameters) {
23
+ const open = requestedPairing(relay);
24
+ if (open === undefined)
25
+ return Promise.resolve(noRequest);
26
+ if (parameters["pairingId"] !== open.pairingId)
27
+ return Promise.resolve(changed);
28
+ const running = acceptances.get(open);
29
+ if (running !== undefined)
30
+ return running;
31
+ const accepting = accepted(relay, open).finally(() => acceptances.delete(open));
32
+ acceptances.set(open, accepting);
33
+ return accepting;
34
+ }
35
+ async function accepted(relay, open) {
36
+ const { request } = open;
37
+ if (request === undefined)
38
+ return noRequest;
39
+ const pairedAt = new Date().toISOString();
40
+ const rejection = await relay.store.add({
41
+ deviceId: request.deviceId,
42
+ name: request.name,
43
+ platform: request.platform,
44
+ agreementPublicKey: request.agreementPublicKey,
45
+ answerPublicKey: request.answerPublicKey,
46
+ approvalPublicKey: request.approvalPublicKey,
47
+ pairedAt,
48
+ state: "checking",
49
+ removedAt: null,
50
+ });
51
+ if (rejection !== undefined) {
52
+ await denied(open);
53
+ relay.sessions.end(open, rejection);
54
+ return refused(rejection, "This phone was removed. Connect it as a new phone.");
55
+ }
56
+ const { receipt, nonce } = signedReceipt(open.registration.keys, {
57
+ daemonId: open.registration.link.daemonId,
58
+ deviceId: request.deviceId,
59
+ pairedAt,
60
+ });
61
+ const reply = await relayRequest(open.registration.link.origin, {
62
+ method: "POST",
63
+ path: `/api/v1/relay/pairings/${open.pairingId}/receipt`,
64
+ token: open.registration.link.token,
65
+ body: { receipt },
66
+ });
67
+ if (reply.status !== 200)
68
+ return refused("receipt-failed", `cloving.ai answered ${String(reply.status)} to the receipt.`);
69
+ open.nonce = nonce;
70
+ open.stage = "checking";
71
+ return result({ deviceId: request.deviceId, state: "checking" });
72
+ }
73
+ export async function pairDeny(relay, parameters) {
74
+ const open = requestedPairing(relay);
75
+ if (open === undefined)
76
+ return noRequest;
77
+ if (parameters["pairingId"] !== open.pairingId)
78
+ return changed;
79
+ await denied(open);
80
+ relay.sessions.end(open, null);
81
+ return result({ pairing: null });
82
+ }
83
+ function warning(status, subject) {
84
+ if (status >= 200 && status < 300)
85
+ return null;
86
+ return `cloving.ai did not remove ${subject} (answer ${String(status)}). This computer keeps it removed.`;
87
+ }
88
+ async function serverCall(relay, call) {
89
+ const link = await readLink(relay.context.phoneDirectory);
90
+ if (link === undefined)
91
+ return null;
92
+ const reply = await relayRequest(link.origin, {
93
+ method: call.method,
94
+ path: call.path,
95
+ token: link.token,
96
+ }).catch(() => ({ status: 0, body: null }));
97
+ return warning(reply.status, call.subject);
98
+ }
99
+ export async function phoneRemove(relay, parameters) {
100
+ const deviceId = parameters["deviceId"];
101
+ if (typeof deviceId !== "string" || !(await relay.store.remove(deviceId)))
102
+ return refused("phone-unknown", "No connected phone has this device ID.");
103
+ const notice = await serverCall(relay, {
104
+ method: "DELETE",
105
+ path: `/api/v1/relay/devices/${encodeURIComponent(deviceId)}`,
106
+ subject: "the phone",
107
+ });
108
+ return result({ deviceId, removed: true, warning: notice });
109
+ }
110
+ export async function phoneReset(relay) {
111
+ relay.sessions.cancel();
112
+ const notice = await serverCall(relay, {
113
+ method: "DELETE",
114
+ path: "/api/v1/relay/daemon",
115
+ subject: "this computer",
116
+ });
117
+ const removed = await relay.store.removeAll();
118
+ for (const file of ["link.json", "daemon-keys.json"])
119
+ await rm(join(relay.context.phoneDirectory, file), { force: true });
120
+ return result({ removed, warning: notice });
121
+ }