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,753 @@
1
+ /**
2
+ * Tournament orchestration (stage 3; the three review rounds' findings are
3
+ * the spec). What lives here and what deliberately does not:
4
+ *
5
+ * - the race digest and the JOINT approval digest (finding 31): the
6
+ * approval a person gives covers the scope AND the race terms as one
7
+ * hash — editing either strands it;
8
+ * - planning: turning asked-for agents and dollars into approved-terms
9
+ * rows, refusing anything the money machinery cannot bound (finding
10
+ * 24: eligibility comes from the capability matrix, budgets from the
11
+ * pinned prices, and every agent carries its own overrun reserve);
12
+ * - admission: ONE transaction that proves everything or admits nothing
13
+ * (finding 3/19/22) — the claim, the digests re-derived from stored
14
+ * rows, worker-process slots, quota by distinct key, the money
15
+ * arithmetic — then creates the contest skeleton;
16
+ * - the ready barrier (finding 19): worktrees and setup finish for every
17
+ * agent before anything spawns; the barrier is a state CAS;
18
+ * - child finalization (findings 1/17): an agent finishing NEVER touches
19
+ * the task, the parent claim, or publication — it writes its own row
20
+ * and asks, inside the same transaction, whether the whole tournament
21
+ * just reached its boundary (maybeAggregate);
22
+ * - crash recovery (finding 27): a daemon death mid-dispatch or mid-race
23
+ * converts the contest to 'interrupted' by the same generation CAS the
24
+ * finalizers use — whoever wins, wins alone — and never charges an
25
+ * agent that never started.
26
+ *
27
+ * NOT here: the pick and abandon ceremonies (stage 5), decision-wait
28
+ * re-admission and checkout custody (stage 4), cleanup (stage 6).
29
+ */
30
+ import { profileDigestOf } from "./scope.js";
31
+ import { contestantProfileOf } from "./store.js";
32
+ import { createHash } from "node:crypto";
33
+ import { headWithin, taskBranch } from "./names.js";
34
+ import { release } from "./claim.js";
35
+ import { buildPriceOf, oneCallTailMicrousd, BUILD_PRICE_VERSION } from "./pricing.js";
36
+ import { MONEY_CAPABILITIES, isProviderId, reportsCost, validateSpec } from "./provider.js";
37
+ export const MIN_AGENTS = 2;
38
+ export const MAX_AGENTS = 4;
39
+ // ---------------------------------------------------------------- digests
40
+ /** Canonical and order-preserving: the agents race in the order approved.
41
+ * v2 (foundations finding 8): each lane's FULL execution profile enters the
42
+ * fingerprint — permissions and limits included, not just routing — via the
43
+ * same profile digest every other approval binds. Stored v1 fingerprints
44
+ * keep verifying byte-for-byte (admission compares stored values; nothing
45
+ * recomputes a v1). */
46
+ export function raceDigestOf(terms) {
47
+ const canonical = JSON.stringify({
48
+ v: 2,
49
+ agents: terms.agents.map(agent => [
50
+ agent.provider,
51
+ agent.model,
52
+ agent.repairModel,
53
+ profileDigestOf(contestantProfileOf(agent.provider, agent.model, agent.repairModel, agent.permissionMode)),
54
+ ]),
55
+ per: terms.perAgentBudgetMicrousd,
56
+ total: terms.totalBudgetMicrousd,
57
+ prices: terms.priceVersion,
58
+ retries: 0,
59
+ publication: terms.publicationPolicy,
60
+ });
61
+ return createHash("sha256").update(`race/v2\u0000${canonical}`).digest("hex");
62
+ }
63
+ /** tournament-approval/v1 (finding 31): one yes covers both documents.
64
+ * Comparisons reuse it verbatim — their fingerprint rides the same stored
65
+ * column, and the domains below keep race and comparison terms from ever
66
+ * colliding. */
67
+ export function jointApprovalDigest(scopeDigest, raceDigest) {
68
+ return createHash("sha256").update(`tournament-approval/v1\u0000${scopeDigest}\u0000${raceDigest}`).digest("hex");
69
+ }
70
+ /** comparison/v1 (Phase 3 slice B): order-preserving, each lane's FULL
71
+ * profile digest included, NO money fields — none exist on a comparison. */
72
+ export function comparisonDigestOf(terms) {
73
+ const canonical = JSON.stringify({
74
+ v: 1,
75
+ agents: terms.agents.map(agent => [
76
+ agent.provider,
77
+ agent.model,
78
+ agent.repairModel,
79
+ profileDigestOf(contestantProfileOf(agent.provider, agent.model, agent.repairModel, agent.permissionMode)),
80
+ ]),
81
+ publication: terms.publicationPolicy,
82
+ });
83
+ return createHash("sha256").update(`comparison/v1\u0000${canonical}`).digest("hex");
84
+ }
85
+ /**
86
+ * A comparison admits any registered provider — that is its purpose — but
87
+ * NEVER lets the budget ceremony be bypassed: if every asked lane could
88
+ * hold a native dollar cap, the refusal points at the tournament road
89
+ * (spec D3, the discipline gate). No dollar fields exist here at all;
90
+ * each lane's bound is its sealed profile's wall clock, said in words.
91
+ */
92
+ export function planComparison(input) {
93
+ if (input.agents.length < MIN_AGENTS || input.agents.length > MAX_AGENTS) {
94
+ return { ok: false, reason: "bad-count", message: `a comparison runs ${MIN_AGENTS} to ${MAX_AGENTS} agents` };
95
+ }
96
+ const agents = [];
97
+ const laneWords = [];
98
+ let unmeasuredLanes = 0;
99
+ for (const asked of input.agents) {
100
+ if (!isProviderId(asked.provider)) {
101
+ return { ok: false, reason: "unknown-provider", message: `unknown provider \`${asked.provider}\`` };
102
+ }
103
+ const valid = validateSpec({ provider: asked.provider, model: asked.model });
104
+ if (!valid.ok) {
105
+ return { ok: false, reason: "bad-model", message: valid.problem };
106
+ }
107
+ if (asked.model === "") {
108
+ return { ok: false, reason: "bad-model", message: "every comparison lane names its exact model — the lanes ARE the terms" };
109
+ }
110
+ const capability = MONEY_CAPABILITIES[asked.provider];
111
+ if (!capability.tournamentEligible)
112
+ unmeasuredLanes++;
113
+ const repairModel = asked.repairModel ?? asked.model;
114
+ agents.push({ provider: asked.provider, model: asked.model, repairModel, ...(asked.permissionMode === undefined ? {} : { permissionMode: asked.permissionMode }) });
115
+ laneWords.push(reportsCost(asked.provider)
116
+ ? `${asked.provider} · ${asked.model} — spend measured in dollars; no cap on a comparison, the clock is the bound`
117
+ : `${asked.provider} · ${asked.model} — reports tokens only; dollar exposure unmeasured; the clock is the bound`);
118
+ }
119
+ // Comparisons file exact models for EVERY lane (the filing invariant):
120
+ // claude and codex get the same rule gemini/openrouter already carry.
121
+ for (const agent of agents) {
122
+ if (agent.model === "") {
123
+ return { ok: false, reason: "bad-model", message: "every comparison lane names its exact model" };
124
+ }
125
+ }
126
+ if (unmeasuredLanes === 0) {
127
+ return {
128
+ ok: false,
129
+ reason: "all-lanes-raceable",
130
+ message: "a comparison is not a budget bypass — every one of these agents can hold an enforced dollar cap, so file a tournament for this lineup (it will also need each model's pinned price)",
131
+ };
132
+ }
133
+ const plan = {
134
+ kind: "comparison",
135
+ agents,
136
+ publicationPolicy: input.publicationPolicy ?? "none",
137
+ comparisonDigest: "",
138
+ laneWords,
139
+ };
140
+ plan.comparisonDigest = comparisonDigestOf(plan);
141
+ return { ok: true, plan };
142
+ }
143
+ /** One words helper for every surface that speaks about a contest —
144
+ * screens, holds, notifications, decision subjects (spec E6). */
145
+ export function contestNoun(kind) {
146
+ return kind === "comparison" ? "comparison" : "tournament";
147
+ }
148
+ export function planTournament(input) {
149
+ if (input.agents.length < MIN_AGENTS || input.agents.length > MAX_AGENTS) {
150
+ return { ok: false, reason: "bad-count", message: `a tournament races ${MIN_AGENTS} to ${MAX_AGENTS} agents` };
151
+ }
152
+ if (!Number.isFinite(input.perAgentBudgetUsd) || input.perAgentBudgetUsd <= 0) {
153
+ return { ok: false, reason: "bad-budget", message: "each agent needs a positive dollar budget" };
154
+ }
155
+ if (!Number.isFinite(input.totalBudgetUsd) || input.totalBudgetUsd <= 0) {
156
+ return { ok: false, reason: "bad-budget", message: "the tournament needs a positive total dollar budget" };
157
+ }
158
+ const agents = [];
159
+ const reserves = [];
160
+ for (const asked of input.agents) {
161
+ const capability = MONEY_CAPABILITIES[asked.provider];
162
+ if (capability === undefined || !capability.tournamentEligible) {
163
+ return {
164
+ ok: false,
165
+ reason: "provider-ineligible",
166
+ message: capability?.whyIneligible ?? `${asked.provider} cannot hold a dollar cap, so it cannot race`,
167
+ };
168
+ }
169
+ const model = asked.model;
170
+ const repairModel = asked.repairModel ?? model;
171
+ for (const candidate of [model, repairModel]) {
172
+ if (buildPriceOf(candidate) === null) {
173
+ return {
174
+ ok: false,
175
+ reason: "unpriced-model",
176
+ message: `${candidate} has no pinned price — a budget nobody can compute is not a budget`,
177
+ };
178
+ }
179
+ }
180
+ const tail = oneCallTailMicrousd(model);
181
+ const repairTail = oneCallTailMicrousd(repairModel);
182
+ if (tail === null || repairTail === null) {
183
+ return { ok: false, reason: "unpriced-model", message: `${model} has no pinned overrun price` };
184
+ }
185
+ agents.push({ provider: asked.provider, model, repairModel, ...(asked.permissionMode === undefined ? {} : { permissionMode: asked.permissionMode }) });
186
+ reserves.push(Math.max(tail, repairTail));
187
+ }
188
+ const perAgentBudgetMicrousd = Math.round(input.perAgentBudgetUsd * 1_000_000);
189
+ const totalBudgetMicrousd = Math.round(input.totalBudgetUsd * 1_000_000);
190
+ const committed = reserves.reduce((sum, reserve) => sum + perAgentBudgetMicrousd + reserve, 0);
191
+ if (committed > totalBudgetMicrousd) {
192
+ return {
193
+ ok: false,
194
+ reason: "over-total",
195
+ message: `the true worst case is $${(committed / 1_000_000).toFixed(2)} ` +
196
+ `(each agent's budget plus its overrun reserve) — the total must cover it`,
197
+ };
198
+ }
199
+ const plan = {
200
+ agents,
201
+ perAgentBudgetMicrousd,
202
+ overrunReserveMicrousd: Math.max(...reserves),
203
+ perAgentReserveMicrousd: reserves,
204
+ totalBudgetMicrousd,
205
+ priceVersion: BUILD_PRICE_VERSION,
206
+ publicationPolicy: input.publicationPolicy ?? "none",
207
+ raceDigest: "",
208
+ };
209
+ plan.raceDigest = raceDigestOf(plan);
210
+ return { ok: true, plan };
211
+ }
212
+ /** Unique per tournament — retained loser branches never collide. */
213
+ export function contestBranch(taskId, contestId, ordinal) {
214
+ return `${taskBranch(taskId)}/contest-${contestId}/c${ordinal}`;
215
+ }
216
+ /**
217
+ * All or none (finding 3): every check inside one reentrant transaction
218
+ * with the claim acquisition the caller already made — the caller passes
219
+ * its live claim, and a refusal here obliges the caller to release it.
220
+ */
221
+ export function admitContest(store, args, now) {
222
+ return store.transact(() => {
223
+ // D1 (external dispatch): external work does not race in this release.
224
+ if (store.mirrorByTask(args.taskId) !== null) {
225
+ return {
226
+ ok: false,
227
+ reason: "contest-open",
228
+ message: "external work races in a follow-up release — file the tournament on a local task",
229
+ };
230
+ }
231
+ if (store.openContestFor(args.taskRef) !== null) {
232
+ return { ok: false, reason: "contest-open", message: "a tournament is already running on this task" };
233
+ }
234
+ // Steering and tournaments stay closed in BOTH directions (arc 1): a
235
+ // pending note would reach exactly one racer and silently miss the
236
+ // rest, so neither side starts while the other is open.
237
+ if (store.pendingSteerCount(args.taskRef) > 0) {
238
+ return {
239
+ ok: false,
240
+ reason: "steering-pending",
241
+ message: "a steering note is still waiting to reach an agent — let it land or withdraw it before racing agents on this task",
242
+ };
243
+ }
244
+ const terms = store.activeTournamentTerms(args.taskRef);
245
+ if (terms === null) {
246
+ return { ok: false, reason: "no-terms", message: "no tournament terms are filed for this task" };
247
+ }
248
+ // Re-derive, never trust columns (the H1 precedent): the stored digest
249
+ // must equal the digest of the stored terms, and the approval must
250
+ // bind exactly that digest — plus the scope's own approval.
251
+ const derived = terms.kind === "comparison" ? comparisonDigestOf(terms) : raceDigestOf(terms);
252
+ if (derived !== terms.raceDigest) {
253
+ return { ok: false, reason: "digest-drift", message: `the stored ${contestNoun(terms.kind)} terms do not match their own fingerprint` };
254
+ }
255
+ if (!args.scopeApproved || terms.approvedDigest !== terms.raceDigest) {
256
+ return { ok: false, reason: "not-approved", message: `the ${contestNoun(terms.kind)} terms await approval` };
257
+ }
258
+ // Quota, grouped by distinct key; a half-open key may appear ONCE.
259
+ const seen = new Map();
260
+ for (const agent of terms.agents) {
261
+ const key = `${agent.provider}\u0000${agent.model}`;
262
+ seen.set(key, (seen.get(key) ?? 0) + 1);
263
+ }
264
+ for (const [key, count] of seen) {
265
+ const [provider = "", model = ""] = key.split("\u0000");
266
+ const blocked = args.quotaBlocked(provider, model);
267
+ if (blocked === "half-open" && count > 1) {
268
+ return {
269
+ ok: false,
270
+ reason: "duplicate-provider-model",
271
+ message: `${provider} · ${model} is easing back from a limit — one probe exists, and this tournament asks for ${count}`,
272
+ };
273
+ }
274
+ if (blocked !== null && blocked !== "half-open") {
275
+ return { ok: false, reason: "quota", message: `${provider} · ${model} is ${blocked} — nothing was started` };
276
+ }
277
+ }
278
+ // Worker-process capacity (processes mode only; 'tasks' mode keeps the
279
+ // claim-counted contract untouched).
280
+ if (args.capacity !== null && store.liveSlotCount(args.runner) + terms.n > args.capacity) {
281
+ return {
282
+ ok: false,
283
+ reason: "capacity",
284
+ message: `this machine runs ${args.capacity} worker processes at most — ${terms.n} more will not fit right now`,
285
+ };
286
+ }
287
+ const contestId = store.createContest({ taskRef: args.taskRef, terms: terms.id, scopeDigest: args.scopeDigest, raceDigest: terms.raceDigest, kind: terms.kind }, now);
288
+ store.stampContestLease(contestId, args.leaseId, args.runner, args.incarnation);
289
+ const reserves = terms.kind === "comparison" ? [] : perAgentReserves(terms);
290
+ const contestantIds = store.createContestants(contestId, terms.agents.map((agent, index) => ({
291
+ provider: agent.provider,
292
+ model: agent.model,
293
+ repairModel: agent.repairModel,
294
+ ...(agent.permissionMode === undefined ? {} : { permissionMode: agent.permissionMode }),
295
+ branch: contestBranch(args.taskId, contestId, index + 1),
296
+ // Comparison lanes carry no dollar terms; unmeasured lanes are
297
+ // pre-latched as a fact of the lane (spec D6).
298
+ budgetMicrousd: terms.kind === "comparison" ? 0 : terms.perAgentBudgetMicrousd,
299
+ reserveMicrousd: terms.kind === "comparison" ? 0 : (reserves[index] ?? terms.overrunReserveMicrousd),
300
+ ...(terms.kind === "comparison" && !reportsCost(agent.provider)
301
+ ? { unknownSpend: true }
302
+ : {}),
303
+ })));
304
+ const slotIds = store.reserveExecutionSlots(args.runner, terms.n, now);
305
+ slotIds.forEach((slot, index) => {
306
+ const contestant = contestantIds[index];
307
+ if (contestant !== undefined)
308
+ store.assignSlotContestant(slot, contestant);
309
+ });
310
+ return { ok: true, contestId, contestantIds, slotIds };
311
+ });
312
+ }
313
+ function perAgentReserves(terms) {
314
+ return terms.agents.map(agent => {
315
+ const tail = oneCallTailMicrousd(agent.model);
316
+ const repairTail = oneCallTailMicrousd(agent.repairModel);
317
+ return Math.max(tail ?? terms.overrunReserveMicrousd, repairTail ?? terms.overrunReserveMicrousd);
318
+ });
319
+ }
320
+ // ---------------------------------------------------------------- barrier
321
+ /** Everything prepared, nothing spawned: pending→ready per agent, then one
322
+ * dispatching→racing CAS. Any failure leaves the contest for recovery. */
323
+ export function crossReadyBarrier(store, contest, contestantIds) {
324
+ return store.transact(() => {
325
+ for (const id of contestantIds) {
326
+ const row = store.getContestant(id);
327
+ if (row === null || !store.casContestantState(id, ["pending"], "ready", row.generation))
328
+ return false;
329
+ }
330
+ return store.casContestState(contest.id, ["dispatching"], "racing", contest.generation);
331
+ });
332
+ }
333
+ /**
334
+ * The child finalizer (findings 1/17): writes the agent's own row, frees
335
+ * its worker-process slot, and — inside this same transaction — asks
336
+ * whether the tournament just reached its boundary. The parent claim and
337
+ * the task are NEVER touched here; only the aggregate boundary releases
338
+ * the claim, exactly once, by CAS.
339
+ */
340
+ export function finalizeContestant(store, args, now) {
341
+ return store.transact(() => {
342
+ const contestant = store.getContestant(args.contestantId);
343
+ if (contestant === null)
344
+ return { aggregated: null };
345
+ store.casContestantState(args.contestantId, ["building", "ready"], args.outcome, contestant.generation);
346
+ store.releaseContestantRun(args.contestantId, args.runId);
347
+ if (args.slotId !== null)
348
+ store.releaseExecutionSlot(args.slotId, now);
349
+ if (args.measuredMicrousd !== null)
350
+ store.recordContestantSpend(args.contestantId, args.measuredMicrousd);
351
+ else
352
+ store.latchContestantUnknownSpend(args.contestantId);
353
+ return { aggregated: maybeAggregate(store, args.contestId, now) };
354
+ });
355
+ }
356
+ /**
357
+ * The aggregate boundary — runs INSIDE a child/recovery transaction. When
358
+ * no agent remains active: parked-with-open-question → 'decision-wait';
359
+ * at least one 'built' → 'pick-wait'; none → 'exhausted'. The parent
360
+ * claim releases conditionally; the contest-owned hold takes over the
361
+ * task's stillness so the board says "compare and pick", not "vanished".
362
+ */
363
+ export function maybeAggregate(store, contestId, now) {
364
+ const contest = store.getContest(contestId);
365
+ if (contest === null || contest.state !== "racing")
366
+ return null;
367
+ const all = store.contestants(contestId);
368
+ const active = all.filter(one => one.state === "pending" || one.state === "ready" || one.state === "building");
369
+ if (active.length > 0)
370
+ return null;
371
+ const parkedWaiting = all.filter(one => one.state === "parked" && store.openDecisionForContestant(one.id) !== null);
372
+ const target = parkedWaiting.length > 0 ? "decision-wait" : all.some(one => one.state === "built") ? "pick-wait" : "exhausted";
373
+ if (!store.casContestState(contestId, ["racing"], target, contest.generation))
374
+ return null;
375
+ if (contest.currentLeaseId !== null)
376
+ release(store, contest.currentLeaseId, now);
377
+ store.holdOwned({
378
+ taskRef: contest.taskRef,
379
+ ownerKind: "contest",
380
+ ownerId: String(contestId),
381
+ reason: target === "pick-wait"
382
+ ? `${contestNoun(contest.kind)} finished — compare the results and pick one`
383
+ : target === "decision-wait"
384
+ ? "an agent is waiting on your answer"
385
+ : `${contestNoun(contest.kind)} finished with nothing to pick — decide what happens next`,
386
+ until: null,
387
+ }, now);
388
+ // The boundary pages once (dedupe-keyed): a tournament that finished is
389
+ // exactly the "needs me" moment connected messaging exists for. The
390
+ // decision-wait case stays quiet here — the question itself already pages.
391
+ if (target !== "decision-wait") {
392
+ const taskId = store.externalIdFor(contest.taskRef) ?? `ref ${contest.taskRef}`;
393
+ store.enqueueNotification({
394
+ source: { taskRef: contest.taskRef },
395
+ dedupeKey: `contest-${target}:${contestId}`,
396
+ kind: "contest-finished",
397
+ // Only a PICKABLE finish pushes (arc 3): exhausted has nothing to tap.
398
+ ...(target === "pick-wait" ? { pushClass: "pick", link: `/contest/${contestId}` } : {}),
399
+ subject: target === "pick-wait"
400
+ ? `${contestNoun(contest.kind)} finished: ${taskId} — compare and pick`
401
+ : `${contestNoun(contest.kind)} finished with nothing to pick: ${taskId}`,
402
+ body: target === "pick-wait"
403
+ ? `All ${all.length} agents finished. Compare the results and pick one at /contest/${contestId} — nothing continues until you do.`
404
+ : `All ${all.length} agents finished without a pickable result. Decide what happens next at /contest/${contestId}.`,
405
+ }, now);
406
+ }
407
+ return target;
408
+ }
409
+ // ---------------------------------------------------------------- recovery
410
+ /**
411
+ * Crash and reap recovery (finding 27): both CAS on the same generations
412
+ * the finalizers use. An agent that never crossed provider start settles
413
+ * at ZERO; only a started-and-silent one latches its full reservation.
414
+ */
415
+ export function recoverContests(store, now) {
416
+ let recovered = 0;
417
+ for (const contest of store.contestsInStates(["dispatching", "racing"])) {
418
+ const lease = contest.currentLeaseId === null ? null : store.liveClaimByLease(contest.currentLeaseId, now);
419
+ if (lease !== null)
420
+ continue; // still honestly owned
421
+ const fresh = store.getContest(contest.id);
422
+ if (fresh === null)
423
+ continue;
424
+ const moved = store.transact(() => {
425
+ if (!store.casContestState(contest.id, ["dispatching", "racing"], "interrupted", fresh.generation))
426
+ return false;
427
+ for (const contestant of store.contestants(contest.id)) {
428
+ if (contestant.state === "pending" || contestant.state === "ready" || contestant.state === "building") {
429
+ store.casContestantState(contestant.id, [contestant.state], "stopped", contestant.generation);
430
+ const started = contestant.activeRun !== null && store.getRun(contestant.activeRun)?.providerStartedAt != null;
431
+ if (started)
432
+ store.latchContestantUnknownSpend(contestant.id);
433
+ // Never started: accounted stays where it is (zero when untouched).
434
+ if (contestant.activeRun !== null)
435
+ store.releaseContestantRun(contestant.id, contestant.activeRun);
436
+ }
437
+ }
438
+ store.releaseSlotsForContest(contest.id, now);
439
+ store.holdOwned({
440
+ taskRef: contest.taskRef,
441
+ ownerKind: "contest",
442
+ ownerId: String(contest.id),
443
+ reason: `the ${contestNoun(contest.kind)} was interrupted — review it, then abandon or start it again`,
444
+ until: null,
445
+ }, now);
446
+ return true;
447
+ });
448
+ if (moved)
449
+ recovered++;
450
+ }
451
+ return recovered;
452
+ }
453
+ // ---------------------------------------------------------------- the pick
454
+ import { readVerifiedArtifact } from "./evidence.js";
455
+ /**
456
+ * Pickability, exactly as ruled (round-3 finding 20): a real result with
457
+ * verified, unredacted, untruncated evidence whose typed capture verdict
458
+ * says ok — never a prose parse, never an automatic selection.
459
+ */
460
+ export function buildPickView(store, evidenceRoot, contestId) {
461
+ const contest = store.getContest(contestId);
462
+ if (contest === null)
463
+ return null;
464
+ const agents = store.contestants(contestId).map(contestant => {
465
+ // The lineage's final run: the newest run carrying this agent.
466
+ const runs = store
467
+ .runsFor(contest.taskRef)
468
+ .filter(one => one.contestant === contestant.id)
469
+ .sort((a, b) => b.id - a.id);
470
+ const run = runs[0] ?? null;
471
+ const artifacts = run === null ? [] : store.artifactsFor(run.id);
472
+ const diff = artifacts.find(one => one.kind === "terminal-diff") ?? null;
473
+ const stat = artifacts.find(one => one.kind === "diff-stat") ?? null;
474
+ // Plain words only — the internal state/outcome tokens never reach the
475
+ // comparison screen (round-4 finding 17). The tuple digest binds the
476
+ // raw facts separately; these strings are display, not authority.
477
+ const stateWords = {
478
+ pending: "was never started",
479
+ ready: "was set up but never ran",
480
+ building: "is still building",
481
+ parked: "stopped at a question",
482
+ failed: "failed",
483
+ stopped: "was stopped",
484
+ };
485
+ const outcomeWords = {
486
+ failed: "the attempt failed",
487
+ refused: "a gate refused the attempt",
488
+ parked: "the attempt stopped at a question",
489
+ };
490
+ let reason = null;
491
+ if (contestant.state !== "built")
492
+ reason = `did not finish — ${stateWords[contestant.state] ?? "the records have the detail"}`;
493
+ else if (run === null || run.outcome === null)
494
+ reason = "no finished attempt";
495
+ else if (run.outcome === "built" && run.committed !== true)
496
+ reason = "finished without committing";
497
+ else if (run.outcome === "no-change" && run.headRevision !== null && contest.baseSha !== null && run.headRevision !== contest.baseSha)
498
+ reason = "claims no change but the branch moved";
499
+ else if (run.outcome !== "built" && run.outcome !== "no-change")
500
+ reason = outcomeWords[run.outcome] ?? "the attempt did not finish cleanly";
501
+ else if (diff === null || stat === null)
502
+ reason = "evidence was not captured";
503
+ else if (diff.captureStatus !== "ok" || stat.captureStatus !== "ok")
504
+ reason = "evidence capture did not verify";
505
+ else if (diff.redacted)
506
+ reason = "the diff was redacted (something credential-shaped) — unpickable, like unpublishable";
507
+ else if (diff.truncated)
508
+ reason = "the diff was cut to fit — the comparison here would be partial";
509
+ else {
510
+ const provenDiff = readVerifiedArtifact(evidenceRoot, diff);
511
+ const provenStat = readVerifiedArtifact(evidenceRoot, stat);
512
+ if (!provenDiff.ok || !provenStat.ok)
513
+ reason = "the evidence bytes no longer verify";
514
+ }
515
+ return { contestant, run, diff, stat, pickable: reason === null, unpickableReason: reason };
516
+ });
517
+ return { contest, agents };
518
+ }
519
+ /**
520
+ * contest-pick/v1 — the canonical tuple the ceremony's nonce binds
521
+ * (round-1 finding 8 as amended): everything the password authorizes,
522
+ * rebuilt from live state on the POST, never accepted from a client.
523
+ */
524
+ export function pickTupleDigest(view, selection, publication) {
525
+ const tuple = {
526
+ v: "contest-pick/v1",
527
+ contest: view.contest.id,
528
+ generation: view.contest.generation,
529
+ // v27 (Codex slice-B finding 10): kind changes the ceremony's money
530
+ // semantics, so the password binds it. Open nonces at upgrade simply
531
+ // re-mint — they live fifteen minutes.
532
+ kind: view.contest.kind,
533
+ taskRef: view.contest.taskRef,
534
+ scope: view.contest.scopeDigest,
535
+ race: view.contest.raceDigest,
536
+ base: view.contest.baseSha,
537
+ setup: view.contest.setupDigest,
538
+ agents: view.agents.map(agent => ({
539
+ id: agent.contestant.id,
540
+ ordinal: agent.contestant.ordinal,
541
+ provider: agent.contestant.provider,
542
+ model: agent.contestant.model,
543
+ state: agent.contestant.state,
544
+ run: agent.run?.id ?? null,
545
+ outcome: agent.run?.outcome ?? null,
546
+ committed: agent.run?.committed ?? null,
547
+ head: agent.run?.headRevision ?? null,
548
+ diff: agent.diff === null ? null : { id: agent.diff.id, sha: agent.diff.sha256, bytes: agent.diff.bytesStored, capture: agent.diff.captureStatus, redacted: agent.diff.redacted, truncated: agent.diff.truncated },
549
+ stat: agent.stat === null ? null : { id: agent.stat.id, sha: agent.stat.sha256, bytes: agent.stat.bytesStored, capture: agent.stat.captureStatus, redacted: agent.stat.redacted, truncated: agent.stat.truncated },
550
+ accountedMicrousd: agent.contestant.accountedMicrousd,
551
+ unknownSpend: agent.contestant.unknownSpend,
552
+ })),
553
+ selection,
554
+ publication: publication.grant === null
555
+ ? "none"
556
+ : {
557
+ grant: publication.grant.id,
558
+ githubRepo: publication.grant.githubRepo,
559
+ remote: publication.grant.remote,
560
+ base: publication.grant.base,
561
+ headPrefix: publication.grant.headPrefix,
562
+ selector: publication.grant.selector,
563
+ draft: publication.grant.draft,
564
+ head: publication.head,
565
+ },
566
+ };
567
+ return createHash("sha256").update(JSON.stringify(tuple)).digest("hex");
568
+ }
569
+ /**
570
+ * One derivation for both moments — the POST that mints the nonce and the
571
+ * POST that consumes it. Publishability is decided here with exactly the
572
+ * tick path's predicate: committed real change, live grant, branch under
573
+ * the grant's prefix, selector satisfied.
574
+ */
575
+ export function computePickPlan(store, view, contestantId, repo, refOrigin) {
576
+ const chosen = view.agents.find(agent => agent.contestant.id === contestantId);
577
+ if (chosen === undefined || !chosen.pickable || chosen.run === null) {
578
+ return { ok: false, message: chosen?.unpickableReason ?? "that agent cannot be picked" };
579
+ }
580
+ const run = chosen.run;
581
+ const grant = repo === null ? null : store.publicationGrantFor(repo);
582
+ const committed = run.outcome === "built" && run.committed === true;
583
+ // Contest runs are builders — a branchless (reviewer) row can never be
584
+ // picked, and the null check keeps that a typed fact, not an assumption.
585
+ const publishable = committed &&
586
+ grant !== null &&
587
+ run.headRevision !== null &&
588
+ run.branch !== null &&
589
+ headWithin(run.branch, grant.headPrefix) &&
590
+ (grant.selector === "all" || refOrigin === "ours");
591
+ const digest = pickTupleDigest(view, { contestantId }, { grant: publishable ? grant : null, head: publishable ? run.branch : null });
592
+ return { ok: true, chosen: { ...chosen, run }, grant, publishable, digest };
593
+ }
594
+ export function nonceHashOf(value) {
595
+ return createHash("sha256").update(value).digest("hex");
596
+ }
597
+ /**
598
+ * The pick, one transaction (round-1 finding 5 + round-3 findings 8/21):
599
+ * nonce consumed conditionally against the REBUILT tuple digest, contest
600
+ * CAS'd, winner proven again byte-for-byte, losers marked for cleanup,
601
+ * the hold lifted, the task done, and the publication intent created —
602
+ * now and only now — under exactly the tick's own predicate.
603
+ */
604
+ export function finalizeContestPick(store, args, now) {
605
+ return store.transact(() => {
606
+ const view = buildPickView(store, args.evidenceRoot, args.contestId);
607
+ if (view === null || view.contest.state !== "pick-wait") {
608
+ return { ok: false, reason: "not-waiting", message: "this tournament is not waiting for a pick" };
609
+ }
610
+ const plan = computePickPlan(store, view, args.contestantId, args.repo, args.refOrigin);
611
+ if (!plan.ok) {
612
+ return { ok: false, reason: "unpickable", message: plan.message };
613
+ }
614
+ const { chosen, grant, publishable, digest } = plan;
615
+ if (!store.consumeCeremonyNonce(nonceHashOf(args.nonceValue), args.approver, "contest-pick", args.contestId, digest, now)) {
616
+ return { ok: false, reason: "stale", message: "the evidence changed since you read it, or this screen was already used — look again" };
617
+ }
618
+ // The winner's evidence, proved AGAIN inside the transaction: the nonce
619
+ // said "these bytes"; the pick lands only if they are still those bytes.
620
+ if (chosen.diff === null || !readVerifiedArtifact(args.evidenceRoot, chosen.diff).ok) {
621
+ return { ok: false, reason: "evidence", message: "the winner's evidence no longer verifies" };
622
+ }
623
+ if (!store.casContestState(args.contestId, ["pick-wait"], "picked", view.contest.generation)) {
624
+ return { ok: false, reason: "raced", message: "somebody else picked first" };
625
+ }
626
+ store.setContestWinner(args.contestId, args.contestantId, args.approver, now);
627
+ for (const agent of view.agents)
628
+ store.setContestantCleanup(agent.contestant.id, "pending");
629
+ store.releaseOwnedHold("contest", String(args.contestId));
630
+ const done = store.setTaskState(args.taskId, "done", now);
631
+ // With mirrors barred from tournaments (D1), a refusal here is a bug
632
+ // by construction — surface it, never publish over it.
633
+ if (!done.ok)
634
+ throw new Error(`the picked task could not be marked done (${done.reason})`);
635
+ let intent = null;
636
+ if (publishable && grant !== null && chosen.run.headRevision !== null && chosen.run.branch !== null) {
637
+ intent = store.createPublicationIntent({
638
+ run: chosen.run.id,
639
+ taskRef: view.contest.taskRef,
640
+ githubRepo: grant.githubRepo,
641
+ remote: grant.remote,
642
+ base: grant.base,
643
+ head: chosen.run.branch,
644
+ headSha: chosen.run.headRevision,
645
+ bodyHash: "",
646
+ draft: grant.draft,
647
+ }, now);
648
+ }
649
+ return { ok: true, state: "picked", publicationIntent: intent };
650
+ });
651
+ }
652
+ /** Abandon: password + nonce like the pick (it cancels paid-for work and
653
+ * re-opens the task), everything retained, nothing published. */
654
+ export function abandonContest(store, args, now) {
655
+ return store.transact(() => {
656
+ const view = buildPickView(store, args.evidenceRoot, args.contestId);
657
+ if (view === null || !["pick-wait", "exhausted", "interrupted", "decision-wait"].includes(view.contest.state)) {
658
+ return { ok: false, reason: "not-open", message: "this tournament is not in a state an operator can abandon" };
659
+ }
660
+ const digest = pickTupleDigest(view, { abandon: true }, { grant: null, head: null });
661
+ if (!store.consumeCeremonyNonce(nonceHashOf(args.nonceValue), args.approver, "contest-abandon", args.contestId, digest, now)) {
662
+ return { ok: false, reason: "stale", message: "the tournament changed since you read it — look again" };
663
+ }
664
+ if (!store.casContestState(args.contestId, [view.contest.state], "abandoned", view.contest.generation)) {
665
+ return { ok: false, reason: "raced", message: "the tournament moved first" };
666
+ }
667
+ for (const agent of view.agents)
668
+ store.setContestantCleanup(agent.contestant.id, "pending");
669
+ store.releaseOwnedHold("contest", String(args.contestId));
670
+ store.setTaskState(args.taskId, "failed", now);
671
+ return { ok: true };
672
+ });
673
+ }
674
+ // ---------------------------------------------------------------- stage 6
675
+ /** Fourteen days: the escalation threshold for an undecided tournament. */
676
+ export const CONTEST_OVERDUE_MS = 14 * 24 * 60 * 60_000;
677
+ /**
678
+ * The cleanup pass (stage 6): once a tournament is DECIDED — picked or
679
+ * abandoned — every agent's checkout goes back to the pool, winners and
680
+ * losers alike (branches and evidence survive; a checkout is only a
681
+ * checkout). Custody holds: only the runner that owns a checkout releases
682
+ * it, and one that will not release cleanly (dirty, uninspectable) is
683
+ * marked for attention and paged once rather than cleaned by force.
684
+ */
685
+ export async function sweepContestCleanup(store, releaseWorktree, runner, now) {
686
+ let released = 0;
687
+ let attention = 0;
688
+ for (const contestant of store.contestantsForCleanup()) {
689
+ if (contestant.worktree === null) {
690
+ store.setContestantCleanup(contestant.id, "done");
691
+ continue;
692
+ }
693
+ const row = store.getWorktree(contestant.worktree);
694
+ if (row === null) {
695
+ // The pool never knew it, or the row is gone (reimage): nothing to
696
+ // release, nothing to keep waiting on.
697
+ store.setContestantCleanup(contestant.id, "done");
698
+ continue;
699
+ }
700
+ if (row.runner !== null && row.runner !== runner)
701
+ continue; // another machine's custody
702
+ const freed = await releaseWorktree(contestant.worktree);
703
+ if (freed.ok) {
704
+ store.setContestantCleanup(contestant.id, "done");
705
+ released += 1;
706
+ continue;
707
+ }
708
+ store.setContestantCleanup(contestant.id, "attention");
709
+ attention += 1;
710
+ store.enqueueNotification({
711
+ source: { taskRef: store.getContest(contestant.contest)?.taskRef ?? -1 },
712
+ dedupeKey: `contest-cleanup:${contestant.id}`,
713
+ kind: "contest-cleanup",
714
+ subject: `a tournament checkout needs a look: agent ${contestant.ordinal}`,
715
+ body: `The checkout at ${contestant.worktree} would not go back to the pool` +
716
+ ` (${freed.message ?? freed.reason ?? "unknown"}). It was kept, not cleaned — look before anything is removed.`,
717
+ }, now);
718
+ }
719
+ return { released, attention };
720
+ }
721
+ /**
722
+ * Overdue escalation (stage 6): a tournament waiting on a person for
723
+ * fourteen days pages ONCE more — never auto-abandons, never re-pages.
724
+ * The age is derived from the moment the contest-owned hold took the
725
+ * task's stillness, which is exactly when the waiting began.
726
+ */
727
+ export function escalateOverdueContests(store, now) {
728
+ let paged = 0;
729
+ for (const contest of store.contestsInStates(["pick-wait", "exhausted", "interrupted"])) {
730
+ if (contest.overduePaged)
731
+ continue;
732
+ const hold = store
733
+ .activeHolds(contest.taskRef, now)
734
+ .find(one => one.ownerKind === "contest" && one.ownerId === String(contest.id));
735
+ const since = hold?.heldAt ?? contest.createdAt;
736
+ if (now.getTime() - new Date(since).getTime() < CONTEST_OVERDUE_MS)
737
+ continue;
738
+ if (!store.markContestOverduePaged(contest.id))
739
+ continue; // someone else paged first
740
+ const taskId = store.externalIdFor(contest.taskRef) ?? `ref ${contest.taskRef}`;
741
+ const days = Math.floor((now.getTime() - new Date(since).getTime()) / 86_400_000);
742
+ store.enqueueNotification({
743
+ source: { taskRef: contest.taskRef },
744
+ dedupeKey: `contest-overdue:${contest.id}`,
745
+ kind: "contest-overdue",
746
+ subject: `a tournament has waited ${days} days: ${taskId}`,
747
+ body: `The tournament on ${taskId} has been waiting for your decision for ${days} days. ` +
748
+ `Nothing happens on its own — compare and decide at /contest/${contest.id}, or abandon it there.`,
749
+ }, now);
750
+ paged += 1;
751
+ }
752
+ return paged;
753
+ }