@agent-inspect/mcp-server 6.31.10 → 6.31.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-GWLPG4BM.mjs → chunk-FWSGOK3F.mjs} +706 -681
- package/dist/chunk-FWSGOK3F.mjs.map +1 -0
- package/dist/cli.cjs +704 -679
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.cjs +704 -679
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -3
- package/dist/chunk-GWLPG4BM.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1692,7 +1692,7 @@ function stepMixLine(summary) {
|
|
|
1692
1692
|
if (summary.logicSteps > 0) parts.push(`${summary.logicSteps} logic`);
|
|
1693
1693
|
return parts.length > 0 ? parts.join(", ") : "none";
|
|
1694
1694
|
}
|
|
1695
|
-
function
|
|
1695
|
+
function executionLine(summary) {
|
|
1696
1696
|
if (summary.status === "success") {
|
|
1697
1697
|
return summary.errorSteps > 0 ? "Completed with step errors recorded." : "Completed successfully.";
|
|
1698
1698
|
}
|
|
@@ -1710,7 +1710,7 @@ function outcomeLine(summary) {
|
|
|
1710
1710
|
if (summary.status === "running") {
|
|
1711
1711
|
return "Run is still in progress (no run_completed).";
|
|
1712
1712
|
}
|
|
1713
|
-
return "
|
|
1713
|
+
return "Execution status unknown \u2014 inspect events may be incomplete.";
|
|
1714
1714
|
}
|
|
1715
1715
|
function buildRunWhatSummary(events) {
|
|
1716
1716
|
const base = buildRunSummary(events);
|
|
@@ -1801,7 +1801,7 @@ function renderRunWhat(summary, options = {}) {
|
|
|
1801
1801
|
lines.push(`Correlation: ${parts.join(", ")}`);
|
|
1802
1802
|
}
|
|
1803
1803
|
}
|
|
1804
|
-
lines.push(`
|
|
1804
|
+
lines.push(`Execution: ${executionLine(summary)}`);
|
|
1805
1805
|
if (summary.longestStep && summary.totalSteps > 0) {
|
|
1806
1806
|
lines.push(
|
|
1807
1807
|
`Slowest: ${summary.longestStep.name} (${formatDuration2(summary.longestStep.durationMs)}, ${summary.longestStep.type})`
|
|
@@ -1955,6 +1955,369 @@ function findFirstCausalFailure(events, options = {}) {
|
|
|
1955
1955
|
);
|
|
1956
1956
|
}
|
|
1957
1957
|
|
|
1958
|
+
// packages/core/src/diagnostics/programmatic.ts
|
|
1959
|
+
var PROGRAMMATIC_DIAGNOSTIC_SPECS = Object.freeze({
|
|
1960
|
+
AI_TRACE_INPUT_INVALID: {
|
|
1961
|
+
code: "AI_TRACE_INPUT_INVALID",
|
|
1962
|
+
summary: 'Expected { type: "file", path }, { type: "directory", path }, { type: "string", content }, { type: "buffer", content }, or { type: "stdin" }.',
|
|
1963
|
+
remediation: "For a file path, use openTraceFile(path).",
|
|
1964
|
+
relatedCodes: ["invalid_input"]
|
|
1965
|
+
},
|
|
1966
|
+
AI_TRACE_FORMAT_UNSUPPORTED: {
|
|
1967
|
+
code: "AI_TRACE_FORMAT_UNSUPPORTED",
|
|
1968
|
+
summary: "No trace reader could detect the input format.",
|
|
1969
|
+
remediation: "Pass an AgentInspect JSONL file via openTraceFile, or set options.format to a registered reader.",
|
|
1970
|
+
relatedCodes: ["unsupported_format"]
|
|
1971
|
+
},
|
|
1972
|
+
AI_TRACE_FORMAT_AMBIGUOUS: {
|
|
1973
|
+
code: "AI_TRACE_FORMAT_AMBIGUOUS",
|
|
1974
|
+
summary: "Multiple trace readers matched the input with equal confidence.",
|
|
1975
|
+
remediation: "Set options.format explicitly to disambiguate the reader.",
|
|
1976
|
+
relatedCodes: ["ambiguous_format"]
|
|
1977
|
+
},
|
|
1978
|
+
AI_TRACE_FACTS_INPUT_NOT_NORMALIZED: {
|
|
1979
|
+
code: "AI_TRACE_FACTS_INPUT_NOT_NORMALIZED",
|
|
1980
|
+
summary: "TraceFacts requires TraceReadResult or PersistedInspectEvent[].",
|
|
1981
|
+
remediation: "Use openTraceFile() to normalize a JSONL trace first."
|
|
1982
|
+
},
|
|
1983
|
+
AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED: {
|
|
1984
|
+
code: "AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED",
|
|
1985
|
+
summary: "Multiple runs are available; select a run before executing checks.",
|
|
1986
|
+
remediation: "Pass options.runId or TraceCheckInput.selectedRun.",
|
|
1987
|
+
relatedCodes: ["AI_CHECK_RUN_SELECTION_REQUIRED"]
|
|
1988
|
+
},
|
|
1989
|
+
AI_TRACE_RELATIONSHIP_SELF_PARENT: {
|
|
1990
|
+
code: "AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
1991
|
+
summary: "A parentId equals its own event/step id.",
|
|
1992
|
+
remediation: "Reject at capture (AI_LANGGRAPH_SELF_PARENT_REJECTED) or drop via logical projection; do not invent replacement parents.",
|
|
1993
|
+
relatedCodes: [
|
|
1994
|
+
"AI_LANGGRAPH_SELF_PARENT_REJECTED",
|
|
1995
|
+
"AI_LOGICAL_SELF_PARENT_REMOVED"
|
|
1996
|
+
]
|
|
1997
|
+
},
|
|
1998
|
+
AI_TRACE_RELATIONSHIP_CYCLE: {
|
|
1999
|
+
code: "AI_TRACE_RELATIONSHIP_CYCLE",
|
|
2000
|
+
summary: "Trace contains a parentId cycle.",
|
|
2001
|
+
remediation: "Use visibility-first tree linking for legacy fixtures; prefer acyclic capture for new adapter output.",
|
|
2002
|
+
relatedCodes: ["structure.cycle"]
|
|
2003
|
+
},
|
|
2004
|
+
AI_TRACE_CONTRACT_INPUT_INVALID: {
|
|
2005
|
+
code: "AI_TRACE_CONTRACT_INPUT_INVALID",
|
|
2006
|
+
summary: "evaluateTraceContract expects ({ read }, contract) or use evaluateTraceContractRead(read, contract).",
|
|
2007
|
+
remediation: "Pass TraceCheckInput as the first argument: evaluateTraceContract({ read }, contract).",
|
|
2008
|
+
relatedCodes: ["AI_CHECK_INVALID_ARGUMENTS"]
|
|
2009
|
+
},
|
|
2010
|
+
AI_INSPECT_RUN_UNSUPPORTED_OPTION: {
|
|
2011
|
+
code: "AI_INSPECT_RUN_UNSUPPORTED_OPTION",
|
|
2012
|
+
summary: 'inspectRun does not accept a "writer" option.',
|
|
2013
|
+
remediation: "Use createInspector({ writer }) then inspector.run(...). inspectRun persists via the global JSONL path (schema 0.1)."
|
|
2014
|
+
}
|
|
2015
|
+
});
|
|
2016
|
+
function formatProgrammaticDiagnostic(code, detail) {
|
|
2017
|
+
const spec = PROGRAMMATIC_DIAGNOSTIC_SPECS[code];
|
|
2018
|
+
const summary = detail?.trim() ? detail.trim() : spec.summary;
|
|
2019
|
+
return `${code}: ${summary} Remediation: ${spec.remediation}`;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
// packages/core/src/checks/logical-events.ts
|
|
2023
|
+
function isRecord6(value) {
|
|
2024
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2025
|
+
}
|
|
2026
|
+
function legacyEvent(event) {
|
|
2027
|
+
const value = event.attributes?.legacyEvent;
|
|
2028
|
+
return typeof value === "string" ? value : void 0;
|
|
2029
|
+
}
|
|
2030
|
+
function stepIdOf(event) {
|
|
2031
|
+
const value = event.attributes?.stepId;
|
|
2032
|
+
if (typeof value === "string" && value.trim() !== "") return value;
|
|
2033
|
+
return void 0;
|
|
2034
|
+
}
|
|
2035
|
+
function cloneEvent(event) {
|
|
2036
|
+
return {
|
|
2037
|
+
...event,
|
|
2038
|
+
...event.attributes !== void 0 ? { attributes: { ...event.attributes } } : {},
|
|
2039
|
+
...event.error !== void 0 ? { error: { ...event.error } } : {},
|
|
2040
|
+
...event.tokenUsage !== void 0 ? { tokenUsage: { ...event.tokenUsage } } : {},
|
|
2041
|
+
...event.source !== void 0 ? { source: { ...event.source } } : {}
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
function mergeAttributes(start, complete) {
|
|
2045
|
+
const merged = {
|
|
2046
|
+
...isRecord6(complete.attributes) ? complete.attributes : {},
|
|
2047
|
+
...isRecord6(start.attributes) ? start.attributes : {}
|
|
2048
|
+
};
|
|
2049
|
+
merged.legacyEvent = start.attributes?.legacyEvent ?? complete.attributes?.legacyEvent;
|
|
2050
|
+
merged.legacyCompleteEvent = complete.attributes?.legacyEvent;
|
|
2051
|
+
if (complete.attributes?.errorStack !== void 0) {
|
|
2052
|
+
merged.errorStack = complete.attributes.errorStack;
|
|
2053
|
+
}
|
|
2054
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
2055
|
+
}
|
|
2056
|
+
function pairStartComplete(start, complete) {
|
|
2057
|
+
const attributes = mergeAttributes(start, complete);
|
|
2058
|
+
const paired = {
|
|
2059
|
+
...cloneEvent(start),
|
|
2060
|
+
status: complete.status,
|
|
2061
|
+
timestamp: complete.timestamp ?? start.timestamp,
|
|
2062
|
+
...complete.endedAt !== void 0 ? { endedAt: complete.endedAt } : {},
|
|
2063
|
+
...complete.durationMs !== void 0 ? { durationMs: complete.durationMs } : {},
|
|
2064
|
+
...complete.error !== void 0 ? { error: { ...complete.error } } : {},
|
|
2065
|
+
...complete.tokenUsage !== void 0 && start.tokenUsage === void 0 ? { tokenUsage: { ...complete.tokenUsage } } : {},
|
|
2066
|
+
...attributes !== void 0 ? { attributes } : {}
|
|
2067
|
+
};
|
|
2068
|
+
return {
|
|
2069
|
+
...paired,
|
|
2070
|
+
sourceEventIds: Object.freeze([start.eventId, complete.eventId]),
|
|
2071
|
+
projection: {
|
|
2072
|
+
paired: true,
|
|
2073
|
+
absorbedEventIds: Object.freeze([complete.eventId]),
|
|
2074
|
+
parentNormalized: false,
|
|
2075
|
+
...start.parentId !== void 0 ? { originalParentId: start.parentId } : {}
|
|
2076
|
+
}
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
function asLogical(event, extras) {
|
|
2080
|
+
return {
|
|
2081
|
+
...cloneEvent(event),
|
|
2082
|
+
sourceEventIds: Object.freeze([event.eventId]),
|
|
2083
|
+
projection: {
|
|
2084
|
+
paired: false,
|
|
2085
|
+
absorbedEventIds: Object.freeze([]),
|
|
2086
|
+
parentNormalized: extras?.parentNormalized === true,
|
|
2087
|
+
...{}
|
|
2088
|
+
}
|
|
2089
|
+
};
|
|
2090
|
+
}
|
|
2091
|
+
function projectLogicalEvents(events) {
|
|
2092
|
+
const diagnostics = [];
|
|
2093
|
+
const byRun = /* @__PURE__ */ new Map();
|
|
2094
|
+
for (const event of events) {
|
|
2095
|
+
const list = byRun.get(event.runId) ?? [];
|
|
2096
|
+
list.push(event);
|
|
2097
|
+
byRun.set(event.runId, list);
|
|
2098
|
+
}
|
|
2099
|
+
const absorbedIds = /* @__PURE__ */ new Set();
|
|
2100
|
+
const logicalByRawId = /* @__PURE__ */ new Map();
|
|
2101
|
+
const stepIdToLogicalId = /* @__PURE__ */ new Map();
|
|
2102
|
+
const logical = [];
|
|
2103
|
+
const orderedRuns = [...byRun.keys()].sort((a, b) => a.localeCompare(b));
|
|
2104
|
+
for (const runId of orderedRuns) {
|
|
2105
|
+
const runEvents = byRun.get(runId) ?? [];
|
|
2106
|
+
const starts = [];
|
|
2107
|
+
const completes = [];
|
|
2108
|
+
const others = [];
|
|
2109
|
+
for (const event of runEvents) {
|
|
2110
|
+
const legacy = legacyEvent(event);
|
|
2111
|
+
if (legacy === "step_started" || legacy === "run_started" && event.status === "running") {
|
|
2112
|
+
starts.push(event);
|
|
2113
|
+
} else if (legacy === "step_completed" || legacy === "run_completed") {
|
|
2114
|
+
completes.push(event);
|
|
2115
|
+
} else {
|
|
2116
|
+
others.push(event);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
const usedCompletes = /* @__PURE__ */ new Set();
|
|
2120
|
+
for (const start of starts) {
|
|
2121
|
+
const stepId = stepIdOf(start);
|
|
2122
|
+
let match;
|
|
2123
|
+
if (legacyEvent(start) === "run_started") {
|
|
2124
|
+
const candidates = completes.filter(
|
|
2125
|
+
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "run_completed"
|
|
2126
|
+
);
|
|
2127
|
+
if (candidates.length > 1) {
|
|
2128
|
+
diagnostics.push({
|
|
2129
|
+
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
2130
|
+
message: `Multiple run_completed rows for run ${runId}; using first by eventId.`,
|
|
2131
|
+
eventIds: candidates.map((c) => c.eventId)
|
|
2132
|
+
});
|
|
2133
|
+
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
2134
|
+
}
|
|
2135
|
+
match = candidates[0];
|
|
2136
|
+
} else if (stepId) {
|
|
2137
|
+
const candidates = completes.filter(
|
|
2138
|
+
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "step_completed" && stepIdOf(c) === stepId
|
|
2139
|
+
);
|
|
2140
|
+
if (candidates.length > 1) {
|
|
2141
|
+
diagnostics.push({
|
|
2142
|
+
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
2143
|
+
message: `Multiple step_completed rows for stepId ${stepId}; using first by eventId.`,
|
|
2144
|
+
eventIds: candidates.map((c) => c.eventId)
|
|
2145
|
+
});
|
|
2146
|
+
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
2147
|
+
}
|
|
2148
|
+
match = candidates[0];
|
|
2149
|
+
}
|
|
2150
|
+
if (match) {
|
|
2151
|
+
usedCompletes.add(match.eventId);
|
|
2152
|
+
absorbedIds.add(match.eventId);
|
|
2153
|
+
const paired = pairStartComplete(start, match);
|
|
2154
|
+
logical.push(paired);
|
|
2155
|
+
logicalByRawId.set(start.eventId, paired);
|
|
2156
|
+
logicalByRawId.set(match.eventId, paired);
|
|
2157
|
+
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, paired.eventId);
|
|
2158
|
+
} else {
|
|
2159
|
+
diagnostics.push({
|
|
2160
|
+
code: "AI_LOGICAL_PAIR_UNMATCHED_START",
|
|
2161
|
+
message: `No matching complete for start ${start.eventId}.`,
|
|
2162
|
+
eventIds: [start.eventId]
|
|
2163
|
+
});
|
|
2164
|
+
const alone = asLogical(start);
|
|
2165
|
+
logical.push(alone);
|
|
2166
|
+
logicalByRawId.set(start.eventId, alone);
|
|
2167
|
+
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
for (const complete of completes) {
|
|
2171
|
+
if (usedCompletes.has(complete.eventId)) continue;
|
|
2172
|
+
diagnostics.push({
|
|
2173
|
+
code: "AI_LOGICAL_PAIR_UNMATCHED_COMPLETE",
|
|
2174
|
+
message: `No matching start for complete ${complete.eventId}.`,
|
|
2175
|
+
eventIds: [complete.eventId]
|
|
2176
|
+
});
|
|
2177
|
+
const alone = asLogical(complete);
|
|
2178
|
+
logical.push(alone);
|
|
2179
|
+
logicalByRawId.set(complete.eventId, alone);
|
|
2180
|
+
const stepId = stepIdOf(complete);
|
|
2181
|
+
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
2182
|
+
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
for (const event of others) {
|
|
2186
|
+
const alone = asLogical(event);
|
|
2187
|
+
logical.push(alone);
|
|
2188
|
+
logicalByRawId.set(event.eventId, alone);
|
|
2189
|
+
const stepId = stepIdOf(event);
|
|
2190
|
+
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
2191
|
+
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
const logicalById = new Map(logical.map((e) => [e.eventId, e]));
|
|
2196
|
+
const normalized = [];
|
|
2197
|
+
for (const event of logical) {
|
|
2198
|
+
const originalParentId = event.parentId;
|
|
2199
|
+
if (!originalParentId) {
|
|
2200
|
+
normalized.push(event);
|
|
2201
|
+
continue;
|
|
2202
|
+
}
|
|
2203
|
+
let nextParent = originalParentId;
|
|
2204
|
+
let remapped = false;
|
|
2205
|
+
const viaAbsorbed = logicalByRawId.get(originalParentId);
|
|
2206
|
+
if (viaAbsorbed && viaAbsorbed.eventId !== originalParentId) {
|
|
2207
|
+
nextParent = viaAbsorbed.eventId;
|
|
2208
|
+
remapped = true;
|
|
2209
|
+
} else if (!logicalById.has(originalParentId)) {
|
|
2210
|
+
const viaStep = stepIdToLogicalId.get(`${event.runId}:${originalParentId}`);
|
|
2211
|
+
if (viaStep) {
|
|
2212
|
+
nextParent = viaStep;
|
|
2213
|
+
remapped = true;
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
if (!remapped) {
|
|
2217
|
+
if (originalParentId === event.eventId) {
|
|
2218
|
+
diagnostics.push({
|
|
2219
|
+
code: "AI_LOGICAL_SELF_PARENT_REMOVED",
|
|
2220
|
+
message: formatProgrammaticDiagnostic(
|
|
2221
|
+
"AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
2222
|
+
`Removed self-parent edge on ${event.eventId}.`
|
|
2223
|
+
),
|
|
2224
|
+
eventIds: [event.eventId]
|
|
2225
|
+
});
|
|
2226
|
+
const { parentId: _drop, ...rest } = event;
|
|
2227
|
+
normalized.push({
|
|
2228
|
+
...rest,
|
|
2229
|
+
projection: {
|
|
2230
|
+
...event.projection,
|
|
2231
|
+
parentNormalized: true,
|
|
2232
|
+
originalParentId
|
|
2233
|
+
}
|
|
2234
|
+
});
|
|
2235
|
+
continue;
|
|
2236
|
+
}
|
|
2237
|
+
if (!logicalById.has(originalParentId) && !logicalByRawId.has(originalParentId)) {
|
|
2238
|
+
const mapping = event.attributes?.parentMapping;
|
|
2239
|
+
const unresolved = mapping === "unresolved" || event.attributes?.parentUnresolved === true || event.attributes?.unresolvedParent === true || // LangGraph/framework scaffolding sentinel labels are not event ids.
|
|
2240
|
+
/^LangGraph$/i.test(originalParentId) || originalParentId.startsWith("unresolved:");
|
|
2241
|
+
if (!unresolved) {
|
|
2242
|
+
diagnostics.push({
|
|
2243
|
+
code: "AI_LOGICAL_PARENT_UNRESOLVED",
|
|
2244
|
+
message: `Parent ${originalParentId} unresolved for ${event.eventId}.`,
|
|
2245
|
+
eventIds: [event.eventId]
|
|
2246
|
+
});
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
normalized.push(event);
|
|
2250
|
+
continue;
|
|
2251
|
+
}
|
|
2252
|
+
if (nextParent === event.eventId) {
|
|
2253
|
+
diagnostics.push({
|
|
2254
|
+
code: "AI_LOGICAL_SELF_PARENT_REMOVED",
|
|
2255
|
+
message: formatProgrammaticDiagnostic(
|
|
2256
|
+
"AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
2257
|
+
`Removed self-parent edge on ${event.eventId} (was ${originalParentId}).`
|
|
2258
|
+
),
|
|
2259
|
+
eventIds: [event.eventId]
|
|
2260
|
+
});
|
|
2261
|
+
const { parentId: _drop, ...rest } = event;
|
|
2262
|
+
normalized.push({
|
|
2263
|
+
...rest,
|
|
2264
|
+
projection: {
|
|
2265
|
+
...event.projection,
|
|
2266
|
+
parentNormalized: true,
|
|
2267
|
+
originalParentId
|
|
2268
|
+
}
|
|
2269
|
+
});
|
|
2270
|
+
continue;
|
|
2271
|
+
}
|
|
2272
|
+
diagnostics.push({
|
|
2273
|
+
code: "AI_LOGICAL_PARENT_REMAPPED",
|
|
2274
|
+
message: `Remapped parent ${originalParentId} \u2192 ${nextParent} for ${event.eventId}.`,
|
|
2275
|
+
eventIds: [event.eventId]
|
|
2276
|
+
});
|
|
2277
|
+
normalized.push({
|
|
2278
|
+
...event,
|
|
2279
|
+
parentId: nextParent,
|
|
2280
|
+
projection: {
|
|
2281
|
+
...event.projection,
|
|
2282
|
+
parentNormalized: true,
|
|
2283
|
+
originalParentId
|
|
2284
|
+
}
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2287
|
+
const rawIndex = new Map(events.map((e, i) => [e.eventId, i]));
|
|
2288
|
+
normalized.sort((a, b) => {
|
|
2289
|
+
const ai = rawIndex.get(a.sourceEventIds[0]) ?? 0;
|
|
2290
|
+
const bi = rawIndex.get(b.sourceEventIds[0]) ?? 0;
|
|
2291
|
+
return ai - bi || a.eventId.localeCompare(b.eventId);
|
|
2292
|
+
});
|
|
2293
|
+
return {
|
|
2294
|
+
logicalEvents: Object.freeze(normalized),
|
|
2295
|
+
diagnostics: Object.freeze(diagnostics)
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
function resolveCanonicalToolName(event) {
|
|
2299
|
+
const attrs2 = event.attributes;
|
|
2300
|
+
const direct = pickString(attrs2, ["toolName", "tool"]);
|
|
2301
|
+
if (direct) return direct;
|
|
2302
|
+
const metadata = attrs2?.metadata;
|
|
2303
|
+
if (isRecord6(metadata)) {
|
|
2304
|
+
const nested = pickString(metadata, ["toolName", "tool"]);
|
|
2305
|
+
if (nested) return nested;
|
|
2306
|
+
}
|
|
2307
|
+
for (const prefix of ["tool:", "function:", "mcp-tools:"]) {
|
|
2308
|
+
if (event.name.startsWith(prefix)) return event.name.slice(prefix.length);
|
|
2309
|
+
}
|
|
2310
|
+
return event.name;
|
|
2311
|
+
}
|
|
2312
|
+
function pickString(record, keys) {
|
|
2313
|
+
if (!record) return void 0;
|
|
2314
|
+
for (const key of keys) {
|
|
2315
|
+
const value = record[key];
|
|
2316
|
+
if (typeof value === "string" && value.trim() !== "") return value.trim();
|
|
2317
|
+
}
|
|
2318
|
+
return void 0;
|
|
2319
|
+
}
|
|
2320
|
+
|
|
1958
2321
|
// packages/core/src/search.ts
|
|
1959
2322
|
function parseDurationFilter(expr) {
|
|
1960
2323
|
const raw = expr.trim();
|
|
@@ -2065,708 +2428,346 @@ async function searchTraces(metas, options) {
|
|
|
2065
2428
|
stepName: outcome.name,
|
|
2066
2429
|
timestamp: outcome.observedAt,
|
|
2067
2430
|
matchReason: `observation status=${outcome.status}`,
|
|
2068
|
-
matchedFields: ["outcome.status", "outcome.name"],
|
|
2069
|
-
filePath: m.filePath
|
|
2070
|
-
});
|
|
2071
|
-
}
|
|
2072
|
-
}
|
|
2073
|
-
}
|
|
2074
|
-
results.sort((a, b) => {
|
|
2075
|
-
const ta = a.timestamp ?? 0;
|
|
2076
|
-
const tb = b.timestamp ?? 0;
|
|
2077
|
-
if (ta !== tb) return tb - ta;
|
|
2078
|
-
const runCmp = a.runId.localeCompare(b.runId);
|
|
2079
|
-
if (runCmp !== 0) return runCmp;
|
|
2080
|
-
return (a.stepName ?? "").localeCompare(b.stepName ?? "");
|
|
2081
|
-
});
|
|
2082
|
-
return results.slice(0, limit);
|
|
2083
|
-
}
|
|
2084
|
-
function matchRunLevel(m, opts) {
|
|
2085
|
-
if (opts.stepTypeFilter || opts.toolQuery) return [];
|
|
2086
|
-
if (opts.statusFilter && m.status !== opts.statusFilter) return [];
|
|
2087
|
-
if (opts.nameQuery && !nameMatches(m.name ?? m.runId, opts.nameQuery)) {
|
|
2088
|
-
return [];
|
|
2089
|
-
}
|
|
2090
|
-
if (opts.durationFilter && !durationMatches(m.durationMs, opts.durationFilter)) {
|
|
2091
|
-
return [];
|
|
2092
|
-
}
|
|
2093
|
-
const fields = [];
|
|
2094
|
-
if (opts.statusFilter) fields.push("run.status");
|
|
2095
|
-
if (opts.nameQuery) fields.push("run.name");
|
|
2096
|
-
if (opts.durationFilter) fields.push("run.durationMs");
|
|
2097
|
-
if (fields.length === 0) return [];
|
|
2098
|
-
return [
|
|
2099
|
-
{
|
|
2100
|
-
runId: m.runId,
|
|
2101
|
-
runName: m.name,
|
|
2102
|
-
runStatus: m.status,
|
|
2103
|
-
timestamp: m.startedAt,
|
|
2104
|
-
durationMs: m.durationMs,
|
|
2105
|
-
matchReason: `run match: ${fields.join(", ")}`,
|
|
2106
|
-
matchedFields: fields,
|
|
2107
|
-
filePath: m.filePath
|
|
2108
|
-
}
|
|
2109
|
-
];
|
|
2110
|
-
}
|
|
2111
|
-
function matchStepLevel(m, events, opts) {
|
|
2112
|
-
const out = [];
|
|
2113
|
-
const started = /* @__PURE__ */ new Map();
|
|
2114
|
-
for (const e of events) {
|
|
2115
|
-
if (e.event === "step_started") {
|
|
2116
|
-
started.set(e.stepId, e);
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
for (const e of events) {
|
|
2120
|
-
if (e.event !== "step_completed") continue;
|
|
2121
|
-
const c = e;
|
|
2122
|
-
const s = started.get(c.stepId);
|
|
2123
|
-
if (!s) continue;
|
|
2124
|
-
const fields = [];
|
|
2125
|
-
const stepType = s.type;
|
|
2126
|
-
if (opts.stepTypeFilter && stepType !== opts.stepTypeFilter) {
|
|
2127
|
-
continue;
|
|
2128
|
-
}
|
|
2129
|
-
const hasStepFilters = opts.stepTypeFilter || opts.nameQuery || opts.toolQuery || opts.durationFilter || opts.statusFilter === "error" || opts.statusFilter === "success";
|
|
2130
|
-
if (!hasStepFilters) continue;
|
|
2131
|
-
if (opts.statusFilter === "error" && c.status === "error") {
|
|
2132
|
-
fields.push("step.status");
|
|
2133
|
-
} else if (opts.statusFilter === "success" && c.status === "success") {
|
|
2134
|
-
fields.push("step.status");
|
|
2135
|
-
} else if (opts.statusFilter === "error" || opts.statusFilter === "success") {
|
|
2136
|
-
continue;
|
|
2137
|
-
}
|
|
2138
|
-
if (opts.nameQuery) {
|
|
2139
|
-
if (!nameMatches(s.name, opts.nameQuery)) continue;
|
|
2140
|
-
fields.push("step.name");
|
|
2141
|
-
}
|
|
2142
|
-
if (opts.toolQuery) {
|
|
2143
|
-
const toolName = typeof s.metadata?.toolName === "string" ? s.metadata.toolName : s.name;
|
|
2144
|
-
if (!nameMatches(toolName, opts.toolQuery)) continue;
|
|
2145
|
-
fields.push("step.tool");
|
|
2146
|
-
}
|
|
2147
|
-
if (opts.durationFilter) {
|
|
2148
|
-
if (!durationMatches(c.durationMs, opts.durationFilter)) continue;
|
|
2149
|
-
fields.push("step.durationMs");
|
|
2150
|
-
}
|
|
2151
|
-
if (opts.stepTypeFilter) {
|
|
2152
|
-
fields.push("step.type");
|
|
2153
|
-
}
|
|
2154
|
-
if (fields.length === 0) continue;
|
|
2155
|
-
out.push({
|
|
2156
|
-
runId: m.runId,
|
|
2157
|
-
runName: m.name,
|
|
2158
|
-
runStatus: m.status,
|
|
2159
|
-
stepId: c.stepId,
|
|
2160
|
-
stepName: s.name,
|
|
2161
|
-
stepType,
|
|
2162
|
-
timestamp: s.startTime ?? s.timestamp,
|
|
2163
|
-
durationMs: c.durationMs,
|
|
2164
|
-
matchReason: `step match: ${fields.join(", ")}`,
|
|
2165
|
-
matchedFields: fields,
|
|
2166
|
-
filePath: m.filePath
|
|
2167
|
-
});
|
|
2168
|
-
}
|
|
2169
|
-
return out;
|
|
2170
|
-
}
|
|
2171
|
-
async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
|
|
2172
|
-
const metas = [];
|
|
2173
|
-
for (const fileName of fileNames) {
|
|
2174
|
-
try {
|
|
2175
|
-
const filePath = getPath(fileName);
|
|
2176
|
-
const meta = await extractMetadata(filePath);
|
|
2177
|
-
metas.push(meta);
|
|
2178
|
-
} catch {
|
|
2179
|
-
}
|
|
2180
|
-
}
|
|
2181
|
-
return metas;
|
|
2182
|
-
}
|
|
2183
|
-
|
|
2184
|
-
// packages/core/src/sessions/metadata.ts
|
|
2185
|
-
function isNonEmptyString3(value) {
|
|
2186
|
-
return typeof value === "string" && value.trim() !== "";
|
|
2187
|
-
}
|
|
2188
|
-
function finitePositiveInt(value) {
|
|
2189
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 1) {
|
|
2190
|
-
return void 0;
|
|
2191
|
-
}
|
|
2192
|
-
return Math.trunc(value);
|
|
2193
|
-
}
|
|
2194
|
-
function extractSessionWorkflowMetadata(record) {
|
|
2195
|
-
if (!record) return void 0;
|
|
2196
|
-
const out = {};
|
|
2197
|
-
let found = false;
|
|
2198
|
-
const assignString = (key, value) => {
|
|
2199
|
-
if (isNonEmptyString3(value)) {
|
|
2200
|
-
out[key] = value.trim();
|
|
2201
|
-
found = true;
|
|
2202
|
-
}
|
|
2203
|
-
};
|
|
2204
|
-
assignString("sessionId", record.sessionId);
|
|
2205
|
-
assignString("conversationId", record.conversationId);
|
|
2206
|
-
assignString("groupId", record.groupId);
|
|
2207
|
-
assignString("parentGroupId", record.parentGroupId);
|
|
2208
|
-
assignString("retryOf", record.retryOf);
|
|
2209
|
-
assignString("retryReason", record.retryReason);
|
|
2210
|
-
assignString("handoffFrom", record.handoffFrom);
|
|
2211
|
-
assignString("handoffTo", record.handoffTo);
|
|
2212
|
-
assignString("subAgentId", record.subAgentId);
|
|
2213
|
-
assignString("subAgentName", record.subAgentName);
|
|
2214
|
-
assignString("jobId", record.jobId);
|
|
2215
|
-
assignString("queueName", record.queueName);
|
|
2216
|
-
assignString("workflowName", record.workflowName);
|
|
2217
|
-
assignString("workflowStep", record.workflowStep);
|
|
2218
|
-
assignString("toolCallId", record.toolCallId);
|
|
2219
|
-
assignString("mcpToolCallId", record.mcpToolCallId);
|
|
2220
|
-
assignString("linkedStepId", record.linkedStepId);
|
|
2221
|
-
assignString("operationId", record.operationId);
|
|
2222
|
-
assignString("attemptId", record.attemptId);
|
|
2223
|
-
assignString("fallbackOf", record.fallbackOf);
|
|
2224
|
-
assignString("idempotencyKey", record.idempotencyKey);
|
|
2225
|
-
assignString("correlationId", record.correlationId);
|
|
2226
|
-
assignString("requestId", record.requestId);
|
|
2227
|
-
assignString("decisionId", record.decisionId);
|
|
2228
|
-
const attempt = finitePositiveInt(record.attempt);
|
|
2229
|
-
if (attempt !== void 0) {
|
|
2230
|
-
out.attempt = attempt;
|
|
2231
|
-
found = true;
|
|
2232
|
-
}
|
|
2233
|
-
const attemptNumber = finitePositiveInt(record.attemptNumber);
|
|
2234
|
-
if (attemptNumber !== void 0) {
|
|
2235
|
-
out.attemptNumber = attemptNumber;
|
|
2236
|
-
found = true;
|
|
2237
|
-
}
|
|
2238
|
-
return found ? out : void 0;
|
|
2239
|
-
}
|
|
2240
|
-
|
|
2241
|
-
// packages/core/src/bundle/safety-status.ts
|
|
2242
|
-
function aggregateBundleSafeStatus(statuses) {
|
|
2243
|
-
if (statuses.length === 0) return "UNKNOWN";
|
|
2244
|
-
if (statuses.some((status) => status === "UNSAFE")) return "UNSAFE";
|
|
2245
|
-
if (statuses.some((status) => status === "UNKNOWN")) return "UNKNOWN";
|
|
2246
|
-
if (statuses.some((status) => status === "SAFE WITH WARNINGS")) return "SAFE WITH WARNINGS";
|
|
2247
|
-
return "SAFE";
|
|
2248
|
-
}
|
|
2249
|
-
function toMetadataSafeStatus(status) {
|
|
2250
|
-
if (status === "SAFE WITH WARNINGS") return "SAFE_WITH_WARNINGS";
|
|
2251
|
-
return status;
|
|
2252
|
-
}
|
|
2253
|
-
function bundleFailsOnSafety(status, allowUnsafe) {
|
|
2254
|
-
return status === "UNSAFE" || status === "UNKNOWN";
|
|
2255
|
-
}
|
|
2256
|
-
|
|
2257
|
-
// packages/core/src/bundle/manifest.ts
|
|
2258
|
-
var BUNDLE_NOTE = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification. Review before sharing.";
|
|
2259
|
-
function buildBundleMetadata(parts) {
|
|
2260
|
-
const aggregate = aggregateBundleSafeStatus(
|
|
2261
|
-
parts.checks.runs.map((run) => run.status)
|
|
2262
|
-
);
|
|
2263
|
-
return {
|
|
2264
|
-
createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2265
|
-
agentInspectVersion: parts.agentInspectVersion,
|
|
2266
|
-
redactionProfile: parts.profile,
|
|
2267
|
-
sourceTraceCount: parts.resolve.runIds.length,
|
|
2268
|
-
runIds: [...parts.resolve.runIds],
|
|
2269
|
-
safeStatus: toMetadataSafeStatus(aggregate),
|
|
2270
|
-
files: [...parts.files].sort((a, b) => a.localeCompare(b)),
|
|
2271
|
-
note: BUNDLE_NOTE,
|
|
2272
|
-
...parts.resolve.sessionId !== void 0 ? { sessionId: parts.resolve.sessionId } : {},
|
|
2273
|
-
...parts.resolve.since !== void 0 ? { since: parts.resolve.since } : {}
|
|
2274
|
-
};
|
|
2275
|
-
}
|
|
2276
|
-
|
|
2277
|
-
// packages/core/src/evidence/types.ts
|
|
2278
|
-
var EVIDENCE_FORMAT_VERSION = "1.0";
|
|
2279
|
-
var EVIDENCE_ASSESSMENT_NOTE = "Best-effort local safety verification only; not a compliance certification.";
|
|
2280
|
-
var EVIDENCE_MANIFEST_FILENAME = "evidence.json";
|
|
2281
|
-
function sha256Hex(data) {
|
|
2282
|
-
const hash = crypto.createHash("sha256");
|
|
2283
|
-
if (typeof data === "string") {
|
|
2284
|
-
hash.update(data, "utf8");
|
|
2285
|
-
} else {
|
|
2286
|
-
hash.update(data);
|
|
2431
|
+
matchedFields: ["outcome.status", "outcome.name"],
|
|
2432
|
+
filePath: m.filePath
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2287
2436
|
}
|
|
2288
|
-
|
|
2437
|
+
results.sort((a, b) => {
|
|
2438
|
+
const ta = a.timestamp ?? 0;
|
|
2439
|
+
const tb = b.timestamp ?? 0;
|
|
2440
|
+
if (ta !== tb) return tb - ta;
|
|
2441
|
+
const runCmp = a.runId.localeCompare(b.runId);
|
|
2442
|
+
if (runCmp !== 0) return runCmp;
|
|
2443
|
+
return (a.stepName ?? "").localeCompare(b.stepName ?? "");
|
|
2444
|
+
});
|
|
2445
|
+
return results.slice(0, limit);
|
|
2289
2446
|
}
|
|
2290
|
-
function
|
|
2291
|
-
if (
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
if (path14__default.default.isAbsolute(trimmed) || trimmed.startsWith("/")) {
|
|
2296
|
-
throw new Error(`Evidence file path must be relative: ${relativePath}`);
|
|
2447
|
+
function matchRunLevel(m, opts) {
|
|
2448
|
+
if (opts.stepTypeFilter || opts.toolQuery) return [];
|
|
2449
|
+
if (opts.statusFilter && m.status !== opts.statusFilter) return [];
|
|
2450
|
+
if (opts.nameQuery && !nameMatches(m.name ?? m.runId, opts.nameQuery)) {
|
|
2451
|
+
return [];
|
|
2297
2452
|
}
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
throw new Error(`Evidence file path must be relative: ${relativePath}`);
|
|
2453
|
+
if (opts.durationFilter && !durationMatches(m.durationMs, opts.durationFilter)) {
|
|
2454
|
+
return [];
|
|
2301
2455
|
}
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2456
|
+
const fields = [];
|
|
2457
|
+
if (opts.statusFilter) fields.push("run.status");
|
|
2458
|
+
if (opts.nameQuery) fields.push("run.name");
|
|
2459
|
+
if (opts.durationFilter) fields.push("run.durationMs");
|
|
2460
|
+
if (fields.length === 0) return [];
|
|
2461
|
+
return [
|
|
2462
|
+
{
|
|
2463
|
+
runId: m.runId,
|
|
2464
|
+
runName: m.name,
|
|
2465
|
+
runStatus: m.status,
|
|
2466
|
+
timestamp: m.startedAt,
|
|
2467
|
+
durationMs: m.durationMs,
|
|
2468
|
+
matchReason: `run match: ${fields.join(", ")}`,
|
|
2469
|
+
matchedFields: fields,
|
|
2470
|
+
filePath: m.filePath
|
|
2305
2471
|
}
|
|
2306
|
-
|
|
2307
|
-
return parts.join("/");
|
|
2308
|
-
}
|
|
2309
|
-
|
|
2310
|
-
// packages/core/src/evidence/manifest.ts
|
|
2311
|
-
function stable(value) {
|
|
2312
|
-
if (Array.isArray(value)) return value.map(stable);
|
|
2313
|
-
if (value === null || typeof value !== "object") return value;
|
|
2314
|
-
const record = value;
|
|
2315
|
-
return Object.fromEntries(
|
|
2316
|
-
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable(record[key])])
|
|
2317
|
-
);
|
|
2318
|
-
}
|
|
2319
|
-
function serializeEvidenceManifest(manifest) {
|
|
2320
|
-
return `${JSON.stringify(stable(manifest), null, 2)}
|
|
2321
|
-
`;
|
|
2472
|
+
];
|
|
2322
2473
|
}
|
|
2323
|
-
function
|
|
2324
|
-
const
|
|
2325
|
-
const
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
return "redacted-trace";
|
|
2331
|
-
}
|
|
2332
|
-
if (base === "check-results.json") {
|
|
2333
|
-
return "checks";
|
|
2334
|
-
}
|
|
2335
|
-
if (base === "contract.resolved.json") {
|
|
2336
|
-
return "contract";
|
|
2337
|
-
}
|
|
2338
|
-
if (base === "redaction-report.json") {
|
|
2339
|
-
return "redaction-report";
|
|
2340
|
-
}
|
|
2341
|
-
if (base === "summary.md") {
|
|
2342
|
-
return "summary";
|
|
2474
|
+
function matchStepLevel(m, events, opts) {
|
|
2475
|
+
const out = [];
|
|
2476
|
+
const started = /* @__PURE__ */ new Map();
|
|
2477
|
+
for (const e of events) {
|
|
2478
|
+
if (e.event === "step_started") {
|
|
2479
|
+
started.set(e.stepId, e);
|
|
2480
|
+
}
|
|
2343
2481
|
}
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
const
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
);
|
|
2482
|
+
for (const e of events) {
|
|
2483
|
+
if (e.event !== "step_completed") continue;
|
|
2484
|
+
const c = e;
|
|
2485
|
+
const s = started.get(c.stepId);
|
|
2486
|
+
if (!s) continue;
|
|
2487
|
+
const fields = [];
|
|
2488
|
+
const stepType = s.type;
|
|
2489
|
+
if (opts.stepTypeFilter && stepType !== opts.stepTypeFilter) {
|
|
2490
|
+
continue;
|
|
2354
2491
|
}
|
|
2355
|
-
|
|
2356
|
-
|
|
2492
|
+
const hasStepFilters = opts.stepTypeFilter || opts.nameQuery || opts.toolQuery || opts.durationFilter || opts.statusFilter === "error" || opts.statusFilter === "success";
|
|
2493
|
+
if (!hasStepFilters) continue;
|
|
2494
|
+
if (opts.statusFilter === "error" && c.status === "error") {
|
|
2495
|
+
fields.push("step.status");
|
|
2496
|
+
} else if (opts.statusFilter === "success" && c.status === "success") {
|
|
2497
|
+
fields.push("step.status");
|
|
2498
|
+
} else if (opts.statusFilter === "error" || opts.statusFilter === "success") {
|
|
2499
|
+
continue;
|
|
2357
2500
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
role: file.role ?? inferEvidenceFileRole(relativePath)
|
|
2362
|
-
});
|
|
2363
|
-
}
|
|
2364
|
-
return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
2365
|
-
}
|
|
2366
|
-
function buildEvidenceManifest(parts) {
|
|
2367
|
-
const runIds = [...parts.runIds];
|
|
2368
|
-
if (runIds.length === 0) {
|
|
2369
|
-
throw new Error("Evidence manifest requires at least one run id.");
|
|
2370
|
-
}
|
|
2371
|
-
for (const item of parts.sourceHashes) {
|
|
2372
|
-
if (!runIds.includes(item.runId)) {
|
|
2373
|
-
throw new Error(`sourceHashes runId "${item.runId}" is not listed in source.runIds.`);
|
|
2501
|
+
if (opts.nameQuery) {
|
|
2502
|
+
if (!nameMatches(s.name, opts.nameQuery)) continue;
|
|
2503
|
+
fields.push("step.name");
|
|
2374
2504
|
}
|
|
2375
|
-
if (
|
|
2376
|
-
|
|
2505
|
+
if (opts.toolQuery) {
|
|
2506
|
+
const toolName = typeof s.metadata?.toolName === "string" ? s.metadata.toolName : s.name;
|
|
2507
|
+
if (!nameMatches(toolName, opts.toolQuery)) continue;
|
|
2508
|
+
fields.push("step.tool");
|
|
2377
2509
|
}
|
|
2510
|
+
if (opts.durationFilter) {
|
|
2511
|
+
if (!durationMatches(c.durationMs, opts.durationFilter)) continue;
|
|
2512
|
+
fields.push("step.durationMs");
|
|
2513
|
+
}
|
|
2514
|
+
if (opts.stepTypeFilter) {
|
|
2515
|
+
fields.push("step.type");
|
|
2516
|
+
}
|
|
2517
|
+
if (fields.length === 0) continue;
|
|
2518
|
+
out.push({
|
|
2519
|
+
runId: m.runId,
|
|
2520
|
+
runName: m.name,
|
|
2521
|
+
runStatus: m.status,
|
|
2522
|
+
stepId: c.stepId,
|
|
2523
|
+
stepName: s.name,
|
|
2524
|
+
stepType,
|
|
2525
|
+
timestamp: s.startTime ?? s.timestamp,
|
|
2526
|
+
durationMs: c.durationMs,
|
|
2527
|
+
matchReason: `step match: ${fields.join(", ")}`,
|
|
2528
|
+
matchedFields: fields,
|
|
2529
|
+
filePath: m.filePath
|
|
2530
|
+
});
|
|
2378
2531
|
}
|
|
2379
|
-
|
|
2380
|
-
status: parts.assessmentStatus,
|
|
2381
|
-
note: parts.note ?? EVIDENCE_ASSESSMENT_NOTE
|
|
2382
|
-
};
|
|
2383
|
-
if (parts.sourceStatus !== void 0) {
|
|
2384
|
-
assessment.sourceStatus = parts.sourceStatus;
|
|
2385
|
-
}
|
|
2386
|
-
return {
|
|
2387
|
-
evidenceFormatVersion: EVIDENCE_FORMAT_VERSION,
|
|
2388
|
-
generator: {
|
|
2389
|
-
name: parts.generatorName ?? "agent-inspect",
|
|
2390
|
-
version: parts.generatorVersion
|
|
2391
|
-
},
|
|
2392
|
-
createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2393
|
-
source: {
|
|
2394
|
-
runIds,
|
|
2395
|
-
traceSchemaVersions: [...parts.traceSchemaVersions].sort((a, b) => a.localeCompare(b)),
|
|
2396
|
-
sourceHashes: [...parts.sourceHashes].sort((a, b) => a.runId.localeCompare(b.runId))
|
|
2397
|
-
},
|
|
2398
|
-
policy: {
|
|
2399
|
-
redactionProfile: parts.redactionProfile,
|
|
2400
|
-
verificationPolicy: parts.verificationPolicy ?? parts.redactionProfile
|
|
2401
|
-
},
|
|
2402
|
-
assessment,
|
|
2403
|
-
...parts.semantics !== void 0 ? { semantics: { ...parts.semantics } } : {},
|
|
2404
|
-
...parts.contract !== void 0 ? { contract: { ...parts.contract } } : {},
|
|
2405
|
-
files: buildEvidenceFileEntries(parts.files)
|
|
2406
|
-
};
|
|
2407
|
-
}
|
|
2408
|
-
|
|
2409
|
-
// packages/core/src/diagnostics/programmatic.ts
|
|
2410
|
-
var PROGRAMMATIC_DIAGNOSTIC_SPECS = Object.freeze({
|
|
2411
|
-
AI_TRACE_INPUT_INVALID: {
|
|
2412
|
-
code: "AI_TRACE_INPUT_INVALID",
|
|
2413
|
-
summary: 'Expected { type: "file", path }, { type: "directory", path }, { type: "string", content }, { type: "buffer", content }, or { type: "stdin" }.',
|
|
2414
|
-
remediation: "For a file path, use openTraceFile(path).",
|
|
2415
|
-
relatedCodes: ["invalid_input"]
|
|
2416
|
-
},
|
|
2417
|
-
AI_TRACE_FORMAT_UNSUPPORTED: {
|
|
2418
|
-
code: "AI_TRACE_FORMAT_UNSUPPORTED",
|
|
2419
|
-
summary: "No trace reader could detect the input format.",
|
|
2420
|
-
remediation: "Pass an AgentInspect JSONL file via openTraceFile, or set options.format to a registered reader.",
|
|
2421
|
-
relatedCodes: ["unsupported_format"]
|
|
2422
|
-
},
|
|
2423
|
-
AI_TRACE_FORMAT_AMBIGUOUS: {
|
|
2424
|
-
code: "AI_TRACE_FORMAT_AMBIGUOUS",
|
|
2425
|
-
summary: "Multiple trace readers matched the input with equal confidence.",
|
|
2426
|
-
remediation: "Set options.format explicitly to disambiguate the reader.",
|
|
2427
|
-
relatedCodes: ["ambiguous_format"]
|
|
2428
|
-
},
|
|
2429
|
-
AI_TRACE_FACTS_INPUT_NOT_NORMALIZED: {
|
|
2430
|
-
code: "AI_TRACE_FACTS_INPUT_NOT_NORMALIZED",
|
|
2431
|
-
summary: "TraceFacts requires TraceReadResult or PersistedInspectEvent[].",
|
|
2432
|
-
remediation: "Use openTraceFile() to normalize a JSONL trace first."
|
|
2433
|
-
},
|
|
2434
|
-
AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED: {
|
|
2435
|
-
code: "AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED",
|
|
2436
|
-
summary: "Multiple runs are available; select a run before executing checks.",
|
|
2437
|
-
remediation: "Pass options.runId or TraceCheckInput.selectedRun.",
|
|
2438
|
-
relatedCodes: ["AI_CHECK_RUN_SELECTION_REQUIRED"]
|
|
2439
|
-
},
|
|
2440
|
-
AI_TRACE_RELATIONSHIP_SELF_PARENT: {
|
|
2441
|
-
code: "AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
2442
|
-
summary: "A parentId equals its own event/step id.",
|
|
2443
|
-
remediation: "Reject at capture (AI_LANGGRAPH_SELF_PARENT_REJECTED) or drop via logical projection; do not invent replacement parents.",
|
|
2444
|
-
relatedCodes: [
|
|
2445
|
-
"AI_LANGGRAPH_SELF_PARENT_REJECTED",
|
|
2446
|
-
"AI_LOGICAL_SELF_PARENT_REMOVED"
|
|
2447
|
-
]
|
|
2448
|
-
},
|
|
2449
|
-
AI_TRACE_RELATIONSHIP_CYCLE: {
|
|
2450
|
-
code: "AI_TRACE_RELATIONSHIP_CYCLE",
|
|
2451
|
-
summary: "Trace contains a parentId cycle.",
|
|
2452
|
-
remediation: "Use visibility-first tree linking for legacy fixtures; prefer acyclic capture for new adapter output.",
|
|
2453
|
-
relatedCodes: ["structure.cycle"]
|
|
2454
|
-
},
|
|
2455
|
-
AI_TRACE_CONTRACT_INPUT_INVALID: {
|
|
2456
|
-
code: "AI_TRACE_CONTRACT_INPUT_INVALID",
|
|
2457
|
-
summary: "evaluateTraceContract expects ({ read }, contract) or use evaluateTraceContractRead(read, contract).",
|
|
2458
|
-
remediation: "Pass TraceCheckInput as the first argument: evaluateTraceContract({ read }, contract).",
|
|
2459
|
-
relatedCodes: ["AI_CHECK_INVALID_ARGUMENTS"]
|
|
2460
|
-
},
|
|
2461
|
-
AI_INSPECT_RUN_UNSUPPORTED_OPTION: {
|
|
2462
|
-
code: "AI_INSPECT_RUN_UNSUPPORTED_OPTION",
|
|
2463
|
-
summary: 'inspectRun does not accept a "writer" option.',
|
|
2464
|
-
remediation: "Use createInspector({ writer }) then inspector.run(...). inspectRun persists via the global JSONL path (schema 0.1)."
|
|
2465
|
-
}
|
|
2466
|
-
});
|
|
2467
|
-
function formatProgrammaticDiagnostic(code, detail) {
|
|
2468
|
-
const spec = PROGRAMMATIC_DIAGNOSTIC_SPECS[code];
|
|
2469
|
-
const summary = detail?.trim() ? detail.trim() : spec.summary;
|
|
2470
|
-
return `${code}: ${summary} Remediation: ${spec.remediation}`;
|
|
2471
|
-
}
|
|
2472
|
-
|
|
2473
|
-
// packages/core/src/checks/logical-events.ts
|
|
2474
|
-
function isRecord6(value) {
|
|
2475
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2476
|
-
}
|
|
2477
|
-
function legacyEvent(event) {
|
|
2478
|
-
const value = event.attributes?.legacyEvent;
|
|
2479
|
-
return typeof value === "string" ? value : void 0;
|
|
2532
|
+
return out;
|
|
2480
2533
|
}
|
|
2481
|
-
function
|
|
2482
|
-
const
|
|
2483
|
-
|
|
2484
|
-
|
|
2534
|
+
async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
|
|
2535
|
+
const metas = [];
|
|
2536
|
+
for (const fileName of fileNames) {
|
|
2537
|
+
try {
|
|
2538
|
+
const filePath = getPath(fileName);
|
|
2539
|
+
const meta = await extractMetadata(filePath);
|
|
2540
|
+
metas.push(meta);
|
|
2541
|
+
} catch {
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
return metas;
|
|
2485
2545
|
}
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
...event.error !== void 0 ? { error: { ...event.error } } : {},
|
|
2491
|
-
...event.tokenUsage !== void 0 ? { tokenUsage: { ...event.tokenUsage } } : {},
|
|
2492
|
-
...event.source !== void 0 ? { source: { ...event.source } } : {}
|
|
2493
|
-
};
|
|
2546
|
+
|
|
2547
|
+
// packages/core/src/sessions/metadata.ts
|
|
2548
|
+
function isNonEmptyString3(value) {
|
|
2549
|
+
return typeof value === "string" && value.trim() !== "";
|
|
2494
2550
|
}
|
|
2495
|
-
function
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
...isRecord6(start.attributes) ? start.attributes : {}
|
|
2499
|
-
};
|
|
2500
|
-
merged.legacyEvent = start.attributes?.legacyEvent ?? complete.attributes?.legacyEvent;
|
|
2501
|
-
merged.legacyCompleteEvent = complete.attributes?.legacyEvent;
|
|
2502
|
-
if (complete.attributes?.errorStack !== void 0) {
|
|
2503
|
-
merged.errorStack = complete.attributes.errorStack;
|
|
2551
|
+
function finitePositiveInt(value) {
|
|
2552
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 1) {
|
|
2553
|
+
return void 0;
|
|
2504
2554
|
}
|
|
2505
|
-
return
|
|
2555
|
+
return Math.trunc(value);
|
|
2506
2556
|
}
|
|
2507
|
-
function
|
|
2508
|
-
|
|
2509
|
-
const
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
...complete.error !== void 0 ? { error: { ...complete.error } } : {},
|
|
2516
|
-
...complete.tokenUsage !== void 0 && start.tokenUsage === void 0 ? { tokenUsage: { ...complete.tokenUsage } } : {},
|
|
2517
|
-
...attributes !== void 0 ? { attributes } : {}
|
|
2518
|
-
};
|
|
2519
|
-
return {
|
|
2520
|
-
...paired,
|
|
2521
|
-
sourceEventIds: Object.freeze([start.eventId, complete.eventId]),
|
|
2522
|
-
projection: {
|
|
2523
|
-
paired: true,
|
|
2524
|
-
absorbedEventIds: Object.freeze([complete.eventId]),
|
|
2525
|
-
parentNormalized: false,
|
|
2526
|
-
...start.parentId !== void 0 ? { originalParentId: start.parentId } : {}
|
|
2557
|
+
function extractSessionWorkflowMetadata(record) {
|
|
2558
|
+
if (!record) return void 0;
|
|
2559
|
+
const out = {};
|
|
2560
|
+
let found = false;
|
|
2561
|
+
const assignString = (key, value) => {
|
|
2562
|
+
if (isNonEmptyString3(value)) {
|
|
2563
|
+
out[key] = value.trim();
|
|
2564
|
+
found = true;
|
|
2527
2565
|
}
|
|
2528
2566
|
};
|
|
2567
|
+
assignString("sessionId", record.sessionId);
|
|
2568
|
+
assignString("conversationId", record.conversationId);
|
|
2569
|
+
assignString("groupId", record.groupId);
|
|
2570
|
+
assignString("parentGroupId", record.parentGroupId);
|
|
2571
|
+
assignString("retryOf", record.retryOf);
|
|
2572
|
+
assignString("retryReason", record.retryReason);
|
|
2573
|
+
assignString("handoffFrom", record.handoffFrom);
|
|
2574
|
+
assignString("handoffTo", record.handoffTo);
|
|
2575
|
+
assignString("subAgentId", record.subAgentId);
|
|
2576
|
+
assignString("subAgentName", record.subAgentName);
|
|
2577
|
+
assignString("jobId", record.jobId);
|
|
2578
|
+
assignString("queueName", record.queueName);
|
|
2579
|
+
assignString("workflowName", record.workflowName);
|
|
2580
|
+
assignString("workflowStep", record.workflowStep);
|
|
2581
|
+
assignString("toolCallId", record.toolCallId);
|
|
2582
|
+
assignString("mcpToolCallId", record.mcpToolCallId);
|
|
2583
|
+
assignString("linkedStepId", record.linkedStepId);
|
|
2584
|
+
assignString("operationId", record.operationId);
|
|
2585
|
+
assignString("attemptId", record.attemptId);
|
|
2586
|
+
assignString("fallbackOf", record.fallbackOf);
|
|
2587
|
+
assignString("idempotencyKey", record.idempotencyKey);
|
|
2588
|
+
assignString("correlationId", record.correlationId);
|
|
2589
|
+
assignString("requestId", record.requestId);
|
|
2590
|
+
assignString("decisionId", record.decisionId);
|
|
2591
|
+
const attempt = finitePositiveInt(record.attempt);
|
|
2592
|
+
if (attempt !== void 0) {
|
|
2593
|
+
out.attempt = attempt;
|
|
2594
|
+
found = true;
|
|
2595
|
+
}
|
|
2596
|
+
const attemptNumber = finitePositiveInt(record.attemptNumber);
|
|
2597
|
+
if (attemptNumber !== void 0) {
|
|
2598
|
+
out.attemptNumber = attemptNumber;
|
|
2599
|
+
found = true;
|
|
2600
|
+
}
|
|
2601
|
+
return found ? out : void 0;
|
|
2529
2602
|
}
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
...{}
|
|
2539
|
-
}
|
|
2540
|
-
};
|
|
2603
|
+
|
|
2604
|
+
// packages/core/src/bundle/safety-status.ts
|
|
2605
|
+
function aggregateBundleSafeStatus(statuses) {
|
|
2606
|
+
if (statuses.length === 0) return "UNKNOWN";
|
|
2607
|
+
if (statuses.some((status) => status === "UNSAFE")) return "UNSAFE";
|
|
2608
|
+
if (statuses.some((status) => status === "UNKNOWN")) return "UNKNOWN";
|
|
2609
|
+
if (statuses.some((status) => status === "SAFE WITH WARNINGS")) return "SAFE WITH WARNINGS";
|
|
2610
|
+
return "SAFE";
|
|
2541
2611
|
}
|
|
2542
|
-
function
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
const
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
2581
|
-
message: `Multiple run_completed rows for run ${runId}; using first by eventId.`,
|
|
2582
|
-
eventIds: candidates.map((c) => c.eventId)
|
|
2583
|
-
});
|
|
2584
|
-
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
2585
|
-
}
|
|
2586
|
-
match = candidates[0];
|
|
2587
|
-
} else if (stepId) {
|
|
2588
|
-
const candidates = completes.filter(
|
|
2589
|
-
(c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "step_completed" && stepIdOf(c) === stepId
|
|
2590
|
-
);
|
|
2591
|
-
if (candidates.length > 1) {
|
|
2592
|
-
diagnostics.push({
|
|
2593
|
-
code: "AI_LOGICAL_PAIR_AMBIGUOUS",
|
|
2594
|
-
message: `Multiple step_completed rows for stepId ${stepId}; using first by eventId.`,
|
|
2595
|
-
eventIds: candidates.map((c) => c.eventId)
|
|
2596
|
-
});
|
|
2597
|
-
candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
|
|
2598
|
-
}
|
|
2599
|
-
match = candidates[0];
|
|
2600
|
-
}
|
|
2601
|
-
if (match) {
|
|
2602
|
-
usedCompletes.add(match.eventId);
|
|
2603
|
-
absorbedIds.add(match.eventId);
|
|
2604
|
-
const paired = pairStartComplete(start, match);
|
|
2605
|
-
logical.push(paired);
|
|
2606
|
-
logicalByRawId.set(start.eventId, paired);
|
|
2607
|
-
logicalByRawId.set(match.eventId, paired);
|
|
2608
|
-
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, paired.eventId);
|
|
2609
|
-
} else {
|
|
2610
|
-
diagnostics.push({
|
|
2611
|
-
code: "AI_LOGICAL_PAIR_UNMATCHED_START",
|
|
2612
|
-
message: `No matching complete for start ${start.eventId}.`,
|
|
2613
|
-
eventIds: [start.eventId]
|
|
2614
|
-
});
|
|
2615
|
-
const alone = asLogical(start);
|
|
2616
|
-
logical.push(alone);
|
|
2617
|
-
logicalByRawId.set(start.eventId, alone);
|
|
2618
|
-
if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2619
|
-
}
|
|
2620
|
-
}
|
|
2621
|
-
for (const complete of completes) {
|
|
2622
|
-
if (usedCompletes.has(complete.eventId)) continue;
|
|
2623
|
-
diagnostics.push({
|
|
2624
|
-
code: "AI_LOGICAL_PAIR_UNMATCHED_COMPLETE",
|
|
2625
|
-
message: `No matching start for complete ${complete.eventId}.`,
|
|
2626
|
-
eventIds: [complete.eventId]
|
|
2627
|
-
});
|
|
2628
|
-
const alone = asLogical(complete);
|
|
2629
|
-
logical.push(alone);
|
|
2630
|
-
logicalByRawId.set(complete.eventId, alone);
|
|
2631
|
-
const stepId = stepIdOf(complete);
|
|
2632
|
-
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
2633
|
-
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2634
|
-
}
|
|
2635
|
-
}
|
|
2636
|
-
for (const event of others) {
|
|
2637
|
-
const alone = asLogical(event);
|
|
2638
|
-
logical.push(alone);
|
|
2639
|
-
logicalByRawId.set(event.eventId, alone);
|
|
2640
|
-
const stepId = stepIdOf(event);
|
|
2641
|
-
if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
|
|
2642
|
-
stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
|
|
2643
|
-
}
|
|
2644
|
-
}
|
|
2612
|
+
function toMetadataSafeStatus(status) {
|
|
2613
|
+
if (status === "SAFE WITH WARNINGS") return "SAFE_WITH_WARNINGS";
|
|
2614
|
+
return status;
|
|
2615
|
+
}
|
|
2616
|
+
function bundleFailsOnSafety(status, allowUnsafe) {
|
|
2617
|
+
return status === "UNSAFE" || status === "UNKNOWN";
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
// packages/core/src/bundle/manifest.ts
|
|
2621
|
+
var BUNDLE_NOTE = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification. Review before sharing.";
|
|
2622
|
+
function buildBundleMetadata(parts) {
|
|
2623
|
+
const aggregate = aggregateBundleSafeStatus(
|
|
2624
|
+
parts.checks.runs.map((run) => run.status)
|
|
2625
|
+
);
|
|
2626
|
+
return {
|
|
2627
|
+
createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2628
|
+
agentInspectVersion: parts.agentInspectVersion,
|
|
2629
|
+
redactionProfile: parts.profile,
|
|
2630
|
+
sourceTraceCount: parts.resolve.runIds.length,
|
|
2631
|
+
runIds: [...parts.resolve.runIds],
|
|
2632
|
+
safeStatus: toMetadataSafeStatus(aggregate),
|
|
2633
|
+
files: [...parts.files].sort((a, b) => a.localeCompare(b)),
|
|
2634
|
+
note: BUNDLE_NOTE,
|
|
2635
|
+
...parts.resolve.sessionId !== void 0 ? { sessionId: parts.resolve.sessionId } : {},
|
|
2636
|
+
...parts.resolve.since !== void 0 ? { since: parts.resolve.since } : {}
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
// packages/core/src/evidence/types.ts
|
|
2641
|
+
var EVIDENCE_FORMAT_VERSION = "1.0";
|
|
2642
|
+
var EVIDENCE_ASSESSMENT_NOTE = "Best-effort local safety verification only; not a compliance certification.";
|
|
2643
|
+
var EVIDENCE_MANIFEST_FILENAME = "evidence.json";
|
|
2644
|
+
function sha256Hex(data) {
|
|
2645
|
+
const hash = crypto.createHash("sha256");
|
|
2646
|
+
if (typeof data === "string") {
|
|
2647
|
+
hash.update(data, "utf8");
|
|
2648
|
+
} else {
|
|
2649
|
+
hash.update(data);
|
|
2645
2650
|
}
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
nextParent = viaStep;
|
|
2664
|
-
remapped = true;
|
|
2665
|
-
}
|
|
2651
|
+
return hash.digest("hex");
|
|
2652
|
+
}
|
|
2653
|
+
function assertEvidenceRelativePath(relativePath) {
|
|
2654
|
+
if (typeof relativePath !== "string" || relativePath.trim() === "") {
|
|
2655
|
+
throw new Error("Evidence file path must be a non-empty relative path.");
|
|
2656
|
+
}
|
|
2657
|
+
const trimmed = relativePath.trim().replaceAll("\\", "/");
|
|
2658
|
+
if (path14__default.default.isAbsolute(trimmed) || trimmed.startsWith("/")) {
|
|
2659
|
+
throw new Error(`Evidence file path must be relative: ${relativePath}`);
|
|
2660
|
+
}
|
|
2661
|
+
const parts = trimmed.split("/").filter((part) => part !== "");
|
|
2662
|
+
if (parts.length === 0) {
|
|
2663
|
+
throw new Error(`Evidence file path must be relative: ${relativePath}`);
|
|
2664
|
+
}
|
|
2665
|
+
for (const part of parts) {
|
|
2666
|
+
if (part === "." || part === "..") {
|
|
2667
|
+
throw new Error(`Evidence file path must not contain "." or "..": ${relativePath}`);
|
|
2666
2668
|
}
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2669
|
+
}
|
|
2670
|
+
return parts.join("/");
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
// packages/core/src/evidence/manifest.ts
|
|
2674
|
+
function stable(value) {
|
|
2675
|
+
if (Array.isArray(value)) return value.map(stable);
|
|
2676
|
+
if (value === null || typeof value !== "object") return value;
|
|
2677
|
+
const record = value;
|
|
2678
|
+
return Object.fromEntries(
|
|
2679
|
+
Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => [key, stable(record[key])])
|
|
2680
|
+
);
|
|
2681
|
+
}
|
|
2682
|
+
function serializeEvidenceManifest(manifest) {
|
|
2683
|
+
return `${JSON.stringify(stable(manifest), null, 2)}
|
|
2684
|
+
`;
|
|
2685
|
+
}
|
|
2686
|
+
function inferEvidenceFileRole(relativePath) {
|
|
2687
|
+
const normalized = assertEvidenceRelativePath(relativePath);
|
|
2688
|
+
const base = normalized.includes("/") ? normalized.slice(normalized.lastIndexOf("/") + 1) : normalized;
|
|
2689
|
+
if (base === "evidence.html" || base === "trace.html" || base.endsWith(".html")) {
|
|
2690
|
+
return "report";
|
|
2691
|
+
}
|
|
2692
|
+
if (base === "trace.jsonl" || base.endsWith(".jsonl")) {
|
|
2693
|
+
return "redacted-trace";
|
|
2694
|
+
}
|
|
2695
|
+
if (base === "check-results.json") {
|
|
2696
|
+
return "checks";
|
|
2697
|
+
}
|
|
2698
|
+
if (base === "contract.resolved.json") {
|
|
2699
|
+
return "contract";
|
|
2700
|
+
}
|
|
2701
|
+
if (base === "redaction-report.json") {
|
|
2702
|
+
return "redaction-report";
|
|
2703
|
+
}
|
|
2704
|
+
if (base === "summary.md") {
|
|
2705
|
+
return "summary";
|
|
2706
|
+
}
|
|
2707
|
+
return "other";
|
|
2708
|
+
}
|
|
2709
|
+
function buildEvidenceFileEntries(files) {
|
|
2710
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
2711
|
+
for (const file of files) {
|
|
2712
|
+
const relativePath = assertEvidenceRelativePath(file.path);
|
|
2713
|
+
if (relativePath === EVIDENCE_MANIFEST_FILENAME) {
|
|
2714
|
+
throw new Error(
|
|
2715
|
+
`Do not include ${EVIDENCE_MANIFEST_FILENAME} in packaged file hashes (self-hash is undefined).`
|
|
2716
|
+
);
|
|
2702
2717
|
}
|
|
2703
|
-
if (
|
|
2704
|
-
|
|
2705
|
-
code: "AI_LOGICAL_SELF_PARENT_REMOVED",
|
|
2706
|
-
message: formatProgrammaticDiagnostic(
|
|
2707
|
-
"AI_TRACE_RELATIONSHIP_SELF_PARENT",
|
|
2708
|
-
`Removed self-parent edge on ${event.eventId} (was ${originalParentId}).`
|
|
2709
|
-
),
|
|
2710
|
-
eventIds: [event.eventId]
|
|
2711
|
-
});
|
|
2712
|
-
const { parentId: _drop, ...rest } = event;
|
|
2713
|
-
normalized.push({
|
|
2714
|
-
...rest,
|
|
2715
|
-
projection: {
|
|
2716
|
-
...event.projection,
|
|
2717
|
-
parentNormalized: true,
|
|
2718
|
-
originalParentId
|
|
2719
|
-
}
|
|
2720
|
-
});
|
|
2721
|
-
continue;
|
|
2718
|
+
if (byPath.has(relativePath)) {
|
|
2719
|
+
throw new Error(`Duplicate evidence file path: ${relativePath}`);
|
|
2722
2720
|
}
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
});
|
|
2728
|
-
normalized.push({
|
|
2729
|
-
...event,
|
|
2730
|
-
parentId: nextParent,
|
|
2731
|
-
projection: {
|
|
2732
|
-
...event.projection,
|
|
2733
|
-
parentNormalized: true,
|
|
2734
|
-
originalParentId
|
|
2735
|
-
}
|
|
2721
|
+
byPath.set(relativePath, {
|
|
2722
|
+
path: relativePath,
|
|
2723
|
+
sha256: sha256Hex(file.content),
|
|
2724
|
+
role: file.role ?? inferEvidenceFileRole(relativePath)
|
|
2736
2725
|
});
|
|
2737
2726
|
}
|
|
2738
|
-
|
|
2739
|
-
normalized.sort((a, b) => {
|
|
2740
|
-
const ai = rawIndex.get(a.sourceEventIds[0]) ?? 0;
|
|
2741
|
-
const bi = rawIndex.get(b.sourceEventIds[0]) ?? 0;
|
|
2742
|
-
return ai - bi || a.eventId.localeCompare(b.eventId);
|
|
2743
|
-
});
|
|
2744
|
-
return {
|
|
2745
|
-
logicalEvents: Object.freeze(normalized),
|
|
2746
|
-
diagnostics: Object.freeze(diagnostics)
|
|
2747
|
-
};
|
|
2727
|
+
return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
|
|
2748
2728
|
}
|
|
2749
|
-
function
|
|
2750
|
-
const
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
const metadata = attrs2?.metadata;
|
|
2754
|
-
if (isRecord6(metadata)) {
|
|
2755
|
-
const nested = pickString(metadata, ["toolName", "tool"]);
|
|
2756
|
-
if (nested) return nested;
|
|
2729
|
+
function buildEvidenceManifest(parts) {
|
|
2730
|
+
const runIds = [...parts.runIds];
|
|
2731
|
+
if (runIds.length === 0) {
|
|
2732
|
+
throw new Error("Evidence manifest requires at least one run id.");
|
|
2757
2733
|
}
|
|
2758
|
-
for (const
|
|
2759
|
-
if (
|
|
2734
|
+
for (const item of parts.sourceHashes) {
|
|
2735
|
+
if (!runIds.includes(item.runId)) {
|
|
2736
|
+
throw new Error(`sourceHashes runId "${item.runId}" is not listed in source.runIds.`);
|
|
2737
|
+
}
|
|
2738
|
+
if (item.algorithm !== "sha256") {
|
|
2739
|
+
throw new Error(`Unsupported source hash algorithm: ${item.algorithm}`);
|
|
2740
|
+
}
|
|
2760
2741
|
}
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
if (typeof value === "string" && value.trim() !== "") return value.trim();
|
|
2742
|
+
const assessment = {
|
|
2743
|
+
status: parts.assessmentStatus,
|
|
2744
|
+
note: parts.note ?? EVIDENCE_ASSESSMENT_NOTE
|
|
2745
|
+
};
|
|
2746
|
+
if (parts.sourceStatus !== void 0) {
|
|
2747
|
+
assessment.sourceStatus = parts.sourceStatus;
|
|
2768
2748
|
}
|
|
2769
|
-
return
|
|
2749
|
+
return {
|
|
2750
|
+
evidenceFormatVersion: EVIDENCE_FORMAT_VERSION,
|
|
2751
|
+
generator: {
|
|
2752
|
+
name: parts.generatorName ?? "agent-inspect",
|
|
2753
|
+
version: parts.generatorVersion
|
|
2754
|
+
},
|
|
2755
|
+
createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
2756
|
+
source: {
|
|
2757
|
+
runIds,
|
|
2758
|
+
traceSchemaVersions: [...parts.traceSchemaVersions].sort((a, b) => a.localeCompare(b)),
|
|
2759
|
+
sourceHashes: [...parts.sourceHashes].sort((a, b) => a.runId.localeCompare(b.runId))
|
|
2760
|
+
},
|
|
2761
|
+
policy: {
|
|
2762
|
+
redactionProfile: parts.redactionProfile,
|
|
2763
|
+
verificationPolicy: parts.verificationPolicy ?? parts.redactionProfile
|
|
2764
|
+
},
|
|
2765
|
+
assessment,
|
|
2766
|
+
...parts.semantics !== void 0 ? { semantics: { ...parts.semantics } } : {},
|
|
2767
|
+
...parts.contract !== void 0 ? { contract: { ...parts.contract } } : {},
|
|
2768
|
+
...parts.inputs !== void 0 ? { inputs: { ...parts.inputs } } : {},
|
|
2769
|
+
files: buildEvidenceFileEntries(parts.files)
|
|
2770
|
+
};
|
|
2770
2771
|
}
|
|
2771
2772
|
|
|
2772
2773
|
// packages/core/src/checks/derived-failure.ts
|
|
@@ -3440,7 +3441,11 @@ function emptySummary() {
|
|
|
3440
3441
|
failed: 0,
|
|
3441
3442
|
warnings: 0,
|
|
3442
3443
|
errors: 0,
|
|
3443
|
-
rulesEvaluated: 0
|
|
3444
|
+
rulesEvaluated: 0,
|
|
3445
|
+
rulesPassed: 0,
|
|
3446
|
+
rulesWarning: 0,
|
|
3447
|
+
rulesFailed: 0,
|
|
3448
|
+
rulesError: 0
|
|
3444
3449
|
};
|
|
3445
3450
|
}
|
|
3446
3451
|
function errorResult(input, diagnostics, selectedRun, ruleExecutions = []) {
|
|
@@ -3452,7 +3457,11 @@ function errorResult(input, diagnostics, selectedRun, ruleExecutions = []) {
|
|
|
3452
3457
|
summary: {
|
|
3453
3458
|
...emptySummary(),
|
|
3454
3459
|
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
3455
|
-
rulesEvaluated: ruleExecutions.length
|
|
3460
|
+
rulesEvaluated: ruleExecutions.length,
|
|
3461
|
+
rulesPassed: ruleExecutions.filter((item) => item.status === "pass").length,
|
|
3462
|
+
rulesWarning: ruleExecutions.filter((item) => item.status === "warning").length,
|
|
3463
|
+
rulesFailed: ruleExecutions.filter((item) => item.status === "fail").length,
|
|
3464
|
+
rulesError: ruleExecutions.filter((item) => item.status === "error").length
|
|
3456
3465
|
},
|
|
3457
3466
|
findings: [],
|
|
3458
3467
|
diagnostics: [...diagnostics],
|
|
@@ -3612,7 +3621,7 @@ function normalizeFinding(rule, finding) {
|
|
|
3612
3621
|
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
3613
3622
|
};
|
|
3614
3623
|
}
|
|
3615
|
-
function summarize(findings, diagnostics,
|
|
3624
|
+
function summarize(findings, diagnostics, ruleExecutions) {
|
|
3616
3625
|
return {
|
|
3617
3626
|
passed: findings.filter((finding) => finding.status === "pass").length,
|
|
3618
3627
|
failed: findings.filter(
|
|
@@ -3622,7 +3631,11 @@ function summarize(findings, diagnostics, rulesEvaluated) {
|
|
|
3622
3631
|
(finding) => finding.status === "warning" || finding.severity === "warning"
|
|
3623
3632
|
).length,
|
|
3624
3633
|
errors: diagnostics.filter((item) => item.severity === "error").length,
|
|
3625
|
-
rulesEvaluated
|
|
3634
|
+
rulesEvaluated: ruleExecutions.length,
|
|
3635
|
+
rulesPassed: ruleExecutions.filter((item) => item.status === "pass").length,
|
|
3636
|
+
rulesWarning: ruleExecutions.filter((item) => item.status === "warning").length,
|
|
3637
|
+
rulesFailed: ruleExecutions.filter((item) => item.status === "fail").length,
|
|
3638
|
+
rulesError: ruleExecutions.filter((item) => item.status === "error").length
|
|
3626
3639
|
};
|
|
3627
3640
|
}
|
|
3628
3641
|
function classifyRuleExecution(findings, threw) {
|
|
@@ -4063,7 +4076,7 @@ function runTraceChecks(input, options = {}) {
|
|
|
4063
4076
|
}
|
|
4064
4077
|
const eventById = new Map(input.read.events.map((event) => [event.eventId, event]));
|
|
4065
4078
|
const sortedFindings = findings.sort(compareFindings(eventById));
|
|
4066
|
-
const summary = summarize(sortedFindings, diagnostics, ruleExecutions
|
|
4079
|
+
const summary = summarize(sortedFindings, diagnostics, ruleExecutions);
|
|
4067
4080
|
const status = summary.failed > 0 ? "fail" : "pass";
|
|
4068
4081
|
return {
|
|
4069
4082
|
ok: status === "pass",
|
|
@@ -8206,6 +8219,15 @@ function actionForRule(rule) {
|
|
|
8206
8219
|
if (rule.strategy === "full") return "replace";
|
|
8207
8220
|
return rule.strategy;
|
|
8208
8221
|
}
|
|
8222
|
+
function isCompleteRedactionMarker(value, replacement = "[REDACTED]") {
|
|
8223
|
+
if (typeof value !== "string") return false;
|
|
8224
|
+
const trimmed = value.trim();
|
|
8225
|
+
if (trimmed.length === 0) return false;
|
|
8226
|
+
if (trimmed === replacement) return true;
|
|
8227
|
+
if (/^\[HASH:[A-Za-z0-9_-]+\]$/.test(trimmed)) return true;
|
|
8228
|
+
if (/^\[REDACTED:[^\]]*\]$/.test(trimmed)) return true;
|
|
8229
|
+
return false;
|
|
8230
|
+
}
|
|
8209
8231
|
function applyRule(rule, value, replacement) {
|
|
8210
8232
|
if (rule.strategy === "full") return replacement;
|
|
8211
8233
|
const asString = stringifyScalar(value);
|
|
@@ -8320,6 +8342,9 @@ var Redactor2 = class {
|
|
|
8320
8342
|
if (key !== void 0) {
|
|
8321
8343
|
const rule = findCompiledKeyRule(key, this.#rules);
|
|
8322
8344
|
if (rule) {
|
|
8345
|
+
if (isCompleteRedactionMarker(value, this.#replacement)) {
|
|
8346
|
+
return value;
|
|
8347
|
+
}
|
|
8323
8348
|
this.#recordFinding(
|
|
8324
8349
|
state,
|
|
8325
8350
|
makeFinding(path16, `key.${rule.key}`, actionForRule(rule), "key", "warning")
|