@agent-inspect/tui 1.0.3
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/LICENSE +21 -0
- package/dist/index.cjs +320 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +71 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.mjs +308 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AgentInspect contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var agentInspect = require('agent-inspect');
|
|
4
|
+
var React2 = require('react');
|
|
5
|
+
var ink = require('ink');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
|
|
10
|
+
var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
11
|
+
|
|
12
|
+
// packages/tui/src/tree-model.ts
|
|
13
|
+
function dfsFlat(roots, out) {
|
|
14
|
+
for (const n of roots) {
|
|
15
|
+
out.push(n);
|
|
16
|
+
if (n.children.length > 0) dfsFlat(n.children, out);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function toTuiNodes(builds, depth) {
|
|
20
|
+
return builds.map((b) => ({
|
|
21
|
+
id: b.id,
|
|
22
|
+
name: b.name,
|
|
23
|
+
type: b.type,
|
|
24
|
+
status: b.status,
|
|
25
|
+
durationMs: b.durationMs,
|
|
26
|
+
depth,
|
|
27
|
+
parentId: b.parentId,
|
|
28
|
+
error: b.error,
|
|
29
|
+
metadata: b.metadata,
|
|
30
|
+
children: toTuiNodes(b.children, depth + 1)
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
function buildTuiTraceModel(events) {
|
|
34
|
+
const started = events.find((e) => e.event === "run_started");
|
|
35
|
+
if (!started) {
|
|
36
|
+
throw new Error("Invalid trace: missing run_started");
|
|
37
|
+
}
|
|
38
|
+
const runId = started.runId;
|
|
39
|
+
const runName = started.name;
|
|
40
|
+
const completedAll = events.filter((e) => e.event === "run_completed");
|
|
41
|
+
const lastCompleted = completedAll[completedAll.length - 1];
|
|
42
|
+
const runStatus = lastCompleted ? lastCompleted.status : "running";
|
|
43
|
+
const runDurationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
44
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
45
|
+
for (const e of events) {
|
|
46
|
+
if (e.event !== "step_started") continue;
|
|
47
|
+
const s = e;
|
|
48
|
+
nodes.set(s.stepId, {
|
|
49
|
+
id: s.stepId,
|
|
50
|
+
parentId: s.parentId,
|
|
51
|
+
name: s.name,
|
|
52
|
+
type: s.type,
|
|
53
|
+
status: "running",
|
|
54
|
+
startedAt: s.startTime,
|
|
55
|
+
metadata: s.metadata,
|
|
56
|
+
children: []
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
for (const e of events) {
|
|
60
|
+
if (e.event !== "step_completed") continue;
|
|
61
|
+
const c = e;
|
|
62
|
+
const node = nodes.get(c.stepId);
|
|
63
|
+
if (!node) continue;
|
|
64
|
+
node.status = c.status;
|
|
65
|
+
node.durationMs = c.durationMs;
|
|
66
|
+
node.error = c.error !== void 0 && typeof c.error.message === "string" ? c.error.message : void 0;
|
|
67
|
+
}
|
|
68
|
+
const roots = [];
|
|
69
|
+
const sortByStarted = (a, b) => a.startedAt - b.startedAt;
|
|
70
|
+
for (const n of nodes.values()) {
|
|
71
|
+
if (n.parentId !== void 0 && nodes.has(n.parentId)) {
|
|
72
|
+
nodes.get(n.parentId).children.push(n);
|
|
73
|
+
} else {
|
|
74
|
+
roots.push(n);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
roots.sort(sortByStarted);
|
|
78
|
+
for (const n of nodes.values()) {
|
|
79
|
+
n.children.sort(sortByStarted);
|
|
80
|
+
}
|
|
81
|
+
const treeRoots = toTuiNodes(roots, 0);
|
|
82
|
+
const flatNodes = [];
|
|
83
|
+
dfsFlat(treeRoots, flatNodes);
|
|
84
|
+
return {
|
|
85
|
+
runId,
|
|
86
|
+
name: runName,
|
|
87
|
+
status: runStatus,
|
|
88
|
+
durationMs: runDurationMs,
|
|
89
|
+
nodes: treeRoots,
|
|
90
|
+
flatNodes
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function countTreeSteps(roots) {
|
|
94
|
+
let n = 0;
|
|
95
|
+
for (const r of roots) {
|
|
96
|
+
n += 1 + countSubtree(r.children);
|
|
97
|
+
}
|
|
98
|
+
return n;
|
|
99
|
+
}
|
|
100
|
+
function countSubtree(children) {
|
|
101
|
+
let n = 0;
|
|
102
|
+
for (const c of children) {
|
|
103
|
+
n += 1 + countSubtree(c.children);
|
|
104
|
+
}
|
|
105
|
+
return n;
|
|
106
|
+
}
|
|
107
|
+
async function loadTraceForTui(options) {
|
|
108
|
+
const traceDir = agentInspect.resolveTraceDir({ dir: options.dir });
|
|
109
|
+
const events = await agentInspect.readTraceEvents(options.runId, traceDir);
|
|
110
|
+
if (events.length === 0) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`Run not found or trace is empty: ${options.runId} (directory: ${traceDir})`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return buildTuiTraceModel(events);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// packages/tui/src/keymap.ts
|
|
119
|
+
function mapInputToAction(input, key = {}) {
|
|
120
|
+
if (key.escape === true) return "quit";
|
|
121
|
+
if (key.ctrl === true && input === "c") return "quit";
|
|
122
|
+
if (input === "q" || input === "Q") return "quit";
|
|
123
|
+
if (input === "?") return "help";
|
|
124
|
+
if (input === "d" || input === "D") return "details";
|
|
125
|
+
if (key.return === true) return "expand";
|
|
126
|
+
if (input === " ") return "toggle";
|
|
127
|
+
if (key.upArrow === true || input === "k") return "up";
|
|
128
|
+
if (key.downArrow === true || input === "j") return "down";
|
|
129
|
+
if (key.rightArrow === true || input === "l") return "expand";
|
|
130
|
+
if (key.leftArrow === true || input === "h") return "collapse";
|
|
131
|
+
return "unknown";
|
|
132
|
+
}
|
|
133
|
+
function collectVisible(roots, expanded, out) {
|
|
134
|
+
for (const n of roots) {
|
|
135
|
+
out.push(n);
|
|
136
|
+
if (n.children.length > 0 && expanded.has(n.id)) {
|
|
137
|
+
collectVisible(n.children, expanded, out);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function initialExpandedSet(model) {
|
|
142
|
+
const s = /* @__PURE__ */ new Set();
|
|
143
|
+
const small = model.flatNodes.length <= 15;
|
|
144
|
+
if (small) {
|
|
145
|
+
for (const n of model.flatNodes) {
|
|
146
|
+
if (n.children.length > 0) s.add(n.id);
|
|
147
|
+
}
|
|
148
|
+
return s;
|
|
149
|
+
}
|
|
150
|
+
for (const r of model.nodes) {
|
|
151
|
+
if (r.children.length > 0) s.add(r.id);
|
|
152
|
+
}
|
|
153
|
+
return s;
|
|
154
|
+
}
|
|
155
|
+
function formatDur(ms) {
|
|
156
|
+
if (ms === void 0 || !Number.isFinite(ms)) return "-";
|
|
157
|
+
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
158
|
+
return `${(ms / 1e3).toFixed(2)}s`;
|
|
159
|
+
}
|
|
160
|
+
function TraceViewerApp(props) {
|
|
161
|
+
const { model, onExit } = props;
|
|
162
|
+
const { exit } = ink.useApp();
|
|
163
|
+
const [expanded, setExpanded] = React2.useState(() => initialExpandedSet(model));
|
|
164
|
+
const [sel, setSel] = React2.useState(0);
|
|
165
|
+
const [showHelp, setShowHelp] = React2.useState(false);
|
|
166
|
+
const [showDetails, setShowDetails] = React2.useState(true);
|
|
167
|
+
const visible = React2.useMemo(() => {
|
|
168
|
+
const out = [];
|
|
169
|
+
collectVisible(model.nodes, expanded, out);
|
|
170
|
+
return out;
|
|
171
|
+
}, [model.nodes, expanded]);
|
|
172
|
+
React2.useEffect(() => {
|
|
173
|
+
if (visible.length === 0) {
|
|
174
|
+
setSel(0);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
setSel((i) => Math.min(i, visible.length - 1));
|
|
178
|
+
}, [visible.length]);
|
|
179
|
+
const safeSel = visible.length === 0 ? 0 : Math.min(sel, visible.length - 1);
|
|
180
|
+
const selected = visible[safeSel];
|
|
181
|
+
ink.useInput((input, key) => {
|
|
182
|
+
const action = mapInputToAction(input, key);
|
|
183
|
+
switch (action) {
|
|
184
|
+
case "quit":
|
|
185
|
+
onExit?.();
|
|
186
|
+
exit();
|
|
187
|
+
break;
|
|
188
|
+
case "help":
|
|
189
|
+
setShowHelp((h) => !h);
|
|
190
|
+
break;
|
|
191
|
+
case "details":
|
|
192
|
+
setShowDetails((d) => !d);
|
|
193
|
+
break;
|
|
194
|
+
case "up":
|
|
195
|
+
setSel((i) => Math.max(0, i - 1));
|
|
196
|
+
break;
|
|
197
|
+
case "down":
|
|
198
|
+
setSel((i) => Math.min(Math.max(0, visible.length - 1), i + 1));
|
|
199
|
+
break;
|
|
200
|
+
case "expand": {
|
|
201
|
+
const cur = visible.length === 0 ? void 0 : visible[safeSel];
|
|
202
|
+
if (cur?.children.length) {
|
|
203
|
+
setExpanded((prev) => new Set(prev).add(cur.id));
|
|
204
|
+
}
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case "collapse": {
|
|
208
|
+
const cur = visible.length === 0 ? void 0 : visible[safeSel];
|
|
209
|
+
if (cur && expanded.has(cur.id)) {
|
|
210
|
+
setExpanded((prev) => {
|
|
211
|
+
const n = new Set(prev);
|
|
212
|
+
n.delete(cur.id);
|
|
213
|
+
return n;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case "toggle": {
|
|
219
|
+
const cur = visible.length === 0 ? void 0 : visible[safeSel];
|
|
220
|
+
if (cur?.children.length) {
|
|
221
|
+
setExpanded((prev) => {
|
|
222
|
+
const n = new Set(prev);
|
|
223
|
+
if (n.has(cur.id)) n.delete(cur.id);
|
|
224
|
+
else n.add(cur.id);
|
|
225
|
+
return n;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
const indent = (d) => " ".repeat(d);
|
|
233
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
234
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
235
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "AgentInspect Trace Viewer" }),
|
|
236
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
237
|
+
model.name ?? "(unnamed)",
|
|
238
|
+
" \xB7 ",
|
|
239
|
+
model.runId,
|
|
240
|
+
" \xB7 ",
|
|
241
|
+
model.status ?? "unknown",
|
|
242
|
+
" \xB7",
|
|
243
|
+
" ",
|
|
244
|
+
formatDur(model.durationMs)
|
|
245
|
+
] })
|
|
246
|
+
] }),
|
|
247
|
+
showHelp ? /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", marginBottom: 1, borderStyle: "round", paddingX: 1, children: [
|
|
248
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Keys" }),
|
|
249
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: "\u2191/\u2193 or j/k \u2014 move \xB7 \u2190/\u2192 or h/l \u2014 collapse/expand \xB7 space \u2014 toggle branch" }),
|
|
250
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: "enter \u2014 expand \xB7 d \u2014 toggle details \xB7 ? \u2014 this help \xB7 q / Ctrl+C / esc \u2014 quit" })
|
|
251
|
+
] }) : null,
|
|
252
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", children: [
|
|
253
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", width: "55%", children: [
|
|
254
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Execution tree" }),
|
|
255
|
+
visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "No steps recorded" }) : visible.map((n, i) => {
|
|
256
|
+
const branch = n.children.length === 0 ? " " : expanded.has(n.id) ? "\u25BC " : "\u25B6 ";
|
|
257
|
+
const row = `${indent(n.depth)}${branch}${n.name}`;
|
|
258
|
+
const mark = i === safeSel ? "\u203A " : " ";
|
|
259
|
+
const status = n.status ?? "?";
|
|
260
|
+
const line = `${mark}${row} (${status})`;
|
|
261
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { inverse: i === safeSel, children: line }, n.id);
|
|
262
|
+
})
|
|
263
|
+
] }),
|
|
264
|
+
showDetails ? /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", paddingLeft: 2, width: "45%", children: [
|
|
265
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Details" }),
|
|
266
|
+
selected ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
267
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
268
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "name: " }),
|
|
269
|
+
selected.name
|
|
270
|
+
] }),
|
|
271
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
272
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "type: " }),
|
|
273
|
+
selected.type ?? "-"
|
|
274
|
+
] }),
|
|
275
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
276
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "status: " }),
|
|
277
|
+
selected.status ?? "-"
|
|
278
|
+
] }),
|
|
279
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
280
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "duration: " }),
|
|
281
|
+
formatDur(selected.durationMs)
|
|
282
|
+
] }),
|
|
283
|
+
selected.error ? /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
284
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "error: " }),
|
|
285
|
+
selected.error
|
|
286
|
+
] }) : null,
|
|
287
|
+
selected.metadata && Object.keys(selected.metadata).length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ink.Text, { children: [
|
|
288
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "metadata: " }),
|
|
289
|
+
JSON.stringify(selected.metadata)
|
|
290
|
+
] }) : null
|
|
291
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Select a step" })
|
|
292
|
+
] }) : null
|
|
293
|
+
] }),
|
|
294
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Box, { marginTop: 1, children: /* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "\u2191/\u2193 or j/k move \xB7 enter/space expand \xB7 h/l collapse/expand \xB7 d details \xB7 ? help \xB7 q quit" }) })
|
|
295
|
+
] });
|
|
296
|
+
}
|
|
297
|
+
var TTY_MSG = "TUI requires an interactive terminal. Use agent-inspect view without --tui for plain output.";
|
|
298
|
+
async function runTraceViewer(options) {
|
|
299
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
300
|
+
throw new Error(TTY_MSG);
|
|
301
|
+
}
|
|
302
|
+
const model = await loadTraceForTui({
|
|
303
|
+
runId: options.runId,
|
|
304
|
+
dir: options.dir
|
|
305
|
+
});
|
|
306
|
+
const { waitUntilExit } = ink.render(
|
|
307
|
+
React2__default.default.createElement(TraceViewerApp, { model })
|
|
308
|
+
);
|
|
309
|
+
await waitUntilExit();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
exports.TraceViewerApp = TraceViewerApp;
|
|
313
|
+
exports.buildTuiTraceModel = buildTuiTraceModel;
|
|
314
|
+
exports.countTreeSteps = countTreeSteps;
|
|
315
|
+
exports.initialExpandedSet = initialExpandedSet;
|
|
316
|
+
exports.loadTraceForTui = loadTraceForTui;
|
|
317
|
+
exports.mapInputToAction = mapInputToAction;
|
|
318
|
+
exports.runTraceViewer = runTraceViewer;
|
|
319
|
+
//# sourceMappingURL=index.cjs.map
|
|
320
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tree-model.ts","../src/trace-loader.ts","../src/keymap.ts","../src/app.tsx","../src/run-viewer.ts"],"names":["resolveTraceDir","readTraceEvents","useApp","useState","useMemo","useEffect","useInput","jsxs","Box","jsx","Text","Fragment","render","React"],"mappings":"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","file":"index.cjs","sourcesContent":["import type {\n RunCompletedEvent,\n RunStartedEvent,\n StepCompletedEvent,\n StepStartedEvent,\n TraceEvent,\n} from \"agent-inspect\";\n\nimport type { TuiTraceModel, TuiTraceNode } from \"./types.js\";\n\ntype StepBuild = {\n id: string;\n parentId?: string;\n name: string;\n type: string;\n status: string;\n durationMs?: number;\n startedAt: number;\n error?: string;\n metadata?: Record<string, unknown>;\n children: StepBuild[];\n};\n\nfunction dfsFlat(roots: TuiTraceNode[], out: TuiTraceNode[]): void {\n for (const n of roots) {\n out.push(n);\n if (n.children.length > 0) dfsFlat(n.children, out);\n }\n}\n\nfunction toTuiNodes(builds: StepBuild[], depth: number): TuiTraceNode[] {\n return builds.map((b) => ({\n id: b.id,\n name: b.name,\n type: b.type,\n status: b.status,\n durationMs: b.durationMs,\n depth,\n parentId: b.parentId,\n error: b.error,\n metadata: b.metadata,\n children: toTuiNodes(b.children, depth + 1),\n }));\n}\n\n/**\n * Build a navigable tree model from v0.1 JSONL trace events.\n * Does not mutate the input array or event objects.\n */\nexport function buildTuiTraceModel(events: TraceEvent[]): TuiTraceModel {\n const started = events.find((e): e is RunStartedEvent => e.event === \"run_started\");\n if (!started) {\n throw new Error(\"Invalid trace: missing run_started\");\n }\n\n const runId = started.runId;\n const runName = started.name;\n\n const completedAll = events.filter((e): e is RunCompletedEvent => e.event === \"run_completed\");\n const lastCompleted = completedAll[completedAll.length - 1];\n const runStatus = lastCompleted ? lastCompleted.status : \"running\";\n const runDurationMs =\n lastCompleted !== undefined && Number.isFinite(lastCompleted.durationMs)\n ? lastCompleted.durationMs\n : undefined;\n\n const nodes = new Map<string, StepBuild>();\n\n for (const e of events) {\n if (e.event !== \"step_started\") continue;\n const s = e as StepStartedEvent;\n nodes.set(s.stepId, {\n id: s.stepId,\n parentId: s.parentId,\n name: s.name,\n type: s.type,\n status: \"running\",\n startedAt: s.startTime,\n metadata: s.metadata as Record<string, unknown> | undefined,\n children: [],\n });\n }\n\n for (const e of events) {\n if (e.event !== \"step_completed\") continue;\n const c = e as StepCompletedEvent;\n const node = nodes.get(c.stepId);\n if (!node) continue;\n node.status = c.status;\n node.durationMs = c.durationMs;\n node.error =\n c.error !== undefined && typeof c.error.message === \"string\"\n ? c.error.message\n : undefined;\n }\n\n const roots: StepBuild[] = [];\n const sortByStarted = (a: StepBuild, b: StepBuild) => a.startedAt - b.startedAt;\n\n for (const n of nodes.values()) {\n if (n.parentId !== undefined && nodes.has(n.parentId)) {\n nodes.get(n.parentId)!.children.push(n);\n } else {\n roots.push(n);\n }\n }\n\n roots.sort(sortByStarted);\n for (const n of nodes.values()) {\n n.children.sort(sortByStarted);\n }\n\n const treeRoots = toTuiNodes(roots, 0);\n const flatNodes: TuiTraceNode[] = [];\n dfsFlat(treeRoots, flatNodes);\n\n return {\n runId,\n name: runName,\n status: runStatus,\n durationMs: runDurationMs,\n nodes: treeRoots,\n flatNodes,\n };\n}\n\n/** Total step count in tree (for default expand policy). Exported for tests. */\nexport function countTreeSteps(roots: TuiTraceNode[]): number {\n let n = 0;\n for (const r of roots) {\n n += 1 + countSubtree(r.children);\n }\n return n;\n}\n\nfunction countSubtree(children: TuiTraceNode[]): number {\n let n = 0;\n for (const c of children) {\n n += 1 + countSubtree(c.children);\n }\n return n;\n}\n","import { readTraceEvents, resolveTraceDir } from \"agent-inspect\";\n\nimport { buildTuiTraceModel } from \"./tree-model.js\";\nimport type { TuiTraceModel } from \"./types.js\";\n\nexport interface LoadTraceForTuiOptions {\n runId: string;\n dir?: string;\n}\n\n/**\n * Load JSONL trace events and build a TUI model. Does not write or mutate trace files.\n */\nexport async function loadTraceForTui(options: LoadTraceForTuiOptions): Promise<TuiTraceModel> {\n const traceDir = resolveTraceDir({ dir: options.dir });\n const events = await readTraceEvents(options.runId, traceDir);\n\n if (events.length === 0) {\n throw new Error(\n `Run not found or trace is empty: ${options.runId} (directory: ${traceDir})`,\n );\n }\n\n return buildTuiTraceModel(events);\n}\n","export type TuiAction =\n | \"up\"\n | \"down\"\n | \"expand\"\n | \"collapse\"\n | \"toggle\"\n | \"details\"\n | \"help\"\n | \"quit\"\n | \"unknown\";\n\n/** Map Ink `useInput` args to a semantic action. Pure helper for tests. */\nexport function mapInputToAction(\n input: string,\n key: Partial<{\n name: string;\n ctrl: boolean;\n meta: boolean;\n shift: boolean;\n return: boolean;\n escape: boolean;\n upArrow: boolean;\n downArrow: boolean;\n leftArrow: boolean;\n rightArrow: boolean;\n }> = {},\n): TuiAction {\n if (key.escape === true) return \"quit\";\n if (key.ctrl === true && input === \"c\") return \"quit\";\n\n if (input === \"q\" || input === \"Q\") return \"quit\";\n\n if (input === \"?\") return \"help\";\n\n if (input === \"d\" || input === \"D\") return \"details\";\n\n if (key.return === true) return \"expand\";\n\n if (input === \" \") return \"toggle\";\n\n if (key.upArrow === true || input === \"k\") return \"up\";\n if (key.downArrow === true || input === \"j\") return \"down\";\n if (key.rightArrow === true || input === \"l\") return \"expand\";\n if (key.leftArrow === true || input === \"h\") return \"collapse\";\n\n return \"unknown\";\n}\n","import React, { useEffect, useMemo, useState } from \"react\";\nimport { Box, Text, useApp, useInput } from \"ink\";\n\nimport { mapInputToAction } from \"./keymap.js\";\nimport type { TuiTraceModel, TuiTraceNode } from \"./types.js\";\n\nfunction collectVisible(\n roots: TuiTraceNode[],\n expanded: ReadonlySet<string>,\n out: TuiTraceNode[],\n): void {\n for (const n of roots) {\n out.push(n);\n if (n.children.length > 0 && expanded.has(n.id)) {\n collectVisible(n.children, expanded, out);\n }\n }\n}\n\n/** Small traces: expand all branches. Larger traces: only root rows expanded (show first nesting level). */\nexport function initialExpandedSet(model: TuiTraceModel): Set<string> {\n const s = new Set<string>();\n const small = model.flatNodes.length <= 15;\n if (small) {\n for (const n of model.flatNodes) {\n if (n.children.length > 0) s.add(n.id);\n }\n return s;\n }\n for (const r of model.nodes) {\n if (r.children.length > 0) s.add(r.id);\n }\n return s;\n}\n\nfunction formatDur(ms: number | undefined): string {\n if (ms === undefined || !Number.isFinite(ms)) return \"-\";\n if (ms < 1000) return `${Math.round(ms)}ms`;\n return `${(ms / 1000).toFixed(2)}s`;\n}\n\nexport interface TraceViewerAppProps {\n model: TuiTraceModel;\n onExit?: () => void;\n}\n\nexport function TraceViewerApp(props: TraceViewerAppProps): React.ReactElement {\n const { model, onExit } = props;\n const { exit } = useApp();\n const [expanded, setExpanded] = useState(() => initialExpandedSet(model));\n const [sel, setSel] = useState(0);\n const [showHelp, setShowHelp] = useState(false);\n const [showDetails, setShowDetails] = useState(true);\n\n const visible = useMemo(() => {\n const out: TuiTraceNode[] = [];\n collectVisible(model.nodes, expanded, out);\n return out;\n }, [model.nodes, expanded]);\n\n useEffect(() => {\n if (visible.length === 0) {\n setSel(0);\n return;\n }\n setSel((i) => Math.min(i, visible.length - 1));\n }, [visible.length]);\n\n const safeSel = visible.length === 0 ? 0 : Math.min(sel, visible.length - 1);\n const selected = visible[safeSel];\n\n useInput((input, key) => {\n const action = mapInputToAction(input, key);\n switch (action) {\n case \"quit\":\n onExit?.();\n exit();\n break;\n case \"help\":\n setShowHelp((h) => !h);\n break;\n case \"details\":\n setShowDetails((d) => !d);\n break;\n case \"up\":\n setSel((i) => Math.max(0, i - 1));\n break;\n case \"down\":\n setSel((i) => Math.min(Math.max(0, visible.length - 1), i + 1));\n break;\n case \"expand\": {\n const cur = visible.length === 0 ? undefined : visible[safeSel];\n if (cur?.children.length) {\n setExpanded((prev) => new Set(prev).add(cur.id));\n }\n break;\n }\n case \"collapse\": {\n const cur = visible.length === 0 ? undefined : visible[safeSel];\n if (cur && expanded.has(cur.id)) {\n setExpanded((prev) => {\n const n = new Set(prev);\n n.delete(cur.id);\n return n;\n });\n }\n break;\n }\n case \"toggle\": {\n const cur = visible.length === 0 ? undefined : visible[safeSel];\n if (cur?.children.length) {\n setExpanded((prev) => {\n const n = new Set(prev);\n if (n.has(cur.id)) n.delete(cur.id);\n else n.add(cur.id);\n return n;\n });\n }\n break;\n }\n default:\n break;\n }\n });\n\n const indent = (d: number) => \" \".repeat(d);\n\n return (\n <Box flexDirection=\"column\">\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text bold>AgentInspect Trace Viewer</Text>\n <Text>\n {model.name ?? \"(unnamed)\"} · {model.runId} · {model.status ?? \"unknown\"} ·{\" \"}\n {formatDur(model.durationMs)}\n </Text>\n </Box>\n\n {showHelp ? (\n <Box flexDirection=\"column\" marginBottom={1} borderStyle=\"round\" paddingX={1}>\n <Text bold>Keys</Text>\n <Text>↑/↓ or j/k — move · ←/→ or h/l — collapse/expand · space — toggle branch</Text>\n <Text>enter — expand · d — toggle details · ? — this help · q / Ctrl+C / esc — quit</Text>\n </Box>\n ) : null}\n\n <Box flexDirection=\"row\">\n <Box flexDirection=\"column\" width=\"55%\">\n <Text dimColor>Execution tree</Text>\n {visible.length === 0 ? (\n <Text dimColor>No steps recorded</Text>\n ) : (\n visible.map((n, i) => {\n const branch =\n n.children.length === 0\n ? \" \"\n : expanded.has(n.id)\n ? \"▼ \"\n : \"▶ \";\n const row = `${indent(n.depth)}${branch}${n.name}`;\n const mark = i === safeSel ? \"› \" : \" \";\n const status = n.status ?? \"?\";\n const line = `${mark}${row} (${status})`;\n return (\n <Text key={n.id} inverse={i === safeSel}>\n {line}\n </Text>\n );\n })\n )}\n </Box>\n\n {showDetails ? (\n <Box flexDirection=\"column\" paddingLeft={2} width=\"45%\">\n <Text dimColor>Details</Text>\n {selected ? (\n <>\n <Text>\n <Text bold>name: </Text>\n {selected.name}\n </Text>\n <Text>\n <Text bold>type: </Text>\n {selected.type ?? \"-\"}\n </Text>\n <Text>\n <Text bold>status: </Text>\n {selected.status ?? \"-\"}\n </Text>\n <Text>\n <Text bold>duration: </Text>\n {formatDur(selected.durationMs)}\n </Text>\n {selected.error ? (\n <Text>\n <Text bold>error: </Text>\n {selected.error}\n </Text>\n ) : null}\n {selected.metadata && Object.keys(selected.metadata).length > 0 ? (\n <Text>\n <Text bold>metadata: </Text>\n {JSON.stringify(selected.metadata)}\n </Text>\n ) : null}\n </>\n ) : (\n <Text dimColor>Select a step</Text>\n )}\n </Box>\n ) : null}\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n ↑/↓ or j/k move · enter/space expand · h/l collapse/expand · d details · ? help · q quit\n </Text>\n </Box>\n </Box>\n );\n}\n","import React from \"react\";\nimport { render } from \"ink\";\n\nimport { TraceViewerApp } from \"./app.js\";\nimport { loadTraceForTui } from \"./trace-loader.js\";\n\nexport interface RunTraceViewerOptions {\n runId: string;\n dir?: string;\n}\n\nconst TTY_MSG =\n \"TUI requires an interactive terminal. Use agent-inspect view without --tui for plain output.\";\n\nexport async function runTraceViewer(options: RunTraceViewerOptions): Promise<void> {\n if (!process.stdout.isTTY || !process.stdin.isTTY) {\n throw new Error(TTY_MSG);\n }\n\n const model = await loadTraceForTui({\n runId: options.runId,\n dir: options.dir,\n });\n\n const { waitUntilExit } = render(\n React.createElement(TraceViewerApp, { model }),\n );\n await waitUntilExit();\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { TraceEvent } from 'agent-inspect';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface TuiTraceNode {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
type?: string;
|
|
8
|
+
status?: string;
|
|
9
|
+
durationMs?: number;
|
|
10
|
+
depth: number;
|
|
11
|
+
parentId?: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
children: TuiTraceNode[];
|
|
15
|
+
}
|
|
16
|
+
interface TuiTraceModel {
|
|
17
|
+
runId: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
status?: string;
|
|
20
|
+
durationMs?: number;
|
|
21
|
+
nodes: TuiTraceNode[];
|
|
22
|
+
flatNodes: TuiTraceNode[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build a navigable tree model from v0.1 JSONL trace events.
|
|
27
|
+
* Does not mutate the input array or event objects.
|
|
28
|
+
*/
|
|
29
|
+
declare function buildTuiTraceModel(events: TraceEvent[]): TuiTraceModel;
|
|
30
|
+
/** Total step count in tree (for default expand policy). Exported for tests. */
|
|
31
|
+
declare function countTreeSteps(roots: TuiTraceNode[]): number;
|
|
32
|
+
|
|
33
|
+
interface LoadTraceForTuiOptions {
|
|
34
|
+
runId: string;
|
|
35
|
+
dir?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load JSONL trace events and build a TUI model. Does not write or mutate trace files.
|
|
39
|
+
*/
|
|
40
|
+
declare function loadTraceForTui(options: LoadTraceForTuiOptions): Promise<TuiTraceModel>;
|
|
41
|
+
|
|
42
|
+
type TuiAction = "up" | "down" | "expand" | "collapse" | "toggle" | "details" | "help" | "quit" | "unknown";
|
|
43
|
+
/** Map Ink `useInput` args to a semantic action. Pure helper for tests. */
|
|
44
|
+
declare function mapInputToAction(input: string, key?: Partial<{
|
|
45
|
+
name: string;
|
|
46
|
+
ctrl: boolean;
|
|
47
|
+
meta: boolean;
|
|
48
|
+
shift: boolean;
|
|
49
|
+
return: boolean;
|
|
50
|
+
escape: boolean;
|
|
51
|
+
upArrow: boolean;
|
|
52
|
+
downArrow: boolean;
|
|
53
|
+
leftArrow: boolean;
|
|
54
|
+
rightArrow: boolean;
|
|
55
|
+
}>): TuiAction;
|
|
56
|
+
|
|
57
|
+
/** Small traces: expand all branches. Larger traces: only root rows expanded (show first nesting level). */
|
|
58
|
+
declare function initialExpandedSet(model: TuiTraceModel): Set<string>;
|
|
59
|
+
interface TraceViewerAppProps {
|
|
60
|
+
model: TuiTraceModel;
|
|
61
|
+
onExit?: () => void;
|
|
62
|
+
}
|
|
63
|
+
declare function TraceViewerApp(props: TraceViewerAppProps): React.ReactElement;
|
|
64
|
+
|
|
65
|
+
interface RunTraceViewerOptions {
|
|
66
|
+
runId: string;
|
|
67
|
+
dir?: string;
|
|
68
|
+
}
|
|
69
|
+
declare function runTraceViewer(options: RunTraceViewerOptions): Promise<void>;
|
|
70
|
+
|
|
71
|
+
export { type LoadTraceForTuiOptions, type RunTraceViewerOptions, TraceViewerApp, type TraceViewerAppProps, type TuiAction, type TuiTraceModel, type TuiTraceNode, buildTuiTraceModel, countTreeSteps, initialExpandedSet, loadTraceForTui, mapInputToAction, runTraceViewer };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { TraceEvent } from 'agent-inspect';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface TuiTraceNode {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
type?: string;
|
|
8
|
+
status?: string;
|
|
9
|
+
durationMs?: number;
|
|
10
|
+
depth: number;
|
|
11
|
+
parentId?: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
children: TuiTraceNode[];
|
|
15
|
+
}
|
|
16
|
+
interface TuiTraceModel {
|
|
17
|
+
runId: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
status?: string;
|
|
20
|
+
durationMs?: number;
|
|
21
|
+
nodes: TuiTraceNode[];
|
|
22
|
+
flatNodes: TuiTraceNode[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build a navigable tree model from v0.1 JSONL trace events.
|
|
27
|
+
* Does not mutate the input array or event objects.
|
|
28
|
+
*/
|
|
29
|
+
declare function buildTuiTraceModel(events: TraceEvent[]): TuiTraceModel;
|
|
30
|
+
/** Total step count in tree (for default expand policy). Exported for tests. */
|
|
31
|
+
declare function countTreeSteps(roots: TuiTraceNode[]): number;
|
|
32
|
+
|
|
33
|
+
interface LoadTraceForTuiOptions {
|
|
34
|
+
runId: string;
|
|
35
|
+
dir?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load JSONL trace events and build a TUI model. Does not write or mutate trace files.
|
|
39
|
+
*/
|
|
40
|
+
declare function loadTraceForTui(options: LoadTraceForTuiOptions): Promise<TuiTraceModel>;
|
|
41
|
+
|
|
42
|
+
type TuiAction = "up" | "down" | "expand" | "collapse" | "toggle" | "details" | "help" | "quit" | "unknown";
|
|
43
|
+
/** Map Ink `useInput` args to a semantic action. Pure helper for tests. */
|
|
44
|
+
declare function mapInputToAction(input: string, key?: Partial<{
|
|
45
|
+
name: string;
|
|
46
|
+
ctrl: boolean;
|
|
47
|
+
meta: boolean;
|
|
48
|
+
shift: boolean;
|
|
49
|
+
return: boolean;
|
|
50
|
+
escape: boolean;
|
|
51
|
+
upArrow: boolean;
|
|
52
|
+
downArrow: boolean;
|
|
53
|
+
leftArrow: boolean;
|
|
54
|
+
rightArrow: boolean;
|
|
55
|
+
}>): TuiAction;
|
|
56
|
+
|
|
57
|
+
/** Small traces: expand all branches. Larger traces: only root rows expanded (show first nesting level). */
|
|
58
|
+
declare function initialExpandedSet(model: TuiTraceModel): Set<string>;
|
|
59
|
+
interface TraceViewerAppProps {
|
|
60
|
+
model: TuiTraceModel;
|
|
61
|
+
onExit?: () => void;
|
|
62
|
+
}
|
|
63
|
+
declare function TraceViewerApp(props: TraceViewerAppProps): React.ReactElement;
|
|
64
|
+
|
|
65
|
+
interface RunTraceViewerOptions {
|
|
66
|
+
runId: string;
|
|
67
|
+
dir?: string;
|
|
68
|
+
}
|
|
69
|
+
declare function runTraceViewer(options: RunTraceViewerOptions): Promise<void>;
|
|
70
|
+
|
|
71
|
+
export { type LoadTraceForTuiOptions, type RunTraceViewerOptions, TraceViewerApp, type TraceViewerAppProps, type TuiAction, type TuiTraceModel, type TuiTraceNode, buildTuiTraceModel, countTreeSteps, initialExpandedSet, loadTraceForTui, mapInputToAction, runTraceViewer };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { resolveTraceDir, readTraceEvents } from 'agent-inspect';
|
|
2
|
+
import React2, { useState, useMemo, useEffect } from 'react';
|
|
3
|
+
import { useApp, useInput, Box, Text, render } from 'ink';
|
|
4
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// packages/tui/src/tree-model.ts
|
|
7
|
+
function dfsFlat(roots, out) {
|
|
8
|
+
for (const n of roots) {
|
|
9
|
+
out.push(n);
|
|
10
|
+
if (n.children.length > 0) dfsFlat(n.children, out);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function toTuiNodes(builds, depth) {
|
|
14
|
+
return builds.map((b) => ({
|
|
15
|
+
id: b.id,
|
|
16
|
+
name: b.name,
|
|
17
|
+
type: b.type,
|
|
18
|
+
status: b.status,
|
|
19
|
+
durationMs: b.durationMs,
|
|
20
|
+
depth,
|
|
21
|
+
parentId: b.parentId,
|
|
22
|
+
error: b.error,
|
|
23
|
+
metadata: b.metadata,
|
|
24
|
+
children: toTuiNodes(b.children, depth + 1)
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
function buildTuiTraceModel(events) {
|
|
28
|
+
const started = events.find((e) => e.event === "run_started");
|
|
29
|
+
if (!started) {
|
|
30
|
+
throw new Error("Invalid trace: missing run_started");
|
|
31
|
+
}
|
|
32
|
+
const runId = started.runId;
|
|
33
|
+
const runName = started.name;
|
|
34
|
+
const completedAll = events.filter((e) => e.event === "run_completed");
|
|
35
|
+
const lastCompleted = completedAll[completedAll.length - 1];
|
|
36
|
+
const runStatus = lastCompleted ? lastCompleted.status : "running";
|
|
37
|
+
const runDurationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
38
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
39
|
+
for (const e of events) {
|
|
40
|
+
if (e.event !== "step_started") continue;
|
|
41
|
+
const s = e;
|
|
42
|
+
nodes.set(s.stepId, {
|
|
43
|
+
id: s.stepId,
|
|
44
|
+
parentId: s.parentId,
|
|
45
|
+
name: s.name,
|
|
46
|
+
type: s.type,
|
|
47
|
+
status: "running",
|
|
48
|
+
startedAt: s.startTime,
|
|
49
|
+
metadata: s.metadata,
|
|
50
|
+
children: []
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
for (const e of events) {
|
|
54
|
+
if (e.event !== "step_completed") continue;
|
|
55
|
+
const c = e;
|
|
56
|
+
const node = nodes.get(c.stepId);
|
|
57
|
+
if (!node) continue;
|
|
58
|
+
node.status = c.status;
|
|
59
|
+
node.durationMs = c.durationMs;
|
|
60
|
+
node.error = c.error !== void 0 && typeof c.error.message === "string" ? c.error.message : void 0;
|
|
61
|
+
}
|
|
62
|
+
const roots = [];
|
|
63
|
+
const sortByStarted = (a, b) => a.startedAt - b.startedAt;
|
|
64
|
+
for (const n of nodes.values()) {
|
|
65
|
+
if (n.parentId !== void 0 && nodes.has(n.parentId)) {
|
|
66
|
+
nodes.get(n.parentId).children.push(n);
|
|
67
|
+
} else {
|
|
68
|
+
roots.push(n);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
roots.sort(sortByStarted);
|
|
72
|
+
for (const n of nodes.values()) {
|
|
73
|
+
n.children.sort(sortByStarted);
|
|
74
|
+
}
|
|
75
|
+
const treeRoots = toTuiNodes(roots, 0);
|
|
76
|
+
const flatNodes = [];
|
|
77
|
+
dfsFlat(treeRoots, flatNodes);
|
|
78
|
+
return {
|
|
79
|
+
runId,
|
|
80
|
+
name: runName,
|
|
81
|
+
status: runStatus,
|
|
82
|
+
durationMs: runDurationMs,
|
|
83
|
+
nodes: treeRoots,
|
|
84
|
+
flatNodes
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function countTreeSteps(roots) {
|
|
88
|
+
let n = 0;
|
|
89
|
+
for (const r of roots) {
|
|
90
|
+
n += 1 + countSubtree(r.children);
|
|
91
|
+
}
|
|
92
|
+
return n;
|
|
93
|
+
}
|
|
94
|
+
function countSubtree(children) {
|
|
95
|
+
let n = 0;
|
|
96
|
+
for (const c of children) {
|
|
97
|
+
n += 1 + countSubtree(c.children);
|
|
98
|
+
}
|
|
99
|
+
return n;
|
|
100
|
+
}
|
|
101
|
+
async function loadTraceForTui(options) {
|
|
102
|
+
const traceDir = resolveTraceDir({ dir: options.dir });
|
|
103
|
+
const events = await readTraceEvents(options.runId, traceDir);
|
|
104
|
+
if (events.length === 0) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Run not found or trace is empty: ${options.runId} (directory: ${traceDir})`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return buildTuiTraceModel(events);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// packages/tui/src/keymap.ts
|
|
113
|
+
function mapInputToAction(input, key = {}) {
|
|
114
|
+
if (key.escape === true) return "quit";
|
|
115
|
+
if (key.ctrl === true && input === "c") return "quit";
|
|
116
|
+
if (input === "q" || input === "Q") return "quit";
|
|
117
|
+
if (input === "?") return "help";
|
|
118
|
+
if (input === "d" || input === "D") return "details";
|
|
119
|
+
if (key.return === true) return "expand";
|
|
120
|
+
if (input === " ") return "toggle";
|
|
121
|
+
if (key.upArrow === true || input === "k") return "up";
|
|
122
|
+
if (key.downArrow === true || input === "j") return "down";
|
|
123
|
+
if (key.rightArrow === true || input === "l") return "expand";
|
|
124
|
+
if (key.leftArrow === true || input === "h") return "collapse";
|
|
125
|
+
return "unknown";
|
|
126
|
+
}
|
|
127
|
+
function collectVisible(roots, expanded, out) {
|
|
128
|
+
for (const n of roots) {
|
|
129
|
+
out.push(n);
|
|
130
|
+
if (n.children.length > 0 && expanded.has(n.id)) {
|
|
131
|
+
collectVisible(n.children, expanded, out);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function initialExpandedSet(model) {
|
|
136
|
+
const s = /* @__PURE__ */ new Set();
|
|
137
|
+
const small = model.flatNodes.length <= 15;
|
|
138
|
+
if (small) {
|
|
139
|
+
for (const n of model.flatNodes) {
|
|
140
|
+
if (n.children.length > 0) s.add(n.id);
|
|
141
|
+
}
|
|
142
|
+
return s;
|
|
143
|
+
}
|
|
144
|
+
for (const r of model.nodes) {
|
|
145
|
+
if (r.children.length > 0) s.add(r.id);
|
|
146
|
+
}
|
|
147
|
+
return s;
|
|
148
|
+
}
|
|
149
|
+
function formatDur(ms) {
|
|
150
|
+
if (ms === void 0 || !Number.isFinite(ms)) return "-";
|
|
151
|
+
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
152
|
+
return `${(ms / 1e3).toFixed(2)}s`;
|
|
153
|
+
}
|
|
154
|
+
function TraceViewerApp(props) {
|
|
155
|
+
const { model, onExit } = props;
|
|
156
|
+
const { exit } = useApp();
|
|
157
|
+
const [expanded, setExpanded] = useState(() => initialExpandedSet(model));
|
|
158
|
+
const [sel, setSel] = useState(0);
|
|
159
|
+
const [showHelp, setShowHelp] = useState(false);
|
|
160
|
+
const [showDetails, setShowDetails] = useState(true);
|
|
161
|
+
const visible = useMemo(() => {
|
|
162
|
+
const out = [];
|
|
163
|
+
collectVisible(model.nodes, expanded, out);
|
|
164
|
+
return out;
|
|
165
|
+
}, [model.nodes, expanded]);
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (visible.length === 0) {
|
|
168
|
+
setSel(0);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
setSel((i) => Math.min(i, visible.length - 1));
|
|
172
|
+
}, [visible.length]);
|
|
173
|
+
const safeSel = visible.length === 0 ? 0 : Math.min(sel, visible.length - 1);
|
|
174
|
+
const selected = visible[safeSel];
|
|
175
|
+
useInput((input, key) => {
|
|
176
|
+
const action = mapInputToAction(input, key);
|
|
177
|
+
switch (action) {
|
|
178
|
+
case "quit":
|
|
179
|
+
onExit?.();
|
|
180
|
+
exit();
|
|
181
|
+
break;
|
|
182
|
+
case "help":
|
|
183
|
+
setShowHelp((h) => !h);
|
|
184
|
+
break;
|
|
185
|
+
case "details":
|
|
186
|
+
setShowDetails((d) => !d);
|
|
187
|
+
break;
|
|
188
|
+
case "up":
|
|
189
|
+
setSel((i) => Math.max(0, i - 1));
|
|
190
|
+
break;
|
|
191
|
+
case "down":
|
|
192
|
+
setSel((i) => Math.min(Math.max(0, visible.length - 1), i + 1));
|
|
193
|
+
break;
|
|
194
|
+
case "expand": {
|
|
195
|
+
const cur = visible.length === 0 ? void 0 : visible[safeSel];
|
|
196
|
+
if (cur?.children.length) {
|
|
197
|
+
setExpanded((prev) => new Set(prev).add(cur.id));
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
case "collapse": {
|
|
202
|
+
const cur = visible.length === 0 ? void 0 : visible[safeSel];
|
|
203
|
+
if (cur && expanded.has(cur.id)) {
|
|
204
|
+
setExpanded((prev) => {
|
|
205
|
+
const n = new Set(prev);
|
|
206
|
+
n.delete(cur.id);
|
|
207
|
+
return n;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
case "toggle": {
|
|
213
|
+
const cur = visible.length === 0 ? void 0 : visible[safeSel];
|
|
214
|
+
if (cur?.children.length) {
|
|
215
|
+
setExpanded((prev) => {
|
|
216
|
+
const n = new Set(prev);
|
|
217
|
+
if (n.has(cur.id)) n.delete(cur.id);
|
|
218
|
+
else n.add(cur.id);
|
|
219
|
+
return n;
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
const indent = (d) => " ".repeat(d);
|
|
227
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
228
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
229
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "AgentInspect Trace Viewer" }),
|
|
230
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
231
|
+
model.name ?? "(unnamed)",
|
|
232
|
+
" \xB7 ",
|
|
233
|
+
model.runId,
|
|
234
|
+
" \xB7 ",
|
|
235
|
+
model.status ?? "unknown",
|
|
236
|
+
" \xB7",
|
|
237
|
+
" ",
|
|
238
|
+
formatDur(model.durationMs)
|
|
239
|
+
] })
|
|
240
|
+
] }),
|
|
241
|
+
showHelp ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "round", paddingX: 1, children: [
|
|
242
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Keys" }),
|
|
243
|
+
/* @__PURE__ */ jsx(Text, { children: "\u2191/\u2193 or j/k \u2014 move \xB7 \u2190/\u2192 or h/l \u2014 collapse/expand \xB7 space \u2014 toggle branch" }),
|
|
244
|
+
/* @__PURE__ */ jsx(Text, { children: "enter \u2014 expand \xB7 d \u2014 toggle details \xB7 ? \u2014 this help \xB7 q / Ctrl+C / esc \u2014 quit" })
|
|
245
|
+
] }) : null,
|
|
246
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
247
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: "55%", children: [
|
|
248
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Execution tree" }),
|
|
249
|
+
visible.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No steps recorded" }) : visible.map((n, i) => {
|
|
250
|
+
const branch = n.children.length === 0 ? " " : expanded.has(n.id) ? "\u25BC " : "\u25B6 ";
|
|
251
|
+
const row = `${indent(n.depth)}${branch}${n.name}`;
|
|
252
|
+
const mark = i === safeSel ? "\u203A " : " ";
|
|
253
|
+
const status = n.status ?? "?";
|
|
254
|
+
const line = `${mark}${row} (${status})`;
|
|
255
|
+
return /* @__PURE__ */ jsx(Text, { inverse: i === safeSel, children: line }, n.id);
|
|
256
|
+
})
|
|
257
|
+
] }),
|
|
258
|
+
showDetails ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingLeft: 2, width: "45%", children: [
|
|
259
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Details" }),
|
|
260
|
+
selected ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
261
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
262
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "name: " }),
|
|
263
|
+
selected.name
|
|
264
|
+
] }),
|
|
265
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
266
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "type: " }),
|
|
267
|
+
selected.type ?? "-"
|
|
268
|
+
] }),
|
|
269
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
270
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "status: " }),
|
|
271
|
+
selected.status ?? "-"
|
|
272
|
+
] }),
|
|
273
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
274
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "duration: " }),
|
|
275
|
+
formatDur(selected.durationMs)
|
|
276
|
+
] }),
|
|
277
|
+
selected.error ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
278
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "error: " }),
|
|
279
|
+
selected.error
|
|
280
|
+
] }) : null,
|
|
281
|
+
selected.metadata && Object.keys(selected.metadata).length > 0 ? /* @__PURE__ */ jsxs(Text, { children: [
|
|
282
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "metadata: " }),
|
|
283
|
+
JSON.stringify(selected.metadata)
|
|
284
|
+
] }) : null
|
|
285
|
+
] }) : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Select a step" })
|
|
286
|
+
] }) : null
|
|
287
|
+
] }),
|
|
288
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 or j/k move \xB7 enter/space expand \xB7 h/l collapse/expand \xB7 d details \xB7 ? help \xB7 q quit" }) })
|
|
289
|
+
] });
|
|
290
|
+
}
|
|
291
|
+
var TTY_MSG = "TUI requires an interactive terminal. Use agent-inspect view without --tui for plain output.";
|
|
292
|
+
async function runTraceViewer(options) {
|
|
293
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
294
|
+
throw new Error(TTY_MSG);
|
|
295
|
+
}
|
|
296
|
+
const model = await loadTraceForTui({
|
|
297
|
+
runId: options.runId,
|
|
298
|
+
dir: options.dir
|
|
299
|
+
});
|
|
300
|
+
const { waitUntilExit } = render(
|
|
301
|
+
React2.createElement(TraceViewerApp, { model })
|
|
302
|
+
);
|
|
303
|
+
await waitUntilExit();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export { TraceViewerApp, buildTuiTraceModel, countTreeSteps, initialExpandedSet, loadTraceForTui, mapInputToAction, runTraceViewer };
|
|
307
|
+
//# sourceMappingURL=index.mjs.map
|
|
308
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tree-model.ts","../src/trace-loader.ts","../src/keymap.ts","../src/app.tsx","../src/run-viewer.ts"],"names":["React"],"mappings":";;;;;;AAuBA,SAAS,OAAA,CAAQ,OAAuB,GAAA,EAA2B;AACjE,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,IAAA,GAAA,CAAI,KAAK,CAAC,CAAA;AACV,IAAA,IAAI,EAAE,QAAA,CAAS,MAAA,GAAS,GAAG,OAAA,CAAQ,CAAA,CAAE,UAAU,GAAG,CAAA;AAAA,EACpD;AACF;AAEA,SAAS,UAAA,CAAW,QAAqB,KAAA,EAA+B;AACtE,EAAA,OAAO,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,IACxB,IAAI,CAAA,CAAE,EAAA;AAAA,IACN,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,YAAY,CAAA,CAAE,UAAA;AAAA,IACd,KAAA;AAAA,IACA,UAAU,CAAA,CAAE,QAAA;AAAA,IACZ,OAAO,CAAA,CAAE,KAAA;AAAA,IACT,UAAU,CAAA,CAAE,QAAA;AAAA,IACZ,QAAA,EAAU,UAAA,CAAW,CAAA,CAAE,QAAA,EAAU,QAAQ,CAAC;AAAA,GAC5C,CAAE,CAAA;AACJ;AAMO,SAAS,mBAAmB,MAAA,EAAqC;AACtE,EAAA,MAAM,UAAU,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAA4B,CAAA,CAAE,UAAU,aAAa,CAAA;AAClF,EAAA,IAAI,CAAC,OAAA,EAAS;AACZ,IAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AAAA,EACtD;AAEA,EAAA,MAAM,QAAQ,OAAA,CAAQ,KAAA;AACtB,EAAA,MAAM,UAAU,OAAA,CAAQ,IAAA;AAExB,EAAA,MAAM,eAAe,MAAA,CAAO,MAAA,CAAO,CAAC,CAAA,KAA8B,CAAA,CAAE,UAAU,eAAe,CAAA;AAC7F,EAAA,MAAM,aAAA,GAAgB,YAAA,CAAa,YAAA,CAAa,MAAA,GAAS,CAAC,CAAA;AAC1D,EAAA,MAAM,SAAA,GAAY,aAAA,GAAgB,aAAA,CAAc,MAAA,GAAS,SAAA;AACzD,EAAA,MAAM,aAAA,GACJ,kBAAkB,MAAA,IAAa,MAAA,CAAO,SAAS,aAAA,CAAc,UAAU,CAAA,GACnE,aAAA,CAAc,UAAA,GACd,MAAA;AAEN,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAuB;AAEzC,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,IAAA,IAAI,CAAA,CAAE,UAAU,cAAA,EAAgB;AAChC,IAAA,MAAM,CAAA,GAAI,CAAA;AACV,IAAA,KAAA,CAAM,GAAA,CAAI,EAAE,MAAA,EAAQ;AAAA,MAClB,IAAI,CAAA,CAAE,MAAA;AAAA,MACN,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,MAAA,EAAQ,SAAA;AAAA,MACR,WAAW,CAAA,CAAE,SAAA;AAAA,MACb,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,UAAU;AAAC,KACZ,CAAA;AAAA,EACH;AAEA,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACtB,IAAA,IAAI,CAAA,CAAE,UAAU,gBAAA,EAAkB;AAClC,IAAA,MAAM,CAAA,GAAI,CAAA;AACV,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,CAAA,CAAE,MAAM,CAAA;AAC/B,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,IAAA,CAAK,SAAS,CAAA,CAAE,MAAA;AAChB,IAAA,IAAA,CAAK,aAAa,CAAA,CAAE,UAAA;AACpB,IAAA,IAAA,CAAK,KAAA,GACH,CAAA,CAAE,KAAA,KAAU,MAAA,IAAa,OAAO,CAAA,CAAE,KAAA,CAAM,OAAA,KAAY,QAAA,GAChD,CAAA,CAAE,KAAA,CAAM,OAAA,GACR,MAAA;AAAA,EACR;AAEA,EAAA,MAAM,QAAqB,EAAC;AAC5B,EAAA,MAAM,gBAAgB,CAAC,CAAA,EAAc,CAAA,KAAiB,CAAA,CAAE,YAAY,CAAA,CAAE,SAAA;AAEtE,EAAA,KAAA,MAAW,CAAA,IAAK,KAAA,CAAM,MAAA,EAAO,EAAG;AAC9B,IAAA,IAAI,EAAE,QAAA,KAAa,MAAA,IAAa,MAAM,GAAA,CAAI,CAAA,CAAE,QAAQ,CAAA,EAAG;AACrD,MAAA,KAAA,CAAM,IAAI,CAAA,CAAE,QAAQ,CAAA,CAAG,QAAA,CAAS,KAAK,CAAC,CAAA;AAAA,IACxC,CAAA,MAAO;AACL,MAAA,KAAA,CAAM,KAAK,CAAC,CAAA;AAAA,IACd;AAAA,EACF;AAEA,EAAA,KAAA,CAAM,KAAK,aAAa,CAAA;AACxB,EAAA,KAAA,MAAW,CAAA,IAAK,KAAA,CAAM,MAAA,EAAO,EAAG;AAC9B,IAAA,CAAA,CAAE,QAAA,CAAS,KAAK,aAAa,CAAA;AAAA,EAC/B;AAEA,EAAA,MAAM,SAAA,GAAY,UAAA,CAAW,KAAA,EAAO,CAAC,CAAA;AACrC,EAAA,MAAM,YAA4B,EAAC;AACnC,EAAA,OAAA,CAAQ,WAAW,SAAS,CAAA;AAE5B,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,IAAA,EAAM,OAAA;AAAA,IACN,MAAA,EAAQ,SAAA;AAAA,IACR,UAAA,EAAY,aAAA;AAAA,IACZ,KAAA,EAAO,SAAA;AAAA,IACP;AAAA,GACF;AACF;AAGO,SAAS,eAAe,KAAA,EAA+B;AAC5D,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,IAAA,CAAA,IAAK,CAAA,GAAI,YAAA,CAAa,CAAA,CAAE,QAAQ,CAAA;AAAA,EAClC;AACA,EAAA,OAAO,CAAA;AACT;AAEA,SAAS,aAAa,QAAA,EAAkC;AACtD,EAAA,IAAI,CAAA,GAAI,CAAA;AACR,EAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,IAAA,CAAA,IAAK,CAAA,GAAI,YAAA,CAAa,CAAA,CAAE,QAAQ,CAAA;AAAA,EAClC;AACA,EAAA,OAAO,CAAA;AACT;AChIA,eAAsB,gBAAgB,OAAA,EAAyD;AAC7F,EAAA,MAAM,WAAW,eAAA,CAAgB,EAAE,GAAA,EAAK,OAAA,CAAQ,KAAK,CAAA;AACrD,EAAA,MAAM,MAAA,GAAS,MAAM,eAAA,CAAgB,OAAA,CAAQ,OAAO,QAAQ,CAAA;AAE5D,EAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AACvB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,iCAAA,EAAoC,OAAA,CAAQ,KAAK,CAAA,aAAA,EAAgB,QAAQ,CAAA,CAAA;AAAA,KAC3E;AAAA,EACF;AAEA,EAAA,OAAO,mBAAmB,MAAM,CAAA;AAClC;;;ACZO,SAAS,gBAAA,CACd,KAAA,EACA,GAAA,GAWK,EAAC,EACK;AACX,EAAA,IAAI,GAAA,CAAI,MAAA,KAAW,IAAA,EAAM,OAAO,MAAA;AAChC,EAAA,IAAI,GAAA,CAAI,IAAA,KAAS,IAAA,IAAQ,KAAA,KAAU,KAAK,OAAO,MAAA;AAE/C,EAAA,IAAI,KAAA,KAAU,GAAA,IAAO,KAAA,KAAU,GAAA,EAAK,OAAO,MAAA;AAE3C,EAAA,IAAI,KAAA,KAAU,KAAK,OAAO,MAAA;AAE1B,EAAA,IAAI,KAAA,KAAU,GAAA,IAAO,KAAA,KAAU,GAAA,EAAK,OAAO,SAAA;AAE3C,EAAA,IAAI,GAAA,CAAI,MAAA,KAAW,IAAA,EAAM,OAAO,QAAA;AAEhC,EAAA,IAAI,KAAA,KAAU,KAAK,OAAO,QAAA;AAE1B,EAAA,IAAI,GAAA,CAAI,OAAA,KAAY,IAAA,IAAQ,KAAA,KAAU,KAAK,OAAO,IAAA;AAClD,EAAA,IAAI,GAAA,CAAI,SAAA,KAAc,IAAA,IAAQ,KAAA,KAAU,KAAK,OAAO,MAAA;AACpD,EAAA,IAAI,GAAA,CAAI,UAAA,KAAe,IAAA,IAAQ,KAAA,KAAU,KAAK,OAAO,QAAA;AACrD,EAAA,IAAI,GAAA,CAAI,SAAA,KAAc,IAAA,IAAQ,KAAA,KAAU,KAAK,OAAO,UAAA;AAEpD,EAAA,OAAO,SAAA;AACT;ACxCA,SAAS,cAAA,CACP,KAAA,EACA,QAAA,EACA,GAAA,EACM;AACN,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,IAAA,GAAA,CAAI,KAAK,CAAC,CAAA;AACV,IAAA,IAAI,CAAA,CAAE,SAAS,MAAA,GAAS,CAAA,IAAK,SAAS,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,EAAG;AAC/C,MAAA,cAAA,CAAe,CAAA,CAAE,QAAA,EAAU,QAAA,EAAU,GAAG,CAAA;AAAA,IAC1C;AAAA,EACF;AACF;AAGO,SAAS,mBAAmB,KAAA,EAAmC;AACpE,EAAA,MAAM,CAAA,uBAAQ,GAAA,EAAY;AAC1B,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,SAAA,CAAU,MAAA,IAAU,EAAA;AACxC,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,KAAA,MAAW,CAAA,IAAK,MAAM,SAAA,EAAW;AAC/B,MAAA,IAAI,EAAE,QAAA,CAAS,MAAA,GAAS,GAAG,CAAA,CAAE,GAAA,CAAI,EAAE,EAAE,CAAA;AAAA,IACvC;AACA,IAAA,OAAO,CAAA;AAAA,EACT;AACA,EAAA,KAAA,MAAW,CAAA,IAAK,MAAM,KAAA,EAAO;AAC3B,IAAA,IAAI,EAAE,QAAA,CAAS,MAAA,GAAS,GAAG,CAAA,CAAE,GAAA,CAAI,EAAE,EAAE,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,CAAA;AACT;AAEA,SAAS,UAAU,EAAA,EAAgC;AACjD,EAAA,IAAI,OAAO,MAAA,IAAa,CAAC,OAAO,QAAA,CAAS,EAAE,GAAG,OAAO,GAAA;AACrD,EAAA,IAAI,KAAK,GAAA,EAAM,OAAO,GAAG,IAAA,CAAK,KAAA,CAAM,EAAE,CAAC,CAAA,EAAA,CAAA;AACvC,EAAA,OAAO,CAAA,EAAA,CAAI,EAAA,GAAK,GAAA,EAAM,OAAA,CAAQ,CAAC,CAAC,CAAA,CAAA,CAAA;AAClC;AAOO,SAAS,eAAe,KAAA,EAAgD;AAC7E,EAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAO,GAAI,KAAA;AAC1B,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAA,EAAO;AACxB,EAAA,MAAM,CAAC,UAAU,WAAW,CAAA,GAAI,SAAS,MAAM,kBAAA,CAAmB,KAAK,CAAC,CAAA;AACxE,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAI,SAAS,CAAC,CAAA;AAChC,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,KAAK,CAAA;AAC9C,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,IAAI,CAAA;AAEnD,EAAA,MAAM,OAAA,GAAU,QAAQ,MAAM;AAC5B,IAAA,MAAM,MAAsB,EAAC;AAC7B,IAAA,cAAA,CAAe,KAAA,CAAM,KAAA,EAAO,QAAA,EAAU,GAAG,CAAA;AACzC,IAAA,OAAO,GAAA;AAAA,EACT,CAAA,EAAG,CAAC,KAAA,CAAM,KAAA,EAAO,QAAQ,CAAC,CAAA;AAE1B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,MAAA,MAAA,CAAO,CAAC,CAAA;AACR,MAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,CAAC,MAAM,IAAA,CAAK,GAAA,CAAI,GAAG,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAC,CAAA;AAAA,EAC/C,CAAA,EAAG,CAAC,OAAA,CAAQ,MAAM,CAAC,CAAA;AAEnB,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,MAAA,KAAW,CAAA,GAAI,CAAA,GAAI,KAAK,GAAA,CAAI,GAAA,EAAK,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA;AAC3E,EAAA,MAAM,QAAA,GAAW,QAAQ,OAAO,CAAA;AAEhC,EAAA,QAAA,CAAS,CAAC,OAAO,GAAA,KAAQ;AACvB,IAAA,MAAM,MAAA,GAAS,gBAAA,CAAiB,KAAA,EAAO,GAAG,CAAA;AAC1C,IAAA,QAAQ,MAAA;AAAQ,MACd,KAAK,MAAA;AACH,QAAA,MAAA,IAAS;AACT,QAAA,IAAA,EAAK;AACL,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,WAAA,CAAY,CAAC,CAAA,KAAM,CAAC,CAAC,CAAA;AACrB,QAAA;AAAA,MACF,KAAK,SAAA;AACH,QAAA,cAAA,CAAe,CAAC,CAAA,KAAM,CAAC,CAAC,CAAA;AACxB,QAAA;AAAA,MACF,KAAK,IAAA;AACH,QAAA,MAAA,CAAO,CAAC,CAAA,KAAM,IAAA,CAAK,IAAI,CAAA,EAAG,CAAA,GAAI,CAAC,CAAC,CAAA;AAChC,QAAA;AAAA,MACF,KAAK,MAAA;AACH,QAAA,MAAA,CAAO,CAAC,CAAA,KAAM,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAA,CAAQ,MAAA,GAAS,CAAC,CAAA,EAAG,CAAA,GAAI,CAAC,CAAC,CAAA;AAC9D,QAAA;AAAA,MACF,KAAK,QAAA,EAAU;AACb,QAAA,MAAM,MAAM,OAAA,CAAQ,MAAA,KAAW,CAAA,GAAI,MAAA,GAAY,QAAQ,OAAO,CAAA;AAC9D,QAAA,IAAI,GAAA,EAAK,SAAS,MAAA,EAAQ;AACxB,UAAA,WAAA,CAAY,CAAC,SAAS,IAAI,GAAA,CAAI,IAAI,CAAA,CAAE,GAAA,CAAI,GAAA,CAAI,EAAE,CAAC,CAAA;AAAA,QACjD;AACA,QAAA;AAAA,MACF;AAAA,MACA,KAAK,UAAA,EAAY;AACf,QAAA,MAAM,MAAM,OAAA,CAAQ,MAAA,KAAW,CAAA,GAAI,MAAA,GAAY,QAAQ,OAAO,CAAA;AAC9D,QAAA,IAAI,GAAA,IAAO,QAAA,CAAS,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA,EAAG;AAC/B,UAAA,WAAA,CAAY,CAAC,IAAA,KAAS;AACpB,YAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,IAAI,CAAA;AACtB,YAAA,CAAA,CAAE,MAAA,CAAO,IAAI,EAAE,CAAA;AACf,YAAA,OAAO,CAAA;AAAA,UACT,CAAC,CAAA;AAAA,QACH;AACA,QAAA;AAAA,MACF;AAAA,MACA,KAAK,QAAA,EAAU;AACb,QAAA,MAAM,MAAM,OAAA,CAAQ,MAAA,KAAW,CAAA,GAAI,MAAA,GAAY,QAAQ,OAAO,CAAA;AAC9D,QAAA,IAAI,GAAA,EAAK,SAAS,MAAA,EAAQ;AACxB,UAAA,WAAA,CAAY,CAAC,IAAA,KAAS;AACpB,YAAA,MAAM,CAAA,GAAI,IAAI,GAAA,CAAI,IAAI,CAAA;AACtB,YAAA,IAAI,CAAA,CAAE,IAAI,GAAA,CAAI,EAAE,GAAG,CAAA,CAAE,MAAA,CAAO,IAAI,EAAE,CAAA;AAAA,iBAC7B,CAAA,CAAE,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AACjB,YAAA,OAAO,CAAA;AAAA,UACT,CAAC,CAAA;AAAA,QACH;AACA,QAAA;AAAA,MACF;AAEE;AACJ,EACF,CAAC,CAAA;AAED,EAAA,MAAM,MAAA,GAAS,CAAC,CAAA,KAAc,IAAA,CAAK,OAAO,CAAC,CAAA;AAE3C,EAAA,uBACE,IAAA,CAAC,GAAA,EAAA,EAAI,aAAA,EAAc,QAAA,EACjB,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,GAAA,EAAA,EAAI,aAAA,EAAc,QAAA,EAAS,YAAA,EAAc,CAAA,EACxC,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,QAAA,EAAA,2BAAA,EAAyB,CAAA;AAAA,2BACnC,IAAA,EAAA,EACE,QAAA,EAAA;AAAA,QAAA,KAAA,CAAM,IAAA,IAAQ,WAAA;AAAA,QAAY,QAAA;AAAA,QAAI,KAAA,CAAM,KAAA;AAAA,QAAM,QAAA;AAAA,QAAI,MAAM,MAAA,IAAU,SAAA;AAAA,QAAU,OAAA;AAAA,QAAG,GAAA;AAAA,QAC3E,SAAA,CAAU,MAAM,UAAU;AAAA,OAAA,EAC7B;AAAA,KAAA,EACF,CAAA;AAAA,IAEC,QAAA,mBACC,IAAA,CAAC,GAAA,EAAA,EAAI,aAAA,EAAc,QAAA,EAAS,cAAc,CAAA,EAAG,WAAA,EAAY,OAAA,EAAQ,QAAA,EAAU,CAAA,EACzE,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,QAAA,EAAA,MAAA,EAAI,CAAA;AAAA,sBACf,GAAA,CAAC,QAAK,QAAA,EAAA,mHAAA,EAAwE,CAAA;AAAA,sBAC9E,GAAA,CAAC,QAAK,QAAA,EAAA,4GAAA,EAA6E;AAAA,KAAA,EACrF,CAAA,GACE,IAAA;AAAA,oBAEJ,IAAA,CAAC,GAAA,EAAA,EAAI,aAAA,EAAc,KAAA,EACjB,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,GAAA,EAAA,EAAI,aAAA,EAAc,QAAA,EAAS,KAAA,EAAM,KAAA,EAChC,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,QAAA,EAAQ,IAAA,EAAC,QAAA,EAAA,gBAAA,EAAc,CAAA;AAAA,QAC5B,OAAA,CAAQ,MAAA,KAAW,CAAA,mBAClB,GAAA,CAAC,IAAA,EAAA,EAAK,QAAA,EAAQ,IAAA,EAAC,QAAA,EAAA,mBAAA,EAAiB,CAAA,GAEhC,OAAA,CAAQ,GAAA,CAAI,CAAC,GAAG,CAAA,KAAM;AACpB,UAAA,MAAM,MAAA,GACJ,CAAA,CAAE,QAAA,CAAS,MAAA,KAAW,CAAA,GAClB,IAAA,GACA,QAAA,CAAS,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,GACf,SAAA,GACA,SAAA;AACR,UAAA,MAAM,GAAA,GAAM,CAAA,EAAG,MAAA,CAAO,CAAA,CAAE,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,EAAG,CAAA,CAAE,IAAI,CAAA,CAAA;AAChD,UAAA,MAAM,IAAA,GAAO,CAAA,KAAM,OAAA,GAAU,SAAA,GAAO,IAAA;AACpC,UAAA,MAAM,MAAA,GAAS,EAAE,MAAA,IAAU,GAAA;AAC3B,UAAA,MAAM,OAAO,CAAA,EAAG,IAAI,CAAA,EAAG,GAAG,KAAK,MAAM,CAAA,CAAA,CAAA;AACrC,UAAA,2BACG,IAAA,EAAA,EAAgB,OAAA,EAAS,MAAM,OAAA,EAC7B,QAAA,EAAA,IAAA,EAAA,EADQ,EAAE,EAEb,CAAA;AAAA,QAEJ,CAAC;AAAA,OAAA,EAEL,CAAA;AAAA,MAEC,WAAA,wBACE,GAAA,EAAA,EAAI,aAAA,EAAc,UAAS,WAAA,EAAa,CAAA,EAAG,OAAM,KAAA,EAChD,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,QAAA,EAAQ,IAAA,EAAC,QAAA,EAAA,SAAA,EAAO,CAAA;AAAA,QACrB,2BACC,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,0BAAA,IAAA,CAAC,IAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,QAAA,EAAA,QAAA,EAAM,CAAA;AAAA,YAChB,QAAA,CAAS;AAAA,WAAA,EACZ,CAAA;AAAA,+BACC,IAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,QAAA,EAAA,QAAA,EAAM,CAAA;AAAA,YAChB,SAAS,IAAA,IAAQ;AAAA,WAAA,EACpB,CAAA;AAAA,+BACC,IAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,QAAA,EAAA,UAAA,EAAQ,CAAA;AAAA,YAClB,SAAS,MAAA,IAAU;AAAA,WAAA,EACtB,CAAA;AAAA,+BACC,IAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,QAAA,EAAA,YAAA,EAAU,CAAA;AAAA,YACpB,SAAA,CAAU,SAAS,UAAU;AAAA,WAAA,EAChC,CAAA;AAAA,UACC,QAAA,CAAS,KAAA,mBACR,IAAA,CAAC,IAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,QAAA,EAAA,SAAA,EAAO,CAAA;AAAA,YACjB,QAAA,CAAS;AAAA,WAAA,EACZ,CAAA,GACE,IAAA;AAAA,UACH,QAAA,CAAS,QAAA,IAAY,MAAA,CAAO,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA,CAAE,MAAA,GAAS,CAAA,mBAC5D,IAAA,CAAC,IAAA,EAAA,EACC,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,IAAA,EAAA,EAAK,IAAA,EAAI,IAAA,EAAC,QAAA,EAAA,YAAA,EAAU,CAAA;AAAA,YACpB,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,QAAQ;AAAA,WAAA,EACnC,CAAA,GACE;AAAA,SAAA,EACN,CAAA,mBAEA,GAAA,CAAC,IAAA,EAAA,EAAK,QAAA,EAAQ,MAAC,QAAA,EAAA,eAAA,EAAa;AAAA,OAAA,EAEhC,CAAA,GACE;AAAA,KAAA,EACN,CAAA;AAAA,oBAEA,GAAA,CAAC,OAAI,SAAA,EAAW,CAAA,EACd,8BAAC,IAAA,EAAA,EAAK,QAAA,EAAQ,IAAA,EAAC,QAAA,EAAA,mHAAA,EAEf,CAAA,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;AChNA,IAAM,OAAA,GACJ,8FAAA;AAEF,eAAsB,eAAe,OAAA,EAA+C;AAClF,EAAA,IAAI,CAAC,OAAA,CAAQ,MAAA,CAAO,SAAS,CAAC,OAAA,CAAQ,MAAM,KAAA,EAAO;AACjD,IAAA,MAAM,IAAI,MAAM,OAAO,CAAA;AAAA,EACzB;AAEA,EAAA,MAAM,KAAA,GAAQ,MAAM,eAAA,CAAgB;AAAA,IAClC,OAAO,OAAA,CAAQ,KAAA;AAAA,IACf,KAAK,OAAA,CAAQ;AAAA,GACd,CAAA;AAED,EAAA,MAAM,EAAE,eAAc,GAAI,MAAA;AAAA,IACxBA,MAAAA,CAAM,aAAA,CAAc,cAAA,EAAgB,EAAE,OAAO;AAAA,GAC/C;AACA,EAAA,MAAM,aAAA,EAAc;AACtB","file":"index.mjs","sourcesContent":["import type {\n RunCompletedEvent,\n RunStartedEvent,\n StepCompletedEvent,\n StepStartedEvent,\n TraceEvent,\n} from \"agent-inspect\";\n\nimport type { TuiTraceModel, TuiTraceNode } from \"./types.js\";\n\ntype StepBuild = {\n id: string;\n parentId?: string;\n name: string;\n type: string;\n status: string;\n durationMs?: number;\n startedAt: number;\n error?: string;\n metadata?: Record<string, unknown>;\n children: StepBuild[];\n};\n\nfunction dfsFlat(roots: TuiTraceNode[], out: TuiTraceNode[]): void {\n for (const n of roots) {\n out.push(n);\n if (n.children.length > 0) dfsFlat(n.children, out);\n }\n}\n\nfunction toTuiNodes(builds: StepBuild[], depth: number): TuiTraceNode[] {\n return builds.map((b) => ({\n id: b.id,\n name: b.name,\n type: b.type,\n status: b.status,\n durationMs: b.durationMs,\n depth,\n parentId: b.parentId,\n error: b.error,\n metadata: b.metadata,\n children: toTuiNodes(b.children, depth + 1),\n }));\n}\n\n/**\n * Build a navigable tree model from v0.1 JSONL trace events.\n * Does not mutate the input array or event objects.\n */\nexport function buildTuiTraceModel(events: TraceEvent[]): TuiTraceModel {\n const started = events.find((e): e is RunStartedEvent => e.event === \"run_started\");\n if (!started) {\n throw new Error(\"Invalid trace: missing run_started\");\n }\n\n const runId = started.runId;\n const runName = started.name;\n\n const completedAll = events.filter((e): e is RunCompletedEvent => e.event === \"run_completed\");\n const lastCompleted = completedAll[completedAll.length - 1];\n const runStatus = lastCompleted ? lastCompleted.status : \"running\";\n const runDurationMs =\n lastCompleted !== undefined && Number.isFinite(lastCompleted.durationMs)\n ? lastCompleted.durationMs\n : undefined;\n\n const nodes = new Map<string, StepBuild>();\n\n for (const e of events) {\n if (e.event !== \"step_started\") continue;\n const s = e as StepStartedEvent;\n nodes.set(s.stepId, {\n id: s.stepId,\n parentId: s.parentId,\n name: s.name,\n type: s.type,\n status: \"running\",\n startedAt: s.startTime,\n metadata: s.metadata as Record<string, unknown> | undefined,\n children: [],\n });\n }\n\n for (const e of events) {\n if (e.event !== \"step_completed\") continue;\n const c = e as StepCompletedEvent;\n const node = nodes.get(c.stepId);\n if (!node) continue;\n node.status = c.status;\n node.durationMs = c.durationMs;\n node.error =\n c.error !== undefined && typeof c.error.message === \"string\"\n ? c.error.message\n : undefined;\n }\n\n const roots: StepBuild[] = [];\n const sortByStarted = (a: StepBuild, b: StepBuild) => a.startedAt - b.startedAt;\n\n for (const n of nodes.values()) {\n if (n.parentId !== undefined && nodes.has(n.parentId)) {\n nodes.get(n.parentId)!.children.push(n);\n } else {\n roots.push(n);\n }\n }\n\n roots.sort(sortByStarted);\n for (const n of nodes.values()) {\n n.children.sort(sortByStarted);\n }\n\n const treeRoots = toTuiNodes(roots, 0);\n const flatNodes: TuiTraceNode[] = [];\n dfsFlat(treeRoots, flatNodes);\n\n return {\n runId,\n name: runName,\n status: runStatus,\n durationMs: runDurationMs,\n nodes: treeRoots,\n flatNodes,\n };\n}\n\n/** Total step count in tree (for default expand policy). Exported for tests. */\nexport function countTreeSteps(roots: TuiTraceNode[]): number {\n let n = 0;\n for (const r of roots) {\n n += 1 + countSubtree(r.children);\n }\n return n;\n}\n\nfunction countSubtree(children: TuiTraceNode[]): number {\n let n = 0;\n for (const c of children) {\n n += 1 + countSubtree(c.children);\n }\n return n;\n}\n","import { readTraceEvents, resolveTraceDir } from \"agent-inspect\";\n\nimport { buildTuiTraceModel } from \"./tree-model.js\";\nimport type { TuiTraceModel } from \"./types.js\";\n\nexport interface LoadTraceForTuiOptions {\n runId: string;\n dir?: string;\n}\n\n/**\n * Load JSONL trace events and build a TUI model. Does not write or mutate trace files.\n */\nexport async function loadTraceForTui(options: LoadTraceForTuiOptions): Promise<TuiTraceModel> {\n const traceDir = resolveTraceDir({ dir: options.dir });\n const events = await readTraceEvents(options.runId, traceDir);\n\n if (events.length === 0) {\n throw new Error(\n `Run not found or trace is empty: ${options.runId} (directory: ${traceDir})`,\n );\n }\n\n return buildTuiTraceModel(events);\n}\n","export type TuiAction =\n | \"up\"\n | \"down\"\n | \"expand\"\n | \"collapse\"\n | \"toggle\"\n | \"details\"\n | \"help\"\n | \"quit\"\n | \"unknown\";\n\n/** Map Ink `useInput` args to a semantic action. Pure helper for tests. */\nexport function mapInputToAction(\n input: string,\n key: Partial<{\n name: string;\n ctrl: boolean;\n meta: boolean;\n shift: boolean;\n return: boolean;\n escape: boolean;\n upArrow: boolean;\n downArrow: boolean;\n leftArrow: boolean;\n rightArrow: boolean;\n }> = {},\n): TuiAction {\n if (key.escape === true) return \"quit\";\n if (key.ctrl === true && input === \"c\") return \"quit\";\n\n if (input === \"q\" || input === \"Q\") return \"quit\";\n\n if (input === \"?\") return \"help\";\n\n if (input === \"d\" || input === \"D\") return \"details\";\n\n if (key.return === true) return \"expand\";\n\n if (input === \" \") return \"toggle\";\n\n if (key.upArrow === true || input === \"k\") return \"up\";\n if (key.downArrow === true || input === \"j\") return \"down\";\n if (key.rightArrow === true || input === \"l\") return \"expand\";\n if (key.leftArrow === true || input === \"h\") return \"collapse\";\n\n return \"unknown\";\n}\n","import React, { useEffect, useMemo, useState } from \"react\";\nimport { Box, Text, useApp, useInput } from \"ink\";\n\nimport { mapInputToAction } from \"./keymap.js\";\nimport type { TuiTraceModel, TuiTraceNode } from \"./types.js\";\n\nfunction collectVisible(\n roots: TuiTraceNode[],\n expanded: ReadonlySet<string>,\n out: TuiTraceNode[],\n): void {\n for (const n of roots) {\n out.push(n);\n if (n.children.length > 0 && expanded.has(n.id)) {\n collectVisible(n.children, expanded, out);\n }\n }\n}\n\n/** Small traces: expand all branches. Larger traces: only root rows expanded (show first nesting level). */\nexport function initialExpandedSet(model: TuiTraceModel): Set<string> {\n const s = new Set<string>();\n const small = model.flatNodes.length <= 15;\n if (small) {\n for (const n of model.flatNodes) {\n if (n.children.length > 0) s.add(n.id);\n }\n return s;\n }\n for (const r of model.nodes) {\n if (r.children.length > 0) s.add(r.id);\n }\n return s;\n}\n\nfunction formatDur(ms: number | undefined): string {\n if (ms === undefined || !Number.isFinite(ms)) return \"-\";\n if (ms < 1000) return `${Math.round(ms)}ms`;\n return `${(ms / 1000).toFixed(2)}s`;\n}\n\nexport interface TraceViewerAppProps {\n model: TuiTraceModel;\n onExit?: () => void;\n}\n\nexport function TraceViewerApp(props: TraceViewerAppProps): React.ReactElement {\n const { model, onExit } = props;\n const { exit } = useApp();\n const [expanded, setExpanded] = useState(() => initialExpandedSet(model));\n const [sel, setSel] = useState(0);\n const [showHelp, setShowHelp] = useState(false);\n const [showDetails, setShowDetails] = useState(true);\n\n const visible = useMemo(() => {\n const out: TuiTraceNode[] = [];\n collectVisible(model.nodes, expanded, out);\n return out;\n }, [model.nodes, expanded]);\n\n useEffect(() => {\n if (visible.length === 0) {\n setSel(0);\n return;\n }\n setSel((i) => Math.min(i, visible.length - 1));\n }, [visible.length]);\n\n const safeSel = visible.length === 0 ? 0 : Math.min(sel, visible.length - 1);\n const selected = visible[safeSel];\n\n useInput((input, key) => {\n const action = mapInputToAction(input, key);\n switch (action) {\n case \"quit\":\n onExit?.();\n exit();\n break;\n case \"help\":\n setShowHelp((h) => !h);\n break;\n case \"details\":\n setShowDetails((d) => !d);\n break;\n case \"up\":\n setSel((i) => Math.max(0, i - 1));\n break;\n case \"down\":\n setSel((i) => Math.min(Math.max(0, visible.length - 1), i + 1));\n break;\n case \"expand\": {\n const cur = visible.length === 0 ? undefined : visible[safeSel];\n if (cur?.children.length) {\n setExpanded((prev) => new Set(prev).add(cur.id));\n }\n break;\n }\n case \"collapse\": {\n const cur = visible.length === 0 ? undefined : visible[safeSel];\n if (cur && expanded.has(cur.id)) {\n setExpanded((prev) => {\n const n = new Set(prev);\n n.delete(cur.id);\n return n;\n });\n }\n break;\n }\n case \"toggle\": {\n const cur = visible.length === 0 ? undefined : visible[safeSel];\n if (cur?.children.length) {\n setExpanded((prev) => {\n const n = new Set(prev);\n if (n.has(cur.id)) n.delete(cur.id);\n else n.add(cur.id);\n return n;\n });\n }\n break;\n }\n default:\n break;\n }\n });\n\n const indent = (d: number) => \" \".repeat(d);\n\n return (\n <Box flexDirection=\"column\">\n <Box flexDirection=\"column\" marginBottom={1}>\n <Text bold>AgentInspect Trace Viewer</Text>\n <Text>\n {model.name ?? \"(unnamed)\"} · {model.runId} · {model.status ?? \"unknown\"} ·{\" \"}\n {formatDur(model.durationMs)}\n </Text>\n </Box>\n\n {showHelp ? (\n <Box flexDirection=\"column\" marginBottom={1} borderStyle=\"round\" paddingX={1}>\n <Text bold>Keys</Text>\n <Text>↑/↓ or j/k — move · ←/→ or h/l — collapse/expand · space — toggle branch</Text>\n <Text>enter — expand · d — toggle details · ? — this help · q / Ctrl+C / esc — quit</Text>\n </Box>\n ) : null}\n\n <Box flexDirection=\"row\">\n <Box flexDirection=\"column\" width=\"55%\">\n <Text dimColor>Execution tree</Text>\n {visible.length === 0 ? (\n <Text dimColor>No steps recorded</Text>\n ) : (\n visible.map((n, i) => {\n const branch =\n n.children.length === 0\n ? \" \"\n : expanded.has(n.id)\n ? \"▼ \"\n : \"▶ \";\n const row = `${indent(n.depth)}${branch}${n.name}`;\n const mark = i === safeSel ? \"› \" : \" \";\n const status = n.status ?? \"?\";\n const line = `${mark}${row} (${status})`;\n return (\n <Text key={n.id} inverse={i === safeSel}>\n {line}\n </Text>\n );\n })\n )}\n </Box>\n\n {showDetails ? (\n <Box flexDirection=\"column\" paddingLeft={2} width=\"45%\">\n <Text dimColor>Details</Text>\n {selected ? (\n <>\n <Text>\n <Text bold>name: </Text>\n {selected.name}\n </Text>\n <Text>\n <Text bold>type: </Text>\n {selected.type ?? \"-\"}\n </Text>\n <Text>\n <Text bold>status: </Text>\n {selected.status ?? \"-\"}\n </Text>\n <Text>\n <Text bold>duration: </Text>\n {formatDur(selected.durationMs)}\n </Text>\n {selected.error ? (\n <Text>\n <Text bold>error: </Text>\n {selected.error}\n </Text>\n ) : null}\n {selected.metadata && Object.keys(selected.metadata).length > 0 ? (\n <Text>\n <Text bold>metadata: </Text>\n {JSON.stringify(selected.metadata)}\n </Text>\n ) : null}\n </>\n ) : (\n <Text dimColor>Select a step</Text>\n )}\n </Box>\n ) : null}\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n ↑/↓ or j/k move · enter/space expand · h/l collapse/expand · d details · ? help · q quit\n </Text>\n </Box>\n </Box>\n );\n}\n","import React from \"react\";\nimport { render } from \"ink\";\n\nimport { TraceViewerApp } from \"./app.js\";\nimport { loadTraceForTui } from \"./trace-loader.js\";\n\nexport interface RunTraceViewerOptions {\n runId: string;\n dir?: string;\n}\n\nconst TTY_MSG =\n \"TUI requires an interactive terminal. Use agent-inspect view without --tui for plain output.\";\n\nexport async function runTraceViewer(options: RunTraceViewerOptions): Promise<void> {\n if (!process.stdout.isTTY || !process.stdin.isTTY) {\n throw new Error(TTY_MSG);\n }\n\n const model = await loadTraceForTui({\n runId: options.runId,\n dir: options.dir,\n });\n\n const { waitUntilExit } = render(\n React.createElement(TraceViewerApp, { model }),\n );\n await waitUntilExit();\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agent-inspect/tui",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"description": "Optional Ink-based terminal UI for inspecting AgentInspect traces",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.mjs",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.mjs"
|
|
17
|
+
},
|
|
18
|
+
"require": {
|
|
19
|
+
"types": "./dist/index.d.cts",
|
|
20
|
+
"default": "./dist/index.cjs"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"ink": "^5.0.1",
|
|
29
|
+
"react": "^18.3.1",
|
|
30
|
+
"agent-inspect": "1.0.3"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/react": "^18.3.18"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "pnpm --workspace-root exec tsup --config tsup.tui.config.ts",
|
|
37
|
+
"test": "vitest run -c ../../vitest.config.ts"
|
|
38
|
+
}
|
|
39
|
+
}
|