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