agent-inspect 4.3.0 → 5.0.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 (102) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/docs/API.md +3 -0
  3. package/docs/CLI.md +37 -0
  4. package/docs/SCHEMA.md +27 -1
  5. package/package.json +1 -1
  6. package/packages/cli/dist/chunk-TB45H3QT.mjs +7881 -0
  7. package/packages/cli/dist/chunk-TB45H3QT.mjs.map +1 -0
  8. package/packages/cli/dist/index.cjs +10461 -9188
  9. package/packages/cli/dist/index.cjs.map +1 -1
  10. package/packages/cli/dist/index.mjs +2619 -5688
  11. package/packages/cli/dist/index.mjs.map +1 -1
  12. package/packages/cli/dist/{src-YFMPWEIS.mjs → src-ZRFSYML6.mjs} +3 -3
  13. package/packages/cli/dist/{src-YFMPWEIS.mjs.map → src-ZRFSYML6.mjs.map} +1 -1
  14. package/packages/core/dist/advanced.cjs +3410 -21
  15. package/packages/core/dist/advanced.cjs.map +1 -1
  16. package/packages/core/dist/advanced.d.cts +147 -9
  17. package/packages/core/dist/advanced.d.ts +147 -9
  18. package/packages/core/dist/advanced.mjs +603 -20
  19. package/packages/core/dist/advanced.mjs.map +1 -1
  20. package/packages/core/dist/checks.cjs +117 -18
  21. package/packages/core/dist/checks.cjs.map +1 -1
  22. package/packages/core/dist/checks.d.cts +11 -3
  23. package/packages/core/dist/checks.d.ts +11 -3
  24. package/packages/core/dist/checks.mjs +7 -1612
  25. package/packages/core/dist/checks.mjs.map +1 -1
  26. package/packages/core/dist/{chunk-2ZXEMWGK.mjs → chunk-3USJVBLA.mjs} +3 -3
  27. package/packages/core/dist/{chunk-2ZXEMWGK.mjs.map → chunk-3USJVBLA.mjs.map} +1 -1
  28. package/packages/core/dist/{chunk-HY7H3CQM.mjs → chunk-CAWPF22J.mjs} +4 -3
  29. package/packages/core/dist/chunk-CAWPF22J.mjs.map +1 -0
  30. package/packages/core/dist/{chunk-XUNJMAPD.mjs → chunk-KNYL56KZ.mjs} +38 -3
  31. package/packages/core/dist/chunk-KNYL56KZ.mjs.map +1 -0
  32. package/packages/core/dist/{chunk-IZBJAZGF.mjs → chunk-P366CCL2.mjs} +34 -3
  33. package/packages/core/dist/chunk-P366CCL2.mjs.map +1 -0
  34. package/packages/core/dist/chunk-QLYOCCEF.mjs +1650 -0
  35. package/packages/core/dist/chunk-QLYOCCEF.mjs.map +1 -0
  36. package/packages/core/dist/{chunk-4JVGFNQH.mjs → chunk-ULCGIRTC.mjs} +95 -8
  37. package/packages/core/dist/chunk-ULCGIRTC.mjs.map +1 -0
  38. package/packages/core/dist/chunk-VFO76UH3.mjs +228 -0
  39. package/packages/core/dist/chunk-VFO76UH3.mjs.map +1 -0
  40. package/packages/core/dist/{chunk-NQZPZ2FN.mjs → chunk-WRE4CNPF.mjs} +6 -3
  41. package/packages/core/dist/chunk-WRE4CNPF.mjs.map +1 -0
  42. package/packages/core/dist/chunk-X2FLDF7M.mjs +1581 -0
  43. package/packages/core/dist/chunk-X2FLDF7M.mjs.map +1 -0
  44. package/packages/core/dist/{chunk-A4V2H3YU.mjs → chunk-ZWA5RWMV.mjs} +40 -4
  45. package/packages/core/dist/chunk-ZWA5RWMV.mjs.map +1 -0
  46. package/packages/core/dist/{context-DjY-jvQk.d.cts → context-BbfkMYjz.d.cts} +5 -3
  47. package/packages/core/dist/{context-BDZmi53V.d.ts → context-Cp8NRbBX.d.ts} +5 -3
  48. package/packages/core/dist/diff.cjs.map +1 -1
  49. package/packages/core/dist/diff.d.cts +3 -3
  50. package/packages/core/dist/diff.d.ts +3 -3
  51. package/packages/core/dist/diff.mjs +1 -1
  52. package/packages/core/dist/exporters.cjs +137 -1
  53. package/packages/core/dist/exporters.cjs.map +1 -1
  54. package/packages/core/dist/exporters.d.cts +6 -3
  55. package/packages/core/dist/exporters.d.ts +6 -3
  56. package/packages/core/dist/exporters.mjs +71 -6
  57. package/packages/core/dist/exporters.mjs.map +1 -1
  58. package/packages/core/dist/index.cjs +181 -6
  59. package/packages/core/dist/index.cjs.map +1 -1
  60. package/packages/core/dist/index.d.cts +14 -7
  61. package/packages/core/dist/index.d.ts +14 -7
  62. package/packages/core/dist/index.mjs +51 -7
  63. package/packages/core/dist/index.mjs.map +1 -1
  64. package/packages/core/dist/{inspect-event-CYAV7Hxm.d.cts → inspect-event-IqwOHbGo.d.cts} +1 -1
  65. package/packages/core/dist/{inspect-event-CYAV7Hxm.d.ts → inspect-event-IqwOHbGo.d.ts} +1 -1
  66. package/packages/core/dist/{log-config-BNQ9UTmP.d.cts → log-config-CW8w5YQ9.d.cts} +1 -1
  67. package/packages/core/dist/{log-config-D3Yp1SZN.d.ts → log-config-SRA4xTp8.d.ts} +1 -1
  68. package/packages/core/dist/logs.d.cts +3 -3
  69. package/packages/core/dist/logs.d.ts +3 -3
  70. package/packages/core/dist/{persisted-inspect-event-BLvb0jSX.d.ts → persisted-inspect-event-DruwqARZ.d.ts} +1 -1
  71. package/packages/core/dist/{persisted-inspect-event-D5SjBz9b.d.cts → persisted-inspect-event-Gd-W8vB7.d.cts} +1 -1
  72. package/packages/core/dist/persisted.cjs +67 -1
  73. package/packages/core/dist/persisted.cjs.map +1 -1
  74. package/packages/core/dist/persisted.d.cts +5 -5
  75. package/packages/core/dist/persisted.d.ts +5 -5
  76. package/packages/core/dist/persisted.mjs +3 -3
  77. package/packages/core/dist/readers.cjs +72 -3
  78. package/packages/core/dist/readers.cjs.map +1 -1
  79. package/packages/core/dist/readers.d.cts +2 -2
  80. package/packages/core/dist/readers.d.ts +2 -2
  81. package/packages/core/dist/readers.mjs +4 -1580
  82. package/packages/core/dist/readers.mjs.map +1 -1
  83. package/packages/core/dist/{types-CQun9LW6.d.ts → types-B1X87dUW.d.cts} +16 -2
  84. package/packages/core/dist/{types-C-llnPH0.d.cts → types-BVkjSd0q.d.ts} +16 -2
  85. package/packages/core/dist/types-CvsKGweN.d.cts +12 -0
  86. package/packages/core/dist/types-CvsKGweN.d.ts +12 -0
  87. package/packages/core/dist/writers.cjs +2 -1
  88. package/packages/core/dist/writers.cjs.map +1 -1
  89. package/packages/core/dist/writers.d.cts +2 -2
  90. package/packages/core/dist/writers.d.ts +2 -2
  91. package/packages/core/dist/writers.mjs +3 -4
  92. package/packages/core/dist/writers.mjs.map +1 -1
  93. package/packages/cli/dist/chunk-BS5LSKZ3.mjs +0 -3671
  94. package/packages/cli/dist/chunk-BS5LSKZ3.mjs.map +0 -1
  95. package/packages/core/dist/chunk-4JVGFNQH.mjs.map +0 -1
  96. package/packages/core/dist/chunk-A4V2H3YU.mjs.map +0 -1
  97. package/packages/core/dist/chunk-EDTQHZPM.mjs +0 -88
  98. package/packages/core/dist/chunk-EDTQHZPM.mjs.map +0 -1
  99. package/packages/core/dist/chunk-HY7H3CQM.mjs.map +0 -1
  100. package/packages/core/dist/chunk-IZBJAZGF.mjs.map +0 -1
  101. package/packages/core/dist/chunk-NQZPZ2FN.mjs.map +0 -1
  102. package/packages/core/dist/chunk-XUNJMAPD.mjs.map +0 -1
@@ -0,0 +1,1650 @@
1
+ import { extractOutcomesFromPersistedEvents, outcomesMatchingStatus } from './chunk-VFO76UH3.mjs';
2
+
3
+ // packages/core/src/checks/index.ts
4
+ var SEVERITY_RANK = {
5
+ error: 0,
6
+ warning: 1,
7
+ info: 2
8
+ };
9
+ var STATUS_RANK = {
10
+ fail: 0,
11
+ warning: 1,
12
+ pass: 2
13
+ };
14
+ var CONFIDENCE_RANK = {
15
+ unknown: 0,
16
+ heuristic: 1,
17
+ correlated: 2,
18
+ explicit: 3
19
+ };
20
+ var DEFAULT_SENSITIVE_KEYS = [
21
+ "authorization",
22
+ "cookie",
23
+ "token",
24
+ "apikey",
25
+ "api_key",
26
+ "password",
27
+ "secret",
28
+ "email"
29
+ ];
30
+ var DEFAULT_RAW_CONTENT_KEYS = [
31
+ "body",
32
+ "headers",
33
+ "input",
34
+ "messages",
35
+ "output",
36
+ "payload",
37
+ "prompt",
38
+ "requestbody",
39
+ "request_body",
40
+ "responsebody",
41
+ "response_body",
42
+ "rawprompt",
43
+ "raw_prompt",
44
+ "rawoutput",
45
+ "raw_output",
46
+ "toolinput",
47
+ "tool_input",
48
+ "tooloutput",
49
+ "tool_output"
50
+ ];
51
+ var DEFAULT_SECRET_PATTERNS = [
52
+ { id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
53
+ { id: "openai-key", pattern: /sk-[A-Za-z0-9_-]{16,}/ },
54
+ { id: "aws-access-key", pattern: /AKIA[0-9A-Z]{16}/ },
55
+ { id: "github-token", pattern: /gh[opsu]_[A-Za-z0-9_]{20,}/ },
56
+ { id: "key-value-secret", pattern: /(api[_-]?key|token|password|secret)=\S{8,}/i }
57
+ ];
58
+ function compareStrings(a, b) {
59
+ return (a ?? "").localeCompare(b ?? "");
60
+ }
61
+ function diagnostic(code, message, ruleId) {
62
+ return {
63
+ code,
64
+ message,
65
+ severity: "error",
66
+ ...ruleId ? { ruleId } : {}
67
+ };
68
+ }
69
+ function emptySummary() {
70
+ return {
71
+ passed: 0,
72
+ failed: 0,
73
+ warnings: 0,
74
+ errors: 0
75
+ };
76
+ }
77
+ function errorResult(input, diagnostics, selectedRun) {
78
+ return {
79
+ ok: false,
80
+ status: "error",
81
+ format: input.read.format,
82
+ ...selectedRun ? { runId: selectedRun.runId } : {},
83
+ summary: {
84
+ ...emptySummary(),
85
+ errors: diagnostics.filter((item) => item.severity === "error").length
86
+ },
87
+ findings: [],
88
+ diagnostics: [...diagnostics]
89
+ };
90
+ }
91
+ function flattenNodes(nodes) {
92
+ return nodes.flatMap((node) => [node, ...flattenNodes(node.children)]);
93
+ }
94
+ function buildFacts(input, selectedRun) {
95
+ const scopedRuns = selectedRun ? [selectedRun] : input.read.runs;
96
+ const scopedRunIds = new Set(scopedRuns.map((run) => run.runId));
97
+ const scopedEvents = selectedRun === void 0 ? input.read.events : input.read.events.filter((event) => scopedRunIds.has(event.runId));
98
+ const nodes = flattenNodes(scopedRuns.flatMap((run) => run.children));
99
+ const nodesByEventId = /* @__PURE__ */ new Map();
100
+ const childrenByParentId = /* @__PURE__ */ new Map();
101
+ for (const node of nodes) {
102
+ nodesByEventId.set(node.event.eventId, node);
103
+ const parentId = node.event.parentId;
104
+ if (parentId) {
105
+ const children = childrenByParentId.get(parentId) ?? [];
106
+ children.push(node);
107
+ childrenByParentId.set(parentId, children);
108
+ }
109
+ }
110
+ return {
111
+ format: input.read.format,
112
+ runs: Object.freeze([...input.read.runs]),
113
+ events: Object.freeze([...scopedEvents]),
114
+ readerWarnings: Object.freeze([...input.read.warnings]),
115
+ unsupportedFields: Object.freeze([...input.read.unsupportedFields]),
116
+ sourceFiles: Object.freeze([...input.read.sourceFiles]),
117
+ nodesByEventId,
118
+ childrenByParentId,
119
+ rootNodes: Object.freeze(scopedRuns.flatMap((run) => run.children))
120
+ };
121
+ }
122
+ function resolveSelectedRun(input, runId) {
123
+ if (input.selectedRun) {
124
+ if (runId && input.selectedRun.runId !== runId) {
125
+ return {
126
+ diagnostics: [
127
+ diagnostic(
128
+ "AI_CHECK_INVALID_ARGUMENTS",
129
+ `Selected run ${input.selectedRun.runId} does not match requested run ${runId}.`
130
+ )
131
+ ]
132
+ };
133
+ }
134
+ return { run: input.selectedRun, diagnostics: [] };
135
+ }
136
+ if (runId) {
137
+ const run = input.read.runs.find((candidate) => candidate.runId === runId);
138
+ if (!run) {
139
+ return {
140
+ diagnostics: [
141
+ diagnostic("AI_CHECK_RUN_SELECTION_REQUIRED", `Run not found: ${runId}.`)
142
+ ]
143
+ };
144
+ }
145
+ return { run, diagnostics: [] };
146
+ }
147
+ if (input.read.runs.length === 1) {
148
+ return { run: input.read.runs[0], diagnostics: [] };
149
+ }
150
+ if (input.read.runs.length === 0) {
151
+ return {
152
+ diagnostics: [
153
+ diagnostic("AI_CHECK_RUN_SELECTION_REQUIRED", "No runs are available for checks.")
154
+ ]
155
+ };
156
+ }
157
+ return {
158
+ diagnostics: [
159
+ diagnostic(
160
+ "AI_CHECK_RUN_SELECTION_REQUIRED",
161
+ "Multiple runs are available; select a run before executing checks."
162
+ )
163
+ ]
164
+ };
165
+ }
166
+ function selectRules(rules, selectedIds) {
167
+ const diagnostics = [];
168
+ const byId = /* @__PURE__ */ new Map();
169
+ for (const rule of rules) {
170
+ if (byId.has(rule.id)) {
171
+ diagnostics.push(
172
+ diagnostic("AI_CHECK_INVALID_CONFIG", `Duplicate trace check rule id: ${rule.id}.`, rule.id)
173
+ );
174
+ continue;
175
+ }
176
+ byId.set(rule.id, rule);
177
+ }
178
+ if (selectedIds && selectedIds.length > 0) {
179
+ const selected = new Set(selectedIds);
180
+ for (const id of selected) {
181
+ if (!byId.has(id)) {
182
+ diagnostics.push(
183
+ diagnostic("AI_CHECK_INVALID_CONFIG", `Unknown trace check rule id: ${id}.`, id)
184
+ );
185
+ }
186
+ }
187
+ return {
188
+ rules: [...byId.values()].filter((rule) => selected.has(rule.id)).sort(compareRules),
189
+ diagnostics
190
+ };
191
+ }
192
+ return { rules: [...byId.values()].sort(compareRules), diagnostics };
193
+ }
194
+ function compareRules(a, b) {
195
+ return a.id.localeCompare(b.id);
196
+ }
197
+ function eventTimestamp(finding, eventById) {
198
+ const eventId = finding.evidence[0]?.eventId;
199
+ return eventId ? eventById.get(eventId)?.timestamp ?? "" : "";
200
+ }
201
+ function compareFindings(eventById) {
202
+ return (a, b) => {
203
+ if (SEVERITY_RANK[a.severity] !== SEVERITY_RANK[b.severity]) {
204
+ return SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
205
+ }
206
+ const byRule = a.ruleId.localeCompare(b.ruleId);
207
+ if (byRule !== 0) return byRule;
208
+ if (STATUS_RANK[a.status] !== STATUS_RANK[b.status]) {
209
+ return STATUS_RANK[a.status] - STATUS_RANK[b.status];
210
+ }
211
+ const byRun = compareStrings(a.evidence[0]?.runId, b.evidence[0]?.runId);
212
+ if (byRun !== 0) return byRun;
213
+ const byTime = eventTimestamp(a, eventById).localeCompare(eventTimestamp(b, eventById));
214
+ if (byTime !== 0) return byTime;
215
+ const byEvent = compareStrings(a.evidence[0]?.eventId, b.evidence[0]?.eventId);
216
+ if (byEvent !== 0) return byEvent;
217
+ return compareStrings(a.evidence[0]?.path, b.evidence[0]?.path);
218
+ };
219
+ }
220
+ function normalizeFinding(rule, finding) {
221
+ return {
222
+ ruleId: finding.ruleId || rule.id,
223
+ severity: finding.severity ?? rule.defaultSeverity,
224
+ status: finding.status,
225
+ message: finding.message,
226
+ ...finding.expected !== void 0 ? { expected: finding.expected } : {},
227
+ ...finding.actual !== void 0 ? { actual: finding.actual } : {},
228
+ evidence: [...finding.evidence ?? []]
229
+ };
230
+ }
231
+ function summarize(findings, diagnostics) {
232
+ return {
233
+ passed: findings.filter((finding) => finding.status === "pass").length,
234
+ failed: findings.filter(
235
+ (finding) => finding.status === "fail" && finding.severity === "error"
236
+ ).length,
237
+ warnings: findings.filter(
238
+ (finding) => finding.status === "warning" || finding.severity === "warning"
239
+ ).length,
240
+ errors: diagnostics.filter((item) => item.severity === "error").length
241
+ };
242
+ }
243
+ function stringAttr(event, keys) {
244
+ for (const key of keys) {
245
+ const value = event.attributes?.[key];
246
+ if (typeof value === "string" && value.trim() !== "") return value;
247
+ }
248
+ return void 0;
249
+ }
250
+ function numericAttr(event, keys) {
251
+ for (const key of keys) {
252
+ const value = event.attributes?.[key];
253
+ if (typeof value === "number" && Number.isFinite(value)) return value;
254
+ }
255
+ return void 0;
256
+ }
257
+ function booleanAttr(event, keys) {
258
+ for (const key of keys) {
259
+ const value = event.attributes?.[key];
260
+ if (typeof value === "boolean") return value;
261
+ }
262
+ return void 0;
263
+ }
264
+ function stripPrefix(name, prefixes) {
265
+ for (const prefix of prefixes) {
266
+ if (name.startsWith(prefix)) return name.slice(prefix.length);
267
+ }
268
+ return name;
269
+ }
270
+ function eventEvidence(event, path) {
271
+ return {
272
+ runId: event.runId,
273
+ eventId: event.eventId,
274
+ parentId: event.parentId,
275
+ traceId: event.trace?.traceId,
276
+ spanId: event.trace?.spanId,
277
+ kind: event.kind,
278
+ name: event.name,
279
+ status: event.status,
280
+ ...path ? { path } : {}
281
+ };
282
+ }
283
+ function runEvidence(run) {
284
+ return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
285
+ }
286
+ function failFinding(ruleId, message, evidence, expected, actual) {
287
+ return {
288
+ ruleId,
289
+ severity: "error",
290
+ status: "fail",
291
+ message,
292
+ ...expected !== void 0 ? { expected } : {},
293
+ ...actual !== void 0 ? { actual } : {},
294
+ evidence: [...evidence]
295
+ };
296
+ }
297
+ function toolName(event) {
298
+ return stringAttr(event, ["toolName", "tool"]) ?? stripPrefix(event.name, ["tool:", "function:", "mcp-tools:"]);
299
+ }
300
+ function llmModel(event) {
301
+ return stringAttr(event, ["model", "modelId", "responseModelId", "modelName", "model_name"]) ?? stripPrefix(event.name, ["llm:", "generation:", "transcription:", "speech:"]);
302
+ }
303
+ function llmProvider(event) {
304
+ return stringAttr(event, ["provider", "providerName", "provider_name"]);
305
+ }
306
+ function llmFinishReason(event) {
307
+ return stringAttr(event, ["finishReason", "rawFinishReason", "finish_reason"]);
308
+ }
309
+ function retryCount(event) {
310
+ return numericAttr(event, ["retryCount", "retryAttempt", "retry_attempt", "attempt"]);
311
+ }
312
+ function finishedEvents(context, kind) {
313
+ return context.events.filter(
314
+ (event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
315
+ );
316
+ }
317
+ function firstIndexByName(events) {
318
+ const index = /* @__PURE__ */ new Map();
319
+ for (const [position, event] of events.entries()) {
320
+ const name = toolName(event);
321
+ if (!index.has(name)) index.set(name, position);
322
+ }
323
+ return index;
324
+ }
325
+ function isRecord(value) {
326
+ return typeof value === "object" && value !== null && !Array.isArray(value);
327
+ }
328
+ function eventMap(events) {
329
+ return new Map(events.map((event) => [event.eventId, event]));
330
+ }
331
+ function parseEventTime(value) {
332
+ if (!value) return void 0;
333
+ const parsed = Date.parse(value);
334
+ return Number.isFinite(parsed) ? parsed : void 0;
335
+ }
336
+ function eventStartMs(event) {
337
+ return parseEventTime(event.startedAt) ?? parseEventTime(event.timestamp);
338
+ }
339
+ function eventEndMs(event) {
340
+ const endedAt = parseEventTime(event.endedAt);
341
+ if (endedAt !== void 0) return endedAt;
342
+ const startedAt = eventStartMs(event);
343
+ if (startedAt !== void 0 && event.durationMs !== void 0 && Number.isFinite(event.durationMs)) {
344
+ return startedAt + event.durationMs;
345
+ }
346
+ return void 0;
347
+ }
348
+ function sortedStrings(values) {
349
+ return [...values ?? []].sort((a, b) => a.localeCompare(b));
350
+ }
351
+ function normalizedKey(value) {
352
+ return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
353
+ }
354
+ function lastPathSegment(path) {
355
+ const parts = path.split(".");
356
+ return parts[parts.length - 1] ?? path;
357
+ }
358
+ function valueType(value) {
359
+ if (Array.isArray(value)) return "array";
360
+ if (value === null) return "null";
361
+ return typeof value;
362
+ }
363
+ function serializedByteLength(value) {
364
+ try {
365
+ return Buffer.byteLength(JSON.stringify(value), "utf-8");
366
+ } catch {
367
+ return void 0;
368
+ }
369
+ }
370
+ function pushValueEntries(entries, event, value, path, key, depth = 0) {
371
+ entries.push({ event, path, key, value });
372
+ if (depth >= 8) return;
373
+ if (Array.isArray(value)) {
374
+ for (const [index, item] of value.entries()) {
375
+ pushValueEntries(entries, event, item, `${path}.${index}`, String(index), depth + 1);
376
+ }
377
+ return;
378
+ }
379
+ if (!isRecord(value)) return;
380
+ for (const nestedKey of Object.keys(value).sort((a, b) => a.localeCompare(b))) {
381
+ pushValueEntries(
382
+ entries,
383
+ event,
384
+ value[nestedKey],
385
+ `${path}.${nestedKey}`,
386
+ nestedKey,
387
+ depth + 1
388
+ );
389
+ }
390
+ }
391
+ function eventValueEntries(event, options = {}) {
392
+ const entries = [];
393
+ if (event.attributes !== void 0) {
394
+ pushValueEntries(entries, event, event.attributes, "attributes", "attributes");
395
+ }
396
+ if (options.includeSummaries) {
397
+ if (event.inputSummary !== void 0) {
398
+ pushValueEntries(entries, event, event.inputSummary, "inputSummary", "inputSummary");
399
+ }
400
+ if (event.outputSummary !== void 0) {
401
+ pushValueEntries(entries, event, event.outputSummary, "outputSummary", "outputSummary");
402
+ }
403
+ }
404
+ if (options.includeError && event.error !== void 0) {
405
+ pushValueEntries(entries, event, event.error, "error", "error");
406
+ }
407
+ return entries;
408
+ }
409
+ function limitFindings(findings, maxFindings) {
410
+ if (maxFindings === void 0 || findings.length <= maxFindings) return findings;
411
+ return findings.slice(0, Math.max(0, maxFindings));
412
+ }
413
+ function hasRedactionMarker(value, markers) {
414
+ return markers.some((marker) => value.includes(marker)) || /^\[HASH:[A-Za-z0-9_-]+\]$/.test(value);
415
+ }
416
+ function isSensitiveKey(key, sensitiveKeys) {
417
+ if (!key) return false;
418
+ const normalized = normalizedKey(key);
419
+ return sensitiveKeys.some((sensitive) => normalized.includes(normalizedKey(sensitive)));
420
+ }
421
+ function isRawContentKey(key, forbiddenKeys) {
422
+ if (!key) return false;
423
+ const normalized = normalizedKey(key);
424
+ return forbiddenKeys.some((forbidden) => normalized === normalizedKey(forbidden));
425
+ }
426
+ function parentMarkedUnresolved(event) {
427
+ if (booleanAttr(event, [
428
+ "parentUnresolved",
429
+ "unresolvedParent",
430
+ "relationshipUnresolved",
431
+ "unresolvedRelationship"
432
+ ]) === true) {
433
+ return true;
434
+ }
435
+ const resolution = stringAttr(event, [
436
+ "parentResolution",
437
+ "relationshipResolution",
438
+ "relationshipStatus"
439
+ ]);
440
+ return resolution === "unresolved" || resolution === "missing-parent";
441
+ }
442
+ function signalName(event, attributeKeys, prefixes) {
443
+ return stringAttr(event, attributeKeys) ?? stripPrefix(event.name, prefixes);
444
+ }
445
+ function guardrailEvents(context) {
446
+ return finishedEvents2().filter((event) => {
447
+ const name = event.name.toLowerCase();
448
+ if (name.startsWith("guardrail:") || name.includes(".guardrail.")) return true;
449
+ return stringAttr(event, ["guardrailName", "guardrail", "guardrailId"]) !== void 0;
450
+ });
451
+ function finishedEvents2() {
452
+ return context.events.filter((event) => event.status !== "running");
453
+ }
454
+ }
455
+ function retryValue(event) {
456
+ return retryCount(event) ?? 0;
457
+ }
458
+ function eventDurationMs(event) {
459
+ if (event.durationMs !== void 0) return event.durationMs;
460
+ const start = eventStartMs(event);
461
+ const end = eventEndMs(event);
462
+ return start !== void 0 && end !== void 0 && end >= start ? end - start : void 0;
463
+ }
464
+ function treeShape(nodes) {
465
+ const lines = [];
466
+ const visit = (node, path) => {
467
+ lines.push(`${path}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
468
+ node.children.forEach((child, index) => visit(child, `${path}.${index}`));
469
+ };
470
+ nodes.forEach((node, index) => visit(node, String(index)));
471
+ return lines;
472
+ }
473
+ function statusShape(context) {
474
+ return context.events.map((event) => `${event.kind}:${event.name}:${event.status ?? "unknown"}`).sort((a, b) => a.localeCompare(b));
475
+ }
476
+ function toolShape(context) {
477
+ return finishedEvents(context, "TOOL").map(
478
+ (event) => [
479
+ toolName(event),
480
+ event.status ?? "unknown",
481
+ retryValue(event),
482
+ eventDurationMs(event) ?? "unknown"
483
+ ].join(":")
484
+ );
485
+ }
486
+ function llmShape(context) {
487
+ return finishedEvents(context, "LLM").map(
488
+ (event) => [
489
+ llmProvider(event) ?? "unknown",
490
+ llmModel(event) ?? "unknown",
491
+ llmFinishReason(event) ?? "unknown",
492
+ event.tokenUsage?.input ?? 0,
493
+ event.tokenUsage?.output ?? 0,
494
+ event.tokenUsage?.total ?? 0,
495
+ event.tokenUsage?.cached ?? 0
496
+ ].join(":")
497
+ );
498
+ }
499
+ function errorShape(context) {
500
+ return context.events.filter((event) => event.status === "error" || event.error !== void 0).map(
501
+ (event) => [
502
+ event.kind,
503
+ event.name,
504
+ event.error?.name ?? "Error",
505
+ event.error?.code ?? "unknown"
506
+ ].join(":")
507
+ ).sort((a, b) => a.localeCompare(b));
508
+ }
509
+ function retrievalShape(context) {
510
+ return finishedEvents(context, "RETRIEVER").map(
511
+ (event) => signalName(event, ["retrievalName", "retrieverName", "retriever"], ["retriever:", "retrieval:"])
512
+ ).sort((a, b) => a.localeCompare(b));
513
+ }
514
+ function guardrailShape(context) {
515
+ return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
516
+ }
517
+ function firstEvidenceForKind(context, kind, path) {
518
+ const event = context.events.find((candidate) => candidate.kind === kind);
519
+ return event ? [eventEvidence(event, path)] : runEvidence(context.selectedRun);
520
+ }
521
+ function baselineDiffFinding(message, evidence, expected, actual) {
522
+ return failFinding("baseline.regression", message, evidence, expected, actual);
523
+ }
524
+ function createRunStatusRule(options = {}) {
525
+ const expected = options.expected ?? "ok";
526
+ const allowIncomplete = options.allowIncomplete === true;
527
+ return {
528
+ id: "run.status",
529
+ category: "run",
530
+ defaultSeverity: "error",
531
+ evaluate(context) {
532
+ const findings = [];
533
+ const actual = context.selectedRun?.status ?? "unknown";
534
+ if (actual !== expected) {
535
+ findings.push(
536
+ failFinding(
537
+ "run.status",
538
+ `Run status ${actual} did not match expected ${expected}.`,
539
+ runEvidence(context.selectedRun),
540
+ expected,
541
+ actual
542
+ )
543
+ );
544
+ }
545
+ if (!allowIncomplete) {
546
+ const running = context.events.filter((event) => event.status === "running");
547
+ if (running.length > 0) {
548
+ findings.push(
549
+ failFinding(
550
+ "run.status",
551
+ "Run contains incomplete running events.",
552
+ running.map((event) => eventEvidence(event)),
553
+ "no running events",
554
+ running.length
555
+ )
556
+ );
557
+ }
558
+ }
559
+ return findings;
560
+ }
561
+ };
562
+ }
563
+ function createRunDurationRule(options) {
564
+ return {
565
+ id: "run.duration",
566
+ category: "run",
567
+ defaultSeverity: "error",
568
+ evaluate(context) {
569
+ const actual = context.selectedRun?.durationMs;
570
+ if (actual === void 0 || actual <= options.maxDurationMs) return [];
571
+ return [
572
+ failFinding(
573
+ "run.duration",
574
+ `Run duration ${actual}ms exceeded ${options.maxDurationMs}ms.`,
575
+ runEvidence(context.selectedRun),
576
+ { maxDurationMs: options.maxDurationMs },
577
+ actual
578
+ )
579
+ ];
580
+ }
581
+ };
582
+ }
583
+ function createMaxStepDurationRule(options) {
584
+ return {
585
+ id: "run.maxStepDuration",
586
+ category: "run",
587
+ defaultSeverity: "error",
588
+ evaluate(context) {
589
+ const over = context.events.filter((event) => {
590
+ const duration = eventDurationMs(event);
591
+ return duration !== void 0 && duration > options.maxDurationMs;
592
+ });
593
+ if (over.length === 0) return [];
594
+ return [
595
+ failFinding(
596
+ "run.maxStepDuration",
597
+ `${over.length} step(s) exceeded max duration ${options.maxDurationMs}ms.`,
598
+ over.map((event) => eventEvidence(event, "durationMs")),
599
+ { maxDurationMs: options.maxDurationMs },
600
+ over.map((event) => ({
601
+ eventId: event.eventId,
602
+ name: event.name,
603
+ durationMs: eventDurationMs(event)
604
+ }))
605
+ )
606
+ ];
607
+ }
608
+ };
609
+ }
610
+ function createStallDetectionRule(options = {}) {
611
+ const requireEndedAt = options.requireEndedAt === true;
612
+ return {
613
+ id: "run.stall",
614
+ category: "run",
615
+ defaultSeverity: "warning",
616
+ evaluate(context) {
617
+ const findings = [];
618
+ const running = context.events.filter((event) => event.status === "running");
619
+ if (running.length > 0) {
620
+ findings.push(
621
+ failFinding(
622
+ "run.stall",
623
+ `Found ${running.length} event(s) still running (possible stall).`,
624
+ running.map((event) => eventEvidence(event, "status")),
625
+ "no running events",
626
+ running.length
627
+ )
628
+ );
629
+ }
630
+ if (requireEndedAt) {
631
+ const incomplete = context.events.filter(
632
+ (event) => event.startedAt !== void 0 && event.endedAt === void 0 && event.status !== "running"
633
+ );
634
+ if (incomplete.length > 0) {
635
+ findings.push(
636
+ failFinding(
637
+ "run.stall",
638
+ `Found ${incomplete.length} started event(s) without endedAt.`,
639
+ incomplete.map((event) => eventEvidence(event, "endedAt")),
640
+ "endedAt for started events",
641
+ incomplete.length
642
+ )
643
+ );
644
+ }
645
+ }
646
+ return findings;
647
+ }
648
+ };
649
+ }
650
+ function createRequireCompletedRule() {
651
+ return {
652
+ id: "run.requireCompleted",
653
+ category: "run",
654
+ defaultSeverity: "error",
655
+ evaluate(context) {
656
+ const findings = [];
657
+ const runStatus = context.selectedRun?.status;
658
+ if (runStatus === "running") {
659
+ findings.push(
660
+ failFinding(
661
+ "run.requireCompleted",
662
+ "Run is still running.",
663
+ runEvidence(context.selectedRun),
664
+ "completed run",
665
+ runStatus
666
+ )
667
+ );
668
+ }
669
+ const running = context.events.filter((event) => event.status === "running");
670
+ if (running.length > 0) {
671
+ findings.push(
672
+ failFinding(
673
+ "run.requireCompleted",
674
+ `Run has ${running.length} incomplete running event(s).`,
675
+ running.map((event) => eventEvidence(event, "status")),
676
+ "no running events",
677
+ running.length
678
+ )
679
+ );
680
+ }
681
+ return findings;
682
+ }
683
+ };
684
+ }
685
+ function createRunEventCountRule(options) {
686
+ return {
687
+ id: "run.eventCount",
688
+ category: "run",
689
+ defaultSeverity: "error",
690
+ evaluate(context) {
691
+ const count = context.events.filter(
692
+ (event) => options.kind === void 0 || event.kind === options.kind
693
+ ).length;
694
+ const findings = [];
695
+ if (options.min !== void 0 && count < options.min) {
696
+ findings.push(
697
+ failFinding(
698
+ "run.eventCount",
699
+ `Event count ${count} was below minimum ${options.min}.`,
700
+ runEvidence(context.selectedRun),
701
+ { kind: options.kind, min: options.min },
702
+ count
703
+ )
704
+ );
705
+ }
706
+ if (options.max !== void 0 && count > options.max) {
707
+ findings.push(
708
+ failFinding(
709
+ "run.eventCount",
710
+ `Event count ${count} exceeded maximum ${options.max}.`,
711
+ runEvidence(context.selectedRun),
712
+ { kind: options.kind, max: options.max },
713
+ count
714
+ )
715
+ );
716
+ }
717
+ return findings;
718
+ }
719
+ };
720
+ }
721
+ function createRunDepthRule(options) {
722
+ return {
723
+ id: "run.depth",
724
+ category: "run",
725
+ defaultSeverity: "error",
726
+ evaluate(context) {
727
+ const nodes = [...context.nodesByEventId.values()];
728
+ const maxDepth = nodes.reduce((max, node) => Math.max(max, node.depth), 0);
729
+ if (maxDepth <= options.maxDepth) return [];
730
+ const deepest = nodes.filter((node) => node.depth === maxDepth);
731
+ return [
732
+ failFinding(
733
+ "run.depth",
734
+ `Run depth ${maxDepth} exceeded ${options.maxDepth}.`,
735
+ deepest.map((node) => ({
736
+ runId: node.event.runId,
737
+ eventId: node.event.eventId,
738
+ parentId: node.event.parentId,
739
+ kind: node.event.kind,
740
+ name: node.event.name,
741
+ status: node.event.status
742
+ })),
743
+ { maxDepth: options.maxDepth },
744
+ maxDepth
745
+ )
746
+ ];
747
+ }
748
+ };
749
+ }
750
+ function createToolUsageRule(options) {
751
+ return {
752
+ id: "tool.usage",
753
+ category: "tool",
754
+ defaultSeverity: "error",
755
+ evaluate(context) {
756
+ const tools = finishedEvents(context, "TOOL");
757
+ const names = tools.map(toolName);
758
+ const nameSet = new Set(names);
759
+ const findings = [];
760
+ for (const required of options.required ?? []) {
761
+ if (!nameSet.has(required)) {
762
+ findings.push(
763
+ failFinding("tool.usage", `Required tool ${required} did not appear.`, runEvidence(context.selectedRun), required, names)
764
+ );
765
+ }
766
+ }
767
+ const forbidden = new Set(options.forbidden ?? []);
768
+ const allowed = options.allowed ? new Set(options.allowed) : void 0;
769
+ for (const event of tools) {
770
+ const name = toolName(event);
771
+ if (forbidden.has(name)) {
772
+ findings.push(
773
+ failFinding("tool.usage", `Forbidden tool ${name} appeared.`, [eventEvidence(event)], "tool absent", name)
774
+ );
775
+ }
776
+ if (allowed && !allowed.has(name)) {
777
+ findings.push(
778
+ failFinding("tool.usage", `Tool ${name} is not in the allowed tool set.`, [eventEvidence(event)], [...allowed].sort(), name)
779
+ );
780
+ }
781
+ }
782
+ if (options.minCount !== void 0 && tools.length < options.minCount) {
783
+ findings.push(
784
+ failFinding("tool.usage", `Tool count ${tools.length} was below minimum ${options.minCount}.`, runEvidence(context.selectedRun), { minCount: options.minCount }, tools.length)
785
+ );
786
+ }
787
+ if (options.maxCount !== void 0 && tools.length > options.maxCount) {
788
+ findings.push(
789
+ failFinding("tool.usage", `Tool count ${tools.length} exceeded maximum ${options.maxCount}.`, tools.map((event) => eventEvidence(event)), { maxCount: options.maxCount }, tools.length)
790
+ );
791
+ }
792
+ return findings;
793
+ }
794
+ };
795
+ }
796
+ function createToolOrderingRule(options) {
797
+ return {
798
+ id: "tool.order",
799
+ category: "tool",
800
+ defaultSeverity: "error",
801
+ evaluate(context) {
802
+ const tools = finishedEvents(context, "TOOL");
803
+ const index = firstIndexByName(tools);
804
+ const beforeIndex = index.get(options.before);
805
+ const afterIndex = index.get(options.after);
806
+ if (beforeIndex === void 0 || afterIndex === void 0 || beforeIndex < afterIndex) {
807
+ return [];
808
+ }
809
+ return [
810
+ failFinding(
811
+ "tool.order",
812
+ `Tool ${options.before} must appear before ${options.after}.`,
813
+ [eventEvidence(tools[beforeIndex]), eventEvidence(tools[afterIndex])],
814
+ { before: options.before, after: options.after },
815
+ tools.map(toolName)
816
+ )
817
+ ];
818
+ }
819
+ };
820
+ }
821
+ function createToolFailureRule(options) {
822
+ return {
823
+ id: "tool.failures",
824
+ category: "tool",
825
+ defaultSeverity: "error",
826
+ evaluate(context) {
827
+ const tools = finishedEvents(context, "TOOL");
828
+ const failures = tools.filter((event) => event.status === "error");
829
+ const retries = tools.map((event) => ({ event, count: retryCount(event) })).filter((item) => item.count !== void 0);
830
+ const findings = [];
831
+ if (options.maxFailures !== void 0 && failures.length > options.maxFailures) {
832
+ findings.push(
833
+ failFinding(
834
+ "tool.failures",
835
+ `Tool failure count ${failures.length} exceeded ${options.maxFailures}.`,
836
+ failures.map((event) => eventEvidence(event)),
837
+ { maxFailures: options.maxFailures },
838
+ failures.length
839
+ )
840
+ );
841
+ }
842
+ if (options.maxRetries !== void 0) {
843
+ const excessiveRetries = retries.filter((item) => item.count > options.maxRetries);
844
+ if (excessiveRetries.length > 0) {
845
+ findings.push(
846
+ failFinding(
847
+ "tool.failures",
848
+ `Tool retry count exceeded ${options.maxRetries}.`,
849
+ excessiveRetries.map((item) => eventEvidence(item.event, "attributes.retryCount")),
850
+ { maxRetries: options.maxRetries },
851
+ excessiveRetries.map((item) => ({ tool: toolName(item.event), retries: item.count }))
852
+ )
853
+ );
854
+ }
855
+ }
856
+ return findings;
857
+ }
858
+ };
859
+ }
860
+ function createLlmUsageRule(options) {
861
+ return {
862
+ id: "llm.usage",
863
+ category: "llm",
864
+ defaultSeverity: "error",
865
+ evaluate(context) {
866
+ const llms = finishedEvents(context, "LLM");
867
+ const findings = [];
868
+ const allowedModels = options.allowedModels ? new Set(options.allowedModels) : void 0;
869
+ const allowedProviders = options.allowedProviders ? new Set(options.allowedProviders) : void 0;
870
+ const finishReasons = options.finishReasons ? new Set(options.finishReasons) : void 0;
871
+ if (options.maxCalls !== void 0 && llms.length > options.maxCalls) {
872
+ findings.push(
873
+ failFinding(
874
+ "llm.usage",
875
+ `LLM call count ${llms.length} exceeded ${options.maxCalls}.`,
876
+ llms.map((event) => eventEvidence(event)),
877
+ { maxCalls: options.maxCalls },
878
+ llms.length
879
+ )
880
+ );
881
+ }
882
+ for (const event of llms) {
883
+ const model = llmModel(event);
884
+ const provider = llmProvider(event);
885
+ const finishReason = llmFinishReason(event);
886
+ if (allowedModels && (!model || !allowedModels.has(model))) {
887
+ findings.push(
888
+ failFinding("llm.usage", `LLM model ${model ?? "unknown"} is not allowed.`, [eventEvidence(event, "attributes.model")], [...allowedModels].sort(), model ?? "unknown")
889
+ );
890
+ }
891
+ if (allowedProviders && (!provider || !allowedProviders.has(provider))) {
892
+ findings.push(
893
+ failFinding("llm.usage", `LLM provider ${provider ?? "unknown"} is not allowed.`, [eventEvidence(event, "attributes.provider")], [...allowedProviders].sort(), provider ?? "unknown")
894
+ );
895
+ }
896
+ if (finishReasons && (!finishReason || !finishReasons.has(finishReason))) {
897
+ findings.push(
898
+ failFinding("llm.usage", `LLM finish reason ${finishReason ?? "unknown"} is not allowed.`, [eventEvidence(event, "attributes.finishReason")], [...finishReasons].sort(), finishReason ?? "unknown")
899
+ );
900
+ }
901
+ }
902
+ const tokenTotals = llms.reduce(
903
+ (totals, event) => ({
904
+ input: totals.input + (event.tokenUsage?.input ?? 0),
905
+ output: totals.output + (event.tokenUsage?.output ?? 0),
906
+ total: totals.total + (event.tokenUsage?.total ?? 0),
907
+ cached: totals.cached + (event.tokenUsage?.cached ?? 0)
908
+ }),
909
+ { input: 0, output: 0, total: 0, cached: 0 }
910
+ );
911
+ const tokenLimits = [
912
+ ["input", options.maxInputTokens],
913
+ ["output", options.maxOutputTokens],
914
+ ["total", options.maxTotalTokens],
915
+ ["cached", options.maxCachedTokens]
916
+ ];
917
+ for (const [key, limit] of tokenLimits) {
918
+ if (limit !== void 0 && tokenTotals[key] > limit) {
919
+ findings.push(
920
+ failFinding(
921
+ "llm.usage",
922
+ `LLM ${key} token count ${tokenTotals[key]} exceeded ${limit}.`,
923
+ llms.map((event) => eventEvidence(event, `tokenUsage.${key}`)),
924
+ { [`max${key[0].toUpperCase()}${key.slice(1)}Tokens`]: limit },
925
+ tokenTotals[key]
926
+ )
927
+ );
928
+ }
929
+ }
930
+ return findings;
931
+ }
932
+ };
933
+ }
934
+ function createStructureIncompleteRule(options = {}) {
935
+ return {
936
+ id: "structure.incomplete",
937
+ category: "structure",
938
+ defaultSeverity: "error",
939
+ evaluate(context) {
940
+ const findings = [];
941
+ if (!options.allowRunning) {
942
+ const running = context.events.filter((event) => event.status === "running");
943
+ if (running.length > 0) {
944
+ findings.push(
945
+ failFinding(
946
+ "structure.incomplete",
947
+ "Trace contains incomplete running events.",
948
+ running.map((event) => eventEvidence(event, "status")),
949
+ "no running events",
950
+ running.length
951
+ )
952
+ );
953
+ }
954
+ }
955
+ if (options.requireEndedAtForStarted) {
956
+ const missingEndedAt = context.events.filter(
957
+ (event) => event.startedAt !== void 0 && event.endedAt === void 0 && event.status !== "running"
958
+ );
959
+ if (missingEndedAt.length > 0) {
960
+ findings.push(
961
+ failFinding(
962
+ "structure.incomplete",
963
+ "Trace contains events with startedAt but no endedAt.",
964
+ missingEndedAt.map((event) => eventEvidence(event, "endedAt")),
965
+ "endedAt for started events",
966
+ missingEndedAt.length
967
+ )
968
+ );
969
+ }
970
+ }
971
+ return findings;
972
+ }
973
+ };
974
+ }
975
+ function createStructureOrphanRule(options = {}) {
976
+ const allowMarkedUnresolved = options.allowMarkedUnresolved ?? true;
977
+ return {
978
+ id: "structure.orphan",
979
+ category: "structure",
980
+ defaultSeverity: "error",
981
+ evaluate(context) {
982
+ const byId = eventMap(context.events);
983
+ const orphans = context.events.filter((event) => {
984
+ if (!event.parentId || byId.has(event.parentId)) return false;
985
+ return !(allowMarkedUnresolved && parentMarkedUnresolved(event));
986
+ });
987
+ if (orphans.length === 0) return [];
988
+ return [
989
+ failFinding(
990
+ "structure.orphan",
991
+ "Trace contains events whose parentId is not present in the selected run.",
992
+ orphans.map((event) => eventEvidence(event, "parentId")),
993
+ "parentId resolves to an event in the selected run",
994
+ orphans.length
995
+ )
996
+ ];
997
+ }
998
+ };
999
+ }
1000
+ function createStructureCycleRule() {
1001
+ return {
1002
+ id: "structure.cycle",
1003
+ category: "structure",
1004
+ defaultSeverity: "error",
1005
+ evaluate(context) {
1006
+ const byId = eventMap(context.events);
1007
+ const seenCycles = /* @__PURE__ */ new Set();
1008
+ const findings = [];
1009
+ for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
1010
+ const path = [];
1011
+ const seenAt = /* @__PURE__ */ new Map();
1012
+ let current = event;
1013
+ while (current) {
1014
+ const existing = seenAt.get(current.eventId);
1015
+ if (existing !== void 0) {
1016
+ const cycle = path.slice(existing);
1017
+ const key = cycle.map((item) => item.eventId).sort().join("\0");
1018
+ if (!seenCycles.has(key)) {
1019
+ seenCycles.add(key);
1020
+ findings.push(
1021
+ failFinding(
1022
+ "structure.cycle",
1023
+ "Trace contains a parentId cycle.",
1024
+ cycle.map((item) => eventEvidence(item, "parentId")),
1025
+ "acyclic parentId graph",
1026
+ cycle.map((item) => item.eventId).sort()
1027
+ )
1028
+ );
1029
+ }
1030
+ break;
1031
+ }
1032
+ seenAt.set(current.eventId, path.length);
1033
+ path.push(current);
1034
+ current = current.parentId ? byId.get(current.parentId) : void 0;
1035
+ }
1036
+ }
1037
+ return findings;
1038
+ }
1039
+ };
1040
+ }
1041
+ function createStructureRelationshipRule(options = {}) {
1042
+ return {
1043
+ id: "structure.relationship",
1044
+ category: "structure",
1045
+ defaultSeverity: "error",
1046
+ evaluate(context) {
1047
+ const byId = eventMap(context.events);
1048
+ const findings = [];
1049
+ const minConfidence = options.minConfidence;
1050
+ for (const event of context.events) {
1051
+ if (minConfidence && CONFIDENCE_RANK[event.confidence] < CONFIDENCE_RANK[minConfidence]) {
1052
+ findings.push(
1053
+ failFinding(
1054
+ "structure.relationship",
1055
+ `Event confidence ${event.confidence} is below ${minConfidence}.`,
1056
+ [eventEvidence(event, "confidence")],
1057
+ { minConfidence },
1058
+ event.confidence
1059
+ )
1060
+ );
1061
+ }
1062
+ if (!event.parentId) continue;
1063
+ if (event.parentId === event.eventId) {
1064
+ findings.push(
1065
+ failFinding(
1066
+ "structure.relationship",
1067
+ "Event parentId points to itself.",
1068
+ [eventEvidence(event, "parentId")],
1069
+ "parentId references a distinct event",
1070
+ "self"
1071
+ )
1072
+ );
1073
+ continue;
1074
+ }
1075
+ const parent = byId.get(event.parentId);
1076
+ if (!parent) continue;
1077
+ if (options.requireParentBeforeChild) {
1078
+ const parentTime = eventStartMs(parent);
1079
+ const childTime = eventStartMs(event);
1080
+ if (parentTime !== void 0 && childTime !== void 0 && parentTime > childTime) {
1081
+ findings.push(
1082
+ failFinding(
1083
+ "structure.relationship",
1084
+ "Parent event starts after child event.",
1085
+ [eventEvidence(parent), eventEvidence(event, "parentId")],
1086
+ "parent start <= child start",
1087
+ { parentEventId: parent.eventId, childEventId: event.eventId }
1088
+ )
1089
+ );
1090
+ }
1091
+ }
1092
+ if (options.requireTraceParentSpan && parent.trace?.spanId && event.trace) {
1093
+ const actual = event.trace.parentSpanId;
1094
+ if (actual !== parent.trace.spanId) {
1095
+ findings.push(
1096
+ failFinding(
1097
+ "structure.relationship",
1098
+ "Trace parentSpanId does not match parent spanId.",
1099
+ [eventEvidence(event, "trace.parentSpanId")],
1100
+ { parentSpanId: parent.trace.spanId },
1101
+ actual ?? "missing"
1102
+ )
1103
+ );
1104
+ }
1105
+ }
1106
+ }
1107
+ return findings;
1108
+ }
1109
+ };
1110
+ }
1111
+ function createStructureParallelWidthRule(options) {
1112
+ return {
1113
+ id: "structure.parallelWidth",
1114
+ category: "structure",
1115
+ defaultSeverity: "error",
1116
+ evaluate(context) {
1117
+ const findings = [];
1118
+ const byId = eventMap(context.events);
1119
+ if (options.maxChildren !== void 0) {
1120
+ for (const [parentId, children] of context.childrenByParentId.entries()) {
1121
+ if (children.length <= options.maxChildren) continue;
1122
+ const parent = byId.get(parentId);
1123
+ findings.push(
1124
+ failFinding(
1125
+ "structure.parallelWidth",
1126
+ `Parent ${parentId} has ${children.length} children, exceeding ${options.maxChildren}.`,
1127
+ [
1128
+ ...parent ? [eventEvidence(parent)] : [{ runId: context.selectedRun?.runId, eventId: parentId }],
1129
+ ...children.map((child) => ({
1130
+ runId: child.event.runId,
1131
+ eventId: child.event.eventId,
1132
+ parentId: child.event.parentId,
1133
+ kind: child.event.kind,
1134
+ name: child.event.name,
1135
+ status: child.event.status
1136
+ }))
1137
+ ],
1138
+ { maxChildren: options.maxChildren },
1139
+ children.length
1140
+ )
1141
+ );
1142
+ }
1143
+ }
1144
+ if (options.maxConcurrent !== void 0) {
1145
+ const intervals = context.events.map((event) => ({ event, start: eventStartMs(event), end: eventEndMs(event) })).filter(
1146
+ (item) => item.start !== void 0 && item.end !== void 0 && item.end > item.start
1147
+ );
1148
+ const points = intervals.flatMap((item) => [
1149
+ { time: item.start, delta: 1, event: item.event },
1150
+ { time: item.end, delta: -1, event: item.event }
1151
+ ]);
1152
+ points.sort((a, b) => {
1153
+ const byTime = a.time - b.time;
1154
+ if (byTime !== 0) return byTime;
1155
+ const byDelta = a.delta - b.delta;
1156
+ if (byDelta !== 0) return byDelta;
1157
+ return a.event.eventId.localeCompare(b.event.eventId);
1158
+ });
1159
+ const active = /* @__PURE__ */ new Map();
1160
+ let maxActive = [];
1161
+ for (const point of points) {
1162
+ if (point.delta > 0) {
1163
+ active.set(point.event.eventId, point.event);
1164
+ if (active.size > maxActive.length) {
1165
+ maxActive = [...active.values()].sort((a, b) => a.eventId.localeCompare(b.eventId));
1166
+ }
1167
+ } else {
1168
+ active.delete(point.event.eventId);
1169
+ }
1170
+ }
1171
+ if (maxActive.length > options.maxConcurrent) {
1172
+ findings.push(
1173
+ failFinding(
1174
+ "structure.parallelWidth",
1175
+ `Concurrent event width ${maxActive.length} exceeded ${options.maxConcurrent}.`,
1176
+ maxActive.map((event) => eventEvidence(event)),
1177
+ { maxConcurrent: options.maxConcurrent },
1178
+ maxActive.length
1179
+ )
1180
+ );
1181
+ }
1182
+ }
1183
+ return findings;
1184
+ }
1185
+ };
1186
+ }
1187
+ function createSignalRule(ruleId, label, options, selectEvents, nameForEvent) {
1188
+ return {
1189
+ id: ruleId,
1190
+ category: "structure",
1191
+ defaultSeverity: "error",
1192
+ evaluate(context) {
1193
+ const events = selectEvents(context);
1194
+ const names = events.map(nameForEvent);
1195
+ const nameSet = new Set(names);
1196
+ const findings = [];
1197
+ for (const required of sortedStrings(options.required)) {
1198
+ if (!nameSet.has(required)) {
1199
+ findings.push(
1200
+ failFinding(
1201
+ ruleId,
1202
+ `Required ${label} ${required} did not appear.`,
1203
+ runEvidence(context.selectedRun),
1204
+ required,
1205
+ names
1206
+ )
1207
+ );
1208
+ }
1209
+ }
1210
+ const forbidden = new Set(options.forbidden ?? []);
1211
+ const allowed = options.allowed ? new Set(options.allowed) : void 0;
1212
+ for (const event of events) {
1213
+ const name = nameForEvent(event);
1214
+ if (forbidden.has(name)) {
1215
+ findings.push(
1216
+ failFinding(
1217
+ ruleId,
1218
+ `Forbidden ${label} ${name} appeared.`,
1219
+ [eventEvidence(event)],
1220
+ `${label} absent`,
1221
+ name
1222
+ )
1223
+ );
1224
+ }
1225
+ if (allowed && !allowed.has(name)) {
1226
+ findings.push(
1227
+ failFinding(
1228
+ ruleId,
1229
+ `${label[0].toUpperCase()}${label.slice(1)} ${name} is not in the allowed set.`,
1230
+ [eventEvidence(event)],
1231
+ [...allowed].sort(),
1232
+ name
1233
+ )
1234
+ );
1235
+ }
1236
+ }
1237
+ if (options.minCount !== void 0 && events.length < options.minCount) {
1238
+ findings.push(
1239
+ failFinding(
1240
+ ruleId,
1241
+ `${label[0].toUpperCase()}${label.slice(1)} count ${events.length} was below minimum ${options.minCount}.`,
1242
+ runEvidence(context.selectedRun),
1243
+ { minCount: options.minCount },
1244
+ events.length
1245
+ )
1246
+ );
1247
+ }
1248
+ if (options.maxCount !== void 0 && events.length > options.maxCount) {
1249
+ findings.push(
1250
+ failFinding(
1251
+ ruleId,
1252
+ `${label[0].toUpperCase()}${label.slice(1)} count ${events.length} exceeded maximum ${options.maxCount}.`,
1253
+ events.map((event) => eventEvidence(event)),
1254
+ { maxCount: options.maxCount },
1255
+ events.length
1256
+ )
1257
+ );
1258
+ }
1259
+ return findings;
1260
+ }
1261
+ };
1262
+ }
1263
+ function createRetrievalRule(options) {
1264
+ return createSignalRule(
1265
+ "structure.retrieval",
1266
+ "retrieval",
1267
+ options,
1268
+ (context) => finishedEvents(context, "RETRIEVER"),
1269
+ (event) => signalName(event, ["retrievalName", "retrieverName", "retriever"], ["retriever:", "retrieval:"])
1270
+ );
1271
+ }
1272
+ function createGuardrailRule(options) {
1273
+ return createSignalRule(
1274
+ "structure.guardrail",
1275
+ "guardrail",
1276
+ options,
1277
+ guardrailEvents,
1278
+ (event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])
1279
+ );
1280
+ }
1281
+ function createDecisionRule(options) {
1282
+ return createSignalRule(
1283
+ "structure.decision",
1284
+ "decision",
1285
+ options,
1286
+ (context) => finishedEvents(context, "DECISION"),
1287
+ (event) => signalName(event, ["decisionName", "decision", "decisionId"], ["decision:"])
1288
+ );
1289
+ }
1290
+ function createSafetyRedactionRule(options = {}) {
1291
+ const sensitiveKeys = options.sensitiveKeys ?? DEFAULT_SENSITIVE_KEYS;
1292
+ const markers = options.redactedMarkers ?? ["[REDACTED]", "[REDACTED:"];
1293
+ return {
1294
+ id: "safety.redaction",
1295
+ category: "safety",
1296
+ defaultSeverity: "error",
1297
+ evaluate(context) {
1298
+ const findings = [];
1299
+ for (const event of context.events) {
1300
+ for (const entry of eventValueEntries(event, { includeSummaries: true, includeError: true })) {
1301
+ if (!isSensitiveKey(entry.key ?? lastPathSegment(entry.path), sensitiveKeys)) continue;
1302
+ if (typeof entry.value === "string" && hasRedactionMarker(entry.value, markers)) continue;
1303
+ findings.push(
1304
+ failFinding(
1305
+ "safety.redaction",
1306
+ `Sensitive-looking field at ${entry.path} is not redacted.`,
1307
+ [eventEvidence(event, entry.path)],
1308
+ "redaction marker",
1309
+ { path: entry.path, valueType: valueType(entry.value) }
1310
+ )
1311
+ );
1312
+ }
1313
+ }
1314
+ return limitFindings(findings, options.maxFindings);
1315
+ }
1316
+ };
1317
+ }
1318
+ function createSafetyRawContentRule(options = {}) {
1319
+ const forbiddenKeys = options.forbiddenKeys ?? DEFAULT_RAW_CONTENT_KEYS;
1320
+ return {
1321
+ id: "safety.rawPrompt",
1322
+ category: "safety",
1323
+ defaultSeverity: "error",
1324
+ evaluate(context) {
1325
+ const findings = [];
1326
+ for (const event of context.events) {
1327
+ for (const entry of eventValueEntries(event, { includeSummaries: options.includeSummaries })) {
1328
+ const key = entry.key ?? lastPathSegment(entry.path);
1329
+ if (!isRawContentKey(key, forbiddenKeys)) continue;
1330
+ findings.push(
1331
+ failFinding(
1332
+ "safety.rawPrompt",
1333
+ `Raw content-like field ${entry.path} is present.`,
1334
+ [eventEvidence(event, entry.path)],
1335
+ "metadata-only trace fields",
1336
+ { path: entry.path, valueType: valueType(entry.value) }
1337
+ )
1338
+ );
1339
+ }
1340
+ }
1341
+ return limitFindings(findings, options.maxFindings);
1342
+ }
1343
+ };
1344
+ }
1345
+ function createSafetySecretPatternRule(options = {}) {
1346
+ const patterns = options.patterns ?? DEFAULT_SECRET_PATTERNS;
1347
+ const maxStringLength = options.maxStringLength ?? 4096;
1348
+ return {
1349
+ id: "safety.secretPattern",
1350
+ category: "safety",
1351
+ defaultSeverity: "error",
1352
+ evaluate(context) {
1353
+ const findings = [];
1354
+ for (const event of context.events) {
1355
+ for (const entry of eventValueEntries(event, { includeSummaries: true, includeError: true })) {
1356
+ if (typeof entry.value !== "string") continue;
1357
+ const sample = entry.value.slice(0, maxStringLength);
1358
+ for (const pattern of patterns) {
1359
+ pattern.pattern.lastIndex = 0;
1360
+ if (!pattern.pattern.test(sample)) continue;
1361
+ pattern.pattern.lastIndex = 0;
1362
+ findings.push(
1363
+ failFinding(
1364
+ "safety.secretPattern",
1365
+ `Secret-like pattern ${pattern.id} matched at ${entry.path}.`,
1366
+ [eventEvidence(event, entry.path)],
1367
+ "no secret-like strings",
1368
+ { pattern: pattern.id, path: entry.path }
1369
+ )
1370
+ );
1371
+ break;
1372
+ }
1373
+ }
1374
+ }
1375
+ return limitFindings(findings, options.maxFindings);
1376
+ }
1377
+ };
1378
+ }
1379
+ function createSafetyOversizedAttributeRule(options) {
1380
+ return {
1381
+ id: "safety.oversizedAttribute",
1382
+ category: "safety",
1383
+ defaultSeverity: "error",
1384
+ evaluate(context) {
1385
+ const findings = [];
1386
+ for (const event of context.events) {
1387
+ for (const entry of eventValueEntries(event, { includeSummaries: true, includeError: true })) {
1388
+ if (typeof entry.value === "string" && options.maxStringLength !== void 0 && entry.value.length > options.maxStringLength) {
1389
+ findings.push(
1390
+ failFinding(
1391
+ "safety.oversizedAttribute",
1392
+ `String at ${entry.path} exceeds ${options.maxStringLength} characters.`,
1393
+ [eventEvidence(event, entry.path)],
1394
+ { maxStringLength: options.maxStringLength },
1395
+ { path: entry.path, length: entry.value.length }
1396
+ )
1397
+ );
1398
+ }
1399
+ if (Array.isArray(entry.value) && options.maxArrayLength !== void 0 && entry.value.length > options.maxArrayLength) {
1400
+ findings.push(
1401
+ failFinding(
1402
+ "safety.oversizedAttribute",
1403
+ `Array at ${entry.path} exceeds ${options.maxArrayLength} items.`,
1404
+ [eventEvidence(event, entry.path)],
1405
+ { maxArrayLength: options.maxArrayLength },
1406
+ { path: entry.path, length: entry.value.length }
1407
+ )
1408
+ );
1409
+ }
1410
+ if (isRecord(entry.value) && options.maxObjectKeys !== void 0 && Object.keys(entry.value).length > options.maxObjectKeys) {
1411
+ findings.push(
1412
+ failFinding(
1413
+ "safety.oversizedAttribute",
1414
+ `Object at ${entry.path} exceeds ${options.maxObjectKeys} keys.`,
1415
+ [eventEvidence(event, entry.path)],
1416
+ { maxObjectKeys: options.maxObjectKeys },
1417
+ { path: entry.path, keys: Object.keys(entry.value).length }
1418
+ )
1419
+ );
1420
+ }
1421
+ if (options.maxSerializedBytes !== void 0) {
1422
+ const bytes = serializedByteLength(entry.value);
1423
+ if (bytes !== void 0 && bytes > options.maxSerializedBytes) {
1424
+ findings.push(
1425
+ failFinding(
1426
+ "safety.oversizedAttribute",
1427
+ `Value at ${entry.path} exceeds ${options.maxSerializedBytes} serialized bytes.`,
1428
+ [eventEvidence(event, entry.path)],
1429
+ { maxSerializedBytes: options.maxSerializedBytes },
1430
+ { path: entry.path, bytes }
1431
+ )
1432
+ );
1433
+ }
1434
+ }
1435
+ }
1436
+ }
1437
+ return limitFindings(findings, options.maxFindings);
1438
+ }
1439
+ };
1440
+ }
1441
+ function createBaselineRegressionRule(options) {
1442
+ return {
1443
+ id: "baseline.regression",
1444
+ category: "baseline",
1445
+ defaultSeverity: "error",
1446
+ evaluate(context) {
1447
+ const baselineSelection = resolveSelectedRun(options.baseline, options.baselineRunId);
1448
+ if (baselineSelection.diagnostics.length > 0 || !baselineSelection.run) {
1449
+ return [
1450
+ failFinding(
1451
+ "baseline.regression",
1452
+ "Baseline run could not be selected.",
1453
+ runEvidence(context.selectedRun),
1454
+ "selectable baseline run",
1455
+ baselineSelection.diagnostics.map((item) => item.code)
1456
+ )
1457
+ ];
1458
+ }
1459
+ const baselineFacts = buildFacts(options.baseline, baselineSelection.run);
1460
+ const baselineContext = {
1461
+ ...baselineFacts,
1462
+ selectedRun: baselineSelection.run,
1463
+ sourceLabel: options.baseline.sourceLabel
1464
+ };
1465
+ const findings = [];
1466
+ const durationToleranceMs = options.durationToleranceMs ?? 0;
1467
+ if (options.compareFormat && baselineContext.format !== context.format) {
1468
+ findings.push(
1469
+ baselineDiffFinding(
1470
+ "Trace format differs from baseline.",
1471
+ runEvidence(context.selectedRun),
1472
+ baselineContext.format,
1473
+ context.format
1474
+ )
1475
+ );
1476
+ }
1477
+ const baselineRunStatus = baselineContext.selectedRun?.status ?? "unknown";
1478
+ const candidateRunStatus = context.selectedRun?.status ?? "unknown";
1479
+ if (baselineRunStatus !== candidateRunStatus) {
1480
+ findings.push(
1481
+ baselineDiffFinding(
1482
+ "Run status differs from baseline.",
1483
+ runEvidence(context.selectedRun),
1484
+ baselineRunStatus,
1485
+ candidateRunStatus
1486
+ )
1487
+ );
1488
+ }
1489
+ const baselineDuration = baselineContext.selectedRun?.durationMs;
1490
+ const candidateDuration = context.selectedRun?.durationMs;
1491
+ if (baselineDuration !== void 0 && candidateDuration !== void 0 && Math.abs(candidateDuration - baselineDuration) > durationToleranceMs) {
1492
+ findings.push(
1493
+ baselineDiffFinding(
1494
+ "Run duration differs from baseline beyond tolerance.",
1495
+ runEvidence(context.selectedRun),
1496
+ { durationMs: baselineDuration, toleranceMs: durationToleranceMs },
1497
+ candidateDuration
1498
+ )
1499
+ );
1500
+ }
1501
+ const comparisons = [
1502
+ {
1503
+ label: "Tree shape",
1504
+ path: "tree",
1505
+ expected: treeShape(baselineContext.rootNodes),
1506
+ actual: treeShape(context.rootNodes),
1507
+ evidence: runEvidence(context.selectedRun)
1508
+ },
1509
+ {
1510
+ label: "Event statuses",
1511
+ path: "status",
1512
+ expected: statusShape(baselineContext),
1513
+ actual: statusShape(context),
1514
+ evidence: runEvidence(context.selectedRun)
1515
+ },
1516
+ {
1517
+ label: "Tool usage",
1518
+ path: "tool",
1519
+ expected: toolShape(baselineContext),
1520
+ actual: toolShape(context),
1521
+ evidence: firstEvidenceForKind(context, "TOOL", "tool")
1522
+ },
1523
+ {
1524
+ label: "LLM usage",
1525
+ path: "llm",
1526
+ expected: llmShape(baselineContext),
1527
+ actual: llmShape(context),
1528
+ evidence: firstEvidenceForKind(context, "LLM", "llm")
1529
+ },
1530
+ {
1531
+ label: "Error profile",
1532
+ path: "error",
1533
+ expected: errorShape(baselineContext),
1534
+ actual: errorShape(context),
1535
+ evidence: firstEvidenceForKind(context, "ERROR", "error")
1536
+ },
1537
+ {
1538
+ label: "Retrieval signals",
1539
+ path: "retrieval",
1540
+ expected: retrievalShape(baselineContext),
1541
+ actual: retrievalShape(context),
1542
+ evidence: firstEvidenceForKind(context, "RETRIEVER", "retrieval")
1543
+ },
1544
+ {
1545
+ label: "Guardrail signals",
1546
+ path: "guardrail",
1547
+ expected: guardrailShape(baselineContext),
1548
+ actual: guardrailShape(context),
1549
+ evidence: guardrailEvents(context)[0] ? [eventEvidence(guardrailEvents(context)[0], "guardrail")] : runEvidence(context.selectedRun)
1550
+ }
1551
+ ];
1552
+ for (const comparison of comparisons) {
1553
+ if (JSON.stringify(comparison.expected) === JSON.stringify(comparison.actual)) {
1554
+ continue;
1555
+ }
1556
+ findings.push(
1557
+ baselineDiffFinding(
1558
+ `${comparison.label} differs from baseline.`,
1559
+ comparison.evidence.length > 0 ? comparison.evidence : [{ runId: context.selectedRun?.runId, path: comparison.path }],
1560
+ comparison.expected,
1561
+ comparison.actual
1562
+ )
1563
+ );
1564
+ }
1565
+ return findings;
1566
+ }
1567
+ };
1568
+ }
1569
+ function createObservedOutcomeRule(options = {}) {
1570
+ const failOn = options.failOn ?? ["failed"];
1571
+ return {
1572
+ id: "outcome.status",
1573
+ category: "run",
1574
+ defaultSeverity: "error",
1575
+ evaluate(context) {
1576
+ const outcomes = extractOutcomesFromPersistedEvents(context.events);
1577
+ const matching = outcomesMatchingStatus(outcomes, failOn);
1578
+ if (matching.length === 0) return [];
1579
+ return [
1580
+ failFinding(
1581
+ "outcome.status",
1582
+ `Observed outcome count ${matching.length} matched [${failOn.join(", ")}].`,
1583
+ matching.map((outcome) => ({
1584
+ runId: outcome.runId,
1585
+ eventId: outcome.outcomeId,
1586
+ ...outcome.parentId !== void 0 ? { parentId: outcome.parentId } : {},
1587
+ kind: "OUTCOME",
1588
+ name: outcome.name,
1589
+ status: outcome.status,
1590
+ path: `outcome.${outcome.name}`
1591
+ })),
1592
+ { failOn },
1593
+ matching.map((outcome) => ({
1594
+ name: outcome.name,
1595
+ status: outcome.status,
1596
+ expectation: outcome.expectation
1597
+ }))
1598
+ )
1599
+ ];
1600
+ }
1601
+ };
1602
+ }
1603
+ function runTraceChecks(input, options = {}) {
1604
+ const selected = resolveSelectedRun(input, options.runId);
1605
+ if (selected.diagnostics.length > 0) {
1606
+ return errorResult(input, selected.diagnostics, selected.run);
1607
+ }
1608
+ const rules = selectRules(options.rules ?? [], options.select);
1609
+ if (rules.diagnostics.length > 0) {
1610
+ return errorResult(input, rules.diagnostics, selected.run);
1611
+ }
1612
+ const facts = buildFacts(input, selected.run);
1613
+ const context = {
1614
+ ...facts,
1615
+ ...selected.run ? { selectedRun: selected.run } : {},
1616
+ ...input.sourceLabel ? { sourceLabel: input.sourceLabel } : {}
1617
+ };
1618
+ const diagnostics = [];
1619
+ const findings = [];
1620
+ for (const rule of rules.rules) {
1621
+ try {
1622
+ findings.push(...rule.evaluate(context).map((finding) => normalizeFinding(rule, finding)));
1623
+ } catch (error) {
1624
+ const message = error instanceof Error ? error.message : String(error);
1625
+ diagnostics.push(
1626
+ diagnostic("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
1627
+ );
1628
+ }
1629
+ }
1630
+ if (diagnostics.length > 0) {
1631
+ return errorResult(input, diagnostics, selected.run);
1632
+ }
1633
+ const eventById = new Map(input.read.events.map((event) => [event.eventId, event]));
1634
+ const sortedFindings = findings.sort(compareFindings(eventById));
1635
+ const summary = summarize(sortedFindings, diagnostics);
1636
+ const status = summary.failed > 0 ? "fail" : "pass";
1637
+ return {
1638
+ ok: status === "pass",
1639
+ status,
1640
+ format: input.read.format,
1641
+ ...selected.run ? { runId: selected.run.runId } : {},
1642
+ summary,
1643
+ findings: sortedFindings,
1644
+ diagnostics
1645
+ };
1646
+ }
1647
+
1648
+ export { createBaselineRegressionRule, createDecisionRule, createGuardrailRule, createLlmUsageRule, createMaxStepDurationRule, createObservedOutcomeRule, createRequireCompletedRule, createRetrievalRule, createRunDepthRule, createRunDurationRule, createRunEventCountRule, createRunStatusRule, createSafetyOversizedAttributeRule, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createStallDetectionRule, createStructureCycleRule, createStructureIncompleteRule, createStructureOrphanRule, createStructureParallelWidthRule, createStructureRelationshipRule, createToolFailureRule, createToolOrderingRule, createToolUsageRule, runTraceChecks };
1649
+ //# sourceMappingURL=chunk-QLYOCCEF.mjs.map
1650
+ //# sourceMappingURL=chunk-QLYOCCEF.mjs.map