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