@agent-inspect/mcp-server 6.7.5 → 6.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +273 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +274 -61
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -1881,8 +1881,35 @@ var DEFAULT_RAW_CONTENT_KEYS = [
|
|
|
1881
1881
|
"toolinput",
|
|
1882
1882
|
"tool_input",
|
|
1883
1883
|
"tooloutput",
|
|
1884
|
-
"tool_output"
|
|
1884
|
+
"tool_output",
|
|
1885
|
+
// Framework / agent metadata that carries user or task text
|
|
1886
|
+
"currenttask",
|
|
1887
|
+
"current_task",
|
|
1888
|
+
"task",
|
|
1889
|
+
"userinput",
|
|
1890
|
+
"user_input",
|
|
1891
|
+
"requesttext",
|
|
1892
|
+
"request_text",
|
|
1893
|
+
"conversationtext",
|
|
1894
|
+
"conversation_text"
|
|
1885
1895
|
];
|
|
1896
|
+
var DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES = ["tokenUsage", "usage"];
|
|
1897
|
+
var SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
|
|
1898
|
+
"input",
|
|
1899
|
+
"output",
|
|
1900
|
+
"total",
|
|
1901
|
+
"cached",
|
|
1902
|
+
"input_tokens",
|
|
1903
|
+
"inputtokens",
|
|
1904
|
+
"output_tokens",
|
|
1905
|
+
"outputtokens",
|
|
1906
|
+
"total_tokens",
|
|
1907
|
+
"totaltokens",
|
|
1908
|
+
"prompt_tokens",
|
|
1909
|
+
"prompttokens",
|
|
1910
|
+
"completion_tokens",
|
|
1911
|
+
"completiontokens"
|
|
1912
|
+
]);
|
|
1886
1913
|
var DEFAULT_SECRET_PATTERNS = [
|
|
1887
1914
|
{ id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
|
|
1888
1915
|
{ id: "openai-key", pattern: /sk-[A-Za-z0-9_-]{16,}/ },
|
|
@@ -2060,7 +2087,11 @@ function normalizeFinding(rule, finding) {
|
|
|
2060
2087
|
message: finding.message,
|
|
2061
2088
|
...finding.expected !== void 0 ? { expected: finding.expected } : {},
|
|
2062
2089
|
...finding.actual !== void 0 ? { actual: finding.actual } : {},
|
|
2063
|
-
evidence: [...finding.evidence ?? []]
|
|
2090
|
+
evidence: [...finding.evidence ?? []],
|
|
2091
|
+
...finding.category !== void 0 ? { category: finding.category } : {},
|
|
2092
|
+
...finding.confidence !== void 0 ? { confidence: finding.confidence } : {},
|
|
2093
|
+
...finding.detector !== void 0 ? { detector: finding.detector } : {},
|
|
2094
|
+
...finding.action !== void 0 ? { action: finding.action } : {}
|
|
2064
2095
|
};
|
|
2065
2096
|
}
|
|
2066
2097
|
function summarize(findings, diagnostics) {
|
|
@@ -2091,15 +2122,19 @@ function eventEvidence(event, path14) {
|
|
|
2091
2122
|
function runEvidence(run) {
|
|
2092
2123
|
return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
|
|
2093
2124
|
}
|
|
2094
|
-
function failFinding(ruleId, message, evidence, expected, actual) {
|
|
2125
|
+
function failFinding(ruleId, message, evidence, expected, actual, meta) {
|
|
2095
2126
|
return {
|
|
2096
2127
|
ruleId,
|
|
2097
|
-
severity: "error",
|
|
2098
|
-
status: "fail",
|
|
2128
|
+
severity: meta?.severity ?? "error",
|
|
2129
|
+
status: meta?.status ?? "fail",
|
|
2099
2130
|
message,
|
|
2100
2131
|
...expected !== void 0 ? { expected } : {},
|
|
2101
2132
|
...actual !== void 0 ? { actual } : {},
|
|
2102
|
-
evidence: [...evidence]
|
|
2133
|
+
evidence: [...evidence],
|
|
2134
|
+
...meta?.category !== void 0 ? { category: meta.category } : {},
|
|
2135
|
+
...meta?.confidence !== void 0 ? { confidence: meta.confidence } : {},
|
|
2136
|
+
...meta?.detector !== void 0 ? { detector: meta.detector } : {},
|
|
2137
|
+
...meta?.action !== void 0 ? { action: meta.action } : {}
|
|
2103
2138
|
};
|
|
2104
2139
|
}
|
|
2105
2140
|
function isRecord6(value) {
|
|
@@ -2180,6 +2215,19 @@ function isRawContentKey(key, forbiddenKeys) {
|
|
|
2180
2215
|
const normalized = normalizedKey(key);
|
|
2181
2216
|
return forbiddenKeys.some((forbidden) => normalized === normalizedKey(forbidden));
|
|
2182
2217
|
}
|
|
2218
|
+
function isSafeRawContentMetricPath(path14, key, safePathPrefixes) {
|
|
2219
|
+
const leaf = normalizedKey(key ?? lastPathSegment(path14));
|
|
2220
|
+
if (!SAFE_USAGE_LEAF_KEYS.has(leaf)) return false;
|
|
2221
|
+
const parts = path14.split(".").filter(Boolean);
|
|
2222
|
+
if (parts.length < 2) return false;
|
|
2223
|
+
const parent = parts[parts.length - 2] ?? "";
|
|
2224
|
+
const parentNorm = normalizedKey(parent);
|
|
2225
|
+
return safePathPrefixes.some((prefix) => parentNorm === normalizedKey(prefix));
|
|
2226
|
+
}
|
|
2227
|
+
function isRawContentPath(path14, key, forbiddenKeys, safePathPrefixes) {
|
|
2228
|
+
if (isSafeRawContentMetricPath(path14, key, safePathPrefixes)) return false;
|
|
2229
|
+
return isRawContentKey(key ?? lastPathSegment(path14), forbiddenKeys);
|
|
2230
|
+
}
|
|
2183
2231
|
function createRunStatusRule(options = {}) {
|
|
2184
2232
|
const expected = options.expected ?? "ok";
|
|
2185
2233
|
const allowIncomplete = options.allowIncomplete === true;
|
|
@@ -2238,7 +2286,13 @@ function createSafetyRedactionRule(options = {}) {
|
|
|
2238
2286
|
`Sensitive-looking field at ${entry.path} is not redacted.`,
|
|
2239
2287
|
[eventEvidence(event, entry.path)],
|
|
2240
2288
|
"redaction marker",
|
|
2241
|
-
{ path: entry.path, valueType: valueType(entry.value) }
|
|
2289
|
+
{ path: entry.path, valueType: valueType(entry.value) },
|
|
2290
|
+
{
|
|
2291
|
+
category: "credential",
|
|
2292
|
+
confidence: "high",
|
|
2293
|
+
detector: "safety.redaction",
|
|
2294
|
+
action: "redact"
|
|
2295
|
+
}
|
|
2242
2296
|
)
|
|
2243
2297
|
);
|
|
2244
2298
|
}
|
|
@@ -2249,6 +2303,7 @@ function createSafetyRedactionRule(options = {}) {
|
|
|
2249
2303
|
}
|
|
2250
2304
|
function createSafetyRawContentRule(options = {}) {
|
|
2251
2305
|
const forbiddenKeys = options.forbiddenKeys ?? DEFAULT_RAW_CONTENT_KEYS;
|
|
2306
|
+
const safePathPrefixes = options.safePathPrefixes ?? DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES;
|
|
2252
2307
|
return {
|
|
2253
2308
|
id: "safety.rawPrompt",
|
|
2254
2309
|
category: "safety",
|
|
@@ -2258,14 +2313,20 @@ function createSafetyRawContentRule(options = {}) {
|
|
|
2258
2313
|
for (const event of context.events) {
|
|
2259
2314
|
for (const entry of eventValueEntries(event, { includeSummaries: options.includeSummaries })) {
|
|
2260
2315
|
const key = entry.key ?? lastPathSegment(entry.path);
|
|
2261
|
-
if (!
|
|
2316
|
+
if (!isRawContentPath(entry.path, key, forbiddenKeys, safePathPrefixes)) continue;
|
|
2262
2317
|
findings.push(
|
|
2263
2318
|
failFinding(
|
|
2264
2319
|
"safety.rawPrompt",
|
|
2265
2320
|
`Raw content-like field ${entry.path} is present.`,
|
|
2266
2321
|
[eventEvidence(event, entry.path)],
|
|
2267
2322
|
"metadata-only trace fields",
|
|
2268
|
-
{ path: entry.path, valueType: valueType(entry.value) }
|
|
2323
|
+
{ path: entry.path, valueType: valueType(entry.value) },
|
|
2324
|
+
{
|
|
2325
|
+
category: "raw-content",
|
|
2326
|
+
confidence: "high",
|
|
2327
|
+
detector: "safety.rawPrompt",
|
|
2328
|
+
action: "redact-or-omit"
|
|
2329
|
+
}
|
|
2269
2330
|
)
|
|
2270
2331
|
);
|
|
2271
2332
|
}
|
|
@@ -2297,7 +2358,13 @@ function createSafetySecretPatternRule(options = {}) {
|
|
|
2297
2358
|
`Secret-like pattern ${pattern.id} matched at ${entry.path}.`,
|
|
2298
2359
|
[eventEvidence(event, entry.path)],
|
|
2299
2360
|
"no secret-like strings",
|
|
2300
|
-
{ pattern: pattern.id, path: entry.path }
|
|
2361
|
+
{ pattern: pattern.id, path: entry.path },
|
|
2362
|
+
{
|
|
2363
|
+
category: "credential",
|
|
2364
|
+
confidence: "high",
|
|
2365
|
+
detector: pattern.id,
|
|
2366
|
+
action: "redact"
|
|
2367
|
+
}
|
|
2301
2368
|
)
|
|
2302
2369
|
);
|
|
2303
2370
|
break;
|
|
@@ -2324,7 +2391,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
2324
2391
|
`String at ${entry.path} exceeds ${options.maxStringLength} characters.`,
|
|
2325
2392
|
[eventEvidence(event, entry.path)],
|
|
2326
2393
|
{ maxStringLength: options.maxStringLength },
|
|
2327
|
-
{ path: entry.path, length: entry.value.length }
|
|
2394
|
+
{ path: entry.path, length: entry.value.length },
|
|
2395
|
+
{
|
|
2396
|
+
category: "size",
|
|
2397
|
+
confidence: "high",
|
|
2398
|
+
detector: "safety.oversizedAttribute",
|
|
2399
|
+
action: "truncate-or-omit"
|
|
2400
|
+
}
|
|
2328
2401
|
)
|
|
2329
2402
|
);
|
|
2330
2403
|
}
|
|
@@ -2335,7 +2408,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
2335
2408
|
`Array at ${entry.path} exceeds ${options.maxArrayLength} items.`,
|
|
2336
2409
|
[eventEvidence(event, entry.path)],
|
|
2337
2410
|
{ maxArrayLength: options.maxArrayLength },
|
|
2338
|
-
{ path: entry.path, length: entry.value.length }
|
|
2411
|
+
{ path: entry.path, length: entry.value.length },
|
|
2412
|
+
{
|
|
2413
|
+
category: "size",
|
|
2414
|
+
confidence: "high",
|
|
2415
|
+
detector: "safety.oversizedAttribute",
|
|
2416
|
+
action: "truncate-or-omit"
|
|
2417
|
+
}
|
|
2339
2418
|
)
|
|
2340
2419
|
);
|
|
2341
2420
|
}
|
|
@@ -2346,7 +2425,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
2346
2425
|
`Object at ${entry.path} exceeds ${options.maxObjectKeys} keys.`,
|
|
2347
2426
|
[eventEvidence(event, entry.path)],
|
|
2348
2427
|
{ maxObjectKeys: options.maxObjectKeys },
|
|
2349
|
-
{ path: entry.path, keys: Object.keys(entry.value).length }
|
|
2428
|
+
{ path: entry.path, keys: Object.keys(entry.value).length },
|
|
2429
|
+
{
|
|
2430
|
+
category: "size",
|
|
2431
|
+
confidence: "high",
|
|
2432
|
+
detector: "safety.oversizedAttribute",
|
|
2433
|
+
action: "truncate-or-omit"
|
|
2434
|
+
}
|
|
2350
2435
|
)
|
|
2351
2436
|
);
|
|
2352
2437
|
}
|
|
@@ -2359,7 +2444,13 @@ function createSafetyOversizedAttributeRule(options) {
|
|
|
2359
2444
|
`Value at ${entry.path} exceeds ${options.maxSerializedBytes} serialized bytes.`,
|
|
2360
2445
|
[eventEvidence(event, entry.path)],
|
|
2361
2446
|
{ maxSerializedBytes: options.maxSerializedBytes },
|
|
2362
|
-
{ path: entry.path, bytes }
|
|
2447
|
+
{ path: entry.path, bytes },
|
|
2448
|
+
{
|
|
2449
|
+
category: "size",
|
|
2450
|
+
confidence: "high",
|
|
2451
|
+
detector: "safety.oversizedAttribute",
|
|
2452
|
+
action: "truncate-or-omit"
|
|
2453
|
+
}
|
|
2363
2454
|
)
|
|
2364
2455
|
);
|
|
2365
2456
|
}
|
|
@@ -5633,43 +5724,6 @@ function exportRunTree(tree, options) {
|
|
|
5633
5724
|
}
|
|
5634
5725
|
}
|
|
5635
5726
|
}
|
|
5636
|
-
|
|
5637
|
-
// packages/mcp-server/src/assess-trace.ts
|
|
5638
|
-
var DEFAULT_MAX_STRING_LENGTH = 16384;
|
|
5639
|
-
var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
|
|
5640
|
-
var DEFAULT_MAX_OBJECT_KEYS = 200;
|
|
5641
|
-
var DEFAULT_MAX_SERIALIZED_BYTES = 128 * 1024;
|
|
5642
|
-
function buildMcpSafetyRules() {
|
|
5643
|
-
return [
|
|
5644
|
-
createSafetyRawContentRule(),
|
|
5645
|
-
createSafetyRedactionRule(),
|
|
5646
|
-
createSafetySecretPatternRule(),
|
|
5647
|
-
createSafetyOversizedAttributeRule({
|
|
5648
|
-
maxStringLength: DEFAULT_MAX_STRING_LENGTH,
|
|
5649
|
-
maxArrayLength: DEFAULT_MAX_ARRAY_LENGTH,
|
|
5650
|
-
maxObjectKeys: DEFAULT_MAX_OBJECT_KEYS,
|
|
5651
|
-
maxSerializedBytes: DEFAULT_MAX_SERIALIZED_BYTES
|
|
5652
|
-
})
|
|
5653
|
-
];
|
|
5654
|
-
}
|
|
5655
|
-
function statusFrom(findings, hasErrors) {
|
|
5656
|
-
if (hasErrors) return "UNKNOWN";
|
|
5657
|
-
if (findings.some((item) => item.severity === "error")) return "UNSAFE";
|
|
5658
|
-
if (findings.some((item) => item.severity === "warning")) return "SAFE WITH WARNINGS";
|
|
5659
|
-
return "SAFE";
|
|
5660
|
-
}
|
|
5661
|
-
function assessTraceForMcp(read, runId) {
|
|
5662
|
-
const rules = buildMcpSafetyRules();
|
|
5663
|
-
const checkResult = runTraceChecks({ read }, { rules, runId });
|
|
5664
|
-
const hasErrors = checkResult.diagnostics.some((item) => item.severity === "error");
|
|
5665
|
-
const status = statusFrom(checkResult.findings, hasErrors);
|
|
5666
|
-
return {
|
|
5667
|
-
status,
|
|
5668
|
-
errors: checkResult.diagnostics.filter((item) => item.severity === "error").length + checkResult.findings.filter((item) => item.severity === "error").length,
|
|
5669
|
-
warnings: checkResult.diagnostics.filter((item) => item.severity === "warning").length + checkResult.findings.filter((item) => item.severity === "warning").length,
|
|
5670
|
-
findings: checkResult.findings.length
|
|
5671
|
-
};
|
|
5672
|
-
}
|
|
5673
5727
|
var DEFAULT_REDACT_KEYS2 = [
|
|
5674
5728
|
"authorization",
|
|
5675
5729
|
"cookie",
|
|
@@ -5700,7 +5754,12 @@ var SHARE_PROFILE_EXTRA_KEYS2 = [
|
|
|
5700
5754
|
"organizationId",
|
|
5701
5755
|
"traceId",
|
|
5702
5756
|
"spanId",
|
|
5703
|
-
"parentSpanId"
|
|
5757
|
+
"parentSpanId",
|
|
5758
|
+
"currentTask",
|
|
5759
|
+
"task",
|
|
5760
|
+
"userInput",
|
|
5761
|
+
"requestText",
|
|
5762
|
+
"conversationText"
|
|
5704
5763
|
];
|
|
5705
5764
|
var STRICT_PROFILE_EXTRA_KEYS2 = [
|
|
5706
5765
|
"prompt",
|
|
@@ -5768,6 +5827,61 @@ function passesLuhn(value) {
|
|
|
5768
5827
|
}
|
|
5769
5828
|
return sum % 10 === 0;
|
|
5770
5829
|
}
|
|
5830
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
5831
|
+
var EPOCH_MS_RE = /^1[0-9]{12}$/;
|
|
5832
|
+
var EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
|
|
5833
|
+
function pathSuggestsNonCard(path14) {
|
|
5834
|
+
const normalized = path14.toLowerCase().replace(/[^a-z0-9._]/g, "");
|
|
5835
|
+
return /(^|\.)(tokenusage|usage)(\.|$)/.test(normalized) || /(startedat|endedat|durationms|timestamp|createdat|updatedat)(\.|$)/.test(normalized) || /(^|\.)(runid|traceid|spanid|eventid|sessionid|userid|parentid|requestid|correlationid)(\.|$)/.test(
|
|
5836
|
+
normalized
|
|
5837
|
+
) || /(^|\.)ts(\.|$)/.test(normalized);
|
|
5838
|
+
}
|
|
5839
|
+
function isUuidLike(value) {
|
|
5840
|
+
return UUID_RE.test(value.trim());
|
|
5841
|
+
}
|
|
5842
|
+
function isPlausibleCardCandidate(candidate, fullValue) {
|
|
5843
|
+
const digits = digitsOnly(candidate);
|
|
5844
|
+
if (digits.length < 13 || digits.length > 19) return false;
|
|
5845
|
+
if (!passesLuhn(candidate)) return false;
|
|
5846
|
+
const trimmed = fullValue.trim();
|
|
5847
|
+
if (isUuidLike(trimmed)) return false;
|
|
5848
|
+
if (EPOCH_MS_RE.test(digits) && digitsOnly(trimmed) === digits && /^\d+$/.test(trimmed)) {
|
|
5849
|
+
return false;
|
|
5850
|
+
}
|
|
5851
|
+
const start = fullValue.indexOf(candidate);
|
|
5852
|
+
if (start >= 0) {
|
|
5853
|
+
const before = fullValue[start - 1];
|
|
5854
|
+
const after = fullValue[start + candidate.length];
|
|
5855
|
+
if (before && /[A-Za-z0-9_]/.test(before)) return false;
|
|
5856
|
+
if (after && /[A-Za-z0-9_]/.test(after)) return false;
|
|
5857
|
+
}
|
|
5858
|
+
return true;
|
|
5859
|
+
}
|
|
5860
|
+
function looksLikePathOrPackageOrUrl(value) {
|
|
5861
|
+
const trimmed = value.trim();
|
|
5862
|
+
if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(trimmed)) return true;
|
|
5863
|
+
if (/^webpack:/i.test(trimmed)) return true;
|
|
5864
|
+
if (/^@[A-Za-z0-9_.-]+\/[A-Za-z0-9_.@/-]+$/.test(trimmed)) return true;
|
|
5865
|
+
if (/^[A-Za-z]:[\\/]/.test(trimmed) || /^[/\\]/.test(trimmed)) return true;
|
|
5866
|
+
if (/[\\/]/.test(trimmed) && !/\s/.test(trimmed)) return true;
|
|
5867
|
+
return false;
|
|
5868
|
+
}
|
|
5869
|
+
function emailMatchIsPathAdjacent(value, index, length) {
|
|
5870
|
+
const before = value[index - 1];
|
|
5871
|
+
const after = value[index + length];
|
|
5872
|
+
return before === "/" || before === "\\" || after === "/" || after === "\\";
|
|
5873
|
+
}
|
|
5874
|
+
function valueContainsEmail(value) {
|
|
5875
|
+
if (looksLikePathOrPackageOrUrl(value)) return false;
|
|
5876
|
+
EMAIL_RE.lastIndex = 0;
|
|
5877
|
+
for (const match of value.matchAll(EMAIL_RE)) {
|
|
5878
|
+
const text = match[0] ?? "";
|
|
5879
|
+
const index = match.index ?? 0;
|
|
5880
|
+
if (emailMatchIsPathAdjacent(value, index, text.length)) continue;
|
|
5881
|
+
return true;
|
|
5882
|
+
}
|
|
5883
|
+
return false;
|
|
5884
|
+
}
|
|
5771
5885
|
var credentialDetectors = [
|
|
5772
5886
|
patternDetector({
|
|
5773
5887
|
id: "value.authorizationHeader",
|
|
@@ -5815,10 +5929,12 @@ var credentialDetectors = [
|
|
|
5815
5929
|
matchKind: "value",
|
|
5816
5930
|
detect(input) {
|
|
5817
5931
|
if (typeof input.value !== "string") return [];
|
|
5932
|
+
if (pathSuggestsNonCard(input.path)) return [];
|
|
5933
|
+
if (isUuidLike(input.value)) return [];
|
|
5818
5934
|
const candidatePattern = /(?:\d[ -]?){13,19}/g;
|
|
5819
5935
|
for (const match of input.value.matchAll(candidatePattern)) {
|
|
5820
5936
|
const candidate = match[0] ?? "";
|
|
5821
|
-
if (
|
|
5937
|
+
if (isPlausibleCardCandidate(candidate, input.value)) {
|
|
5822
5938
|
return [{ action: "replace", severity: "error", matchKind: "value" }];
|
|
5823
5939
|
}
|
|
5824
5940
|
}
|
|
@@ -5827,10 +5943,15 @@ var credentialDetectors = [
|
|
|
5827
5943
|
}
|
|
5828
5944
|
];
|
|
5829
5945
|
var identifierDetectors = [
|
|
5830
|
-
|
|
5946
|
+
{
|
|
5831
5947
|
id: "value.email",
|
|
5832
|
-
|
|
5833
|
-
|
|
5948
|
+
severity: "warning",
|
|
5949
|
+
matchKind: "value",
|
|
5950
|
+
detect(input) {
|
|
5951
|
+
if (typeof input.value !== "string") return [];
|
|
5952
|
+
return valueContainsEmail(input.value) ? [{ action: "replace", severity: "warning", matchKind: "value" }] : [];
|
|
5953
|
+
}
|
|
5954
|
+
},
|
|
5834
5955
|
patternDetector({
|
|
5835
5956
|
id: "value.phone",
|
|
5836
5957
|
pattern: /\b(?:\+\d{1,3}[\s.-]?)?(?:\(?\d{3}\)?[\s.-])\d{3}[\s.-]\d{4}\b/
|
|
@@ -6048,6 +6169,92 @@ function redact(value, options) {
|
|
|
6048
6169
|
return createRedactor(options).redact(value);
|
|
6049
6170
|
}
|
|
6050
6171
|
|
|
6172
|
+
// packages/mcp-server/src/assess-trace.ts
|
|
6173
|
+
var DEFAULT_MAX_STRING_LENGTH = 16384;
|
|
6174
|
+
var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
|
|
6175
|
+
var DEFAULT_MAX_OBJECT_KEYS = 200;
|
|
6176
|
+
var DEFAULT_MAX_SERIALIZED_BYTES = 128 * 1024;
|
|
6177
|
+
function buildMcpSafetyRules() {
|
|
6178
|
+
return [
|
|
6179
|
+
createSafetyRawContentRule(),
|
|
6180
|
+
createSafetyRedactionRule(),
|
|
6181
|
+
createSafetySecretPatternRule(),
|
|
6182
|
+
createSafetyOversizedAttributeRule({
|
|
6183
|
+
maxStringLength: DEFAULT_MAX_STRING_LENGTH,
|
|
6184
|
+
maxArrayLength: DEFAULT_MAX_ARRAY_LENGTH,
|
|
6185
|
+
maxObjectKeys: DEFAULT_MAX_OBJECT_KEYS,
|
|
6186
|
+
maxSerializedBytes: DEFAULT_MAX_SERIALIZED_BYTES
|
|
6187
|
+
})
|
|
6188
|
+
];
|
|
6189
|
+
}
|
|
6190
|
+
function statusFrom(findings, hasErrors) {
|
|
6191
|
+
if (hasErrors) return "UNKNOWN";
|
|
6192
|
+
if (findings.some((item) => item.severity === "error")) return "UNSAFE";
|
|
6193
|
+
if (findings.some((item) => item.severity === "warning")) return "SAFE WITH WARNINGS";
|
|
6194
|
+
return "SAFE";
|
|
6195
|
+
}
|
|
6196
|
+
function redactJsonl(content, profile) {
|
|
6197
|
+
const trimmed = content.trim();
|
|
6198
|
+
if (trimmed.startsWith("{")) {
|
|
6199
|
+
try {
|
|
6200
|
+
const parsed = JSON.parse(trimmed);
|
|
6201
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && ("schemaVersion" in parsed || "eventId" in parsed || "runId" in parsed)) {
|
|
6202
|
+
} else {
|
|
6203
|
+
const result = redact(parsed, { profile });
|
|
6204
|
+
return `${JSON.stringify(result.value)}
|
|
6205
|
+
`;
|
|
6206
|
+
}
|
|
6207
|
+
} catch {
|
|
6208
|
+
}
|
|
6209
|
+
}
|
|
6210
|
+
const lines = content.split(/\r?\n/);
|
|
6211
|
+
const out = [];
|
|
6212
|
+
for (const line of lines) {
|
|
6213
|
+
if (line.trim() === "") continue;
|
|
6214
|
+
const parsed = JSON.parse(line);
|
|
6215
|
+
const result = redact(parsed, { profile });
|
|
6216
|
+
out.push(JSON.stringify(result.value));
|
|
6217
|
+
}
|
|
6218
|
+
return out.length === 0 ? "" : `${out.join("\n")}
|
|
6219
|
+
`;
|
|
6220
|
+
}
|
|
6221
|
+
function assessTraceForMcp(read, runId) {
|
|
6222
|
+
const rules = buildMcpSafetyRules();
|
|
6223
|
+
const checkResult = runTraceChecks({ read }, { rules, runId });
|
|
6224
|
+
const hasErrors = checkResult.diagnostics.some((item) => item.severity === "error");
|
|
6225
|
+
const status = statusFrom(checkResult.findings, hasErrors);
|
|
6226
|
+
return {
|
|
6227
|
+
status,
|
|
6228
|
+
errors: checkResult.diagnostics.filter((item) => item.severity === "error").length + checkResult.findings.filter((item) => item.severity === "error").length,
|
|
6229
|
+
warnings: checkResult.diagnostics.filter((item) => item.severity === "warning").length + checkResult.findings.filter((item) => item.severity === "warning").length,
|
|
6230
|
+
findings: checkResult.findings.length
|
|
6231
|
+
};
|
|
6232
|
+
}
|
|
6233
|
+
async function assessTraceArtifactForMcp(options) {
|
|
6234
|
+
const source = assessTraceForMcp(options.read, options.runId);
|
|
6235
|
+
try {
|
|
6236
|
+
const raw = await promises.readFile(options.filePath, "utf-8");
|
|
6237
|
+
const redacted = redactJsonl(raw, options.profile);
|
|
6238
|
+
const artifactRead = await openTrace(
|
|
6239
|
+
{ type: "string", content: redacted },
|
|
6240
|
+
{ format: "agent-inspect-jsonl" }
|
|
6241
|
+
);
|
|
6242
|
+
const artifact = assessTraceForMcp(artifactRead, options.runId);
|
|
6243
|
+
return {
|
|
6244
|
+
...artifact,
|
|
6245
|
+
sourceStatus: source.status
|
|
6246
|
+
};
|
|
6247
|
+
} catch {
|
|
6248
|
+
return {
|
|
6249
|
+
status: "UNKNOWN",
|
|
6250
|
+
errors: Math.max(1, source.errors),
|
|
6251
|
+
warnings: source.warnings,
|
|
6252
|
+
findings: source.findings,
|
|
6253
|
+
sourceStatus: source.status
|
|
6254
|
+
};
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
|
|
6051
6258
|
// packages/mcp-server/src/prepare-result.ts
|
|
6052
6259
|
var DEFAULT_MCP_RESULT_MAX_BYTES = 512 * 1024;
|
|
6053
6260
|
function stableStringify(value) {
|
|
@@ -6406,16 +6613,21 @@ async function callReadOnlyTool(context, name, args = {}) {
|
|
|
6406
6613
|
}
|
|
6407
6614
|
case "create_share_safe_bundle": {
|
|
6408
6615
|
const runId = String(args.runId ?? "");
|
|
6409
|
-
const { read } = await openRunTrace(context, runId);
|
|
6616
|
+
const { meta, read } = await openRunTrace(context, runId);
|
|
6410
6617
|
const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
|
|
6411
6618
|
if (!run) return errorResult2(`Run tree not found: ${runId}`);
|
|
6412
|
-
const
|
|
6619
|
+
const profile = redactionProfileForExport(context);
|
|
6620
|
+
const safety = await assessTraceArtifactForMcp({
|
|
6621
|
+
read,
|
|
6622
|
+
runId,
|
|
6623
|
+
filePath: meta.filePath,
|
|
6624
|
+
profile
|
|
6625
|
+
});
|
|
6413
6626
|
if (bundleFailsOnSafety(safety.status)) {
|
|
6414
6627
|
return errorResult2(
|
|
6415
|
-
`Share-safe bundle refused: safety status is ${safety.status}. Resolve findings before export.`
|
|
6628
|
+
`Share-safe bundle refused: artifact safety status is ${safety.status}. Resolve findings before export.`
|
|
6416
6629
|
);
|
|
6417
6630
|
}
|
|
6418
|
-
const profile = redactionProfileForExport(context);
|
|
6419
6631
|
const markdown = exportMarkdown(run, {
|
|
6420
6632
|
redacted: true});
|
|
6421
6633
|
const tree = exportRunTree(run, {
|
|
@@ -6433,6 +6645,7 @@ async function callReadOnlyTool(context, name, args = {}) {
|
|
|
6433
6645
|
{
|
|
6434
6646
|
runId,
|
|
6435
6647
|
status: safety.status,
|
|
6648
|
+
...safety.sourceStatus !== void 0 ? { sourceStatus: safety.sourceStatus } : {},
|
|
6436
6649
|
errors: safety.errors,
|
|
6437
6650
|
warnings: safety.warnings,
|
|
6438
6651
|
findings: safety.findings
|