agentfootprint-lens 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,934 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ AgentLens: () => AgentLens,
24
+ IterationStrip: () => IterationStrip,
25
+ MessagesPanel: () => MessagesPanel,
26
+ ToolCallInspector: () => ToolCallInspector,
27
+ fromAgentSnapshot: () => fromAgentSnapshot,
28
+ resolveLensTheme: () => resolve,
29
+ useLensTheme: () => useLensTheme
30
+ });
31
+ module.exports = __toCommonJS(src_exports);
32
+
33
+ // src/AgentLens.tsx
34
+ var import_react2 = require("react");
35
+
36
+ // src/adapters/fromAgentSnapshot.ts
37
+ function fromAgentSnapshot(runtime) {
38
+ const shared = runtime?.sharedState ?? {};
39
+ const rawMessages = shared.messages ?? [];
40
+ const messages = rawMessages.map(normalizeMessage);
41
+ const commitLog = Array.isArray(runtime?.commitLog) ? runtime.commitLog : [];
42
+ const llmCalls = extractLLMCalls(commitLog);
43
+ const toolExecs = extractToolExecutions(commitLog);
44
+ const instructionEvals = extractInstructionEvals(commitLog);
45
+ const toolResolves = extractToolResolves(commitLog);
46
+ const turns = assembleTurns(messages, llmCalls, toolExecs, instructionEvals, toolResolves);
47
+ const allTools = turns.flatMap((t) => t.iterations.flatMap((i) => i.toolCalls));
48
+ return {
49
+ turns,
50
+ messages,
51
+ tools: allTools,
52
+ finalDecision: shared.decision ?? {},
53
+ rawSnapshot: runtime
54
+ };
55
+ }
56
+ function normalizeMessage(m) {
57
+ const role = (() => {
58
+ if (m.role === "system" || m.role === "user" || m.role === "assistant" || m.role === "tool")
59
+ return m.role;
60
+ return "user";
61
+ })();
62
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
63
+ return {
64
+ role,
65
+ content,
66
+ ...m.toolCalls?.length ? { toolCalls: m.toolCalls } : {},
67
+ ...m.toolCallId ? { toolCallId: m.toolCallId } : {}
68
+ };
69
+ }
70
+ function extractLLMCalls(commitLog) {
71
+ const out = [];
72
+ let iter = 0;
73
+ for (const entry of commitLog) {
74
+ const id = entry.stageId ?? "";
75
+ if (id !== "call-llm" && id !== "streaming-call-llm") continue;
76
+ iter++;
77
+ const u = entry.updates ?? {};
78
+ const rawResponse = u["adapterRawResponse"] ?? u["llmResponse"];
79
+ const usage = rawResponse?.usage;
80
+ out.push({
81
+ iterationIndex: iter,
82
+ model: rawResponse?.model,
83
+ inputTokens: usage?.inputTokens,
84
+ outputTokens: usage?.outputTokens,
85
+ durationMs: u["callDurationMs"] ?? void 0,
86
+ stopReason: rawResponse?.stopReason,
87
+ assistantContent: rawResponse?.content ?? "",
88
+ toolCallsRequested: rawResponse?.toolCalls ?? []
89
+ });
90
+ }
91
+ return out;
92
+ }
93
+ function extractToolExecutions(commitLog) {
94
+ const out = [];
95
+ let iter = 0;
96
+ for (const entry of commitLog) {
97
+ const id = entry.stageId ?? "";
98
+ if (id !== "execute-tool-calls") continue;
99
+ iter++;
100
+ const u = entry.updates ?? {};
101
+ const rawCalls = u["toolCalls"];
102
+ const stubs = (rawCalls ?? []).map((c) => ({
103
+ id: c.id,
104
+ name: c.name,
105
+ arguments: c.arguments ?? {},
106
+ result: "",
107
+ // filled in during assembly
108
+ iterationIndex: iter,
109
+ turnIndex: 0,
110
+ // reassigned during assembly
111
+ decisionUpdate: u["updatedDecision"]
112
+ }));
113
+ out.push({ iterationIndex: iter, toolCalls: stubs });
114
+ }
115
+ return out;
116
+ }
117
+ function extractInstructionEvals(commitLog) {
118
+ const out = [];
119
+ let iter = 0;
120
+ for (const entry of commitLog) {
121
+ const id = entry.stageId ?? "";
122
+ if (id !== "evaluate-instructions") continue;
123
+ iter++;
124
+ const u = entry.updates ?? {};
125
+ const matchedRaw = u["matchedInstructions"];
126
+ const matched = parseMatched(matchedRaw);
127
+ const promptInj = Array.isArray(u["promptInjections"]) ? u["promptInjections"].length : 0;
128
+ const toolInj = Array.isArray(u["toolInjections"]) ? u["toolInjections"].length : 0;
129
+ out.push({
130
+ iterationIndex: iter,
131
+ ...matched.length > 0 && { matchedInstructions: matched },
132
+ promptInjectionCount: promptInj,
133
+ toolInjectionCount: toolInj
134
+ });
135
+ }
136
+ return out;
137
+ }
138
+ function parseMatched(v) {
139
+ if (!v) return [];
140
+ if (Array.isArray(v)) return v.filter((x) => typeof x === "string");
141
+ if (typeof v === "string") {
142
+ const m = v.match(/:\s*(.+)/);
143
+ if (!m) return [];
144
+ return m[1].split(",").map((s) => s.trim().replace(/\.{3}$/, "")).filter(Boolean);
145
+ }
146
+ return [];
147
+ }
148
+ function extractToolResolves(commitLog) {
149
+ const out = [];
150
+ let iter = 0;
151
+ for (const entry of commitLog) {
152
+ const id = entry.stageId ?? "";
153
+ if (id !== "resolve-tools") continue;
154
+ iter++;
155
+ const u = entry.updates ?? {};
156
+ const descs = u["toolDescriptions"];
157
+ out.push({
158
+ iterationIndex: iter,
159
+ visibleTools: (descs ?? []).map((d) => d.name)
160
+ });
161
+ }
162
+ return out;
163
+ }
164
+ function assembleTurns(messages, llmCalls, toolExecs, instructionEvals, toolResolves) {
165
+ const turns = [];
166
+ let currentTurn = null;
167
+ let llmIdx = 0;
168
+ const toolExecByIter = new Map(toolExecs.map((t) => [t.iterationIndex, t]));
169
+ const instrByIter = new Map(instructionEvals.map((i) => [i.iterationIndex, i]));
170
+ const visibleByIter = new Map(toolResolves.map((t) => [t.iterationIndex, t]));
171
+ for (const msg of messages) {
172
+ if (msg.role === "user") {
173
+ if (currentTurn) turns.push(finalizeTurn(currentTurn));
174
+ currentTurn = {
175
+ index: turns.length,
176
+ userPrompt: msg.content,
177
+ iterations: [],
178
+ finalContent: ""
179
+ };
180
+ continue;
181
+ }
182
+ if (!currentTurn) continue;
183
+ if (msg.role === "assistant") {
184
+ const call = llmCalls[llmIdx];
185
+ llmIdx++;
186
+ const iterIndex = call?.iterationIndex ?? currentTurn.iterations.length + 1;
187
+ const exec = toolExecByIter.get(iterIndex);
188
+ const instr = instrByIter.get(iterIndex);
189
+ const visible = visibleByIter.get(iterIndex);
190
+ const toolCalls = (exec?.toolCalls ?? []).map((tc) => ({
191
+ ...tc,
192
+ turnIndex: currentTurn.index
193
+ }));
194
+ const iteration = {
195
+ index: iterIndex,
196
+ ...call?.model && { model: call.model },
197
+ ...call?.inputTokens !== void 0 && { inputTokens: call.inputTokens },
198
+ ...call?.outputTokens !== void 0 && { outputTokens: call.outputTokens },
199
+ ...call?.durationMs !== void 0 && { durationMs: call.durationMs },
200
+ ...call?.stopReason && { stopReason: call.stopReason },
201
+ assistantContent: call?.assistantContent ?? msg.content,
202
+ toolCalls,
203
+ decisionAtStart: {},
204
+ // TODO(phase-2): derive from pre-iter commit
205
+ ...instr?.matchedInstructions && { matchedInstructions: instr.matchedInstructions },
206
+ visibleTools: visible?.visibleTools ?? []
207
+ };
208
+ currentTurn.iterations.push(iteration);
209
+ if (!toolCalls.length) currentTurn.finalContent = iteration.assistantContent;
210
+ continue;
211
+ }
212
+ if (msg.role === "tool" && msg.toolCallId) {
213
+ const iter = currentTurn.iterations[currentTurn.iterations.length - 1];
214
+ if (!iter) continue;
215
+ const idx = iter.toolCalls.findIndex((tc) => tc.id === msg.toolCallId);
216
+ if (idx < 0) continue;
217
+ const updated = { ...iter.toolCalls[idx], result: msg.content };
218
+ iter.toolCalls[idx] = updated;
219
+ }
220
+ }
221
+ if (currentTurn) turns.push(finalizeTurn(currentTurn));
222
+ return turns;
223
+ }
224
+ function finalizeTurn(t) {
225
+ const totalInputTokens = t.iterations.reduce((s, i) => s + (i.inputTokens ?? 0), 0);
226
+ const totalOutputTokens = t.iterations.reduce((s, i) => s + (i.outputTokens ?? 0), 0);
227
+ const totalDurationMs = t.iterations.reduce((s, i) => s + (i.durationMs ?? 0), 0);
228
+ return {
229
+ index: t.index,
230
+ userPrompt: t.userPrompt,
231
+ iterations: t.iterations,
232
+ finalContent: t.finalContent,
233
+ totalInputTokens,
234
+ totalOutputTokens,
235
+ totalDurationMs
236
+ };
237
+ }
238
+
239
+ // src/panels/MessagesPanel.tsx
240
+ var import_react = require("react");
241
+
242
+ // src/theme/useLensTheme.ts
243
+ var import_footprint_explainable_ui = require("footprint-explainable-ui");
244
+ function useLensTheme() {
245
+ const ctx = (0, import_footprint_explainable_ui.useFootprintTheme)();
246
+ return resolve(ctx);
247
+ }
248
+ function resolve(tokens) {
249
+ const t = tokens ?? {};
250
+ const c = t.colors ?? import_footprint_explainable_ui.coolDark.colors ?? {};
251
+ const fallback = import_footprint_explainable_ui.coolDark.colors ?? {};
252
+ return {
253
+ bg: c.bgPrimary ?? fallback.bgPrimary ?? "#0f172a",
254
+ bgElev: c.bgSecondary ?? fallback.bgSecondary ?? "#1e293b",
255
+ bgHover: c.bgTertiary ?? fallback.bgTertiary ?? "#334155",
256
+ border: c.border ?? fallback.border ?? "#334155",
257
+ borderStrong: c.bgTertiary ?? fallback.bgTertiary ?? "#334155",
258
+ text: c.textPrimary ?? fallback.textPrimary ?? "#f8fafc",
259
+ textMuted: c.textSecondary ?? fallback.textSecondary ?? "#94a3b8",
260
+ textSubtle: c.textMuted ?? fallback.textMuted ?? "#64748b",
261
+ accent: c.primary ?? fallback.primary ?? "#6366f1",
262
+ success: c.success ?? fallback.success ?? "#22c55e",
263
+ warning: c.warning ?? fallback.warning ?? "#f59e0b",
264
+ error: c.error ?? fallback.error ?? "#ef4444",
265
+ fontSans: t.fontFamily?.sans ?? import_footprint_explainable_ui.coolDark.fontFamily?.sans ?? "system-ui, sans-serif",
266
+ fontMono: t.fontFamily?.mono ?? import_footprint_explainable_ui.coolDark.fontFamily?.mono ?? "ui-monospace, monospace",
267
+ radius: t.radius ?? import_footprint_explainable_ui.coolDark.radius ?? "8px"
268
+ };
269
+ }
270
+
271
+ // src/panels/MessagesPanel.tsx
272
+ var import_jsx_runtime = require("react/jsx-runtime");
273
+ function MessagesPanel({
274
+ timeline,
275
+ onToolCallClick,
276
+ systemPrompt
277
+ }) {
278
+ const t = useLensTheme();
279
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
280
+ "div",
281
+ {
282
+ "data-fp-lens": "messages-panel",
283
+ style: {
284
+ display: "flex",
285
+ flexDirection: "column",
286
+ gap: 16,
287
+ padding: "16px 24px",
288
+ background: t.bg,
289
+ color: t.text,
290
+ fontFamily: t.fontSans,
291
+ fontSize: 14,
292
+ lineHeight: 1.55,
293
+ minHeight: 0,
294
+ overflow: "auto"
295
+ },
296
+ children: [
297
+ systemPrompt && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SystemBubble, { text: systemPrompt }),
298
+ timeline.turns.map((turn) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TurnBlock, { turn, onToolCallClick }, turn.index))
299
+ ]
300
+ }
301
+ );
302
+ }
303
+ function SystemBubble({ text }) {
304
+ const t = useLensTheme();
305
+ const [open, setOpen] = (0, import_react.useState)(false);
306
+ const preview = text.slice(0, 140).replace(/\s+/g, " ") + (text.length > 140 ? "\u2026" : "");
307
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
308
+ "div",
309
+ {
310
+ style: {
311
+ alignSelf: "center",
312
+ width: "100%",
313
+ maxWidth: 880,
314
+ border: `1px dashed ${t.border}`,
315
+ borderRadius: t.radius,
316
+ padding: "8px 12px",
317
+ background: t.bgElev,
318
+ fontSize: 12,
319
+ color: t.textMuted
320
+ },
321
+ children: [
322
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
323
+ "button",
324
+ {
325
+ onClick: () => setOpen((v) => !v),
326
+ style: {
327
+ background: "transparent",
328
+ border: "none",
329
+ color: t.textMuted,
330
+ cursor: "pointer",
331
+ padding: 0,
332
+ font: "inherit"
333
+ },
334
+ children: [
335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "SYSTEM" }),
336
+ " ",
337
+ open ? "\u25BE" : "\u25B8",
338
+ " ",
339
+ open ? "" : preview
340
+ ]
341
+ }
342
+ ),
343
+ open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
344
+ "pre",
345
+ {
346
+ style: {
347
+ marginTop: 8,
348
+ whiteSpace: "pre-wrap",
349
+ fontFamily: t.fontMono,
350
+ fontSize: 11,
351
+ color: t.text
352
+ },
353
+ children: text
354
+ }
355
+ )
356
+ ]
357
+ }
358
+ );
359
+ }
360
+ function TurnBlock({
361
+ turn,
362
+ onToolCallClick
363
+ }) {
364
+ const t = useLensTheme();
365
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
366
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TurnHeader, { turn }),
367
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UserBubble, { text: turn.userPrompt }),
368
+ turn.iterations.map((iter) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IterationBlock, { iter, onToolCallClick }, iter.index)),
369
+ turn.finalContent && turn.iterations.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 11, color: t.textSubtle, textAlign: "center" }, children: [
370
+ "turn ",
371
+ turn.index + 1,
372
+ " final \xB7 ",
373
+ turn.iterations.length,
374
+ " iter \xB7 ",
375
+ turn.totalInputTokens,
376
+ "\u2192",
377
+ turn.totalOutputTokens,
378
+ " tok \xB7 ",
379
+ (turn.totalDurationMs / 1e3).toFixed(1),
380
+ "s"
381
+ ] })
382
+ ] });
383
+ }
384
+ function TurnHeader({ turn }) {
385
+ const t = useLensTheme();
386
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
387
+ "div",
388
+ {
389
+ style: {
390
+ display: "flex",
391
+ alignItems: "center",
392
+ gap: 8,
393
+ color: t.textSubtle,
394
+ fontSize: 11,
395
+ textTransform: "uppercase",
396
+ letterSpacing: "0.08em",
397
+ fontWeight: 600
398
+ },
399
+ children: [
400
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, height: 1, background: t.border } }),
401
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
402
+ "Turn ",
403
+ turn.index + 1
404
+ ] }),
405
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, height: 1, background: t.border } })
406
+ ]
407
+ }
408
+ );
409
+ }
410
+ function UserBubble({ text }) {
411
+ const t = useLensTheme();
412
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", justifyContent: "flex-end" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
413
+ "div",
414
+ {
415
+ style: {
416
+ background: `color-mix(in srgb, ${t.accent} 18%, ${t.bgElev})`,
417
+ border: `1px solid ${t.border}`,
418
+ color: t.text,
419
+ maxWidth: 720,
420
+ padding: "10px 14px",
421
+ borderRadius: `${t.radius} ${t.radius} 2px ${t.radius}`,
422
+ whiteSpace: "pre-wrap"
423
+ },
424
+ children: text
425
+ }
426
+ ) });
427
+ }
428
+ function IterationBlock({
429
+ iter,
430
+ onToolCallClick
431
+ }) {
432
+ const t = useLensTheme();
433
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
434
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IterationBadge, { iter }),
435
+ iter.assistantContent && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
436
+ "div",
437
+ {
438
+ style: {
439
+ background: t.bgElev,
440
+ border: `1px solid ${t.border}`,
441
+ borderRadius: `2px ${t.radius} ${t.radius} ${t.radius}`,
442
+ padding: "10px 14px",
443
+ maxWidth: 820,
444
+ whiteSpace: "pre-wrap"
445
+ },
446
+ children: iter.assistantContent
447
+ }
448
+ ),
449
+ iter.toolCalls.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 6, paddingLeft: 12 }, children: iter.toolCalls.map((tc) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToolCallCard, { invocation: tc, onClick: onToolCallClick }, tc.id)) })
450
+ ] });
451
+ }
452
+ function IterationBadge({ iter }) {
453
+ const t = useLensTheme();
454
+ const bits = [`iter ${iter.index}`];
455
+ if (iter.model) bits.push(iter.model);
456
+ if (iter.inputTokens !== void 0)
457
+ bits.push(`${iter.inputTokens}\u2192${iter.outputTokens ?? "?"} tok`);
458
+ if (iter.durationMs !== void 0) bits.push(`${(iter.durationMs / 1e3).toFixed(2)}s`);
459
+ if (iter.stopReason) bits.push(iter.stopReason);
460
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
461
+ "div",
462
+ {
463
+ style: {
464
+ alignSelf: "flex-start",
465
+ fontSize: 10,
466
+ color: t.textSubtle,
467
+ textTransform: "uppercase",
468
+ letterSpacing: "0.08em",
469
+ fontWeight: 600,
470
+ fontFamily: t.fontMono
471
+ },
472
+ children: bits.join(" \xB7 ")
473
+ }
474
+ );
475
+ }
476
+ function ToolCallCard({
477
+ invocation,
478
+ onClick
479
+ }) {
480
+ const t = useLensTheme();
481
+ const [open, setOpen] = (0, import_react.useState)(false);
482
+ const preview = shortArgs(invocation.arguments);
483
+ const errored = invocation.error === true;
484
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
485
+ "div",
486
+ {
487
+ style: {
488
+ border: `1px solid ${errored ? t.error : t.border}`,
489
+ borderLeft: `3px solid ${errored ? t.error : t.accent}`,
490
+ borderRadius: 6,
491
+ background: t.bg,
492
+ overflow: "hidden"
493
+ },
494
+ children: [
495
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
496
+ "div",
497
+ {
498
+ onClick: () => {
499
+ setOpen((v) => !v);
500
+ onClick?.(invocation);
501
+ },
502
+ style: {
503
+ padding: "8px 12px",
504
+ cursor: "pointer",
505
+ display: "flex",
506
+ alignItems: "center",
507
+ gap: 10,
508
+ fontSize: 12,
509
+ fontFamily: t.fontMono
510
+ },
511
+ children: [
512
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: errored ? t.error : t.accent, fontWeight: 600 }, children: invocation.name }),
513
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { color: t.textMuted }, children: [
514
+ "(",
515
+ preview,
516
+ ")"
517
+ ] }),
518
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
519
+ invocation.decisionUpdate && Object.keys(invocation.decisionUpdate).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
520
+ "span",
521
+ {
522
+ style: {
523
+ fontSize: 10,
524
+ padding: "1px 6px",
525
+ borderRadius: 3,
526
+ background: `color-mix(in srgb, ${t.warning} 20%, transparent)`,
527
+ color: t.warning,
528
+ fontFamily: t.fontSans,
529
+ fontWeight: 600,
530
+ textTransform: "uppercase"
531
+ },
532
+ children: "decisionUpdate"
533
+ }
534
+ ),
535
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: t.textSubtle }, children: open ? "\u25BE" : "\u25B8" })
536
+ ]
537
+ }
538
+ ),
539
+ open && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "8px 12px", borderTop: `1px solid ${t.border}` }, children: [
540
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, { t, children: "args" }),
541
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JsonBlock, { value: invocation.arguments }),
542
+ invocation.result && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
543
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, { t, style: { marginTop: 10 }, children: "result" }),
544
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
545
+ "pre",
546
+ {
547
+ style: {
548
+ margin: 0,
549
+ padding: "8px 10px",
550
+ background: t.bgElev,
551
+ borderRadius: 4,
552
+ fontSize: 11,
553
+ fontFamily: t.fontMono,
554
+ color: errored ? t.error : t.text,
555
+ maxHeight: 280,
556
+ overflow: "auto",
557
+ whiteSpace: "pre-wrap"
558
+ },
559
+ children: invocation.result
560
+ }
561
+ )
562
+ ] }),
563
+ invocation.decisionUpdate && Object.keys(invocation.decisionUpdate).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
564
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Label, { t, style: { marginTop: 10 }, children: "decisionUpdate" }),
565
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JsonBlock, { value: invocation.decisionUpdate })
566
+ ] })
567
+ ] })
568
+ ]
569
+ }
570
+ );
571
+ }
572
+ function Label({
573
+ t,
574
+ children,
575
+ style
576
+ }) {
577
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
578
+ "div",
579
+ {
580
+ style: {
581
+ fontSize: 10,
582
+ color: t.textSubtle,
583
+ textTransform: "uppercase",
584
+ letterSpacing: "0.08em",
585
+ fontWeight: 600,
586
+ marginBottom: 4,
587
+ ...style
588
+ },
589
+ children
590
+ }
591
+ );
592
+ }
593
+ function JsonBlock({ value }) {
594
+ const t = useLensTheme();
595
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
596
+ "pre",
597
+ {
598
+ style: {
599
+ margin: 0,
600
+ padding: "8px 10px",
601
+ background: t.bgElev,
602
+ borderRadius: 4,
603
+ fontSize: 11,
604
+ fontFamily: t.fontMono,
605
+ color: t.text,
606
+ maxHeight: 200,
607
+ overflow: "auto"
608
+ },
609
+ children: JSON.stringify(value, null, 2)
610
+ }
611
+ );
612
+ }
613
+ function shortArgs(args) {
614
+ const keys = Object.keys(args);
615
+ if (keys.length === 0) return "";
616
+ if (keys.length === 1) {
617
+ const v = args[keys[0]];
618
+ if (typeof v === "string" && v.length < 40) return `${keys[0]}: "${v}"`;
619
+ }
620
+ return keys.join(", ");
621
+ }
622
+
623
+ // src/panels/IterationStrip.tsx
624
+ var import_jsx_runtime2 = require("react/jsx-runtime");
625
+ function IterationStrip({ timeline, selectedKey, onSelect }) {
626
+ const t = useLensTheme();
627
+ const chips = timeline.turns.flatMap(
628
+ (turn) => turn.iterations.map((iter) => ({
629
+ key: `${turn.index}.${iter.index}`,
630
+ turn: turn.index + 1,
631
+ iter: iter.index,
632
+ label: chipLabel(iter),
633
+ tools: iter.toolCalls.length,
634
+ durationMs: iter.durationMs ?? 0,
635
+ stopReason: iter.stopReason
636
+ }))
637
+ );
638
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
639
+ "div",
640
+ {
641
+ "data-fp-lens": "iteration-strip",
642
+ style: {
643
+ display: "flex",
644
+ gap: 4,
645
+ padding: "8px 12px",
646
+ overflowX: "auto",
647
+ borderBottom: `1px solid ${t.border}`,
648
+ background: t.bgElev
649
+ },
650
+ children: [
651
+ chips.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: t.textSubtle, fontSize: 11 }, children: "No iterations yet." }),
652
+ chips.map((c) => {
653
+ const active = c.key === selectedKey;
654
+ const isFinal = c.stopReason === "stop" || c.stopReason === "end_turn";
655
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
656
+ "button",
657
+ {
658
+ onClick: () => onSelect?.(c.key),
659
+ style: {
660
+ background: active ? t.accent : "transparent",
661
+ color: active ? "#fff" : t.textMuted,
662
+ border: `1px solid ${active ? t.accent : t.border}`,
663
+ borderRadius: 4,
664
+ padding: "4px 8px",
665
+ fontSize: 11,
666
+ fontFamily: "ui-monospace, monospace",
667
+ cursor: "pointer",
668
+ whiteSpace: "nowrap",
669
+ flexShrink: 0
670
+ },
671
+ title: `turn ${c.turn} \xB7 iter ${c.iter} \xB7 ${c.tools} tool call${c.tools === 1 ? "" : "s"}${c.stopReason ? ` \xB7 ${c.stopReason}` : ""}`,
672
+ children: [
673
+ "t",
674
+ c.turn,
675
+ ".i",
676
+ c.iter,
677
+ " \xB7 ",
678
+ c.label,
679
+ " ",
680
+ isFinal ? "\u2713" : ""
681
+ ]
682
+ },
683
+ c.key
684
+ );
685
+ })
686
+ ]
687
+ }
688
+ );
689
+ }
690
+ function chipLabel(iter) {
691
+ const d = iter.durationMs ?? 0;
692
+ const secs = d >= 1e3 ? `${(d / 1e3).toFixed(1)}s` : `${Math.round(d)}ms`;
693
+ const toolBit = iter.toolCalls.length > 0 ? `${iter.toolCalls.length}t` : "final";
694
+ return `${secs} \xB7 ${toolBit}`;
695
+ }
696
+
697
+ // src/panels/ToolCallInspector.tsx
698
+ var import_jsx_runtime3 = require("react/jsx-runtime");
699
+ function ToolCallInspector({
700
+ timeline,
701
+ selectedId,
702
+ onSelect
703
+ }) {
704
+ const t = useLensTheme();
705
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
706
+ "div",
707
+ {
708
+ "data-fp-lens": "tool-call-inspector",
709
+ style: {
710
+ background: t.bg,
711
+ color: t.text,
712
+ fontFamily: t.fontSans,
713
+ display: "flex",
714
+ flexDirection: "column",
715
+ minHeight: 0,
716
+ overflow: "hidden"
717
+ },
718
+ children: [
719
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
720
+ "div",
721
+ {
722
+ style: {
723
+ padding: "10px 14px",
724
+ borderBottom: `1px solid ${t.border}`,
725
+ fontSize: 11,
726
+ color: t.textSubtle,
727
+ textTransform: "uppercase",
728
+ letterSpacing: "0.08em",
729
+ fontWeight: 600,
730
+ background: t.bgElev
731
+ },
732
+ children: [
733
+ "Tool calls \xB7 ",
734
+ timeline.tools.length
735
+ ]
736
+ }
737
+ ),
738
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { overflow: "auto", flex: 1 }, children: [
739
+ timeline.tools.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: 14, color: t.textSubtle, fontSize: 12 }, children: "No tool calls yet." }),
740
+ timeline.tools.map((tc) => {
741
+ const active = tc.id === selectedId;
742
+ const errored = tc.error === true;
743
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
744
+ "button",
745
+ {
746
+ onClick: () => onSelect?.(tc),
747
+ style: {
748
+ display: "flex",
749
+ flexDirection: "column",
750
+ alignItems: "stretch",
751
+ width: "100%",
752
+ textAlign: "left",
753
+ padding: "8px 12px",
754
+ background: active ? t.bgHover : "transparent",
755
+ border: "none",
756
+ borderLeft: `3px solid ${active ? t.accent : errored ? t.error : "transparent"}`,
757
+ borderBottom: `1px solid ${t.border}`,
758
+ color: t.text,
759
+ cursor: "pointer",
760
+ fontFamily: "inherit"
761
+ },
762
+ children: [
763
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
764
+ "div",
765
+ {
766
+ style: {
767
+ display: "flex",
768
+ gap: 8,
769
+ alignItems: "baseline",
770
+ fontSize: 12,
771
+ fontFamily: t.fontMono
772
+ },
773
+ children: [
774
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { color: errored ? t.error : t.accent, fontWeight: 600 }, children: tc.name }),
775
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
776
+ "span",
777
+ {
778
+ style: { color: t.textSubtle, fontSize: 10, marginLeft: "auto" },
779
+ children: [
780
+ "t",
781
+ tc.turnIndex + 1,
782
+ ".i",
783
+ tc.iterationIndex,
784
+ tc.durationMs !== void 0 && ` \xB7 ${Math.round(tc.durationMs)}ms`
785
+ ]
786
+ }
787
+ )
788
+ ]
789
+ }
790
+ ),
791
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
792
+ "div",
793
+ {
794
+ style: {
795
+ fontSize: 10,
796
+ color: t.textMuted,
797
+ marginTop: 2,
798
+ fontFamily: t.fontMono,
799
+ overflow: "hidden",
800
+ textOverflow: "ellipsis",
801
+ whiteSpace: "nowrap"
802
+ },
803
+ children: shortArgs2(tc.arguments)
804
+ }
805
+ )
806
+ ]
807
+ },
808
+ tc.id
809
+ );
810
+ })
811
+ ] })
812
+ ]
813
+ }
814
+ );
815
+ }
816
+ function shortArgs2(args) {
817
+ const keys = Object.keys(args);
818
+ if (keys.length === 0) return "\u2014 no args \u2014";
819
+ return keys.map((k) => {
820
+ const v = args[k];
821
+ if (typeof v === "string") return `${k}: "${v.length > 20 ? v.slice(0, 20) + "\u2026" : v}"`;
822
+ if (typeof v === "number" || typeof v === "boolean") return `${k}: ${v}`;
823
+ return `${k}: \u2026`;
824
+ }).join(", ");
825
+ }
826
+
827
+ // src/AgentLens.tsx
828
+ var import_jsx_runtime4 = require("react/jsx-runtime");
829
+ function AgentLens({
830
+ runtimeSnapshot,
831
+ timeline: providedTimeline,
832
+ systemPrompt,
833
+ onToolCallClick
834
+ }) {
835
+ const t = useLensTheme();
836
+ const timeline = (0, import_react2.useMemo)(() => {
837
+ if (providedTimeline) return providedTimeline;
838
+ if (!runtimeSnapshot) return null;
839
+ return fromAgentSnapshot(runtimeSnapshot);
840
+ }, [providedTimeline, runtimeSnapshot]);
841
+ const [selectedToolId, setSelectedToolId] = (0, import_react2.useState)(null);
842
+ const [selectedIterKey, setSelectedIterKey] = (0, import_react2.useState)(null);
843
+ function handleToolClick(inv) {
844
+ setSelectedToolId(inv.id);
845
+ setSelectedIterKey(`${inv.turnIndex}.${inv.iterationIndex}`);
846
+ onToolCallClick?.(inv);
847
+ }
848
+ if (!timeline) {
849
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
850
+ "div",
851
+ {
852
+ "data-fp-lens": "empty",
853
+ style: {
854
+ padding: 32,
855
+ color: t.textMuted,
856
+ fontFamily: t.fontSans,
857
+ textAlign: "center",
858
+ background: t.bg
859
+ },
860
+ children: [
861
+ "No agent run to show yet. Pass ",
862
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { children: "runtimeSnapshot" }),
863
+ " after the agent runs."
864
+ ]
865
+ }
866
+ );
867
+ }
868
+ const derivedSystemPrompt = systemPrompt ?? (typeof timeline.rawSnapshot?.sharedState?.systemPrompt === "string" ? timeline.rawSnapshot.sharedState.systemPrompt : void 0);
869
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
870
+ "div",
871
+ {
872
+ "data-fp-lens": "shell",
873
+ style: {
874
+ display: "grid",
875
+ gridTemplateColumns: "1fr 320px",
876
+ gridTemplateRows: "auto 1fr",
877
+ gridTemplateAreas: '"strip strip" "messages inspector"',
878
+ height: "100%",
879
+ minHeight: 0,
880
+ background: t.bg,
881
+ color: t.text,
882
+ fontFamily: t.fontSans
883
+ },
884
+ children: [
885
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { gridArea: "strip" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
886
+ IterationStrip,
887
+ {
888
+ timeline,
889
+ selectedKey: selectedIterKey,
890
+ onSelect: setSelectedIterKey
891
+ }
892
+ ) }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { gridArea: "messages", minHeight: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
894
+ MessagesPanel,
895
+ {
896
+ timeline,
897
+ onToolCallClick: handleToolClick,
898
+ ...derivedSystemPrompt && { systemPrompt: derivedSystemPrompt }
899
+ }
900
+ ) }),
901
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
902
+ "div",
903
+ {
904
+ style: {
905
+ gridArea: "inspector",
906
+ minHeight: 0,
907
+ overflow: "hidden",
908
+ borderLeft: `1px solid ${t.border}`
909
+ },
910
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
911
+ ToolCallInspector,
912
+ {
913
+ timeline,
914
+ selectedId: selectedToolId,
915
+ onSelect: handleToolClick
916
+ }
917
+ )
918
+ }
919
+ )
920
+ ]
921
+ }
922
+ );
923
+ }
924
+ // Annotate the CommonJS export names for ESM import in node:
925
+ 0 && (module.exports = {
926
+ AgentLens,
927
+ IterationStrip,
928
+ MessagesPanel,
929
+ ToolCallInspector,
930
+ fromAgentSnapshot,
931
+ resolveLensTheme,
932
+ useLensTheme
933
+ });
934
+ //# sourceMappingURL=index.cjs.map