@kitn.ai/ui 0.25.2 → 0.27.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 +17 -6
- package/bin/mcp.js +16 -3
- package/bin/route.js +23 -0
- package/bin/route.test.js +58 -0
- package/dist/{elements/chunks/Icon-b5A8hlHs.js → Icon-BnFKqqtY.js} +1 -1
- package/dist/action-icons-CBskfBMz.js +1 -0
- package/dist/arrow-down-Br0qtpsz.js +1 -0
- package/dist/arrow-left-Dj7xgE12.js +1 -0
- package/dist/artifact-CvXEm1lS.js +1 -0
- package/dist/assets/dev-5XzXYR21.js +91 -0
- package/dist/attachments-C43ZegEq.js +1 -0
- package/dist/audio-visualizer-B-79y1Tj.js +1 -0
- package/dist/badge-BpUbq8c0.js +1 -0
- package/dist/bash-InADTalH.js +1 -6
- package/dist/bell-sn17Ip9v.js +1 -0
- package/dist/{elements/chunks/button-B9okqG73.js → button-BH6rN0U-.js} +1 -1
- package/dist/card-renderer-Cb1gQmth.js +1 -0
- package/dist/card-routing-DifQgS7n.js +1 -0
- package/dist/check-DPeeMUYx.js +1 -0
- package/dist/checkbox-Dc9Yov_B.js +1 -0
- package/dist/checkbox-group-B1kDLLYk.js +1 -0
- package/dist/chevron-down-CHSWk7ZT.js +1 -0
- package/dist/chevron-right-B7l_fMgx.js +1 -0
- package/dist/choice-card-BT3c1S8g.js +1 -0
- package/dist/circle-Bb0yOpqM.js +1 -0
- package/dist/circle-check-BGZpCiz7.js +1 -0
- package/dist/circle-x-wobWkoSx.js +1 -0
- package/dist/{elements/chunks/cn-CU7UAWzk.js → cn-DN5AWfiS.js} +1 -1
- package/dist/code-block-CUQ5mtv0.js +1 -0
- package/dist/collapsible-CPJbf6FM.js +1 -0
- package/dist/components/attachment-types.d.ts +53 -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/card-renderer.d.ts +13 -0
- package/dist/components/card.d.ts +4 -0
- package/dist/components/chat-thread.d.ts +49 -42
- package/dist/components/code-block.d.ts +22 -0
- package/dist/components/conversation-item.d.ts +66 -1
- package/dist/components/conversation-list.d.ts +77 -0
- package/dist/components/form-widgets.d.ts +67 -2
- package/dist/components/form.d.ts +89 -0
- package/dist/components/message.d.ts +29 -0
- package/dist/components/reasoning.d.ts +6 -0
- package/dist/components/scroll-button.d.ts +11 -0
- package/dist/components/thread.d.ts +5 -0
- package/dist/components/toast.d.ts +2 -1
- package/dist/components/tool-types.d.ts +9 -0
- 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/composer-CjngLzal.js +106 -0
- package/dist/confirm-card-DCx_A4bU.js +1 -0
- package/dist/construct-cli.es.js +1169 -0
- package/dist/context-BY8V-Nlp.js +1 -0
- package/dist/controllable-D43BKmtX.js +1 -0
- package/dist/conversation-list-D4lLU_yS.js +1 -0
- package/dist/copy-B6DO4PqB.js +1 -0
- package/dist/core-AYMC6_lb.js +12 -5874
- package/dist/{create-tween-CUzOlaMS.js → create-tween-BPzaefTp.js} +1 -1
- package/dist/{create-tween-B5Y4Im8G.js → create-tween-CnMHviKB.js} +1 -1
- package/dist/{create-tween-4z1XYLkR.js → create-tween-D1aiGZZh.js} +1 -1
- package/dist/{elements/chunks/create-tween-CUrM6D_o.js → create-tween-DUSQUKpr.js} +2 -2
- package/dist/css-M7EaDHN_.js +1 -6
- package/dist/custom-elements.json +3468 -1927
- package/dist/default-input-CucQEOqH.js +1 -0
- package/dist/define-CGGX-7Vg.js +1 -0
- package/dist/define.d.ts +4 -0
- package/dist/define.js +510 -0
- package/dist/define.server.d.ts +5 -0
- package/dist/define.server.js +506 -0
- 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/disclosure-CbLtajUQ.js +1 -0
- package/dist/dropdown-BwhViJFd.js +1 -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/checkbox-group.d.ts +1 -0
- package/dist/elements/checkbox-group.js +1 -0
- package/dist/elements/checkbox.d.ts +1 -0
- package/dist/elements/checkbox.js +1 -0
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- 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-entry.d.ts +12 -0
- 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 +243 -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-data-types.d.ts +37 -0
- 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-grid.d.ts +1 -0
- package/dist/elements/pane-grid.js +1 -0
- 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/radio-group.d.ts +1 -0
- package/dist/elements/radio-group.js +1 -0
- 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/select.d.ts +1 -0
- package/dist/elements/select.js +1 -0
- 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/slider.d.ts +1 -0
- package/dist/elements/slider.js +1 -0
- package/dist/elements/slots.d.ts +36 -4
- 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 +589 -167
- package/dist/{elements/chunks/ellipsis-CVxqfsDZ.js → ellipsis-DFr4Vqr3.js} +1 -1
- package/dist/embed-5s_vU4J0.js +1 -0
- package/dist/engine-javascript-vq0WuIJl.js +14 -2516
- package/dist/{elements/chunks/external-link-DG2sRdQL.js → external-link-u9d85ORQ.js} +1 -1
- package/dist/file-text-CX7_x42o.js +1 -0
- package/dist/{elements/chunks/file-tree-yGWNK0Js.js → file-tree-D3kBjXI5.js} +1 -1
- package/dist/folder-V-YdIL8f.js +1 -0
- package/dist/form-BRNUMCN2.js +1 -0
- package/dist/github-dark-dimmed-DUshB20C.js +1 -4
- package/dist/github-light-JYsPkUQd.js +1 -4
- package/dist/hover-card-DaeiYrTA.js +1 -0
- package/dist/html-CPZ3oZQ7.js +1 -10
- package/dist/icon-BpixdZNq.js +1 -0
- package/dist/{elements/chunks/index-BCRhi84a.js → index-xu_pdcPi.js} +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8473 -6470
- package/dist/index.server.js +6495 -4958
- package/dist/info-8mFsIjS3.js +1 -0
- package/dist/input-d1Yuu-hz.js +1 -0
- package/dist/javascript-C25yR2R2.js +1 -6
- package/dist/json-DxJze_jm.js +1 -6
- package/dist/kai-provider.es.js +40 -40
- package/dist/kai.es.js +1 -1
- package/dist/kbd-961zMPFB.js +1 -0
- package/dist/link-DlZZBGaR.js +1 -0
- package/dist/{elements/chunks/link-preview-Cf5KOP-i.js → link-preview-DIIbV2Sd.js} +1 -1
- package/dist/loader-YQszHFaW.js +1 -0
- package/dist/{elements/chunks/markdown-DbozyYqa.js → markdown-CVk1MPsj.js} +1 -1
- package/dist/mcp.es.js +3873 -544
- package/dist/message-CwKVEdZg.js +1 -0
- package/dist/message-DS8lzHc1.js +1 -0
- package/dist/message-circle-Xf8Q-vZp.js +1 -0
- package/dist/message-square-Cxnwgkns.js +1 -0
- package/dist/{elements/chunks/minimize-2-SegJ_oku.js → minimize-2-UEZmP2QT.js} +1 -1
- package/dist/{elements/chunks/model-switcher-D7JrFFHp.js → model-switcher-Kk1jEQ5t.js} +1 -1
- package/dist/{elements/chunks/overlay-CV7z2YuX.js → overlay-C1od6LOz.js} +1 -1
- package/dist/panel-right-ryET0yyn.js +1 -0
- package/dist/paperclip-BDNoZFJP.js +1 -0
- package/dist/play-OFIvYXUQ.js +1 -0
- package/dist/primitives/card-data-types.d.ts +49 -0
- package/dist/primitives/card-routing.d.ts +1 -33
- package/dist/primitives/card-validate.d.ts +2 -2
- package/dist/primitives/chat-config.d.ts +29 -2
- package/dist/primitives/field-mask.d.ts +96 -0
- package/dist/primitives/field-semantics.d.ts +36 -0
- package/dist/primitives/input-mask.d.ts +46 -0
- package/dist/primitives/url-scheme-policy.d.ts +34 -0
- 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/progress-bar-BvYX0aoy.js +1 -0
- package/dist/prompt-suggestion-BRFu2lcu.js +1 -0
- package/dist/radio-DcL23cNJ.js +1 -0
- package/dist/react/index.d.ts +306 -283
- package/dist/react.js +309 -247
- package/dist/reasoning-Cackq4Rh.js +1 -0
- package/dist/register-impl-C7rENvPK.js +424 -0
- package/dist/{elements/chunks/resizable-Dpzjlvp4.js → resizable-Cp9HUYih.js} +1 -1
- package/dist/rotate-ccw-0DLRDHH5.js +1 -0
- package/dist/rotate-cw-DOB9IGcU.js +1 -0
- package/dist/schemas/form.schema.json +21 -1
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/tool-defs.d.ts +45 -0
- package/dist/schemas.js +398 -305
- package/dist/scroll-area-emVmWP4K.js +1 -0
- package/dist/scroll-button-CeP_gv1x.js +1 -0
- package/dist/select-CWmu00tS.js +1 -0
- package/dist/{elements/chunks/separator-DH2r_ZSZ.js → separator-CE6vly6z.js} +1 -1
- package/dist/{elements/chunks/settings-BFMOd6yB.js → settings-CE-78SvZ.js} +1 -1
- package/dist/{elements/chunks/settings-group-B3VZKIcz.js → settings-group-ZFWNxFQR.js} +1 -1
- package/dist/{elements/chunks/skeleton-N8BuWYmf.js → skeleton-BebdP4mf.js} +1 -1
- package/dist/slider-BAKRYUlz.js +1 -0
- package/dist/slots-DaNjX0rc.js +1 -0
- package/dist/{solid-BMuDmFo_.js → solid-BE5ui6sy.js} +10672 -8116
- package/dist/{solid-CGYNdLRR.js → solid-DFhsce3O.js} +8137 -6056
- package/dist/solid.d.ts +15 -0
- package/dist/solid.js +248 -235
- package/dist/solid.server.js +248 -235
- package/dist/source-B0FlguSx.js +1 -0
- package/dist/star-CBMZvLuN.js +1 -0
- package/dist/state/index.d.ts +5 -1
- package/dist/state/mock.d.ts +32 -2
- 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 +297 -172
- package/dist/store-7eoadydQ.js +1 -0
- package/dist/svelte--5p79yCD.js +1 -15
- package/dist/switch-CViARZCy.js +1 -0
- package/dist/tasks-card-MjCcOhR_.js +1 -0
- package/dist/text-shimmer-DKlwRF2G.js +1 -0
- package/dist/textarea-3ay2Me2-.js +1 -0
- package/dist/theme.tokens.css +82 -13
- package/dist/{elements/chunks/thumbs-up-DQe0hvkZ.js → thumbs-up-DQcu2HMO.js} +1 -1
- package/dist/toast-store-C6yKOhbk.js +1 -0
- package/dist/{elements/chunks/tool-C1YtxwPu.js → tool-BCYNlfKy.js} +1 -1
- package/dist/{elements/chunks/tooltip-BqwnFWhn.js → tooltip-05zI4hjs.js} +1 -1
- package/dist/trash-2-BR1RxGky.js +1 -0
- package/dist/{elements/chunks/triangle-alert-B6or7F5u.js → triangle-alert-DHohDOCy.js} +1 -1
- package/dist/tsx-B8rCNbgL.js +1 -6
- package/dist/types.d.ts +4 -2
- package/dist/typescript-RycA9KXf.js +1 -6
- package/dist/ui/checkbox-group.d.ts +83 -0
- package/dist/ui/checkbox.d.ts +40 -0
- package/dist/ui/dock.d.ts +131 -0
- package/dist/ui/hover-card.d.ts +44 -0
- package/dist/ui/icon.d.ts +3 -0
- package/dist/ui/input.d.ts +48 -2
- package/dist/ui/radio.d.ts +85 -0
- package/dist/ui/select.d.ts +71 -0
- package/dist/ui/slider.d.ts +65 -0
- package/dist/ui/switch.d.ts +9 -2
- package/dist/upload-BbGW6A_u.js +1 -0
- package/dist/url-scheme-policy-DHpoTJwB.js +1 -0
- package/dist/use-card-resolution-CJW4grFa.js +1 -0
- package/dist/{variant-aurora-DwSOfraQ.js → variant-aurora-BUOVgPOB.js} +27 -27
- package/dist/{variant-aurora-wKvAQ5wp.js → variant-aurora-CUsUJvFI.js} +28 -28
- package/dist/{variant-aurora-XEkPT0_z.js → variant-aurora-CW2i4bfr.js} +2 -2
- package/dist/{elements/chunks/variant-aurora-QXTZPyST.js → variant-aurora-xiMPty8f.js} +2 -2
- package/dist/variant-custom-BQtOnmeO.js +1 -0
- package/dist/{variant-custom-BhS9wIpU.js → variant-custom-CAm9L-E5.js} +65 -65
- package/dist/{variant-custom-eFIg2XWQ.js → variant-custom-CAnzzppW.js} +40 -40
- package/dist/variant-custom-D91HjAKz.js +1 -0
- package/dist/{variant-wave-CUWGx9Bn.js → variant-wave-Ca1Ls4_y.js} +21 -21
- package/dist/{variant-wave-CbVsmgwT.js → variant-wave-CxX39DKd.js} +25 -25
- package/dist/{variant-wave-BwzrHD1s.js → variant-wave-DYyzIIZv.js} +2 -2
- package/dist/{elements/chunks/variant-wave-DDCwhLas.js → variant-wave-uY5crU_K.js} +2 -2
- package/dist/vue-BmIZj4XD.js +1 -33
- 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/dist/x-C5m1JFgi.js +1 -0
- package/frameworks/react/index.tsx +325 -108
- package/llms-full.txt +1354 -82
- package/llms.txt +6 -3
- package/package.json +39 -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 +347 -0
- package/src/agent-tooling/construct/cli-entry.ts +8 -0
- package/src/agent-tooling/construct/cli.ts +131 -0
- package/src/agent-tooling/construct/codegen.ts +1353 -0
- package/src/agent-tooling/construct/construct.v1.schema.json +250 -0
- package/src/agent-tooling/construct/dev.ts +148 -0
- package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +7 -0
- package/src/agent-tooling/construct/fixtures/ops-console.construct.json +44 -0
- package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +23 -0
- package/src/agent-tooling/construct/schema.ts +371 -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 +63 -6
- package/src/agent-tooling/mcp/tools/construct.ts +130 -0
- package/src/agent-tooling/mcp/tools/debug.ts +126 -4
- package/src/agent-tooling/mcp/tools/reference.ts +542 -5
- 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/recipes/composed-thread.ts +714 -0
- package/src/agent-tooling/recipes/index.ts +22 -0
- package/src/agent-tooling/recipes/types.ts +32 -0
- package/src/agent-tooling/registry.ts +24 -7
- package/src/agent-tooling/route-emit.ts +26 -2
- package/src/components/attachment-types.ts +54 -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/card-renderer.tsx +39 -8
- package/src/components/card.tsx +10 -2
- package/src/components/chat-container.tsx +4 -1
- package/src/components/chat-scope-picker.tsx +2 -2
- package/src/components/chat-thread.tsx +105 -13
- package/src/components/choice-card.tsx +102 -37
- package/src/components/coachmark.tsx +1 -1
- package/src/components/code-block.tsx +81 -5
- package/src/components/composer.tsx +89 -8
- package/src/components/confirm-card.tsx +20 -4
- package/src/components/conversation-item.tsx +138 -4
- package/src/components/conversation-list.tsx +230 -15
- package/src/components/embed.tsx +4 -0
- package/src/components/empty.tsx +14 -3
- package/src/components/file-tree.tsx +5 -1
- package/src/components/form-widgets.tsx +257 -195
- package/src/components/form.tsx +336 -21
- package/src/components/loader.tsx +12 -5
- package/src/components/message-skills.tsx +1 -1
- package/src/components/message.tsx +142 -12
- package/src/components/prompt-input.tsx +7 -1
- package/src/components/prompt-suggestion.tsx +4 -1
- package/src/components/reasoning.tsx +30 -3
- package/src/components/response-compare.tsx +2 -2
- package/src/components/response-stream.tsx +13 -7
- package/src/components/screen.tsx +5 -0
- package/src/components/scroll-button.tsx +63 -4
- package/src/components/source.tsx +6 -1
- package/src/components/tasks-card.tsx +58 -16
- package/src/components/thread.tsx +10 -1
- package/src/components/toast.tsx +11 -5
- package/src/components/tool-types.ts +9 -0
- 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 +9 -8
- package/src/elements/audio-visualizer.tsx +48 -4
- package/src/elements/chat-workspace.tsx +159 -287
- package/src/elements/chat.tsx +31 -10
- package/src/elements/checkbox-group.tsx +164 -0
- package/src/elements/checkbox.tsx +134 -0
- 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/composer.tsx +12 -1
- package/src/elements/conversation-item.tsx +120 -0
- package/src/elements/conversation-list.tsx +60 -11
- package/src/elements/default-input.tsx +5 -5
- package/src/elements/define-entry.ts +12 -0
- 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 +215 -0
- package/src/elements/dropdown.tsx +123 -0
- package/src/elements/element-data-types.ts +40 -0
- package/src/elements/element-diagnostics.ts +392 -0
- package/src/elements/element-manifest.json +36 -0
- package/src/elements/element-meta.json +1483 -560
- package/src/elements/element-nonscalar.json +185 -0
- package/src/elements/element-types.d.ts +589 -167
- package/src/elements/feedback-bar.tsx +5 -0
- package/src/elements/icon-names.json +29 -0
- package/src/elements/input.tsx +312 -23
- package/src/elements/menu.tsx +13 -0
- package/src/elements/message.tsx +2 -0
- package/src/elements/pane-grid.tsx +111 -0
- package/src/elements/prompt-dock.tsx +2 -1
- package/src/elements/prompt-input.tsx +16 -10
- package/src/elements/radio-group.tsx +129 -0
- package/src/elements/register-impl.ts +52 -0
- package/src/elements/register.ts +28 -0
- package/src/elements/resizable.tsx +52 -25
- package/src/elements/scroll-button.tsx +59 -6
- package/src/elements/select.tsx +165 -0
- package/src/elements/slider.tsx +171 -0
- package/src/elements/slots.ts +108 -13
- package/src/elements/styles.css +119 -9
- package/src/elements/thinking-bar.tsx +16 -7
- package/src/elements/thread.tsx +2 -0
- package/src/elements/toast.tsx +17 -1
- 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 +4 -1
- package/src/primitives/card-data-types.ts +49 -0
- package/src/primitives/card-host.tsx +4 -2
- package/src/primitives/card-routing.ts +7 -56
- package/src/primitives/card-schemas/form.schema.json +21 -1
- package/src/primitives/card-validate.ts +10 -2
- package/src/primitives/chat-config.tsx +33 -6
- package/src/primitives/field-mask.ts +256 -0
- package/src/primitives/field-semantics.ts +115 -0
- package/src/primitives/input-mask.ts +853 -0
- package/src/primitives/toast-store.ts +66 -4
- package/src/primitives/url-scheme-policy.ts +70 -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 +307 -6
- package/src/solid.ts +15 -0
- package/src/state/index.ts +13 -1
- package/src/state/mock.ts +92 -6
- 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/agent-card.tsx +2 -2
- package/src/ui/avatar.tsx +1 -1
- package/src/ui/checkbox-group.tsx +153 -0
- package/src/ui/checkbox.tsx +63 -0
- package/src/ui/dialog.tsx +6 -0
- package/src/ui/dock.tsx +584 -0
- package/src/ui/dropdown.tsx +67 -23
- package/src/ui/hover-card.tsx +156 -6
- package/src/ui/icon.tsx +53 -0
- package/src/ui/input.tsx +275 -16
- package/src/ui/kbd.tsx +1 -1
- package/src/ui/nav.tsx +1 -1
- package/src/ui/pane-group.tsx +2 -2
- package/src/ui/radio.tsx +150 -0
- package/src/ui/resizable.tsx +11 -0
- package/src/ui/select.tsx +168 -0
- package/src/ui/slider.tsx +178 -0
- package/src/ui/switch.tsx +49 -18
- package/src/utils/cn.ts +11 -5
- 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/theme.css +84 -13
- package/dist/elements/chunks/action-icons-DW9muWrY.js +0 -1
- package/dist/elements/chunks/arrow-left-iJVnQmxA.js +0 -1
- package/dist/elements/chunks/artifact-BSuPYFHY.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/badge-TIMiJFKc.js +0 -1
- package/dist/elements/chunks/bash-InADTalH.js +0 -1
- package/dist/elements/chunks/card-renderer-CSoTL_e1.js +0 -1
- package/dist/elements/chunks/card-routing-BK09BdmL.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/choice-card-CGMc3kek.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/circle-x-mhH7pOK2.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/composer-CZZmtYOT.js +0 -69
- package/dist/elements/chunks/confirm-card-BWjf2-xo.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/core-AYMC6_lb.js +0 -12
- package/dist/elements/chunks/css-M7EaDHN_.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/download-DnCyWFJE.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/engine-javascript-vq0WuIJl.js +0 -141
- package/dist/elements/chunks/file-text-D0J9F6M7.js +0 -1
- package/dist/elements/chunks/folder-NV1pi_ud.js +0 -1
- package/dist/elements/chunks/form-Cp8Y00Wn.js +0 -1
- package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +0 -1
- package/dist/elements/chunks/github-light-JYsPkUQd.js +0 -1
- package/dist/elements/chunks/hover-card-6KtXpLwT.js +0 -1
- package/dist/elements/chunks/html-CPZ3oZQ7.js +0 -1
- package/dist/elements/chunks/icon-97R8SR2p.js +0 -1
- package/dist/elements/chunks/info-C8r5Q6cc.js +0 -1
- package/dist/elements/chunks/input-DrseEjhp.js +0 -1
- package/dist/elements/chunks/javascript-C25yR2R2.js +0 -1
- package/dist/elements/chunks/json-DxJze_jm.js +0 -1
- package/dist/elements/chunks/kbd-OGJDUT-O.js +0 -1
- package/dist/elements/chunks/link-BdsfLB7b.js +0 -1
- package/dist/elements/chunks/loader-B5QzK_8_.js +0 -1
- package/dist/elements/chunks/message-CE4k-qaj.js +0 -1
- package/dist/elements/chunks/message-DtXKPqiR.js +0 -1
- package/dist/elements/chunks/message-square-C9ovmtOo.js +0 -1
- package/dist/elements/chunks/paperclip-Bg0ITU3b.js +0 -1
- package/dist/elements/chunks/progress-bar-D1_rzt6N.js +0 -1
- package/dist/elements/chunks/prompt-suggestion-D5Sm8xF1.js +0 -1
- package/dist/elements/chunks/reasoning-BrxQfpfI.js +0 -1
- package/dist/elements/chunks/rotate-cw-DzVDJOA1.js +0 -1
- package/dist/elements/chunks/scroll-area-93A1bbeG.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/source-w9V0NnBn.js +0 -1
- package/dist/elements/chunks/star-DHvACxtm.js +0 -1
- package/dist/elements/chunks/store-HIPGhNz9.js +0 -1
- package/dist/elements/chunks/svelte--5p79yCD.js +0 -1
- package/dist/elements/chunks/tasks-card-lPxoodlA.js +0 -1
- package/dist/elements/chunks/text-shimmer-BY_rI3s5.js +0 -1
- package/dist/elements/chunks/textarea-DPfuhswM.js +0 -1
- package/dist/elements/chunks/toast-store-VUZZBhzH.js +0 -1
- package/dist/elements/chunks/tsx-B8rCNbgL.js +0 -1
- package/dist/elements/chunks/typescript-RycA9KXf.js +0 -1
- package/dist/elements/chunks/use-card-resolution-DJg32jGH.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/vue-BmIZj4XD.js +0 -1
- package/dist/elements/chunks/x-BOhjBeOd.js +0 -1
- package/dist/llms/llms-full.txt +0 -2834
- package/dist/llms/llms.txt +0 -156
- package/dist/primitives/card-validate-generator.testlib.d.ts +0 -20
- package/dist/register-impl-DGmGzrEM.js +0 -145
- package/dist/ui/stat.d.ts +0 -16
- package/dist/variant-custom-MkTNTycd.js +0 -1
- package/src/primitives/card-validate-generator.testlib.ts +0 -38
- package/src/ui/stat.tsx +0 -41
- /package/dist/{elements/chunks/card-tags-D8lZ-C_U.js → card-tags-D8lZ-C_U.js} +0 -0
- /package/dist/{elements/chunks/link-preview-DNILK391.js → link-preview-DNILK391.js} +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The construct format, v1 — Zod is the SINGLE SOURCE OF TRUTH.
|
|
3
|
+
*
|
|
4
|
+
* The published JSON Schema (apps/docs/public/schemas/construct/v1.json) and the
|
|
5
|
+
* checked-in construct.v1.schema.json are DERIVED from this object by
|
|
6
|
+
* scripts/gen-construct-schema.mjs (build:api, drift-guarded). Never edit those
|
|
7
|
+
* by hand; never restate an enum from here anywhere else — read it off
|
|
8
|
+
* `ConstructSchema.shape` or the generated artifact.
|
|
9
|
+
*
|
|
10
|
+
* Format rules (spec, binding): vocabulary never logic — no handlers, no
|
|
11
|
+
* expressions; `.strict()` everywhere so an unknown key is a loud rejection,
|
|
12
|
+
* not a silently ignored one. No secrets, no client: `provider` can name a URL
|
|
13
|
+
* and a wire format, nothing else.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
// REPO-INTERNAL ONLY: isSafeUrl is not reachable from the published package
|
|
17
|
+
// (see agent-tooling/catalog/invariants.ts's own note on this), so the
|
|
18
|
+
// emitted App.tsx cannot import it — the sink codegen writes to
|
|
19
|
+
// (launcherIcon -> <img src>) has no guard it can reach at runtime. Enforcing
|
|
20
|
+
// the SAME policy here, at authoring time, is the reachable equivalent: a
|
|
21
|
+
// hostile launcherIcon is rejected before a construct ever validates, so
|
|
22
|
+
// codegen never has an unsafe value to emit. Reuses the kit's one existing
|
|
23
|
+
// URL-sink policy (markdown.tsx's image renderer, artifact.tsx) rather than
|
|
24
|
+
// authoring a second one. Imported from url-scheme-policy.ts, NOT
|
|
25
|
+
// card-routing.ts: this file compiles under tsconfig.mcp.json's Node-only, no-
|
|
26
|
+
// DOM-lib pass (transitively, via mcp/tools/construct.ts), and card-routing.ts
|
|
27
|
+
// pulls in HTMLElement/window/CustomEvent that pass can't see.
|
|
28
|
+
import { isSafeUrl } from '../../primitives/url-scheme-policy';
|
|
29
|
+
|
|
30
|
+
export const CONSTRUCT_SCHEMA_URL = 'https://ui.kitn.ai/schemas/construct/v1.json';
|
|
31
|
+
|
|
32
|
+
/** A valid custom-element tag: lowercase, starts with a letter, contains a hyphen. */
|
|
33
|
+
const TAG_RE = /^[a-z][a-z0-9]*-[a-z0-9-]+$/;
|
|
34
|
+
|
|
35
|
+
const ProviderSchema = z.discriminatedUnion('mode', [
|
|
36
|
+
z.object({ mode: z.literal('mock') }).strict(),
|
|
37
|
+
z
|
|
38
|
+
.object({
|
|
39
|
+
mode: z.literal('endpoint'),
|
|
40
|
+
/** The CONSUMER's chat route. Kit parses, consumer fetches. */
|
|
41
|
+
url: z.string().min(1),
|
|
42
|
+
wire: z.enum(['openai', 'anthropic']),
|
|
43
|
+
})
|
|
44
|
+
.strict(),
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
export const ConstructSchema = z
|
|
48
|
+
.object({
|
|
49
|
+
$schema: z.string().optional(),
|
|
50
|
+
/** The emitted tag: <acme-support>. Must satisfy customElements.define. */
|
|
51
|
+
name: z
|
|
52
|
+
.string()
|
|
53
|
+
.regex(TAG_RE, 'must be a valid custom-element tag: lowercase, with a hyphen (e.g. "acme-support")'),
|
|
54
|
+
// Widened progressively: fullscreen/aside/split landed in Task 12, custom in Task 13.
|
|
55
|
+
layout: z.enum(['widget', 'fullscreen', 'aside', 'split', 'custom']),
|
|
56
|
+
provider: ProviderSchema,
|
|
57
|
+
/** Plain (unsigned) identity passthrough — no signing/auth infra (owner
|
|
58
|
+
* ruling, 2026-08-26: signed JWT/HMAC identity is later additive
|
|
59
|
+
* vocabulary, not this field). TOP-LEVEL, not nested under `provider`:
|
|
60
|
+
* local history scoping (`capabilities.history.persistence: 'local'`)
|
|
61
|
+
* needs userId independent of provider mode — nesting it inside the
|
|
62
|
+
* provider union would make it silently inert for `mode: 'mock'` +
|
|
63
|
+
* local history, a real and common combination. Threaded as the
|
|
64
|
+
* `x-kai-user-id` header on every emitted fetch to the CONSUMER's own
|
|
65
|
+
* backend (the endpoint provider's chat POST, history's endpoint
|
|
66
|
+
* GET/PUT), and folded into the localStorage key for `local` history so
|
|
67
|
+
* different users on the same browser profile don't share one thread.
|
|
68
|
+
* Constrained to printable-ASCII (no CR/LF, no non-ISO-8859-1 code
|
|
69
|
+
* points) beyond the usual construct-authored-text escaping: this value
|
|
70
|
+
* reaches an HTTP HEADER VALUE, not just a JS string literal, and
|
|
71
|
+
* `fetch()` throws at RUNTIME on a header value containing CR/LF or
|
|
72
|
+
* outside ISO-8859-1 — a bad userId would surface as an opaque crash in
|
|
73
|
+
* the CONSUMER's generated app, not a construct-validation error. Reject
|
|
74
|
+
* loudly here instead, where the author gets a path + message. */
|
|
75
|
+
userId: z
|
|
76
|
+
.string()
|
|
77
|
+
.min(1)
|
|
78
|
+
.regex(
|
|
79
|
+
/^[\x20-\x7E]+$/,
|
|
80
|
+
'must be printable ASCII with no line breaks (it becomes an HTTP header value)',
|
|
81
|
+
)
|
|
82
|
+
.optional(),
|
|
83
|
+
theme: z
|
|
84
|
+
.object({
|
|
85
|
+
/** Any CSS color; becomes --kai-color-primary on the host. */
|
|
86
|
+
accent: z.string().optional(),
|
|
87
|
+
mode: z.enum(['light', 'dark', 'system']).default('system'),
|
|
88
|
+
})
|
|
89
|
+
.strict()
|
|
90
|
+
.optional(),
|
|
91
|
+
/** Construct-wide header chrome, valid on every layout (not layout-scoped
|
|
92
|
+
* like `widget`, and not a capability toggle — a header is a construct-
|
|
93
|
+
* wide fact, like `theme`). Rendered inside ChatThread's own built-in
|
|
94
|
+
* header bar; a logo/icon projects through the kit's EXISTING
|
|
95
|
+
* `header-start` named slot (`slots` vocabulary above), not a second
|
|
96
|
+
* image-prop convention here. */
|
|
97
|
+
header: z
|
|
98
|
+
.object({
|
|
99
|
+
/** Rendered in ChatThread's built-in header bar (left side). Construct-
|
|
100
|
+
* authored/untrusted text, like theme.accent/provider.url — JSON.stringify'd
|
|
101
|
+
* at its one emit site, never a raw JSX attribute string. */
|
|
102
|
+
title: z.string().min(1).optional(),
|
|
103
|
+
})
|
|
104
|
+
.strict()
|
|
105
|
+
.optional(),
|
|
106
|
+
/** Greeting shown while the thread is empty (no messages yet) — the
|
|
107
|
+
* proven "welcome screen" pattern (Intercom-class): title + optional
|
|
108
|
+
* description + optional icon above, with `capabilities.starters`'
|
|
109
|
+
* chips and the composer still rendering below (ChatThread's `empty`
|
|
110
|
+
* REPLACE slot only stands in for the empty MESSAGE LIST — see
|
|
111
|
+
* chat-thread.tsx's own doc comment on `empty` — so the chips are
|
|
112
|
+
* never lost). Construct-wide like `header`, not a capability: it's a
|
|
113
|
+
* fact about the empty state, not a toggleable affordance.
|
|
114
|
+
* `title`/`description` are construct-authored/untrusted text, like
|
|
115
|
+
* `header.title`/`theme.accent`/`provider.url` — JSON.stringify'd at
|
|
116
|
+
* their one emit site, never a raw JSX attribute string. `icon` is a
|
|
117
|
+
* URL reaching an `<img src>` sink in emitted code, exactly like
|
|
118
|
+
* `widget.launcherIcon` — same `isSafeUrl` policy, same superRefine
|
|
119
|
+
* shape, below. */
|
|
120
|
+
empty: z
|
|
121
|
+
.object({
|
|
122
|
+
title: z.string().min(1),
|
|
123
|
+
description: z.string().min(1).optional(),
|
|
124
|
+
icon: z.string().min(1).optional(),
|
|
125
|
+
})
|
|
126
|
+
.strict()
|
|
127
|
+
.optional(),
|
|
128
|
+
// Capability vocabulary, widened one field at a time by later tasks.
|
|
129
|
+
capabilities: z
|
|
130
|
+
.object({
|
|
131
|
+
/** Starter prompts shown on the empty thread; clicking one sends it.
|
|
132
|
+
* 1-6 non-empty strings — construct-authored text, escaped like
|
|
133
|
+
* `theme.accent`/`provider.url` at every emit interpolation site. */
|
|
134
|
+
starters: z.array(z.string().min(1)).min(1).max(6).optional(),
|
|
135
|
+
/** Enables the paperclip attach affordance; accept is a non-empty
|
|
136
|
+
* list of media types/globs, e.g. ["image/*", "application/pdf"] —
|
|
137
|
+
* WHETHER stays with the construct author (this field), HOW stays
|
|
138
|
+
* with the kit (ChatThread's own attach/accept props, threaded
|
|
139
|
+
* through by codegen). */
|
|
140
|
+
attachments: z
|
|
141
|
+
.object({
|
|
142
|
+
/** Accept-list of media types/globs, e.g. ["image/*", "application/pdf"]. */
|
|
143
|
+
accept: z.array(z.string().min(1)).min(1),
|
|
144
|
+
})
|
|
145
|
+
.strict()
|
|
146
|
+
.optional(),
|
|
147
|
+
/** Conversation persistence. `none` (default, nothing emitted): the
|
|
148
|
+
* thread lives only in memory for the tab's lifetime. `local`:
|
|
149
|
+
* persisted to this browser's localStorage, keyed by the construct's
|
|
150
|
+
* tag — a mechanism decision (WHERE); what to retain and for how
|
|
151
|
+
* long stays an app decision (component-scope-boundary), so no
|
|
152
|
+
* retention count/quota lands here. `endpoint`: the CONSUMER's own
|
|
153
|
+
* thread route (GET returns ChatMessage[], PUT stores them) —
|
|
154
|
+
* requires `url`; `url` is rejected for any other persistence
|
|
155
|
+
* (superRefine below, both directions loud). */
|
|
156
|
+
history: z
|
|
157
|
+
.object({
|
|
158
|
+
persistence: z.enum(['none', 'local', 'endpoint']),
|
|
159
|
+
/** endpoint persistence only: the CONSUMER's thread routes (GET returns
|
|
160
|
+
* ChatMessage[], PUT stores them). Refined below. */
|
|
161
|
+
url: z.string().min(1).optional(),
|
|
162
|
+
})
|
|
163
|
+
.strict()
|
|
164
|
+
.optional(),
|
|
165
|
+
/** How the model's thinking (reasoning parts) renders. `'full'`
|
|
166
|
+
* (the default when omitted — see codegen.ts's emitReasoningProp)
|
|
167
|
+
* is the collapsible "Thinking" disclosure, shimmering while it
|
|
168
|
+
* streams. `'compact'` shows only a shimmer/typing loader while
|
|
169
|
+
* reasoning streams, with no expandable detail. `'off'` hides
|
|
170
|
+
* reasoning entirely. This is HOW an existing medium fact (the
|
|
171
|
+
* model's thinking) displays — the kit's call — so it maps straight
|
|
172
|
+
* onto ChatThread's own `reasoning` prop; there is no app-layer
|
|
173
|
+
* quota or retention decision hiding in it. Left `.optional()`
|
|
174
|
+
* rather than `.default('full')`, matching every sibling field in
|
|
175
|
+
* this object: a zod `.default()` here would make `reasoning`
|
|
176
|
+
* REQUIRED on the inferred input type (z.infer is the output type),
|
|
177
|
+
* breaking every `capabilities: {...}` object literal in this file
|
|
178
|
+
* and its tests that doesn't mention it. */
|
|
179
|
+
reasoning: z.enum(['full', 'compact', 'off']).optional(),
|
|
180
|
+
/** Seeds the reasoning disclosure open AND keeps it tracking the
|
|
181
|
+
* stream (open while streaming, closes when it settles) — the
|
|
182
|
+
* pre-Task-19f `full` behavior. Default false/absent: the panel
|
|
183
|
+
* starts closed (just the "Thinking" shimmer chip) and only opens
|
|
184
|
+
* on click — the current default (owner ruling, 2026-08-26).
|
|
185
|
+
* Meaningless when `reasoning` is `'compact'` (no expandable
|
|
186
|
+
* content exists) or `'off'` (nothing renders); rejected on both,
|
|
187
|
+
* loudly, below. */
|
|
188
|
+
reasoningOpen: z.boolean().optional(),
|
|
189
|
+
})
|
|
190
|
+
.strict()
|
|
191
|
+
.optional(),
|
|
192
|
+
/** Named generative-UI card definitions the model can emit as tool calls,
|
|
193
|
+
* rendered in the thread. Top-level (not a capability): a card is
|
|
194
|
+
* something the construct's model can DO at any point once a card is
|
|
195
|
+
* declared, not a thread-affordance toggle like starters/attachments/
|
|
196
|
+
* reasoning. Reuses the kit's own card-tool projection end to end
|
|
197
|
+
* (`cardTools`/`toOpenAITools`/`toAnthropicTools`, `@kitn.ai/ui/schemas`)
|
|
198
|
+
* — never a second one authored here. `schema` is validated structurally
|
|
199
|
+
* only; deep card validation (incl. `x-kai-format` mask hints) is the
|
|
200
|
+
* kit's own card contract at render time. */
|
|
201
|
+
cards: z
|
|
202
|
+
.array(
|
|
203
|
+
z
|
|
204
|
+
.object({
|
|
205
|
+
/** Tool-facing card name, e.g. "refund_approval". */
|
|
206
|
+
name: z.string().regex(/^[a-z][a-z0-9_]*$/),
|
|
207
|
+
/** The kit's card schema JSON (incl. x-kai-format mask hints).
|
|
208
|
+
* Validated structurally here; deep card validation is the kit's
|
|
209
|
+
* own card contract at render time. */
|
|
210
|
+
schema: z.record(z.string(), z.unknown()),
|
|
211
|
+
})
|
|
212
|
+
.strict(),
|
|
213
|
+
)
|
|
214
|
+
.min(1)
|
|
215
|
+
.optional(),
|
|
216
|
+
/** Named `<slot>` projection points the emitted web component exposes —
|
|
217
|
+
* the format's ONLY escape hatch (named slots, no code-in-JSON). Each
|
|
218
|
+
* name must be a valid HTML slot-attribute value AND a legible
|
|
219
|
+
* identifier: kebab-case, starting with a letter (the same shape as a
|
|
220
|
+
* CSS custom-ident) — `slot name="Header!"` is rejected, not sanitized,
|
|
221
|
+
* matching the format's loud-rejection discipline everywhere else.
|
|
222
|
+
* 1-8 entries, no duplicates (superRefine below — a regex alone can't
|
|
223
|
+
* see across array entries). `layout: 'custom'` requires at least one
|
|
224
|
+
* declared slot (superRefine below): `custom` IS the slots grain — a
|
|
225
|
+
* `custom` layout with nothing to project into is not meaningfully
|
|
226
|
+
* different from `fullscreen`. */
|
|
227
|
+
slots: z
|
|
228
|
+
.array(z.string().regex(/^[a-z][a-z0-9-]*$/, 'slot names must be kebab-case, starting with a letter'))
|
|
229
|
+
.min(1)
|
|
230
|
+
.max(8)
|
|
231
|
+
.optional(),
|
|
232
|
+
/** Layout-scoped FAB chrome, `layout: 'widget'` only (superRefine below).
|
|
233
|
+
* Purely additive sibling to `layout` (widen-never-restructure) — mirrors
|
|
234
|
+
* Dock's own props verbatim, threaded through by codegen's emitLayoutOpen. */
|
|
235
|
+
widget: z
|
|
236
|
+
.object({
|
|
237
|
+
/** Which corner. Mirrors Dock's own DockPosition enum verbatim — not a
|
|
238
|
+
* new left/right binary. Omitted keeps Dock's own default (bottom-end). */
|
|
239
|
+
position: z.enum(['bottom-end', 'bottom-start', 'top-end', 'top-start']).optional(),
|
|
240
|
+
/** An image URL for the closed-state launcher glyph, replacing Dock's
|
|
241
|
+
* built-in chat-bubble icon. Construct-authored/untrusted text, like
|
|
242
|
+
* theme.accent and provider.url — escaped the same way at its one emit site. */
|
|
243
|
+
launcherIcon: z.string().min(1).optional(),
|
|
244
|
+
/** Seed Dock's open state at mount. Uncontrolled — never steals focus (Dock's
|
|
245
|
+
* own focus contract, dock.tsx). Omitted keeps Dock's own default (closed). */
|
|
246
|
+
defaultOpen: z.boolean().optional(),
|
|
247
|
+
})
|
|
248
|
+
.strict()
|
|
249
|
+
.optional(),
|
|
250
|
+
})
|
|
251
|
+
.strict()
|
|
252
|
+
.superRefine((construct, ctx) => {
|
|
253
|
+
if (construct.slots) {
|
|
254
|
+
const seen = new Set<string>();
|
|
255
|
+
construct.slots.forEach((name, i) => {
|
|
256
|
+
if (seen.has(name)) {
|
|
257
|
+
ctx.addIssue({
|
|
258
|
+
code: z.ZodIssueCode.custom,
|
|
259
|
+
path: ['slots', i],
|
|
260
|
+
message: `duplicate slot name "${name}"`,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
seen.add(name);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
if (construct.layout === 'custom' && (!construct.slots || construct.slots.length === 0)) {
|
|
267
|
+
ctx.addIssue({
|
|
268
|
+
code: z.ZodIssueCode.custom,
|
|
269
|
+
path: ['slots'],
|
|
270
|
+
message: '"custom" layout requires at least one declared slot — custom IS the slots grain',
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
if (construct.layout === 'split' && construct.slots) {
|
|
274
|
+
const i = construct.slots.indexOf('pane');
|
|
275
|
+
if (i !== -1) {
|
|
276
|
+
ctx.addIssue({
|
|
277
|
+
code: z.ZodIssueCode.custom,
|
|
278
|
+
path: ['slots', i],
|
|
279
|
+
message:
|
|
280
|
+
'"pane" collides with the "split" layout\'s own fixed <slot name="pane"> — choose a different slot name',
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (construct.widget && construct.layout !== 'widget') {
|
|
285
|
+
ctx.addIssue({
|
|
286
|
+
code: z.ZodIssueCode.custom,
|
|
287
|
+
path: ['widget'],
|
|
288
|
+
message: '"widget" is only valid on layout: "widget"',
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
if (construct.widget?.launcherIcon && !isSafeUrl(construct.widget.launcherIcon)) {
|
|
292
|
+
ctx.addIssue({
|
|
293
|
+
code: z.ZodIssueCode.custom,
|
|
294
|
+
path: ['widget', 'launcherIcon'],
|
|
295
|
+
message: 'launcherIcon must be an http(s)/mailto or relative URL — no javascript:/data: schemes',
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
if (construct.empty?.icon && !isSafeUrl(construct.empty.icon)) {
|
|
299
|
+
ctx.addIssue({
|
|
300
|
+
code: z.ZodIssueCode.custom,
|
|
301
|
+
path: ['empty', 'icon'],
|
|
302
|
+
message: 'icon must be an http(s)/mailto or relative URL — no javascript:/data: schemes',
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
const reasoning = construct.capabilities?.reasoning;
|
|
306
|
+
const reasoningOpen = construct.capabilities?.reasoningOpen;
|
|
307
|
+
if (reasoningOpen !== undefined && (reasoning === 'compact' || reasoning === 'off')) {
|
|
308
|
+
ctx.addIssue({
|
|
309
|
+
code: z.ZodIssueCode.custom,
|
|
310
|
+
path: ['capabilities', 'reasoningOpen'],
|
|
311
|
+
message: '"reasoningOpen" only applies when reasoning is "full" or omitted — "compact"/"off" have no disclosure to open',
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
const history = construct.capabilities?.history;
|
|
315
|
+
if (!history) return;
|
|
316
|
+
if (history.persistence === 'endpoint' && !history.url) {
|
|
317
|
+
ctx.addIssue({
|
|
318
|
+
code: z.ZodIssueCode.custom,
|
|
319
|
+
path: ['capabilities', 'history', 'url'],
|
|
320
|
+
message: '"endpoint" persistence requires a url',
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
if (history.persistence !== 'endpoint' && history.url !== undefined) {
|
|
324
|
+
ctx.addIssue({
|
|
325
|
+
code: z.ZodIssueCode.custom,
|
|
326
|
+
path: ['capabilities', 'history', 'url'],
|
|
327
|
+
message: 'url is only valid with "endpoint" persistence',
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
export type Construct = z.infer<typeof ConstructSchema>;
|
|
333
|
+
|
|
334
|
+
export interface ConstructProblem {
|
|
335
|
+
/** Dotted path into the construct, '' for the root. */
|
|
336
|
+
path: string;
|
|
337
|
+
message: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export type ValidationOutcome =
|
|
341
|
+
| { ok: true; construct: Construct }
|
|
342
|
+
| { ok: false; problems: ConstructProblem[] };
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Validate one construct. The ONLY doorway to codegen: a failure never reaches
|
|
346
|
+
* generation — the problems go back to the author/agent with paths and reasons.
|
|
347
|
+
*/
|
|
348
|
+
export function validateConstruct(input: unknown): ValidationOutcome {
|
|
349
|
+
const parsed = ConstructSchema.safeParse(input);
|
|
350
|
+
if (parsed.success) return { ok: true, construct: parsed.data };
|
|
351
|
+
return {
|
|
352
|
+
ok: false,
|
|
353
|
+
problems: parsed.error.issues.flatMap((issue) => {
|
|
354
|
+
// zod's unrecognized-key issue names the keys in its message but paths the
|
|
355
|
+
// OBJECT; surface each unknown key as its own problem so the agent sees
|
|
356
|
+
// exactly which word is not vocabulary.
|
|
357
|
+
if (issue.code === 'unrecognized_keys') {
|
|
358
|
+
return issue.keys.map((key) => ({
|
|
359
|
+
path: [...issue.path.map(String), key].join('.'),
|
|
360
|
+
message: `"${key}" is not construct vocabulary`,
|
|
361
|
+
}));
|
|
362
|
+
}
|
|
363
|
+
return [
|
|
364
|
+
{
|
|
365
|
+
path: issue.path.map(String).join('.'),
|
|
366
|
+
message: issue.message,
|
|
367
|
+
},
|
|
368
|
+
];
|
|
369
|
+
}),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
@@ -317,7 +317,13 @@ function reframeToOpenAISse(body: ReadableStream<Uint8Array>): ReadableStream<Ui
|
|
|
317
317
|
async function chatHandler(request: Request): Promise<Response> {
|
|
318
318
|
// Both model and tools come from the browser. \`tools\` arrives in OpenAI
|
|
319
319
|
// function form and is converted to this API's shape below.
|
|
320
|
-
|
|
320
|
+
let chatBody: ChatRequestBody;
|
|
321
|
+
try {
|
|
322
|
+
chatBody = await readChatRequest(request);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
return toChatErrorResponse(error);
|
|
325
|
+
}
|
|
326
|
+
const { model, messages, tools } = chatBody;
|
|
321
327
|
const { system, messages: anthropicMessages } = toAnthropicBody(messages);
|
|
322
328
|
const anthropicTools = toAnthropicTools(tools);
|
|
323
329
|
|
|
@@ -24,9 +24,44 @@ import type { OpenAIWireMessage } from '@kitn.ai/ui/wire';
|
|
|
24
24
|
*/
|
|
25
25
|
type ChatRequestBody = { messages: OpenAIWireMessage[] };
|
|
26
26
|
|
|
27
|
+
class ChatRequestError extends Error {
|
|
28
|
+
constructor(readonly status: number, message: string) { super(message); }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Narrow the JSON body once, at the edge. A bare GET, a malformed body, or a
|
|
32
|
+
* missing messages array is a ChatRequestError with a status — NEVER an
|
|
33
|
+
* unhandled SyntaxError (findings F-10). */
|
|
34
|
+
async function readChatRequest(request: Request): Promise<ChatRequestBody> {
|
|
35
|
+
if (request.method !== 'POST') {
|
|
36
|
+
throw new ChatRequestError(405, \`Method \${request.method} not allowed — POST /api/chat.\`);
|
|
37
|
+
}
|
|
38
|
+
let parsed: unknown;
|
|
39
|
+
try { parsed = await request.json(); } catch {
|
|
40
|
+
throw new ChatRequestError(400, 'Request body is not valid JSON.');
|
|
41
|
+
}
|
|
42
|
+
const body = parsed as ChatRequestBody;
|
|
43
|
+
if (!Array.isArray(body?.messages)) {
|
|
44
|
+
throw new ChatRequestError(400, 'Request body must carry a messages array.');
|
|
45
|
+
}
|
|
46
|
+
return body;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Map a guard rejection to the Response its status demands; rethrow anything
|
|
50
|
+
* else — an unexpected error should be loud, not laundered into a 400. */
|
|
51
|
+
function toChatErrorResponse(error: unknown): Response {
|
|
52
|
+
if (error instanceof ChatRequestError) return Response.json({ error: error.message }, { status: error.status });
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
|
|
27
56
|
export default {
|
|
28
57
|
async fetch(req: Request, env: Env): Promise<Response> {
|
|
29
|
-
|
|
58
|
+
let chatBody: ChatRequestBody;
|
|
59
|
+
try {
|
|
60
|
+
chatBody = await readChatRequest(req);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return toChatErrorResponse(error);
|
|
63
|
+
}
|
|
64
|
+
const { messages } = chatBody;
|
|
30
65
|
|
|
31
66
|
let nativeStream: ReadableStream<Uint8Array>;
|
|
32
67
|
try {
|
|
@@ -101,7 +136,13 @@ export default {
|
|
|
101
136
|
},
|
|
102
137
|
webRoute: `async function chatHandler(request: Request): Promise<Response> {
|
|
103
138
|
// Proxy Workers AI over its OpenAI-compatible HTTP endpoint, token server-side.
|
|
104
|
-
|
|
139
|
+
let chatBody: ChatRequestBody;
|
|
140
|
+
try {
|
|
141
|
+
chatBody = await readChatRequest(request);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return toChatErrorResponse(error);
|
|
144
|
+
}
|
|
145
|
+
const { messages } = chatBody;
|
|
105
146
|
|
|
106
147
|
const upstream = await fetch(
|
|
107
148
|
\`https://api.cloudflare.com/client/v4/accounts/\${process.env.CF_ACCOUNT_ID}/ai/v1/chat/completions\`,
|
|
@@ -31,7 +31,13 @@ const agent = createReactAgent({
|
|
|
31
31
|
|
|
32
32
|
// Stream a compiled LangGraph agent to the browser as OpenAI-format SSE.
|
|
33
33
|
async function chatHandler(request: Request): Promise<Response> {
|
|
34
|
-
|
|
34
|
+
let chatBody: ChatRequestBody;
|
|
35
|
+
try {
|
|
36
|
+
chatBody = await readChatRequest(request);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
return toChatErrorResponse(error);
|
|
39
|
+
}
|
|
40
|
+
const { messages } = chatBody;
|
|
35
41
|
|
|
36
42
|
// agent.stream() coerces plain {role, content} objects into BaseMessage
|
|
37
43
|
// instances itself, including OpenAI-shaped tool_calls, so the wire messages
|
|
@@ -28,7 +28,13 @@ const mastra = new MastraClient({ baseUrl: MASTRA_URL });
|
|
|
28
28
|
|
|
29
29
|
// Proxy a Mastra agent to the browser as OpenAI-format SSE.
|
|
30
30
|
async function chatHandler(request: Request): Promise<Response> {
|
|
31
|
-
|
|
31
|
+
let chatBody: ChatRequestBody;
|
|
32
|
+
try {
|
|
33
|
+
chatBody = await readChatRequest(request);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return toChatErrorResponse(error);
|
|
36
|
+
}
|
|
37
|
+
const { messages } = chatBody;
|
|
32
38
|
|
|
33
39
|
// MastraClient's Agent.stream() takes AI-SDK CoreMessage[], not the OpenAI
|
|
34
40
|
// wire format: each message has ONE literal role (not a union) and content
|
|
@@ -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',
|