agent-inspect 6.17.4 → 6.17.5
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 +6 -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 +5 -2
- 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 +480 -80
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +399 -68
- 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
|
@@ -3565,14 +3565,15 @@ var init_cohort = __esm({
|
|
|
3565
3565
|
|
|
3566
3566
|
// packages/core/src/sessions/checks.ts
|
|
3567
3567
|
function emptySummary() {
|
|
3568
|
-
return { passed: 0, failed: 0, warnings: 0, errors: 0 };
|
|
3568
|
+
return { passed: 0, failed: 0, warnings: 0, errors: 0, rulesEvaluated: 0 };
|
|
3569
3569
|
}
|
|
3570
3570
|
function mergeSummary(target, source) {
|
|
3571
3571
|
return {
|
|
3572
3572
|
passed: target.passed + source.passed,
|
|
3573
3573
|
failed: target.failed + source.failed,
|
|
3574
3574
|
warnings: target.warnings + source.warnings,
|
|
3575
|
-
errors: target.errors + source.errors
|
|
3575
|
+
errors: target.errors + source.errors,
|
|
3576
|
+
rulesEvaluated: target.rulesEvaluated + source.rulesEvaluated
|
|
3576
3577
|
};
|
|
3577
3578
|
}
|
|
3578
3579
|
function sessionDiagnostic(code, message) {
|
|
@@ -3596,6 +3597,7 @@ function aggregateSessionCheckResults(perRun, scope) {
|
|
|
3596
3597
|
`${scope.scopeKind} not found: ${scope.scopeLabel}`
|
|
3597
3598
|
)
|
|
3598
3599
|
],
|
|
3600
|
+
ruleExecutions: [],
|
|
3599
3601
|
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
3600
3602
|
};
|
|
3601
3603
|
}
|
|
@@ -3616,17 +3618,20 @@ function aggregateSessionCheckResults(perRun, scope) {
|
|
|
3616
3618
|
`No readable traces in ${scope.scopeKind}: ${scope.scopeLabel}`
|
|
3617
3619
|
)
|
|
3618
3620
|
],
|
|
3621
|
+
ruleExecutions: [],
|
|
3619
3622
|
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
3620
3623
|
};
|
|
3621
3624
|
}
|
|
3622
3625
|
let summary = emptySummary();
|
|
3623
3626
|
const findings = [];
|
|
3624
3627
|
const diagnostics = [];
|
|
3628
|
+
const ruleExecutions = [];
|
|
3625
3629
|
const runResults = [];
|
|
3626
3630
|
for (const result of perRun) {
|
|
3627
3631
|
summary = mergeSummary(summary, result.summary);
|
|
3628
3632
|
findings.push(...result.findings);
|
|
3629
3633
|
diagnostics.push(...result.diagnostics);
|
|
3634
|
+
ruleExecutions.push(...result.ruleExecutions ?? []);
|
|
3630
3635
|
if (result.runId) {
|
|
3631
3636
|
runResults.push({ runId: result.runId, status: result.status });
|
|
3632
3637
|
}
|
|
@@ -3639,6 +3644,11 @@ function aggregateSessionCheckResults(perRun, scope) {
|
|
|
3639
3644
|
if (runCmp !== 0) return runCmp;
|
|
3640
3645
|
return a.ruleId.localeCompare(b.ruleId);
|
|
3641
3646
|
});
|
|
3647
|
+
ruleExecutions.sort((a, b) => {
|
|
3648
|
+
const runCmp = (a.runId ?? "").localeCompare(b.runId ?? "");
|
|
3649
|
+
if (runCmp !== 0) return runCmp;
|
|
3650
|
+
return a.ruleId.localeCompare(b.ruleId);
|
|
3651
|
+
});
|
|
3642
3652
|
const hasErrors = diagnostics.some((item) => item.severity === "error");
|
|
3643
3653
|
const status = hasErrors ? "error" : summary.failed > 0 ? "fail" : "pass";
|
|
3644
3654
|
return {
|
|
@@ -3652,6 +3662,7 @@ function aggregateSessionCheckResults(perRun, scope) {
|
|
|
3652
3662
|
summary,
|
|
3653
3663
|
findings,
|
|
3654
3664
|
diagnostics,
|
|
3665
|
+
ruleExecutions,
|
|
3655
3666
|
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
3656
3667
|
};
|
|
3657
3668
|
}
|
|
@@ -6857,10 +6868,11 @@ function emptySummary2() {
|
|
|
6857
6868
|
passed: 0,
|
|
6858
6869
|
failed: 0,
|
|
6859
6870
|
warnings: 0,
|
|
6860
|
-
errors: 0
|
|
6871
|
+
errors: 0,
|
|
6872
|
+
rulesEvaluated: 0
|
|
6861
6873
|
};
|
|
6862
6874
|
}
|
|
6863
|
-
function errorResult(input3, diagnostics, selectedRun) {
|
|
6875
|
+
function errorResult(input3, diagnostics, selectedRun, ruleExecutions = []) {
|
|
6864
6876
|
return {
|
|
6865
6877
|
ok: false,
|
|
6866
6878
|
status: "error",
|
|
@@ -6868,10 +6880,12 @@ function errorResult(input3, diagnostics, selectedRun) {
|
|
|
6868
6880
|
...selectedRun ? { runId: selectedRun.runId } : {},
|
|
6869
6881
|
summary: {
|
|
6870
6882
|
...emptySummary2(),
|
|
6871
|
-
errors: diagnostics.filter((item) => item.severity === "error").length
|
|
6883
|
+
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
6884
|
+
rulesEvaluated: ruleExecutions.length
|
|
6872
6885
|
},
|
|
6873
6886
|
findings: [],
|
|
6874
|
-
diagnostics: [...diagnostics]
|
|
6887
|
+
diagnostics: [...diagnostics],
|
|
6888
|
+
ruleExecutions: [...ruleExecutions]
|
|
6875
6889
|
};
|
|
6876
6890
|
}
|
|
6877
6891
|
function flattenNodes(nodes) {
|
|
@@ -7027,7 +7041,7 @@ function normalizeFinding(rule, finding) {
|
|
|
7027
7041
|
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
7028
7042
|
};
|
|
7029
7043
|
}
|
|
7030
|
-
function summarize(findings, diagnostics) {
|
|
7044
|
+
function summarize(findings, diagnostics, rulesEvaluated) {
|
|
7031
7045
|
return {
|
|
7032
7046
|
passed: findings.filter((finding) => finding.status === "pass").length,
|
|
7033
7047
|
failed: findings.filter(
|
|
@@ -7036,9 +7050,21 @@ function summarize(findings, diagnostics) {
|
|
|
7036
7050
|
warnings: findings.filter(
|
|
7037
7051
|
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
7038
7052
|
).length,
|
|
7039
|
-
errors: diagnostics.filter((item) => item.severity === "error").length
|
|
7053
|
+
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
7054
|
+
rulesEvaluated
|
|
7040
7055
|
};
|
|
7041
7056
|
}
|
|
7057
|
+
function classifyRuleExecution(findings, threw) {
|
|
7058
|
+
if (findings.some((finding) => finding.status === "fail" && finding.severity === "error")) {
|
|
7059
|
+
return "fail";
|
|
7060
|
+
}
|
|
7061
|
+
if (findings.some(
|
|
7062
|
+
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
7063
|
+
)) {
|
|
7064
|
+
return "warning";
|
|
7065
|
+
}
|
|
7066
|
+
return "pass";
|
|
7067
|
+
}
|
|
7042
7068
|
function stringAttr(event, keys) {
|
|
7043
7069
|
for (const key of keys) {
|
|
7044
7070
|
const value = event.attributes?.[key];
|
|
@@ -7135,6 +7161,9 @@ function finishedEvents(context, kind) {
|
|
|
7135
7161
|
(event) => (kind === void 0 || event.kind === kind) && event.status !== "running"
|
|
7136
7162
|
);
|
|
7137
7163
|
}
|
|
7164
|
+
function toolInvocationEvents(context) {
|
|
7165
|
+
return semanticEvents(context).filter((event) => event.kind === "TOOL");
|
|
7166
|
+
}
|
|
7138
7167
|
function isRecord8(value) {
|
|
7139
7168
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7140
7169
|
}
|
|
@@ -7535,7 +7564,7 @@ function createToolUsageRule(options) {
|
|
|
7535
7564
|
category: "tool",
|
|
7536
7565
|
defaultSeverity: "error",
|
|
7537
7566
|
evaluate(context) {
|
|
7538
|
-
const tools =
|
|
7567
|
+
const tools = toolInvocationEvents(context);
|
|
7539
7568
|
const names = tools.map(toolName);
|
|
7540
7569
|
const nameSet = new Set(names);
|
|
7541
7570
|
const findings = [];
|
|
@@ -8185,12 +8214,24 @@ function createBaselineRegressionRule(options) {
|
|
|
8185
8214
|
}
|
|
8186
8215
|
function createObservedOutcomeRule(options = {}) {
|
|
8187
8216
|
const failOn = options.failOn ?? ["failed"];
|
|
8217
|
+
const requireAny = options.requireAny === true;
|
|
8188
8218
|
return {
|
|
8189
8219
|
id: "outcome.status",
|
|
8190
8220
|
category: "run",
|
|
8191
8221
|
defaultSeverity: "error",
|
|
8192
8222
|
evaluate(context) {
|
|
8193
8223
|
const outcomes = extractOutcomesFromPersistedEvents(context.events);
|
|
8224
|
+
if (requireAny && outcomes.length === 0) {
|
|
8225
|
+
return [
|
|
8226
|
+
failFinding(
|
|
8227
|
+
"outcome.status",
|
|
8228
|
+
"Expected at least one observed outcome.",
|
|
8229
|
+
runEvidence(context.selectedRun),
|
|
8230
|
+
{ requireAny: true, expected: "at least one observed outcome" },
|
|
8231
|
+
{ code: "outcome.missing", actual: 0 }
|
|
8232
|
+
)
|
|
8233
|
+
];
|
|
8234
|
+
}
|
|
8194
8235
|
const matching = outcomesMatchingStatus(outcomes, failOn);
|
|
8195
8236
|
if (matching.length === 0) return [];
|
|
8196
8237
|
return [
|
|
@@ -8226,6 +8267,18 @@ function runTraceChecks(input3, options = {}) {
|
|
|
8226
8267
|
if (rules.diagnostics.length > 0) {
|
|
8227
8268
|
return errorResult(input3, rules.diagnostics, selected.run);
|
|
8228
8269
|
}
|
|
8270
|
+
if (rules.rules.length === 0) {
|
|
8271
|
+
return errorResult(
|
|
8272
|
+
input3,
|
|
8273
|
+
[
|
|
8274
|
+
diagnostic3(
|
|
8275
|
+
"AI_CHECK_NO_RULES_EVALUATED",
|
|
8276
|
+
"No trace check rules were evaluated. Configure at least one rule, contract, or CLI check option."
|
|
8277
|
+
)
|
|
8278
|
+
],
|
|
8279
|
+
selected.run
|
|
8280
|
+
);
|
|
8281
|
+
}
|
|
8229
8282
|
const facts = buildFacts2(input3, selected.run);
|
|
8230
8283
|
const context = {
|
|
8231
8284
|
...facts,
|
|
@@ -8234,22 +8287,38 @@ function runTraceChecks(input3, options = {}) {
|
|
|
8234
8287
|
};
|
|
8235
8288
|
const diagnostics = [];
|
|
8236
8289
|
const findings = [];
|
|
8290
|
+
const ruleExecutions = [];
|
|
8237
8291
|
for (const rule of rules.rules) {
|
|
8238
8292
|
try {
|
|
8239
|
-
|
|
8293
|
+
const ruleFindings = rule.evaluate(context).map((finding) => normalizeFinding(rule, finding));
|
|
8294
|
+
findings.push(...ruleFindings);
|
|
8295
|
+
ruleExecutions.push({
|
|
8296
|
+
ruleId: rule.id,
|
|
8297
|
+
category: rule.category,
|
|
8298
|
+
status: classifyRuleExecution(ruleFindings, false),
|
|
8299
|
+
findingCount: ruleFindings.length,
|
|
8300
|
+
...selected.run ? { runId: selected.run.runId } : {}
|
|
8301
|
+
});
|
|
8240
8302
|
} catch (error) {
|
|
8241
8303
|
const message = error instanceof Error ? error.message : String(error);
|
|
8242
8304
|
diagnostics.push(
|
|
8243
8305
|
diagnostic3("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
|
|
8244
8306
|
);
|
|
8307
|
+
ruleExecutions.push({
|
|
8308
|
+
ruleId: rule.id,
|
|
8309
|
+
category: rule.category,
|
|
8310
|
+
status: "error",
|
|
8311
|
+
findingCount: 0,
|
|
8312
|
+
...selected.run ? { runId: selected.run.runId } : {}
|
|
8313
|
+
});
|
|
8245
8314
|
}
|
|
8246
8315
|
}
|
|
8247
8316
|
if (diagnostics.length > 0) {
|
|
8248
|
-
return errorResult(input3, diagnostics, selected.run);
|
|
8317
|
+
return errorResult(input3, diagnostics, selected.run, ruleExecutions);
|
|
8249
8318
|
}
|
|
8250
8319
|
const eventById = new Map(input3.read.events.map((event) => [event.eventId, event]));
|
|
8251
8320
|
const sortedFindings = findings.sort(compareFindings(eventById));
|
|
8252
|
-
const summary = summarize(sortedFindings, diagnostics);
|
|
8321
|
+
const summary = summarize(sortedFindings, diagnostics, ruleExecutions.length);
|
|
8253
8322
|
const status = summary.failed > 0 ? "fail" : "pass";
|
|
8254
8323
|
return {
|
|
8255
8324
|
ok: status === "pass",
|
|
@@ -8258,7 +8327,8 @@ function runTraceChecks(input3, options = {}) {
|
|
|
8258
8327
|
...selected.run ? { runId: selected.run.runId } : {},
|
|
8259
8328
|
summary,
|
|
8260
8329
|
findings: sortedFindings,
|
|
8261
|
-
diagnostics
|
|
8330
|
+
diagnostics,
|
|
8331
|
+
ruleExecutions
|
|
8262
8332
|
};
|
|
8263
8333
|
}
|
|
8264
8334
|
var SEVERITY_RANK, STATUS_RANK, CONFIDENCE_RANK, DEFAULT_SENSITIVE_KEYS, DEFAULT_RAW_CONTENT_KEYS, DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES, SAFE_USAGE_LEAF_KEYS, DEFAULT_SECRET_PATTERNS;
|
|
@@ -10707,8 +10777,7 @@ async function runSuiteCase(suiteCase, config, options) {
|
|
|
10707
10777
|
status: "pass",
|
|
10708
10778
|
format: read.format,
|
|
10709
10779
|
findings: [],
|
|
10710
|
-
diagnostics: []
|
|
10711
|
-
};
|
|
10780
|
+
diagnostics: []};
|
|
10712
10781
|
const observationResult = validateExpectedObservations(suiteCase, read);
|
|
10713
10782
|
const diagnostics = [
|
|
10714
10783
|
...checkResult.diagnostics.map(
|
|
@@ -12511,7 +12580,7 @@ var init_src = __esm({
|
|
|
12511
12580
|
});
|
|
12512
12581
|
|
|
12513
12582
|
// package.json
|
|
12514
|
-
var version = "6.17.
|
|
12583
|
+
var version = "6.17.5";
|
|
12515
12584
|
|
|
12516
12585
|
// packages/cli/src/list.ts
|
|
12517
12586
|
init_advanced();
|
|
@@ -14080,6 +14149,11 @@ async function followFile(filePath, options, onLine, shouldStop) {
|
|
|
14080
14149
|
const msg = e instanceof Error ? e.message : String(e);
|
|
14081
14150
|
throw new Error(`Failed to stat file: ${filePath} (${msg})`);
|
|
14082
14151
|
}
|
|
14152
|
+
if (next.size < pos) {
|
|
14153
|
+
pos = 0;
|
|
14154
|
+
carry = "";
|
|
14155
|
+
lineNumber = 0;
|
|
14156
|
+
}
|
|
14083
14157
|
if (next.size > pos) {
|
|
14084
14158
|
const fh = await promises.open(filePath, "r");
|
|
14085
14159
|
try {
|
|
@@ -17744,8 +17818,11 @@ function checkResultToEvidenceJson(result, runIds) {
|
|
|
17744
17818
|
status,
|
|
17745
17819
|
errors: result.summary.errors,
|
|
17746
17820
|
warnings: result.summary.warnings,
|
|
17747
|
-
findings: result.findings.length
|
|
17821
|
+
findings: result.findings.length,
|
|
17822
|
+
rulesEvaluated: result.summary.rulesEvaluated ?? 0
|
|
17748
17823
|
})),
|
|
17824
|
+
rulesEvaluated: result.summary.rulesEvaluated ?? 0,
|
|
17825
|
+
evaluatedRuleIds: (result.ruleExecutions ?? []).map((item) => item.ruleId),
|
|
17749
17826
|
findings: result.findings,
|
|
17750
17827
|
diagnostics: result.diagnostics
|
|
17751
17828
|
});
|
|
@@ -18370,9 +18447,11 @@ function mergeSafetyExtensions(result, read, options) {
|
|
|
18370
18447
|
passed,
|
|
18371
18448
|
failed,
|
|
18372
18449
|
warnings,
|
|
18373
|
-
errors: result.summary.errors
|
|
18450
|
+
errors: result.summary.errors,
|
|
18451
|
+
rulesEvaluated: result.summary.rulesEvaluated ?? 0
|
|
18374
18452
|
},
|
|
18375
|
-
findings
|
|
18453
|
+
findings,
|
|
18454
|
+
ruleExecutions: result.ruleExecutions ?? []
|
|
18376
18455
|
};
|
|
18377
18456
|
}
|
|
18378
18457
|
|
|
@@ -18492,10 +18571,12 @@ function errorResult2(code, message, format = "unknown") {
|
|
|
18492
18571
|
passed: 0,
|
|
18493
18572
|
failed: 0,
|
|
18494
18573
|
warnings: 0,
|
|
18495
|
-
errors: 1
|
|
18574
|
+
errors: 1,
|
|
18575
|
+
rulesEvaluated: 0
|
|
18496
18576
|
},
|
|
18497
18577
|
findings: [],
|
|
18498
|
-
diagnostics
|
|
18578
|
+
diagnostics,
|
|
18579
|
+
ruleExecutions: []
|
|
18499
18580
|
};
|
|
18500
18581
|
}
|
|
18501
18582
|
function parseNumber(value, label) {
|
|
@@ -18513,36 +18594,329 @@ function asStringArray2(value) {
|
|
|
18513
18594
|
}
|
|
18514
18595
|
return value;
|
|
18515
18596
|
}
|
|
18516
|
-
|
|
18597
|
+
var CHECKS_KEYS = /* @__PURE__ */ new Set(["select", "run", "tool", "llm", "structure", "safety"]);
|
|
18598
|
+
var RUN_KEYS = /* @__PURE__ */ new Set(["expected", "allowIncomplete", "maxDurationMs", "maxDepth"]);
|
|
18599
|
+
var TOOL_KEYS = /* @__PURE__ */ new Set(["required", "forbidden", "allowed", "minCount", "maxCount"]);
|
|
18600
|
+
var LLM_KEYS = /* @__PURE__ */ new Set([
|
|
18601
|
+
"allowedModels",
|
|
18602
|
+
"allowedProviders",
|
|
18603
|
+
"finishReasons",
|
|
18604
|
+
"maxCalls",
|
|
18605
|
+
"maxTotalTokens",
|
|
18606
|
+
"maxInputTokens",
|
|
18607
|
+
"maxOutputTokens",
|
|
18608
|
+
"maxCachedTokens"
|
|
18609
|
+
]);
|
|
18610
|
+
var STRUCTURE_KEYS = /* @__PURE__ */ new Set([
|
|
18611
|
+
"minConfidence",
|
|
18612
|
+
"requireParentBeforeChild",
|
|
18613
|
+
"requireTraceParentSpan",
|
|
18614
|
+
"orphan",
|
|
18615
|
+
"cycle",
|
|
18616
|
+
"maxChildren",
|
|
18617
|
+
"maxConcurrent"
|
|
18618
|
+
]);
|
|
18619
|
+
var SAFETY_KEYS = /* @__PURE__ */ new Set([
|
|
18620
|
+
"redaction",
|
|
18621
|
+
"rawContent",
|
|
18622
|
+
"secretPattern",
|
|
18623
|
+
"maxStringLength",
|
|
18624
|
+
"maxArrayLength",
|
|
18625
|
+
"maxObjectKeys",
|
|
18626
|
+
"maxSerializedBytes",
|
|
18627
|
+
"maxFindings"
|
|
18628
|
+
]);
|
|
18629
|
+
var RUN_EXPECTED = /* @__PURE__ */ new Set(["ok", "error", "running"]);
|
|
18630
|
+
var CONFIDENCE_VALUES = /* @__PURE__ */ new Set(["exact", "high", "medium", "low", "inferred"]);
|
|
18631
|
+
var CheckConfigError = class extends Error {
|
|
18632
|
+
code;
|
|
18633
|
+
constructor(code, message) {
|
|
18634
|
+
super(message);
|
|
18635
|
+
this.name = "CheckConfigError";
|
|
18636
|
+
this.code = code;
|
|
18637
|
+
}
|
|
18638
|
+
};
|
|
18639
|
+
function closestKey(unknown, known) {
|
|
18640
|
+
const lower = unknown.toLowerCase();
|
|
18641
|
+
let best;
|
|
18642
|
+
let bestDistance = Number.POSITIVE_INFINITY;
|
|
18643
|
+
for (const candidate of known) {
|
|
18644
|
+
if (candidate.toLowerCase() === lower) return candidate;
|
|
18645
|
+
let distance = Math.abs(candidate.length - unknown.length);
|
|
18646
|
+
const max = Math.max(candidate.length, unknown.length);
|
|
18647
|
+
for (let i = 0; i < Math.min(candidate.length, unknown.length); i += 1) {
|
|
18648
|
+
if (candidate[i].toLowerCase() !== unknown[i].toLowerCase()) distance += 1;
|
|
18649
|
+
}
|
|
18650
|
+
if (distance < bestDistance && distance <= Math.max(2, Math.floor(max / 3))) {
|
|
18651
|
+
bestDistance = distance;
|
|
18652
|
+
best = candidate;
|
|
18653
|
+
}
|
|
18654
|
+
}
|
|
18655
|
+
return best;
|
|
18656
|
+
}
|
|
18657
|
+
function rejectUnknownKeys(record, allowed, pathPrefix) {
|
|
18658
|
+
for (const key of Object.keys(record)) {
|
|
18659
|
+
if (allowed.has(key)) continue;
|
|
18660
|
+
const suggestion = closestKey(key, [...allowed]);
|
|
18661
|
+
const hint = suggestion ? ` Did you mean "${suggestion}"?` : "";
|
|
18662
|
+
throw new CheckConfigError(
|
|
18663
|
+
"AI_CHECK_CONFIG_UNKNOWN_KEY",
|
|
18664
|
+
`Unknown check config key "${pathPrefix}.${key}".${hint}`
|
|
18665
|
+
);
|
|
18666
|
+
}
|
|
18667
|
+
}
|
|
18668
|
+
function requireObject(value, pathPrefix) {
|
|
18669
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
18670
|
+
throw new CheckConfigError(
|
|
18671
|
+
"AI_CHECK_CONFIG_INVALID_VALUE",
|
|
18672
|
+
`${pathPrefix} must be an object.`
|
|
18673
|
+
);
|
|
18674
|
+
}
|
|
18675
|
+
return value;
|
|
18676
|
+
}
|
|
18677
|
+
function requireStringArray(value, pathPrefix) {
|
|
18678
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.trim() === "")) {
|
|
18679
|
+
throw new CheckConfigError(
|
|
18680
|
+
"AI_CHECK_CONFIG_INVALID_VALUE",
|
|
18681
|
+
`${pathPrefix} must be an array of non-empty strings.`
|
|
18682
|
+
);
|
|
18683
|
+
}
|
|
18684
|
+
return value;
|
|
18685
|
+
}
|
|
18686
|
+
function requireNonNegativeNumber(value, pathPrefix) {
|
|
18687
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
18688
|
+
throw new CheckConfigError(
|
|
18689
|
+
"AI_CHECK_CONFIG_INVALID_VALUE",
|
|
18690
|
+
`${pathPrefix} must be a finite non-negative number.`
|
|
18691
|
+
);
|
|
18692
|
+
}
|
|
18693
|
+
return value;
|
|
18694
|
+
}
|
|
18695
|
+
function requireBoolean(value, pathPrefix) {
|
|
18696
|
+
if (typeof value !== "boolean") {
|
|
18697
|
+
throw new CheckConfigError(
|
|
18698
|
+
"AI_CHECK_CONFIG_INVALID_VALUE",
|
|
18699
|
+
`${pathPrefix} must be a boolean.`
|
|
18700
|
+
);
|
|
18701
|
+
}
|
|
18702
|
+
return value;
|
|
18703
|
+
}
|
|
18704
|
+
function parseRunSection(value) {
|
|
18705
|
+
const record = requireObject(value, "checks.run");
|
|
18706
|
+
rejectUnknownKeys(record, RUN_KEYS, "checks.run");
|
|
18707
|
+
const out = {};
|
|
18708
|
+
if (record.expected !== void 0) {
|
|
18709
|
+
if (typeof record.expected !== "string" || !RUN_EXPECTED.has(record.expected)) {
|
|
18710
|
+
throw new CheckConfigError(
|
|
18711
|
+
"AI_CHECK_CONFIG_INVALID_VALUE",
|
|
18712
|
+
`checks.run.expected must be one of: ok, error, running.`
|
|
18713
|
+
);
|
|
18714
|
+
}
|
|
18715
|
+
out.expected = record.expected;
|
|
18716
|
+
}
|
|
18717
|
+
if (record.allowIncomplete !== void 0) {
|
|
18718
|
+
out.allowIncomplete = requireBoolean(record.allowIncomplete, "checks.run.allowIncomplete");
|
|
18719
|
+
}
|
|
18720
|
+
if (record.maxDurationMs !== void 0) {
|
|
18721
|
+
out.maxDurationMs = requireNonNegativeNumber(record.maxDurationMs, "checks.run.maxDurationMs");
|
|
18722
|
+
}
|
|
18723
|
+
if (record.maxDepth !== void 0) {
|
|
18724
|
+
out.maxDepth = requireNonNegativeNumber(record.maxDepth, "checks.run.maxDepth");
|
|
18725
|
+
}
|
|
18726
|
+
return out;
|
|
18727
|
+
}
|
|
18728
|
+
function parseToolSection(value) {
|
|
18729
|
+
const record = requireObject(value, "checks.tool");
|
|
18730
|
+
rejectUnknownKeys(record, TOOL_KEYS, "checks.tool");
|
|
18731
|
+
const out = {};
|
|
18732
|
+
if (record.required !== void 0) out.required = requireStringArray(record.required, "checks.tool.required");
|
|
18733
|
+
if (record.forbidden !== void 0) out.forbidden = requireStringArray(record.forbidden, "checks.tool.forbidden");
|
|
18734
|
+
if (record.allowed !== void 0) out.allowed = requireStringArray(record.allowed, "checks.tool.allowed");
|
|
18735
|
+
if (record.minCount !== void 0) out.minCount = requireNonNegativeNumber(record.minCount, "checks.tool.minCount");
|
|
18736
|
+
if (record.maxCount !== void 0) out.maxCount = requireNonNegativeNumber(record.maxCount, "checks.tool.maxCount");
|
|
18737
|
+
return out;
|
|
18738
|
+
}
|
|
18739
|
+
function parseLlmSection(value) {
|
|
18740
|
+
const record = requireObject(value, "checks.llm");
|
|
18741
|
+
rejectUnknownKeys(record, LLM_KEYS, "checks.llm");
|
|
18742
|
+
const out = {};
|
|
18743
|
+
if (record.allowedModels !== void 0) {
|
|
18744
|
+
out.allowedModels = requireStringArray(record.allowedModels, "checks.llm.allowedModels");
|
|
18745
|
+
}
|
|
18746
|
+
if (record.allowedProviders !== void 0) {
|
|
18747
|
+
out.allowedProviders = requireStringArray(record.allowedProviders, "checks.llm.allowedProviders");
|
|
18748
|
+
}
|
|
18749
|
+
if (record.finishReasons !== void 0) {
|
|
18750
|
+
out.finishReasons = requireStringArray(record.finishReasons, "checks.llm.finishReasons");
|
|
18751
|
+
}
|
|
18752
|
+
if (record.maxCalls !== void 0) out.maxCalls = requireNonNegativeNumber(record.maxCalls, "checks.llm.maxCalls");
|
|
18753
|
+
if (record.maxTotalTokens !== void 0) {
|
|
18754
|
+
out.maxTotalTokens = requireNonNegativeNumber(record.maxTotalTokens, "checks.llm.maxTotalTokens");
|
|
18755
|
+
}
|
|
18756
|
+
if (record.maxInputTokens !== void 0) {
|
|
18757
|
+
out.maxInputTokens = requireNonNegativeNumber(record.maxInputTokens, "checks.llm.maxInputTokens");
|
|
18758
|
+
}
|
|
18759
|
+
if (record.maxOutputTokens !== void 0) {
|
|
18760
|
+
out.maxOutputTokens = requireNonNegativeNumber(record.maxOutputTokens, "checks.llm.maxOutputTokens");
|
|
18761
|
+
}
|
|
18762
|
+
if (record.maxCachedTokens !== void 0) {
|
|
18763
|
+
out.maxCachedTokens = requireNonNegativeNumber(record.maxCachedTokens, "checks.llm.maxCachedTokens");
|
|
18764
|
+
}
|
|
18765
|
+
return out;
|
|
18766
|
+
}
|
|
18767
|
+
function parseStructureSection(value) {
|
|
18768
|
+
const record = requireObject(value, "checks.structure");
|
|
18769
|
+
rejectUnknownKeys(record, STRUCTURE_KEYS, "checks.structure");
|
|
18770
|
+
const out = {};
|
|
18771
|
+
if (record.minConfidence !== void 0) {
|
|
18772
|
+
if (typeof record.minConfidence !== "string" || !CONFIDENCE_VALUES.has(record.minConfidence)) {
|
|
18773
|
+
throw new CheckConfigError(
|
|
18774
|
+
"AI_CHECK_CONFIG_INVALID_VALUE",
|
|
18775
|
+
`checks.structure.minConfidence must be one of: exact, high, medium, low, inferred.`
|
|
18776
|
+
);
|
|
18777
|
+
}
|
|
18778
|
+
out.minConfidence = record.minConfidence;
|
|
18779
|
+
}
|
|
18780
|
+
if (record.requireParentBeforeChild !== void 0) {
|
|
18781
|
+
out.requireParentBeforeChild = requireBoolean(
|
|
18782
|
+
record.requireParentBeforeChild,
|
|
18783
|
+
"checks.structure.requireParentBeforeChild"
|
|
18784
|
+
);
|
|
18785
|
+
}
|
|
18786
|
+
if (record.requireTraceParentSpan !== void 0) {
|
|
18787
|
+
out.requireTraceParentSpan = requireBoolean(
|
|
18788
|
+
record.requireTraceParentSpan,
|
|
18789
|
+
"checks.structure.requireTraceParentSpan"
|
|
18790
|
+
);
|
|
18791
|
+
}
|
|
18792
|
+
if (record.orphan !== void 0) out.orphan = requireBoolean(record.orphan, "checks.structure.orphan");
|
|
18793
|
+
if (record.cycle !== void 0) out.cycle = requireBoolean(record.cycle, "checks.structure.cycle");
|
|
18794
|
+
if (record.maxChildren !== void 0) {
|
|
18795
|
+
out.maxChildren = requireNonNegativeNumber(record.maxChildren, "checks.structure.maxChildren");
|
|
18796
|
+
}
|
|
18797
|
+
if (record.maxConcurrent !== void 0) {
|
|
18798
|
+
out.maxConcurrent = requireNonNegativeNumber(record.maxConcurrent, "checks.structure.maxConcurrent");
|
|
18799
|
+
}
|
|
18800
|
+
return out;
|
|
18801
|
+
}
|
|
18802
|
+
function parseSafetySection(value) {
|
|
18803
|
+
const record = requireObject(value, "checks.safety");
|
|
18804
|
+
rejectUnknownKeys(record, SAFETY_KEYS, "checks.safety");
|
|
18805
|
+
const out = {};
|
|
18806
|
+
if (record.redaction !== void 0) out.redaction = requireBoolean(record.redaction, "checks.safety.redaction");
|
|
18807
|
+
if (record.rawContent !== void 0) out.rawContent = requireBoolean(record.rawContent, "checks.safety.rawContent");
|
|
18808
|
+
if (record.secretPattern !== void 0) {
|
|
18809
|
+
out.secretPattern = requireBoolean(record.secretPattern, "checks.safety.secretPattern");
|
|
18810
|
+
}
|
|
18811
|
+
if (record.maxStringLength !== void 0) {
|
|
18812
|
+
out.maxStringLength = requireNonNegativeNumber(record.maxStringLength, "checks.safety.maxStringLength");
|
|
18813
|
+
}
|
|
18814
|
+
if (record.maxArrayLength !== void 0) {
|
|
18815
|
+
out.maxArrayLength = requireNonNegativeNumber(record.maxArrayLength, "checks.safety.maxArrayLength");
|
|
18816
|
+
}
|
|
18817
|
+
if (record.maxObjectKeys !== void 0) {
|
|
18818
|
+
out.maxObjectKeys = requireNonNegativeNumber(record.maxObjectKeys, "checks.safety.maxObjectKeys");
|
|
18819
|
+
}
|
|
18820
|
+
if (record.maxSerializedBytes !== void 0) {
|
|
18821
|
+
out.maxSerializedBytes = requireNonNegativeNumber(
|
|
18822
|
+
record.maxSerializedBytes,
|
|
18823
|
+
"checks.safety.maxSerializedBytes"
|
|
18824
|
+
);
|
|
18825
|
+
}
|
|
18826
|
+
if (record.maxFindings !== void 0) {
|
|
18827
|
+
out.maxFindings = requireNonNegativeNumber(record.maxFindings, "checks.safety.maxFindings");
|
|
18828
|
+
}
|
|
18829
|
+
return out;
|
|
18830
|
+
}
|
|
18831
|
+
function sectionHasEffect(section) {
|
|
18832
|
+
if (section === void 0) return false;
|
|
18833
|
+
return Object.keys(section).length > 0;
|
|
18834
|
+
}
|
|
18835
|
+
function checkConfigHasEffect(config) {
|
|
18836
|
+
const checks2 = config.checks;
|
|
18837
|
+
if (checks2 === void 0) return false;
|
|
18838
|
+
if ((checks2.select?.length ?? 0) > 0) return true;
|
|
18839
|
+
if (sectionHasEffect(checks2.run)) return true;
|
|
18840
|
+
if (sectionHasEffect(checks2.tool)) return true;
|
|
18841
|
+
if (sectionHasEffect(checks2.llm)) return true;
|
|
18842
|
+
if (sectionHasEffect(checks2.structure)) return true;
|
|
18843
|
+
if (sectionHasEffect(checks2.safety)) return true;
|
|
18844
|
+
return false;
|
|
18845
|
+
}
|
|
18846
|
+
function parseCheckConfig(value) {
|
|
18517
18847
|
if (value === void 0 || value === null) return {};
|
|
18518
18848
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
18519
|
-
throw new
|
|
18849
|
+
throw new CheckConfigError("AI_CHECK_CONFIG_INVALID_VALUE", "Config must export an object.");
|
|
18520
18850
|
}
|
|
18521
|
-
|
|
18851
|
+
const root = value;
|
|
18852
|
+
if ("contract" in root) {
|
|
18853
|
+
throw new CheckConfigError(
|
|
18854
|
+
"AI_CHECK_CONFIG_UNKNOWN_KEY",
|
|
18855
|
+
'Top-level "contract" is not supported by `agent-inspect check --config`. Use the TraceContract TypeScript API (`defineTraceContract` / `evaluateTraceContract` from `agent-inspect/checks`), or express tool/run/llm rules under `checks`.'
|
|
18856
|
+
);
|
|
18857
|
+
}
|
|
18858
|
+
const rootKeys = Object.keys(root);
|
|
18859
|
+
for (const key of rootKeys) {
|
|
18860
|
+
if (key === "checks") continue;
|
|
18861
|
+
const suggestion = closestKey(key, ["checks"]);
|
|
18862
|
+
const hint = suggestion ? ` Did you mean "${suggestion}"?` : "";
|
|
18863
|
+
throw new CheckConfigError(
|
|
18864
|
+
"AI_CHECK_CONFIG_UNKNOWN_KEY",
|
|
18865
|
+
`Unknown top-level check config key "${key}". Allowed: checks.${hint}`
|
|
18866
|
+
);
|
|
18867
|
+
}
|
|
18868
|
+
if (!("checks" in root)) return {};
|
|
18869
|
+
const checksRecord = requireObject(root.checks, "checks");
|
|
18870
|
+
rejectUnknownKeys(checksRecord, CHECKS_KEYS, "checks");
|
|
18871
|
+
const checks2 = {};
|
|
18872
|
+
if (checksRecord.select !== void 0) {
|
|
18873
|
+
checks2.select = requireStringArray(checksRecord.select, "checks.select");
|
|
18874
|
+
}
|
|
18875
|
+
if (checksRecord.run !== void 0) checks2.run = parseRunSection(checksRecord.run);
|
|
18876
|
+
if (checksRecord.tool !== void 0) checks2.tool = parseToolSection(checksRecord.tool);
|
|
18877
|
+
if (checksRecord.llm !== void 0) checks2.llm = parseLlmSection(checksRecord.llm);
|
|
18878
|
+
if (checksRecord.structure !== void 0) {
|
|
18879
|
+
checks2.structure = parseStructureSection(checksRecord.structure);
|
|
18880
|
+
}
|
|
18881
|
+
if (checksRecord.safety !== void 0) checks2.safety = parseSafetySection(checksRecord.safety);
|
|
18882
|
+
return { checks: checks2 };
|
|
18522
18883
|
}
|
|
18523
18884
|
async function loadConfig(configPath) {
|
|
18524
18885
|
if (configPath === void 0) return {};
|
|
18525
18886
|
const extension = path32__default.default.extname(configPath);
|
|
18526
18887
|
if (TS_CONFIG_EXTENSIONS2.has(extension)) {
|
|
18527
|
-
throw new
|
|
18888
|
+
throw new CheckConfigError(
|
|
18889
|
+
"AI_CHECK_CONFIG_LOAD_FAILED",
|
|
18528
18890
|
"TypeScript check configs require an explicit precompiled JavaScript config or future --config-loader support."
|
|
18529
18891
|
);
|
|
18530
18892
|
}
|
|
18531
18893
|
if (!CONFIG_EXTENSIONS2.has(extension)) {
|
|
18532
|
-
throw new
|
|
18894
|
+
throw new CheckConfigError(
|
|
18895
|
+
"AI_CHECK_CONFIG_LOAD_FAILED",
|
|
18896
|
+
"Unsupported check config extension. Use .json, .js, .mjs, or .cjs."
|
|
18897
|
+
);
|
|
18533
18898
|
}
|
|
18534
18899
|
const absolute = path32__default.default.resolve(configPath);
|
|
18535
|
-
|
|
18536
|
-
|
|
18537
|
-
|
|
18900
|
+
try {
|
|
18901
|
+
if (extension === ".json") {
|
|
18902
|
+
const raw = await promises.readFile(absolute, "utf-8");
|
|
18903
|
+
return parseCheckConfig(JSON.parse(raw));
|
|
18904
|
+
}
|
|
18905
|
+
const mod = await import(url.pathToFileURL(absolute).href);
|
|
18906
|
+
return parseCheckConfig("default" in mod ? mod.default : mod);
|
|
18907
|
+
} catch (error) {
|
|
18908
|
+
if (error instanceof CheckConfigError) throw error;
|
|
18909
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18910
|
+
throw new CheckConfigError(
|
|
18911
|
+
"AI_CHECK_CONFIG_LOAD_FAILED",
|
|
18912
|
+
`Failed to load check config: ${message}`
|
|
18913
|
+
);
|
|
18538
18914
|
}
|
|
18539
|
-
const mod = await import(url.pathToFileURL(absolute).href);
|
|
18540
|
-
return asConfig("default" in mod ? mod.default : mod);
|
|
18541
18915
|
}
|
|
18542
18916
|
function normalizeConfig(config) {
|
|
18543
18917
|
if (config.checks === void 0) return {};
|
|
18544
18918
|
if (typeof config.checks !== "object" || Array.isArray(config.checks)) {
|
|
18545
|
-
throw new
|
|
18919
|
+
throw new CheckConfigError("AI_CHECK_CONFIG_INVALID_VALUE", "checks config must be an object.");
|
|
18546
18920
|
}
|
|
18547
18921
|
return config.checks;
|
|
18548
18922
|
}
|
|
@@ -18620,7 +18994,7 @@ function buildRules(config, options, presetSelect = []) {
|
|
|
18620
18994
|
}
|
|
18621
18995
|
throw new Error(`unsupported status "${value}"`);
|
|
18622
18996
|
});
|
|
18623
|
-
rules.push(createObservedOutcomeRule({ failOn: statuses }));
|
|
18997
|
+
rules.push(createObservedOutcomeRule({ failOn: statuses, requireAny: true }));
|
|
18624
18998
|
} catch (error) {
|
|
18625
18999
|
const message = error instanceof Error ? error.message : String(error);
|
|
18626
19000
|
diagnostics.push(
|
|
@@ -18692,7 +19066,7 @@ function exitCodeFor(result) {
|
|
|
18692
19066
|
return 3;
|
|
18693
19067
|
}
|
|
18694
19068
|
if (codes.some(
|
|
18695
|
-
(code) => code === "AI_CHECK_INVALID_ARGUMENTS" || code === "AI_CHECK_INVALID_CONFIG" || code === "AI_CHECK_CONFIG_LOAD_FAILED" || code === "AI_CHECK_RUN_SELECTION_REQUIRED"
|
|
19069
|
+
(code) => code === "AI_CHECK_INVALID_ARGUMENTS" || code === "AI_CHECK_INVALID_CONFIG" || code === "AI_CHECK_CONFIG_LOAD_FAILED" || code === "AI_CHECK_CONFIG_UNKNOWN_KEY" || code === "AI_CHECK_CONFIG_INVALID_VALUE" || code === "AI_CHECK_CONFIG_NO_EFFECTIVE_RULES" || code === "AI_CHECK_NO_RULES_EVALUATED" || code === "AI_CHECK_RUN_SELECTION_REQUIRED"
|
|
18696
19070
|
)) {
|
|
18697
19071
|
return 2;
|
|
18698
19072
|
}
|
|
@@ -18759,6 +19133,7 @@ function printHuman(result, options = {}) {
|
|
|
18759
19133
|
console.log(
|
|
18760
19134
|
`Summary: ${result.summary.failed} failed, ${result.summary.warnings} warning(s), ${result.summary.errors} error(s)`
|
|
18761
19135
|
);
|
|
19136
|
+
console.log(`Rules evaluated: ${result.summary.rulesEvaluated ?? result.ruleExecutions?.length ?? 0}`);
|
|
18762
19137
|
for (const diagnostic7 of result.diagnostics) {
|
|
18763
19138
|
console.log(`- ${diagnostic7.code}: ${diagnostic7.message}`);
|
|
18764
19139
|
}
|
|
@@ -18790,6 +19165,16 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
|
|
|
18790
19165
|
const useSessionScope = Boolean(sessionId || groupId);
|
|
18791
19166
|
try {
|
|
18792
19167
|
let config = await loadConfig(options.config);
|
|
19168
|
+
if (options.config !== void 0 && !checkConfigHasEffect(config)) {
|
|
19169
|
+
result = errorResult2(
|
|
19170
|
+
"AI_CHECK_CONFIG_NO_EFFECTIVE_RULES",
|
|
19171
|
+
"Explicit --config has no effective check rules. Configure checks.select, checks.run, checks.tool, checks.llm, checks.structure, or checks.safety."
|
|
19172
|
+
);
|
|
19173
|
+
if (options.json) printJson(result);
|
|
19174
|
+
else printHuman(result, options);
|
|
19175
|
+
process.exitCode = exitCodeFor(result);
|
|
19176
|
+
return;
|
|
19177
|
+
}
|
|
18793
19178
|
let effectiveOptions = options;
|
|
18794
19179
|
const resolved = resolvePreset(options.preset, {
|
|
18795
19180
|
hasToolRules: hasToolRulesConfigured(config, options)
|
|
@@ -18909,12 +19294,13 @@ async function checkCommand(target, options = {}, stdin = process.stdin) {
|
|
|
18909
19294
|
}
|
|
18910
19295
|
} catch (error) {
|
|
18911
19296
|
if (phase === "config") {
|
|
18912
|
-
|
|
18913
|
-
|
|
18914
|
-
|
|
18915
|
-
|
|
18916
|
-
message
|
|
18917
|
-
|
|
19297
|
+
if (error instanceof CheckConfigError) {
|
|
19298
|
+
result = errorResult2(error.code, error.message);
|
|
19299
|
+
} else {
|
|
19300
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
19301
|
+
const code = message.startsWith("--") || message.includes("Unknown --preset") ? "AI_CHECK_INVALID_ARGUMENTS" : error instanceof SyntaxError || message.includes("Unsupported check config extension") || message.includes("TypeScript check configs") || message.includes("Config must") || message.includes("checks config") || message.includes("Expected an array") ? "AI_CHECK_INVALID_CONFIG" : "AI_CHECK_CONFIG_LOAD_FAILED";
|
|
19302
|
+
result = errorResult2(code, message);
|
|
19303
|
+
}
|
|
18918
19304
|
} else {
|
|
18919
19305
|
result = readErrorResult(error);
|
|
18920
19306
|
}
|
|
@@ -19845,6 +20231,42 @@ function startViewerServer(options = {}) {
|
|
|
19845
20231
|
});
|
|
19846
20232
|
});
|
|
19847
20233
|
}
|
|
20234
|
+
function openInDefaultBrowser(target) {
|
|
20235
|
+
const platform = process.platform;
|
|
20236
|
+
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
20237
|
+
const args = platform === "win32" ? ["/c", "start", "", target] : [target];
|
|
20238
|
+
return new Promise((resolve) => {
|
|
20239
|
+
try {
|
|
20240
|
+
const child = child_process.spawn(command, args, {
|
|
20241
|
+
detached: true,
|
|
20242
|
+
stdio: "ignore",
|
|
20243
|
+
windowsHide: platform === "win32"
|
|
20244
|
+
});
|
|
20245
|
+
child.once("spawn", () => {
|
|
20246
|
+
try {
|
|
20247
|
+
child.unref();
|
|
20248
|
+
resolve({ ok: true });
|
|
20249
|
+
} catch (error) {
|
|
20250
|
+
resolve({
|
|
20251
|
+
ok: false,
|
|
20252
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
20253
|
+
});
|
|
20254
|
+
}
|
|
20255
|
+
});
|
|
20256
|
+
child.once("error", (error) => {
|
|
20257
|
+
resolve({
|
|
20258
|
+
ok: false,
|
|
20259
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
20260
|
+
});
|
|
20261
|
+
});
|
|
20262
|
+
} catch (error) {
|
|
20263
|
+
resolve({
|
|
20264
|
+
ok: false,
|
|
20265
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
20266
|
+
});
|
|
20267
|
+
}
|
|
20268
|
+
});
|
|
20269
|
+
}
|
|
19848
20270
|
|
|
19849
20271
|
// packages/cli/src/serve.ts
|
|
19850
20272
|
function parsePort(value) {
|
|
@@ -19874,9 +20296,11 @@ async function serveCommand(options = {}) {
|
|
|
19874
20296
|
if (options.open === true && host !== "127.0.0.1" && host !== "localhost") {
|
|
19875
20297
|
console.warn("Skipping browser open for non-local host binding.");
|
|
19876
20298
|
} else if (options.open === true) {
|
|
19877
|
-
const
|
|
19878
|
-
|
|
19879
|
-
|
|
20299
|
+
const opened = await openInDefaultBrowser(info.url);
|
|
20300
|
+
if (!opened.ok) {
|
|
20301
|
+
console.warn("Viewer started, but the browser could not be opened automatically.");
|
|
20302
|
+
console.warn(`Open manually: ${info.url}`);
|
|
20303
|
+
}
|
|
19880
20304
|
}
|
|
19881
20305
|
await new Promise(() => {
|
|
19882
20306
|
});
|
|
@@ -20035,9 +20459,11 @@ async function studioCommand(options = {}) {
|
|
|
20035
20459
|
if (options.open === true && info.host !== "127.0.0.1" && info.host !== "localhost") {
|
|
20036
20460
|
console.warn("Skipping browser open for non-local host binding.");
|
|
20037
20461
|
} else if (options.open === true) {
|
|
20038
|
-
const
|
|
20039
|
-
|
|
20040
|
-
|
|
20462
|
+
const opened = await openInDefaultBrowser(info.url);
|
|
20463
|
+
if (!opened.ok) {
|
|
20464
|
+
console.warn("Studio started, but the browser could not be opened automatically.");
|
|
20465
|
+
console.warn(`Open manually: ${info.url}`);
|
|
20466
|
+
}
|
|
20041
20467
|
}
|
|
20042
20468
|
await new Promise(() => {
|
|
20043
20469
|
});
|
|
@@ -20796,7 +21222,7 @@ function asStringArray3(value, label) {
|
|
|
20796
21222
|
}
|
|
20797
21223
|
return value;
|
|
20798
21224
|
}
|
|
20799
|
-
function
|
|
21225
|
+
function asConfig(value) {
|
|
20800
21226
|
if (value === void 0 || value === null) return {};
|
|
20801
21227
|
if (typeof value !== "object" || Array.isArray(value)) {
|
|
20802
21228
|
throw new Error("Config must export an object.");
|
|
@@ -20817,10 +21243,10 @@ async function loadConfig2(configPath) {
|
|
|
20817
21243
|
const absolute = path32__default.default.resolve(configPath);
|
|
20818
21244
|
if (extension === ".json") {
|
|
20819
21245
|
const raw = await promises.readFile(absolute, "utf-8");
|
|
20820
|
-
return
|
|
21246
|
+
return asConfig(JSON.parse(raw));
|
|
20821
21247
|
}
|
|
20822
21248
|
const mod = await import(url.pathToFileURL(absolute).href);
|
|
20823
|
-
return
|
|
21249
|
+
return asConfig("default" in mod ? mod.default : mod);
|
|
20824
21250
|
}
|
|
20825
21251
|
function normalizeConfig2(config) {
|
|
20826
21252
|
if (config.eval === void 0) return {};
|
|
@@ -22335,33 +22761,6 @@ async function resolveHtmlPath(root) {
|
|
|
22335
22761
|
`No evidence.html found under ${root}. Pass a bundle directory or .html path.`
|
|
22336
22762
|
);
|
|
22337
22763
|
}
|
|
22338
|
-
function openLocalFile(filePath) {
|
|
22339
|
-
const fileUrl = url.pathToFileURL(filePath).href;
|
|
22340
|
-
const platform = process.platform;
|
|
22341
|
-
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
22342
|
-
const args = platform === "darwin" ? [filePath] : platform === "win32" ? ["/c", "start", "", filePath] : [filePath];
|
|
22343
|
-
return new Promise((resolve) => {
|
|
22344
|
-
try {
|
|
22345
|
-
const child = child_process.spawn(command, args, {
|
|
22346
|
-
detached: true,
|
|
22347
|
-
stdio: "ignore"
|
|
22348
|
-
});
|
|
22349
|
-
child.on("error", (error) => {
|
|
22350
|
-
resolve({
|
|
22351
|
-
ok: false,
|
|
22352
|
-
detail: `${error instanceof Error ? error.message : String(error)}; open manually: ${fileUrl}`
|
|
22353
|
-
});
|
|
22354
|
-
});
|
|
22355
|
-
child.unref();
|
|
22356
|
-
resolve({ ok: true, detail: fileUrl });
|
|
22357
|
-
} catch (error) {
|
|
22358
|
-
resolve({
|
|
22359
|
-
ok: false,
|
|
22360
|
-
detail: `${error instanceof Error ? error.message : String(error)}; open manually: ${fileUrl}`
|
|
22361
|
-
});
|
|
22362
|
-
}
|
|
22363
|
-
});
|
|
22364
|
-
}
|
|
22365
22764
|
async function bundleOpenCommand(targetPath, options = {}) {
|
|
22366
22765
|
const root = path32__default.default.resolve(targetPath.trim() || ".");
|
|
22367
22766
|
try {
|
|
@@ -22431,23 +22830,24 @@ async function bundleOpenCommand(targetPath, options = {}) {
|
|
|
22431
22830
|
await promises.readFile(path32__default.default.join(path32__default.default.dirname(htmlPath), "evidence.json"), "utf-8");
|
|
22432
22831
|
} catch {
|
|
22433
22832
|
}
|
|
22434
|
-
const
|
|
22833
|
+
const fileUrl = url.pathToFileURL(htmlPath).href;
|
|
22834
|
+
const opened = await openInDefaultBrowser(fileUrl);
|
|
22435
22835
|
if (options.json) {
|
|
22436
22836
|
console.log(
|
|
22437
22837
|
writeJson6({
|
|
22438
22838
|
ok: opened.ok,
|
|
22439
22839
|
path: htmlPath,
|
|
22440
22840
|
opened: opened.ok,
|
|
22441
|
-
detail: opened.detail
|
|
22841
|
+
detail: opened.ok ? fileUrl : `${opened.detail ?? "unknown error"}; open manually: ${fileUrl}`
|
|
22442
22842
|
}).trimEnd()
|
|
22443
22843
|
);
|
|
22444
22844
|
} else if (opened.ok) {
|
|
22445
22845
|
console.log(`Opened local Evidence: ${htmlPath}`);
|
|
22446
|
-
console.log(`URL: ${
|
|
22846
|
+
console.log(`URL: ${fileUrl}`);
|
|
22447
22847
|
} else {
|
|
22448
22848
|
console.error(`[AgentInspect] Could not open browser automatically.`);
|
|
22449
22849
|
console.error(`Open this file locally: ${htmlPath}`);
|
|
22450
|
-
console.error(opened.detail);
|
|
22850
|
+
console.error(`${opened.detail ?? "unknown error"}; open manually: ${fileUrl}`);
|
|
22451
22851
|
}
|
|
22452
22852
|
if (!opened.ok) {
|
|
22453
22853
|
process.exitCode = 0;
|