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,20 @@
1
+ import { ChatDeliveryError, type ChatIdentity } from "./chat-delivery-state.js";
2
+ import { chatObject as object } from "./chat-delivery.js";
3
+ export { object };
4
+ export declare const discordId: (v: unknown) => v is string;
5
+ export type DiscordCredentials = ChatIdentity & {
6
+ botToken: string;
7
+ };
8
+ export type DiscordApi = (method: "GET" | "POST" | "PATCH", path: string, body?: Record<string, unknown>, file?: {
9
+ bytes: Uint8Array;
10
+ name: string;
11
+ }) => Promise<Record<string, unknown>>;
12
+ export declare class DiscordError extends ChatDeliveryError {
13
+ }
14
+ export declare function discordApi(token: string, fetcher?: typeof fetch): DiscordApi;
15
+ export declare const discordCredentialFile: (dir: string) => string;
16
+ export declare function loadDiscordCredentials(dir: string): DiscordCredentials | null;
17
+ export declare function saveDiscordCredentials(dir: string, value: DiscordCredentials): void;
18
+ export declare function clearDiscordCredentials(dir: string): void;
19
+ export declare function checkDiscordCredentials(botToken: string, fetcher?: typeof fetch): Promise<DiscordCredentials>;
20
+ export declare function discordMember(api: DiscordApi, member: string, channel: string): Promise<boolean>;
@@ -0,0 +1,138 @@
1
+ /** Discord REST requests and private installation credentials. */
2
+ import { chmodSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { randomBytes } from "node:crypto";
5
+ import { ChatDeliveryError, chatHash, } from "./chat-delivery-state.js";
6
+ import { chatObject as object } from "./chat-delivery.js";
7
+ export { object };
8
+ export const discordId = (v) => typeof v === "string" && /^[1-9][0-9]{16,19}$/.test(v);
9
+ export class DiscordError extends ChatDeliveryError {
10
+ }
11
+ export function discordApi(token, fetcher = fetch) {
12
+ return async (method, path, body, file) => {
13
+ // Only relative API routes; never send the bot credential to supplied URLs.
14
+ if (!/^\/(?:users\/@me|users\/[0-9]+|oauth2\/applications\/@me|channels\/[0-9]+(?:\/messages(?:\/[0-9]+)?(?:\?limit=100)?)?|interactions\/[0-9]+\/[A-Za-z0-9._-]+\/callback)$/.test(path))
15
+ throw new DiscordError("Invalid Discord request");
16
+ let response;
17
+ const headers = { Authorization: `Bot ${token}` };
18
+ let payload;
19
+ if (file) {
20
+ const form = new FormData();
21
+ form.set("payload_json", JSON.stringify(body ?? {}));
22
+ form.set("files[0]", new Blob([Buffer.from(file.bytes)]), file.name);
23
+ payload = form;
24
+ }
25
+ else if (body) {
26
+ headers["Content-Type"] = "application/json";
27
+ payload = JSON.stringify(body);
28
+ }
29
+ try {
30
+ response = await fetcher(`https://discord.com/api/v10${path}`, {
31
+ method,
32
+ headers,
33
+ ...(payload ? { body: payload } : {}),
34
+ redirect: "error",
35
+ signal: AbortSignal.timeout(15_000),
36
+ });
37
+ }
38
+ catch {
39
+ throw new DiscordError("Discord did not confirm delivery", 15_000, true);
40
+ }
41
+ let raw;
42
+ try {
43
+ raw = response.status === 204 ? {} : await response.json();
44
+ }
45
+ catch {
46
+ throw new DiscordError("Discord returned an incomplete receipt", 15_000, true);
47
+ }
48
+ const data = object(raw);
49
+ if (response.status === 429) {
50
+ const seconds = Number(data.retry_after ?? response.headers.get("retry-after"));
51
+ throw new DiscordError("ratelimited", Number.isFinite(seconds) && seconds > 0
52
+ ? Math.ceil(seconds * 1000)
53
+ : 60_000);
54
+ }
55
+ if (!response.ok) {
56
+ const message = response.status === 401
57
+ ? "Discord access expired. Disconnect and reconnect with the current bot token."
58
+ : response.status === 403
59
+ ? "Discord refused access. Check the app and direct-message permissions."
60
+ : response.status === 404
61
+ ? "The Discord message or conversation is unavailable. Open the saved chat."
62
+ : response.status === 413
63
+ ? "Discord refused this file size. Open the saved result to view the evidence."
64
+ : "Discord could not complete the request";
65
+ throw new DiscordError(message, response.status >= 500 ? 15_000 : 60_000, response.status >= 500);
66
+ }
67
+ return Array.isArray(raw) ? { items: raw } : data;
68
+ };
69
+ }
70
+ export const discordCredentialFile = (dir) => join(dir, "discord-connection.json");
71
+ export function loadDiscordCredentials(dir) {
72
+ try {
73
+ const v = JSON.parse(readFileSync(discordCredentialFile(dir), "utf8"));
74
+ return validToken(v.botToken) &&
75
+ discordId(v.app) &&
76
+ discordId(v.bot) &&
77
+ typeof v.workspace === "string" &&
78
+ v.installation === chatHash(`discord:${v.app}:${v.bot}`)
79
+ ? v
80
+ : null;
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ }
86
+ const validToken = (token) => typeof token === "string" && /^[A-Za-z0-9._-]{30,300}$/.test(token);
87
+ export function saveDiscordCredentials(dir, value) {
88
+ const target = discordCredentialFile(dir), temp = `${target}.${randomBytes(8).toString("hex")}.tmp`;
89
+ try {
90
+ writeFileSync(temp, JSON.stringify(value) + "\n", {
91
+ mode: 0o600,
92
+ flag: "wx",
93
+ });
94
+ chmodSync(temp, 0o600);
95
+ renameSync(temp, target);
96
+ }
97
+ finally {
98
+ rmSync(temp, { force: true });
99
+ }
100
+ }
101
+ export function clearDiscordCredentials(dir) {
102
+ rmSync(discordCredentialFile(dir), { force: true });
103
+ }
104
+ export async function checkDiscordCredentials(botToken, fetcher = fetch) {
105
+ if (!validToken(botToken))
106
+ throw new DiscordError("Enter the bot token from your Discord application");
107
+ const api = discordApi(botToken, fetcher), user = await api("GET", "/users/@me"), app = await api("GET", "/oauth2/applications/@me");
108
+ if (!discordId(user.id) ||
109
+ user.bot !== true ||
110
+ !discordId(app.id) ||
111
+ (app.bot !== undefined && object(app.bot).id !== user.id))
112
+ throw new DiscordError("Discord did not confirm the app and bot identity");
113
+ if (typeof app.interactions_endpoint_url === "string" &&
114
+ app.interactions_endpoint_url)
115
+ throw new DiscordError("Clear the app’s Interactions Endpoint URL in Discord so buttons can reach this installation.");
116
+ return {
117
+ botToken,
118
+ app: app.id,
119
+ bot: user.id,
120
+ workspace: typeof app.name === "string" ? app.name : "Toolroll",
121
+ installation: chatHash(`discord:${app.id}:${user.id}`),
122
+ };
123
+ }
124
+ export async function discordMember(api, member, channel) {
125
+ if (!discordId(member) || !discordId(channel))
126
+ return false;
127
+ const user = await api("GET", `/users/${member}`), dm = await api("GET", `/channels/${channel}`);
128
+ const recipients = Array.isArray(dm.recipients)
129
+ ? dm.recipients.map(object)
130
+ : [];
131
+ return (user.id === member &&
132
+ user.bot !== true &&
133
+ user.system !== true &&
134
+ dm.id === channel &&
135
+ dm.type === 1 &&
136
+ recipients.length === 1 &&
137
+ recipients[0]?.id === member);
138
+ }
@@ -0,0 +1,16 @@
1
+ import { ChatState, type ChatIdentity } from "./chat-delivery-state.js";
2
+ import { type ChatDeliveryOptions } from "./chat-delivery.js";
3
+ import { type DiscordApi } from "./discord-api.js";
4
+ export type DiscordChatOptions = Omit<ChatDeliveryOptions, "state" | "label" | "member" | "partSize" | "maxProposal"> & {
5
+ api: DiscordApi;
6
+ };
7
+ export declare const discordDelivery: (options: DiscordChatOptions) => ChatDeliveryOptions;
8
+ export declare const processDiscordEvent: (options: DiscordChatOptions) => Promise<boolean>;
9
+ export declare const planDiscordRooms: (options: DiscordChatOptions) => Promise<void>;
10
+ export declare const planDiscordNotifications: (options: DiscordChatOptions) => Promise<void>;
11
+ /** Save only normalized input. Interaction credentials and pairing codes never enter SQLite. */
12
+ export declare function receiveDiscord(state: ChatState, identity: ChatIdentity, type: string, raw: unknown, now: Date): boolean;
13
+ /** Escape Discord's presentation syntax; allowed_mentions also prevents actual pings. */
14
+ export declare const discordPlain: (text: string) => string;
15
+ export declare function discordCard(text: string, buttons?: Record<string, unknown>[]): Record<string, unknown>;
16
+ export declare function deliverDiscordPart(options: DiscordChatOptions): Promise<boolean>;
@@ -0,0 +1,381 @@
1
+ /** Discord messages and buttons transport the shared assistant's saved actions. */
2
+ import { chatQuestionButtons } from "./teammate-question.js";
3
+ import { chatFlowButtons } from "./chat-flow.js";
4
+ import { channelInbox } from "./chat-inbox.js";
5
+ import { roomCommand } from "./chat-rooms.js";
6
+ import { ChatState, ChatDeliveryError, chatHash, } from "./chat-delivery-state.js";
7
+ import { processChatEvent, planChatNotifications, channelAccess, chatObject as object, planRoomMessages, } from "./chat-delivery.js";
8
+ import { chatResultHref } from "./chat-controls.js";
9
+ import { MATE_MESSAGE_MAX_CHARS } from "./mate.js";
10
+ import { proposalPreview, proposalLink, proposalOutcomeText, armedCardText, armedYesLabel, } from "./chat-channel.js";
11
+ import { resultImageFileName, safeResultImageCaption, verifyResultImage, } from "./chat-evidence.js";
12
+ import { discordId, discordMember, DiscordError, } from "./discord-api.js";
13
+ export const discordDelivery = (options) => ({
14
+ ...options,
15
+ state: new ChatState(options.store, "discord"),
16
+ label: "Discord",
17
+ member: (member, channel) => discordMember(options.api, member, channel),
18
+ partSize: 1800,
19
+ maxProposal: 3400,
20
+ });
21
+ export const processDiscordEvent = (options) => processChatEvent(discordDelivery(options));
22
+ export const planDiscordRooms = (options) => planRoomMessages(discordDelivery(options));
23
+ export const planDiscordNotifications = (options) => planChatNotifications(discordDelivery(options));
24
+ /** Save only normalized input. Interaction credentials and pairing codes never enter SQLite. */
25
+ export function receiveDiscord(state, identity, type, raw, now) {
26
+ const body = object(raw);
27
+ if (body.webhook_id !== undefined)
28
+ return false;
29
+ // A guild channel is a room: accepted only while it follows a conversation,
30
+ // or for the `/team` words that make it follow one.
31
+ const isRoom = body.guild_id !== undefined;
32
+ let member, channel, id, ts, thread, kind, payload;
33
+ if (type === "MESSAGE_CREATE") {
34
+ const author = object(body.author), ref = object(body.message_reference);
35
+ if (author.bot === true ||
36
+ author.system === true ||
37
+ ![0, 19].includes(Number(body.type)) ||
38
+ typeof body.content !== "string" ||
39
+ (!isRoom && ref.guild_id !== undefined))
40
+ return false;
41
+ member = author.id;
42
+ channel = body.channel_id;
43
+ id = body.id;
44
+ ts = body.id;
45
+ thread = ref.message_id ?? body.id;
46
+ if (ref.channel_id !== undefined && ref.channel_id !== channel)
47
+ return false;
48
+ const pair = /^pair ([a-f0-9]{32})$/.exec(body.content.trim());
49
+ kind = pair ? "pair" : "message";
50
+ payload = pair
51
+ ? { hash: chatHash(pair[1]) }
52
+ : {
53
+ text: body.content.slice(0, MATE_MESSAGE_MAX_CHARS + 1),
54
+ originalLength: body.content.length,
55
+ ...(Array.isArray(body.attachments) && body.attachments.length
56
+ ? {
57
+ unsupported: "Incoming files are not supported yet. Describe the request in a message; saved result screenshots can still be sent here.",
58
+ }
59
+ : {}),
60
+ };
61
+ }
62
+ else if (type === "INTERACTION_CREATE") {
63
+ const data = object(body.data), message = object(body.message);
64
+ if (body.type !== 3 ||
65
+ body.application_id !== identity.app ||
66
+ (isRoom ? object(body.channel).type !== 0 : object(body.channel).type !== 1) ||
67
+ object(message.author).id !== identity.bot ||
68
+ message.channel_id !== body.channel_id ||
69
+ !/^so_[a-f0-9]{32}$/.test(String(data.custom_id)))
70
+ return false;
71
+ member = isRoom ? object(object(body.member).user).id : object(body.user).id;
72
+ channel = body.channel_id;
73
+ id = body.id;
74
+ ts = message.id;
75
+ thread = message.id;
76
+ kind = "action";
77
+ payload = { token: String(data.custom_id).slice(3) };
78
+ }
79
+ else
80
+ return false;
81
+ if (!discordId(member) ||
82
+ !discordId(channel) ||
83
+ !discordId(id) ||
84
+ !discordId(ts) ||
85
+ !discordId(thread) ||
86
+ member === identity.bot)
87
+ return false;
88
+ // v89: a channel that feeds a flow takes anyone's message (as a card, with no say over anything); "flow 12" connects one.
89
+ const inbox = isRoom && kind === "message" ? channelInbox(state.store, "discord", identity.installation, String(channel), String(payload.text ?? "")) : { watched: false, command: false };
90
+ const roomish = isRoom && (state.room(identity.installation, String(channel)) !== null || (kind === "message" && roomCommand(String(payload.text ?? "")) !== null) || inbox.watched || inbox.command);
91
+ if (isRoom && !roomish)
92
+ return false;
93
+ const binding = state.bindingFor(identity.installation, member);
94
+ const open = inbox.watched && !inbox.command;
95
+ if (kind === "pair"
96
+ ? !!binding || isRoom
97
+ : !open && (!binding ||
98
+ !state.live(binding) ||
99
+ (binding.channel !== channel && !roomish)))
100
+ return false;
101
+ return state.enqueue({
102
+ id: chatHash(`${identity.installation}:${kind === "action" ? "interaction" : "message"}:${id}`),
103
+ installation: identity.installation,
104
+ binding: kind === "pair" || binding === null ? null : binding.id,
105
+ kind,
106
+ channel,
107
+ member,
108
+ ts,
109
+ thread,
110
+ payload: JSON.stringify(payload),
111
+ created: now.toISOString(),
112
+ });
113
+ }
114
+ function link(origin, target) {
115
+ if (!origin ||
116
+ !target ||
117
+ !target.path.startsWith("/") ||
118
+ target.path.startsWith("//"))
119
+ return [];
120
+ try {
121
+ const url = new URL(origin);
122
+ if (url.protocol !== "https:" ||
123
+ url.username ||
124
+ url.password ||
125
+ url.search ||
126
+ url.hash)
127
+ return [];
128
+ return [
129
+ {
130
+ type: 2,
131
+ style: 5,
132
+ label: target.label.slice(0, 80),
133
+ url: url.origin + target.path,
134
+ },
135
+ ];
136
+ }
137
+ catch {
138
+ return [];
139
+ }
140
+ }
141
+ /** Escape Discord's presentation syntax; allowed_mentions also prevents actual pings. */
142
+ export const discordPlain = (text) => text.replace(/([\\`*_~|>#\[\]])/g, "\\$1").replace(/@/g, "@\u200b");
143
+ export function discordCard(text, buttons = []) {
144
+ const lines = text.split("\n"), first = lines[0] ?? "Toolroll", title = discordPlain(first).length <= 256 &&
145
+ first.length <= 150 &&
146
+ lines.length > 1
147
+ ? first
148
+ : "Toolroll", detail = title === first ? lines.slice(1).join("\n").trim() : text;
149
+ // Long answers were split before this point; approval controls only appear when full terms fit.
150
+ return {
151
+ content: "",
152
+ embeds: [
153
+ {
154
+ title: discordPlain(title),
155
+ description: discordPlain(detail || title).slice(0, 4096),
156
+ color: 0x297b70,
157
+ },
158
+ ],
159
+ // A row holds at most 5 buttons (a question's 4 options and "Answer in words", then the link, need two).
160
+ components: Array.from({ length: Math.min(5, Math.ceil(buttons.length / 5)) }, (_, row) => ({ type: 1, components: buttons.slice(row * 5, row * 5 + 5) })),
161
+ allowed_mentions: { parse: [], replied_user: false },
162
+ };
163
+ }
164
+ export async function deliverDiscordPart(options) {
165
+ const shared = discordDelivery(options), state = shared.state, { store, identity } = options, now = options.clock?.() ?? new Date();
166
+ if (!options.current() ||
167
+ !state.owns(identity.installation, options.owner, now))
168
+ return false;
169
+ const row = state
170
+ .prepare("SELECT p.* FROM chat_part p JOIN chat_event e ON e.id=p.event WHERE e.installation=? AND p.state='pending' AND (e.kind!='notice' OR ?=1) AND (p.next_at IS NULL OR p.next_at<=?) ORDER BY p.id LIMIT 1")
171
+ .get(identity.installation, options.canNotify?.() === false ? 0 : 1, now.toISOString());
172
+ if (!row)
173
+ return false;
174
+ const event = state.event(row.event), binding = event.binding === null ? null : state.bindingById(event.binding);
175
+ if (!binding ||
176
+ new Date(row.created).getTime() + 86_400_000 < now.getTime()) {
177
+ state
178
+ .prepare("UPDATE chat_part SET state='dropped',problem='Delivery expired or access changed; open the saved chat' WHERE id=?")
179
+ .run(row.id);
180
+ return true;
181
+ }
182
+ try {
183
+ const session = event.session === null ? null : store.getMateSession(event.session), repos = await channelAccess(shared, binding, session?.ceilingDigest);
184
+ if (event.kind === "notice" && options.canNotify?.() === false)
185
+ return false;
186
+ const content = JSON.parse(row.payload);
187
+ const destination = content.channel ?? binding.channel;
188
+ if (content.task &&
189
+ !repos.includes(store.lookupRef(content.task)?.repo ?? ""))
190
+ throw new DiscordError("Connected projects changed");
191
+ let text = content.text, buttons = [], file;
192
+ if (content.image) {
193
+ const image = verifyResultImage(store, options.evidenceRoot, repos, content.image);
194
+ if (!image.ok)
195
+ text = `Screenshot not sent\n${image.problem}. Open the saved result to review it.`;
196
+ else if (image.bytes.length > 10 * 1024 * 1024)
197
+ text =
198
+ "Screenshot exceeds Discord’s upload limit. Open the saved result to review it.";
199
+ else {
200
+ file = {
201
+ bytes: image.bytes,
202
+ name: resultImageFileName(content.image.taskId, content.image.run, content.image.artifact, image.format),
203
+ };
204
+ text = safeResultImageCaption(content.text, content.image.taskId, content.image.run);
205
+ }
206
+ buttons = link(options.origin(), {
207
+ label: "Open result",
208
+ path: chatResultHref(content.image.taskId, content.image.run, "checks"),
209
+ });
210
+ }
211
+ if (content.proposal) {
212
+ const proposal = store.getMateProposal(content.proposal);
213
+ if (!proposal)
214
+ text = "This proposal is unavailable.";
215
+ else if (proposal.state !== "pending")
216
+ text = proposalOutcomeText(proposal);
217
+ else {
218
+ const preview = proposalPreview(store, proposal, repos, "discord");
219
+ text =
220
+ content.phase === "armed"
221
+ ? armedCardText(proposal, preview.text)
222
+ : preview.text.replace("\n\nConfirm or Dismiss below. Nothing changes until you confirm.", "");
223
+ if (preview.buttons &&
224
+ preview.text.length <= 3400 &&
225
+ discordPlain(text).length <= 3900) {
226
+ buttons = state
227
+ .prepare("SELECT token,phase FROM chat_action WHERE part=? AND consumed IS NULL AND expires>? ORDER BY rowid")
228
+ .all(row.id, now.toISOString())
229
+ .map((action) => ({
230
+ type: 2,
231
+ style: action.phase === "yes" ? 4 : action.phase === "confirm" ? 1 : 2,
232
+ label: action.phase === "yes"
233
+ ? armedYesLabel(proposal)
234
+ : action.phase === "cancel"
235
+ ? "Cancel"
236
+ : action.phase === "dismiss"
237
+ ? "Dismiss"
238
+ : "Confirm",
239
+ custom_id: `so_${action.token}`,
240
+ }));
241
+ if (!buttons.length)
242
+ text = "This confirmation expired. Ask for a fresh proposal.";
243
+ }
244
+ else {
245
+ buttons = link(options.origin(), proposalLink(store, proposal, repos) ?? {
246
+ label: "Review action",
247
+ path: "/chat",
248
+ });
249
+ text = "Review the full action in Toolroll before confirming.";
250
+ if (!buttons.length)
251
+ text += " Open Toolroll on your computer.";
252
+ }
253
+ }
254
+ }
255
+ else if (!content.image)
256
+ buttons = [
257
+ // A flow decision (v88): Approve / Edit / Send back, then the link.
258
+ ...(content.flow
259
+ ? chatFlowButtons(state, row.id, now).map((one) => ({
260
+ type: 2,
261
+ style: one.action === "approve" ? 3 : 2,
262
+ label: one.label,
263
+ custom_id: `so_${one.token}`,
264
+ }))
265
+ : []),
266
+ // A teammate's question (v93): its options, then "Answer in words".
267
+ ...(content.question
268
+ ? chatQuestionButtons(state, row.id, now).map((one) => ({
269
+ type: 2,
270
+ style: one.words ? 1 : 2,
271
+ label: one.label.slice(0, 80),
272
+ custom_id: `so_${one.token}`,
273
+ }))
274
+ : []),
275
+ ...link(options.origin(), content.link),
276
+ ];
277
+ let target = content.edit ?? row.message;
278
+ if (target && content.image && row.uploaded) {
279
+ if (!file)
280
+ text =
281
+ "Screenshot delivered earlier. The saved evidence has since changed; open the result to review its current state.";
282
+ file = undefined;
283
+ }
284
+ const nonce = chatHash(`discord:part:${row.id}:${event.id}`).slice(0, 24);
285
+ // Recent Discord nonce receipts reconcile uncertain sends, including attachments.
286
+ if (row.uncertain && !target) {
287
+ const recent = await options.api("GET", `/channels/${destination}/messages?limit=100`);
288
+ const found = (Array.isArray(recent.items) ? recent.items : [])
289
+ .map(object)
290
+ .find((m) => m.nonce === nonce &&
291
+ object(m.author).id === identity.bot &&
292
+ m.channel_id === destination &&
293
+ discordId(m.id));
294
+ if (found) {
295
+ // Record the recovered placement, then repaint current terms/progress.
296
+ // A notification or proposal may have changed while delivery was uncertain.
297
+ target = String(found.id);
298
+ if (content.image) {
299
+ const attachments = Array.isArray(found.attachments)
300
+ ? found.attachments.map(object)
301
+ : [];
302
+ if (file &&
303
+ !attachments.some((a) => a.filename === file.name &&
304
+ Number(a.size) === file.bytes.length))
305
+ throw new DiscordError("Discord’s earlier file receipt does not match the saved evidence", 60_000);
306
+ if (!file && attachments.length)
307
+ text =
308
+ "Screenshot delivered earlier. The saved evidence has since changed; open the result to review its current state.";
309
+ // Preserve the original remote attachment; never upload it again on an edit.
310
+ file = undefined;
311
+ }
312
+ state
313
+ .prepare("UPDATE chat_part SET message=?,uploaded=? WHERE id=?")
314
+ .run(target, content.image &&
315
+ Array.isArray(found.attachments) &&
316
+ found.attachments.length
317
+ ? 1
318
+ : 0, row.id);
319
+ }
320
+ }
321
+ await channelAccess(shared, binding, session?.ceilingDigest);
322
+ if (file && content.image) {
323
+ const fresh = verifyResultImage(store, options.evidenceRoot, repos, content.image);
324
+ if (!fresh.ok)
325
+ throw new DiscordError("Screenshot evidence changed before upload");
326
+ file.bytes = fresh.bytes;
327
+ }
328
+ const args = discordCard(text, buttons);
329
+ if (file)
330
+ args.attachments = [
331
+ { id: 0, filename: file.name, description: text.slice(0, 1024) },
332
+ ];
333
+ if (!target) {
334
+ args.nonce = nonce;
335
+ args.enforce_nonce = true;
336
+ if (discordId(event.thread))
337
+ args.message_reference = {
338
+ message_id: event.thread,
339
+ channel_id: destination,
340
+ fail_if_not_exists: false,
341
+ };
342
+ }
343
+ const answer = await options.api(target ? "PATCH" : "POST", `/channels/${destination}/messages${target ? `/${target}` : ""}`, args, file);
344
+ if (!discordId(answer.id) ||
345
+ answer.channel_id !== destination ||
346
+ (target && answer.id !== target) ||
347
+ object(answer.author).id !== identity.bot ||
348
+ (file &&
349
+ (!Array.isArray(answer.attachments) ||
350
+ !answer.attachments.some((v) => object(v).filename === file.name &&
351
+ Number(object(v).size) === file.bytes.length))))
352
+ throw new DiscordError("Discord did not confirm the message and file identity", 15_000, true);
353
+ state
354
+ .prepare("UPDATE chat_part SET state='sent',message=?,attempts=attempts+1,problem=NULL,next_at=NULL WHERE id=?")
355
+ .run(answer.id, row.id);
356
+ state
357
+ .prepare("UPDATE chat_runtime SET problem=NULL WHERE installation=? AND owner=?")
358
+ .run(identity.installation, options.owner);
359
+ return true;
360
+ }
361
+ catch (error) {
362
+ const problem = error instanceof ChatDeliveryError
363
+ ? error
364
+ : new DiscordError("Discord delivery is waiting to retry", 15_000, true);
365
+ const until = new Date((options.clock?.() ?? new Date()).getTime() +
366
+ Math.max(problem.retryMs, [5000, 15000, 60000, 300000][Math.min(row.attempts, 3)])).toISOString();
367
+ state
368
+ .prepare("UPDATE chat_part SET attempts=attempts+1,uncertain=uncertain+?,next_at=?,problem=? WHERE id=?")
369
+ .run(problem.uncertain ? 1 : 0, until, problem.message, row.id);
370
+ state
371
+ .prepare("UPDATE chat_runtime SET problem=? WHERE installation=? AND owner=?")
372
+ .run(problem.code === "ratelimited"
373
+ ? "Discord asked us to wait. Saved replies will retry."
374
+ : problem.message, identity.installation, options.owner);
375
+ if (problem.code === "ratelimited")
376
+ state
377
+ .prepare("UPDATE chat_runtime SET retry_at=? WHERE installation=?")
378
+ .run(until, identity.installation);
379
+ return true;
380
+ }
381
+ }
@@ -0,0 +1,7 @@
1
+ import type { Store } from "./store.js";
2
+ export declare function discordSettingsHtml(store: Store, dir: string, csrf: string, options?: {
3
+ code?: string;
4
+ problem?: string;
5
+ now?: Date;
6
+ who?: string;
7
+ }): string;
@@ -0,0 +1,75 @@
1
+ import { loadPrimary } from "./webhooks.js";
2
+ import { loadDiscordCredentials } from "./discord-api.js";
3
+ import { ChatState } from "./chat-delivery-state.js";
4
+ const escape = (text) => text
5
+ .replaceAll("&", "&amp;")
6
+ .replaceAll("<", "&lt;")
7
+ .replaceAll(">", "&gt;")
8
+ .replaceAll('"', "&quot;")
9
+ .replaceAll("'", "&#39;");
10
+ export function discordSettingsHtml(store, dir, csrf, options = {}) {
11
+ const credentials = loadDiscordCredentials(dir), state = new ChatState(store, "discord"), bindings = credentials ? state.bindings(credentials.installation).filter(one => state.live(one)) : [], binding = options.who === undefined ? null : bindings.find(one => one.approver === options.who) ?? null, others = bindings.length - (binding === null ? 0 : 1);
12
+ const runtime = credentials
13
+ ? state.db
14
+ .prepare("SELECT connected,problem,lease_until FROM discord_runtime WHERE installation=?")
15
+ .get(credentials.installation)
16
+ : null;
17
+ const now = options.now ?? new Date(), live = runtime &&
18
+ typeof runtime.lease_until === "string" &&
19
+ runtime.lease_until > now.toISOString() &&
20
+ runtime.connected;
21
+ const hidden = `<input type="hidden" name="csrf" value="${escape(csrf)}">`;
22
+ const password = '<label>Your Toolroll password<input style="min-height:44px" type="password" name="password" autocomplete="current-password" required></label>';
23
+ const post = (action, content) => `<form method="post" action="/settings/discord/${action}" class="card">${hidden}${content}</form>`;
24
+ let content = "";
25
+ if (!credentials) {
26
+ content =
27
+ "<p>Manage projects and review results in a private Discord conversation.</p>" +
28
+ '<details open><summary>Create your Discord app</summary><ol><li><a href="https://discord.com/developers/applications" target="_blank" rel="noopener noreferrer">Create an application</a>, then copy its token from the Bot page.</li><li>Keep the Interactions Endpoint URL empty and privileged intents off.</li><li>Under Installation, enable Guild Install with the bot scope and no server permissions. Use its install link to add it to your server, then open a direct message to the bot.</li></ol></details>' +
29
+ post("connect", '<label>Bot token<input style="min-height:44px" type="password" name="bot-token" autocomplete="off" required></label>' +
30
+ password +
31
+ '<p class="meta">The token stays on this installation. Next, pair your own Discord account.</p><button type="submit">Connect Discord</button>');
32
+ }
33
+ else {
34
+ content = `<p><strong>${escape(credentials.workspace)}</strong> · ${live ? "Connected" : "Waiting for connection"}</p>`;
35
+ if (runtime?.problem)
36
+ content += `<p role="status">${escape(String(runtime.problem))}</p>`;
37
+ if (!live && !runtime?.problem)
38
+ content +=
39
+ "<p>Keep the Toolroll worker running to receive Discord messages.</p>";
40
+ if (options.code) {
41
+ content +=
42
+ "<h2>Pair your account</h2><p>Send this in a direct message to your Toolroll Discord app. It expires in 10 minutes.</p>" +
43
+ `<label>Pairing message<input type="text" readonly value="pair ${escape(options.code)}" autocomplete="off" style="width:100%;max-width:100%;font-size:14px;min-height:44px"></label>` +
44
+ '<a class="button-link" style="min-height:44px;white-space:nowrap" href="/settings/discord">Check connection</a>';
45
+ }
46
+ else if (binding && state.live(binding)) {
47
+ const pending = Number(state.db
48
+ .prepare("SELECT count(*) n FROM discord_part p JOIN discord_event e ON e.id=p.event WHERE e.binding=? AND p.state='pending'")
49
+ .get(binding.id)?.n ?? 0);
50
+ const failed = Number(state.db
51
+ .prepare("SELECT count(*) n FROM discord_part p JOIN discord_event e ON e.id=p.event WHERE e.binding=? AND p.state='dropped'")
52
+ .get(binding.id)?.n ?? 0);
53
+ content +=
54
+ `<p>Your Discord account is paired.${others ? ` ${others} teammate${others === 1 ? " is" : "s are"} paired too.` : ""}${pending ? ` ${pending} replies waiting to send.` : ""}${failed ? ` ${failed} replies could not be delivered. Open the saved chat to recover them.` : ""}</p>` +
55
+ "<p>Try “What needs my attention?” or “Show the evidence for the latest result.”</p>" +
56
+ '<p><a href="/chat">Open saved chat</a></p>' +
57
+ (loadPrimary(process.env, dir) === "discord"
58
+ ? "<p>Task updates are sent here.</p>"
59
+ : post("alerts", '<button type="submit">Send task updates here</button>'));
60
+ }
61
+ else {
62
+ content += post("pair", "<h2>Pair your account</h2><p>Your paired Discord account can read your connected projects and confirm proposed changes. Password approvals still open in Toolroll." +
63
+ (others ? ` ${others} teammate${others === 1 ? " is" : "s are"} already paired.` : "") + "</p>" +
64
+ password +
65
+ '<button type="submit">Create pairing code</button>');
66
+ }
67
+ if (binding && state.live(binding) && !options.code)
68
+ content += post("unpair", password + '<p class="meta">Unpairing ends every open button in your chat. Teammates are unaffected.</p><button type="submit">Unpair my account</button>');
69
+ content +=
70
+ "<details><summary>Connection settings</summary>" +
71
+ post("disconnect", password + '<button type="submit">Disconnect Discord</button>') +
72
+ "</details>";
73
+ }
74
+ return `<section style="max-width:42rem;overflow-wrap:anywhere"><p><a href="/settings">Settings</a></p><h1>Discord</h1>${options.problem ? `<p class="problem" role="alert">${escape(options.problem)}</p>` : ""}${content}<details><summary>Access and setup</summary><p>Private messages and replies use your Toolroll permissions. Shared channels are not supported. Secure review links use this installation’s configured HTTPS console address.</p><p>The worker connects out to Discord. It does not need a public webhook or a Tailscale account.</p></details></section>`;
75
+ }
@@ -0,0 +1,10 @@
1
+ import { type DiscordChatOptions } from "./discord-chat.js";
2
+ export declare function discordReadyMatches(raw: unknown, identity: {
3
+ app: string;
4
+ bot: string;
5
+ }): boolean;
6
+ export declare function followDiscord(options: Omit<DiscordChatOptions, "identity" | "api" | "owner" | "current"> & {
7
+ dir: string;
8
+ signal: AbortSignal;
9
+ notifications: () => boolean;
10
+ }): Promise<void>;