@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
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
// The adapter core: read neutral chunks, drive a sink, and report the turn.
|
|
2
|
+
//
|
|
3
|
+
// PORTABILITY RULES: no React, no Solid, no DOM, no fetch, no SSE, and NO
|
|
4
|
+
// PROVIDER SDK. The only kit values imported are the three part builders, which
|
|
5
|
+
// are REUSED rather than reimplemented so that `ModelTurn.parts` is produced by
|
|
6
|
+
// exactly the code that drove the sink.
|
|
7
|
+
import type { MessagePart, MessageSource } from '../elements/chat-types';
|
|
8
|
+
import { appendReasoningPart, appendTextPart, fingerprint, upsertToolPart } from '../state/parts';
|
|
9
|
+
import {
|
|
10
|
+
normalizeStopReason,
|
|
11
|
+
type AssistantStreamSink,
|
|
12
|
+
type ConsumeOptions,
|
|
13
|
+
type ModelStreamChunk,
|
|
14
|
+
type ModelToolCall,
|
|
15
|
+
type ModelToolCallDelta,
|
|
16
|
+
type ModelTurn,
|
|
17
|
+
type ModelUsage,
|
|
18
|
+
type StopReason,
|
|
19
|
+
} from './chunk';
|
|
20
|
+
import type { RawOrigin } from '../components/tool-types';
|
|
21
|
+
|
|
22
|
+
// ── Tool-call accumulator ────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
interface MutableCall {
|
|
25
|
+
index: number;
|
|
26
|
+
id: string | null;
|
|
27
|
+
name: string;
|
|
28
|
+
argumentsText: string;
|
|
29
|
+
announced: boolean;
|
|
30
|
+
/** Fingerprint of the last `input` written, so a re-parse of unchanged text
|
|
31
|
+
* does not patch the part again. */
|
|
32
|
+
lastInputFp: string | null;
|
|
33
|
+
providerExecuted: boolean;
|
|
34
|
+
output?: Record<string, unknown>;
|
|
35
|
+
outputError?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const snapshot = (c: MutableCall): ModelToolCall => ({
|
|
39
|
+
index: c.index,
|
|
40
|
+
id: c.id ?? `call_${c.index}`,
|
|
41
|
+
name: c.name,
|
|
42
|
+
argumentsText: c.argumentsText,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The provider-shaped tool-call block, REASSEMBLED from the fragments, kept on
|
|
47
|
+
* the part so an encoder can round-trip without re-deriving it.
|
|
48
|
+
*
|
|
49
|
+
* Tagged `custom.` on purpose: it is a reconstruction, not a payload the
|
|
50
|
+
* provider handed over intact, so it must never be mistaken for one of the
|
|
51
|
+
* verbatim blocks (Anthropic `thinking`) a provider refuses to accept rebuilt.
|
|
52
|
+
*/
|
|
53
|
+
const rawOf = (c: MutableCall): RawOrigin => ({
|
|
54
|
+
source: 'custom.wire.tool_call',
|
|
55
|
+
payload: { id: c.id ?? `call_${c.index}`, name: c.name, arguments: c.argumentsText },
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
function clip(s: string, n: number): string {
|
|
59
|
+
return s.length <= n ? s : `${s.slice(0, n)}...`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Parse accumulated argument text into a JSON OBJECT, or undefined. A prefix of
|
|
63
|
+
* a JSON object never parses, which is why `rawInput` exists. No tolerant
|
|
64
|
+
* partial-JSON closer ships: guessing at a half-written object produces
|
|
65
|
+
* confidently wrong tool inputs. */
|
|
66
|
+
function parseArgumentObject(text: string): Record<string, unknown> | undefined {
|
|
67
|
+
const trimmed = text.trim();
|
|
68
|
+
if (trimmed === '') return undefined;
|
|
69
|
+
try {
|
|
70
|
+
const value: unknown = JSON.parse(trimmed);
|
|
71
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
72
|
+
return value as Record<string, unknown>;
|
|
73
|
+
} catch {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Correlate tool-call fragments by `index` and drive the ToolPart lifecycle.
|
|
80
|
+
* THE HARD PART: `id`, `name` and each slice of `arguments` arrive across
|
|
81
|
+
* arbitrarily many chunks in provider-dependent order.
|
|
82
|
+
*
|
|
83
|
+
* A ToolPart is pushed (`input-streaming`) the first time a call has BOTH an id
|
|
84
|
+
* and a non-empty name: announcing earlier would create a panel whose `type` is
|
|
85
|
+
* the empty string. Everything before that is buffered and flushed at announce.
|
|
86
|
+
*/
|
|
87
|
+
export function createToolCallAccumulator(sink: AssistantStreamSink, opts: ConsumeOptions = {}) {
|
|
88
|
+
const calls = new Map<number, MutableCall>();
|
|
89
|
+
|
|
90
|
+
/** Write the argument text that has arrived so far.
|
|
91
|
+
*
|
|
92
|
+
* `rawInput` is written on EVERY fragment, so a consumer watching the part
|
|
93
|
+
* sees the arguments assemble character by character. `input` is written only
|
|
94
|
+
* when the whole accumulated text parses to a JSON object. Honest caveat:
|
|
95
|
+
* with plain `JSON.parse` a prefix never parses, so `input` in practice still
|
|
96
|
+
* lands once, at the end. `rawInput` is what streams. */
|
|
97
|
+
const writeArguments = (call: MutableCall) => {
|
|
98
|
+
const id = call.id!;
|
|
99
|
+
const text = call.argumentsText;
|
|
100
|
+
const parsed = parseArgumentObject(text);
|
|
101
|
+
if (parsed) {
|
|
102
|
+
const fp = fingerprint(parsed);
|
|
103
|
+
if (fp !== call.lastInputFp) {
|
|
104
|
+
call.lastInputFp = fp;
|
|
105
|
+
sink.upsertTool(id, { rawInput: text, input: parsed, state: 'input-available' });
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
sink.upsertTool(id, { rawInput: text });
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const announce = (call: MutableCall) => {
|
|
113
|
+
if (call.announced) return;
|
|
114
|
+
call.id ??= `call_${call.index}`;
|
|
115
|
+
call.announced = true;
|
|
116
|
+
sink.upsertTool(call.id, { type: call.name || 'unknown_tool', state: 'input-streaming' });
|
|
117
|
+
// Fragments that arrived before the id did are flushed now.
|
|
118
|
+
if (call.argumentsText) writeArguments(call);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const apply = (raw: ModelToolCallDelta) => {
|
|
122
|
+
const index = typeof raw.index === 'number' ? raw.index : 0;
|
|
123
|
+
let call = calls.get(index);
|
|
124
|
+
if (!call) {
|
|
125
|
+
call = {
|
|
126
|
+
index,
|
|
127
|
+
id: null,
|
|
128
|
+
name: '',
|
|
129
|
+
argumentsText: '',
|
|
130
|
+
announced: false,
|
|
131
|
+
lastInputFp: null,
|
|
132
|
+
providerExecuted: false,
|
|
133
|
+
};
|
|
134
|
+
calls.set(index, call);
|
|
135
|
+
}
|
|
136
|
+
if (raw.id && !call.id) call.id = raw.id;
|
|
137
|
+
if (raw.name) call.name += raw.name;
|
|
138
|
+
if (call.id && call.name) announce(call);
|
|
139
|
+
|
|
140
|
+
if (typeof raw.arguments === 'string' && raw.arguments !== '') {
|
|
141
|
+
call.argumentsText += raw.arguments;
|
|
142
|
+
if (call.announced) writeArguments(call);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (raw.output !== undefined || raw.outputError !== undefined) {
|
|
146
|
+
// A result the PROVIDER executed. Force the announce so the panel exists,
|
|
147
|
+
// then complete it. `settle` must not touch it afterwards.
|
|
148
|
+
announce(call);
|
|
149
|
+
call.providerExecuted = true;
|
|
150
|
+
if (raw.outputError !== undefined) {
|
|
151
|
+
call.outputError = raw.outputError;
|
|
152
|
+
sink.upsertTool(call.id!, { state: 'output-error', errorText: raw.outputError });
|
|
153
|
+
} else {
|
|
154
|
+
call.output = raw.output;
|
|
155
|
+
sink.upsertTool(call.id!, { state: 'output-available', output: raw.output });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/** Settle every accumulated call once the stream ends. */
|
|
161
|
+
const settle = (stopReason: StopReason | undefined, streamError?: string): ModelToolCall[] =>
|
|
162
|
+
[...calls.values()]
|
|
163
|
+
.sort((a, b) => a.index - b.index)
|
|
164
|
+
.map((call) => {
|
|
165
|
+
announce(call); // a call that only ever had arguments still gets a panel
|
|
166
|
+
const id = call.id!;
|
|
167
|
+
const base = snapshot(call);
|
|
168
|
+
|
|
169
|
+
if (call.providerExecuted) {
|
|
170
|
+
// Already at output-available/output-error. Re-settling would overwrite
|
|
171
|
+
// the provider's own result with a parse of its arguments.
|
|
172
|
+
return {
|
|
173
|
+
...base,
|
|
174
|
+
providerExecuted: true,
|
|
175
|
+
...(call.output !== undefined ? { output: call.output } : {}),
|
|
176
|
+
...(call.outputError !== undefined ? { error: call.outputError } : {}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Attached once, at settle: before that `argumentsText` is still growing
|
|
181
|
+
// and a raw snapshot of half a JSON string is worse than none.
|
|
182
|
+
const raw = rawOf(call);
|
|
183
|
+
// `type` is re-sent because a provider may split the tool NAME across
|
|
184
|
+
// fragments; the announce could have fired on a prefix.
|
|
185
|
+
const name = call.name || 'unknown_tool';
|
|
186
|
+
|
|
187
|
+
if (streamError) {
|
|
188
|
+
const error = `Stream failed before the tool call completed: ${streamError}`;
|
|
189
|
+
sink.upsertTool(id, {
|
|
190
|
+
type: name,
|
|
191
|
+
state: 'output-error',
|
|
192
|
+
errorText: error,
|
|
193
|
+
rawInput: call.argumentsText,
|
|
194
|
+
raw,
|
|
195
|
+
});
|
|
196
|
+
return { ...base, error };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (!call.name) {
|
|
200
|
+
const error = 'Tool call arrived with no function name; cannot dispatch it.';
|
|
201
|
+
sink.upsertTool(id, { state: 'output-error', errorText: error, raw });
|
|
202
|
+
return { ...base, error };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const rawArgs = call.argumentsText.trim();
|
|
206
|
+
try {
|
|
207
|
+
// An argument-less tool legitimately streams '' or '{}'.
|
|
208
|
+
const parsed: unknown = rawArgs === '' ? {} : JSON.parse(rawArgs);
|
|
209
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
210
|
+
throw new Error('arguments must be a JSON object');
|
|
211
|
+
}
|
|
212
|
+
const input = parsed as Record<string, unknown>;
|
|
213
|
+
sink.upsertTool(id, {
|
|
214
|
+
type: name,
|
|
215
|
+
state: 'input-available',
|
|
216
|
+
input,
|
|
217
|
+
rawInput: call.argumentsText,
|
|
218
|
+
raw,
|
|
219
|
+
});
|
|
220
|
+
const ready: ModelToolCall = { ...base, input };
|
|
221
|
+
opts.onToolCallReady?.(ready);
|
|
222
|
+
return ready;
|
|
223
|
+
} catch (e) {
|
|
224
|
+
const truncated =
|
|
225
|
+
stopReason === 'length' ? ' (the stream hit the token limit mid-call)' : '';
|
|
226
|
+
const error =
|
|
227
|
+
`Malformed tool arguments${truncated}: ${(e as Error).message}. ` +
|
|
228
|
+
`Received ${rawArgs.length} chars: ${clip(rawArgs, 160)}`;
|
|
229
|
+
sink.upsertTool(id, {
|
|
230
|
+
type: name,
|
|
231
|
+
state: 'output-error',
|
|
232
|
+
errorText: error,
|
|
233
|
+
rawInput: call.argumentsText,
|
|
234
|
+
raw,
|
|
235
|
+
});
|
|
236
|
+
return { ...base, error };
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
return { apply, settle };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ── Part recording and sink tee ──────────────────────────────────────────────
|
|
244
|
+
|
|
245
|
+
/** A sink that writes nowhere but into a `MessagePart[]`, using the kit's own
|
|
246
|
+
* builders. Teed alongside the real sink so `ModelTurn.parts` cannot drift from
|
|
247
|
+
* what the message actually received. */
|
|
248
|
+
function createPartsRecorder(): AssistantStreamSink & { parts(): MessagePart[] } {
|
|
249
|
+
let parts: MessagePart[] = [];
|
|
250
|
+
return {
|
|
251
|
+
appendText(delta) {
|
|
252
|
+
parts = appendTextPart(parts, delta);
|
|
253
|
+
},
|
|
254
|
+
appendReasoning(delta, opts) {
|
|
255
|
+
// `streamId` is DROPPED here on purpose. It namespaces block indices for a
|
|
256
|
+
// sink that outlives one stream; this recorder starts a fresh array per
|
|
257
|
+
// call, so within `ModelTurn.parts` there is nothing to disambiguate. Worse,
|
|
258
|
+
// it is a new value on every call, so keeping it would make `parts`
|
|
259
|
+
// non-reproducible for identical input and break the determinism the
|
|
260
|
+
// adapter guarantees (same chunks in, same parts out, whatever the byte
|
|
261
|
+
// boundaries). The host's own sink still receives it.
|
|
262
|
+
parts = appendReasoningPart(parts, delta, { ...opts, streamId: undefined });
|
|
263
|
+
},
|
|
264
|
+
upsertTool(toolCallId, patch) {
|
|
265
|
+
parts = upsertToolPart(parts, toolCallId, patch);
|
|
266
|
+
},
|
|
267
|
+
addSource(source) {
|
|
268
|
+
parts = [...parts, { type: 'source', source }];
|
|
269
|
+
},
|
|
270
|
+
parts: () => parts,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Drive two sinks from one call. `a` (the host's) goes first so the visible
|
|
275
|
+
* message updates in stream order. */
|
|
276
|
+
function teeSink(a: AssistantStreamSink, b: AssistantStreamSink): AssistantStreamSink {
|
|
277
|
+
return {
|
|
278
|
+
appendText(delta) {
|
|
279
|
+
a.appendText(delta);
|
|
280
|
+
b.appendText(delta);
|
|
281
|
+
},
|
|
282
|
+
appendReasoning(delta, opts) {
|
|
283
|
+
a.appendReasoning(delta, opts);
|
|
284
|
+
b.appendReasoning(delta, opts);
|
|
285
|
+
},
|
|
286
|
+
upsertTool(toolCallId, patch) {
|
|
287
|
+
a.upsertTool(toolCallId, patch);
|
|
288
|
+
b.upsertTool(toolCallId, patch);
|
|
289
|
+
},
|
|
290
|
+
addSource(source) {
|
|
291
|
+
a.addSource?.(source);
|
|
292
|
+
b.addSource?.(source);
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// ── The main loop ────────────────────────────────────────────────────────────
|
|
298
|
+
|
|
299
|
+
/** One id per `consumeModelStream` call, i.e. per provider response stream.
|
|
300
|
+
*
|
|
301
|
+
* It namespaces reasoning block indices. Anthropic restarts content-block
|
|
302
|
+
* indices at 0 on every message, and a tool loop reads several messages into ONE
|
|
303
|
+
* assistant turn, so without this round 2's block 0 merges into round 1's part
|
|
304
|
+
* and overwrites its verbatim `raw`. See `appendReasoningPart`.
|
|
305
|
+
*
|
|
306
|
+
* A monotonic counter, not a UUID: this never leaves the process, is compared
|
|
307
|
+
* only for equality against parts built in the same process, and a counter keeps
|
|
308
|
+
* the value short and reproducible in test output. */
|
|
309
|
+
let streamSeq = 0;
|
|
310
|
+
const nextStreamId = (): string => `wire-${++streamSeq}`;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Read one turn's worth of `ModelStreamChunk`s and drive `sink` with them.
|
|
314
|
+
*
|
|
315
|
+
* The sink is expected to be the kit's `AssistantStream`, which produces a NEW
|
|
316
|
+
* message object (and a new `parts` array) on every real mutation: that is what
|
|
317
|
+
* makes `<kai-thread>` re-render. This adapter calls the sink once per delta and
|
|
318
|
+
* never batches; batching is a host concern.
|
|
319
|
+
*
|
|
320
|
+
* This is also the escape hatch for a consumer who already has neutral chunks
|
|
321
|
+
* from somewhere else and does not need a WireFormat at all.
|
|
322
|
+
*/
|
|
323
|
+
export async function consumeModelStream(
|
|
324
|
+
chunks: AsyncIterable<ModelStreamChunk>,
|
|
325
|
+
sink: AssistantStreamSink,
|
|
326
|
+
opts: ConsumeOptions = {},
|
|
327
|
+
): Promise<ModelTurn> {
|
|
328
|
+
const label = opts.reasoningLabel ?? 'Thinking';
|
|
329
|
+
const streamId = nextStreamId();
|
|
330
|
+
const recorder = createPartsRecorder();
|
|
331
|
+
const out = teeSink(sink, recorder);
|
|
332
|
+
const tools = createToolCallAccumulator(out, opts);
|
|
333
|
+
|
|
334
|
+
let text = '';
|
|
335
|
+
let reasoning = '';
|
|
336
|
+
let reasoningChunks = 0;
|
|
337
|
+
let chunkCount = 0;
|
|
338
|
+
let finishReason: string | null = null;
|
|
339
|
+
let error: ModelTurn['error'];
|
|
340
|
+
let usage: ModelUsage | undefined;
|
|
341
|
+
const sources: MessageSource[] = [];
|
|
342
|
+
|
|
343
|
+
for await (const chunk of chunks) {
|
|
344
|
+
chunkCount++;
|
|
345
|
+
|
|
346
|
+
if (chunk.usage) usage = { ...usage, ...chunk.usage };
|
|
347
|
+
|
|
348
|
+
if (chunk.error) {
|
|
349
|
+
error = chunk.error;
|
|
350
|
+
if (chunk.finishReason) finishReason = chunk.finishReason;
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (chunk.text) {
|
|
355
|
+
text += chunk.text;
|
|
356
|
+
out.appendText(chunk.text);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// REWORK 1. The spike gated this whole branch on `if (chunk.reasoning)`,
|
|
360
|
+
// which is falsy for ''. That dropped exactly the payloads `raw` exists to
|
|
361
|
+
// preserve: an Anthropic redacted_thinking block (opaque, no readable text,
|
|
362
|
+
// and the docs require sending it back "including any blocks with empty
|
|
363
|
+
// thinking fields") and the assembled block emitted at content_block_stop
|
|
364
|
+
// after signature_delta. Both arrive with no text.
|
|
365
|
+
if (
|
|
366
|
+
chunk.reasoning !== undefined ||
|
|
367
|
+
chunk.reasoningRaw !== undefined ||
|
|
368
|
+
chunk.reasoningSignature !== undefined
|
|
369
|
+
) {
|
|
370
|
+
const delta = chunk.reasoning ?? '';
|
|
371
|
+
reasoning += delta;
|
|
372
|
+
// Only NON-EMPTY text counts as "reasoning streamed".
|
|
373
|
+
if (delta !== '') reasoningChunks++;
|
|
374
|
+
// `raw` and `signature` are spread in only when present: passing undefined
|
|
375
|
+
// would blank a value an earlier delta already established.
|
|
376
|
+
out.appendReasoning(delta, {
|
|
377
|
+
index: chunk.reasoningIndex ?? 0,
|
|
378
|
+
// Namespaces the index to THIS stream, so a later round reading into the
|
|
379
|
+
// same sink opens its own part instead of overwriting this one's `raw`.
|
|
380
|
+
streamId,
|
|
381
|
+
label,
|
|
382
|
+
...(chunk.reasoningRaw ? { raw: chunk.reasoningRaw } : {}),
|
|
383
|
+
...(chunk.reasoningSignature ? { signature: chunk.reasoningSignature } : {}),
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (chunk.toolCalls) for (const tc of chunk.toolCalls) tools.apply(tc);
|
|
388
|
+
|
|
389
|
+
if (chunk.sources) {
|
|
390
|
+
for (const source of chunk.sources) {
|
|
391
|
+
sources.push(source);
|
|
392
|
+
out.addSource?.(source);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (chunk.finishReason) finishReason = chunk.finishReason;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// A turn that received NOTHING has failed, and used to say so nowhere.
|
|
400
|
+
//
|
|
401
|
+
// The response was 200 and the body carried no parsable frame at all, which is
|
|
402
|
+
// exactly what a proxy route produces when it forwards a provider failure
|
|
403
|
+
// WITHOUT its status: an upstream 401 (no key — the most common first run)
|
|
404
|
+
// arrives as HTTP 200 whose body is a JSON error object mislabelled
|
|
405
|
+
// text/event-stream. `readModelStream` cannot throw for it (the response is
|
|
406
|
+
// ok) and the SSE decoder finds no `data:` line, so the turn resolved empty:
|
|
407
|
+
// no text, no error, no console output, nothing to debug. The route templates
|
|
408
|
+
// forward the status now; this is the second, independent guard, and it also
|
|
409
|
+
// covers a truncated body, a non-streaming completion returned by mistake, and
|
|
410
|
+
// an HTML proxy page.
|
|
411
|
+
if (chunkCount === 0 && !error) {
|
|
412
|
+
error = {
|
|
413
|
+
code: 'empty-stream',
|
|
414
|
+
message:
|
|
415
|
+
'The model stream produced no chunks. The response was 200 but nothing in its body parsed ' +
|
|
416
|
+
'as a stream frame — check the endpoint really sent Content-Type: text/event-stream and that ' +
|
|
417
|
+
'the request set stream: true. A route that forwards a provider error without its status ' +
|
|
418
|
+
'lands here: the body is a JSON error, not SSE.',
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// REWORK 3. `finishReason` is reported verbatim; `stopReason` is what the
|
|
423
|
+
// adapter itself branches on, so no OpenAI literal leaks into adapter logic.
|
|
424
|
+
const stopReason = normalizeStopReason(finishReason) ?? (error ? 'error' : undefined);
|
|
425
|
+
const toolCalls = tools.settle(stopReason, error?.message);
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
parts: recorder.parts(),
|
|
429
|
+
text,
|
|
430
|
+
reasoning,
|
|
431
|
+
toolCalls,
|
|
432
|
+
sources,
|
|
433
|
+
finishReason,
|
|
434
|
+
reasoningChunks,
|
|
435
|
+
chunks: chunkCount,
|
|
436
|
+
...(stopReason ? { stopReason } : {}),
|
|
437
|
+
...(error ? { error } : {}),
|
|
438
|
+
...(usage ? { usage } : {}),
|
|
439
|
+
};
|
|
440
|
+
}
|