@tonyclaw/agent-inspector 2.1.14 → 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-Ccrrcx1j.js +1 -0
- package/.output/public/assets/ProxyViewerContainer-DLqClc9A.js +106 -0
- package/.output/public/assets/ReplayDialog-C7axhr-l.js +1 -0
- package/.output/public/assets/{RequestAnatomy-BNahe83D.js → RequestAnatomy-WOptg8j_.js} +1 -1
- package/.output/public/assets/{ResponseView-DSOnGqi6.js → ResponseView-CSAcxh_M.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-BEKTDklB.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-DWOkqdCa.js → index-DMPNh46t.js} +1 -1
- package/.output/public/assets/{json-viewer-C2JpgcW0.js → json-viewer-inX5QSa3.js} +1 -1
- package/.output/public/assets/{main-CSONBwwn.js → main-aCAKYGSD.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +224 -194
- package/.output/server/{_sessionId-CPAa37n5.mjs → _sessionId-C0fhvQnn.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-ceW5VxMo.mjs → CompareDrawer-CO-Ti5sg.mjs} +36 -15
- package/.output/server/_ssr/{ProxyViewerContainer-CDfEE_w-.mjs → ProxyViewerContainer-N4J7uBCX.mjs} +1153 -494
- package/.output/server/_ssr/{ReplayDialog-V0s_eEbR.mjs → ReplayDialog-J1VxC0In.mjs} +14 -7
- package/.output/server/_ssr/{RequestAnatomy-f1ccwR9d.mjs → RequestAnatomy-DBH-F3rq.mjs} +3 -3
- package/.output/server/_ssr/{ResponseView-BIRrqG4H.mjs → ResponseView-DLFwV7Hd.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-V3JFjCgX.mjs → StreamingChunkSequence-BywmuND0.mjs} +3 -3
- package/.output/server/_ssr/{index-DsykulzS.mjs → index-CStymIc0.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-Dcnm0Ivf.mjs → json-viewer-DlQu0N1x.mjs} +3 -3
- package/.output/server/_ssr/{router-4bdm6Mt2.mjs → router-CQ4Zxhmr.mjs} +118 -47
- package/.output/server/{_tanstack-start-manifest_v-Xp4CO64V.mjs → _tanstack-start-manifest_v-BHeJBGfo.mjs} +1 -1
- package/.output/server/index.mjs +75 -75
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +174 -107
- package/src/components/ProxyViewerContainer.tsx +77 -67
- package/src/components/alerts/AlertsDialog.tsx +2 -1
- package/src/components/clients/ClientLogo.tsx +132 -0
- package/src/components/groups/GroupsDialog.tsx +37 -14
- 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 +62 -29
- package/src/components/proxy-viewer/CompareDrawer.tsx +38 -8
- 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 +65 -61
- package/src/components/proxy-viewer/LogEntry.tsx +31 -18
- package/src/components/proxy-viewer/LogEntryHeader.tsx +42 -49
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +60 -0
- package/src/components/proxy-viewer/ReplayDialog.tsx +8 -2
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +9 -9
- package/src/components/proxy-viewer/ThreadConnector.tsx +22 -11
- package/src/components/proxy-viewer/TurnGroup.tsx +30 -36
- package/src/components/proxy-viewer/TurnGroupList.tsx +3 -0
- package/src/components/proxy-viewer/viewerState.ts +5 -6
- package/src/components/ui/icon-trigger.ts +2 -0
- package/src/components/ui/transient-toast.tsx +104 -0
- package/src/lib/sessionInfoContract.ts +5 -0
- package/src/lib/stopReason.ts +48 -16
- 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 +36 -3
- 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/CompareDrawer-DjgjIFx7.js +0 -1
- package/.output/public/assets/ProxyViewerContainer-CWUQZLYy.js +0 -106
- package/.output/public/assets/ReplayDialog-CU0Tbb2c.js +0 -1
- package/.output/public/assets/_sessionId-Cif8JZdn.js +0 -1
- package/.output/public/assets/index-D_WfwzUi.js +0 -1
- package/.output/public/assets/index-DtLuQrs0.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,
|
|
@@ -40,13 +41,14 @@ import { formatTimestampRange } from "../lib/timeDisplay";
|
|
|
40
41
|
import { cn, formatContextWindowTokens, formatTokens } from "../lib/utils";
|
|
41
42
|
import { useProviders } from "../lib/useProviders";
|
|
42
43
|
import packageJson from "../../package.json";
|
|
43
|
-
import { groupLogsByConversation } from "./proxy-viewer";
|
|
44
|
+
import { groupLogsByConversation, type ConversationGroupData } from "./proxy-viewer";
|
|
44
45
|
|
|
45
46
|
import { CrabLogo } from "./ui/crab-logo";
|
|
46
47
|
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";
|
|
@@ -72,6 +74,25 @@ function truncateSessionId(id: string): string {
|
|
|
72
74
|
return `${id.slice(0, 12)}...${id.slice(-12)}`;
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
function buildDisplayNumberByLogId(groups: readonly ConversationGroupData[]): Map<number, number> {
|
|
78
|
+
const result = new Map<number, number>();
|
|
79
|
+
for (const group of groups) {
|
|
80
|
+
for (let index = 0; index < group.logs.length; index += 1) {
|
|
81
|
+
const log = group.logs[index];
|
|
82
|
+
if (log === undefined) continue;
|
|
83
|
+
result.set(log.id, index + 1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function displayNumberForLog(
|
|
90
|
+
log: CapturedLog,
|
|
91
|
+
displayNumberByLogId: ReadonlyMap<number, number>,
|
|
92
|
+
): number {
|
|
93
|
+
return displayNumberByLogId.get(log.id) ?? log.id;
|
|
94
|
+
}
|
|
95
|
+
|
|
75
96
|
export function shouldShowRawExport(captureMode: CaptureMode): boolean {
|
|
76
97
|
return captureMode === "full";
|
|
77
98
|
}
|
|
@@ -247,7 +268,7 @@ function slateStatusClass(status: SessionSlateStatus): string {
|
|
|
247
268
|
case "watch":
|
|
248
269
|
return "border-amber-300/35 bg-amber-400/10 text-amber-100";
|
|
249
270
|
case "clear":
|
|
250
|
-
return "border-
|
|
271
|
+
return "border-slate-300/20 bg-slate-300/[0.08] text-slate-100";
|
|
251
272
|
}
|
|
252
273
|
}
|
|
253
274
|
|
|
@@ -255,9 +276,9 @@ function SessionOpeningSlate({ stats }: { stats: SessionSlateStats }): JSX.Eleme
|
|
|
255
276
|
if (stats.requests === 0) return null;
|
|
256
277
|
|
|
257
278
|
return (
|
|
258
|
-
<div className="
|
|
279
|
+
<div className="border border-border bg-card shadow-sm mb-4 rounded-[8px]">
|
|
259
280
|
<div className="grid grid-cols-2 gap-0 md:grid-cols-[minmax(220px,1.2fr)_repeat(4,minmax(120px,1fr))]">
|
|
260
|
-
<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">
|
|
261
282
|
<div className="flex items-center gap-2 text-[10px] font-semibold uppercase text-muted-foreground">
|
|
262
283
|
<Clapperboard className="size-3.5 text-cyan-300" />
|
|
263
284
|
Opening Slate
|
|
@@ -269,7 +290,7 @@ function SessionOpeningSlate({ stats }: { stats: SessionSlateStats }): JSX.Eleme
|
|
|
269
290
|
{stats.timeRange ?? "Timeline pending"}
|
|
270
291
|
</div>
|
|
271
292
|
</div>
|
|
272
|
-
<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">
|
|
273
294
|
<div className="text-[10px] uppercase text-muted-foreground">Status</div>
|
|
274
295
|
<div
|
|
275
296
|
className={cn(
|
|
@@ -280,13 +301,13 @@ function SessionOpeningSlate({ stats }: { stats: SessionSlateStats }): JSX.Eleme
|
|
|
280
301
|
{stats.statusLabel}
|
|
281
302
|
</div>
|
|
282
303
|
</div>
|
|
283
|
-
<div className="border-b border-
|
|
304
|
+
<div className="border-b border-white/10 px-4 py-3 md:border-b-0 md:border-r">
|
|
284
305
|
<div className="text-[10px] uppercase text-muted-foreground">Requests</div>
|
|
285
306
|
<div className="mt-2 font-mono text-xl font-semibold text-foreground">
|
|
286
307
|
{stats.requests}
|
|
287
308
|
</div>
|
|
288
309
|
</div>
|
|
289
|
-
<div className="border-r border-
|
|
310
|
+
<div className="border-r border-white/10 px-4 py-3 md:border-r">
|
|
290
311
|
<div className="flex items-center gap-1 text-[10px] uppercase text-muted-foreground">
|
|
291
312
|
<Siren className="size-3 text-red-300" />
|
|
292
313
|
Failure
|
|
@@ -392,7 +413,7 @@ function sessionRiskBarClass(risk: ContextRiskLevel): string {
|
|
|
392
413
|
case "unknown":
|
|
393
414
|
return "bg-muted-foreground/50";
|
|
394
415
|
case "ok":
|
|
395
|
-
return "bg-
|
|
416
|
+
return "bg-cyan-300";
|
|
396
417
|
}
|
|
397
418
|
}
|
|
398
419
|
|
|
@@ -451,7 +472,7 @@ function evidenceChipClass(value: string): string {
|
|
|
451
472
|
normalized.includes("complete") ||
|
|
452
473
|
normalized.includes("accept")
|
|
453
474
|
) {
|
|
454
|
-
return "border-
|
|
475
|
+
return "border-cyan-300/25 bg-cyan-400/10 text-cyan-100";
|
|
455
476
|
}
|
|
456
477
|
return "border-border/70 bg-background/70 text-muted-foreground";
|
|
457
478
|
}
|
|
@@ -494,11 +515,11 @@ function SessionMembershipPanel({
|
|
|
494
515
|
);
|
|
495
516
|
|
|
496
517
|
return (
|
|
497
|
-
<div className="
|
|
518
|
+
<div className="border border-border bg-card shadow-sm mb-3 rounded-[8px] text-xs">
|
|
498
519
|
<button
|
|
499
520
|
type="button"
|
|
500
521
|
onClick={() => setOpen((value) => !value)}
|
|
501
|
-
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]"
|
|
502
523
|
aria-expanded={open}
|
|
503
524
|
>
|
|
504
525
|
<div className="min-w-0 flex-1">
|
|
@@ -536,7 +557,7 @@ function SessionMembershipPanel({
|
|
|
536
557
|
)}
|
|
537
558
|
</button>
|
|
538
559
|
{open && (
|
|
539
|
-
<div className="space-y-2 border-t border-
|
|
560
|
+
<div className="space-y-2 border-t border-white/10 px-3 py-2">
|
|
540
561
|
{memberships.map((membership) => {
|
|
541
562
|
const member = membership.member;
|
|
542
563
|
const metadataRows = getMemberMetadataRows(member);
|
|
@@ -576,7 +597,7 @@ function SessionMembershipPanel({
|
|
|
576
597
|
{metadataRows.map((row) => (
|
|
577
598
|
<div
|
|
578
599
|
key={row.key}
|
|
579
|
-
className="min-w-0 rounded
|
|
600
|
+
className="border border-border bg-muted/40 min-w-0 rounded-md px-2 py-1"
|
|
580
601
|
>
|
|
581
602
|
<div className="font-mono text-[10px] text-muted-foreground">{row.key}</div>
|
|
582
603
|
<div
|
|
@@ -634,7 +655,7 @@ function LogPaginationButton({
|
|
|
634
655
|
type="button"
|
|
635
656
|
onClick={onClick}
|
|
636
657
|
disabled={disabled}
|
|
637
|
-
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"
|
|
638
659
|
title={title}
|
|
639
660
|
>
|
|
640
661
|
{icon}
|
|
@@ -660,8 +681,8 @@ function LogPaginationBar({
|
|
|
660
681
|
className={cn(
|
|
661
682
|
"flex min-h-9 flex-col gap-2 px-1 py-1 sm:flex-row sm:items-center",
|
|
662
683
|
embedded
|
|
663
|
-
? "border-t border-
|
|
664
|
-
: "
|
|
684
|
+
? "border-t border-white/10 pt-2"
|
|
685
|
+
: "border border-border bg-card shadow-sm mb-3 rounded-[8px] px-2",
|
|
665
686
|
)}
|
|
666
687
|
>
|
|
667
688
|
<div className="w-full min-w-0 px-1 font-mono text-[11px] text-muted-foreground sm:flex-1">
|
|
@@ -719,7 +740,7 @@ function sessionRiskClass(risk: ContextRiskLevel): string {
|
|
|
719
740
|
case "unknown":
|
|
720
741
|
return "text-muted-foreground";
|
|
721
742
|
case "ok":
|
|
722
|
-
return "text-
|
|
743
|
+
return "text-cyan-200";
|
|
723
744
|
}
|
|
724
745
|
}
|
|
725
746
|
|
|
@@ -730,7 +751,7 @@ function sessionHealthClass(level: ContextHealthLevel): string {
|
|
|
730
751
|
case "optimizable":
|
|
731
752
|
return "border-amber-400/25 bg-amber-400/8 text-amber-100";
|
|
732
753
|
case "ok":
|
|
733
|
-
return "border-
|
|
754
|
+
return "border-cyan-300/25 bg-cyan-400/8 text-cyan-100";
|
|
734
755
|
}
|
|
735
756
|
}
|
|
736
757
|
|
|
@@ -793,12 +814,12 @@ function CopyableCommand({ command }: { command: string }): JSX.Element {
|
|
|
793
814
|
}, [command]);
|
|
794
815
|
|
|
795
816
|
return (
|
|
796
|
-
<div className="inline-flex items-center gap-2
|
|
797
|
-
<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>
|
|
798
819
|
<button
|
|
799
820
|
type="button"
|
|
800
821
|
onClick={handleCopy}
|
|
801
|
-
className="
|
|
822
|
+
className="shrink-0 cursor-pointer text-muted-foreground transition-colors hover:text-foreground"
|
|
802
823
|
aria-label="Copy command"
|
|
803
824
|
>
|
|
804
825
|
{copied ? <Check className="size-4" /> : <Copy className="size-4" />}
|
|
@@ -812,8 +833,8 @@ function McpReadyBadge(): JSX.Element {
|
|
|
812
833
|
<TooltipProvider>
|
|
813
834
|
<Tooltip>
|
|
814
835
|
<TooltipTrigger asChild>
|
|
815
|
-
<span className="inline-flex h-7 items-center gap-2 rounded-md
|
|
816
|
-
<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)]" />
|
|
817
838
|
MCP Ready
|
|
818
839
|
<span className="hidden text-cyan-200/70 sm:inline">/api/mcp</span>
|
|
819
840
|
</span>
|
|
@@ -827,6 +848,40 @@ function McpReadyBadge(): JSX.Element {
|
|
|
827
848
|
);
|
|
828
849
|
}
|
|
829
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
|
+
|
|
830
885
|
const CRAB_VARIANT_COLORS = [
|
|
831
886
|
"text-amber-500",
|
|
832
887
|
"text-rose-500",
|
|
@@ -890,7 +945,7 @@ function BrandHeader({
|
|
|
890
945
|
}): JSX.Element {
|
|
891
946
|
if (compact) {
|
|
892
947
|
return (
|
|
893
|
-
<div className="
|
|
948
|
+
<div className="border border-border bg-card shadow-sm rounded-[8px] p-3">
|
|
894
949
|
<div className="flex items-start justify-between gap-3">
|
|
895
950
|
<div className="flex min-w-0 items-start gap-2">
|
|
896
951
|
<CrabFamily compact={true} entrancePhase={crabEntrancePhase} />
|
|
@@ -953,7 +1008,7 @@ function SessionContextLogButton({ label, logId }: { label: string; logId: numbe
|
|
|
953
1008
|
<button
|
|
954
1009
|
type="button"
|
|
955
1010
|
onClick={() => openLogContext(logId)}
|
|
956
|
-
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"
|
|
957
1012
|
title={`Open Context for ${formatLogReference(logId)}`}
|
|
958
1013
|
>
|
|
959
1014
|
{label} {formatLogReference(logId)}
|
|
@@ -990,7 +1045,7 @@ function SessionContextProgressBar({
|
|
|
990
1045
|
title="Open latest Context details"
|
|
991
1046
|
>
|
|
992
1047
|
<span
|
|
993
|
-
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"
|
|
994
1049
|
role="meter"
|
|
995
1050
|
aria-label={ariaLabel}
|
|
996
1051
|
aria-valuemin={0}
|
|
@@ -1025,7 +1080,7 @@ function SessionModelContextRow({ summary }: { summary: SessionModelContextSumma
|
|
|
1025
1080
|
};
|
|
1026
1081
|
|
|
1027
1082
|
return (
|
|
1028
|
-
<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)]">
|
|
1029
1084
|
<div className="min-w-0">
|
|
1030
1085
|
<div className="flex min-w-0 items-center gap-2">
|
|
1031
1086
|
<button
|
|
@@ -1080,7 +1135,7 @@ function SessionModelContextRow({ summary }: { summary: SessionModelContextSumma
|
|
|
1080
1135
|
<TrendIcon
|
|
1081
1136
|
className={cn(
|
|
1082
1137
|
"size-3",
|
|
1083
|
-
trend !== null && trend > 0 ? "text-amber-300" : "text-
|
|
1138
|
+
trend !== null && trend > 0 ? "text-amber-300" : "text-cyan-300",
|
|
1084
1139
|
)}
|
|
1085
1140
|
/>
|
|
1086
1141
|
)}
|
|
@@ -1118,7 +1173,7 @@ function SessionContextSummaryPanel({
|
|
|
1118
1173
|
}): JSX.Element | null {
|
|
1119
1174
|
if (summary.models.length === 0) return null;
|
|
1120
1175
|
return (
|
|
1121
|
-
<div className="mt-3 border-t border-
|
|
1176
|
+
<div className="mt-3 border-t border-white/10 pt-2">
|
|
1122
1177
|
<div className="mb-1 flex items-center justify-between gap-2">
|
|
1123
1178
|
<div className="text-[11px] font-semibold uppercase tracking-normal text-muted-foreground">
|
|
1124
1179
|
Session Context
|
|
@@ -1186,12 +1241,12 @@ function SessionContextBar({
|
|
|
1186
1241
|
}, []);
|
|
1187
1242
|
|
|
1188
1243
|
return (
|
|
1189
|
-
<div className="
|
|
1244
|
+
<div className="border border-border bg-card shadow-sm mb-3 rounded-[8px] px-3 py-2 text-xs">
|
|
1190
1245
|
<div className="flex flex-wrap items-center gap-2">
|
|
1191
1246
|
{showBackLink && (
|
|
1192
1247
|
<a
|
|
1193
1248
|
href="/"
|
|
1194
|
-
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"
|
|
1195
1250
|
aria-label="Back to all sessions"
|
|
1196
1251
|
title="Back to all sessions"
|
|
1197
1252
|
>
|
|
@@ -1228,7 +1283,7 @@ function SessionContextBar({
|
|
|
1228
1283
|
<button
|
|
1229
1284
|
type="button"
|
|
1230
1285
|
onClick={() => setDetailsOpen((value) => !value)}
|
|
1231
|
-
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"
|
|
1232
1287
|
aria-expanded={detailsOpen}
|
|
1233
1288
|
title={detailsOpen ? "Hide context details" : "Show context details"}
|
|
1234
1289
|
>
|
|
@@ -1243,7 +1298,7 @@ function SessionContextBar({
|
|
|
1243
1298
|
<button
|
|
1244
1299
|
type="button"
|
|
1245
1300
|
onClick={handleCopyLink}
|
|
1246
|
-
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"
|
|
1247
1302
|
aria-label={copied ? "Copied session link" : "Copy session link"}
|
|
1248
1303
|
title={copied ? "Copied session link" : "Copy session link"}
|
|
1249
1304
|
>
|
|
@@ -1267,6 +1322,8 @@ export type ProxyViewerProps = {
|
|
|
1267
1322
|
/** Clear only the logs whose ids are passed. Called by the per-group
|
|
1268
1323
|
* Clear button on each conversation header. */
|
|
1269
1324
|
onClearGroup: (ids: number[]) => void;
|
|
1325
|
+
/** Surface short-lived action feedback without adding sticky page chrome. */
|
|
1326
|
+
onNotify?: (notice: TransientToastInput) => void;
|
|
1270
1327
|
onImportLogs?: (file: File) => Promise<ImportLogsResponse>;
|
|
1271
1328
|
viewMode: "simple" | "full";
|
|
1272
1329
|
captureMode: CaptureMode;
|
|
@@ -1300,6 +1357,7 @@ export function ProxyViewer({
|
|
|
1300
1357
|
onModelChange,
|
|
1301
1358
|
onClearAll,
|
|
1302
1359
|
onClearGroup,
|
|
1360
|
+
onNotify,
|
|
1303
1361
|
onImportLogs,
|
|
1304
1362
|
viewMode,
|
|
1305
1363
|
captureMode,
|
|
@@ -1356,13 +1414,17 @@ export function ProxyViewer({
|
|
|
1356
1414
|
try {
|
|
1357
1415
|
const result = await exportLogsAsZip(logs, mode);
|
|
1358
1416
|
if (!result.ok) {
|
|
1359
|
-
|
|
1417
|
+
if (onNotify !== undefined) {
|
|
1418
|
+
onNotify({ message: result.message, tone: "error" });
|
|
1419
|
+
} else {
|
|
1420
|
+
setExportError(result.message);
|
|
1421
|
+
}
|
|
1360
1422
|
}
|
|
1361
1423
|
} finally {
|
|
1362
1424
|
setExporting(null);
|
|
1363
1425
|
}
|
|
1364
1426
|
},
|
|
1365
|
-
[logs],
|
|
1427
|
+
[logs, onNotify],
|
|
1366
1428
|
);
|
|
1367
1429
|
const handleImportClick = useCallback(() => {
|
|
1368
1430
|
importInputRef.current?.click();
|
|
@@ -1380,18 +1442,26 @@ export function ProxyViewer({
|
|
|
1380
1442
|
void onImportLogs(file)
|
|
1381
1443
|
.then((result) => {
|
|
1382
1444
|
const skipped = result.skipped > 0 ? `, skipped ${String(result.skipped)}` : "";
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
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
|
+
}
|
|
1386
1451
|
})
|
|
1387
1452
|
.catch((err: unknown) => {
|
|
1388
|
-
|
|
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
|
+
}
|
|
1389
1459
|
})
|
|
1390
1460
|
.finally(() => {
|
|
1391
1461
|
setImporting(false);
|
|
1392
1462
|
});
|
|
1393
1463
|
},
|
|
1394
|
-
[onImportLogs],
|
|
1464
|
+
[onImportLogs, onNotify],
|
|
1395
1465
|
);
|
|
1396
1466
|
// Close the compare drawer when the user changes the session or model
|
|
1397
1467
|
// filter, since the predecessor relationship may no longer be meaningful.
|
|
@@ -1404,6 +1474,7 @@ export function ProxyViewer({
|
|
|
1404
1474
|
}, []);
|
|
1405
1475
|
|
|
1406
1476
|
const groups = useMemo(() => groupLogsByConversation(logs), [logs]);
|
|
1477
|
+
const displayNumberByLogId = useMemo(() => buildDisplayNumberByLogId(groups), [groups]);
|
|
1407
1478
|
const sessionContextScope = useMemo(
|
|
1408
1479
|
() => resolveSessionContextScope({ pinnedSessionId, selectedSession, sessions, logs }),
|
|
1409
1480
|
[logs, pinnedSessionId, selectedSession, sessions],
|
|
@@ -1447,38 +1518,30 @@ export function ProxyViewer({
|
|
|
1447
1518
|
);
|
|
1448
1519
|
|
|
1449
1520
|
return (
|
|
1450
|
-
<div className="min-h-screen w-full
|
|
1521
|
+
<div className="bg-background min-h-screen w-full text-foreground">
|
|
1451
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)]">
|
|
1452
|
-
<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">
|
|
1453
1524
|
<div className="space-y-4 p-4">
|
|
1454
1525
|
<BrandHeader compact={true} crabEntrancePhase={crabEntrancePhase} />
|
|
1455
1526
|
|
|
1456
|
-
<
|
|
1457
|
-
<div className="
|
|
1458
|
-
<
|
|
1459
|
-
<
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
<div className="min-w-0">
|
|
1468
|
-
<div className="font-mono text-lg font-semibold text-foreground">
|
|
1469
|
-
{formatTokens(hasSessionContext ? sessionContextTotalOut : totalOut)}
|
|
1470
|
-
</div>
|
|
1471
|
-
<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
|
+
/>
|
|
1472
1538
|
</div>
|
|
1473
|
-
</
|
|
1539
|
+
</SidebarPanel>
|
|
1474
1540
|
|
|
1475
|
-
<
|
|
1476
|
-
<div className="
|
|
1477
|
-
Workspace
|
|
1478
|
-
</div>
|
|
1479
|
-
<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">
|
|
1480
1543
|
<div
|
|
1481
|
-
className="font-mono text-xs font-semibold text-
|
|
1544
|
+
className="truncate font-mono text-xs font-semibold text-cyan-100"
|
|
1482
1545
|
title={activeScopeLabel}
|
|
1483
1546
|
>
|
|
1484
1547
|
{activeScopeLabel}
|
|
@@ -1487,7 +1550,7 @@ export function ProxyViewer({
|
|
|
1487
1550
|
{isPinnedSessionPage ? "Session workspace" : "Live workspace"}
|
|
1488
1551
|
</div>
|
|
1489
1552
|
</div>
|
|
1490
|
-
</
|
|
1553
|
+
</SidebarPanel>
|
|
1491
1554
|
|
|
1492
1555
|
{hasSessionContext && sessionContextScope !== null && (
|
|
1493
1556
|
<SessionContextBar
|
|
@@ -1507,44 +1570,43 @@ export function ProxyViewer({
|
|
|
1507
1570
|
<SessionMembershipPanel memberships={sessionMemberships} />
|
|
1508
1571
|
)}
|
|
1509
1572
|
|
|
1510
|
-
<
|
|
1511
|
-
<div className="
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
<
|
|
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" />
|
|
1518
1593
|
</SelectTrigger>
|
|
1519
1594
|
<SelectContent>
|
|
1520
|
-
<SelectItem value="__all__">All
|
|
1521
|
-
{
|
|
1522
|
-
<SelectItem key={
|
|
1523
|
-
{
|
|
1595
|
+
<SelectItem value="__all__">All models</SelectItem>
|
|
1596
|
+
{models.map((m) => (
|
|
1597
|
+
<SelectItem key={m} value={m}>
|
|
1598
|
+
{m}
|
|
1524
1599
|
</SelectItem>
|
|
1525
1600
|
))}
|
|
1526
1601
|
</SelectContent>
|
|
1527
1602
|
</Select>
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
<SelectTrigger className="h-9 w-full text-xs">
|
|
1531
|
-
<SelectValue placeholder="All models" />
|
|
1532
|
-
</SelectTrigger>
|
|
1533
|
-
<SelectContent>
|
|
1534
|
-
<SelectItem value="__all__">All models</SelectItem>
|
|
1535
|
-
{models.map((m) => (
|
|
1536
|
-
<SelectItem key={m} value={m}>
|
|
1537
|
-
{m}
|
|
1538
|
-
</SelectItem>
|
|
1539
|
-
))}
|
|
1540
|
-
</SelectContent>
|
|
1541
|
-
</Select>
|
|
1542
|
-
</div>
|
|
1603
|
+
</div>
|
|
1604
|
+
</SidebarPanel>
|
|
1543
1605
|
</div>
|
|
1544
1606
|
</aside>
|
|
1545
1607
|
|
|
1546
1608
|
<main className="min-w-0">
|
|
1547
|
-
<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">
|
|
1548
1610
|
<div className="flex flex-wrap items-center gap-2">
|
|
1549
1611
|
<div className="min-w-[180px] flex-1">
|
|
1550
1612
|
<div className="text-sm font-semibold text-foreground">Logs</div>
|
|
@@ -1565,7 +1627,7 @@ export function ProxyViewer({
|
|
|
1565
1627
|
type="button"
|
|
1566
1628
|
onClick={handleImportClick}
|
|
1567
1629
|
disabled={importing}
|
|
1568
|
-
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"
|
|
1569
1631
|
title="Import Agent Inspector export"
|
|
1570
1632
|
>
|
|
1571
1633
|
{importing ? (
|
|
@@ -1586,7 +1648,7 @@ export function ProxyViewer({
|
|
|
1586
1648
|
void handleExport("redacted");
|
|
1587
1649
|
}}
|
|
1588
1650
|
disabled={exporting !== null}
|
|
1589
|
-
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"
|
|
1590
1652
|
title="Export redacted logs as JSON ZIP"
|
|
1591
1653
|
>
|
|
1592
1654
|
{exporting === "redacted" ? (
|
|
@@ -1606,7 +1668,7 @@ export function ProxyViewer({
|
|
|
1606
1668
|
void handleExport("raw");
|
|
1607
1669
|
}}
|
|
1608
1670
|
disabled={exporting !== null}
|
|
1609
|
-
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"
|
|
1610
1672
|
title="Export raw logs without redaction"
|
|
1611
1673
|
>
|
|
1612
1674
|
{exporting === "raw" ? (
|
|
@@ -1622,7 +1684,7 @@ export function ProxyViewer({
|
|
|
1622
1684
|
<button
|
|
1623
1685
|
type="button"
|
|
1624
1686
|
onClick={onClearAll}
|
|
1625
|
-
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"
|
|
1626
1688
|
title="Clear all logs"
|
|
1627
1689
|
>
|
|
1628
1690
|
<Trash2 className="size-3.5" />
|
|
@@ -1630,18 +1692,18 @@ export function ProxyViewer({
|
|
|
1630
1692
|
</button>
|
|
1631
1693
|
</div>
|
|
1632
1694
|
<LogPaginationBar logs={logs} pagination={pagination} embedded={true} />
|
|
1633
|
-
{exportError !== null && (
|
|
1695
|
+
{onNotify === undefined && exportError !== null && (
|
|
1634
1696
|
<div className="mt-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive">
|
|
1635
1697
|
{exportError}
|
|
1636
1698
|
</div>
|
|
1637
1699
|
)}
|
|
1638
|
-
{importError !== null && (
|
|
1700
|
+
{onNotify === undefined && importError !== null && (
|
|
1639
1701
|
<div className="mt-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive">
|
|
1640
1702
|
{importError}
|
|
1641
1703
|
</div>
|
|
1642
1704
|
)}
|
|
1643
|
-
{importStatus !== null && importError === null && (
|
|
1644
|
-
<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">
|
|
1645
1707
|
{importStatus}
|
|
1646
1708
|
</div>
|
|
1647
1709
|
)}
|
|
@@ -1652,7 +1714,7 @@ export function ProxyViewer({
|
|
|
1652
1714
|
{logs.length === 0 ? (
|
|
1653
1715
|
selectedSession !== "__all__" ? (
|
|
1654
1716
|
<div className="py-16 text-center text-muted-foreground">
|
|
1655
|
-
<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">
|
|
1656
1718
|
<p className="text-sm font-medium">
|
|
1657
1719
|
{isLoading ? "Loading session data..." : "Session not found"}
|
|
1658
1720
|
</p>
|
|
@@ -1686,7 +1748,7 @@ export function ProxyViewer({
|
|
|
1686
1748
|
</div>
|
|
1687
1749
|
) : (
|
|
1688
1750
|
<div className="py-16 text-center text-muted-foreground">
|
|
1689
|
-
<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">
|
|
1690
1752
|
<p className="text-sm">No requests captured yet.</p>
|
|
1691
1753
|
<div className="flex flex-col items-center gap-2">
|
|
1692
1754
|
<CopyableCommand command="ANTHROPIC_BASE_URL=http://localhost:25947/proxy <your-tool>" />
|
|
@@ -1716,8 +1778,7 @@ export function ProxyViewer({
|
|
|
1716
1778
|
onCompareWithPrevious={handleCompareWithPrevious}
|
|
1717
1779
|
comparisonPredecessors={comparisonPredecessors}
|
|
1718
1780
|
onClearGroup={onClearGroup}
|
|
1719
|
-
|
|
1720
|
-
hasPinnedSessionContext={hasSessionContext}
|
|
1781
|
+
defaultExpanded={groups.length === 1}
|
|
1721
1782
|
timeDisplayFormat={timeDisplayFormat}
|
|
1722
1783
|
/>
|
|
1723
1784
|
</div>
|
|
@@ -1733,7 +1794,13 @@ export function ProxyViewer({
|
|
|
1733
1794
|
{/* Compare drawer: sibling of the log list, not a route change. */}
|
|
1734
1795
|
{comparePair !== null && (
|
|
1735
1796
|
<Suspense fallback={null}>
|
|
1736
|
-
<LazyCompareDrawer
|
|
1797
|
+
<LazyCompareDrawer
|
|
1798
|
+
left={comparePair[0]}
|
|
1799
|
+
right={comparePair[1]}
|
|
1800
|
+
leftDisplayNumber={displayNumberForLog(comparePair[0], displayNumberByLogId)}
|
|
1801
|
+
rightDisplayNumber={displayNumberForLog(comparePair[1], displayNumberByLogId)}
|
|
1802
|
+
onClose={closeCompare}
|
|
1803
|
+
/>
|
|
1737
1804
|
</Suspense>
|
|
1738
1805
|
)}
|
|
1739
1806
|
</div>
|