@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.
Files changed (44) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-Fqqw5Lci.js → CompareDrawer-BRCSDHn3.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-v5Lmx35X.js +106 -0
  4. package/.output/public/assets/{ReplayDialog-hYhz_i6W.js → ReplayDialog-Dkez3vxu.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-CTuWbtwC.js → RequestAnatomy-CHBODGDF.js} +1 -1
  6. package/.output/public/assets/{ResponseView-CsZ7S5Gv.js → ResponseView-Dk2dJY-B.js} +2 -2
  7. package/.output/public/assets/{StreamingChunkSequence-PIbJMotU.js → StreamingChunkSequence-gZpqWwKK.js} +1 -1
  8. package/.output/public/assets/_sessionId-DziA4ZD4.js +1 -0
  9. package/.output/public/assets/{index-F7mynwuH.js → index-B1-V3HCD.js} +1 -1
  10. package/.output/public/assets/index-ChgjUix0.css +1 -0
  11. package/.output/public/assets/index-DkHcxpXi.js +1 -0
  12. package/.output/public/assets/{json-viewer-DCXtNmmD.js → json-viewer-CtvzeDPb.js} +1 -1
  13. package/.output/public/assets/{main-BJZf7_Ib.js → main-CgmDEMGA.js} +2 -2
  14. package/.output/server/{_sessionId-DjSHDNh8.mjs → _sessionId-CbsSRLSa.mjs} +2 -2
  15. package/.output/server/_ssr/{CompareDrawer-Dal52IFo.mjs → CompareDrawer-BiSslksi.mjs} +2 -2
  16. package/.output/server/_ssr/{ProxyViewerContainer-CVqFZGff.mjs → ProxyViewerContainer-DIN2AjhM.mjs} +216 -183
  17. package/.output/server/_ssr/{ReplayDialog-BOEgdMes.mjs → ReplayDialog-DzoWpTis.mjs} +3 -3
  18. package/.output/server/_ssr/{RequestAnatomy-gch7_vCV.mjs → RequestAnatomy-D2tbgAPn.mjs} +2 -2
  19. package/.output/server/_ssr/{ResponseView-lEWPy-H4.mjs → ResponseView-BFP7zo8Z.mjs} +2 -2
  20. package/.output/server/_ssr/{StreamingChunkSequence-DnudbJ1O.mjs → StreamingChunkSequence-BfiRZW_k.mjs} +2 -2
  21. package/.output/server/_ssr/{index-Cza8zDrW.mjs → index-OlcgE4Tl.mjs} +2 -2
  22. package/.output/server/_ssr/index.mjs +2 -2
  23. package/.output/server/_ssr/{json-viewer-C2lIVEX3.mjs → json-viewer-DmhEr-jg.mjs} +2 -2
  24. package/.output/server/_ssr/{router-bF9uzd1k.mjs → router-BMdwHLHh.mjs} +80 -21
  25. package/.output/server/{_tanstack-start-manifest_v-ErvW-mtr.mjs → _tanstack-start-manifest_v-CL4qjwkQ.mjs} +1 -1
  26. package/.output/server/index.mjs +77 -77
  27. package/package.json +1 -1
  28. package/src/components/groups/GroupsDialog.tsx +71 -10
  29. package/src/components/proxy-viewer/ConversationGroupList.tsx +11 -11
  30. package/src/components/proxy-viewer/ConversationHeader.tsx +7 -7
  31. package/src/components/proxy-viewer/LogEntry.tsx +15 -26
  32. package/src/components/proxy-viewer/LogEntryHeader.tsx +11 -14
  33. package/src/components/proxy-viewer/RequestToolsPanel.tsx +15 -15
  34. package/src/components/proxy-viewer/ThreadConnector.tsx +3 -3
  35. package/src/components/proxy-viewer/ToolTraceEvents.tsx +1 -1
  36. package/src/components/proxy-viewer/TurnGroup.tsx +10 -14
  37. package/src/lib/groupContract.ts +6 -0
  38. package/src/proxy/groupStore.ts +24 -0
  39. package/src/routes/api/groups.ts +41 -1
  40. package/src/services/groups.ts +8 -0
  41. package/.output/public/assets/ProxyViewerContainer-CP_RzwW9.js +0 -106
  42. package/.output/public/assets/_sessionId-CUGA9qU3.js +0 -1
  43. package/.output/public/assets/index-G4dTUobw.js +0 -1
  44. 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
- <Button
441
- type="button"
442
- variant="outline"
443
- size="sm"
444
- className="shrink-0"
445
- onClick={handleRefresh}
446
- >
447
- <RefreshCw className="size-3.5" />
448
- Refresh
449
- </Button>
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
- "border border-border bg-muted/40 inline-flex h-7 items-center gap-1 rounded-md px-2 text-xs text-muted-foreground",
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("border border-border bg-card shadow-sm relative isolate rounded-[8px]")}
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="border border-border bg-muted/40 inline-flex size-10 shrink-0 items-center justify-center rounded-[8px] text-slate-200">
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 border border-cyan-200/15 bg-cyan-300/[0.06] px-1.5 py-0.5 font-mono text-[10px] uppercase tracking-[0.16em] text-cyan-100/75">
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="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-slate-200">
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 border-t border-white/10 px-3 pb-3 pt-3 sm:px-4">
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-0 left-2 top-0 w-px bg-white/12" aria-hidden="true" />
444
- <div className="border border-border bg-card shadow-sm overflow-hidden rounded-[8px]">
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="border border-border bg-muted/40 inline-flex size-8 shrink-0 items-center justify-center rounded-md text-amber-100">
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="border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex size-7 shrink-0 items-center justify-center rounded-md text-slate-200">
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 border-t border-white/10 p-2 sm:p-3">
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
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-muted-foreground transition-colors hover:text-foreground";
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
- "border border-border bg-muted/30 inline-flex h-7 min-w-0 items-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground",
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
- "border border-border bg-card shadow-sm flex cursor-pointer items-start gap-3 rounded-[8px] px-3 py-2.5 transition-colors",
154
- "hover:border-cyan-200/20",
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="border border-border bg-muted/40 shrink-0 rounded px-1.5 py-0.5 font-mono text-xs tabular-nums text-muted-foreground"
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="border border-border bg-muted/30 inline-flex h-6 max-w-[180px] shrink-0 items-center gap-1 rounded-md px-1.5 text-xs text-muted-foreground"
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="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-slate-200"
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:border-white/10 hover:bg-white/[0.045] hover:text-foreground data-[state=active]:border-cyan-200/25 data-[state=active]:bg-cyan-300/[0.08] data-[state=active]:text-cyan-100 data-[state=active]:shadow-none";
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
- "border border-border bg-card shadow-sm inspector-scrollbar mx-3 mb-3 max-h-[72vh] overflow-auto rounded-[8px] p-3",
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="border border-border bg-muted/40 rounded-md border-dashed px-3 py-5 text-center text-xs italic text-muted-foreground">
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="border border-border bg-card shadow-sm 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">
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-cyan-400/25 bg-cyan-400/[0.06] text-cyan-100",
222
- warning: "border-amber-400/25 bg-amber-400/[0.08] text-amber-100",
223
- danger: "border-rose-400/25 bg-rose-400/[0.08] text-rose-100",
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="border border-border bg-card shadow-sm overflow-hidden rounded-[8px] font-mono text-xs">
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/10 last:border-b-0"
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 border-r border-white/10 bg-white/[0.035] px-2.5 py-2 font-semibold text-cyan-200">
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
- "border border-border bg-card shadow-sm relative mb-2 overflow-hidden rounded-[8px] transition-[background-color,border-color,box-shadow]",
794
+ "bg-card/80 relative mb-1.5 overflow-hidden rounded-[8px] transition-[background-color,box-shadow]",
795
795
  expanded
796
- ? "border-cyan-200/25 shadow-[0_24px_80px_rgba(0,0,0,0.36),0_0_0_1px_rgba(103,232,249,0.05),inset_0_1px_0_rgba(255,255,255,0.14)]"
797
- : "hover:border-cyan-200/18",
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 border-t border-white/10 bg-black/[0.18] pt-2"
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="border border-border bg-muted/40 inspector-scrollbar relative mx-3 h-auto w-[calc(100%-1.5rem)] justify-start overflow-x-auto rounded-[8px] p-1"
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-slate-300/20",
38
- client_error: "bg-amber-300/[0.10] text-amber-100 border-amber-300/30",
39
- server_error: "bg-rose-400/[0.10] text-rose-100 border-rose-300/30",
40
- pending: "bg-cyan-300/[0.08] text-cyan-100 border-cyan-300/25",
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-input bg-background hover:bg-accent hover:text-accent-foreground size-8 text-muted-foreground shadow-none hover:text-cyan-100";
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
- ? "bg-white/[0.035] shadow-[inset_0_1px_0_rgba(255,255,255,0.10)]"
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
- "border border-border bg-muted/40 inline-flex h-8 shrink-0 items-center gap-2 rounded-md px-2 font-mono",
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="border border-border bg-muted/40 flex h-7 shrink-0 items-center gap-1 rounded-md px-2 text-xs text-muted-foreground">
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="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-slate-200"
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-blue-400/20 bg-blue-400/5 text-blue-300",
48
- shell: "border-teal-400/20 bg-teal-400/5 text-teal-300",
49
- browser: "border-cyan-400/20 bg-cyan-400/5 text-cyan-300",
50
- web: "border-sky-400/20 bg-sky-400/5 text-sky-300",
51
- mcp: "border-violet-400/20 bg-violet-400/5 text-violet-300",
52
- code: "border-emerald-400/20 bg-emerald-400/5 text-emerald-300",
53
- other: "border-muted-foreground/20 bg-muted/30 text-muted-foreground",
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="border border-border bg-card shadow-sm rounded-[8px]">
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="border border-input bg-background hover:bg-accent hover:text-accent-foreground 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"
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 border-t border-white/10 bg-white/[0.025] px-2.5 py-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="border border-border bg-card shadow-sm max-h-64 rounded-[8px] p-2">
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="border border-border bg-card shadow-sm mb-3 overflow-hidden rounded-[8px]">
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 border-t border-white/10 bg-white/[0.025] px-3 py-2">
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 border-t border-white/10 bg-black/10 px-3 py-3">
276
- <label className="border border-input bg-background hover:bg-accent hover:text-accent-foreground flex h-8 items-center gap-2 rounded-md px-2 text-xs">
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-0.5 bg-muted-foreground/30 h-full" />}
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-2 border-muted-foreground/20"
143
- : "bg-muted-foreground/30",
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 border border-border/70 bg-muted/20 px-2.5 py-1.5 text-xs"
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.012]" : "bg-cyan-300/[0.012]";
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] border transition-[border-color,box-shadow]",
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 border border-cyan-300/45 bg-cyan-300/20" />
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/25 h-full" />
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 border",
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 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]",
248
- "border border-border bg-card shadow-sm transition-colors hover:border-cyan-200/20",
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="border border-input bg-background inline-flex size-7 shrink-0 items-center justify-center rounded-md text-slate-200"
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" />
@@ -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>;