@tai42/studio-sdk 0.2.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 (422) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +15 -0
  3. package/README.md +318 -0
  4. package/SECURITY.md +51 -0
  5. package/dist/components/badge.d.ts +13 -0
  6. package/dist/components/badge.d.ts.map +1 -0
  7. package/dist/components/badge.js +14 -0
  8. package/dist/components/badge.js.map +1 -0
  9. package/dist/components/checkbox.d.ts +24 -0
  10. package/dist/components/checkbox.d.ts.map +1 -0
  11. package/dist/components/checkbox.js +31 -0
  12. package/dist/components/checkbox.js.map +1 -0
  13. package/dist/components/code-block.d.ts +7 -0
  14. package/dist/components/code-block.d.ts.map +1 -0
  15. package/dist/components/code-block.js +23 -0
  16. package/dist/components/code-block.js.map +1 -0
  17. package/dist/components/completion-input.d.ts +31 -0
  18. package/dist/components/completion-input.d.ts.map +1 -0
  19. package/dist/components/completion-input.js +61 -0
  20. package/dist/components/completion-input.js.map +1 -0
  21. package/dist/components/components.css +2212 -0
  22. package/dist/components/confirm-dialog.d.ts +40 -0
  23. package/dist/components/confirm-dialog.d.ts.map +1 -0
  24. package/dist/components/confirm-dialog.js +11 -0
  25. package/dist/components/confirm-dialog.js.map +1 -0
  26. package/dist/components/control-styles.d.ts +12 -0
  27. package/dist/components/control-styles.d.ts.map +1 -0
  28. package/dist/components/control-styles.js +14 -0
  29. package/dist/components/control-styles.js.map +1 -0
  30. package/dist/components/copy-field.d.ts +8 -0
  31. package/dist/components/copy-field.d.ts.map +1 -0
  32. package/dist/components/copy-field.js +106 -0
  33. package/dist/components/copy-field.js.map +1 -0
  34. package/dist/components/dialog.d.ts +21 -0
  35. package/dist/components/dialog.d.ts.map +1 -0
  36. package/dist/components/dialog.js +25 -0
  37. package/dist/components/dialog.js.map +1 -0
  38. package/dist/components/drawer.d.ts +22 -0
  39. package/dist/components/drawer.d.ts.map +1 -0
  40. package/dist/components/drawer.js +32 -0
  41. package/dist/components/drawer.js.map +1 -0
  42. package/dist/components/error-boundary.d.ts +33 -0
  43. package/dist/components/error-boundary.d.ts.map +1 -0
  44. package/dist/components/error-boundary.js +36 -0
  45. package/dist/components/error-boundary.js.map +1 -0
  46. package/dist/components/extension-combo-builder.d.ts +70 -0
  47. package/dist/components/extension-combo-builder.d.ts.map +1 -0
  48. package/dist/components/extension-combo-builder.js +154 -0
  49. package/dist/components/extension-combo-builder.js.map +1 -0
  50. package/dist/components/extension-grouping.d.ts +65 -0
  51. package/dist/components/extension-grouping.d.ts.map +1 -0
  52. package/dist/components/extension-grouping.js +78 -0
  53. package/dist/components/extension-grouping.js.map +1 -0
  54. package/dist/components/extension-picker.d.ts +35 -0
  55. package/dist/components/extension-picker.d.ts.map +1 -0
  56. package/dist/components/extension-picker.js +31 -0
  57. package/dist/components/extension-picker.js.map +1 -0
  58. package/dist/components/external-link.d.ts +19 -0
  59. package/dist/components/external-link.d.ts.map +1 -0
  60. package/dist/components/external-link.js +12 -0
  61. package/dist/components/external-link.js.map +1 -0
  62. package/dist/components/field.d.ts +64 -0
  63. package/dist/components/field.d.ts.map +1 -0
  64. package/dist/components/field.js +56 -0
  65. package/dist/components/field.js.map +1 -0
  66. package/dist/components/fleet-report.d.ts +39 -0
  67. package/dist/components/fleet-report.d.ts.map +1 -0
  68. package/dist/components/fleet-report.js +50 -0
  69. package/dist/components/fleet-report.js.map +1 -0
  70. package/dist/components/fonts.css +22 -0
  71. package/dist/components/icons.d.ts +142 -0
  72. package/dist/components/icons.d.ts.map +1 -0
  73. package/dist/components/icons.js +139 -0
  74. package/dist/components/icons.js.map +1 -0
  75. package/dist/components/inputs.d.ts +52 -0
  76. package/dist/components/inputs.d.ts.map +1 -0
  77. package/dist/components/inputs.js +35 -0
  78. package/dist/components/inputs.js.map +1 -0
  79. package/dist/components/json-diff.d.ts +40 -0
  80. package/dist/components/json-diff.d.ts.map +1 -0
  81. package/dist/components/json-diff.js +119 -0
  82. package/dist/components/json-diff.js.map +1 -0
  83. package/dist/components/json-tree.d.ts +8 -0
  84. package/dist/components/json-tree.d.ts.map +1 -0
  85. package/dist/components/json-tree.js +70 -0
  86. package/dist/components/json-tree.js.map +1 -0
  87. package/dist/components/modal-focus.d.ts +15 -0
  88. package/dist/components/modal-focus.d.ts.map +1 -0
  89. package/dist/components/modal-focus.js +136 -0
  90. package/dist/components/modal-focus.js.map +1 -0
  91. package/dist/components/page-header.d.ts +56 -0
  92. package/dist/components/page-header.d.ts.map +1 -0
  93. package/dist/components/page-header.js +24 -0
  94. package/dist/components/page-header.js.map +1 -0
  95. package/dist/components/primitives.d.ts +186 -0
  96. package/dist/components/primitives.d.ts.map +1 -0
  97. package/dist/components/primitives.js +204 -0
  98. package/dist/components/primitives.js.map +1 -0
  99. package/dist/components/progress-bar.d.ts +29 -0
  100. package/dist/components/progress-bar.d.ts.map +1 -0
  101. package/dist/components/progress-bar.js +48 -0
  102. package/dist/components/progress-bar.js.map +1 -0
  103. package/dist/components/radio-group.d.ts +37 -0
  104. package/dist/components/radio-group.d.ts.map +1 -0
  105. package/dist/components/radio-group.js +56 -0
  106. package/dist/components/radio-group.js.map +1 -0
  107. package/dist/components/reveal-input.d.ts +13 -0
  108. package/dist/components/reveal-input.d.ts.map +1 -0
  109. package/dist/components/reveal-input.js +31 -0
  110. package/dist/components/reveal-input.js.map +1 -0
  111. package/dist/components/scroll-region.d.ts +85 -0
  112. package/dist/components/scroll-region.d.ts.map +1 -0
  113. package/dist/components/scroll-region.js +574 -0
  114. package/dist/components/scroll-region.js.map +1 -0
  115. package/dist/components/select.d.ts +53 -0
  116. package/dist/components/select.d.ts.map +1 -0
  117. package/dist/components/select.js +33 -0
  118. package/dist/components/select.js.map +1 -0
  119. package/dist/components/table.d.ts +59 -0
  120. package/dist/components/table.d.ts.map +1 -0
  121. package/dist/components/table.js +20 -0
  122. package/dist/components/table.js.map +1 -0
  123. package/dist/components/tabs.d.ts +15 -0
  124. package/dist/components/tabs.d.ts.map +1 -0
  125. package/dist/components/tabs.js +13 -0
  126. package/dist/components/tabs.js.map +1 -0
  127. package/dist/components/tags.d.ts +46 -0
  128. package/dist/components/tags.d.ts.map +1 -0
  129. package/dist/components/tags.js +68 -0
  130. package/dist/components/tags.js.map +1 -0
  131. package/dist/components/tokens.css +392 -0
  132. package/dist/components/tokens.d.ts +15 -0
  133. package/dist/components/tokens.d.ts.map +1 -0
  134. package/dist/components/tokens.js +101 -0
  135. package/dist/components/tokens.js.map +1 -0
  136. package/dist/components/tool-picker.d.ts +24 -0
  137. package/dist/components/tool-picker.d.ts.map +1 -0
  138. package/dist/components/tool-picker.js +97 -0
  139. package/dist/components/tool-picker.js.map +1 -0
  140. package/dist/components/tooltip.d.ts +17 -0
  141. package/dist/components/tooltip.d.ts.map +1 -0
  142. package/dist/components/tooltip.js +18 -0
  143. package/dist/components/tooltip.js.map +1 -0
  144. package/dist/components/version-history-panel.d.ts +71 -0
  145. package/dist/components/version-history-panel.d.ts.map +1 -0
  146. package/dist/components/version-history-panel.js +148 -0
  147. package/dist/components/version-history-panel.js.map +1 -0
  148. package/dist/download.d.ts +7 -0
  149. package/dist/download.d.ts.map +1 -0
  150. package/dist/download.js +18 -0
  151. package/dist/download.js.map +1 -0
  152. package/dist/element-slot.d.ts +4 -0
  153. package/dist/element-slot.d.ts.map +1 -0
  154. package/dist/element-slot.js +20 -0
  155. package/dist/element-slot.js.map +1 -0
  156. package/dist/elicitation/ElicitationForm.d.ts +34 -0
  157. package/dist/elicitation/ElicitationForm.d.ts.map +1 -0
  158. package/dist/elicitation/ElicitationForm.js +24 -0
  159. package/dist/elicitation/ElicitationForm.js.map +1 -0
  160. package/dist/errors.d.ts +9 -0
  161. package/dist/errors.d.ts.map +1 -0
  162. package/dist/errors.js +15 -0
  163. package/dist/errors.js.map +1 -0
  164. package/dist/extension-combos.d.ts +13 -0
  165. package/dist/extension-combos.d.ts.map +1 -0
  166. package/dist/extension-combos.js +9 -0
  167. package/dist/extension-combos.js.map +1 -0
  168. package/dist/hooks/useApi.d.ts +1222 -0
  169. package/dist/hooks/useApi.d.ts.map +1 -0
  170. package/dist/hooks/useApi.js +22 -0
  171. package/dist/hooks/useApi.js.map +1 -0
  172. package/dist/hooks/useAuth.d.ts +13 -0
  173. package/dist/hooks/useAuth.d.ts.map +1 -0
  174. package/dist/hooks/useAuth.js +53 -0
  175. package/dist/hooks/useAuth.js.map +1 -0
  176. package/dist/hooks/useBreakpoint.d.ts +10 -0
  177. package/dist/hooks/useBreakpoint.d.ts.map +1 -0
  178. package/dist/hooks/useBreakpoint.js +66 -0
  179. package/dist/hooks/useBreakpoint.js.map +1 -0
  180. package/dist/hooks/useCapabilities.d.ts +88 -0
  181. package/dist/hooks/useCapabilities.d.ts.map +1 -0
  182. package/dist/hooks/useCapabilities.js +203 -0
  183. package/dist/hooks/useCapabilities.js.map +1 -0
  184. package/dist/hooks/useSse.d.ts +13 -0
  185. package/dist/hooks/useSse.d.ts.map +1 -0
  186. package/dist/hooks/useSse.js +298 -0
  187. package/dist/hooks/useSse.js.map +1 -0
  188. package/dist/hooks/useTheme.d.ts +20 -0
  189. package/dist/hooks/useTheme.d.ts.map +1 -0
  190. package/dist/hooks/useTheme.js +97 -0
  191. package/dist/hooks/useTheme.js.map +1 -0
  192. package/dist/hooks/useUnauthorized.d.ts +3 -0
  193. package/dist/hooks/useUnauthorized.d.ts.map +1 -0
  194. package/dist/hooks/useUnauthorized.js +18 -0
  195. package/dist/hooks/useUnauthorized.js.map +1 -0
  196. package/dist/host.d.ts +11 -0
  197. package/dist/host.d.ts.map +1 -0
  198. package/dist/host.js +10 -0
  199. package/dist/host.js.map +1 -0
  200. package/dist/index.d.ts +118 -0
  201. package/dist/index.d.ts.map +1 -0
  202. package/dist/index.js +91 -0
  203. package/dist/index.js.map +1 -0
  204. package/dist/navigation/context.d.ts +25 -0
  205. package/dist/navigation/context.d.ts.map +1 -0
  206. package/dist/navigation/context.js +45 -0
  207. package/dist/navigation/context.js.map +1 -0
  208. package/dist/navigation/index.d.ts +4 -0
  209. package/dist/navigation/index.d.ts.map +1 -0
  210. package/dist/navigation/index.js +2 -0
  211. package/dist/navigation/index.js.map +1 -0
  212. package/dist/navigation/types.d.ts +97 -0
  213. package/dist/navigation/types.d.ts.map +1 -0
  214. package/dist/navigation/types.js +16 -0
  215. package/dist/navigation/types.js.map +1 -0
  216. package/dist/plugin/host-state.d.ts +45 -0
  217. package/dist/plugin/host-state.d.ts.map +1 -0
  218. package/dist/plugin/host-state.js +62 -0
  219. package/dist/plugin/host-state.js.map +1 -0
  220. package/dist/plugin/registry.d.ts +35 -0
  221. package/dist/plugin/registry.d.ts.map +1 -0
  222. package/dist/plugin/registry.js +129 -0
  223. package/dist/plugin/registry.js.map +1 -0
  224. package/dist/plugin/types.d.ts +165 -0
  225. package/dist/plugin/types.d.ts.map +1 -0
  226. package/dist/plugin/types.js +2 -0
  227. package/dist/plugin/types.js.map +1 -0
  228. package/dist/plugin/version.d.ts +34 -0
  229. package/dist/plugin/version.d.ts.map +1 -0
  230. package/dist/plugin/version.js +37 -0
  231. package/dist/plugin/version.js.map +1 -0
  232. package/dist/query-keys.d.ts +31 -0
  233. package/dist/query-keys.d.ts.map +1 -0
  234. package/dist/query-keys.js +31 -0
  235. package/dist/query-keys.js.map +1 -0
  236. package/dist/schema-editor/SchemaEditor.d.ts +43 -0
  237. package/dist/schema-editor/SchemaEditor.d.ts.map +1 -0
  238. package/dist/schema-editor/SchemaEditor.js +106 -0
  239. package/dist/schema-editor/SchemaEditor.js.map +1 -0
  240. package/dist/schema-editor/index.d.ts +10 -0
  241. package/dist/schema-editor/index.d.ts.map +1 -0
  242. package/dist/schema-editor/index.js +8 -0
  243. package/dist/schema-editor/index.js.map +1 -0
  244. package/dist/schema-editor/lint.d.ts +37 -0
  245. package/dist/schema-editor/lint.d.ts.map +1 -0
  246. package/dist/schema-editor/lint.js +49 -0
  247. package/dist/schema-editor/lint.js.map +1 -0
  248. package/dist/schema-form/SchemaForm.d.ts +70 -0
  249. package/dist/schema-form/SchemaForm.d.ts.map +1 -0
  250. package/dist/schema-form/SchemaForm.js +13 -0
  251. package/dist/schema-form/SchemaForm.js.map +1 -0
  252. package/dist/schema-form/array-field.d.ts +20 -0
  253. package/dist/schema-form/array-field.d.ts.map +1 -0
  254. package/dist/schema-form/array-field.js +39 -0
  255. package/dist/schema-form/array-field.js.map +1 -0
  256. package/dist/schema-form/classify.d.ts +87 -0
  257. package/dist/schema-form/classify.d.ts.map +1 -0
  258. package/dist/schema-form/classify.js +199 -0
  259. package/dist/schema-form/classify.js.map +1 -0
  260. package/dist/schema-form/context.d.ts +4 -0
  261. package/dist/schema-form/context.d.ts.map +1 -0
  262. package/dist/schema-form/context.js +14 -0
  263. package/dist/schema-form/context.js.map +1 -0
  264. package/dist/schema-form/default-value.d.ts +8 -0
  265. package/dist/schema-form/default-value.d.ts.map +1 -0
  266. package/dist/schema-form/default-value.js +64 -0
  267. package/dist/schema-form/default-value.js.map +1 -0
  268. package/dist/schema-form/enum-field.d.ts +19 -0
  269. package/dist/schema-form/enum-field.d.ts.map +1 -0
  270. package/dist/schema-form/enum-field.js +26 -0
  271. package/dist/schema-form/enum-field.js.map +1 -0
  272. package/dist/schema-form/field-group.d.ts +9 -0
  273. package/dist/schema-form/field-group.d.ts.map +1 -0
  274. package/dist/schema-form/field-group.js +32 -0
  275. package/dist/schema-form/field-group.js.map +1 -0
  276. package/dist/schema-form/field-node.d.ts +24 -0
  277. package/dist/schema-form/field-node.d.ts.map +1 -0
  278. package/dist/schema-form/field-node.js +51 -0
  279. package/dist/schema-form/field-node.js.map +1 -0
  280. package/dist/schema-form/index.d.ts +13 -0
  281. package/dist/schema-form/index.d.ts.map +1 -0
  282. package/dist/schema-form/index.js +11 -0
  283. package/dist/schema-form/index.js.map +1 -0
  284. package/dist/schema-form/media-field.d.ts +24 -0
  285. package/dist/schema-form/media-field.d.ts.map +1 -0
  286. package/dist/schema-form/media-field.js +169 -0
  287. package/dist/schema-form/media-field.js.map +1 -0
  288. package/dist/schema-form/media.d.ts +40 -0
  289. package/dist/schema-form/media.d.ts.map +1 -0
  290. package/dist/schema-form/media.js +65 -0
  291. package/dist/schema-form/media.js.map +1 -0
  292. package/dist/schema-form/object-fields.d.ts +20 -0
  293. package/dist/schema-form/object-fields.d.ts.map +1 -0
  294. package/dist/schema-form/object-fields.js +24 -0
  295. package/dist/schema-form/object-fields.js.map +1 -0
  296. package/dist/schema-form/resolve.d.ts +41 -0
  297. package/dist/schema-form/resolve.d.ts.map +1 -0
  298. package/dist/schema-form/resolve.js +102 -0
  299. package/dist/schema-form/resolve.js.map +1 -0
  300. package/dist/schema-form/string-field.d.ts +20 -0
  301. package/dist/schema-form/string-field.d.ts.map +1 -0
  302. package/dist/schema-form/string-field.js +68 -0
  303. package/dist/schema-form/string-field.js.map +1 -0
  304. package/dist/schema-form/styles.d.ts +18 -0
  305. package/dist/schema-form/styles.d.ts.map +1 -0
  306. package/dist/schema-form/styles.js +18 -0
  307. package/dist/schema-form/styles.js.map +1 -0
  308. package/dist/schema-form/types.d.ts +67 -0
  309. package/dist/schema-form/types.d.ts.map +1 -0
  310. package/dist/schema-form/types.js +10 -0
  311. package/dist/schema-form/types.js.map +1 -0
  312. package/dist/schema-form/union-field.d.ts +24 -0
  313. package/dist/schema-form/union-field.d.ts.map +1 -0
  314. package/dist/schema-form/union-field.js +55 -0
  315. package/dist/schema-form/union-field.js.map +1 -0
  316. package/dist/schema-form/union.d.ts +4 -0
  317. package/dist/schema-form/union.d.ts.map +1 -0
  318. package/dist/schema-form/union.js +68 -0
  319. package/dist/schema-form/union.js.map +1 -0
  320. package/dist/schema-form/unsupported-notice.d.ts +18 -0
  321. package/dist/schema-form/unsupported-notice.d.ts.map +1 -0
  322. package/dist/schema-form/unsupported-notice.js +8 -0
  323. package/dist/schema-form/unsupported-notice.js.map +1 -0
  324. package/dist/schema-form/validate.d.ts +18 -0
  325. package/dist/schema-form/validate.d.ts.map +1 -0
  326. package/dist/schema-form/validate.js +162 -0
  327. package/dist/schema-form/validate.js.map +1 -0
  328. package/dist/structured-output/StructuredOutput.d.ts +27 -0
  329. package/dist/structured-output/StructuredOutput.d.ts.map +1 -0
  330. package/dist/structured-output/StructuredOutput.js +41 -0
  331. package/dist/structured-output/StructuredOutput.js.map +1 -0
  332. package/dist/testing.d.ts +80 -0
  333. package/dist/testing.d.ts.map +1 -0
  334. package/dist/testing.js +207 -0
  335. package/dist/testing.js.map +1 -0
  336. package/package.json +101 -0
  337. package/src/components/badge.tsx +31 -0
  338. package/src/components/checkbox.tsx +93 -0
  339. package/src/components/code-block.tsx +37 -0
  340. package/src/components/completion-input.tsx +137 -0
  341. package/src/components/components.css +2212 -0
  342. package/src/components/confirm-dialog.tsx +77 -0
  343. package/src/components/control-styles.ts +14 -0
  344. package/src/components/copy-field.tsx +178 -0
  345. package/src/components/dialog.tsx +76 -0
  346. package/src/components/drawer.tsx +83 -0
  347. package/src/components/error-boundary.tsx +56 -0
  348. package/src/components/extension-combo-builder.tsx +305 -0
  349. package/src/components/extension-grouping.ts +116 -0
  350. package/src/components/extension-picker.tsx +97 -0
  351. package/src/components/external-link.tsx +40 -0
  352. package/src/components/field.tsx +169 -0
  353. package/src/components/fleet-report.tsx +136 -0
  354. package/src/components/fonts.css +22 -0
  355. package/src/components/icons.tsx +504 -0
  356. package/src/components/inputs.tsx +125 -0
  357. package/src/components/json-diff.tsx +192 -0
  358. package/src/components/json-tree.tsx +129 -0
  359. package/src/components/modal-focus.ts +150 -0
  360. package/src/components/page-header.tsx +107 -0
  361. package/src/components/primitives.tsx +489 -0
  362. package/src/components/progress-bar.tsx +100 -0
  363. package/src/components/radio-group.tsx +160 -0
  364. package/src/components/reveal-input.tsx +87 -0
  365. package/src/components/scroll-region.tsx +703 -0
  366. package/src/components/select.tsx +149 -0
  367. package/src/components/table.tsx +94 -0
  368. package/src/components/tabs.tsx +51 -0
  369. package/src/components/tags.tsx +137 -0
  370. package/src/components/tokens.css +392 -0
  371. package/src/components/tokens.ts +102 -0
  372. package/src/components/tool-picker.tsx +175 -0
  373. package/src/components/tooltip.tsx +46 -0
  374. package/src/components/version-history-panel.tsx +407 -0
  375. package/src/css.d.ts +2 -0
  376. package/src/download.ts +17 -0
  377. package/src/element-slot.ts +22 -0
  378. package/src/elicitation/ElicitationForm.tsx +87 -0
  379. package/src/errors.ts +12 -0
  380. package/src/extension-combos.ts +18 -0
  381. package/src/hooks/useApi.ts +27 -0
  382. package/src/hooks/useAuth.ts +67 -0
  383. package/src/hooks/useBreakpoint.ts +78 -0
  384. package/src/hooks/useCapabilities.ts +242 -0
  385. package/src/hooks/useSse.ts +314 -0
  386. package/src/hooks/useTheme.ts +133 -0
  387. package/src/hooks/useUnauthorized.ts +20 -0
  388. package/src/host.ts +15 -0
  389. package/src/index.ts +267 -0
  390. package/src/navigation/context.tsx +90 -0
  391. package/src/navigation/index.ts +9 -0
  392. package/src/navigation/types.ts +90 -0
  393. package/src/plugin/host-state.ts +93 -0
  394. package/src/plugin/registry.ts +174 -0
  395. package/src/plugin/types.ts +178 -0
  396. package/src/plugin/version.ts +44 -0
  397. package/src/query-keys.ts +34 -0
  398. package/src/schema-editor/SchemaEditor.tsx +186 -0
  399. package/src/schema-editor/index.ts +9 -0
  400. package/src/schema-editor/lint.ts +70 -0
  401. package/src/schema-form/SchemaForm.tsx +107 -0
  402. package/src/schema-form/array-field.tsx +110 -0
  403. package/src/schema-form/classify.ts +302 -0
  404. package/src/schema-form/context.ts +17 -0
  405. package/src/schema-form/default-value.ts +65 -0
  406. package/src/schema-form/enum-field.tsx +65 -0
  407. package/src/schema-form/field-group.tsx +71 -0
  408. package/src/schema-form/field-node.tsx +161 -0
  409. package/src/schema-form/index.ts +12 -0
  410. package/src/schema-form/media-field.tsx +281 -0
  411. package/src/schema-form/media.ts +69 -0
  412. package/src/schema-form/object-fields.tsx +74 -0
  413. package/src/schema-form/resolve.ts +114 -0
  414. package/src/schema-form/string-field.tsx +161 -0
  415. package/src/schema-form/styles.ts +20 -0
  416. package/src/schema-form/types.ts +71 -0
  417. package/src/schema-form/union-field.tsx +122 -0
  418. package/src/schema-form/union.ts +78 -0
  419. package/src/schema-form/unsupported-notice.tsx +39 -0
  420. package/src/schema-form/validate.ts +183 -0
  421. package/src/structured-output/StructuredOutput.tsx +81 -0
  422. package/src/testing.ts +235 -0
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The pure parse + structural lint behind the `SchemaEditor` module. It never throws:
3
+ * a malformed value IS the result it reports. Full meta-schema validation stays
4
+ * server-side (the backend 400s carry the detail); this is the fast, loud client
5
+ * guard that keeps an obviously-broken schema from ever leaving the dialog.
6
+ */
7
+
8
+ /** The parse/lint outcome for one editor text buffer. */
9
+ export interface SchemaLintResult {
10
+ /**
11
+ * The parsed schema dict when the text is a syntactically valid JSON object, or
12
+ * `null` when the text is empty or does not parse. Carries the parsed object even
13
+ * when a lint (e.g. a missing `title`) fails, so the preview can still show the
14
+ * authored shape — the consumer gates on {@link valid}, never on this alone.
15
+ */
16
+ readonly schema: Record<string, unknown> | null;
17
+ /**
18
+ * True when the text is empty or a valid schema. False when the text is non-empty
19
+ * but fails to parse or fails a structural lint — the consumer blocks submit.
20
+ */
21
+ readonly valid: boolean;
22
+ /** A loud, human-readable message when {@link valid} is false; else `undefined`. */
23
+ readonly error: string | undefined;
24
+ }
25
+
26
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
27
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
28
+ }
29
+
30
+ /**
31
+ * Parse and structurally lint a JSON-Schema editor buffer.
32
+ *
33
+ * - Empty text is VALID (an unset, optional schema) with a `null` schema.
34
+ * - Malformed JSON is INVALID; the parser message is surfaced verbatim.
35
+ * - A top-level value that is not a JSON object (an array or a scalar) is INVALID.
36
+ * - When `requireTitle`, a top-level string `"title"` is REQUIRED (the
37
+ * `response_format` contract the backend enforces); its absence is INVALID.
38
+ *
39
+ * The parsed object is returned untouched — no key is normalized, reordered, or
40
+ * dropped — so a schema carrying `$defs`/`anyOf` round-trips exactly.
41
+ */
42
+ export function lintSchemaText(text: string, requireTitle: boolean): SchemaLintResult {
43
+ const trimmed = text.trim();
44
+ if (trimmed === '') return { schema: null, valid: true, error: undefined };
45
+
46
+ let parsed: unknown;
47
+ try {
48
+ parsed = JSON.parse(trimmed);
49
+ } catch (error) {
50
+ return {
51
+ schema: null,
52
+ valid: false,
53
+ error: error instanceof Error ? error.message : 'Invalid JSON.',
54
+ };
55
+ }
56
+
57
+ if (!isPlainObject(parsed)) {
58
+ return { schema: null, valid: false, error: 'The schema must be a JSON object.' };
59
+ }
60
+
61
+ if (requireTitle && typeof parsed.title !== 'string') {
62
+ return {
63
+ schema: parsed,
64
+ valid: false,
65
+ error: 'A top-level "title" string is required for this schema.',
66
+ };
67
+ }
68
+
69
+ return { schema: parsed, valid: true, error: undefined };
70
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * `SchemaForm` — the controlled, JSON-Schema-driven auto-form. Given a schema
3
+ * (as Pydantic v2 emits) plus a value and `onChange`, it renders the form and
4
+ * emits a plain JS value; the CALLER owns the submit button. It is the
5
+ * load-bearing renderer reused by the tools run panel and the interactions form
6
+ * answer format, which is why it lives in the SDK (features cannot import each
7
+ * other).
8
+ *
9
+ * SAFETY: every schema-derived string (title, description, enum label, notice)
10
+ * is rendered as TEXT through the DS components — React escapes it. No markdown,
11
+ * no HTML, no `dangerouslySetInnerHTML` sink ever touches a schema-supplied
12
+ * string. Pinned by a test.
13
+ *
14
+ * MEDIA UPLOADS: a string field renders as a file-upload / drag-drop control
15
+ * (instead of a text box) when its schema carries media annotations, so an
16
+ * image/file input stays a JSON-serializable string that round-trips through the
17
+ * normal submit path. The annotation contract:
18
+ * - `contentEncoding: "base64"` + `contentMediaType` (e.g. `"image/*"`,
19
+ * `"application/pdf"`) → the emitted value is the raw base64 body (no
20
+ * `data:` prefix); `contentMediaType` also constrains the accepted MIME.
21
+ * - `format: "data-url"` (optionally with `contentMediaType`) → the emitted
22
+ * value is a full `data:<mime>;base64,<body>` URL.
23
+ * - `contentMaxBytes` pins a per-field size cap (bytes); otherwise the
24
+ * {@link SchemaFormProps.maxUploadBytes} prop, else {@link DEFAULT_MAX_UPLOAD_BYTES}.
25
+ * An over-cap value or a MIME that does not match `contentMediaType` is REJECTED
26
+ * with a visible error — never silently truncated or accepted. The cap holds on
27
+ * EVERY input path (file picker, the paste fallback, and `validateAgainstSchema`),
28
+ * measured against the value's DECODED byte size. A text fallback (paste a
29
+ * URL/base64) stays available. The uploaded filename renders ESCAPED.
30
+ *
31
+ * `contentMaxBytes` is a NONSTANDARD JSON-Schema extension keyword (a standard
32
+ * validator ignores it); these client-side caps are UX guards, and the SERVER
33
+ * MUST independently validate the decoded size. The base64 decoding and cap
34
+ * checks live in the sibling `media` module.
35
+ */
36
+ import type { ReactNode } from 'react';
37
+
38
+ import { CompletionProviderContext, MaxUploadBytesContext } from './context';
39
+ import { FieldNode } from './field-node';
40
+ import { DEFAULT_MAX_UPLOAD_BYTES } from './media';
41
+ import { stackClass } from './styles';
42
+ import type { JsonSchema, SchemaFormErrors } from './types';
43
+
44
+ // -- Public API --------------------------------------------------------------
45
+
46
+ /**
47
+ * Fetch argument-value suggestions for a string field. `argName` is the field's
48
+ * path within the form (a property key at the root, a dotted path when nested);
49
+ * `partial` is the value typed so far. The caller owns the source (e.g. an MCP
50
+ * `completion/complete` call) — SchemaForm only routes string fields through the
51
+ * completion-backed input when a provider is supplied.
52
+ */
53
+ export type CompletionProvider = (argName: string, partial: string) => Promise<string[]>;
54
+
55
+ export interface SchemaFormProps {
56
+ readonly schema: JsonSchema;
57
+ readonly value: unknown;
58
+ readonly onChange: (value: unknown) => void;
59
+ readonly errors?: SchemaFormErrors;
60
+ readonly idPrefix?: string;
61
+ /**
62
+ * Optional. When supplied, every string field renders through the
63
+ * completion-backed input (argument autocomplete); when omitted, string fields
64
+ * render as a plain text input — the default, unchanged behaviour.
65
+ */
66
+ readonly completionProvider?: CompletionProvider;
67
+ /**
68
+ * Optional. Default byte cap for media-upload fields (see the module
69
+ * doc-comment). A field's own `contentMaxBytes` annotation overrides it;
70
+ * absent both, {@link DEFAULT_MAX_UPLOAD_BYTES} applies.
71
+ */
72
+ readonly maxUploadBytes?: number;
73
+ }
74
+
75
+ /**
76
+ * Render a controlled form for `schema`. The root is usually an object (a tool's
77
+ * input schema); a scalar/array/union root renders as a single field group.
78
+ */
79
+ export function SchemaForm({
80
+ schema,
81
+ value,
82
+ onChange,
83
+ errors,
84
+ idPrefix = 'schema-form',
85
+ completionProvider,
86
+ maxUploadBytes = DEFAULT_MAX_UPLOAD_BYTES,
87
+ }: SchemaFormProps): ReactNode {
88
+ return (
89
+ <CompletionProviderContext.Provider value={completionProvider}>
90
+ <MaxUploadBytesContext.Provider value={maxUploadBytes}>
91
+ <div className={stackClass} data-testid={idPrefix}>
92
+ <FieldNode
93
+ schema={schema}
94
+ root={schema}
95
+ value={value}
96
+ onChange={onChange}
97
+ path=""
98
+ label={undefined}
99
+ required
100
+ errors={errors}
101
+ idPrefix={idPrefix}
102
+ />
103
+ </div>
104
+ </MaxUploadBytesContext.Provider>
105
+ </CompletionProviderContext.Provider>
106
+ );
107
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * The array field renderer. Each item is a nested {@link FieldNode}; edits map
3
+ * the list to a new array (add appends a seeded item, remove filters by index).
4
+ * The whole list re-renders as one controlled value, so index keys are correct.
5
+ */
6
+ import type { ReactNode } from 'react';
7
+
8
+ import { CloseIcon } from '../components/icons';
9
+ import { Button } from '../components/primitives';
10
+ import { classifySchema } from './classify';
11
+ import { defaultValueForSchema } from './default-value';
12
+ import { FieldGroup } from './field-group';
13
+ import { FieldNode } from './field-node';
14
+ import type { JsonSchema, SchemaFormErrors } from './types';
15
+
16
+ export function ArrayField({
17
+ heading,
18
+ description,
19
+ error,
20
+ items,
21
+ root,
22
+ value,
23
+ onChange,
24
+ path,
25
+ errors,
26
+ idPrefix,
27
+ }: {
28
+ heading: string;
29
+ description: string | undefined;
30
+ error: string | undefined;
31
+ items: JsonSchema;
32
+ root: JsonSchema;
33
+ value: unknown;
34
+ onChange: (value: unknown) => void;
35
+ path: string;
36
+ errors: SchemaFormErrors | undefined;
37
+ idPrefix: string;
38
+ }): ReactNode {
39
+ const list = Array.isArray(value) ? (value as unknown[]) : [];
40
+ const addItem = (): void => {
41
+ onChange([...list, newItemValue(items, root)]);
42
+ };
43
+ const removeItem = (index: number): void => {
44
+ onChange(list.filter((_, position) => position !== index));
45
+ };
46
+ const setItem = (index: number, next: unknown): void => {
47
+ onChange(list.map((item, position) => (position === index ? next : item)));
48
+ };
49
+
50
+ return (
51
+ <FieldGroup heading={heading} description={description} error={error} atRoot={false}>
52
+ {list.length === 0 ? <span className="tai-field-hint">No items</span> : null}
53
+ {list.map((item, index) => (
54
+ <div
55
+ // Index keys are correct here: items have no stable identity and the
56
+ // whole list re-renders as one controlled value on every edit.
57
+ key={index}
58
+ className="tai-row"
59
+ >
60
+ {/* The item takes the row's spare width; the Remove button keeps its own. */}
61
+ <div style={{ flex: 1 }}>
62
+ <FieldNode
63
+ schema={items}
64
+ root={root}
65
+ value={item}
66
+ onChange={(next) => {
67
+ setItem(index, next);
68
+ }}
69
+ path={`${path}[${String(index)}]`}
70
+ label={`Item ${String(index + 1)}`}
71
+ required
72
+ errors={errors}
73
+ idPrefix={idPrefix}
74
+ />
75
+ </div>
76
+ <Button
77
+ type="button"
78
+ variant="secondary"
79
+ aria-label={`Remove item ${String(index + 1)}`}
80
+ onClick={() => {
81
+ removeItem(index);
82
+ }}
83
+ >
84
+ <CloseIcon />
85
+ Remove
86
+ </Button>
87
+ </div>
88
+ ))}
89
+ <div>
90
+ <Button type="button" variant="secondary" onClick={addItem}>
91
+ Add item
92
+ </Button>
93
+ </div>
94
+ </FieldGroup>
95
+ );
96
+ }
97
+
98
+ function newItemValue(schema: JsonSchema, root: JsonSchema): unknown {
99
+ const seeded = defaultValueForSchema(schema, root);
100
+ if (seeded !== undefined) return seeded;
101
+ const classified = classifySchema(schema, root);
102
+ switch (classified.model.kind) {
103
+ case 'string':
104
+ return '';
105
+ case 'boolean':
106
+ return false;
107
+ default:
108
+ return undefined;
109
+ }
110
+ }
@@ -0,0 +1,302 @@
1
+ /**
2
+ * The single source of truth for "what kind of field is this schema?". Both the
3
+ * renderer and the validator classify through here so a construct is treated
4
+ * identically everywhere. Classification resolves `$ref`, normalizes `anyOf`/
5
+ * `oneOf` (null-union → nullable single field; multi-member → variant selector),
6
+ * and lands every unrecognized construct on the explicit `unsupported` kind —
7
+ * a field is never silently dropped.
8
+ */
9
+ import {
10
+ isNullSchema,
11
+ resolveRef,
12
+ scalarLabel,
13
+ typeList,
14
+ unionMembers,
15
+ variantLabel,
16
+ variantTag,
17
+ type EnumOption,
18
+ } from './resolve';
19
+ import type { JsonSchema } from './types';
20
+
21
+ /**
22
+ * How a string field accepts binary content as a JSON-serializable string.
23
+ * Derived from a field's JSON-Schema media annotations (see the `SchemaForm` module).
24
+ */
25
+ export interface MediaUpload {
26
+ /**
27
+ * `base64` — the emitted value is the raw base64 body (no `data:` prefix),
28
+ * matching `contentEncoding: "base64"`. `data-url` — the emitted value is a
29
+ * full `data:<mime>;base64,<body>` URL, matching `format: "data-url"`.
30
+ */
31
+ readonly encoding: 'base64' | 'data-url';
32
+ /**
33
+ * The `contentMediaType` constraint (e.g. `image/*`, `application/pdf`), used
34
+ * for MIME validation and the file picker's `accept`. `undefined` for a bare
35
+ * `format: "data-url"` field with no declared type.
36
+ */
37
+ readonly mediaType: string | undefined;
38
+ /** A per-field byte cap from `contentMaxBytes`, if the schema pins one. */
39
+ readonly maxBytes: number | undefined;
40
+ }
41
+
42
+ /** One selectable variant of a union field. */
43
+ export interface UnionVariant {
44
+ readonly label: string;
45
+ /** The discriminator tag value, or `undefined` for a non-discriminated union. */
46
+ readonly tag: unknown;
47
+ /** The variant schema as written (unresolved — the renderer resolves it). */
48
+ readonly schema: JsonSchema;
49
+ }
50
+
51
+ /** The renderer-facing classification of a schema node. */
52
+ export type FieldModel =
53
+ | { readonly kind: 'const'; readonly value: unknown }
54
+ | { readonly kind: 'enum'; readonly options: readonly EnumOption[] }
55
+ | {
56
+ readonly kind: 'string';
57
+ readonly format: string | undefined;
58
+ /** Present when media annotations opt the field into the upload control. */
59
+ readonly media: MediaUpload | undefined;
60
+ }
61
+ | { readonly kind: 'number'; readonly integer: boolean }
62
+ | { readonly kind: 'boolean' }
63
+ | { readonly kind: 'array'; readonly items: JsonSchema }
64
+ | {
65
+ readonly kind: 'object';
66
+ readonly properties: readonly (readonly [string, JsonSchema])[];
67
+ readonly required: ReadonlySet<string>;
68
+ }
69
+ | {
70
+ readonly kind: 'union';
71
+ readonly variants: readonly UnionVariant[];
72
+ readonly discriminator: string | undefined;
73
+ }
74
+ | { readonly kind: 'unsupported'; readonly reason: string };
75
+
76
+ /** A fully classified field: its model plus resolved metadata. */
77
+ export interface ClassifiedField {
78
+ readonly model: FieldModel;
79
+ /** True when JSON `null` is an accepted value (a null-union member). */
80
+ readonly nullable: boolean;
81
+ /** The effective (ref-resolved) schema this classification describes. */
82
+ readonly schema: JsonSchema;
83
+ readonly title: string | undefined;
84
+ readonly description: string | undefined;
85
+ }
86
+
87
+ function firstString(...values: readonly unknown[]): string | undefined {
88
+ for (const value of values) {
89
+ if (typeof value === 'string' && value.length > 0) return value;
90
+ }
91
+ return undefined;
92
+ }
93
+
94
+ /**
95
+ * Detect the media-upload annotations on a (ref-resolved) string schema. A field
96
+ * opts into the upload control when it carries `contentEncoding: "base64"` plus a
97
+ * `contentMediaType`, OR `format: "data-url"`. Anything else is a plain string.
98
+ */
99
+ function mediaUpload(schema: JsonSchema): MediaUpload | undefined {
100
+ const mediaType =
101
+ typeof schema.contentMediaType === 'string' ? schema.contentMediaType : undefined;
102
+ const maxBytes =
103
+ typeof schema.contentMaxBytes === 'number' && schema.contentMaxBytes > 0
104
+ ? schema.contentMaxBytes
105
+ : undefined;
106
+ if (schema.format === 'data-url') {
107
+ return { encoding: 'data-url', mediaType, maxBytes };
108
+ }
109
+ if (schema.contentEncoding === 'base64' && mediaType !== undefined) {
110
+ return { encoding: 'base64', mediaType, maxBytes };
111
+ }
112
+ return undefined;
113
+ }
114
+
115
+ function unsupported(
116
+ schema: JsonSchema,
117
+ reason: string,
118
+ title: string | undefined,
119
+ description: string | undefined,
120
+ ): ClassifiedField {
121
+ return { model: { kind: 'unsupported', reason }, nullable: false, schema, title, description };
122
+ }
123
+
124
+ /**
125
+ * Classify a schema node (resolving `$ref` first). `wrapper` metadata (a
126
+ * ref-site title/description) is threaded down so a `{ $ref, title }` or a
127
+ * null-union wrapper keeps its property-level label.
128
+ */
129
+ export function classifySchema(raw: JsonSchema, root: JsonSchema): ClassifiedField {
130
+ const resolved = resolveRef(raw, root);
131
+ const title = firstString(raw.title, resolved.title);
132
+ const description = firstString(raw.description, resolved.description);
133
+
134
+ // const pins a single value.
135
+ if ('const' in resolved) {
136
+ return {
137
+ model: { kind: 'const', value: resolved.const },
138
+ nullable: false,
139
+ schema: resolved,
140
+ title,
141
+ description,
142
+ };
143
+ }
144
+
145
+ // enum → a fixed choice list.
146
+ if (resolved.enum !== undefined) {
147
+ const options: EnumOption[] = resolved.enum.map((value) => ({
148
+ value,
149
+ label: scalarLabel(value),
150
+ }));
151
+ const nullable = resolved.enum.some((value) => value === null);
152
+ return { model: { kind: 'enum', options }, nullable, schema: resolved, title, description };
153
+ }
154
+
155
+ // anyOf/oneOf union normalization.
156
+ const members = unionMembers(resolved);
157
+ if (members !== undefined) {
158
+ const nonNull = members.filter((member) => !isNullSchema(resolveRef(member, root)));
159
+ const nullable = nonNull.length !== members.length;
160
+ const soleNonNull = nonNull[0];
161
+ if (soleNonNull === undefined) {
162
+ return unsupported(resolved, 'union with no non-null members', title, description);
163
+ }
164
+ if (nonNull.length === 1) {
165
+ const inner = classifySchema(soleNonNull, root);
166
+ // Media annotations (`contentEncoding`/`contentMediaType`/`format: data-url`)
167
+ // that land on the OUTER null-union wrapper of an OPTIONAL field would
168
+ // otherwise be lost when delegating to the inner string member, silently
169
+ // degrading an optional upload field to a plain text box. Re-detect them on
170
+ // the wrapper and attach to the inner string model.
171
+ const model =
172
+ inner.model.kind === 'string' && inner.model.media === undefined
173
+ ? { ...inner.model, media: mediaUpload(resolved) }
174
+ : inner.model;
175
+ return {
176
+ model,
177
+ nullable: nullable || inner.nullable,
178
+ schema: inner.schema,
179
+ title: title ?? inner.title,
180
+ description: description ?? inner.description,
181
+ };
182
+ }
183
+ const discriminator = resolved.discriminator?.propertyName;
184
+ const variants: UnionVariant[] = nonNull.map((member, index) => {
185
+ const tag = discriminator === undefined ? undefined : variantTag(member, discriminator, root);
186
+ return { label: variantLabel(member, index, tag, root), tag, schema: member };
187
+ });
188
+ return {
189
+ model: { kind: 'union', variants, discriminator },
190
+ nullable,
191
+ schema: resolved,
192
+ title,
193
+ description,
194
+ };
195
+ }
196
+
197
+ // allOf: Pydantic wraps a single $ref this way; a multi-member intersection is
198
+ // not a form-renderable construct.
199
+ if (resolved.allOf !== undefined) {
200
+ const soleAllOf = resolved.allOf[0];
201
+ if (resolved.allOf.length === 1 && soleAllOf !== undefined) {
202
+ const inner = classifySchema(soleAllOf, root);
203
+ return {
204
+ model: inner.model,
205
+ nullable: inner.nullable,
206
+ schema: inner.schema,
207
+ title: title ?? inner.title,
208
+ description: description ?? inner.description,
209
+ };
210
+ }
211
+ return unsupported(resolved, 'allOf intersection of multiple schemas', title, description);
212
+ }
213
+
214
+ // Type-driven classification.
215
+ const types = typeList(resolved);
216
+ const nonNullTypes = types.filter((type) => type !== 'null');
217
+ const nullableByType = nonNullTypes.length !== types.length;
218
+
219
+ if (nonNullTypes.length === 0) {
220
+ return unsupported(resolved, 'schema declares no renderable type', title, description);
221
+ }
222
+ if (nonNullTypes.length > 1) {
223
+ return unsupported(
224
+ resolved,
225
+ `multiple JSON types (${nonNullTypes.join(', ')}) without a discriminator`,
226
+ title,
227
+ description,
228
+ );
229
+ }
230
+
231
+ const type = nonNullTypes[0];
232
+ if (type === undefined) {
233
+ return unsupported(resolved, 'schema declares no renderable type', title, description);
234
+ }
235
+ switch (type) {
236
+ case 'string':
237
+ return {
238
+ model: { kind: 'string', format: resolved.format, media: mediaUpload(resolved) },
239
+ nullable: nullableByType,
240
+ schema: resolved,
241
+ title,
242
+ description,
243
+ };
244
+ case 'number':
245
+ return {
246
+ model: { kind: 'number', integer: false },
247
+ nullable: nullableByType,
248
+ schema: resolved,
249
+ title,
250
+ description,
251
+ };
252
+ case 'integer':
253
+ return {
254
+ model: { kind: 'number', integer: true },
255
+ nullable: nullableByType,
256
+ schema: resolved,
257
+ title,
258
+ description,
259
+ };
260
+ case 'boolean':
261
+ return {
262
+ model: { kind: 'boolean' },
263
+ nullable: nullableByType,
264
+ schema: resolved,
265
+ title,
266
+ description,
267
+ };
268
+ case 'array': {
269
+ if (resolved.items === undefined) {
270
+ return unsupported(resolved, 'array without an items schema', title, description);
271
+ }
272
+ return {
273
+ model: { kind: 'array', items: resolved.items },
274
+ nullable: nullableByType,
275
+ schema: resolved,
276
+ title,
277
+ description,
278
+ };
279
+ }
280
+ case 'object': {
281
+ if (resolved.properties === undefined) {
282
+ return unsupported(
283
+ resolved,
284
+ 'free-form object (additionalProperties map) is not supported',
285
+ title,
286
+ description,
287
+ );
288
+ }
289
+ const properties: (readonly [string, JsonSchema])[] = Object.entries(resolved.properties);
290
+ const required = new Set(resolved.required ?? []);
291
+ return {
292
+ model: { kind: 'object', properties, required },
293
+ nullable: nullableByType,
294
+ schema: resolved,
295
+ title,
296
+ description,
297
+ };
298
+ }
299
+ default:
300
+ return unsupported(resolved, `unrecognized type "${String(type)}"`, title, description);
301
+ }
302
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Ambient React contexts for the schema-form tree: the optional argument
3
+ * completion provider and the default media-upload byte cap. A field deep in the
4
+ * tree reads these here rather than every intermediate node drilling the prop.
5
+ */
6
+ import { createContext } from 'react';
7
+
8
+ import { DEFAULT_MAX_UPLOAD_BYTES } from './media';
9
+ import type { CompletionProvider } from './SchemaForm';
10
+
11
+ // Ambient completion provider for the whole form. A string field deep in the
12
+ // tree reads it here rather than every intermediate node drilling the prop.
13
+ export const CompletionProviderContext = createContext<CompletionProvider | undefined>(undefined);
14
+
15
+ // Ambient default upload cap for the whole form; a media field reads it here so
16
+ // intermediate object/array nodes never drill the prop.
17
+ export const MaxUploadBytesContext = createContext<number>(DEFAULT_MAX_UPLOAD_BYTES);
@@ -0,0 +1,65 @@
1
+ /**
2
+ * `defaultValueForSchema` — the initial form value for a schema, honoring
3
+ * `default`/`const` and seeding required nested structure.
4
+ *
5
+ * Inclusion rules keep the seed honest:
6
+ * - a REQUIRED property is seeded (objects → a nested seed, arrays → `[]`); a
7
+ * required scalar with no default is left ABSENT so validation flags it
8
+ * rather than inventing a value;
9
+ * - an OPTIONAL property is included only when it carries an explicit
10
+ * `default` — an optional object/array is NOT auto-materialized, so its own
11
+ * required children can't raise spurious errors on a field the user never
12
+ * opted into.
13
+ */
14
+ import { classifySchema } from './classify';
15
+ import type { JsonSchema } from './types';
16
+
17
+ /** The explicit `default` (wrapper takes precedence over the resolved node), if any. */
18
+ function explicitDefault(
19
+ raw: JsonSchema,
20
+ resolved: JsonSchema,
21
+ ): { present: boolean; value: unknown } {
22
+ if ('default' in raw) return { present: true, value: raw.default };
23
+ if ('default' in resolved) return { present: true, value: resolved.default };
24
+ return { present: false, value: undefined };
25
+ }
26
+
27
+ /**
28
+ * The initial value for a schema. `root` carries the `$defs` document for
29
+ * `$ref` resolution and defaults to the schema itself (the common
30
+ * whole-tool-schema call).
31
+ */
32
+ export function defaultValueForSchema(schema: JsonSchema, root: JsonSchema = schema): unknown {
33
+ const classified = classifySchema(schema, root);
34
+ const explicit = explicitDefault(schema, classified.schema);
35
+ if (explicit.present) return explicit.value;
36
+
37
+ const { model } = classified;
38
+ switch (model.kind) {
39
+ case 'const':
40
+ return model.value;
41
+ case 'object': {
42
+ const result: Record<string, unknown> = {};
43
+ for (const [name, propSchema] of model.properties) {
44
+ if (model.required.has(name)) {
45
+ const seeded = defaultValueForSchema(propSchema, root);
46
+ if (seeded !== undefined) result[name] = seeded;
47
+ continue;
48
+ }
49
+ const propClassified = classifySchema(propSchema, root);
50
+ const propDefault = explicitDefault(propSchema, propClassified.schema);
51
+ if (propDefault.present) result[name] = propDefault.value;
52
+ }
53
+ return result;
54
+ }
55
+ case 'array':
56
+ return [];
57
+ case 'enum':
58
+ case 'string':
59
+ case 'number':
60
+ case 'boolean':
61
+ case 'union':
62
+ case 'unsupported':
63
+ return undefined;
64
+ }
65
+ }