tinybeeai 0.0.0-stage → 0.16.1

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 (1034) hide show
  1. package/.railway/railway.ts +83 -0
  2. package/.railway/tsconfig.json +13 -0
  3. package/AGENTS.md +102 -0
  4. package/Dockerfile +108 -0
  5. package/LICENSE +21 -0
  6. package/README.md +275 -2
  7. package/bin/localdost.js +29 -0
  8. package/bin/tinybeeai.js +28 -0
  9. package/compose.yml +63 -0
  10. package/demo/payloads/ci_failure.json +46 -0
  11. package/demo/payloads/outage.json +18 -0
  12. package/demo/payloads/pr_comment_edited.json +50 -0
  13. package/demo/payloads/pr_merged.json +80 -0
  14. package/demo/payloads/pr_opened.json +69 -0
  15. package/demo/payloads/pr_synchronize.json +69 -0
  16. package/demo/payloads/ticket_created.json +138 -0
  17. package/demo/sample-app/playwright.config.ts +20 -0
  18. package/demo/sample-app/server.ts +163 -0
  19. package/demo/sample-app/tests/checkout.spec.ts +13 -0
  20. package/demo/sample-app/tests/smoke.spec.ts +8 -0
  21. package/dist/agent/approval-retries.js +59 -0
  22. package/dist/agent/approval-retries.js.map +1 -0
  23. package/dist/agent/approvals.js +390 -0
  24. package/dist/agent/approvals.js.map +1 -0
  25. package/dist/agent/chat-bridge.js +154 -0
  26. package/dist/agent/chat-bridge.js.map +1 -0
  27. package/dist/agent/chat-lead.js +475 -0
  28. package/dist/agent/chat-lead.js.map +1 -0
  29. package/dist/agent/chat-loop.js +106 -0
  30. package/dist/agent/chat-loop.js.map +1 -0
  31. package/dist/agent/chat-mcp.js +49 -0
  32. package/dist/agent/chat-mcp.js.map +1 -0
  33. package/dist/agent/chat-tool-blocks.js +453 -0
  34. package/dist/agent/chat-tool-blocks.js.map +1 -0
  35. package/dist/agent/chat-view.js +94 -0
  36. package/dist/agent/chat-view.js.map +1 -0
  37. package/dist/agent/chat-views.js +781 -0
  38. package/dist/agent/chat-views.js.map +1 -0
  39. package/dist/agent/chat.js +1703 -0
  40. package/dist/agent/chat.js.map +1 -0
  41. package/dist/agent/destinations.js +150 -0
  42. package/dist/agent/destinations.js.map +1 -0
  43. package/dist/agent/llm-cli.js +451 -0
  44. package/dist/agent/llm-cli.js.map +1 -0
  45. package/dist/agent/llm-codex.js +493 -0
  46. package/dist/agent/llm-codex.js.map +1 -0
  47. package/dist/agent/llm-openai.js +211 -0
  48. package/dist/agent/llm-openai.js.map +1 -0
  49. package/dist/agent/llm.js +1464 -0
  50. package/dist/agent/llm.js.map +1 -0
  51. package/dist/agent/memory.js +33 -0
  52. package/dist/agent/memory.js.map +1 -0
  53. package/dist/agent/mock-views.js +93 -0
  54. package/dist/agent/mock-views.js.map +1 -0
  55. package/dist/agent/model-availability.js +207 -0
  56. package/dist/agent/model-availability.js.map +1 -0
  57. package/dist/agent/model-errors.js +188 -0
  58. package/dist/agent/model-errors.js.map +1 -0
  59. package/dist/agent/model-guard.js +311 -0
  60. package/dist/agent/model-guard.js.map +1 -0
  61. package/dist/agent/prompts.js +374 -0
  62. package/dist/agent/prompts.js.map +1 -0
  63. package/dist/agent/qa-lenses/qa-auth.md +32 -0
  64. package/dist/agent/qa-lenses/qa-check-then-act.md +33 -0
  65. package/dist/agent/qa-lenses/qa-control-has-no-effect.md +32 -0
  66. package/dist/agent/qa-lenses/qa-dates.md +31 -0
  67. package/dist/agent/qa-lenses/qa-double-submit.md +34 -0
  68. package/dist/agent/qa-lenses/qa-feature-flag.md +29 -0
  69. package/dist/agent/qa-lenses/qa-migration.md +35 -0
  70. package/dist/agent/qa-lenses/qa-money.md +38 -0
  71. package/dist/agent/qa-lenses/qa-response-shape-break.md +33 -0
  72. package/dist/agent/qa-lenses/qa-validation.md +29 -0
  73. package/dist/agent/qa-prompts.js +660 -0
  74. package/dist/agent/qa-prompts.js.map +1 -0
  75. package/dist/agent/reasoning.js +55 -0
  76. package/dist/agent/reasoning.js.map +1 -0
  77. package/dist/agent/redact.js +48 -0
  78. package/dist/agent/redact.js.map +1 -0
  79. package/dist/agent/rules.js +927 -0
  80. package/dist/agent/rules.js.map +1 -0
  81. package/dist/agent/runs.js +440 -0
  82. package/dist/agent/runs.js.map +1 -0
  83. package/dist/agent/send-retry.js +62 -0
  84. package/dist/agent/send-retry.js.map +1 -0
  85. package/dist/agent/team-notes.js +288 -0
  86. package/dist/agent/team-notes.js.map +1 -0
  87. package/dist/agent/token-meter.js +31 -0
  88. package/dist/agent/token-meter.js.map +1 -0
  89. package/dist/agent/triage.js +506 -0
  90. package/dist/agent/triage.js.map +1 -0
  91. package/dist/api.js +1047 -0
  92. package/dist/api.js.map +1 -0
  93. package/dist/approval-targets.js +107 -0
  94. package/dist/approval-targets.js.map +1 -0
  95. package/dist/bee/cloudflared.js +108 -0
  96. package/dist/bee/cloudflared.js.map +1 -0
  97. package/dist/bee/health.js +99 -0
  98. package/dist/bee/health.js.map +1 -0
  99. package/dist/bee/hive-client.js +75 -0
  100. package/dist/bee/hive-client.js.map +1 -0
  101. package/dist/bee/identity.js +39 -0
  102. package/dist/bee/identity.js.map +1 -0
  103. package/dist/bee/models.js +510 -0
  104. package/dist/bee/models.js.map +1 -0
  105. package/dist/bee/pairing.js +351 -0
  106. package/dist/bee/pairing.js.map +1 -0
  107. package/dist/bee/relay-client.js +50 -0
  108. package/dist/bee/relay-client.js.map +1 -0
  109. package/dist/bee/routes.js +214 -0
  110. package/dist/bee/routes.js.map +1 -0
  111. package/dist/bee/setup-page.js +223 -0
  112. package/dist/bee/setup-page.js.map +1 -0
  113. package/dist/bee/state.js +41 -0
  114. package/dist/bee/state.js.map +1 -0
  115. package/dist/bee/status.js +117 -0
  116. package/dist/bee/status.js.map +1 -0
  117. package/dist/bee/tunnel-manager.js +245 -0
  118. package/dist/bee/tunnel-manager.js.map +1 -0
  119. package/dist/bee/updater.js +268 -0
  120. package/dist/bee/updater.js.map +1 -0
  121. package/dist/boot.js +41 -0
  122. package/dist/boot.js.map +1 -0
  123. package/dist/call/index.js +145 -0
  124. package/dist/call/index.js.map +1 -0
  125. package/dist/case-folders.js +294 -0
  126. package/dist/case-folders.js.map +1 -0
  127. package/dist/cases.js +554 -0
  128. package/dist/cases.js.map +1 -0
  129. package/dist/chat-blocks.js +709 -0
  130. package/dist/chat-blocks.js.map +1 -0
  131. package/dist/chat-parse-util.js +38 -0
  132. package/dist/chat-parse-util.js.map +1 -0
  133. package/dist/chat-results.js +96 -0
  134. package/dist/chat-results.js.map +1 -0
  135. package/dist/chat-stats.js +193 -0
  136. package/dist/chat-stats.js.map +1 -0
  137. package/dist/chat-view-specs.js +186 -0
  138. package/dist/chat-view-specs.js.map +1 -0
  139. package/dist/cli/doctor-ops.js +361 -0
  140. package/dist/cli/doctor-ops.js.map +1 -0
  141. package/dist/cli/doctor.js +289 -0
  142. package/dist/cli/doctor.js.map +1 -0
  143. package/dist/cli/dogfood.js +70 -0
  144. package/dist/cli/dogfood.js.map +1 -0
  145. package/dist/cli/env-file.js +34 -0
  146. package/dist/cli/env-file.js.map +1 -0
  147. package/dist/cli/eval-chat-cases.js +359 -0
  148. package/dist/cli/eval-chat-cases.js.map +1 -0
  149. package/dist/cli/eval-chat-results.js +85 -0
  150. package/dist/cli/eval-chat-results.js.map +1 -0
  151. package/dist/cli/eval-chat.js +508 -0
  152. package/dist/cli/eval-chat.js.map +1 -0
  153. package/dist/cli/eval-designer.js +67 -0
  154. package/dist/cli/eval-designer.js.map +1 -0
  155. package/dist/cli/eval-qa-review.js +284 -0
  156. package/dist/cli/eval-qa-review.js.map +1 -0
  157. package/dist/cli/golden-export.js +35 -0
  158. package/dist/cli/golden-export.js.map +1 -0
  159. package/dist/cli/index.js +90 -0
  160. package/dist/cli/index.js.map +1 -0
  161. package/dist/cli/init.js +746 -0
  162. package/dist/cli/init.js.map +1 -0
  163. package/dist/cli/scan-repo.js +199 -0
  164. package/dist/cli/scan-repo.js.map +1 -0
  165. package/dist/cli/seed.js +108 -0
  166. package/dist/cli/seed.js.map +1 -0
  167. package/dist/cli/tinybee.js +247 -0
  168. package/dist/cli/tinybee.js.map +1 -0
  169. package/dist/config.js +138 -0
  170. package/dist/config.js.map +1 -0
  171. package/dist/connect/aivision-local.js +833 -0
  172. package/dist/connect/aivision-local.js.map +1 -0
  173. package/dist/connect/aivision-token.js +84 -0
  174. package/dist/connect/aivision-token.js.map +1 -0
  175. package/dist/connect/crypto.js +65 -0
  176. package/dist/connect/crypto.js.map +1 -0
  177. package/dist/connect/deps.js +110 -0
  178. package/dist/connect/deps.js.map +1 -0
  179. package/dist/connect/engines.js +144 -0
  180. package/dist/connect/engines.js.map +1 -0
  181. package/dist/connect/github-access.js +74 -0
  182. package/dist/connect/github-access.js.map +1 -0
  183. package/dist/connect/github-app.js +129 -0
  184. package/dist/connect/github-app.js.map +1 -0
  185. package/dist/connect/index.js +9 -0
  186. package/dist/connect/index.js.map +1 -0
  187. package/dist/connect/model-choices.js +201 -0
  188. package/dist/connect/model-choices.js.map +1 -0
  189. package/dist/connect/providers.js +900 -0
  190. package/dist/connect/providers.js.map +1 -0
  191. package/dist/connect/readiness.js +232 -0
  192. package/dist/connect/readiness.js.map +1 -0
  193. package/dist/connect/redact.js +16 -0
  194. package/dist/connect/redact.js.map +1 -0
  195. package/dist/connect/registry.js +406 -0
  196. package/dist/connect/registry.js.map +1 -0
  197. package/dist/connect/store.js +283 -0
  198. package/dist/connect/store.js.map +1 -0
  199. package/dist/connect/tunnel.js +134 -0
  200. package/dist/connect/tunnel.js.map +1 -0
  201. package/dist/connect/types.js +8 -0
  202. package/dist/connect/types.js.map +1 -0
  203. package/dist/connections/index.js +5 -0
  204. package/dist/connections/index.js.map +1 -0
  205. package/dist/connections/manager.js +223 -0
  206. package/dist/connections/manager.js.map +1 -0
  207. package/dist/connections/presets.js +264 -0
  208. package/dist/connections/presets.js.map +1 -0
  209. package/dist/connections/store.js +132 -0
  210. package/dist/connections/store.js.map +1 -0
  211. package/dist/connections/tools.js +206 -0
  212. package/dist/connections/tools.js.map +1 -0
  213. package/dist/db-schema.js +355 -0
  214. package/dist/db-schema.js.map +1 -0
  215. package/dist/db.js +379 -0
  216. package/dist/db.js.map +1 -0
  217. package/dist/delivery.js +49 -0
  218. package/dist/delivery.js.map +1 -0
  219. package/dist/deploy-wait.js +701 -0
  220. package/dist/deploy-wait.js.map +1 -0
  221. package/dist/disk.js +243 -0
  222. package/dist/disk.js.map +1 -0
  223. package/dist/dot-health.js +966 -0
  224. package/dist/dot-health.js.map +1 -0
  225. package/dist/dot-remove.js +246 -0
  226. package/dist/dot-remove.js.map +1 -0
  227. package/dist/events.js +38 -0
  228. package/dist/events.js.map +1 -0
  229. package/dist/evidence-pack.js +423 -0
  230. package/dist/evidence-pack.js.map +1 -0
  231. package/dist/flows/answers.js +289 -0
  232. package/dist/flows/answers.js.map +1 -0
  233. package/dist/flows/change.js +811 -0
  234. package/dist/flows/change.js.map +1 -0
  235. package/dist/flows/checkStatus.js +524 -0
  236. package/dist/flows/checkStatus.js.map +1 -0
  237. package/dist/flows/ci.js +236 -0
  238. package/dist/flows/ci.js.map +1 -0
  239. package/dist/flows/ciFailure.js +76 -0
  240. package/dist/flows/ciFailure.js.map +1 -0
  241. package/dist/flows/common.js +522 -0
  242. package/dist/flows/common.js.map +1 -0
  243. package/dist/flows/default-branch.js +29 -0
  244. package/dist/flows/default-branch.js.map +1 -0
  245. package/dist/flows/defectDraft.js +447 -0
  246. package/dist/flows/defectDraft.js.map +1 -0
  247. package/dist/flows/defects.js +623 -0
  248. package/dist/flows/defects.js.map +1 -0
  249. package/dist/flows/digest.js +295 -0
  250. package/dist/flows/digest.js.map +1 -0
  251. package/dist/flows/impact.js +82 -0
  252. package/dist/flows/impact.js.map +1 -0
  253. package/dist/flows/index.js +23 -0
  254. package/dist/flows/index.js.map +1 -0
  255. package/dist/flows/intent-sources.js +72 -0
  256. package/dist/flows/intent-sources.js.map +1 -0
  257. package/dist/flows/mergeCases.js +169 -0
  258. package/dist/flows/mergeCases.js.map +1 -0
  259. package/dist/flows/nightlyRun.js +82 -0
  260. package/dist/flows/nightlyRun.js.map +1 -0
  261. package/dist/flows/outage.js +64 -0
  262. package/dist/flows/outage.js.map +1 -0
  263. package/dist/flows/postMerge.js +5 -0
  264. package/dist/flows/postMerge.js.map +1 -0
  265. package/dist/flows/prCheck.js +5 -0
  266. package/dist/flows/prCheck.js.map +1 -0
  267. package/dist/flows/prTarget.js +66 -0
  268. package/dist/flows/prTarget.js.map +1 -0
  269. package/dist/flows/qa.js +363 -0
  270. package/dist/flows/qa.js.map +1 -0
  271. package/dist/flows/questions.js +160 -0
  272. package/dist/flows/questions.js.map +1 -0
  273. package/dist/flows/recovery.js +64 -0
  274. package/dist/flows/recovery.js.map +1 -0
  275. package/dist/flows/rerun.js +50 -0
  276. package/dist/flows/rerun.js.map +1 -0
  277. package/dist/flows/testDesign.js +294 -0
  278. package/dist/flows/testDesign.js.map +1 -0
  279. package/dist/flows/testDesignJob.js +46 -0
  280. package/dist/flows/testDesignJob.js.map +1 -0
  281. package/dist/flows/testRepo.js +228 -0
  282. package/dist/flows/testRepo.js.map +1 -0
  283. package/dist/flows/testSavings.js +381 -0
  284. package/dist/flows/testSavings.js.map +1 -0
  285. package/dist/flows/testStatus.js +343 -0
  286. package/dist/flows/testStatus.js.map +1 -0
  287. package/dist/flows/ticketContext.js +425 -0
  288. package/dist/flows/ticketContext.js.map +1 -0
  289. package/dist/flows/ticketReview.js +249 -0
  290. package/dist/flows/ticketReview.js.map +1 -0
  291. package/dist/flows/ticks.js +73 -0
  292. package/dist/flows/ticks.js.map +1 -0
  293. package/dist/hive/admin.js +122 -0
  294. package/dist/hive/admin.js.map +1 -0
  295. package/dist/hive/auth.js +130 -0
  296. package/dist/hive/auth.js.map +1 -0
  297. package/dist/hive/billing.js +278 -0
  298. package/dist/hive/billing.js.map +1 -0
  299. package/dist/hive/config.js +63 -0
  300. package/dist/hive/config.js.map +1 -0
  301. package/dist/hive/db.js +186 -0
  302. package/dist/hive/db.js.map +1 -0
  303. package/dist/hive/embed.js +76 -0
  304. package/dist/hive/embed.js.map +1 -0
  305. package/dist/hive/errors.js +17 -0
  306. package/dist/hive/errors.js.map +1 -0
  307. package/dist/hive/google.js +75 -0
  308. package/dist/hive/google.js.map +1 -0
  309. package/dist/hive/index.js +25 -0
  310. package/dist/hive/index.js.map +1 -0
  311. package/dist/hive/jwt.js +57 -0
  312. package/dist/hive/jwt.js.map +1 -0
  313. package/dist/hive/keys.js +61 -0
  314. package/dist/hive/keys.js.map +1 -0
  315. package/dist/hive/mail.js +27 -0
  316. package/dist/hive/mail.js.map +1 -0
  317. package/dist/hive/native-push.js +255 -0
  318. package/dist/hive/native-push.js.map +1 -0
  319. package/dist/hive/pairing.js +342 -0
  320. package/dist/hive/pairing.js.map +1 -0
  321. package/dist/hive/presence.js +121 -0
  322. package/dist/hive/presence.js.map +1 -0
  323. package/dist/hive/push.js +185 -0
  324. package/dist/hive/push.js.map +1 -0
  325. package/dist/hive/relay.js +205 -0
  326. package/dist/hive/relay.js.map +1 -0
  327. package/dist/hive/routes.js +228 -0
  328. package/dist/hive/routes.js.map +1 -0
  329. package/dist/hive/server.js +12 -0
  330. package/dist/hive/server.js.map +1 -0
  331. package/dist/hive/session.js +28 -0
  332. package/dist/hive/session.js.map +1 -0
  333. package/dist/hive/tenants.js +287 -0
  334. package/dist/hive/tenants.js.map +1 -0
  335. package/dist/hive/tunnel/cloudflare.js +160 -0
  336. package/dist/hive/tunnel/cloudflare.js.map +1 -0
  337. package/dist/hive/tunnel/mock.js +52 -0
  338. package/dist/hive/tunnel/mock.js.map +1 -0
  339. package/dist/hive/tunnel/provider.js +19 -0
  340. package/dist/hive/tunnel/provider.js.map +1 -0
  341. package/dist/hive/updates.js +61 -0
  342. package/dist/hive/updates.js.map +1 -0
  343. package/dist/http-error.js +11 -0
  344. package/dist/http-error.js.map +1 -0
  345. package/dist/impact/classify.js +226 -0
  346. package/dist/impact/classify.js.map +1 -0
  347. package/dist/impact/find.js +259 -0
  348. package/dist/impact/find.js.map +1 -0
  349. package/dist/impact/pr.js +54 -0
  350. package/dist/impact/pr.js.map +1 -0
  351. package/dist/impact/store.js +365 -0
  352. package/dist/impact/store.js.map +1 -0
  353. package/dist/impact/text.js +158 -0
  354. package/dist/impact/text.js.map +1 -0
  355. package/dist/index.js +288 -0
  356. package/dist/index.js.map +1 -0
  357. package/dist/integrations/adf.js +397 -0
  358. package/dist/integrations/adf.js.map +1 -0
  359. package/dist/integrations/bot-marker.js +103 -0
  360. package/dist/integrations/bot-marker.js.map +1 -0
  361. package/dist/integrations/github-issues.js +705 -0
  362. package/dist/integrations/github-issues.js.map +1 -0
  363. package/dist/integrations/github.js +1284 -0
  364. package/dist/integrations/github.js.map +1 -0
  365. package/dist/integrations/issue-create.js +60 -0
  366. package/dist/integrations/issue-create.js.map +1 -0
  367. package/dist/integrations/jira.js +279 -0
  368. package/dist/integrations/jira.js.map +1 -0
  369. package/dist/integrations/linear.js +273 -0
  370. package/dist/integrations/linear.js.map +1 -0
  371. package/dist/integrations/own-identity-warm.js +47 -0
  372. package/dist/integrations/own-identity-warm.js.map +1 -0
  373. package/dist/integrations/own-identity.js +39 -0
  374. package/dist/integrations/own-identity.js.map +1 -0
  375. package/dist/integrations/status-change.js +42 -0
  376. package/dist/integrations/status-change.js.map +1 -0
  377. package/dist/lead-defects.js +202 -0
  378. package/dist/lead-defects.js.map +1 -0
  379. package/dist/lead-digest.js +333 -0
  380. package/dist/lead-digest.js.map +1 -0
  381. package/dist/lead-release.js +286 -0
  382. package/dist/lead-release.js.map +1 -0
  383. package/dist/lead.js +328 -0
  384. package/dist/lead.js.map +1 -0
  385. package/dist/legacy-names.js +51 -0
  386. package/dist/legacy-names.js.map +1 -0
  387. package/dist/limits.js +44 -0
  388. package/dist/limits.js.map +1 -0
  389. package/dist/link-trust.js +224 -0
  390. package/dist/link-trust.js.map +1 -0
  391. package/dist/links.js +219 -0
  392. package/dist/links.js.map +1 -0
  393. package/dist/loopback.js +35 -0
  394. package/dist/loopback.js.map +1 -0
  395. package/dist/model-cap.js +60 -0
  396. package/dist/model-cap.js.map +1 -0
  397. package/dist/model-usage.js +173 -0
  398. package/dist/model-usage.js.map +1 -0
  399. package/dist/monitor/backfill.js +39 -0
  400. package/dist/monitor/backfill.js.map +1 -0
  401. package/dist/monitor/browser-probe.js +198 -0
  402. package/dist/monitor/browser-probe.js.map +1 -0
  403. package/dist/monitor/catch-up.js +31 -0
  404. package/dist/monitor/catch-up.js.map +1 -0
  405. package/dist/monitor/pollers.js +1606 -0
  406. package/dist/monitor/pollers.js.map +1 -0
  407. package/dist/monitor/probes.js +178 -0
  408. package/dist/monitor/probes.js.map +1 -0
  409. package/dist/monitor/self-health.js +228 -0
  410. package/dist/monitor/self-health.js.map +1 -0
  411. package/dist/monitor/statuspage.js +105 -0
  412. package/dist/monitor/statuspage.js.map +1 -0
  413. package/dist/nightly-scheduler.js +127 -0
  414. package/dist/nightly-scheduler.js.map +1 -0
  415. package/dist/nightly.js +141 -0
  416. package/dist/nightly.js.map +1 -0
  417. package/dist/notify/headline.js +114 -0
  418. package/dist/notify/headline.js.map +1 -0
  419. package/dist/notify/push.js +174 -0
  420. package/dist/notify/push.js.map +1 -0
  421. package/dist/object-cards.js +585 -0
  422. package/dist/object-cards.js.map +1 -0
  423. package/dist/object-links.js +47 -0
  424. package/dist/object-links.js.map +1 -0
  425. package/dist/onboarding-checks.js +141 -0
  426. package/dist/onboarding-checks.js.map +1 -0
  427. package/dist/onboarding.js +401 -0
  428. package/dist/onboarding.js.map +1 -0
  429. package/dist/path-filters.js +62 -0
  430. package/dist/path-filters.js.map +1 -0
  431. package/dist/phone/alerts.js +67 -0
  432. package/dist/phone/alerts.js.map +1 -0
  433. package/dist/phone/auth.js +240 -0
  434. package/dist/phone/auth.js.map +1 -0
  435. package/dist/phone/pairing.js +442 -0
  436. package/dist/phone/pairing.js.map +1 -0
  437. package/dist/phone/prefs.js +27 -0
  438. package/dist/phone/prefs.js.map +1 -0
  439. package/dist/phone/tailscale.js +161 -0
  440. package/dist/phone/tailscale.js.map +1 -0
  441. package/dist/pr-scope.js +441 -0
  442. package/dist/pr-scope.js.map +1 -0
  443. package/dist/proc.js +156 -0
  444. package/dist/proc.js.map +1 -0
  445. package/dist/profile/cache.js +108 -0
  446. package/dist/profile/cache.js.map +1 -0
  447. package/dist/profile/ci.js +216 -0
  448. package/dist/profile/ci.js.map +1 -0
  449. package/dist/profile/index.js +8 -0
  450. package/dist/profile/index.js.map +1 -0
  451. package/dist/profile/job.js +135 -0
  452. package/dist/profile/job.js.map +1 -0
  453. package/dist/profile/queries/go-tags.scm +47 -0
  454. package/dist/profile/queries/java-tags.scm +25 -0
  455. package/dist/profile/queries/javascript-tags.scm +104 -0
  456. package/dist/profile/queries/python-tags.scm +19 -0
  457. package/dist/profile/queries/typescript-tags.scm +28 -0
  458. package/dist/profile/scan-thread.js +42 -0
  459. package/dist/profile/scan-thread.js.map +1 -0
  460. package/dist/profile/scan-worker.js +16 -0
  461. package/dist/profile/scan-worker.js.map +1 -0
  462. package/dist/profile/scan.js +351 -0
  463. package/dist/profile/scan.js.map +1 -0
  464. package/dist/profile/stack.js +528 -0
  465. package/dist/profile/stack.js.map +1 -0
  466. package/dist/profile/store.js +35 -0
  467. package/dist/profile/store.js.map +1 -0
  468. package/dist/profile/tests.js +399 -0
  469. package/dist/profile/tests.js.map +1 -0
  470. package/dist/queue.js +350 -0
  471. package/dist/queue.js.map +1 -0
  472. package/dist/refs.js +756 -0
  473. package/dist/refs.js.map +1 -0
  474. package/dist/release-notes.js +272 -0
  475. package/dist/release-notes.js.map +1 -0
  476. package/dist/reports/build.js +243 -0
  477. package/dist/reports/build.js.map +1 -0
  478. package/dist/reports/index.js +211 -0
  479. package/dist/reports/index.js.map +1 -0
  480. package/dist/reports/store.js +150 -0
  481. package/dist/reports/store.js.map +1 -0
  482. package/dist/reports/ticket-titles.js +124 -0
  483. package/dist/reports/ticket-titles.js.map +1 -0
  484. package/dist/reports/trace-map.js +1278 -0
  485. package/dist/reports/trace-map.js.map +1 -0
  486. package/dist/reports/traceability.js +422 -0
  487. package/dist/reports/traceability.js.map +1 -0
  488. package/dist/repos/mirror.js +356 -0
  489. package/dist/repos/mirror.js.map +1 -0
  490. package/dist/repos-github.js +108 -0
  491. package/dist/repos-github.js.map +1 -0
  492. package/dist/repos.js +891 -0
  493. package/dist/repos.js.map +1 -0
  494. package/dist/review/backend-anthropic.js +90 -0
  495. package/dist/review/backend-anthropic.js.map +1 -0
  496. package/dist/review/backend-cli.js +248 -0
  497. package/dist/review/backend-cli.js.map +1 -0
  498. package/dist/review/backend-codex.js +81 -0
  499. package/dist/review/backend-codex.js.map +1 -0
  500. package/dist/review/backend-mock.js +686 -0
  501. package/dist/review/backend-mock.js.map +1 -0
  502. package/dist/review/backend-only.js +168 -0
  503. package/dist/review/backend-only.js.map +1 -0
  504. package/dist/review/backend.js +105 -0
  505. package/dist/review/backend.js.map +1 -0
  506. package/dist/review/baseline/render-v1.js +483 -0
  507. package/dist/review/baseline/render-v1.js.map +1 -0
  508. package/dist/review/change-summary.js +197 -0
  509. package/dist/review/change-summary.js.map +1 -0
  510. package/dist/review/changelog.js +40 -0
  511. package/dist/review/changelog.js.map +1 -0
  512. package/dist/review/check.js +494 -0
  513. package/dist/review/check.js.map +1 -0
  514. package/dist/review/checkout.js +185 -0
  515. package/dist/review/checkout.js.map +1 -0
  516. package/dist/review/classify.js +178 -0
  517. package/dist/review/classify.js.map +1 -0
  518. package/dist/review/coverage.js +347 -0
  519. package/dist/review/coverage.js.map +1 -0
  520. package/dist/review/decision-eval.js +121 -0
  521. package/dist/review/decision-eval.js.map +1 -0
  522. package/dist/review/deliver.js +104 -0
  523. package/dist/review/deliver.js.map +1 -0
  524. package/dist/review/designer-eval.js +280 -0
  525. package/dist/review/designer-eval.js.map +1 -0
  526. package/dist/review/dogfood-digest.js +136 -0
  527. package/dist/review/dogfood-digest.js.map +1 -0
  528. package/dist/review/dogfood-eval.js +486 -0
  529. package/dist/review/dogfood-eval.js.map +1 -0
  530. package/dist/review/environment.js +109 -0
  531. package/dist/review/environment.js.map +1 -0
  532. package/dist/review/eval.js +405 -0
  533. package/dist/review/eval.js.map +1 -0
  534. package/dist/review/glob.js +110 -0
  535. package/dist/review/glob.js.map +1 -0
  536. package/dist/review/golden-export.js +219 -0
  537. package/dist/review/golden-export.js.map +1 -0
  538. package/dist/review/impact.js +406 -0
  539. package/dist/review/impact.js.map +1 -0
  540. package/dist/review/imports.js +542 -0
  541. package/dist/review/imports.js.map +1 -0
  542. package/dist/review/index.js +512 -0
  543. package/dist/review/index.js.map +1 -0
  544. package/dist/review/intent.js +128 -0
  545. package/dist/review/intent.js.map +1 -0
  546. package/dist/review/lenses.js +167 -0
  547. package/dist/review/lenses.js.map +1 -0
  548. package/dist/review/linked-tickets.js +293 -0
  549. package/dist/review/linked-tickets.js.map +1 -0
  550. package/dist/review/merge.js +370 -0
  551. package/dist/review/merge.js.map +1 -0
  552. package/dist/review/on-demand.js +218 -0
  553. package/dist/review/on-demand.js.map +1 -0
  554. package/dist/review/prompt-eval.js +299 -0
  555. package/dist/review/prompt-eval.js.map +1 -0
  556. package/dist/review/prompt-variants.js +37 -0
  557. package/dist/review/prompt-variants.js.map +1 -0
  558. package/dist/review/readability-eval.js +217 -0
  559. package/dist/review/readability-eval.js.map +1 -0
  560. package/dist/review/readability.js +295 -0
  561. package/dist/review/readability.js.map +1 -0
  562. package/dist/review/render.js +2409 -0
  563. package/dist/review/render.js.map +1 -0
  564. package/dist/review/repo.js +194 -0
  565. package/dist/review/repo.js.map +1 -0
  566. package/dist/review/report-plan.js +90 -0
  567. package/dist/review/report-plan.js.map +1 -0
  568. package/dist/review/routes.js +195 -0
  569. package/dist/review/routes.js.map +1 -0
  570. package/dist/review/run-summary.js +34 -0
  571. package/dist/review/run-summary.js.map +1 -0
  572. package/dist/review/run.js +245 -0
  573. package/dist/review/run.js.map +1 -0
  574. package/dist/review/scope.js +454 -0
  575. package/dist/review/scope.js.map +1 -0
  576. package/dist/review/security.js +432 -0
  577. package/dist/review/security.js.map +1 -0
  578. package/dist/review/signals.js +550 -0
  579. package/dist/review/signals.js.map +1 -0
  580. package/dist/review/specs.js +150 -0
  581. package/dist/review/specs.js.map +1 -0
  582. package/dist/review/ste.js +133 -0
  583. package/dist/review/ste.js.map +1 -0
  584. package/dist/review/test-repo-file.js +18 -0
  585. package/dist/review/test-repo-file.js.map +1 -0
  586. package/dist/review/test-repo-lines.js +53 -0
  587. package/dist/review/test-repo-lines.js.map +1 -0
  588. package/dist/review/ticket-on-demand.js +232 -0
  589. package/dist/review/ticket-on-demand.js.map +1 -0
  590. package/dist/review/yaml.js +298 -0
  591. package/dist/review/yaml.js.map +1 -0
  592. package/dist/room.js +361 -0
  593. package/dist/room.js.map +1 -0
  594. package/dist/routes/activity.js +219 -0
  595. package/dist/routes/activity.js.map +1 -0
  596. package/dist/routes/call.js +29 -0
  597. package/dist/routes/call.js.map +1 -0
  598. package/dist/routes/cards.js +40 -0
  599. package/dist/routes/cards.js.map +1 -0
  600. package/dist/routes/cases.js +133 -0
  601. package/dist/routes/cases.js.map +1 -0
  602. package/dist/routes/connect.js +268 -0
  603. package/dist/routes/connect.js.map +1 -0
  604. package/dist/routes/connection-presets.js +32 -0
  605. package/dist/routes/connection-presets.js.map +1 -0
  606. package/dist/routes/connections.js +177 -0
  607. package/dist/routes/connections.js.map +1 -0
  608. package/dist/routes/disk.js +19 -0
  609. package/dist/routes/disk.js.map +1 -0
  610. package/dist/routes/docs.js +71 -0
  611. package/dist/routes/docs.js.map +1 -0
  612. package/dist/routes/dot-suggest.js +1002 -0
  613. package/dist/routes/dot-suggest.js.map +1 -0
  614. package/dist/routes/evidence-pack.js +16 -0
  615. package/dist/routes/evidence-pack.js.map +1 -0
  616. package/dist/routes/health.js +121 -0
  617. package/dist/routes/health.js.map +1 -0
  618. package/dist/routes/impact.js +48 -0
  619. package/dist/routes/impact.js.map +1 -0
  620. package/dist/routes/lead.js +50 -0
  621. package/dist/routes/lead.js.map +1 -0
  622. package/dist/routes/links.js +13 -0
  623. package/dist/routes/links.js.map +1 -0
  624. package/dist/routes/markdown.js +62 -0
  625. package/dist/routes/markdown.js.map +1 -0
  626. package/dist/routes/onboarding.js +47 -0
  627. package/dist/routes/onboarding.js.map +1 -0
  628. package/dist/routes/phone.js +231 -0
  629. package/dist/routes/phone.js.map +1 -0
  630. package/dist/routes/release-notes.js +30 -0
  631. package/dist/routes/release-notes.js.map +1 -0
  632. package/dist/routes/reports.js +102 -0
  633. package/dist/routes/reports.js.map +1 -0
  634. package/dist/routes/repos.js +91 -0
  635. package/dist/routes/repos.js.map +1 -0
  636. package/dist/routes/review.js +283 -0
  637. package/dist/routes/review.js.map +1 -0
  638. package/dist/routes/roles.js +114 -0
  639. package/dist/routes/roles.js.map +1 -0
  640. package/dist/routes/room.js +21 -0
  641. package/dist/routes/room.js.map +1 -0
  642. package/dist/routes/runner.js +156 -0
  643. package/dist/routes/runner.js.map +1 -0
  644. package/dist/routes/runs.js +84 -0
  645. package/dist/routes/runs.js.map +1 -0
  646. package/dist/routes/setup.js +18 -0
  647. package/dist/routes/setup.js.map +1 -0
  648. package/dist/routes/test-runs.js +220 -0
  649. package/dist/routes/test-runs.js.map +1 -0
  650. package/dist/routes/ticket-status.js +17 -0
  651. package/dist/routes/ticket-status.js.map +1 -0
  652. package/dist/routes/traceability.js +143 -0
  653. package/dist/routes/traceability.js.map +1 -0
  654. package/dist/routes/true.js +142 -0
  655. package/dist/routes/true.js.map +1 -0
  656. package/dist/routes/updates.js +70 -0
  657. package/dist/routes/updates.js.map +1 -0
  658. package/dist/runner/adapters/index.js +12 -0
  659. package/dist/runner/adapters/index.js.map +1 -0
  660. package/dist/runner/adapters/playwright.js +68 -0
  661. package/dist/runner/adapters/playwright.js.map +1 -0
  662. package/dist/runner/aivision/cases.js +172 -0
  663. package/dist/runner/aivision/cases.js.map +1 -0
  664. package/dist/runner/aivision/cli.js +201 -0
  665. package/dist/runner/aivision/cli.js.map +1 -0
  666. package/dist/runner/aivision/index.js +281 -0
  667. package/dist/runner/aivision/index.js.map +1 -0
  668. package/dist/runner/aivision/server.js +394 -0
  669. package/dist/runner/aivision/server.js.map +1 -0
  670. package/dist/runner/aivision/shared.js +140 -0
  671. package/dist/runner/aivision/shared.js.map +1 -0
  672. package/dist/runner/artifact-cap.js +189 -0
  673. package/dist/runner/artifact-cap.js.map +1 -0
  674. package/dist/runner/artifacts.js +433 -0
  675. package/dist/runner/artifacts.js.map +1 -0
  676. package/dist/runner/command.js +564 -0
  677. package/dist/runner/command.js.map +1 -0
  678. package/dist/runner/copyfiles.js +221 -0
  679. package/dist/runner/copyfiles.js.map +1 -0
  680. package/dist/runner/engines.js +32 -0
  681. package/dist/runner/engines.js.map +1 -0
  682. package/dist/runner/evidence.js +114 -0
  683. package/dist/runner/evidence.js.map +1 -0
  684. package/dist/runner/host-env.js +160 -0
  685. package/dist/runner/host-env.js.map +1 -0
  686. package/dist/runner/index.js +1221 -0
  687. package/dist/runner/index.js.map +1 -0
  688. package/dist/runner/observe.js +603 -0
  689. package/dist/runner/observe.js.map +1 -0
  690. package/dist/runner/playwright-json.js +108 -0
  691. package/dist/runner/playwright-json.js.map +1 -0
  692. package/dist/runner/pwgen/exec.js +192 -0
  693. package/dist/runner/pwgen/exec.js.map +1 -0
  694. package/dist/runner/pwgen/explore.js +111 -0
  695. package/dist/runner/pwgen/explore.js.map +1 -0
  696. package/dist/runner/pwgen/index.js +423 -0
  697. package/dist/runner/pwgen/index.js.map +1 -0
  698. package/dist/runner/pwgen/propose.js +69 -0
  699. package/dist/runner/pwgen/propose.js.map +1 -0
  700. package/dist/runner/pwgen/source.js +632 -0
  701. package/dist/runner/pwgen/source.js.map +1 -0
  702. package/dist/runner/setup.js +222 -0
  703. package/dist/runner/setup.js.map +1 -0
  704. package/dist/runner/step-reporter.js +107 -0
  705. package/dist/runner/step-reporter.js.map +1 -0
  706. package/dist/runner/test-repo.js +326 -0
  707. package/dist/runner/test-repo.js.map +1 -0
  708. package/dist/runner/typed-value.js +46 -0
  709. package/dist/runner/typed-value.js.map +1 -0
  710. package/dist/runner/verify.js +133 -0
  711. package/dist/runner/verify.js.map +1 -0
  712. package/dist/runner/worktree.js +349 -0
  713. package/dist/runner/worktree.js.map +1 -0
  714. package/dist/sandbox.js +61 -0
  715. package/dist/sandbox.js.map +1 -0
  716. package/dist/server.js +262 -0
  717. package/dist/server.js.map +1 -0
  718. package/dist/shadow-week.js +126 -0
  719. package/dist/shadow-week.js.map +1 -0
  720. package/dist/shutdown.js +92 -0
  721. package/dist/shutdown.js.map +1 -0
  722. package/dist/slack/index.js +645 -0
  723. package/dist/slack/index.js.map +1 -0
  724. package/dist/stages/config.js +147 -0
  725. package/dist/stages/config.js.map +1 -0
  726. package/dist/stages/index.js +41 -0
  727. package/dist/stages/index.js.map +1 -0
  728. package/dist/suites.js +293 -0
  729. package/dist/suites.js.map +1 -0
  730. package/dist/target-url.js +116 -0
  731. package/dist/target-url.js.map +1 -0
  732. package/dist/test-runs.js +662 -0
  733. package/dist/test-runs.js.map +1 -0
  734. package/dist/ticket-keys.js +97 -0
  735. package/dist/ticket-keys.js.map +1 -0
  736. package/dist/ticket-supersede.js +105 -0
  737. package/dist/ticket-supersede.js.map +1 -0
  738. package/dist/tinybee/ed25519.js +99 -0
  739. package/dist/tinybee/ed25519.js.map +1 -0
  740. package/dist/tinybee/models-routes.js +19 -0
  741. package/dist/tinybee/models-routes.js.map +1 -0
  742. package/dist/tinybee/pair-payload.js +41 -0
  743. package/dist/tinybee/pair-payload.js.map +1 -0
  744. package/dist/tinybee/update-manifest.js +77 -0
  745. package/dist/tinybee/update-manifest.js.map +1 -0
  746. package/dist/tinybee/well-known.js +64 -0
  747. package/dist/tinybee/well-known.js.map +1 -0
  748. package/dist/tinybee/workflows/activity.js +23 -0
  749. package/dist/tinybee/workflows/activity.js.map +1 -0
  750. package/dist/tinybee/workflows/cron.js +210 -0
  751. package/dist/tinybee/workflows/cron.js.map +1 -0
  752. package/dist/tinybee/workflows/engine.js +371 -0
  753. package/dist/tinybee/workflows/engine.js.map +1 -0
  754. package/dist/tinybee/workflows/from-text.js +203 -0
  755. package/dist/tinybee/workflows/from-text.js.map +1 -0
  756. package/dist/tinybee/workflows/routes.js +189 -0
  757. package/dist/tinybee/workflows/routes.js.map +1 -0
  758. package/dist/tinybee/workflows/scheduler.js +100 -0
  759. package/dist/tinybee/workflows/scheduler.js.map +1 -0
  760. package/dist/tinybee/workflows/skills.js +227 -0
  761. package/dist/tinybee/workflows/skills.js.map +1 -0
  762. package/dist/tinybee/workflows/store.js +205 -0
  763. package/dist/tinybee/workflows/store.js.map +1 -0
  764. package/dist/tinybee/workflows/types.js +180 -0
  765. package/dist/tinybee/workflows/types.js.map +1 -0
  766. package/dist/tinybee/workflows/yaml.js +323 -0
  767. package/dist/tinybee/workflows/yaml.js.map +1 -0
  768. package/dist/true/api.js +495 -0
  769. package/dist/true/api.js.map +1 -0
  770. package/dist/true/client.js +292 -0
  771. package/dist/true/client.js.map +1 -0
  772. package/dist/true/clock.js +36 -0
  773. package/dist/true/clock.js.map +1 -0
  774. package/dist/true/events.js +125 -0
  775. package/dist/true/events.js.map +1 -0
  776. package/dist/true/hosts.js +98 -0
  777. package/dist/true/hosts.js.map +1 -0
  778. package/dist/true/index.js +29 -0
  779. package/dist/true/index.js.map +1 -0
  780. package/dist/true/key.js +54 -0
  781. package/dist/true/key.js.map +1 -0
  782. package/dist/true/line.js +69 -0
  783. package/dist/true/line.js.map +1 -0
  784. package/dist/true/mask.js +31 -0
  785. package/dist/true/mask.js.map +1 -0
  786. package/dist/true/names.js +114 -0
  787. package/dist/true/names.js.map +1 -0
  788. package/dist/true/outbox.js +233 -0
  789. package/dist/true/outbox.js.map +1 -0
  790. package/dist/true/replay.js +164 -0
  791. package/dist/true/replay.js.map +1 -0
  792. package/dist/true/settings.js +123 -0
  793. package/dist/true/settings.js.map +1 -0
  794. package/dist/true/status.js +189 -0
  795. package/dist/true/status.js.map +1 -0
  796. package/dist/true/sync-cases.js +222 -0
  797. package/dist/true/sync-cases.js.map +1 -0
  798. package/dist/true/sync-manual.js +295 -0
  799. package/dist/true/sync-manual.js.map +1 -0
  800. package/dist/true/sync-runs.js +109 -0
  801. package/dist/true/sync-runs.js.map +1 -0
  802. package/dist/true/worker.js +414 -0
  803. package/dist/true/worker.js.map +1 -0
  804. package/dist/types.js +32 -0
  805. package/dist/types.js.map +1 -0
  806. package/dist/updates/asks.js +38 -0
  807. package/dist/updates/asks.js.map +1 -0
  808. package/dist/updates/index.js +835 -0
  809. package/dist/updates/index.js.map +1 -0
  810. package/dist/updates/owner.js +24 -0
  811. package/dist/updates/owner.js.map +1 -0
  812. package/dist/updates/prefs.js +118 -0
  813. package/dist/updates/prefs.js.map +1 -0
  814. package/dist/updates/route.js +78 -0
  815. package/dist/updates/route.js.map +1 -0
  816. package/dist/updates/sources.js +345 -0
  817. package/dist/updates/sources.js.map +1 -0
  818. package/dist/updates/words.js +452 -0
  819. package/dist/updates/words.js.map +1 -0
  820. package/dist/version.js +53 -0
  821. package/dist/version.js.map +1 -0
  822. package/dist/view-compute.js +382 -0
  823. package/dist/view-compute.js.map +1 -0
  824. package/dist/view-spec.js +894 -0
  825. package/dist/view-spec.js.map +1 -0
  826. package/dist/web/apple-touch-icon.png +0 -0
  827. package/dist/web/assets/ApprovalCard-Br_z5z7G.css +1 -0
  828. package/dist/web/assets/ApprovalCard-CfGhGFo_.js +3 -0
  829. package/dist/web/assets/CardFromRef-BaTGmZ9c.js +2 -0
  830. package/dist/web/assets/CardFromRef-DO7i2uDd.css +1 -0
  831. package/dist/web/assets/Cases-8l_6UcLh.js +2 -0
  832. package/dist/web/assets/Cases-CpfED0uB.css +1 -0
  833. package/dist/web/assets/Chat-BKDiR0qt.js +3 -0
  834. package/dist/web/assets/Chat-DxjdyYUu.css +1 -0
  835. package/dist/web/assets/ConfirmDialog-CIMveD-b.js +2 -0
  836. package/dist/web/assets/ConnectSheet-Cm5vbZdR.js +4 -0
  837. package/dist/web/assets/Docs-HKGuY0WE.js +2 -0
  838. package/dist/web/assets/Docs-fodKx8S7.css +1 -0
  839. package/dist/web/assets/DotDetail-DskS3NWe.js +2 -0
  840. package/dist/web/assets/DotDetail-OWRSD_1x.css +1 -0
  841. package/dist/web/assets/DotStage-BZDclfzV.css +1 -0
  842. package/dist/web/assets/DotStage-CtMNzCN3.js +2 -0
  843. package/dist/web/assets/Dots-BqCsS34m.js +2 -0
  844. package/dist/web/assets/EvidencePack-DQ3kCJX4.js +2 -0
  845. package/dist/web/assets/EvidencePack-DV_9ETsA.css +1 -0
  846. package/dist/web/assets/FailedDeliveries-1F2kFcs9.js +2 -0
  847. package/dist/web/assets/FailedDeliveries-DXa2_VvI.css +1 -0
  848. package/dist/web/assets/Feed-5wiHRTFK.js +2 -0
  849. package/dist/web/assets/GfmView-C-NYaduC.js +4 -0
  850. package/dist/web/assets/ImpactList-C-RNuDB0.js +2 -0
  851. package/dist/web/assets/ImpactList-C612dFdw.css +1 -0
  852. package/dist/web/assets/Inbox-kc1bKVL_.js +2 -0
  853. package/dist/web/assets/JoinScreen-C3L4wmP3.js +2 -0
  854. package/dist/web/assets/JoinScreen-COeAUv5v.css +1 -0
  855. package/dist/web/assets/LeadDialog-N3Zul9Rw.js +8 -0
  856. package/dist/web/assets/LeadDialog-xyWjAuAS.css +1 -0
  857. package/dist/web/assets/ListeningLine-BRFNaczY.js +2 -0
  858. package/dist/web/assets/ListeningLine-pN8CgyiK.css +1 -0
  859. package/dist/web/assets/Mascots-pIyGpxtD.js +2 -0
  860. package/dist/web/assets/ModelChoices-BxO1zyyr.js +2 -0
  861. package/dist/web/assets/ModelChoices-DQJnVxfs.css +1 -0
  862. package/dist/web/assets/NewDotDialog-CDPUWmgN.js +2 -0
  863. package/dist/web/assets/NewDotDialog-DrPPqkSZ.css +1 -0
  864. package/dist/web/assets/NewTestRunDialog-BQBthVHE.js +2 -0
  865. package/dist/web/assets/NoBreakNames-Csxdqdt6.js +2 -0
  866. package/dist/web/assets/Pair-omex_K7H.js +2 -0
  867. package/dist/web/assets/PushSection-CmqHlE_2.js +2 -0
  868. package/dist/web/assets/ReleaseNotes-B6VqFewP.js +2 -0
  869. package/dist/web/assets/ReleaseNotes-DBJUL7Vm.css +1 -0
  870. package/dist/web/assets/ReportPage-BHS1xWEt.js +162 -0
  871. package/dist/web/assets/Reports-BEHnDIb1.js +2 -0
  872. package/dist/web/assets/ReviewChangeDialog-BVSt-cw-.js +2 -0
  873. package/dist/web/assets/ReviewChangeDialog-BbIlHRx0.css +1 -0
  874. package/dist/web/assets/Room-BoG_uge4.js +2 -0
  875. package/dist/web/assets/Room-evAfiW_e.css +1 -0
  876. package/dist/web/assets/RunActions-ZY7yf1AF.js +2 -0
  877. package/dist/web/assets/RunDetail-C8St20GQ.css +1 -0
  878. package/dist/web/assets/RunDetail-qADXjBvd.js +9 -0
  879. package/dist/web/assets/RunRow-BKEJv8ib.js +2 -0
  880. package/dist/web/assets/RunnerTry-Dgpp67-s.js +6 -0
  881. package/dist/web/assets/RunnerTry-DsweWOfO.css +1 -0
  882. package/dist/web/assets/Settings-fs-QJwNM.js +2 -0
  883. package/dist/web/assets/Setup-DI6hBEP-.js +2 -0
  884. package/dist/web/assets/TestRows-wh03T5Li.js +4 -0
  885. package/dist/web/assets/TestRun-DTOyF-Pv.js +2 -0
  886. package/dist/web/assets/TestRuns-ggDjm1Q8.js +2 -0
  887. package/dist/web/assets/TinyBeeAddLaptop-bc3pmh3T.js +2 -0
  888. package/dist/web/assets/TinyBeeAi-CkyOzhm5.js +2 -0
  889. package/dist/web/assets/TinyBeeConnections-B2K_P38u.js +2 -0
  890. package/dist/web/assets/TinyBeeHome-DSpxX5KS.js +3 -0
  891. package/dist/web/assets/TinyBeePlan-BamiLziu.js +2 -0
  892. package/dist/web/assets/TinyBeeSignup-vHcz0Wen.js +2 -0
  893. package/dist/web/assets/TinyBeeSkills-By9RLUse.js +2 -0
  894. package/dist/web/assets/TinyBeeWorkflow-DeNO7Jsh.js +2 -0
  895. package/dist/web/assets/TinyBeeWorkflowEditor-B6OGC2OT.js +2 -0
  896. package/dist/web/assets/TinyBeeWorkflows-BN4-h6lt.js +2 -0
  897. package/dist/web/assets/Trace-BBCI1aIB.css +1 -0
  898. package/dist/web/assets/Trace-aF92UWaH.js +2 -0
  899. package/dist/web/assets/UpdatesSection-CCWEbkue.js +2 -0
  900. package/dist/web/assets/Welcome-CCcy558Q.js +2 -0
  901. package/dist/web/assets/activity-C3qilrDm.css +1 -0
  902. package/dist/web/assets/activity-CBfTSxqw.js +2 -0
  903. package/dist/web/assets/authedMedia-BTYt_tyu.js +2 -0
  904. package/dist/web/assets/beeApi-BHW8Tx1s.js +2 -0
  905. package/dist/web/assets/data-CmXvx3Mj.js +2 -0
  906. package/dist/web/assets/engines-CfdJODFj.css +1 -0
  907. package/dist/web/assets/format-BBZ8vFyT.js +2 -0
  908. package/dist/web/assets/forms-Coc7p2UF.css +1 -0
  909. package/dist/web/assets/hivePush-DZJDokj7.js +2 -0
  910. package/dist/web/assets/impactModel-MY1OFuiP.js +2 -0
  911. package/dist/web/assets/index-B6qKm1NW.js +13 -0
  912. package/dist/web/assets/index-BvKyT1X7.css +1 -0
  913. package/dist/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  914. package/dist/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  915. package/dist/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  916. package/dist/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  917. package/dist/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  918. package/dist/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  919. package/dist/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  920. package/dist/web/assets/lead-BL_P_kri.css +1 -0
  921. package/dist/web/assets/model-BIQy5IVG.js +2 -0
  922. package/dist/web/assets/modelsApi-PUSySRQw.js +2 -0
  923. package/dist/web/assets/newDotModel-BaLLyw57.js +2 -0
  924. package/dist/web/assets/onboarding-bfKqnM4Z.css +1 -0
  925. package/dist/web/assets/phone-Rs7FoyG0.css +1 -0
  926. package/dist/web/assets/phoneApi-BG_1-M-D.js +3 -0
  927. package/dist/web/assets/releaseModel-EynYMVOC.js +2 -0
  928. package/dist/web/assets/releaseNotesModel-A1Rtv3cV.js +2 -0
  929. package/dist/web/assets/reports-BWaA1HhM.js +37 -0
  930. package/dist/web/assets/reports-DXwdWQsB.css +1 -0
  931. package/dist/web/assets/router-W6nZsQ33.js +2 -0
  932. package/dist/web/assets/runs-BY0Owv93.css +1 -0
  933. package/dist/web/assets/shadowWeekModel-BUUiww09.js +2 -0
  934. package/dist/web/assets/storage-yvL36AWt.js +2 -0
  935. package/dist/web/assets/store-IqrqHuFR.js +2 -0
  936. package/dist/web/assets/testruns-BzIZ0cR0.css +1 -0
  937. package/dist/web/assets/testruns-DyaqX4ji.js +2 -0
  938. package/dist/web/assets/true-9Q68cs5b.js +2 -0
  939. package/dist/web/assets/true-DPGBpoir.css +1 -0
  940. package/dist/web/assets/ui-DcmhYyYp.js +13 -0
  941. package/dist/web/assets/ui-DxEDgbIx.css +1 -0
  942. package/dist/web/assets/usePauseDot-BV60ZVy1.js +2 -0
  943. package/dist/web/assets/usePauseDot-NrSzMI1C.css +1 -0
  944. package/dist/web/assets/usePush-Dsm-TjlG.js +2 -0
  945. package/dist/web/assets/useRefChips-CbafNx59.js +68 -0
  946. package/dist/web/assets/useRestoreDot-D0B6W82H.js +2 -0
  947. package/dist/web/assets/view-worker-pyeVAhUT.js +2 -0
  948. package/dist/web/assets/workflowUi-QYsd3n9J.js +2 -0
  949. package/dist/web/favicon.png +0 -0
  950. package/dist/web/icon-192.png +0 -0
  951. package/dist/web/icon-512.png +0 -0
  952. package/dist/web/icon-maskable-512.png +0 -0
  953. package/dist/web/index.html +45 -0
  954. package/dist/web/manifest.webmanifest +23 -0
  955. package/dist/web/mascots/argus-attention.svg +7 -0
  956. package/dist/web/mascots/argus-ok.svg +7 -0
  957. package/dist/web/mascots/argus-outage.svg +7 -0
  958. package/dist/web/mascots/argus-running.svg +7 -0
  959. package/dist/web/mascots/bolt-attention.svg +7 -0
  960. package/dist/web/mascots/bolt-ok.svg +7 -0
  961. package/dist/web/mascots/bolt-outage.svg +7 -0
  962. package/dist/web/mascots/bolt-running.svg +7 -0
  963. package/dist/web/mascots/echo-attention.svg +7 -0
  964. package/dist/web/mascots/echo-ok.svg +7 -0
  965. package/dist/web/mascots/echo-outage.svg +7 -0
  966. package/dist/web/mascots/echo-running.svg +7 -0
  967. package/dist/web/mascots/lens-attention.svg +7 -0
  968. package/dist/web/mascots/lens-ok.svg +7 -0
  969. package/dist/web/mascots/lens-outage.svg +7 -0
  970. package/dist/web/mascots/lens-running.svg +7 -0
  971. package/dist/web/mascots/moss-attention.svg +7 -0
  972. package/dist/web/mascots/moss-ok.svg +7 -0
  973. package/dist/web/mascots/moss-outage.svg +7 -0
  974. package/dist/web/mascots/moss-running.svg +7 -0
  975. package/dist/web/mascots/nit-attention.svg +7 -0
  976. package/dist/web/mascots/nit-ok.svg +7 -0
  977. package/dist/web/mascots/nit-outage.svg +7 -0
  978. package/dist/web/mascots/nit-running.svg +7 -0
  979. package/dist/web/mascots/nova-attention.svg +7 -0
  980. package/dist/web/mascots/nova-ok.svg +7 -0
  981. package/dist/web/mascots/nova-outage.svg +7 -0
  982. package/dist/web/mascots/nova-running.svg +7 -0
  983. package/dist/web/mascots/penn-attention.svg +7 -0
  984. package/dist/web/mascots/penn-ok.svg +7 -0
  985. package/dist/web/mascots/penn-outage.svg +7 -0
  986. package/dist/web/mascots/penn-running.svg +7 -0
  987. package/dist/web/mascots/pip-attention.svg +7 -0
  988. package/dist/web/mascots/pip-ok.svg +7 -0
  989. package/dist/web/mascots/pip-outage.svg +7 -0
  990. package/dist/web/mascots/pip-running.svg +7 -0
  991. package/dist/web/mascots/ward-attention.svg +7 -0
  992. package/dist/web/mascots/ward-ok.svg +7 -0
  993. package/dist/web/mascots/ward-outage.svg +7 -0
  994. package/dist/web/mascots/ward-running.svg +7 -0
  995. package/dist/web/sw.js +98 -0
  996. package/dist/webhook-seen.js +53 -0
  997. package/dist/webhook-seen.js.map +1 -0
  998. package/dist/webhooks.js +625 -0
  999. package/dist/webhooks.js.map +1 -0
  1000. package/dist/zip.js +63 -0
  1001. package/dist/zip.js.map +1 -0
  1002. package/docs/API-V2.md +532 -0
  1003. package/docs/CHAT-EVAL.md +130 -0
  1004. package/docs/CHAT-UI.md +416 -0
  1005. package/docs/CONNECTIONS.md +89 -0
  1006. package/docs/DATA-MODEL.md +518 -0
  1007. package/docs/DEPLOY.md +577 -0
  1008. package/docs/DOGFOOD-LEDGER.md +81 -0
  1009. package/docs/GITHUB.md +148 -0
  1010. package/docs/INTEGRATION-MODEL.md +200 -0
  1011. package/docs/LEAD-DOT.md +553 -0
  1012. package/docs/LIST-PATTERNS.md +52 -0
  1013. package/docs/MULTI-REPO.md +766 -0
  1014. package/docs/PARITY.md +276 -0
  1015. package/docs/PR-REVIEW-QA.md +1266 -0
  1016. package/docs/PR-REVIEW.md +357 -0
  1017. package/docs/SANDBOX.md +320 -0
  1018. package/docs/SETUP.md +776 -0
  1019. package/docs/SLACK.md +80 -0
  1020. package/docs/STAGES.md +619 -0
  1021. package/docs/TRACE-MAP.md +89 -0
  1022. package/docs/TRACKERS.md +225 -0
  1023. package/docs/VOICE.md +36 -0
  1024. package/docs/tinybee/00-README.md +45 -0
  1025. package/docs/tinybee/01-PRD.md +216 -0
  1026. package/docs/tinybee/02-architecture-pairing.md +199 -0
  1027. package/docs/tinybee/03-local-agent.md +222 -0
  1028. package/docs/tinybee/04-local-setup-ui.md +128 -0
  1029. package/docs/tinybee/05-mobile-app-ux.md +199 -0
  1030. package/docs/tinybee/06-environments-and-delivery.md +148 -0
  1031. package/docs/tinybee/07-coding-agent-handoff.md +116 -0
  1032. package/docs/tinybee/OPEN-QUESTIONS.md +26 -0
  1033. package/docs/tinybee/PROGRESS.md +134 -0
  1034. package/package.json +111 -4
@@ -0,0 +1,645 @@
1
+ // Slack bot over Socket Mode (no public URL). A mention routes to a dot and the dot answers in the thread.
2
+ // Outage and digest notices post to the dot's slackChannel through gate() as ActionKind 'slack_post'.
3
+ import { config, liveEnv, log, onSettingsChange } from '../config.js';
4
+ import { logRefused, sandboxOn, sandboxSkips } from '../sandbox.js';
5
+ import { get, insert, list } from '../db.js';
6
+ import { getMeta, setMeta } from '../connect/store.js';
7
+ import { publish } from '../events.js';
8
+ import { answerChat } from '../agent/chat.js';
9
+ import { mdText, renderRefs } from '../refs.js';
10
+ import { LinkTrust, serverUrlTrusted } from '../link-trust.js';
11
+ import { approveAction, effectiveLevel, gate, rejectAction, safeReview } from '../agent/rules.js';
12
+ import { redactCode } from '../agent/redact.js';
13
+ import { workLine } from '../agent/reasoning.js';
14
+ import { updatesOwnDelivery } from '../updates/owner.js';
15
+ import { appUrl, decisionText, slackLink } from '../updates/words.js';
16
+ const MAX_TEXT = 39_000;
17
+ const NOTICE_DEDUPE_MS = 10 * 60_000;
18
+ let client;
19
+ let app;
20
+ let status = { state: 'off' };
21
+ /** Thread ts -> dot id, so follow-up mentions in a thread stay with the same dot. */
22
+ const threadDots = new Map();
23
+ const noticeSent = new Map();
24
+ /** True once the server has called startSlack(), so saved tokens (re)start Socket Mode live. */
25
+ let wanted = false;
26
+ let restarting = Promise.resolve();
27
+ onSettingsChange((keys) => {
28
+ if (!wanted || !keys.some((k) => k === 'SLACK_BOT_TOKEN' || k === 'SLACK_APP_TOKEN'))
29
+ return;
30
+ restarting = restarting
31
+ .then(async () => {
32
+ await stopSlack();
33
+ wanted = true;
34
+ await startSlack();
35
+ })
36
+ .catch((err) => log.warn(`slack: restart after a settings change failed: ${String(err)}`));
37
+ });
38
+ function csv(v) {
39
+ return (v ?? '')
40
+ .split(',')
41
+ .map((s) => s.trim())
42
+ .filter(Boolean);
43
+ }
44
+ export function slackConfigured() {
45
+ return Boolean(liveEnv.SLACK_APP_TOKEN && liveEnv.SLACK_BOT_TOKEN);
46
+ }
47
+ /** Team and user allowlists. An unset list allows everyone the app's install already reaches. */
48
+ export function slackAllowed(teamId, userId) {
49
+ const teams = csv(liveEnv.SLACK_ALLOWED_TEAM);
50
+ const users = csv(liveEnv.SLACK_ALLOWED_USERS);
51
+ if (teams.length && (!teamId || !teams.includes(teamId)))
52
+ return false;
53
+ if (users.length && (!userId || !users.includes(userId)))
54
+ return false;
55
+ return true;
56
+ }
57
+ /** Readiness row for GET /api/setup, plus the live connection flag. */
58
+ export function slackStatus() {
59
+ const base = { key: 'slack', label: 'Slack', docs: 'docs/SETUP.md#slack' };
60
+ const allowlist = { teams: csv(liveEnv.SLACK_ALLOWED_TEAM).length, users: csv(liveEnv.SLACK_ALLOWED_USERS).length };
61
+ if (!slackConfigured()) {
62
+ return { ...base, state: 'missing', detail: 'Set SLACK_APP_TOKEN and SLACK_BOT_TOKEN for Socket Mode.', connected: false, allowlist };
63
+ }
64
+ if (status.state === 'error')
65
+ return { ...base, state: 'error', detail: status.error ?? 'Slack connection failed.', connected: false, allowlist };
66
+ const open = !allowlist.teams && !allowlist.users ? ' No allowlist set.' : '';
67
+ if (status.state === 'connected')
68
+ return {
69
+ ...base,
70
+ state: 'ready',
71
+ detail: `Socket Mode, connected${status.team ? ` to ${status.team}` : ''}.${open} ${INTERACTIVITY_TIP}`,
72
+ connected: true,
73
+ allowlist,
74
+ };
75
+ return {
76
+ ...base,
77
+ state: 'ready',
78
+ detail: `Socket Mode, ${status.state === 'connecting' ? 'connecting' : 'not started'}.${open}`,
79
+ connected: false,
80
+ allowlist,
81
+ };
82
+ }
83
+ /** Escapes the three characters Slack treats as markup, so untrusted text shows as typed. */
84
+ export function escapeMrkdwn(text) {
85
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
86
+ }
87
+ /** Text that shows as typed, inside a code span: Slack makes no link of it, and the characters Slack reads as markup stay escaped. */
88
+ function codeSpan(s) {
89
+ return `\`${escapeMrkdwn(s.replace(/[`\u0000]/g, ''))}\``;
90
+ }
91
+ /** `<url|label>`, with the text and the address escaped so a `|` or `>` cannot end the link. */
92
+ function liveLink(url, label) {
93
+ const addr = escapeMrkdwn(url).replace(/\|/g, '%7C');
94
+ return label === undefined ? `<${addr}>` : `<${addr}|${escapeMrkdwn(label).replace(/\|/g, '\u00a6')}>`;
95
+ }
96
+ const SCHEME = String.raw `(?:https?:\/\/|mailto:)`;
97
+ const URL_LIKE = /https?:\/\/|www\.|mailto:|[\w.+-]+@[\w-]+\.[\w.-]+|\]\(/i;
98
+ const EMAIL = /[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+/g;
99
+ // A host name with no scheme and no `www.`, which Slack makes a link by itself: `evil.com`, `login.bank`, `evil.studio`. Slack links a name
100
+ // that ends in any real top-level domain, and there are over a thousand, so the list below is of the endings that are file names and not
101
+ // domains: `app.ts`, `package.json`, `styles.css`, `Node.js` stay as they are. Any other ending of 2 to 24 letters counts as a domain, with or
102
+ // without a path, and a dotted word such as `cart.total` then shows in a code span, which does no harm. A file ending counts only with a path
103
+ // (`evil.js/login`). `md`, `sh`, `py` and `rs` are country domains too, so they are not on the list: `README.md` shows in a code span.
104
+ const FILE_ENDINGS = 'js|jsx|mjs|cjs|ts|tsx|mts|cts|json|jsonc|json5|mdx|txt|log|yml|yaml|toml|ini|cfg|conf|env|lock|css|scss|sass|less|html|htm|xml|svg|png|jpg|jpeg|gif|webp|ico|pdf|csv|tsv|sql|bash|zsh|rb|go|java|kt|swift|php|cs|cpp|hpp|vue|svelte|map|gz|tgz|tar|bak|tmp|spec|test|snap|diff|patch';
105
+ const BARE_HOST = new RegExp(String.raw `(?<![\w@./:+-])(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:(?!(?:${FILE_ENDINGS})(?![\w-]))[a-z]{2,24}(?![\w-])(?!\.[\w-])(?::\d+)?(?:\/[^\s<>"'\u0060\u0000]*)?|[a-z]{2,24}(?::\d+)?\/[^\s<>"'\u0060\u0000]*)`, 'gi');
106
+ /**
107
+ * Converts the chat's Markdown to Slack mrkdwn: bold, links, headings. Every other character is escaped, so a PR
108
+ * title like `<!channel>` or `<https://evil|Open details>` stays plain text; only the `[text](https://...)` links
109
+ * the server builds become `<url|text>`, with their text and address escaped so a `|` or `>` cannot end the link.
110
+ * With `opts.trusted`, "the server builds" is decided per address (see MrkdwnOptions).
111
+ */
112
+ export function toMrkdwn(md, opts = {}) {
113
+ const held = [];
114
+ const hold = (s) => `\u0000${held.push(s) - 1}\u0000`;
115
+ const trusted = opts.trusted;
116
+ let text = md.replace(/\u0000/g, '');
117
+ if (!trusted) {
118
+ text = text.replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, (_m, label, url) => hold(liveLink(url, label)));
119
+ }
120
+ else {
121
+ const live = (url) => /^https?:\/\//i.test(url) && trusted(url);
122
+ /** Bare addresses and e-mail addresses of a stretch of text. */
123
+ const bare = (s) => s
124
+ .replace(/(?:https?:\/\/|www\.)[^\s<>"'`\u0000]+/gi, (m) => {
125
+ const trail = /[.,;:!?)\]}]+$/.exec(m)?.[0] ?? '';
126
+ const url = trail ? m.slice(0, -trail.length) : m;
127
+ return (live(url) ? hold(liveLink(url)) : hold(codeSpan(url))) + trail;
128
+ })
129
+ .replace(EMAIL, (m) => hold(codeSpan(m)))
130
+ .replace(BARE_HOST, (m) => {
131
+ const trail = /[.,;:!?)\]}]+$/.exec(m)?.[0] ?? '';
132
+ return hold(codeSpan(trail ? m.slice(0, -trail.length) : m)) + trail;
133
+ });
134
+ /** A link that is not live: the label (its own addresses made inert too), then the address as code. */
135
+ const inert = (label, url) => {
136
+ const l = label?.trim();
137
+ return hold(l && l !== url ? `${escapeMrkdwn(bare(l))} (${hold(codeSpan(url))})` : codeSpan(url));
138
+ };
139
+ // The model's own code spans: one with an address in it loses its backticks and is handled as text (its address then gets a span of
140
+ // ours), one without keeps its form. Any backtick left over becomes a look-alike, so no stray one can pair with a span of ours.
141
+ // A fence keeps its own three backticks at each end and loses the ones inside it: an odd number of them in the fence would make the count
142
+ // of backticks in the text odd, and clip would then cut at the end of a span of ours and leave it open.
143
+ text = text.replace(/```[\s\S]*?```|`[^`\n]+`/g, (m) => URL_LIKE.test(m) ? m.replace(/`/g, '') : hold(escapeMrkdwn(m.startsWith('```') ? `\`\`\`${m.slice(3, -3).replace(/`/g, '\u02cb')}\`\`\`` : m)));
144
+ text = text.replace(/`/g, '\u02cb');
145
+ text = text.replace(new RegExp(String.raw `\[([^\]]+)\]\((${SCHEME}[^)\s]*)\)`, 'gi'), (_m, label, url) => live(url) ? hold(liveLink(url, label)) : inert(label, url));
146
+ text = text.replace(new RegExp(String.raw `<(${SCHEME}[^>|\s]*)(?:\|([^>]*))?>`, 'gi'), (_m, url, label) => live(url) ? hold(liveLink(url, label ?? url)) : inert(label, url));
147
+ text = bare(text);
148
+ }
149
+ let out = escapeMrkdwn(text);
150
+ if (opts.lookAlike)
151
+ out = mdText(out);
152
+ out = out.replace(/^#{1,6}\s+(.+)$/gm, '*$1*').replace(/\*\*(.+?)\*\*/g, '*$1*');
153
+ // A held piece may hold another (the address in a label).
154
+ for (let i = 0; i <= held.length && /\u0000\d+\u0000/.test(out); i++)
155
+ out = out.replace(/\u0000(\d+)\u0000/g, (_m, n) => held[Number(n)] ?? '');
156
+ return out;
157
+ }
158
+ /** A chat answer as Slack text (KAT-380): only an address server code built in this turn is a live link. */
159
+ export function answerMrkdwn(md, trust) {
160
+ // An app link the server wrote as a page of the app (`#/cases?...`, the designed test cases) opens in the app, and Slack needs the whole
161
+ // address: it is joined to the public URL, and that address is then the one that is live. With no public URL there is nothing to link to.
162
+ const base = config.publicUrl?.replace(/\/$/, '');
163
+ const text = md.replace(/\[([^\]\n]+)\]\((#\/[^)\s]*)\)/g, (m, label, page) => {
164
+ if (!trust.has(page))
165
+ return m;
166
+ if (!base)
167
+ return label.trim();
168
+ const whole = `${base}/${page}`;
169
+ trust.add(whole);
170
+ return `[${label}](${whole})`;
171
+ });
172
+ return toMrkdwn(text, { trusted: (u) => trust.has(u) });
173
+ }
174
+ function clip(text) {
175
+ if (text.length <= MAX_TEXT)
176
+ return text;
177
+ let cut = text.slice(0, MAX_TEXT);
178
+ // The cut never lands inside a code span or a link, where a piece of an address could be left outside of its span (KAT-380).
179
+ if ((cut.match(/`/g)?.length ?? 0) % 2)
180
+ cut = cut.slice(0, cut.lastIndexOf('`'));
181
+ const open = cut.lastIndexOf('<');
182
+ if (open > cut.lastIndexOf('>'))
183
+ cut = cut.slice(0, open);
184
+ return `${cut}\n[truncated]`;
185
+ }
186
+ function stripMentions(text) {
187
+ return text
188
+ .replace(/<@[A-Z0-9]+(\|[^>]*)?>/g, ' ')
189
+ .replace(/\s+/g, ' ')
190
+ .trim();
191
+ }
192
+ function escapeRe(s) {
193
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
194
+ }
195
+ /** Picks the dot for a mention: one named in the text (longest name wins), else the thread's dot, else the channel's dot. */
196
+ export function pickDot(text, channel, threadTs) {
197
+ const dots = list('watches').filter((w) => !w.archived);
198
+ const clean = stripMentions(text).toLowerCase();
199
+ const named = dots
200
+ .filter((w) => w.name && new RegExp(`(^|[^a-z0-9])@?${escapeRe(w.name.toLowerCase())}($|[^a-z0-9])`).test(clean))
201
+ .sort((a, b) => b.name.length - a.name.length)[0];
202
+ if (named)
203
+ return named;
204
+ const remembered = threadTs ? threadDots.get(threadTs) : undefined;
205
+ if (remembered) {
206
+ const w = dots.find((d) => d.id === remembered);
207
+ if (w)
208
+ return w;
209
+ }
210
+ const norm = (c) => c.replace(/^#/, '').toLowerCase();
211
+ // Several dots can share a channel. An unnamed message reaches the lead among them (KAT-405), else the first in table order; a message that
212
+ // names a member went to that member above.
213
+ const sharing = dots.filter((w) => w.slackChannel && norm(w.slackChannel) === norm(channel));
214
+ return sharing.find((w) => w.kind === 'lead') ?? sharing[0];
215
+ }
216
+ /**
217
+ * Channels where someone mentioned the bot. Slack lists private channels only with the groups:read scope,
218
+ * which older installs lack, so the channel picker also offers these. Saved once per new channel.
219
+ */
220
+ export function rememberChannel(id, name) {
221
+ if (!id)
222
+ return;
223
+ const seen = getMeta('slack').seenChannels ?? [];
224
+ const known = seen.find((c) => c.id === id);
225
+ if (known && (known.name || !name))
226
+ return;
227
+ const next = [...seen.filter((c) => c.id !== id), { id, ...(name ? { name } : {}) }].slice(-100);
228
+ setMeta('slack', { seenChannels: next });
229
+ }
230
+ /** Handles one app_mention. Returns what happened, for logs and tests. */
231
+ export async function handleMention(event, teamId, deps) {
232
+ if (event.bot_id)
233
+ return 'ignored';
234
+ const team = teamId ?? event.team ?? event.user_team;
235
+ const thread = event.thread_ts ?? event.ts;
236
+ if (!slackAllowed(team, event.user)) {
237
+ log.warn(`slack: mention from ${event.user ?? 'unknown'} in team ${team ?? 'unknown'} is not on the allowlist`);
238
+ return 'denied';
239
+ }
240
+ rememberChannel(event.channel, deps.channelName ? await deps.channelName(event.channel).catch(() => undefined) : undefined);
241
+ const text = stripMentions(event.text);
242
+ const dot = pickDot(event.text, event.channel, event.thread_ts);
243
+ if (dot) {
244
+ threadDots.set(thread, dot.id);
245
+ if (threadDots.size > 1000)
246
+ threadDots.delete(threadDots.keys().next().value);
247
+ }
248
+ // The addresses server code builds for this answer. Nothing else the answer carries is a live link (KAT-380).
249
+ const trust = new LinkTrust();
250
+ const answer = deps.answer ??
251
+ ((input) => answerChat({ ...input, channel: 'slack' }, () => { }, () => { }));
252
+ try {
253
+ // Work the turn queues (a rerun, a review) reports back in this thread (KAT-143).
254
+ const origin = {
255
+ channel: 'slack',
256
+ slackChannel: event.channel,
257
+ threadTs: thread,
258
+ ...(event.user ? { user: event.user } : {}),
259
+ text: text.slice(0, 200),
260
+ };
261
+ let work;
262
+ const onWork = (w) => {
263
+ work = w;
264
+ };
265
+ const reply = text
266
+ ? await answer(dot ? { dotId: dot.id, text, origin, onWork, trust } : { text, origin, onWork, trust })
267
+ : 'Ask me something, for example "is prod up?" or "what broke today?"';
268
+ const prefix = dot ? `*${dot.name}*: ` : '';
269
+ // Titled links for every PR, ticket, and run the answer names (KAT-225); a no-op for text answerChat already titled.
270
+ const titled = await renderRefs(reply, { ...(dot ? { dotId: dot.id } : {}), format: 'markdown', absolute: true, trust });
271
+ await deps.post({
272
+ channel: event.channel,
273
+ thread_ts: thread,
274
+ text: clip(prefix + answerMrkdwn(titled, trust)) + workedLine(work, dot?.id),
275
+ unfurl_links: false,
276
+ });
277
+ return 'answered';
278
+ }
279
+ catch (err) {
280
+ log.error('slack: answer failed', err);
281
+ try {
282
+ await deps.post({
283
+ channel: event.channel,
284
+ thread_ts: thread,
285
+ text: clip(answerMrkdwn('Sorry, I could not answer that. The error is in the localdost server log.', trust)),
286
+ });
287
+ }
288
+ catch {
289
+ /* nothing more to do */
290
+ }
291
+ return 'failed';
292
+ }
293
+ }
294
+ /** Starts the Socket Mode app when both tokens are set. Returns false when Slack is not configured. */
295
+ export async function startSlack() {
296
+ // Sandbox mode (KAT-286): no Socket Mode connection, so nothing is read from or posted to Slack.
297
+ if (sandboxOn()) {
298
+ sandboxSkips('Slack');
299
+ status = { state: 'off' };
300
+ return false;
301
+ }
302
+ wanted = true;
303
+ if (!slackConfigured()) {
304
+ status = { state: 'off' };
305
+ return false;
306
+ }
307
+ if (app)
308
+ return true;
309
+ status = { state: 'connecting', startedAt: new Date().toISOString() };
310
+ try {
311
+ const { App, LogLevel } = await import('@slack/bolt');
312
+ const bolt = new App({
313
+ token: liveEnv.SLACK_BOT_TOKEN,
314
+ appToken: liveEnv.SLACK_APP_TOKEN,
315
+ socketMode: true,
316
+ logLevel: LogLevel.WARN,
317
+ });
318
+ bolt.event('app_mention', async ({ event, body, client: web }) => {
319
+ const teamId = body.team_id;
320
+ await handleMention(event, teamId, {
321
+ post: (args) => web.chat.postMessage(args),
322
+ channelName: async (id) => (await web.conversations.info({ channel: id })).channel?.name,
323
+ });
324
+ });
325
+ // Approve and Reject on a dot's decision message (KAT-143). Interactivity rides the same socket.
326
+ bolt.action(/^localdost_(approve|reject)$/, async ({ ack, body, action, respond }) => {
327
+ await ack();
328
+ const a = action;
329
+ const b = body;
330
+ const text = await handleSlackDecision({
331
+ approvalId: a.value ?? '',
332
+ decision: a.action_id === 'localdost_approve' ? 'approve' : 'reject',
333
+ user: b.user?.id,
334
+ userName: b.user?.username ?? b.user?.name,
335
+ team: b.team?.id ?? b.user?.team_id,
336
+ });
337
+ await respond({ replace_original: false, response_type: 'in_channel', text }).catch((err) => log.warn(`slack: reply to a button press failed: ${String(err)}`));
338
+ });
339
+ // Preview opens the Inbox in the browser; Slack still sends the press, which needs an answer.
340
+ bolt.action('localdost_preview', async ({ ack }) => {
341
+ await ack();
342
+ });
343
+ bolt.error(async (err) => {
344
+ log.error('slack: bolt error', err);
345
+ });
346
+ await bolt.start();
347
+ client = bolt.client;
348
+ app = bolt;
349
+ try {
350
+ const who = (await bolt.client.auth.test());
351
+ status = { ...status, state: 'connected', botUserId: who.user_id, team: who.team };
352
+ }
353
+ catch {
354
+ status = { ...status, state: 'connected' };
355
+ }
356
+ if (!csv(liveEnv.SLACK_ALLOWED_TEAM).length && !csv(liveEnv.SLACK_ALLOWED_USERS).length)
357
+ log.warnOnce('slack-allowlist', 'Slack has no SLACK_ALLOWED_TEAM or SLACK_ALLOWED_USERS; anyone in the installed workspace can ask the dots.');
358
+ await tipAdminOnFirstConnect();
359
+ log.info(`Slack connected over Socket Mode${status.team ? ` (${status.team})` : ''}`);
360
+ return true;
361
+ }
362
+ catch (err) {
363
+ status = { state: 'error', error: err instanceof Error ? err.message : String(err) };
364
+ log.error('slack: start failed', err);
365
+ return false;
366
+ }
367
+ }
368
+ export async function stopSlack() {
369
+ wanted = false;
370
+ const a = app;
371
+ app = undefined;
372
+ client = undefined;
373
+ status = { state: 'off' };
374
+ if (a)
375
+ await a.stop().catch(() => undefined);
376
+ }
377
+ /** Test seam: use a fake Web API client, or undefined to fall back to the outbox. */
378
+ export function setSlackClient(c) {
379
+ client = c;
380
+ }
381
+ function channelOf(action) {
382
+ const meta = action.meta ?? {};
383
+ const ch = typeof meta.channel === 'string' && meta.channel ? meta.channel : action.target.replace(/^slack:/, '');
384
+ if (!ch)
385
+ throw new Error('slack_post needs a channel (meta.channel or target slack:<channel>)');
386
+ return ch.replace(/^#/, '');
387
+ }
388
+ /**
389
+ * Executor for ActionKind 'slack_post'. Posts through the Slack client, or writes an outbox row when Slack is not
390
+ * connected (demo mode), matching how the other integrations behave without credentials.
391
+ */
392
+ export async function executeSlackPost(action, watch, runId) {
393
+ if (action.kind !== 'slack_post')
394
+ throw new Error(`executeSlackPost got ${action.kind}`);
395
+ const channel = channelOf(action);
396
+ const dotId = watch?.id ?? (typeof action.meta?.watchId === 'string' ? action.meta.watchId : undefined);
397
+ const refOpts = { ...(dotId ? { dotId } : {}), absolute: true };
398
+ // A reviewed or approved draft goes out as written. Titling a reference reads text from the tracker that nobody
399
+ // reviewed (KAT-256), so only a templated notice built from fixed strings gets titles.
400
+ const titled = action.meta?.templated === true;
401
+ const [title, body] = titled
402
+ ? await Promise.all([renderRefs(action.title, { ...refOpts, format: 'text' }), renderRefs(action.body, { ...refOpts, format: 'markdown' })])
403
+ : [action.title, action.body];
404
+ const text = clip(`*${escapeMrkdwn(title)}*\n${toMrkdwn(body)}`);
405
+ if (client) {
406
+ const threadTs = typeof action.meta?.threadTs === 'string' ? action.meta.threadTs : undefined;
407
+ await client.chat.postMessage({ channel, text, ...(threadTs ? { thread_ts: threadTs } : {}), unfurl_links: false });
408
+ return;
409
+ }
410
+ const watchId = watch?.id ?? (typeof action.meta?.watchId === 'string' ? action.meta.watchId : 'slack');
411
+ const rid = runId ?? (typeof action.meta?.runId === 'string' ? action.meta.runId : undefined);
412
+ const item = insert('outbox', rid ? { watchId, runId: rid, action } : { watchId, action });
413
+ publish({ type: 'outbox', item });
414
+ }
415
+ /** "Checked 4 things in 16 s" with the link to the dot's chat (KAT-374); empty when the turn reported nothing. */
416
+ function workedLine(work, dotId) {
417
+ if (!work)
418
+ return '';
419
+ const link = slackLink(dotId ? `/#/chat/${dotId}` : '/#/chat', 'Open in localdost', config.publicUrl);
420
+ return `\n_${workLine(work)}_${link ? ` · ${link}` : ''}`;
421
+ }
422
+ /** The app link under a notice as Markdown, which toMrkdwn turns into a Slack link. */
423
+ function noticeLink(url) {
424
+ const abs = appUrl(url, config.publicUrl);
425
+ return abs ? `\n[Open in localdost](${abs})` : '';
426
+ }
427
+ /**
428
+ * Posts an outage or digest notice to the dot's slackChannel. With a runId it goes through gate() and is recorded
429
+ * on the run. Without one there is no run to hold an approval, so it posts only when gate() would act: the
430
+ * effective level is 'act' (a templated notice under rule act) and auto-review passes; anything else is blocked
431
+ * (KAT-54). `opts.templated` marks a notice built from fixed strings plus probe fields. Deduped per notice key for
432
+ * 10 minutes, like push.
433
+ */
434
+ export async function postNotice(watch, notice, runId, opts = {}) {
435
+ if (!watch.slackChannel)
436
+ return 'skipped';
437
+ // The update router posts outages and digests itself, once, to the dot's channel or DM (KAT-143).
438
+ if (updatesOwnDelivery())
439
+ return 'skipped';
440
+ const now = Date.now();
441
+ const last = noticeSent.get(notice.key);
442
+ if (last && now - last < NOTICE_DEDUPE_MS)
443
+ return 'skipped';
444
+ noticeSent.set(notice.key, now);
445
+ for (const [k, t] of noticeSent)
446
+ if (now - t > NOTICE_DEDUPE_MS)
447
+ noticeSent.delete(k);
448
+ const channel = watch.slackChannel.replace(/^#/, '');
449
+ const action = {
450
+ kind: 'slack_post',
451
+ target: `slack:${channel}`,
452
+ title: notice.title,
453
+ // A templated notice is built by the server; any other body carries model or tracker text, so no link in it stays live.
454
+ body: `${opts.templated ? notice.body : mdText(notice.body)}${noticeLink(notice.url)}`,
455
+ meta: {
456
+ channel,
457
+ watchId: watch.id,
458
+ key: notice.key,
459
+ urgency: notice.urgency,
460
+ ...(runId ? { runId } : {}),
461
+ ...(opts.templated ? { templated: true } : {}),
462
+ },
463
+ };
464
+ if (runId)
465
+ return gate(action, watch, runId);
466
+ if (effectiveLevel(watch, action) !== 'act') {
467
+ log.info(`slack: notice "${notice.key}" not posted: no run to hold an approval, and only templated notices post on their own`);
468
+ return 'blocked';
469
+ }
470
+ const safe = { ...action, title: redactCode(action.title), body: redactCode(action.body) };
471
+ const review = await safeReview(safe, watch);
472
+ if (!review.ok || review.escalate) {
473
+ log.warn(`slack: notice "${notice.key}" not posted: ${review.reason}`);
474
+ return 'blocked';
475
+ }
476
+ try {
477
+ await executeSlackPost(safe, watch);
478
+ return 'executed';
479
+ }
480
+ catch (err) {
481
+ log.warn('slack: notice post failed', err);
482
+ return 'blocked';
483
+ }
484
+ }
485
+ /** Relative app links (`/#/runs/id`) point at the public URL, or lose the link when there is none. */
486
+ export function absoluteLinks(md) {
487
+ const base = config.publicUrl?.replace(/\/$/, '');
488
+ return md.replace(/\[([^\]]+)\]\((\/[^)\s]*)\)/g, (_m, label, path) => (base ? `[${label}](${base}${path})` : label));
489
+ }
490
+ /** The Block Kit message for a decision: the text, then Preview, the button that posts, and the one that drops it. */
491
+ export function updateBlocks(text, approvalId, buttons = { post: 'Post', cancel: "Don't post" }) {
492
+ if (!approvalId)
493
+ return undefined;
494
+ return [
495
+ { type: 'section', text: { type: 'mrkdwn', text: text.slice(0, 2900) } },
496
+ {
497
+ type: 'actions',
498
+ elements: [
499
+ ...(buttons.previewUrl
500
+ ? [{ type: 'button', text: { type: 'plain_text', text: 'Preview' }, action_id: 'localdost_preview', url: buttons.previewUrl }]
501
+ : []),
502
+ { type: 'button', text: { type: 'plain_text', text: buttons.post.slice(0, 75) }, style: 'primary', action_id: 'localdost_approve', value: approvalId },
503
+ { type: 'button', text: { type: 'plain_text', text: buttons.cancel.slice(0, 75) }, action_id: 'localdost_reject', value: approvalId },
504
+ ],
505
+ },
506
+ ];
507
+ }
508
+ // A Slack app made before interactivity was in the manifest shows the buttons but Slack refuses the click (KAT-280).
509
+ // A refused click never reaches the server, and the Slack API offers no read of the app's Interactivity switch, so
510
+ // no failure can trigger the tip. The admin gets it once, when the Slack connection is first made, and Setup shows it.
511
+ export const INTERACTIVITY_TIP = 'Turn on Interactivity in the localdost Slack app (docs/SLACK.md) so the Approve and Reject buttons work. Until then, people can decide in the localdost Inbox.';
512
+ /** The admin: the first user on SLACK_ALLOWED_USERS. A delivery user is not the admin. */
513
+ function slackAdmin() {
514
+ return csv(liveEnv.SLACK_ALLOWED_USERS)[0];
515
+ }
516
+ /** Sends the admin the Interactivity tip the first time Slack connects. Returns whether it sent one. */
517
+ export async function tipAdminOnFirstConnect(c = client) {
518
+ if (!c || getMeta('slack').interactivityTipSent)
519
+ return false;
520
+ const admin = slackAdmin();
521
+ if (!admin)
522
+ return false;
523
+ setMeta('slack', { interactivityTipSent: true });
524
+ await c.chat
525
+ .postMessage({ channel: admin, text: INTERACTIVITY_TIP, unfurl_links: false })
526
+ .catch((err) => log.warn(`slack: tip for the admin failed: ${String(err)}`));
527
+ return true;
528
+ }
529
+ /**
530
+ * Posts one dot update. It is a templated slack_post: it goes out under rule act (or act_when_asked when a
531
+ * person asked) once auto-review passes, like postNotice without a run. Without a Slack connection it lands
532
+ * in the outbox, as the other integrations do in demo mode.
533
+ */
534
+ export async function postUpdate(watch, p) {
535
+ const channel = p.channel.replace(/^#/, '');
536
+ if (!channel)
537
+ return { ok: false, reason: 'no Slack channel' };
538
+ if (sandboxOn()) {
539
+ logRefused('slack_post', `slack:${channel}`);
540
+ return { ok: false, reason: `sandbox: refused slack_post to slack:${channel}` };
541
+ }
542
+ const action = {
543
+ kind: 'slack_post',
544
+ target: `slack:${channel}`,
545
+ title: p.title,
546
+ body: p.body,
547
+ meta: {
548
+ channel,
549
+ watchId: watch.id,
550
+ key: p.key,
551
+ templated: true,
552
+ ...(p.threadTs ? { threadTs: p.threadTs } : {}),
553
+ ...(p.asked ? { askedBy: 'user' } : {}),
554
+ ...(p.runId ? { runId: p.runId } : {}),
555
+ },
556
+ };
557
+ const level = effectiveLevel(watch, action);
558
+ if (level !== 'act' && !(level === 'act_when_asked' && p.asked))
559
+ return { ok: false, reason: `rule for slack_post is ${level}` };
560
+ const safe = { ...action, title: redactCode(action.title), body: redactCode(action.body) };
561
+ const review = await safeReview(safe, watch);
562
+ if (!review.ok || review.escalate)
563
+ return { ok: false, reason: review.reason };
564
+ const refOpts = { dotId: watch.id, absolute: true };
565
+ const [title, body] = await Promise.all([renderRefs(safe.title, { ...refOpts, format: 'text' }), renderRefs(safe.body, { ...refOpts, format: 'markdown' })]);
566
+ // One absolute link under the message; a decision has its Preview button instead.
567
+ const link = p.approvalId ? '' : slackLink(p.url, p.linkLabel ?? 'Open details', config.publicUrl);
568
+ // The update text mixes links the server built with titles and errors from PRs, tickets and tests. A link stays live only when its address is one
569
+ // the server builds (an app page, a PR or ticket of a repo or tracker the dots hold), and the look-alike brackets go on here and nowhere else (KAT-380).
570
+ const watches = list('watches');
571
+ const how = { trusted: (u) => serverUrlTrusted(u, watches), lookAlike: true };
572
+ const text = `${clip(`*${toMrkdwn(title, how)}*\n${toMrkdwn(absoluteLinks(body), how)}`)}${link ? `\n${link}` : ''}`;
573
+ if (client) {
574
+ const blocks = updateBlocks(text, p.approvalId, decisionButtons(watch, p));
575
+ const res = (await client.chat.postMessage({
576
+ channel,
577
+ text,
578
+ ...(p.threadTs ? { thread_ts: p.threadTs } : {}),
579
+ ...(blocks ? { blocks } : {}),
580
+ unfurl_links: false,
581
+ }));
582
+ return { ok: true, channel: res?.channel ?? channel, ...(res?.ts ? { ts: res.ts } : {}) };
583
+ }
584
+ const item = insert('outbox', p.runId ? { watchId: watch.id, runId: p.runId, action: safe } : { watchId: watch.id, action: safe });
585
+ publish({ type: 'outbox', item });
586
+ // Kept in the outbox, not sent: an update must not count Slack as delivered when no Slack is connected.
587
+ return { ok: false, reason: 'Slack is not connected; the update is in the outbox' };
588
+ }
589
+ function decisionButtons(watch, p) {
590
+ const a = p.approvalId ? get('approvals', p.approvalId) : undefined;
591
+ if (!a)
592
+ return undefined;
593
+ const run = a.runId ? get('runs', a.runId) : undefined;
594
+ const d = decisionText(a, run, watch.name);
595
+ const previewUrl = appUrl(p.url ?? '/#/inbox', config.publicUrl);
596
+ return { post: d.post, cancel: d.cancel, ...(previewUrl ? { previewUrl } : {}) };
597
+ }
598
+ /** `Slack anh.chu` when Slack sent a name, else `Slack U07ABC123`: the id stays as the fallback and never needs the users:read scope. */
599
+ export function slackApprover(d) {
600
+ const name = (d.userName ?? '')
601
+ .replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2066-\u2069<>@]/g, ' ')
602
+ .replace(/\s+/g, ' ')
603
+ .trim()
604
+ .slice(0, 40);
605
+ return `Slack ${name || d.user}`;
606
+ }
607
+ /** Approve or Reject pressed in Slack. Same allowlist as mentions. Returns the reply text in plain words. */
608
+ export async function handleSlackDecision(d, deps = {}) {
609
+ if (!slackAllowed(d.team, d.user))
610
+ return 'You are not on the allowlist for this localdost server.';
611
+ const a = d.approvalId ? get('approvals', d.approvalId) : undefined;
612
+ if (!a)
613
+ return 'That draft is gone.';
614
+ // Approving sends something outside localdost, so it needs a named person: a user on SLACK_ALLOWED_USERS, or
615
+ // the dot's own Slack user. An empty allowlist lets anyone mention the dot, but not approve its drafts.
616
+ const owner = a.watchId ? get('watches', a.watchId)?.updates?.slackUser : undefined;
617
+ const approvers = csv(liveEnv.SLACK_ALLOWED_USERS);
618
+ if (!d.user || !(approvers.includes(d.user) || (owner && owner === d.user)))
619
+ return 'Only the dot owner or a user on SLACK_ALLOWED_USERS can approve or reject from Slack. Use the localdost Inbox instead.';
620
+ if (a.status !== 'pending')
621
+ return `Already ${a.status}.`;
622
+ if (a.expiresAt && Date.parse(a.expiresAt) <= Date.now())
623
+ return 'That draft expired.';
624
+ const who = d.user ? `<@${d.user}>` : 'Someone';
625
+ try {
626
+ // The record keeps who pressed the button (KAT-356).
627
+ const by = { by: slackApprover(d) };
628
+ if (d.decision === 'approve')
629
+ await (deps.approve ?? approveAction)(a.id, undefined, by);
630
+ else
631
+ await (deps.reject ?? rejectAction)(a.id, undefined, by);
632
+ }
633
+ catch (err) {
634
+ log.warn(`slack: ${d.decision} of ${a.id} failed: ${String(err)}`);
635
+ return `Could not ${d.decision} it. The error is in the localdost server log.`;
636
+ }
637
+ return `${who} ${d.decision === 'approve' ? 'approved' : 'rejected'}: ${a.action.title}`;
638
+ }
639
+ /** Test helper: forget thread routing and notice dedupe. */
640
+ export function resetSlackState() {
641
+ setMeta('slack', { interactivityTipSent: false });
642
+ threadDots.clear();
643
+ noticeSent.clear();
644
+ }
645
+ //# sourceMappingURL=index.js.map