@xemahq/ui-kernel 0.9.1 → 0.10.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 (284) hide show
  1. package/README.md +2 -2
  2. package/dist/lib/biome-host/biome-builders.d.ts.map +1 -1
  3. package/dist/lib/biome-host/biome-builders.js +3 -1
  4. package/dist/lib/biome-host/biome-builders.js.map +1 -1
  5. package/dist/lib/biome-host/biome-registry.d.ts.map +1 -1
  6. package/dist/lib/biome-host/biome-registry.js.map +1 -1
  7. package/dist/lib/biome-host/biome-scope-hooks.d.ts +11 -0
  8. package/dist/lib/biome-host/biome-scope-hooks.d.ts.map +1 -0
  9. package/dist/lib/biome-host/biome-scope-hooks.js +25 -0
  10. package/dist/lib/biome-host/biome-scope-hooks.js.map +1 -0
  11. package/dist/lib/biome-host/biome-scope.d.ts +1 -10
  12. package/dist/lib/biome-host/biome-scope.d.ts.map +1 -1
  13. package/dist/lib/biome-host/biome-scope.js +1 -24
  14. package/dist/lib/biome-host/biome-scope.js.map +1 -1
  15. package/dist/lib/biome-host/biome-scoped-query.d.ts +1 -1
  16. package/dist/lib/biome-host/biome-scoped-query.d.ts.map +1 -1
  17. package/dist/lib/biome-host/biome-scoped-query.js +1 -1
  18. package/dist/lib/biome-host/biome-scoped-query.js.map +1 -1
  19. package/dist/lib/biome-host/create-biome-orval-config.js +2 -2
  20. package/dist/lib/biome-host/create-biome-orval-config.js.map +1 -1
  21. package/dist/lib/biome-host/errors.js.map +1 -1
  22. package/dist/lib/biome-host/index.d.ts +1 -2
  23. package/dist/lib/biome-host/index.d.ts.map +1 -1
  24. package/dist/lib/biome-host/index.js +1 -2
  25. package/dist/lib/biome-host/index.js.map +1 -1
  26. package/dist/lib/biome-host/session-contributions.d.ts +9 -4
  27. package/dist/lib/biome-host/session-contributions.d.ts.map +1 -1
  28. package/dist/lib/capabilities/capability-context.d.ts +9 -0
  29. package/dist/lib/capabilities/capability-context.d.ts.map +1 -0
  30. package/dist/lib/capabilities/capability-context.js +13 -0
  31. package/dist/lib/capabilities/capability-context.js.map +1 -0
  32. package/dist/lib/capabilities/capability-provider.d.ts +2 -8
  33. package/dist/lib/capabilities/capability-provider.d.ts.map +1 -1
  34. package/dist/lib/capabilities/capability-provider.js +1 -12
  35. package/dist/lib/capabilities/capability-provider.js.map +1 -1
  36. package/dist/lib/capabilities/index.d.ts +1 -0
  37. package/dist/lib/capabilities/index.d.ts.map +1 -1
  38. package/dist/lib/capabilities/index.js +1 -0
  39. package/dist/lib/capabilities/index.js.map +1 -1
  40. package/dist/lib/capabilities/use-capability.d.ts.map +1 -1
  41. package/dist/lib/capabilities/use-capability.js +1 -1
  42. package/dist/lib/capabilities/use-capability.js.map +1 -1
  43. package/dist/lib/system-bus/deeplink.js.map +1 -1
  44. package/dist/lib/system-bus/host-ports.d.ts +2 -1
  45. package/dist/lib/system-bus/host-ports.d.ts.map +1 -1
  46. package/dist/lib/system-bus/intent-registry.d.ts.map +1 -1
  47. package/dist/lib/system-bus/intent-registry.js.map +1 -1
  48. package/dist/offline/index.d.ts +2 -1
  49. package/dist/offline/index.d.ts.map +1 -1
  50. package/dist/offline/index.js +2 -1
  51. package/dist/offline/index.js.map +1 -1
  52. package/dist/offline/offline-status-badge.js +1 -1
  53. package/dist/offline/offline-status-badge.js.map +1 -1
  54. package/dist/offline/outbox.js.map +1 -1
  55. package/dist/offline/react-hooks.d.ts +7 -0
  56. package/dist/offline/react-hooks.d.ts.map +1 -0
  57. package/dist/offline/react-hooks.js +25 -0
  58. package/dist/offline/react-hooks.js.map +1 -0
  59. package/dist/offline/react.d.ts +0 -4
  60. package/dist/offline/react.d.ts.map +1 -1
  61. package/dist/offline/react.js +2 -24
  62. package/dist/offline/react.js.map +1 -1
  63. package/dist/offline/storage.d.ts.map +1 -1
  64. package/dist/offline/storage.js +8 -5
  65. package/dist/offline/storage.js.map +1 -1
  66. package/dist/registry/index.d.ts +1 -0
  67. package/dist/registry/index.d.ts.map +1 -1
  68. package/dist/registry/index.js +1 -0
  69. package/dist/registry/index.js.map +1 -1
  70. package/dist/registry/lib/primitives/SessionEventCard.d.ts.map +1 -1
  71. package/dist/registry/lib/primitives/SessionEventCard.js +1 -1
  72. package/dist/registry/lib/primitives/SessionEventCard.js.map +1 -1
  73. package/dist/registry/lib/session-context-builder.d.ts +10 -2
  74. package/dist/registry/lib/session-context-builder.d.ts.map +1 -1
  75. package/dist/registry/lib/session-context-builder.js +14 -7
  76. package/dist/registry/lib/session-context-builder.js.map +1 -1
  77. package/dist/registry/lib/session-context-provider.d.ts +1 -3
  78. package/dist/registry/lib/session-context-provider.d.ts.map +1 -1
  79. package/dist/registry/lib/session-context-provider.js +2 -15
  80. package/dist/registry/lib/session-context-provider.js.map +1 -1
  81. package/dist/registry/lib/session-context.d.ts +5 -0
  82. package/dist/registry/lib/session-context.d.ts.map +1 -0
  83. package/dist/registry/lib/session-context.js +15 -0
  84. package/dist/registry/lib/session-context.js.map +1 -0
  85. package/dist/registry/lib/session-selectors.d.ts.map +1 -1
  86. package/dist/registry/lib/session-selectors.js +3 -1
  87. package/dist/registry/lib/session-selectors.js.map +1 -1
  88. package/dist/ui/branding/app-branding-context.d.ts +9 -0
  89. package/dist/ui/branding/app-branding-context.d.ts.map +1 -0
  90. package/dist/ui/branding/app-branding-context.js +10 -0
  91. package/dist/ui/branding/app-branding-context.js.map +1 -0
  92. package/dist/ui/branding/app-branding-provider.d.ts +0 -6
  93. package/dist/ui/branding/app-branding-provider.d.ts.map +1 -1
  94. package/dist/ui/branding/app-branding-provider.js +1 -8
  95. package/dist/ui/branding/app-branding-provider.js.map +1 -1
  96. package/dist/ui/capture/schema-field-input.js +1 -1
  97. package/dist/ui/capture/schema-field-input.js.map +1 -1
  98. package/dist/ui/capture/use-prefill.d.ts.map +1 -1
  99. package/dist/ui/capture/use-prefill.js +3 -1
  100. package/dist/ui/capture/use-prefill.js.map +1 -1
  101. package/dist/ui/capture/use-wizard.d.ts +1 -1
  102. package/dist/ui/capture/use-wizard.d.ts.map +1 -1
  103. package/dist/ui/capture/use-wizard.js +1 -1
  104. package/dist/ui/capture/use-wizard.js.map +1 -1
  105. package/dist/ui/capture/wizard.d.ts.map +1 -1
  106. package/dist/ui/capture/wizard.js.map +1 -1
  107. package/dist/ui/index.d.ts +5 -1
  108. package/dist/ui/index.d.ts.map +1 -1
  109. package/dist/ui/index.js +5 -1
  110. package/dist/ui/index.js.map +1 -1
  111. package/dist/ui/primitives/alert-dialog.js +1 -1
  112. package/dist/ui/primitives/alert-dialog.js.map +1 -1
  113. package/dist/ui/primitives/badge-variants.d.ts +4 -0
  114. package/dist/ui/primitives/badge-variants.d.ts.map +1 -0
  115. package/dist/ui/primitives/badge-variants.js +15 -0
  116. package/dist/ui/primitives/badge-variants.js.map +1 -0
  117. package/dist/ui/primitives/badge.d.ts +3 -5
  118. package/dist/ui/primitives/badge.d.ts.map +1 -1
  119. package/dist/ui/primitives/badge.js +2 -15
  120. package/dist/ui/primitives/badge.js.map +1 -1
  121. package/dist/ui/primitives/button-variants.d.ts +5 -0
  122. package/dist/ui/primitives/button-variants.d.ts.map +1 -0
  123. package/dist/ui/primitives/button-variants.js +24 -0
  124. package/dist/ui/primitives/button-variants.js.map +1 -0
  125. package/dist/ui/primitives/button.d.ts +3 -6
  126. package/dist/ui/primitives/button.d.ts.map +1 -1
  127. package/dist/ui/primitives/button.js +2 -24
  128. package/dist/ui/primitives/button.js.map +1 -1
  129. package/dist/ui/primitives/form-stepper.d.ts +1 -13
  130. package/dist/ui/primitives/form-stepper.d.ts.map +1 -1
  131. package/dist/ui/primitives/form-stepper.js +1 -6
  132. package/dist/ui/primitives/form-stepper.js.map +1 -1
  133. package/dist/ui/primitives/form-stepper.types.d.ts +14 -0
  134. package/dist/ui/primitives/form-stepper.types.d.ts.map +1 -0
  135. package/dist/ui/primitives/form-stepper.types.js +7 -0
  136. package/dist/ui/primitives/form-stepper.types.js.map +1 -0
  137. package/dist/ui/primitives/preview-device-frame.js +1 -1
  138. package/dist/ui/primitives/preview-device-frame.js.map +1 -1
  139. package/dist/ui/primitives/tag-multi-select.d.ts.map +1 -1
  140. package/dist/ui/primitives/tag-multi-select.js +1 -1
  141. package/dist/ui/primitives/tag-multi-select.js.map +1 -1
  142. package/dist/vista/index.d.ts +4 -0
  143. package/dist/vista/index.d.ts.map +1 -1
  144. package/dist/vista/index.js +4 -0
  145. package/dist/vista/index.js.map +1 -1
  146. package/dist/vista/surfaces/FilesSurface.d.ts +1 -12
  147. package/dist/vista/surfaces/FilesSurface.d.ts.map +1 -1
  148. package/dist/vista/surfaces/FilesSurface.js +2 -46
  149. package/dist/vista/surfaces/FilesSurface.js.map +1 -1
  150. package/dist/vista/surfaces/FormSurface.d.ts.map +1 -1
  151. package/dist/vista/surfaces/FormSurface.js +39 -12
  152. package/dist/vista/surfaces/FormSurface.js.map +1 -1
  153. package/dist/vista/surfaces/elicitation-transport.d.ts +12 -3
  154. package/dist/vista/surfaces/elicitation-transport.d.ts.map +1 -1
  155. package/dist/vista/surfaces/elicitation-transport.js +96 -3
  156. package/dist/vista/surfaces/elicitation-transport.js.map +1 -1
  157. package/dist/vista/surfaces/file-surface-utils.d.ts +13 -0
  158. package/dist/vista/surfaces/file-surface-utils.d.ts.map +1 -0
  159. package/dist/vista/surfaces/file-surface-utils.js +46 -0
  160. package/dist/vista/surfaces/file-surface-utils.js.map +1 -0
  161. package/dist/vista/surfaces/index.d.ts +4 -3
  162. package/dist/vista/surfaces/index.d.ts.map +1 -1
  163. package/dist/vista/surfaces/index.js +4 -81
  164. package/dist/vista/surfaces/index.js.map +1 -1
  165. package/dist/vista/surfaces/register-surfaces.d.ts +2 -0
  166. package/dist/vista/surfaces/register-surfaces.d.ts.map +1 -0
  167. package/dist/vista/surfaces/register-surfaces.js +80 -0
  168. package/dist/vista/surfaces/register-surfaces.js.map +1 -0
  169. package/dist/vista/surfaces/text-surfaces.js +1 -1
  170. package/dist/vista/surfaces/text-surfaces.js.map +1 -1
  171. package/dist/vista/vista-code-renderer-context.d.ts +9 -0
  172. package/dist/vista/vista-code-renderer-context.d.ts.map +1 -0
  173. package/dist/vista/vista-code-renderer-context.js +6 -0
  174. package/dist/vista/vista-code-renderer-context.js.map +1 -0
  175. package/dist/vista/vista-code-renderer.d.ts +1 -6
  176. package/dist/vista/vista-code-renderer.d.ts.map +1 -1
  177. package/dist/vista/vista-code-renderer.js +1 -5
  178. package/dist/vista/vista-code-renderer.js.map +1 -1
  179. package/dist/vista/vista-file-context.d.ts +8 -0
  180. package/dist/vista/vista-file-context.d.ts.map +1 -0
  181. package/dist/vista/vista-file-context.js +6 -0
  182. package/dist/vista/vista-file-context.js.map +1 -0
  183. package/dist/vista/vista-file-source.d.ts +1 -6
  184. package/dist/vista/vista-file-source.d.ts.map +1 -1
  185. package/dist/vista/vista-file-source.js +2 -5
  186. package/dist/vista/vista-file-source.js.map +1 -1
  187. package/dist/vista/vista-form-context.d.ts +13 -0
  188. package/dist/vista/vista-form-context.d.ts.map +1 -0
  189. package/dist/vista/vista-form-context.js +6 -0
  190. package/dist/vista/vista-form-context.js.map +1 -0
  191. package/dist/vista/vista-form-host.d.ts +1 -10
  192. package/dist/vista/vista-form-host.d.ts.map +1 -1
  193. package/dist/vista/vista-form-host.js +1 -5
  194. package/dist/vista/vista-form-host.js.map +1 -1
  195. package/dist/vista/vista-open-context.d.ts +1 -3
  196. package/dist/vista/vista-open-context.d.ts.map +1 -1
  197. package/dist/vista/vista-open-context.js +1 -5
  198. package/dist/vista/vista-open-context.js.map +1 -1
  199. package/dist/vista/vista-open-state.d.ts +5 -0
  200. package/dist/vista/vista-open-state.d.ts.map +1 -0
  201. package/dist/vista/vista-open-state.js +6 -0
  202. package/dist/vista/vista-open-state.js.map +1 -0
  203. package/dist/vista/vista-payloads.d.ts +2 -3
  204. package/dist/vista/vista-payloads.d.ts.map +1 -1
  205. package/dist/vista/vista-payloads.js +18 -21
  206. package/dist/vista/vista-payloads.js.map +1 -1
  207. package/dist/vista/vista-tools.d.ts.map +1 -1
  208. package/dist/vista/vista-tools.js.map +1 -1
  209. package/package.json +3 -3
  210. package/src/lib/biome-host/biome-builders.ts +4 -1
  211. package/src/lib/biome-host/biome-registry.ts +0 -1
  212. package/src/lib/biome-host/biome-scope-hooks.ts +39 -0
  213. package/src/lib/biome-host/biome-scope.tsx +1 -68
  214. package/src/lib/biome-host/biome-scoped-query.ts +4 -1
  215. package/src/lib/biome-host/create-biome-orval-config.ts +2 -2
  216. package/src/lib/biome-host/errors.ts +1 -1
  217. package/src/lib/biome-host/index.ts +1 -2
  218. package/src/lib/biome-host/session-contributions.ts +21 -18
  219. package/src/lib/capabilities/capability-context.ts +25 -0
  220. package/src/lib/capabilities/capability-provider.tsx +5 -34
  221. package/src/lib/capabilities/index.ts +1 -0
  222. package/src/lib/capabilities/use-capability.ts +4 -1
  223. package/src/lib/system-bus/deeplink.ts +1 -1
  224. package/src/lib/system-bus/host-ports.ts +2 -1
  225. package/src/lib/system-bus/intent-registry.ts +1 -2
  226. package/src/offline/index.ts +4 -2
  227. package/src/offline/offline-status-badge.tsx +1 -1
  228. package/src/offline/outbox.test.ts +23 -16
  229. package/src/offline/outbox.ts +1 -1
  230. package/src/offline/react-hooks.ts +37 -0
  231. package/src/offline/react.tsx +1 -40
  232. package/src/offline/storage.ts +11 -6
  233. package/src/registry/index.ts +1 -0
  234. package/src/registry/lib/primitives/SessionEventCard.tsx +4 -2
  235. package/src/registry/lib/session-context-builder.ts +33 -17
  236. package/src/registry/lib/session-context-provider.tsx +8 -25
  237. package/src/registry/lib/session-context.ts +21 -0
  238. package/src/registry/lib/session-selectors.ts +3 -1
  239. package/src/ui/branding/app-branding-context.ts +20 -0
  240. package/src/ui/branding/app-branding-provider.tsx +5 -27
  241. package/src/ui/capture/schema-field-input.tsx +7 -1
  242. package/src/ui/capture/use-prefill.ts +4 -1
  243. package/src/ui/capture/use-wizard.ts +2 -2
  244. package/src/ui/capture/wizard.tsx +2 -1
  245. package/src/ui/index.ts +7 -2
  246. package/src/ui/primitives/alert-dialog.tsx +1 -1
  247. package/src/ui/primitives/badge-variants.ts +21 -0
  248. package/src/ui/primitives/badge.tsx +3 -19
  249. package/src/ui/primitives/button-variants.ts +31 -0
  250. package/src/ui/primitives/button.tsx +3 -28
  251. package/src/ui/primitives/form-stepper.tsx +1 -16
  252. package/src/ui/primitives/form-stepper.types.ts +15 -0
  253. package/src/ui/primitives/preview-device-frame.tsx +1 -1
  254. package/src/ui/primitives/tag-multi-select.tsx +3 -4
  255. package/src/vista/form-payload.spec.ts +71 -25
  256. package/src/vista/index.ts +4 -0
  257. package/src/vista/surfaces/FilesSurface.tsx +2 -68
  258. package/src/vista/surfaces/FormSurface.tsx +125 -26
  259. package/src/vista/surfaces/elicitation-transport.spec.ts +205 -23
  260. package/src/vista/surfaces/elicitation-transport.ts +192 -14
  261. package/src/vista/surfaces/file-surface-utils.ts +62 -0
  262. package/src/vista/surfaces/index.ts +24 -0
  263. package/src/vista/surfaces/{index.tsx → register-surfaces.tsx} +1 -21
  264. package/src/vista/surfaces/text-surfaces.tsx +1 -1
  265. package/src/vista/vista-code-renderer-context.ts +16 -0
  266. package/src/vista/vista-code-renderer.tsx +4 -16
  267. package/src/vista/vista-file-context.ts +14 -0
  268. package/src/vista/vista-file-source.tsx +5 -28
  269. package/src/vista/vista-form-context.ts +20 -0
  270. package/src/vista/vista-form-host.tsx +11 -36
  271. package/src/vista/vista-open-context.tsx +4 -14
  272. package/src/vista/vista-open-state.ts +11 -0
  273. package/src/vista/vista-payloads.ts +44 -46
  274. package/src/vista/vista-tools.ts +2 -2
  275. package/dist/lib/biome-host/response-envelope.d.ts +0 -3
  276. package/dist/lib/biome-host/response-envelope.d.ts.map +0 -1
  277. package/dist/lib/biome-host/response-envelope.js +0 -21
  278. package/dist/lib/biome-host/response-envelope.js.map +0 -1
  279. package/dist/lib/biome-host/session-profiles.d.ts +0 -18
  280. package/dist/lib/biome-host/session-profiles.d.ts.map +0 -1
  281. package/dist/lib/biome-host/session-profiles.js +0 -35
  282. package/dist/lib/biome-host/session-profiles.js.map +0 -1
  283. package/src/lib/biome-host/response-envelope.ts +0 -69
  284. package/src/lib/biome-host/session-profiles.ts +0 -99
@@ -199,7 +199,8 @@ export interface VistaTablePayload {
199
199
  export function validateTablePayload(raw: unknown): VistaTablePayload | null {
200
200
  const record = asRecord(raw);
201
201
  if (record === null) return null;
202
- if (!Array.isArray(record.columns) || !Array.isArray(record.rows)) return null;
202
+ if (!Array.isArray(record.columns) || !Array.isArray(record.rows))
203
+ return null;
203
204
  if (!record.columns.every((c) => typeof c === 'string')) return null;
204
205
  if (!record.rows.every((r) => Array.isArray(r))) return null;
205
206
  return {
@@ -244,7 +245,8 @@ export function validateChartPayload(raw: unknown): VistaChartPayload | null {
244
245
  if (record === null) return null;
245
246
  if (typeof record.kind !== 'string' || !CHART_KINDS.includes(record.kind))
246
247
  return null;
247
- if (typeof record.title !== 'string' || record.title.length === 0) return null;
248
+ if (typeof record.title !== 'string' || record.title.length === 0)
249
+ return null;
248
250
  if (!Array.isArray(record.series) || record.series.length === 0) return null;
249
251
  const series: VistaChartSeries[] = [];
250
252
  for (const entry of record.series) {
@@ -377,37 +379,18 @@ export function validateWebPayload(raw: unknown): VistaWebPayload | null {
377
379
  : null;
378
380
  }
379
381
 
380
- /**
381
- * An `http(s)` ABSOLUTE URL, or `null`.
382
- *
383
- * Every URL on a {@link VistaFormPayload} must be absolute. That is the
384
- * structural half of "the value goes browser → service and never through the
385
- * agent": a relative path would resolve against the HOST app's origin — i.e.
386
- * its BFF, where the chat transport lives — so a relative submit target is
387
- * rejected outright rather than normalised.
388
- */
389
- function absoluteHttpUrl(value: unknown): string | null {
390
- if (typeof value !== 'string' || value.length === 0) return null;
391
- let parsed: URL;
392
- try {
393
- parsed = new URL(value);
394
- } catch {
395
- return null;
396
- }
397
- return parsed.protocol === 'https:' || parsed.protocol === 'http:' ? value : null;
398
- }
399
-
400
382
  /**
401
383
  * The consent / install handoff of a {@link VistaFormPayload}.
402
384
  *
403
385
  * Present when the connector's app auth is `oauth2` or `app_install`: there is
404
386
  * no field form to fill, only a link the user follows in another window. It is
405
- * fully generic — the URL is minted per-provider by the owning service and
406
- * carried here verbatim, so no provider is special-cased in the renderer.
387
+ * fully generic — the owning service supplies a path under the host-configured
388
+ * trusted service origin, so no provider is special-cased in the renderer and
389
+ * an agent-controlled payload never selects a network authority.
407
390
  */
408
391
  export interface VistaFormLaunch {
409
- /** ABSOLUTE consent / install URL, opened in a new window. */
410
- readonly url: string;
392
+ /** Root-relative consent / install path on the trusted elicitation service. */
393
+ readonly path: string;
411
394
  /** Button copy, e.g. "Install Acme" / "Continue to Acme". */
412
395
  readonly label: string;
413
396
  /** Optional explanation rendered above the button. */
@@ -421,10 +404,10 @@ export interface VistaFormLaunch {
421
404
  * never see. Two mutually-exclusive modes, chosen by the presence of
422
405
  * {@link launch}:
423
406
  *
424
- * • FIELD mode — render `fields`, POST them to `submitUrl` with the user's
425
- * own JWT;
407
+ * • FIELD mode — render `fields`, POST them through the host's trusted
408
+ * elicitation-service base URL with the user's own JWT;
426
409
  * • LAUNCH mode — render the consent/install link, then confirm completion
427
- * against `statusUrl`.
410
+ * through that same trusted service.
428
411
  *
429
412
  * Both settle into the same `{ submitted, ref }` shape, so the agent's view of
430
413
  * the world does not depend on which mode ran.
@@ -434,10 +417,6 @@ export interface VistaFormPayload {
434
417
  readonly ref: string;
435
418
  /** Why the credential is being asked for — shown to the user verbatim. */
436
419
  readonly purpose: string;
437
- /** ABSOLUTE service URL the captured fields are POSTed to. */
438
- readonly submitUrl: string;
439
- /** ABSOLUTE service URL reporting whether the elicitation has settled. */
440
- readonly statusUrl: string;
441
420
  /** The onboarding form's fields. MAY be empty — the service is the authority. */
442
421
  readonly fields: readonly CaptureFieldSpec[];
443
422
  /** Optional heading override (defaults to the tab title). */
@@ -448,16 +427,26 @@ export interface VistaFormPayload {
448
427
  readonly launch?: VistaFormLaunch;
449
428
  }
450
429
 
430
+ const VISTA_FORM_REF_PATTERN = /^[A-Za-z0-9_-]{1,256}$/;
431
+
432
+ /** Whether a value is one opaque base64url elicitation path segment. */
433
+ export function isValidVistaFormRef(value: unknown): value is string {
434
+ return typeof value === 'string' && VISTA_FORM_REF_PATTERN.test(value);
435
+ }
436
+
451
437
  export function validateFormPayload(raw: unknown): VistaFormPayload | null {
452
438
  const record = asRecord(raw);
453
439
  if (record === null) return null;
454
- if (typeof record.ref !== 'string' || record.ref.length === 0) return null;
440
+ // The service generates base64url refs. Keeping the accepted alphabet closed
441
+ // makes the ref safe as exactly one URL path segment; dot segments, slashes,
442
+ // percent escapes and Unicode lookalikes are all rejected.
443
+ if (!isValidVistaFormRef(record.ref)) {
444
+ return null;
445
+ }
455
446
  if (typeof record.purpose !== 'string') return null;
456
-
457
- const submitUrl = absoluteHttpUrl(record.submitUrl);
458
- if (submitUrl === null) return null;
459
- const statusUrl = absoluteHttpUrl(record.statusUrl);
460
- if (statusUrl === null) return null;
447
+ // Deliberately reject the old contract rather than silently ignoring its
448
+ // payload-selected network targets.
449
+ if ('submitUrl' in record || 'statusUrl' in record) return null;
461
450
 
462
451
  const fields = validateCaptureFields(record.fields);
463
452
  if (fields === null) return null;
@@ -471,24 +460,32 @@ export function validateFormPayload(raw: unknown): VistaFormPayload | null {
471
460
  if (record.launch !== undefined) {
472
461
  const raw_ = asRecord(record.launch);
473
462
  if (raw_ === null) return null;
474
- const url = absoluteHttpUrl(raw_.url);
475
- if (url === null) return null;
463
+ if ('url' in raw_) return null;
464
+ if (
465
+ typeof raw_.path !== 'string' ||
466
+ !raw_.path.startsWith('/') ||
467
+ raw_.path.startsWith('//') ||
468
+ raw_.path.includes('\\')
469
+ ) {
470
+ return null;
471
+ }
476
472
  if (typeof raw_.label !== 'string' || raw_.label.length === 0) return null;
477
473
  const hint = optionalString(raw_.hint);
478
474
  if (hint === null) return null;
479
- launch = hint === undefined ? { url, label: raw_.label } : { url, label: raw_.label, hint };
475
+ launch =
476
+ hint === undefined
477
+ ? { path: raw_.path, label: raw_.label }
478
+ : { path: raw_.path, label: raw_.label, hint };
480
479
  }
481
480
 
482
481
  const payload: {
483
482
  ref: string;
484
483
  purpose: string;
485
- submitUrl: string;
486
- statusUrl: string;
487
484
  fields: readonly CaptureFieldSpec[];
488
485
  heading?: string;
489
486
  submitLabel?: string;
490
487
  launch?: VistaFormLaunch;
491
- } = { ref: record.ref, purpose: record.purpose, submitUrl, statusUrl, fields };
488
+ } = { ref: record.ref, purpose: record.purpose, fields };
492
489
  if (heading !== undefined) payload.heading = heading;
493
490
  if (submitLabel !== undefined) payload.submitLabel = submitLabel;
494
491
  if (launch !== undefined) payload.launch = launch;
@@ -505,7 +502,8 @@ export interface VistaAppPayload {
505
502
  export function validateAppPayload(raw: unknown): VistaAppPayload | null {
506
503
  const record = asRecord(raw);
507
504
  if (record === null) return null;
508
- if (typeof record.appId !== 'string' || record.appId.length === 0) return null;
505
+ if (typeof record.appId !== 'string' || record.appId.length === 0)
506
+ return null;
509
507
  const path = optionalString(record.path);
510
508
  if (path === null) return null;
511
509
  return path === undefined
@@ -120,7 +120,7 @@ export const VistaToolResultSchema: z.ZodType<VistaToolResult> = z.union([
120
120
  error: z.nativeEnum(VistaToolError),
121
121
  detail: z.string().optional(),
122
122
  }),
123
- ]) as z.ZodType<VistaToolResult>;
123
+ ]);
124
124
 
125
125
  /**
126
126
  * Perform a relayed Vista tool call against a mounted panel's controller.
@@ -151,7 +151,7 @@ export function dispatchVistaTool(
151
151
  const { tabId, ...patch } = parsed.data;
152
152
  const refusal = refusalFor(controller, tabId);
153
153
  if (refusal !== null) return refusal;
154
- if (!controller.update(tabId, patch as VistaSurfacePatch)) {
154
+ if (!controller.update(tabId, patch)) {
155
155
  // The tab is listed but not opened-tab-owned: it is a HOST tab, and a
156
156
  // patch to it would be overwritten by the host's next reconcile.
157
157
  return {
@@ -1,3 +0,0 @@
1
- export declare function unwrapList<T>(value: unknown): T[];
2
- export declare function unwrapData<T>(value: unknown): T;
3
- //# sourceMappingURL=response-envelope.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"response-envelope.d.ts","sourceRoot":"","sources":["../../../src/lib/biome-host/response-envelope.ts"],"names":[],"mappings":"AA+BA,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,EAAE,CAYjD;AAeD,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAU/C"}
@@ -1,21 +0,0 @@
1
- export function unwrapList(value) {
2
- if (Array.isArray(value)) {
3
- return value;
4
- }
5
- if (value !== null &&
6
- typeof value === 'object' &&
7
- Array.isArray(value.data)) {
8
- return value.data;
9
- }
10
- return [];
11
- }
12
- export function unwrapData(value) {
13
- if (value !== null &&
14
- typeof value === 'object' &&
15
- 'data' in value &&
16
- !('pagination' in value)) {
17
- return value.data;
18
- }
19
- return value;
20
- }
21
- //# sourceMappingURL=response-envelope.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"response-envelope.js","sourceRoot":"","sources":["../../../src/lib/biome-host/response-envelope.ts"],"names":[],"mappings":"AA+BA,MAAM,UAAU,UAAU,CAAI,KAAc;IAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAY,CAAC;IACtB,CAAC;IACD,IACE,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,CAAC,OAAO,CAAE,KAA4B,CAAC,IAAI,CAAC,EACjD,CAAC;QACD,OAAQ,KAAuB,CAAC,IAAI,CAAC;IACvC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAeD,MAAM,UAAU,UAAU,CAAI,KAAc;IAC1C,IACE,KAAK,KAAK,IAAI;QACd,OAAO,KAAK,KAAK,QAAQ;QACzB,MAAM,IAAK,KAAiC;QAC5C,CAAC,CAAC,YAAY,IAAK,KAAiC,CAAC,EACrD,CAAC;QACD,OAAQ,KAAqB,CAAC,IAAI,CAAC;IACrC,CAAC;IACD,OAAO,KAAU,CAAC;AACpB,CAAC"}
@@ -1,18 +0,0 @@
1
- export declare const SessionCapabilities: {
2
- readonly HasGit: "hasGit";
3
- readonly HasWorkspaceFiles: "hasWorkspaceFiles";
4
- readonly SupportsKbPush: "supportsKbPush";
5
- readonly HasArtifacts: "hasArtifacts";
6
- readonly SupportsForking: "supportsForking";
7
- readonly SupportsSharing: "supportsSharing";
8
- };
9
- export type SessionCapability = (typeof SessionCapabilities)[keyof typeof SessionCapabilities];
10
- export interface SessionProfileDescriptor {
11
- readonly key: string;
12
- readonly biomeId: string;
13
- readonly label: string;
14
- readonly description?: string;
15
- readonly capabilities: readonly string[];
16
- }
17
- export declare function useSessionProfileDescriptor(biomeId: string | undefined, profileKey: string | undefined): SessionProfileDescriptor;
18
- //# sourceMappingURL=session-profiles.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-profiles.d.ts","sourceRoot":"","sources":["../../../src/lib/biome-host/session-profiles.ts"],"names":[],"mappings":"AA6BA,eAAO,MAAM,mBAAmB;;;;;;;CAatB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAC3B,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAC;AAEjE,MAAM,WAAW,wBAAwB;IAKvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAE9B,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAcD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,wBAAwB,CAqB1B"}
@@ -1,35 +0,0 @@
1
- import { useMemo } from 'react';
2
- import { biomeRegistry, useBiomeRegistryRevision } from './biome-registry';
3
- export const SessionCapabilities = {
4
- HasGit: 'hasGit',
5
- HasWorkspaceFiles: 'hasWorkspaceFiles',
6
- SupportsKbPush: 'supportsKbPush',
7
- HasArtifacts: 'hasArtifacts',
8
- SupportsForking: 'supportsForking',
9
- SupportsSharing: 'supportsSharing',
10
- };
11
- export function useSessionProfileDescriptor(biomeId, profileKey) {
12
- const revision = useBiomeRegistryRevision();
13
- return useMemo(() => {
14
- const fallback = {
15
- key: profileKey ?? 'unknown',
16
- biomeId: biomeId ?? 'unknown',
17
- label: profileKey ?? 'Unknown profile',
18
- capabilities: [],
19
- };
20
- if (!profileKey)
21
- return fallback;
22
- let crossPluginMatch;
23
- for (const biome of biomeRegistry.list()) {
24
- for (const profile of biome.session?.profiles ?? []) {
25
- if (profile.key !== profileKey)
26
- continue;
27
- if (biomeId != null && profile.biomeId === biomeId)
28
- return profile;
29
- crossPluginMatch ??= profile;
30
- }
31
- }
32
- return crossPluginMatch ?? fallback;
33
- }, [revision, biomeId, profileKey]);
34
- }
35
- //# sourceMappingURL=session-profiles.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-profiles.js","sourceRoot":"","sources":["../../../src/lib/biome-host/session-profiles.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAU3E,MAAM,CAAC,MAAM,mBAAmB,GAAG;IAEjC,MAAM,EAAE,QAAQ;IAEhB,iBAAiB,EAAE,mBAAmB;IAEtC,cAAc,EAAE,gBAAgB;IAEhC,YAAY,EAAE,cAAc;IAE5B,eAAe,EAAE,iBAAiB;IAElC,eAAe,EAAE,iBAAiB;CAC1B,CAAC;AAgCX,MAAM,UAAU,2BAA2B,CACzC,OAA2B,EAC3B,UAA8B;IAE9B,MAAM,QAAQ,GAAG,wBAAwB,EAAE,CAAC;IAC5C,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,MAAM,QAAQ,GAA6B;YACzC,GAAG,EAAE,UAAU,IAAI,SAAS;YAC5B,OAAO,EAAE,OAAO,IAAI,SAAS;YAC7B,KAAK,EAAE,UAAU,IAAI,iBAAiB;YACtC,YAAY,EAAE,EAAE;SACjB,CAAC;QACF,IAAI,CAAC,UAAU;YAAE,OAAO,QAAQ,CAAC;QACjC,IAAI,gBAAsD,CAAC;QAC3D,KAAK,MAAM,KAAK,IAAI,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;YACzC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,CAAC;gBACpD,IAAI,OAAO,CAAC,GAAG,KAAK,UAAU;oBAAE,SAAS;gBACzC,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO;oBAAE,OAAO,OAAO,CAAC;gBACnE,gBAAgB,KAAK,OAAO,CAAC;YAC/B,CAAC;QACH,CAAC;QACD,OAAO,gBAAgB,IAAI,QAAQ,CAAC;IAEtC,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;AACtC,CAAC"}
@@ -1,69 +0,0 @@
1
- /**
2
- * Frontend mirror of the backend `ResponseEnvelopeInterceptor`.
3
- *
4
- * Every 2xx body the platform returns is wrapped as `{ data: T }`, or
5
- * `{ data: T[], pagination }` for a paginated list (single source of truth:
6
- * `@xemahq/platform-common`'s `ResponseEnvelopeInterceptor`). Orval's
7
- * `custom-fetch` mutators usually peel that envelope at the HTTP boundary, but
8
- * spec drift (a controller typed `T[]` that emits `{ data }`), public clients
9
- * that DON'T peel, and transient partial responses leave a `.data`-wrapped or
10
- * mis-shaped value where biome code expects a bare value/array — and a raw
11
- * `.map` on it white-screens the route.
12
- *
13
- * These are the ONE shared, pure unwrap helpers — no host context, no fetch.
14
- * Biomes (and the host shell's own `unwrap-list.ts`) should adopt these instead
15
- * of re-implementing the guard. This is the shared helper only; adopters are
16
- * migrated incrementally, not in a blanket rewrite.
17
- */
18
-
19
- /**
20
- * Resilient list unwrap for DISPLAY surfaces — normalises a React-Query `.data`
21
- * to an array so the page renders its empty/error state instead of crashing:
22
- *
23
- * - already an array → returned as-is
24
- * - `{ data: T[] }` envelope → `value.data`
25
- * - anything else → `[]`
26
- *
27
- * Deliberately NON-throwing: the query's own `isError`/`isLoading` flags drive
28
- * the visible error/empty UI; this only guards the render path. For
29
- * mutation/single-resource flows where an unexpected shape is a real bug, use
30
- * {@link unwrapData} (fail-fast) instead.
31
- */
32
- export function unwrapList<T>(value: unknown): T[] {
33
- if (Array.isArray(value)) {
34
- return value as T[];
35
- }
36
- if (
37
- value !== null &&
38
- typeof value === 'object' &&
39
- Array.isArray((value as { data?: unknown }).data)
40
- ) {
41
- return (value as { data: T[] }).data;
42
- }
43
- return [];
44
- }
45
-
46
- /**
47
- * Fail-fast single-resource unwrap. Returns `value.data` when the value is a
48
- * `{ data }` envelope; returns the value unchanged when it is already the
49
- * unwrapped resource (an Orval client that peeled the envelope at the boundary).
50
- *
51
- * Use this on mutation / single-resource flows where the response SHOULD be a
52
- * concrete object — unlike {@link unwrapList}, it does not silently coerce, so
53
- * a genuinely-unexpected shape surfaces as the value rather than being masked.
54
- *
55
- * A paginated envelope (`{ data, pagination }`) is returned INTACT — callers
56
- * that need both fields read them off the returned object; peeling only `data`
57
- * would drop the pagination cursor.
58
- */
59
- export function unwrapData<T>(value: unknown): T {
60
- if (
61
- value !== null &&
62
- typeof value === 'object' &&
63
- 'data' in (value as Record<string, unknown>) &&
64
- !('pagination' in (value as Record<string, unknown>))
65
- ) {
66
- return (value as { data: T }).data;
67
- }
68
- return value as T;
69
- }
@@ -1,99 +0,0 @@
1
- /**
2
- * Session profile descriptors — coarse capability flags that gate which
3
- * UI surfaces a session shows. A profile is owned by exactly one biome
4
- * (matched by `(biomeId, key)`) and declares a set of namespaced
5
- * capability strings the host fans out as `SessionContext.capabilities`.
6
- *
7
- * Biomes consume capabilities through each contribution's `appliesTo`
8
- * predicate — the host shell itself never branches on a capability
9
- * string. This keeps the host neutral; a session that doesn't declare
10
- * `hasGit` simply gets no git-touching contributions because every git
11
- * contribution gates on `ctx.capabilities.has('hasGit')`.
12
- *
13
- * Capability namespacing: well-known capabilities are documented here as
14
- * constants so first-party biomes agree on a vocabulary; third-party
15
- * biomes MUST namespace their own capabilities (`<biome-id>:<flag>`)
16
- * to avoid collisions.
17
- */
18
- import { useMemo } from 'react';
19
-
20
- import { biomeRegistry, useBiomeRegistryRevision } from './biome-registry';
21
-
22
- /**
23
- * Well-known capability flags first-party biomes share. Third-party
24
- * biomes SHOULD prefer namespacing their own (`<biome-id>:<flag>`).
25
- *
26
- * Adding a constant here is a contract-stability statement — it
27
- * documents a flag enough first-party biomes use that we want a stable
28
- * spelling. Removing one is a breaking change.
29
- */
30
- export const SessionCapabilities = {
31
- /** Session backs a git workspace; branches, commits, push, PR. */
32
- HasGit: 'hasGit',
33
- /** Session emits workspace file mutations (created/edited/deleted). */
34
- HasWorkspaceFiles: 'hasWorkspaceFiles',
35
- /** Session can publish artefacts back to the knowledge base. */
36
- SupportsKbPush: 'supportsKbPush',
37
- /** Session can open / render in-workspace artefacts (apps, docs). */
38
- HasArtifacts: 'hasArtifacts',
39
- /** Session can fork to a new branch. */
40
- SupportsForking: 'supportsForking',
41
- /** Session can share its output (deliverable) to other surfaces. */
42
- SupportsSharing: 'supportsSharing',
43
- } as const;
44
-
45
- export type SessionCapability =
46
- (typeof SessionCapabilities)[keyof typeof SessionCapabilities];
47
-
48
- export interface SessionProfileDescriptor {
49
- /**
50
- * Stable profile key — matches the backend's `Session.profileKey`
51
- * (or the session manifest slug for newer profiles).
52
- */
53
- readonly key: string;
54
- /** Biome that owns this profile. */
55
- readonly biomeId: string;
56
- /** Displayed label. */
57
- readonly label: string;
58
- readonly description?: string;
59
- /** Capability flags the host fans out as `SessionContext.capabilities`. */
60
- readonly capabilities: readonly string[];
61
- }
62
-
63
- /**
64
- * Hook returning the descriptor that matches a session's `(biomeId,
65
- * profileKey)`. Falls back to a synthetic descriptor with the well-known
66
- * core capabilities when no biome owns the profile — that's how
67
- * sessions started before any biome loaded still get usable defaults
68
- * (today's behaviour).
69
- *
70
- * Resolution order:
71
- * 1. Exact match on `(biomeId, key)`.
72
- * 2. Exact match on `key` alone (cross-biome profile name).
73
- * 3. Synthetic fallback (`unknown` biome, empty capabilities).
74
- */
75
- export function useSessionProfileDescriptor(
76
- biomeId: string | undefined,
77
- profileKey: string | undefined,
78
- ): SessionProfileDescriptor {
79
- const revision = useBiomeRegistryRevision();
80
- return useMemo(() => {
81
- const fallback: SessionProfileDescriptor = {
82
- key: profileKey ?? 'unknown',
83
- biomeId: biomeId ?? 'unknown',
84
- label: profileKey ?? 'Unknown profile',
85
- capabilities: [],
86
- };
87
- if (!profileKey) return fallback;
88
- let crossPluginMatch: SessionProfileDescriptor | undefined;
89
- for (const biome of biomeRegistry.list()) {
90
- for (const profile of biome.session?.profiles ?? []) {
91
- if (profile.key !== profileKey) continue;
92
- if (biomeId != null && profile.biomeId === biomeId) return profile;
93
- crossPluginMatch ??= profile;
94
- }
95
- }
96
- return crossPluginMatch ?? fallback;
97
- // eslint-disable-next-line react-hooks/exhaustive-deps
98
- }, [revision, biomeId, profileKey]);
99
- }