@tangle-network/sandbox-ui 2.0.6 → 2.0.7

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.
@@ -1,612 +0,0 @@
1
- import { t as cn } from "./utils-BER_bdgE.js";
2
- import { i as modelBrandFor, t as ModelBrandStack } from "./model-brand-bTcMjQtB.js";
3
- import { t as ProviderIcon } from "./provider-logo-CPKRPHcS.js";
4
- import { f as syncAmbient, l as activityTiming, t as AgentGraph, u as ambientStyle } from "./AgentGraph-CFlI9qta.js";
5
- import { t as LatestEvents } from "./latest-events-v_vXRyd7.js";
6
- import { jsx, jsxs } from "react/jsx-runtime";
7
- import { useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
8
- import { X } from "lucide-react";
9
- import { AgentTimeline } from "@tangle-network/ui/chat";
10
- import { ApprovalCard, ApprovalDiffSummary, ExpandedToolDetail, InlineThinkingItem, InlineThinkingItem as InlineThinkingItem$1, InlineToolItem, LiveDuration, RunGroup, RunPhaseList, RunRowShell, RunRowStatusDot, RunStatusIcon as RunStatusIcon$1, TaskList, ToolCallFeed, parseToolEvent, runStatus, runStatus as runStatus$1, showsTaskPriority, statusMeta as statusMeta$1, taskProgress, taskStatus } from "@tangle-network/ui/run";
11
- import { FlowWaterfall, toolGlyph } from "@tangle-network/charts/react";
12
- // src/run/trace-waterfall.tsx
13
- /** Agent and model spans show their lab/host mark; tool spans show their provider's mark. Unknown keeps the kind glyph. */
14
- function traceSpanMark(span) {
15
- if (span.kind === "pipeline" || span.kind === "model") {
16
- const identity = span.detail ? modelBrandFor(span.detail) : null;
17
- return identity ? /* @__PURE__ */ jsx(ModelBrandStack, {
18
- identity,
19
- size: "sm"
20
- }) : void 0;
21
- }
22
- const provider = span.icon ?? (span.kind === "tool" ? span.detail : void 0);
23
- return provider ? /* @__PURE__ */ jsx(ProviderIcon, {
24
- id: provider,
25
- displayName: provider,
26
- size: 14,
27
- className: "rounded-[3px]"
28
- }) : void 0;
29
- }
30
- /**
31
- * The run inspector: the agent timeline (`defaultView="agents"`) and the span waterfall over one clock, from
32
- * `@tangle-network/charts`, with Sandbox UI's brand marks. Feed it `otlpFlowTrace(spans)` from the
33
- * `traces convert` contract. Import `@tangle-network/charts/react/flow-waterfall.css` once.
34
- */
35
- function TraceWaterfall({ renderIcon = traceSpanMark, ...props }) {
36
- return /* @__PURE__ */ jsx(FlowWaterfall, {
37
- renderIcon,
38
- ...props
39
- });
40
- }
41
- // src/run/agent-stream.ts
42
- const TOOL_TYPES = /* @__PURE__ */ new Set([
43
- "bash",
44
- "read",
45
- "write",
46
- "edit",
47
- "glob",
48
- "grep",
49
- "list",
50
- "download",
51
- "inspect",
52
- "audit"
53
- ]);
54
- const toolType = (name) => {
55
- const key = (name ?? "").toLowerCase();
56
- return TOOL_TYPES.has(key) ? key : "unknown";
57
- };
58
- const brief = (value, max = 240) => {
59
- if (value === void 0 || value === null) return void 0;
60
- const text = typeof value === "string" ? value : JSON.stringify(value);
61
- return text.length > max ? `${text.slice(0, max - 1)}…` : text;
62
- };
63
- const failed = (result) => typeof result === "object" && result !== null && ("error" in result || result.is_error === true);
64
- const recorded = (text) => {
65
- try {
66
- const value = JSON.parse(text);
67
- return typeof value === "object" && value !== null ? `\`\`\`json\n${text}\n\`\`\`` : text;
68
- } catch {
69
- return text;
70
- }
71
- };
72
- /** Fold stream rows into transcript entries: text of one turn merges, a tool call becomes a row its result completes. */
73
- function foldAgentRows(entries, rows) {
74
- const next = [...entries];
75
- for (const row of rows) {
76
- const at = Date.parse(row.at);
77
- const { event } = row;
78
- const last = next[next.length - 1];
79
- if (event.kind === "text" && event.text) {
80
- if (last?.kind === "message" && last.role === "assistant" && !last.sealed) next[next.length - 1] = {
81
- ...last,
82
- content: last.content + event.text
83
- };
84
- else next.push({
85
- id: `m-${row.seq}`,
86
- at,
87
- kind: "message",
88
- role: "assistant",
89
- content: event.text
90
- });
91
- } else if (event.kind === "user" && event.text) next.push({
92
- id: `u-${row.seq}`,
93
- at,
94
- kind: "message",
95
- role: "user",
96
- content: event.text
97
- });
98
- else if (event.kind === "notice" && event.text) {
99
- if (event.provenance === "delegated-input") next.push({
100
- id: `u-${row.seq}`,
101
- at,
102
- kind: "message",
103
- role: "user",
104
- content: recorded(event.text)
105
- });
106
- else if (event.provenance === "recorded-result") next.push({
107
- id: `m-${row.seq}`,
108
- at,
109
- kind: "message",
110
- role: "assistant",
111
- content: recorded(event.text),
112
- sealed: true
113
- });
114
- else next.push({
115
- id: `n-${row.seq}`,
116
- at,
117
- kind: "notice",
118
- text: event.text
119
- });
120
- } else if (event.kind === "reasoning" && event.text) {
121
- if (last?.kind === "reasoning") next[next.length - 1] = {
122
- ...last,
123
- text: last.text + event.text
124
- };
125
- else next.push({
126
- id: `r-${row.seq}`,
127
- at,
128
- kind: "reasoning",
129
- text: event.text
130
- });
131
- } else if (event.kind === "tool-call") {
132
- const id = event.toolCallId ?? `t-${row.seq}`;
133
- next.push({
134
- id,
135
- at,
136
- kind: "tool",
137
- call: {
138
- id,
139
- type: toolType(event.toolName),
140
- label: event.toolName ?? "tool",
141
- status: "running",
142
- detail: brief(event.args, 120)
143
- }
144
- });
145
- } else if (event.kind === "tool-result") {
146
- const index = next.findIndex((entry) => entry.kind === "tool" && entry.call.id === event.toolCallId);
147
- const entry = next[index];
148
- if (entry?.kind === "tool") next[index] = {
149
- ...entry,
150
- call: {
151
- ...entry.call,
152
- status: failed(event.result) ? "error" : "success",
153
- output: brief(event.result),
154
- duration: Math.max(0, at - entry.at)
155
- }
156
- };
157
- }
158
- }
159
- return next;
160
- }
161
- function reduce(state, action) {
162
- switch (action.type) {
163
- case "reset": return {
164
- generation: action.generation,
165
- entries: [],
166
- lastSeq: 0,
167
- alive: true
168
- };
169
- case "rows": {
170
- const base = action.generation !== void 0 && action.generation !== state.generation ? {
171
- entries: [],
172
- lastSeq: 0
173
- } : state;
174
- const fresh = action.rows.filter((row) => row.seq > base.lastSeq);
175
- return {
176
- generation: action.generation ?? state.generation,
177
- entries: foldAgentRows(base.entries, fresh),
178
- lastSeq: fresh.reduce((max, row) => Math.max(max, row.seq), base.lastSeq),
179
- alive: action.alive ?? state.alive,
180
- error: void 0
181
- };
182
- }
183
- case "end": return {
184
- ...state,
185
- alive: false,
186
- error: void 0
187
- };
188
- case "error": return {
189
- ...state,
190
- error: action.message
191
- };
192
- case "unreachable": return {
193
- ...state,
194
- error: "The agent stream is unreachable; the viewer may be closed."
195
- };
196
- }
197
- }
198
- /**
199
- * Follow one agent's message stream over Server-Sent Events: `rows`, `reset` and `end` events, reconnecting with
200
- * `?after=<lastSeq>` when the server closes the connection, so a long turn streams without gaps or repeats.
201
- */
202
- function useAgentStream(url) {
203
- const [state, dispatch] = useReducer(reduce, {
204
- entries: [],
205
- lastSeq: 0,
206
- alive: false
207
- });
208
- useEffect(() => {
209
- if (!url || typeof EventSource === "undefined") return;
210
- dispatch({ type: "reset" });
211
- let lastSeq = 0;
212
- let closed = false;
213
- let source;
214
- let retry;
215
- const open = () => {
216
- const target = new URL(url, globalThis.location?.href);
217
- if (lastSeq > 0) target.searchParams.set("after", String(lastSeq));
218
- source = new EventSource(target.toString());
219
- let opened = false;
220
- source.onopen = () => {
221
- opened = true;
222
- };
223
- source.addEventListener("rows", (message) => {
224
- const data = JSON.parse(message.data);
225
- lastSeq = Math.max(lastSeq, data.lastSeq ?? 0, ...data.events.map((row) => row.seq));
226
- dispatch({
227
- type: "rows",
228
- generation: data.generation,
229
- rows: data.events,
230
- alive: data.alive
231
- });
232
- });
233
- source.addEventListener("reset", (message) => {
234
- lastSeq = 0;
235
- dispatch({
236
- type: "reset",
237
- generation: JSON.parse(message.data).generation
238
- });
239
- });
240
- source.addEventListener("end", () => {
241
- closed = true;
242
- source?.close();
243
- dispatch({ type: "end" });
244
- });
245
- source.onerror = () => {
246
- source?.close();
247
- if (closed) return;
248
- if (!opened) dispatch({ type: "unreachable" });
249
- retry = setTimeout(open, 1e3);
250
- };
251
- };
252
- open();
253
- return () => {
254
- closed = true;
255
- clearTimeout(retry);
256
- source?.close();
257
- };
258
- }, [url]);
259
- return state;
260
- }
261
- // src/run/trace-workspace.tsx
262
- /** The agent that owns a span: the span itself when it is an agent, else its nearest agent ancestor. */
263
- function agentOf(span, byId) {
264
- for (let cursor = span; cursor; cursor = cursor.parentId ? byId.get(cursor.parentId) : void 0) if (cursor.kind === "pipeline") return cursor;
265
- }
266
- /** The trace's agents as a spawn tree at the cursor: an agent not started by then is left out, one that ends after it is
267
- * still running, so the Graph and the Timeline always show the same moment. */
268
- function agentGraphAt(spans, byId, at, epoch, cards, extraEdges = []) {
269
- const nodes = [], edges = [], seen = /* @__PURE__ */ new Set();
270
- const timed = new Set(spans.flatMap((span) => span.kind === "pipeline" && span.id ? [span.id] : []));
271
- const recorded = /* @__PURE__ */ new Map();
272
- for (const [index, span] of spans.entries()) {
273
- if (span.kind === "pipeline" || at !== null && span.startMs > at) continue;
274
- const owner = agentOf(span, byId);
275
- if (!owner?.id) continue;
276
- const events = recorded.get(owner.id) ?? [];
277
- const endpoints = [{
278
- suffix: "start",
279
- label: "Started ",
280
- time: span.startMs
281
- }];
282
- if (span.status !== "running" && span.endMs !== span.startMs && (at === null || span.endMs <= at)) endpoints.push({
283
- suffix: "end",
284
- label: "Recorded ",
285
- time: span.endMs
286
- });
287
- for (const endpoint of endpoints) events.push({
288
- id: (span.id ?? String(index)) + ":" + endpoint.suffix,
289
- label: endpoint.label + span.name,
290
- at: epoch == null || !Number.isFinite(epoch) ? void 0 : epoch + endpoint.time
291
- });
292
- recorded.set(owner.id, events);
293
- }
294
- for (const span of spans) {
295
- if (span.kind !== "pipeline" || !span.id || seen.has(span.id) || at !== null && span.startMs > at) continue;
296
- seen.add(span.id);
297
- const card = cards?.[span.id];
298
- const state = at !== null && (span.endMs > at || span.status === "running") ? "running" : runStatus(span.status ?? "unknown");
299
- const activity = (span.activityAt ?? []).reduce((last, time) => Number.isFinite(time) && (at === null || time <= at) ? Math.max(last, time) : last, -Infinity);
300
- const events = (card?.events ?? recorded.get(span.id) ?? []).filter((event) => at === null || epoch != null && event.at != null && event.at <= epoch + at).sort((a, b) => (a.at ?? 0) - (b.at ?? 0));
301
- const timing = activityTiming({
302
- events,
303
- lastActivityAt: epoch != null && Number.isFinite(epoch) && Number.isFinite(activity) ? epoch + activity : at === null ? card?.lastActivityAt : void 0,
304
- medianEventGapMs: span.medianEventGapMs ?? (at === null ? card?.medianEventGapMs : void 0)
305
- });
306
- nodes.push({
307
- ...card,
308
- id: span.id,
309
- label: card?.label ?? span.name,
310
- model: card?.model ?? span.detail,
311
- status: at === null && card?.status ? card.status : state === "done" ? "succeeded" : state,
312
- costUsd: at === null ? card?.costUsd ?? span.costUsd : span.endMs <= at ? span.costUsd : void 0,
313
- startedAt: epoch == null || !Number.isFinite(epoch) ? at === null ? card?.startedAt : void 0 : epoch + span.startMs,
314
- ...timing,
315
- events: events.slice(-4),
316
- turn: at === null ? card?.turn : void 0,
317
- result: at === null ? card?.result ?? span.output : span.endMs <= at ? span.output : void 0,
318
- commits: at === null ? card?.commits : void 0,
319
- tokens: at === null ? card?.tokens : void 0
320
- });
321
- const parent = span.parentId ? byId.get(span.parentId) : void 0, spawner = parent && agentOf(parent, byId);
322
- if (spawner?.id) edges.push({
323
- from: spawner.id,
324
- to: span.id,
325
- kind: "spawn"
326
- });
327
- }
328
- for (const [id, card] of Object.entries(cards ?? {})) if (!timed.has(id)) nodes.push(at === null ? {
329
- ...card,
330
- id,
331
- label: card.label ?? id,
332
- status: card.status ?? "unknown"
333
- } : {
334
- id,
335
- label: card.label ?? id,
336
- model: card.model,
337
- harness: card.harness,
338
- role: card.role,
339
- status: "unknown",
340
- turn: "Timing unavailable at this cursor"
341
- });
342
- const ids = new Set(nodes.map((node) => node.id));
343
- for (const edge of extraEdges) if (ids.has(edge.from) && ids.has(edge.to) && (edge.kind !== "spawn" || !edges.some((prior) => prior.kind === "spawn" && prior.from === edge.from && prior.to === edge.to))) edges.push(edge);
344
- return {
345
- nodes,
346
- edges
347
- };
348
- }
349
- function timelineItems(entries, streaming) {
350
- return entries.map((entry, index) => {
351
- if (entry.kind === "message") return {
352
- id: entry.id,
353
- kind: "message",
354
- role: entry.role,
355
- content: entry.content,
356
- isStreaming: streaming && index === entries.length - 1
357
- };
358
- if (entry.kind === "tool") return {
359
- id: entry.id,
360
- kind: "tool",
361
- call: entry.call
362
- };
363
- if (entry.kind === "notice") return {
364
- id: entry.id,
365
- kind: "custom",
366
- content: /* @__PURE__ */ jsx("p", {
367
- className: "text-xs text-muted-foreground",
368
- children: entry.text
369
- })
370
- };
371
- return {
372
- id: entry.id,
373
- kind: "custom",
374
- content: /* @__PURE__ */ jsx(InlineThinkingItem, {
375
- part: {
376
- type: "reasoning",
377
- text: entry.text
378
- },
379
- defaultOpen: false,
380
- openWhileActive: false
381
- })
382
- };
383
- });
384
- }
385
- function NotTimeIndexed({ label }) {
386
- return /* @__PURE__ */ jsxs("p", {
387
- className: "p-4 text-sm text-muted-foreground",
388
- children: [label, ": not time-indexed"]
389
- });
390
- }
391
- /**
392
- * The run workspace: the agent timeline and span waterfall over one clock, the selected agent's conversation
393
- * streaming in a right-hand drawer, and the page's code workspace and scoreboard following the time cursor. Scrubbing is time travel:
394
- * every panel shows what was true at the cursor, and the conversation shows what had been said by then.
395
- */
396
- function TraceWorkspace({ trace, streamUrl, events, onTimeChange, files, scoreboard, selectedAgentId, onSelectAgent, agentCards, agentGraph, className, ...waterfall }) {
397
- const byId = useMemo(() => new Map(trace.spans.flatMap((span) => span.id ? [[span.id, span]] : [])), [trace]);
398
- const [ownSelection, setOwnSelection] = useState();
399
- const selected = selectedAgentId ?? ownSelection;
400
- const select = (agentId) => {
401
- setOwnSelection(agentId);
402
- onSelectAgent?.(agentId);
403
- };
404
- const agent = selected ? byId.get(selected) : void 0, card = selected ? agentCards?.[selected] : void 0;
405
- const agentId = agent?.id ?? (card ? selected : void 0), agentName = agent?.name ?? card?.label ?? agentId, agentModel = agent?.detail ?? card?.model;
406
- const { defaultLayout = "tree", ...graphOptions } = agentGraph ?? {};
407
- const [graphLayout, setGraphLayout] = useState(defaultLayout);
408
- const [cursor, setCursor] = useState(null);
409
- const stream = useAgentStream(selected && streamUrl ? streamUrl(selected) : void 0);
410
- const origin = useMemo(() => {
411
- if (waterfall.startedAt !== void 0) return waterfall.startedAt;
412
- const first = stream.entries[0]?.at;
413
- return agent && first !== void 0 ? first - agent.startMs : void 0;
414
- }, [
415
- waterfall.startedAt,
416
- agent,
417
- stream.entries
418
- ]);
419
- const graph = useMemo(() => agentGraphAt(trace.spans, byId, cursor, waterfall.startedAt, agentCards, agentGraph?.edges), [
420
- trace,
421
- byId,
422
- cursor,
423
- waterfall.startedAt,
424
- agentCards,
425
- agentGraph?.edges
426
- ]);
427
- const selectedNode = graph.nodes.find((node) => node.id === agentId);
428
- const facts = cursor === null || agent && agent.endMs <= cursor ? [...agent?.facts ?? []] : [];
429
- for (const [label, value] of [["Recorded result", selectedNode?.result], ["Activity", selectedNode?.turn]]) if (value && !facts.some((fact) => fact.value === value)) facts.push({
430
- label,
431
- value
432
- });
433
- const graphNow = cursor !== null && waterfall.startedAt !== void 0 ? waterfall.startedAt + cursor : void 0;
434
- const shown = cursor === null || origin === void 0 ? stream.entries : stream.entries.filter((entry) => entry.at - origin <= cursor);
435
- const live = stream.alive && !stream.error && cursor === null;
436
- const items = useMemo(() => timelineItems(shown, live), [shown, live]);
437
- const scroller = useRef(null);
438
- const pinned = useRef(true);
439
- const [unseen, setUnseen] = useState(false);
440
- useLayoutEffect(() => {
441
- const element = scroller.current;
442
- if (!element) return;
443
- if (pinned.current) element.scrollTop = element.scrollHeight;
444
- else setUnseen(true);
445
- }, [items.length, items[items.length - 1]]);
446
- useEffect(() => {
447
- pinned.current = true;
448
- setUnseen(false);
449
- }, [selected]);
450
- return /* @__PURE__ */ jsxs("div", {
451
- className: cn("sandbox-trace-workspace flex min-h-0 flex-col gap-3", className),
452
- children: [
453
- /* @__PURE__ */ jsx("div", {
454
- className: agentId ? "min-w-0 lg:mr-[420px]" : "min-w-0",
455
- children: /* @__PURE__ */ jsx(TraceWaterfall, {
456
- ...waterfall,
457
- trace,
458
- markers: events,
459
- selectedId: agentId,
460
- onDeselect: () => select(void 0),
461
- onSelect: (span) => {
462
- const owner = agentOf(span, byId);
463
- if (owner?.id) select(owner.id);
464
- },
465
- onTimeChange: (time) => {
466
- setCursor(time);
467
- onTimeChange?.(time);
468
- },
469
- latestEvents: /* @__PURE__ */ jsx(LatestEvents, {
470
- agents: graph.nodes,
471
- now: graphNow,
472
- live: cursor === null,
473
- selectedId: agentId,
474
- onSelect: select
475
- }),
476
- graph: /* @__PURE__ */ jsxs("section", {
477
- className: "flex h-[60vh] min-h-[480px] flex-col",
478
- "aria-label": "Agent graph",
479
- children: [/* @__PURE__ */ jsxs("label", {
480
- className: "flex h-8 shrink-0 items-center justify-end gap-2 px-3 text-xs text-muted-foreground",
481
- children: ["Layout", /* @__PURE__ */ jsxs("select", {
482
- "aria-label": "Graph layout",
483
- value: graphLayout,
484
- onChange: (event) => setGraphLayout(event.currentTarget.value),
485
- className: "rounded-[var(--radius-control)] border border-border bg-card px-2 py-1 text-foreground",
486
- children: [/* @__PURE__ */ jsx("option", {
487
- value: "tree",
488
- children: "Tree"
489
- }), /* @__PURE__ */ jsx("option", {
490
- value: "hub",
491
- children: "Hub"
492
- })]
493
- })]
494
- }), /* @__PURE__ */ jsx(AgentGraph, {
495
- ...graphOptions,
496
- layout: graphLayout,
497
- renderEventIcon: (event) => toolGlyph(event.label),
498
- nodes: graph.nodes,
499
- edges: graph.edges,
500
- selectedId: agentId ?? null,
501
- onSelect: select,
502
- showLatestEvents: false,
503
- now: graphNow,
504
- live: cursor === null && graph.nodes.some((node) => [
505
- "running",
506
- "waiting",
507
- "needs-you",
508
- "failed"
509
- ].includes(node.status)),
510
- className: cn("min-h-0 flex-1", graphOptions.className)
511
- })]
512
- })
513
- })
514
- }),
515
- files && /* @__PURE__ */ jsx("section", {
516
- "aria-label": "Code at the cursor",
517
- className: "flex min-h-[70vh] flex-col overflow-hidden rounded-xl border border-border bg-card",
518
- children: files
519
- }),
520
- scoreboard && /* @__PURE__ */ jsx("section", {
521
- "aria-label": "Scoreboard at the cursor",
522
- className: "overflow-hidden rounded-xl border border-border bg-card",
523
- children: scoreboard
524
- }),
525
- agentId && /* @__PURE__ */ jsxs("aside", {
526
- "aria-label": `${agentName} conversation`,
527
- onKeyDown: (event) => {
528
- if (event.key === "Escape") select(void 0);
529
- },
530
- className: "fixed inset-y-0 right-0 z-40 flex w-full max-w-[420px] flex-col border-l border-border bg-card shadow-2xl",
531
- children: [
532
- /* @__PURE__ */ jsxs("header", {
533
- className: "flex shrink-0 items-center gap-2 border-b border-border px-3 py-2",
534
- children: [
535
- /* @__PURE__ */ jsx("span", {
536
- className: cn("h-1.5 w-1.5 shrink-0 rounded-full", live ? "bg-[var(--surface-info-text)]" : "bg-muted-foreground"),
537
- "aria-hidden": true,
538
- "data-motion": live ? "essential" : void 0,
539
- style: live ? ambientStyle : void 0,
540
- onAnimationStart: syncAmbient
541
- }),
542
- /* @__PURE__ */ jsx("h3", {
543
- className: "min-w-0 flex-1 truncate text-sm font-semibold",
544
- children: agentName
545
- }),
546
- agentModel && /* @__PURE__ */ jsx("span", {
547
- className: "truncate text-xs text-muted-foreground",
548
- children: agentModel
549
- }),
550
- /* @__PURE__ */ jsx("button", {
551
- type: "button",
552
- "aria-label": "Close conversation",
553
- title: "Close (Esc)",
554
- onClick: () => select(void 0),
555
- className: "shrink-0 rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
556
- children: /* @__PURE__ */ jsx(X, {
557
- "aria-hidden": true,
558
- className: "h-4 w-4"
559
- })
560
- })
561
- ]
562
- }),
563
- facts.length > 0 && /* @__PURE__ */ jsx("section", {
564
- "aria-label": "Recorded agent details",
565
- tabIndex: 0,
566
- className: "max-h-[35vh] shrink-0 overflow-y-auto border-b border-border px-3 py-2 text-sm",
567
- children: /* @__PURE__ */ jsx("dl", {
568
- className: "space-y-2",
569
- children: facts.map((fact, index) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", {
570
- className: "text-xs text-muted-foreground",
571
- children: fact.label
572
- }), /* @__PURE__ */ jsx("dd", {
573
- className: "m-0 whitespace-pre-wrap break-words",
574
- children: fact.value
575
- })] }, index))
576
- })
577
- }),
578
- /* @__PURE__ */ jsx("div", {
579
- ref: scroller,
580
- onScroll: (event) => {
581
- const element = event.currentTarget;
582
- pinned.current = element.scrollHeight - element.scrollTop - element.clientHeight < 48;
583
- if (pinned.current) setUnseen(false);
584
- },
585
- "data-primary-content": "",
586
- className: "min-h-0 flex-1 overflow-y-auto px-3 py-2",
587
- children: streamUrl && (cursor === null || origin !== void 0) ? /* @__PURE__ */ jsx(AgentTimeline, {
588
- items,
589
- isThinking: live && items.length === 0,
590
- emptyState: /* @__PURE__ */ jsx("p", {
591
- className: "p-4 text-sm text-muted-foreground",
592
- children: stream.error ?? "No messages yet."
593
- })
594
- }) : /* @__PURE__ */ jsx(NotTimeIndexed, { label: "Conversation" })
595
- }),
596
- unseen && /* @__PURE__ */ jsx("button", {
597
- type: "button",
598
- onClick: () => {
599
- const element = scroller.current;
600
- if (element) element.scrollTop = element.scrollHeight;
601
- pinned.current = true;
602
- setUnseen(false);
603
- },
604
- className: "absolute bottom-3 left-1/2 -translate-x-1/2 rounded-full border border-border bg-background px-3 py-1 text-xs shadow-sm",
605
- children: "New output"
606
- })
607
- ]
608
- })
609
- ]
610
- });
611
- }
612
- export { TraceWaterfall as C, useAgentStream as S, statusMeta$1 as _, InlineToolItem as a, TraceWorkspace as b, RunPhaseList as c, RunStatusIcon$1 as d, TaskList as f, showsTaskPriority as g, runStatus$1 as h, InlineThinkingItem$1 as i, RunRowShell as l, parseToolEvent as m, ApprovalDiffSummary as n, LiveDuration as o, ToolCallFeed as p, ExpandedToolDetail as r, RunGroup as s, ApprovalCard as t, RunRowStatusDot as u, taskProgress as v, traceSpanMark as w, foldAgentRows as x, taskStatus as y };