@agent-inspect/mcp-server 6.27.0 → 6.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-2E7RWC2E.mjs → chunk-BLLG3Q4N.mjs} +1975 -1922
- package/dist/chunk-BLLG3Q4N.mjs.map +1 -0
- package/dist/cli.cjs +1973 -1920
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.cjs +1973 -1920
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -3
- package/dist/chunk-2E7RWC2E.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -351,6 +351,8 @@ function isPersistedTokenUsage(value) {
|
|
|
351
351
|
if (!isOptionalNonNegativeNumber(value.output)) return false;
|
|
352
352
|
if (!isOptionalNonNegativeNumber(value.total)) return false;
|
|
353
353
|
if (!isOptionalNonNegativeNumber(value.cached)) return false;
|
|
354
|
+
if (!isOptionalNonNegativeNumber(value.cacheWrite)) return false;
|
|
355
|
+
if (!isOptionalNonNegativeNumber(value.reasoning)) return false;
|
|
354
356
|
return true;
|
|
355
357
|
}
|
|
356
358
|
function isPersistedTraceContext(value) {
|
|
@@ -1253,7 +1255,13 @@ function buildRunSummary(events) {
|
|
|
1253
1255
|
tokensInput: isNonNegativeFiniteNumber(s.metadata?.tokens?.input) ? s.metadata.tokens.input : void 0,
|
|
1254
1256
|
tokensOutput: isNonNegativeFiniteNumber(s.metadata?.tokens?.output) ? s.metadata.tokens.output : void 0,
|
|
1255
1257
|
tokensTotal: isNonNegativeFiniteNumber(s.metadata?.tokens?.total) ? s.metadata.tokens.total : void 0,
|
|
1256
|
-
tokensCached: isNonNegativeFiniteNumber(s.metadata?.tokens?.cached) ? s.metadata.tokens.cached : void 0
|
|
1258
|
+
tokensCached: isNonNegativeFiniteNumber(s.metadata?.tokens?.cached) ? s.metadata.tokens.cached : void 0,
|
|
1259
|
+
tokensCacheWrite: isNonNegativeFiniteNumber(
|
|
1260
|
+
s.metadata?.tokens?.cacheWrite
|
|
1261
|
+
) ? s.metadata.tokens.cacheWrite : void 0,
|
|
1262
|
+
tokensReasoning: isNonNegativeFiniteNumber(
|
|
1263
|
+
s.metadata?.tokens?.reasoning
|
|
1264
|
+
) ? s.metadata.tokens.reasoning : void 0
|
|
1257
1265
|
});
|
|
1258
1266
|
}
|
|
1259
1267
|
}
|
|
@@ -1277,9 +1285,13 @@ function buildRunSummary(events) {
|
|
|
1277
1285
|
let totalTokensOutput = 0;
|
|
1278
1286
|
let totalTokensTotal = 0;
|
|
1279
1287
|
let totalTokensCached = 0;
|
|
1288
|
+
let totalTokensCacheWrite = 0;
|
|
1289
|
+
let totalTokensReasoning = 0;
|
|
1280
1290
|
let tokenBearingSteps = 0;
|
|
1281
1291
|
let stepsWithKnownTotal = 0;
|
|
1282
1292
|
let hasCachedTokens = false;
|
|
1293
|
+
let hasCacheWriteTokens = false;
|
|
1294
|
+
let hasReasoningTokens = false;
|
|
1283
1295
|
const depthCache = /* @__PURE__ */ new Map();
|
|
1284
1296
|
for (const [id, s] of steps.entries()) {
|
|
1285
1297
|
totalSteps += 1;
|
|
@@ -1294,7 +1306,7 @@ function buildRunSummary(events) {
|
|
|
1294
1306
|
longestStep = { name: s.name, durationMs: s.durationMs, type: s.type };
|
|
1295
1307
|
}
|
|
1296
1308
|
}
|
|
1297
|
-
if (s.tokensInput !== void 0 || s.tokensOutput !== void 0 || s.tokensTotal !== void 0 || s.tokensCached !== void 0) {
|
|
1309
|
+
if (s.tokensInput !== void 0 || s.tokensOutput !== void 0 || s.tokensTotal !== void 0 || s.tokensCached !== void 0 || s.tokensCacheWrite !== void 0 || s.tokensReasoning !== void 0) {
|
|
1298
1310
|
tokenBearingSteps += 1;
|
|
1299
1311
|
if (s.tokensInput !== void 0) totalTokensInput += s.tokensInput;
|
|
1300
1312
|
if (s.tokensOutput !== void 0) totalTokensOutput += s.tokensOutput;
|
|
@@ -1309,6 +1321,14 @@ function buildRunSummary(events) {
|
|
|
1309
1321
|
totalTokensCached += s.tokensCached;
|
|
1310
1322
|
hasCachedTokens = true;
|
|
1311
1323
|
}
|
|
1324
|
+
if (s.tokensCacheWrite !== void 0) {
|
|
1325
|
+
totalTokensCacheWrite += s.tokensCacheWrite;
|
|
1326
|
+
hasCacheWriteTokens = true;
|
|
1327
|
+
}
|
|
1328
|
+
if (s.tokensReasoning !== void 0) {
|
|
1329
|
+
totalTokensReasoning += s.tokensReasoning;
|
|
1330
|
+
hasReasoningTokens = true;
|
|
1331
|
+
}
|
|
1312
1332
|
}
|
|
1313
1333
|
}
|
|
1314
1334
|
const summary = {
|
|
@@ -1328,7 +1348,9 @@ function buildRunSummary(events) {
|
|
|
1328
1348
|
input: totalTokensInput,
|
|
1329
1349
|
output: totalTokensOutput,
|
|
1330
1350
|
...stepsWithKnownTotal === tokenBearingSteps ? { total: totalTokensTotal } : {},
|
|
1331
|
-
...hasCachedTokens ? { cached: totalTokensCached } : {}
|
|
1351
|
+
...hasCachedTokens ? { cached: totalTokensCached } : {},
|
|
1352
|
+
...hasCacheWriteTokens ? { cacheWrite: totalTokensCacheWrite } : {},
|
|
1353
|
+
...hasReasoningTokens ? { reasoning: totalTokensReasoning } : {}
|
|
1332
1354
|
}
|
|
1333
1355
|
} : {}
|
|
1334
1356
|
};
|
|
@@ -1599,6 +1621,12 @@ function renderRunWhat(summary, options = {}) {
|
|
|
1599
1621
|
if (summary.totalTokens.cached !== void 0) {
|
|
1600
1622
|
tokenParts.push(`${summary.totalTokens.cached} cached`);
|
|
1601
1623
|
}
|
|
1624
|
+
if (summary.totalTokens.cacheWrite !== void 0) {
|
|
1625
|
+
tokenParts.push(`${summary.totalTokens.cacheWrite} cache-write`);
|
|
1626
|
+
}
|
|
1627
|
+
if (summary.totalTokens.reasoning !== void 0) {
|
|
1628
|
+
tokenParts.push(`${summary.totalTokens.reasoning} reasoning`);
|
|
1629
|
+
}
|
|
1602
1630
|
lines.push(`Tokens: ${tokenParts.join(" / ")}`);
|
|
1603
1631
|
}
|
|
1604
1632
|
if (showCorrelation && summary.correlation) {
|
|
@@ -2150,6 +2178,9 @@ function inferEvidenceFileRole(relativePath) {
|
|
|
2150
2178
|
if (base === "check-results.json") {
|
|
2151
2179
|
return "checks";
|
|
2152
2180
|
}
|
|
2181
|
+
if (base === "contract.resolved.json") {
|
|
2182
|
+
return "contract";
|
|
2183
|
+
}
|
|
2153
2184
|
if (base === "redaction-report.json") {
|
|
2154
2185
|
return "redaction-report";
|
|
2155
2186
|
}
|
|
@@ -2216,1921 +2247,1461 @@ function buildEvidenceManifest(parts) {
|
|
|
2216
2247
|
},
|
|
2217
2248
|
assessment,
|
|
2218
2249
|
...parts.semantics !== void 0 ? { semantics: { ...parts.semantics } } : {},
|
|
2250
|
+
...parts.contract !== void 0 ? { contract: { ...parts.contract } } : {},
|
|
2219
2251
|
files: buildEvidenceFileEntries(parts.files)
|
|
2220
2252
|
};
|
|
2221
2253
|
}
|
|
2222
2254
|
|
|
2223
|
-
// packages/core/src/
|
|
2224
|
-
var
|
|
2255
|
+
// packages/core/src/diagnostics/programmatic.ts
|
|
2256
|
+
var PROGRAMMATIC_DIAGNOSTIC_SPECS = Object.freeze({
|
|
2257
|
+
AI_TRACE_INPUT_INVALID: {
|
|
2258
|
+
code: "AI_TRACE_INPUT_INVALID",
|
|
2259
|
+
summary: 'Expected { type: "file", path }, { type: "directory", path }, { type: "string", content }, { type: "buffer", content }, or { type: "stdin" }.',
|
|
2260
|
+
remediation: "For a file path, use openTraceFile(path).",
|
|
2261
|
+
relatedCodes: ["invalid_input"]
|
|
2262
|
+
},
|
|
2263
|
+
AI_TRACE_FORMAT_UNSUPPORTED: {
|
|
2264
|
+
code: "AI_TRACE_FORMAT_UNSUPPORTED",
|
|
2265
|
+
summary: "No trace reader could detect the input format.",
|
|
2266
|
+
remediation: "Pass an AgentInspect JSONL file via openTraceFile, or set options.format to a registered reader.",
|
|
2267
|
+
relatedCodes: ["unsupported_format"]
|
|
2268
|
+
},
|
|
2269
|
+
AI_TRACE_FORMAT_AMBIGUOUS: {
|
|
2270
|
+
code: "AI_TRACE_FORMAT_AMBIGUOUS",
|
|
2271
|
+
summary: "Multiple trace readers matched the input with equal confidence.",
|
|
2272
|
+
remediation: "Set options.format explicitly to disambiguate the reader.",
|
|
2273
|
+
relatedCodes: ["ambiguous_format"]
|
|
2274
|
+
},
|
|
2275
|
+
AI_TRACE_FACTS_INPUT_NOT_NORMALIZED: {
|
|
2276
|
+
code: "AI_TRACE_FACTS_INPUT_NOT_NORMALIZED",
|
|
2277
|
+
summary: "TraceFacts requires TraceReadResult or PersistedInspectEvent[].",
|
|
2278
|
+
remediation: "Use openTraceFile() to normalize a JSONL trace first."
|
|
2279
|
+
},
|
|
2280
|
+
AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED: {
|
|
2281
|
+
code: "AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED",
|
|
2282
|
+
summary: "Multiple runs are available; select a run before executing checks.",
|
|
2283
|
+
remediation: "Pass options.runId or TraceCheckInput.selectedRun.",
|
|
2284
|
+
relatedCodes: ["AI_CHECK_RUN_SELECTION_REQUIRED"]
|
|
2285
|
+
},
|
|
2286
|
+
AI_TRACE_RELATIONSHIP_SELF_PARENT: {
|
|
2287
|
+
code: "AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
2288
|
+
summary: "A parentId equals its own event/step id.",
|
|
2289
|
+
remediation: "Reject at capture (AI_LANGGRAPH_SELF_PARENT_REJECTED) or drop via logical projection; do not invent replacement parents.",
|
|
2290
|
+
relatedCodes: [
|
|
2291
|
+
"AI_LANGGRAPH_SELF_PARENT_REJECTED",
|
|
2292
|
+
"AI_LOGICAL_SELF_PARENT_REMOVED"
|
|
2293
|
+
]
|
|
2294
|
+
},
|
|
2295
|
+
AI_TRACE_RELATIONSHIP_CYCLE: {
|
|
2296
|
+
code: "AI_TRACE_RELATIONSHIP_CYCLE",
|
|
2297
|
+
summary: "Trace contains a parentId cycle.",
|
|
2298
|
+
remediation: "Use visibility-first tree linking for legacy fixtures; prefer acyclic capture for new adapter output.",
|
|
2299
|
+
relatedCodes: ["structure.cycle"]
|
|
2300
|
+
}
|
|
2301
|
+
});
|
|
2302
|
+
function formatProgrammaticDiagnostic(code, detail) {
|
|
2303
|
+
const spec = PROGRAMMATIC_DIAGNOSTIC_SPECS[code];
|
|
2304
|
+
const summary = detail?.trim() ? detail.trim() : spec.summary;
|
|
2305
|
+
return `${code}: ${summary} Remediation: ${spec.remediation}`;
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
// packages/core/src/safety/sensitive-key.ts
|
|
2309
|
+
function keyHasExplicitSeparator(value) {
|
|
2310
|
+
return /[_\-.]/.test(value);
|
|
2311
|
+
}
|
|
2312
|
+
function normalizeSensitiveKey(value) {
|
|
2313
|
+
return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
2314
|
+
}
|
|
2315
|
+
var NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
|
|
2316
|
+
[
|
|
2317
|
+
"tokens",
|
|
2318
|
+
"max_tokens",
|
|
2319
|
+
"min_tokens",
|
|
2320
|
+
"ls_max_tokens",
|
|
2321
|
+
"token_count",
|
|
2322
|
+
"token_limit",
|
|
2323
|
+
"token_budget",
|
|
2324
|
+
"input_tokens",
|
|
2325
|
+
"output_tokens",
|
|
2326
|
+
"total_tokens",
|
|
2327
|
+
"cached_tokens",
|
|
2328
|
+
"prompt_tokens",
|
|
2329
|
+
"completion_tokens"
|
|
2330
|
+
].map(normalizeSensitiveKey)
|
|
2331
|
+
);
|
|
2332
|
+
var DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
|
|
2225
2333
|
"authorization",
|
|
2226
2334
|
"cookie",
|
|
2227
2335
|
"token",
|
|
2336
|
+
"access_token",
|
|
2337
|
+
"accesstoken",
|
|
2338
|
+
"auth_token",
|
|
2339
|
+
"authtoken",
|
|
2340
|
+
"refresh_token",
|
|
2341
|
+
"refreshtoken",
|
|
2342
|
+
"id_token",
|
|
2343
|
+
"idtoken",
|
|
2344
|
+
"bearer_token",
|
|
2345
|
+
"bearertoken",
|
|
2346
|
+
"api_token",
|
|
2347
|
+
"apitoken",
|
|
2228
2348
|
"apikey",
|
|
2349
|
+
"api_key",
|
|
2229
2350
|
"password",
|
|
2230
2351
|
"secret",
|
|
2231
2352
|
"email"
|
|
2232
2353
|
];
|
|
2233
|
-
function
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
return false;
|
|
2354
|
+
function isTokenCredentialKey(normalized) {
|
|
2355
|
+
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
2356
|
+
if (normalized === "token") return true;
|
|
2357
|
+
if (normalized.endsWith("tokens")) return false;
|
|
2358
|
+
return normalized.endsWith("token");
|
|
2239
2359
|
}
|
|
2240
|
-
function
|
|
2241
|
-
if (
|
|
2242
|
-
|
|
2243
|
-
if (
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2360
|
+
function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSITIVE_KEYS) {
|
|
2361
|
+
if (!key) return false;
|
|
2362
|
+
const normalized = normalizeSensitiveKey(key);
|
|
2363
|
+
if (!normalized) return false;
|
|
2364
|
+
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
2365
|
+
const allowPrefixCompound = keyHasExplicitSeparator(key);
|
|
2366
|
+
for (const sensitive of sensitiveKeys) {
|
|
2367
|
+
const s = normalizeSensitiveKey(sensitive);
|
|
2368
|
+
if (!s) continue;
|
|
2369
|
+
if (s === "token") {
|
|
2370
|
+
if (isTokenCredentialKey(normalized)) return true;
|
|
2371
|
+
continue;
|
|
2372
|
+
}
|
|
2373
|
+
if (normalized === s) return true;
|
|
2374
|
+
if (normalized.endsWith(`_${s}`)) return true;
|
|
2375
|
+
if (allowPrefixCompound && normalized.startsWith(`${s}_`)) return true;
|
|
2248
2376
|
}
|
|
2249
|
-
return
|
|
2377
|
+
return false;
|
|
2250
2378
|
}
|
|
2251
|
-
|
|
2252
|
-
|
|
2379
|
+
|
|
2380
|
+
// packages/core/src/checks/logical-events.ts
|
|
2381
|
+
function isRecord6(value) {
|
|
2382
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2253
2383
|
}
|
|
2254
|
-
function
|
|
2255
|
-
|
|
2384
|
+
function legacyEvent(event) {
|
|
2385
|
+
const value = event.attributes?.legacyEvent;
|
|
2386
|
+
return typeof value === "string" ? value : void 0;
|
|
2256
2387
|
}
|
|
2257
|
-
function
|
|
2258
|
-
|
|
2259
|
-
if (
|
|
2260
|
-
|
|
2261
|
-
const out = {};
|
|
2262
|
-
for (const k of Object.keys(o).sort()) {
|
|
2263
|
-
out[k] = sortKeysDeep(o[k]);
|
|
2264
|
-
}
|
|
2265
|
-
return out;
|
|
2388
|
+
function stepIdOf(event) {
|
|
2389
|
+
const value = event.attributes?.stepId;
|
|
2390
|
+
if (typeof value === "string" && value.trim() !== "") return value;
|
|
2391
|
+
return void 0;
|
|
2266
2392
|
}
|
|
2267
|
-
function
|
|
2268
|
-
|
|
2269
|
-
|
|
2393
|
+
function cloneEvent(event) {
|
|
2394
|
+
return {
|
|
2395
|
+
...event,
|
|
2396
|
+
...event.attributes !== void 0 ? { attributes: { ...event.attributes } } : {},
|
|
2397
|
+
...event.error !== void 0 ? { error: { ...event.error } } : {},
|
|
2398
|
+
...event.tokenUsage !== void 0 ? { tokenUsage: { ...event.tokenUsage } } : {},
|
|
2399
|
+
...event.source !== void 0 ? { source: { ...event.source } } : {}
|
|
2400
|
+
};
|
|
2270
2401
|
}
|
|
2271
|
-
function
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
}
|
|
2281
|
-
const v = attrs2[key];
|
|
2282
|
-
out[key] = compactValue(v, maxLen, redacted);
|
|
2402
|
+
function mergeAttributes(start, complete) {
|
|
2403
|
+
const merged = {
|
|
2404
|
+
...isRecord6(complete.attributes) ? complete.attributes : {},
|
|
2405
|
+
...isRecord6(start.attributes) ? start.attributes : {}
|
|
2406
|
+
};
|
|
2407
|
+
merged.legacyEvent = start.attributes?.legacyEvent ?? complete.attributes?.legacyEvent;
|
|
2408
|
+
merged.legacyCompleteEvent = complete.attributes?.legacyEvent;
|
|
2409
|
+
if (complete.attributes?.errorStack !== void 0) {
|
|
2410
|
+
merged.errorStack = complete.attributes.errorStack;
|
|
2283
2411
|
}
|
|
2284
|
-
return
|
|
2412
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
2285
2413
|
}
|
|
2286
|
-
function
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2414
|
+
function pairStartComplete(start, complete) {
|
|
2415
|
+
const attributes = mergeAttributes(start, complete);
|
|
2416
|
+
const paired = {
|
|
2417
|
+
...cloneEvent(start),
|
|
2418
|
+
status: complete.status,
|
|
2419
|
+
timestamp: complete.timestamp ?? start.timestamp,
|
|
2420
|
+
...complete.endedAt !== void 0 ? { endedAt: complete.endedAt } : {},
|
|
2421
|
+
...complete.durationMs !== void 0 ? { durationMs: complete.durationMs } : {},
|
|
2422
|
+
...complete.error !== void 0 ? { error: { ...complete.error } } : {},
|
|
2423
|
+
...complete.tokenUsage !== void 0 && start.tokenUsage === void 0 ? { tokenUsage: { ...complete.tokenUsage } } : {},
|
|
2424
|
+
...attributes !== void 0 ? { attributes } : {}
|
|
2425
|
+
};
|
|
2426
|
+
return {
|
|
2427
|
+
...paired,
|
|
2428
|
+
sourceEventIds: Object.freeze([start.eventId, complete.eventId]),
|
|
2429
|
+
projection: {
|
|
2430
|
+
paired: true,
|
|
2431
|
+
absorbedEventIds: Object.freeze([complete.eventId]),
|
|
2432
|
+
parentNormalized: false,
|
|
2433
|
+
...start.parentId !== void 0 ? { originalParentId: start.parentId } : {}
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2302
2436
|
}
|
|
2303
|
-
function
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2437
|
+
function asLogical(event, extras) {
|
|
2438
|
+
return {
|
|
2439
|
+
...cloneEvent(event),
|
|
2440
|
+
sourceEventIds: Object.freeze([event.eventId]),
|
|
2441
|
+
projection: {
|
|
2442
|
+
paired: false,
|
|
2443
|
+
absorbedEventIds: Object.freeze([]),
|
|
2444
|
+
parentNormalized: extras?.parentNormalized === true,
|
|
2445
|
+
...{}
|
|
2309
2446
|
}
|
|
2310
|
-
}
|
|
2311
|
-
walk(tree.children);
|
|
2312
|
-
return out;
|
|
2447
|
+
};
|
|
2313
2448
|
}
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
}
|
|
2322
|
-
function mapStepStatus(s) {
|
|
2323
|
-
if (s === void 0) return "running";
|
|
2324
|
-
return s;
|
|
2325
|
-
}
|
|
2326
|
-
function manualTraceEventsToComparableRun(events) {
|
|
2327
|
-
const started = events.find((e) => e.event === "run_started");
|
|
2328
|
-
if (!started || started.event !== "run_started") {
|
|
2329
|
-
throw new Error("Invalid trace: missing run_started");
|
|
2330
|
-
}
|
|
2331
|
-
const rs = started;
|
|
2332
|
-
const runId = rs.runId;
|
|
2333
|
-
const completedAll = events.filter((e) => e.event === "run_completed");
|
|
2334
|
-
const lastCompleted = completedAll[completedAll.length - 1];
|
|
2335
|
-
let runStatus;
|
|
2336
|
-
if (lastCompleted === void 0) runStatus = "running";
|
|
2337
|
-
else runStatus = lastCompleted.status;
|
|
2338
|
-
const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
2339
|
-
const steps = /* @__PURE__ */ new Map();
|
|
2340
|
-
let order = 0;
|
|
2341
|
-
for (const e of events) {
|
|
2342
|
-
if (e.event !== "step_started") continue;
|
|
2343
|
-
const s = e;
|
|
2344
|
-
const meta = s.metadata ? { ...s.metadata } : void 0;
|
|
2345
|
-
steps.set(s.stepId, {
|
|
2346
|
-
id: s.stepId,
|
|
2347
|
-
parentId: s.parentId,
|
|
2348
|
-
name: s.name,
|
|
2349
|
-
type: s.type,
|
|
2350
|
-
order: order++,
|
|
2351
|
-
timestamp: s.timestamp,
|
|
2352
|
-
metadata: meta
|
|
2353
|
-
});
|
|
2354
|
-
}
|
|
2355
|
-
for (const e of events) {
|
|
2356
|
-
if (e.event !== "step_completed") continue;
|
|
2357
|
-
const acc = steps.get(e.stepId);
|
|
2358
|
-
if (!acc) continue;
|
|
2359
|
-
acc.status = e.status;
|
|
2360
|
-
acc.durationMs = e.durationMs;
|
|
2361
|
-
if (e.error?.message) acc.errorMsg = e.error.message;
|
|
2362
|
-
const extra = e;
|
|
2363
|
-
if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
|
|
2364
|
-
acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
|
|
2365
|
-
}
|
|
2366
|
-
}
|
|
2367
|
-
const nodes = /* @__PURE__ */ new Map();
|
|
2368
|
-
for (const acc of steps.values()) {
|
|
2369
|
-
let meta = acc.metadata ? { ...acc.metadata } : void 0;
|
|
2370
|
-
if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
|
|
2371
|
-
meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
|
|
2372
|
-
}
|
|
2373
|
-
const outputPreview = extractOutputPreview(meta);
|
|
2374
|
-
if (meta !== void 0 && ("outputPreview" in meta || "resultPreview" in meta)) {
|
|
2375
|
-
delete meta.outputPreview;
|
|
2376
|
-
delete meta.resultPreview;
|
|
2377
|
-
}
|
|
2378
|
-
const sc = {
|
|
2379
|
-
id: acc.id,
|
|
2380
|
-
name: acc.name,
|
|
2381
|
-
type: acc.type,
|
|
2382
|
-
status: mapStepStatus(acc.status),
|
|
2383
|
-
durationMs: acc.durationMs,
|
|
2384
|
-
error: acc.errorMsg,
|
|
2385
|
-
metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
|
|
2386
|
-
outputPreview,
|
|
2387
|
-
children: []
|
|
2388
|
-
};
|
|
2389
|
-
nodes.set(acc.id, sc);
|
|
2449
|
+
function projectLogicalEvents(events) {
|
|
2450
|
+
const diagnostics = [];
|
|
2451
|
+
const byRun = /* @__PURE__ */ new Map();
|
|
2452
|
+
for (const event of events) {
|
|
2453
|
+
const list = byRun.get(event.runId) ?? [];
|
|
2454
|
+
list.push(event);
|
|
2455
|
+
byRun.set(event.runId, list);
|
|
2390
2456
|
}
|
|
2391
|
-
const
|
|
2392
|
-
const
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
const
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2457
|
+
const absorbedIds = /* @__PURE__ */ new Set();
|
|
2458
|
+
const logicalByRawId = /* @__PURE__ */ new Map();
|
|
2459
|
+
const stepIdToLogicalId = /* @__PURE__ */ new Map();
|
|
2460
|
+
const logical = [];
|
|
2461
|
+
const orderedRuns = [...byRun.keys()].sort((a, b) => a.localeCompare(b));
|
|
2462
|
+
for (const runId of orderedRuns) {
|
|
2463
|
+
const runEvents = byRun.get(runId) ?? [];
|
|
2464
|
+
const starts = [];
|
|
2465
|
+
const completes = [];
|
|
2466
|
+
const others = [];
|
|
2467
|
+
for (const event of runEvents) {
|
|
2468
|
+
const legacy = legacyEvent(event);
|
|
2469
|
+
if (legacy === "step_started" || legacy === "run_started" && event.status === "running") {
|
|
2470
|
+
starts.push(event);
|
|
2471
|
+
} else if (legacy === "step_completed" || legacy === "run_completed") {
|
|
2472
|
+
completes.push(event);
|
|
2473
|
+
} else {
|
|
2474
|
+
others.push(event);
|
|
2475
|
+
}
|
|
2403
2476
|
}
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2477
|
+
const usedCompletes = /* @__PURE__ */ new Set();
|
|
2478
|
+
for (const start of starts) {
|
|
2479
|
+
const stepId = stepIdOf(start);
|
|
2480
|
+
let match;
|
|
2481
|
+
if (legacyEvent(start) === "run_started") {
|
|
2482
|
+
const candidates = completes.filter(
|
|
2483
|
+
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "run_completed"
|
|
2484
|
+
);
|
|
2485
|
+
if (candidates.length > 1) {
|
|
2486
|
+
diagnostics.push({
|
|
2487
|
+
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
2488
|
+
message: `Multiple run_completed rows for run ${runId}; using first by eventId.`,
|
|
2489
|
+
eventIds: candidates.map((c) => c.eventId)
|
|
2490
|
+
});
|
|
2491
|
+
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
2492
|
+
}
|
|
2493
|
+
match = candidates[0];
|
|
2494
|
+
} else if (stepId) {
|
|
2495
|
+
const candidates = completes.filter(
|
|
2496
|
+
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "step_completed" && stepIdOf(c) === stepId
|
|
2497
|
+
);
|
|
2498
|
+
if (candidates.length > 1) {
|
|
2499
|
+
diagnostics.push({
|
|
2500
|
+
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
2501
|
+
message: `Multiple step_completed rows for stepId ${stepId}; using first by eventId.`,
|
|
2502
|
+
eventIds: candidates.map((c) => c.eventId)
|
|
2503
|
+
});
|
|
2504
|
+
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
2505
|
+
}
|
|
2506
|
+
match = candidates[0];
|
|
2507
|
+
}
|
|
2508
|
+
if (match) {
|
|
2509
|
+
usedCompletes.add(match.eventId);
|
|
2510
|
+
absorbedIds.add(match.eventId);
|
|
2511
|
+
const paired = pairStartComplete(start, match);
|
|
2512
|
+
logical.push(paired);
|
|
2513
|
+
logicalByRawId.set(start.eventId, paired);
|
|
2514
|
+
logicalByRawId.set(match.eventId, paired);
|
|
2515
|
+
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, paired.eventId);
|
|
2516
|
+
} else {
|
|
2517
|
+
diagnostics.push({
|
|
2518
|
+
code: "AI_LOGICAL_PAIR_UNMATCHED_START",
|
|
2519
|
+
message: `No matching complete for start ${start.eventId}.`,
|
|
2520
|
+
eventIds: [start.eventId]
|
|
2521
|
+
});
|
|
2522
|
+
const alone = asLogical(start);
|
|
2523
|
+
logical.push(alone);
|
|
2524
|
+
logicalByRawId.set(start.eventId, alone);
|
|
2525
|
+
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2436
2526
|
}
|
|
2437
2527
|
}
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2528
|
+
for (const complete of completes) {
|
|
2529
|
+
if (usedCompletes.has(complete.eventId)) continue;
|
|
2530
|
+
diagnostics.push({
|
|
2531
|
+
code: "AI_LOGICAL_PAIR_UNMATCHED_COMPLETE",
|
|
2532
|
+
message: `No matching start for complete ${complete.eventId}.`,
|
|
2533
|
+
eventIds: [complete.eventId]
|
|
2534
|
+
});
|
|
2535
|
+
const alone = asLogical(complete);
|
|
2536
|
+
logical.push(alone);
|
|
2537
|
+
logicalByRawId.set(complete.eventId, alone);
|
|
2538
|
+
const stepId = stepIdOf(complete);
|
|
2539
|
+
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
2540
|
+
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2541
|
+
}
|
|
2442
2542
|
}
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2543
|
+
for (const event of others) {
|
|
2544
|
+
const alone = asLogical(event);
|
|
2545
|
+
logical.push(alone);
|
|
2546
|
+
logicalByRawId.set(event.eventId, alone);
|
|
2547
|
+
const stepId = stepIdOf(event);
|
|
2548
|
+
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
2549
|
+
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2550
|
+
}
|
|
2448
2551
|
}
|
|
2449
2552
|
}
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2553
|
+
const logicalById = new Map(logical.map((e) => [e.eventId, e]));
|
|
2554
|
+
const normalized = [];
|
|
2555
|
+
for (const event of logical) {
|
|
2556
|
+
const originalParentId = event.parentId;
|
|
2557
|
+
if (!originalParentId) {
|
|
2558
|
+
normalized.push(event);
|
|
2559
|
+
continue;
|
|
2453
2560
|
}
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2561
|
+
let nextParent = originalParentId;
|
|
2562
|
+
let remapped = false;
|
|
2563
|
+
const viaAbsorbed = logicalByRawId.get(originalParentId);
|
|
2564
|
+
if (viaAbsorbed && viaAbsorbed.eventId !== originalParentId) {
|
|
2565
|
+
nextParent = viaAbsorbed.eventId;
|
|
2566
|
+
remapped = true;
|
|
2567
|
+
} else if (!logicalById.has(originalParentId)) {
|
|
2568
|
+
const viaStep = stepIdToLogicalId.get(`${event.runId}:${originalParentId}`);
|
|
2569
|
+
if (viaStep) {
|
|
2570
|
+
nextParent = viaStep;
|
|
2571
|
+
remapped = true;
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
if (!remapped) {
|
|
2575
|
+
if (originalParentId === event.eventId) {
|
|
2576
|
+
diagnostics.push({
|
|
2577
|
+
code: "AI_LOGICAL_SELF_PARENT_REMOVED",
|
|
2578
|
+
message: formatProgrammaticDiagnostic(
|
|
2579
|
+
"AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
2580
|
+
`Removed self-parent edge on ${event.eventId}.`
|
|
2581
|
+
),
|
|
2582
|
+
eventIds: [event.eventId]
|
|
2583
|
+
});
|
|
2584
|
+
const { parentId: _drop, ...rest } = event;
|
|
2585
|
+
normalized.push({
|
|
2586
|
+
...rest,
|
|
2587
|
+
projection: {
|
|
2588
|
+
...event.projection,
|
|
2589
|
+
parentNormalized: true,
|
|
2590
|
+
originalParentId
|
|
2591
|
+
}
|
|
2592
|
+
});
|
|
2593
|
+
continue;
|
|
2594
|
+
}
|
|
2595
|
+
if (!logicalById.has(originalParentId) && !logicalByRawId.has(originalParentId)) {
|
|
2596
|
+
const mapping = event.attributes?.parentMapping;
|
|
2597
|
+
const unresolved = mapping === "unresolved" || event.attributes?.parentUnresolved === true || event.attributes?.unresolvedParent === true || // LangGraph/framework scaffolding sentinel labels are not event ids.
|
|
2598
|
+
/^LangGraph$/i.test(originalParentId) || originalParentId.startsWith("unresolved:");
|
|
2599
|
+
if (!unresolved) {
|
|
2600
|
+
diagnostics.push({
|
|
2601
|
+
code: "AI_LOGICAL_PARENT_UNRESOLVED",
|
|
2602
|
+
message: `Parent ${originalParentId} unresolved for ${event.eventId}.`,
|
|
2603
|
+
eventIds: [event.eventId]
|
|
2604
|
+
});
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
normalized.push(event);
|
|
2608
|
+
continue;
|
|
2609
|
+
}
|
|
2610
|
+
if (nextParent === event.eventId) {
|
|
2611
|
+
diagnostics.push({
|
|
2612
|
+
code: "AI_LOGICAL_SELF_PARENT_REMOVED",
|
|
2613
|
+
message: formatProgrammaticDiagnostic(
|
|
2614
|
+
"AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
2615
|
+
`Removed self-parent edge on ${event.eventId} (was ${originalParentId}).`
|
|
2616
|
+
),
|
|
2617
|
+
eventIds: [event.eventId]
|
|
2618
|
+
});
|
|
2619
|
+
const { parentId: _drop, ...rest } = event;
|
|
2620
|
+
normalized.push({
|
|
2621
|
+
...rest,
|
|
2622
|
+
projection: {
|
|
2623
|
+
...event.projection,
|
|
2624
|
+
parentNormalized: true,
|
|
2625
|
+
originalParentId
|
|
2626
|
+
}
|
|
2627
|
+
});
|
|
2628
|
+
continue;
|
|
2629
|
+
}
|
|
2630
|
+
diagnostics.push({
|
|
2631
|
+
code: "AI_LOGICAL_PARENT_REMAPPED",
|
|
2632
|
+
message: `Remapped parent ${originalParentId} \u2192 ${nextParent} for ${event.eventId}.`,
|
|
2633
|
+
eventIds: [event.eventId]
|
|
2487
2634
|
});
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
path: path16,
|
|
2497
|
-
left: le || void 0,
|
|
2498
|
-
right: re || void 0
|
|
2635
|
+
normalized.push({
|
|
2636
|
+
...event,
|
|
2637
|
+
parentId: nextParent,
|
|
2638
|
+
projection: {
|
|
2639
|
+
...event.projection,
|
|
2640
|
+
parentNormalized: true,
|
|
2641
|
+
originalParentId
|
|
2642
|
+
}
|
|
2499
2643
|
});
|
|
2500
2644
|
}
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
const
|
|
2504
|
-
const
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2645
|
+
const rawIndex = new Map(events.map((e, i) => [e.eventId, i]));
|
|
2646
|
+
normalized.sort((a, b) => {
|
|
2647
|
+
const ai = rawIndex.get(a.sourceEventIds[0]) ?? 0;
|
|
2648
|
+
const bi = rawIndex.get(b.sourceEventIds[0]) ?? 0;
|
|
2649
|
+
return ai - bi || a.eventId.localeCompare(b.eventId);
|
|
2650
|
+
});
|
|
2651
|
+
return {
|
|
2652
|
+
logicalEvents: Object.freeze(normalized),
|
|
2653
|
+
diagnostics: Object.freeze(diagnostics)
|
|
2654
|
+
};
|
|
2655
|
+
}
|
|
2656
|
+
function resolveCanonicalToolName(event) {
|
|
2657
|
+
const attrs2 = event.attributes;
|
|
2658
|
+
const direct = pickString(attrs2, ["toolName", "tool"]);
|
|
2659
|
+
if (direct) return direct;
|
|
2660
|
+
const metadata = attrs2?.metadata;
|
|
2661
|
+
if (isRecord6(metadata)) {
|
|
2662
|
+
const nested = pickString(metadata, ["toolName", "tool"]);
|
|
2663
|
+
if (nested) return nested;
|
|
2519
2664
|
}
|
|
2520
|
-
const
|
|
2521
|
-
|
|
2522
|
-
if (lm !== rm) {
|
|
2523
|
-
out.push({
|
|
2524
|
-
kind: "metadata",
|
|
2525
|
-
severity: "info",
|
|
2526
|
-
message: "Step metadata differs",
|
|
2527
|
-
path: path16,
|
|
2528
|
-
left: L.metadata,
|
|
2529
|
-
right: R.metadata
|
|
2530
|
-
});
|
|
2665
|
+
for (const prefix of ["tool:", "function:", "mcp-tools:"]) {
|
|
2666
|
+
if (event.name.startsWith(prefix)) return event.name.slice(prefix.length);
|
|
2531
2667
|
}
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
path: path16,
|
|
2540
|
-
left: L.outputPreview,
|
|
2541
|
-
right: R.outputPreview
|
|
2542
|
-
});
|
|
2668
|
+
return event.name;
|
|
2669
|
+
}
|
|
2670
|
+
function pickString(record, keys) {
|
|
2671
|
+
if (!record) return void 0;
|
|
2672
|
+
for (const key of keys) {
|
|
2673
|
+
const value = record[key];
|
|
2674
|
+
if (typeof value === "string" && value.trim() !== "") return value.trim();
|
|
2543
2675
|
}
|
|
2676
|
+
return void 0;
|
|
2544
2677
|
}
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
severity: "warning",
|
|
2565
|
-
message: `Step only in right run: ${rch.name}`,
|
|
2566
|
-
path: buildPath([...segments, pathSeg(rch, ci)]),
|
|
2567
|
-
left: void 0,
|
|
2568
|
-
right: rch.id
|
|
2569
|
-
});
|
|
2678
|
+
|
|
2679
|
+
// packages/core/src/checks/derived-failure.ts
|
|
2680
|
+
function isRecord7(value) {
|
|
2681
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2682
|
+
}
|
|
2683
|
+
function pickString2(record, keys) {
|
|
2684
|
+
if (!record) return void 0;
|
|
2685
|
+
for (const key of keys) {
|
|
2686
|
+
const value = record[key];
|
|
2687
|
+
if (typeof value === "string" && value.trim() !== "") return value;
|
|
2688
|
+
}
|
|
2689
|
+
return void 0;
|
|
2690
|
+
}
|
|
2691
|
+
function pickNumber(record, keys) {
|
|
2692
|
+
if (!record) return void 0;
|
|
2693
|
+
for (const key of keys) {
|
|
2694
|
+
const value = record[key];
|
|
2695
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
2696
|
+
return value;
|
|
2570
2697
|
}
|
|
2571
|
-
ci += 1;
|
|
2572
2698
|
}
|
|
2699
|
+
return void 0;
|
|
2573
2700
|
}
|
|
2574
|
-
function
|
|
2701
|
+
function eventMetadata(event) {
|
|
2702
|
+
const attrs2 = isRecord7(event.attributes) ? event.attributes : void 0;
|
|
2703
|
+
const nested = attrs2 !== void 0 && isRecord7(attrs2.metadata) ? attrs2.metadata : void 0;
|
|
2575
2704
|
return {
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
focus: "all",
|
|
2579
|
-
check: "all"
|
|
2705
|
+
...attrs2 ?? {},
|
|
2706
|
+
...nested ?? {}
|
|
2580
2707
|
};
|
|
2581
2708
|
}
|
|
2582
|
-
function
|
|
2583
|
-
return
|
|
2709
|
+
function canonicalName(event) {
|
|
2710
|
+
if (event.kind === "TOOL") return resolveCanonicalToolName(event);
|
|
2711
|
+
return event.name;
|
|
2584
2712
|
}
|
|
2585
|
-
function
|
|
2586
|
-
const
|
|
2587
|
-
const
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
const raw = [];
|
|
2592
|
-
if ((left.status ?? "") !== (right.status ?? "")) {
|
|
2593
|
-
raw.push({
|
|
2594
|
-
kind: "run-status",
|
|
2595
|
-
severity: "warning",
|
|
2596
|
-
message: "Run completion status differs",
|
|
2597
|
-
left: left.status,
|
|
2598
|
-
right: right.status
|
|
2599
|
-
});
|
|
2713
|
+
function linkKeys(event) {
|
|
2714
|
+
const meta = eventMetadata(event);
|
|
2715
|
+
const keys = [];
|
|
2716
|
+
for (const key of ["linkedStepId", "toolCallId", "mcpToolCallId"]) {
|
|
2717
|
+
const value = pickString2(meta, [key]);
|
|
2718
|
+
if (value !== void 0) keys.push(`${key}:${value}`);
|
|
2600
2719
|
}
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
right: rd
|
|
2616
|
-
});
|
|
2720
|
+
const stepId = pickString2(meta, ["stepId"]);
|
|
2721
|
+
if (stepId !== void 0) keys.push(`stepId:${stepId}`);
|
|
2722
|
+
return keys;
|
|
2723
|
+
}
|
|
2724
|
+
function buildRunContexts(logicalEvents) {
|
|
2725
|
+
const byRun = /* @__PURE__ */ new Map();
|
|
2726
|
+
for (const event of logicalEvents) {
|
|
2727
|
+
const existing = byRun.get(event.runId) ?? {
|
|
2728
|
+
runId: event.runId,
|
|
2729
|
+
name: event.name
|
|
2730
|
+
};
|
|
2731
|
+
const meta = eventMetadata(event);
|
|
2732
|
+
if (event.kind === "RUN" || existing.name === event.runId) {
|
|
2733
|
+
existing.name = event.name || existing.name;
|
|
2617
2734
|
}
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
let idx = 0;
|
|
2621
|
-
for (const [ls, rs] of pairs) {
|
|
2622
|
-
if (ls !== void 0 && rs !== void 0) {
|
|
2623
|
-
compareRecursive(ls, rs, [pathSeg(ls, idx)], opts, raw);
|
|
2624
|
-
idx += 1;
|
|
2625
|
-
} else if (ls !== void 0) {
|
|
2626
|
-
raw.push({
|
|
2627
|
-
kind: "step-removed",
|
|
2628
|
-
severity: "warning",
|
|
2629
|
-
message: `Step only in left run: ${ls.name}`,
|
|
2630
|
-
path: buildPath([pathSeg(ls, idx)]),
|
|
2631
|
-
left: ls.id,
|
|
2632
|
-
right: void 0
|
|
2633
|
-
});
|
|
2634
|
-
idx += 1;
|
|
2635
|
-
} else if (rs !== void 0) {
|
|
2636
|
-
raw.push({
|
|
2637
|
-
kind: "step-added",
|
|
2638
|
-
severity: "warning",
|
|
2639
|
-
message: `Step only in right run: ${rs.name}`,
|
|
2640
|
-
path: buildPath([pathSeg(rs, idx)]),
|
|
2641
|
-
left: void 0,
|
|
2642
|
-
right: rs.id
|
|
2643
|
-
});
|
|
2644
|
-
idx += 1;
|
|
2735
|
+
if (event.kind === "RUN" && event.status !== void 0 && event.status !== "running") {
|
|
2736
|
+
existing.status = event.status;
|
|
2645
2737
|
}
|
|
2738
|
+
existing.retryOf ??= pickString2(meta, ["retryOf"]);
|
|
2739
|
+
existing.attempt ??= pickNumber(meta, ["attempt", "retryAttempt", "retryCount"]);
|
|
2740
|
+
existing.sessionId ??= pickString2(meta, ["sessionId", "conversationId"]);
|
|
2741
|
+
existing.groupId ??= pickString2(meta, ["groupId"]);
|
|
2742
|
+
existing.parentGroupId ??= pickString2(meta, ["parentGroupId"]);
|
|
2743
|
+
existing.fallbackOf ??= pickString2(meta, ["fallbackOf", "fallbackFrom"]);
|
|
2744
|
+
byRun.set(event.runId, existing);
|
|
2646
2745
|
}
|
|
2647
|
-
|
|
2648
|
-
let errors = 0;
|
|
2649
|
-
let warnings = 0;
|
|
2650
|
-
let info = 0;
|
|
2651
|
-
for (const d of differences) {
|
|
2652
|
-
if (d.severity === "error") errors += 1;
|
|
2653
|
-
else if (d.severity === "warning") warnings += 1;
|
|
2654
|
-
else info += 1;
|
|
2655
|
-
}
|
|
2656
|
-
const firstVisible = differences[0];
|
|
2657
|
-
const firstDivergence = firstVisible !== void 0 ? {
|
|
2658
|
-
kind: "first-divergence",
|
|
2659
|
-
severity: firstVisible.severity,
|
|
2660
|
-
message: `First divergence: ${firstVisible.message}`,
|
|
2661
|
-
path: firstVisible.path,
|
|
2662
|
-
left: firstVisible.left,
|
|
2663
|
-
right: firstVisible.right
|
|
2664
|
-
} : void 0;
|
|
2665
|
-
const summary = {
|
|
2666
|
-
leftRunId: left.runId,
|
|
2667
|
-
rightRunId: right.runId,
|
|
2668
|
-
totalDifferences: differences.length,
|
|
2669
|
-
errors,
|
|
2670
|
-
warnings,
|
|
2671
|
-
info,
|
|
2672
|
-
firstDivergence
|
|
2673
|
-
};
|
|
2674
|
-
return { summary, differences };
|
|
2746
|
+
return byRun;
|
|
2675
2747
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
(
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
let c = n;
|
|
2682
|
-
for (let k = 0; k < 8; k += 1) {
|
|
2683
|
-
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
2684
|
-
}
|
|
2685
|
-
table[n] = c >>> 0;
|
|
2686
|
-
}
|
|
2687
|
-
return table;
|
|
2688
|
-
})();
|
|
2689
|
-
|
|
2690
|
-
// packages/core/src/diagnostics/programmatic.ts
|
|
2691
|
-
var PROGRAMMATIC_DIAGNOSTIC_SPECS = Object.freeze({
|
|
2692
|
-
AI_TRACE_INPUT_INVALID: {
|
|
2693
|
-
code: "AI_TRACE_INPUT_INVALID",
|
|
2694
|
-
summary: 'Expected { type: "file", path }, { type: "directory", path }, { type: "string", content }, { type: "buffer", content }, or { type: "stdin" }.',
|
|
2695
|
-
remediation: "For a file path, use openTraceFile(path).",
|
|
2696
|
-
relatedCodes: ["invalid_input"]
|
|
2697
|
-
},
|
|
2698
|
-
AI_TRACE_FORMAT_UNSUPPORTED: {
|
|
2699
|
-
code: "AI_TRACE_FORMAT_UNSUPPORTED",
|
|
2700
|
-
summary: "No trace reader could detect the input format.",
|
|
2701
|
-
remediation: "Pass an AgentInspect JSONL file via openTraceFile, or set options.format to a registered reader.",
|
|
2702
|
-
relatedCodes: ["unsupported_format"]
|
|
2703
|
-
},
|
|
2704
|
-
AI_TRACE_FORMAT_AMBIGUOUS: {
|
|
2705
|
-
code: "AI_TRACE_FORMAT_AMBIGUOUS",
|
|
2706
|
-
summary: "Multiple trace readers matched the input with equal confidence.",
|
|
2707
|
-
remediation: "Set options.format explicitly to disambiguate the reader.",
|
|
2708
|
-
relatedCodes: ["ambiguous_format"]
|
|
2709
|
-
},
|
|
2710
|
-
AI_TRACE_FACTS_INPUT_NOT_NORMALIZED: {
|
|
2711
|
-
code: "AI_TRACE_FACTS_INPUT_NOT_NORMALIZED",
|
|
2712
|
-
summary: "TraceFacts requires TraceReadResult or PersistedInspectEvent[].",
|
|
2713
|
-
remediation: "Use openTraceFile() to normalize a JSONL trace first."
|
|
2714
|
-
},
|
|
2715
|
-
AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED: {
|
|
2716
|
-
code: "AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED",
|
|
2717
|
-
summary: "Multiple runs are available; select a run before executing checks.",
|
|
2718
|
-
remediation: "Pass options.runId or TraceCheckInput.selectedRun.",
|
|
2719
|
-
relatedCodes: ["AI_CHECK_RUN_SELECTION_REQUIRED"]
|
|
2720
|
-
},
|
|
2721
|
-
AI_TRACE_RELATIONSHIP_SELF_PARENT: {
|
|
2722
|
-
code: "AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
2723
|
-
summary: "A parentId equals its own event/step id.",
|
|
2724
|
-
remediation: "Reject at capture (AI_LANGGRAPH_SELF_PARENT_REJECTED) or drop via logical projection; do not invent replacement parents.",
|
|
2725
|
-
relatedCodes: [
|
|
2726
|
-
"AI_LANGGRAPH_SELF_PARENT_REJECTED",
|
|
2727
|
-
"AI_LOGICAL_SELF_PARENT_REMOVED"
|
|
2728
|
-
]
|
|
2729
|
-
},
|
|
2730
|
-
AI_TRACE_RELATIONSHIP_CYCLE: {
|
|
2731
|
-
code: "AI_TRACE_RELATIONSHIP_CYCLE",
|
|
2732
|
-
summary: "Trace contains a parentId cycle.",
|
|
2733
|
-
remediation: "Use visibility-first tree linking for legacy fixtures; prefer acyclic capture for new adapter output.",
|
|
2734
|
-
relatedCodes: ["structure.cycle"]
|
|
2748
|
+
function sameCorrelationScope(a, b) {
|
|
2749
|
+
if (a.sessionId && b.sessionId && a.sessionId === b.sessionId) return true;
|
|
2750
|
+
if (a.groupId && b.groupId && a.groupId === b.groupId) return true;
|
|
2751
|
+
if (a.parentGroupId && b.parentGroupId && a.parentGroupId === b.parentGroupId) {
|
|
2752
|
+
return true;
|
|
2735
2753
|
}
|
|
2736
|
-
|
|
2737
|
-
function formatProgrammaticDiagnostic(code, detail) {
|
|
2738
|
-
const spec = PROGRAMMATIC_DIAGNOSTIC_SPECS[code];
|
|
2739
|
-
const summary = detail?.trim() ? detail.trim() : spec.summary;
|
|
2740
|
-
return `${code}: ${summary} Remediation: ${spec.remediation}`;
|
|
2754
|
+
return false;
|
|
2741
2755
|
}
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
function keyHasExplicitSeparator(value) {
|
|
2745
|
-
return /[_\-.]/.test(value);
|
|
2756
|
+
function isSuccessful(event) {
|
|
2757
|
+
return event.status === "ok";
|
|
2746
2758
|
}
|
|
2747
|
-
function
|
|
2748
|
-
return
|
|
2759
|
+
function isFailure(event) {
|
|
2760
|
+
return event.status === "error";
|
|
2749
2761
|
}
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
"min_tokens",
|
|
2755
|
-
"ls_max_tokens",
|
|
2756
|
-
"token_count",
|
|
2757
|
-
"token_limit",
|
|
2758
|
-
"token_budget",
|
|
2759
|
-
"input_tokens",
|
|
2760
|
-
"output_tokens",
|
|
2761
|
-
"total_tokens",
|
|
2762
|
-
"cached_tokens",
|
|
2763
|
-
"prompt_tokens",
|
|
2764
|
-
"completion_tokens"
|
|
2765
|
-
].map(normalizeSensitiveKey)
|
|
2766
|
-
);
|
|
2767
|
-
var DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
|
|
2768
|
-
"authorization",
|
|
2769
|
-
"cookie",
|
|
2770
|
-
"token",
|
|
2771
|
-
"access_token",
|
|
2772
|
-
"accesstoken",
|
|
2773
|
-
"auth_token",
|
|
2774
|
-
"authtoken",
|
|
2775
|
-
"refresh_token",
|
|
2776
|
-
"refreshtoken",
|
|
2777
|
-
"id_token",
|
|
2778
|
-
"idtoken",
|
|
2779
|
-
"bearer_token",
|
|
2780
|
-
"bearertoken",
|
|
2781
|
-
"api_token",
|
|
2782
|
-
"apitoken",
|
|
2783
|
-
"apikey",
|
|
2784
|
-
"api_key",
|
|
2785
|
-
"password",
|
|
2786
|
-
"secret",
|
|
2787
|
-
"email"
|
|
2788
|
-
];
|
|
2789
|
-
function isTokenCredentialKey(normalized) {
|
|
2790
|
-
if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
|
|
2791
|
-
if (normalized === "token") return true;
|
|
2792
|
-
if (normalized.endsWith("tokens")) return false;
|
|
2793
|
-
return normalized.endsWith("token");
|
|
2762
|
+
function compareEventOrder(a, b) {
|
|
2763
|
+
const byTime = a.timestamp.localeCompare(b.timestamp);
|
|
2764
|
+
if (byTime !== 0) return byTime;
|
|
2765
|
+
return a.eventId.localeCompare(b.eventId);
|
|
2794
2766
|
}
|
|
2795
|
-
function
|
|
2796
|
-
|
|
2797
|
-
const
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
const
|
|
2801
|
-
for (const
|
|
2802
|
-
|
|
2803
|
-
if (
|
|
2804
|
-
|
|
2805
|
-
|
|
2767
|
+
function collectCandidates(failure, logicalEvents, runs) {
|
|
2768
|
+
const failureRun = runs.get(failure.runId);
|
|
2769
|
+
const failureLinks = new Set(linkKeys(failure));
|
|
2770
|
+
const failureName = canonicalName(failure);
|
|
2771
|
+
const failureAttempt = pickNumber(eventMetadata(failure), ["attempt", "retryAttempt", "retryCount"]) ?? failureRun?.attempt;
|
|
2772
|
+
const candidates = [];
|
|
2773
|
+
for (const event of logicalEvents) {
|
|
2774
|
+
if (event.eventId === failure.eventId) continue;
|
|
2775
|
+
if (event.status === "running") continue;
|
|
2776
|
+
const eventRun = runs.get(event.runId);
|
|
2777
|
+
const eventMeta = eventMetadata(event);
|
|
2778
|
+
const sameRun = event.runId === failure.runId;
|
|
2779
|
+
if (eventRun?.retryOf === failure.runId) {
|
|
2780
|
+
candidates.push({
|
|
2781
|
+
event,
|
|
2782
|
+
basis: "retryOf",
|
|
2783
|
+
confidence: "explicit",
|
|
2784
|
+
viaRunId: event.runId
|
|
2785
|
+
});
|
|
2806
2786
|
continue;
|
|
2807
2787
|
}
|
|
2808
|
-
if (
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2788
|
+
if (eventRun?.fallbackOf === failure.runId || pickString2(eventMeta, ["fallbackOf", "fallbackFrom"]) === failure.runId) {
|
|
2789
|
+
candidates.push({
|
|
2790
|
+
event,
|
|
2791
|
+
basis: "fallbackOf",
|
|
2792
|
+
confidence: "explicit",
|
|
2793
|
+
viaRunId: event.runId
|
|
2794
|
+
});
|
|
2795
|
+
continue;
|
|
2796
|
+
}
|
|
2797
|
+
if (sameRun && compareEventOrder(failure, event) >= 0) continue;
|
|
2798
|
+
const eventLinks = linkKeys(event);
|
|
2799
|
+
const sharedLink = eventLinks.find((key) => failureLinks.has(key));
|
|
2800
|
+
if (sharedLink !== void 0) {
|
|
2801
|
+
candidates.push({
|
|
2802
|
+
event,
|
|
2803
|
+
basis: sharedLink.split(":")[0] ?? "linkedId",
|
|
2804
|
+
confidence: "explicit"
|
|
2805
|
+
});
|
|
2806
|
+
continue;
|
|
2807
|
+
}
|
|
2808
|
+
const eventAttempt = pickNumber(eventMeta, ["attempt", "retryAttempt", "retryCount"]) ?? eventRun?.attempt;
|
|
2809
|
+
const sameParent = failure.parentId !== void 0 && event.parentId !== void 0 && failure.parentId === event.parentId;
|
|
2810
|
+
const differentParent = failure.parentId !== void 0 && event.parentId !== void 0 && failure.parentId !== event.parentId;
|
|
2811
|
+
const sessionScoped = failureRun !== void 0 && eventRun !== void 0 && sameCorrelationScope(failureRun, eventRun);
|
|
2812
|
+
if (canonicalName(event) === failureName && failureAttempt !== void 0 && eventAttempt !== void 0 && eventAttempt > failureAttempt && !differentParent && (sameParent || sessionScoped || sameRun)) {
|
|
2813
|
+
candidates.push({
|
|
2814
|
+
event,
|
|
2815
|
+
basis: "attempt-progression",
|
|
2816
|
+
confidence: "correlated",
|
|
2817
|
+
...event.runId !== failure.runId ? { viaRunId: event.runId } : {}
|
|
2818
|
+
});
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
const byId = /* @__PURE__ */ new Map();
|
|
2822
|
+
for (const candidate of candidates) {
|
|
2823
|
+
const prev = byId.get(candidate.event.eventId);
|
|
2824
|
+
if (!prev || prev.confidence !== "explicit" && candidate.confidence === "explicit") {
|
|
2825
|
+
byId.set(candidate.event.eventId, candidate);
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
return [...byId.values()].sort((a, b) => compareEventOrder(a.event, b.event));
|
|
2827
2829
|
}
|
|
2828
|
-
function
|
|
2830
|
+
function classifyFailure(failure, candidates, runs, logicalEvents) {
|
|
2831
|
+
const successful = candidates.filter((c) => isSuccessful(c.event));
|
|
2832
|
+
const unsuccessful = candidates.filter((c) => !isSuccessful(c.event));
|
|
2833
|
+
const retryRunIds = Object.freeze(
|
|
2834
|
+
[...new Set(candidates.map((c) => c.viaRunId).filter((id) => id !== void 0))].sort(
|
|
2835
|
+
(a, b) => a.localeCompare(b)
|
|
2836
|
+
)
|
|
2837
|
+
);
|
|
2838
|
+
if (successful.length > 1) {
|
|
2839
|
+
const distinctRuns = new Set(successful.map((c) => c.event.runId));
|
|
2840
|
+
const distinctParents = new Set(
|
|
2841
|
+
successful.map((c) => c.event.parentId ?? "").filter((id) => id !== "")
|
|
2842
|
+
);
|
|
2843
|
+
if (distinctRuns.size > 1 || distinctParents.size > 1) {
|
|
2844
|
+
return {
|
|
2845
|
+
eventId: failure.eventId,
|
|
2846
|
+
runId: failure.runId,
|
|
2847
|
+
name: failure.name,
|
|
2848
|
+
kind: failure.kind,
|
|
2849
|
+
role: "unknown",
|
|
2850
|
+
confidence: "unknown",
|
|
2851
|
+
basis: Object.freeze(["ambiguous-recovery-candidates"]),
|
|
2852
|
+
recoveryEventIds: Object.freeze(
|
|
2853
|
+
successful.map((c) => c.event.eventId).sort((a, b) => a.localeCompare(b))
|
|
2854
|
+
),
|
|
2855
|
+
retryRunIds
|
|
2856
|
+
};
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
if (successful.length >= 1) {
|
|
2860
|
+
const best = successful[0];
|
|
2861
|
+
return {
|
|
2862
|
+
eventId: failure.eventId,
|
|
2863
|
+
runId: failure.runId,
|
|
2864
|
+
name: failure.name,
|
|
2865
|
+
kind: failure.kind,
|
|
2866
|
+
role: "recovered",
|
|
2867
|
+
confidence: best.confidence,
|
|
2868
|
+
basis: Object.freeze([best.basis]),
|
|
2869
|
+
recoveryEventIds: Object.freeze(
|
|
2870
|
+
successful.map((c) => c.event.eventId).sort((a, b) => a.localeCompare(b))
|
|
2871
|
+
),
|
|
2872
|
+
retryRunIds
|
|
2873
|
+
};
|
|
2874
|
+
}
|
|
2875
|
+
if (candidates.length > 0) {
|
|
2876
|
+
const best = candidates[0];
|
|
2877
|
+
return {
|
|
2878
|
+
eventId: failure.eventId,
|
|
2879
|
+
runId: failure.runId,
|
|
2880
|
+
name: failure.name,
|
|
2881
|
+
kind: failure.kind,
|
|
2882
|
+
role: "transient",
|
|
2883
|
+
confidence: best.confidence,
|
|
2884
|
+
basis: Object.freeze([
|
|
2885
|
+
best.basis,
|
|
2886
|
+
unsuccessful.some((c) => c.event.status === void 0) ? "retry-incomplete" : "retry-without-success"
|
|
2887
|
+
]),
|
|
2888
|
+
recoveryEventIds: Object.freeze([]),
|
|
2889
|
+
retryRunIds
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
const failureMeta = eventMetadata(failure);
|
|
2893
|
+
const declaredSuccessor = pickString2(failureMeta, [
|
|
2894
|
+
"retriedBy",
|
|
2895
|
+
"nextRetryRunId",
|
|
2896
|
+
"retryRunId"
|
|
2897
|
+
]);
|
|
2898
|
+
if (declaredSuccessor !== void 0 && !runs.has(declaredSuccessor)) {
|
|
2899
|
+
return {
|
|
2900
|
+
eventId: failure.eventId,
|
|
2901
|
+
runId: failure.runId,
|
|
2902
|
+
name: failure.name,
|
|
2903
|
+
kind: failure.kind,
|
|
2904
|
+
role: "transient",
|
|
2905
|
+
confidence: "explicit",
|
|
2906
|
+
basis: Object.freeze(["retry-declared", "retry-run-missing"]),
|
|
2907
|
+
recoveryEventIds: Object.freeze([]),
|
|
2908
|
+
retryRunIds: Object.freeze([declaredSuccessor])
|
|
2909
|
+
};
|
|
2910
|
+
}
|
|
2911
|
+
for (const run of runs.values()) {
|
|
2912
|
+
if (run.retryOf === failure.runId) {
|
|
2913
|
+
return {
|
|
2914
|
+
eventId: failure.eventId,
|
|
2915
|
+
runId: failure.runId,
|
|
2916
|
+
name: failure.name,
|
|
2917
|
+
kind: failure.kind,
|
|
2918
|
+
role: "transient",
|
|
2919
|
+
confidence: "explicit",
|
|
2920
|
+
basis: Object.freeze(["retryOf", "retry-run-missing-or-empty"]),
|
|
2921
|
+
recoveryEventIds: Object.freeze([]),
|
|
2922
|
+
retryRunIds: Object.freeze([run.runId])
|
|
2923
|
+
};
|
|
2924
|
+
}
|
|
2925
|
+
}
|
|
2926
|
+
const failureRun = runs.get(failure.runId);
|
|
2927
|
+
const hasSuccessorDeclared = [...runs.values()].some((run) => run.retryOf === failure.runId);
|
|
2928
|
+
const isFinalInChain = failureRun !== void 0 && !hasSuccessorDeclared && (failureRun.retryOf !== void 0 || failureRun.attempt !== void 0 && failureRun.attempt > 1 || pickNumber(eventMetadata(failure), ["attempt"]) !== void 0);
|
|
2929
|
+
if (isFinalInChain && failureRun?.status === "error" && !logicalEvents.some(
|
|
2930
|
+
(event) => event.runId === failure.runId && event.eventId !== failure.eventId && isSuccessful(event) && canonicalName(event) === canonicalName(failure)
|
|
2931
|
+
)) {
|
|
2932
|
+
return {
|
|
2933
|
+
eventId: failure.eventId,
|
|
2934
|
+
runId: failure.runId,
|
|
2935
|
+
name: failure.name,
|
|
2936
|
+
kind: failure.kind,
|
|
2937
|
+
role: "terminal",
|
|
2938
|
+
confidence: failureRun.retryOf !== void 0 ? "explicit" : "correlated",
|
|
2939
|
+
basis: Object.freeze(["final-retry-chain-member", "enclosing-run-error"]),
|
|
2940
|
+
recoveryEventIds: Object.freeze([]),
|
|
2941
|
+
retryRunIds: Object.freeze(
|
|
2942
|
+
failureRun.retryOf !== void 0 ? [failureRun.retryOf] : []
|
|
2943
|
+
)
|
|
2944
|
+
};
|
|
2945
|
+
}
|
|
2829
2946
|
return {
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2947
|
+
eventId: failure.eventId,
|
|
2948
|
+
runId: failure.runId,
|
|
2949
|
+
name: failure.name,
|
|
2950
|
+
kind: failure.kind,
|
|
2951
|
+
role: "unknown",
|
|
2952
|
+
confidence: "unknown",
|
|
2953
|
+
basis: Object.freeze(["no-explicit-or-correlated-recovery"]),
|
|
2954
|
+
recoveryEventIds: Object.freeze([]),
|
|
2955
|
+
retryRunIds: Object.freeze([])
|
|
2835
2956
|
};
|
|
2836
2957
|
}
|
|
2837
|
-
function
|
|
2838
|
-
const
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2958
|
+
function deriveFailureFacts(logicalEvents) {
|
|
2959
|
+
const runs = buildRunContexts(logicalEvents);
|
|
2960
|
+
const failures = logicalEvents.filter((event) => isFailure(event)).sort(compareEventOrder);
|
|
2961
|
+
const failureFacts = failures.map(
|
|
2962
|
+
(failure) => classifyFailure(failure, collectCandidates(failure, logicalEvents, runs), runs, logicalEvents)
|
|
2963
|
+
);
|
|
2964
|
+
const byRole = /* @__PURE__ */ new Map([
|
|
2965
|
+
["transient", []],
|
|
2966
|
+
["recovered", []],
|
|
2967
|
+
["terminal", []],
|
|
2968
|
+
["unknown", []]
|
|
2969
|
+
]);
|
|
2970
|
+
for (const fact of failureFacts) {
|
|
2971
|
+
byRole.get(fact.role).push(fact);
|
|
2846
2972
|
}
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2973
|
+
for (const [role, list] of byRole) {
|
|
2974
|
+
byRole.set(
|
|
2975
|
+
role,
|
|
2976
|
+
Object.freeze(
|
|
2977
|
+
[...list].sort((a, b) => {
|
|
2978
|
+
const byRun = a.runId.localeCompare(b.runId);
|
|
2979
|
+
if (byRun !== 0) return byRun;
|
|
2980
|
+
return a.eventId.localeCompare(b.eventId);
|
|
2981
|
+
})
|
|
2982
|
+
)
|
|
2983
|
+
);
|
|
2984
|
+
}
|
|
2985
|
+
const failureRoleCounts = {
|
|
2986
|
+
transient: byRole.get("transient").length,
|
|
2987
|
+
recovered: byRole.get("recovered").length,
|
|
2988
|
+
terminal: byRole.get("terminal").length,
|
|
2989
|
+
unknown: byRole.get("unknown").length
|
|
2860
2990
|
};
|
|
2861
2991
|
return {
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
paired: true,
|
|
2866
|
-
absorbedEventIds: Object.freeze([complete.eventId]),
|
|
2867
|
-
parentNormalized: false,
|
|
2868
|
-
...start.parentId !== void 0 ? { originalParentId: start.parentId } : {}
|
|
2869
|
-
}
|
|
2992
|
+
failureFacts: Object.freeze(failureFacts),
|
|
2993
|
+
failuresByRole: byRole,
|
|
2994
|
+
failureRoleCounts
|
|
2870
2995
|
};
|
|
2871
2996
|
}
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
projection: {
|
|
2877
|
-
paired: false,
|
|
2878
|
-
absorbedEventIds: Object.freeze([]),
|
|
2879
|
-
parentNormalized: extras?.parentNormalized === true,
|
|
2880
|
-
...{}
|
|
2881
|
-
}
|
|
2882
|
-
};
|
|
2997
|
+
|
|
2998
|
+
// packages/core/src/checks/relationship-facts.ts
|
|
2999
|
+
function attrs(event) {
|
|
3000
|
+
return event.attributes && typeof event.attributes === "object" ? event.attributes : {};
|
|
2883
3001
|
}
|
|
2884
|
-
function
|
|
3002
|
+
function stringAttr(record, key) {
|
|
3003
|
+
const value = record[key];
|
|
3004
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
3005
|
+
}
|
|
3006
|
+
function deriveRelationshipFacts(events) {
|
|
3007
|
+
const relationships = [];
|
|
2885
3008
|
const diagnostics = [];
|
|
2886
|
-
const
|
|
3009
|
+
const byId = new Map(events.map((event) => [event.eventId, event]));
|
|
3010
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3011
|
+
const push = (edge) => {
|
|
3012
|
+
const key = `${edge.type}:${edge.fromEventId}:${edge.toEventId ?? ""}:${edge.externalRef ?? ""}`;
|
|
3013
|
+
if (seen.has(key)) return;
|
|
3014
|
+
seen.add(key);
|
|
3015
|
+
relationships.push(edge);
|
|
3016
|
+
};
|
|
2887
3017
|
for (const event of events) {
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
for (const runId of orderedRuns) {
|
|
2898
|
-
const runEvents = byRun.get(runId) ?? [];
|
|
2899
|
-
const starts = [];
|
|
2900
|
-
const completes = [];
|
|
2901
|
-
const others = [];
|
|
2902
|
-
for (const event of runEvents) {
|
|
2903
|
-
const legacy = legacyEvent(event);
|
|
2904
|
-
if (legacy === "step_started" || legacy === "run_started" && event.status === "running") {
|
|
2905
|
-
starts.push(event);
|
|
2906
|
-
} else if (legacy === "step_completed" || legacy === "run_completed") {
|
|
2907
|
-
completes.push(event);
|
|
2908
|
-
} else {
|
|
2909
|
-
others.push(event);
|
|
2910
|
-
}
|
|
2911
|
-
}
|
|
2912
|
-
const usedCompletes = /* @__PURE__ */ new Set();
|
|
2913
|
-
for (const start of starts) {
|
|
2914
|
-
const stepId = stepIdOf(start);
|
|
2915
|
-
let match;
|
|
2916
|
-
if (legacyEvent(start) === "run_started") {
|
|
2917
|
-
const candidates = completes.filter(
|
|
2918
|
-
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "run_completed"
|
|
2919
|
-
);
|
|
2920
|
-
if (candidates.length > 1) {
|
|
2921
|
-
diagnostics.push({
|
|
2922
|
-
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
2923
|
-
message: `Multiple run_completed rows for run ${runId}; using first by eventId.`,
|
|
2924
|
-
eventIds: candidates.map((c) => c.eventId)
|
|
2925
|
-
});
|
|
2926
|
-
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
2927
|
-
}
|
|
2928
|
-
match = candidates[0];
|
|
2929
|
-
} else if (stepId) {
|
|
2930
|
-
const candidates = completes.filter(
|
|
2931
|
-
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "step_completed" && stepIdOf(c) === stepId
|
|
2932
|
-
);
|
|
2933
|
-
if (candidates.length > 1) {
|
|
2934
|
-
diagnostics.push({
|
|
2935
|
-
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
2936
|
-
message: `Multiple step_completed rows for stepId ${stepId}; using first by eventId.`,
|
|
2937
|
-
eventIds: candidates.map((c) => c.eventId)
|
|
2938
|
-
});
|
|
2939
|
-
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
2940
|
-
}
|
|
2941
|
-
match = candidates[0];
|
|
2942
|
-
}
|
|
2943
|
-
if (match) {
|
|
2944
|
-
usedCompletes.add(match.eventId);
|
|
2945
|
-
absorbedIds.add(match.eventId);
|
|
2946
|
-
const paired = pairStartComplete(start, match);
|
|
2947
|
-
logical.push(paired);
|
|
2948
|
-
logicalByRawId.set(start.eventId, paired);
|
|
2949
|
-
logicalByRawId.set(match.eventId, paired);
|
|
2950
|
-
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, paired.eventId);
|
|
2951
|
-
} else {
|
|
3018
|
+
if (event.parentId) {
|
|
3019
|
+
push({
|
|
3020
|
+
type: "parent-child",
|
|
3021
|
+
fromEventId: event.parentId,
|
|
3022
|
+
toEventId: event.eventId,
|
|
3023
|
+
confidence: byId.has(event.parentId) ? "explicit" : "unknown",
|
|
3024
|
+
basis: ["persisted.parentId"]
|
|
3025
|
+
});
|
|
3026
|
+
if (!byId.has(event.parentId)) {
|
|
2952
3027
|
diagnostics.push({
|
|
2953
|
-
code: "
|
|
2954
|
-
message: `
|
|
2955
|
-
|
|
3028
|
+
code: "AI_RELATIONSHIP_PARENT_MISSING",
|
|
3029
|
+
message: `parentId ${event.parentId} is not present in the event set.`,
|
|
3030
|
+
eventId: event.eventId
|
|
2956
3031
|
});
|
|
2957
|
-
const alone = asLogical(start);
|
|
2958
|
-
logical.push(alone);
|
|
2959
|
-
logicalByRawId.set(start.eventId, alone);
|
|
2960
|
-
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2961
3032
|
}
|
|
2962
3033
|
}
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
3034
|
+
const bag = attrs(event);
|
|
3035
|
+
const meta = extractSessionWorkflowMetadata(bag) ?? {};
|
|
3036
|
+
const nested = bag.metadata && typeof bag.metadata === "object" ? extractSessionWorkflowMetadata(bag.metadata) : void 0;
|
|
3037
|
+
const workflow = { ...meta, ...nested };
|
|
3038
|
+
if (workflow.retryOf) {
|
|
3039
|
+
const target = events.find((candidate) => candidate.runId === workflow.retryOf);
|
|
3040
|
+
push({
|
|
3041
|
+
type: "retry-of",
|
|
3042
|
+
fromEventId: event.eventId,
|
|
3043
|
+
...target ? { toEventId: target.eventId } : {},
|
|
3044
|
+
externalRef: workflow.retryOf,
|
|
3045
|
+
confidence: target ? "explicit" : "correlated",
|
|
3046
|
+
basis: ["attributes.retryOf"]
|
|
2969
3047
|
});
|
|
2970
|
-
const alone = asLogical(complete);
|
|
2971
|
-
logical.push(alone);
|
|
2972
|
-
logicalByRawId.set(complete.eventId, alone);
|
|
2973
|
-
const stepId = stepIdOf(complete);
|
|
2974
|
-
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
2975
|
-
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2976
|
-
}
|
|
2977
|
-
}
|
|
2978
|
-
for (const event of others) {
|
|
2979
|
-
const alone = asLogical(event);
|
|
2980
|
-
logical.push(alone);
|
|
2981
|
-
logicalByRawId.set(event.eventId, alone);
|
|
2982
|
-
const stepId = stepIdOf(event);
|
|
2983
|
-
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
2984
|
-
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2985
|
-
}
|
|
2986
3048
|
}
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
3049
|
+
const attemptOf = stringAttr(bag, "attemptOf") ?? stringAttr(bag, "operationId");
|
|
3050
|
+
if (attemptOf && workflow.attempt !== void 0) {
|
|
3051
|
+
push({
|
|
3052
|
+
type: "attempt-of",
|
|
3053
|
+
fromEventId: event.eventId,
|
|
3054
|
+
externalRef: attemptOf,
|
|
3055
|
+
confidence: "explicit",
|
|
3056
|
+
basis: workflow.attempt !== void 0 ? ["attributes.attempt", "attributes.operationId"] : ["attributes.operationId"]
|
|
3057
|
+
});
|
|
2995
3058
|
}
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
nextParent = viaStep;
|
|
3006
|
-
remapped = true;
|
|
3007
|
-
}
|
|
3059
|
+
const fallbackOf = stringAttr(bag, "fallbackOf");
|
|
3060
|
+
if (fallbackOf) {
|
|
3061
|
+
push({
|
|
3062
|
+
type: "fallback-of",
|
|
3063
|
+
fromEventId: event.eventId,
|
|
3064
|
+
externalRef: fallbackOf,
|
|
3065
|
+
confidence: "explicit",
|
|
3066
|
+
basis: ["attributes.fallbackOf"]
|
|
3067
|
+
});
|
|
3008
3068
|
}
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
});
|
|
3019
|
-
const { parentId: _drop, ...rest } = event;
|
|
3020
|
-
normalized.push({
|
|
3021
|
-
...rest,
|
|
3022
|
-
projection: {
|
|
3023
|
-
...event.projection,
|
|
3024
|
-
parentNormalized: true,
|
|
3025
|
-
originalParentId
|
|
3026
|
-
}
|
|
3027
|
-
});
|
|
3028
|
-
continue;
|
|
3029
|
-
}
|
|
3030
|
-
if (!logicalById.has(originalParentId) && !logicalByRawId.has(originalParentId)) {
|
|
3031
|
-
const mapping = event.attributes?.parentMapping;
|
|
3032
|
-
const unresolved = mapping === "unresolved" || event.attributes?.parentUnresolved === true || event.attributes?.unresolvedParent === true || // LangGraph/framework scaffolding sentinel labels are not event ids.
|
|
3033
|
-
/^LangGraph$/i.test(originalParentId) || originalParentId.startsWith("unresolved:");
|
|
3034
|
-
if (!unresolved) {
|
|
3035
|
-
diagnostics.push({
|
|
3036
|
-
code: "AI_LOGICAL_PARENT_UNRESOLVED",
|
|
3037
|
-
message: `Parent ${originalParentId} unresolved for ${event.eventId}.`,
|
|
3038
|
-
eventIds: [event.eventId]
|
|
3039
|
-
});
|
|
3040
|
-
}
|
|
3041
|
-
}
|
|
3042
|
-
normalized.push(event);
|
|
3043
|
-
continue;
|
|
3069
|
+
const remediationOf = stringAttr(bag, "remediationOf");
|
|
3070
|
+
if (remediationOf) {
|
|
3071
|
+
push({
|
|
3072
|
+
type: "remediation-of",
|
|
3073
|
+
fromEventId: event.eventId,
|
|
3074
|
+
externalRef: remediationOf,
|
|
3075
|
+
confidence: "explicit",
|
|
3076
|
+
basis: ["attributes.remediationOf"]
|
|
3077
|
+
});
|
|
3044
3078
|
}
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
),
|
|
3052
|
-
|
|
3079
|
+
const evidenceFor = stringAttr(bag, "evidenceFor");
|
|
3080
|
+
if (evidenceFor) {
|
|
3081
|
+
push({
|
|
3082
|
+
type: "evidence-for",
|
|
3083
|
+
fromEventId: event.eventId,
|
|
3084
|
+
...byId.has(evidenceFor) ? { toEventId: evidenceFor } : { externalRef: evidenceFor },
|
|
3085
|
+
confidence: byId.has(evidenceFor) ? "explicit" : "correlated",
|
|
3086
|
+
basis: ["attributes.evidenceFor"]
|
|
3053
3087
|
});
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3088
|
+
}
|
|
3089
|
+
const acceptedBy = stringAttr(bag, "acceptedBy");
|
|
3090
|
+
if (acceptedBy) {
|
|
3091
|
+
push({
|
|
3092
|
+
type: "accepted-by",
|
|
3093
|
+
fromEventId: event.eventId,
|
|
3094
|
+
...byId.has(acceptedBy) ? { toEventId: acceptedBy } : { externalRef: acceptedBy },
|
|
3095
|
+
confidence: byId.has(acceptedBy) ? "explicit" : "correlated",
|
|
3096
|
+
basis: ["attributes.acceptedBy"]
|
|
3097
|
+
});
|
|
3098
|
+
}
|
|
3099
|
+
const supersedes = stringAttr(bag, "supersedes");
|
|
3100
|
+
if (supersedes) {
|
|
3101
|
+
push({
|
|
3102
|
+
type: "supersedes",
|
|
3103
|
+
fromEventId: event.eventId,
|
|
3104
|
+
...byId.has(supersedes) ? { toEventId: supersedes } : { externalRef: supersedes },
|
|
3105
|
+
confidence: byId.has(supersedes) ? "explicit" : "correlated",
|
|
3106
|
+
basis: ["attributes.supersedes"]
|
|
3107
|
+
});
|
|
3108
|
+
}
|
|
3109
|
+
const sourceLineage = stringAttr(bag, "sourceLineage") ?? stringAttr(bag, "sourceEventId");
|
|
3110
|
+
if (sourceLineage) {
|
|
3111
|
+
push({
|
|
3112
|
+
type: "source-lineage",
|
|
3113
|
+
fromEventId: event.eventId,
|
|
3114
|
+
externalRef: sourceLineage,
|
|
3115
|
+
confidence: "explicit",
|
|
3116
|
+
basis: ["attributes.sourceLineage"]
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3119
|
+
const unsupported = stringAttr(bag, "relationshipType");
|
|
3120
|
+
if (unsupported && unsupported !== "parent-child" && ![
|
|
3121
|
+
"source-lineage",
|
|
3122
|
+
"attempt-of",
|
|
3123
|
+
"retry-of",
|
|
3124
|
+
"fallback-of",
|
|
3125
|
+
"remediation-of",
|
|
3126
|
+
"evidence-for",
|
|
3127
|
+
"accepted-by",
|
|
3128
|
+
"supersedes"
|
|
3129
|
+
].includes(unsupported)) {
|
|
3130
|
+
diagnostics.push({
|
|
3131
|
+
code: "AI_RELATIONSHIP_UNSUPPORTED_TYPE",
|
|
3132
|
+
message: `Unsupported relationshipType ${unsupported} was reported without flattening.`,
|
|
3133
|
+
eventId: event.eventId
|
|
3062
3134
|
});
|
|
3063
|
-
continue;
|
|
3064
3135
|
}
|
|
3065
|
-
diagnostics.push({
|
|
3066
|
-
code: "AI_LOGICAL_PARENT_REMAPPED",
|
|
3067
|
-
message: `Remapped parent ${originalParentId} \u2192 ${nextParent} for ${event.eventId}.`,
|
|
3068
|
-
eventIds: [event.eventId]
|
|
3069
|
-
});
|
|
3070
|
-
normalized.push({
|
|
3071
|
-
...event,
|
|
3072
|
-
parentId: nextParent,
|
|
3073
|
-
projection: {
|
|
3074
|
-
...event.projection,
|
|
3075
|
-
parentNormalized: true,
|
|
3076
|
-
originalParentId
|
|
3077
|
-
}
|
|
3078
|
-
});
|
|
3079
3136
|
}
|
|
3080
|
-
const rawIndex = new Map(events.map((e, i) => [e.eventId, i]));
|
|
3081
|
-
normalized.sort((a, b) => {
|
|
3082
|
-
const ai = rawIndex.get(a.sourceEventIds[0]) ?? 0;
|
|
3083
|
-
const bi = rawIndex.get(b.sourceEventIds[0]) ?? 0;
|
|
3084
|
-
return ai - bi || a.eventId.localeCompare(b.eventId);
|
|
3085
|
-
});
|
|
3086
3137
|
return {
|
|
3087
|
-
|
|
3138
|
+
relationships: Object.freeze(relationships),
|
|
3088
3139
|
diagnostics: Object.freeze(diagnostics)
|
|
3089
3140
|
};
|
|
3090
3141
|
}
|
|
3091
|
-
function resolveCanonicalToolName(event) {
|
|
3092
|
-
const attrs2 = event.attributes;
|
|
3093
|
-
const direct = pickString(attrs2, ["toolName", "tool"]);
|
|
3094
|
-
if (direct) return direct;
|
|
3095
|
-
const metadata = attrs2?.metadata;
|
|
3096
|
-
if (isRecord6(metadata)) {
|
|
3097
|
-
const nested = pickString(metadata, ["toolName", "tool"]);
|
|
3098
|
-
if (nested) return nested;
|
|
3099
|
-
}
|
|
3100
|
-
for (const prefix of ["tool:", "function:", "mcp-tools:"]) {
|
|
3101
|
-
if (event.name.startsWith(prefix)) return event.name.slice(prefix.length);
|
|
3102
|
-
}
|
|
3103
|
-
return event.name;
|
|
3104
|
-
}
|
|
3105
|
-
function pickString(record, keys) {
|
|
3106
|
-
if (!record) return void 0;
|
|
3107
|
-
for (const key of keys) {
|
|
3108
|
-
const value = record[key];
|
|
3109
|
-
if (typeof value === "string" && value.trim() !== "") return value.trim();
|
|
3110
|
-
}
|
|
3111
|
-
return void 0;
|
|
3112
|
-
}
|
|
3113
3142
|
|
|
3114
|
-
// packages/core/src/checks/
|
|
3115
|
-
function
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3143
|
+
// packages/core/src/checks/trace-facts.ts
|
|
3144
|
+
function summarizeSemanticParity(events) {
|
|
3145
|
+
const projection = projectLogicalEvents(events);
|
|
3146
|
+
const logical = projection.logicalEvents;
|
|
3147
|
+
const finishedTools = logical.filter(
|
|
3148
|
+
(event) => event.kind === "TOOL" && event.status !== "running"
|
|
3149
|
+
);
|
|
3150
|
+
const finishedToolNames = Object.freeze(
|
|
3151
|
+
finishedTools.map((event) => resolveCanonicalToolName(event)).sort((a, b) => a.localeCompare(b))
|
|
3152
|
+
);
|
|
3153
|
+
const derived = deriveFailureFacts(logical);
|
|
3154
|
+
return {
|
|
3155
|
+
rawEventCount: events.length,
|
|
3156
|
+
logicalEventCount: logical.length,
|
|
3157
|
+
runningLogicalCount: logical.filter((event) => event.status === "running").length,
|
|
3158
|
+
finishedToolNames,
|
|
3159
|
+
finishedToolCount: finishedTools.length,
|
|
3160
|
+
pairedCount: logical.filter((event) => event.projection.paired).length,
|
|
3161
|
+
parentRemapCount: projection.diagnostics.filter(
|
|
3162
|
+
(item) => item.code === "AI_LOGICAL_PARENT_REMAPPED"
|
|
3163
|
+
).length,
|
|
3164
|
+
diagnostics: projection.diagnostics,
|
|
3165
|
+
failureRoleCounts: derived.failureRoleCounts
|
|
3166
|
+
};
|
|
3125
3167
|
}
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
}
|
|
3168
|
+
var TRACE_FACTS_INPUT_NOT_NORMALIZED = formatProgrammaticDiagnostic(
|
|
3169
|
+
"AI_TRACE_FACTS_INPUT_NOT_NORMALIZED"
|
|
3170
|
+
);
|
|
3171
|
+
function isTraceReadResult(input) {
|
|
3172
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
3173
|
+
return false;
|
|
3133
3174
|
}
|
|
3134
|
-
|
|
3175
|
+
const record = input;
|
|
3176
|
+
return Array.isArray(record.events) && Array.isArray(record.runs) && typeof record.format === "string" && Array.isArray(record.warnings);
|
|
3135
3177
|
}
|
|
3136
|
-
function
|
|
3137
|
-
|
|
3138
|
-
const
|
|
3139
|
-
return
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
};
|
|
3178
|
+
function looksLikeRawV01TraceEvents(input) {
|
|
3179
|
+
if (!Array.isArray(input) || input.length === 0) return false;
|
|
3180
|
+
const first = input[0];
|
|
3181
|
+
if (typeof first !== "object" || first === null) return false;
|
|
3182
|
+
const row = first;
|
|
3183
|
+
return typeof row.event === "string" && (row.schemaVersion === "0.1" || row.eventId === void 0);
|
|
3143
3184
|
}
|
|
3144
|
-
function
|
|
3145
|
-
if (
|
|
3146
|
-
return
|
|
3185
|
+
function isPersistedInspectEventArray(input) {
|
|
3186
|
+
if (!Array.isArray(input)) return false;
|
|
3187
|
+
if (input.length === 0) return true;
|
|
3188
|
+
const first = input[0];
|
|
3189
|
+
if (typeof first !== "object" || first === null) return false;
|
|
3190
|
+
const row = first;
|
|
3191
|
+
return typeof row.eventId === "string" && (row.schemaVersion === "0.2" || row.schemaVersion === "1.0" || row.schemaVersion === "0.1") && typeof row.event !== "string";
|
|
3147
3192
|
}
|
|
3148
|
-
function
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
for (const key of ["linkedStepId", "toolCallId", "mcpToolCallId"]) {
|
|
3152
|
-
const value = pickString2(meta, [key]);
|
|
3153
|
-
if (value !== void 0) keys.push(`${key}:${value}`);
|
|
3193
|
+
function resolveTraceFactsEvents(input) {
|
|
3194
|
+
if (isTraceReadResult(input)) {
|
|
3195
|
+
return input.events;
|
|
3154
3196
|
}
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3197
|
+
if (looksLikeRawV01TraceEvents(input)) {
|
|
3198
|
+
throw new TypeError(TRACE_FACTS_INPUT_NOT_NORMALIZED);
|
|
3199
|
+
}
|
|
3200
|
+
if (isPersistedInspectEventArray(input)) {
|
|
3201
|
+
return input;
|
|
3202
|
+
}
|
|
3203
|
+
throw new TypeError(TRACE_FACTS_INPUT_NOT_NORMALIZED);
|
|
3158
3204
|
}
|
|
3159
|
-
function
|
|
3160
|
-
const
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3205
|
+
function buildTraceFacts(input) {
|
|
3206
|
+
const events = resolveTraceFactsEvents(input);
|
|
3207
|
+
const projection = projectLogicalEvents(events);
|
|
3208
|
+
const toolsByName = /* @__PURE__ */ new Map();
|
|
3209
|
+
const llmEvents = [];
|
|
3210
|
+
const outcomeEvents = [];
|
|
3211
|
+
for (const event of projection.logicalEvents) {
|
|
3212
|
+
if (event.kind === "TOOL" && event.status !== "running") {
|
|
3213
|
+
const name = resolveCanonicalToolName(event);
|
|
3214
|
+
const list = toolsByName.get(name) ?? [];
|
|
3215
|
+
list.push(event);
|
|
3216
|
+
toolsByName.set(name, list);
|
|
3169
3217
|
}
|
|
3170
|
-
if (event.kind === "
|
|
3171
|
-
|
|
3218
|
+
if (event.kind === "LLM" && event.status !== "running") {
|
|
3219
|
+
llmEvents.push(event);
|
|
3220
|
+
}
|
|
3221
|
+
if (event.kind === "OUTCOME") {
|
|
3222
|
+
outcomeEvents.push(event);
|
|
3172
3223
|
}
|
|
3173
|
-
existing.retryOf ??= pickString2(meta, ["retryOf"]);
|
|
3174
|
-
existing.attempt ??= pickNumber(meta, ["attempt", "retryAttempt", "retryCount"]);
|
|
3175
|
-
existing.sessionId ??= pickString2(meta, ["sessionId", "conversationId"]);
|
|
3176
|
-
existing.groupId ??= pickString2(meta, ["groupId"]);
|
|
3177
|
-
existing.parentGroupId ??= pickString2(meta, ["parentGroupId"]);
|
|
3178
|
-
existing.fallbackOf ??= pickString2(meta, ["fallbackOf", "fallbackFrom"]);
|
|
3179
|
-
byRun.set(event.runId, existing);
|
|
3180
3224
|
}
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
function sameCorrelationScope(a, b) {
|
|
3184
|
-
if (a.sessionId && b.sessionId && a.sessionId === b.sessionId) return true;
|
|
3185
|
-
if (a.groupId && b.groupId && a.groupId === b.groupId) return true;
|
|
3186
|
-
if (a.parentGroupId && b.parentGroupId && a.parentGroupId === b.parentGroupId) {
|
|
3187
|
-
return true;
|
|
3225
|
+
for (const [name, list] of [...toolsByName.entries()]) {
|
|
3226
|
+
toolsByName.set(name, Object.freeze([...list]));
|
|
3188
3227
|
}
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
return
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3228
|
+
const derived = deriveFailureFacts(projection.logicalEvents);
|
|
3229
|
+
const relationship = deriveRelationshipFacts(events);
|
|
3230
|
+
const summary = summarizeSemanticParity(events);
|
|
3231
|
+
return {
|
|
3232
|
+
rawEvents: Object.freeze([...events]),
|
|
3233
|
+
logicalEvents: projection.logicalEvents,
|
|
3234
|
+
diagnostics: projection.diagnostics,
|
|
3235
|
+
toolsByName,
|
|
3236
|
+
llmEvents: Object.freeze(llmEvents),
|
|
3237
|
+
outcomeEvents: Object.freeze(outcomeEvents),
|
|
3238
|
+
summary: {
|
|
3239
|
+
...summary,
|
|
3240
|
+
failureRoleCounts: derived.failureRoleCounts
|
|
3241
|
+
},
|
|
3242
|
+
failureFacts: derived.failureFacts,
|
|
3243
|
+
failuresByRole: derived.failuresByRole,
|
|
3244
|
+
relationships: relationship.relationships,
|
|
3245
|
+
relationshipDiagnostics: relationship.diagnostics
|
|
3246
|
+
};
|
|
3201
3247
|
}
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3248
|
+
|
|
3249
|
+
// packages/core/src/checks/index.ts
|
|
3250
|
+
var SEVERITY_RANK = {
|
|
3251
|
+
error: 0,
|
|
3252
|
+
warning: 1,
|
|
3253
|
+
info: 2
|
|
3254
|
+
};
|
|
3255
|
+
var STATUS_RANK = {
|
|
3256
|
+
fail: 0,
|
|
3257
|
+
warning: 1,
|
|
3258
|
+
pass: 2
|
|
3259
|
+
};
|
|
3260
|
+
var DEFAULT_SENSITIVE_KEYS = DEFAULT_CREDENTIAL_SENSITIVE_KEYS;
|
|
3261
|
+
var DEFAULT_RAW_CONTENT_KEYS = [
|
|
3262
|
+
"body",
|
|
3263
|
+
"headers",
|
|
3264
|
+
"input",
|
|
3265
|
+
"messages",
|
|
3266
|
+
"output",
|
|
3267
|
+
"payload",
|
|
3268
|
+
"prompt",
|
|
3269
|
+
"requestbody",
|
|
3270
|
+
"request_body",
|
|
3271
|
+
"responsebody",
|
|
3272
|
+
"response_body",
|
|
3273
|
+
"rawprompt",
|
|
3274
|
+
"raw_prompt",
|
|
3275
|
+
"rawoutput",
|
|
3276
|
+
"raw_output",
|
|
3277
|
+
"toolinput",
|
|
3278
|
+
"tool_input",
|
|
3279
|
+
"tooloutput",
|
|
3280
|
+
"tool_output",
|
|
3281
|
+
// Framework / agent metadata that carries user or task text
|
|
3282
|
+
"currenttask",
|
|
3283
|
+
"current_task",
|
|
3284
|
+
"task",
|
|
3285
|
+
"userinput",
|
|
3286
|
+
"user_input",
|
|
3287
|
+
"requesttext",
|
|
3288
|
+
"request_text",
|
|
3289
|
+
"conversationtext",
|
|
3290
|
+
"conversation_text"
|
|
3291
|
+
];
|
|
3292
|
+
var DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES = [
|
|
3293
|
+
"tokenUsage",
|
|
3294
|
+
"usage",
|
|
3295
|
+
"tokens"
|
|
3296
|
+
];
|
|
3297
|
+
var SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
|
|
3298
|
+
"input",
|
|
3299
|
+
"output",
|
|
3300
|
+
"total",
|
|
3301
|
+
"cached",
|
|
3302
|
+
"cachewrite",
|
|
3303
|
+
"cache_write",
|
|
3304
|
+
"reasoning",
|
|
3305
|
+
"input_tokens",
|
|
3306
|
+
"inputtokens",
|
|
3307
|
+
"output_tokens",
|
|
3308
|
+
"outputtokens",
|
|
3309
|
+
"total_tokens",
|
|
3310
|
+
"totaltokens",
|
|
3311
|
+
"prompt_tokens",
|
|
3312
|
+
"prompttokens",
|
|
3313
|
+
"completion_tokens",
|
|
3314
|
+
"completiontokens",
|
|
3315
|
+
"cache_read",
|
|
3316
|
+
"cacheread",
|
|
3317
|
+
"cache_creation",
|
|
3318
|
+
"cachecreation",
|
|
3319
|
+
"reasoning_tokens",
|
|
3320
|
+
"reasoningtokens"
|
|
3321
|
+
]);
|
|
3322
|
+
var DEFAULT_SECRET_PATTERNS = [
|
|
3323
|
+
{ id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
|
|
3324
|
+
{ id: "openai-key", pattern: /sk-[A-Za-z0-9_-]{16,}/ },
|
|
3325
|
+
{ id: "aws-access-key", pattern: /AKIA[0-9A-Z]{16}/ },
|
|
3326
|
+
{ id: "github-token", pattern: /gh[opsu]_[A-Za-z0-9_]{20,}/ },
|
|
3327
|
+
// Keep in sync with packages/redact/src/key-value-secret.ts (KEY_VALUE_SECRET_PATTERN_SOURCE).
|
|
3328
|
+
{
|
|
3329
|
+
id: "key-value-secret",
|
|
3330
|
+
pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})/i
|
|
3255
3331
|
}
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3332
|
+
];
|
|
3333
|
+
function compareStrings(a, b) {
|
|
3334
|
+
return (a ?? "").localeCompare(b ?? "");
|
|
3335
|
+
}
|
|
3336
|
+
function diagnostic(code, message, ruleId) {
|
|
3337
|
+
return {
|
|
3338
|
+
code,
|
|
3339
|
+
message,
|
|
3340
|
+
severity: "error",
|
|
3341
|
+
...ruleId ? { ruleId } : {}
|
|
3342
|
+
};
|
|
3343
|
+
}
|
|
3344
|
+
function emptySummary() {
|
|
3345
|
+
return {
|
|
3346
|
+
passed: 0,
|
|
3347
|
+
failed: 0,
|
|
3348
|
+
warnings: 0,
|
|
3349
|
+
errors: 0,
|
|
3350
|
+
rulesEvaluated: 0
|
|
3351
|
+
};
|
|
3352
|
+
}
|
|
3353
|
+
function errorResult(input, diagnostics, selectedRun, ruleExecutions = []) {
|
|
3354
|
+
return {
|
|
3355
|
+
ok: false,
|
|
3356
|
+
status: "error",
|
|
3357
|
+
format: input.read.format,
|
|
3358
|
+
...selectedRun ? { runId: selectedRun.runId } : {},
|
|
3359
|
+
summary: {
|
|
3360
|
+
...emptySummary(),
|
|
3361
|
+
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
3362
|
+
rulesEvaluated: ruleExecutions.length
|
|
3363
|
+
},
|
|
3364
|
+
findings: [],
|
|
3365
|
+
diagnostics: [...diagnostics],
|
|
3366
|
+
ruleExecutions: [...ruleExecutions]
|
|
3367
|
+
};
|
|
3368
|
+
}
|
|
3369
|
+
function flattenNodes(nodes) {
|
|
3370
|
+
return nodes.flatMap((node) => [node, ...flattenNodes(node.children)]);
|
|
3371
|
+
}
|
|
3372
|
+
function buildFacts(input, selectedRun) {
|
|
3373
|
+
const scopedRuns = selectedRun ? [selectedRun] : input.read.runs;
|
|
3374
|
+
const scopedRunIds = new Set(scopedRuns.map((run) => run.runId));
|
|
3375
|
+
const scopedEvents = selectedRun === void 0 ? input.read.events : input.read.events.filter((event) => scopedRunIds.has(event.runId));
|
|
3376
|
+
const nodes = flattenNodes(scopedRuns.flatMap((run) => run.children));
|
|
3377
|
+
const nodesByEventId = /* @__PURE__ */ new Map();
|
|
3378
|
+
const childrenByParentId = /* @__PURE__ */ new Map();
|
|
3379
|
+
for (const node of nodes) {
|
|
3380
|
+
nodesByEventId.set(node.event.eventId, node);
|
|
3381
|
+
const parentId = node.event.parentId;
|
|
3382
|
+
if (parentId) {
|
|
3383
|
+
const children = childrenByParentId.get(parentId) ?? [];
|
|
3384
|
+
children.push(node);
|
|
3385
|
+
childrenByParentId.set(parentId, children);
|
|
3261
3386
|
}
|
|
3262
3387
|
}
|
|
3263
|
-
|
|
3388
|
+
const projection = projectLogicalEvents(scopedEvents);
|
|
3389
|
+
return {
|
|
3390
|
+
format: input.read.format,
|
|
3391
|
+
runs: Object.freeze([...input.read.runs]),
|
|
3392
|
+
events: Object.freeze([...scopedEvents]),
|
|
3393
|
+
logicalEvents: projection.logicalEvents,
|
|
3394
|
+
logicalProjectionDiagnostics: projection.diagnostics,
|
|
3395
|
+
readerWarnings: Object.freeze([...input.read.warnings]),
|
|
3396
|
+
unsupportedFields: Object.freeze([...input.read.unsupportedFields]),
|
|
3397
|
+
sourceFiles: Object.freeze([...input.read.sourceFiles]),
|
|
3398
|
+
nodesByEventId,
|
|
3399
|
+
childrenByParentId,
|
|
3400
|
+
rootNodes: Object.freeze(scopedRuns.flatMap((run) => run.children))
|
|
3401
|
+
};
|
|
3264
3402
|
}
|
|
3265
|
-
function
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
const retryRunIds = Object.freeze(
|
|
3269
|
-
[...new Set(candidates.map((c) => c.viaRunId).filter((id) => id !== void 0))].sort(
|
|
3270
|
-
(a, b) => a.localeCompare(b)
|
|
3271
|
-
)
|
|
3272
|
-
);
|
|
3273
|
-
if (successful.length > 1) {
|
|
3274
|
-
const distinctRuns = new Set(successful.map((c) => c.event.runId));
|
|
3275
|
-
const distinctParents = new Set(
|
|
3276
|
-
successful.map((c) => c.event.parentId ?? "").filter((id) => id !== "")
|
|
3277
|
-
);
|
|
3278
|
-
if (distinctRuns.size > 1 || distinctParents.size > 1) {
|
|
3403
|
+
function resolveSelectedRun(input, runId) {
|
|
3404
|
+
if (input.selectedRun) {
|
|
3405
|
+
if (runId && input.selectedRun.runId !== runId) {
|
|
3279
3406
|
return {
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
basis: Object.freeze(["ambiguous-recovery-candidates"]),
|
|
3287
|
-
recoveryEventIds: Object.freeze(
|
|
3288
|
-
successful.map((c) => c.event.eventId).sort((a, b) => a.localeCompare(b))
|
|
3289
|
-
),
|
|
3290
|
-
retryRunIds
|
|
3407
|
+
diagnostics: [
|
|
3408
|
+
diagnostic(
|
|
3409
|
+
"AI_CHECK_INVALID_ARGUMENTS",
|
|
3410
|
+
`Selected run ${input.selectedRun.runId} does not match requested run ${runId}.`
|
|
3411
|
+
)
|
|
3412
|
+
]
|
|
3291
3413
|
};
|
|
3292
3414
|
}
|
|
3415
|
+
return { run: input.selectedRun, diagnostics: [] };
|
|
3293
3416
|
}
|
|
3294
|
-
if (
|
|
3295
|
-
const
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
recoveryEventIds: Object.freeze(
|
|
3305
|
-
successful.map((c) => c.event.eventId).sort((a, b) => a.localeCompare(b))
|
|
3306
|
-
),
|
|
3307
|
-
retryRunIds
|
|
3308
|
-
};
|
|
3417
|
+
if (runId) {
|
|
3418
|
+
const run = input.read.runs.find((candidate) => candidate.runId === runId);
|
|
3419
|
+
if (!run) {
|
|
3420
|
+
return {
|
|
3421
|
+
diagnostics: [
|
|
3422
|
+
diagnostic("AI_CHECK_RUN_SELECTION_REQUIRED", `Run not found: ${runId}.`)
|
|
3423
|
+
]
|
|
3424
|
+
};
|
|
3425
|
+
}
|
|
3426
|
+
return { run, diagnostics: [] };
|
|
3309
3427
|
}
|
|
3310
|
-
if (
|
|
3311
|
-
|
|
3312
|
-
return {
|
|
3313
|
-
eventId: failure.eventId,
|
|
3314
|
-
runId: failure.runId,
|
|
3315
|
-
name: failure.name,
|
|
3316
|
-
kind: failure.kind,
|
|
3317
|
-
role: "transient",
|
|
3318
|
-
confidence: best.confidence,
|
|
3319
|
-
basis: Object.freeze([
|
|
3320
|
-
best.basis,
|
|
3321
|
-
unsuccessful.some((c) => c.event.status === void 0) ? "retry-incomplete" : "retry-without-success"
|
|
3322
|
-
]),
|
|
3323
|
-
recoveryEventIds: Object.freeze([]),
|
|
3324
|
-
retryRunIds
|
|
3325
|
-
};
|
|
3428
|
+
if (input.read.runs.length === 1) {
|
|
3429
|
+
return { run: input.read.runs[0], diagnostics: [] };
|
|
3326
3430
|
}
|
|
3327
|
-
|
|
3328
|
-
const declaredSuccessor = pickString2(failureMeta, [
|
|
3329
|
-
"retriedBy",
|
|
3330
|
-
"nextRetryRunId",
|
|
3331
|
-
"retryRunId"
|
|
3332
|
-
]);
|
|
3333
|
-
if (declaredSuccessor !== void 0 && !runs.has(declaredSuccessor)) {
|
|
3431
|
+
if (input.read.runs.length === 0) {
|
|
3334
3432
|
return {
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3433
|
+
diagnostics: [
|
|
3434
|
+
diagnostic(
|
|
3435
|
+
"AI_CHECK_RUN_SELECTION_REQUIRED",
|
|
3436
|
+
formatProgrammaticDiagnostic(
|
|
3437
|
+
"AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED",
|
|
3438
|
+
"No runs are available for checks."
|
|
3439
|
+
)
|
|
3440
|
+
)
|
|
3441
|
+
]
|
|
3344
3442
|
};
|
|
3345
3443
|
}
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3444
|
+
return {
|
|
3445
|
+
diagnostics: [
|
|
3446
|
+
diagnostic(
|
|
3447
|
+
"AI_CHECK_RUN_SELECTION_REQUIRED",
|
|
3448
|
+
formatProgrammaticDiagnostic("AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED")
|
|
3449
|
+
)
|
|
3450
|
+
]
|
|
3451
|
+
};
|
|
3452
|
+
}
|
|
3453
|
+
function selectRules(rules, selectedIds) {
|
|
3454
|
+
const diagnostics = [];
|
|
3455
|
+
const byId = /* @__PURE__ */ new Map();
|
|
3456
|
+
for (const rule of rules) {
|
|
3457
|
+
if (byId.has(rule.id)) {
|
|
3458
|
+
diagnostics.push(
|
|
3459
|
+
diagnostic("AI_CHECK_INVALID_CONFIG", `Duplicate trace check rule id: ${rule.id}.`, rule.id)
|
|
3460
|
+
);
|
|
3461
|
+
continue;
|
|
3359
3462
|
}
|
|
3463
|
+
byId.set(rule.id, rule);
|
|
3360
3464
|
}
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3465
|
+
if (selectedIds && selectedIds.length > 0) {
|
|
3466
|
+
const selected = new Set(selectedIds);
|
|
3467
|
+
for (const id of selected) {
|
|
3468
|
+
if (!byId.has(id)) {
|
|
3469
|
+
diagnostics.push(
|
|
3470
|
+
diagnostic("AI_CHECK_INVALID_CONFIG", `Unknown trace check rule id: ${id}.`, id)
|
|
3471
|
+
);
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3367
3474
|
return {
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
name: failure.name,
|
|
3371
|
-
kind: failure.kind,
|
|
3372
|
-
role: "terminal",
|
|
3373
|
-
confidence: failureRun.retryOf !== void 0 ? "explicit" : "correlated",
|
|
3374
|
-
basis: Object.freeze(["final-retry-chain-member", "enclosing-run-error"]),
|
|
3375
|
-
recoveryEventIds: Object.freeze([]),
|
|
3376
|
-
retryRunIds: Object.freeze(
|
|
3377
|
-
failureRun.retryOf !== void 0 ? [failureRun.retryOf] : []
|
|
3378
|
-
)
|
|
3475
|
+
rules: [...byId.values()].filter((rule) => selected.has(rule.id)).sort(compareRules),
|
|
3476
|
+
diagnostics
|
|
3379
3477
|
};
|
|
3380
3478
|
}
|
|
3479
|
+
return { rules: [...byId.values()].sort(compareRules), diagnostics };
|
|
3480
|
+
}
|
|
3481
|
+
function compareRules(a, b) {
|
|
3482
|
+
return a.id.localeCompare(b.id);
|
|
3483
|
+
}
|
|
3484
|
+
function eventTimestamp(finding, eventById) {
|
|
3485
|
+
const eventId = finding.evidence[0]?.eventId;
|
|
3486
|
+
return eventId ? eventById.get(eventId)?.timestamp ?? "" : "";
|
|
3487
|
+
}
|
|
3488
|
+
function compareFindings(eventById) {
|
|
3489
|
+
return (a, b) => {
|
|
3490
|
+
if (SEVERITY_RANK[a.severity] !== SEVERITY_RANK[b.severity]) {
|
|
3491
|
+
return SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
|
|
3492
|
+
}
|
|
3493
|
+
const byRule = a.ruleId.localeCompare(b.ruleId);
|
|
3494
|
+
if (byRule !== 0) return byRule;
|
|
3495
|
+
if (STATUS_RANK[a.status] !== STATUS_RANK[b.status]) {
|
|
3496
|
+
return STATUS_RANK[a.status] - STATUS_RANK[b.status];
|
|
3497
|
+
}
|
|
3498
|
+
const byRun = compareStrings(a.evidence[0]?.runId, b.evidence[0]?.runId);
|
|
3499
|
+
if (byRun !== 0) return byRun;
|
|
3500
|
+
const byTime = eventTimestamp(a, eventById).localeCompare(eventTimestamp(b, eventById));
|
|
3501
|
+
if (byTime !== 0) return byTime;
|
|
3502
|
+
const byEvent = compareStrings(a.evidence[0]?.eventId, b.evidence[0]?.eventId);
|
|
3503
|
+
if (byEvent !== 0) return byEvent;
|
|
3504
|
+
return compareStrings(a.evidence[0]?.path, b.evidence[0]?.path);
|
|
3505
|
+
};
|
|
3506
|
+
}
|
|
3507
|
+
function normalizeFinding(rule, finding) {
|
|
3381
3508
|
return {
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3509
|
+
ruleId: finding.ruleId || rule.id,
|
|
3510
|
+
severity: finding.severity ?? rule.defaultSeverity,
|
|
3511
|
+
status: finding.status,
|
|
3512
|
+
message: finding.message,
|
|
3513
|
+
...finding.expected !== void 0 ? { expected: finding.expected } : {},
|
|
3514
|
+
...finding.actual !== void 0 ? { actual: finding.actual } : {},
|
|
3515
|
+
evidence: [...finding.evidence ?? []],
|
|
3516
|
+
...finding.category !== void 0 ? { category: finding.category } : {},
|
|
3517
|
+
...finding.confidence !== void 0 ? { confidence: finding.confidence } : {},
|
|
3518
|
+
...finding.detector !== void 0 ? { detector: finding.detector } : {},
|
|
3519
|
+
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
3391
3520
|
};
|
|
3392
3521
|
}
|
|
3393
|
-
function
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3522
|
+
function summarize(findings, diagnostics, rulesEvaluated) {
|
|
3523
|
+
return {
|
|
3524
|
+
passed: findings.filter((finding) => finding.status === "pass").length,
|
|
3525
|
+
failed: findings.filter(
|
|
3526
|
+
(finding) => finding.status === "fail" && finding.severity === "error"
|
|
3527
|
+
).length,
|
|
3528
|
+
warnings: findings.filter(
|
|
3529
|
+
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
3530
|
+
).length,
|
|
3531
|
+
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
3532
|
+
rulesEvaluated
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3535
|
+
function classifyRuleExecution(findings, threw) {
|
|
3536
|
+
if (findings.some((finding) => finding.status === "fail" && finding.severity === "error")) {
|
|
3537
|
+
return "fail";
|
|
3407
3538
|
}
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
[...list].sort((a, b) => {
|
|
3413
|
-
const byRun = a.runId.localeCompare(b.runId);
|
|
3414
|
-
if (byRun !== 0) return byRun;
|
|
3415
|
-
return a.eventId.localeCompare(b.eventId);
|
|
3416
|
-
})
|
|
3417
|
-
)
|
|
3418
|
-
);
|
|
3539
|
+
if (findings.some(
|
|
3540
|
+
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
3541
|
+
)) {
|
|
3542
|
+
return "warning";
|
|
3419
3543
|
}
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3544
|
+
return "pass";
|
|
3545
|
+
}
|
|
3546
|
+
function eventEvidence(event, path16) {
|
|
3547
|
+
return {
|
|
3548
|
+
runId: event.runId,
|
|
3549
|
+
eventId: event.eventId,
|
|
3550
|
+
parentId: event.parentId,
|
|
3551
|
+
traceId: event.trace?.traceId,
|
|
3552
|
+
spanId: event.trace?.spanId,
|
|
3553
|
+
kind: event.kind,
|
|
3554
|
+
name: event.name,
|
|
3555
|
+
status: event.status,
|
|
3556
|
+
...path16 ? { path: path16 } : {}
|
|
3425
3557
|
};
|
|
3558
|
+
}
|
|
3559
|
+
function runEvidence(run) {
|
|
3560
|
+
return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
|
|
3561
|
+
}
|
|
3562
|
+
function failFinding(ruleId, message, evidence, expected, actual, meta) {
|
|
3426
3563
|
return {
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3564
|
+
ruleId,
|
|
3565
|
+
severity: meta?.severity ?? "error",
|
|
3566
|
+
status: meta?.status ?? "fail",
|
|
3567
|
+
message,
|
|
3568
|
+
...expected !== void 0 ? { expected } : {},
|
|
3569
|
+
...actual !== void 0 ? { actual } : {},
|
|
3570
|
+
evidence: [...evidence],
|
|
3571
|
+
...meta?.category !== void 0 ? { category: meta.category } : {},
|
|
3572
|
+
...meta?.confidence !== void 0 ? { confidence: meta.confidence } : {},
|
|
3573
|
+
...meta?.detector !== void 0 ? { detector: meta.detector } : {},
|
|
3574
|
+
...meta?.action !== void 0 ? { action: meta.action } : {}
|
|
3430
3575
|
};
|
|
3431
3576
|
}
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
function attrs(event) {
|
|
3435
|
-
return event.attributes && typeof event.attributes === "object" ? event.attributes : {};
|
|
3577
|
+
function semanticEvents(context) {
|
|
3578
|
+
return context.logicalEvents ?? context.events;
|
|
3436
3579
|
}
|
|
3437
|
-
function
|
|
3438
|
-
|
|
3439
|
-
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
3580
|
+
function isRecord8(value) {
|
|
3581
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3440
3582
|
}
|
|
3441
|
-
function
|
|
3442
|
-
|
|
3443
|
-
const diagnostics = [];
|
|
3444
|
-
const byId = new Map(events.map((event) => [event.eventId, event]));
|
|
3445
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3446
|
-
const push = (edge) => {
|
|
3447
|
-
const key = `${edge.type}:${edge.fromEventId}:${edge.toEventId ?? ""}:${edge.externalRef ?? ""}`;
|
|
3448
|
-
if (seen.has(key)) return;
|
|
3449
|
-
seen.add(key);
|
|
3450
|
-
relationships.push(edge);
|
|
3451
|
-
};
|
|
3452
|
-
for (const event of events) {
|
|
3453
|
-
if (event.parentId) {
|
|
3454
|
-
push({
|
|
3455
|
-
type: "parent-child",
|
|
3456
|
-
fromEventId: event.parentId,
|
|
3457
|
-
toEventId: event.eventId,
|
|
3458
|
-
confidence: byId.has(event.parentId) ? "explicit" : "unknown",
|
|
3459
|
-
basis: ["persisted.parentId"]
|
|
3460
|
-
});
|
|
3461
|
-
if (!byId.has(event.parentId)) {
|
|
3462
|
-
diagnostics.push({
|
|
3463
|
-
code: "AI_RELATIONSHIP_PARENT_MISSING",
|
|
3464
|
-
message: `parentId ${event.parentId} is not present in the event set.`,
|
|
3465
|
-
eventId: event.eventId
|
|
3466
|
-
});
|
|
3467
|
-
}
|
|
3468
|
-
}
|
|
3469
|
-
const bag = attrs(event);
|
|
3470
|
-
const meta = extractSessionWorkflowMetadata(bag) ?? {};
|
|
3471
|
-
const nested = bag.metadata && typeof bag.metadata === "object" ? extractSessionWorkflowMetadata(bag.metadata) : void 0;
|
|
3472
|
-
const workflow = { ...meta, ...nested };
|
|
3473
|
-
if (workflow.retryOf) {
|
|
3474
|
-
const target = events.find((candidate) => candidate.runId === workflow.retryOf);
|
|
3475
|
-
push({
|
|
3476
|
-
type: "retry-of",
|
|
3477
|
-
fromEventId: event.eventId,
|
|
3478
|
-
...target ? { toEventId: target.eventId } : {},
|
|
3479
|
-
externalRef: workflow.retryOf,
|
|
3480
|
-
confidence: target ? "explicit" : "correlated",
|
|
3481
|
-
basis: ["attributes.retryOf"]
|
|
3482
|
-
});
|
|
3483
|
-
}
|
|
3484
|
-
const attemptOf = stringAttr(bag, "attemptOf") ?? stringAttr(bag, "operationId");
|
|
3485
|
-
if (attemptOf && workflow.attempt !== void 0) {
|
|
3486
|
-
push({
|
|
3487
|
-
type: "attempt-of",
|
|
3488
|
-
fromEventId: event.eventId,
|
|
3489
|
-
externalRef: attemptOf,
|
|
3490
|
-
confidence: "explicit",
|
|
3491
|
-
basis: workflow.attempt !== void 0 ? ["attributes.attempt", "attributes.operationId"] : ["attributes.operationId"]
|
|
3492
|
-
});
|
|
3493
|
-
}
|
|
3494
|
-
const fallbackOf = stringAttr(bag, "fallbackOf");
|
|
3495
|
-
if (fallbackOf) {
|
|
3496
|
-
push({
|
|
3497
|
-
type: "fallback-of",
|
|
3498
|
-
fromEventId: event.eventId,
|
|
3499
|
-
externalRef: fallbackOf,
|
|
3500
|
-
confidence: "explicit",
|
|
3501
|
-
basis: ["attributes.fallbackOf"]
|
|
3502
|
-
});
|
|
3503
|
-
}
|
|
3504
|
-
const remediationOf = stringAttr(bag, "remediationOf");
|
|
3505
|
-
if (remediationOf) {
|
|
3506
|
-
push({
|
|
3507
|
-
type: "remediation-of",
|
|
3508
|
-
fromEventId: event.eventId,
|
|
3509
|
-
externalRef: remediationOf,
|
|
3510
|
-
confidence: "explicit",
|
|
3511
|
-
basis: ["attributes.remediationOf"]
|
|
3512
|
-
});
|
|
3513
|
-
}
|
|
3514
|
-
const evidenceFor = stringAttr(bag, "evidenceFor");
|
|
3515
|
-
if (evidenceFor) {
|
|
3516
|
-
push({
|
|
3517
|
-
type: "evidence-for",
|
|
3518
|
-
fromEventId: event.eventId,
|
|
3519
|
-
...byId.has(evidenceFor) ? { toEventId: evidenceFor } : { externalRef: evidenceFor },
|
|
3520
|
-
confidence: byId.has(evidenceFor) ? "explicit" : "correlated",
|
|
3521
|
-
basis: ["attributes.evidenceFor"]
|
|
3522
|
-
});
|
|
3523
|
-
}
|
|
3524
|
-
const acceptedBy = stringAttr(bag, "acceptedBy");
|
|
3525
|
-
if (acceptedBy) {
|
|
3526
|
-
push({
|
|
3527
|
-
type: "accepted-by",
|
|
3528
|
-
fromEventId: event.eventId,
|
|
3529
|
-
...byId.has(acceptedBy) ? { toEventId: acceptedBy } : { externalRef: acceptedBy },
|
|
3530
|
-
confidence: byId.has(acceptedBy) ? "explicit" : "correlated",
|
|
3531
|
-
basis: ["attributes.acceptedBy"]
|
|
3532
|
-
});
|
|
3533
|
-
}
|
|
3534
|
-
const supersedes = stringAttr(bag, "supersedes");
|
|
3535
|
-
if (supersedes) {
|
|
3536
|
-
push({
|
|
3537
|
-
type: "supersedes",
|
|
3538
|
-
fromEventId: event.eventId,
|
|
3539
|
-
...byId.has(supersedes) ? { toEventId: supersedes } : { externalRef: supersedes },
|
|
3540
|
-
confidence: byId.has(supersedes) ? "explicit" : "correlated",
|
|
3541
|
-
basis: ["attributes.supersedes"]
|
|
3542
|
-
});
|
|
3543
|
-
}
|
|
3544
|
-
const sourceLineage = stringAttr(bag, "sourceLineage") ?? stringAttr(bag, "sourceEventId");
|
|
3545
|
-
if (sourceLineage) {
|
|
3546
|
-
push({
|
|
3547
|
-
type: "source-lineage",
|
|
3548
|
-
fromEventId: event.eventId,
|
|
3549
|
-
externalRef: sourceLineage,
|
|
3550
|
-
confidence: "explicit",
|
|
3551
|
-
basis: ["attributes.sourceLineage"]
|
|
3552
|
-
});
|
|
3553
|
-
}
|
|
3554
|
-
const unsupported = stringAttr(bag, "relationshipType");
|
|
3555
|
-
if (unsupported && unsupported !== "parent-child" && ![
|
|
3556
|
-
"source-lineage",
|
|
3557
|
-
"attempt-of",
|
|
3558
|
-
"retry-of",
|
|
3559
|
-
"fallback-of",
|
|
3560
|
-
"remediation-of",
|
|
3561
|
-
"evidence-for",
|
|
3562
|
-
"accepted-by",
|
|
3563
|
-
"supersedes"
|
|
3564
|
-
].includes(unsupported)) {
|
|
3565
|
-
diagnostics.push({
|
|
3566
|
-
code: "AI_RELATIONSHIP_UNSUPPORTED_TYPE",
|
|
3567
|
-
message: `Unsupported relationshipType ${unsupported} was reported without flattening.`,
|
|
3568
|
-
eventId: event.eventId
|
|
3569
|
-
});
|
|
3570
|
-
}
|
|
3571
|
-
}
|
|
3572
|
-
return {
|
|
3573
|
-
relationships: Object.freeze(relationships),
|
|
3574
|
-
diagnostics: Object.freeze(diagnostics)
|
|
3575
|
-
};
|
|
3576
|
-
}
|
|
3577
|
-
|
|
3578
|
-
// packages/core/src/checks/trace-facts.ts
|
|
3579
|
-
function summarizeSemanticParity(events) {
|
|
3580
|
-
const projection = projectLogicalEvents(events);
|
|
3581
|
-
const logical = projection.logicalEvents;
|
|
3582
|
-
const finishedTools = logical.filter(
|
|
3583
|
-
(event) => event.kind === "TOOL" && event.status !== "running"
|
|
3584
|
-
);
|
|
3585
|
-
const finishedToolNames = Object.freeze(
|
|
3586
|
-
finishedTools.map((event) => resolveCanonicalToolName(event)).sort((a, b) => a.localeCompare(b))
|
|
3587
|
-
);
|
|
3588
|
-
const derived = deriveFailureFacts(logical);
|
|
3589
|
-
return {
|
|
3590
|
-
rawEventCount: events.length,
|
|
3591
|
-
logicalEventCount: logical.length,
|
|
3592
|
-
runningLogicalCount: logical.filter((event) => event.status === "running").length,
|
|
3593
|
-
finishedToolNames,
|
|
3594
|
-
finishedToolCount: finishedTools.length,
|
|
3595
|
-
pairedCount: logical.filter((event) => event.projection.paired).length,
|
|
3596
|
-
parentRemapCount: projection.diagnostics.filter(
|
|
3597
|
-
(item) => item.code === "AI_LOGICAL_PARENT_REMAPPED"
|
|
3598
|
-
).length,
|
|
3599
|
-
diagnostics: projection.diagnostics,
|
|
3600
|
-
failureRoleCounts: derived.failureRoleCounts
|
|
3601
|
-
};
|
|
3602
|
-
}
|
|
3603
|
-
var TRACE_FACTS_INPUT_NOT_NORMALIZED = formatProgrammaticDiagnostic(
|
|
3604
|
-
"AI_TRACE_FACTS_INPUT_NOT_NORMALIZED"
|
|
3605
|
-
);
|
|
3606
|
-
function isTraceReadResult(input) {
|
|
3607
|
-
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
3608
|
-
return false;
|
|
3609
|
-
}
|
|
3610
|
-
const record = input;
|
|
3611
|
-
return Array.isArray(record.events) && Array.isArray(record.runs) && typeof record.format === "string" && Array.isArray(record.warnings);
|
|
3583
|
+
function normalizedKey(value) {
|
|
3584
|
+
return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
|
|
3612
3585
|
}
|
|
3613
|
-
function
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
if (typeof first !== "object" || first === null) return false;
|
|
3617
|
-
const row = first;
|
|
3618
|
-
return typeof row.event === "string" && (row.schemaVersion === "0.1" || row.eventId === void 0);
|
|
3586
|
+
function lastPathSegment(path16) {
|
|
3587
|
+
const parts = path16.split(".");
|
|
3588
|
+
return parts[parts.length - 1] ?? path16;
|
|
3619
3589
|
}
|
|
3620
|
-
function
|
|
3621
|
-
if (
|
|
3622
|
-
if (
|
|
3623
|
-
|
|
3624
|
-
if (typeof first !== "object" || first === null) return false;
|
|
3625
|
-
const row = first;
|
|
3626
|
-
return typeof row.eventId === "string" && (row.schemaVersion === "0.2" || row.schemaVersion === "1.0" || row.schemaVersion === "0.1") && typeof row.event !== "string";
|
|
3590
|
+
function valueType(value) {
|
|
3591
|
+
if (Array.isArray(value)) return "array";
|
|
3592
|
+
if (value === null) return "null";
|
|
3593
|
+
return typeof value;
|
|
3627
3594
|
}
|
|
3628
|
-
function
|
|
3629
|
-
|
|
3630
|
-
return
|
|
3595
|
+
function serializedByteLength(value) {
|
|
3596
|
+
try {
|
|
3597
|
+
return Buffer.byteLength(JSON.stringify(value), "utf-8");
|
|
3598
|
+
} catch {
|
|
3599
|
+
return void 0;
|
|
3631
3600
|
}
|
|
3632
|
-
|
|
3633
|
-
|
|
3601
|
+
}
|
|
3602
|
+
function pushValueEntries(entries, event, value, path16, key, depth = 0) {
|
|
3603
|
+
entries.push({ event, path: path16, key, value });
|
|
3604
|
+
if (depth >= 8) return;
|
|
3605
|
+
if (Array.isArray(value)) {
|
|
3606
|
+
for (const [index, item] of value.entries()) {
|
|
3607
|
+
pushValueEntries(entries, event, item, `${path16}.${index}`, String(index), depth + 1);
|
|
3608
|
+
}
|
|
3609
|
+
return;
|
|
3634
3610
|
}
|
|
3635
|
-
if (
|
|
3636
|
-
|
|
3611
|
+
if (!isRecord8(value)) return;
|
|
3612
|
+
for (const nestedKey of Object.keys(value).sort((a, b) => a.localeCompare(b))) {
|
|
3613
|
+
pushValueEntries(
|
|
3614
|
+
entries,
|
|
3615
|
+
event,
|
|
3616
|
+
value[nestedKey],
|
|
3617
|
+
`${path16}.${nestedKey}`,
|
|
3618
|
+
nestedKey,
|
|
3619
|
+
depth + 1
|
|
3620
|
+
);
|
|
3637
3621
|
}
|
|
3638
|
-
throw new TypeError(TRACE_FACTS_INPUT_NOT_NORMALIZED);
|
|
3639
3622
|
}
|
|
3640
|
-
function
|
|
3641
|
-
const
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
const name = resolveCanonicalToolName(event);
|
|
3649
|
-
const list = toolsByName.get(name) ?? [];
|
|
3650
|
-
list.push(event);
|
|
3651
|
-
toolsByName.set(name, list);
|
|
3652
|
-
}
|
|
3653
|
-
if (event.kind === "LLM" && event.status !== "running") {
|
|
3654
|
-
llmEvents.push(event);
|
|
3623
|
+
function eventValueEntries(event, options = {}) {
|
|
3624
|
+
const entries = [];
|
|
3625
|
+
if (event.attributes !== void 0) {
|
|
3626
|
+
pushValueEntries(entries, event, event.attributes, "attributes", "attributes");
|
|
3627
|
+
}
|
|
3628
|
+
if (options.includeSummaries) {
|
|
3629
|
+
if (event.inputSummary !== void 0) {
|
|
3630
|
+
pushValueEntries(entries, event, event.inputSummary, "inputSummary", "inputSummary");
|
|
3655
3631
|
}
|
|
3656
|
-
if (event.
|
|
3657
|
-
|
|
3632
|
+
if (event.outputSummary !== void 0) {
|
|
3633
|
+
pushValueEntries(entries, event, event.outputSummary, "outputSummary", "outputSummary");
|
|
3658
3634
|
}
|
|
3659
3635
|
}
|
|
3660
|
-
|
|
3661
|
-
|
|
3636
|
+
if (options.includeError && event.error !== void 0) {
|
|
3637
|
+
pushValueEntries(entries, event, event.error, "error", "error");
|
|
3662
3638
|
}
|
|
3663
|
-
|
|
3664
|
-
const relationship = deriveRelationshipFacts(events);
|
|
3665
|
-
const summary = summarizeSemanticParity(events);
|
|
3666
|
-
return {
|
|
3667
|
-
rawEvents: Object.freeze([...events]),
|
|
3668
|
-
logicalEvents: projection.logicalEvents,
|
|
3669
|
-
diagnostics: projection.diagnostics,
|
|
3670
|
-
toolsByName,
|
|
3671
|
-
llmEvents: Object.freeze(llmEvents),
|
|
3672
|
-
outcomeEvents: Object.freeze(outcomeEvents),
|
|
3673
|
-
summary: {
|
|
3674
|
-
...summary,
|
|
3675
|
-
failureRoleCounts: derived.failureRoleCounts
|
|
3676
|
-
},
|
|
3677
|
-
failureFacts: derived.failureFacts,
|
|
3678
|
-
failuresByRole: derived.failuresByRole,
|
|
3679
|
-
relationships: relationship.relationships,
|
|
3680
|
-
relationshipDiagnostics: relationship.diagnostics
|
|
3681
|
-
};
|
|
3639
|
+
return entries;
|
|
3682
3640
|
}
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
// packages/core/src/checks/index.ts
|
|
3688
|
-
var SEVERITY_RANK = {
|
|
3689
|
-
error: 0,
|
|
3690
|
-
warning: 1,
|
|
3691
|
-
info: 2
|
|
3692
|
-
};
|
|
3693
|
-
var STATUS_RANK = {
|
|
3694
|
-
fail: 0,
|
|
3695
|
-
warning: 1,
|
|
3696
|
-
pass: 2
|
|
3697
|
-
};
|
|
3698
|
-
var DEFAULT_SENSITIVE_KEYS = DEFAULT_CREDENTIAL_SENSITIVE_KEYS;
|
|
3699
|
-
var DEFAULT_RAW_CONTENT_KEYS = [
|
|
3700
|
-
"body",
|
|
3701
|
-
"headers",
|
|
3702
|
-
"input",
|
|
3703
|
-
"messages",
|
|
3704
|
-
"output",
|
|
3705
|
-
"payload",
|
|
3706
|
-
"prompt",
|
|
3707
|
-
"requestbody",
|
|
3708
|
-
"request_body",
|
|
3709
|
-
"responsebody",
|
|
3710
|
-
"response_body",
|
|
3711
|
-
"rawprompt",
|
|
3712
|
-
"raw_prompt",
|
|
3713
|
-
"rawoutput",
|
|
3714
|
-
"raw_output",
|
|
3715
|
-
"toolinput",
|
|
3716
|
-
"tool_input",
|
|
3717
|
-
"tooloutput",
|
|
3718
|
-
"tool_output",
|
|
3719
|
-
// Framework / agent metadata that carries user or task text
|
|
3720
|
-
"currenttask",
|
|
3721
|
-
"current_task",
|
|
3722
|
-
"task",
|
|
3723
|
-
"userinput",
|
|
3724
|
-
"user_input",
|
|
3725
|
-
"requesttext",
|
|
3726
|
-
"request_text",
|
|
3727
|
-
"conversationtext",
|
|
3728
|
-
"conversation_text"
|
|
3729
|
-
];
|
|
3730
|
-
var DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES = [
|
|
3731
|
-
"tokenUsage",
|
|
3732
|
-
"usage",
|
|
3733
|
-
"tokens"
|
|
3734
|
-
];
|
|
3735
|
-
var SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
|
|
3736
|
-
"input",
|
|
3737
|
-
"output",
|
|
3738
|
-
"total",
|
|
3739
|
-
"cached",
|
|
3740
|
-
"input_tokens",
|
|
3741
|
-
"inputtokens",
|
|
3742
|
-
"output_tokens",
|
|
3743
|
-
"outputtokens",
|
|
3744
|
-
"total_tokens",
|
|
3745
|
-
"totaltokens",
|
|
3746
|
-
"prompt_tokens",
|
|
3747
|
-
"prompttokens",
|
|
3748
|
-
"completion_tokens",
|
|
3749
|
-
"completiontokens"
|
|
3750
|
-
]);
|
|
3751
|
-
var DEFAULT_SECRET_PATTERNS = [
|
|
3752
|
-
{ id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
|
|
3753
|
-
{ id: "openai-key", pattern: /sk-[A-Za-z0-9_-]{16,}/ },
|
|
3754
|
-
{ id: "aws-access-key", pattern: /AKIA[0-9A-Z]{16}/ },
|
|
3755
|
-
{ id: "github-token", pattern: /gh[opsu]_[A-Za-z0-9_]{20,}/ },
|
|
3756
|
-
// Keep in sync with packages/redact/src/key-value-secret.ts (KEY_VALUE_SECRET_PATTERN_SOURCE).
|
|
3757
|
-
{
|
|
3758
|
-
id: "key-value-secret",
|
|
3759
|
-
pattern: /\b(?:api[_-]?key|internal[_-]?token|access[_-]?token|auth[_-]?token|password|secret|token)=([^\s"'\\]{8,})/i
|
|
3760
|
-
}
|
|
3761
|
-
];
|
|
3762
|
-
function compareStrings(a, b) {
|
|
3763
|
-
return (a ?? "").localeCompare(b ?? "");
|
|
3641
|
+
function limitFindings(findings, maxFindings) {
|
|
3642
|
+
if (maxFindings === void 0 || findings.length <= maxFindings) return findings;
|
|
3643
|
+
return findings.slice(0, Math.max(0, maxFindings));
|
|
3764
3644
|
}
|
|
3765
|
-
function
|
|
3766
|
-
return
|
|
3767
|
-
code,
|
|
3768
|
-
message,
|
|
3769
|
-
severity: "error",
|
|
3770
|
-
...ruleId ? { ruleId } : {}
|
|
3771
|
-
};
|
|
3645
|
+
function hasRedactionMarker(value, markers) {
|
|
3646
|
+
return markers.some((marker) => value.includes(marker)) || /^\[HASH:[A-Za-z0-9_-]+\]$/.test(value);
|
|
3772
3647
|
}
|
|
3773
|
-
function
|
|
3774
|
-
return
|
|
3775
|
-
passed: 0,
|
|
3776
|
-
failed: 0,
|
|
3777
|
-
warnings: 0,
|
|
3778
|
-
errors: 0,
|
|
3779
|
-
rulesEvaluated: 0
|
|
3780
|
-
};
|
|
3648
|
+
function isSensitiveKey(key, sensitiveKeys) {
|
|
3649
|
+
return isCredentialSensitiveKey(key, sensitiveKeys);
|
|
3781
3650
|
}
|
|
3782
|
-
function
|
|
3783
|
-
return
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
format: input.read.format,
|
|
3787
|
-
...selectedRun ? { runId: selectedRun.runId } : {},
|
|
3788
|
-
summary: {
|
|
3789
|
-
...emptySummary(),
|
|
3790
|
-
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
3791
|
-
rulesEvaluated: ruleExecutions.length
|
|
3792
|
-
},
|
|
3793
|
-
findings: [],
|
|
3794
|
-
diagnostics: [...diagnostics],
|
|
3795
|
-
ruleExecutions: [...ruleExecutions]
|
|
3796
|
-
};
|
|
3651
|
+
function isRawContentKey(key, forbiddenKeys) {
|
|
3652
|
+
if (!key) return false;
|
|
3653
|
+
const normalized = normalizedKey(key);
|
|
3654
|
+
return forbiddenKeys.some((forbidden) => normalized === normalizedKey(forbidden));
|
|
3797
3655
|
}
|
|
3798
|
-
function
|
|
3799
|
-
|
|
3656
|
+
function isSafeRawContentMetricPath(path16, key, safePathPrefixes) {
|
|
3657
|
+
const leaf = normalizedKey(key ?? lastPathSegment(path16));
|
|
3658
|
+
if (!SAFE_USAGE_LEAF_KEYS.has(leaf)) return false;
|
|
3659
|
+
const parts = path16.split(".").filter(Boolean);
|
|
3660
|
+
if (parts.length < 2) return false;
|
|
3661
|
+
const parent = parts[parts.length - 2] ?? "";
|
|
3662
|
+
const parentNorm = normalizedKey(parent);
|
|
3663
|
+
return safePathPrefixes.some((prefix) => parentNorm === normalizedKey(prefix));
|
|
3800
3664
|
}
|
|
3801
|
-
function
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
const scopedEvents = selectedRun === void 0 ? input.read.events : input.read.events.filter((event) => scopedRunIds.has(event.runId));
|
|
3805
|
-
const nodes = flattenNodes(scopedRuns.flatMap((run) => run.children));
|
|
3806
|
-
const nodesByEventId = /* @__PURE__ */ new Map();
|
|
3807
|
-
const childrenByParentId = /* @__PURE__ */ new Map();
|
|
3808
|
-
for (const node of nodes) {
|
|
3809
|
-
nodesByEventId.set(node.event.eventId, node);
|
|
3810
|
-
const parentId = node.event.parentId;
|
|
3811
|
-
if (parentId) {
|
|
3812
|
-
const children = childrenByParentId.get(parentId) ?? [];
|
|
3813
|
-
children.push(node);
|
|
3814
|
-
childrenByParentId.set(parentId, children);
|
|
3815
|
-
}
|
|
3816
|
-
}
|
|
3817
|
-
const projection = projectLogicalEvents(scopedEvents);
|
|
3818
|
-
return {
|
|
3819
|
-
format: input.read.format,
|
|
3820
|
-
runs: Object.freeze([...input.read.runs]),
|
|
3821
|
-
events: Object.freeze([...scopedEvents]),
|
|
3822
|
-
logicalEvents: projection.logicalEvents,
|
|
3823
|
-
logicalProjectionDiagnostics: projection.diagnostics,
|
|
3824
|
-
readerWarnings: Object.freeze([...input.read.warnings]),
|
|
3825
|
-
unsupportedFields: Object.freeze([...input.read.unsupportedFields]),
|
|
3826
|
-
sourceFiles: Object.freeze([...input.read.sourceFiles]),
|
|
3827
|
-
nodesByEventId,
|
|
3828
|
-
childrenByParentId,
|
|
3829
|
-
rootNodes: Object.freeze(scopedRuns.flatMap((run) => run.children))
|
|
3830
|
-
};
|
|
3665
|
+
function isRawContentPath(path16, key, forbiddenKeys, safePathPrefixes) {
|
|
3666
|
+
if (isSafeRawContentMetricPath(path16, key, safePathPrefixes)) return false;
|
|
3667
|
+
return isRawContentKey(key ?? lastPathSegment(path16), forbiddenKeys);
|
|
3831
3668
|
}
|
|
3832
|
-
function
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3669
|
+
function createRunStatusRule(options = {}) {
|
|
3670
|
+
const expected = options.expected ?? "ok";
|
|
3671
|
+
const allowIncomplete = options.allowIncomplete === true;
|
|
3672
|
+
return {
|
|
3673
|
+
id: "run.status",
|
|
3674
|
+
category: "run",
|
|
3675
|
+
defaultSeverity: "error",
|
|
3676
|
+
evaluate(context) {
|
|
3677
|
+
const findings = [];
|
|
3678
|
+
const actual = context.selectedRun?.status ?? "unknown";
|
|
3679
|
+
if (actual !== expected) {
|
|
3680
|
+
findings.push(
|
|
3681
|
+
failFinding(
|
|
3682
|
+
"run.status",
|
|
3683
|
+
`Run status ${actual} did not match expected ${expected}.`,
|
|
3684
|
+
runEvidence(context.selectedRun),
|
|
3685
|
+
expected,
|
|
3686
|
+
actual
|
|
3840
3687
|
)
|
|
3841
|
-
|
|
3842
|
-
}
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
return { run: input.read.runs[0], diagnostics: [] };
|
|
3859
|
-
}
|
|
3860
|
-
if (input.read.runs.length === 0) {
|
|
3861
|
-
return {
|
|
3862
|
-
diagnostics: [
|
|
3863
|
-
diagnostic(
|
|
3864
|
-
"AI_CHECK_RUN_SELECTION_REQUIRED",
|
|
3865
|
-
formatProgrammaticDiagnostic(
|
|
3866
|
-
"AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED",
|
|
3867
|
-
"No runs are available for checks."
|
|
3868
|
-
)
|
|
3869
|
-
)
|
|
3870
|
-
]
|
|
3871
|
-
};
|
|
3872
|
-
}
|
|
3873
|
-
return {
|
|
3874
|
-
diagnostics: [
|
|
3875
|
-
diagnostic(
|
|
3876
|
-
"AI_CHECK_RUN_SELECTION_REQUIRED",
|
|
3877
|
-
formatProgrammaticDiagnostic("AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED")
|
|
3878
|
-
)
|
|
3879
|
-
]
|
|
3880
|
-
};
|
|
3881
|
-
}
|
|
3882
|
-
function selectRules(rules, selectedIds) {
|
|
3883
|
-
const diagnostics = [];
|
|
3884
|
-
const byId = /* @__PURE__ */ new Map();
|
|
3885
|
-
for (const rule of rules) {
|
|
3886
|
-
if (byId.has(rule.id)) {
|
|
3887
|
-
diagnostics.push(
|
|
3888
|
-
diagnostic("AI_CHECK_INVALID_CONFIG", `Duplicate trace check rule id: ${rule.id}.`, rule.id)
|
|
3889
|
-
);
|
|
3890
|
-
continue;
|
|
3891
|
-
}
|
|
3892
|
-
byId.set(rule.id, rule);
|
|
3893
|
-
}
|
|
3894
|
-
if (selectedIds && selectedIds.length > 0) {
|
|
3895
|
-
const selected = new Set(selectedIds);
|
|
3896
|
-
for (const id of selected) {
|
|
3897
|
-
if (!byId.has(id)) {
|
|
3898
|
-
diagnostics.push(
|
|
3899
|
-
diagnostic("AI_CHECK_INVALID_CONFIG", `Unknown trace check rule id: ${id}.`, id)
|
|
3900
|
-
);
|
|
3901
|
-
}
|
|
3902
|
-
}
|
|
3903
|
-
return {
|
|
3904
|
-
rules: [...byId.values()].filter((rule) => selected.has(rule.id)).sort(compareRules),
|
|
3905
|
-
diagnostics
|
|
3906
|
-
};
|
|
3907
|
-
}
|
|
3908
|
-
return { rules: [...byId.values()].sort(compareRules), diagnostics };
|
|
3909
|
-
}
|
|
3910
|
-
function compareRules(a, b) {
|
|
3911
|
-
return a.id.localeCompare(b.id);
|
|
3912
|
-
}
|
|
3913
|
-
function eventTimestamp(finding, eventById) {
|
|
3914
|
-
const eventId = finding.evidence[0]?.eventId;
|
|
3915
|
-
return eventId ? eventById.get(eventId)?.timestamp ?? "" : "";
|
|
3916
|
-
}
|
|
3917
|
-
function compareFindings(eventById) {
|
|
3918
|
-
return (a, b) => {
|
|
3919
|
-
if (SEVERITY_RANK[a.severity] !== SEVERITY_RANK[b.severity]) {
|
|
3920
|
-
return SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
|
|
3921
|
-
}
|
|
3922
|
-
const byRule = a.ruleId.localeCompare(b.ruleId);
|
|
3923
|
-
if (byRule !== 0) return byRule;
|
|
3924
|
-
if (STATUS_RANK[a.status] !== STATUS_RANK[b.status]) {
|
|
3925
|
-
return STATUS_RANK[a.status] - STATUS_RANK[b.status];
|
|
3926
|
-
}
|
|
3927
|
-
const byRun = compareStrings(a.evidence[0]?.runId, b.evidence[0]?.runId);
|
|
3928
|
-
if (byRun !== 0) return byRun;
|
|
3929
|
-
const byTime = eventTimestamp(a, eventById).localeCompare(eventTimestamp(b, eventById));
|
|
3930
|
-
if (byTime !== 0) return byTime;
|
|
3931
|
-
const byEvent = compareStrings(a.evidence[0]?.eventId, b.evidence[0]?.eventId);
|
|
3932
|
-
if (byEvent !== 0) return byEvent;
|
|
3933
|
-
return compareStrings(a.evidence[0]?.path, b.evidence[0]?.path);
|
|
3934
|
-
};
|
|
3935
|
-
}
|
|
3936
|
-
function normalizeFinding(rule, finding) {
|
|
3937
|
-
return {
|
|
3938
|
-
ruleId: finding.ruleId || rule.id,
|
|
3939
|
-
severity: finding.severity ?? rule.defaultSeverity,
|
|
3940
|
-
status: finding.status,
|
|
3941
|
-
message: finding.message,
|
|
3942
|
-
...finding.expected !== void 0 ? { expected: finding.expected } : {},
|
|
3943
|
-
...finding.actual !== void 0 ? { actual: finding.actual } : {},
|
|
3944
|
-
evidence: [...finding.evidence ?? []],
|
|
3945
|
-
...finding.category !== void 0 ? { category: finding.category } : {},
|
|
3946
|
-
...finding.confidence !== void 0 ? { confidence: finding.confidence } : {},
|
|
3947
|
-
...finding.detector !== void 0 ? { detector: finding.detector } : {},
|
|
3948
|
-
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
3949
|
-
};
|
|
3950
|
-
}
|
|
3951
|
-
function summarize(findings, diagnostics, rulesEvaluated) {
|
|
3952
|
-
return {
|
|
3953
|
-
passed: findings.filter((finding) => finding.status === "pass").length,
|
|
3954
|
-
failed: findings.filter(
|
|
3955
|
-
(finding) => finding.status === "fail" && finding.severity === "error"
|
|
3956
|
-
).length,
|
|
3957
|
-
warnings: findings.filter(
|
|
3958
|
-
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
3959
|
-
).length,
|
|
3960
|
-
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
3961
|
-
rulesEvaluated
|
|
3962
|
-
};
|
|
3963
|
-
}
|
|
3964
|
-
function classifyRuleExecution(findings, threw) {
|
|
3965
|
-
if (findings.some((finding) => finding.status === "fail" && finding.severity === "error")) {
|
|
3966
|
-
return "fail";
|
|
3967
|
-
}
|
|
3968
|
-
if (findings.some(
|
|
3969
|
-
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
3970
|
-
)) {
|
|
3971
|
-
return "warning";
|
|
3972
|
-
}
|
|
3973
|
-
return "pass";
|
|
3974
|
-
}
|
|
3975
|
-
function eventEvidence(event, path16) {
|
|
3976
|
-
return {
|
|
3977
|
-
runId: event.runId,
|
|
3978
|
-
eventId: event.eventId,
|
|
3979
|
-
parentId: event.parentId,
|
|
3980
|
-
traceId: event.trace?.traceId,
|
|
3981
|
-
spanId: event.trace?.spanId,
|
|
3982
|
-
kind: event.kind,
|
|
3983
|
-
name: event.name,
|
|
3984
|
-
status: event.status,
|
|
3985
|
-
...path16 ? { path: path16 } : {}
|
|
3986
|
-
};
|
|
3987
|
-
}
|
|
3988
|
-
function runEvidence(run) {
|
|
3989
|
-
return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
|
|
3990
|
-
}
|
|
3991
|
-
function failFinding(ruleId, message, evidence, expected, actual, meta) {
|
|
3992
|
-
return {
|
|
3993
|
-
ruleId,
|
|
3994
|
-
severity: meta?.severity ?? "error",
|
|
3995
|
-
status: meta?.status ?? "fail",
|
|
3996
|
-
message,
|
|
3997
|
-
...expected !== void 0 ? { expected } : {},
|
|
3998
|
-
...actual !== void 0 ? { actual } : {},
|
|
3999
|
-
evidence: [...evidence],
|
|
4000
|
-
...meta?.category !== void 0 ? { category: meta.category } : {},
|
|
4001
|
-
...meta?.confidence !== void 0 ? { confidence: meta.confidence } : {},
|
|
4002
|
-
...meta?.detector !== void 0 ? { detector: meta.detector } : {},
|
|
4003
|
-
...meta?.action !== void 0 ? { action: meta.action } : {}
|
|
4004
|
-
};
|
|
4005
|
-
}
|
|
4006
|
-
function semanticEvents(context) {
|
|
4007
|
-
return context.logicalEvents ?? context.events;
|
|
4008
|
-
}
|
|
4009
|
-
function isRecord8(value) {
|
|
4010
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4011
|
-
}
|
|
4012
|
-
function normalizedKey(value) {
|
|
4013
|
-
return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
|
|
4014
|
-
}
|
|
4015
|
-
function lastPathSegment(path16) {
|
|
4016
|
-
const parts = path16.split(".");
|
|
4017
|
-
return parts[parts.length - 1] ?? path16;
|
|
4018
|
-
}
|
|
4019
|
-
function valueType(value) {
|
|
4020
|
-
if (Array.isArray(value)) return "array";
|
|
4021
|
-
if (value === null) return "null";
|
|
4022
|
-
return typeof value;
|
|
4023
|
-
}
|
|
4024
|
-
function serializedByteLength(value) {
|
|
4025
|
-
try {
|
|
4026
|
-
return Buffer.byteLength(JSON.stringify(value), "utf-8");
|
|
4027
|
-
} catch {
|
|
4028
|
-
return void 0;
|
|
4029
|
-
}
|
|
4030
|
-
}
|
|
4031
|
-
function pushValueEntries(entries, event, value, path16, key, depth = 0) {
|
|
4032
|
-
entries.push({ event, path: path16, key, value });
|
|
4033
|
-
if (depth >= 8) return;
|
|
4034
|
-
if (Array.isArray(value)) {
|
|
4035
|
-
for (const [index, item] of value.entries()) {
|
|
4036
|
-
pushValueEntries(entries, event, item, `${path16}.${index}`, String(index), depth + 1);
|
|
4037
|
-
}
|
|
4038
|
-
return;
|
|
4039
|
-
}
|
|
4040
|
-
if (!isRecord8(value)) return;
|
|
4041
|
-
for (const nestedKey of Object.keys(value).sort((a, b) => a.localeCompare(b))) {
|
|
4042
|
-
pushValueEntries(
|
|
4043
|
-
entries,
|
|
4044
|
-
event,
|
|
4045
|
-
value[nestedKey],
|
|
4046
|
-
`${path16}.${nestedKey}`,
|
|
4047
|
-
nestedKey,
|
|
4048
|
-
depth + 1
|
|
4049
|
-
);
|
|
4050
|
-
}
|
|
4051
|
-
}
|
|
4052
|
-
function eventValueEntries(event, options = {}) {
|
|
4053
|
-
const entries = [];
|
|
4054
|
-
if (event.attributes !== void 0) {
|
|
4055
|
-
pushValueEntries(entries, event, event.attributes, "attributes", "attributes");
|
|
4056
|
-
}
|
|
4057
|
-
if (options.includeSummaries) {
|
|
4058
|
-
if (event.inputSummary !== void 0) {
|
|
4059
|
-
pushValueEntries(entries, event, event.inputSummary, "inputSummary", "inputSummary");
|
|
4060
|
-
}
|
|
4061
|
-
if (event.outputSummary !== void 0) {
|
|
4062
|
-
pushValueEntries(entries, event, event.outputSummary, "outputSummary", "outputSummary");
|
|
4063
|
-
}
|
|
4064
|
-
}
|
|
4065
|
-
if (options.includeError && event.error !== void 0) {
|
|
4066
|
-
pushValueEntries(entries, event, event.error, "error", "error");
|
|
4067
|
-
}
|
|
4068
|
-
return entries;
|
|
4069
|
-
}
|
|
4070
|
-
function limitFindings(findings, maxFindings) {
|
|
4071
|
-
if (maxFindings === void 0 || findings.length <= maxFindings) return findings;
|
|
4072
|
-
return findings.slice(0, Math.max(0, maxFindings));
|
|
4073
|
-
}
|
|
4074
|
-
function hasRedactionMarker(value, markers) {
|
|
4075
|
-
return markers.some((marker) => value.includes(marker)) || /^\[HASH:[A-Za-z0-9_-]+\]$/.test(value);
|
|
4076
|
-
}
|
|
4077
|
-
function isSensitiveKey(key, sensitiveKeys) {
|
|
4078
|
-
return isCredentialSensitiveKey(key, sensitiveKeys);
|
|
4079
|
-
}
|
|
4080
|
-
function isRawContentKey(key, forbiddenKeys) {
|
|
4081
|
-
if (!key) return false;
|
|
4082
|
-
const normalized = normalizedKey(key);
|
|
4083
|
-
return forbiddenKeys.some((forbidden) => normalized === normalizedKey(forbidden));
|
|
4084
|
-
}
|
|
4085
|
-
function isSafeRawContentMetricPath(path16, key, safePathPrefixes) {
|
|
4086
|
-
const leaf = normalizedKey(key ?? lastPathSegment(path16));
|
|
4087
|
-
if (!SAFE_USAGE_LEAF_KEYS.has(leaf)) return false;
|
|
4088
|
-
const parts = path16.split(".").filter(Boolean);
|
|
4089
|
-
if (parts.length < 2) return false;
|
|
4090
|
-
const parent = parts[parts.length - 2] ?? "";
|
|
4091
|
-
const parentNorm = normalizedKey(parent);
|
|
4092
|
-
return safePathPrefixes.some((prefix) => parentNorm === normalizedKey(prefix));
|
|
4093
|
-
}
|
|
4094
|
-
function isRawContentPath(path16, key, forbiddenKeys, safePathPrefixes) {
|
|
4095
|
-
if (isSafeRawContentMetricPath(path16, key, safePathPrefixes)) return false;
|
|
4096
|
-
return isRawContentKey(key ?? lastPathSegment(path16), forbiddenKeys);
|
|
4097
|
-
}
|
|
4098
|
-
function createRunStatusRule(options = {}) {
|
|
4099
|
-
const expected = options.expected ?? "ok";
|
|
4100
|
-
const allowIncomplete = options.allowIncomplete === true;
|
|
4101
|
-
return {
|
|
4102
|
-
id: "run.status",
|
|
4103
|
-
category: "run",
|
|
4104
|
-
defaultSeverity: "error",
|
|
4105
|
-
evaluate(context) {
|
|
4106
|
-
const findings = [];
|
|
4107
|
-
const actual = context.selectedRun?.status ?? "unknown";
|
|
4108
|
-
if (actual !== expected) {
|
|
4109
|
-
findings.push(
|
|
4110
|
-
failFinding(
|
|
4111
|
-
"run.status",
|
|
4112
|
-
`Run status ${actual} did not match expected ${expected}.`,
|
|
4113
|
-
runEvidence(context.selectedRun),
|
|
4114
|
-
expected,
|
|
4115
|
-
actual
|
|
4116
|
-
)
|
|
4117
|
-
);
|
|
4118
|
-
}
|
|
4119
|
-
if (!allowIncomplete) {
|
|
4120
|
-
const running = semanticEvents(context).filter((event) => event.status === "running");
|
|
4121
|
-
if (running.length > 0) {
|
|
4122
|
-
findings.push(
|
|
4123
|
-
failFinding(
|
|
4124
|
-
"run.status",
|
|
4125
|
-
"Run contains incomplete running events.",
|
|
4126
|
-
running.map((event) => eventEvidence(event)),
|
|
4127
|
-
"no running events",
|
|
4128
|
-
running.length
|
|
4129
|
-
)
|
|
4130
|
-
);
|
|
4131
|
-
}
|
|
4132
|
-
}
|
|
4133
|
-
return findings;
|
|
3688
|
+
);
|
|
3689
|
+
}
|
|
3690
|
+
if (!allowIncomplete) {
|
|
3691
|
+
const running = semanticEvents(context).filter((event) => event.status === "running");
|
|
3692
|
+
if (running.length > 0) {
|
|
3693
|
+
findings.push(
|
|
3694
|
+
failFinding(
|
|
3695
|
+
"run.status",
|
|
3696
|
+
"Run contains incomplete running events.",
|
|
3697
|
+
running.map((event) => eventEvidence(event)),
|
|
3698
|
+
"no running events",
|
|
3699
|
+
running.length
|
|
3700
|
+
)
|
|
3701
|
+
);
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
return findings;
|
|
4134
3705
|
}
|
|
4135
3706
|
};
|
|
4136
3707
|
}
|
|
@@ -4240,168 +3811,638 @@ function createSafetySecretPatternRule(options = {}) {
|
|
|
4240
3811
|
}
|
|
4241
3812
|
return limitFindings(findings, options.maxFindings);
|
|
4242
3813
|
}
|
|
4243
|
-
};
|
|
3814
|
+
};
|
|
3815
|
+
}
|
|
3816
|
+
function createSafetyOversizedAttributeRule(options) {
|
|
3817
|
+
return {
|
|
3818
|
+
id: "safety.oversizedAttribute",
|
|
3819
|
+
category: "safety",
|
|
3820
|
+
defaultSeverity: "error",
|
|
3821
|
+
evaluate(context) {
|
|
3822
|
+
const findings = [];
|
|
3823
|
+
for (const event of context.events) {
|
|
3824
|
+
for (const entry of eventValueEntries(event, { includeSummaries: true, includeError: true })) {
|
|
3825
|
+
if (typeof entry.value === "string" && options.maxStringLength !== void 0 && entry.value.length > options.maxStringLength) {
|
|
3826
|
+
findings.push(
|
|
3827
|
+
failFinding(
|
|
3828
|
+
"safety.oversizedAttribute",
|
|
3829
|
+
`String at ${entry.path} exceeds ${options.maxStringLength} characters.`,
|
|
3830
|
+
[eventEvidence(event, entry.path)],
|
|
3831
|
+
{ maxStringLength: options.maxStringLength },
|
|
3832
|
+
{ path: entry.path, length: entry.value.length },
|
|
3833
|
+
{
|
|
3834
|
+
category: "size",
|
|
3835
|
+
confidence: "high",
|
|
3836
|
+
detector: "safety.oversizedAttribute",
|
|
3837
|
+
action: "truncate-or-omit"
|
|
3838
|
+
}
|
|
3839
|
+
)
|
|
3840
|
+
);
|
|
3841
|
+
}
|
|
3842
|
+
if (Array.isArray(entry.value) && options.maxArrayLength !== void 0 && entry.value.length > options.maxArrayLength) {
|
|
3843
|
+
findings.push(
|
|
3844
|
+
failFinding(
|
|
3845
|
+
"safety.oversizedAttribute",
|
|
3846
|
+
`Array at ${entry.path} exceeds ${options.maxArrayLength} items.`,
|
|
3847
|
+
[eventEvidence(event, entry.path)],
|
|
3848
|
+
{ maxArrayLength: options.maxArrayLength },
|
|
3849
|
+
{ path: entry.path, length: entry.value.length },
|
|
3850
|
+
{
|
|
3851
|
+
category: "size",
|
|
3852
|
+
confidence: "high",
|
|
3853
|
+
detector: "safety.oversizedAttribute",
|
|
3854
|
+
action: "truncate-or-omit"
|
|
3855
|
+
}
|
|
3856
|
+
)
|
|
3857
|
+
);
|
|
3858
|
+
}
|
|
3859
|
+
if (isRecord8(entry.value) && options.maxObjectKeys !== void 0 && Object.keys(entry.value).length > options.maxObjectKeys) {
|
|
3860
|
+
findings.push(
|
|
3861
|
+
failFinding(
|
|
3862
|
+
"safety.oversizedAttribute",
|
|
3863
|
+
`Object at ${entry.path} exceeds ${options.maxObjectKeys} keys.`,
|
|
3864
|
+
[eventEvidence(event, entry.path)],
|
|
3865
|
+
{ maxObjectKeys: options.maxObjectKeys },
|
|
3866
|
+
{ path: entry.path, keys: Object.keys(entry.value).length },
|
|
3867
|
+
{
|
|
3868
|
+
category: "size",
|
|
3869
|
+
confidence: "high",
|
|
3870
|
+
detector: "safety.oversizedAttribute",
|
|
3871
|
+
action: "truncate-or-omit"
|
|
3872
|
+
}
|
|
3873
|
+
)
|
|
3874
|
+
);
|
|
3875
|
+
}
|
|
3876
|
+
if (options.maxSerializedBytes !== void 0) {
|
|
3877
|
+
const bytes = serializedByteLength(entry.value);
|
|
3878
|
+
if (bytes !== void 0 && bytes > options.maxSerializedBytes) {
|
|
3879
|
+
findings.push(
|
|
3880
|
+
failFinding(
|
|
3881
|
+
"safety.oversizedAttribute",
|
|
3882
|
+
`Value at ${entry.path} exceeds ${options.maxSerializedBytes} serialized bytes.`,
|
|
3883
|
+
[eventEvidence(event, entry.path)],
|
|
3884
|
+
{ maxSerializedBytes: options.maxSerializedBytes },
|
|
3885
|
+
{ path: entry.path, bytes },
|
|
3886
|
+
{
|
|
3887
|
+
category: "size",
|
|
3888
|
+
confidence: "high",
|
|
3889
|
+
detector: "safety.oversizedAttribute",
|
|
3890
|
+
action: "truncate-or-omit"
|
|
3891
|
+
}
|
|
3892
|
+
)
|
|
3893
|
+
);
|
|
3894
|
+
}
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
return limitFindings(findings, options.maxFindings);
|
|
3899
|
+
}
|
|
3900
|
+
};
|
|
3901
|
+
}
|
|
3902
|
+
function runTraceChecks(input, options = {}) {
|
|
3903
|
+
const selected = resolveSelectedRun(input, options.runId);
|
|
3904
|
+
if (selected.diagnostics.length > 0) {
|
|
3905
|
+
return errorResult(input, selected.diagnostics, selected.run);
|
|
3906
|
+
}
|
|
3907
|
+
const rules = selectRules(options.rules ?? [], options.select);
|
|
3908
|
+
if (rules.diagnostics.length > 0) {
|
|
3909
|
+
return errorResult(input, rules.diagnostics, selected.run);
|
|
3910
|
+
}
|
|
3911
|
+
if (rules.rules.length === 0) {
|
|
3912
|
+
return errorResult(
|
|
3913
|
+
input,
|
|
3914
|
+
[
|
|
3915
|
+
diagnostic(
|
|
3916
|
+
"AI_CHECK_NO_RULES_EVALUATED",
|
|
3917
|
+
"No trace check rules were evaluated. Configure at least one rule, contract, or CLI check option."
|
|
3918
|
+
)
|
|
3919
|
+
],
|
|
3920
|
+
selected.run
|
|
3921
|
+
);
|
|
3922
|
+
}
|
|
3923
|
+
const facts = buildFacts(input, selected.run);
|
|
3924
|
+
const context = {
|
|
3925
|
+
...facts,
|
|
3926
|
+
...selected.run ? { selectedRun: selected.run } : {},
|
|
3927
|
+
...input.sourceLabel ? { sourceLabel: input.sourceLabel } : {}
|
|
3928
|
+
};
|
|
3929
|
+
const diagnostics = [];
|
|
3930
|
+
const findings = [];
|
|
3931
|
+
const ruleExecutions = [];
|
|
3932
|
+
for (const rule of rules.rules) {
|
|
3933
|
+
try {
|
|
3934
|
+
const ruleFindings = rule.evaluate(context).map((finding) => normalizeFinding(rule, finding));
|
|
3935
|
+
findings.push(...ruleFindings);
|
|
3936
|
+
ruleExecutions.push({
|
|
3937
|
+
ruleId: rule.id,
|
|
3938
|
+
category: rule.category,
|
|
3939
|
+
status: classifyRuleExecution(ruleFindings, false),
|
|
3940
|
+
findingCount: ruleFindings.length,
|
|
3941
|
+
...selected.run ? { runId: selected.run.runId } : {}
|
|
3942
|
+
});
|
|
3943
|
+
} catch (error) {
|
|
3944
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3945
|
+
diagnostics.push(
|
|
3946
|
+
diagnostic("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
|
|
3947
|
+
);
|
|
3948
|
+
ruleExecutions.push({
|
|
3949
|
+
ruleId: rule.id,
|
|
3950
|
+
category: rule.category,
|
|
3951
|
+
status: "error",
|
|
3952
|
+
findingCount: 0,
|
|
3953
|
+
...selected.run ? { runId: selected.run.runId } : {}
|
|
3954
|
+
});
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
if (diagnostics.length > 0) {
|
|
3958
|
+
return errorResult(input, diagnostics, selected.run, ruleExecutions);
|
|
3959
|
+
}
|
|
3960
|
+
const eventById = new Map(input.read.events.map((event) => [event.eventId, event]));
|
|
3961
|
+
const sortedFindings = findings.sort(compareFindings(eventById));
|
|
3962
|
+
const summary = summarize(sortedFindings, diagnostics, ruleExecutions.length);
|
|
3963
|
+
const status = summary.failed > 0 ? "fail" : "pass";
|
|
3964
|
+
return {
|
|
3965
|
+
ok: status === "pass",
|
|
3966
|
+
status,
|
|
3967
|
+
format: input.read.format,
|
|
3968
|
+
...selected.run ? { runId: selected.run.runId } : {},
|
|
3969
|
+
summary,
|
|
3970
|
+
findings: sortedFindings,
|
|
3971
|
+
diagnostics,
|
|
3972
|
+
ruleExecutions
|
|
3973
|
+
};
|
|
3974
|
+
}
|
|
3975
|
+
|
|
3976
|
+
// packages/core/src/checks/contract.ts
|
|
3977
|
+
new Set(OBSERVED_OUTCOME_METHODS);
|
|
3978
|
+
|
|
3979
|
+
// packages/core/src/exporters/helpers.ts
|
|
3980
|
+
var REDACT_SUBSTRINGS = [
|
|
3981
|
+
"authorization",
|
|
3982
|
+
"cookie",
|
|
3983
|
+
"token",
|
|
3984
|
+
"apikey",
|
|
3985
|
+
"password",
|
|
3986
|
+
"secret",
|
|
3987
|
+
"email"
|
|
3988
|
+
];
|
|
3989
|
+
function shouldRedactKey(key) {
|
|
3990
|
+
const k = key.toLowerCase();
|
|
3991
|
+
for (const s of REDACT_SUBSTRINGS) {
|
|
3992
|
+
if (k.includes(s)) return true;
|
|
3993
|
+
}
|
|
3994
|
+
return false;
|
|
3995
|
+
}
|
|
3996
|
+
function safeString(value, maxLength) {
|
|
3997
|
+
if (value === null || value === void 0) return "";
|
|
3998
|
+
let s;
|
|
3999
|
+
if (typeof value === "string") s = value;
|
|
4000
|
+
else if (typeof value === "number" || typeof value === "boolean") s = String(value);
|
|
4001
|
+
else s = stableJson(value, false);
|
|
4002
|
+
if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
|
|
4003
|
+
return `${s.slice(0, maxLength)}\u2026`;
|
|
4004
|
+
}
|
|
4005
|
+
return s;
|
|
4006
|
+
}
|
|
4007
|
+
function escapeMarkdown(value) {
|
|
4008
|
+
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
4009
|
+
}
|
|
4010
|
+
function escapeHtml(value) {
|
|
4011
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4012
|
+
}
|
|
4013
|
+
function sortKeysDeep(input) {
|
|
4014
|
+
if (input === null || typeof input !== "object") return input;
|
|
4015
|
+
if (Array.isArray(input)) return input.map(sortKeysDeep);
|
|
4016
|
+
const o = input;
|
|
4017
|
+
const out = {};
|
|
4018
|
+
for (const k of Object.keys(o).sort()) {
|
|
4019
|
+
out[k] = sortKeysDeep(o[k]);
|
|
4020
|
+
}
|
|
4021
|
+
return out;
|
|
4022
|
+
}
|
|
4023
|
+
function stableJson(value, pretty) {
|
|
4024
|
+
const sorted = sortKeysDeep(value);
|
|
4025
|
+
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
4026
|
+
}
|
|
4027
|
+
function compactAttributes(attrs2, options) {
|
|
4028
|
+
if (attrs2 === void 0) return {};
|
|
4029
|
+
const maxLen = options?.maxLength ?? 500;
|
|
4030
|
+
const redacted = options?.redacted ?? true;
|
|
4031
|
+
const out = {};
|
|
4032
|
+
for (const key of Object.keys(attrs2).sort()) {
|
|
4033
|
+
if (redacted && shouldRedactKey(key)) {
|
|
4034
|
+
out[key] = "[REDACTED]";
|
|
4035
|
+
continue;
|
|
4036
|
+
}
|
|
4037
|
+
const v = attrs2[key];
|
|
4038
|
+
out[key] = compactValue(v, maxLen, redacted);
|
|
4039
|
+
}
|
|
4040
|
+
return out;
|
|
4041
|
+
}
|
|
4042
|
+
function compactValue(value, maxLen, redacted) {
|
|
4043
|
+
if (value === null || typeof value !== "object") {
|
|
4044
|
+
return typeof value === "string" ? safeString(value, maxLen) : value;
|
|
4045
|
+
}
|
|
4046
|
+
if (Array.isArray(value)) {
|
|
4047
|
+
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
|
|
4048
|
+
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
4049
|
+
return arr;
|
|
4050
|
+
}
|
|
4051
|
+
const o = value;
|
|
4052
|
+
const inner = {};
|
|
4053
|
+
for (const k of Object.keys(o)) {
|
|
4054
|
+
if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
4055
|
+
else inner[k] = compactValue(o[k], maxLen, redacted);
|
|
4056
|
+
}
|
|
4057
|
+
return inner;
|
|
4058
|
+
}
|
|
4059
|
+
function flattenTree(tree) {
|
|
4060
|
+
const out = [];
|
|
4061
|
+
function walk(nodes) {
|
|
4062
|
+
for (const n of nodes) {
|
|
4063
|
+
out.push(n);
|
|
4064
|
+
if (n.children.length > 0) walk(n.children);
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
walk(tree.children);
|
|
4068
|
+
return out;
|
|
4069
|
+
}
|
|
4070
|
+
|
|
4071
|
+
// packages/core/src/diff/comparable.ts
|
|
4072
|
+
function extractOutputPreview(meta) {
|
|
4073
|
+
if (meta === void 0) return void 0;
|
|
4074
|
+
if ("outputPreview" in meta) return meta.outputPreview;
|
|
4075
|
+
if ("resultPreview" in meta) return meta.resultPreview;
|
|
4076
|
+
return void 0;
|
|
4077
|
+
}
|
|
4078
|
+
function mapStepStatus(s) {
|
|
4079
|
+
if (s === void 0) return "running";
|
|
4080
|
+
return s;
|
|
4081
|
+
}
|
|
4082
|
+
function manualTraceEventsToComparableRun(events) {
|
|
4083
|
+
const started = events.find((e) => e.event === "run_started");
|
|
4084
|
+
if (!started || started.event !== "run_started") {
|
|
4085
|
+
throw new Error("Invalid trace: missing run_started");
|
|
4086
|
+
}
|
|
4087
|
+
const rs = started;
|
|
4088
|
+
const runId = rs.runId;
|
|
4089
|
+
const completedAll = events.filter((e) => e.event === "run_completed");
|
|
4090
|
+
const lastCompleted = completedAll[completedAll.length - 1];
|
|
4091
|
+
let runStatus;
|
|
4092
|
+
if (lastCompleted === void 0) runStatus = "running";
|
|
4093
|
+
else runStatus = lastCompleted.status;
|
|
4094
|
+
const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
4095
|
+
const steps = /* @__PURE__ */ new Map();
|
|
4096
|
+
let order = 0;
|
|
4097
|
+
for (const e of events) {
|
|
4098
|
+
if (e.event !== "step_started") continue;
|
|
4099
|
+
const s = e;
|
|
4100
|
+
const meta = s.metadata ? { ...s.metadata } : void 0;
|
|
4101
|
+
steps.set(s.stepId, {
|
|
4102
|
+
id: s.stepId,
|
|
4103
|
+
parentId: s.parentId,
|
|
4104
|
+
name: s.name,
|
|
4105
|
+
type: s.type,
|
|
4106
|
+
order: order++,
|
|
4107
|
+
timestamp: s.timestamp,
|
|
4108
|
+
metadata: meta
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
4111
|
+
for (const e of events) {
|
|
4112
|
+
if (e.event !== "step_completed") continue;
|
|
4113
|
+
const acc = steps.get(e.stepId);
|
|
4114
|
+
if (!acc) continue;
|
|
4115
|
+
acc.status = e.status;
|
|
4116
|
+
acc.durationMs = e.durationMs;
|
|
4117
|
+
if (e.error?.message) acc.errorMsg = e.error.message;
|
|
4118
|
+
const extra = e;
|
|
4119
|
+
if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
|
|
4120
|
+
acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
4124
|
+
for (const acc of steps.values()) {
|
|
4125
|
+
let meta = acc.metadata ? { ...acc.metadata } : void 0;
|
|
4126
|
+
if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
|
|
4127
|
+
meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
|
|
4128
|
+
}
|
|
4129
|
+
const outputPreview = extractOutputPreview(meta);
|
|
4130
|
+
if (meta !== void 0 && ("outputPreview" in meta || "resultPreview" in meta)) {
|
|
4131
|
+
delete meta.outputPreview;
|
|
4132
|
+
delete meta.resultPreview;
|
|
4133
|
+
}
|
|
4134
|
+
const sc = {
|
|
4135
|
+
id: acc.id,
|
|
4136
|
+
name: acc.name,
|
|
4137
|
+
type: acc.type,
|
|
4138
|
+
status: mapStepStatus(acc.status),
|
|
4139
|
+
durationMs: acc.durationMs,
|
|
4140
|
+
error: acc.errorMsg,
|
|
4141
|
+
metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
|
|
4142
|
+
outputPreview,
|
|
4143
|
+
children: []
|
|
4144
|
+
};
|
|
4145
|
+
nodes.set(acc.id, sc);
|
|
4146
|
+
}
|
|
4147
|
+
const roots = [];
|
|
4148
|
+
const sortByOrder = (a, b) => {
|
|
4149
|
+
const oa = steps.get(a.id)?.order ?? 0;
|
|
4150
|
+
const ob = steps.get(b.id)?.order ?? 0;
|
|
4151
|
+
return oa - ob;
|
|
4152
|
+
};
|
|
4153
|
+
for (const acc of steps.values()) {
|
|
4154
|
+
const node = nodes.get(acc.id);
|
|
4155
|
+
if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
|
|
4156
|
+
nodes.get(acc.parentId).children.push(node);
|
|
4157
|
+
} else {
|
|
4158
|
+
roots.push(node);
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
roots.sort(sortByOrder);
|
|
4162
|
+
for (const n of nodes.values()) {
|
|
4163
|
+
n.children.sort(sortByOrder);
|
|
4164
|
+
}
|
|
4165
|
+
return {
|
|
4166
|
+
runId,
|
|
4167
|
+
name: rs.name,
|
|
4168
|
+
status: runStatus,
|
|
4169
|
+
durationMs,
|
|
4170
|
+
steps: roots
|
|
4171
|
+
};
|
|
4172
|
+
}
|
|
4173
|
+
|
|
4174
|
+
// packages/core/src/diff/engine.ts
|
|
4175
|
+
var DEFAULT_THRESHOLD_MS = 0;
|
|
4176
|
+
function pathSeg(step, index) {
|
|
4177
|
+
return { index, name: step.name, stepId: step.id };
|
|
4178
|
+
}
|
|
4179
|
+
function buildPath(segments) {
|
|
4180
|
+
return { path: [...segments] };
|
|
4181
|
+
}
|
|
4182
|
+
function pairSteps(left, right) {
|
|
4183
|
+
const usedRight = /* @__PURE__ */ new Set();
|
|
4184
|
+
const pairs = [];
|
|
4185
|
+
for (let i = 0; i < left.length; i++) {
|
|
4186
|
+
const L = left[i];
|
|
4187
|
+
let R = right.find((r) => !usedRight.has(r.id) && r.id === L.id);
|
|
4188
|
+
if (R === void 0 && i < right.length && !usedRight.has(right[i].id)) {
|
|
4189
|
+
const cand = right[i];
|
|
4190
|
+
if (cand.name === L.name && (cand.type ?? "") === (L.type ?? "")) {
|
|
4191
|
+
R = cand;
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
if (R === void 0) {
|
|
4195
|
+
R = right.find(
|
|
4196
|
+
(r) => !usedRight.has(r.id) && r.name === L.name && (r.type ?? "") === (L.type ?? "")
|
|
4197
|
+
);
|
|
4198
|
+
}
|
|
4199
|
+
if (R !== void 0) {
|
|
4200
|
+
usedRight.add(R.id);
|
|
4201
|
+
pairs.push([L, R]);
|
|
4202
|
+
} else {
|
|
4203
|
+
pairs.push([L, void 0]);
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
for (const R of right) {
|
|
4207
|
+
if (!usedRight.has(R.id)) {
|
|
4208
|
+
pairs.push([void 0, R]);
|
|
4209
|
+
}
|
|
4210
|
+
}
|
|
4211
|
+
return pairs;
|
|
4212
|
+
}
|
|
4213
|
+
function compareLeafSteps(L, R, segments, opts, out) {
|
|
4214
|
+
const path16 = buildPath(segments);
|
|
4215
|
+
if (L.name !== R.name) {
|
|
4216
|
+
out.push({
|
|
4217
|
+
kind: "structure",
|
|
4218
|
+
severity: "warning",
|
|
4219
|
+
message: "Step name differs",
|
|
4220
|
+
path: path16,
|
|
4221
|
+
left: L.name,
|
|
4222
|
+
right: R.name
|
|
4223
|
+
});
|
|
4224
|
+
}
|
|
4225
|
+
if ((L.type ?? "") !== (R.type ?? "")) {
|
|
4226
|
+
out.push({
|
|
4227
|
+
kind: "step-type",
|
|
4228
|
+
severity: "warning",
|
|
4229
|
+
message: "Step type differs",
|
|
4230
|
+
path: path16,
|
|
4231
|
+
left: L.type,
|
|
4232
|
+
right: R.type
|
|
4233
|
+
});
|
|
4234
|
+
}
|
|
4235
|
+
if ((L.status ?? "") !== (R.status ?? "")) {
|
|
4236
|
+
out.push({
|
|
4237
|
+
kind: "step-status",
|
|
4238
|
+
severity: "warning",
|
|
4239
|
+
message: "Step status differs",
|
|
4240
|
+
path: path16,
|
|
4241
|
+
left: L.status,
|
|
4242
|
+
right: R.status
|
|
4243
|
+
});
|
|
4244
|
+
}
|
|
4245
|
+
const le = L.error ?? "";
|
|
4246
|
+
const re = R.error ?? "";
|
|
4247
|
+
if (le !== re) {
|
|
4248
|
+
out.push({
|
|
4249
|
+
kind: "error",
|
|
4250
|
+
severity: "error",
|
|
4251
|
+
message: "Step error message differs",
|
|
4252
|
+
path: path16,
|
|
4253
|
+
left: le || void 0,
|
|
4254
|
+
right: re || void 0
|
|
4255
|
+
});
|
|
4256
|
+
}
|
|
4257
|
+
if (!opts.ignoreDuration) {
|
|
4258
|
+
const ld = L.durationMs;
|
|
4259
|
+
const rd = R.durationMs;
|
|
4260
|
+
const th = opts.durationThresholdMs;
|
|
4261
|
+
let differs = false;
|
|
4262
|
+
if (ld === void 0 && rd === void 0) differs = false;
|
|
4263
|
+
else if (ld === void 0 || rd === void 0) differs = true;
|
|
4264
|
+
else differs = Math.abs(ld - rd) > th;
|
|
4265
|
+
if (differs) {
|
|
4266
|
+
out.push({
|
|
4267
|
+
kind: "duration",
|
|
4268
|
+
severity: "info",
|
|
4269
|
+
message: "Step duration differs",
|
|
4270
|
+
path: path16,
|
|
4271
|
+
left: ld,
|
|
4272
|
+
right: rd
|
|
4273
|
+
});
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
const lm = stableJson(L.metadata ?? {});
|
|
4277
|
+
const rm = stableJson(R.metadata ?? {});
|
|
4278
|
+
if (lm !== rm) {
|
|
4279
|
+
out.push({
|
|
4280
|
+
kind: "metadata",
|
|
4281
|
+
severity: "info",
|
|
4282
|
+
message: "Step metadata differs",
|
|
4283
|
+
path: path16,
|
|
4284
|
+
left: L.metadata,
|
|
4285
|
+
right: R.metadata
|
|
4286
|
+
});
|
|
4287
|
+
}
|
|
4288
|
+
const lo = stableJson(L.outputPreview ?? null);
|
|
4289
|
+
const ro = stableJson(R.outputPreview ?? null);
|
|
4290
|
+
if (lo !== ro) {
|
|
4291
|
+
out.push({
|
|
4292
|
+
kind: "output",
|
|
4293
|
+
severity: "info",
|
|
4294
|
+
message: "Output preview differs",
|
|
4295
|
+
path: path16,
|
|
4296
|
+
left: L.outputPreview,
|
|
4297
|
+
right: R.outputPreview
|
|
4298
|
+
});
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
function compareRecursive(L, R, segments, opts, out) {
|
|
4302
|
+
compareLeafSteps(L, R, segments, opts, out);
|
|
4303
|
+
const pairs = pairSteps(L.children, R.children);
|
|
4304
|
+
let ci = 0;
|
|
4305
|
+
for (const [lch, rch] of pairs) {
|
|
4306
|
+
if (lch !== void 0 && rch !== void 0) {
|
|
4307
|
+
compareRecursive(lch, rch, [...segments, pathSeg(lch, ci)], opts, out);
|
|
4308
|
+
} else if (lch !== void 0) {
|
|
4309
|
+
out.push({
|
|
4310
|
+
kind: "step-removed",
|
|
4311
|
+
severity: "warning",
|
|
4312
|
+
message: `Step only in left run: ${lch.name}`,
|
|
4313
|
+
path: buildPath([...segments, pathSeg(lch, ci)]),
|
|
4314
|
+
left: lch.id,
|
|
4315
|
+
right: void 0
|
|
4316
|
+
});
|
|
4317
|
+
} else if (rch !== void 0) {
|
|
4318
|
+
out.push({
|
|
4319
|
+
kind: "step-added",
|
|
4320
|
+
severity: "warning",
|
|
4321
|
+
message: `Step only in right run: ${rch.name}`,
|
|
4322
|
+
path: buildPath([...segments, pathSeg(rch, ci)]),
|
|
4323
|
+
left: void 0,
|
|
4324
|
+
right: rch.id
|
|
4325
|
+
});
|
|
4326
|
+
}
|
|
4327
|
+
ci += 1;
|
|
4328
|
+
}
|
|
4244
4329
|
}
|
|
4245
|
-
function
|
|
4330
|
+
function mergeDiffDefaults(options) {
|
|
4246
4331
|
return {
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
const findings = [];
|
|
4252
|
-
for (const event of context.events) {
|
|
4253
|
-
for (const entry of eventValueEntries(event, { includeSummaries: true, includeError: true })) {
|
|
4254
|
-
if (typeof entry.value === "string" && options.maxStringLength !== void 0 && entry.value.length > options.maxStringLength) {
|
|
4255
|
-
findings.push(
|
|
4256
|
-
failFinding(
|
|
4257
|
-
"safety.oversizedAttribute",
|
|
4258
|
-
`String at ${entry.path} exceeds ${options.maxStringLength} characters.`,
|
|
4259
|
-
[eventEvidence(event, entry.path)],
|
|
4260
|
-
{ maxStringLength: options.maxStringLength },
|
|
4261
|
-
{ path: entry.path, length: entry.value.length },
|
|
4262
|
-
{
|
|
4263
|
-
category: "size",
|
|
4264
|
-
confidence: "high",
|
|
4265
|
-
detector: "safety.oversizedAttribute",
|
|
4266
|
-
action: "truncate-or-omit"
|
|
4267
|
-
}
|
|
4268
|
-
)
|
|
4269
|
-
);
|
|
4270
|
-
}
|
|
4271
|
-
if (Array.isArray(entry.value) && options.maxArrayLength !== void 0 && entry.value.length > options.maxArrayLength) {
|
|
4272
|
-
findings.push(
|
|
4273
|
-
failFinding(
|
|
4274
|
-
"safety.oversizedAttribute",
|
|
4275
|
-
`Array at ${entry.path} exceeds ${options.maxArrayLength} items.`,
|
|
4276
|
-
[eventEvidence(event, entry.path)],
|
|
4277
|
-
{ maxArrayLength: options.maxArrayLength },
|
|
4278
|
-
{ path: entry.path, length: entry.value.length },
|
|
4279
|
-
{
|
|
4280
|
-
category: "size",
|
|
4281
|
-
confidence: "high",
|
|
4282
|
-
detector: "safety.oversizedAttribute",
|
|
4283
|
-
action: "truncate-or-omit"
|
|
4284
|
-
}
|
|
4285
|
-
)
|
|
4286
|
-
);
|
|
4287
|
-
}
|
|
4288
|
-
if (isRecord8(entry.value) && options.maxObjectKeys !== void 0 && Object.keys(entry.value).length > options.maxObjectKeys) {
|
|
4289
|
-
findings.push(
|
|
4290
|
-
failFinding(
|
|
4291
|
-
"safety.oversizedAttribute",
|
|
4292
|
-
`Object at ${entry.path} exceeds ${options.maxObjectKeys} keys.`,
|
|
4293
|
-
[eventEvidence(event, entry.path)],
|
|
4294
|
-
{ maxObjectKeys: options.maxObjectKeys },
|
|
4295
|
-
{ path: entry.path, keys: Object.keys(entry.value).length },
|
|
4296
|
-
{
|
|
4297
|
-
category: "size",
|
|
4298
|
-
confidence: "high",
|
|
4299
|
-
detector: "safety.oversizedAttribute",
|
|
4300
|
-
action: "truncate-or-omit"
|
|
4301
|
-
}
|
|
4302
|
-
)
|
|
4303
|
-
);
|
|
4304
|
-
}
|
|
4305
|
-
if (options.maxSerializedBytes !== void 0) {
|
|
4306
|
-
const bytes = serializedByteLength(entry.value);
|
|
4307
|
-
if (bytes !== void 0 && bytes > options.maxSerializedBytes) {
|
|
4308
|
-
findings.push(
|
|
4309
|
-
failFinding(
|
|
4310
|
-
"safety.oversizedAttribute",
|
|
4311
|
-
`Value at ${entry.path} exceeds ${options.maxSerializedBytes} serialized bytes.`,
|
|
4312
|
-
[eventEvidence(event, entry.path)],
|
|
4313
|
-
{ maxSerializedBytes: options.maxSerializedBytes },
|
|
4314
|
-
{ path: entry.path, bytes },
|
|
4315
|
-
{
|
|
4316
|
-
category: "size",
|
|
4317
|
-
confidence: "high",
|
|
4318
|
-
detector: "safety.oversizedAttribute",
|
|
4319
|
-
action: "truncate-or-omit"
|
|
4320
|
-
}
|
|
4321
|
-
)
|
|
4322
|
-
);
|
|
4323
|
-
}
|
|
4324
|
-
}
|
|
4325
|
-
}
|
|
4326
|
-
}
|
|
4327
|
-
return limitFindings(findings, options.maxFindings);
|
|
4328
|
-
}
|
|
4332
|
+
ignoreDuration: false,
|
|
4333
|
+
durationThresholdMs: DEFAULT_THRESHOLD_MS,
|
|
4334
|
+
focus: "all",
|
|
4335
|
+
check: "all"
|
|
4329
4336
|
};
|
|
4330
4337
|
}
|
|
4331
|
-
function
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
const
|
|
4337
|
-
|
|
4338
|
-
|
|
4338
|
+
function kindMatchesFilter(kind, merged) {
|
|
4339
|
+
return true;
|
|
4340
|
+
}
|
|
4341
|
+
function diffRuns(left, right, options) {
|
|
4342
|
+
const merged = mergeDiffDefaults();
|
|
4343
|
+
const opts = {
|
|
4344
|
+
ignoreDuration: merged.ignoreDuration,
|
|
4345
|
+
durationThresholdMs: merged.durationThresholdMs
|
|
4346
|
+
};
|
|
4347
|
+
const raw = [];
|
|
4348
|
+
if ((left.status ?? "") !== (right.status ?? "")) {
|
|
4349
|
+
raw.push({
|
|
4350
|
+
kind: "run-status",
|
|
4351
|
+
severity: "warning",
|
|
4352
|
+
message: "Run completion status differs",
|
|
4353
|
+
left: left.status,
|
|
4354
|
+
right: right.status
|
|
4355
|
+
});
|
|
4339
4356
|
}
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4357
|
+
{
|
|
4358
|
+
const ld = left.durationMs;
|
|
4359
|
+
const rd = right.durationMs;
|
|
4360
|
+
const th = merged.durationThresholdMs;
|
|
4361
|
+
let differs = false;
|
|
4362
|
+
if (ld === void 0 && rd === void 0) differs = false;
|
|
4363
|
+
else if (ld === void 0 || rd === void 0) differs = true;
|
|
4364
|
+
else differs = Math.abs(ld - rd) > th;
|
|
4365
|
+
if (differs) {
|
|
4366
|
+
raw.push({
|
|
4367
|
+
kind: "duration",
|
|
4368
|
+
severity: "info",
|
|
4369
|
+
message: "Run duration differs",
|
|
4370
|
+
left: ld,
|
|
4371
|
+
right: rd
|
|
4372
|
+
});
|
|
4373
|
+
}
|
|
4351
4374
|
}
|
|
4352
|
-
const
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
ruleId: rule.id,
|
|
4367
|
-
category: rule.category,
|
|
4368
|
-
status: classifyRuleExecution(ruleFindings, false),
|
|
4369
|
-
findingCount: ruleFindings.length,
|
|
4370
|
-
...selected.run ? { runId: selected.run.runId } : {}
|
|
4375
|
+
const pairs = pairSteps(left.steps, right.steps);
|
|
4376
|
+
let idx = 0;
|
|
4377
|
+
for (const [ls, rs] of pairs) {
|
|
4378
|
+
if (ls !== void 0 && rs !== void 0) {
|
|
4379
|
+
compareRecursive(ls, rs, [pathSeg(ls, idx)], opts, raw);
|
|
4380
|
+
idx += 1;
|
|
4381
|
+
} else if (ls !== void 0) {
|
|
4382
|
+
raw.push({
|
|
4383
|
+
kind: "step-removed",
|
|
4384
|
+
severity: "warning",
|
|
4385
|
+
message: `Step only in left run: ${ls.name}`,
|
|
4386
|
+
path: buildPath([pathSeg(ls, idx)]),
|
|
4387
|
+
left: ls.id,
|
|
4388
|
+
right: void 0
|
|
4371
4389
|
});
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
findingCount: 0,
|
|
4382
|
-
...selected.run ? { runId: selected.run.runId } : {}
|
|
4390
|
+
idx += 1;
|
|
4391
|
+
} else if (rs !== void 0) {
|
|
4392
|
+
raw.push({
|
|
4393
|
+
kind: "step-added",
|
|
4394
|
+
severity: "warning",
|
|
4395
|
+
message: `Step only in right run: ${rs.name}`,
|
|
4396
|
+
path: buildPath([pathSeg(rs, idx)]),
|
|
4397
|
+
left: void 0,
|
|
4398
|
+
right: rs.id
|
|
4383
4399
|
});
|
|
4400
|
+
idx += 1;
|
|
4384
4401
|
}
|
|
4385
4402
|
}
|
|
4386
|
-
|
|
4387
|
-
|
|
4403
|
+
const differences = raw.filter((d) => kindMatchesFilter(d.kind));
|
|
4404
|
+
let errors = 0;
|
|
4405
|
+
let warnings = 0;
|
|
4406
|
+
let info = 0;
|
|
4407
|
+
for (const d of differences) {
|
|
4408
|
+
if (d.severity === "error") errors += 1;
|
|
4409
|
+
else if (d.severity === "warning") warnings += 1;
|
|
4410
|
+
else info += 1;
|
|
4388
4411
|
}
|
|
4389
|
-
const
|
|
4390
|
-
const
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4412
|
+
const firstVisible = differences[0];
|
|
4413
|
+
const firstDivergence = firstVisible !== void 0 ? {
|
|
4414
|
+
kind: "first-divergence",
|
|
4415
|
+
severity: firstVisible.severity,
|
|
4416
|
+
message: `First divergence: ${firstVisible.message}`,
|
|
4417
|
+
path: firstVisible.path,
|
|
4418
|
+
left: firstVisible.left,
|
|
4419
|
+
right: firstVisible.right
|
|
4420
|
+
} : void 0;
|
|
4421
|
+
const summary = {
|
|
4422
|
+
leftRunId: left.runId,
|
|
4423
|
+
rightRunId: right.runId,
|
|
4424
|
+
totalDifferences: differences.length,
|
|
4425
|
+
errors,
|
|
4426
|
+
warnings,
|
|
4427
|
+
info,
|
|
4428
|
+
firstDivergence
|
|
4402
4429
|
};
|
|
4430
|
+
return { summary, differences };
|
|
4403
4431
|
}
|
|
4404
4432
|
|
|
4433
|
+
// packages/core/src/evidence/zip.ts
|
|
4434
|
+
(() => {
|
|
4435
|
+
const table = new Uint32Array(256);
|
|
4436
|
+
for (let n = 0; n < 256; n += 1) {
|
|
4437
|
+
let c = n;
|
|
4438
|
+
for (let k = 0; k < 8; k += 1) {
|
|
4439
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
4440
|
+
}
|
|
4441
|
+
table[n] = c >>> 0;
|
|
4442
|
+
}
|
|
4443
|
+
return table;
|
|
4444
|
+
})();
|
|
4445
|
+
|
|
4405
4446
|
// packages/core/src/persisted/token-usage.ts
|
|
4406
4447
|
function isRecord9(value) {
|
|
4407
4448
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -4415,16 +4456,20 @@ function normalizeTokenUsage(value) {
|
|
|
4415
4456
|
const output = nonNegativeFinite(value.output);
|
|
4416
4457
|
const suppliedTotal = nonNegativeFinite(value.total);
|
|
4417
4458
|
const cached = nonNegativeFinite(value.cached);
|
|
4459
|
+
const cacheWrite = nonNegativeFinite(value.cacheWrite);
|
|
4460
|
+
const reasoning = nonNegativeFinite(value.reasoning);
|
|
4418
4461
|
const derivedTotal = input !== void 0 && output !== void 0 && Number.isFinite(input + output) ? input + output : void 0;
|
|
4419
4462
|
const total = suppliedTotal ?? derivedTotal;
|
|
4420
|
-
if (input === void 0 && output === void 0 && total === void 0 && cached === void 0) {
|
|
4463
|
+
if (input === void 0 && output === void 0 && total === void 0 && cached === void 0 && cacheWrite === void 0 && reasoning === void 0) {
|
|
4421
4464
|
return void 0;
|
|
4422
4465
|
}
|
|
4423
4466
|
return {
|
|
4424
4467
|
...input !== void 0 ? { input } : {},
|
|
4425
4468
|
...output !== void 0 ? { output } : {},
|
|
4426
4469
|
...total !== void 0 ? { total } : {},
|
|
4427
|
-
...cached !== void 0 ? { cached } : {}
|
|
4470
|
+
...cached !== void 0 ? { cached } : {},
|
|
4471
|
+
...cacheWrite !== void 0 ? { cacheWrite } : {},
|
|
4472
|
+
...reasoning !== void 0 ? { reasoning } : {}
|
|
4428
4473
|
};
|
|
4429
4474
|
}
|
|
4430
4475
|
|
|
@@ -5583,6 +5628,8 @@ function readOpenInferenceTokenUsage(attributes) {
|
|
|
5583
5628
|
const completion = attributes["llm.token_count.completion"];
|
|
5584
5629
|
const total = attributes["llm.token_count.total"];
|
|
5585
5630
|
const cached = attributes["llm.token_count.prompt_details.cache_read"];
|
|
5631
|
+
const cacheWrite = attributes["llm.token_count.prompt_details.cache_write"] ?? attributes["llm.token_count.prompt_details.cache_creation"];
|
|
5632
|
+
const reasoning = attributes["llm.token_count.completion_details.reasoning"] ?? attributes["llm.token_count.completion_details.reasoning_tokens"];
|
|
5586
5633
|
const usage = {};
|
|
5587
5634
|
if (typeof prompt === "number" && Number.isFinite(prompt) && prompt >= 0) {
|
|
5588
5635
|
usage.input = prompt;
|
|
@@ -5596,6 +5643,12 @@ function readOpenInferenceTokenUsage(attributes) {
|
|
|
5596
5643
|
if (typeof cached === "number" && Number.isFinite(cached) && cached >= 0) {
|
|
5597
5644
|
usage.cached = cached;
|
|
5598
5645
|
}
|
|
5646
|
+
if (typeof cacheWrite === "number" && Number.isFinite(cacheWrite) && cacheWrite >= 0) {
|
|
5647
|
+
usage.cacheWrite = cacheWrite;
|
|
5648
|
+
}
|
|
5649
|
+
if (typeof reasoning === "number" && Number.isFinite(reasoning) && reasoning >= 0) {
|
|
5650
|
+
usage.reasoning = reasoning;
|
|
5651
|
+
}
|
|
5599
5652
|
if (usage.total === void 0 && usage.input !== void 0 && usage.output !== void 0) {
|
|
5600
5653
|
usage.total = usage.input + usage.output;
|
|
5601
5654
|
}
|