agent-inspect 6.28.0 → 6.29.1
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/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/docs/CLI.md +19 -1
- package/docs/LIMITATIONS.md +2 -2
- package/docs/SAFE-TRACE-SHARING.md +1 -1
- package/docs/SCHEMA.md +1 -1
- package/docs/STANDARDS.md +1 -1
- package/package.json +2 -1
- package/packages/cli/dist/{chunk-JVQ4LTHR.mjs → chunk-UK4HD5M3.mjs} +181 -32
- package/packages/cli/dist/chunk-UK4HD5M3.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +251 -103
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +63 -72
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-M5J5YC5E.mjs → src-CMPIJGUE.mjs} +3 -3
- package/packages/cli/dist/{src-M5J5YC5E.mjs.map → src-CMPIJGUE.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +153 -30
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +20 -7
- package/packages/core/dist/advanced.d.ts +20 -7
- package/packages/core/dist/advanced.mjs +114 -38
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.cjs +235 -34
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.d.cts +5 -5
- package/packages/core/dist/checks.d.ts +5 -5
- package/packages/core/dist/checks.mjs +5 -5
- package/packages/core/dist/{chunk-NXQF73EC.mjs → chunk-5B4DNGFB.mjs} +9 -5
- package/packages/core/dist/chunk-5B4DNGFB.mjs.map +1 -0
- package/packages/core/dist/{chunk-JBNRJLIV.mjs → chunk-5RF7FYGL.mjs} +33 -7
- package/packages/core/dist/chunk-5RF7FYGL.mjs.map +1 -0
- package/packages/core/dist/{chunk-U25RHRSU.mjs → chunk-65LFRW2T.mjs} +13 -6
- package/packages/core/dist/chunk-65LFRW2T.mjs.map +1 -0
- package/packages/core/dist/{chunk-WGDSM37E.mjs → chunk-AQMJFKI6.mjs} +5 -5
- package/packages/core/dist/{chunk-WGDSM37E.mjs.map → chunk-AQMJFKI6.mjs.map} +1 -1
- package/packages/core/dist/{chunk-R4SSYU6D.mjs → chunk-COZPMOUF.mjs} +3 -3
- package/packages/core/dist/{chunk-R4SSYU6D.mjs.map → chunk-COZPMOUF.mjs.map} +1 -1
- package/packages/core/dist/{chunk-NDRMIL7Z.mjs → chunk-FKUTGUFG.mjs} +12 -4
- package/packages/core/dist/chunk-FKUTGUFG.mjs.map +1 -0
- package/packages/core/dist/{chunk-ME4WXYY3.mjs → chunk-GPGBKN7W.mjs} +238 -37
- package/packages/core/dist/chunk-GPGBKN7W.mjs.map +1 -0
- package/packages/core/dist/{chunk-P366CCL2.mjs → chunk-QJI52TH3.mjs} +4 -2
- package/packages/core/dist/chunk-QJI52TH3.mjs.map +1 -0
- package/packages/core/dist/{chunk-WRE4CNPF.mjs → chunk-SJ3C5RIS.mjs} +3 -3
- package/packages/core/dist/chunk-SJ3C5RIS.mjs.map +1 -0
- package/packages/core/dist/{context-DgcvkJ9t.d.ts → context-B8zzsmjX.d.ts} +2 -2
- package/packages/core/dist/{context-DGMqxFpz.d.cts → context-BhaVHtOT.d.cts} +2 -2
- package/packages/core/dist/diff.d.cts +1 -1
- package/packages/core/dist/diff.d.ts +1 -1
- package/packages/core/dist/exporters.cjs +39 -8
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.d.cts +13 -3
- package/packages/core/dist/exporters.d.ts +13 -3
- package/packages/core/dist/exporters.mjs +15 -10
- package/packages/core/dist/exporters.mjs.map +1 -1
- package/packages/core/dist/{index-C6bCIbFO.d.cts → index-BT3KcO6-.d.cts} +1 -1
- package/packages/core/dist/{index-DVetZwGG.d.cts → index-C61p7wBR.d.cts} +3 -3
- package/packages/core/dist/{index-BOA1AeEU.d.ts → index-DTO2iGeq.d.ts} +3 -3
- package/packages/core/dist/{index-BzyNPfLm.d.ts → index-reskZCVW.d.ts} +1 -1
- package/packages/core/dist/index.cjs +10 -1
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.d.cts +4 -4
- package/packages/core/dist/index.d.ts +4 -4
- package/packages/core/dist/index.mjs +6 -6
- package/packages/core/dist/{persisted-inspect-event-CbaRYpbw.d.ts → persisted-inspect-event-BMwvGIB8.d.ts} +5 -0
- package/packages/core/dist/{persisted-inspect-event-DiJ0I_vi.d.cts → persisted-inspect-event-DPaCiars.d.cts} +5 -0
- package/packages/core/dist/persisted.cjs +8 -2
- package/packages/core/dist/persisted.cjs.map +1 -1
- package/packages/core/dist/persisted.d.cts +3 -3
- package/packages/core/dist/persisted.d.ts +3 -3
- package/packages/core/dist/persisted.mjs +3 -3
- package/packages/core/dist/readers.cjs +16 -2
- package/packages/core/dist/readers.cjs.map +1 -1
- package/packages/core/dist/readers.d.cts +2 -2
- package/packages/core/dist/readers.d.ts +2 -2
- package/packages/core/dist/readers.mjs +4 -4
- package/packages/core/dist/{types-wXuxMUop.d.cts → types-BSzhK7Z1.d.cts} +9 -0
- package/packages/core/dist/{types-xv9bSgOK.d.ts → types-C5Y8Idle.d.ts} +9 -0
- package/packages/core/dist/writers.cjs +10 -1
- package/packages/core/dist/writers.cjs.map +1 -1
- package/packages/core/dist/writers.d.cts +1 -1
- package/packages/core/dist/writers.d.ts +1 -1
- package/packages/core/dist/writers.mjs +3 -3
- package/packages/cli/dist/chunk-JVQ4LTHR.mjs.map +0 -1
- package/packages/core/dist/chunk-JBNRJLIV.mjs.map +0 -1
- package/packages/core/dist/chunk-ME4WXYY3.mjs.map +0 -1
- package/packages/core/dist/chunk-NDRMIL7Z.mjs.map +0 -1
- package/packages/core/dist/chunk-NXQF73EC.mjs.map +0 -1
- package/packages/core/dist/chunk-P366CCL2.mjs.map +0 -1
- package/packages/core/dist/chunk-U25RHRSU.mjs.map +0 -1
- package/packages/core/dist/chunk-WRE4CNPF.mjs.map +0 -1
|
@@ -1462,6 +1462,76 @@ function evaluateControlRules(events, rules, runEvidence2, observationNames) {
|
|
|
1462
1462
|
return findings;
|
|
1463
1463
|
}
|
|
1464
1464
|
|
|
1465
|
+
// packages/core/src/checks/canonical-equality.ts
|
|
1466
|
+
var DEFAULT_MAX_DEPTH = 8;
|
|
1467
|
+
var DEFAULT_MAX_BYTES = 16384;
|
|
1468
|
+
function sortKeysDeep(input, depth, maxDepth) {
|
|
1469
|
+
if (depth > maxDepth) {
|
|
1470
|
+
throw new Error("AI_CHECK_CANONICAL_DEPTH_EXCEEDED");
|
|
1471
|
+
}
|
|
1472
|
+
if (input === null || typeof input !== "object") return input;
|
|
1473
|
+
if (Array.isArray(input)) {
|
|
1474
|
+
return input.map((item) => sortKeysDeep(item, depth + 1, maxDepth));
|
|
1475
|
+
}
|
|
1476
|
+
const record = input;
|
|
1477
|
+
const out = {};
|
|
1478
|
+
for (const key of Object.keys(record).sort()) {
|
|
1479
|
+
out[key] = sortKeysDeep(record[key], depth + 1, maxDepth);
|
|
1480
|
+
}
|
|
1481
|
+
return out;
|
|
1482
|
+
}
|
|
1483
|
+
function encodeCanonicalStructured(value, limits) {
|
|
1484
|
+
const maxDepth = DEFAULT_MAX_DEPTH;
|
|
1485
|
+
const maxBytes = DEFAULT_MAX_BYTES;
|
|
1486
|
+
try {
|
|
1487
|
+
const sorted = sortKeysDeep(value, 0, maxDepth);
|
|
1488
|
+
const encoded = JSON.stringify(sorted);
|
|
1489
|
+
if (encoded === void 0) {
|
|
1490
|
+
return {
|
|
1491
|
+
ok: false,
|
|
1492
|
+
reason: "Value is not JSON-serializable for canonical comparison.",
|
|
1493
|
+
code: "AI_CHECK_CANONICAL_UNSERIALIZABLE"
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
if (Buffer.byteLength(encoded, "utf8") > maxBytes) {
|
|
1497
|
+
return {
|
|
1498
|
+
ok: false,
|
|
1499
|
+
reason: "Canonical structured value exceeds bounded size limit.",
|
|
1500
|
+
code: "AI_CHECK_CANONICAL_SIZE_EXCEEDED"
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1503
|
+
return { ok: true, value: encoded };
|
|
1504
|
+
} catch (error) {
|
|
1505
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1506
|
+
if (message === "AI_CHECK_CANONICAL_DEPTH_EXCEEDED") {
|
|
1507
|
+
return {
|
|
1508
|
+
ok: false,
|
|
1509
|
+
reason: "Canonical structured value exceeds bounded depth limit.",
|
|
1510
|
+
code: "AI_CHECK_CANONICAL_DEPTH_EXCEEDED"
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
return {
|
|
1514
|
+
ok: false,
|
|
1515
|
+
reason: "Canonical structured encoding failed.",
|
|
1516
|
+
code: "AI_CHECK_CANONICAL_UNSERIALIZABLE"
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
function canonicalStructuredEqual(left, right, limits) {
|
|
1521
|
+
const a = encodeCanonicalStructured(left);
|
|
1522
|
+
if (!a.ok) return { equal: false, reason: a.reason, code: a.code };
|
|
1523
|
+
const b = encodeCanonicalStructured(right);
|
|
1524
|
+
if (!b.ok) return { equal: false, reason: b.reason, code: b.code };
|
|
1525
|
+
if (a.value !== b.value) {
|
|
1526
|
+
return {
|
|
1527
|
+
equal: false,
|
|
1528
|
+
reason: "Canonical structured values differ.",
|
|
1529
|
+
code: "AI_CHECK_CANONICAL_NOT_EQUAL"
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
return { equal: true };
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1465
1535
|
// packages/core/src/checks/tool-arguments.ts
|
|
1466
1536
|
function decodeToken(token) {
|
|
1467
1537
|
return token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
@@ -1507,7 +1577,7 @@ function jsonType(value) {
|
|
|
1507
1577
|
return typeof value;
|
|
1508
1578
|
}
|
|
1509
1579
|
function valuesEqual(left, right) {
|
|
1510
|
-
return
|
|
1580
|
+
return canonicalStructuredEqual(left, right).equal === true;
|
|
1511
1581
|
}
|
|
1512
1582
|
function extractToolArgumentPayload(event) {
|
|
1513
1583
|
const attrs2 = event.attributes ?? {};
|
|
@@ -1980,11 +2050,9 @@ function argumentDigestOf(event) {
|
|
|
1980
2050
|
function argumentFingerprint(event) {
|
|
1981
2051
|
const payload = extractToolArgumentPayload(event);
|
|
1982
2052
|
if (payload.present) {
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
return { kind: "missing" };
|
|
1987
|
-
}
|
|
2053
|
+
const encoded = encodeCanonicalStructured(payload.value);
|
|
2054
|
+
if (!encoded.ok) return { kind: "missing" };
|
|
2055
|
+
return { kind: "structured", value: encoded.value };
|
|
1988
2056
|
}
|
|
1989
2057
|
const digest = argumentDigestOf(event);
|
|
1990
2058
|
if (digest) return { kind: "digest", value: digest };
|
|
@@ -2079,19 +2147,84 @@ function llmReferencesTool(llm, toolEvent, requireExplicit) {
|
|
|
2079
2147
|
if (toolCallId && refs.has(toolCallId)) return true;
|
|
2080
2148
|
return false;
|
|
2081
2149
|
}
|
|
2150
|
+
function attemptIdOf2(event) {
|
|
2151
|
+
const value = workflowFor2(event).attemptId;
|
|
2152
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
|
|
2153
|
+
}
|
|
2154
|
+
function hasDuplicateAttemptIds2(members) {
|
|
2155
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2156
|
+
for (const event of members) {
|
|
2157
|
+
const id = attemptIdOf2(event);
|
|
2158
|
+
if (!id) continue;
|
|
2159
|
+
if (seen.has(id)) return true;
|
|
2160
|
+
seen.add(id);
|
|
2161
|
+
}
|
|
2162
|
+
return false;
|
|
2163
|
+
}
|
|
2164
|
+
function findRetryTarget(members, retryOf) {
|
|
2165
|
+
return members.find(
|
|
2166
|
+
(candidate) => candidate.eventId === retryOf || attemptIdOf2(candidate) === retryOf || workflowFor2(candidate).operationId === retryOf
|
|
2167
|
+
);
|
|
2168
|
+
}
|
|
2082
2169
|
function groupToolAttempts(events, toolName2) {
|
|
2170
|
+
const members = events.filter(
|
|
2171
|
+
(event) => event.kind === "TOOL" && resolveCanonicalToolName(event) === toolName2
|
|
2172
|
+
);
|
|
2173
|
+
const parent = /* @__PURE__ */ new Map();
|
|
2174
|
+
const ensure = (id) => {
|
|
2175
|
+
if (!parent.has(id)) parent.set(id, id);
|
|
2176
|
+
return id;
|
|
2177
|
+
};
|
|
2178
|
+
const find = (id) => {
|
|
2179
|
+
ensure(id);
|
|
2180
|
+
let cur = id;
|
|
2181
|
+
while (parent.get(cur) !== cur) {
|
|
2182
|
+
cur = parent.get(cur);
|
|
2183
|
+
}
|
|
2184
|
+
let walk = id;
|
|
2185
|
+
while (parent.get(walk) !== cur) {
|
|
2186
|
+
const next = parent.get(walk);
|
|
2187
|
+
parent.set(walk, cur);
|
|
2188
|
+
walk = next;
|
|
2189
|
+
}
|
|
2190
|
+
return cur;
|
|
2191
|
+
};
|
|
2192
|
+
const union = (a, b) => {
|
|
2193
|
+
const ra = find(a);
|
|
2194
|
+
const rb = find(b);
|
|
2195
|
+
if (ra !== rb) parent.set(ra, rb);
|
|
2196
|
+
};
|
|
2197
|
+
const nodeId = (event) => {
|
|
2198
|
+
const operationId = workflowFor2(event).operationId;
|
|
2199
|
+
if (typeof operationId === "string" && operationId.trim() !== "") {
|
|
2200
|
+
return `op:${operationId.trim()}`;
|
|
2201
|
+
}
|
|
2202
|
+
return `event:${event.eventId}`;
|
|
2203
|
+
};
|
|
2204
|
+
for (const event of members) {
|
|
2205
|
+
ensure(nodeId(event));
|
|
2206
|
+
}
|
|
2207
|
+
for (const event of members) {
|
|
2208
|
+
const retryOf = workflowFor2(event).retryOf;
|
|
2209
|
+
if (typeof retryOf !== "string" || retryOf.trim() === "") continue;
|
|
2210
|
+
const target = findRetryTarget(members, retryOf.trim());
|
|
2211
|
+
if (target) {
|
|
2212
|
+
union(nodeId(event), nodeId(target));
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2083
2215
|
const groups = /* @__PURE__ */ new Map();
|
|
2084
|
-
for (const event of
|
|
2085
|
-
|
|
2086
|
-
if (resolveCanonicalToolName(event) !== toolName2) continue;
|
|
2087
|
-
const workflow = workflowFor2(event);
|
|
2088
|
-
const key = workflow.operationId ?? `__tool__:${toolName2}`;
|
|
2216
|
+
for (const event of members) {
|
|
2217
|
+
const key = find(nodeId(event));
|
|
2089
2218
|
const list = groups.get(key) ?? [];
|
|
2090
2219
|
list.push(event);
|
|
2091
2220
|
groups.set(key, list);
|
|
2092
2221
|
}
|
|
2093
2222
|
return groups;
|
|
2094
2223
|
}
|
|
2224
|
+
function latestAttempt(ordered) {
|
|
2225
|
+
if (ordered.length === 0) return void 0;
|
|
2226
|
+
return ordered[ordered.length - 1];
|
|
2227
|
+
}
|
|
2095
2228
|
function evaluateRecoveryOperations(events, operations) {
|
|
2096
2229
|
const findings = [];
|
|
2097
2230
|
const llmEvents = events.filter((event) => event.kind === "LLM");
|
|
@@ -2114,6 +2247,17 @@ function evaluateRecoveryOperations(events, operations) {
|
|
|
2114
2247
|
}
|
|
2115
2248
|
for (const [operationKey, members] of groups) {
|
|
2116
2249
|
const ordered = sortByTime2(members);
|
|
2250
|
+
if (hasDuplicateAttemptIds2(members)) {
|
|
2251
|
+
findings.push(
|
|
2252
|
+
fail3(
|
|
2253
|
+
"contract.retry.operations.duplicate-attempt-id",
|
|
2254
|
+
`Tool ${operation.tool} operation ${operationKey} has duplicate attemptId values.`,
|
|
2255
|
+
members.slice(0, 4).map(eventEvidence2),
|
|
2256
|
+
"unique attemptId",
|
|
2257
|
+
"duplicate"
|
|
2258
|
+
)
|
|
2259
|
+
);
|
|
2260
|
+
}
|
|
2117
2261
|
if (operation.maxAttempts !== void 0) {
|
|
2118
2262
|
const count = countOperationAttempts(members);
|
|
2119
2263
|
if (count > operation.maxAttempts) {
|
|
@@ -2150,8 +2294,21 @@ function evaluateRecoveryOperations(events, operations) {
|
|
|
2150
2294
|
let sawEarlierError = false;
|
|
2151
2295
|
let sawLaterOkAfterError = false;
|
|
2152
2296
|
let lastOk;
|
|
2297
|
+
let identityUnavailable = false;
|
|
2153
2298
|
for (const event of ordered) {
|
|
2154
2299
|
const classification = classifyRetryAttempt(event, prior, members);
|
|
2300
|
+
if (classification.kind === "unknown") {
|
|
2301
|
+
identityUnavailable = true;
|
|
2302
|
+
findings.push(
|
|
2303
|
+
fail3(
|
|
2304
|
+
"contract.retry.operations.attempt-identity",
|
|
2305
|
+
`Tool ${operation.tool} operation ${operationKey} has ambiguous attempt identity (${classification.reason}).`,
|
|
2306
|
+
[eventEvidence2(event)],
|
|
2307
|
+
"explicit operationId/attemptId/retryOf chronology",
|
|
2308
|
+
{ code: "AI_CHECK_RECOVERY_ATTEMPT_IDENTITY_UNAVAILABLE", reason: classification.reason }
|
|
2309
|
+
)
|
|
2310
|
+
);
|
|
2311
|
+
}
|
|
2155
2312
|
const isRetry = classification.kind === "retry";
|
|
2156
2313
|
if (isRetry && operation.requireFailureBeforeRetry) {
|
|
2157
2314
|
const earlierFailure = prior.some(
|
|
@@ -2186,6 +2343,16 @@ function evaluateRecoveryOperations(events, operations) {
|
|
|
2186
2343
|
)
|
|
2187
2344
|
);
|
|
2188
2345
|
}
|
|
2346
|
+
} else {
|
|
2347
|
+
findings.push(
|
|
2348
|
+
fail3(
|
|
2349
|
+
"contract.retry.operations.retryable-error",
|
|
2350
|
+
`Retry of tool ${operation.tool} has no prior error code evidence.`,
|
|
2351
|
+
[eventEvidence2(event)],
|
|
2352
|
+
[...allowed],
|
|
2353
|
+
null
|
|
2354
|
+
)
|
|
2355
|
+
);
|
|
2189
2356
|
}
|
|
2190
2357
|
}
|
|
2191
2358
|
if (isRetry && (operation.requireSameArguments === true || operation.requireSameArguments === "structured-or-digest")) {
|
|
@@ -2203,6 +2370,16 @@ function evaluateRecoveryOperations(events, operations) {
|
|
|
2203
2370
|
)
|
|
2204
2371
|
);
|
|
2205
2372
|
}
|
|
2373
|
+
} else {
|
|
2374
|
+
findings.push(
|
|
2375
|
+
fail3(
|
|
2376
|
+
"contract.retry.operations.same-arguments",
|
|
2377
|
+
"Structured or digest argument evidence unavailable for same-arguments check.",
|
|
2378
|
+
[eventEvidence2(event)],
|
|
2379
|
+
"matching structured args or digests",
|
|
2380
|
+
{ code: "AI_CHECK_RECOVERY_ARGUMENT_EVIDENCE_UNAVAILABLE" }
|
|
2381
|
+
)
|
|
2382
|
+
);
|
|
2206
2383
|
}
|
|
2207
2384
|
}
|
|
2208
2385
|
if (event.status === "error") {
|
|
@@ -2215,23 +2392,23 @@ function evaluateRecoveryOperations(events, operations) {
|
|
|
2215
2392
|
}
|
|
2216
2393
|
prior.push(event);
|
|
2217
2394
|
}
|
|
2395
|
+
const latest = latestAttempt(ordered);
|
|
2218
2396
|
if (operation.requireTerminalSuccess) {
|
|
2219
|
-
|
|
2220
|
-
if (!hasOk) {
|
|
2397
|
+
if (!latest || latest.status !== "ok") {
|
|
2221
2398
|
findings.push(
|
|
2222
2399
|
fail3(
|
|
2223
2400
|
"contract.retry.operations.terminal-success",
|
|
2224
|
-
`Tool ${operation.tool} operation ${operationKey}
|
|
2401
|
+
`Tool ${operation.tool} operation ${operationKey} does not terminate with ok on the latest attempt.`,
|
|
2225
2402
|
ordered.slice(0, 4).map(eventEvidence2),
|
|
2226
|
-
"ok",
|
|
2227
|
-
|
|
2403
|
+
"latest attempt ok",
|
|
2404
|
+
latest?.status ?? "missing"
|
|
2228
2405
|
)
|
|
2229
2406
|
);
|
|
2230
2407
|
}
|
|
2231
2408
|
}
|
|
2232
2409
|
if (operation.requireRecoveredFailureVisible) {
|
|
2233
|
-
const
|
|
2234
|
-
if (ordered.length > 1 &&
|
|
2410
|
+
const latestOk = latest?.status === "ok";
|
|
2411
|
+
if (ordered.length > 1 && latestOk && !sawLaterOkAfterError) {
|
|
2235
2412
|
findings.push(
|
|
2236
2413
|
fail3(
|
|
2237
2414
|
"contract.retry.operations.recovered-failure-visible",
|
|
@@ -2244,21 +2421,36 @@ function evaluateRecoveryOperations(events, operations) {
|
|
|
2244
2421
|
}
|
|
2245
2422
|
}
|
|
2246
2423
|
const dependency = operation.successfulResultDependency;
|
|
2247
|
-
if (dependency?.consumerKind === "LLM"
|
|
2248
|
-
const
|
|
2249
|
-
|
|
2250
|
-
(
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2424
|
+
if (dependency?.consumerKind === "LLM") {
|
|
2425
|
+
const successForDependency = latest?.status === "ok" ? latest : identityUnavailable ? void 0 : lastOk;
|
|
2426
|
+
if (successForDependency) {
|
|
2427
|
+
if (sideEffectClass === "write" && isUnevaluableWriteCompletion(successForDependency) && !hasIdempotencyEvidence2(successForDependency)) {
|
|
2428
|
+
findings.push(
|
|
2429
|
+
fail3(
|
|
2430
|
+
"contract.retry.operations.successful-result-dependency",
|
|
2431
|
+
`Successful ${operation.tool} result dependency is unevaluable because write completion evidence is unavailable.`,
|
|
2432
|
+
[eventEvidence2(successForDependency)],
|
|
2433
|
+
"LLM reference to successful tool event",
|
|
2434
|
+
{ code: "AI_CHECK_RECOVERY_WRITE_COMPLETION_UNAVAILABLE" }
|
|
2435
|
+
)
|
|
2436
|
+
);
|
|
2437
|
+
} else {
|
|
2438
|
+
const requireExplicit = dependency.requireExplicitReference === true;
|
|
2439
|
+
const referenced = llmEvents.some(
|
|
2440
|
+
(llmEvent) => llmReferencesTool(llmEvent, successForDependency, requireExplicit)
|
|
2441
|
+
);
|
|
2442
|
+
if (!referenced) {
|
|
2443
|
+
findings.push(
|
|
2444
|
+
fail3(
|
|
2445
|
+
"contract.retry.operations.successful-result-dependency",
|
|
2446
|
+
requireExplicit ? `Successful ${operation.tool} result is not explicitly referenced by a later LLM event.` : `Successful ${operation.tool} result has no later LLM consumer.`,
|
|
2447
|
+
[eventEvidence2(successForDependency)],
|
|
2448
|
+
"LLM reference to successful tool event",
|
|
2449
|
+
{ code: "AI_CHECK_RECOVERY_RESULT_DEPENDENCY_MISSING" }
|
|
2450
|
+
)
|
|
2451
|
+
);
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2262
2454
|
}
|
|
2263
2455
|
}
|
|
2264
2456
|
}
|
|
@@ -3332,6 +3524,9 @@ var SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
|
|
|
3332
3524
|
"output",
|
|
3333
3525
|
"total",
|
|
3334
3526
|
"cached",
|
|
3527
|
+
"cachewrite",
|
|
3528
|
+
"cache_write",
|
|
3529
|
+
"reasoning",
|
|
3335
3530
|
"input_tokens",
|
|
3336
3531
|
"inputtokens",
|
|
3337
3532
|
"output_tokens",
|
|
@@ -3341,7 +3536,13 @@ var SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
|
|
|
3341
3536
|
"prompt_tokens",
|
|
3342
3537
|
"prompttokens",
|
|
3343
3538
|
"completion_tokens",
|
|
3344
|
-
"completiontokens"
|
|
3539
|
+
"completiontokens",
|
|
3540
|
+
"cache_read",
|
|
3541
|
+
"cacheread",
|
|
3542
|
+
"cache_creation",
|
|
3543
|
+
"cachecreation",
|
|
3544
|
+
"reasoning_tokens",
|
|
3545
|
+
"reasoningtokens"
|
|
3345
3546
|
]);
|
|
3346
3547
|
var DEFAULT_SECRET_PATTERNS = [
|
|
3347
3548
|
{ id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
|