agent-inspect 1.9.0 → 2.0.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 +6 -0
- package/README.md +13 -9
- package/docs/ADAPTER-CONFORMANCE.md +1 -1
- package/docs/API.md +30 -29
- package/docs/CLI.md +41 -11
- package/docs/EXPORTS.md +1 -1
- package/docs/GETTING-STARTED.md +2 -1
- package/docs/KNOWN-ISSUES.md +8 -3
- package/docs/LIMITATIONS.md +6 -6
- package/docs/MIGRATION.md +8 -8
- package/docs/SCHEMA.md +29 -12
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +7135 -7481
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +7135 -7481
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/core/dist/advanced.cjs +2047 -26
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +346 -196
- package/packages/core/dist/advanced.d.ts +346 -196
- package/packages/core/dist/advanced.mjs +628 -5
- package/packages/core/dist/advanced.mjs.map +1 -1
- package/packages/core/dist/checks.d.cts +1 -1
- package/packages/core/dist/checks.d.ts +1 -1
- package/packages/core/dist/chunk-2ZXEMWGK.mjs +549 -0
- package/packages/core/dist/chunk-2ZXEMWGK.mjs.map +1 -0
- package/packages/core/dist/{chunk-57S5D6HR.mjs → chunk-4JVGFNQH.mjs} +50 -6
- package/packages/core/dist/chunk-4JVGFNQH.mjs.map +1 -0
- package/packages/core/dist/{chunk-6QSLZCBJ.mjs → chunk-A4V2H3YU.mjs} +329 -163
- package/packages/core/dist/chunk-A4V2H3YU.mjs.map +1 -0
- package/packages/core/dist/{chunk-74XZ6N7Q.mjs → chunk-IZBJAZGF.mjs} +12 -4
- package/packages/core/dist/chunk-IZBJAZGF.mjs.map +1 -0
- package/packages/core/dist/{chunk-VTIB5MDK.mjs → chunk-NQZPZ2FN.mjs} +15 -10
- package/packages/core/dist/chunk-NQZPZ2FN.mjs.map +1 -0
- package/packages/core/dist/{chunk-TZISEVLQ.mjs → chunk-XUNJMAPD.mjs} +3 -3
- package/packages/core/dist/{chunk-TZISEVLQ.mjs.map → chunk-XUNJMAPD.mjs.map} +1 -1
- package/packages/core/dist/context-CSKnzpXR.d.cts +177 -0
- package/packages/core/dist/context-yv2VSDQF.d.ts +177 -0
- package/packages/core/dist/diff.d.cts +2 -2
- package/packages/core/dist/diff.d.ts +2 -2
- package/packages/core/dist/diff.mjs +473 -3
- package/packages/core/dist/diff.mjs.map +1 -1
- package/packages/core/dist/exporters.cjs +691 -0
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.d.cts +24 -3
- package/packages/core/dist/exporters.d.ts +24 -3
- package/packages/core/dist/exporters.mjs +1113 -3
- package/packages/core/dist/exporters.mjs.map +1 -1
- package/packages/core/dist/index.cjs +1385 -8168
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.d.cts +8 -350
- package/packages/core/dist/index.d.ts +8 -350
- package/packages/core/dist/index.mjs +18 -1478
- package/packages/core/dist/index.mjs.map +1 -1
- package/packages/core/dist/{log-config-DanPV3P9.d.cts → log-config-8aE5Kxtr.d.cts} +1 -1
- package/packages/core/dist/{log-config-BPHS4Sds.d.ts → log-config-DVimQJho.d.ts} +1 -1
- package/packages/core/dist/logs.d.cts +2 -2
- package/packages/core/dist/logs.d.ts +2 -2
- package/packages/core/dist/logs.mjs +785 -2
- package/packages/core/dist/logs.mjs.map +1 -1
- package/packages/core/dist/{persisted-inspect-event-Cw7TeYGr.d.ts → persisted-inspect-event-D-WpXeZX.d.ts} +14 -5
- package/packages/core/dist/{persisted-inspect-event-DHPfzUd8.d.cts → persisted-inspect-event-DHcHPUKv.d.cts} +14 -5
- package/packages/core/dist/persisted.cjs +11 -1
- package/packages/core/dist/persisted.cjs.map +1 -1
- package/packages/core/dist/persisted.d.cts +4 -4
- package/packages/core/dist/persisted.d.ts +4 -4
- package/packages/core/dist/persisted.mjs +150 -3
- package/packages/core/dist/persisted.mjs.map +1 -1
- package/packages/core/dist/readers.cjs +27 -10
- package/packages/core/dist/readers.cjs.map +1 -1
- package/packages/core/dist/readers.d.cts +1 -1
- package/packages/core/dist/readers.d.ts +1 -1
- package/packages/core/dist/readers.mjs +1580 -4
- package/packages/core/dist/readers.mjs.map +1 -1
- package/packages/core/dist/{types-Ap9uMdx_.d.ts → types-D-Y1kOU-.d.ts} +2 -2
- package/packages/core/dist/{types-B2-BU5CS.d.cts → types-DgMN3qow.d.cts} +2 -2
- package/packages/core/dist/writers.cjs +49 -3
- package/packages/core/dist/writers.cjs.map +1 -1
- package/packages/core/dist/writers.d.cts +1 -1
- package/packages/core/dist/writers.d.ts +1 -1
- package/packages/core/dist/writers.mjs +341 -4
- package/packages/core/dist/writers.mjs.map +1 -1
- package/packages/core/dist/chunk-57S5D6HR.mjs.map +0 -1
- package/packages/core/dist/chunk-6QSLZCBJ.mjs.map +0 -1
- package/packages/core/dist/chunk-6SZPTECC.mjs +0 -342
- package/packages/core/dist/chunk-6SZPTECC.mjs.map +0 -1
- package/packages/core/dist/chunk-74XZ6N7Q.mjs.map +0 -1
- package/packages/core/dist/chunk-HR7G62IE.mjs +0 -785
- package/packages/core/dist/chunk-HR7G62IE.mjs.map +0 -1
- package/packages/core/dist/chunk-TFLPUZ56.mjs +0 -1571
- package/packages/core/dist/chunk-TFLPUZ56.mjs.map +0 -1
- package/packages/core/dist/chunk-U2BGPESY.mjs +0 -150
- package/packages/core/dist/chunk-U2BGPESY.mjs.map +0 -1
- package/packages/core/dist/chunk-VTIB5MDK.mjs.map +0 -1
- package/packages/core/dist/chunk-Y56BPA3B.mjs +0 -990
- package/packages/core/dist/chunk-Y56BPA3B.mjs.map +0 -1
- package/packages/core/dist/chunk-YWAOOXLR.mjs +0 -475
- package/packages/core/dist/chunk-YWAOOXLR.mjs.map +0 -1
|
@@ -1,1473 +1,13 @@
|
|
|
1
|
-
import { getCurrentContext, getCurrentDepth, getParentStepId, getTraceSafetyFromContext, runWithStepContext,
|
|
2
|
-
export {
|
|
3
|
-
export { inspectEventToPersistedInspectEvent, inspectEventsToPersistedInspectEvents } from './chunk-U2BGPESY.mjs';
|
|
4
|
-
export { DEFAULT_LOG_INGEST_CONFIG, EventNormalizer, JsonLogParser, LiveLogAccumulator, Log4jsParser, loadLogIngestConfig, matchMapping, mergeLogIngestConfig, parseLogLine, parseLogsToTrees, renderRunTree, renderRunTrees, wildcardMatch } from './chunk-HR7G62IE.mjs';
|
|
5
|
-
import { redactTraceEventsForReport, exportMarkdown, exportHtml, manualTraceEventsToRunTree, redactRunTreeForExport } from './chunk-Y56BPA3B.mjs';
|
|
6
|
-
export { EXPORT_PAYLOAD_VERSION, exportHtml, exportMarkdown, exportOpenInference, exportOtlpJson, exportRunTree, manualTraceEventsToRunTree, mergeExportDefaults, redactRunTreeForExport, validateExport, validateExportContent } from './chunk-Y56BPA3B.mjs';
|
|
7
|
-
export { diffRuns, diffTraceEvents, manualTraceEventsToComparableRun, renderRunDiff } from './chunk-YWAOOXLR.mjs';
|
|
1
|
+
import { getCurrentContext, getCurrentDepth, getParentStepId, getTraceSafetyFromContext, runWithStepContext, printStepStart, printStepComplete, printError, printFailedAt, inspectRun } from './chunk-A4V2H3YU.mjs';
|
|
2
|
+
export { createInspector, getCurrentCorrelationMetadata, inspectRun, maybeInspectRun } from './chunk-A4V2H3YU.mjs';
|
|
8
3
|
import './chunk-BT7CATSD.mjs';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import
|
|
13
|
-
|
|
14
|
-
import
|
|
15
|
-
export { resolveRedactionProfile } from './chunk-EDTQHZPM.mjs';
|
|
16
|
-
import { Redactor } from './chunk-VU6O5QAH.mjs';
|
|
17
|
-
export { DEFAULT_REDACT_KEYS, Redactor } from './chunk-VU6O5QAH.mjs';
|
|
18
|
-
export { DEFAULT_TRACE_READERS, TraceReadError, agentInspectJsonlReader, detectTraceFormat, openTrace, readTrace } from './chunk-TFLPUZ56.mjs';
|
|
19
|
-
export { persistedInspectEventToInspectEvent, persistedInspectEventsToInspectEvents, persistedInspectEventsToRunTrees, traceEventToPersistedInspectEvent, traceEventsToPersistedInspectEvents, traceEventsToPersistedRunTrees } from './chunk-TZISEVLQ.mjs';
|
|
20
|
-
import { buildRunStartedMetadata } from './chunk-S4YWKV4G.mjs';
|
|
21
|
-
export { TreeBuilder } from './chunk-E5F2LQCX.mjs';
|
|
22
|
-
import { warn, createStepId, formatError, truncateName, getDefaultTraceDir, parseTraceJsonl, parseDuration, formatTimestamp, formatDuration, isTraceEvent, createRunId, getTraceFilePath } from './chunk-VTIB5MDK.mjs';
|
|
23
|
-
export { DEFAULT_TRACE_DIR_NAME, FALLBACK_TRACE_DIR, MAX_NAME_LENGTH, RUNS_DIR_NAME, createRunId, createStepId, ensureTraceDir, formatDuration, formatError, formatTimestamp, getDefaultTraceDir, getTraceFilePath, isStepStatus, isStepType, isTraceEvent, parseDuration, parseTraceJsonl, truncateName, unknownTraceFormatMessage, warn } from './chunk-VTIB5MDK.mjs';
|
|
24
|
-
export { isPersistedInspectEvent, persistedInspectEventToTraceEvents, persistedInspectEventsToTraceEvents } from './chunk-74XZ6N7Q.mjs';
|
|
4
|
+
import { prepareTraceEventForDisk, writeTraceEvent } from './chunk-4JVGFNQH.mjs';
|
|
5
|
+
import './chunk-EDTQHZPM.mjs';
|
|
6
|
+
import './chunk-VU6O5QAH.mjs';
|
|
7
|
+
import './chunk-S4YWKV4G.mjs';
|
|
8
|
+
import { warn, createStepId, formatError, truncateName } from './chunk-NQZPZ2FN.mjs';
|
|
9
|
+
import './chunk-IZBJAZGF.mjs';
|
|
25
10
|
import './chunk-7TGZLWEE.mjs';
|
|
26
|
-
import { readdir, stat, readFile } from 'fs/promises';
|
|
27
|
-
import path from 'path';
|
|
28
|
-
import { createReadStream } from 'fs';
|
|
29
|
-
import { createInterface } from 'readline';
|
|
30
|
-
|
|
31
|
-
function resolveTraceDir(options = {}) {
|
|
32
|
-
if (typeof options.dir === "string" && options.dir.trim() !== "") {
|
|
33
|
-
return options.dir.trim();
|
|
34
|
-
}
|
|
35
|
-
const envDir = process.env.AGENT_INSPECT_TRACE_DIR;
|
|
36
|
-
if (typeof envDir === "string" && envDir.trim() !== "") {
|
|
37
|
-
return envDir.trim();
|
|
38
|
-
}
|
|
39
|
-
return getDefaultTraceDir();
|
|
40
|
-
}
|
|
41
|
-
var TraceDirectory = class {
|
|
42
|
-
#dir;
|
|
43
|
-
constructor(options = {}) {
|
|
44
|
-
this.#dir = resolveTraceDir(options);
|
|
45
|
-
}
|
|
46
|
-
getPath(filename) {
|
|
47
|
-
return filename ? path.join(this.#dir, filename) : this.#dir;
|
|
48
|
-
}
|
|
49
|
-
async list() {
|
|
50
|
-
try {
|
|
51
|
-
const files = await readdir(this.#dir);
|
|
52
|
-
return files.filter((f) => f.endsWith(".jsonl"));
|
|
53
|
-
} catch (e) {
|
|
54
|
-
if (e && typeof e === "object" && "code" in e && e.code === "ENOENT") {
|
|
55
|
-
return [];
|
|
56
|
-
}
|
|
57
|
-
throw e;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
async getFileStats(filename) {
|
|
61
|
-
return await stat(this.getPath(filename));
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
function isFiniteNumber(v) {
|
|
65
|
-
return typeof v === "number" && Number.isFinite(v);
|
|
66
|
-
}
|
|
67
|
-
function parseIsoToMs(value) {
|
|
68
|
-
if (value === void 0) return void 0;
|
|
69
|
-
const parsed = Date.parse(value);
|
|
70
|
-
return Number.isFinite(parsed) ? parsed : void 0;
|
|
71
|
-
}
|
|
72
|
-
async function extractMetadata(filePath, _quickScan) {
|
|
73
|
-
const stats = await stat(filePath);
|
|
74
|
-
let runIdFromFile = path.basename(filePath);
|
|
75
|
-
if (runIdFromFile.endsWith(".jsonl")) {
|
|
76
|
-
runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
|
|
77
|
-
}
|
|
78
|
-
const raw = await readFile(filePath, "utf-8");
|
|
79
|
-
const parsedTrace = parseTraceJsonl(raw, { warnings: false });
|
|
80
|
-
let runId;
|
|
81
|
-
let name;
|
|
82
|
-
let startedAt;
|
|
83
|
-
let endedAt;
|
|
84
|
-
let explicitDurationMs;
|
|
85
|
-
let hasRunStarted = false;
|
|
86
|
-
let hasRunCompleted = false;
|
|
87
|
-
let runCompletedStatus;
|
|
88
|
-
let anyStepError = false;
|
|
89
|
-
const anyKnownEvent = parsedTrace.sourceEventCount > 0;
|
|
90
|
-
let persistedStatus;
|
|
91
|
-
const persistedRun = parsedTrace.persisted.find(
|
|
92
|
-
(event) => event.kind === "RUN"
|
|
93
|
-
);
|
|
94
|
-
if (persistedRun) {
|
|
95
|
-
runId = persistedRun.runId;
|
|
96
|
-
if (persistedRun.name.trim() !== "") {
|
|
97
|
-
name = persistedRun.name;
|
|
98
|
-
}
|
|
99
|
-
startedAt = parseIsoToMs(persistedRun.startedAt) ?? parseIsoToMs(persistedRun.timestamp);
|
|
100
|
-
endedAt = parseIsoToMs(persistedRun.endedAt);
|
|
101
|
-
if (isFiniteNumber(persistedRun.durationMs)) {
|
|
102
|
-
explicitDurationMs = persistedRun.durationMs;
|
|
103
|
-
if (endedAt === void 0 && startedAt !== void 0) {
|
|
104
|
-
endedAt = startedAt + persistedRun.durationMs;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (persistedRun.status === "ok") persistedStatus = "success";
|
|
108
|
-
else if (persistedRun.status === "error") persistedStatus = "error";
|
|
109
|
-
else if (persistedRun.status === "running") persistedStatus = "running";
|
|
110
|
-
else if (persistedRun.status === "unknown") persistedStatus = "unknown";
|
|
111
|
-
} else {
|
|
112
|
-
runId = parsedTrace.persisted[0]?.runId;
|
|
113
|
-
}
|
|
114
|
-
for (const e of parsedTrace.events) {
|
|
115
|
-
if (runId === void 0 && typeof e.runId === "string") {
|
|
116
|
-
runId = e.runId;
|
|
117
|
-
}
|
|
118
|
-
if (e.event === "run_started") {
|
|
119
|
-
hasRunStarted = true;
|
|
120
|
-
const rs = e;
|
|
121
|
-
if (typeof rs.name === "string" && rs.name.trim() !== "") {
|
|
122
|
-
name = rs.name;
|
|
123
|
-
}
|
|
124
|
-
if (isFiniteNumber(rs.startTime)) {
|
|
125
|
-
startedAt = rs.startTime;
|
|
126
|
-
} else if (isFiniteNumber(rs.timestamp)) {
|
|
127
|
-
startedAt = rs.timestamp;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
if (e.event === "run_completed") {
|
|
131
|
-
hasRunCompleted = true;
|
|
132
|
-
const rc = e;
|
|
133
|
-
runCompletedStatus = rc.status;
|
|
134
|
-
if (isFiniteNumber(rc.endTime)) endedAt = rc.endTime;
|
|
135
|
-
else if (isFiniteNumber(rc.timestamp)) endedAt = rc.timestamp;
|
|
136
|
-
if (isFiniteNumber(rc.durationMs)) explicitDurationMs = rc.durationMs;
|
|
137
|
-
}
|
|
138
|
-
if (e.event === "step_completed") {
|
|
139
|
-
const sc = e;
|
|
140
|
-
if (sc.status === "error") {
|
|
141
|
-
anyStepError = true;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const resolvedRunId = runId ?? runIdFromFile;
|
|
146
|
-
let status = "unknown";
|
|
147
|
-
if (hasRunCompleted && (runCompletedStatus === "success" || runCompletedStatus === "error")) {
|
|
148
|
-
status = runCompletedStatus;
|
|
149
|
-
} else if (anyStepError) {
|
|
150
|
-
status = "error";
|
|
151
|
-
} else if (persistedStatus !== void 0) {
|
|
152
|
-
status = persistedStatus;
|
|
153
|
-
} else if (hasRunStarted && !hasRunCompleted) {
|
|
154
|
-
status = "running";
|
|
155
|
-
} else if (anyKnownEvent) {
|
|
156
|
-
status = "unknown";
|
|
157
|
-
} else {
|
|
158
|
-
status = "unknown";
|
|
159
|
-
}
|
|
160
|
-
const durationMs = explicitDurationMs ?? (startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt ? endedAt - startedAt : void 0);
|
|
161
|
-
return {
|
|
162
|
-
runId: resolvedRunId,
|
|
163
|
-
name,
|
|
164
|
-
status,
|
|
165
|
-
startedAt,
|
|
166
|
-
endedAt,
|
|
167
|
-
durationMs,
|
|
168
|
-
eventCount: parsedTrace.sourceEventCount,
|
|
169
|
-
filePath,
|
|
170
|
-
fileSize: stats.size,
|
|
171
|
-
createdAt: stats.birthtime
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
function isNonNegativeFiniteNumber(value) {
|
|
175
|
-
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
176
|
-
}
|
|
177
|
-
function buildRunSummary(events) {
|
|
178
|
-
const started = events.find(
|
|
179
|
-
(e) => e.event === "run_started"
|
|
180
|
-
);
|
|
181
|
-
const completed = events.filter(
|
|
182
|
-
(e) => e.event === "run_completed"
|
|
183
|
-
);
|
|
184
|
-
const lastCompleted = completed[completed.length - 1];
|
|
185
|
-
const runId = started?.runId ?? events.find((e) => typeof e.runId === "string")?.runId ?? "unknown-run";
|
|
186
|
-
const name = typeof started?.name === "string" && started.name.trim() !== "" ? started.name : void 0;
|
|
187
|
-
const status = lastCompleted ? lastCompleted.status : started ? "running" : "unknown";
|
|
188
|
-
const durationMs = lastCompleted && isFiniteNumber(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
189
|
-
started && isFiniteNumber(started.startTime) ? started.startTime : void 0;
|
|
190
|
-
const steps = /* @__PURE__ */ new Map();
|
|
191
|
-
for (const e of events) {
|
|
192
|
-
if (e.event === "step_started") {
|
|
193
|
-
const s = e;
|
|
194
|
-
steps.set(s.stepId, {
|
|
195
|
-
type: s.type,
|
|
196
|
-
name: s.name,
|
|
197
|
-
status: "running",
|
|
198
|
-
parentId: s.parentId,
|
|
199
|
-
tokensInput: isNonNegativeFiniteNumber(s.metadata?.tokens?.input) ? s.metadata.tokens.input : void 0,
|
|
200
|
-
tokensOutput: isNonNegativeFiniteNumber(s.metadata?.tokens?.output) ? s.metadata.tokens.output : void 0,
|
|
201
|
-
tokensTotal: isNonNegativeFiniteNumber(s.metadata?.tokens?.total) ? s.metadata.tokens.total : void 0,
|
|
202
|
-
tokensCached: isNonNegativeFiniteNumber(s.metadata?.tokens?.cached) ? s.metadata.tokens.cached : void 0
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
for (const e of events) {
|
|
207
|
-
if (e.event === "step_completed") {
|
|
208
|
-
const c = e;
|
|
209
|
-
const existing = steps.get(c.stepId);
|
|
210
|
-
if (!existing) continue;
|
|
211
|
-
existing.status = c.status;
|
|
212
|
-
existing.durationMs = c.durationMs;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
let totalSteps = 0;
|
|
216
|
-
let llmSteps = 0;
|
|
217
|
-
let toolSteps = 0;
|
|
218
|
-
let logicSteps = 0;
|
|
219
|
-
let errorSteps = 0;
|
|
220
|
-
let maxDepth = 0;
|
|
221
|
-
let longestStep;
|
|
222
|
-
let totalTokensInput = 0;
|
|
223
|
-
let totalTokensOutput = 0;
|
|
224
|
-
let totalTokensTotal = 0;
|
|
225
|
-
let totalTokensCached = 0;
|
|
226
|
-
let tokenBearingSteps = 0;
|
|
227
|
-
let stepsWithKnownTotal = 0;
|
|
228
|
-
let hasCachedTokens = false;
|
|
229
|
-
const depthCache = /* @__PURE__ */ new Map();
|
|
230
|
-
const computeDepth = (stepId) => {
|
|
231
|
-
const cached = depthCache.get(stepId);
|
|
232
|
-
if (cached !== void 0) return cached;
|
|
233
|
-
const node = steps.get(stepId);
|
|
234
|
-
if (!node) return 0;
|
|
235
|
-
const parent = node.parentId;
|
|
236
|
-
if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
|
|
237
|
-
depthCache.set(stepId, 0);
|
|
238
|
-
return 0;
|
|
239
|
-
}
|
|
240
|
-
const d = Math.min(1e3, computeDepth(parent) + 1);
|
|
241
|
-
depthCache.set(stepId, d);
|
|
242
|
-
return d;
|
|
243
|
-
};
|
|
244
|
-
for (const [id, s] of steps.entries()) {
|
|
245
|
-
totalSteps += 1;
|
|
246
|
-
if (s.type === "llm") llmSteps += 1;
|
|
247
|
-
else if (s.type === "tool") toolSteps += 1;
|
|
248
|
-
else logicSteps += 1;
|
|
249
|
-
if (s.status === "error") errorSteps += 1;
|
|
250
|
-
const depth = computeDepth(id);
|
|
251
|
-
if (depth > maxDepth) maxDepth = depth;
|
|
252
|
-
if (typeof s.durationMs === "number" && Number.isFinite(s.durationMs)) {
|
|
253
|
-
if (!longestStep || s.durationMs > longestStep.durationMs) {
|
|
254
|
-
longestStep = { name: s.name, durationMs: s.durationMs, type: s.type };
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
if (s.tokensInput !== void 0 || s.tokensOutput !== void 0 || s.tokensTotal !== void 0 || s.tokensCached !== void 0) {
|
|
258
|
-
tokenBearingSteps += 1;
|
|
259
|
-
if (s.tokensInput !== void 0) totalTokensInput += s.tokensInput;
|
|
260
|
-
if (s.tokensOutput !== void 0) totalTokensOutput += s.tokensOutput;
|
|
261
|
-
if (s.tokensTotal !== void 0) {
|
|
262
|
-
totalTokensTotal += s.tokensTotal;
|
|
263
|
-
stepsWithKnownTotal += 1;
|
|
264
|
-
} else if (s.tokensInput !== void 0 && s.tokensOutput !== void 0) {
|
|
265
|
-
totalTokensTotal += s.tokensInput + s.tokensOutput;
|
|
266
|
-
stepsWithKnownTotal += 1;
|
|
267
|
-
}
|
|
268
|
-
if (s.tokensCached !== void 0) {
|
|
269
|
-
totalTokensCached += s.tokensCached;
|
|
270
|
-
hasCachedTokens = true;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
const summary = {
|
|
275
|
-
runId,
|
|
276
|
-
name,
|
|
277
|
-
status,
|
|
278
|
-
durationMs,
|
|
279
|
-
totalSteps,
|
|
280
|
-
llmSteps,
|
|
281
|
-
toolSteps,
|
|
282
|
-
logicSteps,
|
|
283
|
-
errorSteps,
|
|
284
|
-
maxDepth,
|
|
285
|
-
...longestStep ? { longestStep } : {},
|
|
286
|
-
...tokenBearingSteps > 0 ? {
|
|
287
|
-
totalTokens: {
|
|
288
|
-
input: totalTokensInput,
|
|
289
|
-
output: totalTokensOutput,
|
|
290
|
-
...stepsWithKnownTotal === tokenBearingSteps ? { total: totalTokensTotal } : {},
|
|
291
|
-
...hasCachedTokens ? { cached: totalTokensCached } : {}
|
|
292
|
-
}
|
|
293
|
-
} : {}
|
|
294
|
-
};
|
|
295
|
-
return summary;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// packages/core/src/trace-filter.ts
|
|
299
|
-
function toLower(s) {
|
|
300
|
-
return typeof s === "string" ? s.toLowerCase() : "";
|
|
301
|
-
}
|
|
302
|
-
function filterTraces(traces, options) {
|
|
303
|
-
const input = [...traces];
|
|
304
|
-
let out = input.filter((t) => {
|
|
305
|
-
if (options.status && t.status !== options.status) return false;
|
|
306
|
-
if (options.name) {
|
|
307
|
-
const q = options.name.toLowerCase();
|
|
308
|
-
const hay = `${toLower(t.name)} ${toLower(t.runId)}`;
|
|
309
|
-
if (!hay.includes(q)) return false;
|
|
310
|
-
}
|
|
311
|
-
if (options.since) {
|
|
312
|
-
const windowMs = parseDuration(options.since);
|
|
313
|
-
const cutoff = Date.now() - windowMs;
|
|
314
|
-
const started = typeof t.startedAt === "number" ? t.startedAt : void 0;
|
|
315
|
-
const basis = started ?? t.createdAt.getTime();
|
|
316
|
-
if (!Number.isFinite(basis) || basis < cutoff) return false;
|
|
317
|
-
}
|
|
318
|
-
return true;
|
|
319
|
-
});
|
|
320
|
-
out.sort((a, b) => {
|
|
321
|
-
const aTime = (typeof a.startedAt === "number" ? a.startedAt : void 0) ?? a.createdAt.getTime();
|
|
322
|
-
const bTime = (typeof b.startedAt === "number" ? b.startedAt : void 0) ?? b.createdAt.getTime();
|
|
323
|
-
return bTime - aTime;
|
|
324
|
-
});
|
|
325
|
-
if (typeof options.limit === "number" && Number.isFinite(options.limit)) {
|
|
326
|
-
const n = Math.max(0, Math.floor(options.limit));
|
|
327
|
-
out = out.slice(0, n);
|
|
328
|
-
}
|
|
329
|
-
return out;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// packages/core/src/timeline.ts
|
|
333
|
-
function finite(n) {
|
|
334
|
-
return typeof n === "number" && Number.isFinite(n);
|
|
335
|
-
}
|
|
336
|
-
function pickStreamingMeta(metadata) {
|
|
337
|
-
if (!metadata || typeof metadata !== "object") return void 0;
|
|
338
|
-
const chunkCount = metadata.chunkCount;
|
|
339
|
-
const streamDurationMs = metadata.streamDurationMs;
|
|
340
|
-
const streamedCharCount = metadata.streamedCharCount;
|
|
341
|
-
if (!finite(chunkCount) && !finite(streamDurationMs) && !finite(streamedCharCount)) {
|
|
342
|
-
return void 0;
|
|
343
|
-
}
|
|
344
|
-
return {
|
|
345
|
-
...finite(chunkCount) ? { chunkCount } : {},
|
|
346
|
-
...finite(streamDurationMs) ? { streamDurationMs } : {},
|
|
347
|
-
...finite(streamedCharCount) ? { streamedCharCount } : {}
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
function pickCorrelation(metadata) {
|
|
351
|
-
if (!metadata || typeof metadata !== "object") return void 0;
|
|
352
|
-
const out = {};
|
|
353
|
-
for (const key of [
|
|
354
|
-
"correlationId",
|
|
355
|
-
"requestId",
|
|
356
|
-
"decisionId",
|
|
357
|
-
"groupId"
|
|
358
|
-
]) {
|
|
359
|
-
const v = metadata[key];
|
|
360
|
-
if (typeof v === "string" && v.trim() !== "") {
|
|
361
|
-
out[key] = v;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
365
|
-
}
|
|
366
|
-
function buildRunTimeline(events, options = {}) {
|
|
367
|
-
const started = events.find(
|
|
368
|
-
(e) => e.event === "run_started"
|
|
369
|
-
);
|
|
370
|
-
const completed = events.filter(
|
|
371
|
-
(e) => e.event === "run_completed"
|
|
372
|
-
);
|
|
373
|
-
const lastCompleted = completed[completed.length - 1];
|
|
374
|
-
const runId = started?.runId ?? events.find((e) => typeof e.runId === "string")?.runId ?? "unknown-run";
|
|
375
|
-
const runStart = started && finite(started.startTime) ? started.startTime : started && finite(started.timestamp) ? started.timestamp : void 0;
|
|
376
|
-
const status = lastCompleted ? lastCompleted.status : started ? "running" : "unknown";
|
|
377
|
-
const steps = /* @__PURE__ */ new Map();
|
|
378
|
-
for (const e of events) {
|
|
379
|
-
if (e.event === "step_started") {
|
|
380
|
-
const s = e;
|
|
381
|
-
steps.set(s.stepId, {
|
|
382
|
-
name: s.name,
|
|
383
|
-
type: s.type,
|
|
384
|
-
parentId: s.parentId,
|
|
385
|
-
startedAt: finite(s.startTime) ? s.startTime : s.timestamp,
|
|
386
|
-
status: "running",
|
|
387
|
-
metadata: s.metadata
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
for (const e of events) {
|
|
392
|
-
if (e.event !== "step_completed") continue;
|
|
393
|
-
const c = e;
|
|
394
|
-
const node = steps.get(c.stepId);
|
|
395
|
-
if (!node) continue;
|
|
396
|
-
node.status = c.status;
|
|
397
|
-
if (finite(c.durationMs)) node.durationMs = c.durationMs;
|
|
398
|
-
}
|
|
399
|
-
const depthCache = /* @__PURE__ */ new Map();
|
|
400
|
-
const computeDepth = (stepId) => {
|
|
401
|
-
const cached = depthCache.get(stepId);
|
|
402
|
-
if (cached !== void 0) return cached;
|
|
403
|
-
const node = steps.get(stepId);
|
|
404
|
-
if (!node) return 0;
|
|
405
|
-
const parent = node.parentId;
|
|
406
|
-
if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
|
|
407
|
-
depthCache.set(stepId, 0);
|
|
408
|
-
return 0;
|
|
409
|
-
}
|
|
410
|
-
const d = Math.min(1e3, computeDepth(parent) + 1);
|
|
411
|
-
depthCache.set(stepId, d);
|
|
412
|
-
return d;
|
|
413
|
-
};
|
|
414
|
-
const entries = [];
|
|
415
|
-
for (const [stepId, s] of steps.entries()) {
|
|
416
|
-
const offsetMs = runStart !== void 0 && finite(s.startedAt) ? Math.max(0, s.startedAt - runStart) : 0;
|
|
417
|
-
entries.push({
|
|
418
|
-
stepId,
|
|
419
|
-
name: s.name,
|
|
420
|
-
type: s.type,
|
|
421
|
-
status: s.status,
|
|
422
|
-
depth: computeDepth(stepId),
|
|
423
|
-
startedAt: s.startedAt,
|
|
424
|
-
offsetMs,
|
|
425
|
-
durationMs: s.durationMs,
|
|
426
|
-
isError: s.status === "error",
|
|
427
|
-
streaming: pickStreamingMeta(s.metadata)
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
entries.sort((a, b) => a.startedAt - b.startedAt);
|
|
431
|
-
const slowTopN = options.slowTopN ?? 3;
|
|
432
|
-
if (options.focus === "slow" && entries.length > 0) {
|
|
433
|
-
const ranked = [...entries].filter((e) => finite(e.durationMs)).sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0));
|
|
434
|
-
const slowIds = new Set(
|
|
435
|
-
ranked.slice(0, slowTopN).map((e) => e.stepId)
|
|
436
|
-
);
|
|
437
|
-
for (const e of entries) {
|
|
438
|
-
if (slowIds.has(e.stepId)) e.slow = true;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
return {
|
|
442
|
-
runId,
|
|
443
|
-
name: typeof started?.name === "string" && started.name.trim() !== "" ? started.name : void 0,
|
|
444
|
-
status,
|
|
445
|
-
startedAt: runStart,
|
|
446
|
-
endedAt: lastCompleted && finite(lastCompleted.endTime) ? lastCompleted.endTime : void 0,
|
|
447
|
-
durationMs: lastCompleted && finite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0,
|
|
448
|
-
correlation: pickCorrelation(
|
|
449
|
-
started?.metadata
|
|
450
|
-
),
|
|
451
|
-
entries
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
function renderTimeline(timeline, options = {}) {
|
|
455
|
-
const lines = [];
|
|
456
|
-
lines.push(`Timeline: ${timeline.name ?? timeline.runId}`);
|
|
457
|
-
lines.push(`Run ID: ${timeline.runId}`);
|
|
458
|
-
lines.push(`Status: ${timeline.status}`);
|
|
459
|
-
if (timeline.startedAt !== void 0) {
|
|
460
|
-
lines.push(`Started: ${formatTimestamp(timeline.startedAt)}`);
|
|
461
|
-
}
|
|
462
|
-
if (timeline.durationMs !== void 0) {
|
|
463
|
-
lines.push(`Duration: ${formatDuration(timeline.durationMs)}`);
|
|
464
|
-
}
|
|
465
|
-
if (timeline.correlation) {
|
|
466
|
-
const parts = Object.entries(timeline.correlation).filter(([, v]) => typeof v === "string").map(([k, v]) => `${k}=${v}`);
|
|
467
|
-
if (parts.length > 0) {
|
|
468
|
-
lines.push(`Correlation: ${parts.join(", ")}`);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
lines.push("");
|
|
472
|
-
lines.push("Steps (chronological):");
|
|
473
|
-
const show = timeline.entries.filter((e) => {
|
|
474
|
-
if (options.focus === "slow") return e.slow === true;
|
|
475
|
-
return true;
|
|
476
|
-
});
|
|
477
|
-
if (show.length === 0) {
|
|
478
|
-
lines.push(
|
|
479
|
-
options.focus === "slow" ? "(no steps with duration for slow focus)" : "(no steps)"
|
|
480
|
-
);
|
|
481
|
-
return lines.join("\n");
|
|
482
|
-
}
|
|
483
|
-
for (const e of show) {
|
|
484
|
-
const prefix = e.slow ? "[slow] " : "";
|
|
485
|
-
const typeTag = e.type === "llm" ? "llm" : e.type === "tool" ? "tool" : e.type;
|
|
486
|
-
const dur = e.durationMs !== void 0 ? formatDuration(e.durationMs) : "-";
|
|
487
|
-
const err = e.isError ? " error" : "";
|
|
488
|
-
const off = formatDuration(e.offsetMs);
|
|
489
|
-
let line = `${prefix}+${off} ${typeTag}:${e.name} (${dur})${err}`;
|
|
490
|
-
if (e.streaming?.chunkCount !== void 0) {
|
|
491
|
-
line += ` chunks=${e.streaming.chunkCount}`;
|
|
492
|
-
}
|
|
493
|
-
if (e.streaming?.streamDurationMs !== void 0) {
|
|
494
|
-
line += ` stream=${formatDuration(e.streaming.streamDurationMs)}`;
|
|
495
|
-
}
|
|
496
|
-
lines.push(line);
|
|
497
|
-
}
|
|
498
|
-
return lines.join("\n");
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// packages/core/src/what.ts
|
|
502
|
-
function pickCorrelation2(metadata) {
|
|
503
|
-
if (!metadata) return void 0;
|
|
504
|
-
const out = {};
|
|
505
|
-
for (const key of [
|
|
506
|
-
"correlationId",
|
|
507
|
-
"requestId",
|
|
508
|
-
"decisionId",
|
|
509
|
-
"groupId"
|
|
510
|
-
]) {
|
|
511
|
-
const value = metadata[key];
|
|
512
|
-
if (typeof value === "string" && value.trim() !== "") {
|
|
513
|
-
out[key] = value;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
517
|
-
}
|
|
518
|
-
function stepMixLine(summary) {
|
|
519
|
-
const parts = [];
|
|
520
|
-
if (summary.llmSteps > 0) parts.push(`${summary.llmSteps} LLM`);
|
|
521
|
-
if (summary.toolSteps > 0) parts.push(`${summary.toolSteps} tool`);
|
|
522
|
-
if (summary.logicSteps > 0) parts.push(`${summary.logicSteps} logic`);
|
|
523
|
-
return parts.length > 0 ? parts.join(", ") : "none";
|
|
524
|
-
}
|
|
525
|
-
function outcomeLine(summary) {
|
|
526
|
-
if (summary.status === "success") {
|
|
527
|
-
return summary.errorSteps > 0 ? "Completed with step errors recorded." : "Completed successfully.";
|
|
528
|
-
}
|
|
529
|
-
if (summary.status === "error") {
|
|
530
|
-
if (summary.failedStepNames.length > 0) {
|
|
531
|
-
const names = summary.failedStepNames.slice(0, 3).join(", ");
|
|
532
|
-
const suffix = summary.failedStepNames.length > 3 ? ` (+${summary.failedStepNames.length - 3} more)` : "";
|
|
533
|
-
return `Failed at step(s): ${names}${suffix}.`;
|
|
534
|
-
}
|
|
535
|
-
if (summary.runErrorMessage) {
|
|
536
|
-
return `Run failed: ${summary.runErrorMessage}`;
|
|
537
|
-
}
|
|
538
|
-
return "Run failed.";
|
|
539
|
-
}
|
|
540
|
-
if (summary.status === "running") {
|
|
541
|
-
return "Run is still in progress (no run_completed).";
|
|
542
|
-
}
|
|
543
|
-
return "Outcome unknown \u2014 inspect events may be incomplete.";
|
|
544
|
-
}
|
|
545
|
-
function buildRunWhatSummary(events) {
|
|
546
|
-
const base = buildRunSummary(events);
|
|
547
|
-
const started = events.find(
|
|
548
|
-
(e) => e.event === "run_started"
|
|
549
|
-
);
|
|
550
|
-
const completed = events.filter(
|
|
551
|
-
(e) => e.event === "run_completed"
|
|
552
|
-
);
|
|
553
|
-
const lastCompleted = completed[completed.length - 1];
|
|
554
|
-
const failedStepNames = [];
|
|
555
|
-
const stepNames = /* @__PURE__ */ new Map();
|
|
556
|
-
for (const e of events) {
|
|
557
|
-
if (e.event === "step_started") {
|
|
558
|
-
const s = e;
|
|
559
|
-
stepNames.set(s.stepId, s.name);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
for (const e of events) {
|
|
563
|
-
if (e.event === "step_completed") {
|
|
564
|
-
const sc = e;
|
|
565
|
-
if (sc.status === "error") {
|
|
566
|
-
failedStepNames.push(stepNames.get(sc.stepId) ?? sc.stepId);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
return {
|
|
571
|
-
runId: base.runId,
|
|
572
|
-
name: base.name,
|
|
573
|
-
status: base.status,
|
|
574
|
-
durationMs: base.durationMs,
|
|
575
|
-
totalSteps: base.totalSteps,
|
|
576
|
-
llmSteps: base.llmSteps,
|
|
577
|
-
toolSteps: base.toolSteps,
|
|
578
|
-
logicSteps: base.logicSteps,
|
|
579
|
-
errorSteps: base.errorSteps,
|
|
580
|
-
maxDepth: base.maxDepth,
|
|
581
|
-
longestStep: base.longestStep,
|
|
582
|
-
totalTokens: base.totalTokens,
|
|
583
|
-
correlation: pickCorrelation2(started?.metadata),
|
|
584
|
-
failedStepNames,
|
|
585
|
-
runErrorMessage: lastCompleted?.error?.message
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
function renderRunWhat(summary, options = {}) {
|
|
589
|
-
const showCorrelation = options.correlation !== false;
|
|
590
|
-
const lines = [];
|
|
591
|
-
const label = summary.name ?? summary.runId;
|
|
592
|
-
lines.push(`What: ${label}`);
|
|
593
|
-
const duration = summary.durationMs !== void 0 ? formatDuration(summary.durationMs) : "\u2014";
|
|
594
|
-
lines.push(
|
|
595
|
-
`Status: ${summary.status} \xB7 Duration: ${duration} \xB7 Steps: ${summary.totalSteps} (${stepMixLine(summary)})`
|
|
596
|
-
);
|
|
597
|
-
if (summary.totalTokens) {
|
|
598
|
-
const tokenParts = [
|
|
599
|
-
`${summary.totalTokens.input} in`,
|
|
600
|
-
`${summary.totalTokens.output} out`
|
|
601
|
-
];
|
|
602
|
-
if (summary.totalTokens.total !== void 0) {
|
|
603
|
-
tokenParts.push(`${summary.totalTokens.total} total`);
|
|
604
|
-
}
|
|
605
|
-
if (summary.totalTokens.cached !== void 0) {
|
|
606
|
-
tokenParts.push(`${summary.totalTokens.cached} cached`);
|
|
607
|
-
}
|
|
608
|
-
lines.push(`Tokens: ${tokenParts.join(" / ")}`);
|
|
609
|
-
}
|
|
610
|
-
if (showCorrelation && summary.correlation) {
|
|
611
|
-
const parts = [];
|
|
612
|
-
if (summary.correlation.correlationId) {
|
|
613
|
-
parts.push(`correlationId=${summary.correlation.correlationId}`);
|
|
614
|
-
}
|
|
615
|
-
if (summary.correlation.requestId) {
|
|
616
|
-
parts.push(`requestId=${summary.correlation.requestId}`);
|
|
617
|
-
}
|
|
618
|
-
if (summary.correlation.decisionId) {
|
|
619
|
-
parts.push(`decisionId=${summary.correlation.decisionId}`);
|
|
620
|
-
}
|
|
621
|
-
if (summary.correlation.groupId) {
|
|
622
|
-
parts.push(`groupId=${summary.correlation.groupId}`);
|
|
623
|
-
}
|
|
624
|
-
if (parts.length > 0) {
|
|
625
|
-
lines.push(`Correlation: ${parts.join(", ")}`);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
lines.push(`Outcome: ${outcomeLine(summary)}`);
|
|
629
|
-
if (summary.longestStep && summary.totalSteps > 0) {
|
|
630
|
-
lines.push(
|
|
631
|
-
`Slowest: ${summary.longestStep.name} (${formatDuration(summary.longestStep.durationMs)}, ${summary.longestStep.type})`
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
if (summary.maxDepth > 0) {
|
|
635
|
-
lines.push(`Max depth: ${summary.maxDepth}`);
|
|
636
|
-
}
|
|
637
|
-
return lines.join("\n");
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// packages/core/src/report.ts
|
|
641
|
-
function resolveTree(events, profile) {
|
|
642
|
-
const tree = manualTraceEventsToRunTree(events);
|
|
643
|
-
return profile === "local" ? tree : redactRunTreeForExport(tree, { redactionProfile: profile });
|
|
644
|
-
}
|
|
645
|
-
function tailMarkdownSection(content, heading) {
|
|
646
|
-
const marker = `## ${heading}`;
|
|
647
|
-
const idx = content.indexOf(marker);
|
|
648
|
-
if (idx < 0) return "";
|
|
649
|
-
return content.slice(idx).trimEnd();
|
|
650
|
-
}
|
|
651
|
-
function extractHtmlFragment(content, start, end) {
|
|
652
|
-
const startIdx = content.indexOf(start);
|
|
653
|
-
if (startIdx < 0) return "";
|
|
654
|
-
const endIdx = content.indexOf(end, startIdx + start.length);
|
|
655
|
-
if (endIdx < 0) return content.slice(startIdx);
|
|
656
|
-
return content.slice(startIdx, endIdx);
|
|
657
|
-
}
|
|
658
|
-
var REPORT_HTML_CSS = `
|
|
659
|
-
body{font-family:system-ui,sans-serif;line-height:1.5;margin:1.5rem;max-width:960px;color:#111}
|
|
660
|
-
h1{font-size:1.35rem}
|
|
661
|
-
h2{font-size:1.1rem;margin-top:1.5rem}
|
|
662
|
-
pre{white-space:pre-wrap;background:#f8f8f8;padding:0.75rem;overflow:auto;font-size:0.9rem}
|
|
663
|
-
ul.tree{list-style:none;padding-left:1rem}
|
|
664
|
-
ul.tree.nested{padding-left:1.25rem;border-left:1px solid #ddd;margin:0.25rem 0}
|
|
665
|
-
.nm{font-weight:600}
|
|
666
|
-
.meta{color:#555;font-size:0.9rem}
|
|
667
|
-
footer{margin-top:2rem;font-size:0.85rem;color:#555}
|
|
668
|
-
`.trim();
|
|
669
|
-
function buildRunReport(events, options) {
|
|
670
|
-
const profile = options.redactionProfile ?? "local";
|
|
671
|
-
const safeEvents = redactTraceEventsForReport(events, {
|
|
672
|
-
redactionProfile: profile
|
|
673
|
-
});
|
|
674
|
-
const whatSummary = buildRunWhatSummary(safeEvents);
|
|
675
|
-
const whatText = renderRunWhat(whatSummary, {
|
|
676
|
-
correlation: options.correlation !== false
|
|
677
|
-
});
|
|
678
|
-
const timelineText = renderTimeline(buildRunTimeline(safeEvents));
|
|
679
|
-
const tree = resolveTree(safeEvents, profile);
|
|
680
|
-
const exportOpts = {
|
|
681
|
-
includeMetadata: false,
|
|
682
|
-
includeAttributes: options.includeAttributes === true,
|
|
683
|
-
includeErrors: options.includeErrors !== false,
|
|
684
|
-
redacted: true,
|
|
685
|
-
redactionProfile: profile,
|
|
686
|
-
maxAttributeLength: 500
|
|
687
|
-
};
|
|
688
|
-
if (options.format === "markdown") {
|
|
689
|
-
const treeMd = exportMarkdown(tree, exportOpts);
|
|
690
|
-
const tail = tailMarkdownSection(treeMd.content, "Execution tree");
|
|
691
|
-
const title2 = escapeMarkdown(whatSummary.name ?? whatSummary.runId);
|
|
692
|
-
const lines = [
|
|
693
|
-
`# AgentInspect Report: ${title2}`,
|
|
694
|
-
"",
|
|
695
|
-
"Generated locally by AgentInspect. Review for sensitive data before sharing.",
|
|
696
|
-
"",
|
|
697
|
-
"## What happened",
|
|
698
|
-
"",
|
|
699
|
-
"```text",
|
|
700
|
-
whatText,
|
|
701
|
-
"```",
|
|
702
|
-
"",
|
|
703
|
-
"## Timeline",
|
|
704
|
-
"",
|
|
705
|
-
"```text",
|
|
706
|
-
timelineText,
|
|
707
|
-
"```",
|
|
708
|
-
""
|
|
709
|
-
];
|
|
710
|
-
if (tail) {
|
|
711
|
-
lines.push(tail, "");
|
|
712
|
-
} else {
|
|
713
|
-
lines.push("## Execution tree", "", "(no steps)", "");
|
|
714
|
-
}
|
|
715
|
-
return {
|
|
716
|
-
format: "markdown",
|
|
717
|
-
content: lines.join("\n"),
|
|
718
|
-
contentType: "text/markdown",
|
|
719
|
-
fileExtension: ".md"
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
const treeHtml = exportHtml(tree, exportOpts);
|
|
723
|
-
const title = escapeHtml(whatSummary.name ?? whatSummary.runId);
|
|
724
|
-
const treeSection = extractHtmlFragment(
|
|
725
|
-
treeHtml.content,
|
|
726
|
-
'<section class="tree">',
|
|
727
|
-
"</section>"
|
|
728
|
-
);
|
|
729
|
-
const errorsSection = treeHtml.content.includes("<h2>Errors</h2>") ? extractHtmlFragment(treeHtml.content, "<h2>Errors</h2>", "<footer>") : "";
|
|
730
|
-
const attrsSection = treeHtml.content.includes("<h2>Attributes (bounded)</h2>") ? extractHtmlFragment(
|
|
731
|
-
treeHtml.content,
|
|
732
|
-
"<h2>Attributes (bounded)</h2>",
|
|
733
|
-
"<footer>"
|
|
734
|
-
) : "";
|
|
735
|
-
const html = `<!doctype html>
|
|
736
|
-
<html lang="en">
|
|
737
|
-
<head>
|
|
738
|
-
<meta charset="utf-8"/>
|
|
739
|
-
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
740
|
-
<title>AgentInspect Report: ${title}</title>
|
|
741
|
-
<style>${REPORT_HTML_CSS}</style>
|
|
742
|
-
</head>
|
|
743
|
-
<body>
|
|
744
|
-
<header><h1>AgentInspect Report: ${title}</h1></header>
|
|
745
|
-
<p class="note">Generated locally by AgentInspect. Review for sensitive data before sharing.</p>
|
|
746
|
-
<section class="what"><h2>What happened</h2><pre>${escapeHtml(whatText)}</pre></section>
|
|
747
|
-
<section class="timeline"><h2>Timeline</h2><pre>${escapeHtml(timelineText)}</pre></section>
|
|
748
|
-
${treeSection || '<section class="tree"><h2>Execution tree</h2><p>No steps recorded.</p></section>'}
|
|
749
|
-
${errorsSection}
|
|
750
|
-
${attrsSection}
|
|
751
|
-
<footer>Generated locally by AgentInspect. Review for sensitive data before sharing.</footer>
|
|
752
|
-
</body>
|
|
753
|
-
</html>`;
|
|
754
|
-
return {
|
|
755
|
-
format: "html",
|
|
756
|
-
content: html,
|
|
757
|
-
contentType: "text/html",
|
|
758
|
-
fileExtension: ".html"
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
// packages/core/src/explain.ts
|
|
763
|
-
function flatten(nodes, out = []) {
|
|
764
|
-
for (const node of nodes) {
|
|
765
|
-
out.push({ node, index: out.length + 1 });
|
|
766
|
-
flatten(node.children, out);
|
|
767
|
-
}
|
|
768
|
-
return out;
|
|
769
|
-
}
|
|
770
|
-
function redactValue(redactor, key, value) {
|
|
771
|
-
return redactor.redactValue(key, value);
|
|
772
|
-
}
|
|
773
|
-
function fact(id, label, value, redactor) {
|
|
774
|
-
return {
|
|
775
|
-
id,
|
|
776
|
-
label,
|
|
777
|
-
value: redactValue(redactor, id.split(".").at(-1) ?? id, value),
|
|
778
|
-
source: "trace",
|
|
779
|
-
confidence: "observed"
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
function topKinds(run) {
|
|
783
|
-
return Object.entries(run.metadata.kinds).filter(([, count]) => count > 0).sort((a, b) => {
|
|
784
|
-
if (b[1] !== a[1]) return b[1] - a[1];
|
|
785
|
-
return a[0].localeCompare(b[0]);
|
|
786
|
-
}).slice(0, 5).map(([kind, count]) => `${kind}:${count}`);
|
|
787
|
-
}
|
|
788
|
-
function countErrorNodes(nodes) {
|
|
789
|
-
return nodes.filter((entry) => entry.node.event.status === "error").length;
|
|
790
|
-
}
|
|
791
|
-
function slowestNode(nodes) {
|
|
792
|
-
return nodes.filter((entry) => entry.node.event.durationMs !== void 0).sort((a, b) => {
|
|
793
|
-
const delta = (b.node.event.durationMs ?? 0) - (a.node.event.durationMs ?? 0);
|
|
794
|
-
return delta !== 0 ? delta : a.index - b.index;
|
|
795
|
-
})[0];
|
|
796
|
-
}
|
|
797
|
-
function attributeFacts(nodes, redactor) {
|
|
798
|
-
const facts = [];
|
|
799
|
-
for (const entry of nodes) {
|
|
800
|
-
const attrs = entry.node.event.attributes;
|
|
801
|
-
if (attrs === void 0) continue;
|
|
802
|
-
for (const key of Object.keys(attrs).sort()) {
|
|
803
|
-
facts.push({
|
|
804
|
-
id: `node.${entry.index}.attributes.${key}`,
|
|
805
|
-
label: `${entry.node.event.name} attribute ${key}`,
|
|
806
|
-
value: redactValue(redactor, key, attrs[key]),
|
|
807
|
-
source: "trace",
|
|
808
|
-
confidence: "observed"
|
|
809
|
-
});
|
|
810
|
-
if (facts.length >= 8) return facts;
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
return facts;
|
|
814
|
-
}
|
|
815
|
-
function buildFacts(run, redactor) {
|
|
816
|
-
const nodes = flatten(run.children);
|
|
817
|
-
const facts = [
|
|
818
|
-
fact("run.id", "Run id", run.runId, redactor),
|
|
819
|
-
fact("run.name", "Run name", run.name ?? run.runId, redactor),
|
|
820
|
-
fact("run.status", "Run status", run.status ?? "unknown", redactor),
|
|
821
|
-
fact("run.totalEvents", "Total events", run.metadata.totalEvents, redactor),
|
|
822
|
-
fact("run.stepCount", "Top-level step count", run.children.length, redactor),
|
|
823
|
-
fact("run.nodeCount", "Total node count", nodes.length, redactor),
|
|
824
|
-
fact("run.errorNodeCount", "Error node count", countErrorNodes(nodes), redactor),
|
|
825
|
-
fact("run.kinds", "Observed kind mix", topKinds(run), redactor)
|
|
826
|
-
];
|
|
827
|
-
if (run.durationMs !== void 0) {
|
|
828
|
-
facts.push(fact("run.durationMs", "Run duration milliseconds", run.durationMs, redactor));
|
|
829
|
-
}
|
|
830
|
-
const slowest = slowestNode(nodes);
|
|
831
|
-
if (slowest !== void 0) {
|
|
832
|
-
facts.push(
|
|
833
|
-
fact("run.slowestNode", "Slowest observed node", {
|
|
834
|
-
name: slowest.node.event.name,
|
|
835
|
-
kind: slowest.node.event.kind,
|
|
836
|
-
durationMs: slowest.node.event.durationMs
|
|
837
|
-
}, redactor)
|
|
838
|
-
);
|
|
839
|
-
}
|
|
840
|
-
facts.push(...attributeFacts(nodes, redactor));
|
|
841
|
-
return facts;
|
|
842
|
-
}
|
|
843
|
-
function buildInferences(run, facts) {
|
|
844
|
-
const inferences = [];
|
|
845
|
-
const errorFact = facts.find((item) => item.id === "run.errorNodeCount");
|
|
846
|
-
const kindFact = facts.find((item) => item.id === "run.kinds");
|
|
847
|
-
const durationFact = facts.find((item) => item.id === "run.durationMs");
|
|
848
|
-
const errorNodeCount = typeof errorFact?.value === "number" ? errorFact.value : 0;
|
|
849
|
-
if (run.status === "error" || errorNodeCount > 0) {
|
|
850
|
-
inferences.push({
|
|
851
|
-
id: "outcome.error",
|
|
852
|
-
label: "Outcome",
|
|
853
|
-
text: "The run recorded an error status or at least one error node.",
|
|
854
|
-
evidence: ["run.status", "run.errorNodeCount"],
|
|
855
|
-
confidence: "deterministic"
|
|
856
|
-
});
|
|
857
|
-
} else if (run.status === "ok") {
|
|
858
|
-
inferences.push({
|
|
859
|
-
id: "outcome.success",
|
|
860
|
-
label: "Outcome",
|
|
861
|
-
text: "The run completed without observed error nodes.",
|
|
862
|
-
evidence: ["run.status", "run.errorNodeCount"],
|
|
863
|
-
confidence: "deterministic"
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
if (kindFact !== void 0) {
|
|
867
|
-
inferences.push({
|
|
868
|
-
id: "shape.kind-mix",
|
|
869
|
-
label: "Trace shape",
|
|
870
|
-
text: "The explanation is based on the observed event kind mix, not generated content.",
|
|
871
|
-
evidence: [kindFact.id],
|
|
872
|
-
confidence: "deterministic"
|
|
873
|
-
});
|
|
874
|
-
}
|
|
875
|
-
if (durationFact !== void 0) {
|
|
876
|
-
inferences.push({
|
|
877
|
-
id: "timing.duration",
|
|
878
|
-
label: "Timing",
|
|
879
|
-
text: "Timing claims are limited to persisted duration fields in the trace.",
|
|
880
|
-
evidence: [durationFact.id],
|
|
881
|
-
confidence: "deterministic"
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
return inferences;
|
|
885
|
-
}
|
|
886
|
-
function buildLocalExplanation(run, options = {}) {
|
|
887
|
-
const redactionProfile = options.redactionProfile ?? "local";
|
|
888
|
-
const resolved = resolveRedactionProfile(redactionProfile);
|
|
889
|
-
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
890
|
-
const mode = options.mode ?? "local";
|
|
891
|
-
const facts = buildFacts(run, redactor);
|
|
892
|
-
return {
|
|
893
|
-
mode,
|
|
894
|
-
runId: String(redactValue(redactor, "runId", run.runId)),
|
|
895
|
-
...run.name !== void 0 ? { name: String(redactValue(redactor, "name", run.name)) } : {},
|
|
896
|
-
...run.status !== void 0 ? { status: run.status } : {},
|
|
897
|
-
redactionProfile,
|
|
898
|
-
facts,
|
|
899
|
-
inferences: mode === "dry-run" ? [] : buildInferences(run, facts),
|
|
900
|
-
notes: [
|
|
901
|
-
"Generated locally without provider or network calls.",
|
|
902
|
-
"Facts are observed from normalized trace data; inferences are deterministic labels."
|
|
903
|
-
]
|
|
904
|
-
};
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
// packages/core/src/stats.ts
|
|
908
|
-
function percentile(sorted, p) {
|
|
909
|
-
if (sorted.length === 0) return void 0;
|
|
910
|
-
const idx = Math.min(
|
|
911
|
-
sorted.length - 1,
|
|
912
|
-
Math.max(0, Math.ceil(p / 100 * sorted.length) - 1)
|
|
913
|
-
);
|
|
914
|
-
return sorted[idx];
|
|
915
|
-
}
|
|
916
|
-
async function readRunStartedMetadata(filePath) {
|
|
917
|
-
try {
|
|
918
|
-
const events = await readTraceEventsFromFile(filePath);
|
|
919
|
-
for (const event of events) {
|
|
920
|
-
if (event.event !== "run_started") continue;
|
|
921
|
-
const rs = event;
|
|
922
|
-
if (rs.metadata && typeof rs.metadata === "object") {
|
|
923
|
-
return rs.metadata;
|
|
924
|
-
}
|
|
925
|
-
return void 0;
|
|
926
|
-
}
|
|
927
|
-
} catch {
|
|
928
|
-
}
|
|
929
|
-
return void 0;
|
|
930
|
-
}
|
|
931
|
-
function metaMatchesCorrelation(metadata, correlationId, groupId) {
|
|
932
|
-
if (correlationId) {
|
|
933
|
-
const v = metadata?.correlationId;
|
|
934
|
-
if (typeof v !== "string" || v !== correlationId) return false;
|
|
935
|
-
}
|
|
936
|
-
if (groupId) {
|
|
937
|
-
const v = metadata?.groupId;
|
|
938
|
-
if (typeof v !== "string" || v !== groupId) return false;
|
|
939
|
-
}
|
|
940
|
-
return true;
|
|
941
|
-
}
|
|
942
|
-
async function buildTraceStats(metas, options) {
|
|
943
|
-
let filtered = filterTraces(metas, { since: options.since });
|
|
944
|
-
if (options.correlationId || options.groupId) {
|
|
945
|
-
const next = [];
|
|
946
|
-
for (const m of filtered) {
|
|
947
|
-
const md = await readRunStartedMetadata(m.filePath);
|
|
948
|
-
if (metaMatchesCorrelation(md, options.correlationId, options.groupId)) {
|
|
949
|
-
next.push(m);
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
filtered = next;
|
|
953
|
-
}
|
|
954
|
-
let successCount = 0;
|
|
955
|
-
let errorCount = 0;
|
|
956
|
-
let runningCount = 0;
|
|
957
|
-
let unknownCount = 0;
|
|
958
|
-
const durations = [];
|
|
959
|
-
let totalSteps = 0;
|
|
960
|
-
let totalLlmSteps = 0;
|
|
961
|
-
let totalToolSteps = 0;
|
|
962
|
-
let totalErrorSteps = 0;
|
|
963
|
-
const slowestRuns = [];
|
|
964
|
-
const slowestSteps = [];
|
|
965
|
-
for (const m of filtered) {
|
|
966
|
-
if (m.status === "success") successCount += 1;
|
|
967
|
-
else if (m.status === "error") errorCount += 1;
|
|
968
|
-
else if (m.status === "running") runningCount += 1;
|
|
969
|
-
else unknownCount += 1;
|
|
970
|
-
if (typeof m.durationMs === "number" && Number.isFinite(m.durationMs) && m.durationMs >= 0) {
|
|
971
|
-
durations.push(m.durationMs);
|
|
972
|
-
slowestRuns.push({
|
|
973
|
-
runId: m.runId,
|
|
974
|
-
name: m.name,
|
|
975
|
-
durationMs: m.durationMs,
|
|
976
|
-
status: m.status
|
|
977
|
-
});
|
|
978
|
-
}
|
|
979
|
-
try {
|
|
980
|
-
const events = await readTraceEventsFromFile(m.filePath);
|
|
981
|
-
if (events.length === 0) continue;
|
|
982
|
-
const summary = buildRunSummary(events);
|
|
983
|
-
totalSteps += summary.totalSteps;
|
|
984
|
-
totalLlmSteps += summary.llmSteps;
|
|
985
|
-
totalToolSteps += summary.toolSteps;
|
|
986
|
-
totalErrorSteps += summary.errorSteps;
|
|
987
|
-
const steps = collectCompletedSteps(events, m.runId);
|
|
988
|
-
for (const s of steps) {
|
|
989
|
-
slowestSteps.push(s);
|
|
990
|
-
}
|
|
991
|
-
} catch {
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
slowestRuns.sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0));
|
|
995
|
-
slowestSteps.sort((a, b) => b.durationMs - a.durationMs);
|
|
996
|
-
const runLimit = options.slowRunLimit ?? 5;
|
|
997
|
-
const stepLimit = options.slowStepLimit ?? 5;
|
|
998
|
-
const sortedDur = [...durations].sort((a, b) => a - b);
|
|
999
|
-
const totalRuns = filtered.length;
|
|
1000
|
-
const errorRate = totalRuns > 0 ? errorCount / totalRuns : 0;
|
|
1001
|
-
const sumDur = durations.reduce((a, b) => a + b, 0);
|
|
1002
|
-
return {
|
|
1003
|
-
traceDir: options.traceDir,
|
|
1004
|
-
...options.since ? { since: options.since } : {},
|
|
1005
|
-
...options.correlationId ? { correlationId: options.correlationId } : {},
|
|
1006
|
-
...options.groupId ? { groupId: options.groupId } : {},
|
|
1007
|
-
totalRuns,
|
|
1008
|
-
successCount,
|
|
1009
|
-
errorCount,
|
|
1010
|
-
runningCount,
|
|
1011
|
-
unknownCount,
|
|
1012
|
-
errorRate,
|
|
1013
|
-
duration: {
|
|
1014
|
-
...sortedDur.length > 0 ? {
|
|
1015
|
-
minMs: sortedDur[0],
|
|
1016
|
-
maxMs: sortedDur[sortedDur.length - 1],
|
|
1017
|
-
avgMs: sumDur / sortedDur.length,
|
|
1018
|
-
p50Ms: percentile(sortedDur, 50),
|
|
1019
|
-
p95Ms: percentile(sortedDur, 95)
|
|
1020
|
-
} : {}
|
|
1021
|
-
},
|
|
1022
|
-
totalSteps,
|
|
1023
|
-
avgStepsPerRun: totalRuns > 0 ? totalSteps / totalRuns : 0,
|
|
1024
|
-
totalLlmSteps,
|
|
1025
|
-
totalToolSteps,
|
|
1026
|
-
totalErrorSteps,
|
|
1027
|
-
slowestRuns: slowestRuns.slice(0, runLimit),
|
|
1028
|
-
slowestSteps: slowestSteps.slice(0, stepLimit)
|
|
1029
|
-
};
|
|
1030
|
-
}
|
|
1031
|
-
function collectCompletedSteps(events, runId) {
|
|
1032
|
-
const started = /* @__PURE__ */ new Map();
|
|
1033
|
-
const out = [];
|
|
1034
|
-
for (const e of events) {
|
|
1035
|
-
if (e.event === "step_started") {
|
|
1036
|
-
const s = e;
|
|
1037
|
-
started.set(s.stepId, { name: s.name, type: s.type });
|
|
1038
|
-
}
|
|
1039
|
-
if (e.event === "step_completed") {
|
|
1040
|
-
const c = e;
|
|
1041
|
-
if (c.status !== "success" && c.status !== "error") continue;
|
|
1042
|
-
if (typeof c.durationMs !== "number" || !Number.isFinite(c.durationMs)) {
|
|
1043
|
-
continue;
|
|
1044
|
-
}
|
|
1045
|
-
const meta = started.get(c.stepId);
|
|
1046
|
-
out.push({
|
|
1047
|
-
runId,
|
|
1048
|
-
stepName: meta?.name ?? c.stepId,
|
|
1049
|
-
stepType: meta?.type ?? "logic",
|
|
1050
|
-
durationMs: c.durationMs
|
|
1051
|
-
});
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
return out;
|
|
1055
|
-
}
|
|
1056
|
-
function renderTraceStats(stats) {
|
|
1057
|
-
const lines = [];
|
|
1058
|
-
lines.push("Trace stats (local)");
|
|
1059
|
-
lines.push(`Directory: ${stats.traceDir}`);
|
|
1060
|
-
if (stats.since) lines.push(`Since: ${stats.since}`);
|
|
1061
|
-
if (stats.correlationId) lines.push(`Correlation ID: ${stats.correlationId}`);
|
|
1062
|
-
if (stats.groupId) lines.push(`Group ID: ${stats.groupId}`);
|
|
1063
|
-
lines.push("");
|
|
1064
|
-
lines.push(`Runs: ${stats.totalRuns}`);
|
|
1065
|
-
lines.push(
|
|
1066
|
-
` success: ${stats.successCount} error: ${stats.errorCount} running: ${stats.runningCount} unknown: ${stats.unknownCount}`
|
|
1067
|
-
);
|
|
1068
|
-
lines.push(`Error rate: ${(stats.errorRate * 100).toFixed(1)}%`);
|
|
1069
|
-
if (stats.duration.avgMs !== void 0) {
|
|
1070
|
-
lines.push(
|
|
1071
|
-
`Duration: min ${formatDuration(stats.duration.minMs ?? 0)} | avg ${formatDuration(stats.duration.avgMs)} | p50 ${formatDuration(stats.duration.p50Ms ?? 0)} | p95 ${formatDuration(stats.duration.p95Ms ?? 0)} | max ${formatDuration(stats.duration.maxMs ?? 0)}`
|
|
1072
|
-
);
|
|
1073
|
-
}
|
|
1074
|
-
lines.push("");
|
|
1075
|
-
lines.push(`Steps: ${stats.totalSteps} (avg ${stats.avgStepsPerRun.toFixed(1)} per run)`);
|
|
1076
|
-
lines.push(
|
|
1077
|
-
` LLM: ${stats.totalLlmSteps} tool: ${stats.totalToolSteps} errors: ${stats.totalErrorSteps}`
|
|
1078
|
-
);
|
|
1079
|
-
if (stats.slowestRuns.length > 0) {
|
|
1080
|
-
lines.push("");
|
|
1081
|
-
lines.push("Slowest runs:");
|
|
1082
|
-
for (const r of stats.slowestRuns) {
|
|
1083
|
-
lines.push(
|
|
1084
|
-
` ${r.runId} | ${r.name ?? "-"} | ${formatDuration(r.durationMs ?? 0)} | ${r.status}`
|
|
1085
|
-
);
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
if (stats.slowestSteps.length > 0) {
|
|
1089
|
-
lines.push("");
|
|
1090
|
-
lines.push("Slowest steps:");
|
|
1091
|
-
for (const s of stats.slowestSteps) {
|
|
1092
|
-
lines.push(
|
|
1093
|
-
` ${s.runId} | ${s.stepType}:${s.stepName} | ${formatDuration(s.durationMs)}`
|
|
1094
|
-
);
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
return lines.join("\n");
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
// packages/core/src/search.ts
|
|
1101
|
-
function parseDurationFilter(expr) {
|
|
1102
|
-
const raw = expr.trim();
|
|
1103
|
-
const m = raw.match(/^(>=|<=|>|<)\s*(.+)$/);
|
|
1104
|
-
if (!m) {
|
|
1105
|
-
throw new Error(
|
|
1106
|
-
`Invalid --duration "${expr}". Use forms like >5s, >=500ms, <2m.`
|
|
1107
|
-
);
|
|
1108
|
-
}
|
|
1109
|
-
const op = m[1];
|
|
1110
|
-
const ms = parseDuration(m[2].trim());
|
|
1111
|
-
return { op, ms };
|
|
1112
|
-
}
|
|
1113
|
-
function durationMatches(valueMs, filter) {
|
|
1114
|
-
if (valueMs === void 0 || !Number.isFinite(valueMs)) return false;
|
|
1115
|
-
switch (filter.op) {
|
|
1116
|
-
case ">":
|
|
1117
|
-
return valueMs > filter.ms;
|
|
1118
|
-
case ">=":
|
|
1119
|
-
return valueMs >= filter.ms;
|
|
1120
|
-
case "<":
|
|
1121
|
-
return valueMs < filter.ms;
|
|
1122
|
-
case "<=":
|
|
1123
|
-
return valueMs <= filter.ms;
|
|
1124
|
-
default:
|
|
1125
|
-
return false;
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
function normalizeStepTypeFilter(kind, type) {
|
|
1129
|
-
const v = (kind ?? type)?.trim().toLowerCase();
|
|
1130
|
-
return v && v !== "" ? v : void 0;
|
|
1131
|
-
}
|
|
1132
|
-
function nameMatches(hay, needle) {
|
|
1133
|
-
return hay.toLowerCase().includes(needle.toLowerCase());
|
|
1134
|
-
}
|
|
1135
|
-
async function searchTraces(metas, options) {
|
|
1136
|
-
let filtered = filterTraces(metas, { since: options.since });
|
|
1137
|
-
const stepTypeFilter = normalizeStepTypeFilter(options.kind, options.type);
|
|
1138
|
-
const nameQuery = options.name?.trim();
|
|
1139
|
-
const toolQuery = options.tool?.trim();
|
|
1140
|
-
let durationFilter;
|
|
1141
|
-
if (options.duration) {
|
|
1142
|
-
durationFilter = parseDurationFilter(options.duration);
|
|
1143
|
-
}
|
|
1144
|
-
const limit = options.limit ?? 50;
|
|
1145
|
-
const hasContentFilter = Boolean(
|
|
1146
|
-
options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter
|
|
1147
|
-
);
|
|
1148
|
-
const results = [];
|
|
1149
|
-
if (!hasContentFilter) {
|
|
1150
|
-
for (const m of filtered) {
|
|
1151
|
-
results.push({
|
|
1152
|
-
runId: m.runId,
|
|
1153
|
-
runName: m.name,
|
|
1154
|
-
runStatus: m.status,
|
|
1155
|
-
timestamp: m.startedAt,
|
|
1156
|
-
durationMs: m.durationMs,
|
|
1157
|
-
matchReason: "trace in directory",
|
|
1158
|
-
matchedFields: ["run"],
|
|
1159
|
-
filePath: m.filePath
|
|
1160
|
-
});
|
|
1161
|
-
}
|
|
1162
|
-
return results.slice(0, limit);
|
|
1163
|
-
}
|
|
1164
|
-
for (const m of filtered) {
|
|
1165
|
-
if (options.status && m.status !== options.status) continue;
|
|
1166
|
-
let events = [];
|
|
1167
|
-
try {
|
|
1168
|
-
events = await readTraceEventsFromFile(m.filePath);
|
|
1169
|
-
} catch {
|
|
1170
|
-
continue;
|
|
1171
|
-
}
|
|
1172
|
-
if (events.length === 0) continue;
|
|
1173
|
-
const runMatches = matchRunLevel(m, {
|
|
1174
|
-
stepTypeFilter,
|
|
1175
|
-
nameQuery,
|
|
1176
|
-
toolQuery,
|
|
1177
|
-
durationFilter,
|
|
1178
|
-
statusFilter: options.status
|
|
1179
|
-
});
|
|
1180
|
-
results.push(...runMatches);
|
|
1181
|
-
const stepMatches = matchStepLevel(m, events, {
|
|
1182
|
-
stepTypeFilter,
|
|
1183
|
-
nameQuery,
|
|
1184
|
-
toolQuery,
|
|
1185
|
-
durationFilter,
|
|
1186
|
-
statusFilter: options.status
|
|
1187
|
-
});
|
|
1188
|
-
results.push(...stepMatches);
|
|
1189
|
-
}
|
|
1190
|
-
results.sort((a, b) => {
|
|
1191
|
-
const ta = a.timestamp ?? 0;
|
|
1192
|
-
const tb = b.timestamp ?? 0;
|
|
1193
|
-
if (ta !== tb) return ta - tb;
|
|
1194
|
-
const runCmp = a.runId.localeCompare(b.runId);
|
|
1195
|
-
if (runCmp !== 0) return runCmp;
|
|
1196
|
-
return (a.stepName ?? "").localeCompare(b.stepName ?? "");
|
|
1197
|
-
});
|
|
1198
|
-
return results.slice(0, limit);
|
|
1199
|
-
}
|
|
1200
|
-
function matchRunLevel(m, opts) {
|
|
1201
|
-
if (opts.stepTypeFilter || opts.toolQuery) return [];
|
|
1202
|
-
const out = [];
|
|
1203
|
-
const fields = [];
|
|
1204
|
-
if (opts.statusFilter && m.status === opts.statusFilter) {
|
|
1205
|
-
fields.push("run.status");
|
|
1206
|
-
}
|
|
1207
|
-
if (opts.nameQuery && nameMatches(m.name ?? m.runId, opts.nameQuery)) {
|
|
1208
|
-
fields.push("run.name");
|
|
1209
|
-
}
|
|
1210
|
-
if (opts.durationFilter && durationMatches(m.durationMs, opts.durationFilter)) {
|
|
1211
|
-
fields.push("run.durationMs");
|
|
1212
|
-
}
|
|
1213
|
-
if (fields.length === 0) return out;
|
|
1214
|
-
out.push({
|
|
1215
|
-
runId: m.runId,
|
|
1216
|
-
runName: m.name,
|
|
1217
|
-
runStatus: m.status,
|
|
1218
|
-
timestamp: m.startedAt,
|
|
1219
|
-
durationMs: m.durationMs,
|
|
1220
|
-
matchReason: `run match: ${fields.join(", ")}`,
|
|
1221
|
-
matchedFields: fields,
|
|
1222
|
-
filePath: m.filePath
|
|
1223
|
-
});
|
|
1224
|
-
return out;
|
|
1225
|
-
}
|
|
1226
|
-
function matchStepLevel(m, events, opts) {
|
|
1227
|
-
const out = [];
|
|
1228
|
-
const started = /* @__PURE__ */ new Map();
|
|
1229
|
-
for (const e of events) {
|
|
1230
|
-
if (e.event === "step_started") {
|
|
1231
|
-
started.set(e.stepId, e);
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
for (const e of events) {
|
|
1235
|
-
if (e.event !== "step_completed") continue;
|
|
1236
|
-
const c = e;
|
|
1237
|
-
const s = started.get(c.stepId);
|
|
1238
|
-
if (!s) continue;
|
|
1239
|
-
const fields = [];
|
|
1240
|
-
const stepType = s.type;
|
|
1241
|
-
if (opts.stepTypeFilter && stepType !== opts.stepTypeFilter) {
|
|
1242
|
-
continue;
|
|
1243
|
-
}
|
|
1244
|
-
const hasStepFilters = opts.stepTypeFilter || opts.nameQuery || opts.toolQuery || opts.durationFilter || opts.statusFilter === "error" || opts.statusFilter === "success";
|
|
1245
|
-
if (!hasStepFilters) continue;
|
|
1246
|
-
if (opts.statusFilter === "error" && c.status === "error") {
|
|
1247
|
-
fields.push("step.status");
|
|
1248
|
-
} else if (opts.statusFilter === "success" && c.status === "success") {
|
|
1249
|
-
fields.push("step.status");
|
|
1250
|
-
} else if (opts.statusFilter === "error" || opts.statusFilter === "success") {
|
|
1251
|
-
continue;
|
|
1252
|
-
}
|
|
1253
|
-
if (opts.nameQuery) {
|
|
1254
|
-
if (!nameMatches(s.name, opts.nameQuery)) continue;
|
|
1255
|
-
fields.push("step.name");
|
|
1256
|
-
}
|
|
1257
|
-
if (opts.toolQuery) {
|
|
1258
|
-
const toolName = typeof s.metadata?.toolName === "string" ? s.metadata.toolName : s.name;
|
|
1259
|
-
if (!nameMatches(toolName, opts.toolQuery)) continue;
|
|
1260
|
-
fields.push("step.tool");
|
|
1261
|
-
}
|
|
1262
|
-
if (opts.durationFilter) {
|
|
1263
|
-
if (!durationMatches(c.durationMs, opts.durationFilter)) continue;
|
|
1264
|
-
fields.push("step.durationMs");
|
|
1265
|
-
}
|
|
1266
|
-
if (opts.stepTypeFilter) {
|
|
1267
|
-
fields.push("step.type");
|
|
1268
|
-
}
|
|
1269
|
-
if (fields.length === 0) continue;
|
|
1270
|
-
out.push({
|
|
1271
|
-
runId: m.runId,
|
|
1272
|
-
runName: m.name,
|
|
1273
|
-
runStatus: m.status,
|
|
1274
|
-
stepId: c.stepId,
|
|
1275
|
-
stepName: s.name,
|
|
1276
|
-
stepType,
|
|
1277
|
-
timestamp: s.startTime ?? s.timestamp,
|
|
1278
|
-
durationMs: c.durationMs,
|
|
1279
|
-
matchReason: `step match: ${fields.join(", ")}`,
|
|
1280
|
-
matchedFields: fields,
|
|
1281
|
-
filePath: m.filePath
|
|
1282
|
-
});
|
|
1283
|
-
}
|
|
1284
|
-
return out;
|
|
1285
|
-
}
|
|
1286
|
-
async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
|
|
1287
|
-
const metas = [];
|
|
1288
|
-
for (const fileName of fileNames) {
|
|
1289
|
-
try {
|
|
1290
|
-
const filePath = getPath(fileName);
|
|
1291
|
-
const meta = await extractMetadata(filePath);
|
|
1292
|
-
metas.push(meta);
|
|
1293
|
-
} catch {
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
return metas;
|
|
1297
|
-
}
|
|
1298
|
-
var KNOWN_EVENTS = /* @__PURE__ */ new Set([
|
|
1299
|
-
"run_started",
|
|
1300
|
-
"run_completed",
|
|
1301
|
-
"step_started",
|
|
1302
|
-
"step_completed"
|
|
1303
|
-
]);
|
|
1304
|
-
function isRecord(value) {
|
|
1305
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1306
|
-
}
|
|
1307
|
-
function safeParse(line) {
|
|
1308
|
-
try {
|
|
1309
|
-
return JSON.parse(line);
|
|
1310
|
-
} catch {
|
|
1311
|
-
return void 0;
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
async function isAgentInspectTrace(filePath) {
|
|
1315
|
-
try {
|
|
1316
|
-
const rl = createInterface({
|
|
1317
|
-
input: createReadStream(filePath, { encoding: "utf8" }),
|
|
1318
|
-
crlfDelay: Infinity
|
|
1319
|
-
});
|
|
1320
|
-
let checked = 0;
|
|
1321
|
-
for await (const line of rl) {
|
|
1322
|
-
const trimmed = line.trim();
|
|
1323
|
-
if (trimmed === "") continue;
|
|
1324
|
-
const parsed = safeParse(trimmed);
|
|
1325
|
-
if (!parsed) continue;
|
|
1326
|
-
if (!isRecord(parsed)) continue;
|
|
1327
|
-
checked += 1;
|
|
1328
|
-
if (isTraceEvent(parsed)) return true;
|
|
1329
|
-
const ev = parsed.event;
|
|
1330
|
-
const runId = parsed.runId;
|
|
1331
|
-
if (typeof ev === "string" && KNOWN_EVENTS.has(ev) && typeof runId === "string") {
|
|
1332
|
-
return true;
|
|
1333
|
-
}
|
|
1334
|
-
if (checked >= 20) break;
|
|
1335
|
-
}
|
|
1336
|
-
return false;
|
|
1337
|
-
} catch {
|
|
1338
|
-
return false;
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
// packages/core/src/inspect-run.ts
|
|
1343
|
-
function normalizeRunName(name) {
|
|
1344
|
-
if (typeof name !== "string" || name.trim() === "") {
|
|
1345
|
-
return "unnamed-run";
|
|
1346
|
-
}
|
|
1347
|
-
return truncateName(name.trim(), 100);
|
|
1348
|
-
}
|
|
1349
|
-
async function safeInstrumentation(label, op) {
|
|
1350
|
-
try {
|
|
1351
|
-
await Promise.resolve(op());
|
|
1352
|
-
} catch (e) {
|
|
1353
|
-
warn(`inspectRun: ${label}`, e);
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
async function inspectRun(name, fn, options) {
|
|
1357
|
-
if (typeof fn !== "function") {
|
|
1358
|
-
throw new TypeError("inspectRun requires `fn` to be a function");
|
|
1359
|
-
}
|
|
1360
|
-
if (options?.enabled === false) {
|
|
1361
|
-
return Promise.resolve(fn());
|
|
1362
|
-
}
|
|
1363
|
-
const runName = normalizeRunName(name);
|
|
1364
|
-
const runId = createRunId();
|
|
1365
|
-
const traceDir = resolveTraceDir({ dir: options?.traceDir });
|
|
1366
|
-
const traceSafety = resolveTraceSafetyOptions(options);
|
|
1367
|
-
const runMetadata = buildRunStartedMetadata(options);
|
|
1368
|
-
const context = {
|
|
1369
|
-
runId,
|
|
1370
|
-
runName,
|
|
1371
|
-
traceDir,
|
|
1372
|
-
silent: options?.silent ?? false,
|
|
1373
|
-
metadata: runMetadata
|
|
1374
|
-
};
|
|
1375
|
-
return runWithContext(context, async () => {
|
|
1376
|
-
const startTime = Date.now();
|
|
1377
|
-
let traceFilePath;
|
|
1378
|
-
await safeInstrumentation("initializeTraceFile", async () => {
|
|
1379
|
-
traceFilePath = await initializeTraceFile(runId, traceDir);
|
|
1380
|
-
});
|
|
1381
|
-
await safeInstrumentation("writeTraceEvent(run_started)", async () => {
|
|
1382
|
-
const started = {
|
|
1383
|
-
schemaVersion: "0.1",
|
|
1384
|
-
event: "run_started",
|
|
1385
|
-
timestamp: startTime,
|
|
1386
|
-
runId,
|
|
1387
|
-
name: runName,
|
|
1388
|
-
startTime,
|
|
1389
|
-
...runMetadata !== void 0 ? { metadata: runMetadata } : {}
|
|
1390
|
-
};
|
|
1391
|
-
await writeTraceEvent(
|
|
1392
|
-
prepareTraceEventForDisk(started, traceSafety),
|
|
1393
|
-
traceDir
|
|
1394
|
-
);
|
|
1395
|
-
});
|
|
1396
|
-
await safeInstrumentation("printRunStart", () => {
|
|
1397
|
-
printRunStart(runId, runName);
|
|
1398
|
-
});
|
|
1399
|
-
let result;
|
|
1400
|
-
try {
|
|
1401
|
-
result = await Promise.resolve(fn());
|
|
1402
|
-
} catch (userError) {
|
|
1403
|
-
const endTime2 = Date.now();
|
|
1404
|
-
const durationMs2 = endTime2 - startTime;
|
|
1405
|
-
const formatted = formatError(userError);
|
|
1406
|
-
const printPath2 = traceFilePath ?? getTraceFilePath(runId, traceDir);
|
|
1407
|
-
await safeInstrumentation("writeTraceEvent(run_completed error)", async () => {
|
|
1408
|
-
const completed = {
|
|
1409
|
-
schemaVersion: "0.1",
|
|
1410
|
-
event: "run_completed",
|
|
1411
|
-
timestamp: endTime2,
|
|
1412
|
-
runId,
|
|
1413
|
-
status: "error",
|
|
1414
|
-
endTime: endTime2,
|
|
1415
|
-
durationMs: durationMs2,
|
|
1416
|
-
error: formatted
|
|
1417
|
-
};
|
|
1418
|
-
await writeTraceEvent(
|
|
1419
|
-
prepareTraceEventForDisk(completed, traceSafety),
|
|
1420
|
-
traceDir
|
|
1421
|
-
);
|
|
1422
|
-
});
|
|
1423
|
-
await safeInstrumentation("printRunComplete(error)", () => {
|
|
1424
|
-
printRunComplete(runName, runId, durationMs2, "error", printPath2);
|
|
1425
|
-
});
|
|
1426
|
-
throw userError;
|
|
1427
|
-
}
|
|
1428
|
-
const endTime = Date.now();
|
|
1429
|
-
const durationMs = endTime - startTime;
|
|
1430
|
-
const printPath = traceFilePath ?? getTraceFilePath(runId, traceDir);
|
|
1431
|
-
await safeInstrumentation("writeTraceEvent(run_completed success)", async () => {
|
|
1432
|
-
const completed = {
|
|
1433
|
-
schemaVersion: "0.1",
|
|
1434
|
-
event: "run_completed",
|
|
1435
|
-
timestamp: endTime,
|
|
1436
|
-
runId,
|
|
1437
|
-
status: "success",
|
|
1438
|
-
endTime,
|
|
1439
|
-
durationMs
|
|
1440
|
-
};
|
|
1441
|
-
await writeTraceEvent(
|
|
1442
|
-
prepareTraceEventForDisk(completed, traceSafety),
|
|
1443
|
-
traceDir
|
|
1444
|
-
);
|
|
1445
|
-
});
|
|
1446
|
-
await safeInstrumentation("printRunComplete(success)", () => {
|
|
1447
|
-
printRunComplete(runName, runId, durationMs, "success", printPath);
|
|
1448
|
-
});
|
|
1449
|
-
return result;
|
|
1450
|
-
}, traceSafety);
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
// packages/core/src/maybe-inspect-run.ts
|
|
1454
|
-
var ENABLED_ENV_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on", "enabled"]);
|
|
1455
|
-
function isAgentInspectEnabled(value) {
|
|
1456
|
-
if (value === void 0) return false;
|
|
1457
|
-
const normalized = value.trim().toLowerCase();
|
|
1458
|
-
if (normalized === "") return false;
|
|
1459
|
-
return ENABLED_ENV_VALUES.has(normalized);
|
|
1460
|
-
}
|
|
1461
|
-
async function maybeInspectRun(name, fn, options) {
|
|
1462
|
-
if (typeof fn !== "function") {
|
|
1463
|
-
throw new TypeError("maybeInspectRun requires `fn` to be a function");
|
|
1464
|
-
}
|
|
1465
|
-
const enabled = options?.enabled !== void 0 ? options.enabled : isAgentInspectEnabled(process.env.AGENT_INSPECT);
|
|
1466
|
-
if (!enabled) {
|
|
1467
|
-
return Promise.resolve(fn());
|
|
1468
|
-
}
|
|
1469
|
-
return inspectRun(name, fn, options);
|
|
1470
|
-
}
|
|
1471
11
|
|
|
1472
12
|
// packages/core/src/step.ts
|
|
1473
13
|
function normalizeStepName(name) {
|
|
@@ -1476,7 +16,7 @@ function normalizeStepName(name) {
|
|
|
1476
16
|
}
|
|
1477
17
|
return truncateName(name.trim(), 100);
|
|
1478
18
|
}
|
|
1479
|
-
async function
|
|
19
|
+
async function safeInstrumentation(label, op) {
|
|
1480
20
|
try {
|
|
1481
21
|
await Promise.resolve(op());
|
|
1482
22
|
} catch (e) {
|
|
@@ -1500,7 +40,7 @@ async function stepImpl(name, fn, options) {
|
|
|
1500
40
|
const metadata = options?.metadata;
|
|
1501
41
|
const traceSafety = getTraceSafetyFromContext();
|
|
1502
42
|
const startTime = Date.now();
|
|
1503
|
-
await
|
|
43
|
+
await safeInstrumentation("writeTraceEvent(step_started)", async () => {
|
|
1504
44
|
const started = {
|
|
1505
45
|
schemaVersion: "0.1",
|
|
1506
46
|
event: "step_started",
|
|
@@ -1516,7 +56,7 @@ async function stepImpl(name, fn, options) {
|
|
|
1516
56
|
const safe = traceSafety !== void 0 ? prepareTraceEventForDisk(started, traceSafety) : started;
|
|
1517
57
|
await writeTraceEvent(safe, context.traceDir);
|
|
1518
58
|
});
|
|
1519
|
-
await
|
|
59
|
+
await safeInstrumentation("printStepStart", () => {
|
|
1520
60
|
printStepStart(stepName, renderDepth);
|
|
1521
61
|
});
|
|
1522
62
|
let result;
|
|
@@ -1528,7 +68,7 @@ async function stepImpl(name, fn, options) {
|
|
|
1528
68
|
const endTime2 = Date.now();
|
|
1529
69
|
const durationMs2 = endTime2 - startTime;
|
|
1530
70
|
const formatted = formatError(userError);
|
|
1531
|
-
await
|
|
71
|
+
await safeInstrumentation("writeTraceEvent(step_completed error)", async () => {
|
|
1532
72
|
const completed = {
|
|
1533
73
|
schemaVersion: "0.1",
|
|
1534
74
|
event: "step_completed",
|
|
@@ -1543,20 +83,20 @@ async function stepImpl(name, fn, options) {
|
|
|
1543
83
|
const safe = traceSafety !== void 0 ? prepareTraceEventForDisk(completed, traceSafety) : completed;
|
|
1544
84
|
await writeTraceEvent(safe, context.traceDir);
|
|
1545
85
|
});
|
|
1546
|
-
await
|
|
86
|
+
await safeInstrumentation("printStepComplete(error)", () => {
|
|
1547
87
|
printStepComplete(stepName, durationMs2, "error", renderDepth);
|
|
1548
88
|
});
|
|
1549
|
-
await
|
|
89
|
+
await safeInstrumentation("printError", () => {
|
|
1550
90
|
printError(formatted, renderDepth);
|
|
1551
91
|
});
|
|
1552
|
-
await
|
|
92
|
+
await safeInstrumentation("printFailedAt", () => {
|
|
1553
93
|
printFailedAt(stepName);
|
|
1554
94
|
});
|
|
1555
95
|
throw userError;
|
|
1556
96
|
}
|
|
1557
97
|
const endTime = Date.now();
|
|
1558
98
|
const durationMs = endTime - startTime;
|
|
1559
|
-
await
|
|
99
|
+
await safeInstrumentation("writeTraceEvent(step_completed success)", async () => {
|
|
1560
100
|
const completed = {
|
|
1561
101
|
schemaVersion: "0.1",
|
|
1562
102
|
event: "step_completed",
|
|
@@ -1570,7 +110,7 @@ async function stepImpl(name, fn, options) {
|
|
|
1570
110
|
const safe = traceSafety !== void 0 ? prepareTraceEventForDisk(completed, traceSafety) : completed;
|
|
1571
111
|
await writeTraceEvent(safe, context.traceDir);
|
|
1572
112
|
});
|
|
1573
|
-
await
|
|
113
|
+
await safeInstrumentation("printStepComplete(success)", () => {
|
|
1574
114
|
printStepComplete(stepName, durationMs, "success", renderDepth);
|
|
1575
115
|
});
|
|
1576
116
|
return result;
|
|
@@ -1668,6 +208,6 @@ function observe(agent, options) {
|
|
|
1668
208
|
}
|
|
1669
209
|
}
|
|
1670
210
|
|
|
1671
|
-
export {
|
|
211
|
+
export { observe, step };
|
|
1672
212
|
//# sourceMappingURL=index.mjs.map
|
|
1673
213
|
//# sourceMappingURL=index.mjs.map
|