ag-ui-validate 0.1.0 → 0.3.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 +77 -10
- package/dist/{catalog-Ci9dqc1a.cjs → catalog-AB5vuZ8j.cjs} +3 -3
- package/dist/catalog-AB5vuZ8j.cjs.map +1 -0
- package/dist/{catalog-BglXBNbL.js → catalog-DqTI4VLt.js} +3 -3
- package/dist/catalog-DqTI4VLt.js.map +1 -0
- package/dist/cli.js +107 -55
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +2 -2
- package/dist/report.cjs +43 -1
- package/dist/report.cjs.map +1 -1
- package/dist/report.d.cts +7 -1
- package/dist/report.d.ts +7 -1
- package/dist/report.js +43 -2
- package/dist/report.js.map +1 -1
- package/dist/{src-HmI-kxef.cjs → src-D04i3Vso.cjs} +2 -2
- package/dist/src-D04i3Vso.cjs.map +1 -0
- package/dist/{src-rGZ2G4qA.js → src-Dcuqz2QC.js} +2 -2
- package/dist/src-Dcuqz2QC.js.map +1 -0
- package/dist/transport.cjs +1 -1
- package/dist/transport.cjs.map +1 -1
- package/dist/transport.js +1 -1
- package/dist/transport.js.map +1 -1
- package/dist/vitest.js +2 -2
- package/dist/vitest.js.map +1 -1
- package/{src → js/src}/cli-args.ts +11 -0
- package/{src → js/src}/cli.ts +8 -3
- package/{src → js/src}/report/index.ts +1 -1
- package/{src → js/src}/report/pretty.ts +43 -0
- package/{src → js/src}/rules/catalog.ts +1 -1
- package/{src → js/src}/transport/index.ts +1 -1
- package/package.json +14 -13
- package/{src/rules → spec}/catalog.json +1 -1
- package/spec/event-categories.json +56 -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/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 → js/src}/index.ts +0 -0
- /package/{src → js/src}/protocol/event-table.ts +0 -0
- /package/{src → js/src}/protocol/jsonpatch.ts +0 -0
- /package/{src → js/src}/report/json.ts +0 -0
- /package/{src → js/src}/report/junit.ts +0 -0
- /package/{src → js/src}/report/sarif.ts +0 -0
- /package/{src → js/src}/rules/checks/context.ts +0 -0
- /package/{src → js/src}/rules/checks/lifecycle.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/text.ts +0 -0
- /package/{src → js/src}/rules/checks/toolcalls.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
|
@@ -20,6 +20,8 @@ export interface CliConfig {
|
|
|
20
20
|
sarifFile?: string
|
|
21
21
|
junitFile?: string
|
|
22
22
|
jsonFile?: string
|
|
23
|
+
/** Pretty output only: one line per rule with a count, not one per finding. */
|
|
24
|
+
group: boolean
|
|
23
25
|
help: boolean
|
|
24
26
|
version: boolean
|
|
25
27
|
}
|
|
@@ -40,6 +42,7 @@ Output (default: human-readable):
|
|
|
40
42
|
--json-file <path> additionally write the JSON report to a file
|
|
41
43
|
--sarif-file <path> additionally write a SARIF log to a file
|
|
42
44
|
--junit-file <path> additionally write JUnit XML to a file
|
|
45
|
+
--group one line per rule with a count (large streams)
|
|
43
46
|
--no-color disable ANSI colors
|
|
44
47
|
|
|
45
48
|
Rules:
|
|
@@ -67,6 +70,7 @@ const FLAGS: Record<string, Flag> = {
|
|
|
67
70
|
"--help": { takesValue: false, apply: (c) => ((c.help = true), null) },
|
|
68
71
|
"--version": { takesValue: false, apply: (c) => ((c.version = true), null) },
|
|
69
72
|
"--no-color": { takesValue: false, apply: (c) => ((c.color = false), null) },
|
|
73
|
+
"--group": { takesValue: false, apply: (c) => ((c.group = true), null) },
|
|
70
74
|
"--json": { takesValue: false, apply: (c) => setFormat(c, "json") },
|
|
71
75
|
"--sarif": { takesValue: false, apply: (c) => setFormat(c, "sarif") },
|
|
72
76
|
"--junit": { takesValue: false, apply: (c) => setFormat(c, "junit") },
|
|
@@ -150,6 +154,7 @@ export function parseCliArgs(argv: string[]): ParseResult {
|
|
|
150
154
|
color: null,
|
|
151
155
|
headers: {},
|
|
152
156
|
severityOverrides: {},
|
|
157
|
+
group: false,
|
|
153
158
|
help: false,
|
|
154
159
|
version: false,
|
|
155
160
|
}
|
|
@@ -182,6 +187,12 @@ export function parseCliArgs(argv: string[]): ParseResult {
|
|
|
182
187
|
if (error !== null) return { ok: false, error }
|
|
183
188
|
}
|
|
184
189
|
|
|
190
|
+
if (config.group && config.format !== "pretty") {
|
|
191
|
+
return {
|
|
192
|
+
ok: false,
|
|
193
|
+
error: "--group only applies to the default pretty output, not --json/--sarif/--junit",
|
|
194
|
+
}
|
|
195
|
+
}
|
|
185
196
|
if (targets.length > 1) {
|
|
186
197
|
return { ok: false, error: `expected exactly one target, got ${targets.length}` }
|
|
187
198
|
}
|
package/{src → js/src}/cli.ts
RENAMED
|
@@ -7,7 +7,7 @@ import { createReadStream, readFileSync, writeFileSync } from "node:fs"
|
|
|
7
7
|
import process from "node:process"
|
|
8
8
|
import { decideExitCode, parseCliArgs, USAGE } from "./cli-args.js"
|
|
9
9
|
import type { CliConfig } from "./cli-args.js"
|
|
10
|
-
import { formatDiagnosticLine, formatReportSummary, toJsonReport, toJUnit, toSarif } from "./report/index.js"
|
|
10
|
+
import { formatDiagnosticLine, formatGroupedDiagnostics, formatReportSummary, toJsonReport, toJUnit, toSarif } from "./report/index.js"
|
|
11
11
|
import { TransportError, validateBody, validateEndpoint } from "./transport/index.js"
|
|
12
12
|
import type { TransportOptions, TransportResult } from "./transport/index.js"
|
|
13
13
|
import type { Diagnostic, ValidatorOptions } from "./types.js"
|
|
@@ -62,8 +62,9 @@ async function main(): Promise<number> {
|
|
|
62
62
|
|
|
63
63
|
// Pretty mode streams findings as they are detected; machine formats emit
|
|
64
64
|
// one document at the end, so nothing else may touch stdout before it.
|
|
65
|
+
// --group defers everything to one collapsed block after the run.
|
|
65
66
|
let printed = 0
|
|
66
|
-
const onDiagnostic = pretty
|
|
67
|
+
const onDiagnostic = pretty && !config.group
|
|
67
68
|
? (d: Diagnostic): void => {
|
|
68
69
|
printed += 1
|
|
69
70
|
process.stdout.write(`${formatDiagnosticLine(d, { color })}\n`)
|
|
@@ -116,7 +117,11 @@ async function main(): Promise<number> {
|
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
if (pretty) {
|
|
119
|
-
if (
|
|
120
|
+
if (config.group && report.diagnostics.length > 0) {
|
|
121
|
+
process.stdout.write(`${formatGroupedDiagnostics(report.diagnostics, { color })}\n\n`)
|
|
122
|
+
} else if (printed > 0) {
|
|
123
|
+
process.stdout.write("\n")
|
|
124
|
+
}
|
|
120
125
|
process.stdout.write(`${formatReportSummary(report, { color })}\n`)
|
|
121
126
|
} else if (config.format === "json") {
|
|
122
127
|
const doc = toJsonReport(report, { tool: { name: TOOL_NAME, version }, target: targetLabel })
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ag-ui-validate/report: pure reporters over a core Report. No I/O — callers
|
|
2
2
|
// decide where the strings go.
|
|
3
|
-
export { formatDiagnosticLine, formatReportSummary } from "./pretty.js"
|
|
3
|
+
export { formatDiagnosticLine, formatGroupedDiagnostics, formatReportSummary } from "./pretty.js"
|
|
4
4
|
export type { PrettyOptions } from "./pretty.js"
|
|
5
5
|
export { toJsonReport } from "./json.js"
|
|
6
6
|
export type { JsonReportDocument, JsonReportOptions } from "./json.js"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Human-readable output. Pure string formatting — the CLI decides where it
|
|
2
2
|
// goes and whether a TTY wants color.
|
|
3
|
+
import { RULES } from "../rules/catalog.js"
|
|
3
4
|
import type { Diagnostic, Report, Severity } from "../types.js"
|
|
4
5
|
|
|
5
6
|
export interface PrettyOptions {
|
|
@@ -25,6 +26,48 @@ function count(n: number, noun: string): string {
|
|
|
25
26
|
return `${n} ${noun}${n === 1 ? "" : "s"}`
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
const SEVERITY_RANK: Record<Severity, number> = { error: 0, warning: 1, info: 2 }
|
|
30
|
+
const SAMPLE_INDEXES = 3
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* One line per rule instead of one per occurrence — for large streams where
|
|
34
|
+
* the same violation repeats. Totals stay honest in the summary; this only
|
|
35
|
+
* changes what is listed.
|
|
36
|
+
*/
|
|
37
|
+
export function formatGroupedDiagnostics(diagnostics: Diagnostic[], opts: PrettyOptions): string {
|
|
38
|
+
const groups = new Map<string, Diagnostic[]>()
|
|
39
|
+
for (const d of diagnostics) {
|
|
40
|
+
const list = groups.get(d.rule)
|
|
41
|
+
if (list === undefined) groups.set(d.rule, [d])
|
|
42
|
+
else list.push(d)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const sorted = [...groups.values()].sort((a, b) => {
|
|
46
|
+
const bySeverity = SEVERITY_RANK[a[0]!.severity] - SEVERITY_RANK[b[0]!.severity]
|
|
47
|
+
return bySeverity !== 0 ? bySeverity : a[0]!.rule.localeCompare(b[0]!.rule)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const lines: string[] = []
|
|
51
|
+
for (const group of sorted) {
|
|
52
|
+
const first = group[0]!
|
|
53
|
+
const title = RULES.get(first.rule)?.title ?? first.message
|
|
54
|
+
const indexes = group.map((d) => d.eventIndex).filter((i) => i >= 0)
|
|
55
|
+
let where: string
|
|
56
|
+
if (indexes.length === 0) {
|
|
57
|
+
where = "stream-level"
|
|
58
|
+
} else {
|
|
59
|
+
const sample = indexes.slice(0, SAMPLE_INDEXES).join(", ")
|
|
60
|
+
const rest = indexes.length - SAMPLE_INDEXES
|
|
61
|
+
where = `events ${sample}${rest > 0 ? ` (+${rest} more)` : ""}`
|
|
62
|
+
}
|
|
63
|
+
const head = paint(SGR[first.severity], `${SYMBOL[first.severity]} ${first.rule}`, opts.color)
|
|
64
|
+
const meta = paint("2", `${first.severity.padEnd(7)} ×${group.length}`, opts.color)
|
|
65
|
+
const cite = paint("2", ` ↳ ${first.specUrl}`, opts.color)
|
|
66
|
+
lines.push(`${head} ${meta} ${title} — ${where}`, cite)
|
|
67
|
+
}
|
|
68
|
+
return lines.join("\n")
|
|
69
|
+
}
|
|
70
|
+
|
|
28
71
|
export function formatReportSummary(report: Report, opts: PrettyOptions): string {
|
|
29
72
|
const { errors, warnings, info } = report.summary
|
|
30
73
|
const lines: string[] = []
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// malformed catalog is a programming error in this package, so the loader
|
|
7
7
|
// throws loudly at import time.
|
|
8
8
|
|
|
9
|
-
import catalogJson from "
|
|
9
|
+
import catalogJson from "../../../spec/catalog.json"
|
|
10
10
|
|
|
11
11
|
export type Severity = "error" | "warning" | "info"
|
|
12
12
|
export type SeverityOrOff = Severity | "off"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ag-ui-validate/transport: SSE + NDJSON clients over the pure core.
|
|
2
2
|
// This is the I/O layer — fetch, streams, and clocks live here, never in the
|
|
3
3
|
// core. Uses only web-platform APIs (fetch, TextDecoder, AbortController) so
|
|
4
|
-
// it stays isomorphic: Node
|
|
4
|
+
// it stays isomorphic: Node 22+, browsers, Deno, Workers.
|
|
5
5
|
|
|
6
6
|
import { createValidator } from "../index.js"
|
|
7
7
|
import type { Diagnostic, Report, ValidationLayer, ValidatorOptions } from "../index.js"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ag-ui-validate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Conformance validator for the AG-UI protocol. Feed it an event stream, get every violation with a rule ID, a severity, and a link to the governing spec section.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ag-ui",
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
"type": "module",
|
|
24
24
|
"sideEffects": [
|
|
25
25
|
"./dist/vitest.js",
|
|
26
|
-
"./src/vitest/index.ts"
|
|
26
|
+
"./js/src/vitest/index.ts"
|
|
27
27
|
],
|
|
28
28
|
"engines": {
|
|
29
|
-
"node": ">=
|
|
29
|
+
"node": ">=22"
|
|
30
30
|
},
|
|
31
31
|
"bin": {
|
|
32
32
|
"ag-ui-validate": "./dist/cli.js"
|
|
@@ -88,20 +88,21 @@
|
|
|
88
88
|
},
|
|
89
89
|
"files": [
|
|
90
90
|
"dist",
|
|
91
|
-
"src"
|
|
91
|
+
"js/src",
|
|
92
|
+
"spec"
|
|
92
93
|
],
|
|
93
94
|
"scripts": {
|
|
94
|
-
"build": "tsdown",
|
|
95
|
-
"typecheck": "tsc --noEmit -p tsconfig.src.json && tsc --noEmit",
|
|
96
|
-
"test": "vitest run",
|
|
97
|
-
"test:watch": "vitest",
|
|
98
|
-
"fixtures:build": "npm run build && node scripts/build-fixtures.mjs",
|
|
99
|
-
"protocol:derive": "node scripts/generate-event-table.mjs",
|
|
95
|
+
"build": "tsdown --config js/tsdown.config.mjs",
|
|
96
|
+
"typecheck": "tsc --noEmit -p js/tsconfig.src.json && tsc --noEmit -p js/tsconfig.json",
|
|
97
|
+
"test": "vitest run --config js/vitest.config.ts",
|
|
98
|
+
"test:watch": "vitest --config js/vitest.config.ts",
|
|
99
|
+
"fixtures:build": "npm run build && node js/scripts/build-fixtures.mjs",
|
|
100
|
+
"protocol:derive": "node js/scripts/generate-event-table.mjs",
|
|
100
101
|
"docs:generate": "node scripts/generate-rule-docs.mjs",
|
|
101
102
|
"docs:check": "node scripts/generate-rule-docs.mjs --check",
|
|
102
|
-
"demo": "node scripts/demo.mjs",
|
|
103
|
-
"fuzz": "node scripts/fuzz.mjs",
|
|
104
|
-
"e2e": "node scripts/e2e-live-server.mjs",
|
|
103
|
+
"demo": "node js/scripts/demo.mjs",
|
|
104
|
+
"fuzz": "node js/scripts/fuzz.mjs",
|
|
105
|
+
"e2e": "node js/scripts/e2e-live-server.mjs",
|
|
105
106
|
"links:check": "node scripts/check-spec-links.mjs",
|
|
106
107
|
"changeset": "changeset",
|
|
107
108
|
"version": "changeset version",
|
|
@@ -341,7 +341,7 @@
|
|
|
341
341
|
"id": "AGUI503",
|
|
342
342
|
"severity": "error",
|
|
343
343
|
"title": "Unknown event type",
|
|
344
|
-
"messageTemplate": "Unknown event type '{type}' (not in
|
|
344
|
+
"messageTemplate": "Unknown event type '{type}' (not in the installed AG-UI SDK v{sdkVersion}, and not RAW or CUSTOM)",
|
|
345
345
|
"specUrl": "https://docs.ag-ui.com/concepts/events#event-types-overview",
|
|
346
346
|
"specQuote": "Events in the protocol are categorized by their purpose.",
|
|
347
347
|
"since": "0.x",
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Wire-type -> category/deprecation data for the canonical event table. Hand-maintained (not derivable from the SDK schemas themselves); shared by every language's event-table derivation tool so this classification is written once. Categories are display/grouping metadata only, not conformance rules — those live in catalog.json.",
|
|
3
|
+
"categories": [
|
|
4
|
+
"lifecycle",
|
|
5
|
+
"text",
|
|
6
|
+
"toolcall",
|
|
7
|
+
"state",
|
|
8
|
+
"activity",
|
|
9
|
+
"reasoning",
|
|
10
|
+
"thinking",
|
|
11
|
+
"special"
|
|
12
|
+
],
|
|
13
|
+
"eventCategory": {
|
|
14
|
+
"RUN_STARTED": "lifecycle",
|
|
15
|
+
"RUN_FINISHED": "lifecycle",
|
|
16
|
+
"RUN_ERROR": "lifecycle",
|
|
17
|
+
"STEP_STARTED": "lifecycle",
|
|
18
|
+
"STEP_FINISHED": "lifecycle",
|
|
19
|
+
"TEXT_MESSAGE_START": "text",
|
|
20
|
+
"TEXT_MESSAGE_CONTENT": "text",
|
|
21
|
+
"TEXT_MESSAGE_END": "text",
|
|
22
|
+
"TEXT_MESSAGE_CHUNK": "text",
|
|
23
|
+
"TOOL_CALL_START": "toolcall",
|
|
24
|
+
"TOOL_CALL_ARGS": "toolcall",
|
|
25
|
+
"TOOL_CALL_END": "toolcall",
|
|
26
|
+
"TOOL_CALL_CHUNK": "toolcall",
|
|
27
|
+
"TOOL_CALL_RESULT": "toolcall",
|
|
28
|
+
"STATE_SNAPSHOT": "state",
|
|
29
|
+
"STATE_DELTA": "state",
|
|
30
|
+
"MESSAGES_SNAPSHOT": "state",
|
|
31
|
+
"ACTIVITY_SNAPSHOT": "activity",
|
|
32
|
+
"ACTIVITY_DELTA": "activity",
|
|
33
|
+
"REASONING_START": "reasoning",
|
|
34
|
+
"REASONING_MESSAGE_START": "reasoning",
|
|
35
|
+
"REASONING_MESSAGE_CONTENT": "reasoning",
|
|
36
|
+
"REASONING_MESSAGE_END": "reasoning",
|
|
37
|
+
"REASONING_MESSAGE_CHUNK": "reasoning",
|
|
38
|
+
"REASONING_END": "reasoning",
|
|
39
|
+
"REASONING_ENCRYPTED_VALUE": "reasoning",
|
|
40
|
+
"THINKING_START": "thinking",
|
|
41
|
+
"THINKING_END": "thinking",
|
|
42
|
+
"THINKING_TEXT_MESSAGE_START": "thinking",
|
|
43
|
+
"THINKING_TEXT_MESSAGE_CONTENT": "thinking",
|
|
44
|
+
"THINKING_TEXT_MESSAGE_END": "thinking",
|
|
45
|
+
"RAW": "special",
|
|
46
|
+
"CUSTOM": "special"
|
|
47
|
+
},
|
|
48
|
+
"$deprecatedComment": "@deprecated markers in @ag-ui/core's type definitions, 'Will be removed in 1.0.0'. Maps a deprecated wire type to its replacement.",
|
|
49
|
+
"deprecated": {
|
|
50
|
+
"THINKING_START": "REASONING_START",
|
|
51
|
+
"THINKING_END": "REASONING_END",
|
|
52
|
+
"THINKING_TEXT_MESSAGE_START": "REASONING_MESSAGE_START",
|
|
53
|
+
"THINKING_TEXT_MESSAGE_CONTENT": "REASONING_MESSAGE_CONTENT",
|
|
54
|
+
"THINKING_TEXT_MESSAGE_END": "REASONING_MESSAGE_END"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# AG-UI conformance fixture corpus
|
|
2
|
+
|
|
3
|
+
A language-neutral corpus for testing AG-UI protocol validators. Any
|
|
4
|
+
implementation — TypeScript, Python, Go — that consumes the shared rule
|
|
5
|
+
catalog ([`catalog.json`](../catalog.json)) can be tested
|
|
6
|
+
against these files. The corpus, not per-rule test code, is the suite: adding
|
|
7
|
+
a rule to the catalog means adding a fixture directory here.
|
|
8
|
+
|
|
9
|
+
## Layout
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
fixtures/
|
|
13
|
+
├── valid/ one stream per canonical AG-UI feature
|
|
14
|
+
│ ├── agentic-chat.jsonl
|
|
15
|
+
│ ├── agentic-chat.expected.json (optional; assumed [] when absent)
|
|
16
|
+
│ └── …
|
|
17
|
+
└── invalid/ one directory per rule
|
|
18
|
+
├── AGUI203-unterminated-tool-call/
|
|
19
|
+
│ ├── stream.jsonl the event stream
|
|
20
|
+
│ ├── expected.json the exact expected Diagnostic[]
|
|
21
|
+
│ └── options.json (optional) validator options for this run
|
|
22
|
+
└── AGUI506-keepalive-gap/ transport rules use a scenario instead
|
|
23
|
+
├── scenario.json an HTTP response body as timed byte chunks
|
|
24
|
+
└── expected.json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Running the corpus against an implementation
|
|
28
|
+
|
|
29
|
+
For every event-stream fixture (`stream.jsonl`):
|
|
30
|
+
|
|
31
|
+
1. Read the `.jsonl` file and split it into lines; drop empty lines.
|
|
32
|
+
2. Feed each remaining line to the validator **as a raw string** — do not
|
|
33
|
+
pre-parse. Malformed lines are intentional (they exercise AGUI502); a
|
|
34
|
+
conforming validator treats them as diagnostics, never exceptions.
|
|
35
|
+
3. If `options.json` exists, construct the validator with those options
|
|
36
|
+
(e.g. `{"features": ["shared-state"]}` for AGUI305).
|
|
37
|
+
4. After the last line, finalize (end-of-stream checks).
|
|
38
|
+
5. Deep-compare the accumulated diagnostics with `expected.json`. Order
|
|
39
|
+
matters: diagnostics are emitted in stream order, and end-of-stream
|
|
40
|
+
diagnostics use `eventIndex: -1`.
|
|
41
|
+
|
|
42
|
+
For every transport fixture (`scenario.json`) — these exercise the rules that
|
|
43
|
+
need a live connection (AGUI501, AGUI505–AGUI508):
|
|
44
|
+
|
|
45
|
+
1. The scenario describes an HTTP response: `contentType` and `chunks`, each
|
|
46
|
+
chunk being `{ "gapMs": n, "text": "…" }` — the raw bytes as they arrived,
|
|
47
|
+
with `gapMs` of simulated silence before the chunk.
|
|
48
|
+
2. Feed the chunk bytes through your transport layer (SSE or NDJSON per the
|
|
49
|
+
content type, sniffing when unknown) with a simulated clock that advances
|
|
50
|
+
by each `gapMs`; the keepalive window is the default 30 000 ms.
|
|
51
|
+
3. `"abnormalEof": true` means the connection errored after the last chunk
|
|
52
|
+
(as opposed to closing cleanly) — the AGUI508 condition.
|
|
53
|
+
4. Finalize and deep-compare diagnostics with `expected.json` as above.
|
|
54
|
+
|
|
55
|
+
Every `invalid/` fixture must fire its directory's namesake rule; every
|
|
56
|
+
`valid/` fixture must produce exactly its `*.expected.json` (usually nothing —
|
|
57
|
+
they are the false-positive guards). `valid/predictive-state-updates` expects
|
|
58
|
+
one `info` finding: the Dojo's real `PredictState` custom event name is
|
|
59
|
+
un-namespaced (AGUI903, see `docs/spec-questions.md` SQ-14).
|
|
60
|
+
|
|
61
|
+
## Diagnostic shape
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"rule": "AGUI203",
|
|
66
|
+
"severity": "error",
|
|
67
|
+
"message": "TOOL_CALL_START id 'call_7' never terminated",
|
|
68
|
+
"eventIndex": 3,
|
|
69
|
+
"eventType": "RUN_FINISHED",
|
|
70
|
+
"pointer": "/toolCallId",
|
|
71
|
+
"relatedEventIndex": 1,
|
|
72
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#tool-call-events"
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`eventType`, `pointer`, and `relatedEventIndex` are optional and omitted when
|
|
77
|
+
absent. Messages come from the catalog's `messageTemplate`s, so independent
|
|
78
|
+
implementations produce identical output.
|
|
79
|
+
|
|
80
|
+
## Maintenance
|
|
81
|
+
|
|
82
|
+
Fixtures are generated by [`scripts/build-fixtures.mjs`](../../js/scripts/build-fixtures.mjs),
|
|
83
|
+
which declares each stream's *intended* rule hits independently of the
|
|
84
|
+
validator and refuses to write anything on mismatch — regeneration can never
|
|
85
|
+
silently bake an implementation bug into `expected.json`.
|
|
86
|
+
|
|
87
|
+
## Not yet covered
|
|
88
|
+
|
|
89
|
+
- **`valid/real-world/`** — captures from real frameworks (LangGraph, CrewAI,
|
|
90
|
+
Pydantic AI, Agno, Langflow `stream_protocol=agui`). Contributions welcome;
|
|
91
|
+
sanitise credentials before submitting.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI001",
|
|
4
|
+
"severity": "error",
|
|
5
|
+
"message": "First event of the run is TEXT_MESSAGE_START; expected RUN_STARTED",
|
|
6
|
+
"eventIndex": 0,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#runstarted",
|
|
8
|
+
"eventType": "TEXT_MESSAGE_START"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{"type":"TEXT_MESSAGE_START","messageId":"msg_001","role":"assistant","timestamp":1755300007900}
|
|
2
|
+
{"type":"TEXT_MESSAGE_CONTENT","messageId":"msg_001","delta":"Hello without a run.","timestamp":1755300008000}
|
|
3
|
+
{"type":"TEXT_MESSAGE_END","messageId":"msg_001","timestamp":1755300008100}
|
|
4
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","timestamp":1755300008200}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI002",
|
|
4
|
+
"severity": "error",
|
|
5
|
+
"message": "Duplicate RUN_STARTED (runId 'run_002') before the active run terminated",
|
|
6
|
+
"eventIndex": 1,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#lifecycle-events",
|
|
8
|
+
"eventType": "RUN_STARTED",
|
|
9
|
+
"relatedEventIndex": 0
|
|
10
|
+
}
|
|
11
|
+
]
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300008300}
|
|
2
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_002","timestamp":1755300008400}
|
|
3
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","outcome":{"type":"success"},"timestamp":1755300008500}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300008600}
|
|
2
|
+
{"type":"TEXT_MESSAGE_START","messageId":"msg_001","role":"assistant","timestamp":1755300008700}
|
|
3
|
+
{"type":"TEXT_MESSAGE_CONTENT","messageId":"msg_001","delta":"This run just stops.","timestamp":1755300008800}
|
|
4
|
+
{"type":"TEXT_MESSAGE_END","messageId":"msg_001","timestamp":1755300008900}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI004",
|
|
4
|
+
"severity": "error",
|
|
5
|
+
"message": "TEXT_MESSAGE_START follows the run's terminal RUN_FINISHED",
|
|
6
|
+
"eventIndex": 2,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#lifecycle-events",
|
|
8
|
+
"eventType": "TEXT_MESSAGE_START",
|
|
9
|
+
"relatedEventIndex": 1
|
|
10
|
+
}
|
|
11
|
+
]
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300009000}
|
|
2
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","outcome":{"type":"success"},"timestamp":1755300009100}
|
|
3
|
+
{"type":"TEXT_MESSAGE_START","messageId":"msg_001","role":"assistant","timestamp":1755300009200}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI005",
|
|
4
|
+
"severity": "error",
|
|
5
|
+
"message": "RUN_ERROR emitted after the run already terminated with RUN_FINISHED",
|
|
6
|
+
"eventIndex": 2,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#runfinished",
|
|
8
|
+
"eventType": "RUN_ERROR",
|
|
9
|
+
"relatedEventIndex": 1
|
|
10
|
+
}
|
|
11
|
+
]
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300009300}
|
|
2
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","outcome":{"type":"success"},"timestamp":1755300009400}
|
|
3
|
+
{"type":"RUN_ERROR","message":"boom","timestamp":1755300009500}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI006",
|
|
4
|
+
"severity": "error",
|
|
5
|
+
"message": "STEP_FINISHED 'plan' has no open STEP_STARTED",
|
|
6
|
+
"eventIndex": 1,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#stepfinished",
|
|
8
|
+
"eventType": "STEP_FINISHED",
|
|
9
|
+
"pointer": "/stepName"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300009600}
|
|
2
|
+
{"type":"STEP_FINISHED","stepName":"plan","timestamp":1755300009700}
|
|
3
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","outcome":{"type":"success"},"timestamp":1755300009800}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300009900}
|
|
2
|
+
{"type":"STEP_STARTED","stepName":"plan","timestamp":1755300010000}
|
|
3
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","outcome":{"type":"success"},"timestamp":1755300010100}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI008",
|
|
4
|
+
"severity": "warning",
|
|
5
|
+
"message": "RUN_FINISHED runId 'run_999' does not match RUN_STARTED runId 'run_001'",
|
|
6
|
+
"eventIndex": 1,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#runstarted",
|
|
8
|
+
"eventType": "RUN_FINISHED",
|
|
9
|
+
"pointer": "/runId",
|
|
10
|
+
"relatedEventIndex": 0
|
|
11
|
+
}
|
|
12
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI101",
|
|
4
|
+
"severity": "error",
|
|
5
|
+
"message": "TEXT_MESSAGE_CONTENT for messageId 'msg_ghost' with no open TEXT_MESSAGE_START",
|
|
6
|
+
"eventIndex": 1,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#text-message-events",
|
|
8
|
+
"eventType": "TEXT_MESSAGE_CONTENT",
|
|
9
|
+
"pointer": "/messageId"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300010400}
|
|
2
|
+
{"type":"TEXT_MESSAGE_CONTENT","messageId":"msg_ghost","delta":"orphan","timestamp":1755300010500}
|
|
3
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","outcome":{"type":"success"},"timestamp":1755300010600}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI102",
|
|
4
|
+
"severity": "error",
|
|
5
|
+
"message": "TEXT_MESSAGE_END for messageId 'msg_ghost' with no open TEXT_MESSAGE_START",
|
|
6
|
+
"eventIndex": 1,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#textmessageend",
|
|
8
|
+
"eventType": "TEXT_MESSAGE_END",
|
|
9
|
+
"pointer": "/messageId"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300010700}
|
|
2
|
+
{"type":"TEXT_MESSAGE_END","messageId":"msg_ghost","timestamp":1755300010800}
|
|
3
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","outcome":{"type":"success"},"timestamp":1755300010900}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300011000}
|
|
2
|
+
{"type":"TEXT_MESSAGE_START","messageId":"msg_001","role":"assistant","timestamp":1755300011100}
|
|
3
|
+
{"type":"TEXT_MESSAGE_CONTENT","messageId":"msg_001","delta":"never ends","timestamp":1755300011200}
|
|
4
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","outcome":{"type":"success"},"timestamp":1755300011300}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI104",
|
|
4
|
+
"severity": "error",
|
|
5
|
+
"message": "messageId 'msg_001' was already used by a completed message",
|
|
6
|
+
"eventIndex": 4,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#textmessagestart",
|
|
8
|
+
"eventType": "TEXT_MESSAGE_START",
|
|
9
|
+
"pointer": "/messageId",
|
|
10
|
+
"relatedEventIndex": 3
|
|
11
|
+
}
|
|
12
|
+
]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300011400}
|
|
2
|
+
{"type":"TEXT_MESSAGE_START","messageId":"msg_001","role":"assistant","timestamp":1755300011500}
|
|
3
|
+
{"type":"TEXT_MESSAGE_CONTENT","messageId":"msg_001","delta":"first use","timestamp":1755300011600}
|
|
4
|
+
{"type":"TEXT_MESSAGE_END","messageId":"msg_001","timestamp":1755300011700}
|
|
5
|
+
{"type":"TEXT_MESSAGE_START","messageId":"msg_001","role":"assistant","timestamp":1755300011800}
|
|
6
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","outcome":{"type":"success"},"timestamp":1755300011900}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI105",
|
|
4
|
+
"severity": "warning",
|
|
5
|
+
"message": "TEXT_MESSAGE_CONTENT for messageId 'msg_001' has an empty delta",
|
|
6
|
+
"eventIndex": 2,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#textmessagecontent",
|
|
8
|
+
"eventType": "TEXT_MESSAGE_CONTENT",
|
|
9
|
+
"pointer": "/delta",
|
|
10
|
+
"relatedEventIndex": 1
|
|
11
|
+
}
|
|
12
|
+
]
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{"type":"RUN_STARTED","threadId":"thread_001","runId":"run_001","timestamp":1755300012000}
|
|
2
|
+
{"type":"TEXT_MESSAGE_START","messageId":"msg_001","role":"assistant","timestamp":1755300012100}
|
|
3
|
+
{"type":"TEXT_MESSAGE_CONTENT","messageId":"msg_001","delta":"","timestamp":1755300012200}
|
|
4
|
+
{"type":"TEXT_MESSAGE_END","messageId":"msg_001","timestamp":1755300012300}
|
|
5
|
+
{"type":"RUN_FINISHED","threadId":"thread_001","runId":"run_001","outcome":{"type":"success"},"timestamp":1755300012400}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"rule": "AGUI106",
|
|
4
|
+
"severity": "error",
|
|
5
|
+
"message": "TEXT_MESSAGE_START for messageId 'msg_001', which is already open",
|
|
6
|
+
"eventIndex": 2,
|
|
7
|
+
"specUrl": "https://docs.ag-ui.com/concepts/events#implementation-considerations",
|
|
8
|
+
"eventType": "TEXT_MESSAGE_START",
|
|
9
|
+
"pointer": "/messageId",
|
|
10
|
+
"relatedEventIndex": 1
|
|
11
|
+
}
|
|
12
|
+
]
|