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