@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.mjs CHANGED
@@ -4,7 +4,7 @@ import path from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { AsyncLocalStorage } from 'async_hooks';
6
6
  import crypto from 'crypto';
7
- import { readdir, stat, readFile } from 'fs/promises';
7
+ import { readFile, readdir, stat } from 'fs/promises';
8
8
  import os from 'os';
9
9
  import 'nanoid';
10
10
  import 'chalk';
@@ -1871,8 +1871,35 @@ var DEFAULT_RAW_CONTENT_KEYS = [
1871
1871
  "toolinput",
1872
1872
  "tool_input",
1873
1873
  "tooloutput",
1874
- "tool_output"
1874
+ "tool_output",
1875
+ // Framework / agent metadata that carries user or task text
1876
+ "currenttask",
1877
+ "current_task",
1878
+ "task",
1879
+ "userinput",
1880
+ "user_input",
1881
+ "requesttext",
1882
+ "request_text",
1883
+ "conversationtext",
1884
+ "conversation_text"
1875
1885
  ];
1886
+ var DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES = ["tokenUsage", "usage"];
1887
+ var SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
1888
+ "input",
1889
+ "output",
1890
+ "total",
1891
+ "cached",
1892
+ "input_tokens",
1893
+ "inputtokens",
1894
+ "output_tokens",
1895
+ "outputtokens",
1896
+ "total_tokens",
1897
+ "totaltokens",
1898
+ "prompt_tokens",
1899
+ "prompttokens",
1900
+ "completion_tokens",
1901
+ "completiontokens"
1902
+ ]);
1876
1903
  var DEFAULT_SECRET_PATTERNS = [
1877
1904
  { id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
1878
1905
  { id: "openai-key", pattern: /sk-[A-Za-z0-9_-]{16,}/ },
@@ -2050,7 +2077,11 @@ function normalizeFinding(rule, finding) {
2050
2077
  message: finding.message,
2051
2078
  ...finding.expected !== void 0 ? { expected: finding.expected } : {},
2052
2079
  ...finding.actual !== void 0 ? { actual: finding.actual } : {},
2053
- evidence: [...finding.evidence ?? []]
2080
+ evidence: [...finding.evidence ?? []],
2081
+ ...finding.category !== void 0 ? { category: finding.category } : {},
2082
+ ...finding.confidence !== void 0 ? { confidence: finding.confidence } : {},
2083
+ ...finding.detector !== void 0 ? { detector: finding.detector } : {},
2084
+ ...finding.action !== void 0 ? { action: finding.action } : {}
2054
2085
  };
2055
2086
  }
2056
2087
  function summarize(findings, diagnostics) {
@@ -2081,15 +2112,19 @@ function eventEvidence(event, path14) {
2081
2112
  function runEvidence(run) {
2082
2113
  return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
2083
2114
  }
2084
- function failFinding(ruleId, message, evidence, expected, actual) {
2115
+ function failFinding(ruleId, message, evidence, expected, actual, meta) {
2085
2116
  return {
2086
2117
  ruleId,
2087
- severity: "error",
2088
- status: "fail",
2118
+ severity: meta?.severity ?? "error",
2119
+ status: meta?.status ?? "fail",
2089
2120
  message,
2090
2121
  ...expected !== void 0 ? { expected } : {},
2091
2122
  ...actual !== void 0 ? { actual } : {},
2092
- evidence: [...evidence]
2123
+ evidence: [...evidence],
2124
+ ...meta?.category !== void 0 ? { category: meta.category } : {},
2125
+ ...meta?.confidence !== void 0 ? { confidence: meta.confidence } : {},
2126
+ ...meta?.detector !== void 0 ? { detector: meta.detector } : {},
2127
+ ...meta?.action !== void 0 ? { action: meta.action } : {}
2093
2128
  };
2094
2129
  }
2095
2130
  function isRecord6(value) {
@@ -2170,6 +2205,19 @@ function isRawContentKey(key, forbiddenKeys) {
2170
2205
  const normalized = normalizedKey(key);
2171
2206
  return forbiddenKeys.some((forbidden) => normalized === normalizedKey(forbidden));
2172
2207
  }
2208
+ function isSafeRawContentMetricPath(path14, key, safePathPrefixes) {
2209
+ const leaf = normalizedKey(key ?? lastPathSegment(path14));
2210
+ if (!SAFE_USAGE_LEAF_KEYS.has(leaf)) return false;
2211
+ const parts = path14.split(".").filter(Boolean);
2212
+ if (parts.length < 2) return false;
2213
+ const parent = parts[parts.length - 2] ?? "";
2214
+ const parentNorm = normalizedKey(parent);
2215
+ return safePathPrefixes.some((prefix) => parentNorm === normalizedKey(prefix));
2216
+ }
2217
+ function isRawContentPath(path14, key, forbiddenKeys, safePathPrefixes) {
2218
+ if (isSafeRawContentMetricPath(path14, key, safePathPrefixes)) return false;
2219
+ return isRawContentKey(key ?? lastPathSegment(path14), forbiddenKeys);
2220
+ }
2173
2221
  function createRunStatusRule(options = {}) {
2174
2222
  const expected = options.expected ?? "ok";
2175
2223
  const allowIncomplete = options.allowIncomplete === true;
@@ -2228,7 +2276,13 @@ function createSafetyRedactionRule(options = {}) {
2228
2276
  `Sensitive-looking field at ${entry.path} is not redacted.`,
2229
2277
  [eventEvidence(event, entry.path)],
2230
2278
  "redaction marker",
2231
- { path: entry.path, valueType: valueType(entry.value) }
2279
+ { path: entry.path, valueType: valueType(entry.value) },
2280
+ {
2281
+ category: "credential",
2282
+ confidence: "high",
2283
+ detector: "safety.redaction",
2284
+ action: "redact"
2285
+ }
2232
2286
  )
2233
2287
  );
2234
2288
  }
@@ -2239,6 +2293,7 @@ function createSafetyRedactionRule(options = {}) {
2239
2293
  }
2240
2294
  function createSafetyRawContentRule(options = {}) {
2241
2295
  const forbiddenKeys = options.forbiddenKeys ?? DEFAULT_RAW_CONTENT_KEYS;
2296
+ const safePathPrefixes = options.safePathPrefixes ?? DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES;
2242
2297
  return {
2243
2298
  id: "safety.rawPrompt",
2244
2299
  category: "safety",
@@ -2248,14 +2303,20 @@ function createSafetyRawContentRule(options = {}) {
2248
2303
  for (const event of context.events) {
2249
2304
  for (const entry of eventValueEntries(event, { includeSummaries: options.includeSummaries })) {
2250
2305
  const key = entry.key ?? lastPathSegment(entry.path);
2251
- if (!isRawContentKey(key, forbiddenKeys)) continue;
2306
+ if (!isRawContentPath(entry.path, key, forbiddenKeys, safePathPrefixes)) continue;
2252
2307
  findings.push(
2253
2308
  failFinding(
2254
2309
  "safety.rawPrompt",
2255
2310
  `Raw content-like field ${entry.path} is present.`,
2256
2311
  [eventEvidence(event, entry.path)],
2257
2312
  "metadata-only trace fields",
2258
- { path: entry.path, valueType: valueType(entry.value) }
2313
+ { path: entry.path, valueType: valueType(entry.value) },
2314
+ {
2315
+ category: "raw-content",
2316
+ confidence: "high",
2317
+ detector: "safety.rawPrompt",
2318
+ action: "redact-or-omit"
2319
+ }
2259
2320
  )
2260
2321
  );
2261
2322
  }
@@ -2287,7 +2348,13 @@ function createSafetySecretPatternRule(options = {}) {
2287
2348
  `Secret-like pattern ${pattern.id} matched at ${entry.path}.`,
2288
2349
  [eventEvidence(event, entry.path)],
2289
2350
  "no secret-like strings",
2290
- { pattern: pattern.id, path: entry.path }
2351
+ { pattern: pattern.id, path: entry.path },
2352
+ {
2353
+ category: "credential",
2354
+ confidence: "high",
2355
+ detector: pattern.id,
2356
+ action: "redact"
2357
+ }
2291
2358
  )
2292
2359
  );
2293
2360
  break;
@@ -2314,7 +2381,13 @@ function createSafetyOversizedAttributeRule(options) {
2314
2381
  `String at ${entry.path} exceeds ${options.maxStringLength} characters.`,
2315
2382
  [eventEvidence(event, entry.path)],
2316
2383
  { maxStringLength: options.maxStringLength },
2317
- { path: entry.path, length: entry.value.length }
2384
+ { path: entry.path, length: entry.value.length },
2385
+ {
2386
+ category: "size",
2387
+ confidence: "high",
2388
+ detector: "safety.oversizedAttribute",
2389
+ action: "truncate-or-omit"
2390
+ }
2318
2391
  )
2319
2392
  );
2320
2393
  }
@@ -2325,7 +2398,13 @@ function createSafetyOversizedAttributeRule(options) {
2325
2398
  `Array at ${entry.path} exceeds ${options.maxArrayLength} items.`,
2326
2399
  [eventEvidence(event, entry.path)],
2327
2400
  { maxArrayLength: options.maxArrayLength },
2328
- { path: entry.path, length: entry.value.length }
2401
+ { path: entry.path, length: entry.value.length },
2402
+ {
2403
+ category: "size",
2404
+ confidence: "high",
2405
+ detector: "safety.oversizedAttribute",
2406
+ action: "truncate-or-omit"
2407
+ }
2329
2408
  )
2330
2409
  );
2331
2410
  }
@@ -2336,7 +2415,13 @@ function createSafetyOversizedAttributeRule(options) {
2336
2415
  `Object at ${entry.path} exceeds ${options.maxObjectKeys} keys.`,
2337
2416
  [eventEvidence(event, entry.path)],
2338
2417
  { maxObjectKeys: options.maxObjectKeys },
2339
- { path: entry.path, keys: Object.keys(entry.value).length }
2418
+ { path: entry.path, keys: Object.keys(entry.value).length },
2419
+ {
2420
+ category: "size",
2421
+ confidence: "high",
2422
+ detector: "safety.oversizedAttribute",
2423
+ action: "truncate-or-omit"
2424
+ }
2340
2425
  )
2341
2426
  );
2342
2427
  }
@@ -2349,7 +2434,13 @@ function createSafetyOversizedAttributeRule(options) {
2349
2434
  `Value at ${entry.path} exceeds ${options.maxSerializedBytes} serialized bytes.`,
2350
2435
  [eventEvidence(event, entry.path)],
2351
2436
  { maxSerializedBytes: options.maxSerializedBytes },
2352
- { path: entry.path, bytes }
2437
+ { path: entry.path, bytes },
2438
+ {
2439
+ category: "size",
2440
+ confidence: "high",
2441
+ detector: "safety.oversizedAttribute",
2442
+ action: "truncate-or-omit"
2443
+ }
2353
2444
  )
2354
2445
  );
2355
2446
  }
@@ -5623,43 +5714,6 @@ function exportRunTree(tree, options) {
5623
5714
  }
5624
5715
  }
5625
5716
  }
5626
-
5627
- // packages/mcp-server/src/assess-trace.ts
5628
- var DEFAULT_MAX_STRING_LENGTH = 16384;
5629
- var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
5630
- var DEFAULT_MAX_OBJECT_KEYS = 200;
5631
- var DEFAULT_MAX_SERIALIZED_BYTES = 128 * 1024;
5632
- function buildMcpSafetyRules() {
5633
- return [
5634
- createSafetyRawContentRule(),
5635
- createSafetyRedactionRule(),
5636
- createSafetySecretPatternRule(),
5637
- createSafetyOversizedAttributeRule({
5638
- maxStringLength: DEFAULT_MAX_STRING_LENGTH,
5639
- maxArrayLength: DEFAULT_MAX_ARRAY_LENGTH,
5640
- maxObjectKeys: DEFAULT_MAX_OBJECT_KEYS,
5641
- maxSerializedBytes: DEFAULT_MAX_SERIALIZED_BYTES
5642
- })
5643
- ];
5644
- }
5645
- function statusFrom(findings, hasErrors) {
5646
- if (hasErrors) return "UNKNOWN";
5647
- if (findings.some((item) => item.severity === "error")) return "UNSAFE";
5648
- if (findings.some((item) => item.severity === "warning")) return "SAFE WITH WARNINGS";
5649
- return "SAFE";
5650
- }
5651
- function assessTraceForMcp(read, runId) {
5652
- const rules = buildMcpSafetyRules();
5653
- const checkResult = runTraceChecks({ read }, { rules, runId });
5654
- const hasErrors = checkResult.diagnostics.some((item) => item.severity === "error");
5655
- const status = statusFrom(checkResult.findings, hasErrors);
5656
- return {
5657
- status,
5658
- errors: checkResult.diagnostics.filter((item) => item.severity === "error").length + checkResult.findings.filter((item) => item.severity === "error").length,
5659
- warnings: checkResult.diagnostics.filter((item) => item.severity === "warning").length + checkResult.findings.filter((item) => item.severity === "warning").length,
5660
- findings: checkResult.findings.length
5661
- };
5662
- }
5663
5717
  var DEFAULT_REDACT_KEYS2 = [
5664
5718
  "authorization",
5665
5719
  "cookie",
@@ -5690,7 +5744,12 @@ var SHARE_PROFILE_EXTRA_KEYS2 = [
5690
5744
  "organizationId",
5691
5745
  "traceId",
5692
5746
  "spanId",
5693
- "parentSpanId"
5747
+ "parentSpanId",
5748
+ "currentTask",
5749
+ "task",
5750
+ "userInput",
5751
+ "requestText",
5752
+ "conversationText"
5694
5753
  ];
5695
5754
  var STRICT_PROFILE_EXTRA_KEYS2 = [
5696
5755
  "prompt",
@@ -5758,6 +5817,61 @@ function passesLuhn(value) {
5758
5817
  }
5759
5818
  return sum % 10 === 0;
5760
5819
  }
5820
+ 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;
5821
+ var EPOCH_MS_RE = /^1[0-9]{12}$/;
5822
+ var EMAIL_RE = /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi;
5823
+ function pathSuggestsNonCard(path14) {
5824
+ const normalized = path14.toLowerCase().replace(/[^a-z0-9._]/g, "");
5825
+ return /(^|\.)(tokenusage|usage)(\.|$)/.test(normalized) || /(startedat|endedat|durationms|timestamp|createdat|updatedat)(\.|$)/.test(normalized) || /(^|\.)(runid|traceid|spanid|eventid|sessionid|userid|parentid|requestid|correlationid)(\.|$)/.test(
5826
+ normalized
5827
+ ) || /(^|\.)ts(\.|$)/.test(normalized);
5828
+ }
5829
+ function isUuidLike(value) {
5830
+ return UUID_RE.test(value.trim());
5831
+ }
5832
+ function isPlausibleCardCandidate(candidate, fullValue) {
5833
+ const digits = digitsOnly(candidate);
5834
+ if (digits.length < 13 || digits.length > 19) return false;
5835
+ if (!passesLuhn(candidate)) return false;
5836
+ const trimmed = fullValue.trim();
5837
+ if (isUuidLike(trimmed)) return false;
5838
+ if (EPOCH_MS_RE.test(digits) && digitsOnly(trimmed) === digits && /^\d+$/.test(trimmed)) {
5839
+ return false;
5840
+ }
5841
+ const start = fullValue.indexOf(candidate);
5842
+ if (start >= 0) {
5843
+ const before = fullValue[start - 1];
5844
+ const after = fullValue[start + candidate.length];
5845
+ if (before && /[A-Za-z0-9_]/.test(before)) return false;
5846
+ if (after && /[A-Za-z0-9_]/.test(after)) return false;
5847
+ }
5848
+ return true;
5849
+ }
5850
+ function looksLikePathOrPackageOrUrl(value) {
5851
+ const trimmed = value.trim();
5852
+ if (/^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(trimmed)) return true;
5853
+ if (/^webpack:/i.test(trimmed)) return true;
5854
+ if (/^@[A-Za-z0-9_.-]+\/[A-Za-z0-9_.@/-]+$/.test(trimmed)) return true;
5855
+ if (/^[A-Za-z]:[\\/]/.test(trimmed) || /^[/\\]/.test(trimmed)) return true;
5856
+ if (/[\\/]/.test(trimmed) && !/\s/.test(trimmed)) return true;
5857
+ return false;
5858
+ }
5859
+ function emailMatchIsPathAdjacent(value, index, length) {
5860
+ const before = value[index - 1];
5861
+ const after = value[index + length];
5862
+ return before === "/" || before === "\\" || after === "/" || after === "\\";
5863
+ }
5864
+ function valueContainsEmail(value) {
5865
+ if (looksLikePathOrPackageOrUrl(value)) return false;
5866
+ EMAIL_RE.lastIndex = 0;
5867
+ for (const match of value.matchAll(EMAIL_RE)) {
5868
+ const text = match[0] ?? "";
5869
+ const index = match.index ?? 0;
5870
+ if (emailMatchIsPathAdjacent(value, index, text.length)) continue;
5871
+ return true;
5872
+ }
5873
+ return false;
5874
+ }
5761
5875
  var credentialDetectors = [
5762
5876
  patternDetector({
5763
5877
  id: "value.authorizationHeader",
@@ -5805,10 +5919,12 @@ var credentialDetectors = [
5805
5919
  matchKind: "value",
5806
5920
  detect(input) {
5807
5921
  if (typeof input.value !== "string") return [];
5922
+ if (pathSuggestsNonCard(input.path)) return [];
5923
+ if (isUuidLike(input.value)) return [];
5808
5924
  const candidatePattern = /(?:\d[ -]?){13,19}/g;
5809
5925
  for (const match of input.value.matchAll(candidatePattern)) {
5810
5926
  const candidate = match[0] ?? "";
5811
- if (passesLuhn(candidate)) {
5927
+ if (isPlausibleCardCandidate(candidate, input.value)) {
5812
5928
  return [{ action: "replace", severity: "error", matchKind: "value" }];
5813
5929
  }
5814
5930
  }
@@ -5817,10 +5933,15 @@ var credentialDetectors = [
5817
5933
  }
5818
5934
  ];
5819
5935
  var identifierDetectors = [
5820
- patternDetector({
5936
+ {
5821
5937
  id: "value.email",
5822
- pattern: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i
5823
- }),
5938
+ severity: "warning",
5939
+ matchKind: "value",
5940
+ detect(input) {
5941
+ if (typeof input.value !== "string") return [];
5942
+ return valueContainsEmail(input.value) ? [{ action: "replace", severity: "warning", matchKind: "value" }] : [];
5943
+ }
5944
+ },
5824
5945
  patternDetector({
5825
5946
  id: "value.phone",
5826
5947
  pattern: /\b(?:\+\d{1,3}[\s.-]?)?(?:\(?\d{3}\)?[\s.-])\d{3}[\s.-]\d{4}\b/
@@ -6038,6 +6159,92 @@ function redact(value, options) {
6038
6159
  return createRedactor(options).redact(value);
6039
6160
  }
6040
6161
 
6162
+ // packages/mcp-server/src/assess-trace.ts
6163
+ var DEFAULT_MAX_STRING_LENGTH = 16384;
6164
+ var DEFAULT_MAX_ARRAY_LENGTH = 1e3;
6165
+ var DEFAULT_MAX_OBJECT_KEYS = 200;
6166
+ var DEFAULT_MAX_SERIALIZED_BYTES = 128 * 1024;
6167
+ function buildMcpSafetyRules() {
6168
+ return [
6169
+ createSafetyRawContentRule(),
6170
+ createSafetyRedactionRule(),
6171
+ createSafetySecretPatternRule(),
6172
+ createSafetyOversizedAttributeRule({
6173
+ maxStringLength: DEFAULT_MAX_STRING_LENGTH,
6174
+ maxArrayLength: DEFAULT_MAX_ARRAY_LENGTH,
6175
+ maxObjectKeys: DEFAULT_MAX_OBJECT_KEYS,
6176
+ maxSerializedBytes: DEFAULT_MAX_SERIALIZED_BYTES
6177
+ })
6178
+ ];
6179
+ }
6180
+ function statusFrom(findings, hasErrors) {
6181
+ if (hasErrors) return "UNKNOWN";
6182
+ if (findings.some((item) => item.severity === "error")) return "UNSAFE";
6183
+ if (findings.some((item) => item.severity === "warning")) return "SAFE WITH WARNINGS";
6184
+ return "SAFE";
6185
+ }
6186
+ function redactJsonl(content, profile) {
6187
+ const trimmed = content.trim();
6188
+ if (trimmed.startsWith("{")) {
6189
+ try {
6190
+ const parsed = JSON.parse(trimmed);
6191
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && ("schemaVersion" in parsed || "eventId" in parsed || "runId" in parsed)) {
6192
+ } else {
6193
+ const result = redact(parsed, { profile });
6194
+ return `${JSON.stringify(result.value)}
6195
+ `;
6196
+ }
6197
+ } catch {
6198
+ }
6199
+ }
6200
+ const lines = content.split(/\r?\n/);
6201
+ const out = [];
6202
+ for (const line of lines) {
6203
+ if (line.trim() === "") continue;
6204
+ const parsed = JSON.parse(line);
6205
+ const result = redact(parsed, { profile });
6206
+ out.push(JSON.stringify(result.value));
6207
+ }
6208
+ return out.length === 0 ? "" : `${out.join("\n")}
6209
+ `;
6210
+ }
6211
+ function assessTraceForMcp(read, runId) {
6212
+ const rules = buildMcpSafetyRules();
6213
+ const checkResult = runTraceChecks({ read }, { rules, runId });
6214
+ const hasErrors = checkResult.diagnostics.some((item) => item.severity === "error");
6215
+ const status = statusFrom(checkResult.findings, hasErrors);
6216
+ return {
6217
+ status,
6218
+ errors: checkResult.diagnostics.filter((item) => item.severity === "error").length + checkResult.findings.filter((item) => item.severity === "error").length,
6219
+ warnings: checkResult.diagnostics.filter((item) => item.severity === "warning").length + checkResult.findings.filter((item) => item.severity === "warning").length,
6220
+ findings: checkResult.findings.length
6221
+ };
6222
+ }
6223
+ async function assessTraceArtifactForMcp(options) {
6224
+ const source = assessTraceForMcp(options.read, options.runId);
6225
+ try {
6226
+ const raw = await readFile(options.filePath, "utf-8");
6227
+ const redacted = redactJsonl(raw, options.profile);
6228
+ const artifactRead = await openTrace(
6229
+ { type: "string", content: redacted },
6230
+ { format: "agent-inspect-jsonl" }
6231
+ );
6232
+ const artifact = assessTraceForMcp(artifactRead, options.runId);
6233
+ return {
6234
+ ...artifact,
6235
+ sourceStatus: source.status
6236
+ };
6237
+ } catch {
6238
+ return {
6239
+ status: "UNKNOWN",
6240
+ errors: Math.max(1, source.errors),
6241
+ warnings: source.warnings,
6242
+ findings: source.findings,
6243
+ sourceStatus: source.status
6244
+ };
6245
+ }
6246
+ }
6247
+
6041
6248
  // packages/mcp-server/src/prepare-result.ts
6042
6249
  var DEFAULT_MCP_RESULT_MAX_BYTES = 512 * 1024;
6043
6250
  function stableStringify(value) {
@@ -6396,16 +6603,21 @@ async function callReadOnlyTool(context, name, args = {}) {
6396
6603
  }
6397
6604
  case "create_share_safe_bundle": {
6398
6605
  const runId = String(args.runId ?? "");
6399
- const { read } = await openRunTrace(context, runId);
6606
+ const { meta, read } = await openRunTrace(context, runId);
6400
6607
  const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
6401
6608
  if (!run) return errorResult2(`Run tree not found: ${runId}`);
6402
- const safety = assessTraceForMcp(read, runId);
6609
+ const profile = redactionProfileForExport(context);
6610
+ const safety = await assessTraceArtifactForMcp({
6611
+ read,
6612
+ runId,
6613
+ filePath: meta.filePath,
6614
+ profile
6615
+ });
6403
6616
  if (bundleFailsOnSafety(safety.status)) {
6404
6617
  return errorResult2(
6405
- `Share-safe bundle refused: safety status is ${safety.status}. Resolve findings before export.`
6618
+ `Share-safe bundle refused: artifact safety status is ${safety.status}. Resolve findings before export.`
6406
6619
  );
6407
6620
  }
6408
- const profile = redactionProfileForExport(context);
6409
6621
  const markdown = exportMarkdown(run, {
6410
6622
  redacted: true});
6411
6623
  const tree = exportRunTree(run, {
@@ -6423,6 +6635,7 @@ async function callReadOnlyTool(context, name, args = {}) {
6423
6635
  {
6424
6636
  runId,
6425
6637
  status: safety.status,
6638
+ ...safety.sourceStatus !== void 0 ? { sourceStatus: safety.sourceStatus } : {},
6426
6639
  errors: safety.errors,
6427
6640
  warnings: safety.warnings,
6428
6641
  findings: safety.findings