agent-inspect 6.17.4 → 6.17.6
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/ADAPTERS.md +3 -3
- package/docs/API.md +13 -7
- package/docs/CLI.md +2 -0
- package/docs/LIMITATIONS.md +1 -0
- package/docs/TRACE-CONTRACTS.md +90 -0
- package/package.json +8 -5
- package/packages/cli/dist/{chunk-YX6RFLV5.mjs → chunk-GOHYSE7W.mjs} +86 -17
- package/packages/cli/dist/chunk-GOHYSE7W.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +522 -81
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +441 -69
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-X6SLMBNH.mjs → src-OYGG7QAZ.mjs} +3 -3
- package/packages/cli/dist/{src-X6SLMBNH.mjs.map → src-OYGG7QAZ.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +85 -16
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +1 -1
- package/packages/core/dist/advanced.d.ts +1 -1
- package/packages/core/dist/advanced.mjs +15 -5
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.cjs +119 -26
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.d.cts +28 -2
- package/packages/core/dist/checks.d.ts +28 -2
- package/packages/core/dist/checks.mjs +1 -1
- package/packages/core/dist/{chunk-HN377P23.mjs → chunk-DIZPIPY2.mjs} +121 -28
- package/packages/core/dist/chunk-DIZPIPY2.mjs.map +1 -0
- package/packages/core/dist/{index-Xk9X-yjY.d.cts → index-DWu54Y28.d.cts} +107 -122
- package/packages/core/dist/{index-BsCcOKxy.d.ts → index-DlwbVqEs.d.ts} +107 -122
- package/packages/cli/dist/chunk-YX6RFLV5.mjs.map +0 -1
- package/packages/core/dist/chunk-HN377P23.mjs.map +0 -1
|
@@ -673,9 +673,13 @@ function contractToRules(contract) {
|
|
|
673
673
|
rules.push(createStructureIncompleteRule({ requireEndedAtForStarted: true }));
|
|
674
674
|
}
|
|
675
675
|
if (contract.tools) {
|
|
676
|
+
const order = contract.tools.requiredOrder ?? [];
|
|
676
677
|
const required = [
|
|
677
|
-
|
|
678
|
-
|
|
678
|
+
.../* @__PURE__ */ new Set([
|
|
679
|
+
...contract.tools.required ?? [],
|
|
680
|
+
...contract.tools.requiredTools ?? [],
|
|
681
|
+
...order
|
|
682
|
+
])
|
|
679
683
|
];
|
|
680
684
|
const forbidden = [
|
|
681
685
|
...contract.tools.forbidden ?? [],
|
|
@@ -689,9 +693,14 @@ function contractToRules(contract) {
|
|
|
689
693
|
...contract.tools.maxCalls !== void 0 ? { maxCount: contract.tools.maxCalls } : {}
|
|
690
694
|
})
|
|
691
695
|
);
|
|
692
|
-
const order = contract.tools.requiredOrder ?? [];
|
|
693
696
|
for (let i = 0; i < order.length - 1; i += 1) {
|
|
694
|
-
rules.push(
|
|
697
|
+
rules.push(
|
|
698
|
+
createToolOrderingRule({
|
|
699
|
+
before: order[i],
|
|
700
|
+
after: order[i + 1],
|
|
701
|
+
id: `contract.tool.order.${i}`
|
|
702
|
+
})
|
|
703
|
+
);
|
|
695
704
|
}
|
|
696
705
|
}
|
|
697
706
|
if (contract.llm) {
|
|
@@ -851,10 +860,11 @@ function emptySummary() {
|
|
|
851
860
|
passed: 0,
|
|
852
861
|
failed: 0,
|
|
853
862
|
warnings: 0,
|
|
854
|
-
errors: 0
|
|
863
|
+
errors: 0,
|
|
864
|
+
rulesEvaluated: 0
|
|
855
865
|
};
|
|
856
866
|
}
|
|
857
|
-
function errorResult(input, diagnostics, selectedRun) {
|
|
867
|
+
function errorResult(input, diagnostics, selectedRun, ruleExecutions = []) {
|
|
858
868
|
return {
|
|
859
869
|
ok: false,
|
|
860
870
|
status: "error",
|
|
@@ -862,10 +872,12 @@ function errorResult(input, diagnostics, selectedRun) {
|
|
|
862
872
|
...selectedRun ? { runId: selectedRun.runId } : {},
|
|
863
873
|
summary: {
|
|
864
874
|
...emptySummary(),
|
|
865
|
-
errors: diagnostics.filter((item) => item.severity === "error").length
|
|
875
|
+
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
876
|
+
rulesEvaluated: ruleExecutions.length
|
|
866
877
|
},
|
|
867
878
|
findings: [],
|
|
868
|
-
diagnostics: [...diagnostics]
|
|
879
|
+
diagnostics: [...diagnostics],
|
|
880
|
+
ruleExecutions: [...ruleExecutions]
|
|
869
881
|
};
|
|
870
882
|
}
|
|
871
883
|
function flattenNodes(nodes) {
|
|
@@ -1021,7 +1033,7 @@ function normalizeFinding(rule, finding) {
|
|
|
1021
1033
|
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
1022
1034
|
};
|
|
1023
1035
|
}
|
|
1024
|
-
function summarize(findings, diagnostics) {
|
|
1036
|
+
function summarize(findings, diagnostics, rulesEvaluated) {
|
|
1025
1037
|
return {
|
|
1026
1038
|
passed: findings.filter((finding) => finding.status === "pass").length,
|
|
1027
1039
|
failed: findings.filter(
|
|
@@ -1030,9 +1042,21 @@ function summarize(findings, diagnostics) {
|
|
|
1030
1042
|
warnings: findings.filter(
|
|
1031
1043
|
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
1032
1044
|
).length,
|
|
1033
|
-
errors: diagnostics.filter((item) => item.severity === "error").length
|
|
1045
|
+
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
1046
|
+
rulesEvaluated
|
|
1034
1047
|
};
|
|
1035
1048
|
}
|
|
1049
|
+
function classifyRuleExecution(findings, threw) {
|
|
1050
|
+
if (findings.some((finding) => finding.status === "fail" && finding.severity === "error")) {
|
|
1051
|
+
return "fail";
|
|
1052
|
+
}
|
|
1053
|
+
if (findings.some(
|
|
1054
|
+
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
1055
|
+
)) {
|
|
1056
|
+
return "warning";
|
|
1057
|
+
}
|
|
1058
|
+
return "pass";
|
|
1059
|
+
}
|
|
1036
1060
|
function stringAttr(event, keys) {
|
|
1037
1061
|
for (const key of keys) {
|
|
1038
1062
|
const value = event.attributes?.[key];
|
|
@@ -1129,6 +1153,9 @@ function finishedEvents(context, kind) {
|
|
|
1129
1153
|
(event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
|
|
1130
1154
|
);
|
|
1131
1155
|
}
|
|
1156
|
+
function toolInvocationEvents(context) {
|
|
1157
|
+
return semanticEvents(context).filter((event) => event.kind === "TOOL");
|
|
1158
|
+
}
|
|
1132
1159
|
function firstIndexByName(events) {
|
|
1133
1160
|
const index = /* @__PURE__ */ new Map();
|
|
1134
1161
|
for (const [position, event] of events.entries()) {
|
|
@@ -1576,7 +1603,7 @@ function createToolUsageRule(options) {
|
|
|
1576
1603
|
category: "tool",
|
|
1577
1604
|
defaultSeverity: "error",
|
|
1578
1605
|
evaluate(context) {
|
|
1579
|
-
const tools =
|
|
1606
|
+
const tools = toolInvocationEvents(context);
|
|
1580
1607
|
const names = tools.map(toolName);
|
|
1581
1608
|
const nameSet = new Set(names);
|
|
1582
1609
|
const findings = [];
|
|
@@ -1617,8 +1644,9 @@ function createToolUsageRule(options) {
|
|
|
1617
1644
|
};
|
|
1618
1645
|
}
|
|
1619
1646
|
function createToolOrderingRule(options) {
|
|
1647
|
+
const ruleId = options.id ?? "tool.order";
|
|
1620
1648
|
return {
|
|
1621
|
-
id:
|
|
1649
|
+
id: ruleId,
|
|
1622
1650
|
category: "tool",
|
|
1623
1651
|
defaultSeverity: "error",
|
|
1624
1652
|
evaluate(context) {
|
|
@@ -1626,18 +1654,42 @@ function createToolOrderingRule(options) {
|
|
|
1626
1654
|
const index = firstIndexByName(tools);
|
|
1627
1655
|
const beforeIndex = index.get(options.before);
|
|
1628
1656
|
const afterIndex = index.get(options.after);
|
|
1629
|
-
if (beforeIndex === void 0 || afterIndex === void 0
|
|
1657
|
+
if (beforeIndex === void 0 || afterIndex === void 0) {
|
|
1630
1658
|
return [];
|
|
1631
1659
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1660
|
+
if (beforeIndex >= afterIndex) {
|
|
1661
|
+
return [
|
|
1662
|
+
failFinding(
|
|
1663
|
+
ruleId,
|
|
1664
|
+
`Tool ${options.before} must appear before ${options.after}.`,
|
|
1665
|
+
[eventEvidence(tools[beforeIndex]), eventEvidence(tools[afterIndex])],
|
|
1666
|
+
{ before: options.before, after: options.after },
|
|
1667
|
+
tools.map(toolName)
|
|
1668
|
+
)
|
|
1669
|
+
];
|
|
1670
|
+
}
|
|
1671
|
+
const beforeEvent = tools[beforeIndex];
|
|
1672
|
+
const afterEvent = tools[afterIndex];
|
|
1673
|
+
const beforeEnd = eventEndMs(beforeEvent);
|
|
1674
|
+
const afterStart = eventStartMs(afterEvent);
|
|
1675
|
+
if (beforeEnd !== void 0 && afterStart !== void 0 && beforeEnd > afterStart) {
|
|
1676
|
+
return [
|
|
1677
|
+
{
|
|
1678
|
+
ruleId,
|
|
1679
|
+
severity: "warning",
|
|
1680
|
+
status: "warning",
|
|
1681
|
+
message: `Tool order ${options.before} before ${options.after} satisfied start/encounter order, but intervals overlap so causal completion before the next tool was not established.`,
|
|
1682
|
+
expected: { before: options.before, after: options.after, mode: "first-occurrence" },
|
|
1683
|
+
actual: {
|
|
1684
|
+
code: "tool.order.overlap",
|
|
1685
|
+
beforeEndedAt: beforeEvent.endedAt,
|
|
1686
|
+
afterStartedAt: afterEvent.startedAt ?? afterEvent.timestamp
|
|
1687
|
+
},
|
|
1688
|
+
evidence: [eventEvidence(beforeEvent), eventEvidence(afterEvent)]
|
|
1689
|
+
}
|
|
1690
|
+
];
|
|
1691
|
+
}
|
|
1692
|
+
return [];
|
|
1641
1693
|
}
|
|
1642
1694
|
};
|
|
1643
1695
|
}
|
|
@@ -2434,12 +2486,24 @@ function createBaselineRegressionRule(options) {
|
|
|
2434
2486
|
}
|
|
2435
2487
|
function createObservedOutcomeRule(options = {}) {
|
|
2436
2488
|
const failOn = options.failOn ?? ["failed"];
|
|
2489
|
+
const requireAny = options.requireAny === true;
|
|
2437
2490
|
return {
|
|
2438
2491
|
id: "outcome.status",
|
|
2439
2492
|
category: "run",
|
|
2440
2493
|
defaultSeverity: "error",
|
|
2441
2494
|
evaluate(context) {
|
|
2442
2495
|
const outcomes = extractOutcomesFromPersistedEvents(context.events);
|
|
2496
|
+
if (requireAny && outcomes.length === 0) {
|
|
2497
|
+
return [
|
|
2498
|
+
failFinding(
|
|
2499
|
+
"outcome.status",
|
|
2500
|
+
"Expected at least one observed outcome.",
|
|
2501
|
+
runEvidence(context.selectedRun),
|
|
2502
|
+
{ requireAny: true, expected: "at least one observed outcome" },
|
|
2503
|
+
{ code: "outcome.missing", actual: 0 }
|
|
2504
|
+
)
|
|
2505
|
+
];
|
|
2506
|
+
}
|
|
2443
2507
|
const matching = outcomesMatchingStatus(outcomes, failOn);
|
|
2444
2508
|
if (matching.length === 0) return [];
|
|
2445
2509
|
return [
|
|
@@ -2475,6 +2539,18 @@ function runTraceChecks(input, options = {}) {
|
|
|
2475
2539
|
if (rules.diagnostics.length > 0) {
|
|
2476
2540
|
return errorResult(input, rules.diagnostics, selected.run);
|
|
2477
2541
|
}
|
|
2542
|
+
if (rules.rules.length === 0) {
|
|
2543
|
+
return errorResult(
|
|
2544
|
+
input,
|
|
2545
|
+
[
|
|
2546
|
+
diagnostic(
|
|
2547
|
+
"AI_CHECK_NO_RULES_EVALUATED",
|
|
2548
|
+
"No trace check rules were evaluated. Configure at least one rule, contract, or CLI check option."
|
|
2549
|
+
)
|
|
2550
|
+
],
|
|
2551
|
+
selected.run
|
|
2552
|
+
);
|
|
2553
|
+
}
|
|
2478
2554
|
const facts = buildFacts(input, selected.run);
|
|
2479
2555
|
const context = {
|
|
2480
2556
|
...facts,
|
|
@@ -2483,22 +2559,38 @@ function runTraceChecks(input, options = {}) {
|
|
|
2483
2559
|
};
|
|
2484
2560
|
const diagnostics = [];
|
|
2485
2561
|
const findings = [];
|
|
2562
|
+
const ruleExecutions = [];
|
|
2486
2563
|
for (const rule of rules.rules) {
|
|
2487
2564
|
try {
|
|
2488
|
-
|
|
2565
|
+
const ruleFindings = rule.evaluate(context).map((finding) => normalizeFinding(rule, finding));
|
|
2566
|
+
findings.push(...ruleFindings);
|
|
2567
|
+
ruleExecutions.push({
|
|
2568
|
+
ruleId: rule.id,
|
|
2569
|
+
category: rule.category,
|
|
2570
|
+
status: classifyRuleExecution(ruleFindings, false),
|
|
2571
|
+
findingCount: ruleFindings.length,
|
|
2572
|
+
...selected.run ? { runId: selected.run.runId } : {}
|
|
2573
|
+
});
|
|
2489
2574
|
} catch (error) {
|
|
2490
2575
|
const message = error instanceof Error ? error.message : String(error);
|
|
2491
2576
|
diagnostics.push(
|
|
2492
2577
|
diagnostic("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
|
|
2493
2578
|
);
|
|
2579
|
+
ruleExecutions.push({
|
|
2580
|
+
ruleId: rule.id,
|
|
2581
|
+
category: rule.category,
|
|
2582
|
+
status: "error",
|
|
2583
|
+
findingCount: 0,
|
|
2584
|
+
...selected.run ? { runId: selected.run.runId } : {}
|
|
2585
|
+
});
|
|
2494
2586
|
}
|
|
2495
2587
|
}
|
|
2496
2588
|
if (diagnostics.length > 0) {
|
|
2497
|
-
return errorResult(input, diagnostics, selected.run);
|
|
2589
|
+
return errorResult(input, diagnostics, selected.run, ruleExecutions);
|
|
2498
2590
|
}
|
|
2499
2591
|
const eventById = new Map(input.read.events.map((event) => [event.eventId, event]));
|
|
2500
2592
|
const sortedFindings = findings.sort(compareFindings(eventById));
|
|
2501
|
-
const summary = summarize(sortedFindings, diagnostics);
|
|
2593
|
+
const summary = summarize(sortedFindings, diagnostics, ruleExecutions.length);
|
|
2502
2594
|
const status = summary.failed > 0 ? "fail" : "pass";
|
|
2503
2595
|
return {
|
|
2504
2596
|
ok: status === "pass",
|
|
@@ -2507,7 +2599,8 @@ function runTraceChecks(input, options = {}) {
|
|
|
2507
2599
|
...selected.run ? { runId: selected.run.runId } : {},
|
|
2508
2600
|
summary,
|
|
2509
2601
|
findings: sortedFindings,
|
|
2510
|
-
diagnostics
|
|
2602
|
+
diagnostics,
|
|
2603
|
+
ruleExecutions
|
|
2511
2604
|
};
|
|
2512
2605
|
}
|
|
2513
2606
|
|