agent-inspect 6.8.0 → 6.10.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/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/docs/BUNDLES.md +31 -9
- package/docs/CI-ARTIFACTS.md +10 -1
- package/docs/CLI.md +20 -4
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-TO4VENHV.mjs → chunk-36IJ76LH.mjs} +1857 -158
- package/packages/cli/dist/chunk-36IJ76LH.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +10764 -8877
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +825 -754
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-Z5W27YCW.mjs → src-YFN3Q3GP.mjs} +3 -3
- package/packages/cli/dist/{src-Z5W27YCW.mjs.map → src-YFN3Q3GP.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +1718 -18
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +338 -4
- package/packages/core/dist/advanced.d.ts +338 -4
- package/packages/core/dist/advanced.mjs +1166 -12
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.cjs +105 -14
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.d.cts +2 -2
- package/packages/core/dist/checks.d.ts +2 -2
- package/packages/core/dist/checks.mjs +1 -1
- package/packages/core/dist/chunk-F7STQ5JF.mjs +479 -0
- package/packages/core/dist/chunk-F7STQ5JF.mjs.map +1 -0
- package/packages/core/dist/{chunk-EQVXDGGU.mjs → chunk-QT6CQ2XA.mjs} +107 -16
- package/packages/core/dist/chunk-QT6CQ2XA.mjs.map +1 -0
- package/packages/core/dist/diff.mjs +3 -477
- package/packages/core/dist/diff.mjs.map +1 -1
- package/packages/core/dist/{index-BblJEdYZ.d.ts → index-BO7l0iAe.d.ts} +44 -1
- package/packages/core/dist/{index-DyKk7iR9.d.cts → index-mdFcxSOR.d.cts} +44 -1
- package/packages/core/dist/reporters.cjs +25 -0
- package/packages/core/dist/reporters.cjs.map +1 -1
- package/packages/core/dist/reporters.d.cts +16 -2
- package/packages/core/dist/reporters.d.ts +16 -2
- package/packages/core/dist/reporters.mjs +24 -1
- package/packages/core/dist/reporters.mjs.map +1 -1
- package/packages/cli/dist/chunk-TO4VENHV.mjs.map +0 -1
- package/packages/core/dist/chunk-EQVXDGGU.mjs.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readdir, stat, readFile, access } from 'fs/promises';
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import path5 from 'path';
|
|
4
|
-
import crypto2, { webcrypto } from 'crypto';
|
|
4
|
+
import crypto2, { createHash, webcrypto } from 'crypto';
|
|
5
5
|
import process2 from 'process';
|
|
6
6
|
import tty from 'tty';
|
|
7
7
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
@@ -3443,12 +3443,12 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
3443
3443
|
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
|
|
3444
3444
|
);
|
|
3445
3445
|
const ordered = [...runs].sort(compareRuns);
|
|
3446
|
-
const
|
|
3446
|
+
const path14 = [];
|
|
3447
3447
|
const visited = /* @__PURE__ */ new Set();
|
|
3448
3448
|
const pushRun = (run, confidence, source) => {
|
|
3449
3449
|
if (visited.has(run.runId)) return;
|
|
3450
3450
|
visited.add(run.runId);
|
|
3451
|
-
|
|
3451
|
+
path14.push({
|
|
3452
3452
|
runId: run.runId,
|
|
3453
3453
|
name: run.name,
|
|
3454
3454
|
startedAt: run.startedAt,
|
|
@@ -3473,7 +3473,7 @@ function buildCriticalPath(runs, handoffs) {
|
|
|
3473
3473
|
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
3474
3474
|
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
3475
3475
|
}
|
|
3476
|
-
return
|
|
3476
|
+
return path14;
|
|
3477
3477
|
}
|
|
3478
3478
|
function metaRunIdMatches(run, token, runById) {
|
|
3479
3479
|
const meta = extractSessionWorkflowMetadata(run.metadata);
|
|
@@ -3804,8 +3804,9 @@ function buildBundleSummaryMarkdown(parts) {
|
|
|
3804
3804
|
""
|
|
3805
3805
|
];
|
|
3806
3806
|
for (const run of checks.runs) {
|
|
3807
|
+
const source = run.sourceStatus !== void 0 && run.sourceStatus !== run.status ? `; source ${run.sourceStatus}` : "";
|
|
3807
3808
|
lines.push(
|
|
3808
|
-
`- \`${run.runId}\`: ${run.status} (${run.findings} finding(s), ${run.errors} error(s), ${run.warnings} warning(s))`
|
|
3809
|
+
`- \`${run.runId}\`: artifact ${run.status}${source} (${run.findings} finding(s), ${run.errors} error(s), ${run.warnings} warning(s))`
|
|
3809
3810
|
);
|
|
3810
3811
|
}
|
|
3811
3812
|
lines.push("", "## Redaction", "", `Total findings: ${redaction.totalFindings}`, "");
|
|
@@ -3846,13 +3847,13 @@ function assertBundlePathContained(outputDir, relativePath) {
|
|
|
3846
3847
|
}
|
|
3847
3848
|
return resolved;
|
|
3848
3849
|
}
|
|
3849
|
-
function normalizeBundleOutputPath(out) {
|
|
3850
|
+
function normalizeBundleOutputPath(out, options) {
|
|
3850
3851
|
const trimmed = out.trim();
|
|
3851
3852
|
if (trimmed === "") {
|
|
3852
3853
|
throw new Error("--out requires a non-empty path.");
|
|
3853
3854
|
}
|
|
3854
3855
|
const resolved = path5.resolve(trimmed);
|
|
3855
|
-
if (resolved.toLowerCase().endsWith(".zip")) {
|
|
3856
|
+
if (options?.preserveZipExtension !== true && resolved.toLowerCase().endsWith(".zip")) {
|
|
3856
3857
|
return resolved.slice(0, -4);
|
|
3857
3858
|
}
|
|
3858
3859
|
return resolved;
|
|
@@ -3863,6 +3864,1721 @@ function defaultBundleOutputPath(runIds) {
|
|
|
3863
3864
|
return path5.resolve(`agent-inspect-bundle-${label}-${stamp}`);
|
|
3864
3865
|
}
|
|
3865
3866
|
|
|
3867
|
+
// packages/core/src/evidence/types.ts
|
|
3868
|
+
var EVIDENCE_FORMAT_VERSION = "1.0";
|
|
3869
|
+
var EVIDENCE_ASSESSMENT_NOTE = "Best-effort local safety verification only; not a compliance certification.";
|
|
3870
|
+
var EVIDENCE_MANIFEST_FILENAME = "evidence.json";
|
|
3871
|
+
var SHA256_RE = /^[a-f0-9]{64}$/i;
|
|
3872
|
+
function sha256Hex(data) {
|
|
3873
|
+
const hash = createHash("sha256");
|
|
3874
|
+
if (typeof data === "string") {
|
|
3875
|
+
hash.update(data, "utf8");
|
|
3876
|
+
} else {
|
|
3877
|
+
hash.update(data);
|
|
3878
|
+
}
|
|
3879
|
+
return hash.digest("hex");
|
|
3880
|
+
}
|
|
3881
|
+
function isSha256Hex(value) {
|
|
3882
|
+
return SHA256_RE.test(value);
|
|
3883
|
+
}
|
|
3884
|
+
function sha256Equals(expected, actual) {
|
|
3885
|
+
if (!isSha256Hex(expected) || !isSha256Hex(actual)) {
|
|
3886
|
+
return false;
|
|
3887
|
+
}
|
|
3888
|
+
const a = expected.toLowerCase();
|
|
3889
|
+
const b = actual.toLowerCase();
|
|
3890
|
+
if (a.length !== b.length) return false;
|
|
3891
|
+
let mismatch = 0;
|
|
3892
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
3893
|
+
mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
3894
|
+
}
|
|
3895
|
+
return mismatch === 0;
|
|
3896
|
+
}
|
|
3897
|
+
function assertEvidenceRelativePath(relativePath) {
|
|
3898
|
+
if (typeof relativePath !== "string" || relativePath.trim() === "") {
|
|
3899
|
+
throw new Error("Evidence file path must be a non-empty relative path.");
|
|
3900
|
+
}
|
|
3901
|
+
const trimmed = relativePath.trim().replaceAll("\\", "/");
|
|
3902
|
+
if (path5.isAbsolute(trimmed) || trimmed.startsWith("/")) {
|
|
3903
|
+
throw new Error(`Evidence file path must be relative: ${relativePath}`);
|
|
3904
|
+
}
|
|
3905
|
+
const parts = trimmed.split("/").filter((part) => part !== "");
|
|
3906
|
+
if (parts.length === 0) {
|
|
3907
|
+
throw new Error(`Evidence file path must be relative: ${relativePath}`);
|
|
3908
|
+
}
|
|
3909
|
+
for (const part of parts) {
|
|
3910
|
+
if (part === "." || part === "..") {
|
|
3911
|
+
throw new Error(`Evidence file path must not contain "." or "..": ${relativePath}`);
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
return parts.join("/");
|
|
3915
|
+
}
|
|
3916
|
+
|
|
3917
|
+
// packages/core/src/evidence/manifest.ts
|
|
3918
|
+
function stable(value) {
|
|
3919
|
+
if (Array.isArray(value)) return value.map(stable);
|
|
3920
|
+
if (value === null || typeof value !== "object") return value;
|
|
3921
|
+
const record = value;
|
|
3922
|
+
return Object.fromEntries(
|
|
3923
|
+
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable(record[key])])
|
|
3924
|
+
);
|
|
3925
|
+
}
|
|
3926
|
+
function serializeEvidenceManifest(manifest) {
|
|
3927
|
+
return `${JSON.stringify(stable(manifest), null, 2)}
|
|
3928
|
+
`;
|
|
3929
|
+
}
|
|
3930
|
+
function inferEvidenceFileRole(relativePath) {
|
|
3931
|
+
const normalized = assertEvidenceRelativePath(relativePath);
|
|
3932
|
+
const base = normalized.includes("/") ? normalized.slice(normalized.lastIndexOf("/") + 1) : normalized;
|
|
3933
|
+
if (base === "evidence.html" || base === "trace.html" || base.endsWith(".html")) {
|
|
3934
|
+
return "report";
|
|
3935
|
+
}
|
|
3936
|
+
if (base === "trace.jsonl" || base.endsWith(".jsonl")) {
|
|
3937
|
+
return "redacted-trace";
|
|
3938
|
+
}
|
|
3939
|
+
if (base === "check-results.json") {
|
|
3940
|
+
return "checks";
|
|
3941
|
+
}
|
|
3942
|
+
if (base === "redaction-report.json") {
|
|
3943
|
+
return "redaction-report";
|
|
3944
|
+
}
|
|
3945
|
+
if (base === "summary.md") {
|
|
3946
|
+
return "summary";
|
|
3947
|
+
}
|
|
3948
|
+
return "other";
|
|
3949
|
+
}
|
|
3950
|
+
function buildEvidenceFileEntries(files) {
|
|
3951
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
3952
|
+
for (const file of files) {
|
|
3953
|
+
const relativePath = assertEvidenceRelativePath(file.path);
|
|
3954
|
+
if (relativePath === EVIDENCE_MANIFEST_FILENAME) {
|
|
3955
|
+
throw new Error(
|
|
3956
|
+
`Do not include ${EVIDENCE_MANIFEST_FILENAME} in packaged file hashes (self-hash is undefined).`
|
|
3957
|
+
);
|
|
3958
|
+
}
|
|
3959
|
+
if (byPath.has(relativePath)) {
|
|
3960
|
+
throw new Error(`Duplicate evidence file path: ${relativePath}`);
|
|
3961
|
+
}
|
|
3962
|
+
byPath.set(relativePath, {
|
|
3963
|
+
path: relativePath,
|
|
3964
|
+
sha256: sha256Hex(file.content),
|
|
3965
|
+
role: file.role ?? inferEvidenceFileRole(relativePath)
|
|
3966
|
+
});
|
|
3967
|
+
}
|
|
3968
|
+
return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
3969
|
+
}
|
|
3970
|
+
function buildEvidenceManifest(parts) {
|
|
3971
|
+
const runIds = [...parts.runIds];
|
|
3972
|
+
if (runIds.length === 0) {
|
|
3973
|
+
throw new Error("Evidence manifest requires at least one run id.");
|
|
3974
|
+
}
|
|
3975
|
+
for (const item of parts.sourceHashes) {
|
|
3976
|
+
if (!runIds.includes(item.runId)) {
|
|
3977
|
+
throw new Error(`sourceHashes runId "${item.runId}" is not listed in source.runIds.`);
|
|
3978
|
+
}
|
|
3979
|
+
if (item.algorithm !== "sha256") {
|
|
3980
|
+
throw new Error(`Unsupported source hash algorithm: ${item.algorithm}`);
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
const assessment = {
|
|
3984
|
+
status: parts.assessmentStatus,
|
|
3985
|
+
note: parts.note ?? EVIDENCE_ASSESSMENT_NOTE
|
|
3986
|
+
};
|
|
3987
|
+
if (parts.sourceStatus !== void 0) {
|
|
3988
|
+
assessment.sourceStatus = parts.sourceStatus;
|
|
3989
|
+
}
|
|
3990
|
+
return {
|
|
3991
|
+
evidenceFormatVersion: EVIDENCE_FORMAT_VERSION,
|
|
3992
|
+
generator: {
|
|
3993
|
+
name: parts.generatorName ?? "agent-inspect",
|
|
3994
|
+
version: parts.generatorVersion
|
|
3995
|
+
},
|
|
3996
|
+
createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
3997
|
+
source: {
|
|
3998
|
+
runIds,
|
|
3999
|
+
traceSchemaVersions: [...parts.traceSchemaVersions].sort((a, b) => a.localeCompare(b)),
|
|
4000
|
+
sourceHashes: [...parts.sourceHashes].sort((a, b) => a.runId.localeCompare(b.runId))
|
|
4001
|
+
},
|
|
4002
|
+
policy: {
|
|
4003
|
+
redactionProfile: parts.redactionProfile,
|
|
4004
|
+
verificationPolicy: parts.verificationPolicy ?? parts.redactionProfile
|
|
4005
|
+
},
|
|
4006
|
+
assessment,
|
|
4007
|
+
files: buildEvidenceFileEntries(parts.files)
|
|
4008
|
+
};
|
|
4009
|
+
}
|
|
4010
|
+
function validateEvidenceManifest(value) {
|
|
4011
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4012
|
+
throw new Error("Evidence manifest must be a JSON object.");
|
|
4013
|
+
}
|
|
4014
|
+
const record = value;
|
|
4015
|
+
if (record.evidenceFormatVersion !== EVIDENCE_FORMAT_VERSION) {
|
|
4016
|
+
throw new Error(
|
|
4017
|
+
`Unsupported evidenceFormatVersion: ${String(record.evidenceFormatVersion)}`
|
|
4018
|
+
);
|
|
4019
|
+
}
|
|
4020
|
+
const generator = record.generator;
|
|
4021
|
+
if (generator === null || typeof generator !== "object" || Array.isArray(generator) || typeof generator.name !== "string" || typeof generator.version !== "string") {
|
|
4022
|
+
throw new Error("Evidence manifest requires generator.name and generator.version.");
|
|
4023
|
+
}
|
|
4024
|
+
const source = record.source;
|
|
4025
|
+
if (source === null || typeof source !== "object" || Array.isArray(source)) {
|
|
4026
|
+
throw new Error("Evidence manifest requires source.");
|
|
4027
|
+
}
|
|
4028
|
+
const sourceRecord = source;
|
|
4029
|
+
if (!Array.isArray(sourceRecord.runIds) || sourceRecord.runIds.length === 0) {
|
|
4030
|
+
throw new Error("Evidence manifest source.runIds must be a non-empty array.");
|
|
4031
|
+
}
|
|
4032
|
+
if (!Array.isArray(sourceRecord.traceSchemaVersions)) {
|
|
4033
|
+
throw new Error("Evidence manifest source.traceSchemaVersions must be an array.");
|
|
4034
|
+
}
|
|
4035
|
+
if (!Array.isArray(sourceRecord.sourceHashes)) {
|
|
4036
|
+
throw new Error("Evidence manifest source.sourceHashes must be an array.");
|
|
4037
|
+
}
|
|
4038
|
+
const policy = record.policy;
|
|
4039
|
+
if (policy === null || typeof policy !== "object" || Array.isArray(policy)) {
|
|
4040
|
+
throw new Error("Evidence manifest requires policy.");
|
|
4041
|
+
}
|
|
4042
|
+
const assessment = record.assessment;
|
|
4043
|
+
if (assessment === null || typeof assessment !== "object" || Array.isArray(assessment) || typeof assessment.status !== "string") {
|
|
4044
|
+
throw new Error("Evidence manifest requires assessment.status.");
|
|
4045
|
+
}
|
|
4046
|
+
if (!Array.isArray(record.files) || record.files.length === 0) {
|
|
4047
|
+
throw new Error("Evidence manifest files must be a non-empty array.");
|
|
4048
|
+
}
|
|
4049
|
+
for (const file of record.files) {
|
|
4050
|
+
if (file === null || typeof file !== "object" || Array.isArray(file)) {
|
|
4051
|
+
throw new Error("Evidence manifest file entries must be objects.");
|
|
4052
|
+
}
|
|
4053
|
+
const entry = file;
|
|
4054
|
+
if (typeof entry.path !== "string") {
|
|
4055
|
+
throw new Error("Evidence file entry requires path.");
|
|
4056
|
+
}
|
|
4057
|
+
assertEvidenceRelativePath(entry.path);
|
|
4058
|
+
if (typeof entry.sha256 !== "string" || !/^[a-f0-9]{64}$/i.test(entry.sha256)) {
|
|
4059
|
+
throw new Error(`Evidence file entry requires sha256 hex for ${entry.path}.`);
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
return value;
|
|
4063
|
+
}
|
|
4064
|
+
function parseEvidenceManifestJson(text) {
|
|
4065
|
+
let parsed;
|
|
4066
|
+
try {
|
|
4067
|
+
parsed = JSON.parse(text);
|
|
4068
|
+
} catch (error) {
|
|
4069
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4070
|
+
throw new Error(`Evidence manifest is not valid JSON: ${message}`);
|
|
4071
|
+
}
|
|
4072
|
+
return validateEvidenceManifest(parsed);
|
|
4073
|
+
}
|
|
4074
|
+
function collectTraceSchemaVersions(jsonl) {
|
|
4075
|
+
const versions = /* @__PURE__ */ new Set();
|
|
4076
|
+
for (const line of jsonl.split(/\r?\n/)) {
|
|
4077
|
+
const trimmed = line.trim();
|
|
4078
|
+
if (trimmed === "") continue;
|
|
4079
|
+
try {
|
|
4080
|
+
const row = JSON.parse(trimmed);
|
|
4081
|
+
if (typeof row.schemaVersion === "string" && row.schemaVersion.trim() !== "") {
|
|
4082
|
+
versions.add(row.schemaVersion.trim());
|
|
4083
|
+
}
|
|
4084
|
+
} catch {
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
return [...versions].sort((a, b) => a.localeCompare(b));
|
|
4088
|
+
}
|
|
4089
|
+
|
|
4090
|
+
// packages/core/src/exporters/helpers.ts
|
|
4091
|
+
var REDACT_SUBSTRINGS = [
|
|
4092
|
+
"authorization",
|
|
4093
|
+
"cookie",
|
|
4094
|
+
"token",
|
|
4095
|
+
"apikey",
|
|
4096
|
+
"password",
|
|
4097
|
+
"secret",
|
|
4098
|
+
"email"
|
|
4099
|
+
];
|
|
4100
|
+
function shouldRedactKey(key) {
|
|
4101
|
+
const k = key.toLowerCase();
|
|
4102
|
+
for (const s of REDACT_SUBSTRINGS) {
|
|
4103
|
+
if (k.includes(s)) return true;
|
|
4104
|
+
}
|
|
4105
|
+
return false;
|
|
4106
|
+
}
|
|
4107
|
+
function safeString(value, maxLength) {
|
|
4108
|
+
if (value === null || value === void 0) return "";
|
|
4109
|
+
let s;
|
|
4110
|
+
if (typeof value === "string") s = value;
|
|
4111
|
+
else if (typeof value === "number" || typeof value === "boolean") s = String(value);
|
|
4112
|
+
else s = stableJson(value, false);
|
|
4113
|
+
if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
|
|
4114
|
+
return `${s.slice(0, maxLength)}\u2026`;
|
|
4115
|
+
}
|
|
4116
|
+
return s;
|
|
4117
|
+
}
|
|
4118
|
+
function escapeMarkdown(value) {
|
|
4119
|
+
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
4120
|
+
}
|
|
4121
|
+
function escapeHtml(value) {
|
|
4122
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4123
|
+
}
|
|
4124
|
+
function sortKeysDeep(input) {
|
|
4125
|
+
if (input === null || typeof input !== "object") return input;
|
|
4126
|
+
if (Array.isArray(input)) return input.map(sortKeysDeep);
|
|
4127
|
+
const o = input;
|
|
4128
|
+
const out = {};
|
|
4129
|
+
for (const k of Object.keys(o).sort()) {
|
|
4130
|
+
out[k] = sortKeysDeep(o[k]);
|
|
4131
|
+
}
|
|
4132
|
+
return out;
|
|
4133
|
+
}
|
|
4134
|
+
function stableJson(value, pretty) {
|
|
4135
|
+
const sorted = sortKeysDeep(value);
|
|
4136
|
+
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
4137
|
+
}
|
|
4138
|
+
function compactAttributes(attrs, options) {
|
|
4139
|
+
if (attrs === void 0) return {};
|
|
4140
|
+
const maxLen = options?.maxLength ?? 500;
|
|
4141
|
+
const redacted = options?.redacted ?? true;
|
|
4142
|
+
const out = {};
|
|
4143
|
+
for (const key of Object.keys(attrs).sort()) {
|
|
4144
|
+
if (redacted && shouldRedactKey(key)) {
|
|
4145
|
+
out[key] = "[REDACTED]";
|
|
4146
|
+
continue;
|
|
4147
|
+
}
|
|
4148
|
+
const v = attrs[key];
|
|
4149
|
+
out[key] = compactValue(v, maxLen, redacted);
|
|
4150
|
+
}
|
|
4151
|
+
return out;
|
|
4152
|
+
}
|
|
4153
|
+
function compactValue(value, maxLen, redacted) {
|
|
4154
|
+
if (value === null || typeof value !== "object") {
|
|
4155
|
+
return typeof value === "string" ? safeString(value, maxLen) : value;
|
|
4156
|
+
}
|
|
4157
|
+
if (Array.isArray(value)) {
|
|
4158
|
+
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
|
|
4159
|
+
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
4160
|
+
return arr;
|
|
4161
|
+
}
|
|
4162
|
+
const o = value;
|
|
4163
|
+
const inner = {};
|
|
4164
|
+
for (const k of Object.keys(o)) {
|
|
4165
|
+
if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
4166
|
+
else inner[k] = compactValue(o[k], maxLen, redacted);
|
|
4167
|
+
}
|
|
4168
|
+
return inner;
|
|
4169
|
+
}
|
|
4170
|
+
function flattenTree(tree) {
|
|
4171
|
+
const out = [];
|
|
4172
|
+
function walk(nodes) {
|
|
4173
|
+
for (const n of nodes) {
|
|
4174
|
+
out.push(n);
|
|
4175
|
+
if (n.children.length > 0) walk(n.children);
|
|
4176
|
+
}
|
|
4177
|
+
}
|
|
4178
|
+
walk(tree.children);
|
|
4179
|
+
return out;
|
|
4180
|
+
}
|
|
4181
|
+
function zeroKinds() {
|
|
4182
|
+
return {
|
|
4183
|
+
RUN: 0,
|
|
4184
|
+
AGENT: 0,
|
|
4185
|
+
LLM: 0,
|
|
4186
|
+
TOOL: 0,
|
|
4187
|
+
CHAIN: 0,
|
|
4188
|
+
RETRIEVER: 0,
|
|
4189
|
+
DECISION: 0,
|
|
4190
|
+
RESULT: 0,
|
|
4191
|
+
ERROR: 0,
|
|
4192
|
+
LOGIC: 0,
|
|
4193
|
+
LOG: 0,
|
|
4194
|
+
OUTCOME: 0
|
|
4195
|
+
};
|
|
4196
|
+
}
|
|
4197
|
+
|
|
4198
|
+
// packages/core/src/evidence/views.ts
|
|
4199
|
+
function renderTreeHtml(nodes, ulClass = "tree") {
|
|
4200
|
+
if (nodes.length === 0) return "";
|
|
4201
|
+
const parts = [`<ul class="${ulClass}">`];
|
|
4202
|
+
for (const n of nodes) {
|
|
4203
|
+
const ev = n.event;
|
|
4204
|
+
const status = ev.status ?? "?";
|
|
4205
|
+
const dur = ev.durationMs !== void 0 && Number.isFinite(ev.durationMs) ? `${ev.durationMs}ms` : "-";
|
|
4206
|
+
const errClass = ev.status === "error" ? " is-error" : "";
|
|
4207
|
+
parts.push(`<li class="tree-node${errClass}">`);
|
|
4208
|
+
parts.push(
|
|
4209
|
+
`<span class="nm">${escapeHtml(ev.name)}</span> <span class="meta">[${escapeHtml(ev.kind)}] ${escapeHtml(status)} (${escapeHtml(dur)})</span>`
|
|
4210
|
+
);
|
|
4211
|
+
if (n.children.length > 0) {
|
|
4212
|
+
parts.push(renderTreeHtml(n.children, "tree nested"));
|
|
4213
|
+
}
|
|
4214
|
+
parts.push("</li>");
|
|
4215
|
+
}
|
|
4216
|
+
parts.push("</ul>");
|
|
4217
|
+
return parts.join("");
|
|
4218
|
+
}
|
|
4219
|
+
function buildEvidenceTreeViewHtml(trees) {
|
|
4220
|
+
if (trees.length === 0) {
|
|
4221
|
+
return `<p class="muted">No execution trees available.</p>`;
|
|
4222
|
+
}
|
|
4223
|
+
const parts = [];
|
|
4224
|
+
for (const tree of trees) {
|
|
4225
|
+
parts.push(`<article class="run-block">`);
|
|
4226
|
+
parts.push(`<h3><code>${escapeHtml(tree.runId)}</code></h3>`);
|
|
4227
|
+
if (tree.name) {
|
|
4228
|
+
parts.push(`<p class="muted">Name: ${escapeHtml(tree.name)}</p>`);
|
|
4229
|
+
}
|
|
4230
|
+
parts.push(
|
|
4231
|
+
`<p>Status: <strong>${escapeHtml(String(tree.status ?? "unknown"))}</strong>${tree.durationMs !== void 0 ? ` \xB7 ${escapeHtml(String(tree.durationMs))}ms` : ""}</p>`
|
|
4232
|
+
);
|
|
4233
|
+
parts.push(
|
|
4234
|
+
tree.children.length > 0 ? renderTreeHtml(tree.children) : `<p class="muted">No steps recorded.</p>`
|
|
4235
|
+
);
|
|
4236
|
+
parts.push(`</article>`);
|
|
4237
|
+
}
|
|
4238
|
+
return parts.join("\n");
|
|
4239
|
+
}
|
|
4240
|
+
function timelineRows(tree) {
|
|
4241
|
+
const flat = flattenTree(tree);
|
|
4242
|
+
const origin = tree.startedAt ?? flat.reduce((min, n) => {
|
|
4243
|
+
const t = n.event.timestamp;
|
|
4244
|
+
if (!Number.isFinite(t)) return min;
|
|
4245
|
+
return min === void 0 ? t : Math.min(min, t);
|
|
4246
|
+
}, void 0) ?? 0;
|
|
4247
|
+
return flat.filter((n) => n.event.kind !== "RUN").map((n) => {
|
|
4248
|
+
const started = Number.isFinite(n.event.timestamp) ? n.event.timestamp : origin;
|
|
4249
|
+
const durationMs = n.event.durationMs !== void 0 && Number.isFinite(n.event.durationMs) ? Math.max(0, n.event.durationMs) : 0;
|
|
4250
|
+
return {
|
|
4251
|
+
name: n.event.name,
|
|
4252
|
+
kind: n.event.kind,
|
|
4253
|
+
status: n.event.status ?? "?",
|
|
4254
|
+
offsetMs: Math.max(0, started - origin),
|
|
4255
|
+
durationMs,
|
|
4256
|
+
isError: n.event.status === "error"
|
|
4257
|
+
};
|
|
4258
|
+
}).sort((a, b) => a.offsetMs - b.offsetMs || a.name.localeCompare(b.name));
|
|
4259
|
+
}
|
|
4260
|
+
function buildEvidenceTimelineViewHtml(trees) {
|
|
4261
|
+
if (trees.length === 0) {
|
|
4262
|
+
return `<p class="muted">No timeline data available.</p>`;
|
|
4263
|
+
}
|
|
4264
|
+
const parts = [];
|
|
4265
|
+
for (const tree of trees) {
|
|
4266
|
+
const rows = timelineRows(tree);
|
|
4267
|
+
const maxEnd = rows.reduce(
|
|
4268
|
+
(max, row) => Math.max(max, row.offsetMs + Math.max(row.durationMs, 1)),
|
|
4269
|
+
1
|
|
4270
|
+
);
|
|
4271
|
+
parts.push(`<article class="run-block">`);
|
|
4272
|
+
parts.push(`<h3><code>${escapeHtml(tree.runId)}</code></h3>`);
|
|
4273
|
+
if (rows.length === 0) {
|
|
4274
|
+
parts.push(`<p class="muted">No step timings recorded.</p>`);
|
|
4275
|
+
} else {
|
|
4276
|
+
parts.push(`<div class="waterfall" role="list">`);
|
|
4277
|
+
for (const row of rows) {
|
|
4278
|
+
const left = row.offsetMs / maxEnd * 100;
|
|
4279
|
+
const width = Math.max(0.8, Math.max(row.durationMs, 1) / maxEnd * 100);
|
|
4280
|
+
const err = row.isError ? " is-error" : "";
|
|
4281
|
+
parts.push(
|
|
4282
|
+
`<div class="wf-row${err}" role="listitem"><div class="wf-label"><span class="nm">${escapeHtml(row.name)}</span> <span class="meta">[${escapeHtml(row.kind)}] ${escapeHtml(row.status)} \xB7 ${escapeHtml(String(row.durationMs))}ms @+${escapeHtml(String(row.offsetMs))}ms</span></div><div class="wf-track"><span class="wf-bar" style="left:${left.toFixed(2)}%;width:${width.toFixed(2)}%"></span></div></div>`
|
|
4283
|
+
);
|
|
4284
|
+
}
|
|
4285
|
+
parts.push(`</div>`);
|
|
4286
|
+
}
|
|
4287
|
+
parts.push(`</article>`);
|
|
4288
|
+
}
|
|
4289
|
+
return parts.join("\n");
|
|
4290
|
+
}
|
|
4291
|
+
function findNodeByEventId(nodes, eventId) {
|
|
4292
|
+
for (const node of nodes) {
|
|
4293
|
+
if (node.event.eventId === eventId) return node;
|
|
4294
|
+
const child = findNodeByEventId(node.children, eventId);
|
|
4295
|
+
if (child) return child;
|
|
4296
|
+
}
|
|
4297
|
+
return void 0;
|
|
4298
|
+
}
|
|
4299
|
+
function buildAncestorChain(tree, failure) {
|
|
4300
|
+
const chain = [failure];
|
|
4301
|
+
let parentId = failure.event.parentId;
|
|
4302
|
+
const guard = /* @__PURE__ */ new Set([failure.event.eventId]);
|
|
4303
|
+
while (parentId && !guard.has(parentId)) {
|
|
4304
|
+
guard.add(parentId);
|
|
4305
|
+
const parent = findNodeByEventId(tree.children, parentId);
|
|
4306
|
+
if (!parent) break;
|
|
4307
|
+
chain.unshift(parent);
|
|
4308
|
+
parentId = parent.event.parentId;
|
|
4309
|
+
}
|
|
4310
|
+
return chain;
|
|
4311
|
+
}
|
|
4312
|
+
function buildEvidenceCausalFailureViewHtml(trees) {
|
|
4313
|
+
if (trees.length === 0) {
|
|
4314
|
+
return `<p class="muted">No runs available for causal analysis.</p>`;
|
|
4315
|
+
}
|
|
4316
|
+
const parts = [];
|
|
4317
|
+
for (const tree of trees) {
|
|
4318
|
+
parts.push(`<article class="run-block">`);
|
|
4319
|
+
parts.push(`<h3><code>${escapeHtml(tree.runId)}</code></h3>`);
|
|
4320
|
+
const errors = flattenTree(tree).filter((n) => n.event.status === "error" || n.event.kind === "ERROR").sort((a, b) => a.event.timestamp - b.event.timestamp);
|
|
4321
|
+
if (errors.length === 0) {
|
|
4322
|
+
parts.push(
|
|
4323
|
+
`<p class="muted">No error-status events found. Run status: <strong>${escapeHtml(String(tree.status ?? "unknown"))}</strong>.</p>`
|
|
4324
|
+
);
|
|
4325
|
+
parts.push(`</article>`);
|
|
4326
|
+
continue;
|
|
4327
|
+
}
|
|
4328
|
+
const first = errors[0];
|
|
4329
|
+
const chain = buildAncestorChain(tree, first);
|
|
4330
|
+
parts.push(`<p>First error by timestamp:</p>`);
|
|
4331
|
+
parts.push(`<ol class="causal-chain">`);
|
|
4332
|
+
for (const node of chain) {
|
|
4333
|
+
const isTip = node.event.eventId === first.event.eventId;
|
|
4334
|
+
const msg = typeof node.event.attributes?.message === "string" ? node.event.attributes.message : typeof node.event.attributes?.error === "string" ? node.event.attributes.error : void 0;
|
|
4335
|
+
parts.push(
|
|
4336
|
+
`<li class="${isTip ? "causal-tip" : ""}"><span class="nm">${escapeHtml(node.event.name)}</span> <span class="meta">[${escapeHtml(node.event.kind)}] ${escapeHtml(node.event.status ?? "?")} \xB7 ${escapeHtml(node.event.eventId)}</span>${msg ? `<div class="causal-msg">${escapeHtml(msg.slice(0, 400))}</div>` : ""}</li>`
|
|
4337
|
+
);
|
|
4338
|
+
}
|
|
4339
|
+
parts.push(`</ol>`);
|
|
4340
|
+
if (errors.length > 1) {
|
|
4341
|
+
parts.push(
|
|
4342
|
+
`<p class="muted">${escapeHtml(String(errors.length - 1))} additional error event(s) not shown in the primary chain.</p>`
|
|
4343
|
+
);
|
|
4344
|
+
}
|
|
4345
|
+
parts.push(`</article>`);
|
|
4346
|
+
}
|
|
4347
|
+
return parts.join("\n");
|
|
4348
|
+
}
|
|
4349
|
+
var EVIDENCE_VIEW_CSS = `
|
|
4350
|
+
ul.tree{list-style:none;padding-left:1rem;margin:.5rem 0}
|
|
4351
|
+
ul.tree.nested{padding-left:1.25rem;border-left:1px solid var(--line);margin:.25rem 0}
|
|
4352
|
+
.tree-node.is-error .nm,.wf-row.is-error .nm,.causal-tip .nm{color:var(--unsafe)}
|
|
4353
|
+
.waterfall{display:flex;flex-direction:column;gap:.45rem;max-width:52rem}
|
|
4354
|
+
.wf-row{display:grid;grid-template-columns:minmax(10rem,18rem) 1fr;gap:.6rem;align-items:center}
|
|
4355
|
+
.wf-track{position:relative;height:.7rem;background:#e8e8e4;border-radius:.25rem;overflow:hidden}
|
|
4356
|
+
.wf-bar{position:absolute;top:0;bottom:0;background:var(--accent);border-radius:.25rem}
|
|
4357
|
+
.wf-row.is-error .wf-bar{background:var(--unsafe)}
|
|
4358
|
+
.causal-chain{max-width:44rem}
|
|
4359
|
+
.causal-msg{margin:.25rem 0 0;color:var(--muted);font-size:.92rem}
|
|
4360
|
+
.run-block{margin:0 0 1.25rem;padding-bottom:1rem;border-bottom:1px solid var(--line)}
|
|
4361
|
+
.run-block:last-child{border-bottom:0}
|
|
4362
|
+
@media (max-width:720px){
|
|
4363
|
+
.wf-row{grid-template-columns:1fr}
|
|
4364
|
+
}
|
|
4365
|
+
`.trim();
|
|
4366
|
+
|
|
4367
|
+
// packages/core/src/evidence/html-shell.ts
|
|
4368
|
+
var EVIDENCE_HTML_FILENAME = "evidence.html";
|
|
4369
|
+
var EVIDENCE_HTML_NOTE = "Generated locally by AgentInspect. Share-checked evidence for review \u2014 not a compliance or security certification.";
|
|
4370
|
+
var EVIDENCE_VIEW_IDS = [
|
|
4371
|
+
"summary",
|
|
4372
|
+
"tree",
|
|
4373
|
+
"timeline",
|
|
4374
|
+
"causal",
|
|
4375
|
+
"tools-llm",
|
|
4376
|
+
"outcomes",
|
|
4377
|
+
"contracts",
|
|
4378
|
+
"circuit",
|
|
4379
|
+
"diff",
|
|
4380
|
+
"safety",
|
|
4381
|
+
"provenance"
|
|
4382
|
+
];
|
|
4383
|
+
function statusClass(status) {
|
|
4384
|
+
if (status === "SAFE") return "st-safe";
|
|
4385
|
+
if (status === "SAFE WITH WARNINGS") return "st-warn";
|
|
4386
|
+
if (status === "UNSAFE") return "st-unsafe";
|
|
4387
|
+
return "st-unknown";
|
|
4388
|
+
}
|
|
4389
|
+
function viewLabel(id) {
|
|
4390
|
+
switch (id) {
|
|
4391
|
+
case "summary":
|
|
4392
|
+
return "Summary";
|
|
4393
|
+
case "tree":
|
|
4394
|
+
return "Tree";
|
|
4395
|
+
case "timeline":
|
|
4396
|
+
return "Timeline";
|
|
4397
|
+
case "causal":
|
|
4398
|
+
return "Causal failure";
|
|
4399
|
+
case "tools-llm":
|
|
4400
|
+
return "Tools / LLM";
|
|
4401
|
+
case "outcomes":
|
|
4402
|
+
return "Outcomes";
|
|
4403
|
+
case "contracts":
|
|
4404
|
+
return "Contracts / checks";
|
|
4405
|
+
case "circuit":
|
|
4406
|
+
return "Circuit / guardrails";
|
|
4407
|
+
case "diff":
|
|
4408
|
+
return "Diff";
|
|
4409
|
+
case "safety":
|
|
4410
|
+
return "Safety / redaction";
|
|
4411
|
+
case "provenance":
|
|
4412
|
+
return "Provenance";
|
|
4413
|
+
default: {
|
|
4414
|
+
const _exhaustive = id;
|
|
4415
|
+
return _exhaustive;
|
|
4416
|
+
}
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
function encodeEmbeddedEvidenceJson(value) {
|
|
4420
|
+
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e");
|
|
4421
|
+
}
|
|
4422
|
+
function buildEmbeddedPayload(input) {
|
|
4423
|
+
return {
|
|
4424
|
+
evidenceFormatVersion: input.evidenceFormatVersion ?? "1.0",
|
|
4425
|
+
generator: {
|
|
4426
|
+
name: input.generatorName,
|
|
4427
|
+
version: input.generatorVersion
|
|
4428
|
+
},
|
|
4429
|
+
createdAt: input.createdAt,
|
|
4430
|
+
runIds: [...input.runIds],
|
|
4431
|
+
assessment: {
|
|
4432
|
+
status: input.assessmentStatus,
|
|
4433
|
+
...input.sourceStatus !== void 0 ? { sourceStatus: input.sourceStatus } : {}
|
|
4434
|
+
},
|
|
4435
|
+
policy: {
|
|
4436
|
+
redactionProfile: input.redactionProfile,
|
|
4437
|
+
verificationPolicy: input.verificationPolicy
|
|
4438
|
+
},
|
|
4439
|
+
checkSummary: input.checkSummary
|
|
4440
|
+
};
|
|
4441
|
+
}
|
|
4442
|
+
function buildEvidenceHtmlShell(input) {
|
|
4443
|
+
if (input.runIds.length === 0) {
|
|
4444
|
+
throw new Error("Evidence HTML shell requires at least one run id.");
|
|
4445
|
+
}
|
|
4446
|
+
const maxChars = input.maxEmbeddedJsonChars ?? 64 * 1024;
|
|
4447
|
+
let embedded = encodeEmbeddedEvidenceJson(buildEmbeddedPayload(input));
|
|
4448
|
+
if (embedded.length > maxChars) {
|
|
4449
|
+
embedded = encodeEmbeddedEvidenceJson({
|
|
4450
|
+
truncated: true,
|
|
4451
|
+
evidenceFormatVersion: input.evidenceFormatVersion ?? "1.0",
|
|
4452
|
+
runIds: [...input.runIds],
|
|
4453
|
+
assessment: { status: input.assessmentStatus },
|
|
4454
|
+
note: "Embedded payload truncated to bound; open evidence.json / trace files for full detail."
|
|
4455
|
+
});
|
|
4456
|
+
}
|
|
4457
|
+
const title = escapeHtml(input.title ?? "AgentInspect evidence");
|
|
4458
|
+
const runList = input.runIds.map((id) => `<li><code>${escapeHtml(id)}</code></li>`).join("");
|
|
4459
|
+
const summaryBody = input.summaryText !== void 0 && input.summaryText.trim() !== "" ? `<pre class="summary-md">${escapeHtml(input.summaryText)}</pre>` : `<p class="muted">Open <code>summary.md</code> in this bundle for the full text summary.</p>`;
|
|
4460
|
+
const checkRows = input.checkSummary?.runs.map(
|
|
4461
|
+
(run) => `<tr><td><code>${escapeHtml(run.runId)}</code></td><td class="${statusClass(run.status)}">${escapeHtml(run.status)}</td><td>${run.errors}</td><td>${run.warnings}</td><td>${run.findings}</td></tr>`
|
|
4462
|
+
).join("") ?? "";
|
|
4463
|
+
const nav = EVIDENCE_VIEW_IDS.map(
|
|
4464
|
+
(id) => `<a class="nav-link" href="#view-${id}" data-view="${id}">${escapeHtml(viewLabel(id))}</a>`
|
|
4465
|
+
).join("\n");
|
|
4466
|
+
const stubPanels = EVIDENCE_VIEW_IDS.filter((id) => id !== "summary").map((id) => {
|
|
4467
|
+
const body = input.viewBodies?.[id];
|
|
4468
|
+
if (body !== void 0 && body.trim() !== "") {
|
|
4469
|
+
return ` <section id="view-${id}" class="panel" hidden>
|
|
4470
|
+
<h2>${escapeHtml(viewLabel(id))}</h2>
|
|
4471
|
+
${body}
|
|
4472
|
+
</section>`;
|
|
4473
|
+
}
|
|
4474
|
+
return ` <section id="view-${id}" class="panel" hidden>
|
|
4475
|
+
<h2>${escapeHtml(viewLabel(id))}</h2>
|
|
4476
|
+
<p class="muted">This view will be filled in a later AgentInspect 6.10 release. The shell is offline-ready.</p>
|
|
4477
|
+
</section>`;
|
|
4478
|
+
}).join("\n");
|
|
4479
|
+
return `<!doctype html>
|
|
4480
|
+
<html lang="en">
|
|
4481
|
+
<head>
|
|
4482
|
+
<meta charset="utf-8"/>
|
|
4483
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
4484
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"/>
|
|
4485
|
+
<meta name="referrer" content="no-referrer"/>
|
|
4486
|
+
<title>${title}</title>
|
|
4487
|
+
<style>
|
|
4488
|
+
:root{--bg:#f7f7f5;--fg:#1a1a1a;--muted:#5c5c5c;--line:#d8d8d4;--accent:#0b5fff;--safe:#0a7a3e;--warn:#9a6700;--unsafe:#b42318;--unknown:#5c5c5c}
|
|
4489
|
+
*{box-sizing:border-box}
|
|
4490
|
+
body{margin:0;font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;background:var(--bg);color:var(--fg);line-height:1.5}
|
|
4491
|
+
a{color:var(--accent)}
|
|
4492
|
+
header{padding:1.25rem 1.5rem;border-bottom:1px solid var(--line);background:#fff}
|
|
4493
|
+
header h1{margin:0 0 .35rem;font-size:1.35rem}
|
|
4494
|
+
.note{margin:0;color:var(--muted);font-size:.92rem;max-width:52rem}
|
|
4495
|
+
.layout{display:grid;grid-template-columns:14rem 1fr;min-height:70vh}
|
|
4496
|
+
nav{padding:1rem;border-right:1px solid var(--line);background:#fff}
|
|
4497
|
+
nav .nav-link{display:block;padding:.4rem .55rem;margin:0 0 .2rem;border-radius:.35rem;text-decoration:none;color:inherit}
|
|
4498
|
+
nav .nav-link:hover,nav .nav-link:focus{background:#eef3ff;outline:2px solid var(--accent);outline-offset:1px}
|
|
4499
|
+
main{padding:1.25rem 1.5rem}
|
|
4500
|
+
.panel[hidden]{display:none}
|
|
4501
|
+
.badge{display:inline-block;padding:.15rem .55rem;border-radius:.3rem;font-size:.85rem;font-weight:600}
|
|
4502
|
+
.st-safe{color:var(--safe)}.st-warn{color:var(--warn)}.st-unsafe{color:var(--unsafe)}.st-unknown{color:var(--unknown)}
|
|
4503
|
+
table{border-collapse:collapse;width:100%;max-width:48rem;background:#fff}
|
|
4504
|
+
th,td{border:1px solid var(--line);padding:.4rem .55rem;text-align:left;vertical-align:top}
|
|
4505
|
+
th{background:#f0f0ec}
|
|
4506
|
+
.summary-md,pre.data{white-space:pre-wrap;word-break:break-word;background:#fff;border:1px solid var(--line);padding:.75rem;max-width:52rem}
|
|
4507
|
+
.muted{color:var(--muted)}
|
|
4508
|
+
ul.runs{margin:.4rem 0 1rem;padding-left:1.2rem}
|
|
4509
|
+
@media print{
|
|
4510
|
+
nav{display:none}
|
|
4511
|
+
.layout{display:block}
|
|
4512
|
+
.panel[hidden]{display:block!important;page-break-before:always}
|
|
4513
|
+
header{border:0}
|
|
4514
|
+
}
|
|
4515
|
+
@media (max-width:720px){
|
|
4516
|
+
.layout{grid-template-columns:1fr}
|
|
4517
|
+
nav{border-right:0;border-bottom:1px solid var(--line);display:flex;flex-wrap:wrap;gap:.25rem}
|
|
4518
|
+
}
|
|
4519
|
+
${EVIDENCE_VIEW_CSS}
|
|
4520
|
+
</style>
|
|
4521
|
+
</head>
|
|
4522
|
+
<body>
|
|
4523
|
+
<header>
|
|
4524
|
+
<h1>${title}</h1>
|
|
4525
|
+
<p class="note">${escapeHtml(EVIDENCE_HTML_NOTE)}</p>
|
|
4526
|
+
</header>
|
|
4527
|
+
<div class="layout">
|
|
4528
|
+
<nav aria-label="Evidence views">
|
|
4529
|
+
${nav}
|
|
4530
|
+
</nav>
|
|
4531
|
+
<main id="main">
|
|
4532
|
+
<section id="view-summary" class="panel" tabindex="-1">
|
|
4533
|
+
<h2>Summary</h2>
|
|
4534
|
+
<p>Artifact status: <span class="badge ${statusClass(input.assessmentStatus)}">${escapeHtml(input.assessmentStatus)}</span>
|
|
4535
|
+
${input.sourceStatus !== void 0 ? ` \xB7 Source status: <span class="badge ${statusClass(input.sourceStatus)}">${escapeHtml(input.sourceStatus)}</span>` : ""}</p>
|
|
4536
|
+
<p>Profile: <code>${escapeHtml(input.redactionProfile)}</code> \xB7 Verification: <code>${escapeHtml(input.verificationPolicy)}</code></p>
|
|
4537
|
+
<p>Generator: <code>${escapeHtml(input.generatorName)}@${escapeHtml(input.generatorVersion)}</code>
|
|
4538
|
+
${input.createdAt ? ` \xB7 Created: <code>${escapeHtml(input.createdAt)}</code>` : ""}</p>
|
|
4539
|
+
<h3>Runs</h3>
|
|
4540
|
+
<ul class="runs">${runList}</ul>
|
|
4541
|
+
${checkRows ? `<h3>Check summary</h3>
|
|
4542
|
+
<table>
|
|
4543
|
+
<thead><tr><th>runId</th><th>artifact</th><th>errors</th><th>warnings</th><th>findings</th></tr></thead>
|
|
4544
|
+
<tbody>${checkRows}</tbody>
|
|
4545
|
+
</table>` : ""}
|
|
4546
|
+
<h3>Text summary</h3>
|
|
4547
|
+
${summaryBody}
|
|
4548
|
+
</section>
|
|
4549
|
+
${stubPanels}
|
|
4550
|
+
</main>
|
|
4551
|
+
</div>
|
|
4552
|
+
<script type="application/json" id="ai-evidence-data">${embedded}</script>
|
|
4553
|
+
<script>
|
|
4554
|
+
(function(){
|
|
4555
|
+
var links=document.querySelectorAll("nav .nav-link");
|
|
4556
|
+
var panels=document.querySelectorAll("main .panel");
|
|
4557
|
+
function show(id){
|
|
4558
|
+
for(var i=0;i<panels.length;i++){
|
|
4559
|
+
var p=panels[i];
|
|
4560
|
+
var on=p.id==="view-"+id;
|
|
4561
|
+
if(on){p.removeAttribute("hidden");}else{p.setAttribute("hidden","");}
|
|
4562
|
+
}
|
|
4563
|
+
for(var j=0;j<links.length;j++){
|
|
4564
|
+
var a=links[j];
|
|
4565
|
+
if(a.getAttribute("data-view")===id){a.setAttribute("aria-current","page");}
|
|
4566
|
+
else{a.removeAttribute("aria-current");}
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4569
|
+
for(var k=0;k<links.length;k++){
|
|
4570
|
+
links[k].addEventListener("click",function(ev){
|
|
4571
|
+
var id=ev.currentTarget.getAttribute("data-view");
|
|
4572
|
+
if(!id)return;
|
|
4573
|
+
ev.preventDefault();
|
|
4574
|
+
if(history.replaceState){history.replaceState(null,"","#view-"+id);}
|
|
4575
|
+
show(id);
|
|
4576
|
+
var panel=document.getElementById("view-"+id);
|
|
4577
|
+
if(panel)panel.focus();
|
|
4578
|
+
});
|
|
4579
|
+
}
|
|
4580
|
+
var hash=(location.hash||"").replace(/^#view-/,"");
|
|
4581
|
+
var initial="summary";
|
|
4582
|
+
for(var n=0;n<links.length;n++){
|
|
4583
|
+
if(links[n].getAttribute("data-view")===hash){initial=hash;break;}
|
|
4584
|
+
}
|
|
4585
|
+
show(initial);
|
|
4586
|
+
})();
|
|
4587
|
+
</script>
|
|
4588
|
+
</body>
|
|
4589
|
+
</html>
|
|
4590
|
+
`;
|
|
4591
|
+
}
|
|
4592
|
+
|
|
4593
|
+
// packages/core/src/diff/renderer.ts
|
|
4594
|
+
function formatPath(path14) {
|
|
4595
|
+
if (path14 === void 0 || path14.path.length === 0) {
|
|
4596
|
+
return "(run)";
|
|
4597
|
+
}
|
|
4598
|
+
return path14.path.map((s) => s.name).join(" > ");
|
|
4599
|
+
}
|
|
4600
|
+
function formatValue(v, verbose) {
|
|
4601
|
+
if (v === void 0) return "(undefined)";
|
|
4602
|
+
if (typeof v === "string") return v;
|
|
4603
|
+
if (typeof v === "number" || typeof v === "boolean") return String(v);
|
|
4604
|
+
const s = JSON.stringify(v);
|
|
4605
|
+
if (verbose || s.length <= 120) return s;
|
|
4606
|
+
return `${s.slice(0, 117)}...`;
|
|
4607
|
+
}
|
|
4608
|
+
function renderRunDiff(result, options) {
|
|
4609
|
+
const json = options?.json === true;
|
|
4610
|
+
const verbose = options?.verbose === true;
|
|
4611
|
+
const color = options?.color === true;
|
|
4612
|
+
if (json) {
|
|
4613
|
+
return JSON.stringify(result, null, 2);
|
|
4614
|
+
}
|
|
4615
|
+
const sev = (s, level) => {
|
|
4616
|
+
if (!color) return s;
|
|
4617
|
+
if (level === "error") return source_default.red(s);
|
|
4618
|
+
if (level === "warning") return source_default.yellow(s);
|
|
4619
|
+
return source_default.gray(s);
|
|
4620
|
+
};
|
|
4621
|
+
const lines = [];
|
|
4622
|
+
const { summary } = result;
|
|
4623
|
+
lines.push("Run diff");
|
|
4624
|
+
lines.push(`Left: ${summary.leftRunId}`);
|
|
4625
|
+
lines.push(`Right: ${summary.rightRunId}`);
|
|
4626
|
+
lines.push("");
|
|
4627
|
+
lines.push("Summary:");
|
|
4628
|
+
lines.push(` Differences: ${summary.totalDifferences}`);
|
|
4629
|
+
lines.push(` Errors: ${summary.errors}`);
|
|
4630
|
+
lines.push(` Warnings: ${summary.warnings}`);
|
|
4631
|
+
lines.push(` Info: ${summary.info}`);
|
|
4632
|
+
lines.push("");
|
|
4633
|
+
const fd = summary.firstDivergence;
|
|
4634
|
+
const firstKind = result.differences[0]?.kind;
|
|
4635
|
+
if (fd !== void 0) {
|
|
4636
|
+
lines.push("First divergence:");
|
|
4637
|
+
const where = formatPath(fd.path);
|
|
4638
|
+
const displayKind = firstKind ?? fd.kind;
|
|
4639
|
+
lines.push(` ${displayKind} at ${where}`);
|
|
4640
|
+
if (fd.left !== void 0 || fd.right !== void 0) {
|
|
4641
|
+
lines.push(` left: ${formatValue(fd.left, verbose)}`);
|
|
4642
|
+
lines.push(` right: ${formatValue(fd.right, verbose)}`);
|
|
4643
|
+
}
|
|
4644
|
+
lines.push("");
|
|
4645
|
+
}
|
|
4646
|
+
lines.push("Differences:");
|
|
4647
|
+
if (result.differences.length === 0) {
|
|
4648
|
+
lines.push(" (none)");
|
|
4649
|
+
return lines.join("\n");
|
|
4650
|
+
}
|
|
4651
|
+
const showSides = (kind) => verbose || [
|
|
4652
|
+
"run-status",
|
|
4653
|
+
"step-status",
|
|
4654
|
+
"error",
|
|
4655
|
+
"duration",
|
|
4656
|
+
"step-type",
|
|
4657
|
+
"structure",
|
|
4658
|
+
"step-added",
|
|
4659
|
+
"step-removed"
|
|
4660
|
+
].includes(kind);
|
|
4661
|
+
for (const d of result.differences) {
|
|
4662
|
+
const tag = sev(`[${d.severity}]`, d.severity);
|
|
4663
|
+
const pathStr = d.path !== void 0 ? ` ${formatPath(d.path)}` : "";
|
|
4664
|
+
lines.push(` ${tag} ${d.kind}${pathStr}`);
|
|
4665
|
+
lines.push(` ${d.message}`);
|
|
4666
|
+
if (d.left !== void 0 || d.right !== void 0) {
|
|
4667
|
+
if (showSides(d.kind)) {
|
|
4668
|
+
lines.push(` left: ${formatValue(d.left, verbose)}`);
|
|
4669
|
+
lines.push(` right: ${formatValue(d.right, verbose)}`);
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
return lines.join("\n");
|
|
4674
|
+
}
|
|
4675
|
+
|
|
4676
|
+
// packages/core/src/diff/comparable.ts
|
|
4677
|
+
function extractOutputPreview(meta) {
|
|
4678
|
+
if (meta === void 0) return void 0;
|
|
4679
|
+
if ("outputPreview" in meta) return meta.outputPreview;
|
|
4680
|
+
if ("resultPreview" in meta) return meta.resultPreview;
|
|
4681
|
+
return void 0;
|
|
4682
|
+
}
|
|
4683
|
+
function mapStepStatus(s) {
|
|
4684
|
+
if (s === void 0) return "running";
|
|
4685
|
+
return s;
|
|
4686
|
+
}
|
|
4687
|
+
function manualTraceEventsToComparableRun(events) {
|
|
4688
|
+
const started = events.find((e) => e.event === "run_started");
|
|
4689
|
+
if (!started || started.event !== "run_started") {
|
|
4690
|
+
throw new Error("Invalid trace: missing run_started");
|
|
4691
|
+
}
|
|
4692
|
+
const rs = started;
|
|
4693
|
+
const runId = rs.runId;
|
|
4694
|
+
const completedAll = events.filter((e) => e.event === "run_completed");
|
|
4695
|
+
const lastCompleted = completedAll[completedAll.length - 1];
|
|
4696
|
+
let runStatus;
|
|
4697
|
+
if (lastCompleted === void 0) runStatus = "running";
|
|
4698
|
+
else runStatus = lastCompleted.status;
|
|
4699
|
+
const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
4700
|
+
const steps = /* @__PURE__ */ new Map();
|
|
4701
|
+
let order = 0;
|
|
4702
|
+
for (const e of events) {
|
|
4703
|
+
if (e.event !== "step_started") continue;
|
|
4704
|
+
const s = e;
|
|
4705
|
+
const meta = s.metadata ? { ...s.metadata } : void 0;
|
|
4706
|
+
steps.set(s.stepId, {
|
|
4707
|
+
id: s.stepId,
|
|
4708
|
+
parentId: s.parentId,
|
|
4709
|
+
name: s.name,
|
|
4710
|
+
type: s.type,
|
|
4711
|
+
order: order++,
|
|
4712
|
+
timestamp: s.timestamp,
|
|
4713
|
+
metadata: meta
|
|
4714
|
+
});
|
|
4715
|
+
}
|
|
4716
|
+
for (const e of events) {
|
|
4717
|
+
if (e.event !== "step_completed") continue;
|
|
4718
|
+
const acc = steps.get(e.stepId);
|
|
4719
|
+
if (!acc) continue;
|
|
4720
|
+
acc.status = e.status;
|
|
4721
|
+
acc.durationMs = e.durationMs;
|
|
4722
|
+
if (e.error?.message) acc.errorMsg = e.error.message;
|
|
4723
|
+
const extra = e;
|
|
4724
|
+
if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
|
|
4725
|
+
acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
4729
|
+
for (const acc of steps.values()) {
|
|
4730
|
+
let meta = acc.metadata ? { ...acc.metadata } : void 0;
|
|
4731
|
+
if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
|
|
4732
|
+
meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
|
|
4733
|
+
}
|
|
4734
|
+
const outputPreview = extractOutputPreview(meta);
|
|
4735
|
+
if (meta !== void 0 && ("outputPreview" in meta || "resultPreview" in meta)) {
|
|
4736
|
+
delete meta.outputPreview;
|
|
4737
|
+
delete meta.resultPreview;
|
|
4738
|
+
}
|
|
4739
|
+
const sc = {
|
|
4740
|
+
id: acc.id,
|
|
4741
|
+
name: acc.name,
|
|
4742
|
+
type: acc.type,
|
|
4743
|
+
status: mapStepStatus(acc.status),
|
|
4744
|
+
durationMs: acc.durationMs,
|
|
4745
|
+
error: acc.errorMsg,
|
|
4746
|
+
metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
|
|
4747
|
+
outputPreview,
|
|
4748
|
+
children: []
|
|
4749
|
+
};
|
|
4750
|
+
nodes.set(acc.id, sc);
|
|
4751
|
+
}
|
|
4752
|
+
const roots = [];
|
|
4753
|
+
const sortByOrder = (a, b) => {
|
|
4754
|
+
const oa = steps.get(a.id)?.order ?? 0;
|
|
4755
|
+
const ob = steps.get(b.id)?.order ?? 0;
|
|
4756
|
+
return oa - ob;
|
|
4757
|
+
};
|
|
4758
|
+
for (const acc of steps.values()) {
|
|
4759
|
+
const node = nodes.get(acc.id);
|
|
4760
|
+
if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
|
|
4761
|
+
nodes.get(acc.parentId).children.push(node);
|
|
4762
|
+
} else {
|
|
4763
|
+
roots.push(node);
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
roots.sort(sortByOrder);
|
|
4767
|
+
for (const n of nodes.values()) {
|
|
4768
|
+
n.children.sort(sortByOrder);
|
|
4769
|
+
}
|
|
4770
|
+
return {
|
|
4771
|
+
runId,
|
|
4772
|
+
name: rs.name,
|
|
4773
|
+
status: runStatus,
|
|
4774
|
+
durationMs,
|
|
4775
|
+
steps: roots
|
|
4776
|
+
};
|
|
4777
|
+
}
|
|
4778
|
+
|
|
4779
|
+
// packages/core/src/diff/engine.ts
|
|
4780
|
+
var DEFAULT_THRESHOLD_MS = 0;
|
|
4781
|
+
function pathSeg(step, index) {
|
|
4782
|
+
return { index, name: step.name, stepId: step.id };
|
|
4783
|
+
}
|
|
4784
|
+
function buildPath(segments) {
|
|
4785
|
+
return { path: [...segments] };
|
|
4786
|
+
}
|
|
4787
|
+
function pairSteps(left, right) {
|
|
4788
|
+
const usedRight = /* @__PURE__ */ new Set();
|
|
4789
|
+
const pairs = [];
|
|
4790
|
+
for (let i = 0; i < left.length; i++) {
|
|
4791
|
+
const L = left[i];
|
|
4792
|
+
let R = right.find((r) => !usedRight.has(r.id) && r.id === L.id);
|
|
4793
|
+
if (R === void 0 && i < right.length && !usedRight.has(right[i].id)) {
|
|
4794
|
+
const cand = right[i];
|
|
4795
|
+
if (cand.name === L.name && (cand.type ?? "") === (L.type ?? "")) {
|
|
4796
|
+
R = cand;
|
|
4797
|
+
}
|
|
4798
|
+
}
|
|
4799
|
+
if (R === void 0) {
|
|
4800
|
+
R = right.find(
|
|
4801
|
+
(r) => !usedRight.has(r.id) && r.name === L.name && (r.type ?? "") === (L.type ?? "")
|
|
4802
|
+
);
|
|
4803
|
+
}
|
|
4804
|
+
if (R !== void 0) {
|
|
4805
|
+
usedRight.add(R.id);
|
|
4806
|
+
pairs.push([L, R]);
|
|
4807
|
+
} else {
|
|
4808
|
+
pairs.push([L, void 0]);
|
|
4809
|
+
}
|
|
4810
|
+
}
|
|
4811
|
+
for (const R of right) {
|
|
4812
|
+
if (!usedRight.has(R.id)) {
|
|
4813
|
+
pairs.push([void 0, R]);
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4816
|
+
return pairs;
|
|
4817
|
+
}
|
|
4818
|
+
function compareLeafSteps(L, R, segments, opts, out) {
|
|
4819
|
+
const path14 = buildPath(segments);
|
|
4820
|
+
if (L.name !== R.name) {
|
|
4821
|
+
out.push({
|
|
4822
|
+
kind: "structure",
|
|
4823
|
+
severity: "warning",
|
|
4824
|
+
message: "Step name differs",
|
|
4825
|
+
path: path14,
|
|
4826
|
+
left: L.name,
|
|
4827
|
+
right: R.name
|
|
4828
|
+
});
|
|
4829
|
+
}
|
|
4830
|
+
if ((L.type ?? "") !== (R.type ?? "")) {
|
|
4831
|
+
out.push({
|
|
4832
|
+
kind: "step-type",
|
|
4833
|
+
severity: "warning",
|
|
4834
|
+
message: "Step type differs",
|
|
4835
|
+
path: path14,
|
|
4836
|
+
left: L.type,
|
|
4837
|
+
right: R.type
|
|
4838
|
+
});
|
|
4839
|
+
}
|
|
4840
|
+
if ((L.status ?? "") !== (R.status ?? "")) {
|
|
4841
|
+
out.push({
|
|
4842
|
+
kind: "step-status",
|
|
4843
|
+
severity: "warning",
|
|
4844
|
+
message: "Step status differs",
|
|
4845
|
+
path: path14,
|
|
4846
|
+
left: L.status,
|
|
4847
|
+
right: R.status
|
|
4848
|
+
});
|
|
4849
|
+
}
|
|
4850
|
+
const le = L.error ?? "";
|
|
4851
|
+
const re = R.error ?? "";
|
|
4852
|
+
if (le !== re) {
|
|
4853
|
+
out.push({
|
|
4854
|
+
kind: "error",
|
|
4855
|
+
severity: "error",
|
|
4856
|
+
message: "Step error message differs",
|
|
4857
|
+
path: path14,
|
|
4858
|
+
left: le || void 0,
|
|
4859
|
+
right: re || void 0
|
|
4860
|
+
});
|
|
4861
|
+
}
|
|
4862
|
+
if (!opts.ignoreDuration) {
|
|
4863
|
+
const ld = L.durationMs;
|
|
4864
|
+
const rd = R.durationMs;
|
|
4865
|
+
const th = opts.durationThresholdMs;
|
|
4866
|
+
let differs = false;
|
|
4867
|
+
if (ld === void 0 && rd === void 0) differs = false;
|
|
4868
|
+
else if (ld === void 0 || rd === void 0) differs = true;
|
|
4869
|
+
else differs = Math.abs(ld - rd) > th;
|
|
4870
|
+
if (differs) {
|
|
4871
|
+
out.push({
|
|
4872
|
+
kind: "duration",
|
|
4873
|
+
severity: "info",
|
|
4874
|
+
message: "Step duration differs",
|
|
4875
|
+
path: path14,
|
|
4876
|
+
left: ld,
|
|
4877
|
+
right: rd
|
|
4878
|
+
});
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4881
|
+
const lm = stableJson(L.metadata ?? {});
|
|
4882
|
+
const rm = stableJson(R.metadata ?? {});
|
|
4883
|
+
if (lm !== rm) {
|
|
4884
|
+
out.push({
|
|
4885
|
+
kind: "metadata",
|
|
4886
|
+
severity: "info",
|
|
4887
|
+
message: "Step metadata differs",
|
|
4888
|
+
path: path14,
|
|
4889
|
+
left: L.metadata,
|
|
4890
|
+
right: R.metadata
|
|
4891
|
+
});
|
|
4892
|
+
}
|
|
4893
|
+
const lo = stableJson(L.outputPreview ?? null);
|
|
4894
|
+
const ro = stableJson(R.outputPreview ?? null);
|
|
4895
|
+
if (lo !== ro) {
|
|
4896
|
+
out.push({
|
|
4897
|
+
kind: "output",
|
|
4898
|
+
severity: "info",
|
|
4899
|
+
message: "Output preview differs",
|
|
4900
|
+
path: path14,
|
|
4901
|
+
left: L.outputPreview,
|
|
4902
|
+
right: R.outputPreview
|
|
4903
|
+
});
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
function compareRecursive(L, R, segments, opts, out) {
|
|
4907
|
+
compareLeafSteps(L, R, segments, opts, out);
|
|
4908
|
+
const pairs = pairSteps(L.children, R.children);
|
|
4909
|
+
let ci = 0;
|
|
4910
|
+
for (const [lch, rch] of pairs) {
|
|
4911
|
+
if (lch !== void 0 && rch !== void 0) {
|
|
4912
|
+
compareRecursive(lch, rch, [...segments, pathSeg(lch, ci)], opts, out);
|
|
4913
|
+
} else if (lch !== void 0) {
|
|
4914
|
+
out.push({
|
|
4915
|
+
kind: "step-removed",
|
|
4916
|
+
severity: "warning",
|
|
4917
|
+
message: `Step only in left run: ${lch.name}`,
|
|
4918
|
+
path: buildPath([...segments, pathSeg(lch, ci)]),
|
|
4919
|
+
left: lch.id,
|
|
4920
|
+
right: void 0
|
|
4921
|
+
});
|
|
4922
|
+
} else if (rch !== void 0) {
|
|
4923
|
+
out.push({
|
|
4924
|
+
kind: "step-added",
|
|
4925
|
+
severity: "warning",
|
|
4926
|
+
message: `Step only in right run: ${rch.name}`,
|
|
4927
|
+
path: buildPath([...segments, pathSeg(rch, ci)]),
|
|
4928
|
+
left: void 0,
|
|
4929
|
+
right: rch.id
|
|
4930
|
+
});
|
|
4931
|
+
}
|
|
4932
|
+
ci += 1;
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
function mergeDiffDefaults(options) {
|
|
4936
|
+
return {
|
|
4937
|
+
ignoreDuration: options?.ignoreDuration ?? false,
|
|
4938
|
+
durationThresholdMs: options?.durationThresholdMs !== void 0 ? options.durationThresholdMs : DEFAULT_THRESHOLD_MS,
|
|
4939
|
+
focus: options?.focus ?? "all",
|
|
4940
|
+
check: options?.check ?? "all"
|
|
4941
|
+
};
|
|
4942
|
+
}
|
|
4943
|
+
function kindMatchesFilter(kind, merged) {
|
|
4944
|
+
const { focus, check } = merged;
|
|
4945
|
+
if (check !== "all") {
|
|
4946
|
+
if (check === "structure") {
|
|
4947
|
+
if (!["step-added", "step-removed", "structure", "step-type"].includes(kind)) return false;
|
|
4948
|
+
} else if (check === "outputs") {
|
|
4949
|
+
if (!["metadata", "output"].includes(kind)) return false;
|
|
4950
|
+
} else if (check === "errors") {
|
|
4951
|
+
if (!["run-status", "step-status", "error"].includes(kind)) return false;
|
|
4952
|
+
} else if (check === "timing") {
|
|
4953
|
+
if (kind !== "duration") return false;
|
|
4954
|
+
}
|
|
4955
|
+
}
|
|
4956
|
+
if (focus !== "all") {
|
|
4957
|
+
if (focus === "errors") {
|
|
4958
|
+
if (!["run-status", "step-status", "error"].includes(kind)) return false;
|
|
4959
|
+
} else if (focus === "structure") {
|
|
4960
|
+
if (!["step-added", "step-removed", "structure", "step-type"].includes(kind)) return false;
|
|
4961
|
+
} else if (focus === "outputs") {
|
|
4962
|
+
if (!["metadata", "output"].includes(kind)) return false;
|
|
4963
|
+
}
|
|
4964
|
+
}
|
|
4965
|
+
return true;
|
|
4966
|
+
}
|
|
4967
|
+
function diffRuns(left, right, options) {
|
|
4968
|
+
const merged = mergeDiffDefaults(options);
|
|
4969
|
+
const opts = {
|
|
4970
|
+
ignoreDuration: merged.ignoreDuration,
|
|
4971
|
+
durationThresholdMs: merged.durationThresholdMs
|
|
4972
|
+
};
|
|
4973
|
+
const raw = [];
|
|
4974
|
+
if ((left.status ?? "") !== (right.status ?? "")) {
|
|
4975
|
+
raw.push({
|
|
4976
|
+
kind: "run-status",
|
|
4977
|
+
severity: "warning",
|
|
4978
|
+
message: "Run completion status differs",
|
|
4979
|
+
left: left.status,
|
|
4980
|
+
right: right.status
|
|
4981
|
+
});
|
|
4982
|
+
}
|
|
4983
|
+
if (!merged.ignoreDuration) {
|
|
4984
|
+
const ld = left.durationMs;
|
|
4985
|
+
const rd = right.durationMs;
|
|
4986
|
+
const th = merged.durationThresholdMs;
|
|
4987
|
+
let differs = false;
|
|
4988
|
+
if (ld === void 0 && rd === void 0) differs = false;
|
|
4989
|
+
else if (ld === void 0 || rd === void 0) differs = true;
|
|
4990
|
+
else differs = Math.abs(ld - rd) > th;
|
|
4991
|
+
if (differs) {
|
|
4992
|
+
raw.push({
|
|
4993
|
+
kind: "duration",
|
|
4994
|
+
severity: "info",
|
|
4995
|
+
message: "Run duration differs",
|
|
4996
|
+
left: ld,
|
|
4997
|
+
right: rd
|
|
4998
|
+
});
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
const pairs = pairSteps(left.steps, right.steps);
|
|
5002
|
+
let idx = 0;
|
|
5003
|
+
for (const [ls, rs] of pairs) {
|
|
5004
|
+
if (ls !== void 0 && rs !== void 0) {
|
|
5005
|
+
compareRecursive(ls, rs, [pathSeg(ls, idx)], opts, raw);
|
|
5006
|
+
idx += 1;
|
|
5007
|
+
} else if (ls !== void 0) {
|
|
5008
|
+
raw.push({
|
|
5009
|
+
kind: "step-removed",
|
|
5010
|
+
severity: "warning",
|
|
5011
|
+
message: `Step only in left run: ${ls.name}`,
|
|
5012
|
+
path: buildPath([pathSeg(ls, idx)]),
|
|
5013
|
+
left: ls.id,
|
|
5014
|
+
right: void 0
|
|
5015
|
+
});
|
|
5016
|
+
idx += 1;
|
|
5017
|
+
} else if (rs !== void 0) {
|
|
5018
|
+
raw.push({
|
|
5019
|
+
kind: "step-added",
|
|
5020
|
+
severity: "warning",
|
|
5021
|
+
message: `Step only in right run: ${rs.name}`,
|
|
5022
|
+
path: buildPath([pathSeg(rs, idx)]),
|
|
5023
|
+
left: void 0,
|
|
5024
|
+
right: rs.id
|
|
5025
|
+
});
|
|
5026
|
+
idx += 1;
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
const differences = raw.filter((d) => kindMatchesFilter(d.kind, merged));
|
|
5030
|
+
let errors = 0;
|
|
5031
|
+
let warnings = 0;
|
|
5032
|
+
let info = 0;
|
|
5033
|
+
for (const d of differences) {
|
|
5034
|
+
if (d.severity === "error") errors += 1;
|
|
5035
|
+
else if (d.severity === "warning") warnings += 1;
|
|
5036
|
+
else info += 1;
|
|
5037
|
+
}
|
|
5038
|
+
const firstVisible = differences[0];
|
|
5039
|
+
const firstDivergence = firstVisible !== void 0 ? {
|
|
5040
|
+
kind: "first-divergence",
|
|
5041
|
+
severity: firstVisible.severity,
|
|
5042
|
+
message: `First divergence: ${firstVisible.message}`,
|
|
5043
|
+
path: firstVisible.path,
|
|
5044
|
+
left: firstVisible.left,
|
|
5045
|
+
right: firstVisible.right
|
|
5046
|
+
} : void 0;
|
|
5047
|
+
const summary = {
|
|
5048
|
+
leftRunId: left.runId,
|
|
5049
|
+
rightRunId: right.runId,
|
|
5050
|
+
totalDifferences: differences.length,
|
|
5051
|
+
errors,
|
|
5052
|
+
warnings,
|
|
5053
|
+
info,
|
|
5054
|
+
firstDivergence
|
|
5055
|
+
};
|
|
5056
|
+
return { summary, differences };
|
|
5057
|
+
}
|
|
5058
|
+
|
|
5059
|
+
// packages/core/src/diff/index.ts
|
|
5060
|
+
function diffTraceEvents(leftEvents, rightEvents, options) {
|
|
5061
|
+
const left = manualTraceEventsToComparableRun(leftEvents);
|
|
5062
|
+
const right = manualTraceEventsToComparableRun(rightEvents);
|
|
5063
|
+
return diffRuns(left, right, options);
|
|
5064
|
+
}
|
|
5065
|
+
|
|
5066
|
+
// packages/core/src/evidence/views-contract.ts
|
|
5067
|
+
function boundMessage(message, max = 200) {
|
|
5068
|
+
const trimmed = message.trim();
|
|
5069
|
+
if (trimmed.length <= max) return trimmed;
|
|
5070
|
+
return `${trimmed.slice(0, max)}\u2026`;
|
|
5071
|
+
}
|
|
5072
|
+
function buildEvidenceContractsViewHtml(input) {
|
|
5073
|
+
const rows = input.runs.map(
|
|
5074
|
+
(run) => `<tr><td><code>${escapeHtml(run.runId)}</code></td><td>${escapeHtml(run.status)}</td><td>${escapeHtml(run.sourceStatus ?? "\u2014")}</td><td>${run.errors}</td><td>${run.warnings}</td><td>${run.findings}</td></tr>`
|
|
5075
|
+
).join("");
|
|
5076
|
+
const findings = input.findingSummaries ?? [];
|
|
5077
|
+
const findingRows = findings.length === 0 ? `<p class="muted">No structured check findings recorded for the redacted artifact.</p>` : `<table>
|
|
5078
|
+
<thead><tr><th>runId</th><th>severity</th><th>rule</th><th>category</th><th>detector</th><th>message</th></tr></thead>
|
|
5079
|
+
<tbody>${findings.map(
|
|
5080
|
+
(f) => `<tr><td><code>${escapeHtml(f.runId)}</code></td><td>${escapeHtml(f.severity)}</td><td><code>${escapeHtml(f.ruleId)}</code></td><td>${escapeHtml(f.category ?? "\u2014")}</td><td>${escapeHtml(f.detector ?? "\u2014")}</td><td>${escapeHtml(boundMessage(f.message))}</td></tr>`
|
|
5081
|
+
).join("")}</tbody>
|
|
5082
|
+
</table>`;
|
|
5083
|
+
return `<p>Aggregate artifact status: <strong>${escapeHtml(input.aggregateStatus)}</strong></p>
|
|
5084
|
+
<table>
|
|
5085
|
+
<thead><tr><th>runId</th><th>artifact</th><th>source</th><th>errors</th><th>warnings</th><th>findings</th></tr></thead>
|
|
5086
|
+
<tbody>${rows}</tbody>
|
|
5087
|
+
</table>
|
|
5088
|
+
<h3>Finding summaries</h3>
|
|
5089
|
+
${findingRows}
|
|
5090
|
+
<p class="muted">TraceContract / check details are best-effort local results \u2014 not a compliance certification.</p>`;
|
|
5091
|
+
}
|
|
5092
|
+
function buildEvidenceOutcomesViewHtml(runs) {
|
|
5093
|
+
if (runs.length === 0) {
|
|
5094
|
+
return `<p class="muted">No runs available for outcome extraction.</p>`;
|
|
5095
|
+
}
|
|
5096
|
+
const parts = [];
|
|
5097
|
+
for (const run of runs) {
|
|
5098
|
+
const forRun = run.events.filter((event) => event.runId === run.runId);
|
|
5099
|
+
const summary = summarizeObservedOutcomes(extractOutcomesFromPersistedEvents(forRun));
|
|
5100
|
+
parts.push(`<article class="run-block">`);
|
|
5101
|
+
parts.push(`<h3><code>${escapeHtml(run.runId)}</code></h3>`);
|
|
5102
|
+
parts.push(renderObservedOutcomesHtml(summary));
|
|
5103
|
+
parts.push(`</article>`);
|
|
5104
|
+
}
|
|
5105
|
+
return parts.join("\n");
|
|
5106
|
+
}
|
|
5107
|
+
function buildEvidenceDiffViewHtml(parts) {
|
|
5108
|
+
if (parts === void 0 || parts.leftEvents.length === 0 || parts.rightEvents.length === 0) {
|
|
5109
|
+
return `<p class="muted">No baseline/candidate pair was supplied for this evidence bundle. Attach two runs (or a reporter baseline) to populate this view.</p>`;
|
|
5110
|
+
}
|
|
5111
|
+
try {
|
|
5112
|
+
const left = persistedInspectEventsToTraceEvents(
|
|
5113
|
+
parts.leftEvents.filter((e) => e.runId === parts.leftRunId)
|
|
5114
|
+
);
|
|
5115
|
+
const right = persistedInspectEventsToTraceEvents(
|
|
5116
|
+
parts.rightEvents.filter((e) => e.runId === parts.rightRunId)
|
|
5117
|
+
);
|
|
5118
|
+
if (left.length === 0 || right.length === 0) {
|
|
5119
|
+
return `<p class="muted">Could not normalize both runs for diff (missing v0.1-compatible events).</p>`;
|
|
5120
|
+
}
|
|
5121
|
+
const result = diffTraceEvents(left, right);
|
|
5122
|
+
const text = renderRunDiff(result, { color: false, verbose: false });
|
|
5123
|
+
return `<p>Comparing <code>${escapeHtml(parts.leftRunId)}</code> \u2192 <code>${escapeHtml(parts.rightRunId)}</code></p>
|
|
5124
|
+
<pre class="summary-md">${escapeHtml(text)}</pre>`;
|
|
5125
|
+
} catch (error) {
|
|
5126
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5127
|
+
return `<p class="muted">Diff unavailable: ${escapeHtml(message)}</p>`;
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
|
|
5131
|
+
// packages/core/src/evidence/views-safety.ts
|
|
5132
|
+
function buildEvidenceSafetyViewHtml(input) {
|
|
5133
|
+
const findingRows = (input.findingSummaries ?? []).length === 0 ? `<p class="muted">No safety findings on the redacted artifact.</p>` : `<table>
|
|
5134
|
+
<thead><tr><th>runId</th><th>severity</th><th>category</th><th>detector</th><th>action</th><th>message</th></tr></thead>
|
|
5135
|
+
<tbody>${(input.findingSummaries ?? []).map((f) => {
|
|
5136
|
+
const msg = f.message.length > 160 ? `${f.message.slice(0, 160)}\u2026` : f.message;
|
|
5137
|
+
return `<tr><td><code>${escapeHtml(f.runId)}</code></td><td>${escapeHtml(f.severity)}</td><td>${escapeHtml(f.category ?? "\u2014")}</td><td>${escapeHtml(f.detector ?? "\u2014")}</td><td>${escapeHtml(f.action ?? "\u2014")}</td><td>${escapeHtml(msg)}</td></tr>`;
|
|
5138
|
+
}).join("")}</tbody>
|
|
5139
|
+
</table>`;
|
|
5140
|
+
const redactionBlock = input.redaction === void 0 ? `<p class="muted">No redaction report attached.</p>` : `<p>Total redaction findings: <strong>${input.redaction.totalFindings}</strong></p>
|
|
5141
|
+
<ul>${input.redaction.runs.map(
|
|
5142
|
+
(run) => `<li><code>${escapeHtml(run.runId)}</code>: ${run.findings} finding(s); detectors: ${escapeHtml(run.detectors.join(", ") || "none")}</li>`
|
|
5143
|
+
).join("")}</ul>`;
|
|
5144
|
+
return `<p>Artifact status: <strong>${escapeHtml(String(input.artifactStatus))}</strong>
|
|
5145
|
+
${input.sourceStatus !== void 0 ? ` \xB7 Source status: <strong>${escapeHtml(String(input.sourceStatus))}</strong>` : ""}</p>
|
|
5146
|
+
<p>Redaction profile: <code>${escapeHtml(input.redactionProfile)}</code> \xB7 Verification: <code>${escapeHtml(input.verificationPolicy)}</code></p>
|
|
5147
|
+
<h3>Redaction</h3>
|
|
5148
|
+
${redactionBlock}
|
|
5149
|
+
<h3>Safety findings (artifact)</h3>
|
|
5150
|
+
${findingRows}
|
|
5151
|
+
<p class="muted">Best-effort local verification only \u2014 not a compliance certification. Gate sharing on artifact status.</p>`;
|
|
5152
|
+
}
|
|
5153
|
+
function buildEvidenceProvenanceViewHtml(input) {
|
|
5154
|
+
const hashes = input.sourceHashes.length === 0 ? `<p class="muted">No source hashes recorded.</p>` : `<table>
|
|
5155
|
+
<thead><tr><th>runId</th><th>algorithm</th><th>hash</th></tr></thead>
|
|
5156
|
+
<tbody>${input.sourceHashes.map(
|
|
5157
|
+
(h) => `<tr><td><code>${escapeHtml(h.runId)}</code></td><td>${escapeHtml(h.algorithm)}</td><td><code>${escapeHtml(h.hash)}</code></td></tr>`
|
|
5158
|
+
).join("")}</tbody>
|
|
5159
|
+
</table>`;
|
|
5160
|
+
const files = input.packagedFiles.length === 0 ? `<p class="muted">No packaged files listed.</p>` : `<ul>${input.packagedFiles.map(
|
|
5161
|
+
(f) => `<li><code>${escapeHtml(f.path)}</code>${f.role ? ` <span class="meta">(${escapeHtml(f.role)})</span>` : ""}</li>`
|
|
5162
|
+
).join("")}</ul>`;
|
|
5163
|
+
return `<p>Generator: <code>${escapeHtml(input.generatorName)}@${escapeHtml(input.generatorVersion)}</code>
|
|
5164
|
+
\xB7 Evidence format: <code>${escapeHtml(input.evidenceFormatVersion)}</code>
|
|
5165
|
+
${input.createdAt ? ` \xB7 Created: <code>${escapeHtml(input.createdAt)}</code>` : ""}</p>
|
|
5166
|
+
<p>Runs: ${input.runIds.map((id) => `<code>${escapeHtml(id)}</code>`).join(", ")}</p>
|
|
5167
|
+
<p>Trace schema versions: ${input.traceSchemaVersions.length > 0 ? input.traceSchemaVersions.map((v) => `<code>${escapeHtml(v)}</code>`).join(", ") : '<span class="muted">unknown</span>'}</p>
|
|
5168
|
+
<h3>Source hashes (pre-redaction input)</h3>
|
|
5169
|
+
${hashes}
|
|
5170
|
+
<h3>Packaged files</h3>
|
|
5171
|
+
${files}
|
|
5172
|
+
<p class="muted">${escapeHtml(input.note ?? "Reader/mapping losses are reported elsewhere when present; relationships are never invented without confidence policy.")}</p>`;
|
|
5173
|
+
}
|
|
5174
|
+
function buildEvidenceToolsLlmViewHtml(trees) {
|
|
5175
|
+
if (trees.length === 0) {
|
|
5176
|
+
return `<p class="muted">No runs available for tool/LLM metadata.</p>`;
|
|
5177
|
+
}
|
|
5178
|
+
const parts = [];
|
|
5179
|
+
for (const tree of trees) {
|
|
5180
|
+
const nodes = flattenTree(tree).filter(
|
|
5181
|
+
(n) => n.event.kind === "TOOL" || n.event.kind === "LLM" || n.event.kind === "AGENT"
|
|
5182
|
+
);
|
|
5183
|
+
parts.push(`<article class="run-block">`);
|
|
5184
|
+
parts.push(`<h3><code>${escapeHtml(tree.runId)}</code></h3>`);
|
|
5185
|
+
if (nodes.length === 0) {
|
|
5186
|
+
parts.push(`<p class="muted">No TOOL/LLM/AGENT events in this run.</p>`);
|
|
5187
|
+
} else {
|
|
5188
|
+
parts.push(`<table>
|
|
5189
|
+
<thead><tr><th>name</th><th>kind</th><th>status</th><th>durationMs</th></tr></thead>
|
|
5190
|
+
<tbody>${nodes.map((n) => {
|
|
5191
|
+
const dur = n.event.durationMs !== void 0 && Number.isFinite(n.event.durationMs) ? String(n.event.durationMs) : "\u2014";
|
|
5192
|
+
return `<tr><td>${escapeHtml(n.event.name)}</td><td>${escapeHtml(n.event.kind)}</td><td>${escapeHtml(n.event.status ?? "?")}</td><td>${escapeHtml(dur)}</td></tr>`;
|
|
5193
|
+
}).join("")}</tbody>
|
|
5194
|
+
</table>`);
|
|
5195
|
+
}
|
|
5196
|
+
parts.push(`</article>`);
|
|
5197
|
+
}
|
|
5198
|
+
return parts.join("\n");
|
|
5199
|
+
}
|
|
5200
|
+
function buildEvidenceCircuitViewHtml(parts) {
|
|
5201
|
+
const findings = parts?.findings ?? [];
|
|
5202
|
+
if (findings.length === 0) {
|
|
5203
|
+
return `<p class="muted">No circuit or guardrail findings were attached to this evidence bundle.</p>`;
|
|
5204
|
+
}
|
|
5205
|
+
return `<table>
|
|
5206
|
+
<thead><tr><th>runId</th><th>name</th><th>status</th><th>detail</th></tr></thead>
|
|
5207
|
+
<tbody>${findings.map(
|
|
5208
|
+
(f) => `<tr><td><code>${escapeHtml(f.runId)}</code></td><td>${escapeHtml(f.name)}</td><td>${escapeHtml(f.status)}</td><td>${escapeHtml(f.detail ?? "\u2014")}</td></tr>`
|
|
5209
|
+
).join("")}</tbody>
|
|
5210
|
+
</table>`;
|
|
5211
|
+
}
|
|
5212
|
+
|
|
5213
|
+
// packages/core/src/evidence/zip.ts
|
|
5214
|
+
var CRC_TABLE = (() => {
|
|
5215
|
+
const table = new Uint32Array(256);
|
|
5216
|
+
for (let n = 0; n < 256; n += 1) {
|
|
5217
|
+
let c = n;
|
|
5218
|
+
for (let k = 0; k < 8; k += 1) {
|
|
5219
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
5220
|
+
}
|
|
5221
|
+
table[n] = c >>> 0;
|
|
5222
|
+
}
|
|
5223
|
+
return table;
|
|
5224
|
+
})();
|
|
5225
|
+
function crc32(data) {
|
|
5226
|
+
let crc = 4294967295;
|
|
5227
|
+
for (let i = 0; i < data.length; i += 1) {
|
|
5228
|
+
crc = CRC_TABLE[(crc ^ data[i]) & 255] ^ crc >>> 8;
|
|
5229
|
+
}
|
|
5230
|
+
return (crc ^ 4294967295) >>> 0;
|
|
5231
|
+
}
|
|
5232
|
+
function toBytes(content) {
|
|
5233
|
+
return typeof content === "string" ? Buffer.from(content, "utf8") : content;
|
|
5234
|
+
}
|
|
5235
|
+
function u16(value) {
|
|
5236
|
+
const buf = Buffer.alloc(2);
|
|
5237
|
+
buf.writeUInt16LE(value >>> 0, 0);
|
|
5238
|
+
return buf;
|
|
5239
|
+
}
|
|
5240
|
+
function u32(value) {
|
|
5241
|
+
const buf = Buffer.alloc(4);
|
|
5242
|
+
buf.writeUInt32LE(value >>> 0, 0);
|
|
5243
|
+
return buf;
|
|
5244
|
+
}
|
|
5245
|
+
function buildZipArchive(entries) {
|
|
5246
|
+
if (entries.length === 0) {
|
|
5247
|
+
throw new Error("ZIP archive requires at least one entry.");
|
|
5248
|
+
}
|
|
5249
|
+
const locals = [];
|
|
5250
|
+
const centrals = [];
|
|
5251
|
+
let offset = 0;
|
|
5252
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5253
|
+
for (const entry of entries) {
|
|
5254
|
+
const name = assertEvidenceRelativePath(entry.path);
|
|
5255
|
+
if (seen.has(name)) {
|
|
5256
|
+
throw new Error(`Duplicate ZIP entry path: ${name}`);
|
|
5257
|
+
}
|
|
5258
|
+
seen.add(name);
|
|
5259
|
+
const nameBytes = Buffer.from(name, "utf8");
|
|
5260
|
+
const data = toBytes(entry.content);
|
|
5261
|
+
const checksum = crc32(data);
|
|
5262
|
+
const size = data.byteLength;
|
|
5263
|
+
const local = Buffer.concat([
|
|
5264
|
+
u32(67324752),
|
|
5265
|
+
u16(20),
|
|
5266
|
+
// version needed
|
|
5267
|
+
u16(0),
|
|
5268
|
+
// flags
|
|
5269
|
+
u16(0),
|
|
5270
|
+
// method STORE
|
|
5271
|
+
u16(0),
|
|
5272
|
+
// time
|
|
5273
|
+
u16(0),
|
|
5274
|
+
// date
|
|
5275
|
+
u32(checksum),
|
|
5276
|
+
u32(size),
|
|
5277
|
+
u32(size),
|
|
5278
|
+
u16(nameBytes.length),
|
|
5279
|
+
u16(0),
|
|
5280
|
+
// extra length
|
|
5281
|
+
nameBytes,
|
|
5282
|
+
Buffer.from(data)
|
|
5283
|
+
]);
|
|
5284
|
+
const central = Buffer.concat([
|
|
5285
|
+
u32(33639248),
|
|
5286
|
+
u16(20),
|
|
5287
|
+
// version made by
|
|
5288
|
+
u16(20),
|
|
5289
|
+
// version needed
|
|
5290
|
+
u16(0),
|
|
5291
|
+
u16(0),
|
|
5292
|
+
u16(0),
|
|
5293
|
+
u16(0),
|
|
5294
|
+
u32(checksum),
|
|
5295
|
+
u32(size),
|
|
5296
|
+
u32(size),
|
|
5297
|
+
u16(nameBytes.length),
|
|
5298
|
+
u16(0),
|
|
5299
|
+
u16(0),
|
|
5300
|
+
u16(0),
|
|
5301
|
+
u16(0),
|
|
5302
|
+
u32(0),
|
|
5303
|
+
u32(offset),
|
|
5304
|
+
nameBytes
|
|
5305
|
+
]);
|
|
5306
|
+
locals.push(local);
|
|
5307
|
+
centrals.push(central);
|
|
5308
|
+
offset += local.length;
|
|
5309
|
+
}
|
|
5310
|
+
const centralDir = Buffer.concat(centrals);
|
|
5311
|
+
const end = Buffer.concat([
|
|
5312
|
+
u32(101010256),
|
|
5313
|
+
u16(0),
|
|
5314
|
+
u16(0),
|
|
5315
|
+
u16(entries.length),
|
|
5316
|
+
u16(entries.length),
|
|
5317
|
+
u32(centralDir.length),
|
|
5318
|
+
u32(offset),
|
|
5319
|
+
u16(0)
|
|
5320
|
+
]);
|
|
5321
|
+
return Buffer.concat([...locals, centralDir, end]);
|
|
5322
|
+
}
|
|
5323
|
+
async function listFilesRecursive(root) {
|
|
5324
|
+
const out = [];
|
|
5325
|
+
async function walk(dir) {
|
|
5326
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
5327
|
+
for (const entry of entries) {
|
|
5328
|
+
const abs = path5.join(dir, entry.name);
|
|
5329
|
+
if (entry.isDirectory()) {
|
|
5330
|
+
await walk(abs);
|
|
5331
|
+
} else if (entry.isFile()) {
|
|
5332
|
+
const rel = path5.relative(root, abs).split(path5.sep).join("/");
|
|
5333
|
+
out.push(rel);
|
|
5334
|
+
}
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5337
|
+
await walk(root);
|
|
5338
|
+
return out.sort((a, b) => a.localeCompare(b));
|
|
5339
|
+
}
|
|
5340
|
+
async function verifyEvidenceDirectory(rootPath, options = {}) {
|
|
5341
|
+
const unexpectedMode = options.unexpectedFiles ?? "fail";
|
|
5342
|
+
const root = path5.resolve(rootPath);
|
|
5343
|
+
const issues = [];
|
|
5344
|
+
let rootStat;
|
|
5345
|
+
try {
|
|
5346
|
+
rootStat = await stat(root);
|
|
5347
|
+
} catch (error) {
|
|
5348
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5349
|
+
return {
|
|
5350
|
+
ok: false,
|
|
5351
|
+
status: "fail",
|
|
5352
|
+
root,
|
|
5353
|
+
issues: [{ code: "io_error", severity: "error", message: `Cannot read path: ${message}` }],
|
|
5354
|
+
checkedFiles: 0
|
|
5355
|
+
};
|
|
5356
|
+
}
|
|
5357
|
+
if (!rootStat.isDirectory()) {
|
|
5358
|
+
return {
|
|
5359
|
+
ok: false,
|
|
5360
|
+
status: "fail",
|
|
5361
|
+
root,
|
|
5362
|
+
issues: [
|
|
5363
|
+
{
|
|
5364
|
+
code: "io_error",
|
|
5365
|
+
severity: "error",
|
|
5366
|
+
message: "Evidence verify expects a directory containing evidence.json (unpack ZIP first)."
|
|
5367
|
+
}
|
|
5368
|
+
],
|
|
5369
|
+
checkedFiles: 0
|
|
5370
|
+
};
|
|
5371
|
+
}
|
|
5372
|
+
const manifestPath = path5.join(root, EVIDENCE_MANIFEST_FILENAME);
|
|
5373
|
+
let manifestText;
|
|
5374
|
+
try {
|
|
5375
|
+
manifestText = await readFile(manifestPath, "utf-8");
|
|
5376
|
+
} catch {
|
|
5377
|
+
return {
|
|
5378
|
+
ok: false,
|
|
5379
|
+
status: "fail",
|
|
5380
|
+
root,
|
|
5381
|
+
issues: [
|
|
5382
|
+
{
|
|
5383
|
+
code: "manifest_missing",
|
|
5384
|
+
severity: "error",
|
|
5385
|
+
message: `Missing ${EVIDENCE_MANIFEST_FILENAME}`,
|
|
5386
|
+
path: EVIDENCE_MANIFEST_FILENAME
|
|
5387
|
+
}
|
|
5388
|
+
],
|
|
5389
|
+
checkedFiles: 0
|
|
5390
|
+
};
|
|
5391
|
+
}
|
|
5392
|
+
let manifest;
|
|
5393
|
+
try {
|
|
5394
|
+
manifest = parseEvidenceManifestJson(manifestText);
|
|
5395
|
+
} catch (error) {
|
|
5396
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5397
|
+
return {
|
|
5398
|
+
ok: false,
|
|
5399
|
+
status: "fail",
|
|
5400
|
+
root,
|
|
5401
|
+
issues: [
|
|
5402
|
+
{
|
|
5403
|
+
code: "manifest_invalid",
|
|
5404
|
+
severity: "error",
|
|
5405
|
+
message,
|
|
5406
|
+
path: EVIDENCE_MANIFEST_FILENAME
|
|
5407
|
+
}
|
|
5408
|
+
],
|
|
5409
|
+
checkedFiles: 0
|
|
5410
|
+
};
|
|
5411
|
+
}
|
|
5412
|
+
if (!manifest.assessment?.status) {
|
|
5413
|
+
issues.push({
|
|
5414
|
+
code: "assessment_missing",
|
|
5415
|
+
severity: "error",
|
|
5416
|
+
message: "Manifest assessment.status is required."
|
|
5417
|
+
});
|
|
5418
|
+
}
|
|
5419
|
+
if (!manifest.generator?.name || !manifest.generator?.version) {
|
|
5420
|
+
issues.push({
|
|
5421
|
+
code: "provenance_missing",
|
|
5422
|
+
severity: "error",
|
|
5423
|
+
message: "Manifest generator.name and generator.version are required."
|
|
5424
|
+
});
|
|
5425
|
+
}
|
|
5426
|
+
if (!manifest.source?.runIds?.length) {
|
|
5427
|
+
issues.push({
|
|
5428
|
+
code: "provenance_missing",
|
|
5429
|
+
severity: "error",
|
|
5430
|
+
message: "Manifest source.runIds must be non-empty."
|
|
5431
|
+
});
|
|
5432
|
+
}
|
|
5433
|
+
const listed = /* @__PURE__ */ new Set();
|
|
5434
|
+
for (const file of manifest.files) {
|
|
5435
|
+
let rel;
|
|
5436
|
+
try {
|
|
5437
|
+
rel = assertEvidenceRelativePath(file.path);
|
|
5438
|
+
} catch (error) {
|
|
5439
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5440
|
+
issues.push({
|
|
5441
|
+
code: "path_unsafe",
|
|
5442
|
+
severity: "error",
|
|
5443
|
+
message,
|
|
5444
|
+
path: file.path
|
|
5445
|
+
});
|
|
5446
|
+
continue;
|
|
5447
|
+
}
|
|
5448
|
+
if (rel === EVIDENCE_MANIFEST_FILENAME) {
|
|
5449
|
+
issues.push({
|
|
5450
|
+
code: "manifest_invalid",
|
|
5451
|
+
severity: "error",
|
|
5452
|
+
message: `${EVIDENCE_MANIFEST_FILENAME} must not list itself in files[].`,
|
|
5453
|
+
path: rel
|
|
5454
|
+
});
|
|
5455
|
+
continue;
|
|
5456
|
+
}
|
|
5457
|
+
listed.add(rel);
|
|
5458
|
+
const abs = path5.join(root, ...rel.split("/"));
|
|
5459
|
+
let bytes;
|
|
5460
|
+
try {
|
|
5461
|
+
bytes = await readFile(abs);
|
|
5462
|
+
} catch {
|
|
5463
|
+
issues.push({
|
|
5464
|
+
code: "file_missing",
|
|
5465
|
+
severity: "error",
|
|
5466
|
+
message: `Listed file missing: ${rel}`,
|
|
5467
|
+
path: rel
|
|
5468
|
+
});
|
|
5469
|
+
continue;
|
|
5470
|
+
}
|
|
5471
|
+
const actual = sha256Hex(bytes);
|
|
5472
|
+
if (!sha256Equals(file.sha256, actual)) {
|
|
5473
|
+
issues.push({
|
|
5474
|
+
code: "hash_mismatch",
|
|
5475
|
+
severity: "error",
|
|
5476
|
+
message: `SHA-256 mismatch for ${rel}`,
|
|
5477
|
+
path: rel
|
|
5478
|
+
});
|
|
5479
|
+
}
|
|
5480
|
+
}
|
|
5481
|
+
let onDisk = [];
|
|
5482
|
+
try {
|
|
5483
|
+
onDisk = await listFilesRecursive(root);
|
|
5484
|
+
} catch (error) {
|
|
5485
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5486
|
+
issues.push({ code: "io_error", severity: "error", message });
|
|
5487
|
+
}
|
|
5488
|
+
for (const rel of onDisk) {
|
|
5489
|
+
if (rel === EVIDENCE_MANIFEST_FILENAME) continue;
|
|
5490
|
+
if (listed.has(rel)) continue;
|
|
5491
|
+
if (unexpectedMode === "ignore") continue;
|
|
5492
|
+
issues.push({
|
|
5493
|
+
code: "file_unexpected",
|
|
5494
|
+
severity: unexpectedMode === "warn" ? "warning" : "error",
|
|
5495
|
+
message: `Unexpected file not listed in manifest: ${rel}`,
|
|
5496
|
+
path: rel
|
|
5497
|
+
});
|
|
5498
|
+
}
|
|
5499
|
+
const hasError = issues.some((issue) => issue.severity === "error");
|
|
5500
|
+
return {
|
|
5501
|
+
ok: !hasError,
|
|
5502
|
+
status: hasError ? "fail" : "pass",
|
|
5503
|
+
root,
|
|
5504
|
+
manifest,
|
|
5505
|
+
issues,
|
|
5506
|
+
checkedFiles: listed.size
|
|
5507
|
+
};
|
|
5508
|
+
}
|
|
5509
|
+
|
|
5510
|
+
// packages/core/src/evidence/ci.ts
|
|
5511
|
+
function asMap(value) {
|
|
5512
|
+
if (value instanceof Map) return new Map(value);
|
|
5513
|
+
return new Map(Object.entries(value));
|
|
5514
|
+
}
|
|
5515
|
+
function buildEvidenceCiPackage(input) {
|
|
5516
|
+
const sources = asMap(input.sourceContents);
|
|
5517
|
+
const sourceHashes = input.runIds.map((runId) => ({
|
|
5518
|
+
runId,
|
|
5519
|
+
algorithm: "sha256",
|
|
5520
|
+
hash: sha256Hex(sources.get(runId) ?? "")
|
|
5521
|
+
}));
|
|
5522
|
+
const schemaVersions = /* @__PURE__ */ new Set();
|
|
5523
|
+
for (const content of sources.values()) {
|
|
5524
|
+
for (const version of collectTraceSchemaVersions(content)) {
|
|
5525
|
+
schemaVersions.add(version);
|
|
5526
|
+
}
|
|
5527
|
+
}
|
|
5528
|
+
for (const version of collectTraceSchemaVersions(input.redactedTraceJsonl)) {
|
|
5529
|
+
schemaVersions.add(version);
|
|
5530
|
+
}
|
|
5531
|
+
const createdAt = input.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
5532
|
+
const evidenceHtml = buildEvidenceHtmlShell({
|
|
5533
|
+
title: "AgentInspect evidence",
|
|
5534
|
+
runIds: input.runIds,
|
|
5535
|
+
assessmentStatus: input.assessmentStatus,
|
|
5536
|
+
sourceStatus: input.sourceStatus,
|
|
5537
|
+
redactionProfile: input.redactionProfile,
|
|
5538
|
+
verificationPolicy: input.redactionProfile,
|
|
5539
|
+
generatorName: "agent-inspect",
|
|
5540
|
+
generatorVersion: input.generatorVersion,
|
|
5541
|
+
createdAt,
|
|
5542
|
+
summaryText: input.summaryText,
|
|
5543
|
+
checkSummary: {
|
|
5544
|
+
aggregateStatus: input.assessmentStatus,
|
|
5545
|
+
runs: input.runIds.map((runId) => ({
|
|
5546
|
+
runId,
|
|
5547
|
+
status: input.assessmentStatus,
|
|
5548
|
+
sourceStatus: input.sourceStatus,
|
|
5549
|
+
errors: input.assessmentStatus === "UNSAFE" || input.assessmentStatus === "UNKNOWN" ? 1 : 0,
|
|
5550
|
+
warnings: input.assessmentStatus === "SAFE WITH WARNINGS" ? 1 : 0,
|
|
5551
|
+
findings: 0
|
|
5552
|
+
}))
|
|
5553
|
+
}
|
|
5554
|
+
});
|
|
5555
|
+
const packaged = [
|
|
5556
|
+
{ path: EVIDENCE_HTML_FILENAME, content: evidenceHtml },
|
|
5557
|
+
{ path: "check-results.json", content: input.checkResultsJson },
|
|
5558
|
+
{ path: "trace.jsonl", content: input.redactedTraceJsonl }
|
|
5559
|
+
];
|
|
5560
|
+
const manifest = buildEvidenceManifest({
|
|
5561
|
+
generatorVersion: input.generatorVersion,
|
|
5562
|
+
runIds: input.runIds,
|
|
5563
|
+
traceSchemaVersions: [...schemaVersions].sort((a, b) => a.localeCompare(b)),
|
|
5564
|
+
sourceHashes,
|
|
5565
|
+
redactionProfile: input.redactionProfile,
|
|
5566
|
+
verificationPolicy: input.redactionProfile,
|
|
5567
|
+
assessmentStatus: input.assessmentStatus,
|
|
5568
|
+
sourceStatus: input.sourceStatus,
|
|
5569
|
+
files: packaged,
|
|
5570
|
+
createdAt,
|
|
5571
|
+
note: EVIDENCE_ASSESSMENT_NOTE
|
|
5572
|
+
});
|
|
5573
|
+
return {
|
|
5574
|
+
"evidence.html": evidenceHtml,
|
|
5575
|
+
"evidence.json": serializeEvidenceManifest(manifest),
|
|
5576
|
+
"check-results.json": input.checkResultsJson,
|
|
5577
|
+
"trace.jsonl": input.redactedTraceJsonl,
|
|
5578
|
+
manifest
|
|
5579
|
+
};
|
|
5580
|
+
}
|
|
5581
|
+
|
|
3866
5582
|
// packages/core/src/suite/types.ts
|
|
3867
5583
|
var DEFAULT_SUITE_CONFIG_NAMES = [
|
|
3868
5584
|
"agent-inspect.suite.json",
|
|
@@ -4180,8 +5896,35 @@ var DEFAULT_RAW_CONTENT_KEYS = [
|
|
|
4180
5896
|
"toolinput",
|
|
4181
5897
|
"tool_input",
|
|
4182
5898
|
"tooloutput",
|
|
4183
|
-
"tool_output"
|
|
5899
|
+
"tool_output",
|
|
5900
|
+
// Framework / agent metadata that carries user or task text
|
|
5901
|
+
"currenttask",
|
|
5902
|
+
"current_task",
|
|
5903
|
+
"task",
|
|
5904
|
+
"userinput",
|
|
5905
|
+
"user_input",
|
|
5906
|
+
"requesttext",
|
|
5907
|
+
"request_text",
|
|
5908
|
+
"conversationtext",
|
|
5909
|
+
"conversation_text"
|
|
4184
5910
|
];
|
|
5911
|
+
var DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES = ["tokenUsage", "usage"];
|
|
5912
|
+
var SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
|
|
5913
|
+
"input",
|
|
5914
|
+
"output",
|
|
5915
|
+
"total",
|
|
5916
|
+
"cached",
|
|
5917
|
+
"input_tokens",
|
|
5918
|
+
"inputtokens",
|
|
5919
|
+
"output_tokens",
|
|
5920
|
+
"outputtokens",
|
|
5921
|
+
"total_tokens",
|
|
5922
|
+
"totaltokens",
|
|
5923
|
+
"prompt_tokens",
|
|
5924
|
+
"prompttokens",
|
|
5925
|
+
"completion_tokens",
|
|
5926
|
+
"completiontokens"
|
|
5927
|
+
]);
|
|
4185
5928
|
var DEFAULT_SECRET_PATTERNS = [
|
|
4186
5929
|
{ id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
|
|
4187
5930
|
{ id: "openai-key", pattern: /sk-[A-Za-z0-9_-]{16,}/ },
|
|
@@ -4359,7 +6102,11 @@ function normalizeFinding(rule, finding) {
|
|
|
4359
6102
|
message: finding.message,
|
|
4360
6103
|
...finding.expected !== void 0 ? { expected: finding.expected } : {},
|
|
4361
6104
|
...finding.actual !== void 0 ? { actual: finding.actual } : {},
|
|
4362
|
-
evidence: [...finding.evidence ?? []]
|
|
6105
|
+
evidence: [...finding.evidence ?? []],
|
|
6106
|
+
...finding.category !== void 0 ? { category: finding.category } : {},
|
|
6107
|
+
...finding.confidence !== void 0 ? { confidence: finding.confidence } : {},
|
|
6108
|
+
...finding.detector !== void 0 ? { detector: finding.detector } : {},
|
|
6109
|
+
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
4363
6110
|
};
|
|
4364
6111
|
}
|
|
4365
6112
|
function summarize(findings, diagnostics) {
|
|
@@ -4401,7 +6148,7 @@ function stripPrefix(name, prefixes) {
|
|
|
4401
6148
|
}
|
|
4402
6149
|
return name;
|
|
4403
6150
|
}
|
|
4404
|
-
function eventEvidence(event,
|
|
6151
|
+
function eventEvidence(event, path14) {
|
|
4405
6152
|
return {
|
|
4406
6153
|
runId: event.runId,
|
|
4407
6154
|
eventId: event.eventId,
|
|
@@ -4411,21 +6158,25 @@ function eventEvidence(event, path12) {
|
|
|
4411
6158
|
kind: event.kind,
|
|
4412
6159
|
name: event.name,
|
|
4413
6160
|
status: event.status,
|
|
4414
|
-
...
|
|
6161
|
+
...path14 ? { path: path14 } : {}
|
|
4415
6162
|
};
|
|
4416
6163
|
}
|
|
4417
6164
|
function runEvidence(run) {
|
|
4418
6165
|
return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
|
|
4419
6166
|
}
|
|
4420
|
-
function failFinding(ruleId, message, evidence, expected, actual) {
|
|
6167
|
+
function failFinding(ruleId, message, evidence, expected, actual, meta) {
|
|
4421
6168
|
return {
|
|
4422
6169
|
ruleId,
|
|
4423
|
-
severity: "error",
|
|
4424
|
-
status: "fail",
|
|
6170
|
+
severity: meta?.severity ?? "error",
|
|
6171
|
+
status: meta?.status ?? "fail",
|
|
4425
6172
|
message,
|
|
4426
6173
|
...expected !== void 0 ? { expected } : {},
|
|
4427
6174
|
...actual !== void 0 ? { actual } : {},
|
|
4428
|
-
evidence: [...evidence]
|
|
6175
|
+
evidence: [...evidence],
|
|
6176
|
+
...meta?.category !== void 0 ? { category: meta.category } : {},
|
|
6177
|
+
...meta?.confidence !== void 0 ? { confidence: meta.confidence } : {},
|
|
6178
|
+
...meta?.detector !== void 0 ? { detector: meta.detector } : {},
|
|
6179
|
+
...meta?.action !== void 0 ? { action: meta.action } : {}
|
|
4429
6180
|
};
|
|
4430
6181
|
}
|
|
4431
6182
|
function toolName(event) {
|
|
@@ -4474,9 +6225,9 @@ function eventEndMs(event) {
|
|
|
4474
6225
|
function normalizedKey(value) {
|
|
4475
6226
|
return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
|
|
4476
6227
|
}
|
|
4477
|
-
function lastPathSegment(
|
|
4478
|
-
const parts =
|
|
4479
|
-
return parts[parts.length - 1] ??
|
|
6228
|
+
function lastPathSegment(path14) {
|
|
6229
|
+
const parts = path14.split(".");
|
|
6230
|
+
return parts[parts.length - 1] ?? path14;
|
|
4480
6231
|
}
|
|
4481
6232
|
function valueType(value) {
|
|
4482
6233
|
if (Array.isArray(value)) return "array";
|
|
@@ -4490,12 +6241,12 @@ function serializedByteLength(value) {
|
|
|
4490
6241
|
return void 0;
|
|
4491
6242
|
}
|
|
4492
6243
|
}
|
|
4493
|
-
function pushValueEntries(entries, event, value,
|
|
4494
|
-
entries.push({ event, path:
|
|
6244
|
+
function pushValueEntries(entries, event, value, path14, key, depth = 0) {
|
|
6245
|
+
entries.push({ event, path: path14, key, value });
|
|
4495
6246
|
if (depth >= 8) return;
|
|
4496
6247
|
if (Array.isArray(value)) {
|
|
4497
6248
|
for (const [index, item] of value.entries()) {
|
|
4498
|
-
pushValueEntries(entries, event, item, `${
|
|
6249
|
+
pushValueEntries(entries, event, item, `${path14}.${index}`, String(index), depth + 1);
|
|
4499
6250
|
}
|
|
4500
6251
|
return;
|
|
4501
6252
|
}
|
|
@@ -4505,7 +6256,7 @@ function pushValueEntries(entries, event, value, path12, key, depth = 0) {
|
|
|
4505
6256
|
entries,
|
|
4506
6257
|
event,
|
|
4507
6258
|
value[nestedKey],
|
|
4508
|
-
`${
|
|
6259
|
+
`${path14}.${nestedKey}`,
|
|
4509
6260
|
nestedKey,
|
|
4510
6261
|
depth + 1
|
|
4511
6262
|
);
|
|
@@ -4546,6 +6297,19 @@ function isRawContentKey(key, forbiddenKeys) {
|
|
|
4546
6297
|
const normalized = normalizedKey(key);
|
|
4547
6298
|
return forbiddenKeys.some((forbidden) => normalized === normalizedKey(forbidden));
|
|
4548
6299
|
}
|
|
6300
|
+
function isSafeRawContentMetricPath(path14, key, safePathPrefixes) {
|
|
6301
|
+
const leaf = normalizedKey(key ?? lastPathSegment(path14));
|
|
6302
|
+
if (!SAFE_USAGE_LEAF_KEYS.has(leaf)) return false;
|
|
6303
|
+
const parts = path14.split(".").filter(Boolean);
|
|
6304
|
+
if (parts.length < 2) return false;
|
|
6305
|
+
const parent = parts[parts.length - 2] ?? "";
|
|
6306
|
+
const parentNorm = normalizedKey(parent);
|
|
6307
|
+
return safePathPrefixes.some((prefix) => parentNorm === normalizedKey(prefix));
|
|
6308
|
+
}
|
|
6309
|
+
function isRawContentPath(path14, key, forbiddenKeys, safePathPrefixes) {
|
|
6310
|
+
if (isSafeRawContentMetricPath(path14, key, safePathPrefixes)) return false;
|
|
6311
|
+
return isRawContentKey(key ?? lastPathSegment(path14), forbiddenKeys);
|
|
6312
|
+
}
|
|
4549
6313
|
function parentMarkedUnresolved(event) {
|
|
4550
6314
|
if (booleanAttr(event, [
|
|
4551
6315
|
"parentUnresolved",
|
|
@@ -4586,9 +6350,9 @@ function eventDurationMs(event) {
|
|
|
4586
6350
|
}
|
|
4587
6351
|
function treeShape(nodes) {
|
|
4588
6352
|
const lines = [];
|
|
4589
|
-
const visit = (node,
|
|
4590
|
-
lines.push(`${
|
|
4591
|
-
node.children.forEach((child, index) => visit(child, `${
|
|
6353
|
+
const visit = (node, path14) => {
|
|
6354
|
+
lines.push(`${path14}:${node.event.kind}:${node.event.name}:${node.event.status ?? "unknown"}`);
|
|
6355
|
+
node.children.forEach((child, index) => visit(child, `${path14}.${index}`));
|
|
4592
6356
|
};
|
|
4593
6357
|
nodes.forEach((node, index) => visit(node, String(index)));
|
|
4594
6358
|
return lines;
|
|
@@ -4637,9 +6401,9 @@ function retrievalShape(context) {
|
|
|
4637
6401
|
function guardrailShape(context) {
|
|
4638
6402
|
return guardrailEvents(context).map((event) => signalName(event, ["guardrailName", "guardrail", "guardrailId"], ["guardrail:"])).sort((a, b) => a.localeCompare(b));
|
|
4639
6403
|
}
|
|
4640
|
-
function firstEvidenceForKind(context, kind,
|
|
6404
|
+
function firstEvidenceForKind(context, kind, path14) {
|
|
4641
6405
|
const event = context.events.find((candidate) => candidate.kind === kind);
|
|
4642
|
-
return event ? [eventEvidence(event,
|
|
6406
|
+
return event ? [eventEvidence(event, path14)] : runEvidence(context.selectedRun);
|
|
4643
6407
|
}
|
|
4644
6408
|
function baselineDiffFinding(message, evidence, expected, actual) {
|
|
4645
6409
|
return failFinding("baseline.regression", message, evidence, expected, actual);
|
|
@@ -4989,13 +6753,13 @@ function createStructureCycleRule() {
|
|
|
4989
6753
|
const seenCycles = /* @__PURE__ */ new Set();
|
|
4990
6754
|
const findings = [];
|
|
4991
6755
|
for (const event of [...context.events].sort((a, b) => a.eventId.localeCompare(b.eventId))) {
|
|
4992
|
-
const
|
|
6756
|
+
const path14 = [];
|
|
4993
6757
|
const seenAt = /* @__PURE__ */ new Map();
|
|
4994
6758
|
let current = event;
|
|
4995
6759
|
while (current) {
|
|
4996
6760
|
const existing = seenAt.get(current.eventId);
|
|
4997
6761
|
if (existing !== void 0) {
|
|
4998
|
-
const cycle =
|
|
6762
|
+
const cycle = path14.slice(existing);
|
|
4999
6763
|
const key = cycle.map((item) => item.eventId).sort().join("\0");
|
|
5000
6764
|
if (!seenCycles.has(key)) {
|
|
5001
6765
|
seenCycles.add(key);
|
|
@@ -5011,8 +6775,8 @@ function createStructureCycleRule() {
|
|
|
5011
6775
|
}
|
|
5012
6776
|
break;
|
|
5013
6777
|
}
|
|
5014
|
-
seenAt.set(current.eventId,
|
|
5015
|
-
|
|
6778
|
+
seenAt.set(current.eventId, path14.length);
|
|
6779
|
+
path14.push(current);
|
|
5016
6780
|
current = current.parentId ? byId.get(current.parentId) : void 0;
|
|
5017
6781
|
}
|
|
5018
6782
|
}
|
|
@@ -5185,7 +6949,13 @@ function createSafetyRedactionRule(options = {}) {
|
|
|
5185
6949
|
`Sensitive-looking field at ${entry.path} is not redacted.`,
|
|
5186
6950
|
[eventEvidence(event, entry.path)],
|
|
5187
6951
|
"redaction marker",
|
|
5188
|
-
{ path: entry.path, valueType: valueType(entry.value) }
|
|
6952
|
+
{ path: entry.path, valueType: valueType(entry.value) },
|
|
6953
|
+
{
|
|
6954
|
+
category: "credential",
|
|
6955
|
+
confidence: "high",
|
|
6956
|
+
detector: "safety.redaction",
|
|
6957
|
+
action: "redact"
|
|
6958
|
+
}
|
|
5189
6959
|
)
|
|
5190
6960
|
);
|
|
5191
6961
|
}
|
|
@@ -5196,6 +6966,7 @@ function createSafetyRedactionRule(options = {}) {
|
|
|
5196
6966
|
}
|
|
5197
6967
|
function createSafetyRawContentRule(options = {}) {
|
|
5198
6968
|
const forbiddenKeys = options.forbiddenKeys ?? DEFAULT_RAW_CONTENT_KEYS;
|
|
6969
|
+
const safePathPrefixes = options.safePathPrefixes ?? DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES;
|
|
5199
6970
|
return {
|
|
5200
6971
|
id: "safety.rawPrompt",
|
|
5201
6972
|
category: "safety",
|
|
@@ -5205,14 +6976,20 @@ function createSafetyRawContentRule(options = {}) {
|
|
|
5205
6976
|
for (const event of context.events) {
|
|
5206
6977
|
for (const entry of eventValueEntries(event, { includeSummaries: options.includeSummaries })) {
|
|
5207
6978
|
const key = entry.key ?? lastPathSegment(entry.path);
|
|
5208
|
-
if (!
|
|
6979
|
+
if (!isRawContentPath(entry.path, key, forbiddenKeys, safePathPrefixes)) continue;
|
|
5209
6980
|
findings.push(
|
|
5210
6981
|
failFinding(
|
|
5211
6982
|
"safety.rawPrompt",
|
|
5212
6983
|
`Raw content-like field ${entry.path} is present.`,
|
|
5213
6984
|
[eventEvidence(event, entry.path)],
|
|
5214
6985
|
"metadata-only trace fields",
|
|
5215
|
-
{ path: entry.path, valueType: valueType(entry.value) }
|
|
6986
|
+
{ path: entry.path, valueType: valueType(entry.value) },
|
|
6987
|
+
{
|
|
6988
|
+
category: "raw-content",
|
|
6989
|
+
confidence: "high",
|
|
6990
|
+
detector: "safety.rawPrompt",
|
|
6991
|
+
action: "redact-or-omit"
|
|
6992
|
+
}
|
|
5216
6993
|
)
|
|
5217
6994
|
);
|
|
5218
6995
|
}
|
|
@@ -5244,7 +7021,13 @@ function createSafetySecretPatternRule(options = {}) {
|
|
|
5244
7021
|
`Secret-like pattern ${pattern.id} matched at ${entry.path}.`,
|
|
5245
7022
|
[eventEvidence(event, entry.path)],
|
|
5246
7023
|
"no secret-like strings",
|
|
5247
|
-
{ pattern: pattern.id, path: entry.path }
|
|
7024
|
+
{ pattern: pattern.id, path: entry.path },
|
|
7025
|
+
{
|
|
7026
|
+
category: "credential",
|
|
7027
|
+
confidence: "high",
|
|
7028
|
+
detector: pattern.id,
|
|
7029
|
+
action: "redact"
|
|
7030
|
+
}
|
|
5248
7031
|
)
|
|
5249
7032
|
);
|
|
5250
7033
|
break;
|
|
@@ -5271,7 +7054,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
5271
7054
|
`String at ${entry.path} exceeds ${options.maxStringLength} characters.`,
|
|
5272
7055
|
[eventEvidence(event, entry.path)],
|
|
5273
7056
|
{ maxStringLength: options.maxStringLength },
|
|
5274
|
-
{ path: entry.path, length: entry.value.length }
|
|
7057
|
+
{ path: entry.path, length: entry.value.length },
|
|
7058
|
+
{
|
|
7059
|
+
category: "size",
|
|
7060
|
+
confidence: "high",
|
|
7061
|
+
detector: "safety.oversizedAttribute",
|
|
7062
|
+
action: "truncate-or-omit"
|
|
7063
|
+
}
|
|
5275
7064
|
)
|
|
5276
7065
|
);
|
|
5277
7066
|
}
|
|
@@ -5282,7 +7071,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
5282
7071
|
`Array at ${entry.path} exceeds ${options.maxArrayLength} items.`,
|
|
5283
7072
|
[eventEvidence(event, entry.path)],
|
|
5284
7073
|
{ maxArrayLength: options.maxArrayLength },
|
|
5285
|
-
{ path: entry.path, length: entry.value.length }
|
|
7074
|
+
{ path: entry.path, length: entry.value.length },
|
|
7075
|
+
{
|
|
7076
|
+
category: "size",
|
|
7077
|
+
confidence: "high",
|
|
7078
|
+
detector: "safety.oversizedAttribute",
|
|
7079
|
+
action: "truncate-or-omit"
|
|
7080
|
+
}
|
|
5286
7081
|
)
|
|
5287
7082
|
);
|
|
5288
7083
|
}
|
|
@@ -5293,7 +7088,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
5293
7088
|
`Object at ${entry.path} exceeds ${options.maxObjectKeys} keys.`,
|
|
5294
7089
|
[eventEvidence(event, entry.path)],
|
|
5295
7090
|
{ maxObjectKeys: options.maxObjectKeys },
|
|
5296
|
-
{ path: entry.path, keys: Object.keys(entry.value).length }
|
|
7091
|
+
{ path: entry.path, keys: Object.keys(entry.value).length },
|
|
7092
|
+
{
|
|
7093
|
+
category: "size",
|
|
7094
|
+
confidence: "high",
|
|
7095
|
+
detector: "safety.oversizedAttribute",
|
|
7096
|
+
action: "truncate-or-omit"
|
|
7097
|
+
}
|
|
5297
7098
|
)
|
|
5298
7099
|
);
|
|
5299
7100
|
}
|
|
@@ -5306,7 +7107,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
5306
7107
|
`Value at ${entry.path} exceeds ${options.maxSerializedBytes} serialized bytes.`,
|
|
5307
7108
|
[eventEvidence(event, entry.path)],
|
|
5308
7109
|
{ maxSerializedBytes: options.maxSerializedBytes },
|
|
5309
|
-
{ path: entry.path, bytes }
|
|
7110
|
+
{ path: entry.path, bytes },
|
|
7111
|
+
{
|
|
7112
|
+
category: "size",
|
|
7113
|
+
confidence: "high",
|
|
7114
|
+
detector: "safety.oversizedAttribute",
|
|
7115
|
+
action: "truncate-or-omit"
|
|
7116
|
+
}
|
|
5310
7117
|
)
|
|
5311
7118
|
);
|
|
5312
7119
|
}
|
|
@@ -5626,7 +7433,7 @@ function mapErrorInfo(error) {
|
|
|
5626
7433
|
function mapTokenUsageFromMetadata(metadata) {
|
|
5627
7434
|
return normalizeTokenUsage(metadata?.tokens);
|
|
5628
7435
|
}
|
|
5629
|
-
function
|
|
7436
|
+
function compactAttributes2(entries) {
|
|
5630
7437
|
const out = {};
|
|
5631
7438
|
for (const [key, value] of Object.entries(entries)) {
|
|
5632
7439
|
if (value !== void 0) {
|
|
@@ -5644,7 +7451,7 @@ function traceEventToPersistedInspectEvent(event, options) {
|
|
|
5644
7451
|
case "run_started": {
|
|
5645
7452
|
const tsStart = toIsoTimestamp(event.startTime);
|
|
5646
7453
|
const correlation = extractCorrelationMetadata(event.metadata);
|
|
5647
|
-
const attributes =
|
|
7454
|
+
const attributes = compactAttributes2({
|
|
5648
7455
|
legacyEvent: "run_started",
|
|
5649
7456
|
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
5650
7457
|
correlationId: correlation?.correlationId,
|
|
@@ -5670,7 +7477,7 @@ function traceEventToPersistedInspectEvent(event, options) {
|
|
|
5670
7477
|
case "run_completed": {
|
|
5671
7478
|
const tsEnd = toIsoTimestamp(event.endTime);
|
|
5672
7479
|
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
5673
|
-
const attributes =
|
|
7480
|
+
const attributes = compactAttributes2({
|
|
5674
7481
|
legacyEvent: "run_completed",
|
|
5675
7482
|
errorStack,
|
|
5676
7483
|
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
@@ -5694,7 +7501,7 @@ function traceEventToPersistedInspectEvent(event, options) {
|
|
|
5694
7501
|
case "step_started": {
|
|
5695
7502
|
const tsStart = toIsoTimestamp(event.startTime);
|
|
5696
7503
|
const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
|
|
5697
|
-
const attributes =
|
|
7504
|
+
const attributes = compactAttributes2({
|
|
5698
7505
|
legacyEvent: "step_started",
|
|
5699
7506
|
stepId: event.stepId,
|
|
5700
7507
|
stepType: event.type,
|
|
@@ -5725,7 +7532,7 @@ function traceEventToPersistedInspectEvent(event, options) {
|
|
|
5725
7532
|
case "step_completed": {
|
|
5726
7533
|
const tsEnd = toIsoTimestamp(event.endTime);
|
|
5727
7534
|
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
5728
|
-
const attributes =
|
|
7535
|
+
const attributes = compactAttributes2({
|
|
5729
7536
|
legacyEvent: "step_completed",
|
|
5730
7537
|
stepId: event.stepId,
|
|
5731
7538
|
errorStack,
|
|
@@ -5749,7 +7556,7 @@ function traceEventToPersistedInspectEvent(event, options) {
|
|
|
5749
7556
|
}
|
|
5750
7557
|
case "outcome_observed": {
|
|
5751
7558
|
const tsObserved = toIsoTimestamp(event.observedAt);
|
|
5752
|
-
const attributes =
|
|
7559
|
+
const attributes = compactAttributes2({
|
|
5753
7560
|
legacyEvent: "outcome_observed",
|
|
5754
7561
|
outcomeId: event.outcomeId,
|
|
5755
7562
|
outcomeStatus: event.status,
|
|
@@ -5884,7 +7691,7 @@ var TreeBuilder = class {
|
|
|
5884
7691
|
};
|
|
5885
7692
|
|
|
5886
7693
|
// packages/core/src/persisted/to-inspect-event.ts
|
|
5887
|
-
function
|
|
7694
|
+
function compactAttributes3(entries) {
|
|
5888
7695
|
const out = {};
|
|
5889
7696
|
for (const [key, value] of Object.entries(entries)) {
|
|
5890
7697
|
if (value !== void 0) {
|
|
@@ -5993,7 +7800,7 @@ function persistedInspectEventToInspectEvent(event) {
|
|
|
5993
7800
|
timestamp: ts.ms,
|
|
5994
7801
|
confidence: event.confidence,
|
|
5995
7802
|
source: mapPersistedSourceToInspect(event),
|
|
5996
|
-
attributes:
|
|
7803
|
+
attributes: compactAttributes3(attrs)
|
|
5997
7804
|
};
|
|
5998
7805
|
if (event.parentId !== void 0) {
|
|
5999
7806
|
out.parentId = event.parentId;
|
|
@@ -8466,114 +10273,6 @@ async function analyzeCohort(runsInput, options) {
|
|
|
8466
10273
|
};
|
|
8467
10274
|
}
|
|
8468
10275
|
|
|
8469
|
-
// packages/core/src/exporters/helpers.ts
|
|
8470
|
-
var REDACT_SUBSTRINGS = [
|
|
8471
|
-
"authorization",
|
|
8472
|
-
"cookie",
|
|
8473
|
-
"token",
|
|
8474
|
-
"apikey",
|
|
8475
|
-
"password",
|
|
8476
|
-
"secret",
|
|
8477
|
-
"email"
|
|
8478
|
-
];
|
|
8479
|
-
function shouldRedactKey(key) {
|
|
8480
|
-
const k = key.toLowerCase();
|
|
8481
|
-
for (const s of REDACT_SUBSTRINGS) {
|
|
8482
|
-
if (k.includes(s)) return true;
|
|
8483
|
-
}
|
|
8484
|
-
return false;
|
|
8485
|
-
}
|
|
8486
|
-
function safeString(value, maxLength) {
|
|
8487
|
-
if (value === null || value === void 0) return "";
|
|
8488
|
-
let s;
|
|
8489
|
-
if (typeof value === "string") s = value;
|
|
8490
|
-
else if (typeof value === "number" || typeof value === "boolean") s = String(value);
|
|
8491
|
-
else s = stableJson(value, false);
|
|
8492
|
-
if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
|
|
8493
|
-
return `${s.slice(0, maxLength)}\u2026`;
|
|
8494
|
-
}
|
|
8495
|
-
return s;
|
|
8496
|
-
}
|
|
8497
|
-
function escapeMarkdown(value) {
|
|
8498
|
-
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
8499
|
-
}
|
|
8500
|
-
function escapeHtml(value) {
|
|
8501
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
8502
|
-
}
|
|
8503
|
-
function sortKeysDeep(input) {
|
|
8504
|
-
if (input === null || typeof input !== "object") return input;
|
|
8505
|
-
if (Array.isArray(input)) return input.map(sortKeysDeep);
|
|
8506
|
-
const o = input;
|
|
8507
|
-
const out = {};
|
|
8508
|
-
for (const k of Object.keys(o).sort()) {
|
|
8509
|
-
out[k] = sortKeysDeep(o[k]);
|
|
8510
|
-
}
|
|
8511
|
-
return out;
|
|
8512
|
-
}
|
|
8513
|
-
function stableJson(value, pretty) {
|
|
8514
|
-
const sorted = sortKeysDeep(value);
|
|
8515
|
-
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
8516
|
-
}
|
|
8517
|
-
function compactAttributes3(attrs, options) {
|
|
8518
|
-
if (attrs === void 0) return {};
|
|
8519
|
-
const maxLen = options?.maxLength ?? 500;
|
|
8520
|
-
const redacted = options?.redacted ?? true;
|
|
8521
|
-
const out = {};
|
|
8522
|
-
for (const key of Object.keys(attrs).sort()) {
|
|
8523
|
-
if (redacted && shouldRedactKey(key)) {
|
|
8524
|
-
out[key] = "[REDACTED]";
|
|
8525
|
-
continue;
|
|
8526
|
-
}
|
|
8527
|
-
const v = attrs[key];
|
|
8528
|
-
out[key] = compactValue(v, maxLen, redacted);
|
|
8529
|
-
}
|
|
8530
|
-
return out;
|
|
8531
|
-
}
|
|
8532
|
-
function compactValue(value, maxLen, redacted) {
|
|
8533
|
-
if (value === null || typeof value !== "object") {
|
|
8534
|
-
return typeof value === "string" ? safeString(value, maxLen) : value;
|
|
8535
|
-
}
|
|
8536
|
-
if (Array.isArray(value)) {
|
|
8537
|
-
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
|
|
8538
|
-
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
8539
|
-
return arr;
|
|
8540
|
-
}
|
|
8541
|
-
const o = value;
|
|
8542
|
-
const inner = {};
|
|
8543
|
-
for (const k of Object.keys(o)) {
|
|
8544
|
-
if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
8545
|
-
else inner[k] = compactValue(o[k], maxLen, redacted);
|
|
8546
|
-
}
|
|
8547
|
-
return inner;
|
|
8548
|
-
}
|
|
8549
|
-
function flattenTree(tree) {
|
|
8550
|
-
const out = [];
|
|
8551
|
-
function walk(nodes) {
|
|
8552
|
-
for (const n of nodes) {
|
|
8553
|
-
out.push(n);
|
|
8554
|
-
if (n.children.length > 0) walk(n.children);
|
|
8555
|
-
}
|
|
8556
|
-
}
|
|
8557
|
-
walk(tree.children);
|
|
8558
|
-
return out;
|
|
8559
|
-
}
|
|
8560
|
-
function zeroKinds() {
|
|
8561
|
-
return {
|
|
8562
|
-
RUN: 0,
|
|
8563
|
-
AGENT: 0,
|
|
8564
|
-
LLM: 0,
|
|
8565
|
-
TOOL: 0,
|
|
8566
|
-
CHAIN: 0,
|
|
8567
|
-
RETRIEVER: 0,
|
|
8568
|
-
DECISION: 0,
|
|
8569
|
-
RESULT: 0,
|
|
8570
|
-
ERROR: 0,
|
|
8571
|
-
LOGIC: 0,
|
|
8572
|
-
LOG: 0,
|
|
8573
|
-
OUTCOME: 0
|
|
8574
|
-
};
|
|
8575
|
-
}
|
|
8576
|
-
|
|
8577
10276
|
// packages/core/src/cohort/render.ts
|
|
8578
10277
|
function formatRate(value) {
|
|
8579
10278
|
if (value === void 0) return "n/a";
|
|
@@ -9054,6 +10753,6 @@ function renderGateReport(result, options = {}) {
|
|
|
9054
10753
|
return renderGateSummaryMarkdown(result);
|
|
9055
10754
|
}
|
|
9056
10755
|
|
|
9057
|
-
export { COHORT_METRIC_IDS, DEFAULT_SUITE_ARTIFACTS_DIR, Redactor, TraceDirectory, TraceReadError, TreeBuilder, aggregateBundleSafeStatus, aggregateSessionCheckResults, analyzeCohort, applyProfileMetadataCaps, assertBundlePathContained, buildActivitySummary, buildBundleMetadata, buildBundleSummaryMarkdown, buildLocalExplanation, buildPlaceholderArtifact, buildRunSummary, buildRunTimeline, buildRunWhatSummary, buildSessionIndex, buildTraceStats, bundleFailsOnSafety, bundleRunAssetRelativePath,
|
|
9058
|
-
//# sourceMappingURL=chunk-
|
|
9059
|
-
//# sourceMappingURL=chunk-
|
|
10756
|
+
export { COHORT_METRIC_IDS, DEFAULT_SUITE_ARTIFACTS_DIR, EVIDENCE_FORMAT_VERSION, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, Redactor, TraceDirectory, TraceReadError, TreeBuilder, aggregateBundleSafeStatus, aggregateSessionCheckResults, analyzeCohort, applyProfileMetadataCaps, assertBundlePathContained, assertEvidenceRelativePath, buildActivitySummary, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceCausalFailureViewHtml, buildEvidenceCiPackage, buildEvidenceCircuitViewHtml, buildEvidenceContractsViewHtml, buildEvidenceDiffViewHtml, buildEvidenceHtmlShell, buildEvidenceManifest, buildEvidenceOutcomesViewHtml, buildEvidenceProvenanceViewHtml, buildEvidenceSafetyViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceTreeViewHtml, buildLocalExplanation, buildPlaceholderArtifact, buildRunSummary, buildRunTimeline, buildRunWhatSummary, buildSessionIndex, buildTraceStats, buildZipArchive, bundleFailsOnSafety, bundleRunAssetRelativePath, collectTraceSchemaVersions, compactAttributes, createBaselineRegressionRule, createLlmUsageRule, createMaxStepDurationRule, createObservedOutcomeRule, createRequireCompletedRule, createRunDepthRule, createRunDurationRule, createRunStatusRule, createSafetyOversizedAttributeRule, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createStallDetectionRule, createStructureCycleRule, createStructureOrphanRule, createStructureParallelWidthRule, createStructureRelationshipRule, createToolUsageRule, defaultBundleOutputPath, defaultSuiteConfigTemplate, diffRuns, diffTraceEvents, enrichSessionRunRecord, escapeHtml, escapeMarkdown, extractMetadata, extractOutcomesFromTraceEvents, filterMetasBySessionScope, filterTraces, flattenTree, formatDuration2 as formatDuration, formatTimestamp, gateHasThresholds, getIndent, getTraceFilePath, inferEvidenceFileRole, isAgentInspectTrace, isPersistedInspectEvent, loadSessionRunRecords, loadSuiteConfig, loadTraceMetadataList, manualTraceEventsToComparableRun, nanoid, normalizeBundleOutputPath, openTrace, parseCohortMetricList, parseDuration, parseDurationFilter, parseGateList, parseTraceJsonl, persistedInspectEventsToTraceEvents, renderActivitySummaryHuman, renderCohortReport, renderErrorLine, renderGateReport, renderObservedOutcomesHtml, renderObservedOutcomesMarkdown, renderRunDiff, renderRunWhat, renderStepLine, renderSuiteReport, renderTimeline, renderTraceStats, resolveBundleRunIds, resolveRedactionProfile, resolveSuiteTemplate, resolveTraceDir, runGate, runSuite, runTraceChecks, safeString, sanitizeBundleRunId, searchTraces, serializeEvidenceManifest, sha256Hex, stableJson, summarizeObservedOutcomes, traceEventToPersistedInspectEvent, truncateName, truncateStringForProfile, validateEvent, validateSuiteConfig, verifyEvidenceDirectory, zeroKinds };
|
|
10757
|
+
//# sourceMappingURL=chunk-36IJ76LH.mjs.map
|
|
10758
|
+
//# sourceMappingURL=chunk-36IJ76LH.mjs.map
|