toolroll 0.5.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 (626) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1113 -0
  3. package/THIRD_PARTY_NOTICES.md +449 -0
  4. package/dist/accent-colors.d.ts +48 -0
  5. package/dist/accent-colors.js +122 -0
  6. package/dist/action-ledger.d.ts +26 -0
  7. package/dist/action-ledger.js +75 -0
  8. package/dist/agent-fence.d.ts +42 -0
  9. package/dist/agent-fence.js +183 -0
  10. package/dist/agentconfig.d.ts +216 -0
  11. package/dist/agentconfig.js +454 -0
  12. package/dist/api-tokens.d.ts +16 -0
  13. package/dist/api-tokens.js +27 -0
  14. package/dist/approval-policy.d.ts +69 -0
  15. package/dist/approval-policy.js +137 -0
  16. package/dist/approval-rules-ui.d.ts +25 -0
  17. package/dist/approval-rules-ui.js +39 -0
  18. package/dist/assignment-adapters.d.ts +180 -0
  19. package/dist/assignment-adapters.js +239 -0
  20. package/dist/assignment-brief.d.ts +71 -0
  21. package/dist/assignment-brief.js +129 -0
  22. package/dist/assignment-delivery.d.ts +56 -0
  23. package/dist/assignment-delivery.js +160 -0
  24. package/dist/assignment-presentation.d.ts +26 -0
  25. package/dist/assignment-presentation.js +80 -0
  26. package/dist/assignment-status.d.ts +62 -0
  27. package/dist/assignment-status.js +152 -0
  28. package/dist/assignment-ui.d.ts +71 -0
  29. package/dist/assignment-ui.js +103 -0
  30. package/dist/assignment.d.ts +222 -0
  31. package/dist/assignment.js +399 -0
  32. package/dist/attest.d.ts +56 -0
  33. package/dist/attest.js +153 -0
  34. package/dist/backend.d.ts +97 -0
  35. package/dist/backend.js +166 -0
  36. package/dist/backup-ui.d.ts +22 -0
  37. package/dist/backup-ui.js +59 -0
  38. package/dist/backup.d.ts +84 -0
  39. package/dist/backup.js +421 -0
  40. package/dist/beads.d.ts +34 -0
  41. package/dist/beads.js +135 -0
  42. package/dist/bin.d.ts +2 -0
  43. package/dist/bin.js +22 -0
  44. package/dist/board.d.ts +150 -0
  45. package/dist/board.js +210 -0
  46. package/dist/boot-identity.d.ts +63 -0
  47. package/dist/boot-identity.js +99 -0
  48. package/dist/browser/THIRD_PARTY_NOTICES.txt +2295 -0
  49. package/dist/browser/workspace.css +4 -0
  50. package/dist/browser/workspace.js +225 -0
  51. package/dist/browser-crew.d.ts +11 -0
  52. package/dist/browser-crew.js +50 -0
  53. package/dist/browser-shell.d.ts +8 -0
  54. package/dist/browser-shell.js +60 -0
  55. package/dist/browser-workspace.d.ts +780 -0
  56. package/dist/browser-workspace.js +47 -0
  57. package/dist/budget-alerts.d.ts +19 -0
  58. package/dist/budget-alerts.js +46 -0
  59. package/dist/builder.d.ts +370 -0
  60. package/dist/builder.js +3262 -0
  61. package/dist/capscan.d.ts +35 -0
  62. package/dist/capscan.js +113 -0
  63. package/dist/chat-acceptance.d.ts +71 -0
  64. package/dist/chat-acceptance.js +120 -0
  65. package/dist/chat-actions.d.ts +265 -0
  66. package/dist/chat-actions.js +1273 -0
  67. package/dist/chat-channel.d.ts +109 -0
  68. package/dist/chat-channel.js +652 -0
  69. package/dist/chat-continuity.d.ts +32 -0
  70. package/dist/chat-continuity.js +317 -0
  71. package/dist/chat-controls.d.ts +113 -0
  72. package/dist/chat-controls.js +53 -0
  73. package/dist/chat-delivery-state.d.ts +159 -0
  74. package/dist/chat-delivery-state.js +309 -0
  75. package/dist/chat-delivery.d.ts +38 -0
  76. package/dist/chat-delivery.js +693 -0
  77. package/dist/chat-display.d.ts +1 -0
  78. package/dist/chat-display.js +18 -0
  79. package/dist/chat-evidence.d.ts +123 -0
  80. package/dist/chat-evidence.js +207 -0
  81. package/dist/chat-flow.d.ts +44 -0
  82. package/dist/chat-flow.js +215 -0
  83. package/dist/chat-inbox.d.ts +57 -0
  84. package/dist/chat-inbox.js +123 -0
  85. package/dist/chat-polish.d.ts +16 -0
  86. package/dist/chat-polish.js +135 -0
  87. package/dist/chat-review.d.ts +46 -0
  88. package/dist/chat-review.js +98 -0
  89. package/dist/chat-rooms.d.ts +123 -0
  90. package/dist/chat-rooms.js +216 -0
  91. package/dist/chat-task-actions.d.ts +44 -0
  92. package/dist/chat-task-actions.js +79 -0
  93. package/dist/check-progress.d.ts +51 -0
  94. package/dist/check-progress.js +290 -0
  95. package/dist/child-database.d.ts +12 -0
  96. package/dist/child-database.js +30 -0
  97. package/dist/claim.d.ts +688 -0
  98. package/dist/claim.js +1740 -0
  99. package/dist/cli.d.ts +137 -0
  100. package/dist/cli.js +1461 -0
  101. package/dist/codex-limits.d.ts +15 -0
  102. package/dist/codex-limits.js +145 -0
  103. package/dist/coding-context.d.ts +55 -0
  104. package/dist/coding-context.js +92 -0
  105. package/dist/coding-handoff.d.ts +57 -0
  106. package/dist/coding-handoff.js +277 -0
  107. package/dist/coding-provider.d.ts +72 -0
  108. package/dist/coding-provider.js +424 -0
  109. package/dist/coding-shipping-ui.d.ts +4 -0
  110. package/dist/coding-shipping-ui.js +12 -0
  111. package/dist/coding-types.d.ts +62 -0
  112. package/dist/coding-types.js +1 -0
  113. package/dist/coding-ui.d.ts +17 -0
  114. package/dist/coding-ui.js +364 -0
  115. package/dist/coding-update.d.ts +15 -0
  116. package/dist/coding-update.js +149 -0
  117. package/dist/coding-workspace.d.ts +124 -0
  118. package/dist/coding-workspace.js +768 -0
  119. package/dist/container-state.d.ts +5 -0
  120. package/dist/container-state.js +62 -0
  121. package/dist/containment.d.ts +197 -0
  122. package/dist/containment.js +559 -0
  123. package/dist/contest.d.ts +318 -0
  124. package/dist/contest.js +753 -0
  125. package/dist/control-setup.d.ts +35 -0
  126. package/dist/control-setup.js +40 -0
  127. package/dist/control-ui.d.ts +29 -0
  128. package/dist/control-ui.js +53 -0
  129. package/dist/controller-service.d.ts +1 -0
  130. package/dist/controller-service.js +19 -0
  131. package/dist/controller-supervisor.d.ts +30 -0
  132. package/dist/controller-supervisor.js +104 -0
  133. package/dist/converse.d.ts +304 -0
  134. package/dist/converse.js +849 -0
  135. package/dist/coordinator-proposals.d.ts +29 -0
  136. package/dist/coordinator-proposals.js +137 -0
  137. package/dist/coordinator.d.ts +186 -0
  138. package/dist/coordinator.js +447 -0
  139. package/dist/credentials-ui.d.ts +25 -0
  140. package/dist/credentials-ui.js +48 -0
  141. package/dist/daemon.d.ts +209 -0
  142. package/dist/daemon.js +604 -0
  143. package/dist/decision.d.ts +120 -0
  144. package/dist/decision.js +388 -0
  145. package/dist/demo.d.ts +55 -0
  146. package/dist/demo.js +992 -0
  147. package/dist/desktop-access.d.ts +28 -0
  148. package/dist/desktop-access.js +88 -0
  149. package/dist/desktop-bundle.d.ts +16 -0
  150. package/dist/desktop-bundle.js +74 -0
  151. package/dist/desktop-host.d.ts +54 -0
  152. package/dist/desktop-host.js +508 -0
  153. package/dist/desktop-update-gate.d.ts +12 -0
  154. package/dist/desktop-update-gate.js +91 -0
  155. package/dist/desktop-update-recovery.d.ts +14 -0
  156. package/dist/desktop-update-recovery.js +243 -0
  157. package/dist/desktop-update.d.ts +110 -0
  158. package/dist/desktop-update.js +740 -0
  159. package/dist/discord-api.d.ts +20 -0
  160. package/dist/discord-api.js +138 -0
  161. package/dist/discord-chat.d.ts +16 -0
  162. package/dist/discord-chat.js +381 -0
  163. package/dist/discord-settings.d.ts +7 -0
  164. package/dist/discord-settings.js +75 -0
  165. package/dist/discord.d.ts +10 -0
  166. package/dist/discord.js +169 -0
  167. package/dist/discover.d.ts +75 -0
  168. package/dist/discover.js +150 -0
  169. package/dist/dispatch.d.ts +100 -0
  170. package/dist/dispatch.js +311 -0
  171. package/dist/dispose.d.ts +161 -0
  172. package/dist/dispose.js +803 -0
  173. package/dist/email-settings.d.ts +34 -0
  174. package/dist/email-settings.js +50 -0
  175. package/dist/envelope.d.ts +47 -0
  176. package/dist/envelope.js +105 -0
  177. package/dist/evidence-pack.d.ts +185 -0
  178. package/dist/evidence-pack.js +258 -0
  179. package/dist/evidence.d.ts +345 -0
  180. package/dist/evidence.js +782 -0
  181. package/dist/exec.d.ts +285 -0
  182. package/dist/exec.js +1841 -0
  183. package/dist/exhaustion.d.ts +85 -0
  184. package/dist/exhaustion.js +141 -0
  185. package/dist/export-ui.d.ts +4 -0
  186. package/dist/export-ui.js +17 -0
  187. package/dist/export.d.ts +48 -0
  188. package/dist/export.js +305 -0
  189. package/dist/flow-actions.d.ts +77 -0
  190. package/dist/flow-actions.js +203 -0
  191. package/dist/flow-code.d.ts +50 -0
  192. package/dist/flow-code.js +159 -0
  193. package/dist/flow-draft.d.ts +38 -0
  194. package/dist/flow-draft.js +80 -0
  195. package/dist/flow-engine.d.ts +66 -0
  196. package/dist/flow-engine.js +416 -0
  197. package/dist/flow-insights.d.ts +95 -0
  198. package/dist/flow-insights.js +149 -0
  199. package/dist/flow-live.d.ts +24 -0
  200. package/dist/flow-live.js +150 -0
  201. package/dist/flow-people.d.ts +24 -0
  202. package/dist/flow-people.js +91 -0
  203. package/dist/flow-replies.d.ts +48 -0
  204. package/dist/flow-replies.js +113 -0
  205. package/dist/flow-scripts.d.ts +38 -0
  206. package/dist/flow-scripts.js +73 -0
  207. package/dist/flow-secrets.d.ts +13 -0
  208. package/dist/flow-secrets.js +45 -0
  209. package/dist/flow-sort.d.ts +82 -0
  210. package/dist/flow-sort.js +153 -0
  211. package/dist/flow-steps.d.ts +40 -0
  212. package/dist/flow-steps.js +408 -0
  213. package/dist/flow-triggers.d.ts +244 -0
  214. package/dist/flow-triggers.js +959 -0
  215. package/dist/flows-ui.d.ts +20 -0
  216. package/dist/flows-ui.js +175 -0
  217. package/dist/flows.d.ts +229 -0
  218. package/dist/flows.js +968 -0
  219. package/dist/fonts.d.ts +19 -0
  220. package/dist/fonts.js +19 -0
  221. package/dist/gaps.d.ts +35 -0
  222. package/dist/gaps.js +101 -0
  223. package/dist/gate-failure.d.ts +40 -0
  224. package/dist/gate-failure.js +66 -0
  225. package/dist/git.d.ts +54 -0
  226. package/dist/git.js +94 -0
  227. package/dist/google-mail.d.ts +58 -0
  228. package/dist/google-mail.js +151 -0
  229. package/dist/grant.d.ts +136 -0
  230. package/dist/grant.js +238 -0
  231. package/dist/graph.d.ts +164 -0
  232. package/dist/graph.js +383 -0
  233. package/dist/guides.d.ts +22 -0
  234. package/dist/guides.js +363 -0
  235. package/dist/held.d.ts +150 -0
  236. package/dist/held.js +799 -0
  237. package/dist/invoke.d.ts +112 -0
  238. package/dist/invoke.js +780 -0
  239. package/dist/issues.d.ts +41 -0
  240. package/dist/issues.js +131 -0
  241. package/dist/job-object-helper.ps1 +252 -0
  242. package/dist/jsonl-discriminants.d.ts +11 -0
  243. package/dist/jsonl-discriminants.js +93 -0
  244. package/dist/keys.d.ts +104 -0
  245. package/dist/keys.js +229 -0
  246. package/dist/kits-ui.d.ts +17 -0
  247. package/dist/kits-ui.js +46 -0
  248. package/dist/kits.d.ts +86 -0
  249. package/dist/kits.js +215 -0
  250. package/dist/knowledge-cli.d.ts +101 -0
  251. package/dist/knowledge-cli.js +92 -0
  252. package/dist/knowledge-ui.d.ts +24 -0
  253. package/dist/knowledge-ui.js +40 -0
  254. package/dist/lead-context.d.ts +8 -0
  255. package/dist/lead-context.js +56 -0
  256. package/dist/lead-follow.d.ts +22 -0
  257. package/dist/lead-follow.js +167 -0
  258. package/dist/lead-status.d.ts +75 -0
  259. package/dist/lead-status.js +285 -0
  260. package/dist/ledger-chain.d.ts +46 -0
  261. package/dist/ledger-chain.js +187 -0
  262. package/dist/ledger-csv.d.ts +4 -0
  263. package/dist/ledger-csv.js +11 -0
  264. package/dist/ledger-view.d.ts +24 -0
  265. package/dist/ledger-view.js +58 -0
  266. package/dist/limits-ui.d.ts +26 -0
  267. package/dist/limits-ui.js +67 -0
  268. package/dist/link.d.ts +72 -0
  269. package/dist/link.js +217 -0
  270. package/dist/live.d.ts +96 -0
  271. package/dist/live.js +366 -0
  272. package/dist/liveness.d.ts +30 -0
  273. package/dist/liveness.js +42 -0
  274. package/dist/log.d.ts +7 -0
  275. package/dist/log.js +25 -0
  276. package/dist/mailbox.d.ts +57 -0
  277. package/dist/mailbox.js +150 -0
  278. package/dist/maintenance.d.ts +11 -0
  279. package/dist/maintenance.js +35 -0
  280. package/dist/mate-cli.d.ts +52 -0
  281. package/dist/mate-cli.js +345 -0
  282. package/dist/mate-contract.d.ts +10 -0
  283. package/dist/mate-contract.js +30 -0
  284. package/dist/mate-doors.d.ts +69 -0
  285. package/dist/mate-doors.js +548 -0
  286. package/dist/mate-progress.d.ts +29 -0
  287. package/dist/mate-progress.js +121 -0
  288. package/dist/mate-tools.d.ts +94 -0
  289. package/dist/mate-tools.js +1878 -0
  290. package/dist/mate.d.ts +92 -0
  291. package/dist/mate.js +435 -0
  292. package/dist/mcp-connect.d.ts +103 -0
  293. package/dist/mcp-connect.js +252 -0
  294. package/dist/mcp.d.ts +27 -0
  295. package/dist/mcp.js +651 -0
  296. package/dist/memory-cli.d.ts +466 -0
  297. package/dist/memory-cli.js +111 -0
  298. package/dist/memory-pass.d.ts +160 -0
  299. package/dist/memory-pass.js +409 -0
  300. package/dist/metrics.d.ts +3 -0
  301. package/dist/metrics.js +56 -0
  302. package/dist/mobile-viewport.d.ts +3 -0
  303. package/dist/mobile-viewport.js +37 -0
  304. package/dist/model-catalog.d.ts +118 -0
  305. package/dist/model-catalog.js +376 -0
  306. package/dist/models-cli.d.ts +102 -0
  307. package/dist/models-cli.js +66 -0
  308. package/dist/models-ui.d.ts +34 -0
  309. package/dist/models-ui.js +68 -0
  310. package/dist/modes.d.ts +80 -0
  311. package/dist/modes.js +182 -0
  312. package/dist/monitoring-settings.d.ts +44 -0
  313. package/dist/monitoring-settings.js +173 -0
  314. package/dist/monitoring-ui.d.ts +20 -0
  315. package/dist/monitoring-ui.js +51 -0
  316. package/dist/monitoring.d.ts +34 -0
  317. package/dist/monitoring.js +221 -0
  318. package/dist/names.d.ts +67 -0
  319. package/dist/names.js +102 -0
  320. package/dist/observations.d.ts +40 -0
  321. package/dist/observations.js +211 -0
  322. package/dist/oidc.d.ts +71 -0
  323. package/dist/oidc.js +182 -0
  324. package/dist/onboard.d.ts +108 -0
  325. package/dist/onboard.js +325 -0
  326. package/dist/openrouter-models.d.ts +30 -0
  327. package/dist/openrouter-models.js +120 -0
  328. package/dist/operate.d.ts +118 -0
  329. package/dist/operate.js +11321 -0
  330. package/dist/peek-cli.d.ts +97 -0
  331. package/dist/peek-cli.js +215 -0
  332. package/dist/peek.d.ts +134 -0
  333. package/dist/peek.js +578 -0
  334. package/dist/phase-routing.d.ts +307 -0
  335. package/dist/phase-routing.js +658 -0
  336. package/dist/plan-auto.d.ts +14 -0
  337. package/dist/plan-auto.js +92 -0
  338. package/dist/plan.d.ts +186 -0
  339. package/dist/plan.js +401 -0
  340. package/dist/planner-source.d.ts +251 -0
  341. package/dist/planner-source.js +460 -0
  342. package/dist/planner.d.ts +75 -0
  343. package/dist/planner.js +992 -0
  344. package/dist/policy-ui.d.ts +20 -0
  345. package/dist/policy-ui.js +41 -0
  346. package/dist/policy.d.ts +111 -0
  347. package/dist/policy.js +231 -0
  348. package/dist/prepared-evidence.d.ts +16 -0
  349. package/dist/prepared-evidence.js +96 -0
  350. package/dist/pricing.d.ts +45 -0
  351. package/dist/pricing.js +77 -0
  352. package/dist/principal.d.ts +42 -0
  353. package/dist/principal.js +82 -0
  354. package/dist/probe.d.ts +46 -0
  355. package/dist/probe.js +79 -0
  356. package/dist/process-custody.d.ts +17 -0
  357. package/dist/process-custody.js +91 -0
  358. package/dist/process-liveness.d.ts +10 -0
  359. package/dist/process-liveness.js +81 -0
  360. package/dist/process-recovery-anchor.d.ts +75 -0
  361. package/dist/process-recovery-anchor.js +188 -0
  362. package/dist/process-recovery-coalition.d.ts +25 -0
  363. package/dist/process-recovery-coalition.js +105 -0
  364. package/dist/process-recovery-eligibility.d.ts +178 -0
  365. package/dist/process-recovery-eligibility.js +170 -0
  366. package/dist/process-recovery-native.d.ts +172 -0
  367. package/dist/process-recovery-native.js +592 -0
  368. package/dist/process-recovery-provenance.d.ts +163 -0
  369. package/dist/process-recovery-provenance.js +292 -0
  370. package/dist/process-recovery-services.d.ts +56 -0
  371. package/dist/process-recovery-services.js +191 -0
  372. package/dist/process-recovery-settlement.d.ts +32 -0
  373. package/dist/process-recovery-settlement.js +132 -0
  374. package/dist/process-recovery.d.ts +41 -0
  375. package/dist/process-recovery.js +91 -0
  376. package/dist/process-tree.d.ts +41 -0
  377. package/dist/process-tree.js +264 -0
  378. package/dist/project-access.d.ts +14 -0
  379. package/dist/project-access.js +34 -0
  380. package/dist/project-cli.d.ts +18 -0
  381. package/dist/project-cli.js +104 -0
  382. package/dist/project-delete-ui.d.ts +22 -0
  383. package/dist/project-delete-ui.js +48 -0
  384. package/dist/project-delete.d.ts +55 -0
  385. package/dist/project-delete.js +413 -0
  386. package/dist/project-knowledge.d.ts +111 -0
  387. package/dist/project-knowledge.js +241 -0
  388. package/dist/project-learning.d.ts +100 -0
  389. package/dist/project-learning.js +438 -0
  390. package/dist/project-memory.d.ts +81 -0
  391. package/dist/project-memory.js +264 -0
  392. package/dist/project-skills.d.ts +152 -0
  393. package/dist/project-skills.js +660 -0
  394. package/dist/project-tools.d.ts +219 -0
  395. package/dist/project-tools.js +796 -0
  396. package/dist/project.d.ts +61 -0
  397. package/dist/project.js +125 -0
  398. package/dist/prompt.d.ts +22 -0
  399. package/dist/prompt.js +72 -0
  400. package/dist/proof.d.ts +513 -0
  401. package/dist/proof.js +1140 -0
  402. package/dist/proposal.d.ts +82 -0
  403. package/dist/proposal.js +210 -0
  404. package/dist/provider-connection.d.ts +22 -0
  405. package/dist/provider-connection.js +98 -0
  406. package/dist/provider-limits.d.ts +35 -0
  407. package/dist/provider-limits.js +95 -0
  408. package/dist/provider.d.ts +279 -0
  409. package/dist/provider.js +935 -0
  410. package/dist/publish.d.ts +107 -0
  411. package/dist/publish.js +650 -0
  412. package/dist/pulls.d.ts +118 -0
  413. package/dist/pulls.js +240 -0
  414. package/dist/push.d.ts +95 -0
  415. package/dist/push.js +353 -0
  416. package/dist/quality.d.ts +8 -0
  417. package/dist/quality.js +6 -0
  418. package/dist/recipe-ui.d.ts +18 -0
  419. package/dist/recipe-ui.js +152 -0
  420. package/dist/recipes.d.ts +81 -0
  421. package/dist/recipes.js +332 -0
  422. package/dist/remote.d.ts +67 -0
  423. package/dist/remote.js +122 -0
  424. package/dist/render.d.ts +64 -0
  425. package/dist/render.js +587 -0
  426. package/dist/report-summary.d.ts +13 -0
  427. package/dist/report-summary.js +15 -0
  428. package/dist/repos.d.ts +65 -0
  429. package/dist/repos.js +277 -0
  430. package/dist/repository-context-ui.d.ts +2 -0
  431. package/dist/repository-context-ui.js +10 -0
  432. package/dist/repository-context.d.ts +67 -0
  433. package/dist/repository-context.js +376 -0
  434. package/dist/restart-certification.d.ts +134 -0
  435. package/dist/restart-certification.js +226 -0
  436. package/dist/result-actions.d.ts +28 -0
  437. package/dist/result-actions.js +270 -0
  438. package/dist/result-completion.d.ts +18 -0
  439. package/dist/result-completion.js +66 -0
  440. package/dist/result-review.d.ts +213 -0
  441. package/dist/result-review.js +430 -0
  442. package/dist/retention-ui.d.ts +12 -0
  443. package/dist/retention-ui.js +39 -0
  444. package/dist/retention.d.ts +72 -0
  445. package/dist/retention.js +288 -0
  446. package/dist/review-context.d.ts +243 -0
  447. package/dist/review-context.js +1043 -0
  448. package/dist/review-evidence.d.ts +33 -0
  449. package/dist/review-evidence.js +56 -0
  450. package/dist/reviewer.d.ts +97 -0
  451. package/dist/reviewer.js +202 -0
  452. package/dist/routine.d.ts +228 -0
  453. package/dist/routine.js +764 -0
  454. package/dist/runner.d.ts +266 -0
  455. package/dist/runner.js +399 -0
  456. package/dist/scan.d.ts +32 -0
  457. package/dist/scan.js +92 -0
  458. package/dist/scope.d.ts +695 -0
  459. package/dist/scope.js +1176 -0
  460. package/dist/scout-report.d.ts +42 -0
  461. package/dist/scout-report.js +108 -0
  462. package/dist/scout.d.ts +69 -0
  463. package/dist/scout.js +351 -0
  464. package/dist/serve.d.ts +313 -0
  465. package/dist/serve.js +21776 -0
  466. package/dist/session-brief.d.ts +8 -0
  467. package/dist/session-brief.js +24 -0
  468. package/dist/session-cli.d.ts +13 -0
  469. package/dist/session-cli.js +284 -0
  470. package/dist/session-contract.d.ts +183 -0
  471. package/dist/session-contract.js +122 -0
  472. package/dist/session-http.d.ts +19 -0
  473. package/dist/session-http.js +154 -0
  474. package/dist/session-server.d.ts +12 -0
  475. package/dist/session-server.js +37 -0
  476. package/dist/session-service.d.ts +22 -0
  477. package/dist/session-service.js +157 -0
  478. package/dist/setup-guide.d.ts +39 -0
  479. package/dist/setup-guide.js +93 -0
  480. package/dist/sign-in-guard.d.ts +45 -0
  481. package/dist/sign-in-guard.js +74 -0
  482. package/dist/skills-ui.d.ts +11 -0
  483. package/dist/skills-ui.js +58 -0
  484. package/dist/skills.d.ts +97 -0
  485. package/dist/skills.js +276 -0
  486. package/dist/slack-api.d.ts +55 -0
  487. package/dist/slack-api.js +229 -0
  488. package/dist/slack-chat.d.ts +28 -0
  489. package/dist/slack-chat.js +413 -0
  490. package/dist/slack-settings.d.ts +7 -0
  491. package/dist/slack-settings.js +75 -0
  492. package/dist/slack-state.d.ts +13 -0
  493. package/dist/slack-state.js +9 -0
  494. package/dist/slack.d.ts +13 -0
  495. package/dist/slack.js +167 -0
  496. package/dist/spend-ui.d.ts +28 -0
  497. package/dist/spend-ui.js +95 -0
  498. package/dist/spend.d.ts +142 -0
  499. package/dist/spend.js +313 -0
  500. package/dist/sqlite-runtime.d.ts +3 -0
  501. package/dist/sqlite-runtime.js +6 -0
  502. package/dist/sso-settings.d.ts +33 -0
  503. package/dist/sso-settings.js +103 -0
  504. package/dist/sso-ui.d.ts +22 -0
  505. package/dist/sso-ui.js +39 -0
  506. package/dist/storage.d.ts +15 -0
  507. package/dist/storage.js +84 -0
  508. package/dist/store.d.ts +7754 -0
  509. package/dist/store.js +23279 -0
  510. package/dist/structured-output.d.ts +58 -0
  511. package/dist/structured-output.js +103 -0
  512. package/dist/style-asset.d.ts +7 -0
  513. package/dist/style-asset.js +41 -0
  514. package/dist/subscription-chat.d.ts +46 -0
  515. package/dist/subscription-chat.js +275 -0
  516. package/dist/summary.d.ts +33 -0
  517. package/dist/summary.js +71 -0
  518. package/dist/supervisor.mjs +297 -0
  519. package/dist/surface.d.ts +63 -0
  520. package/dist/surface.js +352 -0
  521. package/dist/sync.d.ts +94 -0
  522. package/dist/sync.js +279 -0
  523. package/dist/task-composer.d.ts +13 -0
  524. package/dist/task-composer.js +52 -0
  525. package/dist/task-control.d.ts +165 -0
  526. package/dist/task-control.js +214 -0
  527. package/dist/task-outcome-cli.d.ts +10 -0
  528. package/dist/task-outcome-cli.js +92 -0
  529. package/dist/task-text.d.ts +30 -0
  530. package/dist/task-text.js +41 -0
  531. package/dist/team-cli.d.ts +15 -0
  532. package/dist/team-cli.js +618 -0
  533. package/dist/team-contract.d.ts +92 -0
  534. package/dist/team-contract.js +1 -0
  535. package/dist/team-http.d.ts +17 -0
  536. package/dist/team-http.js +144 -0
  537. package/dist/team-leads.d.ts +81 -0
  538. package/dist/team-leads.js +565 -0
  539. package/dist/team-runtime.d.ts +27 -0
  540. package/dist/team-runtime.js +264 -0
  541. package/dist/team-ui.d.ts +3 -0
  542. package/dist/team-ui.js +9 -0
  543. package/dist/team-updates.d.ts +8 -0
  544. package/dist/team-updates.js +57 -0
  545. package/dist/teammate-admin.d.ts +53 -0
  546. package/dist/teammate-admin.js +111 -0
  547. package/dist/teammate-desk.d.ts +60 -0
  548. package/dist/teammate-desk.js +154 -0
  549. package/dist/teammate-memory.d.ts +64 -0
  550. package/dist/teammate-memory.js +196 -0
  551. package/dist/teammate-question.d.ts +59 -0
  552. package/dist/teammate-question.js +175 -0
  553. package/dist/teammate-tools.d.ts +108 -0
  554. package/dist/teammate-tools.js +271 -0
  555. package/dist/teammate-week.d.ts +68 -0
  556. package/dist/teammate-week.js +132 -0
  557. package/dist/teammate-work.d.ts +76 -0
  558. package/dist/teammate-work.js +280 -0
  559. package/dist/teammates-ui.d.ts +20 -0
  560. package/dist/teammates-ui.js +166 -0
  561. package/dist/teammates.d.ts +180 -0
  562. package/dist/teammates.js +253 -0
  563. package/dist/teams-api.d.ts +33 -0
  564. package/dist/teams-api.js +204 -0
  565. package/dist/teams-chat.d.ts +26 -0
  566. package/dist/teams-chat.js +215 -0
  567. package/dist/teams-settings.d.ts +12 -0
  568. package/dist/teams-settings.js +57 -0
  569. package/dist/teams.d.ts +21 -0
  570. package/dist/teams.js +124 -0
  571. package/dist/telegram-flow.d.ts +49 -0
  572. package/dist/telegram-flow.js +110 -0
  573. package/dist/telegram-mate.d.ts +137 -0
  574. package/dist/telegram-mate.js +707 -0
  575. package/dist/telegram-progress.d.ts +14 -0
  576. package/dist/telegram-progress.js +129 -0
  577. package/dist/telegram-settings.d.ts +9 -0
  578. package/dist/telegram-settings.js +36 -0
  579. package/dist/telegram-status.d.ts +75 -0
  580. package/dist/telegram-status.js +265 -0
  581. package/dist/telegram-team.d.ts +85 -0
  582. package/dist/telegram-team.js +359 -0
  583. package/dist/telegram.d.ts +230 -0
  584. package/dist/telegram.js +1931 -0
  585. package/dist/templates.d.ts +65 -0
  586. package/dist/templates.js +118 -0
  587. package/dist/tool-launcher.d.ts +1 -0
  588. package/dist/tool-launcher.js +101 -0
  589. package/dist/tools-ui.d.ts +30 -0
  590. package/dist/tools-ui.js +40 -0
  591. package/dist/transitions-recipes.d.ts +1 -0
  592. package/dist/transitions-recipes.js +203 -0
  593. package/dist/tree-proof.d.ts +34 -0
  594. package/dist/tree-proof.js +58 -0
  595. package/dist/verification-evidence.d.ts +44 -0
  596. package/dist/verification-evidence.js +238 -0
  597. package/dist/version.d.ts +2 -0
  598. package/dist/version.js +10 -0
  599. package/dist/webhooks.d.ts +91 -0
  600. package/dist/webhooks.js +283 -0
  601. package/dist/work-index.d.ts +68 -0
  602. package/dist/work-index.js +384 -0
  603. package/dist/work-summary.d.ts +57 -0
  604. package/dist/work-summary.js +116 -0
  605. package/dist/workspace-motion.d.ts +4 -0
  606. package/dist/workspace-motion.js +133 -0
  607. package/dist/workspace-revision.d.ts +19 -0
  608. package/dist/workspace-revision.js +92 -0
  609. package/dist/workspace-ui.d.ts +204 -0
  610. package/dist/workspace-ui.js +411 -0
  611. package/dist/worktree-notices.d.ts +13 -0
  612. package/dist/worktree-notices.js +17 -0
  613. package/dist/worktree.d.ts +249 -0
  614. package/dist/worktree.js +720 -0
  615. package/package.json +122 -0
  616. package/scripts/canary-assertions.mjs +42 -0
  617. package/scripts/crash-canary.mjs +253 -0
  618. package/scripts/fixtures/crash-process.mjs +74 -0
  619. package/scripts/fixtures/pilot-scenarios.mjs +85 -0
  620. package/scripts/fixtures/restart-service.mjs +27 -0
  621. package/scripts/launchd-certification.mjs +143 -0
  622. package/scripts/pilot.mjs +121 -0
  623. package/scripts/proof-preflight.mjs +140 -0
  624. package/scripts/provider-canary.mjs +379 -0
  625. package/scripts/recovery-canary.mjs +135 -0
  626. package/scripts/restart-certification.mjs +98 -0
@@ -0,0 +1,266 @@
1
+ /**
2
+ * A machine that may be given work.
3
+ *
4
+ * Auth is here from the first commit, and that is a deliberate ordering rather
5
+ * than diligence for its own sake. A control plane that dispatches work to
6
+ * unauthenticated runners for six months grows six months of code that assumes
7
+ * the caller is whoever it says it is, and retrofitting identity into that is
8
+ * how the retrofit gets a hole in it. It costs almost nothing to do now.
9
+ *
10
+ * **The token is never stored.** Registration mints one, prints it once, and
11
+ * keeps only a hash. There is no command that recovers it, because a control
12
+ * plane able to hand back a runner's credential is one whose database is worth
13
+ * stealing — and because the same rule governs secrets everywhere else in this
14
+ * design: the control plane holds metadata, the runner holds values.
15
+ *
16
+ * Liveness is separate from a lease expiring, and the distinction matters at
17
+ * 3am. A lease expires because time passed; a runner is dead because it
18
+ * stopped saying otherwise. Reaping on the first tells you a task is free
19
+ * again. Reaping on the second tells you a *machine* is gone, and everything
20
+ * it was holding — claims and worktrees alike — needs recovering together.
21
+ */
22
+ import type { Store, Mutation } from "./store.js";
23
+ import { type ProviderId } from "./provider.js";
24
+ import type { ReadinessObservation } from "./phase-routing.js";
25
+ /** A runner's repo binding, in the same canonical form task filing uses —
26
+ * the claim gate compares these by equality, so both sides must resolve
27
+ * through one convention (MCP gateway spec v6, the runner tuple). */
28
+ export declare function canonicalRepos(repos: readonly string[]): string[];
29
+ export type Runner = {
30
+ name: string;
31
+ host: string;
32
+ /** How many tasks it will hold at once. */
33
+ capacity: number;
34
+ repos: string[];
35
+ agents: string[];
36
+ registeredAt: string;
37
+ heartbeatAt: string;
38
+ retiredAt: string | null;
39
+ /** The queue column's theme note (v19) — operator prose, display only. */
40
+ queueNote?: string | null;
41
+ };
42
+ export type Registration = {
43
+ runner: Runner;
44
+ token: string;
45
+ /** What the previous holder of this name was still holding, taken back. */
46
+ reclaimed: Reclaim | null;
47
+ };
48
+ export type AuthResult = {
49
+ ok: true;
50
+ runner: Runner;
51
+ } | {
52
+ ok: false;
53
+ reason: "unknown" | "bad-token" | "retired" | "expired";
54
+ };
55
+ /** v101: a runner's token lasts a year from its registration; registering again (every start does) renews it. */
56
+ export declare const RUNNER_TOKEN_MS: number;
57
+ /**
58
+ * How long a runner may go quiet before it is presumed gone.
59
+ *
60
+ * Comfortably more than any sensible heartbeat interval: declaring a busy
61
+ * machine dead because one check-in was slow costs more than waiting another
62
+ * minute for a machine that really is.
63
+ */
64
+ export declare const DEFAULT_LIVENESS_MS: number;
65
+ export type RegisterOptions = {
66
+ name: string;
67
+ host: string;
68
+ capacity?: number;
69
+ repos?: readonly string[];
70
+ agents?: readonly string[];
71
+ now: Date;
72
+ /** Injected so a test can assert on a token it chose. */
73
+ newToken?: () => string;
74
+ mutation?: Mutation;
75
+ };
76
+ export declare function register(store: Store, options: RegisterOptions): Registration;
77
+ /**
78
+ * Whether this caller is who it says it is.
79
+ *
80
+ * Compared in constant time. The comparison is between hashes rather than
81
+ * secrets, so the practical risk is slight — but a timing-variable equality on
82
+ * a credential path is the kind of thing that gets copied into somewhere it
83
+ * matters, and there is no reason to leave the example lying around.
84
+ */
85
+ export declare function authenticate(store: Store, name: string, token: string): AuthResult;
86
+ /**
87
+ * "I am still here." Authenticated AND ATOMIC (arc 2 findings 25/33): the
88
+ * hash is verified and the heartbeat stamped in one write transaction, so
89
+ * a takeover that rotates the credential between the check and the touch
90
+ * cannot receive a stale incarnation's pulse — after rotation commits, the
91
+ * very next heartbeat refuses, and the caller must treat that as fatal.
92
+ */
93
+ export declare function heartbeat(store: Store, name: string, token: string, now: Date): AuthResult;
94
+ /**
95
+ * Extend a live runner's project binding using that runner's own credential.
96
+ *
97
+ * `up` owns both the credential and the registry watcher, so a project the
98
+ * operator admitted through its console can join without a second operator
99
+ * ceremony or a restart. Authentication and the additive write share one
100
+ * transaction: a replaced process cannot widen its successor's binding.
101
+ */
102
+ export declare function addRunnerReposAuthed(store: Store, args: {
103
+ name: string;
104
+ token: string;
105
+ repos: readonly string[];
106
+ }, now: Date): AuthResult;
107
+ /**
108
+ * The one runner-name rule (arc 2 finding 13): nonempty, control-free, at
109
+ * most 60 characters — the console form's rule, now shared by every door.
110
+ */
111
+ export declare const RUNNER_NAME_MAX = 60;
112
+ /** The most tasks one worker may be set to run at once (the console's register form says the same). */
113
+ export declare const RUNNER_CAPACITY_MAX = 64;
114
+ /** A capacity as typed: a whole number from 1 to RUNNER_CAPACITY_MAX, or null. */
115
+ export declare function parseRunnerCapacity(value: string): number | null;
116
+ export declare function validRunnerName(name: string): boolean;
117
+ /** A generated base name: lowercase [a-z0-9-], collapsed, trimmed. The
118
+ * suffix budget (finding 24) is the CALLER's arithmetic — truncate the
119
+ * base to `RUNNER_NAME_MAX - suffix.length` before appending. */
120
+ export declare function normalizeRunnerName(raw: string): string;
121
+ /**
122
+ * The atomic take-or-refuse door (arc 2 findings 1/15/16/26/31): ONE write
123
+ * transaction that proves the name is free — absent, retired, or dead by
124
+ * the same predicate recovery uses, with NO unexpired watch lease — then
125
+ * finishes every open run the dead holder left (requeueing what no newer
126
+ * live claim owns), reclaims, and rotates. Two racers serialize: one wins,
127
+ * the other gets the typed refusal.
128
+ */
129
+ export declare function registerRunnerIfIdle(store: Store, options: RegisterOptions): (Registration & {
130
+ ok: true;
131
+ recoveredRuns: number;
132
+ }) | {
133
+ ok: false;
134
+ reason: "runner-alive" | "watch-live";
135
+ detail: string;
136
+ };
137
+ /**
138
+ * The authenticated lease door (arc 2 findings 15/26): credential verified
139
+ * and lease acquired/renewed in the SAME transaction — after a takeover
140
+ * rotates the hash, a stale incarnation can neither acquire nor renew. A
141
+ * takeover of an expired lease recovers the superseded incarnation INSIDE
142
+ * this transaction, so no crash can separate "B owns the lease" from "A
143
+ * was recovered".
144
+ */
145
+ export declare function acquireWatchLeaseAuthed(store: Store, args: {
146
+ runner: string;
147
+ token: string;
148
+ repo: string;
149
+ owner: string;
150
+ ttlMs: number;
151
+ }, now: Date): {
152
+ ok: true;
153
+ generation: number;
154
+ superseded: string | null;
155
+ recovered: number;
156
+ } | {
157
+ ok: false;
158
+ reason: "unknown" | "bad-token" | "retired" | "expired" | "watch-busy" | "unauthorized-repo";
159
+ holder?: string;
160
+ until?: string;
161
+ };
162
+ /** The renewal, equally authenticated: a false answer is FATAL to the
163
+ * caller — a loop that keeps admitting work after losing its lease or its
164
+ * credential is the exact hole the doors exist to close. */
165
+ export declare function heartbeatWatchLeaseAuthed(store: Store, args: {
166
+ runner: string;
167
+ token: string;
168
+ repo: string;
169
+ owner: string;
170
+ ttlMs: number;
171
+ }, now: Date): boolean;
172
+ /**
173
+ * Retirement fenced by THIS holder's credential (arc 2 finding 28): a
174
+ * successor that already rotated makes the predecessor's cleanup a typed
175
+ * no-op instead of retiring the winner.
176
+ */
177
+ export declare function retireRunnerIfCurrent(store: Store, name: string, token: string, now: Date): {
178
+ ok: true;
179
+ } | {
180
+ ok: false;
181
+ reason: "unknown" | "bad-token" | "retired" | "expired";
182
+ };
183
+ export declare function isAlive(runner: Runner, now: Date, livenessMs?: number): boolean;
184
+ /** Runners that have stopped saying they are there. */
185
+ export declare function deadRunners(store: Store, now: Date, livenessMs?: number): Runner[];
186
+ /** What a re-registration takes back from the previous holder of its name. */
187
+ export type Reclaim = {
188
+ runner: string;
189
+ /** Lease ids released because the machine holding them is gone. */
190
+ claims: string[];
191
+ /** Worktree paths handed back to the pool. */
192
+ worktrees: string[];
193
+ };
194
+ /** What one reconcile pass took back from a proven-dead runner. */
195
+ export type Recovery = Reclaim & {
196
+ /** Run ids finished as failed/interrupted because the machine that opened
197
+ * them is gone — including runs whose claim a reaper or the runner itself
198
+ * had already released (P0.1a). */
199
+ runs: number[];
200
+ /** Built-in task ids returned to the queue because no newer live lease
201
+ * owned them once the dead runner's rows were settled. */
202
+ requeued: string[];
203
+ };
204
+ /** Whether a pass found anything at all to take back. */
205
+ export declare function recoveredAnything(one: Recovery): boolean;
206
+ /**
207
+ * Take back everything a dead runner was holding.
208
+ *
209
+ * Claims, open runs, and worktrees together, in one pass, because they are
210
+ * halves of the same fact: the machine is gone. Recovering one and not the
211
+ * others leaves a task dispatchable with its working copy still checked out
212
+ * to a process that no longer exists — which fails later, further away, and
213
+ * looks like a git problem rather than a scheduling one — or a run record
214
+ * that stays open forever after its lease was released and a successor
215
+ * finished the work (P0.1a: the real run 1501).
216
+ *
217
+ * Runs settle through the same transactional walk the takeover door uses
218
+ * (`recoverRunnerWork`), AFTER this runner's claims are released: the walk
219
+ * finishes every open run recorded against the runner outside a live held
220
+ * session as failed/interrupted, and requeues its task only when no newer
221
+ * live lease owns it. A finished outcome is never rewritten; a successor's
222
+ * claim, run, worktree, and task state are somebody else's rows and stay
223
+ * exactly as they were.
224
+ *
225
+ * Worktrees are handed back **unverified**. Nobody watched what the dead
226
+ * process was doing when it stopped, so the directory on disk is a claim about
227
+ * the past rather than a description of the present, and something has to look
228
+ * before it is reused.
229
+ */
230
+ export declare function recoverDead(store: Store, now: Date, livenessMs?: number): Recovery[];
231
+ export declare function hashToken(token: string): string;
232
+ /**
233
+ * What ONE machine can say about a provider without spending a token:
234
+ * the binary answers `--version`; where a cheap identity probe exists
235
+ * (codex, openrouter: `login status`) whether it is logged in; where a key
236
+ * env var is required (openrouter) whether it is present; and, for an
237
+ * attested provider (gemini), whether the installed version is inside the
238
+ * range this build proved. Claude has no non-spending login check, so an
239
+ * installed claude is UNKNOWN — stated, never upgraded to ready.
240
+ *
241
+ * Every answer carries the probe's own words. The observation is the
242
+ * runner's and is recorded under its name only through the authenticated
243
+ * road below; a runner never reports for another machine.
244
+ */
245
+ export type ReadinessProbe = (file: string, args: readonly string[], options?: {
246
+ timeoutMs?: number;
247
+ omitEnv?: readonly string[];
248
+ }) => Promise<{
249
+ code: number;
250
+ stdout: string;
251
+ stderr: string;
252
+ notFound: boolean;
253
+ timedOut: boolean;
254
+ }>;
255
+ export declare function observeProviderReadiness(probe: ReadinessProbe, env?: Record<string, string | undefined>, providers?: readonly ProviderId[]): Promise<Omit<ReadinessObservation, "runner" | "observedAt">[]>;
256
+ /**
257
+ * Record a runner's observations under its OWN authenticated name: the
258
+ * credential is verified and the rows written in one transaction, so a
259
+ * replaced incarnation cannot report for its successor, and no caller can
260
+ * report for a machine it does not hold the token of.
261
+ */
262
+ export declare function reportProviderReadinessAuthed(store: Store, args: {
263
+ name: string;
264
+ token: string;
265
+ observations: readonly Omit<ReadinessObservation, "runner" | "observedAt">[];
266
+ }, now: Date): AuthResult;
package/dist/runner.js ADDED
@@ -0,0 +1,399 @@
1
+ /**
2
+ * A machine that may be given work.
3
+ *
4
+ * Auth is here from the first commit, and that is a deliberate ordering rather
5
+ * than diligence for its own sake. A control plane that dispatches work to
6
+ * unauthenticated runners for six months grows six months of code that assumes
7
+ * the caller is whoever it says it is, and retrofitting identity into that is
8
+ * how the retrofit gets a hole in it. It costs almost nothing to do now.
9
+ *
10
+ * **The token is never stored.** Registration mints one, prints it once, and
11
+ * keeps only a hash. There is no command that recovers it, because a control
12
+ * plane able to hand back a runner's credential is one whose database is worth
13
+ * stealing — and because the same rule governs secrets everywhere else in this
14
+ * design: the control plane holds metadata, the runner holds values.
15
+ *
16
+ * Liveness is separate from a lease expiring, and the distinction matters at
17
+ * 3am. A lease expires because time passed; a runner is dead because it
18
+ * stopped saying otherwise. Reaping on the first tells you a task is free
19
+ * again. Reaping on the second tells you a *machine* is gone, and everything
20
+ * it was holding — claims and worktrees alike — needs recovering together.
21
+ */
22
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
23
+ import { resolve } from "node:path";
24
+ import { canonicalProject } from "./project.js";
25
+ import { ALL_CREDENTIAL_ENV, PROVIDER_IDS, inspectionOf } from "./provider.js";
26
+ import { attestationOf, versionInRange } from "./attest.js";
27
+ /** A runner's repo binding, in the same canonical form task filing uses —
28
+ * the claim gate compares these by equality, so both sides must resolve
29
+ * through one convention (MCP gateway spec v6, the runner tuple). */
30
+ export function canonicalRepos(repos) {
31
+ return repos.map(repo => canonicalProject(repo) ?? resolve(repo));
32
+ }
33
+ /** v101: a runner's token lasts a year from its registration; registering again (every start does) renews it. */
34
+ export const RUNNER_TOKEN_MS = 365 * 86_400_000;
35
+ /**
36
+ * How long a runner may go quiet before it is presumed gone.
37
+ *
38
+ * Comfortably more than any sensible heartbeat interval: declaring a busy
39
+ * machine dead because one check-in was slow costs more than waiting another
40
+ * minute for a machine that really is.
41
+ */
42
+ export const DEFAULT_LIVENESS_MS = 3 * 60_000;
43
+ export function register(store, options) {
44
+ const { name, host, capacity = 1, repos = [], agents = [], now, newToken = mintToken, mutation = {}, } = options;
45
+ const token = newToken();
46
+ /**
47
+ * Anything the previous holder of this name still had is taken back first.
48
+ *
49
+ * Registering under a name is a new process saying it exists; whatever the
50
+ * last one was holding, it is not holding now. Skipping this is worse than
51
+ * it sounds: the fresh `heartbeat_at` makes the old identity look alive
52
+ * again, so the reaper walks straight past its abandoned claims and
53
+ * worktrees and they are stranded for good.
54
+ */
55
+ const previous = store.getRunner(name);
56
+ const reclaimed = previous === null
57
+ ? null
58
+ : {
59
+ runner: name,
60
+ claims: store.releaseClaimsOf(name, now),
61
+ worktrees: store.releaseWorktreesOf(name, now),
62
+ };
63
+ const proposed = {
64
+ name,
65
+ host,
66
+ capacity,
67
+ repos: canonicalRepos(repos),
68
+ agents: [...agents],
69
+ registeredAt: now.toISOString(),
70
+ heartbeatAt: now.toISOString(),
71
+ retiredAt: null,
72
+ };
73
+ // The store assigns the incarnation while holding its write transaction.
74
+ // Doing it from the `previous` snapshot above is racy: two registrars can
75
+ // read the same predecessor and otherwise mint the same generation.
76
+ const runner = store.saveRunner(proposed, hashToken(token), mutation);
77
+ return {
78
+ runner,
79
+ token,
80
+ reclaimed: reclaimed !== null && (reclaimed.claims.length > 0 || reclaimed.worktrees.length > 0)
81
+ ? reclaimed
82
+ : null,
83
+ };
84
+ }
85
+ /**
86
+ * Whether this caller is who it says it is.
87
+ *
88
+ * Compared in constant time. The comparison is between hashes rather than
89
+ * secrets, so the practical risk is slight — but a timing-variable equality on
90
+ * a credential path is the kind of thing that gets copied into somewhere it
91
+ * matters, and there is no reason to leave the example lying around.
92
+ */
93
+ export function authenticate(store, name, token) {
94
+ const found = store.getRunner(name);
95
+ if (found === null)
96
+ return { ok: false, reason: "unknown" };
97
+ if (found.runner.retiredAt !== null)
98
+ return { ok: false, reason: "retired" };
99
+ if (Date.now() - Date.parse(found.runner.registeredAt) > RUNNER_TOKEN_MS)
100
+ return { ok: false, reason: "expired" };
101
+ return sameDigest(found.credentialHash, hashToken(token))
102
+ ? { ok: true, runner: found.runner }
103
+ : { ok: false, reason: "bad-token" };
104
+ }
105
+ /**
106
+ * "I am still here." Authenticated AND ATOMIC (arc 2 findings 25/33): the
107
+ * hash is verified and the heartbeat stamped in one write transaction, so
108
+ * a takeover that rotates the credential between the check and the touch
109
+ * cannot receive a stale incarnation's pulse — after rotation commits, the
110
+ * very next heartbeat refuses, and the caller must treat that as fatal.
111
+ */
112
+ export function heartbeat(store, name, token, now) {
113
+ return store.transact(() => {
114
+ const auth = authenticate(store, name, token);
115
+ if (!auth.ok)
116
+ return auth;
117
+ store.touchRunner(name, now);
118
+ return { ok: true, runner: { ...auth.runner, heartbeatAt: now.toISOString() } };
119
+ });
120
+ }
121
+ /**
122
+ * Extend a live runner's project binding using that runner's own credential.
123
+ *
124
+ * `up` owns both the credential and the registry watcher, so a project the
125
+ * operator admitted through its console can join without a second operator
126
+ * ceremony or a restart. Authentication and the additive write share one
127
+ * transaction: a replaced process cannot widen its successor's binding.
128
+ */
129
+ export function addRunnerReposAuthed(store, args, now) {
130
+ return store.transact(() => {
131
+ const auth = authenticate(store, args.name, args.token);
132
+ if (!auth.ok)
133
+ return auth;
134
+ const repos = [...new Set([...auth.runner.repos, ...canonicalRepos(args.repos)])].sort();
135
+ const bound = store.bindRunnerRepos(args.name, repos, now);
136
+ if (!bound.ok)
137
+ return { ok: false, reason: bound.reason };
138
+ store.touchRunner(args.name, now);
139
+ return {
140
+ ok: true,
141
+ runner: { ...auth.runner, repos: bound.repos, heartbeatAt: now.toISOString() },
142
+ };
143
+ });
144
+ }
145
+ /**
146
+ * The one runner-name rule (arc 2 finding 13): nonempty, control-free, at
147
+ * most 60 characters — the console form's rule, now shared by every door.
148
+ */
149
+ export const RUNNER_NAME_MAX = 60;
150
+ /** The most tasks one worker may be set to run at once (the console's register form says the same). */
151
+ export const RUNNER_CAPACITY_MAX = 64;
152
+ /** A capacity as typed: a whole number from 1 to RUNNER_CAPACITY_MAX, or null. */
153
+ export function parseRunnerCapacity(value) {
154
+ const capacity = Number(value.trim());
155
+ return value.trim() !== "" && Number.isInteger(capacity) && capacity >= 1 && capacity <= RUNNER_CAPACITY_MAX ? capacity : null;
156
+ }
157
+ export function validRunnerName(name) {
158
+ if (name === "" || name.length > RUNNER_NAME_MAX)
159
+ return false;
160
+ for (const char of name) {
161
+ const code = char.codePointAt(0) ?? 0;
162
+ if (code < 0x20 || code === 0x7f)
163
+ return false;
164
+ }
165
+ return true;
166
+ }
167
+ /** A generated base name: lowercase [a-z0-9-], collapsed, trimmed. The
168
+ * suffix budget (finding 24) is the CALLER's arithmetic — truncate the
169
+ * base to `RUNNER_NAME_MAX - suffix.length` before appending. */
170
+ export function normalizeRunnerName(raw) {
171
+ const collapsed = raw
172
+ .toLowerCase()
173
+ .replace(/[^a-z0-9-]+/g, "-")
174
+ .replace(/-+/g, "-")
175
+ .replace(/^-|-$/g, "");
176
+ return (collapsed === "" ? "worker" : collapsed).slice(0, RUNNER_NAME_MAX);
177
+ }
178
+ /**
179
+ * The atomic take-or-refuse door (arc 2 findings 1/15/16/26/31): ONE write
180
+ * transaction that proves the name is free — absent, retired, or dead by
181
+ * the same predicate recovery uses, with NO unexpired watch lease — then
182
+ * finishes every open run the dead holder left (requeueing what no newer
183
+ * live claim owns), reclaims, and rotates. Two racers serialize: one wins,
184
+ * the other gets the typed refusal.
185
+ */
186
+ export function registerRunnerIfIdle(store, options) {
187
+ const { name, now } = options;
188
+ return store.transact(() => {
189
+ const existing = store.getRunner(name);
190
+ if (existing !== null && existing.runner.retiredAt === null && isAlive(existing.runner, now)) {
191
+ return {
192
+ ok: false,
193
+ reason: "runner-alive",
194
+ detail: `${name} heartbeated ${existing.runner.heartbeatAt} — it looks alive`,
195
+ };
196
+ }
197
+ const lease = store.liveWatchLeaseOf(name, now);
198
+ if (lease !== null) {
199
+ return {
200
+ ok: false,
201
+ reason: "watch-live",
202
+ detail: `${name} holds a live watch lease on ${lease.repo} until ${lease.until}`,
203
+ };
204
+ }
205
+ // Recovery BEFORE reclaim (finding 16): the open-run evidence must be
206
+ // read while it still exists; register()'s releaseClaimsOf would
207
+ // otherwise hide it from every later recovery.
208
+ const recoveredRuns = existing === null ? 0 : store.recoverRunnerWork(name, now).runs.length;
209
+ // A restart keeps the capacity an operator set; only an explicit one changes it.
210
+ const registration = register(store, { ...options, capacity: options.capacity ?? existing?.runner.capacity ?? 1 });
211
+ return { ok: true, ...registration, recoveredRuns };
212
+ });
213
+ }
214
+ /**
215
+ * The authenticated lease door (arc 2 findings 15/26): credential verified
216
+ * and lease acquired/renewed in the SAME transaction — after a takeover
217
+ * rotates the hash, a stale incarnation can neither acquire nor renew. A
218
+ * takeover of an expired lease recovers the superseded incarnation INSIDE
219
+ * this transaction, so no crash can separate "B owns the lease" from "A
220
+ * was recovered".
221
+ */
222
+ export function acquireWatchLeaseAuthed(store, args, now) {
223
+ return store.transact(() => {
224
+ const auth = authenticate(store, args.runner, args.token);
225
+ if (!auth.ok)
226
+ return { ok: false, reason: auth.reason };
227
+ // Watching a repo is a runner road like any other (MCP spec v6,
228
+ // round-4 finding 1): membership is proven in the same transaction
229
+ // as the lease, before any repo activity rides it.
230
+ if (!auth.runner.repos.includes(canonicalProject(args.repo) ?? resolve(args.repo))) {
231
+ return { ok: false, reason: "unauthorized-repo" };
232
+ }
233
+ const got = store.acquireWatchLease(args.runner, args.repo, args.owner, args.ttlMs, now);
234
+ if (!got.ok)
235
+ return { ok: false, reason: "watch-busy", holder: got.holder, until: got.until };
236
+ const recovered = got.superseded === null ? 0 : store.recoverIncarnation(args.runner, got.superseded, now);
237
+ return { ok: true, generation: got.generation, superseded: got.superseded, recovered };
238
+ });
239
+ }
240
+ /** The renewal, equally authenticated: a false answer is FATAL to the
241
+ * caller — a loop that keeps admitting work after losing its lease or its
242
+ * credential is the exact hole the doors exist to close. */
243
+ export function heartbeatWatchLeaseAuthed(store, args, now) {
244
+ return store.transact(() => {
245
+ const auth = authenticate(store, args.runner, args.token);
246
+ if (!auth.ok)
247
+ return false;
248
+ const renewed = store.heartbeatWatchLease(args.runner, args.repo, args.owner, args.ttlMs, now);
249
+ if (renewed)
250
+ store.touchRunner(args.runner, now);
251
+ return renewed;
252
+ });
253
+ }
254
+ /**
255
+ * Retirement fenced by THIS holder's credential (arc 2 finding 28): a
256
+ * successor that already rotated makes the predecessor's cleanup a typed
257
+ * no-op instead of retiring the winner.
258
+ */
259
+ export function retireRunnerIfCurrent(store, name, token, now) {
260
+ return store.transact(() => {
261
+ const auth = authenticate(store, name, token);
262
+ if (!auth.ok)
263
+ return { ok: false, reason: auth.reason };
264
+ store.retireRunner(name, now);
265
+ return { ok: true };
266
+ });
267
+ }
268
+ export function isAlive(runner, now, livenessMs = DEFAULT_LIVENESS_MS) {
269
+ if (runner.retiredAt !== null)
270
+ return false;
271
+ const last = Date.parse(runner.heartbeatAt);
272
+ if (Number.isNaN(last))
273
+ return false;
274
+ return now.getTime() - last < livenessMs;
275
+ }
276
+ /** Runners that have stopped saying they are there. */
277
+ export function deadRunners(store, now, livenessMs = DEFAULT_LIVENESS_MS) {
278
+ return store.listRunners().filter(runner => !isAlive(runner, now, livenessMs));
279
+ }
280
+ /** Whether a pass found anything at all to take back. */
281
+ export function recoveredAnything(one) {
282
+ return one.claims.length > 0 || one.worktrees.length > 0 || one.runs.length > 0 || one.requeued.length > 0;
283
+ }
284
+ /**
285
+ * Take back everything a dead runner was holding.
286
+ *
287
+ * Claims, open runs, and worktrees together, in one pass, because they are
288
+ * halves of the same fact: the machine is gone. Recovering one and not the
289
+ * others leaves a task dispatchable with its working copy still checked out
290
+ * to a process that no longer exists — which fails later, further away, and
291
+ * looks like a git problem rather than a scheduling one — or a run record
292
+ * that stays open forever after its lease was released and a successor
293
+ * finished the work (P0.1a: the real run 1501).
294
+ *
295
+ * Runs settle through the same transactional walk the takeover door uses
296
+ * (`recoverRunnerWork`), AFTER this runner's claims are released: the walk
297
+ * finishes every open run recorded against the runner outside a live held
298
+ * session as failed/interrupted, and requeues its task only when no newer
299
+ * live lease owns it. A finished outcome is never rewritten; a successor's
300
+ * claim, run, worktree, and task state are somebody else's rows and stay
301
+ * exactly as they were.
302
+ *
303
+ * Worktrees are handed back **unverified**. Nobody watched what the dead
304
+ * process was doing when it stopped, so the directory on disk is a claim about
305
+ * the past rather than a description of the present, and something has to look
306
+ * before it is reused.
307
+ */
308
+ export function recoverDead(store, now, livenessMs = DEFAULT_LIVENESS_MS) {
309
+ const recovered = [];
310
+ for (const candidate of deadRunners(store, now, livenessMs)) {
311
+ // Death is re-proved inside the transaction that acts on it. The snapshot
312
+ // above is a survey of a moving world: a runner whose pulse lands between
313
+ // the survey and the release is alive, and releasing its claims anyway
314
+ // would take work from a machine that just said "still here" — the exact
315
+ // loss the heartbeat exists to prevent.
316
+ const recovery = store.transact(() => {
317
+ const fresh = store.getRunner(candidate.name);
318
+ if (fresh === null || isAlive(fresh.runner, now, livenessMs))
319
+ return null;
320
+ // Claims and worktrees first, so the report names every path handed
321
+ // back; the run walk's own per-task worktree release then finds
322
+ // nothing left to do here.
323
+ const requeued = [];
324
+ const claims = store.releaseClaimsOf(candidate.name, now, requeued);
325
+ const worktrees = store.releaseWorktreesOf(candidate.name, now);
326
+ const settled = store.recoverRunnerWork(candidate.name, now);
327
+ return { runner: candidate.name, claims, worktrees, runs: settled.runs, requeued: [...new Set([...requeued, ...settled.requeued])] };
328
+ });
329
+ if (recovery !== null)
330
+ recovered.push(recovery);
331
+ }
332
+ return recovered;
333
+ }
334
+ /** 32 bytes of randomness, urlsafe, so it survives being pasted anywhere. */
335
+ function mintToken() {
336
+ return randomBytes(32).toString("base64url");
337
+ }
338
+ export function hashToken(token) {
339
+ return createHash("sha256").update(token, "utf8").digest("hex");
340
+ }
341
+ function sameDigest(left, right) {
342
+ const a = Buffer.from(left, "utf8");
343
+ const b = Buffer.from(right, "utf8");
344
+ // timingSafeEqual throws on a length mismatch, which is itself a leak of
345
+ // sorts; equal-length hex digests make that unreachable in practice, and the
346
+ // guard keeps it from throwing if one is ever malformed.
347
+ return a.length === b.length && timingSafeEqual(a, b);
348
+ }
349
+ const READINESS_PROBE_TIMEOUT_MS = 5_000;
350
+ export async function observeProviderReadiness(probe, env = process.env, providers = PROVIDER_IDS) {
351
+ return Promise.all(providers.map(async (provider) => {
352
+ const facts = inspectionOf(provider);
353
+ const version = await probe(facts.binary, ["--version"], { timeoutMs: READINESS_PROBE_TIMEOUT_MS, omitEnv: ALL_CREDENTIAL_ENV });
354
+ if (version.notFound) {
355
+ return { provider, state: "unavailable", reason: `\`${facts.binary}\` is not installed on this runner's PATH`, probe: "version" };
356
+ }
357
+ if (version.timedOut || version.code !== 0) {
358
+ return { provider, state: "unavailable", reason: `\`${facts.binary} --version\` ${version.timedOut ? "timed out" : `exited ${version.code}`}`, probe: "version" };
359
+ }
360
+ const installed = version.stdout.trim().split("\n")[0] ?? "";
361
+ const range = attestationOf(provider);
362
+ if (range !== null && !versionInRange(installed, range)) {
363
+ return { provider, state: "unavailable", reason: `installed ${facts.binary} ${installed || "(unversioned)"} is outside this build's attested range ${range.floor}–${range.ceiling}`, probe: "version" };
364
+ }
365
+ if (facts.requiresEnv !== null && (env[facts.requiresEnv] ?? "") === "") {
366
+ return { provider, state: "unavailable", reason: `${facts.requiresEnv} is absent from this runner's environment`, probe: "key" };
367
+ }
368
+ // A key-driven provider (openrouter rides the codex harness on an API
369
+ // key) is ready on key PRESENCE — presence, never authorization — and
370
+ // needs no harness login. A login-driven one asks the harness.
371
+ if (facts.requiresEnv !== null) {
372
+ return { provider, state: "ready", reason: `installed (${installed}); ${facts.requiresEnv} present (presence, not authorization)`, probe: "key" };
373
+ }
374
+ if (facts.identityProbe !== null) {
375
+ const identity = await probe(facts.binary, [...facts.identityProbe], { timeoutMs: READINESS_PROBE_TIMEOUT_MS, omitEnv: ALL_CREDENTIAL_ENV });
376
+ if (identity.code !== 0) {
377
+ return { provider, state: "unavailable", reason: `\`${facts.binary} ${facts.identityProbe.join(" ")}\` says not logged in`, probe: "identity" };
378
+ }
379
+ const who = identity.stdout.trim().split("\n")[0] ?? "";
380
+ return { provider, state: "ready", reason: `installed (${installed}); ${who === "" ? "logged in" : who}`, probe: "identity" };
381
+ }
382
+ return { provider, state: "unknown", reason: `installed (${installed}); no non-spending login check exists — a real run is the proof`, probe: "version" };
383
+ }));
384
+ }
385
+ /**
386
+ * Record a runner's observations under its OWN authenticated name: the
387
+ * credential is verified and the rows written in one transaction, so a
388
+ * replaced incarnation cannot report for its successor, and no caller can
389
+ * report for a machine it does not hold the token of.
390
+ */
391
+ export function reportProviderReadinessAuthed(store, args, now) {
392
+ return store.transact(() => {
393
+ const auth = authenticate(store, args.name, args.token);
394
+ if (!auth.ok)
395
+ return auth;
396
+ store.recordProviderReadiness(args.name, args.observations, now);
397
+ return auth;
398
+ });
399
+ }