@tangle-network/sandbox-ui 6.0.1 → 7.0.1
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/README.md +8 -0
- package/dist/{AgentTree-D4d06rfH.js → AgentTree-Dzy6lrfP.js} +1 -1
- package/dist/{ResearchReport-D1TFmldm.js → ResearchReport-DKWDmzdK.js} +2 -2
- package/dist/{chat-BrlLa4rg.js → chat-B_5Lwevm.js} +2 -2
- package/dist/chat.d.ts +1 -1
- package/dist/chat.js +1 -1
- package/dist/globals.css +1562 -6987
- package/dist/{index-5zOVKgDT.d.ts → index-1Q4Jtlic.d.ts} +2 -2
- package/dist/{index-D6fZ1Hgz.d.ts → index-PolYo7h9.d.ts} +11 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -6
- package/dist/record/ResearchReport.js +1 -1
- package/dist/{record-C-EUpK-n.js → record-Cc_WNtG8.js} +2 -2
- package/dist/record.js +1 -1
- package/dist/report-viewer.js +90 -90
- package/dist/run/live.js +1 -1
- package/dist/run/record.js +3 -3
- package/dist/run-DBHKEnV_.js +5 -0
- package/dist/run.d.ts +1 -1
- package/dist/run.js +5 -5
- package/dist/runtime-viewer/AgentGraphCanvas-xHVW-YRV-Ch1fnK9k.js +957 -0
- package/dist/runtime-viewer/latest-events-CMbdBLcT-uhVN4JLh.js +17401 -0
- package/dist/styles.css +1562 -6990
- package/dist/tailwind.css +824 -7407
- package/dist/{trace-workspace-Cfil2Ma9.js → trace-workspace-B7Ht17Nh.js} +406 -133
- package/dist/{use-follow-tail-DZFn7QDO.js → use-follow-tail-Df965WQi.js} +46 -11
- package/dist/workspace-viewer.js +10386 -10180
- package/package.json +3 -3
- package/dist/runtime-viewer/AgentGraphCanvas-DGq7_u8--C6yDfIUT.js +0 -8555
- package/dist/runtime-viewer/latest-events-CRq9sGkK-CfoBl1dP.js +0 -9592
- package/dist/runtime-viewer/sandbox-ui-BEziPi7D.css +0 -2
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { t as cn } from "./utils-BER_bdgE.js";
|
|
1
|
+
import { t as cn$1 } from "./utils-BER_bdgE.js";
|
|
2
2
|
import { i as modelBrandFor, t as ModelBrandStack } from "./model-brand-BQlarCIo.js";
|
|
3
3
|
import { t as ProviderIcon } from "./provider-logo-CPKRPHcS.js";
|
|
4
|
-
import { t as AgentGraph
|
|
5
|
-
import { s as timelineItems, t as useFollowTail } from "./use-follow-tail-
|
|
4
|
+
import { t as AgentGraph } from "./AgentGraph-BGWx9hhS.js";
|
|
5
|
+
import { s as timelineItems, t as useFollowTail } from "./use-follow-tail-Df965WQi.js";
|
|
6
|
+
import { Button, SegmentedControl } from "@tangle-network/ui/primitives";
|
|
6
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
|
-
import { X } from "lucide-react";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
9
|
+
import { ArrowLeft, ChevronRight, X } from "lucide-react";
|
|
10
|
+
import { formatCost, formatCount, formatSpanDuration } from "@tangle-network/ui/utils";
|
|
11
|
+
import { activityTiming } from "@tangle-network/charts/react/agent-graph/presentation";
|
|
12
|
+
import { AgentTimeline, AgentTimelineColumn } from "@tangle-network/ui/chat";
|
|
13
|
+
import { RunStatusIcon, runStatus } from "@tangle-network/ui/run";
|
|
12
14
|
import { FlowWaterfall, toolGlyph } from "@tangle-network/charts/react";
|
|
13
|
-
import { LatestEvents } from "@tangle-network/charts/react/agent-graph";
|
|
14
15
|
import { useEventStream } from "@tangle-network/ui/hooks";
|
|
15
16
|
// src/run/trace-waterfall.tsx
|
|
16
17
|
/** Agent and model spans show their lab/host mark; tool spans show their provider's mark. Unknown keeps the kind glyph. */
|
|
@@ -426,6 +427,330 @@ function useAgentStream(url, openEventSource, history) {
|
|
|
426
427
|
} } : {}
|
|
427
428
|
};
|
|
428
429
|
}
|
|
430
|
+
// src/run/agent-inspector.tsx
|
|
431
|
+
/** Below the tablet breakpoint the inspector is a full-screen sheet over the run, never a squeezed side pane. */
|
|
432
|
+
const PHONE = "(max-width: 767px)";
|
|
433
|
+
function usePhone() {
|
|
434
|
+
return useSyncExternalStore((notify) => {
|
|
435
|
+
const query = matchMedia(PHONE);
|
|
436
|
+
query.addEventListener("change", notify);
|
|
437
|
+
return () => query.removeEventListener("change", notify);
|
|
438
|
+
}, () => matchMedia(PHONE).matches, () => false);
|
|
439
|
+
}
|
|
440
|
+
const WIDTH_KEY = "sandbox-ui:trace-inspector-width";
|
|
441
|
+
const MIN_PX = 360;
|
|
442
|
+
const MIN_LEFT_PX = 320;
|
|
443
|
+
const STEP = .02;
|
|
444
|
+
const readWidth = () => {
|
|
445
|
+
try {
|
|
446
|
+
const value = Number(localStorage.getItem(WIDTH_KEY));
|
|
447
|
+
return value > 0 && value < 1 ? value : .4;
|
|
448
|
+
} catch {
|
|
449
|
+
return .4;
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
/** The docked inspector's share of the workspace: dragged or arrowed, remembered for this viewer, never under 360px. */
|
|
453
|
+
function useSplit(root) {
|
|
454
|
+
const [fraction, setFraction] = useState(.4), [total, setTotal] = useState(0);
|
|
455
|
+
useEffect(() => setFraction(readWidth()), []);
|
|
456
|
+
useEffect(() => {
|
|
457
|
+
const element = root.current;
|
|
458
|
+
if (!element) return;
|
|
459
|
+
const observer = new ResizeObserver(([entry]) => requestAnimationFrame(() => setTotal(entry.contentRect.width)));
|
|
460
|
+
observer.observe(element);
|
|
461
|
+
return () => observer.disconnect();
|
|
462
|
+
}, [root]);
|
|
463
|
+
const max = total ? Math.max(MIN_PX, total - MIN_LEFT_PX) : Infinity;
|
|
464
|
+
const width = Math.round(Math.min(max, Math.max(MIN_PX, fraction * (total || 1e3))));
|
|
465
|
+
const commit = (next) => {
|
|
466
|
+
const value = Math.min(.85, Math.max(.1, next));
|
|
467
|
+
setFraction(value);
|
|
468
|
+
try {
|
|
469
|
+
localStorage.setItem(WIDTH_KEY, String(value));
|
|
470
|
+
} catch {}
|
|
471
|
+
};
|
|
472
|
+
return {
|
|
473
|
+
width,
|
|
474
|
+
separator: {
|
|
475
|
+
role: "separator",
|
|
476
|
+
tabIndex: 0,
|
|
477
|
+
"aria-orientation": "vertical",
|
|
478
|
+
"aria-label": "Resize agent inspector",
|
|
479
|
+
"aria-valuemin": total ? Math.round(MIN_PX / total * 100) : 0,
|
|
480
|
+
"aria-valuemax": total ? Math.round(max / total * 100) : 100,
|
|
481
|
+
"aria-valuenow": total ? Math.round(width / total * 100) : Math.round(fraction * 100),
|
|
482
|
+
onKeyDown: (event) => {
|
|
483
|
+
const by = event.key === "ArrowLeft" ? STEP : event.key === "ArrowRight" ? -.02 : 0;
|
|
484
|
+
if (by) {
|
|
485
|
+
event.preventDefault();
|
|
486
|
+
commit(width / (total || 1e3) + by);
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
onPointerDown: (event) => {
|
|
490
|
+
const box = root.current?.getBoundingClientRect();
|
|
491
|
+
if (!box) return;
|
|
492
|
+
event.preventDefault();
|
|
493
|
+
const handle = event.currentTarget;
|
|
494
|
+
handle.setPointerCapture(event.pointerId);
|
|
495
|
+
const move = (next) => commit((box.right - next.clientX) / box.width);
|
|
496
|
+
const stop = () => {
|
|
497
|
+
handle.removeEventListener("pointermove", move);
|
|
498
|
+
handle.removeEventListener("pointerup", stop);
|
|
499
|
+
handle.removeEventListener("pointercancel", stop);
|
|
500
|
+
};
|
|
501
|
+
handle.addEventListener("pointermove", move);
|
|
502
|
+
handle.addEventListener("pointerup", stop);
|
|
503
|
+
handle.addEventListener("pointercancel", stop);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
/** Text held to a few lines with a control to read the rest; the control appears only when lines are hidden. */
|
|
509
|
+
function Clamped({ text, lines, className }) {
|
|
510
|
+
const [open, setOpen] = useState(false), [over, setOver] = useState(false), ref = useRef(null);
|
|
511
|
+
useLayoutEffect(() => {
|
|
512
|
+
const element = ref.current;
|
|
513
|
+
if (!element || open) return;
|
|
514
|
+
const measure = () => setOver(element.scrollHeight > element.clientHeight + 1);
|
|
515
|
+
measure();
|
|
516
|
+
const observer = new ResizeObserver(() => requestAnimationFrame(measure));
|
|
517
|
+
observer.observe(element);
|
|
518
|
+
return () => observer.disconnect();
|
|
519
|
+
}, [
|
|
520
|
+
text,
|
|
521
|
+
open,
|
|
522
|
+
lines
|
|
523
|
+
]);
|
|
524
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
525
|
+
className: "min-w-0",
|
|
526
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
527
|
+
ref,
|
|
528
|
+
className: cn$1("m-0 whitespace-pre-wrap break-words", !open && (lines === 2 ? "line-clamp-2" : "line-clamp-3"), className),
|
|
529
|
+
children: text
|
|
530
|
+
}), (over || open) && /* @__PURE__ */ jsx("button", {
|
|
531
|
+
type: "button",
|
|
532
|
+
"aria-expanded": open,
|
|
533
|
+
onClick: () => setOpen(!open),
|
|
534
|
+
className: "mt-0.5 rounded-sm text-xs text-muted-foreground underline-offset-2 hover:text-foreground hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-[hsl(var(--ring))]",
|
|
535
|
+
children: open ? "Show less" : "Show all"
|
|
536
|
+
})]
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
/** Fresh input, cached input and output as one stacked bar, so the split reads at a glance and its numbers stay in the line. */
|
|
540
|
+
function TokenBar({ fresh, cached, out }) {
|
|
541
|
+
const total = fresh + cached + out;
|
|
542
|
+
if (!total) return null;
|
|
543
|
+
const parts = [
|
|
544
|
+
[
|
|
545
|
+
"fresh in",
|
|
546
|
+
fresh,
|
|
547
|
+
"var(--chart-series-1)"
|
|
548
|
+
],
|
|
549
|
+
[
|
|
550
|
+
"cached in",
|
|
551
|
+
cached,
|
|
552
|
+
"var(--chart-muted)"
|
|
553
|
+
],
|
|
554
|
+
[
|
|
555
|
+
"out",
|
|
556
|
+
out,
|
|
557
|
+
"var(--chart-series-2)"
|
|
558
|
+
]
|
|
559
|
+
];
|
|
560
|
+
const label = parts.filter(([, value]) => value).map(([name, value]) => `${formatCount(value)} ${name}`).join(", ");
|
|
561
|
+
return /* @__PURE__ */ jsx("span", {
|
|
562
|
+
role: "img",
|
|
563
|
+
"aria-label": `Tokens: ${label}`,
|
|
564
|
+
title: label,
|
|
565
|
+
className: "flex h-1 w-full overflow-hidden rounded-full bg-muted",
|
|
566
|
+
children: parts.map(([name, value, color]) => value ? /* @__PURE__ */ jsx("span", { style: {
|
|
567
|
+
width: `${value / total * 100}%`,
|
|
568
|
+
background: color
|
|
569
|
+
} }, name) : null)
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
/** The header's one line of numbers, from the agent's own span: duration, cost and tokens; unknowns are left out. */
|
|
573
|
+
function metaOf(span) {
|
|
574
|
+
if (!span) return {
|
|
575
|
+
line: [],
|
|
576
|
+
tokens: void 0
|
|
577
|
+
};
|
|
578
|
+
const line = [];
|
|
579
|
+
if (span.endMs > span.startMs) line.push(formatSpanDuration(span.endMs - span.startMs));
|
|
580
|
+
if (span.costUsd) line.push((span.costBound === "lower" ? "≥" : "") + formatCost(span.costUsd));
|
|
581
|
+
const input = span.promptTokens ?? 0, out = span.completionTokens ?? 0, cached = Math.min(span.cachedTokens ?? 0, input);
|
|
582
|
+
if (input) line.push(`${formatCount(input)} in`);
|
|
583
|
+
if (out) line.push(`${formatCount(out)} out`);
|
|
584
|
+
return {
|
|
585
|
+
line,
|
|
586
|
+
tokens: input || out ? {
|
|
587
|
+
fresh: input - cached,
|
|
588
|
+
cached,
|
|
589
|
+
out
|
|
590
|
+
} : void 0
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
/** Facts the header already states are not repeated in Details. */
|
|
594
|
+
const IN_HEADER = /^(tokens|cost|duration|failure|model)$/i;
|
|
595
|
+
/** The selected agent: a compact header, its failure, its commission, then its conversation; recorded facts wait in Details. */
|
|
596
|
+
function AgentInspector({ name, model, status, span, live, failure, silent, commission, facts, detail, children, scroller, unseen, onJump, onClose, mode, style }) {
|
|
597
|
+
const brand = model ? modelBrandFor(model) : null, meta = metaOf(span);
|
|
598
|
+
const details = facts.filter((fact) => !IN_HEADER.test(fact.label) && fact.value !== failure);
|
|
599
|
+
const sheetRef = useRef(null);
|
|
600
|
+
const sheet = mode === "sheet";
|
|
601
|
+
useEffect(() => {
|
|
602
|
+
if (!sheet) return;
|
|
603
|
+
const opener = document.activeElement, overflow = document.body.style.overflow;
|
|
604
|
+
document.body.style.overflow = "hidden";
|
|
605
|
+
sheetRef.current?.focus();
|
|
606
|
+
return () => {
|
|
607
|
+
document.body.style.overflow = overflow;
|
|
608
|
+
if (opener?.isConnected) opener.focus();
|
|
609
|
+
};
|
|
610
|
+
}, [sheet]);
|
|
611
|
+
return /* @__PURE__ */ jsxs("aside", {
|
|
612
|
+
ref: sheetRef,
|
|
613
|
+
tabIndex: sheet ? -1 : void 0,
|
|
614
|
+
role: sheet ? "dialog" : void 0,
|
|
615
|
+
"aria-modal": sheet || void 0,
|
|
616
|
+
"aria-label": `${name} conversation`,
|
|
617
|
+
style,
|
|
618
|
+
onKeyDown: (event) => {
|
|
619
|
+
if (event.key === "Escape") {
|
|
620
|
+
event.stopPropagation();
|
|
621
|
+
onClose();
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
className: cn$1("relative flex min-h-0 flex-col overflow-hidden bg-card text-foreground outline-none", mode === "docked" && "shrink-0 rounded-xl border border-border", mode === "overlay" && "fixed inset-y-0 right-0 z-40 w-full max-w-[420px] border-l border-border shadow-2xl", sheet && "fixed inset-0 z-[1100] bg-background"),
|
|
625
|
+
children: [
|
|
626
|
+
/* @__PURE__ */ jsxs("header", {
|
|
627
|
+
className: "flex shrink-0 items-start gap-2 border-b border-border px-3 py-2.5",
|
|
628
|
+
children: [
|
|
629
|
+
sheet && /* @__PURE__ */ jsxs(Button, {
|
|
630
|
+
variant: "ghost",
|
|
631
|
+
size: "sm",
|
|
632
|
+
onClick: onClose,
|
|
633
|
+
"aria-label": "Back to the run",
|
|
634
|
+
className: "-ml-1.5 shrink-0 gap-1 px-2",
|
|
635
|
+
children: [/* @__PURE__ */ jsx(ArrowLeft, {
|
|
636
|
+
"aria-hidden": true,
|
|
637
|
+
className: "h-4 w-4"
|
|
638
|
+
}), "Back"]
|
|
639
|
+
}),
|
|
640
|
+
/* @__PURE__ */ jsxs("div", {
|
|
641
|
+
className: cn$1("min-w-0 flex-1", sheet && "pt-1.5"),
|
|
642
|
+
children: [
|
|
643
|
+
/* @__PURE__ */ jsxs("div", {
|
|
644
|
+
className: "flex min-w-0 items-start gap-2",
|
|
645
|
+
children: [
|
|
646
|
+
/* @__PURE__ */ jsx(RunStatusIcon, {
|
|
647
|
+
status,
|
|
648
|
+
className: "mt-0.5 size-4 shrink-0"
|
|
649
|
+
}),
|
|
650
|
+
/* @__PURE__ */ jsx("h3", {
|
|
651
|
+
className: "m-0 min-w-0 flex-1 break-words text-sm font-semibold leading-5",
|
|
652
|
+
children: name
|
|
653
|
+
}),
|
|
654
|
+
live && /* @__PURE__ */ jsxs("span", {
|
|
655
|
+
className: "flex shrink-0 items-center gap-1 text-xs font-medium text-[var(--surface-info-text)]",
|
|
656
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
657
|
+
"aria-hidden": true,
|
|
658
|
+
className: "h-1.5 w-1.5 rounded-full bg-current"
|
|
659
|
+
}), "Live"]
|
|
660
|
+
})
|
|
661
|
+
]
|
|
662
|
+
}),
|
|
663
|
+
(model || meta.line.length > 0) && /* @__PURE__ */ jsxs("p", {
|
|
664
|
+
className: "m-0 mt-1 min-w-0 text-xs tabular-nums text-muted-foreground",
|
|
665
|
+
children: [brand && /* @__PURE__ */ jsx("span", {
|
|
666
|
+
className: "mr-1.5 inline-flex align-[-3px]",
|
|
667
|
+
children: /* @__PURE__ */ jsx(ModelBrandStack, {
|
|
668
|
+
identity: brand,
|
|
669
|
+
size: "sm"
|
|
670
|
+
})
|
|
671
|
+
}), [model, ...meta.line].filter(Boolean).join(" · ")]
|
|
672
|
+
}),
|
|
673
|
+
meta.tokens && /* @__PURE__ */ jsx("div", {
|
|
674
|
+
className: "mt-1.5 max-w-[240px]",
|
|
675
|
+
children: /* @__PURE__ */ jsx(TokenBar, { ...meta.tokens })
|
|
676
|
+
})
|
|
677
|
+
]
|
|
678
|
+
}),
|
|
679
|
+
!sheet && /* @__PURE__ */ jsx("button", {
|
|
680
|
+
type: "button",
|
|
681
|
+
"aria-label": "Close conversation",
|
|
682
|
+
title: "Close (Esc)",
|
|
683
|
+
onClick: onClose,
|
|
684
|
+
className: "shrink-0 rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-[hsl(var(--ring))]",
|
|
685
|
+
children: /* @__PURE__ */ jsx(X, {
|
|
686
|
+
"aria-hidden": true,
|
|
687
|
+
className: "h-4 w-4"
|
|
688
|
+
})
|
|
689
|
+
})
|
|
690
|
+
]
|
|
691
|
+
}),
|
|
692
|
+
failure && /* @__PURE__ */ jsx("section", {
|
|
693
|
+
"aria-label": "Failure",
|
|
694
|
+
className: "shrink-0 border-b border-border px-3 py-2.5 text-sm font-medium leading-5 text-[var(--surface-danger-text)]",
|
|
695
|
+
children: /* @__PURE__ */ jsx(Clamped, {
|
|
696
|
+
text: failure,
|
|
697
|
+
lines: 3
|
|
698
|
+
})
|
|
699
|
+
}),
|
|
700
|
+
commission && (commission.text || commission.by) && /* @__PURE__ */ jsxs("section", {
|
|
701
|
+
"aria-label": "Commission",
|
|
702
|
+
className: "shrink-0 border-b border-border px-3 py-2 text-sm",
|
|
703
|
+
children: [commission.by && /* @__PURE__ */ jsxs("p", {
|
|
704
|
+
className: "m-0 text-xs text-muted-foreground",
|
|
705
|
+
children: ["from ", commission.by]
|
|
706
|
+
}), commission.text && /* @__PURE__ */ jsx(Clamped, {
|
|
707
|
+
text: commission.text,
|
|
708
|
+
lines: 2
|
|
709
|
+
})]
|
|
710
|
+
}),
|
|
711
|
+
/* @__PURE__ */ jsx("div", {
|
|
712
|
+
ref: scroller,
|
|
713
|
+
"data-primary-content": "",
|
|
714
|
+
className: "min-h-0 flex-1 overflow-y-auto px-3 py-2",
|
|
715
|
+
children
|
|
716
|
+
}),
|
|
717
|
+
unseen && /* @__PURE__ */ jsx("button", {
|
|
718
|
+
type: "button",
|
|
719
|
+
onClick: onJump,
|
|
720
|
+
className: "absolute bottom-14 left-1/2 -translate-x-1/2 rounded-full border border-border bg-background px-3 py-1 text-xs shadow-sm",
|
|
721
|
+
children: "New output"
|
|
722
|
+
}),
|
|
723
|
+
(details.length > 0 || detail) && /* @__PURE__ */ jsxs("details", {
|
|
724
|
+
open: silent || void 0,
|
|
725
|
+
className: "group max-h-[45%] shrink-0 overflow-y-auto border-t border-border text-sm",
|
|
726
|
+
children: [/* @__PURE__ */ jsxs("summary", {
|
|
727
|
+
className: "flex cursor-pointer list-none items-center gap-1.5 px-3 py-2 text-xs font-medium text-muted-foreground hover:text-foreground before:hidden before:content-none [&::-webkit-details-marker]:hidden",
|
|
728
|
+
children: [/* @__PURE__ */ jsx(ChevronRight, {
|
|
729
|
+
"aria-hidden": true,
|
|
730
|
+
className: "h-3.5 w-3.5 transition-transform group-open:rotate-90"
|
|
731
|
+
}), "Details"]
|
|
732
|
+
}), /* @__PURE__ */ jsxs("section", {
|
|
733
|
+
"aria-label": "Recorded agent details",
|
|
734
|
+
tabIndex: 0,
|
|
735
|
+
className: "px-3 pb-3",
|
|
736
|
+
children: [details.length > 0 && /* @__PURE__ */ jsx("dl", {
|
|
737
|
+
className: "m-0 grid grid-cols-[minmax(0,auto)_minmax(0,1fr)] gap-x-3 gap-y-1",
|
|
738
|
+
children: details.map((fact, index) => /* @__PURE__ */ jsxs("div", {
|
|
739
|
+
className: "contents",
|
|
740
|
+
children: [/* @__PURE__ */ jsx("dt", {
|
|
741
|
+
className: "text-xs text-muted-foreground",
|
|
742
|
+
children: fact.label
|
|
743
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
744
|
+
className: "m-0 whitespace-pre-wrap break-words text-xs",
|
|
745
|
+
children: fact.value
|
|
746
|
+
})]
|
|
747
|
+
}, index))
|
|
748
|
+
}), detail]
|
|
749
|
+
})]
|
|
750
|
+
})
|
|
751
|
+
]
|
|
752
|
+
});
|
|
753
|
+
}
|
|
429
754
|
// src/run/trace-workspace.tsx
|
|
430
755
|
/** The agent that owns a span: the span itself when it is an agent, else its nearest agent ancestor. */
|
|
431
756
|
function agentOf(span, byId) {
|
|
@@ -515,11 +840,11 @@ function agentGraphAt(spans, byId, at, epoch, cards, extraEdges = []) {
|
|
|
515
840
|
};
|
|
516
841
|
}
|
|
517
842
|
/**
|
|
518
|
-
* The run workspace: the agent timeline and span waterfall over one clock,
|
|
519
|
-
*
|
|
843
|
+
* The run workspace: the agent timeline and span waterfall over one clock, filling its parent's height, with the selected
|
|
844
|
+
* agent's conversation at the center of its inspector, and the page's code workspace and scoreboard following the time cursor. Scrubbing is time travel:
|
|
520
845
|
* every panel shows what was true at the cursor, and the conversation shows what had been said by then.
|
|
521
846
|
*/
|
|
522
|
-
function TraceWorkspace({ trace, streamUrl, openEventSource, events, onTimeChange, files, scoreboard, selectedAgentId, conversationOpen, onSelectAgent, renderAgentDrawer, docked, commission, agentCards, agentGraph, className, ...waterfall }) {
|
|
847
|
+
function TraceWorkspace({ trace, streamUrl, openEventSource, events, onTimeChange, files, scoreboard, selectedAgentId, conversationOpen, onSelectAgent, renderAgentDrawer, transcriptNote, docked, commission, agentCards, agentGraph, className, ...waterfall }) {
|
|
523
848
|
const byId = useMemo(() => new Map(trace.spans.flatMap((span) => span.id ? [[span.id, span]] : [])), [trace]);
|
|
524
849
|
const [ownSelection, setOwnSelection] = useState();
|
|
525
850
|
const selected = selectedAgentId ?? ownSelection;
|
|
@@ -568,11 +893,31 @@ function TraceWorkspace({ trace, streamUrl, openEventSource, events, onTimeChang
|
|
|
568
893
|
identity: showConversation ? selected : void 0,
|
|
569
894
|
content: items
|
|
570
895
|
});
|
|
896
|
+
const root = useRef(null);
|
|
897
|
+
const phone = usePhone(), split = useSplit(root);
|
|
898
|
+
const mode = phone ? "sheet" : docked ? "docked" : "overlay";
|
|
899
|
+
const status = live ? "running" : selectedNode?.status ?? runStatus(agent?.status ?? "unknown");
|
|
900
|
+
const failure = facts.find((fact) => fact.label === "Failure")?.value ?? (status === "failed" ? selectedNode?.result ?? void 0 : void 0);
|
|
901
|
+
const said = (text) => !!text && shown.some((entry) => entry.kind === "message" && entry.role === "user" && entry.content.trim() === text.trim());
|
|
902
|
+
const silent = agentId && transcriptNote?.(agentId) || "No conversation was recorded for this agent.";
|
|
903
|
+
const note = !streamUrl ? silent : cursor !== null && origin === void 0 ? "This conversation has no timestamps to replay at the cursor." : items.length > 0 || live ? void 0 : stream.error ? ended ? stream.error : `Reconnecting… ${stream.error}` : ended ? silent : "No messages yet.";
|
|
904
|
+
const conversation = note === void 0 ? /* @__PURE__ */ jsx(AgentTimelineColumn, {
|
|
905
|
+
className: "max-w-[46rem] p-0",
|
|
906
|
+
children: /* @__PURE__ */ jsx(AgentTimeline, {
|
|
907
|
+
items,
|
|
908
|
+
isThinking: live && items.length === 0
|
|
909
|
+
})
|
|
910
|
+
}) : /* @__PURE__ */ jsx("p", {
|
|
911
|
+
className: "m-0 py-1 text-sm text-muted-foreground",
|
|
912
|
+
"data-stream": stream.error ? "reconnecting" : ended ? "ended" : "empty",
|
|
913
|
+
children: note
|
|
914
|
+
});
|
|
571
915
|
const left = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
572
916
|
/* @__PURE__ */ jsx("div", {
|
|
573
|
-
className:
|
|
917
|
+
className: cn$1("flex min-h-[320px] min-w-0 flex-1 flex-col", showConversation && mode === "overlay" && "lg:mr-[420px]"),
|
|
574
918
|
children: /* @__PURE__ */ jsx(TraceWaterfall, {
|
|
575
919
|
...waterfall,
|
|
920
|
+
maxHeight: waterfall.maxHeight ?? "fill",
|
|
576
921
|
trace,
|
|
577
922
|
markers: events,
|
|
578
923
|
selectedId: agentId,
|
|
@@ -585,32 +930,22 @@ function TraceWorkspace({ trace, streamUrl, openEventSource, events, onTimeChang
|
|
|
585
930
|
setCursor(time);
|
|
586
931
|
onTimeChange?.(time);
|
|
587
932
|
},
|
|
588
|
-
latestEvents: /* @__PURE__ */ jsx(LatestEvents, {
|
|
589
|
-
agents: graph.nodes,
|
|
590
|
-
now: graphNow,
|
|
591
|
-
live: cursor === null,
|
|
592
|
-
selectedId: agentId,
|
|
593
|
-
onSelect: select
|
|
594
|
-
}),
|
|
595
933
|
graph: /* @__PURE__ */ jsxs("section", {
|
|
596
|
-
className: "flex
|
|
934
|
+
className: "relative flex min-h-[320px] flex-1 flex-col",
|
|
597
935
|
"aria-label": "Agent graph",
|
|
598
|
-
children: [/* @__PURE__ */
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
})]
|
|
612
|
-
})]
|
|
613
|
-
}), /* @__PURE__ */ jsx(AgentGraph$1, {
|
|
936
|
+
children: [/* @__PURE__ */ jsx(SegmentedControl, {
|
|
937
|
+
"aria-label": "Graph layout",
|
|
938
|
+
value: graphLayout,
|
|
939
|
+
onValueChange: setGraphLayout,
|
|
940
|
+
className: "absolute right-2 top-1 z-10",
|
|
941
|
+
options: [{
|
|
942
|
+
value: "tree",
|
|
943
|
+
label: "Tree"
|
|
944
|
+
}, {
|
|
945
|
+
value: "hub",
|
|
946
|
+
label: "Hub"
|
|
947
|
+
}]
|
|
948
|
+
}), /* @__PURE__ */ jsx(AgentGraph, {
|
|
614
949
|
...graphOptions,
|
|
615
950
|
layout: graphLayout,
|
|
616
951
|
renderEventIcon: (event) => toolGlyph(event.label),
|
|
@@ -626,116 +961,54 @@ function TraceWorkspace({ trace, streamUrl, openEventSource, events, onTimeChang
|
|
|
626
961
|
"needs-you",
|
|
627
962
|
"failed"
|
|
628
963
|
].includes(node.status)),
|
|
629
|
-
className: cn("min-h-0 flex-1", graphOptions.className)
|
|
964
|
+
className: cn$1("min-h-0 flex-1", graphOptions.className)
|
|
630
965
|
})]
|
|
631
966
|
})
|
|
632
967
|
})
|
|
633
968
|
}),
|
|
634
969
|
files && /* @__PURE__ */ jsx("section", {
|
|
635
970
|
"aria-label": "Code at the cursor",
|
|
636
|
-
className: "flex h-[70vh] min-h-0 flex-col overflow-hidden rounded-xl border border-border bg-card",
|
|
971
|
+
className: "flex h-[70vh] min-h-0 shrink-0 flex-col overflow-hidden rounded-xl border border-border bg-card",
|
|
637
972
|
children: files
|
|
638
973
|
}),
|
|
639
974
|
scoreboard && /* @__PURE__ */ jsx("section", {
|
|
640
975
|
"aria-label": "Scoreboard at the cursor",
|
|
641
|
-
className: "overflow-hidden rounded-xl border border-border bg-card",
|
|
976
|
+
className: "shrink-0 overflow-hidden rounded-xl border border-border bg-card",
|
|
642
977
|
children: scoreboard
|
|
643
978
|
})
|
|
644
979
|
] });
|
|
645
980
|
return /* @__PURE__ */ jsxs("div", {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
981
|
+
ref: root,
|
|
982
|
+
className: cn$1("sandbox-trace-workspace flex h-full min-h-0 flex-row", className),
|
|
983
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
984
|
+
className: cn$1("flex min-h-0 min-w-0 flex-1 flex-col gap-3", (files || scoreboard) && "overflow-y-auto"),
|
|
649
985
|
children: left
|
|
650
|
-
})
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
type: "button",
|
|
677
|
-
"aria-label": "Close conversation",
|
|
678
|
-
title: "Close (Esc)",
|
|
679
|
-
onClick: () => select(void 0),
|
|
680
|
-
className: "shrink-0 rounded-md p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
681
|
-
children: /* @__PURE__ */ jsx(X, {
|
|
682
|
-
"aria-hidden": true,
|
|
683
|
-
className: "h-4 w-4"
|
|
684
|
-
})
|
|
685
|
-
})
|
|
686
|
-
]
|
|
687
|
-
}),
|
|
688
|
-
(facts.length > 0 || detail) && /* @__PURE__ */ jsxs("section", {
|
|
689
|
-
"aria-label": "Recorded agent details",
|
|
690
|
-
tabIndex: 0,
|
|
691
|
-
className: "max-h-[35vh] shrink-0 overflow-y-auto border-b border-border px-3 py-2 text-sm",
|
|
692
|
-
children: [facts.length > 0 && /* @__PURE__ */ jsx("dl", {
|
|
693
|
-
className: "space-y-2",
|
|
694
|
-
children: facts.map((fact, index) => /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", {
|
|
695
|
-
className: "text-xs text-muted-foreground",
|
|
696
|
-
children: fact.label
|
|
697
|
-
}), /* @__PURE__ */ jsx("dd", {
|
|
698
|
-
className: "m-0 whitespace-pre-wrap break-words",
|
|
699
|
-
children: fact.value
|
|
700
|
-
})] }, index))
|
|
701
|
-
}), detail]
|
|
702
|
-
}),
|
|
703
|
-
commission && (commission.text || commission.by) && /* @__PURE__ */ jsxs("section", {
|
|
704
|
-
"aria-label": "Commission",
|
|
705
|
-
className: "shrink-0 border-b border-border bg-muted/40 px-3 py-2 text-sm",
|
|
706
|
-
children: [commission.by && /* @__PURE__ */ jsxs("p", {
|
|
707
|
-
className: "m-0 mb-1 text-xs text-muted-foreground",
|
|
708
|
-
children: ["commissioned by ", commission.by]
|
|
709
|
-
}), commission.text && /* @__PURE__ */ jsx("p", {
|
|
710
|
-
className: "m-0 line-clamp-6 whitespace-pre-wrap break-words",
|
|
711
|
-
children: commission.text
|
|
712
|
-
})]
|
|
713
|
-
}),
|
|
714
|
-
/* @__PURE__ */ jsx("div", {
|
|
715
|
-
ref: scroller,
|
|
716
|
-
"data-primary-content": "",
|
|
717
|
-
className: "min-h-0 flex-1 overflow-y-auto px-3 py-2",
|
|
718
|
-
children: streamUrl && (cursor === null || origin !== void 0) ? /* @__PURE__ */ jsx(AgentTimeline, {
|
|
719
|
-
items,
|
|
720
|
-
isThinking: live && items.length === 0,
|
|
721
|
-
emptyState: /* @__PURE__ */ jsx("p", {
|
|
722
|
-
className: "p-4 text-sm text-muted-foreground",
|
|
723
|
-
"data-stream": stream.error ? "reconnecting" : ended ? "ended" : "empty",
|
|
724
|
-
children: stream.error ? ended ? stream.error : `Reconnecting… ${stream.error}` : ended ? "The conversation ended." : "No messages yet."
|
|
725
|
-
})
|
|
726
|
-
}) : /* @__PURE__ */ jsx("p", {
|
|
727
|
-
className: "p-4 text-sm text-muted-foreground",
|
|
728
|
-
children: "Conversation: not time-indexed"
|
|
729
|
-
})
|
|
730
|
-
}),
|
|
731
|
-
unseen && /* @__PURE__ */ jsx("button", {
|
|
732
|
-
type: "button",
|
|
733
|
-
onClick: () => jump(),
|
|
734
|
-
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",
|
|
735
|
-
children: "New output"
|
|
736
|
-
})
|
|
737
|
-
]
|
|
738
|
-
})]
|
|
986
|
+
}), showConversation && agentId && /* @__PURE__ */ jsxs(Fragment, { children: [mode === "docked" && /* @__PURE__ */ jsx("div", {
|
|
987
|
+
...split.separator,
|
|
988
|
+
className: "group relative w-3 shrink-0 cursor-col-resize touch-none outline-none",
|
|
989
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
990
|
+
"aria-hidden": true,
|
|
991
|
+
className: "absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-border transition-colors group-hover:bg-[hsl(var(--ring))] group-focus-visible:w-0.5 group-focus-visible:bg-[hsl(var(--ring))]"
|
|
992
|
+
})
|
|
993
|
+
}), /* @__PURE__ */ jsx(AgentInspector, {
|
|
994
|
+
name: agentName ?? agentId,
|
|
995
|
+
model: agentModel && !/^not recorded$/i.test(agentModel) ? agentModel : void 0,
|
|
996
|
+
status,
|
|
997
|
+
span: agent,
|
|
998
|
+
live,
|
|
999
|
+
failure,
|
|
1000
|
+
silent: note !== void 0 && note === silent,
|
|
1001
|
+
commission: said(commission?.text) ? { by: commission?.by } : commission,
|
|
1002
|
+
facts,
|
|
1003
|
+
detail,
|
|
1004
|
+
scroller,
|
|
1005
|
+
unseen,
|
|
1006
|
+
onJump: () => jump(),
|
|
1007
|
+
onClose: () => select(void 0),
|
|
1008
|
+
mode,
|
|
1009
|
+
style: mode === "docked" ? { width: split.width } : void 0,
|
|
1010
|
+
children: conversation
|
|
1011
|
+
})] })]
|
|
739
1012
|
});
|
|
740
1013
|
}
|
|
741
|
-
export {
|
|
1014
|
+
export { TraceWaterfall as a, useEventStream as i, foldAgentRows as n, traceSpanMark as o, useAgentStream as r, TraceWorkspace as t };
|