@tonyclaw/agent-inspector 2.1.13 → 2.1.15
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 +1 -0
- package/.output/public/assets/ProxyViewerContainer-h851qWNp.js +106 -0
- package/.output/public/assets/ReplayDialog-BNpC0548.js +1 -0
- package/.output/public/assets/{RequestAnatomy-BjlBT-Cy.js → RequestAnatomy-Ds1uRLVB.js} +1 -1
- package/.output/public/assets/ResponseView-7KPVqKl5.js +3 -0
- package/.output/public/assets/{StreamingChunkSequence-D2_SMhlE.js → StreamingChunkSequence-BHQT261s.js} +1 -1
- package/.output/public/assets/_sessionId-DWePGjnS.js +1 -0
- package/.output/public/assets/index-CI1-G8ua.js +1 -0
- package/.output/public/assets/index-DdhFqPsI.css +1 -0
- package/.output/public/assets/index-DjKt8XKe.js +14 -0
- package/.output/public/assets/json-viewer-CkCu-rka.js +1 -0
- package/.output/public/assets/{main-Dtspb4Ui.js → main-DpD1N0S8.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +194 -200
- package/.output/server/_libs/react-markdown.mjs +90 -2
- package/.output/server/{_sessionId-CXDcLuvi.mjs → _sessionId-DF9Sy8cP.mjs} +6 -57
- package/.output/server/_ssr/{CompareDrawer-CMoCAoeq.mjs → CompareDrawer-BoxztaO7.mjs} +50 -74
- package/.output/server/_ssr/{ProxyViewerContainer-BW2vVCBN.mjs → ProxyViewerContainer-CRBkqFlJ.mjs} +1221 -799
- package/.output/server/_ssr/{ReplayDialog-ChXL1t8H.mjs → ReplayDialog-Cc1dyDuK.mjs} +18 -62
- package/.output/server/_ssr/{RequestAnatomy-DtKzIlfU.mjs → RequestAnatomy-CMGSsz5Z.mjs} +6 -57
- package/.output/server/_ssr/{ResponseView-B5I8drzc.mjs → ResponseView-Cp10DM1D.mjs} +139 -61
- package/.output/server/_ssr/{StreamingChunkSequence-DvwjQNcO.mjs → StreamingChunkSequence-B1VGxy3A.mjs} +13 -58
- package/.output/server/_ssr/{index-D_ZHtRfl.mjs → index-47XVPghS.mjs} +6 -57
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/json-viewer-zDE2rrmJ.mjs +478 -0
- package/.output/server/_ssr/{router-DhL9Wp3N.mjs → router-DVeuZFqI.mjs} +680 -174
- package/.output/server/{_tanstack-start-manifest_v-BHgoAmxZ.mjs → _tanstack-start-manifest_v-Bp8JxtPW.mjs} +1 -1
- package/.output/server/index.mjs +72 -58
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +28 -2
- package/src/components/ProxyViewerContainer.tsx +25 -17
- package/src/components/clients/ClientLogo.tsx +132 -0
- package/src/components/groups/GroupsDialog.tsx +18 -13
- package/src/components/providers/ImportWizardDialog.tsx +7 -1
- package/src/components/providers/ProviderCard.tsx +10 -1
- package/src/components/providers/ProviderForm.tsx +157 -41
- package/src/components/providers/ProvidersPanel.tsx +5 -1
- package/src/components/proxy-viewer/AgentTraceSummary.tsx +56 -23
- package/src/components/proxy-viewer/AnswerMarkdown.tsx +8 -3
- package/src/components/proxy-viewer/CompareDrawer.tsx +58 -14
- package/src/components/proxy-viewer/ConversationGroup.tsx +11 -15
- package/src/components/proxy-viewer/ConversationHeader.tsx +33 -41
- package/src/components/proxy-viewer/LogEntry.tsx +41 -12
- package/src/components/proxy-viewer/LogEntryHeader.tsx +122 -51
- 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 +19 -13
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +9 -3
- package/src/components/proxy-viewer/ThreadConnector.tsx +18 -7
- package/src/components/proxy-viewer/TurnGroup.tsx +67 -32
- package/src/components/proxy-viewer/TurnGroupList.tsx +153 -0
- package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +74 -2
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +13 -7
- package/src/components/proxy-viewer/formats/index.tsx +10 -3
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +185 -17
- package/src/components/proxy-viewer/log-formats/openai.ts +107 -15
- package/src/components/proxy-viewer/logFocus.ts +15 -2
- package/src/components/proxy-viewer/requestTools.ts +7 -3
- package/src/components/proxy-viewer/viewerState.ts +77 -5
- package/src/components/ui/json-viewer-bulk.ts +41 -6
- package/src/components/ui/json-viewer.tsx +9 -8
- package/src/contracts/index.ts +15 -1
- package/src/contracts/openai.ts +94 -0
- package/src/lib/providerContract.ts +1 -0
- package/src/lib/providerModelMetadata.ts +7 -1
- package/src/lib/sessionInfoContract.ts +1 -0
- package/src/lib/stopReason.ts +59 -15
- package/src/lib/upstreamUrl.ts +46 -0
- package/src/mcp/previewExtractor.ts +73 -3
- package/src/mcp/server.ts +2 -0
- package/src/mcp/toolHandlers.ts +2 -0
- package/src/proxy/constants.ts +2 -0
- package/src/proxy/formats/openai/handler.ts +40 -9
- package/src/proxy/formats/openai/index.ts +7 -0
- package/src/proxy/formats/openai/schemas.ts +15 -1
- package/src/proxy/formats/openai/stream.ts +529 -135
- package/src/proxy/formats/registry.ts +9 -1
- package/src/proxy/providerImporters.ts +45 -5
- package/src/proxy/providers.ts +31 -11
- package/src/proxy/schemas.ts +6 -0
- package/src/proxy/sessionInfo.ts +5 -1
- package/src/proxy/toolSchemaWarnings.ts +25 -3
- package/src/proxy/upstream.ts +18 -37
- package/src/routes/api/providers.$providerId.ts +1 -0
- package/src/routes/api/providers.ts +2 -0
- package/.output/public/assets/CompareDrawer-Cz_1vIpR.js +0 -1
- package/.output/public/assets/ProxyViewerContainer-7QSiluMf.js +0 -117
- package/.output/public/assets/ReplayDialog-sBA1KAYD.js +0 -1
- package/.output/public/assets/ResponseView-PtEKzml9.js +0 -1
- package/.output/public/assets/_sessionId-DZfB4ruK.js +0 -1
- package/.output/public/assets/index-B-QQLbpz.js +0 -1
- package/.output/public/assets/index-CmtfjQPv.css +0 -1
|
@@ -32,17 +32,51 @@ import { Badge } from "../ui/badge";
|
|
|
32
32
|
import { Button } from "../ui/button";
|
|
33
33
|
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
|
|
34
34
|
import type { CacheTrend } from "./cacheTrend";
|
|
35
|
+
import { ROW_CHEVRON_SLOT_CLASS } from "./ProviderLogoStack";
|
|
35
36
|
|
|
36
37
|
const STATUS_BADGE_CLASSES: Record<StatusCategory, string> = {
|
|
37
|
-
success: "bg-emerald-
|
|
38
|
-
client_error: "bg-amber-
|
|
39
|
-
server_error: "bg-rose-
|
|
40
|
-
pending: "bg-
|
|
38
|
+
success: "bg-emerald-400/[0.08] text-emerald-200 border-emerald-300/25",
|
|
39
|
+
client_error: "bg-amber-300/[0.10] text-amber-100 border-amber-300/30",
|
|
40
|
+
server_error: "bg-rose-400/[0.10] text-rose-100 border-rose-300/30",
|
|
41
|
+
pending: "bg-cyan-300/[0.08] text-cyan-100 border-cyan-300/25",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const STATUS_CUE_CLASSES: Record<
|
|
45
|
+
StatusCategory,
|
|
46
|
+
{
|
|
47
|
+
frame: string;
|
|
48
|
+
dot: string;
|
|
49
|
+
rail: string;
|
|
50
|
+
}
|
|
51
|
+
> = {
|
|
52
|
+
success: {
|
|
53
|
+
frame: "border-emerald-300/20 bg-emerald-300/[0.06] text-emerald-100",
|
|
54
|
+
dot: "bg-emerald-300 shadow-[0_0_14px_rgba(110,231,183,0.8)]",
|
|
55
|
+
rail: "bg-emerald-300/45 shadow-[0_0_22px_rgba(110,231,183,0.28)]",
|
|
56
|
+
},
|
|
57
|
+
client_error: {
|
|
58
|
+
frame: "border-amber-300/25 bg-amber-300/[0.08] text-amber-100",
|
|
59
|
+
dot: "bg-amber-200 shadow-[0_0_14px_rgba(252,211,77,0.8)]",
|
|
60
|
+
rail: "bg-amber-300/50 shadow-[0_0_22px_rgba(252,211,77,0.28)]",
|
|
61
|
+
},
|
|
62
|
+
server_error: {
|
|
63
|
+
frame: "border-rose-300/25 bg-rose-300/[0.08] text-rose-100",
|
|
64
|
+
dot: "bg-rose-300 shadow-[0_0_14px_rgba(253,164,175,0.8)]",
|
|
65
|
+
rail: "bg-rose-300/50 shadow-[0_0_22px_rgba(253,164,175,0.28)]",
|
|
66
|
+
},
|
|
67
|
+
pending: {
|
|
68
|
+
frame: "border-cyan-300/25 bg-cyan-300/[0.08] text-cyan-100",
|
|
69
|
+
dot: "bg-cyan-200 shadow-[0_0_14px_rgba(103,232,249,0.8)]",
|
|
70
|
+
rail: "bg-cyan-300/50 shadow-[0_0_22px_rgba(103,232,249,0.28)]",
|
|
71
|
+
},
|
|
41
72
|
};
|
|
42
73
|
|
|
43
74
|
const HEADER_ACTION_BUTTON_CLASS =
|
|
44
75
|
"size-8 border-white/10 bg-[#0a0d11] text-muted-foreground shadow-none hover:border-cyan-300/30 hover:bg-cyan-400/10 hover:text-cyan-100";
|
|
45
76
|
|
|
77
|
+
const TELEMETRY_CHIP_CLASS =
|
|
78
|
+
"flex h-7 items-center gap-1 rounded-md border border-white/10 bg-black/20 px-2 text-xs shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]";
|
|
79
|
+
|
|
46
80
|
function formatElapsed(ms: number): string {
|
|
47
81
|
if (ms < 1000) return `${ms}ms`;
|
|
48
82
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
@@ -53,6 +87,19 @@ function formatTokenRate(tokensPerSecond: number): string {
|
|
|
53
87
|
return `${tokensPerSecond.toFixed(1)}/s`;
|
|
54
88
|
}
|
|
55
89
|
|
|
90
|
+
function statusCueLabel(category: StatusCategory, status: number | null): string {
|
|
91
|
+
switch (category) {
|
|
92
|
+
case "success":
|
|
93
|
+
return status === null ? "OK" : String(status);
|
|
94
|
+
case "client_error":
|
|
95
|
+
return status === null ? "WARN" : String(status);
|
|
96
|
+
case "server_error":
|
|
97
|
+
return status === null ? "FAIL" : String(status);
|
|
98
|
+
case "pending":
|
|
99
|
+
return "ROLL";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
56
103
|
/**
|
|
57
104
|
* Inline trend indicator: small arrow (green up / red down) plus the absolute
|
|
58
105
|
* delta in compact form. Returns `null` when there is no trend to display.
|
|
@@ -109,6 +156,8 @@ export type HeaderTabActions = Record<string, HeaderTabAction | undefined>;
|
|
|
109
156
|
|
|
110
157
|
export type LogEntryHeaderProps = {
|
|
111
158
|
log: CapturedLog;
|
|
159
|
+
/** Human-facing ordinal inside the current session. Internal actions still use `log.id`. */
|
|
160
|
+
displayNumber: number;
|
|
112
161
|
/** Number of messages in the request (supports both Anthropic and OpenAI formats). */
|
|
113
162
|
messageCount?: number | null;
|
|
114
163
|
/** Number of tools defined in the request (supports both Anthropic and OpenAI formats). */
|
|
@@ -138,6 +187,7 @@ export type LogEntryHeaderProps = {
|
|
|
138
187
|
|
|
139
188
|
export const LogEntryHeader = memo(function ({
|
|
140
189
|
log,
|
|
190
|
+
displayNumber,
|
|
141
191
|
messageCount = null,
|
|
142
192
|
toolCount = null,
|
|
143
193
|
expanded,
|
|
@@ -169,6 +219,8 @@ export const LogEntryHeader = memo(function ({
|
|
|
169
219
|
log.clientProjectFolder !== "";
|
|
170
220
|
const hasClientCwd =
|
|
171
221
|
log.clientCwd !== null && log.clientCwd !== undefined && log.clientCwd !== "";
|
|
222
|
+
const statusCue = STATUS_CUE_CLASSES[statusCategory];
|
|
223
|
+
const statusLabel = statusCueLabel(statusCategory, log.responseStatus);
|
|
172
224
|
|
|
173
225
|
return (
|
|
174
226
|
<TooltipProvider>
|
|
@@ -179,9 +231,11 @@ export const LogEntryHeader = memo(function ({
|
|
|
179
231
|
data-nav-id={`log-${log.id}`}
|
|
180
232
|
data-nav-action={expanded ? "collapse" : "expand"}
|
|
181
233
|
className={cn(
|
|
182
|
-
"flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 px-3 py-
|
|
183
|
-
expanded
|
|
184
|
-
|
|
234
|
+
"group/header relative isolate flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 overflow-hidden px-3 py-2 cursor-pointer transition-[background-color,border-color,box-shadow]",
|
|
235
|
+
expanded
|
|
236
|
+
? "bg-[linear-gradient(90deg,#0f1720_0%,#090d12_50%,#07090d_100%)] shadow-[inset_0_1px_0_rgba(255,255,255,0.06)]"
|
|
237
|
+
: "bg-[#080b10] hover:bg-[#0b1016]",
|
|
238
|
+
"select-none before:pointer-events-none before:absolute before:inset-x-0 before:top-0 before:h-px before:bg-white/10",
|
|
185
239
|
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none",
|
|
186
240
|
)}
|
|
187
241
|
onClick={onToggle}
|
|
@@ -192,51 +246,70 @@ export const LogEntryHeader = memo(function ({
|
|
|
192
246
|
}
|
|
193
247
|
}}
|
|
194
248
|
>
|
|
249
|
+
<span
|
|
250
|
+
className={cn(
|
|
251
|
+
"absolute left-0 top-2 bottom-2 w-0.5 rounded-r-full transition-colors",
|
|
252
|
+
statusCue.rail,
|
|
253
|
+
)}
|
|
254
|
+
aria-hidden="true"
|
|
255
|
+
/>
|
|
256
|
+
<span className={ROW_CHEVRON_SLOT_CLASS}>
|
|
257
|
+
{expanded ? (
|
|
258
|
+
<ChevronDown className="size-4 shrink-0" />
|
|
259
|
+
) : (
|
|
260
|
+
<ChevronRight className="size-4 shrink-0" />
|
|
261
|
+
)}
|
|
262
|
+
</span>
|
|
263
|
+
|
|
195
264
|
{/* Request ID */}
|
|
196
|
-
<span
|
|
197
|
-
|
|
265
|
+
<span
|
|
266
|
+
className={cn(
|
|
267
|
+
"inline-flex h-8 shrink-0 items-center gap-2 rounded-md border px-2 font-mono shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]",
|
|
268
|
+
statusCue.frame,
|
|
269
|
+
)}
|
|
270
|
+
>
|
|
271
|
+
<span className={cn("size-1.5 rounded-full", statusCue.dot)} aria-hidden="true" />
|
|
272
|
+
<span className="text-xs font-semibold tabular-nums" title={`Log ID ${String(log.id)}`}>
|
|
273
|
+
#{displayNumber}
|
|
274
|
+
</span>
|
|
198
275
|
</span>
|
|
199
276
|
|
|
200
277
|
{/* Request start time */}
|
|
201
|
-
<span className="flex items-center gap-1 text-muted-foreground text-xs shrink-0">
|
|
278
|
+
<span className="flex h-7 items-center gap-1 rounded-md border border-white/10 bg-black/20 px-2 text-muted-foreground text-xs shrink-0">
|
|
202
279
|
<Clock className="size-3" />
|
|
203
280
|
<span className="font-mono tabular-nums" title={log.timestamp}>
|
|
204
281
|
{formatTimestamp(log.timestamp, timeDisplayFormat)}
|
|
205
282
|
</span>
|
|
206
283
|
</span>
|
|
207
284
|
|
|
208
|
-
{/* Model logo moved to the session/conversation header — per-log rows
|
|
209
|
-
no longer render the logo (sessions with many logs would show the
|
|
210
|
-
same provider logo dozens of times). */}
|
|
211
|
-
|
|
212
285
|
{/* Response Status — only shown for non-200 or pending. Each category
|
|
213
286
|
carries a distinct icon in addition to color so the meaning is
|
|
214
287
|
legible without color perception. */}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
288
|
+
<Badge
|
|
289
|
+
variant="outline"
|
|
290
|
+
className={cn(
|
|
291
|
+
"h-6 gap-1 rounded-md px-2 py-0 font-mono text-[10px] font-semibold uppercase tabular-nums shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]",
|
|
292
|
+
STATUS_BADGE_CLASSES[statusCategory],
|
|
293
|
+
)}
|
|
294
|
+
>
|
|
295
|
+
{statusCategory === "success" ? (
|
|
296
|
+
<Check className="size-3" aria-label="Success" />
|
|
297
|
+
) : statusCategory === "server_error" ? (
|
|
298
|
+
<OctagonAlert className="size-3" aria-label="Server error" />
|
|
299
|
+
) : statusCategory === "client_error" ? (
|
|
300
|
+
<AlertTriangle className="size-3" aria-label="Client error" />
|
|
301
|
+
) : (
|
|
302
|
+
<Loader2 className="size-3 animate-spin" aria-label="Pending" />
|
|
303
|
+
)}
|
|
304
|
+
{statusLabel}
|
|
305
|
+
</Badge>
|
|
233
306
|
|
|
234
307
|
{warningCount > 0 && (
|
|
235
308
|
<Tooltip>
|
|
236
309
|
<TooltipTrigger asChild>
|
|
237
310
|
<Badge
|
|
238
311
|
variant="outline"
|
|
239
|
-
className="
|
|
312
|
+
className="h-6 gap-1 rounded-md border-amber-300/30 bg-amber-300/[0.10] px-2 py-0 font-mono text-[10px] tabular-nums text-amber-100"
|
|
240
313
|
>
|
|
241
314
|
<AlertTriangle className="size-3" aria-label="Tool schema warnings" />
|
|
242
315
|
{warningCount}
|
|
@@ -258,7 +331,12 @@ export const LogEntryHeader = memo(function ({
|
|
|
258
331
|
(isSlowResponse ? (
|
|
259
332
|
<Tooltip>
|
|
260
333
|
<TooltipTrigger asChild>
|
|
261
|
-
<span
|
|
334
|
+
<span
|
|
335
|
+
className={cn(
|
|
336
|
+
TELEMETRY_CHIP_CLASS,
|
|
337
|
+
"shrink-0 border-amber-300/25 text-amber-200",
|
|
338
|
+
)}
|
|
339
|
+
>
|
|
262
340
|
<Clock className="size-3" />
|
|
263
341
|
<span className="font-mono tabular-nums">{formatElapsed(log.elapsedMs)}</span>
|
|
264
342
|
<AlertTriangle className="size-3" aria-label="Slow response" />
|
|
@@ -270,7 +348,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
270
348
|
</TooltipContent>
|
|
271
349
|
</Tooltip>
|
|
272
350
|
) : (
|
|
273
|
-
<span className="
|
|
351
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground")}>
|
|
274
352
|
<Clock className="size-3" />
|
|
275
353
|
<span className="font-mono tabular-nums">{formatElapsed(log.elapsedMs)}</span>
|
|
276
354
|
</span>
|
|
@@ -280,7 +358,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
280
358
|
{log.streaming && firstChunkLabel !== null && (
|
|
281
359
|
<Tooltip>
|
|
282
360
|
<TooltipTrigger asChild>
|
|
283
|
-
<span className="
|
|
361
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-cyan-300")}>
|
|
284
362
|
<Radio className="size-3" />
|
|
285
363
|
<span className="font-mono tabular-nums">first {firstChunkLabel}</span>
|
|
286
364
|
</span>
|
|
@@ -291,7 +369,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
291
369
|
{log.streaming && tokenRateLabel !== null && (
|
|
292
370
|
<Tooltip>
|
|
293
371
|
<TooltipTrigger asChild>
|
|
294
|
-
<span className="
|
|
372
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-emerald-300")}>
|
|
295
373
|
<Gauge className="size-3" />
|
|
296
374
|
<span className="font-mono tabular-nums">{tokenRateLabel}</span>
|
|
297
375
|
</span>
|
|
@@ -302,7 +380,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
302
380
|
|
|
303
381
|
{/* Token counts */}
|
|
304
382
|
{hasTokens && (
|
|
305
|
-
<span className="
|
|
383
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "min-w-0 sm:shrink-0")}>
|
|
306
384
|
<Zap className="size-3 text-muted-foreground" />
|
|
307
385
|
<span className="flex min-w-0 flex-wrap items-center gap-x-1 font-mono tabular-nums">
|
|
308
386
|
<span
|
|
@@ -327,7 +405,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
327
405
|
)}
|
|
328
406
|
{/* Cache tokens */}
|
|
329
407
|
{log.cacheCreationInputTokens !== null && log.cacheCreationInputTokens > 0 && (
|
|
330
|
-
<span className="
|
|
408
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0")}>
|
|
331
409
|
<CacheTrendIndicator trend={cacheTrend?.creation ?? null} />
|
|
332
410
|
<span className="font-mono tabular-nums text-emerald-400">
|
|
333
411
|
KV Cache +{formatTokens(log.cacheCreationInputTokens)}
|
|
@@ -335,7 +413,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
335
413
|
</span>
|
|
336
414
|
)}
|
|
337
415
|
{log.cacheReadInputTokens !== null && log.cacheReadInputTokens > 0 && (
|
|
338
|
-
<span className="
|
|
416
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0")}>
|
|
339
417
|
<CacheTrendIndicator trend={cacheTrend?.read ?? null} />
|
|
340
418
|
<span className="font-mono tabular-nums text-purple-400">
|
|
341
419
|
KV Cache ~{formatTokens(log.cacheReadInputTokens)}
|
|
@@ -345,7 +423,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
345
423
|
|
|
346
424
|
{/* Message count */}
|
|
347
425
|
{messageCount !== null && (
|
|
348
|
-
<span className="
|
|
426
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground")}>
|
|
349
427
|
<MessageSquare className="size-3" />
|
|
350
428
|
<span className="font-mono tabular-nums">{messageCount}</span>
|
|
351
429
|
</span>
|
|
@@ -353,7 +431,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
353
431
|
|
|
354
432
|
{/* Tool count */}
|
|
355
433
|
{toolCount !== null && (
|
|
356
|
-
<span className="
|
|
434
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground")}>
|
|
357
435
|
<Wrench className="size-3" />
|
|
358
436
|
<span className="font-mono tabular-nums">{toolCount}</span>
|
|
359
437
|
</span>
|
|
@@ -362,7 +440,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
362
440
|
{/* Origin */}
|
|
363
441
|
{log.origin !== null && (
|
|
364
442
|
<span
|
|
365
|
-
className="hidden
|
|
443
|
+
className={cn(TELEMETRY_CHIP_CLASS, "hidden shrink-0 text-muted-foreground xl:flex")}
|
|
366
444
|
title={`Origin: ${log.origin}`}
|
|
367
445
|
>
|
|
368
446
|
<Globe className="size-3" />
|
|
@@ -376,7 +454,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
376
454
|
{(hasClientPid || hasClientProjectFolder) && (
|
|
377
455
|
<Tooltip>
|
|
378
456
|
<TooltipTrigger asChild>
|
|
379
|
-
<span className="hidden
|
|
457
|
+
<span className={cn(TELEMETRY_CHIP_CLASS, "hidden shrink-0 text-purple-300 xl:flex")}>
|
|
380
458
|
<FileTerminal className="size-3" />
|
|
381
459
|
{hasClientProjectFolder ? (
|
|
382
460
|
<span className="font-mono tabular-nums">{log.clientProjectFolder}</span>
|
|
@@ -537,13 +615,6 @@ export const LogEntryHeader = memo(function ({
|
|
|
537
615
|
)}
|
|
538
616
|
</span>
|
|
539
617
|
)}
|
|
540
|
-
|
|
541
|
-
{/* Expand chevron */}
|
|
542
|
-
{expanded ? (
|
|
543
|
-
<ChevronDown className="size-4 text-muted-foreground shrink-0" />
|
|
544
|
-
) : (
|
|
545
|
-
<ChevronRight className="size-4 text-muted-foreground shrink-0" />
|
|
546
|
-
)}
|
|
547
618
|
</div>
|
|
548
619
|
</TooltipProvider>
|
|
549
620
|
);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { JSX } from "react";
|
|
2
|
+
import { cn } from "../../lib/utils";
|
|
3
|
+
import { ProviderLogo, type Provider } from "../providers/ProviderLogo";
|
|
4
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/tooltip";
|
|
5
|
+
|
|
6
|
+
const MAX_VISIBLE_LOGOS = 3;
|
|
7
|
+
|
|
8
|
+
export const ROW_CHEVRON_SLOT_CLASS =
|
|
9
|
+
"flex h-8 w-5 shrink-0 items-center justify-center text-muted-foreground";
|
|
10
|
+
|
|
11
|
+
export type ProviderLogoStackItem = {
|
|
12
|
+
provider: Provider;
|
|
13
|
+
tooltip: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function ProviderLogoStack({
|
|
17
|
+
items,
|
|
18
|
+
ariaLabel,
|
|
19
|
+
className,
|
|
20
|
+
}: {
|
|
21
|
+
items: readonly ProviderLogoStackItem[];
|
|
22
|
+
ariaLabel: string;
|
|
23
|
+
className?: string;
|
|
24
|
+
}): JSX.Element {
|
|
25
|
+
if (items.length === 0) {
|
|
26
|
+
return (
|
|
27
|
+
<span className={cn("flex h-8 w-12 shrink-0 items-center", className)} aria-hidden="true" />
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const visibleItems = items.slice(0, MAX_VISIBLE_LOGOS);
|
|
32
|
+
const overflowCount = items.length - visibleItems.length;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<TooltipProvider delayDuration={150}>
|
|
36
|
+
<span className={cn("flex h-8 w-12 shrink-0 items-center", className)} aria-label={ariaLabel}>
|
|
37
|
+
{visibleItems.map((item, index) => (
|
|
38
|
+
<Tooltip key={`${item.provider}-${String(index)}`}>
|
|
39
|
+
<TooltipTrigger asChild>
|
|
40
|
+
<span
|
|
41
|
+
className={cn(
|
|
42
|
+
"inline-flex size-6 shrink-0 items-center justify-center rounded-md border border-white/10 bg-black/20 shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]",
|
|
43
|
+
index > 0 && "-ml-2",
|
|
44
|
+
)}
|
|
45
|
+
>
|
|
46
|
+
<ProviderLogo provider={item.provider} className="size-4" />
|
|
47
|
+
</span>
|
|
48
|
+
</TooltipTrigger>
|
|
49
|
+
<TooltipContent>{item.tooltip}</TooltipContent>
|
|
50
|
+
</Tooltip>
|
|
51
|
+
))}
|
|
52
|
+
{overflowCount > 0 && (
|
|
53
|
+
<span className="-ml-2 inline-flex h-6 min-w-6 shrink-0 items-center justify-center rounded-md border border-white/10 bg-black/40 px-1 font-mono text-[10px] text-muted-foreground">
|
|
54
|
+
+{overflowCount}
|
|
55
|
+
</span>
|
|
56
|
+
)}
|
|
57
|
+
</span>
|
|
58
|
+
</TooltipProvider>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -35,6 +35,7 @@ type ReplayResult = z.infer<typeof ReplayResultSchema>;
|
|
|
35
35
|
|
|
36
36
|
type ReplayDialogProps = {
|
|
37
37
|
log: CapturedLog;
|
|
38
|
+
displayNumber: number;
|
|
38
39
|
open: boolean;
|
|
39
40
|
onOpenChange: (open: boolean) => void;
|
|
40
41
|
};
|
|
@@ -159,7 +160,12 @@ function ReplayComparisonTable({
|
|
|
159
160
|
);
|
|
160
161
|
}
|
|
161
162
|
|
|
162
|
-
export function ReplayDialog({
|
|
163
|
+
export function ReplayDialog({
|
|
164
|
+
log,
|
|
165
|
+
displayNumber,
|
|
166
|
+
open,
|
|
167
|
+
onOpenChange,
|
|
168
|
+
}: ReplayDialogProps): JSX.Element {
|
|
163
169
|
const { providers } = useProviders();
|
|
164
170
|
const originalBody = log.rawRequestBody ?? "{}";
|
|
165
171
|
const [modifiedBody, setModifiedBody] = useState<string>(() => {
|
|
@@ -310,7 +316,7 @@ export function ReplayDialog({ log, open, onOpenChange }: ReplayDialogProps): JS
|
|
|
310
316
|
<DialogHeader>
|
|
311
317
|
<DialogTitle className="flex items-center gap-2">
|
|
312
318
|
<RotateCcw className="size-4" />
|
|
313
|
-
|
|
319
|
+
<span title={`Log ID ${String(log.id)}`}>Replay Request #{displayNumber}</span>
|
|
314
320
|
</DialogTitle>
|
|
315
321
|
</DialogHeader>
|
|
316
322
|
|
|
@@ -13,12 +13,11 @@ import {
|
|
|
13
13
|
Terminal,
|
|
14
14
|
Wrench,
|
|
15
15
|
} from "lucide-react";
|
|
16
|
-
import { memo, useMemo, useState, type JSX } from "react";
|
|
16
|
+
import { memo, Suspense, useMemo, useState, type JSX } from "react";
|
|
17
17
|
import { cn } from "../../lib/utils";
|
|
18
18
|
import { Badge } from "../ui/badge";
|
|
19
19
|
import { Button } from "../ui/button";
|
|
20
20
|
import { Collapsible, CollapsibleContent } from "../ui/collapsible";
|
|
21
|
-
import { JsonViewer } from "../ui/json-viewer";
|
|
22
21
|
import { safeJsonValue } from "../ui/json-viewer-bulk";
|
|
23
22
|
import { ScrollArea } from "../ui/scroll-area";
|
|
24
23
|
import type {
|
|
@@ -26,6 +25,7 @@ import type {
|
|
|
26
25
|
RequestToolDefinition,
|
|
27
26
|
RequestToolsSummary,
|
|
28
27
|
} from "./requestTools";
|
|
28
|
+
import { LazyJsonViewer } from "./lazy";
|
|
29
29
|
import { useCopyFeedback } from "./useCopyFeedback";
|
|
30
30
|
|
|
31
31
|
type RequestToolsProps = {
|
|
@@ -152,17 +152,23 @@ function ToolRow({ tool, called }: { tool: RequestToolDefinition; called: boolea
|
|
|
152
152
|
</button>
|
|
153
153
|
</div>
|
|
154
154
|
<CollapsibleContent>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
<
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
155
|
+
{open && (
|
|
156
|
+
<div className="space-y-2 border-t border-white/10 bg-white/[0.02] px-2.5 py-2">
|
|
157
|
+
{tool.description !== null && (
|
|
158
|
+
<p className="text-xs leading-relaxed text-muted-foreground">{tool.description}</p>
|
|
159
|
+
)}
|
|
160
|
+
<RequiredParameters names={tool.requiredParameters} />
|
|
161
|
+
{tool.schema !== null && (
|
|
162
|
+
<ScrollArea className="max-h-64 rounded-md border border-white/10 bg-[#06080b] p-2">
|
|
163
|
+
<Suspense
|
|
164
|
+
fallback={<div className="text-xs text-muted-foreground">Loading JSON...</div>}
|
|
165
|
+
>
|
|
166
|
+
<LazyJsonViewer data={safeJsonValue(tool.schema)} defaultExpandDepth={0} />
|
|
167
|
+
</Suspense>
|
|
168
|
+
</ScrollArea>
|
|
169
|
+
)}
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
166
172
|
</CollapsibleContent>
|
|
167
173
|
</div>
|
|
168
174
|
</Collapsible>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { memo, useState, useEffect, useMemo, type JSX } from "react";
|
|
1
|
+
import { memo, Suspense, useState, useEffect, useMemo, type JSX } from "react";
|
|
2
2
|
import { ChevronDown, ChevronRight, Loader2 } from "lucide-react";
|
|
3
3
|
import { Badge } from "../ui/badge";
|
|
4
|
-
import { JsonViewer } from "../ui/json-viewer";
|
|
5
4
|
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
|
|
6
5
|
import type { StreamingChunk } from "../../contracts";
|
|
6
|
+
import { LazyJsonViewer } from "./lazy";
|
|
7
7
|
|
|
8
8
|
export type StreamingChunkSequenceProps = {
|
|
9
9
|
logId: number;
|
|
@@ -140,7 +140,13 @@ export const StreamingChunkSequence = memo(function StreamingChunkSequence({
|
|
|
140
140
|
{chunk.type}
|
|
141
141
|
</span>
|
|
142
142
|
</div>
|
|
143
|
-
<
|
|
143
|
+
<Suspense
|
|
144
|
+
fallback={
|
|
145
|
+
<div className="text-xs text-muted-foreground">Loading JSON...</div>
|
|
146
|
+
}
|
|
147
|
+
>
|
|
148
|
+
<LazyJsonViewer data={chunk} defaultExpandDepth={0} showCopy />
|
|
149
|
+
</Suspense>
|
|
144
150
|
</div>
|
|
145
151
|
))}
|
|
146
152
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type JSX, useMemo } from "react";
|
|
2
2
|
import { cn } from "../../lib/utils";
|
|
3
|
-
import type
|
|
3
|
+
import { isTurnBoundary, type StopReason } from "../../lib/stopReason";
|
|
4
4
|
import { getCrabVariant, getInteriorCrabVariant } from "../ui/crab-variants";
|
|
5
5
|
|
|
6
6
|
export type ThreadConnectorProps = {
|
|
@@ -18,10 +18,24 @@ export type ThreadConnectorProps = {
|
|
|
18
18
|
onToggle?: () => void;
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
function boundaryTitle(stopReason: StopReason): string {
|
|
22
|
+
switch (stopReason) {
|
|
23
|
+
case "end_turn":
|
|
24
|
+
return "End of Turn (Anthropic)";
|
|
25
|
+
case "stop":
|
|
26
|
+
return "End of Turn (OpenAI)";
|
|
27
|
+
case "length":
|
|
28
|
+
return "End of Turn (length limit)";
|
|
29
|
+
case "tool_use":
|
|
30
|
+
case null:
|
|
31
|
+
return "End of Turn";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
/**
|
|
22
36
|
* Vertical timeline connector. Top spacer uses a calc() that
|
|
23
37
|
* mixes rem (py-1 + half-line) and px (border) so the crab
|
|
24
|
-
* centre-line matches the LogEntry
|
|
38
|
+
* centre-line matches the LogEntry display number regardless of the
|
|
25
39
|
* root font-size. The bottom spacer is flex-1 so the outgoing
|
|
26
40
|
* line adapts to whatever height the LogEntry occupies.
|
|
27
41
|
*/
|
|
@@ -35,7 +49,7 @@ export function ThreadConnector({
|
|
|
35
49
|
collapsible = false,
|
|
36
50
|
onToggle,
|
|
37
51
|
}: ThreadConnectorProps): JSX.Element {
|
|
38
|
-
const isBoundary = stopReason
|
|
52
|
+
const isBoundary = isTurnBoundary(stopReason);
|
|
39
53
|
const isFusedBoundary = isOnlyEntry && isTurnStart && isBoundary;
|
|
40
54
|
const isRunning = isPending && !isBoundary;
|
|
41
55
|
const Crab = useMemo(() => getCrabVariant(crabIndex), [crabIndex]);
|
|
@@ -84,10 +98,7 @@ export function ThreadConnector({
|
|
|
84
98
|
/>
|
|
85
99
|
</span>
|
|
86
100
|
) : isBoundary ? (
|
|
87
|
-
<span
|
|
88
|
-
title={stopReason === "end_turn" ? "End of Turn (Anthropic)" : "End of Turn (OpenAI)"}
|
|
89
|
-
{...interactiveProps}
|
|
90
|
-
>
|
|
101
|
+
<span title={boundaryTitle(stopReason)} {...interactiveProps}>
|
|
91
102
|
<Crab
|
|
92
103
|
className={cn(
|
|
93
104
|
"size-3.5 text-amber-400",
|