@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
@@ -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);
@@ -279,9 +279,9 @@ function check(schema, value, path, root, issues, depth) {
279
279
  }
280
280
  if (matchesType("object", value)) {
281
281
  const record2 = 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) {
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
285
  if (record2[key] === void 0) {
286
286
  issues.push({
287
287
  path: [
@@ -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,
@@ -393,7 +393,7 @@ function buildCatalog(byName, validator) {
393
393
  ]
394
394
  };
395
395
  }
396
- const result = await validateProps(definition.props, props, validator);
396
+ const result = await validateProps(definition.outputProps ?? definition.props, props, validator);
397
397
  return result;
398
398
  },
399
399
  validateSync(component, props) {
@@ -409,11 +409,11 @@ function buildCatalog(byName, validator) {
409
409
  ]
410
410
  };
411
411
  }
412
- return validatePropsSync(definition.props, props, validator);
412
+ return validatePropsSync(definition.outputProps ?? definition.props, props, validator);
413
413
  },
414
414
  jsonSchemaFor(component) {
415
415
  const definition = byName.get(component);
416
- return definition === void 0 ? void 0 : toJsonSchema(definition.props);
416
+ return definition === void 0 ? void 0 : toJsonSchema(definition.outputProps ?? definition.props);
417
417
  },
418
418
  extend(more) {
419
419
  const next = new Map(byName);
@@ -437,7 +437,7 @@ __name(toolNameFor, "toolNameFor");
437
437
 
438
438
  // src/genui/tree.ts
439
439
  var GENUI_TREE_COMPONENT = "genui:tree";
440
- async function validateTree(catalog, tree, limits = {}) {
440
+ async function validateTree(catalog, tree, limits = {}, phase = "output") {
441
441
  const maxDepth = limits.maxDepth ?? 12;
442
442
  const maxNodes = limits.maxNodes ?? 200;
443
443
  const issues = [];
@@ -489,7 +489,7 @@ async function validateTree(catalog, tree, limits = {}) {
489
489
  });
490
490
  return void 0;
491
491
  }
492
- 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 ?? {});
493
493
  let props = {};
494
494
  if (!validated.ok) {
495
495
  for (const issue of validated.issues) {
@@ -711,7 +711,7 @@ function catalogToModelText(catalog, options = {}) {
711
711
  lines.push("Components you can show (one tool each):");
712
712
  }
713
713
  for (const component of catalog.modelComponents()) {
714
- const schema = catalog.jsonSchemaFor(component.name);
714
+ const schema = toJsonSchema(component.props);
715
715
  const head = mode === "per-component" ? `- ${component.name} (tool \`${toolNameFor(component.name, options.namePrefix)}\`)` : `- ${component.name}`;
716
716
  lines.push(`${head}: ${component.description}`);
717
717
  if (schema !== void 0) {
@@ -726,38 +726,38 @@ function catalogToModelText(catalog, options = {}) {
726
726
  __name(catalogToModelText, "catalogToModelText");
727
727
  function summarizeSchema(schema, root, depth) {
728
728
  if (typeof schema !== "object" || schema === null) return "unknown";
729
- const s = schema;
729
+ const s2 = schema;
730
730
  if (depth > 5) return "\u2026";
731
- if (typeof s.$ref === "string") {
732
- 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";
733
733
  }
734
- if (Array.isArray(s.enum)) {
735
- 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(" | ");
736
736
  }
737
- if ("const" in s) return JSON.stringify(s.const);
738
- const variants = s.anyOf ?? s.oneOf;
737
+ if ("const" in s2) return JSON.stringify(s2.const);
738
+ const variants = s2.anyOf ?? s2.oneOf;
739
739
  if (Array.isArray(variants)) {
740
740
  return variants.map((variant) => summarizeSchema(variant, root, depth + 1)).join(" | ");
741
741
  }
742
- const types = Array.isArray(s.type) ? s.type : s.type !== void 0 ? [
743
- s.type
742
+ const types = Array.isArray(s2.type) ? s2.type : s2.type !== void 0 ? [
743
+ s2.type
744
744
  ] : [];
745
745
  if (types.length > 1) {
746
746
  return types.map((type2) => summarizeSchema({
747
- ...s,
747
+ ...s2,
748
748
  type: type2
749
749
  }, root, depth + 1)).join(" | ");
750
750
  }
751
751
  const type = types[0];
752
752
  if (type === "array") {
753
- 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);
754
754
  return /[|\s]/.test(item) && !item.startsWith("{") ? `(${item})[]` : `${item}[]`;
755
755
  }
756
- if (type === "object" || type === void 0 && s.properties !== void 0) {
757
- const properties = s.properties ?? {};
756
+ if (type === "object" || type === void 0 && s2.properties !== void 0) {
757
+ const properties = s2.properties ?? {};
758
758
  const keys = Object.keys(properties);
759
759
  if (keys.length === 0) return "object";
760
- const required = new Set(Array.isArray(s.required) ? s.required : []);
760
+ const required = new Set(Array.isArray(s2.required) ? s2.required : []);
761
761
  const fields = keys.map((key) => {
762
762
  const field = properties[key];
763
763
  const note = typeof field?.description === "string" && depth === 0 ? ` /* ${field.description} */` : "";
@@ -925,7 +925,7 @@ function componentTool(catalog, component, options) {
925
925
  if (definition === void 0) {
926
926
  throw new Error(`component "${component.name}" is not available here`);
927
927
  }
928
- const validated = await validateProps(definition.props, input, resolved.validator);
928
+ const validated = await validateProps(dynamic ? definition.props : definition.outputProps ?? definition.props, input, resolved.validator);
929
929
  if (!validated.ok) {
930
930
  throw new Error(`invalid ${component.name} props: ${formatIssues(validated.issues)}`);
931
931
  }
@@ -981,7 +981,7 @@ function treeTool(catalog, options) {
981
981
  const handler = {
982
982
  async execute(input, ctx) {
983
983
  const resolved = await catalogFor(catalog, options, scopeOf(ctx));
984
- const result = await validateTree(resolved, input, options.treeLimits);
984
+ const result = await validateTree(resolved, input, options.treeLimits, dynamic ? "input" : "output");
985
985
  if (!result.ok) {
986
986
  throw new Error(`invalid UI tree: ${formatIssues(result.issues)}`);
987
987
  }
@@ -1005,7 +1005,7 @@ function treeTool(catalog, options) {
1005
1005
  kind: "read",
1006
1006
  description: describeFor(catalog),
1007
1007
  inputSchema: dynamic ? permissiveSchema(treeJsonSchema(catalog)) : asyncJsonStandardSchema(treeJsonSchema(catalog), async (value) => {
1008
- const result = await validateTree(catalog, value, options.treeLimits);
1008
+ const result = await validateTree(catalog, value, options.treeLimits, "input");
1009
1009
  return result.ok ? {
1010
1010
  value: result.value
1011
1011
  } : {
@@ -1040,7 +1040,7 @@ function showToolJsonSchema(catalog) {
1040
1040
  };
1041
1041
  }
1042
1042
  __name(showToolJsonSchema, "showToolJsonSchema");
1043
- async function validateShow(catalog, input) {
1043
+ async function validateShow(catalog, input, phase = "input") {
1044
1044
  if (typeof input !== "object" || input === null || Array.isArray(input)) {
1045
1045
  return {
1046
1046
  ok: false,
@@ -1081,7 +1081,7 @@ async function validateShow(catalog, input) {
1081
1081
  ]
1082
1082
  };
1083
1083
  }
1084
- 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);
1085
1085
  if (!validated.ok) {
1086
1086
  return {
1087
1087
  ok: false,
@@ -1121,7 +1121,7 @@ function showTool(catalog, options) {
1121
1121
  const handler = {
1122
1122
  async execute(input, ctx) {
1123
1123
  const resolved = await catalogFor(catalog, options, scopeOf(ctx));
1124
- const result = await validateShow(resolved, input);
1124
+ const result = await validateShow(resolved, input, dynamic ? "input" : "output");
1125
1125
  if (!result.ok) {
1126
1126
  throw new Error(`invalid ${name} call: ${formatIssues(result.issues)}`);
1127
1127
  }
@@ -1220,6 +1220,586 @@ function asyncJsonStandardSchema(schema, validate) {
1220
1220
  };
1221
1221
  }
1222
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");
1223
1803
  export {
1224
1804
  COMPONENT_NAME,
1225
1805
  GENUI_SHOW_TOOL,
@@ -1227,7 +1807,10 @@ export {
1227
1807
  ajvValidator,
1228
1808
  builtinJsonSchemaValidator,
1229
1809
  catalogToModelText,
1810
+ chart,
1230
1811
  componentToText,
1812
+ createComponent,
1813
+ createComponentRegistry,
1231
1814
  defineCatalog,
1232
1815
  defineComponent,
1233
1816
  fillTemplate,
@@ -1239,13 +1822,16 @@ export {
1239
1822
  negotiateCatalog,
1240
1823
  prepareUiEmission,
1241
1824
  showToolJsonSchema,
1825
+ snapshotComponentPresentation,
1242
1826
  summarizeSchema,
1827
+ table,
1243
1828
  toJsonSchema,
1244
1829
  toSnakeCase,
1245
1830
  toolNameFor,
1246
1831
  treeJsonSchema,
1247
1832
  treeToFlatSpec,
1248
1833
  treeToText,
1834
+ validatePresentationBatch,
1249
1835
  validateProps,
1250
1836
  validatePropsSync,
1251
1837
  validateTree,