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
package/dist/backup.js ADDED
@@ -0,0 +1,421 @@
1
+ /**
2
+ * Backups (sprint 8): the database, copied on a schedule with SQLite's online
3
+ * backup (one read snapshot, so the console and workers keep writing), into
4
+ * a folder the operator chooses (by default `backups` beside the database),
5
+ * keeping the newest N. Each backup is checked (it opens, it passes SQLite's
6
+ * quick check) before it counts, and each one's outcome is kept.
7
+ *
8
+ * Only the database is copied. Provider keys, sign-in files and other secrets
9
+ * live in their own files beside it and are never part of a backup.
10
+ *
11
+ * `restore` puts a backup back, only with Toolroll stopped: it checks
12
+ * the backup's schema version and walks its ledger chain first, keeps the
13
+ * current database as a copy, and with --dry-run changes nothing.
14
+ */
15
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
16
+ import { chmodSync, closeSync, constants, copyFileSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readdirSync, realpathSync, renameSync, rmSync, statSync } from "node:fs";
17
+ import { hostname } from "node:os";
18
+ import { basename, dirname, isAbsolute, join, resolve } from "node:path";
19
+ import { createRequire } from "node:module";
20
+ import { BACKUP_EVERY_HOURS, MAX_KEEP } from "./backup-ui.js";
21
+ import { verifyLedgerChain } from "./ledger-chain.js";
22
+ import { openStore, readSchemaVersion, SCHEMA_VERSION } from "./store.js";
23
+ /** A scheduled backup's name: the database it came from (see `databaseTag`), then when it was made. Backups made
24
+ * before the tag was added have none. Retention only ever removes files named like this. */
25
+ const BACKUP_NAME = /^standing-orders-(?:([0-9a-f]{8})-)?(\d{4}-\d{2}-\d{2}-\d{6})(?:-(\d+))?\.db$/;
26
+ /** The oldest schema whose ledger has a hash chain to verify. */
27
+ export const OLDEST_RESTORABLE = 103;
28
+ export { BACKUP_EVERY_HOURS, MAX_KEEP };
29
+ /** How long the console's lease lasts without a renewal: a console that stopped holds it no longer than this. */
30
+ const LEASE_MS = 3 * 60_000;
31
+ /** A worker that heartbeat this recently is taken to be running. */
32
+ const RUNNER_FRESH_MS = 3 * 60_000;
33
+ export const defaultBackupFolder = (databaseFile) => join(dirname(databaseFile), "backups");
34
+ export const backupFolderOf = (settings, databaseFile) => settings.folder ?? defaultBackupFolder(databaseFile);
35
+ /** 2026-09-28T05:34:39.123Z → 2026-09-28-053439 (UTC). */
36
+ const stampOf = (now) => now.toISOString().replace(/\.\d+Z$/, "").replace("T", "-").replace(/:/g, "");
37
+ const message = (error) => error instanceof Error ? error.message : String(error);
38
+ /** Loaded on first use, as store.ts does, so modules that only import this one (the console, and tests that load it
39
+ * in a browser-like environment) never need `node:sqlite` itself. */
40
+ function sqlite() {
41
+ return createRequire(import.meta.url)("node:sqlite");
42
+ }
43
+ function connect(file) {
44
+ const db = new (sqlite().DatabaseSync)(file);
45
+ db.exec("PRAGMA busy_timeout = 5000");
46
+ return db;
47
+ }
48
+ /** Copy `sourceFile` into `target` with the online backup (all pages in one step: one consistent snapshot, and
49
+ * writers carry on meanwhile), then make the copy one self-contained file and check it. */
50
+ export async function onlineCopy(sourceFile, target) {
51
+ const source = connect(sourceFile);
52
+ // Every page in one step (SQLite's -1 means that too, but newer Node builds take only a positive rate).
53
+ try {
54
+ await sqlite().backup(source, target, { rate: 1_000_000 });
55
+ }
56
+ finally {
57
+ source.close();
58
+ }
59
+ chmodSync(target, 0o600);
60
+ const copy = connect(target);
61
+ let schemaVersion;
62
+ try {
63
+ copy.exec("PRAGMA journal_mode = DELETE");
64
+ const check = copy.prepare("PRAGMA quick_check").get();
65
+ if (check?.["quick_check"] !== "ok")
66
+ throw new Error(`the copy failed SQLite's check (${String(check?.["quick_check"] ?? "no answer")})`);
67
+ const version = readSchemaVersion(copy);
68
+ schemaVersion = version.ok ? version.version : null;
69
+ }
70
+ finally {
71
+ copy.close();
72
+ }
73
+ const handle = openSync(target, "r");
74
+ try {
75
+ fsyncSync(handle);
76
+ }
77
+ finally {
78
+ closeSync(handle);
79
+ }
80
+ return { bytes: statSync(target).size, schemaVersion };
81
+ }
82
+ /** A folder the operator may choose: a full path, outside every project and build checkout (agents work there). */
83
+ export function checkBackupFolder(path, forbidden) {
84
+ const folder = path.trim();
85
+ if (!isAbsolute(folder) || folder.length > 500 || /[\u0000-\u001f]/.test(folder))
86
+ return "The folder is a full path, like /Volumes/Backup/standing-orders.";
87
+ const at = resolve(folder);
88
+ const inside = (one, parent) => one === parent || one.startsWith(`${parent}/`);
89
+ if (forbidden.some(one => inside(at, resolve(one)) || inside(resolve(one), at)))
90
+ return "Choose a folder outside your projects and build checkouts: agents work there.";
91
+ return null;
92
+ }
93
+ /** A short tag for one database, from its full path, so databases that share a backup folder tell their backups apart. */
94
+ export function databaseTag(databaseFile) {
95
+ let path = resolve(databaseFile);
96
+ try {
97
+ path = realpathSync(path);
98
+ }
99
+ catch { /* not there yet: its path as given */ }
100
+ return createHash("sha256").update(path).digest("hex").slice(0, 8);
101
+ }
102
+ export function backupOwner(store, databaseFile) {
103
+ const made = store.handle.prepare("SELECT file FROM backup_run WHERE ok = 1 AND file IS NOT NULL").all().map(row => resolve(String(row["file"])));
104
+ return { tag: databaseTag(databaseFile), made: new Set(made) };
105
+ }
106
+ /** This database's backups in a folder, made on a schedule or by `backup now`, newest first. A backup tagged for
107
+ * another database is never this one's. An untagged one (made before backups were tagged) is this one's only when its
108
+ * own history records making it: pruning deletes, so what can't be proved ours is left alone. */
109
+ export function backupFiles(folder, owner) {
110
+ let names;
111
+ try {
112
+ names = readdirSync(folder);
113
+ }
114
+ catch {
115
+ return [];
116
+ }
117
+ const named = names.flatMap(name => { const m = BACKUP_NAME.exec(name); return m === null ? [] : [{ name, tag: m[1] ?? null, order: `${m[2]}-${(m[3] ?? "1").padStart(6, "0")}` }]; });
118
+ return named
119
+ .filter(one => one.tag === owner.tag || (one.tag === null && owner.made.has(resolve(folder, one.name))))
120
+ .sort((a, b) => a.order < b.order ? 1 : a.order > b.order ? -1 : a.name < b.name ? 1 : -1)
121
+ .flatMap(({ name }) => {
122
+ const path = join(folder, name);
123
+ try {
124
+ const stat = lstatSync(path);
125
+ return stat.isFile() ? [{ name, path, bytes: stat.size }] : [];
126
+ }
127
+ catch {
128
+ return [];
129
+ }
130
+ });
131
+ }
132
+ /** Remove all but this database's newest `keep` backups. Never touches another database's, or anything else in the folder. */
133
+ export function pruneBackups(folder, keep, owner) {
134
+ let removed = 0;
135
+ for (const one of backupFiles(folder, owner).slice(Math.max(1, keep))) {
136
+ try {
137
+ rmSync(one.path);
138
+ removed++;
139
+ }
140
+ catch { /* the next backup tries again */ }
141
+ }
142
+ return removed;
143
+ }
144
+ /** A failure in plain words: the common file-system ones by name, anything else as SQLite or Node said it. */
145
+ function failureWords(error, folder) {
146
+ const code = error?.code;
147
+ if (code === "EACCES" || code === "EPERM" || code === "EROFS")
148
+ return `Toolroll isn't allowed to write to ${folder}.`;
149
+ if (code === "ENOSPC" || code === "EDQUOT")
150
+ return `The disk holding ${folder} is full.`;
151
+ if (code === "ENOTDIR" || code === "EEXIST")
152
+ return `${folder} isn't a folder.`;
153
+ return message(error);
154
+ }
155
+ /** Back the database up now, keep the newest N, and record how it went. */
156
+ export async function backupNow(store, databaseFile, trigger, clock = () => new Date()) {
157
+ const settings = store.backupSettings();
158
+ const folder = backupFolderOf(settings, databaseFile);
159
+ const id = store.startBackupRun(trigger, clock());
160
+ let partial = null;
161
+ try {
162
+ mkdirSync(folder, { recursive: true, mode: 0o700 });
163
+ const stamp = `${databaseTag(databaseFile)}-${stampOf(clock())}`;
164
+ let name = `standing-orders-${stamp}.db`;
165
+ for (let n = 2; existsSync(join(folder, name)); n++)
166
+ name = `standing-orders-${stamp}-${n}.db`;
167
+ partial = join(folder, `.${name}.${randomBytes(4).toString("hex")}.partial`);
168
+ const made = await onlineCopy(databaseFile, partial);
169
+ const file = join(folder, name);
170
+ renameSync(partial, file);
171
+ partial = null;
172
+ const removed = pruneBackups(folder, settings.keep, backupOwner(store, databaseFile));
173
+ store.finishBackupRun(id, { ok: true, file, bytes: made.bytes, schemaVersion: made.schemaVersion, removed }, clock());
174
+ return { ok: true, id, file, bytes: made.bytes, removed };
175
+ }
176
+ catch (error) {
177
+ if (partial !== null)
178
+ rmSync(partial, { force: true });
179
+ const words = failureWords(error, folder);
180
+ store.finishBackupRun(id, { ok: false, error: words }, clock());
181
+ if (trigger === "scheduled") {
182
+ const day = clock().toISOString().slice(0, 10);
183
+ store.enqueueNotification({ dedupeKey: `backup-failed:${day}`, kind: "backup-failed", pushClass: "attention", link: "/settings/backups",
184
+ subject: "A backup failed", body: `The scheduled backup didn't finish: ${words.slice(0, 200)}`, source: { installation: true } }, clock());
185
+ }
186
+ return { ok: false, id, error: words };
187
+ }
188
+ }
189
+ /** Whether a scheduled backup is due: the last good one is older than the schedule, and the last try over an hour ago. */
190
+ export function backupDue(store, now) {
191
+ const settings = store.backupSettings();
192
+ if (!settings.enabled)
193
+ return false;
194
+ const runs = store.backupRuns(50);
195
+ const last = runs[0];
196
+ if (last !== undefined && last.finishedAt === null && now.getTime() - Date.parse(last.startedAt) < 60 * 60_000)
197
+ return false;
198
+ const good = runs.find(one => one.ok === true);
199
+ const every = settings.everyHours * 60 * 60_000;
200
+ if (good !== undefined && now.getTime() - Date.parse(good.startedAt) < every)
201
+ return false;
202
+ return last === undefined || now.getTime() - Date.parse(last.startedAt) >= Math.min(every, 60 * 60_000);
203
+ }
204
+ /** One pass of the loop beside the console: renew the lease (a restore sees it), and back up when due. */
205
+ export async function backupPass(store, databaseFile, holder, clock = () => new Date()) {
206
+ const now = clock();
207
+ if (!store.holdBackupLease(holder, now, new Date(now.getTime() + LEASE_MS)))
208
+ return null;
209
+ if (!backupDue(store, now))
210
+ return null;
211
+ return backupNow(store, databaseFile, "scheduled", clock);
212
+ }
213
+ /** The loop beside the console: a pass a minute. */
214
+ export function startBackups(store, databaseFile, everyMs = 60_000) {
215
+ const holder = `${hostname()}:${process.pid}:${randomUUID().slice(0, 8)}`;
216
+ let stopped = false, timer = null;
217
+ const pass = async () => {
218
+ if (stopped)
219
+ return;
220
+ try {
221
+ await backupPass(store, databaseFile, holder);
222
+ }
223
+ catch { /* the next pass tries again */ }
224
+ if (!stopped) {
225
+ timer = setTimeout(() => void pass(), everyMs);
226
+ timer.unref?.();
227
+ }
228
+ };
229
+ timer = setTimeout(() => void pass(), 2_000);
230
+ timer.unref?.();
231
+ return () => {
232
+ stopped = true;
233
+ if (timer !== null)
234
+ clearTimeout(timer);
235
+ try {
236
+ store.releaseBackupLease(holder);
237
+ }
238
+ catch { /* the lease runs out on its own */ }
239
+ };
240
+ }
241
+ /** What's running against the database now: the console (its backup lease), and workers (watch leases, runner heartbeats). */
242
+ export function runningNow(store, now) {
243
+ const running = [];
244
+ if (store.backupLease(now) !== null)
245
+ running.push("the console");
246
+ const watches = store.handle.prepare("SELECT COUNT(*) AS n FROM watch_lease WHERE expires_at >= ?").get(now.toISOString());
247
+ const fresh = new Date(now.getTime() - RUNNER_FRESH_MS).toISOString();
248
+ const runners = store.handle.prepare("SELECT name FROM runner WHERE retired_at IS NULL AND heartbeat_at >= ? ORDER BY name").all(fresh).map(row => String(row["name"]));
249
+ if (Number(watches?.["n"] ?? 0) > 0 || runners.length > 0)
250
+ running.push(runners.length === 0 ? "a worker" : `the worker ${runners.join(", ")}`);
251
+ return running;
252
+ }
253
+ /**
254
+ * Put a backup back in place of the database. Refuses while Toolroll is running, when the backup isn't a
255
+ * Toolroll database this build can read (its schema version), or when its ledger chain doesn't verify. The
256
+ * backup is copied beside the database first and every check runs on that copy, so what was checked is what goes in.
257
+ */
258
+ export async function restoreDatabase(options) {
259
+ const { databaseFile, dryRun, now } = options;
260
+ const file = resolve(options.file);
261
+ const report = { ok: false, dryRun, file, refusals: [], schemaVersion: null, ledger: null, savedAs: null };
262
+ const refuse = (reason, words) => { report.refusals.push({ reason, words }); return report; };
263
+ let stat;
264
+ try {
265
+ stat = lstatSync(file);
266
+ }
267
+ catch {
268
+ return refuse("not-found", `There's no file at ${file}.`);
269
+ }
270
+ if (!stat.isFile())
271
+ return refuse("not-found", `${file} isn't a plain file.`);
272
+ if (existsSync(databaseFile) && resolve(databaseFile) === file)
273
+ return refuse("same-file", "That's the database in use now, not a backup.");
274
+ // What's running, and where the copy of the current database goes, from the database as it is.
275
+ let folder;
276
+ {
277
+ const store = openStore(databaseFile);
278
+ try {
279
+ const running = runningNow(store, now);
280
+ if (running.length > 0)
281
+ refuse("running", `Toolroll is running (${running.join(" and ")}). Stop it first, then restore. If it has just stopped, wait a few minutes.`);
282
+ folder = backupFolderOf(store.backupSettings(), databaseFile);
283
+ }
284
+ finally {
285
+ store.close();
286
+ }
287
+ }
288
+ const staged = join(dirname(databaseFile), `.restore-${randomBytes(6).toString("hex")}.db`);
289
+ let keepStaged = false;
290
+ try {
291
+ copyFileSync(file, staged, constants.COPYFILE_EXCL);
292
+ chmodSync(staged, 0o600);
293
+ if (existsSync(`${file}-wal`) && statSync(`${file}-wal`).size > 0) {
294
+ return refuse("unreadable", "That backup still has changes in its -wal file beside it. Open it once with sqlite3 to fold them in, or choose another backup.");
295
+ }
296
+ let db;
297
+ try {
298
+ db = connect(staged);
299
+ }
300
+ catch {
301
+ return refuse("unreadable", "That file isn't a database Toolroll can open.");
302
+ }
303
+ try {
304
+ let check;
305
+ try {
306
+ check = String(db.prepare("PRAGMA quick_check").get()?.["quick_check"] ?? "");
307
+ }
308
+ catch {
309
+ check = "not a database";
310
+ }
311
+ if (check !== "ok")
312
+ return refuse("unreadable", `That file isn't a sound SQLite database (${check}).`);
313
+ const version = readSchemaVersion(db);
314
+ const schema = version.ok ? version.version : null;
315
+ report.schemaVersion = schema;
316
+ if (schema === null)
317
+ return refuse("schema", "That file isn't a Toolroll database: it has no schema version.");
318
+ if (schema > SCHEMA_VERSION)
319
+ return refuse("schema", `That backup was made by a newer Toolroll (schema ${schema}; this one reads up to ${SCHEMA_VERSION}). Restore it with that version.`);
320
+ if (schema < OLDEST_RESTORABLE)
321
+ return refuse("schema", `That backup is from schema ${schema}, before the ledger had a hash chain to check. Restore it with the Toolroll that made it.`);
322
+ const chain = verifyLedgerChain(db);
323
+ report.ledger = { entries: chain.entries, through: chain.through };
324
+ if (!chain.ok)
325
+ return refuse("ledger", `That backup's ledger chain doesn't verify: ${chain.problem?.what ?? "it breaks"}.`);
326
+ db.exec("PRAGMA journal_mode = DELETE");
327
+ }
328
+ finally {
329
+ db.close();
330
+ }
331
+ // It must open as this build's database (every table it needs, every step up to this schema) before it may replace
332
+ // the live one: a sound file with the right version can still be missing what Toolroll reads.
333
+ if (report.refusals.length === 0) {
334
+ const probe = `${staged}.probe`;
335
+ try {
336
+ copyFileSync(staged, probe);
337
+ try {
338
+ openStore(probe).close();
339
+ }
340
+ catch (error) {
341
+ refuse("schema", `That backup doesn't open as a Toolroll database (${firstLine(error)}).`);
342
+ }
343
+ }
344
+ finally {
345
+ for (const suffix of ["", "-wal", "-shm", "-journal"])
346
+ rmSync(`${probe}${suffix}`, { force: true });
347
+ }
348
+ }
349
+ if (report.refusals.length > 0 || dryRun) {
350
+ report.ok = report.refusals.length === 0;
351
+ return report;
352
+ }
353
+ // Keep the database as it is now, checked like any backup, before anything replaces it.
354
+ mkdirSync(folder, { recursive: true, mode: 0o700 });
355
+ const saved = join(folder, `before-restore-${stampOf(now)}-${randomBytes(3).toString("hex")}.db`);
356
+ const partial = `${saved}.partial`;
357
+ try {
358
+ await onlineCopy(databaseFile, partial);
359
+ renameSync(partial, saved);
360
+ }
361
+ catch (error) {
362
+ rmSync(partial, { force: true });
363
+ throw new Error(`the current database couldn't be saved first, so nothing was replaced (${message(error)})`);
364
+ }
365
+ report.savedAs = saved;
366
+ // Again, just before the swap: nothing may have started meanwhile.
367
+ const store = openStore(databaseFile);
368
+ try {
369
+ const running = runningNow(store, new Date());
370
+ if (running.length > 0)
371
+ return refuse("running", `Toolroll started (${running.join(" and ")}). Stop it first, then restore. The database is unchanged.`);
372
+ }
373
+ finally {
374
+ store.close();
375
+ }
376
+ // Fold the write-ahead log in and drop it, so nothing of the old database is left beside the new one.
377
+ const live = connect(databaseFile);
378
+ try {
379
+ live.exec("PRAGMA wal_checkpoint(TRUNCATE)");
380
+ live.exec("PRAGMA journal_mode = DELETE");
381
+ }
382
+ finally {
383
+ live.close();
384
+ }
385
+ for (const suffix of ["-wal", "-shm", "-journal"])
386
+ rmSync(`${databaseFile}${suffix}`, { force: true });
387
+ renameSync(staged, databaseFile);
388
+ keepStaged = true;
389
+ // The restored database brought up to this build, and the restore in its ledger. Should it still not open, the
390
+ // database it replaced goes back, and the restore says so.
391
+ let restored;
392
+ try {
393
+ restored = openStore(databaseFile);
394
+ }
395
+ catch (error) {
396
+ for (const suffix of ["", "-wal", "-shm", "-journal"])
397
+ rmSync(`${databaseFile}${suffix}`, { force: true });
398
+ copyFileSync(saved, databaseFile);
399
+ chmodSync(databaseFile, 0o600);
400
+ report.savedAs = null;
401
+ return refuse("unreadable", `The restored database didn't open (${firstLine(error)}), so the one it replaced was put back as it was.`);
402
+ }
403
+ try {
404
+ restored.recordAction({ at: now.toISOString(), actor: options.actor ?? "command line", repo: null, taskId: null, runId: null, action: "database restored", outcome: "restored", source: "policy",
405
+ detail: `from ${basename(file)} (schema ${report.schemaVersion}); the database before it was kept as ${basename(saved)}` });
406
+ }
407
+ finally {
408
+ restored.close();
409
+ }
410
+ report.ok = true;
411
+ return report;
412
+ }
413
+ finally {
414
+ if (!keepStaged)
415
+ rmSync(staged, { force: true });
416
+ }
417
+ }
418
+ /** An error's first line, for a sentence. */
419
+ function firstLine(error) {
420
+ return (error instanceof Error ? error.message : String(error)).split("\n")[0].slice(0, 200);
421
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * beads, through the `bd` binary already on the machine.
3
+ *
4
+ * Every command here was read out of beads' own documentation rather than
5
+ * guessed at, and the ones that were not are refused instead of attempted.
6
+ * That distinction is the whole design of this file, so it is written down
7
+ * per call:
8
+ *
9
+ * bd ready --json confirmed — the ready set, which is the same
10
+ * question `list_ready` asks
11
+ * bd create "<title>" confirmed
12
+ * bd close <id> confirmed
13
+ * bd dep add <a> <b> confirmed — native edges, so they are used
14
+ * bd show <id> --json the subcommand is confirmed, the --json flag is not
15
+ * bd update --status NOT confirmed; only `--claim` appears in the docs
16
+ *
17
+ * Where the flag is unconfirmed the call parses defensively and fails closed
18
+ * with a message naming what could not be established. Writing a plausible
19
+ * flag into somebody's issue tracker and finding out at 3am whether it meant
20
+ * what we hoped is the exact failure this project exists to avoid.
21
+ *
22
+ * Unlike the GitHub adapter, none of this has been exercised against a real
23
+ * installation — `bd` is not present on the machine it was written on. It is
24
+ * built to the documentation, tested against a stubbed runner, and should be
25
+ * treated as unproven until somebody runs it with beads actually installed.
26
+ */
27
+ import { type GraphBackend, type Runner } from "./backend.js";
28
+ /** Local work, so a local bound. */
29
+ export declare const BEADS_TIMEOUT_MS = 10000;
30
+ export type BeadsOptions = {
31
+ repo: string;
32
+ runner?: Runner;
33
+ };
34
+ export declare function beads(options: BeadsOptions): GraphBackend;
package/dist/beads.js ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * beads, through the `bd` binary already on the machine.
3
+ *
4
+ * Every command here was read out of beads' own documentation rather than
5
+ * guessed at, and the ones that were not are refused instead of attempted.
6
+ * That distinction is the whole design of this file, so it is written down
7
+ * per call:
8
+ *
9
+ * bd ready --json confirmed — the ready set, which is the same
10
+ * question `list_ready` asks
11
+ * bd create "<title>" confirmed
12
+ * bd close <id> confirmed
13
+ * bd dep add <a> <b> confirmed — native edges, so they are used
14
+ * bd show <id> --json the subcommand is confirmed, the --json flag is not
15
+ * bd update --status NOT confirmed; only `--claim` appears in the docs
16
+ *
17
+ * Where the flag is unconfirmed the call parses defensively and fails closed
18
+ * with a message naming what could not be established. Writing a plausible
19
+ * flag into somebody's issue tracker and finding out at 3am whether it meant
20
+ * what we hoped is the exact failure this project exists to avoid.
21
+ *
22
+ * Unlike the GitHub adapter, none of this has been exercised against a real
23
+ * installation — `bd` is not present on the machine it was written on. It is
24
+ * built to the documentation, tested against a stubbed runner, and should be
25
+ * treated as unproven until somebody runs it with beads actually installed.
26
+ */
27
+ import { run } from "./exec.js";
28
+ import { ok, no, describeFailure, parseJson, } from "./backend.js";
29
+ const BD = "bd";
30
+ /** Local work, so a local bound. */
31
+ export const BEADS_TIMEOUT_MS = 10_000;
32
+ export function beads(options) {
33
+ const { repo, runner = run } = options;
34
+ const call = (args) => runner(BD, args, { cwd: repo, timeoutMs: BEADS_TIMEOUT_MS });
35
+ return {
36
+ name: "beads",
37
+ // Confirmed native, and the only backend here with edges we can write.
38
+ deps: "native",
39
+ async listReady() {
40
+ const result = await call(["ready", "--json"]);
41
+ if (result.code !== 0)
42
+ return no("unreachable", describeFailure(result));
43
+ const parsed = parseJson(result.stdout);
44
+ if (!Array.isArray(parsed))
45
+ return no("unreadable", "bd returned something that is not a list");
46
+ return ok(parsed.map(readIssue).filter((task) => task !== null));
47
+ },
48
+ async get(id) {
49
+ const result = await call(["show", id, "--json"]);
50
+ if (result.code !== 0)
51
+ return no("unreachable", describeFailure(result));
52
+ const task = readIssue(parseJson(result.stdout));
53
+ if (task === null) {
54
+ return no("unreadable", "bd show did not return JSON — this build may not support --json on show");
55
+ }
56
+ return ok(task);
57
+ },
58
+ /**
59
+ * `bd create` is confirmed; what it prints is not. The id is read out of
60
+ * its output if it is there, and the absence of one is reported rather
61
+ * than filled in: a caller holding an id we invented would attach claims
62
+ * and leases to a task that does not exist.
63
+ */
64
+ async create(spec) {
65
+ const result = await call(["create", spec.title, "--json"]);
66
+ if (result.code !== 0)
67
+ return no("rejected", describeFailure(result));
68
+ const id = readCreatedId(result.stdout);
69
+ if (id === null) {
70
+ return no("unreadable", "bd created the task but did not print an id we could read — check `bd ready` for it");
71
+ }
72
+ return ok(id);
73
+ },
74
+ /**
75
+ * Only closing is confirmed. Everything else would need `bd update` with a
76
+ * status flag that this adapter has not established exists, and guessing
77
+ * at it is how a queue quietly fills with tasks in a state nobody meant.
78
+ */
79
+ async setState(id, state) {
80
+ if (state !== "done" && state !== "cancelled" && state !== "failed") {
81
+ return no("unsupported", `beads: only closing is confirmed here, so \`${state}\` is not applied — the flag for it has not been established`);
82
+ }
83
+ const result = await call(["close", id]);
84
+ if (result.code !== 0)
85
+ return no("rejected", describeFailure(result));
86
+ return ok(undefined);
87
+ },
88
+ /** `bd dep add <child> <parent>`: the child waits for the parent. */
89
+ async addEdge(blocked, blocker) {
90
+ const result = await call(["dep", "add", blocked, blocker]);
91
+ if (result.code !== 0)
92
+ return no("rejected", describeFailure(result));
93
+ return ok(undefined);
94
+ },
95
+ };
96
+ }
97
+ /** beads ids are its own; only their shape is assumed, never their meaning. */
98
+ function readIssue(record) {
99
+ if (typeof record !== "object" || record === null)
100
+ return null;
101
+ const raw = record;
102
+ const id = raw["id"];
103
+ if (typeof id !== "string" && typeof id !== "number")
104
+ return null;
105
+ return {
106
+ id: String(id),
107
+ title: typeof raw["title"] === "string" ? raw["title"] : "",
108
+ state: readState(raw["status"] ?? raw["state"]),
109
+ };
110
+ }
111
+ /**
112
+ * beads' status vocabulary is not documented in full, so this maps the values
113
+ * that are, and treats anything unrecognised as queued rather than inventing a
114
+ * meaning for it. Being wrong toward "still to do" leaves work visible; being
115
+ * wrong toward "done" loses it.
116
+ */
117
+ function readState(value) {
118
+ const text = String(value ?? "").toLowerCase();
119
+ if (text === "closed" || text === "done")
120
+ return "done";
121
+ if (text === "in_progress" || text === "in-progress")
122
+ return "running";
123
+ return "queued";
124
+ }
125
+ function readCreatedId(stdout) {
126
+ const parsed = parseJson(stdout);
127
+ if (typeof parsed === "object" && parsed !== null) {
128
+ const id = parsed["id"];
129
+ if (typeof id === "string" || typeof id === "number")
130
+ return String(id);
131
+ }
132
+ // Falls back to the id-shaped token beads prints in its examples (bd-a1b2).
133
+ const printed = /\b(bd-[a-z0-9]+)\b/i.exec(stdout);
134
+ return printed?.[1] ?? null;
135
+ }
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/bin.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The package bin (install review). Nothing here touches the store: this
4
+ * file exists so the runtime is checked BEFORE `node:sqlite` is imported,
5
+ * and the answer to "why did it die" is one sentence instead of a resolver
6
+ * stack. Bun's runtime has no node:sqlite; `bunx toolroll …`
7
+ * (without --bun) hands the shebang to Node and works.
8
+ */
9
+ const versions = process.versions;
10
+ const [major = 0, minor = 0] = versions.node.split(".").map(Number);
11
+ if (versions.bun !== undefined) {
12
+ process.stderr.write("Toolroll runs on Node 22.13 or newer — Bun's runtime has no node:sqlite.\n" +
13
+ "Run it with Node: `bunx toolroll up` (no --bun) or `npx toolroll up`.\n");
14
+ process.exit(2);
15
+ }
16
+ if (major < 22 || (major === 22 && minor < 13)) {
17
+ process.stderr.write(`Toolroll needs Node 22.13 or newer (this is ${versions.node}) — it uses node:sqlite.\n`);
18
+ process.exit(2);
19
+ }
20
+ const { runCliProcess } = await import("./cli.js");
21
+ await runCliProcess(process.argv.slice(2));
22
+ export {};