@kitn.ai/ui 0.25.2 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (603) hide show
  1. package/README.md +17 -6
  2. package/bin/mcp.js +16 -3
  3. package/bin/route.js +23 -0
  4. package/bin/route.test.js +58 -0
  5. package/dist/{elements/chunks/Icon-b5A8hlHs.js → Icon-BnFKqqtY.js} +1 -1
  6. package/dist/action-icons-CBskfBMz.js +1 -0
  7. package/dist/arrow-down-Br0qtpsz.js +1 -0
  8. package/dist/arrow-left-Dj7xgE12.js +1 -0
  9. package/dist/artifact-CvXEm1lS.js +1 -0
  10. package/dist/assets/dev-5XzXYR21.js +91 -0
  11. package/dist/attachments-C43ZegEq.js +1 -0
  12. package/dist/audio-visualizer-B-79y1Tj.js +1 -0
  13. package/dist/badge-BpUbq8c0.js +1 -0
  14. package/dist/bash-InADTalH.js +1 -6
  15. package/dist/bell-sn17Ip9v.js +1 -0
  16. package/dist/{elements/chunks/button-B9okqG73.js → button-BH6rN0U-.js} +1 -1
  17. package/dist/card-renderer-Cb1gQmth.js +1 -0
  18. package/dist/card-routing-DifQgS7n.js +1 -0
  19. package/dist/check-DPeeMUYx.js +1 -0
  20. package/dist/checkbox-Dc9Yov_B.js +1 -0
  21. package/dist/checkbox-group-B1kDLLYk.js +1 -0
  22. package/dist/chevron-down-CHSWk7ZT.js +1 -0
  23. package/dist/chevron-right-B7l_fMgx.js +1 -0
  24. package/dist/choice-card-BT3c1S8g.js +1 -0
  25. package/dist/circle-Bb0yOpqM.js +1 -0
  26. package/dist/circle-check-BGZpCiz7.js +1 -0
  27. package/dist/circle-x-wobWkoSx.js +1 -0
  28. package/dist/{elements/chunks/cn-CU7UAWzk.js → cn-DN5AWfiS.js} +1 -1
  29. package/dist/code-block-CUQ5mtv0.js +1 -0
  30. package/dist/collapsible-CPJbf6FM.js +1 -0
  31. package/dist/components/attachment-types.d.ts +53 -1
  32. package/dist/components/attachments.d.ts +31 -0
  33. package/dist/components/audio-visualizer/fit-scale.d.ts +50 -0
  34. package/dist/components/audio-visualizer/index.d.ts +15 -1
  35. package/dist/components/audio-visualizer/variant-bar.d.ts +24 -1
  36. package/dist/components/card-renderer.d.ts +13 -0
  37. package/dist/components/card.d.ts +4 -0
  38. package/dist/components/chat-thread.d.ts +49 -42
  39. package/dist/components/code-block.d.ts +22 -0
  40. package/dist/components/conversation-item.d.ts +66 -1
  41. package/dist/components/conversation-list.d.ts +77 -0
  42. package/dist/components/form-widgets.d.ts +67 -2
  43. package/dist/components/form.d.ts +89 -0
  44. package/dist/components/message.d.ts +29 -0
  45. package/dist/components/reasoning.d.ts +6 -0
  46. package/dist/components/scroll-button.d.ts +11 -0
  47. package/dist/components/thread.d.ts +5 -0
  48. package/dist/components/toast.d.ts +2 -1
  49. package/dist/components/tool-types.d.ts +9 -0
  50. package/dist/components/voice-input.d.ts +8 -0
  51. package/dist/components/voice-output.d.ts +12 -1
  52. package/dist/components/workspace-shell.d.ts +87 -0
  53. package/dist/composer-CjngLzal.js +106 -0
  54. package/dist/confirm-card-DCx_A4bU.js +1 -0
  55. package/dist/construct-cli.es.js +1169 -0
  56. package/dist/context-BY8V-Nlp.js +1 -0
  57. package/dist/controllable-D43BKmtX.js +1 -0
  58. package/dist/conversation-list-D4lLU_yS.js +1 -0
  59. package/dist/copy-B6DO4PqB.js +1 -0
  60. package/dist/core-AYMC6_lb.js +12 -5874
  61. package/dist/{create-tween-CUzOlaMS.js → create-tween-BPzaefTp.js} +1 -1
  62. package/dist/{create-tween-B5Y4Im8G.js → create-tween-CnMHviKB.js} +1 -1
  63. package/dist/{create-tween-4z1XYLkR.js → create-tween-D1aiGZZh.js} +1 -1
  64. package/dist/{elements/chunks/create-tween-CUrM6D_o.js → create-tween-DUSQUKpr.js} +2 -2
  65. package/dist/css-M7EaDHN_.js +1 -6
  66. package/dist/custom-elements.json +3468 -1927
  67. package/dist/default-input-CucQEOqH.js +1 -0
  68. package/dist/define-CGGX-7Vg.js +1 -0
  69. package/dist/define.d.ts +4 -0
  70. package/dist/define.js +510 -0
  71. package/dist/define.server.d.ts +5 -0
  72. package/dist/define.server.js +506 -0
  73. package/dist/diagnostics/hook.d.ts +81 -0
  74. package/dist/diagnostics/index.d.ts +7 -0
  75. package/dist/diagnostics/report-request.d.ts +61 -0
  76. package/dist/diagnostics.d.ts +5 -0
  77. package/dist/diagnostics.js +209 -0
  78. package/dist/disclosure-CbLtajUQ.js +1 -0
  79. package/dist/dropdown-BwhViJFd.js +1 -0
  80. package/dist/elements/agent-card.js +1 -1
  81. package/dist/elements/artifact.js +1 -1
  82. package/dist/elements/attachments.js +1 -1
  83. package/dist/elements/audio-visualizer.js +1 -1
  84. package/dist/elements/autoloader.js +1 -1
  85. package/dist/elements/avatar.js +1 -1
  86. package/dist/elements/badge.js +1 -1
  87. package/dist/elements/button.js +1 -1
  88. package/dist/elements/card.js +1 -1
  89. package/dist/elements/cards.js +1 -1
  90. package/dist/elements/chain-of-thought.js +1 -1
  91. package/dist/elements/chat-scope-picker.js +1 -1
  92. package/dist/elements/chat-workspace.js +1 -1
  93. package/dist/elements/chat.js +1 -1
  94. package/dist/elements/checkbox-group.d.ts +1 -0
  95. package/dist/elements/checkbox-group.js +1 -0
  96. package/dist/elements/checkbox.d.ts +1 -0
  97. package/dist/elements/checkbox.js +1 -0
  98. package/dist/elements/checkpoint.js +1 -1
  99. package/dist/elements/choice.js +1 -1
  100. package/dist/elements/coachmark.js +1 -1
  101. package/dist/elements/code-block.js +1 -1
  102. package/dist/elements/command.js +1 -1
  103. package/dist/elements/compare.js +1 -1
  104. package/dist/elements/composer.js +1 -1
  105. package/dist/elements/confirm-card.js +1 -1
  106. package/dist/elements/context-meter.js +1 -1
  107. package/dist/elements/conversation-item.d.ts +1 -0
  108. package/dist/elements/conversation-item.js +1 -0
  109. package/dist/elements/conversation-list.js +1 -1
  110. package/dist/elements/default-input.d.ts +3 -3
  111. package/dist/elements/define-entry.d.ts +12 -0
  112. package/dist/elements/define.d.ts +27 -0
  113. package/dist/elements/diagnostic-events.d.ts +74 -0
  114. package/dist/elements/dialog.js +1 -1
  115. package/dist/elements/dock.d.ts +1 -0
  116. package/dist/elements/dock.js +243 -0
  117. package/dist/elements/dropdown.d.ts +1 -0
  118. package/dist/elements/dropdown.js +1 -0
  119. package/dist/elements/editable-label.js +1 -1
  120. package/dist/elements/element-data-types.d.ts +37 -0
  121. package/dist/elements/element-diagnostics.d.ts +81 -0
  122. package/dist/elements/embed.js +1 -1
  123. package/dist/elements/empty.js +1 -1
  124. package/dist/elements/feedback-bar.js +1 -1
  125. package/dist/elements/file-tree.js +1 -1
  126. package/dist/elements/file-upload.js +1 -1
  127. package/dist/elements/form.js +1 -1
  128. package/dist/elements/hover-card.js +1 -1
  129. package/dist/elements/icon.js +1 -1
  130. package/dist/elements/image.js +1 -1
  131. package/dist/elements/input.js +1 -1
  132. package/dist/elements/kbd.js +1 -1
  133. package/dist/elements/link-preview.js +1 -1
  134. package/dist/elements/loader.js +1 -1
  135. package/dist/elements/markdown.js +1 -1
  136. package/dist/elements/menu.js +1 -1
  137. package/dist/elements/message-skills.js +1 -1
  138. package/dist/elements/message.js +1 -1
  139. package/dist/elements/model-switcher.js +1 -1
  140. package/dist/elements/nav.js +1 -1
  141. package/dist/elements/notice.js +1 -1
  142. package/dist/elements/pane-grid.d.ts +1 -0
  143. package/dist/elements/pane-grid.js +1 -0
  144. package/dist/elements/pane-group.js +1 -1
  145. package/dist/elements/pane.js +1 -1
  146. package/dist/elements/popover.js +1 -1
  147. package/dist/elements/progress-bar.js +1 -1
  148. package/dist/elements/prompt-dock.js +1 -1
  149. package/dist/elements/prompt-input.js +1 -1
  150. package/dist/elements/prompt-suggestions.js +1 -1
  151. package/dist/elements/radio-group.d.ts +1 -0
  152. package/dist/elements/radio-group.js +1 -0
  153. package/dist/elements/reasoning.js +1 -1
  154. package/dist/elements/register.d.ts +2 -0
  155. package/dist/elements/remote.js +1 -1
  156. package/dist/elements/resizable.js +1 -1
  157. package/dist/elements/response-stream.js +3 -3
  158. package/dist/elements/screen.js +1 -1
  159. package/dist/elements/scroll-area.js +1 -1
  160. package/dist/elements/scroll-button.js +1 -1
  161. package/dist/elements/search.js +1 -1
  162. package/dist/elements/segmented.js +1 -1
  163. package/dist/elements/select.d.ts +1 -0
  164. package/dist/elements/select.js +1 -0
  165. package/dist/elements/separator.js +1 -1
  166. package/dist/elements/setting-item.js +1 -1
  167. package/dist/elements/settings-group.js +1 -1
  168. package/dist/elements/skeleton.js +1 -1
  169. package/dist/elements/slider.d.ts +1 -0
  170. package/dist/elements/slider.js +1 -0
  171. package/dist/elements/slots.d.ts +36 -4
  172. package/dist/elements/source.js +1 -1
  173. package/dist/elements/status.js +1 -1
  174. package/dist/elements/switch.js +1 -1
  175. package/dist/elements/tabs.js +1 -1
  176. package/dist/elements/tasks.js +1 -1
  177. package/dist/elements/text-shimmer.js +1 -1
  178. package/dist/elements/thinking-bar.js +1 -1
  179. package/dist/elements/thread.js +1 -1
  180. package/dist/elements/toast.js +1 -1
  181. package/dist/elements/tool.js +1 -1
  182. package/dist/elements/tooltip.js +1 -1
  183. package/dist/elements/voice-input.js +1 -1
  184. package/dist/elements/voice-output.js +1 -1
  185. package/dist/elements.d.ts +589 -167
  186. package/dist/{elements/chunks/ellipsis-CVxqfsDZ.js → ellipsis-DFr4Vqr3.js} +1 -1
  187. package/dist/embed-5s_vU4J0.js +1 -0
  188. package/dist/engine-javascript-vq0WuIJl.js +14 -2516
  189. package/dist/{elements/chunks/external-link-DG2sRdQL.js → external-link-u9d85ORQ.js} +1 -1
  190. package/dist/file-text-CX7_x42o.js +1 -0
  191. package/dist/{elements/chunks/file-tree-yGWNK0Js.js → file-tree-D3kBjXI5.js} +1 -1
  192. package/dist/folder-V-YdIL8f.js +1 -0
  193. package/dist/form-BRNUMCN2.js +1 -0
  194. package/dist/github-dark-dimmed-DUshB20C.js +1 -4
  195. package/dist/github-light-JYsPkUQd.js +1 -4
  196. package/dist/hover-card-DaeiYrTA.js +1 -0
  197. package/dist/html-CPZ3oZQ7.js +1 -10
  198. package/dist/icon-BpixdZNq.js +1 -0
  199. package/dist/{elements/chunks/index-BCRhi84a.js → index-xu_pdcPi.js} +1 -1
  200. package/dist/index.d.ts +3 -1
  201. package/dist/index.js +8473 -6470
  202. package/dist/index.server.js +6495 -4958
  203. package/dist/info-8mFsIjS3.js +1 -0
  204. package/dist/input-d1Yuu-hz.js +1 -0
  205. package/dist/javascript-C25yR2R2.js +1 -6
  206. package/dist/json-DxJze_jm.js +1 -6
  207. package/dist/kai-provider.es.js +40 -40
  208. package/dist/kai.es.js +1 -1
  209. package/dist/kbd-961zMPFB.js +1 -0
  210. package/dist/link-DlZZBGaR.js +1 -0
  211. package/dist/{elements/chunks/link-preview-Cf5KOP-i.js → link-preview-DIIbV2Sd.js} +1 -1
  212. package/dist/loader-YQszHFaW.js +1 -0
  213. package/dist/{elements/chunks/markdown-DbozyYqa.js → markdown-CVk1MPsj.js} +1 -1
  214. package/dist/mcp.es.js +3873 -544
  215. package/dist/message-CwKVEdZg.js +1 -0
  216. package/dist/message-DS8lzHc1.js +1 -0
  217. package/dist/message-circle-Xf8Q-vZp.js +1 -0
  218. package/dist/message-square-Cxnwgkns.js +1 -0
  219. package/dist/{elements/chunks/minimize-2-SegJ_oku.js → minimize-2-UEZmP2QT.js} +1 -1
  220. package/dist/{elements/chunks/model-switcher-D7JrFFHp.js → model-switcher-Kk1jEQ5t.js} +1 -1
  221. package/dist/{elements/chunks/overlay-CV7z2YuX.js → overlay-C1od6LOz.js} +1 -1
  222. package/dist/panel-right-ryET0yyn.js +1 -0
  223. package/dist/paperclip-BDNoZFJP.js +1 -0
  224. package/dist/play-OFIvYXUQ.js +1 -0
  225. package/dist/primitives/card-data-types.d.ts +49 -0
  226. package/dist/primitives/card-routing.d.ts +1 -33
  227. package/dist/primitives/card-validate.d.ts +2 -2
  228. package/dist/primitives/chat-config.d.ts +29 -2
  229. package/dist/primitives/field-mask.d.ts +96 -0
  230. package/dist/primitives/field-semantics.d.ts +36 -0
  231. package/dist/primitives/input-mask.d.ts +46 -0
  232. package/dist/primitives/url-scheme-policy.d.ts +34 -0
  233. package/dist/primitives/use-resize-observer.d.ts +23 -0
  234. package/dist/primitives/use-speech-recognition.d.ts +8 -0
  235. package/dist/primitives/use-text-stream.d.ts +5 -0
  236. package/dist/progress-bar-BvYX0aoy.js +1 -0
  237. package/dist/prompt-suggestion-BRFu2lcu.js +1 -0
  238. package/dist/radio-DcL23cNJ.js +1 -0
  239. package/dist/react/index.d.ts +306 -283
  240. package/dist/react.js +309 -247
  241. package/dist/reasoning-Cackq4Rh.js +1 -0
  242. package/dist/register-impl-C7rENvPK.js +424 -0
  243. package/dist/{elements/chunks/resizable-Dpzjlvp4.js → resizable-Cp9HUYih.js} +1 -1
  244. package/dist/rotate-ccw-0DLRDHH5.js +1 -0
  245. package/dist/rotate-cw-DOB9IGcU.js +1 -0
  246. package/dist/schemas/form.schema.json +21 -1
  247. package/dist/schemas/index.d.ts +1 -1
  248. package/dist/schemas/tool-defs.d.ts +45 -0
  249. package/dist/schemas.js +398 -305
  250. package/dist/scroll-area-emVmWP4K.js +1 -0
  251. package/dist/scroll-button-CeP_gv1x.js +1 -0
  252. package/dist/select-CWmu00tS.js +1 -0
  253. package/dist/{elements/chunks/separator-DH2r_ZSZ.js → separator-CE6vly6z.js} +1 -1
  254. package/dist/{elements/chunks/settings-BFMOd6yB.js → settings-CE-78SvZ.js} +1 -1
  255. package/dist/{elements/chunks/settings-group-B3VZKIcz.js → settings-group-ZFWNxFQR.js} +1 -1
  256. package/dist/{elements/chunks/skeleton-N8BuWYmf.js → skeleton-BebdP4mf.js} +1 -1
  257. package/dist/slider-BAKRYUlz.js +1 -0
  258. package/dist/slots-DaNjX0rc.js +1 -0
  259. package/dist/{solid-BMuDmFo_.js → solid-BE5ui6sy.js} +10672 -8116
  260. package/dist/{solid-CGYNdLRR.js → solid-DFhsce3O.js} +8137 -6056
  261. package/dist/solid.d.ts +15 -0
  262. package/dist/solid.js +248 -235
  263. package/dist/solid.server.js +248 -235
  264. package/dist/source-B0FlguSx.js +1 -0
  265. package/dist/star-CBMZvLuN.js +1 -0
  266. package/dist/state/index.d.ts +5 -1
  267. package/dist/state/mock.d.ts +32 -2
  268. package/dist/state/persistence.d.ts +51 -0
  269. package/dist/state/stream.d.ts +50 -0
  270. package/dist/state/threads.d.ts +71 -0
  271. package/dist/state.js +297 -172
  272. package/dist/store-7eoadydQ.js +1 -0
  273. package/dist/svelte--5p79yCD.js +1 -15
  274. package/dist/switch-CViARZCy.js +1 -0
  275. package/dist/tasks-card-MjCcOhR_.js +1 -0
  276. package/dist/text-shimmer-DKlwRF2G.js +1 -0
  277. package/dist/textarea-3ay2Me2-.js +1 -0
  278. package/dist/theme.tokens.css +82 -13
  279. package/dist/{elements/chunks/thumbs-up-DQe0hvkZ.js → thumbs-up-DQcu2HMO.js} +1 -1
  280. package/dist/toast-store-C6yKOhbk.js +1 -0
  281. package/dist/{elements/chunks/tool-C1YtxwPu.js → tool-BCYNlfKy.js} +1 -1
  282. package/dist/{elements/chunks/tooltip-BqwnFWhn.js → tooltip-05zI4hjs.js} +1 -1
  283. package/dist/trash-2-BR1RxGky.js +1 -0
  284. package/dist/{elements/chunks/triangle-alert-B6or7F5u.js → triangle-alert-DHohDOCy.js} +1 -1
  285. package/dist/tsx-B8rCNbgL.js +1 -6
  286. package/dist/types.d.ts +4 -2
  287. package/dist/typescript-RycA9KXf.js +1 -6
  288. package/dist/ui/checkbox-group.d.ts +83 -0
  289. package/dist/ui/checkbox.d.ts +40 -0
  290. package/dist/ui/dock.d.ts +131 -0
  291. package/dist/ui/hover-card.d.ts +44 -0
  292. package/dist/ui/icon.d.ts +3 -0
  293. package/dist/ui/input.d.ts +48 -2
  294. package/dist/ui/radio.d.ts +85 -0
  295. package/dist/ui/select.d.ts +71 -0
  296. package/dist/ui/slider.d.ts +65 -0
  297. package/dist/ui/switch.d.ts +9 -2
  298. package/dist/upload-BbGW6A_u.js +1 -0
  299. package/dist/url-scheme-policy-DHpoTJwB.js +1 -0
  300. package/dist/use-card-resolution-CJW4grFa.js +1 -0
  301. package/dist/{variant-aurora-DwSOfraQ.js → variant-aurora-BUOVgPOB.js} +27 -27
  302. package/dist/{variant-aurora-wKvAQ5wp.js → variant-aurora-CUsUJvFI.js} +28 -28
  303. package/dist/{variant-aurora-XEkPT0_z.js → variant-aurora-CW2i4bfr.js} +2 -2
  304. package/dist/{elements/chunks/variant-aurora-QXTZPyST.js → variant-aurora-xiMPty8f.js} +2 -2
  305. package/dist/variant-custom-BQtOnmeO.js +1 -0
  306. package/dist/{variant-custom-BhS9wIpU.js → variant-custom-CAm9L-E5.js} +65 -65
  307. package/dist/{variant-custom-eFIg2XWQ.js → variant-custom-CAnzzppW.js} +40 -40
  308. package/dist/variant-custom-D91HjAKz.js +1 -0
  309. package/dist/{variant-wave-CUWGx9Bn.js → variant-wave-Ca1Ls4_y.js} +21 -21
  310. package/dist/{variant-wave-CbVsmgwT.js → variant-wave-CxX39DKd.js} +25 -25
  311. package/dist/{variant-wave-BwzrHD1s.js → variant-wave-DYyzIIZv.js} +2 -2
  312. package/dist/{elements/chunks/variant-wave-DDCwhLas.js → variant-wave-uY5crU_K.js} +2 -2
  313. package/dist/vue-BmIZj4XD.js +1 -33
  314. package/dist/wire/chunk.d.ts +47 -0
  315. package/dist/wire/diagnostics.d.ts +575 -0
  316. package/dist/wire/encode-probe.d.ts +70 -0
  317. package/dist/wire/encode.d.ts +24 -0
  318. package/dist/wire/index.d.ts +2 -0
  319. package/dist/wire/sse.d.ts +10 -2
  320. package/dist/wire.js +917 -480
  321. package/dist/x-C5m1JFgi.js +1 -0
  322. package/frameworks/react/index.tsx +325 -108
  323. package/llms-full.txt +1354 -82
  324. package/llms.txt +6 -3
  325. package/package.json +39 -6
  326. package/src/agent-tooling/archetypes.ts +41 -9
  327. package/src/agent-tooling/catalog/README.md +676 -0
  328. package/src/agent-tooling/catalog/catalog-types.ts +231 -0
  329. package/src/agent-tooling/catalog/fabrications.ts +96 -0
  330. package/src/agent-tooling/catalog/invariants.ts +284 -0
  331. package/src/agent-tooling/catalog/labs-titles.ts +114 -0
  332. package/src/agent-tooling/catalog/scenarios.ts +87 -0
  333. package/src/agent-tooling/catalog/surfaces.ts +347 -0
  334. package/src/agent-tooling/construct/cli-entry.ts +8 -0
  335. package/src/agent-tooling/construct/cli.ts +131 -0
  336. package/src/agent-tooling/construct/codegen.ts +1353 -0
  337. package/src/agent-tooling/construct/construct.v1.schema.json +250 -0
  338. package/src/agent-tooling/construct/dev.ts +148 -0
  339. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +7 -0
  340. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +44 -0
  341. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +23 -0
  342. package/src/agent-tooling/construct/schema.ts +371 -0
  343. package/src/agent-tooling/integrations/anthropic.ts +7 -1
  344. package/src/agent-tooling/integrations/cloudflare.ts +43 -2
  345. package/src/agent-tooling/integrations/langgraph.ts +7 -1
  346. package/src/agent-tooling/integrations/mastra.ts +7 -1
  347. package/src/agent-tooling/integrations/mock.ts +92 -17
  348. package/src/agent-tooling/integrations/ollama.ts +7 -1
  349. package/src/agent-tooling/integrations/openai.ts +7 -1
  350. package/src/agent-tooling/integrations/openrouter.ts +7 -1
  351. package/src/agent-tooling/integrations/pi.ts +56 -1
  352. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +7 -1
  353. package/src/agent-tooling/mcp/css-raw.d.ts +12 -0
  354. package/src/agent-tooling/mcp/manifest.ts +100 -1
  355. package/src/agent-tooling/mcp/server.ts +63 -6
  356. package/src/agent-tooling/mcp/tools/construct.ts +130 -0
  357. package/src/agent-tooling/mcp/tools/debug.ts +126 -4
  358. package/src/agent-tooling/mcp/tools/reference.ts +542 -5
  359. package/src/agent-tooling/mcp/tools/scaffold.ts +1834 -307
  360. package/src/agent-tooling/mcp/tools/theme.ts +146 -30
  361. package/src/agent-tooling/mcp/validate-args.ts +141 -0
  362. package/src/agent-tooling/recipes/composed-thread.ts +714 -0
  363. package/src/agent-tooling/recipes/index.ts +22 -0
  364. package/src/agent-tooling/recipes/types.ts +32 -0
  365. package/src/agent-tooling/registry.ts +24 -7
  366. package/src/agent-tooling/route-emit.ts +26 -2
  367. package/src/components/attachment-types.ts +54 -1
  368. package/src/components/attachments.tsx +169 -22
  369. package/src/components/audio-visualizer/fit-scale.ts +112 -0
  370. package/src/components/audio-visualizer/index.tsx +80 -3
  371. package/src/components/audio-visualizer/variant-aurora.tsx +10 -4
  372. package/src/components/audio-visualizer/variant-bar.tsx +38 -6
  373. package/src/components/audio-visualizer/variant-custom.tsx +8 -2
  374. package/src/components/audio-visualizer/variant-grid.tsx +16 -11
  375. package/src/components/audio-visualizer/variant-radial.tsx +15 -10
  376. package/src/components/audio-visualizer/variant-wave.tsx +7 -2
  377. package/src/components/card-renderer.tsx +39 -8
  378. package/src/components/card.tsx +10 -2
  379. package/src/components/chat-container.tsx +4 -1
  380. package/src/components/chat-scope-picker.tsx +2 -2
  381. package/src/components/chat-thread.tsx +105 -13
  382. package/src/components/choice-card.tsx +102 -37
  383. package/src/components/coachmark.tsx +1 -1
  384. package/src/components/code-block.tsx +81 -5
  385. package/src/components/composer.tsx +89 -8
  386. package/src/components/confirm-card.tsx +20 -4
  387. package/src/components/conversation-item.tsx +138 -4
  388. package/src/components/conversation-list.tsx +230 -15
  389. package/src/components/embed.tsx +4 -0
  390. package/src/components/empty.tsx +14 -3
  391. package/src/components/file-tree.tsx +5 -1
  392. package/src/components/form-widgets.tsx +257 -195
  393. package/src/components/form.tsx +336 -21
  394. package/src/components/loader.tsx +12 -5
  395. package/src/components/message-skills.tsx +1 -1
  396. package/src/components/message.tsx +142 -12
  397. package/src/components/prompt-input.tsx +7 -1
  398. package/src/components/prompt-suggestion.tsx +4 -1
  399. package/src/components/reasoning.tsx +30 -3
  400. package/src/components/response-compare.tsx +2 -2
  401. package/src/components/response-stream.tsx +13 -7
  402. package/src/components/screen.tsx +5 -0
  403. package/src/components/scroll-button.tsx +63 -4
  404. package/src/components/source.tsx +6 -1
  405. package/src/components/tasks-card.tsx +58 -16
  406. package/src/components/thread.tsx +10 -1
  407. package/src/components/toast.tsx +11 -5
  408. package/src/components/tool-types.ts +9 -0
  409. package/src/components/voice-input.tsx +35 -3
  410. package/src/components/voice-output.tsx +46 -6
  411. package/src/components/workspace-shell.tsx +285 -0
  412. package/src/diagnostics/hook.ts +352 -0
  413. package/src/diagnostics/index.ts +82 -0
  414. package/src/diagnostics/report-request.ts +296 -0
  415. package/src/elements/attachments.tsx +9 -8
  416. package/src/elements/audio-visualizer.tsx +48 -4
  417. package/src/elements/chat-workspace.tsx +159 -287
  418. package/src/elements/chat.tsx +31 -10
  419. package/src/elements/checkbox-group.tsx +164 -0
  420. package/src/elements/checkbox.tsx +134 -0
  421. package/src/elements/code-block.tsx +19 -1
  422. package/src/elements/command.tsx +4 -1
  423. package/src/elements/compiled.css +1 -1
  424. package/src/elements/composer.tsx +12 -1
  425. package/src/elements/conversation-item.tsx +120 -0
  426. package/src/elements/conversation-list.tsx +60 -11
  427. package/src/elements/default-input.tsx +5 -5
  428. package/src/elements/define-entry.ts +12 -0
  429. package/src/elements/define.tsx +168 -8
  430. package/src/elements/diagnostic-events.ts +114 -0
  431. package/src/elements/dialog.tsx +47 -0
  432. package/src/elements/disclosure.ts +34 -12
  433. package/src/elements/dock.tsx +215 -0
  434. package/src/elements/dropdown.tsx +123 -0
  435. package/src/elements/element-data-types.ts +40 -0
  436. package/src/elements/element-diagnostics.ts +392 -0
  437. package/src/elements/element-manifest.json +36 -0
  438. package/src/elements/element-meta.json +1483 -560
  439. package/src/elements/element-nonscalar.json +185 -0
  440. package/src/elements/element-types.d.ts +589 -167
  441. package/src/elements/feedback-bar.tsx +5 -0
  442. package/src/elements/icon-names.json +29 -0
  443. package/src/elements/input.tsx +312 -23
  444. package/src/elements/menu.tsx +13 -0
  445. package/src/elements/message.tsx +2 -0
  446. package/src/elements/pane-grid.tsx +111 -0
  447. package/src/elements/prompt-dock.tsx +2 -1
  448. package/src/elements/prompt-input.tsx +16 -10
  449. package/src/elements/radio-group.tsx +129 -0
  450. package/src/elements/register-impl.ts +52 -0
  451. package/src/elements/register.ts +28 -0
  452. package/src/elements/resizable.tsx +52 -25
  453. package/src/elements/scroll-button.tsx +59 -6
  454. package/src/elements/select.tsx +165 -0
  455. package/src/elements/slider.tsx +171 -0
  456. package/src/elements/slots.ts +108 -13
  457. package/src/elements/styles.css +119 -9
  458. package/src/elements/thinking-bar.tsx +16 -7
  459. package/src/elements/thread.tsx +2 -0
  460. package/src/elements/toast.tsx +17 -1
  461. package/src/elements/tool.tsx +26 -5
  462. package/src/elements/voice-input.tsx +11 -2
  463. package/src/elements/voice-output.tsx +16 -3
  464. package/src/index.ts +4 -1
  465. package/src/primitives/card-data-types.ts +49 -0
  466. package/src/primitives/card-host.tsx +4 -2
  467. package/src/primitives/card-routing.ts +7 -56
  468. package/src/primitives/card-schemas/form.schema.json +21 -1
  469. package/src/primitives/card-validate.ts +10 -2
  470. package/src/primitives/chat-config.tsx +33 -6
  471. package/src/primitives/field-mask.ts +256 -0
  472. package/src/primitives/field-semantics.ts +115 -0
  473. package/src/primitives/input-mask.ts +853 -0
  474. package/src/primitives/toast-store.ts +66 -4
  475. package/src/primitives/url-scheme-policy.ts +70 -0
  476. package/src/primitives/use-resize-observer.ts +33 -0
  477. package/src/primitives/use-speech-recognition.ts +12 -1
  478. package/src/primitives/use-stick-to-bottom.ts +34 -2
  479. package/src/primitives/use-text-stream.ts +33 -2
  480. package/src/remote/provider-runtime.ts +6 -3
  481. package/src/schemas/index.ts +1 -0
  482. package/src/schemas/tool-defs.ts +307 -6
  483. package/src/solid.ts +15 -0
  484. package/src/state/index.ts +13 -1
  485. package/src/state/mock.ts +92 -6
  486. package/src/state/persistence.ts +180 -0
  487. package/src/state/stream.ts +71 -4
  488. package/src/state/threads.ts +142 -0
  489. package/src/types.ts +4 -2
  490. package/src/ui/agent-card.tsx +2 -2
  491. package/src/ui/avatar.tsx +1 -1
  492. package/src/ui/checkbox-group.tsx +153 -0
  493. package/src/ui/checkbox.tsx +63 -0
  494. package/src/ui/dialog.tsx +6 -0
  495. package/src/ui/dock.tsx +584 -0
  496. package/src/ui/dropdown.tsx +67 -23
  497. package/src/ui/hover-card.tsx +156 -6
  498. package/src/ui/icon.tsx +53 -0
  499. package/src/ui/input.tsx +275 -16
  500. package/src/ui/kbd.tsx +1 -1
  501. package/src/ui/nav.tsx +1 -1
  502. package/src/ui/pane-group.tsx +2 -2
  503. package/src/ui/radio.tsx +150 -0
  504. package/src/ui/resizable.tsx +11 -0
  505. package/src/ui/select.tsx +168 -0
  506. package/src/ui/slider.tsx +178 -0
  507. package/src/ui/switch.tsx +49 -18
  508. package/src/utils/cn.ts +11 -5
  509. package/src/wire/chunk.ts +47 -0
  510. package/src/wire/consume.ts +185 -26
  511. package/src/wire/diagnostics.ts +727 -0
  512. package/src/wire/encode-probe.ts +214 -0
  513. package/src/wire/encode.ts +267 -14
  514. package/src/wire/formats/anthropic.ts +8 -1
  515. package/src/wire/formats/openai.ts +5 -0
  516. package/src/wire/index.ts +22 -0
  517. package/src/wire/read.ts +250 -6
  518. package/src/wire/sse.ts +16 -3
  519. package/theme.css +84 -13
  520. package/dist/elements/chunks/action-icons-DW9muWrY.js +0 -1
  521. package/dist/elements/chunks/arrow-left-iJVnQmxA.js +0 -1
  522. package/dist/elements/chunks/artifact-BSuPYFHY.js +0 -1
  523. package/dist/elements/chunks/attachments-CL6TaqIb.js +0 -1
  524. package/dist/elements/chunks/audio-visualizer-BuUqQmaO.js +0 -1
  525. package/dist/elements/chunks/badge-TIMiJFKc.js +0 -1
  526. package/dist/elements/chunks/bash-InADTalH.js +0 -1
  527. package/dist/elements/chunks/card-renderer-CSoTL_e1.js +0 -1
  528. package/dist/elements/chunks/card-routing-BK09BdmL.js +0 -1
  529. package/dist/elements/chunks/chat-thread-DxoKaStk.js +0 -1
  530. package/dist/elements/chunks/check-MdLzbxrm.js +0 -1
  531. package/dist/elements/chunks/chevron-down-MK7IzDqe.js +0 -1
  532. package/dist/elements/chunks/chevron-right-C5jXhmtV.js +0 -1
  533. package/dist/elements/chunks/choice-card-CGMc3kek.js +0 -1
  534. package/dist/elements/chunks/circle-BG7VkPep.js +0 -1
  535. package/dist/elements/chunks/circle-check-GqvO2UBs.js +0 -1
  536. package/dist/elements/chunks/circle-x-mhH7pOK2.js +0 -1
  537. package/dist/elements/chunks/code-block-ClhQO9RW.js +0 -1
  538. package/dist/elements/chunks/collapsible-DBcBkFPh.js +0 -1
  539. package/dist/elements/chunks/composer-CZZmtYOT.js +0 -69
  540. package/dist/elements/chunks/confirm-card-BWjf2-xo.js +0 -1
  541. package/dist/elements/chunks/context-hKjNqiyQ.js +0 -1
  542. package/dist/elements/chunks/conversation-list-CsGJjSKG.js +0 -1
  543. package/dist/elements/chunks/core-AYMC6_lb.js +0 -12
  544. package/dist/elements/chunks/css-M7EaDHN_.js +0 -1
  545. package/dist/elements/chunks/default-input-B1Mem5wu.js +0 -1
  546. package/dist/elements/chunks/define-B_tXMYRm.js +0 -1
  547. package/dist/elements/chunks/disclosure-DAoYOKew.js +0 -1
  548. package/dist/elements/chunks/download-DnCyWFJE.js +0 -1
  549. package/dist/elements/chunks/dropdown-CmNna5lT.js +0 -1
  550. package/dist/elements/chunks/embed-CjAD2jSi.js +0 -1
  551. package/dist/elements/chunks/engine-javascript-vq0WuIJl.js +0 -141
  552. package/dist/elements/chunks/file-text-D0J9F6M7.js +0 -1
  553. package/dist/elements/chunks/folder-NV1pi_ud.js +0 -1
  554. package/dist/elements/chunks/form-Cp8Y00Wn.js +0 -1
  555. package/dist/elements/chunks/github-dark-dimmed-DUshB20C.js +0 -1
  556. package/dist/elements/chunks/github-light-JYsPkUQd.js +0 -1
  557. package/dist/elements/chunks/hover-card-6KtXpLwT.js +0 -1
  558. package/dist/elements/chunks/html-CPZ3oZQ7.js +0 -1
  559. package/dist/elements/chunks/icon-97R8SR2p.js +0 -1
  560. package/dist/elements/chunks/info-C8r5Q6cc.js +0 -1
  561. package/dist/elements/chunks/input-DrseEjhp.js +0 -1
  562. package/dist/elements/chunks/javascript-C25yR2R2.js +0 -1
  563. package/dist/elements/chunks/json-DxJze_jm.js +0 -1
  564. package/dist/elements/chunks/kbd-OGJDUT-O.js +0 -1
  565. package/dist/elements/chunks/link-BdsfLB7b.js +0 -1
  566. package/dist/elements/chunks/loader-B5QzK_8_.js +0 -1
  567. package/dist/elements/chunks/message-CE4k-qaj.js +0 -1
  568. package/dist/elements/chunks/message-DtXKPqiR.js +0 -1
  569. package/dist/elements/chunks/message-square-C9ovmtOo.js +0 -1
  570. package/dist/elements/chunks/paperclip-Bg0ITU3b.js +0 -1
  571. package/dist/elements/chunks/progress-bar-D1_rzt6N.js +0 -1
  572. package/dist/elements/chunks/prompt-suggestion-D5Sm8xF1.js +0 -1
  573. package/dist/elements/chunks/reasoning-BrxQfpfI.js +0 -1
  574. package/dist/elements/chunks/rotate-cw-DzVDJOA1.js +0 -1
  575. package/dist/elements/chunks/scroll-area-93A1bbeG.js +0 -1
  576. package/dist/elements/chunks/scroll-button-CrTo7wm4.js +0 -1
  577. package/dist/elements/chunks/share-DgNbkJKY.js +0 -1
  578. package/dist/elements/chunks/slots-CIw9RlAe.js +0 -1
  579. package/dist/elements/chunks/source-w9V0NnBn.js +0 -1
  580. package/dist/elements/chunks/star-DHvACxtm.js +0 -1
  581. package/dist/elements/chunks/store-HIPGhNz9.js +0 -1
  582. package/dist/elements/chunks/svelte--5p79yCD.js +0 -1
  583. package/dist/elements/chunks/tasks-card-lPxoodlA.js +0 -1
  584. package/dist/elements/chunks/text-shimmer-BY_rI3s5.js +0 -1
  585. package/dist/elements/chunks/textarea-DPfuhswM.js +0 -1
  586. package/dist/elements/chunks/toast-store-VUZZBhzH.js +0 -1
  587. package/dist/elements/chunks/tsx-B8rCNbgL.js +0 -1
  588. package/dist/elements/chunks/typescript-RycA9KXf.js +0 -1
  589. package/dist/elements/chunks/use-card-resolution-DJg32jGH.js +0 -1
  590. package/dist/elements/chunks/variant-custom-D5ZlmziF.js +0 -1
  591. package/dist/elements/chunks/video-Bb86fVYI.js +0 -1
  592. package/dist/elements/chunks/vue-BmIZj4XD.js +0 -1
  593. package/dist/elements/chunks/x-BOhjBeOd.js +0 -1
  594. package/dist/llms/llms-full.txt +0 -2834
  595. package/dist/llms/llms.txt +0 -156
  596. package/dist/primitives/card-validate-generator.testlib.d.ts +0 -20
  597. package/dist/register-impl-DGmGzrEM.js +0 -145
  598. package/dist/ui/stat.d.ts +0 -16
  599. package/dist/variant-custom-MkTNTycd.js +0 -1
  600. package/src/primitives/card-validate-generator.testlib.ts +0 -38
  601. package/src/ui/stat.tsx +0 -41
  602. /package/dist/{elements/chunks/card-tags-D8lZ-C_U.js → card-tags-D8lZ-C_U.js} +0 -0
  603. /package/dist/{elements/chunks/link-preview-DNILK391.js → link-preview-DNILK391.js} +0 -0
@@ -22,6 +22,8 @@ import {
22
22
  } from '../primitives/card-validate';
23
23
  import type { CardEnvelope, CardEvent, CardHost, CardResolution } from '../primitives/card-contract';
24
24
  import { emitCardEvent } from '../primitives/card-routing';
25
+ import { compileMask, formatForDisplay, formatRaw, normalizeToRaw } from '../primitives/field-mask';
26
+ import { FIELD_SEMANTIC_TYPES, fieldSemantics, type FieldSemanticType } from '../primitives/field-semantics';
25
27
  import { useCardHost } from '../primitives/card-host';
26
28
  import { useCardResolution } from './use-card-resolution';
27
29
  import { Check } from 'lucide-solid';
@@ -167,6 +169,233 @@ export function widgetFor(field: FormField, inlineMax: number): WidgetKind {
167
169
  }
168
170
  }
169
171
 
172
+ // ─────────────────────────────────────────────────────────────────────────────
173
+ // Field formats (spec §7.3) — the model-facing half of masked fields.
174
+ // ─────────────────────────────────────────────────────────────────────────────
175
+
176
+ /**
177
+ * What one field's `x-kai-*` format hints resolve to.
178
+ *
179
+ * The three props on the left are `Input`'s masking surface verbatim;
180
+ * `hint` is the TEXT statement of the expected format, which `FieldRow` renders and
181
+ * links through its EXISTING `aria-describedby` chain (no third channel).
182
+ * Every field is `undefined` when the field carries no hints, which is what "byte for
183
+ * byte the behavior of today" means here.
184
+ */
185
+ export interface FieldMaskHint {
186
+ /** The compiled-clean mask pattern handed to `Input`'s `format`. */
187
+ format?: string;
188
+ /** An explicit, aligned display guide. Absent = the pattern derives its own. */
189
+ guide?: string;
190
+ /** The tier-1 semantic type: `inputmode`, `autocomplete`, and the canonical form. */
191
+ semantic?: FieldSemanticType;
192
+ /** The expected format, stated in text for the description chain. */
193
+ hint?: string;
194
+ /**
195
+ * Everything that was ignored and why, as DATA rather than as a side effect.
196
+ *
197
+ * Resolution runs inside a memo on a render path, so it must never throw and must
198
+ * not warn twice for the same reason on a re-render. Returning the messages lets
199
+ * `FieldRow` own the once-per-row discipline and lets the pure tests assert the
200
+ * wording without a console spy.
201
+ */
202
+ warnings: string[];
203
+ }
204
+
205
+ /** The resolution of a field with no format hints: shared, frozen, and the default
206
+ * every non-text widget carries so the prop bag has one shape. */
207
+ export const EMPTY_MASK_HINT: FieldMaskHint = Object.freeze({ warnings: [] as string[] });
208
+
209
+ /**
210
+ * Messages already printed for a given field DEFINITION.
211
+ *
212
+ * Keyed on the field object, not on the row, and that is the fix rather than the
213
+ * convenience. A per-row `Set` looks right and does not work: `FieldRow`'s body runs
214
+ * TWICE for one visible row (measured — the parent rebuilds the `<For>` once at mount),
215
+ * so each build got its own empty Set and every bad hint printed twice. The reviewer
216
+ * caught that because the tests asserted `toHaveBeenCalled()` and never a count; they
217
+ * assert `toHaveBeenCalledTimes` now.
218
+ *
219
+ * A `WeakMap` because the key is model output: it must not pin a discarded card's field
220
+ * definitions in memory, and it must not dedupe across cards either. The repo's
221
+ * reactivity contract makes the identity exactly right — an edited field arrives as a
222
+ * NEW object (CLAUDE.md, the `kai-` contract), so a changed hint warns again, while the
223
+ * same definition rendered twice warns once.
224
+ */
225
+ const WARNED_FIELDS = new WeakMap<FormField, Set<string>>();
226
+
227
+ function warnOncePerField(field: FormField, message: string): void {
228
+ let seen = WARNED_FIELDS.get(field);
229
+ if (seen === undefined) {
230
+ seen = new Set<string>();
231
+ WARNED_FIELDS.set(field, seen);
232
+ }
233
+ if (seen.has(message)) return;
234
+ seen.add(message);
235
+ // eslint-disable-next-line no-console
236
+ console.warn(message);
237
+ }
238
+
239
+ /** Clip a model-supplied string before it reaches a console warning. Not a security
240
+ * boundary — the console is not a sink — but a 500-character pattern printing itself
241
+ * on every render is noise that hides the sentence that matters (M4 precedent). */
242
+ function clipForWarning(text: string): string {
243
+ return text.length <= 32 ? text : `${text.slice(0, 32)}… (${text.length} chars)`;
244
+ }
245
+
246
+ const SEMANTIC_TOKENS: readonly string[] = FIELD_SEMANTIC_TYPES;
247
+
248
+ /**
249
+ * Resolve a field's `x-kai-format` / `x-kai-mask` / `x-kai-mask-guide` into the
250
+ * masking props `Input` takes, or into nothing at all.
251
+ *
252
+ * EVERY INPUT HERE IS MODEL OUTPUT and therefore untrusted. The hints are
253
+ * display-only — they produce text and caret positions, never HTML, a URL or an
254
+ * attribute on a navigable element — so this is about denial of service and
255
+ * confusion, not injection: an unknown token, a pattern the engine refuses and a
256
+ * misaligned guide each degrade to the largest thing that still works, and say so.
257
+ * A THROW here would take out the whole card, including the fields that were fine.
258
+ */
259
+ export function resolveFieldMask(field: FormField, fieldKey = ''): FieldMaskHint {
260
+ const declared: unknown = field['x-kai-format'];
261
+ const mask: unknown = field['x-kai-mask'];
262
+ const guideIn: unknown = field['x-kai-mask-guide'];
263
+ const where = fieldKey ? ` on field "${clipForWarning(fieldKey)}"` : '';
264
+
265
+ if (declared === undefined) {
266
+ // A pattern with nothing to attach it to. Silence here would leave a model
267
+ // convinced it had asked for a mask and a developer looking at a plain field.
268
+ if (typeof mask === 'string' || typeof guideIn === 'string') {
269
+ return {
270
+ warnings: [
271
+ `kai-form: x-kai-mask${where} has no x-kai-format and is ignored. ` +
272
+ `Set "x-kai-format": "custom" to mask with it.`,
273
+ ],
274
+ };
275
+ }
276
+ return { warnings: [] };
277
+ }
278
+
279
+ if (typeof declared !== 'string' || !SEMANTIC_TOKENS.includes(declared)) {
280
+ return {
281
+ warnings: [
282
+ `kai-form: x-kai-format "${clipForWarning(String(declared))}"${where} is not one of ` +
283
+ `${SEMANTIC_TOKENS.join(', ')}. Rendering an unmasked text field.`,
284
+ ],
285
+ };
286
+ }
287
+
288
+ const semantic = declared as FieldSemanticType;
289
+ const warnings: string[] = [];
290
+ const custom = semantic === 'custom';
291
+
292
+ if (!custom && typeof mask === 'string') {
293
+ warnings.push(
294
+ `kai-form: x-kai-mask${where} is read only with x-kai-format "custom"; ` +
295
+ `"${semantic}" brings its own format. The mask is ignored.`,
296
+ );
297
+ }
298
+
299
+ const pattern = custom ? mask : fieldSemantics(semantic).defaultFormat;
300
+ if (typeof pattern !== 'string' || pattern === '') {
301
+ warnings.push(
302
+ `kai-form: x-kai-format "custom"${where} needs an x-kai-mask pattern. ` +
303
+ `Rendering an unmasked text field.`,
304
+ );
305
+ return { warnings };
306
+ }
307
+
308
+ // Compile the PATTERN first, alone. Compiling it with a bad guide would report the
309
+ // guide's failure and the pattern's as one event, and the two have different
310
+ // fallbacks: a refused pattern means no mask at all, a refused guide means this
311
+ // mask with its own derived guide.
312
+ let compiled;
313
+ try {
314
+ compiled = compileMask(pattern);
315
+ } catch (err) {
316
+ warnings.push(
317
+ `kai-form: x-kai-mask "${clipForWarning(pattern)}"${where} did not compile ` +
318
+ `(${err instanceof Error ? err.message : String(err)}). Rendering an unmasked text field.`,
319
+ );
320
+ return { warnings };
321
+ }
322
+
323
+ // A pattern with no `#`/`@`/`*` COMPILES — unknown characters are literals by
324
+ // position, which is the rule that makes `V-***` work and is not negotiable. But it
325
+ // compiles to capacity 0: a field that accepts nothing, shows nothing and submits
326
+ // nothing. Left alone that is the quietest failure in the whole feature, and the one
327
+ // a model is likeliest to reach — `mm/dd/yyyy` and `CHG-1234` are the strings this
328
+ // schema's own descriptions put in front of it, one free-form key away from where
329
+ // they belong. So it is refused here, out loud, on the same path as a pattern the
330
+ // engine rejects outright. `compileMask` cannot make this call for both callers: a
331
+ // zero-capacity pattern is a legitimate compile, it is only a nonsense FIELD.
332
+ if (compiled.capacity === 0) {
333
+ warnings.push(
334
+ `kai-form: x-kai-mask "${clipForWarning(pattern)}"${where} has no fill positions, so nothing ` +
335
+ `could ever be typed into it. Use # for a digit, @ for a letter or digit, * for an ` +
336
+ `obscurable one (e.g. "##/##/####", with "mm/dd/yyyy" as x-kai-mask-guide). ` +
337
+ `Rendering an unmasked text field.`,
338
+ );
339
+ return { warnings };
340
+ }
341
+
342
+ let guide = typeof guideIn === 'string' ? guideIn : undefined;
343
+ if (guide !== undefined) {
344
+ try {
345
+ compileMask(pattern, guide);
346
+ } catch (err) {
347
+ warnings.push(
348
+ `kai-form: x-kai-mask-guide "${clipForWarning(guide)}"${where} does not align with the ` +
349
+ `pattern (${err instanceof Error ? err.message : String(err)}). Using the pattern's own guide.`,
350
+ );
351
+ guide = undefined;
352
+ }
353
+ }
354
+
355
+ return {
356
+ format: pattern,
357
+ guide,
358
+ semantic,
359
+ // The guide when there is one (`mm/dd/yyyy` reads as a format to a person); the
360
+ // pattern otherwise, because a DERIVED guide is spaces at every fill position and
361
+ // says nothing out loud. A visual guide is not a description either way (spec §6).
362
+ hint: `Format: ${guide ?? pattern}`,
363
+ warnings,
364
+ };
365
+ }
366
+
367
+ /**
368
+ * The text a masked control SHOWS for a stored (canonical) value.
369
+ *
370
+ * The store holds one value per field and it is the canonical one: digits for
371
+ * `tel`/`ssn`/`credit-card`. That value is also what flows back into the control as its
372
+ * `value` prop — and writing `5550101234` over a field the masker just wrote
373
+ * `555-010-1234` into un-formats it one keystroke behind the user, with the caret
374
+ * jumping to the end. `<kai-input>` hit exactly this and split display from canonical;
375
+ * this is the same split, one layer up, for the Solid `Form`.
376
+ *
377
+ * Re-formatting through the SAME pure engine means the string handed back is the string
378
+ * already in the field, which the HTML value setter treats as a no-op (caret included).
379
+ *
380
+ * THE GUIDE BRANCH IS NOT A CHOICE MADE HERE — it mirrors `display()` in
381
+ * `primitives/input-mask.ts`: with an explicit guide the field is always the pattern's
382
+ * full length, without one it shows up to the last typed character. Disagreeing with the
383
+ * masker about that would put the fight straight back.
384
+ */
385
+ function maskedDisplay(hint: FieldMaskHint, value: unknown): unknown {
386
+ if (hint.format === undefined || typeof value !== 'string' || value === '') return value;
387
+ try {
388
+ const pattern = compileMask(hint.format, hint.guide);
389
+ const raw = normalizeToRaw(pattern, value);
390
+ return hint.guide === undefined ? formatRaw(pattern, raw) : formatForDisplay(pattern, raw);
391
+ } catch {
392
+ // Unreachable in practice — `resolveFieldMask` only reports a `format` that already
393
+ // compiled — but this runs on a render path, so it degrades to the stored text
394
+ // rather than taking the card down if that ever stops being true.
395
+ return value;
396
+ }
397
+ }
398
+
170
399
  /** Humanize a camelCase / snake_case property key into a label. */
171
400
  export function humanize(key: string): string {
172
401
  const spaced = key
@@ -626,6 +855,13 @@ export function Form(props: FormProps): JSX.Element {
626
855
  >
627
856
  <div class="flex w-full flex-wrap items-center justify-between gap-2">
628
857
  <Show when={dismissible()}>
858
+ {/* The contract `dismiss` verb — a footer ACTION (it emits
859
+ `{kind:'dismiss'}` and collapses the card to a re-openable
860
+ stub), not the chrome close in `Card`'s own `dismissible`
861
+ prop, which the contract cards deliberately never set.
862
+ confirm/choice/tasks were unlabelled ghost ✕ icons and now
863
+ match this one, so all four dismiss controls are the same
864
+ labelled ghost button. */}
629
865
  <Button
630
866
  type="button"
631
867
  variant="ghost"
@@ -738,39 +974,59 @@ interface FieldRowProps {
738
974
 
739
975
  function FieldRow(props: FieldRowProps): JSX.Element {
740
976
  const id = `f-${props.fieldKey}-${Math.random().toString(36).slice(2, 8)}`;
977
+ const labelId = `${id}-label`;
741
978
  const errorId = `${id}-err`;
742
979
  const descId = `${id}-desc`;
980
+ const formatId = `${id}-fmt`;
743
981
  const label = () => props.field.title ?? humanize(props.fieldKey);
744
982
  const widget = createMemo(() => widgetFor(props.field, props.inlineMax));
745
983
  const placeholder = () => props.field['x-kai-placeholder'];
984
+
985
+ // Field formats (spec §7.3). A memo, so a keystroke does not re-resolve: it reads
986
+ // `props.field` and nothing else, which is the subscription discipline the rest of
987
+ // this row is built on (K-D12b).
988
+ const maskHint = createMemo(() => {
989
+ const resolved = resolveFieldMask(props.field, props.fieldKey);
990
+ for (const message of resolved.warnings) warnOncePerField(props.field, message);
991
+ return resolved;
992
+ });
993
+
746
994
  const describedBy = () =>
747
- [props.field.description ? descId : '', props.error() ? errorId : '']
995
+ [
996
+ props.field.description ? descId : '',
997
+ maskHint().hint ? formatId : '',
998
+ props.error() ? errorId : '',
999
+ ]
748
1000
  .filter(Boolean)
749
1001
  .join(' ') || undefined;
750
1002
 
751
- const common = () => ({
752
- id,
753
- value: props.value(),
754
- field: props.field,
755
- disabled: props.disabled || props.field.readOnly === true,
756
- placeholder: placeholder(),
757
- required: props.required,
758
- invalid: Boolean(props.error()),
759
- describedBy: describedBy(),
760
- label: label(),
761
- onInput: props.onInput,
762
- onBlur: props.onBlur,
763
- });
1003
+ // `fieldset` and `repeater` render a REAL <fieldset><legend> holding this same text,
1004
+ // so the row's own label would print it twice. They are the only two kinds that
1005
+ // supply their own VISIBLE grouping label.
1006
+ //
1007
+ // The other four kinds this list used to hold — radio, checkbox-group, multiselect,
1008
+ // taglist — supplied an `aria-label` and nothing else, which is a name only a screen
1009
+ // reader can reach: "Severity", "Environments" and "Tags" were announced and INVISIBLE
1010
+ // on screen. The row now always renders the text for them and hands `labelId` to the
1011
+ // widget, which points its group at it with `aria-labelledby`. One string, visible,
1012
+ // correctly associated.
1013
+ const ownLegend = () => ['fieldset', 'repeater'].includes(widget());
1014
+
1015
+ // Which kinds have no single labelable control for `<label for>` to name. Their label
1016
+ // is still an element with an id, which is what `aria-labelledby` needs; `taglist` is
1017
+ // deliberately NOT here, because its draft <input> carries the row id and a real
1018
+ // `for` association is strictly better than an ARIA one.
1019
+ const groupOnly = () => ['radio', 'checkbox-group', 'multiselect'].includes(widget());
1020
+
1021
+ const common = fieldCommon(props, id, placeholder, label, describedBy, maskHint, () =>
1022
+ ownLegend() ? undefined : labelId,
1023
+ );
764
1024
 
765
- // A nested fieldset / repeater / checkbox-group provide their own grouping
766
- // label, so the row's <label> is only rendered for simple single controls.
767
- const isGrouped = () =>
768
- ['fieldset', 'repeater', 'checkbox-group', 'multiselect', 'radio', 'taglist'].includes(widget());
769
1025
 
770
1026
  return (
771
1027
  <div class="flex flex-col gap-2 rounded-xl bg-surface p-3.5" data-field={props.fieldKey}>
772
- <Show when={!isGrouped()}>
773
- <label for={id} class="text-sm font-medium text-foreground">
1028
+ <Show when={!ownLegend()}>
1029
+ <label id={labelId} for={groupOnly() ? undefined : id} class="text-sm font-medium text-foreground">
774
1030
  {label()}
775
1031
  <Show when={props.required}>
776
1032
  <span class="text-destructive dark:text-red-400" aria-hidden="true">{' *'}</span>
@@ -784,7 +1040,16 @@ function FieldRow(props: FieldRowProps): JSX.Element {
784
1040
  </p>
785
1041
  </Show>
786
1042
 
787
- <WidgetSwitch widget={widget()} common={common()} fieldKey={props.fieldKey} />
1043
+ {/* The expected format, as TEXT and in the same describedby chain as the
1044
+ description and the error (spec §6, SC 3.3.2). The visual guide inside the
1045
+ control is not a description and does not replace this. */}
1046
+ <Show when={maskHint().hint}>
1047
+ <p id={formatId} class="text-xs text-muted-foreground">
1048
+ {maskHint().hint}
1049
+ </p>
1050
+ </Show>
1051
+
1052
+ <WidgetSwitch widget={widget()} common={common} fieldKey={props.fieldKey} />
788
1053
 
789
1054
  <Show when={props.error()}>
790
1055
  <p id={errorId} role="alert" class="text-xs text-destructive dark:text-red-400">
@@ -795,6 +1060,53 @@ function FieldRow(props: FieldRowProps): JSX.Element {
795
1060
  );
796
1061
  }
797
1062
 
1063
+ /**
1064
+ * The widget prop bag for one field row. **Internal** — exported only so the
1065
+ * subscription contract can be pinned directly
1066
+ * (`tests/components/form-field-subscriptions.test.tsx`); it is not re-exported
1067
+ * from the package.
1068
+ *
1069
+ * GETTERS, NOT AN OBJECT FACTORY, and this is the whole point (K-D12b). The
1070
+ * factory read `props.value()` alongside everything else and rebuilt the bag on
1071
+ * every call, so a reader of ANY prop — `invalid`, say — subscribed to `value`
1072
+ * too and re-ran on every keystroke. Combined with the `<input>` being rebuilt
1073
+ * when its class expression re-ran (K-D12a), that is what made typing into a
1074
+ * `kai-form` text field lose focus after each character.
1075
+ *
1076
+ * One stable object with one getter per prop: each reader tracks exactly the
1077
+ * signal behind the prop it read. The object identity is constant, so the
1078
+ * `WidgetSwitch` prop below never changes either.
1079
+ */
1080
+ export function fieldCommon(
1081
+ props: FieldRowProps,
1082
+ id: string,
1083
+ placeholder: () => string | undefined,
1084
+ label: () => string,
1085
+ describedBy: () => string | undefined = () => undefined,
1086
+ mask: () => FieldMaskHint = () => EMPTY_MASK_HINT,
1087
+ labelledBy: () => string | undefined = () => undefined,
1088
+ ): ReturnType<FieldRowCommon> {
1089
+ return {
1090
+ id,
1091
+ // The DISPLAY text for a masked field, the stored value verbatim for every other
1092
+ // one (`maskedDisplay` is the identity when nothing resolved). What the form
1093
+ // SUBMITS is untouched by this: that comes off the store, which the control fills
1094
+ // with the canonical value through `onInput`.
1095
+ get value() { return maskedDisplay(mask(), props.value()); },
1096
+ get mask() { return mask(); },
1097
+ get field() { return props.field; },
1098
+ get disabled() { return props.disabled || props.field.readOnly === true; },
1099
+ get placeholder() { return placeholder(); },
1100
+ get required() { return props.required; },
1101
+ get invalid() { return Boolean(props.error()); },
1102
+ get describedBy() { return describedBy(); },
1103
+ get label() { return label(); },
1104
+ get labelledBy() { return labelledBy(); },
1105
+ onInput: (v) => props.onInput(v),
1106
+ onBlur: () => props.onBlur(),
1107
+ };
1108
+ }
1109
+
798
1110
  interface WidgetSwitchProps {
799
1111
  widget: WidgetKind;
800
1112
  fieldKey: string;
@@ -803,6 +1115,7 @@ interface WidgetSwitchProps {
803
1115
  type FieldRowCommon = () => {
804
1116
  id: string;
805
1117
  value: unknown;
1118
+ mask: FieldMaskHint;
806
1119
  field: FormField;
807
1120
  disabled: boolean;
808
1121
  placeholder?: string;
@@ -810,6 +1123,8 @@ type FieldRowCommon = () => {
810
1123
  invalid: boolean;
811
1124
  describedBy?: string;
812
1125
  label: string;
1126
+ /** The id of the row's visible <label>, for widgets that name a GROUP. */
1127
+ labelledBy?: string;
813
1128
  onInput: (v: unknown) => void;
814
1129
  onBlur: () => void;
815
1130
  };
@@ -250,7 +250,11 @@ export function TerminalLoader(props: TerminalLoaderProps) {
250
250
  const containerSizes = { sm: 'h-4', md: 'h-5', lg: 'h-6' };
251
251
  return (
252
252
  <div class={cn('flex items-center space-x-1', containerSizes[size()], props.class)}>
253
- <span class={cn('text-primary font-mono', textSizes[size()])}>{'>'}</span>
253
+ {/* Decorative status chrome, not a control — was the BRAND token
254
+ (text-primary). The sibling TextShimmerLoader below already renders
255
+ its "Thinking" text purely in --color-foreground/--color-muted-foreground,
256
+ with no brand color at all; matches that precedent. */}
257
+ <span class={cn('text-foreground font-mono', textSizes[size()])}>{'>'}</span>
254
258
  <div class={cn('bg-primary animate-[blink_1s_step-end_infinite]', cursorSizes[size()])} />
255
259
  <span class="sr-only">Loading</span>
256
260
  </div>
@@ -302,13 +306,16 @@ export function TextDotsLoader(props: TextDotsLoaderProps) {
302
306
  const textSizes = { sm: 'text-xs', md: 'text-sm', lg: 'text-base' };
303
307
  return (
304
308
  <div class={cn('inline-flex items-center', props.class)}>
305
- <span class={cn('text-primary font-medium', textSizes[size()])}>
309
+ {/* Content/chrome, not a control — was `text-primary` on the label AND
310
+ the dots (both must match: one phrase, "Thinking..."). Matches
311
+ TextShimmerLoader's neutral precedent above. */}
312
+ <span class={cn('text-foreground font-medium', textSizes[size()])}>
306
313
  {props.text ?? 'Thinking'}
307
314
  </span>
308
315
  <span class="inline-flex">
309
- <span class="text-primary animate-[loading-dots_1.4s_infinite_0.2s]">.</span>
310
- <span class="text-primary animate-[loading-dots_1.4s_infinite_0.4s]">.</span>
311
- <span class="text-primary animate-[loading-dots_1.4s_infinite_0.6s]">.</span>
316
+ <span class="text-foreground animate-[loading-dots_1.4s_infinite_0.2s]">.</span>
317
+ <span class="text-foreground animate-[loading-dots_1.4s_infinite_0.4s]">.</span>
318
+ <span class="text-foreground animate-[loading-dots_1.4s_infinite_0.6s]">.</span>
312
319
  </span>
313
320
  </div>
314
321
  );
@@ -27,7 +27,7 @@ function MessageSkills(props: MessageSkillsProps) {
27
27
  <div class={cn("flex items-center gap-1 flex-wrap", props.class)}>
28
28
  <For each={props.skills}>
29
29
  {(skill) => (
30
- <span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-violet-400/10 text-violet-600 dark:text-violet-400">
30
+ <span class="inline-flex items-center px-1.5 py-0.5 rounded text-micro font-medium bg-violet-400/10 text-violet-600 dark:text-violet-400">
31
31
  {skill.name}
32
32
  </span>
33
33
  )}