ag-ui-validate 0.1.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.
- package/LICENSE +21 -0
- package/README.md +229 -0
- package/dist/catalog-BglXBNbL.js +472 -0
- package/dist/catalog-BglXBNbL.js.map +1 -0
- package/dist/catalog-Ci9dqc1a.cjs +495 -0
- package/dist/catalog-Ci9dqc1a.cjs.map +1 -0
- package/dist/cli.js +2783 -0
- package/dist/cli.js.map +1 -0
- package/dist/index-Hmqj3r_r.d.cts +52 -0
- package/dist/index-oNG1kOp9.d.ts +52 -0
- package/dist/index.cjs +14 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/report.cjs +139 -0
- package/dist/report.cjs.map +1 -0
- package/dist/report.d.cts +85 -0
- package/dist/report.d.ts +85 -0
- package/dist/report.js +134 -0
- package/dist/report.js.map +1 -0
- package/dist/src-HmI-kxef.cjs +1596 -0
- package/dist/src-HmI-kxef.cjs.map +1 -0
- package/dist/src-rGZ2G4qA.js +1555 -0
- package/dist/src-rGZ2G4qA.js.map +1 -0
- package/dist/transport.cjs +329 -0
- package/dist/transport.cjs.map +1 -0
- package/dist/transport.d.cts +89 -0
- package/dist/transport.d.ts +89 -0
- package/dist/transport.js +323 -0
- package/dist/transport.js.map +1 -0
- package/dist/types-oH_QTnn2.d.cts +148 -0
- package/dist/types-oH_QTnn2.d.ts +148 -0
- package/dist/vitest.d.ts +28 -0
- package/dist/vitest.js +2089 -0
- package/dist/vitest.js.map +1 -0
- package/package.json +127 -0
- package/src/cli-args.ts +202 -0
- package/src/cli.ts +147 -0
- package/src/index.ts +465 -0
- package/src/protocol/event-table.ts +316 -0
- package/src/protocol/jsonpatch.ts +220 -0
- package/src/report/index.ts +10 -0
- package/src/report/json.ts +20 -0
- package/src/report/junit.ts +56 -0
- package/src/report/pretty.ts +59 -0
- package/src/report/sarif.ts +109 -0
- package/src/rules/catalog.json +431 -0
- package/src/rules/catalog.ts +84 -0
- package/src/rules/checks/context.ts +117 -0
- package/src/rules/checks/lifecycle.ts +59 -0
- package/src/rules/checks/reasoning.ts +97 -0
- package/src/rules/checks/state.ts +72 -0
- package/src/rules/checks/text.ts +109 -0
- package/src/rules/checks/toolcalls.ts +167 -0
- package/src/rules/checks/transport.ts +17 -0
- package/src/transport/index.ts +331 -0
- package/src/transport/ndjson.ts +25 -0
- package/src/transport/sse.ts +126 -0
- package/src/types.ts +136 -0
- package/src/vitest/index.ts +19 -0
- package/src/vitest/matcher.ts +77 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
//#region src/rules/catalog.d.ts
|
|
2
|
+
type Severity = "error" | "warning" | "info";
|
|
3
|
+
type SeverityOrOff = Severity | "off";
|
|
4
|
+
interface RuleDefinition {
|
|
5
|
+
/** e.g. "AGUI203" */
|
|
6
|
+
id: string;
|
|
7
|
+
severity: Severity;
|
|
8
|
+
title: string;
|
|
9
|
+
/** Human template with {placeholder} slots filled per diagnostic. */
|
|
10
|
+
messageTemplate: string;
|
|
11
|
+
/** Governing spec section. Mandatory: rules that cannot cite one don't ship. */
|
|
12
|
+
specUrl: string;
|
|
13
|
+
/** Exact sentence from the spec section, where one exists. */
|
|
14
|
+
specQuote?: string;
|
|
15
|
+
since: string;
|
|
16
|
+
/** Canonical AG-UI feature this rule relates to, if any. */
|
|
17
|
+
feature?: string;
|
|
18
|
+
/** True when the rule only fires if opts.features declares `feature`. */
|
|
19
|
+
requiresFeature?: boolean;
|
|
20
|
+
/** Cross-reference into docs/spec-questions.md for downgraded/ambiguous rules. */
|
|
21
|
+
specQuestion?: string;
|
|
22
|
+
/** Where the rule is evaluated. Transport rules are skipped (and the skip
|
|
23
|
+
* reported) when validating recorded input with no transport in play. */
|
|
24
|
+
checkedIn: "core" | "transport";
|
|
25
|
+
}
|
|
26
|
+
interface Catalog {
|
|
27
|
+
catalogVersion: string;
|
|
28
|
+
spec: string;
|
|
29
|
+
rules: readonly RuleDefinition[];
|
|
30
|
+
}
|
|
31
|
+
/** Validates catalog data and returns it typed. Throws on structural problems. */
|
|
32
|
+
declare function validateCatalog(data: unknown): Catalog;
|
|
33
|
+
declare const CATALOG: Catalog;
|
|
34
|
+
declare const RULES: ReadonlyMap<string, RuleDefinition>;
|
|
35
|
+
/** Fills a rule's messageTemplate. Unknown placeholders are left intact. */
|
|
36
|
+
declare function formatMessage(rule: RuleDefinition, params: Record<string, unknown>): string;
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/types.d.ts
|
|
39
|
+
/** A single conformance finding. */
|
|
40
|
+
interface Diagnostic {
|
|
41
|
+
/** Rule ID, e.g. "AGUI203". */
|
|
42
|
+
rule: string;
|
|
43
|
+
severity: Severity;
|
|
44
|
+
/** Human-readable, includes the offending id/value. */
|
|
45
|
+
message: string;
|
|
46
|
+
/**
|
|
47
|
+
* 0-based position in the stream of the event this diagnostic is about.
|
|
48
|
+
* Stream-level diagnostics (e.g. AGUI902) use -1.
|
|
49
|
+
*/
|
|
50
|
+
eventIndex: number;
|
|
51
|
+
/** The event's declared type, if parseable. */
|
|
52
|
+
eventType?: string;
|
|
53
|
+
/** RFC 6901 JSON pointer into the event, e.g. "/toolCallId". */
|
|
54
|
+
pointer?: string;
|
|
55
|
+
/** e.g. the unterminated TOOL_CALL_START this refers back to. */
|
|
56
|
+
relatedEventIndex?: number;
|
|
57
|
+
/** Link to the governing spec section. Always populated. */
|
|
58
|
+
specUrl: string;
|
|
59
|
+
}
|
|
60
|
+
/** The seven canonical AG-UI features (from the AG-UI Dojo). */
|
|
61
|
+
type CanonicalFeature = "agentic-chat" | "backend-tool-rendering" | "human-in-the-loop" | "agentic-generative-ui" | "tool-based-generative-ui" | "shared-state" | "predictive-state-updates";
|
|
62
|
+
declare const CANONICAL_FEATURES: readonly CanonicalFeature[];
|
|
63
|
+
/**
|
|
64
|
+
* Feature-matrix status. Capability discovery (getCapabilities()) is
|
|
65
|
+
* out-of-band and invisible to a passive stream observer, so this matrix is
|
|
66
|
+
* inferred from observed events; features whose exercise cannot be
|
|
67
|
+
* distinguished passively are "not-inferable". See docs/spec-questions.md SQ-13.
|
|
68
|
+
*/
|
|
69
|
+
type FeatureStatus = "exercised" | "not-exercised" | "not-inferable";
|
|
70
|
+
type FeatureMatrix = Record<CanonicalFeature, FeatureStatus>;
|
|
71
|
+
interface Summary {
|
|
72
|
+
errors: number;
|
|
73
|
+
warnings: number;
|
|
74
|
+
info: number;
|
|
75
|
+
}
|
|
76
|
+
interface SkippedRule {
|
|
77
|
+
rule: string;
|
|
78
|
+
reason: string;
|
|
79
|
+
}
|
|
80
|
+
interface Report {
|
|
81
|
+
diagnostics: Diagnostic[];
|
|
82
|
+
summary: Summary;
|
|
83
|
+
features: FeatureMatrix;
|
|
84
|
+
/**
|
|
85
|
+
* Rules that were not evaluated in this mode and why — e.g. transport rules
|
|
86
|
+
* when validating recorded input. Skips are reported, never silent.
|
|
87
|
+
*/
|
|
88
|
+
skipped: SkippedRule[];
|
|
89
|
+
eventCount: number;
|
|
90
|
+
/**
|
|
91
|
+
* Unexpected internal validator errors. The validator never throws on any
|
|
92
|
+
* input; if a check itself crashes, the message lands here instead.
|
|
93
|
+
*/
|
|
94
|
+
internalErrors: string[];
|
|
95
|
+
}
|
|
96
|
+
type ValidationLayer = "core" | "transport";
|
|
97
|
+
interface ValidatorOptions {
|
|
98
|
+
/** Pins the rule set to a spec version. Only "0.x" exists today. */
|
|
99
|
+
spec?: "0.x";
|
|
100
|
+
/**
|
|
101
|
+
* Declared features. Enables feature-conditional rules (e.g. AGUI305 fires
|
|
102
|
+
* only when "shared-state" is declared).
|
|
103
|
+
*/
|
|
104
|
+
features?: string[];
|
|
105
|
+
/** Per-rule severity overrides; "off" disables a rule. */
|
|
106
|
+
severityOverrides?: Record<string, SeverityOrOff>;
|
|
107
|
+
/**
|
|
108
|
+
* Which rule layers are being evaluated. "core" is always on. Wrapping
|
|
109
|
+
* layers that check transport rules (via emitExternal) declare "transport"
|
|
110
|
+
* so those rules stop being reported as skipped. Default: ["core"].
|
|
111
|
+
*/
|
|
112
|
+
layers?: ValidationLayer[];
|
|
113
|
+
}
|
|
114
|
+
interface Validator {
|
|
115
|
+
/**
|
|
116
|
+
* Feed one event — a parsed object, or a raw JSON string (malformed JSON is
|
|
117
|
+
* a diagnostic, not an exception). Returns diagnostics detectable at this
|
|
118
|
+
* event, in stream order. Never throws.
|
|
119
|
+
*/
|
|
120
|
+
feed(event: unknown): Diagnostic[];
|
|
121
|
+
/**
|
|
122
|
+
* End-of-stream checks (unterminated tool calls, missing RUN_FINISHED, …).
|
|
123
|
+
* Idempotent: second and later calls return []. Never throws.
|
|
124
|
+
*/
|
|
125
|
+
finalize(): Diagnostic[];
|
|
126
|
+
/** Cumulative report over everything fed so far. Never throws. */
|
|
127
|
+
report(): Report;
|
|
128
|
+
/**
|
|
129
|
+
* For wrapping layers (transport, CLI): report a layer-checked rule through
|
|
130
|
+
* the same catalog formatting, severity overrides, and summary as core
|
|
131
|
+
* diagnostics. Returns the diagnostic, or null when the rule is unknown
|
|
132
|
+
* (recorded in internalErrors) or overridden off. Never throws.
|
|
133
|
+
*/
|
|
134
|
+
emitExternal(rule: string, params?: Record<string, unknown>, extra?: {
|
|
135
|
+
eventIndex?: number;
|
|
136
|
+
pointer?: string;
|
|
137
|
+
relatedEventIndex?: number;
|
|
138
|
+
}): Diagnostic | null;
|
|
139
|
+
/**
|
|
140
|
+
* For wrapping layers: declare that a rule was NOT evaluated and why (e.g.
|
|
141
|
+
* timing rules on recorded input). The entry appears in report().skipped,
|
|
142
|
+
* replacing any layer-computed entry for the same rule. Never throws.
|
|
143
|
+
*/
|
|
144
|
+
markSkipped(rule: string, reason: string): void;
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
export { SeverityOrOff as _, FeatureStatus as a, Summary as c, ValidatorOptions as d, CATALOG as f, Severity as g, RuleDefinition as h, FeatureMatrix as i, ValidationLayer as l, RULES as m, CanonicalFeature as n, Report as o, Catalog as p, Diagnostic as r, SkippedRule as s, CANONICAL_FEATURES as t, Validator as u, formatMessage as v, validateCatalog as y };
|
|
148
|
+
//# sourceMappingURL=types-oH_QTnn2.d.cts.map
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
//#region src/rules/catalog.d.ts
|
|
2
|
+
type Severity = "error" | "warning" | "info";
|
|
3
|
+
type SeverityOrOff = Severity | "off";
|
|
4
|
+
interface RuleDefinition {
|
|
5
|
+
/** e.g. "AGUI203" */
|
|
6
|
+
id: string;
|
|
7
|
+
severity: Severity;
|
|
8
|
+
title: string;
|
|
9
|
+
/** Human template with {placeholder} slots filled per diagnostic. */
|
|
10
|
+
messageTemplate: string;
|
|
11
|
+
/** Governing spec section. Mandatory: rules that cannot cite one don't ship. */
|
|
12
|
+
specUrl: string;
|
|
13
|
+
/** Exact sentence from the spec section, where one exists. */
|
|
14
|
+
specQuote?: string;
|
|
15
|
+
since: string;
|
|
16
|
+
/** Canonical AG-UI feature this rule relates to, if any. */
|
|
17
|
+
feature?: string;
|
|
18
|
+
/** True when the rule only fires if opts.features declares `feature`. */
|
|
19
|
+
requiresFeature?: boolean;
|
|
20
|
+
/** Cross-reference into docs/spec-questions.md for downgraded/ambiguous rules. */
|
|
21
|
+
specQuestion?: string;
|
|
22
|
+
/** Where the rule is evaluated. Transport rules are skipped (and the skip
|
|
23
|
+
* reported) when validating recorded input with no transport in play. */
|
|
24
|
+
checkedIn: "core" | "transport";
|
|
25
|
+
}
|
|
26
|
+
interface Catalog {
|
|
27
|
+
catalogVersion: string;
|
|
28
|
+
spec: string;
|
|
29
|
+
rules: readonly RuleDefinition[];
|
|
30
|
+
}
|
|
31
|
+
/** Validates catalog data and returns it typed. Throws on structural problems. */
|
|
32
|
+
declare function validateCatalog(data: unknown): Catalog;
|
|
33
|
+
declare const CATALOG: Catalog;
|
|
34
|
+
declare const RULES: ReadonlyMap<string, RuleDefinition>;
|
|
35
|
+
/** Fills a rule's messageTemplate. Unknown placeholders are left intact. */
|
|
36
|
+
declare function formatMessage(rule: RuleDefinition, params: Record<string, unknown>): string;
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/types.d.ts
|
|
39
|
+
/** A single conformance finding. */
|
|
40
|
+
interface Diagnostic {
|
|
41
|
+
/** Rule ID, e.g. "AGUI203". */
|
|
42
|
+
rule: string;
|
|
43
|
+
severity: Severity;
|
|
44
|
+
/** Human-readable, includes the offending id/value. */
|
|
45
|
+
message: string;
|
|
46
|
+
/**
|
|
47
|
+
* 0-based position in the stream of the event this diagnostic is about.
|
|
48
|
+
* Stream-level diagnostics (e.g. AGUI902) use -1.
|
|
49
|
+
*/
|
|
50
|
+
eventIndex: number;
|
|
51
|
+
/** The event's declared type, if parseable. */
|
|
52
|
+
eventType?: string;
|
|
53
|
+
/** RFC 6901 JSON pointer into the event, e.g. "/toolCallId". */
|
|
54
|
+
pointer?: string;
|
|
55
|
+
/** e.g. the unterminated TOOL_CALL_START this refers back to. */
|
|
56
|
+
relatedEventIndex?: number;
|
|
57
|
+
/** Link to the governing spec section. Always populated. */
|
|
58
|
+
specUrl: string;
|
|
59
|
+
}
|
|
60
|
+
/** The seven canonical AG-UI features (from the AG-UI Dojo). */
|
|
61
|
+
type CanonicalFeature = "agentic-chat" | "backend-tool-rendering" | "human-in-the-loop" | "agentic-generative-ui" | "tool-based-generative-ui" | "shared-state" | "predictive-state-updates";
|
|
62
|
+
declare const CANONICAL_FEATURES: readonly CanonicalFeature[];
|
|
63
|
+
/**
|
|
64
|
+
* Feature-matrix status. Capability discovery (getCapabilities()) is
|
|
65
|
+
* out-of-band and invisible to a passive stream observer, so this matrix is
|
|
66
|
+
* inferred from observed events; features whose exercise cannot be
|
|
67
|
+
* distinguished passively are "not-inferable". See docs/spec-questions.md SQ-13.
|
|
68
|
+
*/
|
|
69
|
+
type FeatureStatus = "exercised" | "not-exercised" | "not-inferable";
|
|
70
|
+
type FeatureMatrix = Record<CanonicalFeature, FeatureStatus>;
|
|
71
|
+
interface Summary {
|
|
72
|
+
errors: number;
|
|
73
|
+
warnings: number;
|
|
74
|
+
info: number;
|
|
75
|
+
}
|
|
76
|
+
interface SkippedRule {
|
|
77
|
+
rule: string;
|
|
78
|
+
reason: string;
|
|
79
|
+
}
|
|
80
|
+
interface Report {
|
|
81
|
+
diagnostics: Diagnostic[];
|
|
82
|
+
summary: Summary;
|
|
83
|
+
features: FeatureMatrix;
|
|
84
|
+
/**
|
|
85
|
+
* Rules that were not evaluated in this mode and why — e.g. transport rules
|
|
86
|
+
* when validating recorded input. Skips are reported, never silent.
|
|
87
|
+
*/
|
|
88
|
+
skipped: SkippedRule[];
|
|
89
|
+
eventCount: number;
|
|
90
|
+
/**
|
|
91
|
+
* Unexpected internal validator errors. The validator never throws on any
|
|
92
|
+
* input; if a check itself crashes, the message lands here instead.
|
|
93
|
+
*/
|
|
94
|
+
internalErrors: string[];
|
|
95
|
+
}
|
|
96
|
+
type ValidationLayer = "core" | "transport";
|
|
97
|
+
interface ValidatorOptions {
|
|
98
|
+
/** Pins the rule set to a spec version. Only "0.x" exists today. */
|
|
99
|
+
spec?: "0.x";
|
|
100
|
+
/**
|
|
101
|
+
* Declared features. Enables feature-conditional rules (e.g. AGUI305 fires
|
|
102
|
+
* only when "shared-state" is declared).
|
|
103
|
+
*/
|
|
104
|
+
features?: string[];
|
|
105
|
+
/** Per-rule severity overrides; "off" disables a rule. */
|
|
106
|
+
severityOverrides?: Record<string, SeverityOrOff>;
|
|
107
|
+
/**
|
|
108
|
+
* Which rule layers are being evaluated. "core" is always on. Wrapping
|
|
109
|
+
* layers that check transport rules (via emitExternal) declare "transport"
|
|
110
|
+
* so those rules stop being reported as skipped. Default: ["core"].
|
|
111
|
+
*/
|
|
112
|
+
layers?: ValidationLayer[];
|
|
113
|
+
}
|
|
114
|
+
interface Validator {
|
|
115
|
+
/**
|
|
116
|
+
* Feed one event — a parsed object, or a raw JSON string (malformed JSON is
|
|
117
|
+
* a diagnostic, not an exception). Returns diagnostics detectable at this
|
|
118
|
+
* event, in stream order. Never throws.
|
|
119
|
+
*/
|
|
120
|
+
feed(event: unknown): Diagnostic[];
|
|
121
|
+
/**
|
|
122
|
+
* End-of-stream checks (unterminated tool calls, missing RUN_FINISHED, …).
|
|
123
|
+
* Idempotent: second and later calls return []. Never throws.
|
|
124
|
+
*/
|
|
125
|
+
finalize(): Diagnostic[];
|
|
126
|
+
/** Cumulative report over everything fed so far. Never throws. */
|
|
127
|
+
report(): Report;
|
|
128
|
+
/**
|
|
129
|
+
* For wrapping layers (transport, CLI): report a layer-checked rule through
|
|
130
|
+
* the same catalog formatting, severity overrides, and summary as core
|
|
131
|
+
* diagnostics. Returns the diagnostic, or null when the rule is unknown
|
|
132
|
+
* (recorded in internalErrors) or overridden off. Never throws.
|
|
133
|
+
*/
|
|
134
|
+
emitExternal(rule: string, params?: Record<string, unknown>, extra?: {
|
|
135
|
+
eventIndex?: number;
|
|
136
|
+
pointer?: string;
|
|
137
|
+
relatedEventIndex?: number;
|
|
138
|
+
}): Diagnostic | null;
|
|
139
|
+
/**
|
|
140
|
+
* For wrapping layers: declare that a rule was NOT evaluated and why (e.g.
|
|
141
|
+
* timing rules on recorded input). The entry appears in report().skipped,
|
|
142
|
+
* replacing any layer-computed entry for the same rule. Never throws.
|
|
143
|
+
*/
|
|
144
|
+
markSkipped(rule: string, reason: string): void;
|
|
145
|
+
}
|
|
146
|
+
//#endregion
|
|
147
|
+
export { SeverityOrOff as _, FeatureStatus as a, Summary as c, ValidatorOptions as d, CATALOG as f, Severity as g, RuleDefinition as h, FeatureMatrix as i, ValidationLayer as l, RULES as m, CanonicalFeature as n, Report as o, Catalog as p, Diagnostic as r, SkippedRule as s, CANONICAL_FEATURES as t, Validator as u, formatMessage as v, validateCatalog as y };
|
|
148
|
+
//# sourceMappingURL=types-oH_QTnn2.d.ts.map
|
package/dist/vitest.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/rules/catalog.d.ts
|
|
2
|
+
type Severity = "error" | "warning" | "info";
|
|
3
|
+
type SeverityOrOff = Severity | "off";
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region src/vitest/matcher.d.ts
|
|
6
|
+
interface ToBeValidAGUIOptions {
|
|
7
|
+
/** Declared features (enables feature-conditional rules, e.g. AGUI305). */
|
|
8
|
+
features?: string[];
|
|
9
|
+
/** Per-rule severity overrides; "off" disables a rule. */
|
|
10
|
+
severityOverrides?: Record<string, SeverityOrOff>;
|
|
11
|
+
/** Fail when warning-severity findings exceed this budget. */
|
|
12
|
+
maxWarnings?: number;
|
|
13
|
+
}
|
|
14
|
+
interface MatcherResult {
|
|
15
|
+
pass: boolean;
|
|
16
|
+
message: () => string;
|
|
17
|
+
}
|
|
18
|
+
declare function toBeValidAGUI(received: unknown, options?: ToBeValidAGUIOptions): MatcherResult;
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/vitest/index.d.ts
|
|
21
|
+
declare module "vitest" {
|
|
22
|
+
interface Matchers<T = any> {
|
|
23
|
+
toBeValidAGUI(options?: ToBeValidAGUIOptions): T;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { type MatcherResult, type ToBeValidAGUIOptions, toBeValidAGUI };
|
|
28
|
+
//# sourceMappingURL=vitest.d.ts.map
|