@urbicon-ui/blocks 6.40.3 → 6.43.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 (84) hide show
  1. package/dist/components/Calendar/calendar.variants.d.ts +63 -63
  2. package/dist/components/Chat/A2UIView/A2UINode.svelte +20 -39
  3. package/dist/components/Chat/A2UIView/A2UIView.svelte +47 -50
  4. package/dist/components/Chat/A2UIView/a2ui-basic-catalog.d.ts +17 -0
  5. package/dist/components/Chat/A2UIView/a2ui-basic-catalog.js +69 -0
  6. package/dist/components/Chat/A2UIView/a2ui-catalog.d.ts +95 -0
  7. package/dist/components/Chat/A2UIView/a2ui-catalog.js +87 -0
  8. package/dist/components/Chat/A2UIView/a2ui-node-common.d.ts +40 -0
  9. package/dist/components/Chat/A2UIView/a2ui-node-common.js +72 -0
  10. package/dist/components/Chat/A2UIView/a2ui-prompt.d.ts +21 -10
  11. package/dist/components/Chat/A2UIView/a2ui-prompt.js +170 -24
  12. package/dist/components/Chat/A2UIView/a2ui-registry.d.ts +1 -1
  13. package/dist/components/Chat/A2UIView/a2ui-registry.js +2 -1
  14. package/dist/components/Chat/A2UIView/a2ui-render.d.ts +30 -0
  15. package/dist/components/Chat/A2UIView/a2ui-render.js +61 -23
  16. package/dist/components/Chat/A2UIView/a2ui-router.d.ts +136 -0
  17. package/dist/components/Chat/A2UIView/a2ui-router.js +264 -0
  18. package/dist/components/Chat/A2UIView/a2ui-schema.d.ts +45 -0
  19. package/dist/components/Chat/A2UIView/a2ui-schema.js +133 -0
  20. package/dist/components/Chat/A2UIView/a2ui-stream.d.ts +120 -0
  21. package/dist/components/Chat/A2UIView/a2ui-stream.js +406 -0
  22. package/dist/components/Chat/A2UIView/a2ui-transcript.d.ts +65 -0
  23. package/dist/components/Chat/A2UIView/a2ui-transcript.js +101 -0
  24. package/dist/components/Chat/A2UIView/a2ui-validate.d.ts +33 -1
  25. package/dist/components/Chat/A2UIView/a2ui-validate.js +167 -63
  26. package/dist/components/Chat/A2UIView/a2ui.types.d.ts +50 -0
  27. package/dist/components/Chat/A2UIView/a2ui.types.js +38 -1
  28. package/dist/components/Chat/A2UIView/index.d.ts +35 -2
  29. package/dist/components/Chat/A2UIView/index.js +13 -0
  30. package/dist/components/Chat/A2UIView/urbicon/UrbiconA2UINode.svelte +831 -0
  31. package/dist/components/Chat/A2UIView/urbicon/UrbiconA2UINode.svelte.d.ts +4 -0
  32. package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-catalog.d.ts +14 -0
  33. package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-catalog.js +87 -0
  34. package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-registry.d.ts +78 -0
  35. package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon-registry.js +783 -0
  36. package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon.variants.d.ts +43 -0
  37. package/dist/components/Chat/A2UIView/urbicon/a2ui-urbicon.variants.js +33 -0
  38. package/dist/components/Chat/A2UIView/urbicon/urbicon-axes.gen.d.ts +7 -0
  39. package/dist/components/Chat/A2UIView/urbicon/urbicon-axes.gen.js +86 -0
  40. package/dist/components/Chat/ChatMessage/chat-message.variants.d.ts +19 -19
  41. package/dist/components/CopyButton/copy-button.variants.d.ts +4 -4
  42. package/dist/components/FileUpload/FileUpload.svelte +8 -5
  43. package/dist/components/FileUpload/fileUpload.variants.js +15 -4
  44. package/dist/components/PinInput/PinInput.svelte +1 -0
  45. package/dist/components/PinInput/pin-input.variants.js +14 -1
  46. package/dist/components/TimeInput/time-input.variants.js +16 -1
  47. package/dist/i18n/index.d.ts +2 -398
  48. package/dist/internal/field-chrome.d.ts +18 -1
  49. package/dist/internal/field-chrome.js +18 -1
  50. package/dist/primitives/Alert/alert.variants.d.ts +8 -8
  51. package/dist/primitives/Avatar/avatar.variants.js +4 -1
  52. package/dist/primitives/Badge/badge.variants.js +11 -2
  53. package/dist/primitives/Button/button.variants.js +4 -1
  54. package/dist/primitives/Card/card.variants.js +4 -1
  55. package/dist/primitives/Checkbox/checkbox.variants.js +7 -1
  56. package/dist/primitives/Combobox/Combobox.svelte +9 -1
  57. package/dist/primitives/Combobox/combobox.variants.d.ts +25 -0
  58. package/dist/primitives/Combobox/combobox.variants.js +32 -3
  59. package/dist/primitives/Combobox/index.d.ts +8 -2
  60. package/dist/primitives/Dialog/dialog.variants.js +4 -1
  61. package/dist/primitives/Drawer/drawer.variants.js +4 -1
  62. package/dist/primitives/Input/input.variants.js +17 -1
  63. package/dist/primitives/JourneyTimeline/journey-timeline.variants.d.ts +22 -22
  64. package/dist/primitives/Menu/menu.variants.js +5 -1
  65. package/dist/primitives/Progress/progress.variants.js +5 -2
  66. package/dist/primitives/RadioGroup/radioGroup.variants.js +68 -9
  67. package/dist/primitives/Select/select.variants.js +13 -4
  68. package/dist/primitives/Slider/Slider.svelte +4 -1
  69. package/dist/primitives/Slider/slider.variants.js +9 -2
  70. package/dist/primitives/Spinner/spinner.variants.d.ts +16 -16
  71. package/dist/primitives/Stepper/stepper.variants.d.ts +11 -11
  72. package/dist/primitives/Textarea/textarea.variants.js +15 -1
  73. package/dist/primitives/Toast/Toaster.svelte +6 -9
  74. package/dist/primitives/Toast/toast.variants.d.ts +12 -12
  75. package/dist/primitives/Toast/toast.variants.js +5 -1
  76. package/dist/primitives/Toggle/toggle.variants.js +92 -18
  77. package/dist/primitives/Tooltip/tooltip.variants.d.ts +3 -3
  78. package/dist/primitives/Tooltip/tooltip.variants.js +5 -1
  79. package/dist/style/themes/index.css +8 -2
  80. package/dist/utils/guide.svelte.js +16 -4
  81. package/dist/utils/persistent-state.svelte.d.ts +25 -1
  82. package/dist/utils/persistent-state.svelte.js +98 -10
  83. package/docs/MIGRATION-v5.md +29 -0
  84. package/package.json +3 -3
@@ -21,8 +21,10 @@
21
21
  * because "dangling" is a warning mid-stream and an error once settled.
22
22
  */
23
23
  import { A2UI_ISSUE_CODES } from './a2ui.types.js';
24
+ import { basicA2uiCatalogSpec, resolveCatalog } from './a2ui-catalog.js';
24
25
  import { cloneData, deleteAtPointer, getAtPointer, setAtPointer } from './a2ui-data.js';
25
- import { A2UI_ICON_NAMES, A2UI_IGNORED_PROPS, A2UI_REGISTRY, A2UI_SUPPORTED_VERSIONS, A2UI_SVG_PATH_RE, UNSUPPORTED_A2UI_COMPONENTS } from './a2ui-registry.js';
26
+ import { A2UI_SUPPORTED_VERSIONS, A2UI_SVG_PATH_RE } from './a2ui-registry.js';
27
+ import { validateSchemaWrite } from './a2ui-schema.js';
26
28
  const PROTO_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
27
29
  // DoS caps. The per-surface render walk is already bounded (depth 32, nodes
28
30
  // 512), but that leaves the *number* of surfaces and the per-message component
@@ -96,7 +98,7 @@ function dynamicRefIssues(value, path, surfaceId) {
96
98
  }
97
99
  return [];
98
100
  }
99
- function validateProp(componentName, key, spec, value, path, surfaceId) {
101
+ function validateProp(componentName, key, spec, value, path, surfaceId, iconNames) {
100
102
  const issues = [];
101
103
  const mismatch = (detail) => {
102
104
  issues.push(issue('error', A2UI_ISSUE_CODES.TYPE_MISMATCH, detail, { surfaceId, path }));
@@ -130,12 +132,20 @@ function validateProp(componentName, key, spec, value, path, surfaceId) {
130
132
  return mismatch(`"${key}" on ${componentName} must be a component id (string)`);
131
133
  case 'childList':
132
134
  return validateChildList(componentName, key, value, path, surfaceId);
135
+ case 'labeledChildren':
136
+ return validateLabeledChildren(componentName, key, value, path, surfaceId);
133
137
  case 'action':
134
138
  return validateAction(componentName, key, value, path, surfaceId);
135
139
  case 'options':
136
- return validateOptions(componentName, key, value, path, surfaceId);
140
+ // A dynamic options prop may be a { path } binding to a list the agent
141
+ // fetched — the shape is checked when it resolves (see collectGraphIssues),
142
+ // because at envelope time the data model may not carry it yet.
143
+ if (spec.dynamic && isDynamicRef(value)) {
144
+ return { store: true, issues: dynamicRefIssues(value, path, surfaceId) };
145
+ }
146
+ return validateOptions(componentName, key, value, path, surfaceId, spec.dynamic);
137
147
  case 'icon':
138
- return validateIcon(value, path, surfaceId);
148
+ return validateIcon(value, path, surfaceId, iconNames);
139
149
  case 'accessibility':
140
150
  if (isPlainObject(value))
141
151
  return { store: true, issues };
@@ -168,6 +178,33 @@ function validateChildList(componentName, key, value, path, surfaceId) {
168
178
  ]
169
179
  };
170
180
  }
181
+ /**
182
+ * A `labeledChildren` value is an array of `{ label, child }` items: `label` is
183
+ * a (dynamic) string, `child` a component id referenced by the labelled slot
184
+ * (Accordion items, Tabs later). The whole prop is rejected if ANY item is
185
+ * malformed, so the render/graph layers may assume every stored item is a valid
186
+ * `{ label, child: string }` — this keeps the item→child index alignment the
187
+ * dispatcher relies on when zipping labels with resolved child nodes.
188
+ */
189
+ function validateLabeledChildren(componentName, key, value, path, surfaceId) {
190
+ const bad = (detail) => ({
191
+ store: false,
192
+ issues: [issue('error', A2UI_ISSUE_CODES.TYPE_MISMATCH, detail, { surfaceId, path })]
193
+ });
194
+ if (!Array.isArray(value))
195
+ return bad(`"${key}" on ${componentName} must be an array of { label, child } items`);
196
+ const issues = [];
197
+ for (let i = 0; i < value.length; i++) {
198
+ const item = value[i];
199
+ if (!isPlainObject(item) || typeof item.child !== 'string' || !isDynamicString(item.label)) {
200
+ return bad(`item ${i} on ${componentName} must be { label, child: string }`);
201
+ }
202
+ for (const labelIssue of dynamicRefIssues(item.label, `${path}/${i}/label`, surfaceId)) {
203
+ issues.push(labelIssue);
204
+ }
205
+ }
206
+ return { store: true, issues };
207
+ }
171
208
  function validateAction(componentName, key, value, path, surfaceId) {
172
209
  const issues = [];
173
210
  if (!isPlainObject(value)) {
@@ -219,13 +256,13 @@ function validateAction(componentName, key, value, path, surfaceId) {
219
256
  }));
220
257
  return { store: false, issues };
221
258
  }
222
- function validateOptions(componentName, key, value, path, surfaceId) {
259
+ function validateOptions(componentName, key, value, path, surfaceId, dynamic) {
223
260
  const bad = (detail) => ({
224
261
  store: false,
225
262
  issues: [issue('error', A2UI_ISSUE_CODES.TYPE_MISMATCH, detail, { surfaceId, path })]
226
263
  });
227
264
  if (!Array.isArray(value))
228
- return bad(`"${key}" on ${componentName} must be an array of { label, value } options`);
265
+ return bad(`"${key}" on ${componentName} must be an array of { label, value } options${dynamic ? ' or a { path } binding to one' : ''}`);
229
266
  const issues = [];
230
267
  const seenValues = new Set();
231
268
  for (let i = 0; i < value.length; i++) {
@@ -248,9 +285,9 @@ function validateOptions(componentName, key, value, path, surfaceId) {
248
285
  }
249
286
  return { store: true, issues };
250
287
  }
251
- function validateIcon(value, path, surfaceId) {
288
+ function validateIcon(value, path, surfaceId, iconNames) {
252
289
  if (typeof value === 'string') {
253
- if (A2UI_ICON_NAMES.includes(value))
290
+ if (iconNames.includes(value))
254
291
  return { store: true, issues: [] };
255
292
  return {
256
293
  store: true,
@@ -326,9 +363,10 @@ function validateComponent(surface, raw, envelopeIndex, compIndex, idsSeen) {
326
363
  surface.components.set(id, { id, component: '', props: new Map(), sourceIndex: envelopeIndex });
327
364
  return;
328
365
  }
329
- const spec = A2UI_REGISTRY[componentName];
366
+ const catalog = surface.catalog;
367
+ const spec = catalog.registry[componentName];
330
368
  if (!spec) {
331
- if (UNSUPPORTED_A2UI_COMPONENTS.has(componentName)) {
369
+ if (catalog.unsupportedComponents.has(componentName)) {
332
370
  surface.issues.push(issue('error', A2UI_ISSUE_CODES.UNSUPPORTED_COMPONENT, `Component "${componentName}" is not part of the rendered subset`, {
333
371
  surfaceId,
334
372
  path: `${base}/component`
@@ -356,7 +394,7 @@ function validateComponent(surface, raw, envelopeIndex, compIndex, idsSeen) {
356
394
  surface.issues.push(issue('error', A2UI_ISSUE_CODES.PROTOTYPE_POLLUTION, `Prohibited property "${key}" on "${id}"`, { surfaceId, path: `${base}/${key}` }));
357
395
  continue;
358
396
  }
359
- if (A2UI_IGNORED_PROPS.has(key)) {
397
+ if (catalog.ignoredProps.has(key)) {
360
398
  surface.issues.push(issue('warning', A2UI_ISSUE_CODES.IGNORED_PROP, `Property "${key}" on ${componentName} is not supported and is ignored`, {
361
399
  surfaceId,
362
400
  path: `${base}/${key}`
@@ -368,7 +406,7 @@ function validateComponent(surface, raw, envelopeIndex, compIndex, idsSeen) {
368
406
  surface.issues.push(issue('error', A2UI_ISSUE_CODES.UNKNOWN_PROP, `Unknown property "${key}" on ${componentName}`, { surfaceId, path: `${base}/${key}` }));
369
407
  continue;
370
408
  }
371
- const result = validateProp(componentName, key, propSpec, comp[key], `${base}/${key}`, surfaceId);
409
+ const result = validateProp(componentName, key, propSpec, comp[key], `${base}/${key}`, surfaceId, catalog.iconNames);
372
410
  for (const propIssue of result.issues)
373
411
  surface.issues.push(propIssue);
374
412
  if (result.store)
@@ -382,24 +420,14 @@ function validateComponent(surface, raw, envelopeIndex, compIndex, idsSeen) {
382
420
  }));
383
421
  }
384
422
  }
385
- if (componentName === 'ChoicePicker') {
386
- if (props.get('displayStyle') === 'chips' || props.get('filterable') === true) {
387
- surface.issues.push(issue('warning', A2UI_ISSUE_CODES.CHOICEPICKER_FALLBACK, `ChoicePicker "${id}" chips/filterable are rendered with a fallback`, {
388
- surfaceId,
389
- path: base
390
- }));
391
- }
392
- }
393
- if (componentName === 'DateTimeInput') {
394
- // Both flags default to false in the spec — a DateTimeInput without either
395
- // would have no input UI at all. We read tolerantly (render a date input)
396
- // and report loudly so the agent fixes the payload.
397
- if (props.get('enableDate') !== true && props.get('enableTime') !== true) {
398
- surface.issues.push(issue('warning', A2UI_ISSUE_CODES.DATETIME_NO_MODE, `DateTimeInput "${id}" sets neither enableDate nor enableTime; rendering a date input`, {
399
- surfaceId,
400
- path: base
401
- }));
402
- }
423
+ // Per-component post-validation checks are catalog DATA (the Basic catalog
424
+ // owns ChoicePicker chips-fallback and DateTimeInput missing-mode); the engine
425
+ // stays free of catalog-specific branches. Message strings and issue paths are
426
+ // byte-identical to the pre-refactor hardcoded blocks.
427
+ const check = catalog.componentChecks?.[componentName];
428
+ if (check) {
429
+ for (const checkIssue of check({ id, props, surfaceId, base }))
430
+ surface.issues.push(checkIssue);
403
431
  }
404
432
  surface.components.set(id, { id, component: componentName, props, sourceIndex: envelopeIndex });
405
433
  }
@@ -426,9 +454,10 @@ const CREATE_SURFACE_KEYS = new Set([
426
454
  const UPDATE_COMPONENTS_KEYS = new Set(['surfaceId', 'components']);
427
455
  const UPDATE_DATA_MODEL_KEYS = new Set(['surfaceId', 'path', 'value']);
428
456
  const DELETE_SURFACE_KEYS = new Set(['surfaceId']);
429
- function createProcessor() {
457
+ function createProcessor(catalogs, dataSchema) {
430
458
  const surfaces = new Map();
431
459
  const globalIssues = [];
460
+ const defaultCatalog = catalogs[0];
432
461
  function handleCreateSurface(env, base) {
433
462
  const cs = env.createSurface;
434
463
  if (!isPlainObject(cs)) {
@@ -460,7 +489,9 @@ function createProcessor() {
460
489
  surfaceId,
461
490
  components: new Map(),
462
491
  dataModel: undefined,
463
- issues: []
492
+ issues: [],
493
+ catalog: defaultCatalog,
494
+ sendDataModel: false
464
495
  };
465
496
  surfaces.set(surfaceId, surface);
466
497
  if (typeof cs.catalogId !== 'string') {
@@ -469,6 +500,17 @@ function createProcessor() {
469
500
  path: `${base}/createSurface/catalogId`
470
501
  }));
471
502
  }
503
+ else {
504
+ const resolved = resolveCatalog(catalogs, cs.catalogId);
505
+ if (resolved) {
506
+ surface.catalog = resolved;
507
+ }
508
+ else if (catalogs.length >= 2) {
509
+ // Only a multi-catalog processor can meaningfully reject an id; a single
510
+ // catalog accepts any string (back-compat) and uses the default silently.
511
+ surface.issues.push(issue('warning', A2UI_ISSUE_CODES.UNKNOWN_CATALOG, `Unknown catalogId "${cs.catalogId}"; falling back to "${defaultCatalog.catalogId}"`, { surfaceId, path: `${base}/createSurface/catalogId` }));
512
+ }
513
+ }
472
514
  if ('theme' in cs) {
473
515
  surface.issues.push(issue('warning', A2UI_ISSUE_CODES.SURFACE_PROP_IGNORED, 'theme is ignored', {
474
516
  surfaceId,
@@ -476,10 +518,15 @@ function createProcessor() {
476
518
  }));
477
519
  }
478
520
  if ('sendDataModel' in cs) {
479
- surface.issues.push(issue('warning', A2UI_ISSUE_CODES.SURFACE_PROP_IGNORED, 'sendDataModel is ignored', {
480
- surfaceId,
481
- path: `${base}/createSurface/sendDataModel`
482
- }));
521
+ if (typeof cs.sendDataModel === 'boolean') {
522
+ surface.sendDataModel = cs.sendDataModel;
523
+ }
524
+ else {
525
+ surface.issues.push(issue('warning', A2UI_ISSUE_CODES.TYPE_MISMATCH, 'sendDataModel must be a boolean', {
526
+ surfaceId,
527
+ path: `${base}/createSurface/sendDataModel`
528
+ }));
529
+ }
483
530
  }
484
531
  flagExtraKeys(cs, CREATE_SURFACE_KEYS, surface.issues, surfaceId, `${base}/createSurface`, 'createSurface');
485
532
  }
@@ -558,6 +605,14 @@ function createProcessor() {
558
605
  return;
559
606
  }
560
607
  const hasValue = 'value' in udm;
608
+ // Schema validation (opt-in): a type mismatch on a declared pointer is an
609
+ // error, a write to an undeclared top-level branch a warning — both relayed
610
+ // to the agent. Runs on the raw written value before it enters the model.
611
+ if (dataSchema) {
612
+ const schemaIssues = validateSchemaWrite(dataSchema, pathValue, hasValue ? udm.value : undefined, surfaceId);
613
+ for (const schemaIssue of schemaIssues)
614
+ surface.issues.push(schemaIssue);
615
+ }
561
616
  const whole = pathValue === undefined || pathValue === '' || pathValue === '/';
562
617
  if (whole) {
563
618
  surface.dataModel = hasValue ? cloneData(udm.value) : undefined;
@@ -638,8 +693,9 @@ function createProcessor() {
638
693
  }
639
694
  return { surfaces, globalIssues, apply };
640
695
  }
641
- export function createA2uiProcessor() {
642
- return createProcessor();
696
+ export function createA2uiProcessor(options) {
697
+ const catalogs = options?.catalogs?.length ? options.catalogs : [basicA2uiCatalogSpec];
698
+ return createProcessor(catalogs, options?.dataSchema);
643
699
  }
644
700
  /**
645
701
  * Normalize a render payload into an envelope list. Accepts an envelope array,
@@ -690,37 +746,65 @@ export function collectGraphIssues(surface, options) {
690
746
  let nodeCount = 0;
691
747
  let overflowReported = false;
692
748
  let aborted = false;
749
+ // Child references are discovered by prop KIND (childId / childList) from the
750
+ // surface catalog's registry, not by fixed `child`/`children` names — so a
751
+ // catalog may name its child slots freely. Behaviour is unchanged for Basic,
752
+ // whose only child props ARE `child`/`children`.
753
+ const registry = surface.catalog.registry;
693
754
  const childTargets = (comp, scopePrefix) => {
694
755
  const out = [];
695
- const single = comp.props.get('child');
696
- if (typeof single === 'string')
697
- out.push({ id: single, scope: scopePrefix });
698
- const children = comp.props.get('children');
699
- if (Array.isArray(children)) {
700
- for (const childId of children)
701
- if (typeof childId === 'string')
702
- out.push({ id: childId, scope: scopePrefix });
703
- }
704
- else if (children && typeof children === 'object') {
705
- const template = children;
706
- if (typeof template.componentId === 'string' && typeof template.path === 'string') {
707
- const absPath = template.path.startsWith('/')
708
- ? template.path
709
- : `${scopePrefix ?? ''}/${template.path}`;
710
- const list = getAtPointer(surface.dataModel, absPath);
711
- if (Array.isArray(list)) {
712
- for (let i = 0; i < list.length; i++) {
713
- out.push({ id: template.componentId, scope: `${absPath}/${i}` });
756
+ const spec = registry[comp.component];
757
+ if (!spec)
758
+ return out;
759
+ for (const [key, propSpec] of Object.entries(spec.props)) {
760
+ if (propSpec.kind === 'childId') {
761
+ const single = comp.props.get(key);
762
+ if (typeof single === 'string')
763
+ out.push({ id: single, scope: scopePrefix });
764
+ }
765
+ else if (propSpec.kind === 'labeledChildren') {
766
+ const items = comp.props.get(key);
767
+ if (Array.isArray(items)) {
768
+ for (const item of items) {
769
+ if (item && typeof item === 'object') {
770
+ const child = item.child;
771
+ if (typeof child === 'string')
772
+ out.push({ id: child, scope: scopePrefix });
773
+ }
714
774
  if (out.length > maxNodes)
715
775
  break; // bound expansion
716
776
  }
717
777
  }
718
- else if (list !== undefined) {
719
- issues.push(issue('warning', A2UI_ISSUE_CODES.TEMPLATE_PATH_NOT_ARRAY, `Template path "${absPath}" did not resolve to an array`, {
720
- surfaceId
721
- }));
778
+ }
779
+ else if (propSpec.kind === 'childList') {
780
+ const children = comp.props.get(key);
781
+ if (Array.isArray(children)) {
782
+ for (const childId of children)
783
+ if (typeof childId === 'string')
784
+ out.push({ id: childId, scope: scopePrefix });
785
+ }
786
+ else if (children && typeof children === 'object') {
787
+ const template = children;
788
+ if (typeof template.componentId === 'string' && typeof template.path === 'string') {
789
+ const absPath = template.path.startsWith('/')
790
+ ? template.path
791
+ : `${scopePrefix ?? ''}/${template.path}`;
792
+ const list = getAtPointer(surface.dataModel, absPath);
793
+ if (Array.isArray(list)) {
794
+ for (let i = 0; i < list.length; i++) {
795
+ out.push({ id: template.componentId, scope: `${absPath}/${i}` });
796
+ if (out.length > maxNodes)
797
+ break; // bound expansion
798
+ }
799
+ }
800
+ else if (list !== undefined) {
801
+ issues.push(issue('warning', A2UI_ISSUE_CODES.TEMPLATE_PATH_NOT_ARRAY, `Template path "${absPath}" did not resolve to an array`, {
802
+ surfaceId
803
+ }));
804
+ }
805
+ // list === undefined → data not present yet; render nothing (graceful).
806
+ }
722
807
  }
723
- // list === undefined → data not present yet; render nothing (graceful).
724
808
  }
725
809
  }
726
810
  return out;
@@ -749,7 +833,27 @@ export function collectGraphIssues(surface, options) {
749
833
  }
750
834
  return;
751
835
  }
752
- if (comp.props.has('weight') && parentComponent !== 'Row' && parentComponent !== 'Column') {
836
+ // A bound `options` list is only checkable once it resolves — the envelope
837
+ // validator saw a { path }, not a list. Silent while the path is still
838
+ // undefined (the agent fills it in with a later updateDataModel); a warning
839
+ // once it holds something that is not an option list, because the control
840
+ // then renders empty and the user is stuck with no way to choose.
841
+ for (const [key, propSpec] of Object.entries(registry[comp.component]?.props ?? {})) {
842
+ if (propSpec.kind !== 'options' || !propSpec.dynamic)
843
+ continue;
844
+ const bound = comp.props.get(key);
845
+ if (!isPlainObject(bound) || typeof bound.path !== 'string')
846
+ continue;
847
+ // Same absolute/relative rule as a childList template path.
848
+ const pointer = bound.path.startsWith('/')
849
+ ? bound.path
850
+ : `${scopePrefix ?? ''}/${bound.path}`;
851
+ const resolvedList = getAtPointer(surface.dataModel, pointer);
852
+ if (resolvedList === undefined || Array.isArray(resolvedList))
853
+ continue;
854
+ issues.push(issue('warning', A2UI_ISSUE_CODES.OPTIONS_NOT_A_LIST, `"${key}" on "${id}" is bound to "${pointer}", which is not a list of options`, { surfaceId }));
855
+ }
856
+ if (comp.props.has('weight') && !surface.catalog.flexContainers.has(parentComponent ?? '')) {
753
857
  issues.push(issue('warning', A2UI_ISSUE_CODES.WEIGHT_CONTEXT, `"weight" on "${id}" is only honored as a direct child of Row or Column`, {
754
858
  surfaceId
755
859
  }));
@@ -30,6 +30,19 @@ export interface A2uiActionEvent {
30
30
  /** ISO 8601 timestamp (`new Date().toISOString()`). */
31
31
  timestamp: string;
32
32
  context: Record<string, unknown>;
33
+ /**
34
+ * The surface's complete data model — present only when the surface was
35
+ * created with `sendDataModel: true`.
36
+ *
37
+ * The spec calls this the client data model and has the client append it to
38
+ * the metadata of every message sent to the agent, but leaves WHERE metadata
39
+ * travels to the transport binding (A2A `metadata`, HTTP headers, …). This
40
+ * field is that payload; put it wherever your transport carries metadata.
41
+ *
42
+ * It is the antidote to a half-filled `context`: the agent sees everything the
43
+ * user entered, including fields it forgot to list on the action.
44
+ */
45
+ dataModel?: unknown;
33
46
  }
34
47
  /** Severity of a validation issue. `error` renders a fault chip; `warning` degrades. */
35
48
  export type A2uiIssueSeverity = 'error' | 'warning';
@@ -115,5 +128,42 @@ export declare const A2UI_ISSUE_CODES: {
115
128
  readonly CHOICEPICKER_FALLBACK: "CHOICEPICKER_FALLBACK";
116
129
  /** A `createSurface`-only prop (`theme`, `sendDataModel`) that the engine ignores. */
117
130
  readonly SURFACE_PROP_IGNORED: "SURFACE_PROP_IGNORED";
131
+ /**
132
+ * `createSurface.catalogId` names no configured catalog — the surface falls
133
+ * back to the first (default) catalog. Only emitted in a multi-catalog setup;
134
+ * a single-catalog processor accepts any id string silently (back-compat).
135
+ */
136
+ readonly UNKNOWN_CATALOG: "UNKNOWN_CATALOG";
137
+ /**
138
+ * A `Text` value looks like it carries Markdown, but `Text` renders PLAIN.
139
+ * A heuristic warning steering the agent to `RichText` (Urbicon catalog).
140
+ */
141
+ readonly MARKDOWN_IN_TEXT: "MARKDOWN_IN_TEXT";
142
+ /**
143
+ * An `updateDataModel` write's value type contradicts the surface data
144
+ * schema's declared type at that pointer (opt-in `dataSchema`).
145
+ */
146
+ readonly SCHEMA_TYPE_MISMATCH: "SCHEMA_TYPE_MISMATCH";
147
+ /**
148
+ * An `updateDataModel` write targets a top-level path the surface data schema
149
+ * does not declare (opt-in `dataSchema`; warning).
150
+ */
151
+ readonly SCHEMA_UNDECLARED_PATH: "SCHEMA_UNDECLARED_PATH";
152
+ /**
153
+ * `createSurface` re-used a surfaceId that another source (an earlier chat
154
+ * message) already owns. The spec requires a surfaceId to be unique for the
155
+ * renderer's lifetime, so this is a protocol violation the agent should hear
156
+ * about; {@link A2uiSurfaceRouter} still transfers ownership to the newest
157
+ * source so the fresh surface renders (last writer wins). Router-only —
158
+ * a single processor reports the same case as `DUPLICATE_SURFACE`.
159
+ */
160
+ readonly SURFACE_RECREATED: "SURFACE_RECREATED";
161
+ /**
162
+ * An `options` `{ path }` binding resolved to a value that is not a list of
163
+ * `{ label, value }` options — the control renders empty. Not emitted while
164
+ * the path is still `undefined`: the agent is expected to fill it in with a
165
+ * later `updateDataModel`, and an empty chooser mid-flow is normal.
166
+ */
167
+ readonly OPTIONS_NOT_A_LIST: "OPTIONS_NOT_A_LIST";
118
168
  };
119
169
  export type A2uiIssueCode = (typeof A2UI_ISSUE_CODES)[keyof typeof A2UI_ISSUE_CODES];
@@ -81,5 +81,42 @@ export const A2UI_ISSUE_CODES = {
81
81
  /** `ChoicePicker` `displayStyle: 'chips'` / `filterable` — rendered with a fallback. */
82
82
  CHOICEPICKER_FALLBACK: 'CHOICEPICKER_FALLBACK',
83
83
  /** A `createSurface`-only prop (`theme`, `sendDataModel`) that the engine ignores. */
84
- SURFACE_PROP_IGNORED: 'SURFACE_PROP_IGNORED'
84
+ SURFACE_PROP_IGNORED: 'SURFACE_PROP_IGNORED',
85
+ /**
86
+ * `createSurface.catalogId` names no configured catalog — the surface falls
87
+ * back to the first (default) catalog. Only emitted in a multi-catalog setup;
88
+ * a single-catalog processor accepts any id string silently (back-compat).
89
+ */
90
+ UNKNOWN_CATALOG: 'UNKNOWN_CATALOG',
91
+ /**
92
+ * A `Text` value looks like it carries Markdown, but `Text` renders PLAIN.
93
+ * A heuristic warning steering the agent to `RichText` (Urbicon catalog).
94
+ */
95
+ MARKDOWN_IN_TEXT: 'MARKDOWN_IN_TEXT',
96
+ /**
97
+ * An `updateDataModel` write's value type contradicts the surface data
98
+ * schema's declared type at that pointer (opt-in `dataSchema`).
99
+ */
100
+ SCHEMA_TYPE_MISMATCH: 'SCHEMA_TYPE_MISMATCH',
101
+ /**
102
+ * An `updateDataModel` write targets a top-level path the surface data schema
103
+ * does not declare (opt-in `dataSchema`; warning).
104
+ */
105
+ SCHEMA_UNDECLARED_PATH: 'SCHEMA_UNDECLARED_PATH',
106
+ /**
107
+ * `createSurface` re-used a surfaceId that another source (an earlier chat
108
+ * message) already owns. The spec requires a surfaceId to be unique for the
109
+ * renderer's lifetime, so this is a protocol violation the agent should hear
110
+ * about; {@link A2uiSurfaceRouter} still transfers ownership to the newest
111
+ * source so the fresh surface renders (last writer wins). Router-only —
112
+ * a single processor reports the same case as `DUPLICATE_SURFACE`.
113
+ */
114
+ SURFACE_RECREATED: 'SURFACE_RECREATED',
115
+ /**
116
+ * An `options` `{ path }` binding resolved to a value that is not a list of
117
+ * `{ label, value }` options — the control renders empty. Not emitted while
118
+ * the path is still `undefined`: the agent is expected to fill it in with a
119
+ * later `updateDataModel`, and an empty chooser mid-flow is normal.
120
+ */
121
+ OPTIONS_NOT_A_LIST: 'OPTIONS_NOT_A_LIST'
85
122
  };
@@ -1,6 +1,8 @@
1
1
  import type { HTMLAttributes } from 'svelte/elements';
2
2
  import type { MarkdownUrlPolicy } from '../markdown/types.js';
3
3
  import type { A2uiActionEvent, A2uiValidationIssue } from './a2ui.types.js';
4
+ import type { A2uiCatalog } from './a2ui-catalog.js';
5
+ import type { A2uiDataSchema } from './a2ui-schema.js';
4
6
  import type { A2UIViewSlots } from './a2ui-view.variants.js';
5
7
  /**
6
8
  * @description Renders a trusted-catalog A2UI (Agent-to-UI, v0.9.1 `basic`
@@ -16,7 +18,13 @@ import type { A2UIViewSlots } from './a2ui-view.variants.js';
16
18
  * renderer — wire it in per surface via `partRenderers.a2ui` to keep it out of
17
19
  * the base bundle. Generate the agent-side prompt with the shipped
18
20
  * `a2uiSystemPrompt()`; validate a payload without a DOM with
19
- * `createA2uiProcessor()` — never hand-roll either.
21
+ * `createA2uiProcessor()` — never hand-roll either. Opt into the richer
22
+ * Urbicon-native catalog (real intents/variants, Section, RichText, Accordion)
23
+ * by passing `catalogs={[urbiconA2uiCatalog]}` (tree-shaken out otherwise), and
24
+ * type-check the data model with an optional `dataSchema`. One view owns the
25
+ * surfaces of ONE payload; to let an agent patch a surface it sent in an earlier
26
+ * chat turn (multi-step forms), route the later envelopes into that payload with
27
+ * `A2uiSurfaceRouter` — never give a second view the same surfaceId.
20
28
  * @tag ai
21
29
  * @related ChatMessage
22
30
  * @stability experimental
@@ -77,6 +85,23 @@ export interface A2UIViewProps extends Omit<HTMLAttributes<HTMLDivElement>, 'cla
77
85
  blockedImageLabel?: string;
78
86
  /** Screen-reader label of the streaming placeholder. @default 'Loading UI' */
79
87
  pendingLabel?: string;
88
+ /**
89
+ * Additional A2UI catalogs this view can render, beyond the always-present
90
+ * Basic catalog (which is prepended automatically as the default/fallback). A
91
+ * surface renders through the catalog whose id its `createSurface.catalogId`
92
+ * names. Pass the shipped `urbiconA2uiCatalog` to enable the Urbicon-native
93
+ * catalog. Resolved once at init (icon setup reads context) — keep it
94
+ * referentially stable.
95
+ */
96
+ catalogs?: readonly A2uiCatalog[];
97
+ /**
98
+ * Optional surface data schema. When set, every `updateDataModel` write is
99
+ * validated against it (type mismatch on a declared pointer → error;
100
+ * undeclared top-level branch → warning), reported via `onValidationError`.
101
+ * Document the same schema to the agent with `a2uiDataSchemaSection`. Keep it
102
+ * referentially stable.
103
+ */
104
+ dataSchema?: A2uiDataSchema;
80
105
  /** Extra classes merged onto the root element. */
81
106
  class?: string;
82
107
  /** Strip the component's default tv() classes. */
@@ -96,7 +121,15 @@ export interface A2UIViewProps extends Omit<HTMLAttributes<HTMLDivElement>, 'cla
96
121
  }
97
122
  export { default as A2UIView } from './A2UIView.svelte';
98
123
  export { A2UI_ISSUE_CODES, type A2uiActionEvent, type A2uiEnvelope, type A2uiIssueCode, type A2uiIssueSeverity, type A2uiValidationIssue } from './a2ui.types.js';
124
+ export { basicA2uiCatalog } from './a2ui-basic-catalog.js';
125
+ export { type A2uiCatalog, type A2uiCatalogSpec, type A2uiComponentCheck, type A2uiComponentCheckContext, basicA2uiCatalogSpec, resolveCatalog } from './a2ui-catalog.js';
99
126
  export { a2uiSystemPrompt } from './a2ui-prompt.js';
100
127
  export { A2UI_CATALOG_ID, A2UI_ICON_NAMES, A2UI_REGISTRY, A2UI_SUPPORTED_VERSIONS, type A2uiComponentSpec, type A2uiPropKind, type A2uiPropSpec, UNSUPPORTED_A2UI_COMPONENTS } from './a2ui-registry.js';
101
- export { type A2uiComponentInstance, type A2uiProcessor, type A2uiSurfaceState, collectGraphIssues, createA2uiProcessor, normalizeA2uiPayload } from './a2ui-validate.js';
128
+ export { type A2uiRoutePatch, type A2uiRouteResult, A2uiSurfaceRouter } from './a2ui-router.js';
129
+ export { type A2uiDataSchema, type A2uiSchemaField, type A2uiSchemaType, a2uiDataSchemaSection, validateSchemaWrite } from './a2ui-schema.js';
130
+ export { A2UI_FENCE_TAG, type A2uiStreamIssue, type A2uiStreamPart, A2uiStreamSplitter, type A2uiStreamTextPart, type A2uiStreamUiPart, type A2uiTransportSectionOptions, a2uiFencedTransportSection } from './a2ui-stream.js';
131
+ export { editA2uiPayload, type PatchedSurface, type RouteMessageResult, revokeMessage, routeMessageParts, sourceKey } from './a2ui-transcript.js';
132
+ export { type A2uiComponentInstance, type A2uiProcessor, type A2uiProcessorOptions, type A2uiSurfaceState, collectGraphIssues, createA2uiProcessor, normalizeA2uiPayload } from './a2ui-validate.js';
102
133
  export { type A2UIViewSlots, type A2UIViewVariants, a2uiViewVariants } from './a2ui-view.variants.js';
134
+ export { urbiconA2uiCatalog } from './urbicon/a2ui-urbicon-catalog.js';
135
+ export { SHARED_AXES, UNSUPPORTED_URBICON_A2UI_COMPONENTS, URBICON_A2UI_CATALOG_ID, URBICON_A2UI_ICON_NAMES, URBICON_A2UI_REGISTRY, type UrbiconComponentSpec, type UrbiconPropSpec, urbiconA2uiCatalogSpec } from './urbicon/a2ui-urbicon-registry.js';
@@ -1,7 +1,20 @@
1
1
  export { default as A2UIView } from './A2UIView.svelte';
2
2
  // ── Engine surface (Svelte-free; a server building the prompt has no DOM) ────
3
3
  export { A2UI_ISSUE_CODES } from './a2ui.types.js';
4
+ export { basicA2uiCatalog } from './a2ui-basic-catalog.js';
5
+ // ── Catalog abstraction (the seam for a second, custom catalog) ──────────────
6
+ export { basicA2uiCatalogSpec, resolveCatalog } from './a2ui-catalog.js';
4
7
  export { a2uiSystemPrompt } from './a2ui-prompt.js';
5
8
  export { A2UI_CATALOG_ID, A2UI_ICON_NAMES, A2UI_REGISTRY, A2UI_SUPPORTED_VERSIONS, UNSUPPORTED_A2UI_COMPONENTS } from './a2ui-registry.js';
9
+ // ── Cross-message surface routing (keeps a surface patchable after its turn) ──
10
+ export { A2uiSurfaceRouter } from './a2ui-router.js';
11
+ export { a2uiDataSchemaSection, validateSchemaWrite } from './a2ui-schema.js';
12
+ // ── Fenced-JSONL transport: the token-stream parser and the prompt that feeds it ──
13
+ export { A2UI_FENCE_TAG, A2uiStreamSplitter, a2uiFencedTransportSection } from './a2ui-stream.js';
14
+ // ── Transcript wiring: deliver routed envelopes into the messages that own them ─
15
+ export { editA2uiPayload, revokeMessage, routeMessageParts, sourceKey } from './a2ui-transcript.js';
6
16
  export { collectGraphIssues, createA2uiProcessor, normalizeA2uiPayload } from './a2ui-validate.js';
7
17
  export { a2uiViewVariants } from './a2ui-view.variants.js';
18
+ // ── Urbicon-native catalog (opt-in; tree-shaken out unless imported) ─────────
19
+ export { urbiconA2uiCatalog } from './urbicon/a2ui-urbicon-catalog.js';
20
+ export { SHARED_AXES, UNSUPPORTED_URBICON_A2UI_COMPONENTS, URBICON_A2UI_CATALOG_ID, URBICON_A2UI_ICON_NAMES, URBICON_A2UI_REGISTRY, urbiconA2uiCatalogSpec } from './urbicon/a2ui-urbicon-registry.js';