@kitn.ai/ui 0.25.1 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -7
- package/dist/components/attachment-types.d.ts +46 -1
- package/dist/components/attachments.d.ts +31 -0
- package/dist/components/audio-visualizer/fit-scale.d.ts +50 -0
- package/dist/components/audio-visualizer/index.d.ts +15 -1
- package/dist/components/audio-visualizer/variant-bar.d.ts +24 -1
- package/dist/components/chat-thread.d.ts +3 -41
- package/dist/components/code-block.d.ts +22 -0
- package/dist/components/conversation-item.d.ts +54 -1
- package/dist/components/conversation-list.d.ts +65 -0
- package/dist/components/form.d.ts +42 -0
- package/dist/components/message.d.ts +7 -0
- package/dist/components/toast.d.ts +2 -1
- package/dist/components/voice-input.d.ts +8 -0
- package/dist/components/voice-output.d.ts +12 -1
- package/dist/components/workspace-shell.d.ts +87 -0
- package/dist/{create-tween-B5Y4Im8G.js → create-tween-BFT6c-Y8.js} +1 -1
- package/dist/{create-tween-4z1XYLkR.js → create-tween-CJNlzoaO.js} +1 -1
- package/dist/{create-tween-CUzOlaMS.js → create-tween-CQED4T0g.js} +1 -1
- package/dist/custom-elements.json +2931 -2572
- package/dist/diagnostics/hook.d.ts +81 -0
- package/dist/diagnostics/index.d.ts +7 -0
- package/dist/diagnostics/report-request.d.ts +61 -0
- package/dist/diagnostics.d.ts +5 -0
- package/dist/diagnostics.js +209 -0
- package/dist/elements/agent-card.js +1 -1
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/audio-visualizer.js +1 -1
- package/dist/elements/autoloader.js +1 -1
- package/dist/elements/avatar.js +1 -1
- package/dist/elements/badge.js +1 -1
- package/dist/elements/button.js +1 -1
- package/dist/elements/card.js +1 -1
- package/dist/elements/cards.js +1 -1
- package/dist/elements/chain-of-thought.js +1 -1
- package/dist/elements/chat-scope-picker.js +1 -1
- package/dist/elements/chat-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/chunks/{Icon-b5A8hlHs.js → Icon--Y8vsFal.js} +1 -1
- package/dist/elements/chunks/action-icons-D9u3OzYW.js +1 -0
- package/dist/elements/chunks/arrow-left-CVh1TeN7.js +1 -0
- package/dist/elements/chunks/{artifact-BSuPYFHY.js → artifact-BkYcfIlZ.js} +1 -1
- package/dist/elements/chunks/attachments-DwfNlubq.js +1 -0
- package/dist/elements/chunks/audio-visualizer-D7lB6mvR.js +1 -0
- package/dist/elements/chunks/{badge-TIMiJFKc.js → badge-Q0-Cmwzz.js} +1 -1
- package/dist/elements/chunks/{button-B9okqG73.js → button-flUbeufF.js} +1 -1
- package/dist/elements/chunks/{card-renderer-CSoTL_e1.js → card-renderer-3Dyl-FpN.js} +1 -1
- package/dist/elements/chunks/check-B5kBPKeo.js +1 -0
- package/dist/elements/chunks/chevron-down-DZ1GS39h.js +1 -0
- package/dist/elements/chunks/chevron-right-CvP7OUd2.js +1 -0
- package/dist/elements/chunks/{choice-card-CGMc3kek.js → choice-card-D4QUIOWZ.js} +1 -1
- package/dist/elements/chunks/circle-CDf9g6MQ.js +1 -0
- package/dist/elements/chunks/circle-check-CA47Nqbz.js +1 -0
- package/dist/elements/chunks/{circle-x-mhH7pOK2.js → circle-x-CJ_HVbC8.js} +1 -1
- package/dist/elements/chunks/code-block-BdLiTitd.js +1 -0
- package/dist/elements/chunks/collapsible-BzbXpPzr.js +1 -0
- package/dist/elements/chunks/{composer-CZZmtYOT.js → composer-Byve-n8K.js} +2 -2
- package/dist/elements/chunks/{confirm-card-BWjf2-xo.js → confirm-card-B0flE3MN.js} +1 -1
- package/dist/elements/chunks/context-VLIL3sJ-.js +1 -0
- package/dist/elements/chunks/controllable-DTwTIlwp.js +1 -0
- package/dist/elements/chunks/conversation-item-BDztezOG.js +1 -0
- package/dist/elements/chunks/copy-Nf-kqxXH.js +1 -0
- package/dist/elements/chunks/{create-tween-CUrM6D_o.js → create-tween-c4Z8TDYz.js} +2 -2
- package/dist/elements/chunks/default-input-C2iLFq0b.js +1 -0
- package/dist/elements/chunks/define-wjHR1MkU.js +1 -0
- package/dist/elements/chunks/disclosure-CcNbyLy0.js +1 -0
- package/dist/elements/chunks/{download-DnCyWFJE.js → download-C4V7tBdb.js} +1 -1
- package/dist/elements/chunks/dropdown-BgWTthUx.js +1 -0
- package/dist/elements/chunks/{ellipsis-CVxqfsDZ.js → ellipsis-7gU33mL2.js} +1 -1
- package/dist/elements/chunks/embed-D3KewsIJ.js +1 -0
- package/dist/elements/chunks/{external-link-DG2sRdQL.js → external-link-CSOEsNlx.js} +1 -1
- package/dist/elements/chunks/{file-text-D0J9F6M7.js → file-text-D1G8khle.js} +1 -1
- package/dist/elements/chunks/{file-tree-yGWNK0Js.js → file-tree-zvB2i7P9.js} +1 -1
- package/dist/elements/chunks/{folder-NV1pi_ud.js → folder-WW2LGLid.js} +1 -1
- package/dist/elements/chunks/form-CQirdC4W.js +1 -0
- package/dist/elements/chunks/hover-card-CJJkfHTK.js +1 -0
- package/dist/elements/chunks/icon-Cfgmf4fa.js +1 -0
- package/dist/elements/chunks/{info-C8r5Q6cc.js → info-DX3YMOt6.js} +1 -1
- package/dist/elements/chunks/input-BtW9U6Sq.js +1 -0
- package/dist/elements/chunks/{kbd-OGJDUT-O.js → kbd-LGXwuT_V.js} +1 -1
- package/dist/elements/chunks/{link-BdsfLB7b.js → link-2My69BqT.js} +1 -1
- package/dist/elements/chunks/{link-preview-Cf5KOP-i.js → link-preview-OQ9IEq0f.js} +1 -1
- package/dist/elements/chunks/{loader-B5QzK_8_.js → loader-iOxc_Tu-.js} +1 -1
- package/dist/elements/chunks/{markdown-DbozyYqa.js → markdown-DgBFaGAz.js} +1 -1
- package/dist/elements/chunks/{message-DtXKPqiR.js → message-VMZohrXx.js} +1 -1
- package/dist/elements/chunks/message-circle-CpXMM-_n.js +1 -0
- package/dist/elements/chunks/message-square-DxcWtETQ.js +1 -0
- package/dist/elements/chunks/message-tZmV2A3p.js +1 -0
- package/dist/elements/chunks/{minimize-2-SegJ_oku.js → minimize-2-DPIUKTrT.js} +1 -1
- package/dist/elements/chunks/{model-switcher-D7JrFFHp.js → model-switcher-DsT8tFB0.js} +1 -1
- package/dist/elements/chunks/{overlay-CV7z2YuX.js → overlay-DuiwdXjm.js} +1 -1
- package/dist/elements/chunks/{paperclip-Bg0ITU3b.js → paperclip-CRZVp4z_.js} +1 -1
- package/dist/elements/chunks/{progress-bar-D1_rzt6N.js → progress-bar-DNy66e_E.js} +1 -1
- package/dist/elements/chunks/{prompt-suggestion-D5Sm8xF1.js → prompt-suggestion-Bn64Nl7L.js} +1 -1
- package/dist/elements/chunks/reasoning-Emn9N4-T.js +1 -0
- package/dist/elements/chunks/{resizable-Dpzjlvp4.js → resizable-D181b4fM.js} +1 -1
- package/dist/elements/chunks/{rotate-cw-DzVDJOA1.js → rotate-cw-CPWe37D9.js} +1 -1
- package/dist/elements/chunks/{scroll-area-93A1bbeG.js → scroll-area-ClZkZxIk.js} +1 -1
- package/dist/elements/chunks/scroll-button-DPE-t3x_.js +1 -0
- package/dist/elements/chunks/{separator-DH2r_ZSZ.js → separator-CeJHQqLW.js} +1 -1
- package/dist/elements/chunks/{settings-BFMOd6yB.js → settings-CqaHgJF9.js} +1 -1
- package/dist/elements/chunks/{settings-group-B3VZKIcz.js → settings-group-CjqjLCOn.js} +1 -1
- package/dist/elements/chunks/share-CyfnNEqp.js +1 -0
- package/dist/elements/chunks/{skeleton-N8BuWYmf.js → skeleton-D8akBnKS.js} +1 -1
- package/dist/elements/chunks/slots-DaNjX0rc.js +1 -0
- package/dist/elements/chunks/{source-w9V0NnBn.js → source-UWCvmVak.js} +1 -1
- package/dist/elements/chunks/{star-DHvACxtm.js → star-DoHFAikQ.js} +1 -1
- package/dist/elements/chunks/store-Cjq0qwYN.js +1 -0
- package/dist/elements/chunks/{tasks-card-lPxoodlA.js → tasks-card-CmZ5xh6v.js} +1 -1
- package/dist/elements/chunks/{text-shimmer-BY_rI3s5.js → text-shimmer-DYn09zrz.js} +1 -1
- package/dist/elements/chunks/textarea-skiGHURM.js +1 -0
- package/dist/elements/chunks/{thumbs-up-DQe0hvkZ.js → thumbs-up-DX2Ijk3H.js} +1 -1
- package/dist/elements/chunks/{toast-store-VUZZBhzH.js → toast-store-CFnQpThX.js} +1 -1
- package/dist/elements/chunks/{tool-C1YtxwPu.js → tool-DKQ2D1DP.js} +1 -1
- package/dist/elements/chunks/{tooltip-BqwnFWhn.js → tooltip-B0G99y8w.js} +1 -1
- package/dist/elements/chunks/{triangle-alert-B6or7F5u.js → triangle-alert-F9cyUEyy.js} +1 -1
- package/dist/elements/chunks/{use-card-resolution-DJg32jGH.js → use-card-resolution-DoA2f_EU.js} +1 -1
- package/dist/elements/chunks/{variant-aurora-QXTZPyST.js → variant-aurora-qt6Zct8M.js} +2 -2
- package/dist/elements/chunks/variant-custom-tJp22_BQ.js +1 -0
- package/dist/{variant-wave-BwzrHD1s.js → elements/chunks/variant-wave-CaQ5ti39.js} +2 -2
- package/dist/elements/chunks/x-B6UnOiLw.js +1 -0
- package/dist/elements/coachmark.js +1 -1
- package/dist/elements/code-block.js +1 -1
- package/dist/elements/command.js +1 -1
- package/dist/elements/compare.js +1 -1
- package/dist/elements/composer.js +1 -1
- package/dist/elements/confirm-card.js +1 -1
- package/dist/elements/context-meter.js +1 -1
- package/dist/elements/conversation-item.d.ts +1 -0
- package/dist/elements/conversation-item.js +1 -0
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/default-input.d.ts +3 -3
- package/dist/elements/define.d.ts +27 -0
- package/dist/elements/diagnostic-events.d.ts +74 -0
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/dock.d.ts +1 -0
- package/dist/elements/dock.js +149 -0
- package/dist/elements/dropdown.d.ts +1 -0
- package/dist/elements/dropdown.js +1 -0
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/element-diagnostics.d.ts +81 -0
- package/dist/elements/embed.js +1 -1
- package/dist/elements/empty.js +1 -1
- package/dist/elements/feedback-bar.js +1 -1
- package/dist/elements/file-tree.js +1 -1
- package/dist/elements/file-upload.js +1 -1
- package/dist/elements/form.js +1 -1
- package/dist/elements/hover-card.js +1 -1
- package/dist/elements/icon.js +1 -1
- package/dist/elements/image.js +1 -1
- package/dist/elements/input.js +1 -1
- package/dist/elements/kbd.js +1 -1
- package/dist/elements/link-preview.js +1 -1
- package/dist/elements/loader.js +1 -1
- package/dist/elements/markdown.js +1 -1
- package/dist/elements/menu.js +1 -1
- package/dist/elements/message-skills.js +1 -1
- package/dist/elements/message.js +1 -1
- package/dist/elements/model-switcher.js +1 -1
- package/dist/elements/nav.js +1 -1
- package/dist/elements/notice.js +1 -1
- package/dist/elements/pane-group.js +1 -1
- package/dist/elements/pane.js +1 -1
- package/dist/elements/popover.js +1 -1
- package/dist/elements/progress-bar.js +1 -1
- package/dist/elements/prompt-dock.js +1 -1
- package/dist/elements/prompt-input.js +1 -1
- package/dist/elements/prompt-suggestions.js +1 -1
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/register.d.ts +2 -0
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +3 -3
- package/dist/elements/screen.js +1 -1
- package/dist/elements/scroll-area.js +1 -1
- package/dist/elements/scroll-button.js +1 -1
- package/dist/elements/search.js +1 -1
- package/dist/elements/segmented.js +1 -1
- package/dist/elements/separator.js +1 -1
- package/dist/elements/setting-item.js +1 -1
- package/dist/elements/settings-group.js +1 -1
- package/dist/elements/skeleton.js +1 -1
- package/dist/elements/slots.d.ts +35 -3
- package/dist/elements/source.js +1 -1
- package/dist/elements/status.js +1 -1
- package/dist/elements/switch.js +1 -1
- package/dist/elements/tabs.js +1 -1
- package/dist/elements/tasks.js +1 -1
- package/dist/elements/text-shimmer.js +1 -1
- package/dist/elements/thinking-bar.js +1 -1
- package/dist/elements/thread.js +1 -1
- package/dist/elements/toast.js +1 -1
- package/dist/elements/tool.js +1 -1
- package/dist/elements/tooltip.js +1 -1
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.js +1 -1
- package/dist/elements.d.ts +232 -148
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5456 -4931
- package/dist/index.server.js +4142 -3669
- package/dist/kai-provider.es.js +40 -40
- package/dist/kai.es.js +1 -1
- package/dist/llms/llms-full.txt +184 -69
- package/dist/llms/llms.txt +3 -3
- package/dist/mcp.es.js +2669 -546
- package/dist/primitives/use-resize-observer.d.ts +23 -0
- package/dist/primitives/use-speech-recognition.d.ts +8 -0
- package/dist/primitives/use-text-stream.d.ts +5 -0
- package/dist/react/index.d.ts +115 -266
- package/dist/react.js +247 -221
- package/dist/register-impl-DpE7icIb.js +293 -0
- package/dist/schemas/index.d.ts +1 -1
- package/dist/schemas/tool-defs.d.ts +35 -0
- package/dist/schemas.js +403 -310
- package/dist/{solid-BMuDmFo_.js → solid-BtdqNZI7.js} +8435 -7394
- package/dist/{solid-CGYNdLRR.js → solid-CnavwBye.js} +5987 -5035
- package/dist/solid.d.ts +5 -0
- package/dist/solid.js +210 -203
- package/dist/solid.server.js +210 -203
- package/dist/state/index.d.ts +4 -0
- package/dist/state/persistence.d.ts +51 -0
- package/dist/state/stream.d.ts +50 -0
- package/dist/state/threads.d.ts +71 -0
- package/dist/state.js +269 -169
- package/dist/types.d.ts +4 -2
- package/dist/ui/dock.d.ts +109 -0
- package/dist/ui/hover-card.d.ts +44 -0
- package/dist/{variant-aurora-DwSOfraQ.js → variant-aurora-B8II3-I1.js} +27 -27
- package/dist/{variant-aurora-wKvAQ5wp.js → variant-aurora-CMNWOSJR.js} +28 -28
- package/dist/{variant-aurora-XEkPT0_z.js → variant-aurora-CcuDMWwQ.js} +2 -2
- package/dist/{variant-custom-eFIg2XWQ.js → variant-custom-BdvYC7af.js} +40 -40
- package/dist/{variant-custom-BhS9wIpU.js → variant-custom-C714mqKl.js} +65 -65
- package/dist/variant-custom-L16JLLZy.js +1 -0
- package/dist/{elements/chunks/variant-wave-DDCwhLas.js → variant-wave-2S4La2Ka.js} +2 -2
- package/dist/{variant-wave-CbVsmgwT.js → variant-wave-D7ruiA1W.js} +25 -25
- package/dist/{variant-wave-CUWGx9Bn.js → variant-wave-ecwykeyL.js} +21 -21
- package/dist/wire/chunk.d.ts +47 -0
- package/dist/wire/diagnostics.d.ts +575 -0
- package/dist/wire/encode-probe.d.ts +70 -0
- package/dist/wire/encode.d.ts +24 -0
- package/dist/wire/index.d.ts +2 -0
- package/dist/wire/sse.d.ts +10 -2
- package/dist/wire.js +917 -480
- package/frameworks/react/index.tsx +132 -93
- package/llms-full.txt +184 -69
- package/llms.txt +3 -3
- package/package.json +27 -7
- package/src/agent-tooling/archetypes.ts +41 -9
- package/src/agent-tooling/catalog/README.md +676 -0
- package/src/agent-tooling/catalog/catalog-types.ts +231 -0
- package/src/agent-tooling/catalog/fabrications.ts +96 -0
- package/src/agent-tooling/catalog/invariants.ts +284 -0
- package/src/agent-tooling/catalog/labs-titles.ts +114 -0
- package/src/agent-tooling/catalog/scenarios.ts +87 -0
- package/src/agent-tooling/catalog/surfaces.ts +275 -0
- package/src/agent-tooling/integrations/anthropic.ts +7 -1
- package/src/agent-tooling/integrations/cloudflare.ts +43 -2
- package/src/agent-tooling/integrations/langgraph.ts +7 -1
- package/src/agent-tooling/integrations/mastra.ts +7 -1
- package/src/agent-tooling/integrations/mock.ts +92 -17
- package/src/agent-tooling/integrations/ollama.ts +7 -1
- package/src/agent-tooling/integrations/openai.ts +7 -1
- package/src/agent-tooling/integrations/openrouter.ts +7 -1
- package/src/agent-tooling/integrations/pi.ts +56 -1
- package/src/agent-tooling/integrations/vercel-ai-sdk.ts +7 -1
- package/src/agent-tooling/mcp/css-raw.d.ts +12 -0
- package/src/agent-tooling/mcp/manifest.ts +100 -1
- package/src/agent-tooling/mcp/server.ts +61 -5
- package/src/agent-tooling/mcp/tools/reference.ts +440 -4
- package/src/agent-tooling/mcp/tools/scaffold.ts +1834 -307
- package/src/agent-tooling/mcp/tools/theme.ts +146 -30
- package/src/agent-tooling/mcp/validate-args.ts +141 -0
- package/src/agent-tooling/registry.ts +19 -7
- package/src/agent-tooling/route-emit.ts +26 -2
- package/src/components/attachment-types.ts +47 -1
- package/src/components/attachments.tsx +169 -22
- package/src/components/audio-visualizer/fit-scale.ts +112 -0
- package/src/components/audio-visualizer/index.tsx +80 -3
- package/src/components/audio-visualizer/variant-aurora.tsx +10 -4
- package/src/components/audio-visualizer/variant-bar.tsx +38 -6
- package/src/components/audio-visualizer/variant-custom.tsx +8 -2
- package/src/components/audio-visualizer/variant-grid.tsx +16 -11
- package/src/components/audio-visualizer/variant-radial.tsx +15 -10
- package/src/components/audio-visualizer/variant-wave.tsx +7 -2
- package/src/components/chat-container.tsx +4 -1
- package/src/components/chat-thread.tsx +36 -7
- package/src/components/code-block.tsx +79 -3
- package/src/components/composer.tsx +8 -1
- package/src/components/conversation-item.tsx +114 -4
- package/src/components/conversation-list.tsx +212 -12
- package/src/components/embed.tsx +4 -0
- package/src/components/form.tsx +41 -14
- package/src/components/message.tsx +75 -5
- package/src/components/response-stream.tsx +13 -7
- package/src/components/screen.tsx +5 -0
- package/src/components/scroll-button.tsx +7 -0
- package/src/components/toast.tsx +11 -5
- package/src/components/voice-input.tsx +35 -3
- package/src/components/voice-output.tsx +46 -6
- package/src/components/workspace-shell.tsx +285 -0
- package/src/diagnostics/hook.ts +352 -0
- package/src/diagnostics/index.ts +82 -0
- package/src/diagnostics/report-request.ts +296 -0
- package/src/elements/attachments.tsx +6 -7
- package/src/elements/audio-visualizer.tsx +48 -4
- package/src/elements/chat-workspace.tsx +159 -287
- package/src/elements/chat.tsx +12 -9
- package/src/elements/code-block.tsx +19 -1
- package/src/elements/command.tsx +4 -1
- package/src/elements/compiled.css +1 -1
- package/src/elements/conversation-item.tsx +80 -0
- package/src/elements/conversation-list.tsx +60 -11
- package/src/elements/default-input.tsx +5 -5
- package/src/elements/define.tsx +168 -8
- package/src/elements/diagnostic-events.ts +114 -0
- package/src/elements/dialog.tsx +47 -0
- package/src/elements/disclosure.ts +34 -12
- package/src/elements/dock.tsx +204 -0
- package/src/elements/dropdown.tsx +123 -0
- package/src/elements/element-diagnostics.ts +392 -0
- package/src/elements/element-manifest.json +12 -0
- package/src/elements/element-meta.json +672 -439
- package/src/elements/element-nonscalar.json +170 -0
- package/src/elements/element-types.d.ts +232 -148
- package/src/elements/menu.tsx +13 -0
- package/src/elements/prompt-dock.tsx +2 -1
- package/src/elements/prompt-input.tsx +8 -8
- package/src/elements/register-impl.ts +46 -0
- package/src/elements/register.ts +28 -0
- package/src/elements/resizable.tsx +52 -25
- package/src/elements/scroll-button.tsx +5 -0
- package/src/elements/slots.ts +107 -12
- package/src/elements/styles.css +30 -6
- package/src/elements/thinking-bar.tsx +16 -7
- package/src/elements/toast.tsx +3 -0
- package/src/elements/tool.tsx +26 -5
- package/src/elements/voice-input.tsx +11 -2
- package/src/elements/voice-output.tsx +16 -3
- package/src/index.ts +2 -0
- package/src/primitives/use-resize-observer.ts +33 -0
- package/src/primitives/use-speech-recognition.ts +12 -1
- package/src/primitives/use-stick-to-bottom.ts +34 -2
- package/src/primitives/use-text-stream.ts +33 -2
- package/src/remote/provider-runtime.ts +6 -3
- package/src/schemas/index.ts +1 -0
- package/src/schemas/tool-defs.ts +297 -6
- package/src/solid.ts +5 -0
- package/src/state/index.ts +12 -0
- package/src/state/persistence.ts +180 -0
- package/src/state/stream.ts +71 -4
- package/src/state/threads.ts +142 -0
- package/src/types.ts +4 -2
- package/src/ui/dialog.tsx +6 -0
- package/src/ui/dock.tsx +445 -0
- package/src/ui/dropdown.tsx +67 -23
- package/src/ui/hover-card.tsx +156 -6
- package/src/ui/input.tsx +29 -10
- package/src/ui/resizable.tsx +11 -0
- package/src/wire/chunk.ts +47 -0
- package/src/wire/consume.ts +185 -26
- package/src/wire/diagnostics.ts +727 -0
- package/src/wire/encode-probe.ts +214 -0
- package/src/wire/encode.ts +267 -14
- package/src/wire/formats/anthropic.ts +8 -1
- package/src/wire/formats/openai.ts +5 -0
- package/src/wire/index.ts +22 -0
- package/src/wire/read.ts +250 -6
- package/src/wire/sse.ts +16 -3
- package/dist/elements/chunks/action-icons-DW9muWrY.js +0 -1
- package/dist/elements/chunks/arrow-left-iJVnQmxA.js +0 -1
- package/dist/elements/chunks/attachments-CL6TaqIb.js +0 -1
- package/dist/elements/chunks/audio-visualizer-BuUqQmaO.js +0 -1
- package/dist/elements/chunks/chat-thread-DxoKaStk.js +0 -1
- package/dist/elements/chunks/check-MdLzbxrm.js +0 -1
- package/dist/elements/chunks/chevron-down-MK7IzDqe.js +0 -1
- package/dist/elements/chunks/chevron-right-C5jXhmtV.js +0 -1
- package/dist/elements/chunks/circle-BG7VkPep.js +0 -1
- package/dist/elements/chunks/circle-check-GqvO2UBs.js +0 -1
- package/dist/elements/chunks/code-block-ClhQO9RW.js +0 -1
- package/dist/elements/chunks/collapsible-DBcBkFPh.js +0 -1
- package/dist/elements/chunks/context-hKjNqiyQ.js +0 -1
- package/dist/elements/chunks/conversation-list-CsGJjSKG.js +0 -1
- package/dist/elements/chunks/default-input-B1Mem5wu.js +0 -1
- package/dist/elements/chunks/define-B_tXMYRm.js +0 -1
- package/dist/elements/chunks/disclosure-DAoYOKew.js +0 -1
- package/dist/elements/chunks/dropdown-CmNna5lT.js +0 -1
- package/dist/elements/chunks/embed-CjAD2jSi.js +0 -1
- package/dist/elements/chunks/form-Cp8Y00Wn.js +0 -1
- package/dist/elements/chunks/hover-card-6KtXpLwT.js +0 -1
- package/dist/elements/chunks/icon-97R8SR2p.js +0 -1
- package/dist/elements/chunks/input-DrseEjhp.js +0 -1
- package/dist/elements/chunks/message-CE4k-qaj.js +0 -1
- package/dist/elements/chunks/message-square-C9ovmtOo.js +0 -1
- package/dist/elements/chunks/reasoning-BrxQfpfI.js +0 -1
- package/dist/elements/chunks/scroll-button-CrTo7wm4.js +0 -1
- package/dist/elements/chunks/share-DgNbkJKY.js +0 -1
- package/dist/elements/chunks/slots-CIw9RlAe.js +0 -1
- package/dist/elements/chunks/store-HIPGhNz9.js +0 -1
- package/dist/elements/chunks/textarea-DPfuhswM.js +0 -1
- package/dist/elements/chunks/variant-custom-D5ZlmziF.js +0 -1
- package/dist/elements/chunks/video-Bb86fVYI.js +0 -1
- package/dist/elements/chunks/x-BOhjBeOd.js +0 -1
- package/dist/primitives/card-validate-generator.testlib.d.ts +0 -20
- package/dist/register-impl-DGmGzrEM.js +0 -145
- package/dist/variant-custom-MkTNTycd.js +0 -1
- package/src/primitives/card-validate-generator.testlib.ts +0 -38
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
import { ModelUsage } from './chunk.js';
|
|
2
|
+
import { ElementDiagnosticEvent } from '../elements/diagnostic-events.js';
|
|
3
|
+
/** The envelope every diagnostic event shares. `t` is `Date.now()` at emission. */
|
|
4
|
+
export interface WireDiagnosticBase {
|
|
5
|
+
type: string;
|
|
6
|
+
t: number;
|
|
7
|
+
/** Correlates every event from one provider response stream. */
|
|
8
|
+
streamId?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The APP'S grouping of several reads into one logical turn. Present only
|
|
11
|
+
* when the app declared it (`ConsumeOptions.traceId`), absent otherwise.
|
|
12
|
+
*
|
|
13
|
+
* THE KIT GROUPS NOTHING ON ITS OWN. A tool loop or a set of sub-agents makes
|
|
14
|
+
* several model calls that belong together, and the kit sees one Response at
|
|
15
|
+
* a time with no way to know which ones those are. Inferring it -- by
|
|
16
|
+
* timing, by sink identity, by anything -- would produce a grouping that is
|
|
17
|
+
* right often enough to be trusted and wrong exactly when a session is
|
|
18
|
+
* confusing enough to need a panel.
|
|
19
|
+
*/
|
|
20
|
+
traceId?: string;
|
|
21
|
+
/** The app's name for THIS read within the trace (`'planner'`,
|
|
22
|
+
* `'executor'`). Present only when declared; never derived from the format,
|
|
23
|
+
* the model or the URL. */
|
|
24
|
+
label?: string;
|
|
25
|
+
}
|
|
26
|
+
/** The stream opened: the source resolved and the format is about to be opened.
|
|
27
|
+
*
|
|
28
|
+
* CONNECTION IDENTITY (`url`, `hasQuery`, `status`, `contentType`) is reported
|
|
29
|
+
* only when the source was a `Response`, and each field is ABSENT rather than
|
|
30
|
+
* empty when the response did not state it. A `Response` built in a test or by
|
|
31
|
+
* a service worker has `url: ''`, and an empty string there would read as "it
|
|
32
|
+
* was served from the origin root", which is a confident wrong answer. */
|
|
33
|
+
export interface WireOpenEvent extends WireDiagnosticBase {
|
|
34
|
+
type: 'wire.open';
|
|
35
|
+
/** `opts.format.id`, e.g. `openai.chat-completions`. */
|
|
36
|
+
format: string;
|
|
37
|
+
source: 'response' | 'stream' | 'iterable';
|
|
38
|
+
/**
|
|
39
|
+
* ORIGIN AND PATHNAME ONLY. The query string is never reported, on any
|
|
40
|
+
* switch, and it is not payload either: `?api_key=sk-...` is a CREDENTIAL, a
|
|
41
|
+
* different class from the conversation content the payload key exists for,
|
|
42
|
+
* and a credential does not get a switch that turns it on. `hasQuery` carries
|
|
43
|
+
* the one bit a reader needs from it.
|
|
44
|
+
*
|
|
45
|
+
* Absent when the response did not state a URL, or stated one that does not
|
|
46
|
+
* parse.
|
|
47
|
+
*/
|
|
48
|
+
url?: string;
|
|
49
|
+
/** Whether the response URL carried a query string at all. Absent exactly
|
|
50
|
+
* when `url` is. */
|
|
51
|
+
hasQuery?: boolean;
|
|
52
|
+
/** The HTTP status. Always 2xx here -- a non-ok response throws and reports
|
|
53
|
+
* `wire.failed` instead -- but reported verbatim rather than assumed. */
|
|
54
|
+
status?: number;
|
|
55
|
+
/**
|
|
56
|
+
* The response's `content-type` header, VERBATIM.
|
|
57
|
+
*
|
|
58
|
+
* THE FIELD THAT PAYS FOR THIS EVENT. `text/html` or `application/json` where
|
|
59
|
+
* `text/event-stream` was expected is the classic proxy or misconfiguration
|
|
60
|
+
* tell, and from inside the parse it is invisible: the frames simply never
|
|
61
|
+
* arrive and the turn resolves empty. The response knew all along.
|
|
62
|
+
*/
|
|
63
|
+
contentType?: string;
|
|
64
|
+
}
|
|
65
|
+
/** One decoded SSE frame, and what the format made of it. */
|
|
66
|
+
export interface WireFrameEvent extends WireDiagnosticBase {
|
|
67
|
+
type: 'wire.frame';
|
|
68
|
+
/** 1-based. The final `seq` equals `wire.close`'s `frames`. */
|
|
69
|
+
seq: number;
|
|
70
|
+
/** UTF-8 byte length of the raw `data:` payload. */
|
|
71
|
+
bytes: number;
|
|
72
|
+
/** Neutral chunks this frame yielded. */
|
|
73
|
+
chunks: number;
|
|
74
|
+
/**
|
|
75
|
+
* The union of `Object.keys` over this frame's chunks. THE field that earns
|
|
76
|
+
* this design: frames arriving whose chunks never once carry a content key
|
|
77
|
+
* (`text`, `reasoning`, `toolCalls`, `sources`) is the failure a chunk count
|
|
78
|
+
* alone cannot separate from a healthy stream.
|
|
79
|
+
*/
|
|
80
|
+
fields: string[];
|
|
81
|
+
/** Present when this frame stated a model id. */
|
|
82
|
+
model?: string;
|
|
83
|
+
/** Content-bearing, so opt-in: the raw `data:` payload, verbatim. */
|
|
84
|
+
payload?: {
|
|
85
|
+
raw: string;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** A message part the recorder actually produced. */
|
|
89
|
+
export interface WirePartEvent extends WireDiagnosticBase {
|
|
90
|
+
type: 'wire.part';
|
|
91
|
+
/** The `MessagePart` type: 'text' | 'reasoning' | 'tool' | 'source'. */
|
|
92
|
+
variant: string;
|
|
93
|
+
index: number;
|
|
94
|
+
/** Delta LENGTH, never the delta. Present for text and reasoning only. */
|
|
95
|
+
chars?: number;
|
|
96
|
+
/** Content-bearing, so opt-in. One key per variant this write can carry:
|
|
97
|
+
* `delta` for text and reasoning, `patch` for a tool write (which holds the
|
|
98
|
+
* arguments and the output), `source` for a citation. */
|
|
99
|
+
payload?: {
|
|
100
|
+
delta?: string;
|
|
101
|
+
patch?: unknown;
|
|
102
|
+
source?: unknown;
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** The turn finished. Everything the widened empty-turn guard judges on. */
|
|
106
|
+
export interface WireCloseEvent extends WireDiagnosticBase {
|
|
107
|
+
type: 'wire.close';
|
|
108
|
+
/** Absent when `consumeModelStream` was called directly: there were no frames. */
|
|
109
|
+
frames?: number;
|
|
110
|
+
chunks: number;
|
|
111
|
+
/** Count per variant actually produced. Empty beside a non-zero `frames` is
|
|
112
|
+
* the wrong-dialect signature. */
|
|
113
|
+
parts: Record<string, number>;
|
|
114
|
+
finishReason: string | null;
|
|
115
|
+
stopReason?: string;
|
|
116
|
+
/** The kit's own closed vocabulary, so a panel keys its explanation off the
|
|
117
|
+
* code and never needs the message. */
|
|
118
|
+
errorCode?: string | number;
|
|
119
|
+
usage?: ModelUsage;
|
|
120
|
+
ms: number;
|
|
121
|
+
/** Content-bearing, so opt-in: the assembled turn, plus the in-band error's
|
|
122
|
+
* own message.
|
|
123
|
+
*
|
|
124
|
+
* ALL THREE TERMINAL EVENTS TREAT PROVIDER MESSAGE TEXT IDENTICALLY --
|
|
125
|
+
* `wire.close`, `wire.failed` and `wire.interrupted`. Each faces the same
|
|
126
|
+
* hazard, that a provider's message can echo request content back, and
|
|
127
|
+
* payload is precisely the switch that accepts it. Disagreeing here would
|
|
128
|
+
* read as an oversight and be "fixed" later by someone with less context. */
|
|
129
|
+
payload?: {
|
|
130
|
+
text: string;
|
|
131
|
+
reasoning: string;
|
|
132
|
+
toolCalls: unknown[];
|
|
133
|
+
sources: unknown[];
|
|
134
|
+
/** The in-band error's message. Absent when the turn carried no error. */
|
|
135
|
+
message?: string;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The read DIED. A terminal event for a stream that never reached `wire.close`.
|
|
140
|
+
*
|
|
141
|
+
* WHY IT EXISTS: without it, a stream that errored mid-read emitted `wire.open`
|
|
142
|
+
* and then nothing at all -- no close, no failed -- and a panel showed that
|
|
143
|
+
* stream open forever. In an observability tool that is a request which
|
|
144
|
+
* silently vanished, and "the one that never finished" is precisely the request
|
|
145
|
+
* someone opens the panel to find.
|
|
146
|
+
*
|
|
147
|
+
* MUTUALLY EXCLUSIVE with `wire.close`: exactly one of the two ends a read that
|
|
148
|
+
* got as far as `wire.open`. It is emitted from `readModelStream` only, so a
|
|
149
|
+
* direct `consumeModelStream` caller (which never emitted `wire.open` either)
|
|
150
|
+
* does not get one.
|
|
151
|
+
*
|
|
152
|
+
* The error itself is UNCHANGED and still thrown: this observes and rethrows.
|
|
153
|
+
*/
|
|
154
|
+
export interface WireInterruptedEvent extends WireDiagnosticBase {
|
|
155
|
+
type: 'wire.interrupted';
|
|
156
|
+
/** Frames decoded before it died. */
|
|
157
|
+
frames: number;
|
|
158
|
+
/** Neutral chunks yielded before it died. */
|
|
159
|
+
chunks: number;
|
|
160
|
+
/**
|
|
161
|
+
* `'abort'` only when the error IDENTIFIES ITSELF as one (`name` of
|
|
162
|
+
* `AbortError`), which is what a fetch abort produces. Anything else is
|
|
163
|
+
* `'error'` -- a guess here would turn "the provider dropped the connection"
|
|
164
|
+
* into "your user navigated away", and those have opposite fixes.
|
|
165
|
+
*/
|
|
166
|
+
reason: 'error' | 'abort';
|
|
167
|
+
/** The caught error's `name` (`'AbortError'`, `'TypeError'`). Metadata: a
|
|
168
|
+
* closed-ish vocabulary a panel keys an explanation off. The MESSAGE is
|
|
169
|
+
* payload. Absent when the thrown value carries no readable name. */
|
|
170
|
+
errorName?: string;
|
|
171
|
+
/** Content-bearing, so opt-in. The error's own message, which can echo
|
|
172
|
+
* request content back the way a provider's error text does. */
|
|
173
|
+
payload?: {
|
|
174
|
+
message?: string;
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** A non-ok HTTP response: the `WireError` path, before a chunk was read. */
|
|
178
|
+
export interface WireFailedEvent extends WireDiagnosticBase {
|
|
179
|
+
type: 'wire.failed';
|
|
180
|
+
status: number;
|
|
181
|
+
statusText: string;
|
|
182
|
+
bodyBytes: number;
|
|
183
|
+
bodyIsJson: boolean;
|
|
184
|
+
/** The parsed body's error CODE. Never the message: a provider's error text
|
|
185
|
+
* can echo request content back. */
|
|
186
|
+
providerCode?: string | number;
|
|
187
|
+
/** Content-bearing, so opt-in: the raw body and the provider's own error
|
|
188
|
+
* message, which is exactly the field that echoes request content back and
|
|
189
|
+
* is why the code is the half that travels by default. */
|
|
190
|
+
payload?: {
|
|
191
|
+
bodyText: string;
|
|
192
|
+
message?: string;
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/** One attachment the encoder handled, and what became of it.
|
|
196
|
+
*
|
|
197
|
+
* MEDIA TYPE AND SIZE, never the name and never the bytes. A filename is
|
|
198
|
+
* something the user typed and is payload; the media type and the size are
|
|
199
|
+
* facts about the shape of what went on the wire. */
|
|
200
|
+
export interface EncodeAttachmentReport {
|
|
201
|
+
/** As the encoder settled it, which is not always what the host declared: a
|
|
202
|
+
* `data:` URI's own media type wins over the field beside it. Absent when
|
|
203
|
+
* nothing named the file and classification failed before settling one. */
|
|
204
|
+
mediaType?: string;
|
|
205
|
+
/**
|
|
206
|
+
* Byte length of the bytes that went on the wire.
|
|
207
|
+
*
|
|
208
|
+
* PRESENT ONLY WHEN PAYLOAD CAPTURE IS ON. Counting it exactly is an O(n)
|
|
209
|
+
* scan of the payload, and the whole thread is re-encoded every turn, so it
|
|
210
|
+
* is a recurring per-turn cost rather than a one-off -- the same rule
|
|
211
|
+
* `EncodeRequestEvent.bytes` follows, for the same reason. The fields around
|
|
212
|
+
* it cost nothing and are always present, so the diagnosis ("this attachment
|
|
213
|
+
* was skipped") survives without the refinement ("...and it was 240 kB").
|
|
214
|
+
*
|
|
215
|
+
* ABSENT for a remote attachment even then: the provider dereferences that
|
|
216
|
+
* URL itself and the bytes never enter this process, so any number would be
|
|
217
|
+
* invented -- and `0` beside a 40 MB PDF is the exact confident zero the
|
|
218
|
+
* forward-compat rule exists to prevent.
|
|
219
|
+
*
|
|
220
|
+
* Absent always means NOT REPORTED, and is never backfilled with an estimate.
|
|
221
|
+
*/
|
|
222
|
+
bytes?: number;
|
|
223
|
+
/** Whether anything at all reached the wire for this attachment. */
|
|
224
|
+
encoded: boolean;
|
|
225
|
+
/**
|
|
226
|
+
* `'encoded'` -- became an image/file/document block.
|
|
227
|
+
* `'as-text'` -- a text file, inlined as text CONTENT, because neither API
|
|
228
|
+
* has an arbitrary-file block. Worth distinguishing: it is
|
|
229
|
+
* why an attachment can be "sent" and still not be visible to
|
|
230
|
+
* the model as a file.
|
|
231
|
+
* `'skipped'` -- nothing went out for it. See `reason`.
|
|
232
|
+
*/
|
|
233
|
+
disposition: 'encoded' | 'skipped' | 'as-text';
|
|
234
|
+
/** Why it was skipped, in the encoder's own words -- the same sentence the
|
|
235
|
+
* throw would have carried. Present on `'skipped'` only. */
|
|
236
|
+
reason?: string;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* A thread was encoded for a provider. THE WRITE-PATH HEADLINE.
|
|
240
|
+
*
|
|
241
|
+
* CONTEXT INTEGRITY is the question this answers: is the context that goes to
|
|
242
|
+
* the model the context you think it is? The encoder is the one layer that sees
|
|
243
|
+
* both sides -- the `ChatMessage[]` going in and the provider messages coming
|
|
244
|
+
* out -- so it is the only place the delta between them can be stated as data
|
|
245
|
+
* instead of as folklore.
|
|
246
|
+
*
|
|
247
|
+
* Every field is metadata: counts, variant names, media types, sizes. The
|
|
248
|
+
* encoded body itself lives under `payload`.
|
|
249
|
+
*/
|
|
250
|
+
export interface EncodeRequestEvent extends WireDiagnosticBase {
|
|
251
|
+
type: 'encode.request';
|
|
252
|
+
format: 'openai' | 'anthropic';
|
|
253
|
+
/** `ChatMessage[]` in. */
|
|
254
|
+
threadMessages: number;
|
|
255
|
+
/** Provider messages out. NOT the same number, by design: one assistant turn
|
|
256
|
+
* carrying a tool call splits into three wire messages, and a truncating
|
|
257
|
+
* host shrinks it the other way. The delta is the point. */
|
|
258
|
+
wireMessages: number;
|
|
259
|
+
/**
|
|
260
|
+
* System-role messages in the ENCODED OUTPUT.
|
|
261
|
+
*
|
|
262
|
+
* ZERO IS THE COMMON ANSWER AND IT IS THE FINDING, which is why it is a
|
|
263
|
+
* stated 0 rather than an omitted key: `ChatMessage.role` has no system
|
|
264
|
+
* member at all, so the system prompt is always being added somewhere the kit
|
|
265
|
+
* cannot see -- a server route, a gateway, a middleware. A developer chasing
|
|
266
|
+
* "why is the model ignoring its instructions" needs to know the kit is not
|
|
267
|
+
* the layer holding them.
|
|
268
|
+
*/
|
|
269
|
+
systemMessages: number;
|
|
270
|
+
/** Role counts over the ENCODED OUTPUT. A role with no messages is absent
|
|
271
|
+
* rather than 0; `systemMessages` is stated separately for that reason. */
|
|
272
|
+
byRole: Record<string, number>;
|
|
273
|
+
/** Part variants present in the THREAD, by count. */
|
|
274
|
+
partsIn: Record<string, number>;
|
|
275
|
+
/** Part variants that reached the wire, by count. `partsIn` minus this is the
|
|
276
|
+
* round-trip loss, and each loss also has its own `encode.dropped`. */
|
|
277
|
+
partsEncoded: Record<string, number>;
|
|
278
|
+
/** One entry per `file` part the encoder handled, in encounter order. */
|
|
279
|
+
attachments: EncodeAttachmentReport[];
|
|
280
|
+
/**
|
|
281
|
+
* UTF-8 byte length of the encoded body as JSON.
|
|
282
|
+
*
|
|
283
|
+
* PRESENT ONLY WHEN PAYLOAD CAPTURE IS ON, because producing it means
|
|
284
|
+
* serializing the whole body -- every inlined attachment included -- and
|
|
285
|
+
* that is a cost the metadata stream refuses to pay for one number. When the
|
|
286
|
+
* body is already being materialized for `payload`, it is free and exact.
|
|
287
|
+
* Absent also when the body could not be stringified at all (a circular
|
|
288
|
+
* `tool.output`, a BigInt). Absent means NOT REPORTED, never zero, and it is
|
|
289
|
+
* deliberately not backfilled with an estimate.
|
|
290
|
+
*/
|
|
291
|
+
bytes?: number;
|
|
292
|
+
/** Content-bearing, so opt-in. `attachments` is positionally aligned with the
|
|
293
|
+
* metadata array above. */
|
|
294
|
+
payload?: {
|
|
295
|
+
body: unknown;
|
|
296
|
+
attachments?: Array<{
|
|
297
|
+
filename?: string;
|
|
298
|
+
}>;
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* A part that was in the thread and is NOT on the wire.
|
|
303
|
+
*
|
|
304
|
+
* THE EVENT THE WHOLE WRITE PATH IS FOR. "Your attachment rendered in the
|
|
305
|
+
* thread and was never sent to the model" was a sentence nobody could get out
|
|
306
|
+
* of the kit, and every one of these drops is deliberate, documented, and was
|
|
307
|
+
* invisible.
|
|
308
|
+
*
|
|
309
|
+
* It reports; it never decides. Nothing about what the encoders drop changed in
|
|
310
|
+
* order to add it.
|
|
311
|
+
*/
|
|
312
|
+
export interface EncodeDroppedEvent extends WireDiagnosticBase {
|
|
313
|
+
type: 'encode.dropped';
|
|
314
|
+
/** The `MessagePart` type, taken from the part itself at the site that
|
|
315
|
+
* already discriminated it. */
|
|
316
|
+
variant: string;
|
|
317
|
+
/** Parts this event accounts for. Always 1 today -- every site reports per
|
|
318
|
+
* part, because per-part is what carries the indices -- and the field exists
|
|
319
|
+
* so a site that ever aggregates can say N without a new event type. */
|
|
320
|
+
count: number;
|
|
321
|
+
/** Position in the `ChatMessage[]` that was passed in. */
|
|
322
|
+
messageIndex?: number;
|
|
323
|
+
/** Position in that message's `parts`. */
|
|
324
|
+
partIndex?: number;
|
|
325
|
+
/** The reason documented at the drop site, in one sentence. */
|
|
326
|
+
reason: string;
|
|
327
|
+
/** Content-bearing, so opt-in: the dropped part itself, verbatim. */
|
|
328
|
+
payload?: {
|
|
329
|
+
part: unknown;
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* What the APP says it sent. Emitted only by `reportRequest`, never by the kit
|
|
334
|
+
* on its own.
|
|
335
|
+
*
|
|
336
|
+
* ★ THE ONLY EVENT THE KIT DOES NOT OBSERVE FOR ITSELF, and that is the point.
|
|
337
|
+
* In the normal shape the app's server route adds the system prompt, picks the
|
|
338
|
+
* model, and performs any RAG or guardrail injection, and NONE of it passes
|
|
339
|
+
* through the kit -- which is why `encode.request.systemMessages` is always 0.
|
|
340
|
+
* That 0 says "the system prompt is being added somewhere I cannot see"; this
|
|
341
|
+
* event is how a developer chooses to make that somewhere visible.
|
|
342
|
+
*
|
|
343
|
+
* Deliberate disclosure, so it is the app's decision and not the kit's
|
|
344
|
+
* collection. See `reportRequest` for why that distinction is load-bearing.
|
|
345
|
+
*/
|
|
346
|
+
export interface AppRequestEvent extends WireDiagnosticBase {
|
|
347
|
+
type: 'app.request';
|
|
348
|
+
/** Length of the body's `messages` array. Absent when there was no array to
|
|
349
|
+
* count -- which is a different fact from a request carrying none. */
|
|
350
|
+
messages?: number;
|
|
351
|
+
/**
|
|
352
|
+
* Role counts over that array, INCLUDING `system`.
|
|
353
|
+
*
|
|
354
|
+
* This is the half the kit structurally cannot see: `ChatMessage.role` has no
|
|
355
|
+
* system member at all, so a system prompt exists only in the body the app
|
|
356
|
+
* builds. A role nobody sent is absent rather than 0.
|
|
357
|
+
*
|
|
358
|
+
* ★ THESE COUNTS NEED NOT SUM TO `messages`, and a consumer must not assume
|
|
359
|
+
* they do. An entry stating no readable role is counted in `messages` and
|
|
360
|
+
* contributes to no bucket -- a body of 5 messages where 1 states a role
|
|
361
|
+
* gives `messages: 5` with `byRole: { user: 1 }`. That is deliberate:
|
|
362
|
+
* bucketing the rest under `'unknown'` would invent a role nobody sent. A
|
|
363
|
+
* panel rendering these as a stacked bar should render the difference as
|
|
364
|
+
* unattributed rather than scaling it away.
|
|
365
|
+
*/
|
|
366
|
+
byRole?: Record<string, number>;
|
|
367
|
+
/**
|
|
368
|
+
* System-role messages, stated explicitly whenever the array could be counted.
|
|
369
|
+
*
|
|
370
|
+
* ZERO IS A FINDING and must be distinguishable from "not reported": it says
|
|
371
|
+
* nothing is setting a system prompt at this layer either. Reading it off
|
|
372
|
+
* `byRole.system` could not tell those apart, which is the whole reason this
|
|
373
|
+
* field is separate -- and it lines up directly against
|
|
374
|
+
* `EncodeRequestEvent.systemMessages` for the comparison that answers "are
|
|
375
|
+
* additional prompts being added?".
|
|
376
|
+
*/
|
|
377
|
+
systemMessages?: number;
|
|
378
|
+
/** Length of the body's `tools` array. A present-but-empty array reports 0,
|
|
379
|
+
* which is a real and different state from the key being absent: it says the
|
|
380
|
+
* app meant to send tools and sent none. Absent when there is no array. */
|
|
381
|
+
tools?: number;
|
|
382
|
+
/**
|
|
383
|
+
* The REQUESTED model, read verbatim from the body.
|
|
384
|
+
*
|
|
385
|
+
* ★ NEVER INFERRED, and absent stays absent. Paired with `WireFrameEvent`'s
|
|
386
|
+
* SERVED model this is the devtools spec's "selected Claude, served gpt-4o"
|
|
387
|
+
* finding -- two independent facts a panel COMPARES and never reconciles.
|
|
388
|
+
* Deriving either half from the other would make them agree in exactly the
|
|
389
|
+
* mismatch case the pair exists to catch.
|
|
390
|
+
*/
|
|
391
|
+
model?: string;
|
|
392
|
+
/** ORIGIN AND PATHNAME ONLY, on the same terms as `WireOpenEvent.url`: a
|
|
393
|
+
* query string can carry a credential, and a credential is not conversation
|
|
394
|
+
* content, so it does not travel under any switch. Absent when no URL was
|
|
395
|
+
* supplied or it did not parse. */
|
|
396
|
+
url?: string;
|
|
397
|
+
/** Whether that URL carried a query string. Absent exactly when `url` is. */
|
|
398
|
+
hasQuery?: boolean;
|
|
399
|
+
/**
|
|
400
|
+
* UTF-8 byte length of the body AS IT WILL BE SENT.
|
|
401
|
+
*
|
|
402
|
+
* PRESENT ONLY UNDER PAYLOAD CAPTURE, by the same rule
|
|
403
|
+
* `EncodeRequestEvent.bytes` follows: measuring means materializing the body,
|
|
404
|
+
* and a request is made every turn.
|
|
405
|
+
*
|
|
406
|
+
* ABSENT WHENEVER IT CANNOT BE KNOWN EXACTLY, which is a larger set than it
|
|
407
|
+
* looks. A string, a JSON-declaring body, a `Blob`/`File` and an
|
|
408
|
+
* `ArrayBuffer` or view can all be measured exactly and are. A `FormData`, a
|
|
409
|
+
* `ReadableStream`, a `URLSearchParams`, a `Map`, or a circular body cannot,
|
|
410
|
+
* and report nothing -- never the length of the `"{}"` they happen to
|
|
411
|
+
* stringify to, which is how a 40 MB upload came to report `bytes: 2`.
|
|
412
|
+
*/
|
|
413
|
+
bytes?: number;
|
|
414
|
+
/**
|
|
415
|
+
* Content-bearing, so opt-in: the body itself, exactly as handed over.
|
|
416
|
+
*
|
|
417
|
+
* ★ PUBLISHED BY REFERENCE, NOT CLONED. A subscriber that mutates
|
|
418
|
+
* `payload.body` mutates the object the app is about to send. Deep-cloning
|
|
419
|
+
* every request would be expensive on exactly the large bodies worth
|
|
420
|
+
* inspecting, and would defeat the point of handing the real thing over -- so
|
|
421
|
+
* the contract is that a consumer TREATS THIS AS READ-ONLY. Clone it yourself
|
|
422
|
+
* if you intend to edit it.
|
|
423
|
+
*/
|
|
424
|
+
payload?: {
|
|
425
|
+
body: unknown;
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
export type WireDiagnosticEvent = WireOpenEvent | WireFrameEvent | WirePartEvent | WireCloseEvent | WireFailedEvent | WireInterruptedEvent | EncodeRequestEvent | EncodeDroppedEvent | AppRequestEvent;
|
|
429
|
+
/** The three correlating fields, built ONCE per read and spread onto every
|
|
430
|
+
* event it emits. One definition so `read.ts` and `consume.ts` cannot drift
|
|
431
|
+
* about which events carry a trace. */
|
|
432
|
+
export interface WireCorrelation {
|
|
433
|
+
streamId: string;
|
|
434
|
+
traceId?: string;
|
|
435
|
+
label?: string;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* INTERNAL. The correlation for one read.
|
|
439
|
+
*
|
|
440
|
+
* ABSENT, NOT UNDEFINED. `traceId` and `label` are omitted entirely when the app
|
|
441
|
+
* did not declare them, so a consumer's `'traceId' in event` answers "did the
|
|
442
|
+
* app group this" rather than "did the kit have an opinion". A key present with
|
|
443
|
+
* an undefined value is the kit claiming it looked, and it is not in a position
|
|
444
|
+
* to make that claim.
|
|
445
|
+
*/
|
|
446
|
+
export declare function wireCorrelation(streamId: string, opts: {
|
|
447
|
+
traceId?: string;
|
|
448
|
+
label?: string;
|
|
449
|
+
}): WireCorrelation;
|
|
450
|
+
/**
|
|
451
|
+
* EVERY diagnostic event, from every layer that emits one.
|
|
452
|
+
*
|
|
453
|
+
* There is one emitter and one stream, so there has to be one union. `wire.*`
|
|
454
|
+
* events say what the parse pipeline saw; `element.*` events say what a
|
|
455
|
+
* consumer did to a live custom element. A panel wants both and wants them in
|
|
456
|
+
* arrival order, which is precisely why they share a channel rather than
|
|
457
|
+
* getting one each.
|
|
458
|
+
*
|
|
459
|
+
* WIDENING, not repurposing: no existing field changed meaning and no existing
|
|
460
|
+
* type was renamed, so the forward-compat rule at the top of this file holds. A
|
|
461
|
+
* consumer that already switches on `type` and ignores what it does not know --
|
|
462
|
+
* which the contract requires -- is unaffected. What DOES move is the static
|
|
463
|
+
* type a `.d.ts` consumer sees on `subscribeWireDiagnostics`, `drain()` and
|
|
464
|
+
* `attach()`: it is now the wider union, so code that assigned the callback
|
|
465
|
+
* parameter straight into a `WireDiagnosticEvent` needs a narrowing check it
|
|
466
|
+
* should have had anyway.
|
|
467
|
+
*/
|
|
468
|
+
export type KaiDiagnosticEvent = WireDiagnosticEvent | ElementDiagnosticEvent;
|
|
469
|
+
/**
|
|
470
|
+
* Narrow the shared stream to the element half — and, by negation, to the wire
|
|
471
|
+
* half, which is what most callers actually want:
|
|
472
|
+
*
|
|
473
|
+
* if (isElementDiagnosticEvent(e)) { … } else { e.streamId … }
|
|
474
|
+
*
|
|
475
|
+
* WHY THIS DIRECTION and not an `isWireDiagnosticEvent`. The element family is
|
|
476
|
+
* CLOSED and small; the non-element side is the one that keeps growing, and it
|
|
477
|
+
* does not even share one prefix -- `wire.*`, `encode.*` and `app.*` are all in
|
|
478
|
+
* `WireDiagnosticEvent` today, with `kit.warn` queued in the inventory behind
|
|
479
|
+
* them. That is not a prediction: `encode.*` and `app.*` both arrived while this
|
|
480
|
+
* branch was open, so a positive `wire.*` allowlist written a fortnight ago
|
|
481
|
+
* would already be answering "not a wire event" for two live families. Testing
|
|
482
|
+
* the closed set and taking the complement stays correct as the open set grows.
|
|
483
|
+
*
|
|
484
|
+
* Exists because a panel, and every test in this repo that reads `streamId` or
|
|
485
|
+
* `traceId` off "every event", needs exactly this one check now that two layers
|
|
486
|
+
* share one channel. The prefix is pinned by `element-artifact-divergence.test.ts`.
|
|
487
|
+
*/
|
|
488
|
+
export declare function isElementDiagnosticEvent(e: KaiDiagnosticEvent): e is ElementDiagnosticEvent;
|
|
489
|
+
type Subscriber = (e: KaiDiagnosticEvent) => void;
|
|
490
|
+
/**
|
|
491
|
+
* Subscribe to wire diagnostics. Returns an unsubscribe function.
|
|
492
|
+
*
|
|
493
|
+
* SSR-safe: `globalThis` is available in every runtime the kit imports into, and
|
|
494
|
+
* nothing here touches `window`.
|
|
495
|
+
*/
|
|
496
|
+
export declare function subscribeWireDiagnostics(fn: Subscriber): () => void;
|
|
497
|
+
/**
|
|
498
|
+
* INTERNAL. Every emission site gates on this BEFORE constructing an event
|
|
499
|
+
* object, which is what makes the no-subscriber path cost one symbol read and
|
|
500
|
+
* allocate nothing -- not even the shared state.
|
|
501
|
+
*/
|
|
502
|
+
export declare function wireDiagnosticsActive(): boolean;
|
|
503
|
+
/**
|
|
504
|
+
* INTERNAL. Whether content-bearing values may ride under the `payload` key.
|
|
505
|
+
*
|
|
506
|
+
* A SEPARATE SWITCH FROM `wireDiagnosticsActive`, and the separation is the
|
|
507
|
+
* security property rather than a preference. The panel is a pasted script tag
|
|
508
|
+
* on a live site and `?kai-devtools=1` is guessable; a stranger who guesses it
|
|
509
|
+
* gets the SHAPE of a conversation and not one word of its content. Turning
|
|
510
|
+
* that into content takes a second, deliberate signal that no URL can set.
|
|
511
|
+
*
|
|
512
|
+
* Every emission site checks this INSIDE its `wireDiagnosticsActive()` branch
|
|
513
|
+
* and builds the payload object only then, so a session with a subscriber and
|
|
514
|
+
* no payload switch pays one extra boolean read per event.
|
|
515
|
+
*/
|
|
516
|
+
export declare function wirePayloadActive(): boolean;
|
|
517
|
+
/**
|
|
518
|
+
* INTERNAL. Turn payload capture on or off.
|
|
519
|
+
*
|
|
520
|
+
* Called by the devtools hook at install, from the payload signal it read
|
|
521
|
+
* synchronously. It is not part of the public surface: a consumer who can flip
|
|
522
|
+
* this can make the kit start recording its own users' conversations, and that
|
|
523
|
+
* is a decision the app makes through the signal, deliberately, not something a
|
|
524
|
+
* dependency can reach in and do.
|
|
525
|
+
*
|
|
526
|
+
* Turning it ON allocates the shared state; turning it OFF does not, so the
|
|
527
|
+
* dormant no-signal path still allocates nothing at all.
|
|
528
|
+
*/
|
|
529
|
+
export declare function setWirePayloadCapture(on: boolean): void;
|
|
530
|
+
/**
|
|
531
|
+
* INTERNAL. The `payload` key, or nothing.
|
|
532
|
+
*
|
|
533
|
+
* Every content-bearing value in this module goes through here, which is what
|
|
534
|
+
* makes the boundary reviewable: there is one call to grep for, one key to
|
|
535
|
+
* strip in a test, and no field-by-field audit. The builder is a THUNK so that
|
|
536
|
+
* with the switch off nothing is read, copied or stringified -- the cost of
|
|
537
|
+
* carrying an unused payload is a boolean.
|
|
538
|
+
*/
|
|
539
|
+
export declare function withPayload<T>(build: () => T): {
|
|
540
|
+
payload: T;
|
|
541
|
+
} | Record<string, never>;
|
|
542
|
+
/**
|
|
543
|
+
* INTERNAL. Deliver to every subscriber.
|
|
544
|
+
*
|
|
545
|
+
* Iterates a SNAPSHOT so a subscriber that unsubscribes (or subscribes) during
|
|
546
|
+
* delivery cannot make the loop skip its neighbour. Each call is wrapped
|
|
547
|
+
* individually: a panel that throws must not take down the stream it is
|
|
548
|
+
* watching, nor starve the other subscribers. The throw is swallowed rather
|
|
549
|
+
* than re-reported because there is nowhere to report it TO that is not itself
|
|
550
|
+
* a subscriber.
|
|
551
|
+
*/
|
|
552
|
+
export declare function emitWireDiagnostic(e: KaiDiagnosticEvent): void;
|
|
553
|
+
/**
|
|
554
|
+
* INTERNAL. One id per provider response stream.
|
|
555
|
+
*
|
|
556
|
+
* It correlates diagnostics AND namespaces reasoning block indices. Anthropic
|
|
557
|
+
* restarts content-block indices at 0 on every message, and a tool loop reads
|
|
558
|
+
* several messages into ONE assistant turn, so without this round 2's block 0
|
|
559
|
+
* merges into round 1's part and overwrites its verbatim `raw`. See
|
|
560
|
+
* `appendReasoningPart`.
|
|
561
|
+
*
|
|
562
|
+
* A monotonic counter, not a UUID: this never leaves the process, is compared
|
|
563
|
+
* only for equality against parts built in the same process, and a counter keeps
|
|
564
|
+
* the value short and reproducible in test output.
|
|
565
|
+
*
|
|
566
|
+
* It lives HERE rather than in `consume.ts` because `readModelStream` opens the
|
|
567
|
+
* format and counts frames before `consumeModelStream` runs, and every event
|
|
568
|
+
* from one read has to carry the same id.
|
|
569
|
+
*
|
|
570
|
+
* The counter sits in the SHARED state, so two copies of this module continue
|
|
571
|
+
* one sequence instead of both restarting at `wire-1` and minting the same id
|
|
572
|
+
* for different streams. See the note on `STATE_KEY`.
|
|
573
|
+
*/
|
|
574
|
+
export declare function nextStreamId(): string;
|
|
575
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { EncodeAttachmentReport } from './diagnostics.js';
|
|
2
|
+
/**
|
|
3
|
+
* Bytes a base64 payload stands for, WITHOUT decoding it.
|
|
4
|
+
*
|
|
5
|
+
* Four base64 characters carry three bytes, less the padding. Decoding a 40 MB
|
|
6
|
+
* attachment to count it would make the diagnostic more expensive than the
|
|
7
|
+
* encode it is watching.
|
|
8
|
+
*
|
|
9
|
+
* ★ THIS IS AN O(n) SCAN AND THERE IS NO EXACT WAY AROUND IT -- about 0.43 ms
|
|
10
|
+
* per MB of attachment. THAT IS WHY ITS CALLER IS PAYLOAD-GATED (see `bytesOf`
|
|
11
|
+
* in encode.ts): the thread is re-encoded every turn, so the scan is a
|
|
12
|
+
* recurring per-turn cost rather than a one-off, and unbounded-per-turn is what
|
|
13
|
+
* fails "cheap enough to leave on". With payload off this is never called; with
|
|
14
|
+
* payload on it runs and the number is exact.
|
|
15
|
+
*
|
|
16
|
+
* THE ALTERNATIVE WAS NOT TO GUESS, and three candidates were measured before
|
|
17
|
+
* settling on gating instead:
|
|
18
|
+
*
|
|
19
|
+
* · A charCodeAt counting loop -- 7x SLOWER on the shape that actually occurs
|
|
20
|
+
* (a `data:` URI, no whitespace): 2.77 ms vs 0.61 ms per 1.4 MB. It avoids
|
|
21
|
+
* a copy that a whitespace-free `.replace` never makes, because V8 returns
|
|
22
|
+
* the original string when the pattern does not match. Strictly worse.
|
|
23
|
+
* · Native whitespace `.test()` then O(1) arithmetic on `.length` -- exact,
|
|
24
|
+
* and identical in time (0.60 ms), because the test is the same full scan.
|
|
25
|
+
* No win worth the extra branch.
|
|
26
|
+
* · Probing only a prefix for whitespace, then O(1) arithmetic -- 170x faster
|
|
27
|
+
* (0.0036 ms) and REJECTED anyway: it assumes whitespace is periodic line
|
|
28
|
+
* wrapping, so a payload whose only whitespace sits past the probe window
|
|
29
|
+
* is silently OVER-counted. That is an estimate wearing a measurement's
|
|
30
|
+
* clothes, which is the one thing a size field must never be.
|
|
31
|
+
*
|
|
32
|
+
* So the resolution is exact-but-gated rather than always-on-but-approximate. A
|
|
33
|
+
* cheaper counter is welcome; a less truthful one is not, and `base64-bytes.
|
|
34
|
+
* test.ts` pins the arithmetic against a real decode so any replacement has to
|
|
35
|
+
* prove it is the same function rather than merely a plausible one.
|
|
36
|
+
*/
|
|
37
|
+
export declare function base64Bytes(data: string): number;
|
|
38
|
+
export interface EncodeProbe {
|
|
39
|
+
/** One part seen in the thread. `variant` is `part.type`, read at a site that
|
|
40
|
+
* has the part in hand -- this function never looks. */
|
|
41
|
+
seen(variant: string): void;
|
|
42
|
+
/** One part that reached the wire. */
|
|
43
|
+
encoded(variant: string): void;
|
|
44
|
+
/** One part that did NOT. `part` is carried only to be published under the
|
|
45
|
+
* payload key, and is `unknown` so it cannot be inspected here. */
|
|
46
|
+
dropped(variant: string, reason: string, messageIndex: number, partIndex: number, part: unknown): void;
|
|
47
|
+
/** One `file` part's outcome, plus the filename held back for the payload
|
|
48
|
+
* key. Called exactly once per file part, whatever became of it. */
|
|
49
|
+
attachment(report: EncodeAttachmentReport, filename?: string): void;
|
|
50
|
+
/** Emit `encode.request`. Called once, after the body is built. */
|
|
51
|
+
finish(format: 'openai' | 'anthropic', threadMessages: number, body: ReadonlyArray<{
|
|
52
|
+
role: string;
|
|
53
|
+
}>): void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A ledger for one encode call.
|
|
57
|
+
*
|
|
58
|
+
* Built ONLY inside an active-diagnostics branch. It holds counts and, for the
|
|
59
|
+
* payload key, filenames; it never holds message text.
|
|
60
|
+
*
|
|
61
|
+
* NO `streamId`, deliberately. An encode is not a read: it happens before there
|
|
62
|
+
* is a response to correlate to, and minting an id here would invent a
|
|
63
|
+
* relationship the kit cannot vouch for. `traceId`/`label` DO carry, because
|
|
64
|
+
* those are the app's own declaration and the app is the one layer that knows
|
|
65
|
+
* the encoded request and the read that answered it are the same turn.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createEncodeProbe(opts: {
|
|
68
|
+
traceId?: string;
|
|
69
|
+
label?: string;
|
|
70
|
+
}): EncodeProbe;
|
package/dist/wire/encode.d.ts
CHANGED
|
@@ -100,6 +100,30 @@ export interface FileEncodeOptions {
|
|
|
100
100
|
* enable it; that would just move the failure to a provider 400.
|
|
101
101
|
*/
|
|
102
102
|
accept?: MediaTypeFilter;
|
|
103
|
+
/**
|
|
104
|
+
* The app's own id for the logical turn this encode belongs to, carried onto
|
|
105
|
+
* every diagnostic event the encode emits. Purely diagnostic: nothing here
|
|
106
|
+
* branches on it and it never reaches a provider.
|
|
107
|
+
*
|
|
108
|
+
* THE SAME FIELD, THE SAME MEANING, as `ConsumeOptions.traceId` -- and that
|
|
109
|
+
* symmetry is the whole payoff. Encoding happens BEFORE a read opens, so
|
|
110
|
+
* there is no stream to attach an encode to and the kit will not invent one.
|
|
111
|
+
* Pass the same id to both halves:
|
|
112
|
+
*
|
|
113
|
+
* const body = toOpenAIMessages(messages, { traceId: 'turn-42' });
|
|
114
|
+
* readOpenAIStream(res, sink, { traceId: 'turn-42' });
|
|
115
|
+
*
|
|
116
|
+
* and the request and the response it produced sit together, with a tool loop
|
|
117
|
+
* or a sub-agent fan-out grouping into one trace. Without it you still see
|
|
118
|
+
* both halves; they are simply unlinked, which is the honest rendering --
|
|
119
|
+
* pinning an encode to "the next stream that opens" would be a guess, and an
|
|
120
|
+
* encode may be followed by no stream at all.
|
|
121
|
+
*/
|
|
122
|
+
traceId?: string;
|
|
123
|
+
/** The app's name for this call inside its trace (`'planner'`, `'retry-2'`).
|
|
124
|
+
* Same field and same meaning as `ConsumeOptions.label`. Absent when not
|
|
125
|
+
* supplied. */
|
|
126
|
+
label?: string;
|
|
103
127
|
}
|
|
104
128
|
export type AnthropicEncodeOptions = FileEncodeOptions;
|
|
105
129
|
/** Anthropic content blocks are an open, provider-owned union. Keeping them as
|