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.
Files changed (168) hide show
  1. package/README.md +82 -23
  2. package/dist/{catalog-BglXBNbL.js → catalog-3e6INLCp.js} +122 -4
  3. package/dist/catalog-3e6INLCp.js.map +1 -0
  4. package/dist/{catalog-Ci9dqc1a.cjs → catalog-DH5gDIbo.cjs} +122 -4
  5. package/dist/catalog-DH5gDIbo.cjs.map +1 -0
  6. package/dist/cli.js +641 -63
  7. package/dist/cli.js.map +1 -1
  8. package/dist/{index-Hmqj3r_r.d.cts → index-Cydfs-rB.d.ts} +4 -4
  9. package/dist/{index-oNG1kOp9.d.ts → index-DsVmjcLO.d.cts} +4 -4
  10. package/dist/index.cjs +2 -2
  11. package/dist/index.d.cts +2 -2
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.js +2 -2
  14. package/dist/report.cjs +11 -2
  15. package/dist/report.cjs.map +1 -1
  16. package/dist/report.d.cts +21 -12
  17. package/dist/report.d.ts +21 -12
  18. package/dist/report.js +11 -2
  19. package/dist/report.js.map +1 -1
  20. package/dist/{src-rGZ2G4qA.js → src-CezZ73Mm.js} +494 -59
  21. package/dist/src-CezZ73Mm.js.map +1 -0
  22. package/dist/{src-HmI-kxef.cjs → src-CyugLoks.cjs} +494 -59
  23. package/dist/src-CyugLoks.cjs.map +1 -0
  24. package/dist/transport.cjs +1 -1
  25. package/dist/transport.cjs.map +1 -1
  26. package/dist/transport.d.cts +1 -1
  27. package/dist/transport.d.ts +1 -1
  28. package/dist/transport.js +1 -1
  29. package/dist/transport.js.map +1 -1
  30. package/dist/{types-oH_QTnn2.d.ts → types-CUIgyj2N.d.cts} +3 -1
  31. package/dist/{types-oH_QTnn2.d.cts → types-CUIgyj2N.d.ts} +3 -1
  32. package/dist/vitest.js +616 -61
  33. package/dist/vitest.js.map +1 -1
  34. package/{src → js/src}/cli-args.ts +16 -0
  35. package/{src → js/src}/cli.ts +1 -1
  36. package/{src → js/src}/index.ts +5 -0
  37. package/{src → js/src}/protocol/event-table.ts +93 -2
  38. package/js/src/report/json.ts +31 -0
  39. package/{src → js/src}/report/sarif.ts +3 -6
  40. package/{src → js/src}/rules/catalog.ts +5 -1
  41. package/{src → js/src}/rules/checks/context.ts +48 -3
  42. package/{src → js/src}/rules/checks/lifecycle.ts +5 -2
  43. package/js/src/rules/checks/subagents.ts +78 -0
  44. package/{src → js/src}/rules/checks/text.ts +11 -3
  45. package/{src → js/src}/rules/checks/toolcalls.ts +8 -2
  46. package/{src → js/src}/transport/index.ts +1 -1
  47. package/package.json +15 -14
  48. package/{src/rules → spec}/catalog.json +109 -2
  49. package/spec/event-categories.json +60 -0
  50. package/spec/fixtures/README.md +91 -0
  51. package/spec/fixtures/invalid/AGUI001-first-event-not-run-started/expected.json +10 -0
  52. package/spec/fixtures/invalid/AGUI001-first-event-not-run-started/stream.jsonl +4 -0
  53. package/spec/fixtures/invalid/AGUI002-duplicate-run-started/expected.json +11 -0
  54. package/spec/fixtures/invalid/AGUI002-duplicate-run-started/stream.jsonl +3 -0
  55. package/spec/fixtures/invalid/AGUI003-run-never-terminated/expected.json +10 -0
  56. package/spec/fixtures/invalid/AGUI003-run-never-terminated/stream.jsonl +4 -0
  57. package/spec/fixtures/invalid/AGUI004-event-after-terminal/expected.json +11 -0
  58. package/spec/fixtures/invalid/AGUI004-event-after-terminal/stream.jsonl +3 -0
  59. package/spec/fixtures/invalid/AGUI005-finished-and-error/expected.json +11 -0
  60. package/spec/fixtures/invalid/AGUI005-finished-and-error/stream.jsonl +3 -0
  61. package/spec/fixtures/invalid/AGUI006-step-finished-unmatched/expected.json +11 -0
  62. package/spec/fixtures/invalid/AGUI006-step-finished-unmatched/stream.jsonl +3 -0
  63. package/spec/fixtures/invalid/AGUI007-step-unterminated/expected.json +10 -0
  64. package/spec/fixtures/invalid/AGUI007-step-unterminated/stream.jsonl +3 -0
  65. package/spec/fixtures/invalid/AGUI008-unstable-run-ids/expected.json +12 -0
  66. package/spec/fixtures/invalid/AGUI008-unstable-run-ids/stream.jsonl +2 -0
  67. package/spec/fixtures/invalid/AGUI101-content-without-start/expected.json +11 -0
  68. package/spec/fixtures/invalid/AGUI101-content-without-start/stream.jsonl +3 -0
  69. package/spec/fixtures/invalid/AGUI102-end-without-start/expected.json +11 -0
  70. package/spec/fixtures/invalid/AGUI102-end-without-start/stream.jsonl +3 -0
  71. package/spec/fixtures/invalid/AGUI103-message-unterminated/expected.json +10 -0
  72. package/spec/fixtures/invalid/AGUI103-message-unterminated/stream.jsonl +4 -0
  73. package/spec/fixtures/invalid/AGUI104-duplicate-message-id/expected.json +12 -0
  74. package/spec/fixtures/invalid/AGUI104-duplicate-message-id/stream.jsonl +6 -0
  75. package/spec/fixtures/invalid/AGUI105-empty-content-delta/expected.json +12 -0
  76. package/spec/fixtures/invalid/AGUI105-empty-content-delta/stream.jsonl +5 -0
  77. package/spec/fixtures/invalid/AGUI106-interleaved-same-message-id/expected.json +12 -0
  78. package/spec/fixtures/invalid/AGUI106-interleaved-same-message-id/stream.jsonl +5 -0
  79. package/spec/fixtures/invalid/AGUI201-args-without-start/expected.json +11 -0
  80. package/spec/fixtures/invalid/AGUI201-args-without-start/stream.jsonl +3 -0
  81. package/spec/fixtures/invalid/AGUI202-end-without-start/expected.json +11 -0
  82. package/spec/fixtures/invalid/AGUI202-end-without-start/stream.jsonl +3 -0
  83. package/spec/fixtures/invalid/AGUI203-unterminated-tool-call/expected.json +10 -0
  84. package/spec/fixtures/invalid/AGUI203-unterminated-tool-call/stream.jsonl +4 -0
  85. package/spec/fixtures/invalid/AGUI204-args-not-json/expected.json +10 -0
  86. package/spec/fixtures/invalid/AGUI204-args-not-json/stream.jsonl +6 -0
  87. package/spec/fixtures/invalid/AGUI205-duplicate-tool-call-id/expected.json +12 -0
  88. package/spec/fixtures/invalid/AGUI205-duplicate-tool-call-id/stream.jsonl +5 -0
  89. package/spec/fixtures/invalid/AGUI206-result-before-end/expected.json +12 -0
  90. package/spec/fixtures/invalid/AGUI206-result-before-end/stream.jsonl +5 -0
  91. package/spec/fixtures/invalid/AGUI207-result-unknown-id/expected.json +11 -0
  92. package/spec/fixtures/invalid/AGUI207-result-unknown-id/stream.jsonl +3 -0
  93. package/spec/fixtures/invalid/AGUI208-unknown-parent-message-id/expected.json +11 -0
  94. package/spec/fixtures/invalid/AGUI208-unknown-parent-message-id/stream.jsonl +4 -0
  95. package/spec/fixtures/invalid/AGUI301-delta-before-snapshot/expected.json +10 -0
  96. package/spec/fixtures/invalid/AGUI301-delta-before-snapshot/stream.jsonl +3 -0
  97. package/spec/fixtures/invalid/AGUI302-delta-failed-to-apply/expected.json +11 -0
  98. package/spec/fixtures/invalid/AGUI302-delta-failed-to-apply/stream.jsonl +4 -0
  99. package/spec/fixtures/invalid/AGUI303-invalid-patch-document/expected.json +11 -0
  100. package/spec/fixtures/invalid/AGUI303-invalid-patch-document/stream.jsonl +4 -0
  101. package/spec/fixtures/invalid/AGUI304-midrun-snapshot-discards-deltas/expected.json +10 -0
  102. package/spec/fixtures/invalid/AGUI304-midrun-snapshot-discards-deltas/stream.jsonl +5 -0
  103. package/spec/fixtures/invalid/AGUI305-shared-state-never-established/expected.json +9 -0
  104. package/spec/fixtures/invalid/AGUI305-shared-state-never-established/options.json +5 -0
  105. package/spec/fixtures/invalid/AGUI305-shared-state-never-established/stream.jsonl +5 -0
  106. package/spec/fixtures/invalid/AGUI401-reasoning-content-without-start/expected.json +11 -0
  107. package/spec/fixtures/invalid/AGUI401-reasoning-content-without-start/stream.jsonl +3 -0
  108. package/spec/fixtures/invalid/AGUI402-reasoning-unterminated/expected.json +10 -0
  109. package/spec/fixtures/invalid/AGUI402-reasoning-unterminated/stream.jsonl +3 -0
  110. package/spec/fixtures/invalid/AGUI501-missing-data-prefix/expected.json +9 -0
  111. package/spec/fixtures/invalid/AGUI501-missing-data-prefix/scenario.json +17 -0
  112. package/spec/fixtures/invalid/AGUI502-payload-not-json/expected.json +10 -0
  113. package/spec/fixtures/invalid/AGUI502-payload-not-json/stream.jsonl +3 -0
  114. package/spec/fixtures/invalid/AGUI503-unknown-event-type/expected.json +10 -0
  115. package/spec/fixtures/invalid/AGUI503-unknown-event-type/stream.jsonl +3 -0
  116. package/spec/fixtures/invalid/AGUI504-schema-violation/expected.json +11 -0
  117. package/spec/fixtures/invalid/AGUI504-schema-violation/stream.jsonl +4 -0
  118. package/spec/fixtures/invalid/AGUI505-unexpected-content-type/expected.json +9 -0
  119. package/spec/fixtures/invalid/AGUI505-unexpected-content-type/scenario.json +25 -0
  120. package/spec/fixtures/invalid/AGUI506-keepalive-gap/expected.json +9 -0
  121. package/spec/fixtures/invalid/AGUI506-keepalive-gap/scenario.json +25 -0
  122. package/spec/fixtures/invalid/AGUI507-buffered-response/expected.json +9 -0
  123. package/spec/fixtures/invalid/AGUI507-buffered-response/scenario.json +9 -0
  124. package/spec/fixtures/invalid/AGUI508-connection-dropped/expected.json +17 -0
  125. package/spec/fixtures/invalid/AGUI508-connection-dropped/scenario.json +10 -0
  126. package/spec/fixtures/invalid/AGUI601-duplicate-subagent-started/expected.json +12 -0
  127. package/spec/fixtures/invalid/AGUI601-duplicate-subagent-started/stream.jsonl +5 -0
  128. package/spec/fixtures/invalid/AGUI602-finished-without-start/expected.json +11 -0
  129. package/spec/fixtures/invalid/AGUI602-finished-without-start/stream.jsonl +3 -0
  130. package/spec/fixtures/invalid/AGUI603-error-without-start/expected.json +11 -0
  131. package/spec/fixtures/invalid/AGUI603-error-without-start/stream.jsonl +3 -0
  132. package/spec/fixtures/invalid/AGUI604-subagent-unterminated/expected.json +10 -0
  133. package/spec/fixtures/invalid/AGUI604-subagent-unterminated/stream.jsonl +3 -0
  134. package/spec/fixtures/invalid/AGUI605-unknown-parent-subagent-run-id/expected.json +11 -0
  135. package/spec/fixtures/invalid/AGUI605-unknown-parent-subagent-run-id/stream.jsonl +4 -0
  136. package/spec/fixtures/invalid/AGUI606-continuation-owner-mismatch/expected.json +11 -0
  137. package/spec/fixtures/invalid/AGUI606-continuation-owner-mismatch/stream.jsonl +6 -0
  138. package/spec/fixtures/invalid/AGUI901-raw-wraps-typed-event/expected.json +11 -0
  139. package/spec/fixtures/invalid/AGUI901-raw-wraps-typed-event/stream.jsonl +3 -0
  140. package/spec/fixtures/invalid/AGUI902-no-timestamps/expected.json +9 -0
  141. package/spec/fixtures/invalid/AGUI902-no-timestamps/stream.jsonl +2 -0
  142. package/spec/fixtures/invalid/AGUI903-custom-name-not-namespaced/expected.json +11 -0
  143. package/spec/fixtures/invalid/AGUI903-custom-name-not-namespaced/stream.jsonl +3 -0
  144. package/spec/fixtures/valid/agentic-chat.jsonl +14 -0
  145. package/spec/fixtures/valid/agentic-generative-ui.jsonl +13 -0
  146. package/spec/fixtures/valid/backend-tool-rendering.jsonl +14 -0
  147. package/spec/fixtures/valid/human-in-the-loop.jsonl +13 -0
  148. package/spec/fixtures/valid/predictive-state-updates.expected.json +11 -0
  149. package/spec/fixtures/valid/predictive-state-updates.jsonl +11 -0
  150. package/spec/fixtures/valid/shared-state.jsonl +8 -0
  151. package/spec/fixtures/valid/tool-based-generative-ui.jsonl +5 -0
  152. package/dist/catalog-BglXBNbL.js.map +0 -1
  153. package/dist/catalog-Ci9dqc1a.cjs.map +0 -1
  154. package/dist/src-HmI-kxef.cjs.map +0 -1
  155. package/dist/src-rGZ2G4qA.js.map +0 -1
  156. package/src/report/json.ts +0 -20
  157. /package/{src → js/src}/protocol/jsonpatch.ts +0 -0
  158. /package/{src → js/src}/report/index.ts +0 -0
  159. /package/{src → js/src}/report/junit.ts +0 -0
  160. /package/{src → js/src}/report/pretty.ts +0 -0
  161. /package/{src → js/src}/rules/checks/reasoning.ts +0 -0
  162. /package/{src → js/src}/rules/checks/state.ts +0 -0
  163. /package/{src → js/src}/rules/checks/transport.ts +0 -0
  164. /package/{src → js/src}/transport/ndjson.ts +0 -0
  165. /package/{src → js/src}/transport/sse.ts +0 -0
  166. /package/{src → js/src}/types.ts +0 -0
  167. /package/{src → js/src}/vitest/index.ts +0 -0
  168. /package/{src → js/src}/vitest/matcher.ts +0 -0
package/dist/cli.js CHANGED
@@ -24,6 +24,7 @@ Rules:
24
24
  --off AGUI### shorthand for --rule AGUI###=off
25
25
  --features <a,b,...> declare exercised features (enables e.g. AGUI305)
26
26
  --max-warnings <n> exit 1 when warnings exceed n
27
+ --fail-on <error|warning|none> severity that triggers a nonzero exit (default: error)
27
28
 
28
29
  Endpoint options:
29
30
  --header "Name: value" extra request header (repeatable)
@@ -37,6 +38,11 @@ const SEVERITIES$1 = /* @__PURE__ */ new Set([
37
38
  "info",
38
39
  "off"
39
40
  ]);
41
+ const FAIL_ON_VALUES = /* @__PURE__ */ new Set([
42
+ "error",
43
+ "warning",
44
+ "none"
45
+ ]);
40
46
  const RULE_ID = /^AGUI\d{3}$/;
41
47
  const FLAGS = {
42
48
  "--help": {
@@ -97,6 +103,14 @@ const FLAGS = {
97
103
  return null;
98
104
  }
99
105
  },
106
+ "--fail-on": {
107
+ takesValue: true,
108
+ apply: (c, v) => {
109
+ if (!FAIL_ON_VALUES.has(v)) return `--fail-on expects error, warning, or none, got '${v}'`;
110
+ c.failOn = v;
111
+ return null;
112
+ }
113
+ },
100
114
  "--timeout": {
101
115
  takesValue: true,
102
116
  apply: (c, v) => {
@@ -206,22 +220,25 @@ function parseCliArgs(argv) {
206
220
  config
207
221
  };
208
222
  }
209
- function decideExitCode(summary, maxWarnings) {
223
+ function decideExitCode(summary, maxWarnings, failOn = "error") {
224
+ if (failOn === "none") return 0;
210
225
  if (summary.errors > 0) return 1;
226
+ if (failOn === "warning" && summary.warnings > 0) return 1;
211
227
  if (maxWarnings !== void 0 && summary.warnings > maxWarnings) return 1;
212
228
  return 0;
213
229
  }
214
230
  //#endregion
215
- //#region src/rules/catalog.json
231
+ //#region ../spec/catalog.json
216
232
  var catalog_default = {
217
233
  $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.",
218
- catalogVersion: "0.1.0",
234
+ catalogVersion: "0.4.0",
219
235
  spec: "0.x",
220
236
  rules: [
221
237
  {
222
238
  "id": "AGUI001",
223
239
  "severity": "error",
224
240
  "title": "Run does not start with RUN_STARTED",
241
+ "category": "lifecycle",
225
242
  "messageTemplate": "First event of the run is {type}; expected RUN_STARTED",
226
243
  "specUrl": "https://docs.ag-ui.com/concepts/events#runstarted",
227
244
  "specQuote": "The RunStarted event is the first event emitted when an agent begins processing a request.",
@@ -232,6 +249,7 @@ var catalog_default = {
232
249
  "id": "AGUI002",
233
250
  "severity": "error",
234
251
  "title": "Multiple RUN_STARTED in one run",
252
+ "category": "lifecycle",
235
253
  "messageTemplate": "Duplicate RUN_STARTED (runId '{runId}') before the active run terminated",
236
254
  "specUrl": "https://docs.ag-ui.com/concepts/events#lifecycle-events",
237
255
  "specQuote": "The RunStarted and either RunFinished or RunError events are mandatory, forming the boundaries of an agent run.",
@@ -242,6 +260,7 @@ var catalog_default = {
242
260
  "id": "AGUI003",
243
261
  "severity": "error",
244
262
  "title": "Run not terminated",
263
+ "category": "lifecycle",
245
264
  "messageTemplate": "Run '{runId}' ended without RUN_FINISHED or RUN_ERROR",
246
265
  "specUrl": "https://docs.ag-ui.com/concepts/events#runfinished",
247
266
  "specQuote": "Every run terminates with either RunFinished or RunError.",
@@ -252,6 +271,7 @@ var catalog_default = {
252
271
  "id": "AGUI004",
253
272
  "severity": "error",
254
273
  "title": "Event after terminal event",
274
+ "category": "lifecycle",
255
275
  "messageTemplate": "{type} follows the run's terminal {terminalType}",
256
276
  "specUrl": "https://docs.ag-ui.com/concepts/events#lifecycle-events",
257
277
  "specQuote": "The RunStarted and either RunFinished or RunError events are mandatory, forming the boundaries of an agent run.",
@@ -262,6 +282,7 @@ var catalog_default = {
262
282
  "id": "AGUI005",
263
283
  "severity": "error",
264
284
  "title": "RUN_FINISHED and RUN_ERROR are mutually exclusive",
285
+ "category": "lifecycle",
265
286
  "messageTemplate": "{type} emitted after the run already terminated with {terminalType}",
266
287
  "specUrl": "https://docs.ag-ui.com/concepts/events#runfinished",
267
288
  "specQuote": "Every run terminates with either RunFinished or RunError.",
@@ -272,6 +293,7 @@ var catalog_default = {
272
293
  "id": "AGUI006",
273
294
  "severity": "error",
274
295
  "title": "STEP_FINISHED without matching STEP_STARTED",
296
+ "category": "lifecycle",
275
297
  "messageTemplate": "STEP_FINISHED '{stepName}' has no open STEP_STARTED",
276
298
  "specUrl": "https://docs.ag-ui.com/concepts/events#stepfinished",
277
299
  "specQuote": "The stepName must match the corresponding StepStarted event.",
@@ -282,6 +304,7 @@ var catalog_default = {
282
304
  "id": "AGUI007",
283
305
  "severity": "error",
284
306
  "title": "Step unterminated at run end",
307
+ "category": "lifecycle",
285
308
  "messageTemplate": "STEP_STARTED '{stepName}' never finished",
286
309
  "specUrl": "https://docs.ag-ui.com/concepts/events#stepstarted",
287
310
  "specQuote": "The stepName must match the corresponding StepStarted event.",
@@ -292,6 +315,7 @@ var catalog_default = {
292
315
  "id": "AGUI008",
293
316
  "severity": "warning",
294
317
  "title": "Unstable threadId/runId across the run",
318
+ "category": "lifecycle",
295
319
  "messageTemplate": "RUN_FINISHED {field} '{actual}' does not match RUN_STARTED {field} '{expected}'",
296
320
  "specUrl": "https://docs.ag-ui.com/concepts/events#runstarted",
297
321
  "specQuote": "It also provides crucial identifiers that can be used to associate subsequent events with this specific run.",
@@ -302,6 +326,7 @@ var catalog_default = {
302
326
  "id": "AGUI101",
303
327
  "severity": "error",
304
328
  "title": "TEXT_MESSAGE_CONTENT without start",
329
+ "category": "text",
305
330
  "messageTemplate": "TEXT_MESSAGE_CONTENT for messageId '{messageId}' with no open TEXT_MESSAGE_START",
306
331
  "specUrl": "https://docs.ag-ui.com/concepts/events#text-message-events",
307
332
  "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.",
@@ -313,6 +338,7 @@ var catalog_default = {
313
338
  "id": "AGUI102",
314
339
  "severity": "error",
315
340
  "title": "TEXT_MESSAGE_END without start",
341
+ "category": "text",
316
342
  "messageTemplate": "TEXT_MESSAGE_END for messageId '{messageId}' with no open TEXT_MESSAGE_START",
317
343
  "specUrl": "https://docs.ag-ui.com/concepts/events#textmessageend",
318
344
  "specQuote": "messageId: Matches the ID from TextMessageStart",
@@ -324,6 +350,7 @@ var catalog_default = {
324
350
  "id": "AGUI103",
325
351
  "severity": "error",
326
352
  "title": "Text message unterminated at run end",
353
+ "category": "text",
327
354
  "messageTemplate": "TEXT_MESSAGE_START messageId '{messageId}' never ended",
328
355
  "specUrl": "https://docs.ag-ui.com/concepts/events#text-message-events",
329
356
  "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.",
@@ -335,6 +362,7 @@ var catalog_default = {
335
362
  "id": "AGUI104",
336
363
  "severity": "error",
337
364
  "title": "Duplicate messageId within a run",
365
+ "category": "text",
338
366
  "messageTemplate": "messageId '{messageId}' was already used by a completed message",
339
367
  "specUrl": "https://docs.ag-ui.com/concepts/events#textmessagestart",
340
368
  "specQuote": "It establishes a unique messageId that will be referenced by subsequent content chunks and the end event.",
@@ -346,6 +374,7 @@ var catalog_default = {
346
374
  "id": "AGUI105",
347
375
  "severity": "warning",
348
376
  "title": "Empty content delta",
377
+ "category": "text",
349
378
  "messageTemplate": "TEXT_MESSAGE_CONTENT for messageId '{messageId}' has an empty delta",
350
379
  "specUrl": "https://docs.ag-ui.com/concepts/events#textmessagecontent",
351
380
  "specQuote": "delta: Text content chunk (non-empty)",
@@ -358,6 +387,7 @@ var catalog_default = {
358
387
  "id": "AGUI106",
359
388
  "severity": "error",
360
389
  "title": "Interleaved message streams sharing a messageId",
390
+ "category": "text",
361
391
  "messageTemplate": "TEXT_MESSAGE_START for messageId '{messageId}', which is already open",
362
392
  "specUrl": "https://docs.ag-ui.com/concepts/events#implementation-considerations",
363
393
  "specQuote": "Events with the same ID (e.g., messageId, toolCallId) belong to the same logical stream",
@@ -369,6 +399,7 @@ var catalog_default = {
369
399
  "id": "AGUI201",
370
400
  "severity": "error",
371
401
  "title": "TOOL_CALL_ARGS without start",
402
+ "category": "toolcall",
372
403
  "messageTemplate": "TOOL_CALL_ARGS for toolCallId '{toolCallId}' with no open TOOL_CALL_START",
373
404
  "specUrl": "https://docs.ag-ui.com/concepts/events#tool-call-events",
374
405
  "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.",
@@ -380,6 +411,7 @@ var catalog_default = {
380
411
  "id": "AGUI202",
381
412
  "severity": "error",
382
413
  "title": "TOOL_CALL_END without start",
414
+ "category": "toolcall",
383
415
  "messageTemplate": "TOOL_CALL_END for toolCallId '{toolCallId}' with no open TOOL_CALL_START",
384
416
  "specUrl": "https://docs.ag-ui.com/concepts/events#toolcallend",
385
417
  "specQuote": "toolCallId: Matches the ID from ToolCallStart",
@@ -391,6 +423,7 @@ var catalog_default = {
391
423
  "id": "AGUI203",
392
424
  "severity": "error",
393
425
  "title": "Unterminated tool call",
426
+ "category": "toolcall",
394
427
  "messageTemplate": "TOOL_CALL_START id '{toolCallId}' never terminated",
395
428
  "specUrl": "https://docs.ag-ui.com/concepts/events#tool-call-events",
396
429
  "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.",
@@ -402,6 +435,7 @@ var catalog_default = {
402
435
  "id": "AGUI204",
403
436
  "severity": "error",
404
437
  "title": "Tool call arguments are not valid JSON",
438
+ "category": "toolcall",
405
439
  "messageTemplate": "Concatenated TOOL_CALL_ARGS for toolCallId '{toolCallId}' do not parse as JSON: {error}",
406
440
  "specUrl": "https://docs.ag-ui.com/concepts/events#toolcallargs",
407
441
  "specQuote": "Frontends should concatenate these deltas in the order received to construct the complete arguments object.",
@@ -413,6 +447,7 @@ var catalog_default = {
413
447
  "id": "AGUI205",
414
448
  "severity": "error",
415
449
  "title": "Duplicate toolCallId within a run",
450
+ "category": "toolcall",
416
451
  "messageTemplate": "toolCallId '{toolCallId}' was already used by a completed tool call",
417
452
  "specUrl": "https://docs.ag-ui.com/concepts/events#toolcallstart",
418
453
  "specQuote": "toolCallId: Unique identifier for the tool call",
@@ -424,6 +459,7 @@ var catalog_default = {
424
459
  "id": "AGUI206",
425
460
  "severity": "warning",
426
461
  "title": "TOOL_CALL_RESULT before TOOL_CALL_END",
462
+ "category": "toolcall",
427
463
  "messageTemplate": "TOOL_CALL_RESULT for toolCallId '{toolCallId}' arrived while the call is still open",
428
464
  "specUrl": "https://docs.ag-ui.com/concepts/events#toolcallresult",
429
465
  "specQuote": "This event is sent after the tool has been executed by the system and contains the actual output generated by the tool.",
@@ -435,6 +471,7 @@ var catalog_default = {
435
471
  "id": "AGUI207",
436
472
  "severity": "error",
437
473
  "title": "TOOL_CALL_RESULT references unknown toolCallId",
474
+ "category": "toolcall",
438
475
  "messageTemplate": "TOOL_CALL_RESULT references toolCallId '{toolCallId}', which was never started",
439
476
  "specUrl": "https://docs.ag-ui.com/concepts/events#toolcallresult",
440
477
  "specQuote": "toolCallId: Matches the ID from the corresponding ToolCallStart event",
@@ -446,6 +483,7 @@ var catalog_default = {
446
483
  "id": "AGUI208",
447
484
  "severity": "info",
448
485
  "title": "parentMessageId references unknown message",
486
+ "category": "toolcall",
449
487
  "messageTemplate": "TOOL_CALL_START parentMessageId '{parentMessageId}' matches no message observed in this stream",
450
488
  "specUrl": "https://docs.ag-ui.com/concepts/events#toolcallstart",
451
489
  "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.",
@@ -458,6 +496,7 @@ var catalog_default = {
458
496
  "id": "AGUI301",
459
497
  "severity": "info",
460
498
  "title": "STATE_DELTA before any STATE_SNAPSHOT",
499
+ "category": "state",
461
500
  "messageTemplate": "STATE_DELTA precedes any STATE_SNAPSHOT; the base it applies to is not observable on this stream",
462
501
  "specUrl": "https://docs.ag-ui.com/concepts/events#statesnapshot",
463
502
  "specQuote": "This event is typically sent at the beginning of an interaction or when synchronization is needed.",
@@ -470,6 +509,7 @@ var catalog_default = {
470
509
  "id": "AGUI302",
471
510
  "severity": "error",
472
511
  "title": "STATE_DELTA failed to apply",
512
+ "category": "state",
473
513
  "messageTemplate": "STATE_DELTA failed to apply: {error}",
474
514
  "specUrl": "https://docs.ag-ui.com/concepts/events#statedelta",
475
515
  "specQuote": "Each delta represents specific changes to apply to the current state model.",
@@ -481,6 +521,7 @@ var catalog_default = {
481
521
  "id": "AGUI303",
482
522
  "severity": "error",
483
523
  "title": "STATE_DELTA is not a valid RFC 6902 patch document",
524
+ "category": "state",
484
525
  "messageTemplate": "STATE_DELTA is not a valid RFC 6902 patch document: {error}",
485
526
  "specUrl": "https://docs.ag-ui.com/concepts/events#statedelta",
486
527
  "specQuote": "The StateDelta event contains incremental updates to the agent's state in the form of JSON Patch operations (as defined in RFC 6902).",
@@ -492,6 +533,7 @@ var catalog_default = {
492
533
  "id": "AGUI304",
493
534
  "severity": "info",
494
535
  "title": "Mid-run STATE_SNAPSHOT discards accumulated deltas",
536
+ "category": "state",
495
537
  "messageTemplate": "STATE_SNAPSHOT replaces state previously built from {deltaCount} delta(s)",
496
538
  "specUrl": "https://docs.ag-ui.com/concepts/events#statesnapshot",
497
539
  "specQuote": "This event is typically sent at the beginning of an interaction or when synchronization is needed.",
@@ -503,6 +545,7 @@ var catalog_default = {
503
545
  "id": "AGUI305",
504
546
  "severity": "warning",
505
547
  "title": "Shared state declared but never established",
548
+ "category": "state",
506
549
  "messageTemplate": "features include 'shared-state' but no STATE_SNAPSHOT was emitted",
507
550
  "specUrl": "https://docs.ag-ui.com/concepts/events#state-management-events",
508
551
  "specQuote": "These events are used to manage and synchronize the agent's state with the frontend.",
@@ -515,6 +558,7 @@ var catalog_default = {
515
558
  "id": "AGUI401",
516
559
  "severity": "error",
517
560
  "title": "REASONING_MESSAGE_CONTENT without start",
561
+ "category": "reasoning",
518
562
  "messageTemplate": "REASONING_MESSAGE_CONTENT for messageId '{messageId}' with no open REASONING_MESSAGE_START",
519
563
  "specUrl": "https://docs.ag-ui.com/concepts/events#reasoningmessagecontent",
520
564
  "specQuote": "Multiple content events with the same messageId should be concatenated to form the complete visible reasoning.",
@@ -526,6 +570,7 @@ var catalog_default = {
526
570
  "id": "AGUI402",
527
571
  "severity": "warning",
528
572
  "title": "Reasoning unterminated at run end",
573
+ "category": "reasoning",
529
574
  "messageTemplate": "{startType} messageId '{messageId}' never ended",
530
575
  "specUrl": "https://docs.ag-ui.com/concepts/events#reasoning-events",
531
576
  "specQuote": "Reasoning events support LLM reasoning visibility and continuity, enabling chain-of-thought reasoning while maintaining privacy.",
@@ -537,6 +582,7 @@ var catalog_default = {
537
582
  "id": "AGUI501",
538
583
  "severity": "error",
539
584
  "title": "Malformed SSE framing",
585
+ "category": "transport",
540
586
  "messageTemplate": "Malformed SSE framing: {detail}",
541
587
  "specUrl": "https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation",
542
588
  "since": "0.x",
@@ -546,6 +592,7 @@ var catalog_default = {
546
592
  "id": "AGUI502",
547
593
  "severity": "error",
548
594
  "title": "Event payload is not valid JSON",
595
+ "category": "transport",
549
596
  "messageTemplate": "Event payload is not valid JSON: {error}",
550
597
  "specUrl": "https://docs.ag-ui.com/concepts/events#base-event-properties",
551
598
  "specQuote": "All events share a common set of base properties.",
@@ -556,7 +603,8 @@ var catalog_default = {
556
603
  "id": "AGUI503",
557
604
  "severity": "error",
558
605
  "title": "Unknown event type",
559
- "messageTemplate": "Unknown event type '{type}' (not in @ag-ui/core v{sdkVersion}, and not RAW or CUSTOM)",
606
+ "category": "transport",
607
+ "messageTemplate": "Unknown event type '{type}' (not in the installed AG-UI SDK v{sdkVersion}, and not RAW or CUSTOM)",
560
608
  "specUrl": "https://docs.ag-ui.com/concepts/events#event-types-overview",
561
609
  "specQuote": "Events in the protocol are categorized by their purpose.",
562
610
  "since": "0.x",
@@ -566,6 +614,7 @@ var catalog_default = {
566
614
  "id": "AGUI504",
567
615
  "severity": "error",
568
616
  "title": "Event fails schema validation for its declared type",
617
+ "category": "transport",
569
618
  "messageTemplate": "{type}: {detail}",
570
619
  "specUrl": "https://docs.ag-ui.com/concepts/events#base-event-properties",
571
620
  "specQuote": "All events share a common set of base properties.",
@@ -576,6 +625,7 @@ var catalog_default = {
576
625
  "id": "AGUI505",
577
626
  "severity": "warning",
578
627
  "title": "Unexpected Content-Type",
628
+ "category": "transport",
579
629
  "messageTemplate": "Content-Type '{contentType}' is neither text/event-stream nor application/x-ndjson",
580
630
  "specUrl": "https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model",
581
631
  "since": "0.x",
@@ -585,6 +635,7 @@ var catalog_default = {
585
635
  "id": "AGUI506",
586
636
  "severity": "info",
587
637
  "title": "No keepalive frame within the configured window",
638
+ "category": "transport",
588
639
  "messageTemplate": "No event or keepalive frame for {seconds}s",
589
640
  "specUrl": "https://docs.ag-ui.com/concepts/architecture#standard-http-client",
590
641
  "since": "0.x",
@@ -595,6 +646,7 @@ var catalog_default = {
595
646
  "id": "AGUI507",
596
647
  "severity": "info",
597
648
  "title": "Response appears buffered rather than incrementally flushed",
649
+ "category": "transport",
598
650
  "messageTemplate": "Response appears buffered: {detail}",
599
651
  "specUrl": "https://docs.ag-ui.com/concepts/architecture#standard-http-client",
600
652
  "since": "0.x",
@@ -605,16 +657,85 @@ var catalog_default = {
605
657
  "id": "AGUI508",
606
658
  "severity": "error",
607
659
  "title": "Stream ended without a terminal event",
660
+ "category": "transport",
608
661
  "messageTemplate": "Connection ended mid-run '{runId}' without RUN_FINISHED or RUN_ERROR",
609
662
  "specUrl": "https://docs.ag-ui.com/concepts/events#runfinished",
610
663
  "specQuote": "Every run terminates with either RunFinished or RunError.",
611
664
  "since": "0.x",
612
665
  "checkedIn": "transport"
613
666
  },
667
+ {
668
+ "id": "AGUI601",
669
+ "severity": "error",
670
+ "title": "Duplicate SUBAGENT_STARTED for a subagentRunId",
671
+ "category": "subagent",
672
+ "messageTemplate": "SUBAGENT_STARTED reuses subagentRunId '{subagentRunId}', which is already in use this run",
673
+ "specUrl": "https://docs.ag-ui.com/concepts/subagents#rules-clients-enforce",
674
+ "specQuote": "a subagent is not started twice within a run",
675
+ "since": "0.x",
676
+ "checkedIn": "core"
677
+ },
678
+ {
679
+ "id": "AGUI602",
680
+ "severity": "error",
681
+ "title": "SUBAGENT_FINISHED without matching SUBAGENT_STARTED",
682
+ "category": "subagent",
683
+ "messageTemplate": "SUBAGENT_FINISHED for subagentRunId '{subagentRunId}' with no open SUBAGENT_STARTED",
684
+ "specUrl": "https://docs.ag-ui.com/concepts/subagents#rules-clients-enforce",
685
+ "specQuote": "SubagentFinished and SubagentError name a subagent that is currently active",
686
+ "since": "0.x",
687
+ "checkedIn": "core"
688
+ },
689
+ {
690
+ "id": "AGUI603",
691
+ "severity": "error",
692
+ "title": "SUBAGENT_ERROR without matching SUBAGENT_STARTED",
693
+ "category": "subagent",
694
+ "messageTemplate": "SUBAGENT_ERROR for subagentRunId '{subagentRunId}' with no open SUBAGENT_STARTED",
695
+ "specUrl": "https://docs.ag-ui.com/concepts/subagents#rules-clients-enforce",
696
+ "specQuote": "SubagentFinished and SubagentError name a subagent that is currently active",
697
+ "since": "0.x",
698
+ "checkedIn": "core"
699
+ },
700
+ {
701
+ "id": "AGUI604",
702
+ "severity": "error",
703
+ "title": "Subagent unterminated at run end",
704
+ "category": "subagent",
705
+ "messageTemplate": "SUBAGENT_STARTED subagentRunId '{subagentRunId}' never closed with SUBAGENT_FINISHED or SUBAGENT_ERROR",
706
+ "specUrl": "https://docs.ag-ui.com/concepts/subagents#rules-clients-enforce",
707
+ "specQuote": "Every started subagent is closed before RunFinished.",
708
+ "since": "0.x",
709
+ "checkedIn": "core"
710
+ },
711
+ {
712
+ "id": "AGUI605",
713
+ "severity": "warning",
714
+ "title": "parentSubagentRunId references a subagent never started",
715
+ "category": "subagent",
716
+ "messageTemplate": "SUBAGENT_STARTED parentSubagentRunId '{parentSubagentRunId}' matches no subagent observed in this stream",
717
+ "specUrl": "https://docs.ag-ui.com/concepts/subagents#nesting-and-concurrency",
718
+ "specQuote": "parentSubagentRunId need only name a subagent that has been started, not one still active",
719
+ "since": "0.x",
720
+ "checkedIn": "core"
721
+ },
722
+ {
723
+ "id": "AGUI606",
724
+ "severity": "warning",
725
+ "title": "Continuation event's subagentRunId disagrees with its entity's owner",
726
+ "category": "subagent",
727
+ "messageTemplate": "{type} declares subagentRunId '{actual}', but {entityType} '{entityId}' was opened under {expected}",
728
+ "specUrl": "https://docs.ag-ui.com/concepts/subagents#rules-clients-enforce",
729
+ "specQuote": "Continuation and close events agree with the owner their entity was created under.",
730
+ "since": "0.x",
731
+ "specQuestion": "SQ-15",
732
+ "checkedIn": "core"
733
+ },
614
734
  {
615
735
  "id": "AGUI901",
616
736
  "severity": "info",
617
737
  "title": "RAW event wraps a typed AG-UI event",
738
+ "category": "hygiene",
618
739
  "messageTemplate": "RAW event wraps an event of type '{wrappedType}', which has a typed AG-UI equivalent",
619
740
  "specUrl": "https://docs.ag-ui.com/concepts/events#raw",
620
741
  "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.",
@@ -625,6 +746,7 @@ var catalog_default = {
625
746
  "id": "AGUI902",
626
747
  "severity": "info",
627
748
  "title": "Events carry no timestamps",
749
+ "category": "hygiene",
628
750
  "messageTemplate": "None of the {eventCount} events carry the optional timestamp property",
629
751
  "specUrl": "https://docs.ag-ui.com/concepts/events#base-event-properties",
630
752
  "specQuote": "timestamp: Optional timestamp indicating when the event was created",
@@ -635,6 +757,7 @@ var catalog_default = {
635
757
  "id": "AGUI903",
636
758
  "severity": "info",
637
759
  "title": "CUSTOM event name is not namespaced",
760
+ "category": "hygiene",
638
761
  "messageTemplate": "CUSTOM event name '{name}' has no namespace prefix (e.g. 'vendor.event')",
639
762
  "specUrl": "https://docs.ag-ui.com/concepts/events#custom",
640
763
  "specQuote": "Teams should document their custom events to ensure consistent implementation across frontends and agents.",
@@ -652,6 +775,16 @@ const SEVERITIES = [
652
775
  "info"
653
776
  ];
654
777
  const LAYERS = ["core", "transport"];
778
+ const CATEGORIES = [
779
+ "lifecycle",
780
+ "text",
781
+ "toolcall",
782
+ "state",
783
+ "reasoning",
784
+ "transport",
785
+ "hygiene",
786
+ "subagent"
787
+ ];
655
788
  /** Validates catalog data and returns it typed. Throws on structural problems. */
656
789
  function validateCatalog(data) {
657
790
  const problems = [];
@@ -665,6 +798,7 @@ function validateCatalog(data) {
665
798
  seen.add(rule.id);
666
799
  if (!SEVERITIES.includes(rule.severity)) problems.push(`${where}: bad severity '${rule.severity}'`);
667
800
  if (!rule.title) problems.push(`${where}: missing title`);
801
+ if (!CATEGORIES.includes(rule.category)) problems.push(`${where}: bad category '${rule.category}'`);
668
802
  if (!rule.messageTemplate) problems.push(`${where}: missing messageTemplate`);
669
803
  if (!rule.specUrl?.startsWith("https://")) problems.push(`${where}: specUrl must be an https URL`);
670
804
  if (!rule.since) problems.push(`${where}: missing since`);
@@ -765,9 +899,17 @@ function formatReportSummary(report, opts) {
765
899
  //#endregion
766
900
  //#region src/report/json.ts
767
901
  function toJsonReport(report, opts) {
902
+ const diagnostics = report.diagnostics.map((d) => {
903
+ const category = RULES.get(d.rule)?.category;
904
+ return category === void 0 ? { ...d } : {
905
+ ...d,
906
+ category
907
+ };
908
+ });
768
909
  const doc = {
769
910
  tool: opts.tool,
770
- ...report
911
+ ...report,
912
+ diagnostics
771
913
  };
772
914
  if (opts.target !== void 0) doc.target = opts.target;
773
915
  return doc;
@@ -791,6 +933,7 @@ function toSarif(report, opts) {
791
933
  if (catalog !== void 0) {
792
934
  entry.shortDescription = { text: catalog.title };
793
935
  entry.defaultConfiguration = { level: LEVEL[catalog.severity] };
936
+ entry.properties = { tags: [catalog.category] };
794
937
  }
795
938
  rules.set(d.rule, entry);
796
939
  }
@@ -854,13 +997,17 @@ function toJUnit(report, opts) {
854
997
  //#endregion
855
998
  //#region src/protocol/event-table.ts
856
999
  /** Version of @ag-ui/core this table was derived from. */
857
- const SDK_VERSION = "0.0.58";
1000
+ const SDK_VERSION = "0.0.59";
858
1001
  /** Canonical wire event types and their schemas, derived from @ag-ui/core. */
859
1002
  const EVENT_TABLE = {
860
1003
  "TEXT_MESSAGE_START": {
861
1004
  category: "text",
862
1005
  specUrl: "https://docs.ag-ui.com/concepts/events#textmessagestart",
863
1006
  fields: {
1007
+ "metadata": {
1008
+ kind: "object",
1009
+ required: false
1010
+ },
864
1011
  "messageId": {
865
1012
  kind: "string",
866
1013
  required: true
@@ -878,6 +1025,10 @@ const EVENT_TABLE = {
878
1025
  "name": {
879
1026
  kind: "string",
880
1027
  required: false
1028
+ },
1029
+ "subagentRunId": {
1030
+ kind: "string",
1031
+ required: false
881
1032
  }
882
1033
  }
883
1034
  },
@@ -885,6 +1036,10 @@ const EVENT_TABLE = {
885
1036
  category: "text",
886
1037
  specUrl: "https://docs.ag-ui.com/concepts/events#textmessagecontent",
887
1038
  fields: {
1039
+ "metadata": {
1040
+ kind: "object",
1041
+ required: false
1042
+ },
888
1043
  "messageId": {
889
1044
  kind: "string",
890
1045
  required: true
@@ -892,21 +1047,39 @@ const EVENT_TABLE = {
892
1047
  "delta": {
893
1048
  kind: "string",
894
1049
  required: true
1050
+ },
1051
+ "subagentRunId": {
1052
+ kind: "string",
1053
+ required: false
895
1054
  }
896
1055
  }
897
1056
  },
898
1057
  "TEXT_MESSAGE_END": {
899
1058
  category: "text",
900
1059
  specUrl: "https://docs.ag-ui.com/concepts/events#textmessageend",
901
- fields: { "messageId": {
902
- kind: "string",
903
- required: true
904
- } }
1060
+ fields: {
1061
+ "metadata": {
1062
+ kind: "object",
1063
+ required: false
1064
+ },
1065
+ "messageId": {
1066
+ kind: "string",
1067
+ required: true
1068
+ },
1069
+ "subagentRunId": {
1070
+ kind: "string",
1071
+ required: false
1072
+ }
1073
+ }
905
1074
  },
906
1075
  "TEXT_MESSAGE_CHUNK": {
907
1076
  category: "text",
908
1077
  specUrl: "https://docs.ag-ui.com/concepts/events#textmessagechunk",
909
1078
  fields: {
1079
+ "metadata": {
1080
+ kind: "object",
1081
+ required: false
1082
+ },
910
1083
  "messageId": {
911
1084
  kind: "string",
912
1085
  required: false
@@ -928,6 +1101,10 @@ const EVENT_TABLE = {
928
1101
  "name": {
929
1102
  kind: "string",
930
1103
  required: false
1104
+ },
1105
+ "subagentRunId": {
1106
+ kind: "string",
1107
+ required: false
931
1108
  }
932
1109
  }
933
1110
  },
@@ -935,6 +1112,10 @@ const EVENT_TABLE = {
935
1112
  category: "toolcall",
936
1113
  specUrl: "https://docs.ag-ui.com/concepts/events#toolcallstart",
937
1114
  fields: {
1115
+ "metadata": {
1116
+ kind: "object",
1117
+ required: false
1118
+ },
938
1119
  "toolCallId": {
939
1120
  kind: "string",
940
1121
  required: true
@@ -946,6 +1127,10 @@ const EVENT_TABLE = {
946
1127
  "parentMessageId": {
947
1128
  kind: "string",
948
1129
  required: false
1130
+ },
1131
+ "subagentRunId": {
1132
+ kind: "string",
1133
+ required: false
949
1134
  }
950
1135
  }
951
1136
  },
@@ -953,6 +1138,10 @@ const EVENT_TABLE = {
953
1138
  category: "toolcall",
954
1139
  specUrl: "https://docs.ag-ui.com/concepts/events#toolcallargs",
955
1140
  fields: {
1141
+ "metadata": {
1142
+ kind: "object",
1143
+ required: false
1144
+ },
956
1145
  "toolCallId": {
957
1146
  kind: "string",
958
1147
  required: true
@@ -960,21 +1149,39 @@ const EVENT_TABLE = {
960
1149
  "delta": {
961
1150
  kind: "string",
962
1151
  required: true
1152
+ },
1153
+ "subagentRunId": {
1154
+ kind: "string",
1155
+ required: false
963
1156
  }
964
1157
  }
965
1158
  },
966
1159
  "TOOL_CALL_END": {
967
1160
  category: "toolcall",
968
1161
  specUrl: "https://docs.ag-ui.com/concepts/events#toolcallend",
969
- fields: { "toolCallId": {
970
- kind: "string",
971
- required: true
972
- } }
1162
+ fields: {
1163
+ "metadata": {
1164
+ kind: "object",
1165
+ required: false
1166
+ },
1167
+ "toolCallId": {
1168
+ kind: "string",
1169
+ required: true
1170
+ },
1171
+ "subagentRunId": {
1172
+ kind: "string",
1173
+ required: false
1174
+ }
1175
+ }
973
1176
  },
974
1177
  "TOOL_CALL_CHUNK": {
975
1178
  category: "toolcall",
976
1179
  specUrl: "https://docs.ag-ui.com/concepts/events#toolcallchunk",
977
1180
  fields: {
1181
+ "metadata": {
1182
+ kind: "object",
1183
+ required: false
1184
+ },
978
1185
  "toolCallId": {
979
1186
  kind: "string",
980
1187
  required: false
@@ -990,6 +1197,10 @@ const EVENT_TABLE = {
990
1197
  "delta": {
991
1198
  kind: "string",
992
1199
  required: false
1200
+ },
1201
+ "subagentRunId": {
1202
+ kind: "string",
1203
+ required: false
993
1204
  }
994
1205
  }
995
1206
  },
@@ -997,6 +1208,10 @@ const EVENT_TABLE = {
997
1208
  category: "toolcall",
998
1209
  specUrl: "https://docs.ag-ui.com/concepts/events#toolcallresult",
999
1210
  fields: {
1211
+ "metadata": {
1212
+ kind: "object",
1213
+ required: false
1214
+ },
1000
1215
  "messageId": {
1001
1216
  kind: "string",
1002
1217
  required: true
@@ -1013,6 +1228,10 @@ const EVENT_TABLE = {
1013
1228
  kind: "string",
1014
1229
  required: false,
1015
1230
  enum: ["tool"]
1231
+ },
1232
+ "subagentRunId": {
1233
+ kind: "string",
1234
+ required: false
1016
1235
  }
1017
1236
  }
1018
1237
  },
@@ -1020,66 +1239,117 @@ const EVENT_TABLE = {
1020
1239
  category: "thinking",
1021
1240
  deprecated: "REASONING_START",
1022
1241
  specUrl: "https://docs.ag-ui.com/concepts/events#thinking-events-deprecated",
1023
- fields: { "title": {
1024
- kind: "string",
1025
- required: false
1026
- } }
1242
+ fields: {
1243
+ "metadata": {
1244
+ kind: "object",
1245
+ required: false
1246
+ },
1247
+ "title": {
1248
+ kind: "string",
1249
+ required: false
1250
+ }
1251
+ }
1027
1252
  },
1028
1253
  "THINKING_END": {
1029
1254
  category: "thinking",
1030
1255
  deprecated: "REASONING_END",
1031
1256
  specUrl: "https://docs.ag-ui.com/concepts/events#thinking-events-deprecated",
1032
- fields: {}
1257
+ fields: { "metadata": {
1258
+ kind: "object",
1259
+ required: false
1260
+ } }
1033
1261
  },
1034
1262
  "THINKING_TEXT_MESSAGE_START": {
1035
1263
  category: "thinking",
1036
1264
  deprecated: "REASONING_MESSAGE_START",
1037
1265
  specUrl: "https://docs.ag-ui.com/concepts/events#thinking-events-deprecated",
1038
- fields: {}
1266
+ fields: { "metadata": {
1267
+ kind: "object",
1268
+ required: false
1269
+ } }
1039
1270
  },
1040
1271
  "THINKING_TEXT_MESSAGE_CONTENT": {
1041
1272
  category: "thinking",
1042
1273
  deprecated: "REASONING_MESSAGE_CONTENT",
1043
1274
  specUrl: "https://docs.ag-ui.com/concepts/events#thinking-events-deprecated",
1044
- fields: { "delta": {
1045
- kind: "string",
1046
- required: true
1047
- } }
1275
+ fields: {
1276
+ "metadata": {
1277
+ kind: "object",
1278
+ required: false
1279
+ },
1280
+ "delta": {
1281
+ kind: "string",
1282
+ required: true
1283
+ }
1284
+ }
1048
1285
  },
1049
1286
  "THINKING_TEXT_MESSAGE_END": {
1050
1287
  category: "thinking",
1051
1288
  deprecated: "REASONING_MESSAGE_END",
1052
1289
  specUrl: "https://docs.ag-ui.com/concepts/events#thinking-events-deprecated",
1053
- fields: {}
1290
+ fields: { "metadata": {
1291
+ kind: "object",
1292
+ required: false
1293
+ } }
1054
1294
  },
1055
1295
  "STATE_SNAPSHOT": {
1056
1296
  category: "state",
1057
1297
  specUrl: "https://docs.ag-ui.com/concepts/events#statesnapshot",
1058
- fields: { "snapshot": {
1059
- kind: "any",
1060
- required: true
1061
- } }
1298
+ fields: {
1299
+ "metadata": {
1300
+ kind: "object",
1301
+ required: false
1302
+ },
1303
+ "snapshot": {
1304
+ kind: "any",
1305
+ required: true
1306
+ },
1307
+ "subagentRunId": {
1308
+ kind: "string",
1309
+ required: false
1310
+ }
1311
+ }
1062
1312
  },
1063
1313
  "STATE_DELTA": {
1064
1314
  category: "state",
1065
1315
  specUrl: "https://docs.ag-ui.com/concepts/events#statedelta",
1066
- fields: { "delta": {
1067
- kind: "array",
1068
- required: true
1069
- } }
1316
+ fields: {
1317
+ "metadata": {
1318
+ kind: "object",
1319
+ required: false
1320
+ },
1321
+ "delta": {
1322
+ kind: "array",
1323
+ required: true
1324
+ },
1325
+ "subagentRunId": {
1326
+ kind: "string",
1327
+ required: false
1328
+ }
1329
+ }
1070
1330
  },
1071
1331
  "MESSAGES_SNAPSHOT": {
1072
1332
  category: "state",
1073
1333
  specUrl: "https://docs.ag-ui.com/concepts/events#messagessnapshot",
1074
- fields: { "messages": {
1075
- kind: "array",
1076
- required: true
1077
- } }
1334
+ fields: {
1335
+ "metadata": {
1336
+ kind: "object",
1337
+ required: false
1338
+ },
1339
+ "messages": {
1340
+ kind: "array",
1341
+ required: true
1342
+ }
1343
+ }
1078
1344
  },
1079
1345
  "ACTIVITY_SNAPSHOT": {
1080
1346
  category: "activity",
1081
1347
  specUrl: "https://docs.ag-ui.com/concepts/events#activitysnapshot",
1082
1348
  fields: {
1349
+ "metadata": {
1350
+ kind: "object",
1351
+ required: false
1352
+ },
1083
1353
  "messageId": {
1084
1354
  kind: "string",
1085
1355
  required: true
@@ -1095,6 +1365,10 @@ const EVENT_TABLE = {
1095
1365
  "replace": {
1096
1366
  kind: "boolean",
1097
1367
  required: false
1368
+ },
1369
+ "subagentRunId": {
1370
+ kind: "string",
1371
+ required: false
1098
1372
  }
1099
1373
  }
1100
1374
  },
@@ -1102,6 +1376,10 @@ const EVENT_TABLE = {
1102
1376
  category: "activity",
1103
1377
  specUrl: "https://docs.ag-ui.com/concepts/events#activitydelta",
1104
1378
  fields: {
1379
+ "metadata": {
1380
+ kind: "object",
1381
+ required: false
1382
+ },
1105
1383
  "messageId": {
1106
1384
  kind: "string",
1107
1385
  required: true
@@ -1113,6 +1391,10 @@ const EVENT_TABLE = {
1113
1391
  "patch": {
1114
1392
  kind: "array",
1115
1393
  required: true
1394
+ },
1395
+ "subagentRunId": {
1396
+ kind: "string",
1397
+ required: false
1116
1398
  }
1117
1399
  }
1118
1400
  },
@@ -1120,6 +1402,10 @@ const EVENT_TABLE = {
1120
1402
  category: "special",
1121
1403
  specUrl: "https://docs.ag-ui.com/concepts/events#raw",
1122
1404
  fields: {
1405
+ "metadata": {
1406
+ kind: "object",
1407
+ required: false
1408
+ },
1123
1409
  "event": {
1124
1410
  kind: "any",
1125
1411
  required: true
@@ -1127,6 +1413,10 @@ const EVENT_TABLE = {
1127
1413
  "source": {
1128
1414
  kind: "string",
1129
1415
  required: false
1416
+ },
1417
+ "subagentRunId": {
1418
+ kind: "string",
1419
+ required: false
1130
1420
  }
1131
1421
  }
1132
1422
  },
@@ -1134,6 +1424,10 @@ const EVENT_TABLE = {
1134
1424
  category: "special",
1135
1425
  specUrl: "https://docs.ag-ui.com/concepts/events#custom",
1136
1426
  fields: {
1427
+ "metadata": {
1428
+ kind: "object",
1429
+ required: false
1430
+ },
1137
1431
  "name": {
1138
1432
  kind: "string",
1139
1433
  required: true
@@ -1141,6 +1435,10 @@ const EVENT_TABLE = {
1141
1435
  "value": {
1142
1436
  kind: "any",
1143
1437
  required: true
1438
+ },
1439
+ "subagentRunId": {
1440
+ kind: "string",
1441
+ required: false
1144
1442
  }
1145
1443
  }
1146
1444
  },
@@ -1148,6 +1446,10 @@ const EVENT_TABLE = {
1148
1446
  category: "lifecycle",
1149
1447
  specUrl: "https://docs.ag-ui.com/concepts/events#runstarted",
1150
1448
  fields: {
1449
+ "metadata": {
1450
+ kind: "object",
1451
+ required: false
1452
+ },
1151
1453
  "threadId": {
1152
1454
  kind: "string",
1153
1455
  required: true
@@ -1170,6 +1472,10 @@ const EVENT_TABLE = {
1170
1472
  category: "lifecycle",
1171
1473
  specUrl: "https://docs.ag-ui.com/concepts/events#runfinished",
1172
1474
  fields: {
1475
+ "metadata": {
1476
+ kind: "object",
1477
+ required: false
1478
+ },
1173
1479
  "threadId": {
1174
1480
  kind: "string",
1175
1481
  required: true
@@ -1196,6 +1502,10 @@ const EVENT_TABLE = {
1196
1502
  category: "lifecycle",
1197
1503
  specUrl: "https://docs.ag-ui.com/concepts/events#runerror",
1198
1504
  fields: {
1505
+ "metadata": {
1506
+ kind: "object",
1507
+ required: false
1508
+ },
1199
1509
  "message": {
1200
1510
  kind: "string",
1201
1511
  required: true
@@ -1213,31 +1523,65 @@ const EVENT_TABLE = {
1213
1523
  "STEP_STARTED": {
1214
1524
  category: "lifecycle",
1215
1525
  specUrl: "https://docs.ag-ui.com/concepts/events#stepstarted",
1216
- fields: { "stepName": {
1217
- kind: "string",
1218
- required: true
1219
- } }
1526
+ fields: {
1527
+ "metadata": {
1528
+ kind: "object",
1529
+ required: false
1530
+ },
1531
+ "stepName": {
1532
+ kind: "string",
1533
+ required: true
1534
+ },
1535
+ "subagentRunId": {
1536
+ kind: "string",
1537
+ required: false
1538
+ }
1539
+ }
1220
1540
  },
1221
1541
  "STEP_FINISHED": {
1222
1542
  category: "lifecycle",
1223
1543
  specUrl: "https://docs.ag-ui.com/concepts/events#stepfinished",
1224
- fields: { "stepName": {
1225
- kind: "string",
1226
- required: true
1227
- } }
1544
+ fields: {
1545
+ "metadata": {
1546
+ kind: "object",
1547
+ required: false
1548
+ },
1549
+ "stepName": {
1550
+ kind: "string",
1551
+ required: true
1552
+ },
1553
+ "subagentRunId": {
1554
+ kind: "string",
1555
+ required: false
1556
+ }
1557
+ }
1228
1558
  },
1229
1559
  "REASONING_START": {
1230
1560
  category: "reasoning",
1231
1561
  specUrl: "https://docs.ag-ui.com/concepts/events#reasoningstart",
1232
- fields: { "messageId": {
1233
- kind: "string",
1234
- required: true
1235
- } }
1562
+ fields: {
1563
+ "metadata": {
1564
+ kind: "object",
1565
+ required: false
1566
+ },
1567
+ "messageId": {
1568
+ kind: "string",
1569
+ required: true
1570
+ },
1571
+ "subagentRunId": {
1572
+ kind: "string",
1573
+ required: false
1574
+ }
1575
+ }
1236
1576
  },
1237
1577
  "REASONING_MESSAGE_START": {
1238
1578
  category: "reasoning",
1239
1579
  specUrl: "https://docs.ag-ui.com/concepts/events#reasoningmessagestart",
1240
1580
  fields: {
1581
+ "metadata": {
1582
+ kind: "object",
1583
+ required: false
1584
+ },
1241
1585
  "messageId": {
1242
1586
  kind: "string",
1243
1587
  required: true
@@ -1246,6 +1590,10 @@ const EVENT_TABLE = {
1246
1590
  kind: "string",
1247
1591
  required: true,
1248
1592
  enum: ["reasoning"]
1593
+ },
1594
+ "subagentRunId": {
1595
+ kind: "string",
1596
+ required: false
1249
1597
  }
1250
1598
  }
1251
1599
  },
@@ -1253,6 +1601,10 @@ const EVENT_TABLE = {
1253
1601
  category: "reasoning",
1254
1602
  specUrl: "https://docs.ag-ui.com/concepts/events#reasoningmessagecontent",
1255
1603
  fields: {
1604
+ "metadata": {
1605
+ kind: "object",
1606
+ required: false
1607
+ },
1256
1608
  "messageId": {
1257
1609
  kind: "string",
1258
1610
  required: true
@@ -1260,21 +1612,39 @@ const EVENT_TABLE = {
1260
1612
  "delta": {
1261
1613
  kind: "string",
1262
1614
  required: true
1615
+ },
1616
+ "subagentRunId": {
1617
+ kind: "string",
1618
+ required: false
1263
1619
  }
1264
1620
  }
1265
1621
  },
1266
1622
  "REASONING_MESSAGE_END": {
1267
1623
  category: "reasoning",
1268
1624
  specUrl: "https://docs.ag-ui.com/concepts/events#reasoningmessageend",
1269
- fields: { "messageId": {
1270
- kind: "string",
1271
- required: true
1272
- } }
1625
+ fields: {
1626
+ "metadata": {
1627
+ kind: "object",
1628
+ required: false
1629
+ },
1630
+ "messageId": {
1631
+ kind: "string",
1632
+ required: true
1633
+ },
1634
+ "subagentRunId": {
1635
+ kind: "string",
1636
+ required: false
1637
+ }
1638
+ }
1273
1639
  },
1274
1640
  "REASONING_MESSAGE_CHUNK": {
1275
1641
  category: "reasoning",
1276
1642
  specUrl: "https://docs.ag-ui.com/concepts/events#reasoningmessagechunk",
1277
1643
  fields: {
1644
+ "metadata": {
1645
+ kind: "object",
1646
+ required: false
1647
+ },
1278
1648
  "messageId": {
1279
1649
  kind: "string",
1280
1650
  required: false
@@ -1282,21 +1652,39 @@ const EVENT_TABLE = {
1282
1652
  "delta": {
1283
1653
  kind: "string",
1284
1654
  required: false
1655
+ },
1656
+ "subagentRunId": {
1657
+ kind: "string",
1658
+ required: false
1285
1659
  }
1286
1660
  }
1287
1661
  },
1288
1662
  "REASONING_END": {
1289
1663
  category: "reasoning",
1290
1664
  specUrl: "https://docs.ag-ui.com/concepts/events#reasoningend",
1291
- fields: { "messageId": {
1292
- kind: "string",
1293
- required: true
1294
- } }
1665
+ fields: {
1666
+ "metadata": {
1667
+ kind: "object",
1668
+ required: false
1669
+ },
1670
+ "messageId": {
1671
+ kind: "string",
1672
+ required: true
1673
+ },
1674
+ "subagentRunId": {
1675
+ kind: "string",
1676
+ required: false
1677
+ }
1678
+ }
1295
1679
  },
1296
1680
  "REASONING_ENCRYPTED_VALUE": {
1297
1681
  category: "reasoning",
1298
1682
  specUrl: "https://docs.ag-ui.com/concepts/events#reasoningencryptedvalue",
1299
1683
  fields: {
1684
+ "metadata": {
1685
+ kind: "object",
1686
+ required: false
1687
+ },
1300
1688
  "subtype": {
1301
1689
  kind: "string",
1302
1690
  required: true,
@@ -1309,6 +1697,88 @@ const EVENT_TABLE = {
1309
1697
  "encryptedValue": {
1310
1698
  kind: "string",
1311
1699
  required: true
1700
+ },
1701
+ "subagentRunId": {
1702
+ kind: "string",
1703
+ required: false
1704
+ }
1705
+ }
1706
+ },
1707
+ "SUBAGENT_STARTED": {
1708
+ category: "subagent",
1709
+ specUrl: "https://docs.ag-ui.com/concepts/events#subagent-events",
1710
+ fields: {
1711
+ "metadata": {
1712
+ kind: "object",
1713
+ required: false
1714
+ },
1715
+ "subagentRunId": {
1716
+ kind: "string",
1717
+ required: true
1718
+ },
1719
+ "name": {
1720
+ kind: "string",
1721
+ required: true
1722
+ },
1723
+ "description": {
1724
+ kind: "string",
1725
+ required: false
1726
+ },
1727
+ "parentSubagentRunId": {
1728
+ kind: "string",
1729
+ required: false
1730
+ },
1731
+ "parentToolCallId": {
1732
+ kind: "string",
1733
+ required: false
1734
+ },
1735
+ "parentMessageId": {
1736
+ kind: "string",
1737
+ required: false
1738
+ }
1739
+ }
1740
+ },
1741
+ "SUBAGENT_FINISHED": {
1742
+ category: "subagent",
1743
+ specUrl: "https://docs.ag-ui.com/concepts/events#subagent-events",
1744
+ fields: {
1745
+ "metadata": {
1746
+ kind: "object",
1747
+ required: false
1748
+ },
1749
+ "subagentRunId": {
1750
+ kind: "string",
1751
+ required: true
1752
+ },
1753
+ "result": {
1754
+ kind: "any",
1755
+ required: false
1756
+ },
1757
+ "outcome": {
1758
+ kind: "object",
1759
+ required: false
1760
+ }
1761
+ }
1762
+ },
1763
+ "SUBAGENT_ERROR": {
1764
+ category: "subagent",
1765
+ specUrl: "https://docs.ag-ui.com/concepts/events#subagent-events",
1766
+ fields: {
1767
+ "metadata": {
1768
+ kind: "object",
1769
+ required: false
1770
+ },
1771
+ "subagentRunId": {
1772
+ kind: "string",
1773
+ required: true
1774
+ },
1775
+ "message": {
1776
+ kind: "string",
1777
+ required: true
1778
+ },
1779
+ "code": {
1780
+ kind: "string",
1781
+ required: false
1312
1782
  }
1313
1783
  }
1314
1784
  }
@@ -1329,10 +1799,14 @@ function newRunState(init) {
1329
1799
  openMessages: /* @__PURE__ */ new Map(),
1330
1800
  closedMessages: /* @__PURE__ */ new Map(),
1331
1801
  knownMessageIds: /* @__PURE__ */ new Set(),
1802
+ messageOwner: /* @__PURE__ */ new Map(),
1332
1803
  openToolCalls: /* @__PURE__ */ new Map(),
1333
1804
  closedToolCalls: /* @__PURE__ */ new Map(),
1334
1805
  knownToolCallIds: /* @__PURE__ */ new Set(),
1335
1806
  openSteps: /* @__PURE__ */ new Map(),
1807
+ openSubagents: /* @__PURE__ */ new Map(),
1808
+ closedSubagents: /* @__PURE__ */ new Map(),
1809
+ knownSubagentRunIds: /* @__PURE__ */ new Set(),
1336
1810
  openReasoningBlocks: /* @__PURE__ */ new Map(),
1337
1811
  openReasoningMessages: /* @__PURE__ */ new Map(),
1338
1812
  state: {
@@ -1352,6 +1826,21 @@ function str(event, field) {
1352
1826
  const v = event[field];
1353
1827
  return typeof v === "string" ? v : void 0;
1354
1828
  }
1829
+ /** AGUI606 — a continuation/close event's explicit subagentRunId must agree
1830
+ * with the owner recorded when its entity opened. Only compared when the
1831
+ * event explicitly tags itself; omitting the field is never flagged (SQ-15).
1832
+ * Shared by text/toolcalls/lifecycle — not the *_CHUNK forms or
1833
+ * TOOL_CALL_RESULT, which are independently attributed by spec. */
1834
+ function checkOwnerConsistency(emit, type, entityType, entityId, event, owner) {
1835
+ const actual = str(event, "subagentRunId");
1836
+ if (actual !== void 0 && actual !== owner) emit("AGUI606", {
1837
+ type,
1838
+ actual,
1839
+ entityType,
1840
+ entityId,
1841
+ expected: owner !== void 0 ? `'${owner}'` : "the run"
1842
+ }, { pointer: "/subagentRunId" });
1843
+ }
1355
1844
  //#endregion
1356
1845
  //#region src/rules/checks/lifecycle.ts
1357
1846
  function handleStepEvent(api) {
@@ -1363,7 +1852,8 @@ function handleStepEvent(api) {
1363
1852
  if (open !== void 0) open.count += 1;
1364
1853
  else run.openSteps.set(stepName, {
1365
1854
  count: 1,
1366
- firstIndex: index
1855
+ firstIndex: index,
1856
+ owner: str(event, "subagentRunId")
1367
1857
  });
1368
1858
  return;
1369
1859
  }
@@ -1373,6 +1863,7 @@ function handleStepEvent(api) {
1373
1863
  emit("AGUI006", { stepName }, { pointer: "/stepName" });
1374
1864
  return;
1375
1865
  }
1866
+ checkOwnerConsistency(emit, type, "stepName", stepName, event, open.owner);
1376
1867
  open.count -= 1;
1377
1868
  if (open.count === 0) run.openSteps.delete(stepName);
1378
1869
  }
@@ -1775,6 +2266,76 @@ function handleStateEvent(api) {
1775
2266
  }
1776
2267
  }
1777
2268
  //#endregion
2269
+ //#region src/rules/checks/subagents.ts
2270
+ function handleSubagentEvent(api) {
2271
+ const { type, event, run, emit, index } = api;
2272
+ switch (type) {
2273
+ case "SUBAGENT_STARTED": {
2274
+ const id = str(event, "subagentRunId");
2275
+ if (id === void 0) return;
2276
+ run.knownSubagentRunIds.add(id);
2277
+ const open = run.openSubagents.get(id);
2278
+ if (open !== void 0) {
2279
+ emit("AGUI601", { subagentRunId: id }, {
2280
+ pointer: "/subagentRunId",
2281
+ relatedEventIndex: open.startIndex
2282
+ });
2283
+ return;
2284
+ }
2285
+ const closed = run.closedSubagents.get(id);
2286
+ if (closed !== void 0 && !closed.resumable) {
2287
+ emit("AGUI601", { subagentRunId: id }, {
2288
+ pointer: "/subagentRunId",
2289
+ relatedEventIndex: closed.index
2290
+ });
2291
+ return;
2292
+ }
2293
+ run.openSubagents.set(id, { startIndex: index });
2294
+ if (closed !== void 0) run.closedSubagents.delete(id);
2295
+ const parent = str(event, "parentSubagentRunId");
2296
+ if (parent !== void 0 && !run.knownSubagentRunIds.has(parent)) emit("AGUI605", { parentSubagentRunId: parent }, { pointer: "/parentSubagentRunId" });
2297
+ return;
2298
+ }
2299
+ case "SUBAGENT_FINISHED": {
2300
+ const id = str(event, "subagentRunId");
2301
+ if (id === void 0) return;
2302
+ if (run.openSubagents.get(id) === void 0) {
2303
+ emit("AGUI602", { subagentRunId: id }, { pointer: "/subagentRunId" });
2304
+ return;
2305
+ }
2306
+ run.openSubagents.delete(id);
2307
+ const outcome = event.outcome;
2308
+ const resumable = typeof outcome === "object" && outcome !== null && outcome.type === "suspended";
2309
+ run.closedSubagents.set(id, {
2310
+ index,
2311
+ resumable
2312
+ });
2313
+ return;
2314
+ }
2315
+ case "SUBAGENT_ERROR": {
2316
+ const id = str(event, "subagentRunId");
2317
+ if (id === void 0) return;
2318
+ if (run.openSubagents.get(id) === void 0) {
2319
+ emit("AGUI603", { subagentRunId: id }, { pointer: "/subagentRunId" });
2320
+ return;
2321
+ }
2322
+ run.openSubagents.delete(id);
2323
+ run.closedSubagents.set(id, {
2324
+ index,
2325
+ resumable: false
2326
+ });
2327
+ return;
2328
+ }
2329
+ }
2330
+ }
2331
+ /** AGUI604 — open subagents when the run reaches a clean end. */
2332
+ function endOfRunSubagents(run, emit, atIndex) {
2333
+ for (const [id, open] of run.openSubagents) emit("AGUI604", { subagentRunId: id }, {
2334
+ eventIndex: atIndex,
2335
+ relatedEventIndex: open.startIndex
2336
+ });
2337
+ }
2338
+ //#endregion
1778
2339
  //#region src/rules/checks/text.ts
1779
2340
  function handleTextEvent(api) {
1780
2341
  const { type, event, run, emit, index } = api;
@@ -1797,8 +2358,13 @@ function handleTextEvent(api) {
1797
2358
  });
1798
2359
  return;
1799
2360
  }
1800
- run.openMessages.set(id, { startIndex: index });
2361
+ const owner = str(event, "subagentRunId");
2362
+ run.openMessages.set(id, {
2363
+ startIndex: index,
2364
+ owner
2365
+ });
1801
2366
  run.knownMessageIds.add(id);
2367
+ run.messageOwner.set(id, owner);
1802
2368
  return;
1803
2369
  }
1804
2370
  case "TEXT_MESSAGE_CONTENT": {
@@ -1817,15 +2383,18 @@ function handleTextEvent(api) {
1817
2383
  pointer: "/delta",
1818
2384
  relatedEventIndex: open.startIndex
1819
2385
  });
2386
+ checkOwnerConsistency(emit, type, "messageId", id, event, open.owner);
1820
2387
  return;
1821
2388
  }
1822
2389
  case "TEXT_MESSAGE_END": {
1823
2390
  const id = str(event, "messageId");
1824
2391
  if (id === void 0) return;
1825
- if (!run.openMessages.has(id)) {
2392
+ const open = run.openMessages.get(id);
2393
+ if (open === void 0) {
1826
2394
  emit("AGUI102", { messageId: id }, { pointer: "/messageId" });
1827
2395
  return;
1828
2396
  }
2397
+ checkOwnerConsistency(emit, type, "messageId", id, event, open.owner);
1829
2398
  run.openMessages.delete(id);
1830
2399
  run.closedMessages.set(id, index);
1831
2400
  return;
@@ -1854,6 +2423,7 @@ function handleTextEvent(api) {
1854
2423
  startIndex: index
1855
2424
  };
1856
2425
  run.knownMessageIds.add(id);
2426
+ run.messageOwner.set(id, str(event, "subagentRunId"));
1857
2427
  return;
1858
2428
  }
1859
2429
  }
@@ -1888,12 +2458,14 @@ function handleToolCallEvent(api) {
1888
2458
  });
1889
2459
  return;
1890
2460
  }
2461
+ const parent = str(event, "parentMessageId");
2462
+ const owner = str(event, "subagentRunId") ?? (parent !== void 0 ? run.messageOwner.get(parent) : void 0);
1891
2463
  run.openToolCalls.set(id, {
1892
2464
  startIndex: index,
1893
2465
  args: "",
1894
- sawArgs: false
2466
+ sawArgs: false,
2467
+ owner
1895
2468
  });
1896
- const parent = str(event, "parentMessageId");
1897
2469
  if (parent !== void 0 && !run.knownMessageIds.has(parent)) emit("AGUI208", { parentMessageId: parent }, { pointer: "/parentMessageId" });
1898
2470
  return;
1899
2471
  }
@@ -1910,6 +2482,7 @@ function handleToolCallEvent(api) {
1910
2482
  open.args += delta;
1911
2483
  open.sawArgs = true;
1912
2484
  }
2485
+ checkOwnerConsistency(emit, type, "toolCallId", id, event, open.owner);
1913
2486
  return;
1914
2487
  }
1915
2488
  case "TOOL_CALL_END": {
@@ -1920,6 +2493,7 @@ function handleToolCallEvent(api) {
1920
2493
  emit("AGUI202", { toolCallId: id }, { pointer: "/toolCallId" });
1921
2494
  return;
1922
2495
  }
2496
+ checkOwnerConsistency(emit, type, "toolCallId", id, event, open.owner);
1923
2497
  run.openToolCalls.delete(id);
1924
2498
  run.closedToolCalls.set(id, index);
1925
2499
  checkArgsJson(id, open, emit, index);
@@ -2142,6 +2716,7 @@ function createValidator(opts = {}) {
2142
2716
  endOfRunToolCalls(r, emit, atIndex);
2143
2717
  endOfRunSteps(r, emit, atIndex);
2144
2718
  endOfRunReasoning(r, emit, atIndex);
2719
+ endOfRunSubagents(r, emit, atIndex);
2145
2720
  }
2146
2721
  /** Opens the implicit run scope for streams that never announced one. */
2147
2722
  function ensureRun(index, type, emit) {
@@ -2292,6 +2867,9 @@ function createValidator(opts = {}) {
2292
2867
  break;
2293
2868
  case "thinking": break;
2294
2869
  case "activity": break;
2870
+ case "subagent":
2871
+ handleSubagentEvent(a);
2872
+ break;
2295
2873
  case "special": if (type === "RAW") {
2296
2874
  const wrapped = ev.event;
2297
2875
  if (typeof wrapped === "object" && wrapped !== null) {
@@ -2820,7 +3398,7 @@ async function main() {
2820
3398
  process.stdout.write(`${JSON.stringify(doc, null, 2)}\n`);
2821
3399
  } else if (config.format === "sarif") process.stdout.write(`${JSON.stringify(toSarif(report, sarifOptions), null, 2)}\n`);
2822
3400
  else process.stdout.write(toJUnit(report, { name: targetLabel }));
2823
- return decideExitCode(report.summary, config.maxWarnings);
3401
+ return decideExitCode(report.summary, config.maxWarnings, config.failOn);
2824
3402
  }
2825
3403
  main().then((code) => {
2826
3404
  process.exitCode = code;