ag-ui-validate 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +229 -0
  3. package/dist/catalog-BglXBNbL.js +472 -0
  4. package/dist/catalog-BglXBNbL.js.map +1 -0
  5. package/dist/catalog-Ci9dqc1a.cjs +495 -0
  6. package/dist/catalog-Ci9dqc1a.cjs.map +1 -0
  7. package/dist/cli.js +2783 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/index-Hmqj3r_r.d.cts +52 -0
  10. package/dist/index-oNG1kOp9.d.ts +52 -0
  11. package/dist/index.cjs +14 -0
  12. package/dist/index.d.cts +3 -0
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.js +3 -0
  15. package/dist/report.cjs +139 -0
  16. package/dist/report.cjs.map +1 -0
  17. package/dist/report.d.cts +85 -0
  18. package/dist/report.d.ts +85 -0
  19. package/dist/report.js +134 -0
  20. package/dist/report.js.map +1 -0
  21. package/dist/src-HmI-kxef.cjs +1596 -0
  22. package/dist/src-HmI-kxef.cjs.map +1 -0
  23. package/dist/src-rGZ2G4qA.js +1555 -0
  24. package/dist/src-rGZ2G4qA.js.map +1 -0
  25. package/dist/transport.cjs +329 -0
  26. package/dist/transport.cjs.map +1 -0
  27. package/dist/transport.d.cts +89 -0
  28. package/dist/transport.d.ts +89 -0
  29. package/dist/transport.js +323 -0
  30. package/dist/transport.js.map +1 -0
  31. package/dist/types-oH_QTnn2.d.cts +148 -0
  32. package/dist/types-oH_QTnn2.d.ts +148 -0
  33. package/dist/vitest.d.ts +28 -0
  34. package/dist/vitest.js +2089 -0
  35. package/dist/vitest.js.map +1 -0
  36. package/package.json +127 -0
  37. package/src/cli-args.ts +202 -0
  38. package/src/cli.ts +147 -0
  39. package/src/index.ts +465 -0
  40. package/src/protocol/event-table.ts +316 -0
  41. package/src/protocol/jsonpatch.ts +220 -0
  42. package/src/report/index.ts +10 -0
  43. package/src/report/json.ts +20 -0
  44. package/src/report/junit.ts +56 -0
  45. package/src/report/pretty.ts +59 -0
  46. package/src/report/sarif.ts +109 -0
  47. package/src/rules/catalog.json +431 -0
  48. package/src/rules/catalog.ts +84 -0
  49. package/src/rules/checks/context.ts +117 -0
  50. package/src/rules/checks/lifecycle.ts +59 -0
  51. package/src/rules/checks/reasoning.ts +97 -0
  52. package/src/rules/checks/state.ts +72 -0
  53. package/src/rules/checks/text.ts +109 -0
  54. package/src/rules/checks/toolcalls.ts +167 -0
  55. package/src/rules/checks/transport.ts +17 -0
  56. package/src/transport/index.ts +331 -0
  57. package/src/transport/ndjson.ts +25 -0
  58. package/src/transport/sse.ts +126 -0
  59. package/src/types.ts +136 -0
  60. package/src/vitest/index.ts +19 -0
  61. package/src/vitest/matcher.ts +77 -0
@@ -0,0 +1,1555 @@
1
+ import { n as RULES, r as formatMessage, t as CATALOG } from "./catalog-BglXBNbL.js";
2
+ //#region src/protocol/event-table.ts
3
+ /** Version of @ag-ui/core this table was derived from. */
4
+ const SDK_VERSION = "0.0.58";
5
+ /** Canonical wire event types and their schemas, derived from @ag-ui/core. */
6
+ const EVENT_TABLE = {
7
+ "TEXT_MESSAGE_START": {
8
+ category: "text",
9
+ specUrl: "https://docs.ag-ui.com/concepts/events#textmessagestart",
10
+ fields: {
11
+ "messageId": {
12
+ kind: "string",
13
+ required: true
14
+ },
15
+ "role": {
16
+ kind: "string",
17
+ required: false,
18
+ enum: [
19
+ "developer",
20
+ "system",
21
+ "assistant",
22
+ "user"
23
+ ]
24
+ },
25
+ "name": {
26
+ kind: "string",
27
+ required: false
28
+ }
29
+ }
30
+ },
31
+ "TEXT_MESSAGE_CONTENT": {
32
+ category: "text",
33
+ specUrl: "https://docs.ag-ui.com/concepts/events#textmessagecontent",
34
+ fields: {
35
+ "messageId": {
36
+ kind: "string",
37
+ required: true
38
+ },
39
+ "delta": {
40
+ kind: "string",
41
+ required: true
42
+ }
43
+ }
44
+ },
45
+ "TEXT_MESSAGE_END": {
46
+ category: "text",
47
+ specUrl: "https://docs.ag-ui.com/concepts/events#textmessageend",
48
+ fields: { "messageId": {
49
+ kind: "string",
50
+ required: true
51
+ } }
52
+ },
53
+ "TEXT_MESSAGE_CHUNK": {
54
+ category: "text",
55
+ specUrl: "https://docs.ag-ui.com/concepts/events#textmessagechunk",
56
+ fields: {
57
+ "messageId": {
58
+ kind: "string",
59
+ required: false
60
+ },
61
+ "role": {
62
+ kind: "string",
63
+ required: false,
64
+ enum: [
65
+ "developer",
66
+ "system",
67
+ "assistant",
68
+ "user"
69
+ ]
70
+ },
71
+ "delta": {
72
+ kind: "string",
73
+ required: false
74
+ },
75
+ "name": {
76
+ kind: "string",
77
+ required: false
78
+ }
79
+ }
80
+ },
81
+ "TOOL_CALL_START": {
82
+ category: "toolcall",
83
+ specUrl: "https://docs.ag-ui.com/concepts/events#toolcallstart",
84
+ fields: {
85
+ "toolCallId": {
86
+ kind: "string",
87
+ required: true
88
+ },
89
+ "toolCallName": {
90
+ kind: "string",
91
+ required: true
92
+ },
93
+ "parentMessageId": {
94
+ kind: "string",
95
+ required: false
96
+ }
97
+ }
98
+ },
99
+ "TOOL_CALL_ARGS": {
100
+ category: "toolcall",
101
+ specUrl: "https://docs.ag-ui.com/concepts/events#toolcallargs",
102
+ fields: {
103
+ "toolCallId": {
104
+ kind: "string",
105
+ required: true
106
+ },
107
+ "delta": {
108
+ kind: "string",
109
+ required: true
110
+ }
111
+ }
112
+ },
113
+ "TOOL_CALL_END": {
114
+ category: "toolcall",
115
+ specUrl: "https://docs.ag-ui.com/concepts/events#toolcallend",
116
+ fields: { "toolCallId": {
117
+ kind: "string",
118
+ required: true
119
+ } }
120
+ },
121
+ "TOOL_CALL_CHUNK": {
122
+ category: "toolcall",
123
+ specUrl: "https://docs.ag-ui.com/concepts/events#toolcallchunk",
124
+ fields: {
125
+ "toolCallId": {
126
+ kind: "string",
127
+ required: false
128
+ },
129
+ "toolCallName": {
130
+ kind: "string",
131
+ required: false
132
+ },
133
+ "parentMessageId": {
134
+ kind: "string",
135
+ required: false
136
+ },
137
+ "delta": {
138
+ kind: "string",
139
+ required: false
140
+ }
141
+ }
142
+ },
143
+ "TOOL_CALL_RESULT": {
144
+ category: "toolcall",
145
+ specUrl: "https://docs.ag-ui.com/concepts/events#toolcallresult",
146
+ fields: {
147
+ "messageId": {
148
+ kind: "string",
149
+ required: true
150
+ },
151
+ "toolCallId": {
152
+ kind: "string",
153
+ required: true
154
+ },
155
+ "content": {
156
+ kind: "string",
157
+ required: true
158
+ },
159
+ "role": {
160
+ kind: "string",
161
+ required: false,
162
+ enum: ["tool"]
163
+ }
164
+ }
165
+ },
166
+ "THINKING_START": {
167
+ category: "thinking",
168
+ deprecated: "REASONING_START",
169
+ specUrl: "https://docs.ag-ui.com/concepts/events#thinking-events-deprecated",
170
+ fields: { "title": {
171
+ kind: "string",
172
+ required: false
173
+ } }
174
+ },
175
+ "THINKING_END": {
176
+ category: "thinking",
177
+ deprecated: "REASONING_END",
178
+ specUrl: "https://docs.ag-ui.com/concepts/events#thinking-events-deprecated",
179
+ fields: {}
180
+ },
181
+ "THINKING_TEXT_MESSAGE_START": {
182
+ category: "thinking",
183
+ deprecated: "REASONING_MESSAGE_START",
184
+ specUrl: "https://docs.ag-ui.com/concepts/events#thinking-events-deprecated",
185
+ fields: {}
186
+ },
187
+ "THINKING_TEXT_MESSAGE_CONTENT": {
188
+ category: "thinking",
189
+ deprecated: "REASONING_MESSAGE_CONTENT",
190
+ specUrl: "https://docs.ag-ui.com/concepts/events#thinking-events-deprecated",
191
+ fields: { "delta": {
192
+ kind: "string",
193
+ required: true
194
+ } }
195
+ },
196
+ "THINKING_TEXT_MESSAGE_END": {
197
+ category: "thinking",
198
+ deprecated: "REASONING_MESSAGE_END",
199
+ specUrl: "https://docs.ag-ui.com/concepts/events#thinking-events-deprecated",
200
+ fields: {}
201
+ },
202
+ "STATE_SNAPSHOT": {
203
+ category: "state",
204
+ specUrl: "https://docs.ag-ui.com/concepts/events#statesnapshot",
205
+ fields: { "snapshot": {
206
+ kind: "any",
207
+ required: true
208
+ } }
209
+ },
210
+ "STATE_DELTA": {
211
+ category: "state",
212
+ specUrl: "https://docs.ag-ui.com/concepts/events#statedelta",
213
+ fields: { "delta": {
214
+ kind: "array",
215
+ required: true
216
+ } }
217
+ },
218
+ "MESSAGES_SNAPSHOT": {
219
+ category: "state",
220
+ specUrl: "https://docs.ag-ui.com/concepts/events#messagessnapshot",
221
+ fields: { "messages": {
222
+ kind: "array",
223
+ required: true
224
+ } }
225
+ },
226
+ "ACTIVITY_SNAPSHOT": {
227
+ category: "activity",
228
+ specUrl: "https://docs.ag-ui.com/concepts/events#activitysnapshot",
229
+ fields: {
230
+ "messageId": {
231
+ kind: "string",
232
+ required: true
233
+ },
234
+ "activityType": {
235
+ kind: "string",
236
+ required: true
237
+ },
238
+ "content": {
239
+ kind: "object",
240
+ required: true
241
+ },
242
+ "replace": {
243
+ kind: "boolean",
244
+ required: false
245
+ }
246
+ }
247
+ },
248
+ "ACTIVITY_DELTA": {
249
+ category: "activity",
250
+ specUrl: "https://docs.ag-ui.com/concepts/events#activitydelta",
251
+ fields: {
252
+ "messageId": {
253
+ kind: "string",
254
+ required: true
255
+ },
256
+ "activityType": {
257
+ kind: "string",
258
+ required: true
259
+ },
260
+ "patch": {
261
+ kind: "array",
262
+ required: true
263
+ }
264
+ }
265
+ },
266
+ "RAW": {
267
+ category: "special",
268
+ specUrl: "https://docs.ag-ui.com/concepts/events#raw",
269
+ fields: {
270
+ "event": {
271
+ kind: "any",
272
+ required: true
273
+ },
274
+ "source": {
275
+ kind: "string",
276
+ required: false
277
+ }
278
+ }
279
+ },
280
+ "CUSTOM": {
281
+ category: "special",
282
+ specUrl: "https://docs.ag-ui.com/concepts/events#custom",
283
+ fields: {
284
+ "name": {
285
+ kind: "string",
286
+ required: true
287
+ },
288
+ "value": {
289
+ kind: "any",
290
+ required: true
291
+ }
292
+ }
293
+ },
294
+ "RUN_STARTED": {
295
+ category: "lifecycle",
296
+ specUrl: "https://docs.ag-ui.com/concepts/events#runstarted",
297
+ fields: {
298
+ "threadId": {
299
+ kind: "string",
300
+ required: true
301
+ },
302
+ "runId": {
303
+ kind: "string",
304
+ required: true
305
+ },
306
+ "parentRunId": {
307
+ kind: "string",
308
+ required: false
309
+ },
310
+ "input": {
311
+ kind: "object",
312
+ required: false
313
+ }
314
+ }
315
+ },
316
+ "RUN_FINISHED": {
317
+ category: "lifecycle",
318
+ specUrl: "https://docs.ag-ui.com/concepts/events#runfinished",
319
+ fields: {
320
+ "threadId": {
321
+ kind: "string",
322
+ required: true
323
+ },
324
+ "runId": {
325
+ kind: "string",
326
+ required: true
327
+ },
328
+ "result": {
329
+ kind: "any",
330
+ required: false
331
+ },
332
+ "outcome": {
333
+ kind: "object",
334
+ required: false
335
+ },
336
+ "usage": {
337
+ kind: "array",
338
+ required: false
339
+ }
340
+ }
341
+ },
342
+ "RUN_ERROR": {
343
+ category: "lifecycle",
344
+ specUrl: "https://docs.ag-ui.com/concepts/events#runerror",
345
+ fields: {
346
+ "message": {
347
+ kind: "string",
348
+ required: true
349
+ },
350
+ "code": {
351
+ kind: "string",
352
+ required: false
353
+ },
354
+ "usage": {
355
+ kind: "array",
356
+ required: false
357
+ }
358
+ }
359
+ },
360
+ "STEP_STARTED": {
361
+ category: "lifecycle",
362
+ specUrl: "https://docs.ag-ui.com/concepts/events#stepstarted",
363
+ fields: { "stepName": {
364
+ kind: "string",
365
+ required: true
366
+ } }
367
+ },
368
+ "STEP_FINISHED": {
369
+ category: "lifecycle",
370
+ specUrl: "https://docs.ag-ui.com/concepts/events#stepfinished",
371
+ fields: { "stepName": {
372
+ kind: "string",
373
+ required: true
374
+ } }
375
+ },
376
+ "REASONING_START": {
377
+ category: "reasoning",
378
+ specUrl: "https://docs.ag-ui.com/concepts/events#reasoningstart",
379
+ fields: { "messageId": {
380
+ kind: "string",
381
+ required: true
382
+ } }
383
+ },
384
+ "REASONING_MESSAGE_START": {
385
+ category: "reasoning",
386
+ specUrl: "https://docs.ag-ui.com/concepts/events#reasoningmessagestart",
387
+ fields: {
388
+ "messageId": {
389
+ kind: "string",
390
+ required: true
391
+ },
392
+ "role": {
393
+ kind: "string",
394
+ required: true,
395
+ enum: ["reasoning"]
396
+ }
397
+ }
398
+ },
399
+ "REASONING_MESSAGE_CONTENT": {
400
+ category: "reasoning",
401
+ specUrl: "https://docs.ag-ui.com/concepts/events#reasoningmessagecontent",
402
+ fields: {
403
+ "messageId": {
404
+ kind: "string",
405
+ required: true
406
+ },
407
+ "delta": {
408
+ kind: "string",
409
+ required: true
410
+ }
411
+ }
412
+ },
413
+ "REASONING_MESSAGE_END": {
414
+ category: "reasoning",
415
+ specUrl: "https://docs.ag-ui.com/concepts/events#reasoningmessageend",
416
+ fields: { "messageId": {
417
+ kind: "string",
418
+ required: true
419
+ } }
420
+ },
421
+ "REASONING_MESSAGE_CHUNK": {
422
+ category: "reasoning",
423
+ specUrl: "https://docs.ag-ui.com/concepts/events#reasoningmessagechunk",
424
+ fields: {
425
+ "messageId": {
426
+ kind: "string",
427
+ required: false
428
+ },
429
+ "delta": {
430
+ kind: "string",
431
+ required: false
432
+ }
433
+ }
434
+ },
435
+ "REASONING_END": {
436
+ category: "reasoning",
437
+ specUrl: "https://docs.ag-ui.com/concepts/events#reasoningend",
438
+ fields: { "messageId": {
439
+ kind: "string",
440
+ required: true
441
+ } }
442
+ },
443
+ "REASONING_ENCRYPTED_VALUE": {
444
+ category: "reasoning",
445
+ specUrl: "https://docs.ag-ui.com/concepts/events#reasoningencryptedvalue",
446
+ fields: {
447
+ "subtype": {
448
+ kind: "string",
449
+ required: true,
450
+ enum: ["tool-call", "message"]
451
+ },
452
+ "entityId": {
453
+ kind: "string",
454
+ required: true
455
+ },
456
+ "encryptedValue": {
457
+ kind: "string",
458
+ required: true
459
+ }
460
+ }
461
+ }
462
+ };
463
+ /** All canonical wire `type` values, in @ag-ui/core enum order. */
464
+ const EVENT_TYPES = Object.keys(EVENT_TABLE);
465
+ /**
466
+ * Wire types documented as drafts (https://docs.ag-ui.com/drafts/overview) but
467
+ * not yet in @ag-ui/core. Not errors: reported at info severity.
468
+ */
469
+ const DRAFT_EVENT_TYPES = ["META"];
470
+ //#endregion
471
+ //#region src/rules/checks/context.ts
472
+ function newRunState(init) {
473
+ return {
474
+ ...init,
475
+ terminal: null,
476
+ openMessages: /* @__PURE__ */ new Map(),
477
+ closedMessages: /* @__PURE__ */ new Map(),
478
+ knownMessageIds: /* @__PURE__ */ new Set(),
479
+ openToolCalls: /* @__PURE__ */ new Map(),
480
+ closedToolCalls: /* @__PURE__ */ new Map(),
481
+ knownToolCallIds: /* @__PURE__ */ new Set(),
482
+ openSteps: /* @__PURE__ */ new Map(),
483
+ openReasoningBlocks: /* @__PURE__ */ new Map(),
484
+ openReasoningMessages: /* @__PURE__ */ new Map(),
485
+ state: {
486
+ known: false,
487
+ value: void 0,
488
+ deltasSinceSnapshot: 0,
489
+ snapshotSeen: false,
490
+ agui301Fired: false
491
+ },
492
+ textChunk: null,
493
+ toolChunk: null,
494
+ reasoningChunk: null
495
+ };
496
+ }
497
+ /** Reads a field only if it is a string (schema problems already reported). */
498
+ function str(event, field) {
499
+ const v = event[field];
500
+ return typeof v === "string" ? v : void 0;
501
+ }
502
+ //#endregion
503
+ //#region src/rules/checks/lifecycle.ts
504
+ function handleStepEvent(api) {
505
+ const { type, event, run, emit, index } = api;
506
+ const stepName = str(event, "stepName");
507
+ if (stepName === void 0) return;
508
+ if (type === "STEP_STARTED") {
509
+ const open = run.openSteps.get(stepName);
510
+ if (open !== void 0) open.count += 1;
511
+ else run.openSteps.set(stepName, {
512
+ count: 1,
513
+ firstIndex: index
514
+ });
515
+ return;
516
+ }
517
+ if (type === "STEP_FINISHED") {
518
+ const open = run.openSteps.get(stepName);
519
+ if (open === void 0) {
520
+ emit("AGUI006", { stepName }, { pointer: "/stepName" });
521
+ return;
522
+ }
523
+ open.count -= 1;
524
+ if (open.count === 0) run.openSteps.delete(stepName);
525
+ }
526
+ }
527
+ /** AGUI008 — RUN_FINISHED must carry the ids RUN_STARTED established. */
528
+ function checkRunIdStability(api) {
529
+ const { event, run, emit } = api;
530
+ if (run.implicit) return;
531
+ for (const field of ["threadId", "runId"]) {
532
+ const actual = str(api.event, field);
533
+ const expected = field === "threadId" ? run.threadId : run.runId;
534
+ if (actual !== void 0 && expected !== null && actual !== expected) emit("AGUI008", {
535
+ field,
536
+ actual,
537
+ expected
538
+ }, {
539
+ pointer: `/${field}`,
540
+ relatedEventIndex: run.startIndex
541
+ });
542
+ }
543
+ }
544
+ /** AGUI007 — open steps when the run reaches a clean end. */
545
+ function endOfRunSteps(run, emit, atIndex) {
546
+ for (const [stepName, open] of run.openSteps) emit("AGUI007", { stepName }, {
547
+ eventIndex: atIndex,
548
+ relatedEventIndex: open.firstIndex
549
+ });
550
+ }
551
+ //#endregion
552
+ //#region src/rules/checks/reasoning.ts
553
+ function handleReasoningEvent(api) {
554
+ const { type, event, run, emit, index } = api;
555
+ switch (type) {
556
+ case "REASONING_START": {
557
+ const id = str(event, "messageId");
558
+ if (id !== void 0) run.openReasoningBlocks.set(id, index);
559
+ return;
560
+ }
561
+ case "REASONING_END": {
562
+ const id = str(event, "messageId");
563
+ if (id !== void 0) run.openReasoningBlocks.delete(id);
564
+ return;
565
+ }
566
+ case "REASONING_MESSAGE_START": {
567
+ const id = str(event, "messageId");
568
+ if (id !== void 0) run.openReasoningMessages.set(id, index);
569
+ return;
570
+ }
571
+ case "REASONING_MESSAGE_CONTENT": {
572
+ const id = str(event, "messageId");
573
+ if (id === void 0) return;
574
+ const openChunk = run.reasoningChunk !== null && run.reasoningChunk.messageId === id;
575
+ if (!run.openReasoningMessages.has(id) && !openChunk) emit("AGUI401", { messageId: id }, { pointer: "/messageId" });
576
+ return;
577
+ }
578
+ case "REASONING_MESSAGE_END": {
579
+ const id = str(event, "messageId");
580
+ if (id !== void 0) run.openReasoningMessages.delete(id);
581
+ return;
582
+ }
583
+ case "REASONING_MESSAGE_CHUNK": {
584
+ const id = str(event, "messageId");
585
+ if (id === void 0) {
586
+ if (run.reasoningChunk === null) {
587
+ emit("AGUI504", {
588
+ type,
589
+ detail: "first REASONING_MESSAGE_CHUNK must include messageId"
590
+ }, { pointer: "/messageId" });
591
+ return;
592
+ }
593
+ if (event.delta === "") run.reasoningChunk = null;
594
+ return;
595
+ }
596
+ if (run.reasoningChunk !== null && run.reasoningChunk.messageId !== id) run.reasoningChunk = null;
597
+ if (event.delta === "") {
598
+ run.reasoningChunk = null;
599
+ return;
600
+ }
601
+ if (run.reasoningChunk === null) run.reasoningChunk = {
602
+ messageId: id,
603
+ startIndex: index
604
+ };
605
+ return;
606
+ }
607
+ }
608
+ }
609
+ /** Chunked reasoning also closes on any non-reasoning event (documented). */
610
+ function closeReasoningChunk(run) {
611
+ run.reasoningChunk = null;
612
+ }
613
+ /** AGUI402 — unterminated reasoning at a clean run end. */
614
+ function endOfRunReasoning(run, emit, atIndex) {
615
+ for (const [id, startIndex] of run.openReasoningBlocks) emit("AGUI402", {
616
+ startType: "REASONING_START",
617
+ messageId: id
618
+ }, {
619
+ eventIndex: atIndex,
620
+ relatedEventIndex: startIndex
621
+ });
622
+ for (const [id, startIndex] of run.openReasoningMessages) emit("AGUI402", {
623
+ startType: "REASONING_MESSAGE_START",
624
+ messageId: id
625
+ }, {
626
+ eventIndex: atIndex,
627
+ relatedEventIndex: startIndex
628
+ });
629
+ }
630
+ //#endregion
631
+ //#region src/protocol/jsonpatch.ts
632
+ const OPS = [
633
+ "add",
634
+ "remove",
635
+ "replace",
636
+ "move",
637
+ "copy",
638
+ "test"
639
+ ];
640
+ /** RFC 6901: "" → whole document; "/a/b" → ["a", "b"]. Returns null when invalid. */
641
+ function parsePointer(pointer) {
642
+ if (typeof pointer !== "string") return null;
643
+ if (pointer === "") return [];
644
+ if (!pointer.startsWith("/")) return null;
645
+ return pointer.slice(1).split("/").map((t) => t.replace(/~1/g, "/").replace(/~0/g, "~"));
646
+ }
647
+ /** Structural validation of a patch document, without applying it. */
648
+ function validatePatchShape(patch) {
649
+ if (!Array.isArray(patch)) return {
650
+ error: "patch document must be an array of operations",
651
+ opIndex: -1,
652
+ pointer: ""
653
+ };
654
+ for (let i = 0; i < patch.length; i++) {
655
+ const op = patch[i];
656
+ if (typeof op !== "object" || op === null || Array.isArray(op)) return {
657
+ error: `operation ${i} is not an object`,
658
+ opIndex: i,
659
+ pointer: `/${i}`
660
+ };
661
+ const o = op;
662
+ if (!OPS.includes(o.op)) return {
663
+ error: `operation ${i} has invalid op '${String(o.op)}'`,
664
+ opIndex: i,
665
+ pointer: `/${i}/op`
666
+ };
667
+ if (parsePointer(o.path) === null) return {
668
+ error: `operation ${i} (${String(o.op)}) has invalid path`,
669
+ opIndex: i,
670
+ pointer: `/${i}/path`
671
+ };
672
+ if ((o.op === "add" || o.op === "replace" || o.op === "test") && !("value" in o)) return {
673
+ error: `operation ${i} (${String(o.op)}) is missing value`,
674
+ opIndex: i,
675
+ pointer: `/${i}`
676
+ };
677
+ if ((o.op === "move" || o.op === "copy") && parsePointer(o.from) === null) return {
678
+ error: `operation ${i} (${String(o.op)}) is missing or has invalid from`,
679
+ opIndex: i,
680
+ pointer: `/${i}`
681
+ };
682
+ }
683
+ return null;
684
+ }
685
+ function clone(value) {
686
+ if (Array.isArray(value)) return value.map(clone);
687
+ if (typeof value === "object" && value !== null) {
688
+ const out = {};
689
+ for (const [k, v] of Object.entries(value)) out[k] = clone(v);
690
+ return out;
691
+ }
692
+ return value;
693
+ }
694
+ function deepEqual(a, b) {
695
+ if (a === b) return true;
696
+ if (Array.isArray(a) && Array.isArray(b)) return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
697
+ if (typeof a === "object" && a !== null && typeof b === "object" && b !== null && !Array.isArray(a) && !Array.isArray(b)) {
698
+ const ka = Object.keys(a);
699
+ const kb = Object.keys(b);
700
+ return ka.length === kb.length && ka.every((k) => deepEqual(a[k], b[k]));
701
+ }
702
+ return false;
703
+ }
704
+ function arrayIndex(token, length, allowAppend) {
705
+ if (allowAppend && token === "-") return length;
706
+ if (!/^(0|[1-9]\d*)$/.test(token)) return null;
707
+ const i = Number(token);
708
+ return i > length ? null : i;
709
+ }
710
+ function locate(doc, tokens, forAdd) {
711
+ if (tokens.length === 0) return {
712
+ parent: null,
713
+ key: "",
714
+ exists: true,
715
+ value: doc
716
+ };
717
+ let current = doc;
718
+ for (let i = 0; i < tokens.length - 1; i++) {
719
+ const token = tokens[i];
720
+ if (Array.isArray(current)) {
721
+ const idx = arrayIndex(token, current.length, false);
722
+ if (idx === null || idx >= current.length) return `path segment '/${token}' does not exist`;
723
+ current = current[idx];
724
+ } else if (typeof current === "object" && current !== null) {
725
+ const obj = current;
726
+ if (!(token in obj)) return `path segment '/${token}' does not exist`;
727
+ current = obj[token];
728
+ } else return `path segment '/${token}' is not an object or array`;
729
+ }
730
+ const last = tokens[tokens.length - 1];
731
+ if (Array.isArray(current)) {
732
+ const idx = arrayIndex(last, current.length, forAdd);
733
+ if (idx === null) return `'${last}' is not a valid index for an array of length ${current.length}`;
734
+ return {
735
+ parent: current,
736
+ key: idx,
737
+ exists: idx < current.length,
738
+ value: current[idx]
739
+ };
740
+ }
741
+ if (typeof current === "object" && current !== null) {
742
+ const obj = current;
743
+ return {
744
+ parent: obj,
745
+ key: last,
746
+ exists: last in obj,
747
+ value: obj[last]
748
+ };
749
+ }
750
+ return `target of '/${last}' is not an object or array`;
751
+ }
752
+ /** Applies an RFC 6902 patch. Validates shape first; never throws. */
753
+ function applyPatch(doc, patch) {
754
+ const shape = validatePatchShape(patch);
755
+ if (shape !== null) return {
756
+ ok: false,
757
+ error: shape.error,
758
+ opIndex: shape.opIndex
759
+ };
760
+ let result = clone(doc);
761
+ const ops = patch;
762
+ const getAt = (pointer) => {
763
+ const loc = locate(result, parsePointer(pointer), false);
764
+ if (typeof loc === "string") return {
765
+ ok: false,
766
+ error: `${pointer}: ${loc}`
767
+ };
768
+ if (!loc.exists) return {
769
+ ok: false,
770
+ error: `${pointer} does not exist`
771
+ };
772
+ return {
773
+ ok: true,
774
+ value: loc.value
775
+ };
776
+ };
777
+ const setAt = (pointer, value, mustExist) => {
778
+ const tokens = parsePointer(pointer);
779
+ if (tokens.length === 0) {
780
+ result = value;
781
+ return null;
782
+ }
783
+ const loc = locate(result, tokens, !mustExist);
784
+ if (typeof loc === "string") return `${pointer}: ${loc}`;
785
+ if (mustExist && !loc.exists) return `${pointer} does not exist`;
786
+ if (Array.isArray(loc.parent)) {
787
+ if (mustExist) loc.parent[loc.key] = value;
788
+ else loc.parent.splice(loc.key, 0, value);
789
+ } else loc.parent[loc.key] = value;
790
+ return null;
791
+ };
792
+ const removeAt = (pointer) => {
793
+ const tokens = parsePointer(pointer);
794
+ if (tokens.length === 0) return {
795
+ ok: false,
796
+ error: "cannot remove the whole document"
797
+ };
798
+ const loc = locate(result, tokens, false);
799
+ if (typeof loc === "string") return {
800
+ ok: false,
801
+ error: `${pointer}: ${loc}`
802
+ };
803
+ if (!loc.exists) return {
804
+ ok: false,
805
+ error: `${pointer} does not exist`
806
+ };
807
+ if (Array.isArray(loc.parent)) loc.parent.splice(loc.key, 1);
808
+ else delete loc.parent[loc.key];
809
+ return {
810
+ ok: true,
811
+ removed: loc.value
812
+ };
813
+ };
814
+ for (let i = 0; i < ops.length; i++) {
815
+ const op = ops[i];
816
+ const path = op.path;
817
+ let error = null;
818
+ switch (op.op) {
819
+ case "add":
820
+ error = setAt(path, clone(op.value), false);
821
+ break;
822
+ case "replace":
823
+ error = setAt(path, clone(op.value), true);
824
+ break;
825
+ case "remove": {
826
+ const r = removeAt(path);
827
+ if (!r.ok) error = r.error;
828
+ break;
829
+ }
830
+ case "move": {
831
+ const from = op.from;
832
+ if (path.startsWith(`${from}/`)) {
833
+ error = `cannot move ${from} into its own child ${path}`;
834
+ break;
835
+ }
836
+ const r = removeAt(from);
837
+ if (!r.ok) {
838
+ error = r.error;
839
+ break;
840
+ }
841
+ error = setAt(path, r.removed, false);
842
+ break;
843
+ }
844
+ case "copy": {
845
+ const r = getAt(op.from);
846
+ if (!r.ok) {
847
+ error = r.error;
848
+ break;
849
+ }
850
+ error = setAt(path, clone(r.value), false);
851
+ break;
852
+ }
853
+ case "test": {
854
+ const r = getAt(path);
855
+ if (!r.ok) error = r.error;
856
+ else if (!deepEqual(r.value, op.value)) error = `test failed at ${path}`;
857
+ break;
858
+ }
859
+ }
860
+ if (error !== null) return {
861
+ ok: false,
862
+ error,
863
+ opIndex: i
864
+ };
865
+ }
866
+ return {
867
+ ok: true,
868
+ result
869
+ };
870
+ }
871
+ //#endregion
872
+ //#region src/rules/checks/state.ts
873
+ function handleStateEvent(api) {
874
+ const { type, event, run, stream, emit } = api;
875
+ switch (type) {
876
+ case "STATE_SNAPSHOT":
877
+ api.feature("shared-state");
878
+ if (run.state.deltasSinceSnapshot > 0) emit("AGUI304", { deltaCount: run.state.deltasSinceSnapshot }, {});
879
+ run.state.known = true;
880
+ run.state.value = event.snapshot;
881
+ run.state.snapshotSeen = true;
882
+ run.state.deltasSinceSnapshot = 0;
883
+ stream.anySnapshot = true;
884
+ return;
885
+ case "STATE_DELTA": {
886
+ api.feature("shared-state");
887
+ const delta = event.delta;
888
+ if (!Array.isArray(delta)) return;
889
+ const shape = validatePatchShape(delta);
890
+ if (shape !== null) {
891
+ emit("AGUI303", { error: shape.error }, { pointer: `/delta${shape.pointer}` });
892
+ return;
893
+ }
894
+ if (!run.state.snapshotSeen && !run.state.agui301Fired) {
895
+ emit("AGUI301", {}, {});
896
+ run.state.agui301Fired = true;
897
+ }
898
+ if (run.state.known) {
899
+ const applied = applyPatch(run.state.value, delta);
900
+ if (applied.ok) run.state.value = applied.result;
901
+ else emit("AGUI302", { error: applied.error }, { pointer: `/delta/${applied.opIndex}` });
902
+ }
903
+ run.state.deltasSinceSnapshot += 1;
904
+ return;
905
+ }
906
+ case "MESSAGES_SNAPSHOT": {
907
+ const messages = event.messages;
908
+ if (!Array.isArray(messages)) return;
909
+ for (const message of messages) {
910
+ if (typeof message !== "object" || message === null) continue;
911
+ const m = message;
912
+ if (typeof m.id === "string") run.knownMessageIds.add(m.id);
913
+ if (Array.isArray(m.toolCalls)) {
914
+ for (const call of m.toolCalls) if (typeof call === "object" && call !== null) {
915
+ const id = call.id;
916
+ if (typeof id === "string") run.knownToolCallIds.add(id);
917
+ }
918
+ }
919
+ }
920
+ return;
921
+ }
922
+ }
923
+ }
924
+ //#endregion
925
+ //#region src/rules/checks/text.ts
926
+ function handleTextEvent(api) {
927
+ const { type, event, run, emit, index } = api;
928
+ api.feature("agentic-chat");
929
+ switch (type) {
930
+ case "TEXT_MESSAGE_START": {
931
+ const id = str(event, "messageId");
932
+ if (id === void 0) return;
933
+ if (run.openMessages.has(id)) {
934
+ emit("AGUI106", { messageId: id }, {
935
+ pointer: "/messageId",
936
+ relatedEventIndex: run.openMessages.get(id).startIndex
937
+ });
938
+ return;
939
+ }
940
+ if (run.closedMessages.has(id)) {
941
+ emit("AGUI104", { messageId: id }, {
942
+ pointer: "/messageId",
943
+ relatedEventIndex: run.closedMessages.get(id)
944
+ });
945
+ return;
946
+ }
947
+ run.openMessages.set(id, { startIndex: index });
948
+ run.knownMessageIds.add(id);
949
+ return;
950
+ }
951
+ case "TEXT_MESSAGE_CONTENT": {
952
+ const id = str(event, "messageId");
953
+ if (id === void 0) return;
954
+ const open = run.openMessages.get(id);
955
+ if (open === void 0) {
956
+ const closedAt = run.closedMessages.get(id);
957
+ emit("AGUI101", { messageId: id }, {
958
+ pointer: "/messageId",
959
+ ...closedAt !== void 0 ? { relatedEventIndex: closedAt } : {}
960
+ });
961
+ return;
962
+ }
963
+ if (event.delta === "") emit("AGUI105", { messageId: id }, {
964
+ pointer: "/delta",
965
+ relatedEventIndex: open.startIndex
966
+ });
967
+ return;
968
+ }
969
+ case "TEXT_MESSAGE_END": {
970
+ const id = str(event, "messageId");
971
+ if (id === void 0) return;
972
+ if (!run.openMessages.has(id)) {
973
+ emit("AGUI102", { messageId: id }, { pointer: "/messageId" });
974
+ return;
975
+ }
976
+ run.openMessages.delete(id);
977
+ run.closedMessages.set(id, index);
978
+ return;
979
+ }
980
+ case "TEXT_MESSAGE_CHUNK": {
981
+ const id = str(event, "messageId");
982
+ if (id === void 0) {
983
+ if (run.textChunk === null) emit("AGUI504", {
984
+ type,
985
+ detail: "first TEXT_MESSAGE_CHUNK for a message must include messageId"
986
+ }, { pointer: "/messageId" });
987
+ return;
988
+ }
989
+ if (run.openMessages.has(id)) return;
990
+ if (run.textChunk !== null && run.textChunk.messageId === id) return;
991
+ closeTextChunk(run, index);
992
+ if (run.closedMessages.has(id)) {
993
+ emit("AGUI104", { messageId: id }, {
994
+ pointer: "/messageId",
995
+ relatedEventIndex: run.closedMessages.get(id)
996
+ });
997
+ return;
998
+ }
999
+ run.textChunk = {
1000
+ messageId: id,
1001
+ startIndex: index
1002
+ };
1003
+ run.knownMessageIds.add(id);
1004
+ return;
1005
+ }
1006
+ }
1007
+ }
1008
+ /** Chunk streams close implicitly on the next non-chunk event. */
1009
+ function closeTextChunk(run, atIndex) {
1010
+ if (run.textChunk === null) return;
1011
+ run.closedMessages.set(run.textChunk.messageId, atIndex);
1012
+ run.textChunk = null;
1013
+ }
1014
+ /** AGUI103 — open messages when the run reaches a clean end. */
1015
+ function endOfRunText(run, emit, atIndex) {
1016
+ for (const [id, open] of run.openMessages) emit("AGUI103", { messageId: id }, {
1017
+ eventIndex: atIndex,
1018
+ relatedEventIndex: open.startIndex
1019
+ });
1020
+ }
1021
+ //#endregion
1022
+ //#region src/rules/checks/toolcalls.ts
1023
+ function handleToolCallEvent(api) {
1024
+ const { type, event, run, emit, index } = api;
1025
+ api.feature("backend-tool-rendering");
1026
+ switch (type) {
1027
+ case "TOOL_CALL_START": {
1028
+ const id = str(event, "toolCallId");
1029
+ if (id === void 0) return;
1030
+ if (run.openToolCalls.has(id) || run.closedToolCalls.has(id)) {
1031
+ const related = run.openToolCalls.get(id)?.startIndex ?? run.closedToolCalls.get(id);
1032
+ emit("AGUI205", { toolCallId: id }, {
1033
+ pointer: "/toolCallId",
1034
+ relatedEventIndex: related
1035
+ });
1036
+ return;
1037
+ }
1038
+ run.openToolCalls.set(id, {
1039
+ startIndex: index,
1040
+ args: "",
1041
+ sawArgs: false
1042
+ });
1043
+ const parent = str(event, "parentMessageId");
1044
+ if (parent !== void 0 && !run.knownMessageIds.has(parent)) emit("AGUI208", { parentMessageId: parent }, { pointer: "/parentMessageId" });
1045
+ return;
1046
+ }
1047
+ case "TOOL_CALL_ARGS": {
1048
+ const id = str(event, "toolCallId");
1049
+ if (id === void 0) return;
1050
+ const open = run.openToolCalls.get(id);
1051
+ if (open === void 0) {
1052
+ emit("AGUI201", { toolCallId: id }, { pointer: "/toolCallId" });
1053
+ return;
1054
+ }
1055
+ const delta = str(event, "delta");
1056
+ if (delta !== void 0) {
1057
+ open.args += delta;
1058
+ open.sawArgs = true;
1059
+ }
1060
+ return;
1061
+ }
1062
+ case "TOOL_CALL_END": {
1063
+ const id = str(event, "toolCallId");
1064
+ if (id === void 0) return;
1065
+ const open = run.openToolCalls.get(id);
1066
+ if (open === void 0) {
1067
+ emit("AGUI202", { toolCallId: id }, { pointer: "/toolCallId" });
1068
+ return;
1069
+ }
1070
+ run.openToolCalls.delete(id);
1071
+ run.closedToolCalls.set(id, index);
1072
+ checkArgsJson(id, open, emit, index);
1073
+ return;
1074
+ }
1075
+ case "TOOL_CALL_RESULT": {
1076
+ const id = str(event, "toolCallId");
1077
+ if (id !== void 0) {
1078
+ const open = run.openToolCalls.get(id);
1079
+ if (open !== void 0) emit("AGUI206", { toolCallId: id }, {
1080
+ pointer: "/toolCallId",
1081
+ relatedEventIndex: open.startIndex
1082
+ });
1083
+ else if (!run.closedToolCalls.has(id) && !run.knownToolCallIds.has(id)) emit("AGUI207", { toolCallId: id }, { pointer: "/toolCallId" });
1084
+ }
1085
+ const messageId = str(event, "messageId");
1086
+ if (messageId !== void 0) run.knownMessageIds.add(messageId);
1087
+ return;
1088
+ }
1089
+ case "TOOL_CALL_CHUNK": {
1090
+ const id = str(event, "toolCallId");
1091
+ const delta = str(event, "delta");
1092
+ if (id === void 0) {
1093
+ if (run.toolChunk === null) {
1094
+ emit("AGUI504", {
1095
+ type,
1096
+ detail: "first TOOL_CALL_CHUNK for a tool call must include toolCallId and toolCallName"
1097
+ }, { pointer: "/toolCallId" });
1098
+ return;
1099
+ }
1100
+ if (delta !== void 0) {
1101
+ run.toolChunk.args += delta;
1102
+ run.toolChunk.sawArgs = true;
1103
+ }
1104
+ return;
1105
+ }
1106
+ const openExplicit = run.openToolCalls.get(id);
1107
+ if (openExplicit !== void 0) {
1108
+ if (delta !== void 0) {
1109
+ openExplicit.args += delta;
1110
+ openExplicit.sawArgs = true;
1111
+ }
1112
+ return;
1113
+ }
1114
+ if (run.toolChunk !== null && run.toolChunk.toolCallId === id) {
1115
+ if (delta !== void 0) {
1116
+ run.toolChunk.args += delta;
1117
+ run.toolChunk.sawArgs = true;
1118
+ }
1119
+ return;
1120
+ }
1121
+ closeToolChunk(run, emit, index);
1122
+ if (run.closedToolCalls.has(id)) {
1123
+ emit("AGUI205", { toolCallId: id }, {
1124
+ pointer: "/toolCallId",
1125
+ relatedEventIndex: run.closedToolCalls.get(id)
1126
+ });
1127
+ return;
1128
+ }
1129
+ if (str(event, "toolCallName") === void 0) emit("AGUI504", {
1130
+ type,
1131
+ detail: "first TOOL_CALL_CHUNK for a tool call must include toolCallName"
1132
+ }, { pointer: "/toolCallName" });
1133
+ run.toolChunk = {
1134
+ toolCallId: id,
1135
+ startIndex: index,
1136
+ args: delta ?? "",
1137
+ sawArgs: delta !== void 0 && delta.length > 0
1138
+ };
1139
+ return;
1140
+ }
1141
+ }
1142
+ }
1143
+ function checkArgsJson(id, call, emit, atIndex) {
1144
+ if (!call.sawArgs || call.args.length === 0) return;
1145
+ try {
1146
+ JSON.parse(call.args);
1147
+ } catch (e) {
1148
+ emit("AGUI204", {
1149
+ toolCallId: id,
1150
+ error: e instanceof Error ? e.message : String(e)
1151
+ }, {
1152
+ eventIndex: atIndex,
1153
+ relatedEventIndex: call.startIndex
1154
+ });
1155
+ }
1156
+ }
1157
+ /** Chunk streams close implicitly on the next non-chunk event. */
1158
+ function closeToolChunk(run, emit, atIndex) {
1159
+ if (run.toolChunk === null) return;
1160
+ const { toolCallId, startIndex, args, sawArgs } = run.toolChunk;
1161
+ run.toolChunk = null;
1162
+ run.closedToolCalls.set(toolCallId, atIndex);
1163
+ checkArgsJson(toolCallId, {
1164
+ startIndex,
1165
+ args,
1166
+ sawArgs
1167
+ }, emit, atIndex);
1168
+ }
1169
+ /** AGUI203 — open tool calls when the run reaches a clean end. */
1170
+ function endOfRunToolCalls(run, emit, atIndex) {
1171
+ for (const [id, open] of run.openToolCalls) emit("AGUI203", { toolCallId: id }, {
1172
+ eventIndex: atIndex,
1173
+ relatedEventIndex: open.startIndex
1174
+ });
1175
+ }
1176
+ //#endregion
1177
+ //#region src/rules/checks/transport.ts
1178
+ const TRANSPORT_SKIP_REASON = "transport-layer rule; only checkable against a live connection (validated by ag-ui-validate/transport)";
1179
+ const TRANSPORT_RULE_IDS = CATALOG.rules.filter((r) => r.checkedIn === "transport").map((r) => r.id);
1180
+ //#endregion
1181
+ //#region src/types.ts
1182
+ const CANONICAL_FEATURES = [
1183
+ "agentic-chat",
1184
+ "backend-tool-rendering",
1185
+ "human-in-the-loop",
1186
+ "agentic-generative-ui",
1187
+ "tool-based-generative-ui",
1188
+ "shared-state",
1189
+ "predictive-state-updates"
1190
+ ];
1191
+ //#endregion
1192
+ //#region src/index.ts
1193
+ const DRAFTS_META_URL = "https://docs.ag-ui.com/drafts/meta-events";
1194
+ const NOT_INFERABLE = ["agentic-generative-ui", "tool-based-generative-ui"];
1195
+ /** "runStarted" → "RUN_STARTED": case-insensitive match against wire types. */
1196
+ const CANONICAL_BY_SQUASHED = new Map(EVENT_TYPES.map((t) => [t.replace(/_/g, "").toLowerCase(), t]));
1197
+ function describeValue(v) {
1198
+ if (v === null) return "null";
1199
+ if (Array.isArray(v)) return "array";
1200
+ return typeof v;
1201
+ }
1202
+ function createValidator(opts = {}) {
1203
+ const overrides = opts.severityOverrides ?? {};
1204
+ const declaredFeatures = new Set(opts.features ?? []);
1205
+ const layers = /* @__PURE__ */ new Set(["core", ...opts.layers ?? []]);
1206
+ const diagnostics = [];
1207
+ const internalErrors = [];
1208
+ const explicitSkips = /* @__PURE__ */ new Map();
1209
+ const stream = {
1210
+ eventCount: 0,
1211
+ sawTimestamp: false,
1212
+ anySnapshot: false,
1213
+ agui001Fired: false,
1214
+ features: /* @__PURE__ */ new Set()
1215
+ };
1216
+ let run = null;
1217
+ let finalized = false;
1218
+ function mkEmit(batch, current) {
1219
+ return (ruleId, params, extra = {}) => {
1220
+ const rule = RULES.get(ruleId);
1221
+ if (rule === void 0) {
1222
+ internalErrors.push(`emit() for unknown rule ${ruleId}`);
1223
+ return;
1224
+ }
1225
+ const override = overrides[ruleId];
1226
+ if (override === "off") return;
1227
+ const severity = override ?? extra.severity ?? rule.severity;
1228
+ const aboutCurrentEvent = extra.eventIndex === void 0 && current !== null;
1229
+ const diag = {
1230
+ rule: ruleId,
1231
+ severity,
1232
+ message: formatMessage(rule, params) + (extra.messageSuffix ?? ""),
1233
+ eventIndex: extra.eventIndex ?? current?.index ?? -1,
1234
+ specUrl: extra.specUrl ?? rule.specUrl
1235
+ };
1236
+ if (aboutCurrentEvent) diag.eventType = current.type;
1237
+ if (extra.pointer !== void 0) diag.pointer = extra.pointer;
1238
+ if (extra.relatedEventIndex !== void 0) diag.relatedEventIndex = extra.relatedEventIndex;
1239
+ diagnostics.push(diag);
1240
+ batch.push(diag);
1241
+ };
1242
+ }
1243
+ function validateSchema(type, spec, ev, emit) {
1244
+ for (const [field, fs] of Object.entries(spec.fields)) {
1245
+ const v = ev[field];
1246
+ if (v === void 0) {
1247
+ if (fs.required) emit("AGUI504", {
1248
+ type,
1249
+ detail: `missing required field '${field}' (${fs.kind})`
1250
+ }, { pointer: `/${field}` });
1251
+ continue;
1252
+ }
1253
+ let kindOk = true;
1254
+ switch (fs.kind) {
1255
+ case "string":
1256
+ case "number":
1257
+ case "boolean":
1258
+ kindOk = typeof v === fs.kind;
1259
+ break;
1260
+ case "array":
1261
+ kindOk = Array.isArray(v);
1262
+ break;
1263
+ case "object": kindOk = typeof v === "object" && v !== null && !Array.isArray(v);
1264
+ }
1265
+ if (!kindOk) {
1266
+ emit("AGUI504", {
1267
+ type,
1268
+ detail: `field '${field}' must be ${fs.kind === "array" ? "an" : "a"} ${fs.kind}, got ${describeValue(v)}`
1269
+ }, { pointer: `/${field}` });
1270
+ continue;
1271
+ }
1272
+ if (fs.enum !== void 0 && typeof v === "string" && !fs.enum.includes(v)) emit("AGUI504", {
1273
+ type,
1274
+ detail: `field '${field}' must be one of ${fs.enum.join("|")}, got '${v}'`
1275
+ }, { pointer: `/${field}` });
1276
+ }
1277
+ }
1278
+ /** Chunk streams close implicitly on any event of a different type. */
1279
+ function closeChunks(r, emit, atIndex, except) {
1280
+ if (except !== "TEXT_MESSAGE_CHUNK") closeTextChunk(r, atIndex);
1281
+ if (except !== "TOOL_CALL_CHUNK") closeToolChunk(r, emit, atIndex);
1282
+ if (except !== "REASONING_MESSAGE_CHUNK") closeReasoningChunk(r);
1283
+ }
1284
+ /** Unterminated-at-run-end rules. Only on a *clean* end (RUN_FINISHED or a
1285
+ * stream that just stops): after RUN_ERROR, open streams are expected
1286
+ * debris of the failure, and flagging them would manufacture noise. */
1287
+ function endOfRunChecks(r, emit, atIndex) {
1288
+ endOfRunText(r, emit, atIndex);
1289
+ endOfRunToolCalls(r, emit, atIndex);
1290
+ endOfRunSteps(r, emit, atIndex);
1291
+ endOfRunReasoning(r, emit, atIndex);
1292
+ }
1293
+ /** Opens the implicit run scope for streams that never announced one. */
1294
+ function ensureRun(index, type, emit) {
1295
+ if (run === null) {
1296
+ if (!stream.agui001Fired) {
1297
+ emit("AGUI001", { type }, {});
1298
+ stream.agui001Fired = true;
1299
+ }
1300
+ run = newRunState({
1301
+ runId: null,
1302
+ threadId: null,
1303
+ startIndex: index,
1304
+ implicit: true
1305
+ });
1306
+ }
1307
+ return run;
1308
+ }
1309
+ function processEvent(input, batch) {
1310
+ const index = stream.eventCount;
1311
+ stream.eventCount += 1;
1312
+ let parsed = input;
1313
+ if (typeof input === "string") try {
1314
+ parsed = JSON.parse(input);
1315
+ } catch (e) {
1316
+ mkEmit(batch, {
1317
+ index,
1318
+ type: ""
1319
+ })("AGUI502", { error: e instanceof Error ? e.message : String(e) });
1320
+ return;
1321
+ }
1322
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1323
+ mkEmit(batch, {
1324
+ index,
1325
+ type: ""
1326
+ })("AGUI502", { error: `payload is ${describeValue(parsed)}, expected a JSON object` });
1327
+ return;
1328
+ }
1329
+ const ev = parsed;
1330
+ if (typeof ev.type !== "string") {
1331
+ mkEmit(batch, {
1332
+ index,
1333
+ type: ""
1334
+ })("AGUI504", {
1335
+ type: "(untyped)",
1336
+ detail: "event has no string 'type' property"
1337
+ }, { pointer: "/type" });
1338
+ return;
1339
+ }
1340
+ const type = ev.type;
1341
+ const emit = mkEmit(batch, {
1342
+ index,
1343
+ type
1344
+ });
1345
+ if ("timestamp" in ev && ev.timestamp !== void 0) {
1346
+ if (typeof ev.timestamp === "number") stream.sawTimestamp = true;
1347
+ else emit("AGUI504", {
1348
+ type,
1349
+ detail: `field 'timestamp' must be a number, got ${describeValue(ev.timestamp)}`
1350
+ }, { pointer: "/timestamp" });
1351
+ }
1352
+ const spec = EVENT_TABLE[type];
1353
+ if (spec === void 0) {
1354
+ if (DRAFT_EVENT_TYPES.includes(type)) emit("AGUI503", {
1355
+ type,
1356
+ sdkVersion: SDK_VERSION
1357
+ }, {
1358
+ severity: "info",
1359
+ specUrl: DRAFTS_META_URL,
1360
+ messageSuffix: " — documented draft event type, not yet in @ag-ui/core"
1361
+ });
1362
+ else {
1363
+ const canonical = CANONICAL_BY_SQUASHED.get(type.replace(/[_\-\s]/g, "").toLowerCase());
1364
+ emit("AGUI503", {
1365
+ type,
1366
+ sdkVersion: SDK_VERSION
1367
+ }, { ...canonical !== void 0 ? { messageSuffix: ` — did you mean '${canonical}'? AG-UI wire types use SCREAMING_SNAKE_CASE` } : {} });
1368
+ }
1369
+ return;
1370
+ }
1371
+ validateSchema(type, spec, ev, emit);
1372
+ if (type === "RUN_STARTED") {
1373
+ if (run === null) run = newRunState({
1374
+ runId: str(ev, "runId") ?? null,
1375
+ threadId: str(ev, "threadId") ?? null,
1376
+ startIndex: index,
1377
+ implicit: false
1378
+ });
1379
+ else if (run.terminal !== null) run = newRunState({
1380
+ runId: str(ev, "runId") ?? null,
1381
+ threadId: str(ev, "threadId") ?? null,
1382
+ startIndex: index,
1383
+ implicit: false
1384
+ });
1385
+ else if (run.implicit) {
1386
+ closeChunks(run, emit, index);
1387
+ run.implicit = false;
1388
+ run.runId = str(ev, "runId") ?? null;
1389
+ run.threadId = str(ev, "threadId") ?? null;
1390
+ } else {
1391
+ closeChunks(run, emit, index);
1392
+ emit("AGUI002", { runId: str(ev, "runId") ?? "(missing)" }, { relatedEventIndex: run.startIndex });
1393
+ }
1394
+ return;
1395
+ }
1396
+ const r = ensureRun(index, type, emit);
1397
+ if (r.terminal !== null) {
1398
+ const terminalType = r.terminal.type;
1399
+ if ((type === "RUN_FINISHED" || type === "RUN_ERROR") && type !== terminalType) emit("AGUI005", {
1400
+ type,
1401
+ terminalType
1402
+ }, { relatedEventIndex: r.terminal.index });
1403
+ else emit("AGUI004", {
1404
+ type,
1405
+ terminalType
1406
+ }, { relatedEventIndex: r.terminal.index });
1407
+ return;
1408
+ }
1409
+ closeChunks(r, emit, index, type);
1410
+ if (type === "RUN_FINISHED" || type === "RUN_ERROR") {
1411
+ if (type === "RUN_FINISHED") {
1412
+ checkRunIdStability(api(index, type, ev, r, emit));
1413
+ endOfRunChecks(r, emit, index);
1414
+ const outcome = ev.outcome;
1415
+ if (typeof outcome === "object" && outcome !== null && outcome.type === "interrupt") stream.features.add("human-in-the-loop");
1416
+ }
1417
+ r.terminal = {
1418
+ type,
1419
+ index
1420
+ };
1421
+ return;
1422
+ }
1423
+ const a = api(index, type, ev, r, emit);
1424
+ switch (spec.category) {
1425
+ case "lifecycle":
1426
+ handleStepEvent(a);
1427
+ break;
1428
+ case "text":
1429
+ handleTextEvent(a);
1430
+ break;
1431
+ case "toolcall":
1432
+ handleToolCallEvent(a);
1433
+ break;
1434
+ case "state":
1435
+ handleStateEvent(a);
1436
+ break;
1437
+ case "reasoning":
1438
+ handleReasoningEvent(a);
1439
+ break;
1440
+ case "thinking": break;
1441
+ case "activity": break;
1442
+ case "special": if (type === "RAW") {
1443
+ const wrapped = ev.event;
1444
+ if (typeof wrapped === "object" && wrapped !== null) {
1445
+ const wrappedType = wrapped.type;
1446
+ if (typeof wrappedType === "string" && EVENT_TABLE[wrappedType] !== void 0) emit("AGUI901", { wrappedType }, { pointer: "/event/type" });
1447
+ }
1448
+ } else if (type === "CUSTOM") {
1449
+ const name = str(ev, "name");
1450
+ if (name !== void 0) {
1451
+ if (name === "PredictState") stream.features.add("predictive-state-updates");
1452
+ if (!/[.:/]/.test(name)) emit("AGUI903", { name }, { pointer: "/name" });
1453
+ }
1454
+ }
1455
+ }
1456
+ }
1457
+ function api(index, type, event, r, emit) {
1458
+ return {
1459
+ index,
1460
+ type,
1461
+ event,
1462
+ run: r,
1463
+ stream,
1464
+ emit,
1465
+ feature: (f) => stream.features.add(f)
1466
+ };
1467
+ }
1468
+ return {
1469
+ feed(event) {
1470
+ const batch = [];
1471
+ try {
1472
+ processEvent(event, batch);
1473
+ } catch (e) {
1474
+ internalErrors.push(`feed(event ${stream.eventCount - 1}): ${e instanceof Error ? e.stack ?? e.message : String(e)}`);
1475
+ }
1476
+ return batch;
1477
+ },
1478
+ finalize() {
1479
+ if (finalized) return [];
1480
+ finalized = true;
1481
+ const batch = [];
1482
+ const emit = mkEmit(batch, null);
1483
+ try {
1484
+ if (run !== null && run.terminal === null) {
1485
+ closeChunks(run, emit, -1);
1486
+ emit("AGUI003", { runId: run.runId ?? "(unknown)" }, {
1487
+ eventIndex: -1,
1488
+ relatedEventIndex: run.startIndex
1489
+ });
1490
+ endOfRunChecks(run, emit, -1);
1491
+ }
1492
+ if (stream.eventCount > 0 && !stream.sawTimestamp) emit("AGUI902", { eventCount: stream.eventCount }, { eventIndex: -1 });
1493
+ if (declaredFeatures.has("shared-state") && !stream.anySnapshot) emit("AGUI305", {}, { eventIndex: -1 });
1494
+ } catch (e) {
1495
+ internalErrors.push(`finalize(): ${e instanceof Error ? e.stack ?? e.message : String(e)}`);
1496
+ }
1497
+ return batch;
1498
+ },
1499
+ emitExternal(rule, params = {}, extra = {}) {
1500
+ const batch = [];
1501
+ try {
1502
+ mkEmit(batch, null)(rule, params, extra);
1503
+ } catch (e) {
1504
+ internalErrors.push(`emitExternal(${rule}): ${e instanceof Error ? e.message : String(e)}`);
1505
+ }
1506
+ return batch[0] ?? null;
1507
+ },
1508
+ markSkipped(rule, reason) {
1509
+ explicitSkips.set(String(rule), String(reason));
1510
+ },
1511
+ report() {
1512
+ const summary = {
1513
+ errors: 0,
1514
+ warnings: 0,
1515
+ info: 0
1516
+ };
1517
+ for (const d of diagnostics) if (d.severity === "error") summary.errors += 1;
1518
+ else if (d.severity === "warning") summary.warnings += 1;
1519
+ else summary.info += 1;
1520
+ const features = {};
1521
+ for (const f of CANONICAL_FEATURES) features[f] = NOT_INFERABLE.includes(f) ? "not-inferable" : stream.features.has(f) ? "exercised" : "not-exercised";
1522
+ let skipped = layers.has("transport") ? [] : TRANSPORT_RULE_IDS.filter((id) => overrides[id] !== "off").map((rule) => ({
1523
+ rule,
1524
+ reason: TRANSPORT_SKIP_REASON
1525
+ }));
1526
+ if (!declaredFeatures.has("shared-state") && overrides.AGUI305 !== "off") skipped.push({
1527
+ rule: "AGUI305",
1528
+ reason: "only evaluated when the 'shared-state' feature is declared via options.features"
1529
+ });
1530
+ for (const [rule, severity] of Object.entries(overrides)) if (severity === "off" && RULES.has(rule)) skipped.push({
1531
+ rule,
1532
+ reason: "disabled by severityOverrides"
1533
+ });
1534
+ if (explicitSkips.size > 0) {
1535
+ skipped = skipped.filter((s) => !explicitSkips.has(s.rule));
1536
+ for (const [rule, reason] of explicitSkips) skipped.push({
1537
+ rule,
1538
+ reason
1539
+ });
1540
+ }
1541
+ return {
1542
+ diagnostics: [...diagnostics],
1543
+ summary,
1544
+ features,
1545
+ skipped,
1546
+ eventCount: stream.eventCount,
1547
+ internalErrors: [...internalErrors]
1548
+ };
1549
+ }
1550
+ };
1551
+ }
1552
+ //#endregion
1553
+ export { EVENT_TABLE as a, validatePatchShape as i, CANONICAL_FEATURES as n, EVENT_TYPES as o, applyPatch as r, SDK_VERSION as s, createValidator as t };
1554
+
1555
+ //# sourceMappingURL=src-rGZ2G4qA.js.map