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
|
@@ -183,6 +183,51 @@ function isPersistedInspectEvent(value) {
|
|
|
183
183
|
return true;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
// packages/core/src/correlation-metadata.ts
|
|
187
|
+
var TRACE_CORRELATION_KEYS = [
|
|
188
|
+
"correlationId",
|
|
189
|
+
"requestId",
|
|
190
|
+
"decisionId",
|
|
191
|
+
"groupId"
|
|
192
|
+
];
|
|
193
|
+
function isNonEmptyString2(value) {
|
|
194
|
+
return typeof value === "string" && value.length > 0;
|
|
195
|
+
}
|
|
196
|
+
function extractCorrelationMetadata(record) {
|
|
197
|
+
if (!record) {
|
|
198
|
+
return void 0;
|
|
199
|
+
}
|
|
200
|
+
const out = {};
|
|
201
|
+
let found = false;
|
|
202
|
+
for (const key of TRACE_CORRELATION_KEYS) {
|
|
203
|
+
const value = record[key];
|
|
204
|
+
if (isNonEmptyString2(value)) {
|
|
205
|
+
out[key] = value;
|
|
206
|
+
found = true;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return found ? out : void 0;
|
|
210
|
+
}
|
|
211
|
+
function buildRunStartedMetadata(options) {
|
|
212
|
+
if (!options) {
|
|
213
|
+
return void 0;
|
|
214
|
+
}
|
|
215
|
+
const merged = options.metadata !== void 0 ? { ...options.metadata } : {};
|
|
216
|
+
if (isNonEmptyString2(options.correlationId)) {
|
|
217
|
+
merged.correlationId = options.correlationId;
|
|
218
|
+
}
|
|
219
|
+
if (isNonEmptyString2(options.requestId)) {
|
|
220
|
+
merged.requestId = options.requestId;
|
|
221
|
+
}
|
|
222
|
+
if (isNonEmptyString2(options.decisionId)) {
|
|
223
|
+
merged.decisionId = options.decisionId;
|
|
224
|
+
}
|
|
225
|
+
if (isNonEmptyString2(options.groupId)) {
|
|
226
|
+
merged.groupId = options.groupId;
|
|
227
|
+
}
|
|
228
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
186
231
|
// packages/core/src/persisted/from-trace-event.ts
|
|
187
232
|
function sanitizeIdPart(value) {
|
|
188
233
|
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
@@ -289,9 +334,14 @@ function traceEventToPersistedInspectEvent(event, options) {
|
|
|
289
334
|
switch (event.event) {
|
|
290
335
|
case "run_started": {
|
|
291
336
|
const tsStart = toIsoTimestamp(event.startTime);
|
|
337
|
+
const correlation = extractCorrelationMetadata(event.metadata);
|
|
292
338
|
const attributes = compactAttributes({
|
|
293
339
|
legacyEvent: "run_started",
|
|
294
340
|
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
341
|
+
correlationId: correlation?.correlationId,
|
|
342
|
+
requestId: correlation?.requestId,
|
|
343
|
+
decisionId: correlation?.decisionId,
|
|
344
|
+
groupId: correlation?.groupId,
|
|
295
345
|
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
296
346
|
});
|
|
297
347
|
return {
|
|
@@ -1128,7 +1178,7 @@ function stableHash(value) {
|
|
|
1128
1178
|
const h = crypto.createHash("sha256").update(value, "utf8").digest("hex");
|
|
1129
1179
|
return h.slice(0, 8);
|
|
1130
1180
|
}
|
|
1131
|
-
function compileRules(rules) {
|
|
1181
|
+
function compileRules(rules, extraKeys) {
|
|
1132
1182
|
const out = /* @__PURE__ */ new Map();
|
|
1133
1183
|
const set = (r) => {
|
|
1134
1184
|
const k = toKey(r.key);
|
|
@@ -1137,6 +1187,11 @@ function compileRules(rules) {
|
|
|
1137
1187
|
for (const k of DEFAULT_REDACT_KEYS) {
|
|
1138
1188
|
set({ key: k, strategy: "full" });
|
|
1139
1189
|
}
|
|
1190
|
+
for (const k of extraKeys ?? []) {
|
|
1191
|
+
if (typeof k === "string" && k.length > 0) {
|
|
1192
|
+
set({ key: k, strategy: "full" });
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1140
1195
|
for (const r of rules ?? []) {
|
|
1141
1196
|
if (typeof r === "string") {
|
|
1142
1197
|
set({ key: r, strategy: "full" });
|
|
@@ -1154,7 +1209,7 @@ function compileRules(rules) {
|
|
|
1154
1209
|
var Redactor = class {
|
|
1155
1210
|
#rules;
|
|
1156
1211
|
constructor(options) {
|
|
1157
|
-
this.#rules = compileRules(options?.rules);
|
|
1212
|
+
this.#rules = compileRules(options?.rules, options?.extraKeys);
|
|
1158
1213
|
}
|
|
1159
1214
|
redactValue(key, value) {
|
|
1160
1215
|
const k = toKey(key);
|
|
@@ -1859,6 +1914,91 @@ function warn(message, error) {
|
|
|
1859
1914
|
}
|
|
1860
1915
|
console.warn(`${base}: ${formatError(error).message}`);
|
|
1861
1916
|
}
|
|
1917
|
+
|
|
1918
|
+
// packages/core/src/redaction-profiles.ts
|
|
1919
|
+
var SHARE_PROFILE_EXTRA_KEYS = [
|
|
1920
|
+
"userEmail",
|
|
1921
|
+
"customerEmail",
|
|
1922
|
+
"phone",
|
|
1923
|
+
"phoneNumber",
|
|
1924
|
+
"address",
|
|
1925
|
+
"ip",
|
|
1926
|
+
"ipAddress",
|
|
1927
|
+
"sessionId",
|
|
1928
|
+
"requestId",
|
|
1929
|
+
"correlationId",
|
|
1930
|
+
"decisionId",
|
|
1931
|
+
"groupId",
|
|
1932
|
+
"customerId",
|
|
1933
|
+
"userId",
|
|
1934
|
+
"accountId",
|
|
1935
|
+
"tenantId",
|
|
1936
|
+
"orgId",
|
|
1937
|
+
"organizationId",
|
|
1938
|
+
"traceId",
|
|
1939
|
+
"spanId",
|
|
1940
|
+
"parentSpanId"
|
|
1941
|
+
];
|
|
1942
|
+
var STRICT_PROFILE_EXTRA_KEYS = [
|
|
1943
|
+
"prompt",
|
|
1944
|
+
"completion",
|
|
1945
|
+
"input",
|
|
1946
|
+
"output",
|
|
1947
|
+
"inputPreview",
|
|
1948
|
+
"outputPreview",
|
|
1949
|
+
"message",
|
|
1950
|
+
"messages",
|
|
1951
|
+
"transcript",
|
|
1952
|
+
"context",
|
|
1953
|
+
"document",
|
|
1954
|
+
"documents",
|
|
1955
|
+
"chunk",
|
|
1956
|
+
"chunks",
|
|
1957
|
+
"retrieval",
|
|
1958
|
+
"query"
|
|
1959
|
+
];
|
|
1960
|
+
function resolveRedactionProfile(profile = "local") {
|
|
1961
|
+
switch (profile) {
|
|
1962
|
+
case "local":
|
|
1963
|
+
return { profile: "local", extraKeys: [] };
|
|
1964
|
+
case "share":
|
|
1965
|
+
return {
|
|
1966
|
+
profile: "share",
|
|
1967
|
+
extraKeys: SHARE_PROFILE_EXTRA_KEYS,
|
|
1968
|
+
maxMetadataValueLengthCap: 500,
|
|
1969
|
+
maxPreviewLengthCap: 200
|
|
1970
|
+
};
|
|
1971
|
+
case "strict":
|
|
1972
|
+
return {
|
|
1973
|
+
profile: "strict",
|
|
1974
|
+
extraKeys: [...SHARE_PROFILE_EXTRA_KEYS, ...STRICT_PROFILE_EXTRA_KEYS],
|
|
1975
|
+
maxMetadataValueLengthCap: 200,
|
|
1976
|
+
maxPreviewLengthCap: 80
|
|
1977
|
+
};
|
|
1978
|
+
default:
|
|
1979
|
+
return { profile: "local", extraKeys: [] };
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
function isPreviewKey(key) {
|
|
1983
|
+
return key.toLowerCase().includes("preview");
|
|
1984
|
+
}
|
|
1985
|
+
function applyProfileMetadataCaps(maxMetadataValueLength, maxPreviewLength, resolved) {
|
|
1986
|
+
let meta = maxMetadataValueLength;
|
|
1987
|
+
let preview = maxPreviewLength;
|
|
1988
|
+
if (resolved.maxMetadataValueLengthCap !== void 0) {
|
|
1989
|
+
meta = Math.min(meta, resolved.maxMetadataValueLengthCap);
|
|
1990
|
+
}
|
|
1991
|
+
if (resolved.maxPreviewLengthCap !== void 0) {
|
|
1992
|
+
preview = Math.min(preview, resolved.maxPreviewLengthCap);
|
|
1993
|
+
}
|
|
1994
|
+
return { maxMetadataValueLength: meta, maxPreviewLength: preview };
|
|
1995
|
+
}
|
|
1996
|
+
function truncateStringForProfile(value, key, maxMetadataValueLength, maxPreviewLength) {
|
|
1997
|
+
const max = isPreviewKey(key) ? maxPreviewLength : maxMetadataValueLength;
|
|
1998
|
+
if (max <= 0) return "\u2026";
|
|
1999
|
+
if (value.length <= max) return value;
|
|
2000
|
+
return `${value.slice(0, max)}\u2026`;
|
|
2001
|
+
}
|
|
1862
2002
|
function isRecord7(value) {
|
|
1863
2003
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1864
2004
|
}
|
|
@@ -2062,7 +2202,7 @@ var DEFAULT_MAX_EVENT_BYTES = 65536;
|
|
|
2062
2202
|
function isRecord8(value) {
|
|
2063
2203
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2064
2204
|
}
|
|
2065
|
-
function
|
|
2205
|
+
function isPreviewKey2(key) {
|
|
2066
2206
|
return key.toLowerCase().includes("preview");
|
|
2067
2207
|
}
|
|
2068
2208
|
function truncateString(value, maxLen) {
|
|
@@ -2085,11 +2225,28 @@ function resolveTraceSafetyOptions(options) {
|
|
|
2085
2225
|
redactEnabled = true;
|
|
2086
2226
|
redactionRules = redact.rules;
|
|
2087
2227
|
}
|
|
2228
|
+
const profile = options?.redactionProfile ?? "local";
|
|
2229
|
+
const resolvedProfile = resolveRedactionProfile(profile);
|
|
2230
|
+
const userMaxMetadata = typeof options?.maxMetadataValueLength === "number" && Number.isFinite(options.maxMetadataValueLength) && options.maxMetadataValueLength >= 0 ? Math.floor(options.maxMetadataValueLength) : void 0;
|
|
2231
|
+
const userMaxPreview = typeof options?.maxPreviewLength === "number" && Number.isFinite(options.maxPreviewLength) && options.maxPreviewLength >= 0 ? Math.floor(options.maxPreviewLength) : void 0;
|
|
2232
|
+
let maxMetadataValueLength = userMaxMetadata ?? DEFAULT_MAX_METADATA_VALUE_LENGTH;
|
|
2233
|
+
let maxPreviewLength = userMaxPreview ?? DEFAULT_MAX_PREVIEW_LENGTH;
|
|
2234
|
+
if (redactEnabled && profile !== "local") {
|
|
2235
|
+
const capped = applyProfileMetadataCaps(
|
|
2236
|
+
maxMetadataValueLength,
|
|
2237
|
+
maxPreviewLength,
|
|
2238
|
+
resolvedProfile
|
|
2239
|
+
);
|
|
2240
|
+
maxMetadataValueLength = capped.maxMetadataValueLength;
|
|
2241
|
+
maxPreviewLength = capped.maxPreviewLength;
|
|
2242
|
+
}
|
|
2088
2243
|
return {
|
|
2089
2244
|
redactEnabled,
|
|
2090
2245
|
redactionRules,
|
|
2091
|
-
|
|
2092
|
-
|
|
2246
|
+
redactionProfile: profile,
|
|
2247
|
+
profileExtraKeys: redactEnabled ? resolvedProfile.extraKeys : [],
|
|
2248
|
+
maxMetadataValueLength,
|
|
2249
|
+
maxPreviewLength,
|
|
2093
2250
|
maxEventBytes: typeof options?.maxEventBytes === "number" && Number.isFinite(options.maxEventBytes) && options.maxEventBytes > 0 ? Math.floor(options.maxEventBytes) : DEFAULT_MAX_EVENT_BYTES
|
|
2094
2251
|
};
|
|
2095
2252
|
}
|
|
@@ -2097,7 +2254,7 @@ function boundMetadataValue(key, value, opts, seen, depth) {
|
|
|
2097
2254
|
if (depth > 32) return "[MaxDepth]";
|
|
2098
2255
|
if (value === null || typeof value !== "object") {
|
|
2099
2256
|
if (typeof value === "string") {
|
|
2100
|
-
const max =
|
|
2257
|
+
const max = isPreviewKey2(key) ? opts.maxPreviewLength : opts.maxMetadataValueLength;
|
|
2101
2258
|
return truncateString(value, max);
|
|
2102
2259
|
}
|
|
2103
2260
|
return value;
|
|
@@ -2123,7 +2280,10 @@ function boundMetadataValue(key, value, opts, seen, depth) {
|
|
|
2123
2280
|
}
|
|
2124
2281
|
function redactMetadata(metadata, opts) {
|
|
2125
2282
|
if (!opts.redactEnabled) return { ...metadata };
|
|
2126
|
-
const redactor = new Redactor({
|
|
2283
|
+
const redactor = new Redactor({
|
|
2284
|
+
rules: opts.redactionRules,
|
|
2285
|
+
extraKeys: opts.profileExtraKeys
|
|
2286
|
+
});
|
|
2127
2287
|
return redactor.redactRecord(metadata);
|
|
2128
2288
|
}
|
|
2129
2289
|
function prepareMetadataForDisk(metadata, opts) {
|
|
@@ -2295,6 +2455,13 @@ function getCurrentRunId() {
|
|
|
2295
2455
|
return void 0;
|
|
2296
2456
|
}
|
|
2297
2457
|
}
|
|
2458
|
+
function getCurrentCorrelationMetadata() {
|
|
2459
|
+
try {
|
|
2460
|
+
return extractCorrelationMetadata(storage.getStore()?.metadata);
|
|
2461
|
+
} catch {
|
|
2462
|
+
return void 0;
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2298
2465
|
function getCurrentRunName() {
|
|
2299
2466
|
try {
|
|
2300
2467
|
return storage.getStore()?.runName;
|
|
@@ -2657,6 +2824,574 @@ function filterTraces(traces, options) {
|
|
|
2657
2824
|
}
|
|
2658
2825
|
return out;
|
|
2659
2826
|
}
|
|
2827
|
+
|
|
2828
|
+
// packages/core/src/timeline.ts
|
|
2829
|
+
function finite(n) {
|
|
2830
|
+
return typeof n === "number" && Number.isFinite(n);
|
|
2831
|
+
}
|
|
2832
|
+
function pickStreamingMeta(metadata) {
|
|
2833
|
+
if (!metadata || typeof metadata !== "object") return void 0;
|
|
2834
|
+
const chunkCount = metadata.chunkCount;
|
|
2835
|
+
const streamDurationMs = metadata.streamDurationMs;
|
|
2836
|
+
const streamedCharCount = metadata.streamedCharCount;
|
|
2837
|
+
if (!finite(chunkCount) && !finite(streamDurationMs) && !finite(streamedCharCount)) {
|
|
2838
|
+
return void 0;
|
|
2839
|
+
}
|
|
2840
|
+
return {
|
|
2841
|
+
...finite(chunkCount) ? { chunkCount } : {},
|
|
2842
|
+
...finite(streamDurationMs) ? { streamDurationMs } : {},
|
|
2843
|
+
...finite(streamedCharCount) ? { streamedCharCount } : {}
|
|
2844
|
+
};
|
|
2845
|
+
}
|
|
2846
|
+
function pickCorrelation(metadata) {
|
|
2847
|
+
if (!metadata || typeof metadata !== "object") return void 0;
|
|
2848
|
+
const out = {};
|
|
2849
|
+
for (const key of [
|
|
2850
|
+
"correlationId",
|
|
2851
|
+
"requestId",
|
|
2852
|
+
"decisionId",
|
|
2853
|
+
"groupId"
|
|
2854
|
+
]) {
|
|
2855
|
+
const v = metadata[key];
|
|
2856
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
2857
|
+
out[key] = v;
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
2861
|
+
}
|
|
2862
|
+
function buildRunTimeline(events, options = {}) {
|
|
2863
|
+
const started = events.find(
|
|
2864
|
+
(e) => e.event === "run_started"
|
|
2865
|
+
);
|
|
2866
|
+
const completed = events.filter(
|
|
2867
|
+
(e) => e.event === "run_completed"
|
|
2868
|
+
);
|
|
2869
|
+
const lastCompleted = completed[completed.length - 1];
|
|
2870
|
+
const runId = started?.runId ?? events.find((e) => typeof e.runId === "string")?.runId ?? "unknown-run";
|
|
2871
|
+
const runStart = started && finite(started.startTime) ? started.startTime : started && finite(started.timestamp) ? started.timestamp : void 0;
|
|
2872
|
+
const status = lastCompleted ? lastCompleted.status : started ? "running" : "unknown";
|
|
2873
|
+
const steps = /* @__PURE__ */ new Map();
|
|
2874
|
+
for (const e of events) {
|
|
2875
|
+
if (e.event === "step_started") {
|
|
2876
|
+
const s = e;
|
|
2877
|
+
steps.set(s.stepId, {
|
|
2878
|
+
name: s.name,
|
|
2879
|
+
type: s.type,
|
|
2880
|
+
parentId: s.parentId,
|
|
2881
|
+
startedAt: finite(s.startTime) ? s.startTime : s.timestamp,
|
|
2882
|
+
status: "running",
|
|
2883
|
+
metadata: s.metadata
|
|
2884
|
+
});
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
for (const e of events) {
|
|
2888
|
+
if (e.event !== "step_completed") continue;
|
|
2889
|
+
const c = e;
|
|
2890
|
+
const node = steps.get(c.stepId);
|
|
2891
|
+
if (!node) continue;
|
|
2892
|
+
node.status = c.status;
|
|
2893
|
+
if (finite(c.durationMs)) node.durationMs = c.durationMs;
|
|
2894
|
+
}
|
|
2895
|
+
const depthCache = /* @__PURE__ */ new Map();
|
|
2896
|
+
const computeDepth = (stepId) => {
|
|
2897
|
+
const cached = depthCache.get(stepId);
|
|
2898
|
+
if (cached !== void 0) return cached;
|
|
2899
|
+
const node = steps.get(stepId);
|
|
2900
|
+
if (!node) return 0;
|
|
2901
|
+
const parent = node.parentId;
|
|
2902
|
+
if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
|
|
2903
|
+
depthCache.set(stepId, 0);
|
|
2904
|
+
return 0;
|
|
2905
|
+
}
|
|
2906
|
+
const d = Math.min(1e3, computeDepth(parent) + 1);
|
|
2907
|
+
depthCache.set(stepId, d);
|
|
2908
|
+
return d;
|
|
2909
|
+
};
|
|
2910
|
+
const entries = [];
|
|
2911
|
+
for (const [stepId, s] of steps.entries()) {
|
|
2912
|
+
const offsetMs = runStart !== void 0 && finite(s.startedAt) ? Math.max(0, s.startedAt - runStart) : 0;
|
|
2913
|
+
entries.push({
|
|
2914
|
+
stepId,
|
|
2915
|
+
name: s.name,
|
|
2916
|
+
type: s.type,
|
|
2917
|
+
status: s.status,
|
|
2918
|
+
depth: computeDepth(stepId),
|
|
2919
|
+
startedAt: s.startedAt,
|
|
2920
|
+
offsetMs,
|
|
2921
|
+
durationMs: s.durationMs,
|
|
2922
|
+
isError: s.status === "error",
|
|
2923
|
+
streaming: pickStreamingMeta(s.metadata)
|
|
2924
|
+
});
|
|
2925
|
+
}
|
|
2926
|
+
entries.sort((a, b) => a.startedAt - b.startedAt);
|
|
2927
|
+
const slowTopN = options.slowTopN ?? 3;
|
|
2928
|
+
if (options.focus === "slow" && entries.length > 0) {
|
|
2929
|
+
const ranked = [...entries].filter((e) => finite(e.durationMs)).sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0));
|
|
2930
|
+
const slowIds = new Set(
|
|
2931
|
+
ranked.slice(0, slowTopN).map((e) => e.stepId)
|
|
2932
|
+
);
|
|
2933
|
+
for (const e of entries) {
|
|
2934
|
+
if (slowIds.has(e.stepId)) e.slow = true;
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
return {
|
|
2938
|
+
runId,
|
|
2939
|
+
name: typeof started?.name === "string" && started.name.trim() !== "" ? started.name : void 0,
|
|
2940
|
+
status,
|
|
2941
|
+
startedAt: runStart,
|
|
2942
|
+
endedAt: lastCompleted && finite(lastCompleted.endTime) ? lastCompleted.endTime : void 0,
|
|
2943
|
+
durationMs: lastCompleted && finite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0,
|
|
2944
|
+
correlation: pickCorrelation(
|
|
2945
|
+
started?.metadata
|
|
2946
|
+
),
|
|
2947
|
+
entries
|
|
2948
|
+
};
|
|
2949
|
+
}
|
|
2950
|
+
function renderTimeline(timeline, options = {}) {
|
|
2951
|
+
const lines = [];
|
|
2952
|
+
lines.push(`Timeline: ${timeline.name ?? timeline.runId}`);
|
|
2953
|
+
lines.push(`Run ID: ${timeline.runId}`);
|
|
2954
|
+
lines.push(`Status: ${timeline.status}`);
|
|
2955
|
+
if (timeline.startedAt !== void 0) {
|
|
2956
|
+
lines.push(`Started: ${formatTimestamp(timeline.startedAt)}`);
|
|
2957
|
+
}
|
|
2958
|
+
if (timeline.durationMs !== void 0) {
|
|
2959
|
+
lines.push(`Duration: ${formatDuration2(timeline.durationMs)}`);
|
|
2960
|
+
}
|
|
2961
|
+
if (timeline.correlation) {
|
|
2962
|
+
const parts = Object.entries(timeline.correlation).filter(([, v]) => typeof v === "string").map(([k, v]) => `${k}=${v}`);
|
|
2963
|
+
if (parts.length > 0) {
|
|
2964
|
+
lines.push(`Correlation: ${parts.join(", ")}`);
|
|
2965
|
+
}
|
|
2966
|
+
}
|
|
2967
|
+
lines.push("");
|
|
2968
|
+
lines.push("Steps (chronological):");
|
|
2969
|
+
const show = timeline.entries.filter((e) => {
|
|
2970
|
+
if (options.focus === "slow") return e.slow === true;
|
|
2971
|
+
return true;
|
|
2972
|
+
});
|
|
2973
|
+
if (show.length === 0) {
|
|
2974
|
+
lines.push(
|
|
2975
|
+
options.focus === "slow" ? "(no steps with duration for slow focus)" : "(no steps)"
|
|
2976
|
+
);
|
|
2977
|
+
return lines.join("\n");
|
|
2978
|
+
}
|
|
2979
|
+
for (const e of show) {
|
|
2980
|
+
const prefix = e.slow ? "[slow] " : "";
|
|
2981
|
+
const typeTag = e.type === "llm" ? "llm" : e.type === "tool" ? "tool" : e.type;
|
|
2982
|
+
const dur = e.durationMs !== void 0 ? formatDuration2(e.durationMs) : "-";
|
|
2983
|
+
const err = e.isError ? " error" : "";
|
|
2984
|
+
const off = formatDuration2(e.offsetMs);
|
|
2985
|
+
let line = `${prefix}+${off} ${typeTag}:${e.name} (${dur})${err}`;
|
|
2986
|
+
if (e.streaming?.chunkCount !== void 0) {
|
|
2987
|
+
line += ` chunks=${e.streaming.chunkCount}`;
|
|
2988
|
+
}
|
|
2989
|
+
if (e.streaming?.streamDurationMs !== void 0) {
|
|
2990
|
+
line += ` stream=${formatDuration2(e.streaming.streamDurationMs)}`;
|
|
2991
|
+
}
|
|
2992
|
+
lines.push(line);
|
|
2993
|
+
}
|
|
2994
|
+
return lines.join("\n");
|
|
2995
|
+
}
|
|
2996
|
+
function percentile(sorted, p) {
|
|
2997
|
+
if (sorted.length === 0) return void 0;
|
|
2998
|
+
const idx = Math.min(
|
|
2999
|
+
sorted.length - 1,
|
|
3000
|
+
Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
|
|
3001
|
+
);
|
|
3002
|
+
return sorted[idx];
|
|
3003
|
+
}
|
|
3004
|
+
async function readRunStartedMetadata(filePath) {
|
|
3005
|
+
try {
|
|
3006
|
+
const raw = await readFile(filePath, "utf-8");
|
|
3007
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
3008
|
+
const trimmed = line.trim();
|
|
3009
|
+
if (trimmed === "") continue;
|
|
3010
|
+
let parsed;
|
|
3011
|
+
try {
|
|
3012
|
+
parsed = JSON.parse(trimmed);
|
|
3013
|
+
} catch {
|
|
3014
|
+
continue;
|
|
3015
|
+
}
|
|
3016
|
+
if (!isTraceEvent(parsed)) continue;
|
|
3017
|
+
if (parsed.event !== "run_started") continue;
|
|
3018
|
+
const rs = parsed;
|
|
3019
|
+
if (rs.metadata && typeof rs.metadata === "object") {
|
|
3020
|
+
return rs.metadata;
|
|
3021
|
+
}
|
|
3022
|
+
return void 0;
|
|
3023
|
+
}
|
|
3024
|
+
} catch {
|
|
3025
|
+
}
|
|
3026
|
+
return void 0;
|
|
3027
|
+
}
|
|
3028
|
+
function metaMatchesCorrelation(metadata, correlationId, groupId) {
|
|
3029
|
+
if (correlationId) {
|
|
3030
|
+
const v = metadata?.correlationId;
|
|
3031
|
+
if (typeof v !== "string" || v !== correlationId) return false;
|
|
3032
|
+
}
|
|
3033
|
+
if (groupId) {
|
|
3034
|
+
const v = metadata?.groupId;
|
|
3035
|
+
if (typeof v !== "string" || v !== groupId) return false;
|
|
3036
|
+
}
|
|
3037
|
+
return true;
|
|
3038
|
+
}
|
|
3039
|
+
async function buildTraceStats(metas, options) {
|
|
3040
|
+
let filtered = filterTraces(metas, { since: options.since });
|
|
3041
|
+
if (options.correlationId || options.groupId) {
|
|
3042
|
+
const next = [];
|
|
3043
|
+
for (const m of filtered) {
|
|
3044
|
+
const md = await readRunStartedMetadata(m.filePath);
|
|
3045
|
+
if (metaMatchesCorrelation(md, options.correlationId, options.groupId)) {
|
|
3046
|
+
next.push(m);
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
filtered = next;
|
|
3050
|
+
}
|
|
3051
|
+
let successCount = 0;
|
|
3052
|
+
let errorCount = 0;
|
|
3053
|
+
let runningCount = 0;
|
|
3054
|
+
let unknownCount = 0;
|
|
3055
|
+
const durations = [];
|
|
3056
|
+
let totalSteps = 0;
|
|
3057
|
+
let totalLlmSteps = 0;
|
|
3058
|
+
let totalToolSteps = 0;
|
|
3059
|
+
let totalErrorSteps = 0;
|
|
3060
|
+
const slowestRuns = [];
|
|
3061
|
+
const slowestSteps = [];
|
|
3062
|
+
for (const m of filtered) {
|
|
3063
|
+
if (m.status === "success") successCount += 1;
|
|
3064
|
+
else if (m.status === "error") errorCount += 1;
|
|
3065
|
+
else if (m.status === "running") runningCount += 1;
|
|
3066
|
+
else unknownCount += 1;
|
|
3067
|
+
if (typeof m.durationMs === "number" && Number.isFinite(m.durationMs) && m.durationMs >= 0) {
|
|
3068
|
+
durations.push(m.durationMs);
|
|
3069
|
+
slowestRuns.push({
|
|
3070
|
+
runId: m.runId,
|
|
3071
|
+
name: m.name,
|
|
3072
|
+
durationMs: m.durationMs,
|
|
3073
|
+
status: m.status
|
|
3074
|
+
});
|
|
3075
|
+
}
|
|
3076
|
+
try {
|
|
3077
|
+
const events = await readTraceEvents(m.runId, options.traceDir);
|
|
3078
|
+
if (events.length === 0) continue;
|
|
3079
|
+
const summary = buildRunSummary(events);
|
|
3080
|
+
totalSteps += summary.totalSteps;
|
|
3081
|
+
totalLlmSteps += summary.llmSteps;
|
|
3082
|
+
totalToolSteps += summary.toolSteps;
|
|
3083
|
+
totalErrorSteps += summary.errorSteps;
|
|
3084
|
+
const steps = collectCompletedSteps(events, m.runId);
|
|
3085
|
+
for (const s of steps) {
|
|
3086
|
+
slowestSteps.push(s);
|
|
3087
|
+
}
|
|
3088
|
+
} catch {
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
slowestRuns.sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0));
|
|
3092
|
+
slowestSteps.sort((a, b) => b.durationMs - a.durationMs);
|
|
3093
|
+
const runLimit = options.slowRunLimit ?? 5;
|
|
3094
|
+
const stepLimit = options.slowStepLimit ?? 5;
|
|
3095
|
+
const sortedDur = [...durations].sort((a, b) => a - b);
|
|
3096
|
+
const totalRuns = filtered.length;
|
|
3097
|
+
const errorRate = totalRuns > 0 ? errorCount / totalRuns : 0;
|
|
3098
|
+
const sumDur = durations.reduce((a, b) => a + b, 0);
|
|
3099
|
+
return {
|
|
3100
|
+
traceDir: options.traceDir,
|
|
3101
|
+
...options.since ? { since: options.since } : {},
|
|
3102
|
+
...options.correlationId ? { correlationId: options.correlationId } : {},
|
|
3103
|
+
...options.groupId ? { groupId: options.groupId } : {},
|
|
3104
|
+
totalRuns,
|
|
3105
|
+
successCount,
|
|
3106
|
+
errorCount,
|
|
3107
|
+
runningCount,
|
|
3108
|
+
unknownCount,
|
|
3109
|
+
errorRate,
|
|
3110
|
+
duration: {
|
|
3111
|
+
...sortedDur.length > 0 ? {
|
|
3112
|
+
minMs: sortedDur[0],
|
|
3113
|
+
maxMs: sortedDur[sortedDur.length - 1],
|
|
3114
|
+
avgMs: sumDur / sortedDur.length,
|
|
3115
|
+
p50Ms: percentile(sortedDur, 50),
|
|
3116
|
+
p95Ms: percentile(sortedDur, 95)
|
|
3117
|
+
} : {}
|
|
3118
|
+
},
|
|
3119
|
+
totalSteps,
|
|
3120
|
+
avgStepsPerRun: totalRuns > 0 ? totalSteps / totalRuns : 0,
|
|
3121
|
+
totalLlmSteps,
|
|
3122
|
+
totalToolSteps,
|
|
3123
|
+
totalErrorSteps,
|
|
3124
|
+
slowestRuns: slowestRuns.slice(0, runLimit),
|
|
3125
|
+
slowestSteps: slowestSteps.slice(0, stepLimit)
|
|
3126
|
+
};
|
|
3127
|
+
}
|
|
3128
|
+
function collectCompletedSteps(events, runId) {
|
|
3129
|
+
const started = /* @__PURE__ */ new Map();
|
|
3130
|
+
const out = [];
|
|
3131
|
+
for (const e of events) {
|
|
3132
|
+
if (e.event === "step_started") {
|
|
3133
|
+
const s = e;
|
|
3134
|
+
started.set(s.stepId, { name: s.name, type: s.type });
|
|
3135
|
+
}
|
|
3136
|
+
if (e.event === "step_completed") {
|
|
3137
|
+
const c = e;
|
|
3138
|
+
if (c.status !== "success" && c.status !== "error") continue;
|
|
3139
|
+
if (typeof c.durationMs !== "number" || !Number.isFinite(c.durationMs)) {
|
|
3140
|
+
continue;
|
|
3141
|
+
}
|
|
3142
|
+
const meta = started.get(c.stepId);
|
|
3143
|
+
out.push({
|
|
3144
|
+
runId,
|
|
3145
|
+
stepName: meta?.name ?? c.stepId,
|
|
3146
|
+
stepType: meta?.type ?? "logic",
|
|
3147
|
+
durationMs: c.durationMs
|
|
3148
|
+
});
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
return out;
|
|
3152
|
+
}
|
|
3153
|
+
function renderTraceStats(stats) {
|
|
3154
|
+
const lines = [];
|
|
3155
|
+
lines.push("Trace stats (local)");
|
|
3156
|
+
lines.push(`Directory: ${stats.traceDir}`);
|
|
3157
|
+
if (stats.since) lines.push(`Since: ${stats.since}`);
|
|
3158
|
+
if (stats.correlationId) lines.push(`Correlation ID: ${stats.correlationId}`);
|
|
3159
|
+
if (stats.groupId) lines.push(`Group ID: ${stats.groupId}`);
|
|
3160
|
+
lines.push("");
|
|
3161
|
+
lines.push(`Runs: ${stats.totalRuns}`);
|
|
3162
|
+
lines.push(
|
|
3163
|
+
` success: ${stats.successCount} error: ${stats.errorCount} running: ${stats.runningCount} unknown: ${stats.unknownCount}`
|
|
3164
|
+
);
|
|
3165
|
+
lines.push(`Error rate: ${(stats.errorRate * 100).toFixed(1)}%`);
|
|
3166
|
+
if (stats.duration.avgMs !== void 0) {
|
|
3167
|
+
lines.push(
|
|
3168
|
+
`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)}`
|
|
3169
|
+
);
|
|
3170
|
+
}
|
|
3171
|
+
lines.push("");
|
|
3172
|
+
lines.push(`Steps: ${stats.totalSteps} (avg ${stats.avgStepsPerRun.toFixed(1)} per run)`);
|
|
3173
|
+
lines.push(
|
|
3174
|
+
` LLM: ${stats.totalLlmSteps} tool: ${stats.totalToolSteps} errors: ${stats.totalErrorSteps}`
|
|
3175
|
+
);
|
|
3176
|
+
if (stats.slowestRuns.length > 0) {
|
|
3177
|
+
lines.push("");
|
|
3178
|
+
lines.push("Slowest runs:");
|
|
3179
|
+
for (const r of stats.slowestRuns) {
|
|
3180
|
+
lines.push(
|
|
3181
|
+
` ${r.runId} | ${r.name ?? "-"} | ${formatDuration2(r.durationMs ?? 0)} | ${r.status}`
|
|
3182
|
+
);
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
if (stats.slowestSteps.length > 0) {
|
|
3186
|
+
lines.push("");
|
|
3187
|
+
lines.push("Slowest steps:");
|
|
3188
|
+
for (const s of stats.slowestSteps) {
|
|
3189
|
+
lines.push(
|
|
3190
|
+
` ${s.runId} | ${s.stepType}:${s.stepName} | ${formatDuration2(s.durationMs)}`
|
|
3191
|
+
);
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
return lines.join("\n");
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
// packages/core/src/search.ts
|
|
3198
|
+
function parseDurationFilter(expr) {
|
|
3199
|
+
const raw = expr.trim();
|
|
3200
|
+
const m = raw.match(/^(>=|<=|>|<)\s*(.+)$/);
|
|
3201
|
+
if (!m) {
|
|
3202
|
+
throw new Error(
|
|
3203
|
+
`Invalid --duration "${expr}". Use forms like >5s, >=500ms, <2m.`
|
|
3204
|
+
);
|
|
3205
|
+
}
|
|
3206
|
+
const op = m[1];
|
|
3207
|
+
const ms = parseDuration(m[2].trim());
|
|
3208
|
+
return { op, ms };
|
|
3209
|
+
}
|
|
3210
|
+
function durationMatches(valueMs, filter) {
|
|
3211
|
+
if (valueMs === void 0 || !Number.isFinite(valueMs)) return false;
|
|
3212
|
+
switch (filter.op) {
|
|
3213
|
+
case ">":
|
|
3214
|
+
return valueMs > filter.ms;
|
|
3215
|
+
case ">=":
|
|
3216
|
+
return valueMs >= filter.ms;
|
|
3217
|
+
case "<":
|
|
3218
|
+
return valueMs < filter.ms;
|
|
3219
|
+
case "<=":
|
|
3220
|
+
return valueMs <= filter.ms;
|
|
3221
|
+
default:
|
|
3222
|
+
return false;
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
function normalizeStepTypeFilter(kind, type) {
|
|
3226
|
+
const v = (kind ?? type)?.trim().toLowerCase();
|
|
3227
|
+
return v && v !== "" ? v : void 0;
|
|
3228
|
+
}
|
|
3229
|
+
function nameMatches(hay, needle) {
|
|
3230
|
+
return hay.toLowerCase().includes(needle.toLowerCase());
|
|
3231
|
+
}
|
|
3232
|
+
async function searchTraces(metas, options) {
|
|
3233
|
+
let filtered = filterTraces(metas, { since: options.since });
|
|
3234
|
+
const stepTypeFilter = normalizeStepTypeFilter(options.kind, options.type);
|
|
3235
|
+
const nameQuery = options.name?.trim();
|
|
3236
|
+
const toolQuery = options.tool?.trim();
|
|
3237
|
+
let durationFilter;
|
|
3238
|
+
if (options.duration) {
|
|
3239
|
+
durationFilter = parseDurationFilter(options.duration);
|
|
3240
|
+
}
|
|
3241
|
+
const limit = options.limit ?? 50;
|
|
3242
|
+
const hasContentFilter = Boolean(
|
|
3243
|
+
options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter
|
|
3244
|
+
);
|
|
3245
|
+
const results = [];
|
|
3246
|
+
if (!hasContentFilter) {
|
|
3247
|
+
for (const m of filtered) {
|
|
3248
|
+
results.push({
|
|
3249
|
+
runId: m.runId,
|
|
3250
|
+
runName: m.name,
|
|
3251
|
+
runStatus: m.status,
|
|
3252
|
+
timestamp: m.startedAt,
|
|
3253
|
+
durationMs: m.durationMs,
|
|
3254
|
+
matchReason: "trace in directory",
|
|
3255
|
+
matchedFields: ["run"],
|
|
3256
|
+
filePath: m.filePath
|
|
3257
|
+
});
|
|
3258
|
+
}
|
|
3259
|
+
return results.slice(0, limit);
|
|
3260
|
+
}
|
|
3261
|
+
for (const m of filtered) {
|
|
3262
|
+
if (options.status && m.status !== options.status) continue;
|
|
3263
|
+
let events = [];
|
|
3264
|
+
try {
|
|
3265
|
+
events = await readTraceEvents(m.runId, options.traceDir);
|
|
3266
|
+
} catch {
|
|
3267
|
+
continue;
|
|
3268
|
+
}
|
|
3269
|
+
if (events.length === 0) continue;
|
|
3270
|
+
const runMatches = matchRunLevel(m, {
|
|
3271
|
+
stepTypeFilter,
|
|
3272
|
+
nameQuery,
|
|
3273
|
+
toolQuery,
|
|
3274
|
+
durationFilter,
|
|
3275
|
+
statusFilter: options.status
|
|
3276
|
+
});
|
|
3277
|
+
results.push(...runMatches);
|
|
3278
|
+
const stepMatches = matchStepLevel(m, events, {
|
|
3279
|
+
stepTypeFilter,
|
|
3280
|
+
nameQuery,
|
|
3281
|
+
toolQuery,
|
|
3282
|
+
durationFilter,
|
|
3283
|
+
statusFilter: options.status
|
|
3284
|
+
});
|
|
3285
|
+
results.push(...stepMatches);
|
|
3286
|
+
}
|
|
3287
|
+
results.sort((a, b) => {
|
|
3288
|
+
const ta = a.timestamp ?? 0;
|
|
3289
|
+
const tb = b.timestamp ?? 0;
|
|
3290
|
+
if (ta !== tb) return ta - tb;
|
|
3291
|
+
const runCmp = a.runId.localeCompare(b.runId);
|
|
3292
|
+
if (runCmp !== 0) return runCmp;
|
|
3293
|
+
return (a.stepName ?? "").localeCompare(b.stepName ?? "");
|
|
3294
|
+
});
|
|
3295
|
+
return results.slice(0, limit);
|
|
3296
|
+
}
|
|
3297
|
+
function matchRunLevel(m, opts) {
|
|
3298
|
+
if (opts.stepTypeFilter || opts.toolQuery) return [];
|
|
3299
|
+
const out = [];
|
|
3300
|
+
const fields = [];
|
|
3301
|
+
if (opts.statusFilter && m.status === opts.statusFilter) {
|
|
3302
|
+
fields.push("run.status");
|
|
3303
|
+
}
|
|
3304
|
+
if (opts.nameQuery && nameMatches(m.name ?? m.runId, opts.nameQuery)) {
|
|
3305
|
+
fields.push("run.name");
|
|
3306
|
+
}
|
|
3307
|
+
if (opts.durationFilter && durationMatches(m.durationMs, opts.durationFilter)) {
|
|
3308
|
+
fields.push("run.durationMs");
|
|
3309
|
+
}
|
|
3310
|
+
if (fields.length === 0) return out;
|
|
3311
|
+
out.push({
|
|
3312
|
+
runId: m.runId,
|
|
3313
|
+
runName: m.name,
|
|
3314
|
+
runStatus: m.status,
|
|
3315
|
+
timestamp: m.startedAt,
|
|
3316
|
+
durationMs: m.durationMs,
|
|
3317
|
+
matchReason: `run match: ${fields.join(", ")}`,
|
|
3318
|
+
matchedFields: fields,
|
|
3319
|
+
filePath: m.filePath
|
|
3320
|
+
});
|
|
3321
|
+
return out;
|
|
3322
|
+
}
|
|
3323
|
+
function matchStepLevel(m, events, opts) {
|
|
3324
|
+
const out = [];
|
|
3325
|
+
const started = /* @__PURE__ */ new Map();
|
|
3326
|
+
for (const e of events) {
|
|
3327
|
+
if (e.event === "step_started") {
|
|
3328
|
+
started.set(e.stepId, e);
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
for (const e of events) {
|
|
3332
|
+
if (e.event !== "step_completed") continue;
|
|
3333
|
+
const c = e;
|
|
3334
|
+
const s = started.get(c.stepId);
|
|
3335
|
+
if (!s) continue;
|
|
3336
|
+
const fields = [];
|
|
3337
|
+
const stepType = s.type;
|
|
3338
|
+
if (opts.stepTypeFilter && stepType !== opts.stepTypeFilter) {
|
|
3339
|
+
continue;
|
|
3340
|
+
}
|
|
3341
|
+
const hasStepFilters = opts.stepTypeFilter || opts.nameQuery || opts.toolQuery || opts.durationFilter || opts.statusFilter === "error" || opts.statusFilter === "success";
|
|
3342
|
+
if (!hasStepFilters) continue;
|
|
3343
|
+
if (opts.statusFilter === "error" && c.status === "error") {
|
|
3344
|
+
fields.push("step.status");
|
|
3345
|
+
} else if (opts.statusFilter === "success" && c.status === "success") {
|
|
3346
|
+
fields.push("step.status");
|
|
3347
|
+
} else if (opts.statusFilter === "error" || opts.statusFilter === "success") {
|
|
3348
|
+
continue;
|
|
3349
|
+
}
|
|
3350
|
+
if (opts.nameQuery) {
|
|
3351
|
+
if (!nameMatches(s.name, opts.nameQuery)) continue;
|
|
3352
|
+
fields.push("step.name");
|
|
3353
|
+
}
|
|
3354
|
+
if (opts.toolQuery) {
|
|
3355
|
+
const toolName = typeof s.metadata?.toolName === "string" ? s.metadata.toolName : s.name;
|
|
3356
|
+
if (!nameMatches(toolName, opts.toolQuery)) continue;
|
|
3357
|
+
fields.push("step.tool");
|
|
3358
|
+
}
|
|
3359
|
+
if (opts.durationFilter) {
|
|
3360
|
+
if (!durationMatches(c.durationMs, opts.durationFilter)) continue;
|
|
3361
|
+
fields.push("step.durationMs");
|
|
3362
|
+
}
|
|
3363
|
+
if (opts.stepTypeFilter) {
|
|
3364
|
+
fields.push("step.type");
|
|
3365
|
+
}
|
|
3366
|
+
if (fields.length === 0) continue;
|
|
3367
|
+
out.push({
|
|
3368
|
+
runId: m.runId,
|
|
3369
|
+
runName: m.name,
|
|
3370
|
+
runStatus: m.status,
|
|
3371
|
+
stepId: c.stepId,
|
|
3372
|
+
stepName: s.name,
|
|
3373
|
+
stepType,
|
|
3374
|
+
timestamp: s.startTime ?? s.timestamp,
|
|
3375
|
+
durationMs: c.durationMs,
|
|
3376
|
+
matchReason: `step match: ${fields.join(", ")}`,
|
|
3377
|
+
matchedFields: fields,
|
|
3378
|
+
filePath: m.filePath
|
|
3379
|
+
});
|
|
3380
|
+
}
|
|
3381
|
+
return out;
|
|
3382
|
+
}
|
|
3383
|
+
async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
|
|
3384
|
+
const metas = [];
|
|
3385
|
+
for (const fileName of fileNames) {
|
|
3386
|
+
try {
|
|
3387
|
+
const filePath = getPath(fileName);
|
|
3388
|
+
const meta = await extractMetadata(filePath);
|
|
3389
|
+
metas.push(meta);
|
|
3390
|
+
} catch {
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
return metas;
|
|
3394
|
+
}
|
|
2660
3395
|
var KNOWN_EVENTS = /* @__PURE__ */ new Set([
|
|
2661
3396
|
"run_started",
|
|
2662
3397
|
"run_completed",
|
|
@@ -3938,12 +4673,13 @@ async function inspectRun(name, fn, options) {
|
|
|
3938
4673
|
const runId = createRunId();
|
|
3939
4674
|
const traceDir = resolveTraceDir({ dir: options?.traceDir });
|
|
3940
4675
|
const traceSafety = resolveTraceSafetyOptions(options);
|
|
4676
|
+
const runMetadata = buildRunStartedMetadata(options);
|
|
3941
4677
|
const context = {
|
|
3942
4678
|
runId,
|
|
3943
4679
|
runName,
|
|
3944
4680
|
traceDir,
|
|
3945
4681
|
silent: options?.silent ?? false,
|
|
3946
|
-
metadata:
|
|
4682
|
+
metadata: runMetadata
|
|
3947
4683
|
};
|
|
3948
4684
|
return runWithContext(context, async () => {
|
|
3949
4685
|
const startTime = Date.now();
|
|
@@ -3959,7 +4695,7 @@ async function inspectRun(name, fn, options) {
|
|
|
3959
4695
|
runId,
|
|
3960
4696
|
name: runName,
|
|
3961
4697
|
startTime,
|
|
3962
|
-
...
|
|
4698
|
+
...runMetadata !== void 0 ? { metadata: runMetadata } : {}
|
|
3963
4699
|
};
|
|
3964
4700
|
await writeTraceEvent(
|
|
3965
4701
|
prepareTraceEventForDisk(started, traceSafety),
|
|
@@ -4244,6 +4980,134 @@ function observe(agent, options) {
|
|
|
4244
4980
|
// packages/core/src/exporters/types.ts
|
|
4245
4981
|
var EXPORT_PAYLOAD_VERSION = "0.1.2";
|
|
4246
4982
|
|
|
4983
|
+
// packages/core/src/exporters/redact-export.ts
|
|
4984
|
+
function isRecord10(value) {
|
|
4985
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4986
|
+
}
|
|
4987
|
+
function deepClone(value) {
|
|
4988
|
+
if (value === null || typeof value !== "object") {
|
|
4989
|
+
return value;
|
|
4990
|
+
}
|
|
4991
|
+
if (Array.isArray(value)) {
|
|
4992
|
+
return value.map((item) => deepClone(item));
|
|
4993
|
+
}
|
|
4994
|
+
const out = {};
|
|
4995
|
+
for (const [k, v] of Object.entries(value)) {
|
|
4996
|
+
out[k] = deepClone(v);
|
|
4997
|
+
}
|
|
4998
|
+
return out;
|
|
4999
|
+
}
|
|
5000
|
+
function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
5001
|
+
if (depth > 32) {
|
|
5002
|
+
return { truncated: true, reason: "maxDepth" };
|
|
5003
|
+
}
|
|
5004
|
+
const out = {};
|
|
5005
|
+
for (const [key, value] of Object.entries(record)) {
|
|
5006
|
+
out[key] = boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth);
|
|
5007
|
+
}
|
|
5008
|
+
return out;
|
|
5009
|
+
}
|
|
5010
|
+
function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
5011
|
+
if (value === null || typeof value !== "object") {
|
|
5012
|
+
if (typeof value === "string") {
|
|
5013
|
+
return truncateStringForProfile(
|
|
5014
|
+
value,
|
|
5015
|
+
key,
|
|
5016
|
+
maxMetadataValueLength,
|
|
5017
|
+
maxPreviewLength
|
|
5018
|
+
);
|
|
5019
|
+
}
|
|
5020
|
+
return value;
|
|
5021
|
+
}
|
|
5022
|
+
if (seen.has(value)) return "[Circular]";
|
|
5023
|
+
seen.add(value);
|
|
5024
|
+
if (Array.isArray(value)) {
|
|
5025
|
+
return value.slice(0, 50).map(
|
|
5026
|
+
(item, index) => boundValue(
|
|
5027
|
+
item,
|
|
5028
|
+
String(index),
|
|
5029
|
+
maxMetadataValueLength,
|
|
5030
|
+
maxPreviewLength,
|
|
5031
|
+
seen,
|
|
5032
|
+
depth + 1
|
|
5033
|
+
)
|
|
5034
|
+
);
|
|
5035
|
+
}
|
|
5036
|
+
return boundAttributeValues(
|
|
5037
|
+
value,
|
|
5038
|
+
maxMetadataValueLength,
|
|
5039
|
+
maxPreviewLength,
|
|
5040
|
+
seen,
|
|
5041
|
+
depth + 1
|
|
5042
|
+
);
|
|
5043
|
+
}
|
|
5044
|
+
function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPreviewLength) {
|
|
5045
|
+
if (!attrs || Object.keys(attrs).length === 0) {
|
|
5046
|
+
return attrs;
|
|
5047
|
+
}
|
|
5048
|
+
const redacted = redactor.redactRecord(attrs);
|
|
5049
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
5050
|
+
const bounded = boundAttributeValues(
|
|
5051
|
+
redacted,
|
|
5052
|
+
maxMetadataValueLength,
|
|
5053
|
+
maxPreviewLength,
|
|
5054
|
+
seen,
|
|
5055
|
+
0
|
|
5056
|
+
);
|
|
5057
|
+
const err = bounded.error;
|
|
5058
|
+
if (isRecord10(err) && typeof err.message === "string") {
|
|
5059
|
+
bounded.error = {
|
|
5060
|
+
...err,
|
|
5061
|
+
message: truncateStringForProfile(
|
|
5062
|
+
err.message,
|
|
5063
|
+
"message",
|
|
5064
|
+
maxMetadataValueLength,
|
|
5065
|
+
maxPreviewLength
|
|
5066
|
+
),
|
|
5067
|
+
...typeof err.stack === "string" ? {
|
|
5068
|
+
stack: truncateStringForProfile(
|
|
5069
|
+
err.stack,
|
|
5070
|
+
"stack",
|
|
5071
|
+
maxMetadataValueLength,
|
|
5072
|
+
maxPreviewLength
|
|
5073
|
+
)
|
|
5074
|
+
} : {}
|
|
5075
|
+
};
|
|
5076
|
+
}
|
|
5077
|
+
return bounded;
|
|
5078
|
+
}
|
|
5079
|
+
function redactRunTreeForExport(tree, options) {
|
|
5080
|
+
const profile = options?.redactionProfile ?? "local";
|
|
5081
|
+
if (profile === "local") {
|
|
5082
|
+
return deepClone(tree);
|
|
5083
|
+
}
|
|
5084
|
+
const resolved = resolveRedactionProfile(profile);
|
|
5085
|
+
const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
|
|
5086
|
+
2e3,
|
|
5087
|
+
500,
|
|
5088
|
+
resolved
|
|
5089
|
+
);
|
|
5090
|
+
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
5091
|
+
const clone = deepClone(tree);
|
|
5092
|
+
function walk(nodes) {
|
|
5093
|
+
for (const node of nodes) {
|
|
5094
|
+
if (node.event.attributes !== void 0) {
|
|
5095
|
+
node.event.attributes = redactEventAttributes(
|
|
5096
|
+
node.event.attributes,
|
|
5097
|
+
redactor,
|
|
5098
|
+
maxMetadataValueLength,
|
|
5099
|
+
maxPreviewLength
|
|
5100
|
+
);
|
|
5101
|
+
}
|
|
5102
|
+
if (node.children.length > 0) {
|
|
5103
|
+
walk(node.children);
|
|
5104
|
+
}
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
walk(clone.children);
|
|
5108
|
+
return clone;
|
|
5109
|
+
}
|
|
5110
|
+
|
|
4247
5111
|
// packages/core/src/exporters/html-exporter.ts
|
|
4248
5112
|
function renderTreeHtml(nodes, ulClass = "tree") {
|
|
4249
5113
|
if (nodes.length === 0) return "";
|
|
@@ -4980,20 +5844,22 @@ function mergeExportDefaults(options) {
|
|
|
4980
5844
|
includeErrors: options.includeErrors ?? true,
|
|
4981
5845
|
pretty: options.pretty ?? true,
|
|
4982
5846
|
redacted: options.redacted ?? true,
|
|
4983
|
-
maxAttributeLength: options.maxAttributeLength ?? 500
|
|
5847
|
+
maxAttributeLength: options.maxAttributeLength ?? 500,
|
|
5848
|
+
redactionProfile: options.redactionProfile ?? "local"
|
|
4984
5849
|
};
|
|
4985
5850
|
}
|
|
4986
5851
|
function exportRunTree(tree, options) {
|
|
4987
5852
|
const opts = mergeExportDefaults(options);
|
|
5853
|
+
const exportTree = opts.redactionProfile === "local" ? tree : redactRunTreeForExport(tree, { redactionProfile: opts.redactionProfile });
|
|
4988
5854
|
switch (opts.format) {
|
|
4989
5855
|
case "markdown":
|
|
4990
|
-
return exportMarkdown(
|
|
5856
|
+
return exportMarkdown(exportTree, opts);
|
|
4991
5857
|
case "html":
|
|
4992
|
-
return exportHtml(
|
|
5858
|
+
return exportHtml(exportTree, opts);
|
|
4993
5859
|
case "openinference":
|
|
4994
|
-
return exportOpenInference(
|
|
5860
|
+
return exportOpenInference(exportTree, opts);
|
|
4995
5861
|
case "otlp-json":
|
|
4996
|
-
return exportOtlpJson(
|
|
5862
|
+
return exportOtlpJson(exportTree, opts);
|
|
4997
5863
|
default: {
|
|
4998
5864
|
const _x = opts.format;
|
|
4999
5865
|
throw new Error(`Unsupported export format: ${String(_x)}`);
|
|
@@ -5010,6 +5876,6 @@ function validateExport(result) {
|
|
|
5010
5876
|
};
|
|
5011
5877
|
}
|
|
5012
5878
|
|
|
5013
|
-
export { DEFAULT_LOG_INGEST_CONFIG, DEFAULT_MAX_EVENT_BYTES, DEFAULT_MAX_METADATA_VALUE_LENGTH, DEFAULT_MAX_PREVIEW_LENGTH, DEFAULT_REDACT_KEYS, DEFAULT_TRACE_DIR_NAME, EXPORT_PAYLOAD_VERSION, EventNormalizer, FALLBACK_TRACE_DIR, JsonLogParser, LiveLogAccumulator, Log4jsParser, MAX_NAME_LENGTH, MAX_TERMINAL_DEPTH, MAX_TERMINAL_NAME_LENGTH, RUNS_DIR_NAME, Redactor, TERMINAL_INDENT, TraceDirectory, TreeBuilder, buildRunSummary, compactAttributes4 as compactAttributes, createRunId, createStepId, diffRuns, diffTraceEvents, ensureTraceDir, escapeHtml, escapeMarkdown, exportHtml, exportMarkdown, exportOpenInference, exportOtlpJson, exportRunTree, extractMetadata, filterTraces, flattenTree, formatDuration2 as formatDuration, formatError, formatTerminalName, formatTimestamp, getCurrentContext, getCurrentDepth, getCurrentRunId, getCurrentRunName, getCurrentStepId, getDefaultTraceDir, getIndent, getParentStepId, getRunIdFromTraceFileName, getTraceDirFromContext, getTraceFilePath, getTraceSafetyFromContext, hasActiveContext, initializeTraceFile, inspectEventToPersistedInspectEvent, inspectEventsToPersistedInspectEvents, inspectRun, isAgentInspectEnabled, isAgentInspectTrace, isPersistedInspectEvent, isSilentContext, isStepStatus, isStepType, isTraceEvent, listTraceFiles, loadLogIngestConfig, manualTraceEventsToComparableRun, manualTraceEventsToRunTree, matchMapping, maybeInspectRun, mergeExportDefaults, mergeLogIngestConfig, observe, parseDuration, parseLogLine, parseLogsToTrees, persistedInspectEventToInspectEvent, persistedInspectEventsToInspectEvents, persistedInspectEventsToRunTrees, prepareMetadataForDisk, prepareTraceEventForDisk, printError, printFailedAt, printRunComplete, printRunStart, printStepComplete, printStepStart, readTraceEvents, readTraceFile, renderErrorLine, renderRunDiff, renderRunSummary, renderRunTree, renderRunTrees, renderStepLine, resolveTraceDir, resolveTraceSafetyOptions, runWithContext, runWithStepContext, safeString2 as safeString, serializeEvent, stableJson, step, summarizeTree, traceEventToPersistedInspectEvent, traceEventsToPersistedInspectEvents, traceEventsToPersistedRunTrees, truncateName, validateEvent, validateExport, validateExportContent, warn, wildcardMatch, writeTraceEvent };
|
|
5879
|
+
export { DEFAULT_LOG_INGEST_CONFIG, DEFAULT_MAX_EVENT_BYTES, DEFAULT_MAX_METADATA_VALUE_LENGTH, DEFAULT_MAX_PREVIEW_LENGTH, DEFAULT_REDACT_KEYS, DEFAULT_TRACE_DIR_NAME, EXPORT_PAYLOAD_VERSION, EventNormalizer, FALLBACK_TRACE_DIR, JsonLogParser, LiveLogAccumulator, Log4jsParser, MAX_NAME_LENGTH, MAX_TERMINAL_DEPTH, MAX_TERMINAL_NAME_LENGTH, RUNS_DIR_NAME, Redactor, TERMINAL_INDENT, TraceDirectory, TreeBuilder, buildRunSummary, buildRunTimeline, buildTraceStats, compactAttributes4 as compactAttributes, createRunId, createStepId, diffRuns, diffTraceEvents, ensureTraceDir, escapeHtml, escapeMarkdown, exportHtml, exportMarkdown, exportOpenInference, exportOtlpJson, exportRunTree, extractMetadata, filterTraces, flattenTree, formatDuration2 as formatDuration, formatError, formatTerminalName, formatTimestamp, getCurrentContext, getCurrentCorrelationMetadata, getCurrentDepth, getCurrentRunId, getCurrentRunName, getCurrentStepId, getDefaultTraceDir, getIndent, getParentStepId, getRunIdFromTraceFileName, getTraceDirFromContext, getTraceFilePath, getTraceSafetyFromContext, hasActiveContext, initializeTraceFile, inspectEventToPersistedInspectEvent, inspectEventsToPersistedInspectEvents, inspectRun, isAgentInspectEnabled, isAgentInspectTrace, isPersistedInspectEvent, isSilentContext, isStepStatus, isStepType, isTraceEvent, listTraceFiles, loadLogIngestConfig, loadTraceMetadataList, manualTraceEventsToComparableRun, manualTraceEventsToRunTree, matchMapping, maybeInspectRun, mergeExportDefaults, mergeLogIngestConfig, observe, parseDuration, parseDurationFilter, parseLogLine, parseLogsToTrees, persistedInspectEventToInspectEvent, persistedInspectEventsToInspectEvents, persistedInspectEventsToRunTrees, prepareMetadataForDisk, prepareTraceEventForDisk, printError, printFailedAt, printRunComplete, printRunStart, printStepComplete, printStepStart, readTraceEvents, readTraceFile, redactRunTreeForExport, renderErrorLine, renderRunDiff, renderRunSummary, renderRunTree, renderRunTrees, renderStepLine, renderTimeline, renderTraceStats, resolveRedactionProfile, resolveTraceDir, resolveTraceSafetyOptions, runWithContext, runWithStepContext, safeString2 as safeString, searchTraces, serializeEvent, stableJson, step, summarizeTree, traceEventToPersistedInspectEvent, traceEventsToPersistedInspectEvents, traceEventsToPersistedRunTrees, truncateName, validateEvent, validateExport, validateExportContent, warn, wildcardMatch, writeTraceEvent };
|
|
5014
5880
|
//# sourceMappingURL=index.mjs.map
|
|
5015
5881
|
//# sourceMappingURL=index.mjs.map
|