@tonyclaw/agent-inspector 2.1.18 → 2.1.20
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-Fqqw5Lci.js → CompareDrawer-BRCSDHn3.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-v5Lmx35X.js +106 -0
- package/.output/public/assets/{ReplayDialog-hYhz_i6W.js → ReplayDialog-Dkez3vxu.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-CTuWbtwC.js → RequestAnatomy-CHBODGDF.js} +1 -1
- package/.output/public/assets/{ResponseView-CsZ7S5Gv.js → ResponseView-Dk2dJY-B.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-PIbJMotU.js → StreamingChunkSequence-gZpqWwKK.js} +1 -1
- package/.output/public/assets/_sessionId-DziA4ZD4.js +1 -0
- package/.output/public/assets/{index-F7mynwuH.js → index-B1-V3HCD.js} +1 -1
- package/.output/public/assets/index-ChgjUix0.css +1 -0
- package/.output/public/assets/index-DkHcxpXi.js +1 -0
- package/.output/public/assets/{json-viewer-DCXtNmmD.js → json-viewer-CtvzeDPb.js} +1 -1
- package/.output/public/assets/{main-BJZf7_Ib.js → main-CgmDEMGA.js} +2 -2
- package/.output/server/{_sessionId-DjSHDNh8.mjs → _sessionId-CbsSRLSa.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-Dal52IFo.mjs → CompareDrawer-BiSslksi.mjs} +2 -2
- package/.output/server/_ssr/{ProxyViewerContainer-CVqFZGff.mjs → ProxyViewerContainer-DIN2AjhM.mjs} +216 -183
- package/.output/server/_ssr/{ReplayDialog-BOEgdMes.mjs → ReplayDialog-DzoWpTis.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-gch7_vCV.mjs → RequestAnatomy-D2tbgAPn.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-lEWPy-H4.mjs → ResponseView-BFP7zo8Z.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-DnudbJ1O.mjs → StreamingChunkSequence-BfiRZW_k.mjs} +2 -2
- package/.output/server/_ssr/{index-Cza8zDrW.mjs → index-OlcgE4Tl.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-C2lIVEX3.mjs → json-viewer-DmhEr-jg.mjs} +2 -2
- package/.output/server/_ssr/{router-bF9uzd1k.mjs → router-BMdwHLHh.mjs} +80 -21
- package/.output/server/{_tanstack-start-manifest_v-ErvW-mtr.mjs → _tanstack-start-manifest_v-CL4qjwkQ.mjs} +1 -1
- package/.output/server/index.mjs +77 -77
- package/package.json +1 -1
- package/src/components/groups/GroupsDialog.tsx +71 -10
- package/src/components/proxy-viewer/ConversationGroupList.tsx +11 -11
- package/src/components/proxy-viewer/ConversationHeader.tsx +7 -7
- package/src/components/proxy-viewer/LogEntry.tsx +15 -26
- package/src/components/proxy-viewer/LogEntryHeader.tsx +11 -14
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +15 -15
- package/src/components/proxy-viewer/ThreadConnector.tsx +3 -3
- package/src/components/proxy-viewer/ToolTraceEvents.tsx +1 -1
- package/src/components/proxy-viewer/TurnGroup.tsx +10 -14
- package/src/lib/groupContract.ts +6 -0
- package/src/proxy/groupStore.ts +24 -0
- package/src/routes/api/groups.ts +41 -1
- package/src/services/groups.ts +8 -0
- package/.output/public/assets/ProxyViewerContainer-CP_RzwW9.js +0 -106
- package/.output/public/assets/_sessionId-CUGA9qU3.js +0 -1
- package/.output/public/assets/index-G4dTUobw.js +0 -1
- package/.output/public/assets/index-gvYkgMPF.css +0 -1
|
@@ -19,6 +19,7 @@ import { cn } from "../../lib/utils";
|
|
|
19
19
|
import { parseJsonResponse, readApiError } from "../../lib/apiClient";
|
|
20
20
|
import {
|
|
21
21
|
DeleteInspectorGroupResponseSchema,
|
|
22
|
+
DeleteInspectorGroupsResponseSchema,
|
|
22
23
|
GroupEvidenceExportResultSchema,
|
|
23
24
|
type GroupEvidenceReadResponse,
|
|
24
25
|
type GroupEvidenceExportResult,
|
|
@@ -34,6 +35,7 @@ import { ClientLogo, clientAppLabel, detectClientApp } from "../clients/ClientLo
|
|
|
34
35
|
import { useGroupEvidence } from "../../lib/useGroupEvidence";
|
|
35
36
|
import { useGroups } from "../../lib/useGroups";
|
|
36
37
|
import { useCopyFeedback } from "../proxy-viewer/useCopyFeedback";
|
|
38
|
+
import { TransientToast, useTransientToast } from "../ui/transient-toast";
|
|
37
39
|
|
|
38
40
|
type MemberStatus = InspectorGroupMember["status"];
|
|
39
41
|
|
|
@@ -56,6 +58,10 @@ export function groupDeletePath(groupId: string, deleteEvidence: boolean): strin
|
|
|
56
58
|
return deleteEvidence ? `${path}?deleteEvidence=1` : path;
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
export function groupsClearPath(deleteEvidence: boolean): string {
|
|
62
|
+
return deleteEvidence ? "/api/groups?deleteEvidence=1" : "/api/groups";
|
|
63
|
+
}
|
|
64
|
+
|
|
59
65
|
export function summarizeGroup(group: InspectorGroup): GroupSummary {
|
|
60
66
|
let completedMembers = 0;
|
|
61
67
|
let failedMembers = 0;
|
|
@@ -285,6 +291,8 @@ export function GroupsDialog(): JSX.Element {
|
|
|
285
291
|
const [deleteEvidence, setDeleteEvidence] = useState(false);
|
|
286
292
|
const [deletingGroupId, setDeletingGroupId] = useState<string | null>(null);
|
|
287
293
|
const [deleteError, setDeleteError] = useState<string | null>(null);
|
|
294
|
+
const [clearingGroups, setClearingGroups] = useState(false);
|
|
295
|
+
const { notice, showToast, dismissToast } = useTransientToast();
|
|
288
296
|
const { groups, isLoading, error, mutate } = useGroups();
|
|
289
297
|
|
|
290
298
|
const selectedGroup = useMemo(
|
|
@@ -315,6 +323,47 @@ export function GroupsDialog(): JSX.Element {
|
|
|
315
323
|
void mutate();
|
|
316
324
|
}, [mutate]);
|
|
317
325
|
|
|
326
|
+
const handleClearGroups = useCallback(async () => {
|
|
327
|
+
if (groups.length === 0 || clearingGroups) return;
|
|
328
|
+
|
|
329
|
+
setClearingGroups(true);
|
|
330
|
+
setDeleteTarget(null);
|
|
331
|
+
setDeleteEvidence(false);
|
|
332
|
+
setDeleteError(null);
|
|
333
|
+
setExportError(null);
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const response = await fetch(groupsClearPath(false), { method: "DELETE" });
|
|
337
|
+
if (!response.ok) {
|
|
338
|
+
showToast({
|
|
339
|
+
message: await readApiError(
|
|
340
|
+
response,
|
|
341
|
+
`Clear failed with status ${String(response.status)}`,
|
|
342
|
+
),
|
|
343
|
+
tone: "error",
|
|
344
|
+
});
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const parsed = await parseJsonResponse(response, DeleteInspectorGroupsResponseSchema);
|
|
349
|
+
setExportResult(null);
|
|
350
|
+
setSelectedGroupId(null);
|
|
351
|
+
await mutate();
|
|
352
|
+
await mutateEvidence();
|
|
353
|
+
showToast({
|
|
354
|
+
message: `Cleared ${formatNumber(parsed.deleted)} group${parsed.deleted === 1 ? "" : "s"}.`,
|
|
355
|
+
tone: "success",
|
|
356
|
+
});
|
|
357
|
+
} catch (err) {
|
|
358
|
+
showToast({
|
|
359
|
+
message: err instanceof Error ? err.message : "Unknown error clearing groups",
|
|
360
|
+
tone: "error",
|
|
361
|
+
});
|
|
362
|
+
} finally {
|
|
363
|
+
setClearingGroups(false);
|
|
364
|
+
}
|
|
365
|
+
}, [clearingGroups, groups.length, mutate, mutateEvidence, showToast]);
|
|
366
|
+
|
|
318
367
|
const handleExport = useCallback(
|
|
319
368
|
async (group: InspectorGroup) => {
|
|
320
369
|
setExportingGroupId(group.id);
|
|
@@ -404,6 +453,7 @@ export function GroupsDialog(): JSX.Element {
|
|
|
404
453
|
|
|
405
454
|
return (
|
|
406
455
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
456
|
+
<TransientToast notice={notice} onDismiss={dismissToast} />
|
|
407
457
|
<DialogTrigger asChild>
|
|
408
458
|
<Button
|
|
409
459
|
type="button"
|
|
@@ -437,16 +487,27 @@ export function GroupsDialog(): JSX.Element {
|
|
|
437
487
|
<p className="text-xs text-muted-foreground">
|
|
438
488
|
Evaluation batches, model runs, and exported evidence packs.
|
|
439
489
|
</p>
|
|
440
|
-
<
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
490
|
+
<div className="flex shrink-0 items-center gap-2">
|
|
491
|
+
<Button
|
|
492
|
+
type="button"
|
|
493
|
+
variant="outline"
|
|
494
|
+
size="sm"
|
|
495
|
+
disabled={groups.length === 0 || clearingGroups}
|
|
496
|
+
onClick={() => void handleClearGroups()}
|
|
497
|
+
title="Clear all groups. Session logs and run records stay intact."
|
|
498
|
+
>
|
|
499
|
+
{clearingGroups ? (
|
|
500
|
+
<Loader2 className="size-3.5 animate-spin" />
|
|
501
|
+
) : (
|
|
502
|
+
<Trash2 className="size-3.5" />
|
|
503
|
+
)}
|
|
504
|
+
Clear
|
|
505
|
+
</Button>
|
|
506
|
+
<Button type="button" variant="outline" size="sm" onClick={handleRefresh}>
|
|
507
|
+
<RefreshCw className="size-3.5" />
|
|
508
|
+
Refresh
|
|
509
|
+
</Button>
|
|
510
|
+
</div>
|
|
450
511
|
</div>
|
|
451
512
|
|
|
452
513
|
<div className="grid min-h-0 flex-1 gap-4 md:grid-cols-[260px_minmax(0,1fr)]">
|
|
@@ -265,7 +265,7 @@ function SummaryChip({
|
|
|
265
265
|
return (
|
|
266
266
|
<span
|
|
267
267
|
className={cn(
|
|
268
|
-
"
|
|
268
|
+
"bg-white/[0.045] inline-flex h-7 items-center gap-1 rounded-md px-2 text-xs text-muted-foreground",
|
|
269
269
|
className,
|
|
270
270
|
)}
|
|
271
271
|
>
|
|
@@ -349,7 +349,7 @@ function IdeGroupSection({
|
|
|
349
349
|
|
|
350
350
|
return (
|
|
351
351
|
<section
|
|
352
|
-
className={cn("
|
|
352
|
+
className={cn("bg-card/75 shadow-sm relative isolate rounded-[8px]")}
|
|
353
353
|
data-ide-group={group.id}
|
|
354
354
|
>
|
|
355
355
|
<button
|
|
@@ -359,7 +359,7 @@ function IdeGroupSection({
|
|
|
359
359
|
aria-expanded={!collapsed}
|
|
360
360
|
>
|
|
361
361
|
{group.client === "unknown" ? (
|
|
362
|
-
<span className="
|
|
362
|
+
<span className="bg-white/[0.045] inline-flex size-10 shrink-0 items-center justify-center rounded-[8px] text-slate-200">
|
|
363
363
|
<Monitor className="size-5" />
|
|
364
364
|
</span>
|
|
365
365
|
) : (
|
|
@@ -374,7 +374,7 @@ function IdeGroupSection({
|
|
|
374
374
|
>
|
|
375
375
|
{group.label}
|
|
376
376
|
</span>
|
|
377
|
-
<span className="rounded
|
|
377
|
+
<span className="rounded bg-cyan-300/[0.06] px-1.5 py-0.5 font-mono text-[10px] uppercase tracking-[0.16em] text-cyan-100/75">
|
|
378
378
|
IDE
|
|
379
379
|
</span>
|
|
380
380
|
</span>
|
|
@@ -396,13 +396,13 @@ function IdeGroupSection({
|
|
|
396
396
|
<SummaryChip label="OUT" value={formatTokenCount(totals.outputTokens)} />
|
|
397
397
|
</span>
|
|
398
398
|
|
|
399
|
-
<span className="
|
|
399
|
+
<span className="bg-white/[0.045] hover:bg-white/[0.075] inline-flex size-8 shrink-0 items-center justify-center rounded-md text-slate-200">
|
|
400
400
|
{collapsed ? <ChevronRight className="size-4" /> : <ChevronDown className="size-4" />}
|
|
401
401
|
</span>
|
|
402
402
|
</button>
|
|
403
403
|
|
|
404
404
|
{!collapsed && (
|
|
405
|
-
<div className="space-y-3
|
|
405
|
+
<div className="space-y-3 px-3 pb-3 pt-2 sm:px-4">
|
|
406
406
|
{group.processes.map((processGroup) => (
|
|
407
407
|
<ProcessGroupSection
|
|
408
408
|
key={processGroup.id}
|
|
@@ -440,15 +440,15 @@ function ProcessGroupSection({
|
|
|
440
440
|
|
|
441
441
|
return (
|
|
442
442
|
<section className="relative pl-5">
|
|
443
|
-
<span className="absolute bottom-
|
|
444
|
-
<div className="
|
|
443
|
+
<span className="absolute bottom-1 left-2 top-1 w-px bg-white/[0.055]" aria-hidden="true" />
|
|
444
|
+
<div className="bg-black/[0.10] overflow-hidden rounded-[8px]">
|
|
445
445
|
<button
|
|
446
446
|
type="button"
|
|
447
447
|
className="flex w-full min-w-0 items-center gap-3 px-3 py-2.5 text-left transition-colors hover:bg-white/[0.035] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300/40"
|
|
448
448
|
onClick={onToggle}
|
|
449
449
|
aria-expanded={!collapsed}
|
|
450
450
|
>
|
|
451
|
-
<span className="
|
|
451
|
+
<span className="bg-white/[0.045] inline-flex size-8 shrink-0 items-center justify-center rounded-md text-amber-100">
|
|
452
452
|
<Cpu className="size-4" />
|
|
453
453
|
</span>
|
|
454
454
|
|
|
@@ -482,13 +482,13 @@ function ProcessGroupSection({
|
|
|
482
482
|
<SummaryChip label="OUT" value={formatTokenCount(totals.outputTokens)} />
|
|
483
483
|
</span>
|
|
484
484
|
|
|
485
|
-
<span className="
|
|
485
|
+
<span className="bg-white/[0.045] hover:bg-white/[0.075] inline-flex size-7 shrink-0 items-center justify-center rounded-md text-slate-200">
|
|
486
486
|
{collapsed ? <ChevronRight className="size-4" /> : <ChevronDown className="size-4" />}
|
|
487
487
|
</span>
|
|
488
488
|
</button>
|
|
489
489
|
|
|
490
490
|
{!collapsed && (
|
|
491
|
-
<div className="space-y-2
|
|
491
|
+
<div className="space-y-2 p-2 pt-1 sm:p-3 sm:pt-2">
|
|
492
492
|
{group.sessions.map((session) => (
|
|
493
493
|
<ConversationGroup
|
|
494
494
|
key={session.id}
|
|
@@ -22,7 +22,7 @@ import { ProviderLogoStack } from "./ProviderLogoStack";
|
|
|
22
22
|
import { resolveLogFormat } from "./log-formats";
|
|
23
23
|
|
|
24
24
|
const HEADER_ACTION_BUTTON_CLASS =
|
|
25
|
-
"
|
|
25
|
+
"bg-white/[0.045] hover:bg-white/[0.075] inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground";
|
|
26
26
|
|
|
27
27
|
function HeaderMetric({
|
|
28
28
|
icon,
|
|
@@ -38,7 +38,7 @@ function HeaderMetric({
|
|
|
38
38
|
return (
|
|
39
39
|
<span
|
|
40
40
|
className={cn(
|
|
41
|
-
"
|
|
41
|
+
"bg-white/[0.045] inline-flex h-7 min-w-0 items-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground",
|
|
42
42
|
className,
|
|
43
43
|
)}
|
|
44
44
|
title={title}
|
|
@@ -150,8 +150,8 @@ export function ConversationHeader({
|
|
|
150
150
|
data-nav-id={`conv-${conversationId}`}
|
|
151
151
|
data-nav-action={expanded ? "collapse" : "expand"}
|
|
152
152
|
className={cn(
|
|
153
|
-
"
|
|
154
|
-
"hover:
|
|
153
|
+
"bg-card/80 shadow-sm flex cursor-pointer items-start gap-3 rounded-[8px] px-3 py-2.5 transition-colors",
|
|
154
|
+
"hover:bg-card",
|
|
155
155
|
"select-none",
|
|
156
156
|
"sticky top-0 z-10 mb-2",
|
|
157
157
|
"focus-visible:ring-1 focus-visible:ring-cyan-300/35 focus-visible:outline-none",
|
|
@@ -204,7 +204,7 @@ export function ConversationHeader({
|
|
|
204
204
|
clientPid !== null &&
|
|
205
205
|
clientPid !== undefined && (
|
|
206
206
|
<span
|
|
207
|
-
className="
|
|
207
|
+
className="bg-white/[0.045] shrink-0 rounded px-1.5 py-0.5 font-mono text-xs tabular-nums text-muted-foreground"
|
|
208
208
|
title={`Client process ID ${clientPid}`}
|
|
209
209
|
>
|
|
210
210
|
PID {clientPid}
|
|
@@ -226,7 +226,7 @@ export function ConversationHeader({
|
|
|
226
226
|
|
|
227
227
|
{showClientIdentity && clientDisplayLabel !== null && clientDisplayLabel !== "" && (
|
|
228
228
|
<span
|
|
229
|
-
className="
|
|
229
|
+
className="bg-white/[0.045] inline-flex h-6 max-w-[180px] shrink-0 items-center gap-1 rounded-md px-1.5 text-xs text-muted-foreground"
|
|
230
230
|
title={clientTooltip ?? clientDisplayLabel}
|
|
231
231
|
>
|
|
232
232
|
<ClientLogo client={clientApp} className="size-4 shrink-0" />
|
|
@@ -282,7 +282,7 @@ export function ConversationHeader({
|
|
|
282
282
|
|
|
283
283
|
{/* Expand chevron - shows spinner when collapsed and group has pending logs */}
|
|
284
284
|
<span
|
|
285
|
-
className="
|
|
285
|
+
className="bg-white/[0.045] hover:bg-white/[0.075] inline-flex size-8 shrink-0 items-center justify-center rounded-md text-slate-200"
|
|
286
286
|
aria-hidden="true"
|
|
287
287
|
>
|
|
288
288
|
{expanded ? (
|
|
@@ -81,7 +81,7 @@ const BODY_CHUNK_BYTES = 256 * 1024;
|
|
|
81
81
|
const CHUNKED_BODY_THRESHOLD_BYTES = 1024 * 1024;
|
|
82
82
|
|
|
83
83
|
const TAB_TRIGGER_CLASS =
|
|
84
|
-
"h-9 flex-none rounded-md border border-transparent bg-transparent px-3 text-xs font-semibold text-muted-foreground shadow-none transition-colors hover:
|
|
84
|
+
"h-9 flex-none rounded-md border border-transparent bg-transparent px-3 text-xs font-semibold text-muted-foreground shadow-none transition-colors hover:bg-white/[0.045] hover:text-foreground data-[state=active]:bg-cyan-300/[0.08] data-[state=active]:text-cyan-100 data-[state=active]:shadow-none";
|
|
85
85
|
|
|
86
86
|
function createEmptyBodyPreviewState(totalBytes: number | null = null): BodyPreviewState {
|
|
87
87
|
return {
|
|
@@ -190,7 +190,7 @@ function ExpandedPanel({
|
|
|
190
190
|
return (
|
|
191
191
|
<div
|
|
192
192
|
className={cn(
|
|
193
|
-
"
|
|
193
|
+
"bg-black/[0.14] inspector-scrollbar mx-3 mb-3 max-h-[72vh] overflow-auto rounded-[8px] p-3",
|
|
194
194
|
className,
|
|
195
195
|
)}
|
|
196
196
|
>
|
|
@@ -201,7 +201,7 @@ function ExpandedPanel({
|
|
|
201
201
|
|
|
202
202
|
function EmptyState({ children }: { children: ReactNode }): JSX.Element {
|
|
203
203
|
return (
|
|
204
|
-
<div className="
|
|
204
|
+
<div className="bg-white/[0.035] rounded-md px-3 py-5 text-center text-xs italic text-muted-foreground">
|
|
205
205
|
{children}
|
|
206
206
|
</div>
|
|
207
207
|
);
|
|
@@ -209,7 +209,7 @@ function EmptyState({ children }: { children: ReactNode }): JSX.Element {
|
|
|
209
209
|
|
|
210
210
|
function RawTextBlock({ children }: { children: ReactNode }): JSX.Element {
|
|
211
211
|
return (
|
|
212
|
-
<pre className="
|
|
212
|
+
<pre className="bg-black/[0.18] inspector-scrollbar max-h-[64vh] overflow-auto whitespace-pre-wrap break-words rounded-[8px] p-3 font-mono text-xs leading-relaxed text-muted-foreground">
|
|
213
213
|
{children}
|
|
214
214
|
</pre>
|
|
215
215
|
);
|
|
@@ -218,9 +218,9 @@ function RawTextBlock({ children }: { children: ReactNode }): JSX.Element {
|
|
|
218
218
|
type NoticeTone = "loading" | "warning" | "danger";
|
|
219
219
|
|
|
220
220
|
const NOTICE_TONE_CLASSES: Record<NoticeTone, string> = {
|
|
221
|
-
loading: "border-
|
|
222
|
-
warning: "border-
|
|
223
|
-
danger: "border-
|
|
221
|
+
loading: "border-transparent bg-cyan-400/[0.06] text-cyan-100",
|
|
222
|
+
warning: "border-transparent bg-amber-400/[0.08] text-amber-100",
|
|
223
|
+
danger: "border-transparent bg-rose-400/[0.08] text-rose-100",
|
|
224
224
|
};
|
|
225
225
|
|
|
226
226
|
function NoticeBlock({
|
|
@@ -307,13 +307,13 @@ function HeaderRows({
|
|
|
307
307
|
if (entries.length === 0) return <EmptyState>{emptyLabel}</EmptyState>;
|
|
308
308
|
|
|
309
309
|
return (
|
|
310
|
-
<div className="
|
|
310
|
+
<div className="bg-black/[0.14] overflow-hidden rounded-[8px] font-mono text-xs">
|
|
311
311
|
{entries.map(([key, value]) => (
|
|
312
312
|
<div
|
|
313
313
|
key={key}
|
|
314
|
-
className="grid min-w-0 grid-cols-[minmax(140px,0.32fr)_minmax(0,1fr)] border-b border-white/
|
|
314
|
+
className="grid min-w-0 grid-cols-[minmax(140px,0.32fr)_minmax(0,1fr)] border-b border-white/[0.055] last:border-b-0"
|
|
315
315
|
>
|
|
316
|
-
<div className="min-w-0 truncate
|
|
316
|
+
<div className="min-w-0 truncate bg-white/[0.035] px-2.5 py-2 font-semibold text-cyan-200">
|
|
317
317
|
{key}
|
|
318
318
|
</div>
|
|
319
319
|
<div className="min-w-0 truncate px-2.5 py-2 text-muted-foreground" title={value}>
|
|
@@ -791,23 +791,12 @@ export const LogEntry = memo(function ({
|
|
|
791
791
|
<TooltipProvider>
|
|
792
792
|
<div
|
|
793
793
|
className={cn(
|
|
794
|
-
"
|
|
794
|
+
"bg-card/80 relative mb-1.5 overflow-hidden rounded-[8px] transition-[background-color,box-shadow]",
|
|
795
795
|
expanded
|
|
796
|
-
? "
|
|
797
|
-
: "hover:
|
|
796
|
+
? "bg-card shadow-[0_18px_60px_rgba(0,0,0,0.30),0_0_0_1px_rgba(103,232,249,0.08)]"
|
|
797
|
+
: "hover:bg-card",
|
|
798
798
|
)}
|
|
799
799
|
>
|
|
800
|
-
<div
|
|
801
|
-
className={cn(
|
|
802
|
-
"pointer-events-none absolute inset-y-3 left-1 w-px rounded-full",
|
|
803
|
-
expanded ? "bg-cyan-200/55 shadow-[0_0_20px_rgba(103,232,249,0.26)]" : "bg-white/12",
|
|
804
|
-
)}
|
|
805
|
-
aria-hidden="true"
|
|
806
|
-
/>
|
|
807
|
-
<div
|
|
808
|
-
className="pointer-events-none absolute inset-x-3 top-0 h-px bg-gradient-to-r from-transparent via-white/15 to-transparent"
|
|
809
|
-
aria-hidden="true"
|
|
810
|
-
/>
|
|
811
800
|
<LogEntryHeader
|
|
812
801
|
log={displayLog}
|
|
813
802
|
displayNumber={displayNumber}
|
|
@@ -832,7 +821,7 @@ export const LogEntry = memo(function ({
|
|
|
832
821
|
|
|
833
822
|
{expanded && (
|
|
834
823
|
<div
|
|
835
|
-
className="relative
|
|
824
|
+
className="relative bg-black/[0.14] pt-2"
|
|
836
825
|
onClick={(e) => e.stopPropagation()}
|
|
837
826
|
onKeyDown={(e) => e.stopPropagation()}
|
|
838
827
|
>
|
|
@@ -843,7 +832,7 @@ export const LogEntry = memo(function ({
|
|
|
843
832
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="gap-2">
|
|
844
833
|
<TabsList
|
|
845
834
|
variant="line"
|
|
846
|
-
className="
|
|
835
|
+
className="bg-white/[0.04] inspector-scrollbar relative mx-3 h-auto w-[calc(100%-1.5rem)] justify-start overflow-x-auto rounded-[8px] p-1"
|
|
847
836
|
>
|
|
848
837
|
{viewMode === "full" && (
|
|
849
838
|
<TabsTrigger className={TAB_TRIGGER_CLASS} value="raw-headers">
|
|
@@ -34,10 +34,10 @@ import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/
|
|
|
34
34
|
import type { CacheTrend } from "./cacheTrend";
|
|
35
35
|
|
|
36
36
|
const STATUS_BADGE_CLASSES: Record<StatusCategory, string> = {
|
|
37
|
-
success: "bg-slate-300/[0.08] text-slate-100 border-
|
|
38
|
-
client_error: "bg-amber-300/[0.10] text-amber-100 border-
|
|
39
|
-
server_error: "bg-rose-400/[0.10] text-rose-100 border-
|
|
40
|
-
pending: "bg-cyan-300/[0.08] text-cyan-100 border-
|
|
37
|
+
success: "bg-slate-300/[0.08] text-slate-100 border-transparent",
|
|
38
|
+
client_error: "bg-amber-300/[0.10] text-amber-100 border-transparent",
|
|
39
|
+
server_error: "bg-rose-400/[0.10] text-rose-100 border-transparent",
|
|
40
|
+
pending: "bg-cyan-300/[0.08] text-cyan-100 border-transparent",
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
const STATUS_CUE_CLASSES: Record<
|
|
@@ -66,10 +66,9 @@ const STATUS_CUE_CLASSES: Record<
|
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
const HEADER_ACTION_BUTTON_CLASS =
|
|
69
|
-
"border border-
|
|
69
|
+
"border border-transparent bg-white/[0.045] hover:bg-white/[0.075] size-8 text-muted-foreground shadow-none hover:text-cyan-100";
|
|
70
70
|
|
|
71
|
-
const TELEMETRY_CHIP_CLASS =
|
|
72
|
-
"border border-border bg-muted/40 flex h-7 items-center gap-1 rounded-md px-2 text-xs";
|
|
71
|
+
const TELEMETRY_CHIP_CLASS = "bg-white/[0.045] flex h-7 items-center gap-1 rounded-md px-2 text-xs";
|
|
73
72
|
|
|
74
73
|
function formatElapsed(ms: number): string {
|
|
75
74
|
if (ms < 1000) return `${ms}ms`;
|
|
@@ -232,10 +231,8 @@ export const LogEntryHeader = memo(function ({
|
|
|
232
231
|
data-nav-action={expanded ? "collapse" : "expand"}
|
|
233
232
|
className={cn(
|
|
234
233
|
"group/header relative isolate flex min-w-0 cursor-pointer items-start gap-2 overflow-hidden px-3 py-2 transition-[background-color,border-color,box-shadow]",
|
|
235
|
-
expanded
|
|
236
|
-
|
|
237
|
-
: "bg-transparent hover:bg-white/[0.035]",
|
|
238
|
-
"select-none before:pointer-events-none before:absolute before:inset-x-3 before:top-0 before:h-px before:bg-white/12",
|
|
234
|
+
expanded ? "bg-white/[0.04]" : "bg-transparent hover:bg-white/[0.035]",
|
|
235
|
+
"select-none",
|
|
239
236
|
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none",
|
|
240
237
|
)}
|
|
241
238
|
onClick={onToggle}
|
|
@@ -250,7 +247,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
250
247
|
{/* Request ID */}
|
|
251
248
|
<span
|
|
252
249
|
className={cn(
|
|
253
|
-
"
|
|
250
|
+
"inline-flex h-8 shrink-0 items-center gap-2 rounded-md px-2 font-mono",
|
|
254
251
|
statusCue.frame,
|
|
255
252
|
)}
|
|
256
253
|
>
|
|
@@ -261,7 +258,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
261
258
|
</span>
|
|
262
259
|
|
|
263
260
|
{/* Request start time */}
|
|
264
|
-
<span className="
|
|
261
|
+
<span className="bg-white/[0.045] flex h-7 shrink-0 items-center gap-1 rounded-md px-2 text-xs text-muted-foreground">
|
|
265
262
|
<Clock className="size-3" />
|
|
266
263
|
<span className="font-mono tabular-nums" title={log.timestamp}>
|
|
267
264
|
{formatTimestamp(log.timestamp, timeDisplayFormat)}
|
|
@@ -608,7 +605,7 @@ export const LogEntryHeader = memo(function ({
|
|
|
608
605
|
)}
|
|
609
606
|
|
|
610
607
|
<span
|
|
611
|
-
className="
|
|
608
|
+
className="bg-white/[0.045] hover:bg-white/[0.075] inline-flex size-8 shrink-0 items-center justify-center rounded-md text-slate-200"
|
|
612
609
|
aria-hidden="true"
|
|
613
610
|
>
|
|
614
611
|
{expanded ? (
|
|
@@ -44,13 +44,13 @@ const CATEGORY_LABELS: Record<RequestToolCategory, string> = {
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
const CATEGORY_CLASSES: Record<RequestToolCategory, string> = {
|
|
47
|
-
file: "border-
|
|
48
|
-
shell: "border-
|
|
49
|
-
browser: "border-
|
|
50
|
-
web: "border-
|
|
51
|
-
mcp: "border-
|
|
52
|
-
code: "border-
|
|
53
|
-
other: "border-
|
|
47
|
+
file: "border-transparent bg-blue-400/5 text-blue-300",
|
|
48
|
+
shell: "border-transparent bg-teal-400/5 text-teal-300",
|
|
49
|
+
browser: "border-transparent bg-cyan-400/5 text-cyan-300",
|
|
50
|
+
web: "border-transparent bg-sky-400/5 text-sky-300",
|
|
51
|
+
mcp: "border-transparent bg-violet-400/5 text-violet-300",
|
|
52
|
+
code: "border-transparent bg-emerald-400/5 text-emerald-300",
|
|
53
|
+
other: "border-transparent bg-muted/30 text-muted-foreground",
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
function CategoryIcon({ category }: { category: RequestToolCategory }): JSX.Element {
|
|
@@ -101,7 +101,7 @@ function ToolRow({ tool, called }: { tool: RequestToolDefinition; called: boolea
|
|
|
101
101
|
|
|
102
102
|
return (
|
|
103
103
|
<Collapsible open={open} onOpenChange={setOpen}>
|
|
104
|
-
<div className="
|
|
104
|
+
<div className="bg-white/[0.035] rounded-[8px]">
|
|
105
105
|
<div className="flex min-w-0 items-center gap-2 px-2.5 py-2">
|
|
106
106
|
<button
|
|
107
107
|
type="button"
|
|
@@ -140,7 +140,7 @@ function ToolRow({ tool, called }: { tool: RequestToolDefinition; called: boolea
|
|
|
140
140
|
</button>
|
|
141
141
|
<button
|
|
142
142
|
type="button"
|
|
143
|
-
className="
|
|
143
|
+
className="bg-white/[0.045] hover:bg-white/[0.075] inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
144
144
|
onClick={nameCopy.copy}
|
|
145
145
|
aria-label={nameCopy.copied ? "Copied tool name" : "Copy tool name"}
|
|
146
146
|
>
|
|
@@ -153,13 +153,13 @@ function ToolRow({ tool, called }: { tool: RequestToolDefinition; called: boolea
|
|
|
153
153
|
</div>
|
|
154
154
|
<CollapsibleContent>
|
|
155
155
|
{open && (
|
|
156
|
-
<div className="space-y-2
|
|
156
|
+
<div className="space-y-2 bg-white/[0.025] px-2.5 py-2">
|
|
157
157
|
{tool.description !== null && (
|
|
158
158
|
<p className="text-xs leading-relaxed text-muted-foreground">{tool.description}</p>
|
|
159
159
|
)}
|
|
160
160
|
<RequiredParameters names={tool.requiredParameters} />
|
|
161
161
|
{tool.schema !== null && (
|
|
162
|
-
<ScrollArea className="
|
|
162
|
+
<ScrollArea className="bg-black/[0.16] max-h-64 rounded-[8px] p-2">
|
|
163
163
|
<Suspense
|
|
164
164
|
fallback={<div className="text-xs text-muted-foreground">Loading JSON...</div>}
|
|
165
165
|
>
|
|
@@ -203,7 +203,7 @@ export const RequestTools = memo(function RequestTools({
|
|
|
203
203
|
const hiddenPreviewCount = summary.tools.length - previewTools.length;
|
|
204
204
|
|
|
205
205
|
return (
|
|
206
|
-
<section className="
|
|
206
|
+
<section className="bg-black/[0.10] mb-3 overflow-hidden rounded-[8px]">
|
|
207
207
|
<div className="flex min-w-0 flex-wrap items-center gap-2 px-3 py-2">
|
|
208
208
|
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
209
209
|
<Wrench className="size-4 shrink-0 text-sky-300/80" />
|
|
@@ -251,7 +251,7 @@ export const RequestTools = memo(function RequestTools({
|
|
|
251
251
|
</Button>
|
|
252
252
|
</div>
|
|
253
253
|
|
|
254
|
-
<div className="flex min-w-0 flex-wrap gap-1.5
|
|
254
|
+
<div className="flex min-w-0 flex-wrap gap-1.5 bg-white/[0.025] px-3 py-2">
|
|
255
255
|
{previewTools.map((tool) => (
|
|
256
256
|
<Badge
|
|
257
257
|
key={tool.name}
|
|
@@ -272,8 +272,8 @@ export const RequestTools = memo(function RequestTools({
|
|
|
272
272
|
</div>
|
|
273
273
|
|
|
274
274
|
{expanded && (
|
|
275
|
-
<div className="space-y-2
|
|
276
|
-
<label className="
|
|
275
|
+
<div className="space-y-2 bg-black/10 px-3 py-3">
|
|
276
|
+
<label className="bg-white/[0.045] hover:bg-white/[0.075] flex h-8 items-center gap-2 rounded-md px-2 text-xs">
|
|
277
277
|
<Search className="size-3.5 shrink-0 text-muted-foreground" />
|
|
278
278
|
<input
|
|
279
279
|
value={query}
|
|
@@ -69,7 +69,7 @@ export function ThreadConnector({
|
|
|
69
69
|
return (
|
|
70
70
|
<div className="flex flex-col items-center w-6 shrink-0 pt-0.5 pb-0.5">
|
|
71
71
|
<div className="flex justify-center h-[calc(0.75rem-8px)]">
|
|
72
|
-
{!isFirst && <div className="w-
|
|
72
|
+
{!isFirst && <div className="w-px bg-muted-foreground/18 h-full" />}
|
|
73
73
|
</div>
|
|
74
74
|
|
|
75
75
|
{isFusedBoundary ? (
|
|
@@ -139,8 +139,8 @@ export function ThreadConnector({
|
|
|
139
139
|
className={cn(
|
|
140
140
|
"w-0.5 h-full",
|
|
141
141
|
isPending
|
|
142
|
-
? "border-dashed bg-transparent border-l
|
|
143
|
-
: "bg-muted-foreground/
|
|
142
|
+
? "border-dashed bg-transparent border-l border-muted-foreground/14"
|
|
143
|
+
: "bg-muted-foreground/18",
|
|
144
144
|
)}
|
|
145
145
|
/>
|
|
146
146
|
)}
|
|
@@ -14,7 +14,7 @@ function ToolTraceEventRow({ event }: { event: ToolTraceEvent }): JSX.Element {
|
|
|
14
14
|
return (
|
|
15
15
|
<div
|
|
16
16
|
key={event.id}
|
|
17
|
-
className="group/tool-trace flex min-w-0 items-center gap-2 rounded-md
|
|
17
|
+
className="group/tool-trace flex min-w-0 items-center gap-2 rounded-md bg-white/[0.035] px-2.5 py-1.5 text-xs"
|
|
18
18
|
>
|
|
19
19
|
<Wrench className="size-3.5 shrink-0 text-sky-400/70" />
|
|
20
20
|
<span className="font-mono font-semibold text-foreground/80">{event.name}</span>
|
|
@@ -143,7 +143,7 @@ export const TurnGroup = memo(function TurnGroup({
|
|
|
143
143
|
};
|
|
144
144
|
}, [entries, lastIdx]);
|
|
145
145
|
|
|
146
|
-
const bgClass = turnIndex % 2 === 0 ? "bg-white/[0.
|
|
146
|
+
const bgClass = turnIndex % 2 === 0 ? "bg-white/[0.008]" : "bg-cyan-300/[0.008]";
|
|
147
147
|
const aggregateIsSlow =
|
|
148
148
|
aggregate.maxElapsed !== null &&
|
|
149
149
|
slowResponseThresholdSeconds > 0 &&
|
|
@@ -194,10 +194,8 @@ export const TurnGroup = memo(function TurnGroup({
|
|
|
194
194
|
aria-label={collapsed ? undefined : turnLabel}
|
|
195
195
|
data-nav-id={collapsed ? undefined : `turn-${String(firstLogId)}`}
|
|
196
196
|
className={cn(
|
|
197
|
-
"rounded-[8px]
|
|
198
|
-
isPending
|
|
199
|
-
? "border-amber-300/15 shadow-[0_0_0_1px_rgba(252,211,77,0.04)]"
|
|
200
|
-
: "border-transparent",
|
|
197
|
+
"rounded-[8px] transition-[background-color,box-shadow]",
|
|
198
|
+
isPending ? "bg-amber-300/[0.018] shadow-[0_0_0_1px_rgba(252,211,77,0.035)]" : "",
|
|
201
199
|
!collapsed &&
|
|
202
200
|
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none",
|
|
203
201
|
)}
|
|
@@ -226,16 +224,14 @@ export const TurnGroup = memo(function TurnGroup({
|
|
|
226
224
|
|
|
227
225
|
<div className="w-6 shrink-0 flex flex-col items-center pt-1.5 pb-1.5">
|
|
228
226
|
<div className="flex justify-center h-[calc(0.75rem-8px)]" />
|
|
229
|
-
<span className="size-2 rounded-full
|
|
227
|
+
<span className="size-2 rounded-full bg-cyan-300/45" />
|
|
230
228
|
<div className="flex-1 flex justify-center min-h-0">
|
|
231
|
-
<div className="w-px bg-muted-foreground/
|
|
229
|
+
<div className="w-px bg-muted-foreground/14 h-full" />
|
|
232
230
|
</div>
|
|
233
231
|
<span
|
|
234
232
|
className={cn(
|
|
235
|
-
"size-2 rounded-full
|
|
236
|
-
isComplete
|
|
237
|
-
? "border-amber-300/45 bg-amber-300/20"
|
|
238
|
-
: "border-muted-foreground/30 bg-muted/30",
|
|
233
|
+
"size-2 rounded-full",
|
|
234
|
+
isComplete ? "bg-amber-300/45" : "bg-muted-foreground/25",
|
|
239
235
|
)}
|
|
240
236
|
/>
|
|
241
237
|
</div>
|
|
@@ -244,8 +240,8 @@ export const TurnGroup = memo(function TurnGroup({
|
|
|
244
240
|
{entries.length > 1 && (
|
|
245
241
|
<div
|
|
246
242
|
className={cn(
|
|
247
|
-
"flex-1 min-w-0 mb-0.5 rounded-[8px] py-1.5 px-3 flex items-center gap-3 text-xs
|
|
248
|
-
"
|
|
243
|
+
"flex-1 min-w-0 mb-0.5 rounded-[8px] py-1.5 px-3 flex items-center gap-3 text-xs",
|
|
244
|
+
"bg-card/80 transition-colors hover:bg-card",
|
|
249
245
|
bgClass,
|
|
250
246
|
)}
|
|
251
247
|
>
|
|
@@ -318,7 +314,7 @@ export const TurnGroup = memo(function TurnGroup({
|
|
|
318
314
|
<span className="flex-1 min-w-0" />
|
|
319
315
|
|
|
320
316
|
<span
|
|
321
|
-
className="
|
|
317
|
+
className="bg-white/[0.045] inline-flex size-7 shrink-0 items-center justify-center rounded-md text-slate-200"
|
|
322
318
|
aria-hidden="true"
|
|
323
319
|
>
|
|
324
320
|
<ChevronRight className="size-4 shrink-0" />
|
package/src/lib/groupContract.ts
CHANGED
|
@@ -103,6 +103,11 @@ export const DeleteInspectorGroupResponseSchema = z.object({
|
|
|
103
103
|
evidencePath: z.string().nullable(),
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
+
export const DeleteInspectorGroupsResponseSchema = z.object({
|
|
107
|
+
deleted: z.number().int().nonnegative(),
|
|
108
|
+
groups: z.array(DeleteInspectorGroupResponseSchema),
|
|
109
|
+
});
|
|
110
|
+
|
|
106
111
|
export const InspectorGroupExportMemberSchema = z.object({
|
|
107
112
|
member: InspectorGroupMemberSchema,
|
|
108
113
|
run: InspectorRunSchema.nullable(),
|
|
@@ -153,6 +158,7 @@ export type AddInspectorGroupSessionInput = z.infer<typeof AddInspectorGroupSess
|
|
|
153
158
|
export type GroupEvidenceExportOptions = z.infer<typeof GroupEvidenceExportOptionsSchema>;
|
|
154
159
|
export type DeleteInspectorGroupOptions = z.infer<typeof DeleteInspectorGroupOptionsSchema>;
|
|
155
160
|
export type DeleteInspectorGroupResponse = z.infer<typeof DeleteInspectorGroupResponseSchema>;
|
|
161
|
+
export type DeleteInspectorGroupsResponse = z.infer<typeof DeleteInspectorGroupsResponseSchema>;
|
|
156
162
|
export type InspectorGroupExportMember = z.infer<typeof InspectorGroupExportMemberSchema>;
|
|
157
163
|
export type InspectorGroupEvidenceSummary = z.infer<typeof InspectorGroupEvidenceSummarySchema>;
|
|
158
164
|
export type GroupEvidenceReadResponse = z.infer<typeof GroupEvidenceReadResponseSchema>;
|