@kitn.ai/ui 0.27.0 → 0.29.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 +10 -2
- package/dist/{Icon-BnFKqqtY.js → Icon-DObhsRtm.js} +1 -1
- package/dist/{action-icons-CBskfBMz.js → action-icons-CcgNN2oT.js} +1 -1
- package/dist/agent-tooling/construct/public.d.ts +15 -0
- package/dist/agent-tooling/construct/schema.d.ts +111 -0
- package/dist/arrow-down-CjRU3JgV.js +1 -0
- package/dist/arrow-left-Bnpb1UEW.js +1 -0
- package/dist/{artifact-CvXEm1lS.js → artifact-Bwt7OgcR.js} +1 -1
- package/dist/{attachments-C43ZegEq.js → attachments-Qr4kg-2b.js} +1 -1
- package/dist/{audio-visualizer-B-79y1Tj.js → audio-visualizer-DToYdfSc.js} +1 -1
- package/dist/{badge-BpUbq8c0.js → badge-CdEu_jn2.js} +1 -1
- package/dist/{bell-sn17Ip9v.js → bell-DQAAOpDz.js} +1 -1
- package/dist/{button-BH6rN0U-.js → button-DkuOnGLy.js} +1 -1
- package/dist/{card-renderer-Cb1gQmth.js → card-renderer-Jx70m6_l.js} +1 -1
- package/dist/{card-routing-DifQgS7n.js → card-routing-0YidER_3.js} +1 -1
- package/dist/check-njkVdZu2.js +1 -0
- package/dist/{checkbox-Dc9Yov_B.js → checkbox-D7iRB3nL.js} +1 -1
- package/dist/{checkbox-group-B1kDLLYk.js → checkbox-group-B2_rpoPM.js} +1 -1
- package/dist/chevron-down-B72s6PXR.js +1 -0
- package/dist/chevron-right-BatuHzUm.js +1 -0
- package/dist/{choice-card-BT3c1S8g.js → choice-card-m0BYqqOK.js} +1 -1
- package/dist/circle-check-B62pODB3.js +1 -0
- package/dist/{circle-x-wobWkoSx.js → circle-x-BPG0iPst.js} +1 -1
- package/dist/circle-zx0ByzY9.js +1 -0
- package/dist/code-block-ClMLKb7C.js +1 -0
- package/dist/{collapsible-CPJbf6FM.js → collapsible-B4DlWicL.js} +1 -1
- package/dist/components/chat-thread.d.ts +89 -1
- package/dist/components/conversation-item.d.ts +16 -0
- package/dist/components/conversation-panel.d.ts +29 -0
- package/dist/components/home-panel.d.ts +34 -0
- package/dist/components/widget-tab-bar.d.ts +16 -0
- package/dist/{composer-CjngLzal.js → composer-sQ0HcvyG.js} +3 -3
- package/dist/{confirm-card-DCx_A4bU.js → confirm-card-rQWDQsnC.js} +1 -1
- package/dist/construct-cli.es.js +185 -17
- package/dist/construct.d.ts +5 -0
- package/dist/construct.js +327 -0
- package/dist/{context-BY8V-Nlp.js → context-0kg4ZX1J.js} +1 -1
- package/dist/controllable-CnGpMF6r.js +1 -0
- package/dist/conversation-item-BxwQLaFJ.js +1 -0
- package/dist/conversation-list-BCfGN1JU.js +1 -0
- package/dist/{copy-B6DO4PqB.js → copy-n9u9rqpi.js} +1 -1
- package/dist/core-C8fzo39E.js +1 -12
- package/dist/{create-tween-CnMHviKB.js → create-tween-C-q-JCM-.js} +1 -1
- package/dist/{create-tween-D1aiGZZh.js → create-tween-CRrJXxBd.js} +1 -1
- package/dist/{create-tween-BPzaefTp.js → create-tween-DZ3RRDHD.js} +1 -1
- package/dist/{create-tween-DUSQUKpr.js → create-tween-uPkArjIG.js} +1 -1
- package/dist/custom-elements.json +51 -2
- package/dist/{default-input-CucQEOqH.js → default-input-CaMrJkgH.js} +1 -1
- package/dist/define-By-uL588.js +1 -0
- package/dist/define.js +2 -2
- package/dist/define.server.js +2 -2
- package/dist/{disclosure-CbLtajUQ.js → disclosure-WUImBbDv.js} +1 -1
- package/dist/{dropdown-BwhViJFd.js → dropdown-wZVeJOMd.js} +1 -1
- package/dist/elements/agent-card.js +1 -1
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/audio-visualizer.js +1 -1
- package/dist/elements/avatar.js +1 -1
- package/dist/elements/badge.js +1 -1
- package/dist/elements/button.js +1 -1
- package/dist/elements/card.js +1 -1
- package/dist/elements/cards.js +1 -1
- package/dist/elements/chain-of-thought.js +1 -1
- package/dist/elements/chat-scope-picker.js +1 -1
- package/dist/elements/chat-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkbox-group.js +1 -1
- package/dist/elements/checkbox.js +1 -1
- 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.js +1 -1
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/dock.js +3 -3
- package/dist/elements/dropdown.js +1 -1
- package/dist/elements/editable-label.js +1 -1
- 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.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/radio-group.js +1 -1
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +1 -1
- 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.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/slider.js +1 -1
- 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 +20 -4
- package/dist/{ellipsis-DFr4Vqr3.js → ellipsis-CbOpJnU3.js} +1 -1
- package/dist/{embed-5s_vU4J0.js → embed-CrfjkbUG.js} +1 -1
- package/dist/engine-javascript-C1x7zo1_.js +1 -141
- package/dist/{external-link-u9d85ORQ.js → external-link-ByXAwBBQ.js} +1 -1
- package/dist/{file-text-CX7_x42o.js → file-text-DMib2qwt.js} +1 -1
- package/dist/{file-tree-D3kBjXI5.js → file-tree-CJqM2aP7.js} +1 -1
- package/dist/{folder-V-YdIL8f.js → folder-_BAb929C.js} +1 -1
- package/dist/{form-BRNUMCN2.js → form-DSZgZQZk.js} +1 -1
- package/dist/{hover-card-DaeiYrTA.js → hover-card-BQbul9eq.js} +1 -1
- package/dist/{icon-BpixdZNq.js → icon-CdcfRhYI.js} +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2842 -2703
- package/dist/index.server.js +2820 -2681
- package/dist/{info-8mFsIjS3.js → info-DsFjGuI_.js} +1 -1
- package/dist/{input-d1Yuu-hz.js → input-DKbmkMos.js} +1 -1
- package/dist/kai.es.js +1 -1
- package/dist/{kbd-961zMPFB.js → kbd-PvdGhX4s.js} +1 -1
- package/dist/{link-preview-DIIbV2Sd.js → link-preview-DbM1866N.js} +1 -1
- package/dist/{link-DlZZBGaR.js → link-zF8PzWfF.js} +1 -1
- package/dist/{loader-YQszHFaW.js → loader-BM4CEz4G.js} +1 -1
- package/dist/{markdown-CVk1MPsj.js → markdown-LMvmAyBk.js} +7 -7
- package/dist/mcp.es.js +79 -5
- package/dist/{message-CwKVEdZg.js → message-BSsgvHvf.js} +1 -1
- package/dist/{message-DS8lzHc1.js → message-BYZECyek.js} +1 -1
- package/dist/message-circle-DruwObfX.js +1 -0
- package/dist/message-square-BG6q6mlm.js +1 -0
- package/dist/{minimize-2-UEZmP2QT.js → minimize-2-BQRoBV1V.js} +1 -1
- package/dist/{model-switcher-Kk1jEQ5t.js → model-switcher-CMpCOYcB.js} +1 -1
- package/dist/{overlay-C1od6LOz.js → overlay-Cb-MkyDa.js} +1 -1
- package/dist/{panel-right-ryET0yyn.js → panel-right-i8warFxT.js} +1 -1
- package/dist/{paperclip-BDNoZFJP.js → paperclip-ByY3UVO6.js} +1 -1
- package/dist/play-BvKKq3fX.js +1 -0
- package/dist/primitives/conversation-store.d.ts +54 -0
- package/dist/{progress-bar-BvYX0aoy.js → progress-bar-jVAHvMUf.js} +1 -1
- package/dist/{prompt-suggestion-BRFu2lcu.js → prompt-suggestion-CD-qtNgC.js} +1 -1
- package/dist/{radio-DcL23cNJ.js → radio-BsoBMhkF.js} +1 -1
- package/dist/react/index.d.ts +393 -0
- package/dist/react.js +20 -20
- package/dist/{reasoning-Cackq4Rh.js → reasoning-4kBPtlTx.js} +1 -1
- package/dist/register-impl-B1hLkrI8.js +424 -0
- package/dist/{resizable-Cp9HUYih.js → resizable-uPmY9VY-.js} +1 -1
- package/dist/{rotate-ccw-0DLRDHH5.js → rotate-ccw-B2xn93py.js} +1 -1
- package/dist/{rotate-cw-DOB9IGcU.js → rotate-cw-CMVLoCyH.js} +1 -1
- package/dist/{scroll-area-emVmWP4K.js → scroll-area-BqLUo8ZZ.js} +1 -1
- package/dist/{scroll-button-CeP_gv1x.js → scroll-button-DslXiJyL.js} +1 -1
- package/dist/{select-CWmu00tS.js → select-2cLRtPGT.js} +1 -1
- package/dist/{separator-CE6vly6z.js → separator-BWQtNJcM.js} +1 -1
- package/dist/{settings-CE-78SvZ.js → settings-BBR63jLg.js} +1 -1
- package/dist/{settings-group-ZFWNxFQR.js → settings-group-B2ciy-Iw.js} +1 -1
- package/dist/{skeleton-BebdP4mf.js → skeleton-CkHI-HgC.js} +1 -1
- package/dist/{slider-BAKRYUlz.js → slider-DDR9U-i0.js} +1 -1
- package/dist/{solid-BE5ui6sy.js → solid-BzCFdL25.js} +7720 -7058
- package/dist/{solid-DFhsce3O.js → solid-DruVOXnf.js} +5864 -5283
- package/dist/solid.d.ts +6 -2
- package/dist/solid.js +217 -210
- package/dist/solid.server.js +217 -210
- package/dist/{source-B0FlguSx.js → source-GEnFF528.js} +1 -1
- package/dist/{star-CBMZvLuN.js → star-CilHq8GP.js} +1 -1
- package/dist/store-Ciyk6eZG.js +1 -0
- package/dist/{switch-CViARZCy.js → switch-KkeQIbuH.js} +1 -1
- package/dist/{tasks-card-MjCcOhR_.js → tasks-card-BrN4bggJ.js} +1 -1
- package/dist/{text-shimmer-DKlwRF2G.js → text-shimmer-Dcj1MHuq.js} +1 -1
- package/dist/{textarea-3ay2Me2-.js → textarea-Dpvbngbg.js} +1 -1
- package/dist/theme.tokens.css +28 -0
- package/dist/{thumbs-up-DQcu2HMO.js → thumbs-up-CsT9Wzb9.js} +1 -1
- package/dist/{toast-store-C6yKOhbk.js → toast-store-tgK6MuMv.js} +1 -1
- package/dist/{tool-BCYNlfKy.js → tool-DzUjIBT8.js} +1 -1
- package/dist/{tooltip-05zI4hjs.js → tooltip-Bkkg_4t0.js} +1 -1
- package/dist/{trash-2-BR1RxGky.js → trash-2-jBKxjGGQ.js} +1 -1
- package/dist/{triangle-alert-DHohDOCy.js → triangle-alert-DlQ68ppf.js} +1 -1
- package/dist/types.d.ts +48 -3
- package/dist/ui/dock.d.ts +29 -0
- package/dist/{upload-BbGW6A_u.js → upload-TcAHXv9W.js} +1 -1
- package/dist/{url-scheme-policy-DHpoTJwB.js → url-scheme-policy-DR5DgvkM.js} +1 -1
- package/dist/{use-card-resolution-CJW4grFa.js → use-card-resolution-DaZqAdWf.js} +1 -1
- package/dist/{variant-aurora-xiMPty8f.js → variant-aurora-BhLuYxJR.js} +1 -1
- package/dist/{variant-aurora-BUOVgPOB.js → variant-aurora-CNmfHhcx.js} +2 -2
- package/dist/{variant-aurora-CUsUJvFI.js → variant-aurora-D6GsdTPA.js} +2 -2
- package/dist/{variant-aurora-CW2i4bfr.js → variant-aurora-DbW_OWL2.js} +1 -1
- package/dist/{variant-custom-D91HjAKz.js → variant-custom-BRvaNHSi.js} +1 -1
- package/dist/{variant-custom-BQtOnmeO.js → variant-custom-BvYHR62b.js} +1 -1
- package/dist/{variant-custom-CAnzzppW.js → variant-custom-DMTrEO5w.js} +2 -2
- package/dist/{variant-custom-CAm9L-E5.js → variant-custom-DV2x254s.js} +2 -2
- package/dist/{variant-wave-Ca1Ls4_y.js → variant-wave-BNMGrDfj.js} +2 -2
- package/dist/{variant-wave-uY5crU_K.js → variant-wave-BpDd8t3Z.js} +1 -1
- package/dist/{variant-wave-CxX39DKd.js → variant-wave-CyBueXj9.js} +2 -2
- package/dist/{variant-wave-DYyzIIZv.js → variant-wave-sp-qwsdI.js} +1 -1
- package/dist/x-Z5UntkIJ.js +1 -0
- package/frameworks/react/index.tsx +14 -4
- package/llms-full.txt +7 -2
- package/package.json +16 -16
- package/src/elements/element-meta.json +49 -6
- package/theme.css +28 -0
- package/bin/route.test.js +0 -58
- package/dist/arrow-down-Br0qtpsz.js +0 -1
- package/dist/arrow-left-Dj7xgE12.js +0 -1
- package/dist/check-DPeeMUYx.js +0 -1
- package/dist/chevron-down-CHSWk7ZT.js +0 -1
- package/dist/chevron-right-B7l_fMgx.js +0 -1
- package/dist/circle-Bb0yOpqM.js +0 -1
- package/dist/circle-check-BGZpCiz7.js +0 -1
- package/dist/code-block-CUQ5mtv0.js +0 -1
- package/dist/controllable-D43BKmtX.js +0 -1
- package/dist/conversation-list-D4lLU_yS.js +0 -1
- package/dist/define-CGGX-7Vg.js +0 -1
- package/dist/message-circle-Xf8Q-vZp.js +0 -1
- package/dist/message-square-Cxnwgkns.js +0 -1
- package/dist/play-OFIvYXUQ.js +0 -1
- package/dist/register-impl-C7rENvPK.js +0 -424
- package/dist/store-7eoadydQ.js +0 -1
- package/dist/x-C5m1JFgi.js +0 -1
- package/src/agent-tooling/archetypes.ts +0 -205
- package/src/agent-tooling/catalog/README.md +0 -676
- package/src/agent-tooling/catalog/catalog-types.ts +0 -231
- package/src/agent-tooling/catalog/fabrications.ts +0 -96
- package/src/agent-tooling/catalog/invariants.ts +0 -284
- package/src/agent-tooling/catalog/labs-titles.ts +0 -114
- package/src/agent-tooling/catalog/scenarios.ts +0 -87
- package/src/agent-tooling/catalog/surfaces.ts +0 -347
- package/src/agent-tooling/construct/cli-entry.ts +0 -8
- package/src/agent-tooling/construct/cli.ts +0 -131
- package/src/agent-tooling/construct/codegen.ts +0 -1353
- package/src/agent-tooling/construct/construct.v1.schema.json +0 -250
- package/src/agent-tooling/construct/dev.ts +0 -148
- package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +0 -7
- package/src/agent-tooling/construct/fixtures/ops-console.construct.json +0 -44
- package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +0 -23
- package/src/agent-tooling/construct/schema.ts +0 -371
- package/src/agent-tooling/integrations/anthropic.ts +0 -442
- package/src/agent-tooling/integrations/cloudflare.ts +0 -218
- package/src/agent-tooling/integrations/langgraph.ts +0 -133
- package/src/agent-tooling/integrations/mastra.ts +0 -182
- package/src/agent-tooling/integrations/mock.ts +0 -148
- package/src/agent-tooling/integrations/ollama.ts +0 -97
- package/src/agent-tooling/integrations/openai.ts +0 -95
- package/src/agent-tooling/integrations/openrouter.ts +0 -82
- package/src/agent-tooling/integrations/pi.ts +0 -176
- package/src/agent-tooling/integrations/pydantic-ai.ts +0 -108
- package/src/agent-tooling/integrations/vercel-ai-sdk.ts +0 -398
- package/src/agent-tooling/mcp/css-raw.d.ts +0 -12
- package/src/agent-tooling/mcp/manifest.ts +0 -422
- package/src/agent-tooling/mcp/server.ts +0 -116
- package/src/agent-tooling/mcp/stdio.ts +0 -18
- package/src/agent-tooling/mcp/tools/construct.ts +0 -130
- package/src/agent-tooling/mcp/tools/debug.ts +0 -621
- package/src/agent-tooling/mcp/tools/reference.ts +0 -980
- package/src/agent-tooling/mcp/tools/scaffold.ts +0 -6708
- package/src/agent-tooling/mcp/tools/theme.ts +0 -456
- package/src/agent-tooling/mcp/tools/types.ts +0 -14
- package/src/agent-tooling/mcp/validate-args.ts +0 -141
- package/src/agent-tooling/recipes/composed-thread.ts +0 -714
- package/src/agent-tooling/recipes/index.ts +0 -22
- package/src/agent-tooling/recipes/types.ts +0 -32
- package/src/agent-tooling/registry.ts +0 -132
- package/src/agent-tooling/route-emit.ts +0 -305
- package/src/agent-tooling/types.ts +0 -390
- package/src/components/artifact-card.tsx +0 -120
- package/src/components/artifact.tsx +0 -914
- package/src/components/attachment-types.ts +0 -66
- package/src/components/attachments.tsx +0 -536
- package/src/components/audio-visualizer/audio-visualizer.voice-fixture.ts +0 -257
- package/src/components/audio-visualizer/aurora.glsl.ts +0 -330
- package/src/components/audio-visualizer/fit-scale.ts +0 -112
- package/src/components/audio-visualizer/index.tsx +0 -528
- package/src/components/audio-visualizer/labs/lab-choreography.ts +0 -187
- package/src/components/audio-visualizer/labs/lab-shaders.ts +0 -363
- package/src/components/audio-visualizer/labs/lab-visualizer.tsx +0 -102
- package/src/components/audio-visualizer/shader-canvas.tsx +0 -876
- package/src/components/audio-visualizer/sizes.ts +0 -54
- package/src/components/audio-visualizer/variant-aurora.tsx +0 -305
- package/src/components/audio-visualizer/variant-bar.tsx +0 -175
- package/src/components/audio-visualizer/variant-custom.tsx +0 -344
- package/src/components/audio-visualizer/variant-grid.tsx +0 -175
- package/src/components/audio-visualizer/variant-radial.tsx +0 -163
- package/src/components/audio-visualizer/variant-wave.tsx +0 -130
- package/src/components/audio-visualizer/wave.glsl.ts +0 -130
- package/src/components/card-fallback.tsx +0 -60
- package/src/components/card-renderer.tsx +0 -220
- package/src/components/card.tsx +0 -212
- package/src/components/chain-of-thought.tsx +0 -348
- package/src/components/chat-container.tsx +0 -89
- package/src/components/chat-scope-picker.tsx +0 -57
- package/src/components/chat-thread.tsx +0 -518
- package/src/components/checkpoint.tsx +0 -88
- package/src/components/choice-card.tsx +0 -743
- package/src/components/coachmark.tsx +0 -249
- package/src/components/code-block.tsx +0 -181
- package/src/components/composer-dom.ts +0 -159
- package/src/components/composer-highlight.ts +0 -242
- package/src/components/composer-history.ts +0 -82
- package/src/components/composer.tsx +0 -1075
- package/src/components/confirm-card.tsx +0 -402
- package/src/components/context.tsx +0 -377
- package/src/components/conversation-item.tsx +0 -218
- package/src/components/conversation-list.tsx +0 -429
- package/src/components/dismissed-stub.tsx +0 -80
- package/src/components/embed.tsx +0 -200
- package/src/components/empty.tsx +0 -177
- package/src/components/feedback-bar.tsx +0 -166
- package/src/components/file-tree.tsx +0 -530
- package/src/components/file-upload.tsx +0 -167
- package/src/components/form-widgets.tsx +0 -525
- package/src/components/form.tsx +0 -1296
- package/src/components/image.tsx +0 -65
- package/src/components/link-preview.tsx +0 -195
- package/src/components/loader.tsx +0 -367
- package/src/components/markdown.tsx +0 -162
- package/src/components/message-skills.tsx +0 -40
- package/src/components/message.tsx +0 -765
- package/src/components/model-switcher.tsx +0 -132
- package/src/components/prompt-input.tsx +0 -188
- package/src/components/prompt-suggestion.tsx +0 -150
- package/src/components/reasoning.tsx +0 -293
- package/src/components/response-compare-types.ts +0 -152
- package/src/components/response-compare.tsx +0 -455
- package/src/components/response-stream.tsx +0 -109
- package/src/components/screen.tsx +0 -181
- package/src/components/scroll-button.tsx +0 -93
- package/src/components/source.tsx +0 -192
- package/src/components/tasks-card.tsx +0 -801
- package/src/components/text-shimmer.tsx +0 -37
- package/src/components/thinking-bar.tsx +0 -50
- package/src/components/thread.tsx +0 -228
- package/src/components/toast.tsx +0 -503
- package/src/components/tool-classify.ts +0 -26
- package/src/components/tool-types.ts +0 -49
- package/src/components/tool.tsx +0 -190
- package/src/components/use-card-resolution.ts +0 -44
- package/src/components/voice-input.tsx +0 -222
- package/src/components/voice-output.tsx +0 -242
- package/src/components/workspace-shell.tsx +0 -285
- package/src/diagnostics/hook.ts +0 -352
- package/src/diagnostics/index.ts +0 -82
- package/src/diagnostics/report-request.ts +0 -296
- package/src/elements/agent-card.tsx +0 -67
- package/src/elements/artifact.tsx +0 -194
- package/src/elements/attachments.tsx +0 -140
- package/src/elements/audio-visualizer.tsx +0 -227
- package/src/elements/autoloader.ts +0 -89
- package/src/elements/avatar.tsx +0 -33
- package/src/elements/badge.tsx +0 -32
- package/src/elements/button.tsx +0 -149
- package/src/elements/card.tsx +0 -133
- package/src/elements/cards.tsx +0 -266
- package/src/elements/chain-of-thought.tsx +0 -141
- package/src/elements/chat-scope-picker.tsx +0 -65
- package/src/elements/chat-types.ts +0 -102
- package/src/elements/chat-workspace.tsx +0 -188
- package/src/elements/chat.tsx +0 -212
- package/src/elements/checkbox-group.tsx +0 -164
- package/src/elements/checkbox.tsx +0 -134
- package/src/elements/checkpoint.tsx +0 -47
- package/src/elements/choice.tsx +0 -100
- package/src/elements/coachmark.tsx +0 -90
- package/src/elements/code-block.tsx +0 -60
- package/src/elements/command.tsx +0 -197
- package/src/elements/compare.tsx +0 -111
- package/src/elements/compiled.css +0 -2
- package/src/elements/composer.tsx +0 -162
- package/src/elements/confirm-card.tsx +0 -76
- package/src/elements/context-meter.tsx +0 -107
- package/src/elements/conversation-item.tsx +0 -120
- package/src/elements/conversation-list.tsx +0 -203
- package/src/elements/css.ts +0 -5
- package/src/elements/default-input.tsx +0 -392
- package/src/elements/define-entry.ts +0 -12
- package/src/elements/define.tsx +0 -563
- package/src/elements/diagnostic-events.ts +0 -114
- package/src/elements/dialog.tsx +0 -145
- package/src/elements/disclosure.ts +0 -95
- package/src/elements/dock.tsx +0 -215
- package/src/elements/dropdown.tsx +0 -123
- package/src/elements/editable-label.tsx +0 -140
- package/src/elements/element-data-types.ts +0 -152
- package/src/elements/element-diagnostics.ts +0 -392
- package/src/elements/element-manifest.json +0 -354
- package/src/elements/element-nonscalar.json +0 -185
- package/src/elements/element-types.d.ts +0 -4337
- package/src/elements/embed.tsx +0 -35
- package/src/elements/empty.tsx +0 -29
- package/src/elements/feedback-bar.tsx +0 -66
- package/src/elements/file-tree.tsx +0 -62
- package/src/elements/file-upload.tsx +0 -44
- package/src/elements/form.tsx +0 -94
- package/src/elements/hover-card.tsx +0 -80
- package/src/elements/icon.tsx +0 -41
- package/src/elements/image.tsx +0 -32
- package/src/elements/input.tsx +0 -492
- package/src/elements/kbd.tsx +0 -45
- package/src/elements/link-preview.tsx +0 -34
- package/src/elements/loader.tsx +0 -25
- package/src/elements/markdown.tsx +0 -38
- package/src/elements/menu.tsx +0 -241
- package/src/elements/message-skills.tsx +0 -83
- package/src/elements/message.tsx +0 -361
- package/src/elements/model-switcher.tsx +0 -121
- package/src/elements/nav.tsx +0 -87
- package/src/elements/notice.tsx +0 -53
- package/src/elements/pane-grid.tsx +0 -111
- package/src/elements/pane-group.tsx +0 -119
- package/src/elements/pane.tsx +0 -129
- package/src/elements/popover.tsx +0 -81
- package/src/elements/progress-bar.tsx +0 -39
- package/src/elements/prompt-dock.tsx +0 -82
- package/src/elements/prompt-input.tsx +0 -240
- package/src/elements/prompt-suggestions.tsx +0 -113
- package/src/elements/radio-group.tsx +0 -129
- package/src/elements/reasoning.tsx +0 -71
- package/src/elements/register-impl.ts +0 -148
- package/src/elements/register.ts +0 -76
- package/src/elements/remote.tsx +0 -225
- package/src/elements/resizable.globals.d.ts +0 -36
- package/src/elements/resizable.tsx +0 -704
- package/src/elements/response-stream.tsx +0 -40
- package/src/elements/screen.tsx +0 -90
- package/src/elements/scroll-area.tsx +0 -33
- package/src/elements/scroll-button.tsx +0 -179
- package/src/elements/search.tsx +0 -185
- package/src/elements/segmented.tsx +0 -129
- package/src/elements/select.tsx +0 -165
- package/src/elements/separator.tsx +0 -36
- package/src/elements/setting-item.tsx +0 -50
- package/src/elements/settings-group.tsx +0 -37
- package/src/elements/skeleton.tsx +0 -45
- package/src/elements/slider.tsx +0 -171
- package/src/elements/slot-text.ts +0 -73
- package/src/elements/slots.ts +0 -787
- package/src/elements/source.tsx +0 -126
- package/src/elements/status.tsx +0 -42
- package/src/elements/styles.css +0 -328
- package/src/elements/switch.tsx +0 -118
- package/src/elements/tabs.tsx +0 -95
- package/src/elements/tasks.tsx +0 -103
- package/src/elements/text-shimmer.tsx +0 -28
- package/src/elements/thinking-bar.tsx +0 -43
- package/src/elements/thread.tsx +0 -139
- package/src/elements/toast.tsx +0 -106
- package/src/elements/tool.tsx +0 -78
- package/src/elements/tooltip.tsx +0 -85
- package/src/elements/validate-messages.ts +0 -76
- package/src/elements/voice-input.tsx +0 -100
- package/src/elements/voice-output.tsx +0 -82
- package/src/index.ts +0 -428
- package/src/primitives/audio-bands.ts +0 -174
- package/src/primitives/card-component-types.ts +0 -63
- package/src/primitives/card-contract.ts +0 -80
- package/src/primitives/card-data-types.ts +0 -361
- package/src/primitives/card-host.tsx +0 -37
- package/src/primitives/card-recovery.ts +0 -160
- package/src/primitives/card-registry.tsx +0 -78
- package/src/primitives/card-resolution.ts +0 -36
- package/src/primitives/card-routing.ts +0 -85
- package/src/primitives/card-schemas/artifact.schema.json +0 -92
- package/src/primitives/card-schemas/card-envelope.schema.json +0 -14
- package/src/primitives/card-schemas/card-event.schema.json +0 -12
- package/src/primitives/card-schemas/choice.schema.json +0 -75
- package/src/primitives/card-schemas/confirm.schema.json +0 -65
- package/src/primitives/card-schemas/embed.schema.json +0 -63
- package/src/primitives/card-schemas/form.result.schema.json +0 -7
- package/src/primitives/card-schemas/form.schema.json +0 -53
- package/src/primitives/card-schemas/link.schema.json +0 -54
- package/src/primitives/card-schemas/tasks.result.schema.json +0 -16
- package/src/primitives/card-schemas/tasks.schema.json +0 -83
- package/src/primitives/card-tags.ts +0 -61
- package/src/primitives/card-validate-cards.ts +0 -255
- package/src/primitives/card-validate-schemas.ts +0 -58
- package/src/primitives/card-validate.ts +0 -131
- package/src/primitives/chat-config.tsx +0 -103
- package/src/primitives/composer-model.ts +0 -49
- package/src/primitives/composer-triggers.ts +0 -35
- package/src/primitives/controllable.ts +0 -21
- package/src/primitives/create-kai-chat.ts +0 -66
- package/src/primitives/create-tween.ts +0 -266
- package/src/primitives/embed-providers.ts +0 -254
- package/src/primitives/field-mask.ts +0 -256
- package/src/primitives/field-semantics.ts +0 -115
- package/src/primitives/highlighter.ts +0 -157
- package/src/primitives/input-mask.ts +0 -853
- package/src/primitives/link-preview.ts +0 -87
- package/src/primitives/message-feedback.ts +0 -124
- package/src/primitives/pdf-preview.ts +0 -121
- package/src/primitives/toast-store.ts +0 -338
- package/src/primitives/url-scheme-policy.ts +0 -70
- package/src/primitives/use-audio-analysis.ts +0 -340
- package/src/primitives/use-auto-resize.ts +0 -31
- package/src/primitives/use-resize-observer.ts +0 -45
- package/src/primitives/use-sequencer.ts +0 -72
- package/src/primitives/use-speech-recognition.ts +0 -146
- package/src/primitives/use-stick-to-bottom.ts +0 -75
- package/src/primitives/use-text-stream.ts +0 -143
- package/src/primitives/use-voice-recorder.ts +0 -62
- package/src/primitives/visualizer-sequences.ts +0 -350
- package/src/remote/host-embed.ts +0 -345
- package/src/remote/index.ts +0 -2
- package/src/remote/origin.ts +0 -30
- package/src/remote/provider-runtime.ts +0 -262
- package/src/remote/provider.ts +0 -2
- package/src/remote/validate.ts +0 -22
- package/src/remote/version.ts +0 -12
- package/src/remote/wire.ts +0 -48
- package/src/schemas/from-tool-call.ts +0 -151
- package/src/schemas/index.ts +0 -341
- package/src/schemas/provider-subsets.ts +0 -538
- package/src/schemas/registry.ts +0 -355
- package/src/schemas/tool-defs.ts +0 -793
- package/src/solid.ts +0 -165
- package/src/state/index.ts +0 -44
- package/src/state/messages.ts +0 -53
- package/src/state/mock.ts +0 -297
- package/src/state/parts.ts +0 -295
- package/src/state/persistence.ts +0 -180
- package/src/state/stream.ts +0 -246
- package/src/state/suggestions.ts +0 -9
- package/src/state/threads.ts +0 -142
- package/src/test-utils/fake-clock.ts +0 -88
- package/src/types.ts +0 -53
- package/src/ui/action-icons.ts +0 -53
- package/src/ui/agent-card.tsx +0 -218
- package/src/ui/avatar.tsx +0 -23
- package/src/ui/badge.tsx +0 -21
- package/src/ui/button.tsx +0 -49
- package/src/ui/card.tsx +0 -245
- package/src/ui/checkbox-group.tsx +0 -153
- package/src/ui/checkbox.tsx +0 -63
- package/src/ui/collapsible.tsx +0 -163
- package/src/ui/command.tsx +0 -103
- package/src/ui/dialog.tsx +0 -223
- package/src/ui/dock.tsx +0 -584
- package/src/ui/dropdown.tsx +0 -636
- package/src/ui/editable-label.tsx +0 -112
- package/src/ui/hover-card.tsx +0 -345
- package/src/ui/icon.tsx +0 -167
- package/src/ui/input.tsx +0 -391
- package/src/ui/kbd.tsx +0 -118
- package/src/ui/nav.tsx +0 -282
- package/src/ui/notice.tsx +0 -84
- package/src/ui/overlay.tsx +0 -277
- package/src/ui/pane-grid.tsx +0 -117
- package/src/ui/pane-group.tsx +0 -291
- package/src/ui/pane.tsx +0 -193
- package/src/ui/popover.tsx +0 -106
- package/src/ui/progress-bar.tsx +0 -83
- package/src/ui/prompt-dock.tsx +0 -145
- package/src/ui/radio.tsx +0 -150
- package/src/ui/resizable.tsx +0 -693
- package/src/ui/scroll-area.tsx +0 -26
- package/src/ui/segmented.tsx +0 -109
- package/src/ui/select.tsx +0 -168
- package/src/ui/separator.tsx +0 -10
- package/src/ui/settings-group.tsx +0 -67
- package/src/ui/skeleton.tsx +0 -73
- package/src/ui/slider.tsx +0 -178
- package/src/ui/status.tsx +0 -48
- package/src/ui/switch.tsx +0 -119
- package/src/ui/tabs.tsx +0 -168
- package/src/ui/textarea.tsx +0 -21
- package/src/ui/tooltip.tsx +0 -118
- package/src/utils/cn.ts +0 -30
- package/src/wire/chunk.ts +0 -248
- package/src/wire/consume.ts +0 -599
- package/src/wire/diagnostics.ts +0 -727
- package/src/wire/encode-probe.ts +0 -214
- package/src/wire/encode.ts +0 -947
- package/src/wire/files.ts +0 -342
- package/src/wire/formats/anthropic.ts +0 -274
- package/src/wire/formats/openai.ts +0 -228
- package/src/wire/index.ts +0 -95
- package/src/wire/media-types.ts +0 -344
- package/src/wire/read.ts +0 -396
- package/src/wire/sink-helpers.ts +0 -50
- package/src/wire/sse.ts +0 -153
package/src/schemas/tool-defs.ts
DELETED
|
@@ -1,793 +0,0 @@
|
|
|
1
|
-
// Card schemas -> provider tool definitions.
|
|
2
|
-
//
|
|
3
|
-
// THE PROBLEM THIS SOLVES
|
|
4
|
-
// -----------------------
|
|
5
|
-
// A developer who wants a model to produce a `confirm` card today hand-writes a tool
|
|
6
|
-
// definition that approximates a schema they cannot import, then hand-writes a mapper
|
|
7
|
-
// from the model's arguments back to a CardEnvelope. This repo's own reference
|
|
8
|
-
// harness does exactly that and says so in a comment
|
|
9
|
-
// (examples/internal/openrouter-spike/src/tools.ts). That is the same shape written
|
|
10
|
-
// in five places. This file removes two of them: the tool definition is GENERATED
|
|
11
|
-
// from the schema the card already validates against, so it cannot drift.
|
|
12
|
-
//
|
|
13
|
-
// WHAT THIS FILE IS NOT
|
|
14
|
-
// ---------------------
|
|
15
|
-
// It is not a provider client and it never becomes one. No `openai`, no
|
|
16
|
-
// `@anthropic-ai/sdk`, no `fetch`. The provider envelope shapes below are declared
|
|
17
|
-
// here as plain structural types, which is what lets `@kitn.ai/ui` keep its rule that
|
|
18
|
-
// nothing under `src/` depends on a provider SDK. The kit PARSES and PROJECTS; the
|
|
19
|
-
// consumer FETCHES.
|
|
20
|
-
//
|
|
21
|
-
// THE HEADLINE, MEASURED, NOT RECALLED
|
|
22
|
-
// ------------------------------------
|
|
23
|
-
// `strict: true` currently throws for EVERY built-in card on BOTH providers. That is
|
|
24
|
-
// not a bug in this file, it is what the schemas say when you hold them against the
|
|
25
|
-
// two documented subsets (see provider-subsets.ts for the two source docs). The
|
|
26
|
-
// authored schemas use `minLength` (confirm/choice/tasks), `maxLength` +
|
|
27
|
-
// `format: "uri"` (link/embed/artifact), `minimum` (tasks/embed/artifact),
|
|
28
|
-
// `maxItems` (confirm/artifact), `pattern` (embed), `if`/`then` (embed), `oneOf`
|
|
29
|
-
// (artifact), a free-form object (form) and untyped `payload`/`allowOther` members
|
|
30
|
-
// (confirm/choice). Non-strict is therefore the default and the mode the
|
|
31
|
-
// five-configuration conformance spike actually proved cards in — but since F-20 it
|
|
32
|
-
// is no longer a raw pass-through either: a root combinator (`anyOf` on artifact,
|
|
33
|
-
// `allOf` on embed) gets HTTP 400 from OpenAI and Anthropic even non-strict, so the
|
|
34
|
-
// non-strict projection relaxes those root combinators LOUDLY (constraint restated in
|
|
35
|
-
// the description, console.warn names the relaxation, registry.validate still
|
|
36
|
-
// enforces at render time). The jsonschema projection stays byte-faithful to the
|
|
37
|
-
// authored schema.
|
|
38
|
-
//
|
|
39
|
-
// The alternative was to silently drop the offending keywords. That is rejected for
|
|
40
|
-
// the reason the plan gives: a dropped `maxItems: 4` means the model emits six
|
|
41
|
-
// actions and the card renders six buttons, with nothing anywhere saying the contract
|
|
42
|
-
// was quietly loosened. A throw that NAMES the card, the path and the keyword is a
|
|
43
|
-
// signal on the developer's own machine, which is the whole point.
|
|
44
|
-
|
|
45
|
-
import { toolNameForCardType } from './from-tool-call';
|
|
46
|
-
import { cardSchemas } from './index';
|
|
47
|
-
import type { CardSchema, CardSchemaName } from './index';
|
|
48
|
-
import {
|
|
49
|
-
ANTHROPIC_STRICT,
|
|
50
|
-
OPENAI_STRICT,
|
|
51
|
-
checkProviderSubset,
|
|
52
|
-
type ProviderSubset,
|
|
53
|
-
type StrictProviderId,
|
|
54
|
-
type SubsetViolation,
|
|
55
|
-
} from './provider-subsets';
|
|
56
|
-
|
|
57
|
-
// THE TOOL NAME IS NOT SPELLED HERE
|
|
58
|
-
// ---------------------------------
|
|
59
|
-
// `kai_confirm` is produced by `toolNameForCardType()` in ./from-tool-call, which
|
|
60
|
-
// also owns the inverse (`cardTypeFromToolName`). Both directions come off one
|
|
61
|
-
// string in one module on purpose. If this file wrote `'kai_' + type` instead, the
|
|
62
|
-
// two halves could drift, and the symptom would be a model dutifully calling
|
|
63
|
-
// `kai_confirm` while the loop's parser looks for something else and routes it to
|
|
64
|
-
// `runTool`, where it disappears with no error anywhere.
|
|
65
|
-
//
|
|
66
|
-
// A `toolPrefix` OVERRIDE IS NOT SHIPPED, AND THAT IS A DECISION.
|
|
67
|
-
// The plan reserves `createCardRegistry({ toolPrefix })` for Phase 2. It is
|
|
68
|
-
// deliberately NOT stubbed in here, because the inverse hard-codes the `kai_` prefix:
|
|
69
|
-
// a `toolPrefix` honoured on this side alone would generate tools the parser cannot
|
|
70
|
-
// recognise, which is a worse failure than not having the option. The seam is the
|
|
71
|
-
// `CardToolSource` interface below; the override lands in both directions in one
|
|
72
|
-
// change, or not at all.
|
|
73
|
-
|
|
74
|
-
// ---------------------------------------------------------------------------
|
|
75
|
-
// Provider envelope shapes, declared here rather than imported from an SDK
|
|
76
|
-
// ---------------------------------------------------------------------------
|
|
77
|
-
|
|
78
|
-
/** A projected schema. Deliberately loose: `type` may become `['string','null']`. */
|
|
79
|
-
export type ToolParameters = Readonly<Record<string, unknown>>;
|
|
80
|
-
|
|
81
|
-
/** `POST /v1/chat/completions` and `/v1/responses` tool entries. */
|
|
82
|
-
export interface OpenAIToolDef {
|
|
83
|
-
readonly type: 'function';
|
|
84
|
-
readonly function: {
|
|
85
|
-
readonly name: string;
|
|
86
|
-
readonly description: string;
|
|
87
|
-
readonly parameters: ToolParameters;
|
|
88
|
-
readonly strict?: true;
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** `POST /v1/messages` tool entries. Note `input_schema`, NOT `function.parameters`. */
|
|
93
|
-
export interface AnthropicToolDef {
|
|
94
|
-
readonly name: string;
|
|
95
|
-
readonly description: string;
|
|
96
|
-
readonly input_schema: ToolParameters;
|
|
97
|
-
readonly strict?: true;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* The bare form, for the Vercel AI SDK and anything else that wants the schema
|
|
102
|
-
* without a provider envelope: `tool({ inputSchema: jsonSchema(def.schema) })`.
|
|
103
|
-
*
|
|
104
|
-
* First-class, not a fallback. A developer on `ai@7` defines tools with Zod, and
|
|
105
|
-
* handing them an OpenAI-shaped envelope they have to unwrap makes our JSON Schema an
|
|
106
|
-
* awkward second source of truth instead of the one they use.
|
|
107
|
-
*/
|
|
108
|
-
export interface JsonSchemaToolDef {
|
|
109
|
-
readonly name: string;
|
|
110
|
-
readonly description: string;
|
|
111
|
-
readonly schema: ToolParameters;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export type ToolProvider = 'openai' | 'anthropic' | 'jsonschema';
|
|
115
|
-
|
|
116
|
-
export type ToolDefFor<P extends ToolProvider> = P extends 'openai'
|
|
117
|
-
? OpenAIToolDef
|
|
118
|
-
: P extends 'anthropic'
|
|
119
|
-
? AnthropicToolDef
|
|
120
|
-
: JsonSchemaToolDef;
|
|
121
|
-
|
|
122
|
-
export type ToolDef = OpenAIToolDef | AnthropicToolDef | JsonSchemaToolDef;
|
|
123
|
-
|
|
124
|
-
export interface CardToolOptions<P extends ToolProvider = ToolProvider> {
|
|
125
|
-
readonly provider: P;
|
|
126
|
-
/**
|
|
127
|
-
* Opt in to the provider's grammar-constrained mode.
|
|
128
|
-
*
|
|
129
|
-
* Default `false`, because that is the mode the conformance sweep proved cards in
|
|
130
|
-
* and because, today, `true` throws for every built-in card on both providers.
|
|
131
|
-
* When it throws it names the card, the path and the keyword.
|
|
132
|
-
*/
|
|
133
|
-
readonly strict?: boolean;
|
|
134
|
-
/**
|
|
135
|
-
* Narrow the DERIVED tool schema per card type, after projection.
|
|
136
|
-
*
|
|
137
|
-
* A card's authored schema is deliberately permissive — `files` is optional on an
|
|
138
|
-
* artifact, its items need only a `path` — because the card CONTRACT must accept
|
|
139
|
-
* what a host may legitimately render. A model filling the tool in, though, is
|
|
140
|
-
* often better served by a tighter tool: requiring `code` on every file, or at
|
|
141
|
-
* least one file at all, moves a class of failures from render time (a card the
|
|
142
|
-
* registry rejects) to generation time (the model literally cannot emit one).
|
|
143
|
-
*
|
|
144
|
-
* This narrows the WIRE projection only — the authored schema and
|
|
145
|
-
* `registry.validate()` are untouched, so a hand-built envelope still validates.
|
|
146
|
-
* Applied for every provider, `jsonschema` included, and merged loudly: an
|
|
147
|
-
* unknown path or a required name the node has no property for is a TypeError
|
|
148
|
-
* naming the card, the path and the field, because a tool that can never be
|
|
149
|
-
* satisfied must not ship quietly.
|
|
150
|
-
*
|
|
151
|
-
* IT CANNOT REACH A FORM CARD'S FIELDS, and that is a known limitation rather
|
|
152
|
-
* than an oversight. The form
|
|
153
|
-
* card's payload IS a JSON Schema, so `form.schema.json` describes `properties`
|
|
154
|
-
* as a map of field definitions and there is no node at `properties.ticketId`
|
|
155
|
-
* for a rule to land on — `require: { form: [{ path: 'properties.ticketId' }] }`
|
|
156
|
-
* raises the TypeError above, correctly. So an app cannot pin
|
|
157
|
-
* `x-kai-format: 'custom'` onto one field of a form the model authors. The
|
|
158
|
-
* enum on that hint is what makes a small model pick a valid token; an app that
|
|
159
|
-
* needs a guarantee checks the envelope when it arrives.
|
|
160
|
-
*/
|
|
161
|
-
readonly require?: Readonly<Record<string, readonly CardRequireRule[]>>;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* One narrowing instruction for one node of one card's projected schema.
|
|
166
|
-
*
|
|
167
|
-
* `path` is a dot-path from the envelope-data root through `properties` keys
|
|
168
|
-
* (`''` names the root node itself). On an ARRAY node, `required` narrows
|
|
169
|
-
* `items.required` and `minItems` lands on the array; on an object node,
|
|
170
|
-
* `required` lands on the node's own `required`. Existing requirements merge,
|
|
171
|
-
* never replace.
|
|
172
|
-
*/
|
|
173
|
-
export interface CardRequireRule {
|
|
174
|
-
/** Dot-path from the envelope-data root to a schema node, e.g. 'files'. */
|
|
175
|
-
readonly path: string;
|
|
176
|
-
/** Property names to require. On an ARRAY node this narrows `items.required`; on an object node, the node's own `required`. */
|
|
177
|
-
readonly required?: readonly string[];
|
|
178
|
-
/** Sets `minItems` (array nodes only). */
|
|
179
|
-
readonly minItems?: number;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Where the card types come from.
|
|
184
|
-
*
|
|
185
|
-
* Phase 1 hands this the built-in `cardSchemas` map directly. Phase 2's
|
|
186
|
-
* `createCardRegistry()` returns an object that is structurally a `CardToolSource`,
|
|
187
|
-
* so `cardTools(registry, opts)` starts working on custom card types without this
|
|
188
|
-
* signature changing. That is the seam, left open on purpose and not built yet.
|
|
189
|
-
*/
|
|
190
|
-
export interface CardToolSource {
|
|
191
|
-
readonly schemas: Readonly<Record<string, CardSchema>>;
|
|
192
|
-
/** Tool descriptions by card type. Falls back to the built-in copy, then the schema's own. */
|
|
193
|
-
readonly descriptions?: Readonly<Record<string, string>>;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export type CardToolInput = CardToolSource | Readonly<Record<string, CardSchema>>;
|
|
197
|
-
|
|
198
|
-
// ---------------------------------------------------------------------------
|
|
199
|
-
// Tool descriptions
|
|
200
|
-
// ---------------------------------------------------------------------------
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* What each card is FOR, addressed to a model.
|
|
204
|
-
*
|
|
205
|
-
* This is not a sixth copy of a shape. The shape stays in the JSON Schema and is
|
|
206
|
-
* never restated here; these are one sentence each of purpose, which exists nowhere
|
|
207
|
-
* else, and which a schema's own `description` ("Data payload for a `confirm` card")
|
|
208
|
-
* does not provide because it is written for a developer reading the contract.
|
|
209
|
-
*
|
|
210
|
-
* Typed as an exhaustive `Record<CardSchemaName, string>`, so adding an eighth card
|
|
211
|
-
* type fails `tsc` here rather than silently shipping a tool with no description.
|
|
212
|
-
*/
|
|
213
|
-
export const CARD_TOOL_DESCRIPTIONS: Readonly<Record<CardSchemaName, string>> = Object.freeze({
|
|
214
|
-
confirm:
|
|
215
|
-
'Show the user an approval card and wait for their decision before you act. Use this whenever you are about to do something consequential, destructive or irreversible.',
|
|
216
|
-
choice:
|
|
217
|
-
'Ask the user to pick exactly one option from a list of rich choices. Use this instead of asking in prose when the options are known and finite.',
|
|
218
|
-
tasks:
|
|
219
|
-
'Show the user a checklist they can tick, either to select several items for you to act on or to track progress through a sequence of steps.',
|
|
220
|
-
form: 'Ask the user for structured input by rendering a form. The data is itself a JSON Schema describing the fields to collect.',
|
|
221
|
-
link: 'Show a rich preview of a web page: title, description, image and domain. The card renders what you supply and never fetches the page itself.',
|
|
222
|
-
embed: 'Embed a video player (YouTube, Vimeo, or a direct player URL) inline in the conversation.',
|
|
223
|
-
artifact:
|
|
224
|
-
'Show something you built (a page, an app, a set of files) in a framed preview with its source alongside, so the user can look at both.',
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
// ---------------------------------------------------------------------------
|
|
228
|
-
// The error
|
|
229
|
-
// ---------------------------------------------------------------------------
|
|
230
|
-
|
|
231
|
-
export interface UnsupportedCardTool {
|
|
232
|
-
readonly cardType: string;
|
|
233
|
-
readonly toolName: string;
|
|
234
|
-
readonly violations: readonly SubsetViolation[];
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Thrown by `cardTools(..., { strict: true })` when a card cannot be expressed in the
|
|
239
|
-
* provider's strict subset.
|
|
240
|
-
*
|
|
241
|
-
* Carries the structured detail as well as the message, so a build script can render
|
|
242
|
-
* it however it likes rather than parsing prose.
|
|
243
|
-
*/
|
|
244
|
-
export class UnsupportedCardToolSchemaError extends Error {
|
|
245
|
-
override readonly name = 'UnsupportedCardToolSchemaError';
|
|
246
|
-
readonly provider: StrictProviderId;
|
|
247
|
-
readonly source: string;
|
|
248
|
-
readonly cards: readonly UnsupportedCardTool[];
|
|
249
|
-
|
|
250
|
-
constructor(subset: ProviderSubset, cards: readonly UnsupportedCardTool[]) {
|
|
251
|
-
super(formatUnsupported(subset, cards));
|
|
252
|
-
this.provider = subset.id;
|
|
253
|
-
this.source = subset.source;
|
|
254
|
-
this.cards = cards;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/** Every distinct keyword named across every failing card, deduped, in order. */
|
|
258
|
-
get keywords(): string[] {
|
|
259
|
-
const seen = new Set<string>();
|
|
260
|
-
for (const card of this.cards) for (const v of card.violations) seen.add(v.keyword);
|
|
261
|
-
return [...seen];
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function formatUnsupported(subset: ProviderSubset, cards: readonly UnsupportedCardTool[]): string {
|
|
266
|
-
const lines: string[] = [];
|
|
267
|
-
const plural = cards.length === 1 ? 'card type' : 'card types';
|
|
268
|
-
lines.push(`${cards.length} ${plural} cannot be a STRICT tool definition under ${subset.label}:`);
|
|
269
|
-
for (const card of cards) {
|
|
270
|
-
lines.push('');
|
|
271
|
-
lines.push(` ${card.toolName} (card type "${card.cardType}") — ${card.violations.length} problem(s):`);
|
|
272
|
-
for (const v of card.violations) {
|
|
273
|
-
lines.push(` ${v.path}: \`${v.keyword}\` — ${v.reason}`);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
lines.push('');
|
|
277
|
-
lines.push(` subset source: ${subset.source} (read ${subset.checkedOn})`);
|
|
278
|
-
lines.push(
|
|
279
|
-
' Fix: drop `strict: true`. Non-strict mode projects a provider-valid schema (root combinators are relaxed with the constraint restated in the description and still enforced by card validation). Rewriting a built-in card schema to fit a strict subset would change what a valid card IS, which is a card-contract change, not a projection setting.',
|
|
280
|
-
);
|
|
281
|
-
return lines.join('\n');
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// ---------------------------------------------------------------------------
|
|
285
|
-
// The projection
|
|
286
|
-
// ---------------------------------------------------------------------------
|
|
287
|
-
|
|
288
|
-
const isRecord = (v: unknown): v is Record<string, unknown> =>
|
|
289
|
-
typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Root combinators the two largest providers refuse on a tool schema's root node
|
|
293
|
-
* (measured: OpenAI and Anthropic 400 before the model runs — findings F-20;
|
|
294
|
-
* DeepSeek's OpenAI-compatible route requires the same workaround). The AUTHORED
|
|
295
|
-
* schemas keep them — card validation keeps every constraint. Only the WIRE
|
|
296
|
-
* projection relaxes, loudly: the constraint is restated in the description the
|
|
297
|
-
* model reads, a console.warn names the relaxation, and a violating envelope
|
|
298
|
-
* still fails registry.validate() at render time with a hard diagnostic.
|
|
299
|
-
*/
|
|
300
|
-
const ROOT_RELAXATIONS: Readonly<Record<string, string>> = {
|
|
301
|
-
artifact:
|
|
302
|
-
'Provide `src` (a preview URL) or `files` — at least one. An envelope with neither is rejected.',
|
|
303
|
-
embed:
|
|
304
|
-
"For provider 'generic', include `url`. For 'youtube'/'vimeo', include `id` or `url`.",
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
/** Strip a banned combinator key from the projected ROOT node only (nested
|
|
308
|
-
* combinators are accepted by every provider measured and stay). Returns the
|
|
309
|
-
* relaxation note to restate in the TOOL description the model reads. Two
|
|
310
|
-
* distinct no-note return paths: `undefined` when NOTHING was banned (the wire
|
|
311
|
-
* projection is faithful, nothing was widened), and GENERIC_NOTE when something
|
|
312
|
-
* WAS banned but no curated copy exists (a custom card) — that path still
|
|
313
|
-
* restates the relaxation, because deleting a combinator without telling the
|
|
314
|
-
* model would be a silent widening. */
|
|
315
|
-
const GENERIC_RELAXATION_NOTE =
|
|
316
|
-
'The root constraint of this schema was relaxed for this provider; consult the card registry for what a valid payload is.';
|
|
317
|
-
|
|
318
|
-
const BANNED_ROOT_KEYS = ['anyOf', 'allOf', 'oneOf', 'not', 'enum', 'const'] as const;
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* The object shape a literal `enum` / `const` VALUE implies.
|
|
322
|
-
*
|
|
323
|
-
* `const: { op: 'ping' }` is a whole-instance constraint, which a provider refuses
|
|
324
|
-
* at the root exactly like a combinator. The instance it admits is an object, so
|
|
325
|
-
* the shape survives the relaxation as `properties: { op: { const: 'ping' } }` with
|
|
326
|
-
* `op` required — nothing widened that the branch did not already allow.
|
|
327
|
-
*/
|
|
328
|
-
function branchFromLiteral(value: unknown): Record<string, unknown> | null {
|
|
329
|
-
if (!isRecord(value)) return null;
|
|
330
|
-
const properties: Record<string, unknown> = {};
|
|
331
|
-
for (const [key, member] of Object.entries(value)) properties[key] = { const: member };
|
|
332
|
-
return { type: 'object', properties, required: Object.keys(value) };
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/** The branch schemas one banned root keyword contributes, or `[]` for one that has none. */
|
|
336
|
-
function branchesOfRootKeyword(keyword: string, value: unknown): Record<string, unknown>[] {
|
|
337
|
-
if (keyword === 'anyOf' || keyword === 'oneOf' || keyword === 'allOf') {
|
|
338
|
-
return Array.isArray(value) ? value.filter(isRecord) : [];
|
|
339
|
-
}
|
|
340
|
-
if (keyword === 'const') return [branchFromLiteral(value)].filter((b): b is Record<string, unknown> => b !== null);
|
|
341
|
-
if (keyword === 'enum') {
|
|
342
|
-
return Array.isArray(value)
|
|
343
|
-
? value.map(branchFromLiteral).filter((b): b is Record<string, unknown> => b !== null)
|
|
344
|
-
: [];
|
|
345
|
-
}
|
|
346
|
-
// `not` is a NEGATIVE constraint: nothing it says is a property the instance may
|
|
347
|
-
// carry, so there is nothing sound to merge back. It is dropped and restated in
|
|
348
|
-
// the description like every other relaxation.
|
|
349
|
-
return [];
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Fold a root combinator's branches back into the object root that replaces it.
|
|
354
|
-
*
|
|
355
|
-
* The two built-ins showed the shape of the rule and this generalises it rather
|
|
356
|
-
* than special-casing them. `artifact`'s root `anyOf` is `[{required:['src']},
|
|
357
|
-
* {required:['files']}]`: a DISJUNCTION guarantees only what EVERY branch
|
|
358
|
-
* guarantees, so the intersection is empty and `required` stays absent — which is
|
|
359
|
-
* exactly what the artifact projection already did. `embed`'s root `allOf` is two
|
|
360
|
-
* `if`/`then` pairs: a CONJUNCTION guarantees every branch's own top-level
|
|
361
|
-
* `required`, and an `if`/`then` branch declares none, so again nothing is added.
|
|
362
|
-
* A consumer card whose branches carry real `properties` is the case neither
|
|
363
|
-
* built-in reaches, and the one this exists for.
|
|
364
|
-
*
|
|
365
|
-
* Properties are UNIONed (each branch's shape must remain expressible — dropping
|
|
366
|
-
* one is the silent narrowing the whole F-20 fix exists to avoid); a name already
|
|
367
|
-
* on the root keeps the root's definition. `required` is the intersection for
|
|
368
|
-
* `anyOf`/`oneOf`/`enum`/`const` and the union for `allOf`, unioned in turn with
|
|
369
|
-
* whatever the root already required, and filtered to names the merged
|
|
370
|
-
* `properties` actually carries so the tool can never be unsatisfiable.
|
|
371
|
-
*/
|
|
372
|
-
function mergeBranchesIntoRoot(parameters: Record<string, unknown>, banned: readonly string[]): void {
|
|
373
|
-
const properties: Record<string, unknown> = isRecord(parameters.properties)
|
|
374
|
-
? { ...parameters.properties }
|
|
375
|
-
: {};
|
|
376
|
-
const guaranteed = new Set<string>(
|
|
377
|
-
Array.isArray(parameters.required) ? parameters.required.map(String) : [],
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
for (const keyword of banned) {
|
|
381
|
-
const branches = branchesOfRootKeyword(keyword, parameters[keyword]);
|
|
382
|
-
if (branches.length === 0) continue;
|
|
383
|
-
|
|
384
|
-
for (const branch of branches) {
|
|
385
|
-
if (!isRecord(branch.properties)) continue;
|
|
386
|
-
for (const [name, sub] of Object.entries(branch.properties)) {
|
|
387
|
-
if (!(name in properties)) properties[name] = sub;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
const perBranch = branches.map(
|
|
392
|
-
(branch) => new Set(Array.isArray(branch.required) ? branch.required.map(String) : []),
|
|
393
|
-
);
|
|
394
|
-
if (keyword === 'allOf') {
|
|
395
|
-
for (const set of perBranch) for (const name of set) guaranteed.add(name);
|
|
396
|
-
} else {
|
|
397
|
-
// Disjunction: keep only what every branch requires.
|
|
398
|
-
const [first, ...rest] = perBranch;
|
|
399
|
-
for (const name of first) {
|
|
400
|
-
if (rest.every((set) => set.has(name))) guaranteed.add(name);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
if (Object.keys(properties).length > 0) parameters.properties = properties;
|
|
406
|
-
const required = [...guaranteed].filter((name) => name in properties);
|
|
407
|
-
if (required.length > 0) parameters.required = required;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Make the projected ROOT the object shape every provider demands.
|
|
412
|
-
*
|
|
413
|
-
* Deleting a root combinator is only half of F-20. A schema whose root was NOTHING
|
|
414
|
-
* BUT the combinator projects to `{"title":…,"description":…}` once it is gone —
|
|
415
|
-
* accepted by no provider (measured: OpenAI 400, "schema must be a JSON Schema of
|
|
416
|
-
* type \"object\"") and unfillable by any model. Every built-in declares
|
|
417
|
-
* `type: "object"` at its root, so nothing in the suite reached this until a
|
|
418
|
-
* consumer card did.
|
|
419
|
-
*
|
|
420
|
-
* A root that declares a NON-object type cannot be a tool schema at all, and
|
|
421
|
-
* asserting `type: "object"` over it would be a lie about the card. That refuses,
|
|
422
|
-
* naming the card.
|
|
423
|
-
*/
|
|
424
|
-
function ensureObjectRoot(parameters: Record<string, unknown>, cardType: string): void {
|
|
425
|
-
const declared = parameters.type;
|
|
426
|
-
if (declared !== undefined && declared !== 'object') {
|
|
427
|
-
throw new TypeError(
|
|
428
|
-
`cardTools: the '${cardType}' card schema declares root type '${String(declared)}', but a provider tool ` +
|
|
429
|
-
`schema root must be an object (OpenAI and Anthropic reject anything else with HTTP 400). ` +
|
|
430
|
-
`Wrap the payload in an object, or project this card with provider 'jsonschema'.`,
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
parameters.type = 'object';
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
function relaxRootCombinators(parameters: Record<string, unknown>, cardType: string): string | undefined {
|
|
437
|
-
const note = ROOT_RELAXATIONS[cardType];
|
|
438
|
-
const banned = BANNED_ROOT_KEYS.filter((k) => k in parameters);
|
|
439
|
-
// The object-root guarantee holds for EVERY provider projection, banned keys or
|
|
440
|
-
// not: it is what the provider requires, not what the relaxation broke.
|
|
441
|
-
ensureObjectRoot(parameters, cardType);
|
|
442
|
-
if (banned.length === 0) return undefined;
|
|
443
|
-
mergeBranchesIntoRoot(parameters, banned);
|
|
444
|
-
for (const k of banned) delete parameters[k];
|
|
445
|
-
// eslint-disable-next-line no-console
|
|
446
|
-
console.warn(
|
|
447
|
-
`[kai-card-tools] relaxed root ${banned.join('/')} on the projected '${toolNameForCardType(cardType)}' tool schema ` +
|
|
448
|
-
`for this provider (it would be refused with HTTP 400); the constraint now lives in the ` +
|
|
449
|
-
`description and is still enforced by card validation.`,
|
|
450
|
-
);
|
|
451
|
-
return note ?? GENERIC_RELAXATION_NOTE;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* Apply one card's `require` rules to its PROJECTED parameters.
|
|
456
|
-
*
|
|
457
|
-
* Runs after `project()` and `relaxRootCombinators()`, on the copy the projection
|
|
458
|
-
* built — never on the authored schema, which `registry.validate()` keeps reading.
|
|
459
|
-
* Merges into whatever `required` already exists rather than replacing it, so a
|
|
460
|
-
* rule can only ever NARROW what the model may emit, never widen it.
|
|
461
|
-
*/
|
|
462
|
-
function applyRequire(
|
|
463
|
-
parameters: Record<string, unknown>,
|
|
464
|
-
cardType: string,
|
|
465
|
-
rules: readonly CardRequireRule[],
|
|
466
|
-
): void {
|
|
467
|
-
for (const rule of rules) {
|
|
468
|
-
// Resolve the dot-path through `properties` keys. `''` names the root node;
|
|
469
|
-
// every other segment must name a property of the node reached so far.
|
|
470
|
-
let node: Record<string, unknown> = parameters;
|
|
471
|
-
if (rule.path !== '') {
|
|
472
|
-
for (const segment of rule.path.split('.')) {
|
|
473
|
-
const props = isRecord(node.properties) ? node.properties : undefined;
|
|
474
|
-
const next = props?.[segment];
|
|
475
|
-
if (!isRecord(next)) {
|
|
476
|
-
throw new TypeError(
|
|
477
|
-
`cardTools: require on '${cardType}': path '${rule.path}' does not resolve in the schema`,
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
node = next;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const isArrayNode = node.type === 'array' || (node.type === undefined && 'items' in node);
|
|
485
|
-
if (isArrayNode && rule.minItems !== undefined) node.minItems = rule.minItems;
|
|
486
|
-
if (!isArrayNode && rule.minItems !== undefined) {
|
|
487
|
-
throw new TypeError(
|
|
488
|
-
`cardTools: minItems at require path '${rule.path}' applies to array nodes only, ` +
|
|
489
|
-
`but '${rule.path}' in the '${cardType}' schema is not one`,
|
|
490
|
-
);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (rule.required !== undefined) {
|
|
494
|
-
// On an array node the requirement lives on the ITEMS (each element must carry
|
|
495
|
-
// the field), not on the array itself.
|
|
496
|
-
const target = isArrayNode && isRecord(node.items) ? node.items : node;
|
|
497
|
-
const props = isRecord(target.properties) ? target.properties : undefined;
|
|
498
|
-
for (const field of rule.required) {
|
|
499
|
-
if (!props || !(field in props)) {
|
|
500
|
-
throw new TypeError(
|
|
501
|
-
`cardTools: require on '${cardType}' at path '${rule.path}': '${field}' is not a property of that node — ` +
|
|
502
|
-
`a required name with no property would make the tool unsatisfiable`,
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
const existing = Array.isArray(target.required) ? target.required.map(String) : [];
|
|
507
|
-
target.required = [...new Set([...existing, ...rule.required])];
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* Keywords stripped from every projection, strict or not.
|
|
514
|
-
*
|
|
515
|
-
* `$schema` and `$id` are meta: they address the document, they do not describe the
|
|
516
|
-
* instance, and neither provider's subset lists them. `x-kai-*` are our own UI hints
|
|
517
|
-
* (`x-kai-control: "tone"` tells a renderer to draw a tone picker); a model has no
|
|
518
|
-
* use for them, they cost tokens in every request, and under strict they are an
|
|
519
|
-
* unlisted keyword. Stripping them is not a downgrade of the contract, because they
|
|
520
|
-
* never constrained an instance in the first place.
|
|
521
|
-
*/
|
|
522
|
-
function isStrippedKeyword(key: string): boolean {
|
|
523
|
-
return key === '$schema' || key === '$id' || key.startsWith('x-');
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
const SUBSCHEMA_KEYS = ['items', 'if', 'then', 'else', 'not', 'contains', 'propertyNames', 'unevaluatedItems', 'additionalItems'];
|
|
527
|
-
const SUBSCHEMA_MAP_KEYS = ['properties', 'patternProperties', '$defs', 'definitions', 'dependentSchemas'];
|
|
528
|
-
const SUBSCHEMA_LIST_KEYS = ['allOf', 'anyOf', 'oneOf', 'prefixItems'];
|
|
529
|
-
|
|
530
|
-
interface ProjectOptions {
|
|
531
|
-
/** Force `additionalProperties: false` on every object. Both strict subsets demand it. */
|
|
532
|
-
readonly closeObjects: boolean;
|
|
533
|
-
/** Put every property in `required`, widening the newly-required ones with `null`. OpenAI only. */
|
|
534
|
-
readonly requireEveryProperty: boolean;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Rewrite a schema for the wire.
|
|
539
|
-
*
|
|
540
|
-
* Only ever REMOVES metadata or ADDS what a provider mandates. It never drops a
|
|
541
|
-
* constraint, which is the invariant that keeps a projected tool definition and the
|
|
542
|
-
* card's own validator describing the same set of valid cards.
|
|
543
|
-
*/
|
|
544
|
-
function project(node: unknown, opts: ProjectOptions): unknown {
|
|
545
|
-
if (!isRecord(node)) return node;
|
|
546
|
-
|
|
547
|
-
const out: Record<string, unknown> = {};
|
|
548
|
-
for (const [key, value] of Object.entries(node)) {
|
|
549
|
-
if (isStrippedKeyword(key)) continue;
|
|
550
|
-
out[key] = value;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// Recurse. `properties` KEYS are arbitrary names, not keywords: form.schema.json
|
|
554
|
-
// has properties literally called `properties` and `x-kai-order`, and stripping
|
|
555
|
-
// those would delete real fields rather than metadata.
|
|
556
|
-
for (const key of SUBSCHEMA_MAP_KEYS) {
|
|
557
|
-
const map = out[key];
|
|
558
|
-
if (!isRecord(map)) continue;
|
|
559
|
-
const next: Record<string, unknown> = {};
|
|
560
|
-
for (const [name, child] of Object.entries(map)) next[name] = project(child, opts);
|
|
561
|
-
out[key] = next;
|
|
562
|
-
}
|
|
563
|
-
for (const key of SUBSCHEMA_KEYS) {
|
|
564
|
-
if (!(key in out) || typeof out[key] === 'boolean') continue;
|
|
565
|
-
out[key] = project(out[key], opts);
|
|
566
|
-
}
|
|
567
|
-
if (isRecord(out.additionalProperties)) out.additionalProperties = project(out.additionalProperties, opts);
|
|
568
|
-
for (const key of SUBSCHEMA_LIST_KEYS) {
|
|
569
|
-
const list = out[key];
|
|
570
|
-
if (!Array.isArray(list)) continue;
|
|
571
|
-
out[key] = list.map((child) => project(child, opts));
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
const props = isRecord(out.properties) ? out.properties : undefined;
|
|
575
|
-
const looksLikeObject = out.type === 'object' || props !== undefined;
|
|
576
|
-
|
|
577
|
-
if (looksLikeObject && opts.closeObjects && !('additionalProperties' in out)) {
|
|
578
|
-
out.additionalProperties = false;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
if (looksLikeObject && opts.requireEveryProperty && props) {
|
|
582
|
-
const originallyRequired = Array.isArray(out.required) ? out.required.map(String) : [];
|
|
583
|
-
const all = Object.keys(props);
|
|
584
|
-
for (const name of all) {
|
|
585
|
-
if (originallyRequired.includes(name)) continue;
|
|
586
|
-
props[name] = widenWithNull(props[name]);
|
|
587
|
-
}
|
|
588
|
-
out.required = all;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
return out;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/**
|
|
595
|
-
* OpenAI's documented emulation of an optional field: keep it in `required` and let
|
|
596
|
-
* its type admit `null`.
|
|
597
|
-
*
|
|
598
|
-
* The `enum` half matters. `{ type: ['string','null'], enum: ['a','b'] }` admits
|
|
599
|
-
* nothing new, because the enum still excludes null, so the field would be required
|
|
600
|
-
* AND unsatisfiable-as-absent. `null` therefore joins the enum too.
|
|
601
|
-
*
|
|
602
|
-
* A subschema with nothing to widen (no `type`, no `anyOf`) is returned untouched.
|
|
603
|
-
* That is not a silent pass: `checkProviderSubset` reports it as an untyped
|
|
604
|
-
* subschema, which is the real problem and a truer message than "could not widen".
|
|
605
|
-
*/
|
|
606
|
-
function widenWithNull(sub: unknown): unknown {
|
|
607
|
-
if (!isRecord(sub)) return sub;
|
|
608
|
-
const out: Record<string, unknown> = { ...sub };
|
|
609
|
-
|
|
610
|
-
if ('type' in out) {
|
|
611
|
-
const declared = Array.isArray(out.type) ? out.type.map(String) : [String(out.type)];
|
|
612
|
-
if (!declared.includes('null')) out.type = [...declared, 'null'];
|
|
613
|
-
} else if (Array.isArray(out.anyOf)) {
|
|
614
|
-
if (!out.anyOf.some((b) => isRecord(b) && b.type === 'null')) out.anyOf = [...out.anyOf, { type: 'null' }];
|
|
615
|
-
} else {
|
|
616
|
-
return out;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
if (Array.isArray(out.enum) && !out.enum.includes(null)) out.enum = [...out.enum, null];
|
|
620
|
-
return out;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
// ---------------------------------------------------------------------------
|
|
624
|
-
// cardTools
|
|
625
|
-
// ---------------------------------------------------------------------------
|
|
626
|
-
|
|
627
|
-
function normalizeSource(input: CardToolInput): {
|
|
628
|
-
schemas: Readonly<Record<string, CardSchema>>;
|
|
629
|
-
descriptions: Readonly<Record<string, string>>;
|
|
630
|
-
} {
|
|
631
|
-
// A `CardToolSource` is recognised by carrying a `schemas` map. A bare
|
|
632
|
-
// `Record<type, CardSchema>` cannot collide with that unless someone names a card
|
|
633
|
-
// type "schemas" AND its schema document is itself a map of schema documents,
|
|
634
|
-
// which is not a shape the card contract can produce.
|
|
635
|
-
const maybe = input as Partial<CardToolSource>;
|
|
636
|
-
if (isRecord(maybe.schemas)) {
|
|
637
|
-
return { schemas: maybe.schemas, descriptions: maybe.descriptions ?? {} };
|
|
638
|
-
}
|
|
639
|
-
return { schemas: input as Readonly<Record<string, CardSchema>>, descriptions: {} };
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
function describe(cardType: string, schema: CardSchema, overrides: Readonly<Record<string, string>>): string {
|
|
643
|
-
const custom = overrides[cardType];
|
|
644
|
-
if (typeof custom === 'string' && custom.length > 0) return custom;
|
|
645
|
-
const builtIn = (CARD_TOOL_DESCRIPTIONS as Readonly<Record<string, string>>)[cardType];
|
|
646
|
-
if (typeof builtIn === 'string' && builtIn.length > 0) return builtIn;
|
|
647
|
-
// Last resort for a custom card type registered without a description. The schema's
|
|
648
|
-
// own prose is worse guidance than a purpose sentence, but it beats an empty string,
|
|
649
|
-
// and Phase 2's registry warns when a type arrives with neither.
|
|
650
|
-
const own = schema.description;
|
|
651
|
-
return typeof own === 'string' && own.length > 0 ? own : `Render a \`${cardType}\` card.`;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/**
|
|
655
|
-
* Project the card schemas into tool definitions for one provider.
|
|
656
|
-
*
|
|
657
|
-
* ```ts
|
|
658
|
-
* const tools = [...myTools, ...cardTools({ provider: 'openai' })];
|
|
659
|
-
* const tools = cardTools(cards, { provider: 'anthropic' });
|
|
660
|
-
* const tools = cardTools({ provider: 'jsonschema' }).map((t) =>
|
|
661
|
-
* tool({ description: t.description, inputSchema: jsonSchema(t.schema) }));
|
|
662
|
-
* ```
|
|
663
|
-
*
|
|
664
|
-
* @throws {UnsupportedCardToolSchemaError} with `strict: true`, naming every card,
|
|
665
|
-
* path and keyword the provider cannot express. See the file header: today that is
|
|
666
|
-
* every built-in card on both providers.
|
|
667
|
-
*/
|
|
668
|
-
export function cardTools<P extends ToolProvider>(opts: CardToolOptions<P>): ToolDefFor<P>[];
|
|
669
|
-
export function cardTools<P extends ToolProvider>(registry: CardToolInput, opts: CardToolOptions<P>): ToolDefFor<P>[];
|
|
670
|
-
export function cardTools(a: CardToolInput | CardToolOptions, b?: CardToolOptions): ToolDef[] {
|
|
671
|
-
// Single-argument form: `cardTools({ provider: 'openai' })` defaults the registry
|
|
672
|
-
// to the built-ins, which is the shape the developer-facing docs lead with.
|
|
673
|
-
const optionsOnly = b === undefined;
|
|
674
|
-
const opts = (optionsOnly ? (a as CardToolOptions) : (b as CardToolOptions));
|
|
675
|
-
if (!isRecord(opts) || typeof opts.provider !== 'string') {
|
|
676
|
-
throw new TypeError('cardTools: an options object with a `provider` is required, e.g. cardTools({ provider: "openai" })');
|
|
677
|
-
}
|
|
678
|
-
const { schemas, descriptions } = normalizeSource(
|
|
679
|
-
optionsOnly ? (builtInSchemas() as CardToolInput) : (a as CardToolInput),
|
|
680
|
-
);
|
|
681
|
-
const strict = opts.strict === true;
|
|
682
|
-
|
|
683
|
-
if (opts.provider === 'jsonschema' && strict) {
|
|
684
|
-
throw new TypeError(
|
|
685
|
-
'cardTools: `strict: true` is provider-specific and cannot be checked for `provider: "jsonschema"`, ' +
|
|
686
|
-
'because the two documented strict subsets differ (see @kitn.ai/ui/schemas provider-subsets). ' +
|
|
687
|
-
'Pass provider "openai" or "anthropic" to project and check strictly, or drop `strict`.',
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
const subset: ProviderSubset | null = strict
|
|
692
|
-
? opts.provider === 'openai'
|
|
693
|
-
? OPENAI_STRICT
|
|
694
|
-
: ANTHROPIC_STRICT
|
|
695
|
-
: null;
|
|
696
|
-
|
|
697
|
-
const projectOptions: ProjectOptions = {
|
|
698
|
-
closeObjects: subset !== null,
|
|
699
|
-
requireEveryProperty: subset !== null && subset.requireAllPropertiesRequired,
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
const defs: ToolDef[] = [];
|
|
703
|
-
const failures: UnsupportedCardTool[] = [];
|
|
704
|
-
|
|
705
|
-
// A `require` key naming a card the call does not carry is a typo, and a typo here
|
|
706
|
-
// would otherwise narrow NOTHING while looking like it narrowed something — the
|
|
707
|
-
// same decide-loudly class as an unknown dot-path below.
|
|
708
|
-
if (opts.require !== undefined) {
|
|
709
|
-
for (const key of Object.keys(opts.require)) {
|
|
710
|
-
if (!(key in schemas)) {
|
|
711
|
-
throw new TypeError(
|
|
712
|
-
`cardTools: require names card type '${key}', which this call does not carry ` +
|
|
713
|
-
`(carries: ${Object.keys(schemas).join(', ') || 'none'})`,
|
|
714
|
-
);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
for (const [cardType, schema] of Object.entries(schemas)) {
|
|
720
|
-
const name = toolNameForCardType(cardType);
|
|
721
|
-
const description = describe(cardType, schema, descriptions);
|
|
722
|
-
const parameters = project(schema, projectOptions) as ToolParameters;
|
|
723
|
-
|
|
724
|
-
// F-20: non-strict only. Strict mode already refuses these cards with the full
|
|
725
|
-
// subset check, so the relaxation must never mask it. The note is restated in the
|
|
726
|
-
// TOOL description — the envelope field the model reads when choosing the tool.
|
|
727
|
-
const relaxedNote =
|
|
728
|
-
subset === null && opts.provider !== 'jsonschema'
|
|
729
|
-
? relaxRootCombinators(parameters as Record<string, unknown>, cardType)
|
|
730
|
-
: undefined;
|
|
731
|
-
const wireDescription = relaxedNote ? `${description} ${relaxedNote}` : description;
|
|
732
|
-
|
|
733
|
-
// F-23: every provider, jsonschema included — a consumer handing the bare schema
|
|
734
|
-
// to the Vercel AI SDK narrows their tool the same way. Runs BEFORE the strict
|
|
735
|
-
// subset check so that check judges the schema that actually ships.
|
|
736
|
-
const requireRules = opts.require?.[cardType];
|
|
737
|
-
if (requireRules !== undefined) applyRequire(parameters as Record<string, unknown>, cardType, requireRules);
|
|
738
|
-
|
|
739
|
-
if (subset) {
|
|
740
|
-
const violations = checkProviderSubset(parameters, subset);
|
|
741
|
-
if (violations.length > 0) {
|
|
742
|
-
failures.push({ cardType, toolName: name, violations });
|
|
743
|
-
continue;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
if (opts.provider === 'openai') {
|
|
748
|
-
defs.push({ type: 'function', function: { name, description: wireDescription, parameters, ...(strict ? { strict: true as const } : {}) } });
|
|
749
|
-
} else if (opts.provider === 'anthropic') {
|
|
750
|
-
defs.push({ name, description: wireDescription, input_schema: parameters, ...(strict ? { strict: true as const } : {}) });
|
|
751
|
-
} else {
|
|
752
|
-
defs.push({ name, description, schema: parameters });
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
if (subset && failures.length > 0) throw new UnsupportedCardToolSchemaError(subset, failures);
|
|
757
|
-
return defs;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
/**
|
|
761
|
-
* The built-in map, read at CALL time.
|
|
762
|
-
*
|
|
763
|
-
* `./index` re-exports this module, and this module imports `cardSchemas` back out
|
|
764
|
-
* of it, which is a genuine ESM import cycle. It is safe because the value is only
|
|
765
|
-
* ever read inside a function body: by the time anyone calls `cardTools()`, both
|
|
766
|
-
* module bodies have finished evaluating and the live binding is populated. Reading
|
|
767
|
-
* it at TOP level here would be a temporal-dead-zone crash, so do not hoist it into
|
|
768
|
-
* a `const`.
|
|
769
|
-
*/
|
|
770
|
-
function builtInSchemas(): Readonly<Record<string, CardSchema>> {
|
|
771
|
-
return cardSchemas;
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
/** `cardTools(registry, { provider: 'openai' })`, spelled out. */
|
|
775
|
-
export function toOpenAITools(registry?: CardToolInput, opts?: { strict?: boolean }): OpenAIToolDef[] {
|
|
776
|
-
return registry === undefined
|
|
777
|
-
? cardTools({ provider: 'openai', strict: opts?.strict })
|
|
778
|
-
: cardTools(registry, { provider: 'openai', strict: opts?.strict });
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
/** `cardTools(registry, { provider: 'anthropic' })`, spelled out. */
|
|
782
|
-
export function toAnthropicTools(registry?: CardToolInput, opts?: { strict?: boolean }): AnthropicToolDef[] {
|
|
783
|
-
return registry === undefined
|
|
784
|
-
? cardTools({ provider: 'anthropic', strict: opts?.strict })
|
|
785
|
-
: cardTools(registry, { provider: 'anthropic', strict: opts?.strict });
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
/** `cardTools(registry, { provider: 'jsonschema' })`, spelled out. */
|
|
789
|
-
export function toJsonSchemaTools(registry?: CardToolInput): JsonSchemaToolDef[] {
|
|
790
|
-
return registry === undefined
|
|
791
|
-
? cardTools({ provider: 'jsonschema' })
|
|
792
|
-
: cardTools(registry, { provider: 'jsonschema' });
|
|
793
|
-
}
|