@dudousxd/nestjs-agent-core 0.39.0 → 0.41.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 (40) hide show
  1. package/README.md +7 -0
  2. package/dist/ag-ui/index.cjs +123 -9
  3. package/dist/ag-ui/index.cjs.map +1 -1
  4. package/dist/ag-ui/index.d.cts +19 -3
  5. package/dist/ag-ui/index.d.ts +19 -3
  6. package/dist/ag-ui/index.js +122 -9
  7. package/dist/ag-ui/index.js.map +1 -1
  8. package/dist/{catalog-CrzetM3_.d.cts → catalog-BKS3Yxo6.d.cts} +2 -0
  9. package/dist/{catalog-CrzetM3_.d.ts → catalog-BKS3Yxo6.d.ts} +2 -0
  10. package/dist/genui/builtins.cjs +2 -0
  11. package/dist/genui/builtins.cjs.map +1 -1
  12. package/dist/genui/builtins.d.cts +1 -1
  13. package/dist/genui/builtins.d.ts +1 -1
  14. package/dist/genui/builtins.js +2 -0
  15. package/dist/genui/builtins.js.map +1 -1
  16. package/dist/genui/index.cjs +780 -106
  17. package/dist/genui/index.cjs.map +1 -1
  18. package/dist/genui/index.d.cts +5 -5
  19. package/dist/genui/index.d.ts +5 -5
  20. package/dist/genui/index.js +770 -105
  21. package/dist/genui/index.js.map +1 -1
  22. package/dist/guardrails/index.cjs +3 -1
  23. package/dist/guardrails/index.cjs.map +1 -1
  24. package/dist/guardrails/index.d.cts +5 -4
  25. package/dist/guardrails/index.d.ts +5 -4
  26. package/dist/guardrails/index.js +3 -1
  27. package/dist/guardrails/index.js.map +1 -1
  28. package/dist/index.cjs +3324 -380
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +388 -96
  31. package/dist/index.d.ts +388 -96
  32. package/dist/index.js +3279 -377
  33. package/dist/index.js.map +1 -1
  34. package/dist/{processors-6p9nrqEi.d.cts → processors-BXM-MNga.d.cts} +1 -1
  35. package/dist/{processors-Bp5XmF6V.d.ts → processors-ByDr5POu.d.ts} +1 -1
  36. package/dist/{stream-events-CbVEowYb.d.cts → stream-events-BGKLD6RX.d.ts} +664 -86
  37. package/dist/{stream-events-CbVEowYb.d.ts → stream-events-CF-6-Y6L.d.cts} +664 -86
  38. package/package.json +1 -1
  39. package/dist/tool-DZYLEKnl.d.cts +0 -121
  40. package/dist/tool-_iq4xRyk.d.ts +0 -121
@@ -27,7 +27,10 @@ __export(genui_exports, {
27
27
  ajvValidator: () => ajvValidator,
28
28
  builtinJsonSchemaValidator: () => builtinJsonSchemaValidator,
29
29
  catalogToModelText: () => catalogToModelText,
30
+ chart: () => chart,
30
31
  componentToText: () => componentToText,
32
+ createComponent: () => createComponent,
33
+ createComponentRegistry: () => createComponentRegistry,
31
34
  defineCatalog: () => defineCatalog,
32
35
  defineComponent: () => defineComponent,
33
36
  fillTemplate: () => fillTemplate,
@@ -36,17 +39,23 @@ __export(genui_exports, {
36
39
  getPath: () => getPath,
37
40
  isStandardSchema: () => isStandardSchema,
38
41
  jsonStandardSchema: () => jsonStandardSchema,
42
+ negotiateCatalog: () => negotiateCatalog,
43
+ prepareUiEmission: () => prepareUiEmission,
39
44
  showToolJsonSchema: () => showToolJsonSchema,
45
+ snapshotComponentPresentation: () => snapshotComponentPresentation,
40
46
  summarizeSchema: () => summarizeSchema,
47
+ table: () => table,
41
48
  toJsonSchema: () => toJsonSchema,
42
49
  toSnakeCase: () => toSnakeCase,
43
50
  toolNameFor: () => toolNameFor,
44
51
  treeJsonSchema: () => treeJsonSchema,
45
52
  treeToFlatSpec: () => treeToFlatSpec,
46
53
  treeToText: () => treeToText,
54
+ validatePresentationBatch: () => validatePresentationBatch,
47
55
  validateProps: () => validateProps,
48
56
  validatePropsSync: () => validatePropsSync,
49
- validateTree: () => validateTree
57
+ validateTree: () => validateTree,
58
+ validateUiCapabilities: () => validateUiCapabilities
50
59
  });
51
60
  module.exports = __toCommonJS(genui_exports);
52
61
 
@@ -226,21 +235,21 @@ function check(schema, value, path, root, issues, depth) {
226
235
  });
227
236
  return;
228
237
  }
229
- const s = schema;
230
- if (typeof s.$ref === "string") {
231
- const target = resolveRef(root, s.$ref);
238
+ const s2 = schema;
239
+ if (typeof s2.$ref === "string") {
240
+ const target = resolveRef(root, s2.$ref);
232
241
  if (target === void 0) {
233
242
  issues.push({
234
243
  path,
235
- message: `references unknown schema ${s.$ref}`
244
+ message: `references unknown schema ${s2.$ref}`
236
245
  });
237
246
  return;
238
247
  }
239
248
  check(target, value, path, root, issues, depth + 1);
240
249
  }
241
- if (s.type !== void 0) {
242
- const types = Array.isArray(s.type) ? s.type : [
243
- s.type
250
+ if (s2.type !== void 0) {
251
+ const types = Array.isArray(s2.type) ? s2.type : [
252
+ s2.type
244
253
  ];
245
254
  if (!types.some((type) => matchesType(type, value))) {
246
255
  issues.push({
@@ -250,76 +259,76 @@ function check(schema, value, path, root, issues, depth) {
250
259
  return;
251
260
  }
252
261
  }
253
- if (Array.isArray(s.enum) && !s.enum.some((option) => deepEqual(option, value))) {
262
+ if (Array.isArray(s2.enum) && !s2.enum.some((option) => deepEqual(option, value))) {
254
263
  issues.push({
255
264
  path,
256
- message: `must be one of ${s.enum.map((option) => JSON.stringify(option)).join(", ")}`
265
+ message: `must be one of ${s2.enum.map((option) => JSON.stringify(option)).join(", ")}`
257
266
  });
258
267
  }
259
- if ("const" in s && !deepEqual(s.const, value)) {
268
+ if ("const" in s2 && !deepEqual(s2.const, value)) {
260
269
  issues.push({
261
270
  path,
262
- message: `must be ${JSON.stringify(s.const)}`
271
+ message: `must be ${JSON.stringify(s2.const)}`
263
272
  });
264
273
  }
265
274
  if (typeof value === "string") {
266
- if (typeof s.minLength === "number" && value.length < s.minLength) {
275
+ if (typeof s2.minLength === "number" && value.length < s2.minLength) {
267
276
  issues.push({
268
277
  path,
269
- message: `must be at least ${s.minLength} characters`
278
+ message: `must be at least ${s2.minLength} characters`
270
279
  });
271
280
  }
272
- if (typeof s.maxLength === "number" && value.length > s.maxLength) {
281
+ if (typeof s2.maxLength === "number" && value.length > s2.maxLength) {
273
282
  issues.push({
274
283
  path,
275
- message: `must be at most ${s.maxLength} characters`
284
+ message: `must be at most ${s2.maxLength} characters`
276
285
  });
277
286
  }
278
- if (typeof s.pattern === "string") {
287
+ if (typeof s2.pattern === "string") {
279
288
  let pattern;
280
289
  try {
281
- pattern = new RegExp(s.pattern, "u");
290
+ pattern = new RegExp(s2.pattern, "u");
282
291
  } catch {
283
292
  pattern = void 0;
284
293
  }
285
294
  if (pattern !== void 0 && !pattern.test(value)) {
286
295
  issues.push({
287
296
  path,
288
- message: `must match ${s.pattern}`
297
+ message: `must match ${s2.pattern}`
289
298
  });
290
299
  }
291
300
  }
292
301
  }
293
302
  if (typeof value === "number") {
294
- if (typeof s.minimum === "number" && value < s.minimum) {
303
+ if (typeof s2.minimum === "number" && value < s2.minimum) {
295
304
  issues.push({
296
305
  path,
297
- message: `must be >= ${s.minimum}`
306
+ message: `must be >= ${s2.minimum}`
298
307
  });
299
308
  }
300
- if (typeof s.maximum === "number" && value > s.maximum) {
309
+ if (typeof s2.maximum === "number" && value > s2.maximum) {
301
310
  issues.push({
302
311
  path,
303
- message: `must be <= ${s.maximum}`
312
+ message: `must be <= ${s2.maximum}`
304
313
  });
305
314
  }
306
315
  }
307
316
  if (Array.isArray(value)) {
308
- if (typeof s.minItems === "number" && value.length < s.minItems) {
317
+ if (typeof s2.minItems === "number" && value.length < s2.minItems) {
309
318
  issues.push({
310
319
  path,
311
- message: `must have at least ${s.minItems} items`
320
+ message: `must have at least ${s2.minItems} items`
312
321
  });
313
322
  }
314
- if (typeof s.maxItems === "number" && value.length > s.maxItems) {
323
+ if (typeof s2.maxItems === "number" && value.length > s2.maxItems) {
315
324
  issues.push({
316
325
  path,
317
- message: `must have at most ${s.maxItems} items`
326
+ message: `must have at most ${s2.maxItems} items`
318
327
  });
319
328
  }
320
- if (s.items !== void 0 && !Array.isArray(s.items)) {
329
+ if (s2.items !== void 0 && !Array.isArray(s2.items)) {
321
330
  value.forEach((item, index) => {
322
- check(s.items, item, [
331
+ check(s2.items, item, [
323
332
  ...path,
324
333
  index
325
334
  ], root, issues, depth + 1);
@@ -327,11 +336,11 @@ function check(schema, value, path, root, issues, depth) {
327
336
  }
328
337
  }
329
338
  if (matchesType("object", value)) {
330
- const record = value;
331
- const properties = typeof s.properties === "object" && s.properties !== null ? s.properties : {};
332
- if (Array.isArray(s.required)) {
333
- for (const key of s.required) {
334
- if (record[key] === void 0) {
339
+ const record2 = value;
340
+ const properties = typeof s2.properties === "object" && s2.properties !== null ? s2.properties : {};
341
+ if (Array.isArray(s2.required)) {
342
+ for (const key of s2.required) {
343
+ if (record2[key] === void 0) {
335
344
  issues.push({
336
345
  path: [
337
346
  ...path,
@@ -342,7 +351,7 @@ function check(schema, value, path, root, issues, depth) {
342
351
  }
343
352
  }
344
353
  }
345
- for (const [key, entry] of Object.entries(record)) {
354
+ for (const [key, entry] of Object.entries(record2)) {
346
355
  if (key in properties) {
347
356
  if (entry !== void 0) {
348
357
  check(properties[key], entry, [
@@ -350,7 +359,7 @@ function check(schema, value, path, root, issues, depth) {
350
359
  key
351
360
  ], root, issues, depth + 1);
352
361
  }
353
- } else if (s.additionalProperties === false) {
362
+ } else if (s2.additionalProperties === false) {
354
363
  issues.push({
355
364
  path: [
356
365
  ...path,
@@ -358,34 +367,34 @@ function check(schema, value, path, root, issues, depth) {
358
367
  ],
359
368
  message: "is not a known property"
360
369
  });
361
- } else if (typeof s.additionalProperties === "object" && s.additionalProperties !== null) {
362
- check(s.additionalProperties, entry, [
370
+ } else if (typeof s2.additionalProperties === "object" && s2.additionalProperties !== null) {
371
+ check(s2.additionalProperties, entry, [
363
372
  ...path,
364
373
  key
365
374
  ], root, issues, depth + 1);
366
375
  }
367
376
  }
368
377
  }
369
- if (Array.isArray(s.allOf)) {
370
- for (const sub of s.allOf) check(sub, value, path, root, issues, depth + 1);
378
+ if (Array.isArray(s2.allOf)) {
379
+ for (const sub of s2.allOf) check(sub, value, path, root, issues, depth + 1);
371
380
  }
372
- if (Array.isArray(s.anyOf) || Array.isArray(s.oneOf)) {
373
- const options = Array.isArray(s.anyOf) ? s.anyOf : s.oneOf;
381
+ if (Array.isArray(s2.anyOf) || Array.isArray(s2.oneOf)) {
382
+ const options = Array.isArray(s2.anyOf) ? s2.anyOf : s2.oneOf;
374
383
  const passing = options.filter((sub) => {
375
384
  const nested = [];
376
385
  check(sub, value, path, root, nested, depth + 1);
377
386
  return nested.length === 0;
378
387
  }).length;
379
- if (Array.isArray(s.anyOf) ? passing === 0 : passing !== 1) {
388
+ if (Array.isArray(s2.anyOf) ? passing === 0 : passing !== 1) {
380
389
  issues.push({
381
390
  path,
382
- message: Array.isArray(s.anyOf) ? "does not match any allowed shape" : "must match exactly one allowed shape"
391
+ message: Array.isArray(s2.anyOf) ? "does not match any allowed shape" : "must match exactly one allowed shape"
383
392
  });
384
393
  }
385
394
  }
386
- if (s.not !== void 0) {
395
+ if (s2.not !== void 0) {
387
396
  const nested = [];
388
- check(s.not, value, path, root, nested, depth + 1);
397
+ check(s2.not, value, path, root, nested, depth + 1);
389
398
  if (nested.length === 0) {
390
399
  issues.push({
391
400
  path,
@@ -402,6 +411,8 @@ function defineComponent(definition) {
402
411
  if (!COMPONENT_NAME.test(definition.name)) {
403
412
  throw new Error(`genui: component name "${definition.name}" must be letters and digits, starting with a letter (e.g. DataTable)`);
404
413
  }
414
+ if (definition.version !== void 0 && (!Number.isSafeInteger(definition.version) || definition.version <= 0)) throw new RangeError("genui: component version must be a positive safe integer");
415
+ if (definition.fallbackText !== void 0 && typeof definition.fallbackText !== "function") throw new TypeError("genui: fallbackText must be a function");
405
416
  return definition;
406
417
  }
407
418
  __name(defineComponent, "defineComponent");
@@ -440,7 +451,7 @@ function buildCatalog(byName, validator) {
440
451
  ]
441
452
  };
442
453
  }
443
- const result = await validateProps(definition.props, props, validator);
454
+ const result = await validateProps(definition.outputProps ?? definition.props, props, validator);
444
455
  return result;
445
456
  },
446
457
  validateSync(component, props) {
@@ -456,11 +467,11 @@ function buildCatalog(byName, validator) {
456
467
  ]
457
468
  };
458
469
  }
459
- return validatePropsSync(definition.props, props, validator);
470
+ return validatePropsSync(definition.outputProps ?? definition.props, props, validator);
460
471
  },
461
472
  jsonSchemaFor(component) {
462
473
  const definition = byName.get(component);
463
- return definition === void 0 ? void 0 : toJsonSchema(definition.props);
474
+ return definition === void 0 ? void 0 : toJsonSchema(definition.outputProps ?? definition.props);
464
475
  },
465
476
  extend(more) {
466
477
  const next = new Map(byName);
@@ -484,7 +495,7 @@ __name(toolNameFor, "toolNameFor");
484
495
 
485
496
  // src/genui/tree.ts
486
497
  var GENUI_TREE_COMPONENT = "genui:tree";
487
- async function validateTree(catalog, tree, limits = {}) {
498
+ async function validateTree(catalog, tree, limits = {}, phase = "output") {
488
499
  const maxDepth = limits.maxDepth ?? 12;
489
500
  const maxNodes = limits.maxNodes ?? 200;
490
501
  const issues = [];
@@ -536,7 +547,7 @@ async function validateTree(catalog, tree, limits = {}) {
536
547
  });
537
548
  return void 0;
538
549
  }
539
- const validated = await catalog.validate(element.type, element.props ?? {});
550
+ const validated = phase === "input" ? await validateProps(definition.props, element.props ?? {}, catalog.validator) : await catalog.validate(element.type, element.props ?? {});
540
551
  let props = {};
541
552
  if (!validated.ok) {
542
553
  for (const issue of validated.issues) {
@@ -710,7 +721,8 @@ function componentToText(catalog, component, props) {
710
721
  const fallback = catalog.get(component)?.fallbackText;
711
722
  if (fallback !== void 0) {
712
723
  try {
713
- return fallback(props);
724
+ const text = fallback(props);
725
+ if (typeof text === "string" && text.trim().length > 0) return text;
714
726
  } catch {
715
727
  }
716
728
  }
@@ -724,7 +736,8 @@ function treeToText(catalog, root) {
724
736
  const definition = catalog.get(node.type);
725
737
  if (definition?.fallbackText !== void 0) {
726
738
  try {
727
- out.push(definition.fallbackText(node.props));
739
+ const text = definition.fallbackText(node.props);
740
+ out.push(typeof text === "string" && text.trim().length > 0 ? text : jsonBlock(node.props));
728
741
  } catch {
729
742
  out.push(jsonBlock(node.props));
730
743
  }
@@ -741,7 +754,7 @@ function jsonBlock(props) {
741
754
  const title = typeof props.title === "string" && props.title.length > 0 ? `*${props.title}*
742
755
  ` : "";
743
756
  return `${title}\`\`\`
744
- ${JSON.stringify(props, null, 2).slice(0, 2500)}
757
+ ${JSON.stringify(props, null, 2)}
745
758
  \`\`\``;
746
759
  }
747
760
  __name(jsonBlock, "jsonBlock");
@@ -756,7 +769,7 @@ function catalogToModelText(catalog, options = {}) {
756
769
  lines.push("Components you can show (one tool each):");
757
770
  }
758
771
  for (const component of catalog.modelComponents()) {
759
- const schema = catalog.jsonSchemaFor(component.name);
772
+ const schema = toJsonSchema(component.props);
760
773
  const head = mode === "per-component" ? `- ${component.name} (tool \`${toolNameFor(component.name, options.namePrefix)}\`)` : `- ${component.name}`;
761
774
  lines.push(`${head}: ${component.description}`);
762
775
  if (schema !== void 0) {
@@ -771,38 +784,38 @@ function catalogToModelText(catalog, options = {}) {
771
784
  __name(catalogToModelText, "catalogToModelText");
772
785
  function summarizeSchema(schema, root, depth) {
773
786
  if (typeof schema !== "object" || schema === null) return "unknown";
774
- const s = schema;
787
+ const s2 = schema;
775
788
  if (depth > 5) return "\u2026";
776
- if (typeof s.$ref === "string") {
777
- return s.$ref === "#" ? "Self" : s.$ref.split("/").pop() ?? "unknown";
789
+ if (typeof s2.$ref === "string") {
790
+ return s2.$ref === "#" ? "Self" : s2.$ref.split("/").pop() ?? "unknown";
778
791
  }
779
- if (Array.isArray(s.enum)) {
780
- return s.enum.map((option) => JSON.stringify(option)).join(" | ");
792
+ if (Array.isArray(s2.enum)) {
793
+ return s2.enum.map((option) => JSON.stringify(option)).join(" | ");
781
794
  }
782
- if ("const" in s) return JSON.stringify(s.const);
783
- const variants = s.anyOf ?? s.oneOf;
795
+ if ("const" in s2) return JSON.stringify(s2.const);
796
+ const variants = s2.anyOf ?? s2.oneOf;
784
797
  if (Array.isArray(variants)) {
785
798
  return variants.map((variant) => summarizeSchema(variant, root, depth + 1)).join(" | ");
786
799
  }
787
- const types = Array.isArray(s.type) ? s.type : s.type !== void 0 ? [
788
- s.type
800
+ const types = Array.isArray(s2.type) ? s2.type : s2.type !== void 0 ? [
801
+ s2.type
789
802
  ] : [];
790
803
  if (types.length > 1) {
791
804
  return types.map((type2) => summarizeSchema({
792
- ...s,
805
+ ...s2,
793
806
  type: type2
794
807
  }, root, depth + 1)).join(" | ");
795
808
  }
796
809
  const type = types[0];
797
810
  if (type === "array") {
798
- const item = s.items === void 0 ? "unknown" : summarizeSchema(s.items, root, depth + 1);
811
+ const item = s2.items === void 0 ? "unknown" : summarizeSchema(s2.items, root, depth + 1);
799
812
  return /[|\s]/.test(item) && !item.startsWith("{") ? `(${item})[]` : `${item}[]`;
800
813
  }
801
- if (type === "object" || type === void 0 && s.properties !== void 0) {
802
- const properties = s.properties ?? {};
814
+ if (type === "object" || type === void 0 && s2.properties !== void 0) {
815
+ const properties = s2.properties ?? {};
803
816
  const keys = Object.keys(properties);
804
817
  if (keys.length === 0) return "object";
805
- const required = new Set(Array.isArray(s.required) ? s.required : []);
818
+ const required = new Set(Array.isArray(s2.required) ? s2.required : []);
806
819
  const fields = keys.map((key) => {
807
820
  const field = properties[key];
808
821
  const note = typeof field?.description === "string" && depth === 0 ? ` /* ${field.description} */` : "";
@@ -832,6 +845,81 @@ function getPath(source, path) {
832
845
  }
833
846
  __name(getPath, "getPath");
834
847
 
848
+ // src/genui/capabilities.ts
849
+ function validateUiCapabilities(value) {
850
+ if (!record(value) || Object.keys(value).some((key) => key !== "components") || !Array.isArray(value.components)) throw new TypeError("Invalid UI capabilities");
851
+ const seen = /* @__PURE__ */ new Set();
852
+ const components = value.components.map((component) => {
853
+ if (!record(component) || Object.keys(component).some((key) => key !== "name" && key !== "version") || typeof component.name !== "string" || !COMPONENT_NAME.test(component.name) || !Number.isSafeInteger(component.version) || Number(component.version) <= 0 || seen.has(component.name)) throw new TypeError("Invalid UI component capability");
854
+ seen.add(component.name);
855
+ return {
856
+ name: component.name,
857
+ version: Number(component.version)
858
+ };
859
+ });
860
+ return {
861
+ components
862
+ };
863
+ }
864
+ __name(validateUiCapabilities, "validateUiCapabilities");
865
+ function record(value) {
866
+ return typeof value === "object" && value !== null && !Array.isArray(value);
867
+ }
868
+ __name(record, "record");
869
+ function negotiateCatalog(catalog, capabilities) {
870
+ if (capabilities === void 0) return catalog;
871
+ const supported = validateUiCapabilities(capabilities).components;
872
+ return defineCatalog(catalog.components.filter((component) => supported.some((entry) => entry.name === component.name && entry.version === (component.version ?? 1))), {
873
+ jsonSchemaValidator: catalog.validator
874
+ });
875
+ }
876
+ __name(negotiateCatalog, "negotiateCatalog");
877
+ async function prepareUiEmission(catalog, capabilities, component, props, version) {
878
+ const drawable = negotiateCatalog(catalog, capabilities);
879
+ if (component === GENUI_TREE_COMPONENT) {
880
+ if (version !== void 0 && version !== 1) throw new Error("Invalid UI tree version");
881
+ const validated2 = await validateTree(catalog, props.root);
882
+ if (!validated2.ok) throw new Error(`Invalid UI tree: ${formatIssues(validated2.issues)}`);
883
+ const supported = /* @__PURE__ */ __name((node) => drawable.has(node.type) && (node.children ?? []).every(supported), "supported");
884
+ const versions = /* @__PURE__ */ new Map();
885
+ const collectVersions = /* @__PURE__ */ __name((node) => {
886
+ versions.set(node.type, catalog.get(node.type)?.version ?? 1);
887
+ for (const child of node.children ?? []) collectVersions(child);
888
+ }, "collectVersions");
889
+ collectVersions(validated2.value);
890
+ return supported(validated2.value) ? {
891
+ kind: "ui",
892
+ component,
893
+ props: {
894
+ root: validated2.value
895
+ },
896
+ version: 1,
897
+ fallbackText: treeToText(catalog, validated2.value),
898
+ componentVersions: Object.fromEntries(versions)
899
+ } : {
900
+ kind: "text",
901
+ text: treeToText(catalog, validated2.value)
902
+ };
903
+ }
904
+ const definition = catalog.get(component);
905
+ if (definition === void 0) throw new Error(`Unknown UI component "${component}"`);
906
+ const currentVersion = definition.version ?? 1;
907
+ if (version !== void 0 && version !== currentVersion) throw new Error(`Invalid UI component version for "${component}"`);
908
+ const validated = await catalog.validate(component, props);
909
+ if (!validated.ok) throw new Error(`Invalid UI props: ${formatIssues(validated.issues)}`);
910
+ return drawable.has(component) ? {
911
+ kind: "ui",
912
+ component,
913
+ props: validated.value,
914
+ version: currentVersion,
915
+ fallbackText: componentToText(catalog, component, validated.value)
916
+ } : {
917
+ kind: "text",
918
+ text: componentToText(catalog, component, validated.value)
919
+ };
920
+ }
921
+ __name(prepareUiEmission, "prepareUiEmission");
922
+
835
923
  // src/genui/tools.ts
836
924
  var GENUI_SHOW_TOOL = "ui__show";
837
925
  function genuiTools(catalog, options = {}) {
@@ -895,30 +983,29 @@ function componentTool(catalog, component, options) {
895
983
  if (definition === void 0) {
896
984
  throw new Error(`component "${component.name}" is not available here`);
897
985
  }
898
- const validated = await validateProps(definition.props, input, resolved.validator);
986
+ const validated = await validateProps(dynamic ? definition.props : definition.outputProps ?? definition.props, input, resolved.validator);
899
987
  if (!validated.ok) {
900
988
  throw new Error(`invalid ${component.name} props: ${formatIssues(validated.issues)}`);
901
989
  }
902
990
  return push(ctx, component.name, validated.value, definition.version);
903
991
  }
904
992
  };
905
- if (dynamic) {
906
- handler.describe = async (scope) => {
907
- const resolved = await catalogFor(catalog, options, scopeOf(scope));
908
- const definition = modelComponent(resolved, component.name);
909
- if (definition === void 0) {
910
- return {
911
- description: `Not available in this conversation \u2014 do not call this tool. (${component.name})`
912
- };
913
- }
993
+ handler.describe = async (scope) => {
994
+ const resolved = negotiateCatalog(await catalogFor(catalog, options, scopeOf(scope)), scope.uiCapabilities);
995
+ const definition = modelComponent(resolved, component.name);
996
+ if (definition === void 0) {
914
997
  return {
915
- description: describeFor(definition),
916
- inputSchema: permissiveSchema(toJsonSchema(definition.props) ?? {
917
- type: "object"
918
- })
998
+ available: false,
999
+ description: `Not available in this conversation \u2014 do not call this tool. (${component.name})`
919
1000
  };
1001
+ }
1002
+ return {
1003
+ description: describeFor(definition),
1004
+ inputSchema: permissiveSchema(toJsonSchema(definition.props) ?? {
1005
+ type: "object"
1006
+ })
920
1007
  };
921
- }
1008
+ };
922
1009
  return {
923
1010
  spec: {
924
1011
  name: toolNameFor(component.name, options.namePrefix),
@@ -952,7 +1039,7 @@ function treeTool(catalog, options) {
952
1039
  const handler = {
953
1040
  async execute(input, ctx) {
954
1041
  const resolved = await catalogFor(catalog, options, scopeOf(ctx));
955
- const result = await validateTree(resolved, input, options.treeLimits);
1042
+ const result = await validateTree(resolved, input, options.treeLimits, dynamic ? "input" : "output");
956
1043
  if (!result.ok) {
957
1044
  throw new Error(`invalid UI tree: ${formatIssues(result.issues)}`);
958
1045
  }
@@ -962,22 +1049,21 @@ function treeTool(catalog, options) {
962
1049
  });
963
1050
  }
964
1051
  };
965
- if (dynamic) {
966
- handler.describe = async (scope) => {
967
- const resolved = await catalogFor(catalog, options, scopeOf(scope));
968
- return {
969
- description: describeFor(resolved),
970
- inputSchema: permissiveSchema(treeJsonSchema(resolved))
971
- };
1052
+ handler.describe = async (scope) => {
1053
+ const resolved = negotiateCatalog(await catalogFor(catalog, options, scopeOf(scope)), scope.uiCapabilities);
1054
+ return {
1055
+ available: resolved.modelComponents().length > 0,
1056
+ description: describeFor(resolved),
1057
+ inputSchema: permissiveSchema(treeJsonSchema(resolved))
972
1058
  };
973
- }
1059
+ };
974
1060
  return {
975
1061
  spec: {
976
1062
  name,
977
1063
  kind: "read",
978
1064
  description: describeFor(catalog),
979
1065
  inputSchema: dynamic ? permissiveSchema(treeJsonSchema(catalog)) : asyncJsonStandardSchema(treeJsonSchema(catalog), async (value) => {
980
- const result = await validateTree(catalog, value, options.treeLimits);
1066
+ const result = await validateTree(catalog, value, options.treeLimits, "input");
981
1067
  return result.ok ? {
982
1068
  value: result.value
983
1069
  } : {
@@ -1012,7 +1098,7 @@ function showToolJsonSchema(catalog) {
1012
1098
  };
1013
1099
  }
1014
1100
  __name(showToolJsonSchema, "showToolJsonSchema");
1015
- async function validateShow(catalog, input) {
1101
+ async function validateShow(catalog, input, phase = "input") {
1016
1102
  if (typeof input !== "object" || input === null || Array.isArray(input)) {
1017
1103
  return {
1018
1104
  ok: false,
@@ -1053,7 +1139,7 @@ async function validateShow(catalog, input) {
1053
1139
  ]
1054
1140
  };
1055
1141
  }
1056
- const validated = await validateProps(definition.props, props ?? {}, catalog.validator);
1142
+ const validated = await validateProps(phase === "input" ? definition.props : definition.outputProps ?? definition.props, props ?? {}, catalog.validator);
1057
1143
  if (!validated.ok) {
1058
1144
  return {
1059
1145
  ok: false,
@@ -1093,22 +1179,21 @@ function showTool(catalog, options) {
1093
1179
  const handler = {
1094
1180
  async execute(input, ctx) {
1095
1181
  const resolved = await catalogFor(catalog, options, scopeOf(ctx));
1096
- const result = await validateShow(resolved, input);
1182
+ const result = await validateShow(resolved, input, dynamic ? "input" : "output");
1097
1183
  if (!result.ok) {
1098
1184
  throw new Error(`invalid ${name} call: ${formatIssues(result.issues)}`);
1099
1185
  }
1100
1186
  return push(ctx, result.definition.name, result.props, result.definition.version);
1101
1187
  }
1102
1188
  };
1103
- if (dynamic) {
1104
- handler.describe = async (scope) => {
1105
- const resolved = await catalogFor(catalog, options, scopeOf(scope));
1106
- return {
1107
- description: describeFor(resolved),
1108
- inputSchema: permissiveSchema(showToolJsonSchema(resolved))
1109
- };
1189
+ handler.describe = async (scope) => {
1190
+ const resolved = negotiateCatalog(await catalogFor(catalog, options, scopeOf(scope)), scope.uiCapabilities);
1191
+ return {
1192
+ available: resolved.modelComponents().length > 0,
1193
+ description: describeFor(resolved),
1194
+ inputSchema: permissiveSchema(showToolJsonSchema(resolved))
1110
1195
  };
1111
- }
1196
+ };
1112
1197
  return {
1113
1198
  spec: {
1114
1199
  name,
@@ -1193,6 +1278,586 @@ function asyncJsonStandardSchema(schema, validate) {
1193
1278
  };
1194
1279
  }
1195
1280
  __name(asyncJsonStandardSchema, "asyncJsonStandardSchema");
1281
+
1282
+ // src/genui/builtins.ts
1283
+ var str = /* @__PURE__ */ __name((description, extra = {}) => ({
1284
+ type: "string",
1285
+ ...description !== void 0 ? {
1286
+ description
1287
+ } : {},
1288
+ ...extra
1289
+ }), "str");
1290
+ var obj = /* @__PURE__ */ __name((properties, required = []) => ({
1291
+ type: "object",
1292
+ properties,
1293
+ required,
1294
+ additionalProperties: false
1295
+ }), "obj");
1296
+ var arr = /* @__PURE__ */ __name((items, extra = {}) => ({
1297
+ type: "array",
1298
+ items,
1299
+ ...extra
1300
+ }), "arr");
1301
+ var cell = {
1302
+ type: [
1303
+ "string",
1304
+ "number",
1305
+ "boolean",
1306
+ "null"
1307
+ ]
1308
+ };
1309
+ var row = {
1310
+ type: "object",
1311
+ additionalProperties: cell
1312
+ };
1313
+ var s = /* @__PURE__ */ __name((value) => value === null || value === void 0 ? "" : String(value), "s");
1314
+ var heading = /* @__PURE__ */ __name((props) => props.title ? `*${s(props.title)}*
1315
+ ` : "", "heading");
1316
+ function textTable(columns, rows, max = 15) {
1317
+ const shown = rows.slice(0, max);
1318
+ const widths = columns.map((column) => Math.min(28, Math.max(column.label.length, ...shown.map((r) => s(r[column.key]).length))));
1319
+ const format = /* @__PURE__ */ __name((values) => values.map((value, index) => value.slice(0, widths[index]).padEnd(widths[index] ?? 0)).join(" "), "format");
1320
+ const lines = [
1321
+ format(columns.map((column) => column.label)),
1322
+ widths.map((width) => "-".repeat(width)).join(" "),
1323
+ ...shown.map((r) => format(columns.map((column) => s(r[column.key]))))
1324
+ ];
1325
+ if (rows.length > max) lines.push(`\u2026 ${rows.length - max} more rows`);
1326
+ return `\`\`\`
1327
+ ${lines.join("\n")}
1328
+ \`\`\``;
1329
+ }
1330
+ __name(textTable, "textTable");
1331
+ function lineDiff(before, after) {
1332
+ const a = before.split("\n");
1333
+ const b = after.split("\n");
1334
+ if (a.length * b.length > 25e4) {
1335
+ return [
1336
+ ...a.map((text) => ({
1337
+ op: "del",
1338
+ text
1339
+ })),
1340
+ ...b.map((text) => ({
1341
+ op: "add",
1342
+ text
1343
+ }))
1344
+ ];
1345
+ }
1346
+ const dp = Array.from({
1347
+ length: a.length + 1
1348
+ }, () => new Array(b.length + 1).fill(0));
1349
+ const at = /* @__PURE__ */ __name((i2, j2) => dp[i2]?.[j2] ?? 0, "at");
1350
+ for (let i2 = a.length - 1; i2 >= 0; i2--) {
1351
+ for (let j2 = b.length - 1; j2 >= 0; j2--) {
1352
+ dp[i2][j2] = a[i2] === b[j2] ? at(i2 + 1, j2 + 1) + 1 : Math.max(at(i2 + 1, j2), at(i2, j2 + 1));
1353
+ }
1354
+ }
1355
+ const out = [];
1356
+ let i = 0;
1357
+ let j = 0;
1358
+ while (i < a.length && j < b.length) {
1359
+ if (a[i] === b[j]) {
1360
+ out.push({
1361
+ op: "same",
1362
+ text: a[i]
1363
+ });
1364
+ i++;
1365
+ j++;
1366
+ } else if (at(i + 1, j) >= at(i, j + 1)) {
1367
+ out.push({
1368
+ op: "del",
1369
+ text: a[i++]
1370
+ });
1371
+ } else {
1372
+ out.push({
1373
+ op: "add",
1374
+ text: b[j++]
1375
+ });
1376
+ }
1377
+ }
1378
+ while (i < a.length) out.push({
1379
+ op: "del",
1380
+ text: a[i++]
1381
+ });
1382
+ while (j < b.length) out.push({
1383
+ op: "add",
1384
+ text: b[j++]
1385
+ });
1386
+ return out;
1387
+ }
1388
+ __name(lineDiff, "lineDiff");
1389
+ function bar(value, max, width = 20) {
1390
+ const n = max > 0 ? Math.round(Math.abs(value) / max * width) : 0;
1391
+ return "\u2588".repeat(n) || "\u258F";
1392
+ }
1393
+ __name(bar, "bar");
1394
+ var DataTable = defineComponent({
1395
+ name: "DataTable",
1396
+ title: "Data table",
1397
+ description: "Tabular data (records with the same fields) as a sortable table. Prefer it over markdown tables for more than 3 rows.",
1398
+ props: obj({
1399
+ title: str("Short caption"),
1400
+ columns: arr(obj({
1401
+ key: str("Field name in each row"),
1402
+ label: str("Column header"),
1403
+ align: str(void 0, {
1404
+ enum: [
1405
+ "left",
1406
+ "right",
1407
+ "center"
1408
+ ]
1409
+ }),
1410
+ format: str("How to format values", {
1411
+ enum: [
1412
+ "text",
1413
+ "number",
1414
+ "currency",
1415
+ "percent",
1416
+ "date",
1417
+ "link"
1418
+ ]
1419
+ })
1420
+ }, [
1421
+ "key",
1422
+ "label"
1423
+ ]), {
1424
+ minItems: 1,
1425
+ maxItems: 12
1426
+ }),
1427
+ rows: arr(row, {
1428
+ maxItems: 500
1429
+ })
1430
+ }, [
1431
+ "columns",
1432
+ "rows"
1433
+ ]),
1434
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${textTable(props.columns ?? [], props.rows ?? [])}`, "fallbackText")
1435
+ });
1436
+ var Chart = defineComponent({
1437
+ name: "Chart",
1438
+ title: "Chart",
1439
+ description: "`bar` to compare categories, `line` for a trend over time. `data` is a list of points; `xKey` names the category/time field and each series names a numeric field.",
1440
+ props: obj({
1441
+ type: str("Chart type", {
1442
+ enum: [
1443
+ "bar",
1444
+ "line"
1445
+ ]
1446
+ }),
1447
+ title: str(),
1448
+ xKey: str("Field of each point used for the x axis"),
1449
+ series: arr(obj({
1450
+ key: str("Numeric field"),
1451
+ label: str()
1452
+ }, [
1453
+ "key"
1454
+ ]), {
1455
+ minItems: 1,
1456
+ maxItems: 6
1457
+ }),
1458
+ data: arr(row, {
1459
+ minItems: 1,
1460
+ maxItems: 200
1461
+ }),
1462
+ unit: str("Unit shown after values, e.g. `KB` or `%`")
1463
+ }, [
1464
+ "type",
1465
+ "xKey",
1466
+ "series",
1467
+ "data"
1468
+ ]),
1469
+ fallbackText: /* @__PURE__ */ __name((props) => {
1470
+ const series = props.series ?? [];
1471
+ const data = props.data ?? [];
1472
+ const first = series[0]?.key ?? "";
1473
+ const max = Math.max(0, ...data.map((point) => Math.abs(Number(point[first]) || 0)));
1474
+ const unit = props.unit ? ` ${s(props.unit)}` : "";
1475
+ const lines = data.slice(0, 20).map((point) => `${s(point[props.xKey]).slice(0, 18).padEnd(18)} ${bar(Number(point[first]) || 0, max)} ${s(point[first])}${unit}`);
1476
+ return `${heading(props)}\`\`\`
1477
+ ${lines.join("\n")}
1478
+ \`\`\``;
1479
+ }, "fallbackText")
1480
+ });
1481
+ var KpiCards = defineComponent({
1482
+ name: "KpiCards",
1483
+ title: "KPI cards",
1484
+ description: "A few key numbers (metrics, totals, counts) as cards.",
1485
+ props: obj({
1486
+ title: str(),
1487
+ items: arr(obj({
1488
+ label: str(),
1489
+ value: {
1490
+ type: [
1491
+ "string",
1492
+ "number"
1493
+ ]
1494
+ },
1495
+ delta: str("Change, e.g. `+12%`"),
1496
+ trend: str(void 0, {
1497
+ enum: [
1498
+ "up",
1499
+ "down",
1500
+ "flat"
1501
+ ]
1502
+ }),
1503
+ hint: str()
1504
+ }, [
1505
+ "label",
1506
+ "value"
1507
+ ]), {
1508
+ minItems: 1,
1509
+ maxItems: 8
1510
+ })
1511
+ }, [
1512
+ "items"
1513
+ ]),
1514
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.items ?? []).map((item) => `\u2022 *${s(item.label)}:* ${s(item.value)}${item.delta ? ` (${s(item.delta)})` : ""}`).join("\n")}`, "fallbackText")
1515
+ });
1516
+ var SourceCards = defineComponent({
1517
+ name: "SourceCards",
1518
+ title: "Sources",
1519
+ description: "The documents, pages or links an answer is based on, as cards.",
1520
+ props: obj({
1521
+ title: str(),
1522
+ items: arr(obj({
1523
+ title: str(),
1524
+ url: str(),
1525
+ snippet: str(),
1526
+ source: str("e.g. Wiki, GitHub")
1527
+ }, [
1528
+ "title"
1529
+ ]), {
1530
+ minItems: 1,
1531
+ maxItems: 12
1532
+ })
1533
+ }, [
1534
+ "items"
1535
+ ]),
1536
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.items ?? []).map((item) => `\u2022 ${item.url ? `<${s(item.url)}|${s(item.title)}>` : s(item.title)}${item.source ? ` \u2014 ${s(item.source)}` : ""}`).join("\n")}`, "fallbackText")
1537
+ });
1538
+ var Checklist = defineComponent({
1539
+ name: "Checklist",
1540
+ title: "Checklist",
1541
+ description: "Steps or a to-do list with done/not-done state.",
1542
+ props: obj({
1543
+ title: str(),
1544
+ items: arr(obj({
1545
+ label: str(),
1546
+ done: {
1547
+ type: "boolean"
1548
+ },
1549
+ note: str()
1550
+ }, [
1551
+ "label"
1552
+ ]), {
1553
+ minItems: 1,
1554
+ maxItems: 50
1555
+ })
1556
+ }, [
1557
+ "items"
1558
+ ]),
1559
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.items ?? []).map((item) => `${item.done ? "\u2611" : "\u2610"} ${s(item.label)}${item.note ? ` \u2014 _${s(item.note)}_` : ""}`).join("\n")}`, "fallbackText")
1560
+ });
1561
+ var Timeline = defineComponent({
1562
+ name: "Timeline",
1563
+ title: "Timeline",
1564
+ description: "Events in chronological order (the history of a project, an incident, a deal).",
1565
+ props: obj({
1566
+ title: str(),
1567
+ events: arr(obj({
1568
+ date: str(),
1569
+ title: str(),
1570
+ description: str()
1571
+ }, [
1572
+ "title"
1573
+ ]), {
1574
+ minItems: 1,
1575
+ maxItems: 50
1576
+ })
1577
+ }, [
1578
+ "events"
1579
+ ]),
1580
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${(props.events ?? []).map((event) => `\u2022 ${event.date ? `*${s(event.date)}* ` : ""}${s(event.title)}${event.description ? ` \u2014 ${s(event.description)}` : ""}`).join("\n")}`, "fallbackText")
1581
+ });
1582
+ var CodeBlock = defineComponent({
1583
+ name: "CodeBlock",
1584
+ title: "Code",
1585
+ description: "A code snippet or file excerpt with syntax highlighting.",
1586
+ props: obj({
1587
+ title: str(),
1588
+ language: str(),
1589
+ code: str()
1590
+ }, [
1591
+ "code"
1592
+ ]),
1593
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}\`\`\`
1594
+ ${s(props.code)}
1595
+ \`\`\``, "fallbackText")
1596
+ });
1597
+ var Diff = defineComponent({
1598
+ name: "Diff",
1599
+ title: "Diff",
1600
+ description: "A before/after change to a text or code file, as a line diff.",
1601
+ props: obj({
1602
+ title: str(),
1603
+ language: str(),
1604
+ before: str(),
1605
+ after: str()
1606
+ }, [
1607
+ "before",
1608
+ "after"
1609
+ ]),
1610
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}\`\`\`
1611
+ ${lineDiff(s(props.before), s(props.after)).map((line) => `${line.op === "add" ? "+" : line.op === "del" ? "-" : " "} ${line.text}`).join("\n")}
1612
+ \`\`\``, "fallbackText")
1613
+ });
1614
+ var Callout = defineComponent({
1615
+ name: "Callout",
1616
+ title: "Callout",
1617
+ description: "A short note, warning or success message, emphasized.",
1618
+ props: obj({
1619
+ tone: str(void 0, {
1620
+ enum: [
1621
+ "info",
1622
+ "success",
1623
+ "warning",
1624
+ "danger"
1625
+ ]
1626
+ }),
1627
+ title: str(),
1628
+ text: str()
1629
+ }, [
1630
+ "text"
1631
+ ]),
1632
+ fallbackText: /* @__PURE__ */ __name((props) => `${props.tone === "warning" || props.tone === "danger" ? ":warning: " : ":information_source: "}${heading(props)}${s(props.text)}`, "fallbackText")
1633
+ });
1634
+ var Stack = defineComponent({
1635
+ name: "Stack",
1636
+ title: "Stack",
1637
+ description: "Lays its children out vertically (or horizontally with direction=row).",
1638
+ props: obj({
1639
+ direction: str(void 0, {
1640
+ enum: [
1641
+ "column",
1642
+ "row"
1643
+ ]
1644
+ }),
1645
+ gap: {
1646
+ type: "number"
1647
+ }
1648
+ }),
1649
+ children: true,
1650
+ fallbackText: /* @__PURE__ */ __name(() => "", "fallbackText")
1651
+ });
1652
+ var Card = defineComponent({
1653
+ name: "Card",
1654
+ title: "Card",
1655
+ description: "A box with an optional title around its children.",
1656
+ props: obj({
1657
+ title: str(),
1658
+ subtitle: str()
1659
+ }),
1660
+ children: true,
1661
+ fallbackText: /* @__PURE__ */ __name((props) => props.title ? `*${s(props.title)}*${props.subtitle ? ` \u2014 ${s(props.subtitle)}` : ""}` : "", "fallbackText")
1662
+ });
1663
+ var Heading = defineComponent({
1664
+ name: "Heading",
1665
+ title: "Heading",
1666
+ description: "A heading.",
1667
+ props: obj({
1668
+ text: str()
1669
+ }, [
1670
+ "text"
1671
+ ]),
1672
+ fallbackText: /* @__PURE__ */ __name((props) => `*${s(props.text)}*`, "fallbackText")
1673
+ });
1674
+ var Text = defineComponent({
1675
+ name: "Text",
1676
+ title: "Text",
1677
+ description: "A paragraph of plain text (no markdown).",
1678
+ props: obj({
1679
+ text: str(),
1680
+ muted: {
1681
+ type: "boolean"
1682
+ }
1683
+ }, [
1684
+ "text"
1685
+ ]),
1686
+ fallbackText: /* @__PURE__ */ __name((props) => s(props.text), "fallbackText")
1687
+ });
1688
+ var Badge = defineComponent({
1689
+ name: "Badge",
1690
+ title: "Badge",
1691
+ description: "A small label.",
1692
+ props: obj({
1693
+ text: str(),
1694
+ tone: str(void 0, {
1695
+ enum: [
1696
+ "neutral",
1697
+ "brand",
1698
+ "success",
1699
+ "warning",
1700
+ "danger"
1701
+ ]
1702
+ })
1703
+ }, [
1704
+ "text"
1705
+ ]),
1706
+ fallbackText: /* @__PURE__ */ __name((props) => `[${s(props.text)}]`, "fallbackText")
1707
+ });
1708
+ var Link = defineComponent({
1709
+ name: "Link",
1710
+ title: "Link",
1711
+ description: "A link to an http(s) URL.",
1712
+ props: obj({
1713
+ text: str(),
1714
+ url: str(void 0, {
1715
+ pattern: "^https?://"
1716
+ })
1717
+ }, [
1718
+ "text",
1719
+ "url"
1720
+ ]),
1721
+ fallbackText: /* @__PURE__ */ __name((props) => `<${s(props.url)}|${s(props.text || props.url)}>`, "fallbackText")
1722
+ });
1723
+ var Image = defineComponent({
1724
+ name: "Image",
1725
+ title: "Image",
1726
+ description: "An image from an https URL.",
1727
+ props: obj({
1728
+ url: str(void 0, {
1729
+ pattern: "^https://"
1730
+ }),
1731
+ alt: str()
1732
+ }, [
1733
+ "url"
1734
+ ]),
1735
+ fallbackText: /* @__PURE__ */ __name((props) => s(props.url), "fallbackText")
1736
+ });
1737
+
1738
+ // src/genui/registry.ts
1739
+ function snapshotComponentPresentation(value) {
1740
+ const seen = /* @__PURE__ */ new Set();
1741
+ const check2 = /* @__PURE__ */ __name((entry) => {
1742
+ if (entry === null || typeof entry === "string" || typeof entry === "boolean") return;
1743
+ if (typeof entry === "number" && Number.isFinite(entry)) return;
1744
+ if (typeof entry !== "object" || entry === null) throw new TypeError("genui: presentation must contain only JSON values");
1745
+ if (seen.has(entry)) throw new TypeError("genui: presentation must contain acyclic JSON values");
1746
+ if (!Array.isArray(entry) && Object.getPrototypeOf(entry) !== Object.prototype && Object.getPrototypeOf(entry) !== null) throw new TypeError("genui: presentation must contain plain JSON objects");
1747
+ seen.add(entry);
1748
+ for (const item of Object.values(entry)) check2(item);
1749
+ seen.delete(entry);
1750
+ }, "check");
1751
+ check2(value);
1752
+ return JSON.parse(JSON.stringify(value));
1753
+ }
1754
+ __name(snapshotComponentPresentation, "snapshotComponentPresentation");
1755
+ function createComponent(definition, options = {}) {
1756
+ defineComponent(definition);
1757
+ const catalog = defineCatalog([
1758
+ definition
1759
+ ], options);
1760
+ const factory = /* @__PURE__ */ __name(async (props) => {
1761
+ const result = await validateProps(definition.props, props, catalog.validator);
1762
+ if (!result.ok) throw new Error(`genui: invalid ${definition.name} props: ${formatIssues(result.issues)}`);
1763
+ const normalized = snapshotComponentPresentation(result.value);
1764
+ if (typeof normalized !== "object" || normalized === null || Array.isArray(normalized)) throw new TypeError("genui: component props must be a JSON object");
1765
+ const portable = await catalog.validate(definition.name, normalized);
1766
+ if (!portable.ok || canonicalJson(snapshotComponentPresentation(portable.value)) !== canonicalJson(normalized)) {
1767
+ throw new Error(`genui: ${definition.name} requires outputProps accepting stable normalized JSON props`);
1768
+ }
1769
+ return {
1770
+ component: definition.name,
1771
+ props: normalized,
1772
+ version: definition.version ?? 1,
1773
+ fallbackText: componentToText(catalog, definition.name, normalized)
1774
+ };
1775
+ }, "factory");
1776
+ return Object.assign(factory, {
1777
+ definition
1778
+ });
1779
+ }
1780
+ __name(createComponent, "createComponent");
1781
+ function createComponentRegistry(options = {}) {
1782
+ let catalog = defineCatalog([], options);
1783
+ const channels = /* @__PURE__ */ new Map();
1784
+ const registry = {
1785
+ get catalog() {
1786
+ return catalog;
1787
+ },
1788
+ get manifest() {
1789
+ return catalog.components.map((definition) => {
1790
+ const props = catalog.jsonSchemaFor(definition.name);
1791
+ return {
1792
+ name: definition.name,
1793
+ title: definition.title,
1794
+ description: definition.description,
1795
+ version: definition.version ?? 1,
1796
+ ...props === void 0 ? {} : {
1797
+ props: snapshotComponentPresentation(props)
1798
+ }
1799
+ };
1800
+ });
1801
+ },
1802
+ register(definition, renderers) {
1803
+ if (catalog.has(definition.name)) throw new Error(`genui: component "${definition.name}" is already registered`);
1804
+ catalog = catalog.extend([
1805
+ definition
1806
+ ]);
1807
+ channels.set(definition.name, {
1808
+ ...renderers
1809
+ });
1810
+ return this;
1811
+ },
1812
+ async prepare(presentation) {
1813
+ const definition = catalog.get(presentation.component);
1814
+ if (definition === void 0) throw new Error(`genui: unknown component "${presentation.component}"`);
1815
+ if (presentation.version !== (definition.version ?? 1)) throw new Error(`genui: component "${presentation.component}" version mismatch`);
1816
+ const validated = await catalog.validate(presentation.component, presentation.props);
1817
+ if (!validated.ok) throw new Error(`genui: invalid ${presentation.component} props: ${formatIssues(validated.issues)}`);
1818
+ const props = snapshotComponentPresentation(validated.value);
1819
+ if (typeof props !== "object" || props === null || Array.isArray(props)) throw new TypeError("genui: component props must be a JSON object");
1820
+ return {
1821
+ component: definition.name,
1822
+ props,
1823
+ version: definition.version ?? 1,
1824
+ fallbackText: componentToText(catalog, definition.name, props)
1825
+ };
1826
+ },
1827
+ async render(presentation, channel, context) {
1828
+ const prepared = await this.prepare(presentation);
1829
+ const mapping = channels.get(prepared.component);
1830
+ const renderer = mapping === void 0 ? void 0 : Object.hasOwn(mapping, channel) ? mapping[channel] : Object.hasOwn(mapping, "text") ? mapping.text : void 0;
1831
+ return renderer === void 0 ? prepared.fallbackText : renderer(prepared.props, context);
1832
+ }
1833
+ };
1834
+ return registry;
1835
+ }
1836
+ __name(createComponentRegistry, "createComponentRegistry");
1837
+ var table = createComponent(DataTable);
1838
+ var chart = createComponent(Chart);
1839
+ function validatePresentationBatch(presentations) {
1840
+ const batch = Array.isArray(presentations) ? presentations : [
1841
+ presentations
1842
+ ];
1843
+ return batch.map((item) => {
1844
+ const presentation = snapshotComponentPresentation(item);
1845
+ if (typeof presentation !== "object" || presentation === null || typeof presentation.component !== "string" || !/^[A-Za-z][A-Za-z0-9]{0,63}$/.test(presentation.component) || !Number.isSafeInteger(presentation.version) || presentation.version <= 0 || typeof presentation.fallbackText !== "string" || typeof presentation.props !== "object" || presentation.props === null || Array.isArray(presentation.props)) {
1846
+ throw new TypeError("genui: invalid component presentation");
1847
+ }
1848
+ return presentation;
1849
+ });
1850
+ }
1851
+ __name(validatePresentationBatch, "validatePresentationBatch");
1852
+ function canonicalJson(value) {
1853
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
1854
+ if (typeof value === "object" && value !== null) {
1855
+ const record2 = value;
1856
+ return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record2[key])}`).join(",")}}`;
1857
+ }
1858
+ return JSON.stringify(value);
1859
+ }
1860
+ __name(canonicalJson, "canonicalJson");
1196
1861
  // Annotate the CommonJS export names for ESM import in node:
1197
1862
  0 && (module.exports = {
1198
1863
  COMPONENT_NAME,
@@ -1201,7 +1866,10 @@ __name(asyncJsonStandardSchema, "asyncJsonStandardSchema");
1201
1866
  ajvValidator,
1202
1867
  builtinJsonSchemaValidator,
1203
1868
  catalogToModelText,
1869
+ chart,
1204
1870
  componentToText,
1871
+ createComponent,
1872
+ createComponentRegistry,
1205
1873
  defineCatalog,
1206
1874
  defineComponent,
1207
1875
  fillTemplate,
@@ -1210,16 +1878,22 @@ __name(asyncJsonStandardSchema, "asyncJsonStandardSchema");
1210
1878
  getPath,
1211
1879
  isStandardSchema,
1212
1880
  jsonStandardSchema,
1881
+ negotiateCatalog,
1882
+ prepareUiEmission,
1213
1883
  showToolJsonSchema,
1884
+ snapshotComponentPresentation,
1214
1885
  summarizeSchema,
1886
+ table,
1215
1887
  toJsonSchema,
1216
1888
  toSnakeCase,
1217
1889
  toolNameFor,
1218
1890
  treeJsonSchema,
1219
1891
  treeToFlatSpec,
1220
1892
  treeToText,
1893
+ validatePresentationBatch,
1221
1894
  validateProps,
1222
1895
  validatePropsSync,
1223
- validateTree
1896
+ validateTree,
1897
+ validateUiCapabilities
1224
1898
  });
1225
1899
  //# sourceMappingURL=index.cjs.map