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.
- package/CHANGELOG.md +41 -4
- package/README.md +41 -38
- package/docs/ADAPTERS.md +122 -21
- package/docs/API.md +25 -7
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CLI.md +71 -2
- package/docs/EXPORTS.md +80 -8
- package/docs/GETTING-STARTED.md +29 -1
- package/docs/KNOWN-ISSUES.md +6 -0
- package/docs/LIMITATIONS.md +17 -0
- package/docs/SCHEMA.md +6 -4
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +1019 -14
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +1019 -14
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/core/dist/index.cjs +890 -14
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.d.cts +176 -3
- package/packages/core/dist/index.d.ts +176 -3
- package/packages/core/dist/index.mjs +881 -15
- package/packages/core/dist/index.mjs.map +1 -1
|
@@ -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
|
|
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
|
-
|
|
2102
|
-
|
|
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 =
|
|
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({
|
|
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:
|
|
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
|
-
...
|
|
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(
|
|
5866
|
+
return exportMarkdown(exportTree, opts);
|
|
5001
5867
|
case "html":
|
|
5002
|
-
return exportHtml(
|
|
5868
|
+
return exportHtml(exportTree, opts);
|
|
5003
5869
|
case "openinference":
|
|
5004
|
-
return exportOpenInference(
|
|
5870
|
+
return exportOpenInference(exportTree, opts);
|
|
5005
5871
|
case "otlp-json":
|
|
5006
|
-
return exportOtlpJson(
|
|
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;
|