agent-inspect 6.31.10 → 6.31.12
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-JCKC4ZRV.mjs → chunk-GBAGZVUX.mjs} +2268 -1963
- package/packages/cli/dist/chunk-GBAGZVUX.mjs.map +1 -0
- package/packages/cli/dist/index.cjs +2277 -1947
- 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-4UD7JNDR.mjs → src-F44EQY4S.mjs} +3 -3
- package/packages/cli/dist/{src-4UD7JNDR.mjs.map → src-F44EQY4S.mjs.map} +1 -1
- package/packages/core/dist/advanced.cjs +2179 -1900
- 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 +303 -36
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.cjs +48 -10
- 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-CPQAOJW4.mjs → chunk-CCYKNAOD.mjs} +50 -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/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/cli/dist/chunk-JCKC4ZRV.mjs.map +0 -1
- package/packages/core/dist/chunk-CPQAOJW4.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,2116 +3003,2165 @@ 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
|
-
);
|
|
3094
|
-
}
|
|
3095
|
-
facts.push(...attributeFacts(nodes, redactor));
|
|
3096
|
-
return facts;
|
|
3097
|
-
}
|
|
3098
|
-
function buildInferences(run, facts) {
|
|
3099
|
-
const inferences = [];
|
|
3100
|
-
const errorFact = facts.find((item) => item.id === "run.errorNodeCount");
|
|
3101
|
-
const kindFact = facts.find((item) => item.id === "run.kinds");
|
|
3102
|
-
const durationFact = facts.find((item) => item.id === "run.durationMs");
|
|
3103
|
-
const errorNodeCount = typeof errorFact?.value === "number" ? errorFact.value : 0;
|
|
3104
|
-
if (run.status === "error" || errorNodeCount > 0) {
|
|
3105
|
-
inferences.push({
|
|
3106
|
-
id: "outcome.error",
|
|
3107
|
-
label: "Outcome",
|
|
3108
|
-
text: "The run recorded an error status or at least one error node.",
|
|
3109
|
-
evidence: ["run.status", "run.errorNodeCount"],
|
|
3110
|
-
confidence: "deterministic"
|
|
3111
|
-
});
|
|
3112
|
-
} else if (run.status === "ok") {
|
|
3113
|
-
inferences.push({
|
|
3114
|
-
id: "outcome.success",
|
|
3115
|
-
label: "Outcome",
|
|
3116
|
-
text: "The run completed without observed error nodes.",
|
|
3117
|
-
evidence: ["run.status", "run.errorNodeCount"],
|
|
3118
|
-
confidence: "deterministic"
|
|
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
|
-
});
|
|
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;
|
|
3138
3106
|
}
|
|
3139
|
-
return
|
|
3107
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
3140
3108
|
}
|
|
3141
|
-
function
|
|
3142
|
-
const
|
|
3143
|
-
const
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
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
|
+
};
|
|
3147
3121
|
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
|
-
]
|
|
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
|
+
}
|
|
3159
3130
|
};
|
|
3160
3131
|
}
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
if (!m) {
|
|
3173
|
-
throw new Error(
|
|
3174
|
-
`Invalid --duration "${expr}". Use forms like >5s, >=500ms, <2m.`
|
|
3175
|
-
);
|
|
3176
|
-
}
|
|
3177
|
-
const op = m[1];
|
|
3178
|
-
const ms = parseDuration(m[2].trim());
|
|
3179
|
-
return { op, ms };
|
|
3180
|
-
}
|
|
3181
|
-
function durationMatches(valueMs, filter) {
|
|
3182
|
-
if (valueMs === void 0 || !Number.isFinite(valueMs)) return false;
|
|
3183
|
-
switch (filter.op) {
|
|
3184
|
-
case ">":
|
|
3185
|
-
return valueMs > filter.ms;
|
|
3186
|
-
case ">=":
|
|
3187
|
-
return valueMs >= filter.ms;
|
|
3188
|
-
case "<":
|
|
3189
|
-
return valueMs < filter.ms;
|
|
3190
|
-
case "<=":
|
|
3191
|
-
return valueMs <= filter.ms;
|
|
3192
|
-
default:
|
|
3193
|
-
return false;
|
|
3194
|
-
}
|
|
3195
|
-
}
|
|
3196
|
-
function normalizeStepTypeFilter(kind, type) {
|
|
3197
|
-
const v = (kind ?? type)?.trim().toLowerCase();
|
|
3198
|
-
return v && v !== "" ? v : void 0;
|
|
3199
|
-
}
|
|
3200
|
-
function nameMatches(hay, needle) {
|
|
3201
|
-
return hay.toLowerCase().includes(needle.toLowerCase());
|
|
3132
|
+
function asLogical(event, extras) {
|
|
3133
|
+
return {
|
|
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
|
+
}
|
|
3142
|
+
};
|
|
3202
3143
|
}
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
const
|
|
3206
|
-
const
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
durationFilter = parseDurationFilter(options.duration);
|
|
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);
|
|
3211
3151
|
}
|
|
3212
|
-
const
|
|
3213
|
-
const
|
|
3214
|
-
const
|
|
3215
|
-
const
|
|
3216
|
-
|
|
3217
|
-
)
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
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]
|
|
3232
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
|
+
}
|
|
3233
3246
|
}
|
|
3234
|
-
results.sort((a, b) => {
|
|
3235
|
-
const ta = a.timestamp ?? 0;
|
|
3236
|
-
const tb = b.timestamp ?? 0;
|
|
3237
|
-
if (ta !== tb) return tb - ta;
|
|
3238
|
-
return a.runId.localeCompare(b.runId);
|
|
3239
|
-
});
|
|
3240
|
-
return results.slice(0, limit);
|
|
3241
3247
|
}
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
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);
|
|
3248
3254
|
continue;
|
|
3249
3255
|
}
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
nameQuery,
|
|
3262
|
-
toolQuery,
|
|
3263
|
-
durationFilter,
|
|
3264
|
-
statusFilter: options.status
|
|
3265
|
-
});
|
|
3266
|
-
results.push(...stepMatches);
|
|
3267
|
-
if (observationStatus2) {
|
|
3268
|
-
const outcomes = extractOutcomesFromTraceEvents(events);
|
|
3269
|
-
const matched = outcomes.filter((outcome) => outcome.status === observationStatus2);
|
|
3270
|
-
for (const outcome of matched) {
|
|
3271
|
-
results.push({
|
|
3272
|
-
runId: m.runId,
|
|
3273
|
-
runName: m.name,
|
|
3274
|
-
runStatus: m.status,
|
|
3275
|
-
stepName: outcome.name,
|
|
3276
|
-
timestamp: outcome.observedAt,
|
|
3277
|
-
matchReason: `observation status=${outcome.status}`,
|
|
3278
|
-
matchedFields: ["outcome.status", "outcome.name"],
|
|
3279
|
-
filePath: m.filePath
|
|
3280
|
-
});
|
|
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;
|
|
3281
3267
|
}
|
|
3282
3268
|
}
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
}
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
filePath: m.filePath
|
|
3318
|
-
}
|
|
3319
|
-
];
|
|
3320
|
-
}
|
|
3321
|
-
function matchStepLevel(m, events, opts) {
|
|
3322
|
-
const out = [];
|
|
3323
|
-
const started = /* @__PURE__ */ new Map();
|
|
3324
|
-
for (const e of events) {
|
|
3325
|
-
if (e.event === "step_started") {
|
|
3326
|
-
started.set(e.stepId, e);
|
|
3327
|
-
}
|
|
3328
|
-
}
|
|
3329
|
-
for (const e of events) {
|
|
3330
|
-
if (e.event !== "step_completed") continue;
|
|
3331
|
-
const c = e;
|
|
3332
|
-
const s = started.get(c.stepId);
|
|
3333
|
-
if (!s) continue;
|
|
3334
|
-
const fields = [];
|
|
3335
|
-
const stepType = s.type;
|
|
3336
|
-
if (opts.stepTypeFilter && stepType !== opts.stepTypeFilter) {
|
|
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);
|
|
3337
3303
|
continue;
|
|
3338
3304
|
}
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
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
|
+
});
|
|
3346
3323
|
continue;
|
|
3347
3324
|
}
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
if (opts.stepTypeFilter) {
|
|
3362
|
-
fields.push("step.type");
|
|
3363
|
-
}
|
|
3364
|
-
if (fields.length === 0) continue;
|
|
3365
|
-
out.push({
|
|
3366
|
-
runId: m.runId,
|
|
3367
|
-
runName: m.name,
|
|
3368
|
-
runStatus: m.status,
|
|
3369
|
-
stepId: c.stepId,
|
|
3370
|
-
stepName: s.name,
|
|
3371
|
-
stepType,
|
|
3372
|
-
timestamp: s.startTime ?? s.timestamp,
|
|
3373
|
-
durationMs: c.durationMs,
|
|
3374
|
-
matchReason: `step match: ${fields.join(", ")}`,
|
|
3375
|
-
matchedFields: fields,
|
|
3376
|
-
filePath: m.filePath
|
|
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
|
+
}
|
|
3377
3338
|
});
|
|
3378
3339
|
}
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
}
|
|
3390
|
-
}
|
|
3391
|
-
return metas;
|
|
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
|
+
};
|
|
3392
3350
|
}
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
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;
|
|
3400
3359
|
}
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
// packages/core/src/sessions/metadata.ts
|
|
3404
|
-
function isNonEmptyString3(value) {
|
|
3405
|
-
return typeof value === "string" && value.trim() !== "";
|
|
3406
|
-
}
|
|
3407
|
-
function finitePositiveInt(value) {
|
|
3408
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 1) {
|
|
3409
|
-
return void 0;
|
|
3360
|
+
for (const prefix of ["tool:", "function:", "mcp-tools:"]) {
|
|
3361
|
+
if (event.name.startsWith(prefix)) return event.name.slice(prefix.length);
|
|
3410
3362
|
}
|
|
3411
|
-
return
|
|
3363
|
+
return event.name;
|
|
3412
3364
|
}
|
|
3413
|
-
function
|
|
3365
|
+
function pickString(record, keys) {
|
|
3414
3366
|
if (!record) return void 0;
|
|
3415
|
-
const
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
if (isNonEmptyString3(value)) {
|
|
3419
|
-
out[key] = value.trim();
|
|
3420
|
-
found = true;
|
|
3421
|
-
}
|
|
3422
|
-
};
|
|
3423
|
-
assignString("sessionId", record.sessionId);
|
|
3424
|
-
assignString("conversationId", record.conversationId);
|
|
3425
|
-
assignString("groupId", record.groupId);
|
|
3426
|
-
assignString("parentGroupId", record.parentGroupId);
|
|
3427
|
-
assignString("retryOf", record.retryOf);
|
|
3428
|
-
assignString("retryReason", record.retryReason);
|
|
3429
|
-
assignString("handoffFrom", record.handoffFrom);
|
|
3430
|
-
assignString("handoffTo", record.handoffTo);
|
|
3431
|
-
assignString("subAgentId", record.subAgentId);
|
|
3432
|
-
assignString("subAgentName", record.subAgentName);
|
|
3433
|
-
assignString("jobId", record.jobId);
|
|
3434
|
-
assignString("queueName", record.queueName);
|
|
3435
|
-
assignString("workflowName", record.workflowName);
|
|
3436
|
-
assignString("workflowStep", record.workflowStep);
|
|
3437
|
-
assignString("toolCallId", record.toolCallId);
|
|
3438
|
-
assignString("mcpToolCallId", record.mcpToolCallId);
|
|
3439
|
-
assignString("linkedStepId", record.linkedStepId);
|
|
3440
|
-
assignString("operationId", record.operationId);
|
|
3441
|
-
assignString("attemptId", record.attemptId);
|
|
3442
|
-
assignString("fallbackOf", record.fallbackOf);
|
|
3443
|
-
assignString("idempotencyKey", record.idempotencyKey);
|
|
3444
|
-
assignString("correlationId", record.correlationId);
|
|
3445
|
-
assignString("requestId", record.requestId);
|
|
3446
|
-
assignString("decisionId", record.decisionId);
|
|
3447
|
-
const attempt = finitePositiveInt(record.attempt);
|
|
3448
|
-
if (attempt !== void 0) {
|
|
3449
|
-
out.attempt = attempt;
|
|
3450
|
-
found = true;
|
|
3451
|
-
}
|
|
3452
|
-
const attemptNumber2 = finitePositiveInt(record.attemptNumber);
|
|
3453
|
-
if (attemptNumber2 !== void 0) {
|
|
3454
|
-
out.attemptNumber = attemptNumber2;
|
|
3455
|
-
found = true;
|
|
3456
|
-
}
|
|
3457
|
-
return found ? out : void 0;
|
|
3458
|
-
}
|
|
3459
|
-
function sessionKeyForRun(meta2, options) {
|
|
3460
|
-
if (meta2?.sessionId) return meta2.sessionId;
|
|
3461
|
-
if (options?.correlateByGroupId && meta2?.groupId) {
|
|
3462
|
-
return `group:${meta2.groupId}`;
|
|
3367
|
+
for (const key of keys) {
|
|
3368
|
+
const value = record[key];
|
|
3369
|
+
if (typeof value === "string" && value.trim() !== "") return value.trim();
|
|
3463
3370
|
}
|
|
3464
3371
|
return void 0;
|
|
3465
3372
|
}
|
|
3466
|
-
var
|
|
3467
|
-
"packages/core/src/
|
|
3373
|
+
var init_logical_events = __esm({
|
|
3374
|
+
"packages/core/src/checks/logical-events.ts"() {
|
|
3375
|
+
init_programmatic();
|
|
3468
3376
|
}
|
|
3469
3377
|
});
|
|
3470
3378
|
|
|
3471
|
-
// packages/core/src/
|
|
3472
|
-
function
|
|
3473
|
-
|
|
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";
|
|
3474
3385
|
}
|
|
3475
|
-
function
|
|
3476
|
-
|
|
3477
|
-
}
|
|
3478
|
-
|
|
3479
|
-
let latest = 0;
|
|
3480
|
-
for (const run of runs) {
|
|
3481
|
-
const ms = activityMs(run);
|
|
3482
|
-
if (ms > latest) latest = ms;
|
|
3386
|
+
function flatten(nodes, out = []) {
|
|
3387
|
+
for (const node of nodes) {
|
|
3388
|
+
out.push({ node, index: out.length + 1 });
|
|
3389
|
+
flatten(node.children, out);
|
|
3483
3390
|
}
|
|
3484
|
-
return
|
|
3391
|
+
return out;
|
|
3485
3392
|
}
|
|
3486
|
-
function
|
|
3487
|
-
|
|
3488
|
-
for (const run of runs) {
|
|
3489
|
-
if (run.startedAt === void 0) continue;
|
|
3490
|
-
if (earliest === void 0 || run.startedAt < earliest) {
|
|
3491
|
-
earliest = run.startedAt;
|
|
3492
|
-
}
|
|
3493
|
-
}
|
|
3494
|
-
return earliest;
|
|
3393
|
+
function redactValue(redactor, key, value) {
|
|
3394
|
+
return redactor.redactValue(key, value);
|
|
3495
3395
|
}
|
|
3496
|
-
function
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
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
|
+
};
|
|
3504
3404
|
}
|
|
3505
|
-
function
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
if (!isExplicitSessionStatus(raw)) continue;
|
|
3511
|
-
const priority = EXPLICIT_STATUS_PRIORITY[raw] ?? 0;
|
|
3512
|
-
if (priority > bestPriority) {
|
|
3513
|
-
bestPriority = priority;
|
|
3514
|
-
best = raw;
|
|
3515
|
-
}
|
|
3516
|
-
}
|
|
3517
|
-
return best;
|
|
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}`);
|
|
3518
3410
|
}
|
|
3519
|
-
function
|
|
3520
|
-
|
|
3521
|
-
const latest = errorRuns[0];
|
|
3522
|
-
if (!latest) return void 0;
|
|
3523
|
-
const meta2 = latest.metadata ?? {};
|
|
3524
|
-
const message = typeof meta2.errorMessage === "string" && meta2.errorMessage.trim() !== "" ? meta2.errorMessage.trim() : latest.name ?? latest.runId;
|
|
3525
|
-
const code = typeof meta2.errorCode === "string" && meta2.errorCode.trim() !== "" ? meta2.errorCode.trim() : void 0;
|
|
3526
|
-
return { runId: latest.runId, message, code };
|
|
3411
|
+
function countErrorNodes(nodes) {
|
|
3412
|
+
return nodes.filter((entry) => entry.node.event.status === "error").length;
|
|
3527
3413
|
}
|
|
3528
|
-
function
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
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;
|
|
3548
3439
|
}
|
|
3549
3440
|
}
|
|
3550
|
-
return
|
|
3441
|
+
return facts;
|
|
3551
3442
|
}
|
|
3552
|
-
function
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
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
|
+
);
|
|
3557
3499
|
}
|
|
3558
3500
|
}
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
function deriveSessionStatus(runs, options = {}) {
|
|
3562
|
-
if (runs.length === 0) return "unknown";
|
|
3563
|
-
if (runs.some((run) => run.status === "running")) return "running";
|
|
3564
|
-
const explicit = pickExplicitStatus(runs);
|
|
3565
|
-
if (explicit && explicit !== "running") return explicit;
|
|
3566
|
-
if (runs.some((run) => run.status === "error")) return "error";
|
|
3567
|
-
if (runs.every((run) => run.status === "success")) return "completed";
|
|
3568
|
-
const nowMs = options.nowMs ?? Date.now();
|
|
3569
|
-
const staleThresholdMs = options.staleThresholdMs ?? DEFAULT_STALE_THRESHOLD_MS;
|
|
3570
|
-
const lastMs = latestActivityMs(runs);
|
|
3571
|
-
if (lastMs > 0 && nowMs - lastMs > staleThresholdMs) return "stale";
|
|
3572
|
-
return "unknown";
|
|
3501
|
+
facts.push(...attributeFacts(nodes, redactor));
|
|
3502
|
+
return facts;
|
|
3573
3503
|
}
|
|
3574
|
-
function
|
|
3575
|
-
const
|
|
3576
|
-
const
|
|
3577
|
-
const
|
|
3578
|
-
const
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
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
|
+
});
|
|
3588
3526
|
}
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
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);
|
|
3594
3553
|
return {
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
observationSummary: deriveObservationSummary(sessionRuns),
|
|
3607
|
-
checkSummary: deriveCheckSummary(sessionRuns)
|
|
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
|
+
]
|
|
3608
3565
|
};
|
|
3609
3566
|
}
|
|
3610
|
-
var
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
EXPLICIT_STATUS_PRIORITY = {
|
|
3616
|
-
error: 5,
|
|
3617
|
-
waiting_input: 4,
|
|
3618
|
-
idle: 3,
|
|
3619
|
-
stale: 2,
|
|
3620
|
-
completed: 1
|
|
3621
|
-
};
|
|
3622
|
-
EXPLICIT_SESSION_STATUSES = /* @__PURE__ */ new Set([
|
|
3623
|
-
"running",
|
|
3624
|
-
"waiting_input",
|
|
3625
|
-
"idle",
|
|
3626
|
-
"completed",
|
|
3627
|
-
"error",
|
|
3628
|
-
"stale",
|
|
3629
|
-
"unknown"
|
|
3630
|
-
]);
|
|
3567
|
+
var init_explain = __esm({
|
|
3568
|
+
"packages/core/src/explain.ts"() {
|
|
3569
|
+
init_logical_events();
|
|
3570
|
+
init_redactor();
|
|
3571
|
+
init_redaction_profiles();
|
|
3631
3572
|
}
|
|
3632
3573
|
});
|
|
3633
3574
|
|
|
3634
|
-
// packages/core/src/
|
|
3635
|
-
function
|
|
3636
|
-
const
|
|
3637
|
-
const
|
|
3638
|
-
if (
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
return `${name} session ${session.sessionId} ${status} with observation warning`;
|
|
3575
|
+
// packages/core/src/search.ts
|
|
3576
|
+
function parseDurationFilter(expr) {
|
|
3577
|
+
const raw = expr.trim();
|
|
3578
|
+
const m = raw.match(/^(>=|<=|>|<)\s*(.+)$/);
|
|
3579
|
+
if (!m) {
|
|
3580
|
+
throw new Error(
|
|
3581
|
+
`Invalid --duration "${expr}". Use forms like >5s, >=500ms, <2m.`
|
|
3582
|
+
);
|
|
3643
3583
|
}
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
if (!since || since.trim() === "") return nowMs - 7 * 864e5;
|
|
3648
|
-
const trimmed = since.trim().toLowerCase();
|
|
3649
|
-
const match = /^(\d+)([smhd])$/.exec(trimmed);
|
|
3650
|
-
if (!match) return nowMs - 7 * 864e5;
|
|
3651
|
-
const amount = Number.parseInt(match[1], 10);
|
|
3652
|
-
const unit = match[2];
|
|
3653
|
-
const mult = unit === "s" ? 1e3 : unit === "m" ? 6e4 : unit === "h" ? 36e5 : 864e5;
|
|
3654
|
-
return nowMs - amount * mult;
|
|
3584
|
+
const op = m[1];
|
|
3585
|
+
const ms = parseDuration(m[2].trim());
|
|
3586
|
+
return { op, ms };
|
|
3655
3587
|
}
|
|
3656
|
-
function
|
|
3657
|
-
|
|
3588
|
+
function durationMatches(valueMs, filter) {
|
|
3589
|
+
if (valueMs === void 0 || !Number.isFinite(valueMs)) return false;
|
|
3590
|
+
switch (filter.op) {
|
|
3591
|
+
case ">":
|
|
3592
|
+
return valueMs > filter.ms;
|
|
3593
|
+
case ">=":
|
|
3594
|
+
return valueMs >= filter.ms;
|
|
3595
|
+
case "<":
|
|
3596
|
+
return valueMs < filter.ms;
|
|
3597
|
+
case "<=":
|
|
3598
|
+
return valueMs <= filter.ms;
|
|
3599
|
+
default:
|
|
3600
|
+
return false;
|
|
3601
|
+
}
|
|
3658
3602
|
}
|
|
3659
|
-
function
|
|
3660
|
-
|
|
3603
|
+
function normalizeStepTypeFilter(kind, type) {
|
|
3604
|
+
const v = (kind ?? type)?.trim().toLowerCase();
|
|
3605
|
+
return v && v !== "" ? v : void 0;
|
|
3661
3606
|
}
|
|
3662
|
-
function
|
|
3663
|
-
|
|
3664
|
-
if (!summary) return 0;
|
|
3665
|
-
return summary.warn;
|
|
3607
|
+
function nameMatches(hay, needle) {
|
|
3608
|
+
return hay.toLowerCase().includes(needle.toLowerCase());
|
|
3666
3609
|
}
|
|
3667
|
-
function
|
|
3668
|
-
|
|
3669
|
-
const
|
|
3670
|
-
const
|
|
3671
|
-
const
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
});
|
|
3676
|
-
const entries = [...inWindow].sort((a, b) => Date.parse(b.lastActivity) - Date.parse(a.lastActivity)).slice(0, limit).map((session) => ({
|
|
3677
|
-
sessionId: session.sessionId,
|
|
3678
|
-
status: session.status,
|
|
3679
|
-
summary: statusLine(session),
|
|
3680
|
-
lastActivity: session.lastActivity,
|
|
3681
|
-
runCount: session.runIds.length
|
|
3682
|
-
}));
|
|
3683
|
-
let failed = 0;
|
|
3684
|
-
let stale = 0;
|
|
3685
|
-
let guardrailWarningTotal = 0;
|
|
3686
|
-
for (const session of inWindow) {
|
|
3687
|
-
if (isFailed(session.status)) failed += 1;
|
|
3688
|
-
if (isStale(session.status)) stale += 1;
|
|
3689
|
-
guardrailWarningTotal += guardrailWarnings(session);
|
|
3610
|
+
async function searchTraces(metas, options) {
|
|
3611
|
+
let filtered = filterTraces(metas, { since: options.since });
|
|
3612
|
+
const stepTypeFilter = normalizeStepTypeFilter(options.kind, options.type);
|
|
3613
|
+
const nameQuery = options.name?.trim();
|
|
3614
|
+
const toolQuery = options.tool?.trim();
|
|
3615
|
+
let durationFilter;
|
|
3616
|
+
if (options.duration) {
|
|
3617
|
+
durationFilter = parseDurationFilter(options.duration);
|
|
3690
3618
|
}
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
guardrailWarnings: guardrailWarningTotal,
|
|
3697
|
-
entries
|
|
3698
|
-
};
|
|
3699
|
-
}
|
|
3700
|
-
function renderActivitySummaryHuman(summary, options) {
|
|
3701
|
-
const lines = [];
|
|
3702
|
-
const nowMs = Date.now();
|
|
3703
|
-
const todayStart = new Date(nowMs);
|
|
3704
|
-
todayStart.setHours(0, 0, 0, 0);
|
|
3705
|
-
const todayMs = todayStart.getTime();
|
|
3706
|
-
const today = summary.entries.filter(
|
|
3707
|
-
(entry) => Date.parse(entry.lastActivity) >= todayMs
|
|
3619
|
+
const limit = options.limit ?? 50;
|
|
3620
|
+
const sessionId = options.session?.trim();
|
|
3621
|
+
const observationStatus2 = parseObservationFilter(options.observation);
|
|
3622
|
+
const hasContentFilter = Boolean(
|
|
3623
|
+
options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter || observationStatus2
|
|
3708
3624
|
);
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3625
|
+
const results = [];
|
|
3626
|
+
const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
|
|
3627
|
+
if (!hasContentFilter) {
|
|
3628
|
+
for (const m of filtered) {
|
|
3629
|
+
results.push({
|
|
3630
|
+
runId: m.runId,
|
|
3631
|
+
runName: m.name,
|
|
3632
|
+
runStatus: m.status,
|
|
3633
|
+
timestamp: m.startedAt,
|
|
3634
|
+
durationMs: m.durationMs,
|
|
3635
|
+
matchReason: sessionLabel ? `trace in session ${sessionLabel}` : "trace in directory",
|
|
3636
|
+
matchedFields: sessionLabel ? ["run", "session"] : ["run"],
|
|
3637
|
+
filePath: m.filePath,
|
|
3638
|
+
...sessionLabel ? { sessionId: sessionLabel } : {}
|
|
3639
|
+
});
|
|
3713
3640
|
}
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
return lines.join("\n");
|
|
3722
|
-
}
|
|
3723
|
-
var init_activity = __esm({
|
|
3724
|
-
"packages/core/src/sessions/activity.ts"() {
|
|
3725
|
-
}
|
|
3726
|
-
});
|
|
3727
|
-
|
|
3728
|
-
// packages/core/src/sessions/types.ts
|
|
3729
|
-
var init_types3 = __esm({
|
|
3730
|
-
"packages/core/src/sessions/types.ts"() {
|
|
3641
|
+
results.sort((a, b) => {
|
|
3642
|
+
const ta = a.timestamp ?? 0;
|
|
3643
|
+
const tb = b.timestamp ?? 0;
|
|
3644
|
+
if (ta !== tb) return tb - ta;
|
|
3645
|
+
return a.runId.localeCompare(b.runId);
|
|
3646
|
+
});
|
|
3647
|
+
return results.slice(0, limit);
|
|
3731
3648
|
}
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3649
|
+
for (const m of filtered) {
|
|
3650
|
+
if (options.status && m.status !== options.status) continue;
|
|
3651
|
+
let events = [];
|
|
3652
|
+
try {
|
|
3653
|
+
events = await readTraceEventsFromFile(m.filePath);
|
|
3654
|
+
} catch {
|
|
3655
|
+
continue;
|
|
3656
|
+
}
|
|
3657
|
+
if (events.length === 0) continue;
|
|
3658
|
+
const runMatches = matchRunLevel(m, {
|
|
3659
|
+
stepTypeFilter,
|
|
3660
|
+
nameQuery,
|
|
3661
|
+
toolQuery,
|
|
3662
|
+
durationFilter,
|
|
3663
|
+
statusFilter: options.status
|
|
3664
|
+
});
|
|
3665
|
+
results.push(...runMatches);
|
|
3666
|
+
const stepMatches = matchStepLevel(m, events, {
|
|
3667
|
+
stepTypeFilter,
|
|
3668
|
+
nameQuery,
|
|
3669
|
+
toolQuery,
|
|
3670
|
+
durationFilter,
|
|
3671
|
+
statusFilter: options.status
|
|
3672
|
+
});
|
|
3673
|
+
results.push(...stepMatches);
|
|
3674
|
+
if (observationStatus2) {
|
|
3675
|
+
const outcomes = extractOutcomesFromTraceEvents(events);
|
|
3676
|
+
const matched = outcomes.filter((outcome) => outcome.status === observationStatus2);
|
|
3677
|
+
for (const outcome of matched) {
|
|
3678
|
+
results.push({
|
|
3679
|
+
runId: m.runId,
|
|
3680
|
+
runName: m.name,
|
|
3681
|
+
runStatus: m.status,
|
|
3682
|
+
stepName: outcome.name,
|
|
3683
|
+
timestamp: outcome.observedAt,
|
|
3684
|
+
matchReason: `observation status=${outcome.status}`,
|
|
3685
|
+
matchedFields: ["outcome.status", "outcome.name"],
|
|
3686
|
+
filePath: m.filePath
|
|
3687
|
+
});
|
|
3743
3688
|
}
|
|
3744
|
-
break;
|
|
3745
3689
|
}
|
|
3746
|
-
} catch {
|
|
3747
|
-
}
|
|
3748
|
-
return {
|
|
3749
|
-
runId: meta2.runId,
|
|
3750
|
-
name: meta2.name,
|
|
3751
|
-
status: meta2.status,
|
|
3752
|
-
startedAt: meta2.startedAt,
|
|
3753
|
-
endedAt: meta2.endedAt,
|
|
3754
|
-
durationMs: meta2.durationMs,
|
|
3755
|
-
filePath: meta2.filePath,
|
|
3756
|
-
metadata
|
|
3757
|
-
};
|
|
3758
|
-
}
|
|
3759
|
-
async function loadSessionRunRecords(metas) {
|
|
3760
|
-
const out = [];
|
|
3761
|
-
for (const meta2 of metas) {
|
|
3762
|
-
out.push(await enrichSessionRunRecord(meta2));
|
|
3763
3690
|
}
|
|
3764
|
-
|
|
3691
|
+
results.sort((a, b) => {
|
|
3692
|
+
const ta = a.timestamp ?? 0;
|
|
3693
|
+
const tb = b.timestamp ?? 0;
|
|
3694
|
+
if (ta !== tb) return tb - ta;
|
|
3695
|
+
const runCmp = a.runId.localeCompare(b.runId);
|
|
3696
|
+
if (runCmp !== 0) return runCmp;
|
|
3697
|
+
return (a.stepName ?? "").localeCompare(b.stepName ?? "");
|
|
3698
|
+
});
|
|
3699
|
+
return results.slice(0, limit);
|
|
3765
3700
|
}
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3701
|
+
function matchRunLevel(m, opts) {
|
|
3702
|
+
if (opts.stepTypeFilter || opts.toolQuery) return [];
|
|
3703
|
+
if (opts.statusFilter && m.status !== opts.statusFilter) return [];
|
|
3704
|
+
if (opts.nameQuery && !nameMatches(m.name ?? m.runId, opts.nameQuery)) {
|
|
3705
|
+
return [];
|
|
3769
3706
|
}
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
if (
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3707
|
+
if (opts.durationFilter && !durationMatches(m.durationMs, opts.durationFilter)) {
|
|
3708
|
+
return [];
|
|
3709
|
+
}
|
|
3710
|
+
const fields = [];
|
|
3711
|
+
if (opts.statusFilter) fields.push("run.status");
|
|
3712
|
+
if (opts.nameQuery) fields.push("run.name");
|
|
3713
|
+
if (opts.durationFilter) fields.push("run.durationMs");
|
|
3714
|
+
if (fields.length === 0) return [];
|
|
3715
|
+
return [
|
|
3716
|
+
{
|
|
3717
|
+
runId: m.runId,
|
|
3718
|
+
runName: m.name,
|
|
3719
|
+
runStatus: m.status,
|
|
3720
|
+
timestamp: m.startedAt,
|
|
3721
|
+
durationMs: m.durationMs,
|
|
3722
|
+
matchReason: `run match: ${fields.join(", ")}`,
|
|
3723
|
+
matchedFields: fields,
|
|
3724
|
+
filePath: m.filePath
|
|
3725
|
+
}
|
|
3726
|
+
];
|
|
3727
|
+
}
|
|
3728
|
+
function matchStepLevel(m, events, opts) {
|
|
3729
|
+
const out = [];
|
|
3730
|
+
const started = /* @__PURE__ */ new Map();
|
|
3731
|
+
for (const e of events) {
|
|
3732
|
+
if (e.event === "step_started") {
|
|
3733
|
+
started.set(e.stepId, e);
|
|
3792
3734
|
}
|
|
3793
|
-
const runIdSet = new Set(session.runIds);
|
|
3794
|
-
const filtered = metas.filter((meta2) => runIdSet.has(meta2.runId));
|
|
3795
|
-
return {
|
|
3796
|
-
metas: filtered,
|
|
3797
|
-
scopeLabel: sessionId,
|
|
3798
|
-
scopeKind: "session",
|
|
3799
|
-
runIds: session.runIds,
|
|
3800
|
-
warnings,
|
|
3801
|
-
notFound: false
|
|
3802
|
-
};
|
|
3803
3735
|
}
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
notFound: true
|
|
3814
|
-
};
|
|
3736
|
+
for (const e of events) {
|
|
3737
|
+
if (e.event !== "step_completed") continue;
|
|
3738
|
+
const c = e;
|
|
3739
|
+
const s = started.get(c.stepId);
|
|
3740
|
+
if (!s) continue;
|
|
3741
|
+
const fields = [];
|
|
3742
|
+
const stepType = s.type;
|
|
3743
|
+
if (opts.stepTypeFilter && stepType !== opts.stepTypeFilter) {
|
|
3744
|
+
continue;
|
|
3815
3745
|
}
|
|
3816
|
-
const
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
}
|
|
3746
|
+
const hasStepFilters = opts.stepTypeFilter || opts.nameQuery || opts.toolQuery || opts.durationFilter || opts.statusFilter === "error" || opts.statusFilter === "success";
|
|
3747
|
+
if (!hasStepFilters) continue;
|
|
3748
|
+
if (opts.statusFilter === "error" && c.status === "error") {
|
|
3749
|
+
fields.push("step.status");
|
|
3750
|
+
} else if (opts.statusFilter === "success" && c.status === "success") {
|
|
3751
|
+
fields.push("step.status");
|
|
3752
|
+
} else if (opts.statusFilter === "error" || opts.statusFilter === "success") {
|
|
3753
|
+
continue;
|
|
3754
|
+
}
|
|
3755
|
+
if (opts.nameQuery) {
|
|
3756
|
+
if (!nameMatches(s.name, opts.nameQuery)) continue;
|
|
3757
|
+
fields.push("step.name");
|
|
3758
|
+
}
|
|
3759
|
+
if (opts.toolQuery) {
|
|
3760
|
+
const toolName2 = typeof s.metadata?.toolName === "string" ? s.metadata.toolName : s.name;
|
|
3761
|
+
if (!nameMatches(toolName2, opts.toolQuery)) continue;
|
|
3762
|
+
fields.push("step.tool");
|
|
3763
|
+
}
|
|
3764
|
+
if (opts.durationFilter) {
|
|
3765
|
+
if (!durationMatches(c.durationMs, opts.durationFilter)) continue;
|
|
3766
|
+
fields.push("step.durationMs");
|
|
3767
|
+
}
|
|
3768
|
+
if (opts.stepTypeFilter) {
|
|
3769
|
+
fields.push("step.type");
|
|
3770
|
+
}
|
|
3771
|
+
if (fields.length === 0) continue;
|
|
3772
|
+
out.push({
|
|
3773
|
+
runId: m.runId,
|
|
3774
|
+
runName: m.name,
|
|
3775
|
+
runStatus: m.status,
|
|
3776
|
+
stepId: c.stepId,
|
|
3777
|
+
stepName: s.name,
|
|
3778
|
+
stepType,
|
|
3779
|
+
timestamp: s.startTime ?? s.timestamp,
|
|
3780
|
+
durationMs: c.durationMs,
|
|
3781
|
+
matchReason: `step match: ${fields.join(", ")}`,
|
|
3782
|
+
matchedFields: fields,
|
|
3783
|
+
filePath: m.filePath
|
|
3784
|
+
});
|
|
3825
3785
|
}
|
|
3826
|
-
return
|
|
3827
|
-
metas: [...metas],
|
|
3828
|
-
scopeLabel: "",
|
|
3829
|
-
scopeKind: "session",
|
|
3830
|
-
runIds: [],
|
|
3831
|
-
warnings,
|
|
3832
|
-
notFound: false
|
|
3833
|
-
};
|
|
3786
|
+
return out;
|
|
3834
3787
|
}
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3788
|
+
async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
|
|
3789
|
+
const metas = [];
|
|
3790
|
+
for (const fileName of fileNames) {
|
|
3791
|
+
try {
|
|
3792
|
+
const filePath = getPath(fileName);
|
|
3793
|
+
const meta2 = await extractMetadata(filePath);
|
|
3794
|
+
metas.push(meta2);
|
|
3795
|
+
} catch {
|
|
3796
|
+
}
|
|
3839
3797
|
}
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3798
|
+
return metas;
|
|
3799
|
+
}
|
|
3800
|
+
var init_search = __esm({
|
|
3801
|
+
"packages/core/src/search.ts"() {
|
|
3802
|
+
init_trace_metadata();
|
|
3803
|
+
init_trace_filter();
|
|
3804
|
+
init_storage();
|
|
3805
|
+
init_duration();
|
|
3806
|
+
init_outcomes();
|
|
3847
3807
|
}
|
|
3848
3808
|
});
|
|
3849
3809
|
|
|
3850
|
-
// packages/core/src/sessions/
|
|
3851
|
-
function
|
|
3852
|
-
return
|
|
3810
|
+
// packages/core/src/sessions/metadata.ts
|
|
3811
|
+
function isNonEmptyString3(value) {
|
|
3812
|
+
return typeof value === "string" && value.trim() !== "";
|
|
3853
3813
|
}
|
|
3854
|
-
function
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3814
|
+
function finitePositiveInt(value) {
|
|
3815
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 1) {
|
|
3816
|
+
return void 0;
|
|
3817
|
+
}
|
|
3818
|
+
return Math.trunc(value);
|
|
3819
|
+
}
|
|
3820
|
+
function extractSessionWorkflowMetadata(record) {
|
|
3821
|
+
if (!record) return void 0;
|
|
3822
|
+
const out = {};
|
|
3823
|
+
let found = false;
|
|
3824
|
+
const assignString = (key, value) => {
|
|
3825
|
+
if (isNonEmptyString3(value)) {
|
|
3826
|
+
out[key] = value.trim();
|
|
3827
|
+
found = true;
|
|
3828
|
+
}
|
|
3861
3829
|
};
|
|
3830
|
+
assignString("sessionId", record.sessionId);
|
|
3831
|
+
assignString("conversationId", record.conversationId);
|
|
3832
|
+
assignString("groupId", record.groupId);
|
|
3833
|
+
assignString("parentGroupId", record.parentGroupId);
|
|
3834
|
+
assignString("retryOf", record.retryOf);
|
|
3835
|
+
assignString("retryReason", record.retryReason);
|
|
3836
|
+
assignString("handoffFrom", record.handoffFrom);
|
|
3837
|
+
assignString("handoffTo", record.handoffTo);
|
|
3838
|
+
assignString("subAgentId", record.subAgentId);
|
|
3839
|
+
assignString("subAgentName", record.subAgentName);
|
|
3840
|
+
assignString("jobId", record.jobId);
|
|
3841
|
+
assignString("queueName", record.queueName);
|
|
3842
|
+
assignString("workflowName", record.workflowName);
|
|
3843
|
+
assignString("workflowStep", record.workflowStep);
|
|
3844
|
+
assignString("toolCallId", record.toolCallId);
|
|
3845
|
+
assignString("mcpToolCallId", record.mcpToolCallId);
|
|
3846
|
+
assignString("linkedStepId", record.linkedStepId);
|
|
3847
|
+
assignString("operationId", record.operationId);
|
|
3848
|
+
assignString("attemptId", record.attemptId);
|
|
3849
|
+
assignString("fallbackOf", record.fallbackOf);
|
|
3850
|
+
assignString("idempotencyKey", record.idempotencyKey);
|
|
3851
|
+
assignString("correlationId", record.correlationId);
|
|
3852
|
+
assignString("requestId", record.requestId);
|
|
3853
|
+
assignString("decisionId", record.decisionId);
|
|
3854
|
+
const attempt = finitePositiveInt(record.attempt);
|
|
3855
|
+
if (attempt !== void 0) {
|
|
3856
|
+
out.attempt = attempt;
|
|
3857
|
+
found = true;
|
|
3858
|
+
}
|
|
3859
|
+
const attemptNumber2 = finitePositiveInt(record.attemptNumber);
|
|
3860
|
+
if (attemptNumber2 !== void 0) {
|
|
3861
|
+
out.attemptNumber = attemptNumber2;
|
|
3862
|
+
found = true;
|
|
3863
|
+
}
|
|
3864
|
+
return found ? out : void 0;
|
|
3862
3865
|
}
|
|
3863
|
-
function
|
|
3864
|
-
|
|
3866
|
+
function sessionKeyForRun(meta2, options) {
|
|
3867
|
+
if (meta2?.sessionId) return meta2.sessionId;
|
|
3868
|
+
if (options?.correlateByGroupId && meta2?.groupId) {
|
|
3869
|
+
return `group:${meta2.groupId}`;
|
|
3870
|
+
}
|
|
3871
|
+
return void 0;
|
|
3865
3872
|
}
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
return {
|
|
3869
|
-
ok: false,
|
|
3870
|
-
status: "error",
|
|
3871
|
-
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
3872
|
-
scopeKind: scope.scopeKind,
|
|
3873
|
-
scopeLabel: scope.scopeLabel,
|
|
3874
|
-
runIds: [],
|
|
3875
|
-
runResults: [],
|
|
3876
|
-
summary: { ...emptySummary(), errors: 1 },
|
|
3877
|
-
findings: [],
|
|
3878
|
-
diagnostics: [
|
|
3879
|
-
sessionDiagnostic(
|
|
3880
|
-
"AI_CHECK_INVALID_ARGUMENTS",
|
|
3881
|
-
`${scope.scopeKind} not found: ${scope.scopeLabel}`
|
|
3882
|
-
)
|
|
3883
|
-
],
|
|
3884
|
-
ruleExecutions: [],
|
|
3885
|
-
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
3886
|
-
};
|
|
3873
|
+
var init_metadata = __esm({
|
|
3874
|
+
"packages/core/src/sessions/metadata.ts"() {
|
|
3887
3875
|
}
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
`No readable traces in ${scope.scopeKind}: ${scope.scopeLabel}`
|
|
3903
|
-
)
|
|
3904
|
-
],
|
|
3905
|
-
ruleExecutions: [],
|
|
3906
|
-
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
3907
|
-
};
|
|
3876
|
+
});
|
|
3877
|
+
|
|
3878
|
+
// packages/core/src/sessions/status.ts
|
|
3879
|
+
function isExplicitSessionStatus(value) {
|
|
3880
|
+
return typeof value === "string" && EXPLICIT_SESSION_STATUSES.has(value);
|
|
3881
|
+
}
|
|
3882
|
+
function activityMs(run) {
|
|
3883
|
+
return run.endedAt ?? run.startedAt ?? 0;
|
|
3884
|
+
}
|
|
3885
|
+
function latestActivityMs(runs) {
|
|
3886
|
+
let latest = 0;
|
|
3887
|
+
for (const run of runs) {
|
|
3888
|
+
const ms = activityMs(run);
|
|
3889
|
+
if (ms > latest) latest = ms;
|
|
3908
3890
|
}
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
const
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
diagnostics.push(...result.diagnostics);
|
|
3918
|
-
ruleExecutions.push(...result.ruleExecutions ?? []);
|
|
3919
|
-
if (result.runId) {
|
|
3920
|
-
runResults.push({ runId: result.runId, status: result.status });
|
|
3891
|
+
return latest;
|
|
3892
|
+
}
|
|
3893
|
+
function earliestStart(runs) {
|
|
3894
|
+
let earliest;
|
|
3895
|
+
for (const run of runs) {
|
|
3896
|
+
if (run.startedAt === void 0) continue;
|
|
3897
|
+
if (earliest === void 0 || run.startedAt < earliest) {
|
|
3898
|
+
earliest = run.startedAt;
|
|
3921
3899
|
}
|
|
3922
3900
|
}
|
|
3923
|
-
|
|
3924
|
-
findings.sort((a, b) => {
|
|
3925
|
-
const runCmp = (a.evidence[0]?.runId ?? "").localeCompare(
|
|
3926
|
-
b.evidence[0]?.runId ?? ""
|
|
3927
|
-
);
|
|
3928
|
-
if (runCmp !== 0) return runCmp;
|
|
3929
|
-
return a.ruleId.localeCompare(b.ruleId);
|
|
3930
|
-
});
|
|
3931
|
-
ruleExecutions.sort((a, b) => {
|
|
3932
|
-
const runCmp = (a.runId ?? "").localeCompare(b.runId ?? "");
|
|
3933
|
-
if (runCmp !== 0) return runCmp;
|
|
3934
|
-
return a.ruleId.localeCompare(b.ruleId);
|
|
3935
|
-
});
|
|
3936
|
-
const hasErrors = diagnostics.some((item) => item.severity === "error");
|
|
3937
|
-
const status = hasErrors ? "error" : summary.failed > 0 ? "fail" : "pass";
|
|
3938
|
-
return {
|
|
3939
|
-
ok: status === "pass",
|
|
3940
|
-
status,
|
|
3941
|
-
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
3942
|
-
scopeKind: scope.scopeKind,
|
|
3943
|
-
scopeLabel: scope.scopeLabel,
|
|
3944
|
-
runIds: scope.runIds,
|
|
3945
|
-
runResults,
|
|
3946
|
-
summary,
|
|
3947
|
-
findings,
|
|
3948
|
-
diagnostics,
|
|
3949
|
-
ruleExecutions,
|
|
3950
|
-
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
3951
|
-
};
|
|
3901
|
+
return earliest;
|
|
3952
3902
|
}
|
|
3953
|
-
|
|
3954
|
-
|
|
3903
|
+
function latestEndWhenAllEnded(runs) {
|
|
3904
|
+
if (runs.length === 0) return void 0;
|
|
3905
|
+
let latest;
|
|
3906
|
+
for (const run of runs) {
|
|
3907
|
+
if (run.endedAt === void 0) return void 0;
|
|
3908
|
+
if (latest === void 0 || run.endedAt > latest) latest = run.endedAt;
|
|
3955
3909
|
}
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
// packages/core/src/sessions/index.ts
|
|
3959
|
-
function compareRuns(a, b) {
|
|
3960
|
-
const aStart = a.startedAt ?? 0;
|
|
3961
|
-
const bStart = b.startedAt ?? 0;
|
|
3962
|
-
if (aStart !== bStart) return aStart - bStart;
|
|
3963
|
-
return a.runId.localeCompare(b.runId);
|
|
3910
|
+
return latest;
|
|
3964
3911
|
}
|
|
3965
|
-
function
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
groupId,
|
|
3976
|
-
parentGroupId: metaByRunId.get(runId)?.parentGroupId,
|
|
3977
|
-
runIds: [runId]
|
|
3978
|
-
});
|
|
3912
|
+
function pickExplicitStatus(runs) {
|
|
3913
|
+
let best;
|
|
3914
|
+
let bestPriority = 0;
|
|
3915
|
+
for (const run of runs) {
|
|
3916
|
+
const raw = run.metadata?.sessionStatus;
|
|
3917
|
+
if (!isExplicitSessionStatus(raw)) continue;
|
|
3918
|
+
const priority = EXPLICIT_STATUS_PRIORITY[raw] ?? 0;
|
|
3919
|
+
if (priority > bestPriority) {
|
|
3920
|
+
bestPriority = priority;
|
|
3921
|
+
best = raw;
|
|
3979
3922
|
}
|
|
3980
3923
|
}
|
|
3981
|
-
return
|
|
3982
|
-
...group,
|
|
3983
|
-
runIds: [...group.runIds].sort((a, b) => a.localeCompare(b))
|
|
3984
|
-
}));
|
|
3924
|
+
return best;
|
|
3985
3925
|
}
|
|
3986
|
-
function
|
|
3987
|
-
const
|
|
3988
|
-
const
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
if (meta2.handoffFrom) {
|
|
4008
|
-
pushEdge({
|
|
4009
|
-
from: meta2.handoffFrom,
|
|
4010
|
-
to: runId,
|
|
4011
|
-
source: "manual",
|
|
4012
|
-
confidence: "explicit"
|
|
4013
|
-
});
|
|
3926
|
+
function deriveLastError(runs) {
|
|
3927
|
+
const errorRuns = runs.filter((run) => run.status === "error").sort((a, b) => activityMs(b) - activityMs(a));
|
|
3928
|
+
const latest = errorRuns[0];
|
|
3929
|
+
if (!latest) return void 0;
|
|
3930
|
+
const meta2 = latest.metadata ?? {};
|
|
3931
|
+
const message = typeof meta2.errorMessage === "string" && meta2.errorMessage.trim() !== "" ? meta2.errorMessage.trim() : latest.name ?? latest.runId;
|
|
3932
|
+
const code = typeof meta2.errorCode === "string" && meta2.errorCode.trim() !== "" ? meta2.errorCode.trim() : void 0;
|
|
3933
|
+
return { runId: latest.runId, message, code };
|
|
3934
|
+
}
|
|
3935
|
+
function deriveCheckSummary(runs) {
|
|
3936
|
+
let pass2 = 0;
|
|
3937
|
+
let fail7 = 0;
|
|
3938
|
+
let warn2 = 0;
|
|
3939
|
+
let found = false;
|
|
3940
|
+
for (const run of runs) {
|
|
3941
|
+
const summary = run.metadata?.checkSummary;
|
|
3942
|
+
if (!summary || typeof summary !== "object") continue;
|
|
3943
|
+
const record = summary;
|
|
3944
|
+
if (typeof record.pass === "number") {
|
|
3945
|
+
pass2 += record.pass;
|
|
3946
|
+
found = true;
|
|
4014
3947
|
}
|
|
4015
|
-
if (
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
to: meta2.handoffTo,
|
|
4019
|
-
source: "manual",
|
|
4020
|
-
confidence: "explicit"
|
|
4021
|
-
});
|
|
3948
|
+
if (typeof record.fail === "number") {
|
|
3949
|
+
fail7 += record.fail;
|
|
3950
|
+
found = true;
|
|
4022
3951
|
}
|
|
4023
|
-
if (
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
to: meta2.subAgentId,
|
|
4027
|
-
source: "inferred",
|
|
4028
|
-
confidence: "correlated"
|
|
4029
|
-
});
|
|
4030
|
-
warnings.push({
|
|
4031
|
-
code: "ambiguous-handoff-endpoints",
|
|
4032
|
-
message: "Handoff inferred from parentGroupId and subAgentId without explicit handoffFrom/handoffTo.",
|
|
4033
|
-
runId,
|
|
4034
|
-
sessionId
|
|
4035
|
-
});
|
|
3952
|
+
if (typeof record.warn === "number") {
|
|
3953
|
+
warn2 += record.warn;
|
|
3954
|
+
found = true;
|
|
4036
3955
|
}
|
|
4037
3956
|
}
|
|
4038
|
-
return
|
|
4039
|
-
const from = a.from.localeCompare(b.from);
|
|
4040
|
-
if (from !== 0) return from;
|
|
4041
|
-
return a.to.localeCompare(b.to);
|
|
4042
|
-
});
|
|
3957
|
+
return found ? { pass: pass2, fail: fail7, warn: warn2 } : void 0;
|
|
4043
3958
|
}
|
|
4044
|
-
function
|
|
4045
|
-
const
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
if (meta2.retryOf) {
|
|
4050
|
-
retries.push({
|
|
4051
|
-
runId,
|
|
4052
|
-
retryOf: meta2.retryOf,
|
|
4053
|
-
attempt: meta2.attempt,
|
|
4054
|
-
source: "manual",
|
|
4055
|
-
confidence: "explicit"
|
|
4056
|
-
});
|
|
4057
|
-
continue;
|
|
4058
|
-
}
|
|
4059
|
-
if (meta2.attempt !== void 0 && meta2.attempt > 1) {
|
|
4060
|
-
retries.push({
|
|
4061
|
-
runId,
|
|
4062
|
-
attempt: meta2.attempt,
|
|
4063
|
-
source: "inferred",
|
|
4064
|
-
confidence: "correlated"
|
|
4065
|
-
});
|
|
4066
|
-
warnings.push({
|
|
4067
|
-
code: "ambiguous-retry-link",
|
|
4068
|
-
message: "attempt > 1 without retryOf; retry link is correlated only.",
|
|
4069
|
-
runId,
|
|
4070
|
-
sessionId
|
|
4071
|
-
});
|
|
3959
|
+
function deriveObservationSummary(runs) {
|
|
3960
|
+
for (const run of [...runs].sort((a, b) => activityMs(b) - activityMs(a))) {
|
|
3961
|
+
const value = run.metadata?.observationSummary;
|
|
3962
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
3963
|
+
return value.trim();
|
|
4072
3964
|
}
|
|
4073
3965
|
}
|
|
4074
|
-
return
|
|
3966
|
+
return void 0;
|
|
4075
3967
|
}
|
|
4076
|
-
function
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
);
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
);
|
|
4084
|
-
const
|
|
4085
|
-
const
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
if (visited.has(run.runId)) return;
|
|
4089
|
-
visited.add(run.runId);
|
|
4090
|
-
path44.push({
|
|
4091
|
-
runId: run.runId,
|
|
4092
|
-
name: run.name,
|
|
4093
|
-
startedAt: run.startedAt,
|
|
4094
|
-
durationMs: run.durationMs,
|
|
4095
|
-
confidence,
|
|
4096
|
-
source
|
|
4097
|
-
});
|
|
4098
|
-
};
|
|
4099
|
-
for (const edge of handoffs) {
|
|
4100
|
-
if (edge.confidence !== "explicit") continue;
|
|
4101
|
-
const fromRun = [...runById.values()].find(
|
|
4102
|
-
(run) => run.runId === edge.from || metaRunIdMatches(run, edge.from, runById)
|
|
4103
|
-
);
|
|
4104
|
-
const toRun = [...runById.values()].find(
|
|
4105
|
-
(run) => run.runId === edge.to || metaRunIdMatches(run, edge.to, runById)
|
|
4106
|
-
);
|
|
4107
|
-
if (fromRun) pushRun(fromRun, "explicit", "manual");
|
|
4108
|
-
if (toRun) pushRun(toRun, "explicit", "manual");
|
|
4109
|
-
}
|
|
4110
|
-
for (const run of ordered) {
|
|
4111
|
-
if (visited.has(run.runId)) continue;
|
|
4112
|
-
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
4113
|
-
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
4114
|
-
}
|
|
4115
|
-
return path44;
|
|
4116
|
-
}
|
|
4117
|
-
function metaRunIdMatches(run, token, runById) {
|
|
4118
|
-
const meta2 = extractSessionWorkflowMetadata(run.metadata);
|
|
4119
|
-
return meta2?.subAgentId === token || meta2?.groupId === token || runById.has(token);
|
|
3968
|
+
function deriveSessionStatus(runs, options = {}) {
|
|
3969
|
+
if (runs.length === 0) return "unknown";
|
|
3970
|
+
if (runs.some((run) => run.status === "running")) return "running";
|
|
3971
|
+
const explicit = pickExplicitStatus(runs);
|
|
3972
|
+
if (explicit && explicit !== "running") return explicit;
|
|
3973
|
+
if (runs.some((run) => run.status === "error")) return "error";
|
|
3974
|
+
if (runs.every((run) => run.status === "success")) return "completed";
|
|
3975
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
3976
|
+
const staleThresholdMs = options.staleThresholdMs ?? DEFAULT_STALE_THRESHOLD_MS;
|
|
3977
|
+
const lastMs = latestActivityMs(runs);
|
|
3978
|
+
if (lastMs > 0 && nowMs - lastMs > staleThresholdMs) return "stale";
|
|
3979
|
+
return "unknown";
|
|
4120
3980
|
}
|
|
4121
|
-
function
|
|
4122
|
-
const
|
|
4123
|
-
const
|
|
4124
|
-
const
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
const
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
if (!key) {
|
|
4136
|
-
unscopedRunIds.push(run.runId);
|
|
4137
|
-
continue;
|
|
4138
|
-
}
|
|
4139
|
-
const bucket = sessionsByKey.get(key) ?? [];
|
|
4140
|
-
bucket.push(run);
|
|
4141
|
-
sessionsByKey.set(key, bucket);
|
|
4142
|
-
}
|
|
4143
|
-
const sessions = [...sessionsByKey.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([sessionId, sessionRuns]) => {
|
|
4144
|
-
const runIds = sessionRuns.map((run) => run.runId).sort();
|
|
4145
|
-
const handoffs = buildHandoffs(runIds, metaByRunId, warnings, sessionId);
|
|
4146
|
-
const retries = buildRetries(runIds, metaByRunId, warnings, sessionId);
|
|
4147
|
-
const groups = buildGroups(runIds, metaByRunId);
|
|
4148
|
-
const criticalPath = buildCriticalPath(sessionRuns, handoffs);
|
|
4149
|
-
const confidences = new Set(handoffs.map((edge) => edge.confidence));
|
|
4150
|
-
if (confidences.has("explicit") && confidences.has("correlated")) {
|
|
4151
|
-
warnings.push({
|
|
4152
|
-
code: "mixed-confidence-group",
|
|
4153
|
-
message: "Session aggregates explicit and correlated handoff edges.",
|
|
4154
|
-
sessionId
|
|
4155
|
-
});
|
|
4156
|
-
}
|
|
4157
|
-
return enrichSessionSummary(
|
|
4158
|
-
{
|
|
4159
|
-
sessionId,
|
|
4160
|
-
runIds,
|
|
4161
|
-
groups,
|
|
4162
|
-
handoffs,
|
|
4163
|
-
retries,
|
|
4164
|
-
criticalPath
|
|
4165
|
-
},
|
|
4166
|
-
runs,
|
|
4167
|
-
{
|
|
4168
|
-
nowMs: options.nowMs,
|
|
4169
|
-
staleThresholdMs: options.staleThresholdMs
|
|
4170
|
-
}
|
|
4171
|
-
);
|
|
4172
|
-
});
|
|
4173
|
-
if (sessions.length === 0 && runs.length > 0) {
|
|
4174
|
-
warnings.push({
|
|
4175
|
-
code: "missing-session-id",
|
|
4176
|
-
message: "No sessionId (or correlated groupId) found on input runs."
|
|
4177
|
-
});
|
|
3981
|
+
function enrichSessionSummary(summary, runs, options = {}) {
|
|
3982
|
+
const sessionRuns = runs.filter((run) => summary.runIds.includes(run.runId)).sort((a, b) => a.runId.localeCompare(b.runId));
|
|
3983
|
+
const startedAt = earliestStart(sessionRuns);
|
|
3984
|
+
const endedAt = latestEndWhenAllEnded(sessionRuns);
|
|
3985
|
+
const durationMs2 = startedAt !== void 0 && endedAt !== void 0 ? endedAt - startedAt : void 0;
|
|
3986
|
+
let correlationId;
|
|
3987
|
+
let jobId;
|
|
3988
|
+
let workflowId;
|
|
3989
|
+
for (const run of sessionRuns) {
|
|
3990
|
+
const meta2 = extractSessionWorkflowMetadata(run.metadata);
|
|
3991
|
+
if (!correlationId && meta2?.correlationId) correlationId = meta2.correlationId;
|
|
3992
|
+
if (!jobId && meta2?.jobId) jobId = meta2.jobId;
|
|
3993
|
+
if (!workflowId && meta2?.workflowName) workflowId = meta2.workflowName;
|
|
3994
|
+
else if (!workflowId && meta2?.workflowStep) workflowId = meta2.workflowStep;
|
|
4178
3995
|
}
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
3996
|
+
const lastMs = latestActivityMs(sessionRuns);
|
|
3997
|
+
const lastActivity = lastMs > 0 ? new Date(lastMs).toISOString() : (/* @__PURE__ */ new Date(0)).toISOString();
|
|
3998
|
+
const retryCount2 = summary.retries.filter(
|
|
3999
|
+
(retry) => retry.retryOf !== void 0 || (retry.attempt ?? 0) > 1
|
|
4000
|
+
).length;
|
|
4184
4001
|
return {
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4002
|
+
...summary,
|
|
4003
|
+
status: deriveSessionStatus(sessionRuns, options),
|
|
4004
|
+
startedAt,
|
|
4005
|
+
endedAt,
|
|
4006
|
+
durationMs: durationMs2,
|
|
4007
|
+
correlationId,
|
|
4008
|
+
jobId,
|
|
4009
|
+
workflowId,
|
|
4010
|
+
lastError: deriveLastError(sessionRuns),
|
|
4011
|
+
lastActivity,
|
|
4012
|
+
retryCount: retryCount2,
|
|
4013
|
+
observationSummary: deriveObservationSummary(sessionRuns),
|
|
4014
|
+
checkSummary: deriveCheckSummary(sessionRuns)
|
|
4189
4015
|
};
|
|
4190
4016
|
}
|
|
4191
|
-
var
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
init_status();
|
|
4017
|
+
var DEFAULT_STALE_THRESHOLD_MS, EXPLICIT_STATUS_PRIORITY, EXPLICIT_SESSION_STATUSES;
|
|
4018
|
+
var init_status = __esm({
|
|
4019
|
+
"packages/core/src/sessions/status.ts"() {
|
|
4195
4020
|
init_metadata();
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
return void 0;
|
|
4213
|
-
}
|
|
4214
|
-
}
|
|
4215
|
-
async function isAgentInspectTrace(filePath) {
|
|
4216
|
-
try {
|
|
4217
|
-
const rl = readline.createInterface({
|
|
4218
|
-
input: fs.createReadStream(filePath, { encoding: "utf8" }),
|
|
4219
|
-
crlfDelay: Infinity
|
|
4220
|
-
});
|
|
4221
|
-
let checked = 0;
|
|
4222
|
-
for await (const line of rl) {
|
|
4223
|
-
const trimmed = line.trim();
|
|
4224
|
-
if (trimmed === "") continue;
|
|
4225
|
-
const parsed = safeParse(trimmed);
|
|
4226
|
-
if (!parsed) continue;
|
|
4227
|
-
if (!isRecord6(parsed)) continue;
|
|
4228
|
-
checked += 1;
|
|
4229
|
-
if (isTraceEvent(parsed)) return true;
|
|
4230
|
-
const ev = parsed.event;
|
|
4231
|
-
const runId = parsed.runId;
|
|
4232
|
-
if (typeof ev === "string" && KNOWN_EVENTS.has(ev) && typeof runId === "string") {
|
|
4233
|
-
return true;
|
|
4234
|
-
}
|
|
4235
|
-
if (checked >= 20) break;
|
|
4236
|
-
}
|
|
4237
|
-
return false;
|
|
4238
|
-
} catch {
|
|
4239
|
-
return false;
|
|
4240
|
-
}
|
|
4241
|
-
}
|
|
4242
|
-
var KNOWN_EVENTS;
|
|
4243
|
-
var init_trace_verification = __esm({
|
|
4244
|
-
"packages/core/src/trace-verification.ts"() {
|
|
4245
|
-
init_types();
|
|
4246
|
-
KNOWN_EVENTS = /* @__PURE__ */ new Set([
|
|
4247
|
-
"run_started",
|
|
4248
|
-
"run_completed",
|
|
4249
|
-
"step_started",
|
|
4250
|
-
"step_completed"
|
|
4021
|
+
DEFAULT_STALE_THRESHOLD_MS = 864e5;
|
|
4022
|
+
EXPLICIT_STATUS_PRIORITY = {
|
|
4023
|
+
error: 5,
|
|
4024
|
+
waiting_input: 4,
|
|
4025
|
+
idle: 3,
|
|
4026
|
+
stale: 2,
|
|
4027
|
+
completed: 1
|
|
4028
|
+
};
|
|
4029
|
+
EXPLICIT_SESSION_STATUSES = /* @__PURE__ */ new Set([
|
|
4030
|
+
"running",
|
|
4031
|
+
"waiting_input",
|
|
4032
|
+
"idle",
|
|
4033
|
+
"completed",
|
|
4034
|
+
"error",
|
|
4035
|
+
"stale",
|
|
4036
|
+
"unknown"
|
|
4251
4037
|
]);
|
|
4252
4038
|
}
|
|
4253
4039
|
});
|
|
4254
4040
|
|
|
4255
|
-
// packages/core/src/
|
|
4256
|
-
function
|
|
4257
|
-
const
|
|
4258
|
-
|
|
4259
|
-
|
|
4041
|
+
// packages/core/src/sessions/activity.ts
|
|
4042
|
+
function statusLine(session) {
|
|
4043
|
+
const name = session.workflowId ?? session.correlationId ?? session.sessionId;
|
|
4044
|
+
const status = session.status;
|
|
4045
|
+
if (session.lastError) {
|
|
4046
|
+
return `${name} session ${session.sessionId} failed at ${session.lastError.message}`;
|
|
4260
4047
|
}
|
|
4261
|
-
|
|
4262
|
-
}
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
if (run.endedAt !== void 0 && Number.isFinite(run.endedAt)) return run.endedAt;
|
|
4266
|
-
return void 0;
|
|
4048
|
+
if (session.observationSummary) {
|
|
4049
|
+
return `${name} session ${session.sessionId} ${status} with observation warning`;
|
|
4050
|
+
}
|
|
4051
|
+
return `${name} session ${session.sessionId} ${status}`;
|
|
4267
4052
|
}
|
|
4268
|
-
function
|
|
4269
|
-
|
|
4270
|
-
const
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4053
|
+
function parseSinceMs(since, nowMs) {
|
|
4054
|
+
if (!since || since.trim() === "") return nowMs - 7 * 864e5;
|
|
4055
|
+
const trimmed = since.trim().toLowerCase();
|
|
4056
|
+
const match = /^(\d+)([smhd])$/.exec(trimmed);
|
|
4057
|
+
if (!match) return nowMs - 7 * 864e5;
|
|
4058
|
+
const amount = Number.parseInt(match[1], 10);
|
|
4059
|
+
const unit = match[2];
|
|
4060
|
+
const mult = unit === "s" ? 1e3 : unit === "m" ? 6e4 : unit === "h" ? 36e5 : 864e5;
|
|
4061
|
+
return nowMs - amount * mult;
|
|
4062
|
+
}
|
|
4063
|
+
function isFailed(status) {
|
|
4064
|
+
return status === "error";
|
|
4065
|
+
}
|
|
4066
|
+
function isStale(status) {
|
|
4067
|
+
return status === "stale";
|
|
4068
|
+
}
|
|
4069
|
+
function guardrailWarnings(session) {
|
|
4070
|
+
const summary = session.checkSummary;
|
|
4071
|
+
if (!summary) return 0;
|
|
4072
|
+
return summary.warn;
|
|
4073
|
+
}
|
|
4074
|
+
function buildActivitySummary(index, options = {}) {
|
|
4075
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
4076
|
+
const sinceMs = parseSinceMs(options.since, nowMs);
|
|
4077
|
+
const sinceIso = new Date(sinceMs).toISOString();
|
|
4078
|
+
const limit = Number.isInteger(options.limit) && options.limit > 0 ? options.limit : 20;
|
|
4079
|
+
const inWindow = index.sessions.filter((session) => {
|
|
4080
|
+
const activityMs2 = Date.parse(session.lastActivity);
|
|
4081
|
+
return Number.isFinite(activityMs2) && activityMs2 >= sinceMs;
|
|
4082
|
+
});
|
|
4083
|
+
const entries = [...inWindow].sort((a, b) => Date.parse(b.lastActivity) - Date.parse(a.lastActivity)).slice(0, limit).map((session) => ({
|
|
4084
|
+
sessionId: session.sessionId,
|
|
4085
|
+
status: session.status,
|
|
4086
|
+
summary: statusLine(session),
|
|
4087
|
+
lastActivity: session.lastActivity,
|
|
4088
|
+
runCount: session.runIds.length
|
|
4089
|
+
}));
|
|
4090
|
+
let failed = 0;
|
|
4091
|
+
let stale = 0;
|
|
4092
|
+
let guardrailWarningTotal = 0;
|
|
4093
|
+
for (const session of inWindow) {
|
|
4094
|
+
if (isFailed(session.status)) failed += 1;
|
|
4095
|
+
if (isStale(session.status)) stale += 1;
|
|
4096
|
+
guardrailWarningTotal += guardrailWarnings(session);
|
|
4276
4097
|
}
|
|
4277
|
-
return
|
|
4098
|
+
return {
|
|
4099
|
+
since: sinceIso,
|
|
4100
|
+
sessions: inWindow.length,
|
|
4101
|
+
failed,
|
|
4102
|
+
stale,
|
|
4103
|
+
guardrailWarnings: guardrailWarningTotal,
|
|
4104
|
+
entries
|
|
4105
|
+
};
|
|
4278
4106
|
}
|
|
4279
|
-
function
|
|
4280
|
-
|
|
4107
|
+
function renderActivitySummaryHuman(summary, options) {
|
|
4108
|
+
const lines = [];
|
|
4109
|
+
const nowMs = Date.now();
|
|
4110
|
+
const todayStart = new Date(nowMs);
|
|
4111
|
+
todayStart.setHours(0, 0, 0, 0);
|
|
4112
|
+
const todayMs = todayStart.getTime();
|
|
4113
|
+
const today = summary.entries.filter(
|
|
4114
|
+
(entry) => Date.parse(entry.lastActivity) >= todayMs
|
|
4115
|
+
);
|
|
4116
|
+
if (today.length > 0) {
|
|
4117
|
+
lines.push("Today");
|
|
4118
|
+
for (const entry of today) {
|
|
4119
|
+
lines.push(` ${entry.summary}`);
|
|
4120
|
+
}
|
|
4121
|
+
lines.push("");
|
|
4122
|
+
}
|
|
4123
|
+
lines.push(`Since ${summary.since}`);
|
|
4124
|
+
lines.push(` ${summary.sessions} sessions`);
|
|
4125
|
+
lines.push(` ${summary.failed} failed`);
|
|
4126
|
+
lines.push(` ${summary.stale} stale`);
|
|
4127
|
+
lines.push(` ${summary.guardrailWarnings} guardrail warnings`);
|
|
4128
|
+
return lines.join("\n");
|
|
4281
4129
|
}
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
const sessionId = options.sessionId?.trim();
|
|
4285
|
-
const since = options.since?.trim();
|
|
4286
|
-
const modes = [runId ? 1 : 0, sessionId ? 1 : 0, since ? 1 : 0].reduce((a, b) => a + b, 0);
|
|
4287
|
-
if (modes === 0) {
|
|
4288
|
-
throw new Error(
|
|
4289
|
-
"bundle requires a run id, --session <sessionId>, or --since <duration>."
|
|
4290
|
-
);
|
|
4130
|
+
var init_activity = __esm({
|
|
4131
|
+
"packages/core/src/sessions/activity.ts"() {
|
|
4291
4132
|
}
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4133
|
+
});
|
|
4134
|
+
|
|
4135
|
+
// packages/core/src/sessions/types.ts
|
|
4136
|
+
var init_types3 = __esm({
|
|
4137
|
+
"packages/core/src/sessions/types.ts"() {
|
|
4296
4138
|
}
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4139
|
+
});
|
|
4140
|
+
|
|
4141
|
+
// packages/core/src/sessions/load.ts
|
|
4142
|
+
async function enrichSessionRunRecord(meta2) {
|
|
4143
|
+
let metadata;
|
|
4144
|
+
try {
|
|
4145
|
+
const events = await readTraceEventsFromFile(meta2.filePath);
|
|
4146
|
+
for (const event of events) {
|
|
4147
|
+
if (event.event !== "run_started") continue;
|
|
4148
|
+
if (event.metadata && typeof event.metadata === "object") {
|
|
4149
|
+
metadata = event.metadata;
|
|
4150
|
+
}
|
|
4151
|
+
break;
|
|
4301
4152
|
}
|
|
4302
|
-
|
|
4153
|
+
} catch {
|
|
4154
|
+
}
|
|
4155
|
+
return {
|
|
4156
|
+
runId: meta2.runId,
|
|
4157
|
+
name: meta2.name,
|
|
4158
|
+
status: meta2.status,
|
|
4159
|
+
startedAt: meta2.startedAt,
|
|
4160
|
+
endedAt: meta2.endedAt,
|
|
4161
|
+
durationMs: meta2.durationMs,
|
|
4162
|
+
filePath: meta2.filePath,
|
|
4163
|
+
metadata
|
|
4164
|
+
};
|
|
4165
|
+
}
|
|
4166
|
+
async function loadSessionRunRecords(metas) {
|
|
4167
|
+
const out = [];
|
|
4168
|
+
for (const meta2 of metas) {
|
|
4169
|
+
out.push(await enrichSessionRunRecord(meta2));
|
|
4170
|
+
}
|
|
4171
|
+
return out;
|
|
4172
|
+
}
|
|
4173
|
+
var init_load = __esm({
|
|
4174
|
+
"packages/core/src/sessions/load.ts"() {
|
|
4175
|
+
init_storage();
|
|
4303
4176
|
}
|
|
4177
|
+
});
|
|
4178
|
+
|
|
4179
|
+
// packages/core/src/sessions/scope.ts
|
|
4180
|
+
function filterMetasBySessionScope(metas, records, options) {
|
|
4181
|
+
const sessionId = options.sessionId?.trim();
|
|
4182
|
+
const groupId = options.groupId?.trim();
|
|
4183
|
+
const warnings = [];
|
|
4304
4184
|
if (sessionId) {
|
|
4305
|
-
const
|
|
4185
|
+
const index = buildSessionIndex(records, {
|
|
4186
|
+
correlateByGroupId: options.correlateByGroupId === true
|
|
4187
|
+
});
|
|
4188
|
+
warnings.push(...index.warnings);
|
|
4189
|
+
const session = index.sessions.find((item) => item.sessionId === sessionId);
|
|
4306
4190
|
if (!session) {
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4191
|
+
return {
|
|
4192
|
+
metas: [],
|
|
4193
|
+
scopeLabel: sessionId,
|
|
4194
|
+
scopeKind: "session",
|
|
4195
|
+
runIds: [],
|
|
4196
|
+
warnings,
|
|
4197
|
+
notFound: true
|
|
4198
|
+
};
|
|
4311
4199
|
}
|
|
4200
|
+
const runIdSet = new Set(session.runIds);
|
|
4201
|
+
const filtered = metas.filter((meta2) => runIdSet.has(meta2.runId));
|
|
4312
4202
|
return {
|
|
4313
|
-
|
|
4314
|
-
sessionId
|
|
4203
|
+
metas: filtered,
|
|
4204
|
+
scopeLabel: sessionId,
|
|
4205
|
+
scopeKind: "session",
|
|
4206
|
+
runIds: session.runIds,
|
|
4207
|
+
warnings,
|
|
4208
|
+
notFound: false
|
|
4315
4209
|
};
|
|
4316
4210
|
}
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4211
|
+
if (groupId) {
|
|
4212
|
+
const runIds = records.filter((run) => extractSessionWorkflowMetadata(run.metadata)?.groupId === groupId).map((run) => run.runId).sort();
|
|
4213
|
+
if (runIds.length === 0) {
|
|
4214
|
+
return {
|
|
4215
|
+
metas: [],
|
|
4216
|
+
scopeLabel: groupId,
|
|
4217
|
+
scopeKind: "group",
|
|
4218
|
+
runIds: [],
|
|
4219
|
+
warnings,
|
|
4220
|
+
notFound: true
|
|
4221
|
+
};
|
|
4222
|
+
}
|
|
4223
|
+
const runIdSet = new Set(runIds);
|
|
4224
|
+
return {
|
|
4225
|
+
metas: metas.filter((meta2) => runIdSet.has(meta2.runId)),
|
|
4226
|
+
scopeLabel: groupId,
|
|
4227
|
+
scopeKind: "group",
|
|
4228
|
+
runIds,
|
|
4229
|
+
warnings,
|
|
4230
|
+
notFound: false
|
|
4231
|
+
};
|
|
4320
4232
|
}
|
|
4321
|
-
return {
|
|
4233
|
+
return {
|
|
4234
|
+
metas: [...metas],
|
|
4235
|
+
scopeLabel: "",
|
|
4236
|
+
scopeKind: "session",
|
|
4237
|
+
runIds: [],
|
|
4238
|
+
warnings,
|
|
4239
|
+
notFound: false
|
|
4240
|
+
};
|
|
4322
4241
|
}
|
|
4323
|
-
var
|
|
4324
|
-
"packages/core/src/
|
|
4325
|
-
|
|
4242
|
+
var init_scope = __esm({
|
|
4243
|
+
"packages/core/src/sessions/scope.ts"() {
|
|
4244
|
+
init_sessions();
|
|
4245
|
+
init_metadata();
|
|
4326
4246
|
}
|
|
4327
4247
|
});
|
|
4328
4248
|
|
|
4329
|
-
// packages/core/src/
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
if (statuses.some((status) => status === "SAFE WITH WARNINGS")) return "SAFE WITH WARNINGS";
|
|
4335
|
-
return "SAFE";
|
|
4336
|
-
}
|
|
4337
|
-
function toMetadataSafeStatus(status) {
|
|
4338
|
-
if (status === "SAFE WITH WARNINGS") return "SAFE_WITH_WARNINGS";
|
|
4339
|
-
return status;
|
|
4340
|
-
}
|
|
4341
|
-
function bundleFailsOnSafety(status, allowUnsafe) {
|
|
4342
|
-
if (allowUnsafe) return false;
|
|
4343
|
-
return status === "UNSAFE" || status === "UNKNOWN";
|
|
4344
|
-
}
|
|
4345
|
-
var init_safety_status = __esm({
|
|
4346
|
-
"packages/core/src/bundle/safety-status.ts"() {
|
|
4249
|
+
// packages/core/src/sessions/cohort.ts
|
|
4250
|
+
var init_cohort = __esm({
|
|
4251
|
+
"packages/core/src/sessions/cohort.ts"() {
|
|
4252
|
+
init_sessions();
|
|
4253
|
+
init_metadata();
|
|
4347
4254
|
}
|
|
4348
4255
|
});
|
|
4349
4256
|
|
|
4350
|
-
// packages/core/src/
|
|
4351
|
-
function
|
|
4352
|
-
const aggregate = aggregateBundleSafeStatus(
|
|
4353
|
-
parts.checks.runs.map((run) => run.status)
|
|
4354
|
-
);
|
|
4257
|
+
// packages/core/src/sessions/checks.ts
|
|
4258
|
+
function emptySummary() {
|
|
4355
4259
|
return {
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
...parts.resolve.since !== void 0 ? { since: parts.resolve.since } : {}
|
|
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
|
|
4366
4269
|
};
|
|
4367
4270
|
}
|
|
4368
|
-
function
|
|
4271
|
+
function mergeSummary(target, source) {
|
|
4369
4272
|
return {
|
|
4370
|
-
|
|
4371
|
-
|
|
4273
|
+
passed: target.passed + source.passed,
|
|
4274
|
+
failed: target.failed + source.failed,
|
|
4275
|
+
warnings: target.warnings + source.warnings,
|
|
4276
|
+
errors: target.errors + source.errors,
|
|
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
|
|
4372
4282
|
};
|
|
4373
4283
|
}
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
"packages/core/src/bundle/manifest.ts"() {
|
|
4377
|
-
init_safety_status();
|
|
4378
|
-
BUNDLE_NOTE = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification. Review before sharing.";
|
|
4379
|
-
PLACEHOLDER_NOTE = "No eval or performance artifacts were requested for this bundle.";
|
|
4380
|
-
}
|
|
4381
|
-
});
|
|
4382
|
-
|
|
4383
|
-
// packages/core/src/bundle/summary.ts
|
|
4384
|
-
function markdownTable(rows) {
|
|
4385
|
-
const lines = ["| Field | Value |", "| --- | --- |"];
|
|
4386
|
-
for (const [key, value] of rows) {
|
|
4387
|
-
lines.push(`| ${key} | ${value ?? "unknown"} |`);
|
|
4388
|
-
}
|
|
4389
|
-
return lines.join("\n");
|
|
4390
|
-
}
|
|
4391
|
-
function buildBundleSummaryMarkdown(parts) {
|
|
4392
|
-
const { metadata, checks: checks2, redaction } = parts;
|
|
4393
|
-
const lines = [
|
|
4394
|
-
"# AgentInspect trace bundle",
|
|
4395
|
-
"",
|
|
4396
|
-
metadata.note,
|
|
4397
|
-
"",
|
|
4398
|
-
"## Overview",
|
|
4399
|
-
"",
|
|
4400
|
-
markdownTable([
|
|
4401
|
-
["Created", metadata.createdAt],
|
|
4402
|
-
["AgentInspect", metadata.agentInspectVersion],
|
|
4403
|
-
["Redaction profile", metadata.redactionProfile],
|
|
4404
|
-
["Safe status", metadata.safeStatus],
|
|
4405
|
-
["Source traces", metadata.sourceTraceCount],
|
|
4406
|
-
["Runs", metadata.runIds.join(", ")],
|
|
4407
|
-
...metadata.sessionId ? [["Session", metadata.sessionId]] : [],
|
|
4408
|
-
...metadata.since ? [["Since", metadata.since]] : []
|
|
4409
|
-
]),
|
|
4410
|
-
"",
|
|
4411
|
-
"## Safety checks",
|
|
4412
|
-
"",
|
|
4413
|
-
`Aggregate: **${checks2.aggregateStatus}**`,
|
|
4414
|
-
""
|
|
4415
|
-
];
|
|
4416
|
-
for (const run of checks2.runs) {
|
|
4417
|
-
const source = run.sourceStatus !== void 0 && run.sourceStatus !== run.status ? `; source ${run.sourceStatus}` : "";
|
|
4418
|
-
lines.push(
|
|
4419
|
-
`- \`${run.runId}\`: artifact ${run.status}${source} (${run.findings} finding(s), ${run.errors} error(s), ${run.warnings} warning(s))`
|
|
4420
|
-
);
|
|
4421
|
-
}
|
|
4422
|
-
lines.push("", "## Redaction", "", `Total findings: ${redaction.totalFindings}`, "");
|
|
4423
|
-
for (const run of redaction.runs) {
|
|
4424
|
-
const detectors = run.detectors.length > 0 ? run.detectors.join(", ") : "none";
|
|
4425
|
-
lines.push(`- \`${run.runId}\`: ${run.findings} finding(s); detectors: ${detectors}`);
|
|
4426
|
-
}
|
|
4427
|
-
lines.push(
|
|
4428
|
-
"",
|
|
4429
|
-
"## Files",
|
|
4430
|
-
"",
|
|
4431
|
-
...metadata.files.map((file) => `- \`${file}\``),
|
|
4432
|
-
"",
|
|
4433
|
-
"_Review every generated artifact before sharing outside your team._",
|
|
4434
|
-
""
|
|
4435
|
-
);
|
|
4436
|
-
return lines.join("\n");
|
|
4437
|
-
}
|
|
4438
|
-
var init_summary = __esm({
|
|
4439
|
-
"packages/core/src/bundle/summary.ts"() {
|
|
4440
|
-
}
|
|
4441
|
-
});
|
|
4442
|
-
function sanitizeBundleRunId(runId) {
|
|
4443
|
-
let safe = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
|
|
4444
|
-
safe = path33__default.default.basename(safe);
|
|
4445
|
-
safe = safe.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
4446
|
-
if (safe === "" || safe === "." || safe === "..") {
|
|
4447
|
-
safe = "run_unknown";
|
|
4448
|
-
}
|
|
4449
|
-
return safe;
|
|
4450
|
-
}
|
|
4451
|
-
function bundleRunAssetRelativePath(runId, extension) {
|
|
4452
|
-
const safe = sanitizeBundleRunId(runId);
|
|
4453
|
-
const ext = extension.startsWith(".") ? extension : `.${extension}`;
|
|
4454
|
-
return path33__default.default.posix.join("assets", "runs", `${safe}${ext}`);
|
|
4284
|
+
function sessionDiagnostic(code, message) {
|
|
4285
|
+
return { code, message, severity: "error" };
|
|
4455
4286
|
}
|
|
4456
|
-
function
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4287
|
+
function aggregateSessionCheckResults(perRun, scope) {
|
|
4288
|
+
if (scope.notFound) {
|
|
4289
|
+
return {
|
|
4290
|
+
ok: false,
|
|
4291
|
+
status: "error",
|
|
4292
|
+
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
4293
|
+
scopeKind: scope.scopeKind,
|
|
4294
|
+
scopeLabel: scope.scopeLabel,
|
|
4295
|
+
runIds: [],
|
|
4296
|
+
runResults: [],
|
|
4297
|
+
summary: { ...emptySummary(), errors: 1 },
|
|
4298
|
+
findings: [],
|
|
4299
|
+
diagnostics: [
|
|
4300
|
+
sessionDiagnostic(
|
|
4301
|
+
"AI_CHECK_INVALID_ARGUMENTS",
|
|
4302
|
+
`${scope.scopeKind} not found: ${scope.scopeLabel}`
|
|
4303
|
+
)
|
|
4304
|
+
],
|
|
4305
|
+
ruleExecutions: [],
|
|
4306
|
+
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
4307
|
+
};
|
|
4461
4308
|
}
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4309
|
+
if (scope.empty || perRun.length === 0) {
|
|
4310
|
+
return {
|
|
4311
|
+
ok: false,
|
|
4312
|
+
status: "error",
|
|
4313
|
+
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
4314
|
+
scopeKind: scope.scopeKind,
|
|
4315
|
+
scopeLabel: scope.scopeLabel,
|
|
4316
|
+
runIds: scope.runIds,
|
|
4317
|
+
runResults: [],
|
|
4318
|
+
summary: { ...emptySummary(), errors: 1 },
|
|
4319
|
+
findings: [],
|
|
4320
|
+
diagnostics: [
|
|
4321
|
+
sessionDiagnostic(
|
|
4322
|
+
"AI_CHECK_TRACE_UNREADABLE",
|
|
4323
|
+
`No readable traces in ${scope.scopeKind}: ${scope.scopeLabel}`
|
|
4324
|
+
)
|
|
4325
|
+
],
|
|
4326
|
+
ruleExecutions: [],
|
|
4327
|
+
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
4328
|
+
};
|
|
4468
4329
|
}
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4330
|
+
let summary = emptySummary();
|
|
4331
|
+
const findings = [];
|
|
4332
|
+
const diagnostics = [];
|
|
4333
|
+
const ruleExecutions = [];
|
|
4334
|
+
const runResults = [];
|
|
4335
|
+
for (const result of perRun) {
|
|
4336
|
+
summary = mergeSummary(summary, result.summary);
|
|
4337
|
+
findings.push(...result.findings);
|
|
4338
|
+
diagnostics.push(...result.diagnostics);
|
|
4339
|
+
ruleExecutions.push(...result.ruleExecutions ?? []);
|
|
4340
|
+
if (result.runId) {
|
|
4341
|
+
runResults.push({ runId: result.runId, status: result.status });
|
|
4342
|
+
}
|
|
4472
4343
|
}
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4344
|
+
runResults.sort((a, b) => a.runId.localeCompare(b.runId));
|
|
4345
|
+
findings.sort((a, b) => {
|
|
4346
|
+
const runCmp = (a.evidence[0]?.runId ?? "").localeCompare(
|
|
4347
|
+
b.evidence[0]?.runId ?? ""
|
|
4348
|
+
);
|
|
4349
|
+
if (runCmp !== 0) return runCmp;
|
|
4350
|
+
return a.ruleId.localeCompare(b.ruleId);
|
|
4351
|
+
});
|
|
4352
|
+
ruleExecutions.sort((a, b) => {
|
|
4353
|
+
const runCmp = (a.runId ?? "").localeCompare(b.runId ?? "");
|
|
4354
|
+
if (runCmp !== 0) return runCmp;
|
|
4355
|
+
return a.ruleId.localeCompare(b.ruleId);
|
|
4356
|
+
});
|
|
4357
|
+
const hasErrors = diagnostics.some((item) => item.severity === "error");
|
|
4358
|
+
const status = hasErrors ? "error" : summary.failed > 0 ? "fail" : "pass";
|
|
4359
|
+
return {
|
|
4360
|
+
ok: status === "pass",
|
|
4361
|
+
status,
|
|
4362
|
+
format: perRun[0]?.format ?? "agent-inspect-jsonl",
|
|
4363
|
+
scopeKind: scope.scopeKind,
|
|
4364
|
+
scopeLabel: scope.scopeLabel,
|
|
4365
|
+
runIds: scope.runIds,
|
|
4366
|
+
runResults,
|
|
4367
|
+
summary,
|
|
4368
|
+
findings,
|
|
4369
|
+
diagnostics,
|
|
4370
|
+
ruleExecutions,
|
|
4371
|
+
...scope.sessionWarnings?.length ? { sessionWarnings: [...scope.sessionWarnings] } : {}
|
|
4372
|
+
};
|
|
4479
4373
|
}
|
|
4480
|
-
var
|
|
4481
|
-
"packages/core/src/
|
|
4482
|
-
}
|
|
4483
|
-
});
|
|
4484
|
-
|
|
4485
|
-
// packages/core/src/bundle/index.ts
|
|
4486
|
-
var init_bundle = __esm({
|
|
4487
|
-
"packages/core/src/bundle/index.ts"() {
|
|
4488
|
-
init_resolve();
|
|
4489
|
-
init_manifest();
|
|
4490
|
-
init_summary();
|
|
4491
|
-
init_safety_status();
|
|
4492
|
-
init_paths();
|
|
4374
|
+
var init_checks = __esm({
|
|
4375
|
+
"packages/core/src/sessions/checks.ts"() {
|
|
4493
4376
|
}
|
|
4494
4377
|
});
|
|
4495
4378
|
|
|
4496
|
-
// packages/core/src/
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4379
|
+
// packages/core/src/sessions/index.ts
|
|
4380
|
+
function compareRuns(a, b) {
|
|
4381
|
+
const aStart = a.startedAt ?? 0;
|
|
4382
|
+
const bStart = b.startedAt ?? 0;
|
|
4383
|
+
if (aStart !== bStart) return aStart - bStart;
|
|
4384
|
+
return a.runId.localeCompare(b.runId);
|
|
4385
|
+
}
|
|
4386
|
+
function buildGroups(runIds, metaByRunId) {
|
|
4387
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
4388
|
+
for (const runId of runIds) {
|
|
4389
|
+
const groupId = metaByRunId.get(runId)?.groupId;
|
|
4390
|
+
if (!groupId) continue;
|
|
4391
|
+
const existing = byGroup.get(groupId);
|
|
4392
|
+
if (existing) {
|
|
4393
|
+
existing.runIds.push(runId);
|
|
4394
|
+
} else {
|
|
4395
|
+
byGroup.set(groupId, {
|
|
4396
|
+
groupId,
|
|
4397
|
+
parentGroupId: metaByRunId.get(runId)?.parentGroupId,
|
|
4398
|
+
runIds: [runId]
|
|
4399
|
+
});
|
|
4400
|
+
}
|
|
4512
4401
|
}
|
|
4513
|
-
return
|
|
4402
|
+
return [...byGroup.values()].map((group) => ({
|
|
4403
|
+
...group,
|
|
4404
|
+
runIds: [...group.runIds].sort((a, b) => a.localeCompare(b))
|
|
4405
|
+
}));
|
|
4514
4406
|
}
|
|
4515
|
-
function
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
return
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4407
|
+
function buildHandoffs(runIds, metaByRunId, warnings, sessionId) {
|
|
4408
|
+
const edges = [];
|
|
4409
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4410
|
+
const pushEdge = (edge) => {
|
|
4411
|
+
const key = `${edge.from}->${edge.to}:${edge.confidence}`;
|
|
4412
|
+
if (seen.has(key)) return;
|
|
4413
|
+
seen.add(key);
|
|
4414
|
+
edges.push(edge);
|
|
4415
|
+
};
|
|
4416
|
+
for (const runId of runIds) {
|
|
4417
|
+
const meta2 = metaByRunId.get(runId);
|
|
4418
|
+
if (!meta2) continue;
|
|
4419
|
+
if (meta2.handoffFrom && meta2.handoffTo) {
|
|
4420
|
+
pushEdge({
|
|
4421
|
+
from: meta2.handoffFrom,
|
|
4422
|
+
to: meta2.handoffTo,
|
|
4423
|
+
source: "manual",
|
|
4424
|
+
confidence: "explicit"
|
|
4425
|
+
});
|
|
4426
|
+
continue;
|
|
4427
|
+
}
|
|
4428
|
+
if (meta2.handoffFrom) {
|
|
4429
|
+
pushEdge({
|
|
4430
|
+
from: meta2.handoffFrom,
|
|
4431
|
+
to: runId,
|
|
4432
|
+
source: "manual",
|
|
4433
|
+
confidence: "explicit"
|
|
4434
|
+
});
|
|
4435
|
+
}
|
|
4436
|
+
if (meta2.handoffTo) {
|
|
4437
|
+
pushEdge({
|
|
4438
|
+
from: runId,
|
|
4439
|
+
to: meta2.handoffTo,
|
|
4440
|
+
source: "manual",
|
|
4441
|
+
confidence: "explicit"
|
|
4442
|
+
});
|
|
4443
|
+
}
|
|
4444
|
+
if (meta2.subAgentId && meta2.parentGroupId && !meta2.handoffFrom && !meta2.handoffTo) {
|
|
4445
|
+
pushEdge({
|
|
4446
|
+
from: meta2.parentGroupId,
|
|
4447
|
+
to: meta2.subAgentId,
|
|
4448
|
+
source: "inferred",
|
|
4449
|
+
confidence: "correlated"
|
|
4450
|
+
});
|
|
4451
|
+
warnings.push({
|
|
4452
|
+
code: "ambiguous-handoff-endpoints",
|
|
4453
|
+
message: "Handoff inferred from parentGroupId and subAgentId without explicit handoffFrom/handoffTo.",
|
|
4454
|
+
runId,
|
|
4455
|
+
sessionId
|
|
4456
|
+
});
|
|
4457
|
+
}
|
|
4528
4458
|
}
|
|
4529
|
-
return
|
|
4459
|
+
return edges.sort((a, b) => {
|
|
4460
|
+
const from = a.from.localeCompare(b.from);
|
|
4461
|
+
if (from !== 0) return from;
|
|
4462
|
+
return a.to.localeCompare(b.to);
|
|
4463
|
+
});
|
|
4530
4464
|
}
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4465
|
+
function buildRetries(runIds, metaByRunId, warnings, sessionId) {
|
|
4466
|
+
const retries = [];
|
|
4467
|
+
for (const runId of runIds) {
|
|
4468
|
+
const meta2 = metaByRunId.get(runId);
|
|
4469
|
+
if (!meta2) continue;
|
|
4470
|
+
if (meta2.retryOf) {
|
|
4471
|
+
retries.push({
|
|
4472
|
+
runId,
|
|
4473
|
+
retryOf: meta2.retryOf,
|
|
4474
|
+
attempt: meta2.attempt,
|
|
4475
|
+
source: "manual",
|
|
4476
|
+
confidence: "explicit"
|
|
4477
|
+
});
|
|
4478
|
+
continue;
|
|
4479
|
+
}
|
|
4480
|
+
if (meta2.attempt !== void 0 && meta2.attempt > 1) {
|
|
4481
|
+
retries.push({
|
|
4482
|
+
runId,
|
|
4483
|
+
attempt: meta2.attempt,
|
|
4484
|
+
source: "inferred",
|
|
4485
|
+
confidence: "correlated"
|
|
4486
|
+
});
|
|
4487
|
+
warnings.push({
|
|
4488
|
+
code: "ambiguous-retry-link",
|
|
4489
|
+
message: "attempt > 1 without retryOf; retry link is correlated only.",
|
|
4490
|
+
runId,
|
|
4491
|
+
sessionId
|
|
4492
|
+
});
|
|
4552
4493
|
}
|
|
4553
4494
|
}
|
|
4554
|
-
return
|
|
4495
|
+
return retries.sort((a, b) => a.runId.localeCompare(b.runId));
|
|
4555
4496
|
}
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
// packages/core/src/evidence/manifest.ts
|
|
4562
|
-
function stable(value) {
|
|
4563
|
-
if (Array.isArray(value)) return value.map(stable);
|
|
4564
|
-
if (value === null || typeof value !== "object") return value;
|
|
4565
|
-
const record = value;
|
|
4566
|
-
return Object.fromEntries(
|
|
4567
|
-
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable(record[key])])
|
|
4497
|
+
function buildCriticalPath(runs, handoffs) {
|
|
4498
|
+
const runById = new Map(runs.map((run) => [run.runId, run]));
|
|
4499
|
+
const explicitTargets = new Set(
|
|
4500
|
+
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.to)
|
|
4568
4501
|
);
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
const
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4502
|
+
const explicitSources = new Set(
|
|
4503
|
+
handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
|
|
4504
|
+
);
|
|
4505
|
+
const ordered = [...runs].sort(compareRuns);
|
|
4506
|
+
const path44 = [];
|
|
4507
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4508
|
+
const pushRun = (run, confidence, source) => {
|
|
4509
|
+
if (visited.has(run.runId)) return;
|
|
4510
|
+
visited.add(run.runId);
|
|
4511
|
+
path44.push({
|
|
4512
|
+
runId: run.runId,
|
|
4513
|
+
name: run.name,
|
|
4514
|
+
startedAt: run.startedAt,
|
|
4515
|
+
durationMs: run.durationMs,
|
|
4516
|
+
confidence,
|
|
4517
|
+
source
|
|
4518
|
+
});
|
|
4519
|
+
};
|
|
4520
|
+
for (const edge of handoffs) {
|
|
4521
|
+
if (edge.confidence !== "explicit") continue;
|
|
4522
|
+
const fromRun = [...runById.values()].find(
|
|
4523
|
+
(run) => run.runId === edge.from || metaRunIdMatches(run, edge.from, runById)
|
|
4524
|
+
);
|
|
4525
|
+
const toRun = [...runById.values()].find(
|
|
4526
|
+
(run) => run.runId === edge.to || metaRunIdMatches(run, edge.to, runById)
|
|
4527
|
+
);
|
|
4528
|
+
if (fromRun) pushRun(fromRun, "explicit", "manual");
|
|
4529
|
+
if (toRun) pushRun(toRun, "explicit", "manual");
|
|
4591
4530
|
}
|
|
4592
|
-
|
|
4593
|
-
|
|
4531
|
+
for (const run of ordered) {
|
|
4532
|
+
if (visited.has(run.runId)) continue;
|
|
4533
|
+
const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
|
|
4534
|
+
pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
|
|
4594
4535
|
}
|
|
4595
|
-
return
|
|
4536
|
+
return path44;
|
|
4596
4537
|
}
|
|
4597
|
-
function
|
|
4598
|
-
const
|
|
4599
|
-
|
|
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));
|
|
4538
|
+
function metaRunIdMatches(run, token, runById) {
|
|
4539
|
+
const meta2 = extractSessionWorkflowMetadata(run.metadata);
|
|
4540
|
+
return meta2?.subAgentId === token || meta2?.groupId === token || runById.has(token);
|
|
4616
4541
|
}
|
|
4617
|
-
function
|
|
4618
|
-
const
|
|
4619
|
-
|
|
4620
|
-
|
|
4542
|
+
function buildSessionIndex(inputRuns, options = {}) {
|
|
4543
|
+
const warnings = [];
|
|
4544
|
+
const runs = [...inputRuns].sort(compareRuns);
|
|
4545
|
+
const metaByRunId = /* @__PURE__ */ new Map();
|
|
4546
|
+
for (const run of runs) {
|
|
4547
|
+
metaByRunId.set(run.runId, extractSessionWorkflowMetadata(run.metadata));
|
|
4621
4548
|
}
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4549
|
+
const sessionsByKey = /* @__PURE__ */ new Map();
|
|
4550
|
+
const unscopedRunIds = [];
|
|
4551
|
+
for (const run of runs) {
|
|
4552
|
+
const meta2 = metaByRunId.get(run.runId);
|
|
4553
|
+
const key = sessionKeyForRun(meta2, {
|
|
4554
|
+
correlateByGroupId: options.correlateByGroupId === true
|
|
4555
|
+
});
|
|
4556
|
+
if (!key) {
|
|
4557
|
+
unscopedRunIds.push(run.runId);
|
|
4558
|
+
continue;
|
|
4628
4559
|
}
|
|
4560
|
+
const bucket = sessionsByKey.get(key) ?? [];
|
|
4561
|
+
bucket.push(run);
|
|
4562
|
+
sessionsByKey.set(key, bucket);
|
|
4629
4563
|
}
|
|
4630
|
-
const
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
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)}`
|
|
4564
|
+
const sessions = [...sessionsByKey.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([sessionId, sessionRuns]) => {
|
|
4565
|
+
const runIds = sessionRuns.map((run) => run.runId).sort();
|
|
4566
|
+
const handoffs = buildHandoffs(runIds, metaByRunId, warnings, sessionId);
|
|
4567
|
+
const retries = buildRetries(runIds, metaByRunId, warnings, sessionId);
|
|
4568
|
+
const groups = buildGroups(runIds, metaByRunId);
|
|
4569
|
+
const criticalPath = buildCriticalPath(sessionRuns, handoffs);
|
|
4570
|
+
const confidences = new Set(handoffs.map((edge) => edge.confidence));
|
|
4571
|
+
if (confidences.has("explicit") && confidences.has("correlated")) {
|
|
4572
|
+
warnings.push({
|
|
4573
|
+
code: "mixed-confidence-group",
|
|
4574
|
+
message: "Session aggregates explicit and correlated handoff edges.",
|
|
4575
|
+
sessionId
|
|
4576
|
+
});
|
|
4577
|
+
}
|
|
4578
|
+
return enrichSessionSummary(
|
|
4579
|
+
{
|
|
4580
|
+
sessionId,
|
|
4581
|
+
runIds,
|
|
4582
|
+
groups,
|
|
4583
|
+
handoffs,
|
|
4584
|
+
retries,
|
|
4585
|
+
criticalPath
|
|
4586
|
+
},
|
|
4587
|
+
runs,
|
|
4588
|
+
{
|
|
4589
|
+
nowMs: options.nowMs,
|
|
4590
|
+
staleThresholdMs: options.staleThresholdMs
|
|
4591
|
+
}
|
|
4667
4592
|
);
|
|
4593
|
+
});
|
|
4594
|
+
if (sessions.length === 0 && runs.length > 0) {
|
|
4595
|
+
warnings.push({
|
|
4596
|
+
code: "missing-session-id",
|
|
4597
|
+
message: "No sessionId (or correlated groupId) found on input runs."
|
|
4598
|
+
});
|
|
4668
4599
|
}
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4600
|
+
warnings.sort((a, b) => {
|
|
4601
|
+
const code = a.code.localeCompare(b.code);
|
|
4602
|
+
if (code !== 0) return code;
|
|
4603
|
+
return (a.runId ?? "").localeCompare(b.runId ?? "");
|
|
4604
|
+
});
|
|
4605
|
+
return {
|
|
4606
|
+
runs,
|
|
4607
|
+
sessions,
|
|
4608
|
+
unscopedRunIds: unscopedRunIds.sort(),
|
|
4609
|
+
warnings
|
|
4610
|
+
};
|
|
4611
|
+
}
|
|
4612
|
+
var init_sessions = __esm({
|
|
4613
|
+
"packages/core/src/sessions/index.ts"() {
|
|
4614
|
+
init_metadata();
|
|
4615
|
+
init_status();
|
|
4616
|
+
init_metadata();
|
|
4617
|
+
init_status();
|
|
4618
|
+
init_activity();
|
|
4619
|
+
init_types3();
|
|
4620
|
+
init_load();
|
|
4621
|
+
init_scope();
|
|
4622
|
+
init_cohort();
|
|
4623
|
+
init_checks();
|
|
4672
4624
|
}
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4625
|
+
});
|
|
4626
|
+
function isRecord7(value) {
|
|
4627
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4628
|
+
}
|
|
4629
|
+
function safeParse(line) {
|
|
4630
|
+
try {
|
|
4631
|
+
return JSON.parse(line);
|
|
4632
|
+
} catch {
|
|
4633
|
+
return void 0;
|
|
4676
4634
|
}
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4635
|
+
}
|
|
4636
|
+
async function isAgentInspectTrace(filePath) {
|
|
4637
|
+
try {
|
|
4638
|
+
const rl = readline.createInterface({
|
|
4639
|
+
input: fs.createReadStream(filePath, { encoding: "utf8" }),
|
|
4640
|
+
crlfDelay: Infinity
|
|
4641
|
+
});
|
|
4642
|
+
let checked = 0;
|
|
4643
|
+
for await (const line of rl) {
|
|
4644
|
+
const trimmed = line.trim();
|
|
4645
|
+
if (trimmed === "") continue;
|
|
4646
|
+
const parsed = safeParse(trimmed);
|
|
4647
|
+
if (!parsed) continue;
|
|
4648
|
+
if (!isRecord7(parsed)) continue;
|
|
4649
|
+
checked += 1;
|
|
4650
|
+
if (isTraceEvent(parsed)) return true;
|
|
4651
|
+
const ev = parsed.event;
|
|
4652
|
+
const runId = parsed.runId;
|
|
4653
|
+
if (typeof ev === "string" && KNOWN_EVENTS.has(ev) && typeof runId === "string") {
|
|
4654
|
+
return true;
|
|
4655
|
+
}
|
|
4656
|
+
if (checked >= 20) break;
|
|
4657
|
+
}
|
|
4658
|
+
return false;
|
|
4659
|
+
} catch {
|
|
4660
|
+
return false;
|
|
4680
4661
|
}
|
|
4681
|
-
|
|
4682
|
-
|
|
4662
|
+
}
|
|
4663
|
+
var KNOWN_EVENTS;
|
|
4664
|
+
var init_trace_verification = __esm({
|
|
4665
|
+
"packages/core/src/trace-verification.ts"() {
|
|
4666
|
+
init_types();
|
|
4667
|
+
KNOWN_EVENTS = /* @__PURE__ */ new Set([
|
|
4668
|
+
"run_started",
|
|
4669
|
+
"run_completed",
|
|
4670
|
+
"step_started",
|
|
4671
|
+
"step_completed"
|
|
4672
|
+
]);
|
|
4683
4673
|
}
|
|
4684
|
-
|
|
4685
|
-
|
|
4674
|
+
});
|
|
4675
|
+
|
|
4676
|
+
// packages/core/src/bundle/resolve.ts
|
|
4677
|
+
function parseSinceCutoff(since) {
|
|
4678
|
+
const trimmed = since.trim();
|
|
4679
|
+
if (trimmed === "") {
|
|
4680
|
+
throw new Error("--since requires a non-empty duration (e.g. 24h, 7d).");
|
|
4686
4681
|
}
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4682
|
+
return Date.now() - parseDuration(trimmed);
|
|
4683
|
+
}
|
|
4684
|
+
function runActivityMs(run) {
|
|
4685
|
+
if (run.startedAt !== void 0 && Number.isFinite(run.startedAt)) return run.startedAt;
|
|
4686
|
+
if (run.endedAt !== void 0 && Number.isFinite(run.endedAt)) return run.endedAt;
|
|
4687
|
+
return void 0;
|
|
4688
|
+
}
|
|
4689
|
+
function runsInSinceWindow(runs, since) {
|
|
4690
|
+
const cutoff = parseSinceCutoff(since);
|
|
4691
|
+
const ids = [];
|
|
4692
|
+
for (const run of runs) {
|
|
4693
|
+
const activity = runActivityMs(run);
|
|
4694
|
+
if (activity !== void 0 && activity >= cutoff) {
|
|
4695
|
+
ids.push(run.runId);
|
|
4696
|
+
}
|
|
4690
4697
|
}
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4698
|
+
return ids.sort((a, b) => a.localeCompare(b));
|
|
4699
|
+
}
|
|
4700
|
+
function findSession(index, sessionId) {
|
|
4701
|
+
return index.sessions.find((session) => session.sessionId === sessionId);
|
|
4702
|
+
}
|
|
4703
|
+
function resolveBundleRunIds(index, runs, options) {
|
|
4704
|
+
const runId = options.runId?.trim();
|
|
4705
|
+
const sessionId = options.sessionId?.trim();
|
|
4706
|
+
const since = options.since?.trim();
|
|
4707
|
+
const modes = [runId ? 1 : 0, sessionId ? 1 : 0, since ? 1 : 0].reduce((a, b) => a + b, 0);
|
|
4708
|
+
if (modes === 0) {
|
|
4709
|
+
throw new Error(
|
|
4710
|
+
"bundle requires a run id, --session <sessionId>, or --since <duration>."
|
|
4711
|
+
);
|
|
4694
4712
|
}
|
|
4695
|
-
if (
|
|
4696
|
-
throw new Error(
|
|
4713
|
+
if (modes > 1) {
|
|
4714
|
+
throw new Error(
|
|
4715
|
+
"bundle accepts only one target: a run id, --session, or --since (not combined)."
|
|
4716
|
+
);
|
|
4697
4717
|
}
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4718
|
+
if (runId) {
|
|
4719
|
+
const known = runs.some((run) => run.runId === runId);
|
|
4720
|
+
if (!known) {
|
|
4721
|
+
throw new Error(`Run "${runId}" was not found in the trace directory.`);
|
|
4701
4722
|
}
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4723
|
+
return { runIds: [runId] };
|
|
4724
|
+
}
|
|
4725
|
+
if (sessionId) {
|
|
4726
|
+
const session = findSession(index, sessionId);
|
|
4727
|
+
if (!session) {
|
|
4728
|
+
throw new Error(`Session "${sessionId}" was not found.`);
|
|
4705
4729
|
}
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
throw new Error(`Evidence file entry requires sha256 hex for ${entry.path}.`);
|
|
4730
|
+
if (session.runIds.length === 0) {
|
|
4731
|
+
throw new Error(`Session "${sessionId}" has no runs to bundle.`);
|
|
4709
4732
|
}
|
|
4733
|
+
return {
|
|
4734
|
+
runIds: [...session.runIds].sort((a, b) => a.localeCompare(b)),
|
|
4735
|
+
sessionId
|
|
4736
|
+
};
|
|
4710
4737
|
}
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
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
|
-
}
|
|
4738
|
+
const runIds = runsInSinceWindow(runs, since);
|
|
4739
|
+
if (runIds.length === 0) {
|
|
4740
|
+
throw new Error(`No runs matched --since ${since}.`);
|
|
4735
4741
|
}
|
|
4736
|
-
return
|
|
4742
|
+
return { runIds, since };
|
|
4737
4743
|
}
|
|
4738
|
-
var
|
|
4739
|
-
"packages/core/src/
|
|
4740
|
-
|
|
4741
|
-
init_paths2();
|
|
4742
|
-
init_types4();
|
|
4744
|
+
var init_resolve = __esm({
|
|
4745
|
+
"packages/core/src/bundle/resolve.ts"() {
|
|
4746
|
+
init_duration();
|
|
4743
4747
|
}
|
|
4744
4748
|
});
|
|
4745
4749
|
|
|
4746
|
-
// packages/core/src/
|
|
4747
|
-
function
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4750
|
+
// packages/core/src/bundle/safety-status.ts
|
|
4751
|
+
function aggregateBundleSafeStatus(statuses) {
|
|
4752
|
+
if (statuses.length === 0) return "UNKNOWN";
|
|
4753
|
+
if (statuses.some((status) => status === "UNSAFE")) return "UNSAFE";
|
|
4754
|
+
if (statuses.some((status) => status === "UNKNOWN")) return "UNKNOWN";
|
|
4755
|
+
if (statuses.some((status) => status === "SAFE WITH WARNINGS")) return "SAFE WITH WARNINGS";
|
|
4756
|
+
return "SAFE";
|
|
4751
4757
|
}
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
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
|
-
AI_TRACE_CONTRACT_INPUT_INVALID: {
|
|
4801
|
-
code: "AI_TRACE_CONTRACT_INPUT_INVALID",
|
|
4802
|
-
summary: "evaluateTraceContract expects ({ read }, contract) or use evaluateTraceContractRead(read, contract).",
|
|
4803
|
-
remediation: "Pass TraceCheckInput as the first argument: evaluateTraceContract({ read }, contract).",
|
|
4804
|
-
relatedCodes: ["AI_CHECK_INVALID_ARGUMENTS"]
|
|
4805
|
-
},
|
|
4806
|
-
AI_INSPECT_RUN_UNSUPPORTED_OPTION: {
|
|
4807
|
-
code: "AI_INSPECT_RUN_UNSUPPORTED_OPTION",
|
|
4808
|
-
summary: 'inspectRun does not accept a "writer" option.',
|
|
4809
|
-
remediation: "Use createInspector({ writer }) then inspector.run(...). inspectRun persists via the global JSONL path (schema 0.1)."
|
|
4810
|
-
}
|
|
4811
|
-
});
|
|
4758
|
+
function toMetadataSafeStatus(status) {
|
|
4759
|
+
if (status === "SAFE WITH WARNINGS") return "SAFE_WITH_WARNINGS";
|
|
4760
|
+
return status;
|
|
4761
|
+
}
|
|
4762
|
+
function bundleFailsOnSafety(status, allowUnsafe) {
|
|
4763
|
+
if (allowUnsafe) return false;
|
|
4764
|
+
return status === "UNSAFE" || status === "UNKNOWN";
|
|
4765
|
+
}
|
|
4766
|
+
var init_safety_status = __esm({
|
|
4767
|
+
"packages/core/src/bundle/safety-status.ts"() {
|
|
4812
4768
|
}
|
|
4813
4769
|
});
|
|
4814
4770
|
|
|
4815
|
-
// packages/core/src/
|
|
4816
|
-
function
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
const value = event.attributes?.legacyEvent;
|
|
4821
|
-
return typeof value === "string" ? value : void 0;
|
|
4822
|
-
}
|
|
4823
|
-
function stepIdOf(event) {
|
|
4824
|
-
const value = event.attributes?.stepId;
|
|
4825
|
-
if (typeof value === "string" && value.trim() !== "") return value;
|
|
4826
|
-
return void 0;
|
|
4827
|
-
}
|
|
4828
|
-
function cloneEvent(event) {
|
|
4771
|
+
// packages/core/src/bundle/manifest.ts
|
|
4772
|
+
function buildBundleMetadata(parts) {
|
|
4773
|
+
const aggregate = aggregateBundleSafeStatus(
|
|
4774
|
+
parts.checks.runs.map((run) => run.status)
|
|
4775
|
+
);
|
|
4829
4776
|
return {
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4777
|
+
createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
4778
|
+
agentInspectVersion: parts.agentInspectVersion,
|
|
4779
|
+
redactionProfile: parts.profile,
|
|
4780
|
+
sourceTraceCount: parts.resolve.runIds.length,
|
|
4781
|
+
runIds: [...parts.resolve.runIds],
|
|
4782
|
+
safeStatus: toMetadataSafeStatus(aggregate),
|
|
4783
|
+
files: [...parts.files].sort((a, b) => a.localeCompare(b)),
|
|
4784
|
+
note: BUNDLE_NOTE,
|
|
4785
|
+
...parts.resolve.sessionId !== void 0 ? { sessionId: parts.resolve.sessionId } : {},
|
|
4786
|
+
...parts.resolve.since !== void 0 ? { since: parts.resolve.since } : {}
|
|
4835
4787
|
};
|
|
4836
4788
|
}
|
|
4837
|
-
function
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4789
|
+
function buildPlaceholderArtifact() {
|
|
4790
|
+
return {
|
|
4791
|
+
status: "not_requested",
|
|
4792
|
+
note: PLACEHOLDER_NOTE
|
|
4841
4793
|
};
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4794
|
+
}
|
|
4795
|
+
var BUNDLE_NOTE, PLACEHOLDER_NOTE;
|
|
4796
|
+
var init_manifest = __esm({
|
|
4797
|
+
"packages/core/src/bundle/manifest.ts"() {
|
|
4798
|
+
init_safety_status();
|
|
4799
|
+
BUNDLE_NOTE = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification. Review before sharing.";
|
|
4800
|
+
PLACEHOLDER_NOTE = "No eval or performance artifacts were requested for this bundle.";
|
|
4846
4801
|
}
|
|
4847
|
-
|
|
4802
|
+
});
|
|
4803
|
+
|
|
4804
|
+
// packages/core/src/bundle/summary.ts
|
|
4805
|
+
function markdownTable(rows) {
|
|
4806
|
+
const lines = ["| Field | Value |", "| --- | --- |"];
|
|
4807
|
+
for (const [key, value] of rows) {
|
|
4808
|
+
lines.push(`| ${key} | ${value ?? "unknown"} |`);
|
|
4809
|
+
}
|
|
4810
|
+
return lines.join("\n");
|
|
4848
4811
|
}
|
|
4849
|
-
function
|
|
4850
|
-
const
|
|
4851
|
-
const
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4812
|
+
function buildBundleSummaryMarkdown(parts) {
|
|
4813
|
+
const { metadata, checks: checks2, redaction } = parts;
|
|
4814
|
+
const lines = [
|
|
4815
|
+
"# AgentInspect trace bundle",
|
|
4816
|
+
"",
|
|
4817
|
+
metadata.note,
|
|
4818
|
+
"",
|
|
4819
|
+
"## Overview",
|
|
4820
|
+
"",
|
|
4821
|
+
markdownTable([
|
|
4822
|
+
["Created", metadata.createdAt],
|
|
4823
|
+
["AgentInspect", metadata.agentInspectVersion],
|
|
4824
|
+
["Redaction profile", metadata.redactionProfile],
|
|
4825
|
+
["Safe status", metadata.safeStatus],
|
|
4826
|
+
["Source traces", metadata.sourceTraceCount],
|
|
4827
|
+
["Runs", metadata.runIds.join(", ")],
|
|
4828
|
+
...metadata.sessionId ? [["Session", metadata.sessionId]] : [],
|
|
4829
|
+
...metadata.since ? [["Since", metadata.since]] : []
|
|
4830
|
+
]),
|
|
4831
|
+
"",
|
|
4832
|
+
"## Safety checks",
|
|
4833
|
+
"",
|
|
4834
|
+
`Aggregate: **${checks2.aggregateStatus}**`,
|
|
4835
|
+
""
|
|
4836
|
+
];
|
|
4837
|
+
for (const run of checks2.runs) {
|
|
4838
|
+
const source = run.sourceStatus !== void 0 && run.sourceStatus !== run.status ? `; source ${run.sourceStatus}` : "";
|
|
4839
|
+
lines.push(
|
|
4840
|
+
`- \`${run.runId}\`: artifact ${run.status}${source} (${run.findings} finding(s), ${run.errors} error(s), ${run.warnings} warning(s))`
|
|
4841
|
+
);
|
|
4842
|
+
}
|
|
4843
|
+
lines.push("", "## Redaction", "", `Total findings: ${redaction.totalFindings}`, "");
|
|
4844
|
+
for (const run of redaction.runs) {
|
|
4845
|
+
const detectors = run.detectors.length > 0 ? run.detectors.join(", ") : "none";
|
|
4846
|
+
lines.push(`- \`${run.runId}\`: ${run.findings} finding(s); detectors: ${detectors}`);
|
|
4847
|
+
}
|
|
4848
|
+
lines.push(
|
|
4849
|
+
"",
|
|
4850
|
+
"## Files",
|
|
4851
|
+
"",
|
|
4852
|
+
...metadata.files.map((file) => `- \`${file}\``),
|
|
4853
|
+
"",
|
|
4854
|
+
"_Review every generated artifact before sharing outside your team._",
|
|
4855
|
+
""
|
|
4856
|
+
);
|
|
4857
|
+
return lines.join("\n");
|
|
4871
4858
|
}
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
}
|
|
4859
|
+
var init_summary = __esm({
|
|
4860
|
+
"packages/core/src/bundle/summary.ts"() {
|
|
4861
|
+
}
|
|
4862
|
+
});
|
|
4863
|
+
function sanitizeBundleRunId(runId) {
|
|
4864
|
+
let safe = typeof runId === "string" && runId.trim() !== "" ? runId.trim() : "run_unknown";
|
|
4865
|
+
safe = path33__default.default.basename(safe);
|
|
4866
|
+
safe = safe.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
4867
|
+
if (safe === "" || safe === "." || safe === "..") {
|
|
4868
|
+
safe = "run_unknown";
|
|
4869
|
+
}
|
|
4870
|
+
return safe;
|
|
4883
4871
|
}
|
|
4884
|
-
function
|
|
4885
|
-
const
|
|
4886
|
-
const
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4872
|
+
function bundleRunAssetRelativePath(runId, extension) {
|
|
4873
|
+
const safe = sanitizeBundleRunId(runId);
|
|
4874
|
+
const ext = extension.startsWith(".") ? extension : `.${extension}`;
|
|
4875
|
+
return path33__default.default.posix.join("assets", "runs", `${safe}${ext}`);
|
|
4876
|
+
}
|
|
4877
|
+
function assertBundlePathContained(outputDir, relativePath) {
|
|
4878
|
+
const base = path33__default.default.resolve(outputDir);
|
|
4879
|
+
const resolved = path33__default.default.resolve(base, relativePath);
|
|
4880
|
+
if (resolved !== base && !resolved.startsWith(base + path33__default.default.sep)) {
|
|
4881
|
+
throw new Error(`Bundle path escapes output directory: ${relativePath}`);
|
|
4891
4882
|
}
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
const
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
const runEvents = byRun.get(runId) ?? [];
|
|
4899
|
-
const starts = [];
|
|
4900
|
-
const completes = [];
|
|
4901
|
-
const others = [];
|
|
4902
|
-
for (const event of runEvents) {
|
|
4903
|
-
const legacy = legacyEvent(event);
|
|
4904
|
-
if (legacy === "step_started" || legacy === "run_started" && event.status === "running") {
|
|
4905
|
-
starts.push(event);
|
|
4906
|
-
} else if (legacy === "step_completed" || legacy === "run_completed") {
|
|
4907
|
-
completes.push(event);
|
|
4908
|
-
} else {
|
|
4909
|
-
others.push(event);
|
|
4910
|
-
}
|
|
4911
|
-
}
|
|
4912
|
-
const usedCompletes = /* @__PURE__ */ new Set();
|
|
4913
|
-
for (const start of starts) {
|
|
4914
|
-
const stepId = stepIdOf(start);
|
|
4915
|
-
let match;
|
|
4916
|
-
if (legacyEvent(start) === "run_started") {
|
|
4917
|
-
const candidates = completes.filter(
|
|
4918
|
-
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "run_completed"
|
|
4919
|
-
);
|
|
4920
|
-
if (candidates.length > 1) {
|
|
4921
|
-
diagnostics.push({
|
|
4922
|
-
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
4923
|
-
message: `Multiple run_completed rows for run ${runId}; using first by eventId.`,
|
|
4924
|
-
eventIds: candidates.map((c) => c.eventId)
|
|
4925
|
-
});
|
|
4926
|
-
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
4927
|
-
}
|
|
4928
|
-
match = candidates[0];
|
|
4929
|
-
} else if (stepId) {
|
|
4930
|
-
const candidates = completes.filter(
|
|
4931
|
-
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "step_completed" && stepIdOf(c) === stepId
|
|
4932
|
-
);
|
|
4933
|
-
if (candidates.length > 1) {
|
|
4934
|
-
diagnostics.push({
|
|
4935
|
-
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
4936
|
-
message: `Multiple step_completed rows for stepId ${stepId}; using first by eventId.`,
|
|
4937
|
-
eventIds: candidates.map((c) => c.eventId)
|
|
4938
|
-
});
|
|
4939
|
-
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
4940
|
-
}
|
|
4941
|
-
match = candidates[0];
|
|
4942
|
-
}
|
|
4943
|
-
if (match) {
|
|
4944
|
-
usedCompletes.add(match.eventId);
|
|
4945
|
-
absorbedIds.add(match.eventId);
|
|
4946
|
-
const paired = pairStartComplete(start, match);
|
|
4947
|
-
logical.push(paired);
|
|
4948
|
-
logicalByRawId.set(start.eventId, paired);
|
|
4949
|
-
logicalByRawId.set(match.eventId, paired);
|
|
4950
|
-
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, paired.eventId);
|
|
4951
|
-
} else {
|
|
4952
|
-
diagnostics.push({
|
|
4953
|
-
code: "AI_LOGICAL_PAIR_UNMATCHED_START",
|
|
4954
|
-
message: `No matching complete for start ${start.eventId}.`,
|
|
4955
|
-
eventIds: [start.eventId]
|
|
4956
|
-
});
|
|
4957
|
-
const alone = asLogical(start);
|
|
4958
|
-
logical.push(alone);
|
|
4959
|
-
logicalByRawId.set(start.eventId, alone);
|
|
4960
|
-
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
4961
|
-
}
|
|
4962
|
-
}
|
|
4963
|
-
for (const complete of completes) {
|
|
4964
|
-
if (usedCompletes.has(complete.eventId)) continue;
|
|
4965
|
-
diagnostics.push({
|
|
4966
|
-
code: "AI_LOGICAL_PAIR_UNMATCHED_COMPLETE",
|
|
4967
|
-
message: `No matching start for complete ${complete.eventId}.`,
|
|
4968
|
-
eventIds: [complete.eventId]
|
|
4969
|
-
});
|
|
4970
|
-
const alone = asLogical(complete);
|
|
4971
|
-
logical.push(alone);
|
|
4972
|
-
logicalByRawId.set(complete.eventId, alone);
|
|
4973
|
-
const stepId = stepIdOf(complete);
|
|
4974
|
-
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
4975
|
-
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
4976
|
-
}
|
|
4977
|
-
}
|
|
4978
|
-
for (const event of others) {
|
|
4979
|
-
const alone = asLogical(event);
|
|
4980
|
-
logical.push(alone);
|
|
4981
|
-
logicalByRawId.set(event.eventId, alone);
|
|
4982
|
-
const stepId = stepIdOf(event);
|
|
4983
|
-
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
4984
|
-
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
4985
|
-
}
|
|
4986
|
-
}
|
|
4883
|
+
return resolved;
|
|
4884
|
+
}
|
|
4885
|
+
function normalizeBundleOutputPath(out, options) {
|
|
4886
|
+
const trimmed = out.trim();
|
|
4887
|
+
if (trimmed === "") {
|
|
4888
|
+
throw new Error("--out requires a non-empty path.");
|
|
4987
4889
|
}
|
|
4988
|
-
const
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
4890
|
+
const resolved = path33__default.default.resolve(trimmed);
|
|
4891
|
+
if (options?.preserveZipExtension !== true && resolved.toLowerCase().endsWith(".zip")) {
|
|
4892
|
+
return resolved.slice(0, -4);
|
|
4893
|
+
}
|
|
4894
|
+
return resolved;
|
|
4895
|
+
}
|
|
4896
|
+
function defaultBundleOutputPath(runIds) {
|
|
4897
|
+
const label = runIds.length === 1 ? sanitizeBundleRunId(runIds[0]) : `multi-${runIds.length}`;
|
|
4898
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
4899
|
+
return path33__default.default.resolve(`agent-inspect-bundle-${label}-${stamp}`);
|
|
4900
|
+
}
|
|
4901
|
+
var init_paths = __esm({
|
|
4902
|
+
"packages/core/src/bundle/paths.ts"() {
|
|
4903
|
+
}
|
|
4904
|
+
});
|
|
4905
|
+
|
|
4906
|
+
// packages/core/src/bundle/index.ts
|
|
4907
|
+
var init_bundle = __esm({
|
|
4908
|
+
"packages/core/src/bundle/index.ts"() {
|
|
4909
|
+
init_resolve();
|
|
4910
|
+
init_manifest();
|
|
4911
|
+
init_summary();
|
|
4912
|
+
init_safety_status();
|
|
4913
|
+
init_paths();
|
|
4914
|
+
}
|
|
4915
|
+
});
|
|
4916
|
+
|
|
4917
|
+
// packages/core/src/evidence/types.ts
|
|
4918
|
+
var EVIDENCE_FORMAT_VERSION, EVIDENCE_ASSESSMENT_NOTE, EVIDENCE_MANIFEST_FILENAME, EVIDENCE_RESOLVED_CONTRACT_FILENAME;
|
|
4919
|
+
var init_types4 = __esm({
|
|
4920
|
+
"packages/core/src/evidence/types.ts"() {
|
|
4921
|
+
EVIDENCE_FORMAT_VERSION = "1.0";
|
|
4922
|
+
EVIDENCE_ASSESSMENT_NOTE = "Best-effort local safety verification only; not a compliance certification.";
|
|
4923
|
+
EVIDENCE_MANIFEST_FILENAME = "evidence.json";
|
|
4924
|
+
EVIDENCE_RESOLVED_CONTRACT_FILENAME = "contract.resolved.json";
|
|
4925
|
+
}
|
|
4926
|
+
});
|
|
4927
|
+
function sha256Hex(data) {
|
|
4928
|
+
const hash = crypto.createHash("sha256");
|
|
4929
|
+
if (typeof data === "string") {
|
|
4930
|
+
hash.update(data, "utf8");
|
|
4931
|
+
} else {
|
|
4932
|
+
hash.update(data);
|
|
4933
|
+
}
|
|
4934
|
+
return hash.digest("hex");
|
|
4935
|
+
}
|
|
4936
|
+
function isSha256Hex(value) {
|
|
4937
|
+
return SHA256_RE.test(value);
|
|
4938
|
+
}
|
|
4939
|
+
function sha256Equals(expected, actual) {
|
|
4940
|
+
if (!isSha256Hex(expected) || !isSha256Hex(actual)) {
|
|
4941
|
+
return false;
|
|
4942
|
+
}
|
|
4943
|
+
const a = expected.toLowerCase();
|
|
4944
|
+
const b = actual.toLowerCase();
|
|
4945
|
+
if (a.length !== b.length) return false;
|
|
4946
|
+
let mismatch = 0;
|
|
4947
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
4948
|
+
mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
4949
|
+
}
|
|
4950
|
+
return mismatch === 0;
|
|
4951
|
+
}
|
|
4952
|
+
var SHA256_RE;
|
|
4953
|
+
var init_hash = __esm({
|
|
4954
|
+
"packages/core/src/evidence/hash.ts"() {
|
|
4955
|
+
SHA256_RE = /^[a-f0-9]{64}$/i;
|
|
4956
|
+
}
|
|
4957
|
+
});
|
|
4958
|
+
function assertEvidenceRelativePath(relativePath) {
|
|
4959
|
+
if (typeof relativePath !== "string" || relativePath.trim() === "") {
|
|
4960
|
+
throw new Error("Evidence file path must be a non-empty relative path.");
|
|
4961
|
+
}
|
|
4962
|
+
const trimmed = relativePath.trim().replaceAll("\\", "/");
|
|
4963
|
+
if (path33__default.default.isAbsolute(trimmed) || trimmed.startsWith("/")) {
|
|
4964
|
+
throw new Error(`Evidence file path must be relative: ${relativePath}`);
|
|
4965
|
+
}
|
|
4966
|
+
const parts = trimmed.split("/").filter((part) => part !== "");
|
|
4967
|
+
if (parts.length === 0) {
|
|
4968
|
+
throw new Error(`Evidence file path must be relative: ${relativePath}`);
|
|
4969
|
+
}
|
|
4970
|
+
for (const part of parts) {
|
|
4971
|
+
if (part === "." || part === "..") {
|
|
4972
|
+
throw new Error(`Evidence file path must not contain "." or "..": ${relativePath}`);
|
|
5008
4973
|
}
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
4974
|
+
}
|
|
4975
|
+
return parts.join("/");
|
|
4976
|
+
}
|
|
4977
|
+
var init_paths2 = __esm({
|
|
4978
|
+
"packages/core/src/evidence/paths.ts"() {
|
|
4979
|
+
}
|
|
4980
|
+
});
|
|
4981
|
+
|
|
4982
|
+
// packages/core/src/evidence/manifest.ts
|
|
4983
|
+
function stable(value) {
|
|
4984
|
+
if (Array.isArray(value)) return value.map(stable);
|
|
4985
|
+
if (value === null || typeof value !== "object") return value;
|
|
4986
|
+
const record = value;
|
|
4987
|
+
return Object.fromEntries(
|
|
4988
|
+
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable(record[key])])
|
|
4989
|
+
);
|
|
4990
|
+
}
|
|
4991
|
+
function serializeEvidenceManifest(manifest) {
|
|
4992
|
+
return `${JSON.stringify(stable(manifest), null, 2)}
|
|
4993
|
+
`;
|
|
4994
|
+
}
|
|
4995
|
+
function inferEvidenceFileRole(relativePath) {
|
|
4996
|
+
const normalized = assertEvidenceRelativePath(relativePath);
|
|
4997
|
+
const base = normalized.includes("/") ? normalized.slice(normalized.lastIndexOf("/") + 1) : normalized;
|
|
4998
|
+
if (base === "evidence.html" || base === "trace.html" || base.endsWith(".html")) {
|
|
4999
|
+
return "report";
|
|
5000
|
+
}
|
|
5001
|
+
if (base === "trace.jsonl" || base.endsWith(".jsonl")) {
|
|
5002
|
+
return "redacted-trace";
|
|
5003
|
+
}
|
|
5004
|
+
if (base === "check-results.json") {
|
|
5005
|
+
return "checks";
|
|
5006
|
+
}
|
|
5007
|
+
if (base === "contract.resolved.json") {
|
|
5008
|
+
return "contract";
|
|
5009
|
+
}
|
|
5010
|
+
if (base === "redaction-report.json") {
|
|
5011
|
+
return "redaction-report";
|
|
5012
|
+
}
|
|
5013
|
+
if (base === "summary.md") {
|
|
5014
|
+
return "summary";
|
|
5015
|
+
}
|
|
5016
|
+
return "other";
|
|
5017
|
+
}
|
|
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
|
+
);
|
|
5044
5026
|
}
|
|
5045
|
-
if (
|
|
5046
|
-
|
|
5047
|
-
code: "AI_LOGICAL_SELF_PARENT_REMOVED",
|
|
5048
|
-
message: formatProgrammaticDiagnostic(
|
|
5049
|
-
"AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
5050
|
-
`Removed self-parent edge on ${event.eventId} (was ${originalParentId}).`
|
|
5051
|
-
),
|
|
5052
|
-
eventIds: [event.eventId]
|
|
5053
|
-
});
|
|
5054
|
-
const { parentId: _drop, ...rest } = event;
|
|
5055
|
-
normalized.push({
|
|
5056
|
-
...rest,
|
|
5057
|
-
projection: {
|
|
5058
|
-
...event.projection,
|
|
5059
|
-
parentNormalized: true,
|
|
5060
|
-
originalParentId
|
|
5061
|
-
}
|
|
5062
|
-
});
|
|
5063
|
-
continue;
|
|
5027
|
+
if (byPath.has(relativePath)) {
|
|
5028
|
+
throw new Error(`Duplicate evidence file path: ${relativePath}`);
|
|
5064
5029
|
}
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
});
|
|
5070
|
-
normalized.push({
|
|
5071
|
-
...event,
|
|
5072
|
-
parentId: nextParent,
|
|
5073
|
-
projection: {
|
|
5074
|
-
...event.projection,
|
|
5075
|
-
parentNormalized: true,
|
|
5076
|
-
originalParentId
|
|
5077
|
-
}
|
|
5030
|
+
byPath.set(relativePath, {
|
|
5031
|
+
path: relativePath,
|
|
5032
|
+
sha256: sha256Hex(file.content),
|
|
5033
|
+
role: file.role ?? inferEvidenceFileRole(relativePath)
|
|
5078
5034
|
});
|
|
5079
5035
|
}
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5036
|
+
return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
5037
|
+
}
|
|
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.");
|
|
5042
|
+
}
|
|
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.`);
|
|
5046
|
+
}
|
|
5047
|
+
if (item.algorithm !== "sha256") {
|
|
5048
|
+
throw new Error(`Unsupported source hash algorithm: ${item.algorithm}`);
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
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;
|
|
5057
|
+
}
|
|
5086
5058
|
return {
|
|
5087
|
-
|
|
5088
|
-
|
|
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)
|
|
5089
5079
|
};
|
|
5090
5080
|
}
|
|
5091
|
-
function
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
if (direct) return direct;
|
|
5095
|
-
const metadata = attrs?.metadata;
|
|
5096
|
-
if (isRecord7(metadata)) {
|
|
5097
|
-
const nested = pickString(metadata, ["toolName", "tool"]);
|
|
5098
|
-
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.");
|
|
5099
5084
|
}
|
|
5100
|
-
|
|
5101
|
-
|
|
5085
|
+
const record = value;
|
|
5086
|
+
if (record.evidenceFormatVersion !== EVIDENCE_FORMAT_VERSION) {
|
|
5087
|
+
throw new Error(
|
|
5088
|
+
`Unsupported evidenceFormatVersion: ${String(record.evidenceFormatVersion)}`
|
|
5089
|
+
);
|
|
5102
5090
|
}
|
|
5103
|
-
|
|
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;
|
|
5104
5134
|
}
|
|
5105
|
-
function
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
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}`);
|
|
5110
5142
|
}
|
|
5111
|
-
return
|
|
5143
|
+
return validateEvidenceManifest(parsed);
|
|
5112
5144
|
}
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
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();
|
|
5116
5165
|
}
|
|
5117
5166
|
});
|
|
5118
5167
|
|
|
@@ -5504,7 +5553,11 @@ function emptySummary2() {
|
|
|
5504
5553
|
failed: 0,
|
|
5505
5554
|
warnings: 0,
|
|
5506
5555
|
errors: 0,
|
|
5507
|
-
rulesEvaluated: 0
|
|
5556
|
+
rulesEvaluated: 0,
|
|
5557
|
+
rulesPassed: 0,
|
|
5558
|
+
rulesWarning: 0,
|
|
5559
|
+
rulesFailed: 0,
|
|
5560
|
+
rulesError: 0
|
|
5508
5561
|
};
|
|
5509
5562
|
}
|
|
5510
5563
|
function errorResult(input3, diagnostics, selectedRun, ruleExecutions = []) {
|
|
@@ -5516,7 +5569,11 @@ function errorResult(input3, diagnostics, selectedRun, ruleExecutions = []) {
|
|
|
5516
5569
|
summary: {
|
|
5517
5570
|
...emptySummary2(),
|
|
5518
5571
|
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
5519
|
-
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
|
|
5520
5577
|
},
|
|
5521
5578
|
findings: [],
|
|
5522
5579
|
diagnostics: [...diagnostics],
|
|
@@ -5676,7 +5733,7 @@ function normalizeFinding(rule, finding) {
|
|
|
5676
5733
|
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
5677
5734
|
};
|
|
5678
5735
|
}
|
|
5679
|
-
function summarize(findings, diagnostics,
|
|
5736
|
+
function summarize(findings, diagnostics, ruleExecutions) {
|
|
5680
5737
|
return {
|
|
5681
5738
|
passed: findings.filter((finding) => finding.status === "pass").length,
|
|
5682
5739
|
failed: findings.filter(
|
|
@@ -5686,7 +5743,11 @@ function summarize(findings, diagnostics, rulesEvaluated) {
|
|
|
5686
5743
|
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
5687
5744
|
).length,
|
|
5688
5745
|
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
5689
|
-
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
|
|
5690
5751
|
};
|
|
5691
5752
|
}
|
|
5692
5753
|
function classifyRuleExecution(findings, threw) {
|
|
@@ -7352,7 +7413,7 @@ function runTraceChecks(input3, options = {}) {
|
|
|
7352
7413
|
}
|
|
7353
7414
|
const eventById = new Map(input3.read.events.map((event) => [event.eventId, event]));
|
|
7354
7415
|
const sortedFindings = findings.sort(compareFindings(eventById));
|
|
7355
|
-
const summary = summarize(sortedFindings, diagnostics, ruleExecutions
|
|
7416
|
+
const summary = summarize(sortedFindings, diagnostics, ruleExecutions);
|
|
7356
7417
|
const status = summary.failed > 0 ? "fail" : "pass";
|
|
7357
7418
|
return {
|
|
7358
7419
|
ok: status === "pass",
|
|
@@ -9491,7 +9552,17 @@ function emptyPassResult(input3, runId) {
|
|
|
9491
9552
|
status: "pass",
|
|
9492
9553
|
format: input3.read.format,
|
|
9493
9554
|
...runId !== void 0 ? { runId } : {},
|
|
9494
|
-
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
|
+
},
|
|
9495
9566
|
findings: [],
|
|
9496
9567
|
diagnostics: [],
|
|
9497
9568
|
ruleExecutions: []
|
|
@@ -9519,7 +9590,11 @@ function mergeContractResults(input3, parts, extraFindings = []) {
|
|
|
9519
9590
|
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
9520
9591
|
).length,
|
|
9521
9592
|
errors,
|
|
9522
|
-
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
|
|
9523
9598
|
},
|
|
9524
9599
|
findings,
|
|
9525
9600
|
diagnostics,
|
|
@@ -9597,7 +9672,11 @@ function invalidContractInputResult() {
|
|
|
9597
9672
|
failed: 0,
|
|
9598
9673
|
warnings: 0,
|
|
9599
9674
|
errors: 1,
|
|
9600
|
-
rulesEvaluated: 0
|
|
9675
|
+
rulesEvaluated: 0,
|
|
9676
|
+
rulesPassed: 0,
|
|
9677
|
+
rulesWarning: 0,
|
|
9678
|
+
rulesFailed: 0,
|
|
9679
|
+
rulesError: 0
|
|
9601
9680
|
},
|
|
9602
9681
|
findings: [],
|
|
9603
9682
|
diagnostics: [
|
|
@@ -9650,7 +9729,11 @@ function evaluateTraceContract(input3, contract, options = {}) {
|
|
|
9650
9729
|
failed: 0,
|
|
9651
9730
|
warnings: 0,
|
|
9652
9731
|
errors: shapeErrors.length,
|
|
9653
|
-
rulesEvaluated: 0
|
|
9732
|
+
rulesEvaluated: 0,
|
|
9733
|
+
rulesPassed: 0,
|
|
9734
|
+
rulesWarning: 0,
|
|
9735
|
+
rulesFailed: 0,
|
|
9736
|
+
rulesError: 0
|
|
9654
9737
|
},
|
|
9655
9738
|
findings: [],
|
|
9656
9739
|
diagnostics: shapeErrors.map((item) => ({
|
|
@@ -9675,7 +9758,11 @@ function evaluateTraceContract(input3, contract, options = {}) {
|
|
|
9675
9758
|
failed: 0,
|
|
9676
9759
|
warnings: 0,
|
|
9677
9760
|
errors: scoped.diagnostics.length,
|
|
9678
|
-
rulesEvaluated: 0
|
|
9761
|
+
rulesEvaluated: 0,
|
|
9762
|
+
rulesPassed: 0,
|
|
9763
|
+
rulesWarning: 0,
|
|
9764
|
+
rulesFailed: 0,
|
|
9765
|
+
rulesError: 0
|
|
9679
9766
|
},
|
|
9680
9767
|
findings: [],
|
|
9681
9768
|
diagnostics: scoped.diagnostics,
|
|
@@ -11831,10 +11918,102 @@ function asStringArray(value, label) {
|
|
|
11831
11918
|
function asPositiveNumber(value, label) {
|
|
11832
11919
|
if (value === void 0) return void 0;
|
|
11833
11920
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
11834
|
-
throw new Error(`${label} must be a non-negative number.`);
|
|
11921
|
+
throw new Error(`${label} must be a non-negative finite number.`);
|
|
11922
|
+
}
|
|
11923
|
+
return value;
|
|
11924
|
+
}
|
|
11925
|
+
function asBoolean(value, label) {
|
|
11926
|
+
if (value === void 0) return void 0;
|
|
11927
|
+
if (typeof value !== "boolean") {
|
|
11928
|
+
throw new Error(`${label} must be a boolean (not a string or other type).`);
|
|
11929
|
+
}
|
|
11930
|
+
return value;
|
|
11931
|
+
}
|
|
11932
|
+
function assertObject(value, label) {
|
|
11933
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
11934
|
+
throw new Error(`${label} must be an object.`);
|
|
11835
11935
|
}
|
|
11836
11936
|
return value;
|
|
11837
11937
|
}
|
|
11938
|
+
function assertKnownKeys(raw, allowed, label) {
|
|
11939
|
+
const allowedSet = new Set(allowed);
|
|
11940
|
+
for (const key of Object.keys(raw)) {
|
|
11941
|
+
if (!allowedSet.has(key)) {
|
|
11942
|
+
throw new Error(`${label} has unknown key "${key}".`);
|
|
11943
|
+
}
|
|
11944
|
+
}
|
|
11945
|
+
}
|
|
11946
|
+
function asExpectConfig(value, label) {
|
|
11947
|
+
if (value === void 0) return void 0;
|
|
11948
|
+
const raw = assertObject(value, label);
|
|
11949
|
+
assertKnownKeys(raw, EXPECT_KEYS, label);
|
|
11950
|
+
const checkStatus = raw.checkStatus;
|
|
11951
|
+
if (checkStatus !== "pass" && checkStatus !== "fail" && checkStatus !== "error") {
|
|
11952
|
+
throw new Error(`${label}.checkStatus must be "pass", "fail", or "error".`);
|
|
11953
|
+
}
|
|
11954
|
+
const findingRuleIds = asStringArray(raw.findingRuleIds, `${label}.findingRuleIds`);
|
|
11955
|
+
const minAssertions = asPositiveNumber(raw.minAssertions, `${label}.minAssertions`);
|
|
11956
|
+
const rejectUnexpectedFindings = asBoolean(
|
|
11957
|
+
raw.rejectUnexpectedFindings,
|
|
11958
|
+
`${label}.rejectUnexpectedFindings`
|
|
11959
|
+
);
|
|
11960
|
+
return {
|
|
11961
|
+
checkStatus,
|
|
11962
|
+
...findingRuleIds !== void 0 ? { findingRuleIds } : {},
|
|
11963
|
+
...rejectUnexpectedFindings !== void 0 ? { rejectUnexpectedFindings } : {},
|
|
11964
|
+
...minAssertions !== void 0 ? { minAssertions } : {}
|
|
11965
|
+
};
|
|
11966
|
+
}
|
|
11967
|
+
function asChecksConfig(value, label) {
|
|
11968
|
+
const raw = assertObject(value, label);
|
|
11969
|
+
assertKnownKeys(raw, CHECKS_KEYS, label);
|
|
11970
|
+
const checks2 = {};
|
|
11971
|
+
const select = asStringArray(raw.select, `${label}.select`);
|
|
11972
|
+
if (select !== void 0) checks2.select = select;
|
|
11973
|
+
if (raw.run !== void 0) {
|
|
11974
|
+
const run = assertObject(raw.run, `${label}.run`);
|
|
11975
|
+
assertKnownKeys(run, CHECKS_RUN_KEYS, `${label}.run`);
|
|
11976
|
+
checks2.run = {
|
|
11977
|
+
...asPositiveNumber(run.maxDurationMs, `${label}.run.maxDurationMs`) !== void 0 ? { maxDurationMs: asPositiveNumber(run.maxDurationMs, `${label}.run.maxDurationMs`) } : {},
|
|
11978
|
+
...asPositiveNumber(run.maxDepth, `${label}.run.maxDepth`) !== void 0 ? { maxDepth: asPositiveNumber(run.maxDepth, `${label}.run.maxDepth`) } : {}
|
|
11979
|
+
};
|
|
11980
|
+
}
|
|
11981
|
+
if (raw.tool !== void 0) {
|
|
11982
|
+
const tool = assertObject(raw.tool, `${label}.tool`);
|
|
11983
|
+
assertKnownKeys(tool, CHECKS_TOOL_KEYS, `${label}.tool`);
|
|
11984
|
+
checks2.tool = {
|
|
11985
|
+
...asStringArray(tool.required, `${label}.tool.required`) !== void 0 ? { required: asStringArray(tool.required, `${label}.tool.required`) } : {},
|
|
11986
|
+
...asStringArray(tool.forbidden, `${label}.tool.forbidden`) !== void 0 ? { forbidden: asStringArray(tool.forbidden, `${label}.tool.forbidden`) } : {}
|
|
11987
|
+
};
|
|
11988
|
+
}
|
|
11989
|
+
if (raw.llm !== void 0) {
|
|
11990
|
+
const llm = assertObject(raw.llm, `${label}.llm`);
|
|
11991
|
+
assertKnownKeys(llm, CHECKS_LLM_KEYS, `${label}.llm`);
|
|
11992
|
+
checks2.llm = {
|
|
11993
|
+
...asStringArray(llm.allowedModels, `${label}.llm.allowedModels`) !== void 0 ? { allowedModels: asStringArray(llm.allowedModels, `${label}.llm.allowedModels`) } : {},
|
|
11994
|
+
...asPositiveNumber(llm.maxTotalTokens, `${label}.llm.maxTotalTokens`) !== void 0 ? {
|
|
11995
|
+
maxTotalTokens: asPositiveNumber(
|
|
11996
|
+
llm.maxTotalTokens,
|
|
11997
|
+
`${label}.llm.maxTotalTokens`
|
|
11998
|
+
)
|
|
11999
|
+
} : {}
|
|
12000
|
+
};
|
|
12001
|
+
}
|
|
12002
|
+
return checks2;
|
|
12003
|
+
}
|
|
12004
|
+
function asEvalConfig(value, label) {
|
|
12005
|
+
const raw = assertObject(value, label);
|
|
12006
|
+
assertKnownKeys(raw, EVAL_KEYS, label);
|
|
12007
|
+
return {
|
|
12008
|
+
...asBoolean(raw.requireSuccess, `${label}.requireSuccess`) !== void 0 ? { requireSuccess: asBoolean(raw.requireSuccess, `${label}.requireSuccess`) } : {},
|
|
12009
|
+
...asStringArray(raw.requiredTools, `${label}.requiredTools`) !== void 0 ? { requiredTools: asStringArray(raw.requiredTools, `${label}.requiredTools`) } : {},
|
|
12010
|
+
...asStringArray(raw.forbiddenTools, `${label}.forbiddenTools`) !== void 0 ? { forbiddenTools: asStringArray(raw.forbiddenTools, `${label}.forbiddenTools`) } : {},
|
|
12011
|
+
...asPositiveNumber(raw.maxDurationMs, `${label}.maxDurationMs`) !== void 0 ? { maxDurationMs: asPositiveNumber(raw.maxDurationMs, `${label}.maxDurationMs`) } : {},
|
|
12012
|
+
...asPositiveNumber(raw.maxDepth, `${label}.maxDepth`) !== void 0 ? { maxDepth: asPositiveNumber(raw.maxDepth, `${label}.maxDepth`) } : {},
|
|
12013
|
+
...asPositiveNumber(raw.maxRetries, `${label}.maxRetries`) !== void 0 ? { maxRetries: asPositiveNumber(raw.maxRetries, `${label}.maxRetries`) } : {},
|
|
12014
|
+
...asPositiveNumber(raw.maxTotalTokens, `${label}.maxTotalTokens`) !== void 0 ? { maxTotalTokens: asPositiveNumber(raw.maxTotalTokens, `${label}.maxTotalTokens`) } : {}
|
|
12015
|
+
};
|
|
12016
|
+
}
|
|
11838
12017
|
function validateCaseConfig(value, index) {
|
|
11839
12018
|
const diagnostics = [];
|
|
11840
12019
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
@@ -11845,6 +12024,7 @@ function validateCaseConfig(value, index) {
|
|
|
11845
12024
|
}
|
|
11846
12025
|
const raw = value;
|
|
11847
12026
|
try {
|
|
12027
|
+
assertKnownKeys(raw, CASE_KEYS, `cases[${index}]`);
|
|
11848
12028
|
const id = asString(raw.id, `cases[${index}].id`);
|
|
11849
12029
|
if (id === void 0) {
|
|
11850
12030
|
diagnostics.push(
|
|
@@ -11877,7 +12057,8 @@ function validateCaseConfig(value, index) {
|
|
|
11877
12057
|
raw.expectedObservations,
|
|
11878
12058
|
`cases[${index}].expectedObservations`
|
|
11879
12059
|
)
|
|
11880
|
-
} : {}
|
|
12060
|
+
} : {},
|
|
12061
|
+
...asExpectConfig(raw.expect, `cases[${index}].expect`) !== void 0 ? { expect: asExpectConfig(raw.expect, `cases[${index}].expect`) } : {}
|
|
11881
12062
|
},
|
|
11882
12063
|
diagnostics
|
|
11883
12064
|
};
|
|
@@ -11892,6 +12073,7 @@ function normalizeSuiteConfig(value) {
|
|
|
11892
12073
|
throw new Error("Suite config must export an object.");
|
|
11893
12074
|
}
|
|
11894
12075
|
const raw = value;
|
|
12076
|
+
assertKnownKeys(raw, ROOT_KEYS, "suite config");
|
|
11895
12077
|
const name = asString(raw.name, "name");
|
|
11896
12078
|
const traces = asString(raw.traces, "traces");
|
|
11897
12079
|
if (name === void 0) throw new Error("name is required.");
|
|
@@ -11920,25 +12102,15 @@ function normalizeSuiteConfig(value) {
|
|
|
11920
12102
|
}
|
|
11921
12103
|
const config = { name, traces, cases };
|
|
11922
12104
|
if (raw.checks !== void 0) {
|
|
11923
|
-
|
|
11924
|
-
throw new Error("checks must be an object.");
|
|
11925
|
-
}
|
|
11926
|
-
config.checks = raw.checks;
|
|
12105
|
+
config.checks = asChecksConfig(raw.checks, "checks");
|
|
11927
12106
|
}
|
|
11928
12107
|
if (raw.eval !== void 0) {
|
|
11929
|
-
|
|
11930
|
-
throw new Error("eval must be an object.");
|
|
11931
|
-
}
|
|
11932
|
-
config.eval = raw.eval;
|
|
12108
|
+
config.eval = asEvalConfig(raw.eval, "eval");
|
|
11933
12109
|
}
|
|
11934
12110
|
if (raw.artifacts !== void 0) {
|
|
11935
|
-
|
|
11936
|
-
|
|
11937
|
-
|
|
11938
|
-
const outputDir = asString(
|
|
11939
|
-
raw.artifacts.outputDir,
|
|
11940
|
-
"artifacts.outputDir"
|
|
11941
|
-
);
|
|
12111
|
+
const artifacts = assertObject(raw.artifacts, "artifacts");
|
|
12112
|
+
assertKnownKeys(artifacts, ["outputDir"], "artifacts");
|
|
12113
|
+
const outputDir = asString(artifacts.outputDir, "artifacts.outputDir");
|
|
11942
12114
|
config.artifacts = outputDir !== void 0 ? { outputDir } : {};
|
|
11943
12115
|
}
|
|
11944
12116
|
if (raw.baseline !== void 0) {
|
|
@@ -11981,8 +12153,50 @@ async function validateSuiteConfig(config, options) {
|
|
|
11981
12153
|
}
|
|
11982
12154
|
return { ok: diagnostics.every((item) => item.severity !== "error"), diagnostics };
|
|
11983
12155
|
}
|
|
12156
|
+
var CASE_KEYS, EXPECT_KEYS, CHECKS_KEYS, CHECKS_RUN_KEYS, CHECKS_TOOL_KEYS, CHECKS_LLM_KEYS, EVAL_KEYS, ROOT_KEYS;
|
|
11984
12157
|
var init_validate2 = __esm({
|
|
11985
12158
|
"packages/core/src/suite/validate.ts"() {
|
|
12159
|
+
CASE_KEYS = [
|
|
12160
|
+
"id",
|
|
12161
|
+
"trace",
|
|
12162
|
+
"runId",
|
|
12163
|
+
"input",
|
|
12164
|
+
"requireTools",
|
|
12165
|
+
"forbidTools",
|
|
12166
|
+
"maxDurationMs",
|
|
12167
|
+
"expectedObservations",
|
|
12168
|
+
"expect"
|
|
12169
|
+
];
|
|
12170
|
+
EXPECT_KEYS = [
|
|
12171
|
+
"checkStatus",
|
|
12172
|
+
"findingRuleIds",
|
|
12173
|
+
"rejectUnexpectedFindings",
|
|
12174
|
+
"minAssertions"
|
|
12175
|
+
];
|
|
12176
|
+
CHECKS_KEYS = ["select", "run", "tool", "llm"];
|
|
12177
|
+
CHECKS_RUN_KEYS = ["maxDurationMs", "maxDepth"];
|
|
12178
|
+
CHECKS_TOOL_KEYS = ["required", "forbidden"];
|
|
12179
|
+
CHECKS_LLM_KEYS = ["allowedModels", "maxTotalTokens"];
|
|
12180
|
+
EVAL_KEYS = [
|
|
12181
|
+
"requireSuccess",
|
|
12182
|
+
"requiredTools",
|
|
12183
|
+
"forbiddenTools",
|
|
12184
|
+
"maxDurationMs",
|
|
12185
|
+
"maxDepth",
|
|
12186
|
+
"maxRetries",
|
|
12187
|
+
"maxTotalTokens"
|
|
12188
|
+
];
|
|
12189
|
+
ROOT_KEYS = [
|
|
12190
|
+
"name",
|
|
12191
|
+
"traces",
|
|
12192
|
+
"cases",
|
|
12193
|
+
"checks",
|
|
12194
|
+
"eval",
|
|
12195
|
+
"redactionProfile",
|
|
12196
|
+
"artifacts",
|
|
12197
|
+
"baseline",
|
|
12198
|
+
"candidate"
|
|
12199
|
+
];
|
|
11986
12200
|
}
|
|
11987
12201
|
});
|
|
11988
12202
|
function diagnostic4(code, message) {
|
|
@@ -14557,6 +14771,78 @@ function validateExpectedObservations(suiteCase, read) {
|
|
|
14557
14771
|
}
|
|
14558
14772
|
return { ok: diagnostics.length === 0, diagnostics };
|
|
14559
14773
|
}
|
|
14774
|
+
function evaluateCaseExpect(suiteCase, checkResult, observationCount) {
|
|
14775
|
+
const expectation = suiteCase.expect;
|
|
14776
|
+
if (expectation === void 0) {
|
|
14777
|
+
return { ok: true, diagnostics: [] };
|
|
14778
|
+
}
|
|
14779
|
+
const diagnostics = [];
|
|
14780
|
+
if (checkResult.status !== expectation.checkStatus) {
|
|
14781
|
+
diagnostics.push(
|
|
14782
|
+
diagnostic5(
|
|
14783
|
+
"AI_SUITE_EXPECT_MISMATCH",
|
|
14784
|
+
`Expected check status "${expectation.checkStatus}", got "${checkResult.status}".`,
|
|
14785
|
+
"error",
|
|
14786
|
+
suiteCase.id
|
|
14787
|
+
)
|
|
14788
|
+
);
|
|
14789
|
+
}
|
|
14790
|
+
const failedFindings = checkResult.findings.filter(
|
|
14791
|
+
(finding) => finding.status === "fail" && finding.severity === "error"
|
|
14792
|
+
);
|
|
14793
|
+
const requiredIds = expectation.findingRuleIds ?? [];
|
|
14794
|
+
for (const ruleId of requiredIds) {
|
|
14795
|
+
if (!failedFindings.some((finding) => finding.ruleId === ruleId)) {
|
|
14796
|
+
diagnostics.push(
|
|
14797
|
+
diagnostic5(
|
|
14798
|
+
"AI_SUITE_EXPECT_MISMATCH",
|
|
14799
|
+
`Expected failing finding for rule "${ruleId}" was not present.`,
|
|
14800
|
+
"error",
|
|
14801
|
+
suiteCase.id
|
|
14802
|
+
)
|
|
14803
|
+
);
|
|
14804
|
+
}
|
|
14805
|
+
}
|
|
14806
|
+
const rejectUnexpected = expectation.rejectUnexpectedFindings !== false;
|
|
14807
|
+
if (rejectUnexpected && requiredIds.length > 0) {
|
|
14808
|
+
for (const finding of failedFindings) {
|
|
14809
|
+
if (!requiredIds.includes(finding.ruleId)) {
|
|
14810
|
+
diagnostics.push(
|
|
14811
|
+
diagnostic5(
|
|
14812
|
+
"AI_SUITE_EXPECT_MISMATCH",
|
|
14813
|
+
`Unexpected failing finding for rule "${finding.ruleId}".`,
|
|
14814
|
+
"error",
|
|
14815
|
+
suiteCase.id
|
|
14816
|
+
)
|
|
14817
|
+
);
|
|
14818
|
+
}
|
|
14819
|
+
}
|
|
14820
|
+
}
|
|
14821
|
+
if (expectation.minAssertions !== void 0) {
|
|
14822
|
+
const assertionCount = checkResult.ruleExecutions.length + observationCount;
|
|
14823
|
+
if (assertionCount < expectation.minAssertions) {
|
|
14824
|
+
diagnostics.push(
|
|
14825
|
+
diagnostic5(
|
|
14826
|
+
"AI_SUITE_EXPECT_MISMATCH",
|
|
14827
|
+
`Expected at least ${expectation.minAssertions} assertion(s), got ${assertionCount}.`,
|
|
14828
|
+
"error",
|
|
14829
|
+
suiteCase.id
|
|
14830
|
+
)
|
|
14831
|
+
);
|
|
14832
|
+
}
|
|
14833
|
+
}
|
|
14834
|
+
if (expectation.checkStatus === "fail" && checkResult.diagnostics.some((item) => item.severity === "error") && checkResult.status === "error") {
|
|
14835
|
+
diagnostics.push(
|
|
14836
|
+
diagnostic5(
|
|
14837
|
+
"AI_SUITE_EXPECT_MISMATCH",
|
|
14838
|
+
'Check ended in evaluator/config error; semantic failure expectation requires status "fail".',
|
|
14839
|
+
"error",
|
|
14840
|
+
suiteCase.id
|
|
14841
|
+
)
|
|
14842
|
+
);
|
|
14843
|
+
}
|
|
14844
|
+
return { ok: diagnostics.length === 0, diagnostics };
|
|
14845
|
+
}
|
|
14560
14846
|
async function runSuiteCase(suiteCase, config, options) {
|
|
14561
14847
|
const resolved = await resolveSuiteCaseTrace(suiteCase, options);
|
|
14562
14848
|
if (resolved.missing || resolved.tracePath === void 0) {
|
|
@@ -14609,9 +14895,21 @@ async function runSuiteCase(suiteCase, config, options) {
|
|
|
14609
14895
|
status: "pass",
|
|
14610
14896
|
format: read.format,
|
|
14611
14897
|
findings: [],
|
|
14612
|
-
diagnostics: []
|
|
14898
|
+
diagnostics: [],
|
|
14899
|
+
ruleExecutions: []
|
|
14900
|
+
};
|
|
14613
14901
|
const observationResult = validateExpectedObservations(suiteCase, read);
|
|
14614
|
-
const
|
|
14902
|
+
const checkOk = checkResult.ok;
|
|
14903
|
+
const observationsOk = observationResult.ok;
|
|
14904
|
+
const evalOk = config.eval?.requireSuccess === true ? checkResult.findings.every(
|
|
14905
|
+
(finding) => finding.ruleId !== "run.status" || finding.status !== "fail"
|
|
14906
|
+
) && checkResult.diagnostics.every((item) => item.severity !== "error") : void 0;
|
|
14907
|
+
const expectResult = evaluateCaseExpect(
|
|
14908
|
+
suiteCase,
|
|
14909
|
+
checkResult,
|
|
14910
|
+
compiled.observationCount
|
|
14911
|
+
);
|
|
14912
|
+
const checkFailureDiagnostics = [
|
|
14615
14913
|
...checkResult.diagnostics.map(
|
|
14616
14914
|
(item) => diagnostic5(
|
|
14617
14915
|
"AI_SUITE_CASE_CHECK_FAILED",
|
|
@@ -14627,15 +14925,24 @@ async function runSuiteCase(suiteCase, config, options) {
|
|
|
14627
14925
|
finding.severity,
|
|
14628
14926
|
suiteCase.id
|
|
14629
14927
|
)
|
|
14630
|
-
)
|
|
14631
|
-
...observationResult.diagnostics
|
|
14928
|
+
)
|
|
14632
14929
|
];
|
|
14633
|
-
const
|
|
14634
|
-
const
|
|
14635
|
-
|
|
14636
|
-
|
|
14637
|
-
|
|
14638
|
-
|
|
14930
|
+
const expectMatched = suiteCase.expect !== void 0 && expectResult.ok && observationsOk;
|
|
14931
|
+
const diagnostics = expectMatched ? [
|
|
14932
|
+
...observationResult.diagnostics,
|
|
14933
|
+
...expectResult.diagnostics,
|
|
14934
|
+
diagnostic5(
|
|
14935
|
+
"AI_SUITE_CASE_CHECK_FAILED",
|
|
14936
|
+
`Expected semantic failure matched (check.status=${checkResult.status}).`,
|
|
14937
|
+
"info",
|
|
14938
|
+
suiteCase.id
|
|
14939
|
+
)
|
|
14940
|
+
] : [
|
|
14941
|
+
...checkFailureDiagnostics,
|
|
14942
|
+
...observationResult.diagnostics,
|
|
14943
|
+
...expectResult.diagnostics
|
|
14944
|
+
];
|
|
14945
|
+
const ok = suiteCase.expect !== void 0 ? expectResult.ok && observationsOk : checkOk && observationsOk;
|
|
14639
14946
|
const status = ok ? "pass" : checkResult.status === "error" ? "error" : "fail";
|
|
14640
14947
|
return {
|
|
14641
14948
|
id: suiteCase.id,
|
|
@@ -16453,7 +16760,7 @@ var init_src = __esm({
|
|
|
16453
16760
|
});
|
|
16454
16761
|
|
|
16455
16762
|
// package.json
|
|
16456
|
-
var version = "6.31.
|
|
16763
|
+
var version = "6.31.12";
|
|
16457
16764
|
|
|
16458
16765
|
// packages/cli/src/list.ts
|
|
16459
16766
|
init_advanced();
|
|
@@ -21318,6 +21625,15 @@ function actionForRule(rule) {
|
|
|
21318
21625
|
if (rule.strategy === "full") return "replace";
|
|
21319
21626
|
return rule.strategy;
|
|
21320
21627
|
}
|
|
21628
|
+
function isCompleteRedactionMarker(value, replacement = "[REDACTED]") {
|
|
21629
|
+
if (typeof value !== "string") return false;
|
|
21630
|
+
const trimmed = value.trim();
|
|
21631
|
+
if (trimmed.length === 0) return false;
|
|
21632
|
+
if (trimmed === replacement) return true;
|
|
21633
|
+
if (/^\[HASH:[A-Za-z0-9_-]+\]$/.test(trimmed)) return true;
|
|
21634
|
+
if (/^\[REDACTED:[^\]]*\]$/.test(trimmed)) return true;
|
|
21635
|
+
return false;
|
|
21636
|
+
}
|
|
21321
21637
|
function applyRule(rule, value, replacement) {
|
|
21322
21638
|
if (rule.strategy === "full") return replacement;
|
|
21323
21639
|
const asString2 = stringifyScalar(value);
|
|
@@ -21432,6 +21748,9 @@ var Redactor2 = class {
|
|
|
21432
21748
|
if (key !== void 0) {
|
|
21433
21749
|
const rule = findCompiledKeyRule(key, this.#rules);
|
|
21434
21750
|
if (rule) {
|
|
21751
|
+
if (isCompleteRedactionMarker(value, this.#replacement)) {
|
|
21752
|
+
return value;
|
|
21753
|
+
}
|
|
21435
21754
|
this.#recordFinding(
|
|
21436
21755
|
state,
|
|
21437
21756
|
makeFinding(path44, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|
|
@@ -22494,7 +22813,8 @@ async function explainCommand(target, options = {}, stdin = process.stdin) {
|
|
|
22494
22813
|
const mode = options.dryRun ? "dry-run" : "local";
|
|
22495
22814
|
const explanation = buildLocalExplanation(selected, {
|
|
22496
22815
|
mode,
|
|
22497
|
-
redactionProfile
|
|
22816
|
+
redactionProfile,
|
|
22817
|
+
events: read.events
|
|
22498
22818
|
});
|
|
22499
22819
|
if (options.json) {
|
|
22500
22820
|
writeJson({
|
|
@@ -23811,7 +24131,11 @@ function mergeSafetyExtensions(result, read, options) {
|
|
|
23811
24131
|
failed,
|
|
23812
24132
|
warnings,
|
|
23813
24133
|
errors,
|
|
23814
|
-
rulesEvaluated: ruleExecutions.length
|
|
24134
|
+
rulesEvaluated: ruleExecutions.length,
|
|
24135
|
+
rulesPassed: ruleExecutions.filter((item) => item.status === "pass").length,
|
|
24136
|
+
rulesWarning: ruleExecutions.filter((item) => item.status === "warning").length,
|
|
24137
|
+
rulesFailed: ruleExecutions.filter((item) => item.status === "fail").length,
|
|
24138
|
+
rulesError: ruleExecutions.filter((item) => item.status === "error").length
|
|
23815
24139
|
},
|
|
23816
24140
|
findings,
|
|
23817
24141
|
ruleExecutions
|
|
@@ -23963,7 +24287,11 @@ function errorResult2(code, message, format = "unknown") {
|
|
|
23963
24287
|
failed: 0,
|
|
23964
24288
|
warnings: 0,
|
|
23965
24289
|
errors: 1,
|
|
23966
|
-
rulesEvaluated: 0
|
|
24290
|
+
rulesEvaluated: 0,
|
|
24291
|
+
rulesPassed: 0,
|
|
24292
|
+
rulesWarning: 0,
|
|
24293
|
+
rulesFailed: 0,
|
|
24294
|
+
rulesError: 0
|
|
23967
24295
|
},
|
|
23968
24296
|
findings: [],
|
|
23969
24297
|
diagnostics,
|
|
@@ -23985,7 +24313,7 @@ function asStringArray2(value) {
|
|
|
23985
24313
|
}
|
|
23986
24314
|
return value;
|
|
23987
24315
|
}
|
|
23988
|
-
var
|
|
24316
|
+
var CHECKS_KEYS2 = /* @__PURE__ */ new Set(["select", "run", "tool", "llm", "structure", "safety"]);
|
|
23989
24317
|
var ROOT_CONFIG_KEYS = /* @__PURE__ */ new Set(["checks", "contract"]);
|
|
23990
24318
|
var CONTRACT_KEYS = /* @__PURE__ */ new Set([
|
|
23991
24319
|
"run",
|
|
@@ -25046,7 +25374,7 @@ function parseCheckConfig(value) {
|
|
|
25046
25374
|
const out = {};
|
|
25047
25375
|
if ("checks" in root) {
|
|
25048
25376
|
const checksRecord = requireObject(root.checks, "checks");
|
|
25049
|
-
rejectUnknownKeys(checksRecord,
|
|
25377
|
+
rejectUnknownKeys(checksRecord, CHECKS_KEYS2, "checks");
|
|
25050
25378
|
const checks2 = {};
|
|
25051
25379
|
if (checksRecord.select !== void 0) {
|
|
25052
25380
|
checks2.select = requireStringArray(checksRecord.select, "checks.select");
|
|
@@ -25367,9 +25695,11 @@ function printHuman2(result, options = {}) {
|
|
|
25367
25695
|
console.log(`Format: ${result.format}`);
|
|
25368
25696
|
if (result.runId !== void 0) console.log(`Run: ${result.runId}`);
|
|
25369
25697
|
console.log(
|
|
25370
|
-
`
|
|
25698
|
+
`Findings: ${result.summary.failed} failed, ${result.summary.warnings} warning(s), ${result.summary.errors} error(s), ${result.summary.passed} passed`
|
|
25699
|
+
);
|
|
25700
|
+
console.log(
|
|
25701
|
+
`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)`
|
|
25371
25702
|
);
|
|
25372
|
-
console.log(`Rules evaluated: ${result.summary.rulesEvaluated ?? result.ruleExecutions?.length ?? 0}`);
|
|
25373
25703
|
for (const diagnostic8 of result.diagnostics) {
|
|
25374
25704
|
console.log(`- ${diagnostic8.code}: ${diagnostic8.message}`);
|
|
25375
25705
|
}
|