agent-inspect 6.31.9 → 6.31.11
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/NESTJS.md +56 -18
- package/package.json +1 -1
- package/packages/cli/dist/{chunk-SWUZRRDT.mjs → chunk-MQYHJL3W.mjs} +2216 -1938
- package/packages/cli/dist/chunk-MQYHJL3W.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +975 -671
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +32 -9
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/cli/dist/{src-XFIPYODW.mjs → src-76R6E2LQ.mjs} +3 -3
- package/packages/cli/dist/{src-XFIPYODW.mjs.map → src-76R6E2LQ.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +2079 -1879
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +56 -4
- package/packages/core/dist/advanced.d.ts +56 -4
- package/packages/core/dist/advanced.mjs +188 -23
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.cjs +126 -9
- package/packages/core/dist/checks.cjs.map +1 -1
- package/packages/core/dist/checks.d.cts +3 -3
- package/packages/core/dist/checks.d.ts +3 -3
- package/packages/core/dist/checks.mjs +3 -2
- package/packages/core/dist/{chunk-L5WLVBW4.mjs → chunk-AHUZNHFI.mjs} +4 -55
- package/packages/core/dist/chunk-AHUZNHFI.mjs.map +1 -0
- package/packages/core/dist/{chunk-2CCRUI42.mjs → chunk-CCYKNAOD.mjs} +119 -12
- package/packages/core/dist/chunk-CCYKNAOD.mjs.map +1 -0
- package/packages/core/dist/{chunk-7YYS742B.mjs → chunk-M3RJD3BP.mjs} +5 -5
- package/packages/core/dist/{chunk-7YYS742B.mjs.map → chunk-M3RJD3BP.mjs.map} +1 -1
- package/packages/core/dist/{chunk-2IRRUNIN.mjs → chunk-OAUNS5CS.mjs} +3 -3
- package/packages/core/dist/{chunk-2IRRUNIN.mjs.map → chunk-OAUNS5CS.mjs.map} +1 -1
- package/packages/core/dist/{chunk-IJJSPJXV.mjs → chunk-QOC3PJO3.mjs} +4 -4
- package/packages/core/dist/{chunk-IJJSPJXV.mjs.map → chunk-QOC3PJO3.mjs.map} +1 -1
- package/packages/core/dist/chunk-S6LQBLX5.mjs +67 -0
- package/packages/core/dist/chunk-S6LQBLX5.mjs.map +1 -0
- package/packages/core/dist/{chunk-3SPBELJB.mjs → chunk-SSG3ICI4.mjs} +16 -2
- package/packages/core/dist/chunk-SSG3ICI4.mjs.map +1 -0
- package/packages/core/dist/exporters.cjs +3 -3
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.mjs +1 -1
- package/packages/core/dist/{index-EQihlEei.d.cts → index-DACU4Dy1.d.cts} +12 -0
- package/packages/core/dist/{index-CgOAGuM5.d.ts → index-b4tIGrzq.d.ts} +12 -0
- package/packages/core/dist/index.cjs +77 -0
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.mjs +3 -2
- package/packages/core/dist/index.mjs.map +1 -1
- package/packages/core/dist/persisted.cjs +11 -0
- package/packages/core/dist/persisted.cjs.map +1 -1
- package/packages/core/dist/persisted.mjs +4 -3
- package/packages/core/dist/persisted.mjs.map +1 -1
- package/packages/core/dist/{programmatic-gZTWLObB.d.cts → programmatic-BLlC7Ytq.d.cts} +1 -1
- package/packages/core/dist/{programmatic-gZTWLObB.d.ts → programmatic-BLlC7Ytq.d.ts} +1 -1
- package/packages/core/dist/readers.cjs +11 -0
- package/packages/core/dist/readers.cjs.map +1 -1
- package/packages/core/dist/readers.d.cts +1 -1
- package/packages/core/dist/readers.d.ts +1 -1
- package/packages/core/dist/readers.mjs +4 -3
- package/packages/cli/dist/chunk-SWUZRRDT.mjs.map +0 -1
- package/packages/core/dist/chunk-2CCRUI42.mjs.map +0 -1
- package/packages/core/dist/chunk-3SPBELJB.mjs.map +0 -1
- package/packages/core/dist/chunk-L5WLVBW4.mjs.map +0 -1
|
@@ -2868,7 +2868,7 @@ function stepMixLine(summary) {
|
|
|
2868
2868
|
if (summary.logicSteps > 0) parts.push(`${summary.logicSteps} logic`);
|
|
2869
2869
|
return parts.length > 0 ? parts.join(", ") : "none";
|
|
2870
2870
|
}
|
|
2871
|
-
function
|
|
2871
|
+
function executionLine(summary) {
|
|
2872
2872
|
if (summary.status === "success") {
|
|
2873
2873
|
return summary.errorSteps > 0 ? "Completed with step errors recorded." : "Completed successfully.";
|
|
2874
2874
|
}
|
|
@@ -2886,7 +2886,7 @@ function outcomeLine(summary) {
|
|
|
2886
2886
|
if (summary.status === "running") {
|
|
2887
2887
|
return "Run is still in progress (no run_completed).";
|
|
2888
2888
|
}
|
|
2889
|
-
return "
|
|
2889
|
+
return "Execution status unknown \u2014 inspect events may be incomplete.";
|
|
2890
2890
|
}
|
|
2891
2891
|
function buildRunWhatSummary(events) {
|
|
2892
2892
|
const base = buildRunSummary(events);
|
|
@@ -2977,7 +2977,7 @@ function renderRunWhat(summary, options = {}) {
|
|
|
2977
2977
|
lines.push(`Correlation: ${parts.join(", ")}`);
|
|
2978
2978
|
}
|
|
2979
2979
|
}
|
|
2980
|
-
lines.push(`
|
|
2980
|
+
lines.push(`Execution: ${executionLine(summary)}`);
|
|
2981
2981
|
if (summary.longestStep && summary.totalSteps > 0) {
|
|
2982
2982
|
lines.push(
|
|
2983
2983
|
`Slowest: ${summary.longestStep.name} (${formatDuration2(summary.longestStep.durationMs)}, ${summary.longestStep.type})`
|
|
@@ -3003,163 +3003,570 @@ var init_causal_failure = __esm({
|
|
|
3003
3003
|
}
|
|
3004
3004
|
});
|
|
3005
3005
|
|
|
3006
|
-
// packages/core/src/
|
|
3007
|
-
function
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
return "unknown";
|
|
3006
|
+
// packages/core/src/diagnostics/programmatic.ts
|
|
3007
|
+
function formatProgrammaticDiagnostic(code, detail) {
|
|
3008
|
+
const spec = PROGRAMMATIC_DIAGNOSTIC_SPECS[code];
|
|
3009
|
+
const summary = detail?.trim() ? detail.trim() : spec.summary;
|
|
3010
|
+
return `${code}: ${summary} Remediation: ${spec.remediation}`;
|
|
3012
3011
|
}
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3012
|
+
var PROGRAMMATIC_DIAGNOSTIC_SPECS;
|
|
3013
|
+
var init_programmatic = __esm({
|
|
3014
|
+
"packages/core/src/diagnostics/programmatic.ts"() {
|
|
3015
|
+
PROGRAMMATIC_DIAGNOSTIC_SPECS = Object.freeze({
|
|
3016
|
+
AI_TRACE_INPUT_INVALID: {
|
|
3017
|
+
code: "AI_TRACE_INPUT_INVALID",
|
|
3018
|
+
summary: 'Expected { type: "file", path }, { type: "directory", path }, { type: "string", content }, { type: "buffer", content }, or { type: "stdin" }.',
|
|
3019
|
+
remediation: "For a file path, use openTraceFile(path).",
|
|
3020
|
+
relatedCodes: ["invalid_input"]
|
|
3021
|
+
},
|
|
3022
|
+
AI_TRACE_FORMAT_UNSUPPORTED: {
|
|
3023
|
+
code: "AI_TRACE_FORMAT_UNSUPPORTED",
|
|
3024
|
+
summary: "No trace reader could detect the input format.",
|
|
3025
|
+
remediation: "Pass an AgentInspect JSONL file via openTraceFile, or set options.format to a registered reader.",
|
|
3026
|
+
relatedCodes: ["unsupported_format"]
|
|
3027
|
+
},
|
|
3028
|
+
AI_TRACE_FORMAT_AMBIGUOUS: {
|
|
3029
|
+
code: "AI_TRACE_FORMAT_AMBIGUOUS",
|
|
3030
|
+
summary: "Multiple trace readers matched the input with equal confidence.",
|
|
3031
|
+
remediation: "Set options.format explicitly to disambiguate the reader.",
|
|
3032
|
+
relatedCodes: ["ambiguous_format"]
|
|
3033
|
+
},
|
|
3034
|
+
AI_TRACE_FACTS_INPUT_NOT_NORMALIZED: {
|
|
3035
|
+
code: "AI_TRACE_FACTS_INPUT_NOT_NORMALIZED",
|
|
3036
|
+
summary: "TraceFacts requires TraceReadResult or PersistedInspectEvent[].",
|
|
3037
|
+
remediation: "Use openTraceFile() to normalize a JSONL trace first."
|
|
3038
|
+
},
|
|
3039
|
+
AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED: {
|
|
3040
|
+
code: "AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED",
|
|
3041
|
+
summary: "Multiple runs are available; select a run before executing checks.",
|
|
3042
|
+
remediation: "Pass options.runId or TraceCheckInput.selectedRun.",
|
|
3043
|
+
relatedCodes: ["AI_CHECK_RUN_SELECTION_REQUIRED"]
|
|
3044
|
+
},
|
|
3045
|
+
AI_TRACE_RELATIONSHIP_SELF_PARENT: {
|
|
3046
|
+
code: "AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
3047
|
+
summary: "A parentId equals its own event/step id.",
|
|
3048
|
+
remediation: "Reject at capture (AI_LANGGRAPH_SELF_PARENT_REJECTED) or drop via logical projection; do not invent replacement parents.",
|
|
3049
|
+
relatedCodes: [
|
|
3050
|
+
"AI_LANGGRAPH_SELF_PARENT_REJECTED",
|
|
3051
|
+
"AI_LOGICAL_SELF_PARENT_REMOVED"
|
|
3052
|
+
]
|
|
3053
|
+
},
|
|
3054
|
+
AI_TRACE_RELATIONSHIP_CYCLE: {
|
|
3055
|
+
code: "AI_TRACE_RELATIONSHIP_CYCLE",
|
|
3056
|
+
summary: "Trace contains a parentId cycle.",
|
|
3057
|
+
remediation: "Use visibility-first tree linking for legacy fixtures; prefer acyclic capture for new adapter output.",
|
|
3058
|
+
relatedCodes: ["structure.cycle"]
|
|
3059
|
+
},
|
|
3060
|
+
AI_TRACE_CONTRACT_INPUT_INVALID: {
|
|
3061
|
+
code: "AI_TRACE_CONTRACT_INPUT_INVALID",
|
|
3062
|
+
summary: "evaluateTraceContract expects ({ read }, contract) or use evaluateTraceContractRead(read, contract).",
|
|
3063
|
+
remediation: "Pass TraceCheckInput as the first argument: evaluateTraceContract({ read }, contract).",
|
|
3064
|
+
relatedCodes: ["AI_CHECK_INVALID_ARGUMENTS"]
|
|
3065
|
+
},
|
|
3066
|
+
AI_INSPECT_RUN_UNSUPPORTED_OPTION: {
|
|
3067
|
+
code: "AI_INSPECT_RUN_UNSUPPORTED_OPTION",
|
|
3068
|
+
summary: 'inspectRun does not accept a "writer" option.',
|
|
3069
|
+
remediation: "Use createInspector({ writer }) then inspector.run(...). inspectRun persists via the global JSONL path (schema 0.1)."
|
|
3070
|
+
}
|
|
3071
|
+
});
|
|
3017
3072
|
}
|
|
3018
|
-
|
|
3073
|
+
});
|
|
3074
|
+
|
|
3075
|
+
// packages/core/src/checks/logical-events.ts
|
|
3076
|
+
function isRecord6(value) {
|
|
3077
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3019
3078
|
}
|
|
3020
|
-
function
|
|
3021
|
-
|
|
3079
|
+
function legacyEvent(event) {
|
|
3080
|
+
const value = event.attributes?.legacyEvent;
|
|
3081
|
+
return typeof value === "string" ? value : void 0;
|
|
3022
3082
|
}
|
|
3023
|
-
function
|
|
3083
|
+
function stepIdOf(event) {
|
|
3084
|
+
const value = event.attributes?.stepId;
|
|
3085
|
+
if (typeof value === "string" && value.trim() !== "") return value;
|
|
3086
|
+
return void 0;
|
|
3087
|
+
}
|
|
3088
|
+
function cloneEvent(event) {
|
|
3024
3089
|
return {
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3090
|
+
...event,
|
|
3091
|
+
...event.attributes !== void 0 ? { attributes: { ...event.attributes } } : {},
|
|
3092
|
+
...event.error !== void 0 ? { error: { ...event.error } } : {},
|
|
3093
|
+
...event.tokenUsage !== void 0 ? { tokenUsage: { ...event.tokenUsage } } : {},
|
|
3094
|
+
...event.source !== void 0 ? { source: { ...event.source } } : {}
|
|
3030
3095
|
};
|
|
3031
3096
|
}
|
|
3032
|
-
function
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
function slowestNode(nodes) {
|
|
3042
|
-
return nodes.filter(
|
|
3043
|
-
(entry) => entry.node.event.durationMs !== void 0 && // The RUN boundary spans the whole run, so its duration always dominates
|
|
3044
|
-
// and shadows every real step. Rank actual work nodes only, matching the
|
|
3045
|
-
// stats slowest-step ranking, which never counts the run envelope.
|
|
3046
|
-
entry.node.event.kind !== "RUN"
|
|
3047
|
-
).sort((a, b) => {
|
|
3048
|
-
const delta = (b.node.event.durationMs ?? 0) - (a.node.event.durationMs ?? 0);
|
|
3049
|
-
return delta !== 0 ? delta : a.index - b.index;
|
|
3050
|
-
})[0];
|
|
3051
|
-
}
|
|
3052
|
-
function attributeFacts(nodes, redactor) {
|
|
3053
|
-
const facts = [];
|
|
3054
|
-
for (const entry of nodes) {
|
|
3055
|
-
const attrs = entry.node.event.attributes;
|
|
3056
|
-
if (attrs === void 0) continue;
|
|
3057
|
-
for (const key of Object.keys(attrs).sort()) {
|
|
3058
|
-
facts.push({
|
|
3059
|
-
id: `node.${entry.index}.attributes.${key}`,
|
|
3060
|
-
label: `${entry.node.event.name} attribute ${key}`,
|
|
3061
|
-
value: redactValue(redactor, key, attrs[key]),
|
|
3062
|
-
source: "trace",
|
|
3063
|
-
confidence: "observed"
|
|
3064
|
-
});
|
|
3065
|
-
if (facts.length >= 8) return facts;
|
|
3066
|
-
}
|
|
3067
|
-
}
|
|
3068
|
-
return facts;
|
|
3069
|
-
}
|
|
3070
|
-
function buildFacts(run, redactor) {
|
|
3071
|
-
const nodes = flatten(run.children);
|
|
3072
|
-
const facts = [
|
|
3073
|
-
fact("run.id", "Run id", run.runId, redactor),
|
|
3074
|
-
fact("run.name", "Run name", run.name ?? run.runId, redactor),
|
|
3075
|
-
fact("run.status", "Run status", toDisplayStatus(run.status), redactor),
|
|
3076
|
-
fact("run.totalEvents", "Total events", run.metadata.totalEvents, redactor),
|
|
3077
|
-
fact("run.stepCount", "Top-level step count", run.children.length, redactor),
|
|
3078
|
-
fact("run.nodeCount", "Total node count", nodes.length, redactor),
|
|
3079
|
-
fact("run.errorNodeCount", "Error node count", countErrorNodes(nodes), redactor),
|
|
3080
|
-
fact("run.kinds", "Observed kind mix", topKinds(run), redactor)
|
|
3081
|
-
];
|
|
3082
|
-
if (run.durationMs !== void 0) {
|
|
3083
|
-
facts.push(fact("run.durationMs", "Run duration milliseconds", run.durationMs, redactor));
|
|
3084
|
-
}
|
|
3085
|
-
const slowest = slowestNode(nodes);
|
|
3086
|
-
if (slowest !== void 0) {
|
|
3087
|
-
facts.push(
|
|
3088
|
-
fact("run.slowestNode", "Slowest observed node", {
|
|
3089
|
-
name: slowest.node.event.name,
|
|
3090
|
-
kind: slowest.node.event.kind,
|
|
3091
|
-
durationMs: slowest.node.event.durationMs
|
|
3092
|
-
}, redactor)
|
|
3093
|
-
);
|
|
3097
|
+
function mergeAttributes(start, complete) {
|
|
3098
|
+
const merged = {
|
|
3099
|
+
...isRecord6(complete.attributes) ? complete.attributes : {},
|
|
3100
|
+
...isRecord6(start.attributes) ? start.attributes : {}
|
|
3101
|
+
};
|
|
3102
|
+
merged.legacyEvent = start.attributes?.legacyEvent ?? complete.attributes?.legacyEvent;
|
|
3103
|
+
merged.legacyCompleteEvent = complete.attributes?.legacyEvent;
|
|
3104
|
+
if (complete.attributes?.errorStack !== void 0) {
|
|
3105
|
+
merged.errorStack = complete.attributes.errorStack;
|
|
3094
3106
|
}
|
|
3095
|
-
|
|
3096
|
-
return facts;
|
|
3107
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
3097
3108
|
}
|
|
3098
|
-
function
|
|
3099
|
-
const
|
|
3100
|
-
const
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
}
|
|
3121
|
-
if (kindFact !== void 0) {
|
|
3122
|
-
inferences.push({
|
|
3123
|
-
id: "shape.kind-mix",
|
|
3124
|
-
label: "Trace shape",
|
|
3125
|
-
text: "The explanation is based on the observed event kind mix, not generated content.",
|
|
3126
|
-
evidence: [kindFact.id],
|
|
3127
|
-
confidence: "deterministic"
|
|
3128
|
-
});
|
|
3129
|
-
}
|
|
3130
|
-
if (durationFact !== void 0) {
|
|
3131
|
-
inferences.push({
|
|
3132
|
-
id: "timing.duration",
|
|
3133
|
-
label: "Timing",
|
|
3134
|
-
text: "Timing claims are limited to persisted duration fields in the trace.",
|
|
3135
|
-
evidence: [durationFact.id],
|
|
3136
|
-
confidence: "deterministic"
|
|
3137
|
-
});
|
|
3138
|
-
}
|
|
3139
|
-
return inferences;
|
|
3109
|
+
function pairStartComplete(start, complete) {
|
|
3110
|
+
const attributes = mergeAttributes(start, complete);
|
|
3111
|
+
const paired = {
|
|
3112
|
+
...cloneEvent(start),
|
|
3113
|
+
status: complete.status,
|
|
3114
|
+
timestamp: complete.timestamp ?? start.timestamp,
|
|
3115
|
+
...complete.endedAt !== void 0 ? { endedAt: complete.endedAt } : {},
|
|
3116
|
+
...complete.durationMs !== void 0 ? { durationMs: complete.durationMs } : {},
|
|
3117
|
+
...complete.error !== void 0 ? { error: { ...complete.error } } : {},
|
|
3118
|
+
...complete.tokenUsage !== void 0 && start.tokenUsage === void 0 ? { tokenUsage: { ...complete.tokenUsage } } : {},
|
|
3119
|
+
...attributes !== void 0 ? { attributes } : {}
|
|
3120
|
+
};
|
|
3121
|
+
return {
|
|
3122
|
+
...paired,
|
|
3123
|
+
sourceEventIds: Object.freeze([start.eventId, complete.eventId]),
|
|
3124
|
+
projection: {
|
|
3125
|
+
paired: true,
|
|
3126
|
+
absorbedEventIds: Object.freeze([complete.eventId]),
|
|
3127
|
+
parentNormalized: false,
|
|
3128
|
+
...start.parentId !== void 0 ? { originalParentId: start.parentId } : {}
|
|
3129
|
+
}
|
|
3130
|
+
};
|
|
3140
3131
|
}
|
|
3141
|
-
function
|
|
3142
|
-
const redactionProfile = options.redactionProfile ?? "local";
|
|
3143
|
-
const resolved = resolveRedactionProfile(redactionProfile);
|
|
3144
|
-
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
3145
|
-
const mode = options.mode ?? "local";
|
|
3146
|
-
const facts = buildFacts(run, redactor);
|
|
3132
|
+
function asLogical(event, extras) {
|
|
3147
3133
|
return {
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
"Generated locally without provider or network calls.",
|
|
3157
|
-
"Facts are observed from normalized trace data; inferences are deterministic labels."
|
|
3158
|
-
]
|
|
3134
|
+
...cloneEvent(event),
|
|
3135
|
+
sourceEventIds: Object.freeze([event.eventId]),
|
|
3136
|
+
projection: {
|
|
3137
|
+
paired: false,
|
|
3138
|
+
absorbedEventIds: Object.freeze([]),
|
|
3139
|
+
parentNormalized: extras?.parentNormalized === true,
|
|
3140
|
+
...{}
|
|
3141
|
+
}
|
|
3159
3142
|
};
|
|
3160
3143
|
}
|
|
3161
|
-
|
|
3162
|
-
|
|
3144
|
+
function projectLogicalEvents(events) {
|
|
3145
|
+
const diagnostics = [];
|
|
3146
|
+
const byRun = /* @__PURE__ */ new Map();
|
|
3147
|
+
for (const event of events) {
|
|
3148
|
+
const list2 = byRun.get(event.runId) ?? [];
|
|
3149
|
+
list2.push(event);
|
|
3150
|
+
byRun.set(event.runId, list2);
|
|
3151
|
+
}
|
|
3152
|
+
const absorbedIds = /* @__PURE__ */ new Set();
|
|
3153
|
+
const logicalByRawId = /* @__PURE__ */ new Map();
|
|
3154
|
+
const stepIdToLogicalId = /* @__PURE__ */ new Map();
|
|
3155
|
+
const logical = [];
|
|
3156
|
+
const orderedRuns = [...byRun.keys()].sort((a, b) => a.localeCompare(b));
|
|
3157
|
+
for (const runId of orderedRuns) {
|
|
3158
|
+
const runEvents = byRun.get(runId) ?? [];
|
|
3159
|
+
const starts = [];
|
|
3160
|
+
const completes = [];
|
|
3161
|
+
const others = [];
|
|
3162
|
+
for (const event of runEvents) {
|
|
3163
|
+
const legacy = legacyEvent(event);
|
|
3164
|
+
if (legacy === "step_started" || legacy === "run_started" && event.status === "running") {
|
|
3165
|
+
starts.push(event);
|
|
3166
|
+
} else if (legacy === "step_completed" || legacy === "run_completed") {
|
|
3167
|
+
completes.push(event);
|
|
3168
|
+
} else {
|
|
3169
|
+
others.push(event);
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
const usedCompletes = /* @__PURE__ */ new Set();
|
|
3173
|
+
for (const start of starts) {
|
|
3174
|
+
const stepId = stepIdOf(start);
|
|
3175
|
+
let match;
|
|
3176
|
+
if (legacyEvent(start) === "run_started") {
|
|
3177
|
+
const candidates = completes.filter(
|
|
3178
|
+
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "run_completed"
|
|
3179
|
+
);
|
|
3180
|
+
if (candidates.length > 1) {
|
|
3181
|
+
diagnostics.push({
|
|
3182
|
+
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
3183
|
+
message: `Multiple run_completed rows for run ${runId}; using first by eventId.`,
|
|
3184
|
+
eventIds: candidates.map((c) => c.eventId)
|
|
3185
|
+
});
|
|
3186
|
+
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
3187
|
+
}
|
|
3188
|
+
match = candidates[0];
|
|
3189
|
+
} else if (stepId) {
|
|
3190
|
+
const candidates = completes.filter(
|
|
3191
|
+
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "step_completed" && stepIdOf(c) === stepId
|
|
3192
|
+
);
|
|
3193
|
+
if (candidates.length > 1) {
|
|
3194
|
+
diagnostics.push({
|
|
3195
|
+
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
3196
|
+
message: `Multiple step_completed rows for stepId ${stepId}; using first by eventId.`,
|
|
3197
|
+
eventIds: candidates.map((c) => c.eventId)
|
|
3198
|
+
});
|
|
3199
|
+
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
3200
|
+
}
|
|
3201
|
+
match = candidates[0];
|
|
3202
|
+
}
|
|
3203
|
+
if (match) {
|
|
3204
|
+
usedCompletes.add(match.eventId);
|
|
3205
|
+
absorbedIds.add(match.eventId);
|
|
3206
|
+
const paired = pairStartComplete(start, match);
|
|
3207
|
+
logical.push(paired);
|
|
3208
|
+
logicalByRawId.set(start.eventId, paired);
|
|
3209
|
+
logicalByRawId.set(match.eventId, paired);
|
|
3210
|
+
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, paired.eventId);
|
|
3211
|
+
} else {
|
|
3212
|
+
diagnostics.push({
|
|
3213
|
+
code: "AI_LOGICAL_PAIR_UNMATCHED_START",
|
|
3214
|
+
message: `No matching complete for start ${start.eventId}.`,
|
|
3215
|
+
eventIds: [start.eventId]
|
|
3216
|
+
});
|
|
3217
|
+
const alone = asLogical(start);
|
|
3218
|
+
logical.push(alone);
|
|
3219
|
+
logicalByRawId.set(start.eventId, alone);
|
|
3220
|
+
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
for (const complete of completes) {
|
|
3224
|
+
if (usedCompletes.has(complete.eventId)) continue;
|
|
3225
|
+
diagnostics.push({
|
|
3226
|
+
code: "AI_LOGICAL_PAIR_UNMATCHED_COMPLETE",
|
|
3227
|
+
message: `No matching start for complete ${complete.eventId}.`,
|
|
3228
|
+
eventIds: [complete.eventId]
|
|
3229
|
+
});
|
|
3230
|
+
const alone = asLogical(complete);
|
|
3231
|
+
logical.push(alone);
|
|
3232
|
+
logicalByRawId.set(complete.eventId, alone);
|
|
3233
|
+
const stepId = stepIdOf(complete);
|
|
3234
|
+
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
3235
|
+
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
for (const event of others) {
|
|
3239
|
+
const alone = asLogical(event);
|
|
3240
|
+
logical.push(alone);
|
|
3241
|
+
logicalByRawId.set(event.eventId, alone);
|
|
3242
|
+
const stepId = stepIdOf(event);
|
|
3243
|
+
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
3244
|
+
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
const logicalById = new Map(logical.map((e) => [e.eventId, e]));
|
|
3249
|
+
const normalized = [];
|
|
3250
|
+
for (const event of logical) {
|
|
3251
|
+
const originalParentId = event.parentId;
|
|
3252
|
+
if (!originalParentId) {
|
|
3253
|
+
normalized.push(event);
|
|
3254
|
+
continue;
|
|
3255
|
+
}
|
|
3256
|
+
let nextParent = originalParentId;
|
|
3257
|
+
let remapped = false;
|
|
3258
|
+
const viaAbsorbed = logicalByRawId.get(originalParentId);
|
|
3259
|
+
if (viaAbsorbed && viaAbsorbed.eventId !== originalParentId) {
|
|
3260
|
+
nextParent = viaAbsorbed.eventId;
|
|
3261
|
+
remapped = true;
|
|
3262
|
+
} else if (!logicalById.has(originalParentId)) {
|
|
3263
|
+
const viaStep = stepIdToLogicalId.get(`${event.runId}:${originalParentId}`);
|
|
3264
|
+
if (viaStep) {
|
|
3265
|
+
nextParent = viaStep;
|
|
3266
|
+
remapped = true;
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
if (!remapped) {
|
|
3270
|
+
if (originalParentId === event.eventId) {
|
|
3271
|
+
diagnostics.push({
|
|
3272
|
+
code: "AI_LOGICAL_SELF_PARENT_REMOVED",
|
|
3273
|
+
message: formatProgrammaticDiagnostic(
|
|
3274
|
+
"AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
3275
|
+
`Removed self-parent edge on ${event.eventId}.`
|
|
3276
|
+
),
|
|
3277
|
+
eventIds: [event.eventId]
|
|
3278
|
+
});
|
|
3279
|
+
const { parentId: _drop, ...rest } = event;
|
|
3280
|
+
normalized.push({
|
|
3281
|
+
...rest,
|
|
3282
|
+
projection: {
|
|
3283
|
+
...event.projection,
|
|
3284
|
+
parentNormalized: true,
|
|
3285
|
+
originalParentId
|
|
3286
|
+
}
|
|
3287
|
+
});
|
|
3288
|
+
continue;
|
|
3289
|
+
}
|
|
3290
|
+
if (!logicalById.has(originalParentId) && !logicalByRawId.has(originalParentId)) {
|
|
3291
|
+
const mapping = event.attributes?.parentMapping;
|
|
3292
|
+
const unresolved = mapping === "unresolved" || event.attributes?.parentUnresolved === true || event.attributes?.unresolvedParent === true || // LangGraph/framework scaffolding sentinel labels are not event ids.
|
|
3293
|
+
/^LangGraph$/i.test(originalParentId) || originalParentId.startsWith("unresolved:");
|
|
3294
|
+
if (!unresolved) {
|
|
3295
|
+
diagnostics.push({
|
|
3296
|
+
code: "AI_LOGICAL_PARENT_UNRESOLVED",
|
|
3297
|
+
message: `Parent ${originalParentId} unresolved for ${event.eventId}.`,
|
|
3298
|
+
eventIds: [event.eventId]
|
|
3299
|
+
});
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
normalized.push(event);
|
|
3303
|
+
continue;
|
|
3304
|
+
}
|
|
3305
|
+
if (nextParent === event.eventId) {
|
|
3306
|
+
diagnostics.push({
|
|
3307
|
+
code: "AI_LOGICAL_SELF_PARENT_REMOVED",
|
|
3308
|
+
message: formatProgrammaticDiagnostic(
|
|
3309
|
+
"AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
3310
|
+
`Removed self-parent edge on ${event.eventId} (was ${originalParentId}).`
|
|
3311
|
+
),
|
|
3312
|
+
eventIds: [event.eventId]
|
|
3313
|
+
});
|
|
3314
|
+
const { parentId: _drop, ...rest } = event;
|
|
3315
|
+
normalized.push({
|
|
3316
|
+
...rest,
|
|
3317
|
+
projection: {
|
|
3318
|
+
...event.projection,
|
|
3319
|
+
parentNormalized: true,
|
|
3320
|
+
originalParentId
|
|
3321
|
+
}
|
|
3322
|
+
});
|
|
3323
|
+
continue;
|
|
3324
|
+
}
|
|
3325
|
+
diagnostics.push({
|
|
3326
|
+
code: "AI_LOGICAL_PARENT_REMAPPED",
|
|
3327
|
+
message: `Remapped parent ${originalParentId} \u2192 ${nextParent} for ${event.eventId}.`,
|
|
3328
|
+
eventIds: [event.eventId]
|
|
3329
|
+
});
|
|
3330
|
+
normalized.push({
|
|
3331
|
+
...event,
|
|
3332
|
+
parentId: nextParent,
|
|
3333
|
+
projection: {
|
|
3334
|
+
...event.projection,
|
|
3335
|
+
parentNormalized: true,
|
|
3336
|
+
originalParentId
|
|
3337
|
+
}
|
|
3338
|
+
});
|
|
3339
|
+
}
|
|
3340
|
+
const rawIndex = new Map(events.map((e, i) => [e.eventId, i]));
|
|
3341
|
+
normalized.sort((a, b) => {
|
|
3342
|
+
const ai = rawIndex.get(a.sourceEventIds[0]) ?? 0;
|
|
3343
|
+
const bi = rawIndex.get(b.sourceEventIds[0]) ?? 0;
|
|
3344
|
+
return ai - bi || a.eventId.localeCompare(b.eventId);
|
|
3345
|
+
});
|
|
3346
|
+
return {
|
|
3347
|
+
logicalEvents: Object.freeze(normalized),
|
|
3348
|
+
diagnostics: Object.freeze(diagnostics)
|
|
3349
|
+
};
|
|
3350
|
+
}
|
|
3351
|
+
function resolveCanonicalToolName(event) {
|
|
3352
|
+
const attrs = event.attributes;
|
|
3353
|
+
const direct = pickString(attrs, ["toolName", "tool"]);
|
|
3354
|
+
if (direct) return direct;
|
|
3355
|
+
const metadata = attrs?.metadata;
|
|
3356
|
+
if (isRecord6(metadata)) {
|
|
3357
|
+
const nested = pickString(metadata, ["toolName", "tool"]);
|
|
3358
|
+
if (nested) return nested;
|
|
3359
|
+
}
|
|
3360
|
+
for (const prefix of ["tool:", "function:", "mcp-tools:"]) {
|
|
3361
|
+
if (event.name.startsWith(prefix)) return event.name.slice(prefix.length);
|
|
3362
|
+
}
|
|
3363
|
+
return event.name;
|
|
3364
|
+
}
|
|
3365
|
+
function pickString(record, keys) {
|
|
3366
|
+
if (!record) return void 0;
|
|
3367
|
+
for (const key of keys) {
|
|
3368
|
+
const value = record[key];
|
|
3369
|
+
if (typeof value === "string" && value.trim() !== "") return value.trim();
|
|
3370
|
+
}
|
|
3371
|
+
return void 0;
|
|
3372
|
+
}
|
|
3373
|
+
var init_logical_events = __esm({
|
|
3374
|
+
"packages/core/src/checks/logical-events.ts"() {
|
|
3375
|
+
init_programmatic();
|
|
3376
|
+
}
|
|
3377
|
+
});
|
|
3378
|
+
|
|
3379
|
+
// packages/core/src/explain.ts
|
|
3380
|
+
function toDisplayStatus(status) {
|
|
3381
|
+
if (status === "ok") return "success";
|
|
3382
|
+
if (status === "error") return "error";
|
|
3383
|
+
if (status === "running") return "running";
|
|
3384
|
+
return "unknown";
|
|
3385
|
+
}
|
|
3386
|
+
function flatten(nodes, out = []) {
|
|
3387
|
+
for (const node of nodes) {
|
|
3388
|
+
out.push({ node, index: out.length + 1 });
|
|
3389
|
+
flatten(node.children, out);
|
|
3390
|
+
}
|
|
3391
|
+
return out;
|
|
3392
|
+
}
|
|
3393
|
+
function redactValue(redactor, key, value) {
|
|
3394
|
+
return redactor.redactValue(key, value);
|
|
3395
|
+
}
|
|
3396
|
+
function fact(id, label, value, redactor) {
|
|
3397
|
+
return {
|
|
3398
|
+
id,
|
|
3399
|
+
label,
|
|
3400
|
+
value: redactValue(redactor, id.split(".").at(-1) ?? id, value),
|
|
3401
|
+
source: "trace",
|
|
3402
|
+
confidence: "observed"
|
|
3403
|
+
};
|
|
3404
|
+
}
|
|
3405
|
+
function topKinds(run) {
|
|
3406
|
+
return Object.entries(run.metadata.kinds).filter(([, count]) => count > 0).sort((a, b) => {
|
|
3407
|
+
if (b[1] !== a[1]) return b[1] - a[1];
|
|
3408
|
+
return a[0].localeCompare(b[0]);
|
|
3409
|
+
}).slice(0, 5).map(([kind, count]) => `${kind}:${count}`);
|
|
3410
|
+
}
|
|
3411
|
+
function countErrorNodes(nodes) {
|
|
3412
|
+
return nodes.filter((entry) => entry.node.event.status === "error").length;
|
|
3413
|
+
}
|
|
3414
|
+
function slowestNode(nodes) {
|
|
3415
|
+
return nodes.filter(
|
|
3416
|
+
(entry) => entry.node.event.durationMs !== void 0 && // The RUN boundary spans the whole run, so its duration always dominates
|
|
3417
|
+
// and shadows every real step. Rank actual work nodes only, matching the
|
|
3418
|
+
// stats slowest-step ranking, which never counts the run envelope.
|
|
3419
|
+
entry.node.event.kind !== "RUN"
|
|
3420
|
+
).sort((a, b) => {
|
|
3421
|
+
const delta = (b.node.event.durationMs ?? 0) - (a.node.event.durationMs ?? 0);
|
|
3422
|
+
return delta !== 0 ? delta : a.index - b.index;
|
|
3423
|
+
})[0];
|
|
3424
|
+
}
|
|
3425
|
+
function attributeFacts(nodes, redactor) {
|
|
3426
|
+
const facts = [];
|
|
3427
|
+
for (const entry of nodes) {
|
|
3428
|
+
const attrs = entry.node.event.attributes;
|
|
3429
|
+
if (attrs === void 0) continue;
|
|
3430
|
+
for (const key of Object.keys(attrs).sort()) {
|
|
3431
|
+
facts.push({
|
|
3432
|
+
id: `node.${entry.index}.attributes.${key}`,
|
|
3433
|
+
label: `${entry.node.event.name} attribute ${key}`,
|
|
3434
|
+
value: redactValue(redactor, key, attrs[key]),
|
|
3435
|
+
source: "trace",
|
|
3436
|
+
confidence: "observed"
|
|
3437
|
+
});
|
|
3438
|
+
if (facts.length >= 8) return facts;
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
return facts;
|
|
3442
|
+
}
|
|
3443
|
+
function buildFacts(run, redactor, events) {
|
|
3444
|
+
const nodes = flatten(run.children);
|
|
3445
|
+
const facts = [
|
|
3446
|
+
fact("run.id", "Run id", run.runId, redactor),
|
|
3447
|
+
fact("run.name", "Run name", run.name ?? run.runId, redactor),
|
|
3448
|
+
fact("run.status", "Run status", toDisplayStatus(run.status), redactor),
|
|
3449
|
+
fact("run.totalEvents", "Total events", run.metadata.totalEvents, redactor),
|
|
3450
|
+
fact("run.stepCount", "Top-level step count", run.children.length, redactor),
|
|
3451
|
+
fact("run.nodeCount", "Total node count", nodes.length, redactor),
|
|
3452
|
+
fact("run.errorNodeCount", "Error node count", countErrorNodes(nodes), redactor),
|
|
3453
|
+
fact("run.kinds", "Observed kind mix", topKinds(run), redactor)
|
|
3454
|
+
];
|
|
3455
|
+
if (run.durationMs !== void 0) {
|
|
3456
|
+
facts.push(fact("run.durationMs", "Run duration milliseconds", run.durationMs, redactor));
|
|
3457
|
+
}
|
|
3458
|
+
const slowest = slowestNode(nodes);
|
|
3459
|
+
if (slowest !== void 0) {
|
|
3460
|
+
facts.push(
|
|
3461
|
+
fact("run.slowestNode", "Slowest observed node", {
|
|
3462
|
+
name: slowest.node.event.name,
|
|
3463
|
+
kind: slowest.node.event.kind,
|
|
3464
|
+
durationMs: slowest.node.event.durationMs
|
|
3465
|
+
}, redactor)
|
|
3466
|
+
);
|
|
3467
|
+
}
|
|
3468
|
+
if (events !== void 0) {
|
|
3469
|
+
const scoped = events.filter((event) => event.runId === run.runId);
|
|
3470
|
+
const { logicalEvents: logical } = projectLogicalEvents(scoped);
|
|
3471
|
+
const logicalSteps = logical.filter((event) => event.kind !== "RUN");
|
|
3472
|
+
facts.push(
|
|
3473
|
+
fact("run.rawEventCount", "Raw persisted event count", scoped.length, redactor),
|
|
3474
|
+
fact(
|
|
3475
|
+
"run.logicalStepCount",
|
|
3476
|
+
"Logical lifecycle step count",
|
|
3477
|
+
logicalSteps.length,
|
|
3478
|
+
redactor
|
|
3479
|
+
),
|
|
3480
|
+
fact(
|
|
3481
|
+
"run.logicalEventCount",
|
|
3482
|
+
"Logical lifecycle event count (including RUN)",
|
|
3483
|
+
logical.length,
|
|
3484
|
+
redactor
|
|
3485
|
+
)
|
|
3486
|
+
);
|
|
3487
|
+
const slowestLogical = [...logicalSteps].filter((event) => typeof event.durationMs === "number").sort((a, b) => {
|
|
3488
|
+
const delta = (b.durationMs ?? 0) - (a.durationMs ?? 0);
|
|
3489
|
+
return delta !== 0 ? delta : a.eventId.localeCompare(b.eventId);
|
|
3490
|
+
})[0];
|
|
3491
|
+
if (slowestLogical !== void 0) {
|
|
3492
|
+
facts.push(
|
|
3493
|
+
fact("run.slowestLogicalStep", "Slowest logical lifecycle step", {
|
|
3494
|
+
name: slowestLogical.name,
|
|
3495
|
+
kind: slowestLogical.kind,
|
|
3496
|
+
durationMs: slowestLogical.durationMs
|
|
3497
|
+
}, redactor)
|
|
3498
|
+
);
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
facts.push(...attributeFacts(nodes, redactor));
|
|
3502
|
+
return facts;
|
|
3503
|
+
}
|
|
3504
|
+
function buildInferences(run, facts) {
|
|
3505
|
+
const inferences = [];
|
|
3506
|
+
const errorFact = facts.find((item) => item.id === "run.errorNodeCount");
|
|
3507
|
+
const kindFact = facts.find((item) => item.id === "run.kinds");
|
|
3508
|
+
const durationFact = facts.find((item) => item.id === "run.durationMs");
|
|
3509
|
+
const errorNodeCount = typeof errorFact?.value === "number" ? errorFact.value : 0;
|
|
3510
|
+
if (run.status === "error" || errorNodeCount > 0) {
|
|
3511
|
+
inferences.push({
|
|
3512
|
+
id: "outcome.error",
|
|
3513
|
+
label: "Outcome",
|
|
3514
|
+
text: "The run recorded an error status or at least one error node.",
|
|
3515
|
+
evidence: ["run.status", "run.errorNodeCount"],
|
|
3516
|
+
confidence: "deterministic"
|
|
3517
|
+
});
|
|
3518
|
+
} else if (run.status === "ok") {
|
|
3519
|
+
inferences.push({
|
|
3520
|
+
id: "outcome.success",
|
|
3521
|
+
label: "Outcome",
|
|
3522
|
+
text: "The run completed without observed error nodes.",
|
|
3523
|
+
evidence: ["run.status", "run.errorNodeCount"],
|
|
3524
|
+
confidence: "deterministic"
|
|
3525
|
+
});
|
|
3526
|
+
}
|
|
3527
|
+
if (kindFact !== void 0) {
|
|
3528
|
+
inferences.push({
|
|
3529
|
+
id: "shape.kind-mix",
|
|
3530
|
+
label: "Trace shape",
|
|
3531
|
+
text: "The explanation is based on the observed event kind mix, not generated content.",
|
|
3532
|
+
evidence: [kindFact.id],
|
|
3533
|
+
confidence: "deterministic"
|
|
3534
|
+
});
|
|
3535
|
+
}
|
|
3536
|
+
if (durationFact !== void 0) {
|
|
3537
|
+
inferences.push({
|
|
3538
|
+
id: "timing.duration",
|
|
3539
|
+
label: "Timing",
|
|
3540
|
+
text: "Timing claims are limited to persisted duration fields in the trace.",
|
|
3541
|
+
evidence: [durationFact.id],
|
|
3542
|
+
confidence: "deterministic"
|
|
3543
|
+
});
|
|
3544
|
+
}
|
|
3545
|
+
return inferences;
|
|
3546
|
+
}
|
|
3547
|
+
function buildLocalExplanation(run, options = {}) {
|
|
3548
|
+
const redactionProfile = options.redactionProfile ?? "local";
|
|
3549
|
+
const resolved = resolveRedactionProfile(redactionProfile);
|
|
3550
|
+
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
3551
|
+
const mode = options.mode ?? "local";
|
|
3552
|
+
const facts = buildFacts(run, redactor, options.events);
|
|
3553
|
+
return {
|
|
3554
|
+
mode,
|
|
3555
|
+
runId: String(redactValue(redactor, "runId", run.runId)),
|
|
3556
|
+
...run.name !== void 0 ? { name: String(redactValue(redactor, "name", run.name)) } : {},
|
|
3557
|
+
...run.status !== void 0 ? { status: toDisplayStatus(run.status) } : {},
|
|
3558
|
+
redactionProfile,
|
|
3559
|
+
facts,
|
|
3560
|
+
inferences: mode === "dry-run" ? [] : buildInferences(run, facts),
|
|
3561
|
+
notes: [
|
|
3562
|
+
"Generated locally without provider or network calls.",
|
|
3563
|
+
"Facts are observed from normalized trace data; inferences are deterministic labels."
|
|
3564
|
+
]
|
|
3565
|
+
};
|
|
3566
|
+
}
|
|
3567
|
+
var init_explain = __esm({
|
|
3568
|
+
"packages/core/src/explain.ts"() {
|
|
3569
|
+
init_logical_events();
|
|
3163
3570
|
init_redactor();
|
|
3164
3571
|
init_redaction_profiles();
|
|
3165
3572
|
}
|
|
@@ -3849,7 +4256,17 @@ var init_cohort = __esm({
|
|
|
3849
4256
|
|
|
3850
4257
|
// packages/core/src/sessions/checks.ts
|
|
3851
4258
|
function emptySummary() {
|
|
3852
|
-
return {
|
|
4259
|
+
return {
|
|
4260
|
+
passed: 0,
|
|
4261
|
+
failed: 0,
|
|
4262
|
+
warnings: 0,
|
|
4263
|
+
errors: 0,
|
|
4264
|
+
rulesEvaluated: 0,
|
|
4265
|
+
rulesPassed: 0,
|
|
4266
|
+
rulesWarning: 0,
|
|
4267
|
+
rulesFailed: 0,
|
|
4268
|
+
rulesError: 0
|
|
4269
|
+
};
|
|
3853
4270
|
}
|
|
3854
4271
|
function mergeSummary(target, source) {
|
|
3855
4272
|
return {
|
|
@@ -3857,7 +4274,11 @@ function mergeSummary(target, source) {
|
|
|
3857
4274
|
failed: target.failed + source.failed,
|
|
3858
4275
|
warnings: target.warnings + source.warnings,
|
|
3859
4276
|
errors: target.errors + source.errors,
|
|
3860
|
-
rulesEvaluated: target.rulesEvaluated + source.rulesEvaluated
|
|
4277
|
+
rulesEvaluated: target.rulesEvaluated + source.rulesEvaluated,
|
|
4278
|
+
rulesPassed: target.rulesPassed + source.rulesPassed,
|
|
4279
|
+
rulesWarning: target.rulesWarning + source.rulesWarning,
|
|
4280
|
+
rulesFailed: target.rulesFailed + source.rulesFailed,
|
|
4281
|
+
rulesError: target.rulesError + source.rulesError
|
|
3861
4282
|
};
|
|
3862
4283
|
}
|
|
3863
4284
|
function sessionDiagnostic(code, message) {
|
|
@@ -4202,7 +4623,7 @@ var init_sessions = __esm({
|
|
|
4202
4623
|
init_checks();
|
|
4203
4624
|
}
|
|
4204
4625
|
});
|
|
4205
|
-
function
|
|
4626
|
+
function isRecord7(value) {
|
|
4206
4627
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4207
4628
|
}
|
|
4208
4629
|
function safeParse(line) {
|
|
@@ -4224,7 +4645,7 @@ async function isAgentInspectTrace(filePath) {
|
|
|
4224
4645
|
if (trimmed === "") continue;
|
|
4225
4646
|
const parsed = safeParse(trimmed);
|
|
4226
4647
|
if (!parsed) continue;
|
|
4227
|
-
if (!
|
|
4648
|
+
if (!isRecord7(parsed)) continue;
|
|
4228
4649
|
checked += 1;
|
|
4229
4650
|
if (isTraceEvent(parsed)) return true;
|
|
4230
4651
|
const ev = parsed.event;
|
|
@@ -4590,518 +5011,157 @@ function inferEvidenceFileRole(relativePath) {
|
|
|
4590
5011
|
return "redaction-report";
|
|
4591
5012
|
}
|
|
4592
5013
|
if (base === "summary.md") {
|
|
4593
|
-
return "summary";
|
|
4594
|
-
}
|
|
4595
|
-
return "other";
|
|
4596
|
-
}
|
|
4597
|
-
function buildEvidenceFileEntries(files) {
|
|
4598
|
-
const byPath = /* @__PURE__ */ new Map();
|
|
4599
|
-
for (const file of files) {
|
|
4600
|
-
const relativePath = assertEvidenceRelativePath(file.path);
|
|
4601
|
-
if (relativePath === EVIDENCE_MANIFEST_FILENAME) {
|
|
4602
|
-
throw new Error(
|
|
4603
|
-
`Do not include ${EVIDENCE_MANIFEST_FILENAME} in packaged file hashes (self-hash is undefined).`
|
|
4604
|
-
);
|
|
4605
|
-
}
|
|
4606
|
-
if (byPath.has(relativePath)) {
|
|
4607
|
-
throw new Error(`Duplicate evidence file path: ${relativePath}`);
|
|
4608
|
-
}
|
|
4609
|
-
byPath.set(relativePath, {
|
|
4610
|
-
path: relativePath,
|
|
4611
|
-
sha256: sha256Hex(file.content),
|
|
4612
|
-
role: file.role ?? inferEvidenceFileRole(relativePath)
|
|
4613
|
-
});
|
|
4614
|
-
}
|
|
4615
|
-
return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
4616
|
-
}
|
|
4617
|
-
function buildEvidenceManifest(parts) {
|
|
4618
|
-
const runIds = [...parts.runIds];
|
|
4619
|
-
if (runIds.length === 0) {
|
|
4620
|
-
throw new Error("Evidence manifest requires at least one run id.");
|
|
4621
|
-
}
|
|
4622
|
-
for (const item of parts.sourceHashes) {
|
|
4623
|
-
if (!runIds.includes(item.runId)) {
|
|
4624
|
-
throw new Error(`sourceHashes runId "${item.runId}" is not listed in source.runIds.`);
|
|
4625
|
-
}
|
|
4626
|
-
if (item.algorithm !== "sha256") {
|
|
4627
|
-
throw new Error(`Unsupported source hash algorithm: ${item.algorithm}`);
|
|
4628
|
-
}
|
|
4629
|
-
}
|
|
4630
|
-
const assessment = {
|
|
4631
|
-
status: parts.assessmentStatus,
|
|
4632
|
-
note: parts.note ?? EVIDENCE_ASSESSMENT_NOTE
|
|
4633
|
-
};
|
|
4634
|
-
if (parts.sourceStatus !== void 0) {
|
|
4635
|
-
assessment.sourceStatus = parts.sourceStatus;
|
|
4636
|
-
}
|
|
4637
|
-
return {
|
|
4638
|
-
evidenceFormatVersion: EVIDENCE_FORMAT_VERSION,
|
|
4639
|
-
generator: {
|
|
4640
|
-
name: parts.generatorName ?? "agent-inspect",
|
|
4641
|
-
version: parts.generatorVersion
|
|
4642
|
-
},
|
|
4643
|
-
createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
4644
|
-
source: {
|
|
4645
|
-
runIds,
|
|
4646
|
-
traceSchemaVersions: [...parts.traceSchemaVersions].sort((a, b) => a.localeCompare(b)),
|
|
4647
|
-
sourceHashes: [...parts.sourceHashes].sort((a, b) => a.runId.localeCompare(b.runId))
|
|
4648
|
-
},
|
|
4649
|
-
policy: {
|
|
4650
|
-
redactionProfile: parts.redactionProfile,
|
|
4651
|
-
verificationPolicy: parts.verificationPolicy ?? parts.redactionProfile
|
|
4652
|
-
},
|
|
4653
|
-
assessment,
|
|
4654
|
-
...parts.semantics !== void 0 ? { semantics: { ...parts.semantics } } : {},
|
|
4655
|
-
...parts.contract !== void 0 ? { contract: { ...parts.contract } } : {},
|
|
4656
|
-
files: buildEvidenceFileEntries(parts.files)
|
|
4657
|
-
};
|
|
4658
|
-
}
|
|
4659
|
-
function validateEvidenceManifest(value) {
|
|
4660
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4661
|
-
throw new Error("Evidence manifest must be a JSON object.");
|
|
4662
|
-
}
|
|
4663
|
-
const record = value;
|
|
4664
|
-
if (record.evidenceFormatVersion !== EVIDENCE_FORMAT_VERSION) {
|
|
4665
|
-
throw new Error(
|
|
4666
|
-
`Unsupported evidenceFormatVersion: ${String(record.evidenceFormatVersion)}`
|
|
4667
|
-
);
|
|
4668
|
-
}
|
|
4669
|
-
const generator = record.generator;
|
|
4670
|
-
if (generator === null || typeof generator !== "object" || Array.isArray(generator) || typeof generator.name !== "string" || typeof generator.version !== "string") {
|
|
4671
|
-
throw new Error("Evidence manifest requires generator.name and generator.version.");
|
|
4672
|
-
}
|
|
4673
|
-
const source = record.source;
|
|
4674
|
-
if (source === null || typeof source !== "object" || Array.isArray(source)) {
|
|
4675
|
-
throw new Error("Evidence manifest requires source.");
|
|
4676
|
-
}
|
|
4677
|
-
const sourceRecord = source;
|
|
4678
|
-
if (!Array.isArray(sourceRecord.runIds) || sourceRecord.runIds.length === 0) {
|
|
4679
|
-
throw new Error("Evidence manifest source.runIds must be a non-empty array.");
|
|
4680
|
-
}
|
|
4681
|
-
if (!Array.isArray(sourceRecord.traceSchemaVersions)) {
|
|
4682
|
-
throw new Error("Evidence manifest source.traceSchemaVersions must be an array.");
|
|
4683
|
-
}
|
|
4684
|
-
if (!Array.isArray(sourceRecord.sourceHashes)) {
|
|
4685
|
-
throw new Error("Evidence manifest source.sourceHashes must be an array.");
|
|
4686
|
-
}
|
|
4687
|
-
const policy = record.policy;
|
|
4688
|
-
if (policy === null || typeof policy !== "object" || Array.isArray(policy)) {
|
|
4689
|
-
throw new Error("Evidence manifest requires policy.");
|
|
4690
|
-
}
|
|
4691
|
-
const assessment = record.assessment;
|
|
4692
|
-
if (assessment === null || typeof assessment !== "object" || Array.isArray(assessment) || typeof assessment.status !== "string") {
|
|
4693
|
-
throw new Error("Evidence manifest requires assessment.status.");
|
|
4694
|
-
}
|
|
4695
|
-
if (!Array.isArray(record.files) || record.files.length === 0) {
|
|
4696
|
-
throw new Error("Evidence manifest files must be a non-empty array.");
|
|
4697
|
-
}
|
|
4698
|
-
for (const file of record.files) {
|
|
4699
|
-
if (file === null || typeof file !== "object" || Array.isArray(file)) {
|
|
4700
|
-
throw new Error("Evidence manifest file entries must be objects.");
|
|
4701
|
-
}
|
|
4702
|
-
const entry = file;
|
|
4703
|
-
if (typeof entry.path !== "string") {
|
|
4704
|
-
throw new Error("Evidence file entry requires path.");
|
|
4705
|
-
}
|
|
4706
|
-
assertEvidenceRelativePath(entry.path);
|
|
4707
|
-
if (typeof entry.sha256 !== "string" || !/^[a-f0-9]{64}$/i.test(entry.sha256)) {
|
|
4708
|
-
throw new Error(`Evidence file entry requires sha256 hex for ${entry.path}.`);
|
|
4709
|
-
}
|
|
4710
|
-
}
|
|
4711
|
-
return value;
|
|
4712
|
-
}
|
|
4713
|
-
function parseEvidenceManifestJson(text) {
|
|
4714
|
-
let parsed;
|
|
4715
|
-
try {
|
|
4716
|
-
parsed = JSON.parse(text);
|
|
4717
|
-
} catch (error) {
|
|
4718
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
4719
|
-
throw new Error(`Evidence manifest is not valid JSON: ${message}`);
|
|
4720
|
-
}
|
|
4721
|
-
return validateEvidenceManifest(parsed);
|
|
4722
|
-
}
|
|
4723
|
-
function collectTraceSchemaVersions(jsonl) {
|
|
4724
|
-
const versions = /* @__PURE__ */ new Set();
|
|
4725
|
-
for (const line of jsonl.split(/\r?\n/)) {
|
|
4726
|
-
const trimmed = line.trim();
|
|
4727
|
-
if (trimmed === "") continue;
|
|
4728
|
-
try {
|
|
4729
|
-
const row = JSON.parse(trimmed);
|
|
4730
|
-
if (typeof row.schemaVersion === "string" && row.schemaVersion.trim() !== "") {
|
|
4731
|
-
versions.add(row.schemaVersion.trim());
|
|
4732
|
-
}
|
|
4733
|
-
} catch {
|
|
4734
|
-
}
|
|
4735
|
-
}
|
|
4736
|
-
return [...versions].sort((a, b) => a.localeCompare(b));
|
|
4737
|
-
}
|
|
4738
|
-
var init_manifest2 = __esm({
|
|
4739
|
-
"packages/core/src/evidence/manifest.ts"() {
|
|
4740
|
-
init_hash();
|
|
4741
|
-
init_paths2();
|
|
4742
|
-
init_types4();
|
|
4743
|
-
}
|
|
4744
|
-
});
|
|
4745
|
-
|
|
4746
|
-
// packages/core/src/diagnostics/programmatic.ts
|
|
4747
|
-
function formatProgrammaticDiagnostic(code, detail) {
|
|
4748
|
-
const spec = PROGRAMMATIC_DIAGNOSTIC_SPECS[code];
|
|
4749
|
-
const summary = detail?.trim() ? detail.trim() : spec.summary;
|
|
4750
|
-
return `${code}: ${summary} Remediation: ${spec.remediation}`;
|
|
4751
|
-
}
|
|
4752
|
-
var PROGRAMMATIC_DIAGNOSTIC_SPECS;
|
|
4753
|
-
var init_programmatic = __esm({
|
|
4754
|
-
"packages/core/src/diagnostics/programmatic.ts"() {
|
|
4755
|
-
PROGRAMMATIC_DIAGNOSTIC_SPECS = Object.freeze({
|
|
4756
|
-
AI_TRACE_INPUT_INVALID: {
|
|
4757
|
-
code: "AI_TRACE_INPUT_INVALID",
|
|
4758
|
-
summary: 'Expected { type: "file", path }, { type: "directory", path }, { type: "string", content }, { type: "buffer", content }, or { type: "stdin" }.',
|
|
4759
|
-
remediation: "For a file path, use openTraceFile(path).",
|
|
4760
|
-
relatedCodes: ["invalid_input"]
|
|
4761
|
-
},
|
|
4762
|
-
AI_TRACE_FORMAT_UNSUPPORTED: {
|
|
4763
|
-
code: "AI_TRACE_FORMAT_UNSUPPORTED",
|
|
4764
|
-
summary: "No trace reader could detect the input format.",
|
|
4765
|
-
remediation: "Pass an AgentInspect JSONL file via openTraceFile, or set options.format to a registered reader.",
|
|
4766
|
-
relatedCodes: ["unsupported_format"]
|
|
4767
|
-
},
|
|
4768
|
-
AI_TRACE_FORMAT_AMBIGUOUS: {
|
|
4769
|
-
code: "AI_TRACE_FORMAT_AMBIGUOUS",
|
|
4770
|
-
summary: "Multiple trace readers matched the input with equal confidence.",
|
|
4771
|
-
remediation: "Set options.format explicitly to disambiguate the reader.",
|
|
4772
|
-
relatedCodes: ["ambiguous_format"]
|
|
4773
|
-
},
|
|
4774
|
-
AI_TRACE_FACTS_INPUT_NOT_NORMALIZED: {
|
|
4775
|
-
code: "AI_TRACE_FACTS_INPUT_NOT_NORMALIZED",
|
|
4776
|
-
summary: "TraceFacts requires TraceReadResult or PersistedInspectEvent[].",
|
|
4777
|
-
remediation: "Use openTraceFile() to normalize a JSONL trace first."
|
|
4778
|
-
},
|
|
4779
|
-
AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED: {
|
|
4780
|
-
code: "AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED",
|
|
4781
|
-
summary: "Multiple runs are available; select a run before executing checks.",
|
|
4782
|
-
remediation: "Pass options.runId or TraceCheckInput.selectedRun.",
|
|
4783
|
-
relatedCodes: ["AI_CHECK_RUN_SELECTION_REQUIRED"]
|
|
4784
|
-
},
|
|
4785
|
-
AI_TRACE_RELATIONSHIP_SELF_PARENT: {
|
|
4786
|
-
code: "AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
4787
|
-
summary: "A parentId equals its own event/step id.",
|
|
4788
|
-
remediation: "Reject at capture (AI_LANGGRAPH_SELF_PARENT_REJECTED) or drop via logical projection; do not invent replacement parents.",
|
|
4789
|
-
relatedCodes: [
|
|
4790
|
-
"AI_LANGGRAPH_SELF_PARENT_REJECTED",
|
|
4791
|
-
"AI_LOGICAL_SELF_PARENT_REMOVED"
|
|
4792
|
-
]
|
|
4793
|
-
},
|
|
4794
|
-
AI_TRACE_RELATIONSHIP_CYCLE: {
|
|
4795
|
-
code: "AI_TRACE_RELATIONSHIP_CYCLE",
|
|
4796
|
-
summary: "Trace contains a parentId cycle.",
|
|
4797
|
-
remediation: "Use visibility-first tree linking for legacy fixtures; prefer acyclic capture for new adapter output.",
|
|
4798
|
-
relatedCodes: ["structure.cycle"]
|
|
4799
|
-
}
|
|
4800
|
-
});
|
|
4801
|
-
}
|
|
4802
|
-
});
|
|
4803
|
-
|
|
4804
|
-
// packages/core/src/checks/logical-events.ts
|
|
4805
|
-
function isRecord7(value) {
|
|
4806
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4807
|
-
}
|
|
4808
|
-
function legacyEvent(event) {
|
|
4809
|
-
const value = event.attributes?.legacyEvent;
|
|
4810
|
-
return typeof value === "string" ? value : void 0;
|
|
4811
|
-
}
|
|
4812
|
-
function stepIdOf(event) {
|
|
4813
|
-
const value = event.attributes?.stepId;
|
|
4814
|
-
if (typeof value === "string" && value.trim() !== "") return value;
|
|
4815
|
-
return void 0;
|
|
4816
|
-
}
|
|
4817
|
-
function cloneEvent(event) {
|
|
4818
|
-
return {
|
|
4819
|
-
...event,
|
|
4820
|
-
...event.attributes !== void 0 ? { attributes: { ...event.attributes } } : {},
|
|
4821
|
-
...event.error !== void 0 ? { error: { ...event.error } } : {},
|
|
4822
|
-
...event.tokenUsage !== void 0 ? { tokenUsage: { ...event.tokenUsage } } : {},
|
|
4823
|
-
...event.source !== void 0 ? { source: { ...event.source } } : {}
|
|
4824
|
-
};
|
|
4825
|
-
}
|
|
4826
|
-
function mergeAttributes(start, complete) {
|
|
4827
|
-
const merged = {
|
|
4828
|
-
...isRecord7(complete.attributes) ? complete.attributes : {},
|
|
4829
|
-
...isRecord7(start.attributes) ? start.attributes : {}
|
|
4830
|
-
};
|
|
4831
|
-
merged.legacyEvent = start.attributes?.legacyEvent ?? complete.attributes?.legacyEvent;
|
|
4832
|
-
merged.legacyCompleteEvent = complete.attributes?.legacyEvent;
|
|
4833
|
-
if (complete.attributes?.errorStack !== void 0) {
|
|
4834
|
-
merged.errorStack = complete.attributes.errorStack;
|
|
5014
|
+
return "summary";
|
|
4835
5015
|
}
|
|
4836
|
-
return
|
|
5016
|
+
return "other";
|
|
4837
5017
|
}
|
|
4838
|
-
function
|
|
4839
|
-
const
|
|
4840
|
-
const
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
...complete.error !== void 0 ? { error: { ...complete.error } } : {},
|
|
4847
|
-
...complete.tokenUsage !== void 0 && start.tokenUsage === void 0 ? { tokenUsage: { ...complete.tokenUsage } } : {},
|
|
4848
|
-
...attributes !== void 0 ? { attributes } : {}
|
|
4849
|
-
};
|
|
4850
|
-
return {
|
|
4851
|
-
...paired,
|
|
4852
|
-
sourceEventIds: Object.freeze([start.eventId, complete.eventId]),
|
|
4853
|
-
projection: {
|
|
4854
|
-
paired: true,
|
|
4855
|
-
absorbedEventIds: Object.freeze([complete.eventId]),
|
|
4856
|
-
parentNormalized: false,
|
|
4857
|
-
...start.parentId !== void 0 ? { originalParentId: start.parentId } : {}
|
|
5018
|
+
function buildEvidenceFileEntries(files) {
|
|
5019
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
5020
|
+
for (const file of files) {
|
|
5021
|
+
const relativePath = assertEvidenceRelativePath(file.path);
|
|
5022
|
+
if (relativePath === EVIDENCE_MANIFEST_FILENAME) {
|
|
5023
|
+
throw new Error(
|
|
5024
|
+
`Do not include ${EVIDENCE_MANIFEST_FILENAME} in packaged file hashes (self-hash is undefined).`
|
|
5025
|
+
);
|
|
4858
5026
|
}
|
|
4859
|
-
|
|
4860
|
-
}
|
|
4861
|
-
function asLogical(event, extras) {
|
|
4862
|
-
return {
|
|
4863
|
-
...cloneEvent(event),
|
|
4864
|
-
sourceEventIds: Object.freeze([event.eventId]),
|
|
4865
|
-
projection: {
|
|
4866
|
-
paired: false,
|
|
4867
|
-
absorbedEventIds: Object.freeze([]),
|
|
4868
|
-
parentNormalized: extras?.parentNormalized === true,
|
|
4869
|
-
...{}
|
|
5027
|
+
if (byPath.has(relativePath)) {
|
|
5028
|
+
throw new Error(`Duplicate evidence file path: ${relativePath}`);
|
|
4870
5029
|
}
|
|
4871
|
-
|
|
5030
|
+
byPath.set(relativePath, {
|
|
5031
|
+
path: relativePath,
|
|
5032
|
+
sha256: sha256Hex(file.content),
|
|
5033
|
+
role: file.role ?? inferEvidenceFileRole(relativePath)
|
|
5034
|
+
});
|
|
5035
|
+
}
|
|
5036
|
+
return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
4872
5037
|
}
|
|
4873
|
-
function
|
|
4874
|
-
const
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
const list2 = byRun.get(event.runId) ?? [];
|
|
4878
|
-
list2.push(event);
|
|
4879
|
-
byRun.set(event.runId, list2);
|
|
5038
|
+
function buildEvidenceManifest(parts) {
|
|
5039
|
+
const runIds = [...parts.runIds];
|
|
5040
|
+
if (runIds.length === 0) {
|
|
5041
|
+
throw new Error("Evidence manifest requires at least one run id.");
|
|
4880
5042
|
}
|
|
4881
|
-
const
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
const logical = [];
|
|
4885
|
-
const orderedRuns = [...byRun.keys()].sort((a, b) => a.localeCompare(b));
|
|
4886
|
-
for (const runId of orderedRuns) {
|
|
4887
|
-
const runEvents = byRun.get(runId) ?? [];
|
|
4888
|
-
const starts = [];
|
|
4889
|
-
const completes = [];
|
|
4890
|
-
const others = [];
|
|
4891
|
-
for (const event of runEvents) {
|
|
4892
|
-
const legacy = legacyEvent(event);
|
|
4893
|
-
if (legacy === "step_started" || legacy === "run_started" && event.status === "running") {
|
|
4894
|
-
starts.push(event);
|
|
4895
|
-
} else if (legacy === "step_completed" || legacy === "run_completed") {
|
|
4896
|
-
completes.push(event);
|
|
4897
|
-
} else {
|
|
4898
|
-
others.push(event);
|
|
4899
|
-
}
|
|
4900
|
-
}
|
|
4901
|
-
const usedCompletes = /* @__PURE__ */ new Set();
|
|
4902
|
-
for (const start of starts) {
|
|
4903
|
-
const stepId = stepIdOf(start);
|
|
4904
|
-
let match;
|
|
4905
|
-
if (legacyEvent(start) === "run_started") {
|
|
4906
|
-
const candidates = completes.filter(
|
|
4907
|
-
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "run_completed"
|
|
4908
|
-
);
|
|
4909
|
-
if (candidates.length > 1) {
|
|
4910
|
-
diagnostics.push({
|
|
4911
|
-
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
4912
|
-
message: `Multiple run_completed rows for run ${runId}; using first by eventId.`,
|
|
4913
|
-
eventIds: candidates.map((c) => c.eventId)
|
|
4914
|
-
});
|
|
4915
|
-
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
4916
|
-
}
|
|
4917
|
-
match = candidates[0];
|
|
4918
|
-
} else if (stepId) {
|
|
4919
|
-
const candidates = completes.filter(
|
|
4920
|
-
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "step_completed" && stepIdOf(c) === stepId
|
|
4921
|
-
);
|
|
4922
|
-
if (candidates.length > 1) {
|
|
4923
|
-
diagnostics.push({
|
|
4924
|
-
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
4925
|
-
message: `Multiple step_completed rows for stepId ${stepId}; using first by eventId.`,
|
|
4926
|
-
eventIds: candidates.map((c) => c.eventId)
|
|
4927
|
-
});
|
|
4928
|
-
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
4929
|
-
}
|
|
4930
|
-
match = candidates[0];
|
|
4931
|
-
}
|
|
4932
|
-
if (match) {
|
|
4933
|
-
usedCompletes.add(match.eventId);
|
|
4934
|
-
absorbedIds.add(match.eventId);
|
|
4935
|
-
const paired = pairStartComplete(start, match);
|
|
4936
|
-
logical.push(paired);
|
|
4937
|
-
logicalByRawId.set(start.eventId, paired);
|
|
4938
|
-
logicalByRawId.set(match.eventId, paired);
|
|
4939
|
-
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, paired.eventId);
|
|
4940
|
-
} else {
|
|
4941
|
-
diagnostics.push({
|
|
4942
|
-
code: "AI_LOGICAL_PAIR_UNMATCHED_START",
|
|
4943
|
-
message: `No matching complete for start ${start.eventId}.`,
|
|
4944
|
-
eventIds: [start.eventId]
|
|
4945
|
-
});
|
|
4946
|
-
const alone = asLogical(start);
|
|
4947
|
-
logical.push(alone);
|
|
4948
|
-
logicalByRawId.set(start.eventId, alone);
|
|
4949
|
-
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
4950
|
-
}
|
|
4951
|
-
}
|
|
4952
|
-
for (const complete of completes) {
|
|
4953
|
-
if (usedCompletes.has(complete.eventId)) continue;
|
|
4954
|
-
diagnostics.push({
|
|
4955
|
-
code: "AI_LOGICAL_PAIR_UNMATCHED_COMPLETE",
|
|
4956
|
-
message: `No matching start for complete ${complete.eventId}.`,
|
|
4957
|
-
eventIds: [complete.eventId]
|
|
4958
|
-
});
|
|
4959
|
-
const alone = asLogical(complete);
|
|
4960
|
-
logical.push(alone);
|
|
4961
|
-
logicalByRawId.set(complete.eventId, alone);
|
|
4962
|
-
const stepId = stepIdOf(complete);
|
|
4963
|
-
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
4964
|
-
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
4965
|
-
}
|
|
5043
|
+
for (const item of parts.sourceHashes) {
|
|
5044
|
+
if (!runIds.includes(item.runId)) {
|
|
5045
|
+
throw new Error(`sourceHashes runId "${item.runId}" is not listed in source.runIds.`);
|
|
4966
5046
|
}
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
logical.push(alone);
|
|
4970
|
-
logicalByRawId.set(event.eventId, alone);
|
|
4971
|
-
const stepId = stepIdOf(event);
|
|
4972
|
-
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
4973
|
-
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
4974
|
-
}
|
|
5047
|
+
if (item.algorithm !== "sha256") {
|
|
5048
|
+
throw new Error(`Unsupported source hash algorithm: ${item.algorithm}`);
|
|
4975
5049
|
}
|
|
4976
5050
|
}
|
|
4977
|
-
const
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
continue;
|
|
4984
|
-
}
|
|
4985
|
-
let nextParent = originalParentId;
|
|
4986
|
-
let remapped = false;
|
|
4987
|
-
const viaAbsorbed = logicalByRawId.get(originalParentId);
|
|
4988
|
-
if (viaAbsorbed && viaAbsorbed.eventId !== originalParentId) {
|
|
4989
|
-
nextParent = viaAbsorbed.eventId;
|
|
4990
|
-
remapped = true;
|
|
4991
|
-
} else if (!logicalById.has(originalParentId)) {
|
|
4992
|
-
const viaStep = stepIdToLogicalId.get(`${event.runId}:${originalParentId}`);
|
|
4993
|
-
if (viaStep) {
|
|
4994
|
-
nextParent = viaStep;
|
|
4995
|
-
remapped = true;
|
|
4996
|
-
}
|
|
4997
|
-
}
|
|
4998
|
-
if (!remapped) {
|
|
4999
|
-
if (originalParentId === event.eventId) {
|
|
5000
|
-
diagnostics.push({
|
|
5001
|
-
code: "AI_LOGICAL_SELF_PARENT_REMOVED",
|
|
5002
|
-
message: formatProgrammaticDiagnostic(
|
|
5003
|
-
"AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
5004
|
-
`Removed self-parent edge on ${event.eventId}.`
|
|
5005
|
-
),
|
|
5006
|
-
eventIds: [event.eventId]
|
|
5007
|
-
});
|
|
5008
|
-
const { parentId: _drop, ...rest } = event;
|
|
5009
|
-
normalized.push({
|
|
5010
|
-
...rest,
|
|
5011
|
-
projection: {
|
|
5012
|
-
...event.projection,
|
|
5013
|
-
parentNormalized: true,
|
|
5014
|
-
originalParentId
|
|
5015
|
-
}
|
|
5016
|
-
});
|
|
5017
|
-
continue;
|
|
5018
|
-
}
|
|
5019
|
-
if (!logicalById.has(originalParentId) && !logicalByRawId.has(originalParentId)) {
|
|
5020
|
-
const mapping = event.attributes?.parentMapping;
|
|
5021
|
-
const unresolved = mapping === "unresolved" || event.attributes?.parentUnresolved === true || event.attributes?.unresolvedParent === true || // LangGraph/framework scaffolding sentinel labels are not event ids.
|
|
5022
|
-
/^LangGraph$/i.test(originalParentId) || originalParentId.startsWith("unresolved:");
|
|
5023
|
-
if (!unresolved) {
|
|
5024
|
-
diagnostics.push({
|
|
5025
|
-
code: "AI_LOGICAL_PARENT_UNRESOLVED",
|
|
5026
|
-
message: `Parent ${originalParentId} unresolved for ${event.eventId}.`,
|
|
5027
|
-
eventIds: [event.eventId]
|
|
5028
|
-
});
|
|
5029
|
-
}
|
|
5030
|
-
}
|
|
5031
|
-
normalized.push(event);
|
|
5032
|
-
continue;
|
|
5033
|
-
}
|
|
5034
|
-
if (nextParent === event.eventId) {
|
|
5035
|
-
diagnostics.push({
|
|
5036
|
-
code: "AI_LOGICAL_SELF_PARENT_REMOVED",
|
|
5037
|
-
message: formatProgrammaticDiagnostic(
|
|
5038
|
-
"AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
5039
|
-
`Removed self-parent edge on ${event.eventId} (was ${originalParentId}).`
|
|
5040
|
-
),
|
|
5041
|
-
eventIds: [event.eventId]
|
|
5042
|
-
});
|
|
5043
|
-
const { parentId: _drop, ...rest } = event;
|
|
5044
|
-
normalized.push({
|
|
5045
|
-
...rest,
|
|
5046
|
-
projection: {
|
|
5047
|
-
...event.projection,
|
|
5048
|
-
parentNormalized: true,
|
|
5049
|
-
originalParentId
|
|
5050
|
-
}
|
|
5051
|
-
});
|
|
5052
|
-
continue;
|
|
5053
|
-
}
|
|
5054
|
-
diagnostics.push({
|
|
5055
|
-
code: "AI_LOGICAL_PARENT_REMAPPED",
|
|
5056
|
-
message: `Remapped parent ${originalParentId} \u2192 ${nextParent} for ${event.eventId}.`,
|
|
5057
|
-
eventIds: [event.eventId]
|
|
5058
|
-
});
|
|
5059
|
-
normalized.push({
|
|
5060
|
-
...event,
|
|
5061
|
-
parentId: nextParent,
|
|
5062
|
-
projection: {
|
|
5063
|
-
...event.projection,
|
|
5064
|
-
parentNormalized: true,
|
|
5065
|
-
originalParentId
|
|
5066
|
-
}
|
|
5067
|
-
});
|
|
5051
|
+
const assessment = {
|
|
5052
|
+
status: parts.assessmentStatus,
|
|
5053
|
+
note: parts.note ?? EVIDENCE_ASSESSMENT_NOTE
|
|
5054
|
+
};
|
|
5055
|
+
if (parts.sourceStatus !== void 0) {
|
|
5056
|
+
assessment.sourceStatus = parts.sourceStatus;
|
|
5068
5057
|
}
|
|
5069
|
-
const rawIndex = new Map(events.map((e, i) => [e.eventId, i]));
|
|
5070
|
-
normalized.sort((a, b) => {
|
|
5071
|
-
const ai = rawIndex.get(a.sourceEventIds[0]) ?? 0;
|
|
5072
|
-
const bi = rawIndex.get(b.sourceEventIds[0]) ?? 0;
|
|
5073
|
-
return ai - bi || a.eventId.localeCompare(b.eventId);
|
|
5074
|
-
});
|
|
5075
5058
|
return {
|
|
5076
|
-
|
|
5077
|
-
|
|
5059
|
+
evidenceFormatVersion: EVIDENCE_FORMAT_VERSION,
|
|
5060
|
+
generator: {
|
|
5061
|
+
name: parts.generatorName ?? "agent-inspect",
|
|
5062
|
+
version: parts.generatorVersion
|
|
5063
|
+
},
|
|
5064
|
+
createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5065
|
+
source: {
|
|
5066
|
+
runIds,
|
|
5067
|
+
traceSchemaVersions: [...parts.traceSchemaVersions].sort((a, b) => a.localeCompare(b)),
|
|
5068
|
+
sourceHashes: [...parts.sourceHashes].sort((a, b) => a.runId.localeCompare(b.runId))
|
|
5069
|
+
},
|
|
5070
|
+
policy: {
|
|
5071
|
+
redactionProfile: parts.redactionProfile,
|
|
5072
|
+
verificationPolicy: parts.verificationPolicy ?? parts.redactionProfile
|
|
5073
|
+
},
|
|
5074
|
+
assessment,
|
|
5075
|
+
...parts.semantics !== void 0 ? { semantics: { ...parts.semantics } } : {},
|
|
5076
|
+
...parts.contract !== void 0 ? { contract: { ...parts.contract } } : {},
|
|
5077
|
+
...parts.inputs !== void 0 ? { inputs: { ...parts.inputs } } : {},
|
|
5078
|
+
files: buildEvidenceFileEntries(parts.files)
|
|
5078
5079
|
};
|
|
5079
5080
|
}
|
|
5080
|
-
function
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
if (direct) return direct;
|
|
5084
|
-
const metadata = attrs?.metadata;
|
|
5085
|
-
if (isRecord7(metadata)) {
|
|
5086
|
-
const nested = pickString(metadata, ["toolName", "tool"]);
|
|
5087
|
-
if (nested) return nested;
|
|
5081
|
+
function validateEvidenceManifest(value) {
|
|
5082
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
5083
|
+
throw new Error("Evidence manifest must be a JSON object.");
|
|
5088
5084
|
}
|
|
5089
|
-
|
|
5090
|
-
|
|
5085
|
+
const record = value;
|
|
5086
|
+
if (record.evidenceFormatVersion !== EVIDENCE_FORMAT_VERSION) {
|
|
5087
|
+
throw new Error(
|
|
5088
|
+
`Unsupported evidenceFormatVersion: ${String(record.evidenceFormatVersion)}`
|
|
5089
|
+
);
|
|
5091
5090
|
}
|
|
5092
|
-
|
|
5091
|
+
const generator = record.generator;
|
|
5092
|
+
if (generator === null || typeof generator !== "object" || Array.isArray(generator) || typeof generator.name !== "string" || typeof generator.version !== "string") {
|
|
5093
|
+
throw new Error("Evidence manifest requires generator.name and generator.version.");
|
|
5094
|
+
}
|
|
5095
|
+
const source = record.source;
|
|
5096
|
+
if (source === null || typeof source !== "object" || Array.isArray(source)) {
|
|
5097
|
+
throw new Error("Evidence manifest requires source.");
|
|
5098
|
+
}
|
|
5099
|
+
const sourceRecord = source;
|
|
5100
|
+
if (!Array.isArray(sourceRecord.runIds) || sourceRecord.runIds.length === 0) {
|
|
5101
|
+
throw new Error("Evidence manifest source.runIds must be a non-empty array.");
|
|
5102
|
+
}
|
|
5103
|
+
if (!Array.isArray(sourceRecord.traceSchemaVersions)) {
|
|
5104
|
+
throw new Error("Evidence manifest source.traceSchemaVersions must be an array.");
|
|
5105
|
+
}
|
|
5106
|
+
if (!Array.isArray(sourceRecord.sourceHashes)) {
|
|
5107
|
+
throw new Error("Evidence manifest source.sourceHashes must be an array.");
|
|
5108
|
+
}
|
|
5109
|
+
const policy = record.policy;
|
|
5110
|
+
if (policy === null || typeof policy !== "object" || Array.isArray(policy)) {
|
|
5111
|
+
throw new Error("Evidence manifest requires policy.");
|
|
5112
|
+
}
|
|
5113
|
+
const assessment = record.assessment;
|
|
5114
|
+
if (assessment === null || typeof assessment !== "object" || Array.isArray(assessment) || typeof assessment.status !== "string") {
|
|
5115
|
+
throw new Error("Evidence manifest requires assessment.status.");
|
|
5116
|
+
}
|
|
5117
|
+
if (!Array.isArray(record.files) || record.files.length === 0) {
|
|
5118
|
+
throw new Error("Evidence manifest files must be a non-empty array.");
|
|
5119
|
+
}
|
|
5120
|
+
for (const file of record.files) {
|
|
5121
|
+
if (file === null || typeof file !== "object" || Array.isArray(file)) {
|
|
5122
|
+
throw new Error("Evidence manifest file entries must be objects.");
|
|
5123
|
+
}
|
|
5124
|
+
const entry = file;
|
|
5125
|
+
if (typeof entry.path !== "string") {
|
|
5126
|
+
throw new Error("Evidence file entry requires path.");
|
|
5127
|
+
}
|
|
5128
|
+
assertEvidenceRelativePath(entry.path);
|
|
5129
|
+
if (typeof entry.sha256 !== "string" || !/^[a-f0-9]{64}$/i.test(entry.sha256)) {
|
|
5130
|
+
throw new Error(`Evidence file entry requires sha256 hex for ${entry.path}.`);
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5133
|
+
return value;
|
|
5093
5134
|
}
|
|
5094
|
-
function
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5135
|
+
function parseEvidenceManifestJson(text) {
|
|
5136
|
+
let parsed;
|
|
5137
|
+
try {
|
|
5138
|
+
parsed = JSON.parse(text);
|
|
5139
|
+
} catch (error) {
|
|
5140
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5141
|
+
throw new Error(`Evidence manifest is not valid JSON: ${message}`);
|
|
5099
5142
|
}
|
|
5100
|
-
return
|
|
5143
|
+
return validateEvidenceManifest(parsed);
|
|
5101
5144
|
}
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5145
|
+
function collectTraceSchemaVersions(jsonl) {
|
|
5146
|
+
const versions = /* @__PURE__ */ new Set();
|
|
5147
|
+
for (const line of jsonl.split(/\r?\n/)) {
|
|
5148
|
+
const trimmed = line.trim();
|
|
5149
|
+
if (trimmed === "") continue;
|
|
5150
|
+
try {
|
|
5151
|
+
const row = JSON.parse(trimmed);
|
|
5152
|
+
if (typeof row.schemaVersion === "string" && row.schemaVersion.trim() !== "") {
|
|
5153
|
+
versions.add(row.schemaVersion.trim());
|
|
5154
|
+
}
|
|
5155
|
+
} catch {
|
|
5156
|
+
}
|
|
5157
|
+
}
|
|
5158
|
+
return [...versions].sort((a, b) => a.localeCompare(b));
|
|
5159
|
+
}
|
|
5160
|
+
var init_manifest2 = __esm({
|
|
5161
|
+
"packages/core/src/evidence/manifest.ts"() {
|
|
5162
|
+
init_hash();
|
|
5163
|
+
init_paths2();
|
|
5164
|
+
init_types4();
|
|
5105
5165
|
}
|
|
5106
5166
|
});
|
|
5107
5167
|
|
|
@@ -5493,7 +5553,11 @@ function emptySummary2() {
|
|
|
5493
5553
|
failed: 0,
|
|
5494
5554
|
warnings: 0,
|
|
5495
5555
|
errors: 0,
|
|
5496
|
-
rulesEvaluated: 0
|
|
5556
|
+
rulesEvaluated: 0,
|
|
5557
|
+
rulesPassed: 0,
|
|
5558
|
+
rulesWarning: 0,
|
|
5559
|
+
rulesFailed: 0,
|
|
5560
|
+
rulesError: 0
|
|
5497
5561
|
};
|
|
5498
5562
|
}
|
|
5499
5563
|
function errorResult(input3, diagnostics, selectedRun, ruleExecutions = []) {
|
|
@@ -5505,7 +5569,11 @@ function errorResult(input3, diagnostics, selectedRun, ruleExecutions = []) {
|
|
|
5505
5569
|
summary: {
|
|
5506
5570
|
...emptySummary2(),
|
|
5507
5571
|
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
5508
|
-
rulesEvaluated: ruleExecutions.length
|
|
5572
|
+
rulesEvaluated: ruleExecutions.length,
|
|
5573
|
+
rulesPassed: ruleExecutions.filter((item) => item.status === "pass").length,
|
|
5574
|
+
rulesWarning: ruleExecutions.filter((item) => item.status === "warning").length,
|
|
5575
|
+
rulesFailed: ruleExecutions.filter((item) => item.status === "fail").length,
|
|
5576
|
+
rulesError: ruleExecutions.filter((item) => item.status === "error").length
|
|
5509
5577
|
},
|
|
5510
5578
|
findings: [],
|
|
5511
5579
|
diagnostics: [...diagnostics],
|
|
@@ -5665,7 +5733,7 @@ function normalizeFinding(rule, finding) {
|
|
|
5665
5733
|
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
5666
5734
|
};
|
|
5667
5735
|
}
|
|
5668
|
-
function summarize(findings, diagnostics,
|
|
5736
|
+
function summarize(findings, diagnostics, ruleExecutions) {
|
|
5669
5737
|
return {
|
|
5670
5738
|
passed: findings.filter((finding) => finding.status === "pass").length,
|
|
5671
5739
|
failed: findings.filter(
|
|
@@ -5675,7 +5743,11 @@ function summarize(findings, diagnostics, rulesEvaluated) {
|
|
|
5675
5743
|
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
5676
5744
|
).length,
|
|
5677
5745
|
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
5678
|
-
rulesEvaluated
|
|
5746
|
+
rulesEvaluated: ruleExecutions.length,
|
|
5747
|
+
rulesPassed: ruleExecutions.filter((item) => item.status === "pass").length,
|
|
5748
|
+
rulesWarning: ruleExecutions.filter((item) => item.status === "warning").length,
|
|
5749
|
+
rulesFailed: ruleExecutions.filter((item) => item.status === "fail").length,
|
|
5750
|
+
rulesError: ruleExecutions.filter((item) => item.status === "error").length
|
|
5679
5751
|
};
|
|
5680
5752
|
}
|
|
5681
5753
|
function classifyRuleExecution(findings, threw) {
|
|
@@ -7341,7 +7413,7 @@ function runTraceChecks(input3, options = {}) {
|
|
|
7341
7413
|
}
|
|
7342
7414
|
const eventById = new Map(input3.read.events.map((event) => [event.eventId, event]));
|
|
7343
7415
|
const sortedFindings = findings.sort(compareFindings(eventById));
|
|
7344
|
-
const summary = summarize(sortedFindings, diagnostics, ruleExecutions
|
|
7416
|
+
const summary = summarize(sortedFindings, diagnostics, ruleExecutions);
|
|
7345
7417
|
const status = summary.failed > 0 ? "fail" : "pass";
|
|
7346
7418
|
return {
|
|
7347
7419
|
ok: status === "pass",
|
|
@@ -9480,7 +9552,17 @@ function emptyPassResult(input3, runId) {
|
|
|
9480
9552
|
status: "pass",
|
|
9481
9553
|
format: input3.read.format,
|
|
9482
9554
|
...runId !== void 0 ? { runId } : {},
|
|
9483
|
-
summary: {
|
|
9555
|
+
summary: {
|
|
9556
|
+
passed: 0,
|
|
9557
|
+
failed: 0,
|
|
9558
|
+
warnings: 0,
|
|
9559
|
+
errors: 0,
|
|
9560
|
+
rulesEvaluated: 0,
|
|
9561
|
+
rulesPassed: 0,
|
|
9562
|
+
rulesWarning: 0,
|
|
9563
|
+
rulesFailed: 0,
|
|
9564
|
+
rulesError: 0
|
|
9565
|
+
},
|
|
9484
9566
|
findings: [],
|
|
9485
9567
|
diagnostics: [],
|
|
9486
9568
|
ruleExecutions: []
|
|
@@ -9508,7 +9590,11 @@ function mergeContractResults(input3, parts, extraFindings = []) {
|
|
|
9508
9590
|
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
9509
9591
|
).length,
|
|
9510
9592
|
errors,
|
|
9511
|
-
rulesEvaluated: ruleExecutions.length
|
|
9593
|
+
rulesEvaluated: ruleExecutions.length,
|
|
9594
|
+
rulesPassed: ruleExecutions.filter((item) => item.status === "pass").length,
|
|
9595
|
+
rulesWarning: ruleExecutions.filter((item) => item.status === "warning").length,
|
|
9596
|
+
rulesFailed: ruleExecutions.filter((item) => item.status === "fail").length,
|
|
9597
|
+
rulesError: ruleExecutions.filter((item) => item.status === "error").length
|
|
9512
9598
|
},
|
|
9513
9599
|
findings,
|
|
9514
9600
|
diagnostics,
|
|
@@ -9556,7 +9642,76 @@ function defineTraceContract(input3) {
|
|
|
9556
9642
|
...alternatives ? { alternatives } : {}
|
|
9557
9643
|
};
|
|
9558
9644
|
}
|
|
9645
|
+
function isTraceReadLike(value) {
|
|
9646
|
+
if (typeof value !== "object" || value === null) return false;
|
|
9647
|
+
const candidate = value;
|
|
9648
|
+
return typeof candidate.format === "string" && Array.isArray(candidate.runs) && Array.isArray(candidate.events);
|
|
9649
|
+
}
|
|
9650
|
+
function isTraceCheckInputLike(value) {
|
|
9651
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
9652
|
+
return false;
|
|
9653
|
+
}
|
|
9654
|
+
return isTraceReadLike(value.read);
|
|
9655
|
+
}
|
|
9656
|
+
function looksLikeTraceContract(value) {
|
|
9657
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
9658
|
+
return false;
|
|
9659
|
+
}
|
|
9660
|
+
if (isTraceReadLike(value) || isTraceCheckInputLike(value)) {
|
|
9661
|
+
return false;
|
|
9662
|
+
}
|
|
9663
|
+
return true;
|
|
9664
|
+
}
|
|
9665
|
+
function invalidContractInputResult() {
|
|
9666
|
+
return {
|
|
9667
|
+
ok: false,
|
|
9668
|
+
status: "error",
|
|
9669
|
+
format: "unknown",
|
|
9670
|
+
summary: {
|
|
9671
|
+
passed: 0,
|
|
9672
|
+
failed: 0,
|
|
9673
|
+
warnings: 0,
|
|
9674
|
+
errors: 1,
|
|
9675
|
+
rulesEvaluated: 0,
|
|
9676
|
+
rulesPassed: 0,
|
|
9677
|
+
rulesWarning: 0,
|
|
9678
|
+
rulesFailed: 0,
|
|
9679
|
+
rulesError: 0
|
|
9680
|
+
},
|
|
9681
|
+
findings: [],
|
|
9682
|
+
diagnostics: [
|
|
9683
|
+
{
|
|
9684
|
+
code: "AI_CHECK_INVALID_ARGUMENTS",
|
|
9685
|
+
message: formatProgrammaticDiagnostic("AI_TRACE_CONTRACT_INPUT_INVALID"),
|
|
9686
|
+
severity: "error"
|
|
9687
|
+
}
|
|
9688
|
+
],
|
|
9689
|
+
ruleExecutions: []
|
|
9690
|
+
};
|
|
9691
|
+
}
|
|
9692
|
+
function validateEvaluateTraceContractArgs(input3, contract) {
|
|
9693
|
+
if (isTraceCheckInputLike(input3) && looksLikeTraceContract(contract)) {
|
|
9694
|
+
return void 0;
|
|
9695
|
+
}
|
|
9696
|
+
if (looksLikeTraceContract(input3) && (isTraceCheckInputLike(contract) || isTraceReadLike(contract))) {
|
|
9697
|
+
return invalidContractInputResult();
|
|
9698
|
+
}
|
|
9699
|
+
if (isTraceReadLike(input3)) {
|
|
9700
|
+
return invalidContractInputResult();
|
|
9701
|
+
}
|
|
9702
|
+
if (!isTraceCheckInputLike(input3)) {
|
|
9703
|
+
return invalidContractInputResult();
|
|
9704
|
+
}
|
|
9705
|
+
if (!looksLikeTraceContract(contract)) {
|
|
9706
|
+
return invalidContractInputResult();
|
|
9707
|
+
}
|
|
9708
|
+
return void 0;
|
|
9709
|
+
}
|
|
9559
9710
|
function evaluateTraceContract(input3, contract, options = {}) {
|
|
9711
|
+
const inputGuard = validateEvaluateTraceContractArgs(input3, contract);
|
|
9712
|
+
if (inputGuard !== void 0) {
|
|
9713
|
+
return inputGuard;
|
|
9714
|
+
}
|
|
9560
9715
|
const shapeErrors = [
|
|
9561
9716
|
...validateAllowedStatusesShape(contract.run?.allowedStatuses),
|
|
9562
9717
|
...validateAlternativesShape(contract.alternatives),
|
|
@@ -9574,7 +9729,11 @@ function evaluateTraceContract(input3, contract, options = {}) {
|
|
|
9574
9729
|
failed: 0,
|
|
9575
9730
|
warnings: 0,
|
|
9576
9731
|
errors: shapeErrors.length,
|
|
9577
|
-
rulesEvaluated: 0
|
|
9732
|
+
rulesEvaluated: 0,
|
|
9733
|
+
rulesPassed: 0,
|
|
9734
|
+
rulesWarning: 0,
|
|
9735
|
+
rulesFailed: 0,
|
|
9736
|
+
rulesError: 0
|
|
9578
9737
|
},
|
|
9579
9738
|
findings: [],
|
|
9580
9739
|
diagnostics: shapeErrors.map((item) => ({
|
|
@@ -9599,7 +9758,11 @@ function evaluateTraceContract(input3, contract, options = {}) {
|
|
|
9599
9758
|
failed: 0,
|
|
9600
9759
|
warnings: 0,
|
|
9601
9760
|
errors: scoped.diagnostics.length,
|
|
9602
|
-
rulesEvaluated: 0
|
|
9761
|
+
rulesEvaluated: 0,
|
|
9762
|
+
rulesPassed: 0,
|
|
9763
|
+
rulesWarning: 0,
|
|
9764
|
+
rulesFailed: 0,
|
|
9765
|
+
rulesError: 0
|
|
9603
9766
|
},
|
|
9604
9767
|
findings: [],
|
|
9605
9768
|
diagnostics: scoped.diagnostics,
|
|
@@ -9705,6 +9868,7 @@ var init_contract = __esm({
|
|
|
9705
9868
|
init_retry_safety();
|
|
9706
9869
|
init_tool_arguments();
|
|
9707
9870
|
init_outcomes();
|
|
9871
|
+
init_programmatic();
|
|
9708
9872
|
init_contract_scope();
|
|
9709
9873
|
init_tool_arguments();
|
|
9710
9874
|
ALLOWED_STATUS_ALIASES = {
|
|
@@ -11758,6 +11922,28 @@ function asPositiveNumber(value, label) {
|
|
|
11758
11922
|
}
|
|
11759
11923
|
return value;
|
|
11760
11924
|
}
|
|
11925
|
+
function asExpectConfig(value, label) {
|
|
11926
|
+
if (value === void 0) return void 0;
|
|
11927
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
11928
|
+
throw new Error(`${label} must be an object.`);
|
|
11929
|
+
}
|
|
11930
|
+
const raw = value;
|
|
11931
|
+
const checkStatus = raw.checkStatus;
|
|
11932
|
+
if (checkStatus !== "pass" && checkStatus !== "fail" && checkStatus !== "error") {
|
|
11933
|
+
throw new Error(`${label}.checkStatus must be "pass", "fail", or "error".`);
|
|
11934
|
+
}
|
|
11935
|
+
const findingRuleIds = asStringArray(raw.findingRuleIds, `${label}.findingRuleIds`);
|
|
11936
|
+
const minAssertions = asPositiveNumber(raw.minAssertions, `${label}.minAssertions`);
|
|
11937
|
+
const rejectUnexpectedFindings = raw.rejectUnexpectedFindings === void 0 ? void 0 : typeof raw.rejectUnexpectedFindings === "boolean" ? raw.rejectUnexpectedFindings : (() => {
|
|
11938
|
+
throw new Error(`${label}.rejectUnexpectedFindings must be a boolean.`);
|
|
11939
|
+
})();
|
|
11940
|
+
return {
|
|
11941
|
+
checkStatus,
|
|
11942
|
+
...findingRuleIds !== void 0 ? { findingRuleIds } : {},
|
|
11943
|
+
...rejectUnexpectedFindings !== void 0 ? { rejectUnexpectedFindings } : {},
|
|
11944
|
+
...minAssertions !== void 0 ? { minAssertions } : {}
|
|
11945
|
+
};
|
|
11946
|
+
}
|
|
11761
11947
|
function validateCaseConfig(value, index) {
|
|
11762
11948
|
const diagnostics = [];
|
|
11763
11949
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
@@ -11800,7 +11986,8 @@ function validateCaseConfig(value, index) {
|
|
|
11800
11986
|
raw.expectedObservations,
|
|
11801
11987
|
`cases[${index}].expectedObservations`
|
|
11802
11988
|
)
|
|
11803
|
-
} : {}
|
|
11989
|
+
} : {},
|
|
11990
|
+
...asExpectConfig(raw.expect, `cases[${index}].expect`) !== void 0 ? { expect: asExpectConfig(raw.expect, `cases[${index}].expect`) } : {}
|
|
11804
11991
|
},
|
|
11805
11992
|
diagnostics
|
|
11806
11993
|
};
|
|
@@ -14480,6 +14667,78 @@ function validateExpectedObservations(suiteCase, read) {
|
|
|
14480
14667
|
}
|
|
14481
14668
|
return { ok: diagnostics.length === 0, diagnostics };
|
|
14482
14669
|
}
|
|
14670
|
+
function evaluateCaseExpect(suiteCase, checkResult, observationCount) {
|
|
14671
|
+
const expectation = suiteCase.expect;
|
|
14672
|
+
if (expectation === void 0) {
|
|
14673
|
+
return { ok: true, diagnostics: [] };
|
|
14674
|
+
}
|
|
14675
|
+
const diagnostics = [];
|
|
14676
|
+
if (checkResult.status !== expectation.checkStatus) {
|
|
14677
|
+
diagnostics.push(
|
|
14678
|
+
diagnostic5(
|
|
14679
|
+
"AI_SUITE_EXPECT_MISMATCH",
|
|
14680
|
+
`Expected check status "${expectation.checkStatus}", got "${checkResult.status}".`,
|
|
14681
|
+
"error",
|
|
14682
|
+
suiteCase.id
|
|
14683
|
+
)
|
|
14684
|
+
);
|
|
14685
|
+
}
|
|
14686
|
+
const failedFindings = checkResult.findings.filter(
|
|
14687
|
+
(finding) => finding.status === "fail" && finding.severity === "error"
|
|
14688
|
+
);
|
|
14689
|
+
const requiredIds = expectation.findingRuleIds ?? [];
|
|
14690
|
+
for (const ruleId of requiredIds) {
|
|
14691
|
+
if (!failedFindings.some((finding) => finding.ruleId === ruleId)) {
|
|
14692
|
+
diagnostics.push(
|
|
14693
|
+
diagnostic5(
|
|
14694
|
+
"AI_SUITE_EXPECT_MISMATCH",
|
|
14695
|
+
`Expected failing finding for rule "${ruleId}" was not present.`,
|
|
14696
|
+
"error",
|
|
14697
|
+
suiteCase.id
|
|
14698
|
+
)
|
|
14699
|
+
);
|
|
14700
|
+
}
|
|
14701
|
+
}
|
|
14702
|
+
const rejectUnexpected = expectation.rejectUnexpectedFindings !== false;
|
|
14703
|
+
if (rejectUnexpected && requiredIds.length > 0) {
|
|
14704
|
+
for (const finding of failedFindings) {
|
|
14705
|
+
if (!requiredIds.includes(finding.ruleId)) {
|
|
14706
|
+
diagnostics.push(
|
|
14707
|
+
diagnostic5(
|
|
14708
|
+
"AI_SUITE_EXPECT_MISMATCH",
|
|
14709
|
+
`Unexpected failing finding for rule "${finding.ruleId}".`,
|
|
14710
|
+
"error",
|
|
14711
|
+
suiteCase.id
|
|
14712
|
+
)
|
|
14713
|
+
);
|
|
14714
|
+
}
|
|
14715
|
+
}
|
|
14716
|
+
}
|
|
14717
|
+
if (expectation.minAssertions !== void 0) {
|
|
14718
|
+
const assertionCount = checkResult.ruleExecutions.length + observationCount;
|
|
14719
|
+
if (assertionCount < expectation.minAssertions) {
|
|
14720
|
+
diagnostics.push(
|
|
14721
|
+
diagnostic5(
|
|
14722
|
+
"AI_SUITE_EXPECT_MISMATCH",
|
|
14723
|
+
`Expected at least ${expectation.minAssertions} assertion(s), got ${assertionCount}.`,
|
|
14724
|
+
"error",
|
|
14725
|
+
suiteCase.id
|
|
14726
|
+
)
|
|
14727
|
+
);
|
|
14728
|
+
}
|
|
14729
|
+
}
|
|
14730
|
+
if (expectation.checkStatus === "fail" && checkResult.diagnostics.some((item) => item.severity === "error") && checkResult.status === "error") {
|
|
14731
|
+
diagnostics.push(
|
|
14732
|
+
diagnostic5(
|
|
14733
|
+
"AI_SUITE_EXPECT_MISMATCH",
|
|
14734
|
+
'Check ended in evaluator/config error; semantic failure expectation requires status "fail".',
|
|
14735
|
+
"error",
|
|
14736
|
+
suiteCase.id
|
|
14737
|
+
)
|
|
14738
|
+
);
|
|
14739
|
+
}
|
|
14740
|
+
return { ok: diagnostics.length === 0, diagnostics };
|
|
14741
|
+
}
|
|
14483
14742
|
async function runSuiteCase(suiteCase, config, options) {
|
|
14484
14743
|
const resolved = await resolveSuiteCaseTrace(suiteCase, options);
|
|
14485
14744
|
if (resolved.missing || resolved.tracePath === void 0) {
|
|
@@ -14532,9 +14791,21 @@ async function runSuiteCase(suiteCase, config, options) {
|
|
|
14532
14791
|
status: "pass",
|
|
14533
14792
|
format: read.format,
|
|
14534
14793
|
findings: [],
|
|
14535
|
-
diagnostics: []
|
|
14794
|
+
diagnostics: [],
|
|
14795
|
+
ruleExecutions: []
|
|
14796
|
+
};
|
|
14536
14797
|
const observationResult = validateExpectedObservations(suiteCase, read);
|
|
14537
|
-
const
|
|
14798
|
+
const checkOk = checkResult.ok;
|
|
14799
|
+
const observationsOk = observationResult.ok;
|
|
14800
|
+
const evalOk = config.eval?.requireSuccess === true ? checkResult.findings.every(
|
|
14801
|
+
(finding) => finding.ruleId !== "run.status" || finding.status !== "fail"
|
|
14802
|
+
) && checkResult.diagnostics.every((item) => item.severity !== "error") : void 0;
|
|
14803
|
+
const expectResult = evaluateCaseExpect(
|
|
14804
|
+
suiteCase,
|
|
14805
|
+
checkResult,
|
|
14806
|
+
compiled.observationCount
|
|
14807
|
+
);
|
|
14808
|
+
const checkFailureDiagnostics = [
|
|
14538
14809
|
...checkResult.diagnostics.map(
|
|
14539
14810
|
(item) => diagnostic5(
|
|
14540
14811
|
"AI_SUITE_CASE_CHECK_FAILED",
|
|
@@ -14550,15 +14821,24 @@ async function runSuiteCase(suiteCase, config, options) {
|
|
|
14550
14821
|
finding.severity,
|
|
14551
14822
|
suiteCase.id
|
|
14552
14823
|
)
|
|
14553
|
-
)
|
|
14554
|
-
...observationResult.diagnostics
|
|
14824
|
+
)
|
|
14555
14825
|
];
|
|
14556
|
-
const
|
|
14557
|
-
const
|
|
14558
|
-
|
|
14559
|
-
|
|
14560
|
-
|
|
14561
|
-
|
|
14826
|
+
const expectMatched = suiteCase.expect !== void 0 && expectResult.ok && observationsOk;
|
|
14827
|
+
const diagnostics = expectMatched ? [
|
|
14828
|
+
...observationResult.diagnostics,
|
|
14829
|
+
...expectResult.diagnostics,
|
|
14830
|
+
diagnostic5(
|
|
14831
|
+
"AI_SUITE_CASE_CHECK_FAILED",
|
|
14832
|
+
`Expected semantic failure matched (check.status=${checkResult.status}).`,
|
|
14833
|
+
"info",
|
|
14834
|
+
suiteCase.id
|
|
14835
|
+
)
|
|
14836
|
+
] : [
|
|
14837
|
+
...checkFailureDiagnostics,
|
|
14838
|
+
...observationResult.diagnostics,
|
|
14839
|
+
...expectResult.diagnostics
|
|
14840
|
+
];
|
|
14841
|
+
const ok = suiteCase.expect !== void 0 ? expectResult.ok && observationsOk : checkOk && observationsOk;
|
|
14562
14842
|
const status = ok ? "pass" : checkResult.status === "error" ? "error" : "fail";
|
|
14563
14843
|
return {
|
|
14564
14844
|
id: suiteCase.id,
|
|
@@ -15858,6 +16138,7 @@ var init_inspect_run = __esm({
|
|
|
15858
16138
|
"packages/core/src/inspect-run.ts"() {
|
|
15859
16139
|
init_correlation_metadata();
|
|
15860
16140
|
init_context();
|
|
16141
|
+
init_programmatic();
|
|
15861
16142
|
init_storage();
|
|
15862
16143
|
init_terminal();
|
|
15863
16144
|
init_trace_directory();
|
|
@@ -16375,7 +16656,7 @@ var init_src = __esm({
|
|
|
16375
16656
|
});
|
|
16376
16657
|
|
|
16377
16658
|
// package.json
|
|
16378
|
-
var version = "6.31.
|
|
16659
|
+
var version = "6.31.11";
|
|
16379
16660
|
|
|
16380
16661
|
// packages/cli/src/list.ts
|
|
16381
16662
|
init_advanced();
|
|
@@ -21240,6 +21521,15 @@ function actionForRule(rule) {
|
|
|
21240
21521
|
if (rule.strategy === "full") return "replace";
|
|
21241
21522
|
return rule.strategy;
|
|
21242
21523
|
}
|
|
21524
|
+
function isCompleteRedactionMarker(value, replacement = "[REDACTED]") {
|
|
21525
|
+
if (typeof value !== "string") return false;
|
|
21526
|
+
const trimmed = value.trim();
|
|
21527
|
+
if (trimmed.length === 0) return false;
|
|
21528
|
+
if (trimmed === replacement) return true;
|
|
21529
|
+
if (/^\[HASH:[A-Za-z0-9_-]+\]$/.test(trimmed)) return true;
|
|
21530
|
+
if (/^\[REDACTED:[^\]]*\]$/.test(trimmed)) return true;
|
|
21531
|
+
return false;
|
|
21532
|
+
}
|
|
21243
21533
|
function applyRule(rule, value, replacement) {
|
|
21244
21534
|
if (rule.strategy === "full") return replacement;
|
|
21245
21535
|
const asString2 = stringifyScalar(value);
|
|
@@ -21354,6 +21644,9 @@ var Redactor2 = class {
|
|
|
21354
21644
|
if (key !== void 0) {
|
|
21355
21645
|
const rule = findCompiledKeyRule(key, this.#rules);
|
|
21356
21646
|
if (rule) {
|
|
21647
|
+
if (isCompleteRedactionMarker(value, this.#replacement)) {
|
|
21648
|
+
return value;
|
|
21649
|
+
}
|
|
21357
21650
|
this.#recordFinding(
|
|
21358
21651
|
state,
|
|
21359
21652
|
makeFinding(path44, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
@@ -22416,7 +22709,8 @@ async function explainCommand(target, options = {}, stdin = process.stdin) {
|
|
|
22416
22709
|
const mode = options.dryRun ? "dry-run" : "local";
|
|
22417
22710
|
const explanation = buildLocalExplanation(selected, {
|
|
22418
22711
|
mode,
|
|
22419
|
-
redactionProfile
|
|
22712
|
+
redactionProfile,
|
|
22713
|
+
events: read.events
|
|
22420
22714
|
});
|
|
22421
22715
|
if (options.json) {
|
|
22422
22716
|
writeJson({
|
|
@@ -23733,7 +24027,11 @@ function mergeSafetyExtensions(result, read, options) {
|
|
|
23733
24027
|
failed,
|
|
23734
24028
|
warnings,
|
|
23735
24029
|
errors,
|
|
23736
|
-
rulesEvaluated: ruleExecutions.length
|
|
24030
|
+
rulesEvaluated: ruleExecutions.length,
|
|
24031
|
+
rulesPassed: ruleExecutions.filter((item) => item.status === "pass").length,
|
|
24032
|
+
rulesWarning: ruleExecutions.filter((item) => item.status === "warning").length,
|
|
24033
|
+
rulesFailed: ruleExecutions.filter((item) => item.status === "fail").length,
|
|
24034
|
+
rulesError: ruleExecutions.filter((item) => item.status === "error").length
|
|
23737
24035
|
},
|
|
23738
24036
|
findings,
|
|
23739
24037
|
ruleExecutions
|
|
@@ -23885,7 +24183,11 @@ function errorResult2(code, message, format = "unknown") {
|
|
|
23885
24183
|
failed: 0,
|
|
23886
24184
|
warnings: 0,
|
|
23887
24185
|
errors: 1,
|
|
23888
|
-
rulesEvaluated: 0
|
|
24186
|
+
rulesEvaluated: 0,
|
|
24187
|
+
rulesPassed: 0,
|
|
24188
|
+
rulesWarning: 0,
|
|
24189
|
+
rulesFailed: 0,
|
|
24190
|
+
rulesError: 0
|
|
23889
24191
|
},
|
|
23890
24192
|
findings: [],
|
|
23891
24193
|
diagnostics,
|
|
@@ -25289,9 +25591,11 @@ function printHuman2(result, options = {}) {
|
|
|
25289
25591
|
console.log(`Format: ${result.format}`);
|
|
25290
25592
|
if (result.runId !== void 0) console.log(`Run: ${result.runId}`);
|
|
25291
25593
|
console.log(
|
|
25292
|
-
`
|
|
25594
|
+
`Findings: ${result.summary.failed} failed, ${result.summary.warnings} warning(s), ${result.summary.errors} error(s), ${result.summary.passed} passed`
|
|
25595
|
+
);
|
|
25596
|
+
console.log(
|
|
25597
|
+
`Rules: ${result.summary.rulesEvaluated ?? result.ruleExecutions?.length ?? 0} evaluated (${result.summary.rulesPassed ?? 0} passed, ${result.summary.rulesWarning ?? 0} warning, ${result.summary.rulesFailed ?? 0} failed, ${result.summary.rulesError ?? 0} error)`
|
|
25293
25598
|
);
|
|
25294
|
-
console.log(`Rules evaluated: ${result.summary.rulesEvaluated ?? result.ruleExecutions?.length ?? 0}`);
|
|
25295
25599
|
for (const diagnostic8 of result.diagnostics) {
|
|
25296
25600
|
console.log(`- ${diagnostic8.code}: ${diagnostic8.message}`);
|
|
25297
25601
|
}
|