ag-ui-validate 0.2.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.
- package/README.md +82 -23
- package/dist/{catalog-BglXBNbL.js → catalog-3e6INLCp.js} +122 -4
- package/dist/catalog-3e6INLCp.js.map +1 -0
- package/dist/{catalog-Ci9dqc1a.cjs → catalog-DH5gDIbo.cjs} +122 -4
- package/dist/catalog-DH5gDIbo.cjs.map +1 -0
- package/dist/cli.js +641 -63
- package/dist/cli.js.map +1 -1
- package/dist/{index-Hmqj3r_r.d.cts → index-Cydfs-rB.d.ts} +4 -4
- package/dist/{index-oNG1kOp9.d.ts → index-DsVmjcLO.d.cts} +4 -4
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/report.cjs +11 -2
- package/dist/report.cjs.map +1 -1
- package/dist/report.d.cts +21 -12
- package/dist/report.d.ts +21 -12
- package/dist/report.js +11 -2
- package/dist/report.js.map +1 -1
- package/dist/{src-rGZ2G4qA.js → src-CezZ73Mm.js} +494 -59
- package/dist/src-CezZ73Mm.js.map +1 -0
- package/dist/{src-HmI-kxef.cjs → src-CyugLoks.cjs} +494 -59
- package/dist/src-CyugLoks.cjs.map +1 -0
- package/dist/transport.cjs +1 -1
- package/dist/transport.cjs.map +1 -1
- package/dist/transport.d.cts +1 -1
- package/dist/transport.d.ts +1 -1
- package/dist/transport.js +1 -1
- package/dist/transport.js.map +1 -1
- package/dist/{types-oH_QTnn2.d.ts → types-CUIgyj2N.d.cts} +3 -1
- package/dist/{types-oH_QTnn2.d.cts → types-CUIgyj2N.d.ts} +3 -1
- package/dist/vitest.js +616 -61
- package/dist/vitest.js.map +1 -1
- package/{src → js/src}/cli-args.ts +16 -0
- package/{src → js/src}/cli.ts +1 -1
- package/{src → js/src}/index.ts +5 -0
- package/{src → js/src}/protocol/event-table.ts +93 -2
- package/js/src/report/json.ts +31 -0
- package/{src → js/src}/report/sarif.ts +3 -6
- package/{src → js/src}/rules/catalog.ts +5 -1
- package/{src → js/src}/rules/checks/context.ts +48 -3
- package/{src → js/src}/rules/checks/lifecycle.ts +5 -2
- package/js/src/rules/checks/subagents.ts +78 -0
- package/{src → js/src}/rules/checks/text.ts +11 -3
- package/{src → js/src}/rules/checks/toolcalls.ts +8 -2
- package/{src → js/src}/transport/index.ts +1 -1
- package/package.json +15 -14
- package/{src/rules → spec}/catalog.json +109 -2
- package/spec/event-categories.json +60 -0
- package/spec/fixtures/README.md +91 -0
- package/spec/fixtures/invalid/AGUI001-first-event-not-run-started/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI001-first-event-not-run-started/stream.jsonl +4 -0
- package/spec/fixtures/invalid/AGUI002-duplicate-run-started/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI002-duplicate-run-started/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI003-run-never-terminated/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI003-run-never-terminated/stream.jsonl +4 -0
- package/spec/fixtures/invalid/AGUI004-event-after-terminal/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI004-event-after-terminal/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI005-finished-and-error/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI005-finished-and-error/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI006-step-finished-unmatched/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI006-step-finished-unmatched/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI007-step-unterminated/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI007-step-unterminated/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI008-unstable-run-ids/expected.json +12 -0
- package/spec/fixtures/invalid/AGUI008-unstable-run-ids/stream.jsonl +2 -0
- package/spec/fixtures/invalid/AGUI101-content-without-start/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI101-content-without-start/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI102-end-without-start/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI102-end-without-start/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI103-message-unterminated/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI103-message-unterminated/stream.jsonl +4 -0
- package/spec/fixtures/invalid/AGUI104-duplicate-message-id/expected.json +12 -0
- package/spec/fixtures/invalid/AGUI104-duplicate-message-id/stream.jsonl +6 -0
- package/spec/fixtures/invalid/AGUI105-empty-content-delta/expected.json +12 -0
- package/spec/fixtures/invalid/AGUI105-empty-content-delta/stream.jsonl +5 -0
- package/spec/fixtures/invalid/AGUI106-interleaved-same-message-id/expected.json +12 -0
- package/spec/fixtures/invalid/AGUI106-interleaved-same-message-id/stream.jsonl +5 -0
- package/spec/fixtures/invalid/AGUI201-args-without-start/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI201-args-without-start/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI202-end-without-start/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI202-end-without-start/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI203-unterminated-tool-call/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI203-unterminated-tool-call/stream.jsonl +4 -0
- package/spec/fixtures/invalid/AGUI204-args-not-json/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI204-args-not-json/stream.jsonl +6 -0
- package/spec/fixtures/invalid/AGUI205-duplicate-tool-call-id/expected.json +12 -0
- package/spec/fixtures/invalid/AGUI205-duplicate-tool-call-id/stream.jsonl +5 -0
- package/spec/fixtures/invalid/AGUI206-result-before-end/expected.json +12 -0
- package/spec/fixtures/invalid/AGUI206-result-before-end/stream.jsonl +5 -0
- package/spec/fixtures/invalid/AGUI207-result-unknown-id/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI207-result-unknown-id/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI208-unknown-parent-message-id/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI208-unknown-parent-message-id/stream.jsonl +4 -0
- package/spec/fixtures/invalid/AGUI301-delta-before-snapshot/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI301-delta-before-snapshot/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI302-delta-failed-to-apply/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI302-delta-failed-to-apply/stream.jsonl +4 -0
- package/spec/fixtures/invalid/AGUI303-invalid-patch-document/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI303-invalid-patch-document/stream.jsonl +4 -0
- package/spec/fixtures/invalid/AGUI304-midrun-snapshot-discards-deltas/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI304-midrun-snapshot-discards-deltas/stream.jsonl +5 -0
- package/spec/fixtures/invalid/AGUI305-shared-state-never-established/expected.json +9 -0
- package/spec/fixtures/invalid/AGUI305-shared-state-never-established/options.json +5 -0
- package/spec/fixtures/invalid/AGUI305-shared-state-never-established/stream.jsonl +5 -0
- package/spec/fixtures/invalid/AGUI401-reasoning-content-without-start/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI401-reasoning-content-without-start/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI402-reasoning-unterminated/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI402-reasoning-unterminated/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI501-missing-data-prefix/expected.json +9 -0
- package/spec/fixtures/invalid/AGUI501-missing-data-prefix/scenario.json +17 -0
- package/spec/fixtures/invalid/AGUI502-payload-not-json/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI502-payload-not-json/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI503-unknown-event-type/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI503-unknown-event-type/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI504-schema-violation/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI504-schema-violation/stream.jsonl +4 -0
- package/spec/fixtures/invalid/AGUI505-unexpected-content-type/expected.json +9 -0
- package/spec/fixtures/invalid/AGUI505-unexpected-content-type/scenario.json +25 -0
- package/spec/fixtures/invalid/AGUI506-keepalive-gap/expected.json +9 -0
- package/spec/fixtures/invalid/AGUI506-keepalive-gap/scenario.json +25 -0
- package/spec/fixtures/invalid/AGUI507-buffered-response/expected.json +9 -0
- package/spec/fixtures/invalid/AGUI507-buffered-response/scenario.json +9 -0
- package/spec/fixtures/invalid/AGUI508-connection-dropped/expected.json +17 -0
- package/spec/fixtures/invalid/AGUI508-connection-dropped/scenario.json +10 -0
- package/spec/fixtures/invalid/AGUI601-duplicate-subagent-started/expected.json +12 -0
- package/spec/fixtures/invalid/AGUI601-duplicate-subagent-started/stream.jsonl +5 -0
- package/spec/fixtures/invalid/AGUI602-finished-without-start/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI602-finished-without-start/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI603-error-without-start/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI603-error-without-start/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI604-subagent-unterminated/expected.json +10 -0
- package/spec/fixtures/invalid/AGUI604-subagent-unterminated/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI605-unknown-parent-subagent-run-id/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI605-unknown-parent-subagent-run-id/stream.jsonl +4 -0
- package/spec/fixtures/invalid/AGUI606-continuation-owner-mismatch/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI606-continuation-owner-mismatch/stream.jsonl +6 -0
- package/spec/fixtures/invalid/AGUI901-raw-wraps-typed-event/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI901-raw-wraps-typed-event/stream.jsonl +3 -0
- package/spec/fixtures/invalid/AGUI902-no-timestamps/expected.json +9 -0
- package/spec/fixtures/invalid/AGUI902-no-timestamps/stream.jsonl +2 -0
- package/spec/fixtures/invalid/AGUI903-custom-name-not-namespaced/expected.json +11 -0
- package/spec/fixtures/invalid/AGUI903-custom-name-not-namespaced/stream.jsonl +3 -0
- package/spec/fixtures/valid/agentic-chat.jsonl +14 -0
- package/spec/fixtures/valid/agentic-generative-ui.jsonl +13 -0
- package/spec/fixtures/valid/backend-tool-rendering.jsonl +14 -0
- package/spec/fixtures/valid/human-in-the-loop.jsonl +13 -0
- package/spec/fixtures/valid/predictive-state-updates.expected.json +11 -0
- package/spec/fixtures/valid/predictive-state-updates.jsonl +11 -0
- package/spec/fixtures/valid/shared-state.jsonl +8 -0
- package/spec/fixtures/valid/tool-based-generative-ui.jsonl +5 -0
- package/dist/catalog-BglXBNbL.js.map +0 -1
- package/dist/catalog-Ci9dqc1a.cjs.map +0 -1
- package/dist/src-HmI-kxef.cjs.map +0 -1
- package/dist/src-rGZ2G4qA.js.map +0 -1
- package/src/report/json.ts +0 -20
- /package/{src → js/src}/protocol/jsonpatch.ts +0 -0
- /package/{src → js/src}/report/index.ts +0 -0
- /package/{src → js/src}/report/junit.ts +0 -0
- /package/{src → js/src}/report/pretty.ts +0 -0
- /package/{src → js/src}/rules/checks/reasoning.ts +0 -0
- /package/{src → js/src}/rules/checks/state.ts +0 -0
- /package/{src → js/src}/rules/checks/transport.ts +0 -0
- /package/{src → js/src}/transport/ndjson.ts +0 -0
- /package/{src → js/src}/transport/sse.ts +0 -0
- /package/{src → js/src}/types.ts +0 -0
- /package/{src → js/src}/vitest/index.ts +0 -0
- /package/{src → js/src}/vitest/matcher.ts +0 -0
package/README.md
CHANGED
|
@@ -15,10 +15,10 @@ spec section.
|
|
|
15
15
|
2 errors, 0 warnings, 1 info — 3 of 7 AG-UI features exercised
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
> **Status
|
|
19
|
-
>
|
|
20
|
-
>
|
|
21
|
-
>
|
|
18
|
+
> **Status:** released to both npm (`npm install ag-ui-validate`) and PyPI
|
|
19
|
+
> (`pip install ag-ui-validate`). Both packages version in lockstep — same
|
|
20
|
+
> rule catalog, same fixture corpus, byte-identical CLI; see
|
|
21
|
+
> [Python](#python) below.
|
|
22
22
|
|
|
23
23
|
## Why
|
|
24
24
|
|
|
@@ -29,7 +29,7 @@ section*. This project is that tool — the AG-UI analogue of what
|
|
|
29
29
|
|
|
30
30
|
Three design commitments make it trustworthy:
|
|
31
31
|
|
|
32
|
-
- **Every diagnostic cites the spec.** Each of the
|
|
32
|
+
- **Every diagnostic cites the spec.** Each of the 46 rules carries a
|
|
33
33
|
`specUrl` (and where possible an exact `specQuote`) pointing at the
|
|
34
34
|
governing section of [docs.ag-ui.com](https://docs.ag-ui.com) or the WHATWG
|
|
35
35
|
SSE spec. Behaviour the spec doesn't clearly govern is reported at `info`
|
|
@@ -39,7 +39,7 @@ Three design commitments make it trustworthy:
|
|
|
39
39
|
unknown event types, hostile objects — all diagnostics, never exceptions
|
|
40
40
|
(fuzz-tested against 50k hostile inputs).
|
|
41
41
|
- **False positives are treated as worse than false negatives.** The rules are
|
|
42
|
-
grounded in `@ag-ui/core` v0.0.
|
|
42
|
+
grounded in `@ag-ui/core` v0.0.59 and the current docs; where the two
|
|
43
43
|
disagree, the SDK wins and the discrepancy is recorded.
|
|
44
44
|
|
|
45
45
|
## Quickstart
|
|
@@ -56,10 +56,10 @@ npx ag-ui-validate run.jsonl # recorded stream (NDJSON/JSONL
|
|
|
56
56
|
cat run.jsonl | npx ag-ui-validate - # stdin
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Exit codes: `0` clean, `1` findings at
|
|
60
|
-
`--max-warnings`), `2` tool failure.
|
|
61
|
-
meaningless for recordings, so they are
|
|
62
|
-
rather than risking false positives.
|
|
59
|
+
Exit codes: `0` clean, `1` findings at or above the `--fail-on` threshold
|
|
60
|
+
(`error` by default, or warnings over `--max-warnings`), `2` tool failure.
|
|
61
|
+
Timing-based transport rules are meaningless for recordings, so they are
|
|
62
|
+
reported as *skipped with a reason* rather than risking false positives.
|
|
63
63
|
|
|
64
64
|
Useful flags (see `--help` for all):
|
|
65
65
|
|
|
@@ -70,20 +70,23 @@ Useful flags (see `--help` for all):
|
|
|
70
70
|
| `--rule AGUI105=error`, `--off AGUI902` | per-rule severity overrides |
|
|
71
71
|
| `--features shared-state,...` | declare exercised features (enables e.g. AGUI305) |
|
|
72
72
|
| `--max-warnings 0` | fail CI on any warning |
|
|
73
|
+
| `--fail-on <error\|warning\|none>` | severity that triggers a nonzero exit (default `error`; `none` for report-only runs) |
|
|
73
74
|
| `--header "Authorization: Bearer …"`, `--timeout 30` | endpoint options |
|
|
74
75
|
|
|
75
76
|
### Validate in CI (GitHub Action)
|
|
76
77
|
|
|
77
78
|
```yaml
|
|
78
|
-
- uses: langport-dev/ag-ui-validate
|
|
79
|
+
- uses: langport-dev/ag-ui-validate-action@v1
|
|
79
80
|
with:
|
|
80
81
|
target: http://localhost:8000/agui # or a recorded .jsonl file
|
|
81
82
|
sarif-file: agui.sarif # optional: upload via codeql-action
|
|
82
83
|
```
|
|
83
84
|
|
|
84
|
-
The step fails on error-severity findings, writes a findings table
|
|
85
|
-
summary,
|
|
86
|
-
|
|
85
|
+
The step fails on error-severity findings, writes a findings table (with a
|
|
86
|
+
counts-by-category breakdown) to the job summary, annotates the PR inline
|
|
87
|
+
with `::error`/`::warning` for each finding, and exposes
|
|
88
|
+
`errors`/`warnings`/`info`/`sarif-path`/`report-path` outputs — see
|
|
89
|
+
[langport-dev/ag-ui-validate-action](https://github.com/langport-dev/ag-ui-validate-action).
|
|
87
90
|
|
|
88
91
|
### Test your agent in Vitest
|
|
89
92
|
|
|
@@ -124,7 +127,7 @@ const { diagnostics, summary, features, skipped } = v.report()
|
|
|
124
127
|
```
|
|
125
128
|
|
|
126
129
|
The core is a pure function over an event sequence: zero I/O, zero runtime
|
|
127
|
-
dependencies, isomorphic across Node
|
|
130
|
+
dependencies, isomorphic across Node 22+, browsers, Deno, and Workers.
|
|
128
131
|
|
|
129
132
|
### Validate a live endpoint
|
|
130
133
|
|
|
@@ -170,12 +173,67 @@ import { formatReportSummary, toSarif, toJUnit } from "ag-ui-validate/report"
|
|
|
170
173
|
}
|
|
171
174
|
```
|
|
172
175
|
|
|
176
|
+
## Python
|
|
177
|
+
|
|
178
|
+
A native Python port ships in [py/](py/) — the same rule catalog, the same
|
|
179
|
+
fixture corpus, and a CLI with byte-identical flags, error messages, and
|
|
180
|
+
JSON/SARIF/JUnit output to the TypeScript one above. It's checked against
|
|
181
|
+
the TypeScript implementation on every PR by Parity CI (see
|
|
182
|
+
[docs/TESTING.md](docs/TESTING.md) for how, and
|
|
183
|
+
[docs/PYTHON-PORT-PLAN.md](docs/PYTHON-PORT-PLAN.md) for the port's full
|
|
184
|
+
milestone history).
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
pip install ag-ui-validate # or "ag-ui-validate[transport]" for just the endpoint-validating extras
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### CLI
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
ag-ui-validate http://localhost:8000/agui # live endpoint (POSTs a RunAgentInput)
|
|
194
|
+
ag-ui-validate run.jsonl # recorded stream (NDJSON/JSONL or SSE capture)
|
|
195
|
+
cat run.jsonl | ag-ui-validate - # stdin
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Same exit codes and the same flags as the [CLI](#cli) above (see
|
|
199
|
+
`ag-ui-validate --help`) — the Python argument parser is a direct,
|
|
200
|
+
hand-rolled port of the TypeScript one rather than `argparse`/`click`,
|
|
201
|
+
specifically to keep both CLIs' invocation and error messages
|
|
202
|
+
byte-for-byte identical.
|
|
203
|
+
|
|
204
|
+
### Test your agent in pytest
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from ag_ui_validate.pytest_plugin import assert_valid_agui
|
|
208
|
+
|
|
209
|
+
def test_my_agent_stream(captured_events):
|
|
210
|
+
assert_valid_agui(captured_events, features=["shared-state"], max_warnings=0)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`assert_valid_agui` accepts a list of events (dicts or JSON strings) or a
|
|
214
|
+
whole JSONL capture as one string, and raises `AssertionError` with every
|
|
215
|
+
finding's rule ID and spec link on failure. It registers automatically as a
|
|
216
|
+
pytest plugin on install — no `conftest.py` setup needed. An async
|
|
217
|
+
counterpart validates a live endpoint directly from a test:
|
|
218
|
+
|
|
219
|
+
```python
|
|
220
|
+
from ag_ui_validate.pytest_plugin import assert_valid_agui_endpoint
|
|
221
|
+
|
|
222
|
+
async def test_my_live_agent():
|
|
223
|
+
await assert_valid_agui_endpoint("http://localhost:8000/agui", max_warnings=0)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
`assert_valid_agui` has zero runtime dependencies; only the
|
|
227
|
+
endpoint-validating helpers (`assert_valid_agui_endpoint`,
|
|
228
|
+
`validate_agui_endpoint`) pull in `httpx`
|
|
229
|
+
(`pip install ag-ui-validate[transport]`).
|
|
230
|
+
|
|
173
231
|
## The rule catalog
|
|
174
232
|
|
|
175
|
-
|
|
176
|
-
[
|
|
177
|
-
(
|
|
178
|
-
|
|
233
|
+
46 rules, maintained as **data** in
|
|
234
|
+
[spec/catalog.json](spec/catalog.json), shared by both implementations in
|
|
235
|
+
this repo ([TypeScript](.) and [Python](py/)) rather than duplicated.
|
|
236
|
+
Every rule has its own page — spec grounding,
|
|
179
237
|
**[rule index](docs/rules/README.md)** (generated from the catalog,
|
|
180
238
|
drift-checked in CI):
|
|
181
239
|
|
|
@@ -187,17 +245,18 @@ drift-checked in CI):
|
|
|
187
245
|
| State | AGUI301–305 | RFC 6902 patch validity, deltas that fail to apply to reconstructed state |
|
|
188
246
|
| Reasoning | AGUI401–402 | reasoning content without an open reasoning message |
|
|
189
247
|
| Transport | AGUI501–508 | SSE framing, Content-Type, keepalive gaps, buffering, dropped connections |
|
|
248
|
+
| Subagents | AGUI601–606 | duplicate/unmatched `SUBAGENT_STARTED`/`FINISHED`/`ERROR`, unterminated subagents, unknown `parentSubagentRunId`, continuation events that disagree with their entity's owner |
|
|
190
249
|
| Hygiene | AGUI901–903 | `RAW`-wrapping typed events, missing timestamps, un-namespaced `CUSTOM` names |
|
|
191
250
|
|
|
192
|
-
The event taxonomy itself (
|
|
251
|
+
The event taxonomy itself (36 wire types, field schemas) is derived from
|
|
193
252
|
[`@ag-ui/core`](https://www.npmjs.com/package/@ag-ui/core)'s own schemas and
|
|
194
253
|
drift-tested against the installed SDK on every run.
|
|
195
254
|
|
|
196
255
|
## The fixture corpus
|
|
197
256
|
|
|
198
|
-
[fixtures/](fixtures/README.md) is a language-neutral conformance corpus:
|
|
257
|
+
[spec/fixtures/](spec/fixtures/README.md) is a language-neutral conformance corpus:
|
|
199
258
|
7 valid streams (one per canonical AG-UI feature — the false-positive guards)
|
|
200
|
-
and
|
|
259
|
+
and 46 invalid fixtures (one per rule) with exact expected diagnostics. Any
|
|
201
260
|
validator implementation that consumes the shared catalog can be tested
|
|
202
261
|
against it; the replay protocol is documented in the corpus README.
|
|
203
262
|
|
|
@@ -219,7 +278,7 @@ Component-by-component instructions live in
|
|
|
219
278
|
rules are tracked in [docs/spec-questions.md](docs/spec-questions.md).
|
|
220
279
|
|
|
221
280
|
Adding a rule: add the catalog entry (with its `specUrl`), add the fixture
|
|
222
|
-
stream + intended findings to `scripts/build-fixtures.mjs`, and run
|
|
281
|
+
stream + intended findings to `js/scripts/build-fixtures.mjs`, and run
|
|
223
282
|
`npm run fixtures:build` — the meta-tests fail until both exist. Rule
|
|
224
283
|
*proposals* belong upstream as issues on
|
|
225
284
|
[`ag-ui-protocol/ag-ui`](https://github.com/ag-ui-protocol/ag-ui) first; this
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
//#region
|
|
1
|
+
//#region ../spec/catalog.json
|
|
2
2
|
var catalog_default = {
|
|
3
3
|
$comment: "AG-UI conformance rule catalog. Data, not code: a Python/Go implementation shares these rules. Severities follow the working agreement that behaviour the spec does not clearly govern is at most 'info' — entries with 'specQuestion' were downgraded accordingly; see docs/spec-questions.md.",
|
|
4
|
-
catalogVersion: "0.
|
|
4
|
+
catalogVersion: "0.4.0",
|
|
5
5
|
spec: "0.x",
|
|
6
6
|
rules: [
|
|
7
7
|
{
|
|
8
8
|
"id": "AGUI001",
|
|
9
9
|
"severity": "error",
|
|
10
10
|
"title": "Run does not start with RUN_STARTED",
|
|
11
|
+
"category": "lifecycle",
|
|
11
12
|
"messageTemplate": "First event of the run is {type}; expected RUN_STARTED",
|
|
12
13
|
"specUrl": "https://docs.ag-ui.com/concepts/events#runstarted",
|
|
13
14
|
"specQuote": "The RunStarted event is the first event emitted when an agent begins processing a request.",
|
|
@@ -18,6 +19,7 @@ var catalog_default = {
|
|
|
18
19
|
"id": "AGUI002",
|
|
19
20
|
"severity": "error",
|
|
20
21
|
"title": "Multiple RUN_STARTED in one run",
|
|
22
|
+
"category": "lifecycle",
|
|
21
23
|
"messageTemplate": "Duplicate RUN_STARTED (runId '{runId}') before the active run terminated",
|
|
22
24
|
"specUrl": "https://docs.ag-ui.com/concepts/events#lifecycle-events",
|
|
23
25
|
"specQuote": "The RunStarted and either RunFinished or RunError events are mandatory, forming the boundaries of an agent run.",
|
|
@@ -28,6 +30,7 @@ var catalog_default = {
|
|
|
28
30
|
"id": "AGUI003",
|
|
29
31
|
"severity": "error",
|
|
30
32
|
"title": "Run not terminated",
|
|
33
|
+
"category": "lifecycle",
|
|
31
34
|
"messageTemplate": "Run '{runId}' ended without RUN_FINISHED or RUN_ERROR",
|
|
32
35
|
"specUrl": "https://docs.ag-ui.com/concepts/events#runfinished",
|
|
33
36
|
"specQuote": "Every run terminates with either RunFinished or RunError.",
|
|
@@ -38,6 +41,7 @@ var catalog_default = {
|
|
|
38
41
|
"id": "AGUI004",
|
|
39
42
|
"severity": "error",
|
|
40
43
|
"title": "Event after terminal event",
|
|
44
|
+
"category": "lifecycle",
|
|
41
45
|
"messageTemplate": "{type} follows the run's terminal {terminalType}",
|
|
42
46
|
"specUrl": "https://docs.ag-ui.com/concepts/events#lifecycle-events",
|
|
43
47
|
"specQuote": "The RunStarted and either RunFinished or RunError events are mandatory, forming the boundaries of an agent run.",
|
|
@@ -48,6 +52,7 @@ var catalog_default = {
|
|
|
48
52
|
"id": "AGUI005",
|
|
49
53
|
"severity": "error",
|
|
50
54
|
"title": "RUN_FINISHED and RUN_ERROR are mutually exclusive",
|
|
55
|
+
"category": "lifecycle",
|
|
51
56
|
"messageTemplate": "{type} emitted after the run already terminated with {terminalType}",
|
|
52
57
|
"specUrl": "https://docs.ag-ui.com/concepts/events#runfinished",
|
|
53
58
|
"specQuote": "Every run terminates with either RunFinished or RunError.",
|
|
@@ -58,6 +63,7 @@ var catalog_default = {
|
|
|
58
63
|
"id": "AGUI006",
|
|
59
64
|
"severity": "error",
|
|
60
65
|
"title": "STEP_FINISHED without matching STEP_STARTED",
|
|
66
|
+
"category": "lifecycle",
|
|
61
67
|
"messageTemplate": "STEP_FINISHED '{stepName}' has no open STEP_STARTED",
|
|
62
68
|
"specUrl": "https://docs.ag-ui.com/concepts/events#stepfinished",
|
|
63
69
|
"specQuote": "The stepName must match the corresponding StepStarted event.",
|
|
@@ -68,6 +74,7 @@ var catalog_default = {
|
|
|
68
74
|
"id": "AGUI007",
|
|
69
75
|
"severity": "error",
|
|
70
76
|
"title": "Step unterminated at run end",
|
|
77
|
+
"category": "lifecycle",
|
|
71
78
|
"messageTemplate": "STEP_STARTED '{stepName}' never finished",
|
|
72
79
|
"specUrl": "https://docs.ag-ui.com/concepts/events#stepstarted",
|
|
73
80
|
"specQuote": "The stepName must match the corresponding StepStarted event.",
|
|
@@ -78,6 +85,7 @@ var catalog_default = {
|
|
|
78
85
|
"id": "AGUI008",
|
|
79
86
|
"severity": "warning",
|
|
80
87
|
"title": "Unstable threadId/runId across the run",
|
|
88
|
+
"category": "lifecycle",
|
|
81
89
|
"messageTemplate": "RUN_FINISHED {field} '{actual}' does not match RUN_STARTED {field} '{expected}'",
|
|
82
90
|
"specUrl": "https://docs.ag-ui.com/concepts/events#runstarted",
|
|
83
91
|
"specQuote": "It also provides crucial identifiers that can be used to associate subsequent events with this specific run.",
|
|
@@ -88,6 +96,7 @@ var catalog_default = {
|
|
|
88
96
|
"id": "AGUI101",
|
|
89
97
|
"severity": "error",
|
|
90
98
|
"title": "TEXT_MESSAGE_CONTENT without start",
|
|
99
|
+
"category": "text",
|
|
91
100
|
"messageTemplate": "TEXT_MESSAGE_CONTENT for messageId '{messageId}' with no open TEXT_MESSAGE_START",
|
|
92
101
|
"specUrl": "https://docs.ag-ui.com/concepts/events#text-message-events",
|
|
93
102
|
"specQuote": "A message begins with a TextMessageStart event, followed by one or more TextMessageContent events that deliver chunks of text as they become available, and concludes with a TextMessageEnd event.",
|
|
@@ -99,6 +108,7 @@ var catalog_default = {
|
|
|
99
108
|
"id": "AGUI102",
|
|
100
109
|
"severity": "error",
|
|
101
110
|
"title": "TEXT_MESSAGE_END without start",
|
|
111
|
+
"category": "text",
|
|
102
112
|
"messageTemplate": "TEXT_MESSAGE_END for messageId '{messageId}' with no open TEXT_MESSAGE_START",
|
|
103
113
|
"specUrl": "https://docs.ag-ui.com/concepts/events#textmessageend",
|
|
104
114
|
"specQuote": "messageId: Matches the ID from TextMessageStart",
|
|
@@ -110,6 +120,7 @@ var catalog_default = {
|
|
|
110
120
|
"id": "AGUI103",
|
|
111
121
|
"severity": "error",
|
|
112
122
|
"title": "Text message unterminated at run end",
|
|
123
|
+
"category": "text",
|
|
113
124
|
"messageTemplate": "TEXT_MESSAGE_START messageId '{messageId}' never ended",
|
|
114
125
|
"specUrl": "https://docs.ag-ui.com/concepts/events#text-message-events",
|
|
115
126
|
"specQuote": "A message begins with a TextMessageStart event, followed by one or more TextMessageContent events that deliver chunks of text as they become available, and concludes with a TextMessageEnd event.",
|
|
@@ -121,6 +132,7 @@ var catalog_default = {
|
|
|
121
132
|
"id": "AGUI104",
|
|
122
133
|
"severity": "error",
|
|
123
134
|
"title": "Duplicate messageId within a run",
|
|
135
|
+
"category": "text",
|
|
124
136
|
"messageTemplate": "messageId '{messageId}' was already used by a completed message",
|
|
125
137
|
"specUrl": "https://docs.ag-ui.com/concepts/events#textmessagestart",
|
|
126
138
|
"specQuote": "It establishes a unique messageId that will be referenced by subsequent content chunks and the end event.",
|
|
@@ -132,6 +144,7 @@ var catalog_default = {
|
|
|
132
144
|
"id": "AGUI105",
|
|
133
145
|
"severity": "warning",
|
|
134
146
|
"title": "Empty content delta",
|
|
147
|
+
"category": "text",
|
|
135
148
|
"messageTemplate": "TEXT_MESSAGE_CONTENT for messageId '{messageId}' has an empty delta",
|
|
136
149
|
"specUrl": "https://docs.ag-ui.com/concepts/events#textmessagecontent",
|
|
137
150
|
"specQuote": "delta: Text content chunk (non-empty)",
|
|
@@ -144,6 +157,7 @@ var catalog_default = {
|
|
|
144
157
|
"id": "AGUI106",
|
|
145
158
|
"severity": "error",
|
|
146
159
|
"title": "Interleaved message streams sharing a messageId",
|
|
160
|
+
"category": "text",
|
|
147
161
|
"messageTemplate": "TEXT_MESSAGE_START for messageId '{messageId}', which is already open",
|
|
148
162
|
"specUrl": "https://docs.ag-ui.com/concepts/events#implementation-considerations",
|
|
149
163
|
"specQuote": "Events with the same ID (e.g., messageId, toolCallId) belong to the same logical stream",
|
|
@@ -155,6 +169,7 @@ var catalog_default = {
|
|
|
155
169
|
"id": "AGUI201",
|
|
156
170
|
"severity": "error",
|
|
157
171
|
"title": "TOOL_CALL_ARGS without start",
|
|
172
|
+
"category": "toolcall",
|
|
158
173
|
"messageTemplate": "TOOL_CALL_ARGS for toolCallId '{toolCallId}' with no open TOOL_CALL_START",
|
|
159
174
|
"specUrl": "https://docs.ag-ui.com/concepts/events#tool-call-events",
|
|
160
175
|
"specQuote": "When an agent needs to use a tool, it emits a ToolCallStart event, followed by one or more ToolCallArgs events that stream the arguments being passed to the tool, and concludes with a ToolCallEnd event.",
|
|
@@ -166,6 +181,7 @@ var catalog_default = {
|
|
|
166
181
|
"id": "AGUI202",
|
|
167
182
|
"severity": "error",
|
|
168
183
|
"title": "TOOL_CALL_END without start",
|
|
184
|
+
"category": "toolcall",
|
|
169
185
|
"messageTemplate": "TOOL_CALL_END for toolCallId '{toolCallId}' with no open TOOL_CALL_START",
|
|
170
186
|
"specUrl": "https://docs.ag-ui.com/concepts/events#toolcallend",
|
|
171
187
|
"specQuote": "toolCallId: Matches the ID from ToolCallStart",
|
|
@@ -177,6 +193,7 @@ var catalog_default = {
|
|
|
177
193
|
"id": "AGUI203",
|
|
178
194
|
"severity": "error",
|
|
179
195
|
"title": "Unterminated tool call",
|
|
196
|
+
"category": "toolcall",
|
|
180
197
|
"messageTemplate": "TOOL_CALL_START id '{toolCallId}' never terminated",
|
|
181
198
|
"specUrl": "https://docs.ag-ui.com/concepts/events#tool-call-events",
|
|
182
199
|
"specQuote": "When an agent needs to use a tool, it emits a ToolCallStart event, followed by one or more ToolCallArgs events that stream the arguments being passed to the tool, and concludes with a ToolCallEnd event.",
|
|
@@ -188,6 +205,7 @@ var catalog_default = {
|
|
|
188
205
|
"id": "AGUI204",
|
|
189
206
|
"severity": "error",
|
|
190
207
|
"title": "Tool call arguments are not valid JSON",
|
|
208
|
+
"category": "toolcall",
|
|
191
209
|
"messageTemplate": "Concatenated TOOL_CALL_ARGS for toolCallId '{toolCallId}' do not parse as JSON: {error}",
|
|
192
210
|
"specUrl": "https://docs.ag-ui.com/concepts/events#toolcallargs",
|
|
193
211
|
"specQuote": "Frontends should concatenate these deltas in the order received to construct the complete arguments object.",
|
|
@@ -199,6 +217,7 @@ var catalog_default = {
|
|
|
199
217
|
"id": "AGUI205",
|
|
200
218
|
"severity": "error",
|
|
201
219
|
"title": "Duplicate toolCallId within a run",
|
|
220
|
+
"category": "toolcall",
|
|
202
221
|
"messageTemplate": "toolCallId '{toolCallId}' was already used by a completed tool call",
|
|
203
222
|
"specUrl": "https://docs.ag-ui.com/concepts/events#toolcallstart",
|
|
204
223
|
"specQuote": "toolCallId: Unique identifier for the tool call",
|
|
@@ -210,6 +229,7 @@ var catalog_default = {
|
|
|
210
229
|
"id": "AGUI206",
|
|
211
230
|
"severity": "warning",
|
|
212
231
|
"title": "TOOL_CALL_RESULT before TOOL_CALL_END",
|
|
232
|
+
"category": "toolcall",
|
|
213
233
|
"messageTemplate": "TOOL_CALL_RESULT for toolCallId '{toolCallId}' arrived while the call is still open",
|
|
214
234
|
"specUrl": "https://docs.ag-ui.com/concepts/events#toolcallresult",
|
|
215
235
|
"specQuote": "This event is sent after the tool has been executed by the system and contains the actual output generated by the tool.",
|
|
@@ -221,6 +241,7 @@ var catalog_default = {
|
|
|
221
241
|
"id": "AGUI207",
|
|
222
242
|
"severity": "error",
|
|
223
243
|
"title": "TOOL_CALL_RESULT references unknown toolCallId",
|
|
244
|
+
"category": "toolcall",
|
|
224
245
|
"messageTemplate": "TOOL_CALL_RESULT references toolCallId '{toolCallId}', which was never started",
|
|
225
246
|
"specUrl": "https://docs.ag-ui.com/concepts/events#toolcallresult",
|
|
226
247
|
"specQuote": "toolCallId: Matches the ID from the corresponding ToolCallStart event",
|
|
@@ -232,6 +253,7 @@ var catalog_default = {
|
|
|
232
253
|
"id": "AGUI208",
|
|
233
254
|
"severity": "info",
|
|
234
255
|
"title": "parentMessageId references unknown message",
|
|
256
|
+
"category": "toolcall",
|
|
235
257
|
"messageTemplate": "TOOL_CALL_START parentMessageId '{parentMessageId}' matches no message observed in this stream",
|
|
236
258
|
"specUrl": "https://docs.ag-ui.com/concepts/events#toolcallstart",
|
|
237
259
|
"specQuote": "The optional parentMessageId allows linking the tool call to a specific message in the conversation, providing context for why the tool is being used.",
|
|
@@ -244,6 +266,7 @@ var catalog_default = {
|
|
|
244
266
|
"id": "AGUI301",
|
|
245
267
|
"severity": "info",
|
|
246
268
|
"title": "STATE_DELTA before any STATE_SNAPSHOT",
|
|
269
|
+
"category": "state",
|
|
247
270
|
"messageTemplate": "STATE_DELTA precedes any STATE_SNAPSHOT; the base it applies to is not observable on this stream",
|
|
248
271
|
"specUrl": "https://docs.ag-ui.com/concepts/events#statesnapshot",
|
|
249
272
|
"specQuote": "This event is typically sent at the beginning of an interaction or when synchronization is needed.",
|
|
@@ -256,6 +279,7 @@ var catalog_default = {
|
|
|
256
279
|
"id": "AGUI302",
|
|
257
280
|
"severity": "error",
|
|
258
281
|
"title": "STATE_DELTA failed to apply",
|
|
282
|
+
"category": "state",
|
|
259
283
|
"messageTemplate": "STATE_DELTA failed to apply: {error}",
|
|
260
284
|
"specUrl": "https://docs.ag-ui.com/concepts/events#statedelta",
|
|
261
285
|
"specQuote": "Each delta represents specific changes to apply to the current state model.",
|
|
@@ -267,6 +291,7 @@ var catalog_default = {
|
|
|
267
291
|
"id": "AGUI303",
|
|
268
292
|
"severity": "error",
|
|
269
293
|
"title": "STATE_DELTA is not a valid RFC 6902 patch document",
|
|
294
|
+
"category": "state",
|
|
270
295
|
"messageTemplate": "STATE_DELTA is not a valid RFC 6902 patch document: {error}",
|
|
271
296
|
"specUrl": "https://docs.ag-ui.com/concepts/events#statedelta",
|
|
272
297
|
"specQuote": "The StateDelta event contains incremental updates to the agent's state in the form of JSON Patch operations (as defined in RFC 6902).",
|
|
@@ -278,6 +303,7 @@ var catalog_default = {
|
|
|
278
303
|
"id": "AGUI304",
|
|
279
304
|
"severity": "info",
|
|
280
305
|
"title": "Mid-run STATE_SNAPSHOT discards accumulated deltas",
|
|
306
|
+
"category": "state",
|
|
281
307
|
"messageTemplate": "STATE_SNAPSHOT replaces state previously built from {deltaCount} delta(s)",
|
|
282
308
|
"specUrl": "https://docs.ag-ui.com/concepts/events#statesnapshot",
|
|
283
309
|
"specQuote": "This event is typically sent at the beginning of an interaction or when synchronization is needed.",
|
|
@@ -289,6 +315,7 @@ var catalog_default = {
|
|
|
289
315
|
"id": "AGUI305",
|
|
290
316
|
"severity": "warning",
|
|
291
317
|
"title": "Shared state declared but never established",
|
|
318
|
+
"category": "state",
|
|
292
319
|
"messageTemplate": "features include 'shared-state' but no STATE_SNAPSHOT was emitted",
|
|
293
320
|
"specUrl": "https://docs.ag-ui.com/concepts/events#state-management-events",
|
|
294
321
|
"specQuote": "These events are used to manage and synchronize the agent's state with the frontend.",
|
|
@@ -301,6 +328,7 @@ var catalog_default = {
|
|
|
301
328
|
"id": "AGUI401",
|
|
302
329
|
"severity": "error",
|
|
303
330
|
"title": "REASONING_MESSAGE_CONTENT without start",
|
|
331
|
+
"category": "reasoning",
|
|
304
332
|
"messageTemplate": "REASONING_MESSAGE_CONTENT for messageId '{messageId}' with no open REASONING_MESSAGE_START",
|
|
305
333
|
"specUrl": "https://docs.ag-ui.com/concepts/events#reasoningmessagecontent",
|
|
306
334
|
"specQuote": "Multiple content events with the same messageId should be concatenated to form the complete visible reasoning.",
|
|
@@ -312,6 +340,7 @@ var catalog_default = {
|
|
|
312
340
|
"id": "AGUI402",
|
|
313
341
|
"severity": "warning",
|
|
314
342
|
"title": "Reasoning unterminated at run end",
|
|
343
|
+
"category": "reasoning",
|
|
315
344
|
"messageTemplate": "{startType} messageId '{messageId}' never ended",
|
|
316
345
|
"specUrl": "https://docs.ag-ui.com/concepts/events#reasoning-events",
|
|
317
346
|
"specQuote": "Reasoning events support LLM reasoning visibility and continuity, enabling chain-of-thought reasoning while maintaining privacy.",
|
|
@@ -323,6 +352,7 @@ var catalog_default = {
|
|
|
323
352
|
"id": "AGUI501",
|
|
324
353
|
"severity": "error",
|
|
325
354
|
"title": "Malformed SSE framing",
|
|
355
|
+
"category": "transport",
|
|
326
356
|
"messageTemplate": "Malformed SSE framing: {detail}",
|
|
327
357
|
"specUrl": "https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation",
|
|
328
358
|
"since": "0.x",
|
|
@@ -332,6 +362,7 @@ var catalog_default = {
|
|
|
332
362
|
"id": "AGUI502",
|
|
333
363
|
"severity": "error",
|
|
334
364
|
"title": "Event payload is not valid JSON",
|
|
365
|
+
"category": "transport",
|
|
335
366
|
"messageTemplate": "Event payload is not valid JSON: {error}",
|
|
336
367
|
"specUrl": "https://docs.ag-ui.com/concepts/events#base-event-properties",
|
|
337
368
|
"specQuote": "All events share a common set of base properties.",
|
|
@@ -342,7 +373,8 @@ var catalog_default = {
|
|
|
342
373
|
"id": "AGUI503",
|
|
343
374
|
"severity": "error",
|
|
344
375
|
"title": "Unknown event type",
|
|
345
|
-
"
|
|
376
|
+
"category": "transport",
|
|
377
|
+
"messageTemplate": "Unknown event type '{type}' (not in the installed AG-UI SDK v{sdkVersion}, and not RAW or CUSTOM)",
|
|
346
378
|
"specUrl": "https://docs.ag-ui.com/concepts/events#event-types-overview",
|
|
347
379
|
"specQuote": "Events in the protocol are categorized by their purpose.",
|
|
348
380
|
"since": "0.x",
|
|
@@ -352,6 +384,7 @@ var catalog_default = {
|
|
|
352
384
|
"id": "AGUI504",
|
|
353
385
|
"severity": "error",
|
|
354
386
|
"title": "Event fails schema validation for its declared type",
|
|
387
|
+
"category": "transport",
|
|
355
388
|
"messageTemplate": "{type}: {detail}",
|
|
356
389
|
"specUrl": "https://docs.ag-ui.com/concepts/events#base-event-properties",
|
|
357
390
|
"specQuote": "All events share a common set of base properties.",
|
|
@@ -362,6 +395,7 @@ var catalog_default = {
|
|
|
362
395
|
"id": "AGUI505",
|
|
363
396
|
"severity": "warning",
|
|
364
397
|
"title": "Unexpected Content-Type",
|
|
398
|
+
"category": "transport",
|
|
365
399
|
"messageTemplate": "Content-Type '{contentType}' is neither text/event-stream nor application/x-ndjson",
|
|
366
400
|
"specUrl": "https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model",
|
|
367
401
|
"since": "0.x",
|
|
@@ -371,6 +405,7 @@ var catalog_default = {
|
|
|
371
405
|
"id": "AGUI506",
|
|
372
406
|
"severity": "info",
|
|
373
407
|
"title": "No keepalive frame within the configured window",
|
|
408
|
+
"category": "transport",
|
|
374
409
|
"messageTemplate": "No event or keepalive frame for {seconds}s",
|
|
375
410
|
"specUrl": "https://docs.ag-ui.com/concepts/architecture#standard-http-client",
|
|
376
411
|
"since": "0.x",
|
|
@@ -381,6 +416,7 @@ var catalog_default = {
|
|
|
381
416
|
"id": "AGUI507",
|
|
382
417
|
"severity": "info",
|
|
383
418
|
"title": "Response appears buffered rather than incrementally flushed",
|
|
419
|
+
"category": "transport",
|
|
384
420
|
"messageTemplate": "Response appears buffered: {detail}",
|
|
385
421
|
"specUrl": "https://docs.ag-ui.com/concepts/architecture#standard-http-client",
|
|
386
422
|
"since": "0.x",
|
|
@@ -391,16 +427,85 @@ var catalog_default = {
|
|
|
391
427
|
"id": "AGUI508",
|
|
392
428
|
"severity": "error",
|
|
393
429
|
"title": "Stream ended without a terminal event",
|
|
430
|
+
"category": "transport",
|
|
394
431
|
"messageTemplate": "Connection ended mid-run '{runId}' without RUN_FINISHED or RUN_ERROR",
|
|
395
432
|
"specUrl": "https://docs.ag-ui.com/concepts/events#runfinished",
|
|
396
433
|
"specQuote": "Every run terminates with either RunFinished or RunError.",
|
|
397
434
|
"since": "0.x",
|
|
398
435
|
"checkedIn": "transport"
|
|
399
436
|
},
|
|
437
|
+
{
|
|
438
|
+
"id": "AGUI601",
|
|
439
|
+
"severity": "error",
|
|
440
|
+
"title": "Duplicate SUBAGENT_STARTED for a subagentRunId",
|
|
441
|
+
"category": "subagent",
|
|
442
|
+
"messageTemplate": "SUBAGENT_STARTED reuses subagentRunId '{subagentRunId}', which is already in use this run",
|
|
443
|
+
"specUrl": "https://docs.ag-ui.com/concepts/subagents#rules-clients-enforce",
|
|
444
|
+
"specQuote": "a subagent is not started twice within a run",
|
|
445
|
+
"since": "0.x",
|
|
446
|
+
"checkedIn": "core"
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
"id": "AGUI602",
|
|
450
|
+
"severity": "error",
|
|
451
|
+
"title": "SUBAGENT_FINISHED without matching SUBAGENT_STARTED",
|
|
452
|
+
"category": "subagent",
|
|
453
|
+
"messageTemplate": "SUBAGENT_FINISHED for subagentRunId '{subagentRunId}' with no open SUBAGENT_STARTED",
|
|
454
|
+
"specUrl": "https://docs.ag-ui.com/concepts/subagents#rules-clients-enforce",
|
|
455
|
+
"specQuote": "SubagentFinished and SubagentError name a subagent that is currently active",
|
|
456
|
+
"since": "0.x",
|
|
457
|
+
"checkedIn": "core"
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
"id": "AGUI603",
|
|
461
|
+
"severity": "error",
|
|
462
|
+
"title": "SUBAGENT_ERROR without matching SUBAGENT_STARTED",
|
|
463
|
+
"category": "subagent",
|
|
464
|
+
"messageTemplate": "SUBAGENT_ERROR for subagentRunId '{subagentRunId}' with no open SUBAGENT_STARTED",
|
|
465
|
+
"specUrl": "https://docs.ag-ui.com/concepts/subagents#rules-clients-enforce",
|
|
466
|
+
"specQuote": "SubagentFinished and SubagentError name a subagent that is currently active",
|
|
467
|
+
"since": "0.x",
|
|
468
|
+
"checkedIn": "core"
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
"id": "AGUI604",
|
|
472
|
+
"severity": "error",
|
|
473
|
+
"title": "Subagent unterminated at run end",
|
|
474
|
+
"category": "subagent",
|
|
475
|
+
"messageTemplate": "SUBAGENT_STARTED subagentRunId '{subagentRunId}' never closed with SUBAGENT_FINISHED or SUBAGENT_ERROR",
|
|
476
|
+
"specUrl": "https://docs.ag-ui.com/concepts/subagents#rules-clients-enforce",
|
|
477
|
+
"specQuote": "Every started subagent is closed before RunFinished.",
|
|
478
|
+
"since": "0.x",
|
|
479
|
+
"checkedIn": "core"
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
"id": "AGUI605",
|
|
483
|
+
"severity": "warning",
|
|
484
|
+
"title": "parentSubagentRunId references a subagent never started",
|
|
485
|
+
"category": "subagent",
|
|
486
|
+
"messageTemplate": "SUBAGENT_STARTED parentSubagentRunId '{parentSubagentRunId}' matches no subagent observed in this stream",
|
|
487
|
+
"specUrl": "https://docs.ag-ui.com/concepts/subagents#nesting-and-concurrency",
|
|
488
|
+
"specQuote": "parentSubagentRunId need only name a subagent that has been started, not one still active",
|
|
489
|
+
"since": "0.x",
|
|
490
|
+
"checkedIn": "core"
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
"id": "AGUI606",
|
|
494
|
+
"severity": "warning",
|
|
495
|
+
"title": "Continuation event's subagentRunId disagrees with its entity's owner",
|
|
496
|
+
"category": "subagent",
|
|
497
|
+
"messageTemplate": "{type} declares subagentRunId '{actual}', but {entityType} '{entityId}' was opened under {expected}",
|
|
498
|
+
"specUrl": "https://docs.ag-ui.com/concepts/subagents#rules-clients-enforce",
|
|
499
|
+
"specQuote": "Continuation and close events agree with the owner their entity was created under.",
|
|
500
|
+
"since": "0.x",
|
|
501
|
+
"specQuestion": "SQ-15",
|
|
502
|
+
"checkedIn": "core"
|
|
503
|
+
},
|
|
400
504
|
{
|
|
401
505
|
"id": "AGUI901",
|
|
402
506
|
"severity": "info",
|
|
403
507
|
"title": "RAW event wraps a typed AG-UI event",
|
|
508
|
+
"category": "hygiene",
|
|
404
509
|
"messageTemplate": "RAW event wraps an event of type '{wrappedType}', which has a typed AG-UI equivalent",
|
|
405
510
|
"specUrl": "https://docs.ag-ui.com/concepts/events#raw",
|
|
406
511
|
"specQuote": "The Raw event acts as a container for events originating from external systems or sources that don't natively follow the Agent UI Protocol.",
|
|
@@ -411,6 +516,7 @@ var catalog_default = {
|
|
|
411
516
|
"id": "AGUI902",
|
|
412
517
|
"severity": "info",
|
|
413
518
|
"title": "Events carry no timestamps",
|
|
519
|
+
"category": "hygiene",
|
|
414
520
|
"messageTemplate": "None of the {eventCount} events carry the optional timestamp property",
|
|
415
521
|
"specUrl": "https://docs.ag-ui.com/concepts/events#base-event-properties",
|
|
416
522
|
"specQuote": "timestamp: Optional timestamp indicating when the event was created",
|
|
@@ -421,6 +527,7 @@ var catalog_default = {
|
|
|
421
527
|
"id": "AGUI903",
|
|
422
528
|
"severity": "info",
|
|
423
529
|
"title": "CUSTOM event name is not namespaced",
|
|
530
|
+
"category": "hygiene",
|
|
424
531
|
"messageTemplate": "CUSTOM event name '{name}' has no namespace prefix (e.g. 'vendor.event')",
|
|
425
532
|
"specUrl": "https://docs.ag-ui.com/concepts/events#custom",
|
|
426
533
|
"specQuote": "Teams should document their custom events to ensure consistent implementation across frontends and agents.",
|
|
@@ -438,6 +545,16 @@ const SEVERITIES = [
|
|
|
438
545
|
"info"
|
|
439
546
|
];
|
|
440
547
|
const LAYERS = ["core", "transport"];
|
|
548
|
+
const CATEGORIES = [
|
|
549
|
+
"lifecycle",
|
|
550
|
+
"text",
|
|
551
|
+
"toolcall",
|
|
552
|
+
"state",
|
|
553
|
+
"reasoning",
|
|
554
|
+
"transport",
|
|
555
|
+
"hygiene",
|
|
556
|
+
"subagent"
|
|
557
|
+
];
|
|
441
558
|
/** Validates catalog data and returns it typed. Throws on structural problems. */
|
|
442
559
|
function validateCatalog(data) {
|
|
443
560
|
const problems = [];
|
|
@@ -451,6 +568,7 @@ function validateCatalog(data) {
|
|
|
451
568
|
seen.add(rule.id);
|
|
452
569
|
if (!SEVERITIES.includes(rule.severity)) problems.push(`${where}: bad severity '${rule.severity}'`);
|
|
453
570
|
if (!rule.title) problems.push(`${where}: missing title`);
|
|
571
|
+
if (!CATEGORIES.includes(rule.category)) problems.push(`${where}: bad category '${rule.category}'`);
|
|
454
572
|
if (!rule.messageTemplate) problems.push(`${where}: missing messageTemplate`);
|
|
455
573
|
if (!rule.specUrl?.startsWith("https://")) problems.push(`${where}: specUrl must be an https URL`);
|
|
456
574
|
if (!rule.since) problems.push(`${where}: missing since`);
|
|
@@ -469,4 +587,4 @@ function formatMessage(rule, params) {
|
|
|
469
587
|
//#endregion
|
|
470
588
|
export { validateCatalog as i, RULES as n, formatMessage as r, CATALOG as t };
|
|
471
589
|
|
|
472
|
-
//# sourceMappingURL=catalog-
|
|
590
|
+
//# sourceMappingURL=catalog-3e6INLCp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-3e6INLCp.js","names":["catalogJson"],"sources":["../spec/catalog.json","../js/src/rules/catalog.ts"],"sourcesContent":["","// Typed loader for the rule catalog. The catalog itself is data\n// (catalog.json) so other implementations can share it; this module gives it\n// types and validates its invariants once at load time.\n//\n// Note the asymmetry: the validator never throws on stream input, but a\n// malformed catalog is a programming error in this package, so the loader\n// throws loudly at import time.\n\nimport catalogJson from \"../../../spec/catalog.json\"\n\nexport type Severity = \"error\" | \"warning\" | \"info\"\nexport type SeverityOrOff = Severity | \"off\"\n\nconst SEVERITIES: readonly string[] = [\"error\", \"warning\", \"info\"]\nconst LAYERS: readonly string[] = [\"core\", \"transport\"]\nexport const CATEGORIES: readonly string[] = [\"lifecycle\", \"text\", \"toolcall\", \"state\", \"reasoning\", \"transport\", \"hygiene\", \"subagent\"]\n\nexport interface RuleDefinition {\n /** e.g. \"AGUI203\" */\n id: string\n severity: Severity\n title: string\n /** Which part of the protocol this rule governs, e.g. \"toolcall\". */\n category: string\n /** Human template with {placeholder} slots filled per diagnostic. */\n messageTemplate: string\n /** Governing spec section. Mandatory: rules that cannot cite one don't ship. */\n specUrl: string\n /** Exact sentence from the spec section, where one exists. */\n specQuote?: string\n since: string\n /** Canonical AG-UI feature this rule relates to, if any. */\n feature?: string\n /** True when the rule only fires if opts.features declares `feature`. */\n requiresFeature?: boolean\n /** Cross-reference into docs/spec-questions.md for downgraded/ambiguous rules. */\n specQuestion?: string\n /** Where the rule is evaluated. Transport rules are skipped (and the skip\n * reported) when validating recorded input with no transport in play. */\n checkedIn: \"core\" | \"transport\"\n}\n\nexport interface Catalog {\n catalogVersion: string\n spec: string\n rules: readonly RuleDefinition[]\n}\n\n/** Validates catalog data and returns it typed. Throws on structural problems. */\nexport function validateCatalog(data: unknown): Catalog {\n const problems: string[] = []\n const cat = data as Catalog\n if (typeof cat !== \"object\" || cat === null || !Array.isArray(cat.rules)) {\n throw new Error(\"rule catalog: expected an object with a rules array\")\n }\n const seen = new Set<string>()\n for (const rule of cat.rules) {\n const where = rule?.id ?? \"<missing id>\"\n if (!/^AGUI\\d{3}$/.test(rule.id ?? \"\")) problems.push(`${where}: id must match AGUI###`)\n if (seen.has(rule.id)) problems.push(`${where}: duplicate id`)\n seen.add(rule.id)\n if (!SEVERITIES.includes(rule.severity)) problems.push(`${where}: bad severity '${rule.severity}'`)\n if (!rule.title) problems.push(`${where}: missing title`)\n if (!CATEGORIES.includes(rule.category)) problems.push(`${where}: bad category '${rule.category}'`)\n if (!rule.messageTemplate) problems.push(`${where}: missing messageTemplate`)\n if (!rule.specUrl?.startsWith(\"https://\")) problems.push(`${where}: specUrl must be an https URL`)\n if (!rule.since) problems.push(`${where}: missing since`)\n if (!LAYERS.includes(rule.checkedIn)) problems.push(`${where}: bad checkedIn '${rule.checkedIn}'`)\n if (rule.requiresFeature && !rule.feature) problems.push(`${where}: requiresFeature without feature`)\n }\n if (problems.length > 0) {\n throw new Error(`rule catalog is invalid:\\n ${problems.join(\"\\n \")}`)\n }\n return cat\n}\n\nexport const CATALOG: Catalog = validateCatalog(catalogJson)\n\nexport const RULES: ReadonlyMap<string, RuleDefinition> = new Map(\n CATALOG.rules.map((r) => [r.id, r]),\n)\n\n/** Fills a rule's messageTemplate. Unknown placeholders are left intact. */\nexport function formatMessage(rule: RuleDefinition, params: Record<string, unknown>): string {\n return rule.messageTemplate.replace(/\\{(\\w+)\\}/g, (whole, key: string) =>\n key in params ? String(params[key]) : whole,\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACaA,MAAM,aAAgC;CAAC;CAAS;CAAW;AAAM;AACjE,MAAM,SAA4B,CAAC,QAAQ,WAAW;AACtD,MAAa,aAAgC;CAAC;CAAa;CAAQ;CAAY;CAAS;CAAa;CAAa;CAAW;AAAU;;AAkCvI,SAAgB,gBAAgB,MAAwB;CACtD,MAAM,WAAqB,CAAC;CAC5B,MAAM,MAAM;CACZ,IAAI,OAAO,QAAQ,YAAY,QAAQ,QAAQ,CAAC,MAAM,QAAQ,IAAI,KAAK,GACrE,MAAM,IAAI,MAAM,qDAAqD;CAEvE,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,QAAQ,IAAI,OAAO;EAC5B,MAAM,QAAQ,MAAM,MAAM;EAC1B,IAAI,CAAC,cAAc,KAAK,KAAK,MAAM,EAAE,GAAG,SAAS,KAAK,GAAG,MAAM,wBAAwB;EACvF,IAAI,KAAK,IAAI,KAAK,EAAE,GAAG,SAAS,KAAK,GAAG,MAAM,eAAe;EAC7D,KAAK,IAAI,KAAK,EAAE;EAChB,IAAI,CAAC,WAAW,SAAS,KAAK,QAAQ,GAAG,SAAS,KAAK,GAAG,MAAM,kBAAkB,KAAK,SAAS,EAAE;EAClG,IAAI,CAAC,KAAK,OAAO,SAAS,KAAK,GAAG,MAAM,gBAAgB;EACxD,IAAI,CAAC,WAAW,SAAS,KAAK,QAAQ,GAAG,SAAS,KAAK,GAAG,MAAM,kBAAkB,KAAK,SAAS,EAAE;EAClG,IAAI,CAAC,KAAK,iBAAiB,SAAS,KAAK,GAAG,MAAM,0BAA0B;EAC5E,IAAI,CAAC,KAAK,SAAS,WAAW,UAAU,GAAG,SAAS,KAAK,GAAG,MAAM,+BAA+B;EACjG,IAAI,CAAC,KAAK,OAAO,SAAS,KAAK,GAAG,MAAM,gBAAgB;EACxD,IAAI,CAAC,OAAO,SAAS,KAAK,SAAS,GAAG,SAAS,KAAK,GAAG,MAAM,mBAAmB,KAAK,UAAU,EAAE;EACjG,IAAI,KAAK,mBAAmB,CAAC,KAAK,SAAS,SAAS,KAAK,GAAG,MAAM,kCAAkC;CACtG;CACA,IAAI,SAAS,SAAS,GACpB,MAAM,IAAI,MAAM,+BAA+B,SAAS,KAAK,MAAM,GAAG;CAExE,OAAO;AACT;AAEA,MAAa,UAAmB,gBAAgBA,eAAW;AAE3D,MAAa,QAA6C,IAAI,IAC5D,QAAQ,MAAM,KAAK,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CACpC;;AAGA,SAAgB,cAAc,MAAsB,QAAyC;CAC3F,OAAO,KAAK,gBAAgB,QAAQ,eAAe,OAAO,QACxD,OAAO,SAAS,OAAO,OAAO,IAAI,IAAI,KACxC;AACF"}
|