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,173 @@
1
+ /**
2
+ * Settings → Monitoring (v104): where Toolroll sends what it does, for
3
+ * the tools a company already watches. Kept in `monitoring.json` beside the
4
+ * database (0600, the agent fence keeps agents out), never in the database:
5
+ * the webhook's signing secret and a collector's header value are secrets.
6
+ *
7
+ * - The audit stream: every sealed ledger entry, in order, to a webhook
8
+ * (each request signed) and/or a folder of JSON Lines files.
9
+ * - Traces: each finished run as an OpenTelemetry span, to a collector.
10
+ */
11
+ import { randomBytes } from "node:crypto";
12
+ import { closeSync, constants, existsSync, fsyncSync, openSync, readFileSync, realpathSync, renameSync, rmSync, writeSync } from "node:fs";
13
+ import { dirname, isAbsolute, join, resolve } from "node:path";
14
+ const FILE = "monitoring.json";
15
+ /** Where an address points, without its path or query (a webhook may carry a key in either). */
16
+ export const origin = (address) => { try {
17
+ return new URL(address).origin;
18
+ }
19
+ catch {
20
+ return "?";
21
+ } };
22
+ export const NO_MONITORING = { webhook: null, folder: null, traces: null };
23
+ /** An address Toolroll may send to: https anywhere, http only to this machine (a collector beside it). */
24
+ export function sendableAddress(value) {
25
+ let url;
26
+ try {
27
+ url = new URL(value.trim());
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ if (url.username !== "" || url.password !== "" || url.hash !== "")
33
+ return null;
34
+ const local = ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname);
35
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && local))
36
+ return null;
37
+ return url.toString().replace(/\/+$/, "");
38
+ }
39
+ /** A header name a collector asks for (x-honeycomb-team, DD-API-KEY, Authorization); never one HTTP itself owns. */
40
+ const HEADER_NAME = /^[A-Za-z0-9][A-Za-z0-9-]{0,63}$/;
41
+ const RESERVED_HEADERS = new Set(["host", "content-length", "content-type", "transfer-encoding", "connection", "cookie", "user-agent"]);
42
+ export function readMonitoring(dir) {
43
+ if (!dir)
44
+ return NO_MONITORING;
45
+ const file = join(dir, FILE);
46
+ if (!existsSync(file))
47
+ return NO_MONITORING;
48
+ try {
49
+ const value = JSON.parse(readFileSync(file, "utf8"));
50
+ const webhook = value.webhook != null && typeof value.webhook.url === "string" && typeof value.webhook.secret === "string" && sendableAddress(value.webhook.url) !== null && value.webhook.secret.length >= 32
51
+ ? { url: sendableAddress(value.webhook.url), secret: value.webhook.secret } : null;
52
+ const folder = value.folder != null && typeof value.folder.path === "string" && isAbsolute(value.folder.path) ? { path: value.folder.path } : null;
53
+ const header = value.traces?.header != null && typeof value.traces.header.name === "string" && typeof value.traces.header.value === "string" && HEADER_NAME.test(value.traces.header.name)
54
+ ? { name: value.traces.header.name, value: value.traces.header.value } : null;
55
+ const traces = value.traces != null && typeof value.traces.endpoint === "string" && sendableAddress(value.traces.endpoint) !== null
56
+ ? { endpoint: sendableAddress(value.traces.endpoint), header } : null;
57
+ return { webhook, folder, traces };
58
+ }
59
+ catch {
60
+ return NO_MONITORING;
61
+ }
62
+ }
63
+ function write(dir, settings) {
64
+ const file = join(dir, FILE);
65
+ // A fresh name, made here (never a file or link already there), readable by its owner only, then swapped in.
66
+ const next = `${file}.${randomBytes(8).toString("hex")}.tmp`;
67
+ const handle = openSync(next, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
68
+ try {
69
+ try {
70
+ writeSync(handle, `${JSON.stringify(settings, null, 2)}\n`);
71
+ fsyncSync(handle);
72
+ }
73
+ finally {
74
+ closeSync(handle);
75
+ }
76
+ renameSync(next, file);
77
+ }
78
+ catch (error) {
79
+ rmSync(next, { force: true });
80
+ throw error;
81
+ }
82
+ }
83
+ /** A fresh signing secret for the webhook (shown once; the receiver keeps it to check each request). */
84
+ export function newSigningSecret() {
85
+ return `whsec_${randomBytes(32).toString("base64url")}`;
86
+ }
87
+ /**
88
+ * Save what the form sent. A blank field turns that destination off; a header value left blank keeps the one
89
+ * saved. A new webhook address, or `rotate`, makes a new signing secret, handed back once as `secret`.
90
+ */
91
+ /** A path as it really is: its nearest existing folder resolved (so /tmp and /private/tmp are one), in one case
92
+ * (macOS folders ignore it). */
93
+ function realish(path) {
94
+ let at = resolve(path), rest = "";
95
+ while (!existsSync(at) && dirname(at) !== at) {
96
+ rest = join(at.slice(dirname(at).length + 1), rest);
97
+ at = dirname(at);
98
+ }
99
+ try {
100
+ return join(realpathSync.native(at), rest).toLowerCase();
101
+ }
102
+ catch {
103
+ return resolve(path).toLowerCase();
104
+ }
105
+ }
106
+ const within = (path, folder) => path === folder || path.startsWith(`${folder}/`);
107
+ export function saveMonitoring(dir, form, previous, forbidden = []) {
108
+ let webhook = null, secret = null;
109
+ if (form.webhook.trim() !== "") {
110
+ const url = sendableAddress(form.webhook);
111
+ if (url === null)
112
+ return { ok: false, said: "The webhook is an https address (http only for this machine), with no user name or password in it." };
113
+ const keep = previous.webhook !== null && previous.webhook.url === url && !form.rotate;
114
+ if (!keep)
115
+ secret = newSigningSecret();
116
+ webhook = { url, secret: keep ? previous.webhook.secret : secret };
117
+ }
118
+ let folder = null;
119
+ if (form.folder.trim() !== "") {
120
+ const path = form.folder.trim();
121
+ if (!isAbsolute(path) || path.length > 500 || /[\u0000-\u001f]/.test(path))
122
+ return { ok: false, said: "The folder is a full path, like /var/log/standing-orders." };
123
+ // Never where an agent works or Toolroll keeps its own state: an agent could read the whole stream there.
124
+ const real = realish(path);
125
+ if ([dir, ...forbidden].some(one => within(real, realish(one)) || within(realish(one), real)))
126
+ return { ok: false, said: "Choose a folder outside Toolroll's own folder and your projects, like /var/log/standing-orders." };
127
+ folder = { path };
128
+ }
129
+ let traces = null;
130
+ if (form.tracesEndpoint.trim() !== "") {
131
+ const endpoint = sendableAddress(form.tracesEndpoint);
132
+ if (endpoint === null)
133
+ return { ok: false, said: "The collector is an https address (http only for this machine), like https://otel.example.com:4318." };
134
+ const name = form.headerName.trim();
135
+ let header = null;
136
+ if (name !== "") {
137
+ if (!HEADER_NAME.test(name) || RESERVED_HEADERS.has(name.toLowerCase()))
138
+ return { ok: false, said: "The header name is letters, digits and dashes, like x-honeycomb-team." };
139
+ // A saved key stays only with the same collector (origin) and header: it never follows the address elsewhere.
140
+ const sameCollector = previous.traces !== null && origin(previous.traces.endpoint) === origin(endpoint) && previous.traces.header?.name === name;
141
+ const value = form.headerValue.trim() !== "" ? form.headerValue.trim() : sameCollector ? previous.traces.header.value : "";
142
+ if (value === "" || value.length > 2000 || /[\u0000-\u001f]/.test(value))
143
+ return { ok: false, said: "Enter the header's value (the collector's API key)." };
144
+ header = { name, value };
145
+ }
146
+ traces = { endpoint, header };
147
+ }
148
+ const settings = { webhook, folder, traces };
149
+ write(dir, settings);
150
+ return { ok: true, settings, secret };
151
+ }
152
+ /** The change in words for the ledger: where things go and header names, never a secret or a full address. */
153
+ export function monitoringWords(settings) {
154
+ const parts = [
155
+ settings.webhook === null ? null : `webhook ${origin(settings.webhook.url)}`,
156
+ settings.folder === null ? null : `folder ${settings.folder.path}`,
157
+ settings.traces === null ? null : `traces ${origin(settings.traces.endpoint)}${settings.traces.header === null ? "" : ` (${settings.traces.header.name})`}`,
158
+ ].filter((one) => one !== null);
159
+ return parts.length === 0 ? "off" : parts.join("; ");
160
+ }
161
+ /** A change for the ledger: before → after, and what changed that the words alone don't show (never the values). */
162
+ export function monitoringChange(before, after, newSecret) {
163
+ const notes = [
164
+ before.webhook !== null && after.webhook !== null && before.webhook.url !== after.webhook.url && origin(before.webhook.url) === origin(after.webhook.url) ? "webhook address changed" : null,
165
+ newSecret && before.webhook !== null && after.webhook !== null && before.webhook.url === after.webhook.url ? "new signing secret" : null,
166
+ before.traces !== null && after.traces !== null && before.traces.endpoint !== after.traces.endpoint && origin(before.traces.endpoint) === origin(after.traces.endpoint) ? "collector address changed" : null,
167
+ before.traces?.header != null && after.traces?.header != null && before.traces.header.name === after.traces.header.name && before.traces.header.value !== after.traces.header.value ? "new header value" : null,
168
+ ].filter((one) => one !== null);
169
+ const words = { before: monitoringWords(before), after: monitoringWords(after) };
170
+ if (words.before === words.after && notes.length === 0)
171
+ return null;
172
+ return `${words.before} → ${words.after}${notes.length === 0 ? "" : ` (${notes.join(", ")})`}`;
173
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Settings → Monitoring (v104): an instance operator points Toolroll at
3
+ * the company's tools. One form, the status of each destination beside it,
4
+ * and how Prometheus reads `/metrics`. The signing secret is shown once.
5
+ */
6
+ import type { MonitoringStatus } from "./store.js";
7
+ import type { MonitoringSettings } from "./monitoring-settings.js";
8
+ export declare const MONITORING_CSS: string;
9
+ export type MonitoringView = {
10
+ settings: MonitoringSettings;
11
+ status: MonitoringStatus[];
12
+ head: number;
13
+ origin: string | null;
14
+ };
15
+ export declare function monitoringHtml(view: MonitoringView, csrf: string, notice: {
16
+ said?: string | null;
17
+ problem?: string | null;
18
+ }): string;
19
+ /** The webhook's signing secret, once: a focused page with no script. */
20
+ export declare function signingSecretHtml(url: string, secret: string): string;
@@ -0,0 +1,51 @@
1
+ import { targetOf } from "./monitoring.js";
2
+ const e = (value) => String(value ?? "").replace(/[&<>"']/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
3
+ const when = (at) => `${e(at.slice(0, 16).replace("T", " "))} UTC`;
4
+ export const MONITORING_CSS = `.monitoring{max-width:720px;min-width:0}.monitoring fieldset{border:0;padding:0;margin:20px 0 0;min-width:0}` +
5
+ `.monitoring legend{font-weight:600;font-size:.9375rem;margin-bottom:6px}.monitoring label{display:grid;gap:4px;margin:10px 0 0;font-size:.875rem}` +
6
+ `.monitoring input[type=text],.monitoring input[type=url],.monitoring input[type=password]{width:100%;box-sizing:border-box}.monitoring .choice{display:flex;gap:8px;align-items:center}` +
7
+ `.monitoring .status{margin:4px 0 0;font-size:.8125rem}.monitoring .status.problem{color:var(--danger)}.monitoring .step-up{margin-top:18px}.monitoring button[type=submit]{margin-top:14px}` +
8
+ `.monitoring pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:.75rem}.monitoring details{margin-top:6px}`;
9
+ /** How a destination is doing, in one line. */
10
+ function statusLine(view, sink, address) {
11
+ // An empty field already says it's off.
12
+ if (address === null)
13
+ return "";
14
+ // A status for another address (it just changed) says nothing about this one yet.
15
+ const one = view.status.find(row => row.sink === sink && row.target === targetOf(address));
16
+ if (one === undefined || (one.lastOkAt === null && one.lastError === null))
17
+ return `<p class="status meta" data-monitoring="${sink}" data-state="starting">Starting: the first delivery goes out within a few seconds.</p>`;
18
+ if (one.failures > 0 && one.lastError !== null) {
19
+ return `<p class="status problem" role="alert" data-monitoring="${sink}" data-state="failing">Failing: ${e(one.lastError)}${one.nextTryAt === null ? "" : ` · trying again ${when(one.nextTryAt)}`}</p>`;
20
+ }
21
+ const behind = Math.max(0, view.head - one.through);
22
+ return `<p class="status meta" data-monitoring="${sink}" data-state="ok">${sink === "traces" ? "Sending" : `Sent through entry #${one.through}`}${one.lastOkAt === null ? "" : ` · last ${when(one.lastOkAt)}`}${behind > 0 && sink !== "traces" ? ` · ${behind} to go` : ""}</p>`;
23
+ }
24
+ export function monitoringHtml(view, csrf, notice) {
25
+ const s = view.settings;
26
+ const note = notice.problem ? `<p class="problem" role="alert">${e(notice.problem)}</p>` : notice.said ? `<p role="status">${e(notice.said)}</p>` : "";
27
+ const metrics = view.origin === null ? "/metrics" : `${view.origin}/metrics`;
28
+ return `<section class="monitoring">${note}` +
29
+ `<form method="post" action="/settings/monitoring"><input type="hidden" name="csrf" value="${e(csrf)}">` +
30
+ `<fieldset><legend>Audit stream</legend><p class="meta">Every ledger entry with its seal, as it happens, to your log system.</p>` +
31
+ `<label>Webhook<input type="url" name="webhook" value="${e(s.webhook?.url ?? "")}" placeholder="https://logs.example.com/standing-orders" spellcheck="false"></label>` +
32
+ statusLine(view, "webhook", s.webhook?.url ?? null) +
33
+ (s.webhook === null ? "" : `<label class="choice"><input type="checkbox" name="rotate" value="1"> Make a new signing secret</label>`) +
34
+ `<label>Folder (JSON Lines, a file per day)<input type="text" name="folder" value="${e(s.folder?.path ?? "")}" placeholder="/var/log/standing-orders" spellcheck="false"></label>` +
35
+ statusLine(view, "folder", s.folder?.path ?? null) + `</fieldset>` +
36
+ `<fieldset><legend>Traces</legend><p class="meta">Each run as an OpenTelemetry span: timings, model, tokens and cost. Never a prompt or code.</p>` +
37
+ `<label>Collector (OTLP over HTTP)<input type="url" name="traces" value="${e(s.traces?.endpoint ?? "")}" placeholder="https://otel.example.com:4318" spellcheck="false"></label>` +
38
+ statusLine(view, "traces", s.traces?.endpoint ?? null) +
39
+ `<label>Header name (optional)<input type="text" name="header-name" value="${e(s.traces?.header?.name ?? "")}" placeholder="x-honeycomb-team" spellcheck="false"></label>` +
40
+ `<label>Header value<input type="password" name="header-value" autocomplete="off" placeholder="${s.traces?.header ? "Saved; leave blank to keep" : "API key"}"></label></fieldset>` +
41
+ `<div class="step-up"><label>Your Toolroll password<input type="password" name="password" autocomplete="current-password"></label></div>` +
42
+ `<button type="submit">Save</button></form>` +
43
+ `<fieldset><legend>Metrics</legend><p class="meta">Prometheus reads <code>${e(metrics)}</code> with an instance operator's API token (<a href="/settings/sessions">Sessions &amp; tokens</a>).</p>` +
44
+ `<details><summary>Example</summary><pre>curl -H "Authorization: Bearer so_…" ${e(metrics)}</pre></details></fieldset></section>`;
45
+ }
46
+ /** The webhook's signing secret, once: a focused page with no script. */
47
+ export function signingSecretHtml(url, secret) {
48
+ return `<h1>Signing secret</h1><div class="card"><p>Give this to the receiver at <span class="mono">${e(url)}</span>. It's shown once.</p><p class="mono secret-value">${e(secret)}</p>` +
49
+ `<p class="meta">Each request carries <code>x-standing-orders-signature: t=&lt;time&gt;,v1=&lt;HMAC-SHA256 of "time.body"&gt;</code>. Check it, and that the time is recent.</p></div>` +
50
+ `<p class="meta"><a href="/settings/monitoring">Back to Monitoring</a></p>`;
51
+ }
@@ -0,0 +1,34 @@
1
+ import type { SealedLedgerEntry, Store } from "./store.js";
2
+ import type { MonitoringSettings } from "./monitoring-settings.js";
3
+ export type Sink = "webhook" | "folder" | "traces";
4
+ export declare const SIGNATURE_HEADER = "x-standing-orders-signature";
5
+ /** An audit event: the ledger entry and its seal, as the stream sends it. */
6
+ export declare function auditEvent(entry: SealedLedgerEntry, instance: string): Record<string, unknown>;
7
+ /** The signature a receiver recomputes: HMAC-SHA256(secret, "<t>.<body>"), hex. */
8
+ export declare function signature(secret: string, timestamp: number, body: string): string;
9
+ export type MonitoringDeps = {
10
+ fetch?: typeof fetch;
11
+ now?: () => Date;
12
+ holder: string;
13
+ instance: string;
14
+ version: string;
15
+ };
16
+ /** One pass over every destination that's set up. Returns what it sent, per destination. */
17
+ export declare function monitoringPass(store: Store, settings: MonitoringSettings, deps: MonitoringDeps): Promise<Partial<Record<Sink, number>>>;
18
+ /** A destination's name in its status row: a digest of its address (the address itself may carry a key). */
19
+ export declare const targetOf: (address: string) => string;
20
+ /** Spans for the batch's finished runs and completed tasks (the rest of the ledger isn't traced; the cursor passes it). */
21
+ export declare function spansFor(store: Store, entries: SealedLedgerEntry[]): Record<string, unknown>[];
22
+ /** This build's version, from its package.json ("unknown" when it can't be read). */
23
+ export declare function packageVersion(): string;
24
+ /** Who this process is, for the lease: host, pid and a random part (a restarted pid is a new holder). */
25
+ export declare function monitoringHolder(): string;
26
+ /** The loop `up` runs: a pass every `everyMs`, settings read afresh each time (a change applies at the next pass). */
27
+ export declare function startMonitoring(options: {
28
+ store: Store;
29
+ settings: () => MonitoringSettings;
30
+ instance: string;
31
+ version: string;
32
+ everyMs?: number;
33
+ progress?: (line: string) => void;
34
+ }): () => void;
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Monitoring (v104): what Toolroll does, sent to the tools a company
3
+ * already watches. One loop in `up` makes a pass every few seconds; each
4
+ * destination is sent to by one process at a time (a lease in
5
+ * `monitoring_status`), in ledger order, at least once: its cursor moves only
6
+ * after a delivery lands, so a failure is retried (backing off to five
7
+ * minutes) and nothing is skipped or reordered.
8
+ *
9
+ * - `webhook`: sealed ledger entries, POSTed in batches, each request signed
10
+ * (`x-standing-orders-signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of
11
+ * "<t>.<body>" with the signing secret>`). Each batch carries the chain
12
+ * head, so the receiver keeps its own copy of the checkpoints.
13
+ * - `folder`: the same events as JSON Lines, a file per day.
14
+ * - `traces`: each finished run as an OpenTelemetry span (OTLP/HTTP JSON),
15
+ * and each completed task as the root span its runs hang under. Model,
16
+ * tokens, cost and timings; never a prompt, a diff or a file.
17
+ */
18
+ import { createHash, createHmac, randomUUID } from "node:crypto";
19
+ import { closeSync, constants, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, writeSync } from "node:fs";
20
+ import { hostname } from "node:os";
21
+ import { basename, join } from "node:path";
22
+ import { COMPLETION_ACTION } from "./result-completion.js";
23
+ export const SIGNATURE_HEADER = "x-standing-orders-signature";
24
+ const BATCH = 200;
25
+ const LEASE_MS = 60_000;
26
+ const TIMEOUT_MS = 10_000;
27
+ /** An audit event: the ledger entry and its seal, as the stream sends it. */
28
+ export function auditEvent(entry, instance) {
29
+ return {
30
+ type: "standing-orders.audit", instance, id: entry.id, at: entry.at, actor: entry.actor, project: entry.repo, task: entry.taskId, run: entry.runId,
31
+ action: entry.action, outcome: entry.outcome, source: entry.source, detail: entry.detail, seal: entry.seal,
32
+ };
33
+ }
34
+ /** The signature a receiver recomputes: HMAC-SHA256(secret, "<t>.<body>"), hex. */
35
+ export function signature(secret, timestamp, body) {
36
+ return `t=${timestamp},v1=${createHmac("sha256", secret).update(`${timestamp}.${body}`, "utf8").digest("hex")}`;
37
+ }
38
+ const backoff = (failures) => Math.min(5 * 60_000, 5_000 * 2 ** Math.min(failures, 10));
39
+ const hex = (text, length) => createHash("sha256").update(text, "utf8").digest("hex").slice(0, length);
40
+ const nanos = (at) => `${BigInt(Date.parse(at)) * 1000000n}`;
41
+ /** One pass over every destination that's set up. Returns what it sent, per destination. */
42
+ export async function monitoringPass(store, settings, deps) {
43
+ const sent = {};
44
+ // A new destination gets the whole audit history; traces start from now.
45
+ if (settings.webhook !== null)
46
+ sent.webhook = await deliver(store, "webhook", targetOf(settings.webhook.url), () => 0, deps, entries => postSigned(settings.webhook, entries, store, deps));
47
+ if (settings.folder !== null)
48
+ sent.folder = await deliver(store, "folder", targetOf(settings.folder.path), () => 0, deps, async (entries) => { appendFolder(settings.folder.path, entries, deps.instance); return entries.length; });
49
+ if (settings.traces !== null)
50
+ sent.traces = await deliver(store, "traces", targetOf(settings.traces.endpoint), () => store.ledgerHeadId(), deps, entries => postTraces(settings.traces, entries, store, deps));
51
+ return sent;
52
+ }
53
+ /** A destination's name in its status row: a digest of its address (the address itself may carry a key). */
54
+ export const targetOf = (address) => hex(`standing-orders/monitoring/${address}`, 16);
55
+ /** Send what's due to one destination: hold it, start it over if it now points elsewhere, read after its cursor,
56
+ * hand the batch over, and move the cursor only when it landed and nothing changed meanwhile. */
57
+ async function deliver(store, sink, target, start, deps, send) {
58
+ const now = deps.now ?? (() => new Date());
59
+ const at = now();
60
+ if (!store.holdMonitoring(sink, deps.holder, at, new Date(at.getTime() + LEASE_MS)))
61
+ return 0;
62
+ let status = store.monitoringStatus(sink)[0];
63
+ if (status === undefined || status.target !== target) {
64
+ store.resetMonitoring(sink, target, start());
65
+ status = store.monitoringStatus(sink)[0];
66
+ }
67
+ if (status.nextTryAt != null && Date.parse(status.nextTryAt) > at.getTime())
68
+ return 0;
69
+ const from = status.through;
70
+ const entries = store.sealedAfter(from, BATCH);
71
+ if (entries.length === 0)
72
+ return 0;
73
+ try {
74
+ const count = await send(entries);
75
+ return store.monitoringDelivered(sink, deps.holder, target, from, entries[entries.length - 1].id, count, now()) ? count : 0;
76
+ }
77
+ catch (error) {
78
+ store.monitoringFailed(sink, deps.holder, target, error instanceof Error ? error.message : String(error), now(), new Date(now().getTime() + backoff(status.failures + 1)));
79
+ return 0;
80
+ }
81
+ }
82
+ async function post(url, body, headers, deps) {
83
+ const response = await (deps.fetch ?? fetch)(url, {
84
+ method: "POST", body, redirect: "manual", signal: AbortSignal.timeout(TIMEOUT_MS),
85
+ headers: { "content-type": "application/json", "user-agent": `toolroll/${deps.version}`, ...headers },
86
+ });
87
+ await response.body?.cancel().catch(() => { });
88
+ if (response.status < 200 || response.status > 299)
89
+ throw new Error(`${new URL(url).host} answered ${response.status}`);
90
+ }
91
+ async function postSigned(webhook, entries, store, deps) {
92
+ const last = entries[entries.length - 1];
93
+ const body = JSON.stringify({ events: entries.map(one => auditEvent(one, deps.instance)), chain: { through: last.id, head: last.seal?.hash ?? null } });
94
+ const timestamp = Math.floor((deps.now ?? (() => new Date()))().getTime() / 1000);
95
+ await post(webhook.url, body, { [SIGNATURE_HEADER]: signature(webhook.secret, timestamp, body), "x-standing-orders-delivery": `${entries[0].id}-${last.id}` }, deps);
96
+ return entries.length;
97
+ }
98
+ /** Append events to the day's file (by each entry's own day), made readable by its owner only. The folder must be
99
+ * a real folder of this user's that nobody else can write to (a link, or a shared folder, could send the stream
100
+ * somewhere else). */
101
+ function appendFolder(folder, entries, instance) {
102
+ mkdirSync(folder, { recursive: true, mode: 0o700 });
103
+ const stat = lstatSync(folder);
104
+ if (stat.isSymbolicLink() || !stat.isDirectory())
105
+ throw new Error("the folder is a link, not a folder");
106
+ if (typeof process.getuid === "function" && stat.uid !== process.getuid())
107
+ throw new Error("the folder belongs to someone else");
108
+ if ((stat.mode & 0o022) !== 0)
109
+ throw new Error("others can write to the folder");
110
+ const byDay = new Map();
111
+ for (const entry of entries) {
112
+ const day = /^\d{4}-\d{2}-\d{2}/.test(entry.at) ? entry.at.slice(0, 10) : "undated";
113
+ byDay.set(day, [...(byDay.get(day) ?? []), JSON.stringify(auditEvent(entry, instance))]);
114
+ }
115
+ for (const [day, lines] of byDay) {
116
+ // Never through a link someone left where the day's file goes: it would append to wherever it points.
117
+ const handle = openSync(join(folder, `standing-orders-audit-${day}.jsonl`), constants.O_WRONLY | constants.O_APPEND | constants.O_CREAT | constants.O_NOFOLLOW, 0o600);
118
+ try {
119
+ writeSync(handle, `${lines.join("\n")}\n`);
120
+ fsyncSync(handle);
121
+ }
122
+ finally {
123
+ closeSync(handle);
124
+ }
125
+ }
126
+ }
127
+ const money = (key, value) => value === null ? [] : [{ key, value: { doubleValue: value } }];
128
+ const attr = (key, value) => value === null || value === undefined ? [] : [{ key, value: typeof value === "string" ? { stringValue: value } : Number.isInteger(value) ? { intValue: String(value) } : { doubleValue: value } }];
129
+ /** Spans for the batch's finished runs and completed tasks (the rest of the ledger isn't traced; the cursor passes it). */
130
+ export function spansFor(store, entries) {
131
+ const spans = [];
132
+ const rootOf = (taskId) => store.revisionAncestryStatus(taskId).chain[0] ?? taskId;
133
+ for (const entry of entries) {
134
+ if (entry.source !== "work")
135
+ continue;
136
+ if (entry.action === "run finished" && entry.runId !== null) {
137
+ const run = store.getRun(entry.runId);
138
+ const taskId = run === null ? null : store.externalIdFor(run.taskRef);
139
+ if (run === null || taskId === null || run.finishedAt === null)
140
+ continue;
141
+ const root = rootOf(taskId);
142
+ const ref = store.refForId(run.taskRef);
143
+ const failed = run.outcome === "failed" || run.outcome === "refused" || run.outcome === "interrupted";
144
+ spans.push({
145
+ traceId: hex(`standing-orders/trace/${root}`, 32), spanId: hex(`standing-orders/run/${run.id}`, 16), parentSpanId: hex(`standing-orders/task/${root}`, 16),
146
+ name: `${run.role} · ${run.provider}`, kind: 1, startTimeUnixNano: nanos(run.startedAt), endTimeUnixNano: nanos(run.finishedAt),
147
+ attributes: [
148
+ ...attr("standing_orders.task.id", taskId), ...attr("standing_orders.task.root", root), ...attr("standing_orders.project", ref?.repo == null ? null : basename(ref.repo)),
149
+ ...attr("standing_orders.run.id", run.id), ...attr("standing_orders.run.role", run.role), ...attr("standing_orders.run.outcome", run.outcome),
150
+ ...attr("gen_ai.system", run.provider), ...attr("gen_ai.request.model", run.model), ...attr("gen_ai.usage.input_tokens", run.tokensIn),
151
+ ...attr("gen_ai.usage.output_tokens", run.tokensOut), ...money("standing_orders.cost_usd", run.costUsd),
152
+ ],
153
+ // The outcome's word only: a run's reason is free text (a path, a provider's message).
154
+ status: failed ? { code: 2, message: run.outcome ?? "failed" } : { code: 1 },
155
+ });
156
+ }
157
+ else if (entry.action === COMPLETION_ACTION && entry.taskId !== null) {
158
+ const root = rootOf(entry.taskId);
159
+ const task = store.getTask(root);
160
+ if (task === null)
161
+ continue;
162
+ const ref = store.lookupRef(root);
163
+ spans.push({
164
+ traceId: hex(`standing-orders/trace/${root}`, 32), spanId: hex(`standing-orders/task/${root}`, 16),
165
+ name: `task · ${ref?.repo == null ? "unplaced" : basename(ref.repo)}`, kind: 1, startTimeUnixNano: nanos(task.createdAt), endTimeUnixNano: nanos(entry.at),
166
+ attributes: [...attr("standing_orders.task.id", root), ...attr("standing_orders.project", ref?.repo == null ? null : basename(ref.repo)), ...attr("standing_orders.completed_by", entry.actor)],
167
+ status: { code: 1 },
168
+ });
169
+ }
170
+ }
171
+ return spans;
172
+ }
173
+ async function postTraces(traces, entries, store, deps) {
174
+ const spans = spansFor(store, entries);
175
+ if (spans.length === 0)
176
+ return 0;
177
+ const body = JSON.stringify({ resourceSpans: [{
178
+ resource: { attributes: [...attr("service.name", "toolroll"), ...attr("service.version", deps.version), ...attr("service.instance.id", deps.instance)] },
179
+ scopeSpans: [{ scope: { name: "toolroll", version: deps.version }, spans }],
180
+ }] });
181
+ const url = traces.endpoint.endsWith("/v1/traces") ? traces.endpoint : `${traces.endpoint}/v1/traces`;
182
+ await post(url, body, traces.header === null ? {} : { [traces.header.name]: traces.header.value }, deps);
183
+ return spans.length;
184
+ }
185
+ /** This build's version, from its package.json ("unknown" when it can't be read). */
186
+ export function packageVersion() {
187
+ try {
188
+ const parsed = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
189
+ return typeof parsed.version === "string" ? parsed.version : "unknown";
190
+ }
191
+ catch {
192
+ return "unknown";
193
+ }
194
+ }
195
+ /** Who this process is, for the lease: host, pid and a random part (a restarted pid is a new holder). */
196
+ export function monitoringHolder() {
197
+ return `${hostname()}:${process.pid}:${randomUUID().slice(0, 8)}`;
198
+ }
199
+ /** The loop `up` runs: a pass every `everyMs`, settings read afresh each time (a change applies at the next pass). */
200
+ export function startMonitoring(options) {
201
+ const holder = monitoringHolder();
202
+ let stopped = false, timer = null;
203
+ const pass = async () => {
204
+ if (stopped)
205
+ return;
206
+ try {
207
+ const settings = options.settings();
208
+ if (settings.webhook !== null || settings.folder !== null || settings.traces !== null) {
209
+ await monitoringPass(options.store, settings, { holder, instance: options.instance, version: options.version });
210
+ }
211
+ }
212
+ catch (error) {
213
+ options.progress?.(`monitoring: a pass failed (${error instanceof Error ? error.message : String(error)})`);
214
+ }
215
+ if (!stopped)
216
+ timer = setTimeout(() => void pass(), options.everyMs ?? 5_000);
217
+ };
218
+ timer = setTimeout(() => void pass(), 1_000);
219
+ return () => { stopped = true; if (timer !== null)
220
+ clearTimeout(timer); };
221
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The product's internal name, and every older one it still answers to.
3
+ *
4
+ * Toolroll was Standing Orders, which was nightorders. A rename must never
5
+ * orphan an installation: new things use the new name, while a folder,
6
+ * variable, branch, launchd job or skill that already exists under an older
7
+ * name keeps being found. Nothing is moved; the old one is used until one
8
+ * exists under the new name. Strings that feed a hash, a digest or a stored
9
+ * format id are not names in this sense and stay byte-identical.
10
+ */
11
+ export declare const NAME = "toolroll";
12
+ /** Older names, newest first. */
13
+ export declare const LEGACY_NAMES: readonly ["standing-orders", "nightorders"];
14
+ export declare const NAMES: readonly string[];
15
+ /**
16
+ * A file inside the product's folder under `base` (~/.config, ~/.cache, or a
17
+ * home dotfolder when `dot` is set). The first name that already holds the
18
+ * file wins; otherwise the file goes into the folder that holds the database,
19
+ * else the first folder that exists, else a new-name folder — so one install
20
+ * keeps one folder, and a fresh one gets ~/.config/toolroll.
21
+ */
22
+ export declare function namedPath(base: string, rest: readonly string[], options?: {
23
+ dot?: boolean;
24
+ names?: readonly string[];
25
+ }): string;
26
+ /** The product's folder under `base`: see namedPath. */
27
+ export declare function namedFolder(base: string, options?: {
28
+ dot?: boolean;
29
+ names?: readonly string[];
30
+ }): string;
31
+ /** Every folder under `base` that any name has made, for fencing and cleanup. */
32
+ export declare function existingFolders(base: string, options?: {
33
+ dot?: boolean;
34
+ }): string[];
35
+ /** ~/.config (or $XDG_CONFIG_HOME). */
36
+ export declare function configBase(env: Record<string, string | undefined>, home: string): string;
37
+ export declare const ENV_PREFIX = "TOOLROLL_";
38
+ export declare const LEGACY_ENV_PREFIX = "STANDING_ORDERS_";
39
+ /**
40
+ * An environment setting by its suffix ("DB" → TOOLROLL_DB). The new name
41
+ * wins whenever it is set; the old STANDING_ORDERS_ one is the fallback.
42
+ */
43
+ export declare function envValue(env: Record<string, string | undefined>, suffix: string): string | undefined;
44
+ /** The Telegram bot token's variable, and the name it had before the rename: agents never see either.
45
+ * Kept here, in a module with no imports of its own, so builder.ts can read it at load time. */
46
+ export declare const TELEGRAM_TOKEN_ENV = "TOOLROLL_TELEGRAM_TOKEN";
47
+ export declare const TELEGRAM_TOKEN_ENVS: readonly string[];
48
+ /** Both names of a setting, for a child the plane starts: older tools read the old one. */
49
+ export declare function envTwins(suffix: string, value: string): Record<string, string>;
50
+ /** True for a variable of either name ("TOOLROLL_DB", "STANDING_ORDERS_DB"). */
51
+ export declare function isOwnEnv(key: string): boolean;
52
+ /** New task branches are toolroll/<id>; the plane's older ones stay its own. */
53
+ export declare const BRANCH_PREFIX = "toolroll/";
54
+ export declare const BRANCH_PREFIXES: readonly string[];
55
+ export declare function taskBranch(id: string): string;
56
+ /** A branch the plane made, under either name. */
57
+ export declare function isOwnBranch(branch: string): boolean;
58
+ /**
59
+ * Whether a head branch is inside a publication grant's prefix. A grant made
60
+ * for the plane's own branches under one name covers the same branches under
61
+ * the other: a standing-orders/ grant still publishes toolroll/<id> work.
62
+ */
63
+ export declare function headWithin(head: string, prefix: string): boolean;
64
+ /** Every name a task's own branch may have, newest first. */
65
+ export declare function taskBranches(id: string): string[];
66
+ /** The first candidate branch that already exists (a retry reuses its branch), else the first — the new name. */
67
+ export declare function existingOrFirst(candidates: readonly string[], exists: (branch: string) => Promise<boolean>): Promise<string>;