@kitn.ai/ui 0.25.2 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/components/attachment-types.d.ts +46 -1
- package/dist/components/attachments.d.ts +31 -0
- package/dist/components/audio-visualizer/fit-scale.d.ts +50 -0
- package/dist/components/audio-visualizer/index.d.ts +15 -1
- package/dist/components/audio-visualizer/variant-bar.d.ts +24 -1
- package/dist/components/chat-thread.d.ts +3 -41
- package/dist/components/code-block.d.ts +22 -0
- package/dist/components/conversation-item.d.ts +54 -1
- package/dist/components/conversation-list.d.ts +65 -0
- package/dist/components/form.d.ts +42 -0
- package/dist/components/message.d.ts +7 -0
- package/dist/components/toast.d.ts +2 -1
- package/dist/components/voice-input.d.ts +8 -0
- package/dist/components/voice-output.d.ts +12 -1
- package/dist/components/workspace-shell.d.ts +87 -0
- package/dist/{create-tween-B5Y4Im8G.js → create-tween-BFT6c-Y8.js} +1 -1
- package/dist/{create-tween-4z1XYLkR.js → create-tween-CJNlzoaO.js} +1 -1
- package/dist/{create-tween-CUzOlaMS.js → create-tween-CQED4T0g.js} +1 -1
- package/dist/custom-elements.json +2931 -2572
- package/dist/diagnostics/hook.d.ts +81 -0
- package/dist/diagnostics/index.d.ts +7 -0
- package/dist/diagnostics/report-request.d.ts +61 -0
- package/dist/diagnostics.d.ts +5 -0
- package/dist/diagnostics.js +209 -0
- 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/autoloader.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-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-b5A8hlHs.js → Icon--Y8vsFal.js} +1 -1
- package/dist/elements/chunks/action-icons-D9u3OzYW.js +1 -0
- package/dist/elements/chunks/arrow-left-CVh1TeN7.js +1 -0
- package/dist/elements/chunks/{artifact-BSuPYFHY.js → artifact-BkYcfIlZ.js} +1 -1
- package/dist/elements/chunks/attachments-DwfNlubq.js +1 -0
- package/dist/elements/chunks/audio-visualizer-D7lB6mvR.js +1 -0
- package/dist/elements/chunks/{badge-TIMiJFKc.js → badge-Q0-Cmwzz.js} +1 -1
- package/dist/elements/chunks/{button-B9okqG73.js → button-flUbeufF.js} +1 -1
- package/dist/elements/chunks/{card-renderer-CSoTL_e1.js → card-renderer-3Dyl-FpN.js} +1 -1
- package/dist/elements/chunks/check-B5kBPKeo.js +1 -0
- package/dist/elements/chunks/chevron-down-DZ1GS39h.js +1 -0
- package/dist/elements/chunks/chevron-right-CvP7OUd2.js +1 -0
- package/dist/elements/chunks/{choice-card-CGMc3kek.js → choice-card-D4QUIOWZ.js} +1 -1
- package/dist/elements/chunks/circle-CDf9g6MQ.js +1 -0
- package/dist/elements/chunks/circle-check-CA47Nqbz.js +1 -0
- package/dist/elements/chunks/{circle-x-mhH7pOK2.js → circle-x-CJ_HVbC8.js} +1 -1
- package/dist/elements/chunks/code-block-BdLiTitd.js +1 -0
- package/dist/elements/chunks/collapsible-BzbXpPzr.js +1 -0
- package/dist/elements/chunks/{composer-CZZmtYOT.js → composer-Byve-n8K.js} +2 -2
- package/dist/elements/chunks/{confirm-card-BWjf2-xo.js → confirm-card-B0flE3MN.js} +1 -1
- package/dist/elements/chunks/context-VLIL3sJ-.js +1 -0
- package/dist/elements/chunks/controllable-DTwTIlwp.js +1 -0
- package/dist/elements/chunks/conversation-item-BDztezOG.js +1 -0
- package/dist/elements/chunks/copy-Nf-kqxXH.js +1 -0
- package/dist/elements/chunks/{create-tween-CUrM6D_o.js → create-tween-c4Z8TDYz.js} +2 -2
- package/dist/elements/chunks/default-input-C2iLFq0b.js +1 -0
- package/dist/elements/chunks/define-wjHR1MkU.js +1 -0
- package/dist/elements/chunks/disclosure-CcNbyLy0.js +1 -0
- package/dist/elements/chunks/{download-DnCyWFJE.js → download-C4V7tBdb.js} +1 -1
- package/dist/elements/chunks/dropdown-BgWTthUx.js +1 -0
- package/dist/elements/chunks/{ellipsis-CVxqfsDZ.js → ellipsis-7gU33mL2.js} +1 -1
- package/dist/elements/chunks/embed-D3KewsIJ.js +1 -0
- package/dist/elements/chunks/{external-link-DG2sRdQL.js → external-link-CSOEsNlx.js} +1 -1
- package/dist/elements/chunks/{file-text-D0J9F6M7.js → file-text-D1G8khle.js} +1 -1
- package/dist/elements/chunks/{file-tree-yGWNK0Js.js → file-tree-zvB2i7P9.js} +1 -1
- package/dist/elements/chunks/{folder-NV1pi_ud.js → folder-WW2LGLid.js} +1 -1
- package/dist/elements/chunks/form-CQirdC4W.js +1 -0
- package/dist/elements/chunks/hover-card-CJJkfHTK.js +1 -0
- package/dist/elements/chunks/icon-Cfgmf4fa.js +1 -0
- package/dist/elements/chunks/{info-C8r5Q6cc.js → info-DX3YMOt6.js} +1 -1
- package/dist/elements/chunks/input-BtW9U6Sq.js +1 -0
- package/dist/elements/chunks/{kbd-OGJDUT-O.js → kbd-LGXwuT_V.js} +1 -1
- package/dist/elements/chunks/{link-BdsfLB7b.js → link-2My69BqT.js} +1 -1
- package/dist/elements/chunks/{link-preview-Cf5KOP-i.js → link-preview-OQ9IEq0f.js} +1 -1
- package/dist/elements/chunks/{loader-B5QzK_8_.js → loader-iOxc_Tu-.js} +1 -1
- package/dist/elements/chunks/{markdown-DbozyYqa.js → markdown-DgBFaGAz.js} +1 -1
- package/dist/elements/chunks/{message-DtXKPqiR.js → message-VMZohrXx.js} +1 -1
- package/dist/elements/chunks/message-circle-CpXMM-_n.js +1 -0
- package/dist/elements/chunks/message-square-DxcWtETQ.js +1 -0
- package/dist/elements/chunks/message-tZmV2A3p.js +1 -0
- package/dist/elements/chunks/{minimize-2-SegJ_oku.js → minimize-2-DPIUKTrT.js} +1 -1
- package/dist/elements/chunks/{model-switcher-D7JrFFHp.js → model-switcher-DsT8tFB0.js} +1 -1
- package/dist/elements/chunks/{overlay-CV7z2YuX.js → overlay-DuiwdXjm.js} +1 -1
- package/dist/elements/chunks/{paperclip-Bg0ITU3b.js → paperclip-CRZVp4z_.js} +1 -1
- package/dist/elements/chunks/{progress-bar-D1_rzt6N.js → progress-bar-DNy66e_E.js} +1 -1
- package/dist/elements/chunks/{prompt-suggestion-D5Sm8xF1.js → prompt-suggestion-Bn64Nl7L.js} +1 -1
- package/dist/elements/chunks/reasoning-Emn9N4-T.js +1 -0
- package/dist/elements/chunks/{resizable-Dpzjlvp4.js → resizable-D181b4fM.js} +1 -1
- package/dist/elements/chunks/{rotate-cw-DzVDJOA1.js → rotate-cw-CPWe37D9.js} +1 -1
- package/dist/elements/chunks/{scroll-area-93A1bbeG.js → scroll-area-ClZkZxIk.js} +1 -1
- package/dist/elements/chunks/scroll-button-DPE-t3x_.js +1 -0
- package/dist/elements/chunks/{separator-DH2r_ZSZ.js → separator-CeJHQqLW.js} +1 -1
- package/dist/elements/chunks/{settings-BFMOd6yB.js → settings-CqaHgJF9.js} +1 -1
- package/dist/elements/chunks/{settings-group-B3VZKIcz.js → settings-group-CjqjLCOn.js} +1 -1
- package/dist/elements/chunks/share-CyfnNEqp.js +1 -0
- package/dist/elements/chunks/{skeleton-N8BuWYmf.js → skeleton-D8akBnKS.js} +1 -1
- package/dist/elements/chunks/slots-DaNjX0rc.js +1 -0
- package/dist/elements/chunks/{source-w9V0NnBn.js → source-UWCvmVak.js} +1 -1
- package/dist/elements/chunks/{star-DHvACxtm.js → star-DoHFAikQ.js} +1 -1
- package/dist/elements/chunks/store-Cjq0qwYN.js +1 -0
- package/dist/elements/chunks/{tasks-card-lPxoodlA.js → tasks-card-CmZ5xh6v.js} +1 -1
- package/dist/elements/chunks/{text-shimmer-BY_rI3s5.js → text-shimmer-DYn09zrz.js} +1 -1
- package/dist/elements/chunks/textarea-skiGHURM.js +1 -0
- package/dist/elements/chunks/{thumbs-up-DQe0hvkZ.js → thumbs-up-DX2Ijk3H.js} +1 -1
- package/dist/elements/chunks/{toast-store-VUZZBhzH.js → toast-store-CFnQpThX.js} +1 -1
- package/dist/elements/chunks/{tool-C1YtxwPu.js → tool-DKQ2D1DP.js} +1 -1
- package/dist/elements/chunks/{tooltip-BqwnFWhn.js → tooltip-B0G99y8w.js} +1 -1
- package/dist/elements/chunks/{triangle-alert-B6or7F5u.js → triangle-alert-F9cyUEyy.js} +1 -1
- package/dist/elements/chunks/{use-card-resolution-DJg32jGH.js → use-card-resolution-DoA2f_EU.js} +1 -1
- package/dist/elements/chunks/{variant-aurora-QXTZPyST.js → variant-aurora-qt6Zct8M.js} +2 -2
- package/dist/elements/chunks/variant-custom-tJp22_BQ.js +1 -0
- package/dist/{variant-wave-BwzrHD1s.js → elements/chunks/variant-wave-CaQ5ti39.js} +2 -2
- package/dist/elements/chunks/x-B6UnOiLw.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-item.d.ts +1 -0
- package/dist/elements/conversation-item.js +1 -0
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/default-input.d.ts +3 -3
- package/dist/elements/define.d.ts +27 -0
- package/dist/elements/diagnostic-events.d.ts +74 -0
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/dock.d.ts +1 -0
- package/dist/elements/dock.js +149 -0
- package/dist/elements/dropdown.d.ts +1 -0
- package/dist/elements/dropdown.js +1 -0
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/element-diagnostics.d.ts +81 -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 +2 -0
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +3 -3
- 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 +35 -3
- 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/voice-input.js +1 -1
- package/dist/elements/voice-output.js +1 -1
- package/dist/elements.d.ts +232 -148
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5456 -4931
- package/dist/index.server.js +4142 -3669
- package/dist/kai-provider.es.js +40 -40
- package/dist/kai.es.js +1 -1
- package/dist/llms/llms-full.txt +184 -69
- package/dist/llms/llms.txt +3 -3
- package/dist/mcp.es.js +2669 -546
- package/dist/primitives/use-resize-observer.d.ts +23 -0
- package/dist/primitives/use-speech-recognition.d.ts +8 -0
- package/dist/primitives/use-text-stream.d.ts +5 -0
- package/dist/react/index.d.ts +115 -266
- package/dist/react.js +247 -221
- package/dist/register-impl-DpE7icIb.js +293 -0
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/tool-defs.d.ts +35 -0
- package/dist/schemas.js +403 -310
- package/dist/{solid-BMuDmFo_.js → solid-BtdqNZI7.js} +8435 -7394
- package/dist/{solid-CGYNdLRR.js → solid-CnavwBye.js} +5987 -5035
- package/dist/solid.d.ts +5 -0
- package/dist/solid.js +210 -203
- package/dist/solid.server.js +210 -203
- package/dist/state/index.d.ts +4 -0
- package/dist/state/persistence.d.ts +51 -0
- package/dist/state/stream.d.ts +50 -0
- package/dist/state/threads.d.ts +71 -0
- package/dist/state.js +269 -169
- package/dist/types.d.ts +4 -2
- package/dist/ui/dock.d.ts +109 -0
- package/dist/ui/hover-card.d.ts +44 -0
- package/dist/{variant-aurora-DwSOfraQ.js → variant-aurora-B8II3-I1.js} +27 -27
- package/dist/{variant-aurora-wKvAQ5wp.js → variant-aurora-CMNWOSJR.js} +28 -28
- package/dist/{variant-aurora-XEkPT0_z.js → variant-aurora-CcuDMWwQ.js} +2 -2
- package/dist/{variant-custom-eFIg2XWQ.js → variant-custom-BdvYC7af.js} +40 -40
- package/dist/{variant-custom-BhS9wIpU.js → variant-custom-C714mqKl.js} +65 -65
- package/dist/variant-custom-L16JLLZy.js +1 -0
- package/dist/{elements/chunks/variant-wave-DDCwhLas.js → variant-wave-2S4La2Ka.js} +2 -2
- package/dist/{variant-wave-CbVsmgwT.js → variant-wave-D7ruiA1W.js} +25 -25
- package/dist/{variant-wave-CUWGx9Bn.js → variant-wave-ecwykeyL.js} +21 -21
- package/dist/wire/chunk.d.ts +47 -0
- package/dist/wire/diagnostics.d.ts +575 -0
- package/dist/wire/encode-probe.d.ts +70 -0
- package/dist/wire/encode.d.ts +24 -0
- package/dist/wire/index.d.ts +2 -0
- package/dist/wire/sse.d.ts +10 -2
- package/dist/wire.js +917 -480
- package/frameworks/react/index.tsx +132 -93
- package/llms-full.txt +184 -69
- package/llms.txt +3 -3
- package/package.json +26 -6
- package/src/agent-tooling/archetypes.ts +41 -9
- package/src/agent-tooling/catalog/README.md +676 -0
- package/src/agent-tooling/catalog/catalog-types.ts +231 -0
- package/src/agent-tooling/catalog/fabrications.ts +96 -0
- package/src/agent-tooling/catalog/invariants.ts +284 -0
- package/src/agent-tooling/catalog/labs-titles.ts +114 -0
- package/src/agent-tooling/catalog/scenarios.ts +87 -0
- package/src/agent-tooling/catalog/surfaces.ts +275 -0
- package/src/agent-tooling/integrations/anthropic.ts +7 -1
- package/src/agent-tooling/integrations/cloudflare.ts +43 -2
- package/src/agent-tooling/integrations/langgraph.ts +7 -1
- package/src/agent-tooling/integrations/mastra.ts +7 -1
- package/src/agent-tooling/integrations/mock.ts +92 -17
- package/src/agent-tooling/integrations/ollama.ts +7 -1
- package/src/agent-tooling/integrations/openai.ts +7 -1
- package/src/agent-tooling/integrations/openrouter.ts +7 -1
- package/src/agent-tooling/integrations/pi.ts +56 -1
- package/src/agent-tooling/integrations/vercel-ai-sdk.ts +7 -1
- package/src/agent-tooling/mcp/css-raw.d.ts +12 -0
- package/src/agent-tooling/mcp/manifest.ts +100 -1
- package/src/agent-tooling/mcp/server.ts +61 -5
- package/src/agent-tooling/mcp/tools/reference.ts +440 -4
- package/src/agent-tooling/mcp/tools/scaffold.ts +1834 -307
- package/src/agent-tooling/mcp/tools/theme.ts +146 -30
- package/src/agent-tooling/mcp/validate-args.ts +141 -0
- package/src/agent-tooling/registry.ts +19 -7
- package/src/agent-tooling/route-emit.ts +26 -2
- package/src/components/attachment-types.ts +47 -1
- package/src/components/attachments.tsx +169 -22
- package/src/components/audio-visualizer/fit-scale.ts +112 -0
- package/src/components/audio-visualizer/index.tsx +80 -3
- package/src/components/audio-visualizer/variant-aurora.tsx +10 -4
- package/src/components/audio-visualizer/variant-bar.tsx +38 -6
- package/src/components/audio-visualizer/variant-custom.tsx +8 -2
- package/src/components/audio-visualizer/variant-grid.tsx +16 -11
- package/src/components/audio-visualizer/variant-radial.tsx +15 -10
- package/src/components/audio-visualizer/variant-wave.tsx +7 -2
- package/src/components/chat-container.tsx +4 -1
- package/src/components/chat-thread.tsx +36 -7
- package/src/components/code-block.tsx +79 -3
- package/src/components/composer.tsx +8 -1
- package/src/components/conversation-item.tsx +114 -4
- package/src/components/conversation-list.tsx +212 -12
- package/src/components/embed.tsx +4 -0
- package/src/components/form.tsx +41 -14
- package/src/components/message.tsx +75 -5
- package/src/components/response-stream.tsx +13 -7
- package/src/components/screen.tsx +5 -0
- package/src/components/scroll-button.tsx +7 -0
- package/src/components/toast.tsx +11 -5
- package/src/components/voice-input.tsx +35 -3
- package/src/components/voice-output.tsx +46 -6
- package/src/components/workspace-shell.tsx +285 -0
- package/src/diagnostics/hook.ts +352 -0
- package/src/diagnostics/index.ts +82 -0
- package/src/diagnostics/report-request.ts +296 -0
- package/src/elements/attachments.tsx +6 -7
- package/src/elements/audio-visualizer.tsx +48 -4
- package/src/elements/chat-workspace.tsx +159 -287
- package/src/elements/chat.tsx +12 -9
- package/src/elements/code-block.tsx +19 -1
- package/src/elements/command.tsx +4 -1
- package/src/elements/compiled.css +1 -1
- package/src/elements/conversation-item.tsx +80 -0
- package/src/elements/conversation-list.tsx +60 -11
- package/src/elements/default-input.tsx +5 -5
- package/src/elements/define.tsx +168 -8
- package/src/elements/diagnostic-events.ts +114 -0
- package/src/elements/dialog.tsx +47 -0
- package/src/elements/disclosure.ts +34 -12
- package/src/elements/dock.tsx +204 -0
- package/src/elements/dropdown.tsx +123 -0
- package/src/elements/element-diagnostics.ts +392 -0
- package/src/elements/element-manifest.json +12 -0
- package/src/elements/element-meta.json +672 -439
- package/src/elements/element-nonscalar.json +170 -0
- package/src/elements/element-types.d.ts +232 -148
- package/src/elements/menu.tsx +13 -0
- package/src/elements/prompt-dock.tsx +2 -1
- package/src/elements/prompt-input.tsx +8 -8
- package/src/elements/register-impl.ts +46 -0
- package/src/elements/register.ts +28 -0
- package/src/elements/resizable.tsx +52 -25
- package/src/elements/scroll-button.tsx +5 -0
- package/src/elements/slots.ts +107 -12
- package/src/elements/styles.css +30 -6
- package/src/elements/thinking-bar.tsx +16 -7
- package/src/elements/toast.tsx +3 -0
- package/src/elements/tool.tsx +26 -5
- package/src/elements/voice-input.tsx +11 -2
- package/src/elements/voice-output.tsx +16 -3
- package/src/index.ts +2 -0
- package/src/primitives/use-resize-observer.ts +33 -0
- package/src/primitives/use-speech-recognition.ts +12 -1
- package/src/primitives/use-stick-to-bottom.ts +34 -2
- package/src/primitives/use-text-stream.ts +33 -2
- package/src/remote/provider-runtime.ts +6 -3
- package/src/schemas/index.ts +1 -0
- package/src/schemas/tool-defs.ts +297 -6
- package/src/solid.ts +5 -0
- package/src/state/index.ts +12 -0
- package/src/state/persistence.ts +180 -0
- package/src/state/stream.ts +71 -4
- package/src/state/threads.ts +142 -0
- package/src/types.ts +4 -2
- package/src/ui/dialog.tsx +6 -0
- package/src/ui/dock.tsx +445 -0
- package/src/ui/dropdown.tsx +67 -23
- package/src/ui/hover-card.tsx +156 -6
- package/src/ui/input.tsx +29 -10
- package/src/ui/resizable.tsx +11 -0
- package/src/wire/chunk.ts +47 -0
- package/src/wire/consume.ts +185 -26
- package/src/wire/diagnostics.ts +727 -0
- package/src/wire/encode-probe.ts +214 -0
- package/src/wire/encode.ts +267 -14
- package/src/wire/formats/anthropic.ts +8 -1
- package/src/wire/formats/openai.ts +5 -0
- package/src/wire/index.ts +22 -0
- package/src/wire/read.ts +250 -6
- package/src/wire/sse.ts +16 -3
- package/dist/elements/chunks/action-icons-DW9muWrY.js +0 -1
- package/dist/elements/chunks/arrow-left-iJVnQmxA.js +0 -1
- package/dist/elements/chunks/attachments-CL6TaqIb.js +0 -1
- package/dist/elements/chunks/audio-visualizer-BuUqQmaO.js +0 -1
- package/dist/elements/chunks/chat-thread-DxoKaStk.js +0 -1
- package/dist/elements/chunks/check-MdLzbxrm.js +0 -1
- package/dist/elements/chunks/chevron-down-MK7IzDqe.js +0 -1
- package/dist/elements/chunks/chevron-right-C5jXhmtV.js +0 -1
- package/dist/elements/chunks/circle-BG7VkPep.js +0 -1
- package/dist/elements/chunks/circle-check-GqvO2UBs.js +0 -1
- package/dist/elements/chunks/code-block-ClhQO9RW.js +0 -1
- package/dist/elements/chunks/collapsible-DBcBkFPh.js +0 -1
- package/dist/elements/chunks/context-hKjNqiyQ.js +0 -1
- package/dist/elements/chunks/conversation-list-CsGJjSKG.js +0 -1
- package/dist/elements/chunks/default-input-B1Mem5wu.js +0 -1
- package/dist/elements/chunks/define-B_tXMYRm.js +0 -1
- package/dist/elements/chunks/disclosure-DAoYOKew.js +0 -1
- package/dist/elements/chunks/dropdown-CmNna5lT.js +0 -1
- package/dist/elements/chunks/embed-CjAD2jSi.js +0 -1
- package/dist/elements/chunks/form-Cp8Y00Wn.js +0 -1
- package/dist/elements/chunks/hover-card-6KtXpLwT.js +0 -1
- package/dist/elements/chunks/icon-97R8SR2p.js +0 -1
- package/dist/elements/chunks/input-DrseEjhp.js +0 -1
- package/dist/elements/chunks/message-CE4k-qaj.js +0 -1
- package/dist/elements/chunks/message-square-C9ovmtOo.js +0 -1
- package/dist/elements/chunks/reasoning-BrxQfpfI.js +0 -1
- package/dist/elements/chunks/scroll-button-CrTo7wm4.js +0 -1
- package/dist/elements/chunks/share-DgNbkJKY.js +0 -1
- package/dist/elements/chunks/slots-CIw9RlAe.js +0 -1
- package/dist/elements/chunks/store-HIPGhNz9.js +0 -1
- package/dist/elements/chunks/textarea-DPfuhswM.js +0 -1
- package/dist/elements/chunks/variant-custom-D5ZlmziF.js +0 -1
- package/dist/elements/chunks/video-Bb86fVYI.js +0 -1
- package/dist/elements/chunks/x-BOhjBeOd.js +0 -1
- package/dist/primitives/card-validate-generator.testlib.d.ts +0 -20
- package/dist/register-impl-DGmGzrEM.js +0 -145
- package/dist/variant-custom-MkTNTycd.js +0 -1
- package/src/primitives/card-validate-generator.testlib.ts +0 -38
|
@@ -35,9 +35,22 @@ import type { Integration } from '../types';
|
|
|
35
35
|
* Swap `integration` for a real provider (openai, anthropic, openrouter, ollama,
|
|
36
36
|
* …) when ready.
|
|
37
37
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* THE ROUTE IS OPTIONAL, AND IT EXISTS ANYWAY (rung-2 finding G-04, recurring
|
|
39
|
+
* since rung 1). Two clean-room builders in a row were told "replies come from a
|
|
40
|
+
* local dev endpoint that streams a mocked response" and had to hand-invent the
|
|
41
|
+
* server side, because this was the one integration whose block (2) held prose
|
|
42
|
+
* where every other integration ships code. So `webRoute` below is the mock's
|
|
43
|
+
* server half: the SAME `createMockResponder()` frames, written to the wire by a
|
|
44
|
+
* portable `chatHandler(Request) => Response` that the scaffolder wraps with the
|
|
45
|
+
* identical per-framework adapters a real integration gets (Vite dev middleware
|
|
46
|
+
* for the SPA frameworks, POST exports for next/svelte/tanstack, the
|
|
47
|
+
* Express/Worker/Angular hosts). Nothing about the emitted app REQUIRES it — the
|
|
48
|
+
* front end streams locally and never fetches — which the emitted block states,
|
|
49
|
+
* along with the one-expression swap that points onSubmit at it.
|
|
50
|
+
*
|
|
51
|
+
* `routeTemplates` stays empty: the handler is web-standard, so there is no
|
|
52
|
+
* framework whose declaration it cannot be wrapped in. `category: 'mock'` keeps
|
|
53
|
+
* it out of the real provider/gateway/framework/harness catalogs.
|
|
41
54
|
*/
|
|
42
55
|
const mock: Integration = {
|
|
43
56
|
id: 'mock',
|
|
@@ -47,26 +60,88 @@ const mock: Integration = {
|
|
|
47
60
|
streamFormat: 'native',
|
|
48
61
|
envVars: [],
|
|
49
62
|
routeTemplates: {},
|
|
63
|
+
webRoute: `import { createMockResponder } from '@kitn.ai/ui/state';
|
|
64
|
+
|
|
65
|
+
// The kit's own mock responder — no provider, no key, no upstream. MODULE
|
|
66
|
+
// scope, not per-request: the responder owns the cursor into its canned
|
|
67
|
+
// replies, so rebuilding it per turn would answer with the first one forever.
|
|
68
|
+
const mockResponse = createMockResponder();
|
|
69
|
+
|
|
70
|
+
async function chatHandler(request: Request): Promise<Response> {
|
|
71
|
+
// The responder cycles its replies whatever you send, so the prompt is a
|
|
72
|
+
// courtesy — but the body is still read exactly the way a real route reads
|
|
73
|
+
// it, so swapping this handler for a provider's changes nothing upstream of
|
|
74
|
+
// this file.
|
|
75
|
+
let chatBody: ChatRequestBody;
|
|
76
|
+
try {
|
|
77
|
+
chatBody = await readChatRequest(request);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
return toChatErrorResponse(error);
|
|
80
|
+
}
|
|
81
|
+
const { messages } = chatBody;
|
|
82
|
+
const last = [...messages].reverse().find((m) => m.role === 'user');
|
|
83
|
+
const prompt = last && typeof last.content === 'string' ? last.content : '';
|
|
84
|
+
|
|
85
|
+
// createMockResponder() yields COMPLETE OpenAI chat-completions SSE frames —
|
|
86
|
+
// the same strings the front end's local preview streams — so this route only
|
|
87
|
+
// writes them out verbatim. No framing is built here; the browser parses them
|
|
88
|
+
// with readOpenAIStream from @kitn.ai/ui/wire, exactly as it would a real
|
|
89
|
+
// route's response. Every frame is marked as a mock (a ': kai-mock' banner, a
|
|
90
|
+
// _kai_mock field, model 'kai-mock', zero usage).
|
|
91
|
+
const encoder = new TextEncoder();
|
|
92
|
+
let open = true;
|
|
93
|
+
const body = new ReadableStream<Uint8Array>({
|
|
94
|
+
async start(controller) {
|
|
95
|
+
for await (const frame of mockResponse(prompt)) {
|
|
96
|
+
// The browser hangs up when the user asks a new question mid-answer;
|
|
97
|
+
// stop producing frames rather than writing into a cancelled stream.
|
|
98
|
+
if (!open) return;
|
|
99
|
+
controller.enqueue(encoder.encode(frame));
|
|
100
|
+
}
|
|
101
|
+
controller.close();
|
|
102
|
+
},
|
|
103
|
+
cancel() {
|
|
104
|
+
open = false;
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return new Response(body, {
|
|
109
|
+
status: 200,
|
|
110
|
+
headers: {
|
|
111
|
+
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
112
|
+
// no-transform stops a proxy buffering the stream into one blob.
|
|
113
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
114
|
+
Connection: 'keep-alive',
|
|
115
|
+
// A proxy in front of a preview build would otherwise buffer the whole
|
|
116
|
+
// stream and the reply would land all at once instead of streaming.
|
|
117
|
+
'X-Accel-Buffering': 'no',
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}`,
|
|
50
121
|
streamMapping:
|
|
51
|
-
"No backend and no provider, but a real wire. createMockResponder() from @kitn.ai/ui/state yields canned SSE frames and the scaffold reads them with readOpenAIStream from @kitn.ai/ui/wire, on the same code path a real route's response takes: same reader, same part folding, same abort handling. The frames carry the OpenAI chat-completions shape because the mock stands in for your /api/chat ROUTE, not for a provider (every other integration here re-frames its provider to that shape server-side), so going live replaces ONE expression: mockResponse(value) becomes the awaited response from your own chat route, and nothing else in the handler changes. Nothing here can be mistaken for a real turn: the stream opens with a ': kai-mock' SSE comment, every frame carries a _kai_mock field naming createMockResponder, model reports as 'kai-mock' (no provider serves it), and usage is all zeros.",
|
|
122
|
+
"No backend and no provider, but a real wire. createMockResponder() from @kitn.ai/ui/state yields canned SSE frames and the scaffold reads them with readOpenAIStream from @kitn.ai/ui/wire, on the same code path a real route's response takes: same reader, same part folding, same abort handling. The frames carry the OpenAI chat-completions shape because the mock stands in for your /api/chat ROUTE, not for a provider (every other integration here re-frames its provider to that shape server-side), so going live replaces ONE expression: mockResponse(value) becomes the awaited response from your own chat route, and nothing else in the handler changes. Nothing here can be mistaken for a real turn: the stream opens with a ': kai-mock' SSE comment, every frame carries a _kai_mock field naming createMockResponder, model reports as 'kai-mock' (no provider serves it), and usage is all zeros. Block (2) serves the same frames over HTTP as an OPTIONAL route, so the /api/chat seam can be stood up before any provider exists.",
|
|
52
123
|
runNote:
|
|
53
|
-
'No backend or API key needed: the reply is generated in the browser and parsed by the same reader a real route feeds. Run the front-end as-is; swap `integration` for a real provider (openai, anthropic, openrouter, ollama) when ready, and the emitted handler differs by one expression.',
|
|
124
|
+
'No backend or API key needed: the reply is generated in the browser and parsed by the same reader a real route feeds. Run the front-end as-is; swap `integration` for a real provider (openai, anthropic, openrouter, ollama) when ready, and the emitted handler differs by one expression. Block (2) is the OPTIONAL server half: the same mock frames served over HTTP, for standing up the /api/chat seam before any provider exists.',
|
|
54
125
|
docsSlug: 'integrations/mock',
|
|
55
|
-
// Nothing:
|
|
56
|
-
//
|
|
57
|
-
//
|
|
126
|
+
// Nothing: the front end makes no HTTP request at all, and the optional route
|
|
127
|
+
// reads only `messages` — and only as a courtesy prompt the responder is free
|
|
128
|
+
// to ignore. There is no model to pick and no tools array anything would run.
|
|
58
129
|
forwardsFromClient: [],
|
|
59
|
-
// Nothing to install:
|
|
130
|
+
// Nothing to install: the route's one import is the kit itself, which every
|
|
131
|
+
// scaffold already depends on (registry.test.ts pins deps.npm to the route's
|
|
132
|
+
// imports minus @kitn.ai/ui).
|
|
60
133
|
deps: { npm: [], pip: [] },
|
|
61
|
-
// The other 'frontend-safe' entry
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
// precisely why it still has to SAY so rather than be left blank.
|
|
134
|
+
// The other 'frontend-safe' entry. The route now exists (G-04) but changes
|
|
135
|
+
// nothing here: it holds no credential, sends no auth header and reaches no
|
|
136
|
+
// upstream, so there is still nothing a public bundle could give away. The
|
|
137
|
+
// schema's own refinement re-checks that claim against the route source.
|
|
66
138
|
keyExposure: 'frontend-safe',
|
|
67
|
-
// Nothing
|
|
68
|
-
//
|
|
69
|
-
// group of create-kai's gateway prompt
|
|
139
|
+
// Nothing to supply out of band: no upstream, no process, no runtime. The
|
|
140
|
+
// route is optional — the emitted app streams locally without it — which is
|
|
141
|
+
// what keeps mock in the "No backend" group of create-kai's gateway prompt
|
|
142
|
+
// (listGatewayGroups derives that from frontend-safe + outOfBand 'none', not
|
|
143
|
+
// from the absence of a route, precisely because this route is one nobody
|
|
144
|
+
// NEEDS).
|
|
70
145
|
outOfBand: 'none',
|
|
71
146
|
};
|
|
72
147
|
|
|
@@ -20,7 +20,13 @@ const ollama: Integration = {
|
|
|
20
20
|
// The model is pinned here, not sent by the browser. tools IS forwarded:
|
|
21
21
|
// it is undefined unless the front end declared any, and JSON.stringify
|
|
22
22
|
// drops it, so the same handler serves a tool archetype and a plain chat.
|
|
23
|
-
|
|
23
|
+
let chatBody: ChatRequestBody;
|
|
24
|
+
try {
|
|
25
|
+
chatBody = await readChatRequest(request);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return toChatErrorResponse(error);
|
|
28
|
+
}
|
|
29
|
+
const { messages, tools } = chatBody;
|
|
24
30
|
|
|
25
31
|
const upstream = await fetch('http://localhost:11434/v1/chat/completions', {
|
|
26
32
|
method: 'POST',
|
|
@@ -14,7 +14,13 @@ const openai: Integration = {
|
|
|
14
14
|
// Both model and tools come from the browser. \`tools\` is undefined unless the
|
|
15
15
|
// front end declared any, and JSON.stringify drops it, so the same handler
|
|
16
16
|
// serves a tool archetype and a plain chat.
|
|
17
|
-
|
|
17
|
+
let chatBody: ChatRequestBody;
|
|
18
|
+
try {
|
|
19
|
+
chatBody = await readChatRequest(request);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
return toChatErrorResponse(error);
|
|
22
|
+
}
|
|
23
|
+
const { model, messages, tools } = chatBody;
|
|
18
24
|
|
|
19
25
|
const upstream = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
20
26
|
method: 'POST',
|
|
@@ -14,7 +14,13 @@ const openrouter: Integration = {
|
|
|
14
14
|
webRoute: `async function chatHandler(request: Request): Promise<Response> {
|
|
15
15
|
// tools is undefined unless the front end sent one; JSON.stringify drops it,
|
|
16
16
|
// so the same handler serves a tool archetype and a plain chat.
|
|
17
|
-
|
|
17
|
+
let chatBody: ChatRequestBody;
|
|
18
|
+
try {
|
|
19
|
+
chatBody = await readChatRequest(request);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
return toChatErrorResponse(error);
|
|
22
|
+
}
|
|
23
|
+
const { model, messages, tools } = chatBody;
|
|
18
24
|
|
|
19
25
|
const upstream = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
|
20
26
|
method: 'POST',
|
|
@@ -20,9 +20,64 @@ import type { OpenAIWireMessage } from '@kitn.ai/ui/wire';
|
|
|
20
20
|
const app = express();
|
|
21
21
|
app.use(express.json());
|
|
22
22
|
|
|
23
|
+
class ChatRequestError extends Error {
|
|
24
|
+
constructor(readonly status: number, message: string) { super(message); }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Narrow the JSON body once, at the edge. A bare GET or a missing messages
|
|
28
|
+
* array is a ChatRequestError with a status — NEVER an unhandled throw
|
|
29
|
+
* (findings F-10). Malformed JSON is rejected upstream by express.json(),
|
|
30
|
+
* which the error handler below turns into a JSON 400. */
|
|
31
|
+
function readChatRequest(req: express.Request): { messages: OpenAIWireMessage[] } {
|
|
32
|
+
if (req.method !== 'POST') {
|
|
33
|
+
throw new ChatRequestError(405, \`Method \${req.method} not allowed — POST /api/chat.\`);
|
|
34
|
+
}
|
|
35
|
+
const body = (req.body ?? {}) as { messages?: OpenAIWireMessage[] };
|
|
36
|
+
if (!Array.isArray(body.messages)) {
|
|
37
|
+
throw new ChatRequestError(400, 'Request body must carry a messages array.');
|
|
38
|
+
}
|
|
39
|
+
return body as { messages: OpenAIWireMessage[] };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Map a guard rejection to the response its status demands; rethrow anything
|
|
43
|
+
* else — an unexpected error should be loud, not laundered into a 400. */
|
|
44
|
+
function toChatErrorResponse(error: unknown, res: express.Response): void {
|
|
45
|
+
if (error instanceof ChatRequestError) {
|
|
46
|
+
res.status(error.status).json({ error: { message: error.message } });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// express.json() rejects a malformed body at the MIDDLEWARE level, before any
|
|
53
|
+
// handler runs — without this it surfaces as Express's default HTML error page.
|
|
54
|
+
app.use(
|
|
55
|
+
(
|
|
56
|
+
err: { status?: number; message?: string },
|
|
57
|
+
_req: express.Request,
|
|
58
|
+
res: express.Response,
|
|
59
|
+
_next: express.NextFunction,
|
|
60
|
+
) => {
|
|
61
|
+
res.status(err?.status ?? 500).json({ error: { message: err?.message ?? 'Request failed.' } });
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// A bare GET is a 405 with the method named, not Express's default 404 —
|
|
66
|
+
// the resource exists, the verb is wrong.
|
|
67
|
+
app.get('/api/chat', (_req, res) => {
|
|
68
|
+
res.status(405).json({ error: { message: 'Method not allowed — POST /api/chat.' } });
|
|
69
|
+
});
|
|
70
|
+
|
|
23
71
|
// POST /api/chat: bridge a Pi RPC session to the browser as SSE.
|
|
24
72
|
app.post('/api/chat', (req, res) => {
|
|
25
|
-
|
|
73
|
+
let chatBody: { messages: OpenAIWireMessage[] };
|
|
74
|
+
try {
|
|
75
|
+
chatBody = readChatRequest(req);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
toChatErrorResponse(error, res);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const { messages } = chatBody;
|
|
26
81
|
const last = messages.at(-1)?.content;
|
|
27
82
|
// \`content\` is a plain string until the turn carries an attachment, at which
|
|
28
83
|
// point it is an ARRAY of content parts. Pi's RPC mode takes a TEXT prompt and
|
|
@@ -169,7 +169,13 @@ const FINISH_REASONS: Record<string, string> = {
|
|
|
169
169
|
};
|
|
170
170
|
|
|
171
171
|
async function chatHandler(request: Request): Promise<Response> {
|
|
172
|
-
|
|
172
|
+
let chatBody: ChatRequestBody;
|
|
173
|
+
try {
|
|
174
|
+
chatBody = await readChatRequest(request);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
return toChatErrorResponse(error);
|
|
177
|
+
}
|
|
178
|
+
const { messages, tools } = chatBody;
|
|
173
179
|
const toolSet = toToolSet(tools);
|
|
174
180
|
const prompt = toModelMessages(messages);
|
|
175
181
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// `*.css?raw` — Vite inlines the file's TEXT as a string at build time.
|
|
2
|
+
//
|
|
3
|
+
// tools/theme.ts uses it to read the real `--kai-*` token names out of
|
|
4
|
+
// theme.css instead of restating them. The declaration lives here rather than
|
|
5
|
+
// coming from `vite/client` because tsconfig.mcp.json deliberately runs with
|
|
6
|
+
// `types: ["node"]` and no DOM — this is a Node bundle that emits HTML as
|
|
7
|
+
// strings. One narrow module pattern is cheaper than pulling the whole browser
|
|
8
|
+
// client typing into that pass.
|
|
9
|
+
declare module '*.css?raw' {
|
|
10
|
+
const content: string;
|
|
11
|
+
export default content;
|
|
12
|
+
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* `cardTagForType` for the rule and for what happens when an eighth card type lands.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
20
|
+
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
22
|
import { join, dirname, resolve } from 'node:path';
|
|
23
23
|
// The package's own public entry, by the same specifier the scaffolder tells a
|
|
@@ -236,6 +236,105 @@ export function listElements(): string[] {
|
|
|
236
236
|
.sort();
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
// ── Per-element entry map ─────────────────────────────────────────────────────
|
|
240
|
+
//
|
|
241
|
+
// A STATIC import, not an fs read off resolveManifestPath's dual-context pattern:
|
|
242
|
+
// element-manifest.json (unlike custom-elements.json) is never copied into dist/,
|
|
243
|
+
// so an fs read relative to this module's own URL would resolve in the vitest/source
|
|
244
|
+
// context and 404 in the bundled dist/mcp.es.js. A static import sidesteps the gap
|
|
245
|
+
// entirely — Rollup inlines the JSON at build time, so the bundled bin carries the
|
|
246
|
+
// data with no runtime file dependency. NAMED import (not the whole module), matching
|
|
247
|
+
// the same tradeoff element-diagnostics.ts already made for this file: a default
|
|
248
|
+
// import would pull `files` in too for 0 benefit here.
|
|
249
|
+
import { tags as ELEMENT_ENTRY_TAGS } from '../../elements/element-manifest.json';
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The per-element entry basename for a tag, e.g. 'kai-chat' -> 'chat'.
|
|
253
|
+
*
|
|
254
|
+
* Read from element-manifest.json's `tags` map rather than derived by stripping
|
|
255
|
+
* the `kai-` prefix: TEN of the eighty elements do not match that derivation
|
|
256
|
+
* (`kai-conversations` -> `conversation-list`), so a derived path would emit a
|
|
257
|
+
* broken import for them.
|
|
258
|
+
*/
|
|
259
|
+
export function entryForTag(tag: string): string | undefined {
|
|
260
|
+
return (ELEMENT_ENTRY_TAGS as Record<string, string>)[tag];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* The per-element entry basename for a tag the register-all bundle does NOT
|
|
265
|
+
* carry, e.g. 'kai-remote' -> 'remote'. `undefined` for every tag `entryForTag`
|
|
266
|
+
* already answers, and for a tag no built module registers.
|
|
267
|
+
*
|
|
268
|
+
* WHY THIS EXISTS. `entryForTag` reads element-manifest.json, which is generated
|
|
269
|
+
* from the import list in register-impl.ts — so an opt-in element is absent from
|
|
270
|
+
* it by construction, and the reference had nothing to name. It said "find the
|
|
271
|
+
* entry point in the package's published exports", which is the one place in the
|
|
272
|
+
* reference where "how to make this element exist" does not answer itself.
|
|
273
|
+
*
|
|
274
|
+
* WHY IT IS READ AND NOT DERIVED. Stripping `kai-` is wrong for ten of the eighty
|
|
275
|
+
* elements, so guessing here risks exactly the broken import this tool exists to
|
|
276
|
+
* catch. The fact is stated in vite.config.elements.ts, which adds an explicit
|
|
277
|
+
* entry for each opt-in element precisely "so `@kitn.ai/ui/elements/remote`
|
|
278
|
+
* resolves to a real dist file" — but a vite config is not shipped in the
|
|
279
|
+
* package, so the runtime reads that intent where it LANDS: dist/elements/. A
|
|
280
|
+
* candidate is a built module that element-manifest.json does not already claim
|
|
281
|
+
* (plus the two non-element entries the same build emits), and it is matched to a
|
|
282
|
+
* tag by the tag literal it registers. So the answer is the built artifact's,
|
|
283
|
+
* confirmed against the tag rather than assumed from a filename.
|
|
284
|
+
*
|
|
285
|
+
* dist/elements/ is a sibling of the manifest in both contexts — the bundled bin
|
|
286
|
+
* (dist/mcp.es.js) and vitest-over-source, which resolveManifestPath already
|
|
287
|
+
* normalises — so this needs no second resolution strategy. A missing or
|
|
288
|
+
* unreadable directory yields `undefined` rather than throwing: the caller's
|
|
289
|
+
* fallback is the honest "this is opt-in" paragraph it already prints.
|
|
290
|
+
*/
|
|
291
|
+
export function optInEntryForTag(tag: string): string | undefined {
|
|
292
|
+
if (entryForTag(tag)) return undefined;
|
|
293
|
+
return optInEntries().get(tag);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Not element entries: the register-all barrel and the DOM autoloader, both
|
|
297
|
+
* emitted into dist/elements/ by the same per-element build. */
|
|
298
|
+
const NON_ELEMENT_ENTRIES = new Set(['index', 'autoloader']);
|
|
299
|
+
|
|
300
|
+
let _optInEntries: Map<string, string> | undefined;
|
|
301
|
+
|
|
302
|
+
function optInEntries(): Map<string, string> {
|
|
303
|
+
if (_optInEntries) return _optInEntries;
|
|
304
|
+
_optInEntries = new Map();
|
|
305
|
+
|
|
306
|
+
const dir = join(dirname(resolveManifestPath()), 'elements');
|
|
307
|
+
let candidates: string[];
|
|
308
|
+
try {
|
|
309
|
+
const registered = new Set(Object.values(ELEMENT_ENTRY_TAGS as Record<string, string>));
|
|
310
|
+
candidates = readdirSync(dir)
|
|
311
|
+
.filter((f) => f.endsWith('.js'))
|
|
312
|
+
.map((f) => f.slice(0, -'.js'.length))
|
|
313
|
+
.filter((base) => !registered.has(base) && !NON_ELEMENT_ENTRIES.has(base));
|
|
314
|
+
} catch {
|
|
315
|
+
return _optInEntries;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Only the tags element-manifest.json does not already answer for. Narrow on
|
|
319
|
+
// purpose: an element module may mention a tag it merely renders, and matching
|
|
320
|
+
// against the whole tag list would let that be read as a registration.
|
|
321
|
+
const unclaimed = listElements().filter((t) => !entryForTag(t));
|
|
322
|
+
|
|
323
|
+
for (const base of candidates) {
|
|
324
|
+
let code: string;
|
|
325
|
+
try {
|
|
326
|
+
code = readFileSync(join(dir, `${base}.js`), 'utf-8');
|
|
327
|
+
} catch {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
for (const tag of unclaimed) {
|
|
331
|
+
if (!_optInEntries.has(tag) && code.includes(`"${tag}"`)) _optInEntries.set(tag, base);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return _optInEntries;
|
|
336
|
+
}
|
|
337
|
+
|
|
239
338
|
// ── Which elements have anything to do with cards ────────────────────────────
|
|
240
339
|
//
|
|
241
340
|
// Two different populations, and conflating them would be the "attaches card
|
|
@@ -3,15 +3,65 @@ import {
|
|
|
3
3
|
CallToolRequestSchema,
|
|
4
4
|
ListToolsRequestSchema,
|
|
5
5
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
6
7
|
import { z } from 'zod';
|
|
7
8
|
import { reference } from './tools/reference';
|
|
8
9
|
import { scaffold } from './tools/scaffold';
|
|
9
10
|
import { theme } from './tools/theme';
|
|
10
11
|
import { debug } from './tools/debug';
|
|
11
12
|
import type { Tool } from './tools/types';
|
|
13
|
+
import { validateToolArgs } from './validate-args';
|
|
12
14
|
|
|
13
15
|
const tools: Tool[] = [reference, scaffold, theme, debug];
|
|
14
16
|
|
|
17
|
+
const PACKAGE_NAME = '@kitn.ai/ui';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The `serverInfo` every MCP harness reads back on `initialize`. READ, NOT TYPED.
|
|
21
|
+
*
|
|
22
|
+
* This was a hand-typed `{ name: '@kitn.ai/ui', version: '0.15.0' }` and the kit kept
|
|
23
|
+
* releasing past it, so every harness that connected was told a version ten minors
|
|
24
|
+
* stale — the same "derive it, don't type it" failure as the CDN pins, minus their
|
|
25
|
+
* lint. Nothing here is allowed to restate the version: `server.test.ts` reads
|
|
26
|
+
* package.json off disk itself and compares.
|
|
27
|
+
*
|
|
28
|
+
* RESOLVED BY THE PACKAGE'S OWN NAME, not by path arithmetic. This module runs from
|
|
29
|
+
* two places — `<package>/src/agent-tooling/mcp/server.ts` under vitest, and inlined
|
|
30
|
+
* into `<package>/dist/mcp.es.js` for the bin — which sit at different depths, so a
|
|
31
|
+
* relative hop would need one rule per context (what manifest.ts has to do, because
|
|
32
|
+
* `dist/custom-elements.json` is a build artifact with no name to ask for). A
|
|
33
|
+
* self-reference has no depth to get wrong: Node resolves `@kitn.ai/ui/package.json`
|
|
34
|
+
* against the nearest package.json whose `name` matches, which is this package in
|
|
35
|
+
* both contexts and in an install. It also never escapes into a neighbouring
|
|
36
|
+
* checkout the way a parent-directory search can.
|
|
37
|
+
*
|
|
38
|
+
* WHY IT SURVIVES PUBLISHING. npm always ships package.json, `exports` carries
|
|
39
|
+
* `"./package.json": "./package.json"` (self-reference is restricted to the exports
|
|
40
|
+
* map, so that key is load-bearing — drop it and this throws, and the test goes red
|
|
41
|
+
* with it), and `bin/mcp.js` imports `../dist/mcp.es.js` by its own URL, so the
|
|
42
|
+
* resolver starts inside the installed package however it was launched (npx, global
|
|
43
|
+
* install, symlink). Not a JSON import: that inlines the version at BUILD time, which
|
|
44
|
+
* is a second copy that can disagree with the package.json shipped beside it, and it
|
|
45
|
+
* would need `resolveJsonModule` on the repo's browser tsc pass as well as this one.
|
|
46
|
+
*
|
|
47
|
+
* A broken resolve throws instead of falling back to a placeholder — `bin/mcp.js`
|
|
48
|
+
* turns that into a named fatal on stderr, which is the loud version. A wrong version
|
|
49
|
+
* silently announced to every harness is the quiet one.
|
|
50
|
+
*/
|
|
51
|
+
function packageIdentity(): { name: string; version: string } {
|
|
52
|
+
const specifier = `${PACKAGE_NAME}/package.json`;
|
|
53
|
+
const require = createRequire(import.meta.url);
|
|
54
|
+
const pkg = require(specifier) as { name?: string; version?: string };
|
|
55
|
+
if (pkg.name !== PACKAGE_NAME || typeof pkg.version !== 'string') {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`[${PACKAGE_NAME}] Refusing to report a serverInfo that is not this package's: ` +
|
|
58
|
+
`${specifier} resolved to ${require.resolve(specifier)}, whose name is ` +
|
|
59
|
+
`${JSON.stringify(pkg.name)} and version ${JSON.stringify(pkg.version)}.`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return { name: pkg.name, version: pkg.version };
|
|
63
|
+
}
|
|
64
|
+
|
|
15
65
|
/**
|
|
16
66
|
* The AI/UI MCP server. A stdio server (see ./stdio.ts) exposing the AI/UI tools
|
|
17
67
|
* to any MCP harness. Registers four tools; Tasks 2–5 fill in each handler.
|
|
@@ -24,10 +74,7 @@ export interface AiUiServer extends Server {
|
|
|
24
74
|
}
|
|
25
75
|
|
|
26
76
|
export function createServer(): AiUiServer {
|
|
27
|
-
const server = new Server(
|
|
28
|
-
{ name: '@kitn.ai/ui', version: '0.15.0' },
|
|
29
|
-
{ capabilities: { tools: {} } },
|
|
30
|
-
) as AiUiServer;
|
|
77
|
+
const server = new Server(packageIdentity(), { capabilities: { tools: {} } }) as AiUiServer;
|
|
31
78
|
|
|
32
79
|
const byName = new Map(tools.map((t) => [t.name, t]));
|
|
33
80
|
|
|
@@ -50,7 +97,16 @@ export function createServer(): AiUiServer {
|
|
|
50
97
|
content: [{ type: 'text', text: `Unknown tool: ${request.params.name}` }],
|
|
51
98
|
};
|
|
52
99
|
}
|
|
53
|
-
|
|
100
|
+
const args = request.params.arguments ?? {};
|
|
101
|
+
// Enforce the schema each tool ADVERTISES (additionalProperties: false +
|
|
102
|
+
// required) — one path for all four tools. Without this, a wrong key was
|
|
103
|
+
// silently ignored and the handler answered a different question (see
|
|
104
|
+
// validate-args.ts for the observed failure).
|
|
105
|
+
const problem = validateToolArgs(tool.name, tool.inputSchema, args);
|
|
106
|
+
if (problem) {
|
|
107
|
+
return { isError: true, content: [{ type: 'text', text: problem }] };
|
|
108
|
+
}
|
|
109
|
+
return tool.handler(args);
|
|
54
110
|
});
|
|
55
111
|
|
|
56
112
|
server.__listToolsForTest = () => tools.map((t) => t.name);
|