@kitn.ai/ui 0.25.1 → 0.26.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 (409) hide show
  1. package/README.md +5 -7
  2. package/dist/components/attachment-types.d.ts +46 -1
  3. package/dist/components/attachments.d.ts +31 -0
  4. package/dist/components/audio-visualizer/fit-scale.d.ts +50 -0
  5. package/dist/components/audio-visualizer/index.d.ts +15 -1
  6. package/dist/components/audio-visualizer/variant-bar.d.ts +24 -1
  7. package/dist/components/chat-thread.d.ts +3 -41
  8. package/dist/components/code-block.d.ts +22 -0
  9. package/dist/components/conversation-item.d.ts +54 -1
  10. package/dist/components/conversation-list.d.ts +65 -0
  11. package/dist/components/form.d.ts +42 -0
  12. package/dist/components/message.d.ts +7 -0
  13. package/dist/components/toast.d.ts +2 -1
  14. package/dist/components/voice-input.d.ts +8 -0
  15. package/dist/components/voice-output.d.ts +12 -1
  16. package/dist/components/workspace-shell.d.ts +87 -0
  17. package/dist/{create-tween-B5Y4Im8G.js → create-tween-BFT6c-Y8.js} +1 -1
  18. package/dist/{create-tween-4z1XYLkR.js → create-tween-CJNlzoaO.js} +1 -1
  19. package/dist/{create-tween-CUzOlaMS.js → create-tween-CQED4T0g.js} +1 -1
  20. package/dist/custom-elements.json +2931 -2572
  21. package/dist/diagnostics/hook.d.ts +81 -0
  22. package/dist/diagnostics/index.d.ts +7 -0
  23. package/dist/diagnostics/report-request.d.ts +61 -0
  24. package/dist/diagnostics.d.ts +5 -0
  25. package/dist/diagnostics.js +209 -0
  26. package/dist/elements/agent-card.js +1 -1
  27. package/dist/elements/artifact.js +1 -1
  28. package/dist/elements/attachments.js +1 -1
  29. package/dist/elements/audio-visualizer.js +1 -1
  30. package/dist/elements/autoloader.js +1 -1
  31. package/dist/elements/avatar.js +1 -1
  32. package/dist/elements/badge.js +1 -1
  33. package/dist/elements/button.js +1 -1
  34. package/dist/elements/card.js +1 -1
  35. package/dist/elements/cards.js +1 -1
  36. package/dist/elements/chain-of-thought.js +1 -1
  37. package/dist/elements/chat-scope-picker.js +1 -1
  38. package/dist/elements/chat-workspace.js +1 -1
  39. package/dist/elements/chat.js +1 -1
  40. package/dist/elements/checkpoint.js +1 -1
  41. package/dist/elements/choice.js +1 -1
  42. package/dist/elements/chunks/{Icon-b5A8hlHs.js → Icon--Y8vsFal.js} +1 -1
  43. package/dist/elements/chunks/action-icons-D9u3OzYW.js +1 -0
  44. package/dist/elements/chunks/arrow-left-CVh1TeN7.js +1 -0
  45. package/dist/elements/chunks/{artifact-BSuPYFHY.js → artifact-BkYcfIlZ.js} +1 -1
  46. package/dist/elements/chunks/attachments-DwfNlubq.js +1 -0
  47. package/dist/elements/chunks/audio-visualizer-D7lB6mvR.js +1 -0
  48. package/dist/elements/chunks/{badge-TIMiJFKc.js → badge-Q0-Cmwzz.js} +1 -1
  49. package/dist/elements/chunks/{button-B9okqG73.js → button-flUbeufF.js} +1 -1
  50. package/dist/elements/chunks/{card-renderer-CSoTL_e1.js → card-renderer-3Dyl-FpN.js} +1 -1
  51. package/dist/elements/chunks/check-B5kBPKeo.js +1 -0
  52. package/dist/elements/chunks/chevron-down-DZ1GS39h.js +1 -0
  53. package/dist/elements/chunks/chevron-right-CvP7OUd2.js +1 -0
  54. package/dist/elements/chunks/{choice-card-CGMc3kek.js → choice-card-D4QUIOWZ.js} +1 -1
  55. package/dist/elements/chunks/circle-CDf9g6MQ.js +1 -0
  56. package/dist/elements/chunks/circle-check-CA47Nqbz.js +1 -0
  57. package/dist/elements/chunks/{circle-x-mhH7pOK2.js → circle-x-CJ_HVbC8.js} +1 -1
  58. package/dist/elements/chunks/code-block-BdLiTitd.js +1 -0
  59. package/dist/elements/chunks/collapsible-BzbXpPzr.js +1 -0
  60. package/dist/elements/chunks/{composer-CZZmtYOT.js → composer-Byve-n8K.js} +2 -2
  61. package/dist/elements/chunks/{confirm-card-BWjf2-xo.js → confirm-card-B0flE3MN.js} +1 -1
  62. package/dist/elements/chunks/context-VLIL3sJ-.js +1 -0
  63. package/dist/elements/chunks/controllable-DTwTIlwp.js +1 -0
  64. package/dist/elements/chunks/conversation-item-BDztezOG.js +1 -0
  65. package/dist/elements/chunks/copy-Nf-kqxXH.js +1 -0
  66. package/dist/elements/chunks/{create-tween-CUrM6D_o.js → create-tween-c4Z8TDYz.js} +2 -2
  67. package/dist/elements/chunks/default-input-C2iLFq0b.js +1 -0
  68. package/dist/elements/chunks/define-wjHR1MkU.js +1 -0
  69. package/dist/elements/chunks/disclosure-CcNbyLy0.js +1 -0
  70. package/dist/elements/chunks/{download-DnCyWFJE.js → download-C4V7tBdb.js} +1 -1
  71. package/dist/elements/chunks/dropdown-BgWTthUx.js +1 -0
  72. package/dist/elements/chunks/{ellipsis-CVxqfsDZ.js → ellipsis-7gU33mL2.js} +1 -1
  73. package/dist/elements/chunks/embed-D3KewsIJ.js +1 -0
  74. package/dist/elements/chunks/{external-link-DG2sRdQL.js → external-link-CSOEsNlx.js} +1 -1
  75. package/dist/elements/chunks/{file-text-D0J9F6M7.js → file-text-D1G8khle.js} +1 -1
  76. package/dist/elements/chunks/{file-tree-yGWNK0Js.js → file-tree-zvB2i7P9.js} +1 -1
  77. package/dist/elements/chunks/{folder-NV1pi_ud.js → folder-WW2LGLid.js} +1 -1
  78. package/dist/elements/chunks/form-CQirdC4W.js +1 -0
  79. package/dist/elements/chunks/hover-card-CJJkfHTK.js +1 -0
  80. package/dist/elements/chunks/icon-Cfgmf4fa.js +1 -0
  81. package/dist/elements/chunks/{info-C8r5Q6cc.js → info-DX3YMOt6.js} +1 -1
  82. package/dist/elements/chunks/input-BtW9U6Sq.js +1 -0
  83. package/dist/elements/chunks/{kbd-OGJDUT-O.js → kbd-LGXwuT_V.js} +1 -1
  84. package/dist/elements/chunks/{link-BdsfLB7b.js → link-2My69BqT.js} +1 -1
  85. package/dist/elements/chunks/{link-preview-Cf5KOP-i.js → link-preview-OQ9IEq0f.js} +1 -1
  86. package/dist/elements/chunks/{loader-B5QzK_8_.js → loader-iOxc_Tu-.js} +1 -1
  87. package/dist/elements/chunks/{markdown-DbozyYqa.js → markdown-DgBFaGAz.js} +1 -1
  88. package/dist/elements/chunks/{message-DtXKPqiR.js → message-VMZohrXx.js} +1 -1
  89. package/dist/elements/chunks/message-circle-CpXMM-_n.js +1 -0
  90. package/dist/elements/chunks/message-square-DxcWtETQ.js +1 -0
  91. package/dist/elements/chunks/message-tZmV2A3p.js +1 -0
  92. package/dist/elements/chunks/{minimize-2-SegJ_oku.js → minimize-2-DPIUKTrT.js} +1 -1
  93. package/dist/elements/chunks/{model-switcher-D7JrFFHp.js → model-switcher-DsT8tFB0.js} +1 -1
  94. package/dist/elements/chunks/{overlay-CV7z2YuX.js → overlay-DuiwdXjm.js} +1 -1
  95. package/dist/elements/chunks/{paperclip-Bg0ITU3b.js → paperclip-CRZVp4z_.js} +1 -1
  96. package/dist/elements/chunks/{progress-bar-D1_rzt6N.js → progress-bar-DNy66e_E.js} +1 -1
  97. package/dist/elements/chunks/{prompt-suggestion-D5Sm8xF1.js → prompt-suggestion-Bn64Nl7L.js} +1 -1
  98. package/dist/elements/chunks/reasoning-Emn9N4-T.js +1 -0
  99. package/dist/elements/chunks/{resizable-Dpzjlvp4.js → resizable-D181b4fM.js} +1 -1
  100. package/dist/elements/chunks/{rotate-cw-DzVDJOA1.js → rotate-cw-CPWe37D9.js} +1 -1
  101. package/dist/elements/chunks/{scroll-area-93A1bbeG.js → scroll-area-ClZkZxIk.js} +1 -1
  102. package/dist/elements/chunks/scroll-button-DPE-t3x_.js +1 -0
  103. package/dist/elements/chunks/{separator-DH2r_ZSZ.js → separator-CeJHQqLW.js} +1 -1
  104. package/dist/elements/chunks/{settings-BFMOd6yB.js → settings-CqaHgJF9.js} +1 -1
  105. package/dist/elements/chunks/{settings-group-B3VZKIcz.js → settings-group-CjqjLCOn.js} +1 -1
  106. package/dist/elements/chunks/share-CyfnNEqp.js +1 -0
  107. package/dist/elements/chunks/{skeleton-N8BuWYmf.js → skeleton-D8akBnKS.js} +1 -1
  108. package/dist/elements/chunks/slots-DaNjX0rc.js +1 -0
  109. package/dist/elements/chunks/{source-w9V0NnBn.js → source-UWCvmVak.js} +1 -1
  110. package/dist/elements/chunks/{star-DHvACxtm.js → star-DoHFAikQ.js} +1 -1
  111. package/dist/elements/chunks/store-Cjq0qwYN.js +1 -0
  112. package/dist/elements/chunks/{tasks-card-lPxoodlA.js → tasks-card-CmZ5xh6v.js} +1 -1
  113. package/dist/elements/chunks/{text-shimmer-BY_rI3s5.js → text-shimmer-DYn09zrz.js} +1 -1
  114. package/dist/elements/chunks/textarea-skiGHURM.js +1 -0
  115. package/dist/elements/chunks/{thumbs-up-DQe0hvkZ.js → thumbs-up-DX2Ijk3H.js} +1 -1
  116. package/dist/elements/chunks/{toast-store-VUZZBhzH.js → toast-store-CFnQpThX.js} +1 -1
  117. package/dist/elements/chunks/{tool-C1YtxwPu.js → tool-DKQ2D1DP.js} +1 -1
  118. package/dist/elements/chunks/{tooltip-BqwnFWhn.js → tooltip-B0G99y8w.js} +1 -1
  119. package/dist/elements/chunks/{triangle-alert-B6or7F5u.js → triangle-alert-F9cyUEyy.js} +1 -1
  120. package/dist/elements/chunks/{use-card-resolution-DJg32jGH.js → use-card-resolution-DoA2f_EU.js} +1 -1
  121. package/dist/elements/chunks/{variant-aurora-QXTZPyST.js → variant-aurora-qt6Zct8M.js} +2 -2
  122. package/dist/elements/chunks/variant-custom-tJp22_BQ.js +1 -0
  123. package/dist/{variant-wave-BwzrHD1s.js → elements/chunks/variant-wave-CaQ5ti39.js} +2 -2
  124. package/dist/elements/chunks/x-B6UnOiLw.js +1 -0
  125. package/dist/elements/coachmark.js +1 -1
  126. package/dist/elements/code-block.js +1 -1
  127. package/dist/elements/command.js +1 -1
  128. package/dist/elements/compare.js +1 -1
  129. package/dist/elements/composer.js +1 -1
  130. package/dist/elements/confirm-card.js +1 -1
  131. package/dist/elements/context-meter.js +1 -1
  132. package/dist/elements/conversation-item.d.ts +1 -0
  133. package/dist/elements/conversation-item.js +1 -0
  134. package/dist/elements/conversation-list.js +1 -1
  135. package/dist/elements/default-input.d.ts +3 -3
  136. package/dist/elements/define.d.ts +27 -0
  137. package/dist/elements/diagnostic-events.d.ts +74 -0
  138. package/dist/elements/dialog.js +1 -1
  139. package/dist/elements/dock.d.ts +1 -0
  140. package/dist/elements/dock.js +149 -0
  141. package/dist/elements/dropdown.d.ts +1 -0
  142. package/dist/elements/dropdown.js +1 -0
  143. package/dist/elements/editable-label.js +1 -1
  144. package/dist/elements/element-diagnostics.d.ts +81 -0
  145. package/dist/elements/embed.js +1 -1
  146. package/dist/elements/empty.js +1 -1
  147. package/dist/elements/feedback-bar.js +1 -1
  148. package/dist/elements/file-tree.js +1 -1
  149. package/dist/elements/file-upload.js +1 -1
  150. package/dist/elements/form.js +1 -1
  151. package/dist/elements/hover-card.js +1 -1
  152. package/dist/elements/icon.js +1 -1
  153. package/dist/elements/image.js +1 -1
  154. package/dist/elements/input.js +1 -1
  155. package/dist/elements/kbd.js +1 -1
  156. package/dist/elements/link-preview.js +1 -1
  157. package/dist/elements/loader.js +1 -1
  158. package/dist/elements/markdown.js +1 -1
  159. package/dist/elements/menu.js +1 -1
  160. package/dist/elements/message-skills.js +1 -1
  161. package/dist/elements/message.js +1 -1
  162. package/dist/elements/model-switcher.js +1 -1
  163. package/dist/elements/nav.js +1 -1
  164. package/dist/elements/notice.js +1 -1
  165. package/dist/elements/pane-group.js +1 -1
  166. package/dist/elements/pane.js +1 -1
  167. package/dist/elements/popover.js +1 -1
  168. package/dist/elements/progress-bar.js +1 -1
  169. package/dist/elements/prompt-dock.js +1 -1
  170. package/dist/elements/prompt-input.js +1 -1
  171. package/dist/elements/prompt-suggestions.js +1 -1
  172. package/dist/elements/reasoning.js +1 -1
  173. package/dist/elements/register.d.ts +2 -0
  174. package/dist/elements/remote.js +1 -1
  175. package/dist/elements/resizable.js +1 -1
  176. package/dist/elements/response-stream.js +3 -3
  177. package/dist/elements/screen.js +1 -1
  178. package/dist/elements/scroll-area.js +1 -1
  179. package/dist/elements/scroll-button.js +1 -1
  180. package/dist/elements/search.js +1 -1
  181. package/dist/elements/segmented.js +1 -1
  182. package/dist/elements/separator.js +1 -1
  183. package/dist/elements/setting-item.js +1 -1
  184. package/dist/elements/settings-group.js +1 -1
  185. package/dist/elements/skeleton.js +1 -1
  186. package/dist/elements/slots.d.ts +35 -3
  187. package/dist/elements/source.js +1 -1
  188. package/dist/elements/status.js +1 -1
  189. package/dist/elements/switch.js +1 -1
  190. package/dist/elements/tabs.js +1 -1
  191. package/dist/elements/tasks.js +1 -1
  192. package/dist/elements/text-shimmer.js +1 -1
  193. package/dist/elements/thinking-bar.js +1 -1
  194. package/dist/elements/thread.js +1 -1
  195. package/dist/elements/toast.js +1 -1
  196. package/dist/elements/tool.js +1 -1
  197. package/dist/elements/tooltip.js +1 -1
  198. package/dist/elements/voice-input.js +1 -1
  199. package/dist/elements/voice-output.js +1 -1
  200. package/dist/elements.d.ts +232 -148
  201. package/dist/index.d.ts +2 -0
  202. package/dist/index.js +5456 -4931
  203. package/dist/index.server.js +4142 -3669
  204. package/dist/kai-provider.es.js +40 -40
  205. package/dist/kai.es.js +1 -1
  206. package/dist/llms/llms-full.txt +184 -69
  207. package/dist/llms/llms.txt +3 -3
  208. package/dist/mcp.es.js +2669 -546
  209. package/dist/primitives/use-resize-observer.d.ts +23 -0
  210. package/dist/primitives/use-speech-recognition.d.ts +8 -0
  211. package/dist/primitives/use-text-stream.d.ts +5 -0
  212. package/dist/react/index.d.ts +115 -266
  213. package/dist/react.js +247 -221
  214. package/dist/register-impl-DpE7icIb.js +293 -0
  215. package/dist/schemas/index.d.ts +1 -1
  216. package/dist/schemas/tool-defs.d.ts +35 -0
  217. package/dist/schemas.js +403 -310
  218. package/dist/{solid-BMuDmFo_.js → solid-BtdqNZI7.js} +8435 -7394
  219. package/dist/{solid-CGYNdLRR.js → solid-CnavwBye.js} +5987 -5035
  220. package/dist/solid.d.ts +5 -0
  221. package/dist/solid.js +210 -203
  222. package/dist/solid.server.js +210 -203
  223. package/dist/state/index.d.ts +4 -0
  224. package/dist/state/persistence.d.ts +51 -0
  225. package/dist/state/stream.d.ts +50 -0
  226. package/dist/state/threads.d.ts +71 -0
  227. package/dist/state.js +269 -169
  228. package/dist/types.d.ts +4 -2
  229. package/dist/ui/dock.d.ts +109 -0
  230. package/dist/ui/hover-card.d.ts +44 -0
  231. package/dist/{variant-aurora-DwSOfraQ.js → variant-aurora-B8II3-I1.js} +27 -27
  232. package/dist/{variant-aurora-wKvAQ5wp.js → variant-aurora-CMNWOSJR.js} +28 -28
  233. package/dist/{variant-aurora-XEkPT0_z.js → variant-aurora-CcuDMWwQ.js} +2 -2
  234. package/dist/{variant-custom-eFIg2XWQ.js → variant-custom-BdvYC7af.js} +40 -40
  235. package/dist/{variant-custom-BhS9wIpU.js → variant-custom-C714mqKl.js} +65 -65
  236. package/dist/variant-custom-L16JLLZy.js +1 -0
  237. package/dist/{elements/chunks/variant-wave-DDCwhLas.js → variant-wave-2S4La2Ka.js} +2 -2
  238. package/dist/{variant-wave-CbVsmgwT.js → variant-wave-D7ruiA1W.js} +25 -25
  239. package/dist/{variant-wave-CUWGx9Bn.js → variant-wave-ecwykeyL.js} +21 -21
  240. package/dist/wire/chunk.d.ts +47 -0
  241. package/dist/wire/diagnostics.d.ts +575 -0
  242. package/dist/wire/encode-probe.d.ts +70 -0
  243. package/dist/wire/encode.d.ts +24 -0
  244. package/dist/wire/index.d.ts +2 -0
  245. package/dist/wire/sse.d.ts +10 -2
  246. package/dist/wire.js +917 -480
  247. package/frameworks/react/index.tsx +132 -93
  248. package/llms-full.txt +184 -69
  249. package/llms.txt +3 -3
  250. package/package.json +27 -7
  251. package/src/agent-tooling/archetypes.ts +41 -9
  252. package/src/agent-tooling/catalog/README.md +676 -0
  253. package/src/agent-tooling/catalog/catalog-types.ts +231 -0
  254. package/src/agent-tooling/catalog/fabrications.ts +96 -0
  255. package/src/agent-tooling/catalog/invariants.ts +284 -0
  256. package/src/agent-tooling/catalog/labs-titles.ts +114 -0
  257. package/src/agent-tooling/catalog/scenarios.ts +87 -0
  258. package/src/agent-tooling/catalog/surfaces.ts +275 -0
  259. package/src/agent-tooling/integrations/anthropic.ts +7 -1
  260. package/src/agent-tooling/integrations/cloudflare.ts +43 -2
  261. package/src/agent-tooling/integrations/langgraph.ts +7 -1
  262. package/src/agent-tooling/integrations/mastra.ts +7 -1
  263. package/src/agent-tooling/integrations/mock.ts +92 -17
  264. package/src/agent-tooling/integrations/ollama.ts +7 -1
  265. package/src/agent-tooling/integrations/openai.ts +7 -1
  266. package/src/agent-tooling/integrations/openrouter.ts +7 -1
  267. package/src/agent-tooling/integrations/pi.ts +56 -1
  268. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +7 -1
  269. package/src/agent-tooling/mcp/css-raw.d.ts +12 -0
  270. package/src/agent-tooling/mcp/manifest.ts +100 -1
  271. package/src/agent-tooling/mcp/server.ts +61 -5
  272. package/src/agent-tooling/mcp/tools/reference.ts +440 -4
  273. package/src/agent-tooling/mcp/tools/scaffold.ts +1834 -307
  274. package/src/agent-tooling/mcp/tools/theme.ts +146 -30
  275. package/src/agent-tooling/mcp/validate-args.ts +141 -0
  276. package/src/agent-tooling/registry.ts +19 -7
  277. package/src/agent-tooling/route-emit.ts +26 -2
  278. package/src/components/attachment-types.ts +47 -1
  279. package/src/components/attachments.tsx +169 -22
  280. package/src/components/audio-visualizer/fit-scale.ts +112 -0
  281. package/src/components/audio-visualizer/index.tsx +80 -3
  282. package/src/components/audio-visualizer/variant-aurora.tsx +10 -4
  283. package/src/components/audio-visualizer/variant-bar.tsx +38 -6
  284. package/src/components/audio-visualizer/variant-custom.tsx +8 -2
  285. package/src/components/audio-visualizer/variant-grid.tsx +16 -11
  286. package/src/components/audio-visualizer/variant-radial.tsx +15 -10
  287. package/src/components/audio-visualizer/variant-wave.tsx +7 -2
  288. package/src/components/chat-container.tsx +4 -1
  289. package/src/components/chat-thread.tsx +36 -7
  290. package/src/components/code-block.tsx +79 -3
  291. package/src/components/composer.tsx +8 -1
  292. package/src/components/conversation-item.tsx +114 -4
  293. package/src/components/conversation-list.tsx +212 -12
  294. package/src/components/embed.tsx +4 -0
  295. package/src/components/form.tsx +41 -14
  296. package/src/components/message.tsx +75 -5
  297. package/src/components/response-stream.tsx +13 -7
  298. package/src/components/screen.tsx +5 -0
  299. package/src/components/scroll-button.tsx +7 -0
  300. package/src/components/toast.tsx +11 -5
  301. package/src/components/voice-input.tsx +35 -3
  302. package/src/components/voice-output.tsx +46 -6
  303. package/src/components/workspace-shell.tsx +285 -0
  304. package/src/diagnostics/hook.ts +352 -0
  305. package/src/diagnostics/index.ts +82 -0
  306. package/src/diagnostics/report-request.ts +296 -0
  307. package/src/elements/attachments.tsx +6 -7
  308. package/src/elements/audio-visualizer.tsx +48 -4
  309. package/src/elements/chat-workspace.tsx +159 -287
  310. package/src/elements/chat.tsx +12 -9
  311. package/src/elements/code-block.tsx +19 -1
  312. package/src/elements/command.tsx +4 -1
  313. package/src/elements/compiled.css +1 -1
  314. package/src/elements/conversation-item.tsx +80 -0
  315. package/src/elements/conversation-list.tsx +60 -11
  316. package/src/elements/default-input.tsx +5 -5
  317. package/src/elements/define.tsx +168 -8
  318. package/src/elements/diagnostic-events.ts +114 -0
  319. package/src/elements/dialog.tsx +47 -0
  320. package/src/elements/disclosure.ts +34 -12
  321. package/src/elements/dock.tsx +204 -0
  322. package/src/elements/dropdown.tsx +123 -0
  323. package/src/elements/element-diagnostics.ts +392 -0
  324. package/src/elements/element-manifest.json +12 -0
  325. package/src/elements/element-meta.json +672 -439
  326. package/src/elements/element-nonscalar.json +170 -0
  327. package/src/elements/element-types.d.ts +232 -148
  328. package/src/elements/menu.tsx +13 -0
  329. package/src/elements/prompt-dock.tsx +2 -1
  330. package/src/elements/prompt-input.tsx +8 -8
  331. package/src/elements/register-impl.ts +46 -0
  332. package/src/elements/register.ts +28 -0
  333. package/src/elements/resizable.tsx +52 -25
  334. package/src/elements/scroll-button.tsx +5 -0
  335. package/src/elements/slots.ts +107 -12
  336. package/src/elements/styles.css +30 -6
  337. package/src/elements/thinking-bar.tsx +16 -7
  338. package/src/elements/toast.tsx +3 -0
  339. package/src/elements/tool.tsx +26 -5
  340. package/src/elements/voice-input.tsx +11 -2
  341. package/src/elements/voice-output.tsx +16 -3
  342. package/src/index.ts +2 -0
  343. package/src/primitives/use-resize-observer.ts +33 -0
  344. package/src/primitives/use-speech-recognition.ts +12 -1
  345. package/src/primitives/use-stick-to-bottom.ts +34 -2
  346. package/src/primitives/use-text-stream.ts +33 -2
  347. package/src/remote/provider-runtime.ts +6 -3
  348. package/src/schemas/index.ts +1 -0
  349. package/src/schemas/tool-defs.ts +297 -6
  350. package/src/solid.ts +5 -0
  351. package/src/state/index.ts +12 -0
  352. package/src/state/persistence.ts +180 -0
  353. package/src/state/stream.ts +71 -4
  354. package/src/state/threads.ts +142 -0
  355. package/src/types.ts +4 -2
  356. package/src/ui/dialog.tsx +6 -0
  357. package/src/ui/dock.tsx +445 -0
  358. package/src/ui/dropdown.tsx +67 -23
  359. package/src/ui/hover-card.tsx +156 -6
  360. package/src/ui/input.tsx +29 -10
  361. package/src/ui/resizable.tsx +11 -0
  362. package/src/wire/chunk.ts +47 -0
  363. package/src/wire/consume.ts +185 -26
  364. package/src/wire/diagnostics.ts +727 -0
  365. package/src/wire/encode-probe.ts +214 -0
  366. package/src/wire/encode.ts +267 -14
  367. package/src/wire/formats/anthropic.ts +8 -1
  368. package/src/wire/formats/openai.ts +5 -0
  369. package/src/wire/index.ts +22 -0
  370. package/src/wire/read.ts +250 -6
  371. package/src/wire/sse.ts +16 -3
  372. package/dist/elements/chunks/action-icons-DW9muWrY.js +0 -1
  373. package/dist/elements/chunks/arrow-left-iJVnQmxA.js +0 -1
  374. package/dist/elements/chunks/attachments-CL6TaqIb.js +0 -1
  375. package/dist/elements/chunks/audio-visualizer-BuUqQmaO.js +0 -1
  376. package/dist/elements/chunks/chat-thread-DxoKaStk.js +0 -1
  377. package/dist/elements/chunks/check-MdLzbxrm.js +0 -1
  378. package/dist/elements/chunks/chevron-down-MK7IzDqe.js +0 -1
  379. package/dist/elements/chunks/chevron-right-C5jXhmtV.js +0 -1
  380. package/dist/elements/chunks/circle-BG7VkPep.js +0 -1
  381. package/dist/elements/chunks/circle-check-GqvO2UBs.js +0 -1
  382. package/dist/elements/chunks/code-block-ClhQO9RW.js +0 -1
  383. package/dist/elements/chunks/collapsible-DBcBkFPh.js +0 -1
  384. package/dist/elements/chunks/context-hKjNqiyQ.js +0 -1
  385. package/dist/elements/chunks/conversation-list-CsGJjSKG.js +0 -1
  386. package/dist/elements/chunks/default-input-B1Mem5wu.js +0 -1
  387. package/dist/elements/chunks/define-B_tXMYRm.js +0 -1
  388. package/dist/elements/chunks/disclosure-DAoYOKew.js +0 -1
  389. package/dist/elements/chunks/dropdown-CmNna5lT.js +0 -1
  390. package/dist/elements/chunks/embed-CjAD2jSi.js +0 -1
  391. package/dist/elements/chunks/form-Cp8Y00Wn.js +0 -1
  392. package/dist/elements/chunks/hover-card-6KtXpLwT.js +0 -1
  393. package/dist/elements/chunks/icon-97R8SR2p.js +0 -1
  394. package/dist/elements/chunks/input-DrseEjhp.js +0 -1
  395. package/dist/elements/chunks/message-CE4k-qaj.js +0 -1
  396. package/dist/elements/chunks/message-square-C9ovmtOo.js +0 -1
  397. package/dist/elements/chunks/reasoning-BrxQfpfI.js +0 -1
  398. package/dist/elements/chunks/scroll-button-CrTo7wm4.js +0 -1
  399. package/dist/elements/chunks/share-DgNbkJKY.js +0 -1
  400. package/dist/elements/chunks/slots-CIw9RlAe.js +0 -1
  401. package/dist/elements/chunks/store-HIPGhNz9.js +0 -1
  402. package/dist/elements/chunks/textarea-DPfuhswM.js +0 -1
  403. package/dist/elements/chunks/variant-custom-D5ZlmziF.js +0 -1
  404. package/dist/elements/chunks/video-Bb86fVYI.js +0 -1
  405. package/dist/elements/chunks/x-BOhjBeOd.js +0 -1
  406. package/dist/primitives/card-validate-generator.testlib.d.ts +0 -20
  407. package/dist/register-impl-DGmGzrEM.js +0 -145
  408. package/dist/variant-custom-MkTNTycd.js +0 -1
  409. package/src/primitives/card-validate-generator.testlib.ts +0 -38
@@ -0,0 +1,114 @@
1
+ import ts from 'typescript';
2
+
3
+ /**
4
+ * The `Labs/…` title a story file REGISTERS: the `title` property of its
5
+ * default-exported meta object, parsed rather than matched.
6
+ *
7
+ * ★ WHY IT IS SPELLED THAT PRECISELY. Three text-based versions of this question
8
+ * shipped and each was defeated, and the fourth — a parse that was still too
9
+ * broad — was defeated too:
10
+ *
11
+ * 1. A raw regex over file text counted PROSE as a registration. Renaming every
12
+ * real `Labs/Proofs` meta title across all six proof-*.stories.tsx left the
13
+ * `Proofs` inventory row resolving, kept alive by one comment.
14
+ * 2. A hand-rolled comment stripper fixed that and introduced a worse bug. JSX
15
+ * text like `Don't have an account?` put the scanner into single-quote state,
16
+ * which it never left, so every `//` after it survived stripping. Live in
17
+ * three story files; appending ONE comment line to one of them made an
18
+ * invented `GHOSTPROBE` row resolve at exit 0.
19
+ * 3. The equivalence guard between the two copies of that stripper could not
20
+ * see it: both carried the identical bug, so they agreed perfectly.
21
+ * 4. Parsing every `title:` in the file — the first version of THIS function —
22
+ * was still wrong. A `title` nested in a story's `args` is not a
23
+ * registration: `export const S = { args: { title: 'Labs/Apps' } };` in a
24
+ * non-app story conjured a phantom app that satisfied this lint and
25
+ * surfaces.test.ts together, with no comment involved at all.
26
+ *
27
+ * So the rule is the meta's own `title`, and nothing else. Comments are TRIVIA
28
+ * and never nodes; apostrophes, escapes and template literals are the real
29
+ * lexer's problem; and a title that is not the meta's is not a registration.
30
+ * Single-quoted, double-quoted and untemplated-backtick spellings are all read.
31
+ *
32
+ * REGISTERED COPY: `scripts/lint-catalog-drift.mjs` carries an equivalent
33
+ * `readLabsTitles`, because a Node `.mjs` cannot import a `.ts` at runtime.
34
+ * `tests/scripts/catalog-drift-guard-wiring.test.ts` imports both and asserts
35
+ * they agree over every story file in the tree plus adversarial inputs. Note
36
+ * what that guard is worth: it catches DIVERGENCE, and it is blind by
37
+ * construction to a bug both copies share — it stayed green through defects 2
38
+ * AND 4 above, both of which were present in both copies at once.
39
+ */
40
+ export function readLabsTitles(text: string, fileName = 'story.tsx'): string[] {
41
+ // ScriptKind BY EXTENSION. Parsing a `.stories.ts` as TSX loses every title
42
+ // after a `<number>x` cast or a bare `<T>(x) => x` generic arrow, because
43
+ // neither is valid TSX. tsc parses that file as TS, accepts it and stays
44
+ // green, so typecheck does NOT contain this one — the lint would silently
45
+ // drop the registration on a file CI calls fine. storyExtensions() already
46
+ // admits `.ts` because the Storybook glob does.
47
+ const kind = fileName.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
48
+ const source = ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, false, kind);
49
+ const meta = metaObject(source);
50
+ if (!meta) return [];
51
+ const titles: string[] = [];
52
+ for (const prop of meta.properties) {
53
+ if (!ts.isPropertyAssignment(prop)) continue;
54
+ const key = ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name) ? prop.name.text : undefined;
55
+ if (key !== 'title') continue;
56
+ const init = prop.initializer;
57
+ if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
58
+ if (init.text.startsWith('Labs/')) titles.push(init.text);
59
+ }
60
+ }
61
+ return titles;
62
+ }
63
+
64
+ /**
65
+ * The story file's DEFAULT-EXPORTED meta object, or null.
66
+ *
67
+ * Walking every `title:` in the file was the narrower defect: a `title` nested
68
+ * in a story's `args` is not a registration, and Storybook never reads it as
69
+ * one. Measured before this existed, both exit 0:
70
+ * `export const GhostProbeStory = { args: { title: 'Labs/GHOSTPROBE' } };`
71
+ * made an invented inventory row resolve, and the `Labs/Apps` spelling of the
72
+ * same shape produced a PHANTOM app that satisfied the lint and
73
+ * surfaces.test.ts together — the exact both-sides-green shape this guard
74
+ * exists to prevent, reached without a single comment.
75
+ *
76
+ * Restricting to the meta is a measured NO-OP on the clean tree: the title set
77
+ * and the nine `Labs/Apps` files are identical by set difference in both
78
+ * directions.
79
+ *
80
+ * Handles the two shapes in the tree and their wrappers:
81
+ * `const meta = {…} satisfies Meta; export default meta;` and
82
+ * `export default {…} as Meta;`
83
+ * `satisfies`, `as` and parentheses are unwrapped. An angle-bracket assertion
84
+ * (`export default <Meta>{…}`) deliberately is NOT: every story file is parsed
85
+ * as TSX, where `<Meta>{…}` is JSX and never a type assertion, so a branch for
86
+ * it would be dead code. Measured: that spelling returns []. It is also not a
87
+ * spelling any story in the tree uses.
88
+ */
89
+ function metaObject(source: ts.SourceFile): ts.ObjectLiteralExpression | null {
90
+ const unwrap = (expr: ts.Expression | undefined): ts.Expression | undefined => {
91
+ let cur = expr;
92
+ while (
93
+ cur &&
94
+ (ts.isAsExpression(cur) || ts.isSatisfiesExpression(cur) || ts.isParenthesizedExpression(cur))
95
+ ) {
96
+ cur = cur.expression;
97
+ }
98
+ return cur;
99
+ };
100
+ const topLevel = new Map<string, ts.Expression>();
101
+ let defaultExport: ts.Expression | undefined;
102
+ for (const stmt of source.statements) {
103
+ if (ts.isVariableStatement(stmt)) {
104
+ for (const d of stmt.declarationList.declarations) {
105
+ if (ts.isIdentifier(d.name) && d.initializer) topLevel.set(d.name.text, d.initializer);
106
+ }
107
+ } else if (ts.isExportAssignment(stmt) && !stmt.isExportEquals) {
108
+ defaultExport = stmt.expression;
109
+ }
110
+ }
111
+ let expr = unwrap(defaultExport);
112
+ if (expr && ts.isIdentifier(expr)) expr = unwrap(topLevel.get(expr.text));
113
+ return expr && ts.isObjectLiteralExpression(expr) ? expr : null;
114
+ }
@@ -0,0 +1,87 @@
1
+ import { z } from 'zod';
2
+ import { Scenario, type TScenario } from './catalog-types';
3
+
4
+ /**
5
+ * The acceptance deck, spec §6. Normative: written BEFORE the catalog so the
6
+ * catalog is built toward a measurement. The harness gives an agent the catalog
7
+ * and NO kit source; whatever it cannot build names what the catalog is missing.
8
+ */
9
+ export const scenarios: TScenario[] = [
10
+ {
11
+ id: 'S1',
12
+ prompt:
13
+ 'I already have <kai-chat> in my React app. Add a conversations sidebar and let assistant replies open artifacts in a side panel.',
14
+ needs: ['composition validity', 'wiring topology', 'invariant:reactivity-two-halves'],
15
+ depth: 'surface recipe applied to an existing tree',
16
+ scoring: [
17
+ 'emitted code compiles under the react consumer tsc project',
18
+ 'kai-conversations and kai-artifact register and render',
19
+ 'kai-conversation-select wiring updates kai-chat.messages with a new array AND new changed-item objects',
20
+ ],
21
+ },
22
+ {
23
+ id: 'S2',
24
+ prompt:
25
+ 'Add an AI chat to this Vue app. Messages stream from our existing /api/chat endpoint that speaks OpenAI SSE.',
26
+ needs: ['ingredient contracts', 'invariant:kit-parses-consumer-fetches', 'backend: consumer-owned endpoint'],
27
+ depth: 'greenfield, contract',
28
+ scoring: [
29
+ 'imports readOpenAIStream from @kitn.ai/ui/wire; no hand-rolled SSE reader anywhere in the output',
30
+ 'streams correctly against a mock OpenAI-SSE wire fixture',
31
+ 'compiles under the vue consumer path',
32
+ ],
33
+ },
34
+ {
35
+ id: 'S3',
36
+ prompt: 'Give the prompt input slash-commands and voice, like your command palette demo.',
37
+ needs: ['ingredient configuration space', 'function-valued property contract (transcribe)'],
38
+ depth: 'capability',
39
+ scoring: [
40
+ 'transcribe is set as a function-valued JS property, not an attribute',
41
+ 'the slash-command trigger is wired per the ingredient contract',
42
+ ],
43
+ },
44
+ {
45
+ id: 'S4',
46
+ prompt: 'Build me a Perplexity-style research UI: sources, reasoning panel, follow-up suggestions.',
47
+ needs: ['surface recipes'],
48
+ depth: 'whole surface; expected to fail hardest first',
49
+ scoring: ['human eyeball against the perplexity Labs/App story', 'compiles and registers'],
50
+ },
51
+ {
52
+ id: 'S5',
53
+ prompt:
54
+ "I'm on WordPress. No build step. Give me a script tag for a support chat widget that talks to my service at https://example.com/chat.",
55
+ needs: ['delivery target: script-tag', 'invariant:upgrade-race', 'widget recipe', 'backend: consumer-owned endpoint'],
56
+ depth: 'platform embed',
57
+ scoring: [
58
+ 'script-tag only, no bundler assumed',
59
+ 'the output acknowledges the upgrade race per the open invariant (props set after registration, or the documented gate)',
60
+ 'human eyeball in a plain HTML page',
61
+ ],
62
+ },
63
+ {
64
+ id: 'S6',
65
+ prompt: 'Add a spreadsheet-grid message type showing live cell edits.',
66
+ needs: ['the honesty bound: refuse what is not composable from these parts'],
67
+ depth: 'refusal',
68
+ scoring: [
69
+ 'the agent refuses loudly, naming that no such element exists, instead of inventing <kai-datagrid>',
70
+ 'no fabricated tag appears in the output',
71
+ ],
72
+ },
73
+ {
74
+ id: 'S7',
75
+ prompt: 'My messages render but nothing updates while streaming.',
76
+ needs: ['invariant diagnosis fields'],
77
+ depth: 'debugging',
78
+ scoring: [
79
+ 'the answer identifies the reactivity-two-halves cause: same array reference, or same item object identity',
80
+ 'the fix it proposes matches the invariant statement',
81
+ ],
82
+ },
83
+ ];
84
+
85
+ export function listScenarios(): TScenario[] {
86
+ return z.array(Scenario).parse(scenarios);
87
+ }
@@ -0,0 +1,275 @@
1
+ import { z } from 'zod';
2
+ import {
3
+ InventoryEntry,
4
+ PartConsumption,
5
+ SurfaceRecipe,
6
+ type TInventoryEntry,
7
+ type TPartConsumption,
8
+ type TSurfaceRecipe,
9
+ } from './catalog-types';
10
+
11
+ /**
12
+ * Spec §4's sort, as records. Criterion: a surface is product-shaped, something
13
+ * a user could be handed, proven end-to-end by a Labs/App or deployable alone;
14
+ * an ingredient exists only inside something else; fixtures and proofs are
15
+ * corpus (tests), not catalog entries.
16
+ *
17
+ * TWO SENSES OF "CORPUS", and they are not the same thing. Here it is a `sort`:
18
+ * this entry is a fixture or a proof, so it is test material rather than a
19
+ * catalog entry. On a SurfaceRecipe, `corpus:` is a list of repo PATHS that
20
+ * demonstrate the recipe. A story can be both — `split-workspace` is sorted
21
+ * `surface` in this inventory and also appears in a recipe's `corpus:` array,
22
+ * which is coherent (a product-shaped surface is exactly what demonstrates a
23
+ * recipe) but reads as a contradiction if you join the two on the word. A
24
+ * packer must key on `sort === 'corpus'` and on `recipe.corpus` separately.
25
+ *
26
+ * Every `title` is a real Storybook title segment under `Labs/` — the app names
27
+ * are the `Labs/Apps` story FILENAMES, the rest are `title: 'Labs/<x>'` values
28
+ * under src/ (note: NOT all under src/elements/ — Settings lives in src/ui/ and
29
+ * Audio Visualizers under src/components/, so a check scoped to src/elements/
30
+ * alone would wrongly flag them).
31
+ *
32
+ * WHAT IS ENFORCED TODAY, in the present tense. Every claim below was measured
33
+ * by mutation in both directions, not reasoned from reading the guards.
34
+ *
35
+ * EVERY ROW IS RESOLVED AGAINST THE TREE. `lint:catalog-drift` (required CI,
36
+ * needs no build) requires each title to match a Labs story title or a
37
+ * `Labs/Apps` story filename on disk. Misspell ANY row -- tier is irrelevant --
38
+ * and CI fails by name: `Settings` -> `Settingss` gives
39
+ * `inventory: "Settingss" matches no Labs story title or Labs/Apps story file in
40
+ * the tree.` That closes what an earlier version of this comment called
41
+ * "GENUINELY UNCHECKED", and the earlier text is worth remembering as a defect
42
+ * in its own right: it survived the guard landing and went on telling every
43
+ * maintainer that the coverage did not exist, which is how someone ends up
44
+ * rebuilding it.
45
+ *
46
+ * WHAT RESOLUTION DOES NOT COVER IS DELETION, because the check runs row -> tree
47
+ * and a deleted row asks nothing of the tree. The other direction is covered
48
+ * unevenly, and this is the part to read before deleting anything:
49
+ *
50
+ * 1. `surface` rows naming a `Labs/Apps` story FILE -- deletion FAILS
51
+ * surfaces.test.ts, which reads those filenames off disk and requires each
52
+ * to have a row sorted `surface`. Covered in both directions.
53
+ * 2. `Proofs`, `Chat Slots`, `Prompt Input Slots`, `Workspace Slots` --
54
+ * deletion FAILS surfaces.test.ts, which asserts those four exact titles are
55
+ * present and sorted `corpus`. That list is a literal copy of these four
56
+ * titles living inside the test; changing one means changing both.
57
+ * 3. Every remaining row -- deletion PASSES everything. Measured: removing the
58
+ * `Settings` row leaves both surfaces.test.ts and lint:catalog-drift green.
59
+ * This is the one real gap, and the lint's own honesty item 8 states it.
60
+ *
61
+ * Each row's tier is decided by which test in surfaces.test.ts covers it, so
62
+ * read that file, not this list, if they drift.
63
+ */
64
+ export const inventory: TInventoryEntry[] = [
65
+ { title: 'claude-code', sort: 'surface', note: 'Labs/App, end-to-end composition' },
66
+ { title: 'chatgpt', sort: 'surface', note: 'Labs/App, end-to-end composition' },
67
+ { title: 'codex', sort: 'surface', note: 'Labs/App, end-to-end composition' },
68
+ { title: 't3code', sort: 'surface', note: 'Labs/App, end-to-end composition' },
69
+ { title: 'perplexity', sort: 'surface', note: 'Labs/App, end-to-end composition' },
70
+ { title: 'perplexity-pro', sort: 'surface', note: 'Labs/App, end-to-end composition' },
71
+ { title: 'v0', sort: 'surface', note: 'Labs/App, end-to-end composition' },
72
+ { title: 'wisp', sort: 'surface', note: 'Labs/App, end-to-end composition' },
73
+ { title: 'lovable', sort: 'surface', note: 'Labs/App, end-to-end composition' },
74
+ { title: 'split-workspace', sort: 'surface', note: 'Labs/App, end-to-end composition' },
75
+ { title: 'Workspace Home', sort: 'surface', note: 'the workspace preset' },
76
+ { title: 'Message Thread', sort: 'ingredient', note: 'the keystone of the composition-first direction (kai-thread)' },
77
+ { title: 'Composer', sort: 'ingredient', note: 'rich input, lives inside a surface' },
78
+ { title: 'Command', sort: 'ingredient', note: 'palette, summoned inside something' },
79
+ { title: 'Menu', sort: 'ingredient', note: 'menu primitive' },
80
+ { title: 'User Menu', sort: 'ingredient', note: 'account affordance inside a surface' },
81
+ { title: 'Settings', sort: 'ingredient', note: 'a panel, not a product' },
82
+ { title: 'Onboarding checklist', sort: 'ingredient', note: 'lives inside a surface' },
83
+ { title: 'Conversations Collapse', sort: 'ingredient', note: 'behavior of a part' },
84
+ { title: 'Resizable Collapsed', sort: 'ingredient', note: 'behavior of a part' },
85
+ { title: 'Audio Visualizers', sort: 'ingredient', note: 'voice affordance inside a surface' },
86
+ { title: 'Card', sort: 'ingredient', note: 'generative-UI cards arrive as tool calls (settled)' },
87
+ {
88
+ title: 'Foundations',
89
+ sort: 'ingredient',
90
+ note: 'atoms: Input, Search, Kbd, Nav, Tabs, Status, Screen, Progress Bar, Coachmark, EditableLabel, Voice output',
91
+ },
92
+ { title: 'Chat Slots', sort: 'corpus', note: 'fixture proving kai-chat injection seams' },
93
+ { title: 'Prompt Input Slots', sort: 'corpus', note: 'fixture proving prompt-input seams' },
94
+ { title: 'Workspace Slots', sort: 'corpus', note: 'fixture proving workspace injection seams' },
95
+ { title: 'Proofs', sort: 'corpus', note: 'tests by construction' },
96
+ ];
97
+
98
+ /**
99
+ * Two recipes: one per delivery target. Between them they instance every
100
+ * invariant, which is what makes the drift lint's checks non-vacuous. Further
101
+ * recipes are added through acceptance-run iteration, each proven against a
102
+ * scenario before it lands.
103
+ *
104
+ * EVERY `wiring` edge below is EXECUTED in surfaces.test.ts against the real
105
+ * registered elements in jsdom — the event is fired the way a user fires it,
106
+ * the named property is assigned, and the effect is asserted in the shadow DOM.
107
+ * Name resolution against derived.json proves an event and a property EXIST; it
108
+ * does not prove the edge works, and that gap is where a plausible falsehood
109
+ * would live. Add an edge here only with a probe beside it.
110
+ */
111
+ export const surfaceRecipes: TSurfaceRecipe[] = [
112
+ {
113
+ id: 'workspace-chat',
114
+ intent:
115
+ 'Full-screen chat with a conversations sidebar; assistant replies can open artifacts in a resizable side panel.',
116
+ archetypes: ['full-screen'],
117
+ targets: ['bundler'],
118
+ ingredients: ['kai-chat', 'kai-conversations', 'kai-resizable', 'kai-artifact'],
119
+ backend: { endpoint: 'consumer-owned', reader: 'readModelStream' },
120
+ // WHERE the parts go, which the wiring edges below never said. Until this
121
+ // field existed a builder could read the whole recipe, the whole element
122
+ // reference and the whole scaffold and still not know whether the rail was a
123
+ // child of the chat or a sibling beside it — and the two answers lay out
124
+ // differently, so it is not a detail anyone can defer. One agent building
125
+ // from the MCP said so in as many words and then guessed.
126
+ //
127
+ // Slotted, and the tree decided it rather than taste: `<kai-chat>` declares a
128
+ // `sidebar` slot whose own description is "Left column (your nav /
129
+ // conversation list). Fixed width; use compose-your-own for resizable", and
130
+ // this recipe's own corpus story — chat-slots.stories.tsx — composes exactly
131
+ // `<kai-conversations slot="sidebar">` inside `<kai-chat>`.
132
+ //
133
+ // THE TREE ALSO PUBLISHES THE OTHER ANSWER, and an earlier version of this
134
+ // comment claimed it did not ("nothing in the tree composes them as
135
+ // siblings") — asserted without looking, which is the same defect as the
136
+ // over-generous `note` below it. The docs put the pair side by side in at
137
+ // least five places: guides/frameworks/html.mdx, svelte.mdx (a section
138
+ // titled "Add a conversation sidebar"), angular.mdx, vue.mdx, and
139
+ // examples/knowledge-base.mdx with its live demo. Every one of them is a
140
+ // layout the CONSUMER owns — a flex row they write, or `<kai-resizable>` —
141
+ // which is the compose-your-own path the slot's own description points at,
142
+ // and is what you want when the rail must resize. So the two are not in
143
+ // conflict about what is legal; this record says which one the RECIPE is,
144
+ // and the docs currently present the sibling form without saying that.
145
+ composition: [
146
+ {
147
+ child: 'kai-conversations',
148
+ parent: 'kai-chat',
149
+ slot: 'sidebar',
150
+ // Every clause here is checked against chat-thread.tsx, not inferred
151
+ // from the slot's name. The shell's whole sidebar implementation is one
152
+ // `<aside part="sidebar" class="flex w-64 shrink-0 …">` around a
153
+ // `<slot name="sidebar" />`: fixed width, no responsive class in the
154
+ // file, no collapse logic, and `<kai-chat>` never listens for
155
+ // `kai-collapse-toggle`. Collapse belongs to the rail
156
+ // (`collapsed`/`collapse()`), and conversation-list.tsx says so in as
157
+ // many words: "the host owns the surrounding region". A note that
158
+ // credited the shell with collapse and a breakpoint would send a builder
159
+ // into a 16rem empty column holding a floating reopen button.
160
+ note: 'the rail is a light-DOM child of <kai-chat> carrying slot="sidebar", not a sibling: the shell renders it into its own ::part(sidebar) aside, a FIXED-WIDTH column (w-64 in chat-thread.tsx, exposed as a part so you can restyle it) — and that is the whole of what the shell does. Collapse is the RAIL\'s own (collapsed / collapse() / kai-collapse-toggle) and <kai-chat> does not react to it, so a collapsed rail leaves the column at its fixed width; there is no responsive behaviour here either. Give the rail height (display:block;height:100%) and drive it through its own JS properties — being slotted changes where it renders, never how it is wired. If the COLUMN itself has to collapse, resize or respond to width, that is the <kai-workspace> layout shell (its start aside: startCollapsed / collapseBelow / drawer-below, widths via the --kai-workspace-start-* custom properties) or a layout you own',
161
+ },
162
+ ],
163
+ wiring: [
164
+ {
165
+ from: 'kai-conversations',
166
+ event: 'kai-conversation-select',
167
+ to: 'kai-chat',
168
+ property: 'messages',
169
+ note: 'detail is {id}; the host looks the thread up and assigns it — a new array AND a new object per changed item (reactivity-two-halves)',
170
+ },
171
+ {
172
+ from: 'kai-conversations',
173
+ event: 'kai-new-chat',
174
+ to: 'kai-chat',
175
+ property: 'messages',
176
+ note: 'detail is empty by design; the event is the whole signal and the host resets to an empty thread',
177
+ },
178
+ {
179
+ from: 'kai-chat',
180
+ event: 'kai-submit',
181
+ to: 'kai-chat',
182
+ property: 'messages',
183
+ note: 'host reads event.detail.value, appends the user turn, streams the reply through the wire reader onto parts[]',
184
+ },
185
+ {
186
+ from: 'kai-artifact',
187
+ event: 'kai-maximize-change',
188
+ to: 'kai-resizable',
189
+ property: 'maximizedIndex',
190
+ note: 'detail is {maximized}, NOT an index: the host knows which panel holds the artifact and mirrors the boolean onto that index (null to restore). Only needed when the panel is not an ancestor — an artifact INSIDE a kai-resizable-item already drives it through the bubbling kai-maximize-intent protocol, with no host code at all',
191
+ },
192
+ ],
193
+ invariants: [
194
+ 'reactivity-two-halves',
195
+ 'props-not-attributes',
196
+ 'events-non-bubbling',
197
+ 'host-coordinates',
198
+ 'kit-parses-consumer-fetches',
199
+ 'untrusted-model-output',
200
+ ],
201
+ // No single story composes all four ingredients, so the corpus names what
202
+ // each half is really proven by rather than one path that only half applies:
203
+ // chat-slots composes kai-chat with kai-conversations, split-workspace
204
+ // composes kai-resizable with kai-artifact, and surfaces.test.ts is where
205
+ // the four wiring edges above are actually executed.
206
+ corpus: [
207
+ 'packages/ui/src/elements/chat-slots.stories.tsx',
208
+ 'packages/ui/src/elements/split-workspace.stories.tsx',
209
+ 'packages/ui/src/agent-tooling/catalog/surfaces.test.ts',
210
+ ],
211
+ },
212
+ {
213
+ // The script-tag instance. Without it, DeliveryTarget 'script-tag' and the
214
+ // whole upgrade-race invariant exist in the schema with no recipe using
215
+ // them, and S5 has nothing to reconstruct from.
216
+ id: 'support-widget-script-tag',
217
+ intent:
218
+ 'A docked support chat widget added to a page with a script tag and no build step, talking to an endpoint the site owner already runs. The CMS case.',
219
+ archetypes: ['widget', 'docked'],
220
+ targets: ['script-tag'],
221
+ ingredients: ['kai-chat'],
222
+ backend: { endpoint: 'consumer-owned', reader: 'readOpenAIStream' },
223
+ wiring: [
224
+ {
225
+ from: 'kai-chat',
226
+ event: 'kai-submit',
227
+ to: 'kai-chat',
228
+ property: 'messages',
229
+ note: 'host reads event.detail.value, appends the user turn, streams the reply through the wire reader; on this target the host is an inline script, not a framework, so the listener must be attached after customElements.whenDefined (upgrade-race)',
230
+ },
231
+ ],
232
+ invariants: [
233
+ 'upgrade-race',
234
+ 'reactivity-two-halves',
235
+ 'props-not-attributes',
236
+ 'events-non-bubbling',
237
+ 'kit-parses-consumer-fetches',
238
+ 'untrusted-model-output',
239
+ ],
240
+ corpus: ['packages/ui/README.md'],
241
+ },
242
+ ];
243
+
244
+ /**
245
+ * REGISTERED COPY (spec §3). Which MessagePart variants an element consumes is
246
+ * not derivable from any type today, so it is recorded here as an explicit copy.
247
+ * Task 7's drift lint fails when the union gains a variant no record accounts
248
+ * for, which is what stops this going stale silently.
249
+ *
250
+ * WHAT THE CHECKS CAN AND CANNOT SEE. surfaces.test.ts asserts both directions
251
+ * of NAME agreement with `derived.partVariants`: no variant in the union is
252
+ * unaccounted for by any record, and no record claims a variant the union does
253
+ * not have. Neither can check the claim that actually matters — whether
254
+ * `kai-chat` really renders a `source` part is an EDITORIAL judgement, and
255
+ * nothing in the tree derives it, which is exactly why this is a registered
256
+ * copy rather than generated data. A machine cannot tell a correct record from
257
+ * a confident wrong one here. That claim is measured by the acceptance deck,
258
+ * not by CI, so do not add an assertion that looks like it covers it.
259
+ */
260
+ export const partConsumption: TPartConsumption[] = [
261
+ { tag: 'kai-chat', consumes: ['text', 'reasoning', 'tool', 'card', 'source', 'file'] },
262
+ { tag: 'kai-message', consumes: ['text', 'reasoning', 'tool', 'card', 'source', 'file'] },
263
+ ];
264
+
265
+ export function listInventory(): TInventoryEntry[] {
266
+ return z.array(InventoryEntry).parse(inventory);
267
+ }
268
+
269
+ export function listSurfaceRecipes(): TSurfaceRecipe[] {
270
+ return z.array(SurfaceRecipe).parse(surfaceRecipes);
271
+ }
272
+
273
+ export function listPartConsumption(): TPartConsumption[] {
274
+ return z.array(PartConsumption).parse(partConsumption);
275
+ }
@@ -317,7 +317,13 @@ function reframeToOpenAISse(body: ReadableStream<Uint8Array>): ReadableStream<Ui
317
317
  async function chatHandler(request: Request): Promise<Response> {
318
318
  // Both model and tools come from the browser. \`tools\` arrives in OpenAI
319
319
  // function form and is converted to this API's shape below.
320
- const { model, messages, tools } = await readChatRequest(request);
320
+ let chatBody: ChatRequestBody;
321
+ try {
322
+ chatBody = await readChatRequest(request);
323
+ } catch (error) {
324
+ return toChatErrorResponse(error);
325
+ }
326
+ const { model, messages, tools } = chatBody;
321
327
  const { system, messages: anthropicMessages } = toAnthropicBody(messages);
322
328
  const anthropicTools = toAnthropicTools(tools);
323
329
 
@@ -24,9 +24,44 @@ import type { OpenAIWireMessage } from '@kitn.ai/ui/wire';
24
24
  */
25
25
  type ChatRequestBody = { messages: OpenAIWireMessage[] };
26
26
 
27
+ class ChatRequestError extends Error {
28
+ constructor(readonly status: number, message: string) { super(message); }
29
+ }
30
+
31
+ /** Narrow the JSON body once, at the edge. A bare GET, a malformed body, or a
32
+ * missing messages array is a ChatRequestError with a status — NEVER an
33
+ * unhandled SyntaxError (findings F-10). */
34
+ async function readChatRequest(request: Request): Promise<ChatRequestBody> {
35
+ if (request.method !== 'POST') {
36
+ throw new ChatRequestError(405, \`Method \${request.method} not allowed — POST /api/chat.\`);
37
+ }
38
+ let parsed: unknown;
39
+ try { parsed = await request.json(); } catch {
40
+ throw new ChatRequestError(400, 'Request body is not valid JSON.');
41
+ }
42
+ const body = parsed as ChatRequestBody;
43
+ if (!Array.isArray(body?.messages)) {
44
+ throw new ChatRequestError(400, 'Request body must carry a messages array.');
45
+ }
46
+ return body;
47
+ }
48
+
49
+ /** Map a guard rejection to the Response its status demands; rethrow anything
50
+ * else — an unexpected error should be loud, not laundered into a 400. */
51
+ function toChatErrorResponse(error: unknown): Response {
52
+ if (error instanceof ChatRequestError) return Response.json({ error: error.message }, { status: error.status });
53
+ throw error;
54
+ }
55
+
27
56
  export default {
28
57
  async fetch(req: Request, env: Env): Promise<Response> {
29
- const { messages } = (await req.json()) as ChatRequestBody;
58
+ let chatBody: ChatRequestBody;
59
+ try {
60
+ chatBody = await readChatRequest(req);
61
+ } catch (error) {
62
+ return toChatErrorResponse(error);
63
+ }
64
+ const { messages } = chatBody;
30
65
 
31
66
  let nativeStream: ReadableStream<Uint8Array>;
32
67
  try {
@@ -101,7 +136,13 @@ export default {
101
136
  },
102
137
  webRoute: `async function chatHandler(request: Request): Promise<Response> {
103
138
  // Proxy Workers AI over its OpenAI-compatible HTTP endpoint, token server-side.
104
- const { messages } = await readChatRequest(request);
139
+ let chatBody: ChatRequestBody;
140
+ try {
141
+ chatBody = await readChatRequest(request);
142
+ } catch (error) {
143
+ return toChatErrorResponse(error);
144
+ }
145
+ const { messages } = chatBody;
105
146
 
106
147
  const upstream = await fetch(
107
148
  \`https://api.cloudflare.com/client/v4/accounts/\${process.env.CF_ACCOUNT_ID}/ai/v1/chat/completions\`,
@@ -31,7 +31,13 @@ const agent = createReactAgent({
31
31
 
32
32
  // Stream a compiled LangGraph agent to the browser as OpenAI-format SSE.
33
33
  async function chatHandler(request: Request): Promise<Response> {
34
- const { messages } = await readChatRequest(request);
34
+ let chatBody: ChatRequestBody;
35
+ try {
36
+ chatBody = await readChatRequest(request);
37
+ } catch (error) {
38
+ return toChatErrorResponse(error);
39
+ }
40
+ const { messages } = chatBody;
35
41
 
36
42
  // agent.stream() coerces plain {role, content} objects into BaseMessage
37
43
  // instances itself, including OpenAI-shaped tool_calls, so the wire messages
@@ -28,7 +28,13 @@ const mastra = new MastraClient({ baseUrl: MASTRA_URL });
28
28
 
29
29
  // Proxy a Mastra agent to the browser as OpenAI-format SSE.
30
30
  async function chatHandler(request: Request): Promise<Response> {
31
- const { messages } = await readChatRequest(request);
31
+ let chatBody: ChatRequestBody;
32
+ try {
33
+ chatBody = await readChatRequest(request);
34
+ } catch (error) {
35
+ return toChatErrorResponse(error);
36
+ }
37
+ const { messages } = chatBody;
32
38
 
33
39
  // MastraClient's Agent.stream() takes AI-SDK CoreMessage[], not the OpenAI
34
40
  // wire format: each message has ONE literal role (not a union) and content