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,416 @@
1
+ # Chat answers you can act on
2
+
3
+ A chat answer can carry small working views next to its text. When you ask "what is waiting for me?", the dot replies with a sentence and the drafts themselves, each with Approve, Edit, and Reject. It works like a waiter who brings the menu to your table so you can point at what you want.
4
+
5
+ This page describes the answer blocks (KAT-136) and the object cards that came with them: the component registry, the format of a block, how the server and the client check it, what happens with data that does not fit, how actions stay inside the gate, and how blocks stream. The client and the server are built. The server builds every block from a tool result: the lookup tools (see Blocks from lookup tools), `list_cases`, `design_tests`, the stats tools (see Stats and charts), the object cards, and the triage view. The model never writes a block or a row. When one block does not answer the question, the model composes a view from these blocks (see Composed views).
6
+
7
+ ## Sources
8
+
9
+ Two OpenAI designs shaped this. The Intelligent UI in ChatGPT answers fast and puts the tools for the task in the answer, so a question about data comes back as something you can click. The Apps SDK ([ChatGPT UI](https://developers.openai.com/apps-sdk/build/chatgpt-ui)) renders a tool's structured result with a component inline and ships a small UI kit of cards, buttons, and inputs.
10
+
11
+ localdost takes the structured-result idea and the small kit. It leaves out the iframe and the host bridge: every component lives in this repo, and the model never sends HTML or code. The server picks the block kind from the tool the model called and fills in the data from the tool result; the client owns how it looks and what each button does. A composed view goes one step further in the direction of the Intelligent UI: the model arranges our components, the logic runs in a sandbox, and the app draws the result with native components.
12
+
13
+ ## Component registry
14
+
15
+ The registry is `BLOCK_REGISTRY` in `client/src/features/chat/blocks/registry.tsx`. One kind maps to one component.
16
+
17
+ | Kind | Shows | Actions | Action path |
18
+ |---|---|---|---|
19
+ | `runs` | Test runs: status, outcome, tests passed and failed, duration, time. Filter chips (All, Failed, Running, Passed) when there are more than 3. | Open, Rerun | Open: `#/runs/<id>`. Rerun: `POST /api/runs/:id/rerun` |
20
+ | `pulls` | Open PRs: number, title, state, checks, author, branch, the last QA report line | Open, Review now, View report, Show card (the PR's full card under the row) | Review now: `POST /api/dots/:id/review {pr, repo}` with the repo of the block, so PR 12 of a second app repo is not read as PR 12 of the first; ask when the block has no dot |
21
+ | `approvals` | Pending drafts through the Inbox's `ApprovalCard`. In chat each card starts collapsed (title, target, reason, actions); Show draft reveals the exact text | Approve, Edit, Reject | `POST /api/approvals/:id` |
22
+ | `defects` | Defects: key, title, severity, status, the run that found it | Open | Link to the tracker |
23
+ | `probes` | Probe status: up, degraded, down, HTTP status, latency, last check, error | Retry | Ask: "Check the <name> probe again" |
24
+ | `memory` | What the dot remembers: text, kind, who added it | Edit, Delete, Undo | `POST /api/memory` and `DELETE /api/memory/:id` |
25
+ | `ticket_review` | A ticket review: the ticket's card, then risk, risks, test ideas, open questions. When the tracker cannot be read, the key, the title, and an Open link stand in for the card | Post comment (the card has its own buttons) | Approve the drafted comment (`approvalId`), else ask |
26
+ | `release_call` | The release call of a Lead QA dot (KAT-405): the answer word (Ready, Ready with risk, Not ready, No tickets) with the milestone, the blocking reasons under Why, the risks under Also, and the counts and the rule under Details. Built from stored rows by the `release_call` tool, with no model call | Open (on each reason; See them on a group of tickets and Review on a bug report that waits in an Inbox) | Link to the ticket, bug, PR, run or Inbox the reason names |
27
+ | `test_cases` | Test cases with a checkbox each, priority, and result. The `list_cases` chat tool fills it from the test case store (KAT-163): one row per case by key, with its latest result | Tick, Run | Ask: "Run these test cases from run <id>: ..." |
28
+ | `triage` | Bugs the QA review found and failed or flaky tests from recent runs, grouped by cause, with a "3 of 7 triaged" progress line | Tests: Rerun, Mark flaky, Draft defect. Review bugs: Draft defect, the Inbox link, or Open defect | Rerun: `POST /api/runs/:id/rerun`. Mark flaky: `POST /api/memory {kind: flaky_spec}`. Draft defect: `POST /api/runs/:id/defect` with `{file, title}` or `{bugKey}`, which waits in the Inbox |
29
+ | `cards` | A GitHub PR, a GitHub issue, a Jira ticket, or a Linear ticket as a card in that product's own look (see Object cards). The block has no frame or heading, because each card is framed and named | Open, Review now or Review ticket, Comment, View diff or Change status | Review: `POST /api/dots/:id/review`. Comment: `POST /api/cards/comment`. Change status: `POST /api/cards/status`. All through the gate |
30
+ | `table` | Any rows as a compact table, also the fallback for unknown data | Show all | None |
31
+ | `stats` | Big-number tiles: a label, the number, and a sub-line under it. Optional tone (ok, attention, bad) colors the number. | None | Runs it points at open as RefChips |
32
+ | `chart` | A bar, stacked bar, or line chart with series, x labels, and a one-line finding with the key number in bold | None | Runs it points at open as RefChips |
33
+ | `view` | A composed view (KAT-366): layout nodes around the other 14 kinds, with datasets the app computes in a Web Worker. See Composed views | The actions of the blocks inside it, and none of its own | Each block calls its own route |
34
+
35
+ Every block sits in one hairline frame with a heading and a count. Its records are rows with dividers inside that frame. Approvals are the exception: each draft is already a framed card, so that block drops its own frame. Blocks answer to their own width through a container query, so a row puts its actions to the right in a wide reply and under the text on a phone.
36
+
37
+ ## Stats and charts
38
+
39
+ `stats` and `chart` (KAT-237, tier 1) show numbers from run data. They are drawn by our own components in `client/src/features/chat/blocks/charts.tsx` with SVG, so there is no chart library and no HTML from the model. The chat never renders model-written HTML: a model-written view in a sandbox is not built and will not be. A question that needs more than one block gets a composed view instead (see Composed views).
40
+
41
+ Both follow these rules:
42
+
43
+ - Colors are theme tokens only. Series use `--chart-1` to `--chart-4` (in `tokens.css`, each one an alias of a status color, so dark mode follows). A second and later line also differs by dash pattern, so color is never the only cue.
44
+ - Numbers are monospace with tabular digits. Nothing animates.
45
+ - The plot is measured in real pixels, so its text keeps its size at phone width (320px and up). A bar chart whose names do not fit under the bars lies down: names run down the left and the value sits at the end of each bar. A line that is too tight for its names shows every second or third one.
46
+ - Bars start at zero. A percentage line ends at 100 and starts at the next ten below its lowest point.
47
+ - A screen reader gets the finding as the name and the numbers as a hidden table, with "No data" for a gap.
48
+ - A run the block points at shows as a `RefChip` (`client/src/ui/RefChip.tsx`) with its title from `runTitle()` in `src/refs.ts` and a link to `#/runs/<id>`. The block carries `runs: [{ runId, title }]` and an optional `runsLabel`.
49
+
50
+ `stats` fields: `tiles` (up to 8) with `label`, `value` (a string shown as written, or a number), optional `sub`, optional `tone`.
51
+
52
+ `chart` fields: `chartType` (`bar`, `stacked_bar`, `line`), `labels` (up to 31), `series` (up to 4, each `name` and `values` lined up with `labels`, `null` for a gap), optional `unit` (`%`), and `finding`, one line with the key number between `**` marks. The client turns only those marks into bold.
53
+
54
+ ```json
55
+ {
56
+ "id": "b-pass-rate-chart",
57
+ "kind": "chart",
58
+ "title": "Pass rate by day",
59
+ "tool": "pass_rate",
60
+ "chartType": "line",
61
+ "labels": ["Fri", "Sat", "Sun", "Mon", "Tue", "Wed", "Thu"],
62
+ "series": [{ "name": "Pass rate", "values": [96.5, null, null, 92, 88.7, 95.1, 94.2] }],
63
+ "unit": "%",
64
+ "finding": "**94.2%** of tests passed in the last 7 days, up **1.6 points** from the week before.",
65
+ "runs": [{ "runId": "run-a", "title": "Merge check: Coupon total is wrong after the cart refresh" }],
66
+ "runsLabel": "Latest runs with failures"
67
+ }
68
+ ```
69
+
70
+ ### Chat tools that return them
71
+
72
+ Both tools only read. The server builds the blocks from stored runs (`src/chat-stats.ts`), checks them with `parseChatBlock`, streams them as `block` events right after the tool result, sets `tool` so the tool line leaves that call out, and stores them on the assistant message. The model gets the key numbers and writes one or two sentences; it does not repeat the numbers. A voice call or a Slack thread shows no blocks, so the prompt tells the model to say the key numbers there.
73
+
74
+ | Tool | Question | Blocks |
75
+ |---|---|---|
76
+ | `pass_rate` | "Pass rate this week", "how are the tests doing" | `stats` with Pass rate, Test runs, Failed, Flaky, then a `chart` (line) of the pass rate by day |
77
+ | `flaky_by_spec` | "Flaky tests by spec", "which specs are flaky" (`sinceDays`, default 7, at most 90) | A `chart` (bar) of the top 8 spec files by how often a test passed only on a retry, with the latest runs |
78
+
79
+ How the numbers are counted:
80
+
81
+ - The week is today and the 6 days before it, in the server's time zone. The comparison is the 7 days before that.
82
+ - A test counts as passed when its final result is pass. A test that passed on a retry is a pass for the rate and is counted again as flaky. Skipped tests are not counted.
83
+ - A run counts only when its tests ran to the end. A run whose setup failed, that was interrupted, or that is still running says nothing about the tests, so it is left out.
84
+ - When no run qualifies, the tool sends no block and the answer says so in text.
85
+
86
+ The rule-based chat (mock model, and the fallback when a CLI model gives no answer) routes these two questions to the tools too.
87
+
88
+ ## Answer format
89
+
90
+ The types are in `src/types.ts` (`ChatBlock` and its row types). Every field past `id` and `kind` is optional, so a partial stream or an older client still renders something.
91
+
92
+ ```json
93
+ {
94
+ "id": "b-runs",
95
+ "kind": "runs",
96
+ "title": "Runs today",
97
+ "dotId": "0muz17sj3000a15f8a",
98
+ "tool": "list_runs",
99
+ "state": "ready",
100
+ "more": 4,
101
+ "runs": [
102
+ { "id": "run-a", "kind": "post_merge", "status": "done", "outcome": "product_failure", "title": "Coupon total is wrong after the cart refresh", "passed": 41, "failed": 2, "durationMs": 184000, "startedAt": "2026-10-08T14:02:00Z" }
103
+ ]
104
+ }
105
+ ```
106
+
107
+ | Field | Meaning |
108
+ |---|---|
109
+ | `id` | Stable within a turn. A second block with the same id replaces the first. |
110
+ | `kind` | One of the registry kinds. |
111
+ | `title` | Heading. Each component has a default. |
112
+ | `dotId` | Dot the rows belong to. Actions that need a dot use it, then the chat's own dot. |
113
+ | `tool` | Tool call that produced the rows. The transcript hides that tool's card, so a lookup shows once. |
114
+ | `state` | `loading` while the server fills rows, `ready` (the default) when final. |
115
+ | `more` | Rows left out. The block says "4 more runs in the Feed". |
116
+
117
+ The answer is text first, then blocks. Text carries the judgement ("the Cart API is down"); the block carries the records. A block appears only for data that answers the question. Lookups the dot made for context (reading a ticket before it answers) stay on the collapsed tool line and get no block (see Blocks from lookup tools).
118
+
119
+ ## Validation
120
+
121
+ `parseChatBlock` in `src/chat-blocks.ts` checks every block. The server runs it before it streams a block, and the client runs it again before it renders. It has no dependencies, so both sides import the same file (`@chat-blocks` in the client).
122
+
123
+ - A block must be an object with an id. The client passes a local id for a block that arrived without one.
124
+ - Rows are checked one by one. Invalid rows are dropped and counted; valid rows stay.
125
+ - Enums (status, severity, state) keep only known values. The validator drops an unknown value and never guesses a replacement.
126
+ - Strings are trimmed and capped at 500 characters (memory text at 4,000).
127
+ - Links must be `http` or `https`. `javascript:`, `data:`, and protocol-relative links are dropped.
128
+ - A block keeps at most 50 rows. The rest add to `more`.
129
+ - Approval rows must carry an id, a status, and an action with a kind and a body, because the card reads the full record.
130
+
131
+ ## Fallback
132
+
133
+ | Input | Result |
134
+ |---|---|
135
+ | Known kind, some rows invalid | The block with the valid rows |
136
+ | Known kind, every row invalid | A table of the raw rows |
137
+ | Unknown kind with a list of objects | A table: up to 6 columns from the row keys, up to 50 rows |
138
+ | Unknown kind with only fields | A two-column Field and Value table |
139
+ | Not an object, no id and no fallback id, or nothing to show | No block. The answer stays text. |
140
+ | A component throws while it renders | A table of the same rows, in place; the rest of the answer is untouched |
141
+
142
+ A fallback table says "Shown as a table because this answer has no matching view yet." The wrapper owns the horizontal scroll, so a wide table never scrolls the page sideways, and edge shadows show when columns sit off screen.
143
+
144
+ ## Actions and the gate
145
+
146
+ A button runs one of two paths. Neither path posts outside localdost on its own.
147
+
148
+ - **Endpoint.** The button calls a route the app already has: rerun, review on demand, decide an approval, add or delete a note. Reruns and reviews queue a job. Anything that job wants to post (a PR comment, a ticket comment, a check run) goes through `gate()` and waits in the Inbox when the rule says ask.
149
+ - **Ask.** The button sends a follow-up chat message, for example "Check the Cart API probe again". The dot answers with its tools, and any outward action it takes goes through the same gate and approvals as a run.
150
+
151
+ Post comment on a ticket review approves the drafted comment when the block carries `approvalId`, because that approval is the gate step: a person approves the exact text. Without a draft it asks the dot to write one, and the draft waits in the Inbox unless the rule for ticket comments is act.
152
+
153
+ Each action shows its result under the row that started it: "Rerun queued. Open the new run", "A review of this head is already queued or running", or the server's error with its status code. A second press while the first is in flight does nothing. An ask while an answer is still streaming does not send; it says "Wait for the current answer to finish, then try again."
154
+
155
+ ## Streaming
156
+
157
+ `POST /api/chat` streams a block as one SSE event:
158
+
159
+ ```
160
+ data: {"type":"block","block":{"id":"b-runs","kind":"runs","state":"loading"}}
161
+
162
+ data: {"type":"block","block":{"id":"b-runs","kind":"runs","state":"ready","runs":[...]}}
163
+ ```
164
+
165
+ The timeline (`client/src/features/chat/timeline.ts`) validates each event and adds a `block` part in stream order. A block with an id already in the turn replaces that part, so the server can send a loading skeleton first and the rows later. A loading block with no rows shows three skeleton rows shaped like the real ones. When the turn ends (`done`, `error`, or Stop) a block still loading shows the rows it has.
166
+
167
+ Stored replies keep their blocks in `blocks` on the message (`ChatMessage.blocks`). History renders them after the reply text, through the same validation.
168
+
169
+ ## Composed views
170
+
171
+ A composed view is one answer that arranges several blocks, or shows numbers that no single tool gives. "Flaky tests by hour of day" is one: a tile for the busiest hour and a chart of all 24 hours. The model writes a small tree, the server checks it and binds it to the tool results of the turn, and the app draws it with its own components (KAT-366). The model never writes HTML, CSS, a row, a link, or an action. The earlier plan for a model-written html view in a sandbox is dropped.
172
+
173
+ ### When a view shows
174
+
175
+ The model decides, with no picker and no separate prompt. A question that one tool answers gets that tool and its block, or text. The `compose_view` tool exists for the rest, and its description says so: use it when the answer needs several blocks together, or numbers a tool does not give (by hour, by branch, before and after a merge). The rule picker for triage still runs first.
176
+
177
+ On Slack and on a call the model still says the key numbers in words. The view is stored in the dot's chat, and the reply gets one line with a link to that answer. The reply is stored under an id the server picks before the turn starts, so the link carries it: `[The full view is in the chat](https://<public url>/#/chat/<dot id>?m=<message id>)` on Slack, and `[The full view is in the chat](#/chat/<dot id>?m=<message id>)` on a call, which the voice reads as plain words. The app opens the chat of the dot, scrolls to that message, and marks it with a soft panel; the reader stays there, and Latest goes back to the bottom. Slack has no link without a public URL, like every other link from the app, so the line names the dot instead: "The full view is in the chat of Checkout, in the localdost app." A call is not stored as a chat turn, so a view composed in a call is stored on its own as a message of kind `call_view`, under the same id.
178
+
179
+ ### The format
180
+
181
+ The tool input is JSON. `root` is a column of nodes, at most 4 levels deep and 40 nodes in all.
182
+
183
+ ```json
184
+ {
185
+ "title": "Flaky tests by hour of day",
186
+ "datasets": {
187
+ "byHour": {
188
+ "from": "r2",
189
+ "steps": [
190
+ { "op": "derive", "as": "hour", "expr": { "fn": "bucket", "of": "at", "by": "hour_of_day" } },
191
+ { "op": "group", "by": ["hour"], "agg": [{ "as": "flaky", "fn": "count" }], "fill": "hour_of_day" }
192
+ ]
193
+ }
194
+ },
195
+ "vars": { "peak": { "of": "byHour", "fn": "cell", "field": "hour", "row": 0, "format": "hour" } },
196
+ "root": {
197
+ "type": "column",
198
+ "children": [
199
+ { "type": "stats", "tiles": [{ "label": "Busiest hour", "value": "{peak}" }] },
200
+ { "type": "chart", "data": "byHour", "chartType": "line", "x": "hour", "xFormat": "hour", "series": [{ "field": "flaky" }] }
201
+ ]
202
+ }
203
+ }
204
+ ```
205
+
206
+ The registry of components is `VIEW_COMPONENTS` in `src/view-spec.ts`: one entry per component with every prop, its type, and its allowed values. The prop lists in the tool description (`viewSpecGuide()`) are written from it. Nothing else passes.
207
+
208
+ | Component | Props | Shows |
209
+ |---|---|---|
210
+ | `column`, `row` | `children` | A stack. A row puts its children side by side when there is room and stacks them when there is not |
211
+ | `section` | `title`, `children` | A heading over its children |
212
+ | `tabs` | `tabs`: up to 6 of `{ label, children }` | One tab at a time. A tab list with the arrow keys, Home, and End |
213
+ | `text` | `text`, `tone` (`muted`), `href` (http or https) | A short line. `{name}` marks take values from `vars` |
214
+ | `runs`, `pulls`, `approvals`, `defects`, `probes`, `memory`, `ticket_review`, `release_call`, `test_cases`, `cards` | `from` (a result id), `nth`, `title`, `limit` | The block the server builds from that result, the same as the lookup tool shows alone |
215
+ | `triage` | `window` (`last_merge`, `today`, `recent`), `title` | The failure triage, built from the stored runs |
216
+ | `table` | `data` or `from`, `columns` (up to 6 of `{ field, label, format }`), `title`, `limit` | A table of a dataset, or of a result that is already a table |
217
+ | `stats` | `tiles` (up to 6 of `{ label, value, sub, tone }`) or `from`, `title` | Tiles. `tone` is `ok`, `attention`, or `bad`: a name for a theme token, never a color |
218
+ | `chart` | `data`, `chartType`, `x`, `xFormat`, `series` (up to 4 of `{ field, name }`), `unit`, `finding`, `limit`, `title`, or `from` | A bar, stacked bar, or line chart |
219
+
220
+ A color is only a `tone`. A class name, a style, an event handler, an endpoint, or an `action` is not a prop of any component, so it is dropped and counted. The only link a view can hold is the `href` of a `text`, and it opens a new tab with `rel="noopener noreferrer"`.
221
+
222
+ ### Data
223
+
224
+ Data comes from tool results only. Each lookup result of a turn gets an id (`r1`, `r2`, in call order), and the model reads it as `resultId` in the result (a list is wrapped as `{ resultId, rows }`). The server holds the full rows. The model filters through tools, and the view only arranges what came back.
225
+
226
+ Only the chat's own dot feeds a view. A lookup made for another dot (a `dotId` that is not the dot of the chat) gets no id, and the model reads `viewSource: "Not a view source: ..."` with the result, which tells it to look the data up again for the chat's dot. The server checks it again where a dataset or a record node binds a result, and drops the part that names one, so a view never shows the data of a dot the chat is not for. A chat with no dot (the overview) reads any dot.
227
+
228
+ - **Record blocks.** `{ "type": "runs", "from": "r1" }` shows the block that `list_runs` would show, built by the server from the result: its rows, its links, its approval ids. A lookup that the model marked `context` shows nothing alone, and a view may still use it. The view holds the block, so a block of the same turn that was streamed alone is dropped from the stream and from the stored answer.
229
+ - **`test_results`.** One row per test per run, for the runs that finished their tests: `at` (the run time, ISO), `runId`, `dot`, `branch`, `kind`, `suite` (the last two parts of the spec path), `file`, `title`, `status` (`passed`, `failed`, `flaky`, `timedOut`), `durationMs`, and `retries`. It takes `dotId`, `sinceDays` (default 7, at most 90), `branch`, `status`, `file`, `kind`, and `splitAt: "last_merge"`, which adds a `period` column: `before` or `after` the newest merge into the default branch (there is no release record, so a release is that merge). A skipped test is not a result. The branch is the head of a PR check, the branch a merge went into, or the ref of a push. The model gets the columns, the count, and five rows; a call keeps at most 2,000 rows (the newest runs first) and counts the rest in `more`.
230
+ - **Rows that were cut.** A source that holds fewer rows than the tool found (the 2,000 rows of `test_results`, or the 12,000 cells the server sends) keeps its `more` count in the view, so a count, a rate, or a list of branches over it covers part of the data. The view says so in a note under it: "Computed from 336 of 5,136 rows, so counts and rates cover only part of the data. Ask again with a shorter window or one branch to count all of it." The `compose_view` answer carries `partial` (the result, its tool, the rows it holds, and `more`) and tells the model to say so in its sentence and to give no total as a fact, or to call the lookup again with fewer days, one branch, or one spec. The rule-based chat does the same: its reply names the newest rows it read, and never says that only one branch ran when the rows that were left out may hold the other. The chat eval fails a view case whose view read cut rows when the answer does not say it covers part of the data.
231
+ - **Datasets.** `{ "from": "r1" | an earlier dataset, "steps": [...] }`. The steps run in order: `filter` (`eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `contains`), `derive` (a `bucket` of a time into `hour`, `hour_of_day`, `day`, `weekday`, `week`, or `month`, or `ratio`, `percent`, `change`, or `diff` of two fields, where the second may be a number), `group` (with `count`, `sum`, `avg`, `min`, `max`, `distinct`, each with an optional condition, and `fill` for every hour of the day or day of the week), `sort`, `limit`, and `pick`. Fields are plain names, or dotted names for the nested fields of a lookup result.
232
+ - **Vars.** One value out of a dataset (`count`, `sum`, `avg`, `min`, `max`, `cell`), written in a format (`text`, `number`, `percent`, `duration`, `hour`, `weekday`, `day`, and `spec`, a test file path without its `.spec.ts`, so a chart names a suite as `e2e/cart`). A `{name}` mark in a text, a tile, or a finding takes the value. A table shows the words the runs block uses for a column called `outcome` (Product bug, Environment), `kind` (After merge, PR check), or `status` (Timed out), not the stored value.
233
+ - **Figures.** Every figure on screen comes from a tool result, through vars. A tile `value` must hold a `{name}`, and a text, a tile `sub`, or a `finding` that holds a number typed by hand (`1234`, `99.9%`, `12ms`, `#58`) is dropped and the model is told which. A look-back window such as "the last 7 days", a title, and a label may carry a number: they name what the tool was asked for. The check reads digits only: a number spelled out in words, or a claim with no number in it ("everything passed") is not caught, so the sentence the model writes after the view is still its own.
234
+ - **Time zone.** Hours and days follow the server clock, like the pass rate tool, so the sentence of the answer and the chart agree. The view carries `tzOffsetMin`, and a view that buckets a time says which clock it is under the view: "Hours and days are in UTC+7, the clock of the server that runs the dot."
235
+
236
+ ### Logic
237
+
238
+ The expressions are data, not code. The steps above are all there is: a spec names no function, no global, and nothing to evaluate. `src/view-compute.ts` reads them and does arithmetic. It is pure, so the server runs the same code once before it streams a view: a column that is not in the data drops the dataset and every node that reads it, and the model is told which and why. The app runs it again in a Web Worker (`client/src/features/chat/blocks/view-worker.ts`), which returns values and never touches the page.
239
+
240
+ - **No network, no DOM.** The worker removes `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `importScripts`, `BroadcastChannel`, `indexedDB`, `caches`, and sub-workers from its scope before its first message, and has no `document` or `window`. A browser keeps `fetch` on `WorkerGlobalScope.prototype`, so the lockdown cuts each name on the scope and on every prototype of it, and the self-check reports what a prototype still holds. The server also sends the worker file with `Content-Security-Policy: default-src 'none'; connect-src 'none'`, so the browser blocks a connection even if the lockdown failed. The e2e tests ask the real worker what it can reach, on its scope and on its prototypes, and run a script that has the same policy and no lockdown: it takes `fetch` from `WorkerGlobalScope.prototype` and tries a fetch, a synchronous XHR, a WebSocket, and `importScripts` against a server of the test, which sees no request. The same script without the policy does reach the server, so the result means something.
241
+ - **Caps.** 2 seconds per call, then the worker is stopped and the parts that need it say so. At most 60,000 input cells, 400,000 row visits, and 500 rows per dataset. The server sends at most 2,000 rows of a source and 12,000 cells in all, and only the columns the view uses.
242
+ - **A button never reads a computed number.** A view has no button of its own. A button of a block calls its own route (rerun, review, decide an approval, notes, ask a follow-up) with the id of its row, as the server put it there, and the route reads the server's records. Every action still goes through `gate()` and the Inbox. The model cannot define an action or an endpoint.
243
+
244
+ ### Checks
245
+
246
+ `parseViewTree` in `src/view-spec.ts` checks every spec, and `parseChatBlock` calls it for a `view` block. The server runs it on what the model sends, and the client runs it again on what it receives, with the same rules as the blocks:
247
+
248
+ - A component or a prop that the registry does not know is dropped and counted.
249
+ - Depth (4), nodes (40), children (12), tabs (6), datasets (8), steps (8), and vars (12) are capped. Strings are trimmed and capped. A string that opens an HTML tag is dropped: a `<` followed by a letter, `/`, `!`, or `?`, with or without a closing `>`.
250
+ - A figure the model typed is dropped (see Figures under Data), and a tile whose value holds no `{name}` goes.
251
+ - Links are http or https.
252
+ - A pipeline with one bad step drops its dataset, and every node and var that reads it, because a skipped step would show wrong numbers. A dataset or var cannot be named like a built-in (`constructor`, `toString`), and a field cannot be `__proto__`.
253
+ - A record node must carry `from`, and a bound node must carry a valid block of its own kind. A model's spec is never bound, so it cannot hand a view its own block.
254
+
255
+ ### Fallback
256
+
257
+ | Input | Result |
258
+ |---|---|
259
+ | Some nodes, props, or datasets invalid | The view with what is valid. The model is told what was dropped |
260
+ | A chart or table over a dataset with no rows, or over a column it does not have | That node is dropped, with the reason |
261
+ | The whole spec fails, and it holds nodes that read a tool result by id | Every block it contains, shown alone, in document order, however deep it was nested: the record kinds, the triage, and a `stats`, `chart`, or `table` with `from`, each with the `title` and `limit` the registry allows. A node built from a dataset has no block of its own, so it is left out |
262
+ | The whole spec fails and holds no block | An error to the model, and the answer stays text |
263
+ | The client cannot check a stored view | The blocks it holds in a plain column, then text |
264
+ | The worker fails or is too slow | The nodes that read data say why in one line; the blocks the server built still show |
265
+ | A node throws while it renders | One line for that node; the rest of the view stays |
266
+
267
+ ### Streaming
268
+
269
+ The view appears while the model is still writing the call. The Anthropic API backend sends the input of a tool call as pieces of JSON, and `ViewStream` (`src/agent/chat-views.ts`) reads them as they come, without waiting for the end. `scanViewInput` walks the brackets and finds the complete values of `title`, `datasets`, and `vars`, and each child of `root.children` at the moment its closing brace arrives. Nothing is evaluated: a half-written node waits for the next piece.
270
+
271
+ - The first event is a `view` block with `state: "loading"`, sent once the first node of the root has closed. It holds the title, the datasets the tool results can feed, the rows they read, and the vars. The number of nodes is not known yet, so there is no `total`, and the client shows one placeholder.
272
+ - Then each node of the root is one event, `view_node { id, at, node }`, as soon as its closing brace has come. A node goes through the checks of the finished view first: the registry, the binding to a tool result, and the run of the pipelines. A node that fails any of them is skipped.
273
+ - When the call ends, `compose_view` checks the whole spec and sends the finished `block` with the same id, which replaces what streamed. A node that the full check drops disappears then. When the full check fails, the server sends `view_drop { id }` first, so the preview goes, and then the blocks the spec holds, or nothing and the answer in text. A call that no tool call finishes (the same call twice in one message, or a turn that fails) also ends with `view_drop`.
274
+ - Input the scanner cannot read (a spec wrapped in `spec`, a `root` written after the datasets it reads, more than 96,000 characters) streams nothing, and the finished view is sent as before. The preview is never stored and never changes what is stored.
275
+ - A backend that receives the whole call at once (Claude CLI and Codex CLI, through the chat bridge) has nothing to read early. It sends the shell with `total`, then each node with a pause between them, then the finished block.
276
+
277
+ The shell holds every column of a source, because the nodes that read them have not come; the finished view holds only the columns it uses. A node that has not come shows as a placeholder, and so does a node that waits for the worker. The transcript stays at the bottom while the view grows, because it ignores the scroll event of its own move.
278
+
279
+ ```
280
+ data: {"type":"block","block":{"id":"b-view-1","kind":"view","state":"loading","view":{"sources":{...},"datasets":{...},"root":{"type":"column","children":[]}}}}
281
+
282
+ data: {"type":"view_node","id":"b-view-1","at":0,"node":{"type":"stats","tiles":[...]}}
283
+
284
+ data: {"type":"view_node","id":"b-view-1","at":1,"node":{"type":"chart","data":"byHour",...}}
285
+
286
+ data: {"type":"block","block":{"id":"b-view-1","kind":"view","state":"ready","view":{...}}}
287
+ ```
288
+
289
+ A stored reply keeps the finished view, with its sources, in `blocks`. `GET /api/messages` reads the blocks inside it again like any block: a draft that was decided shows as it stands, and a deleted one drops out.
290
+
291
+ ## Triage
292
+
293
+ A chat answer can be a working tool built for the question (KAT-247). The first one is failure triage: ask "what broke?", "what failed since the last merge", "why is the run red?", "which tests are failing", or "anything flaky?" in the app chat, and the dot answers with one short line ("1 bug found by review and 3 tests failed or flaked in the last 6 runs of Checkout (since 09:30 today). Suspected causes: 2 product, 1 flaky.") and a `triage` block.
294
+
295
+ **Picking the view.** `pickChatView` in `src/agent/chat-view.ts` runs before any model call. Today it is a keyword and intent rule; `setViewPicker` swaps in another picker, such as a decision API, with the same signature (it may be async, and a picker that throws means no view). The rule skips calls and Slack (no components there), report-scoped chats, refusals ("don't triage"), and lines with a PR link or a ticket key, which stay with the model. When the rule picks triage, the chat model is not called at all: the block is built from stored runs and the line is written from its counts.
296
+
297
+ **Window.** `triageWindowOf` reads the question. "Since the last merge" reads the runs from the newest post-merge run of a merge into the default branch on; "today" reads the runs since local midnight; anything else reads the last 20 runs. Both named windows stop at 100 runs, and "since the last merge" with no merge on record falls back to the last 20 runs. A run counts when it finished and has test results or a QA review. The block carries `window` and `since` (the window start), and the empty state names both: "No failed tests and no bugs found in the last 4 runs (since 09:30 today)."
298
+
299
+ **Rows.** `buildTriageBlock` in `src/agent/triage.ts` reads the runs of the window for the dot, or for every dot in the all-dots chat. Each bug the QA review found by reading the change is one row: the same list the run report shows under "Bugs found" (`findBugs`, review bugs only), with the bug title, severity, the code it points at, the run and its PR as chips, and the defect state. A newer review of the same PR replaces the older one, and a bug found again after the merge shows once: the newest finding, or the one that already has a defect. Each test that failed, timed out, or was flaky in one of them is one row with the test name and file, the newest run where it failed (as a run chip) and that run's PR (as a PR chip), the result in the newest run that ran it, how many of the runs that ran it it failed in, and the first error line.
300
+
301
+ **Cause.** The suspected cause comes from the verdict of that newest failing run: product failure, test failure, environment failure, or flaky. A case that passed on retry is flaky. When the verdict names no cause, a test that also passed in the window is flaky, else product. Review bugs show first under "Found by review", most severe first; then the test groups in that order: product, test, environment, flaky.
302
+
303
+ **Actions and state.** Rerun queues a rerun of the row's run through the existing endpoint, and marks every row of that run. Mark flaky saves a `flaky_spec` memory note in the format the runner writes (`file :: title (marked flaky in chat)`). Draft defect calls `POST /api/runs/:id/defect`, the run flows' draft path, so a second press or a draft the flow already made links that draft, and the draft waits in the Inbox under the default ask rule. A review bug row has one action that follows its defect state: Draft defect (`{bugKey}`) when there is none, a link to the Inbox while the draft waits, and Open defect with the tracker link once it is filed. A row counts as triaged once any action went through. The state lives in those records, not in the chat: the run's rerun job, the dot memory, and the defect approval. `GET /api/messages` reads it again for every stored triage row, so a reopened chat shows the same progress, including drafts approved or rejected in the Inbox since.
304
+
305
+ ## Blocks from lookup tools
306
+
307
+ Nine read-only chat tools answer with a block (KAT-136). The tool still gives the model its own result. The server turns that same result into the matching block, so the model writes one or two sentences about the rows and never writes a row. `blocksFromToolResult` in `src/agent/chat-tool-blocks.ts` does the mapping. `executeChatTool` calls it after every tool call, so the block is the same on every backend: the Anthropic API, the Codex CLI and the Claude CLI (their calls come through the chat bridge into the same turn), and the rule-based reply that a CLI turn falls back to. Each block goes through `parseChatBlock`, streams as a `block` event right after the tool result, carries `tool` so the tool line leaves that call out, and is stored with the reply.
308
+
309
+ | Tool | Block | Rows |
310
+ |---|---|---|
311
+ | `list_runs` | `runs` | One per run: the trigger title, status, outcome, tests passed (a pass on retry counts) and failed, the duration of the stored run, and when it started. The heading is "Recent runs", or "Runs that need attention" with `onlyProblems`. Runs left out by `limit` count toward `more`. The all-dots chat names the dot of each row. |
312
+ | `runs_in_progress` | `runs` | The runs working now, headed "Running now". A queued job has no run yet, so only the answer text counts it. |
313
+ | `open_prs` | `pulls` | One block per dot and repo, and the block names its repo, which Review now sends with the PR number. The last QA report line, its run, and the head branch come from the newest PR check of that PR, and a QA check that is running shows "Checks running". GitHub's own check state is not read, so no other state is claimed. |
314
+ | `pending_approvals` | `approvals` | The stored approval records, so each card acts on its own record. `more` counts drafts beyond the tool's `limit`. |
315
+ | `recent_defects` | `defects` | One per run that found a problem. The key is the ticket or PR the run was about (`TAD-3`, `#11`; a second run of the same PR is `#11 (2)`). Severity is High only when the run named a high risk. The status is the outcome in words. A headline that starts with the key does not repeat it. A run has no tracker issue, so a row has no Open button, only the link to the run. |
316
+ | `probe_status` | `probes` | One per probe, named by host and path (the address shows only when it says more than the name). A probe whose last check failed shows Degraded until the dot calls an outage (three failures in a row); only a probe the dot declared down shows Down. |
317
+ | `get_memory` | `memory` | The stored notes with their dot and time, so Edit and Delete find them. |
318
+ | `list_tickets_reviewed` | `ticket_review` | One block per ticket, five at most, from the stored review run: risk, risks, test ideas, open questions, the ticket link, and the pending comment draft. |
319
+ | `get_ticket` | `ticket_review` | The ticket's last review, only when the question is about the review (see below). |
320
+
321
+ Rules:
322
+
323
+ - **A block answers the question.** Each of the eight list tools, and `get_ticket`, takes an optional `context` flag. `pass_rate`, `flaky_by_spec`, and `list_cases` take none, and the chat prompt names only the tools that do. A call with `context: true` is a lookup the model makes only to decide its next step, for example to find the run it will rerun. It streams its tool result and no block, and stays on the collapsed tool line (KAT-133). The tool description and the chat prompt say so, and the chat eval checks that the expected kinds show and that a context lookup shows none (see CHAT-EVAL.md).
324
+ - **`get_ticket` reads a ticket, which no block shows.** The ticket's stored review shows as a `ticket_review` block only when the question asks about that review (review, risks, open questions, test ideas, the comment the dot drafted) and the call is not marked `context`. These read the ticket and show nothing: "What is TAD-3 about?", a refusal ("do not review TAD-3"), a request for a new review ("review TAD-3 again", "please review TAD-3", which `review_ticket` answers), a question about the tracker ("the latest comment on TAD-3 in Jira"), and a general risk ("the risks of shipping TAD-3"). A model that reads the ticket before it acts marks the call `context`, and then no question can bring the old review back.
325
+ - **A call shows no block.** The voice call hears the answer, so the server builds no block for the `call` channel. The call prompt asks the model to say the key items in words. A Slack thread gets the same words, and the reply is stored with its blocks for the app chat.
326
+ - **No rows, no block.** A failed call, an empty result, and a result with no rows send no block, and the answer says so in text.
327
+ - **Unknown data becomes a table.** Rows no component row can hold (a result without run ids, an approval the store no longer holds) go to the compact `table` block through the same fallback as any other unknown data, so the answer still shows something and never an empty frame.
328
+ - **Actions stay inside the gate.** A row carries only what the server holds. The approvals block reads the stored records, and a ticket review carries an `approvalId` only for the pending `ticket_comment` draft of its own run, so Post comment is the person approving that exact text. Rerun, Review now, and the memory buttons call the routes the app already has. The model cannot add a link, an approval id, or an endpoint.
329
+ - **A stored reply reads its records again.** `GET /api/messages` runs `refreshStoredBlocks` on every stored block. An approvals block shows each draft as it stands now and drops a draft the store no longer holds. A ticket review loses its `approvalId` once the draft is no longer pending, so a stale card cannot approve it. A runs block shows the run as it ended, and a memory block drops a deleted note.
330
+
331
+ ### Lead QA chat
332
+
333
+ The chat of a Lead QA dot (KAT-405) has two more tools, both built from stored rows with no model call: `team_status` answers with a `table` block headed Team health (Dot, Job, Status, Needs a person, one row per member), and `release_call` answers with the `release_call` block. The read tools above take a member's name or id as `dotId`, or `all` (the default), and the rows say which member each came from. `rerun_tests`, `review_pr`, `review_ticket`, and `design_tests` go to the member whose job it is, under that member's own rules and cap; the answer reads "Asked PR Reviewer to review PR 412." and the result replies in the lead's chat with the member's name. One turn sends at most 3 such requests. The `release_call` block shows the answer word and the milestone, the blocking reasons under Why, the risks under Also with an Open button each, and the counts and the rule under Details. The block never offers release or hold.
334
+
335
+ ## Tests
336
+
337
+ - `client/src/features/chat/blocks/blocks.test.tsx`: validation and fallback rules, the timeline, every component and its actions against stubbed API calls, and the tool line left out for a call whose data a block shows. `tool-lines.test.tsx`: the tool rows, folding words, the details toggle, errors, and reference chips in the answer text. `reasoning-card.test.tsx`: fold and unfold, the clock timer, the step count, the Details part with the token count, text-only steps, the live region, and the call transcript line.
338
+ - `test/chat-reasoning.test.ts`: steps from the stream of each backend with and without summaries, tokens from `model_calls`, the stored work, and call receipts. `e2e/chat-reasoning.spec.ts`: the card while working and folded on the real client.
339
+ - `client/src/features/chat/blocks/charts.test.tsx`: stats and chart validation and fallback, the axis math, and what the components draw (lines with gaps, bars sideways and upright, stacked bars, the hidden data table, RefChips).
340
+ - `test/chat-stats.test.ts`: the pass rate and flaky-by-spec numbers from fixed runs and a fixed clock, and a chat turn that streams and stores the blocks.
341
+ - `test/chat-tool-blocks.test.ts`: one test per lookup tool (the block is emitted, matches the stored data, and passes the validator), the `context` flag and the call channel, the table fallback, the gate rules for `approvalId`, the stored reply read again, and the block on the Anthropic API, Claude CLI, and Codex CLI backends, including the rule-based fallback.
342
+ - `test/chat-eval-blocks.test.ts`: the chat eval fails a case when an expected block kind is missing (`blocks`, `blocksAny`, `noBlocks`), the case table in CHAT-EVAL.md names the kinds, and the runner (`src/cli/eval-chat.ts`) is run on the mock backend to check that it reads the `block` events of the stream.
343
+ - `client/src/features/chat/blocks/triage.test.tsx`, `test/chat-blocks.test.ts`, and `test/chat-triage.test.ts`: triage validation, the view rule on seven phrasings, grouping, each action against its endpoint, and the state after a reload.
344
+ - `test/triage-review.test.ts`: rows from review findings in each defect state, Draft defect by bug key, the window each phrasing picks, and the empty-state text.
345
+ - `client/src/features/cards/cards.test.tsx`: every card kind (what it shows, the state colors by `data-state`, the marks per status and priority), each button against its stubbed route and its result in place, the comment field by keyboard, the unfurl of pasted links (read once per address, never for a bare key, errors with and without a retry), the cards block and its fallback, the run and Inbox lookups. `test/object-cards.test.ts`: references, the PR and ticket cards from fakes of GitHub, Jira, and Linear, the lookup route, the gated comment and status routes (act, ask, sandbox, archived), and the chat tools. `test/client-css.test.ts` checks the card colors and contrast in both themes.
346
+ - `e2e/object-cards.spec.ts`: a streamed answer with all four cards, the keyboard path, the buttons against stubbed routes, a pasted link, the run card (the card of the run's own ticket, not a fixture), the Inbox cards, a PR in a list and a linked PR, and with `LOCALDOST_SCREENS=1` a screenshot of every card in both themes at 390x844 and 1280x800 (`LOCALDOST_SHOTS_DIR`).
347
+ - `e2e/cards-live.ts` (`npx tsx e2e/cards-live.ts`): the cards from live server data. It starts the real server on port 5182 with an empty database, answers the GitHub, Jira, and Linear APIs from fixed fake responses, builds the client, and drives Chromium over a chat answer, three pasted links, a run, and the Inbox with nothing stubbed in the browser. It checks the data on the cards (the counts of the fake checks, the review decision, the stored QA report, the linked PRs, the last review, the run's card against its trigger) and saves its screenshots as `live-*.png`. It exits 1 when a check fails.
348
+ - `test/view-spec.test.ts`, `test/view-compute.test.ts`, and `test/chat-views.test.ts` (KAT-366): the spec check (unknown component or prop, depth and node caps, strings, markup, links, injection strings, a pipeline with one bad step), the expression language over fixed rows, `test_results`, `compose_view` (binding, the stream while the model writes, `view_drop`, the fallback chain, rows that were cut, typed figures, the id each result carries, the link to the stored answer on Slack and calls), and the three questions through the rule-based chat, and over a busy dot whose rows were cut. `test/view-worker-csp.test.ts`: the worker script is served with a policy that forbids connections. `test/chat-views.test.ts` also runs `ViewStream` on input cut at every character and through a fake of the Anthropic stream. `test/chat-eval-blocks.test.ts`: the three view cases of the chat eval.
349
+ - `client/src/features/chat/blocks/view.test.tsx` (KAT-366): the three views drawn from fixed rows, streaming and placeholders, a part that fails, tabs by keyboard, the actions of a block inside a view, the notes under a view (cut rows, the clock), words and units, `view_drop`, and the worker (it cannot reach the network on its scope or a prototype, a slow one is stopped). `client/src/features/chat/answer-link.test.tsx`: the route of a linked answer and the transcript that scrolls to it. `e2e/chat-views.spec.ts`: the same in Chromium with the real worker and its policy header, the probe of a script with that policy and no lockdown, a view that is dropped, a link to an answer, a live run against the server, and with `LOCALDOST_SCREENS=1` screenshots of the views and the fallback states in both themes at 390x844 and 1280x800 (`LOCALDOST_SHOTS_DIR`, default `/tmp/qd-evidence/kat366`).
350
+ - `e2e/chat-blocks.spec.ts`: replays the fixtures (`client/src/features/chat/blocks/fixtures.ts`) through a stubbed `/api/chat` stream in the real app, then runs each action and the keyboard path, and checks the page never scrolls sideways.
351
+ - Screenshots at 390x844 and 1440x900 in light and dark: `LOCALDOST_SCREENS=1 npx playwright test e2e/chat-blocks.spec.ts -g screenshots`, saved to `test-results/chat-ui/`.
352
+
353
+ ## Object cards
354
+
355
+ A PR, an issue, a Jira ticket, and a Linear ticket show as compact cards wherever they appear: in a chat answer, under a message that holds their link, in a run, and in the Inbox. Each card mirrors the visual language of its own product at small size and is built with our own components and the brand marks of KAT-84. Nothing is embedded or loaded from those products, and there are no remote avatar images: a person is their initials.
356
+
357
+ | Card | Head | Body | Colors |
358
+ |---|---|---|---|
359
+ | GitHub PR | Repo, `#number`, when it changed | Title, state pill (Open, Draft, Merged, Closed), `base <- head` branches, checks row, review state, `+lines -lines files`, author, labels, the latest localdost report | Pill: open green, merged purple, closed red, draft grey (Primer's public palette), white text at 4.5:1 |
360
+ | GitHub issue | Repo, `#number`, when it changed | Title, state pill (Open, Closed, Closed as not planned), who opened it, assignee, labels in their own colors | The same pill colors |
361
+ | Jira | Issue type mark, key, status lozenge | Summary, priority chevrons, assignee, labels as grey tags | Lozenge by status category (to do grey, in progress blue, done green), light and dark sets |
362
+ | Linear | Priority bars, identifier, status circle with its word | Title, labels with a dot, assignee | Status circle by kind (backlog dashed, to do ring, in progress half, done filled, canceled crossed) |
363
+
364
+ A ticket card also shows a description preview (three lines), the PRs that close or reference it with their titles and "Merged" when a merge run shows it, and the last localdost ticket review with its risk and whether its comment waits in the Inbox. A PR card shows checks as "All 7 checks pass", "1 of 7 checks failing", "3 of 5 checks running", or "No checks". When one failed or is still running, the card lists checks under that line, one row each with its own status icon and its name in plain sight, failed first, then running, then passed, up to five, with "2 more checks" for the rest. A name stays on one line and cuts with an ellipsis; its full text is the tooltip. When every check passes, the summary line stands alone. Every state has its own shape and its word, so color is never the only cue.
365
+
366
+ ### Where the data comes from
367
+
368
+ `src/object-cards.ts` builds a card from what the dot's connections already read. The PR comes from `getPull` (state, draft, branches, lines, labels, requested reviewers), its checks from the check runs and commit statuses on the head (a token writes localdost' own check as a status), its review decision from the PR's reviews (the latest verdict of each reviewer counts, a dismissed one does not, changes requested wins), and its report from the newest stored run of that PR. A ticket comes from `fetchTicketRef`, which now also returns `status`, `statusKind`, `assignee` and `updatedAt` (Jira `status` and `assignee`, a Linear `state` and `assignee`, a GitHub issue's state and assignees). Linked PRs come from the links table (`closes`, `references`), titles from the stored runs. The review reads that were missing are new and optional on the GitHub client (`pullReviews`, `commitStatuses`), and a read that fails or times out leaves its row out: the card never shows a guess as passing.
369
+
370
+ `GET /api/cards?ref=<link, key, or owner/name#N>&kind=<github_pr|github_issue|jira|linear>&dotId=<id>` answers `{ card }`. Without `dotId` the dot that watches the repo or lists the tracker key answers, else the only dot. Errors say why in words: 404 for a PR or ticket nobody has, 409 for a repo the dot does not watch or a tracker that is not connected, 502 when GitHub does not answer. `parseObjectCard` in `src/chat-blocks.ts` checks every card on both sides, with the same rules as the blocks: strings capped, links `http` or `https` only, label colors six hex digits or none.
371
+
372
+ ### Buttons and the gate
373
+
374
+ Each action is a labeled button, and its result shows in place under the buttons.
375
+
376
+ | Action | What it does |
377
+ |---|---|
378
+ | Open | Opens the object in its own product, in a new tab |
379
+ | Review now (PR) | `POST /api/dots/:id/review { pr, repo }`: queues the PR check, or the merge review for a merged PR. Not shown on a closed PR |
380
+ | Review ticket | The same route with `{ ticket }`: queues a ticket review. Its comment waits in the Inbox under the dot's rule |
381
+ | Comment | Opens a field. Post comment sends `POST /api/cards/comment { ref, kind, dotId, text }`. A PR comment needs a repo the dot watches (409 otherwise, the same answer the card read gives), and is a `pr_comment` action marked `plain`, a new comment that replaces no QA report draft and never edits the sticky one. A ticket comment is a `ticket_comment` action marked `append`, so the sticky ticket review is not overwritten. The dot's rule for that kind decides: it posts, or it waits in the Inbox |
382
+ | View diff (PR) | The PR's files in GitHub |
383
+ | Change status (ticket) | Loads the statuses the tracker offers now (`GET /api/cards/status-options`: Jira transitions, a Linear team's states, or close, close as not planned, and reopen for a GitHub issue), asks for a pick, then `POST /api/cards/status { ref, kind, dotId, optionId }`. It is the new `ticket_status` action, `ask` by default and a row of its own in the rules matrix. An approved change calls the tracker: a Jira transition, a Linear `issueUpdate`, or a GitHub issue state. The draft in the Inbox has no Edit button: the move runs from the status that was picked, not from the draft's words, and `POST /api/approvals/:id` refuses an edit of it with 400 |
384
+
385
+ Both writes answer `{ result, runId, approvalId?, note? }`. `executed` says it went out, `pending_approval` says it waits in the Inbox with a link, and `blocked` or `handed_off` carry the reason in `note` ("Sandbox mode is on, so nothing is sent outside localdost."). Each write runs on a run of its own, like a chat action, so the Inbox groups it under its PR or ticket. Sandbox mode refuses both, and an archived dot answers 409. A button never posts to GitHub, Jira, or Linear by itself.
386
+
387
+ ### Where cards appear
388
+
389
+ - **Chat answers.** `get_ticket`, `review_ticket`, and `review_pr` stream a `cards` block with `tool` set, so the lookup line leaves the answer. The PR card is built after the tool answers, so GitHub's reads never hold the model.
390
+ - **Pasted links.** A GitHub PR or issue address, a Jira browse address, or a Linear issue address in a message unfurls into its card under the message, up to three, each object once. A bare key or number does not unfurl. The client and the server read the message with one function (`src/object-links.ts`, `@object-links` in the client), so the dot's own answer adds no second card for exactly the objects that unfurled, and a fourth link, or an address that does not unfurl (a Jira board link), still gets its card in the answer. A link the dot cannot read shows the server's reason in one line, with Try again only for a failure that may pass. The transcript stays at the bottom while a card loads.
391
+ - **Run detail.** The Trigger section shows the card of the run's PR or ticket above the facts.
392
+ - **Inbox.** A group of drafts for a PR, issue, or ticket shows its card on the page, above the drafts, with no button first. The card reads its object when it comes near the screen (400px of margin), so a long Inbox does not read every PR and ticket at once; until then the card's place is a plain frame. A card the dot cannot read for a reason a retry will not change (not connected, not watched, not found) leaves no note in the Inbox, since the group head already names the object; a failure that may pass keeps its Try again.
393
+ - **Lists and reviews.** A PR in the `pulls` block and a PR linked from a ticket card each have a Show card button that opens the PR's own card under the row (read when it opens, hidden again with Hide card). The `ticket_review` block starts with the ticket's card.
394
+
395
+ The card is a size container: it lays out by its own width, the same on a phone and in a wide reply. Buttons wrap to a second row on a phone and keep full labels, 44px tall on touch and compact with a mouse. Light and dark are both designed, and the colors of GitHub, Jira, and Linear are tokens in `client/src/features/cards/cards.css` with checked contrast.
396
+
397
+ ## Steps card
398
+
399
+ Each answer has a Steps card above its text (`client/src/features/chat/ReasoningCard.tsx`, KAT-374). A tester reads "reasoning" as model internals, so the card says Steps. Its header is one line in everyday words: `Working, 2 steps so far · 0:12` while the dot works and `Checked 4 things in 16 s` when the answer is done.
400
+
401
+ - Steps are the summary lines the backend returned of its reasoning: a Codex CLI `reasoning` item, an Anthropic API summarized `thinking` block (`src/agent/reasoning.ts`). The server keeps the bold title or the first sentence of each, masks secrets, and caps it at 120 characters. A redacted thinking block, raw thinking, and the Claude CLI thinking stream never reach the chat. Each line renders as text, never as HTML.
402
+ - Without a summary, each tool call is a step, in the tool-line words below. Either way each tool call is a row of the card: the running icon and `Checking runs`, then the result, and a tap opens it with `Show details`.
403
+ - The timer reads `m:ss` and counts from the question while the dot works; once done the card shows the server's time for the turn (`16 s`, `2 min 5 s`).
404
+ - The token count is not in the header. The expanded card ends with a Details part (`Tokens 269`) that sums the turn's `model_calls` rows. When no row knows its tokens (the mock, a CLI that reports none) there is no Details part.
405
+ - The card is open while the dot works and folds to its header line when the answer is done. A tap opens or folds it (`aria-expanded` on the toggle); a fold chosen while the dot works holds until the answer is done. A polite live region says each new step (`Step 2: Reading the ticket`) and the end (`Checked 4 things in 16 s`). The spinner stops for reduced motion.
406
+ - A plain answer with no steps has no card. A stored answer comes back folded, with its steps and tool rows.
407
+ - Slack answers end with one line, `Checked 4 things in 16 s`, and the link to the dot's chat (`Answered in 2 s` when the turn took no steps). A call transcript shows the folded line under each answer.
408
+
409
+ ## Tool lines
410
+
411
+ The calls a dot makes while it answers show as one quiet line above the answer text, collapsed: a small status icon, a past-tense phrase with the result in plain words (`Checked 213 runs`, `Read KAT-123 Cleaner lists`, `Looked up 2 tickets`), and the state word (`Done`, `Working`, `Failed`) where the line is wide. Several calls fold into one line (`Checked runs, 3 PRs and 2 tickets`). While a call runs the line reads `Checking runs` with the running icon, then turns into the done line.
412
+
413
+ - A tap lists the calls with their plain results. `Show details` inside the list opens the raw input and result of each call, for debugging.
414
+ - A call whose data an answer block already shows (`tool` on the block) is left out of the line. The match is by tool name, but a call the model marked `context` stays on the line, so a turn with a context lookup and an answering call of the same tool shows the lookup and the block. When every call is left out, there is no line.
415
+ - A failed call (`tool_result.error`) is never folded away or hidden: it keeps a line of its own with the reason, such as `Couldn't check runs: Which dot? More than one exists; pass dotId.`
416
+ - The words come from `client/src/features/chat/tool-lines.ts`; the server sends only the tool name, its input, and a one-line result.
@@ -0,0 +1,89 @@
1
+ # MCP connections
2
+
3
+ A connection plugs an MCP server into the dots' chat. The services localdost itself needs (model, GitHub, trackers, Slack, voice) connect from the Setup page instead, see [SETUP.md](SETUP.md). Once you attach it to a dot, the dot can call the server's tools when you chat with it in the app or in Slack. A test management system, a feature flag service, or an internal admin API are typical examples.
4
+
5
+ ## Add a connection
6
+
7
+ Open Settings, Connections, and add a connection, or call `POST /api/connections`.
8
+
9
+ | Field | Purpose |
10
+ |---|---|
11
+ | Name | Display name, at most 60 characters |
12
+ | Transport | `http` for a Streamable HTTP server, `stdio` for a command localdost starts on its own host |
13
+ | URL | `http` only: the server's MCP endpoint, for example `https://mcp.example.com/mcp` |
14
+ | Command and arguments | `stdio` only: for example `npx` with `-y` and `@acme/mcp-server` |
15
+ | Secret env var | Name of an environment variable that holds a JSON object. For `http` it holds the request headers, for `stdio` extra environment variables |
16
+
17
+ The secret itself never goes into the database or through the API. You put it in the server's environment, and localdost reads the variable on every connect:
18
+
19
+ ```bash
20
+ export ACME_MCP_HEADERS='{"Authorization":"Bearer <token>"}'
21
+ ```
22
+
23
+ Then enter `ACME_MCP_HEADERS` as the secret env var. In `.env.localdost`, reference it from your shell or a password manager the same way as other secrets.
24
+
25
+ After you save, localdost connects within 20 seconds and lists the server's tools. A server with more than 100 tools is cut at 100. Each tool has its own switch, so you can turn off the ones a dot should never see. Refresh a connection after the server adds tools.
26
+
27
+ ## Presets
28
+
29
+ A preset fills in a known MCP server for you: the command, the env var names it reads, and which of its tools only read. In Settings, Connections, choose **Add from preset**, or call `POST /api/connections/presets/:id`.
30
+
31
+ | Preset | What it starts | Env vars passed to the server |
32
+ |---|---|---|
33
+ | `aivision`: AI-Vision (Veesan) | `npx tsx <checkout>/apps/playground/demo/veesan-mcp.mts` from a local checkout of the AI-Vision server | `VEESAN_ENGINE_URL` or `AIVISION_PORT`, `VEESAN_TENANT`, and `VEESAN_SESSION_COOKIE` or `AIVISION_INTERNAL_TOKEN` when the server has sign-in on |
34
+ | `midscene-web-bridge`: Midscene MCP (web bridge) | `npx -y @midscene/web-bridge-mcp`, the open-source Midscene server; the Midscene Chrome extension must be in bridge mode | `MIDSCENE_MODEL_BASE_URL`, `MIDSCENE_MODEL_API_KEY`, `MIDSCENE_MODEL_NAME`, `MIDSCENE_MODEL_FAMILY` |
35
+ | `katalon-true-platform`: Katalon True Platform | An HTTP connection to the Katalon MCP server at the URL you enter. It starts no command | `KATALON_MCP_HEADERS` (request headers as JSON), attached only when the server has it set |
36
+
37
+ Set the env vars on the localdost server, the same way as other secrets. The connection stores the names only and copies the values into the server process on every connect. A preset passes only the names it declares, so no other localdost secret reaches the MCP process. For the AI-Vision preset, `AIVISION_CHECKOUT` pre-fills the checkout path, and the AI-Vision server itself must be running.
38
+
39
+ The Katalon True Platform preset is the one HTTP preset. It takes one parameter, the https address of the server, and stores that address on the connection. It carries no secret: when the localdost server has `KATALON_MCP_HEADERS`, the connection points at that variable by name. It marks nothing read-only itself, so a tool the server does not annotate as read-only goes through the `mcp_tool` rule (Ask first). The Next steps card offers it as the row **Connect True Platform**.
40
+
41
+ The AI-Vision MCP server sends no `readOnlyHint`, so the preset names its read-only tools: listing and reading tests, runs, run status, reports, evidence, coverage, findings, and waiting for a run. These run without approval. Starting, stopping, or changing anything is an `mcp_tool` action and goes through the rules gate. Two guards apply on top:
42
+
43
+ - `veesan_runs_wait` is capped at 50 seconds per call, inside the 60 second tool timeout. The dot calls it again while the run is not settled.
44
+ - `veesan_runs_report` cannot take `saveTo`, so a read never writes a file on the host.
45
+
46
+ The Midscene preset marks nothing read-only itself. Only tools the server annotates as read-only run without approval.
47
+
48
+ When a dot has a connected AI-Vision connection, its chat tools carry the run loop: find cases, start a run (approval), wait, then read verdicts and screenshots. Flows reach the same tools through `callPresetTool(watch, 'aivision', tool, input, runId)` with the same rules, and `presetHintsForWatch(watch)` returns a short paragraph that the dot's chat prompt includes on every backend. The `codex-cli` and `claude-cli` backends reach the same tools through the localdost chat MCP server, so the model decides when to call `veesan_tests_list` or start a run, and a run start still waits for approval.
49
+
50
+ ## Attach to a dot
51
+
52
+ A connection does nothing until a dot uses it. In Settings, select the dot and attach the connection, or set the dot's `connections` field to a list of connection ids with `PATCH /api/dots/:id`. Deleting a connection detaches it from every dot.
53
+
54
+ In chat, each enabled tool appears as `mcp__<connection>__<tool>`.
55
+
56
+ ## Rules for tools
57
+
58
+ MCP servers mark read-only tools with the `readOnlyHint` annotation. localdost trusts that mark:
59
+
60
+ - A read-only tool runs as soon as the model calls it.
61
+ - Any other tool is an outward action of kind `mcp_tool`. It passes the rules gate like a PR comment. The default rule is `ask`, so the call waits in the Inbox with its exact input until you approve it.
62
+
63
+ Change the `mcp_tool` rule of a dot only when you trust every write tool on its attached servers. A tool call times out after 60 seconds, and a result longer than 20,000 characters is cut.
64
+
65
+ ## Stdio safety
66
+
67
+ A stdio connection runs a command on the localdost host with the server's user. localdost accepts a new stdio connection, or a change to one, only when one of these holds:
68
+
69
+ - `LOCALDOST_TOKEN` is set, so only token holders reach the API.
70
+ - The request comes from the same machine.
71
+ - `LOCALDOST_ALLOW_STDIO=1` is set.
72
+
73
+ Prefer `http` connections on a shared or hosted server.
74
+
75
+ ## Data
76
+
77
+ A tool call sends its input to the MCP server, and the result goes into the model prompt for that chat turn. Pick servers you would trust with the dot's chat context.
78
+
79
+ ## API
80
+
81
+ | Route | Purpose |
82
+ |---|---|
83
+ | `GET /api/connections?dotId=` | Connections, optionally only those attached to a dot |
84
+ | `POST /api/connections` | Create, then connect and list tools |
85
+ | `PATCH /api/connections/:id` | Change fields, turn it on or off, or switch tools with `tools: [{ name, enabled }]` |
86
+ | `DELETE /api/connections/:id` | Delete and detach from every dot |
87
+ | `POST /api/connections/:id/refresh` | Reconnect and list tools again |
88
+ | `GET /api/connections/presets` | Presets with their params, env var names and whether each is set, and read-only tools |
89
+ | `POST /api/connections/presets/:id` | Create from a preset with `{ params, name? }`, then connect and list tools |