@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,314 @@
1
+ /**
2
+ * `useSse` — the live interactions stream, implemented over the api-client's
3
+ * `streamInteractions` (fetch + ReadableStream, never EventSource).
4
+ *
5
+ * SSE event contract: events `interaction.add` /
6
+ * `interaction.answered` / `interaction.removed` / `interaction.backlog_done`.
7
+ * The `add` frame (backlog on connect + live tail) carries the full question;
8
+ * `answered` / `removed` frames carry only ids. On (re)connect the server
9
+ * replays the current pending set as the backlog; this hook treats that backlog
10
+ * as authoritative — an entry not re-seen by `interaction.backlog_done` is
11
+ * dropped, so an interaction removed while the client was disconnected does not
12
+ * linger. `add` frames are at-least-once, so entries are de-duplicated by
13
+ * `interaction_id`, and a redelivered `add` preserves the client `answered` flag.
14
+ *
15
+ * The `Interaction` type is imported type-only (no runtime dep on api-client);
16
+ * frames are validated defensively and a malformed frame is surfaced as an error,
17
+ * never rendered as a silent blank. `answered` is not a wire field — it is a
18
+ * client flag this hook flips on an `interaction.answered` event.
19
+ */
20
+ import { useEffect, useRef, useState } from 'react';
21
+ import type { Interaction } from '@tai42/api-client';
22
+
23
+ import { useApi } from './useApi';
24
+ import { useOnUnauthorized } from './useUnauthorized';
25
+
26
+ /** A live interaction plus the client-maintained `answered` flag. */
27
+ export type StreamInteraction = Interaction & { readonly answered: boolean };
28
+
29
+ export interface InteractionsStreamState {
30
+ readonly interactions: StreamInteraction[];
31
+ readonly connected: boolean;
32
+ readonly backlogLoaded: boolean;
33
+ readonly error: Error | null;
34
+ }
35
+
36
+ // Reconnect backoff: capped exponential with full jitter. The delay for attempt
37
+ // n is a random value in [0, min(CAP, BASE * 2**n)); the attempt counter resets
38
+ // only when a connection is proven healthy (`interaction.backlog_done`), not when
39
+ // it merely opens — so a flapping server that accepts the request then drops the
40
+ // body still backs off instead of tight-looping. A healthy tail-reconnect is
41
+ // near-instant while a sustained outage backs off, and the jitter spreads many
42
+ // clients apart rather than retrying in lockstep.
43
+ const RECONNECT_BASE_MS = 1500;
44
+ const RECONNECT_CAP_MS = 30000;
45
+
46
+ // The wire `answer_format` values. Duplicated from the api-client enum by design:
47
+ // the SDK is the leaf and must not import api-client at runtime. The `satisfies`
48
+ // pin against `Interaction['answer_format']` fails to compile if a listed value
49
+ // leaves the union, so this runtime guard cannot silently drift from the type.
50
+ const ANSWER_FORMATS = new Set<string>([
51
+ 'text',
52
+ 'confirm',
53
+ 'select',
54
+ 'form',
55
+ 'external',
56
+ ] as const satisfies readonly Interaction['answer_format'][]);
57
+
58
+ function isRecord(value: unknown): value is Record<string, unknown> {
59
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
60
+ }
61
+
62
+ /** Parse a frame's JSON to a plain object, or null when it is not one. */
63
+ function parseJson(data: string): Record<string, unknown> | null {
64
+ if (!data) return null;
65
+ try {
66
+ const obj: unknown = JSON.parse(data);
67
+ return isRecord(obj) ? obj : null;
68
+ } catch {
69
+ return null;
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Validate an `interaction.add` frame into an `Interaction`, or null when it is
75
+ * malformed. This mirrors the api-client `interaction` zod schema exactly — it
76
+ * accepts what that schema accepts, no more, no less. The strict-required fields
77
+ * — `interaction_id`, `group_id`, `created_at`, `timeout_at` — must be a string
78
+ * and `answer_format` a known enum value; any missing or non-string one returns
79
+ * null so the caller surfaces it as an error rather than rendering a blank card.
80
+ * Two fields are laxer, matching the schema: `question` is
81
+ * `z.string().default('')`, whose default substitutes ONLY an absent/undefined
82
+ * value, so a missing question normalizes to `''` while a present `null` (or any
83
+ * present non-string) is rejected; `format_payload` is nullish, so an absent/null
84
+ * value (text/confirm carry none) normalizes to `{}` while a present non-object is
85
+ * rejected — never silently coerced to a blank card.
86
+ */
87
+ function parseAddFrame(data: string): Interaction | null {
88
+ const obj = parseJson(data);
89
+ if (obj === null) return null;
90
+ const {
91
+ interaction_id,
92
+ group_id,
93
+ question,
94
+ answer_format,
95
+ format_payload,
96
+ created_at,
97
+ timeout_at,
98
+ sensitive,
99
+ server_verified,
100
+ channel,
101
+ media,
102
+ } = obj;
103
+ if (typeof interaction_id !== 'string') return null;
104
+ if (typeof group_id !== 'string') return null;
105
+ // `question` is z.string().default('') in the schema: the default substitutes
106
+ // ONLY an absent/undefined value, so a missing question is valid while a
107
+ // present null (or any other present non-string) is malformed.
108
+ if (question !== undefined && typeof question !== 'string') return null;
109
+ if (typeof created_at !== 'string') return null;
110
+ if (typeof timeout_at !== 'string') return null;
111
+ if (typeof answer_format !== 'string' || !ANSWER_FORMATS.has(answer_format)) return null;
112
+ // `format_payload` is nullish in the schema: absent/null normalizes to {}, but
113
+ // a present non-object is malformed and must not be coerced to a blank card.
114
+ if (format_payload != null && !isRecord(format_payload)) return null;
115
+ // `sensitive` is z.boolean().default(false): an absent value normalizes to
116
+ // false, while a present non-boolean is malformed.
117
+ if (sensitive !== undefined && typeof sensitive !== 'boolean') return null;
118
+ // `server_verified` is z.boolean().optional(): an absent value stays undefined,
119
+ // while a present non-boolean is malformed.
120
+ if (server_verified !== undefined && typeof server_verified !== 'boolean') return null;
121
+ // `channel` is z.string().optional(): the delivery-channel name rides the frame
122
+ // only for a channel-delivered question; absent stays undefined, a present
123
+ // non-string is malformed.
124
+ if (channel !== undefined && typeof channel !== 'string') return null;
125
+ // `media` is z.array(z.unknown()).optional(): the display-only media rides the
126
+ // frame only when the question carries some; absent stays undefined, a present
127
+ // non-array is malformed. The ITEMS stay unknown here (loose by design) — each is
128
+ // validated per item by the renderer, so one bad item never fails this frame.
129
+ if (media !== undefined && !Array.isArray(media)) return null;
130
+ return {
131
+ interaction_id,
132
+ group_id,
133
+ question: typeof question === 'string' ? question : '',
134
+ answer_format: answer_format as Interaction['answer_format'],
135
+ format_payload: isRecord(format_payload) ? format_payload : {},
136
+ created_at,
137
+ timeout_at,
138
+ sensitive: sensitive ?? false,
139
+ ...(typeof server_verified === 'boolean' ? { server_verified } : {}),
140
+ ...(typeof channel === 'string' ? { channel } : {}),
141
+ ...(Array.isArray(media) ? { media } : {}),
142
+ };
143
+ }
144
+
145
+ /** The interaction id from an `answered` / `removed` frame, or null. */
146
+ function parseId(data: string): string | null {
147
+ const id = parseJson(data)?.interaction_id;
148
+ return typeof id === 'string' ? id : null;
149
+ }
150
+
151
+ export function useInteractionsStream(): InteractionsStreamState {
152
+ const api = useApi();
153
+ const onUnauthorized = useOnUnauthorized();
154
+ const [state, setState] = useState<InteractionsStreamState>({
155
+ interactions: [],
156
+ connected: false,
157
+ backlogLoaded: false,
158
+ error: null,
159
+ });
160
+ // Source of truth: a dedupe map keyed by interaction_id, rebuilt into the
161
+ // ordered array on every change.
162
+ const mapRef = useRef<Map<string, StreamInteraction>>(new Map());
163
+
164
+ useEffect(() => {
165
+ const controller = new AbortController();
166
+ const aborted = () => controller.signal.aborted;
167
+
168
+ // A successful data frame rebuilds the list and clears any transient
169
+ // frame-parse error — one bad frame on a healthy stream must not stick.
170
+ const publish = () => {
171
+ setState((prev) => ({
172
+ ...prev,
173
+ interactions: Array.from(mapRef.current.values()),
174
+ error: null,
175
+ }));
176
+ };
177
+
178
+ const surfaceMalformed = (event: string) => {
179
+ setState((prev) => ({ ...prev, error: new Error(`malformed interaction frame: ${event}`) }));
180
+ };
181
+
182
+ // Per-connection reconcile state: the replayed backlog is authoritative, so
183
+ // ids not re-seen by `interaction.backlog_done` are dropped as
184
+ // removed-while-disconnected.
185
+ let reconciling = false;
186
+ let seenInBacklog = new Set<string>();
187
+ // Consecutive failed reconnect attempts. Reset only when a connection proves
188
+ // healthy (`interaction.backlog_done`), NOT merely when it opens — a server
189
+ // that accepts the request then drops the body would otherwise reset every
190
+ // time and never back off.
191
+ let reconnectAttempt = 0;
192
+ const beginConnection = () => {
193
+ reconciling = true;
194
+ seenInBacklog = new Set();
195
+ };
196
+
197
+ const applyFrame = (event: string, data: string) => {
198
+ if (event === 'interaction.backlog_done') {
199
+ if (reconciling) {
200
+ for (const id of mapRef.current.keys()) {
201
+ if (!seenInBacklog.has(id)) mapRef.current.delete(id);
202
+ }
203
+ reconciling = false;
204
+ }
205
+ // A completed backlog is proof the connection is healthy — clear the
206
+ // reconnect backoff so the next drop reconnects promptly.
207
+ reconnectAttempt = 0;
208
+ setState((prev) => ({
209
+ ...prev,
210
+ interactions: Array.from(mapRef.current.values()),
211
+ backlogLoaded: true,
212
+ }));
213
+ return;
214
+ }
215
+ if (event === 'interaction.removed') {
216
+ const id = parseId(data);
217
+ if (id === null) {
218
+ surfaceMalformed(event);
219
+ return;
220
+ }
221
+ mapRef.current.delete(id);
222
+ publish();
223
+ return;
224
+ }
225
+ if (event === 'interaction.answered') {
226
+ const id = parseId(data);
227
+ if (id === null) {
228
+ surfaceMalformed(event);
229
+ return;
230
+ }
231
+ // The answered frame carries only ids — flip the existing item's flag,
232
+ // keeping its question/format. An answered event for an item we never saw
233
+ // has nothing to show.
234
+ const existing = mapRef.current.get(id);
235
+ if (existing) mapRef.current.set(id, { ...existing, answered: true });
236
+ publish();
237
+ return;
238
+ }
239
+ // interaction.add — the full question.
240
+ const interaction = parseAddFrame(data);
241
+ if (interaction === null) {
242
+ surfaceMalformed(event);
243
+ return;
244
+ }
245
+ if (reconciling) seenInBacklog.add(interaction.interaction_id);
246
+ // Dedupe by id; a redelivered add preserves the client answered flag so a
247
+ // prior `answered` is not undone by an at-least-once replay.
248
+ const existing = mapRef.current.get(interaction.interaction_id);
249
+ mapRef.current.set(interaction.interaction_id, {
250
+ ...interaction,
251
+ answered: existing?.answered ?? false,
252
+ });
253
+ publish();
254
+ };
255
+
256
+ // The abort signal is the single cancellation source of truth: the cleanup
257
+ // aborts it, and every loop guard reads it fresh.
258
+ const run = async () => {
259
+ while (!aborted()) {
260
+ try {
261
+ const frames = await api.streamInteractions(controller.signal);
262
+ if (aborted()) return;
263
+ beginConnection();
264
+ setState((prev) => ({ ...prev, connected: true, error: null }));
265
+ for await (const frame of frames) {
266
+ if (aborted()) return;
267
+ applyFrame(frame.event, frame.data);
268
+ }
269
+ } catch (err) {
270
+ if (aborted()) return;
271
+ const error = err instanceof Error ? err : new Error(String(err));
272
+ setState((prev) => ({ ...prev, connected: false, error }));
273
+ if (error.name === 'ApiUnauthorizedError') {
274
+ // A 401 means the stored credential is dead — reconnecting would just
275
+ // replay the same bad key every delay forever. Treat it as terminal
276
+ // and hand off to the app's 401→login handler, the same path every
277
+ // other data call takes; do not loop.
278
+ onUnauthorized();
279
+ return;
280
+ }
281
+ }
282
+ // Stream ended or errored → wait, then reconnect (server replays backlog).
283
+ // Guard before the setState, like every other setState in run(): a
284
+ // microtask scheduled after the `for await` must not write state after
285
+ // the effect cleanup has aborted (and unmounted) the hook.
286
+ if (aborted()) return;
287
+ setState((prev) => ({ ...prev, connected: false }));
288
+ const ceiling = Math.min(RECONNECT_CAP_MS, RECONNECT_BASE_MS * 2 ** reconnectAttempt);
289
+ const delay = Math.random() * ceiling;
290
+ reconnectAttempt += 1;
291
+ await new Promise<void>((resolve) => {
292
+ const onAbort = () => {
293
+ clearTimeout(timer);
294
+ resolve();
295
+ };
296
+ const timer = setTimeout(() => {
297
+ // Normal wake: drop the abort listener so it does not accumulate
298
+ // across reconnects ({ once: true } only self-removes on firing).
299
+ controller.signal.removeEventListener('abort', onAbort);
300
+ resolve();
301
+ }, delay);
302
+ controller.signal.addEventListener('abort', onAbort, { once: true });
303
+ });
304
+ }
305
+ };
306
+
307
+ void run();
308
+ return () => {
309
+ controller.abort();
310
+ };
311
+ }, [api, onUnauthorized]);
312
+
313
+ return state;
314
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * `useTheme` — light/dark theme, token-driven. The active theme stamps
3
+ * `data-theme` on the document root, which the DS token layers key off.
4
+ *
5
+ * A three-state PREFERENCE sits behind the resolved theme: `'light'` and
6
+ * `'dark'` pin it, `'system'` follows `prefers-color-scheme` and tracks OS
7
+ * changes live. The resolved `theme` a consumer reads is always the concrete
8
+ * `'light' | 'dark'` the tokens need — never `'system'`.
9
+ *
10
+ * The preference persists to `localStorage`, but ONLY when the operator sets it
11
+ * explicitly (`setPreference` / `setTheme` / `toggle`). Boot READS the stored
12
+ * value; it never WRITES one, so a session that only ever renders the app leaves
13
+ * storage untouched. A `'system'` preference clears any pinned choice and hands
14
+ * the theme back to the OS.
15
+ */
16
+ import {
17
+ createContext,
18
+ createElement,
19
+ useCallback,
20
+ useContext,
21
+ useEffect,
22
+ useMemo,
23
+ useState,
24
+ } from 'react';
25
+ import type { ReactNode } from 'react';
26
+
27
+ export type Theme = 'light' | 'dark';
28
+ export type ThemePreference = 'light' | 'dark' | 'system';
29
+
30
+ export interface ThemeState {
31
+ /** The resolved theme the tokens key off — always concrete, never `'system'`. */
32
+ readonly theme: Theme;
33
+ /** The operator's choice: a pinned theme, or `'system'` to follow the OS. */
34
+ readonly preference: ThemePreference;
35
+ /** Set the preference and persist it. The only path that writes storage. */
36
+ readonly setPreference: (preference: ThemePreference) => void;
37
+ /** Pin an explicit theme (persists) — a preference shortcut kept for callers. */
38
+ readonly setTheme: (theme: Theme) => void;
39
+ /** Flip between the two pinned themes off the current resolved theme (persists). */
40
+ readonly toggle: () => void;
41
+ }
42
+
43
+ const ThemeContext = createContext<ThemeState | null>(null);
44
+
45
+ /** Where the explicit preference is stored between sessions. */
46
+ const STORAGE_KEY = 'tai-studio.theme-preference';
47
+
48
+ function systemTheme(): Theme {
49
+ try {
50
+ return globalThis.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
51
+ } catch {
52
+ return 'light';
53
+ }
54
+ }
55
+
56
+ /** The persisted preference, or `'system'` when none is stored or storage is
57
+ * unavailable. A READ only — boot never writes, so an untouched session leaves
58
+ * storage alone (the login flows depend on this). */
59
+ function readStoredPreference(): ThemePreference {
60
+ try {
61
+ const raw = globalThis.localStorage.getItem(STORAGE_KEY);
62
+ if (raw === 'light' || raw === 'dark' || raw === 'system') return raw;
63
+ } catch {
64
+ // No storage (private mode / non-browser) — the OS preference is the default.
65
+ }
66
+ return 'system';
67
+ }
68
+
69
+ export function ThemeProvider({ children }: { children: ReactNode }) {
70
+ const [preference, setPreferenceState] = useState<ThemePreference>(readStoredPreference);
71
+ // The live OS resolution, kept current by the listener below; it only feeds the
72
+ // resolved theme while the preference is `'system'`.
73
+ const [systemResolved, setSystemResolved] = useState<Theme>(systemTheme);
74
+ const theme: Theme = preference === 'system' ? systemResolved : preference;
75
+
76
+ useEffect(() => {
77
+ globalThis.document.documentElement.setAttribute('data-theme', theme);
78
+ }, [theme]);
79
+
80
+ // Track the OS preference live, but ONLY while following it. The same defensive
81
+ // guard as `systemTheme()` — jsdom has no `matchMedia`, so subscribing is
82
+ // best-effort and its absence simply leaves the last resolved value in place.
83
+ useEffect(() => {
84
+ if (preference !== 'system') return;
85
+ let query: MediaQueryList;
86
+ try {
87
+ query = globalThis.matchMedia('(prefers-color-scheme: dark)');
88
+ } catch {
89
+ return;
90
+ }
91
+ setSystemResolved(query.matches ? 'dark' : 'light');
92
+ const onChange = (event: MediaQueryListEvent): void => {
93
+ setSystemResolved(event.matches ? 'dark' : 'light');
94
+ };
95
+ query.addEventListener('change', onChange);
96
+ return () => {
97
+ query.removeEventListener('change', onChange);
98
+ };
99
+ }, [preference]);
100
+
101
+ const setPreference = useCallback((next: ThemePreference) => {
102
+ setPreferenceState(next);
103
+ try {
104
+ globalThis.localStorage.setItem(STORAGE_KEY, next);
105
+ } catch {
106
+ // Storage unavailable — the choice still applies for this session in memory.
107
+ }
108
+ }, []);
109
+
110
+ const setTheme = useCallback(
111
+ (next: Theme) => {
112
+ setPreference(next);
113
+ },
114
+ [setPreference],
115
+ );
116
+
117
+ const toggle = useCallback(() => {
118
+ setPreference(theme === 'dark' ? 'light' : 'dark');
119
+ }, [setPreference, theme]);
120
+
121
+ const value = useMemo<ThemeState>(
122
+ () => ({ theme, preference, setPreference, setTheme, toggle }),
123
+ [theme, preference, setPreference, setTheme, toggle],
124
+ );
125
+
126
+ return createElement(ThemeContext.Provider, { value }, children);
127
+ }
128
+
129
+ export function useTheme(): ThemeState {
130
+ const state = useContext(ThemeContext);
131
+ if (state === null) throw new Error('useTheme must be used within a <ThemeProvider>');
132
+ return state;
133
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `useOnUnauthorized` — the app's "the credential is dead, route to login" handler,
3
+ * provided through an SDK-owned context so shared hooks can reach it without a
4
+ * runtime dependency on the shell or the router.
5
+ *
6
+ * The default is a no-op, so a consumer rendered without a provider (a test, or a
7
+ * plugin in isolation) still works — it just does not route. The shell wires the
8
+ * real handler (the same one the api-client's 401 path drives) so a 401 raised
9
+ * OUTSIDE TanStack Query — notably the raw interactions SSE fetch — lands on the
10
+ * one login-routing path instead of being retried against a known-bad key.
11
+ */
12
+ import { createContext, useContext } from 'react';
13
+
14
+ const UnauthorizedContext = createContext<() => void>(() => undefined);
15
+
16
+ export const UnauthorizedProvider = UnauthorizedContext.Provider;
17
+
18
+ export function useOnUnauthorized(): () => void {
19
+ return useContext(UnauthorizedContext);
20
+ }
package/src/host.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @tai42/studio-sdk/host — the HOST-ONLY registry API. `loadPlugin` (the one entry
3
+ * through which a plugin's `register` contributes) and `getContributions` (what the
4
+ * shell reads) live here, apart from the plugin surface, so the served plugin asset
5
+ * carries no way to forge, wipe, or enumerate the registry. Only the host bundle
6
+ * imports this. See SECURITY.md for the trust boundary.
7
+ */
8
+ export { loadPlugin, getContributions } from './plugin/registry';
9
+ export {
10
+ setPluginHostState,
11
+ getPluginHostState,
12
+ subscribePluginHost,
13
+ usePluginContributions,
14
+ } from './plugin/host-state';
15
+ export type { PluginLoaderState, PluginContributionsSnapshot } from './plugin/host-state';