@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
@@ -177,21 +177,21 @@ function check(schema, value, path, root, issues, depth) {
177
177
  });
178
178
  return;
179
179
  }
180
- const s = schema;
181
- if (typeof s.$ref === "string") {
182
- const target = resolveRef(root, s.$ref);
180
+ const s2 = schema;
181
+ if (typeof s2.$ref === "string") {
182
+ const target = resolveRef(root, s2.$ref);
183
183
  if (target === void 0) {
184
184
  issues.push({
185
185
  path,
186
- message: `references unknown schema ${s.$ref}`
186
+ message: `references unknown schema ${s2.$ref}`
187
187
  });
188
188
  return;
189
189
  }
190
190
  check(target, value, path, root, issues, depth + 1);
191
191
  }
192
- if (s.type !== void 0) {
193
- const types = Array.isArray(s.type) ? s.type : [
194
- s.type
192
+ if (s2.type !== void 0) {
193
+ const types = Array.isArray(s2.type) ? s2.type : [
194
+ s2.type
195
195
  ];
196
196
  if (!types.some((type) => matchesType(type, value))) {
197
197
  issues.push({
@@ -201,76 +201,76 @@ function check(schema, value, path, root, issues, depth) {
201
201
  return;
202
202
  }
203
203
  }
204
- if (Array.isArray(s.enum) && !s.enum.some((option) => deepEqual(option, value))) {
204
+ if (Array.isArray(s2.enum) && !s2.enum.some((option) => deepEqual(option, value))) {
205
205
  issues.push({
206
206
  path,
207
- message: `must be one of ${s.enum.map((option) => JSON.stringify(option)).join(", ")}`
207
+ message: `must be one of ${s2.enum.map((option) => JSON.stringify(option)).join(", ")}`
208
208
  });
209
209
  }
210
- if ("const" in s && !deepEqual(s.const, value)) {
210
+ if ("const" in s2 && !deepEqual(s2.const, value)) {
211
211
  issues.push({
212
212
  path,
213
- message: `must be ${JSON.stringify(s.const)}`
213
+ message: `must be ${JSON.stringify(s2.const)}`
214
214
  });
215
215
  }
216
216
  if (typeof value === "string") {
217
- if (typeof s.minLength === "number" && value.length < s.minLength) {
217
+ if (typeof s2.minLength === "number" && value.length < s2.minLength) {
218
218
  issues.push({
219
219
  path,
220
- message: `must be at least ${s.minLength} characters`
220
+ message: `must be at least ${s2.minLength} characters`
221
221
  });
222
222
  }
223
- if (typeof s.maxLength === "number" && value.length > s.maxLength) {
223
+ if (typeof s2.maxLength === "number" && value.length > s2.maxLength) {
224
224
  issues.push({
225
225
  path,
226
- message: `must be at most ${s.maxLength} characters`
226
+ message: `must be at most ${s2.maxLength} characters`
227
227
  });
228
228
  }
229
- if (typeof s.pattern === "string") {
229
+ if (typeof s2.pattern === "string") {
230
230
  let pattern;
231
231
  try {
232
- pattern = new RegExp(s.pattern, "u");
232
+ pattern = new RegExp(s2.pattern, "u");
233
233
  } catch {
234
234
  pattern = void 0;
235
235
  }
236
236
  if (pattern !== void 0 && !pattern.test(value)) {
237
237
  issues.push({
238
238
  path,
239
- message: `must match ${s.pattern}`
239
+ message: `must match ${s2.pattern}`
240
240
  });
241
241
  }
242
242
  }
243
243
  }
244
244
  if (typeof value === "number") {
245
- if (typeof s.minimum === "number" && value < s.minimum) {
245
+ if (typeof s2.minimum === "number" && value < s2.minimum) {
246
246
  issues.push({
247
247
  path,
248
- message: `must be >= ${s.minimum}`
248
+ message: `must be >= ${s2.minimum}`
249
249
  });
250
250
  }
251
- if (typeof s.maximum === "number" && value > s.maximum) {
251
+ if (typeof s2.maximum === "number" && value > s2.maximum) {
252
252
  issues.push({
253
253
  path,
254
- message: `must be <= ${s.maximum}`
254
+ message: `must be <= ${s2.maximum}`
255
255
  });
256
256
  }
257
257
  }
258
258
  if (Array.isArray(value)) {
259
- if (typeof s.minItems === "number" && value.length < s.minItems) {
259
+ if (typeof s2.minItems === "number" && value.length < s2.minItems) {
260
260
  issues.push({
261
261
  path,
262
- message: `must have at least ${s.minItems} items`
262
+ message: `must have at least ${s2.minItems} items`
263
263
  });
264
264
  }
265
- if (typeof s.maxItems === "number" && value.length > s.maxItems) {
265
+ if (typeof s2.maxItems === "number" && value.length > s2.maxItems) {
266
266
  issues.push({
267
267
  path,
268
- message: `must have at most ${s.maxItems} items`
268
+ message: `must have at most ${s2.maxItems} items`
269
269
  });
270
270
  }
271
- if (s.items !== void 0 && !Array.isArray(s.items)) {
271
+ if (s2.items !== void 0 && !Array.isArray(s2.items)) {
272
272
  value.forEach((item, index) => {
273
- check(s.items, item, [
273
+ check(s2.items, item, [
274
274
  ...path,
275
275
  index
276
276
  ], root, issues, depth + 1);
@@ -278,11 +278,11 @@ function check(schema, value, path, root, issues, depth) {
278
278
  }
279
279
  }
280
280
  if (matchesType("object", value)) {
281
- const record = value;
282
- const properties = typeof s.properties === "object" && s.properties !== null ? s.properties : {};
283
- if (Array.isArray(s.required)) {
284
- for (const key of s.required) {
285
- if (record[key] === void 0) {
281
+ const record2 = value;
282
+ const properties = typeof s2.properties === "object" && s2.properties !== null ? s2.properties : {};
283
+ if (Array.isArray(s2.required)) {
284
+ for (const key of s2.required) {
285
+ if (record2[key] === void 0) {
286
286
  issues.push({
287
287
  path: [
288
288
  ...path,
@@ -293,7 +293,7 @@ function check(schema, value, path, root, issues, depth) {
293
293
  }
294
294
  }
295
295
  }
296
- for (const [key, entry] of Object.entries(record)) {
296
+ for (const [key, entry] of Object.entries(record2)) {
297
297
  if (key in properties) {
298
298
  if (entry !== void 0) {
299
299
  check(properties[key], entry, [
@@ -301,7 +301,7 @@ function check(schema, value, path, root, issues, depth) {
301
301
  key
302
302
  ], root, issues, depth + 1);
303
303
  }
304
- } else if (s.additionalProperties === false) {
304
+ } else if (s2.additionalProperties === false) {
305
305
  issues.push({
306
306
  path: [
307
307
  ...path,
@@ -309,34 +309,34 @@ function check(schema, value, path, root, issues, depth) {
309
309
  ],
310
310
  message: "is not a known property"
311
311
  });
312
- } else if (typeof s.additionalProperties === "object" && s.additionalProperties !== null) {
313
- check(s.additionalProperties, entry, [
312
+ } else if (typeof s2.additionalProperties === "object" && s2.additionalProperties !== null) {
313
+ check(s2.additionalProperties, entry, [
314
314
  ...path,
315
315
  key
316
316
  ], root, issues, depth + 1);
317
317
  }
318
318
  }
319
319
  }
320
- if (Array.isArray(s.allOf)) {
321
- for (const sub of s.allOf) check(sub, value, path, root, issues, depth + 1);
320
+ if (Array.isArray(s2.allOf)) {
321
+ for (const sub of s2.allOf) check(sub, value, path, root, issues, depth + 1);
322
322
  }
323
- if (Array.isArray(s.anyOf) || Array.isArray(s.oneOf)) {
324
- const options = Array.isArray(s.anyOf) ? s.anyOf : s.oneOf;
323
+ if (Array.isArray(s2.anyOf) || Array.isArray(s2.oneOf)) {
324
+ const options = Array.isArray(s2.anyOf) ? s2.anyOf : s2.oneOf;
325
325
  const passing = options.filter((sub) => {
326
326
  const nested = [];
327
327
  check(sub, value, path, root, nested, depth + 1);
328
328
  return nested.length === 0;
329
329
  }).length;
330
- if (Array.isArray(s.anyOf) ? passing === 0 : passing !== 1) {
330
+ if (Array.isArray(s2.anyOf) ? passing === 0 : passing !== 1) {
331
331
  issues.push({
332
332
  path,
333
- message: Array.isArray(s.anyOf) ? "does not match any allowed shape" : "must match exactly one allowed shape"
333
+ message: Array.isArray(s2.anyOf) ? "does not match any allowed shape" : "must match exactly one allowed shape"
334
334
  });
335
335
  }
336
336
  }
337
- if (s.not !== void 0) {
337
+ if (s2.not !== void 0) {
338
338
  const nested = [];
339
- check(s.not, value, path, root, nested, depth + 1);
339
+ check(s2.not, value, path, root, nested, depth + 1);
340
340
  if (nested.length === 0) {
341
341
  issues.push({
342
342
  path,
@@ -353,6 +353,8 @@ function defineComponent(definition) {
353
353
  if (!COMPONENT_NAME.test(definition.name)) {
354
354
  throw new Error(`genui: component name "${definition.name}" must be letters and digits, starting with a letter (e.g. DataTable)`);
355
355
  }
356
+ if (definition.version !== void 0 && (!Number.isSafeInteger(definition.version) || definition.version <= 0)) throw new RangeError("genui: component version must be a positive safe integer");
357
+ if (definition.fallbackText !== void 0 && typeof definition.fallbackText !== "function") throw new TypeError("genui: fallbackText must be a function");
356
358
  return definition;
357
359
  }
358
360
  __name(defineComponent, "defineComponent");
@@ -391,7 +393,7 @@ function buildCatalog(byName, validator) {
391
393
  ]
392
394
  };
393
395
  }
394
- const result = await validateProps(definition.props, props, validator);
396
+ const result = await validateProps(definition.outputProps ?? definition.props, props, validator);
395
397
  return result;
396
398
  },
397
399
  validateSync(component, props) {
@@ -407,11 +409,11 @@ function buildCatalog(byName, validator) {
407
409
  ]
408
410
  };
409
411
  }
410
- return validatePropsSync(definition.props, props, validator);
412
+ return validatePropsSync(definition.outputProps ?? definition.props, props, validator);
411
413
  },
412
414
  jsonSchemaFor(component) {
413
415
  const definition = byName.get(component);
414
- return definition === void 0 ? void 0 : toJsonSchema(definition.props);
416
+ return definition === void 0 ? void 0 : toJsonSchema(definition.outputProps ?? definition.props);
415
417
  },
416
418
  extend(more) {
417
419
  const next = new Map(byName);
@@ -435,7 +437,7 @@ __name(toolNameFor, "toolNameFor");
435
437
 
436
438
  // src/genui/tree.ts
437
439
  var GENUI_TREE_COMPONENT = "genui:tree";
438
- async function validateTree(catalog, tree, limits = {}) {
440
+ async function validateTree(catalog, tree, limits = {}, phase = "output") {
439
441
  const maxDepth = limits.maxDepth ?? 12;
440
442
  const maxNodes = limits.maxNodes ?? 200;
441
443
  const issues = [];
@@ -487,7 +489,7 @@ async function validateTree(catalog, tree, limits = {}) {
487
489
  });
488
490
  return void 0;
489
491
  }
490
- const validated = await catalog.validate(element.type, element.props ?? {});
492
+ const validated = phase === "input" ? await validateProps(definition.props, element.props ?? {}, catalog.validator) : await catalog.validate(element.type, element.props ?? {});
491
493
  let props = {};
492
494
  if (!validated.ok) {
493
495
  for (const issue of validated.issues) {
@@ -661,7 +663,8 @@ function componentToText(catalog, component, props) {
661
663
  const fallback = catalog.get(component)?.fallbackText;
662
664
  if (fallback !== void 0) {
663
665
  try {
664
- return fallback(props);
666
+ const text = fallback(props);
667
+ if (typeof text === "string" && text.trim().length > 0) return text;
665
668
  } catch {
666
669
  }
667
670
  }
@@ -675,7 +678,8 @@ function treeToText(catalog, root) {
675
678
  const definition = catalog.get(node.type);
676
679
  if (definition?.fallbackText !== void 0) {
677
680
  try {
678
- out.push(definition.fallbackText(node.props));
681
+ const text = definition.fallbackText(node.props);
682
+ out.push(typeof text === "string" && text.trim().length > 0 ? text : jsonBlock(node.props));
679
683
  } catch {
680
684
  out.push(jsonBlock(node.props));
681
685
  }
@@ -692,7 +696,7 @@ function jsonBlock(props) {
692
696
  const title = typeof props.title === "string" && props.title.length > 0 ? `*${props.title}*
693
697
  ` : "";
694
698
  return `${title}\`\`\`
695
- ${JSON.stringify(props, null, 2).slice(0, 2500)}
699
+ ${JSON.stringify(props, null, 2)}
696
700
  \`\`\``;
697
701
  }
698
702
  __name(jsonBlock, "jsonBlock");
@@ -707,7 +711,7 @@ function catalogToModelText(catalog, options = {}) {
707
711
  lines.push("Components you can show (one tool each):");
708
712
  }
709
713
  for (const component of catalog.modelComponents()) {
710
- const schema = catalog.jsonSchemaFor(component.name);
714
+ const schema = toJsonSchema(component.props);
711
715
  const head = mode === "per-component" ? `- ${component.name} (tool \`${toolNameFor(component.name, options.namePrefix)}\`)` : `- ${component.name}`;
712
716
  lines.push(`${head}: ${component.description}`);
713
717
  if (schema !== void 0) {
@@ -722,38 +726,38 @@ function catalogToModelText(catalog, options = {}) {
722
726
  __name(catalogToModelText, "catalogToModelText");
723
727
  function summarizeSchema(schema, root, depth) {
724
728
  if (typeof schema !== "object" || schema === null) return "unknown";
725
- const s = schema;
729
+ const s2 = schema;
726
730
  if (depth > 5) return "\u2026";
727
- if (typeof s.$ref === "string") {
728
- return s.$ref === "#" ? "Self" : s.$ref.split("/").pop() ?? "unknown";
731
+ if (typeof s2.$ref === "string") {
732
+ return s2.$ref === "#" ? "Self" : s2.$ref.split("/").pop() ?? "unknown";
729
733
  }
730
- if (Array.isArray(s.enum)) {
731
- return s.enum.map((option) => JSON.stringify(option)).join(" | ");
734
+ if (Array.isArray(s2.enum)) {
735
+ return s2.enum.map((option) => JSON.stringify(option)).join(" | ");
732
736
  }
733
- if ("const" in s) return JSON.stringify(s.const);
734
- const variants = s.anyOf ?? s.oneOf;
737
+ if ("const" in s2) return JSON.stringify(s2.const);
738
+ const variants = s2.anyOf ?? s2.oneOf;
735
739
  if (Array.isArray(variants)) {
736
740
  return variants.map((variant) => summarizeSchema(variant, root, depth + 1)).join(" | ");
737
741
  }
738
- const types = Array.isArray(s.type) ? s.type : s.type !== void 0 ? [
739
- s.type
742
+ const types = Array.isArray(s2.type) ? s2.type : s2.type !== void 0 ? [
743
+ s2.type
740
744
  ] : [];
741
745
  if (types.length > 1) {
742
746
  return types.map((type2) => summarizeSchema({
743
- ...s,
747
+ ...s2,
744
748
  type: type2
745
749
  }, root, depth + 1)).join(" | ");
746
750
  }
747
751
  const type = types[0];
748
752
  if (type === "array") {
749
- const item = s.items === void 0 ? "unknown" : summarizeSchema(s.items, root, depth + 1);
753
+ const item = s2.items === void 0 ? "unknown" : summarizeSchema(s2.items, root, depth + 1);
750
754
  return /[|\s]/.test(item) && !item.startsWith("{") ? `(${item})[]` : `${item}[]`;
751
755
  }
752
- if (type === "object" || type === void 0 && s.properties !== void 0) {
753
- const properties = s.properties ?? {};
756
+ if (type === "object" || type === void 0 && s2.properties !== void 0) {
757
+ const properties = s2.properties ?? {};
754
758
  const keys = Object.keys(properties);
755
759
  if (keys.length === 0) return "object";
756
- const required = new Set(Array.isArray(s.required) ? s.required : []);
760
+ const required = new Set(Array.isArray(s2.required) ? s2.required : []);
757
761
  const fields = keys.map((key) => {
758
762
  const field = properties[key];
759
763
  const note = typeof field?.description === "string" && depth === 0 ? ` /* ${field.description} */` : "";
@@ -783,6 +787,81 @@ function getPath(source, path) {
783
787
  }
784
788
  __name(getPath, "getPath");
785
789
 
790
+ // src/genui/capabilities.ts
791
+ function validateUiCapabilities(value) {
792
+ if (!record(value) || Object.keys(value).some((key) => key !== "components") || !Array.isArray(value.components)) throw new TypeError("Invalid UI capabilities");
793
+ const seen = /* @__PURE__ */ new Set();
794
+ const components = value.components.map((component) => {
795
+ 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");
796
+ seen.add(component.name);
797
+ return {
798
+ name: component.name,
799
+ version: Number(component.version)
800
+ };
801
+ });
802
+ return {
803
+ components
804
+ };
805
+ }
806
+ __name(validateUiCapabilities, "validateUiCapabilities");
807
+ function record(value) {
808
+ return typeof value === "object" && value !== null && !Array.isArray(value);
809
+ }
810
+ __name(record, "record");
811
+ function negotiateCatalog(catalog, capabilities) {
812
+ if (capabilities === void 0) return catalog;
813
+ const supported = validateUiCapabilities(capabilities).components;
814
+ return defineCatalog(catalog.components.filter((component) => supported.some((entry) => entry.name === component.name && entry.version === (component.version ?? 1))), {
815
+ jsonSchemaValidator: catalog.validator
816
+ });
817
+ }
818
+ __name(negotiateCatalog, "negotiateCatalog");
819
+ async function prepareUiEmission(catalog, capabilities, component, props, version) {
820
+ const drawable = negotiateCatalog(catalog, capabilities);
821
+ if (component === GENUI_TREE_COMPONENT) {
822
+ if (version !== void 0 && version !== 1) throw new Error("Invalid UI tree version");
823
+ const validated2 = await validateTree(catalog, props.root);
824
+ if (!validated2.ok) throw new Error(`Invalid UI tree: ${formatIssues(validated2.issues)}`);
825
+ const supported = /* @__PURE__ */ __name((node) => drawable.has(node.type) && (node.children ?? []).every(supported), "supported");
826
+ const versions = /* @__PURE__ */ new Map();
827
+ const collectVersions = /* @__PURE__ */ __name((node) => {
828
+ versions.set(node.type, catalog.get(node.type)?.version ?? 1);
829
+ for (const child of node.children ?? []) collectVersions(child);
830
+ }, "collectVersions");
831
+ collectVersions(validated2.value);
832
+ return supported(validated2.value) ? {
833
+ kind: "ui",
834
+ component,
835
+ props: {
836
+ root: validated2.value
837
+ },
838
+ version: 1,
839
+ fallbackText: treeToText(catalog, validated2.value),
840
+ componentVersions: Object.fromEntries(versions)
841
+ } : {
842
+ kind: "text",
843
+ text: treeToText(catalog, validated2.value)
844
+ };
845
+ }
846
+ const definition = catalog.get(component);
847
+ if (definition === void 0) throw new Error(`Unknown UI component "${component}"`);
848
+ const currentVersion = definition.version ?? 1;
849
+ if (version !== void 0 && version !== currentVersion) throw new Error(`Invalid UI component version for "${component}"`);
850
+ const validated = await catalog.validate(component, props);
851
+ if (!validated.ok) throw new Error(`Invalid UI props: ${formatIssues(validated.issues)}`);
852
+ return drawable.has(component) ? {
853
+ kind: "ui",
854
+ component,
855
+ props: validated.value,
856
+ version: currentVersion,
857
+ fallbackText: componentToText(catalog, component, validated.value)
858
+ } : {
859
+ kind: "text",
860
+ text: componentToText(catalog, component, validated.value)
861
+ };
862
+ }
863
+ __name(prepareUiEmission, "prepareUiEmission");
864
+
786
865
  // src/genui/tools.ts
787
866
  var GENUI_SHOW_TOOL = "ui__show";
788
867
  function genuiTools(catalog, options = {}) {
@@ -846,30 +925,29 @@ function componentTool(catalog, component, options) {
846
925
  if (definition === void 0) {
847
926
  throw new Error(`component "${component.name}" is not available here`);
848
927
  }
849
- const validated = await validateProps(definition.props, input, resolved.validator);
928
+ const validated = await validateProps(dynamic ? definition.props : definition.outputProps ?? definition.props, input, resolved.validator);
850
929
  if (!validated.ok) {
851
930
  throw new Error(`invalid ${component.name} props: ${formatIssues(validated.issues)}`);
852
931
  }
853
932
  return push(ctx, component.name, validated.value, definition.version);
854
933
  }
855
934
  };
856
- if (dynamic) {
857
- handler.describe = async (scope) => {
858
- const resolved = await catalogFor(catalog, options, scopeOf(scope));
859
- const definition = modelComponent(resolved, component.name);
860
- if (definition === void 0) {
861
- return {
862
- description: `Not available in this conversation \u2014 do not call this tool. (${component.name})`
863
- };
864
- }
935
+ handler.describe = async (scope) => {
936
+ const resolved = negotiateCatalog(await catalogFor(catalog, options, scopeOf(scope)), scope.uiCapabilities);
937
+ const definition = modelComponent(resolved, component.name);
938
+ if (definition === void 0) {
865
939
  return {
866
- description: describeFor(definition),
867
- inputSchema: permissiveSchema(toJsonSchema(definition.props) ?? {
868
- type: "object"
869
- })
940
+ available: false,
941
+ description: `Not available in this conversation \u2014 do not call this tool. (${component.name})`
870
942
  };
943
+ }
944
+ return {
945
+ description: describeFor(definition),
946
+ inputSchema: permissiveSchema(toJsonSchema(definition.props) ?? {
947
+ type: "object"
948
+ })
871
949
  };
872
- }
950
+ };
873
951
  return {
874
952
  spec: {
875
953
  name: toolNameFor(component.name, options.namePrefix),
@@ -903,7 +981,7 @@ function treeTool(catalog, options) {
903
981
  const handler = {
904
982
  async execute(input, ctx) {
905
983
  const resolved = await catalogFor(catalog, options, scopeOf(ctx));
906
- const result = await validateTree(resolved, input, options.treeLimits);
984
+ const result = await validateTree(resolved, input, options.treeLimits, dynamic ? "input" : "output");
907
985
  if (!result.ok) {
908
986
  throw new Error(`invalid UI tree: ${formatIssues(result.issues)}`);
909
987
  }
@@ -913,22 +991,21 @@ function treeTool(catalog, options) {
913
991
  });
914
992
  }
915
993
  };
916
- if (dynamic) {
917
- handler.describe = async (scope) => {
918
- const resolved = await catalogFor(catalog, options, scopeOf(scope));
919
- return {
920
- description: describeFor(resolved),
921
- inputSchema: permissiveSchema(treeJsonSchema(resolved))
922
- };
994
+ handler.describe = async (scope) => {
995
+ const resolved = negotiateCatalog(await catalogFor(catalog, options, scopeOf(scope)), scope.uiCapabilities);
996
+ return {
997
+ available: resolved.modelComponents().length > 0,
998
+ description: describeFor(resolved),
999
+ inputSchema: permissiveSchema(treeJsonSchema(resolved))
923
1000
  };
924
- }
1001
+ };
925
1002
  return {
926
1003
  spec: {
927
1004
  name,
928
1005
  kind: "read",
929
1006
  description: describeFor(catalog),
930
1007
  inputSchema: dynamic ? permissiveSchema(treeJsonSchema(catalog)) : asyncJsonStandardSchema(treeJsonSchema(catalog), async (value) => {
931
- const result = await validateTree(catalog, value, options.treeLimits);
1008
+ const result = await validateTree(catalog, value, options.treeLimits, "input");
932
1009
  return result.ok ? {
933
1010
  value: result.value
934
1011
  } : {
@@ -963,7 +1040,7 @@ function showToolJsonSchema(catalog) {
963
1040
  };
964
1041
  }
965
1042
  __name(showToolJsonSchema, "showToolJsonSchema");
966
- async function validateShow(catalog, input) {
1043
+ async function validateShow(catalog, input, phase = "input") {
967
1044
  if (typeof input !== "object" || input === null || Array.isArray(input)) {
968
1045
  return {
969
1046
  ok: false,
@@ -1004,7 +1081,7 @@ async function validateShow(catalog, input) {
1004
1081
  ]
1005
1082
  };
1006
1083
  }
1007
- const validated = await validateProps(definition.props, props ?? {}, catalog.validator);
1084
+ const validated = await validateProps(phase === "input" ? definition.props : definition.outputProps ?? definition.props, props ?? {}, catalog.validator);
1008
1085
  if (!validated.ok) {
1009
1086
  return {
1010
1087
  ok: false,
@@ -1044,22 +1121,21 @@ function showTool(catalog, options) {
1044
1121
  const handler = {
1045
1122
  async execute(input, ctx) {
1046
1123
  const resolved = await catalogFor(catalog, options, scopeOf(ctx));
1047
- const result = await validateShow(resolved, input);
1124
+ const result = await validateShow(resolved, input, dynamic ? "input" : "output");
1048
1125
  if (!result.ok) {
1049
1126
  throw new Error(`invalid ${name} call: ${formatIssues(result.issues)}`);
1050
1127
  }
1051
1128
  return push(ctx, result.definition.name, result.props, result.definition.version);
1052
1129
  }
1053
1130
  };
1054
- if (dynamic) {
1055
- handler.describe = async (scope) => {
1056
- const resolved = await catalogFor(catalog, options, scopeOf(scope));
1057
- return {
1058
- description: describeFor(resolved),
1059
- inputSchema: permissiveSchema(showToolJsonSchema(resolved))
1060
- };
1131
+ handler.describe = async (scope) => {
1132
+ const resolved = negotiateCatalog(await catalogFor(catalog, options, scopeOf(scope)), scope.uiCapabilities);
1133
+ return {
1134
+ available: resolved.modelComponents().length > 0,
1135
+ description: describeFor(resolved),
1136
+ inputSchema: permissiveSchema(showToolJsonSchema(resolved))
1061
1137
  };
1062
- }
1138
+ };
1063
1139
  return {
1064
1140
  spec: {
1065
1141
  name,
@@ -1144,6 +1220,586 @@ function asyncJsonStandardSchema(schema, validate) {
1144
1220
  };
1145
1221
  }
1146
1222
  __name(asyncJsonStandardSchema, "asyncJsonStandardSchema");
1223
+
1224
+ // src/genui/builtins.ts
1225
+ var str = /* @__PURE__ */ __name((description, extra = {}) => ({
1226
+ type: "string",
1227
+ ...description !== void 0 ? {
1228
+ description
1229
+ } : {},
1230
+ ...extra
1231
+ }), "str");
1232
+ var obj = /* @__PURE__ */ __name((properties, required = []) => ({
1233
+ type: "object",
1234
+ properties,
1235
+ required,
1236
+ additionalProperties: false
1237
+ }), "obj");
1238
+ var arr = /* @__PURE__ */ __name((items, extra = {}) => ({
1239
+ type: "array",
1240
+ items,
1241
+ ...extra
1242
+ }), "arr");
1243
+ var cell = {
1244
+ type: [
1245
+ "string",
1246
+ "number",
1247
+ "boolean",
1248
+ "null"
1249
+ ]
1250
+ };
1251
+ var row = {
1252
+ type: "object",
1253
+ additionalProperties: cell
1254
+ };
1255
+ var s = /* @__PURE__ */ __name((value) => value === null || value === void 0 ? "" : String(value), "s");
1256
+ var heading = /* @__PURE__ */ __name((props) => props.title ? `*${s(props.title)}*
1257
+ ` : "", "heading");
1258
+ function textTable(columns, rows, max = 15) {
1259
+ const shown = rows.slice(0, max);
1260
+ const widths = columns.map((column) => Math.min(28, Math.max(column.label.length, ...shown.map((r) => s(r[column.key]).length))));
1261
+ const format = /* @__PURE__ */ __name((values) => values.map((value, index) => value.slice(0, widths[index]).padEnd(widths[index] ?? 0)).join(" "), "format");
1262
+ const lines = [
1263
+ format(columns.map((column) => column.label)),
1264
+ widths.map((width) => "-".repeat(width)).join(" "),
1265
+ ...shown.map((r) => format(columns.map((column) => s(r[column.key]))))
1266
+ ];
1267
+ if (rows.length > max) lines.push(`\u2026 ${rows.length - max} more rows`);
1268
+ return `\`\`\`
1269
+ ${lines.join("\n")}
1270
+ \`\`\``;
1271
+ }
1272
+ __name(textTable, "textTable");
1273
+ function lineDiff(before, after) {
1274
+ const a = before.split("\n");
1275
+ const b = after.split("\n");
1276
+ if (a.length * b.length > 25e4) {
1277
+ return [
1278
+ ...a.map((text) => ({
1279
+ op: "del",
1280
+ text
1281
+ })),
1282
+ ...b.map((text) => ({
1283
+ op: "add",
1284
+ text
1285
+ }))
1286
+ ];
1287
+ }
1288
+ const dp = Array.from({
1289
+ length: a.length + 1
1290
+ }, () => new Array(b.length + 1).fill(0));
1291
+ const at = /* @__PURE__ */ __name((i2, j2) => dp[i2]?.[j2] ?? 0, "at");
1292
+ for (let i2 = a.length - 1; i2 >= 0; i2--) {
1293
+ for (let j2 = b.length - 1; j2 >= 0; j2--) {
1294
+ dp[i2][j2] = a[i2] === b[j2] ? at(i2 + 1, j2 + 1) + 1 : Math.max(at(i2 + 1, j2), at(i2, j2 + 1));
1295
+ }
1296
+ }
1297
+ const out = [];
1298
+ let i = 0;
1299
+ let j = 0;
1300
+ while (i < a.length && j < b.length) {
1301
+ if (a[i] === b[j]) {
1302
+ out.push({
1303
+ op: "same",
1304
+ text: a[i]
1305
+ });
1306
+ i++;
1307
+ j++;
1308
+ } else if (at(i + 1, j) >= at(i, j + 1)) {
1309
+ out.push({
1310
+ op: "del",
1311
+ text: a[i++]
1312
+ });
1313
+ } else {
1314
+ out.push({
1315
+ op: "add",
1316
+ text: b[j++]
1317
+ });
1318
+ }
1319
+ }
1320
+ while (i < a.length) out.push({
1321
+ op: "del",
1322
+ text: a[i++]
1323
+ });
1324
+ while (j < b.length) out.push({
1325
+ op: "add",
1326
+ text: b[j++]
1327
+ });
1328
+ return out;
1329
+ }
1330
+ __name(lineDiff, "lineDiff");
1331
+ function bar(value, max, width = 20) {
1332
+ const n = max > 0 ? Math.round(Math.abs(value) / max * width) : 0;
1333
+ return "\u2588".repeat(n) || "\u258F";
1334
+ }
1335
+ __name(bar, "bar");
1336
+ var DataTable = defineComponent({
1337
+ name: "DataTable",
1338
+ title: "Data table",
1339
+ description: "Tabular data (records with the same fields) as a sortable table. Prefer it over markdown tables for more than 3 rows.",
1340
+ props: obj({
1341
+ title: str("Short caption"),
1342
+ columns: arr(obj({
1343
+ key: str("Field name in each row"),
1344
+ label: str("Column header"),
1345
+ align: str(void 0, {
1346
+ enum: [
1347
+ "left",
1348
+ "right",
1349
+ "center"
1350
+ ]
1351
+ }),
1352
+ format: str("How to format values", {
1353
+ enum: [
1354
+ "text",
1355
+ "number",
1356
+ "currency",
1357
+ "percent",
1358
+ "date",
1359
+ "link"
1360
+ ]
1361
+ })
1362
+ }, [
1363
+ "key",
1364
+ "label"
1365
+ ]), {
1366
+ minItems: 1,
1367
+ maxItems: 12
1368
+ }),
1369
+ rows: arr(row, {
1370
+ maxItems: 500
1371
+ })
1372
+ }, [
1373
+ "columns",
1374
+ "rows"
1375
+ ]),
1376
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}${textTable(props.columns ?? [], props.rows ?? [])}`, "fallbackText")
1377
+ });
1378
+ var Chart = defineComponent({
1379
+ name: "Chart",
1380
+ title: "Chart",
1381
+ 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.",
1382
+ props: obj({
1383
+ type: str("Chart type", {
1384
+ enum: [
1385
+ "bar",
1386
+ "line"
1387
+ ]
1388
+ }),
1389
+ title: str(),
1390
+ xKey: str("Field of each point used for the x axis"),
1391
+ series: arr(obj({
1392
+ key: str("Numeric field"),
1393
+ label: str()
1394
+ }, [
1395
+ "key"
1396
+ ]), {
1397
+ minItems: 1,
1398
+ maxItems: 6
1399
+ }),
1400
+ data: arr(row, {
1401
+ minItems: 1,
1402
+ maxItems: 200
1403
+ }),
1404
+ unit: str("Unit shown after values, e.g. `KB` or `%`")
1405
+ }, [
1406
+ "type",
1407
+ "xKey",
1408
+ "series",
1409
+ "data"
1410
+ ]),
1411
+ fallbackText: /* @__PURE__ */ __name((props) => {
1412
+ const series = props.series ?? [];
1413
+ const data = props.data ?? [];
1414
+ const first = series[0]?.key ?? "";
1415
+ const max = Math.max(0, ...data.map((point) => Math.abs(Number(point[first]) || 0)));
1416
+ const unit = props.unit ? ` ${s(props.unit)}` : "";
1417
+ 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}`);
1418
+ return `${heading(props)}\`\`\`
1419
+ ${lines.join("\n")}
1420
+ \`\`\``;
1421
+ }, "fallbackText")
1422
+ });
1423
+ var KpiCards = defineComponent({
1424
+ name: "KpiCards",
1425
+ title: "KPI cards",
1426
+ description: "A few key numbers (metrics, totals, counts) as cards.",
1427
+ props: obj({
1428
+ title: str(),
1429
+ items: arr(obj({
1430
+ label: str(),
1431
+ value: {
1432
+ type: [
1433
+ "string",
1434
+ "number"
1435
+ ]
1436
+ },
1437
+ delta: str("Change, e.g. `+12%`"),
1438
+ trend: str(void 0, {
1439
+ enum: [
1440
+ "up",
1441
+ "down",
1442
+ "flat"
1443
+ ]
1444
+ }),
1445
+ hint: str()
1446
+ }, [
1447
+ "label",
1448
+ "value"
1449
+ ]), {
1450
+ minItems: 1,
1451
+ maxItems: 8
1452
+ })
1453
+ }, [
1454
+ "items"
1455
+ ]),
1456
+ 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")
1457
+ });
1458
+ var SourceCards = defineComponent({
1459
+ name: "SourceCards",
1460
+ title: "Sources",
1461
+ description: "The documents, pages or links an answer is based on, as cards.",
1462
+ props: obj({
1463
+ title: str(),
1464
+ items: arr(obj({
1465
+ title: str(),
1466
+ url: str(),
1467
+ snippet: str(),
1468
+ source: str("e.g. Wiki, GitHub")
1469
+ }, [
1470
+ "title"
1471
+ ]), {
1472
+ minItems: 1,
1473
+ maxItems: 12
1474
+ })
1475
+ }, [
1476
+ "items"
1477
+ ]),
1478
+ 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")
1479
+ });
1480
+ var Checklist = defineComponent({
1481
+ name: "Checklist",
1482
+ title: "Checklist",
1483
+ description: "Steps or a to-do list with done/not-done state.",
1484
+ props: obj({
1485
+ title: str(),
1486
+ items: arr(obj({
1487
+ label: str(),
1488
+ done: {
1489
+ type: "boolean"
1490
+ },
1491
+ note: str()
1492
+ }, [
1493
+ "label"
1494
+ ]), {
1495
+ minItems: 1,
1496
+ maxItems: 50
1497
+ })
1498
+ }, [
1499
+ "items"
1500
+ ]),
1501
+ 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")
1502
+ });
1503
+ var Timeline = defineComponent({
1504
+ name: "Timeline",
1505
+ title: "Timeline",
1506
+ description: "Events in chronological order (the history of a project, an incident, a deal).",
1507
+ props: obj({
1508
+ title: str(),
1509
+ events: arr(obj({
1510
+ date: str(),
1511
+ title: str(),
1512
+ description: str()
1513
+ }, [
1514
+ "title"
1515
+ ]), {
1516
+ minItems: 1,
1517
+ maxItems: 50
1518
+ })
1519
+ }, [
1520
+ "events"
1521
+ ]),
1522
+ 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")
1523
+ });
1524
+ var CodeBlock = defineComponent({
1525
+ name: "CodeBlock",
1526
+ title: "Code",
1527
+ description: "A code snippet or file excerpt with syntax highlighting.",
1528
+ props: obj({
1529
+ title: str(),
1530
+ language: str(),
1531
+ code: str()
1532
+ }, [
1533
+ "code"
1534
+ ]),
1535
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}\`\`\`
1536
+ ${s(props.code)}
1537
+ \`\`\``, "fallbackText")
1538
+ });
1539
+ var Diff = defineComponent({
1540
+ name: "Diff",
1541
+ title: "Diff",
1542
+ description: "A before/after change to a text or code file, as a line diff.",
1543
+ props: obj({
1544
+ title: str(),
1545
+ language: str(),
1546
+ before: str(),
1547
+ after: str()
1548
+ }, [
1549
+ "before",
1550
+ "after"
1551
+ ]),
1552
+ fallbackText: /* @__PURE__ */ __name((props) => `${heading(props)}\`\`\`
1553
+ ${lineDiff(s(props.before), s(props.after)).map((line) => `${line.op === "add" ? "+" : line.op === "del" ? "-" : " "} ${line.text}`).join("\n")}
1554
+ \`\`\``, "fallbackText")
1555
+ });
1556
+ var Callout = defineComponent({
1557
+ name: "Callout",
1558
+ title: "Callout",
1559
+ description: "A short note, warning or success message, emphasized.",
1560
+ props: obj({
1561
+ tone: str(void 0, {
1562
+ enum: [
1563
+ "info",
1564
+ "success",
1565
+ "warning",
1566
+ "danger"
1567
+ ]
1568
+ }),
1569
+ title: str(),
1570
+ text: str()
1571
+ }, [
1572
+ "text"
1573
+ ]),
1574
+ fallbackText: /* @__PURE__ */ __name((props) => `${props.tone === "warning" || props.tone === "danger" ? ":warning: " : ":information_source: "}${heading(props)}${s(props.text)}`, "fallbackText")
1575
+ });
1576
+ var Stack = defineComponent({
1577
+ name: "Stack",
1578
+ title: "Stack",
1579
+ description: "Lays its children out vertically (or horizontally with direction=row).",
1580
+ props: obj({
1581
+ direction: str(void 0, {
1582
+ enum: [
1583
+ "column",
1584
+ "row"
1585
+ ]
1586
+ }),
1587
+ gap: {
1588
+ type: "number"
1589
+ }
1590
+ }),
1591
+ children: true,
1592
+ fallbackText: /* @__PURE__ */ __name(() => "", "fallbackText")
1593
+ });
1594
+ var Card = defineComponent({
1595
+ name: "Card",
1596
+ title: "Card",
1597
+ description: "A box with an optional title around its children.",
1598
+ props: obj({
1599
+ title: str(),
1600
+ subtitle: str()
1601
+ }),
1602
+ children: true,
1603
+ fallbackText: /* @__PURE__ */ __name((props) => props.title ? `*${s(props.title)}*${props.subtitle ? ` \u2014 ${s(props.subtitle)}` : ""}` : "", "fallbackText")
1604
+ });
1605
+ var Heading = defineComponent({
1606
+ name: "Heading",
1607
+ title: "Heading",
1608
+ description: "A heading.",
1609
+ props: obj({
1610
+ text: str()
1611
+ }, [
1612
+ "text"
1613
+ ]),
1614
+ fallbackText: /* @__PURE__ */ __name((props) => `*${s(props.text)}*`, "fallbackText")
1615
+ });
1616
+ var Text = defineComponent({
1617
+ name: "Text",
1618
+ title: "Text",
1619
+ description: "A paragraph of plain text (no markdown).",
1620
+ props: obj({
1621
+ text: str(),
1622
+ muted: {
1623
+ type: "boolean"
1624
+ }
1625
+ }, [
1626
+ "text"
1627
+ ]),
1628
+ fallbackText: /* @__PURE__ */ __name((props) => s(props.text), "fallbackText")
1629
+ });
1630
+ var Badge = defineComponent({
1631
+ name: "Badge",
1632
+ title: "Badge",
1633
+ description: "A small label.",
1634
+ props: obj({
1635
+ text: str(),
1636
+ tone: str(void 0, {
1637
+ enum: [
1638
+ "neutral",
1639
+ "brand",
1640
+ "success",
1641
+ "warning",
1642
+ "danger"
1643
+ ]
1644
+ })
1645
+ }, [
1646
+ "text"
1647
+ ]),
1648
+ fallbackText: /* @__PURE__ */ __name((props) => `[${s(props.text)}]`, "fallbackText")
1649
+ });
1650
+ var Link = defineComponent({
1651
+ name: "Link",
1652
+ title: "Link",
1653
+ description: "A link to an http(s) URL.",
1654
+ props: obj({
1655
+ text: str(),
1656
+ url: str(void 0, {
1657
+ pattern: "^https?://"
1658
+ })
1659
+ }, [
1660
+ "text",
1661
+ "url"
1662
+ ]),
1663
+ fallbackText: /* @__PURE__ */ __name((props) => `<${s(props.url)}|${s(props.text || props.url)}>`, "fallbackText")
1664
+ });
1665
+ var Image = defineComponent({
1666
+ name: "Image",
1667
+ title: "Image",
1668
+ description: "An image from an https URL.",
1669
+ props: obj({
1670
+ url: str(void 0, {
1671
+ pattern: "^https://"
1672
+ }),
1673
+ alt: str()
1674
+ }, [
1675
+ "url"
1676
+ ]),
1677
+ fallbackText: /* @__PURE__ */ __name((props) => s(props.url), "fallbackText")
1678
+ });
1679
+
1680
+ // src/genui/registry.ts
1681
+ function snapshotComponentPresentation(value) {
1682
+ const seen = /* @__PURE__ */ new Set();
1683
+ const check2 = /* @__PURE__ */ __name((entry) => {
1684
+ if (entry === null || typeof entry === "string" || typeof entry === "boolean") return;
1685
+ if (typeof entry === "number" && Number.isFinite(entry)) return;
1686
+ if (typeof entry !== "object" || entry === null) throw new TypeError("genui: presentation must contain only JSON values");
1687
+ if (seen.has(entry)) throw new TypeError("genui: presentation must contain acyclic JSON values");
1688
+ if (!Array.isArray(entry) && Object.getPrototypeOf(entry) !== Object.prototype && Object.getPrototypeOf(entry) !== null) throw new TypeError("genui: presentation must contain plain JSON objects");
1689
+ seen.add(entry);
1690
+ for (const item of Object.values(entry)) check2(item);
1691
+ seen.delete(entry);
1692
+ }, "check");
1693
+ check2(value);
1694
+ return JSON.parse(JSON.stringify(value));
1695
+ }
1696
+ __name(snapshotComponentPresentation, "snapshotComponentPresentation");
1697
+ function createComponent(definition, options = {}) {
1698
+ defineComponent(definition);
1699
+ const catalog = defineCatalog([
1700
+ definition
1701
+ ], options);
1702
+ const factory = /* @__PURE__ */ __name(async (props) => {
1703
+ const result = await validateProps(definition.props, props, catalog.validator);
1704
+ if (!result.ok) throw new Error(`genui: invalid ${definition.name} props: ${formatIssues(result.issues)}`);
1705
+ const normalized = snapshotComponentPresentation(result.value);
1706
+ if (typeof normalized !== "object" || normalized === null || Array.isArray(normalized)) throw new TypeError("genui: component props must be a JSON object");
1707
+ const portable = await catalog.validate(definition.name, normalized);
1708
+ if (!portable.ok || canonicalJson(snapshotComponentPresentation(portable.value)) !== canonicalJson(normalized)) {
1709
+ throw new Error(`genui: ${definition.name} requires outputProps accepting stable normalized JSON props`);
1710
+ }
1711
+ return {
1712
+ component: definition.name,
1713
+ props: normalized,
1714
+ version: definition.version ?? 1,
1715
+ fallbackText: componentToText(catalog, definition.name, normalized)
1716
+ };
1717
+ }, "factory");
1718
+ return Object.assign(factory, {
1719
+ definition
1720
+ });
1721
+ }
1722
+ __name(createComponent, "createComponent");
1723
+ function createComponentRegistry(options = {}) {
1724
+ let catalog = defineCatalog([], options);
1725
+ const channels = /* @__PURE__ */ new Map();
1726
+ const registry = {
1727
+ get catalog() {
1728
+ return catalog;
1729
+ },
1730
+ get manifest() {
1731
+ return catalog.components.map((definition) => {
1732
+ const props = catalog.jsonSchemaFor(definition.name);
1733
+ return {
1734
+ name: definition.name,
1735
+ title: definition.title,
1736
+ description: definition.description,
1737
+ version: definition.version ?? 1,
1738
+ ...props === void 0 ? {} : {
1739
+ props: snapshotComponentPresentation(props)
1740
+ }
1741
+ };
1742
+ });
1743
+ },
1744
+ register(definition, renderers) {
1745
+ if (catalog.has(definition.name)) throw new Error(`genui: component "${definition.name}" is already registered`);
1746
+ catalog = catalog.extend([
1747
+ definition
1748
+ ]);
1749
+ channels.set(definition.name, {
1750
+ ...renderers
1751
+ });
1752
+ return this;
1753
+ },
1754
+ async prepare(presentation) {
1755
+ const definition = catalog.get(presentation.component);
1756
+ if (definition === void 0) throw new Error(`genui: unknown component "${presentation.component}"`);
1757
+ if (presentation.version !== (definition.version ?? 1)) throw new Error(`genui: component "${presentation.component}" version mismatch`);
1758
+ const validated = await catalog.validate(presentation.component, presentation.props);
1759
+ if (!validated.ok) throw new Error(`genui: invalid ${presentation.component} props: ${formatIssues(validated.issues)}`);
1760
+ const props = snapshotComponentPresentation(validated.value);
1761
+ if (typeof props !== "object" || props === null || Array.isArray(props)) throw new TypeError("genui: component props must be a JSON object");
1762
+ return {
1763
+ component: definition.name,
1764
+ props,
1765
+ version: definition.version ?? 1,
1766
+ fallbackText: componentToText(catalog, definition.name, props)
1767
+ };
1768
+ },
1769
+ async render(presentation, channel, context) {
1770
+ const prepared = await this.prepare(presentation);
1771
+ const mapping = channels.get(prepared.component);
1772
+ const renderer = mapping === void 0 ? void 0 : Object.hasOwn(mapping, channel) ? mapping[channel] : Object.hasOwn(mapping, "text") ? mapping.text : void 0;
1773
+ return renderer === void 0 ? prepared.fallbackText : renderer(prepared.props, context);
1774
+ }
1775
+ };
1776
+ return registry;
1777
+ }
1778
+ __name(createComponentRegistry, "createComponentRegistry");
1779
+ var table = createComponent(DataTable);
1780
+ var chart = createComponent(Chart);
1781
+ function validatePresentationBatch(presentations) {
1782
+ const batch = Array.isArray(presentations) ? presentations : [
1783
+ presentations
1784
+ ];
1785
+ return batch.map((item) => {
1786
+ const presentation = snapshotComponentPresentation(item);
1787
+ 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)) {
1788
+ throw new TypeError("genui: invalid component presentation");
1789
+ }
1790
+ return presentation;
1791
+ });
1792
+ }
1793
+ __name(validatePresentationBatch, "validatePresentationBatch");
1794
+ function canonicalJson(value) {
1795
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
1796
+ if (typeof value === "object" && value !== null) {
1797
+ const record2 = value;
1798
+ return `{${Object.keys(record2).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record2[key])}`).join(",")}}`;
1799
+ }
1800
+ return JSON.stringify(value);
1801
+ }
1802
+ __name(canonicalJson, "canonicalJson");
1147
1803
  export {
1148
1804
  COMPONENT_NAME,
1149
1805
  GENUI_SHOW_TOOL,
@@ -1151,7 +1807,10 @@ export {
1151
1807
  ajvValidator,
1152
1808
  builtinJsonSchemaValidator,
1153
1809
  catalogToModelText,
1810
+ chart,
1154
1811
  componentToText,
1812
+ createComponent,
1813
+ createComponentRegistry,
1155
1814
  defineCatalog,
1156
1815
  defineComponent,
1157
1816
  fillTemplate,
@@ -1160,16 +1819,22 @@ export {
1160
1819
  getPath,
1161
1820
  isStandardSchema,
1162
1821
  jsonStandardSchema,
1822
+ negotiateCatalog,
1823
+ prepareUiEmission,
1163
1824
  showToolJsonSchema,
1825
+ snapshotComponentPresentation,
1164
1826
  summarizeSchema,
1827
+ table,
1165
1828
  toJsonSchema,
1166
1829
  toSnakeCase,
1167
1830
  toolNameFor,
1168
1831
  treeJsonSchema,
1169
1832
  treeToFlatSpec,
1170
1833
  treeToText,
1834
+ validatePresentationBatch,
1171
1835
  validateProps,
1172
1836
  validatePropsSync,
1173
- validateTree
1837
+ validateTree,
1838
+ validateUiCapabilities
1174
1839
  };
1175
1840
  //# sourceMappingURL=index.js.map