@tonyclaw/agent-inspector 2.1.15 → 2.1.16
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/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-BcEd6V-V.js → CompareDrawer-Ccrrcx1j.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-DLqClc9A.js +106 -0
- package/.output/public/assets/{ReplayDialog-BNpC0548.js → ReplayDialog-C7axhr-l.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-Ds1uRLVB.js → RequestAnatomy-WOptg8j_.js} +1 -1
- package/.output/public/assets/{ResponseView-7KPVqKl5.js → ResponseView-CSAcxh_M.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-BHQT261s.js → StreamingChunkSequence-DJS5KhPx.js} +1 -1
- package/.output/public/assets/_sessionId-DUSalzKH.js +1 -0
- package/.output/public/assets/index-BPpA21dY.css +1 -0
- package/.output/public/assets/index-Bt0Az2I2.js +1 -0
- package/.output/public/assets/{index-DjKt8XKe.js → index-DMPNh46t.js} +1 -1
- package/.output/public/assets/{json-viewer-CkCu-rka.js → json-viewer-inX5QSa3.js} +1 -1
- package/.output/public/assets/{main-DpD1N0S8.js → main-aCAKYGSD.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +207 -171
- package/.output/server/{_sessionId-DF9Sy8cP.mjs → _sessionId-C0fhvQnn.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-BoxztaO7.mjs → CompareDrawer-CO-Ti5sg.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-CRBkqFlJ.mjs → ProxyViewerContainer-N4J7uBCX.mjs} +807 -386
- package/.output/server/_ssr/{ReplayDialog-Cc1dyDuK.mjs → ReplayDialog-J1VxC0In.mjs} +4 -4
- package/.output/server/_ssr/{RequestAnatomy-CMGSsz5Z.mjs → RequestAnatomy-DBH-F3rq.mjs} +3 -3
- package/.output/server/_ssr/{ResponseView-Cp10DM1D.mjs → ResponseView-DLFwV7Hd.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-B1VGxy3A.mjs → StreamingChunkSequence-BywmuND0.mjs} +3 -3
- package/.output/server/_ssr/{index-47XVPghS.mjs → index-CStymIc0.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-zDE2rrmJ.mjs → json-viewer-DlQu0N1x.mjs} +3 -3
- package/.output/server/_ssr/{router-DVeuZFqI.mjs → router-CQ4Zxhmr.mjs} +112 -46
- package/.output/server/{_tanstack-start-manifest_v-Bp8JxtPW.mjs → _tanstack-start-manifest_v-BHeJBGfo.mjs} +1 -1
- package/.output/server/index.mjs +72 -72
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +146 -105
- package/src/components/ProxyViewerContainer.tsx +77 -67
- package/src/components/alerts/AlertsDialog.tsx +2 -1
- package/src/components/clients/ClientLogo.tsx +1 -1
- package/src/components/groups/GroupsDialog.tsx +19 -1
- package/src/components/providers/ProviderCard.tsx +1 -1
- package/src/components/providers/ProviderForm.tsx +18 -18
- package/src/components/providers/ProviderLogo.tsx +6 -1
- package/src/components/providers/ProvidersPanel.tsx +6 -6
- package/src/components/providers/SettingsDialog.tsx +13 -6
- package/src/components/proxy-viewer/AgentTraceSummary.tsx +6 -6
- package/src/components/proxy-viewer/ConversationGroup.tsx +43 -32
- package/src/components/proxy-viewer/ConversationGroupList.tsx +452 -89
- package/src/components/proxy-viewer/ConversationHeader.tsx +44 -32
- package/src/components/proxy-viewer/LogEntry.tsx +21 -17
- package/src/components/proxy-viewer/LogEntryHeader.tsx +38 -45
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +2 -2
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +9 -9
- package/src/components/proxy-viewer/ThreadConnector.tsx +4 -4
- package/src/components/proxy-viewer/TurnGroup.tsx +16 -11
- package/src/components/proxy-viewer/TurnGroupList.tsx +3 -0
- package/src/components/proxy-viewer/viewerState.ts +0 -4
- package/src/components/ui/icon-trigger.ts +2 -0
- package/src/components/ui/transient-toast.tsx +104 -0
- package/src/lib/sessionInfoContract.ts +4 -0
- package/src/mcp/server.ts +71 -27
- package/src/mcp/toolHandlers.ts +23 -4
- package/src/proxy/formats/openai/alibabaProvider.ts +0 -4
- package/src/proxy/sessionInfo.ts +31 -2
- package/src/proxy/sessionSupervisor.ts +20 -1
- package/src/proxy/store.ts +1 -1
- package/src/routes/api/providers.$providerId.test.log.ts +8 -4
- package/styles/globals.css +180 -180
- package/.output/public/assets/ProxyViewerContainer-h851qWNp.js +0 -106
- package/.output/public/assets/_sessionId-DWePGjnS.js +0 -1
- package/.output/public/assets/index-CI1-G8ua.js +0 -1
- package/.output/public/assets/index-DdhFqPsI.css +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type ChangeEvent,
|
|
3
3
|
type JSX,
|
|
4
|
+
type ReactNode,
|
|
4
5
|
useCallback,
|
|
5
6
|
useEffect,
|
|
6
7
|
useMemo,
|
|
@@ -47,6 +48,7 @@ import { crabVariants } from "./ui/crab-variants";
|
|
|
47
48
|
import { McpLogo } from "./ui/mcp-logo";
|
|
48
49
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
|
|
49
50
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
|
|
51
|
+
import type { TransientToastInput } from "./ui/transient-toast";
|
|
50
52
|
import { AlertsDialog } from "./alerts/AlertsDialog";
|
|
51
53
|
import { GroupsDialog } from "./groups/GroupsDialog";
|
|
52
54
|
import { SettingsDialog } from "./providers/SettingsDialog";
|
|
@@ -266,7 +268,7 @@ function slateStatusClass(status: SessionSlateStatus): string {
|
|
|
266
268
|
case "watch":
|
|
267
269
|
return "border-amber-300/35 bg-amber-400/10 text-amber-100";
|
|
268
270
|
case "clear":
|
|
269
|
-
return "border-
|
|
271
|
+
return "border-slate-300/20 bg-slate-300/[0.08] text-slate-100";
|
|
270
272
|
}
|
|
271
273
|
}
|
|
272
274
|
|
|
@@ -274,9 +276,9 @@ function SessionOpeningSlate({ stats }: { stats: SessionSlateStats }): JSX.Eleme
|
|
|
274
276
|
if (stats.requests === 0) return null;
|
|
275
277
|
|
|
276
278
|
return (
|
|
277
|
-
<div className="
|
|
279
|
+
<div className="border border-border bg-card shadow-sm mb-4 rounded-[8px]">
|
|
278
280
|
<div className="grid grid-cols-2 gap-0 md:grid-cols-[minmax(220px,1.2fr)_repeat(4,minmax(120px,1fr))]">
|
|
279
|
-
<div className="col-span-2 border-b border-
|
|
281
|
+
<div className="col-span-2 border-b border-white/10 px-4 py-3 md:col-span-1 md:border-b-0 md:border-r">
|
|
280
282
|
<div className="flex items-center gap-2 text-[10px] font-semibold uppercase text-muted-foreground">
|
|
281
283
|
<Clapperboard className="size-3.5 text-cyan-300" />
|
|
282
284
|
Opening Slate
|
|
@@ -288,7 +290,7 @@ function SessionOpeningSlate({ stats }: { stats: SessionSlateStats }): JSX.Eleme
|
|
|
288
290
|
{stats.timeRange ?? "Timeline pending"}
|
|
289
291
|
</div>
|
|
290
292
|
</div>
|
|
291
|
-
<div className="border-r border-b border-
|
|
293
|
+
<div className="border-r border-b border-white/10 px-4 py-3 md:border-b-0">
|
|
292
294
|
<div className="text-[10px] uppercase text-muted-foreground">Status</div>
|
|
293
295
|
<div
|
|
294
296
|
className={cn(
|
|
@@ -299,13 +301,13 @@ function SessionOpeningSlate({ stats }: { stats: SessionSlateStats }): JSX.Eleme
|
|
|
299
301
|
{stats.statusLabel}
|
|
300
302
|
</div>
|
|
301
303
|
</div>
|
|
302
|
-
<div className="border-b border-
|
|
304
|
+
<div className="border-b border-white/10 px-4 py-3 md:border-b-0 md:border-r">
|
|
303
305
|
<div className="text-[10px] uppercase text-muted-foreground">Requests</div>
|
|
304
306
|
<div className="mt-2 font-mono text-xl font-semibold text-foreground">
|
|
305
307
|
{stats.requests}
|
|
306
308
|
</div>
|
|
307
309
|
</div>
|
|
308
|
-
<div className="border-r border-
|
|
310
|
+
<div className="border-r border-white/10 px-4 py-3 md:border-r">
|
|
309
311
|
<div className="flex items-center gap-1 text-[10px] uppercase text-muted-foreground">
|
|
310
312
|
<Siren className="size-3 text-red-300" />
|
|
311
313
|
Failure
|
|
@@ -411,7 +413,7 @@ function sessionRiskBarClass(risk: ContextRiskLevel): string {
|
|
|
411
413
|
case "unknown":
|
|
412
414
|
return "bg-muted-foreground/50";
|
|
413
415
|
case "ok":
|
|
414
|
-
return "bg-
|
|
416
|
+
return "bg-cyan-300";
|
|
415
417
|
}
|
|
416
418
|
}
|
|
417
419
|
|
|
@@ -470,7 +472,7 @@ function evidenceChipClass(value: string): string {
|
|
|
470
472
|
normalized.includes("complete") ||
|
|
471
473
|
normalized.includes("accept")
|
|
472
474
|
) {
|
|
473
|
-
return "border-
|
|
475
|
+
return "border-cyan-300/25 bg-cyan-400/10 text-cyan-100";
|
|
474
476
|
}
|
|
475
477
|
return "border-border/70 bg-background/70 text-muted-foreground";
|
|
476
478
|
}
|
|
@@ -513,11 +515,11 @@ function SessionMembershipPanel({
|
|
|
513
515
|
);
|
|
514
516
|
|
|
515
517
|
return (
|
|
516
|
-
<div className="
|
|
518
|
+
<div className="border border-border bg-card shadow-sm mb-3 rounded-[8px] text-xs">
|
|
517
519
|
<button
|
|
518
520
|
type="button"
|
|
519
521
|
onClick={() => setOpen((value) => !value)}
|
|
520
|
-
className="flex w-full items-center gap-3 px-3 py-2 text-left transition-colors hover:bg-
|
|
522
|
+
className="flex w-full items-center gap-3 px-3 py-2 text-left transition-colors hover:bg-white/[0.04]"
|
|
521
523
|
aria-expanded={open}
|
|
522
524
|
>
|
|
523
525
|
<div className="min-w-0 flex-1">
|
|
@@ -555,7 +557,7 @@ function SessionMembershipPanel({
|
|
|
555
557
|
)}
|
|
556
558
|
</button>
|
|
557
559
|
{open && (
|
|
558
|
-
<div className="space-y-2 border-t border-
|
|
560
|
+
<div className="space-y-2 border-t border-white/10 px-3 py-2">
|
|
559
561
|
{memberships.map((membership) => {
|
|
560
562
|
const member = membership.member;
|
|
561
563
|
const metadataRows = getMemberMetadataRows(member);
|
|
@@ -595,7 +597,7 @@ function SessionMembershipPanel({
|
|
|
595
597
|
{metadataRows.map((row) => (
|
|
596
598
|
<div
|
|
597
599
|
key={row.key}
|
|
598
|
-
className="min-w-0 rounded
|
|
600
|
+
className="border border-border bg-muted/40 min-w-0 rounded-md px-2 py-1"
|
|
599
601
|
>
|
|
600
602
|
<div className="font-mono text-[10px] text-muted-foreground">{row.key}</div>
|
|
601
603
|
<div
|
|
@@ -653,7 +655,7 @@ function LogPaginationButton({
|
|
|
653
655
|
type="button"
|
|
654
656
|
onClick={onClick}
|
|
655
657
|
disabled={disabled}
|
|
656
|
-
className="inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs text-muted-foreground transition-colors hover:
|
|
658
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs text-muted-foreground transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-45"
|
|
657
659
|
title={title}
|
|
658
660
|
>
|
|
659
661
|
{icon}
|
|
@@ -679,8 +681,8 @@ function LogPaginationBar({
|
|
|
679
681
|
className={cn(
|
|
680
682
|
"flex min-h-9 flex-col gap-2 px-1 py-1 sm:flex-row sm:items-center",
|
|
681
683
|
embedded
|
|
682
|
-
? "border-t border-
|
|
683
|
-
: "
|
|
684
|
+
? "border-t border-white/10 pt-2"
|
|
685
|
+
: "border border-border bg-card shadow-sm mb-3 rounded-[8px] px-2",
|
|
684
686
|
)}
|
|
685
687
|
>
|
|
686
688
|
<div className="w-full min-w-0 px-1 font-mono text-[11px] text-muted-foreground sm:flex-1">
|
|
@@ -738,7 +740,7 @@ function sessionRiskClass(risk: ContextRiskLevel): string {
|
|
|
738
740
|
case "unknown":
|
|
739
741
|
return "text-muted-foreground";
|
|
740
742
|
case "ok":
|
|
741
|
-
return "text-
|
|
743
|
+
return "text-cyan-200";
|
|
742
744
|
}
|
|
743
745
|
}
|
|
744
746
|
|
|
@@ -749,7 +751,7 @@ function sessionHealthClass(level: ContextHealthLevel): string {
|
|
|
749
751
|
case "optimizable":
|
|
750
752
|
return "border-amber-400/25 bg-amber-400/8 text-amber-100";
|
|
751
753
|
case "ok":
|
|
752
|
-
return "border-
|
|
754
|
+
return "border-cyan-300/25 bg-cyan-400/8 text-cyan-100";
|
|
753
755
|
}
|
|
754
756
|
}
|
|
755
757
|
|
|
@@ -812,12 +814,12 @@ function CopyableCommand({ command }: { command: string }): JSX.Element {
|
|
|
812
814
|
}, [command]);
|
|
813
815
|
|
|
814
816
|
return (
|
|
815
|
-
<div className="inline-flex items-center gap-2
|
|
816
|
-
<pre className="
|
|
817
|
+
<div className="border border-border bg-muted/40 inline-flex max-w-full items-center gap-2 rounded-md px-3 py-2">
|
|
818
|
+
<pre className="m-0 overflow-x-auto font-mono text-sm text-cyan-200">{command}</pre>
|
|
817
819
|
<button
|
|
818
820
|
type="button"
|
|
819
821
|
onClick={handleCopy}
|
|
820
|
-
className="
|
|
822
|
+
className="shrink-0 cursor-pointer text-muted-foreground transition-colors hover:text-foreground"
|
|
821
823
|
aria-label="Copy command"
|
|
822
824
|
>
|
|
823
825
|
{copied ? <Check className="size-4" /> : <Copy className="size-4" />}
|
|
@@ -831,8 +833,8 @@ function McpReadyBadge(): JSX.Element {
|
|
|
831
833
|
<TooltipProvider>
|
|
832
834
|
<Tooltip>
|
|
833
835
|
<TooltipTrigger asChild>
|
|
834
|
-
<span className="inline-flex h-7 items-center gap-2 rounded-md
|
|
835
|
-
<span className="size-1.5 rounded-full bg-
|
|
836
|
+
<span className="border border-border bg-muted/40 inline-flex h-7 items-center gap-2 rounded-md px-2.5 font-mono text-[11px] font-medium text-cyan-200">
|
|
837
|
+
<span className="size-1.5 rounded-full bg-cyan-200 shadow-[0_0_8px_rgba(125,211,252,0.75)]" />
|
|
836
838
|
MCP Ready
|
|
837
839
|
<span className="hidden text-cyan-200/70 sm:inline">/api/mcp</span>
|
|
838
840
|
</span>
|
|
@@ -846,6 +848,40 @@ function McpReadyBadge(): JSX.Element {
|
|
|
846
848
|
);
|
|
847
849
|
}
|
|
848
850
|
|
|
851
|
+
function SidebarPanel({
|
|
852
|
+
label,
|
|
853
|
+
children,
|
|
854
|
+
className,
|
|
855
|
+
}: {
|
|
856
|
+
label: string;
|
|
857
|
+
children: ReactNode;
|
|
858
|
+
className?: string;
|
|
859
|
+
}): JSX.Element {
|
|
860
|
+
return (
|
|
861
|
+
<section className={cn("border border-border bg-card shadow-sm rounded-[8px]", className)}>
|
|
862
|
+
<div className="border-b border-white/10 px-3 py-2">
|
|
863
|
+
<div className="font-mono text-[10px] font-semibold uppercase tracking-[0.14em] text-white/40">
|
|
864
|
+
{label}
|
|
865
|
+
</div>
|
|
866
|
+
</div>
|
|
867
|
+
<div className="p-3">{children}</div>
|
|
868
|
+
</section>
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function SidebarMetric({ label, value }: { label: string; value: string | number }): JSX.Element {
|
|
873
|
+
return (
|
|
874
|
+
<div className="border border-border bg-muted/40 min-w-0 rounded-md px-2 py-2">
|
|
875
|
+
<div className="truncate font-mono text-lg font-semibold leading-6 text-slate-100">
|
|
876
|
+
{value}
|
|
877
|
+
</div>
|
|
878
|
+
<div className="mt-0.5 truncate font-mono text-[10px] uppercase tracking-[0.12em] text-white/35">
|
|
879
|
+
{label}
|
|
880
|
+
</div>
|
|
881
|
+
</div>
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
|
|
849
885
|
const CRAB_VARIANT_COLORS = [
|
|
850
886
|
"text-amber-500",
|
|
851
887
|
"text-rose-500",
|
|
@@ -909,7 +945,7 @@ function BrandHeader({
|
|
|
909
945
|
}): JSX.Element {
|
|
910
946
|
if (compact) {
|
|
911
947
|
return (
|
|
912
|
-
<div className="
|
|
948
|
+
<div className="border border-border bg-card shadow-sm rounded-[8px] p-3">
|
|
913
949
|
<div className="flex items-start justify-between gap-3">
|
|
914
950
|
<div className="flex min-w-0 items-start gap-2">
|
|
915
951
|
<CrabFamily compact={true} entrancePhase={crabEntrancePhase} />
|
|
@@ -972,7 +1008,7 @@ function SessionContextLogButton({ label, logId }: { label: string; logId: numbe
|
|
|
972
1008
|
<button
|
|
973
1009
|
type="button"
|
|
974
1010
|
onClick={() => openLogContext(logId)}
|
|
975
|
-
className="inline-flex h-5 items-center rounded px-1 font-mono text-[10px] text-muted-foreground transition-colors hover:bg-
|
|
1011
|
+
className="inline-flex h-5 items-center rounded px-1 font-mono text-[10px] text-muted-foreground transition-colors hover:bg-white/[0.06] hover:text-foreground"
|
|
976
1012
|
title={`Open Context for ${formatLogReference(logId)}`}
|
|
977
1013
|
>
|
|
978
1014
|
{label} {formatLogReference(logId)}
|
|
@@ -1009,7 +1045,7 @@ function SessionContextProgressBar({
|
|
|
1009
1045
|
title="Open latest Context details"
|
|
1010
1046
|
>
|
|
1011
1047
|
<span
|
|
1012
|
-
className="relative block h-2.5 overflow-hidden rounded-full border border-
|
|
1048
|
+
className="relative block h-2.5 overflow-hidden rounded-full border border-white/10 bg-black/25 transition-colors group-hover:border-cyan-200/35"
|
|
1013
1049
|
role="meter"
|
|
1014
1050
|
aria-label={ariaLabel}
|
|
1015
1051
|
aria-valuemin={0}
|
|
@@ -1044,7 +1080,7 @@ function SessionModelContextRow({ summary }: { summary: SessionModelContextSumma
|
|
|
1044
1080
|
};
|
|
1045
1081
|
|
|
1046
1082
|
return (
|
|
1047
|
-
<div className="grid gap-x-4 gap-y-2 border-t border-
|
|
1083
|
+
<div className="grid gap-x-4 gap-y-2 border-t border-white/10 py-2 first:border-t-0 lg:grid-cols-[minmax(180px,1fr)_minmax(260px,1fr)_minmax(260px,1.4fr)]">
|
|
1048
1084
|
<div className="min-w-0">
|
|
1049
1085
|
<div className="flex min-w-0 items-center gap-2">
|
|
1050
1086
|
<button
|
|
@@ -1099,7 +1135,7 @@ function SessionModelContextRow({ summary }: { summary: SessionModelContextSumma
|
|
|
1099
1135
|
<TrendIcon
|
|
1100
1136
|
className={cn(
|
|
1101
1137
|
"size-3",
|
|
1102
|
-
trend !== null && trend > 0 ? "text-amber-300" : "text-
|
|
1138
|
+
trend !== null && trend > 0 ? "text-amber-300" : "text-cyan-300",
|
|
1103
1139
|
)}
|
|
1104
1140
|
/>
|
|
1105
1141
|
)}
|
|
@@ -1137,7 +1173,7 @@ function SessionContextSummaryPanel({
|
|
|
1137
1173
|
}): JSX.Element | null {
|
|
1138
1174
|
if (summary.models.length === 0) return null;
|
|
1139
1175
|
return (
|
|
1140
|
-
<div className="mt-3 border-t border-
|
|
1176
|
+
<div className="mt-3 border-t border-white/10 pt-2">
|
|
1141
1177
|
<div className="mb-1 flex items-center justify-between gap-2">
|
|
1142
1178
|
<div className="text-[11px] font-semibold uppercase tracking-normal text-muted-foreground">
|
|
1143
1179
|
Session Context
|
|
@@ -1205,12 +1241,12 @@ function SessionContextBar({
|
|
|
1205
1241
|
}, []);
|
|
1206
1242
|
|
|
1207
1243
|
return (
|
|
1208
|
-
<div className="
|
|
1244
|
+
<div className="border border-border bg-card shadow-sm mb-3 rounded-[8px] px-3 py-2 text-xs">
|
|
1209
1245
|
<div className="flex flex-wrap items-center gap-2">
|
|
1210
1246
|
{showBackLink && (
|
|
1211
1247
|
<a
|
|
1212
1248
|
href="/"
|
|
1213
|
-
className="inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:
|
|
1249
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground"
|
|
1214
1250
|
aria-label="Back to all sessions"
|
|
1215
1251
|
title="Back to all sessions"
|
|
1216
1252
|
>
|
|
@@ -1247,7 +1283,7 @@ function SessionContextBar({
|
|
|
1247
1283
|
<button
|
|
1248
1284
|
type="button"
|
|
1249
1285
|
onClick={() => setDetailsOpen((value) => !value)}
|
|
1250
|
-
className="inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md
|
|
1286
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md px-2.5 font-mono text-[11px] text-muted-foreground transition-colors hover:text-foreground"
|
|
1251
1287
|
aria-expanded={detailsOpen}
|
|
1252
1288
|
title={detailsOpen ? "Hide context details" : "Show context details"}
|
|
1253
1289
|
>
|
|
@@ -1262,7 +1298,7 @@ function SessionContextBar({
|
|
|
1262
1298
|
<button
|
|
1263
1299
|
type="button"
|
|
1264
1300
|
onClick={handleCopyLink}
|
|
1265
|
-
className="inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:
|
|
1301
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground"
|
|
1266
1302
|
aria-label={copied ? "Copied session link" : "Copy session link"}
|
|
1267
1303
|
title={copied ? "Copied session link" : "Copy session link"}
|
|
1268
1304
|
>
|
|
@@ -1286,6 +1322,8 @@ export type ProxyViewerProps = {
|
|
|
1286
1322
|
/** Clear only the logs whose ids are passed. Called by the per-group
|
|
1287
1323
|
* Clear button on each conversation header. */
|
|
1288
1324
|
onClearGroup: (ids: number[]) => void;
|
|
1325
|
+
/** Surface short-lived action feedback without adding sticky page chrome. */
|
|
1326
|
+
onNotify?: (notice: TransientToastInput) => void;
|
|
1289
1327
|
onImportLogs?: (file: File) => Promise<ImportLogsResponse>;
|
|
1290
1328
|
viewMode: "simple" | "full";
|
|
1291
1329
|
captureMode: CaptureMode;
|
|
@@ -1319,6 +1357,7 @@ export function ProxyViewer({
|
|
|
1319
1357
|
onModelChange,
|
|
1320
1358
|
onClearAll,
|
|
1321
1359
|
onClearGroup,
|
|
1360
|
+
onNotify,
|
|
1322
1361
|
onImportLogs,
|
|
1323
1362
|
viewMode,
|
|
1324
1363
|
captureMode,
|
|
@@ -1375,13 +1414,17 @@ export function ProxyViewer({
|
|
|
1375
1414
|
try {
|
|
1376
1415
|
const result = await exportLogsAsZip(logs, mode);
|
|
1377
1416
|
if (!result.ok) {
|
|
1378
|
-
|
|
1417
|
+
if (onNotify !== undefined) {
|
|
1418
|
+
onNotify({ message: result.message, tone: "error" });
|
|
1419
|
+
} else {
|
|
1420
|
+
setExportError(result.message);
|
|
1421
|
+
}
|
|
1379
1422
|
}
|
|
1380
1423
|
} finally {
|
|
1381
1424
|
setExporting(null);
|
|
1382
1425
|
}
|
|
1383
1426
|
},
|
|
1384
|
-
[logs],
|
|
1427
|
+
[logs, onNotify],
|
|
1385
1428
|
);
|
|
1386
1429
|
const handleImportClick = useCallback(() => {
|
|
1387
1430
|
importInputRef.current?.click();
|
|
@@ -1399,18 +1442,26 @@ export function ProxyViewer({
|
|
|
1399
1442
|
void onImportLogs(file)
|
|
1400
1443
|
.then((result) => {
|
|
1401
1444
|
const skipped = result.skipped > 0 ? `, skipped ${String(result.skipped)}` : "";
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1445
|
+
const message = `Imported ${String(result.imported)} logs as ${result.sessionId}${skipped}.`;
|
|
1446
|
+
if (onNotify !== undefined) {
|
|
1447
|
+
onNotify({ message, tone: "success" });
|
|
1448
|
+
} else {
|
|
1449
|
+
setImportStatus(message);
|
|
1450
|
+
}
|
|
1405
1451
|
})
|
|
1406
1452
|
.catch((err: unknown) => {
|
|
1407
|
-
|
|
1453
|
+
const message = err instanceof Error ? err.message : "Failed to import logs";
|
|
1454
|
+
if (onNotify !== undefined) {
|
|
1455
|
+
onNotify({ message, tone: "error" });
|
|
1456
|
+
} else {
|
|
1457
|
+
setImportError(message);
|
|
1458
|
+
}
|
|
1408
1459
|
})
|
|
1409
1460
|
.finally(() => {
|
|
1410
1461
|
setImporting(false);
|
|
1411
1462
|
});
|
|
1412
1463
|
},
|
|
1413
|
-
[onImportLogs],
|
|
1464
|
+
[onImportLogs, onNotify],
|
|
1414
1465
|
);
|
|
1415
1466
|
// Close the compare drawer when the user changes the session or model
|
|
1416
1467
|
// filter, since the predecessor relationship may no longer be meaningful.
|
|
@@ -1467,38 +1518,30 @@ export function ProxyViewer({
|
|
|
1467
1518
|
);
|
|
1468
1519
|
|
|
1469
1520
|
return (
|
|
1470
|
-
<div className="min-h-screen w-full
|
|
1521
|
+
<div className="bg-background min-h-screen w-full text-foreground">
|
|
1471
1522
|
<div className="grid min-h-screen w-full grid-cols-1 lg:grid-cols-[340px_minmax(0,1fr)] xl:grid-cols-[360px_minmax(0,1fr)]">
|
|
1472
|
-
<aside className="
|
|
1523
|
+
<aside className="bg-background border-b lg:sticky lg:top-0 lg:h-screen lg:overflow-y-auto lg:border-b-0 lg:border-r">
|
|
1473
1524
|
<div className="space-y-4 p-4">
|
|
1474
1525
|
<BrandHeader compact={true} crabEntrancePhase={crabEntrancePhase} />
|
|
1475
1526
|
|
|
1476
|
-
<
|
|
1477
|
-
<div className="
|
|
1478
|
-
<
|
|
1479
|
-
<
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
<div className="min-w-0">
|
|
1488
|
-
<div className="font-mono text-lg font-semibold text-foreground">
|
|
1489
|
-
{formatTokens(hasSessionContext ? sessionContextTotalOut : totalOut)}
|
|
1490
|
-
</div>
|
|
1491
|
-
<div className="text-[10px] uppercase text-muted-foreground">Output</div>
|
|
1527
|
+
<SidebarPanel label="Session">
|
|
1528
|
+
<div className="grid grid-cols-3 gap-2">
|
|
1529
|
+
<SidebarMetric label="Requests" value={logs.length} />
|
|
1530
|
+
<SidebarMetric
|
|
1531
|
+
label="Input"
|
|
1532
|
+
value={formatTokens(hasSessionContext ? sessionContextTotalIn : totalIn)}
|
|
1533
|
+
/>
|
|
1534
|
+
<SidebarMetric
|
|
1535
|
+
label="Output"
|
|
1536
|
+
value={formatTokens(hasSessionContext ? sessionContextTotalOut : totalOut)}
|
|
1537
|
+
/>
|
|
1492
1538
|
</div>
|
|
1493
|
-
</
|
|
1539
|
+
</SidebarPanel>
|
|
1494
1540
|
|
|
1495
|
-
<
|
|
1496
|
-
<div className="
|
|
1497
|
-
Workspace
|
|
1498
|
-
</div>
|
|
1499
|
-
<div className="rounded-md border border-border bg-[#0c0d0f] px-3 py-2">
|
|
1541
|
+
<SidebarPanel label="Workspace">
|
|
1542
|
+
<div className="border border-border bg-muted/40 rounded-md px-3 py-2">
|
|
1500
1543
|
<div
|
|
1501
|
-
className="font-mono text-xs font-semibold text-
|
|
1544
|
+
className="truncate font-mono text-xs font-semibold text-cyan-100"
|
|
1502
1545
|
title={activeScopeLabel}
|
|
1503
1546
|
>
|
|
1504
1547
|
{activeScopeLabel}
|
|
@@ -1507,7 +1550,7 @@ export function ProxyViewer({
|
|
|
1507
1550
|
{isPinnedSessionPage ? "Session workspace" : "Live workspace"}
|
|
1508
1551
|
</div>
|
|
1509
1552
|
</div>
|
|
1510
|
-
</
|
|
1553
|
+
</SidebarPanel>
|
|
1511
1554
|
|
|
1512
1555
|
{hasSessionContext && sessionContextScope !== null && (
|
|
1513
1556
|
<SessionContextBar
|
|
@@ -1527,44 +1570,43 @@ export function ProxyViewer({
|
|
|
1527
1570
|
<SessionMembershipPanel memberships={sessionMemberships} />
|
|
1528
1571
|
)}
|
|
1529
1572
|
|
|
1530
|
-
<
|
|
1531
|
-
<div className="
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
<
|
|
1573
|
+
<SidebarPanel label="Filters">
|
|
1574
|
+
<div className="space-y-2">
|
|
1575
|
+
{!hideSessionFilter && (
|
|
1576
|
+
<Select value={selectedSession} onValueChange={onSessionChange}>
|
|
1577
|
+
<SelectTrigger className="border border-input bg-background hover:bg-accent hover:text-accent-foreground h-9 w-full text-xs">
|
|
1578
|
+
<SelectValue placeholder="All sessions" />
|
|
1579
|
+
</SelectTrigger>
|
|
1580
|
+
<SelectContent>
|
|
1581
|
+
<SelectItem value="__all__">All sessions</SelectItem>
|
|
1582
|
+
{sessions.map((s) => (
|
|
1583
|
+
<SelectItem key={s} value={s}>
|
|
1584
|
+
{truncateSessionId(s)}
|
|
1585
|
+
</SelectItem>
|
|
1586
|
+
))}
|
|
1587
|
+
</SelectContent>
|
|
1588
|
+
</Select>
|
|
1589
|
+
)}
|
|
1590
|
+
<Select value={selectedModel} onValueChange={onModelChange}>
|
|
1591
|
+
<SelectTrigger className="border border-input bg-background hover:bg-accent hover:text-accent-foreground h-9 w-full text-xs">
|
|
1592
|
+
<SelectValue placeholder="All models" />
|
|
1538
1593
|
</SelectTrigger>
|
|
1539
1594
|
<SelectContent>
|
|
1540
|
-
<SelectItem value="__all__">All
|
|
1541
|
-
{
|
|
1542
|
-
<SelectItem key={
|
|
1543
|
-
{
|
|
1595
|
+
<SelectItem value="__all__">All models</SelectItem>
|
|
1596
|
+
{models.map((m) => (
|
|
1597
|
+
<SelectItem key={m} value={m}>
|
|
1598
|
+
{m}
|
|
1544
1599
|
</SelectItem>
|
|
1545
1600
|
))}
|
|
1546
1601
|
</SelectContent>
|
|
1547
1602
|
</Select>
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
<SelectTrigger className="h-9 w-full text-xs">
|
|
1551
|
-
<SelectValue placeholder="All models" />
|
|
1552
|
-
</SelectTrigger>
|
|
1553
|
-
<SelectContent>
|
|
1554
|
-
<SelectItem value="__all__">All models</SelectItem>
|
|
1555
|
-
{models.map((m) => (
|
|
1556
|
-
<SelectItem key={m} value={m}>
|
|
1557
|
-
{m}
|
|
1558
|
-
</SelectItem>
|
|
1559
|
-
))}
|
|
1560
|
-
</SelectContent>
|
|
1561
|
-
</Select>
|
|
1562
|
-
</div>
|
|
1603
|
+
</div>
|
|
1604
|
+
</SidebarPanel>
|
|
1563
1605
|
</div>
|
|
1564
1606
|
</aside>
|
|
1565
1607
|
|
|
1566
1608
|
<main className="min-w-0">
|
|
1567
|
-
<div className="sticky top-0 z-30 border-b
|
|
1609
|
+
<div className="border border-border bg-background/95 shadow-sm sticky top-0 z-30 border-b px-4 py-3 lg:px-5">
|
|
1568
1610
|
<div className="flex flex-wrap items-center gap-2">
|
|
1569
1611
|
<div className="min-w-[180px] flex-1">
|
|
1570
1612
|
<div className="text-sm font-semibold text-foreground">Logs</div>
|
|
@@ -1585,7 +1627,7 @@ export function ProxyViewer({
|
|
|
1585
1627
|
type="button"
|
|
1586
1628
|
onClick={handleImportClick}
|
|
1587
1629
|
disabled={importing}
|
|
1588
|
-
className="inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground transition-colors hover:
|
|
1630
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
1589
1631
|
title="Import Agent Inspector export"
|
|
1590
1632
|
>
|
|
1591
1633
|
{importing ? (
|
|
@@ -1606,7 +1648,7 @@ export function ProxyViewer({
|
|
|
1606
1648
|
void handleExport("redacted");
|
|
1607
1649
|
}}
|
|
1608
1650
|
disabled={exporting !== null}
|
|
1609
|
-
className="inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground transition-colors hover:
|
|
1651
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
1610
1652
|
title="Export redacted logs as JSON ZIP"
|
|
1611
1653
|
>
|
|
1612
1654
|
{exporting === "redacted" ? (
|
|
@@ -1626,7 +1668,7 @@ export function ProxyViewer({
|
|
|
1626
1668
|
void handleExport("raw");
|
|
1627
1669
|
}}
|
|
1628
1670
|
disabled={exporting !== null}
|
|
1629
|
-
className="inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs text-muted-foreground transition-colors hover:
|
|
1671
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs text-muted-foreground transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
|
1630
1672
|
title="Export raw logs without redaction"
|
|
1631
1673
|
>
|
|
1632
1674
|
{exporting === "raw" ? (
|
|
@@ -1642,7 +1684,7 @@ export function ProxyViewer({
|
|
|
1642
1684
|
<button
|
|
1643
1685
|
type="button"
|
|
1644
1686
|
onClick={onClearAll}
|
|
1645
|
-
className="inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground transition-colors hover:
|
|
1687
|
+
className="border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground transition-colors hover:text-foreground"
|
|
1646
1688
|
title="Clear all logs"
|
|
1647
1689
|
>
|
|
1648
1690
|
<Trash2 className="size-3.5" />
|
|
@@ -1650,18 +1692,18 @@ export function ProxyViewer({
|
|
|
1650
1692
|
</button>
|
|
1651
1693
|
</div>
|
|
1652
1694
|
<LogPaginationBar logs={logs} pagination={pagination} embedded={true} />
|
|
1653
|
-
{exportError !== null && (
|
|
1695
|
+
{onNotify === undefined && exportError !== null && (
|
|
1654
1696
|
<div className="mt-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive">
|
|
1655
1697
|
{exportError}
|
|
1656
1698
|
</div>
|
|
1657
1699
|
)}
|
|
1658
|
-
{importError !== null && (
|
|
1700
|
+
{onNotify === undefined && importError !== null && (
|
|
1659
1701
|
<div className="mt-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive">
|
|
1660
1702
|
{importError}
|
|
1661
1703
|
</div>
|
|
1662
1704
|
)}
|
|
1663
|
-
{importStatus !== null && importError === null && (
|
|
1664
|
-
<div className="mt-2 rounded-md border border-
|
|
1705
|
+
{onNotify === undefined && importStatus !== null && importError === null && (
|
|
1706
|
+
<div className="mt-2 rounded-md border border-cyan-400/25 bg-cyan-500/10 px-3 py-2 text-xs text-cyan-100">
|
|
1665
1707
|
{importStatus}
|
|
1666
1708
|
</div>
|
|
1667
1709
|
)}
|
|
@@ -1672,7 +1714,7 @@ export function ProxyViewer({
|
|
|
1672
1714
|
{logs.length === 0 ? (
|
|
1673
1715
|
selectedSession !== "__all__" ? (
|
|
1674
1716
|
<div className="py-16 text-center text-muted-foreground">
|
|
1675
|
-
<div className="mx-auto max-w-xl space-y-4 rounded-
|
|
1717
|
+
<div className="border border-border bg-card shadow-sm mx-auto max-w-xl space-y-4 rounded-[8px] px-4 py-8">
|
|
1676
1718
|
<p className="text-sm font-medium">
|
|
1677
1719
|
{isLoading ? "Loading session data..." : "Session not found"}
|
|
1678
1720
|
</p>
|
|
@@ -1706,7 +1748,7 @@ export function ProxyViewer({
|
|
|
1706
1748
|
</div>
|
|
1707
1749
|
) : (
|
|
1708
1750
|
<div className="py-16 text-center text-muted-foreground">
|
|
1709
|
-
<div className="mx-auto max-w-2xl space-y-4 rounded-
|
|
1751
|
+
<div className="border border-border bg-card shadow-sm mx-auto max-w-2xl space-y-4 rounded-[8px] px-4 py-8">
|
|
1710
1752
|
<p className="text-sm">No requests captured yet.</p>
|
|
1711
1753
|
<div className="flex flex-col items-center gap-2">
|
|
1712
1754
|
<CopyableCommand command="ANTHROPIC_BASE_URL=http://localhost:25947/proxy <your-tool>" />
|
|
@@ -1736,8 +1778,7 @@ export function ProxyViewer({
|
|
|
1736
1778
|
onCompareWithPrevious={handleCompareWithPrevious}
|
|
1737
1779
|
comparisonPredecessors={comparisonPredecessors}
|
|
1738
1780
|
onClearGroup={onClearGroup}
|
|
1739
|
-
|
|
1740
|
-
hasPinnedSessionContext={hasSessionContext}
|
|
1781
|
+
defaultExpanded={groups.length === 1}
|
|
1741
1782
|
timeDisplayFormat={timeDisplayFormat}
|
|
1742
1783
|
/>
|
|
1743
1784
|
</div>
|