@dudousxd/nestjs-agent-core 0.40.0 → 0.42.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 (36) hide show
  1. package/README.md +7 -0
  2. package/dist/ag-ui/index.cjs +46 -10
  3. package/dist/ag-ui/index.cjs.map +1 -1
  4. package/dist/ag-ui/index.d.cts +9 -3
  5. package/dist/ag-ui/index.d.ts +9 -3
  6. package/dist/ag-ui/index.js +46 -10
  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.map +1 -1
  11. package/dist/genui/builtins.d.cts +1 -1
  12. package/dist/genui/builtins.d.ts +1 -1
  13. package/dist/genui/builtins.js.map +1 -1
  14. package/dist/genui/index.cjs +660 -68
  15. package/dist/genui/index.cjs.map +1 -1
  16. package/dist/genui/index.d.cts +5 -5
  17. package/dist/genui/index.d.ts +5 -5
  18. package/dist/genui/index.js +654 -68
  19. package/dist/genui/index.js.map +1 -1
  20. package/dist/guardrails/index.cjs +3 -1
  21. package/dist/guardrails/index.cjs.map +1 -1
  22. package/dist/guardrails/index.d.cts +5 -4
  23. package/dist/guardrails/index.d.ts +5 -4
  24. package/dist/guardrails/index.js +3 -1
  25. package/dist/guardrails/index.js.map +1 -1
  26. package/dist/index.cjs +1164 -421
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +124 -13
  29. package/dist/index.d.ts +124 -13
  30. package/dist/index.js +1156 -421
  31. package/dist/index.js.map +1 -1
  32. package/dist/{processors-BR3nICcp.d.ts → processors-BKILjME5.d.ts} +1 -1
  33. package/dist/{processors-B7nfbU6E.d.cts → processors-DpHBqRuE.d.cts} +1 -1
  34. package/dist/{stream-events-M0jc7lSW.d.cts → stream-events-C-pj3bVY.d.cts} +92 -4
  35. package/dist/{stream-events-DAmes2Of.d.ts → stream-events-D92SgfRk.d.ts} +92 -4
  36. package/package.json +1 -1
@@ -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,
@@ -39,13 +42,16 @@ __export(genui_exports, {
39
42
  negotiateCatalog: () => negotiateCatalog,
40
43
  prepareUiEmission: () => prepareUiEmission,
41
44
  showToolJsonSchema: () => showToolJsonSchema,
45
+ snapshotComponentPresentation: () => snapshotComponentPresentation,
42
46
  summarizeSchema: () => summarizeSchema,
47
+ table: () => table,
43
48
  toJsonSchema: () => toJsonSchema,
44
49
  toSnakeCase: () => toSnakeCase,
45
50
  toolNameFor: () => toolNameFor,
46
51
  treeJsonSchema: () => treeJsonSchema,
47
52
  treeToFlatSpec: () => treeToFlatSpec,
48
53
  treeToText: () => treeToText,
54
+ validatePresentationBatch: () => validatePresentationBatch,
49
55
  validateProps: () => validateProps,
50
56
  validatePropsSync: () => validatePropsSync,
51
57
  validateTree: () => validateTree,
@@ -229,21 +235,21 @@ function check(schema, value, path, root, issues, depth) {
229
235
  });
230
236
  return;
231
237
  }
232
- const s = schema;
233
- if (typeof s.$ref === "string") {
234
- const target = resolveRef(root, s.$ref);
238
+ const s2 = schema;
239
+ if (typeof s2.$ref === "string") {
240
+ const target = resolveRef(root, s2.$ref);
235
241
  if (target === void 0) {
236
242
  issues.push({
237
243
  path,
238
- message: `references unknown schema ${s.$ref}`
244
+ message: `references unknown schema ${s2.$ref}`
239
245
  });
240
246
  return;
241
247
  }
242
248
  check(target, value, path, root, issues, depth + 1);
243
249
  }
244
- if (s.type !== void 0) {
245
- const types = Array.isArray(s.type) ? s.type : [
246
- s.type
250
+ if (s2.type !== void 0) {
251
+ const types = Array.isArray(s2.type) ? s2.type : [
252
+ s2.type
247
253
  ];
248
254
  if (!types.some((type) => matchesType(type, value))) {
249
255
  issues.push({
@@ -253,76 +259,76 @@ function check(schema, value, path, root, issues, depth) {
253
259
  return;
254
260
  }
255
261
  }
256
- 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))) {
257
263
  issues.push({
258
264
  path,
259
- 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(", ")}`
260
266
  });
261
267
  }
262
- if ("const" in s && !deepEqual(s.const, value)) {
268
+ if ("const" in s2 && !deepEqual(s2.const, value)) {
263
269
  issues.push({
264
270
  path,
265
- message: `must be ${JSON.stringify(s.const)}`
271
+ message: `must be ${JSON.stringify(s2.const)}`
266
272
  });
267
273
  }
268
274
  if (typeof value === "string") {
269
- if (typeof s.minLength === "number" && value.length < s.minLength) {
275
+ if (typeof s2.minLength === "number" && value.length < s2.minLength) {
270
276
  issues.push({
271
277
  path,
272
- message: `must be at least ${s.minLength} characters`
278
+ message: `must be at least ${s2.minLength} characters`
273
279
  });
274
280
  }
275
- if (typeof s.maxLength === "number" && value.length > s.maxLength) {
281
+ if (typeof s2.maxLength === "number" && value.length > s2.maxLength) {
276
282
  issues.push({
277
283
  path,
278
- message: `must be at most ${s.maxLength} characters`
284
+ message: `must be at most ${s2.maxLength} characters`
279
285
  });
280
286
  }
281
- if (typeof s.pattern === "string") {
287
+ if (typeof s2.pattern === "string") {
282
288
  let pattern;
283
289
  try {
284
- pattern = new RegExp(s.pattern, "u");
290
+ pattern = new RegExp(s2.pattern, "u");
285
291
  } catch {
286
292
  pattern = void 0;
287
293
  }
288
294
  if (pattern !== void 0 && !pattern.test(value)) {
289
295
  issues.push({
290
296
  path,
291
- message: `must match ${s.pattern}`
297
+ message: `must match ${s2.pattern}`
292
298
  });
293
299
  }
294
300
  }
295
301
  }
296
302
  if (typeof value === "number") {
297
- if (typeof s.minimum === "number" && value < s.minimum) {
303
+ if (typeof s2.minimum === "number" && value < s2.minimum) {
298
304
  issues.push({
299
305
  path,
300
- message: `must be >= ${s.minimum}`
306
+ message: `must be >= ${s2.minimum}`
301
307
  });
302
308
  }
303
- if (typeof s.maximum === "number" && value > s.maximum) {
309
+ if (typeof s2.maximum === "number" && value > s2.maximum) {
304
310
  issues.push({
305
311
  path,
306
- message: `must be <= ${s.maximum}`
312
+ message: `must be <= ${s2.maximum}`
307
313
  });
308
314
  }
309
315
  }
310
316
  if (Array.isArray(value)) {
311
- if (typeof s.minItems === "number" && value.length < s.minItems) {
317
+ if (typeof s2.minItems === "number" && value.length < s2.minItems) {
312
318
  issues.push({
313
319
  path,
314
- message: `must have at least ${s.minItems} items`
320
+ message: `must have at least ${s2.minItems} items`
315
321
  });
316
322
  }
317
- if (typeof s.maxItems === "number" && value.length > s.maxItems) {
323
+ if (typeof s2.maxItems === "number" && value.length > s2.maxItems) {
318
324
  issues.push({
319
325
  path,
320
- message: `must have at most ${s.maxItems} items`
326
+ message: `must have at most ${s2.maxItems} items`
321
327
  });
322
328
  }
323
- if (s.items !== void 0 && !Array.isArray(s.items)) {
329
+ if (s2.items !== void 0 && !Array.isArray(s2.items)) {
324
330
  value.forEach((item, index) => {
325
- check(s.items, item, [
331
+ check(s2.items, item, [
326
332
  ...path,
327
333
  index
328
334
  ], root, issues, depth + 1);
@@ -331,9 +337,9 @@ function check(schema, value, path, root, issues, depth) {
331
337
  }
332
338
  if (matchesType("object", value)) {
333
339
  const record2 = value;
334
- const properties = typeof s.properties === "object" && s.properties !== null ? s.properties : {};
335
- if (Array.isArray(s.required)) {
336
- for (const key of s.required) {
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) {
337
343
  if (record2[key] === void 0) {
338
344
  issues.push({
339
345
  path: [
@@ -353,7 +359,7 @@ function check(schema, value, path, root, issues, depth) {
353
359
  key
354
360
  ], root, issues, depth + 1);
355
361
  }
356
- } else if (s.additionalProperties === false) {
362
+ } else if (s2.additionalProperties === false) {
357
363
  issues.push({
358
364
  path: [
359
365
  ...path,
@@ -361,34 +367,34 @@ function check(schema, value, path, root, issues, depth) {
361
367
  ],
362
368
  message: "is not a known property"
363
369
  });
364
- } else if (typeof s.additionalProperties === "object" && s.additionalProperties !== null) {
365
- check(s.additionalProperties, entry, [
370
+ } else if (typeof s2.additionalProperties === "object" && s2.additionalProperties !== null) {
371
+ check(s2.additionalProperties, entry, [
366
372
  ...path,
367
373
  key
368
374
  ], root, issues, depth + 1);
369
375
  }
370
376
  }
371
377
  }
372
- if (Array.isArray(s.allOf)) {
373
- 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);
374
380
  }
375
- if (Array.isArray(s.anyOf) || Array.isArray(s.oneOf)) {
376
- 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;
377
383
  const passing = options.filter((sub) => {
378
384
  const nested = [];
379
385
  check(sub, value, path, root, nested, depth + 1);
380
386
  return nested.length === 0;
381
387
  }).length;
382
- if (Array.isArray(s.anyOf) ? passing === 0 : passing !== 1) {
388
+ if (Array.isArray(s2.anyOf) ? passing === 0 : passing !== 1) {
383
389
  issues.push({
384
390
  path,
385
- 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"
386
392
  });
387
393
  }
388
394
  }
389
- if (s.not !== void 0) {
395
+ if (s2.not !== void 0) {
390
396
  const nested = [];
391
- check(s.not, value, path, root, nested, depth + 1);
397
+ check(s2.not, value, path, root, nested, depth + 1);
392
398
  if (nested.length === 0) {
393
399
  issues.push({
394
400
  path,
@@ -445,7 +451,7 @@ function buildCatalog(byName, validator) {
445
451
  ]
446
452
  };
447
453
  }
448
- const result = await validateProps(definition.props, props, validator);
454
+ const result = await validateProps(definition.outputProps ?? definition.props, props, validator);
449
455
  return result;
450
456
  },
451
457
  validateSync(component, props) {
@@ -461,11 +467,11 @@ function buildCatalog(byName, validator) {
461
467
  ]
462
468
  };
463
469
  }
464
- return validatePropsSync(definition.props, props, validator);
470
+ return validatePropsSync(definition.outputProps ?? definition.props, props, validator);
465
471
  },
466
472
  jsonSchemaFor(component) {
467
473
  const definition = byName.get(component);
468
- return definition === void 0 ? void 0 : toJsonSchema(definition.props);
474
+ return definition === void 0 ? void 0 : toJsonSchema(definition.outputProps ?? definition.props);
469
475
  },
470
476
  extend(more) {
471
477
  const next = new Map(byName);
@@ -489,7 +495,7 @@ __name(toolNameFor, "toolNameFor");
489
495
 
490
496
  // src/genui/tree.ts
491
497
  var GENUI_TREE_COMPONENT = "genui:tree";
492
- async function validateTree(catalog, tree, limits = {}) {
498
+ async function validateTree(catalog, tree, limits = {}, phase = "output") {
493
499
  const maxDepth = limits.maxDepth ?? 12;
494
500
  const maxNodes = limits.maxNodes ?? 200;
495
501
  const issues = [];
@@ -541,7 +547,7 @@ async function validateTree(catalog, tree, limits = {}) {
541
547
  });
542
548
  return void 0;
543
549
  }
544
- 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 ?? {});
545
551
  let props = {};
546
552
  if (!validated.ok) {
547
553
  for (const issue of validated.issues) {
@@ -763,7 +769,7 @@ function catalogToModelText(catalog, options = {}) {
763
769
  lines.push("Components you can show (one tool each):");
764
770
  }
765
771
  for (const component of catalog.modelComponents()) {
766
- const schema = catalog.jsonSchemaFor(component.name);
772
+ const schema = toJsonSchema(component.props);
767
773
  const head = mode === "per-component" ? `- ${component.name} (tool \`${toolNameFor(component.name, options.namePrefix)}\`)` : `- ${component.name}`;
768
774
  lines.push(`${head}: ${component.description}`);
769
775
  if (schema !== void 0) {
@@ -778,38 +784,38 @@ function catalogToModelText(catalog, options = {}) {
778
784
  __name(catalogToModelText, "catalogToModelText");
779
785
  function summarizeSchema(schema, root, depth) {
780
786
  if (typeof schema !== "object" || schema === null) return "unknown";
781
- const s = schema;
787
+ const s2 = schema;
782
788
  if (depth > 5) return "\u2026";
783
- if (typeof s.$ref === "string") {
784
- 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";
785
791
  }
786
- if (Array.isArray(s.enum)) {
787
- 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(" | ");
788
794
  }
789
- if ("const" in s) return JSON.stringify(s.const);
790
- const variants = s.anyOf ?? s.oneOf;
795
+ if ("const" in s2) return JSON.stringify(s2.const);
796
+ const variants = s2.anyOf ?? s2.oneOf;
791
797
  if (Array.isArray(variants)) {
792
798
  return variants.map((variant) => summarizeSchema(variant, root, depth + 1)).join(" | ");
793
799
  }
794
- const types = Array.isArray(s.type) ? s.type : s.type !== void 0 ? [
795
- s.type
800
+ const types = Array.isArray(s2.type) ? s2.type : s2.type !== void 0 ? [
801
+ s2.type
796
802
  ] : [];
797
803
  if (types.length > 1) {
798
804
  return types.map((type2) => summarizeSchema({
799
- ...s,
805
+ ...s2,
800
806
  type: type2
801
807
  }, root, depth + 1)).join(" | ");
802
808
  }
803
809
  const type = types[0];
804
810
  if (type === "array") {
805
- 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);
806
812
  return /[|\s]/.test(item) && !item.startsWith("{") ? `(${item})[]` : `${item}[]`;
807
813
  }
808
- if (type === "object" || type === void 0 && s.properties !== void 0) {
809
- const properties = s.properties ?? {};
814
+ if (type === "object" || type === void 0 && s2.properties !== void 0) {
815
+ const properties = s2.properties ?? {};
810
816
  const keys = Object.keys(properties);
811
817
  if (keys.length === 0) return "object";
812
- const required = new Set(Array.isArray(s.required) ? s.required : []);
818
+ const required = new Set(Array.isArray(s2.required) ? s2.required : []);
813
819
  const fields = keys.map((key) => {
814
820
  const field = properties[key];
815
821
  const note = typeof field?.description === "string" && depth === 0 ? ` /* ${field.description} */` : "";
@@ -977,7 +983,7 @@ function componentTool(catalog, component, options) {
977
983
  if (definition === void 0) {
978
984
  throw new Error(`component "${component.name}" is not available here`);
979
985
  }
980
- const validated = await validateProps(definition.props, input, resolved.validator);
986
+ const validated = await validateProps(dynamic ? definition.props : definition.outputProps ?? definition.props, input, resolved.validator);
981
987
  if (!validated.ok) {
982
988
  throw new Error(`invalid ${component.name} props: ${formatIssues(validated.issues)}`);
983
989
  }
@@ -1033,7 +1039,7 @@ function treeTool(catalog, options) {
1033
1039
  const handler = {
1034
1040
  async execute(input, ctx) {
1035
1041
  const resolved = await catalogFor(catalog, options, scopeOf(ctx));
1036
- const result = await validateTree(resolved, input, options.treeLimits);
1042
+ const result = await validateTree(resolved, input, options.treeLimits, dynamic ? "input" : "output");
1037
1043
  if (!result.ok) {
1038
1044
  throw new Error(`invalid UI tree: ${formatIssues(result.issues)}`);
1039
1045
  }
@@ -1057,7 +1063,7 @@ function treeTool(catalog, options) {
1057
1063
  kind: "read",
1058
1064
  description: describeFor(catalog),
1059
1065
  inputSchema: dynamic ? permissiveSchema(treeJsonSchema(catalog)) : asyncJsonStandardSchema(treeJsonSchema(catalog), async (value) => {
1060
- const result = await validateTree(catalog, value, options.treeLimits);
1066
+ const result = await validateTree(catalog, value, options.treeLimits, "input");
1061
1067
  return result.ok ? {
1062
1068
  value: result.value
1063
1069
  } : {
@@ -1092,7 +1098,7 @@ function showToolJsonSchema(catalog) {
1092
1098
  };
1093
1099
  }
1094
1100
  __name(showToolJsonSchema, "showToolJsonSchema");
1095
- async function validateShow(catalog, input) {
1101
+ async function validateShow(catalog, input, phase = "input") {
1096
1102
  if (typeof input !== "object" || input === null || Array.isArray(input)) {
1097
1103
  return {
1098
1104
  ok: false,
@@ -1133,7 +1139,7 @@ async function validateShow(catalog, input) {
1133
1139
  ]
1134
1140
  };
1135
1141
  }
1136
- 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);
1137
1143
  if (!validated.ok) {
1138
1144
  return {
1139
1145
  ok: false,
@@ -1173,7 +1179,7 @@ function showTool(catalog, options) {
1173
1179
  const handler = {
1174
1180
  async execute(input, ctx) {
1175
1181
  const resolved = await catalogFor(catalog, options, scopeOf(ctx));
1176
- const result = await validateShow(resolved, input);
1182
+ const result = await validateShow(resolved, input, dynamic ? "input" : "output");
1177
1183
  if (!result.ok) {
1178
1184
  throw new Error(`invalid ${name} call: ${formatIssues(result.issues)}`);
1179
1185
  }
@@ -1272,6 +1278,586 @@ function asyncJsonStandardSchema(schema, validate) {
1272
1278
  };
1273
1279
  }
1274
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");
1275
1861
  // Annotate the CommonJS export names for ESM import in node:
1276
1862
  0 && (module.exports = {
1277
1863
  COMPONENT_NAME,
@@ -1280,7 +1866,10 @@ __name(asyncJsonStandardSchema, "asyncJsonStandardSchema");
1280
1866
  ajvValidator,
1281
1867
  builtinJsonSchemaValidator,
1282
1868
  catalogToModelText,
1869
+ chart,
1283
1870
  componentToText,
1871
+ createComponent,
1872
+ createComponentRegistry,
1284
1873
  defineCatalog,
1285
1874
  defineComponent,
1286
1875
  fillTemplate,
@@ -1292,13 +1881,16 @@ __name(asyncJsonStandardSchema, "asyncJsonStandardSchema");
1292
1881
  negotiateCatalog,
1293
1882
  prepareUiEmission,
1294
1883
  showToolJsonSchema,
1884
+ snapshotComponentPresentation,
1295
1885
  summarizeSchema,
1886
+ table,
1296
1887
  toJsonSchema,
1297
1888
  toSnakeCase,
1298
1889
  toolNameFor,
1299
1890
  treeJsonSchema,
1300
1891
  treeToFlatSpec,
1301
1892
  treeToText,
1893
+ validatePresentationBatch,
1302
1894
  validateProps,
1303
1895
  validatePropsSync,
1304
1896
  validateTree,