@kitn.ai/ui 0.20.1 → 0.21.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 (500) 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 +68 -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-D3Y5qiij.js +338 -0
  50. package/dist/create-tween-DGKY1FDY.js +341 -0
  51. package/dist/create-tween-paho5Rh-.js +7 -0
  52. package/dist/custom-elements.json +1681 -454
  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-C-2LvGDG.js} +1 -1
  70. package/dist/elements/chunks/{action-icons-ns1Fnedj.js → action-icons-CaYoImEk.js} +1 -1
  71. package/dist/elements/chunks/arrow-left-Bb46H7w9.js +1 -0
  72. package/dist/elements/chunks/artifact-BBIscOxJ.js +1 -0
  73. package/dist/elements/chunks/{attachments-mCB5F1Fq.js → attachments-BB56zoji.js} +1 -1
  74. package/dist/elements/chunks/audio-visualizer-BxOx4THz.js +1 -0
  75. package/dist/elements/chunks/{badge-B4Zuajl2.js → badge-SxLSZ19R.js} +1 -1
  76. package/dist/elements/chunks/{button-ZWNif3--.js → button-CmkUkz6c.js} +1 -1
  77. package/dist/elements/chunks/card-renderer-Db4ffkWv.js +1 -0
  78. package/dist/elements/chunks/card-tags-D8lZ-C_U.js +1 -0
  79. package/dist/elements/chunks/chat-thread-B3Ks-cKr.js +1 -0
  80. package/dist/elements/chunks/check-C8desc_h.js +1 -0
  81. package/dist/elements/chunks/chevron-down-DKtc2HNB.js +1 -0
  82. package/dist/elements/chunks/chevron-right-Dw4GPhwL.js +1 -0
  83. package/dist/elements/chunks/choice-card-Q9cYYDyM.js +1 -0
  84. package/dist/elements/chunks/circle-BeokBsvB.js +1 -0
  85. package/dist/elements/chunks/circle-check-wJSqt859.js +1 -0
  86. package/dist/elements/chunks/{circle-x-B7faJFlY.js → circle-x-D0MEfxOM.js} +1 -1
  87. package/dist/elements/chunks/code-block-C8VzAraE.js +1 -0
  88. package/dist/elements/chunks/{collapsible-DM1Ckd4e.js → collapsible-Cvj61obE.js} +1 -1
  89. package/dist/elements/chunks/{composer-BguS0QL_.js → composer-D86ztyL0.js} +2 -2
  90. package/dist/elements/chunks/confirm-card-BdyDAGLw.js +1 -0
  91. package/dist/elements/chunks/context-WHDl-xSx.js +1 -0
  92. package/dist/elements/chunks/conversation-list-CdR14_Y4.js +1 -0
  93. package/dist/elements/chunks/create-tween-C_A_B638.js +7 -0
  94. package/dist/elements/chunks/{default-input-Cqqilg9t.js → default-input-CSZqUM81.js} +1 -1
  95. package/dist/elements/chunks/define-BzPnNsXg.js +1 -0
  96. package/dist/elements/chunks/{disclosure-CIZ5c8nO.js → disclosure-DVKsoc1F.js} +1 -1
  97. package/dist/elements/chunks/{download-Dsbg3ste.js → download-BjA54IMJ.js} +1 -1
  98. package/dist/elements/chunks/{dropdown-BDd1-Zgi.js → dropdown-BjVVHWXH.js} +1 -1
  99. package/dist/elements/chunks/{ellipsis-Dw6qrss-.js → ellipsis-BNedmqfi.js} +1 -1
  100. package/dist/elements/chunks/embed-DQsl4XEq.js +1 -0
  101. package/dist/elements/chunks/{external-link-Vt74zAoL.js → external-link-Di-dw2CU.js} +1 -1
  102. package/dist/elements/chunks/{file-text-CZrZF6_v.js → file-text-B3sAODCV.js} +1 -1
  103. package/dist/elements/chunks/{file-tree-DF4okKyo.js → file-tree-Dgto-bMn.js} +1 -1
  104. package/dist/elements/chunks/{folder-glP5rha4.js → folder-D6rMadFV.js} +1 -1
  105. package/dist/elements/chunks/form-CILxvhQ7.js +1 -0
  106. package/dist/elements/chunks/hover-card-BtiF4XiU.js +1 -0
  107. package/dist/elements/chunks/{icon-BLb8beoi.js → icon-DKkmze3y.js} +1 -1
  108. package/dist/elements/chunks/{info-CIsv92JK.js → info-CWRjZkuy.js} +1 -1
  109. package/dist/elements/chunks/{input-Dds1kFyt.js → input--QJBK6BA.js} +1 -1
  110. package/dist/elements/chunks/{kbd-De_xbgi9.js → kbd-C3zuKh8R.js} +1 -1
  111. package/dist/elements/chunks/{link-i4r_k7z4.js → link-BL5JA7Q0.js} +1 -1
  112. package/dist/elements/chunks/link-preview-CU37bbXh.js +1 -0
  113. package/dist/elements/chunks/{loader-BmQH4s3V.js → loader-XmpyxyRH.js} +1 -1
  114. package/dist/elements/chunks/{markdown-Zi21WhUz.js → markdown-3Gv5UZ_o.js} +1 -1
  115. package/dist/elements/chunks/message-CnBA2OUt.js +1 -0
  116. package/dist/elements/chunks/message-X5NZbq-W.js +1 -0
  117. package/dist/elements/chunks/message-square-CnpAm_P0.js +1 -0
  118. package/dist/elements/chunks/{minimize-2--ifeCGrl.js → minimize-2-DwyrUTcM.js} +1 -1
  119. package/dist/elements/chunks/{model-switcher-B94YAKsM.js → model-switcher-CclaJEHt.js} +1 -1
  120. package/dist/elements/chunks/{overlay-kD8dwRv_.js → overlay-Cepd-KcS.js} +1 -1
  121. package/dist/elements/chunks/{paperclip-q21dYOSK.js → paperclip-CdwKjrlu.js} +1 -1
  122. package/dist/elements/chunks/{progress-bar-BrAIIaaJ.js → progress-bar-DMVXBD3b.js} +1 -1
  123. package/dist/elements/chunks/{prompt-suggestion-Bjn2K-fR.js → prompt-suggestion-Di_WZE_5.js} +1 -1
  124. package/dist/elements/chunks/reasoning-D0jMch5u.js +1 -0
  125. package/dist/elements/chunks/{resizable-Dp4JExql.js → resizable-1znbL1Gv.js} +1 -1
  126. package/dist/elements/chunks/{rotate-cw-CFWWkpj2.js → rotate-cw-DVhhgx8n.js} +1 -1
  127. package/dist/elements/chunks/{scroll-area-Bb0kHH1j.js → scroll-area-Bs4bFS1P.js} +1 -1
  128. package/dist/elements/chunks/scroll-button-Bet5NLc4.js +1 -0
  129. package/dist/elements/chunks/{separator-9kRgr1X9.js → separator-C5Gh8qGC.js} +1 -1
  130. package/dist/elements/chunks/{settings-B8lmec4D.js → settings-5vmEFg0K.js} +1 -1
  131. package/dist/elements/chunks/{settings-group-I0hEuXiN.js → settings-group-BWLB38kz.js} +1 -1
  132. package/dist/elements/chunks/{share-C3lTiYGt.js → share-BZ3G_DaB.js} +1 -1
  133. package/dist/elements/chunks/skeleton-DafPJQTp.js +1 -0
  134. package/dist/elements/chunks/slots-CIw9RlAe.js +1 -0
  135. package/dist/elements/chunks/source-D_qj_6uJ.js +1 -0
  136. package/dist/elements/chunks/{star-CSJtGxgv.js → star-Cuv7j2IE.js} +1 -1
  137. package/dist/elements/chunks/{store-CHyW95TP.js → store-C1StPHfW.js} +1 -1
  138. package/dist/elements/chunks/tasks-card-EyoBZfpA.js +1 -0
  139. package/dist/elements/chunks/{text-shimmer-Cn9TmNpI.js → text-shimmer-D9gRSh34.js} +1 -1
  140. package/dist/elements/chunks/textarea-DuhtHfcm.js +1 -0
  141. package/dist/elements/chunks/{thumbs-up-PNVVThEF.js → thumbs-up-B9hwv43w.js} +1 -1
  142. package/dist/elements/chunks/{toast-store-DKbQn3Wn.js → toast-store-Bm3DUEDe.js} +1 -1
  143. package/dist/elements/chunks/{tool-DjTRpIpl.js → tool-CDtW7L0z.js} +1 -1
  144. package/dist/elements/chunks/tooltip-eB226mGK.js +1 -0
  145. package/dist/elements/chunks/{triangle-alert-DsHTHCPm.js → triangle-alert-C1SNzTqK.js} +1 -1
  146. package/dist/elements/chunks/use-card-resolution-YZh2MvI5.js +1 -0
  147. package/dist/elements/chunks/{variant-aurora-Dc4nQUia.js → variant-aurora-Cj1F7D0l.js} +2 -2
  148. package/dist/elements/chunks/{variant-custom-y_3kVQhS.js → variant-custom-T9gpt7QE.js} +1 -1
  149. package/dist/elements/chunks/{variant-wave-D5OR1-9Z.js → variant-wave-CxU9z-TE.js} +2 -2
  150. package/dist/elements/chunks/{video-B7vmenoC.js → video-CK4R7Rus.js} +1 -1
  151. package/dist/elements/chunks/x-D1qx0aLd.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 +7 -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 +2 -2
  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/slots.d.ts +16 -0
  208. package/dist/elements/source.js +1 -1
  209. package/dist/elements/status.js +1 -1
  210. package/dist/elements/switch.js +1 -1
  211. package/dist/elements/tabs.js +1 -1
  212. package/dist/elements/tasks.js +1 -1
  213. package/dist/elements/text-shimmer.js +1 -1
  214. package/dist/elements/thinking-bar.js +1 -1
  215. package/dist/elements/thread.js +1 -1
  216. package/dist/elements/toast.js +1 -1
  217. package/dist/elements/tool.js +1 -1
  218. package/dist/elements/tooltip.js +1 -1
  219. package/dist/elements/validate-messages.d.ts +25 -0
  220. package/dist/elements/voice-input.js +1 -1
  221. package/dist/elements/voice-output.js +1 -1
  222. package/dist/elements.d.ts +2725 -214
  223. package/dist/index.d.ts +163 -125
  224. package/dist/index.js +6336 -5802
  225. package/dist/index.server.d.ts +5 -0
  226. package/dist/index.server.js +5446 -4937
  227. package/dist/kai-provider.es.d.ts +5 -0
  228. package/dist/kai-provider.es.js +79 -77
  229. package/dist/kai.es.d.ts +5 -0
  230. package/dist/kai.es.js +1 -1
  231. package/dist/llms/llms-full.txt +762 -148
  232. package/dist/llms/llms.txt +57 -5
  233. package/dist/mcp.es.js +4010 -770
  234. package/dist/primitives/card-component-types.d.ts +11 -0
  235. package/dist/primitives/card-data-types.d.ts +196 -0
  236. package/dist/primitives/card-host.d.ts +1 -1
  237. package/dist/primitives/card-recovery.d.ts +3 -3
  238. package/dist/primitives/card-registry.d.ts +4 -14
  239. package/dist/primitives/card-resolution.d.ts +1 -1
  240. package/dist/primitives/card-routing.d.ts +1 -1
  241. package/dist/primitives/card-tags.d.ts +11 -0
  242. package/dist/primitives/card-validate-cards.d.ts +57 -0
  243. package/dist/primitives/card-validate-generator.testlib.d.ts +20 -0
  244. package/dist/primitives/card-validate-schemas.d.ts +17 -0
  245. package/dist/primitives/card-validate.d.ts +24 -0
  246. package/dist/primitives/create-kai-chat.d.ts +3 -3
  247. package/dist/primitives/embed-providers.d.ts +1 -1
  248. package/dist/primitives/link-preview.d.ts +1 -1
  249. package/dist/primitives/message-feedback.d.ts +2 -2
  250. package/dist/primitives/toast-store.d.ts +1 -1
  251. package/dist/react/index.d.ts +717 -299
  252. package/dist/react/use-kai-chat.d.ts +8 -4
  253. package/dist/react.d.ts +5 -0
  254. package/dist/react.js +417 -316
  255. package/dist/register-impl-bzrZ44ft.js +145 -0
  256. package/dist/remote/host-embed.d.ts +1 -1
  257. package/dist/remote/index.d.ts +2 -2
  258. package/dist/remote/provider-runtime.d.ts +2 -2
  259. package/dist/remote/provider.d.ts +2 -2
  260. package/dist/remote/validate.d.ts +1 -1
  261. package/dist/remote/wire.d.ts +1 -1
  262. package/dist/schemas/artifact.schema.json +92 -0
  263. package/dist/schemas/embed.schema.json +0 -2
  264. package/dist/schemas/from-tool-call.d.ts +48 -0
  265. package/dist/schemas/index.d.ts +48 -0
  266. package/dist/schemas/link.schema.json +0 -2
  267. package/dist/schemas/provider-subsets.d.ts +136 -0
  268. package/dist/schemas/registry.d.ts +140 -0
  269. package/dist/schemas/tool-defs.d.ts +121 -0
  270. package/dist/schemas.d.ts +5 -0
  271. package/dist/schemas.js +717 -0
  272. package/dist/solid-BCJUQqwS.js +22750 -0
  273. package/dist/solid-BdENBcEq.js +20108 -0
  274. package/dist/solid.d.ts +66 -0
  275. package/dist/solid.js +261 -0
  276. package/dist/solid.server.d.ts +5 -0
  277. package/dist/solid.server.js +261 -0
  278. package/dist/state/index.d.ts +14 -4
  279. package/dist/state/messages.d.ts +8 -3
  280. package/dist/state/mock.d.ts +46 -0
  281. package/dist/state/parts.d.ts +85 -0
  282. package/dist/state/stream.d.ts +15 -9
  283. package/dist/state.d.ts +5 -0
  284. package/dist/state.js +253 -84
  285. package/dist/ui/action-icons.d.ts +1 -1
  286. package/dist/ui/collapsible.d.ts +11 -6
  287. package/dist/ui/dropdown.d.ts +47 -42
  288. package/dist/ui/pane-group.d.ts +1 -1
  289. package/dist/variant-aurora-1wMcXRUS.js +381 -0
  290. package/dist/variant-aurora-BKHHiOgL.js +391 -0
  291. package/dist/{variant-aurora-DVtGqVH6.js → variant-aurora-DOb_o4pC.js} +1 -1
  292. package/dist/{variant-custom-Dif-IV4u.js → variant-custom-CJGB0Kbw.js} +1 -1
  293. package/dist/variant-custom-Co0CkXJF.js +200 -0
  294. package/dist/variant-custom-DWSSgmd2.js +170 -0
  295. package/dist/variant-wave-C3-45cL6.js +218 -0
  296. package/dist/{variant-wave-DbfNdvSi.js → variant-wave-CEODq-9q.js} +1 -1
  297. package/dist/variant-wave-jdXBnCUZ.js +208 -0
  298. package/dist/wire/chunk.d.ts +171 -0
  299. package/dist/wire/consume.d.ts +26 -0
  300. package/dist/wire/encode.d.ts +148 -0
  301. package/dist/wire/formats/anthropic.d.ts +2 -0
  302. package/dist/wire/formats/openai.d.ts +5 -0
  303. package/dist/wire/index.d.ts +14 -0
  304. package/dist/wire/read.d.ts +26 -0
  305. package/dist/wire/sink-helpers.d.ts +21 -0
  306. package/dist/wire/sse.d.ts +33 -0
  307. package/dist/wire.d.ts +5 -53
  308. package/dist/wire.js +898 -0
  309. package/frameworks/react/index.tsx +183 -154
  310. package/frameworks/react/runtime.tsx +21 -1
  311. package/frameworks/react/use-kai-chat.tsx +11 -1
  312. package/llms-full.txt +762 -148
  313. package/llms.txt +57 -5
  314. package/package.json +61 -8
  315. package/src/agent-tooling/archetypes.ts +96 -0
  316. package/src/agent-tooling/integrations/anthropic.ts +412 -0
  317. package/src/agent-tooling/integrations/cloudflare.ts +136 -52
  318. package/src/agent-tooling/integrations/langgraph.ts +67 -13
  319. package/src/agent-tooling/integrations/mastra.ts +120 -12
  320. package/src/agent-tooling/integrations/mock.ts +43 -8
  321. package/src/agent-tooling/integrations/ollama.ts +61 -24
  322. package/src/agent-tooling/integrations/openai.ts +87 -0
  323. package/src/agent-tooling/integrations/openrouter.ts +56 -8
  324. package/src/agent-tooling/integrations/pi.ts +79 -23
  325. package/src/agent-tooling/integrations/pydantic-ai.ts +24 -2
  326. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +84 -13
  327. package/src/agent-tooling/mcp/manifest.ts +206 -25
  328. package/src/agent-tooling/mcp/tools/debug.ts +13 -7
  329. package/src/agent-tooling/mcp/tools/reference.ts +285 -6
  330. package/src/agent-tooling/mcp/tools/scaffold.ts +3345 -675
  331. package/src/agent-tooling/registry.ts +17 -0
  332. package/src/agent-tooling/types.ts +229 -1
  333. package/src/components/artifact-card.tsx +120 -0
  334. package/src/components/audio-visualizer/index.tsx +9 -0
  335. package/src/components/audio-visualizer/labs/lab-choreography.ts +187 -0
  336. package/src/components/audio-visualizer/labs/lab-shaders.ts +363 -0
  337. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +102 -0
  338. package/src/components/audio-visualizer/shader-canvas.tsx +31 -1
  339. package/src/components/card-fallback.tsx +42 -10
  340. package/src/components/card-renderer.tsx +146 -9
  341. package/src/components/chat-container.tsx +4 -0
  342. package/src/components/chat-thread.tsx +118 -51
  343. package/src/components/choice-card.tsx +17 -29
  344. package/src/components/composer.tsx +12 -2
  345. package/src/components/confirm-card.tsx +24 -20
  346. package/src/components/context.tsx +12 -4
  347. package/src/components/conversation-list.tsx +18 -2
  348. package/src/components/file-tree.tsx +11 -22
  349. package/src/components/file-upload.tsx +14 -8
  350. package/src/components/form.tsx +14 -47
  351. package/src/components/loader.tsx +39 -12
  352. package/src/components/message-skills.tsx +4 -0
  353. package/src/components/message.tsx +294 -46
  354. package/src/components/reasoning.tsx +92 -5
  355. package/src/components/response-compare-types.ts +25 -0
  356. package/src/components/response-compare.tsx +4 -9
  357. package/src/components/source.tsx +50 -24
  358. package/src/components/tasks-card.tsx +10 -33
  359. package/src/components/thread.tsx +84 -42
  360. package/src/components/toast.tsx +11 -5
  361. package/src/components/tool-classify.ts +26 -0
  362. package/src/components/tool-types.ts +31 -1
  363. package/src/elements/agent-card.tsx +11 -11
  364. package/src/elements/artifact.tsx +15 -7
  365. package/src/elements/attachments.tsx +6 -4
  366. package/src/elements/avatar.tsx +1 -1
  367. package/src/elements/button.tsx +3 -3
  368. package/src/elements/card.tsx +2 -2
  369. package/src/elements/cards.tsx +109 -14
  370. package/src/elements/chain-of-thought.tsx +14 -10
  371. package/src/elements/chat-scope-picker.tsx +9 -7
  372. package/src/elements/chat-types.ts +59 -6
  373. package/src/elements/chat-workspace.tsx +50 -9
  374. package/src/elements/chat.tsx +64 -3
  375. package/src/elements/choice.tsx +8 -8
  376. package/src/elements/coachmark.tsx +1 -1
  377. package/src/elements/command.tsx +8 -23
  378. package/src/elements/compare.tsx +7 -6
  379. package/src/elements/compiled.css +1 -1
  380. package/src/elements/composer.tsx +8 -8
  381. package/src/elements/confirm-card.tsx +9 -9
  382. package/src/elements/context-meter.tsx +4 -18
  383. package/src/elements/conversation-list.tsx +14 -9
  384. package/src/elements/default-input.tsx +3 -3
  385. package/src/elements/define.tsx +178 -43
  386. package/src/elements/editable-label.tsx +17 -2
  387. package/src/elements/element-data-types.ts +112 -0
  388. package/src/elements/element-meta.json +1596 -304
  389. package/src/elements/element-types.d.ts +2636 -211
  390. package/src/elements/feedback-bar.tsx +1 -1
  391. package/src/elements/file-tree.tsx +1 -1
  392. package/src/elements/file-upload.tsx +1 -1
  393. package/src/elements/form.tsx +14 -6
  394. package/src/elements/input.tsx +1 -1
  395. package/src/elements/kbd.tsx +1 -1
  396. package/src/elements/menu.tsx +10 -29
  397. package/src/elements/message-skills.tsx +7 -9
  398. package/src/elements/message.tsx +200 -32
  399. package/src/elements/model-switcher.tsx +5 -3
  400. package/src/elements/pane.tsx +1 -1
  401. package/src/elements/prompt-dock.tsx +8 -8
  402. package/src/elements/prompt-input.tsx +10 -10
  403. package/src/elements/prompt-suggestions.tsx +7 -4
  404. package/src/elements/reasoning.tsx +1 -1
  405. package/src/elements/register.ts +9 -0
  406. package/src/elements/resizable.globals.d.ts +36 -0
  407. package/src/elements/resizable.tsx +24 -13
  408. package/src/elements/response-stream.tsx +1 -1
  409. package/src/elements/scroll-button.tsx +1 -1
  410. package/src/elements/search.tsx +6 -1
  411. package/src/elements/segmented.tsx +5 -11
  412. package/src/elements/separator.tsx +1 -1
  413. package/src/elements/slots.ts +57 -24
  414. package/src/elements/source.tsx +21 -22
  415. package/src/elements/switch.tsx +3 -3
  416. package/src/elements/tasks.tsx +5 -4
  417. package/src/elements/thread.tsx +36 -4
  418. package/src/elements/toast.tsx +3 -2
  419. package/src/elements/tool.tsx +1 -1
  420. package/src/elements/validate-messages.ts +76 -0
  421. package/src/elements/voice-input.tsx +7 -7
  422. package/src/elements/voice-output.tsx +4 -4
  423. package/src/index.ts +141 -6
  424. package/src/primitives/card-component-types.ts +63 -0
  425. package/src/primitives/card-data-types.ts +312 -0
  426. package/src/primitives/card-recovery.ts +13 -2
  427. package/src/primitives/card-registry.tsx +34 -23
  428. package/src/primitives/card-schemas/artifact.schema.json +92 -0
  429. package/src/primitives/card-schemas/embed.schema.json +0 -2
  430. package/src/primitives/card-schemas/link.schema.json +0 -2
  431. package/src/primitives/card-tags.ts +61 -0
  432. package/src/primitives/card-validate-cards.ts +255 -0
  433. package/src/primitives/card-validate-generator.testlib.ts +38 -0
  434. package/src/primitives/card-validate-schemas.ts +58 -0
  435. package/src/primitives/card-validate.ts +48 -20
  436. package/src/primitives/create-tween.ts +54 -2
  437. package/src/primitives/message-feedback.ts +3 -2
  438. package/src/primitives/use-audio-analysis.ts +20 -1
  439. package/src/primitives/use-sequencer.ts +20 -1
  440. package/src/schemas/from-tool-call.ts +151 -0
  441. package/src/schemas/index.ts +340 -0
  442. package/src/schemas/provider-subsets.ts +538 -0
  443. package/src/schemas/registry.ts +355 -0
  444. package/src/schemas/tool-defs.ts +492 -0
  445. package/src/solid.ts +150 -0
  446. package/src/state/index.ts +27 -1
  447. package/src/state/messages.ts +21 -4
  448. package/src/state/mock.ts +211 -0
  449. package/src/state/parts.ts +295 -0
  450. package/src/state/stream.ts +56 -58
  451. package/src/ui/collapsible.tsx +17 -5
  452. package/src/ui/dropdown.tsx +46 -12
  453. package/src/ui/overlay.tsx +10 -4
  454. package/src/wire/chunk.ts +201 -0
  455. package/src/wire/consume.ts +440 -0
  456. package/src/wire/encode.ts +474 -0
  457. package/src/wire/formats/anthropic.ts +267 -0
  458. package/src/wire/formats/openai.ts +187 -0
  459. package/src/wire/index.ts +52 -0
  460. package/src/wire/read.ts +152 -0
  461. package/src/wire/sink-helpers.ts +50 -0
  462. package/src/wire/sse.ts +140 -0
  463. package/dist/create-tween-hvpmSuwW.js +0 -7
  464. package/dist/elements/chunks/arrow-left-DQocUFvy.js +0 -1
  465. package/dist/elements/chunks/audio-visualizer-BqNy7dfm.js +0 -1
  466. package/dist/elements/chunks/card-BkHbp4BA.js +0 -1
  467. package/dist/elements/chunks/chat-thread-CkRN6pZ7.js +0 -1
  468. package/dist/elements/chunks/check-B9-gn6SS.js +0 -1
  469. package/dist/elements/chunks/chevron-down-BNsxc2hk.js +0 -1
  470. package/dist/elements/chunks/chevron-right-Hv00StBO.js +0 -1
  471. package/dist/elements/chunks/circle-0e3KJTDr.js +0 -1
  472. package/dist/elements/chunks/circle-check-BNAS0JEy.js +0 -1
  473. package/dist/elements/chunks/code-block-BFUHn6Va.js +0 -1
  474. package/dist/elements/chunks/context-Cb7k4F-v.js +0 -1
  475. package/dist/elements/chunks/conversation-list-CvoAkWhN.js +0 -1
  476. package/dist/elements/chunks/create-tween-oVvAIG8r.js +0 -7
  477. package/dist/elements/chunks/define-D1din7Ld.js +0 -1
  478. package/dist/elements/chunks/hover-card-Cmm6Q53m.js +0 -1
  479. package/dist/elements/chunks/message-BqyXDN3-.js +0 -1
  480. package/dist/elements/chunks/message-feedback-Djr3z6Yb.js +0 -1
  481. package/dist/elements/chunks/message-square-vFAgbDaX.js +0 -1
  482. package/dist/elements/chunks/reasoning-DTpCGtnY.js +0 -1
  483. package/dist/elements/chunks/scroll-button-B0cH5fXf.js +0 -1
  484. package/dist/elements/chunks/skeleton-DSXW5Yya.js +0 -1
  485. package/dist/elements/chunks/slots--V19HwUb.js +0 -1
  486. package/dist/elements/chunks/textarea-CUwqAX0G.js +0 -1
  487. package/dist/elements/chunks/tooltip-CgBZCVKp.js +0 -1
  488. package/dist/elements/chunks/use-card-resolution-BSu_XHjH.js +0 -1
  489. package/dist/elements/chunks/x-GRUeeQFT.js +0 -1
  490. package/dist/host-embed.d.ts +0 -33
  491. package/dist/kai-provider.d.ts +0 -2
  492. package/dist/origin.d.ts +0 -5
  493. package/dist/provider-runtime.d.ts +0 -27
  494. package/dist/register-impl-DQyejdfb.js +0 -145
  495. package/dist/validate.d.ts +0 -4
  496. package/dist/version.d.ts +0 -4
  497. package/src/components/component-meta.json +0 -4550
  498. package/src/elements/card-media.jpg +0 -0
  499. package/src/elements/resizable.d.ts +0 -27
  500. package/src/elements/video-poster.jpg +0 -0
@@ -7,9 +7,10 @@ const langgraph: Integration = {
7
7
  language: 'ts',
8
8
  streamFormat: 'openai-sse',
9
9
  envVars: ['OPENAI_API_KEY'],
10
- routeTemplates: {
11
- next: `// POST /api/chat stream a compiled LangGraph agent to the browser
12
- import { createReactAgent } from '@langchain/langgraph/prebuilt';
10
+ // No per-framework templates: the handler below is web-standard, so the
11
+ // scaffolder wraps it in the target framework's own route declaration.
12
+ routeTemplates: {},
13
+ webRoute: `import { createReactAgent } from '@langchain/langgraph/prebuilt';
13
14
  import { ChatOpenAI } from '@langchain/openai';
14
15
  import { tool } from '@langchain/core/tools';
15
16
  import { z } from 'zod';
@@ -28,10 +29,35 @@ const agent = createReactAgent({
28
29
  tools: [getWeather],
29
30
  });
30
31
 
31
- export async function POST(req: Request) {
32
- const { messages } = await req.json();
32
+ // Stream a compiled LangGraph agent to the browser as OpenAI-format SSE.
33
+ async function chatHandler(request: Request): Promise<Response> {
34
+ const { messages } = await readChatRequest(request);
33
35
 
34
- const stream = await agent.stream({ messages }, { streamMode: 'messages' });
36
+ // agent.stream() coerces plain {role, content} objects into BaseMessage
37
+ // instances itself, including OpenAI-shaped tool_calls, so the wire messages
38
+ // can be passed through almost as-is. The one incompatible bit is content:
39
+ // OpenAI represents a tool-calls-only assistant turn as content: null, and
40
+ // LangChain's MessageContent type (and runtime coercion) only accepts string.
41
+ const agentMessages = messages.map((m) => ({ ...m, content: m.content ?? '' }));
42
+
43
+ // Let TS infer the stream's element type from this call instead of hand
44
+ // writing it: the real type is a [BaseMessage, metadata] tuple, keyed off the
45
+ // streamMode: 'messages' literal, not the plain object shape it looks like.
46
+ const startStream = () => agent.stream({ messages: agentMessages }, { streamMode: 'messages' });
47
+
48
+ let stream: Awaited<ReturnType<typeof startStream>>;
49
+ try {
50
+ stream = await startStream();
51
+ } catch (err) {
52
+ // A REAL status, before a byte is streamed: a missing OPENAI_API_KEY fails
53
+ // here. Returning 200 would send this JSON out labelled text/event-stream,
54
+ // the SSE reader would find no frame, and the turn would resolve empty with
55
+ // nothing logged and no bubble.
56
+ return new Response(
57
+ JSON.stringify({ error: { message: err instanceof Error ? err.message : 'Agent failed to start' } }),
58
+ { status: 502, headers: { 'Content-Type': 'application/json' } },
59
+ );
60
+ }
35
61
 
36
62
  const encoder = new TextEncoder();
37
63
  const body = new ReadableStream({
@@ -39,22 +65,50 @@ export async function POST(req: Request) {
39
65
  const send = (obj: unknown) =>
40
66
  controller.enqueue(encoder.encode(\`data: \${JSON.stringify(obj)}\\n\\n\`));
41
67
 
42
- for await (const [chunk] of stream) {
43
- if (typeof chunk.content === 'string' && chunk.content) {
44
- send({ choices: [{ delta: { content: chunk.content } }] });
68
+ try {
69
+ for await (const [chunk] of stream) {
70
+ if (typeof chunk.content === 'string' && chunk.content) {
71
+ send({ choices: [{ delta: { content: chunk.content } }] });
72
+ }
45
73
  }
74
+ } catch (err) {
75
+ // The status is spent once the stream started, so report IN BAND:
76
+ // readOpenAIStream lands this on turn.error and keeps what streamed.
77
+ send({ error: { message: err instanceof Error ? err.message : 'Agent stream failed' } });
46
78
  }
47
79
  controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));
48
80
  controller.close();
49
81
  },
50
82
  });
51
83
 
52
- return new Response(body, { headers: { 'Content-Type': 'text/event-stream' } });
84
+ return new Response(body, {
85
+ status: 200,
86
+ headers: {
87
+ 'Content-Type': 'text/event-stream; charset=utf-8',
88
+ // no-transform stops a proxy buffering the stream into one blob.
89
+ 'Cache-Control': 'no-cache, no-transform',
90
+ Connection: 'keep-alive',
91
+ },
92
+ });
53
93
  }`,
54
- },
55
- streamMapping: "Use graph.stream(input, { streamMode: 'messages' }) to get [messageChunk, metadata] tuples. Extract chunk.content (string) and forward as OpenAI-format SSE frames: data: {choices:[{delta:{content}}]}. Close with data: [DONE]. The kai-chat reader handles it.",
56
- runNote: 'Set OPENAI_API_KEY (or the key for your chosen model provider). Install @langchain/langgraph, @langchain/openai, @langchain/core.',
94
+ streamMapping: "Use graph.stream(input, { streamMode: 'messages' }) to get [messageChunk, metadata] tuples. Extract chunk.content (string) and forward as OpenAI-format SSE frames: data: {choices:[{delta:{content}}]}. Close with data: [DONE]. readOpenAIStream from @kitn.ai/ui/wire parses tool calls and reasoning too, but the route template forwards chunk.content only, which is text: the same message chunks carry the tool-call fragments on chunk.tool_call_chunks, so re-frame those onto delta.tool_calls to fill kai-tool.",
95
+ // No install list here: `deps` below is the one, and the scaffolder emits it.
96
+ // This sentence used to end "Install @langchain/langgraph, @langchain/openai,
97
+ // @langchain/core." — three of the four packages, for as long as nobody was
98
+ // comparing it to anything.
99
+ runNote: 'Set OPENAI_API_KEY (or the key for your chosen model provider).',
57
100
  docsSlug: 'integrations/langgraph',
101
+ // Nothing. The agent owns both: ChatOpenAI({ model: 'gpt-4o' }) and the tools
102
+ // array passed to createReactAgent are server-side.
103
+ forwardsFromClient: [],
104
+ // Four, one per import in the route above. `zod` is the one the deleted prose
105
+ // forgot: the tool's `schema:` is a z.object(), so an app installed from that
106
+ // sentence alone fails to build. Deriving this from the imports rather than
107
+ // from a sentence is the point of the field.
108
+ deps: { npm: ['@langchain/langgraph', '@langchain/openai', '@langchain/core', 'zod'], pip: [] },
109
+ // No key appears in the route: `new ChatOpenAI(...)` reads OPENAI_API_KEY from
110
+ // the environment itself. Same invisible-key shape as vercel-ai-sdk.
111
+ keyExposure: 'needs-proxy',
58
112
  };
59
113
 
60
114
  export default langgraph;
@@ -7,21 +7,129 @@ const mastra: Integration = {
7
7
  language: 'ts',
8
8
  streamFormat: 'openai-sse',
9
9
  envVars: ['MASTRA_URL'],
10
- routeTemplates: {
11
- express: `// POST /api/chat your server proxies a Mastra agent to the browser
12
- import { MastraClient } from '@mastra/client-js';
10
+ // No per-framework templates. The old `express` entry was a fragment, not a
11
+ // route: it referenced `messages` and `res` without declaring either, and
12
+ // every non-express framework inherited it anyway. The handler below is
13
+ // web-standard, so the scaffolder wraps it per framework.
14
+ routeTemplates: {},
15
+ webRoute: `import { MastraClient } from '@mastra/client-js';
13
16
 
14
- const mastra = new MastraClient({ baseUrl: process.env.MASTRA_URL });
15
-
16
- const stream = await mastra.getAgent('supportAgent').stream({ messages });
17
- for await (const delta of stream.textStream) {
18
- res.write(\`data: \${JSON.stringify({ choices: [{ delta: { content: delta } }] })}\\n\\n\`);
17
+ // MASTRA_URL is required, not defaulted to localhost: a deployed consumer app
18
+ // that forgets to set it would otherwise construct a client that silently
19
+ // points a production Worker/serverless function at 127.0.0.1, then fail deep
20
+ // inside a fetch call with a confusing connection error. Failing at import
21
+ // time, with a message that names the fix, is louder and cheaper to debug.
22
+ const MASTRA_URL = process.env.MASTRA_URL;
23
+ if (!MASTRA_URL) {
24
+ throw new Error('MASTRA_URL is not set. Point it at your Mastra server, e.g. http://localhost:4111 for \`mastra dev\`.');
19
25
  }
20
- res.write('data: [DONE]\\n\\n');`,
21
- },
22
- streamMapping: "Mastra agents speak Vercel AI SDK v5 and expose stream.textStream (async iterable of string deltas). Iterate textStream and emit data: {choices:[{delta:{content}}]} frames; close with data: [DONE]. kai-chat's SSE reader handles it. For tool calls and reasoning, convert the agent to a UI message stream with @mastra/ai-sdk.",
23
- runNote: 'Set MASTRA_URL to your Mastra server base URL (mastra dev exposes POST /api/agents/:agentId/stream on port 4111). Install @mastra/client-js.',
26
+
27
+ const mastra = new MastraClient({ baseUrl: MASTRA_URL });
28
+
29
+ // Proxy a Mastra agent to the browser as OpenAI-format SSE.
30
+ async function chatHandler(request: Request): Promise<Response> {
31
+ const { messages } = await readChatRequest(request);
32
+
33
+ // MastraClient's Agent.stream() takes AI-SDK CoreMessage[], not the OpenAI
34
+ // wire format: each message has ONE literal role (not a union) and content
35
+ // is never null. A 'tool' wire message is OUR client-side tool bookkeeping;
36
+ // this agent owns its tools server-side (forwardsFromClient is empty), so it
37
+ // has no schema for that shape and the entry is dropped rather than guessed.
38
+ type MastraMessage = { role: 'system'; content: string } | { role: 'user'; content: string } | { role: 'assistant'; content: string };
39
+ const mastraMessages: MastraMessage[] = [];
40
+ for (const m of messages) {
41
+ const content = m.content ?? '';
42
+ // Each branch constructs a literal with ONE fixed role, not m.role (which is
43
+ // still typed as the 4-way union): a union-VALUED field on a single object
44
+ // does not structurally match a union of role-discriminated objects, so
45
+ // widening back to m.role here would reintroduce the original TS2345.
46
+ if (m.role === 'system') mastraMessages.push({ role: 'system', content });
47
+ else if (m.role === 'user') mastraMessages.push({ role: 'user', content });
48
+ else if (m.role === 'assistant') mastraMessages.push({ role: 'assistant', content });
49
+ }
50
+
51
+ let agentStream: Awaited<ReturnType<ReturnType<typeof mastra.getAgent>['stream']>>;
52
+ try {
53
+ agentStream = await mastra.getAgent('supportAgent').stream(mastraMessages);
54
+ } catch (err) {
55
+ // A REAL status, before a byte is streamed: an unreachable MASTRA_URL or an
56
+ // unknown agent id fails here. Returning 200 would send this JSON out
57
+ // labelled text/event-stream, the SSE reader would find no frame, and the
58
+ // turn would resolve empty with nothing logged and no bubble.
59
+ return new Response(
60
+ JSON.stringify({ error: { message: err instanceof Error ? err.message : 'Mastra agent failed to start' } }),
61
+ { status: 502, headers: { 'Content-Type': 'application/json' } },
62
+ );
63
+ }
64
+
65
+ const encoder = new TextEncoder();
66
+ const body = new ReadableStream({
67
+ async start(controller) {
68
+ try {
69
+ // The client SDK has no textStream: it hands back a Response wrapped
70
+ // with processDataStream, which replays the agent's SSE body as typed
71
+ // chunks. Only text-delta carries browser-facing content; tool-call and
72
+ // reasoning chunks exist here too but are out of scope for this route
73
+ // (see streamMapping) and are left unhandled rather than half-wired.
74
+ await agentStream.processDataStream({
75
+ onChunk: (chunk) => {
76
+ if (chunk.type === 'text-delta') {
77
+ const openaiChunk = { choices: [{ delta: { content: chunk.payload.text } }] };
78
+ controller.enqueue(encoder.encode(\`data: \${JSON.stringify(openaiChunk)}\\n\\n\`));
79
+ } else if (chunk.type === 'error') {
80
+ // Mastra reports some failures IN BAND as an 'error' chunk instead
81
+ // of rejecting the stream; fold it into the same frame the catch
82
+ // below emits so readOpenAIStream lands it on turn.error either way.
83
+ const message = chunk.payload.error instanceof Error ? chunk.payload.error.message : 'Mastra agent reported an error';
84
+ controller.enqueue(encoder.encode(\`data: \${JSON.stringify({ error: { message } })}\\n\\n\`));
85
+ }
86
+ },
87
+ });
88
+ } catch (err) {
89
+ // The status is spent once the stream started, so report IN BAND:
90
+ // readOpenAIStream lands this on turn.error and keeps what streamed.
91
+ const message = err instanceof Error ? err.message : 'Mastra stream failed';
92
+ controller.enqueue(encoder.encode(\`data: \${JSON.stringify({ error: { message } })}\\n\\n\`));
93
+ }
94
+ controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));
95
+ controller.close();
96
+ },
97
+ });
98
+
99
+ return new Response(body, {
100
+ status: 200,
101
+ headers: {
102
+ 'Content-Type': 'text/event-stream; charset=utf-8',
103
+ // no-transform stops a proxy buffering the stream into one blob.
104
+ 'Cache-Control': 'no-cache, no-transform',
105
+ Connection: 'keep-alive',
106
+ },
107
+ });
108
+ }`,
109
+ streamMapping: "Mastra's client SDK has no textStream: agent.stream() returns a Response wrapped with processDataStream({ onChunk }), which replays the agent's SSE body as typed chunks (text-delta, tool-call, reasoning-delta, error, finish, ...). Filter to type === 'text-delta' and emit data: {choices:[{delta:{content:payload.text}}]} frames; close with data: [DONE]. readOpenAIStream from @kitn.ai/ui/wire parses that, including tool calls and reasoning, but this route only wires text-delta: re-frame tool-call/tool-result and reasoning-delta chunks onto delta.tool_calls and delta.reasoning to get them too.",
110
+ // No install list here: `deps` below is the one, and the scaffolder emits it.
111
+ // This sentence used to end "Install @mastra/client-js." — right on the day it
112
+ // was written, and unchecked against the route's imports ever after.
113
+ runNote: 'Set MASTRA_URL to your Mastra server base URL (mastra dev exposes POST /api/agents/:agentId/stream on port 4111).',
24
114
  docsSlug: 'integrations/harnesses',
115
+ // Nothing. The Mastra agent owns its model and its tools server-side.
116
+ forwardsFromClient: [],
117
+ // The route's one import.
118
+ deps: { npm: ['@mastra/client-js'], pip: [] },
119
+ // THE JUDGEMENT CALL IN THIS TABLE, so it is argued rather than asserted.
120
+ //
121
+ // MASTRA_URL is not a secret — it is a base URL, and the schema's SECRET_ENV_VAR
122
+ // net deliberately does not match it. So the automatic check cannot decide this
123
+ // one, and 'frontend-safe' would parse cleanly.
124
+ //
125
+ // It is still 'needs-proxy', on what the route actually is: this integration
126
+ // ships a server `chatHandler` that reads `process.env` at module scope, and
127
+ // nothing else. There is no browser-direct path in it. Declaring it
128
+ // 'frontend-safe' would have the CLI point a public bundle at an unauthenticated
129
+ // agent endpoint — a decision a scaffolder must not make silently, even though
130
+ // no key leaks. The conservative direction is the cheap one: a needless server
131
+ // hop costs a process, the other error costs the endpoint.
132
+ keyExposure: 'needs-proxy',
25
133
  };
26
134
 
27
135
  export default mastra;
@@ -1,16 +1,39 @@
1
1
  import type { Integration } from '../types';
2
2
 
3
3
  /**
4
- * mock the zero-config first-win.
4
+ * mock: the zero-config first-win.
5
5
  *
6
- * Not a real backend: there's no provider, no API key, no `/api` route. The
7
- * scaffolder special-cases `integration === 'mock'` and emits a front-end whose
8
- * `onSubmit` SIMULATES a streamed assistant reply CLIENT-SIDE (token-by-token,
9
- * a new array/object reference per chunk honouring the messages contract).
6
+ * No provider, no API key, no `/api` route, no network. The scaffolder
7
+ * special-cases `integration === 'mock'` and emits a front-end whose `onSubmit`
8
+ * calls `createMockResponder()` from `@kitn.ai/ui/state` where a real
9
+ * integration calls `fetch('/api/chat', …)`. That is the ONLY difference between
10
+ * the two emitted handlers. Everything after that expression is already the real
11
+ * path: `readOpenAIStream` parses the frames, `createAssistantStream` folds them
12
+ * onto the message parts, and the try/catch/finally and abort handling are
13
+ * byte-identical. The zero-config preview therefore exercises the kit's own SSE
14
+ * reader rather than a hand-rolled fold that merely resembles it.
15
+ *
16
+ * WHY OPENAI-SHAPED FRAMES. The mock stands in for the consumer's ROUTE, not for
17
+ * a provider. Every other integration in this catalog re-frames its provider to
18
+ * the OpenAI chat-completions shape server-side before the browser sees it
19
+ * (`anthropic`'s route converts the Messages dialect frame by frame, `pi`'s
20
+ * bridge converts Pi's stdout JSON, `vercel-ai-sdk` wraps `result.textStream`),
21
+ * which is why `readOpenAIStream` is the only reader the scaffolder emits.
22
+ * Matching that shape is what keeps "swap the mock for a real backend" a
23
+ * one-expression change instead of a rewrite of the submit handler.
24
+ *
25
+ * WHY YOU CAN ALWAYS TELL. Sharing a real provider's frame shape means shape
26
+ * alone no longer marks the reply as fake, so the tells are explicit: the stream
27
+ * opens with a `: kai-mock` SSE comment, every frame carries a `_kai_mock` field
28
+ * naming the function that produced it, `model` reports as `kai-mock` (no
29
+ * provider serves that, so an echoed mock frame is rejected upstream rather than
30
+ * quietly believed), and usage is all zeros. `src/state/mock.ts` records why each
31
+ * tell sits at a different altitude; do not remove one without reading it.
10
32
  *
11
33
  * This lets `scaffold(useCase, integration: 'mock', framework: 'react')` run with
12
34
  * zero config so a developer sees a live, streaming chat before wiring a model.
13
- * Swap `integration` for a real provider (openrouter, ollama, …) when ready.
35
+ * Swap `integration` for a real provider (openai, anthropic, openrouter, ollama,
36
+ * …) when ready.
14
37
  *
15
38
  * It carries an empty `routeTemplates` on purpose: there is no server route to
16
39
  * emit. `category: 'mock'` keeps it out of the real provider/gateway/framework/
@@ -25,10 +48,22 @@ const mock: Integration = {
25
48
  envVars: [],
26
49
  routeTemplates: {},
27
50
  streamMapping:
28
- 'No backend. onSubmit streams a canned assistant reply client-side, one token at a time, reassigning messages (new array/object reference) per chunk so kai-chat re-renders. Swap integration for a real provider when ready.',
51
+ "No backend and no provider, but a real wire. createMockResponder() from @kitn.ai/ui/state yields canned SSE frames and the scaffold reads them with readOpenAIStream from @kitn.ai/ui/wire, on the same code path a real route's response takes: same reader, same part folding, same abort handling. The frames carry the OpenAI chat-completions shape because the mock stands in for your /api/chat ROUTE, not for a provider (every other integration here re-frames its provider to that shape server-side), so going live replaces ONE expression: mockResponse(value) becomes the awaited response from your own chat route, and nothing else in the handler changes. Nothing here can be mistaken for a real turn: the stream opens with a ': kai-mock' SSE comment, every frame carries a _kai_mock field naming createMockResponder, model reports as 'kai-mock' (no provider serves it), and usage is all zeros.",
29
52
  runNote:
30
- 'No backend or API key needed replies stream locally for preview. Run the front-end as-is; swap `integration` for a real provider (e.g. openrouter, ollama) when ready.',
53
+ 'No backend or API key needed: the reply is generated in the browser and parsed by the same reader a real route feeds. Run the front-end as-is; swap `integration` for a real provider (openai, anthropic, openrouter, ollama) when ready, and the emitted handler differs by one expression.',
31
54
  docsSlug: 'integrations/mock',
55
+ // Nothing: there is no HTTP request at all. The frames are produced in-process
56
+ // by createMockResponder(), so there is no body to forward a model or a tools
57
+ // array on, and no route that would read one.
58
+ forwardsFromClient: [],
59
+ // Nothing to install: there is no route to install anything for.
60
+ deps: { npm: [], pip: [] },
61
+ // The other 'frontend-safe' entry, and the only one that is true by absence:
62
+ // no routeTemplates, no webRoute, no envVars. There is no request, no upstream
63
+ // and no secret, so there is nothing a public bundle could give away. This is
64
+ // the one place where "declares nothing" genuinely means safe — which is
65
+ // precisely why it still has to SAY so rather than be left blank.
66
+ keyExposure: 'frontend-safe',
32
67
  };
33
68
 
34
69
  export default mock;
@@ -7,41 +7,78 @@ const ollama: Integration = {
7
7
  language: 'ts',
8
8
  streamFormat: 'openai-sse',
9
9
  envVars: [],
10
- routeTemplates: {
11
- next: `// app/api/chat/route.ts proxy the browser to local Ollama
12
- export async function POST(req: Request) {
13
- const { messages } = await req.json();
10
+ // No per-framework templates. The handler below is web-standard, so the
11
+ // scaffolder wraps it in the target framework's own route declaration.
12
+ //
13
+ // There was never an `html` entry and there must not be one: routeTemplates
14
+ // keyed by 'html' are selected as the BACKEND ROUTE for framework: 'html', and
15
+ // this integration's used to be a second <kai-chat id="chat"> plus a second
16
+ // kai-submit listener, so pasting both blocks gave a duplicate id and two
17
+ // fetches per submit. chooseRoute refuses an 'html' key outright.
18
+ routeTemplates: {},
19
+ webRoute: `async function chatHandler(request: Request): Promise<Response> {
20
+ // The model is pinned here, not sent by the browser. tools IS forwarded:
21
+ // it is undefined unless the front end declared any, and JSON.stringify
22
+ // drops it, so the same handler serves a tool archetype and a plain chat.
23
+ const { messages, tools } = await readChatRequest(request);
14
24
 
15
25
  const upstream = await fetch('http://localhost:11434/v1/chat/completions', {
16
26
  method: 'POST',
17
27
  headers: { 'Content-Type': 'application/json' },
18
- body: JSON.stringify({ model: 'llama3.2', messages, stream: true }),
28
+ body: JSON.stringify({ model: 'llama3.2', messages, tools, stream: true }),
19
29
  });
20
30
 
21
- // Ollama returns OpenAI-format SSE stream it straight to the browser.
22
- return new Response(upstream.body, { headers: { 'Content-Type': 'text/event-stream' } });
23
- }`,
24
- html: `<kai-chat id="chat"></kai-chat>
25
-
26
- <script type="module">
27
- import '@kitn.ai/ui/elements';
28
- const chat = document.getElementById('chat');
29
-
30
- chat.addEventListener('kai-submit', async (e) => {
31
- const history = [...chat.messages, { id: crypto.randomUUID(), role: 'user', content: e.detail.value }];
32
- chat.messages = [...history, { id: crypto.randomUUID(), role: 'assistant', content: '' }];
33
- await fetch('/api/chat', {
34
- method: 'POST',
31
+ // FORWARD THE STATUS. Returning 200 for a failed upstream is how "ollama is
32
+ // not running" or "that model was never pulled" turns into silence: the error
33
+ // body is JSON, it goes out labelled text/event-stream, the SSE reader finds
34
+ // no frame, and the turn resolves empty with nothing logged and no bubble.
35
+ if (!upstream.ok) {
36
+ return new Response(await upstream.text(), {
37
+ status: upstream.status,
38
+ headers: {
39
+ 'Content-Type': upstream.headers.get('content-type') ?? 'application/json',
40
+ },
41
+ });
42
+ }
43
+ if (!upstream.body) {
44
+ return new Response(JSON.stringify({ error: { message: 'Ollama returned no body to stream.' } }), {
45
+ status: 502,
35
46
  headers: { 'Content-Type': 'application/json' },
36
- body: JSON.stringify({ messages: history }),
37
47
  });
38
- // stream the reply into the last message — see the Streaming recipe
48
+ }
49
+
50
+ // Ollama returns OpenAI-format SSE. Stream it straight to the browser.
51
+ return new Response(upstream.body, {
52
+ status: 200,
53
+ headers: {
54
+ 'Content-Type': 'text/event-stream; charset=utf-8',
55
+ // no-transform stops a proxy buffering the stream into one blob.
56
+ 'Cache-Control': 'no-cache, no-transform',
57
+ Connection: 'keep-alive',
58
+ },
39
59
  });
40
- </script>`,
41
- },
42
- streamMapping: "Ollama's OpenAI-compatible endpoint (http://localhost:11434/v1/chat/completions) returns OpenAI-format SSE pipe upstream.body straight to the browser; kai-chat's reader handles it. No API key needed; pass any string if a client requires one (Ollama ignores it).",
60
+ }`,
61
+ streamMapping:
62
+ "Ollama's OpenAI-compatible endpoint (http://localhost:11434/v1/chat/completions) returns OpenAI-format SSE. Pipe upstream.body straight to the browser; readOpenAIStream from @kitn.ai/ui/wire parses it, including tool calls and reasoning. No API key needed; pass any string if a client requires one (Ollama ignores it).",
43
63
  runNote: 'No API key required. Run: ollama serve (starts on 127.0.0.1:11434), then ollama pull <model>. For browser-direct access, set OLLAMA_ORIGINS to allow the page origin; restart Ollama after any env change.',
44
64
  docsSlug: 'integrations/ollama',
65
+ // No 'model': the route pins llama3.2, so a front-end model const would be an
66
+ // editable value the route throws away. Change the model in the route. 'tools'
67
+ // IS forwarded, which is what lets an agentic scaffold fill its kai-tool panel.
68
+ forwardsFromClient: ['tools'],
69
+ // Ollama's OpenAI-compatible endpoint, and the handler forwards `tools`
70
+ // verbatim into that body — so the client sends OpenAI's function envelope.
71
+ clientToolFormat: 'openai',
72
+ // Nothing. The route is global `fetch` and imports no module; the `ollama`
73
+ // npm package is not used, and the server itself is installed out of band.
74
+ deps: { npm: [], pip: [] },
75
+ // One of the two 'frontend-safe' entries, and it is read off the route rather
76
+ // than off "it's local, so it's fine": the fetch to localhost:11434 sends
77
+ // Content-Type and NOTHING else — no Authorization, no key — and envVars is
78
+ // empty, so there is no secret for a bundle to leak. runNote already documents
79
+ // the browser-direct path (set OLLAMA_ORIGINS to allow the page origin), which
80
+ // is the path this value unlocks.
81
+ keyExposure: 'frontend-safe',
45
82
  };
46
83
 
47
84
  export default ollama;
@@ -0,0 +1,87 @@
1
+ import type { Integration } from '../types';
2
+
3
+ const openai: Integration = {
4
+ id: 'openai',
5
+ title: 'OpenAI',
6
+ category: 'provider',
7
+ language: 'ts',
8
+ streamFormat: 'openai-sse',
9
+ envVars: ['OPENAI_API_KEY'],
10
+ // No per-framework templates: the handler below is web-standard, so the
11
+ // scaffolder wraps it in whatever the target framework routes with.
12
+ routeTemplates: {},
13
+ webRoute: `async function chatHandler(request: Request): Promise<Response> {
14
+ // Both model and tools come from the browser. \`tools\` is undefined unless the
15
+ // front end declared any, and JSON.stringify drops it, so the same handler
16
+ // serves a tool archetype and a plain chat.
17
+ const { model, messages, tools } = await readChatRequest(request);
18
+
19
+ const upstream = await fetch('https://api.openai.com/v1/chat/completions', {
20
+ method: 'POST',
21
+ headers: {
22
+ Authorization: \`Bearer \${process.env.OPENAI_API_KEY}\`,
23
+ 'Content-Type': 'application/json',
24
+ },
25
+ // Model ids here carry NO vendor prefix: 'gpt-4o-mini', never
26
+ // 'openai/gpt-4o-mini' — the prefixed form is an OpenRouter slug and this
27
+ // host answers it with a 404. The front end's \`model\` const is the one to
28
+ // edit; it is seeded from CLIENT_MODEL_IDS.openai.
29
+ body: JSON.stringify({ model, messages, tools, stream: true }),
30
+ });
31
+
32
+ // FORWARD THE STATUS. Returning 200 here is how a missing key turns into
33
+ // silence: the 401 body is JSON, it goes out labelled text/event-stream, the
34
+ // SSE reader finds no frame, and the turn resolves empty with nothing logged
35
+ // and no bubble. With the status intact readOpenAIStream throws a WireError
36
+ // carrying OpenAI's own message, which is the most useful thing you can be
37
+ // handed on a first run.
38
+ if (!upstream.ok) {
39
+ return new Response(await upstream.text(), {
40
+ status: upstream.status,
41
+ headers: {
42
+ 'Content-Type': upstream.headers.get('content-type') ?? 'application/json',
43
+ },
44
+ });
45
+ }
46
+ if (!upstream.body) {
47
+ return new Response(JSON.stringify({ error: { message: 'OpenAI returned no body to stream.' } }), {
48
+ status: 502,
49
+ headers: { 'Content-Type': 'application/json' },
50
+ });
51
+ }
52
+
53
+ // OpenAI IS the format readOpenAIStream parses. Pass it straight through.
54
+ return new Response(upstream.body, {
55
+ status: 200,
56
+ headers: {
57
+ 'Content-Type': 'text/event-stream; charset=utf-8',
58
+ // no-transform stops a proxy buffering the stream into one blob.
59
+ 'Cache-Control': 'no-cache, no-transform',
60
+ Connection: 'keep-alive',
61
+ },
62
+ });
63
+ }`,
64
+ streamMapping:
65
+ "OpenAI's /v1/chat/completions is the reference OpenAI-format SSE stream — this is the one integration with no re-framing at all. Pipe upstream.body straight to the browser; readOpenAIStream from @kitn.ai/ui/wire parses it, including tool calls (delta.tool_calls) and reasoning (delta.reasoning on the reasoning models). Set stream_options: { include_usage: true } in the body if you want a final usage frame; readOpenAIStream reads prompt_tokens/completion_tokens off it.",
66
+ runNote:
67
+ "Set OPENAI_API_KEY (platform.openai.com). Model ids have NO vendor prefix: 'gpt-4o-mini', not 'openai/gpt-4o-mini' — the prefixed form is an OpenRouter slug and api.openai.com answers it with a 404. Change the pinned model in the route.",
68
+ docsSlug: 'integrations/connect-any-model',
69
+ // 'model' was withheld until scaffold.ts could seed it correctly: the shared
70
+ // fallback is 'openai/gpt-4o-mini', an OpenRouter slug that api.openai.com
71
+ // 404s, and shipping an editable const with a broken default is the exact
72
+ // dead-const defect forwardsFromClient exists to prevent. CLIENT_MODEL_IDS now
73
+ // carries a per-integration entry ('gpt-4o-mini'), so the const is real and the
74
+ // route reads it. 'tools' is what lets an agentic scaffold fill its kai-tool
75
+ // panel.
76
+ forwardsFromClient: ['model', 'tools'],
77
+ // The handler forwards `tools` unconverted to /v1/chat/completions — this IS
78
+ // the OpenAI wire, so the client sends OpenAI's own function envelope.
79
+ clientToolFormat: 'openai',
80
+ // Nothing to install. The route is global `fetch` and imports no module at all.
81
+ deps: { npm: [], pip: [] },
82
+ // The route reads OPENAI_API_KEY and puts it in an `Authorization: Bearer`
83
+ // header. A static bundle carrying that is a published key.
84
+ keyExposure: 'needs-proxy',
85
+ };
86
+
87
+ export default openai;
@@ -7,20 +7,68 @@ const openrouter: Integration = {
7
7
  language: 'ts',
8
8
  streamFormat: 'openai-sse',
9
9
  envVars: ['OPENROUTER_API_KEY'],
10
- routeTemplates: {
11
- next: `export async function POST(req: Request) {
12
- const { model, messages } = await req.json();
10
+ // No per-framework templates: the handler below is web-standard, so the
11
+ // scaffolder wraps it in whatever the target framework routes with. This used
12
+ // to be a `next`-only entry that every other framework inherited verbatim.
13
+ routeTemplates: {},
14
+ webRoute: `async function chatHandler(request: Request): Promise<Response> {
15
+ // tools is undefined unless the front end sent one; JSON.stringify drops it,
16
+ // so the same handler serves a tool archetype and a plain chat.
17
+ const { model, messages, tools } = await readChatRequest(request);
18
+
13
19
  const upstream = await fetch('https://openrouter.ai/api/v1/chat/completions', {
14
20
  method: 'POST',
15
- headers: { Authorization: \`Bearer \${process.env.OPENROUTER_API_KEY}\`, 'Content-Type': 'application/json' },
16
- body: JSON.stringify({ model, messages, stream: true }),
21
+ headers: {
22
+ Authorization: \`Bearer \${process.env.OPENROUTER_API_KEY}\`,
23
+ 'Content-Type': 'application/json',
24
+ },
25
+ body: JSON.stringify({ model, messages, tools, stream: true }),
26
+ });
27
+
28
+ // FORWARD THE STATUS. Returning 200 here is how a missing key turns into
29
+ // silence: the 401 body is JSON, it goes out labelled text/event-stream, the
30
+ // SSE reader finds no frame, and the turn resolves empty with nothing logged
31
+ // and no bubble. With the status intact readOpenAIStream throws a WireError
32
+ // carrying the provider's own message.
33
+ if (!upstream.ok) {
34
+ return new Response(await upstream.text(), {
35
+ status: upstream.status,
36
+ headers: {
37
+ 'Content-Type': upstream.headers.get('content-type') ?? 'application/json',
38
+ },
39
+ });
40
+ }
41
+ if (!upstream.body) {
42
+ return new Response(JSON.stringify({ error: { message: 'The provider returned no body to stream.' } }), {
43
+ status: 502,
44
+ headers: { 'Content-Type': 'application/json' },
45
+ });
46
+ }
47
+
48
+ return new Response(upstream.body, {
49
+ status: 200,
50
+ headers: {
51
+ 'Content-Type': 'text/event-stream; charset=utf-8',
52
+ // no-transform stops a proxy buffering the stream into one blob.
53
+ 'Cache-Control': 'no-cache, no-transform',
54
+ Connection: 'keep-alive',
55
+ },
17
56
  });
18
- return new Response(upstream.body, { headers: { 'Content-Type': 'text/event-stream' } });
19
57
  }`,
20
- },
21
- streamMapping: 'OpenRouter returns OpenAI-format SSE pipe upstream.body straight to the browser; the Streaming-recipe reader handles it.',
58
+ streamMapping:
59
+ 'OpenRouter returns OpenAI-format SSE. Pipe upstream.body straight to the browser; readOpenAIStream from @kitn.ai/ui/wire parses it, including tool calls and reasoning.',
22
60
  runNote: 'Set OPENROUTER_API_KEY. Model ids are vendor/model, e.g. openai/gpt-4o.',
23
61
  docsSlug: 'integrations/connect-any-model',
62
+ // The route forwards both, so a scaffold's editable consts reach the gateway.
63
+ forwardsFromClient: ['model', 'tools'],
64
+ // The handler puts the client's `tools` straight into an OpenAI-compatible
65
+ // /chat/completions body, unconverted — so the client must send OpenAI's own
66
+ // `{ type: 'function', function: { parameters } }` envelope.
67
+ clientToolFormat: 'openai',
68
+ // Nothing to install. The route is global `fetch` and imports no module.
69
+ deps: { npm: [], pip: [] },
70
+ // The route reads OPENROUTER_API_KEY into an `Authorization: Bearer` header.
71
+ keyExposure: 'needs-proxy',
24
72
  };
25
73
 
26
74
  export default openrouter;