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,226 @@
1
+ /**
2
+ * Restart certification (plan acceptance 7): a BASELINE of the boot, the
3
+ * runtime, the installed service and the database's live custody, taken
4
+ * before a login or reboot; and a VERIFICATION afterwards against the same
5
+ * database — boot identity as the OS reports it, service liveness by a
6
+ * fresh heartbeat, recovery of what the old boot left pending, task
7
+ * completion — without printing a secret and without rebooting anything.
8
+ *
9
+ * The precise limits, written into every baseline so nobody reads more
10
+ * into a pass than it proves:
11
+ * - macOS LaunchAgents resume at USER LOGIN after a reboot, not before
12
+ * FileVault unlock / login; a machine that sits at the login window
13
+ * runs nothing.
14
+ * - Linux user services depend on a login session or `loginctl
15
+ * enable-linger`; without linger the user manager stops at logout.
16
+ * - The Windows Task Scheduler logon trigger is a LOGON trigger, not a
17
+ * boot service.
18
+ * - This tool never reboots, logs out, or restarts a service itself. A
19
+ * physical reboot that has not happened is not claimed: `bootChanged`
20
+ * is the OS's own word, and `unknown` stays unknown.
21
+ */
22
+ import { hostname } from "node:os";
23
+ import { createHash, randomUUID } from "node:crypto";
24
+ import { dirname, join } from "node:path";
25
+ import { fileURLToPath } from "node:url";
26
+ import { existsSync, readFileSync, realpathSync, readdirSync, statSync } from "node:fs";
27
+ import { bootIdentity, currentBootId, provenDeadByBootChange } from "./boot-identity.js";
28
+ import { containmentStatus, currentContainment } from "./containment.js";
29
+ import { daemonStatus } from "./daemon.js";
30
+ import { isAlive } from "./runner.js";
31
+ import { SCHEMA_VERSION } from "./store.js";
32
+ export const RESTART_LIMITS = [
33
+ "macOS LaunchAgents resume at user login after a reboot, not before FileVault unlock/login; a machine at the login window runs nothing",
34
+ "Linux user services depend on a login session or loginctl enable-linger; without linger the user manager stops at logout",
35
+ "the Windows Task Scheduler logon trigger is a logon trigger, not a boot service",
36
+ "this tool never reboots, logs out, or restarts a service; bootChanged is the OS's own boot identity, and unknown stays unknown",
37
+ "a pass here certifies this database, this service label and this boot — not a physical reboot that did not happen",
38
+ "task recovery means earlier running attempts moved on; only explicitly named completion tasks must reach done",
39
+ ];
40
+ function bootFacts(identity) {
41
+ return identity.ok ? { id: identity.id, source: identity.source, problem: null } : { id: null, source: null, problem: identity.detail };
42
+ }
43
+ const memoryIdentities = new WeakMap();
44
+ function databaseIdentity(store) {
45
+ const file = store.raw().prepare("PRAGMA database_list").all().find(row => row["name"] === "main")?.["file"];
46
+ if (typeof file !== "string" || file === "") {
47
+ let id = memoryIdentities.get(store);
48
+ if (!id) {
49
+ id = randomUUID();
50
+ memoryIdentities.set(store, id);
51
+ }
52
+ return id;
53
+ }
54
+ const path = realpathSync(file), stat = statSync(path);
55
+ return createHash("sha256").update(JSON.stringify([path, stat.dev, stat.ino])).digest("hex");
56
+ }
57
+ export function restartRuntimeDigest(executable = process.execPath, root = dirname(fileURLToPath(import.meta.url))) {
58
+ const hash = createHash("sha256").update(readFileSync(executable));
59
+ for (const name of readdirSync(root).filter(name => /\.(js|ts|ps1)$/.test(name)).sort())
60
+ hash.update(name).update(readFileSync(join(root, name)));
61
+ return hash.digest("hex");
62
+ }
63
+ /** Everything a person needs to compare later, none of what they must not see. */
64
+ export async function recordRestartBaseline(inputs) {
65
+ const { store } = inputs;
66
+ const now = inputs.now ?? new Date();
67
+ const raw = store.raw();
68
+ const service = inputs.service === undefined ? null : { ...(await daemonStatus(inputs.service.definition, inputs.service.run)), label: inputs.service.definition.label, platform: inputs.service.definition.platform };
69
+ const pendingStops = raw.prepare("SELECT run, task_ref, requested_at FROM run_stop WHERE settled_at IS NULL ORDER BY run").all()
70
+ .map(row => ({ run: Number(row["run"]), taskRef: Number(row["task_ref"]), requestedAt: String(row["requested_at"]), problem: store.stopQuiescenceProblem(Number(row["run"])) }));
71
+ const openRuns = raw.prepare("SELECT id, task_ref, role, runner, lease_id FROM run WHERE outcome IS NULL ORDER BY id").all()
72
+ .map(row => ({ id: Number(row["id"]), taskRef: Number(row["task_ref"]), role: String(row["role"]), runner: String(row["runner"]), leaseId: String(row["lease_id"]) }));
73
+ const witnesses = raw.prepare("SELECT run, pid, host, boot_id, containment, exited_at FROM run_process WHERE exited_at IS NULL ORDER BY id").all()
74
+ .map(row => ({ run: Number(row["run"]), pid: row["pid"] === null ? null : Number(row["pid"]), host: String(row["host"]), bootId: row["boot_id"] === null ? null : String(row["boot_id"]), containment: row["containment"] === null ? null : String(row["containment"]), open: true }));
75
+ return {
76
+ version: 2,
77
+ runnerName: inputs.runnerName ?? null,
78
+ completionTasks: [...(inputs.completionTasks ?? [])],
79
+ recordedAt: now.toISOString(),
80
+ host: hostname(),
81
+ boot: bootFacts(inputs.boot ?? bootIdentity()),
82
+ runtime: { digest: (inputs.runtimeDigest ?? restartRuntimeDigest)(), nodeVersion: process.version, execPath: process.execPath, platform: process.platform, packageVersion: inputs.packageVersion ?? readPackageVersion() },
83
+ containment: containmentStatus(currentContainment()),
84
+ service,
85
+ database: {
86
+ identity: databaseIdentity(store),
87
+ schemaVersion: SCHEMA_VERSION,
88
+ runners: store.listRunners().map(one => ({ name: one.name, host: one.host, heartbeatAt: one.heartbeatAt, alive: isAlive(one, now), retired: one.retiredAt !== null })),
89
+ pendingStops,
90
+ openRuns,
91
+ runningTasks: store.listTasks("running").map(task => task.id),
92
+ witnesses,
93
+ },
94
+ limits: RESTART_LIMITS,
95
+ };
96
+ }
97
+ function readPackageVersion() {
98
+ try {
99
+ const url = new URL("../package.json", import.meta.url);
100
+ if (!existsSync(url))
101
+ return null;
102
+ const parsed = JSON.parse(readFileSync(url, "utf8"));
103
+ return typeof parsed.version === "string" ? parsed.version : null;
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
109
+ /**
110
+ * Compare now against the baseline. Recovery is judged by the SAME rules
111
+ * the controller uses (stopQuiescenceProblem, settleQuiescentStops): a
112
+ * stop the old boot left pending settles only when its witnesses are
113
+ * proven gone — by the verified boot change, or by the probes — and a
114
+ * stop that still cannot settle is reported with the exact reason, never
115
+ * forced.
116
+ */
117
+ export async function verifyRestartRecovery(inputs) {
118
+ const { store, baseline } = inputs;
119
+ const expectation = inputs.expect ?? "either";
120
+ const checks = [];
121
+ const identity = inputs.boot ?? bootIdentity();
122
+ const current = await recordRestartBaseline({ ...inputs, ...(baseline.runnerName ? { runnerName: baseline.runnerName } : {}), completionTasks: baseline.completionTasks ?? [], boot: identity });
123
+ const sameDatabase = baseline.version === 2 && baseline.database.identity === current.database.identity;
124
+ checks.push({ name: "database", ok: sameDatabase, detail: sameDatabase ? "same database file identity" : "database identity differs or baseline predates identity verification; no recovery mutations performed" });
125
+ // 1. Boot identity, as the OS reports it.
126
+ const sameHost = baseline.host === current.host;
127
+ const bootChanged = !sameHost || baseline.boot.id === null || current.boot.id === null
128
+ ? null
129
+ : provenDeadByBootChange({ host: current.host, bootId: baseline.boot.id }, { host: current.host, bootId: current.boot.id });
130
+ checks.push({ name: "host", ok: sameHost, detail: sameHost ? `same host ${current.host}` : `baseline was taken on ${baseline.host}, this is ${current.host} — nothing here certifies that machine` });
131
+ checks.push({
132
+ name: "boot-identity",
133
+ ok: bootChanged !== null && (expectation === "either" || (expectation === "reboot") === bootChanged),
134
+ detail: bootChanged === null
135
+ ? `boot identity unknown (baseline ${baseline.boot.id ?? baseline.boot.problem ?? "none"}, now ${current.boot.id ?? current.boot.problem ?? "none"}) — no reboot is claimed`
136
+ : bootChanged
137
+ ? `verified boot change ${baseline.boot.id} → ${current.boot.id}${expectation === "login" ? " — but a login without reboot was expected" : ""}`
138
+ : `same boot ${current.boot.id}${expectation === "reboot" ? " — the reboot has NOT happened" : ""}`,
139
+ });
140
+ // 2. Runtime identity.
141
+ const sameRuntime = baseline.runtime.digest === current.runtime.digest && baseline.runtime.execPath === current.runtime.execPath && baseline.runtime.nodeVersion === current.runtime.nodeVersion && baseline.runtime.packageVersion === current.runtime.packageVersion;
142
+ checks.push({ name: "runtime", ok: sameRuntime, detail: sameRuntime ? `same runtime node ${current.runtime.nodeVersion} at ${current.runtime.execPath}, package ${current.runtime.packageVersion ?? "?"}` : `runtime changed: ${baseline.runtime.nodeVersion}@${baseline.runtime.execPath} (${baseline.runtime.packageVersion ?? "?"}) → ${current.runtime.nodeVersion}@${current.runtime.execPath} (${current.runtime.packageVersion ?? "?"})` });
143
+ checks.push({ name: "schema", ok: current.database.schemaVersion === baseline.database.schemaVersion, detail: `schema v${baseline.database.schemaVersion} → v${current.database.schemaVersion}` });
144
+ // 3. Service liveness: loaded is not working; a working controller shows a running process AND a fresh heartbeat.
145
+ if (baseline.service !== null || current.service !== null) {
146
+ const service = current.service;
147
+ if (service === null)
148
+ checks.push({ name: "service", ok: false, detail: "no service definition was given to verify against" });
149
+ else {
150
+ const problems = service.problems.length === 0 ? "" : ` — ${service.problems.join("; ")}`;
151
+ checks.push({ name: "service", ok: service.state === "running" && service.problems.length === 0 && !service.stale && baseline.service?.label === service.label, detail: `${service.label}: ${service.detail}${service.stale ? " (installed definition is stale: reinstall to load the current one)" : ""}${problems}` });
152
+ }
153
+ }
154
+ const baselineBeats = new Map(baseline.database.runners.map(one => [one.name, one.heartbeatAt]));
155
+ const liveRunners = current.database.runners.filter(one => !one.retired);
156
+ const fresh = liveRunners.filter(one => one.host === current.host && one.alive && (baseline.runnerName === null || one.name === baseline.runnerName) && baselineBeats.has(one.name) && one.heartbeatAt > baselineBeats.get(one.name));
157
+ checks.push({
158
+ name: "heartbeat",
159
+ ok: fresh.length > 0,
160
+ detail: fresh.length > 0
161
+ ? `fresh live heartbeat from ${fresh.map(one => `${one.name} at ${one.heartbeatAt}`).join(", ")}`
162
+ : liveRunners.length === 0
163
+ ? "no live runner is registered — the controller has not come back"
164
+ : `no runner has beaten since the baseline (${liveRunners.map(one => `${one.name}: ${one.heartbeatAt}${one.alive ? "" : " (stale)"}`).join(", ")}) — a loaded service is not a working controller`,
165
+ });
166
+ // 4. Recovery of what the old boot left pending.
167
+ if (sameDatabase && inputs.recover !== false)
168
+ store.settleQuiescentStops(inputs.now ?? new Date());
169
+ const remaining = baseline.database.pendingStops.map(stop => ({ run: stop.run, settled: store.stopOf(stop.run)?.settledAt != null, problem: store.stopQuiescenceProblem(stop.run) }));
170
+ const unsettled = remaining.filter(one => !one.settled);
171
+ checks.push({
172
+ name: "pending-stops",
173
+ ok: unsettled.length === 0,
174
+ detail: baseline.database.pendingStops.length === 0
175
+ ? "no stop was pending at the baseline"
176
+ : unsettled.length === 0
177
+ ? `every pending stop settled (${remaining.map(one => `#${one.run}`).join(", ")})`
178
+ : `still pending: ${unsettled.map(one => `#${one.run} — ${one.problem ?? "unsettled"}`).join("; ")}`,
179
+ });
180
+ const staleOpen = current.database.openRuns.filter(run => baseline.database.openRuns.some(old => old.id === run.id));
181
+ checks.push({
182
+ name: "open-runs",
183
+ ok: staleOpen.length === 0,
184
+ detail: baseline.database.openRuns.length === 0
185
+ ? "no run was open at the baseline"
186
+ : staleOpen.length === 0
187
+ ? `every run open at the baseline has ended (${baseline.database.openRuns.map(run => `#${run.id}`).join(", ")})`
188
+ : `still open from before: ${staleOpen.map(run => `#${run.id} (${run.role}, ${run.runner})`).join(", ")} — recovery has not run, or its lease is still live`,
189
+ });
190
+ const oldWitnesses = baseline.database.witnesses.filter(one => one.bootId !== null && current.boot.id !== null && one.host === current.host);
191
+ const survivors = oldWitnesses.filter(one => !provenDeadByBootChange({ host: one.host, bootId: one.bootId }, { host: current.host, bootId: current.boot.id }));
192
+ checks.push({
193
+ name: "old-boot-custody",
194
+ ok: bootChanged !== true || survivors.length === 0,
195
+ detail: bootChanged === true
196
+ ? `${oldWitnesses.length} witness(es) of the previous boot are proven gone by boot identity`
197
+ : `${baseline.database.witnesses.length} open witness(es) at the baseline are judged by their probes, not by a boot change`,
198
+ });
199
+ // 5. Task completion since the baseline.
200
+ const stillRunning = current.database.runningTasks.filter(id => baseline.database.runningTasks.includes(id));
201
+ checks.push({
202
+ name: "tasks",
203
+ ok: stillRunning.length === 0,
204
+ detail: baseline.database.runningTasks.length === 0
205
+ ? "no task was running at the baseline"
206
+ : stillRunning.length === 0
207
+ ? `every task running at the baseline moved on (${baseline.database.runningTasks.join(", ")})`
208
+ : `still running from before: ${stillRunning.join(", ")}`,
209
+ });
210
+ for (const taskId of baseline.completionTasks ?? []) {
211
+ const task = store.getTask(taskId);
212
+ checks.push({ name: `completion:${taskId}`, ok: task?.state === "done", detail: task ? `${taskId}: ${task.state}` : `${taskId}: missing` });
213
+ }
214
+ return { ok: checks.every(check => check.ok), bootChanged, expectation, checks, current, limits: RESTART_LIMITS };
215
+ }
216
+ /** The baseline and verification are meant to be printed; this keeps a secret from ever entering them. */
217
+ export function assertNoSecret(json, secrets) {
218
+ for (const secret of secrets) {
219
+ if (secret !== "" && json.includes(secret))
220
+ throw new Error("the certification output would contain a credential; refusing to write it");
221
+ }
222
+ }
223
+ /** The current boot id, for scripts that print it beside a baseline. */
224
+ export function bootIdNow() {
225
+ return currentBootId();
226
+ }
@@ -0,0 +1,28 @@
1
+ import type { Store } from "./store.js";
2
+ export type RevisionResult = {
3
+ ok: true;
4
+ id: string;
5
+ } | {
6
+ ok: false;
7
+ status: number;
8
+ message: string;
9
+ };
10
+ type RevisionInput = Parameters<typeof createResultRevision>[2];
11
+ /** One user action: add the typed change and seal exactly the displayed
12
+ * saved notes. A refusal rolls back the note; a replay returns the same task. */
13
+ export declare function requestResultChanges(store: Store, evidenceRoot: string, input: RevisionInput & {
14
+ note: string;
15
+ path: string;
16
+ line: string;
17
+ request: string | null;
18
+ }, now: Date): RevisionResult;
19
+ export declare function createResultRevision(store: Store, evidenceRoot: string, input: {
20
+ run: number;
21
+ batch: string | null;
22
+ source: string | null;
23
+ actor: string;
24
+ repos: readonly string[] | null;
25
+ includeUnplaced?: boolean;
26
+ allowMode?: boolean;
27
+ }, now: Date): RevisionResult;
28
+ export {};
@@ -0,0 +1,270 @@
1
+ /** Shared result-to-revision action for the result form and chat.
2
+ * The caller authenticates; this service checks project access and the exact
3
+ * displayed note batch, then uses the existing atomic revision/approval boundary.
4
+ */
5
+ import { createHash, randomBytes } from "node:crypto";
6
+ import { EVIDENCE_CAPS, readVerifiedArtifact, writeEvidenceFile } from "./evidence.js";
7
+ import { isRevisionFeedback, parseRevisionBatch, revisionSourceOf, commentSourceKey, REQUEST_TOKEN } from "./result-review.js";
8
+ import { validateNote, hasForbiddenControls } from "./decision.js";
9
+ import { TASK_TEXT_LIMITS, validateTaskText } from "./task-text.js";
10
+ import { modeFilingCoverage } from "./scope.js";
11
+ /** One user action: add the typed change and seal exactly the displayed
12
+ * saved notes. A refusal rolls back the note; a replay returns the same task. */
13
+ export function requestResultChanges(store, evidenceRoot, input, now) {
14
+ try {
15
+ return store.transact(() => store.savepoint(() => {
16
+ const fail = (status, message) => { throw new ChangeRequestRefusal(status, message); };
17
+ if (!input.note.trim()) {
18
+ if (input.path.trim() || input.line.trim())
19
+ return fail(400, "Describe the change before attaching a file or line.");
20
+ if (!input.batch)
21
+ return fail(400, "Describe a change or use a saved note.");
22
+ return createResultRevision(store, evidenceRoot, input, now);
23
+ }
24
+ const valid = validateNote(input.note);
25
+ if (!valid.ok)
26
+ return fail(400, valid.problem);
27
+ if (input.request === null || !REQUEST_TOKEN.test(input.request))
28
+ return fail(400, "Reload the result before requesting changes. Your draft stays here.");
29
+ const batch = input.batch === "" ? [] : parseRevisionBatch(input.batch);
30
+ if (batch === null)
31
+ return fail(400, "Reload the result to review the saved notes.");
32
+ const path = input.path.trim() || null;
33
+ const line = input.line.trim() === "" ? null : Number(input.line);
34
+ if (path !== null && (path.length > 300 || hasForbiddenControls(path)))
35
+ return fail(400, "Enter a file path of up to 300 characters, without control characters.");
36
+ if (line !== null && (path === null || !Number.isInteger(line) || line < 1 || line > 1_000_000))
37
+ return fail(400, "Choose a file and a whole line number from 1 to 1,000,000.");
38
+ const run = store.getRun(input.run);
39
+ const repo = run === null ? null : store.refById(run.taskRef)?.repo ?? null;
40
+ if (run === null || (repo === null ? input.includeUnplaced !== true : input.repos !== null && !input.repos.includes(repo)))
41
+ return fail(404, "No such result.");
42
+ const artifact = store.artifactsFor(run.id).find(one => one.kind === "terminal-diff");
43
+ if (!artifact || !readVerifiedArtifact(evidenceRoot, artifact).ok)
44
+ return fail(409, "The saved changes could not be verified. Restore the result before requesting changes.");
45
+ const sourceKey = commentSourceKey(input.actor, input.request);
46
+ let noteId = store.addDiffComment({ artifactId: artifact.id, runId: run.id, path, line, note: valid.note, author: input.actor, sourceKey }, now);
47
+ if (noteId === null) {
48
+ const earlier = store.diffCommentBySourceKey(sourceKey);
49
+ if (!earlier || earlier.run !== run.id || earlier.path !== path || earlier.line !== line || earlier.note !== valid.note)
50
+ return fail(409, "This submission already saved different feedback. Reopen the result and try again.");
51
+ noteId = earlier.id;
52
+ }
53
+ const result = createResultRevision(store, evidenceRoot, { ...input, batch: [...new Set([...batch, noteId])].sort((a, b) => a - b).join(",") }, now);
54
+ if (!result.ok)
55
+ return fail(result.status, result.message);
56
+ return result;
57
+ }));
58
+ }
59
+ catch (error) {
60
+ if (error instanceof ChangeRequestRefusal)
61
+ return { ok: false, status: error.status, message: error.message };
62
+ throw error;
63
+ }
64
+ }
65
+ class ChangeRequestRefusal extends Error {
66
+ status;
67
+ constructor(status, message) {
68
+ super(message);
69
+ this.status = status;
70
+ }
71
+ }
72
+ export function createResultRevision(store, evidenceRoot, input, now) {
73
+ return store.transact(() => {
74
+ const id = input.run;
75
+ const visible = (repo) => repo === null ? input.includeUnplaced === true : input.repos === null || input.repos.includes(repo);
76
+ const fail = (status, message) => ({ ok: false, status, message });
77
+ const found = store.getRun(id);
78
+ if (found === null || !visible(store.refById(found.taskRef)?.repo ?? null))
79
+ return fail(404, "No such result.");
80
+ const sourceTaskId = store.externalIdFor(found.taskRef) ?? "?";
81
+ const sourceScope = store.getScope(sourceTaskId);
82
+ const sourceFamily = store.taskFamilyOf(sourceTaskId, input.repos, input.includeUnplaced ?? false);
83
+ if (sourceFamily === null || sourceFamily.problem !== null)
84
+ return fail(409, "The revision history cannot be verified in this project.");
85
+ const repo = store.refById(found.taskRef)?.repo ?? null;
86
+ // THE DISPLAYED BATCH (repair 2026-09-14, finding 2): the form names
87
+ // the exact note ids its reader saw and the source terms it was
88
+ // rendered against. The seal binds to those ids and nothing else —
89
+ // "every live note at POST time" would let an old submission, a
90
+ // replay, or a second tab consume notes its reader never displayed.
91
+ const batchIds = parseRevisionBatch(input.batch);
92
+ const postedSource = input.source;
93
+ if (batchIds === null || postedSource === null || postedSource === "") {
94
+ return fail(400, "this form is out of date — it names no note batch; reload the result and create the revision from the notes shown there");
95
+ }
96
+ const byId = new Map(store.allDiffComments(id).map(one => [one.id, one]));
97
+ const named = batchIds.map(one => byId.get(one));
98
+ if (named.some(one => one === undefined)) {
99
+ return fail(400, "this batch names notes that are not on this result — reload the result");
100
+ }
101
+ const batch = named;
102
+ // A replay must match the immutable brief's entire batch and source,
103
+ // not just a subset whose notes happen to name the same child. Read-only
104
+ // replay can return its original child even after the source is rescoped.
105
+ const replayChild = (notes) => {
106
+ const child = notes[0]?.consumedBy;
107
+ if (!child || notes.length !== batchIds.length || notes.some(one => one.consumedBy !== child))
108
+ return null;
109
+ const ref = store.lookupRef(child);
110
+ const lineage = ref === null ? null : store.revisionSourceOf(ref.id);
111
+ if (ref === null || !visible(ref.repo) || lineage === null || lineage.sourceRun !== id || lineage.sourceTask !== sourceTaskId)
112
+ return null;
113
+ try {
114
+ const read = readVerifiedArtifact(evidenceRoot, lineage.briefArtifact);
115
+ if (!read.ok)
116
+ return null;
117
+ const brief = JSON.parse(read.content.toString("utf8"));
118
+ if (brief.sourceRun !== id || brief.sourceTask !== sourceTaskId || revisionSourceOf(brief.sourceScopeDigest) !== postedSource || !Array.isArray(brief.comments))
119
+ return null;
120
+ return brief.comments.length === batchIds.length && brief.comments.every((one, at) => one.id === batchIds[at]) ? child : null;
121
+ }
122
+ catch {
123
+ return null;
124
+ }
125
+ };
126
+ // A retry of an OLD request: every note it names is already sealed
127
+ // into one revision — that revision is the answer, and no note added
128
+ // since is touched. A batch partly sealed elsewhere is refused whole.
129
+ const consumers = new Set(batch.map(one => one.consumedBy).filter((one) => one !== null));
130
+ const replay = replayChild(batch);
131
+ if (replay !== null)
132
+ return { ok: true, id: replay };
133
+ if (consumers.size > 0) {
134
+ return fail(409, `some of these notes were already sealed into revision ${[...consumers].join(", ")}; the others are still waiting — reload the result to see the current batch`);
135
+ }
136
+ if (postedSource !== revisionSourceOf(sourceScope?.digest ?? null)) {
137
+ return fail(409, "the task's terms changed since this page was shown — reload the result and read the current terms before creating a revision");
138
+ }
139
+ if (batch.some(one => one.supersededBy !== null)) {
140
+ return fail(409, "a note in this batch was superseded — reload the result to see the current batch");
141
+ }
142
+ if (batch.some(one => !isRevisionFeedback(one)))
143
+ return fail(409, "Review observations are not requested changes. Use Request change to add your feedback first.");
144
+ const comments = batch;
145
+ // Keep the subject recognizable, including when revising a revision.
146
+ // Reserve the suffix before truncating, at whole grapheme boundaries.
147
+ const suffix = " — revision";
148
+ const subject = (store.getTask(sourceTaskId)?.title ?? "").trim().replace(/(?: — revision)+$/u, "").trim() || "Task";
149
+ let prefix = "";
150
+ for (const { segment } of new Intl.Segmenter("en", { granularity: "grapheme" }).segment(subject)) {
151
+ if ((prefix + segment + suffix).length > TASK_TEXT_LIMITS.title)
152
+ break;
153
+ prefix += segment;
154
+ }
155
+ const candidateTitle = `${prefix.trimEnd() || "Task"}${suffix}`;
156
+ const title = validateTaskText({ title: candidateTitle }) === null ? candidateTitle : `Task${suffix}`;
157
+ const feedbackKind = comments.every(one => one.path !== null) ? "annotations" : "feedback";
158
+ const terminal = store.artifactsFor(id).find(one => one.kind === "terminal-diff");
159
+ if (terminal !== undefined) {
160
+ const proven = readVerifiedArtifact(evidenceRoot, terminal);
161
+ if (!proven.ok) {
162
+ return fail(409, `the reviewed diff no longer verifies (${proven.problem}) — the batch cannot seal against it`);
163
+ }
164
+ }
165
+ // The brief is serialized and size-checked BEFORE anything is created
166
+ // (Codex M5-M8 audit, IV-3): a structured artifact must never pass
167
+ // through byte truncation — truncated JSON is not a smaller brief,
168
+ // it is no brief wearing one's name.
169
+ const brief = {
170
+ schema: 1,
171
+ sourceTask: sourceTaskId,
172
+ sourceRun: id,
173
+ sourceScopeDigest: sourceScope?.digest ?? null,
174
+ head: found.headRevision,
175
+ diffArtifactSha: terminal?.sha256 ?? null,
176
+ comments: comments.map(one => ({
177
+ id: one.id,
178
+ path: one.path,
179
+ line: one.line,
180
+ note: one.note,
181
+ author: one.author,
182
+ createdAt: one.createdAt,
183
+ })),
184
+ };
185
+ const briefBytes = Buffer.from(JSON.stringify(brief, null, 2), "utf8");
186
+ if (briefBytes.length > EVIDENCE_CAPS["revision-brief"]) {
187
+ return fail(400, "this batch is too large for one brief — seal it in parts");
188
+ }
189
+ // The file first, under a nonce name — a file whose seal fails is an
190
+ // orphan on disk, never authority. Then ONE transaction: task with
191
+ // the source scope's limits INHERITED (audit IV-2 — a revision that
192
+ // drops the exclusions is an approval screen telling a lie), the
193
+ // artifact row, the relation, and exactly this comment batch.
194
+ const briefName = `revision-brief-${randomBytes(6).toString("hex")}.json`;
195
+ const key = writeEvidenceFile(evidenceRoot, id, briefName, briefBytes);
196
+ // C1's revision arm: when the SEALER is the mode's signer and the
197
+ // mode auto-approves their filings, the revision task approves
198
+ // inside the same transaction, with mode provenance. Everything
199
+ // else about the seal is unchanged; without coverage, the revision
200
+ // keeps its own approval ceremony.
201
+ const sealed = store.transact(() => {
202
+ // Coverage first (cookie road only): its budget and escalated
203
+ // posture ride the FILING so the digest binds them (surfaces
204
+ // round 1, finding 2) — never a stamp after the fact.
205
+ const coverage = input.allowMode === true ? modeFilingCoverage(store, repo, input.actor, now) : null;
206
+ // ONE revision boundary (contract handoff task 2): the source's
207
+ // goal, exclusions, touches, rubric, risk, quality, posture,
208
+ // budget, overrides and pins are read from the SOURCE ROWS inside
209
+ // the seal, which re-proves the task/run/scope-digest binding and
210
+ // the brief's custody first — the scope read above only names the
211
+ // digest this batch was drafted against.
212
+ const result = store.sealRevision({
213
+ source: { task: sourceTaskId, run: id, scopeDigest: sourceScope?.digest ?? null },
214
+ brief: { evidenceRoot, key, sha256: createHash("sha256").update(briefBytes).digest("hex"), bytes: briefBytes.length, capture: "machine-authored revision brief (exit 0)" },
215
+ child: {
216
+ title: `Revise ${sourceTaskId} from ${comments.length} annotation${comments.length === 1 ? "" : "s"} on build #${id}`,
217
+ repair: `apply the ${feedbackKind} recorded on build #${id}; the revision brief carries the exact batch`,
218
+ },
219
+ commentIds: comments.map(one => one.id),
220
+ coverage,
221
+ requestedBy: input.actor,
222
+ }, now);
223
+ if (result.ok) {
224
+ // The unchanged legacy title above feeds the store's existing slug
225
+ // and collision handling. Set only this new row's display title,
226
+ // atomically with its seal; retries returned earlier, untouched.
227
+ store.raw().prepare("UPDATE task SET title = ? WHERE id = ?").run(title, result.id);
228
+ }
229
+ // Planned afresh: the planner reads the copied terms and these notes, and a note asking for
230
+ // more than the old plan allows becomes an amendment the person approves before anything
231
+ // builds. Under a live mode the filing is approved at once instead, as every signer filing
232
+ // is; revisions never inherit the mode's planner authority (docs/AUTO_APPROVAL.md).
233
+ if (result.ok && coverage === null) {
234
+ const ref = store.lookupRef(result.id);
235
+ if (ref !== null)
236
+ store.requestPlan(ref.id, now);
237
+ }
238
+ if (result.ok && coverage !== null) {
239
+ // A scope whose profile could not resolve is unapprovable by the
240
+ // human road (approve() refuses) — the mode road refuses too.
241
+ const filedScope = store.getScope(result.id);
242
+ if (filedScope?.profileState === "resolved") {
243
+ // A false answer here is the coordinator quarantine (review
244
+ // finding 7): the revision stays honestly unapproved and the
245
+ // task page it redirects to says so in the quarantine words.
246
+ void store.sealScopeApproval(result.id, input.actor, now, {}, { kind: "mode", modeDigest: coverage.digest });
247
+ }
248
+ }
249
+ return result;
250
+ });
251
+ if (!sealed.ok) {
252
+ // Two sealers of one batch race on the consumption count; the
253
+ // loser is sent to the winner's revision — the one that consumed
254
+ // EXACTLY this batch, never merely the newest child of the run — so
255
+ // a double click or a two-tab submission lands on ONE revision.
256
+ if (sealed.reason === "duplicate" || sealed.reason === "comments-taken") {
257
+ const settled = store.allDiffComments(id).filter(one => batchIds.includes(one.id));
258
+ const takers = new Set(settled.map(one => one.consumedBy).filter((one) => one !== null));
259
+ const winner = replayChild(settled);
260
+ if (winner !== null)
261
+ return { ok: true, id: winner };
262
+ if (takers.size > 0) {
263
+ return fail(409, `some of these notes were just sealed into revision ${[...takers].join(", ")}; the others are still waiting — reload the result to see the current batch`);
264
+ }
265
+ }
266
+ return fail(409, `could not seal the revision: ${sealed.detail}`);
267
+ }
268
+ return { ok: true, id: sealed.id };
269
+ });
270
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * v102: what a result actually changed, read from the build's sealed diff
3
+ * inventory, and whether a result that changed protected files may go out.
4
+ * Completion and publication share this so neither can be the gap.
5
+ */
6
+ import type { Store } from "./store.js";
7
+ /** The ledger action a person's (or lead's) completion of a result writes. */
8
+ export declare const COMPLETION_ACTION = "assignment handoff checked";
9
+ /** The files a build changed, renames on both sides; null when the inventory can't be read whole. */
10
+ export declare function changedFilesOf(store: Store, runId: number, root: string): string[] | null;
11
+ /** Everything a result's branch carries: this build's changes and every build of every task it revises (a
12
+ * revision builds on its source's head, so its branch holds the source's commits too). null when any of
13
+ * them can't be read whole. */
14
+ export declare function familyChangedFiles(store: Store, taskId: string, runId: number, root: string): string[] | null;
15
+ /** Why a build's branch must wait before it's pushed (and so before any PR or merge): it (or a task it
16
+ * revises) changed protected files on a one-person approval, and nobody but its approver and requesters
17
+ * has marked it complete. */
18
+ export declare function protectedPublicationHold(store: Store, runId: number, root: string): string | null;
@@ -0,0 +1,66 @@
1
+ import { readVerifiedArtifact } from "./evidence.js";
2
+ /** The ledger action a person's (or lead's) completion of a result writes. */
3
+ export const COMPLETION_ACTION = "assignment handoff checked";
4
+ /** The files a build changed, renames on both sides; null when the inventory can't be read whole. */
5
+ export function changedFilesOf(store, runId, root) {
6
+ const stat = store.artifactsFor(runId).find(one => one.kind === "diff-stat");
7
+ if (stat === undefined || stat.truncated || stat.captureStatus !== "ok")
8
+ return null;
9
+ const read = readVerifiedArtifact(root, stat);
10
+ if (!read.ok)
11
+ return null;
12
+ try {
13
+ const inventory = JSON.parse(read.content.toString("utf8"));
14
+ if (inventory.filesTruncated !== false || !Array.isArray(inventory.files))
15
+ return null;
16
+ // A rename changes both places: moving a protected file out of its folder touches it.
17
+ return inventory.files.flatMap(one => [one.path, one.renamedFrom]).filter((path) => typeof path === "string" && path !== "");
18
+ }
19
+ catch {
20
+ return null;
21
+ }
22
+ }
23
+ /** Everything a result's branch carries: this build's changes and every build of every task it revises (a
24
+ * revision builds on its source's head, so its branch holds the source's commits too). null when any of
25
+ * them can't be read whole. */
26
+ export function familyChangedFiles(store, taskId, runId, root) {
27
+ const files = new Set();
28
+ const runs = new Set([runId]);
29
+ const chain = store.revisionAncestryStatus(taskId).chain;
30
+ for (const id of chain.length === 0 ? [taskId] : chain) {
31
+ const ref = store.lookupRef(id);
32
+ if (ref === null)
33
+ continue;
34
+ for (const run of store.runsFor(ref.id))
35
+ if ((run.role === "builder" || run.role === "repair") && (id !== taskId || run.id === runId))
36
+ runs.add(run.id);
37
+ }
38
+ for (const id of runs) {
39
+ // A build that never recorded a diff contributed nothing to the branch; one whose diff can't be read counts as protected.
40
+ if (!store.artifactsFor(id).some(one => one.kind === "diff-stat") && id !== runId)
41
+ continue;
42
+ const changed = changedFilesOf(store, id, root);
43
+ if (changed === null)
44
+ return null;
45
+ for (const file of changed)
46
+ files.add(file);
47
+ }
48
+ return [...files];
49
+ }
50
+ /** Why a build's branch must wait before it's pushed (and so before any PR or merge): it (or a task it
51
+ * revises) changed protected files on a one-person approval, and nobody but its approver and requesters
52
+ * has marked it complete. */
53
+ export function protectedPublicationHold(store, runId, root) {
54
+ const run = store.getRun(runId);
55
+ const taskId = run === null ? null : store.externalIdFor(run.taskRef);
56
+ if (taskId === null)
57
+ return null;
58
+ const changed = familyChangedFiles(store, taskId, runId, root);
59
+ if (store.protectedResultProblem(taskId, changed, null) === null)
60
+ return null;
61
+ const completers = store.handle.prepare("SELECT DISTINCT actor FROM action_ledger WHERE run_id = ? AND action = ?").all(runId, COMPLETION_ACTION)
62
+ .map(row => String(row["actor"])).filter(actor => actor.startsWith("operator:")).map(actor => actor.slice("operator:".length));
63
+ if (completers.some(name => store.protectedResultProblem(taskId, changed, name) === null))
64
+ return null;
65
+ return "held: it changed protected files on a one-person approval, so it publishes once someone other than its approver marks it complete";
66
+ }