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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +229 -0
  3. package/dist/catalog-BglXBNbL.js +472 -0
  4. package/dist/catalog-BglXBNbL.js.map +1 -0
  5. package/dist/catalog-Ci9dqc1a.cjs +495 -0
  6. package/dist/catalog-Ci9dqc1a.cjs.map +1 -0
  7. package/dist/cli.js +2783 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/index-Hmqj3r_r.d.cts +52 -0
  10. package/dist/index-oNG1kOp9.d.ts +52 -0
  11. package/dist/index.cjs +14 -0
  12. package/dist/index.d.cts +3 -0
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.js +3 -0
  15. package/dist/report.cjs +139 -0
  16. package/dist/report.cjs.map +1 -0
  17. package/dist/report.d.cts +85 -0
  18. package/dist/report.d.ts +85 -0
  19. package/dist/report.js +134 -0
  20. package/dist/report.js.map +1 -0
  21. package/dist/src-HmI-kxef.cjs +1596 -0
  22. package/dist/src-HmI-kxef.cjs.map +1 -0
  23. package/dist/src-rGZ2G4qA.js +1555 -0
  24. package/dist/src-rGZ2G4qA.js.map +1 -0
  25. package/dist/transport.cjs +329 -0
  26. package/dist/transport.cjs.map +1 -0
  27. package/dist/transport.d.cts +89 -0
  28. package/dist/transport.d.ts +89 -0
  29. package/dist/transport.js +323 -0
  30. package/dist/transport.js.map +1 -0
  31. package/dist/types-oH_QTnn2.d.cts +148 -0
  32. package/dist/types-oH_QTnn2.d.ts +148 -0
  33. package/dist/vitest.d.ts +28 -0
  34. package/dist/vitest.js +2089 -0
  35. package/dist/vitest.js.map +1 -0
  36. package/package.json +127 -0
  37. package/src/cli-args.ts +202 -0
  38. package/src/cli.ts +147 -0
  39. package/src/index.ts +465 -0
  40. package/src/protocol/event-table.ts +316 -0
  41. package/src/protocol/jsonpatch.ts +220 -0
  42. package/src/report/index.ts +10 -0
  43. package/src/report/json.ts +20 -0
  44. package/src/report/junit.ts +56 -0
  45. package/src/report/pretty.ts +59 -0
  46. package/src/report/sarif.ts +109 -0
  47. package/src/rules/catalog.json +431 -0
  48. package/src/rules/catalog.ts +84 -0
  49. package/src/rules/checks/context.ts +117 -0
  50. package/src/rules/checks/lifecycle.ts +59 -0
  51. package/src/rules/checks/reasoning.ts +97 -0
  52. package/src/rules/checks/state.ts +72 -0
  53. package/src/rules/checks/text.ts +109 -0
  54. package/src/rules/checks/toolcalls.ts +167 -0
  55. package/src/rules/checks/transport.ts +17 -0
  56. package/src/transport/index.ts +331 -0
  57. package/src/transport/ndjson.ts +25 -0
  58. package/src/transport/sse.ts +126 -0
  59. package/src/types.ts +136 -0
  60. package/src/vitest/index.ts +19 -0
  61. package/src/vitest/matcher.ts +77 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Faraz (langport.dev)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # ag-ui-validate
2
+
3
+ Conformance validator for the [AG-UI protocol](https://docs.ag-ui.com)
4
+ (Agent–User Interaction Protocol). Point it at an AG-UI endpoint — or feed it
5
+ a recorded event stream — and it reports every way the stream violates the
6
+ protocol, with a rule ID, a severity, a location, and a link to the governing
7
+ spec section.
8
+
9
+ ```
10
+ ✖ AGUI203 error event 42 TOOL_CALL_START id 'call_7' never terminated
11
+ ✖ AGUI302 error event 51 STATE_DELTA failed to apply: /items/3: '3' is not a valid index for an array of length 0
12
+ ✖ AGUI503 error event 60 Unknown event type 'runStarted' — did you mean 'RUN_STARTED'?
13
+ ℹ AGUI902 info — None of the 61 events carry the optional timestamp property
14
+
15
+ 2 errors, 0 warnings, 1 info — 3 of 7 AG-UI features exercised
16
+ ```
17
+
18
+ > **Status: pre-release.** The core validator, the language-neutral fixture
19
+ > corpus, and the transport layer are implemented and tested. The CLI
20
+ > (`npx ag-ui-validate <url|->`), the Vitest matcher, and SARIF/JUnit
21
+ > reporters are in progress.
22
+
23
+ ## Why
24
+
25
+ AG-UI has SDKs and integrations, but no conformance tooling: nothing tells an
26
+ implementer *your stream is subtly wrong, here's the rule and the spec
27
+ section*. This project is that tool — the AG-UI analogue of what
28
+ [a2a-inspector](https://github.com/a2aproject/a2a-inspector) is for A2A.
29
+
30
+ Three design commitments make it trustworthy:
31
+
32
+ - **Every diagnostic cites the spec.** Each of the 40 rules carries a
33
+ `specUrl` (and where possible an exact `specQuote`) pointing at the
34
+ governing section of [docs.ag-ui.com](https://docs.ag-ui.com) or the WHATWG
35
+ SSE spec. Behaviour the spec doesn't clearly govern is reported at `info`
36
+ severity at most, and logged in
37
+ [docs/spec-questions.md](docs/spec-questions.md) for filing upstream.
38
+ - **The validator never throws.** Broken input is its input. Malformed JSON,
39
+ unknown event types, hostile objects — all diagnostics, never exceptions
40
+ (fuzz-tested against 50k hostile inputs).
41
+ - **False positives are treated as worse than false negatives.** The rules are
42
+ grounded in `@ag-ui/core` v0.0.58 and the current docs; where the two
43
+ disagree, the SDK wins and the discrepancy is recorded.
44
+
45
+ ## Quickstart
46
+
47
+ ```bash
48
+ npm install --save-dev ag-ui-validate
49
+ ```
50
+
51
+ ### CLI
52
+
53
+ ```bash
54
+ npx ag-ui-validate http://localhost:8000/agui # live endpoint (POSTs a RunAgentInput)
55
+ npx ag-ui-validate run.jsonl # recorded stream (NDJSON/JSONL or SSE capture)
56
+ cat run.jsonl | npx ag-ui-validate - # stdin
57
+ ```
58
+
59
+ Exit codes: `0` clean, `1` findings at error level (or warnings over
60
+ `--max-warnings`), `2` tool failure. Timing-based transport rules are
61
+ meaningless for recordings, so they are reported as *skipped with a reason*
62
+ rather than risking false positives.
63
+
64
+ Useful flags (see `--help` for all):
65
+
66
+ | Flag | Effect |
67
+ | --- | --- |
68
+ | `--json` / `--sarif` / `--junit` | machine-readable report on stdout (SARIF 2.1.0 for code scanning, JUnit XML for CI) |
69
+ | `--rule AGUI105=error`, `--off AGUI902` | per-rule severity overrides |
70
+ | `--features shared-state,...` | declare exercised features (enables e.g. AGUI305) |
71
+ | `--max-warnings 0` | fail CI on any warning |
72
+ | `--header "Authorization: Bearer …"`, `--timeout 30` | endpoint options |
73
+
74
+ ### Validate in CI (GitHub Action)
75
+
76
+ ```yaml
77
+ - uses: langport-dev/ag-ui-validate/action@main
78
+ with:
79
+ target: http://localhost:8000/agui # or a recorded .jsonl file
80
+ sarif-file: agui.sarif # optional: upload via codeql-action
81
+ ```
82
+
83
+ The step fails on error-severity findings, writes a findings table to the job
84
+ summary, and exposes `errors`/`warnings`/`info` outputs — see
85
+ [action/README.md](action/README.md).
86
+
87
+ ### Test your agent in Vitest
88
+
89
+ ```ts
90
+ import "ag-ui-validate/vitest" // registers the matcher (put it in setupFiles)
91
+
92
+ it("streams a conformant run", async () => {
93
+ const events = await captureRunEvents(myAgent) // however you record them
94
+ expect(events).toBeValidAGUI()
95
+ })
96
+ ```
97
+
98
+ The matcher takes an array of events (objects or JSON strings) or a whole
99
+ JSONL capture as one string. Failures print each finding with its rule ID and
100
+ spec link. Options mirror the validator:
101
+ `{ features, severityOverrides, maxWarnings }` — e.g.
102
+ `expect(events).toBeValidAGUI({ maxWarnings: 0 })` to fail on warnings too.
103
+ The raw matcher function is also exported, so Jest users can
104
+ `expect.extend({ toBeValidAGUI })` themselves.
105
+
106
+ ### Validate recorded events (pure, runs anywhere)
107
+
108
+ ```ts
109
+ import { createValidator } from "ag-ui-validate"
110
+
111
+ const v = createValidator({
112
+ features: ["shared-state"], // optional: enables feature-specific rules
113
+ severityOverrides: { AGUI902: "off" }, // optional: tune or disable rules
114
+ })
115
+
116
+ for (const event of events) {
117
+ // feed parsed objects or raw JSON strings — bad JSON is a diagnostic
118
+ const diagnostics = v.feed(event) // findings, as soon as detectable
119
+ }
120
+ v.finalize() // end-of-stream checks
121
+
122
+ const { diagnostics, summary, features, skipped } = v.report()
123
+ ```
124
+
125
+ The core is a pure function over an event sequence: zero I/O, zero runtime
126
+ dependencies, isomorphic across Node 20+, browsers, Deno, and Workers.
127
+
128
+ ### Validate a live endpoint
129
+
130
+ ```ts
131
+ import { validateEndpoint } from "ag-ui-validate/transport"
132
+
133
+ const { report, status, eventCount } = await validateEndpoint(
134
+ "http://localhost:8000/agui",
135
+ {
136
+ headers: { authorization: "Bearer …" },
137
+ onDiagnostic: (d) => console.error(`${d.severity} ${d.rule} ${d.message}`),
138
+ },
139
+ )
140
+ ```
141
+
142
+ The transport layer POSTs a minimal `RunAgentInput`, consumes the SSE or
143
+ NDJSON response, streams every frame through the core, and additionally
144
+ evaluates the transport-level rules that recorded input can't exercise: SSE
145
+ framing (including the classic missing-`data:`-prefix bug), Content-Type,
146
+ keepalive gaps, buffered-not-flushed responses, and mid-run disconnects.
147
+
148
+ ### Render a report
149
+
150
+ The CLI's output formats are plain functions over a `Report`, importable for
151
+ your own tooling:
152
+
153
+ ```ts
154
+ import { formatReportSummary, toSarif, toJUnit } from "ag-ui-validate/report"
155
+ ```
156
+
157
+ ### Diagnostic shape
158
+
159
+ ```jsonc
160
+ {
161
+ "rule": "AGUI203",
162
+ "severity": "error", // "error" | "warning" | "info"
163
+ "message": "TOOL_CALL_START id 'call_7' never terminated",
164
+ "eventIndex": 42, // 0-based; -1 for end-of-stream findings
165
+ "eventType": "RUN_FINISHED", // optional
166
+ "pointer": "/toolCallId", // optional RFC 6901 pointer into the event
167
+ "relatedEventIndex": 17, // optional, e.g. the unterminated start
168
+ "specUrl": "https://docs.ag-ui.com/concepts/events#tool-call-events"
169
+ }
170
+ ```
171
+
172
+ ## The rule catalog
173
+
174
+ 40 rules, maintained as **data** in
175
+ [src/rules/catalog.json](src/rules/catalog.json) so other implementations
176
+ (Python, Go, …) can share them. Every rule has its own page — spec grounding,
177
+ severity, and a violating example from the corpus — in the
178
+ **[rule index](docs/rules/README.md)** (generated from the catalog,
179
+ drift-checked in CI):
180
+
181
+ | Group | IDs | Examples |
182
+ |---|---|---|
183
+ | Lifecycle | AGUI001–008 | run must start with `RUN_STARTED`, terminate with `RUN_FINISHED`/`RUN_ERROR`, nothing after a terminal event |
184
+ | Text messages | AGUI101–106 | content without start, unterminated messages, duplicate `messageId` |
185
+ | Tool calls | AGUI201–208 | unterminated calls, args that don't concatenate to valid JSON, results referencing unknown calls |
186
+ | State | AGUI301–305 | RFC 6902 patch validity, deltas that fail to apply to reconstructed state |
187
+ | Reasoning | AGUI401–402 | reasoning content without an open reasoning message |
188
+ | Transport | AGUI501–508 | SSE framing, Content-Type, keepalive gaps, buffering, dropped connections |
189
+ | Hygiene | AGUI901–903 | `RAW`-wrapping typed events, missing timestamps, un-namespaced `CUSTOM` names |
190
+
191
+ The event taxonomy itself (33 wire types, field schemas) is derived from
192
+ [`@ag-ui/core`](https://www.npmjs.com/package/@ag-ui/core)'s own schemas and
193
+ drift-tested against the installed SDK on every run.
194
+
195
+ ## The fixture corpus
196
+
197
+ [fixtures/](fixtures/README.md) is a language-neutral conformance corpus:
198
+ 7 valid streams (one per canonical AG-UI feature — the false-positive guards)
199
+ and 40 invalid fixtures (one per rule) with exact expected diagnostics. Any
200
+ validator implementation that consumes the shared catalog can be tested
201
+ against it; the replay protocol is documented in the corpus README.
202
+
203
+ ## Development
204
+
205
+ ```bash
206
+ npm ci
207
+ npm run typecheck # includes a src-only pass proving the core uses no Node APIs
208
+ npm run build # dual ESM/CJS via tsdown
209
+ npm test # full suite: unit + corpus + drift + purity + SDK alignment
210
+ npm run demo # pretty-printed findings for a deliberately broken stream
211
+ npm run e2e # live-transport checks against a real local HTTP server
212
+ npm run fuzz # 50k hostile inputs against the never-throws invariant
213
+ npm run links:check # every specUrl resolves and every anchor exists
214
+ ```
215
+
216
+ Component-by-component instructions live in
217
+ [docs/TESTING.md](docs/TESTING.md). Spec ambiguities found while grounding the
218
+ rules are tracked in [docs/spec-questions.md](docs/spec-questions.md).
219
+
220
+ Adding a rule: add the catalog entry (with its `specUrl`), add the fixture
221
+ stream + intended findings to `scripts/build-fixtures.mjs`, and run
222
+ `npm run fixtures:build` — the meta-tests fail until both exist. Rule
223
+ *proposals* belong upstream as issues on
224
+ [`ag-ui-protocol/ag-ui`](https://github.com/ag-ui-protocol/ag-ui) first; this
225
+ project does not invent rules the spec doesn't support.
226
+
227
+ ## License
228
+
229
+ MIT — maintained by [Faraz](https://langport.dev).