@kitn.ai/ui 0.28.0 → 0.30.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.
Files changed (615) hide show
  1. package/README.md +11 -7
  2. package/dist/{Icon-DObhsRtm.js → Icon-BzBET3E-.js} +1 -1
  3. package/dist/{action-icons-CcgNN2oT.js → action-icons-DaW2pU6m.js} +1 -1
  4. package/dist/agent-tooling/construct/public.d.ts +15 -0
  5. package/dist/agent-tooling/construct/schema.d.ts +111 -0
  6. package/dist/arrow-down-D783iMJj.js +1 -0
  7. package/dist/arrow-left-0VLM5kyg.js +1 -0
  8. package/dist/artifact-DGjzHYUT.js +1 -0
  9. package/dist/{attachments-Qr4kg-2b.js → attachments-YvXHlg88.js} +1 -1
  10. package/dist/{audio-visualizer-DToYdfSc.js → audio-visualizer-9lz5IHsL.js} +1 -1
  11. package/dist/{badge-CdEu_jn2.js → badge-Dh_vp8A5.js} +1 -1
  12. package/dist/{bell-DQAAOpDz.js → bell-CT7_WUwS.js} +1 -1
  13. package/dist/{button-DkuOnGLy.js → button-C_3te6fm.js} +1 -1
  14. package/dist/{card-renderer-Jx70m6_l.js → card-renderer-DZVaGH2e.js} +1 -1
  15. package/dist/check-CiEOVnEN.js +1 -0
  16. package/dist/{checkbox-D7iRB3nL.js → checkbox-Bu2T2e0U.js} +1 -1
  17. package/dist/{checkbox-group-B2_rpoPM.js → checkbox-group-BiryEXPQ.js} +1 -1
  18. package/dist/chevron-down-CSobuZRG.js +1 -0
  19. package/dist/chevron-right-DR9ZAP-2.js +1 -0
  20. package/dist/{choice-card-m0BYqqOK.js → choice-card-CM6IjkmL.js} +1 -1
  21. package/dist/circle-BC66EYF7.js +1 -0
  22. package/dist/circle-check-BqmahW0O.js +1 -0
  23. package/dist/{circle-x-BPG0iPst.js → circle-x-Bk-dA5Wf.js} +1 -1
  24. package/dist/{code-block-ClMLKb7C.js → code-block-B26t-Q7q.js} +1 -1
  25. package/dist/{collapsible-B4DlWicL.js → collapsible-Bhc636Cf.js} +1 -1
  26. package/dist/components/builder-composer-triggers.d.ts +62 -0
  27. package/dist/components/builder-layout.d.ts +54 -0
  28. package/dist/components/builder-message-actions.d.ts +72 -0
  29. package/dist/components/builder-panel.d.ts +189 -0
  30. package/dist/components/builder-preview.d.ts +11 -0
  31. package/dist/components/builder-shell-controls.d.ts +69 -0
  32. package/dist/components/builder-skeleton.d.ts +58 -0
  33. package/dist/components/builder-start.d.ts +75 -0
  34. package/dist/components/builder-workspace-variants.d.ts +23 -0
  35. package/dist/components/captions.d.ts +66 -0
  36. package/dist/{composer-sQ0HcvyG.js → composer-DMI85B6j.js} +1 -1
  37. package/dist/{confirm-card-rQWDQsnC.js → confirm-card-B0YIAwXp.js} +1 -1
  38. package/dist/construct-cli.es.js +3 -3
  39. package/dist/construct.d.ts +5 -0
  40. package/dist/construct.js +327 -0
  41. package/dist/{context-0kg4ZX1J.js → context-DWgfhqWp.js} +1 -1
  42. package/dist/controllable-BagRpyHv.js +1 -0
  43. package/dist/{conversation-item-BxwQLaFJ.js → conversation-item-BsOsVGmT.js} +1 -1
  44. package/dist/{conversation-list-BCfGN1JU.js → conversation-list-aX3Vtc7L.js} +1 -1
  45. package/dist/{copy-n9u9rqpi.js → copy-BmEwsNhJ.js} +1 -1
  46. package/dist/core-C8fzo39E.js +1 -12
  47. package/dist/{create-tween-uPkArjIG.js → create-tween-CNOAU3Fx.js} +1 -1
  48. package/dist/{create-tween-C-q-JCM-.js → create-tween-WfqiENls.js} +1 -1
  49. package/dist/{create-tween-CRrJXxBd.js → create-tween-wlFtW9Kh.js} +1 -1
  50. package/dist/{create-tween-DZ3RRDHD.js → create-tween-zt79ba32.js} +1 -1
  51. package/dist/{default-input-CaMrJkgH.js → default-input-DMV5Eaze.js} +1 -1
  52. package/dist/define-CI0y3eE4.js +1 -0
  53. package/dist/define.js +40 -40
  54. package/dist/define.server.js +45 -45
  55. package/dist/{disclosure-WUImBbDv.js → disclosure-NnACBxsC.js} +1 -1
  56. package/dist/{dropdown-wZVeJOMd.js → dropdown-DT7G0RGR.js} +1 -1
  57. package/dist/elements/agent-card.js +1 -1
  58. package/dist/elements/artifact.js +1 -1
  59. package/dist/elements/attachments.js +1 -1
  60. package/dist/elements/audio-visualizer.js +1 -1
  61. package/dist/elements/avatar.js +1 -1
  62. package/dist/elements/badge.js +1 -1
  63. package/dist/elements/button.js +1 -1
  64. package/dist/elements/card.js +1 -1
  65. package/dist/elements/cards.js +1 -1
  66. package/dist/elements/chain-of-thought.js +1 -1
  67. package/dist/elements/chat-scope-picker.js +1 -1
  68. package/dist/elements/chat-workspace.js +1 -1
  69. package/dist/elements/chat.js +1 -1
  70. package/dist/elements/checkbox-group.js +1 -1
  71. package/dist/elements/checkbox.js +1 -1
  72. package/dist/elements/checkpoint.js +1 -1
  73. package/dist/elements/choice.js +1 -1
  74. package/dist/elements/coachmark.js +1 -1
  75. package/dist/elements/code-block.js +1 -1
  76. package/dist/elements/command.js +1 -1
  77. package/dist/elements/compare.js +1 -1
  78. package/dist/elements/composer.js +1 -1
  79. package/dist/elements/confirm-card.js +1 -1
  80. package/dist/elements/context-meter.js +1 -1
  81. package/dist/elements/conversation-item.js +1 -1
  82. package/dist/elements/conversation-list.js +1 -1
  83. package/dist/elements/dialog.js +1 -1
  84. package/dist/elements/dock.js +1 -1
  85. package/dist/elements/dropdown.js +1 -1
  86. package/dist/elements/editable-label.js +1 -1
  87. package/dist/elements/embed.js +1 -1
  88. package/dist/elements/empty.js +1 -1
  89. package/dist/elements/feedback-bar.js +1 -1
  90. package/dist/elements/file-tree.js +1 -1
  91. package/dist/elements/file-upload.js +1 -1
  92. package/dist/elements/form.js +1 -1
  93. package/dist/elements/hover-card.js +1 -1
  94. package/dist/elements/icon.js +1 -1
  95. package/dist/elements/image.js +1 -1
  96. package/dist/elements/input.js +1 -1
  97. package/dist/elements/kbd.js +1 -1
  98. package/dist/elements/link-preview.js +1 -1
  99. package/dist/elements/loader.js +1 -1
  100. package/dist/elements/markdown.js +1 -1
  101. package/dist/elements/menu.js +1 -1
  102. package/dist/elements/message-skills.js +1 -1
  103. package/dist/elements/message.js +1 -1
  104. package/dist/elements/model-switcher.js +1 -1
  105. package/dist/elements/nav.js +1 -1
  106. package/dist/elements/notice.js +1 -1
  107. package/dist/elements/pane-grid.js +1 -1
  108. package/dist/elements/pane-group.js +1 -1
  109. package/dist/elements/pane.js +1 -1
  110. package/dist/elements/popover.js +1 -1
  111. package/dist/elements/progress-bar.js +1 -1
  112. package/dist/elements/prompt-dock.js +1 -1
  113. package/dist/elements/prompt-input.js +1 -1
  114. package/dist/elements/prompt-suggestions.js +1 -1
  115. package/dist/elements/radio-group.js +1 -1
  116. package/dist/elements/reasoning.js +1 -1
  117. package/dist/elements/remote.js +1 -1
  118. package/dist/elements/resizable.js +1 -1
  119. package/dist/elements/response-stream.js +1 -1
  120. package/dist/elements/screen.js +1 -1
  121. package/dist/elements/scroll-area.js +1 -1
  122. package/dist/elements/scroll-button.js +1 -1
  123. package/dist/elements/search.js +1 -1
  124. package/dist/elements/segmented.js +1 -1
  125. package/dist/elements/select.js +1 -1
  126. package/dist/elements/separator.js +1 -1
  127. package/dist/elements/setting-item.js +1 -1
  128. package/dist/elements/settings-group.js +1 -1
  129. package/dist/elements/skeleton.js +1 -1
  130. package/dist/elements/slider.js +1 -1
  131. package/dist/elements/source.js +1 -1
  132. package/dist/elements/status.js +1 -1
  133. package/dist/elements/switch.js +1 -1
  134. package/dist/elements/tabs.js +1 -1
  135. package/dist/elements/tasks.js +1 -1
  136. package/dist/elements/text-shimmer.js +1 -1
  137. package/dist/elements/thinking-bar.js +1 -1
  138. package/dist/elements/thread.js +1 -1
  139. package/dist/elements/toast.js +1 -1
  140. package/dist/elements/tool.js +1 -1
  141. package/dist/elements/tooltip.js +1 -1
  142. package/dist/elements/voice-input.js +1 -1
  143. package/dist/elements/voice-output.js +1 -1
  144. package/dist/{ellipsis-CbOpJnU3.js → ellipsis-6pJ7SxYT.js} +1 -1
  145. package/dist/{embed-CrfjkbUG.js → embed-BMje-nxj.js} +1 -1
  146. package/dist/engine-javascript-C1x7zo1_.js +1 -141
  147. package/dist/{external-link-ByXAwBBQ.js → external-link-Beh9DKeR.js} +1 -1
  148. package/dist/{file-text-DMib2qwt.js → file-text-D1GPfZ8Q.js} +1 -1
  149. package/dist/{file-tree-CJqM2aP7.js → file-tree-Dwj2lqg-.js} +1 -1
  150. package/dist/{folder-_BAb929C.js → folder-b6D8x1DY.js} +1 -1
  151. package/dist/{form-DSZgZQZk.js → form-DWgdxDXu.js} +1 -1
  152. package/dist/{hover-card-BQbul9eq.js → hover-card-CWRkqm40.js} +1 -1
  153. package/dist/{icon-CdcfRhYI.js → icon-CL0jdhsf.js} +1 -1
  154. package/dist/index.js +1741 -1725
  155. package/dist/index.server.js +1582 -1566
  156. package/dist/{info-DsFjGuI_.js → info-0cOUherQ.js} +1 -1
  157. package/dist/{input-DKbmkMos.js → input-BH6Ru91Q.js} +1 -1
  158. package/dist/kai.es.js +1 -1
  159. package/dist/{kbd-PvdGhX4s.js → kbd-DRAnVtNX.js} +1 -1
  160. package/dist/{link-zF8PzWfF.js → link-Dlr0vssB.js} +1 -1
  161. package/dist/{link-preview-DbM1866N.js → link-preview-DODgRyV9.js} +1 -1
  162. package/dist/loader-KvJB6NrT.js +1 -0
  163. package/dist/{markdown-LMvmAyBk.js → markdown-DX9tuJVj.js} +1 -1
  164. package/dist/mcp.es.js +9 -5
  165. package/dist/{message-BYZECyek.js → message-BqQ7Ot5q.js} +1 -1
  166. package/dist/{message-BSsgvHvf.js → message-DkaGfBd8.js} +1 -1
  167. package/dist/message-circle-BPpp5QKv.js +1 -0
  168. package/dist/message-square-BLbsd9BJ.js +1 -0
  169. package/dist/{minimize-2-BQRoBV1V.js → minimize-2-CyOHJwcN.js} +1 -1
  170. package/dist/{model-switcher-CMpCOYcB.js → model-switcher-0hrKDiK2.js} +1 -1
  171. package/dist/{overlay-Cb-MkyDa.js → overlay-DBMFXie6.js} +1 -1
  172. package/dist/{panel-right-i8warFxT.js → panel-right-t8SWJL6e.js} +1 -1
  173. package/dist/{paperclip-ByY3UVO6.js → paperclip-CdzuOjbU.js} +1 -1
  174. package/dist/play-BXy1u7rW.js +1 -0
  175. package/dist/primitives/use-auto-resize.d.ts +11 -0
  176. package/dist/{progress-bar-jVAHvMUf.js → progress-bar-CiBtxMWT.js} +1 -1
  177. package/dist/{prompt-suggestion-CD-qtNgC.js → prompt-suggestion-I_DHP21i.js} +1 -1
  178. package/dist/{radio-BsoBMhkF.js → radio-C_p3n39l.js} +1 -1
  179. package/dist/{reasoning-4kBPtlTx.js → reasoning-BDwy_tIe.js} +1 -1
  180. package/dist/register-impl-DvEnhIxx.js +424 -0
  181. package/dist/{resizable-uPmY9VY-.js → resizable-SYC-fStM.js} +1 -1
  182. package/dist/{rotate-ccw-B2xn93py.js → rotate-ccw-B1nwmwuY.js} +1 -1
  183. package/dist/{rotate-cw-CMVLoCyH.js → rotate-cw-CLAnBcm1.js} +1 -1
  184. package/dist/{scroll-area-BqLUo8ZZ.js → scroll-area-zRyXoLhI.js} +1 -1
  185. package/dist/{scroll-button-DslXiJyL.js → scroll-button-jzrbYCG9.js} +1 -1
  186. package/dist/{select-2cLRtPGT.js → select-DM-0nEe0.js} +1 -1
  187. package/dist/{separator-BWQtNJcM.js → separator-BToOOoOt.js} +1 -1
  188. package/dist/{settings-BBR63jLg.js → settings-PaGo_S4U.js} +1 -1
  189. package/dist/{settings-group-B2ciy-Iw.js → settings-group-BFXGIfXq.js} +1 -1
  190. package/dist/{skeleton-CkHI-HgC.js → skeleton-DYpmFnnm.js} +1 -1
  191. package/dist/{slider-DDR9U-i0.js → slider-ebutAmkQ.js} +1 -1
  192. package/dist/{solid-DruVOXnf.js → solid-15v_3WVL.js} +3563 -3412
  193. package/dist/{solid-BzCFdL25.js → solid-5P-otgdi.js} +4502 -4296
  194. package/dist/solid.d.ts +6 -0
  195. package/dist/solid.js +259 -254
  196. package/dist/solid.server.js +259 -254
  197. package/dist/{source-GEnFF528.js → source-c1gpRl9h.js} +1 -1
  198. package/dist/{star-CilHq8GP.js → star-pcV_3LqF.js} +1 -1
  199. package/dist/{store-Ciyk6eZG.js → store-CSgbFFCg.js} +1 -1
  200. package/dist/{switch-KkeQIbuH.js → switch-Bh2OdLV2.js} +1 -1
  201. package/dist/{tasks-card-BrN4bggJ.js → tasks-card-CbqQAn2S.js} +1 -1
  202. package/dist/text-shimmer-BSzgEnDH.js +1 -0
  203. package/dist/textarea-DrYNt_0z.js +1 -0
  204. package/dist/theme.tokens.css +22 -21
  205. package/dist/{thumbs-up-CsT9Wzb9.js → thumbs-up-BL3-NenK.js} +1 -1
  206. package/dist/{toast-store-tgK6MuMv.js → toast-store-DkOlYczt.js} +1 -1
  207. package/dist/{tool-DzUjIBT8.js → tool-CeIWUVUe.js} +1 -1
  208. package/dist/{tooltip-Bkkg_4t0.js → tooltip-GA-nmg06.js} +1 -1
  209. package/dist/{trash-2-jBKxjGGQ.js → trash-2-CT66QWlm.js} +1 -1
  210. package/dist/{triangle-alert-DlQ68ppf.js → triangle-alert-CeHLWxhD.js} +1 -1
  211. package/dist/ui/color-field.d.ts +48 -0
  212. package/dist/ui/textarea.d.ts +4 -0
  213. package/dist/ui/toggle-chip.d.ts +40 -0
  214. package/dist/{upload-TcAHXv9W.js → upload-9Csb0F0J.js} +1 -1
  215. package/dist/{use-card-resolution-DaZqAdWf.js → use-card-resolution-Zg0a08Z-.js} +1 -1
  216. package/dist/{variant-aurora-DbW_OWL2.js → variant-aurora-4VBQHIKV.js} +1 -1
  217. package/dist/{variant-aurora-CNmfHhcx.js → variant-aurora-DBeCAQNk.js} +2 -2
  218. package/dist/{variant-aurora-D6GsdTPA.js → variant-aurora-FzHVXHby.js} +2 -2
  219. package/dist/{variant-aurora-BhLuYxJR.js → variant-aurora-LE3fqxgw.js} +1 -1
  220. package/dist/{variant-custom-BvYHR62b.js → variant-custom-AFRGcOBS.js} +1 -1
  221. package/dist/{variant-custom-DV2x254s.js → variant-custom-BQkNuPpE.js} +2 -2
  222. package/dist/{variant-custom-BRvaNHSi.js → variant-custom-DI5R0rln.js} +1 -1
  223. package/dist/{variant-custom-DMTrEO5w.js → variant-custom-WWnrrRHS.js} +2 -2
  224. package/dist/{variant-wave-CyBueXj9.js → variant-wave-DUCbH5oI.js} +2 -2
  225. package/dist/{variant-wave-BNMGrDfj.js → variant-wave-DuQV7DhQ.js} +2 -2
  226. package/dist/{variant-wave-BpDd8t3Z.js → variant-wave-nSI2sfII.js} +1 -1
  227. package/dist/{variant-wave-sp-qwsdI.js → variant-wave-wTP5v_jU.js} +1 -1
  228. package/dist/x-eiO9V6J6.js +1 -0
  229. package/{src/elements/styles.css → kit-base.css} +27 -44
  230. package/package.json +32 -17
  231. package/solid.css +33 -0
  232. package/theme.css +39 -23
  233. package/bin/route.test.js +0 -58
  234. package/dist/arrow-down-CjRU3JgV.js +0 -1
  235. package/dist/arrow-left-Bnpb1UEW.js +0 -1
  236. package/dist/artifact-Bwt7OgcR.js +0 -1
  237. package/dist/check-njkVdZu2.js +0 -1
  238. package/dist/chevron-down-B72s6PXR.js +0 -1
  239. package/dist/chevron-right-BatuHzUm.js +0 -1
  240. package/dist/circle-check-B62pODB3.js +0 -1
  241. package/dist/circle-zx0ByzY9.js +0 -1
  242. package/dist/controllable-CnGpMF6r.js +0 -1
  243. package/dist/define-By-uL588.js +0 -1
  244. package/dist/loader-BM4CEz4G.js +0 -1
  245. package/dist/message-circle-DruwObfX.js +0 -1
  246. package/dist/message-square-BG6q6mlm.js +0 -1
  247. package/dist/play-BvKKq3fX.js +0 -1
  248. package/dist/register-impl-B1hLkrI8.js +0 -424
  249. package/dist/text-shimmer-Dcj1MHuq.js +0 -1
  250. package/dist/textarea-Dpvbngbg.js +0 -1
  251. package/dist/x-Z5UntkIJ.js +0 -1
  252. package/src/agent-tooling/README.md +0 -192
  253. package/src/agent-tooling/archetypes.ts +0 -205
  254. package/src/agent-tooling/catalog/README.md +0 -676
  255. package/src/agent-tooling/catalog/catalog-types.ts +0 -231
  256. package/src/agent-tooling/catalog/fabrications.ts +0 -96
  257. package/src/agent-tooling/catalog/invariants.ts +0 -284
  258. package/src/agent-tooling/catalog/labs-titles.ts +0 -114
  259. package/src/agent-tooling/catalog/scenarios.ts +0 -87
  260. package/src/agent-tooling/catalog/surfaces.ts +0 -347
  261. package/src/agent-tooling/construct/cli-entry.ts +0 -8
  262. package/src/agent-tooling/construct/cli.ts +0 -147
  263. package/src/agent-tooling/construct/codegen.ts +0 -1635
  264. package/src/agent-tooling/construct/construct.v1.schema.json +0 -319
  265. package/src/agent-tooling/construct/dev.ts +0 -148
  266. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +0 -7
  267. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +0 -44
  268. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +0 -32
  269. package/src/agent-tooling/construct/schema.ts +0 -452
  270. package/src/agent-tooling/integrations/anthropic.ts +0 -442
  271. package/src/agent-tooling/integrations/cloudflare.ts +0 -218
  272. package/src/agent-tooling/integrations/langgraph.ts +0 -133
  273. package/src/agent-tooling/integrations/mastra.ts +0 -182
  274. package/src/agent-tooling/integrations/mock.ts +0 -148
  275. package/src/agent-tooling/integrations/ollama.ts +0 -97
  276. package/src/agent-tooling/integrations/openai.ts +0 -95
  277. package/src/agent-tooling/integrations/openrouter.ts +0 -82
  278. package/src/agent-tooling/integrations/pi.ts +0 -176
  279. package/src/agent-tooling/integrations/pydantic-ai.ts +0 -108
  280. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +0 -398
  281. package/src/agent-tooling/mcp/css-raw.d.ts +0 -12
  282. package/src/agent-tooling/mcp/manifest.ts +0 -422
  283. package/src/agent-tooling/mcp/server.ts +0 -116
  284. package/src/agent-tooling/mcp/stdio.ts +0 -18
  285. package/src/agent-tooling/mcp/tools/construct.ts +0 -130
  286. package/src/agent-tooling/mcp/tools/debug.ts +0 -621
  287. package/src/agent-tooling/mcp/tools/reference.ts +0 -980
  288. package/src/agent-tooling/mcp/tools/scaffold.ts +0 -6708
  289. package/src/agent-tooling/mcp/tools/theme.ts +0 -456
  290. package/src/agent-tooling/mcp/tools/types.ts +0 -14
  291. package/src/agent-tooling/mcp/validate-args.ts +0 -141
  292. package/src/agent-tooling/recipes/composed-thread.ts +0 -714
  293. package/src/agent-tooling/recipes/index.ts +0 -22
  294. package/src/agent-tooling/recipes/types.ts +0 -32
  295. package/src/agent-tooling/registry.ts +0 -132
  296. package/src/agent-tooling/route-emit.ts +0 -305
  297. package/src/agent-tooling/types.ts +0 -390
  298. package/src/components/artifact-card.tsx +0 -120
  299. package/src/components/artifact.tsx +0 -914
  300. package/src/components/attachment-types.ts +0 -66
  301. package/src/components/attachments.tsx +0 -536
  302. package/src/components/audio-visualizer/audio-visualizer.voice-fixture.ts +0 -257
  303. package/src/components/audio-visualizer/aurora.glsl.ts +0 -330
  304. package/src/components/audio-visualizer/fit-scale.ts +0 -112
  305. package/src/components/audio-visualizer/index.tsx +0 -528
  306. package/src/components/audio-visualizer/labs/lab-choreography.ts +0 -187
  307. package/src/components/audio-visualizer/labs/lab-shaders.ts +0 -363
  308. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +0 -102
  309. package/src/components/audio-visualizer/shader-canvas.tsx +0 -876
  310. package/src/components/audio-visualizer/sizes.ts +0 -54
  311. package/src/components/audio-visualizer/variant-aurora.tsx +0 -305
  312. package/src/components/audio-visualizer/variant-bar.tsx +0 -175
  313. package/src/components/audio-visualizer/variant-custom.tsx +0 -344
  314. package/src/components/audio-visualizer/variant-grid.tsx +0 -175
  315. package/src/components/audio-visualizer/variant-radial.tsx +0 -163
  316. package/src/components/audio-visualizer/variant-wave.tsx +0 -130
  317. package/src/components/audio-visualizer/wave.glsl.ts +0 -130
  318. package/src/components/card-fallback.tsx +0 -60
  319. package/src/components/card-renderer.tsx +0 -220
  320. package/src/components/card.tsx +0 -212
  321. package/src/components/chain-of-thought.tsx +0 -348
  322. package/src/components/chat-container.tsx +0 -89
  323. package/src/components/chat-scope-picker.tsx +0 -57
  324. package/src/components/chat-thread.tsx +0 -1014
  325. package/src/components/checkpoint.tsx +0 -88
  326. package/src/components/choice-card.tsx +0 -743
  327. package/src/components/coachmark.tsx +0 -249
  328. package/src/components/code-block.tsx +0 -181
  329. package/src/components/composer-dom.ts +0 -159
  330. package/src/components/composer-highlight.ts +0 -242
  331. package/src/components/composer-history.ts +0 -82
  332. package/src/components/composer.tsx +0 -1075
  333. package/src/components/confirm-card.tsx +0 -402
  334. package/src/components/context.tsx +0 -377
  335. package/src/components/conversation-item.tsx +0 -241
  336. package/src/components/conversation-list.tsx +0 -429
  337. package/src/components/conversation-panel.tsx +0 -118
  338. package/src/components/dismissed-stub.tsx +0 -80
  339. package/src/components/embed.tsx +0 -200
  340. package/src/components/empty.tsx +0 -177
  341. package/src/components/feedback-bar.tsx +0 -166
  342. package/src/components/file-tree.tsx +0 -530
  343. package/src/components/file-upload.tsx +0 -167
  344. package/src/components/form-widgets.tsx +0 -525
  345. package/src/components/form.tsx +0 -1296
  346. package/src/components/home-panel.tsx +0 -159
  347. package/src/components/image.tsx +0 -65
  348. package/src/components/link-preview.tsx +0 -195
  349. package/src/components/loader.tsx +0 -367
  350. package/src/components/markdown.tsx +0 -162
  351. package/src/components/message-skills.tsx +0 -40
  352. package/src/components/message.tsx +0 -765
  353. package/src/components/model-switcher.tsx +0 -132
  354. package/src/components/prompt-input.tsx +0 -188
  355. package/src/components/prompt-suggestion.tsx +0 -150
  356. package/src/components/reasoning.tsx +0 -293
  357. package/src/components/response-compare-types.ts +0 -152
  358. package/src/components/response-compare.tsx +0 -455
  359. package/src/components/response-stream.tsx +0 -109
  360. package/src/components/screen.tsx +0 -181
  361. package/src/components/scroll-button.tsx +0 -93
  362. package/src/components/source.tsx +0 -192
  363. package/src/components/tasks-card.tsx +0 -801
  364. package/src/components/text-shimmer.tsx +0 -37
  365. package/src/components/thinking-bar.tsx +0 -50
  366. package/src/components/thread.tsx +0 -228
  367. package/src/components/toast.tsx +0 -503
  368. package/src/components/tool-classify.ts +0 -26
  369. package/src/components/tool-types.ts +0 -49
  370. package/src/components/tool.tsx +0 -190
  371. package/src/components/use-card-resolution.ts +0 -44
  372. package/src/components/voice-input.tsx +0 -222
  373. package/src/components/voice-output.tsx +0 -242
  374. package/src/components/widget-tab-bar.tsx +0 -73
  375. package/src/components/workspace-shell.tsx +0 -285
  376. package/src/diagnostics/hook.ts +0 -352
  377. package/src/diagnostics/index.ts +0 -82
  378. package/src/diagnostics/report-request.ts +0 -296
  379. package/src/elements/agent-card.tsx +0 -67
  380. package/src/elements/artifact.tsx +0 -194
  381. package/src/elements/attachments.tsx +0 -140
  382. package/src/elements/audio-visualizer.tsx +0 -227
  383. package/src/elements/autoloader.ts +0 -89
  384. package/src/elements/avatar.tsx +0 -33
  385. package/src/elements/badge.tsx +0 -32
  386. package/src/elements/button.tsx +0 -149
  387. package/src/elements/card.tsx +0 -133
  388. package/src/elements/cards.tsx +0 -266
  389. package/src/elements/chain-of-thought.tsx +0 -141
  390. package/src/elements/chat-scope-picker.tsx +0 -65
  391. package/src/elements/chat-types.ts +0 -102
  392. package/src/elements/chat-workspace.tsx +0 -188
  393. package/src/elements/chat.tsx +0 -304
  394. package/src/elements/checkbox-group.tsx +0 -164
  395. package/src/elements/checkbox.tsx +0 -134
  396. package/src/elements/checkpoint.tsx +0 -47
  397. package/src/elements/choice.tsx +0 -100
  398. package/src/elements/coachmark.tsx +0 -90
  399. package/src/elements/code-block.tsx +0 -60
  400. package/src/elements/command.tsx +0 -197
  401. package/src/elements/compare.tsx +0 -111
  402. package/src/elements/compiled.css +0 -2
  403. package/src/elements/composer.tsx +0 -162
  404. package/src/elements/confirm-card.tsx +0 -76
  405. package/src/elements/context-meter.tsx +0 -107
  406. package/src/elements/conversation-item.tsx +0 -120
  407. package/src/elements/conversation-list.tsx +0 -203
  408. package/src/elements/css.ts +0 -5
  409. package/src/elements/default-input.tsx +0 -392
  410. package/src/elements/define-entry.ts +0 -12
  411. package/src/elements/define.tsx +0 -563
  412. package/src/elements/diagnostic-events.ts +0 -114
  413. package/src/elements/dialog.tsx +0 -145
  414. package/src/elements/disclosure.ts +0 -95
  415. package/src/elements/dock.tsx +0 -215
  416. package/src/elements/dropdown.tsx +0 -123
  417. package/src/elements/editable-label.tsx +0 -140
  418. package/src/elements/element-data-types.ts +0 -152
  419. package/src/elements/element-diagnostics.ts +0 -392
  420. package/src/elements/element-manifest.json +0 -354
  421. package/src/elements/element-nonscalar.json +0 -187
  422. package/src/elements/element-types.d.ts +0 -4353
  423. package/src/elements/embed.tsx +0 -35
  424. package/src/elements/empty.tsx +0 -29
  425. package/src/elements/feedback-bar.tsx +0 -66
  426. package/src/elements/file-tree.tsx +0 -62
  427. package/src/elements/file-upload.tsx +0 -44
  428. package/src/elements/form.tsx +0 -94
  429. package/src/elements/hover-card.tsx +0 -80
  430. package/src/elements/icon.tsx +0 -41
  431. package/src/elements/image.tsx +0 -32
  432. package/src/elements/input.tsx +0 -492
  433. package/src/elements/kbd.tsx +0 -45
  434. package/src/elements/link-preview.tsx +0 -34
  435. package/src/elements/loader.tsx +0 -25
  436. package/src/elements/markdown.tsx +0 -38
  437. package/src/elements/menu.tsx +0 -241
  438. package/src/elements/message-skills.tsx +0 -83
  439. package/src/elements/message.tsx +0 -361
  440. package/src/elements/model-switcher.tsx +0 -121
  441. package/src/elements/nav.tsx +0 -87
  442. package/src/elements/notice.tsx +0 -53
  443. package/src/elements/pane-grid.tsx +0 -111
  444. package/src/elements/pane-group.tsx +0 -119
  445. package/src/elements/pane.tsx +0 -129
  446. package/src/elements/popover.tsx +0 -81
  447. package/src/elements/progress-bar.tsx +0 -39
  448. package/src/elements/prompt-dock.tsx +0 -82
  449. package/src/elements/prompt-input.tsx +0 -240
  450. package/src/elements/prompt-suggestions.tsx +0 -113
  451. package/src/elements/radio-group.tsx +0 -129
  452. package/src/elements/reasoning.tsx +0 -71
  453. package/src/elements/register-impl.ts +0 -148
  454. package/src/elements/register.ts +0 -76
  455. package/src/elements/remote.tsx +0 -225
  456. package/src/elements/resizable.globals.d.ts +0 -36
  457. package/src/elements/resizable.tsx +0 -704
  458. package/src/elements/response-stream.tsx +0 -40
  459. package/src/elements/screen.tsx +0 -90
  460. package/src/elements/scroll-area.tsx +0 -33
  461. package/src/elements/scroll-button.tsx +0 -179
  462. package/src/elements/search.tsx +0 -185
  463. package/src/elements/segmented.tsx +0 -129
  464. package/src/elements/select.tsx +0 -165
  465. package/src/elements/separator.tsx +0 -36
  466. package/src/elements/setting-item.tsx +0 -50
  467. package/src/elements/settings-group.tsx +0 -37
  468. package/src/elements/skeleton.tsx +0 -45
  469. package/src/elements/slider.tsx +0 -171
  470. package/src/elements/slot-text.ts +0 -73
  471. package/src/elements/slots.ts +0 -787
  472. package/src/elements/source.tsx +0 -126
  473. package/src/elements/status.tsx +0 -42
  474. package/src/elements/switch.tsx +0 -118
  475. package/src/elements/tabs.tsx +0 -95
  476. package/src/elements/tasks.tsx +0 -103
  477. package/src/elements/text-shimmer.tsx +0 -28
  478. package/src/elements/thinking-bar.tsx +0 -43
  479. package/src/elements/thread.tsx +0 -139
  480. package/src/elements/toast.tsx +0 -106
  481. package/src/elements/tool.tsx +0 -78
  482. package/src/elements/tooltip.tsx +0 -85
  483. package/src/elements/validate-messages.ts +0 -76
  484. package/src/elements/voice-input.tsx +0 -100
  485. package/src/elements/voice-output.tsx +0 -82
  486. package/src/index.ts +0 -430
  487. package/src/primitives/audio-bands.ts +0 -174
  488. package/src/primitives/card-component-types.ts +0 -63
  489. package/src/primitives/card-contract.ts +0 -80
  490. package/src/primitives/card-data-types.ts +0 -361
  491. package/src/primitives/card-host.tsx +0 -37
  492. package/src/primitives/card-recovery.ts +0 -160
  493. package/src/primitives/card-registry.tsx +0 -78
  494. package/src/primitives/card-resolution.ts +0 -36
  495. package/src/primitives/card-routing.ts +0 -85
  496. package/src/primitives/card-schemas/artifact.schema.json +0 -92
  497. package/src/primitives/card-schemas/card-envelope.schema.json +0 -14
  498. package/src/primitives/card-schemas/card-event.schema.json +0 -12
  499. package/src/primitives/card-schemas/choice.schema.json +0 -75
  500. package/src/primitives/card-schemas/confirm.schema.json +0 -65
  501. package/src/primitives/card-schemas/embed.schema.json +0 -63
  502. package/src/primitives/card-schemas/form.result.schema.json +0 -7
  503. package/src/primitives/card-schemas/form.schema.json +0 -53
  504. package/src/primitives/card-schemas/link.schema.json +0 -54
  505. package/src/primitives/card-schemas/tasks.result.schema.json +0 -16
  506. package/src/primitives/card-schemas/tasks.schema.json +0 -83
  507. package/src/primitives/card-tags.ts +0 -61
  508. package/src/primitives/card-validate-cards.ts +0 -255
  509. package/src/primitives/card-validate-schemas.ts +0 -58
  510. package/src/primitives/card-validate.ts +0 -131
  511. package/src/primitives/chat-config.tsx +0 -103
  512. package/src/primitives/composer-model.ts +0 -49
  513. package/src/primitives/composer-triggers.ts +0 -35
  514. package/src/primitives/controllable.ts +0 -21
  515. package/src/primitives/conversation-store.ts +0 -248
  516. package/src/primitives/create-kai-chat.ts +0 -66
  517. package/src/primitives/create-tween.ts +0 -266
  518. package/src/primitives/embed-providers.ts +0 -254
  519. package/src/primitives/field-mask.ts +0 -256
  520. package/src/primitives/field-semantics.ts +0 -115
  521. package/src/primitives/highlighter.ts +0 -157
  522. package/src/primitives/input-mask.ts +0 -853
  523. package/src/primitives/link-preview.ts +0 -87
  524. package/src/primitives/message-feedback.ts +0 -124
  525. package/src/primitives/pdf-preview.ts +0 -121
  526. package/src/primitives/toast-store.ts +0 -338
  527. package/src/primitives/url-scheme-policy.ts +0 -70
  528. package/src/primitives/use-audio-analysis.ts +0 -340
  529. package/src/primitives/use-auto-resize.ts +0 -31
  530. package/src/primitives/use-resize-observer.ts +0 -45
  531. package/src/primitives/use-sequencer.ts +0 -72
  532. package/src/primitives/use-speech-recognition.ts +0 -146
  533. package/src/primitives/use-stick-to-bottom.ts +0 -75
  534. package/src/primitives/use-text-stream.ts +0 -143
  535. package/src/primitives/use-voice-recorder.ts +0 -62
  536. package/src/primitives/visualizer-sequences.ts +0 -350
  537. package/src/remote/host-embed.ts +0 -345
  538. package/src/remote/index.ts +0 -2
  539. package/src/remote/origin.ts +0 -30
  540. package/src/remote/provider-runtime.ts +0 -262
  541. package/src/remote/provider.ts +0 -2
  542. package/src/remote/validate.ts +0 -22
  543. package/src/remote/version.ts +0 -12
  544. package/src/remote/wire.ts +0 -48
  545. package/src/schemas/from-tool-call.ts +0 -151
  546. package/src/schemas/index.ts +0 -341
  547. package/src/schemas/provider-subsets.ts +0 -538
  548. package/src/schemas/registry.ts +0 -355
  549. package/src/schemas/tool-defs.ts +0 -793
  550. package/src/solid.ts +0 -173
  551. package/src/state/index.ts +0 -44
  552. package/src/state/messages.ts +0 -53
  553. package/src/state/mock.ts +0 -297
  554. package/src/state/parts.ts +0 -295
  555. package/src/state/persistence.ts +0 -180
  556. package/src/state/stream.ts +0 -246
  557. package/src/state/suggestions.ts +0 -9
  558. package/src/state/threads.ts +0 -142
  559. package/src/test-utils/fake-clock.ts +0 -88
  560. package/src/types.ts +0 -95
  561. package/src/ui/action-icons.ts +0 -53
  562. package/src/ui/agent-card.tsx +0 -218
  563. package/src/ui/avatar.tsx +0 -23
  564. package/src/ui/badge.tsx +0 -21
  565. package/src/ui/button.tsx +0 -49
  566. package/src/ui/card.tsx +0 -245
  567. package/src/ui/checkbox-group.tsx +0 -153
  568. package/src/ui/checkbox.tsx +0 -63
  569. package/src/ui/collapsible.tsx +0 -163
  570. package/src/ui/command.tsx +0 -103
  571. package/src/ui/dialog.tsx +0 -223
  572. package/src/ui/dock.tsx +0 -630
  573. package/src/ui/dropdown.tsx +0 -636
  574. package/src/ui/editable-label.tsx +0 -112
  575. package/src/ui/hover-card.tsx +0 -345
  576. package/src/ui/icon.tsx +0 -167
  577. package/src/ui/input.tsx +0 -391
  578. package/src/ui/kbd.tsx +0 -118
  579. package/src/ui/nav.tsx +0 -282
  580. package/src/ui/notice.tsx +0 -84
  581. package/src/ui/overlay.tsx +0 -277
  582. package/src/ui/pane-grid.tsx +0 -117
  583. package/src/ui/pane-group.tsx +0 -291
  584. package/src/ui/pane.tsx +0 -193
  585. package/src/ui/popover.tsx +0 -106
  586. package/src/ui/progress-bar.tsx +0 -83
  587. package/src/ui/prompt-dock.tsx +0 -145
  588. package/src/ui/radio.tsx +0 -150
  589. package/src/ui/resizable.tsx +0 -693
  590. package/src/ui/scroll-area.tsx +0 -26
  591. package/src/ui/segmented.tsx +0 -109
  592. package/src/ui/select.tsx +0 -168
  593. package/src/ui/separator.tsx +0 -10
  594. package/src/ui/settings-group.tsx +0 -67
  595. package/src/ui/skeleton.tsx +0 -73
  596. package/src/ui/slider.tsx +0 -178
  597. package/src/ui/status.tsx +0 -48
  598. package/src/ui/switch.tsx +0 -119
  599. package/src/ui/tabs.tsx +0 -168
  600. package/src/ui/textarea.tsx +0 -21
  601. package/src/ui/tooltip.tsx +0 -118
  602. package/src/utils/cn.ts +0 -30
  603. package/src/wire/chunk.ts +0 -248
  604. package/src/wire/consume.ts +0 -599
  605. package/src/wire/diagnostics.ts +0 -727
  606. package/src/wire/encode-probe.ts +0 -214
  607. package/src/wire/encode.ts +0 -947
  608. package/src/wire/files.ts +0 -342
  609. package/src/wire/formats/anthropic.ts +0 -274
  610. package/src/wire/formats/openai.ts +0 -228
  611. package/src/wire/index.ts +0 -95
  612. package/src/wire/media-types.ts +0 -344
  613. package/src/wire/read.ts +0 -396
  614. package/src/wire/sink-helpers.ts +0 -50
  615. package/src/wire/sse.ts +0 -153
@@ -1,16 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://kitn.ai/schemas/card/tasks.result.schema.json",
4
- "title": "TasksCardResult",
5
- "description": "Payload of CardEvent { kind: 'submit' } from a tasks card.",
6
- "type": "object",
7
- "required": ["selected"],
8
- "properties": {
9
- "selected": {
10
- "type": "array",
11
- "description": "Ids of the checked tasks, in the order they appear in `tasks`. Subset of the input task ids.",
12
- "items": { "type": "string", "minLength": 1 },
13
- "uniqueItems": true
14
- }
15
- }
16
- }
@@ -1,83 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://kitn.ai/schemas/card/tasks.schema.json",
4
- "title": "TasksCardData",
5
- "description": "Data payload for a `tasks` card (CardEnvelope.data when type='tasks').",
6
- "type": "object",
7
- "required": ["tasks"],
8
- "properties": {
9
- "mode": {
10
- "type": "string",
11
- "enum": ["select", "progress"],
12
- "default": "select",
13
- "description": "'select' (default) = toggle + a confirm button that emits `submit`. 'progress' = an onboarding-checklist look (header `done / total` count, circular indicators, per-item title/description, no confirm button, where checking a row IS the action). Both share the selection model.",
14
- "x-kai-mode": true
15
- },
16
- "heading": {
17
- "type": "string",
18
- "description": "Optional in-body heading; distinct from CardEnvelope.title."
19
- },
20
- "tasks": {
21
- "type": "array",
22
- "minItems": 1,
23
- "description": "The selectable rows, rendered in order.",
24
- "items": {
25
- "type": "object",
26
- "required": ["id", "label"],
27
- "properties": {
28
- "id": {
29
- "type": "string",
30
- "minLength": 1,
31
- "description": "Stable id; the selected ids are returned in the result. Unique within `tasks`.",
32
- "x-kai-unique": true
33
- },
34
- "label": { "type": "string", "minLength": 1, "description": "Row label." },
35
- "description": { "type": "string", "description": "Optional secondary line under the label." },
36
- "checked": {
37
- "type": "boolean",
38
- "default": false,
39
- "description": "Initial checked state of the row."
40
- },
41
- "disabled": {
42
- "type": "boolean",
43
- "default": false,
44
- "description": "Row is shown but not toggleable (and excluded from select-all)."
45
- }
46
- }
47
- }
48
- },
49
- "selectAll": {
50
- "type": "boolean",
51
- "default": false,
52
- "description": "Render a master select-all checkbox above the list.",
53
- "x-kai-control": "select-all"
54
- },
55
- "confirmLabel": {
56
- "type": "string",
57
- "default": "Confirm",
58
- "description": "Label for the confirm button."
59
- },
60
- "allowEmpty": {
61
- "type": "boolean",
62
- "default": false,
63
- "description": "If true, confirm is enabled with zero selected (emits { selected: [] }). If false, confirm is disabled until >=1 selected."
64
- },
65
- "min": {
66
- "type": "integer",
67
- "minimum": 0,
68
- "description": "Optional minimum number that must be selected to confirm.",
69
- "x-kai-select-min": true
70
- },
71
- "max": {
72
- "type": "integer",
73
- "minimum": 1,
74
- "description": "Optional maximum selectable; further toggles are blocked once reached.",
75
- "x-kai-select-max": true
76
- },
77
- "dismissible": {
78
- "type": "boolean",
79
- "default": false,
80
- "description": "Show a close affordance that emits the `dismiss` verb. The card collapses to a re-openable 'dismissed' stub; the host decides via `onReopen` whether it comes back live or `expired`."
81
- }
82
- }
83
- }
@@ -1,61 +0,0 @@
1
- // src/primitives/card-tags.ts
2
- // `CardEnvelope.type` -> the `kai-*` element that renders it. The authoritative map,
3
- // and the ONLY place it is written down.
4
- //
5
- // WHY IT IS ALONE IN A `.ts`
6
- // --------------------------
7
- // It used to live in card-registry.tsx beside `BUILTIN_CARD_COMPONENTS`, which is
8
- // nothing but Solid. The map itself has no Solid in it — it is `Record<string, string>`
9
- // — but sharing a module with the component map meant no Node/no-DOM project could
10
- // import it AS SOURCE, and the `kai` MCP server is exactly that: tsconfig.mcp.json is
11
- // Node-only, `lib: ["ESNext"]`, no `jsx`. So src/agent-tooling/mcp/manifest.ts
12
- // RE-DERIVED each tag by convention (`kai-<type>`, else the single `kai-<type>-*`) —
13
- // a second copy of a fact this repo already held, one that happened to agree.
14
- //
15
- // Relaxing tsconfig.mcp.json instead is not an option and was measured, not guessed:
16
- // giving that project a `jsx` setting drags the Solid component tree into a Node-only
17
- // pass and takes it from 0 errors to 1364. The Node/no-DOM boundary is load-bearing.
18
- // Splitting the data out is the fix; this file is that split.
19
- //
20
- // Because it is plain data with no DOM and no Solid runtime below it,
21
- // `@kitn.ai/ui/schemas` — the server-safe entry a backend route imports — re-exports
22
- // it, so the MCP and any consumer route read the same map instead of guessing at it.
23
- // card-registry.tsx re-exports everything here in turn, so every existing importer of
24
- // `BUILTIN_CARD_TAGS` / `mergeCardTags` / `CardTagMap` is unaffected.
25
- //
26
- // WHAT THIS SPLIT DOES NOT DO, MEASURED
27
- // -------------------------------------
28
- // It does not change dist/schemas.js. Routing that entry's re-export back through
29
- // card-registry.tsx yields a byte-identical bundle with zero `solid-js` references
30
- // that imports clean under `node`: rollup compiles the Solid tree and discards it,
31
- // since only the tag map is reachable. So no runtime guard tells the two apart, and
32
- // this file must not be sold as one. What it changes is the dependency GRAPH — the
33
- // server-safe entry no longer reaches the component tree at all, instead of reaching
34
- // it and counting on tree-shaking to undo that. See src/schemas/index.ts.
35
- //
36
- // Built-ins cover the 7 contract card types; consumers extend/override via a `types`
37
- // prop (merged OVER the built-ins). kai-card (bare shell) is intentionally NOT a target.
38
-
39
- /** Web-component layer: envelope type → kai-* tag name. */
40
- export type CardTagMap = Record<string, string>;
41
-
42
- /**
43
- * The seven built-in card types and the element that draws each one.
44
- *
45
- * Note `link` → `kai-link-preview`: the one entry that is NOT `kai-<type>`, and the
46
- * reason inferring these from the tag list is a guess rather than a lookup.
47
- */
48
- export const BUILTIN_CARD_TAGS: CardTagMap = {
49
- form: 'kai-form',
50
- confirm: 'kai-confirm',
51
- 'tasks': 'kai-tasks',
52
- choice: 'kai-choice',
53
- link: 'kai-link-preview',
54
- embed: 'kai-embed',
55
- artifact: 'kai-artifact',
56
- };
57
-
58
- /** Built-ins with the consumer's overrides merged on top (consumer wins). */
59
- export function mergeCardTags(types?: CardTagMap): CardTagMap {
60
- return types ? { ...BUILTIN_CARD_TAGS, ...types } : { ...BUILTIN_CARD_TAGS };
61
- }
@@ -1,255 +0,0 @@
1
- // src/primitives/card-validate-cards.ts
2
- // Two-tier validation of a CardEnvelope's `data` against the card schema for its
3
- // type, for the NATIVE dispatchers.
4
- //
5
- // "The card schema for its type" means one of the seven built-ins, OR a schema the
6
- // app registered for a card type of its own. The second half arrived with
7
- // `createCardRegistry` (src/schemas/registry.ts) and is the optional third argument
8
- // to `validateCardData`. It exists because a developer's real generative UI is THEIR
9
- // pricing table, not our confirm card, and a validator that covers only the built-ins
10
- // leaves the one card the app actually cares about as the only unchecked one.
11
- //
12
- // THIS MIRRORS src/remote/provider-runtime.ts:139-147, DELIBERATELY.
13
- // -----------------------------------------------------------------
14
- // The remote (iframe) transport has validated every incoming envelope since the card
15
- // contract landed: `validateAgainstSchema(renderer.schema, envelope.data)`, and on
16
- // failure it renders a placeholder and emits `{ kind: 'error', cardId, message }`.
17
- // The native path (the Solid `<CardRenderer>` and `<kai-cards>`) checked only that
18
- // the TYPE was registered, so the same bad payload was caught in an iframe and
19
- // rendered silently in-process. That asymmetry is what this file closes. The two
20
- // transports are one behaviour; if you change the shape of the failure here, change
21
- // it there too, and say so.
22
- //
23
- // WHY THIS IS ENFORCEMENT AND NOT A SAFETY NET
24
- // --------------------------------------------
25
- // The intended design was that `strict: true` tool definitions would constrain the
26
- // model so envelopes arrived valid by construction. Measured against both providers'
27
- // current strict subsets (see src/schemas/provider-subsets.ts), strict mode cannot
28
- // express ANY of the seven card types today: `form`'s free-form `properties` object
29
- // collapses to `{}` under the mandatory `additionalProperties: false`, and the other
30
- // six trip `minLength`/`maxLength`/`format: "uri"`/`allOf`. So the schema guides the
31
- // model and does not constrain it, and this check is the only thing standing between
32
- // a wrong shape and the user's screen.
33
- //
34
- // WHY THE TIERING IS NOT POLITENESS
35
- // ---------------------------------
36
- // This library is pre-1.0 and already in use. Cards that render acceptably today
37
- // must keep rendering, or turning validation on is a behaviour break dressed up as a
38
- // fix. So a failure that means the card has NOTHING to render replaces the card with
39
- // a diagnostic (hard), and a failure that means the card has content which is merely
40
- // out of bounds renders the card and reports (soft). Both emit `{ kind: 'error' }`,
41
- // because the developer needs to hear about both; only one changes what the user sees.
42
- //
43
- // THE SOFT TIER IS ON BY EVIDENCE, NOT BY CONFIDENCE
44
- // --------------------------------------------------
45
- // The plan (2026-08-11-emit-contract.md §9 risk 4) said validation is net-negative if
46
- // it fires on cards that render fine today, and set the bar at ZERO before the soft
47
- // tier could default on. That was measured, not assumed:
48
- // docs/superpowers/specs/2026-08-12-soft-tier-corpus.md.
49
- //
50
- // 35 card envelopes recorded from five model configurations across both wires, six
51
- // card types. ZERO trip the soft tier; zero trip the hard tier. The check was proven
52
- // live on that same path by seven negative controls (real envelopes mutated into each
53
- // tier's named failure, each required to trip), and a fifth of the envelopes were
54
- // confirmed by replaying them through the real app and reading them back off
55
- // `<kai-thread>.messages`. Margins were wide, not marginal: the worst `link.title`
56
- // observed was 21 characters against a 300 limit.
57
- //
58
- // What that corpus does NOT cover, so it is not claimed: the structured-output path
59
- // (model-authored envelope JSON, which nothing clamps), live `artifact` cards, and
60
- // the soft constraints the spike's own tool projection clamps before they can be hit
61
- // (`tone`/`provider` enums, the generated `id` fields, every `minItems`). If the soft
62
- // tier ever does turn out to be noisy, that is where to look first, and the fix is the
63
- // projection rather than the tier.
64
- //
65
- // WHAT IS ACTUALLY CHECKED
66
- // ------------------------
67
- // The keyword coverage is `validateAgainstSchema`'s, which is a lean subset: `type`,
68
- // `const`, `enum`, `required`, `properties`, `items`, the numeric bounds,
69
- // `minLength`/`maxLength`/`pattern`, `minItems`/`maxItems`/`uniqueItems`. It has no
70
- // applicators and no `additionalProperties`. The consequences are written out per
71
- // keyword in scripts/gen-card-validation-schemas.mjs's NOT_ENFORCED table, and the
72
- // generated projection repeats them at the top of card-validate-schemas.ts. The
73
- // short version, because it bears on what may be claimed: an `embed` missing both
74
- // `id` and `url`, an `artifact` with neither `src` nor `files`, a `link.url` that is
75
- // not a URL, and an undeclared extra property are all NOT caught here.
76
-
77
- import { validateAgainstSchema, type JsonSchema, type ValidationIssue } from './card-validate';
78
- import { CARD_VALIDATION_SCHEMAS, type ValidatedCardType } from './card-validate-schemas';
79
-
80
- export type { ValidatedCardType } from './card-validate-schemas';
81
- export { CARD_VALIDATION_SCHEMAS } from './card-validate-schemas';
82
-
83
- /** How badly a failed constraint hurts the rendered card. */
84
- export type CardValidationTier = 'hard' | 'soft';
85
-
86
- export interface CardValidationIssue extends ValidationIssue {
87
- tier: CardValidationTier;
88
- }
89
-
90
- export interface CardValidationReport {
91
- /** True when nothing failed. A SOFT-only report is not `ok`. */
92
- ok: boolean;
93
- /** `hard` if any issue is hard, else `soft` if any issue is soft, else `ok`. */
94
- tier: CardValidationTier | 'ok';
95
- /** Every failure, hard and soft, in the validator's order. */
96
- issues: CardValidationIssue[];
97
- /** The hard issues only. Non-empty exactly when `tier === 'hard'`. */
98
- hard: CardValidationIssue[];
99
- /** One line naming the failing field paths. Safe to render and to log. */
100
- summary: string;
101
- }
102
-
103
- /**
104
- * Keyword to tier.
105
- *
106
- * The line is PRESENCE versus BOUNDS, and it is drawn per keyword rather than per
107
- * card because the dispatcher does not know what any given card component does with
108
- * a field.
109
- *
110
- * hard the field the card is built around is absent, or is a different kind of
111
- * thing entirely. There is nothing to draw.
112
- * soft the field is present and of the right kind; there is just too much of it,
113
- * too little, or it is outside a declared range. Something renders.
114
- *
115
- * THE TWO ENTRIES THAT ARE JUDGEMENT CALLS, RECORDED BECAUSE THEY ARE ARGUABLE:
116
- *
117
- * `minItems` is HARD while `minLength` is SOFT, and they look like the same shape.
118
- * The difference is what is lost. `confirm.actions` has `minItems: 1`, so violating
119
- * it means a decision card with no decisions: the entire interactive surface is
120
- * gone, and what renders is empty chrome that looks like a bug in this library. The
121
- * plan names that case specifically as the one the diagnostic exists for. Violating
122
- * `minLength: 1` on `actions[].label` means ONE blank button label; the button is
123
- * still there, still focusable, and still emits the correct `id`. Replacing a whole
124
- * card with a diagnostic over one blank label is the worse outcome for the user.
125
- *
126
- * `type` is HARD everywhere, including on optional scalars where a truthy wrong type
127
- * would have rendered fine (`dismissible: "yes"`). That is deliberately blunt. A
128
- * per-field carve-out would need the dispatcher to know which fields each card
129
- * component treats loosely, which it cannot, and a wrong `type` is the single
130
- * strongest signal that the model produced something other than what was asked for.
131
- */
132
- const TIER_BY_KEYWORD: Readonly<Record<string, CardValidationTier>> = Object.freeze({
133
- // hard: nothing to render
134
- type: 'hard',
135
- required: 'hard',
136
- const: 'hard', // pins a discriminator (`form.data.type` must be `"object"`)
137
- minItems: 'hard',
138
- // soft: content is present, bounds are not respected
139
- enum: 'soft', // cards fall back to their default variant for an unknown value
140
- maxItems: 'soft',
141
- minLength: 'soft',
142
- maxLength: 'soft',
143
- pattern: 'soft',
144
- minimum: 'soft',
145
- maximum: 'soft',
146
- exclusiveMinimum: 'soft',
147
- exclusiveMaximum: 'soft',
148
- uniqueItems: 'soft',
149
- });
150
-
151
- /**
152
- * The tier for a keyword the table does not list.
153
- *
154
- * SOFT, on purpose. An unlisted keyword can only appear by someone adding a check to
155
- * `validateAgainstSchema` and not tiering it, and the failure mode of guessing wrong
156
- * is asymmetric: guessing soft under-reacts on one new keyword until someone tiers
157
- * it, while guessing hard would start blanking cards that render fine today, which
158
- * is precisely the regression this design exists to prevent. `card-validate-cards.test.ts`
159
- * asserts the table covers every keyword the validator implements, so this default
160
- * is a fallback for a tree that is already failing its own test, not a resting state.
161
- */
162
- const DEFAULT_TIER: CardValidationTier = 'soft';
163
-
164
- /** True when this envelope type has a BUILT-IN projected schema to validate against. */
165
- export function isValidatedCardType(type: string): type is ValidatedCardType {
166
- return Object.prototype.hasOwnProperty.call(CARD_VALIDATION_SCHEMAS, type);
167
- }
168
-
169
- /**
170
- * The schema to check `type` against, consumer-supplied first.
171
- *
172
- * A consumer schema WINS over a built-in of the same name, matching
173
- * `mergeCardTags`/`mergeCardComponents`, where the consumer's entry is spread over
174
- * ours. Anything else would mean a developer who replaced our confirm card with their
175
- * own still had their data checked against our shape.
176
- */
177
- function schemaFor(
178
- type: string,
179
- extra: Readonly<Record<string, JsonSchema>> | undefined,
180
- ): JsonSchema | null {
181
- if (extra && Object.prototype.hasOwnProperty.call(extra, type)) {
182
- const custom = extra[type];
183
- if (custom && typeof custom === 'object') return custom;
184
- }
185
- return isValidatedCardType(type) ? CARD_VALIDATION_SCHEMAS[type] : null;
186
- }
187
-
188
- function summarize(issues: CardValidationIssue[]): string {
189
- // Name the FIELD PATHS. "invalid card data" on its own sends a developer to a
190
- // debugger; "(root).actions: fewer than minItems 1" sends them to the line.
191
- const shown = issues.slice(0, 3).map((i) => i.message);
192
- const rest = issues.length - shown.length;
193
- return shown.join('; ') + (rest > 0 ? `; and ${rest} more` : '');
194
- }
195
-
196
- /**
197
- * Validate a card envelope's `data` against the schema for `type`.
198
- *
199
- * Returns `null` when there is no schema for the type at all. Returning `null` rather
200
- * than an empty pass keeps "we checked and it was fine" distinguishable from "there
201
- * was nothing to check", so a caller cannot report the second as the first.
202
- *
203
- * @param schemas consumer-registered schemas by card type, normally
204
- * `registry.validationSchemas` from `createCardRegistry`. Without it this function
205
- * knows only the seven built-ins, which would mean a developer's own pricing-table
206
- * card, registered perfectly legally through `cardTypes`, is the one card in the app
207
- * nothing checks. The custom entry wins over a built-in of the same name; see
208
- * {@link schemaFor}.
209
- *
210
- * A consumer schema is used AS AUTHORED rather than through the build-time lean
211
- * projection the built-ins get, because that projection is a bundle-size measure and
212
- * cannot run over a schema this package has never seen. The practical difference is
213
- * only weight: `validateAgainstSchema` ignores every keyword it does not implement, so
214
- * a custom schema is checked to exactly the same depth as a built-in, with the same
215
- * NOT_ENFORCED gaps (no `allOf`/`anyOf`/`oneOf`/`if`, no `$ref`, no
216
- * `additionalProperties`, no `format`).
217
- */
218
- export function validateCardData(
219
- type: string,
220
- data: unknown,
221
- schemas?: Readonly<Record<string, JsonSchema>>,
222
- ): CardValidationReport | null {
223
- const schema = schemaFor(type, schemas);
224
- if (schema === null) return null;
225
- const result = validateAgainstSchema(schema, data);
226
- if (result.valid) return { ok: true, tier: 'ok', issues: [], hard: [], summary: '' };
227
-
228
- const issues: CardValidationIssue[] = result.issues.map((issue) => ({
229
- ...issue,
230
- tier: TIER_BY_KEYWORD[issue.keyword] ?? DEFAULT_TIER,
231
- }));
232
- const hard = issues.filter((i) => i.tier === 'hard');
233
- return {
234
- ok: false,
235
- tier: hard.length > 0 ? 'hard' : 'soft',
236
- issues,
237
- hard,
238
- summary: summarize(hard.length > 0 ? hard : issues),
239
- };
240
- }
241
-
242
- /**
243
- * The message both native dispatchers put on the `error` CardEvent.
244
- *
245
- * Spelled once, here, and prefixed `invalid card data:` to match what
246
- * provider-runtime.ts already emits for the remote transport, so a consumer's
247
- * `policy.onError` sees the same string shape regardless of which transport the card
248
- * arrived through.
249
- */
250
- export function cardValidationMessage(report: CardValidationReport): string {
251
- return `invalid card data (${report.tier}): ${report.summary}`;
252
- }
253
-
254
- /** Exposed for the tier-coverage test, which asserts it against the validator. */
255
- export const TIERS_BY_KEYWORD = TIER_BY_KEYWORD;
@@ -1,58 +0,0 @@
1
- // GENERATED by scripts/gen-card-validation-schemas.mjs. Do not edit by hand.
2
- //
3
- // Run `node scripts/gen-card-validation-schemas.mjs` (or any build, which does it in
4
- // `prebuild`) to regenerate. `card-validate-schemas.sync.test.ts` fails if this file
5
- // and the authored schemas in src/primitives/card-schemas/ disagree.
6
- //
7
- // WHAT THIS IS
8
- // ------------
9
- // The 7 card-data schemas with everything the browser cannot act on removed:
10
- // `$schema`, `$id`, `title`, `description`, `default`, every `x-*` hint, and every
11
- // keyword `validateAgainstSchema` does not implement. The authored documents remain
12
- // the source of truth and ship unchanged at `@kitn.ai/ui/schemas` for the server.
13
- //
14
- // ENFORCED here, read out of validateAgainstSchema's body by the generator:
15
- // const, enum, exclusiveMaximum, exclusiveMinimum, items, maxItems, maxLength, maximum, minItems, minLength, minimum, pattern, properties, required, type, uniqueItems
16
- //
17
- // NOT ENFORCED, and therefore NOT in this projection. Each is a real constraint the
18
- // authored schema makes that nothing checks at render time:
19
- // `additionalProperties`: unknown properties are NOT rejected. `artifact`, `embed` and `link` set `additionalProperties: false`; a model adding a field they do not declare renders exactly as it does today. Implementing it would make every extra field a validation failure on cards that render fine, which is the regression the tiering exists to avoid.
20
- // `allOf`: conditional requirements are NOT checked. This is `embed`'s entire provider/url rule (generic needs `url`; youtube/vimeo need `id` or `url`), so an embed card missing both validates here and renders an empty player. Fixing it means implementing applicators, not widening this table.
21
- // `anyOf`: branch alternatives are NOT checked. This is `artifact`'s root `[{required:["src"]},{required:["files"]}]` rule, so an artifact card carrying NEITHER a `src` nor a `files` list validates here and renders empty chrome. It is also half of `embed`'s youtube/vimeo `id`-or-`url` rule.
22
- // `oneOf`: exactly-one-of is NOT checked. `artifact.height` is `oneOf: [number, string]`, and because that subschema has no other keyword it drops out of the projection entirely: a `height` of `true` or `{}` is not caught.
23
- // `if`: the conditional applicator is NOT evaluated; see `allOf`.
24
- // `then`: the conditional consequent is NOT evaluated; see `allOf`.
25
- // `else`: the conditional alternative is NOT evaluated; see `allOf`.
26
- // `not`: negation is NOT evaluated; a value the schema explicitly forbids validates here. Unused by any card schema today.
27
- // `format`: string formats are NOT checked. `link`, `embed` and `artifact` use `format: "uri"`; a non-URL string in `link.url` validates here. The cards themselves guard what they will actually navigate to or frame, which is where a URL check belongs (a validator that only warns cannot stop a bad origin loading).
28
- // `$ref`: references are NOT resolved; a `$ref`-ed subschema is not checked at all. No card schema uses one today.
29
- // `$defs`: definition blocks are NOT reachable without `$ref`, so their contents are never checked.
30
- // `definitions`: the draft-07 spelling of `$defs`; its contents are NOT checked either, for the same reason. Listed separately so a schema written in the older dialect cannot slip past this table.
31
- import type { JsonSchema } from './card-validate';
32
-
33
- /** The built-in `CardEnvelope.type` values that have a schema to validate against. */
34
- export type ValidatedCardType = "artifact" | "choice" | "confirm" | "embed" | "form" | "link" | "tasks";
35
-
36
- /**
37
- * One widening cast, at the module boundary, for the same reason
38
- * src/schemas/index.ts has one: TypeScript infers `string` for a `"type"` member
39
- * written as a literal in an object literal, while `JsonSchema['type']` is a union
40
- * of string literals, so these documents are not assignable without it. The
41
- * alternative (`as const` throughout) makes every `required` array `readonly`,
42
- * which `JsonSchema` does not accept either.
43
- *
44
- * The cast is not a claim about the contents. What it would otherwise hide is
45
- * checked by the sync test, which re-runs this generator and compares byte for byte.
46
- */
47
- export const CARD_VALIDATION_SCHEMAS = {
48
- "artifact": {"type":"object","properties":{"src":{"type":"string"},"files":{"type":"array","maxItems":500,"items":{"type":"object","required":["path"],"properties":{"path":{"type":"string","maxLength":1024},"url":{"type":"string"},"code":{"type":"string"},"language":{"type":"string","maxLength":40},"type":{"type":"string","enum":["html","pdf","image","other"]},"additions":{"type":"integer","minimum":0},"deletions":{"type":"integer","minimum":0},"status":{"type":"string","enum":["added","modified","deleted","renamed","untracked"]}}}},"tab":{"type":"string","enum":["preview","code"]},"activeFile":{"type":"string","maxLength":1024},"displayUrl":{"type":"string","maxLength":2048}}},
49
- "choice": {"type":"object","required":["options"],"properties":{"prompt":{"type":"string"},"submitLabel":{"type":"string"},"options":{"type":"array","minItems":1,"items":{"type":"object","required":["id","label"],"properties":{"id":{"type":"string","minLength":1},"label":{"type":"string","minLength":1},"description":{"type":"string"},"media":{"type":"object","properties":{"image":{"type":"string"},"imageAlt":{"type":"string"},"icon":{"type":"string"}}},"meta":{"type":"string"},"recommended":{"type":"boolean"},"disabled":{"type":"boolean"}}}},"dismissible":{"type":"boolean"}}},
50
- "confirm": {"type":"object","required":["actions"],"properties":{"heading":{"type":"string"},"body":{"type":"string"},"tone":{"type":"string","enum":["default","warning","danger"]},"actions":{"type":"array","minItems":1,"maxItems":4,"items":{"type":"object","required":["id","label"],"properties":{"id":{"type":"string","minLength":1},"label":{"type":"string","minLength":1},"style":{"type":"string","enum":["primary","default","destructive"]},"default":{"type":"boolean"}}}},"dismissible":{"type":"boolean"}}},
51
- "embed": {"type":"object","required":["provider"],"properties":{"provider":{"type":"string","enum":["youtube","vimeo","generic"]},"id":{"type":"string","maxLength":64,"pattern":"^[A-Za-z0-9_-]+$"},"url":{"type":"string"},"title":{"type":"string","maxLength":300},"poster":{"type":"string"},"start":{"type":"integer","minimum":0},"aspectRatio":{"type":"string","enum":["16:9","4:3","1:1","9:16"]}}},
52
- "form": {"type":"object","required":["type","properties"],"properties":{"type":{"const":"object"},"title":{"type":"string"},"description":{"type":"string"},"required":{"type":"array","items":{"type":"string"}},"properties":{"type":"object"},"x-kai-order":{"type":"array","items":{"type":"string"}},"x-kai-inlineMax":{"type":"integer","minimum":1},"x-kai-submitLabel":{"type":"string"},"x-kai-dismissible":{"type":"boolean"},"x-kai-actions":{"type":"array","items":{"type":"object","required":["id","label"],"properties":{"id":{"type":"string"},"label":{"type":"string"},"variant":{"enum":["default","ghost","outline"]}}}}}},
53
- "link": {"type":"object","required":["url"],"properties":{"url":{"type":"string"},"title":{"type":"string","maxLength":300},"description":{"type":"string","maxLength":1000},"image":{"type":"string"},"imageAlt":{"type":"string","maxLength":300},"favicon":{"type":"string"},"domain":{"type":"string","maxLength":253},"siteName":{"type":"string","maxLength":200}}},
54
- "tasks": {"type":"object","required":["tasks"],"properties":{"mode":{"type":"string","enum":["select","progress"]},"heading":{"type":"string"},"tasks":{"type":"array","minItems":1,"items":{"type":"object","required":["id","label"],"properties":{"id":{"type":"string","minLength":1},"label":{"type":"string","minLength":1},"description":{"type":"string"},"checked":{"type":"boolean"},"disabled":{"type":"boolean"}}}},"selectAll":{"type":"boolean"},"confirmLabel":{"type":"string"},"allowEmpty":{"type":"boolean"},"min":{"type":"integer","minimum":0},"max":{"type":"integer","minimum":1},"dismissible":{"type":"boolean"}}},
55
- } as unknown as Readonly<Record<ValidatedCardType, JsonSchema>>;
56
-
57
- /** The card types this projection covers, in a stable order. */
58
- export const VALIDATED_CARD_TYPES: readonly ValidatedCardType[] = ["artifact","choice","confirm","embed","form","link","tasks"];
@@ -1,131 +0,0 @@
1
- // src/primitives/card-validate.ts
2
- // The single shared lean JSON-Schema validator the contract mandates. Covers the
3
- // subset cards use; `x-*` keywords (incl. x-kai-*) are ignored. No ajv. Used at
4
- // every boundary (incoming card data, outgoing payloads) by cards + both transports.
5
-
6
- export interface JsonSchema {
7
- type?: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object' | 'null';
8
- const?: unknown;
9
- // `readonly`: a construct's card schema is emitted `as const` (codegen.ts,
10
- // emitCardsRegistry) so its literal types survive into CardTools' input —
11
- // that turns array-valued fields into readonly tuples, which a mutable
12
- // `T[]` does not structurally accept even under a `Readonly<Record<...>>`
13
- // wrapper. JsonSchema is read-only data everywhere it is consumed (no
14
- // caller mutates `.required`/`.enum` in place — see the callers in
15
- // tool-defs.ts and form.tsx, which all build a fresh array via spread/
16
- // filter/Set before assigning), so widening these two costs nothing.
17
- enum?: readonly unknown[];
18
- required?: readonly string[];
19
- properties?: Record<string, JsonSchema>;
20
- items?: JsonSchema;
21
- minimum?: number; maximum?: number;
22
- exclusiveMinimum?: number; exclusiveMaximum?: number;
23
- minLength?: number; maxLength?: number;
24
- pattern?: string;
25
- minItems?: number; maxItems?: number;
26
- uniqueItems?: boolean;
27
- // x-* keywords (e.g. x-kai-widget) are allowed and ignored.
28
- [key: `x-${string}`]: unknown;
29
- }
30
-
31
- /**
32
- * One failed constraint, with the KEYWORD that failed kept separate from the prose.
33
- *
34
- * `errors` (the string form) is what every existing caller reads and it is unchanged.
35
- * This structured twin exists because the native card dispatcher has to TIER a
36
- * failure (a missing `required` field means the card cannot render, a `maxItems`
37
- * overrun means it renders five buttons instead of four), and the only honest way to
38
- * tell those apart is the keyword. Re-parsing the prose to recover it would be a
39
- * second, silently-drifting copy of the same fact.
40
- */
41
- export interface ValidationIssue {
42
- /** Where, in readable form: `(root)`, `(root).actions`, `(root).actions[0].id`. */
43
- path: string;
44
- /** The JSON Schema keyword that failed. `required` for a missing property. */
45
- keyword: string;
46
- /** The human-readable message; identical to the matching entry in `errors`. */
47
- message: string;
48
- }
49
-
50
- export interface ValidationResult {
51
- valid: boolean;
52
- errors: string[];
53
- /**
54
- * The same failures as `errors`, one-for-one and in the same order, with the
55
- * keyword and path kept as data. Added alongside `errors` rather than replacing
56
- * it so no existing caller changes.
57
- */
58
- issues: ValidationIssue[];
59
- }
60
-
61
- function typeOf(v: unknown): string {
62
- if (v === null) return 'null';
63
- if (Array.isArray(v)) return 'array';
64
- return typeof v;
65
- }
66
-
67
- function matchesType(v: unknown, t: NonNullable<JsonSchema['type']>): boolean {
68
- switch (t) {
69
- case 'integer': return typeof v === 'number' && Number.isInteger(v);
70
- case 'number': return typeof v === 'number' && Number.isFinite(v);
71
- case 'array': return Array.isArray(v);
72
- case 'null': return v === null;
73
- case 'object': return typeOf(v) === 'object';
74
- default: return typeof v === t;
75
- }
76
- }
77
-
78
- function walk(schema: JsonSchema, value: unknown, path: string, issues: ValidationIssue[]): void {
79
- const at = path || '(root)';
80
- const fail = (keyword: string, message: string, where = at): void => {
81
- issues.push({ path: where, keyword, message: `${where}: ${message}` });
82
- };
83
- if (schema.type && !matchesType(value, schema.type)) {
84
- fail('type', `expected ${schema.type}, got ${typeOf(value)}`);
85
- return; // type wrong → downstream checks are meaningless
86
- }
87
- if ('const' in schema && JSON.stringify(value) !== JSON.stringify(schema.const)) {
88
- fail('const', `must equal const ${JSON.stringify(schema.const)}`);
89
- }
90
- if (schema.enum && !schema.enum.some((e) => JSON.stringify(e) === JSON.stringify(value))) {
91
- fail('enum', `must be one of ${JSON.stringify(schema.enum)}`);
92
- }
93
- if (typeof value === 'number') {
94
- if (schema.minimum !== undefined && value < schema.minimum) fail('minimum', `< minimum ${schema.minimum}`);
95
- if (schema.maximum !== undefined && value > schema.maximum) fail('maximum', `> maximum ${schema.maximum}`);
96
- if (schema.exclusiveMinimum !== undefined && value <= schema.exclusiveMinimum) fail('exclusiveMinimum', '<= exclusiveMinimum');
97
- if (schema.exclusiveMaximum !== undefined && value >= schema.exclusiveMaximum) fail('exclusiveMaximum', '>= exclusiveMaximum');
98
- }
99
- if (typeof value === 'string') {
100
- if (schema.minLength !== undefined && value.length < schema.minLength) fail('minLength', `shorter than minLength ${schema.minLength}`);
101
- if (schema.maxLength !== undefined && value.length > schema.maxLength) fail('maxLength', `longer than maxLength ${schema.maxLength}`);
102
- if (schema.pattern !== undefined && !new RegExp(schema.pattern).test(value)) fail('pattern', 'does not match pattern');
103
- }
104
- if (Array.isArray(value)) {
105
- if (schema.minItems !== undefined && value.length < schema.minItems) fail('minItems', `fewer than minItems ${schema.minItems}`);
106
- if (schema.maxItems !== undefined && value.length > schema.maxItems) fail('maxItems', `more than maxItems ${schema.maxItems}`);
107
- if (schema.uniqueItems) {
108
- const seen = new Set(value.map((v) => JSON.stringify(v)));
109
- if (seen.size !== value.length) fail('uniqueItems', 'items not unique');
110
- }
111
- if (schema.items) value.forEach((v, i) => walk(schema.items!, v, `${at}[${i}]`, issues));
112
- }
113
- if (typeOf(value) === 'object') {
114
- const obj = value as Record<string, unknown>;
115
- for (const key of schema.required ?? []) {
116
- if (!(key in obj) || obj[key] === undefined) fail('required', 'required', `${at}.${key}`);
117
- }
118
- if (schema.properties) {
119
- for (const [key, sub] of Object.entries(schema.properties)) {
120
- if (key in obj && obj[key] !== undefined) walk(sub, obj[key], `${at}.${key}`, issues);
121
- }
122
- }
123
- }
124
- }
125
-
126
- /** Validate `value` against the lean JSON-Schema subset. */
127
- export function validateAgainstSchema(schema: JsonSchema, value: unknown): ValidationResult {
128
- const issues: ValidationIssue[] = [];
129
- walk(schema, value, '', issues);
130
- return { valid: issues.length === 0, errors: issues.map((i) => i.message), issues };
131
- }