@kitn.ai/ui 0.25.2 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (603) hide show
  1. package/README.md +17 -6
  2. package/bin/mcp.js +16 -3
  3. package/bin/route.js +23 -0
  4. package/bin/route.test.js +58 -0
  5. package/dist/{elements/chunks/Icon-b5A8hlHs.js → Icon-BnFKqqtY.js} +1 -1
  6. package/dist/action-icons-CBskfBMz.js +1 -0
  7. package/dist/arrow-down-Br0qtpsz.js +1 -0
  8. package/dist/arrow-left-Dj7xgE12.js +1 -0
  9. package/dist/artifact-CvXEm1lS.js +1 -0
  10. package/dist/assets/dev-5XzXYR21.js +91 -0
  11. package/dist/attachments-C43ZegEq.js +1 -0
  12. package/dist/audio-visualizer-B-79y1Tj.js +1 -0
  13. package/dist/badge-BpUbq8c0.js +1 -0
  14. package/dist/bash-InADTalH.js +1 -6
  15. package/dist/bell-sn17Ip9v.js +1 -0
  16. package/dist/{elements/chunks/button-B9okqG73.js → button-BH6rN0U-.js} +1 -1
  17. package/dist/card-renderer-Cb1gQmth.js +1 -0
  18. package/dist/card-routing-DifQgS7n.js +1 -0
  19. package/dist/check-DPeeMUYx.js +1 -0
  20. package/dist/checkbox-Dc9Yov_B.js +1 -0
  21. package/dist/checkbox-group-B1kDLLYk.js +1 -0
  22. package/dist/chevron-down-CHSWk7ZT.js +1 -0
  23. package/dist/chevron-right-B7l_fMgx.js +1 -0
  24. package/dist/choice-card-BT3c1S8g.js +1 -0
  25. package/dist/circle-Bb0yOpqM.js +1 -0
  26. package/dist/circle-check-BGZpCiz7.js +1 -0
  27. package/dist/circle-x-wobWkoSx.js +1 -0
  28. package/dist/{elements/chunks/cn-CU7UAWzk.js → cn-DN5AWfiS.js} +1 -1
  29. package/dist/code-block-CUQ5mtv0.js +1 -0
  30. package/dist/collapsible-CPJbf6FM.js +1 -0
  31. package/dist/components/attachment-types.d.ts +53 -1
  32. package/dist/components/attachments.d.ts +31 -0
  33. package/dist/components/audio-visualizer/fit-scale.d.ts +50 -0
  34. package/dist/components/audio-visualizer/index.d.ts +15 -1
  35. package/dist/components/audio-visualizer/variant-bar.d.ts +24 -1
  36. package/dist/components/card-renderer.d.ts +13 -0
  37. package/dist/components/card.d.ts +4 -0
  38. package/dist/components/chat-thread.d.ts +49 -42
  39. package/dist/components/code-block.d.ts +22 -0
  40. package/dist/components/conversation-item.d.ts +66 -1
  41. package/dist/components/conversation-list.d.ts +77 -0
  42. package/dist/components/form-widgets.d.ts +67 -2
  43. package/dist/components/form.d.ts +89 -0
  44. package/dist/components/message.d.ts +29 -0
  45. package/dist/components/reasoning.d.ts +6 -0
  46. package/dist/components/scroll-button.d.ts +11 -0
  47. package/dist/components/thread.d.ts +5 -0
  48. package/dist/components/toast.d.ts +2 -1
  49. package/dist/components/tool-types.d.ts +9 -0
  50. package/dist/components/voice-input.d.ts +8 -0
  51. package/dist/components/voice-output.d.ts +12 -1
  52. package/dist/components/workspace-shell.d.ts +87 -0
  53. package/dist/composer-CjngLzal.js +106 -0
  54. package/dist/confirm-card-DCx_A4bU.js +1 -0
  55. package/dist/construct-cli.es.js +1169 -0
  56. package/dist/context-BY8V-Nlp.js +1 -0
  57. package/dist/controllable-D43BKmtX.js +1 -0
  58. package/dist/conversation-list-D4lLU_yS.js +1 -0
  59. package/dist/copy-B6DO4PqB.js +1 -0
  60. package/dist/core-AYMC6_lb.js +12 -5874
  61. package/dist/{create-tween-CUzOlaMS.js → create-tween-BPzaefTp.js} +1 -1
  62. package/dist/{create-tween-B5Y4Im8G.js → create-tween-CnMHviKB.js} +1 -1
  63. package/dist/{create-tween-4z1XYLkR.js → create-tween-D1aiGZZh.js} +1 -1
  64. package/dist/{elements/chunks/create-tween-CUrM6D_o.js → create-tween-DUSQUKpr.js} +2 -2
  65. package/dist/css-M7EaDHN_.js +1 -6
  66. package/dist/custom-elements.json +3468 -1927
  67. package/dist/default-input-CucQEOqH.js +1 -0
  68. package/dist/define-CGGX-7Vg.js +1 -0
  69. package/dist/define.d.ts +4 -0
  70. package/dist/define.js +510 -0
  71. package/dist/define.server.d.ts +5 -0
  72. package/dist/define.server.js +506 -0
  73. package/dist/diagnostics/hook.d.ts +81 -0
  74. package/dist/diagnostics/index.d.ts +7 -0
  75. package/dist/diagnostics/report-request.d.ts +61 -0
  76. package/dist/diagnostics.d.ts +5 -0
  77. package/dist/diagnostics.js +209 -0
  78. package/dist/disclosure-CbLtajUQ.js +1 -0
  79. package/dist/dropdown-BwhViJFd.js +1 -0
  80. package/dist/elements/agent-card.js +1 -1
  81. package/dist/elements/artifact.js +1 -1
  82. package/dist/elements/attachments.js +1 -1
  83. package/dist/elements/audio-visualizer.js +1 -1
  84. package/dist/elements/autoloader.js +1 -1
  85. package/dist/elements/avatar.js +1 -1
  86. package/dist/elements/badge.js +1 -1
  87. package/dist/elements/button.js +1 -1
  88. package/dist/elements/card.js +1 -1
  89. package/dist/elements/cards.js +1 -1
  90. package/dist/elements/chain-of-thought.js +1 -1
  91. package/dist/elements/chat-scope-picker.js +1 -1
  92. package/dist/elements/chat-workspace.js +1 -1
  93. package/dist/elements/chat.js +1 -1
  94. package/dist/elements/checkbox-group.d.ts +1 -0
  95. package/dist/elements/checkbox-group.js +1 -0
  96. package/dist/elements/checkbox.d.ts +1 -0
  97. package/dist/elements/checkbox.js +1 -0
  98. package/dist/elements/checkpoint.js +1 -1
  99. package/dist/elements/choice.js +1 -1
  100. package/dist/elements/coachmark.js +1 -1
  101. package/dist/elements/code-block.js +1 -1
  102. package/dist/elements/command.js +1 -1
  103. package/dist/elements/compare.js +1 -1
  104. package/dist/elements/composer.js +1 -1
  105. package/dist/elements/confirm-card.js +1 -1
  106. package/dist/elements/context-meter.js +1 -1
  107. package/dist/elements/conversation-item.d.ts +1 -0
  108. package/dist/elements/conversation-item.js +1 -0
  109. package/dist/elements/conversation-list.js +1 -1
  110. package/dist/elements/default-input.d.ts +3 -3
  111. package/dist/elements/define-entry.d.ts +12 -0
  112. package/dist/elements/define.d.ts +27 -0
  113. package/dist/elements/diagnostic-events.d.ts +74 -0
  114. package/dist/elements/dialog.js +1 -1
  115. package/dist/elements/dock.d.ts +1 -0
  116. package/dist/elements/dock.js +243 -0
  117. package/dist/elements/dropdown.d.ts +1 -0
  118. package/dist/elements/dropdown.js +1 -0
  119. package/dist/elements/editable-label.js +1 -1
  120. package/dist/elements/element-data-types.d.ts +37 -0
  121. package/dist/elements/element-diagnostics.d.ts +81 -0
  122. package/dist/elements/embed.js +1 -1
  123. package/dist/elements/empty.js +1 -1
  124. package/dist/elements/feedback-bar.js +1 -1
  125. package/dist/elements/file-tree.js +1 -1
  126. package/dist/elements/file-upload.js +1 -1
  127. package/dist/elements/form.js +1 -1
  128. package/dist/elements/hover-card.js +1 -1
  129. package/dist/elements/icon.js +1 -1
  130. package/dist/elements/image.js +1 -1
  131. package/dist/elements/input.js +1 -1
  132. package/dist/elements/kbd.js +1 -1
  133. package/dist/elements/link-preview.js +1 -1
  134. package/dist/elements/loader.js +1 -1
  135. package/dist/elements/markdown.js +1 -1
  136. package/dist/elements/menu.js +1 -1
  137. package/dist/elements/message-skills.js +1 -1
  138. package/dist/elements/message.js +1 -1
  139. package/dist/elements/model-switcher.js +1 -1
  140. package/dist/elements/nav.js +1 -1
  141. package/dist/elements/notice.js +1 -1
  142. package/dist/elements/pane-grid.d.ts +1 -0
  143. package/dist/elements/pane-grid.js +1 -0
  144. package/dist/elements/pane-group.js +1 -1
  145. package/dist/elements/pane.js +1 -1
  146. package/dist/elements/popover.js +1 -1
  147. package/dist/elements/progress-bar.js +1 -1
  148. package/dist/elements/prompt-dock.js +1 -1
  149. package/dist/elements/prompt-input.js +1 -1
  150. package/dist/elements/prompt-suggestions.js +1 -1
  151. package/dist/elements/radio-group.d.ts +1 -0
  152. package/dist/elements/radio-group.js +1 -0
  153. package/dist/elements/reasoning.js +1 -1
  154. package/dist/elements/register.d.ts +2 -0
  155. package/dist/elements/remote.js +1 -1
  156. package/dist/elements/resizable.js +1 -1
  157. package/dist/elements/response-stream.js +3 -3
  158. package/dist/elements/screen.js +1 -1
  159. package/dist/elements/scroll-area.js +1 -1
  160. package/dist/elements/scroll-button.js +1 -1
  161. package/dist/elements/search.js +1 -1
  162. package/dist/elements/segmented.js +1 -1
  163. package/dist/elements/select.d.ts +1 -0
  164. package/dist/elements/select.js +1 -0
  165. package/dist/elements/separator.js +1 -1
  166. package/dist/elements/setting-item.js +1 -1
  167. package/dist/elements/settings-group.js +1 -1
  168. package/dist/elements/skeleton.js +1 -1
  169. package/dist/elements/slider.d.ts +1 -0
  170. package/dist/elements/slider.js +1 -0
  171. package/dist/elements/slots.d.ts +36 -4
  172. package/dist/elements/source.js +1 -1
  173. package/dist/elements/status.js +1 -1
  174. package/dist/elements/switch.js +1 -1
  175. package/dist/elements/tabs.js +1 -1
  176. package/dist/elements/tasks.js +1 -1
  177. package/dist/elements/text-shimmer.js +1 -1
  178. package/dist/elements/thinking-bar.js +1 -1
  179. package/dist/elements/thread.js +1 -1
  180. package/dist/elements/toast.js +1 -1
  181. package/dist/elements/tool.js +1 -1
  182. package/dist/elements/tooltip.js +1 -1
  183. package/dist/elements/voice-input.js +1 -1
  184. package/dist/elements/voice-output.js +1 -1
  185. package/dist/elements.d.ts +589 -167
  186. package/dist/{elements/chunks/ellipsis-CVxqfsDZ.js → ellipsis-DFr4Vqr3.js} +1 -1
  187. package/dist/embed-5s_vU4J0.js +1 -0
  188. package/dist/engine-javascript-vq0WuIJl.js +14 -2516
  189. package/dist/{elements/chunks/external-link-DG2sRdQL.js → external-link-u9d85ORQ.js} +1 -1
  190. package/dist/file-text-CX7_x42o.js +1 -0
  191. package/dist/{elements/chunks/file-tree-yGWNK0Js.js → file-tree-D3kBjXI5.js} +1 -1
  192. package/dist/folder-V-YdIL8f.js +1 -0
  193. package/dist/form-BRNUMCN2.js +1 -0
  194. package/dist/github-dark-dimmed-DUshB20C.js +1 -4
  195. package/dist/github-light-JYsPkUQd.js +1 -4
  196. package/dist/hover-card-DaeiYrTA.js +1 -0
  197. package/dist/html-CPZ3oZQ7.js +1 -10
  198. package/dist/icon-BpixdZNq.js +1 -0
  199. package/dist/{elements/chunks/index-BCRhi84a.js → index-xu_pdcPi.js} +1 -1
  200. package/dist/index.d.ts +3 -1
  201. package/dist/index.js +8473 -6470
  202. package/dist/index.server.js +6495 -4958
  203. package/dist/info-8mFsIjS3.js +1 -0
  204. package/dist/input-d1Yuu-hz.js +1 -0
  205. package/dist/javascript-C25yR2R2.js +1 -6
  206. package/dist/json-DxJze_jm.js +1 -6
  207. package/dist/kai-provider.es.js +40 -40
  208. package/dist/kai.es.js +1 -1
  209. package/dist/kbd-961zMPFB.js +1 -0
  210. package/dist/link-DlZZBGaR.js +1 -0
  211. package/dist/{elements/chunks/link-preview-Cf5KOP-i.js → link-preview-DIIbV2Sd.js} +1 -1
  212. package/dist/loader-YQszHFaW.js +1 -0
  213. package/dist/{elements/chunks/markdown-DbozyYqa.js → markdown-CVk1MPsj.js} +1 -1
  214. package/dist/mcp.es.js +3873 -544
  215. package/dist/message-CwKVEdZg.js +1 -0
  216. package/dist/message-DS8lzHc1.js +1 -0
  217. package/dist/message-circle-Xf8Q-vZp.js +1 -0
  218. package/dist/message-square-Cxnwgkns.js +1 -0
  219. package/dist/{elements/chunks/minimize-2-SegJ_oku.js → minimize-2-UEZmP2QT.js} +1 -1
  220. package/dist/{elements/chunks/model-switcher-D7JrFFHp.js → model-switcher-Kk1jEQ5t.js} +1 -1
  221. package/dist/{elements/chunks/overlay-CV7z2YuX.js → overlay-C1od6LOz.js} +1 -1
  222. package/dist/panel-right-ryET0yyn.js +1 -0
  223. package/dist/paperclip-BDNoZFJP.js +1 -0
  224. package/dist/play-OFIvYXUQ.js +1 -0
  225. package/dist/primitives/card-data-types.d.ts +49 -0
  226. package/dist/primitives/card-routing.d.ts +1 -33
  227. package/dist/primitives/card-validate.d.ts +2 -2
  228. package/dist/primitives/chat-config.d.ts +29 -2
  229. package/dist/primitives/field-mask.d.ts +96 -0
  230. package/dist/primitives/field-semantics.d.ts +36 -0
  231. package/dist/primitives/input-mask.d.ts +46 -0
  232. package/dist/primitives/url-scheme-policy.d.ts +34 -0
  233. package/dist/primitives/use-resize-observer.d.ts +23 -0
  234. package/dist/primitives/use-speech-recognition.d.ts +8 -0
  235. package/dist/primitives/use-text-stream.d.ts +5 -0
  236. package/dist/progress-bar-BvYX0aoy.js +1 -0
  237. package/dist/prompt-suggestion-BRFu2lcu.js +1 -0
  238. package/dist/radio-DcL23cNJ.js +1 -0
  239. package/dist/react/index.d.ts +306 -283
  240. package/dist/react.js +309 -247
  241. package/dist/reasoning-Cackq4Rh.js +1 -0
  242. package/dist/register-impl-C7rENvPK.js +424 -0
  243. package/dist/{elements/chunks/resizable-Dpzjlvp4.js → resizable-Cp9HUYih.js} +1 -1
  244. package/dist/rotate-ccw-0DLRDHH5.js +1 -0
  245. package/dist/rotate-cw-DOB9IGcU.js +1 -0
  246. package/dist/schemas/form.schema.json +21 -1
  247. package/dist/schemas/index.d.ts +1 -1
  248. package/dist/schemas/tool-defs.d.ts +45 -0
  249. package/dist/schemas.js +398 -305
  250. package/dist/scroll-area-emVmWP4K.js +1 -0
  251. package/dist/scroll-button-CeP_gv1x.js +1 -0
  252. package/dist/select-CWmu00tS.js +1 -0
  253. package/dist/{elements/chunks/separator-DH2r_ZSZ.js → separator-CE6vly6z.js} +1 -1
  254. package/dist/{elements/chunks/settings-BFMOd6yB.js → settings-CE-78SvZ.js} +1 -1
  255. package/dist/{elements/chunks/settings-group-B3VZKIcz.js → settings-group-ZFWNxFQR.js} +1 -1
  256. package/dist/{elements/chunks/skeleton-N8BuWYmf.js → skeleton-BebdP4mf.js} +1 -1
  257. package/dist/slider-BAKRYUlz.js +1 -0
  258. package/dist/slots-DaNjX0rc.js +1 -0
  259. package/dist/{solid-BMuDmFo_.js → solid-BE5ui6sy.js} +10672 -8116
  260. package/dist/{solid-CGYNdLRR.js → solid-DFhsce3O.js} +8137 -6056
  261. package/dist/solid.d.ts +15 -0
  262. package/dist/solid.js +248 -235
  263. package/dist/solid.server.js +248 -235
  264. package/dist/source-B0FlguSx.js +1 -0
  265. package/dist/star-CBMZvLuN.js +1 -0
  266. package/dist/state/index.d.ts +5 -1
  267. package/dist/state/mock.d.ts +32 -2
  268. package/dist/state/persistence.d.ts +51 -0
  269. package/dist/state/stream.d.ts +50 -0
  270. package/dist/state/threads.d.ts +71 -0
  271. package/dist/state.js +297 -172
  272. package/dist/store-7eoadydQ.js +1 -0
  273. package/dist/svelte--5p79yCD.js +1 -15
  274. package/dist/switch-CViARZCy.js +1 -0
  275. package/dist/tasks-card-MjCcOhR_.js +1 -0
  276. package/dist/text-shimmer-DKlwRF2G.js +1 -0
  277. package/dist/textarea-3ay2Me2-.js +1 -0
  278. package/dist/theme.tokens.css +82 -13
  279. package/dist/{elements/chunks/thumbs-up-DQe0hvkZ.js → thumbs-up-DQcu2HMO.js} +1 -1
  280. package/dist/toast-store-C6yKOhbk.js +1 -0
  281. package/dist/{elements/chunks/tool-C1YtxwPu.js → tool-BCYNlfKy.js} +1 -1
  282. package/dist/{elements/chunks/tooltip-BqwnFWhn.js → tooltip-05zI4hjs.js} +1 -1
  283. package/dist/trash-2-BR1RxGky.js +1 -0
  284. package/dist/{elements/chunks/triangle-alert-B6or7F5u.js → triangle-alert-DHohDOCy.js} +1 -1
  285. package/dist/tsx-B8rCNbgL.js +1 -6
  286. package/dist/types.d.ts +4 -2
  287. package/dist/typescript-RycA9KXf.js +1 -6
  288. package/dist/ui/checkbox-group.d.ts +83 -0
  289. package/dist/ui/checkbox.d.ts +40 -0
  290. package/dist/ui/dock.d.ts +131 -0
  291. package/dist/ui/hover-card.d.ts +44 -0
  292. package/dist/ui/icon.d.ts +3 -0
  293. package/dist/ui/input.d.ts +48 -2
  294. package/dist/ui/radio.d.ts +85 -0
  295. package/dist/ui/select.d.ts +71 -0
  296. package/dist/ui/slider.d.ts +65 -0
  297. package/dist/ui/switch.d.ts +9 -2
  298. package/dist/upload-BbGW6A_u.js +1 -0
  299. package/dist/url-scheme-policy-DHpoTJwB.js +1 -0
  300. package/dist/use-card-resolution-CJW4grFa.js +1 -0
  301. package/dist/{variant-aurora-DwSOfraQ.js → variant-aurora-BUOVgPOB.js} +27 -27
  302. package/dist/{variant-aurora-wKvAQ5wp.js → variant-aurora-CUsUJvFI.js} +28 -28
  303. package/dist/{variant-aurora-XEkPT0_z.js → variant-aurora-CW2i4bfr.js} +2 -2
  304. package/dist/{elements/chunks/variant-aurora-QXTZPyST.js → variant-aurora-xiMPty8f.js} +2 -2
  305. package/dist/variant-custom-BQtOnmeO.js +1 -0
  306. package/dist/{variant-custom-BhS9wIpU.js → variant-custom-CAm9L-E5.js} +65 -65
  307. package/dist/{variant-custom-eFIg2XWQ.js → variant-custom-CAnzzppW.js} +40 -40
  308. package/dist/variant-custom-D91HjAKz.js +1 -0
  309. package/dist/{variant-wave-CUWGx9Bn.js → variant-wave-Ca1Ls4_y.js} +21 -21
  310. package/dist/{variant-wave-CbVsmgwT.js → variant-wave-CxX39DKd.js} +25 -25
  311. package/dist/{variant-wave-BwzrHD1s.js → variant-wave-DYyzIIZv.js} +2 -2
  312. package/dist/{elements/chunks/variant-wave-DDCwhLas.js → variant-wave-uY5crU_K.js} +2 -2
  313. package/dist/vue-BmIZj4XD.js +1 -33
  314. package/dist/wire/chunk.d.ts +47 -0
  315. package/dist/wire/diagnostics.d.ts +575 -0
  316. package/dist/wire/encode-probe.d.ts +70 -0
  317. package/dist/wire/encode.d.ts +24 -0
  318. package/dist/wire/index.d.ts +2 -0
  319. package/dist/wire/sse.d.ts +10 -2
  320. package/dist/wire.js +917 -480
  321. package/dist/x-C5m1JFgi.js +1 -0
  322. package/frameworks/react/index.tsx +325 -108
  323. package/llms-full.txt +1354 -82
  324. package/llms.txt +6 -3
  325. package/package.json +39 -6
  326. package/src/agent-tooling/archetypes.ts +41 -9
  327. package/src/agent-tooling/catalog/README.md +676 -0
  328. package/src/agent-tooling/catalog/catalog-types.ts +231 -0
  329. package/src/agent-tooling/catalog/fabrications.ts +96 -0
  330. package/src/agent-tooling/catalog/invariants.ts +284 -0
  331. package/src/agent-tooling/catalog/labs-titles.ts +114 -0
  332. package/src/agent-tooling/catalog/scenarios.ts +87 -0
  333. package/src/agent-tooling/catalog/surfaces.ts +347 -0
  334. package/src/agent-tooling/construct/cli-entry.ts +8 -0
  335. package/src/agent-tooling/construct/cli.ts +131 -0
  336. package/src/agent-tooling/construct/codegen.ts +1353 -0
  337. package/src/agent-tooling/construct/construct.v1.schema.json +250 -0
  338. package/src/agent-tooling/construct/dev.ts +148 -0
  339. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +7 -0
  340. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +44 -0
  341. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +23 -0
  342. package/src/agent-tooling/construct/schema.ts +371 -0
  343. package/src/agent-tooling/integrations/anthropic.ts +7 -1
  344. package/src/agent-tooling/integrations/cloudflare.ts +43 -2
  345. package/src/agent-tooling/integrations/langgraph.ts +7 -1
  346. package/src/agent-tooling/integrations/mastra.ts +7 -1
  347. package/src/agent-tooling/integrations/mock.ts +92 -17
  348. package/src/agent-tooling/integrations/ollama.ts +7 -1
  349. package/src/agent-tooling/integrations/openai.ts +7 -1
  350. package/src/agent-tooling/integrations/openrouter.ts +7 -1
  351. package/src/agent-tooling/integrations/pi.ts +56 -1
  352. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +7 -1
  353. package/src/agent-tooling/mcp/css-raw.d.ts +12 -0
  354. package/src/agent-tooling/mcp/manifest.ts +100 -1
  355. package/src/agent-tooling/mcp/server.ts +63 -6
  356. package/src/agent-tooling/mcp/tools/construct.ts +130 -0
  357. package/src/agent-tooling/mcp/tools/debug.ts +126 -4
  358. package/src/agent-tooling/mcp/tools/reference.ts +542 -5
  359. package/src/agent-tooling/mcp/tools/scaffold.ts +1834 -307
  360. package/src/agent-tooling/mcp/tools/theme.ts +146 -30
  361. package/src/agent-tooling/mcp/validate-args.ts +141 -0
  362. package/src/agent-tooling/recipes/composed-thread.ts +714 -0
  363. package/src/agent-tooling/recipes/index.ts +22 -0
  364. package/src/agent-tooling/recipes/types.ts +32 -0
  365. package/src/agent-tooling/registry.ts +24 -7
  366. package/src/agent-tooling/route-emit.ts +26 -2
  367. package/src/components/attachment-types.ts +54 -1
  368. package/src/components/attachments.tsx +169 -22
  369. package/src/components/audio-visualizer/fit-scale.ts +112 -0
  370. package/src/components/audio-visualizer/index.tsx +80 -3
  371. package/src/components/audio-visualizer/variant-aurora.tsx +10 -4
  372. package/src/components/audio-visualizer/variant-bar.tsx +38 -6
  373. package/src/components/audio-visualizer/variant-custom.tsx +8 -2
  374. package/src/components/audio-visualizer/variant-grid.tsx +16 -11
  375. package/src/components/audio-visualizer/variant-radial.tsx +15 -10
  376. package/src/components/audio-visualizer/variant-wave.tsx +7 -2
  377. package/src/components/card-renderer.tsx +39 -8
  378. package/src/components/card.tsx +10 -2
  379. package/src/components/chat-container.tsx +4 -1
  380. package/src/components/chat-scope-picker.tsx +2 -2
  381. package/src/components/chat-thread.tsx +105 -13
  382. package/src/components/choice-card.tsx +102 -37
  383. package/src/components/coachmark.tsx +1 -1
  384. package/src/components/code-block.tsx +81 -5
  385. package/src/components/composer.tsx +89 -8
  386. package/src/components/confirm-card.tsx +20 -4
  387. package/src/components/conversation-item.tsx +138 -4
  388. package/src/components/conversation-list.tsx +230 -15
  389. package/src/components/embed.tsx +4 -0
  390. package/src/components/empty.tsx +14 -3
  391. package/src/components/file-tree.tsx +5 -1
  392. package/src/components/form-widgets.tsx +257 -195
  393. package/src/components/form.tsx +336 -21
  394. package/src/components/loader.tsx +12 -5
  395. package/src/components/message-skills.tsx +1 -1
  396. package/src/components/message.tsx +142 -12
  397. package/src/components/prompt-input.tsx +7 -1
  398. package/src/components/prompt-suggestion.tsx +4 -1
  399. package/src/components/reasoning.tsx +30 -3
  400. package/src/components/response-compare.tsx +2 -2
  401. package/src/components/response-stream.tsx +13 -7
  402. package/src/components/screen.tsx +5 -0
  403. package/src/components/scroll-button.tsx +63 -4
  404. package/src/components/source.tsx +6 -1
  405. package/src/components/tasks-card.tsx +58 -16
  406. package/src/components/thread.tsx +10 -1
  407. package/src/components/toast.tsx +11 -5
  408. package/src/components/tool-types.ts +9 -0
  409. package/src/components/voice-input.tsx +35 -3
  410. package/src/components/voice-output.tsx +46 -6
  411. package/src/components/workspace-shell.tsx +285 -0
  412. package/src/diagnostics/hook.ts +352 -0
  413. package/src/diagnostics/index.ts +82 -0
  414. package/src/diagnostics/report-request.ts +296 -0
  415. package/src/elements/attachments.tsx +9 -8
  416. package/src/elements/audio-visualizer.tsx +48 -4
  417. package/src/elements/chat-workspace.tsx +159 -287
  418. package/src/elements/chat.tsx +31 -10
  419. package/src/elements/checkbox-group.tsx +164 -0
  420. package/src/elements/checkbox.tsx +134 -0
  421. package/src/elements/code-block.tsx +19 -1
  422. package/src/elements/command.tsx +4 -1
  423. package/src/elements/compiled.css +1 -1
  424. package/src/elements/composer.tsx +12 -1
  425. package/src/elements/conversation-item.tsx +120 -0
  426. package/src/elements/conversation-list.tsx +60 -11
  427. package/src/elements/default-input.tsx +5 -5
  428. package/src/elements/define-entry.ts +12 -0
  429. package/src/elements/define.tsx +168 -8
  430. package/src/elements/diagnostic-events.ts +114 -0
  431. package/src/elements/dialog.tsx +47 -0
  432. package/src/elements/disclosure.ts +34 -12
  433. package/src/elements/dock.tsx +215 -0
  434. package/src/elements/dropdown.tsx +123 -0
  435. package/src/elements/element-data-types.ts +40 -0
  436. package/src/elements/element-diagnostics.ts +392 -0
  437. package/src/elements/element-manifest.json +36 -0
  438. package/src/elements/element-meta.json +1483 -560
  439. package/src/elements/element-nonscalar.json +185 -0
  440. package/src/elements/element-types.d.ts +589 -167
  441. package/src/elements/feedback-bar.tsx +5 -0
  442. package/src/elements/icon-names.json +29 -0
  443. package/src/elements/input.tsx +312 -23
  444. package/src/elements/menu.tsx +13 -0
  445. package/src/elements/message.tsx +2 -0
  446. package/src/elements/pane-grid.tsx +111 -0
  447. package/src/elements/prompt-dock.tsx +2 -1
  448. package/src/elements/prompt-input.tsx +16 -10
  449. package/src/elements/radio-group.tsx +129 -0
  450. package/src/elements/register-impl.ts +52 -0
  451. package/src/elements/register.ts +28 -0
  452. package/src/elements/resizable.tsx +52 -25
  453. package/src/elements/scroll-button.tsx +59 -6
  454. package/src/elements/select.tsx +165 -0
  455. package/src/elements/slider.tsx +171 -0
  456. package/src/elements/slots.ts +108 -13
  457. package/src/elements/styles.css +119 -9
  458. package/src/elements/thinking-bar.tsx +16 -7
  459. package/src/elements/thread.tsx +2 -0
  460. package/src/elements/toast.tsx +17 -1
  461. package/src/elements/tool.tsx +26 -5
  462. package/src/elements/voice-input.tsx +11 -2
  463. package/src/elements/voice-output.tsx +16 -3
  464. package/src/index.ts +4 -1
  465. package/src/primitives/card-data-types.ts +49 -0
  466. package/src/primitives/card-host.tsx +4 -2
  467. package/src/primitives/card-routing.ts +7 -56
  468. package/src/primitives/card-schemas/form.schema.json +21 -1
  469. package/src/primitives/card-validate.ts +10 -2
  470. package/src/primitives/chat-config.tsx +33 -6
  471. package/src/primitives/field-mask.ts +256 -0
  472. package/src/primitives/field-semantics.ts +115 -0
  473. package/src/primitives/input-mask.ts +853 -0
  474. package/src/primitives/toast-store.ts +66 -4
  475. package/src/primitives/url-scheme-policy.ts +70 -0
  476. package/src/primitives/use-resize-observer.ts +33 -0
  477. package/src/primitives/use-speech-recognition.ts +12 -1
  478. package/src/primitives/use-stick-to-bottom.ts +34 -2
  479. package/src/primitives/use-text-stream.ts +33 -2
  480. package/src/remote/provider-runtime.ts +6 -3
  481. package/src/schemas/index.ts +1 -0
  482. package/src/schemas/tool-defs.ts +307 -6
  483. package/src/solid.ts +15 -0
  484. package/src/state/index.ts +13 -1
  485. package/src/state/mock.ts +92 -6
  486. package/src/state/persistence.ts +180 -0
  487. package/src/state/stream.ts +71 -4
  488. package/src/state/threads.ts +142 -0
  489. package/src/types.ts +4 -2
  490. package/src/ui/agent-card.tsx +2 -2
  491. package/src/ui/avatar.tsx +1 -1
  492. package/src/ui/checkbox-group.tsx +153 -0
  493. package/src/ui/checkbox.tsx +63 -0
  494. package/src/ui/dialog.tsx +6 -0
  495. package/src/ui/dock.tsx +584 -0
  496. package/src/ui/dropdown.tsx +67 -23
  497. package/src/ui/hover-card.tsx +156 -6
  498. package/src/ui/icon.tsx +53 -0
  499. package/src/ui/input.tsx +275 -16
  500. package/src/ui/kbd.tsx +1 -1
  501. package/src/ui/nav.tsx +1 -1
  502. package/src/ui/pane-group.tsx +2 -2
  503. package/src/ui/radio.tsx +150 -0
  504. package/src/ui/resizable.tsx +11 -0
  505. package/src/ui/select.tsx +168 -0
  506. package/src/ui/slider.tsx +178 -0
  507. package/src/ui/switch.tsx +49 -18
  508. package/src/utils/cn.ts +11 -5
  509. package/src/wire/chunk.ts +47 -0
  510. package/src/wire/consume.ts +185 -26
  511. package/src/wire/diagnostics.ts +727 -0
  512. package/src/wire/encode-probe.ts +214 -0
  513. package/src/wire/encode.ts +267 -14
  514. package/src/wire/formats/anthropic.ts +8 -1
  515. package/src/wire/formats/openai.ts +5 -0
  516. package/src/wire/index.ts +22 -0
  517. package/src/wire/read.ts +250 -6
  518. package/src/wire/sse.ts +16 -3
  519. package/theme.css +84 -13
  520. package/dist/elements/chunks/action-icons-DW9muWrY.js +0 -1
  521. package/dist/elements/chunks/arrow-left-iJVnQmxA.js +0 -1
  522. package/dist/elements/chunks/artifact-BSuPYFHY.js +0 -1
  523. package/dist/elements/chunks/attachments-CL6TaqIb.js +0 -1
  524. package/dist/elements/chunks/audio-visualizer-BuUqQmaO.js +0 -1
  525. package/dist/elements/chunks/badge-TIMiJFKc.js +0 -1
  526. package/dist/elements/chunks/bash-InADTalH.js +0 -1
  527. package/dist/elements/chunks/card-renderer-CSoTL_e1.js +0 -1
  528. package/dist/elements/chunks/card-routing-BK09BdmL.js +0 -1
  529. package/dist/elements/chunks/chat-thread-DxoKaStk.js +0 -1
  530. package/dist/elements/chunks/check-MdLzbxrm.js +0 -1
  531. package/dist/elements/chunks/chevron-down-MK7IzDqe.js +0 -1
  532. package/dist/elements/chunks/chevron-right-C5jXhmtV.js +0 -1
  533. package/dist/elements/chunks/choice-card-CGMc3kek.js +0 -1
  534. package/dist/elements/chunks/circle-BG7VkPep.js +0 -1
  535. package/dist/elements/chunks/circle-check-GqvO2UBs.js +0 -1
  536. package/dist/elements/chunks/circle-x-mhH7pOK2.js +0 -1
  537. package/dist/elements/chunks/code-block-ClhQO9RW.js +0 -1
  538. package/dist/elements/chunks/collapsible-DBcBkFPh.js +0 -1
  539. package/dist/elements/chunks/composer-CZZmtYOT.js +0 -69
  540. package/dist/elements/chunks/confirm-card-BWjf2-xo.js +0 -1
  541. package/dist/elements/chunks/context-hKjNqiyQ.js +0 -1
  542. package/dist/elements/chunks/conversation-list-CsGJjSKG.js +0 -1
  543. package/dist/elements/chunks/core-AYMC6_lb.js +0 -12
  544. package/dist/elements/chunks/css-M7EaDHN_.js +0 -1
  545. package/dist/elements/chunks/default-input-B1Mem5wu.js +0 -1
  546. package/dist/elements/chunks/define-B_tXMYRm.js +0 -1
  547. package/dist/elements/chunks/disclosure-DAoYOKew.js +0 -1
  548. package/dist/elements/chunks/download-DnCyWFJE.js +0 -1
  549. package/dist/elements/chunks/dropdown-CmNna5lT.js +0 -1
  550. package/dist/elements/chunks/embed-CjAD2jSi.js +0 -1
  551. package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +0 -141
  552. package/dist/elements/chunks/file-text-D0J9F6M7.js +0 -1
  553. package/dist/elements/chunks/folder-NV1pi_ud.js +0 -1
  554. package/dist/elements/chunks/form-Cp8Y00Wn.js +0 -1
  555. package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +0 -1
  556. package/dist/elements/chunks/github-light-JYsPkUQd.js +0 -1
  557. package/dist/elements/chunks/hover-card-6KtXpLwT.js +0 -1
  558. package/dist/elements/chunks/html-CPZ3oZQ7.js +0 -1
  559. package/dist/elements/chunks/icon-97R8SR2p.js +0 -1
  560. package/dist/elements/chunks/info-C8r5Q6cc.js +0 -1
  561. package/dist/elements/chunks/input-DrseEjhp.js +0 -1
  562. package/dist/elements/chunks/javascript-C25yR2R2.js +0 -1
  563. package/dist/elements/chunks/json-DxJze_jm.js +0 -1
  564. package/dist/elements/chunks/kbd-OGJDUT-O.js +0 -1
  565. package/dist/elements/chunks/link-BdsfLB7b.js +0 -1
  566. package/dist/elements/chunks/loader-B5QzK_8_.js +0 -1
  567. package/dist/elements/chunks/message-CE4k-qaj.js +0 -1
  568. package/dist/elements/chunks/message-DtXKPqiR.js +0 -1
  569. package/dist/elements/chunks/message-square-C9ovmtOo.js +0 -1
  570. package/dist/elements/chunks/paperclip-Bg0ITU3b.js +0 -1
  571. package/dist/elements/chunks/progress-bar-D1_rzt6N.js +0 -1
  572. package/dist/elements/chunks/prompt-suggestion-D5Sm8xF1.js +0 -1
  573. package/dist/elements/chunks/reasoning-BrxQfpfI.js +0 -1
  574. package/dist/elements/chunks/rotate-cw-DzVDJOA1.js +0 -1
  575. package/dist/elements/chunks/scroll-area-93A1bbeG.js +0 -1
  576. package/dist/elements/chunks/scroll-button-CrTo7wm4.js +0 -1
  577. package/dist/elements/chunks/share-DgNbkJKY.js +0 -1
  578. package/dist/elements/chunks/slots-CIw9RlAe.js +0 -1
  579. package/dist/elements/chunks/source-w9V0NnBn.js +0 -1
  580. package/dist/elements/chunks/star-DHvACxtm.js +0 -1
  581. package/dist/elements/chunks/store-HIPGhNz9.js +0 -1
  582. package/dist/elements/chunks/svelte--5p79yCD.js +0 -1
  583. package/dist/elements/chunks/tasks-card-lPxoodlA.js +0 -1
  584. package/dist/elements/chunks/text-shimmer-BY_rI3s5.js +0 -1
  585. package/dist/elements/chunks/textarea-DPfuhswM.js +0 -1
  586. package/dist/elements/chunks/toast-store-VUZZBhzH.js +0 -1
  587. package/dist/elements/chunks/tsx-B8rCNbgL.js +0 -1
  588. package/dist/elements/chunks/typescript-RycA9KXf.js +0 -1
  589. package/dist/elements/chunks/use-card-resolution-DJg32jGH.js +0 -1
  590. package/dist/elements/chunks/variant-custom-D5ZlmziF.js +0 -1
  591. package/dist/elements/chunks/video-Bb86fVYI.js +0 -1
  592. package/dist/elements/chunks/vue-BmIZj4XD.js +0 -1
  593. package/dist/elements/chunks/x-BOhjBeOd.js +0 -1
  594. package/dist/llms/llms-full.txt +0 -2834
  595. package/dist/llms/llms.txt +0 -156
  596. package/dist/primitives/card-validate-generator.testlib.d.ts +0 -20
  597. package/dist/register-impl-DGmGzrEM.js +0 -145
  598. package/dist/ui/stat.d.ts +0 -16
  599. package/dist/variant-custom-MkTNTycd.js +0 -1
  600. package/src/primitives/card-validate-generator.testlib.ts +0 -38
  601. package/src/ui/stat.tsx +0 -41
  602. /package/dist/{elements/chunks/card-tags-D8lZ-C_U.js → card-tags-D8lZ-C_U.js} +0 -0
  603. /package/dist/{elements/chunks/link-preview-DNILK391.js → link-preview-DNILK391.js} +0 -0
@@ -0,0 +1,727 @@
1
+ // The wire diagnostic event stream: what the parse pipeline SAW, as metadata.
2
+ //
3
+ // The kit is the only layer that sees both "frames arrived" and "parts came
4
+ // out", so it is the only layer that can tell a wrong-dialect read from a quiet
5
+ // one. That fact was previously visible nowhere; this is where it gets said.
6
+ //
7
+ // METADATA BY DEFAULT, and the rule is checkable rather than a matter of taste:
8
+ // if a value comes from the model, the end user, or the app's data, it is
9
+ // PAYLOAD; if it describes the shape, size, timing or identity of that value, it
10
+ // is METADATA. Every field below is metadata by that rule. `errorCode` yes,
11
+ // `message` no -- some providers echo request content back inside the message,
12
+ // which is why the code is the half that travels.
13
+ //
14
+ // PAYLOAD IS A SEPARATE, DELIBERATE SWITCH (`wirePayloadActive`), off by
15
+ // default, with its own signal that no URL can set. Every content-bearing value
16
+ // lives under ONE optional `payload` key, which is what makes the boundary
17
+ // reviewable: a reviewer checks one key rather than re-reading every field
18
+ // name, and `payload-boundary.test.ts` asserts it structurally by deleting that
19
+ // key and finding nothing left. The one thing that never appears under it
20
+ // either is a response URL's query string -- `?api_key=` is a CREDENTIAL, a
21
+ // different class from conversation content, and it does not get a switch.
22
+ //
23
+ // FORWARD COMPATIBILITY, producer side: never repurpose a field name and never
24
+ // change what a value means. New information is a new field or a new type.
25
+ // Consumers are required to ignore both unknown types and unknown fields, which
26
+ // is what lets a CDN-delivered panel float free of the kit's version.
27
+ //
28
+ // SSR: no `window`, no `Date` and no other global touched at module scope, so
29
+ // this imports cleanly anywhere the rest of `wire/` does.
30
+ import type { ModelUsage } from './chunk';
31
+ // TYPE-ONLY, and that is what makes it legal here. `elements/diagnostic-events`
32
+ // declares interfaces and nothing else -- no imports, no runtime -- so this
33
+ // specifier is erased at build and `wire/` acquires no dependency on
34
+ // `elements/`. A real one would be a layering inversion AND would drag the
35
+ // element bundle into every consumer that only parses streams.
36
+ //
37
+ // It has to be named from somewhere, because a subscriber receives ONE stream
38
+ // and the union is what lets it discriminate on `type` instead of being handed
39
+ // an open bag. The alternative -- declaring the element events inside this file
40
+ // -- puts element concepts in the wire layer, which is worse.
41
+ import type { ElementDiagnosticEvent } from '../elements/diagnostic-events';
42
+
43
+ /** The envelope every diagnostic event shares. `t` is `Date.now()` at emission. */
44
+ export interface WireDiagnosticBase {
45
+ type: string;
46
+ t: number;
47
+ /** Correlates every event from one provider response stream. */
48
+ streamId?: string;
49
+ /**
50
+ * The APP'S grouping of several reads into one logical turn. Present only
51
+ * when the app declared it (`ConsumeOptions.traceId`), absent otherwise.
52
+ *
53
+ * THE KIT GROUPS NOTHING ON ITS OWN. A tool loop or a set of sub-agents makes
54
+ * several model calls that belong together, and the kit sees one Response at
55
+ * a time with no way to know which ones those are. Inferring it -- by
56
+ * timing, by sink identity, by anything -- would produce a grouping that is
57
+ * right often enough to be trusted and wrong exactly when a session is
58
+ * confusing enough to need a panel.
59
+ */
60
+ traceId?: string;
61
+ /** The app's name for THIS read within the trace (`'planner'`,
62
+ * `'executor'`). Present only when declared; never derived from the format,
63
+ * the model or the URL. */
64
+ label?: string;
65
+ }
66
+
67
+ /** The stream opened: the source resolved and the format is about to be opened.
68
+ *
69
+ * CONNECTION IDENTITY (`url`, `hasQuery`, `status`, `contentType`) is reported
70
+ * only when the source was a `Response`, and each field is ABSENT rather than
71
+ * empty when the response did not state it. A `Response` built in a test or by
72
+ * a service worker has `url: ''`, and an empty string there would read as "it
73
+ * was served from the origin root", which is a confident wrong answer. */
74
+ export interface WireOpenEvent extends WireDiagnosticBase {
75
+ type: 'wire.open';
76
+ /** `opts.format.id`, e.g. `openai.chat-completions`. */
77
+ format: string;
78
+ source: 'response' | 'stream' | 'iterable';
79
+ /**
80
+ * ORIGIN AND PATHNAME ONLY. The query string is never reported, on any
81
+ * switch, and it is not payload either: `?api_key=sk-...` is a CREDENTIAL, a
82
+ * different class from the conversation content the payload key exists for,
83
+ * and a credential does not get a switch that turns it on. `hasQuery` carries
84
+ * the one bit a reader needs from it.
85
+ *
86
+ * Absent when the response did not state a URL, or stated one that does not
87
+ * parse.
88
+ */
89
+ url?: string;
90
+ /** Whether the response URL carried a query string at all. Absent exactly
91
+ * when `url` is. */
92
+ hasQuery?: boolean;
93
+ /** The HTTP status. Always 2xx here -- a non-ok response throws and reports
94
+ * `wire.failed` instead -- but reported verbatim rather than assumed. */
95
+ status?: number;
96
+ /**
97
+ * The response's `content-type` header, VERBATIM.
98
+ *
99
+ * THE FIELD THAT PAYS FOR THIS EVENT. `text/html` or `application/json` where
100
+ * `text/event-stream` was expected is the classic proxy or misconfiguration
101
+ * tell, and from inside the parse it is invisible: the frames simply never
102
+ * arrive and the turn resolves empty. The response knew all along.
103
+ */
104
+ contentType?: string;
105
+ }
106
+
107
+ /** One decoded SSE frame, and what the format made of it. */
108
+ export interface WireFrameEvent extends WireDiagnosticBase {
109
+ type: 'wire.frame';
110
+ /** 1-based. The final `seq` equals `wire.close`'s `frames`. */
111
+ seq: number;
112
+ /** UTF-8 byte length of the raw `data:` payload. */
113
+ bytes: number;
114
+ /** Neutral chunks this frame yielded. */
115
+ chunks: number;
116
+ /**
117
+ * The union of `Object.keys` over this frame's chunks. THE field that earns
118
+ * this design: frames arriving whose chunks never once carry a content key
119
+ * (`text`, `reasoning`, `toolCalls`, `sources`) is the failure a chunk count
120
+ * alone cannot separate from a healthy stream.
121
+ */
122
+ fields: string[];
123
+ /** Present when this frame stated a model id. */
124
+ model?: string;
125
+ /** Content-bearing, so opt-in: the raw `data:` payload, verbatim. */
126
+ payload?: { raw: string };
127
+ }
128
+
129
+ /** A message part the recorder actually produced. */
130
+ export interface WirePartEvent extends WireDiagnosticBase {
131
+ type: 'wire.part';
132
+ /** The `MessagePart` type: 'text' | 'reasoning' | 'tool' | 'source'. */
133
+ variant: string;
134
+ index: number;
135
+ /** Delta LENGTH, never the delta. Present for text and reasoning only. */
136
+ chars?: number;
137
+ /** Content-bearing, so opt-in. One key per variant this write can carry:
138
+ * `delta` for text and reasoning, `patch` for a tool write (which holds the
139
+ * arguments and the output), `source` for a citation. */
140
+ payload?: { delta?: string; patch?: unknown; source?: unknown };
141
+ }
142
+
143
+ /** The turn finished. Everything the widened empty-turn guard judges on. */
144
+ export interface WireCloseEvent extends WireDiagnosticBase {
145
+ type: 'wire.close';
146
+ /** Absent when `consumeModelStream` was called directly: there were no frames. */
147
+ frames?: number;
148
+ chunks: number;
149
+ /** Count per variant actually produced. Empty beside a non-zero `frames` is
150
+ * the wrong-dialect signature. */
151
+ parts: Record<string, number>;
152
+ finishReason: string | null;
153
+ stopReason?: string;
154
+ /** The kit's own closed vocabulary, so a panel keys its explanation off the
155
+ * code and never needs the message. */
156
+ errorCode?: string | number;
157
+ usage?: ModelUsage;
158
+ ms: number;
159
+ /** Content-bearing, so opt-in: the assembled turn, plus the in-band error's
160
+ * own message.
161
+ *
162
+ * ALL THREE TERMINAL EVENTS TREAT PROVIDER MESSAGE TEXT IDENTICALLY --
163
+ * `wire.close`, `wire.failed` and `wire.interrupted`. Each faces the same
164
+ * hazard, that a provider's message can echo request content back, and
165
+ * payload is precisely the switch that accepts it. Disagreeing here would
166
+ * read as an oversight and be "fixed" later by someone with less context. */
167
+ payload?: {
168
+ text: string;
169
+ reasoning: string;
170
+ toolCalls: unknown[];
171
+ sources: unknown[];
172
+ /** The in-band error's message. Absent when the turn carried no error. */
173
+ message?: string;
174
+ };
175
+ }
176
+
177
+ /**
178
+ * The read DIED. A terminal event for a stream that never reached `wire.close`.
179
+ *
180
+ * WHY IT EXISTS: without it, a stream that errored mid-read emitted `wire.open`
181
+ * and then nothing at all -- no close, no failed -- and a panel showed that
182
+ * stream open forever. In an observability tool that is a request which
183
+ * silently vanished, and "the one that never finished" is precisely the request
184
+ * someone opens the panel to find.
185
+ *
186
+ * MUTUALLY EXCLUSIVE with `wire.close`: exactly one of the two ends a read that
187
+ * got as far as `wire.open`. It is emitted from `readModelStream` only, so a
188
+ * direct `consumeModelStream` caller (which never emitted `wire.open` either)
189
+ * does not get one.
190
+ *
191
+ * The error itself is UNCHANGED and still thrown: this observes and rethrows.
192
+ */
193
+ export interface WireInterruptedEvent extends WireDiagnosticBase {
194
+ type: 'wire.interrupted';
195
+ /** Frames decoded before it died. */
196
+ frames: number;
197
+ /** Neutral chunks yielded before it died. */
198
+ chunks: number;
199
+ /**
200
+ * `'abort'` only when the error IDENTIFIES ITSELF as one (`name` of
201
+ * `AbortError`), which is what a fetch abort produces. Anything else is
202
+ * `'error'` -- a guess here would turn "the provider dropped the connection"
203
+ * into "your user navigated away", and those have opposite fixes.
204
+ */
205
+ reason: 'error' | 'abort';
206
+ /** The caught error's `name` (`'AbortError'`, `'TypeError'`). Metadata: a
207
+ * closed-ish vocabulary a panel keys an explanation off. The MESSAGE is
208
+ * payload. Absent when the thrown value carries no readable name. */
209
+ errorName?: string;
210
+ /** Content-bearing, so opt-in. The error's own message, which can echo
211
+ * request content back the way a provider's error text does. */
212
+ payload?: { message?: string };
213
+ }
214
+
215
+ /** A non-ok HTTP response: the `WireError` path, before a chunk was read. */
216
+ export interface WireFailedEvent extends WireDiagnosticBase {
217
+ type: 'wire.failed';
218
+ status: number;
219
+ statusText: string;
220
+ bodyBytes: number;
221
+ bodyIsJson: boolean;
222
+ /** The parsed body's error CODE. Never the message: a provider's error text
223
+ * can echo request content back. */
224
+ providerCode?: string | number;
225
+ /** Content-bearing, so opt-in: the raw body and the provider's own error
226
+ * message, which is exactly the field that echoes request content back and
227
+ * is why the code is the half that travels by default. */
228
+ payload?: { bodyText: string; message?: string };
229
+ }
230
+
231
+ /** One attachment the encoder handled, and what became of it.
232
+ *
233
+ * MEDIA TYPE AND SIZE, never the name and never the bytes. A filename is
234
+ * something the user typed and is payload; the media type and the size are
235
+ * facts about the shape of what went on the wire. */
236
+ export interface EncodeAttachmentReport {
237
+ /** As the encoder settled it, which is not always what the host declared: a
238
+ * `data:` URI's own media type wins over the field beside it. Absent when
239
+ * nothing named the file and classification failed before settling one. */
240
+ mediaType?: string;
241
+ /**
242
+ * Byte length of the bytes that went on the wire.
243
+ *
244
+ * PRESENT ONLY WHEN PAYLOAD CAPTURE IS ON. Counting it exactly is an O(n)
245
+ * scan of the payload, and the whole thread is re-encoded every turn, so it
246
+ * is a recurring per-turn cost rather than a one-off -- the same rule
247
+ * `EncodeRequestEvent.bytes` follows, for the same reason. The fields around
248
+ * it cost nothing and are always present, so the diagnosis ("this attachment
249
+ * was skipped") survives without the refinement ("...and it was 240 kB").
250
+ *
251
+ * ABSENT for a remote attachment even then: the provider dereferences that
252
+ * URL itself and the bytes never enter this process, so any number would be
253
+ * invented -- and `0` beside a 40 MB PDF is the exact confident zero the
254
+ * forward-compat rule exists to prevent.
255
+ *
256
+ * Absent always means NOT REPORTED, and is never backfilled with an estimate.
257
+ */
258
+ bytes?: number;
259
+ /** Whether anything at all reached the wire for this attachment. */
260
+ encoded: boolean;
261
+ /**
262
+ * `'encoded'` -- became an image/file/document block.
263
+ * `'as-text'` -- a text file, inlined as text CONTENT, because neither API
264
+ * has an arbitrary-file block. Worth distinguishing: it is
265
+ * why an attachment can be "sent" and still not be visible to
266
+ * the model as a file.
267
+ * `'skipped'` -- nothing went out for it. See `reason`.
268
+ */
269
+ disposition: 'encoded' | 'skipped' | 'as-text';
270
+ /** Why it was skipped, in the encoder's own words -- the same sentence the
271
+ * throw would have carried. Present on `'skipped'` only. */
272
+ reason?: string;
273
+ }
274
+
275
+ /**
276
+ * A thread was encoded for a provider. THE WRITE-PATH HEADLINE.
277
+ *
278
+ * CONTEXT INTEGRITY is the question this answers: is the context that goes to
279
+ * the model the context you think it is? The encoder is the one layer that sees
280
+ * both sides -- the `ChatMessage[]` going in and the provider messages coming
281
+ * out -- so it is the only place the delta between them can be stated as data
282
+ * instead of as folklore.
283
+ *
284
+ * Every field is metadata: counts, variant names, media types, sizes. The
285
+ * encoded body itself lives under `payload`.
286
+ */
287
+ export interface EncodeRequestEvent extends WireDiagnosticBase {
288
+ type: 'encode.request';
289
+ format: 'openai' | 'anthropic';
290
+ /** `ChatMessage[]` in. */
291
+ threadMessages: number;
292
+ /** Provider messages out. NOT the same number, by design: one assistant turn
293
+ * carrying a tool call splits into three wire messages, and a truncating
294
+ * host shrinks it the other way. The delta is the point. */
295
+ wireMessages: number;
296
+ /**
297
+ * System-role messages in the ENCODED OUTPUT.
298
+ *
299
+ * ZERO IS THE COMMON ANSWER AND IT IS THE FINDING, which is why it is a
300
+ * stated 0 rather than an omitted key: `ChatMessage.role` has no system
301
+ * member at all, so the system prompt is always being added somewhere the kit
302
+ * cannot see -- a server route, a gateway, a middleware. A developer chasing
303
+ * "why is the model ignoring its instructions" needs to know the kit is not
304
+ * the layer holding them.
305
+ */
306
+ systemMessages: number;
307
+ /** Role counts over the ENCODED OUTPUT. A role with no messages is absent
308
+ * rather than 0; `systemMessages` is stated separately for that reason. */
309
+ byRole: Record<string, number>;
310
+ /** Part variants present in the THREAD, by count. */
311
+ partsIn: Record<string, number>;
312
+ /** Part variants that reached the wire, by count. `partsIn` minus this is the
313
+ * round-trip loss, and each loss also has its own `encode.dropped`. */
314
+ partsEncoded: Record<string, number>;
315
+ /** One entry per `file` part the encoder handled, in encounter order. */
316
+ attachments: EncodeAttachmentReport[];
317
+ /**
318
+ * UTF-8 byte length of the encoded body as JSON.
319
+ *
320
+ * PRESENT ONLY WHEN PAYLOAD CAPTURE IS ON, because producing it means
321
+ * serializing the whole body -- every inlined attachment included -- and
322
+ * that is a cost the metadata stream refuses to pay for one number. When the
323
+ * body is already being materialized for `payload`, it is free and exact.
324
+ * Absent also when the body could not be stringified at all (a circular
325
+ * `tool.output`, a BigInt). Absent means NOT REPORTED, never zero, and it is
326
+ * deliberately not backfilled with an estimate.
327
+ */
328
+ bytes?: number;
329
+ /** Content-bearing, so opt-in. `attachments` is positionally aligned with the
330
+ * metadata array above. */
331
+ payload?: { body: unknown; attachments?: Array<{ filename?: string }> };
332
+ }
333
+
334
+ /**
335
+ * A part that was in the thread and is NOT on the wire.
336
+ *
337
+ * THE EVENT THE WHOLE WRITE PATH IS FOR. "Your attachment rendered in the
338
+ * thread and was never sent to the model" was a sentence nobody could get out
339
+ * of the kit, and every one of these drops is deliberate, documented, and was
340
+ * invisible.
341
+ *
342
+ * It reports; it never decides. Nothing about what the encoders drop changed in
343
+ * order to add it.
344
+ */
345
+ export interface EncodeDroppedEvent extends WireDiagnosticBase {
346
+ type: 'encode.dropped';
347
+ /** The `MessagePart` type, taken from the part itself at the site that
348
+ * already discriminated it. */
349
+ variant: string;
350
+ /** Parts this event accounts for. Always 1 today -- every site reports per
351
+ * part, because per-part is what carries the indices -- and the field exists
352
+ * so a site that ever aggregates can say N without a new event type. */
353
+ count: number;
354
+ /** Position in the `ChatMessage[]` that was passed in. */
355
+ messageIndex?: number;
356
+ /** Position in that message's `parts`. */
357
+ partIndex?: number;
358
+ /** The reason documented at the drop site, in one sentence. */
359
+ reason: string;
360
+ /** Content-bearing, so opt-in: the dropped part itself, verbatim. */
361
+ payload?: { part: unknown };
362
+ }
363
+
364
+ /**
365
+ * What the APP says it sent. Emitted only by `reportRequest`, never by the kit
366
+ * on its own.
367
+ *
368
+ * ★ THE ONLY EVENT THE KIT DOES NOT OBSERVE FOR ITSELF, and that is the point.
369
+ * In the normal shape the app's server route adds the system prompt, picks the
370
+ * model, and performs any RAG or guardrail injection, and NONE of it passes
371
+ * through the kit -- which is why `encode.request.systemMessages` is always 0.
372
+ * That 0 says "the system prompt is being added somewhere I cannot see"; this
373
+ * event is how a developer chooses to make that somewhere visible.
374
+ *
375
+ * Deliberate disclosure, so it is the app's decision and not the kit's
376
+ * collection. See `reportRequest` for why that distinction is load-bearing.
377
+ */
378
+ export interface AppRequestEvent extends WireDiagnosticBase {
379
+ type: 'app.request';
380
+ /** Length of the body's `messages` array. Absent when there was no array to
381
+ * count -- which is a different fact from a request carrying none. */
382
+ messages?: number;
383
+ /**
384
+ * Role counts over that array, INCLUDING `system`.
385
+ *
386
+ * This is the half the kit structurally cannot see: `ChatMessage.role` has no
387
+ * system member at all, so a system prompt exists only in the body the app
388
+ * builds. A role nobody sent is absent rather than 0.
389
+ *
390
+ * ★ THESE COUNTS NEED NOT SUM TO `messages`, and a consumer must not assume
391
+ * they do. An entry stating no readable role is counted in `messages` and
392
+ * contributes to no bucket -- a body of 5 messages where 1 states a role
393
+ * gives `messages: 5` with `byRole: { user: 1 }`. That is deliberate:
394
+ * bucketing the rest under `'unknown'` would invent a role nobody sent. A
395
+ * panel rendering these as a stacked bar should render the difference as
396
+ * unattributed rather than scaling it away.
397
+ */
398
+ byRole?: Record<string, number>;
399
+ /**
400
+ * System-role messages, stated explicitly whenever the array could be counted.
401
+ *
402
+ * ZERO IS A FINDING and must be distinguishable from "not reported": it says
403
+ * nothing is setting a system prompt at this layer either. Reading it off
404
+ * `byRole.system` could not tell those apart, which is the whole reason this
405
+ * field is separate -- and it lines up directly against
406
+ * `EncodeRequestEvent.systemMessages` for the comparison that answers "are
407
+ * additional prompts being added?".
408
+ */
409
+ systemMessages?: number;
410
+ /** Length of the body's `tools` array. A present-but-empty array reports 0,
411
+ * which is a real and different state from the key being absent: it says the
412
+ * app meant to send tools and sent none. Absent when there is no array. */
413
+ tools?: number;
414
+ /**
415
+ * The REQUESTED model, read verbatim from the body.
416
+ *
417
+ * ★ NEVER INFERRED, and absent stays absent. Paired with `WireFrameEvent`'s
418
+ * SERVED model this is the devtools spec's "selected Claude, served gpt-4o"
419
+ * finding -- two independent facts a panel COMPARES and never reconciles.
420
+ * Deriving either half from the other would make them agree in exactly the
421
+ * mismatch case the pair exists to catch.
422
+ */
423
+ model?: string;
424
+ /** ORIGIN AND PATHNAME ONLY, on the same terms as `WireOpenEvent.url`: a
425
+ * query string can carry a credential, and a credential is not conversation
426
+ * content, so it does not travel under any switch. Absent when no URL was
427
+ * supplied or it did not parse. */
428
+ url?: string;
429
+ /** Whether that URL carried a query string. Absent exactly when `url` is. */
430
+ hasQuery?: boolean;
431
+ /**
432
+ * UTF-8 byte length of the body AS IT WILL BE SENT.
433
+ *
434
+ * PRESENT ONLY UNDER PAYLOAD CAPTURE, by the same rule
435
+ * `EncodeRequestEvent.bytes` follows: measuring means materializing the body,
436
+ * and a request is made every turn.
437
+ *
438
+ * ABSENT WHENEVER IT CANNOT BE KNOWN EXACTLY, which is a larger set than it
439
+ * looks. A string, a JSON-declaring body, a `Blob`/`File` and an
440
+ * `ArrayBuffer` or view can all be measured exactly and are. A `FormData`, a
441
+ * `ReadableStream`, a `URLSearchParams`, a `Map`, or a circular body cannot,
442
+ * and report nothing -- never the length of the `"{}"` they happen to
443
+ * stringify to, which is how a 40 MB upload came to report `bytes: 2`.
444
+ */
445
+ bytes?: number;
446
+ /**
447
+ * Content-bearing, so opt-in: the body itself, exactly as handed over.
448
+ *
449
+ * ★ PUBLISHED BY REFERENCE, NOT CLONED. A subscriber that mutates
450
+ * `payload.body` mutates the object the app is about to send. Deep-cloning
451
+ * every request would be expensive on exactly the large bodies worth
452
+ * inspecting, and would defeat the point of handing the real thing over -- so
453
+ * the contract is that a consumer TREATS THIS AS READ-ONLY. Clone it yourself
454
+ * if you intend to edit it.
455
+ */
456
+ payload?: { body: unknown };
457
+ }
458
+
459
+ export type WireDiagnosticEvent =
460
+ | WireOpenEvent
461
+ | WireFrameEvent
462
+ | WirePartEvent
463
+ | WireCloseEvent
464
+ | WireFailedEvent
465
+ | WireInterruptedEvent
466
+ | EncodeRequestEvent
467
+ | EncodeDroppedEvent
468
+ | AppRequestEvent;
469
+
470
+ /** The three correlating fields, built ONCE per read and spread onto every
471
+ * event it emits. One definition so `read.ts` and `consume.ts` cannot drift
472
+ * about which events carry a trace. */
473
+ export interface WireCorrelation {
474
+ streamId: string;
475
+ traceId?: string;
476
+ label?: string;
477
+ }
478
+
479
+ /**
480
+ * INTERNAL. The correlation for one read.
481
+ *
482
+ * ABSENT, NOT UNDEFINED. `traceId` and `label` are omitted entirely when the app
483
+ * did not declare them, so a consumer's `'traceId' in event` answers "did the
484
+ * app group this" rather than "did the kit have an opinion". A key present with
485
+ * an undefined value is the kit claiming it looked, and it is not in a position
486
+ * to make that claim.
487
+ */
488
+ export function wireCorrelation(
489
+ streamId: string,
490
+ opts: { traceId?: string; label?: string },
491
+ ): WireCorrelation {
492
+ return {
493
+ streamId,
494
+ ...(opts.traceId !== undefined ? { traceId: opts.traceId } : {}),
495
+ ...(opts.label !== undefined ? { label: opts.label } : {}),
496
+ };
497
+ }
498
+
499
+ /**
500
+ * EVERY diagnostic event, from every layer that emits one.
501
+ *
502
+ * There is one emitter and one stream, so there has to be one union. `wire.*`
503
+ * events say what the parse pipeline saw; `element.*` events say what a
504
+ * consumer did to a live custom element. A panel wants both and wants them in
505
+ * arrival order, which is precisely why they share a channel rather than
506
+ * getting one each.
507
+ *
508
+ * WIDENING, not repurposing: no existing field changed meaning and no existing
509
+ * type was renamed, so the forward-compat rule at the top of this file holds. A
510
+ * consumer that already switches on `type` and ignores what it does not know --
511
+ * which the contract requires -- is unaffected. What DOES move is the static
512
+ * type a `.d.ts` consumer sees on `subscribeWireDiagnostics`, `drain()` and
513
+ * `attach()`: it is now the wider union, so code that assigned the callback
514
+ * parameter straight into a `WireDiagnosticEvent` needs a narrowing check it
515
+ * should have had anyway.
516
+ */
517
+ export type KaiDiagnosticEvent = WireDiagnosticEvent | ElementDiagnosticEvent;
518
+
519
+ /**
520
+ * Narrow the shared stream to the element half — and, by negation, to the wire
521
+ * half, which is what most callers actually want:
522
+ *
523
+ * if (isElementDiagnosticEvent(e)) { … } else { e.streamId … }
524
+ *
525
+ * WHY THIS DIRECTION and not an `isWireDiagnosticEvent`. The element family is
526
+ * CLOSED and small; the non-element side is the one that keeps growing, and it
527
+ * does not even share one prefix -- `wire.*`, `encode.*` and `app.*` are all in
528
+ * `WireDiagnosticEvent` today, with `kit.warn` queued in the inventory behind
529
+ * them. That is not a prediction: `encode.*` and `app.*` both arrived while this
530
+ * branch was open, so a positive `wire.*` allowlist written a fortnight ago
531
+ * would already be answering "not a wire event" for two live families. Testing
532
+ * the closed set and taking the complement stays correct as the open set grows.
533
+ *
534
+ * Exists because a panel, and every test in this repo that reads `streamId` or
535
+ * `traceId` off "every event", needs exactly this one check now that two layers
536
+ * share one channel. The prefix is pinned by `element-artifact-divergence.test.ts`.
537
+ */
538
+ export function isElementDiagnosticEvent(e: KaiDiagnosticEvent): e is ElementDiagnosticEvent {
539
+ return e.type.startsWith('element.');
540
+ }
541
+
542
+ type Subscriber = (e: KaiDiagnosticEvent) => void;
543
+
544
+ /**
545
+ * THE MUTABLE STATE LIVES ON A GLOBAL, DELIBERATELY.
546
+ *
547
+ * Module-scope state means one emitter PER COPY of this module, and copies are
548
+ * normal rather than exotic. `./wire` and `./diagnostics` are separate rollup
549
+ * bundles that each inline this file, so a subscriber registered through one saw
550
+ * nothing emitted by the other -- which shipped, and made the devtools hook
551
+ * inert for every consumer. Worse, rollup saw a subscriber array that nothing in
552
+ * the diagnostics bundle ever emitted to, concluded it was write-only, and
553
+ * deleted it outright.
554
+ *
555
+ * A shared chunk would fix OUR build and not the class. The second instance is a
556
+ * consumer who bundles the kit and also loads the elements bundle from a CDN:
557
+ * that duplicates the module identically, and nothing we do to our own build
558
+ * config prevents it. A global keyed by `Symbol.for` is the one thing every copy
559
+ * agrees on, because the symbol registry is per-realm rather than per-module.
560
+ *
561
+ * THE COUNTER HAS TO BE IN HERE TOO. Two copies each starting at `wire-1` mint
562
+ * the same id for different streams, and that id NAMESPACES REASONING PARTS --
563
+ * a collision merges one stream's reasoning blocks into another's and overwrites
564
+ * their verbatim provider payload, which is the exact 400 the namespacing exists
565
+ * to avoid.
566
+ *
567
+ * Still no `window`: `globalThis` exists under Node and every SSR runtime, so
568
+ * this stays as import-safe as it was.
569
+ *
570
+ * PER REALM: a Worker, an iframe or an SSR isolate has its own registry and
571
+ * therefore its own emitter -- a panel in the parent document does not see a
572
+ * stream read inside a Worker.
573
+ */
574
+ const STATE_KEY = Symbol.for('kai.wire.diagnostics.v1');
575
+
576
+ interface DiagnosticsState {
577
+ subs: Subscriber[];
578
+ seq: number;
579
+ /** Whether content-bearing values may ride under the `payload` key. A
580
+ * SEPARATE switch from having a subscriber, and it lives here for the same
581
+ * reason the subscriber list does: two copies of this module must agree. */
582
+ payload: boolean;
583
+ }
584
+
585
+ /** `globalThis` viewed as the one property we put on it. */
586
+ type StateHolder = { [key: symbol]: DiagnosticsState | undefined };
587
+
588
+ /** Read WITHOUT allocating, so the emit path and the active check never write to
589
+ * the global.
590
+ *
591
+ * The accurate guarantee: IMPORTING this module allocates nothing; the
592
+ * singleton is created on the first `subscribeWireDiagnostics()` or the first
593
+ * stream read (`nextStreamId`). A read with no subscriber does create it -- it
594
+ * has to, because the id counter lives there -- leaving `{ subs: [], seq: 1 }`. */
595
+ function peekState(): DiagnosticsState | undefined {
596
+ return (globalThis as unknown as StateHolder)[STATE_KEY];
597
+ }
598
+
599
+ /** Read, creating on first use. Only the two paths that must WRITE call this. */
600
+ function state(): DiagnosticsState {
601
+ const holder = globalThis as unknown as StateHolder;
602
+ return (holder[STATE_KEY] ??= { subs: [], seq: 0, payload: false });
603
+ }
604
+
605
+ /**
606
+ * Subscribe to wire diagnostics. Returns an unsubscribe function.
607
+ *
608
+ * SSR-safe: `globalThis` is available in every runtime the kit imports into, and
609
+ * nothing here touches `window`.
610
+ */
611
+ export function subscribeWireDiagnostics(fn: Subscriber): () => void {
612
+ const subs = state().subs;
613
+ subs.push(fn);
614
+ let live = true;
615
+ return () => {
616
+ if (!live) return; // a double-unsubscribe must not evict someone else
617
+ live = false;
618
+ const at = subs.indexOf(fn);
619
+ if (at !== -1) subs.splice(at, 1);
620
+ };
621
+ }
622
+
623
+ /**
624
+ * INTERNAL. Every emission site gates on this BEFORE constructing an event
625
+ * object, which is what makes the no-subscriber path cost one symbol read and
626
+ * allocate nothing -- not even the shared state.
627
+ */
628
+ export function wireDiagnosticsActive(): boolean {
629
+ const s = peekState();
630
+ return s !== undefined && s.subs.length > 0;
631
+ }
632
+
633
+ /**
634
+ * INTERNAL. Whether content-bearing values may ride under the `payload` key.
635
+ *
636
+ * A SEPARATE SWITCH FROM `wireDiagnosticsActive`, and the separation is the
637
+ * security property rather than a preference. The panel is a pasted script tag
638
+ * on a live site and `?kai-devtools=1` is guessable; a stranger who guesses it
639
+ * gets the SHAPE of a conversation and not one word of its content. Turning
640
+ * that into content takes a second, deliberate signal that no URL can set.
641
+ *
642
+ * Every emission site checks this INSIDE its `wireDiagnosticsActive()` branch
643
+ * and builds the payload object only then, so a session with a subscriber and
644
+ * no payload switch pays one extra boolean read per event.
645
+ */
646
+ export function wirePayloadActive(): boolean {
647
+ const s = peekState();
648
+ return s !== undefined && s.payload;
649
+ }
650
+
651
+ /**
652
+ * INTERNAL. Turn payload capture on or off.
653
+ *
654
+ * Called by the devtools hook at install, from the payload signal it read
655
+ * synchronously. It is not part of the public surface: a consumer who can flip
656
+ * this can make the kit start recording its own users' conversations, and that
657
+ * is a decision the app makes through the signal, deliberately, not something a
658
+ * dependency can reach in and do.
659
+ *
660
+ * Turning it ON allocates the shared state; turning it OFF does not, so the
661
+ * dormant no-signal path still allocates nothing at all.
662
+ */
663
+ export function setWirePayloadCapture(on: boolean): void {
664
+ if (!on && peekState() === undefined) return;
665
+ state().payload = on;
666
+ }
667
+
668
+ /**
669
+ * INTERNAL. The `payload` key, or nothing.
670
+ *
671
+ * Every content-bearing value in this module goes through here, which is what
672
+ * makes the boundary reviewable: there is one call to grep for, one key to
673
+ * strip in a test, and no field-by-field audit. The builder is a THUNK so that
674
+ * with the switch off nothing is read, copied or stringified -- the cost of
675
+ * carrying an unused payload is a boolean.
676
+ */
677
+ export function withPayload<T>(build: () => T): { payload: T } | Record<string, never> {
678
+ return wirePayloadActive() ? { payload: build() } : {};
679
+ }
680
+
681
+ /**
682
+ * INTERNAL. Deliver to every subscriber.
683
+ *
684
+ * Iterates a SNAPSHOT so a subscriber that unsubscribes (or subscribes) during
685
+ * delivery cannot make the loop skip its neighbour. Each call is wrapped
686
+ * individually: a panel that throws must not take down the stream it is
687
+ * watching, nor starve the other subscribers. The throw is swallowed rather
688
+ * than re-reported because there is nowhere to report it TO that is not itself
689
+ * a subscriber.
690
+ */
691
+ export function emitWireDiagnostic(e: KaiDiagnosticEvent): void {
692
+ const s = peekState();
693
+ if (!s) return; // nobody has ever subscribed in this realm
694
+ for (const fn of [...s.subs]) {
695
+ try {
696
+ fn(e);
697
+ } catch {
698
+ // a broken observer is the observer's problem, not the stream's
699
+ }
700
+ }
701
+ }
702
+
703
+ /**
704
+ * INTERNAL. One id per provider response stream.
705
+ *
706
+ * It correlates diagnostics AND namespaces reasoning block indices. Anthropic
707
+ * restarts content-block indices at 0 on every message, and a tool loop reads
708
+ * several messages into ONE assistant turn, so without this round 2's block 0
709
+ * merges into round 1's part and overwrites its verbatim `raw`. See
710
+ * `appendReasoningPart`.
711
+ *
712
+ * A monotonic counter, not a UUID: this never leaves the process, is compared
713
+ * only for equality against parts built in the same process, and a counter keeps
714
+ * the value short and reproducible in test output.
715
+ *
716
+ * It lives HERE rather than in `consume.ts` because `readModelStream` opens the
717
+ * format and counts frames before `consumeModelStream` runs, and every event
718
+ * from one read has to carry the same id.
719
+ *
720
+ * The counter sits in the SHARED state, so two copies of this module continue
721
+ * one sequence instead of both restarting at `wire-1` and minting the same id
722
+ * for different streams. See the note on `STATE_KEY`.
723
+ */
724
+ export function nextStreamId(): string {
725
+ const s = state();
726
+ return `wire-${++s.seq}`;
727
+ }