ag-ui-validate 0.3.0 → 0.4.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 (73) hide show
  1. package/README.md +25 -26
  2. package/dist/{catalog-DqTI4VLt.js → catalog-3e6INLCp.js} +120 -2
  3. package/dist/catalog-3e6INLCp.js.map +1 -0
  4. package/dist/{catalog-AB5vuZ8j.cjs → catalog-DH5gDIbo.cjs} +120 -2
  5. package/dist/catalog-DH5gDIbo.cjs.map +1 -0
  6. package/dist/cli.js +639 -61
  7. package/dist/cli.js.map +1 -1
  8. package/dist/{index-Hmqj3r_r.d.cts → index-Cydfs-rB.d.ts} +4 -4
  9. package/dist/{index-oNG1kOp9.d.ts → index-DsVmjcLO.d.cts} +4 -4
  10. package/dist/index.cjs +2 -2
  11. package/dist/index.d.cts +2 -2
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.js +2 -2
  14. package/dist/report.cjs +11 -2
  15. package/dist/report.cjs.map +1 -1
  16. package/dist/report.d.cts +21 -12
  17. package/dist/report.d.ts +21 -12
  18. package/dist/report.js +11 -2
  19. package/dist/report.js.map +1 -1
  20. package/dist/{src-Dcuqz2QC.js → src-CezZ73Mm.js} +494 -59
  21. package/dist/src-CezZ73Mm.js.map +1 -0
  22. package/dist/{src-D04i3Vso.cjs → src-CyugLoks.cjs} +494 -59
  23. package/dist/src-CyugLoks.cjs.map +1 -0
  24. package/dist/transport.cjs +1 -1
  25. package/dist/transport.d.cts +1 -1
  26. package/dist/transport.d.ts +1 -1
  27. package/dist/transport.js +1 -1
  28. package/dist/{types-oH_QTnn2.d.ts → types-CUIgyj2N.d.cts} +3 -1
  29. package/dist/{types-oH_QTnn2.d.cts → types-CUIgyj2N.d.ts} +3 -1
  30. package/dist/vitest.js +614 -59
  31. package/dist/vitest.js.map +1 -1
  32. package/js/src/cli-args.ts +16 -0
  33. package/js/src/cli.ts +1 -1
  34. package/js/src/index.ts +5 -0
  35. package/js/src/protocol/event-table.ts +93 -2
  36. package/js/src/report/json.ts +14 -3
  37. package/js/src/report/sarif.ts +3 -6
  38. package/js/src/rules/catalog.ts +4 -0
  39. package/js/src/rules/checks/context.ts +48 -3
  40. package/js/src/rules/checks/lifecycle.ts +5 -2
  41. package/js/src/rules/checks/subagents.ts +78 -0
  42. package/js/src/rules/checks/text.ts +11 -3
  43. package/js/src/rules/checks/toolcalls.ts +8 -2
  44. package/package.json +2 -2
  45. package/spec/catalog.json +108 -1
  46. package/spec/event-categories.json +6 -2
  47. package/spec/fixtures/invalid/AGUI501-missing-data-prefix/scenario.json +3 -3
  48. package/spec/fixtures/invalid/AGUI502-payload-not-json/stream.jsonl +2 -2
  49. package/spec/fixtures/invalid/AGUI503-unknown-event-type/expected.json +1 -1
  50. package/spec/fixtures/invalid/AGUI503-unknown-event-type/stream.jsonl +3 -3
  51. package/spec/fixtures/invalid/AGUI504-schema-violation/stream.jsonl +4 -4
  52. package/spec/fixtures/invalid/AGUI505-unexpected-content-type/scenario.json +5 -5
  53. package/spec/fixtures/invalid/AGUI506-keepalive-gap/scenario.json +5 -5
  54. package/spec/fixtures/invalid/AGUI507-buffered-response/scenario.json +1 -1
  55. package/spec/fixtures/invalid/AGUI508-connection-dropped/scenario.json +1 -1
  56. package/spec/fixtures/invalid/AGUI601-duplicate-subagent-started/expected.json +12 -0
  57. package/spec/fixtures/invalid/AGUI601-duplicate-subagent-started/stream.jsonl +5 -0
  58. package/spec/fixtures/invalid/AGUI602-finished-without-start/expected.json +11 -0
  59. package/spec/fixtures/invalid/AGUI602-finished-without-start/stream.jsonl +3 -0
  60. package/spec/fixtures/invalid/AGUI603-error-without-start/expected.json +11 -0
  61. package/spec/fixtures/invalid/AGUI603-error-without-start/stream.jsonl +3 -0
  62. package/spec/fixtures/invalid/AGUI604-subagent-unterminated/expected.json +10 -0
  63. package/spec/fixtures/invalid/AGUI604-subagent-unterminated/stream.jsonl +3 -0
  64. package/spec/fixtures/invalid/AGUI605-unknown-parent-subagent-run-id/expected.json +11 -0
  65. package/spec/fixtures/invalid/AGUI605-unknown-parent-subagent-run-id/stream.jsonl +4 -0
  66. package/spec/fixtures/invalid/AGUI606-continuation-owner-mismatch/expected.json +11 -0
  67. package/spec/fixtures/invalid/AGUI606-continuation-owner-mismatch/stream.jsonl +6 -0
  68. package/spec/fixtures/invalid/AGUI901-raw-wraps-typed-event/stream.jsonl +3 -3
  69. package/spec/fixtures/invalid/AGUI903-custom-name-not-namespaced/stream.jsonl +3 -3
  70. package/dist/catalog-AB5vuZ8j.cjs.map +0 -1
  71. package/dist/catalog-DqTI4VLt.js.map +0 -1
  72. package/dist/src-D04i3Vso.cjs.map +0 -1
  73. package/dist/src-Dcuqz2QC.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { d as ValidatorOptions, u as Validator } from "./types-oH_QTnn2.cjs";
1
+ import { d as ValidatorOptions, u as Validator } from "./types-CUIgyj2N.js";
2
2
  //#region src/protocol/event-table.d.ts
3
3
  type FieldKind = "string" | "number" | "boolean" | "object" | "array" | "any";
4
- type EventCategory = "lifecycle" | "text" | "toolcall" | "state" | "activity" | "reasoning" | "thinking" | "special";
4
+ type EventCategory = "lifecycle" | "text" | "toolcall" | "state" | "activity" | "reasoning" | "thinking" | "special" | "subagent";
5
5
  interface FieldSpec {
6
6
  kind: FieldKind;
7
7
  required: boolean;
@@ -17,7 +17,7 @@ interface EventSpec {
17
17
  fields: Readonly<Record<string, FieldSpec>>;
18
18
  }
19
19
  /** Version of @ag-ui/core this table was derived from. */
20
- declare const SDK_VERSION = "0.0.58";
20
+ declare const SDK_VERSION = "0.0.59";
21
21
  /** Canonical wire event types and their schemas, derived from @ag-ui/core. */
22
22
  declare const EVENT_TABLE: Readonly<Record<string, EventSpec>>;
23
23
  /** All canonical wire `type` values, in @ag-ui/core enum order. */
@@ -49,4 +49,4 @@ declare function applyPatch(doc: unknown, patch: unknown): PatchResult;
49
49
  declare function createValidator(opts?: ValidatorOptions): Validator;
50
50
  //#endregion
51
51
  export { EVENT_TYPES as a, FieldKind as c, EVENT_TABLE as i, FieldSpec as l, applyPatch as n, EventCategory as o, validatePatchShape as r, EventSpec as s, createValidator as t, SDK_VERSION as u };
52
- //# sourceMappingURL=index-Hmqj3r_r.d.cts.map
52
+ //# sourceMappingURL=index-Cydfs-rB.d.ts.map
@@ -1,7 +1,7 @@
1
- import { d as ValidatorOptions, u as Validator } from "./types-oH_QTnn2.js";
1
+ import { d as ValidatorOptions, u as Validator } from "./types-CUIgyj2N.cjs";
2
2
  //#region src/protocol/event-table.d.ts
3
3
  type FieldKind = "string" | "number" | "boolean" | "object" | "array" | "any";
4
- type EventCategory = "lifecycle" | "text" | "toolcall" | "state" | "activity" | "reasoning" | "thinking" | "special";
4
+ type EventCategory = "lifecycle" | "text" | "toolcall" | "state" | "activity" | "reasoning" | "thinking" | "special" | "subagent";
5
5
  interface FieldSpec {
6
6
  kind: FieldKind;
7
7
  required: boolean;
@@ -17,7 +17,7 @@ interface EventSpec {
17
17
  fields: Readonly<Record<string, FieldSpec>>;
18
18
  }
19
19
  /** Version of @ag-ui/core this table was derived from. */
20
- declare const SDK_VERSION = "0.0.58";
20
+ declare const SDK_VERSION = "0.0.59";
21
21
  /** Canonical wire event types and their schemas, derived from @ag-ui/core. */
22
22
  declare const EVENT_TABLE: Readonly<Record<string, EventSpec>>;
23
23
  /** All canonical wire `type` values, in @ag-ui/core enum order. */
@@ -49,4 +49,4 @@ declare function applyPatch(doc: unknown, patch: unknown): PatchResult;
49
49
  declare function createValidator(opts?: ValidatorOptions): Validator;
50
50
  //#endregion
51
51
  export { EVENT_TYPES as a, FieldKind as c, EVENT_TABLE as i, FieldSpec as l, applyPatch as n, EventCategory as o, validatePatchShape as r, EventSpec as s, createValidator as t, SDK_VERSION as u };
52
- //# sourceMappingURL=index-oNG1kOp9.d.ts.map
52
+ //# sourceMappingURL=index-DsVmjcLO.d.cts.map
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_src = require("./src-D04i3Vso.cjs");
3
- const require_catalog = require("./catalog-AB5vuZ8j.cjs");
2
+ const require_src = require("./src-CyugLoks.cjs");
3
+ const require_catalog = require("./catalog-DH5gDIbo.cjs");
4
4
  exports.CANONICAL_FEATURES = require_src.CANONICAL_FEATURES;
5
5
  exports.CATALOG = require_catalog.CATALOG;
6
6
  exports.EVENT_TABLE = require_src.EVENT_TABLE;
package/dist/index.d.cts CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as SeverityOrOff, a as FeatureStatus, c as Summary, d as ValidatorOptions, f as CATALOG, g as Severity, h as RuleDefinition, i as FeatureMatrix, l as ValidationLayer, m as RULES, n as CanonicalFeature, o as Report, p as Catalog, r as Diagnostic, s as SkippedRule, t as CANONICAL_FEATURES, u as Validator, v as formatMessage, y as validateCatalog } from "./types-oH_QTnn2.cjs";
2
- import { a as EVENT_TYPES, c as FieldKind, i as EVENT_TABLE, l as FieldSpec, n as applyPatch, o as EventCategory, r as validatePatchShape, s as EventSpec, t as createValidator, u as SDK_VERSION } from "./index-Hmqj3r_r.cjs";
1
+ import { _ as SeverityOrOff, a as FeatureStatus, c as Summary, d as ValidatorOptions, f as CATALOG, g as Severity, h as RuleDefinition, i as FeatureMatrix, l as ValidationLayer, m as RULES, n as CanonicalFeature, o as Report, p as Catalog, r as Diagnostic, s as SkippedRule, t as CANONICAL_FEATURES, u as Validator, v as formatMessage, y as validateCatalog } from "./types-CUIgyj2N.cjs";
2
+ import { a as EVENT_TYPES, c as FieldKind, i as EVENT_TABLE, l as FieldSpec, n as applyPatch, o as EventCategory, r as validatePatchShape, s as EventSpec, t as createValidator, u as SDK_VERSION } from "./index-DsVmjcLO.cjs";
3
3
  export { CANONICAL_FEATURES, CATALOG, CanonicalFeature, type Catalog, Diagnostic, EVENT_TABLE, EVENT_TYPES, type EventCategory, type EventSpec, FeatureMatrix, FeatureStatus, type FieldKind, type FieldSpec, RULES, Report, type RuleDefinition, type Severity as RuleSeverity, type Severity, SDK_VERSION, type SeverityOrOff, SkippedRule, Summary, ValidationLayer, Validator, ValidatorOptions, applyPatch, createValidator, formatMessage, validateCatalog, validatePatchShape };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as SeverityOrOff, a as FeatureStatus, c as Summary, d as ValidatorOptions, f as CATALOG, g as Severity, h as RuleDefinition, i as FeatureMatrix, l as ValidationLayer, m as RULES, n as CanonicalFeature, o as Report, p as Catalog, r as Diagnostic, s as SkippedRule, t as CANONICAL_FEATURES, u as Validator, v as formatMessage, y as validateCatalog } from "./types-oH_QTnn2.js";
2
- import { a as EVENT_TYPES, c as FieldKind, i as EVENT_TABLE, l as FieldSpec, n as applyPatch, o as EventCategory, r as validatePatchShape, s as EventSpec, t as createValidator, u as SDK_VERSION } from "./index-oNG1kOp9.js";
1
+ import { _ as SeverityOrOff, a as FeatureStatus, c as Summary, d as ValidatorOptions, f as CATALOG, g as Severity, h as RuleDefinition, i as FeatureMatrix, l as ValidationLayer, m as RULES, n as CanonicalFeature, o as Report, p as Catalog, r as Diagnostic, s as SkippedRule, t as CANONICAL_FEATURES, u as Validator, v as formatMessage, y as validateCatalog } from "./types-CUIgyj2N.js";
2
+ import { a as EVENT_TYPES, c as FieldKind, i as EVENT_TABLE, l as FieldSpec, n as applyPatch, o as EventCategory, r as validatePatchShape, s as EventSpec, t as createValidator, u as SDK_VERSION } from "./index-Cydfs-rB.js";
3
3
  export { CANONICAL_FEATURES, CATALOG, CanonicalFeature, type Catalog, Diagnostic, EVENT_TABLE, EVENT_TYPES, type EventCategory, type EventSpec, FeatureMatrix, FeatureStatus, type FieldKind, type FieldSpec, RULES, Report, type RuleDefinition, type Severity as RuleSeverity, type Severity, SDK_VERSION, type SeverityOrOff, SkippedRule, Summary, ValidationLayer, Validator, ValidatorOptions, applyPatch, createValidator, formatMessage, validateCatalog, validatePatchShape };
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { a as EVENT_TABLE, i as validatePatchShape, n as CANONICAL_FEATURES, o as EVENT_TYPES, r as applyPatch, s as SDK_VERSION, t as createValidator } from "./src-Dcuqz2QC.js";
2
- import { i as validateCatalog, n as RULES, r as formatMessage, t as CATALOG } from "./catalog-DqTI4VLt.js";
1
+ import { a as EVENT_TABLE, i as validatePatchShape, n as CANONICAL_FEATURES, o as EVENT_TYPES, r as applyPatch, s as SDK_VERSION, t as createValidator } from "./src-CezZ73Mm.js";
2
+ import { i as validateCatalog, n as RULES, r as formatMessage, t as CATALOG } from "./catalog-3e6INLCp.js";
3
3
  export { CANONICAL_FEATURES, CATALOG, EVENT_TABLE, EVENT_TYPES, RULES, SDK_VERSION, applyPatch, createValidator, formatMessage, validateCatalog, validatePatchShape };
package/dist/report.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_catalog = require("./catalog-AB5vuZ8j.cjs");
2
+ const require_catalog = require("./catalog-DH5gDIbo.cjs");
3
3
  //#region src/report/pretty.ts
4
4
  const SYMBOL = {
5
5
  error: "✖",
@@ -84,9 +84,17 @@ function formatReportSummary(report, opts) {
84
84
  //#endregion
85
85
  //#region src/report/json.ts
86
86
  function toJsonReport(report, opts) {
87
+ const diagnostics = report.diagnostics.map((d) => {
88
+ const category = require_catalog.RULES.get(d.rule)?.category;
89
+ return category === void 0 ? { ...d } : {
90
+ ...d,
91
+ category
92
+ };
93
+ });
87
94
  const doc = {
88
95
  tool: opts.tool,
89
- ...report
96
+ ...report,
97
+ diagnostics
90
98
  };
91
99
  if (opts.target !== void 0) doc.target = opts.target;
92
100
  return doc;
@@ -110,6 +118,7 @@ function toSarif(report, opts) {
110
118
  if (catalog !== void 0) {
111
119
  entry.shortDescription = { text: catalog.title };
112
120
  entry.defaultConfiguration = { level: LEVEL[catalog.severity] };
121
+ entry.properties = { tags: [catalog.category] };
113
122
  }
114
123
  rules.set(d.rule, entry);
115
124
  }
@@ -1 +1 @@
1
- {"version":3,"file":"report.cjs","names":["RULES","RULES"],"sources":["../js/src/report/pretty.ts","../js/src/report/json.ts","../js/src/report/sarif.ts","../js/src/report/junit.ts"],"sourcesContent":["// Human-readable output. Pure string formatting — the CLI decides where it\n// goes and whether a TTY wants color.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report, Severity } from \"../types.js\"\n\nexport interface PrettyOptions {\n color: boolean\n}\n\nconst SYMBOL: Record<Severity, string> = { error: \"✖\", warning: \"⚠\", info: \"ℹ\" }\nconst SGR: Record<Severity, string> = { error: \"31\", warning: \"33\", info: \"36\" }\n\nfunction paint(code: string, s: string, on: boolean): string {\n return on ? `\\x1b[${code}m${s}\\x1b[0m` : s\n}\n\nexport function formatDiagnosticLine(d: Diagnostic, opts: PrettyOptions): string {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"—\"\n const head = paint(SGR[d.severity], `${SYMBOL[d.severity]} ${d.rule}`, opts.color)\n const meta = paint(\"2\", `${d.severity.padEnd(7)} ${where.padEnd(10)}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${d.specUrl}`, opts.color)\n return `${head} ${meta} ${d.message}\\n${cite}`\n}\n\nfunction count(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`\n}\n\nconst SEVERITY_RANK: Record<Severity, number> = { error: 0, warning: 1, info: 2 }\nconst SAMPLE_INDEXES = 3\n\n/**\n * One line per rule instead of one per occurrence — for large streams where\n * the same violation repeats. Totals stay honest in the summary; this only\n * changes what is listed.\n */\nexport function formatGroupedDiagnostics(diagnostics: Diagnostic[], opts: PrettyOptions): string {\n const groups = new Map<string, Diagnostic[]>()\n for (const d of diagnostics) {\n const list = groups.get(d.rule)\n if (list === undefined) groups.set(d.rule, [d])\n else list.push(d)\n }\n\n const sorted = [...groups.values()].sort((a, b) => {\n const bySeverity = SEVERITY_RANK[a[0]!.severity] - SEVERITY_RANK[b[0]!.severity]\n return bySeverity !== 0 ? bySeverity : a[0]!.rule.localeCompare(b[0]!.rule)\n })\n\n const lines: string[] = []\n for (const group of sorted) {\n const first = group[0]!\n const title = RULES.get(first.rule)?.title ?? first.message\n const indexes = group.map((d) => d.eventIndex).filter((i) => i >= 0)\n let where: string\n if (indexes.length === 0) {\n where = \"stream-level\"\n } else {\n const sample = indexes.slice(0, SAMPLE_INDEXES).join(\", \")\n const rest = indexes.length - SAMPLE_INDEXES\n where = `events ${sample}${rest > 0 ? ` (+${rest} more)` : \"\"}`\n }\n const head = paint(SGR[first.severity], `${SYMBOL[first.severity]} ${first.rule}`, opts.color)\n const meta = paint(\"2\", `${first.severity.padEnd(7)} ×${group.length}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${first.specUrl}`, opts.color)\n lines.push(`${head} ${meta} ${title} — ${where}`, cite)\n }\n return lines.join(\"\\n\")\n}\n\nexport function formatReportSummary(report: Report, opts: PrettyOptions): string {\n const { errors, warnings, info } = report.summary\n const lines: string[] = []\n\n if (errors + warnings + info === 0) {\n lines.push(\n paint(\"32\", `✔ no conformance violations across ${count(report.eventCount, \"event\")}`, opts.color),\n )\n } else {\n lines.push(\n `${count(errors, \"error\")}, ${count(warnings, \"warning\")}, ${info} info across ${count(report.eventCount, \"event\")}`,\n )\n }\n\n const features = Object.entries(report.features)\n const exercised = features.filter(([, s]) => s === \"exercised\").map(([f]) => f)\n const suffix = exercised.length > 0 ? `: ${exercised.join(\", \")}` : \"\"\n lines.push(`${exercised.length} of ${features.length} AG-UI features exercised${suffix}`)\n\n if (report.skipped.length > 0) {\n lines.push(`${count(report.skipped.length, \"rule\")} not evaluated:`)\n for (const s of report.skipped) {\n lines.push(paint(\"2\", ` – ${s.rule}: ${s.reason}`, opts.color))\n }\n }\n\n for (const e of report.internalErrors) {\n lines.push(paint(\"31\", `! internal validator error: ${e}`, opts.color))\n }\n\n return lines.join(\"\\n\")\n}\n","// Machine-readable report: the core Report plus tool identification, so a\n// stored document is self-describing.\nimport type { Report } from \"../types.js\"\n\nexport interface JsonReportOptions {\n tool: { name: string; version: string }\n /** What was validated: URL, file path, or \"stdin\". */\n target?: string\n}\n\nexport interface JsonReportDocument extends Report {\n tool: { name: string; version: string }\n target?: string\n}\n\nexport function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument {\n const doc: JsonReportDocument = { tool: opts.tool, ...report }\n if (opts.target !== undefined) doc.target = opts.target\n return doc\n}\n","// SARIF 2.1.0 output for code-scanning integrations (e.g. GitHub).\n// Level mapping: error→error, warning→warning, info→note. When the input was a\n// line-oriented file (JSONL/captured SSE fed line-per-event is not guaranteed,\n// so only the caller knows), event N is reported at line N+1 via artifactUri.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface SarifOptions {\n toolVersion: string\n /** URI of the validated artifact; enables line-based locations. */\n artifactUri?: string\n}\n\nexport type SarifLevel = \"error\" | \"warning\" | \"note\"\n\nexport interface SarifResult {\n ruleId: string\n level: SarifLevel\n message: { text: string }\n locations: Array<{\n physicalLocation: {\n artifactLocation: { uri: string }\n region: { startLine: number }\n }\n }>\n}\n\nexport interface SarifLog {\n $schema: string\n version: \"2.1.0\"\n runs: Array<{\n tool: {\n driver: {\n name: string\n version: string\n informationUri: string\n rules: Array<{\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n }>\n }\n }\n results: SarifResult[]\n }>\n}\n\nconst LEVEL: Record<Diagnostic[\"severity\"], SarifLevel> = {\n error: \"error\",\n warning: \"warning\",\n info: \"note\",\n}\n\ntype SarifRule = {\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n}\n\nexport function toSarif(report: Report, opts: SarifOptions): SarifLog {\n const rules = new Map<string, SarifRule>()\n for (const d of report.diagnostics) {\n if (rules.has(d.rule)) continue\n const entry: SarifRule = { id: d.rule, helpUri: d.specUrl }\n const catalog = RULES.get(d.rule)\n if (catalog !== undefined) {\n entry.shortDescription = { text: catalog.title }\n entry.defaultConfiguration = { level: LEVEL[catalog.severity] }\n }\n rules.set(d.rule, entry)\n }\n\n const results: SarifResult[] = report.diagnostics.map((d) => ({\n ruleId: d.rule,\n level: LEVEL[d.severity],\n message: { text: d.message },\n locations:\n opts.artifactUri !== undefined && d.eventIndex >= 0\n ? [\n {\n physicalLocation: {\n artifactLocation: { uri: opts.artifactUri },\n region: { startLine: d.eventIndex + 1 },\n },\n },\n ]\n : [],\n }))\n\n return {\n $schema: \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"ag-ui-validate\",\n version: opts.toolVersion,\n informationUri: \"https://github.com/langport-dev/ag-ui-validate\",\n rules: [...rules.values()],\n },\n },\n results,\n },\n ],\n }\n}\n","// JUnit XML output for CI systems. One testcase per finding: errors are\n// <failure>, warnings and info are <skipped> (visible but not build-breaking —\n// the exit code, not the XML, decides pass/fail). A clean report is a single\n// passing testcase so the suite is never empty.\nimport type { Report } from \"../types.js\"\n\nexport interface JUnitOptions {\n /** Suite name — typically the validated target. */\n name: string\n}\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\")\n}\n\nexport function toJUnit(report: Report, opts: JUnitOptions): string {\n const lines: string[] = ['<?xml version=\"1.0\" encoding=\"UTF-8\"?>']\n const suite = esc(opts.name)\n\n if (report.diagnostics.length === 0) {\n lines.push('<testsuites name=\"ag-ui-validate\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">')\n lines.push(` <testsuite name=\"${suite}\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">`)\n lines.push(\n ` <testcase name=\"AG-UI conformance: no violations across ${report.eventCount} events\" classname=\"${suite}\"/>`,\n )\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n }\n\n const failures = report.diagnostics.filter((d) => d.severity === \"error\").length\n const skipped = report.diagnostics.length - failures\n const counts = `tests=\"${report.diagnostics.length}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\"`\n lines.push(`<testsuites name=\"ag-ui-validate\" ${counts}>`)\n lines.push(` <testsuite name=\"${suite}\" ${counts}>`)\n for (const d of report.diagnostics) {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"stream\"\n const name = esc(`${d.rule} (${where})`)\n lines.push(` <testcase name=\"${name}\" classname=\"${suite}\">`)\n const body = `${esc(d.message)}\\n${esc(d.specUrl)}`\n if (d.severity === \"error\") {\n lines.push(` <failure message=\"${esc(d.message)}\">${body}</failure>`)\n } else {\n lines.push(` <skipped message=\"${esc(`${d.severity}: ${d.message}`)}\">${body}</skipped>`)\n }\n lines.push(\" </testcase>\")\n }\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"report.cjs","names":["RULES","RULES","RULES"],"sources":["../js/src/report/pretty.ts","../js/src/report/json.ts","../js/src/report/sarif.ts","../js/src/report/junit.ts"],"sourcesContent":["// Human-readable output. Pure string formatting — the CLI decides where it\n// goes and whether a TTY wants color.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report, Severity } from \"../types.js\"\n\nexport interface PrettyOptions {\n color: boolean\n}\n\nconst SYMBOL: Record<Severity, string> = { error: \"✖\", warning: \"⚠\", info: \"ℹ\" }\nconst SGR: Record<Severity, string> = { error: \"31\", warning: \"33\", info: \"36\" }\n\nfunction paint(code: string, s: string, on: boolean): string {\n return on ? `\\x1b[${code}m${s}\\x1b[0m` : s\n}\n\nexport function formatDiagnosticLine(d: Diagnostic, opts: PrettyOptions): string {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"—\"\n const head = paint(SGR[d.severity], `${SYMBOL[d.severity]} ${d.rule}`, opts.color)\n const meta = paint(\"2\", `${d.severity.padEnd(7)} ${where.padEnd(10)}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${d.specUrl}`, opts.color)\n return `${head} ${meta} ${d.message}\\n${cite}`\n}\n\nfunction count(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`\n}\n\nconst SEVERITY_RANK: Record<Severity, number> = { error: 0, warning: 1, info: 2 }\nconst SAMPLE_INDEXES = 3\n\n/**\n * One line per rule instead of one per occurrence — for large streams where\n * the same violation repeats. Totals stay honest in the summary; this only\n * changes what is listed.\n */\nexport function formatGroupedDiagnostics(diagnostics: Diagnostic[], opts: PrettyOptions): string {\n const groups = new Map<string, Diagnostic[]>()\n for (const d of diagnostics) {\n const list = groups.get(d.rule)\n if (list === undefined) groups.set(d.rule, [d])\n else list.push(d)\n }\n\n const sorted = [...groups.values()].sort((a, b) => {\n const bySeverity = SEVERITY_RANK[a[0]!.severity] - SEVERITY_RANK[b[0]!.severity]\n return bySeverity !== 0 ? bySeverity : a[0]!.rule.localeCompare(b[0]!.rule)\n })\n\n const lines: string[] = []\n for (const group of sorted) {\n const first = group[0]!\n const title = RULES.get(first.rule)?.title ?? first.message\n const indexes = group.map((d) => d.eventIndex).filter((i) => i >= 0)\n let where: string\n if (indexes.length === 0) {\n where = \"stream-level\"\n } else {\n const sample = indexes.slice(0, SAMPLE_INDEXES).join(\", \")\n const rest = indexes.length - SAMPLE_INDEXES\n where = `events ${sample}${rest > 0 ? ` (+${rest} more)` : \"\"}`\n }\n const head = paint(SGR[first.severity], `${SYMBOL[first.severity]} ${first.rule}`, opts.color)\n const meta = paint(\"2\", `${first.severity.padEnd(7)} ×${group.length}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${first.specUrl}`, opts.color)\n lines.push(`${head} ${meta} ${title} — ${where}`, cite)\n }\n return lines.join(\"\\n\")\n}\n\nexport function formatReportSummary(report: Report, opts: PrettyOptions): string {\n const { errors, warnings, info } = report.summary\n const lines: string[] = []\n\n if (errors + warnings + info === 0) {\n lines.push(\n paint(\"32\", `✔ no conformance violations across ${count(report.eventCount, \"event\")}`, opts.color),\n )\n } else {\n lines.push(\n `${count(errors, \"error\")}, ${count(warnings, \"warning\")}, ${info} info across ${count(report.eventCount, \"event\")}`,\n )\n }\n\n const features = Object.entries(report.features)\n const exercised = features.filter(([, s]) => s === \"exercised\").map(([f]) => f)\n const suffix = exercised.length > 0 ? `: ${exercised.join(\", \")}` : \"\"\n lines.push(`${exercised.length} of ${features.length} AG-UI features exercised${suffix}`)\n\n if (report.skipped.length > 0) {\n lines.push(`${count(report.skipped.length, \"rule\")} not evaluated:`)\n for (const s of report.skipped) {\n lines.push(paint(\"2\", ` – ${s.rule}: ${s.reason}`, opts.color))\n }\n }\n\n for (const e of report.internalErrors) {\n lines.push(paint(\"31\", `! internal validator error: ${e}`, opts.color))\n }\n\n return lines.join(\"\\n\")\n}\n","// Machine-readable report: the core Report plus tool identification, so a\n// stored document is self-describing.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface JsonReportOptions {\n tool: { name: string; version: string }\n /** What was validated: URL, file path, or \"stdin\". */\n target?: string\n}\n\nexport interface JsonDiagnostic extends Diagnostic {\n /** The rule's catalog category, e.g. \"toolcall\". Joined in from the catalog by rule id. */\n category?: string\n}\n\nexport interface JsonReportDocument extends Omit<Report, \"diagnostics\"> {\n tool: { name: string; version: string }\n target?: string\n diagnostics: JsonDiagnostic[]\n}\n\nexport function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument {\n const diagnostics: JsonDiagnostic[] = report.diagnostics.map((d) => {\n const category = RULES.get(d.rule)?.category\n return category === undefined ? { ...d } : { ...d, category }\n })\n const doc: JsonReportDocument = { tool: opts.tool, ...report, diagnostics }\n if (opts.target !== undefined) doc.target = opts.target\n return doc\n}\n","// SARIF 2.1.0 output for code-scanning integrations (e.g. GitHub).\n// Level mapping: error→error, warning→warning, info→note. When the input was a\n// line-oriented file (JSONL/captured SSE fed line-per-event is not guaranteed,\n// so only the caller knows), event N is reported at line N+1 via artifactUri.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface SarifOptions {\n toolVersion: string\n /** URI of the validated artifact; enables line-based locations. */\n artifactUri?: string\n}\n\nexport type SarifLevel = \"error\" | \"warning\" | \"note\"\n\nexport interface SarifResult {\n ruleId: string\n level: SarifLevel\n message: { text: string }\n locations: Array<{\n physicalLocation: {\n artifactLocation: { uri: string }\n region: { startLine: number }\n }\n }>\n}\n\nexport interface SarifLog {\n $schema: string\n version: \"2.1.0\"\n runs: Array<{\n tool: {\n driver: {\n name: string\n version: string\n informationUri: string\n rules: SarifRule[]\n }\n }\n results: SarifResult[]\n }>\n}\n\nconst LEVEL: Record<Diagnostic[\"severity\"], SarifLevel> = {\n error: \"error\",\n warning: \"warning\",\n info: \"note\",\n}\n\ntype SarifRule = {\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n properties?: { tags: string[] }\n}\n\nexport function toSarif(report: Report, opts: SarifOptions): SarifLog {\n const rules = new Map<string, SarifRule>()\n for (const d of report.diagnostics) {\n if (rules.has(d.rule)) continue\n const entry: SarifRule = { id: d.rule, helpUri: d.specUrl }\n const catalog = RULES.get(d.rule)\n if (catalog !== undefined) {\n entry.shortDescription = { text: catalog.title }\n entry.defaultConfiguration = { level: LEVEL[catalog.severity] }\n entry.properties = { tags: [catalog.category] }\n }\n rules.set(d.rule, entry)\n }\n\n const results: SarifResult[] = report.diagnostics.map((d) => ({\n ruleId: d.rule,\n level: LEVEL[d.severity],\n message: { text: d.message },\n locations:\n opts.artifactUri !== undefined && d.eventIndex >= 0\n ? [\n {\n physicalLocation: {\n artifactLocation: { uri: opts.artifactUri },\n region: { startLine: d.eventIndex + 1 },\n },\n },\n ]\n : [],\n }))\n\n return {\n $schema: \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"ag-ui-validate\",\n version: opts.toolVersion,\n informationUri: \"https://github.com/langport-dev/ag-ui-validate\",\n rules: [...rules.values()],\n },\n },\n results,\n },\n ],\n }\n}\n","// JUnit XML output for CI systems. One testcase per finding: errors are\n// <failure>, warnings and info are <skipped> (visible but not build-breaking —\n// the exit code, not the XML, decides pass/fail). A clean report is a single\n// passing testcase so the suite is never empty.\nimport type { Report } from \"../types.js\"\n\nexport interface JUnitOptions {\n /** Suite name — typically the validated target. */\n name: string\n}\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\")\n}\n\nexport function toJUnit(report: Report, opts: JUnitOptions): string {\n const lines: string[] = ['<?xml version=\"1.0\" encoding=\"UTF-8\"?>']\n const suite = esc(opts.name)\n\n if (report.diagnostics.length === 0) {\n lines.push('<testsuites name=\"ag-ui-validate\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">')\n lines.push(` <testsuite name=\"${suite}\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">`)\n lines.push(\n ` <testcase name=\"AG-UI conformance: no violations across ${report.eventCount} events\" classname=\"${suite}\"/>`,\n )\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n }\n\n const failures = report.diagnostics.filter((d) => d.severity === \"error\").length\n const skipped = report.diagnostics.length - failures\n const counts = `tests=\"${report.diagnostics.length}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\"`\n lines.push(`<testsuites name=\"ag-ui-validate\" ${counts}>`)\n lines.push(` <testsuite name=\"${suite}\" ${counts}>`)\n for (const d of report.diagnostics) {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"stream\"\n const name = esc(`${d.rule} (${where})`)\n lines.push(` <testcase name=\"${name}\" classname=\"${suite}\">`)\n const body = `${esc(d.message)}\\n${esc(d.specUrl)}`\n if (d.severity === \"error\") {\n lines.push(` <failure message=\"${esc(d.message)}\">${body}</failure>`)\n } else {\n lines.push(` <skipped message=\"${esc(`${d.severity}: ${d.message}`)}\">${body}</skipped>`)\n }\n lines.push(\" </testcase>\")\n }\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n}\n"],"mappings":"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"}
package/dist/report.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { o as Report, r as Diagnostic } from "./types-oH_QTnn2.cjs";
1
+ import { o as Report, r as Diagnostic } from "./types-CUIgyj2N.cjs";
2
2
  //#region src/report/pretty.d.ts
3
3
  interface PrettyOptions {
4
4
  color: boolean;
@@ -21,12 +21,17 @@ interface JsonReportOptions {
21
21
  /** What was validated: URL, file path, or "stdin". */
22
22
  target?: string;
23
23
  }
24
- interface JsonReportDocument extends Report {
24
+ interface JsonDiagnostic extends Diagnostic {
25
+ /** The rule's catalog category, e.g. "toolcall". Joined in from the catalog by rule id. */
26
+ category?: string;
27
+ }
28
+ interface JsonReportDocument extends Omit<Report, "diagnostics"> {
25
29
  tool: {
26
30
  name: string;
27
31
  version: string;
28
32
  };
29
33
  target?: string;
34
+ diagnostics: JsonDiagnostic[];
30
35
  }
31
36
  declare function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument;
32
37
  //#endregion
@@ -63,21 +68,25 @@ interface SarifLog {
63
68
  name: string;
64
69
  version: string;
65
70
  informationUri: string;
66
- rules: Array<{
67
- id: string;
68
- helpUri: string;
69
- shortDescription?: {
70
- text: string;
71
- };
72
- defaultConfiguration?: {
73
- level: SarifLevel;
74
- };
75
- }>;
71
+ rules: SarifRule[];
76
72
  };
77
73
  };
78
74
  results: SarifResult[];
79
75
  }>;
80
76
  }
77
+ type SarifRule = {
78
+ id: string;
79
+ helpUri: string;
80
+ shortDescription?: {
81
+ text: string;
82
+ };
83
+ defaultConfiguration?: {
84
+ level: SarifLevel;
85
+ };
86
+ properties?: {
87
+ tags: string[];
88
+ };
89
+ };
81
90
  declare function toSarif(report: Report, opts: SarifOptions): SarifLog;
82
91
  //#endregion
83
92
  //#region src/report/junit.d.ts
package/dist/report.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { o as Report, r as Diagnostic } from "./types-oH_QTnn2.js";
1
+ import { o as Report, r as Diagnostic } from "./types-CUIgyj2N.js";
2
2
  //#region src/report/pretty.d.ts
3
3
  interface PrettyOptions {
4
4
  color: boolean;
@@ -21,12 +21,17 @@ interface JsonReportOptions {
21
21
  /** What was validated: URL, file path, or "stdin". */
22
22
  target?: string;
23
23
  }
24
- interface JsonReportDocument extends Report {
24
+ interface JsonDiagnostic extends Diagnostic {
25
+ /** The rule's catalog category, e.g. "toolcall". Joined in from the catalog by rule id. */
26
+ category?: string;
27
+ }
28
+ interface JsonReportDocument extends Omit<Report, "diagnostics"> {
25
29
  tool: {
26
30
  name: string;
27
31
  version: string;
28
32
  };
29
33
  target?: string;
34
+ diagnostics: JsonDiagnostic[];
30
35
  }
31
36
  declare function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument;
32
37
  //#endregion
@@ -63,21 +68,25 @@ interface SarifLog {
63
68
  name: string;
64
69
  version: string;
65
70
  informationUri: string;
66
- rules: Array<{
67
- id: string;
68
- helpUri: string;
69
- shortDescription?: {
70
- text: string;
71
- };
72
- defaultConfiguration?: {
73
- level: SarifLevel;
74
- };
75
- }>;
71
+ rules: SarifRule[];
76
72
  };
77
73
  };
78
74
  results: SarifResult[];
79
75
  }>;
80
76
  }
77
+ type SarifRule = {
78
+ id: string;
79
+ helpUri: string;
80
+ shortDescription?: {
81
+ text: string;
82
+ };
83
+ defaultConfiguration?: {
84
+ level: SarifLevel;
85
+ };
86
+ properties?: {
87
+ tags: string[];
88
+ };
89
+ };
81
90
  declare function toSarif(report: Report, opts: SarifOptions): SarifLog;
82
91
  //#endregion
83
92
  //#region src/report/junit.d.ts
package/dist/report.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as RULES } from "./catalog-DqTI4VLt.js";
1
+ import { n as RULES } from "./catalog-3e6INLCp.js";
2
2
  //#region src/report/pretty.ts
3
3
  const SYMBOL = {
4
4
  error: "✖",
@@ -83,9 +83,17 @@ function formatReportSummary(report, opts) {
83
83
  //#endregion
84
84
  //#region src/report/json.ts
85
85
  function toJsonReport(report, opts) {
86
+ const diagnostics = report.diagnostics.map((d) => {
87
+ const category = RULES.get(d.rule)?.category;
88
+ return category === void 0 ? { ...d } : {
89
+ ...d,
90
+ category
91
+ };
92
+ });
86
93
  const doc = {
87
94
  tool: opts.tool,
88
- ...report
95
+ ...report,
96
+ diagnostics
89
97
  };
90
98
  if (opts.target !== void 0) doc.target = opts.target;
91
99
  return doc;
@@ -109,6 +117,7 @@ function toSarif(report, opts) {
109
117
  if (catalog !== void 0) {
110
118
  entry.shortDescription = { text: catalog.title };
111
119
  entry.defaultConfiguration = { level: LEVEL[catalog.severity] };
120
+ entry.properties = { tags: [catalog.category] };
112
121
  }
113
122
  rules.set(d.rule, entry);
114
123
  }
@@ -1 +1 @@
1
- {"version":3,"file":"report.js","names":[],"sources":["../js/src/report/pretty.ts","../js/src/report/json.ts","../js/src/report/sarif.ts","../js/src/report/junit.ts"],"sourcesContent":["// Human-readable output. Pure string formatting — the CLI decides where it\n// goes and whether a TTY wants color.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report, Severity } from \"../types.js\"\n\nexport interface PrettyOptions {\n color: boolean\n}\n\nconst SYMBOL: Record<Severity, string> = { error: \"✖\", warning: \"⚠\", info: \"ℹ\" }\nconst SGR: Record<Severity, string> = { error: \"31\", warning: \"33\", info: \"36\" }\n\nfunction paint(code: string, s: string, on: boolean): string {\n return on ? `\\x1b[${code}m${s}\\x1b[0m` : s\n}\n\nexport function formatDiagnosticLine(d: Diagnostic, opts: PrettyOptions): string {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"—\"\n const head = paint(SGR[d.severity], `${SYMBOL[d.severity]} ${d.rule}`, opts.color)\n const meta = paint(\"2\", `${d.severity.padEnd(7)} ${where.padEnd(10)}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${d.specUrl}`, opts.color)\n return `${head} ${meta} ${d.message}\\n${cite}`\n}\n\nfunction count(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`\n}\n\nconst SEVERITY_RANK: Record<Severity, number> = { error: 0, warning: 1, info: 2 }\nconst SAMPLE_INDEXES = 3\n\n/**\n * One line per rule instead of one per occurrence — for large streams where\n * the same violation repeats. Totals stay honest in the summary; this only\n * changes what is listed.\n */\nexport function formatGroupedDiagnostics(diagnostics: Diagnostic[], opts: PrettyOptions): string {\n const groups = new Map<string, Diagnostic[]>()\n for (const d of diagnostics) {\n const list = groups.get(d.rule)\n if (list === undefined) groups.set(d.rule, [d])\n else list.push(d)\n }\n\n const sorted = [...groups.values()].sort((a, b) => {\n const bySeverity = SEVERITY_RANK[a[0]!.severity] - SEVERITY_RANK[b[0]!.severity]\n return bySeverity !== 0 ? bySeverity : a[0]!.rule.localeCompare(b[0]!.rule)\n })\n\n const lines: string[] = []\n for (const group of sorted) {\n const first = group[0]!\n const title = RULES.get(first.rule)?.title ?? first.message\n const indexes = group.map((d) => d.eventIndex).filter((i) => i >= 0)\n let where: string\n if (indexes.length === 0) {\n where = \"stream-level\"\n } else {\n const sample = indexes.slice(0, SAMPLE_INDEXES).join(\", \")\n const rest = indexes.length - SAMPLE_INDEXES\n where = `events ${sample}${rest > 0 ? ` (+${rest} more)` : \"\"}`\n }\n const head = paint(SGR[first.severity], `${SYMBOL[first.severity]} ${first.rule}`, opts.color)\n const meta = paint(\"2\", `${first.severity.padEnd(7)} ×${group.length}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${first.specUrl}`, opts.color)\n lines.push(`${head} ${meta} ${title} — ${where}`, cite)\n }\n return lines.join(\"\\n\")\n}\n\nexport function formatReportSummary(report: Report, opts: PrettyOptions): string {\n const { errors, warnings, info } = report.summary\n const lines: string[] = []\n\n if (errors + warnings + info === 0) {\n lines.push(\n paint(\"32\", `✔ no conformance violations across ${count(report.eventCount, \"event\")}`, opts.color),\n )\n } else {\n lines.push(\n `${count(errors, \"error\")}, ${count(warnings, \"warning\")}, ${info} info across ${count(report.eventCount, \"event\")}`,\n )\n }\n\n const features = Object.entries(report.features)\n const exercised = features.filter(([, s]) => s === \"exercised\").map(([f]) => f)\n const suffix = exercised.length > 0 ? `: ${exercised.join(\", \")}` : \"\"\n lines.push(`${exercised.length} of ${features.length} AG-UI features exercised${suffix}`)\n\n if (report.skipped.length > 0) {\n lines.push(`${count(report.skipped.length, \"rule\")} not evaluated:`)\n for (const s of report.skipped) {\n lines.push(paint(\"2\", ` – ${s.rule}: ${s.reason}`, opts.color))\n }\n }\n\n for (const e of report.internalErrors) {\n lines.push(paint(\"31\", `! internal validator error: ${e}`, opts.color))\n }\n\n return lines.join(\"\\n\")\n}\n","// Machine-readable report: the core Report plus tool identification, so a\n// stored document is self-describing.\nimport type { Report } from \"../types.js\"\n\nexport interface JsonReportOptions {\n tool: { name: string; version: string }\n /** What was validated: URL, file path, or \"stdin\". */\n target?: string\n}\n\nexport interface JsonReportDocument extends Report {\n tool: { name: string; version: string }\n target?: string\n}\n\nexport function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument {\n const doc: JsonReportDocument = { tool: opts.tool, ...report }\n if (opts.target !== undefined) doc.target = opts.target\n return doc\n}\n","// SARIF 2.1.0 output for code-scanning integrations (e.g. GitHub).\n// Level mapping: error→error, warning→warning, info→note. When the input was a\n// line-oriented file (JSONL/captured SSE fed line-per-event is not guaranteed,\n// so only the caller knows), event N is reported at line N+1 via artifactUri.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface SarifOptions {\n toolVersion: string\n /** URI of the validated artifact; enables line-based locations. */\n artifactUri?: string\n}\n\nexport type SarifLevel = \"error\" | \"warning\" | \"note\"\n\nexport interface SarifResult {\n ruleId: string\n level: SarifLevel\n message: { text: string }\n locations: Array<{\n physicalLocation: {\n artifactLocation: { uri: string }\n region: { startLine: number }\n }\n }>\n}\n\nexport interface SarifLog {\n $schema: string\n version: \"2.1.0\"\n runs: Array<{\n tool: {\n driver: {\n name: string\n version: string\n informationUri: string\n rules: Array<{\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n }>\n }\n }\n results: SarifResult[]\n }>\n}\n\nconst LEVEL: Record<Diagnostic[\"severity\"], SarifLevel> = {\n error: \"error\",\n warning: \"warning\",\n info: \"note\",\n}\n\ntype SarifRule = {\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n}\n\nexport function toSarif(report: Report, opts: SarifOptions): SarifLog {\n const rules = new Map<string, SarifRule>()\n for (const d of report.diagnostics) {\n if (rules.has(d.rule)) continue\n const entry: SarifRule = { id: d.rule, helpUri: d.specUrl }\n const catalog = RULES.get(d.rule)\n if (catalog !== undefined) {\n entry.shortDescription = { text: catalog.title }\n entry.defaultConfiguration = { level: LEVEL[catalog.severity] }\n }\n rules.set(d.rule, entry)\n }\n\n const results: SarifResult[] = report.diagnostics.map((d) => ({\n ruleId: d.rule,\n level: LEVEL[d.severity],\n message: { text: d.message },\n locations:\n opts.artifactUri !== undefined && d.eventIndex >= 0\n ? [\n {\n physicalLocation: {\n artifactLocation: { uri: opts.artifactUri },\n region: { startLine: d.eventIndex + 1 },\n },\n },\n ]\n : [],\n }))\n\n return {\n $schema: \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"ag-ui-validate\",\n version: opts.toolVersion,\n informationUri: \"https://github.com/langport-dev/ag-ui-validate\",\n rules: [...rules.values()],\n },\n },\n results,\n },\n ],\n }\n}\n","// JUnit XML output for CI systems. One testcase per finding: errors are\n// <failure>, warnings and info are <skipped> (visible but not build-breaking —\n// the exit code, not the XML, decides pass/fail). A clean report is a single\n// passing testcase so the suite is never empty.\nimport type { Report } from \"../types.js\"\n\nexport interface JUnitOptions {\n /** Suite name — typically the validated target. */\n name: string\n}\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\")\n}\n\nexport function toJUnit(report: Report, opts: JUnitOptions): string {\n const lines: string[] = ['<?xml version=\"1.0\" encoding=\"UTF-8\"?>']\n const suite = esc(opts.name)\n\n if (report.diagnostics.length === 0) {\n lines.push('<testsuites name=\"ag-ui-validate\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">')\n lines.push(` <testsuite name=\"${suite}\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">`)\n lines.push(\n ` <testcase name=\"AG-UI conformance: no violations across ${report.eventCount} events\" classname=\"${suite}\"/>`,\n )\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n }\n\n const failures = report.diagnostics.filter((d) => d.severity === \"error\").length\n const skipped = report.diagnostics.length - failures\n const counts = `tests=\"${report.diagnostics.length}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\"`\n lines.push(`<testsuites name=\"ag-ui-validate\" ${counts}>`)\n lines.push(` <testsuite name=\"${suite}\" ${counts}>`)\n for (const d of report.diagnostics) {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"stream\"\n const name = esc(`${d.rule} (${where})`)\n lines.push(` <testcase name=\"${name}\" classname=\"${suite}\">`)\n const body = `${esc(d.message)}\\n${esc(d.specUrl)}`\n if (d.severity === \"error\") {\n lines.push(` <failure message=\"${esc(d.message)}\">${body}</failure>`)\n } else {\n lines.push(` <skipped message=\"${esc(`${d.severity}: ${d.message}`)}\">${body}</skipped>`)\n }\n lines.push(\" </testcase>\")\n }\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"report.js","names":[],"sources":["../js/src/report/pretty.ts","../js/src/report/json.ts","../js/src/report/sarif.ts","../js/src/report/junit.ts"],"sourcesContent":["// Human-readable output. Pure string formatting — the CLI decides where it\n// goes and whether a TTY wants color.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report, Severity } from \"../types.js\"\n\nexport interface PrettyOptions {\n color: boolean\n}\n\nconst SYMBOL: Record<Severity, string> = { error: \"✖\", warning: \"⚠\", info: \"ℹ\" }\nconst SGR: Record<Severity, string> = { error: \"31\", warning: \"33\", info: \"36\" }\n\nfunction paint(code: string, s: string, on: boolean): string {\n return on ? `\\x1b[${code}m${s}\\x1b[0m` : s\n}\n\nexport function formatDiagnosticLine(d: Diagnostic, opts: PrettyOptions): string {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"—\"\n const head = paint(SGR[d.severity], `${SYMBOL[d.severity]} ${d.rule}`, opts.color)\n const meta = paint(\"2\", `${d.severity.padEnd(7)} ${where.padEnd(10)}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${d.specUrl}`, opts.color)\n return `${head} ${meta} ${d.message}\\n${cite}`\n}\n\nfunction count(n: number, noun: string): string {\n return `${n} ${noun}${n === 1 ? \"\" : \"s\"}`\n}\n\nconst SEVERITY_RANK: Record<Severity, number> = { error: 0, warning: 1, info: 2 }\nconst SAMPLE_INDEXES = 3\n\n/**\n * One line per rule instead of one per occurrence — for large streams where\n * the same violation repeats. Totals stay honest in the summary; this only\n * changes what is listed.\n */\nexport function formatGroupedDiagnostics(diagnostics: Diagnostic[], opts: PrettyOptions): string {\n const groups = new Map<string, Diagnostic[]>()\n for (const d of diagnostics) {\n const list = groups.get(d.rule)\n if (list === undefined) groups.set(d.rule, [d])\n else list.push(d)\n }\n\n const sorted = [...groups.values()].sort((a, b) => {\n const bySeverity = SEVERITY_RANK[a[0]!.severity] - SEVERITY_RANK[b[0]!.severity]\n return bySeverity !== 0 ? bySeverity : a[0]!.rule.localeCompare(b[0]!.rule)\n })\n\n const lines: string[] = []\n for (const group of sorted) {\n const first = group[0]!\n const title = RULES.get(first.rule)?.title ?? first.message\n const indexes = group.map((d) => d.eventIndex).filter((i) => i >= 0)\n let where: string\n if (indexes.length === 0) {\n where = \"stream-level\"\n } else {\n const sample = indexes.slice(0, SAMPLE_INDEXES).join(\", \")\n const rest = indexes.length - SAMPLE_INDEXES\n where = `events ${sample}${rest > 0 ? ` (+${rest} more)` : \"\"}`\n }\n const head = paint(SGR[first.severity], `${SYMBOL[first.severity]} ${first.rule}`, opts.color)\n const meta = paint(\"2\", `${first.severity.padEnd(7)} ×${group.length}`, opts.color)\n const cite = paint(\"2\", ` ↳ ${first.specUrl}`, opts.color)\n lines.push(`${head} ${meta} ${title} — ${where}`, cite)\n }\n return lines.join(\"\\n\")\n}\n\nexport function formatReportSummary(report: Report, opts: PrettyOptions): string {\n const { errors, warnings, info } = report.summary\n const lines: string[] = []\n\n if (errors + warnings + info === 0) {\n lines.push(\n paint(\"32\", `✔ no conformance violations across ${count(report.eventCount, \"event\")}`, opts.color),\n )\n } else {\n lines.push(\n `${count(errors, \"error\")}, ${count(warnings, \"warning\")}, ${info} info across ${count(report.eventCount, \"event\")}`,\n )\n }\n\n const features = Object.entries(report.features)\n const exercised = features.filter(([, s]) => s === \"exercised\").map(([f]) => f)\n const suffix = exercised.length > 0 ? `: ${exercised.join(\", \")}` : \"\"\n lines.push(`${exercised.length} of ${features.length} AG-UI features exercised${suffix}`)\n\n if (report.skipped.length > 0) {\n lines.push(`${count(report.skipped.length, \"rule\")} not evaluated:`)\n for (const s of report.skipped) {\n lines.push(paint(\"2\", ` – ${s.rule}: ${s.reason}`, opts.color))\n }\n }\n\n for (const e of report.internalErrors) {\n lines.push(paint(\"31\", `! internal validator error: ${e}`, opts.color))\n }\n\n return lines.join(\"\\n\")\n}\n","// Machine-readable report: the core Report plus tool identification, so a\n// stored document is self-describing.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface JsonReportOptions {\n tool: { name: string; version: string }\n /** What was validated: URL, file path, or \"stdin\". */\n target?: string\n}\n\nexport interface JsonDiagnostic extends Diagnostic {\n /** The rule's catalog category, e.g. \"toolcall\". Joined in from the catalog by rule id. */\n category?: string\n}\n\nexport interface JsonReportDocument extends Omit<Report, \"diagnostics\"> {\n tool: { name: string; version: string }\n target?: string\n diagnostics: JsonDiagnostic[]\n}\n\nexport function toJsonReport(report: Report, opts: JsonReportOptions): JsonReportDocument {\n const diagnostics: JsonDiagnostic[] = report.diagnostics.map((d) => {\n const category = RULES.get(d.rule)?.category\n return category === undefined ? { ...d } : { ...d, category }\n })\n const doc: JsonReportDocument = { tool: opts.tool, ...report, diagnostics }\n if (opts.target !== undefined) doc.target = opts.target\n return doc\n}\n","// SARIF 2.1.0 output for code-scanning integrations (e.g. GitHub).\n// Level mapping: error→error, warning→warning, info→note. When the input was a\n// line-oriented file (JSONL/captured SSE fed line-per-event is not guaranteed,\n// so only the caller knows), event N is reported at line N+1 via artifactUri.\nimport { RULES } from \"../rules/catalog.js\"\nimport type { Diagnostic, Report } from \"../types.js\"\n\nexport interface SarifOptions {\n toolVersion: string\n /** URI of the validated artifact; enables line-based locations. */\n artifactUri?: string\n}\n\nexport type SarifLevel = \"error\" | \"warning\" | \"note\"\n\nexport interface SarifResult {\n ruleId: string\n level: SarifLevel\n message: { text: string }\n locations: Array<{\n physicalLocation: {\n artifactLocation: { uri: string }\n region: { startLine: number }\n }\n }>\n}\n\nexport interface SarifLog {\n $schema: string\n version: \"2.1.0\"\n runs: Array<{\n tool: {\n driver: {\n name: string\n version: string\n informationUri: string\n rules: SarifRule[]\n }\n }\n results: SarifResult[]\n }>\n}\n\nconst LEVEL: Record<Diagnostic[\"severity\"], SarifLevel> = {\n error: \"error\",\n warning: \"warning\",\n info: \"note\",\n}\n\ntype SarifRule = {\n id: string\n helpUri: string\n shortDescription?: { text: string }\n defaultConfiguration?: { level: SarifLevel }\n properties?: { tags: string[] }\n}\n\nexport function toSarif(report: Report, opts: SarifOptions): SarifLog {\n const rules = new Map<string, SarifRule>()\n for (const d of report.diagnostics) {\n if (rules.has(d.rule)) continue\n const entry: SarifRule = { id: d.rule, helpUri: d.specUrl }\n const catalog = RULES.get(d.rule)\n if (catalog !== undefined) {\n entry.shortDescription = { text: catalog.title }\n entry.defaultConfiguration = { level: LEVEL[catalog.severity] }\n entry.properties = { tags: [catalog.category] }\n }\n rules.set(d.rule, entry)\n }\n\n const results: SarifResult[] = report.diagnostics.map((d) => ({\n ruleId: d.rule,\n level: LEVEL[d.severity],\n message: { text: d.message },\n locations:\n opts.artifactUri !== undefined && d.eventIndex >= 0\n ? [\n {\n physicalLocation: {\n artifactLocation: { uri: opts.artifactUri },\n region: { startLine: d.eventIndex + 1 },\n },\n },\n ]\n : [],\n }))\n\n return {\n $schema: \"https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/schemas/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"ag-ui-validate\",\n version: opts.toolVersion,\n informationUri: \"https://github.com/langport-dev/ag-ui-validate\",\n rules: [...rules.values()],\n },\n },\n results,\n },\n ],\n }\n}\n","// JUnit XML output for CI systems. One testcase per finding: errors are\n// <failure>, warnings and info are <skipped> (visible but not build-breaking —\n// the exit code, not the XML, decides pass/fail). A clean report is a single\n// passing testcase so the suite is never empty.\nimport type { Report } from \"../types.js\"\n\nexport interface JUnitOptions {\n /** Suite name — typically the validated target. */\n name: string\n}\n\nfunction esc(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\")\n}\n\nexport function toJUnit(report: Report, opts: JUnitOptions): string {\n const lines: string[] = ['<?xml version=\"1.0\" encoding=\"UTF-8\"?>']\n const suite = esc(opts.name)\n\n if (report.diagnostics.length === 0) {\n lines.push('<testsuites name=\"ag-ui-validate\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">')\n lines.push(` <testsuite name=\"${suite}\" tests=\"1\" failures=\"0\" errors=\"0\" skipped=\"0\">`)\n lines.push(\n ` <testcase name=\"AG-UI conformance: no violations across ${report.eventCount} events\" classname=\"${suite}\"/>`,\n )\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n }\n\n const failures = report.diagnostics.filter((d) => d.severity === \"error\").length\n const skipped = report.diagnostics.length - failures\n const counts = `tests=\"${report.diagnostics.length}\" failures=\"${failures}\" errors=\"0\" skipped=\"${skipped}\"`\n lines.push(`<testsuites name=\"ag-ui-validate\" ${counts}>`)\n lines.push(` <testsuite name=\"${suite}\" ${counts}>`)\n for (const d of report.diagnostics) {\n const where = d.eventIndex >= 0 ? `event ${d.eventIndex}` : \"stream\"\n const name = esc(`${d.rule} (${where})`)\n lines.push(` <testcase name=\"${name}\" classname=\"${suite}\">`)\n const body = `${esc(d.message)}\\n${esc(d.specUrl)}`\n if (d.severity === \"error\") {\n lines.push(` <failure message=\"${esc(d.message)}\">${body}</failure>`)\n } else {\n lines.push(` <skipped message=\"${esc(`${d.severity}: ${d.message}`)}\">${body}</skipped>`)\n }\n lines.push(\" </testcase>\")\n }\n lines.push(\" </testsuite>\")\n lines.push(\"</testsuites>\")\n return `${lines.join(\"\\n\")}\\n`\n}\n"],"mappings":"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"}