@kitn.ai/ui 0.20.1 → 0.22.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 (510) hide show
  1. package/README.md +44 -45
  2. package/dist/components/artifact-card.d.ts +51 -0
  3. package/dist/components/artifact.d.ts +1 -1
  4. package/dist/components/attachments.d.ts +2 -2
  5. package/dist/components/audio-visualizer/index.d.ts +3 -3
  6. package/dist/components/audio-visualizer/labs/lab-choreography.d.ts +59 -0
  7. package/dist/components/audio-visualizer/labs/lab-shaders.d.ts +22 -0
  8. package/dist/components/audio-visualizer/labs/lab-visualizer.d.ts +18 -0
  9. package/dist/components/audio-visualizer/variant-aurora.d.ts +2 -2
  10. package/dist/components/audio-visualizer/variant-bar.d.ts +2 -2
  11. package/dist/components/audio-visualizer/variant-custom.d.ts +2 -2
  12. package/dist/components/audio-visualizer/variant-grid.d.ts +1 -1
  13. package/dist/components/audio-visualizer/variant-radial.d.ts +1 -1
  14. package/dist/components/audio-visualizer/variant-wave.d.ts +1 -1
  15. package/dist/components/card-fallback.d.ts +12 -2
  16. package/dist/components/card-renderer.d.ts +63 -2
  17. package/dist/components/chat-container.d.ts +3 -0
  18. package/dist/components/chat-scope-picker.d.ts +2 -2
  19. package/dist/components/chat-thread.d.ts +77 -18
  20. package/dist/components/choice-card.d.ts +3 -28
  21. package/dist/components/composer-dom.d.ts +1 -1
  22. package/dist/components/composer-highlight.d.ts +1 -1
  23. package/dist/components/composer-history.d.ts +1 -1
  24. package/dist/components/composer.d.ts +1 -1
  25. package/dist/components/confirm-card.d.ts +3 -18
  26. package/dist/components/context.d.ts +8 -4
  27. package/dist/components/conversation-item.d.ts +1 -1
  28. package/dist/components/conversation-list.d.ts +4 -3
  29. package/dist/components/embed.d.ts +3 -3
  30. package/dist/components/file-tree.d.ts +11 -22
  31. package/dist/components/form-widgets.d.ts +1 -1
  32. package/dist/components/form.d.ts +3 -44
  33. package/dist/components/link-preview.d.ts +3 -3
  34. package/dist/components/loader.d.ts +30 -48
  35. package/dist/components/message-skills.d.ts +4 -0
  36. package/dist/components/message.d.ts +84 -21
  37. package/dist/components/model-switcher.d.ts +2 -2
  38. package/dist/components/prompt-input.d.ts +2 -2
  39. package/dist/components/response-compare-types.d.ts +11 -2
  40. package/dist/components/response-compare.d.ts +3 -3
  41. package/dist/components/source.d.ts +12 -3
  42. package/dist/components/tasks-card.d.ts +3 -31
  43. package/dist/components/thread.d.ts +17 -5
  44. package/dist/components/toast.d.ts +2 -2
  45. package/dist/components/tool-classify.d.ts +8 -0
  46. package/dist/components/tool-types.d.ts +29 -1
  47. package/dist/components/tool.d.ts +3 -3
  48. package/dist/components/use-card-resolution.d.ts +1 -1
  49. package/dist/create-tween-COnPwAQN.js +338 -0
  50. package/dist/create-tween-DmTUaXWE.js +341 -0
  51. package/dist/create-tween-qs-jjvNA.js +7 -0
  52. package/dist/custom-elements.json +1726 -463
  53. package/dist/elements/agent-card.js +1 -1
  54. package/dist/elements/artifact.js +1 -1
  55. package/dist/elements/attachments.js +1 -1
  56. package/dist/elements/audio-visualizer.js +1 -1
  57. package/dist/elements/avatar.js +1 -1
  58. package/dist/elements/badge.js +1 -1
  59. package/dist/elements/button.js +1 -1
  60. package/dist/elements/card.js +1 -1
  61. package/dist/elements/cards.js +1 -1
  62. package/dist/elements/chain-of-thought.js +1 -1
  63. package/dist/elements/chat-scope-picker.js +1 -1
  64. package/dist/elements/chat-types.d.ts +70 -10
  65. package/dist/elements/chat-workspace.js +1 -1
  66. package/dist/elements/chat.js +1 -1
  67. package/dist/elements/checkpoint.js +1 -1
  68. package/dist/elements/choice.js +1 -1
  69. package/dist/elements/chunks/{Icon-Tj58ES_d.js → Icon-CHG5z5L7.js} +1 -1
  70. package/dist/elements/chunks/{action-icons-ns1Fnedj.js → action-icons-BAtCEEgg.js} +1 -1
  71. package/dist/elements/chunks/arrow-left-CFPTeL1Y.js +1 -0
  72. package/dist/elements/chunks/artifact-fBZOE4SO.js +1 -0
  73. package/dist/elements/chunks/{attachments-mCB5F1Fq.js → attachments-DpYMe5VQ.js} +1 -1
  74. package/dist/elements/chunks/audio-visualizer-DFwzAcuL.js +1 -0
  75. package/dist/elements/chunks/{badge-B4Zuajl2.js → badge-D6zodm6E.js} +1 -1
  76. package/dist/elements/chunks/{button-ZWNif3--.js → button-C42vrGvO.js} +1 -1
  77. package/dist/elements/chunks/card-renderer-DMZsSuvU.js +1 -0
  78. package/dist/elements/chunks/card-tags-D8lZ-C_U.js +1 -0
  79. package/dist/elements/chunks/chat-thread-CNNEhol-.js +1 -0
  80. package/dist/elements/chunks/check-7Q3K-iXE.js +1 -0
  81. package/dist/elements/chunks/chevron-down-CpNYLn7E.js +1 -0
  82. package/dist/elements/chunks/chevron-right-DrH85Fha.js +1 -0
  83. package/dist/elements/chunks/choice-card-YoWjpsFV.js +1 -0
  84. package/dist/elements/chunks/circle-C9KRjKYv.js +1 -0
  85. package/dist/elements/chunks/circle-check-Dmi7Y4zw.js +1 -0
  86. package/dist/elements/chunks/{circle-x-B7faJFlY.js → circle-x-QIxXda6k.js} +1 -1
  87. package/dist/elements/chunks/code-block-CqeugReY.js +1 -0
  88. package/dist/elements/chunks/{collapsible-DM1Ckd4e.js → collapsible-sTv-_5lF.js} +1 -1
  89. package/dist/elements/chunks/{composer-BguS0QL_.js → composer-ldQyeVEB.js} +2 -2
  90. package/dist/elements/chunks/confirm-card-C0RNOwfn.js +1 -0
  91. package/dist/elements/chunks/context-5qgypNHC.js +1 -0
  92. package/dist/elements/chunks/conversation-list-DLzOacXz.js +1 -0
  93. package/dist/elements/chunks/create-tween-V4TXZ93X.js +7 -0
  94. package/dist/elements/chunks/default-input-DeeRSDHc.js +1 -0
  95. package/dist/elements/chunks/define-lE-QvQFn.js +1 -0
  96. package/dist/elements/chunks/{disclosure-CIZ5c8nO.js → disclosure-DZSa9jog.js} +1 -1
  97. package/dist/elements/chunks/{download-Dsbg3ste.js → download-B2ZmnGqW.js} +1 -1
  98. package/dist/elements/chunks/{dropdown-BDd1-Zgi.js → dropdown-R3B7cSRE.js} +1 -1
  99. package/dist/elements/chunks/{ellipsis-Dw6qrss-.js → ellipsis-BmxceY4l.js} +1 -1
  100. package/dist/elements/chunks/embed-BVrtTZPo.js +1 -0
  101. package/dist/elements/chunks/{external-link-Vt74zAoL.js → external-link-BbdR7YO7.js} +1 -1
  102. package/dist/elements/chunks/{file-text-CZrZF6_v.js → file-text-D7dijo1A.js} +1 -1
  103. package/dist/elements/chunks/{file-tree-DF4okKyo.js → file-tree-0upJ5jeb.js} +1 -1
  104. package/dist/elements/chunks/{folder-glP5rha4.js → folder-D8z58t7B.js} +1 -1
  105. package/dist/elements/chunks/form-p5BKJNw5.js +1 -0
  106. package/dist/elements/chunks/hover-card-CN9s2md5.js +1 -0
  107. package/dist/elements/chunks/{icon-BLb8beoi.js → icon-CNiKP37E.js} +1 -1
  108. package/dist/elements/chunks/{info-CIsv92JK.js → info-BSnuJiHI.js} +1 -1
  109. package/dist/elements/chunks/{input-Dds1kFyt.js → input-BauDmzxX.js} +1 -1
  110. package/dist/elements/chunks/{kbd-De_xbgi9.js → kbd-BlHJaP9b.js} +1 -1
  111. package/dist/elements/chunks/{link-i4r_k7z4.js → link-DEi5iUWC.js} +1 -1
  112. package/dist/elements/chunks/link-preview-BaHyM6W4.js +1 -0
  113. package/dist/elements/chunks/{loader-BmQH4s3V.js → loader-CmJG_Fkq.js} +1 -1
  114. package/dist/elements/chunks/{markdown-Zi21WhUz.js → markdown-D2jaMpCN.js} +1 -1
  115. package/dist/elements/chunks/message-C5UfXZ2Z.js +1 -0
  116. package/dist/elements/chunks/message-CXWLmmLn.js +1 -0
  117. package/dist/elements/chunks/message-square-hXylwsy0.js +1 -0
  118. package/dist/elements/chunks/{minimize-2--ifeCGrl.js → minimize-2-C9ytZFMU.js} +1 -1
  119. package/dist/elements/chunks/model-switcher-CivGfD7-.js +1 -0
  120. package/dist/elements/chunks/{overlay-kD8dwRv_.js → overlay-DJL_sbOm.js} +1 -1
  121. package/dist/elements/chunks/{paperclip-q21dYOSK.js → paperclip-CFaB8naM.js} +1 -1
  122. package/dist/elements/chunks/{progress-bar-BrAIIaaJ.js → progress-bar-Br5SksfR.js} +1 -1
  123. package/dist/elements/chunks/{prompt-suggestion-Bjn2K-fR.js → prompt-suggestion-CMIvtAhU.js} +1 -1
  124. package/dist/elements/chunks/reasoning-DpZcNzLq.js +1 -0
  125. package/dist/elements/chunks/{resizable-Dp4JExql.js → resizable-Q8i3T9a_.js} +1 -1
  126. package/dist/elements/chunks/{rotate-cw-CFWWkpj2.js → rotate-cw-B5IHCBFV.js} +1 -1
  127. package/dist/elements/chunks/{scroll-area-Bb0kHH1j.js → scroll-area-eLeydBje.js} +1 -1
  128. package/dist/elements/chunks/scroll-button-DWbz0yA1.js +1 -0
  129. package/dist/elements/chunks/{separator-9kRgr1X9.js → separator-Xic78AD_.js} +1 -1
  130. package/dist/elements/chunks/{settings-B8lmec4D.js → settings-B8H51pAx.js} +1 -1
  131. package/dist/elements/chunks/{settings-group-I0hEuXiN.js → settings-group-CsG9eYLe.js} +1 -1
  132. package/dist/elements/chunks/{share-C3lTiYGt.js → share-CNTx0z__.js} +1 -1
  133. package/dist/elements/chunks/skeleton-CLlHTcMI.js +1 -0
  134. package/dist/elements/chunks/slots-CIw9RlAe.js +1 -0
  135. package/dist/elements/chunks/source-BoVcU5ok.js +1 -0
  136. package/dist/elements/chunks/{star-CSJtGxgv.js → star-CxQVTVhI.js} +1 -1
  137. package/dist/elements/chunks/{store-CHyW95TP.js → store-CLtS5CUG.js} +1 -1
  138. package/dist/elements/chunks/tasks-card-CzlkLmUt.js +1 -0
  139. package/dist/elements/chunks/{text-shimmer-Cn9TmNpI.js → text-shimmer-D5NhpIQ1.js} +1 -1
  140. package/dist/elements/chunks/textarea-x5Uxsmtm.js +1 -0
  141. package/dist/elements/chunks/{thumbs-up-PNVVThEF.js → thumbs-up-UobcJnx9.js} +1 -1
  142. package/dist/elements/chunks/{toast-store-DKbQn3Wn.js → toast-store-BOfs9eXq.js} +1 -1
  143. package/dist/elements/chunks/{tool-DjTRpIpl.js → tool-Be6Dh_dt.js} +1 -1
  144. package/dist/elements/chunks/tooltip-RxMxyCLF.js +1 -0
  145. package/dist/elements/chunks/{triangle-alert-DsHTHCPm.js → triangle-alert-C4yxH2XN.js} +1 -1
  146. package/dist/elements/chunks/use-card-resolution-jva9ehH6.js +1 -0
  147. package/dist/elements/chunks/{variant-aurora-Dc4nQUia.js → variant-aurora-Dx4VRcjS.js} +2 -2
  148. package/dist/elements/chunks/{variant-custom-y_3kVQhS.js → variant-custom-Y1ZTWgEa.js} +1 -1
  149. package/dist/elements/chunks/{variant-wave-D5OR1-9Z.js → variant-wave-B9gml9Cg.js} +2 -2
  150. package/dist/elements/chunks/{video-B7vmenoC.js → video-CrPHsytT.js} +1 -1
  151. package/dist/elements/chunks/x-_-OPHept.js +1 -0
  152. package/dist/elements/coachmark.js +1 -1
  153. package/dist/elements/code-block.js +1 -1
  154. package/dist/elements/command.js +1 -1
  155. package/dist/elements/compare.js +1 -1
  156. package/dist/elements/composer.js +1 -1
  157. package/dist/elements/confirm-card.js +1 -1
  158. package/dist/elements/context-meter.js +1 -1
  159. package/dist/elements/conversation-list.js +1 -1
  160. package/dist/elements/default-input.d.ts +34 -7
  161. package/dist/elements/define.d.ts +1 -0
  162. package/dist/elements/dialog.js +1 -1
  163. package/dist/elements/disclosure.d.ts +1 -1
  164. package/dist/elements/editable-label.js +1 -1
  165. package/dist/elements/element-data-types.d.ts +107 -0
  166. package/dist/elements/embed.js +1 -1
  167. package/dist/elements/empty.js +1 -1
  168. package/dist/elements/feedback-bar.js +1 -1
  169. package/dist/elements/file-tree.js +1 -1
  170. package/dist/elements/file-upload.js +1 -1
  171. package/dist/elements/form.js +1 -1
  172. package/dist/elements/hover-card.js +1 -1
  173. package/dist/elements/icon.js +1 -1
  174. package/dist/elements/image.js +1 -1
  175. package/dist/elements/input.js +1 -1
  176. package/dist/elements/kbd.js +1 -1
  177. package/dist/elements/link-preview.js +1 -1
  178. package/dist/elements/loader.js +1 -1
  179. package/dist/elements/markdown.js +1 -1
  180. package/dist/elements/menu.js +1 -1
  181. package/dist/elements/message-skills.js +1 -1
  182. package/dist/elements/message.js +1 -1
  183. package/dist/elements/model-switcher.js +1 -1
  184. package/dist/elements/nav.js +1 -1
  185. package/dist/elements/notice.js +1 -1
  186. package/dist/elements/pane-group.js +1 -1
  187. package/dist/elements/pane.js +1 -1
  188. package/dist/elements/popover.js +1 -1
  189. package/dist/elements/progress-bar.js +1 -1
  190. package/dist/elements/prompt-dock.js +1 -1
  191. package/dist/elements/prompt-input.js +1 -1
  192. package/dist/elements/prompt-suggestions.js +1 -1
  193. package/dist/elements/reasoning.js +1 -1
  194. package/dist/elements/register.d.ts +8 -6
  195. package/dist/elements/remote.js +1 -1
  196. package/dist/elements/resizable.js +1 -1
  197. package/dist/elements/response-stream.js +1 -1
  198. package/dist/elements/screen.js +1 -1
  199. package/dist/elements/scroll-area.js +1 -1
  200. package/dist/elements/scroll-button.js +1 -1
  201. package/dist/elements/search.js +1 -1
  202. package/dist/elements/segmented.js +1 -1
  203. package/dist/elements/separator.js +1 -1
  204. package/dist/elements/setting-item.js +1 -1
  205. package/dist/elements/settings-group.js +1 -1
  206. package/dist/elements/skeleton.js +1 -1
  207. package/dist/elements/slot-text.d.ts +51 -0
  208. package/dist/elements/slots.d.ts +21 -0
  209. package/dist/elements/source.js +1 -1
  210. package/dist/elements/status.js +1 -1
  211. package/dist/elements/switch.js +1 -1
  212. package/dist/elements/tabs.js +1 -1
  213. package/dist/elements/tasks.js +1 -1
  214. package/dist/elements/text-shimmer.js +1 -1
  215. package/dist/elements/thinking-bar.js +1 -1
  216. package/dist/elements/thread.js +1 -1
  217. package/dist/elements/toast.js +1 -1
  218. package/dist/elements/tool.js +1 -1
  219. package/dist/elements/tooltip.js +1 -1
  220. package/dist/elements/validate-messages.d.ts +25 -0
  221. package/dist/elements/voice-input.js +1 -1
  222. package/dist/elements/voice-output.js +1 -1
  223. package/dist/elements.d.ts +2734 -217
  224. package/dist/index.d.ts +163 -125
  225. package/dist/index.js +6572 -5990
  226. package/dist/index.server.d.ts +5 -0
  227. package/dist/index.server.js +5403 -4860
  228. package/dist/kai-provider.es.d.ts +5 -0
  229. package/dist/kai-provider.es.js +79 -77
  230. package/dist/kai.es.d.ts +5 -0
  231. package/dist/kai.es.js +1 -1
  232. package/dist/llms/llms-full.txt +774 -151
  233. package/dist/llms/llms.txt +57 -5
  234. package/dist/mcp.es.js +4756 -782
  235. package/dist/primitives/card-component-types.d.ts +11 -0
  236. package/dist/primitives/card-data-types.d.ts +196 -0
  237. package/dist/primitives/card-host.d.ts +1 -1
  238. package/dist/primitives/card-recovery.d.ts +3 -3
  239. package/dist/primitives/card-registry.d.ts +4 -14
  240. package/dist/primitives/card-resolution.d.ts +1 -1
  241. package/dist/primitives/card-routing.d.ts +1 -1
  242. package/dist/primitives/card-tags.d.ts +11 -0
  243. package/dist/primitives/card-validate-cards.d.ts +57 -0
  244. package/dist/primitives/card-validate-generator.testlib.d.ts +20 -0
  245. package/dist/primitives/card-validate-schemas.d.ts +17 -0
  246. package/dist/primitives/card-validate.d.ts +24 -0
  247. package/dist/primitives/create-kai-chat.d.ts +3 -3
  248. package/dist/primitives/embed-providers.d.ts +1 -1
  249. package/dist/primitives/link-preview.d.ts +1 -1
  250. package/dist/primitives/message-feedback.d.ts +2 -2
  251. package/dist/primitives/toast-store.d.ts +1 -1
  252. package/dist/react/index.d.ts +730 -302
  253. package/dist/react/use-kai-chat.d.ts +8 -4
  254. package/dist/react.d.ts +5 -0
  255. package/dist/react.js +418 -317
  256. package/dist/register-impl-AxIlqjo7.js +145 -0
  257. package/dist/remote/host-embed.d.ts +1 -1
  258. package/dist/remote/index.d.ts +2 -2
  259. package/dist/remote/provider-runtime.d.ts +2 -2
  260. package/dist/remote/provider.d.ts +2 -2
  261. package/dist/remote/validate.d.ts +1 -1
  262. package/dist/remote/wire.d.ts +1 -1
  263. package/dist/schemas/artifact.schema.json +92 -0
  264. package/dist/schemas/embed.schema.json +0 -2
  265. package/dist/schemas/from-tool-call.d.ts +48 -0
  266. package/dist/schemas/index.d.ts +48 -0
  267. package/dist/schemas/link.schema.json +0 -2
  268. package/dist/schemas/provider-subsets.d.ts +136 -0
  269. package/dist/schemas/registry.d.ts +140 -0
  270. package/dist/schemas/tool-defs.d.ts +121 -0
  271. package/dist/schemas.d.ts +5 -0
  272. package/dist/schemas.js +717 -0
  273. package/dist/solid-BGYSJyHG.js +22960 -0
  274. package/dist/solid-Dt2nEwfl.js +20194 -0
  275. package/dist/solid.d.ts +66 -0
  276. package/dist/solid.js +261 -0
  277. package/dist/solid.server.d.ts +5 -0
  278. package/dist/solid.server.js +261 -0
  279. package/dist/state/index.d.ts +14 -4
  280. package/dist/state/messages.d.ts +8 -3
  281. package/dist/state/mock.d.ts +46 -0
  282. package/dist/state/parts.d.ts +85 -0
  283. package/dist/state/stream.d.ts +15 -9
  284. package/dist/state.d.ts +5 -0
  285. package/dist/state.js +253 -84
  286. package/dist/ui/action-icons.d.ts +1 -1
  287. package/dist/ui/collapsible.d.ts +11 -6
  288. package/dist/ui/dropdown.d.ts +47 -42
  289. package/dist/ui/pane-group.d.ts +1 -1
  290. package/dist/{variant-aurora-DVtGqVH6.js → variant-aurora-B44X69AG.js} +1 -1
  291. package/dist/variant-aurora-BRhV8Flk.js +391 -0
  292. package/dist/variant-aurora-Ds5ou9BH.js +381 -0
  293. package/dist/variant-custom-B20FhtYj.js +170 -0
  294. package/dist/{variant-custom-Dif-IV4u.js → variant-custom-BvA1Schl.js} +1 -1
  295. package/dist/variant-custom-D6Rtvneb.js +200 -0
  296. package/dist/{variant-wave-DbfNdvSi.js → variant-wave-BUQtkryq.js} +1 -1
  297. package/dist/variant-wave-BVYnqy9L.js +218 -0
  298. package/dist/variant-wave-DXC6_LcH.js +208 -0
  299. package/dist/wire/chunk.d.ts +171 -0
  300. package/dist/wire/consume.d.ts +26 -0
  301. package/dist/wire/encode.d.ts +221 -0
  302. package/dist/wire/files.d.ts +116 -0
  303. package/dist/wire/formats/anthropic.d.ts +2 -0
  304. package/dist/wire/formats/openai.d.ts +5 -0
  305. package/dist/wire/index.d.ts +16 -0
  306. package/dist/wire/media-types.d.ts +106 -0
  307. package/dist/wire/read.d.ts +26 -0
  308. package/dist/wire/sink-helpers.d.ts +21 -0
  309. package/dist/wire/sse.d.ts +33 -0
  310. package/dist/wire.d.ts +5 -53
  311. package/dist/wire.js +1126 -0
  312. package/frameworks/react/index.tsx +191 -158
  313. package/frameworks/react/runtime.tsx +21 -1
  314. package/frameworks/react/use-kai-chat.tsx +11 -1
  315. package/llms-full.txt +774 -151
  316. package/llms.txt +57 -5
  317. package/package.json +62 -8
  318. package/src/agent-tooling/archetypes.ts +127 -0
  319. package/src/agent-tooling/integrations/anthropic.ts +436 -0
  320. package/src/agent-tooling/integrations/cloudflare.ts +141 -52
  321. package/src/agent-tooling/integrations/langgraph.ts +80 -13
  322. package/src/agent-tooling/integrations/mastra.ts +161 -12
  323. package/src/agent-tooling/integrations/mock.ts +47 -8
  324. package/src/agent-tooling/integrations/ollama.ts +68 -24
  325. package/src/agent-tooling/integrations/openai.ts +89 -0
  326. package/src/agent-tooling/integrations/openrouter.ts +58 -8
  327. package/src/agent-tooling/integrations/pi.ts +104 -23
  328. package/src/agent-tooling/integrations/pydantic-ai.ts +61 -5
  329. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +120 -13
  330. package/src/agent-tooling/mcp/manifest.ts +206 -25
  331. package/src/agent-tooling/mcp/tools/debug.ts +13 -7
  332. package/src/agent-tooling/mcp/tools/reference.ts +285 -6
  333. package/src/agent-tooling/mcp/tools/scaffold.ts +4152 -661
  334. package/src/agent-tooling/registry.ts +74 -0
  335. package/src/agent-tooling/types.ts +361 -1
  336. package/src/components/artifact-card.tsx +120 -0
  337. package/src/components/audio-visualizer/index.tsx +9 -0
  338. package/src/components/audio-visualizer/labs/lab-choreography.ts +187 -0
  339. package/src/components/audio-visualizer/labs/lab-shaders.ts +363 -0
  340. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +102 -0
  341. package/src/components/audio-visualizer/shader-canvas.tsx +31 -1
  342. package/src/components/card-fallback.tsx +42 -10
  343. package/src/components/card-renderer.tsx +146 -9
  344. package/src/components/chat-container.tsx +4 -0
  345. package/src/components/chat-thread.tsx +128 -52
  346. package/src/components/choice-card.tsx +17 -29
  347. package/src/components/composer.tsx +12 -2
  348. package/src/components/confirm-card.tsx +24 -20
  349. package/src/components/context.tsx +30 -16
  350. package/src/components/conversation-item.tsx +6 -0
  351. package/src/components/conversation-list.tsx +18 -2
  352. package/src/components/file-tree.tsx +11 -22
  353. package/src/components/file-upload.tsx +14 -8
  354. package/src/components/form.tsx +14 -47
  355. package/src/components/loader.tsx +39 -12
  356. package/src/components/message-skills.tsx +4 -0
  357. package/src/components/message.tsx +294 -46
  358. package/src/components/model-switcher.tsx +38 -8
  359. package/src/components/reasoning.tsx +92 -5
  360. package/src/components/response-compare-types.ts +25 -0
  361. package/src/components/response-compare.tsx +4 -9
  362. package/src/components/source.tsx +50 -24
  363. package/src/components/tasks-card.tsx +10 -33
  364. package/src/components/thread.tsx +84 -42
  365. package/src/components/toast.tsx +17 -6
  366. package/src/components/tool-classify.ts +26 -0
  367. package/src/components/tool-types.ts +31 -1
  368. package/src/elements/agent-card.tsx +11 -11
  369. package/src/elements/artifact.tsx +15 -7
  370. package/src/elements/attachments.tsx +6 -4
  371. package/src/elements/avatar.tsx +1 -1
  372. package/src/elements/button.tsx +29 -6
  373. package/src/elements/card.tsx +2 -2
  374. package/src/elements/cards.tsx +109 -14
  375. package/src/elements/chain-of-thought.tsx +14 -10
  376. package/src/elements/chat-scope-picker.tsx +9 -7
  377. package/src/elements/chat-types.ts +59 -6
  378. package/src/elements/chat-workspace.tsx +50 -9
  379. package/src/elements/chat.tsx +90 -3
  380. package/src/elements/choice.tsx +8 -8
  381. package/src/elements/coachmark.tsx +1 -1
  382. package/src/elements/command.tsx +8 -23
  383. package/src/elements/compare.tsx +7 -6
  384. package/src/elements/compiled.css +1 -1
  385. package/src/elements/composer.tsx +8 -8
  386. package/src/elements/confirm-card.tsx +9 -9
  387. package/src/elements/context-meter.tsx +4 -18
  388. package/src/elements/conversation-list.tsx +14 -9
  389. package/src/elements/default-input.tsx +131 -8
  390. package/src/elements/define.tsx +178 -43
  391. package/src/elements/editable-label.tsx +17 -2
  392. package/src/elements/element-data-types.ts +112 -0
  393. package/src/elements/element-meta.json +1624 -306
  394. package/src/elements/element-types.d.ts +2645 -214
  395. package/src/elements/feedback-bar.tsx +1 -1
  396. package/src/elements/file-tree.tsx +1 -1
  397. package/src/elements/file-upload.tsx +1 -1
  398. package/src/elements/form.tsx +14 -6
  399. package/src/elements/input.tsx +1 -1
  400. package/src/elements/kbd.tsx +1 -1
  401. package/src/elements/menu.tsx +40 -32
  402. package/src/elements/message-skills.tsx +7 -9
  403. package/src/elements/message.tsx +200 -32
  404. package/src/elements/model-switcher.tsx +5 -3
  405. package/src/elements/pane.tsx +1 -1
  406. package/src/elements/prompt-dock.tsx +8 -8
  407. package/src/elements/prompt-input.tsx +10 -10
  408. package/src/elements/prompt-suggestions.tsx +7 -4
  409. package/src/elements/reasoning.tsx +1 -1
  410. package/src/elements/register.ts +9 -0
  411. package/src/elements/resizable.globals.d.ts +36 -0
  412. package/src/elements/resizable.tsx +24 -13
  413. package/src/elements/response-stream.tsx +1 -1
  414. package/src/elements/scroll-button.tsx +1 -1
  415. package/src/elements/search.tsx +6 -1
  416. package/src/elements/segmented.tsx +5 -11
  417. package/src/elements/separator.tsx +1 -1
  418. package/src/elements/slot-text.ts +73 -0
  419. package/src/elements/slots.ts +67 -24
  420. package/src/elements/source.tsx +21 -22
  421. package/src/elements/switch.tsx +3 -3
  422. package/src/elements/tasks.tsx +5 -4
  423. package/src/elements/thread.tsx +36 -4
  424. package/src/elements/toast.tsx +3 -2
  425. package/src/elements/tool.tsx +1 -1
  426. package/src/elements/validate-messages.ts +76 -0
  427. package/src/elements/voice-input.tsx +7 -7
  428. package/src/elements/voice-output.tsx +4 -4
  429. package/src/index.ts +145 -6
  430. package/src/primitives/card-component-types.ts +63 -0
  431. package/src/primitives/card-data-types.ts +312 -0
  432. package/src/primitives/card-recovery.ts +13 -2
  433. package/src/primitives/card-registry.tsx +34 -23
  434. package/src/primitives/card-schemas/artifact.schema.json +92 -0
  435. package/src/primitives/card-schemas/embed.schema.json +0 -2
  436. package/src/primitives/card-schemas/link.schema.json +0 -2
  437. package/src/primitives/card-tags.ts +61 -0
  438. package/src/primitives/card-validate-cards.ts +255 -0
  439. package/src/primitives/card-validate-generator.testlib.ts +38 -0
  440. package/src/primitives/card-validate-schemas.ts +58 -0
  441. package/src/primitives/card-validate.ts +48 -20
  442. package/src/primitives/create-tween.ts +54 -2
  443. package/src/primitives/message-feedback.ts +3 -2
  444. package/src/primitives/use-audio-analysis.ts +20 -1
  445. package/src/primitives/use-sequencer.ts +20 -1
  446. package/src/schemas/from-tool-call.ts +151 -0
  447. package/src/schemas/index.ts +340 -0
  448. package/src/schemas/provider-subsets.ts +538 -0
  449. package/src/schemas/registry.ts +355 -0
  450. package/src/schemas/tool-defs.ts +492 -0
  451. package/src/solid.ts +150 -0
  452. package/src/state/index.ts +27 -1
  453. package/src/state/messages.ts +21 -4
  454. package/src/state/mock.ts +211 -0
  455. package/src/state/parts.ts +295 -0
  456. package/src/state/stream.ts +56 -58
  457. package/src/ui/collapsible.tsx +17 -5
  458. package/src/ui/dropdown.tsx +46 -12
  459. package/src/ui/overlay.tsx +10 -4
  460. package/src/wire/chunk.ts +201 -0
  461. package/src/wire/consume.ts +440 -0
  462. package/src/wire/encode.ts +689 -0
  463. package/src/wire/files.ts +342 -0
  464. package/src/wire/formats/anthropic.ts +267 -0
  465. package/src/wire/formats/openai.ts +187 -0
  466. package/src/wire/index.ts +73 -0
  467. package/src/wire/media-types.ts +344 -0
  468. package/src/wire/read.ts +152 -0
  469. package/src/wire/sink-helpers.ts +50 -0
  470. package/src/wire/sse.ts +140 -0
  471. package/dist/create-tween-hvpmSuwW.js +0 -7
  472. package/dist/elements/chunks/arrow-left-DQocUFvy.js +0 -1
  473. package/dist/elements/chunks/audio-visualizer-BqNy7dfm.js +0 -1
  474. package/dist/elements/chunks/card-BkHbp4BA.js +0 -1
  475. package/dist/elements/chunks/chat-thread-CkRN6pZ7.js +0 -1
  476. package/dist/elements/chunks/check-B9-gn6SS.js +0 -1
  477. package/dist/elements/chunks/chevron-down-BNsxc2hk.js +0 -1
  478. package/dist/elements/chunks/chevron-right-Hv00StBO.js +0 -1
  479. package/dist/elements/chunks/circle-0e3KJTDr.js +0 -1
  480. package/dist/elements/chunks/circle-check-BNAS0JEy.js +0 -1
  481. package/dist/elements/chunks/code-block-BFUHn6Va.js +0 -1
  482. package/dist/elements/chunks/context-Cb7k4F-v.js +0 -1
  483. package/dist/elements/chunks/conversation-list-CvoAkWhN.js +0 -1
  484. package/dist/elements/chunks/create-tween-oVvAIG8r.js +0 -7
  485. package/dist/elements/chunks/default-input-Cqqilg9t.js +0 -1
  486. package/dist/elements/chunks/define-D1din7Ld.js +0 -1
  487. package/dist/elements/chunks/hover-card-Cmm6Q53m.js +0 -1
  488. package/dist/elements/chunks/message-BqyXDN3-.js +0 -1
  489. package/dist/elements/chunks/message-feedback-Djr3z6Yb.js +0 -1
  490. package/dist/elements/chunks/message-square-vFAgbDaX.js +0 -1
  491. package/dist/elements/chunks/model-switcher-B94YAKsM.js +0 -1
  492. package/dist/elements/chunks/reasoning-DTpCGtnY.js +0 -1
  493. package/dist/elements/chunks/scroll-button-B0cH5fXf.js +0 -1
  494. package/dist/elements/chunks/skeleton-DSXW5Yya.js +0 -1
  495. package/dist/elements/chunks/slots--V19HwUb.js +0 -1
  496. package/dist/elements/chunks/textarea-CUwqAX0G.js +0 -1
  497. package/dist/elements/chunks/tooltip-CgBZCVKp.js +0 -1
  498. package/dist/elements/chunks/use-card-resolution-BSu_XHjH.js +0 -1
  499. package/dist/elements/chunks/x-GRUeeQFT.js +0 -1
  500. package/dist/host-embed.d.ts +0 -33
  501. package/dist/kai-provider.d.ts +0 -2
  502. package/dist/origin.d.ts +0 -5
  503. package/dist/provider-runtime.d.ts +0 -27
  504. package/dist/register-impl-DQyejdfb.js +0 -145
  505. package/dist/validate.d.ts +0 -4
  506. package/dist/version.d.ts +0 -4
  507. package/src/components/component-meta.json +0 -4550
  508. package/src/elements/card-media.jpg +0 -0
  509. package/src/elements/resizable.d.ts +0 -27
  510. package/src/elements/video-poster.jpg +0 -0
@@ -17,8 +17,10 @@ import {
17
17
  } from '../components/attachments';
18
18
 
19
19
  interface Props extends Record<string, unknown> {
20
- /** The attachments to render. Set as a JS property (array). */
21
- items: AttachmentData[];
20
+ /** The attachments to render. Omit (or pass an empty array) for the empty
21
+ * state, which shows `emptyText` if set and nothing otherwise. Set as a JS
22
+ * property (array). */
23
+ items?: AttachmentData[];
22
24
  /** Layout: `grid` = visual tiles, `inline` = icon + label chips, `list` = rows. */
23
25
  variant?: AttachmentVariant;
24
26
  /** Wrap each item in a hover card that previews its details. */
@@ -67,11 +69,11 @@ defineWebComponent<Props, Events>('kai-attachments', {
67
69
 
68
70
  return (
69
71
  <Show
70
- when={props.items.length}
72
+ when={props.items?.length}
71
73
  fallback={<Show when={props.emptyText}><AttachmentEmpty>{props.emptyText}</AttachmentEmpty></Show>}
72
74
  >
73
75
  <Attachments variant={variant()}>
74
- <For each={props.items}>
76
+ <For each={props.items ?? []}>
75
77
  {(item) => (
76
78
  <Attachment
77
79
  data={item}
@@ -6,7 +6,7 @@ interface Props extends Record<string, unknown> {
6
6
  src?: string;
7
7
  /** Alt text for the image. Defaults to `fallback`. */
8
8
  alt?: string;
9
- /** Short text shown when there's no image usually initials (e.g. "JD", "AI"). */
9
+ /** Short text shown when there's no image, usually initials (e.g. "JD", "AI"). */
10
10
  fallback?: string;
11
11
  /** Size token: `sm` | `md` (default) | `lg`. */
12
12
  size?: 'sm' | 'md' | 'lg';
@@ -2,9 +2,10 @@ import { Show } from 'solid-js';
2
2
  import { Button } from '../ui/button';
3
3
  import { renderIcon } from '../ui/icon';
4
4
  import { defineWebComponent } from './define';
5
+ import { createSlotText } from './slot-text';
5
6
 
6
7
  interface Props extends Record<string, unknown> {
7
- /** Visual style. `default` (filled), `subtle` (muted text, hover tint the
8
+ /** Visual style. `default` (filled), `subtle` (muted text, hover tint, the
8
9
  * toolbar icon look), `ghost` (transparent, hover fill), `outline`, or
9
10
  * `destructive`. Defaults to `default`. */
10
11
  variant?: 'default' | 'subtle' | 'ghost' | 'outline' | 'destructive';
@@ -17,11 +18,16 @@ interface Props extends Record<string, unknown> {
17
18
  /** Trailing icon, after the label (e.g. `"chevron-down"` for a menu affordance). */
18
19
  iconTrailing?: string;
19
20
  /** Accessible name. REQUIRED for icon-only buttons (no visible text); ignored
20
- * when you slot visible text, which already names the button. */
21
+ * when you slot visible text, which already names the button. An `aria-label`
22
+ * on top of visible text REPLACES that name rather than adding to it, so a
23
+ * button reading "Save" that answers to "Submit" is unusable by speech input
24
+ * (WCAG 2.5.3, Label in Name). The visible text wins. An `icon` / `icon-sm`
25
+ * size hides the slot, which makes the button icon-only whatever you slotted,
26
+ * so `label` is what names it there. */
21
27
  label?: string;
22
28
  /** Disable the button (non-interactive, dimmed). */
23
29
  disabled?: boolean;
24
- /** Stretch the button to the full width of its container (a block button)
30
+ /** Stretch the button to the full width of its container (a block button),
25
31
  * e.g. a card CTA or a stacked action. Attribute: `full`. */
26
32
  full?: boolean;
27
33
  /** Justify the button's content: `start`, `center` (default), or `end`.
@@ -71,6 +77,23 @@ defineWebComponent<Props, Events>('kai-button', {
71
77
  // `label` prop still names it for assistive tech).
72
78
  const iconOnly = () => props.size === 'icon' || props.size === 'icon-sm';
73
79
 
80
+ // Slotted text names the button by itself (name-from-contents reaches through
81
+ // the slot), so `label` must NOT be emitted on top of it: aria-label REPLACES
82
+ // the computed name, and a name that disagrees with the visible text locks
83
+ // speech-input users out of the control (WCAG 2.5.3). This is what the `label`
84
+ // doc has always promised, and what `kai-checkpoint` already does.
85
+ //
86
+ // `flatten` so a consumer who wraps <kai-button> in their own component and
87
+ // forwards a <slot> still counts as having visible text. Safe here because the
88
+ // default slot has no fallback content — see createSlotText.
89
+ const slotted = createSlotText({ flatten: true });
90
+
91
+ // The name to emit, or nothing. An `icon`/`icon-sm` size does not render the
92
+ // default slot at all, so whatever was slotted is INVISIBLE and `label` is the
93
+ // only name available — dropping it there would leave icon buttons nameless,
94
+ // which is the worse bug.
95
+ const ariaLabel = () => (!iconOnly() && slotted.hasText() ? undefined : props.label);
96
+
74
97
  // ── Imperative API (instance methods on the host) ──────────────────────────
75
98
  // The real <button> lives in the shadow root, so native focus()/blur()/click()
76
99
  // on the host hit the wrapper, not the control. Forward to the inner button.
@@ -82,7 +105,7 @@ defineWebComponent<Props, Events>('kai-button', {
82
105
  element.shadowRoot?.querySelector('button')?.focus(options),
83
106
  /** Blur the inner `<button>`. */
84
107
  blur: () => element.shadowRoot?.querySelector('button')?.blur(),
85
- /** Programmatically activate the button runs the same path as a user
108
+ /** Programmatically activate the button. Runs the same path as a user
86
109
  * click and fires kai-click. Forwarding to the inner button means
87
110
  * `disabled` is respected automatically. */
88
111
  click: () => element.shadowRoot?.querySelector('button')?.click(),
@@ -104,7 +127,7 @@ defineWebComponent<Props, Events>('kai-button', {
104
127
  full={flag('full') || !iconOnly()}
105
128
  type={props.type ?? 'button'}
106
129
  disabled={flag('disabled')}
107
- aria-label={props.label}
130
+ aria-label={ariaLabel()}
108
131
  onClick={() => dispatch('kai-click')}
109
132
  >
110
133
  {/* Leading icon: a slotted SVG (slot="icon") wins — drop in any icon
@@ -115,7 +138,7 @@ defineWebComponent<Props, Events>('kai-button', {
115
138
  </Show>
116
139
  </slot>
117
140
  <Show when={!iconOnly()}>
118
- <slot />
141
+ <slot ref={slotted.ref} />
119
142
  </Show>
120
143
  <Show when={props.iconTrailing && !iconOnly()}>
121
144
  {renderIcon(props.iconTrailing, { class: 'size-4 shrink-0 opacity-60', imgClass: 'size-4 shrink-0', spanClass: 'inline-flex size-4 shrink-0 items-center justify-center', ariaHidden: true })}
@@ -7,8 +7,8 @@ interface Props extends Record<string, unknown> {
7
7
  * Attribute: `appearance`. */
8
8
  appearance?: CardAppearance;
9
9
  /** `vertical` (default, media on top) | `horizontal` (media at the start) |
10
- * `responsive` (horizontal when the card's container is wide enough, else
11
- * vertical a container query on the card's own width). Attribute: `orientation`. */
10
+ * `responsive` (horizontal when the card's container is wide enough, else vertical,
11
+ * via a container query on the card's own width). Attribute: `orientation`. */
12
12
  orientation?: CardOrientation;
13
13
  /** The card width below which a `responsive` card collapses to vertical and the
14
14
  * footer actions stack. A CSS length; default `28rem`. Attribute: `collapse`. */
@@ -4,12 +4,15 @@
4
4
  // `kai-card` events through an optional `policy`. The raw events keep bubbling past
5
5
  // <kai-cards> (composed) so document-level listeners still work. Unknown types render
6
6
  // the Solid CardFallback inline and emit a contract `error`.
7
- import { For, Show, createEffect, createSignal, on, onCleanup, onMount, type JSX } from 'solid-js';
7
+ import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount, type JSX } from 'solid-js';
8
8
  import { Dynamic } from 'solid-js/web';
9
9
  import { defineWebComponent } from './define';
10
10
  import type { CardEnvelope, CardEvent, CardPolicy, CardResolution } from '../primitives/card-contract';
11
11
  import { CARD_EVENT_NAME, emitCardEvent, routeCardEvent } from '../primitives/card-routing';
12
- import { mergeCardTags } from '../primitives/card-registry';
12
+ import { BUILTIN_CARD_TAGS, mergeCardTags } from '../primitives/card-registry';
13
+ import type { JsonSchema } from '../primitives/card-validate';
14
+ import { cardValidationMessage, validateCardData, type CardValidationReport } from '../primitives/card-validate-cards';
15
+ import { hasConsumerSchema } from '../components/card-renderer';
13
16
  import { CardFallback } from '../components/card-fallback';
14
17
  // Register the built-in child card elements so that importing <kai-cards> is self-contained.
15
18
  import './form';
@@ -26,18 +29,45 @@ interface Props extends Record<string, unknown> {
26
29
  * Typed as a plain string map (not the `CardTagMap` alias) so the generated React
27
30
  * wrapper inlines it instead of emitting an unresolved named type. */
28
31
  types?: Record<string, string>;
32
+ /** JSON Schemas for the card types this app renders, keyed by envelope type. The
33
+ * companion of `types`, which says what DRAWS a card while this says what a
34
+ * VALID one looks like. An OBJECT, so it is a JS property only: `el.schemas = {
35
+ * 'pricing-table': pricingSchema }`, never an attribute.
36
+ * `createCardRegistry(...).validationSchemas` is exactly this shape.
37
+ *
38
+ * Without it the kit validates its own seven built-ins and leaves your own card
39
+ * type, the one your app actually cares about, as the only unchecked thing on
40
+ * screen. A schema here WINS over a built-in of the same name, matching
41
+ * `mergeCardTags`, where your entry is spread over ours.
42
+ *
43
+ * Typed `Record<string, object>` rather than `Record<string, JsonSchema>`
44
+ * deliberately: an imported `.json` schema widens `"type"` to `string`, and an
45
+ * authored one carries `$schema`/`title`/`description`/`additionalProperties`,
46
+ * so the tighter type would reject both of the normal ways to supply one. See
47
+ * `CardSchemaMap` in components/card-renderer.tsx. */
48
+ schemas?: Record<string, object>;
29
49
  /** Optional CardPolicy handling child events. Property: `el.policy`. */
30
50
  policy?: CardPolicy;
51
+ /** Validate each envelope's `data` against the schema for its type before
52
+ * rendering it, using a built-in's own schema or yours from `schemas`. Default
53
+ * `true`; set `validate-cards="false"` (or `el.validateCards = false`) to opt
54
+ * out. A hard failure (wrong type, a missing required field) renders a
55
+ * diagnostic naming the field instead of the card; a soft failure (bounds)
56
+ * renders the card unchanged. Both emit a contract `error` event. On in
57
+ * production too: a model emitting a bad shape is a production failure mode,
58
+ * so stripping the check there would hide it from exactly the person who needs
59
+ * to see it. */
60
+ validateCards?: boolean;
31
61
  }
32
62
 
33
63
  /** Events fired by `<kai-cards>`. */
34
64
  interface Events {
35
- /** A child card transitioned to a resolved/deferred state (an action was chosen,
36
- * a form/tasks submission landed, or it was dismissed) — re-emitted off the host
37
- * as a non-bubbling convenience event so a consumer can observe resolution
38
- * centrally without diffing the cards array. `detail` = `{ cardId, resolution }`.
39
- * (A `reopen` un-resolves a card and has no `CardResolution`, so it does NOT fire
40
- * this observe reopen via the underlying bubbling `kai-card` event.) */
65
+ /** A child card transitioned to a resolved/deferred state (an action was chosen, a
66
+ * form/tasks submission landed, or it was dismissed). Re-emitted off the host as
67
+ * a non-bubbling convenience event so a consumer can observe resolution centrally
68
+ * without diffing the cards array. `detail` = `{ cardId, resolution }`. (A
69
+ * `reopen` un-resolves a card and has no `CardResolution`, so it does NOT fire
70
+ * this; observe reopen via the underlying bubbling `kai-card` event.) */
41
71
  'kai-card-resolved': { cardId: string; resolution: CardResolution };
42
72
  }
43
73
 
@@ -57,9 +87,47 @@ function resolutionFromEvent(e: CardEvent): CardResolution | undefined {
57
87
  }
58
88
  }
59
89
 
60
- /** A single resolved child: a known kai-* tag (props set imperatively) or the fallback. */
61
- function CardSlot(props: { envelope: CardEnvelope; tag?: string; theme: string; emit: (e: CardEvent) => void }): JSX.Element {
90
+ /** A single resolved child: a known kai-* tag (props set imperatively), a diagnostic
91
+ * for data that cannot render, or the unknown-type fallback. */
92
+ function CardSlot(props: {
93
+ envelope: CardEnvelope;
94
+ tag?: string;
95
+ theme: string;
96
+ validate: boolean;
97
+ schemas?: Record<string, object>;
98
+ emit: (e: CardEvent) => void;
99
+ }): JSX.Element {
62
100
  let ref: HTMLElement | undefined;
101
+
102
+ // MIRRORS src/remote/provider-runtime.ts:139-147, the same way
103
+ // components/card-renderer.tsx does. The remote (iframe) transport has validated
104
+ // every incoming envelope since the contract landed and this native one did not,
105
+ // so the same bad payload was caught in one transport and rendered silently in the
106
+ // other. Two tiers: `hard` (nothing to render) replaces the card with a diagnostic
107
+ // naming the field, `soft` (bounds) renders the card untouched. Both emit `error`.
108
+ //
109
+ // OUR schema applies only to OUR element: `types` lets a consumer point a built-in
110
+ // card type at their own element (`el.types = { confirm: 'my-confirm-el' }`), and
111
+ // our schema describes our element's data, not theirs.
112
+ //
113
+ // A schema the CONSUMER registered through `el.schemas` runs the other way: they
114
+ // wrote it about their own card, and it is the shape their model was told to
115
+ // emit, so it applies whichever element draws the type. That is what validates a
116
+ // `pricing-table` — nobody's built-in, and until this prop existed the one card in
117
+ // the app that nothing checked. Same rule as components/card-renderer.tsx, which
118
+ // compares component identity where this compares tag identity; `hasConsumerSchema`
119
+ // is shared with it so the gate is spelled once.
120
+ const report = createMemo<CardValidationReport | null>(() => {
121
+ if (!props.validate) return null;
122
+ if (!hasConsumerSchema(props.schemas, props.envelope.type) && props.tag !== BUILTIN_CARD_TAGS[props.envelope.type]) {
123
+ return null;
124
+ }
125
+ // `schemas` is passed even when the gate opened on tag identity, so a consumer
126
+ // schema for a built-in type wins over ours — `schemaFor`'s rule.
127
+ return validateCardData(props.envelope.type, props.envelope.data, props.schemas as Record<string, JsonSchema> | undefined);
128
+ });
129
+ const invalid = () => report()?.tier === 'hard';
130
+
63
131
  // Set object/string props as DOM properties on the custom element (reactive).
64
132
  createEffect(() => {
65
133
  if (!ref) return;
@@ -79,19 +147,44 @@ function CardSlot(props: { envelope: CardEnvelope; tag?: string; theme: string;
79
147
  props.emit({ kind: 'error', cardId: props.envelope.id, message: `Unsupported card type: ${props.envelope.type}` });
80
148
  }
81
149
  });
150
+ // One `error` per distinct problem, both tiers. Keyed on the message, not on
151
+ // mount: a consumer re-assigning `el.cards` per stream chunk hands this a new
152
+ // envelope object each time, so an identity-triggered emit would fire per chunk.
153
+ let lastEmitted: string | null = null;
154
+ createEffect(() => {
155
+ const r = report();
156
+ if (!props.tag) return; // an unknown TYPE already reported itself in onMount
157
+ if (!r || r.ok) {
158
+ lastEmitted = null;
159
+ return;
160
+ }
161
+ const message = cardValidationMessage(r);
162
+ if (message === lastEmitted) return;
163
+ lastEmitted = message;
164
+ props.emit({ kind: 'error', cardId: props.envelope.id, message });
165
+ });
166
+
82
167
  return (
83
168
  <Show
84
169
  when={props.tag}
85
170
  fallback={<CardFallback type={props.envelope.type} cardId={props.envelope.id} />}
86
171
  >
87
- {(tag) => <Dynamic component={tag()} ref={ref} />}
172
+ {(tag) => (
173
+ <Show
174
+ when={invalid()}
175
+ // SOFT and valid both take this branch: the card renders as it does today.
176
+ fallback={<Dynamic component={tag()} ref={ref} />}
177
+ >
178
+ <CardFallback type={props.envelope.type} cardId={props.envelope.id} reason={report()!.summary} />
179
+ </Show>
180
+ )}
88
181
  </Show>
89
182
  );
90
183
  }
91
184
 
92
185
  defineWebComponent<Props, Events>(
93
186
  'kai-cards',
94
- { cards: undefined, types: undefined, policy: undefined },
187
+ { cards: undefined, types: undefined, schemas: undefined, policy: undefined, validateCards: true },
95
188
  (props, { element, dispatch, expose }) => {
96
189
  // Local working copy of the card list. The `cards` PROP still drives rendering
97
190
  // (a new prop array re-seeds this), but holding a settable copy lets the
@@ -134,11 +227,11 @@ defineWebComponent<Props, Events>(
134
227
  );
135
228
  expose({
136
229
  /** Programmatically resolve a child card by id: set that envelope's
137
- * `resolution` so the child re-renders into its read-only/resolved view — the
230
+ * `resolution` so the child re-renders into its read-only/resolved view. The
138
231
  * imperative twin of the consumer mutating the cards array. No-op for an
139
232
  * unknown id. */
140
233
  resolve: (cardId: string, resolution: CardResolution) => resolveCard(cardId, resolution),
141
- /** Collapse a card to its re-openable stub from the host side convenience for
234
+ /** Collapse a card to its re-openable stub from the host side. Convenience for
142
235
  * `resolve(cardId, { kind: 'dismissed' })`. */
143
236
  dismiss: (cardId: string) => resolveCard(cardId, { kind: 'dismissed' }),
144
237
  /** Return the live child element node for a card id (or null) so consumers can
@@ -161,6 +254,8 @@ defineWebComponent<Props, Events>(
161
254
  envelope={env}
162
255
  tag={tags()[env.type]}
163
256
  theme={theme()}
257
+ validate={props.validateCards !== false}
258
+ schemas={props.schemas}
164
259
  emit={(e) => emitCardEvent(element, e)}
165
260
  />
166
261
  )}
@@ -19,23 +19,25 @@ type Step = ChainOfThoughtStepData;
19
19
 
20
20
  interface Props extends Record<string, unknown> {
21
21
  /** The reasoning steps. Set as a JS property. Compound sub-parts collapse to
22
- * this one data model (Route 1). Each `{ label, content?, id? }`. */
23
- steps: Step[];
24
- /** Open mode: `'multiple'` (default — any number of steps open at once) or
22
+ * this one data model (Route 1). Each `{ label, content?, id? }`. Omit to
23
+ * supply the steps as `<kai-step>` light-DOM children instead; when both are
24
+ * present the property's steps come first. */
25
+ steps?: Step[];
26
+ /** Open mode: `'multiple'` (default, any number of steps open at once) or
25
27
  * `'single'` (at most one open; opening a step closes the others). */
26
28
  type?: ChainOfThoughtType;
27
29
  /** Controlled open step key(s). When set, it WINS over user interaction (the
28
30
  * consumer owns the open set). String in `single` mode, string[] in
29
31
  * `multiple` mode. Set as a JS property. */
30
32
  value?: string | string[];
31
- /** Uncontrolled INITIAL open step key(s) seeds which steps render expanded.
32
- * Ignored once `value` is provided. Set as a JS property. */
33
+ /** Uncontrolled INITIAL open step key(s), seeding which steps render
34
+ * expanded. Ignored once `value` is provided. Set as a JS property. */
33
35
  defaultValue?: string | string[];
34
36
  }
35
37
 
36
38
  /** Events fired by `<kai-chain-of-thought>`. */
37
39
  interface Events {
38
- /** The open set changed by user click OR an expand()/collapse()/toggle()
40
+ /** The open set changed, by user click OR an expand()/collapse()/toggle()
39
41
  * call. `value` is a string in `single` mode, a string[] in `multiple` mode.
40
42
  * (Maps Radix Accordion's onValueChange.) */
41
43
  'kai-value-change': { value: string | string[] };
@@ -99,7 +101,7 @@ defineWebComponent<Props, Events>('kai-chain-of-thought', {
99
101
  });
100
102
 
101
103
  // Prop steps first; declarative children appended after.
102
- const allSteps = () => [...props.steps, ...slottedSteps()];
104
+ const allSteps = () => [...(props.steps ?? []), ...slottedSteps()];
103
105
 
104
106
  // ── Imperative API (instance methods on the host) ──────────────────────────
105
107
  // Pattern C: the Accordion component owns the open-set; the facade captures its
@@ -109,10 +111,12 @@ defineWebComponent<Props, Events>('kai-chain-of-thought', {
109
111
  // the only props are steps/type/value/defaultValue.
110
112
  let controller: ChainOfThoughtController | undefined;
111
113
  expose({
112
- /** Open one step's detail by index, or with no arg — ALL steps. In `single`
113
- * mode opening one step closes the others (expand-all keeps the last). */
114
+ /** Open one step's detail by index, or ALL steps when called with no arg. In
115
+ * `single` mode opening one step closes the others (expand-all keeps the
116
+ * last). */
114
117
  expand: (index?: number) => controller?.expand(index),
115
- /** Close one step's detail by index, or with no arg ALL steps. */
118
+ /** Close one step's detail by index, or ALL steps when called with no
119
+ * arg. */
116
120
  collapse: (index?: number) => controller?.collapse(index),
117
121
  /** Flip one step's open state by index. */
118
122
  toggle: (index?: number) => controller?.toggle(index),
@@ -5,10 +5,12 @@ import type { DropdownController } from '../ui/dropdown';
5
5
  import type { SearchFilters } from '../types';
6
6
 
7
7
  interface Props extends Record<string, unknown> {
8
- /** Authors to offer as scope filters. Set as a JS property. */
9
- availableAuthors: string[];
10
- /** Tags to offer as scope filters. Set as a JS property. */
11
- availableTags: string[];
8
+ /** Authors to offer as scope filters. Omit to drop the Authors section (for a
9
+ * tag-only picker). Set as a JS property. */
10
+ availableAuthors?: string[];
11
+ /** Tags to offer as scope filters. Omit to drop the Tags section (for an
12
+ * author-only picker). Set as a JS property. */
13
+ availableTags?: string[];
12
14
  /** The label shown on the trigger for the active scope. */
13
15
  currentLabel?: string;
14
16
  /** Drive/observe the dropdown's open state (Shoelace-style: settable + reflected
@@ -17,7 +19,7 @@ interface Props extends Record<string, unknown> {
17
19
  open?: boolean;
18
20
  /** Initial open state on mount (uncontrolled seed). */
19
21
  defaultOpen?: boolean;
20
- /** Disable the trigger click/keyboard and `show()` no longer open the dropdown. */
22
+ /** Disable the trigger: click/keyboard and `show()` no longer open the dropdown. */
21
23
  disabled?: boolean;
22
24
  }
23
25
 
@@ -52,8 +54,8 @@ defineWebComponent<Props, Events>('kai-scope-picker', {
52
54
  return (
53
55
  <ChatScopePicker
54
56
  currentLabel={props.currentLabel ?? 'All Content'}
55
- availableAuthors={props.availableAuthors}
56
- availableTags={props.availableTags}
57
+ availableAuthors={props.availableAuthors ?? []}
58
+ availableTags={props.availableTags ?? []}
57
59
  onScopeChange={(filters) => dispatch('kai-scope-change', { filters })}
58
60
  defaultOpen={flag('defaultOpen')}
59
61
  disabled={flag('disabled')}
@@ -1,5 +1,11 @@
1
- import type { ToolPart } from '../components/tool-types';
1
+ import type { ToolPart, RawOrigin } from '../components/tool-types';
2
2
  import type { AttachmentData } from '../components/attachment-types';
3
+ import type { CardEnvelope } from '../primitives/card-contract';
4
+
5
+ /** Re-exported so consumers of chat-types (and MessagePart) can keep using
6
+ * RawOrigin unqualified. It is defined in components/tool-types.ts, not here,
7
+ * to keep the components -> elements import direction one-way. */
8
+ export type { RawOrigin };
3
9
 
4
10
  /** The five built-in action buttons (each carries its own curated icon + label). */
5
11
  export type ChatMessageAction = 'copy' | 'like' | 'dislike' | 'regenerate' | 'edit';
@@ -31,14 +37,61 @@ export interface AvatarData {
31
37
  alt?: string;
32
38
  }
33
39
 
40
+ /** A citation the model produced. */
41
+ export interface Source {
42
+ id?: string;
43
+ url?: string;
44
+ title?: string;
45
+ snippet?: string;
46
+ /** Citation marker number, when the model numbers its citations. */
47
+ index?: number;
48
+ }
49
+
50
+ /** The public name for the citation carried by a `source` part, and the argument
51
+ * type of `AssistantStream.addSource`. The bare `Source` name is already taken
52
+ * on the public entry by the citation-chip COMPONENT (`./components/source`),
53
+ * so exporting this interface unaliased would be a duplicate identifier and the
54
+ * type would be unnameable from `@kitn.ai/ui`. */
55
+ export type MessageSource = Source;
56
+
57
+ /** One ordered piece of message content. Closed union: extension happens at the CARD
58
+ * layer via the card registry, not by adding variants here. */
59
+ export type MessagePart =
60
+ | { type: 'text'; text: string; raw?: RawOrigin }
61
+ | {
62
+ type: 'reasoning';
63
+ text: string;
64
+ label?: string;
65
+ /** Provider block index. Keeps parallel reasoning blocks distinct. */
66
+ index?: number;
67
+ /** Which provider RESPONSE STREAM `index` was counted in. Anthropic restarts
68
+ * content-block indices at 0 for every message, and a multi-round tool loop
69
+ * folds several of those messages into ONE assistant turn, so `index` alone
70
+ * is not unique within `parts`. Set by the wire adapter, one value per
71
+ * `consumeModelStream` call. See `appendReasoningPart`. */
72
+ streamId?: string;
73
+ /** Load-bearing on the OpenAI wire, informational on the Anthropic one.
74
+ * `toOpenAIMessages({ reasoning: 'include' })` REBUILDS a signed
75
+ * `reasoning_details` entry from `text` plus this, because `raw` after a
76
+ * streamed turn is a textless fragment: only the final frame carries the
77
+ * signature and it has no text, and `appendReasoningPart` resolves `raw`
78
+ * last-write-wins. `toAnthropicMessages` ignores this field and echoes
79
+ * `raw.payload` verbatim — a thinking block rebuilt from text plus
80
+ * signature is a hard 400. */
81
+ signature?: string;
82
+ raw?: RawOrigin;
83
+ }
84
+ | { type: 'tool'; tool: ToolPart; raw?: RawOrigin }
85
+ | { type: 'card'; envelope: CardEnvelope; raw?: RawOrigin }
86
+ | { type: 'source'; source: Source; raw?: RawOrigin }
87
+ | { type: 'file'; attachment: AttachmentData; raw?: RawOrigin };
88
+
34
89
  export interface ChatMessage {
35
90
  id: string;
36
91
  role: 'user' | 'assistant';
37
- content: string;
38
- reasoning?: { text: string; label?: string };
39
- tools?: ToolPart[];
40
- attachments?: AttachmentData[];
41
- /** Action buttons under the message — built-in names and/or custom descriptors. */
92
+ /** The ONLY content channel. Ordered. */
93
+ parts: MessagePart[];
94
+ /** Action buttons under the message. Chrome, not content. */
42
95
  actions?: (ChatMessageAction | CustomAction)[];
43
96
  /** Optional speaker avatar shown to the left of the message column. */
44
97
  avatar?: AvatarData;
@@ -5,6 +5,8 @@ import { readSlots, WORKSPACE_SLOTS } from './slots';
5
5
  import { ChatThread, type ChatThreadContextUsage, type ChatThreadController } from '../components/chat-thread';
6
6
  import { ConversationList, CollapsedRail } from '../components/conversation-list';
7
7
  import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '../ui/resizable';
8
+ import { cardComponentsFromTags } from './message';
9
+ import { createMessagesGuard } from './validate-messages';
8
10
  import type { AttachmentData } from '../components/attachments';
9
11
  import type { TriggerDef } from '../components/composer';
10
12
  import type { ChatMessage } from './chat-types';
@@ -12,14 +14,25 @@ import type { ProseSize } from '../primitives/chat-config';
12
14
  import type { ModelOption, ConversationGroup, ConversationSummary } from '../types';
13
15
 
14
16
  interface Props extends Record<string, unknown> {
15
- /** Pre-bucketed conversation groups for the sidebar. Set as a JS property. */
16
- groups: ConversationGroup[];
17
- /** Flat conversation list (auto-bucketed if `groups` is empty). Set as a JS property. */
18
- conversations: ConversationSummary[];
17
+ /** The sidebar's section headers, rendered in array order. A group carries no
18
+ * conversations of its own; it is matched against `conversations` by id, so
19
+ * the two props are complementary rather than alternatives. Omit for an
20
+ * ungrouped sidebar. Set as a JS property. */
21
+ groups?: ConversationGroup[];
22
+ /** Every conversation in the sidebar, flat. Each one is filed under the group
23
+ * whose `id` equals its `groupId`; one with no `groupId`, or with a `groupId`
24
+ * matching no entry in `groups`, falls into a trailing ungrouped section
25
+ * (headerless when `compact`), so nothing you pass in is ever dropped. There is
26
+ * no recency bucketing. Set as a JS property. Omit for an empty sidebar, or
27
+ * when `no-conversations` replaces the built-in list with your own
28
+ * `sidebar-header` content. */
29
+ conversations?: ConversationSummary[];
19
30
  /** Id of the open conversation, highlighted in the sidebar. */
20
31
  activeId?: string;
21
- /** The active conversation's message thread, newest last. Set as a JS property. */
22
- messages: ChatMessage[];
32
+ /** The active conversation's message thread, newest last. Set as a JS property
33
+ * (`el.messages = [...]`); a NEW array reference per streaming chunk
34
+ * re-renders (mutating in place does not). Omit for an empty thread. */
35
+ messages?: ChatMessage[];
23
36
  value?: string;
24
37
  placeholder?: string;
25
38
  loading?: boolean;
@@ -63,6 +76,26 @@ interface Props extends Record<string, unknown> {
63
76
  * whole rail flex region (for apps that supply their own rail nav). Default
64
77
  * false. Attribute: `no-conversations`. */
65
78
  noConversations?: boolean;
79
+ /** Optional card type -> custom-element tag overrides/additions for `card`
80
+ * parts (merged over the built-ins). Property: `el.cardTypes`. Typed as a
81
+ * plain string map (not the `CardTagMap` alias) so the generated React
82
+ * wrapper inlines it instead of emitting an unresolved named type. */
83
+ cardTypes?: Record<string, string>;
84
+ /** JSON Schemas for the card types this app renders, keyed by envelope type. The
85
+ * companion of `cardTypes`, which says what DRAWS a card while this says what a
86
+ * VALID one looks like. An OBJECT, so it is a JS property only: `el.cardSchemas
87
+ * = { 'pricing-table': pricingSchema }`, never an attribute.
88
+ * `createCardRegistry(...).validationSchemas` is exactly this shape.
89
+ *
90
+ * Without it the kit validates its own seven built-ins and leaves your own card
91
+ * type, the one your app actually cares about, as the only unchecked thing on
92
+ * screen. A schema here WINS over a built-in of the same name.
93
+ *
94
+ * Typed `Record<string, object>` rather than `Record<string, JsonSchema>`
95
+ * deliberately: an imported `.json` schema widens `"type"` to `string`, and an
96
+ * authored one carries `$schema`/`title`/`description`/`additionalProperties`,
97
+ * so the tighter type would reject both of the normal ways to supply one. */
98
+ cardSchemas?: Record<string, object>;
66
99
  }
67
100
 
68
101
  interface Events {
@@ -99,8 +132,14 @@ defineWebComponent<Props, Events>('kai-workspace', {
99
132
  search: false, voice: false, triggers: undefined, kindIcons: undefined,
100
133
  sidebarWidth: 26, sidebarMinWidth: 240, sidebarMaxWidth: 420,
101
134
  sidebarCollapsed: undefined, defaultSidebarCollapsed: undefined, collapseBelow: undefined, compact: undefined,
102
- noConversations: undefined,
135
+ noConversations: undefined, cardTypes: undefined, cardSchemas: undefined,
103
136
  }, (props, { dispatch, flag, expose, element }) => {
137
+ // `messages` is an untyped boundary: a consumer can hand it anything at
138
+ // runtime (a pre-0.20.0 `{ id, role, content }` array, in particular). Skip
139
+ // the invalid entries rather than let `groupMessageParts` throw deep inside a
140
+ // render pass, which would blank the whole workspace instead of one message.
141
+ const validMessages = createMessagesGuard('kai-workspace');
142
+
104
143
  // Which injection slots the consumer has filled. A bare <slot> is always a
105
144
  // truthy JSX node, so we render each region wrapper ONLY when readSlots reports
106
145
  // projected light-DOM content (re-read on childList mutation).
@@ -180,7 +219,7 @@ defineWebComponent<Props, Events>('kai-workspace', {
180
219
  // own state (e.g. an uncontrolled draft) survives the collapse/expand.
181
220
  const threadEl = (
182
221
  <ChatThread
183
- messages={props.messages} value={props.value as string | undefined} placeholder={props.placeholder as string}
222
+ messages={validMessages(props.messages)} value={props.value as string | undefined} placeholder={props.placeholder as string}
184
223
  loading={flag('loading')} suggestions={props.suggestions as string[] | undefined}
185
224
  suggestionMode={props.suggestionMode as 'submit' | 'fill'} proseSize={props.proseSize as ProseSize}
186
225
  codeTheme={props.codeTheme as string} codeHighlight={flag('codeHighlight')}
@@ -189,6 +228,8 @@ defineWebComponent<Props, Events>('kai-workspace', {
189
228
  scrollButton={props.scrollButton !== false} search={flag('search')} voice={flag('voice')}
190
229
  triggers={props.triggers as TriggerDef[] | undefined}
191
230
  kindIcons={props.kindIcons as Record<string, string> | undefined}
231
+ cardTypes={cardComponentsFromTags(props.cardTypes as Record<string, string> | undefined, (props as { theme?: string }).theme)}
232
+ cardSchemas={props.cardSchemas as Record<string, object> | undefined}
192
233
  onValueChange={(value) => dispatch('kai-value-change', { value })}
193
234
  onSubmit={(detail) => dispatch('kai-submit', detail)}
194
235
  onSuggestionClick={(value) => dispatch('kai-suggestion-click', { value })}
@@ -253,7 +294,7 @@ defineWebComponent<Props, Events>('kai-workspace', {
253
294
  <div class="min-h-0 flex-1">
254
295
  <ConversationList
255
296
  class="bg-transparent"
256
- groups={props.groups} conversations={props.conversations} activeId={props.activeId as string | undefined}
297
+ groups={props.groups ?? []} conversations={props.conversations ?? []} activeId={props.activeId as string | undefined}
257
298
  compact={flag('compact')}
258
299
  onSelect={(id) => dispatch('kai-conversation-select', { id })}
259
300
  onNewChat={() => dispatch('kai-new-chat', {})}