agent-inspect 1.2.0 → 1.4.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.
@@ -193,6 +193,51 @@ function isPersistedInspectEvent(value) {
193
193
  return true;
194
194
  }
195
195
 
196
+ // packages/core/src/correlation-metadata.ts
197
+ var TRACE_CORRELATION_KEYS = [
198
+ "correlationId",
199
+ "requestId",
200
+ "decisionId",
201
+ "groupId"
202
+ ];
203
+ function isNonEmptyString2(value) {
204
+ return typeof value === "string" && value.length > 0;
205
+ }
206
+ function extractCorrelationMetadata(record) {
207
+ if (!record) {
208
+ return void 0;
209
+ }
210
+ const out = {};
211
+ let found = false;
212
+ for (const key of TRACE_CORRELATION_KEYS) {
213
+ const value = record[key];
214
+ if (isNonEmptyString2(value)) {
215
+ out[key] = value;
216
+ found = true;
217
+ }
218
+ }
219
+ return found ? out : void 0;
220
+ }
221
+ function buildRunStartedMetadata(options) {
222
+ if (!options) {
223
+ return void 0;
224
+ }
225
+ const merged = options.metadata !== void 0 ? { ...options.metadata } : {};
226
+ if (isNonEmptyString2(options.correlationId)) {
227
+ merged.correlationId = options.correlationId;
228
+ }
229
+ if (isNonEmptyString2(options.requestId)) {
230
+ merged.requestId = options.requestId;
231
+ }
232
+ if (isNonEmptyString2(options.decisionId)) {
233
+ merged.decisionId = options.decisionId;
234
+ }
235
+ if (isNonEmptyString2(options.groupId)) {
236
+ merged.groupId = options.groupId;
237
+ }
238
+ return Object.keys(merged).length > 0 ? merged : void 0;
239
+ }
240
+
196
241
  // packages/core/src/persisted/from-trace-event.ts
197
242
  function sanitizeIdPart(value) {
198
243
  return value.replace(/[^a-zA-Z0-9_-]/g, "_");
@@ -299,9 +344,14 @@ function traceEventToPersistedInspectEvent(event, options) {
299
344
  switch (event.event) {
300
345
  case "run_started": {
301
346
  const tsStart = toIsoTimestamp(event.startTime);
347
+ const correlation = extractCorrelationMetadata(event.metadata);
302
348
  const attributes = compactAttributes({
303
349
  legacyEvent: "run_started",
304
350
  metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
351
+ correlationId: correlation?.correlationId,
352
+ requestId: correlation?.requestId,
353
+ decisionId: correlation?.decisionId,
354
+ groupId: correlation?.groupId,
305
355
  invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
306
356
  });
307
357
  return {
@@ -1138,7 +1188,7 @@ function stableHash(value) {
1138
1188
  const h = crypto__default.default.createHash("sha256").update(value, "utf8").digest("hex");
1139
1189
  return h.slice(0, 8);
1140
1190
  }
1141
- function compileRules(rules) {
1191
+ function compileRules(rules, extraKeys) {
1142
1192
  const out = /* @__PURE__ */ new Map();
1143
1193
  const set = (r) => {
1144
1194
  const k = toKey(r.key);
@@ -1147,6 +1197,11 @@ function compileRules(rules) {
1147
1197
  for (const k of DEFAULT_REDACT_KEYS) {
1148
1198
  set({ key: k, strategy: "full" });
1149
1199
  }
1200
+ for (const k of extraKeys ?? []) {
1201
+ if (typeof k === "string" && k.length > 0) {
1202
+ set({ key: k, strategy: "full" });
1203
+ }
1204
+ }
1150
1205
  for (const r of rules ?? []) {
1151
1206
  if (typeof r === "string") {
1152
1207
  set({ key: r, strategy: "full" });
@@ -1164,7 +1219,7 @@ function compileRules(rules) {
1164
1219
  var Redactor = class {
1165
1220
  #rules;
1166
1221
  constructor(options) {
1167
- this.#rules = compileRules(options?.rules);
1222
+ this.#rules = compileRules(options?.rules, options?.extraKeys);
1168
1223
  }
1169
1224
  redactValue(key, value) {
1170
1225
  const k = toKey(key);
@@ -1869,6 +1924,91 @@ function warn(message, error) {
1869
1924
  }
1870
1925
  console.warn(`${base}: ${formatError(error).message}`);
1871
1926
  }
1927
+
1928
+ // packages/core/src/redaction-profiles.ts
1929
+ var SHARE_PROFILE_EXTRA_KEYS = [
1930
+ "userEmail",
1931
+ "customerEmail",
1932
+ "phone",
1933
+ "phoneNumber",
1934
+ "address",
1935
+ "ip",
1936
+ "ipAddress",
1937
+ "sessionId",
1938
+ "requestId",
1939
+ "correlationId",
1940
+ "decisionId",
1941
+ "groupId",
1942
+ "customerId",
1943
+ "userId",
1944
+ "accountId",
1945
+ "tenantId",
1946
+ "orgId",
1947
+ "organizationId",
1948
+ "traceId",
1949
+ "spanId",
1950
+ "parentSpanId"
1951
+ ];
1952
+ var STRICT_PROFILE_EXTRA_KEYS = [
1953
+ "prompt",
1954
+ "completion",
1955
+ "input",
1956
+ "output",
1957
+ "inputPreview",
1958
+ "outputPreview",
1959
+ "message",
1960
+ "messages",
1961
+ "transcript",
1962
+ "context",
1963
+ "document",
1964
+ "documents",
1965
+ "chunk",
1966
+ "chunks",
1967
+ "retrieval",
1968
+ "query"
1969
+ ];
1970
+ function resolveRedactionProfile(profile = "local") {
1971
+ switch (profile) {
1972
+ case "local":
1973
+ return { profile: "local", extraKeys: [] };
1974
+ case "share":
1975
+ return {
1976
+ profile: "share",
1977
+ extraKeys: SHARE_PROFILE_EXTRA_KEYS,
1978
+ maxMetadataValueLengthCap: 500,
1979
+ maxPreviewLengthCap: 200
1980
+ };
1981
+ case "strict":
1982
+ return {
1983
+ profile: "strict",
1984
+ extraKeys: [...SHARE_PROFILE_EXTRA_KEYS, ...STRICT_PROFILE_EXTRA_KEYS],
1985
+ maxMetadataValueLengthCap: 200,
1986
+ maxPreviewLengthCap: 80
1987
+ };
1988
+ default:
1989
+ return { profile: "local", extraKeys: [] };
1990
+ }
1991
+ }
1992
+ function isPreviewKey(key) {
1993
+ return key.toLowerCase().includes("preview");
1994
+ }
1995
+ function applyProfileMetadataCaps(maxMetadataValueLength, maxPreviewLength, resolved) {
1996
+ let meta = maxMetadataValueLength;
1997
+ let preview = maxPreviewLength;
1998
+ if (resolved.maxMetadataValueLengthCap !== void 0) {
1999
+ meta = Math.min(meta, resolved.maxMetadataValueLengthCap);
2000
+ }
2001
+ if (resolved.maxPreviewLengthCap !== void 0) {
2002
+ preview = Math.min(preview, resolved.maxPreviewLengthCap);
2003
+ }
2004
+ return { maxMetadataValueLength: meta, maxPreviewLength: preview };
2005
+ }
2006
+ function truncateStringForProfile(value, key, maxMetadataValueLength, maxPreviewLength) {
2007
+ const max = isPreviewKey(key) ? maxPreviewLength : maxMetadataValueLength;
2008
+ if (max <= 0) return "\u2026";
2009
+ if (value.length <= max) return value;
2010
+ return `${value.slice(0, max)}\u2026`;
2011
+ }
1872
2012
  function isRecord7(value) {
1873
2013
  return typeof value === "object" && value !== null && !Array.isArray(value);
1874
2014
  }
@@ -2072,7 +2212,7 @@ var DEFAULT_MAX_EVENT_BYTES = 65536;
2072
2212
  function isRecord8(value) {
2073
2213
  return typeof value === "object" && value !== null && !Array.isArray(value);
2074
2214
  }
2075
- function isPreviewKey(key) {
2215
+ function isPreviewKey2(key) {
2076
2216
  return key.toLowerCase().includes("preview");
2077
2217
  }
2078
2218
  function truncateString(value, maxLen) {
@@ -2095,11 +2235,28 @@ function resolveTraceSafetyOptions(options) {
2095
2235
  redactEnabled = true;
2096
2236
  redactionRules = redact.rules;
2097
2237
  }
2238
+ const profile = options?.redactionProfile ?? "local";
2239
+ const resolvedProfile = resolveRedactionProfile(profile);
2240
+ const userMaxMetadata = typeof options?.maxMetadataValueLength === "number" && Number.isFinite(options.maxMetadataValueLength) && options.maxMetadataValueLength >= 0 ? Math.floor(options.maxMetadataValueLength) : void 0;
2241
+ const userMaxPreview = typeof options?.maxPreviewLength === "number" && Number.isFinite(options.maxPreviewLength) && options.maxPreviewLength >= 0 ? Math.floor(options.maxPreviewLength) : void 0;
2242
+ let maxMetadataValueLength = userMaxMetadata ?? DEFAULT_MAX_METADATA_VALUE_LENGTH;
2243
+ let maxPreviewLength = userMaxPreview ?? DEFAULT_MAX_PREVIEW_LENGTH;
2244
+ if (redactEnabled && profile !== "local") {
2245
+ const capped = applyProfileMetadataCaps(
2246
+ maxMetadataValueLength,
2247
+ maxPreviewLength,
2248
+ resolvedProfile
2249
+ );
2250
+ maxMetadataValueLength = capped.maxMetadataValueLength;
2251
+ maxPreviewLength = capped.maxPreviewLength;
2252
+ }
2098
2253
  return {
2099
2254
  redactEnabled,
2100
2255
  redactionRules,
2101
- maxMetadataValueLength: typeof options?.maxMetadataValueLength === "number" && Number.isFinite(options.maxMetadataValueLength) && options.maxMetadataValueLength >= 0 ? Math.floor(options.maxMetadataValueLength) : DEFAULT_MAX_METADATA_VALUE_LENGTH,
2102
- maxPreviewLength: typeof options?.maxPreviewLength === "number" && Number.isFinite(options.maxPreviewLength) && options.maxPreviewLength >= 0 ? Math.floor(options.maxPreviewLength) : DEFAULT_MAX_PREVIEW_LENGTH,
2256
+ redactionProfile: profile,
2257
+ profileExtraKeys: redactEnabled ? resolvedProfile.extraKeys : [],
2258
+ maxMetadataValueLength,
2259
+ maxPreviewLength,
2103
2260
  maxEventBytes: typeof options?.maxEventBytes === "number" && Number.isFinite(options.maxEventBytes) && options.maxEventBytes > 0 ? Math.floor(options.maxEventBytes) : DEFAULT_MAX_EVENT_BYTES
2104
2261
  };
2105
2262
  }
@@ -2107,7 +2264,7 @@ function boundMetadataValue(key, value, opts, seen, depth) {
2107
2264
  if (depth > 32) return "[MaxDepth]";
2108
2265
  if (value === null || typeof value !== "object") {
2109
2266
  if (typeof value === "string") {
2110
- const max = isPreviewKey(key) ? opts.maxPreviewLength : opts.maxMetadataValueLength;
2267
+ const max = isPreviewKey2(key) ? opts.maxPreviewLength : opts.maxMetadataValueLength;
2111
2268
  return truncateString(value, max);
2112
2269
  }
2113
2270
  return value;
@@ -2133,7 +2290,10 @@ function boundMetadataValue(key, value, opts, seen, depth) {
2133
2290
  }
2134
2291
  function redactMetadata(metadata, opts) {
2135
2292
  if (!opts.redactEnabled) return { ...metadata };
2136
- const redactor = new Redactor({ rules: opts.redactionRules });
2293
+ const redactor = new Redactor({
2294
+ rules: opts.redactionRules,
2295
+ extraKeys: opts.profileExtraKeys
2296
+ });
2137
2297
  return redactor.redactRecord(metadata);
2138
2298
  }
2139
2299
  function prepareMetadataForDisk(metadata, opts) {
@@ -2305,6 +2465,13 @@ function getCurrentRunId() {
2305
2465
  return void 0;
2306
2466
  }
2307
2467
  }
2468
+ function getCurrentCorrelationMetadata() {
2469
+ try {
2470
+ return extractCorrelationMetadata(storage.getStore()?.metadata);
2471
+ } catch {
2472
+ return void 0;
2473
+ }
2474
+ }
2308
2475
  function getCurrentRunName() {
2309
2476
  try {
2310
2477
  return storage.getStore()?.runName;
@@ -2667,6 +2834,574 @@ function filterTraces(traces, options) {
2667
2834
  }
2668
2835
  return out;
2669
2836
  }
2837
+
2838
+ // packages/core/src/timeline.ts
2839
+ function finite(n) {
2840
+ return typeof n === "number" && Number.isFinite(n);
2841
+ }
2842
+ function pickStreamingMeta(metadata) {
2843
+ if (!metadata || typeof metadata !== "object") return void 0;
2844
+ const chunkCount = metadata.chunkCount;
2845
+ const streamDurationMs = metadata.streamDurationMs;
2846
+ const streamedCharCount = metadata.streamedCharCount;
2847
+ if (!finite(chunkCount) && !finite(streamDurationMs) && !finite(streamedCharCount)) {
2848
+ return void 0;
2849
+ }
2850
+ return {
2851
+ ...finite(chunkCount) ? { chunkCount } : {},
2852
+ ...finite(streamDurationMs) ? { streamDurationMs } : {},
2853
+ ...finite(streamedCharCount) ? { streamedCharCount } : {}
2854
+ };
2855
+ }
2856
+ function pickCorrelation(metadata) {
2857
+ if (!metadata || typeof metadata !== "object") return void 0;
2858
+ const out = {};
2859
+ for (const key of [
2860
+ "correlationId",
2861
+ "requestId",
2862
+ "decisionId",
2863
+ "groupId"
2864
+ ]) {
2865
+ const v = metadata[key];
2866
+ if (typeof v === "string" && v.trim() !== "") {
2867
+ out[key] = v;
2868
+ }
2869
+ }
2870
+ return Object.keys(out).length > 0 ? out : void 0;
2871
+ }
2872
+ function buildRunTimeline(events, options = {}) {
2873
+ const started = events.find(
2874
+ (e) => e.event === "run_started"
2875
+ );
2876
+ const completed = events.filter(
2877
+ (e) => e.event === "run_completed"
2878
+ );
2879
+ const lastCompleted = completed[completed.length - 1];
2880
+ const runId = started?.runId ?? events.find((e) => typeof e.runId === "string")?.runId ?? "unknown-run";
2881
+ const runStart = started && finite(started.startTime) ? started.startTime : started && finite(started.timestamp) ? started.timestamp : void 0;
2882
+ const status = lastCompleted ? lastCompleted.status : started ? "running" : "unknown";
2883
+ const steps = /* @__PURE__ */ new Map();
2884
+ for (const e of events) {
2885
+ if (e.event === "step_started") {
2886
+ const s = e;
2887
+ steps.set(s.stepId, {
2888
+ name: s.name,
2889
+ type: s.type,
2890
+ parentId: s.parentId,
2891
+ startedAt: finite(s.startTime) ? s.startTime : s.timestamp,
2892
+ status: "running",
2893
+ metadata: s.metadata
2894
+ });
2895
+ }
2896
+ }
2897
+ for (const e of events) {
2898
+ if (e.event !== "step_completed") continue;
2899
+ const c = e;
2900
+ const node = steps.get(c.stepId);
2901
+ if (!node) continue;
2902
+ node.status = c.status;
2903
+ if (finite(c.durationMs)) node.durationMs = c.durationMs;
2904
+ }
2905
+ const depthCache = /* @__PURE__ */ new Map();
2906
+ const computeDepth = (stepId) => {
2907
+ const cached = depthCache.get(stepId);
2908
+ if (cached !== void 0) return cached;
2909
+ const node = steps.get(stepId);
2910
+ if (!node) return 0;
2911
+ const parent = node.parentId;
2912
+ if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
2913
+ depthCache.set(stepId, 0);
2914
+ return 0;
2915
+ }
2916
+ const d = Math.min(1e3, computeDepth(parent) + 1);
2917
+ depthCache.set(stepId, d);
2918
+ return d;
2919
+ };
2920
+ const entries = [];
2921
+ for (const [stepId, s] of steps.entries()) {
2922
+ const offsetMs = runStart !== void 0 && finite(s.startedAt) ? Math.max(0, s.startedAt - runStart) : 0;
2923
+ entries.push({
2924
+ stepId,
2925
+ name: s.name,
2926
+ type: s.type,
2927
+ status: s.status,
2928
+ depth: computeDepth(stepId),
2929
+ startedAt: s.startedAt,
2930
+ offsetMs,
2931
+ durationMs: s.durationMs,
2932
+ isError: s.status === "error",
2933
+ streaming: pickStreamingMeta(s.metadata)
2934
+ });
2935
+ }
2936
+ entries.sort((a, b) => a.startedAt - b.startedAt);
2937
+ const slowTopN = options.slowTopN ?? 3;
2938
+ if (options.focus === "slow" && entries.length > 0) {
2939
+ const ranked = [...entries].filter((e) => finite(e.durationMs)).sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0));
2940
+ const slowIds = new Set(
2941
+ ranked.slice(0, slowTopN).map((e) => e.stepId)
2942
+ );
2943
+ for (const e of entries) {
2944
+ if (slowIds.has(e.stepId)) e.slow = true;
2945
+ }
2946
+ }
2947
+ return {
2948
+ runId,
2949
+ name: typeof started?.name === "string" && started.name.trim() !== "" ? started.name : void 0,
2950
+ status,
2951
+ startedAt: runStart,
2952
+ endedAt: lastCompleted && finite(lastCompleted.endTime) ? lastCompleted.endTime : void 0,
2953
+ durationMs: lastCompleted && finite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0,
2954
+ correlation: pickCorrelation(
2955
+ started?.metadata
2956
+ ),
2957
+ entries
2958
+ };
2959
+ }
2960
+ function renderTimeline(timeline, options = {}) {
2961
+ const lines = [];
2962
+ lines.push(`Timeline: ${timeline.name ?? timeline.runId}`);
2963
+ lines.push(`Run ID: ${timeline.runId}`);
2964
+ lines.push(`Status: ${timeline.status}`);
2965
+ if (timeline.startedAt !== void 0) {
2966
+ lines.push(`Started: ${formatTimestamp(timeline.startedAt)}`);
2967
+ }
2968
+ if (timeline.durationMs !== void 0) {
2969
+ lines.push(`Duration: ${formatDuration2(timeline.durationMs)}`);
2970
+ }
2971
+ if (timeline.correlation) {
2972
+ const parts = Object.entries(timeline.correlation).filter(([, v]) => typeof v === "string").map(([k, v]) => `${k}=${v}`);
2973
+ if (parts.length > 0) {
2974
+ lines.push(`Correlation: ${parts.join(", ")}`);
2975
+ }
2976
+ }
2977
+ lines.push("");
2978
+ lines.push("Steps (chronological):");
2979
+ const show = timeline.entries.filter((e) => {
2980
+ if (options.focus === "slow") return e.slow === true;
2981
+ return true;
2982
+ });
2983
+ if (show.length === 0) {
2984
+ lines.push(
2985
+ options.focus === "slow" ? "(no steps with duration for slow focus)" : "(no steps)"
2986
+ );
2987
+ return lines.join("\n");
2988
+ }
2989
+ for (const e of show) {
2990
+ const prefix = e.slow ? "[slow] " : "";
2991
+ const typeTag = e.type === "llm" ? "llm" : e.type === "tool" ? "tool" : e.type;
2992
+ const dur = e.durationMs !== void 0 ? formatDuration2(e.durationMs) : "-";
2993
+ const err = e.isError ? " error" : "";
2994
+ const off = formatDuration2(e.offsetMs);
2995
+ let line = `${prefix}+${off} ${typeTag}:${e.name} (${dur})${err}`;
2996
+ if (e.streaming?.chunkCount !== void 0) {
2997
+ line += ` chunks=${e.streaming.chunkCount}`;
2998
+ }
2999
+ if (e.streaming?.streamDurationMs !== void 0) {
3000
+ line += ` stream=${formatDuration2(e.streaming.streamDurationMs)}`;
3001
+ }
3002
+ lines.push(line);
3003
+ }
3004
+ return lines.join("\n");
3005
+ }
3006
+ function percentile(sorted, p) {
3007
+ if (sorted.length === 0) return void 0;
3008
+ const idx = Math.min(
3009
+ sorted.length - 1,
3010
+ Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
3011
+ );
3012
+ return sorted[idx];
3013
+ }
3014
+ async function readRunStartedMetadata(filePath) {
3015
+ try {
3016
+ const raw = await promises.readFile(filePath, "utf-8");
3017
+ for (const line of raw.split(/\r?\n/)) {
3018
+ const trimmed = line.trim();
3019
+ if (trimmed === "") continue;
3020
+ let parsed;
3021
+ try {
3022
+ parsed = JSON.parse(trimmed);
3023
+ } catch {
3024
+ continue;
3025
+ }
3026
+ if (!isTraceEvent(parsed)) continue;
3027
+ if (parsed.event !== "run_started") continue;
3028
+ const rs = parsed;
3029
+ if (rs.metadata && typeof rs.metadata === "object") {
3030
+ return rs.metadata;
3031
+ }
3032
+ return void 0;
3033
+ }
3034
+ } catch {
3035
+ }
3036
+ return void 0;
3037
+ }
3038
+ function metaMatchesCorrelation(metadata, correlationId, groupId) {
3039
+ if (correlationId) {
3040
+ const v = metadata?.correlationId;
3041
+ if (typeof v !== "string" || v !== correlationId) return false;
3042
+ }
3043
+ if (groupId) {
3044
+ const v = metadata?.groupId;
3045
+ if (typeof v !== "string" || v !== groupId) return false;
3046
+ }
3047
+ return true;
3048
+ }
3049
+ async function buildTraceStats(metas, options) {
3050
+ let filtered = filterTraces(metas, { since: options.since });
3051
+ if (options.correlationId || options.groupId) {
3052
+ const next = [];
3053
+ for (const m of filtered) {
3054
+ const md = await readRunStartedMetadata(m.filePath);
3055
+ if (metaMatchesCorrelation(md, options.correlationId, options.groupId)) {
3056
+ next.push(m);
3057
+ }
3058
+ }
3059
+ filtered = next;
3060
+ }
3061
+ let successCount = 0;
3062
+ let errorCount = 0;
3063
+ let runningCount = 0;
3064
+ let unknownCount = 0;
3065
+ const durations = [];
3066
+ let totalSteps = 0;
3067
+ let totalLlmSteps = 0;
3068
+ let totalToolSteps = 0;
3069
+ let totalErrorSteps = 0;
3070
+ const slowestRuns = [];
3071
+ const slowestSteps = [];
3072
+ for (const m of filtered) {
3073
+ if (m.status === "success") successCount += 1;
3074
+ else if (m.status === "error") errorCount += 1;
3075
+ else if (m.status === "running") runningCount += 1;
3076
+ else unknownCount += 1;
3077
+ if (typeof m.durationMs === "number" && Number.isFinite(m.durationMs) && m.durationMs >= 0) {
3078
+ durations.push(m.durationMs);
3079
+ slowestRuns.push({
3080
+ runId: m.runId,
3081
+ name: m.name,
3082
+ durationMs: m.durationMs,
3083
+ status: m.status
3084
+ });
3085
+ }
3086
+ try {
3087
+ const events = await readTraceEvents(m.runId, options.traceDir);
3088
+ if (events.length === 0) continue;
3089
+ const summary = buildRunSummary(events);
3090
+ totalSteps += summary.totalSteps;
3091
+ totalLlmSteps += summary.llmSteps;
3092
+ totalToolSteps += summary.toolSteps;
3093
+ totalErrorSteps += summary.errorSteps;
3094
+ const steps = collectCompletedSteps(events, m.runId);
3095
+ for (const s of steps) {
3096
+ slowestSteps.push(s);
3097
+ }
3098
+ } catch {
3099
+ }
3100
+ }
3101
+ slowestRuns.sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0));
3102
+ slowestSteps.sort((a, b) => b.durationMs - a.durationMs);
3103
+ const runLimit = options.slowRunLimit ?? 5;
3104
+ const stepLimit = options.slowStepLimit ?? 5;
3105
+ const sortedDur = [...durations].sort((a, b) => a - b);
3106
+ const totalRuns = filtered.length;
3107
+ const errorRate = totalRuns > 0 ? errorCount / totalRuns : 0;
3108
+ const sumDur = durations.reduce((a, b) => a + b, 0);
3109
+ return {
3110
+ traceDir: options.traceDir,
3111
+ ...options.since ? { since: options.since } : {},
3112
+ ...options.correlationId ? { correlationId: options.correlationId } : {},
3113
+ ...options.groupId ? { groupId: options.groupId } : {},
3114
+ totalRuns,
3115
+ successCount,
3116
+ errorCount,
3117
+ runningCount,
3118
+ unknownCount,
3119
+ errorRate,
3120
+ duration: {
3121
+ ...sortedDur.length > 0 ? {
3122
+ minMs: sortedDur[0],
3123
+ maxMs: sortedDur[sortedDur.length - 1],
3124
+ avgMs: sumDur / sortedDur.length,
3125
+ p50Ms: percentile(sortedDur, 50),
3126
+ p95Ms: percentile(sortedDur, 95)
3127
+ } : {}
3128
+ },
3129
+ totalSteps,
3130
+ avgStepsPerRun: totalRuns > 0 ? totalSteps / totalRuns : 0,
3131
+ totalLlmSteps,
3132
+ totalToolSteps,
3133
+ totalErrorSteps,
3134
+ slowestRuns: slowestRuns.slice(0, runLimit),
3135
+ slowestSteps: slowestSteps.slice(0, stepLimit)
3136
+ };
3137
+ }
3138
+ function collectCompletedSteps(events, runId) {
3139
+ const started = /* @__PURE__ */ new Map();
3140
+ const out = [];
3141
+ for (const e of events) {
3142
+ if (e.event === "step_started") {
3143
+ const s = e;
3144
+ started.set(s.stepId, { name: s.name, type: s.type });
3145
+ }
3146
+ if (e.event === "step_completed") {
3147
+ const c = e;
3148
+ if (c.status !== "success" && c.status !== "error") continue;
3149
+ if (typeof c.durationMs !== "number" || !Number.isFinite(c.durationMs)) {
3150
+ continue;
3151
+ }
3152
+ const meta = started.get(c.stepId);
3153
+ out.push({
3154
+ runId,
3155
+ stepName: meta?.name ?? c.stepId,
3156
+ stepType: meta?.type ?? "logic",
3157
+ durationMs: c.durationMs
3158
+ });
3159
+ }
3160
+ }
3161
+ return out;
3162
+ }
3163
+ function renderTraceStats(stats) {
3164
+ const lines = [];
3165
+ lines.push("Trace stats (local)");
3166
+ lines.push(`Directory: ${stats.traceDir}`);
3167
+ if (stats.since) lines.push(`Since: ${stats.since}`);
3168
+ if (stats.correlationId) lines.push(`Correlation ID: ${stats.correlationId}`);
3169
+ if (stats.groupId) lines.push(`Group ID: ${stats.groupId}`);
3170
+ lines.push("");
3171
+ lines.push(`Runs: ${stats.totalRuns}`);
3172
+ lines.push(
3173
+ ` success: ${stats.successCount} error: ${stats.errorCount} running: ${stats.runningCount} unknown: ${stats.unknownCount}`
3174
+ );
3175
+ lines.push(`Error rate: ${(stats.errorRate * 100).toFixed(1)}%`);
3176
+ if (stats.duration.avgMs !== void 0) {
3177
+ lines.push(
3178
+ `Duration: min ${formatDuration2(stats.duration.minMs ?? 0)} | avg ${formatDuration2(stats.duration.avgMs)} | p50 ${formatDuration2(stats.duration.p50Ms ?? 0)} | p95 ${formatDuration2(stats.duration.p95Ms ?? 0)} | max ${formatDuration2(stats.duration.maxMs ?? 0)}`
3179
+ );
3180
+ }
3181
+ lines.push("");
3182
+ lines.push(`Steps: ${stats.totalSteps} (avg ${stats.avgStepsPerRun.toFixed(1)} per run)`);
3183
+ lines.push(
3184
+ ` LLM: ${stats.totalLlmSteps} tool: ${stats.totalToolSteps} errors: ${stats.totalErrorSteps}`
3185
+ );
3186
+ if (stats.slowestRuns.length > 0) {
3187
+ lines.push("");
3188
+ lines.push("Slowest runs:");
3189
+ for (const r of stats.slowestRuns) {
3190
+ lines.push(
3191
+ ` ${r.runId} | ${r.name ?? "-"} | ${formatDuration2(r.durationMs ?? 0)} | ${r.status}`
3192
+ );
3193
+ }
3194
+ }
3195
+ if (stats.slowestSteps.length > 0) {
3196
+ lines.push("");
3197
+ lines.push("Slowest steps:");
3198
+ for (const s of stats.slowestSteps) {
3199
+ lines.push(
3200
+ ` ${s.runId} | ${s.stepType}:${s.stepName} | ${formatDuration2(s.durationMs)}`
3201
+ );
3202
+ }
3203
+ }
3204
+ return lines.join("\n");
3205
+ }
3206
+
3207
+ // packages/core/src/search.ts
3208
+ function parseDurationFilter(expr) {
3209
+ const raw = expr.trim();
3210
+ const m = raw.match(/^(>=|<=|>|<)\s*(.+)$/);
3211
+ if (!m) {
3212
+ throw new Error(
3213
+ `Invalid --duration "${expr}". Use forms like >5s, >=500ms, <2m.`
3214
+ );
3215
+ }
3216
+ const op = m[1];
3217
+ const ms = parseDuration(m[2].trim());
3218
+ return { op, ms };
3219
+ }
3220
+ function durationMatches(valueMs, filter) {
3221
+ if (valueMs === void 0 || !Number.isFinite(valueMs)) return false;
3222
+ switch (filter.op) {
3223
+ case ">":
3224
+ return valueMs > filter.ms;
3225
+ case ">=":
3226
+ return valueMs >= filter.ms;
3227
+ case "<":
3228
+ return valueMs < filter.ms;
3229
+ case "<=":
3230
+ return valueMs <= filter.ms;
3231
+ default:
3232
+ return false;
3233
+ }
3234
+ }
3235
+ function normalizeStepTypeFilter(kind, type) {
3236
+ const v = (kind ?? type)?.trim().toLowerCase();
3237
+ return v && v !== "" ? v : void 0;
3238
+ }
3239
+ function nameMatches(hay, needle) {
3240
+ return hay.toLowerCase().includes(needle.toLowerCase());
3241
+ }
3242
+ async function searchTraces(metas, options) {
3243
+ let filtered = filterTraces(metas, { since: options.since });
3244
+ const stepTypeFilter = normalizeStepTypeFilter(options.kind, options.type);
3245
+ const nameQuery = options.name?.trim();
3246
+ const toolQuery = options.tool?.trim();
3247
+ let durationFilter;
3248
+ if (options.duration) {
3249
+ durationFilter = parseDurationFilter(options.duration);
3250
+ }
3251
+ const limit = options.limit ?? 50;
3252
+ const hasContentFilter = Boolean(
3253
+ options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter
3254
+ );
3255
+ const results = [];
3256
+ if (!hasContentFilter) {
3257
+ for (const m of filtered) {
3258
+ results.push({
3259
+ runId: m.runId,
3260
+ runName: m.name,
3261
+ runStatus: m.status,
3262
+ timestamp: m.startedAt,
3263
+ durationMs: m.durationMs,
3264
+ matchReason: "trace in directory",
3265
+ matchedFields: ["run"],
3266
+ filePath: m.filePath
3267
+ });
3268
+ }
3269
+ return results.slice(0, limit);
3270
+ }
3271
+ for (const m of filtered) {
3272
+ if (options.status && m.status !== options.status) continue;
3273
+ let events = [];
3274
+ try {
3275
+ events = await readTraceEvents(m.runId, options.traceDir);
3276
+ } catch {
3277
+ continue;
3278
+ }
3279
+ if (events.length === 0) continue;
3280
+ const runMatches = matchRunLevel(m, {
3281
+ stepTypeFilter,
3282
+ nameQuery,
3283
+ toolQuery,
3284
+ durationFilter,
3285
+ statusFilter: options.status
3286
+ });
3287
+ results.push(...runMatches);
3288
+ const stepMatches = matchStepLevel(m, events, {
3289
+ stepTypeFilter,
3290
+ nameQuery,
3291
+ toolQuery,
3292
+ durationFilter,
3293
+ statusFilter: options.status
3294
+ });
3295
+ results.push(...stepMatches);
3296
+ }
3297
+ results.sort((a, b) => {
3298
+ const ta = a.timestamp ?? 0;
3299
+ const tb = b.timestamp ?? 0;
3300
+ if (ta !== tb) return ta - tb;
3301
+ const runCmp = a.runId.localeCompare(b.runId);
3302
+ if (runCmp !== 0) return runCmp;
3303
+ return (a.stepName ?? "").localeCompare(b.stepName ?? "");
3304
+ });
3305
+ return results.slice(0, limit);
3306
+ }
3307
+ function matchRunLevel(m, opts) {
3308
+ if (opts.stepTypeFilter || opts.toolQuery) return [];
3309
+ const out = [];
3310
+ const fields = [];
3311
+ if (opts.statusFilter && m.status === opts.statusFilter) {
3312
+ fields.push("run.status");
3313
+ }
3314
+ if (opts.nameQuery && nameMatches(m.name ?? m.runId, opts.nameQuery)) {
3315
+ fields.push("run.name");
3316
+ }
3317
+ if (opts.durationFilter && durationMatches(m.durationMs, opts.durationFilter)) {
3318
+ fields.push("run.durationMs");
3319
+ }
3320
+ if (fields.length === 0) return out;
3321
+ out.push({
3322
+ runId: m.runId,
3323
+ runName: m.name,
3324
+ runStatus: m.status,
3325
+ timestamp: m.startedAt,
3326
+ durationMs: m.durationMs,
3327
+ matchReason: `run match: ${fields.join(", ")}`,
3328
+ matchedFields: fields,
3329
+ filePath: m.filePath
3330
+ });
3331
+ return out;
3332
+ }
3333
+ function matchStepLevel(m, events, opts) {
3334
+ const out = [];
3335
+ const started = /* @__PURE__ */ new Map();
3336
+ for (const e of events) {
3337
+ if (e.event === "step_started") {
3338
+ started.set(e.stepId, e);
3339
+ }
3340
+ }
3341
+ for (const e of events) {
3342
+ if (e.event !== "step_completed") continue;
3343
+ const c = e;
3344
+ const s = started.get(c.stepId);
3345
+ if (!s) continue;
3346
+ const fields = [];
3347
+ const stepType = s.type;
3348
+ if (opts.stepTypeFilter && stepType !== opts.stepTypeFilter) {
3349
+ continue;
3350
+ }
3351
+ const hasStepFilters = opts.stepTypeFilter || opts.nameQuery || opts.toolQuery || opts.durationFilter || opts.statusFilter === "error" || opts.statusFilter === "success";
3352
+ if (!hasStepFilters) continue;
3353
+ if (opts.statusFilter === "error" && c.status === "error") {
3354
+ fields.push("step.status");
3355
+ } else if (opts.statusFilter === "success" && c.status === "success") {
3356
+ fields.push("step.status");
3357
+ } else if (opts.statusFilter === "error" || opts.statusFilter === "success") {
3358
+ continue;
3359
+ }
3360
+ if (opts.nameQuery) {
3361
+ if (!nameMatches(s.name, opts.nameQuery)) continue;
3362
+ fields.push("step.name");
3363
+ }
3364
+ if (opts.toolQuery) {
3365
+ const toolName = typeof s.metadata?.toolName === "string" ? s.metadata.toolName : s.name;
3366
+ if (!nameMatches(toolName, opts.toolQuery)) continue;
3367
+ fields.push("step.tool");
3368
+ }
3369
+ if (opts.durationFilter) {
3370
+ if (!durationMatches(c.durationMs, opts.durationFilter)) continue;
3371
+ fields.push("step.durationMs");
3372
+ }
3373
+ if (opts.stepTypeFilter) {
3374
+ fields.push("step.type");
3375
+ }
3376
+ if (fields.length === 0) continue;
3377
+ out.push({
3378
+ runId: m.runId,
3379
+ runName: m.name,
3380
+ runStatus: m.status,
3381
+ stepId: c.stepId,
3382
+ stepName: s.name,
3383
+ stepType,
3384
+ timestamp: s.startTime ?? s.timestamp,
3385
+ durationMs: c.durationMs,
3386
+ matchReason: `step match: ${fields.join(", ")}`,
3387
+ matchedFields: fields,
3388
+ filePath: m.filePath
3389
+ });
3390
+ }
3391
+ return out;
3392
+ }
3393
+ async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
3394
+ const metas = [];
3395
+ for (const fileName of fileNames) {
3396
+ try {
3397
+ const filePath = getPath(fileName);
3398
+ const meta = await extractMetadata(filePath);
3399
+ metas.push(meta);
3400
+ } catch {
3401
+ }
3402
+ }
3403
+ return metas;
3404
+ }
2670
3405
  var KNOWN_EVENTS = /* @__PURE__ */ new Set([
2671
3406
  "run_started",
2672
3407
  "run_completed",
@@ -3948,12 +4683,13 @@ async function inspectRun(name, fn, options) {
3948
4683
  const runId = createRunId();
3949
4684
  const traceDir = resolveTraceDir({ dir: options?.traceDir });
3950
4685
  const traceSafety = resolveTraceSafetyOptions(options);
4686
+ const runMetadata = buildRunStartedMetadata(options);
3951
4687
  const context = {
3952
4688
  runId,
3953
4689
  runName,
3954
4690
  traceDir,
3955
4691
  silent: options?.silent ?? false,
3956
- metadata: options?.metadata
4692
+ metadata: runMetadata
3957
4693
  };
3958
4694
  return runWithContext(context, async () => {
3959
4695
  const startTime = Date.now();
@@ -3969,7 +4705,7 @@ async function inspectRun(name, fn, options) {
3969
4705
  runId,
3970
4706
  name: runName,
3971
4707
  startTime,
3972
- ...options?.metadata !== void 0 ? { metadata: options.metadata } : {}
4708
+ ...runMetadata !== void 0 ? { metadata: runMetadata } : {}
3973
4709
  };
3974
4710
  await writeTraceEvent(
3975
4711
  prepareTraceEventForDisk(started, traceSafety),
@@ -4254,6 +4990,134 @@ function observe(agent, options) {
4254
4990
  // packages/core/src/exporters/types.ts
4255
4991
  var EXPORT_PAYLOAD_VERSION = "0.1.2";
4256
4992
 
4993
+ // packages/core/src/exporters/redact-export.ts
4994
+ function isRecord10(value) {
4995
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4996
+ }
4997
+ function deepClone(value) {
4998
+ if (value === null || typeof value !== "object") {
4999
+ return value;
5000
+ }
5001
+ if (Array.isArray(value)) {
5002
+ return value.map((item) => deepClone(item));
5003
+ }
5004
+ const out = {};
5005
+ for (const [k, v] of Object.entries(value)) {
5006
+ out[k] = deepClone(v);
5007
+ }
5008
+ return out;
5009
+ }
5010
+ function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
5011
+ if (depth > 32) {
5012
+ return { truncated: true, reason: "maxDepth" };
5013
+ }
5014
+ const out = {};
5015
+ for (const [key, value] of Object.entries(record)) {
5016
+ out[key] = boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth);
5017
+ }
5018
+ return out;
5019
+ }
5020
+ function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
5021
+ if (value === null || typeof value !== "object") {
5022
+ if (typeof value === "string") {
5023
+ return truncateStringForProfile(
5024
+ value,
5025
+ key,
5026
+ maxMetadataValueLength,
5027
+ maxPreviewLength
5028
+ );
5029
+ }
5030
+ return value;
5031
+ }
5032
+ if (seen.has(value)) return "[Circular]";
5033
+ seen.add(value);
5034
+ if (Array.isArray(value)) {
5035
+ return value.slice(0, 50).map(
5036
+ (item, index) => boundValue(
5037
+ item,
5038
+ String(index),
5039
+ maxMetadataValueLength,
5040
+ maxPreviewLength,
5041
+ seen,
5042
+ depth + 1
5043
+ )
5044
+ );
5045
+ }
5046
+ return boundAttributeValues(
5047
+ value,
5048
+ maxMetadataValueLength,
5049
+ maxPreviewLength,
5050
+ seen,
5051
+ depth + 1
5052
+ );
5053
+ }
5054
+ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPreviewLength) {
5055
+ if (!attrs || Object.keys(attrs).length === 0) {
5056
+ return attrs;
5057
+ }
5058
+ const redacted = redactor.redactRecord(attrs);
5059
+ const seen = /* @__PURE__ */ new WeakSet();
5060
+ const bounded = boundAttributeValues(
5061
+ redacted,
5062
+ maxMetadataValueLength,
5063
+ maxPreviewLength,
5064
+ seen,
5065
+ 0
5066
+ );
5067
+ const err = bounded.error;
5068
+ if (isRecord10(err) && typeof err.message === "string") {
5069
+ bounded.error = {
5070
+ ...err,
5071
+ message: truncateStringForProfile(
5072
+ err.message,
5073
+ "message",
5074
+ maxMetadataValueLength,
5075
+ maxPreviewLength
5076
+ ),
5077
+ ...typeof err.stack === "string" ? {
5078
+ stack: truncateStringForProfile(
5079
+ err.stack,
5080
+ "stack",
5081
+ maxMetadataValueLength,
5082
+ maxPreviewLength
5083
+ )
5084
+ } : {}
5085
+ };
5086
+ }
5087
+ return bounded;
5088
+ }
5089
+ function redactRunTreeForExport(tree, options) {
5090
+ const profile = options?.redactionProfile ?? "local";
5091
+ if (profile === "local") {
5092
+ return deepClone(tree);
5093
+ }
5094
+ const resolved = resolveRedactionProfile(profile);
5095
+ const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
5096
+ 2e3,
5097
+ 500,
5098
+ resolved
5099
+ );
5100
+ const redactor = new Redactor({ extraKeys: resolved.extraKeys });
5101
+ const clone = deepClone(tree);
5102
+ function walk(nodes) {
5103
+ for (const node of nodes) {
5104
+ if (node.event.attributes !== void 0) {
5105
+ node.event.attributes = redactEventAttributes(
5106
+ node.event.attributes,
5107
+ redactor,
5108
+ maxMetadataValueLength,
5109
+ maxPreviewLength
5110
+ );
5111
+ }
5112
+ if (node.children.length > 0) {
5113
+ walk(node.children);
5114
+ }
5115
+ }
5116
+ }
5117
+ walk(clone.children);
5118
+ return clone;
5119
+ }
5120
+
4257
5121
  // packages/core/src/exporters/html-exporter.ts
4258
5122
  function renderTreeHtml(nodes, ulClass = "tree") {
4259
5123
  if (nodes.length === 0) return "";
@@ -4990,20 +5854,22 @@ function mergeExportDefaults(options) {
4990
5854
  includeErrors: options.includeErrors ?? true,
4991
5855
  pretty: options.pretty ?? true,
4992
5856
  redacted: options.redacted ?? true,
4993
- maxAttributeLength: options.maxAttributeLength ?? 500
5857
+ maxAttributeLength: options.maxAttributeLength ?? 500,
5858
+ redactionProfile: options.redactionProfile ?? "local"
4994
5859
  };
4995
5860
  }
4996
5861
  function exportRunTree(tree, options) {
4997
5862
  const opts = mergeExportDefaults(options);
5863
+ const exportTree = opts.redactionProfile === "local" ? tree : redactRunTreeForExport(tree, { redactionProfile: opts.redactionProfile });
4998
5864
  switch (opts.format) {
4999
5865
  case "markdown":
5000
- return exportMarkdown(tree, opts);
5866
+ return exportMarkdown(exportTree, opts);
5001
5867
  case "html":
5002
- return exportHtml(tree, opts);
5868
+ return exportHtml(exportTree, opts);
5003
5869
  case "openinference":
5004
- return exportOpenInference(tree, opts);
5870
+ return exportOpenInference(exportTree, opts);
5005
5871
  case "otlp-json":
5006
- return exportOtlpJson(tree, opts);
5872
+ return exportOtlpJson(exportTree, opts);
5007
5873
  default: {
5008
5874
  const _x = opts.format;
5009
5875
  throw new Error(`Unsupported export format: ${String(_x)}`);
@@ -5041,6 +5907,8 @@ exports.TERMINAL_INDENT = TERMINAL_INDENT;
5041
5907
  exports.TraceDirectory = TraceDirectory;
5042
5908
  exports.TreeBuilder = TreeBuilder;
5043
5909
  exports.buildRunSummary = buildRunSummary;
5910
+ exports.buildRunTimeline = buildRunTimeline;
5911
+ exports.buildTraceStats = buildTraceStats;
5044
5912
  exports.compactAttributes = compactAttributes4;
5045
5913
  exports.createRunId = createRunId;
5046
5914
  exports.createStepId = createStepId;
@@ -5062,6 +5930,7 @@ exports.formatError = formatError;
5062
5930
  exports.formatTerminalName = formatTerminalName;
5063
5931
  exports.formatTimestamp = formatTimestamp;
5064
5932
  exports.getCurrentContext = getCurrentContext;
5933
+ exports.getCurrentCorrelationMetadata = getCurrentCorrelationMetadata;
5065
5934
  exports.getCurrentDepth = getCurrentDepth;
5066
5935
  exports.getCurrentRunId = getCurrentRunId;
5067
5936
  exports.getCurrentRunName = getCurrentRunName;
@@ -5087,6 +5956,7 @@ exports.isStepType = isStepType;
5087
5956
  exports.isTraceEvent = isTraceEvent;
5088
5957
  exports.listTraceFiles = listTraceFiles;
5089
5958
  exports.loadLogIngestConfig = loadLogIngestConfig;
5959
+ exports.loadTraceMetadataList = loadTraceMetadataList;
5090
5960
  exports.manualTraceEventsToComparableRun = manualTraceEventsToComparableRun;
5091
5961
  exports.manualTraceEventsToRunTree = manualTraceEventsToRunTree;
5092
5962
  exports.matchMapping = matchMapping;
@@ -5095,6 +5965,7 @@ exports.mergeExportDefaults = mergeExportDefaults;
5095
5965
  exports.mergeLogIngestConfig = mergeLogIngestConfig;
5096
5966
  exports.observe = observe;
5097
5967
  exports.parseDuration = parseDuration;
5968
+ exports.parseDurationFilter = parseDurationFilter;
5098
5969
  exports.parseLogLine = parseLogLine;
5099
5970
  exports.parseLogsToTrees = parseLogsToTrees;
5100
5971
  exports.persistedInspectEventToInspectEvent = persistedInspectEventToInspectEvent;
@@ -5110,17 +5981,22 @@ exports.printStepComplete = printStepComplete;
5110
5981
  exports.printStepStart = printStepStart;
5111
5982
  exports.readTraceEvents = readTraceEvents;
5112
5983
  exports.readTraceFile = readTraceFile;
5984
+ exports.redactRunTreeForExport = redactRunTreeForExport;
5113
5985
  exports.renderErrorLine = renderErrorLine;
5114
5986
  exports.renderRunDiff = renderRunDiff;
5115
5987
  exports.renderRunSummary = renderRunSummary;
5116
5988
  exports.renderRunTree = renderRunTree;
5117
5989
  exports.renderRunTrees = renderRunTrees;
5118
5990
  exports.renderStepLine = renderStepLine;
5991
+ exports.renderTimeline = renderTimeline;
5992
+ exports.renderTraceStats = renderTraceStats;
5993
+ exports.resolveRedactionProfile = resolveRedactionProfile;
5119
5994
  exports.resolveTraceDir = resolveTraceDir;
5120
5995
  exports.resolveTraceSafetyOptions = resolveTraceSafetyOptions;
5121
5996
  exports.runWithContext = runWithContext;
5122
5997
  exports.runWithStepContext = runWithStepContext;
5123
5998
  exports.safeString = safeString2;
5999
+ exports.searchTraces = searchTraces;
5124
6000
  exports.serializeEvent = serializeEvent;
5125
6001
  exports.stableJson = stableJson;
5126
6002
  exports.step = step;