@tonyclaw/agent-inspector 2.1.14 → 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.
Files changed (50) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/CompareDrawer-BcEd6V-V.js +1 -0
  3. package/.output/public/assets/ProxyViewerContainer-h851qWNp.js +106 -0
  4. package/.output/public/assets/ReplayDialog-BNpC0548.js +1 -0
  5. package/.output/public/assets/{RequestAnatomy-BNahe83D.js → RequestAnatomy-Ds1uRLVB.js} +1 -1
  6. package/.output/public/assets/{ResponseView-DSOnGqi6.js → ResponseView-7KPVqKl5.js} +2 -2
  7. package/.output/public/assets/{StreamingChunkSequence-BEKTDklB.js → StreamingChunkSequence-BHQT261s.js} +1 -1
  8. package/.output/public/assets/_sessionId-DWePGjnS.js +1 -0
  9. package/.output/public/assets/index-CI1-G8ua.js +1 -0
  10. package/.output/public/assets/index-DdhFqPsI.css +1 -0
  11. package/.output/public/assets/{index-DWOkqdCa.js → index-DjKt8XKe.js} +1 -1
  12. package/.output/public/assets/{json-viewer-C2JpgcW0.js → json-viewer-CkCu-rka.js} +1 -1
  13. package/.output/public/assets/{main-CSONBwwn.js → main-DpD1N0S8.js} +2 -2
  14. package/.output/server/_libs/lucide-react.mjs +194 -200
  15. package/.output/server/{_sessionId-CPAa37n5.mjs → _sessionId-DF9Sy8cP.mjs} +2 -2
  16. package/.output/server/_ssr/{CompareDrawer-ceW5VxMo.mjs → CompareDrawer-BoxztaO7.mjs} +36 -15
  17. package/.output/server/_ssr/{ProxyViewerContainer-CDfEE_w-.mjs → ProxyViewerContainer-CRBkqFlJ.mjs} +362 -124
  18. package/.output/server/_ssr/{ReplayDialog-V0s_eEbR.mjs → ReplayDialog-Cc1dyDuK.mjs} +14 -7
  19. package/.output/server/_ssr/{RequestAnatomy-f1ccwR9d.mjs → RequestAnatomy-CMGSsz5Z.mjs} +2 -2
  20. package/.output/server/_ssr/{ResponseView-BIRrqG4H.mjs → ResponseView-Cp10DM1D.mjs} +3 -3
  21. package/.output/server/_ssr/{StreamingChunkSequence-V3JFjCgX.mjs → StreamingChunkSequence-B1VGxy3A.mjs} +2 -2
  22. package/.output/server/_ssr/{index-DsykulzS.mjs → index-47XVPghS.mjs} +2 -2
  23. package/.output/server/_ssr/index.mjs +2 -2
  24. package/.output/server/_ssr/{json-viewer-Dcnm0Ivf.mjs → json-viewer-zDE2rrmJ.mjs} +3 -3
  25. package/.output/server/_ssr/{router-4bdm6Mt2.mjs → router-DVeuZFqI.mjs} +10 -5
  26. package/.output/server/{_tanstack-start-manifest_v-Xp4CO64V.mjs → _tanstack-start-manifest_v-Bp8JxtPW.mjs} +1 -1
  27. package/.output/server/index.mjs +68 -68
  28. package/package.json +1 -1
  29. package/src/components/ProxyViewer.tsx +28 -2
  30. package/src/components/clients/ClientLogo.tsx +132 -0
  31. package/src/components/groups/GroupsDialog.tsx +18 -13
  32. package/src/components/proxy-viewer/AgentTraceSummary.tsx +56 -23
  33. package/src/components/proxy-viewer/CompareDrawer.tsx +38 -8
  34. package/src/components/proxy-viewer/ConversationHeader.tsx +33 -41
  35. package/src/components/proxy-viewer/LogEntry.tsx +10 -1
  36. package/src/components/proxy-viewer/LogEntryHeader.tsx +14 -14
  37. package/src/components/proxy-viewer/ProviderLogoStack.tsx +60 -0
  38. package/src/components/proxy-viewer/ReplayDialog.tsx +8 -2
  39. package/src/components/proxy-viewer/ThreadConnector.tsx +18 -7
  40. package/src/components/proxy-viewer/TurnGroup.tsx +14 -25
  41. package/src/components/proxy-viewer/viewerState.ts +5 -2
  42. package/src/lib/sessionInfoContract.ts +1 -0
  43. package/src/lib/stopReason.ts +48 -16
  44. package/src/proxy/sessionInfo.ts +5 -1
  45. package/.output/public/assets/CompareDrawer-DjgjIFx7.js +0 -1
  46. package/.output/public/assets/ProxyViewerContainer-CWUQZLYy.js +0 -106
  47. package/.output/public/assets/ReplayDialog-CU0Tbb2c.js +0 -1
  48. package/.output/public/assets/_sessionId-Cif8JZdn.js +0 -1
  49. package/.output/public/assets/index-D_WfwzUi.js +0 -1
  50. package/.output/public/assets/index-DtLuQrs0.css +0 -1
@@ -1,10 +1,10 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
- import { u as useProviders, D as Dialog, d as DialogContent, e as DialogHeader, h as DialogTitle, T as Tabs, i as TabsList, j as TabsTrigger, k as TabsContent, l as Button, m as TooltipProvider, n as Tooltip, o as TooltipTrigger, p as TooltipContent, q as dispatchLogFocusRequest } from "./ProxyViewerContainer-CDfEE_w-.mjs";
3
- import { ResponseView } from "./ResponseView-BIRrqG4H.mjs";
4
- import { C as CapturedLogSchema } from "./router-4bdm6Mt2.mjs";
2
+ import { u as useProviders, D as Dialog, d as DialogContent, e as DialogHeader, h as DialogTitle, T as Tabs, i as TabsList, j as TabsTrigger, k as TabsContent, l as Button, m as TooltipProvider, n as Tooltip, o as TooltipTrigger, p as TooltipContent, q as dispatchLogFocusRequest } from "./ProxyViewerContainer-CRBkqFlJ.mjs";
3
+ import { ResponseView } from "./ResponseView-Cp10DM1D.mjs";
4
+ import { C as CapturedLogSchema } from "./router-DVeuZFqI.mjs";
5
5
  import "../_libs/jszip.mjs";
6
6
  import "../_libs/modelcontextprotocol__server.mjs";
7
- import { aa as RotateCcw, an as Braces, ao as Minimize2 } from "../_libs/lucide-react.mjs";
7
+ import { a9 as RotateCcw, am as Braces, an as Minimize2 } from "../_libs/lucide-react.mjs";
8
8
  import { d as object, n as number, a as array, c as boolean, b as string } from "../_libs/zod.mjs";
9
9
  import "../_libs/swr.mjs";
10
10
  import "../_libs/use-sync-external-store.mjs";
@@ -315,7 +315,12 @@ function ReplayComparisonTable({
315
315
  ))
316
316
  ] });
317
317
  }
318
- function ReplayDialog({ log, open, onOpenChange }) {
318
+ function ReplayDialog({
319
+ log,
320
+ displayNumber,
321
+ open,
322
+ onOpenChange
323
+ }) {
319
324
  const { providers } = useProviders();
320
325
  const originalBody = log.rawRequestBody ?? "{}";
321
326
  const [modifiedBody, setModifiedBody] = reactExports.useState(() => {
@@ -442,8 +447,10 @@ function ReplayDialog({ log, open, onOpenChange }) {
442
447
  return /* @__PURE__ */ jsxRuntimeExports.jsx(Dialog, { open, onOpenChange: handleClose, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-w-4xl max-h-[85vh] overflow-auto", children: [
443
448
  /* @__PURE__ */ jsxRuntimeExports.jsx(DialogHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
444
449
  /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCcw, { className: "size-4" }),
445
- "Replay Request #",
446
- log.id
450
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { title: `Log ID ${String(log.id)}`, children: [
451
+ "Replay Request #",
452
+ displayNumber
453
+ ] })
447
454
  ] }) }),
448
455
  /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { defaultValue: "modified", children: [
449
456
  /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
@@ -1,6 +1,6 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
- import { u as useProviders, s as analyzeContextIntelligence, f as formatTokens, m as TooltipProvider, c as cn, n as Tooltip, o as TooltipTrigger, p as TooltipContent, S as SegmentBar, R as ROLE_COLOR_CLASSES, A as ANATOMY_ROLE_LABELS, t as formatContextWindowTokens, C as CONTEXT_USAGE_THRESHOLDS } from "./ProxyViewerContainer-CDfEE_w-.mjs";
3
- import "./router-4bdm6Mt2.mjs";
2
+ import { u as useProviders, s as analyzeContextIntelligence, f as formatTokens, m as TooltipProvider, c as cn, n as Tooltip, o as TooltipTrigger, p as TooltipContent, S as SegmentBar, R as ROLE_COLOR_CLASSES, A as ANATOMY_ROLE_LABELS, t as formatContextWindowTokens, C as CONTEXT_USAGE_THRESHOLDS } from "./ProxyViewerContainer-CRBkqFlJ.mjs";
3
+ import "./router-DVeuZFqI.mjs";
4
4
  import "../_libs/modelcontextprotocol__server.mjs";
5
5
  import "../_libs/jszip.mjs";
6
6
  import { I as Info, C as ChevronDown, f as ChevronRight } from "../_libs/lucide-react.mjs";
@@ -1,9 +1,9 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
- import { g as getLogFormatAdapter, f as formatTokens, c as cn, v as getStatusCategory, B as Badge, w as Collapsible, x as CollapsibleTrigger, y as CollapsibleContent, z as ScrollArea, E as LazyJsonViewer, F as safeJsonValue } from "./ProxyViewerContainer-CDfEE_w-.mjs";
3
- import { s as safeGetOwnProperty } from "./router-4bdm6Mt2.mjs";
2
+ import { g as getLogFormatAdapter, f as formatTokens, c as cn, v as getStatusCategory, B as Badge, w as Collapsible, x as CollapsibleTrigger, y as CollapsibleContent, z as ScrollArea, E as LazyJsonViewer, F as safeJsonValue } from "./ProxyViewerContainer-CRBkqFlJ.mjs";
3
+ import { s as safeGetOwnProperty } from "./router-DVeuZFqI.mjs";
4
4
  import "../_libs/jszip.mjs";
5
5
  import "../_libs/modelcontextprotocol__server.mjs";
6
- import { Z as Zap, r as TriangleAlert, ap as CircleStop, B as Brain, C as ChevronDown, f as ChevronRight, m as Terminal } from "../_libs/lucide-react.mjs";
6
+ import { Z as Zap, q as TriangleAlert, ao as CircleStop, B as Brain, C as ChevronDown, f as ChevronRight, m as Terminal } from "../_libs/lucide-react.mjs";
7
7
  import { R as Root } from "../_libs/radix-ui__react-separator.mjs";
8
8
  import "../_libs/swr.mjs";
9
9
  import "../_libs/use-sync-external-store.mjs";
@@ -1,6 +1,6 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
- import { m as TooltipProvider, n as Tooltip, o as TooltipTrigger, B as Badge, p as TooltipContent, E as LazyJsonViewer } from "./ProxyViewerContainer-CDfEE_w-.mjs";
3
- import "./router-4bdm6Mt2.mjs";
2
+ import { m as TooltipProvider, n as Tooltip, o as TooltipTrigger, B as Badge, p as TooltipContent, E as LazyJsonViewer } from "./ProxyViewerContainer-CRBkqFlJ.mjs";
3
+ import "./router-DVeuZFqI.mjs";
4
4
  import "../_libs/modelcontextprotocol__server.mjs";
5
5
  import "../_libs/jszip.mjs";
6
6
  import { C as ChevronDown, f as ChevronRight, L as LoaderCircle } from "../_libs/lucide-react.mjs";
@@ -1,6 +1,6 @@
1
- import { P as ProxyViewerContainer } from "./ProxyViewerContainer-CDfEE_w-.mjs";
1
+ import { P as ProxyViewerContainer } from "./ProxyViewerContainer-CRBkqFlJ.mjs";
2
2
  import "../_libs/react.mjs";
3
- import "./router-4bdm6Mt2.mjs";
3
+ import "./router-DVeuZFqI.mjs";
4
4
  import "../_libs/modelcontextprotocol__server.mjs";
5
5
  import "../_libs/jszip.mjs";
6
6
  import "../_libs/swr.mjs";
@@ -198,7 +198,7 @@ function getResponse() {
198
198
  return event.res;
199
199
  }
200
200
  async function getStartManifest(matchedRoutes) {
201
- const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-Xp4CO64V.mjs");
201
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-Bp8JxtPW.mjs");
202
202
  const startManifest = tsrStartManifest();
203
203
  const rootRoute = startManifest.routes[rootRouteId] = startManifest.routes[rootRouteId] || {};
204
204
  rootRoute.assets = rootRoute.assets || [];
@@ -776,7 +776,7 @@ let entriesPromise;
776
776
  let baseManifestPromise;
777
777
  let cachedFinalManifestPromise;
778
778
  async function loadEntries() {
779
- const routerEntry = await import("./router-4bdm6Mt2.mjs").then((n) => n.E);
779
+ const routerEntry = await import("./router-DVeuZFqI.mjs").then((n) => n.E);
780
780
  const startEntry = await import("./start-HYkvq4Ni.mjs");
781
781
  return { startEntry, routerEntry };
782
782
  }
@@ -1,9 +1,9 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports } from "../_libs/react.mjs";
2
- import { G as parseJsonText, c as cn, m as TooltipProvider, n as Tooltip, o as TooltipTrigger, p as TooltipContent, b as copyTextToClipboard } from "./ProxyViewerContainer-CDfEE_w-.mjs";
3
- import "./router-4bdm6Mt2.mjs";
2
+ import { G as parseJsonText, c as cn, m as TooltipProvider, n as Tooltip, o as TooltipTrigger, p as TooltipContent, b as copyTextToClipboard } from "./ProxyViewerContainer-CRBkqFlJ.mjs";
3
+ import "./router-DVeuZFqI.mjs";
4
4
  import "../_libs/modelcontextprotocol__server.mjs";
5
5
  import "../_libs/jszip.mjs";
6
- import { a as Check, c as Copy, C as ChevronDown, f as ChevronRight, a7 as ChevronsDown } from "../_libs/lucide-react.mjs";
6
+ import { a as Check, c as Copy, C as ChevronDown, f as ChevronRight, a6 as ChevronsDown } from "../_libs/lucide-react.mjs";
7
7
  import "../_libs/swr.mjs";
8
8
  import "../_libs/use-sync-external-store.mjs";
9
9
  import "../_libs/dequal.mjs";
@@ -65,7 +65,7 @@ import "../_libs/immediate.mjs";
65
65
  import "../_libs/setimmediate.mjs";
66
66
  import "../_libs/pako.mjs";
67
67
  const faviconSvg = "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2024%2024'%20role='img'%20aria-label='Agent%20Inspector'%3e%3cg%20fill='none'%20stroke='%23f59e0b'%20stroke-width='1.5'%20stroke-linecap='round'%20stroke-linejoin='round'%20%3e%3cpath%20d='M5%2013%20C5%209%208%207%2012%207%20C16%207%2019%209%2019%2013%20C19%2016%2016%2018%2012%2018%20C8%2018%205%2016%205%2013%20Z'%20/%3e%3cpath%20d='M5%2011%20C3.5%209.5%201.5%2010%202%2012.5%20C2.5%2014%204%2013.5%205%2012.5'%20/%3e%3cpath%20d='M19%2011%20C20.5%209.5%2022.5%2010%2022%2012.5%20C21.5%2014%2020%2013.5%2019%2012.5'%20/%3e%3cline%20x1='10'%20y1='7'%20x2='9.5'%20y2='5'%20/%3e%3cline%20x1='14'%20y1='7'%20x2='14.5'%20y2='5'%20/%3e%3cline%20x1='6.5'%20y1='16'%20x2='4.5'%20y2='19.5'%20/%3e%3cline%20x1='9'%20y1='17.5'%20x2='8'%20y2='20.5'%20/%3e%3cline%20x1='15'%20y1='17.5'%20x2='16'%20y2='20.5'%20/%3e%3cline%20x1='17.5'%20y1='16'%20x2='19.5'%20y2='19.5'%20/%3e%3c/g%3e%3ccircle%20cx='9.5'%20cy='4.5'%20r='0.9'%20fill='%23f59e0b'%20/%3e%3ccircle%20cx='14.5'%20cy='4.5'%20r='0.9'%20fill='%23f59e0b'%20/%3e%3c/svg%3e";
68
- const appCss = "/assets/index-DtLuQrs0.css";
68
+ const appCss = "/assets/index-DdhFqPsI.css";
69
69
  const Route$D = createRootRoute({
70
70
  head: () => ({
71
71
  meta: [
@@ -108,7 +108,7 @@ function RootDocument({ children }) {
108
108
  ] })
109
109
  ] });
110
110
  }
111
- const $$splitComponentImporter$1 = () => import("./index-DsykulzS.mjs");
111
+ const $$splitComponentImporter$1 = () => import("./index-47XVPghS.mjs");
112
112
  const Route$C = createFileRoute("/")({
113
113
  component: lazyRouteComponent($$splitComponentImporter$1, "component")
114
114
  });
@@ -151,7 +151,7 @@ function decodeSessionIdFromPath(encoded) {
151
151
  function getSessionPath(sessionId) {
152
152
  return `/session/${encodeSessionIdForPath(sessionId)}`;
153
153
  }
154
- const $$splitComponentImporter = () => import("../_sessionId-CPAa37n5.mjs");
154
+ const $$splitComponentImporter = () => import("../_sessionId-DF9Sy8cP.mjs");
155
155
  const Route$B = createFileRoute("/session/$sessionId")({
156
156
  component: lazyRouteComponent($$splitComponentImporter, "component"),
157
157
  parseParams: (params) => ({
@@ -1943,6 +1943,10 @@ function buildSessionInfo(input) {
1943
1943
  const inspectorPath = getSessionPath(input.sessionId);
1944
1944
  const apiPath = buildApiPath(input.sessionId);
1945
1945
  const compactLogsPath = buildCompactLogsPath(input.sessionId);
1946
+ const latestLogs = summaries.slice(0, latestLogLimit).map((summary, index) => ({
1947
+ ...summary,
1948
+ sessionLogNumber: Math.max(1, requestCount - index)
1949
+ }));
1946
1950
  return {
1947
1951
  id: input.sessionId,
1948
1952
  status: deriveSessionStatus({
@@ -1982,7 +1986,7 @@ function buildSessionInfo(input) {
1982
1986
  compactLogsPath,
1983
1987
  compactLogsUrl: buildAbsoluteUrl(input.baseUrl, compactLogsPath),
1984
1988
  latestLogLimit,
1985
- latestLogs: summaries.slice(0, latestLogLimit)
1989
+ latestLogs
1986
1990
  };
1987
1991
  }
1988
1992
  const DEFAULT_UPSTREAM$1 = "https://api.anthropic.com";
@@ -7483,6 +7487,7 @@ const SessionTokenUsageSchema = object({
7483
7487
  });
7484
7488
  const SessionLogSummarySchema = object({
7485
7489
  id: number().int().positive(),
7490
+ sessionLogNumber: number().int().positive().optional(),
7486
7491
  timestamp: string(),
7487
7492
  provider: string().nullable(),
7488
7493
  model: string().nullable(),
@@ -8971,7 +8976,7 @@ const Route$w = createFileRoute("/api/models")({
8971
8976
  }
8972
8977
  }
8973
8978
  });
8974
- const version = "2.1.14";
8979
+ const version = "2.1.15";
8975
8980
  const packageJson = {
8976
8981
  version
8977
8982
  };
@@ -1,4 +1,4 @@
1
- const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/__root.tsx", "children": ["/", "/api/alerts", "/api/config", "/api/groups", "/api/health", "/api/logs", "/api/mcp", "/api/models", "/api/providers", "/api/runs", "/api/sessions", "/proxy/$", "/session/$sessionId", "/api/knowledge/candidates", "/api/knowledge/project-context", "/api/knowledge/search", "/api/knowledge/sessions/$sessionId/candidates"], "preloads": ["/assets/main-CSONBwwn.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-D_WfwzUi.js", "/assets/ProxyViewerContainer-CWUQZLYy.js"] }, "/api/alerts": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/alerts.ts", "children": ["/api/alerts/summary"] }, "/api/config": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/config.ts", "children": ["/api/config/paths"] }, "/api/groups": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.ts", "children": ["/api/groups/$groupId"] }, "/api/health": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/health.ts" }, "/api/logs": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.ts", "children": ["/api/logs/$id", "/api/logs/import", "/api/logs/stream"] }, "/api/mcp": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/mcp.ts" }, "/api/models": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/models.ts" }, "/api/providers": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.ts", "children": ["/api/providers/$providerId", "/api/providers/export", "/api/providers/import", "/api/providers/scan"] }, "/api/runs": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/runs.ts", "children": ["/api/runs/$runId"] }, "/api/sessions": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/sessions.ts" }, "/proxy/$": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/proxy/$.ts" }, "/session/$sessionId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/session/$sessionId.tsx", "assets": [], "preloads": ["/assets/_sessionId-Cif8JZdn.js", "/assets/ProxyViewerContainer-CWUQZLYy.js"] }, "/api/alerts/summary": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/alerts.summary.ts" }, "/api/config/paths": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/config.paths.ts" }, "/api/groups/$groupId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.$groupId.ts", "children": ["/api/groups/$groupId/evidence", "/api/groups/$groupId/sessions"] }, "/api/knowledge/candidates": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.ts", "children": ["/api/knowledge/candidates/$candidateId"] }, "/api/knowledge/project-context": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.project-context.ts" }, "/api/knowledge/search": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.search.ts" }, "/api/logs/$id": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.ts", "children": ["/api/logs/$id/body", "/api/logs/$id/chunks", "/api/logs/$id/replay"] }, "/api/logs/import": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.import.ts" }, "/api/logs/stream": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.stream.ts" }, "/api/providers/$providerId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.ts", "children": ["/api/providers/$providerId/model-metadata", "/api/providers/$providerId/test"] }, "/api/providers/export": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.export.ts" }, "/api/providers/import": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.import.ts" }, "/api/providers/scan": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.scan.ts" }, "/api/runs/$runId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/runs.$runId.ts", "children": ["/api/runs/$runId/evidence"] }, "/api/groups/$groupId/evidence": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.$groupId.evidence.ts" }, "/api/groups/$groupId/sessions": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.$groupId.sessions.ts" }, "/api/knowledge/candidates/$candidateId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.$candidateId.ts", "children": ["/api/knowledge/candidates/$candidateId/promote"] }, "/api/logs/$id/body": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.body.ts" }, "/api/logs/$id/chunks": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.chunks.ts" }, "/api/logs/$id/replay": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.replay.ts" }, "/api/providers/$providerId/model-metadata": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.model-metadata.ts" }, "/api/providers/$providerId/test": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.test.ts", "children": ["/api/providers/$providerId/test/log"] }, "/api/runs/$runId/evidence": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/runs.$runId.evidence.ts" }, "/api/knowledge/candidates/$candidateId/promote": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.$candidateId.promote.ts" }, "/api/knowledge/sessions/$sessionId/candidates": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.sessions.$sessionId.candidates.ts" }, "/api/providers/$providerId/test/log": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.test.log.ts" } }, "clientEntry": "/assets/main-CSONBwwn.js" });
1
+ const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/__root.tsx", "children": ["/", "/api/alerts", "/api/config", "/api/groups", "/api/health", "/api/logs", "/api/mcp", "/api/models", "/api/providers", "/api/runs", "/api/sessions", "/proxy/$", "/session/$sessionId", "/api/knowledge/candidates", "/api/knowledge/project-context", "/api/knowledge/search", "/api/knowledge/sessions/$sessionId/candidates"], "preloads": ["/assets/main-DpD1N0S8.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-CI1-G8ua.js", "/assets/ProxyViewerContainer-h851qWNp.js"] }, "/api/alerts": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/alerts.ts", "children": ["/api/alerts/summary"] }, "/api/config": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/config.ts", "children": ["/api/config/paths"] }, "/api/groups": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.ts", "children": ["/api/groups/$groupId"] }, "/api/health": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/health.ts" }, "/api/logs": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.ts", "children": ["/api/logs/$id", "/api/logs/import", "/api/logs/stream"] }, "/api/mcp": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/mcp.ts" }, "/api/models": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/models.ts" }, "/api/providers": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.ts", "children": ["/api/providers/$providerId", "/api/providers/export", "/api/providers/import", "/api/providers/scan"] }, "/api/runs": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/runs.ts", "children": ["/api/runs/$runId"] }, "/api/sessions": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/sessions.ts" }, "/proxy/$": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/proxy/$.ts" }, "/session/$sessionId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/session/$sessionId.tsx", "assets": [], "preloads": ["/assets/_sessionId-DWePGjnS.js", "/assets/ProxyViewerContainer-h851qWNp.js"] }, "/api/alerts/summary": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/alerts.summary.ts" }, "/api/config/paths": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/config.paths.ts" }, "/api/groups/$groupId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.$groupId.ts", "children": ["/api/groups/$groupId/evidence", "/api/groups/$groupId/sessions"] }, "/api/knowledge/candidates": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.ts", "children": ["/api/knowledge/candidates/$candidateId"] }, "/api/knowledge/project-context": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.project-context.ts" }, "/api/knowledge/search": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.search.ts" }, "/api/logs/$id": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.ts", "children": ["/api/logs/$id/body", "/api/logs/$id/chunks", "/api/logs/$id/replay"] }, "/api/logs/import": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.import.ts" }, "/api/logs/stream": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.stream.ts" }, "/api/providers/$providerId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.ts", "children": ["/api/providers/$providerId/model-metadata", "/api/providers/$providerId/test"] }, "/api/providers/export": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.export.ts" }, "/api/providers/import": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.import.ts" }, "/api/providers/scan": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.scan.ts" }, "/api/runs/$runId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/runs.$runId.ts", "children": ["/api/runs/$runId/evidence"] }, "/api/groups/$groupId/evidence": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.$groupId.evidence.ts" }, "/api/groups/$groupId/sessions": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/groups.$groupId.sessions.ts" }, "/api/knowledge/candidates/$candidateId": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.$candidateId.ts", "children": ["/api/knowledge/candidates/$candidateId/promote"] }, "/api/logs/$id/body": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.body.ts" }, "/api/logs/$id/chunks": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.chunks.ts" }, "/api/logs/$id/replay": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/logs.$id.replay.ts" }, "/api/providers/$providerId/model-metadata": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.model-metadata.ts" }, "/api/providers/$providerId/test": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.test.ts", "children": ["/api/providers/$providerId/test/log"] }, "/api/runs/$runId/evidence": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/runs.$runId.evidence.ts" }, "/api/knowledge/candidates/$candidateId/promote": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.candidates.$candidateId.promote.ts" }, "/api/knowledge/sessions/$sessionId/candidates": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/knowledge.sessions.$sessionId.candidates.ts" }, "/api/providers/$providerId/test/log": { "filePath": "C:/Users/claw/workspace/agent-inspector/src/routes/api/providers.$providerId.test.log.ts" } }, "clientEntry": "/assets/main-DpD1N0S8.js" });
2
2
  export {
3
3
  tsrStartManifest
4
4
  };
@@ -38,114 +38,114 @@ const assets = {
38
38
  "/assets/alibaba-TTwafVwX.svg": {
39
39
  "type": "image/svg+xml",
40
40
  "etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
41
- "mtime": "2026-07-04T07:52:09.683Z",
41
+ "mtime": "2026-07-04T08:29:06.393Z",
42
42
  "size": 5915,
43
43
  "path": "../public/assets/alibaba-TTwafVwX.svg"
44
44
  },
45
- "/assets/CompareDrawer-DjgjIFx7.js": {
45
+ "/assets/CompareDrawer-BcEd6V-V.js": {
46
46
  "type": "text/javascript; charset=utf-8",
47
- "etag": '"4abc-0btVE6dbx7Y766NO7PWvF1M1iGU"',
48
- "mtime": "2026-07-04T07:52:09.684Z",
49
- "size": 19132,
50
- "path": "../public/assets/CompareDrawer-DjgjIFx7.js"
47
+ "etag": '"4bbf-xnctyyrpUlw9iwRDiN8n7dDP2ak"',
48
+ "mtime": "2026-07-04T08:29:06.393Z",
49
+ "size": 19391,
50
+ "path": "../public/assets/CompareDrawer-BcEd6V-V.js"
51
51
  },
52
- "/assets/index-DtLuQrs0.css": {
52
+ "/assets/index-DdhFqPsI.css": {
53
53
  "type": "text/css; charset=utf-8",
54
- "etag": '"21518-poRED+tYeP5Qvm+PlJc9ML357lc"',
55
- "mtime": "2026-07-04T07:52:09.684Z",
56
- "size": 136472,
57
- "path": "../public/assets/index-DtLuQrs0.css"
54
+ "etag": '"215a1-oGtBN21bbG9XXw6KUOxGJs8nN5k"',
55
+ "mtime": "2026-07-04T08:29:06.393Z",
56
+ "size": 136609,
57
+ "path": "../public/assets/index-DdhFqPsI.css"
58
58
  },
59
- "/assets/main-CSONBwwn.js": {
59
+ "/assets/index-DjKt8XKe.js": {
60
60
  "type": "text/javascript; charset=utf-8",
61
- "etag": '"512fd-Pu+R/3IFrhjx0jWsnGjOgCHJZcU"',
62
- "mtime": "2026-07-04T07:52:09.684Z",
63
- "size": 332541,
64
- "path": "../public/assets/main-CSONBwwn.js"
61
+ "etag": '"1cd2c-tx2svUPlWKqZmckbUC3QdLcZvAI"',
62
+ "mtime": "2026-07-04T08:29:06.395Z",
63
+ "size": 118060,
64
+ "path": "../public/assets/index-DjKt8XKe.js"
65
65
  },
66
- "/assets/json-viewer-C2JpgcW0.js": {
66
+ "/assets/json-viewer-CkCu-rka.js": {
67
67
  "type": "text/javascript; charset=utf-8",
68
- "etag": '"1ae6-ksk8GSdXp7U3yDJGEPMSct9P4TY"',
69
- "mtime": "2026-07-04T07:52:09.684Z",
68
+ "etag": '"1ae6-2bgY4h+p+tUN/whEBQOWKsJ3qYQ"',
69
+ "mtime": "2026-07-04T08:29:06.395Z",
70
70
  "size": 6886,
71
- "path": "../public/assets/json-viewer-C2JpgcW0.js"
72
- },
73
- "/assets/index-DWOkqdCa.js": {
74
- "type": "text/javascript; charset=utf-8",
75
- "etag": '"1cd2c-CZN4gATtN3nYQYYeMagog4AYNmY"',
76
- "mtime": "2026-07-04T07:52:09.684Z",
77
- "size": 118060,
78
- "path": "../public/assets/index-DWOkqdCa.js"
71
+ "path": "../public/assets/json-viewer-CkCu-rka.js"
79
72
  },
80
- "/assets/index-D_WfwzUi.js": {
73
+ "/assets/index-CI1-G8ua.js": {
81
74
  "type": "text/javascript; charset=utf-8",
82
- "etag": '"74-XQVEvYcBdAT9Y7puJJV+qSF283E"',
83
- "mtime": "2026-07-04T07:52:09.684Z",
75
+ "etag": '"74-EIeYwTAITYEdQc7ccvRe2pZrl1c"',
76
+ "mtime": "2026-07-04T08:29:06.393Z",
84
77
  "size": 116,
85
- "path": "../public/assets/index-D_WfwzUi.js"
78
+ "path": "../public/assets/index-CI1-G8ua.js"
86
79
  },
87
80
  "/assets/minimax-BPMzvuL-.jpeg": {
88
81
  "type": "image/jpeg",
89
82
  "etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
90
- "mtime": "2026-07-04T07:52:09.680Z",
83
+ "mtime": "2026-07-04T08:29:06.393Z",
91
84
  "size": 6918,
92
85
  "path": "../public/assets/minimax-BPMzvuL-.jpeg"
93
86
  },
94
- "/assets/RequestAnatomy-BNahe83D.js": {
87
+ "/assets/ReplayDialog-BNpC0548.js": {
95
88
  "type": "text/javascript; charset=utf-8",
96
- "etag": '"43f2-wVlE0gwF6MdEMb1Gfjgt0becSBc"',
97
- "mtime": "2026-07-04T07:52:09.684Z",
98
- "size": 17394,
99
- "path": "../public/assets/RequestAnatomy-BNahe83D.js"
89
+ "etag": '"31d6-OqVZaeoLKFYexfhY+mk0sL5psk4"',
90
+ "mtime": "2026-07-04T08:29:06.393Z",
91
+ "size": 12758,
92
+ "path": "../public/assets/ReplayDialog-BNpC0548.js"
100
93
  },
101
- "/assets/ReplayDialog-CU0Tbb2c.js": {
94
+ "/assets/RequestAnatomy-Ds1uRLVB.js": {
102
95
  "type": "text/javascript; charset=utf-8",
103
- "etag": '"318c-P6+sTbtOxV0saSnvK6W3UXJF9EY"',
104
- "mtime": "2026-07-04T07:52:09.684Z",
105
- "size": 12684,
106
- "path": "../public/assets/ReplayDialog-CU0Tbb2c.js"
96
+ "etag": '"43f2-4Ax9Y1ZjuvUwkXemqmrSK7aPKqk"',
97
+ "mtime": "2026-07-04T08:29:06.393Z",
98
+ "size": 17394,
99
+ "path": "../public/assets/RequestAnatomy-Ds1uRLVB.js"
107
100
  },
108
- "/assets/ResponseView-DSOnGqi6.js": {
101
+ "/assets/ResponseView-7KPVqKl5.js": {
109
102
  "type": "text/javascript; charset=utf-8",
110
- "etag": '"3fbb-EOmqliYF05GO5dHV6He+ei/tpn0"',
111
- "mtime": "2026-07-04T07:52:09.684Z",
103
+ "etag": '"3fbb-bslwKE65FOBUHRcwxQQU0c94KJE"',
104
+ "mtime": "2026-07-04T08:29:06.395Z",
112
105
  "size": 16315,
113
- "path": "../public/assets/ResponseView-DSOnGqi6.js"
106
+ "path": "../public/assets/ResponseView-7KPVqKl5.js"
107
+ },
108
+ "/assets/StreamingChunkSequence-BHQT261s.js": {
109
+ "type": "text/javascript; charset=utf-8",
110
+ "etag": '"dd6-yiWtPjTykwFM62Q1LOw43GSPAx0"',
111
+ "mtime": "2026-07-04T08:29:06.395Z",
112
+ "size": 3542,
113
+ "path": "../public/assets/StreamingChunkSequence-BHQT261s.js"
114
+ },
115
+ "/assets/zhipuai-BPNAnxo-.svg": {
116
+ "type": "image/svg+xml",
117
+ "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
118
+ "mtime": "2026-07-04T08:29:06.393Z",
119
+ "size": 11256,
120
+ "path": "../public/assets/zhipuai-BPNAnxo-.svg"
114
121
  },
115
- "/assets/_sessionId-Cif8JZdn.js": {
122
+ "/assets/_sessionId-DWePGjnS.js": {
116
123
  "type": "text/javascript; charset=utf-8",
117
- "etag": '"d2-iFcuyyuI3PczaYjsggRN0OVh2TM"',
118
- "mtime": "2026-07-04T07:52:09.684Z",
124
+ "etag": '"d2-ONKmnhfoA+60/NY+/v084oYX2M4"',
125
+ "mtime": "2026-07-04T08:29:06.393Z",
119
126
  "size": 210,
120
- "path": "../public/assets/_sessionId-Cif8JZdn.js"
127
+ "path": "../public/assets/_sessionId-DWePGjnS.js"
121
128
  },
122
- "/assets/StreamingChunkSequence-BEKTDklB.js": {
129
+ "/assets/main-DpD1N0S8.js": {
123
130
  "type": "text/javascript; charset=utf-8",
124
- "etag": '"dd6-PQOSC6/UIv02KydJR9fuolk+zmw"',
125
- "mtime": "2026-07-04T07:52:09.684Z",
126
- "size": 3542,
127
- "path": "../public/assets/StreamingChunkSequence-BEKTDklB.js"
131
+ "etag": '"512fd-MPKJm1sPG3F35PU/2xjL93KYUyo"',
132
+ "mtime": "2026-07-04T08:29:06.393Z",
133
+ "size": 332541,
134
+ "path": "../public/assets/main-DpD1N0S8.js"
128
135
  },
129
136
  "/assets/qwen-CONDcHqt.png": {
130
137
  "type": "image/png",
131
138
  "etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
132
- "mtime": "2026-07-04T07:52:09.683Z",
139
+ "mtime": "2026-07-04T08:29:06.393Z",
133
140
  "size": 357059,
134
141
  "path": "../public/assets/qwen-CONDcHqt.png"
135
142
  },
136
- "/assets/zhipuai-BPNAnxo-.svg": {
137
- "type": "image/svg+xml",
138
- "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
139
- "mtime": "2026-07-04T07:52:09.683Z",
140
- "size": 11256,
141
- "path": "../public/assets/zhipuai-BPNAnxo-.svg"
142
- },
143
- "/assets/ProxyViewerContainer-CWUQZLYy.js": {
143
+ "/assets/ProxyViewerContainer-h851qWNp.js": {
144
144
  "type": "text/javascript; charset=utf-8",
145
- "etag": '"a7f3e-1JdLbEI7EAIFprw9s8hx8mvoRps"',
146
- "mtime": "2026-07-04T07:52:09.684Z",
147
- "size": 687934,
148
- "path": "../public/assets/ProxyViewerContainer-CWUQZLYy.js"
145
+ "etag": '"a8fdb-3dhZI4F1WvCGzLHRBgWIFmmvah0"',
146
+ "mtime": "2026-07-04T08:29:06.395Z",
147
+ "size": 692187,
148
+ "path": "../public/assets/ProxyViewerContainer-h851qWNp.js"
149
149
  }
150
150
  };
151
151
  function readAsset(id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonyclaw/agent-inspector",
3
- "version": "2.1.14",
3
+ "version": "2.1.15",
4
4
  "type": "module",
5
5
  "description": "Agent observability and knowledge capture layer for AI coding tools.",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@ import { formatTimestampRange } from "../lib/timeDisplay";
40
40
  import { cn, formatContextWindowTokens, formatTokens } from "../lib/utils";
41
41
  import { useProviders } from "../lib/useProviders";
42
42
  import packageJson from "../../package.json";
43
- import { groupLogsByConversation } from "./proxy-viewer";
43
+ import { groupLogsByConversation, type ConversationGroupData } from "./proxy-viewer";
44
44
 
45
45
  import { CrabLogo } from "./ui/crab-logo";
46
46
  import { crabVariants } from "./ui/crab-variants";
@@ -72,6 +72,25 @@ function truncateSessionId(id: string): string {
72
72
  return `${id.slice(0, 12)}...${id.slice(-12)}`;
73
73
  }
74
74
 
75
+ function buildDisplayNumberByLogId(groups: readonly ConversationGroupData[]): Map<number, number> {
76
+ const result = new Map<number, number>();
77
+ for (const group of groups) {
78
+ for (let index = 0; index < group.logs.length; index += 1) {
79
+ const log = group.logs[index];
80
+ if (log === undefined) continue;
81
+ result.set(log.id, index + 1);
82
+ }
83
+ }
84
+ return result;
85
+ }
86
+
87
+ function displayNumberForLog(
88
+ log: CapturedLog,
89
+ displayNumberByLogId: ReadonlyMap<number, number>,
90
+ ): number {
91
+ return displayNumberByLogId.get(log.id) ?? log.id;
92
+ }
93
+
75
94
  export function shouldShowRawExport(captureMode: CaptureMode): boolean {
76
95
  return captureMode === "full";
77
96
  }
@@ -1404,6 +1423,7 @@ export function ProxyViewer({
1404
1423
  }, []);
1405
1424
 
1406
1425
  const groups = useMemo(() => groupLogsByConversation(logs), [logs]);
1426
+ const displayNumberByLogId = useMemo(() => buildDisplayNumberByLogId(groups), [groups]);
1407
1427
  const sessionContextScope = useMemo(
1408
1428
  () => resolveSessionContextScope({ pinnedSessionId, selectedSession, sessions, logs }),
1409
1429
  [logs, pinnedSessionId, selectedSession, sessions],
@@ -1733,7 +1753,13 @@ export function ProxyViewer({
1733
1753
  {/* Compare drawer: sibling of the log list, not a route change. */}
1734
1754
  {comparePair !== null && (
1735
1755
  <Suspense fallback={null}>
1736
- <LazyCompareDrawer left={comparePair[0]} right={comparePair[1]} onClose={closeCompare} />
1756
+ <LazyCompareDrawer
1757
+ left={comparePair[0]}
1758
+ right={comparePair[1]}
1759
+ leftDisplayNumber={displayNumberForLog(comparePair[0], displayNumberByLogId)}
1760
+ rightDisplayNumber={displayNumberForLog(comparePair[1], displayNumberByLogId)}
1761
+ onClose={closeCompare}
1762
+ />
1737
1763
  </Suspense>
1738
1764
  )}
1739
1765
  </div>
@@ -0,0 +1,132 @@
1
+ import type { JSX } from "react";
2
+ import { cn } from "../../lib/utils";
3
+
4
+ export type ClientApp = "codex" | "opencode" | "mimo-code" | "claude-code" | "unknown";
5
+
6
+ export type ClientAppDetectionInput = {
7
+ userAgent?: string | null;
8
+ conversationId?: string | null;
9
+ clientProjectFolder?: string | null;
10
+ };
11
+
12
+ const CLIENT_LABELS: Record<Exclude<ClientApp, "unknown">, string> = {
13
+ codex: "Codex",
14
+ opencode: "OpenCode",
15
+ "mimo-code": "MiMo Code",
16
+ "claude-code": "Claude Code",
17
+ };
18
+
19
+ function normalize(value: string | null | undefined): string {
20
+ return value?.toLowerCase() ?? "";
21
+ }
22
+
23
+ function matchesAny(value: string, candidates: readonly string[]): boolean {
24
+ for (const candidate of candidates) {
25
+ if (value.includes(candidate)) return true;
26
+ }
27
+ return false;
28
+ }
29
+
30
+ export function detectClientApp(input: ClientAppDetectionInput): ClientApp {
31
+ const haystack = [
32
+ normalize(input.userAgent),
33
+ normalize(input.conversationId),
34
+ normalize(input.clientProjectFolder),
35
+ ].join(" ");
36
+
37
+ if (matchesAny(haystack, ["mimocode", "mimo-code", "mimo code"])) return "mimo-code";
38
+ if (matchesAny(haystack, ["opencode", "open-code", "open code"])) return "opencode";
39
+ if (matchesAny(haystack, ["claude-code", "claude code", "claudecode"])) return "claude-code";
40
+ if (matchesAny(haystack, ["codex desktop", "codex-cli", "openai-codex", "codex"])) {
41
+ return "codex";
42
+ }
43
+
44
+ return "unknown";
45
+ }
46
+
47
+ export function clientAppLabel(client: ClientApp): string | null {
48
+ switch (client) {
49
+ case "codex":
50
+ case "opencode":
51
+ case "mimo-code":
52
+ case "claude-code":
53
+ return CLIENT_LABELS[client];
54
+ case "unknown":
55
+ return null;
56
+ }
57
+ }
58
+
59
+ function CodexMark(): JSX.Element {
60
+ return (
61
+ <svg viewBox="0 0 24 24" className="size-full" aria-hidden="true">
62
+ <circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" strokeWidth="2.2" />
63
+ <path
64
+ d="M14.8 8.6a4.8 4.8 0 1 0 0 6.8"
65
+ fill="none"
66
+ stroke="currentColor"
67
+ strokeLinecap="round"
68
+ strokeWidth="2.2"
69
+ />
70
+ </svg>
71
+ );
72
+ }
73
+
74
+ function OpenCodeMark(): JSX.Element {
75
+ return (
76
+ <svg viewBox="0 0 24 24" className="size-full" aria-hidden="true">
77
+ <path d="M7 4h10v16H7z" fill="none" stroke="currentColor" strokeWidth="2.2" />
78
+ <path d="M10 8h4M10 12h4M10 16h2" stroke="currentColor" strokeLinecap="round" />
79
+ </svg>
80
+ );
81
+ }
82
+
83
+ function MiMoCodeMark(): JSX.Element {
84
+ return (
85
+ <svg viewBox="0 0 24 24" className="size-full" aria-hidden="true">
86
+ <path
87
+ d="M5 18V6h3.2l3.8 6.1L15.8 6H19v12h-3V11.3l-3.1 5h-1.8L8 11.3V18z"
88
+ fill="currentColor"
89
+ />
90
+ </svg>
91
+ );
92
+ }
93
+
94
+ function ClaudeCodeMark(): JSX.Element {
95
+ return (
96
+ <svg viewBox="0 0 24 24" className="size-full" aria-hidden="true">
97
+ <path
98
+ fill="currentColor"
99
+ fillRule="evenodd"
100
+ clipRule="evenodd"
101
+ d="M21 10.95h3v3.1h-3v3.03h-1.49V20H18v-2.92h-1.49V20H15v-2.92H9V20H7.49v-2.92H6V20H4.49v-2.92H3v-3.03H0v-3.1h3V5h18v5.95zM6 10.95h1.49V8.1H6v2.85zm10.51 0H18V8.1h-1.49v2.85z"
102
+ />
103
+ </svg>
104
+ );
105
+ }
106
+
107
+ export function ClientLogo({
108
+ client,
109
+ className,
110
+ }: {
111
+ client: ClientApp;
112
+ className?: string;
113
+ }): JSX.Element | null {
114
+ const label = clientAppLabel(client);
115
+ if (label === null) return null;
116
+
117
+ return (
118
+ <span
119
+ className={cn(
120
+ "inline-flex size-6 shrink-0 items-center justify-center rounded-md border border-white/10 bg-black/20 p-1 text-cyan-100 shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]",
121
+ className,
122
+ )}
123
+ aria-label={label}
124
+ title={label}
125
+ >
126
+ {client === "codex" && <CodexMark />}
127
+ {client === "opencode" && <OpenCodeMark />}
128
+ {client === "mimo-code" && <MiMoCodeMark />}
129
+ {client === "claude-code" && <ClaudeCodeMark />}
130
+ </span>
131
+ );
132
+ }