@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.
- package/README.md +44 -45
- package/dist/components/artifact-card.d.ts +51 -0
- package/dist/components/artifact.d.ts +1 -1
- package/dist/components/attachments.d.ts +2 -2
- package/dist/components/audio-visualizer/index.d.ts +3 -3
- package/dist/components/audio-visualizer/labs/lab-choreography.d.ts +59 -0
- package/dist/components/audio-visualizer/labs/lab-shaders.d.ts +22 -0
- package/dist/components/audio-visualizer/labs/lab-visualizer.d.ts +18 -0
- package/dist/components/audio-visualizer/variant-aurora.d.ts +2 -2
- package/dist/components/audio-visualizer/variant-bar.d.ts +2 -2
- package/dist/components/audio-visualizer/variant-custom.d.ts +2 -2
- package/dist/components/audio-visualizer/variant-grid.d.ts +1 -1
- package/dist/components/audio-visualizer/variant-radial.d.ts +1 -1
- package/dist/components/audio-visualizer/variant-wave.d.ts +1 -1
- package/dist/components/card-fallback.d.ts +12 -2
- package/dist/components/card-renderer.d.ts +63 -2
- package/dist/components/chat-container.d.ts +3 -0
- package/dist/components/chat-scope-picker.d.ts +2 -2
- package/dist/components/chat-thread.d.ts +68 -18
- package/dist/components/choice-card.d.ts +3 -28
- package/dist/components/composer-dom.d.ts +1 -1
- package/dist/components/composer-highlight.d.ts +1 -1
- package/dist/components/composer-history.d.ts +1 -1
- package/dist/components/composer.d.ts +1 -1
- package/dist/components/confirm-card.d.ts +3 -18
- package/dist/components/context.d.ts +8 -4
- package/dist/components/conversation-item.d.ts +1 -1
- package/dist/components/conversation-list.d.ts +4 -3
- package/dist/components/embed.d.ts +3 -3
- package/dist/components/file-tree.d.ts +11 -22
- package/dist/components/form-widgets.d.ts +1 -1
- package/dist/components/form.d.ts +3 -44
- package/dist/components/link-preview.d.ts +3 -3
- package/dist/components/loader.d.ts +30 -48
- package/dist/components/message-skills.d.ts +4 -0
- package/dist/components/message.d.ts +84 -21
- package/dist/components/model-switcher.d.ts +2 -2
- package/dist/components/prompt-input.d.ts +2 -2
- package/dist/components/response-compare-types.d.ts +11 -2
- package/dist/components/response-compare.d.ts +3 -3
- package/dist/components/source.d.ts +12 -3
- package/dist/components/tasks-card.d.ts +3 -31
- package/dist/components/thread.d.ts +17 -5
- package/dist/components/toast.d.ts +2 -2
- package/dist/components/tool-classify.d.ts +8 -0
- package/dist/components/tool-types.d.ts +29 -1
- package/dist/components/tool.d.ts +3 -3
- package/dist/components/use-card-resolution.d.ts +1 -1
- package/dist/create-tween-D3Y5qiij.js +338 -0
- package/dist/create-tween-DGKY1FDY.js +341 -0
- package/dist/create-tween-paho5Rh-.js +7 -0
- package/dist/custom-elements.json +1681 -454
- package/dist/elements/agent-card.js +1 -1
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/audio-visualizer.js +1 -1
- package/dist/elements/avatar.js +1 -1
- package/dist/elements/badge.js +1 -1
- package/dist/elements/button.js +1 -1
- package/dist/elements/card.js +1 -1
- package/dist/elements/cards.js +1 -1
- package/dist/elements/chain-of-thought.js +1 -1
- package/dist/elements/chat-scope-picker.js +1 -1
- package/dist/elements/chat-types.d.ts +70 -10
- package/dist/elements/chat-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/chunks/{Icon-Tj58ES_d.js → Icon-C-2LvGDG.js} +1 -1
- package/dist/elements/chunks/{action-icons-ns1Fnedj.js → action-icons-CaYoImEk.js} +1 -1
- package/dist/elements/chunks/arrow-left-Bb46H7w9.js +1 -0
- package/dist/elements/chunks/artifact-BBIscOxJ.js +1 -0
- package/dist/elements/chunks/{attachments-mCB5F1Fq.js → attachments-BB56zoji.js} +1 -1
- package/dist/elements/chunks/audio-visualizer-BxOx4THz.js +1 -0
- package/dist/elements/chunks/{badge-B4Zuajl2.js → badge-SxLSZ19R.js} +1 -1
- package/dist/elements/chunks/{button-ZWNif3--.js → button-CmkUkz6c.js} +1 -1
- package/dist/elements/chunks/card-renderer-Db4ffkWv.js +1 -0
- package/dist/elements/chunks/card-tags-D8lZ-C_U.js +1 -0
- package/dist/elements/chunks/chat-thread-B3Ks-cKr.js +1 -0
- package/dist/elements/chunks/check-C8desc_h.js +1 -0
- package/dist/elements/chunks/chevron-down-DKtc2HNB.js +1 -0
- package/dist/elements/chunks/chevron-right-Dw4GPhwL.js +1 -0
- package/dist/elements/chunks/choice-card-Q9cYYDyM.js +1 -0
- package/dist/elements/chunks/circle-BeokBsvB.js +1 -0
- package/dist/elements/chunks/circle-check-wJSqt859.js +1 -0
- package/dist/elements/chunks/{circle-x-B7faJFlY.js → circle-x-D0MEfxOM.js} +1 -1
- package/dist/elements/chunks/code-block-C8VzAraE.js +1 -0
- package/dist/elements/chunks/{collapsible-DM1Ckd4e.js → collapsible-Cvj61obE.js} +1 -1
- package/dist/elements/chunks/{composer-BguS0QL_.js → composer-D86ztyL0.js} +2 -2
- package/dist/elements/chunks/confirm-card-BdyDAGLw.js +1 -0
- package/dist/elements/chunks/context-WHDl-xSx.js +1 -0
- package/dist/elements/chunks/conversation-list-CdR14_Y4.js +1 -0
- package/dist/elements/chunks/create-tween-C_A_B638.js +7 -0
- package/dist/elements/chunks/{default-input-Cqqilg9t.js → default-input-CSZqUM81.js} +1 -1
- package/dist/elements/chunks/define-BzPnNsXg.js +1 -0
- package/dist/elements/chunks/{disclosure-CIZ5c8nO.js → disclosure-DVKsoc1F.js} +1 -1
- package/dist/elements/chunks/{download-Dsbg3ste.js → download-BjA54IMJ.js} +1 -1
- package/dist/elements/chunks/{dropdown-BDd1-Zgi.js → dropdown-BjVVHWXH.js} +1 -1
- package/dist/elements/chunks/{ellipsis-Dw6qrss-.js → ellipsis-BNedmqfi.js} +1 -1
- package/dist/elements/chunks/embed-DQsl4XEq.js +1 -0
- package/dist/elements/chunks/{external-link-Vt74zAoL.js → external-link-Di-dw2CU.js} +1 -1
- package/dist/elements/chunks/{file-text-CZrZF6_v.js → file-text-B3sAODCV.js} +1 -1
- package/dist/elements/chunks/{file-tree-DF4okKyo.js → file-tree-Dgto-bMn.js} +1 -1
- package/dist/elements/chunks/{folder-glP5rha4.js → folder-D6rMadFV.js} +1 -1
- package/dist/elements/chunks/form-CILxvhQ7.js +1 -0
- package/dist/elements/chunks/hover-card-BtiF4XiU.js +1 -0
- package/dist/elements/chunks/{icon-BLb8beoi.js → icon-DKkmze3y.js} +1 -1
- package/dist/elements/chunks/{info-CIsv92JK.js → info-CWRjZkuy.js} +1 -1
- package/dist/elements/chunks/{input-Dds1kFyt.js → input--QJBK6BA.js} +1 -1
- package/dist/elements/chunks/{kbd-De_xbgi9.js → kbd-C3zuKh8R.js} +1 -1
- package/dist/elements/chunks/{link-i4r_k7z4.js → link-BL5JA7Q0.js} +1 -1
- package/dist/elements/chunks/link-preview-CU37bbXh.js +1 -0
- package/dist/elements/chunks/{loader-BmQH4s3V.js → loader-XmpyxyRH.js} +1 -1
- package/dist/elements/chunks/{markdown-Zi21WhUz.js → markdown-3Gv5UZ_o.js} +1 -1
- package/dist/elements/chunks/message-CnBA2OUt.js +1 -0
- package/dist/elements/chunks/message-X5NZbq-W.js +1 -0
- package/dist/elements/chunks/message-square-CnpAm_P0.js +1 -0
- package/dist/elements/chunks/{minimize-2--ifeCGrl.js → minimize-2-DwyrUTcM.js} +1 -1
- package/dist/elements/chunks/{model-switcher-B94YAKsM.js → model-switcher-CclaJEHt.js} +1 -1
- package/dist/elements/chunks/{overlay-kD8dwRv_.js → overlay-Cepd-KcS.js} +1 -1
- package/dist/elements/chunks/{paperclip-q21dYOSK.js → paperclip-CdwKjrlu.js} +1 -1
- package/dist/elements/chunks/{progress-bar-BrAIIaaJ.js → progress-bar-DMVXBD3b.js} +1 -1
- package/dist/elements/chunks/{prompt-suggestion-Bjn2K-fR.js → prompt-suggestion-Di_WZE_5.js} +1 -1
- package/dist/elements/chunks/reasoning-D0jMch5u.js +1 -0
- package/dist/elements/chunks/{resizable-Dp4JExql.js → resizable-1znbL1Gv.js} +1 -1
- package/dist/elements/chunks/{rotate-cw-CFWWkpj2.js → rotate-cw-DVhhgx8n.js} +1 -1
- package/dist/elements/chunks/{scroll-area-Bb0kHH1j.js → scroll-area-Bs4bFS1P.js} +1 -1
- package/dist/elements/chunks/scroll-button-Bet5NLc4.js +1 -0
- package/dist/elements/chunks/{separator-9kRgr1X9.js → separator-C5Gh8qGC.js} +1 -1
- package/dist/elements/chunks/{settings-B8lmec4D.js → settings-5vmEFg0K.js} +1 -1
- package/dist/elements/chunks/{settings-group-I0hEuXiN.js → settings-group-BWLB38kz.js} +1 -1
- package/dist/elements/chunks/{share-C3lTiYGt.js → share-BZ3G_DaB.js} +1 -1
- package/dist/elements/chunks/skeleton-DafPJQTp.js +1 -0
- package/dist/elements/chunks/slots-CIw9RlAe.js +1 -0
- package/dist/elements/chunks/source-D_qj_6uJ.js +1 -0
- package/dist/elements/chunks/{star-CSJtGxgv.js → star-Cuv7j2IE.js} +1 -1
- package/dist/elements/chunks/{store-CHyW95TP.js → store-C1StPHfW.js} +1 -1
- package/dist/elements/chunks/tasks-card-EyoBZfpA.js +1 -0
- package/dist/elements/chunks/{text-shimmer-Cn9TmNpI.js → text-shimmer-D9gRSh34.js} +1 -1
- package/dist/elements/chunks/textarea-DuhtHfcm.js +1 -0
- package/dist/elements/chunks/{thumbs-up-PNVVThEF.js → thumbs-up-B9hwv43w.js} +1 -1
- package/dist/elements/chunks/{toast-store-DKbQn3Wn.js → toast-store-Bm3DUEDe.js} +1 -1
- package/dist/elements/chunks/{tool-DjTRpIpl.js → tool-CDtW7L0z.js} +1 -1
- package/dist/elements/chunks/tooltip-eB226mGK.js +1 -0
- package/dist/elements/chunks/{triangle-alert-DsHTHCPm.js → triangle-alert-C1SNzTqK.js} +1 -1
- package/dist/elements/chunks/use-card-resolution-YZh2MvI5.js +1 -0
- package/dist/elements/chunks/{variant-aurora-Dc4nQUia.js → variant-aurora-Cj1F7D0l.js} +2 -2
- package/dist/elements/chunks/{variant-custom-y_3kVQhS.js → variant-custom-T9gpt7QE.js} +1 -1
- package/dist/elements/chunks/{variant-wave-D5OR1-9Z.js → variant-wave-CxU9z-TE.js} +2 -2
- package/dist/elements/chunks/{video-B7vmenoC.js → video-CK4R7Rus.js} +1 -1
- package/dist/elements/chunks/x-D1qx0aLd.js +1 -0
- package/dist/elements/coachmark.js +1 -1
- package/dist/elements/code-block.js +1 -1
- package/dist/elements/command.js +1 -1
- package/dist/elements/compare.js +1 -1
- package/dist/elements/composer.js +1 -1
- package/dist/elements/confirm-card.js +1 -1
- package/dist/elements/context-meter.js +1 -1
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/default-input.d.ts +7 -7
- package/dist/elements/define.d.ts +1 -0
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/disclosure.d.ts +1 -1
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/element-data-types.d.ts +107 -0
- package/dist/elements/embed.js +1 -1
- package/dist/elements/empty.js +1 -1
- package/dist/elements/feedback-bar.js +1 -1
- package/dist/elements/file-tree.js +1 -1
- package/dist/elements/file-upload.js +1 -1
- package/dist/elements/form.js +1 -1
- package/dist/elements/hover-card.js +1 -1
- package/dist/elements/icon.js +1 -1
- package/dist/elements/image.js +1 -1
- package/dist/elements/input.js +1 -1
- package/dist/elements/kbd.js +1 -1
- package/dist/elements/link-preview.js +1 -1
- package/dist/elements/loader.js +1 -1
- package/dist/elements/markdown.js +1 -1
- package/dist/elements/menu.js +1 -1
- package/dist/elements/message-skills.js +1 -1
- package/dist/elements/message.js +1 -1
- package/dist/elements/model-switcher.js +1 -1
- package/dist/elements/nav.js +1 -1
- package/dist/elements/notice.js +1 -1
- package/dist/elements/pane-group.js +1 -1
- package/dist/elements/pane.js +1 -1
- package/dist/elements/popover.js +1 -1
- package/dist/elements/progress-bar.js +1 -1
- package/dist/elements/prompt-dock.js +1 -1
- package/dist/elements/prompt-input.js +1 -1
- package/dist/elements/prompt-suggestions.js +1 -1
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/register.d.ts +8 -6
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +2 -2
- package/dist/elements/screen.js +1 -1
- package/dist/elements/scroll-area.js +1 -1
- package/dist/elements/scroll-button.js +1 -1
- package/dist/elements/search.js +1 -1
- package/dist/elements/segmented.js +1 -1
- package/dist/elements/separator.js +1 -1
- package/dist/elements/setting-item.js +1 -1
- package/dist/elements/settings-group.js +1 -1
- package/dist/elements/skeleton.js +1 -1
- package/dist/elements/slots.d.ts +16 -0
- package/dist/elements/source.js +1 -1
- package/dist/elements/status.js +1 -1
- package/dist/elements/switch.js +1 -1
- package/dist/elements/tabs.js +1 -1
- package/dist/elements/tasks.js +1 -1
- package/dist/elements/text-shimmer.js +1 -1
- package/dist/elements/thinking-bar.js +1 -1
- package/dist/elements/thread.js +1 -1
- package/dist/elements/toast.js +1 -1
- package/dist/elements/tool.js +1 -1
- package/dist/elements/tooltip.js +1 -1
- package/dist/elements/validate-messages.d.ts +25 -0
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.js +1 -1
- package/dist/elements.d.ts +2725 -214
- package/dist/index.d.ts +163 -125
- package/dist/index.js +6336 -5802
- package/dist/index.server.d.ts +5 -0
- package/dist/index.server.js +5446 -4937
- package/dist/kai-provider.es.d.ts +5 -0
- package/dist/kai-provider.es.js +79 -77
- package/dist/kai.es.d.ts +5 -0
- package/dist/kai.es.js +1 -1
- package/dist/llms/llms-full.txt +762 -148
- package/dist/llms/llms.txt +57 -5
- package/dist/mcp.es.js +4010 -770
- package/dist/primitives/card-component-types.d.ts +11 -0
- package/dist/primitives/card-data-types.d.ts +196 -0
- package/dist/primitives/card-host.d.ts +1 -1
- package/dist/primitives/card-recovery.d.ts +3 -3
- package/dist/primitives/card-registry.d.ts +4 -14
- package/dist/primitives/card-resolution.d.ts +1 -1
- package/dist/primitives/card-routing.d.ts +1 -1
- package/dist/primitives/card-tags.d.ts +11 -0
- package/dist/primitives/card-validate-cards.d.ts +57 -0
- package/dist/primitives/card-validate-generator.testlib.d.ts +20 -0
- package/dist/primitives/card-validate-schemas.d.ts +17 -0
- package/dist/primitives/card-validate.d.ts +24 -0
- package/dist/primitives/create-kai-chat.d.ts +3 -3
- package/dist/primitives/embed-providers.d.ts +1 -1
- package/dist/primitives/link-preview.d.ts +1 -1
- package/dist/primitives/message-feedback.d.ts +2 -2
- package/dist/primitives/toast-store.d.ts +1 -1
- package/dist/react/index.d.ts +717 -299
- package/dist/react/use-kai-chat.d.ts +8 -4
- package/dist/react.d.ts +5 -0
- package/dist/react.js +417 -316
- package/dist/register-impl-bzrZ44ft.js +145 -0
- package/dist/remote/host-embed.d.ts +1 -1
- package/dist/remote/index.d.ts +2 -2
- package/dist/remote/provider-runtime.d.ts +2 -2
- package/dist/remote/provider.d.ts +2 -2
- package/dist/remote/validate.d.ts +1 -1
- package/dist/remote/wire.d.ts +1 -1
- package/dist/schemas/artifact.schema.json +92 -0
- package/dist/schemas/embed.schema.json +0 -2
- package/dist/schemas/from-tool-call.d.ts +48 -0
- package/dist/schemas/index.d.ts +48 -0
- package/dist/schemas/link.schema.json +0 -2
- package/dist/schemas/provider-subsets.d.ts +136 -0
- package/dist/schemas/registry.d.ts +140 -0
- package/dist/schemas/tool-defs.d.ts +121 -0
- package/dist/schemas.d.ts +5 -0
- package/dist/schemas.js +717 -0
- package/dist/solid-BCJUQqwS.js +22750 -0
- package/dist/solid-BdENBcEq.js +20108 -0
- package/dist/solid.d.ts +66 -0
- package/dist/solid.js +261 -0
- package/dist/solid.server.d.ts +5 -0
- package/dist/solid.server.js +261 -0
- package/dist/state/index.d.ts +14 -4
- package/dist/state/messages.d.ts +8 -3
- package/dist/state/mock.d.ts +46 -0
- package/dist/state/parts.d.ts +85 -0
- package/dist/state/stream.d.ts +15 -9
- package/dist/state.d.ts +5 -0
- package/dist/state.js +253 -84
- package/dist/ui/action-icons.d.ts +1 -1
- package/dist/ui/collapsible.d.ts +11 -6
- package/dist/ui/dropdown.d.ts +47 -42
- package/dist/ui/pane-group.d.ts +1 -1
- package/dist/variant-aurora-1wMcXRUS.js +381 -0
- package/dist/variant-aurora-BKHHiOgL.js +391 -0
- package/dist/{variant-aurora-DVtGqVH6.js → variant-aurora-DOb_o4pC.js} +1 -1
- package/dist/{variant-custom-Dif-IV4u.js → variant-custom-CJGB0Kbw.js} +1 -1
- package/dist/variant-custom-Co0CkXJF.js +200 -0
- package/dist/variant-custom-DWSSgmd2.js +170 -0
- package/dist/variant-wave-C3-45cL6.js +218 -0
- package/dist/{variant-wave-DbfNdvSi.js → variant-wave-CEODq-9q.js} +1 -1
- package/dist/variant-wave-jdXBnCUZ.js +208 -0
- package/dist/wire/chunk.d.ts +171 -0
- package/dist/wire/consume.d.ts +26 -0
- package/dist/wire/encode.d.ts +148 -0
- package/dist/wire/formats/anthropic.d.ts +2 -0
- package/dist/wire/formats/openai.d.ts +5 -0
- package/dist/wire/index.d.ts +14 -0
- package/dist/wire/read.d.ts +26 -0
- package/dist/wire/sink-helpers.d.ts +21 -0
- package/dist/wire/sse.d.ts +33 -0
- package/dist/wire.d.ts +5 -53
- package/dist/wire.js +898 -0
- package/frameworks/react/index.tsx +183 -154
- package/frameworks/react/runtime.tsx +21 -1
- package/frameworks/react/use-kai-chat.tsx +11 -1
- package/llms-full.txt +762 -148
- package/llms.txt +57 -5
- package/package.json +61 -8
- package/src/agent-tooling/archetypes.ts +96 -0
- package/src/agent-tooling/integrations/anthropic.ts +412 -0
- package/src/agent-tooling/integrations/cloudflare.ts +136 -52
- package/src/agent-tooling/integrations/langgraph.ts +67 -13
- package/src/agent-tooling/integrations/mastra.ts +120 -12
- package/src/agent-tooling/integrations/mock.ts +43 -8
- package/src/agent-tooling/integrations/ollama.ts +61 -24
- package/src/agent-tooling/integrations/openai.ts +87 -0
- package/src/agent-tooling/integrations/openrouter.ts +56 -8
- package/src/agent-tooling/integrations/pi.ts +79 -23
- package/src/agent-tooling/integrations/pydantic-ai.ts +24 -2
- package/src/agent-tooling/integrations/vercel-ai-sdk.ts +84 -13
- package/src/agent-tooling/mcp/manifest.ts +206 -25
- package/src/agent-tooling/mcp/tools/debug.ts +13 -7
- package/src/agent-tooling/mcp/tools/reference.ts +285 -6
- package/src/agent-tooling/mcp/tools/scaffold.ts +3345 -675
- package/src/agent-tooling/registry.ts +17 -0
- package/src/agent-tooling/types.ts +229 -1
- package/src/components/artifact-card.tsx +120 -0
- package/src/components/audio-visualizer/index.tsx +9 -0
- package/src/components/audio-visualizer/labs/lab-choreography.ts +187 -0
- package/src/components/audio-visualizer/labs/lab-shaders.ts +363 -0
- package/src/components/audio-visualizer/labs/lab-visualizer.tsx +102 -0
- package/src/components/audio-visualizer/shader-canvas.tsx +31 -1
- package/src/components/card-fallback.tsx +42 -10
- package/src/components/card-renderer.tsx +146 -9
- package/src/components/chat-container.tsx +4 -0
- package/src/components/chat-thread.tsx +118 -51
- package/src/components/choice-card.tsx +17 -29
- package/src/components/composer.tsx +12 -2
- package/src/components/confirm-card.tsx +24 -20
- package/src/components/context.tsx +12 -4
- package/src/components/conversation-list.tsx +18 -2
- package/src/components/file-tree.tsx +11 -22
- package/src/components/file-upload.tsx +14 -8
- package/src/components/form.tsx +14 -47
- package/src/components/loader.tsx +39 -12
- package/src/components/message-skills.tsx +4 -0
- package/src/components/message.tsx +294 -46
- package/src/components/reasoning.tsx +92 -5
- package/src/components/response-compare-types.ts +25 -0
- package/src/components/response-compare.tsx +4 -9
- package/src/components/source.tsx +50 -24
- package/src/components/tasks-card.tsx +10 -33
- package/src/components/thread.tsx +84 -42
- package/src/components/toast.tsx +11 -5
- package/src/components/tool-classify.ts +26 -0
- package/src/components/tool-types.ts +31 -1
- package/src/elements/agent-card.tsx +11 -11
- package/src/elements/artifact.tsx +15 -7
- package/src/elements/attachments.tsx +6 -4
- package/src/elements/avatar.tsx +1 -1
- package/src/elements/button.tsx +3 -3
- package/src/elements/card.tsx +2 -2
- package/src/elements/cards.tsx +109 -14
- package/src/elements/chain-of-thought.tsx +14 -10
- package/src/elements/chat-scope-picker.tsx +9 -7
- package/src/elements/chat-types.ts +59 -6
- package/src/elements/chat-workspace.tsx +50 -9
- package/src/elements/chat.tsx +64 -3
- package/src/elements/choice.tsx +8 -8
- package/src/elements/coachmark.tsx +1 -1
- package/src/elements/command.tsx +8 -23
- package/src/elements/compare.tsx +7 -6
- package/src/elements/compiled.css +1 -1
- package/src/elements/composer.tsx +8 -8
- package/src/elements/confirm-card.tsx +9 -9
- package/src/elements/context-meter.tsx +4 -18
- package/src/elements/conversation-list.tsx +14 -9
- package/src/elements/default-input.tsx +3 -3
- package/src/elements/define.tsx +178 -43
- package/src/elements/editable-label.tsx +17 -2
- package/src/elements/element-data-types.ts +112 -0
- package/src/elements/element-meta.json +1596 -304
- package/src/elements/element-types.d.ts +2636 -211
- package/src/elements/feedback-bar.tsx +1 -1
- package/src/elements/file-tree.tsx +1 -1
- package/src/elements/file-upload.tsx +1 -1
- package/src/elements/form.tsx +14 -6
- package/src/elements/input.tsx +1 -1
- package/src/elements/kbd.tsx +1 -1
- package/src/elements/menu.tsx +10 -29
- package/src/elements/message-skills.tsx +7 -9
- package/src/elements/message.tsx +200 -32
- package/src/elements/model-switcher.tsx +5 -3
- package/src/elements/pane.tsx +1 -1
- package/src/elements/prompt-dock.tsx +8 -8
- package/src/elements/prompt-input.tsx +10 -10
- package/src/elements/prompt-suggestions.tsx +7 -4
- package/src/elements/reasoning.tsx +1 -1
- package/src/elements/register.ts +9 -0
- package/src/elements/resizable.globals.d.ts +36 -0
- package/src/elements/resizable.tsx +24 -13
- package/src/elements/response-stream.tsx +1 -1
- package/src/elements/scroll-button.tsx +1 -1
- package/src/elements/search.tsx +6 -1
- package/src/elements/segmented.tsx +5 -11
- package/src/elements/separator.tsx +1 -1
- package/src/elements/slots.ts +57 -24
- package/src/elements/source.tsx +21 -22
- package/src/elements/switch.tsx +3 -3
- package/src/elements/tasks.tsx +5 -4
- package/src/elements/thread.tsx +36 -4
- package/src/elements/toast.tsx +3 -2
- package/src/elements/tool.tsx +1 -1
- package/src/elements/validate-messages.ts +76 -0
- package/src/elements/voice-input.tsx +7 -7
- package/src/elements/voice-output.tsx +4 -4
- package/src/index.ts +141 -6
- package/src/primitives/card-component-types.ts +63 -0
- package/src/primitives/card-data-types.ts +312 -0
- package/src/primitives/card-recovery.ts +13 -2
- package/src/primitives/card-registry.tsx +34 -23
- package/src/primitives/card-schemas/artifact.schema.json +92 -0
- package/src/primitives/card-schemas/embed.schema.json +0 -2
- package/src/primitives/card-schemas/link.schema.json +0 -2
- package/src/primitives/card-tags.ts +61 -0
- package/src/primitives/card-validate-cards.ts +255 -0
- package/src/primitives/card-validate-generator.testlib.ts +38 -0
- package/src/primitives/card-validate-schemas.ts +58 -0
- package/src/primitives/card-validate.ts +48 -20
- package/src/primitives/create-tween.ts +54 -2
- package/src/primitives/message-feedback.ts +3 -2
- package/src/primitives/use-audio-analysis.ts +20 -1
- package/src/primitives/use-sequencer.ts +20 -1
- package/src/schemas/from-tool-call.ts +151 -0
- package/src/schemas/index.ts +340 -0
- package/src/schemas/provider-subsets.ts +538 -0
- package/src/schemas/registry.ts +355 -0
- package/src/schemas/tool-defs.ts +492 -0
- package/src/solid.ts +150 -0
- package/src/state/index.ts +27 -1
- package/src/state/messages.ts +21 -4
- package/src/state/mock.ts +211 -0
- package/src/state/parts.ts +295 -0
- package/src/state/stream.ts +56 -58
- package/src/ui/collapsible.tsx +17 -5
- package/src/ui/dropdown.tsx +46 -12
- package/src/ui/overlay.tsx +10 -4
- package/src/wire/chunk.ts +201 -0
- package/src/wire/consume.ts +440 -0
- package/src/wire/encode.ts +474 -0
- package/src/wire/formats/anthropic.ts +267 -0
- package/src/wire/formats/openai.ts +187 -0
- package/src/wire/index.ts +52 -0
- package/src/wire/read.ts +152 -0
- package/src/wire/sink-helpers.ts +50 -0
- package/src/wire/sse.ts +140 -0
- package/dist/create-tween-hvpmSuwW.js +0 -7
- package/dist/elements/chunks/arrow-left-DQocUFvy.js +0 -1
- package/dist/elements/chunks/audio-visualizer-BqNy7dfm.js +0 -1
- package/dist/elements/chunks/card-BkHbp4BA.js +0 -1
- package/dist/elements/chunks/chat-thread-CkRN6pZ7.js +0 -1
- package/dist/elements/chunks/check-B9-gn6SS.js +0 -1
- package/dist/elements/chunks/chevron-down-BNsxc2hk.js +0 -1
- package/dist/elements/chunks/chevron-right-Hv00StBO.js +0 -1
- package/dist/elements/chunks/circle-0e3KJTDr.js +0 -1
- package/dist/elements/chunks/circle-check-BNAS0JEy.js +0 -1
- package/dist/elements/chunks/code-block-BFUHn6Va.js +0 -1
- package/dist/elements/chunks/context-Cb7k4F-v.js +0 -1
- package/dist/elements/chunks/conversation-list-CvoAkWhN.js +0 -1
- package/dist/elements/chunks/create-tween-oVvAIG8r.js +0 -7
- package/dist/elements/chunks/define-D1din7Ld.js +0 -1
- package/dist/elements/chunks/hover-card-Cmm6Q53m.js +0 -1
- package/dist/elements/chunks/message-BqyXDN3-.js +0 -1
- package/dist/elements/chunks/message-feedback-Djr3z6Yb.js +0 -1
- package/dist/elements/chunks/message-square-vFAgbDaX.js +0 -1
- package/dist/elements/chunks/reasoning-DTpCGtnY.js +0 -1
- package/dist/elements/chunks/scroll-button-B0cH5fXf.js +0 -1
- package/dist/elements/chunks/skeleton-DSXW5Yya.js +0 -1
- package/dist/elements/chunks/slots--V19HwUb.js +0 -1
- package/dist/elements/chunks/textarea-CUwqAX0G.js +0 -1
- package/dist/elements/chunks/tooltip-CgBZCVKp.js +0 -1
- package/dist/elements/chunks/use-card-resolution-BSu_XHjH.js +0 -1
- package/dist/elements/chunks/x-GRUeeQFT.js +0 -1
- package/dist/host-embed.d.ts +0 -33
- package/dist/kai-provider.d.ts +0 -2
- package/dist/origin.d.ts +0 -5
- package/dist/provider-runtime.d.ts +0 -27
- package/dist/register-impl-DQyejdfb.js +0 -145
- package/dist/validate.d.ts +0 -4
- package/dist/version.d.ts +0 -4
- package/src/components/component-meta.json +0 -4550
- package/src/elements/card-media.jpg +0 -0
- package/src/elements/resizable.d.ts +0 -27
- package/src/elements/video-poster.jpg +0 -0
|
@@ -8,33 +8,89 @@ const pi: Integration = {
|
|
|
8
8
|
streamFormat: 'native',
|
|
9
9
|
envVars: [],
|
|
10
10
|
routeTemplates: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
11
|
+
// Express only, and genuinely so: the bridge spawns a local process, which a
|
|
12
|
+
// Worker or an edge runtime cannot do. Every other framework gets this under
|
|
13
|
+
// the scaffolder's "cannot host" warning, the same way pydantic-ai emits a
|
|
14
|
+
// whole FastAPI service.
|
|
15
|
+
express: `// server.ts
|
|
16
|
+
import express from 'express';
|
|
17
|
+
import { spawn } from 'node:child_process';
|
|
18
|
+
import type { OpenAIWireMessage } from '@kitn.ai/ui/wire';
|
|
19
|
+
|
|
20
|
+
const app = express();
|
|
21
|
+
app.use(express.json());
|
|
22
|
+
|
|
23
|
+
// POST /api/chat: bridge a Pi RPC session to the browser as SSE.
|
|
24
|
+
app.post('/api/chat', (req, res) => {
|
|
25
|
+
const { messages } = req.body as { messages: OpenAIWireMessage[] };
|
|
26
|
+
const prompt = messages.at(-1)?.content ?? '';
|
|
27
|
+
|
|
28
|
+
// Headers before the first frame: without text/event-stream the browser
|
|
29
|
+
// buffers the body and readOpenAIStream never sees a frame.
|
|
30
|
+
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
|
|
31
|
+
// no-transform stops a proxy buffering the stream into one blob.
|
|
32
|
+
res.setHeader('Cache-Control', 'no-cache, no-transform');
|
|
33
|
+
res.setHeader('Connection', 'keep-alive');
|
|
34
|
+
|
|
35
|
+
// ONE PROCESS PER REQUEST. Pi in RPC mode holds conversation state, so a
|
|
36
|
+
// module-level process would leak one caller's turns into the next caller's
|
|
37
|
+
// reply and serialise every request behind a single stdin.
|
|
38
|
+
const pi = spawn('pi', ['--mode', 'rpc', '--no-session']);
|
|
39
|
+
|
|
40
|
+
// A spawn failure (pi not on PATH) arrives asynchronously, by which point the
|
|
41
|
+
// SSE headers are out and the status is spent, so report IN BAND:
|
|
42
|
+
// readOpenAIStream lands this on turn.error instead of leaving a blank bubble.
|
|
43
|
+
pi.on('error', (err) => {
|
|
44
|
+
res.write(\`data: \${JSON.stringify({ error: { message: err.message } })}\\n\\n\`);
|
|
45
|
+
res.write('data: [DONE]\\n\\n');
|
|
46
|
+
res.end();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Send the user's turn. Pi commands are { type, message }.
|
|
50
|
+
pi.stdin.write(JSON.stringify({ type: 'prompt', message: prompt }) + '\\n');
|
|
51
|
+
|
|
52
|
+
let buffer = '';
|
|
53
|
+
pi.stdout.on('data', (chunk: Buffer) => {
|
|
54
|
+
buffer += chunk.toString();
|
|
55
|
+
// Split on \\n rather than readline: readline also breaks on the Unicode
|
|
56
|
+
// separators U+2028/U+2029, which appear inside model output.
|
|
57
|
+
const lines = buffer.split('\\n');
|
|
58
|
+
// pop() is \`string | undefined\`, and the buffer has to stay a string.
|
|
59
|
+
buffer = lines.pop() ?? ''; // hold the partial line for the next chunk
|
|
60
|
+
for (const line of lines) {
|
|
61
|
+
if (!line) continue;
|
|
62
|
+
const event = JSON.parse(line);
|
|
63
|
+
const part = event.assistantMessageEvent;
|
|
64
|
+
if (event.type === 'message_update' && part?.type === 'text_delta') {
|
|
65
|
+
res.write(\`data: \${JSON.stringify({ choices: [{ delta: { content: part.delta } }] })}\\n\\n\`);
|
|
66
|
+
}
|
|
30
67
|
}
|
|
31
|
-
}
|
|
68
|
+
});
|
|
69
|
+
pi.on('close', () => { res.write('data: [DONE]\\n\\n'); res.end(); });
|
|
32
70
|
});
|
|
33
|
-
|
|
71
|
+
|
|
72
|
+
app.listen(3001, () => console.log('chat api: http://localhost:3001/api/chat'));
|
|
73
|
+
// The front end fetches a RELATIVE /api/chat, so proxy that path to this port
|
|
74
|
+
// from your dev server, or serve both from one origin.`,
|
|
34
75
|
},
|
|
35
|
-
streamMapping: "Pi runs as a local stdio process in RPC mode (pi --mode rpc --no-session). It emits newline-delimited JSON events on stdout. Map message_update events where assistantMessageEvent.type === 'text_delta' to data: {choices:[{delta:{content:part.delta}}]} SSE frames; send data: [DONE] on close. Split stdout on \\n (not readline, which breaks on Unicode separators U+2028/U+2029). Pi also emits thinking_delta
|
|
36
|
-
runNote: "Pi must be installed locally and available on PATH as 'pi'. No API key is required by the bridge itself; Pi uses its own credentials. Pi runs with full user permissions
|
|
76
|
+
streamMapping: "Pi runs as a local stdio process in RPC mode (pi --mode rpc --no-session). It emits newline-delimited JSON events on stdout. Map message_update events where assistantMessageEvent.type === 'text_delta' to data: {choices:[{delta:{content:part.delta}}]} SSE frames; send data: [DONE] on close. Split stdout on \\n (not readline, which breaks on Unicode separators U+2028/U+2029). Pi also emits thinking_delta and toolcall_* events; re-frame them onto delta.reasoning and delta.tool_calls in the same frames. readOpenAIStream from @kitn.ai/ui/wire parses it, including tool calls and reasoning.",
|
|
77
|
+
runNote: "Pi must be installed locally and available on PATH as 'pi'. No API key is required by the bridge itself; Pi uses its own credentials. Pi runs with full user permissions, so sandbox it before exposing to a public endpoint. See the RPC reference: https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/rpc.md",
|
|
37
78
|
docsSlug: 'integrations/harnesses',
|
|
79
|
+
// Nothing. Pi runs with its own credentials, model and tools; the bridge
|
|
80
|
+
// forwards a prompt, not a model id.
|
|
81
|
+
forwardsFromClient: [],
|
|
82
|
+
// `express` only. The bridge's other two imports are excluded by the rule:
|
|
83
|
+
// `node:child_process` is a builtin and '@kitn.ai/ui/wire' is the kit. Pi
|
|
84
|
+
// itself is NOT an npm dep of the app — runNote requires it on PATH, and the
|
|
85
|
+
// route reaches it with spawn('pi'), not an import.
|
|
86
|
+
deps: { npm: ['express'], pip: [] },
|
|
87
|
+
// No key anywhere: envVars is empty and Pi uses its own credentials on disk.
|
|
88
|
+
// This is 'needs-proxy' for the OTHER reason the flag covers — a server-only
|
|
89
|
+
// capability. The bridge calls spawn(), which no browser can do, and runNote
|
|
90
|
+
// warns that Pi runs with full user permissions and wants sandboxing before it
|
|
91
|
+
// is exposed. "No key" is not the same fact as "safe in the browser", and
|
|
92
|
+
// collapsing the two is exactly how this flag would be got wrong.
|
|
93
|
+
keyExposure: 'needs-proxy',
|
|
38
94
|
};
|
|
39
95
|
|
|
40
96
|
export default pi;
|
|
@@ -43,10 +43,32 @@ async def chat(req: ChatRequest):
|
|
|
43
43
|
return StreamingResponse(openai_sse(req.messages), media_type='text/event-stream')`,
|
|
44
44
|
},
|
|
45
45
|
streamMapping:
|
|
46
|
-
"Pydantic AI's agent.run_stream() yields text deltas via result.stream_text(delta=True). Each delta is re-framed as a data: {choices:[{delta:{content}}]} SSE line and the stream closes with data: [DONE].
|
|
46
|
+
"Pydantic AI's agent.run_stream() yields text deltas via result.stream_text(delta=True). Each delta is re-framed as a data: {choices:[{delta:{content}}]} SSE line and the stream closes with data: [DONE]. readOpenAIStream from @kitn.ai/ui/wire parses tool calls and reasoning too, but stream_text() yields text only: iterate the run's events instead and re-frame its tool-call events onto delta.tool_calls to fill kai-tool.",
|
|
47
|
+
// No install list here: `deps` below is the one, and the scaffolder emits it.
|
|
48
|
+
// This sentence used to open "Install: pip install pydantic-ai fastapi
|
|
49
|
+
// uvicorn." — three of the four packages, missing the `pydantic` its own route
|
|
50
|
+
// imports on the next line.
|
|
47
51
|
runNote:
|
|
48
|
-
'
|
|
52
|
+
'Set OPENAI_API_KEY. Run: uvicorn main:app --reload (default port 8000). Point kai-chat at http://localhost:8000/api/chat.',
|
|
49
53
|
docsSlug: 'integrations/pydantic-ai',
|
|
54
|
+
// Nothing. Agent('openai:gpt-4o') pins the model and the agent registers its
|
|
55
|
+
// own tools, both server-side.
|
|
56
|
+
forwardsFromClient: [],
|
|
57
|
+
// The only python integration, so the only non-empty `pip`. Three of the four
|
|
58
|
+
// are the route's own imports (`pydantic_ai` is imported under its module
|
|
59
|
+
// name and installed under its hyphenated one); `uvicorn` is the ASGI server
|
|
60
|
+
// the run note starts, which nothing imports and without which the app cannot
|
|
61
|
+
// run. That asymmetry is why the pip guard checks imports ⊆ declared and not
|
|
62
|
+
// the reverse.
|
|
63
|
+
deps: { npm: [], pip: ['pydantic-ai', 'fastapi', 'pydantic', 'uvicorn'] },
|
|
64
|
+
// Agent('openai:gpt-4o') reads OPENAI_API_KEY inside the python process.
|
|
65
|
+
//
|
|
66
|
+
// Worth being precise about, because this one looks like an exception: the
|
|
67
|
+
// FastAPI app sets CORS to allow_origins=['*'] and the browser fetches it
|
|
68
|
+
// DIRECTLY on :8000, with no JS proxy in front. That still is not
|
|
69
|
+
// 'frontend-safe' — the flag asks where the SECRET lives, and it lives in the
|
|
70
|
+
// python process. The FastAPI service IS the server hop.
|
|
71
|
+
keyExposure: 'needs-proxy',
|
|
50
72
|
};
|
|
51
73
|
|
|
52
74
|
export default pydanticAi;
|
|
@@ -7,38 +7,109 @@ const vercelAiSdk: Integration = {
|
|
|
7
7
|
language: 'ts',
|
|
8
8
|
streamFormat: 'ai-sdk',
|
|
9
9
|
envVars: ['AI_GATEWAY_API_KEY'],
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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 { streamText } from 'ai';
|
|
14
|
+
import type { ModelMessage, AssistantContent, ToolResultPart } from 'ai';
|
|
13
15
|
|
|
14
|
-
export const maxDuration = 30
|
|
16
|
+
// Next.js only: add \`export const maxDuration = 30\` to the route file to allow
|
|
17
|
+
// long streaming responses. It is a Next route-segment config, not part of the
|
|
18
|
+
// handler, so it lives in the file rather than in here.
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
// The kit's wire format is OpenAI-shaped (tool_calls on the assistant message,
|
|
21
|
+
// a flat content string on the tool message). The AI SDK's ModelMessage is not:
|
|
22
|
+
// a tool call is a CONTENT PART on the assistant message, and a tool result is a
|
|
23
|
+
// tagged output union, not a bare string. Converting rather than casting is the
|
|
24
|
+
// point — a cast would compile but hand the SDK the wrong shape at runtime.
|
|
25
|
+
function toModelMessages(messages: ChatRequestBody['messages']): ModelMessage[] {
|
|
26
|
+
return messages.map((message): ModelMessage => {
|
|
27
|
+
switch (message.role) {
|
|
28
|
+
case 'system':
|
|
29
|
+
case 'user':
|
|
30
|
+
return { role: message.role, content: message.content ?? '' };
|
|
31
|
+
case 'tool': {
|
|
32
|
+
const result: ToolResultPart = {
|
|
33
|
+
type: 'tool-result',
|
|
34
|
+
toolCallId: message.tool_call_id ?? '',
|
|
35
|
+
toolName: message.name ?? '',
|
|
36
|
+
output: { type: 'text', value: message.content ?? '' },
|
|
37
|
+
};
|
|
38
|
+
return { role: 'tool', content: [result] };
|
|
39
|
+
}
|
|
40
|
+
case 'assistant': {
|
|
41
|
+
if (!message.tool_calls?.length) return { role: 'assistant', content: message.content ?? '' };
|
|
42
|
+
const content: AssistantContent = [];
|
|
43
|
+
if (message.content) content.push({ type: 'text', text: message.content });
|
|
44
|
+
for (const call of message.tool_calls) {
|
|
45
|
+
content.push({
|
|
46
|
+
type: 'tool-call',
|
|
47
|
+
toolCallId: call.id,
|
|
48
|
+
toolName: call.function.name,
|
|
49
|
+
input: JSON.parse(call.function.arguments),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return { role: 'assistant', content };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function chatHandler(request: Request): Promise<Response> {
|
|
59
|
+
const { messages } = await readChatRequest(request);
|
|
18
60
|
|
|
19
61
|
const result = streamText({
|
|
20
62
|
model: 'openai/gpt-4o', // AI Gateway id; needs AI_GATEWAY_API_KEY
|
|
21
|
-
messages,
|
|
63
|
+
messages: toModelMessages(messages),
|
|
22
64
|
});
|
|
23
65
|
|
|
24
66
|
const encoder = new TextEncoder();
|
|
25
67
|
const sse = new ReadableStream({
|
|
26
68
|
async start(controller) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
69
|
+
try {
|
|
70
|
+
for await (const delta of result.textStream) {
|
|
71
|
+
const chunk = { choices: [{ delta: { content: delta } }] };
|
|
72
|
+
controller.enqueue(encoder.encode(\`data: \${JSON.stringify(chunk)}\\n\\n\`));
|
|
73
|
+
}
|
|
74
|
+
} catch (err) {
|
|
75
|
+
// The status is spent by the time the SDK fails — the headers went out
|
|
76
|
+
// with the first byte — so report it IN BAND. readOpenAIStream lands
|
|
77
|
+
// this on turn.error and keeps whatever streamed before it. Without it a
|
|
78
|
+
// failed key is an empty bubble and nothing in the console.
|
|
79
|
+
const message = err instanceof Error ? err.message : 'Model stream failed';
|
|
80
|
+
controller.enqueue(encoder.encode(\`data: \${JSON.stringify({ error: { message } })}\\n\\n\`));
|
|
30
81
|
}
|
|
31
82
|
controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));
|
|
32
83
|
controller.close();
|
|
33
84
|
},
|
|
34
85
|
});
|
|
35
86
|
|
|
36
|
-
return new Response(sse, {
|
|
87
|
+
return new Response(sse, {
|
|
88
|
+
status: 200,
|
|
89
|
+
headers: {
|
|
90
|
+
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
91
|
+
// no-transform stops a proxy buffering the stream into one blob.
|
|
92
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
93
|
+
Connection: 'keep-alive',
|
|
94
|
+
},
|
|
95
|
+
});
|
|
37
96
|
}`,
|
|
38
|
-
},
|
|
39
|
-
streamMapping: "The Vercel AI SDK's toUIMessageStreamResponse() and toTextStreamResponse() don't emit OpenAI-format SSE. Wrap result.textStream manually: iterate text deltas and emit data: {choices:[{delta:{content}}]} frames, closing with data: [DONE]. The kai-chat SSE reader handles it.",
|
|
97
|
+
streamMapping: "The Vercel AI SDK's toUIMessageStreamResponse() and toTextStreamResponse() don't emit OpenAI-format SSE. Wrap result.textStream manually: iterate text deltas and emit data: {choices:[{delta:{content}}]} frames, closing with data: [DONE]. readOpenAIStream from @kitn.ai/ui/wire parses tool calls and reasoning too, but textStream carries neither: it is text deltas only. Switch to result.fullStream, which yields typed parts, and re-frame its tool-call and reasoning parts onto delta.tool_calls and delta.reasoning to get them.",
|
|
40
98
|
runNote: 'Set AI_GATEWAY_API_KEY for the AI Gateway (string model id form: creator/model-name). For direct provider access, import its provider package (e.g. @ai-sdk/openai) and set the corresponding key (e.g. OPENAI_API_KEY).',
|
|
41
99
|
docsSlug: 'integrations/vercel-ai-sdk',
|
|
100
|
+
// Nothing. The route pins model: 'openai/gpt-4o' in the streamText() call and
|
|
101
|
+
// defines any tools there too, so neither belongs in the front end.
|
|
102
|
+
forwardsFromClient: [],
|
|
103
|
+
// `ai` only. A direct provider (e.g. @ai-sdk/openai) is the alternative path
|
|
104
|
+
// described in runNote, not what this route imports, so it is not listed: the
|
|
105
|
+
// rule is what the emitted code actually imports.
|
|
106
|
+
deps: { npm: ['ai'], pip: [] },
|
|
107
|
+
// Grep the route for a key and you find NOTHING — no header, no process.env.
|
|
108
|
+
// The AI SDK reads AI_GATEWAY_API_KEY out of the environment itself, inside
|
|
109
|
+
// streamText(). This is the case that makes inference from route source
|
|
110
|
+
// unsafe and the declaration necessary; `envVars` is what the schema's safety
|
|
111
|
+
// net can still see.
|
|
112
|
+
keyExposure: 'needs-proxy',
|
|
42
113
|
};
|
|
43
114
|
|
|
44
115
|
export default vercelAiSdk;
|
|
@@ -2,16 +2,48 @@
|
|
|
2
2
|
* manifest.ts — reads dist/custom-elements.json (a Custom Elements Manifest)
|
|
3
3
|
* and exposes helpers for the component_reference tool.
|
|
4
4
|
*
|
|
5
|
-
* Resolution strategy (dual-context)
|
|
5
|
+
* Resolution strategy (dual-context) — each context is an EXACT location, never a
|
|
6
|
+
* search. See `resolveManifestPath` for why that distinction is the whole point:
|
|
6
7
|
* 1. Bundled bin: dist/mcp.es.js lives in dist/, so custom-elements.json is
|
|
7
|
-
* a sibling →
|
|
8
|
-
* 2. Vitest (source): manifest.ts lives
|
|
9
|
-
*
|
|
8
|
+
* a sibling → ./custom-elements.json relative to import.meta.url.
|
|
9
|
+
* 2. Vitest (source): manifest.ts lives at <package>/src/agent-tooling/mcp/, so
|
|
10
|
+
* the manifest is <package>/dist/custom-elements.json and nowhere else.
|
|
11
|
+
*
|
|
12
|
+
* It also answers "which of these 80 elements has anything to do with cards", for
|
|
13
|
+
* the card contract component_reference serves. That question lives HERE rather than
|
|
14
|
+
* in reference.ts because half of it is a question about the element manifest — the
|
|
15
|
+
* kit's own `type -> tag` map, crossed against the tag list this module already owns
|
|
16
|
+
* — and because both halves of the answer must come off one place. See
|
|
17
|
+
* `cardTagForType` for the rule and for what happens when an eighth card type lands.
|
|
10
18
|
*/
|
|
11
19
|
|
|
12
20
|
import { readFileSync, existsSync } from 'node:fs';
|
|
13
21
|
import { fileURLToPath } from 'node:url';
|
|
14
|
-
import { join, dirname } from 'node:path';
|
|
22
|
+
import { join, dirname, resolve } from 'node:path';
|
|
23
|
+
// The package's own public entry, by the same specifier the scaffolder tells a
|
|
24
|
+
// consumer's route to use. That is now a CHOICE, and this comment used to say the
|
|
25
|
+
// opposite: the barrel re-exports src/schemas/registry.ts, which type-IMPORTED
|
|
26
|
+
// `CardComponentMap` from src/primitives/card-registry.TSX, and pulling a .tsx into
|
|
27
|
+
// this pass would mean giving tsconfig.mcp.json `jsx` + `jsxImportSource` + the DOM
|
|
28
|
+
// lib, dragging the whole Solid component tree into a Node-only project — measured
|
|
29
|
+
// as 0 errors -> 1364. Those two types moved to the DOM-free
|
|
30
|
+
// src/primitives/card-component-types.ts, so a relative import of the barrel would
|
|
31
|
+
// typecheck here today. It stays on the public specifier anyway, because emitting
|
|
32
|
+
// and consuming the same string is the point of this module. The built entry
|
|
33
|
+
// resolves to dist/schemas/index.d.ts, whose own reference to card-registry is a
|
|
34
|
+
// .d.ts that skipLibCheck skips, so the Node/no-DOM guarantee survives intact.
|
|
35
|
+
//
|
|
36
|
+
// `BUILTIN_CARD_TAGS` is the authoritative `CardEnvelope.type -> kai-* tag` map and
|
|
37
|
+
// arrives here as DATA, not a type. It is authored in src/primitives/card-tags.ts,
|
|
38
|
+
// which holds that map ALONE — no Solid below it — so it is readable from source by a
|
|
39
|
+
// Node/no-DOM project rather than only through this built entry. Before that split it
|
|
40
|
+
// shared a module with `BUILTIN_CARD_COMPONENTS` and this module re-derived it by
|
|
41
|
+
// convention instead; see `cardTagForType`.
|
|
42
|
+
//
|
|
43
|
+
// This costs a build before typecheck and before the unit suite. That is not a new
|
|
44
|
+
// dependency: resolveManifestPath() below already requires dist/custom-elements.json,
|
|
45
|
+
// and CI already runs `nx build ui` ahead of both.
|
|
46
|
+
import { BUILTIN_CARD_TAGS, cardSchemaNames } from '@kitn.ai/ui/schemas';
|
|
15
47
|
|
|
16
48
|
// ── CEM types ────────────────────────────────────────────────────────────────
|
|
17
49
|
|
|
@@ -81,32 +113,95 @@ interface CustomElementsManifest {
|
|
|
81
113
|
}
|
|
82
114
|
|
|
83
115
|
// ── Manifest resolution ───────────────────────────────────────────────────────
|
|
116
|
+
//
|
|
117
|
+
// THE MANIFEST IS THIS PACKAGE'S OWN BUILD ARTIFACT, SO IT IS ADDRESSED, NOT SEARCHED
|
|
118
|
+
// FOR. This used to walk up ten parent directories taking the first
|
|
119
|
+
// `<dir>/dist/custom-elements.json` it found, and that is a categorically weaker
|
|
120
|
+
// thing than it looks: "a custom-elements.json exists somewhere above me" and "THIS
|
|
121
|
+
// package's custom-elements.json exists" are different facts, and a walk-up cannot
|
|
122
|
+
// tell them apart. It reports success for both.
|
|
123
|
+
//
|
|
124
|
+
// It was not hypothetical. Measured from an agent git worktree at
|
|
125
|
+
// `<repo>/.claude/worktrees/<agent>/packages/ui/src/agent-tooling/mcp`, the loop
|
|
126
|
+
// climbed past the worktree's own (unbuilt) `packages/ui/dist`, out of the worktree
|
|
127
|
+
// entirely, and bound on iteration 8 to `<repo>/dist/custom-elements.json` — a
|
|
128
|
+
// leftover from the pre-monorepo layout, six weeks stale, 78 tags, and zero elements
|
|
129
|
+
// declaring `cardSchemas`. The consequence is this repo's dominant failure mode in
|
|
130
|
+
// its purest form: on an unbuilt tree the MCP manifest tests did not error, they
|
|
131
|
+
// PASSED, 16 of 17, against an artifact from a tree nobody was working in. Two
|
|
132
|
+
// checkouts could disagree about what they had tested and nothing said so.
|
|
133
|
+
//
|
|
134
|
+
// So the rule here is now: a missing manifest is a HARD FAILURE that names the path
|
|
135
|
+
// it expected. Never a fallback, never a wider search. If this throws, the answer is
|
|
136
|
+
// to build — not to let it find someone else's build.
|
|
137
|
+
|
|
138
|
+
const PACKAGE_NAME = '@kitn.ai/ui';
|
|
139
|
+
const MANIFEST_FILE = 'custom-elements.json';
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* `<package>/src/agent-tooling/mcp` -> `<package>`. A fixed, exact hop, and it is
|
|
143
|
+
* CHECKED below rather than trusted: if this module is ever moved to a different
|
|
144
|
+
* depth the derived root stops being this package and resolution throws, instead of
|
|
145
|
+
* silently addressing whatever directory happens to sit three levels up.
|
|
146
|
+
*/
|
|
147
|
+
const SOURCE_TO_PACKAGE_ROOT = ['..', '..', '..'] as const;
|
|
148
|
+
|
|
149
|
+
/** Is `root` the root of THIS package — not merely *a* directory holding a dist/? */
|
|
150
|
+
function isThisPackage(root: string): boolean {
|
|
151
|
+
const manifest = join(root, 'package.json');
|
|
152
|
+
if (!existsSync(manifest)) return false;
|
|
153
|
+
try {
|
|
154
|
+
return (JSON.parse(readFileSync(manifest, 'utf-8')) as { name?: string }).name === PACKAGE_NAME;
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
84
159
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Absolute path to this package's Custom Elements Manifest, or a throw naming what
|
|
162
|
+
* it looked for.
|
|
163
|
+
*
|
|
164
|
+
* `fromDir` exists for the tests and defaults to this module's own directory. It is
|
|
165
|
+
* the only way to write the check that matters: the guarantee is not "a manifest was
|
|
166
|
+
* found", it is "THIS package's manifest was found", and the two only come apart
|
|
167
|
+
* when there is a decoy above the origin. manifest.test.ts builds exactly that tree.
|
|
168
|
+
*/
|
|
169
|
+
export function resolveManifestPath(
|
|
170
|
+
fromDir: string = dirname(fileURLToPath(import.meta.url)),
|
|
171
|
+
): string {
|
|
172
|
+
// 1. Bundled bin: dist/mcp.es.js and dist/custom-elements.json are siblings.
|
|
173
|
+
// Unambiguous by construction — a sibling cannot be another checkout's artifact.
|
|
174
|
+
const sibling = join(fromDir, MANIFEST_FILE);
|
|
175
|
+
if (existsSync(sibling)) return sibling;
|
|
176
|
+
|
|
177
|
+
// 2. Source (vitest, tsx): one exact location, derived and then verified.
|
|
178
|
+
const packageRoot = resolve(fromDir, ...SOURCE_TO_PACKAGE_ROOT);
|
|
179
|
+
const expected = join(packageRoot, 'dist', MANIFEST_FILE);
|
|
88
180
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
181
|
+
if (!isThisPackage(packageRoot)) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`[${PACKAGE_NAME}] Cannot locate the Custom Elements Manifest: ${packageRoot} is not ` +
|
|
184
|
+
`the ${PACKAGE_NAME} package root, so ${expected} would not be this package's ` +
|
|
185
|
+
`manifest even if it existed.\n` +
|
|
186
|
+
`Resolved from: ${fromDir}\n` +
|
|
187
|
+
`This module must live at <package>/src/agent-tooling/mcp/ (or be bundled beside ` +
|
|
188
|
+
`${MANIFEST_FILE} in dist/). Resolution deliberately does NOT search parent ` +
|
|
189
|
+
`directories — finding some other checkout's manifest is worse than failing.`,
|
|
190
|
+
);
|
|
93
191
|
}
|
|
94
192
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
dir = parent;
|
|
193
|
+
if (!existsSync(expected)) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`[${PACKAGE_NAME}] Missing build artifact: ${expected}\n` +
|
|
196
|
+
`Resolved from: ${fromDir}\n` +
|
|
197
|
+
`The Custom Elements Manifest is generated by the build. Run \`nx build ui\` ` +
|
|
198
|
+
`(or \`npm run build:api\` in packages/ui) and try again.\n` +
|
|
199
|
+
`Resolution deliberately does NOT search parent directories: binding to a ` +
|
|
200
|
+
`neighbouring checkout's manifest would make this succeed against stale data.`,
|
|
201
|
+
);
|
|
105
202
|
}
|
|
106
203
|
|
|
107
|
-
|
|
108
|
-
`Could not find custom-elements.json. Searched from: ${thisDir}`,
|
|
109
|
-
);
|
|
204
|
+
return expected;
|
|
110
205
|
}
|
|
111
206
|
|
|
112
207
|
// ── Parsed manifest (module-level cache) ─────────────────────────────────────
|
|
@@ -140,3 +235,89 @@ export function listElements(): string[] {
|
|
|
140
235
|
.map((d) => d.tagName!)
|
|
141
236
|
.sort();
|
|
142
237
|
}
|
|
238
|
+
|
|
239
|
+
// ── Which elements have anything to do with cards ────────────────────────────
|
|
240
|
+
//
|
|
241
|
+
// Two different populations, and conflating them would be the "attaches card
|
|
242
|
+
// material to everything" failure:
|
|
243
|
+
//
|
|
244
|
+
// CARD-BACKED kai-confirm, kai-choice, … — one element per CardEnvelope.type.
|
|
245
|
+
// These get a schema and a generated tool definition.
|
|
246
|
+
// CARD HOST kai-chat, kai-message, … — the elements that RENDER a thread of
|
|
247
|
+
// cards and therefore carry the `cardTypes` / `cardSchemas` props.
|
|
248
|
+
// These get the wiring note, not a schema.
|
|
249
|
+
//
|
|
250
|
+
// Neither list is written down here. The host list is derived from the manifest; the
|
|
251
|
+
// card-backed one is the kit's own map, read rather than guessed at. Either way an
|
|
252
|
+
// eighth card type is covered the day it lands rather than the day someone remembers
|
|
253
|
+
// this file.
|
|
254
|
+
|
|
255
|
+
/** Cached derivations. Same lifetime as the parsed manifest above. */
|
|
256
|
+
let _cardTags: Map<string, string> | undefined;
|
|
257
|
+
let _cardHosts: string[] | undefined;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* `CardEnvelope.type` -> the `kai-*` element that renders it.
|
|
261
|
+
*
|
|
262
|
+
* THE MAP IS IMPORTED, NOT INFERRED. `BUILTIN_CARD_TAGS` is the same object
|
|
263
|
+
* `<kai-cards>` dispatches on in the browser, reached here through
|
|
264
|
+
* `@kitn.ai/ui/schemas`. This function used to RE-DERIVE it by convention — the tag
|
|
265
|
+
* is `kai-<t>` when that element exists, else the single element whose tag starts
|
|
266
|
+
* `kai-<t>-` — which was correct against all 80 tags on this tree, `link` ->
|
|
267
|
+
* `kai-link-preview` included, and was still a second copy of a fact the repo already
|
|
268
|
+
* held. The eighth card type is the one that would have broken it: any type whose tag
|
|
269
|
+
* is neither `kai-<t>` nor the sole `kai-<t>-*` had to be noticed by a human, and a
|
|
270
|
+
* future `kai-form-field` would have made `form` ambiguous on its own. A lookup has
|
|
271
|
+
* neither failure mode.
|
|
272
|
+
*
|
|
273
|
+
* WHY IT IS STILL CROSSED AGAINST THE MANIFEST. The map is authoritative for the
|
|
274
|
+
* ASSOCIATION; only the manifest knows what actually got registered. A tag in the map
|
|
275
|
+
* with no element behind it (a rename that missed this file) would otherwise have
|
|
276
|
+
* `component_reference` send a harness to an element that does not exist. Entries with
|
|
277
|
+
* no registered element are dropped, so the answer is a real tag or nothing.
|
|
278
|
+
*
|
|
279
|
+
* WHEN IT BREAKS, IT BREAKS LOUDLY. reference.test.ts asserts every member of
|
|
280
|
+
* `cardSchemaNames` resolves AND that what it resolves to is in `listElements()`, so a
|
|
281
|
+
* card type missing from the map, or pointed at a tag nobody registered, fails a test
|
|
282
|
+
* instead of silently losing its schema in the reference.
|
|
283
|
+
*/
|
|
284
|
+
export function cardTagForType(type: string): string | undefined {
|
|
285
|
+
if (!_cardTags) {
|
|
286
|
+
const present = new Set(listElements());
|
|
287
|
+
_cardTags = new Map(Object.entries(BUILTIN_CARD_TAGS).filter(([, tag]) => present.has(tag)));
|
|
288
|
+
}
|
|
289
|
+
return _cardTags.get(type);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** The inverse: which card type does this element render, if any. */
|
|
293
|
+
export function cardTypeForTag(tag: string): string | undefined {
|
|
294
|
+
for (const name of cardSchemaNames) {
|
|
295
|
+
if (cardTagForType(name) === tag) return name;
|
|
296
|
+
}
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* The elements that host a thread of cards, derived from the manifest: an element is
|
|
302
|
+
* a card host exactly when it declares the `cardSchemas` prop.
|
|
303
|
+
*
|
|
304
|
+
* That prop is the one that carries a developer's own card schemas into the browser
|
|
305
|
+
* validator, so "declares it" and "hosts cards" are the same fact rather than two
|
|
306
|
+
* facts that can disagree. Today it selects kai-chat / kai-message / kai-thread /
|
|
307
|
+
* kai-workspace; an element that grows the prop tomorrow joins without an edit here.
|
|
308
|
+
*/
|
|
309
|
+
export function cardHostTags(): string[] {
|
|
310
|
+
if (!_cardHosts) {
|
|
311
|
+
_cardHosts = getDeclarations()
|
|
312
|
+
.filter(
|
|
313
|
+
(d) =>
|
|
314
|
+
d.tagName !== undefined &&
|
|
315
|
+
(d.members ?? []).some(
|
|
316
|
+
(m) => m.kind === 'field' && m.privacy === 'public' && m.name === 'cardSchemas',
|
|
317
|
+
),
|
|
318
|
+
)
|
|
319
|
+
.map((d) => d.tagName!)
|
|
320
|
+
.sort();
|
|
321
|
+
}
|
|
322
|
+
return _cardHosts;
|
|
323
|
+
}
|
|
@@ -39,7 +39,7 @@ const RULES: Rule[] = [
|
|
|
39
39
|
'```js\n' +
|
|
40
40
|
"// ✅ Works — set messages in JavaScript as a property\n" +
|
|
41
41
|
"const chat = document.querySelector('kai-chat');\n" +
|
|
42
|
-
"chat.messages = [{ id: '1', role: 'assistant',
|
|
42
|
+
"chat.messages = [{ id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hello!' }] }];\n" +
|
|
43
43
|
'```\n\n' +
|
|
44
44
|
'```html\n' +
|
|
45
45
|
'<!-- ❌ Fails — messages cannot be an HTML attribute -->\n' +
|
|
@@ -55,7 +55,7 @@ const RULES: Rule[] = [
|
|
|
55
55
|
title: 'In-place mutation does not trigger a re-render',
|
|
56
56
|
cause:
|
|
57
57
|
'Mutating an existing message object or array in place (e.g. `chat.messages.push(…)` ' +
|
|
58
|
-
'or `chat.messages[i].
|
|
58
|
+
'or `chat.messages[i].parts = […]`) does not trigger a re-render. ' +
|
|
59
59
|
'The element only reacts when it detects a new array/object reference.',
|
|
60
60
|
fix:
|
|
61
61
|
'Assign a NEW array (and a new object) on every change — never mutate in place.\n\n' +
|
|
@@ -63,17 +63,23 @@ const RULES: Rule[] = [
|
|
|
63
63
|
'// ✅ Triggers re-render — new array + new object reference\n' +
|
|
64
64
|
'chat.messages = [\n' +
|
|
65
65
|
' ...chat.messages,\n' +
|
|
66
|
-
" { id: crypto.randomUUID(), role: 'user',
|
|
66
|
+
" { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: userText }] },\n" +
|
|
67
67
|
'];\n\n' +
|
|
68
|
-
'// ✅ During streaming
|
|
68
|
+
'// ✅ During streaming: new array + new object per chunk, and FOLD the delta\n' +
|
|
69
|
+
"// onto the trailing text part so reasoning/tool/card parts survive.\n" +
|
|
70
|
+
"import { appendTextPart } from '@kitn.ai/ui/state';\n" +
|
|
69
71
|
'chat.messages = chat.messages.map((m) =>\n' +
|
|
70
|
-
' m.id === assistantId ? { ...m,
|
|
72
|
+
' m.id === assistantId ? { ...m, parts: appendTextPart(m.parts, delta) } : m\n' +
|
|
71
73
|
');\n\n' +
|
|
72
74
|
'// ❌ Does NOT trigger re-render\n' +
|
|
73
75
|
'chat.messages.push(newMsg);\n' +
|
|
74
|
-
'chat.messages[i].
|
|
76
|
+
'chat.messages[i].parts = appendTextPart(chat.messages[i].parts, delta);\n\n' +
|
|
77
|
+
'// ❌ Re-renders, but DROPS the reasoning/tool/card parts already on the message\n' +
|
|
78
|
+
'chat.messages = chat.messages.map((m) =>\n' +
|
|
79
|
+
" m.id === assistantId ? { ...m, parts: [{ type: 'text', text: accumulated }] } : m\n" +
|
|
80
|
+
');\n' +
|
|
75
81
|
'```\n\n' +
|
|
76
|
-
'The ergonomic path: helpers in `@kitn.ai/ui/state` (`appendMessage`, `updateMessage`, `
|
|
82
|
+
'The ergonomic path: helpers in `@kitn.ai/ui/state` (`appendMessage`, `updateMessage`, `appendText`) ' +
|
|
77
83
|
'and `createAssistantStream` handle the new-reference contract for you. ' +
|
|
78
84
|
'`useKaiChat` (React) and `createKaiChat` (Solid) own state entirely so mutation is never an option.\n\n' +
|
|
79
85
|
'// setMessages((m) => appendMessage(m, msg)) // new array, no footgun',
|