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,154 @@
1
+ /**
2
+ * A teammate's desk (v96): its own flow, where anything asked of it directly
3
+ * lands as a card — a message to it by name in a chat app ("@rosa, where's
4
+ * order 2201?", or "Rosa: …"), or one of its routines ("every weekday at
5
+ * 9:00: …") — and its answer goes back to whoever asked.
6
+ *
7
+ * The desk is an ordinary flow its manager owns and can redraw. The teammate
8
+ * handles each card there within its rules and tools (asking first when they
9
+ * say to), writes its answer, and picks where the card goes: Done, a code
10
+ * change (the Build zone files an ordinary task that a person approves as
11
+ * usual), or its manager. Nothing here gives it more than its rules do.
12
+ */
13
+ import { flowCardHref, flowDefinitionOf } from "./flow-engine.js";
14
+ import { flowFromSteps } from "./flows.js";
15
+ import { addFlowTriggerTo, removeFlowTrigger, runScheduleNow, scheduleFromWords, triggerConfigOf } from "./flow-triggers.js";
16
+ import { describeSchedule, parseSchedule } from "./routine.js";
17
+ import { labelOf, nameOf } from "./teammate-admin.js";
18
+ /** The zone on a desk where the teammate handles what it's asked. */
19
+ export const DESK_ZONE = "handle";
20
+ const DESK_INSTRUCTIONS = "Someone asked you this directly, or it's one of your routines. Do what they ask within your rules, using your tools when it needs them, and write your answer to them in \"text\": it goes back to whoever asked. Pick Done when you've answered, Needs a code change when what they want is a change to this project's code (it's filed as a task for a person to approve), and Needs a person when it's beyond you.";
21
+ /** Its desk, when it has one. */
22
+ export function deskOf(store, mate) {
23
+ const flow = mate.deskFlow === null ? null : store.getFlow(mate.deskFlow);
24
+ return flow !== null && flow.state === "active" ? flow : null;
25
+ }
26
+ /** Its desk, made the first time it's needed: owned by its manager, drawn like any flow. */
27
+ export function ensureDesk(store, mate, now) {
28
+ const had = deskOf(store, mate);
29
+ if (had !== null)
30
+ return had;
31
+ const name = nameOf(mate);
32
+ const person = `For ${mate.manager}`.slice(0, 60);
33
+ const definition = flowFromSteps([
34
+ { id: DESK_ZONE, title: "Requests", kind: "teammate", teammate: mate.handle, reply: true, instructions: DESK_INSTRUCTIONS,
35
+ routes: [{ answer: "Done", goesTo: "Done" }, { answer: "Needs a code change", goesTo: "Build it" }, { answer: "Needs a person", goesTo: person }], ifFails: person },
36
+ { id: "build", title: "Build it", kind: "task", planning: "auto", next: "Done" },
37
+ { id: "person", title: person, kind: "inbox" },
38
+ { id: "done", title: "Done", kind: "done" },
39
+ ], null);
40
+ const id = store.createFlow({ repo: mate.repo, name: `${name}'s desk`.slice(0, 80), definitionJson: JSON.stringify(definition), by: mate.manager }, now);
41
+ store.setTeammateDesk(mate.id, id);
42
+ return store.getFlow(id);
43
+ }
44
+ /** Who a desk card's answer goes to: the person who asked (when they can see the project), else its manager. */
45
+ export function askerOf(store, flow, card, mate) {
46
+ return store.accountOf(card.createdBy) !== null && store.accountCanAccess(card.createdBy, flow.repo) ? card.createdBy : mate.manager;
47
+ }
48
+ /** The answer a teammate wrote, back to whoever asked, under its name. Once per visit. */
49
+ export function replyToAsker(store, flow, card, mate, text, now) {
50
+ const to = askerOf(store, flow, card, mate);
51
+ store.enqueueNotification({ dedupeKey: `teammate-reply:${card.id}:${card.entry}`, kind: "teammate-reply", recipient: to, subject: `${labelOf(mate)}: ${card.title}`.slice(0, 200),
52
+ body: text.slice(0, 3500), link: flowCardHref(flow.id, card.id), source: { project: flow.repo } }, now);
53
+ }
54
+ /** "@rosa …", "@rosa, …", "Rosa: …" or "Rosa, …": the teammate a message is addressed to, and what it says. */
55
+ export function addressedTo(text) {
56
+ const at = /^\s*@([\p{L}\p{N}][\p{L}\p{N}-]{0,31})[\s,:]+([\s\S]*\S[\s\S]*)$/u.exec(text);
57
+ if (at !== null)
58
+ return { name: at[1], said: at[2].trim() };
59
+ const named = /^\s*([\p{L}][\p{L}\p{N}-]{0,31})\s*[,:]\s+([\s\S]*\S[\s\S]*)$/u.exec(text);
60
+ return named === null ? null : { name: named[1], said: named[2].trim() };
61
+ }
62
+ /**
63
+ * A message in someone's chat with Toolroll, addressed to a teammate
64
+ * by name, becomes a card on its desk; null when it isn't addressed to one of
65
+ * the teammates in their projects (it goes to the lead as usual).
66
+ */
67
+ export function messageTeammate(store, from, text, now) {
68
+ const addressed = addressedTo(text);
69
+ if (addressed === null)
70
+ return null;
71
+ const wanted = addressed.name.toLowerCase();
72
+ const mate = store.teammates(from.repos).find(one => one.handle === wanted || nameOf(one).toLowerCase() === wanted);
73
+ if (mate === undefined || !store.accountCanAccess(from.who, mate.repo))
74
+ return null;
75
+ const name = nameOf(mate);
76
+ if (mate.state !== "active")
77
+ return { said: `${name} is paused, so nothing was passed on. Resume ${name} on its page, or ask the lead.` };
78
+ const desk = ensureDesk(store, mate, now);
79
+ const definition = flowDefinitionOf(desk);
80
+ const zone = definition?.stages.find(one => one.id === DESK_ZONE) ?? definition?.stages.find(one => one.id === definition.start);
81
+ if (zone === undefined)
82
+ return { said: `${name}'s desk can't take cards right now.` };
83
+ const said = addressed.said.slice(0, 4000);
84
+ const firstLine = said.split("\n")[0].trim();
85
+ const title = firstLine.length <= 100 ? firstLine : `${firstLine.slice(0, 99)}…`;
86
+ const card = store.addFlowCard({ flow: desk.id, title, description: said === title ? null : said, stage: zone.id, by: from.who, source: { kind: "message", label: `${from.via} message`, url: null } }, now);
87
+ store.setFlowCardWatcher(card, from.who, true, now);
88
+ return { said: `${name} has it. The answer comes here when it's done.`, link: { label: "Open the card", path: flowCardHref(desk.id, card) } };
89
+ }
90
+ /** Its routines: the schedules on its desk that start in its zone. */
91
+ export function routinesOf(store, mate) {
92
+ const desk = deskOf(store, mate);
93
+ if (desk === null)
94
+ return [];
95
+ return store.flowTriggers(desk.id).flatMap(trigger => {
96
+ const config = triggerConfigOf(trigger);
97
+ if (config?.kind !== "schedule" || config.script !== undefined || trigger.state === "removed")
98
+ return [];
99
+ return [{ id: trigger.id, schedule: config.schedule, text: config.title, state: trigger.state, lastAt: trigger.lastAt, lastOutcome: trigger.lastOutcome, nextAt: trigger.nextAt }];
100
+ });
101
+ }
102
+ /** This computer's time zone: what a routine's time means when it names none. */
103
+ export const localZone = () => { try {
104
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
105
+ }
106
+ catch {
107
+ return "UTC";
108
+ } };
109
+ /** A routine's schedule as kept: a calendar time with no zone is this computer's time, not UTC. */
110
+ export function routineSchedule(words) {
111
+ const read = scheduleFromWords(words);
112
+ // A zone the person named, UTC included, stays theirs.
113
+ if (read === null || read.startsWith("every:") || read.includes("@") || /\b(utc|gmt)$/i.test(words.trim()))
114
+ return read;
115
+ return scheduleFromWords(`${words.trim()} ${localZone()}`) ?? read;
116
+ }
117
+ /** A routine: on this schedule ("weekdays 09:00", "daily 17:00 Europe/London", "every 2 hours"), a card on its desk saying what to do. */
118
+ export function addRoutine(store, mate, schedule, text, by, now, dir) {
119
+ const what = text.replace(/\s+/g, " ").trim();
120
+ if (what === "")
121
+ return { ok: false, said: "Say what it should do each time." };
122
+ if (what.length > 200)
123
+ return { ok: false, said: "Keep a routine to 200 characters; put detail in its soul file." };
124
+ if (routinesOf(store, mate).length >= 10)
125
+ return { ok: false, said: "A teammate has at most 10 routines." };
126
+ const kept = routineSchedule(schedule);
127
+ if (kept === null)
128
+ return { ok: false, said: "Say the schedule like “weekdays 09:00”, “daily 17:00”, “monday 09:00” or “every 2 hours”." };
129
+ const desk = ensureDesk(store, mate, now);
130
+ const made = addFlowTriggerTo(store, desk, { kind: "schedule", schedule: kept, title: what, zone: DESK_ZONE }, by, now, dir);
131
+ return made.ok ? { ok: true, said: `${nameOf(mate)} will do that ${describeSchedule(parseSchedule(kept))}. Its answer goes to ${mate.manager}.` } : { ok: false, said: made.message };
132
+ }
133
+ function routineOf(store, mate, id) {
134
+ const desk = deskOf(store, mate);
135
+ const trigger = desk === null ? null : store.flowTriggers(desk.id).find(one => one.id === id) ?? null;
136
+ return trigger !== null && trigger.state !== "removed" ? trigger : null;
137
+ }
138
+ export function removeRoutine(store, mate, id, now, dir) {
139
+ const trigger = routineOf(store, mate, id);
140
+ if (trigger === null)
141
+ return { ok: false, said: "That routine is already gone." };
142
+ removeFlowTrigger(store, trigger, now, dir);
143
+ return { ok: true, said: "Removed. It won't do that any more." };
144
+ }
145
+ /** Try a routine now: its card lands on the desk at once. */
146
+ export function runRoutine(store, mate, id, by, now) {
147
+ const trigger = routineOf(store, mate, id);
148
+ if (trigger === null)
149
+ return { ok: false, said: "That routine is gone." };
150
+ if (mate.state !== "active")
151
+ return { ok: false, said: `${nameOf(mate)} is paused. Resume it first.` };
152
+ const ran = runScheduleNow(store, trigger, by, now);
153
+ return ran.ok ? { ok: true, said: `${nameOf(mate)} is on it. The answer goes to ${mate.manager}.` } : ran;
154
+ }
@@ -0,0 +1,64 @@
1
+ import type { Store, TeammateCallRow, TeammateMemoryRow, TeammateRow } from "./store.js";
2
+ export declare const MEMORY_CHARS = 300;
3
+ /** Facts a teammate keeps on its own: past this, its oldest are forgotten. What people told it is never dropped this way. */
4
+ export declare const MEMORY_CAP = 300;
5
+ /** Approvals in a row of one action before the teammate suggests loosening its rule. */
6
+ export declare const SUGGEST_AFTER = 5;
7
+ export declare const ACCEPT = "accept", DISMISS = "dismiss";
8
+ type Done = {
9
+ ok: true;
10
+ said: string;
11
+ } | {
12
+ ok: false;
13
+ said: string;
14
+ };
15
+ /** The words a text is about: lower case, four letters or more, no filler. */
16
+ export declare function wordsOf(text: string): string[];
17
+ /** Two lines about the same thing: most of their words are shared. */
18
+ export declare function similar(a: string, b: string): boolean;
19
+ /** A memory as it may be kept: one line, plain, short, no secrets. */
20
+ export declare function cleanMemory(text: string): {
21
+ ok: true;
22
+ text: string;
23
+ } | {
24
+ ok: false;
25
+ said: string;
26
+ };
27
+ /** Keep one thing: from a person (what they told it), or from the teammate itself on a turn. The same words twice are kept once. */
28
+ export declare function remember(store: Store, mate: TeammateRow, text: string, from: {
29
+ source: TeammateMemoryRow["source"];
30
+ card?: number | null;
31
+ by: string;
32
+ }, now: Date): Done & {
33
+ id?: number;
34
+ };
35
+ /** Something a person tells a teammate (the page's "Tell Maya something", or the lead): kept in its memory, read by every turn while it's among the latest ten. */
36
+ export declare function tellTeammate(store: Store, mate: TeammateRow, note: string, by: string, now: Date): Done;
37
+ export declare function editMemory(store: Store, mate: TeammateRow, id: number, text: string, by: string, now: Date): Done;
38
+ export declare function forgetMemory(store: Store, mate: TeammateRow, id: number, by: string, now: Date): Done;
39
+ /** Memories whose text holds every word searched for. */
40
+ export declare function searchMemories(store: Store, mate: TeammateRow, query: string): TeammateMemoryRow[];
41
+ /**
42
+ * What one turn reads: what its people told it lately (oldest first), and
43
+ * what it kept that fits the card — the facts sharing most of the card's
44
+ * words, then its newest — up to eight.
45
+ */
46
+ export declare function memoriesFor(store: Store, mate: TeammateRow, cardText: string, now: Date): {
47
+ told: TeammateMemoryRow[];
48
+ kept: TeammateMemoryRow[];
49
+ };
50
+ /** A limit a person would pick: rounded up to a round number. */
51
+ export declare const roundUp: (value: number) => number;
52
+ /**
53
+ * After a person approves an ask-first call: when they have approved this
54
+ * action enough times in a row since it last suggested anything about it,
55
+ * the teammate suggests the rule that would have let it make them all.
56
+ */
57
+ export declare function considerSuggestion(store: Store, mate: TeammateRow, approved: TeammateCallRow, now: Date): number | null;
58
+ /** Its manager answers a suggestion: accept changes the one rule (if it is still what it was), anything else leaves it; their words are remembered. */
59
+ export declare function answerSuggestion(store: Store, mate: TeammateRow, suggestion: number, answer: {
60
+ choice: string | null;
61
+ text: string | null;
62
+ by: string;
63
+ }, now: Date): Done;
64
+ export {};
@@ -0,0 +1,196 @@
1
+ /**
2
+ * What a teammate remembers (v95), and how it learns from its people.
3
+ *
4
+ * Memory is short facts, each a line: what its people told it ("this week,
5
+ * offer free shipping instead of a refund" — v92's notes), and what it kept
6
+ * from its own turns ("Sam prefers email to phone"). Every turn reads what
7
+ * its people said lately, and what it kept that fits the card. People search,
8
+ * edit and forget any of it on the teammate's page or through the lead.
9
+ *
10
+ * Learning: when its manager approves the same ask-first tool action again
11
+ * and again (five in a row, none denied), the teammate suggests loosening
12
+ * that one rule — "You approved my last 5 refunds (amount 55 to 70): may I
13
+ * make them on my own up to 75?" — as a question its manager accepts or
14
+ * not, in their chat app or on its page. Accepting changes only that rule,
15
+ * and only if it is still what it was when suggested.
16
+ */
17
+ import { scanForSecrets } from "./evidence.js";
18
+ import { notifyPeople } from "./flow-people.js";
19
+ import { labelOf, nameOf } from "./teammate-admin.js";
20
+ import { defaultRule, numberFields, setToolRules } from "./teammate-tools.js";
21
+ export const MEMORY_CHARS = 300;
22
+ /** Facts a teammate keeps on its own: past this, its oldest are forgotten. What people told it is never dropped this way. */
23
+ export const MEMORY_CAP = 300;
24
+ /** Approvals in a row of one action before the teammate suggests loosening its rule. */
25
+ export const SUGGEST_AFTER = 5;
26
+ export const ACCEPT = "accept", DISMISS = "dismiss";
27
+ const STOP = new Set("about after again also been before being could does doing from have here into just like more most much must only other over same some such than that their them then there these they this those through very want were what when where which while will with would your yours please thanks thank hello".split(" "));
28
+ /** The words a text is about: lower case, four letters or more, no filler. */
29
+ export function wordsOf(text) {
30
+ return [...new Set(text.toLowerCase().normalize("NFKD").split(/[^\p{L}\p{N}]+/u).filter(one => one.length >= 4 && !STOP.has(one)))].slice(0, 60);
31
+ }
32
+ /** Two lines about the same thing: most of their words are shared. */
33
+ export function similar(a, b) {
34
+ const left = new Set(wordsOf(a)), right = new Set(wordsOf(b));
35
+ if (left.size === 0 || right.size === 0)
36
+ return false;
37
+ const shared = [...left].filter(one => right.has(one)).length;
38
+ return shared / new Set([...left, ...right]).size >= 0.6;
39
+ }
40
+ /** A memory as it may be kept: one line, plain, short, no secrets. */
41
+ export function cleanMemory(text) {
42
+ const line = text.replace(/[\u0000-\u001f\u007f]+/g, " ").replace(/\s+/g, " ").trim();
43
+ if (line === "")
44
+ return { ok: false, said: "Write what it should remember." };
45
+ if (line.length > MEMORY_CHARS)
46
+ return { ok: false, said: `Keep a memory to ${MEMORY_CHARS} characters; put lasting rules in its soul file.` };
47
+ if (scanForSecrets(line).length > 0)
48
+ return { ok: false, said: "That looks like a key or password. Keep secrets out of a teammate's memory." };
49
+ return { ok: true, text: line };
50
+ }
51
+ /** Keep one thing: from a person (what they told it), or from the teammate itself on a turn. The same words twice are kept once. */
52
+ export function remember(store, mate, text, from, now) {
53
+ const clean = cleanMemory(from.source === "teammate" ? text.slice(0, MEMORY_CHARS) : text);
54
+ if (!clean.ok)
55
+ return clean;
56
+ const same = store.teammateMemories(mate.id).find(one => one.text.toLowerCase() === clean.text.toLowerCase());
57
+ if (same !== undefined)
58
+ return { ok: true, said: `${nameOf(mate)} already remembers that.`, id: same.id };
59
+ // Saying nearly the same thing again on the same card (another turn of the same visit) refreshes what it kept instead of adding a second line.
60
+ if (from.source === "teammate" && from.card !== undefined && from.card !== null) {
61
+ const near = store.teammateMemories(mate.id, { source: "teammate" }).find(one => one.card === from.card && similar(one.text, clean.text));
62
+ if (near !== undefined) {
63
+ store.editTeammateMemory(near.id, clean.text, from.by, now);
64
+ return { ok: true, said: `${nameOf(mate)} already remembers that.`, id: near.id };
65
+ }
66
+ }
67
+ const id = store.addTeammateMemory({ teammate: mate.id, text: clean.text, source: from.source, card: from.card ?? null, by: from.by }, now);
68
+ if (from.source === "teammate")
69
+ for (const old of store.teammateMemories(mate.id, { source: "teammate" }).slice(MEMORY_CAP))
70
+ store.forgetTeammateMemory(old.id, "limit", now);
71
+ return { ok: true, said: `${nameOf(mate)} will remember that.`, id };
72
+ }
73
+ /** Something a person tells a teammate (the page's "Tell Maya something", or the lead): kept in its memory, read by every turn while it's among the latest ten. */
74
+ export function tellTeammate(store, mate, note, by, now) {
75
+ const kept = remember(store, mate, note, { source: "person", by }, now);
76
+ return kept.ok ? { ok: true, said: `${nameOf(mate)} will keep that in mind.` } : kept;
77
+ }
78
+ export function editMemory(store, mate, id, text, by, now) {
79
+ const memory = store.teammateMemory(id);
80
+ if (memory === null || memory.teammate !== mate.id)
81
+ return { ok: false, said: "It doesn't remember that any more." };
82
+ const clean = cleanMemory(text);
83
+ if (!clean.ok)
84
+ return clean;
85
+ store.editTeammateMemory(id, clean.text, by, now);
86
+ return { ok: true, said: "Saved." };
87
+ }
88
+ export function forgetMemory(store, mate, id, by, now) {
89
+ const memory = store.teammateMemory(id);
90
+ if (memory === null || memory.teammate !== mate.id)
91
+ return { ok: false, said: "It doesn't remember that any more." };
92
+ store.forgetTeammateMemory(id, by, now);
93
+ return { ok: true, said: `${nameOf(mate)} forgot it.` };
94
+ }
95
+ /** Memories whose text holds every word searched for. */
96
+ export function searchMemories(store, mate, query) {
97
+ const words = query.toLowerCase().split(/[^\p{L}\p{N}$]+/u).filter(one => one.length >= 2);
98
+ return store.teammateMemories(mate.id, { words, limit: 100 });
99
+ }
100
+ /**
101
+ * What one turn reads: what its people told it lately (oldest first), and
102
+ * what it kept that fits the card — the facts sharing most of the card's
103
+ * words, then its newest — up to eight.
104
+ */
105
+ export function memoriesFor(store, mate, cardText, now) {
106
+ const told = store.teammateMemories(mate.id, { source: "person", limit: 10 }).reverse();
107
+ const own = store.teammateMemories(mate.id, { source: "teammate", limit: MEMORY_CAP });
108
+ const words = wordsOf(cardText);
109
+ const scored = own.map(one => ({ one, score: words.filter(word => one.text.toLowerCase().includes(word)).length })).filter(one => one.score > 0)
110
+ .sort((a, b) => b.score - a.score || b.one.id - a.one.id).slice(0, 6).map(one => one.one);
111
+ const kept = [...scored, ...own.filter(one => !scored.includes(one))].slice(0, 8);
112
+ store.markTeammateMemoriesUsed(kept.map(one => one.id), now);
113
+ return { told, kept };
114
+ }
115
+ const numberOf = (value) => typeof value === "number" && Number.isFinite(value) ? value
116
+ : typeof value === "string" && /^\s*\$?\d+(\.\d+)?\s*$/.test(value) ? Number(value.replace(/[$\s]/g, "")) : null;
117
+ /** A limit a person would pick: rounded up to a round number. */
118
+ export const roundUp = (value) => { const step = value < 20 ? 1 : value < 100 ? 5 : value < 1000 ? 25 : 100; return Math.ceil(value / step) * step; };
119
+ /**
120
+ * After a person approves an ask-first call: when they have approved this
121
+ * action enough times in a row since it last suggested anything about it,
122
+ * the teammate suggests the rule that would have let it make them all.
123
+ */
124
+ export function considerSuggestion(store, mate, approved, now) {
125
+ const grant = store.teammateGrant(mate.id, approved.tool);
126
+ const info = grant?.actions.find(one => one.name === approved.action);
127
+ if (grant === null || info === undefined)
128
+ return null;
129
+ const rule = grant.rules[approved.action] ?? defaultRule(info);
130
+ if (rule.use === "never" || (rule.use === "free" && rule.limit === undefined))
131
+ return null;
132
+ const earlier = store.teammateSuggestionsFor(mate.id, approved.tool, approved.action);
133
+ if (earlier.some(one => one.state === "open"))
134
+ return null;
135
+ const since = earlier[0]?.createdAt ?? "";
136
+ const run = [];
137
+ for (const call of store.teammateCallsOf(mate.id, 400).filter(one => one.tool === approved.tool && one.action === approved.action && one.rule === "ask" && one.decidedBy !== null && (one.decidedAt ?? "") > since)) {
138
+ if (call.state === "denied")
139
+ break;
140
+ run.push(call);
141
+ }
142
+ if (run.length < SUGGEST_AFTER)
143
+ return null;
144
+ // The number the rule is limited on (or, with none, the one every approved call gave).
145
+ const field = rule.limit?.field ?? numberFields(info).find(name => run.every(call => numberOf(call.input[name]) !== null)) ?? null;
146
+ const values = field === null ? [] : run.map(call => numberOf(call.input[field])).filter((one) => one !== null);
147
+ let suggested, range = "";
148
+ if (field !== null && values.length === run.length) {
149
+ const over = roundUp(Math.max(...values));
150
+ if (rule.limit !== undefined && over <= rule.limit.over)
151
+ return null;
152
+ suggested = { use: "free", limit: { field, over } };
153
+ const low = Math.min(...values), high = Math.max(...values);
154
+ range = ` (${field} ${low === high ? low : `${low} to ${high}`})`;
155
+ }
156
+ else {
157
+ suggested = { use: "free" };
158
+ }
159
+ const card = store.getFlowCard(approved.card);
160
+ if (card === null)
161
+ return null;
162
+ const said = suggested.limit === undefined
163
+ ? `You approved my last ${run.length} ${approved.action} calls on ${approved.tool}. May I make them on my own from now on?`
164
+ : `You approved my last ${run.length} ${approved.action} calls on ${approved.tool}${range}. May I make them on my own up to ${suggested.limit.field} ${suggested.limit.over}, and ask you above that?`;
165
+ const id = store.addTeammateSuggestion({ teammate: mate.id, tool: approved.tool, action: approved.action, rule: suggested, was: rule, evidence: run.map(one => one.id), said }, now);
166
+ const question = store.openTeammateQuestion({ teammate: mate.id, card: card.id, entry: approved.entry, question: said, options: [{ id: ACCEPT, label: "Yes, change it" }, { id: DISMISS, label: "Not now" }], askedOf: mate.manager, suggestion: id }, now);
167
+ if (question === null)
168
+ return id;
169
+ notifyPeople(store, card, [mate.manager], null, { key: `teammate-q:${question}`, attention: false, subject: `${labelOf(mate)} suggests a rule change`,
170
+ body: `${said}\n\nThis changes only that one rule; you can change it back on ${nameOf(mate)}'s page.` }, now);
171
+ return id;
172
+ }
173
+ /** Its manager answers a suggestion: accept changes the one rule (if it is still what it was), anything else leaves it; their words are remembered. */
174
+ export function answerSuggestion(store, mate, suggestion, answer, now) {
175
+ const offered = store.teammateSuggestion(suggestion);
176
+ if (offered === null || offered.state !== "open")
177
+ return { ok: false, said: "That suggestion was already answered." };
178
+ if (answer.choice === ACCEPT) {
179
+ const grant = store.teammateGrant(mate.id, offered.tool);
180
+ const current = grant === null ? null : grant.rules[offered.action] ?? null;
181
+ const { undo: _undo, ...compared } = current ?? { use: "never" };
182
+ if (current === null || JSON.stringify(compared) !== JSON.stringify((({ undo: _was, ...rest }) => rest)(offered.was))) {
183
+ store.decideTeammateSuggestion(offered.id, "stale", null, now);
184
+ return { ok: false, said: "Its rules changed since it suggested this, so nothing was changed." };
185
+ }
186
+ const changed = setToolRules(store, mate, offered.tool, { [offered.action]: { use: offered.rule.use, limit: offered.rule.limit ?? null, undo: current.undo ?? "" } }, answer.by, now);
187
+ if (!changed.ok)
188
+ return changed;
189
+ store.decideTeammateSuggestion(offered.id, "accepted", answer.by, now);
190
+ return { ok: true, said: `Changed. ${nameOf(mate)} makes ${offered.action} calls on its own ${offered.rule.limit === undefined ? "from now on" : `up to ${offered.rule.limit.field} ${offered.rule.limit.over}`}.` };
191
+ }
192
+ store.decideTeammateSuggestion(offered.id, "dismissed", answer.by, now);
193
+ if (answer.text !== null && answer.text.trim() !== "")
194
+ remember(store, mate, `About ${offered.action} on ${offered.tool}: ${answer.text.trim()}`.slice(0, MEMORY_CHARS), { source: "person", by: answer.by }, now);
195
+ return { ok: true, said: "Left as it is." };
196
+ }
@@ -0,0 +1,59 @@
1
+ import type { ChatBinding, ChatContent, ChatEvent, ChatState } from "./chat-delivery-state.js";
2
+ import type { Store, TelegramBinding, TeammateQuestionRow } from "./store.js";
3
+ import type { InlineButton } from "./telegram-mate.js";
4
+ /** The key notifyPeople gives a teammate's question: flow-card:<card>:teammate-q:<question>:<person>. */
5
+ export declare const TEAMMATE_Q_KEY: RegExp;
6
+ export declare const IN_WORDS = "Answer in words";
7
+ /** The question a notice is about, while it is still open. */
8
+ export declare function openQuestionOf(store: Store, dedupeKey: string): TeammateQuestionRow | null;
9
+ /** Buttons for the question, minted before the send (for the person it asks only); `place` stamps the message they land on. */
10
+ export declare function telegramQuestionButtons(store: Store, binding: TelegramBinding, question: TeammateQuestionRow, now: Date): {
11
+ keyboard: InlineButton[][];
12
+ tokens: string[];
13
+ } | null;
14
+ export type QuestionTapEffect = {
15
+ kind: "ack";
16
+ text: string;
17
+ } | {
18
+ kind: "edit";
19
+ text: string;
20
+ } | {
21
+ kind: "prompt";
22
+ text: string;
23
+ placeholder: string;
24
+ question: number;
25
+ };
26
+ /** A tapped question button, inside the update's transaction. */
27
+ export declare function applyTelegramQuestionTap(store: Store, binding: TelegramBinding, action: NonNullable<ReturnType<Store["getTelegramQuestionAction"]>>, message: {
28
+ text: string;
29
+ }, now: Date): QuestionTapEffect[];
30
+ /** A reply to the "answer in words" prompt, inside the update's transaction. */
31
+ export declare function applyTelegramQuestionReply(store: Store, binding: TelegramBinding, prompt: NonNullable<ReturnType<Store["telegramQuestionPrompt"]>>, text: string, now: Date): string | null;
32
+ /** A question notice as chat parts (the buttons on the last), or null when it isn't one, isn't for this person, or was answered. */
33
+ export declare function questionParts(store: Store, notification: {
34
+ dedupeKey: string;
35
+ subject: string;
36
+ body: string;
37
+ link: string | null;
38
+ }, binding: ChatBinding): ChatContent[] | null;
39
+ /** The live buttons on one part, in the order they were minted. */
40
+ export declare function chatQuestionButtons(state: ChatState, part: number, now: Date): Array<{
41
+ token: string;
42
+ label: string;
43
+ words: boolean;
44
+ }>;
45
+ /** A tapped question button, inside the action's transaction; false when the token isn't one. */
46
+ export declare function applyChatQuestionTap(options: {
47
+ store: Store;
48
+ state: ChatState;
49
+ label: string;
50
+ }, event: ChatEvent, binding: ChatBinding, token: string, now: Date): boolean;
51
+ /** The person's message while an "answer in words" prompt is open: the answer. false when none is open. */
52
+ export declare function answerChatQuestionPrompt(options: {
53
+ store: Store;
54
+ state: ChatState;
55
+ label: string;
56
+ }, event: ChatEvent, binding: ChatBinding, input: {
57
+ text: string;
58
+ originalLength?: number;
59
+ }, now: Date): boolean;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * A teammate's question, answered in the chat app (v93).
3
+ *
4
+ * When a teammate asks its person something ("Refund all $200, or half?"),
5
+ * the notice reaches them in their chat app under the teammate's name, with
6
+ * one button per option and one to answer in their own words. A tap answers
7
+ * at once; "Answer in words" opens a prompt: on Telegram, a reply to it; in
8
+ * Slack, Discord and Teams, their next message in their chat with Standing
9
+ * Orders ("cancel" leaves it). Either way the answer goes through the same
10
+ * door as the card's own panel (answerTeammateQuestion): once, and only from
11
+ * the person it was asked of. Answered anywhere, every button retires.
12
+ */
13
+ import { randomBytes } from "node:crypto";
14
+ import { answerTeammateQuestion } from "./teammate-work.js";
15
+ import { labelOf } from "./teammate-admin.js";
16
+ /** The key notifyPeople gives a teammate's question: flow-card:<card>:teammate-q:<question>:<person>. */
17
+ export const TEAMMATE_Q_KEY = /^flow-card:[1-9][0-9]{0,14}:teammate-q:([1-9][0-9]{0,14}):/;
18
+ export const IN_WORDS = "Answer in words";
19
+ const PROMPT_MS = 30 * 60_000;
20
+ const ANSWER_CHARS = 2000;
21
+ /** The question a notice is about, while it is still open. */
22
+ export function openQuestionOf(store, dedupeKey) {
23
+ const match = TEAMMATE_Q_KEY.exec(dedupeKey);
24
+ const question = match === null ? null : store.teammateQuestion(Number(match[1]));
25
+ return question !== null && question.state === "open" ? question : null;
26
+ }
27
+ const who = (store, question) => { const mate = store.getTeammate(question.teammate); return mate === null ? "Your teammate" : labelOf(mate).split(" · ")[0]; };
28
+ /** Everyone else's buttons retire once a question is answered anywhere. */
29
+ function retireEverywhere(store, question, now, state) {
30
+ store.retireTelegramQuestion(question, now);
31
+ state?.prepare("UPDATE chat_question_action SET consumed=? WHERE question=? AND consumed IS NULL").run(now.toISOString(), question);
32
+ state?.prepare("UPDATE chat_question_prompt SET consumed=? WHERE question=? AND consumed IS NULL").run(now.toISOString(), question);
33
+ }
34
+ // ---- Telegram ----------------------------------------------------------------------
35
+ /** Buttons for the question, minted before the send (for the person it asks only); `place` stamps the message they land on. */
36
+ export function telegramQuestionButtons(store, binding, question, now) {
37
+ if (binding.approver !== question.askedOf)
38
+ return null;
39
+ const choices = [...question.options.map(one => ({ token: randomBytes(16).toString("hex"), choice: one.id, label: one.label })), { token: randomBytes(16).toString("hex"), choice: null, label: `✏️ ${IN_WORDS}` }];
40
+ store.createTelegramQuestionActions({ binding: binding.id, chatId: binding.chatId, question: question.id }, choices, now);
41
+ const rows = [];
42
+ const options = choices.filter(one => one.choice !== null);
43
+ for (let at = 0; at < options.length; at += 2)
44
+ rows.push(options.slice(at, at + 2).map(one => ({ text: one.label.slice(0, 60), callback_data: one.token })));
45
+ rows.push([{ text: choices.at(-1).label, callback_data: choices.at(-1).token }]);
46
+ return { keyboard: rows, tokens: choices.map(one => one.token) };
47
+ }
48
+ /** A tapped question button, inside the update's transaction. */
49
+ export function applyTelegramQuestionTap(store, binding, action, message, now) {
50
+ if (action.consumedAt !== null || action.expiresAt <= now.toISOString())
51
+ return [{ kind: "ack", text: "That was already answered, or these buttons are too old." }];
52
+ const question = store.teammateQuestion(action.question);
53
+ if (question === null || question.state !== "open") {
54
+ store.retireTelegramQuestion(action.question, now);
55
+ return [{ kind: "ack", text: "That was already answered." }];
56
+ }
57
+ if (action.choice === null)
58
+ return [{ kind: "ack", text: "Send your answer" }, { kind: "prompt", question: question.id, placeholder: "Your answer",
59
+ text: `Your answer to ${who(store, question)}: “${question.question}” Reply to this message.` }];
60
+ const answered = answerTeammateQuestion(store, question.id, { choice: action.choice, text: null, by: binding.approver, via: "telegram" }, now);
61
+ if (!answered.ok)
62
+ return [{ kind: "ack", text: answered.said.slice(0, 190) }];
63
+ retireEverywhere(store, question.id, now);
64
+ const label = question.options.find(one => one.id === action.choice)?.label ?? action.choice;
65
+ return [{ kind: "ack", text: "Answered" }, { kind: "edit", text: `${message.text}\n\n✅ You answered: ${label}. ${answered.said}`.slice(0, 4000) }];
66
+ }
67
+ /** A reply to the "answer in words" prompt, inside the update's transaction. */
68
+ export function applyTelegramQuestionReply(store, binding, prompt, text, now) {
69
+ if (prompt.binding !== binding.id)
70
+ return null;
71
+ const said = text.trim();
72
+ if (said === "")
73
+ return "Send the answer itself as a reply.";
74
+ if (said.length > ANSWER_CHARS)
75
+ return `Keep it under ${ANSWER_CHARS.toLocaleString("en-US")} characters.`;
76
+ const answered = answerTeammateQuestion(store, prompt.question, { choice: null, text: said, by: binding.approver, via: "telegram" }, now);
77
+ retireEverywhere(store, prompt.question, now);
78
+ return answered.ok ? `✅ ${answered.said}` : answered.said;
79
+ }
80
+ // ---- Slack, Discord, Teams ---------------------------------------------------------
81
+ /** A question notice as chat parts (the buttons on the last), or null when it isn't one, isn't for this person, or was answered. */
82
+ export function questionParts(store, notification, binding) {
83
+ const question = openQuestionOf(store, notification.dedupeKey);
84
+ if (question === null || question.askedOf !== binding.approver)
85
+ return null;
86
+ return [{ text: `${notification.subject}\n\n${notification.body}`.replace(/[\u0000-\u0008\u000b-\u001f\u007f]/g, "").slice(0, 3000),
87
+ question: { id: question.id, choices: [...question.options.map(one => ({ choice: one.id, label: one.label })), { choice: null, label: IN_WORDS }] },
88
+ ...(notification.link === null ? {} : { link: { label: "Open", path: notification.link } }) }];
89
+ }
90
+ /** The live buttons on one part, in the order they were minted. */
91
+ export function chatQuestionButtons(state, part, now) {
92
+ const row = state.prepare("SELECT payload FROM chat_part WHERE id=?").get(part);
93
+ const content = row === undefined ? null : JSON.parse(String(row["payload"]));
94
+ const labels = new Map((content?.question?.choices ?? []).map(one => [one.choice ?? "", one.label]));
95
+ return state.prepare("SELECT token,choice FROM chat_question_action WHERE part=? AND consumed IS NULL AND expires>? ORDER BY rowid").all(part, now.toISOString())
96
+ .map(one => ({ token: String(one.token), label: (labels.get(one.choice ?? "") ?? IN_WORDS).slice(0, 75), words: one.choice === null }));
97
+ }
98
+ /** Show the tapped notice again with the answer, and without its buttons. */
99
+ function repaint(state, part, event, line) {
100
+ const row = state.prepare("SELECT payload FROM chat_part WHERE id=?").get(part);
101
+ const before = row === undefined ? { text: "" } : JSON.parse(String(row["payload"]));
102
+ const content = { text: `${before.text}\n\n${line}`.slice(0, 3400), edit: event.ts, ...(before.link === undefined ? {} : { link: before.link }), ...(before.channel === undefined ? {} : { channel: before.channel }) };
103
+ state.prepare("UPDATE chat_part SET payload=?,state='pending',next_at=NULL WHERE id=?").run(JSON.stringify(content), part);
104
+ }
105
+ /** A tapped question button, inside the action's transaction; false when the token isn't one. */
106
+ export function applyChatQuestionTap(options, event, binding, token, now) {
107
+ const { store, state } = options;
108
+ const action = state.prepare("SELECT a.*,p.message,e.binding AS owner,e.channel FROM chat_question_action a JOIN chat_part p ON p.id=a.part JOIN chat_event e ON e.id=p.event WHERE a.token=?").get(token);
109
+ if (action === undefined)
110
+ return false;
111
+ const say = (text) => state.plan(event.id, [{ text }], now);
112
+ // Bound to the person, the chat and the exact message the button rode.
113
+ if (Number(action["owner"]) !== binding.id || action["channel"] !== event.channel || action["message"] !== event.ts) {
114
+ say("That button expired or was already used.");
115
+ return true;
116
+ }
117
+ const questionId = Number(action["question"]), part = Number(action["part"]);
118
+ const question = store.teammateQuestion(questionId);
119
+ if (action["consumed"] !== null || String(action["expires"]) <= now.toISOString() || question === null || question.state !== "open") {
120
+ retireEverywhere(store, questionId, now, state);
121
+ repaint(state, part, event, "This was already answered.");
122
+ state.finish(event.id);
123
+ return true;
124
+ }
125
+ if (action["choice"] === null) {
126
+ // One open prompt per person, across flow decisions and questions: the newest is the one the next message answers.
127
+ state.prepare("UPDATE chat_question_prompt SET consumed=? WHERE binding=? AND consumed IS NULL").run(now.toISOString(), binding.id);
128
+ state.prepare("UPDATE chat_flow_prompt SET consumed=? WHERE binding=? AND consumed IS NULL").run(now.toISOString(), binding.id);
129
+ state.prepare("INSERT INTO chat_question_prompt(binding,question,created,expires) VALUES(?,?,?,?)").run(binding.id, questionId, now.toISOString(), new Date(now.getTime() + PROMPT_MS).toISOString());
130
+ say(`Your next message here is your answer to ${who(store, question)}: “${question.question}” Send “cancel” to leave it.`);
131
+ return true;
132
+ }
133
+ const answered = answerTeammateQuestion(store, questionId, { choice: String(action["choice"]), text: null, by: binding.approver, via: options.label.toLowerCase() }, now);
134
+ if (!answered.ok) {
135
+ say(answered.said);
136
+ return true;
137
+ }
138
+ retireEverywhere(store, questionId, now, state);
139
+ const label = question.options.find(one => one.id === action["choice"])?.label ?? String(action["choice"]);
140
+ repaint(state, part, event, `✅ You answered: ${label}. ${answered.said}`);
141
+ state.finish(event.id);
142
+ return true;
143
+ }
144
+ /** The person's message while an "answer in words" prompt is open: the answer. false when none is open. */
145
+ export function answerChatQuestionPrompt(options, event, binding, input, now) {
146
+ const { store, state } = options;
147
+ if (event.channel !== binding.channel)
148
+ return false;
149
+ return store.transact(() => {
150
+ const prompt = state.prepare("SELECT * FROM chat_question_prompt WHERE binding=? AND consumed IS NULL AND expires>? ORDER BY id DESC LIMIT 1").get(binding.id, now.toISOString());
151
+ if (prompt === undefined)
152
+ return false;
153
+ const id = Number(prompt["id"]), questionId = Number(prompt["question"]);
154
+ const say = (text) => state.plan(event.id, [{ text }], now);
155
+ const said = input.text.trim();
156
+ if (/^cancel\.?$/i.test(said)) {
157
+ state.prepare("UPDATE chat_question_prompt SET consumed=? WHERE id=?").run(now.toISOString(), id);
158
+ say("Left it unanswered. The buttons on the message still work.");
159
+ return true;
160
+ }
161
+ if (said === "") {
162
+ say("Send the answer itself, or “cancel”.");
163
+ return true;
164
+ }
165
+ // A message the chat cut short is never taken as part of an answer.
166
+ if ((input.originalLength ?? 0) > input.text.length || said.length > ANSWER_CHARS) {
167
+ say(`That's too long to take from here. Keep it under ${ANSWER_CHARS.toLocaleString("en-US")} characters, or answer on the card.`);
168
+ return true;
169
+ }
170
+ const answered = answerTeammateQuestion(store, questionId, { choice: null, text: said, by: binding.approver, via: options.label.toLowerCase() }, now);
171
+ retireEverywhere(store, questionId, now, state);
172
+ say(answered.ok ? `✅ ${answered.said}` : answered.said);
173
+ return true;
174
+ });
175
+ }