tinybeeai 0.0.0-stage → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1034) hide show
  1. package/.railway/railway.ts +83 -0
  2. package/.railway/tsconfig.json +13 -0
  3. package/AGENTS.md +102 -0
  4. package/Dockerfile +108 -0
  5. package/LICENSE +21 -0
  6. package/README.md +275 -2
  7. package/bin/localdost.js +29 -0
  8. package/bin/tinybeeai.js +28 -0
  9. package/compose.yml +63 -0
  10. package/demo/payloads/ci_failure.json +46 -0
  11. package/demo/payloads/outage.json +18 -0
  12. package/demo/payloads/pr_comment_edited.json +50 -0
  13. package/demo/payloads/pr_merged.json +80 -0
  14. package/demo/payloads/pr_opened.json +69 -0
  15. package/demo/payloads/pr_synchronize.json +69 -0
  16. package/demo/payloads/ticket_created.json +138 -0
  17. package/demo/sample-app/playwright.config.ts +20 -0
  18. package/demo/sample-app/server.ts +163 -0
  19. package/demo/sample-app/tests/checkout.spec.ts +13 -0
  20. package/demo/sample-app/tests/smoke.spec.ts +8 -0
  21. package/dist/agent/approval-retries.js +59 -0
  22. package/dist/agent/approval-retries.js.map +1 -0
  23. package/dist/agent/approvals.js +390 -0
  24. package/dist/agent/approvals.js.map +1 -0
  25. package/dist/agent/chat-bridge.js +154 -0
  26. package/dist/agent/chat-bridge.js.map +1 -0
  27. package/dist/agent/chat-lead.js +475 -0
  28. package/dist/agent/chat-lead.js.map +1 -0
  29. package/dist/agent/chat-loop.js +106 -0
  30. package/dist/agent/chat-loop.js.map +1 -0
  31. package/dist/agent/chat-mcp.js +49 -0
  32. package/dist/agent/chat-mcp.js.map +1 -0
  33. package/dist/agent/chat-tool-blocks.js +453 -0
  34. package/dist/agent/chat-tool-blocks.js.map +1 -0
  35. package/dist/agent/chat-view.js +94 -0
  36. package/dist/agent/chat-view.js.map +1 -0
  37. package/dist/agent/chat-views.js +781 -0
  38. package/dist/agent/chat-views.js.map +1 -0
  39. package/dist/agent/chat.js +1703 -0
  40. package/dist/agent/chat.js.map +1 -0
  41. package/dist/agent/destinations.js +150 -0
  42. package/dist/agent/destinations.js.map +1 -0
  43. package/dist/agent/llm-cli.js +451 -0
  44. package/dist/agent/llm-cli.js.map +1 -0
  45. package/dist/agent/llm-codex.js +493 -0
  46. package/dist/agent/llm-codex.js.map +1 -0
  47. package/dist/agent/llm-openai.js +211 -0
  48. package/dist/agent/llm-openai.js.map +1 -0
  49. package/dist/agent/llm.js +1464 -0
  50. package/dist/agent/llm.js.map +1 -0
  51. package/dist/agent/memory.js +33 -0
  52. package/dist/agent/memory.js.map +1 -0
  53. package/dist/agent/mock-views.js +93 -0
  54. package/dist/agent/mock-views.js.map +1 -0
  55. package/dist/agent/model-availability.js +207 -0
  56. package/dist/agent/model-availability.js.map +1 -0
  57. package/dist/agent/model-errors.js +188 -0
  58. package/dist/agent/model-errors.js.map +1 -0
  59. package/dist/agent/model-guard.js +311 -0
  60. package/dist/agent/model-guard.js.map +1 -0
  61. package/dist/agent/prompts.js +374 -0
  62. package/dist/agent/prompts.js.map +1 -0
  63. package/dist/agent/qa-lenses/qa-auth.md +32 -0
  64. package/dist/agent/qa-lenses/qa-check-then-act.md +33 -0
  65. package/dist/agent/qa-lenses/qa-control-has-no-effect.md +32 -0
  66. package/dist/agent/qa-lenses/qa-dates.md +31 -0
  67. package/dist/agent/qa-lenses/qa-double-submit.md +34 -0
  68. package/dist/agent/qa-lenses/qa-feature-flag.md +29 -0
  69. package/dist/agent/qa-lenses/qa-migration.md +35 -0
  70. package/dist/agent/qa-lenses/qa-money.md +38 -0
  71. package/dist/agent/qa-lenses/qa-response-shape-break.md +33 -0
  72. package/dist/agent/qa-lenses/qa-validation.md +29 -0
  73. package/dist/agent/qa-prompts.js +660 -0
  74. package/dist/agent/qa-prompts.js.map +1 -0
  75. package/dist/agent/reasoning.js +55 -0
  76. package/dist/agent/reasoning.js.map +1 -0
  77. package/dist/agent/redact.js +48 -0
  78. package/dist/agent/redact.js.map +1 -0
  79. package/dist/agent/rules.js +927 -0
  80. package/dist/agent/rules.js.map +1 -0
  81. package/dist/agent/runs.js +440 -0
  82. package/dist/agent/runs.js.map +1 -0
  83. package/dist/agent/send-retry.js +62 -0
  84. package/dist/agent/send-retry.js.map +1 -0
  85. package/dist/agent/team-notes.js +288 -0
  86. package/dist/agent/team-notes.js.map +1 -0
  87. package/dist/agent/token-meter.js +31 -0
  88. package/dist/agent/token-meter.js.map +1 -0
  89. package/dist/agent/triage.js +506 -0
  90. package/dist/agent/triage.js.map +1 -0
  91. package/dist/api.js +1047 -0
  92. package/dist/api.js.map +1 -0
  93. package/dist/approval-targets.js +107 -0
  94. package/dist/approval-targets.js.map +1 -0
  95. package/dist/bee/cloudflared.js +108 -0
  96. package/dist/bee/cloudflared.js.map +1 -0
  97. package/dist/bee/health.js +99 -0
  98. package/dist/bee/health.js.map +1 -0
  99. package/dist/bee/hive-client.js +75 -0
  100. package/dist/bee/hive-client.js.map +1 -0
  101. package/dist/bee/identity.js +39 -0
  102. package/dist/bee/identity.js.map +1 -0
  103. package/dist/bee/models.js +510 -0
  104. package/dist/bee/models.js.map +1 -0
  105. package/dist/bee/pairing.js +351 -0
  106. package/dist/bee/pairing.js.map +1 -0
  107. package/dist/bee/relay-client.js +50 -0
  108. package/dist/bee/relay-client.js.map +1 -0
  109. package/dist/bee/routes.js +214 -0
  110. package/dist/bee/routes.js.map +1 -0
  111. package/dist/bee/setup-page.js +223 -0
  112. package/dist/bee/setup-page.js.map +1 -0
  113. package/dist/bee/state.js +41 -0
  114. package/dist/bee/state.js.map +1 -0
  115. package/dist/bee/status.js +117 -0
  116. package/dist/bee/status.js.map +1 -0
  117. package/dist/bee/tunnel-manager.js +245 -0
  118. package/dist/bee/tunnel-manager.js.map +1 -0
  119. package/dist/bee/updater.js +268 -0
  120. package/dist/bee/updater.js.map +1 -0
  121. package/dist/boot.js +41 -0
  122. package/dist/boot.js.map +1 -0
  123. package/dist/call/index.js +145 -0
  124. package/dist/call/index.js.map +1 -0
  125. package/dist/case-folders.js +294 -0
  126. package/dist/case-folders.js.map +1 -0
  127. package/dist/cases.js +554 -0
  128. package/dist/cases.js.map +1 -0
  129. package/dist/chat-blocks.js +709 -0
  130. package/dist/chat-blocks.js.map +1 -0
  131. package/dist/chat-parse-util.js +38 -0
  132. package/dist/chat-parse-util.js.map +1 -0
  133. package/dist/chat-results.js +96 -0
  134. package/dist/chat-results.js.map +1 -0
  135. package/dist/chat-stats.js +193 -0
  136. package/dist/chat-stats.js.map +1 -0
  137. package/dist/chat-view-specs.js +186 -0
  138. package/dist/chat-view-specs.js.map +1 -0
  139. package/dist/cli/doctor-ops.js +361 -0
  140. package/dist/cli/doctor-ops.js.map +1 -0
  141. package/dist/cli/doctor.js +289 -0
  142. package/dist/cli/doctor.js.map +1 -0
  143. package/dist/cli/dogfood.js +70 -0
  144. package/dist/cli/dogfood.js.map +1 -0
  145. package/dist/cli/env-file.js +34 -0
  146. package/dist/cli/env-file.js.map +1 -0
  147. package/dist/cli/eval-chat-cases.js +359 -0
  148. package/dist/cli/eval-chat-cases.js.map +1 -0
  149. package/dist/cli/eval-chat-results.js +85 -0
  150. package/dist/cli/eval-chat-results.js.map +1 -0
  151. package/dist/cli/eval-chat.js +508 -0
  152. package/dist/cli/eval-chat.js.map +1 -0
  153. package/dist/cli/eval-designer.js +67 -0
  154. package/dist/cli/eval-designer.js.map +1 -0
  155. package/dist/cli/eval-qa-review.js +284 -0
  156. package/dist/cli/eval-qa-review.js.map +1 -0
  157. package/dist/cli/golden-export.js +35 -0
  158. package/dist/cli/golden-export.js.map +1 -0
  159. package/dist/cli/index.js +90 -0
  160. package/dist/cli/index.js.map +1 -0
  161. package/dist/cli/init.js +746 -0
  162. package/dist/cli/init.js.map +1 -0
  163. package/dist/cli/scan-repo.js +199 -0
  164. package/dist/cli/scan-repo.js.map +1 -0
  165. package/dist/cli/seed.js +108 -0
  166. package/dist/cli/seed.js.map +1 -0
  167. package/dist/cli/tinybee.js +247 -0
  168. package/dist/cli/tinybee.js.map +1 -0
  169. package/dist/config.js +138 -0
  170. package/dist/config.js.map +1 -0
  171. package/dist/connect/aivision-local.js +833 -0
  172. package/dist/connect/aivision-local.js.map +1 -0
  173. package/dist/connect/aivision-token.js +84 -0
  174. package/dist/connect/aivision-token.js.map +1 -0
  175. package/dist/connect/crypto.js +65 -0
  176. package/dist/connect/crypto.js.map +1 -0
  177. package/dist/connect/deps.js +110 -0
  178. package/dist/connect/deps.js.map +1 -0
  179. package/dist/connect/engines.js +144 -0
  180. package/dist/connect/engines.js.map +1 -0
  181. package/dist/connect/github-access.js +74 -0
  182. package/dist/connect/github-access.js.map +1 -0
  183. package/dist/connect/github-app.js +129 -0
  184. package/dist/connect/github-app.js.map +1 -0
  185. package/dist/connect/index.js +9 -0
  186. package/dist/connect/index.js.map +1 -0
  187. package/dist/connect/model-choices.js +201 -0
  188. package/dist/connect/model-choices.js.map +1 -0
  189. package/dist/connect/providers.js +900 -0
  190. package/dist/connect/providers.js.map +1 -0
  191. package/dist/connect/readiness.js +232 -0
  192. package/dist/connect/readiness.js.map +1 -0
  193. package/dist/connect/redact.js +16 -0
  194. package/dist/connect/redact.js.map +1 -0
  195. package/dist/connect/registry.js +406 -0
  196. package/dist/connect/registry.js.map +1 -0
  197. package/dist/connect/store.js +283 -0
  198. package/dist/connect/store.js.map +1 -0
  199. package/dist/connect/tunnel.js +134 -0
  200. package/dist/connect/tunnel.js.map +1 -0
  201. package/dist/connect/types.js +8 -0
  202. package/dist/connect/types.js.map +1 -0
  203. package/dist/connections/index.js +5 -0
  204. package/dist/connections/index.js.map +1 -0
  205. package/dist/connections/manager.js +223 -0
  206. package/dist/connections/manager.js.map +1 -0
  207. package/dist/connections/presets.js +264 -0
  208. package/dist/connections/presets.js.map +1 -0
  209. package/dist/connections/store.js +132 -0
  210. package/dist/connections/store.js.map +1 -0
  211. package/dist/connections/tools.js +206 -0
  212. package/dist/connections/tools.js.map +1 -0
  213. package/dist/db-schema.js +355 -0
  214. package/dist/db-schema.js.map +1 -0
  215. package/dist/db.js +379 -0
  216. package/dist/db.js.map +1 -0
  217. package/dist/delivery.js +49 -0
  218. package/dist/delivery.js.map +1 -0
  219. package/dist/deploy-wait.js +701 -0
  220. package/dist/deploy-wait.js.map +1 -0
  221. package/dist/disk.js +243 -0
  222. package/dist/disk.js.map +1 -0
  223. package/dist/dot-health.js +966 -0
  224. package/dist/dot-health.js.map +1 -0
  225. package/dist/dot-remove.js +246 -0
  226. package/dist/dot-remove.js.map +1 -0
  227. package/dist/events.js +38 -0
  228. package/dist/events.js.map +1 -0
  229. package/dist/evidence-pack.js +423 -0
  230. package/dist/evidence-pack.js.map +1 -0
  231. package/dist/flows/answers.js +289 -0
  232. package/dist/flows/answers.js.map +1 -0
  233. package/dist/flows/change.js +811 -0
  234. package/dist/flows/change.js.map +1 -0
  235. package/dist/flows/checkStatus.js +524 -0
  236. package/dist/flows/checkStatus.js.map +1 -0
  237. package/dist/flows/ci.js +236 -0
  238. package/dist/flows/ci.js.map +1 -0
  239. package/dist/flows/ciFailure.js +76 -0
  240. package/dist/flows/ciFailure.js.map +1 -0
  241. package/dist/flows/common.js +522 -0
  242. package/dist/flows/common.js.map +1 -0
  243. package/dist/flows/default-branch.js +29 -0
  244. package/dist/flows/default-branch.js.map +1 -0
  245. package/dist/flows/defectDraft.js +447 -0
  246. package/dist/flows/defectDraft.js.map +1 -0
  247. package/dist/flows/defects.js +623 -0
  248. package/dist/flows/defects.js.map +1 -0
  249. package/dist/flows/digest.js +295 -0
  250. package/dist/flows/digest.js.map +1 -0
  251. package/dist/flows/impact.js +82 -0
  252. package/dist/flows/impact.js.map +1 -0
  253. package/dist/flows/index.js +23 -0
  254. package/dist/flows/index.js.map +1 -0
  255. package/dist/flows/intent-sources.js +72 -0
  256. package/dist/flows/intent-sources.js.map +1 -0
  257. package/dist/flows/mergeCases.js +169 -0
  258. package/dist/flows/mergeCases.js.map +1 -0
  259. package/dist/flows/nightlyRun.js +82 -0
  260. package/dist/flows/nightlyRun.js.map +1 -0
  261. package/dist/flows/outage.js +64 -0
  262. package/dist/flows/outage.js.map +1 -0
  263. package/dist/flows/postMerge.js +5 -0
  264. package/dist/flows/postMerge.js.map +1 -0
  265. package/dist/flows/prCheck.js +5 -0
  266. package/dist/flows/prCheck.js.map +1 -0
  267. package/dist/flows/prTarget.js +66 -0
  268. package/dist/flows/prTarget.js.map +1 -0
  269. package/dist/flows/qa.js +363 -0
  270. package/dist/flows/qa.js.map +1 -0
  271. package/dist/flows/questions.js +160 -0
  272. package/dist/flows/questions.js.map +1 -0
  273. package/dist/flows/recovery.js +64 -0
  274. package/dist/flows/recovery.js.map +1 -0
  275. package/dist/flows/rerun.js +50 -0
  276. package/dist/flows/rerun.js.map +1 -0
  277. package/dist/flows/testDesign.js +294 -0
  278. package/dist/flows/testDesign.js.map +1 -0
  279. package/dist/flows/testDesignJob.js +46 -0
  280. package/dist/flows/testDesignJob.js.map +1 -0
  281. package/dist/flows/testRepo.js +228 -0
  282. package/dist/flows/testRepo.js.map +1 -0
  283. package/dist/flows/testSavings.js +381 -0
  284. package/dist/flows/testSavings.js.map +1 -0
  285. package/dist/flows/testStatus.js +343 -0
  286. package/dist/flows/testStatus.js.map +1 -0
  287. package/dist/flows/ticketContext.js +425 -0
  288. package/dist/flows/ticketContext.js.map +1 -0
  289. package/dist/flows/ticketReview.js +249 -0
  290. package/dist/flows/ticketReview.js.map +1 -0
  291. package/dist/flows/ticks.js +73 -0
  292. package/dist/flows/ticks.js.map +1 -0
  293. package/dist/hive/admin.js +122 -0
  294. package/dist/hive/admin.js.map +1 -0
  295. package/dist/hive/auth.js +130 -0
  296. package/dist/hive/auth.js.map +1 -0
  297. package/dist/hive/billing.js +278 -0
  298. package/dist/hive/billing.js.map +1 -0
  299. package/dist/hive/config.js +63 -0
  300. package/dist/hive/config.js.map +1 -0
  301. package/dist/hive/db.js +186 -0
  302. package/dist/hive/db.js.map +1 -0
  303. package/dist/hive/embed.js +76 -0
  304. package/dist/hive/embed.js.map +1 -0
  305. package/dist/hive/errors.js +17 -0
  306. package/dist/hive/errors.js.map +1 -0
  307. package/dist/hive/google.js +75 -0
  308. package/dist/hive/google.js.map +1 -0
  309. package/dist/hive/index.js +25 -0
  310. package/dist/hive/index.js.map +1 -0
  311. package/dist/hive/jwt.js +57 -0
  312. package/dist/hive/jwt.js.map +1 -0
  313. package/dist/hive/keys.js +61 -0
  314. package/dist/hive/keys.js.map +1 -0
  315. package/dist/hive/mail.js +27 -0
  316. package/dist/hive/mail.js.map +1 -0
  317. package/dist/hive/native-push.js +255 -0
  318. package/dist/hive/native-push.js.map +1 -0
  319. package/dist/hive/pairing.js +342 -0
  320. package/dist/hive/pairing.js.map +1 -0
  321. package/dist/hive/presence.js +121 -0
  322. package/dist/hive/presence.js.map +1 -0
  323. package/dist/hive/push.js +185 -0
  324. package/dist/hive/push.js.map +1 -0
  325. package/dist/hive/relay.js +205 -0
  326. package/dist/hive/relay.js.map +1 -0
  327. package/dist/hive/routes.js +228 -0
  328. package/dist/hive/routes.js.map +1 -0
  329. package/dist/hive/server.js +12 -0
  330. package/dist/hive/server.js.map +1 -0
  331. package/dist/hive/session.js +28 -0
  332. package/dist/hive/session.js.map +1 -0
  333. package/dist/hive/tenants.js +287 -0
  334. package/dist/hive/tenants.js.map +1 -0
  335. package/dist/hive/tunnel/cloudflare.js +160 -0
  336. package/dist/hive/tunnel/cloudflare.js.map +1 -0
  337. package/dist/hive/tunnel/mock.js +52 -0
  338. package/dist/hive/tunnel/mock.js.map +1 -0
  339. package/dist/hive/tunnel/provider.js +19 -0
  340. package/dist/hive/tunnel/provider.js.map +1 -0
  341. package/dist/hive/updates.js +61 -0
  342. package/dist/hive/updates.js.map +1 -0
  343. package/dist/http-error.js +11 -0
  344. package/dist/http-error.js.map +1 -0
  345. package/dist/impact/classify.js +226 -0
  346. package/dist/impact/classify.js.map +1 -0
  347. package/dist/impact/find.js +259 -0
  348. package/dist/impact/find.js.map +1 -0
  349. package/dist/impact/pr.js +54 -0
  350. package/dist/impact/pr.js.map +1 -0
  351. package/dist/impact/store.js +365 -0
  352. package/dist/impact/store.js.map +1 -0
  353. package/dist/impact/text.js +158 -0
  354. package/dist/impact/text.js.map +1 -0
  355. package/dist/index.js +288 -0
  356. package/dist/index.js.map +1 -0
  357. package/dist/integrations/adf.js +397 -0
  358. package/dist/integrations/adf.js.map +1 -0
  359. package/dist/integrations/bot-marker.js +103 -0
  360. package/dist/integrations/bot-marker.js.map +1 -0
  361. package/dist/integrations/github-issues.js +705 -0
  362. package/dist/integrations/github-issues.js.map +1 -0
  363. package/dist/integrations/github.js +1284 -0
  364. package/dist/integrations/github.js.map +1 -0
  365. package/dist/integrations/issue-create.js +60 -0
  366. package/dist/integrations/issue-create.js.map +1 -0
  367. package/dist/integrations/jira.js +279 -0
  368. package/dist/integrations/jira.js.map +1 -0
  369. package/dist/integrations/linear.js +273 -0
  370. package/dist/integrations/linear.js.map +1 -0
  371. package/dist/integrations/own-identity-warm.js +47 -0
  372. package/dist/integrations/own-identity-warm.js.map +1 -0
  373. package/dist/integrations/own-identity.js +39 -0
  374. package/dist/integrations/own-identity.js.map +1 -0
  375. package/dist/integrations/status-change.js +42 -0
  376. package/dist/integrations/status-change.js.map +1 -0
  377. package/dist/lead-defects.js +202 -0
  378. package/dist/lead-defects.js.map +1 -0
  379. package/dist/lead-digest.js +333 -0
  380. package/dist/lead-digest.js.map +1 -0
  381. package/dist/lead-release.js +286 -0
  382. package/dist/lead-release.js.map +1 -0
  383. package/dist/lead.js +328 -0
  384. package/dist/lead.js.map +1 -0
  385. package/dist/legacy-names.js +51 -0
  386. package/dist/legacy-names.js.map +1 -0
  387. package/dist/limits.js +44 -0
  388. package/dist/limits.js.map +1 -0
  389. package/dist/link-trust.js +224 -0
  390. package/dist/link-trust.js.map +1 -0
  391. package/dist/links.js +219 -0
  392. package/dist/links.js.map +1 -0
  393. package/dist/loopback.js +35 -0
  394. package/dist/loopback.js.map +1 -0
  395. package/dist/model-cap.js +60 -0
  396. package/dist/model-cap.js.map +1 -0
  397. package/dist/model-usage.js +173 -0
  398. package/dist/model-usage.js.map +1 -0
  399. package/dist/monitor/backfill.js +39 -0
  400. package/dist/monitor/backfill.js.map +1 -0
  401. package/dist/monitor/browser-probe.js +198 -0
  402. package/dist/monitor/browser-probe.js.map +1 -0
  403. package/dist/monitor/catch-up.js +31 -0
  404. package/dist/monitor/catch-up.js.map +1 -0
  405. package/dist/monitor/pollers.js +1606 -0
  406. package/dist/monitor/pollers.js.map +1 -0
  407. package/dist/monitor/probes.js +178 -0
  408. package/dist/monitor/probes.js.map +1 -0
  409. package/dist/monitor/self-health.js +228 -0
  410. package/dist/monitor/self-health.js.map +1 -0
  411. package/dist/monitor/statuspage.js +105 -0
  412. package/dist/monitor/statuspage.js.map +1 -0
  413. package/dist/nightly-scheduler.js +127 -0
  414. package/dist/nightly-scheduler.js.map +1 -0
  415. package/dist/nightly.js +141 -0
  416. package/dist/nightly.js.map +1 -0
  417. package/dist/notify/headline.js +114 -0
  418. package/dist/notify/headline.js.map +1 -0
  419. package/dist/notify/push.js +174 -0
  420. package/dist/notify/push.js.map +1 -0
  421. package/dist/object-cards.js +585 -0
  422. package/dist/object-cards.js.map +1 -0
  423. package/dist/object-links.js +47 -0
  424. package/dist/object-links.js.map +1 -0
  425. package/dist/onboarding-checks.js +141 -0
  426. package/dist/onboarding-checks.js.map +1 -0
  427. package/dist/onboarding.js +401 -0
  428. package/dist/onboarding.js.map +1 -0
  429. package/dist/path-filters.js +62 -0
  430. package/dist/path-filters.js.map +1 -0
  431. package/dist/phone/alerts.js +67 -0
  432. package/dist/phone/alerts.js.map +1 -0
  433. package/dist/phone/auth.js +240 -0
  434. package/dist/phone/auth.js.map +1 -0
  435. package/dist/phone/pairing.js +442 -0
  436. package/dist/phone/pairing.js.map +1 -0
  437. package/dist/phone/prefs.js +27 -0
  438. package/dist/phone/prefs.js.map +1 -0
  439. package/dist/phone/tailscale.js +161 -0
  440. package/dist/phone/tailscale.js.map +1 -0
  441. package/dist/pr-scope.js +441 -0
  442. package/dist/pr-scope.js.map +1 -0
  443. package/dist/proc.js +156 -0
  444. package/dist/proc.js.map +1 -0
  445. package/dist/profile/cache.js +108 -0
  446. package/dist/profile/cache.js.map +1 -0
  447. package/dist/profile/ci.js +216 -0
  448. package/dist/profile/ci.js.map +1 -0
  449. package/dist/profile/index.js +8 -0
  450. package/dist/profile/index.js.map +1 -0
  451. package/dist/profile/job.js +135 -0
  452. package/dist/profile/job.js.map +1 -0
  453. package/dist/profile/queries/go-tags.scm +47 -0
  454. package/dist/profile/queries/java-tags.scm +25 -0
  455. package/dist/profile/queries/javascript-tags.scm +104 -0
  456. package/dist/profile/queries/python-tags.scm +19 -0
  457. package/dist/profile/queries/typescript-tags.scm +28 -0
  458. package/dist/profile/scan-thread.js +42 -0
  459. package/dist/profile/scan-thread.js.map +1 -0
  460. package/dist/profile/scan-worker.js +16 -0
  461. package/dist/profile/scan-worker.js.map +1 -0
  462. package/dist/profile/scan.js +351 -0
  463. package/dist/profile/scan.js.map +1 -0
  464. package/dist/profile/stack.js +528 -0
  465. package/dist/profile/stack.js.map +1 -0
  466. package/dist/profile/store.js +35 -0
  467. package/dist/profile/store.js.map +1 -0
  468. package/dist/profile/tests.js +399 -0
  469. package/dist/profile/tests.js.map +1 -0
  470. package/dist/queue.js +350 -0
  471. package/dist/queue.js.map +1 -0
  472. package/dist/refs.js +756 -0
  473. package/dist/refs.js.map +1 -0
  474. package/dist/release-notes.js +272 -0
  475. package/dist/release-notes.js.map +1 -0
  476. package/dist/reports/build.js +243 -0
  477. package/dist/reports/build.js.map +1 -0
  478. package/dist/reports/index.js +211 -0
  479. package/dist/reports/index.js.map +1 -0
  480. package/dist/reports/store.js +150 -0
  481. package/dist/reports/store.js.map +1 -0
  482. package/dist/reports/ticket-titles.js +124 -0
  483. package/dist/reports/ticket-titles.js.map +1 -0
  484. package/dist/reports/trace-map.js +1278 -0
  485. package/dist/reports/trace-map.js.map +1 -0
  486. package/dist/reports/traceability.js +422 -0
  487. package/dist/reports/traceability.js.map +1 -0
  488. package/dist/repos/mirror.js +356 -0
  489. package/dist/repos/mirror.js.map +1 -0
  490. package/dist/repos-github.js +108 -0
  491. package/dist/repos-github.js.map +1 -0
  492. package/dist/repos.js +891 -0
  493. package/dist/repos.js.map +1 -0
  494. package/dist/review/backend-anthropic.js +90 -0
  495. package/dist/review/backend-anthropic.js.map +1 -0
  496. package/dist/review/backend-cli.js +248 -0
  497. package/dist/review/backend-cli.js.map +1 -0
  498. package/dist/review/backend-codex.js +81 -0
  499. package/dist/review/backend-codex.js.map +1 -0
  500. package/dist/review/backend-mock.js +686 -0
  501. package/dist/review/backend-mock.js.map +1 -0
  502. package/dist/review/backend-only.js +168 -0
  503. package/dist/review/backend-only.js.map +1 -0
  504. package/dist/review/backend.js +105 -0
  505. package/dist/review/backend.js.map +1 -0
  506. package/dist/review/baseline/render-v1.js +483 -0
  507. package/dist/review/baseline/render-v1.js.map +1 -0
  508. package/dist/review/change-summary.js +197 -0
  509. package/dist/review/change-summary.js.map +1 -0
  510. package/dist/review/changelog.js +40 -0
  511. package/dist/review/changelog.js.map +1 -0
  512. package/dist/review/check.js +494 -0
  513. package/dist/review/check.js.map +1 -0
  514. package/dist/review/checkout.js +185 -0
  515. package/dist/review/checkout.js.map +1 -0
  516. package/dist/review/classify.js +178 -0
  517. package/dist/review/classify.js.map +1 -0
  518. package/dist/review/coverage.js +347 -0
  519. package/dist/review/coverage.js.map +1 -0
  520. package/dist/review/decision-eval.js +121 -0
  521. package/dist/review/decision-eval.js.map +1 -0
  522. package/dist/review/deliver.js +104 -0
  523. package/dist/review/deliver.js.map +1 -0
  524. package/dist/review/designer-eval.js +280 -0
  525. package/dist/review/designer-eval.js.map +1 -0
  526. package/dist/review/dogfood-digest.js +136 -0
  527. package/dist/review/dogfood-digest.js.map +1 -0
  528. package/dist/review/dogfood-eval.js +486 -0
  529. package/dist/review/dogfood-eval.js.map +1 -0
  530. package/dist/review/environment.js +109 -0
  531. package/dist/review/environment.js.map +1 -0
  532. package/dist/review/eval.js +405 -0
  533. package/dist/review/eval.js.map +1 -0
  534. package/dist/review/glob.js +110 -0
  535. package/dist/review/glob.js.map +1 -0
  536. package/dist/review/golden-export.js +219 -0
  537. package/dist/review/golden-export.js.map +1 -0
  538. package/dist/review/impact.js +406 -0
  539. package/dist/review/impact.js.map +1 -0
  540. package/dist/review/imports.js +542 -0
  541. package/dist/review/imports.js.map +1 -0
  542. package/dist/review/index.js +512 -0
  543. package/dist/review/index.js.map +1 -0
  544. package/dist/review/intent.js +128 -0
  545. package/dist/review/intent.js.map +1 -0
  546. package/dist/review/lenses.js +167 -0
  547. package/dist/review/lenses.js.map +1 -0
  548. package/dist/review/linked-tickets.js +293 -0
  549. package/dist/review/linked-tickets.js.map +1 -0
  550. package/dist/review/merge.js +370 -0
  551. package/dist/review/merge.js.map +1 -0
  552. package/dist/review/on-demand.js +218 -0
  553. package/dist/review/on-demand.js.map +1 -0
  554. package/dist/review/prompt-eval.js +299 -0
  555. package/dist/review/prompt-eval.js.map +1 -0
  556. package/dist/review/prompt-variants.js +37 -0
  557. package/dist/review/prompt-variants.js.map +1 -0
  558. package/dist/review/readability-eval.js +217 -0
  559. package/dist/review/readability-eval.js.map +1 -0
  560. package/dist/review/readability.js +295 -0
  561. package/dist/review/readability.js.map +1 -0
  562. package/dist/review/render.js +2409 -0
  563. package/dist/review/render.js.map +1 -0
  564. package/dist/review/repo.js +194 -0
  565. package/dist/review/repo.js.map +1 -0
  566. package/dist/review/report-plan.js +90 -0
  567. package/dist/review/report-plan.js.map +1 -0
  568. package/dist/review/routes.js +195 -0
  569. package/dist/review/routes.js.map +1 -0
  570. package/dist/review/run-summary.js +34 -0
  571. package/dist/review/run-summary.js.map +1 -0
  572. package/dist/review/run.js +245 -0
  573. package/dist/review/run.js.map +1 -0
  574. package/dist/review/scope.js +454 -0
  575. package/dist/review/scope.js.map +1 -0
  576. package/dist/review/security.js +432 -0
  577. package/dist/review/security.js.map +1 -0
  578. package/dist/review/signals.js +550 -0
  579. package/dist/review/signals.js.map +1 -0
  580. package/dist/review/specs.js +150 -0
  581. package/dist/review/specs.js.map +1 -0
  582. package/dist/review/ste.js +133 -0
  583. package/dist/review/ste.js.map +1 -0
  584. package/dist/review/test-repo-file.js +18 -0
  585. package/dist/review/test-repo-file.js.map +1 -0
  586. package/dist/review/test-repo-lines.js +53 -0
  587. package/dist/review/test-repo-lines.js.map +1 -0
  588. package/dist/review/ticket-on-demand.js +232 -0
  589. package/dist/review/ticket-on-demand.js.map +1 -0
  590. package/dist/review/yaml.js +298 -0
  591. package/dist/review/yaml.js.map +1 -0
  592. package/dist/room.js +361 -0
  593. package/dist/room.js.map +1 -0
  594. package/dist/routes/activity.js +219 -0
  595. package/dist/routes/activity.js.map +1 -0
  596. package/dist/routes/call.js +29 -0
  597. package/dist/routes/call.js.map +1 -0
  598. package/dist/routes/cards.js +40 -0
  599. package/dist/routes/cards.js.map +1 -0
  600. package/dist/routes/cases.js +133 -0
  601. package/dist/routes/cases.js.map +1 -0
  602. package/dist/routes/connect.js +268 -0
  603. package/dist/routes/connect.js.map +1 -0
  604. package/dist/routes/connection-presets.js +32 -0
  605. package/dist/routes/connection-presets.js.map +1 -0
  606. package/dist/routes/connections.js +177 -0
  607. package/dist/routes/connections.js.map +1 -0
  608. package/dist/routes/disk.js +19 -0
  609. package/dist/routes/disk.js.map +1 -0
  610. package/dist/routes/docs.js +71 -0
  611. package/dist/routes/docs.js.map +1 -0
  612. package/dist/routes/dot-suggest.js +1002 -0
  613. package/dist/routes/dot-suggest.js.map +1 -0
  614. package/dist/routes/evidence-pack.js +16 -0
  615. package/dist/routes/evidence-pack.js.map +1 -0
  616. package/dist/routes/health.js +121 -0
  617. package/dist/routes/health.js.map +1 -0
  618. package/dist/routes/impact.js +48 -0
  619. package/dist/routes/impact.js.map +1 -0
  620. package/dist/routes/lead.js +50 -0
  621. package/dist/routes/lead.js.map +1 -0
  622. package/dist/routes/links.js +13 -0
  623. package/dist/routes/links.js.map +1 -0
  624. package/dist/routes/markdown.js +62 -0
  625. package/dist/routes/markdown.js.map +1 -0
  626. package/dist/routes/onboarding.js +47 -0
  627. package/dist/routes/onboarding.js.map +1 -0
  628. package/dist/routes/phone.js +231 -0
  629. package/dist/routes/phone.js.map +1 -0
  630. package/dist/routes/release-notes.js +30 -0
  631. package/dist/routes/release-notes.js.map +1 -0
  632. package/dist/routes/reports.js +102 -0
  633. package/dist/routes/reports.js.map +1 -0
  634. package/dist/routes/repos.js +91 -0
  635. package/dist/routes/repos.js.map +1 -0
  636. package/dist/routes/review.js +283 -0
  637. package/dist/routes/review.js.map +1 -0
  638. package/dist/routes/roles.js +114 -0
  639. package/dist/routes/roles.js.map +1 -0
  640. package/dist/routes/room.js +21 -0
  641. package/dist/routes/room.js.map +1 -0
  642. package/dist/routes/runner.js +156 -0
  643. package/dist/routes/runner.js.map +1 -0
  644. package/dist/routes/runs.js +84 -0
  645. package/dist/routes/runs.js.map +1 -0
  646. package/dist/routes/setup.js +18 -0
  647. package/dist/routes/setup.js.map +1 -0
  648. package/dist/routes/test-runs.js +220 -0
  649. package/dist/routes/test-runs.js.map +1 -0
  650. package/dist/routes/ticket-status.js +17 -0
  651. package/dist/routes/ticket-status.js.map +1 -0
  652. package/dist/routes/traceability.js +143 -0
  653. package/dist/routes/traceability.js.map +1 -0
  654. package/dist/routes/true.js +142 -0
  655. package/dist/routes/true.js.map +1 -0
  656. package/dist/routes/updates.js +70 -0
  657. package/dist/routes/updates.js.map +1 -0
  658. package/dist/runner/adapters/index.js +12 -0
  659. package/dist/runner/adapters/index.js.map +1 -0
  660. package/dist/runner/adapters/playwright.js +68 -0
  661. package/dist/runner/adapters/playwright.js.map +1 -0
  662. package/dist/runner/aivision/cases.js +172 -0
  663. package/dist/runner/aivision/cases.js.map +1 -0
  664. package/dist/runner/aivision/cli.js +201 -0
  665. package/dist/runner/aivision/cli.js.map +1 -0
  666. package/dist/runner/aivision/index.js +281 -0
  667. package/dist/runner/aivision/index.js.map +1 -0
  668. package/dist/runner/aivision/server.js +394 -0
  669. package/dist/runner/aivision/server.js.map +1 -0
  670. package/dist/runner/aivision/shared.js +140 -0
  671. package/dist/runner/aivision/shared.js.map +1 -0
  672. package/dist/runner/artifact-cap.js +189 -0
  673. package/dist/runner/artifact-cap.js.map +1 -0
  674. package/dist/runner/artifacts.js +433 -0
  675. package/dist/runner/artifacts.js.map +1 -0
  676. package/dist/runner/command.js +564 -0
  677. package/dist/runner/command.js.map +1 -0
  678. package/dist/runner/copyfiles.js +221 -0
  679. package/dist/runner/copyfiles.js.map +1 -0
  680. package/dist/runner/engines.js +32 -0
  681. package/dist/runner/engines.js.map +1 -0
  682. package/dist/runner/evidence.js +114 -0
  683. package/dist/runner/evidence.js.map +1 -0
  684. package/dist/runner/host-env.js +160 -0
  685. package/dist/runner/host-env.js.map +1 -0
  686. package/dist/runner/index.js +1221 -0
  687. package/dist/runner/index.js.map +1 -0
  688. package/dist/runner/observe.js +603 -0
  689. package/dist/runner/observe.js.map +1 -0
  690. package/dist/runner/playwright-json.js +108 -0
  691. package/dist/runner/playwright-json.js.map +1 -0
  692. package/dist/runner/pwgen/exec.js +192 -0
  693. package/dist/runner/pwgen/exec.js.map +1 -0
  694. package/dist/runner/pwgen/explore.js +111 -0
  695. package/dist/runner/pwgen/explore.js.map +1 -0
  696. package/dist/runner/pwgen/index.js +423 -0
  697. package/dist/runner/pwgen/index.js.map +1 -0
  698. package/dist/runner/pwgen/propose.js +69 -0
  699. package/dist/runner/pwgen/propose.js.map +1 -0
  700. package/dist/runner/pwgen/source.js +632 -0
  701. package/dist/runner/pwgen/source.js.map +1 -0
  702. package/dist/runner/setup.js +222 -0
  703. package/dist/runner/setup.js.map +1 -0
  704. package/dist/runner/step-reporter.js +107 -0
  705. package/dist/runner/step-reporter.js.map +1 -0
  706. package/dist/runner/test-repo.js +326 -0
  707. package/dist/runner/test-repo.js.map +1 -0
  708. package/dist/runner/typed-value.js +46 -0
  709. package/dist/runner/typed-value.js.map +1 -0
  710. package/dist/runner/verify.js +133 -0
  711. package/dist/runner/verify.js.map +1 -0
  712. package/dist/runner/worktree.js +349 -0
  713. package/dist/runner/worktree.js.map +1 -0
  714. package/dist/sandbox.js +61 -0
  715. package/dist/sandbox.js.map +1 -0
  716. package/dist/server.js +262 -0
  717. package/dist/server.js.map +1 -0
  718. package/dist/shadow-week.js +126 -0
  719. package/dist/shadow-week.js.map +1 -0
  720. package/dist/shutdown.js +92 -0
  721. package/dist/shutdown.js.map +1 -0
  722. package/dist/slack/index.js +645 -0
  723. package/dist/slack/index.js.map +1 -0
  724. package/dist/stages/config.js +147 -0
  725. package/dist/stages/config.js.map +1 -0
  726. package/dist/stages/index.js +41 -0
  727. package/dist/stages/index.js.map +1 -0
  728. package/dist/suites.js +293 -0
  729. package/dist/suites.js.map +1 -0
  730. package/dist/target-url.js +116 -0
  731. package/dist/target-url.js.map +1 -0
  732. package/dist/test-runs.js +662 -0
  733. package/dist/test-runs.js.map +1 -0
  734. package/dist/ticket-keys.js +97 -0
  735. package/dist/ticket-keys.js.map +1 -0
  736. package/dist/ticket-supersede.js +105 -0
  737. package/dist/ticket-supersede.js.map +1 -0
  738. package/dist/tinybee/ed25519.js +99 -0
  739. package/dist/tinybee/ed25519.js.map +1 -0
  740. package/dist/tinybee/models-routes.js +19 -0
  741. package/dist/tinybee/models-routes.js.map +1 -0
  742. package/dist/tinybee/pair-payload.js +41 -0
  743. package/dist/tinybee/pair-payload.js.map +1 -0
  744. package/dist/tinybee/update-manifest.js +77 -0
  745. package/dist/tinybee/update-manifest.js.map +1 -0
  746. package/dist/tinybee/well-known.js +64 -0
  747. package/dist/tinybee/well-known.js.map +1 -0
  748. package/dist/tinybee/workflows/activity.js +23 -0
  749. package/dist/tinybee/workflows/activity.js.map +1 -0
  750. package/dist/tinybee/workflows/cron.js +210 -0
  751. package/dist/tinybee/workflows/cron.js.map +1 -0
  752. package/dist/tinybee/workflows/engine.js +371 -0
  753. package/dist/tinybee/workflows/engine.js.map +1 -0
  754. package/dist/tinybee/workflows/from-text.js +203 -0
  755. package/dist/tinybee/workflows/from-text.js.map +1 -0
  756. package/dist/tinybee/workflows/routes.js +189 -0
  757. package/dist/tinybee/workflows/routes.js.map +1 -0
  758. package/dist/tinybee/workflows/scheduler.js +100 -0
  759. package/dist/tinybee/workflows/scheduler.js.map +1 -0
  760. package/dist/tinybee/workflows/skills.js +227 -0
  761. package/dist/tinybee/workflows/skills.js.map +1 -0
  762. package/dist/tinybee/workflows/store.js +205 -0
  763. package/dist/tinybee/workflows/store.js.map +1 -0
  764. package/dist/tinybee/workflows/types.js +180 -0
  765. package/dist/tinybee/workflows/types.js.map +1 -0
  766. package/dist/tinybee/workflows/yaml.js +323 -0
  767. package/dist/tinybee/workflows/yaml.js.map +1 -0
  768. package/dist/true/api.js +495 -0
  769. package/dist/true/api.js.map +1 -0
  770. package/dist/true/client.js +292 -0
  771. package/dist/true/client.js.map +1 -0
  772. package/dist/true/clock.js +36 -0
  773. package/dist/true/clock.js.map +1 -0
  774. package/dist/true/events.js +125 -0
  775. package/dist/true/events.js.map +1 -0
  776. package/dist/true/hosts.js +98 -0
  777. package/dist/true/hosts.js.map +1 -0
  778. package/dist/true/index.js +29 -0
  779. package/dist/true/index.js.map +1 -0
  780. package/dist/true/key.js +54 -0
  781. package/dist/true/key.js.map +1 -0
  782. package/dist/true/line.js +69 -0
  783. package/dist/true/line.js.map +1 -0
  784. package/dist/true/mask.js +31 -0
  785. package/dist/true/mask.js.map +1 -0
  786. package/dist/true/names.js +114 -0
  787. package/dist/true/names.js.map +1 -0
  788. package/dist/true/outbox.js +233 -0
  789. package/dist/true/outbox.js.map +1 -0
  790. package/dist/true/replay.js +164 -0
  791. package/dist/true/replay.js.map +1 -0
  792. package/dist/true/settings.js +123 -0
  793. package/dist/true/settings.js.map +1 -0
  794. package/dist/true/status.js +189 -0
  795. package/dist/true/status.js.map +1 -0
  796. package/dist/true/sync-cases.js +222 -0
  797. package/dist/true/sync-cases.js.map +1 -0
  798. package/dist/true/sync-manual.js +295 -0
  799. package/dist/true/sync-manual.js.map +1 -0
  800. package/dist/true/sync-runs.js +109 -0
  801. package/dist/true/sync-runs.js.map +1 -0
  802. package/dist/true/worker.js +414 -0
  803. package/dist/true/worker.js.map +1 -0
  804. package/dist/types.js +32 -0
  805. package/dist/types.js.map +1 -0
  806. package/dist/updates/asks.js +38 -0
  807. package/dist/updates/asks.js.map +1 -0
  808. package/dist/updates/index.js +835 -0
  809. package/dist/updates/index.js.map +1 -0
  810. package/dist/updates/owner.js +24 -0
  811. package/dist/updates/owner.js.map +1 -0
  812. package/dist/updates/prefs.js +118 -0
  813. package/dist/updates/prefs.js.map +1 -0
  814. package/dist/updates/route.js +78 -0
  815. package/dist/updates/route.js.map +1 -0
  816. package/dist/updates/sources.js +345 -0
  817. package/dist/updates/sources.js.map +1 -0
  818. package/dist/updates/words.js +452 -0
  819. package/dist/updates/words.js.map +1 -0
  820. package/dist/version.js +53 -0
  821. package/dist/version.js.map +1 -0
  822. package/dist/view-compute.js +382 -0
  823. package/dist/view-compute.js.map +1 -0
  824. package/dist/view-spec.js +894 -0
  825. package/dist/view-spec.js.map +1 -0
  826. package/dist/web/apple-touch-icon.png +0 -0
  827. package/dist/web/assets/ApprovalCard-Br_z5z7G.css +1 -0
  828. package/dist/web/assets/ApprovalCard-CfGhGFo_.js +3 -0
  829. package/dist/web/assets/CardFromRef-BaTGmZ9c.js +2 -0
  830. package/dist/web/assets/CardFromRef-DO7i2uDd.css +1 -0
  831. package/dist/web/assets/Cases-8l_6UcLh.js +2 -0
  832. package/dist/web/assets/Cases-CpfED0uB.css +1 -0
  833. package/dist/web/assets/Chat-BKDiR0qt.js +3 -0
  834. package/dist/web/assets/Chat-DxjdyYUu.css +1 -0
  835. package/dist/web/assets/ConfirmDialog-CIMveD-b.js +2 -0
  836. package/dist/web/assets/ConnectSheet-Cm5vbZdR.js +4 -0
  837. package/dist/web/assets/Docs-HKGuY0WE.js +2 -0
  838. package/dist/web/assets/Docs-fodKx8S7.css +1 -0
  839. package/dist/web/assets/DotDetail-DskS3NWe.js +2 -0
  840. package/dist/web/assets/DotDetail-OWRSD_1x.css +1 -0
  841. package/dist/web/assets/DotStage-BZDclfzV.css +1 -0
  842. package/dist/web/assets/DotStage-CtMNzCN3.js +2 -0
  843. package/dist/web/assets/Dots-BqCsS34m.js +2 -0
  844. package/dist/web/assets/EvidencePack-DQ3kCJX4.js +2 -0
  845. package/dist/web/assets/EvidencePack-DV_9ETsA.css +1 -0
  846. package/dist/web/assets/FailedDeliveries-1F2kFcs9.js +2 -0
  847. package/dist/web/assets/FailedDeliveries-DXa2_VvI.css +1 -0
  848. package/dist/web/assets/Feed-5wiHRTFK.js +2 -0
  849. package/dist/web/assets/GfmView-C-NYaduC.js +4 -0
  850. package/dist/web/assets/ImpactList-C-RNuDB0.js +2 -0
  851. package/dist/web/assets/ImpactList-C612dFdw.css +1 -0
  852. package/dist/web/assets/Inbox-kc1bKVL_.js +2 -0
  853. package/dist/web/assets/JoinScreen-C3L4wmP3.js +2 -0
  854. package/dist/web/assets/JoinScreen-COeAUv5v.css +1 -0
  855. package/dist/web/assets/LeadDialog-N3Zul9Rw.js +8 -0
  856. package/dist/web/assets/LeadDialog-xyWjAuAS.css +1 -0
  857. package/dist/web/assets/ListeningLine-BRFNaczY.js +2 -0
  858. package/dist/web/assets/ListeningLine-pN8CgyiK.css +1 -0
  859. package/dist/web/assets/Mascots-pIyGpxtD.js +2 -0
  860. package/dist/web/assets/ModelChoices-BxO1zyyr.js +2 -0
  861. package/dist/web/assets/ModelChoices-DQJnVxfs.css +1 -0
  862. package/dist/web/assets/NewDotDialog-CDPUWmgN.js +2 -0
  863. package/dist/web/assets/NewDotDialog-DrPPqkSZ.css +1 -0
  864. package/dist/web/assets/NewTestRunDialog-BQBthVHE.js +2 -0
  865. package/dist/web/assets/NoBreakNames-Csxdqdt6.js +2 -0
  866. package/dist/web/assets/Pair-omex_K7H.js +2 -0
  867. package/dist/web/assets/PushSection-CmqHlE_2.js +2 -0
  868. package/dist/web/assets/ReleaseNotes-B6VqFewP.js +2 -0
  869. package/dist/web/assets/ReleaseNotes-DBJUL7Vm.css +1 -0
  870. package/dist/web/assets/ReportPage-BHS1xWEt.js +162 -0
  871. package/dist/web/assets/Reports-BEHnDIb1.js +2 -0
  872. package/dist/web/assets/ReviewChangeDialog-BVSt-cw-.js +2 -0
  873. package/dist/web/assets/ReviewChangeDialog-BbIlHRx0.css +1 -0
  874. package/dist/web/assets/Room-BoG_uge4.js +2 -0
  875. package/dist/web/assets/Room-evAfiW_e.css +1 -0
  876. package/dist/web/assets/RunActions-ZY7yf1AF.js +2 -0
  877. package/dist/web/assets/RunDetail-C8St20GQ.css +1 -0
  878. package/dist/web/assets/RunDetail-qADXjBvd.js +9 -0
  879. package/dist/web/assets/RunRow-BKEJv8ib.js +2 -0
  880. package/dist/web/assets/RunnerTry-Dgpp67-s.js +6 -0
  881. package/dist/web/assets/RunnerTry-DsweWOfO.css +1 -0
  882. package/dist/web/assets/Settings-fs-QJwNM.js +2 -0
  883. package/dist/web/assets/Setup-DI6hBEP-.js +2 -0
  884. package/dist/web/assets/TestRows-wh03T5Li.js +4 -0
  885. package/dist/web/assets/TestRun-DTOyF-Pv.js +2 -0
  886. package/dist/web/assets/TestRuns-ggDjm1Q8.js +2 -0
  887. package/dist/web/assets/TinyBeeAddLaptop-bc3pmh3T.js +2 -0
  888. package/dist/web/assets/TinyBeeAi-CkyOzhm5.js +2 -0
  889. package/dist/web/assets/TinyBeeConnections-B2K_P38u.js +2 -0
  890. package/dist/web/assets/TinyBeeHome-DSpxX5KS.js +3 -0
  891. package/dist/web/assets/TinyBeePlan-BamiLziu.js +2 -0
  892. package/dist/web/assets/TinyBeeSignup-vHcz0Wen.js +2 -0
  893. package/dist/web/assets/TinyBeeSkills-By9RLUse.js +2 -0
  894. package/dist/web/assets/TinyBeeWorkflow-DeNO7Jsh.js +2 -0
  895. package/dist/web/assets/TinyBeeWorkflowEditor-B6OGC2OT.js +2 -0
  896. package/dist/web/assets/TinyBeeWorkflows-BN4-h6lt.js +2 -0
  897. package/dist/web/assets/Trace-BBCI1aIB.css +1 -0
  898. package/dist/web/assets/Trace-aF92UWaH.js +2 -0
  899. package/dist/web/assets/UpdatesSection-CCWEbkue.js +2 -0
  900. package/dist/web/assets/Welcome-CCcy558Q.js +2 -0
  901. package/dist/web/assets/activity-C3qilrDm.css +1 -0
  902. package/dist/web/assets/activity-CBfTSxqw.js +2 -0
  903. package/dist/web/assets/authedMedia-BTYt_tyu.js +2 -0
  904. package/dist/web/assets/beeApi-BHW8Tx1s.js +2 -0
  905. package/dist/web/assets/data-CmXvx3Mj.js +2 -0
  906. package/dist/web/assets/engines-CfdJODFj.css +1 -0
  907. package/dist/web/assets/format-BBZ8vFyT.js +2 -0
  908. package/dist/web/assets/forms-Coc7p2UF.css +1 -0
  909. package/dist/web/assets/hivePush-DZJDokj7.js +2 -0
  910. package/dist/web/assets/impactModel-MY1OFuiP.js +2 -0
  911. package/dist/web/assets/index-B6qKm1NW.js +13 -0
  912. package/dist/web/assets/index-BvKyT1X7.css +1 -0
  913. package/dist/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  914. package/dist/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  915. package/dist/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  916. package/dist/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  917. package/dist/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  918. package/dist/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  919. package/dist/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  920. package/dist/web/assets/lead-BL_P_kri.css +1 -0
  921. package/dist/web/assets/model-BIQy5IVG.js +2 -0
  922. package/dist/web/assets/modelsApi-PUSySRQw.js +2 -0
  923. package/dist/web/assets/newDotModel-BaLLyw57.js +2 -0
  924. package/dist/web/assets/onboarding-bfKqnM4Z.css +1 -0
  925. package/dist/web/assets/phone-Rs7FoyG0.css +1 -0
  926. package/dist/web/assets/phoneApi-BG_1-M-D.js +3 -0
  927. package/dist/web/assets/releaseModel-EynYMVOC.js +2 -0
  928. package/dist/web/assets/releaseNotesModel-A1Rtv3cV.js +2 -0
  929. package/dist/web/assets/reports-BWaA1HhM.js +37 -0
  930. package/dist/web/assets/reports-DXwdWQsB.css +1 -0
  931. package/dist/web/assets/router-W6nZsQ33.js +2 -0
  932. package/dist/web/assets/runs-BY0Owv93.css +1 -0
  933. package/dist/web/assets/shadowWeekModel-BUUiww09.js +2 -0
  934. package/dist/web/assets/storage-yvL36AWt.js +2 -0
  935. package/dist/web/assets/store-IqrqHuFR.js +2 -0
  936. package/dist/web/assets/testruns-BzIZ0cR0.css +1 -0
  937. package/dist/web/assets/testruns-DyaqX4ji.js +2 -0
  938. package/dist/web/assets/true-9Q68cs5b.js +2 -0
  939. package/dist/web/assets/true-DPGBpoir.css +1 -0
  940. package/dist/web/assets/ui-DcmhYyYp.js +13 -0
  941. package/dist/web/assets/ui-DxEDgbIx.css +1 -0
  942. package/dist/web/assets/usePauseDot-BV60ZVy1.js +2 -0
  943. package/dist/web/assets/usePauseDot-NrSzMI1C.css +1 -0
  944. package/dist/web/assets/usePush-Dsm-TjlG.js +2 -0
  945. package/dist/web/assets/useRefChips-CbafNx59.js +68 -0
  946. package/dist/web/assets/useRestoreDot-D0B6W82H.js +2 -0
  947. package/dist/web/assets/view-worker-pyeVAhUT.js +2 -0
  948. package/dist/web/assets/workflowUi-QYsd3n9J.js +2 -0
  949. package/dist/web/favicon.png +0 -0
  950. package/dist/web/icon-192.png +0 -0
  951. package/dist/web/icon-512.png +0 -0
  952. package/dist/web/icon-maskable-512.png +0 -0
  953. package/dist/web/index.html +45 -0
  954. package/dist/web/manifest.webmanifest +23 -0
  955. package/dist/web/mascots/argus-attention.svg +7 -0
  956. package/dist/web/mascots/argus-ok.svg +7 -0
  957. package/dist/web/mascots/argus-outage.svg +7 -0
  958. package/dist/web/mascots/argus-running.svg +7 -0
  959. package/dist/web/mascots/bolt-attention.svg +7 -0
  960. package/dist/web/mascots/bolt-ok.svg +7 -0
  961. package/dist/web/mascots/bolt-outage.svg +7 -0
  962. package/dist/web/mascots/bolt-running.svg +7 -0
  963. package/dist/web/mascots/echo-attention.svg +7 -0
  964. package/dist/web/mascots/echo-ok.svg +7 -0
  965. package/dist/web/mascots/echo-outage.svg +7 -0
  966. package/dist/web/mascots/echo-running.svg +7 -0
  967. package/dist/web/mascots/lens-attention.svg +7 -0
  968. package/dist/web/mascots/lens-ok.svg +7 -0
  969. package/dist/web/mascots/lens-outage.svg +7 -0
  970. package/dist/web/mascots/lens-running.svg +7 -0
  971. package/dist/web/mascots/moss-attention.svg +7 -0
  972. package/dist/web/mascots/moss-ok.svg +7 -0
  973. package/dist/web/mascots/moss-outage.svg +7 -0
  974. package/dist/web/mascots/moss-running.svg +7 -0
  975. package/dist/web/mascots/nit-attention.svg +7 -0
  976. package/dist/web/mascots/nit-ok.svg +7 -0
  977. package/dist/web/mascots/nit-outage.svg +7 -0
  978. package/dist/web/mascots/nit-running.svg +7 -0
  979. package/dist/web/mascots/nova-attention.svg +7 -0
  980. package/dist/web/mascots/nova-ok.svg +7 -0
  981. package/dist/web/mascots/nova-outage.svg +7 -0
  982. package/dist/web/mascots/nova-running.svg +7 -0
  983. package/dist/web/mascots/penn-attention.svg +7 -0
  984. package/dist/web/mascots/penn-ok.svg +7 -0
  985. package/dist/web/mascots/penn-outage.svg +7 -0
  986. package/dist/web/mascots/penn-running.svg +7 -0
  987. package/dist/web/mascots/pip-attention.svg +7 -0
  988. package/dist/web/mascots/pip-ok.svg +7 -0
  989. package/dist/web/mascots/pip-outage.svg +7 -0
  990. package/dist/web/mascots/pip-running.svg +7 -0
  991. package/dist/web/mascots/ward-attention.svg +7 -0
  992. package/dist/web/mascots/ward-ok.svg +7 -0
  993. package/dist/web/mascots/ward-outage.svg +7 -0
  994. package/dist/web/mascots/ward-running.svg +7 -0
  995. package/dist/web/sw.js +98 -0
  996. package/dist/webhook-seen.js +53 -0
  997. package/dist/webhook-seen.js.map +1 -0
  998. package/dist/webhooks.js +625 -0
  999. package/dist/webhooks.js.map +1 -0
  1000. package/dist/zip.js +63 -0
  1001. package/dist/zip.js.map +1 -0
  1002. package/docs/API-V2.md +532 -0
  1003. package/docs/CHAT-EVAL.md +130 -0
  1004. package/docs/CHAT-UI.md +416 -0
  1005. package/docs/CONNECTIONS.md +89 -0
  1006. package/docs/DATA-MODEL.md +518 -0
  1007. package/docs/DEPLOY.md +577 -0
  1008. package/docs/DOGFOOD-LEDGER.md +81 -0
  1009. package/docs/GITHUB.md +148 -0
  1010. package/docs/INTEGRATION-MODEL.md +200 -0
  1011. package/docs/LEAD-DOT.md +553 -0
  1012. package/docs/LIST-PATTERNS.md +52 -0
  1013. package/docs/MULTI-REPO.md +766 -0
  1014. package/docs/PARITY.md +276 -0
  1015. package/docs/PR-REVIEW-QA.md +1266 -0
  1016. package/docs/PR-REVIEW.md +357 -0
  1017. package/docs/SANDBOX.md +320 -0
  1018. package/docs/SETUP.md +776 -0
  1019. package/docs/SLACK.md +80 -0
  1020. package/docs/STAGES.md +619 -0
  1021. package/docs/TRACE-MAP.md +89 -0
  1022. package/docs/TRACKERS.md +225 -0
  1023. package/docs/VOICE.md +36 -0
  1024. package/docs/tinybee/00-README.md +45 -0
  1025. package/docs/tinybee/01-PRD.md +216 -0
  1026. package/docs/tinybee/02-architecture-pairing.md +199 -0
  1027. package/docs/tinybee/03-local-agent.md +222 -0
  1028. package/docs/tinybee/04-local-setup-ui.md +128 -0
  1029. package/docs/tinybee/05-mobile-app-ux.md +199 -0
  1030. package/docs/tinybee/06-environments-and-delivery.md +148 -0
  1031. package/docs/tinybee/07-coding-agent-handoff.md +116 -0
  1032. package/docs/tinybee/OPEN-QUESTIONS.md +26 -0
  1033. package/docs/tinybee/PROGRESS.md +134 -0
  1034. package/package.json +111 -4
@@ -0,0 +1,966 @@
1
+ // The health panel of a dot (KAT-394). GET /api/dots/:id/health answers one question at a glance: does this dot need me?
2
+ // Everything comes from rows the dot already stores, so the endpoint calls no model and no tracker.
3
+ //
4
+ // Two layers. `readHealthInput` reads the rows in one pass. `computeHealth` is pure over that input and a fixed clock, so every
5
+ // status rule, every suggestion rule, and every Not set up path has a unit test that builds an input by hand.
6
+ //
7
+ // Status (first match wins):
8
+ // not_watching the dot cannot watch: it is paused or archived (a person chose that), the AI sign-in it uses expired, GitHub does not
9
+ // answer or turned the sign-in down, a poll alarm is up, or there is nothing to watch (no repo, tracker or QA site).
10
+ // It says nothing else: a QA site that is down shows on its own tile. "No check in 24 hours" is not a status, because a
11
+ // quiet repo on a weekend is normal; the strip carries it as a line ("Quiet: no check in 24 h").
12
+ // failing a site check is down, or the latest merge found a bug (a verdict of product failure or test failure, or failed tests that
13
+ // are not an environment failure or flaky). A merge whose verdict is "needs attention" is not a confirmed bug.
14
+ // needs_you something waits for a person's OK, a ticket review left questions, or the latest merge needs a look.
15
+ // healthy none of the above.
16
+ // Suggestion (first rule that matches wins, ties inside a rule are listed in each function):
17
+ // 1 a ticket in the nearest open release has no test cases 2 a bug came back after its fix
18
+ // 3 a flaky test failed 3 times this week 4 the latest merge needs a look
19
+ // 5 the AI calls of the day are 80 percent used
20
+ import { FAMILY_LABEL, listUnavailable } from './agent/model-availability.js';
21
+ import { defaultFamily } from './agent/llm.js';
22
+ import { isLive } from './agent/approvals.js';
23
+ import { ruleFor } from './agent/rules.js';
24
+ import { db, get, list } from './db.js';
25
+ import { deliveryOf } from './delivery.js';
26
+ import { finishedWithTests } from './chat-stats.js';
27
+ import { defectTarget } from './flows/defects.js';
28
+ import { isLead, leadHealth, leadOf, teamIds } from './lead.js';
29
+ import { pollFailureFor } from './monitor/pollers.js';
30
+ import { capState, clockText } from './model-usage.js';
31
+ import { watchRepos } from './repos.js';
32
+ import { buildAll } from './reports/traceability.js';
33
+ import { sandboxOn } from './sandbox.js';
34
+ import { SHADOW_DAYS, shadowExpired } from './shadow-week.js';
35
+ import { stageOn } from './stages/config.js';
36
+ import { NOTHING_TO_WATCH, NO_CHECK_SITE, NO_CHECK_YET, SITE_CHECKS_OFF, SITE_CHECKS_SANDBOX } from './types.js';
37
+ const MIN_MS = 60_000;
38
+ const HOUR_MS = 3_600_000;
39
+ const DAY_MS = 86_400_000;
40
+ const WEEK_MS = 7 * DAY_MS;
41
+ /** A dot with no check in this long is quiet: the strip says so, and the status does not change. */
42
+ export const QUIET_LIMIT_MS = DAY_MS;
43
+ /** The share of the daily calls at which the dot suggests raising the limit. */
44
+ export const AI_NEAR_LIMIT = 0.8;
45
+ /** Results of one test in a week at which the dot suggests fixing it. */
46
+ export const FLAKY_SUGGEST_AT = 3;
47
+ /** The jobs that count as a check of the product. The digest, the repo scan and test design are not checks. */
48
+ export const CHECK_KINDS = ['pr_check', 'post_merge', 'ci_failure', 'rerun', 'nightly_run', 'ticket_review', 'outage', 'recovery'];
49
+ /** The checks of the product's tests, which the Checks tile counts. Ticket reviews have their own tile. */
50
+ const TEST_CHECK_KINDS = new Set(['pr_check', 'post_merge', 'ci_failure', 'rerun', 'nightly_run']);
51
+ /** The checks that read the default branch, which says whether a failing test needs a fix. */
52
+ const MAIN_KINDS = new Set(['post_merge', 'ci_failure', 'rerun', 'nightly_run']);
53
+ /** A verdict that confirms a bug. "needs attention" does not: nobody has said it is a bug, so it reads Needs you, never Failing. */
54
+ const BUG_OUTCOMES = new Set(['product_failure', 'test_failure']);
55
+ // ---------- words ----------
56
+ /** "just now", "2 min ago", "5 h ago", "3 d ago". */
57
+ export function ago(iso, now) {
58
+ const ms = Math.max(0, now - Date.parse(iso));
59
+ if (ms < 45_000)
60
+ return 'just now';
61
+ if (ms < HOUR_MS)
62
+ return `${Math.max(1, Math.round(ms / MIN_MS))} min ago`;
63
+ if (ms < DAY_MS)
64
+ return `${Math.round(ms / HOUR_MS)} h ago`;
65
+ return `${Math.round(ms / DAY_MS)} d ago`;
66
+ }
67
+ /** A wait in a few characters: "12 min", "5 h", "2 d". */
68
+ export function span(ms) {
69
+ if (ms < HOUR_MS)
70
+ return `${Math.max(1, Math.round(ms / MIN_MS))} min`;
71
+ if (ms < DAY_MS)
72
+ return `${Math.round(ms / HOUR_MS)} h`;
73
+ return `${Math.round(ms / DAY_MS)} d`;
74
+ }
75
+ const NBSP = ' ';
76
+ const n = (count, one, many = `${one}s`) => `${count}${NBSP}${count === 1 ? one : many}`;
77
+ const at = (iso) => Date.parse(iso);
78
+ // ---------- tiles ----------
79
+ const link = (label, href) => ({ label, href });
80
+ /** An app link to a page of the dot: `#/trace?dot=<id>&milestone=...`. */
81
+ const page = (route, dotId, more = {}) => `#/${route}?${new URLSearchParams({ dot: dotId, ...more }).toString()}`;
82
+ const setUp = (setup) => ({ label: 'Set up', setup });
83
+ const NOT_SET_UP = 'Not set up';
84
+ function tile(title, line, action, facts = {}) {
85
+ return { title, state: 'ready', line, action, facts };
86
+ }
87
+ const notSetUp = (title, setup) => ({
88
+ title,
89
+ state: 'not_set_up',
90
+ line: NOT_SET_UP,
91
+ action: setUp(setup),
92
+ facts: {},
93
+ });
94
+ const broken = (title) => ({ title, state: 'error', line: 'Could not read this right now.', action: null, facts: {} });
95
+ function reviewTile(i) {
96
+ const title = 'To review';
97
+ const waiting = i.pending.length;
98
+ // Nothing waits, so there is nothing to open: no button.
99
+ if (!waiting)
100
+ return tile(title, 'Nothing is waiting', null, { waiting: 0 });
101
+ const oldest = Math.min(...i.pending.map((a) => at(a.createdAt)));
102
+ return tile(title, `${waiting} waiting, oldest ${span(i.now - oldest)}`, link('Review', '#/inbox'), {
103
+ waiting,
104
+ oldestMinutes: Math.round((i.now - oldest) / MIN_MS),
105
+ });
106
+ }
107
+ /**
108
+ * A draft that files a bug. The main defect path drafts a new bug as `issue_create` with a bug signature (src/flows/defectDraft.ts);
109
+ * `file_defect` is the older kind. An `issue_create` with no signature is a ticket someone asked for, not a bug.
110
+ */
111
+ export function isBugDraft(action) {
112
+ if (action.kind === 'issue_create')
113
+ return typeof action.meta?.bugSig === 'string' && action.meta.bugSig !== '';
114
+ return action.kind === 'file_defect';
115
+ }
116
+ /** Every bug the dot filed: approved drafts, drafts that went out on their own, each once. */
117
+ export function filedBugs(i) {
118
+ const out = new Map();
119
+ for (const a of i.bugDrafts) {
120
+ if (a.status !== 'executed')
121
+ continue;
122
+ const meta = a.action.meta ?? {};
123
+ const key = typeof meta.filedKey === 'string' && meta.filedKey ? meta.filedKey : a.id;
124
+ const url = typeof meta.filedUrl === 'string' && meta.filedUrl ? meta.filedUrl : undefined;
125
+ out.set(key.toLowerCase(), {
126
+ key,
127
+ title: a.action.title,
128
+ at: a.executedAt ?? a.createdAt,
129
+ ...(url ? { url } : {}),
130
+ closed: i.closedKeys.has(key.toLowerCase()),
131
+ });
132
+ }
133
+ for (const b of i.autoFiled)
134
+ if (!out.has(b.key.toLowerCase()))
135
+ out.set(b.key.toLowerCase(), { ...b, closed: b.closed || i.closedKeys.has(b.key.toLowerCase()) });
136
+ return [...out.values()];
137
+ }
138
+ function bugsTile(i) {
139
+ const title = 'Bugs';
140
+ if (!i.dot.canFileBugs)
141
+ return notSetUp(title, 'tracker');
142
+ const filed = filedBugs(i);
143
+ // A bug counts as open until the dot sees its issue closed. GitHub issues say so; Jira and Linear bugs stay open here.
144
+ const open = filed.filter((b) => !b.closed);
145
+ const fresh = open.filter((b) => i.now - at(b.at) <= WEEK_MS);
146
+ const action = link('See bugs', page('trace', i.dot.id));
147
+ // A bug the dot found and drafted waits in To review until a person files it. Without this line the Checks tile says "found a bug"
148
+ // next to a Bugs tile that says none, and the reader cannot tell where the bug went.
149
+ const waiting = i.pending.filter((a) => isBugDraft(a.action)).length;
150
+ const draft = waiting ? `${waiting}\u00a0${waiting === 1 ? 'draft waits' : 'drafts wait'} in To review` : '';
151
+ const facts = { open: open.length, new: fresh.length, waiting };
152
+ if (!open.length)
153
+ return tile(title, draft ? `None open, ${draft}` : 'None open', null, facts);
154
+ const tail = [fresh.length ? `${fresh.length} new this week` : '', draft].filter(Boolean).join(', ');
155
+ return tile(title, `${open.length}\u00a0open${tail ? `, ${tail}` : ''}`, action, facts);
156
+ }
157
+ /** How one check ended, in the words of a tester. A check still running is not counted. */
158
+ function outcomeOf(run) {
159
+ if (run.status === 'running' || run.status === 'cancelled')
160
+ return null;
161
+ const o = run.verdict?.outcome;
162
+ if (o && BUG_OUTCOMES.has(o))
163
+ return 'bug';
164
+ if (o === 'needs_attention')
165
+ return 'look';
166
+ // A merge whose tests failed and which got no verdict still found a bug: the header says so, so the tile says so too.
167
+ if (run.kind === 'post_merge' && !run.verdict && run.status === 'failed' && (run.report?.failed ?? 0) > 0)
168
+ return 'bug';
169
+ // A check that skipped its tests on purpose (Run tests is off for the repo, a docs only change) still reviewed the change and ended
170
+ // with a pass: it finished. Every other way of not running the tests proved nothing.
171
+ const notRun = run.report?.notRun && run.report.notRun.stage !== 'skipped';
172
+ if (run.status === 'failed' || run.status === 'interrupted' || o === 'environment_failure' || notRun || run.report?.interrupted)
173
+ return 'unchecked';
174
+ if (o === 'pass' || o === 'info' || o === 'flaky')
175
+ return 'passed';
176
+ // Done with no verdict: it ran, and said nothing about the product.
177
+ return run.verdict ? 'passed' : 'unchecked';
178
+ }
179
+ /** Whether a finished check found a bug: a verdict of product or test failure, or a merge whose tests failed with no verdict. The lead's release call reads it. */
180
+ export function foundBug(run) {
181
+ return outcomeOf(run) === 'bug';
182
+ }
183
+ /** Whether a run is one of the checks that test a change (a pull request, a merge, a CI failure, a rerun). */
184
+ function isTestCheck(run) {
185
+ return TEST_CHECK_KINDS.has(run.kind);
186
+ }
187
+ /**
188
+ * A run that finished a check: it ended well and looked at the product. A run that failed, was interrupted, was cancelled, is still
189
+ * running, or could not run its tests (an environment failure, no tests run, a fork pull request it skipped) proved nothing about
190
+ * the dot. A check that tests a change counts only when the Checks tile counts it as passed, a bug or a look, so the status, the strip
191
+ * and the tile never disagree about one run.
192
+ */
193
+ export function finishedCheck(run) {
194
+ if (isTestCheck(run)) {
195
+ const o = outcomeOf(run);
196
+ return o === 'passed' || o === 'bug' || o === 'look';
197
+ }
198
+ if (run.status !== 'done')
199
+ return false;
200
+ return run.verdict?.outcome !== 'environment_failure' && !run.report?.notRun && !run.report?.interrupted;
201
+ }
202
+ function checksTile(i) {
203
+ const title = 'Checks this week';
204
+ const action = link('Open', page('feed', i.dot.id));
205
+ const week = i.runs.filter((r) => isTestCheck(r) && i.now - at(r.createdAt) <= WEEK_MS);
206
+ const running = week.filter((r) => r.status === 'running').length;
207
+ const counts = { passed: 0, bug: 0, look: 0, unchecked: 0 };
208
+ // A merge that needs a look is named as one, and only the latest merge is: it is the one check that waits for a person on the default
209
+ // branch, the same run the status and the suggestion read. A CI failure or a rerun that needs a look says "needs a look" under its own
210
+ // words. An older merge that needs a look and that a newer merge replaced waits for no one: it still counts as a check of the week, under
211
+ // "replaced by a newer merge", so the total matches the checks that ran.
212
+ const latest = i.lastMerge;
213
+ let lookMerge = 0;
214
+ let replaced = 0;
215
+ for (const r of week) {
216
+ if (r.kind === 'post_merge' && latest && r.id !== latest.id && outcomeOf(r) === 'look') {
217
+ replaced++;
218
+ continue;
219
+ }
220
+ const o = outcomeOf(r);
221
+ if (o)
222
+ counts[o]++;
223
+ if (o === 'look' && latest && r.id === latest.id && mergeOutcome(i) === 'look')
224
+ lookMerge++;
225
+ }
226
+ const total = counts.passed + counts.bug + counts.look + counts.unchecked + replaced;
227
+ const facts = { total, ...counts, running, lookMerge, replaced };
228
+ // A dot with no repo cannot check a pull request, so a zero would say "quiet week" about something that cannot happen. A repo with
229
+ // Run tests off still gets a review of each pull request, and a review is a check.
230
+ if (!total && !running && !i.dot.hasRepo)
231
+ return notSetUp(title, 'repo');
232
+ if (!total && !running)
233
+ return tile(title, 'No checks this week', null, facts);
234
+ if (!total)
235
+ return tile(title, `${running} running now`, action, facts);
236
+ // Checks that all ended without an answer say what the strip says ("none of them finished"), not a count of checks the dot made.
237
+ if (!counts.passed && !counts.bug && !counts.look && !replaced)
238
+ return tile(title, `${n(counts.unchecked, 'check')} started, none finished${running ? `, ${running} running` : ''}`, action, facts);
239
+ const parts = [
240
+ counts.passed ? `${counts.passed} passed` : '',
241
+ counts.bug ? `${counts.bug} found a bug` : '',
242
+ lookMerge ? `${lookMerge} ${lookMerge === 1 ? 'merge needs' : 'merges need'} a look` : '',
243
+ counts.look - lookMerge ? `${counts.look - lookMerge} ${counts.look - lookMerge === 1 ? 'needs' : 'need'} a look` : '',
244
+ replaced ? `${replaced} replaced by a newer merge` : '',
245
+ counts.unchecked ? `${counts.unchecked} could not check` : '',
246
+ running ? `${running} running` : '',
247
+ ].filter(Boolean);
248
+ return tile(title, `${n(total, 'check')}: ${parts.join(', ')}`, action, facts);
249
+ }
250
+ // ---- release
251
+ const DATE_IN_TITLE = /(\d{4})[-./](\d{1,2})(?:[-./](\d{1,2}))?/;
252
+ /** The day a milestone title names ("2026-10-28", "2026.11"), or null. A month alone means the end of that month. */
253
+ export function milestoneDate(title) {
254
+ const m = DATE_IN_TITLE.exec(title);
255
+ if (!m)
256
+ return null;
257
+ const y = Number(m[1]);
258
+ const mo = Number(m[2]);
259
+ if (mo < 1 || mo > 12)
260
+ return null;
261
+ const d = m[3] ? Number(m[3]) : new Date(y, mo, 0).getDate();
262
+ const out = new Date(y, mo - 1, d).getTime();
263
+ return Number.isFinite(out) ? out : null;
264
+ }
265
+ function startOfDay(t) {
266
+ const d = new Date(t);
267
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
268
+ }
269
+ /**
270
+ * The release the panel talks about: the nearest milestone that still has a ticket not passing. A milestone with a date at or after
271
+ * today comes first, the soonest one. When every dated one is past, the latest. Milestones with no date follow in the tracker's
272
+ * order. null when no milestone has work left.
273
+ */
274
+ export function nearestOpenMilestone(rows, now, scope) {
275
+ const allowed = scope?.length ? new Set(scope.map((s) => s.toLowerCase())) : null;
276
+ const open = [];
277
+ for (const r of rows) {
278
+ if (!r.milestone || (allowed && !allowed.has(r.milestone.toLowerCase())))
279
+ continue;
280
+ if (r.statusCode !== 'passing' && !open.includes(r.milestone))
281
+ open.push(r.milestone);
282
+ }
283
+ if (!open.length)
284
+ return null;
285
+ const today = startOfDay(now);
286
+ const dated = open.map((m) => ({ m, d: milestoneDate(m) })).filter((x) => x.d !== null);
287
+ const coming = dated.filter((x) => x.d >= today).sort((a, b) => a.d - b.d || a.m.localeCompare(b.m, undefined, { numeric: true }));
288
+ if (coming.length)
289
+ return coming[0].m;
290
+ const past = dated.sort((a, b) => b.d - a.d || a.m.localeCompare(b.m, undefined, { numeric: true }));
291
+ if (past.length)
292
+ return past[0].m;
293
+ return open[0];
294
+ }
295
+ const releaseTitle = (milestone) => (/^release\b/i.test(milestone) ? milestone : `Release ${milestone}`);
296
+ function releaseTile(i) {
297
+ if (!i.trace || !i.trace.rows.some((r) => r.milestone))
298
+ return notSetUp('Release', 'tracker');
299
+ const milestone = nearestOpenMilestone(i.trace.rows, i.now, i.milestoneScope);
300
+ if (!milestone)
301
+ return tile('Release', 'No release is open', link('Open report', page('trace', i.dot.id)), { total: 0 });
302
+ const rows = i.trace.rows.filter((r) => r.milestone === milestone);
303
+ const tested = rows.filter((r) => r.statusCode !== 'not_tested').length;
304
+ return tile(releaseTitle(milestone), `${tested} of ${n(rows.length, 'ticket')} tested`, link('Open report', page('trace', i.dot.id, { milestone })), {
305
+ milestone,
306
+ tested,
307
+ total: rows.length,
308
+ });
309
+ }
310
+ // ---- site
311
+ /**
312
+ * A dot whose only source is a QA site, when site checks cannot run for it: the watcher is off (`site_off`) or the server is in sandbox
313
+ * mode (`site_sandbox`). With a repo or a tracker the dot still watches, so this is null. The strip, the status and the QA site tile
314
+ * all read this one answer.
315
+ */
316
+ function siteOnlyBlock(i) {
317
+ if (!i.dot.probes || i.dot.hasRepo || i.dot.hasTracker)
318
+ return null;
319
+ if (i.dot.siteChecks === 'sandbox')
320
+ return 'site_sandbox';
321
+ return i.dot.siteChecks === 'off' ? 'site_off' : null;
322
+ }
323
+ function siteTile(i) {
324
+ const title = 'QA site';
325
+ if (!i.dot.probes)
326
+ return notSetUp(title, 'site');
327
+ // No site check can run, so the tile says why, in the words of the strip, and offers no Check now.
328
+ const block = siteOnlyBlock(i);
329
+ if (block)
330
+ return tile(title, block === 'site_off' ? SITE_CHECKS_OFF : SITE_CHECKS_SANDBOX, null, { sites: i.dot.probes });
331
+ // The button shows only where the server would run it.
332
+ const action = i.dot.canSiteCheck ? { label: 'Check now', run: 'site_check' } : null;
333
+ const rows = i.probeRows;
334
+ if (!rows.length)
335
+ return tile(title, 'Waiting for the first check', action, { sites: i.dot.probes });
336
+ const total = Math.max(i.dot.probes, rows.length);
337
+ const down = rows.filter((r) => r.down || r.status === 'down');
338
+ const failing = rows.filter((r) => !r.down && r.status === 'failing');
339
+ const checked = rows.map((r) => r.lastCheckedAt).filter((x) => !!x);
340
+ const newest = checked.sort().at(-1);
341
+ const when = newest ? `, ${ago(newest, i.now)}` : '';
342
+ // After a time of "down for", a bare "6 min ago" reads as when it went down: a site that is not up says "checked".
343
+ const checkedAgo = newest ? `, checked ${ago(newest, i.now)}` : '';
344
+ const facts = { sites: total, down: down.length, failing: failing.length };
345
+ if (down.length) {
346
+ if (total === 1) {
347
+ const since = down[0].downSince;
348
+ return tile(title, `Down${since ? ` for ${span(Math.max(0, i.now - at(since)))}` : ''}${checkedAgo}`, action, facts);
349
+ }
350
+ return tile(title, `${down.length} of ${n(total, 'site')} down${checkedAgo}`, action, facts);
351
+ }
352
+ if (failing.length) {
353
+ const misses = Math.max(...failing.map((r) => r.consecutiveFailures));
354
+ return tile(title, total === 1
355
+ ? `Not answering, ${n(misses, 'miss', 'misses')} in a row${checkedAgo}`
356
+ : `${failing.length} of ${n(total, 'site')} not answering${checkedAgo}`, action, facts);
357
+ }
358
+ const slow = Math.max(0, ...rows.map((r) => r.lastLatencyMs ?? 0));
359
+ const speed = slow ? `, ${slow}${NBSP}ms` : '';
360
+ return tile(title, total === 1 ? `Up${speed}${when}` : `All ${total} up${slow ? `, slowest ${slow}${NBSP}ms` : ''}${when}`, action, {
361
+ ...facts,
362
+ latencyMs: slow || null,
363
+ });
364
+ }
365
+ /** Each test of the checks of the last 7 days, with how often it was flaky or failed. */
366
+ export function testStats(runs, now) {
367
+ const stats = new Map();
368
+ const week = runs.filter((r) => now - at(r.createdAt) <= WEEK_MS);
369
+ for (const { run, report } of finishedWithTests(week)) {
370
+ const when = at(run.createdAt);
371
+ for (const c of report.cases) {
372
+ if (c.status === 'skipped')
373
+ continue;
374
+ const key = `${c.file}::${c.title}`;
375
+ let s = stats.get(key);
376
+ if (!s)
377
+ stats.set(key, (s = { file: c.file, title: c.title, flaky: 0, failed: 0, latestAt: 0, latestRunId: run.id, main: null, badRunId: '' }));
378
+ const failing = c.status === 'failed' || c.status === 'timedOut';
379
+ if (c.status === 'flaky')
380
+ s.flaky++;
381
+ if (failing)
382
+ s.failed++;
383
+ if (when >= s.latestAt) {
384
+ s.latestAt = when;
385
+ s.latestRunId = run.id;
386
+ }
387
+ if ((c.status === 'flaky' || failing) && (!s.badRunId || when >= s.latestAt))
388
+ s.badRunId = run.id;
389
+ if (MAIN_KINDS.has(run.kind) && (!s.main || when >= s.main.at))
390
+ s.main = { at: when, failing };
391
+ }
392
+ }
393
+ return [...stats.values()];
394
+ }
395
+ function testsTile(i) {
396
+ const title = 'Tests';
397
+ const stats = testStats(i.runs, i.now);
398
+ const needFix = stats.filter((s) => s.main?.failing);
399
+ const flaky = stats.filter((s) => s.flaky > 0 && !s.main?.failing);
400
+ const worst = [...needFix, ...flaky].sort((a, b) => b.latestAt - a.latestAt)[0];
401
+ const action = link('See tests', worst ? `#/runs/${encodeURIComponent(worst.badRunId || worst.latestRunId)}` : page('cases', i.dot.id));
402
+ const facts = { flaky: flaky.length, needsFix: needFix.length, tested: stats.length };
403
+ if (!stats.length && !i.dot.canRunTests)
404
+ return notSetUp(title, 'repo');
405
+ if (!stats.length)
406
+ return tile(title, 'No test results this week', null, facts);
407
+ if (!flaky.length && !needFix.length)
408
+ return tile(title, 'None flaky, none need a fix', action, facts);
409
+ const parts = [
410
+ flaky.length ? `${flaky.length} flaky` : '',
411
+ needFix.length ? `${needFix.length} ${needFix.length === 1 ? 'needs' : 'need'} a fix` : '',
412
+ ].filter(Boolean);
413
+ return tile(title, parts.join(', '), action, facts);
414
+ }
415
+ // ---- tickets
416
+ const ticketKeyOf = (r) => r.trigger?.inlineTicket?.key ?? r.trigger?.ticket?.key ?? r.id;
417
+ function ticketsTile(i) {
418
+ const title = 'Tickets';
419
+ const reviews = i.runs.filter((r) => r.kind === 'ticket_review' && r.status !== 'running' && i.now - at(r.createdAt) <= WEEK_MS);
420
+ if (!i.dot.hasTracker && !reviews.length)
421
+ return notSetUp(title, 'tracker');
422
+ const action = link('See tickets', page('trace', i.dot.id));
423
+ // The newest review of a ticket decides whether it still has questions.
424
+ const latest = new Map();
425
+ for (const r of reviews) {
426
+ const prev = latest.get(ticketKeyOf(r));
427
+ if (!prev || at(r.createdAt) > at(prev.createdAt))
428
+ latest.set(ticketKeyOf(r), r);
429
+ }
430
+ const reviewed = latest.size;
431
+ const asking = [...latest.values()].filter((r) => (r.verdict?.openQuestions?.length ?? 0) > 0).length;
432
+ const facts = { reviewed, needAnswers: asking };
433
+ if (!reviewed)
434
+ return tile(title, 'No tickets reviewed this week', null, facts);
435
+ return tile(title, `${reviewed} reviewed${asking ? `, ${asking} ${asking === 1 ? 'needs' : 'need'} answers` : ', none need answers'}`, action, facts);
436
+ }
437
+ // ---- AI use
438
+ function aiTile(i) {
439
+ const title = 'AI use today';
440
+ // A dot with no daily limit still uses AI: "Not set up" would read as if no AI engine worked, so the tile says what is missing.
441
+ if (!i.cap)
442
+ return tile(title, 'No daily limit', { label: 'Set a limit', setup: 'cap' }, {});
443
+ const left = Math.max(0, i.cap.cap - i.cap.used);
444
+ const action = { label: 'Details', focus: 'model_usage' };
445
+ const facts = { cap: i.cap.cap, used: i.cap.used, left };
446
+ if (i.cap.reached)
447
+ return tile(title, `No AI calls left, back at ${clockText(new Date(i.cap.resetsAt))}`, action, facts);
448
+ return tile(title, `${i.cap.used} of ${i.cap.cap} AI calls used`, action, facts);
449
+ }
450
+ // ---------- status ----------
451
+ const checkRef = (r) => {
452
+ const t = r.trigger;
453
+ if (r.kind === 'ticket_review')
454
+ return t?.inlineTicket?.key ?? t?.ticket?.key ?? null;
455
+ if (t?.pr)
456
+ return `PR #${t.pr.number}`;
457
+ if (t?.push?.ref)
458
+ return t.push.ref.replace(/^refs\/heads\//, '');
459
+ return null;
460
+ };
461
+ function hostOf(url) {
462
+ try {
463
+ return new URL(url).host;
464
+ }
465
+ catch {
466
+ return url;
467
+ }
468
+ }
469
+ /**
470
+ * The check the strip names, and the time of the newest check of any kind. A site check runs every minute, so it would hide the
471
+ * PR or the ticket the dot worked on: the strip names the newest run while it is under a day old, and the site check otherwise.
472
+ * The 24 hour rule reads the newest check of either kind.
473
+ */
474
+ function lastCheckOf(i) {
475
+ const run = i.lastCheckRun;
476
+ const probe = i.probeRows.filter((r) => r.lastCheckedAt).sort((a, b) => b.lastCheckedAt.localeCompare(a.lastCheckedAt))[0];
477
+ const runAt = run ? at(run.createdAt) : null;
478
+ const probeAt = probe ? at(probe.lastCheckedAt) : null;
479
+ const newest = runAt === null && probeAt === null ? null : Math.max(runAt ?? 0, probeAt ?? 0);
480
+ const fromRun = (r) => ({
481
+ kind: r.kind,
482
+ ref: checkRef(r),
483
+ title: r.trigger?.title ?? r.kind,
484
+ at: r.createdAt,
485
+ runId: r.id,
486
+ });
487
+ const fromProbe = (p) => {
488
+ const host = hostOf(p.url);
489
+ return { kind: 'site', ref: host, title: `Site check of ${host}`, at: p.lastCheckedAt };
490
+ };
491
+ if (run && (i.now - runAt < QUIET_LIMIT_MS || !probe))
492
+ return { shown: fromRun(run), newest };
493
+ if (probe)
494
+ return { shown: fromProbe(probe), newest };
495
+ return { shown: null, newest };
496
+ }
497
+ export { NO_CHECK_YET };
498
+ /**
499
+ * The line of a dot that never checked, in plain words and naming only what can start a check for this dot. A dot with a QA site whose
500
+ * checks run says the first site check comes within a minute, because that is the soonest check it will make. A dot whose only source
501
+ * is a site that no check runs for is Not watching and has no first-check line (see notWatchingReason). Without a site, a repository counts as "a PR" whether
502
+ * or not it runs tests (a review of the pull request is a check), and a ticket tracker as "a ticket".
503
+ */
504
+ function noCheckYet(i) {
505
+ if (i.dot.probes && !i.dot.paused && i.dot.siteChecks === 'running')
506
+ return NO_CHECK_SITE;
507
+ const starters = [i.dot.hasRepo ? 'a PR' : '', i.dot.hasTracker ? 'a ticket' : ''].filter(Boolean);
508
+ if (starters.length)
509
+ return `No checks yet. The first one starts when ${starters.join(' or ')} comes in.`;
510
+ return NO_CHECK_YET;
511
+ }
512
+ /**
513
+ * A line for a dot that watches but has nothing recent to show: it never finished a check (why), or it has been quiet for 24 hours.
514
+ * A quiet repo on a weekend is normal, so this is a line and never a status.
515
+ */
516
+ function watchNote(i, newestCheck) {
517
+ if (newestCheck === null) {
518
+ const tried = i.runs.filter((r) => CHECK_KINDS.includes(r.kind));
519
+ if (tried.length && tried.every((r) => r.status === 'running'))
520
+ return 'The first check is still running.';
521
+ if (tried.length)
522
+ return 'Checks started, but none of them finished.';
523
+ return noCheckYet(i);
524
+ }
525
+ if (i.now - newestCheck >= QUIET_LIMIT_MS)
526
+ return 'Quiet: no check in 24 h';
527
+ return null;
528
+ }
529
+ /**
530
+ * Why the dot cannot watch, or null. `fix` opens Setup when a person can repair it there. The sentence says what is wrong and never
531
+ * what to press: the panel adds the button for an admin and "ask your admin" for everyone else, so a member never reads an order
532
+ * they cannot follow.
533
+ */
534
+ function notWatchingReason(i) {
535
+ if (i.dot.archived)
536
+ return { text: 'This dot is archived. Restore it to start watching again.', state: 'archived' };
537
+ if (i.dot.paused)
538
+ return { text: 'This dot is paused. Resume it to start watching again.', state: 'paused' };
539
+ if (i.blockers.signIn)
540
+ return { text: i.blockers.signIn, state: 'sign_in', fix: link('Open Setup', '#/setup') };
541
+ if (i.blockers.github)
542
+ return {
543
+ text: i.blockers.github,
544
+ state: 'github',
545
+ ...(i.blockers.fixable ? { fix: link('Open Setup', '#/setup') } : {}),
546
+ ...(i.blockers.detail ? { detail: i.blockers.detail } : {}),
547
+ };
548
+ // With no repo, no tracker and no site, no PR, ticket or site check can ever start: the sentence says what is missing and Setup is the way out.
549
+ if (!i.dot.hasRepo && !i.dot.hasTracker && !i.dot.probes)
550
+ return { text: NOTHING_TO_WATCH, state: 'nothing', fix: link('Open Setup', '#/setup') };
551
+ // A QA site as the only source, with no site check running: nothing can ever start a check. A sandbox is the server's doing, so it has
552
+ // no fix; the Prod watcher is a switch a person can turn on.
553
+ const block = siteOnlyBlock(i);
554
+ if (block === 'site_sandbox')
555
+ return { text: SITE_CHECKS_SANDBOX, state: 'site_sandbox' };
556
+ if (block === 'site_off')
557
+ return { text: SITE_CHECKS_OFF, state: 'site_off', fix: { label: 'Turn on site checks', setup: 'site' } };
558
+ return null;
559
+ }
560
+ /**
561
+ * A failed GitHub read in words a tester understands. The raw error ("HTTP 401: Bad credentials") goes to the Details fold. `fixable`
562
+ * says a person can repair it on Setup: a refused sign-in or a repo GitHub cannot find. A timeout or a rate limit mends itself.
563
+ */
564
+ export function githubTrouble(label, error) {
565
+ if (/\b(401|403)\b|bad credentials|forbidden|unauthori[sz]ed|requires authentication/i.test(error))
566
+ return { text: `GitHub turned down the sign-in for ${label}.`, fixable: true };
567
+ if (/\b404\b|not found/i.test(error))
568
+ return { text: `GitHub cannot find ${label}.`, fixable: true };
569
+ if (/\b429\b|rate limit/i.test(error))
570
+ return { text: `GitHub is limiting requests for ${label}. localdost waits and tries again.`, fixable: false };
571
+ if (/timeout|timed out|etimedout|econn|enotfound|network|fetch failed|socket/i.test(error))
572
+ return { text: `GitHub did not answer in time for ${label}. localdost keeps trying.`, fixable: false };
573
+ return { text: `GitHub is not answering for ${label}. localdost keeps trying.`, fixable: false };
574
+ }
575
+ /** The latest merge, when it has finished with this outcome. The Checks tile counts the same run the same way. */
576
+ const mergeOutcome = (i) => (i.lastMerge && i.lastMerge.status !== 'running' ? outcomeOf(i.lastMerge) : null);
577
+ function failingReason(i) {
578
+ if (i.probeRows.some((r) => r.down || r.status === 'down'))
579
+ return 'The QA site is down.';
580
+ // Only a confirmed bug fails the dot. A merge that "needs attention" waits for a person (Needs you), and the Checks tile counts it as
581
+ // "needs a look", so the header and the tile never tell two stories about one merge.
582
+ if (mergeOutcome(i) === 'bug')
583
+ return 'The last merge found a bug.';
584
+ return null;
585
+ }
586
+ /** The part of a Needs you reason that names the latest merge. The Room finds the merge it means by this phrase. */
587
+ export const MERGE_LOOK_WORDS = 'the last merge needs a look';
588
+ function needsYouReason(i, tickets) {
589
+ const waiting = i.pending.length;
590
+ const asking = Number(tickets.facts.needAnswers ?? 0);
591
+ const look = mergeOutcome(i) === 'look';
592
+ if (!waiting && !asking && !look)
593
+ return null;
594
+ const parts = [waiting ? `${n(waiting, 'item')} to review` : '', asking ? `${n(asking, 'ticket')} with questions` : '', look ? MERGE_LOOK_WORDS : ''].filter(Boolean);
595
+ const said = parts.length > 1 ? `${parts.slice(0, -1).join(', ')} and ${parts.at(-1)}` : parts[0];
596
+ return `${said[0].toUpperCase()}${said.slice(1)}.`;
597
+ }
598
+ // ---------- suggestion ----------
599
+ /**
600
+ * How the sentence names a ticket. A Jira or Linear key (PROD-1234) names it alone: a title next to it splits the sentence at its
601
+ * commas. A GitHub key reads as code (acme/shop-web#115), so the ticket is named by its issue number and its title in quotes
602
+ * (#115 "Checkout total"), or by the number alone when it has no title. The number is the key a person searches for.
603
+ */
604
+ export function ticketName(hit) {
605
+ const gh = /^[^/\s]+\/[^#\s]+#(\d+)$/.exec(hit.key);
606
+ if (!gh)
607
+ return hit.key;
608
+ const title = hit.title?.trim();
609
+ return title ? `#${gh[1]} "${title}"` : `#${gh[1]}`;
610
+ }
611
+ /**
612
+ * Rule 1. Tickets of the nearest open release that nothing tests: no run, no spec, and no test case names them.
613
+ * Ties go to the first ticket in the tracker's order.
614
+ */
615
+ export function releaseWithoutCases(i) {
616
+ if (!i.trace)
617
+ return null;
618
+ const milestone = nearestOpenMilestone(i.trace.rows, i.now, i.milestoneScope);
619
+ if (!milestone)
620
+ return null;
621
+ const hit = i.trace.rows.find((r) => r.milestone === milestone &&
622
+ r.statusCode === 'not_tested' &&
623
+ r.tests.total === 0 &&
624
+ !i.casedRefs.has(r.ref.toLowerCase()) &&
625
+ !i.casedRefs.has(r.key.toLowerCase()));
626
+ if (!hit)
627
+ return null;
628
+ return {
629
+ rule: 'release_without_cases',
630
+ text: `${ticketName(hit)} is in the release and has no test cases yet.`,
631
+ action: { label: 'Design tests', draft: `Design test cases for ${hit.key}${hit.title ? `: ${hit.title}` : ''}.` },
632
+ };
633
+ }
634
+ /**
635
+ * Rule 2. A bug filed earlier was drafted or filed again later: the tracker no longer showed the first issue as open, so the bug
636
+ * came back after its fix. Both ways to file count: a draft a person approves, and a check that files on its own. Only a filing
637
+ * from the last 7 days counts. Ties go to the newest.
638
+ */
639
+ export function bugCameBack(i) {
640
+ const filings = [];
641
+ for (const a of i.bugDrafts) {
642
+ const sig = a.action.meta?.bugSig;
643
+ if (typeof sig !== 'string' || !['pending', 'approved', 'executed'].includes(a.status))
644
+ continue;
645
+ const url = a.status === 'executed' && typeof a.action.meta?.filedUrl === 'string' ? a.action.meta.filedUrl : undefined;
646
+ filings.push({ id: a.id, sig, at: a.createdAt, title: a.action.title, executed: a.status === 'executed', ...(url ? { url } : {}) });
647
+ }
648
+ // A draft a person approved can also show in the actions of its run, with the same issue key: it is one filing, not two.
649
+ const draftKeys = new Set(i.bugDrafts
650
+ .map((a) => a.action.meta?.filedKey)
651
+ .filter((k) => typeof k === 'string')
652
+ .map((k) => k.toLowerCase()));
653
+ for (const b of i.autoFiled)
654
+ if (b.sig && !draftKeys.has(b.key.toLowerCase()))
655
+ filings.push({ id: b.key, sig: b.sig, at: b.at, title: b.title, executed: true, ...(b.url ? { url: b.url } : {}) });
656
+ const first = new Map();
657
+ for (const f of filings) {
658
+ if (!f.executed)
659
+ continue;
660
+ const prev = first.get(f.sig);
661
+ if (!prev || f.at < prev)
662
+ first.set(f.sig, f.at);
663
+ }
664
+ const again = filings
665
+ .filter((f) => {
666
+ const earliest = first.get(f.sig);
667
+ return earliest !== undefined && f.at > earliest && i.now - at(f.at) <= WEEK_MS;
668
+ })
669
+ .sort((a, b) => b.at.localeCompare(a.at) || a.id.localeCompare(b.id))[0];
670
+ if (!again)
671
+ return null;
672
+ return {
673
+ rule: 'bug_came_back',
674
+ text: `The bug "${again.title}" came back after it was fixed.`,
675
+ action: again.url ? { label: 'Open the bug', href: again.url } : { label: 'Review the bug', href: '#/inbox' },
676
+ };
677
+ }
678
+ /**
679
+ * Rule 3. A flaky test with 3 or more flaky or failed results this week. Ties go to the test with the most results, then the
680
+ * newest, then the path and title in order.
681
+ */
682
+ export function flakyTest(i) {
683
+ const hit = testStats(i.runs, i.now)
684
+ .filter((s) => s.flaky > 0 && s.flaky + s.failed >= FLAKY_SUGGEST_AT)
685
+ .sort((a, b) => b.flaky + b.failed - (a.flaky + a.failed) || b.latestAt - a.latestAt || `${a.file}::${a.title}`.localeCompare(`${b.file}::${b.title}`))[0];
686
+ if (!hit)
687
+ return null;
688
+ const times = hit.flaky + hit.failed;
689
+ // A flaky result is a test that failed and then passed on a retry, so the check around it still passed. Saying "failed" next to a
690
+ // Checks tile that says everything passed would read as a contradiction, so the sentence keeps the two apart.
691
+ const text = hit.failed === 0
692
+ ? `The test "${hit.title}" was flaky ${times} times this week: it failed, then passed on a retry.`
693
+ : `The test "${hit.title}" was flaky or failed ${times} times this week.`;
694
+ return {
695
+ rule: 'flaky_test',
696
+ text,
697
+ action: { label: 'See the test', href: `#/runs/${encodeURIComponent(hit.badRunId || hit.latestRunId)}` },
698
+ };
699
+ }
700
+ /** Rule 4. The latest merge ended "needs attention": nobody called it a bug, and a person must look. */
701
+ export function mergeNeedsLook(i) {
702
+ if (mergeOutcome(i) !== 'look' || !i.lastMerge)
703
+ return null;
704
+ return {
705
+ rule: 'merge_look',
706
+ text: 'The last merge needs a look.',
707
+ action: { label: 'Open the merge', href: `#/runs/${encodeURIComponent(i.lastMerge.id)}` },
708
+ };
709
+ }
710
+ /** Rule 5. The dot used 80 percent or more of its daily calls. */
711
+ export function aiCallsLow(i) {
712
+ const c = i.cap;
713
+ if (!c || c.used / c.cap < AI_NEAR_LIMIT)
714
+ return null;
715
+ const left = Math.max(0, c.cap - c.used);
716
+ return {
717
+ rule: 'ai_calls_low',
718
+ text: left ? `AI calls are almost used up: ${c.used} of ${c.cap} used today.` : `AI calls are used up for today: ${c.cap} of ${c.cap}.`,
719
+ action: { label: 'Raise the limit', setup: 'cap' },
720
+ };
721
+ }
722
+ export function suggestionOf(i) {
723
+ const rules = [releaseWithoutCases, bugCameBack, flakyTest, mergeNeedsLook, aiCallsLow];
724
+ for (const rule of rules) {
725
+ const hit = safely(() => rule(i));
726
+ if (hit)
727
+ return hit;
728
+ }
729
+ return null;
730
+ }
731
+ function safely(fn) {
732
+ try {
733
+ return fn();
734
+ }
735
+ catch {
736
+ return null;
737
+ }
738
+ }
739
+ // ---------- the answer ----------
740
+ export function computeHealth(i) {
741
+ const err = i.errors ?? {};
742
+ const build = (key, title, make) => {
743
+ if (err[key])
744
+ return broken(title);
745
+ try {
746
+ return make();
747
+ }
748
+ catch {
749
+ return broken(title);
750
+ }
751
+ };
752
+ const tiles = {
753
+ review: build('review', 'To review', () => reviewTile(i)),
754
+ bugs: build('bugs', 'Bugs', () => bugsTile(i)),
755
+ checks: build('checks', 'Checks this week', () => checksTile(i)),
756
+ release: build('release', 'Release', () => releaseTile(i)),
757
+ site: build('site', 'QA site', () => siteTile(i)),
758
+ tests: build('tests', 'Tests', () => testsTile(i)),
759
+ tickets: build('tickets', 'Tickets', () => ticketsTile(i)),
760
+ ai: build('ai', 'AI use today', () => aiTile(i)),
761
+ };
762
+ const { shown: lastCheck, newest } = lastCheckOf(i);
763
+ const away = notWatchingReason(i);
764
+ const failing = failingReason(i);
765
+ const needs = needsYouReason(i, tiles.tickets);
766
+ let status;
767
+ let statusReason;
768
+ // Not watching comes first: a dot that cannot watch says so, and what else is wrong (a QA site that is down, say) shows on its own tile.
769
+ if (away)
770
+ [status, statusReason] = ['not_watching', away.text];
771
+ else if (failing)
772
+ [status, statusReason] = ['failing', failing];
773
+ else if (needs)
774
+ [status, statusReason] = ['needs_you', needs];
775
+ else
776
+ [status, statusReason] = ['healthy', 'Nothing needs you.'];
777
+ const trialUntil = i.trialUntil && at(i.trialUntil) > i.now ? at(i.trialUntil) : null;
778
+ let trial = null;
779
+ if (trialUntil !== null) {
780
+ const left = Math.max(1, Math.ceil((trialUntil - i.now) / DAY_MS));
781
+ const days = Math.max(SHADOW_DAYS, left);
782
+ trial = { day: Math.min(days, Math.max(1, days - left + 1)), days };
783
+ }
784
+ return {
785
+ status,
786
+ statusReason,
787
+ ...(away?.fix ? { statusFix: away.fix } : {}),
788
+ ...(away?.detail ? { statusDetail: away.detail } : {}),
789
+ watching: { on: !away, state: away ? away.state : 'on', note: away ? null : watchNote(i, newest), lastCheck, trial, postsWait: i.postsWait },
790
+ tiles,
791
+ suggestion: suggestionOf(i),
792
+ builtAt: new Date(i.now).toISOString(),
793
+ };
794
+ }
795
+ // ---------- reading the rows ----------
796
+ /** Bug drafts are read up to this many, newest first, whatever other drafts the dot has made since. */
797
+ const BUG_DRAFT_LIMIT = 2000;
798
+ const RUN_LIMIT = 400;
799
+ /** Runs that filed a bug are read up to this many, newest first. */
800
+ const FILER_LIMIT = 2000;
801
+ /** Reads every row the panel needs. A piece that fails to read is left out and its tile says so; the others still answer. */
802
+ export function readHealthInput(w, now = Date.now()) {
803
+ const errors = {};
804
+ const guard = (keys, fallback, read) => {
805
+ try {
806
+ return read();
807
+ }
808
+ catch {
809
+ for (const k of keys)
810
+ errors[k] = true;
811
+ return fallback;
812
+ }
813
+ };
814
+ const since = new Date(now - WEEK_MS).toISOString();
815
+ // The newest runs. The tiles read the last 7 days, and the 400 newest are enough for that.
816
+ const recent = guard(['checks', 'tests', 'tickets', 'bugs'], [], () => list('runs', { watchId: w.id }, { orderBy: 'createdAt DESC', limit: RUN_LIMIT }));
817
+ const runs = recent.filter((r) => r.createdAt >= since);
818
+ // The newest check that finished and said something. The newest 200 check runs are read so a long run of failed starts does not hide an older good check.
819
+ const lastCheckRun = safely(() => list('runs', { watchId: w.id, kind: [...CHECK_KINDS] }, { orderBy: 'createdAt DESC', limit: 200 }).find(finishedCheck) ?? null);
820
+ const lastMerge = safely(() => list('runs', { watchId: w.id, kind: 'post_merge' }, { orderBy: 'createdAt DESC', limit: 1 })[0] ?? null);
821
+ const pending = guard(['review'], [], () => list('approvals', { watchId: w.id, status: 'pending' }).filter((a) => isLive(a, now)));
822
+ // Only bug drafts are read, newest first, so a busy dot's newer drafts of other kinds (a PR comment, say) never push an older bug
823
+ // out of the count. The kind sits inside the stored action, so the ids come from one query and the rows from the next.
824
+ const bugDrafts = guard(['bugs'], [], () => {
825
+ const ids = db
826
+ .prepare(`SELECT id FROM approvals WHERE watchId = ? AND json_extract(action, '$.kind') IN ('file_defect', 'issue_create') ORDER BY createdAt DESC LIMIT ${BUG_DRAFT_LIMIT}`)
827
+ .all(w.id).map((r) => r.id);
828
+ if (!ids.length)
829
+ return [];
830
+ return list('approvals', { id: ids }, { orderBy: 'createdAt DESC' }).filter((a) => isBugDraft(a.action));
831
+ });
832
+ const closedKeys = new Set();
833
+ const autoFiled = [];
834
+ guard(['bugs'], null, () => {
835
+ // Every run of the dot that filed a bug, however old, found by its stored actions: a busy dot passes any run count in weeks, and a
836
+ // bug that is still open must stay counted.
837
+ const filers = db
838
+ .prepare(`SELECT createdAt, actions FROM runs WHERE watchId = ? AND actions LIKE '%filedKey%' ORDER BY createdAt DESC LIMIT ${FILER_LIMIT}`)
839
+ .all(w.id);
840
+ for (const r of filers) {
841
+ let acts;
842
+ try {
843
+ acts = r.actions ? JSON.parse(r.actions) : [];
844
+ }
845
+ catch {
846
+ continue;
847
+ }
848
+ for (const x of acts ?? []) {
849
+ if (!isBugDraft(x.action) || x.result !== 'executed')
850
+ continue;
851
+ const key = typeof x.action.meta?.filedKey === 'string' ? x.action.meta.filedKey : '';
852
+ if (!key)
853
+ continue;
854
+ const url = typeof x.action.meta?.filedUrl === 'string' ? x.action.meta.filedUrl : undefined;
855
+ const sig = typeof x.action.meta?.bugSig === 'string' ? x.action.meta.bugSig : undefined;
856
+ autoFiled.push({ key, title: x.action.title, at: r.createdAt, ...(url ? { url } : {}), closed: false, ...(sig ? { sig } : {}) });
857
+ }
858
+ }
859
+ const keys = new Set();
860
+ for (const a of bugDrafts)
861
+ if (typeof a.action.meta?.filedKey === 'string')
862
+ keys.add(a.action.meta.filedKey.toLowerCase());
863
+ for (const b of autoFiled)
864
+ keys.add(b.key.toLowerCase());
865
+ for (const key of keys) {
866
+ if (!/^[^/\s]+\/[^#\s]+#\d+$/.test(key))
867
+ continue;
868
+ if (get('issue_state', key)?.state === 'closed')
869
+ closedKeys.add(key);
870
+ }
871
+ return null;
872
+ });
873
+ const urls = new Set(w.probes.map((p) => p.url));
874
+ const probeRows = guard(['site'], [], () => list('probe_state', { watchId: w.id }).filter((r) => urls.has(r.url)));
875
+ let trace = null;
876
+ const casedRefs = new Set();
877
+ guard(['release'], null, () => {
878
+ // A member of a lead reads the tests of its whole team for its own tickets (KAT-405): a PR check of PR Reviewer and the cases of Ticket
879
+ // Reviewer test a ticket of Ticket Reviewer, so it is not "without test cases" while the release call of the lead says it passes.
880
+ const lead = leadOf(w.id);
881
+ const own = buildAll(w).all;
882
+ trace = { rows: lead ? withTeamEvidence(own, buildAll(lead).all) : own };
883
+ for (const c of list('test_cases', { dotId: lead ? teamIds(w.id) : w.id })) {
884
+ if (c.status === 'retired')
885
+ continue;
886
+ for (const ref of c.requirementRefs ?? [])
887
+ casedRefs.add(ref.toLowerCase());
888
+ }
889
+ return null;
890
+ });
891
+ const cap = guard(['ai'], null, () => capState(w) ?? null);
892
+ const delivery = safely(() => deliveryOf(w));
893
+ const alarm = safely(() => pollFailureFor(w));
894
+ // Only the default model can stop a dot. A stage whose own model is signed out falls back to the default and keeps running, and a
895
+ // model nobody uses is not the dot's problem. A default that another model already answers for (usingInstead) stops nothing either.
896
+ const mainFamily = safely(() => defaultFamily());
897
+ const down = safely(() => listUnavailable().find((u) => u.kind === 'auth_expired' && !u.usingInstead && !!mainFamily && u.family === mainFamily));
898
+ const blockers = {};
899
+ // The reason text of the model layer already says "sign-in expired", so the sentence names the model and stops: no stutter.
900
+ if (down)
901
+ blockers.signIn = `The ${FAMILY_LABEL[down.family] ?? 'AI'} sign-in expired or was rejected.`;
902
+ if (alarm) {
903
+ const t = githubTrouble(alarm.label, alarm.error);
904
+ blockers.github = t.text;
905
+ blockers.fixable = t.fixable;
906
+ blockers.detail = `${alarm.label}: ${alarm.error}`;
907
+ }
908
+ else if (delivery?.listening === 'none') {
909
+ blockers.github = delivery.listeningReason
910
+ ? `${delivery.listeningReason.replace(/[.\s]+$/, '')}.`
911
+ : `Nothing brings a PR to this dot${delivery.listeningMissing?.length ? ` (${delivery.listeningMissing.join(', ')} missing)` : ''}.`;
912
+ blockers.fixable = true;
913
+ }
914
+ const trialUntil = w.shadowWeek && !shadowExpired(w.shadowWeek, now) ? w.shadowWeek.until : null;
915
+ const postsWait = safely(() => ruleFor(w, 'pr_comment') === 'ask') ?? false;
916
+ return {
917
+ now,
918
+ dot: {
919
+ id: w.id,
920
+ paused: !!w.paused,
921
+ archived: !!w.archived,
922
+ createdAt: w.createdAt,
923
+ probes: w.probes.length,
924
+ hasTracker: !!w.tracker,
925
+ hasRepo: !!safely(() => watchRepos(w).length),
926
+ canFileBugs: !!safely(() => defectTarget(w)),
927
+ canRunTests: !!safely(() => watchRepos(w).some((r) => r.runTests !== false)),
928
+ canSiteCheck: !w.paused && !w.archived && !!w.probes.length && !sandboxOn(),
929
+ siteChecks: sandboxOn() ? 'sandbox' : stageOn(w, 'watcher') ? 'running' : 'off',
930
+ },
931
+ runs,
932
+ lastCheckRun,
933
+ lastMerge,
934
+ pending,
935
+ bugDrafts,
936
+ autoFiled,
937
+ closedKeys,
938
+ probeRows,
939
+ trace,
940
+ ...(w.tracker?.kind === 'github' && w.tracker.milestones?.length ? { milestoneScope: w.tracker.milestones } : {}),
941
+ casedRefs,
942
+ cap,
943
+ blockers,
944
+ trialUntil,
945
+ postsWait,
946
+ errors,
947
+ };
948
+ }
949
+ /** The rows of a member with the tests, PRs, bugs and status that its team's rows hold for the same ticket. A ticket the team does not know stays as it is. */
950
+ export function withTeamEvidence(own, team) {
951
+ const byRef = new Map(team.map((r) => [r.ref, r]));
952
+ return own.map((r) => {
953
+ const t = byRef.get(r.ref);
954
+ return t ? { ...r, prs: t.prs, tests: t.tests, defects: t.defects, status: t.status, statusCode: t.statusCode } : r;
955
+ });
956
+ }
957
+ /**
958
+ * The health of a dot. A lead (KAT-405) answers for its team: `readMember` gives each member's answer, the kept one on the route and a
959
+ * fresh read here, so the lead's word is the worst of the members' words.
960
+ */
961
+ export function healthOf(w, now = Date.now(), readMember = (m) => healthOf(m, now)) {
962
+ if (isLead(w))
963
+ return leadHealth(w, now, readMember);
964
+ return computeHealth(readHealthInput(w, now));
965
+ }
966
+ //# sourceMappingURL=dot-health.js.map