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
package/dist/api.js ADDED
@@ -0,0 +1,1047 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import webpush from 'web-push';
5
+ import { config, log } from './config.js';
6
+ import { SANDBOX_BANNER } from './sandbox.js';
7
+ import { count, createWatch, findOne, get, insert, list, remove, update } from './db.js';
8
+ import { liveClientOpened, publish, subscribe } from './events.js';
9
+ import { trackDeviceStream } from './phone/pairing.js';
10
+ import { approverOf, roleOf } from './phone/auth.js';
11
+ import { ingest } from './webhooks.js';
12
+ import { answerChat } from './agent/chat.js';
13
+ import { refreshStoredBlocks } from './agent/triage.js';
14
+ import { llmBackend, STAGE_MODEL_IDS } from './agent/llm.js';
15
+ import { getReport } from './reports/index.js';
16
+ import { approvalExpired, approveAction, rejectAction, RetryingError } from './agent/rules.js';
17
+ import { withTargetTitles } from './approval-targets.js';
18
+ import { approvalStats, closeApproval, liveCount, pendingPrs, reconcileThrottled, sweepTimeExpired } from './agent/approvals.js';
19
+ import { githubFor } from './integrations/github.js';
20
+ import { tidyTrackerScope, trackerScopeError } from './integrations/github-issues.js';
21
+ import { effectiveReviewOptions, parseReviewPatch } from './routes/review.js';
22
+ import { testSavingsError } from './flows/testSavings.js';
23
+ import { prTestTargetError } from './flows/prTarget.js';
24
+ import { testRepoSwitchError } from './flows/testRepo.js';
25
+ import { nightlyError, tidyNightly } from './nightly.js';
26
+ import { settleNightlySave } from './nightly-scheduler.js';
27
+ import { cleanCopyFiles, cleanDeployEnvironment, cleanDeploySec, cleanPassEnv, cleanTestBranches, DEPLOY_ADMIN_MESSAGE, deployAdminChanges, deployWaitError, REPO_ADMIN_CODE, REPO_ADMIN_MESSAGE, RUNNER_ADMIN_CODE, RUNNER_ADMIN_MESSAGE, runnerAdminChanges, testBranchesError, validateRunner, } from './runner/command.js';
28
+ import { releaseDeployWaits } from './deploy-wait.js';
29
+ import { ensureReposForSave } from './repos-github.js';
30
+ import { archiveDot, deleteDot, nameMatches, parseRemoveMode, restoreDot, deletePreview } from './dot-remove.js';
31
+ import { deliveryOf, webhookRoute } from './delivery.js';
32
+ import { scopeUnreadOf } from './pr-scope.js';
33
+ import { pollFailureFor } from './monitor/pollers.js';
34
+ import { getWorkspaceRepo, repoAdminChanges, repoAsRun, repoDeployChanges, repoDeployWaitError, repoId, repoMirror, resolveRepoFields, watchRepos, } from './repos.js';
35
+ import { runSummary } from './review/run-summary.js';
36
+ import { hasEvidencePack } from './evidence-pack.js';
37
+ import { runStage, STAGE_KEYS } from './stages/index.js';
38
+ import { derivedStages, mergeStages, NO_STAGE_OFFER, resetWatcherNotes, stagesError, stagesNoticeError } from './stages/config.js';
39
+ import { VERSION, commitSha } from './version.js';
40
+ import { hiveMode } from './hive/embed.js';
41
+ import { tinybeeHealth } from './bee/status.js';
42
+ import { changedRuleKinds, endShadowWeek, forgetShadowKinds, parseShadowDays, startShadowWeek } from './shadow-week.js';
43
+ import { capOf, capState, modelCapError, usageOf } from './model-usage.js';
44
+ import { foundByLine, sourcesOf } from './lead-defects.js';
45
+ import { forgetLeadIndex, leadError, leadOf, leadStages, memberChanges, onlyMemberRefusal } from './lead.js';
46
+ import { releaseModelCapHolds } from './model-cap.js';
47
+ import { afterTrueSyncSave, mergeTrueSync, TRUE_ADMIN_CODE, TRUE_ADMIN_MESSAGE, trueSyncAdminChanges, trueSyncError } from './true/settings.js';
48
+ import { trueKeyView } from './true/key.js';
49
+ // Dot status
50
+ const ATTENTION_OUTCOMES = new Set(['product_failure', 'test_failure', 'environment_failure', 'needs_attention']);
51
+ /** A simulated or real outage run keeps the dot red this long unless a recovery run follows. */
52
+ const OUTAGE_HOLD_MS = 30 * 60_000;
53
+ function latestRuns(watchId, n = 10) {
54
+ return list('runs', { watchId }, { orderBy: 'id DESC', limit: n });
55
+ }
56
+ /** paused > outage > running > attention > ok. Derived from probe_state, the latest runs, and pending approvals. */
57
+ export function dotStatus(w) {
58
+ if (w.paused)
59
+ return 'paused';
60
+ const probes = list('probe_state', { watchId: w.id });
61
+ const probeDown = probes.some((s) => s.down === true ||
62
+ s.status === 'down' ||
63
+ (s.consecutiveFailures ?? 0) >= 3 ||
64
+ (typeof s.indicator === 'string' && s.indicator !== 'none' && s.indicator !== ''));
65
+ if (probeDown)
66
+ return 'outage';
67
+ const runs = latestRuns(w.id);
68
+ if (runs.some((r) => r.status === 'running'))
69
+ return 'running';
70
+ const lastIncident = runs.find((r) => r.kind === 'outage' || r.kind === 'recovery');
71
+ if (lastIncident?.kind === 'outage' && Date.now() - Date.parse(lastIncident.createdAt) < OUTAGE_HOLD_MS && lastIncident.verdict?.outcome !== 'pass')
72
+ return 'outage';
73
+ // A dot whose GitHub polling keeps failing hears nothing, so it is not all good (KAT-363).
74
+ if (liveCount(w.id) > 0 || pollFailureFor(w))
75
+ return 'attention';
76
+ // Health comes from runs that test the product. A ticket review after a failed merge used to turn
77
+ // the dot green while main was still broken (KAT-74, seen while recording the demo).
78
+ // A run that stopped because no model answered (KAT-102) concluded nothing about the product either way.
79
+ // A run that waits for the dot's daily model cap (KAT-362), or for the deploy of its merge (KAT-368), concluded nothing either.
80
+ const last = runs.find((r) => r.kind !== 'digest' &&
81
+ r.kind !== 'recovery' &&
82
+ r.kind !== 'ticket_review' &&
83
+ r.interruptReason !== 'model_unavailable' &&
84
+ r.interruptReason !== 'model_cap' &&
85
+ r.interruptReason !== 'deploy_wait');
86
+ if (last && (last.status === 'failed' || last.status === 'interrupted'))
87
+ return 'attention';
88
+ if (last?.verdict && ATTENTION_OUTCOMES.has(last.verdict.outcome))
89
+ return 'attention';
90
+ return 'ok';
91
+ }
92
+ function dotRepos(w) {
93
+ const repos = watchRepos(w);
94
+ if (!repos.length)
95
+ return [];
96
+ const last = new Map();
97
+ for (const e of list('events', { watchId: w.id, source: 'github' }, { orderBy: 'id DESC', limit: 100 })) {
98
+ const r = e.trigger?.repo;
99
+ if (!r?.owner || !r.name)
100
+ continue;
101
+ const id = repoId(r.owner, r.name);
102
+ if (!last.has(id))
103
+ last.set(id, e.createdAt);
104
+ }
105
+ return repos.map((r) => {
106
+ const ws = getWorkspaceRepo(r.id);
107
+ return {
108
+ repo: r.id,
109
+ role: r.role,
110
+ owner: r.owner,
111
+ name: r.name,
112
+ defaultBranch: r.defaultBranch,
113
+ ...(r.pathFilters ? { pathFilters: r.pathFilters } : {}),
114
+ ...(r.prScope ? { prScope: r.prScope } : {}),
115
+ ...(r.runTests === false ? { runTests: false } : {}),
116
+ ...(r.deployEnvironment ? { deployEnvironment: r.deployEnvironment } : {}),
117
+ ...(r.waitForDeploySec !== undefined ? { waitForDeploySec: r.waitForDeploySec } : {}),
118
+ ...(r.deployUrlMatch ? { deployUrlMatch: r.deployUrlMatch } : {}),
119
+ ...(r.test ? { test: r.test } : {}),
120
+ ...(r.targets ? { targets: r.targets } : {}),
121
+ scan: ws?.scan ?? null,
122
+ intake: ws?.intake ?? null,
123
+ lastEventAt: last.get(r.id) ?? ws?.lastEventAt ?? null,
124
+ };
125
+ });
126
+ }
127
+ /** Checks PR state for pending PR drafts (merged, closed, moved head), repo by repo. Throttled per dot and repo; a lookup error leaves the draft. */
128
+ async function refreshPrApprovals(watchId) {
129
+ const watches = watchId ? [get('watches', watchId)].filter((w) => !!w) : list('watches');
130
+ const jobs = [];
131
+ for (const w of watches) {
132
+ for (const r of watchRepos(w)) {
133
+ if (!pendingPrs(w.id, r.id).length)
134
+ continue;
135
+ const gh = githubFor(w, { repo: { owner: r.owner, name: r.name } });
136
+ // Demo PR payloads are fixed, so their head would expire drafts the demo just made.
137
+ if (gh.mode === 'demo')
138
+ continue;
139
+ jobs.push((async () => {
140
+ try {
141
+ await reconcileThrottled(w.id, async (pr) => {
142
+ const p = await gh.getPull(pr);
143
+ return p ? { state: p.summary.state, headSha: p.summary.headSha } : null;
144
+ }, undefined, r.id);
145
+ }
146
+ catch (err) {
147
+ log.warn(`approval PR check for ${w.name} (${r.id}) failed: ${String(err)}`);
148
+ }
149
+ })());
150
+ }
151
+ }
152
+ await Promise.all(jobs);
153
+ }
154
+ function scopeView(w) {
155
+ if (w.paused || w.archived)
156
+ return {};
157
+ const unread = scopeUnreadOf(w);
158
+ return unread ? { scopeNotApplied: unread } : {};
159
+ }
160
+ export function dotView(w) {
161
+ const lastRun = latestRuns(w.id, 1)[0] ?? null;
162
+ const withVerdict = latestRuns(w.id).find((r) => r.verdict?.headline);
163
+ return {
164
+ ...w,
165
+ ...(w.repo ? { repo: repoAsRun(w) } : {}),
166
+ repos: dotRepos(w),
167
+ status: dotStatus(w),
168
+ lastRun,
169
+ pendingApprovals: liveCount(w.id),
170
+ latestFinding: withVerdict?.verdict?.headline ?? null,
171
+ lastEventAt: list('events', { watchId: w.id, source: 'github' }, { orderBy: 'id DESC', limit: 1 })[0]?.createdAt ?? null,
172
+ trueKey: trueKeyView(),
173
+ // How the dot hears about GitHub: webhooks, polling, or neither (KAT-352).
174
+ ...deliveryOf(w),
175
+ ...modelCapOf(w),
176
+ ...scopeView(w),
177
+ // The lead that names this dot as a member (KAT-405).
178
+ ...(leadOf(w.id) ? { leadId: leadOf(w.id).id } : {}),
179
+ };
180
+ }
181
+ function modelCapOf(w) {
182
+ const modelCap = capState(w);
183
+ return modelCap ? { modelCap } : undefined;
184
+ }
185
+ const lastStatus = new Map();
186
+ /** The delivery state each dot last showed, as JSON: a change of it publishes the dot again, since the page shows it next to the status (KAT-363). */
187
+ const lastDelivery = new Map();
188
+ function broadcastStatus(watchId, force = false) {
189
+ const watches = watchId ? [get('watches', watchId)].filter((w) => !!w) : list('watches');
190
+ for (const w of watches) {
191
+ const s = dotStatus(w);
192
+ const delivery = deliveryOf(w);
193
+ const shown = JSON.stringify(delivery ?? null);
194
+ if (force || lastStatus.get(w.id) !== s || lastDelivery.get(w.id) !== shown) {
195
+ lastStatus.set(w.id, s);
196
+ lastDelivery.set(w.id, shown);
197
+ publish({ type: 'dot', watchId: w.id, status: s, delivery: delivery ?? {} });
198
+ }
199
+ }
200
+ }
201
+ let statusTimer;
202
+ /** Publishes a `dot` live event whenever a dot's derived status, or how it hears about GitHub, changes. */
203
+ export function startStatusWatcher() {
204
+ if (statusTimer)
205
+ return;
206
+ for (const w of list('watches')) {
207
+ lastStatus.set(w.id, dotStatus(w));
208
+ lastDelivery.set(w.id, JSON.stringify(deliveryOf(w) ?? null));
209
+ }
210
+ subscribe((e) => {
211
+ // Another module may publish its own view of a dot; remember it so the next poll corrects drift.
212
+ if (e.type === 'dot')
213
+ lastStatus.set(e.watchId, e.status);
214
+ else if (e.type === 'run')
215
+ broadcastStatus(e.run.watchId);
216
+ else if (e.type === 'approval')
217
+ broadcastStatus(e.approval.watchId);
218
+ });
219
+ // probe_state changes do not go through the bus, so poll.
220
+ statusTimer = setInterval(() => broadcastStatus(), 5000);
221
+ statusTimer.unref?.();
222
+ }
223
+ // VAPID
224
+ let vapid;
225
+ /** Env keys win; else data/vapid.json; else generate and save there. Call at boot so push.ts finds the file. */
226
+ export function ensureVapidKeys() {
227
+ if (vapid)
228
+ return vapid;
229
+ if (config.vapid.publicKey && config.vapid.privateKey) {
230
+ vapid = { publicKey: config.vapid.publicKey, privateKey: config.vapid.privateKey };
231
+ return vapid;
232
+ }
233
+ const file = join(config.dataDir, 'vapid.json');
234
+ if (existsSync(file)) {
235
+ try {
236
+ const v = JSON.parse(readFileSync(file, 'utf8'));
237
+ if (v.publicKey && v.privateKey)
238
+ return (vapid = { publicKey: v.publicKey, privateKey: v.privateKey });
239
+ }
240
+ catch {
241
+ log.warn(`${file} is unreadable, generating new keys`);
242
+ }
243
+ }
244
+ const v = webpush.generateVAPIDKeys();
245
+ mkdirSync(config.dataDir, { recursive: true });
246
+ writeFileSync(file, JSON.stringify(v, null, 2), { mode: 0o600 });
247
+ log.info(`generated VAPID keys into ${file}`);
248
+ return (vapid = v);
249
+ }
250
+ // SSE
251
+ function openSse(reply) {
252
+ reply.hijack();
253
+ const res = reply.raw;
254
+ res.writeHead(200, {
255
+ 'content-type': 'text/event-stream; charset=utf-8',
256
+ 'cache-control': 'no-cache, no-transform',
257
+ connection: 'keep-alive',
258
+ 'x-accel-buffering': 'no',
259
+ });
260
+ res.write(': connected\n\n');
261
+ return {
262
+ raw: res,
263
+ send(event, data) {
264
+ if (res.writableEnded || res.destroyed)
265
+ return;
266
+ res.write(`${event ? `event: ${event}\n` : ''}data: ${JSON.stringify(data)}\n\n`);
267
+ },
268
+ comment(text) {
269
+ if (!res.writableEnded && !res.destroyed)
270
+ res.write(`: ${text}\n\n`);
271
+ },
272
+ end() {
273
+ if (!res.writableEnded)
274
+ res.end();
275
+ },
276
+ };
277
+ }
278
+ // Routes
279
+ const WATCH_FIELDS = [
280
+ 'name',
281
+ 'mission',
282
+ 'notifyWhen',
283
+ 'mustNot',
284
+ 'repo',
285
+ 'tracker',
286
+ 'baseUrl',
287
+ 'probes',
288
+ 'statusPages',
289
+ 'probeIntervalSec',
290
+ 'runner',
291
+ 'rules',
292
+ 'digestHour',
293
+ 'digestEvery',
294
+ 'paused',
295
+ // API v2: role presets, instructions, mascot, MCP connections, Slack channel. The archive flag is not here: DELETE and restore own it.
296
+ 'role',
297
+ 'instructions',
298
+ 'mascot',
299
+ 'connections',
300
+ 'slackChannel',
301
+ // PR review options (docs/PR-REVIEW-QA.md section 3.1). PUT /api/dots/:id/review checks each value.
302
+ 'review',
303
+ // Multiple repos (docs/MULTI-REPO.md section 1.5). repo and repos are checked by applyRepoFields.
304
+ 'repos',
305
+ 'waitForPreviewSec',
306
+ // A merge run waits for the deploy of the merge to this GitHub environment (KAT-368). Checked by deployWaitError, and the admin's like the runner settings.
307
+ 'waitForDeploySec',
308
+ 'deployEnvironment',
309
+ // What a PR check tests against when no preview of the PR is known (KAT-363). Checked by prTestTargetError.
310
+ 'prTestTarget',
311
+ // Run the Tests repo on app PRs and on merges (KAT-86). Each is true, false, or null for the default (on). Checked by testRepoSwitchError.
312
+ 'testRepoOnPr',
313
+ 'testRepoOnMerge',
314
+ // Nightly test run of the Tests repo suite (KAT-390). Checked by nightlyError and settleNightlySave; nightlyLastSlot is the server's.
315
+ 'nightlyRun',
316
+ // Where the dot's updates go in Slack (KAT-143). Checked by checkUpdates.
317
+ 'updates',
318
+ // Branches besides the default branch that run post-merge tests (KAT-292). Checked by testBranchesError.
319
+ 'testBranches',
320
+ // Stage switches and rules (docs/STAGES.md section 5, KAT-161). Checked by stagesError.
321
+ 'stages',
322
+ // Model per stage (KAT-162). Checked by checkStageModels.
323
+ 'stageModels',
324
+ // The one-time offer of the new stages (KAT-171). A save may only set it to dismissed; checked by stagesNoticeError.
325
+ 'stagesNotice',
326
+ // Fewer test runs (KAT-344). Checked by testSavingsError.
327
+ 'testDocsOnlySkip',
328
+ 'testDebounceSec',
329
+ 'reuseMergeResults',
330
+ // Poll GitHub for this dot (KAT-352). POST /api/dots sets it when the server has no webhook route.
331
+ 'pollGithub',
332
+ // Model calls per day (KAT-362). Checked by modelCapError; 0 and null turn the cap off.
333
+ 'dailyModelCap',
334
+ // Send this dot's cases, runs and results to True Platform (KAT-406). Checked by trueSyncError, and the admin's like the runner settings.
335
+ 'trueSync',
336
+ // A Lead QA dot and its member dots (KAT-405). Checked by leadError; only POST sets `kind`.
337
+ 'kind',
338
+ 'memberIds',
339
+ ];
340
+ export const SIMULATE_KINDS = ['pr_merged', 'pr_opened', 'pr_synchronize', 'pr_comment_edited', 'ticket_created', 'outage', 'ci_failure'];
341
+ function pickWatchFields(body) {
342
+ const out = {};
343
+ for (const k of WATCH_FIELDS)
344
+ if (k in body)
345
+ out[k] = body[k];
346
+ return out;
347
+ }
348
+ /** runner.copyFiles and runner.passEnv are saved trimmed and without repeats. Call it after validateRunner has checked every entry. */
349
+ function tidyRunner(fields) {
350
+ const runner = fields.runner;
351
+ if (!runner || typeof runner !== 'object' || Array.isArray(runner))
352
+ return;
353
+ const r = runner;
354
+ const tidy = {};
355
+ if (Array.isArray(r.copyFiles))
356
+ tidy.copyFiles = cleanCopyFiles(r.copyFiles);
357
+ if (Array.isArray(r.passEnv))
358
+ tidy.passEnv = cleanPassEnv(r.passEnv);
359
+ if (Object.keys(tidy).length)
360
+ fields.runner = { ...runner, ...tidy };
361
+ }
362
+ /**
363
+ * Checks `repo` and `repos` in a save and rewrites both (docs/MULTI-REPO.md section 1.2): `repos`
364
+ * wins and `repo` mirrors its first app entry; a body with only `repo` edits the first app entry.
365
+ * New repos join the workspace. Returns the status and error to answer, or null.
366
+ */
367
+ async function applyRepoFields(fields, current) {
368
+ const res = resolveRepoFields(fields, current);
369
+ if (!res.ok)
370
+ return { code: 400, error: res.error };
371
+ // A repo that waits for a deploy needs an environment, its own or the dot's, as this save leaves them (KAT-395).
372
+ const dotWait = {
373
+ waitForDeploySec: 'waitForDeploySec' in fields ? fields.waitForDeploySec : current?.waitForDeploySec,
374
+ deployEnvironment: 'deployEnvironment' in fields ? fields.deployEnvironment : current?.deployEnvironment,
375
+ };
376
+ const waitError = repoDeployWaitError(dotWait, res.fields ? watchRepos({ repos: res.fields.repos }) : current ? watchRepos(current) : []);
377
+ if (waitError)
378
+ return { code: 400, error: waitError };
379
+ if (!res.fields)
380
+ return null;
381
+ const err = await ensureReposForSave(res.fields.repos);
382
+ if (err)
383
+ return err;
384
+ fields.repos = res.fields.repos;
385
+ // A repo added above joined the workspace with its own default branch, which the mirror reads.
386
+ fields.repo = repoMirror(res.fields.repos) ?? (current ? null : undefined);
387
+ if (fields.repo === undefined)
388
+ delete fields.repo;
389
+ return null;
390
+ }
391
+ /** Checks `updates` in a dot save: slack channel, dm, or off, and a member ID for dm. */
392
+ function checkDigestEvery(v) {
393
+ return v === undefined || v === null || v === 'day' || v === 'week' ? null : 'digestEvery must be day or week.';
394
+ }
395
+ function checkUpdates(v) {
396
+ if (v === undefined || v === null)
397
+ return null;
398
+ if (typeof v !== 'object')
399
+ return 'updates must be an object.';
400
+ const u = v;
401
+ if (u.slack !== undefined && u.slack !== 'channel' && u.slack !== 'dm' && u.slack !== 'off')
402
+ return 'updates.slack must be channel, dm, or off.';
403
+ if (u.slackUser !== undefined && u.slackUser !== '' && (typeof u.slackUser !== 'string' || !/^[UW][A-Z0-9]{2,20}$/.test(u.slackUser)))
404
+ return 'updates.slackUser must be a Slack member ID like U0123ABCD.';
405
+ if (u.slack === 'dm' && !u.slackUser)
406
+ return 'A direct message needs a Slack member ID.';
407
+ return null;
408
+ }
409
+ /**
410
+ * Checks `stageModels` in a dot save (KAT-162): an object of stage key to a model id from the switcher list, or null
411
+ * to clear a stage. Returns the message to answer, or null.
412
+ */
413
+ function checkStageModels(v) {
414
+ if (v === undefined || v === null)
415
+ return null;
416
+ if (typeof v !== 'object' || Array.isArray(v))
417
+ return 'stageModels must be an object of stage to model.';
418
+ for (const [stage, id] of Object.entries(v)) {
419
+ if (!STAGE_KEYS.includes(stage))
420
+ return `stageModels: "${stage}" is not a stage. Use ${STAGE_KEYS.join(', ')}.`;
421
+ if (id === null)
422
+ continue;
423
+ if (typeof id !== 'string' || !STAGE_MODEL_IDS.includes(id))
424
+ return `stageModels.${stage}: "${String(id)}" is not a model. Use ${STAGE_MODEL_IDS.join(', ')}, or null for the default.`;
425
+ }
426
+ return null;
427
+ }
428
+ /** The saved stageModels: the sent stages laid over the dot's own, with cleared (null) stages removed. */
429
+ function mergeStageModels(current, sent) {
430
+ const out = {};
431
+ for (const [k, v] of Object.entries({ ...current, ...sent }))
432
+ if (typeof v === 'string')
433
+ out[k] = v;
434
+ return out;
435
+ }
436
+ function checkPollGithub(v) {
437
+ return v === undefined || typeof v === 'boolean' ? null : 'pollGithub must be true or false.';
438
+ }
439
+ function checkWait(v) {
440
+ if (v === undefined || v === null)
441
+ return null;
442
+ return typeof v === 'number' && Number.isInteger(v) && v >= 0 && v <= 3600 ? null : 'waitForPreviewSec must be a whole number of seconds from 0 to 3600.';
443
+ }
444
+ /** The deploy wait fields as a save stores them: the environment trimmed, and a wait of 0 or an empty environment as null. Call it after deployWaitError. */
445
+ function tidyDeployWait(fields) {
446
+ if ('deployEnvironment' in fields)
447
+ fields.deployEnvironment = cleanDeployEnvironment(fields.deployEnvironment) ?? null;
448
+ if ('waitForDeploySec' in fields)
449
+ fields.waitForDeploySec = cleanDeploySec(fields.waitForDeploySec) ?? null;
450
+ }
451
+ function dotName(watchId, cache) {
452
+ if (!cache.has(watchId))
453
+ cache.set(watchId, get('watches', watchId)?.name ?? '');
454
+ return cache.get(watchId) || undefined;
455
+ }
456
+ function clampLimit(v, def, max = 200) {
457
+ const n = Number(v);
458
+ return Number.isFinite(n) && n > 0 ? Math.min(Math.floor(n), max) : def;
459
+ }
460
+ export async function registerApi(app) {
461
+ app.get('/api/health', async () => ({
462
+ ok: true,
463
+ // The package.json version and the commit this build came from (null when the host does not say).
464
+ version: VERSION,
465
+ commit: commitSha(),
466
+ demo: config.demo,
467
+ sandbox: config.sandbox,
468
+ ...(config.sandbox ? { sandboxBanner: SANDBOX_BANNER } : {}),
469
+ llm: llmBackend(),
470
+ dots: count('watches'),
471
+ // The clock of this server: the nightly test run's time is on it (KAT-390). The offset is minutes behind UTC, as Date.getTimezoneOffset gives it.
472
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || null,
473
+ tzOffsetMin: new Date().getTimezoneOffset(),
474
+ // TinyBee only (docs/tinybee/05-mobile-app-ux.md section 10): budget, connections, update and API version.
475
+ ...(hiveMode() !== 'off' ? { tinybee: tinybeeHealth() } : {}),
476
+ }));
477
+ // Dots
478
+ // Archived dots stay readable by id (runs, reports and chat keep their dot), but the list hides them unless ?archived=1.
479
+ app.get('/api/dots', async (req) => {
480
+ const withArchived = req.query.archived === '1' || req.query.archived === 'true';
481
+ return list('watches')
482
+ .filter((w) => withArchived || !w.archived)
483
+ .map(dotView);
484
+ });
485
+ app.get('/api/dots/:id', async (req, reply) => {
486
+ const w = get('watches', req.params.id);
487
+ if (!w)
488
+ return reply.code(404).send({ error: 'dot not found' });
489
+ return dotView(w);
490
+ });
491
+ app.post('/api/dots', async (req, reply) => {
492
+ const body = req.body ?? {};
493
+ if (typeof body.name !== 'string' || !body.name.trim())
494
+ return reply.code(400).send({ error: 'name is required' });
495
+ const fields = pickWatchFields(body);
496
+ const leadFail = leadError(fields);
497
+ if (leadFail)
498
+ return reply.code(leadFail.code).send({ error: leadFail.error });
499
+ const makingLead = fields.kind === 'lead';
500
+ const runnerError = validateRunner(fields.runner) ?? testBranchesError(fields.testBranches) ?? trackerScopeError(fields.tracker);
501
+ if (runnerError)
502
+ return reply.code(400).send({ error: runnerError });
503
+ if ('testBranches' in fields)
504
+ fields.testBranches = cleanTestBranches(fields.testBranches);
505
+ if ('tracker' in fields)
506
+ fields.tracker = tidyTrackerScope(fields.tracker);
507
+ tidyRunner(fields);
508
+ const waitError = trueSyncError(fields.trueSync) ??
509
+ checkWait(fields.waitForPreviewSec) ??
510
+ deployWaitError(fields) ??
511
+ checkPollGithub(fields.pollGithub) ??
512
+ checkUpdates(fields.updates) ??
513
+ checkDigestEvery(fields.digestEvery) ??
514
+ stagesError(fields.stages) ??
515
+ stagesNoticeError(fields.stagesNotice) ??
516
+ checkStageModels(fields.stageModels) ??
517
+ prTestTargetError(fields.prTestTarget) ??
518
+ testRepoSwitchError(fields) ??
519
+ nightlyError(fields.nightlyRun) ??
520
+ testSavingsError(fields) ??
521
+ modelCapError(fields.dailyModelCap);
522
+ if (waitError)
523
+ return reply.code(400).send({ error: waitError });
524
+ if ('nightlyRun' in fields)
525
+ fields.nightlyRun = tidyNightly(fields.nightlyRun);
526
+ if ('dailyModelCap' in fields)
527
+ fields.dailyModelCap = capOf(fields) ?? null;
528
+ if ('trueSync' in fields)
529
+ fields.trueSync = fields.trueSync === null ? undefined : mergeTrueSync(undefined, fields.trueSync);
530
+ tidyDeployWait(fields);
531
+ // Shadow week (KAT-357): the days come in `shadowWeekDays`; the saved levels and the end date are the server's.
532
+ const shadow = parseShadowDays(body.shadowWeekDays);
533
+ if (!shadow.ok)
534
+ return reply.code(400).send({ error: shadow.error });
535
+ if (shadow.days) {
536
+ const started = startShadowWeek({ rules: fields.rules }, shadow.days);
537
+ fields.rules = started.rules;
538
+ if (started.shadowWeek)
539
+ fields.shadowWeek = started.shadowWeek;
540
+ }
541
+ if (fields.stages)
542
+ fields.stages = mergeStages(undefined, fields.stages);
543
+ if ('stageModels' in fields)
544
+ fields.stageModels = mergeStageModels(undefined, fields.stageModels);
545
+ let reviewPatch;
546
+ if ('review' in fields) {
547
+ reviewPatch = parseReviewPatch(fields.review);
548
+ if (!reviewPatch.ok)
549
+ return reply.code(400).send({ error: `review: ${reviewPatch.error}` });
550
+ }
551
+ // Everything above is checked without writing. Resolving the repos is last: it reads GitHub for
552
+ // all of them and adds them to the workspace only when every one passes (KAT-106).
553
+ const repoError = await applyRepoFields(fields);
554
+ if (repoError)
555
+ return reply.code(repoError.code).send({ error: repoError.error });
556
+ const nightlyRefused = settleNightlySave(undefined, fields);
557
+ if (nightlyRefused)
558
+ return reply.code(400).send({ error: nightlyRefused });
559
+ if (reviewPatch?.ok)
560
+ fields.review = effectiveReviewOptions({ repo: fields.repo, repos: fields.repos, review: reviewPatch.patch });
561
+ // KAT-352: with no webhook route (no public URL or no webhook secret) nothing would tell this dot about a PR, so it polls.
562
+ // An explicit pollGithub in the body is the owner's choice and stays.
563
+ if (fields.pollGithub === undefined && !webhookRoute().ok && !makingLead)
564
+ fields.pollGithub = true;
565
+ // A lead has nothing to derive stages from, so every stage is saved off: a dot with no stage entry runs every stage but the guard.
566
+ if (makingLead) {
567
+ fields.stages = leadStages();
568
+ }
569
+ // A dot made now has nothing to be offered later: the boot migration offers the new stages only to dots that predate them.
570
+ const w = createWatch({ ...NO_STAGE_OFFER, ...fields, name: body.name.trim() });
571
+ if (w.trueSync)
572
+ afterTrueSyncSave(w.id, undefined, w.trueSync);
573
+ if (makingLead) {
574
+ forgetLeadIndex();
575
+ for (const id of w.memberIds ?? [])
576
+ broadcastStatus(id, true);
577
+ }
578
+ broadcastStatus(w.id, makingLead);
579
+ return reply.code(201).send(dotView(w));
580
+ });
581
+ app.patch('/api/dots/:id', async (req, reply) => {
582
+ const w = get('watches', req.params.id);
583
+ if (!w)
584
+ return reply.code(404).send({ error: 'dot not found' });
585
+ const patch = { ...pickWatchFields(req.body ?? {}), updatedAt: new Date().toISOString() };
586
+ if (w.archived && 'paused' in patch)
587
+ return reply.code(409).send({ error: `${w.name} is archived. Restore it before you pause or resume it.` });
588
+ const leadFail = leadError(patch, w);
589
+ if (leadFail)
590
+ return reply.code(leadFail.code).send({ error: leadFail.error });
591
+ // A save that sends the kind it has changes nothing, so it is not written.
592
+ delete patch.kind;
593
+ // What runs as the server user is the admin's (KAT-364): an editor's save may not change the test command, setup, passed
594
+ // variables, copied files, image, mode, or folders of the dot or of a test repo. Which code a run checks out is too (KAT-369):
595
+ // the repos, their branches, and the test branches. Checked before anything is read or written.
596
+ if (roleOf(req.qdCaller) !== 'admin') {
597
+ const body = req.body ?? {};
598
+ // The saved list as stored: the dot's resolved repos name each repo `id`, and this check reads `repo`.
599
+ const changed = runnerAdminChanges({ runner: w.runner, repos: w.repos }, body, {
600
+ runner: 'runner' in body,
601
+ repos: 'repos' in body && body.repos !== undefined,
602
+ });
603
+ // The deploy wait decides when a run starts on the server, so it is the admin's with the runner settings (KAT-368).
604
+ const deploy = [...deployAdminChanges(w, body), ...repoDeployChanges(w, body)];
605
+ if (changed.length || deploy.length)
606
+ return reply
607
+ .code(403)
608
+ .send({ error: changed.length ? RUNNER_ADMIN_MESSAGE : DEPLOY_ADMIN_MESSAGE, code: RUNNER_ADMIN_CODE, fields: [...changed, ...deploy] });
609
+ const moved = repoAdminChanges(w, body);
610
+ if (moved.length)
611
+ return reply.code(403).send({ error: REPO_ADMIN_MESSAGE, code: REPO_ADMIN_CODE, fields: moved });
612
+ // Where the server sends the True Platform key is the admin's too: the switch, the address and the project (KAT-406).
613
+ const trueFields = trueSyncAdminChanges(w, body);
614
+ if (trueFields.length)
615
+ return reply.code(403).send({ error: TRUE_ADMIN_MESSAGE, code: TRUE_ADMIN_CODE, fields: trueFields });
616
+ }
617
+ if (patch.rules && typeof patch.rules === 'object')
618
+ patch.rules = { ...w.rules, ...patch.rules };
619
+ const runnerError = validateRunner(patch.runner) ?? testBranchesError(patch.testBranches) ?? trackerScopeError(patch.tracker);
620
+ if (runnerError)
621
+ return reply.code(400).send({ error: runnerError });
622
+ if ('testBranches' in patch)
623
+ patch.testBranches = cleanTestBranches(patch.testBranches);
624
+ if ('tracker' in patch)
625
+ patch.tracker = tidyTrackerScope(patch.tracker);
626
+ tidyRunner(patch);
627
+ const waitError = trueSyncError(patch.trueSync, w.trueSync) ??
628
+ checkWait(patch.waitForPreviewSec) ??
629
+ deployWaitError(patch, w) ??
630
+ checkPollGithub(patch.pollGithub) ??
631
+ checkUpdates(patch.updates) ??
632
+ checkDigestEvery(patch.digestEvery) ??
633
+ stagesError(patch.stages) ??
634
+ stagesNoticeError(patch.stagesNotice) ??
635
+ checkStageModels(patch.stageModels) ??
636
+ prTestTargetError(patch.prTestTarget) ??
637
+ testRepoSwitchError(patch) ??
638
+ nightlyError(patch.nightlyRun) ??
639
+ testSavingsError(patch) ??
640
+ modelCapError(patch.dailyModelCap);
641
+ if ('nightlyRun' in patch && !waitError)
642
+ patch.nightlyRun = tidyNightly(patch.nightlyRun);
643
+ if ('dailyModelCap' in patch && !waitError)
644
+ patch.dailyModelCap = capOf(patch) ?? null;
645
+ if ('trueSync' in patch && !waitError)
646
+ patch.trueSync = patch.trueSync === null ? null : mergeTrueSync(w.trueSync, patch.trueSync);
647
+ if (!waitError)
648
+ tidyDeployWait(patch);
649
+ if ('stageModels' in patch)
650
+ patch.stageModels = patch.stageModels === null ? {} : mergeStageModels(w.stageModels, patch.stageModels);
651
+ if (waitError)
652
+ return reply.code(400).send({ error: waitError });
653
+ // Shadow week (KAT-357). A rule the save changes by hand leaves the shadow week (a level sent as stored does not); `shadowWeekDays` starts, extends (1 to 30) or ends (0) it.
654
+ const shadow = parseShadowDays(req.body?.shadowWeekDays);
655
+ if (!shadow.ok)
656
+ return reply.code(400).send({ error: shadow.error });
657
+ if (w.shadowWeek && req.body?.rules && typeof req.body.rules === 'object') {
658
+ patch.shadowWeek = forgetShadowKinds(w.shadowWeek, changedRuleKinds(w.rules, req.body.rules));
659
+ }
660
+ if (shadow.days === 0) {
661
+ patch.rules = endShadowWeek({
662
+ rules: patch.rules ?? w.rules,
663
+ shadowWeek: patch.shadowWeek ?? w.shadowWeek,
664
+ });
665
+ patch.shadowWeek = null;
666
+ }
667
+ else if (shadow.days) {
668
+ const started = startShadowWeek({ rules: patch.rules ?? w.rules, shadowWeek: patch.shadowWeek ?? w.shadowWeek }, shadow.days);
669
+ patch.rules = started.rules;
670
+ patch.shadowWeek = started.shadowWeek;
671
+ }
672
+ if (patch.stages) {
673
+ patch.stages = mergeStages(w, patch.stages);
674
+ // A save of stages is a choice, so a dot that was never offered the new stages is not offered them at the next boot.
675
+ // An open offer stays: the dialog answers it, by the notice or by the switches.
676
+ if (!w.stagesNotice)
677
+ patch.stagesNotice = NO_STAGE_OFFER.stagesNotice;
678
+ resetWatcherNotes(w.id);
679
+ }
680
+ let reviewPatch;
681
+ if ('review' in patch) {
682
+ reviewPatch = parseReviewPatch(patch.review);
683
+ if (!reviewPatch.ok)
684
+ return reply.code(400).send({ error: `review: ${reviewPatch.error}` });
685
+ }
686
+ // Checks above write nothing; the remote repo reads come last and add to the workspace only when all pass (KAT-106).
687
+ const repoError = await applyRepoFields(patch, w);
688
+ if (repoError)
689
+ return reply.code(repoError.code).send({ error: repoError.error });
690
+ const nightlyRefused = settleNightlySave(w, patch);
691
+ if (nightlyRefused)
692
+ return reply.code(400).send({ error: nightlyRefused });
693
+ // The defaults come from the dot as this save leaves it: a repo added in the same save turns review on (KAT-171).
694
+ if (reviewPatch?.ok) {
695
+ const after = {
696
+ repo: 'repo' in patch ? (patch.repo ?? undefined) : w.repo,
697
+ repos: 'repos' in patch ? patch.repos : w.repos,
698
+ review: w.review,
699
+ };
700
+ patch.review = { ...effectiveReviewOptions(after), ...reviewPatch.patch };
701
+ }
702
+ // KAT-349: the migration wrote each stage once from the targets the dot had then. A save that adds the first target of a
703
+ // stage (a probe, a tracker, a repo) turns that stage on when the migration switched it off, unless the save sends
704
+ // `stages` itself. A stage the owner switched off stays off (KAT-351).
705
+ if (!patch.stages) {
706
+ const before = derivedStages(w);
707
+ const after = derivedStages({ ...w, ...patch });
708
+ const gained = {};
709
+ for (const k of STAGE_KEYS)
710
+ if (!before[k].on && after[k].on && w.stages?.[k]?.on === false && w.stages[k].derived)
711
+ gained[k] = { on: true };
712
+ if (Object.keys(gained).length) {
713
+ patch.stages = mergeStages(w, gained);
714
+ resetWatcherNotes(w.id);
715
+ }
716
+ }
717
+ update('watches', w.id, patch);
718
+ const saved = get('watches', w.id);
719
+ // A dot switched on queues its cases; one moved to another project forgets what it sent there (KAT-406).
720
+ if ('trueSync' in patch && saved.trueSync)
721
+ afterTrueSyncSave(w.id, w.trueSync, saved.trueSync);
722
+ // A save of `memberIds` publishes a dot event for the lead and for every member added or removed, which drops their kept health
723
+ // answers and the lead index (KAT-405).
724
+ if ('memberIds' in patch) {
725
+ forgetLeadIndex();
726
+ for (const id of memberChanges(w.memberIds, saved.memberIds))
727
+ broadcastStatus(id, true);
728
+ }
729
+ // A cap that went up or off lets the reviews that waited for the reset start now (KAT-362).
730
+ if ('dailyModelCap' in patch)
731
+ releaseModelCapHolds(saved);
732
+ // A merge run that waits for a deploy looks again under the new setting: off ends the wait, another environment is looked at. A pause
733
+ // ends it too, so the run stops now and not at the next look, which a dot on webhooks makes up to 5 min away (KAT-368).
734
+ if ('waitForDeploySec' in patch || 'deployEnvironment' in patch || 'repos' in patch || 'repo' in patch || patch.paused === true)
735
+ releaseDeployWaits(saved);
736
+ // A saved setting changes what the health panel can show (a site check, a tracker, a daily cap) without changing the status, so
737
+ // the dot is published every time: the kept health answer drops and every open page reads it again (KAT-394).
738
+ broadcastStatus(w.id, true);
739
+ return dotView(saved);
740
+ });
741
+ // Calls and tokens of the dot today and over the last 7 days, per stage, and where it stands against its cap (KAT-362).
742
+ app.get('/api/dots/:id/model-usage', async (req, reply) => {
743
+ const w = get('watches', req.params.id);
744
+ if (!w)
745
+ return reply.code(404).send({ error: 'dot not found' });
746
+ return usageOf(w);
747
+ });
748
+ // DELETE removes a dot (docs/API-V2.md). mode=archive (the default) stops it and keeps its history, so a
749
+ // restore brings it back. mode=delete removes the dot and everything that belongs to it, and needs the dot
750
+ // name in confirmName. Both close the dot's pending approvals as rejected. A paired phone may archive
751
+ // (mode=archive given explicitly) and restore, which are reversible; mode=delete stays on the computer
752
+ // (src/phone/auth.ts lists the writes a phone may make).
753
+ app.delete('/api/dots/:id', async (req, reply) => {
754
+ const w = get('watches', req.params.id);
755
+ if (!w)
756
+ return reply.code(404).send({ error: 'dot not found' });
757
+ const mode = parseRemoveMode(req.query.mode);
758
+ if (!mode)
759
+ return reply.code(400).send({ error: 'mode must be archive or delete.' });
760
+ if (mode === 'delete') {
761
+ if (!nameMatches(w, req.query.confirmName))
762
+ return reply.code(400).send({ error: `Type the dot name, ${w.name}, to delete it permanently.` });
763
+ // A lead always keeps 1 to 8 members, so the last one cannot go first (KAT-405).
764
+ const only = onlyMemberRefusal(w);
765
+ if (only)
766
+ return reply.code(409).send({ error: only });
767
+ const summary = await deleteDot(w);
768
+ lastStatus.delete(w.id);
769
+ lastDelivery.delete(w.id);
770
+ // The leads that lost this member show their new member list (KAT-405).
771
+ for (const id of summary.leadsChanged)
772
+ broadcastStatus(id, true);
773
+ return { ok: true, deleted: true, ...summary };
774
+ }
775
+ const summary = await archiveDot(w);
776
+ lastStatus.delete(w.id);
777
+ lastDelivery.delete(w.id);
778
+ broadcastStatus(w.id);
779
+ return { ok: true, archived: true, ...summary };
780
+ });
781
+ // What a permanent delete also takes from the team: test run items of other members that point at this dot's cases, and whether this
782
+ // is the only dot of a lead, which refuses the delete (KAT-405). The confirm dialog reads it before it asks for the name.
783
+ app.get('/api/dots/:id/delete-preview', async (req, reply) => {
784
+ const w = get('watches', req.params.id);
785
+ if (!w)
786
+ return reply.code(404).send({ error: 'dot not found' });
787
+ return deletePreview(w);
788
+ });
789
+ app.post('/api/dots/:id/restore', async (req, reply) => {
790
+ const w = get('watches', req.params.id);
791
+ if (!w)
792
+ return reply.code(404).send({ error: 'dot not found' });
793
+ if (!w.archived)
794
+ return reply.code(409).send({ error: `${w.name} is not archived.` });
795
+ const back = restoreDot(w);
796
+ broadcastStatus(back.id);
797
+ return dotView(back);
798
+ });
799
+ // Feed and runs
800
+ app.get('/api/feed', async (req) => {
801
+ const { dotId, kind } = req.query;
802
+ const names = new Map();
803
+ return list('runs', { watchId: dotId || undefined, kind: kind || undefined }, {
804
+ orderBy: 'id DESC',
805
+ limit: clampLimit(req.query.limit, 50),
806
+ offset: clampLimit(req.query.offset, 0, 100_000),
807
+ }).map((r) => ({ ...r, stage: runStage(r), dotName: dotName(r.watchId, names) }));
808
+ });
809
+ app.get('/api/runs/:id', async (req, reply) => {
810
+ const run = get('runs', req.params.id);
811
+ if (!run)
812
+ return reply.code(404).send({ error: 'run not found' });
813
+ const watch = get('watches', run.watchId);
814
+ return {
815
+ ...run,
816
+ stage: runStage(run),
817
+ dotName: watch?.name,
818
+ summary: safeRunSummary(run, watch?.review?.showFrom),
819
+ // The run page links to its evidence pack when the run names an issue and left a screenshot or a video (KAT-392).
820
+ evidencePack: hasEvidencePack(run),
821
+ approvals: list('approvals', { runId: run.id }),
822
+ outbox: list('outbox', { runId: run.id }),
823
+ };
824
+ });
825
+ // Approvals
826
+ app.get('/api/approvals/stats', async (req) => {
827
+ const days = Math.min(365, Math.max(1, Number(req.query.days) || 30));
828
+ return approvalStats({ sinceIso: new Date(Date.now() - days * 86_400_000).toISOString(), watchId: req.query.dotId || undefined });
829
+ });
830
+ app.get('/api/approvals', async (req) => {
831
+ const names = new Map();
832
+ // Inbox load: close drafts past expiresAt, and check the PRs behind pending drafts at most once a minute per dot.
833
+ sweepTimeExpired();
834
+ // A slow GitHub never holds the Inbox for long: late results arrive as live approval events.
835
+ await Promise.race([refreshPrApprovals(req.query.dotId || undefined), new Promise((r) => setTimeout(r, 4000).unref?.())]);
836
+ const rows = list('approvals', { status: req.query.status || undefined, watchId: req.query.dotId || undefined }, { orderBy: 'id DESC', limit: clampLimit(req.query.limit, 100) }).map((a) => {
837
+ const run = get('runs', a.runId);
838
+ const stage = run ? runStage(run) : undefined;
839
+ // A bug report that two members of a lead found is one item that names both (KAT-405).
840
+ const foundBy = foundByLine(a, (id) => dotName(id, names) ?? 'another dot');
841
+ const sources = foundBy
842
+ ? sourcesOf(a).map((s) => ({ dotName: dotName(s.watchId, names) ?? 'another dot', runId: s.runId, at: s.at, bugSig: s.bugSig }))
843
+ : undefined;
844
+ return { ...a, dotName: dotName(a.watchId, names), ...(stage ? { stage } : {}), ...(foundBy ? { foundBy, sources } : {}) };
845
+ });
846
+ // targetTitle and targetUrl: the ticket, PR, or commit title the Inbox shows as a chip.
847
+ return withTargetTitles(rows);
848
+ });
849
+ app.post('/api/approvals/:id', async (req, reply) => {
850
+ const a = get('approvals', req.params.id);
851
+ if (!a)
852
+ return reply.code(404).send({ error: 'approval not found' });
853
+ if (approvalExpired(a)) {
854
+ if (a.status === 'pending') {
855
+ closeApproval(a, 'expired', 'Nobody decided before it expired.');
856
+ }
857
+ return reply.code(410).send({ error: 'approval expired' });
858
+ }
859
+ if (a.status !== 'pending')
860
+ return reply.code(409).send({ error: `approval is already ${a.status}` });
861
+ const { decision, edit } = req.body ?? {};
862
+ // A status change runs from the option a person picked on the card (meta.optionId), never from its body, so an edited body would be
863
+ // dropped and the ticket would move to the status that was first picked. Refuse it in words instead.
864
+ if (decision === 'approve' && a.action.kind === 'ticket_status' && typeof edit === 'string' && edit.trim())
865
+ return reply.code(400).send({ error: 'A status change cannot be edited. Reject it, then pick another status on the card.' });
866
+ try {
867
+ // A paired device decides under its name (KAT-356); the shared admin token and a local browser name no one.
868
+ const by = approverOf(req);
869
+ if (decision === 'approve')
870
+ await approveAction(a.id, typeof edit === 'string' && edit.trim() ? edit : undefined, by ? { by } : {});
871
+ else if (decision === 'reject')
872
+ await rejectAction(a.id, undefined, by ? { by } : {});
873
+ else
874
+ return reply.code(400).send({ error: "decision must be 'approve' or 'reject'" });
875
+ }
876
+ catch (err) {
877
+ // A send that failed for a temporary reason is retried by the server; a person waits for the backoff to end (KAT-178).
878
+ if (err instanceof RetryingError)
879
+ return reply.code(409).send({ error: err.message, code: 'retrying', nextAt: err.nextAt });
880
+ log.error(`approval ${a.id} ${decision} failed`, err);
881
+ return reply.code(502).send({ error: String(err instanceof Error ? err.message : err) });
882
+ }
883
+ // rules.ts publishes the approval live event.
884
+ return get('approvals', a.id);
885
+ });
886
+ // Memory
887
+ app.get('/api/memory', async (req) => list('memory', { watchId: req.query.dotId || undefined }, { orderBy: 'id DESC' }));
888
+ app.post('/api/memory', async (req, reply) => {
889
+ const { text, kind } = req.body ?? {};
890
+ const watchId = req.body?.dotId ?? req.body?.watchId;
891
+ if (!watchId || !get('watches', watchId))
892
+ return reply.code(400).send({ error: 'dotId must name an existing dot' });
893
+ if (typeof text !== 'string' || !text.trim())
894
+ return reply.code(400).send({ error: 'text is required' });
895
+ const kinds = ['flaky_spec', 'env_quirk', 'convention', 'decision', 'note'];
896
+ const item = insert('memory', { watchId, kind: kind && kinds.includes(kind) ? kind : 'note', text: text.trim(), source: 'user' });
897
+ return reply.code(201).send(item);
898
+ });
899
+ app.delete('/api/memory/:id', async (req, reply) => {
900
+ if (!remove('memory', req.params.id))
901
+ return reply.code(404).send({ error: 'memory item not found' });
902
+ return { ok: true };
903
+ });
904
+ // Chat. Messages are scoped by dot id, or 'all' for the cross-dot chat (agent/chat.ts convention).
905
+ // A report-scoped chat stores its turns under `report:<id>` (agent/chat.ts runChat); ?reportId reads that scope.
906
+ app.get('/api/messages', async (req) => {
907
+ const scope = req.query.reportId ? `report:${req.query.reportId}` : req.query.dotId || 'all';
908
+ const rows = list('messages', { watchId: scope }, { orderBy: 'id DESC', limit: clampLimit(req.query.limit, 50) }).reverse();
909
+ // A dot update with a decision shows the approval as it stands now (KAT-143), and triage rows read their
910
+ // action state again (KAT-247), so a reopened chat shows what was done since.
911
+ return rows.map((m) => {
912
+ const id = m.update?.approvalId;
913
+ const approval = id ? get('approvals', id) : undefined;
914
+ const withUpdate = approval && m.update ? { ...m, update: { ...m.update, approval } } : m;
915
+ return withUpdate.blocks ? { ...withUpdate, blocks: refreshStoredBlocks(withUpdate.blocks) } : withUpdate;
916
+ });
917
+ });
918
+ app.post('/api/chat', async (req, reply) => {
919
+ const text = req.body?.text;
920
+ if (typeof text !== 'string' || !text.trim())
921
+ return reply.code(400).send({ error: 'text is required' });
922
+ const dotId = req.body?.dotId || undefined;
923
+ const watch = dotId ? get('watches', dotId) : undefined;
924
+ if (dotId && !watch)
925
+ return reply.code(404).send({ error: 'dot not found' });
926
+ const reportId = typeof req.body?.reportId === 'string' && req.body.reportId ? req.body.reportId : undefined;
927
+ if (reportId && !getReport(reportId))
928
+ return reply.code(404).send({ error: 'report not found' });
929
+ const channel = req.body?.channel === 'call' ? 'call' : 'chat';
930
+ const sse = openSse(reply);
931
+ let acc = '';
932
+ let closed = false;
933
+ const untrack = req.qdCaller?.kind === 'device'
934
+ ? trackDeviceStream(req.qdCaller.deviceId, () => {
935
+ closed = true;
936
+ sse.end();
937
+ })
938
+ : () => { };
939
+ sse.raw.on('close', () => {
940
+ closed = true;
941
+ untrack();
942
+ });
943
+ try {
944
+ // answerChat loads history and stores both turns itself. Events: tool, tool_result, approval, run.
945
+ const out = await answerChat({ dotId: watch?.id, text, reportId, channel, role: roleOf(req.qdCaller) }, (t) => {
946
+ acc += t;
947
+ if (!closed)
948
+ sse.send('delta', { text: t });
949
+ }, () => { }, (e) => {
950
+ if (closed)
951
+ return;
952
+ const { type, ...data } = e;
953
+ sse.send(type, data);
954
+ });
955
+ const final = typeof out === 'string' && out ? out : acc;
956
+ // The deltas went out as the model wrote them. When the final text differs in more than spacing (titled
957
+ // references, KAT-225), the client swaps it in for what streamed.
958
+ const squash = (t) => t.replace(/\s+/g, '');
959
+ sse.send('done', { text: final, ...(squash(final) !== squash(acc) && acc ? { replace: true } : {}) });
960
+ }
961
+ catch (err) {
962
+ log.error('chat failed', err);
963
+ sse.send('error', { error: String(err instanceof Error ? err.message : err) });
964
+ }
965
+ finally {
966
+ sse.end();
967
+ }
968
+ });
969
+ // Live events
970
+ app.get('/api/live', async (req, reply) => {
971
+ const sse = openSse(reply);
972
+ const unsub = subscribe((e) => sse.send(null, e));
973
+ const closeClient = liveClientOpened();
974
+ const ping = setInterval(() => sse.comment('ping'), 25_000);
975
+ // Revoking a phone ends its open stream at once.
976
+ const untrack = req.qdCaller?.kind === 'device' ? trackDeviceStream(req.qdCaller.deviceId, () => sse.end()) : () => { };
977
+ req.raw.on('close', () => {
978
+ clearInterval(ping);
979
+ unsub();
980
+ closeClient();
981
+ untrack();
982
+ });
983
+ });
984
+ // Push
985
+ app.get('/api/push/vapid', async () => ({ publicKey: ensureVapidKeys().publicKey }));
986
+ app.post('/api/push/subscribe', async (req, reply) => {
987
+ const body = req.body ?? {};
988
+ const sub = (body.subscription && typeof body.subscription === 'object' ? body.subscription : body);
989
+ if (!sub.endpoint || !sub.keys?.p256dh || !sub.keys?.auth)
990
+ return reply.code(400).send({ error: 'invalid push subscription' });
991
+ const existing = findOne('push_subs', { endpoint: sub.endpoint });
992
+ // A paired phone's subscription is bound to its device, so revoking the device stops its pushes.
993
+ const deviceId = req.qdCaller?.kind === 'device' ? req.qdCaller.deviceId : null;
994
+ const row = { endpoint: sub.endpoint, keys: sub.keys, subscription: sub, userAgent: req.headers['user-agent'], deviceId };
995
+ if (existing)
996
+ update('push_subs', existing.id, row);
997
+ else
998
+ insert('push_subs', row);
999
+ return reply.code(201).send({ ok: true });
1000
+ });
1001
+ // Outbox
1002
+ app.get('/api/outbox', async (req) => {
1003
+ const names = new Map();
1004
+ return list('outbox', { watchId: req.query.dotId || undefined, runId: req.query.runId || undefined }, { orderBy: 'id DESC', limit: clampLimit(req.query.limit, 100) }).map((o) => ({ ...o, dotName: dotName(o.watchId, names) }));
1005
+ });
1006
+ // Simulate: same normalize and enqueue path as real webhooks, minus signatures.
1007
+ app.post('/api/simulate/:kind', async (req, reply) => {
1008
+ const kind = req.params.kind;
1009
+ if (!SIMULATE_KINDS.includes(kind))
1010
+ return reply.code(400).send({ error: `kind must be one of ${SIMULATE_KINDS.join(', ')}` });
1011
+ let payload;
1012
+ try {
1013
+ payload = JSON.parse(await readFile(join(config.demoDir, 'payloads', `${kind}.json`), 'utf8'));
1014
+ }
1015
+ catch (err) {
1016
+ return reply.code(404).send({ error: `demo/payloads/${kind}.json missing or invalid: ${String(err)}` });
1017
+ }
1018
+ const dotId = req.body?.dotId || req.query.dotId;
1019
+ if (dotId && !get('watches', dotId))
1020
+ return reply.code(404).send({ error: 'dot not found' });
1021
+ const h = payload.headers ?? {};
1022
+ const eventName = payload.eventName ?? h['X-GitHub-Event'] ?? h['x-github-event'];
1023
+ const result = ingest(payload.source, eventName, payload.body, {
1024
+ simulate: true,
1025
+ watchIds: dotId ? [dotId] : undefined,
1026
+ fallbackToAll: true,
1027
+ });
1028
+ return reply.code(result.accepted ? 202 : 422).send({
1029
+ ok: result.accepted,
1030
+ reason: result.reason,
1031
+ jobKind: result.jobKind,
1032
+ trigger: result.trigger ? { kind: result.trigger.kind, title: result.trigger.title } : undefined,
1033
+ jobs: result.jobs,
1034
+ });
1035
+ });
1036
+ }
1037
+ /** A stored review from an older build can miss a field the renderer reads; the run page still loads without a summary. */
1038
+ function safeRunSummary(run, showFrom) {
1039
+ try {
1040
+ return runSummary(run, showFrom);
1041
+ }
1042
+ catch (err) {
1043
+ log.warn(`run summary for ${run.id} failed: ${String(err)}`);
1044
+ return undefined;
1045
+ }
1046
+ }
1047
+ //# sourceMappingURL=api.js.map