@tonyclaw/agent-inspector 2.1.17 → 2.1.19

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-B9sLBHw5.js → CompareDrawer-CHXN5HYU.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-TZJV7HhE.js +106 -0
  4. package/.output/public/assets/{ReplayDialog-D9I9W9n3.js → ReplayDialog-DQ7nS5Rr.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-DGXK_Rii.js → RequestAnatomy-DXWweH_2.js} +1 -1
  6. package/.output/public/assets/{ResponseView-df_JzwsS.js → ResponseView-3PimPfiD.js} +2 -2
  7. package/.output/public/assets/{StreamingChunkSequence-i3DM5IKL.js → StreamingChunkSequence-4v52v-7a.js} +1 -1
  8. package/.output/public/assets/_sessionId-DfkBPvCC.js +1 -0
  9. package/.output/public/assets/{index-Dqc2r1ea.js → index-BuhFRImh.js} +1 -1
  10. package/.output/public/assets/index-ChgjUix0.css +1 -0
  11. package/.output/public/assets/index-Uf87L52J.js +1 -0
  12. package/.output/public/assets/{json-viewer-D3QWQlSB.js → json-viewer-d2gWBDLO.js} +1 -1
  13. package/.output/public/assets/{main-ZhxhOCmF.js → main-Do-E-46G.js} +2 -2
  14. package/.output/server/{_sessionId-YFrP0lZP.mjs → _sessionId-B82aMW2A.mjs} +2 -2
  15. package/.output/server/_ssr/{CompareDrawer-qC3adEeq.mjs → CompareDrawer-Bud_yIrM.mjs} +2 -2
  16. package/.output/server/_ssr/{ProxyViewerContainer-DCZIJC2b.mjs → ProxyViewerContainer-DqgVb0So.mjs} +1976 -1988
  17. package/.output/server/_ssr/{ReplayDialog-CvX4XYCi.mjs → ReplayDialog-DKbV5fnW.mjs} +3 -3
  18. package/.output/server/_ssr/{RequestAnatomy-HQfrxjO1.mjs → RequestAnatomy-MStxp1Wp.mjs} +2 -2
  19. package/.output/server/_ssr/{ResponseView-QSlSD11e.mjs → ResponseView-HLTCpJcF.mjs} +2 -2
  20. package/.output/server/_ssr/{StreamingChunkSequence-DTAiwlnI.mjs → StreamingChunkSequence-DEiyiUs_.mjs} +2 -2
  21. package/.output/server/_ssr/{index-D_EzfXaN.mjs → index-DamOPFDI.mjs} +2 -2
  22. package/.output/server/_ssr/index.mjs +2 -2
  23. package/.output/server/_ssr/{json-viewer-CrIX0e0Q.mjs → json-viewer-MmTWKxqh.mjs} +2 -2
  24. package/.output/server/_ssr/{router-__o2wrfO.mjs → router-50BIB6ED.mjs} +545 -22
  25. package/.output/server/{_tanstack-start-manifest_v-DbqyLpsI.mjs → _tanstack-start-manifest_v-TSwb1vFc.mjs} +1 -1
  26. package/.output/server/index.mjs +66 -66
  27. package/package.json +1 -1
  28. package/src/components/proxy-viewer/ConversationGroupList.tsx +11 -11
  29. package/src/components/proxy-viewer/ConversationHeader.tsx +7 -7
  30. package/src/components/proxy-viewer/LogEntry.tsx +15 -26
  31. package/src/components/proxy-viewer/LogEntryHeader.tsx +11 -14
  32. package/src/components/proxy-viewer/RequestToolsPanel.tsx +15 -15
  33. package/src/components/proxy-viewer/ThreadConnector.tsx +75 -91
  34. package/src/components/proxy-viewer/ToolTraceEvents.tsx +1 -1
  35. package/src/components/proxy-viewer/TurnGroup.tsx +11 -14
  36. package/src/mcp/server.ts +275 -0
  37. package/src/mcp/toolHandlers.ts +380 -3
  38. package/src/proxy/logSearch.ts +3 -0
  39. package/src/proxy/store.ts +40 -15
  40. package/src/routes/api/logs.ts +8 -2
  41. package/.output/public/assets/ProxyViewerContainer-DbWjc_BE.js +0 -106
  42. package/.output/public/assets/_sessionId-CTpCHdFh.js +0 -1
  43. package/.output/public/assets/index-Blqvndcn.js +0 -1
  44. package/.output/public/assets/index-Cs79WRQj.css +0 -1
@@ -1,5 +1,5 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
- import { I as InspectorGroupsListResponseSchema, C as CapturedLogSchema, D as DEFAULT_CAPTURE_MODE, a as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, b as DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS, c as DEFAULT_TIME_DISPLAY_FORMAT, d as RuntimeConfigSchema, p as packageJson, P as ProviderConfigSchema, i as apiFormatForPath, n as GroupEvidenceExportResultSchema, o as DeleteInspectorGroupResponseSchema, y as createPendingProviderTestResults, z as ProviderTestResultsSchema, B as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, E as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, T as TimeDisplayFormatSchema, l as AlertSummarySchema, m as AlertListResponseSchema, G as GroupEvidenceReadResponseSchema, t as maskApiKey, u as PATH_V1_MESSAGES, v as PATH_V1_CHAT_COMPLETIONS, w as PATH_V1_RESPONSES, q as providerHasContextMetadata, r as findProviderModelMetadata, f as parseOpenAIResponse, g as parseOpenAIResponsesResponse, O as OpenAIRequestSchema, h as OpenAIResponsesRequestSchema, A as AnthropicResponseSchema$1, e as AnthropicRequestSchema, s as safeGetOwnProperty, F as resolveProviderContextWindow, j as getSessionPath, x as previewUpstreamUrl, K as KnowledgeCandidateSchema, H as isPlainRecord, k as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema } from "./router-__o2wrfO.mjs";
2
+ import { I as InspectorGroupsListResponseSchema, C as CapturedLogSchema, D as DEFAULT_CAPTURE_MODE, a as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, b as DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS, c as DEFAULT_TIME_DISPLAY_FORMAT, d as RuntimeConfigSchema, p as packageJson, P as ProviderConfigSchema, i as apiFormatForPath, n as GroupEvidenceExportResultSchema, o as DeleteInspectorGroupResponseSchema, y as createPendingProviderTestResults, z as ProviderTestResultsSchema, B as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, E as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, T as TimeDisplayFormatSchema, l as AlertSummarySchema, m as AlertListResponseSchema, G as GroupEvidenceReadResponseSchema, t as maskApiKey, u as PATH_V1_MESSAGES, v as PATH_V1_CHAT_COMPLETIONS, w as PATH_V1_RESPONSES, q as providerHasContextMetadata, r as findProviderModelMetadata, f as parseOpenAIResponse, g as parseOpenAIResponsesResponse, O as OpenAIRequestSchema, h as OpenAIResponsesRequestSchema, A as AnthropicResponseSchema$1, e as AnthropicRequestSchema, s as safeGetOwnProperty, F as resolveProviderContextWindow, j as getSessionPath, x as previewUpstreamUrl, K as KnowledgeCandidateSchema, H as isPlainRecord, k as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema } from "./router-50BIB6ED.mjs";
3
3
  import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
4
4
  import { J as JSZip } from "../_libs/jszip.mjs";
5
5
  import { c as clsx } from "../_libs/clsx.mjs";
@@ -1481,7 +1481,7 @@ function resolveLogFormat(log) {
1481
1481
  const pathFormat = apiFormatForPath(log.path);
1482
1482
  return pathFormat === "unknown" ? log.apiFormat : pathFormat;
1483
1483
  }
1484
- const HEADER_ACTION_BUTTON_CLASS$1 = "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";
1484
+ const HEADER_ACTION_BUTTON_CLASS$1 = "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";
1485
1485
  function HeaderMetric({
1486
1486
  icon,
1487
1487
  children,
@@ -1492,7 +1492,7 @@ function HeaderMetric({
1492
1492
  "span",
1493
1493
  {
1494
1494
  className: cn(
1495
- "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",
1495
+ "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",
1496
1496
  className
1497
1497
  ),
1498
1498
  title,
@@ -1556,8 +1556,8 @@ function ConversationHeader({
1556
1556
  "data-nav-id": `conv-${conversationId}`,
1557
1557
  "data-nav-action": expanded ? "collapse" : "expand",
1558
1558
  className: cn(
1559
- "border border-border bg-card shadow-sm flex cursor-pointer items-start gap-3 rounded-[8px] px-3 py-2.5 transition-colors",
1560
- "hover:border-cyan-200/20",
1559
+ "bg-card/80 shadow-sm flex cursor-pointer items-start gap-3 rounded-[8px] px-3 py-2.5 transition-colors",
1560
+ "hover:bg-card",
1561
1561
  "select-none",
1562
1562
  "sticky top-0 z-10 mb-2",
1563
1563
  "focus-visible:ring-1 focus-visible:ring-cyan-300/35 focus-visible:outline-none"
@@ -1609,7 +1609,7 @@ function ConversationHeader({
1609
1609
  !isFallbackConversation && showProcessMetadata && clientPid !== null && clientPid !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1610
1610
  "span",
1611
1611
  {
1612
- 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",
1612
+ className: "bg-white/[0.045] shrink-0 rounded px-1.5 py-0.5 font-mono text-xs tabular-nums text-muted-foreground",
1613
1613
  title: `Client process ID ${clientPid}`,
1614
1614
  children: [
1615
1615
  "PID ",
@@ -1628,7 +1628,7 @@ function ConversationHeader({
1628
1628
  showClientIdentity && clientDisplayLabel !== null && clientDisplayLabel !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1629
1629
  "span",
1630
1630
  {
1631
- 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",
1631
+ 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",
1632
1632
  title: clientTooltip ?? clientDisplayLabel,
1633
1633
  children: [
1634
1634
  /* @__PURE__ */ jsxRuntimeExports.jsx(ClientLogo, { client: clientApp, className: "size-4 shrink-0" }),
@@ -1678,7 +1678,7 @@ function ConversationHeader({
1678
1678
  /* @__PURE__ */ jsxRuntimeExports.jsx(
1679
1679
  "span",
1680
1680
  {
1681
- 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",
1681
+ 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",
1682
1682
  "aria-hidden": "true",
1683
1683
  children: expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 shrink-0" }) : isLoading ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-4 shrink-0 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4 shrink-0" })
1684
1684
  }
@@ -1959,27 +1959,27 @@ function useCopyFeedback(text) {
1959
1959
  return { copied, copy };
1960
1960
  }
1961
1961
  const LazyCompareDrawer = reactExports.lazy(
1962
- () => import("./CompareDrawer-qC3adEeq.mjs").then((m) => ({ default: m.CompareDrawer }))
1962
+ () => import("./CompareDrawer-Bud_yIrM.mjs").then((m) => ({ default: m.CompareDrawer }))
1963
1963
  );
1964
1964
  const LazyReplayDialog = reactExports.lazy(
1965
- () => import("./ReplayDialog-CvX4XYCi.mjs").then((m) => ({ default: m.ReplayDialog }))
1965
+ () => import("./ReplayDialog-DKbV5fnW.mjs").then((m) => ({ default: m.ReplayDialog }))
1966
1966
  );
1967
1967
  const LazyRequestAnatomy = reactExports.lazy(
1968
- () => import("./RequestAnatomy-HQfrxjO1.mjs").then((m) => ({ default: m.RequestAnatomy }))
1968
+ () => import("./RequestAnatomy-MStxp1Wp.mjs").then((m) => ({ default: m.RequestAnatomy }))
1969
1969
  );
1970
1970
  const LazyResponseView = reactExports.lazy(
1971
- () => import("./ResponseView-QSlSD11e.mjs").then((m) => ({ default: m.ResponseView }))
1971
+ () => import("./ResponseView-HLTCpJcF.mjs").then((m) => ({ default: m.ResponseView }))
1972
1972
  );
1973
1973
  const LazyStreamingChunkSequence = reactExports.lazy(
1974
- () => import("./StreamingChunkSequence-DTAiwlnI.mjs").then((m) => ({
1974
+ () => import("./StreamingChunkSequence-DEiyiUs_.mjs").then((m) => ({
1975
1975
  default: m.StreamingChunkSequence
1976
1976
  }))
1977
1977
  );
1978
1978
  const LazyJsonViewer = reactExports.lazy(
1979
- () => import("./json-viewer-CrIX0e0Q.mjs").then((m) => ({ default: m.JsonViewer }))
1979
+ () => import("./json-viewer-MmTWKxqh.mjs").then((m) => ({ default: m.JsonViewer }))
1980
1980
  );
1981
1981
  const LazyJsonViewerFromString = reactExports.lazy(
1982
- () => import("./json-viewer-CrIX0e0Q.mjs").then((m) => ({ default: m.JsonViewerFromString }))
1982
+ () => import("./json-viewer-MmTWKxqh.mjs").then((m) => ({ default: m.JsonViewerFromString }))
1983
1983
  );
1984
1984
  const HIGHLIGHT_DURATION_MS = 1200;
1985
1985
  const MAX_HIGHLIGHT_ATTEMPTS = 12;
@@ -2439,10 +2439,10 @@ function Badge({
2439
2439
  );
2440
2440
  }
2441
2441
  const STATUS_BADGE_CLASSES = {
2442
- success: "bg-slate-300/[0.08] text-slate-100 border-slate-300/20",
2443
- client_error: "bg-amber-300/[0.10] text-amber-100 border-amber-300/30",
2444
- server_error: "bg-rose-400/[0.10] text-rose-100 border-rose-300/30",
2445
- pending: "bg-cyan-300/[0.08] text-cyan-100 border-cyan-300/25"
2442
+ success: "bg-slate-300/[0.08] text-slate-100 border-transparent",
2443
+ client_error: "bg-amber-300/[0.10] text-amber-100 border-transparent",
2444
+ server_error: "bg-rose-400/[0.10] text-rose-100 border-transparent",
2445
+ pending: "bg-cyan-300/[0.08] text-cyan-100 border-transparent"
2446
2446
  };
2447
2447
  const STATUS_CUE_CLASSES = {
2448
2448
  success: {
@@ -2462,8 +2462,8 @@ const STATUS_CUE_CLASSES = {
2462
2462
  dot: "bg-cyan-200 shadow-[0_0_14px_rgba(103,232,249,0.8)]"
2463
2463
  }
2464
2464
  };
2465
- const HEADER_ACTION_BUTTON_CLASS = "border border-input bg-background hover:bg-accent hover:text-accent-foreground size-8 text-muted-foreground shadow-none hover:text-cyan-100";
2466
- const TELEMETRY_CHIP_CLASS = "border border-border bg-muted/40 flex h-7 items-center gap-1 rounded-md px-2 text-xs";
2465
+ const HEADER_ACTION_BUTTON_CLASS = "border border-transparent bg-white/[0.045] hover:bg-white/[0.075] size-8 text-muted-foreground shadow-none hover:text-cyan-100";
2466
+ const TELEMETRY_CHIP_CLASS = "bg-white/[0.045] flex h-7 items-center gap-1 rounded-md px-2 text-xs";
2467
2467
  function formatElapsed$3(ms) {
2468
2468
  if (ms < 1e3) return `${ms}ms`;
2469
2469
  return `${(ms / 1e3).toFixed(1)}s`;
@@ -2537,8 +2537,8 @@ const LogEntryHeader = reactExports.memo(function({
2537
2537
  "data-nav-action": expanded ? "collapse" : "expand",
2538
2538
  className: cn(
2539
2539
  "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]",
2540
- expanded ? "bg-white/[0.035] shadow-[inset_0_1px_0_rgba(255,255,255,0.10)]" : "bg-transparent hover:bg-white/[0.035]",
2541
- "select-none before:pointer-events-none before:absolute before:inset-x-3 before:top-0 before:h-px before:bg-white/12",
2540
+ expanded ? "bg-white/[0.04]" : "bg-transparent hover:bg-white/[0.035]",
2541
+ "select-none",
2542
2542
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
2543
2543
  ),
2544
2544
  onClick: onToggle,
@@ -2554,7 +2554,7 @@ const LogEntryHeader = reactExports.memo(function({
2554
2554
  "span",
2555
2555
  {
2556
2556
  className: cn(
2557
- "border border-border bg-muted/40 inline-flex h-8 shrink-0 items-center gap-2 rounded-md px-2 font-mono",
2557
+ "inline-flex h-8 shrink-0 items-center gap-2 rounded-md px-2 font-mono",
2558
2558
  statusCue.frame
2559
2559
  ),
2560
2560
  children: [
@@ -2566,7 +2566,7 @@ const LogEntryHeader = reactExports.memo(function({
2566
2566
  ]
2567
2567
  }
2568
2568
  ),
2569
- /* @__PURE__ */ jsxRuntimeExports.jsxs("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", children: [
2569
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "bg-white/[0.045] flex h-7 shrink-0 items-center gap-1 rounded-md px-2 text-xs text-muted-foreground", children: [
2570
2570
  /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
2571
2571
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", title: log.timestamp, children: formatTimestamp$2(log.timestamp, timeDisplayFormat) })
2572
2572
  ] }),
@@ -2823,7 +2823,7 @@ const LogEntryHeader = reactExports.memo(function({
2823
2823
  /* @__PURE__ */ jsxRuntimeExports.jsx(
2824
2824
  "span",
2825
2825
  {
2826
- 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",
2826
+ 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",
2827
2827
  "aria-hidden": "true",
2828
2828
  children: expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4 shrink-0" })
2829
2829
  }
@@ -2911,13 +2911,13 @@ const CATEGORY_LABELS = {
2911
2911
  other: "other"
2912
2912
  };
2913
2913
  const CATEGORY_CLASSES = {
2914
- file: "border-blue-400/20 bg-blue-400/5 text-blue-300",
2915
- shell: "border-teal-400/20 bg-teal-400/5 text-teal-300",
2916
- browser: "border-cyan-400/20 bg-cyan-400/5 text-cyan-300",
2917
- web: "border-sky-400/20 bg-sky-400/5 text-sky-300",
2918
- mcp: "border-violet-400/20 bg-violet-400/5 text-violet-300",
2919
- code: "border-emerald-400/20 bg-emerald-400/5 text-emerald-300",
2920
- other: "border-muted-foreground/20 bg-muted/30 text-muted-foreground"
2914
+ file: "border-transparent bg-blue-400/5 text-blue-300",
2915
+ shell: "border-transparent bg-teal-400/5 text-teal-300",
2916
+ browser: "border-transparent bg-cyan-400/5 text-cyan-300",
2917
+ web: "border-transparent bg-sky-400/5 text-sky-300",
2918
+ mcp: "border-transparent bg-violet-400/5 text-violet-300",
2919
+ code: "border-transparent bg-emerald-400/5 text-emerald-300",
2920
+ other: "border-transparent bg-muted/30 text-muted-foreground"
2921
2921
  };
2922
2922
  function CategoryIcon({ category }) {
2923
2923
  switch (category) {
@@ -2960,7 +2960,7 @@ function ToolRow({ tool, called }) {
2960
2960
  const [open, setOpen] = reactExports.useState(false);
2961
2961
  const nameCopy = useCopyFeedback(tool.name);
2962
2962
  const hasDetails = tool.description !== null || tool.requiredParameters.length > 0 || tool.schema !== null;
2963
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Collapsible, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-border bg-card shadow-sm rounded-[8px]", children: [
2963
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Collapsible, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white/[0.035] rounded-[8px]", children: [
2964
2964
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2 px-2.5 py-2", children: [
2965
2965
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
2966
2966
  "button",
@@ -2995,17 +2995,17 @@ function ToolRow({ tool, called }) {
2995
2995
  "button",
2996
2996
  {
2997
2997
  type: "button",
2998
- 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",
2998
+ 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",
2999
2999
  onClick: nameCopy.copy,
3000
3000
  "aria-label": nameCopy.copied ? "Copied tool name" : "Copy tool name",
3001
3001
  children: nameCopy.copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-400" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
3002
3002
  }
3003
3003
  )
3004
3004
  ] }),
3005
- /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: open && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 border-t border-white/10 bg-white/[0.025] px-2.5 py-2", children: [
3005
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: open && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 bg-white/[0.025] px-2.5 py-2", children: [
3006
3006
  tool.description !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: tool.description }),
3007
3007
  /* @__PURE__ */ jsxRuntimeExports.jsx(RequiredParameters, { names: tool.requiredParameters }),
3008
- tool.schema !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "border border-border bg-card shadow-sm max-h-64 rounded-[8px] p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3008
+ tool.schema !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "bg-black/[0.16] max-h-64 rounded-[8px] p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3009
3009
  reactExports.Suspense,
3010
3010
  {
3011
3011
  fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: "Loading JSON..." }),
@@ -3035,7 +3035,7 @@ const RequestTools = reactExports.memo(function RequestTools2({
3035
3035
  if (summary === null) return null;
3036
3036
  const previewTools = summary.tools.slice(0, 6);
3037
3037
  const hiddenPreviewCount = summary.tools.length - previewTools.length;
3038
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "border border-border bg-card shadow-sm mb-3 overflow-hidden rounded-[8px]", children: [
3038
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "bg-black/[0.10] mb-3 overflow-hidden rounded-[8px]", children: [
3039
3039
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2 px-3 py-2", children: [
3040
3040
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
3041
3041
  /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-4 shrink-0 text-sky-300/80" }),
@@ -3089,7 +3089,7 @@ const RequestTools = reactExports.memo(function RequestTools2({
3089
3089
  }
3090
3090
  )
3091
3091
  ] }),
3092
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap gap-1.5 border-t border-white/10 bg-white/[0.025] px-3 py-2", children: [
3092
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap gap-1.5 bg-white/[0.025] px-3 py-2", children: [
3093
3093
  previewTools.map((tool) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3094
3094
  Badge,
3095
3095
  {
@@ -3107,8 +3107,8 @@ const RequestTools = reactExports.memo(function RequestTools2({
3107
3107
  hiddenPreviewCount
3108
3108
  ] })
3109
3109
  ] }),
3110
- expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 border-t border-white/10 bg-black/10 px-3 py-3", children: [
3111
- /* @__PURE__ */ jsxRuntimeExports.jsxs("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", children: [
3110
+ expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 bg-black/10 px-3 py-3", children: [
3111
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "bg-white/[0.045] hover:bg-white/[0.075] flex h-8 items-center gap-2 rounded-md px-2 text-xs", children: [
3112
3112
  /* @__PURE__ */ jsxRuntimeExports.jsx(Search, { className: "size-3.5 shrink-0 text-muted-foreground" }),
3113
3113
  /* @__PURE__ */ jsxRuntimeExports.jsx(
3114
3114
  "input",
@@ -3313,7 +3313,7 @@ const FULL_LOG_HYDRATION_TIMEOUT_MS = 15e3;
3313
3313
  const BODY_CHUNK_TIMEOUT_MS = 15e3;
3314
3314
  const BODY_CHUNK_BYTES = 256 * 1024;
3315
3315
  const CHUNKED_BODY_THRESHOLD_BYTES = 1024 * 1024;
3316
- const TAB_TRIGGER_CLASS = "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";
3316
+ const TAB_TRIGGER_CLASS = "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";
3317
3317
  function createEmptyBodyPreviewState(totalBytes = null) {
3318
3318
  return {
3319
3319
  status: "idle",
@@ -3395,7 +3395,7 @@ function ExpandedPanel({
3395
3395
  "div",
3396
3396
  {
3397
3397
  className: cn(
3398
- "border border-border bg-card shadow-sm inspector-scrollbar mx-3 mb-3 max-h-[72vh] overflow-auto rounded-[8px] p-3",
3398
+ "bg-black/[0.14] inspector-scrollbar mx-3 mb-3 max-h-[72vh] overflow-auto rounded-[8px] p-3",
3399
3399
  className
3400
3400
  ),
3401
3401
  children
@@ -3403,15 +3403,15 @@ function ExpandedPanel({
3403
3403
  );
3404
3404
  }
3405
3405
  function EmptyState({ children }) {
3406
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border border-border bg-muted/40 rounded-md border-dashed px-3 py-5 text-center text-xs italic text-muted-foreground", children });
3406
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-white/[0.035] rounded-md px-3 py-5 text-center text-xs italic text-muted-foreground", children });
3407
3407
  }
3408
3408
  function RawTextBlock({ children }) {
3409
- return /* @__PURE__ */ jsxRuntimeExports.jsx("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", children });
3409
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("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", children });
3410
3410
  }
3411
3411
  const NOTICE_TONE_CLASSES = {
3412
- loading: "border-cyan-400/25 bg-cyan-400/[0.06] text-cyan-100",
3413
- warning: "border-amber-400/25 bg-amber-400/[0.08] text-amber-100",
3414
- danger: "border-rose-400/25 bg-rose-400/[0.08] text-rose-100"
3412
+ loading: "border-transparent bg-cyan-400/[0.06] text-cyan-100",
3413
+ warning: "border-transparent bg-amber-400/[0.08] text-amber-100",
3414
+ danger: "border-transparent bg-rose-400/[0.08] text-rose-100"
3415
3415
  };
3416
3416
  function NoticeBlock({
3417
3417
  tone,
@@ -3473,12 +3473,12 @@ function HeaderRows({
3473
3473
  }) {
3474
3474
  const entries = headers === void 0 ? [] : Object.entries(headers).sort(([left], [right]) => left.localeCompare(right));
3475
3475
  if (entries.length === 0) return /* @__PURE__ */ jsxRuntimeExports.jsx(EmptyState, { children: emptyLabel });
3476
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border border-border bg-card shadow-sm overflow-hidden rounded-[8px] font-mono text-xs", children: entries.map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
3476
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-black/[0.14] overflow-hidden rounded-[8px] font-mono text-xs", children: entries.map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
3477
3477
  "div",
3478
3478
  {
3479
- className: "grid min-w-0 grid-cols-[minmax(140px,0.32fr)_minmax(0,1fr)] border-b border-white/10 last:border-b-0",
3479
+ className: "grid min-w-0 grid-cols-[minmax(140px,0.32fr)_minmax(0,1fr)] border-b border-white/[0.055] last:border-b-0",
3480
3480
  children: [
3481
- /* @__PURE__ */ jsxRuntimeExports.jsx("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", children: key }),
3481
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-w-0 truncate bg-white/[0.035] px-2.5 py-2 font-semibold text-cyan-200", children: key }),
3482
3482
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-w-0 truncate px-2.5 py-2 text-muted-foreground", title: value, children: value })
3483
3483
  ]
3484
3484
  },
@@ -3854,27 +3854,10 @@ const LogEntry = reactExports.memo(function({
3854
3854
  "div",
3855
3855
  {
3856
3856
  className: cn(
3857
- "border border-border bg-card shadow-sm relative mb-2 overflow-hidden rounded-[8px] transition-[background-color,border-color,box-shadow]",
3858
- expanded ? "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)]" : "hover:border-cyan-200/18"
3857
+ "bg-card/80 relative mb-1.5 overflow-hidden rounded-[8px] transition-[background-color,box-shadow]",
3858
+ expanded ? "bg-card shadow-[0_18px_60px_rgba(0,0,0,0.30),0_0_0_1px_rgba(103,232,249,0.08)]" : "hover:bg-card"
3859
3859
  ),
3860
3860
  children: [
3861
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3862
- "div",
3863
- {
3864
- className: cn(
3865
- "pointer-events-none absolute inset-y-3 left-1 w-px rounded-full",
3866
- expanded ? "bg-cyan-200/55 shadow-[0_0_20px_rgba(103,232,249,0.26)]" : "bg-white/12"
3867
- ),
3868
- "aria-hidden": "true"
3869
- }
3870
- ),
3871
- /* @__PURE__ */ jsxRuntimeExports.jsx(
3872
- "div",
3873
- {
3874
- className: "pointer-events-none absolute inset-x-3 top-0 h-px bg-gradient-to-r from-transparent via-white/15 to-transparent",
3875
- "aria-hidden": "true"
3876
- }
3877
- ),
3878
3861
  /* @__PURE__ */ jsxRuntimeExports.jsx(
3879
3862
  LogEntryHeader,
3880
3863
  {
@@ -3898,7 +3881,7 @@ const LogEntry = reactExports.memo(function({
3898
3881
  expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3899
3882
  "div",
3900
3883
  {
3901
- className: "relative border-t border-white/10 bg-black/[0.18] pt-2",
3884
+ className: "relative bg-black/[0.14] pt-2",
3902
3885
  onClick: (e) => e.stopPropagation(),
3903
3886
  onKeyDown: (e) => e.stopPropagation(),
3904
3887
  children: [
@@ -3914,7 +3897,7 @@ const LogEntry = reactExports.memo(function({
3914
3897
  TabsList,
3915
3898
  {
3916
3899
  variant: "line",
3917
- 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",
3900
+ 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",
3918
3901
  children: [
3919
3902
  viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "raw-headers", children: "Raw Headers" }),
3920
3903
  viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "headers", children: "Headers" }),
@@ -4134,2032 +4117,2037 @@ const LogEntry = reactExports.memo(function({
4134
4117
  ) })
4135
4118
  ] });
4136
4119
  });
4137
- function boundaryTitle(stopReason) {
4138
- switch (stopReason) {
4139
- case "end_turn":
4140
- return "End of Turn (Anthropic)";
4141
- case "stop":
4142
- return "End of Turn (OpenAI)";
4143
- case "length":
4144
- return "End of Turn (length limit)";
4145
- case "tool_use":
4146
- case null:
4147
- return "End of Turn";
4148
- }
4149
- }
4150
- function markerTone({
4151
- isFusedBoundary,
4152
- isBoundary,
4153
- isTurnStart,
4154
- isRunning
4155
- }) {
4156
- if (isFusedBoundary) return "fused";
4157
- if (isBoundary) return "end";
4158
- if (isTurnStart) return "start";
4159
- if (isRunning) return "running";
4160
- return "idle";
4161
- }
4162
- function markerClass(tone, interactive) {
4163
- const base = "inline-flex size-3.5 items-center justify-center rounded-full border transition-colors";
4164
- const cursor = interactive ? "cursor-pointer hover:border-cyan-200/70 hover:bg-cyan-300/20" : "";
4165
- switch (tone) {
4166
- case "start":
4167
- return cn(base, cursor, "border-cyan-300/50 bg-cyan-300/12 text-cyan-200");
4168
- case "end":
4169
- return cn(base, cursor, "border-amber-300/55 bg-amber-300/12 text-amber-200");
4170
- case "fused":
4171
- return cn(
4172
- base,
4173
- cursor,
4174
- "border-slate-200/50 bg-slate-200/10 text-slate-100 ring-2 ring-cyan-300/15"
4175
- );
4176
- case "running":
4177
- return cn(
4178
- base,
4179
- cursor,
4180
- "border-amber-300/45 bg-amber-300/10 text-amber-200 ring-2 ring-amber-300/10"
4181
- );
4182
- case "idle":
4183
- return cn(base, cursor, "border-muted-foreground/25 bg-muted/30 text-muted-foreground/60");
4184
- }
4185
- }
4186
- function markerInnerClass(tone) {
4187
- switch (tone) {
4188
- case "running":
4189
- return "size-1.5 rounded-full bg-current animate-pulse";
4190
- case "idle":
4191
- return "size-1 rounded-full bg-current opacity-55";
4192
- case "start":
4193
- case "end":
4194
- case "fused":
4195
- return "size-1.5 rounded-full bg-current";
4196
- }
4197
- }
4198
- function markerTitle(tone, stopReason) {
4199
- switch (tone) {
4200
- case "start":
4201
- return "Start of turn";
4202
- case "end":
4203
- return boundaryTitle(stopReason);
4204
- case "fused":
4205
- return "Single-request turn";
4206
- case "running":
4207
- return "Processing";
4208
- case "idle":
4209
- return "Request in turn";
4210
- }
4211
- }
4212
- function ThreadConnector({
4213
- stopReason,
4214
- isPending,
4215
- isFirst,
4216
- isTurnStart,
4217
- isOnlyEntry = false,
4218
- collapsible = false,
4219
- onToggle
4120
+ function SvgShell({
4121
+ className,
4122
+ style,
4123
+ d,
4124
+ eyeStalks,
4125
+ eyes,
4126
+ legs,
4127
+ extras
4220
4128
  }) {
4221
- const isBoundary = isTurnBoundary(stopReason);
4222
- const isFusedBoundary = isOnlyEntry && isTurnStart && isBoundary;
4223
- const isRunning = isPending && !isBoundary;
4224
- const tone = markerTone({ isFusedBoundary, isBoundary, isTurnStart, isRunning });
4225
- const interactive = collapsible && onToggle !== void 0;
4226
- const handleMarkerClick = (event) => {
4227
- if (!interactive || onToggle === void 0) return;
4228
- event.stopPropagation();
4229
- onToggle();
4230
- };
4231
- const handleMarkerKeyDown = (event) => {
4232
- if (!interactive || onToggle === void 0) return;
4233
- if (event.key !== "Enter" && event.key !== " ") return;
4234
- event.preventDefault();
4235
- onToggle();
4236
- };
4237
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center w-6 shrink-0 pt-0.5 pb-0.5", children: [
4238
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center h-[calc(0.75rem-8px)]", children: !isFirst && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-0.5 bg-muted-foreground/30 h-full" }) }),
4239
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4240
- "span",
4241
- {
4242
- role: interactive ? "button" : void 0,
4243
- tabIndex: interactive ? 0 : void 0,
4244
- title: markerTitle(tone, stopReason),
4245
- className: markerClass(tone, interactive),
4246
- onClick: handleMarkerClick,
4247
- onKeyDown: handleMarkerKeyDown,
4248
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: markerInnerClass(tone) })
4249
- }
4250
- ),
4251
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex justify-center min-h-0", children: !isBoundary && /* @__PURE__ */ jsxRuntimeExports.jsx(
4252
- "div",
4253
- {
4254
- className: cn(
4255
- "w-0.5 h-full",
4256
- isPending ? "border-dashed bg-transparent border-l-2 border-muted-foreground/20" : "bg-muted-foreground/30"
4257
- )
4258
- }
4259
- ) })
4260
- ] });
4261
- }
4262
- function ToolTraceEventRow({ event }) {
4263
- const argumentCopy = useCopyFeedback(event.argumentsText);
4264
- const canCopyArguments = event.argumentsText !== null;
4265
4129
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4266
- "div",
4130
+ "svg",
4267
4131
  {
4268
- 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",
4132
+ viewBox: "0 0 24 24",
4133
+ fill: "none",
4134
+ stroke: "currentColor",
4135
+ strokeWidth: "1.5",
4136
+ strokeLinecap: "round",
4137
+ strokeLinejoin: "round",
4138
+ "aria-hidden": "true",
4139
+ style,
4140
+ className: cn("inline-block size-5", className),
4269
4141
  children: [
4270
- /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 shrink-0 text-sky-400/70" }),
4271
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-foreground/80", children: event.name }),
4272
- event.argumentsPreview !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4273
- /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 shrink-0 text-muted-foreground/60" }),
4274
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate font-mono text-muted-foreground", children: event.argumentsPreview })
4275
- ] }),
4276
- canCopyArguments && /* @__PURE__ */ jsxRuntimeExports.jsx(
4277
- "button",
4278
- {
4279
- type: "button",
4280
- className: "ml-auto inline-flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-background/80 hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-hover/tool-trace:opacity-100 group-focus-within/tool-trace:opacity-100",
4281
- onClick: argumentCopy.copy,
4282
- "aria-label": argumentCopy.copied ? "Copied tool arguments" : "Copy tool arguments",
4283
- title: argumentCopy.copied ? "Copied tool arguments" : "Copy tool arguments",
4284
- children: argumentCopy.copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
4285
- }
4286
- )
4142
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d }),
4143
+ eyeStalks,
4144
+ eyes,
4145
+ legs,
4146
+ extras
4287
4147
  ]
4288
- },
4289
- event.id
4148
+ }
4290
4149
  );
4291
4150
  }
4292
- function ToolTraceEvents({ events }) {
4293
- if (events.length === 0) return null;
4294
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-3 mb-2 grid gap-1.5", children: events.map((event) => /* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEventRow, { event }, event.id)) });
4295
- }
4296
- const PREVIEW_LIMIT = 180;
4297
- const toolTraceCache = /* @__PURE__ */ new WeakMap();
4298
- function responseMayContainToolTrace(log, format) {
4299
- const responseText = log.responseText;
4300
- if (responseText === null) return false;
4301
- switch (format) {
4302
- case "anthropic":
4303
- return responseText.includes("tool_use");
4304
- case "openai":
4305
- return responseText.includes("tool_calls") || responseText.includes("function_call");
4306
- case "unknown":
4307
- return false;
4308
- }
4309
- }
4310
- function isTurnCollapsible(entryCount, isComplete, isPending) {
4311
- return entryCount > 1 && isComplete && !isPending;
4151
+ const StdLegs = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4152
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
4153
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
4154
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
4155
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
4156
+ ] });
4157
+ const ShortLegs = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4158
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7", y1: "16", x2: "5.5", y2: "18.5" }),
4159
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "17", x2: "8.5", y2: "19.5" }),
4160
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "17", x2: "15.5", y2: "19.5" }),
4161
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17", y1: "16", x2: "18.5", y2: "18.5" })
4162
+ ] });
4163
+ function Crab1({ className }) {
4164
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4165
+ SvgShell,
4166
+ {
4167
+ className,
4168
+ d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z",
4169
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4170
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
4171
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
4172
+ ] }),
4173
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4174
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
4175
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" })
4176
+ ] }),
4177
+ legs: StdLegs,
4178
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4179
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
4180
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" })
4181
+ ] })
4182
+ }
4183
+ );
4312
4184
  }
4313
- function buildTurnGroups(logs) {
4314
- const groups = [];
4315
- let entries = [];
4316
- let turnIndex = 0;
4317
- for (let index = 0; index < logs.length; index += 1) {
4318
- const log = logs[index];
4319
- if (log === void 0) continue;
4320
- entries.push({ log, stopReason: extractStopReason(log), sessionLogNumber: index + 1 });
4321
- const current = entries[entries.length - 1];
4322
- if (current !== void 0 && isTurnBoundary(current.stopReason)) {
4323
- groups.push({ entries, turnIndex });
4324
- entries = [];
4325
- turnIndex += 1;
4185
+ function Crab2({ className }) {
4186
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4187
+ SvgShell,
4188
+ {
4189
+ className,
4190
+ d: "M4 13 C4 7 7.5 5.5 12 5.5 C16.5 5.5 20 7 20 13 C20 17.5 16.5 19 12 19 C7.5 19 4 17.5 4 13 Z",
4191
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4192
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "6.5", x2: "9.5", y2: "4.5" }),
4193
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "6.5", x2: "14.5", y2: "4.5" })
4194
+ ] }),
4195
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4196
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4", r: "1.1", fill: "currentColor", stroke: "none" }),
4197
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4", r: "1.1", fill: "currentColor", stroke: "none" })
4198
+ ] }),
4199
+ legs: ShortLegs,
4200
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4201
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C4 9.5 2.5 10 3 12 C3.5 13.5 4.5 13 5 12" }),
4202
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20 9.5 21.5 10 21 12 C20.5 13.5 19.5 13 19 12" })
4203
+ ] })
4326
4204
  }
4327
- }
4328
- if (entries.length > 0) groups.push({ entries, turnIndex });
4329
- return groups;
4205
+ );
4330
4206
  }
4331
- function buildValidPredecessors(groups) {
4332
- const predecessors = /* @__PURE__ */ new Map();
4333
- for (const group of groups) {
4334
- for (let index = 1; index < group.logs.length; index += 1) {
4335
- const current = group.logs[index];
4336
- const previous = group.logs[index - 1];
4337
- if (current === void 0 || previous === void 0) continue;
4338
- const currentFormat = resolveLogFormat(current);
4339
- const previousFormat = resolveLogFormat(previous);
4340
- if (currentFormat === "unknown" || currentFormat !== previousFormat) continue;
4341
- predecessors.set(current.id, previous);
4207
+ function Crab3({ className }) {
4208
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4209
+ SvgShell,
4210
+ {
4211
+ className,
4212
+ d: "M3 13.5 C3 8 7 6 12 6 C17 6 21 8 21 13.5 C21 17 17 19 12 19 C7 19 3 17 3 13.5 Z",
4213
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4214
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7", y1: "7", x2: "6.5", y2: "4.5" }),
4215
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17", y1: "7", x2: "17.5", y2: "4.5" })
4216
+ ] }),
4217
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4218
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "6.5", cy: "4", r: "0.8", fill: "currentColor", stroke: "none" }),
4219
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "17.5", cy: "4", r: "0.8", fill: "currentColor", stroke: "none" })
4220
+ ] }),
4221
+ legs: StdLegs,
4222
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4223
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M4 12 C2.5 10.5 1 11 1.5 13 C2 14.5 3.5 14 4.5 13" }),
4224
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M20 12 C21.5 10.5 23 11 22.5 13 C22 14.5 20.5 14 19.5 13" })
4225
+ ] })
4342
4226
  }
4343
- }
4344
- return predecessors;
4227
+ );
4345
4228
  }
4346
- function parseJsonResponse(responseText) {
4347
- if (responseText === null) return null;
4348
- try {
4349
- const parsed = JSON.parse(responseText);
4350
- if (typeof parsed === "string") {
4351
- return JSON.parse(parsed);
4229
+ function Crab4({ className }) {
4230
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4231
+ SvgShell,
4232
+ {
4233
+ className,
4234
+ d: "M6 14 C6 8 9 5.5 12 5.5 C15 5.5 18 8 18 14 C18 18 15 20 12 20 C9 20 6 18 6 14 Z",
4235
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4236
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "6.5", x2: "10", y2: "3.5" }),
4237
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "6.5", x2: "14", y2: "3.5" })
4238
+ ] }),
4239
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4240
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "10", cy: "3", r: "0.7", fill: "currentColor", stroke: "none" }),
4241
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14", cy: "3", r: "0.7", fill: "currentColor", stroke: "none" })
4242
+ ] }),
4243
+ legs: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4244
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7", y1: "17", x2: "5", y2: "20.5" }),
4245
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "18.5", x2: "8.5", y2: "21.5" }),
4246
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "18.5", x2: "15.5", y2: "21.5" }),
4247
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17", y1: "17", x2: "19", y2: "20.5" })
4248
+ ] }),
4249
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4250
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M6.5 11 C5 8.5 3 9 3.5 11.5 C4 13.5 5 13 6 12" }),
4251
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M17.5 11 C19 8.5 21 9 20.5 11.5 C20 13.5 19 13 18 12" })
4252
+ ] })
4352
4253
  }
4353
- return parsed;
4354
- } catch {
4355
- return null;
4356
- }
4357
- }
4358
- function previewValue(value) {
4359
- if (value === void 0 || value === null) return null;
4360
- const raw = typeof value === "string" ? value : JSON.stringify(value);
4361
- if (raw === void 0) return null;
4362
- const normalized = raw.replace(/\s+/g, " ").trim();
4363
- if (normalized.length === 0) return null;
4364
- return normalized.length > PREVIEW_LIMIT ? `${normalized.slice(0, PREVIEW_LIMIT - 1)}...` : normalized;
4254
+ );
4365
4255
  }
4366
- function copyValue(value) {
4367
- if (value === void 0 || value === null) return null;
4368
- if (typeof value === "string") return value.length > 0 ? value : null;
4369
- const raw = JSON.stringify(value, null, 2);
4370
- return raw === void 0 || raw.length === 0 ? null : raw;
4256
+ function Crab5({ className }) {
4257
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4258
+ SvgShell,
4259
+ {
4260
+ className,
4261
+ d: "M5 13 C5 9 8 7 12 7 L13 4 L14 7 C16 7 19 9 19 13 C19 16 16 18 13 18 L12 21 L11 18 C8 18 5 16 5 13 Z",
4262
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4263
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
4264
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
4265
+ ] }),
4266
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4267
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.8", fill: "currentColor", stroke: "none" }),
4268
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.8", fill: "currentColor", stroke: "none" })
4269
+ ] }),
4270
+ legs: StdLegs,
4271
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4272
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
4273
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" })
4274
+ ] })
4275
+ }
4276
+ );
4371
4277
  }
4372
- function extractAnthropicToolTraceEvents(log) {
4373
- const parsed = parseJsonResponse(log.responseText);
4374
- const content = safeGetOwnProperty(parsed, "content");
4375
- if (!Array.isArray(content)) return [];
4376
- const events = [];
4377
- for (const block of content) {
4378
- const type = safeGetOwnProperty(block, "type");
4379
- if (type !== "tool_use") continue;
4380
- const name = safeGetOwnProperty(block, "name");
4381
- if (typeof name !== "string" || name.length === 0) continue;
4382
- const input = safeGetOwnProperty(block, "input");
4383
- events.push({
4384
- id: `${String(log.id)}-anthropic-tool-${String(events.length)}`,
4385
- logId: log.id,
4386
- index: events.length,
4387
- provider: "anthropic",
4388
- name,
4389
- argumentsText: copyValue(input),
4390
- argumentsPreview: previewValue(input)
4391
- });
4392
- }
4393
- return events;
4278
+ function Crab6({ className }) {
4279
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4280
+ SvgShell,
4281
+ {
4282
+ className,
4283
+ d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z",
4284
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4285
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
4286
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
4287
+ ] }),
4288
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4289
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
4290
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "13.5", y1: "4", x2: "15.5", y2: "5" })
4291
+ ] }),
4292
+ legs: StdLegs,
4293
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4294
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
4295
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" })
4296
+ ] })
4297
+ }
4298
+ );
4394
4299
  }
4395
- function extractOpenAIToolTraceEvents(log) {
4396
- const parsed = parseJsonResponse(log.responseText);
4397
- const choices = safeGetOwnProperty(parsed, "choices");
4398
- if (!Array.isArray(choices)) return [];
4399
- const events = [];
4400
- for (const choice of choices) {
4401
- const message = safeGetOwnProperty(choice, "message");
4402
- const toolCalls = safeGetOwnProperty(message, "tool_calls");
4403
- if (!Array.isArray(toolCalls)) continue;
4404
- for (const call of toolCalls) {
4405
- const fn = safeGetOwnProperty(call, "function");
4406
- const name = safeGetOwnProperty(fn, "name");
4407
- if (typeof name !== "string" || name.length === 0) continue;
4408
- const args = safeGetOwnProperty(fn, "arguments");
4409
- events.push({
4410
- id: `${String(log.id)}-openai-tool-${String(events.length)}`,
4411
- logId: log.id,
4412
- index: events.length,
4413
- provider: "openai",
4414
- name,
4415
- argumentsText: copyValue(args),
4416
- argumentsPreview: previewValue(args)
4417
- });
4300
+ function Crab7({ className }) {
4301
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4302
+ SvgShell,
4303
+ {
4304
+ className,
4305
+ d: "M5.5 13.5 C5.5 10 8.5 8 12 8 C15.5 8 18.5 10 18.5 13.5 C18.5 16 15.5 17.5 12 17.5 C8.5 17.5 5.5 16 5.5 13.5 Z",
4306
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4307
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "8", x2: "10", y2: "6" }),
4308
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "8", x2: "14", y2: "6" })
4309
+ ] }),
4310
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4311
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "6", x2: "11", y2: "6.5" }),
4312
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "13", y1: "6", x2: "15", y2: "6.5" })
4313
+ ] }),
4314
+ legs: ShortLegs,
4315
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4316
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M6 11.5 C4.5 10.5 3 11 3.5 12.5 C4 13.5 5 13 5.5 12.5" }),
4317
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M18 11.5 C19.5 10.5 21 11 20.5 12.5 C20 13.5 19 13 18.5 12.5" })
4318
+ ] })
4418
4319
  }
4419
- }
4420
- const output = safeGetOwnProperty(parsed, "output");
4421
- if (Array.isArray(output)) {
4422
- for (const item of output) {
4423
- if (safeGetOwnProperty(item, "type") !== "function_call") continue;
4424
- const name = safeGetOwnProperty(item, "name");
4425
- if (typeof name !== "string" || name.length === 0) continue;
4426
- const args = safeGetOwnProperty(item, "arguments");
4427
- events.push({
4428
- id: `${String(log.id)}-openai-tool-${String(events.length)}`,
4429
- logId: log.id,
4430
- index: events.length,
4431
- provider: "openai",
4432
- name,
4433
- argumentsText: copyValue(args),
4434
- argumentsPreview: previewValue(args)
4435
- });
4320
+ );
4321
+ }
4322
+ function Crab8({ className }) {
4323
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4324
+ SvgShell,
4325
+ {
4326
+ className,
4327
+ d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z",
4328
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4329
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
4330
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
4331
+ ] }),
4332
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4333
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.7", fill: "currentColor", stroke: "none" }),
4334
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.7", fill: "currentColor", stroke: "none" })
4335
+ ] }),
4336
+ legs: StdLegs,
4337
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4338
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
4339
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" }),
4340
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "8.5", y1: "4", x2: "10.5", y2: "4.5" }),
4341
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15.5", y1: "4", x2: "13.5", y2: "4.5" }),
4342
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9 15.5 C10 14.5 14 14.5 15 15.5" })
4343
+ ] })
4436
4344
  }
4437
- }
4438
- return events;
4345
+ );
4439
4346
  }
4440
- function extractToolTraceEvents(log) {
4441
- const cached = toolTraceCache.get(log);
4442
- if (cached !== void 0 && cached.apiFormat === log.apiFormat && cached.responseText === log.responseText) {
4443
- return cached.events;
4444
- }
4445
- const format = resolveLogFormat(log);
4446
- if (!responseMayContainToolTrace(log, format)) {
4447
- const events2 = [];
4448
- toolTraceCache.set(log, {
4449
- apiFormat: log.apiFormat,
4450
- responseText: log.responseText,
4451
- events: events2
4452
- });
4453
- return events2;
4454
- }
4455
- let events;
4456
- switch (format) {
4457
- case "anthropic":
4458
- events = extractAnthropicToolTraceEvents(log);
4459
- break;
4460
- case "openai":
4461
- events = extractOpenAIToolTraceEvents(log);
4462
- break;
4463
- case "unknown":
4464
- events = [];
4465
- break;
4466
- }
4467
- toolTraceCache.set(log, {
4468
- apiFormat: log.apiFormat,
4469
- responseText: log.responseText,
4470
- events
4471
- });
4472
- return events;
4347
+ function Crab9({ className }) {
4348
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4349
+ SvgShell,
4350
+ {
4351
+ className,
4352
+ d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z",
4353
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4354
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
4355
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
4356
+ ] }),
4357
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4358
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "1.2", fill: "none", stroke: "currentColor" }),
4359
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "1.2", fill: "none", stroke: "currentColor" })
4360
+ ] }),
4361
+ legs: StdLegs,
4362
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4363
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
4364
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" }),
4365
+ /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "12", cy: "15", rx: "2", ry: "1.5", fill: "currentColor", stroke: "none" })
4366
+ ] })
4367
+ }
4368
+ );
4473
4369
  }
4474
- function buildTraceSummary(logs, slowResponseThresholdSeconds, knowledgeCandidateCount = 0) {
4475
- let failedCallCount = 0;
4476
- let pendingCallCount = 0;
4477
- let slowCallCount = 0;
4478
- let totalInputTokens = 0;
4479
- let totalOutputTokens = 0;
4480
- let totalCacheCreationInputTokens = 0;
4481
- let totalCacheReadInputTokens = 0;
4482
- let totalElapsedMs = 0;
4483
- let maxElapsedMs = null;
4484
- let toolCallCount = 0;
4485
- for (const log of logs) {
4486
- if (log.responseStatus === null) {
4487
- pendingCallCount += 1;
4488
- } else if (log.responseStatus >= 400) {
4489
- failedCallCount += 1;
4490
- }
4491
- if (log.elapsedMs !== null && slowResponseThresholdSeconds > 0 && log.elapsedMs > slowResponseThresholdSeconds * 1e3) {
4492
- slowCallCount += 1;
4493
- }
4494
- if (log.inputTokens !== null) totalInputTokens += log.inputTokens;
4495
- if (log.outputTokens !== null) totalOutputTokens += log.outputTokens;
4496
- if (log.cacheCreationInputTokens !== null) {
4497
- totalCacheCreationInputTokens += log.cacheCreationInputTokens;
4370
+ function Crab10({ className }) {
4371
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4372
+ SvgShell,
4373
+ {
4374
+ className,
4375
+ d: "M4.5 13 C4.5 8 7.5 6 12 6 C16.5 6 19.5 8 19.5 13 C19.5 17 16.5 18.5 12 18.5 C7.5 18.5 4.5 17 4.5 13 Z",
4376
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4377
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "7", x2: "9", y2: "5" }),
4378
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "7", x2: "15", y2: "5" })
4379
+ ] }),
4380
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4381
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9", cy: "4.5", r: "1", fill: "currentColor", stroke: "none" }),
4382
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "15", cy: "4.5", r: "1", fill: "currentColor", stroke: "none" })
4383
+ ] }),
4384
+ legs: StdLegs,
4385
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4386
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9 2 9.5 2.5 12 C3 14 4 13 5 12" }),
4387
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9 22 9.5 21.5 12 C21 14 20 13 19 12" }),
4388
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9 14.5 C10 16 14 16 15 14.5" }),
4389
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "7", cy: "13", r: "1", fill: "currentColor", stroke: "none", opacity: "0.3" }),
4390
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "17", cy: "13", r: "1", fill: "currentColor", stroke: "none", opacity: "0.3" })
4391
+ ] })
4498
4392
  }
4499
- if (log.cacheReadInputTokens !== null) {
4500
- totalCacheReadInputTokens += log.cacheReadInputTokens;
4393
+ );
4394
+ }
4395
+ function Crab11({ className }) {
4396
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4397
+ SvgShell,
4398
+ {
4399
+ className,
4400
+ d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z",
4401
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4402
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5.5" }),
4403
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5.5" })
4404
+ ] }),
4405
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4406
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7.5", y1: "5.5", x2: "16.5", y2: "5.5" }),
4407
+ /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "7", y: "5", width: "4", height: "2", rx: "0.5", fill: "currentColor", stroke: "none" }),
4408
+ /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "13", y: "5", width: "4", height: "2", rx: "0.5", fill: "currentColor", stroke: "none" })
4409
+ ] }),
4410
+ legs: StdLegs,
4411
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4412
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
4413
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" }),
4414
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9.5 15 C10.5 16 13.5 16 14.5 15" })
4415
+ ] })
4501
4416
  }
4502
- if (log.elapsedMs !== null) {
4503
- totalElapsedMs += log.elapsedMs;
4504
- maxElapsedMs = maxElapsedMs === null ? log.elapsedMs : Math.max(maxElapsedMs, log.elapsedMs);
4417
+ );
4418
+ }
4419
+ function Crab12({ className }) {
4420
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4421
+ SvgShell,
4422
+ {
4423
+ className,
4424
+ d: "M7 13.5 C7 10.5 9.5 9 12 9 C14.5 9 17 10.5 17 13.5 C17 16 14.5 17.5 12 17.5 C9.5 17.5 7 16 7 13.5 Z",
4425
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4426
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10.5", y1: "9.5", x2: "10.5", y2: "8" }),
4427
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "13.5", y1: "9.5", x2: "13.5", y2: "8" })
4428
+ ] }),
4429
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4430
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "10.5", cy: "7.5", r: "0.8", fill: "currentColor", stroke: "none" }),
4431
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "13.5", cy: "7.5", r: "0.8", fill: "currentColor", stroke: "none" })
4432
+ ] }),
4433
+ legs: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4434
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "8", y1: "16", x2: "6.5", y2: "18" }),
4435
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "17", x2: "9.5", y2: "19" }),
4436
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "17", x2: "14.5", y2: "19" }),
4437
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "16", y1: "16", x2: "17.5", y2: "18" })
4438
+ ] }),
4439
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4440
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M7.5 12 C6.5 11 5.5 11.5 6 13 C6.5 14 7 13.5 7.5 13" }),
4441
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M16.5 12 C17.5 11 18.5 11.5 18 13 C17.5 14 17 13.5 16.5 13" })
4442
+ ] })
4505
4443
  }
4506
- toolCallCount += extractToolTraceEvents(log).length;
4444
+ );
4445
+ }
4446
+ const crabVariants = [
4447
+ Crab1,
4448
+ Crab2,
4449
+ Crab3,
4450
+ Crab4,
4451
+ Crab5,
4452
+ Crab6,
4453
+ Crab7,
4454
+ Crab8,
4455
+ Crab9,
4456
+ Crab10,
4457
+ Crab11,
4458
+ Crab12
4459
+ ];
4460
+ function getCrabVariant(index) {
4461
+ return crabVariants[Math.abs(index) % crabVariants.length] ?? Crab1;
4462
+ }
4463
+ function getInteriorCrabVariantIndex(seed) {
4464
+ const interiorCount = crabVariants.length - 2;
4465
+ if (interiorCount <= 0) return 0;
4466
+ const normalizedSeed = Number.isFinite(seed) ? Math.trunc(seed) : 0;
4467
+ return 1 + (normalizedSeed % interiorCount + interiorCount) % interiorCount;
4468
+ }
4469
+ function getInteriorCrabVariant(seed) {
4470
+ return getCrabVariant(getInteriorCrabVariantIndex(seed));
4471
+ }
4472
+ function boundaryTitle(stopReason) {
4473
+ switch (stopReason) {
4474
+ case "end_turn":
4475
+ return "End of Turn (Anthropic)";
4476
+ case "stop":
4477
+ return "End of Turn (OpenAI)";
4478
+ case "length":
4479
+ return "End of Turn (length limit)";
4480
+ case "tool_use":
4481
+ case null:
4482
+ return "End of Turn";
4507
4483
  }
4508
- return {
4509
- llmCallCount: logs.length,
4510
- toolCallCount,
4511
- failedCallCount,
4512
- pendingCallCount,
4513
- slowCallCount,
4514
- totalInputTokens,
4515
- totalOutputTokens,
4516
- totalCacheCreationInputTokens,
4517
- totalCacheReadInputTokens,
4518
- totalElapsedMs,
4519
- maxElapsedMs,
4520
- startedAt: logs[0]?.timestamp ?? null,
4521
- endedAt: logs[logs.length - 1]?.timestamp ?? null,
4522
- knowledgeCandidateCount
4523
- };
4524
4484
  }
4525
- function formatElapsed$2(ms) {
4526
- if (ms < 1e3) return `${ms}ms`;
4527
- return `${(ms / 1e3).toFixed(1)}s`;
4485
+ function crabShellClass(interactive) {
4486
+ return interactive ? "cursor-pointer transition-transform hover:scale-110" : "";
4528
4487
  }
4529
- const TurnGroup = reactExports.memo(function TurnGroup2({
4530
- entries,
4531
- viewMode,
4532
- strip,
4533
- slowResponseThresholdSeconds,
4534
- cacheTrends,
4535
- onCompareWithPrevious,
4536
- comparisonPredecessors,
4537
- turnIndex = 0,
4538
- timeDisplayFormat,
4539
- pendingFocusRequest,
4540
- showLogClientMetadata = true
4488
+ function ThreadConnector({
4489
+ stopReason,
4490
+ isPending,
4491
+ isFirst,
4492
+ isTurnStart,
4493
+ isOnlyEntry = false,
4494
+ crabIndex = 0,
4495
+ collapsible = false,
4496
+ onToggle
4541
4497
  }) {
4542
- const lastIdx = entries.length - 1;
4543
- const lastStop = entries[lastIdx]?.stopReason ?? null;
4544
- const isComplete = lastStop !== null ? isTurnBoundary(lastStop) : false;
4545
- const isPending = entries[lastIdx]?.log.responseStatus === null;
4546
- const isSingleLog = entries.length === 1;
4547
- const collapsible = isTurnCollapsible(entries.length, isComplete, isPending);
4548
- const [collapsed, setCollapsed] = reactExports.useState(false);
4549
- const prevCompleteRef = reactExports.useRef(false);
4550
- reactExports.useEffect(() => {
4551
- if (!collapsible) {
4552
- setCollapsed(false);
4553
- } else if (isComplete && !prevCompleteRef.current) {
4554
- setCollapsed(true);
4555
- }
4556
- prevCompleteRef.current = isComplete;
4557
- }, [collapsible, isComplete]);
4558
- reactExports.useEffect(() => {
4559
- const handleLogFocusRequest = (event) => {
4560
- const request = readLogFocusRequest(event);
4561
- if (request === null) return;
4562
- if (!entries.some((entry) => entry.log.id === request.logId)) return;
4563
- if (collapsible) setCollapsed(false);
4564
- };
4565
- window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4566
- return () => {
4567
- window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4568
- };
4569
- }, [collapsible, entries]);
4570
- reactExports.useEffect(() => {
4571
- if (pendingFocusRequest === void 0) return void 0;
4572
- if (!entries.some((entry) => entry.log.id === pendingFocusRequest.logId)) return void 0;
4573
- if (collapsible) setCollapsed(false);
4574
- const timer = window.setTimeout(() => {
4575
- dispatchLogFocusRequest({
4576
- logId: pendingFocusRequest.logId,
4577
- tab: pendingFocusRequest.tab,
4578
- source: "virtualizer"
4579
- });
4580
- }, 0);
4581
- return () => window.clearTimeout(timer);
4582
- }, [collapsible, entries, pendingFocusRequest]);
4583
- const toggleCollapse = reactExports.useCallback(() => {
4584
- if (collapsible) setCollapsed((prev) => !prev);
4585
- }, [collapsible]);
4586
- const aggregate = reactExports.useMemo(() => {
4587
- let totalInput = 0;
4588
- let totalOutput = 0;
4589
- let maxElapsed = null;
4590
- let hasTokens = false;
4591
- for (const e of entries) {
4592
- if (e.log.inputTokens !== null) {
4593
- totalInput += e.log.inputTokens;
4594
- hasTokens = true;
4595
- }
4596
- if (e.log.outputTokens !== null) {
4597
- totalOutput += e.log.outputTokens;
4598
- hasTokens = true;
4498
+ const isBoundary = isTurnBoundary(stopReason);
4499
+ const isFusedBoundary = isOnlyEntry && isTurnStart && isBoundary;
4500
+ const isRunning = isPending && !isBoundary;
4501
+ const interactive = collapsible && onToggle !== void 0;
4502
+ const Crab = reactExports.useMemo(() => getCrabVariant(crabIndex), [crabIndex]);
4503
+ const FusedCrab = reactExports.useMemo(() => getInteriorCrabVariant(crabIndex), [crabIndex]);
4504
+ const handleCrabClick = (event) => {
4505
+ if (!interactive || onToggle === void 0) return;
4506
+ event.stopPropagation();
4507
+ onToggle();
4508
+ };
4509
+ const handleCrabKeyDown = (event) => {
4510
+ if (!interactive || onToggle === void 0) return;
4511
+ if (event.key !== "Enter" && event.key !== " ") return;
4512
+ event.preventDefault();
4513
+ onToggle();
4514
+ };
4515
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center w-6 shrink-0 pt-0.5 pb-0.5", children: [
4516
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center h-[calc(0.75rem-8px)]", children: !isFirst && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-px bg-muted-foreground/18 h-full" }) }),
4517
+ isFusedBoundary ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4518
+ "span",
4519
+ {
4520
+ role: interactive ? "button" : void 0,
4521
+ tabIndex: interactive ? 0 : void 0,
4522
+ title: "Single-request turn",
4523
+ className: crabShellClass(interactive),
4524
+ onClick: handleCrabClick,
4525
+ onKeyDown: handleCrabKeyDown,
4526
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4527
+ FusedCrab,
4528
+ {
4529
+ className: cn(
4530
+ "size-3.5 text-[#80FF00]",
4531
+ "animate-crab-settle",
4532
+ "drop-shadow-[0_0_4px_rgba(128,255,0,0.5)]"
4533
+ )
4534
+ }
4535
+ )
4599
4536
  }
4600
- if (e.log.elapsedMs !== null) {
4601
- maxElapsed = maxElapsed === null ? e.log.elapsedMs : Math.max(maxElapsed, e.log.elapsedMs);
4537
+ ) : isBoundary ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4538
+ "span",
4539
+ {
4540
+ role: interactive ? "button" : void 0,
4541
+ tabIndex: interactive ? 0 : void 0,
4542
+ title: boundaryTitle(stopReason),
4543
+ className: crabShellClass(interactive),
4544
+ onClick: handleCrabClick,
4545
+ onKeyDown: handleCrabKeyDown,
4546
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4547
+ Crab,
4548
+ {
4549
+ className: cn(
4550
+ "size-3.5 text-amber-400",
4551
+ "animate-crab-settle",
4552
+ "drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]"
4553
+ )
4554
+ }
4555
+ )
4602
4556
  }
4603
- }
4604
- return {
4605
- totalInput,
4606
- totalOutput,
4607
- hasTokens,
4608
- maxElapsed
4609
- };
4610
- }, [entries, lastIdx]);
4611
- const bgClass = turnIndex % 2 === 0 ? "bg-white/[0.012]" : "bg-cyan-300/[0.012]";
4612
- const aggregateIsSlow = aggregate.maxElapsed !== null && slowResponseThresholdSeconds > 0 && aggregate.maxElapsed > slowResponseThresholdSeconds * 1e3;
4613
- const toolEventsByLogId = reactExports.useMemo(() => {
4614
- const events = /* @__PURE__ */ new Map();
4615
- for (const entry of entries) {
4616
- const extracted = extractToolTraceEvents(entry.log);
4617
- if (extracted.length > 0) events.set(entry.log.id, extracted);
4618
- }
4619
- return events;
4620
- }, [entries]);
4621
- const [layoutVersion, setLayoutVersion] = reactExports.useState(0);
4622
- const containerRef = reactExports.useRef(null);
4623
- reactExports.useEffect(() => {
4624
- const el = containerRef.current;
4625
- if (!el) return;
4626
- let raf = 0;
4627
- const ro = new ResizeObserver(() => {
4628
- window.cancelAnimationFrame(raf);
4629
- raf = window.requestAnimationFrame(() => setLayoutVersion((v) => v + 1));
4630
- });
4631
- ro.observe(el);
4632
- return () => {
4633
- ro.disconnect();
4634
- window.cancelAnimationFrame(raf);
4635
- };
4636
- }, []);
4637
- const firstLogId = entries[0]?.log.id ?? turnIndex;
4638
- const turnLabel = `Turn ${String(turnIndex + 1)}`;
4639
- const containmentStyle = reactExports.useMemo(
4640
- () => ({
4641
- contentVisibility: "auto",
4642
- containIntrinsicSize: collapsed ? "48px" : "160px"
4643
- }),
4644
- [collapsed]
4645
- );
4646
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
4557
+ ) : isTurnStart ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4558
+ "span",
4559
+ {
4560
+ role: interactive ? "button" : void 0,
4561
+ tabIndex: interactive ? 0 : void 0,
4562
+ title: "Start of turn",
4563
+ className: crabShellClass(interactive),
4564
+ onClick: handleCrabClick,
4565
+ onKeyDown: handleCrabKeyDown,
4566
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4567
+ Crab,
4568
+ {
4569
+ className: cn(
4570
+ "size-3.5 text-emerald-400",
4571
+ "animate-crab-appear",
4572
+ "drop-shadow-[0_0_4px_rgba(52,211,153,0.5)]"
4573
+ )
4574
+ }
4575
+ )
4576
+ }
4577
+ ) : isRunning ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { title: "Processing", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Crab, { className: cn("size-3.5 text-amber-300/80", "animate-crab-crawl") }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { title: "Request in turn", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Crab, { className: "size-3.5 text-muted-foreground/40" }) }),
4578
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex justify-center min-h-0", children: !isBoundary && /* @__PURE__ */ jsxRuntimeExports.jsx(
4579
+ "div",
4580
+ {
4581
+ className: cn(
4582
+ "w-0.5 h-full",
4583
+ isPending ? "border-dashed bg-transparent border-l border-muted-foreground/14" : "bg-muted-foreground/18"
4584
+ )
4585
+ }
4586
+ ) })
4587
+ ] });
4588
+ }
4589
+ function ToolTraceEventRow({ event }) {
4590
+ const argumentCopy = useCopyFeedback(event.argumentsText);
4591
+ const canCopyArguments = event.argumentsText !== null;
4592
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4647
4593
  "div",
4648
4594
  {
4649
- ref: containerRef,
4650
- style: containmentStyle,
4651
- tabIndex: collapsed ? void 0 : 0,
4652
- role: collapsed ? void 0 : "group",
4653
- "aria-label": collapsed ? void 0 : turnLabel,
4654
- "data-nav-id": collapsed ? void 0 : `turn-${String(firstLogId)}`,
4655
- className: cn(
4656
- "rounded-[8px] border transition-[border-color,box-shadow]",
4657
- isPending ? "border-amber-300/15 shadow-[0_0_0_1px_rgba(252,211,77,0.04)]" : "border-transparent",
4658
- !collapsed && "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
4659
- ),
4660
- children: collapsed ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
4661
- "div",
4662
- {
4663
- "data-nav-id": `turn-collapsed-${String(firstLogId)}`,
4664
- "data-nav-action": "expand",
4665
- role: "button",
4666
- tabIndex: 0,
4667
- className: "flex items-stretch cursor-pointer focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none rounded-lg",
4668
- onClick: toggleCollapse,
4669
- onKeyDown: (e) => {
4670
- if (e.key === "Enter" || e.key === " ") {
4671
- e.preventDefault();
4672
- toggleCollapse();
4673
- }
4674
- },
4675
- children: [
4676
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 shrink-0 flex items-start pt-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground/50 font-mono tabular-nums leading-none select-none", children: turnIndex + 1 }) }),
4677
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-6 shrink-0 flex flex-col items-center pt-1.5 pb-1.5", children: [
4678
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center h-[calc(0.75rem-8px)]" }),
4679
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "size-2 rounded-full border border-cyan-300/45 bg-cyan-300/20" }),
4680
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex justify-center min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-px bg-muted-foreground/25 h-full" }) }),
4681
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4682
- "span",
4683
- {
4684
- className: cn(
4685
- "size-2 rounded-full border",
4686
- isComplete ? "border-amber-300/45 bg-amber-300/20" : "border-muted-foreground/30 bg-muted/30"
4687
- )
4688
- }
4689
- )
4690
- ] }),
4691
- entries.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
4692
- "div",
4693
- {
4694
- className: cn(
4695
- "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)]",
4696
- "border border-border bg-card shadow-sm transition-colors hover:border-cyan-200/20",
4697
- bgClass
4698
- ),
4699
- children: [
4700
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-[10px] font-semibold uppercase tracking-[0.14em] text-muted-foreground/70", children: turnLabel }),
4701
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
4702
- "span",
4703
- {
4704
- className: "text-blue-300/85 font-mono font-semibold tabular-nums shrink-0",
4705
- title: `Log IDs ${String(entries[0]?.log.id ?? "?")} ~ ${String(
4706
- entries[lastIdx]?.log.id ?? "?"
4707
- )}`,
4708
- children: [
4709
- "#",
4710
- entries[0]?.sessionLogNumber ?? "?",
4711
- " ~ #",
4712
- entries[lastIdx]?.sessionLogNumber ?? "?"
4713
- ]
4714
- }
4715
- ),
4716
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground shrink-0", children: [
4717
- entries.length,
4718
- " request",
4719
- entries.length > 1 ? "s" : ""
4720
- ] }),
4721
- aggregate.maxElapsed !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
4722
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
4723
- "span",
4724
- {
4725
- className: cn(
4726
- "flex items-center gap-1 shrink-0",
4727
- aggregateIsSlow ? "text-amber-400" : "text-muted-foreground"
4728
- ),
4729
- children: [
4730
- /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
4731
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed$2(aggregate.maxElapsed) }),
4732
- aggregateIsSlow && /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Slow response" })
4733
- ]
4734
- }
4735
- ) }),
4736
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: aggregateIsSlow ? `Slow response: ${formatElapsed$2(
4737
- aggregate.maxElapsed
4738
- )} exceeds ${formatElapsed$2(slowResponseThresholdSeconds * 1e3)}` : "Slowest request in this turn" })
4739
- ] }) }),
4740
- aggregate.hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 shrink-0", children: [
4741
- /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
4742
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
4743
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-400", children: [
4744
- "IN ",
4745
- formatTokens(aggregate.totalInput)
4746
- ] }),
4747
- " / ",
4748
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-amber-400", children: [
4749
- "OUT ",
4750
- formatTokens(aggregate.totalOutput)
4751
- ] })
4752
- ] })
4753
- ] }),
4754
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 min-w-0" }),
4755
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4756
- "span",
4757
- {
4758
- className: "border border-input bg-background inline-flex size-7 shrink-0 items-center justify-center rounded-md text-slate-200",
4759
- "aria-hidden": "true",
4760
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4 shrink-0" })
4761
- }
4762
- )
4763
- ]
4764
- }
4765
- )
4766
- ]
4767
- }
4768
- ) : (
4769
- /* ---- Expanded: full entries ---- */
4770
- entries.map((entry, visibleIdx) => {
4771
- const { log, stopReason: reason } = entry;
4772
- const isTurnStart = visibleIdx === 0;
4773
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-stretch", children: [
4774
- isTurnStart ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 shrink-0 flex items-start pt-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground/50 font-mono tabular-nums leading-none select-none", children: turnIndex + 1 }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 shrink-0" }),
4775
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4776
- ThreadConnector,
4777
- {
4778
- stopReason: reason,
4779
- isPending: log.responseStatus === null,
4780
- isFirst: visibleIdx === 0,
4781
- isTurnStart,
4782
- isOnlyEntry: isSingleLog,
4783
- collapsible: collapsible && isTurnStart,
4784
- onToggle: toggleCollapse
4785
- }
4786
- ),
4787
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("flex-1 min-w-0 mb-0.5 rounded-[8px]", bgClass), children: [
4788
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4789
- LogEntry,
4790
- {
4791
- log,
4792
- displayNumber: entry.sessionLogNumber,
4793
- viewMode,
4794
- strip,
4795
- slowResponseThresholdSeconds,
4796
- timeDisplayFormat,
4797
- cacheTrend: cacheTrends?.get(log.id) ?? null,
4798
- showClientMetadata: showLogClientMetadata,
4799
- onCompareWithPrevious: comparisonPredecessors.has(log.id) ? onCompareWithPrevious : void 0
4800
- }
4801
- ),
4802
- /* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEvents, { events: toolEventsByLogId.get(log.id) ?? [] })
4803
- ] })
4804
- ] }, log.id);
4805
- })
4806
- )
4807
- }
4595
+ 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",
4596
+ children: [
4597
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 shrink-0 text-sky-400/70" }),
4598
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-foreground/80", children: event.name }),
4599
+ event.argumentsPreview !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4600
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 shrink-0 text-muted-foreground/60" }),
4601
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate font-mono text-muted-foreground", children: event.argumentsPreview })
4602
+ ] }),
4603
+ canCopyArguments && /* @__PURE__ */ jsxRuntimeExports.jsx(
4604
+ "button",
4605
+ {
4606
+ type: "button",
4607
+ className: "ml-auto inline-flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-background/80 hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-hover/tool-trace:opacity-100 group-focus-within/tool-trace:opacity-100",
4608
+ onClick: argumentCopy.copy,
4609
+ "aria-label": argumentCopy.copied ? "Copied tool arguments" : "Copy tool arguments",
4610
+ title: argumentCopy.copied ? "Copied tool arguments" : "Copy tool arguments",
4611
+ children: argumentCopy.copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
4612
+ }
4613
+ )
4614
+ ]
4615
+ },
4616
+ event.id
4808
4617
  );
4809
- });
4810
- const VIRTUALIZE_TURN_THRESHOLD = 24;
4811
- const ESTIMATED_TURN_HEIGHT = 132;
4812
- const VIRTUAL_OVERSCAN = 8;
4813
- function shouldVirtualizeTurnGroups(turnCount) {
4814
- return turnCount > VIRTUALIZE_TURN_THRESHOLD;
4815
4618
  }
4816
- function buildLogTurnIndex(turnGroups) {
4817
- const result = /* @__PURE__ */ new Map();
4818
- for (let index = 0; index < turnGroups.length; index++) {
4819
- const group = turnGroups[index];
4820
- if (group === void 0) continue;
4821
- for (const entry of group.entries) {
4822
- result.set(entry.log.id, index);
4823
- }
4824
- }
4825
- return result;
4619
+ function ToolTraceEvents({ events }) {
4620
+ if (events.length === 0) return null;
4621
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-3 mb-2 grid gap-1.5", children: events.map((event) => /* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEventRow, { event }, event.id)) });
4826
4622
  }
4827
- function renderTurnGroup(turnGroup, props, pendingFocusRequest) {
4828
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
4829
- TurnGroup,
4830
- {
4831
- entries: turnGroup.entries,
4832
- viewMode: props.viewMode,
4833
- strip: props.strip,
4834
- slowResponseThresholdSeconds: props.slowResponseThresholdSeconds,
4835
- timeDisplayFormat: props.timeDisplayFormat,
4836
- cacheTrends: props.cacheTrends,
4837
- onCompareWithPrevious: props.onCompareWithPrevious,
4838
- comparisonPredecessors: props.comparisonPredecessors,
4839
- turnIndex: turnGroup.turnIndex,
4840
- pendingFocusRequest,
4841
- showLogClientMetadata: props.showLogClientMetadata
4842
- },
4843
- turnGroup.turnIndex
4844
- );
4845
- }
4846
- function TurnGroupList(props) {
4847
- if (!shouldVirtualizeTurnGroups(props.turnGroups.length)) {
4848
- return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: props.turnGroups.map((turnGroup) => renderTurnGroup(turnGroup, props)) });
4623
+ const PREVIEW_LIMIT = 180;
4624
+ const toolTraceCache = /* @__PURE__ */ new WeakMap();
4625
+ function responseMayContainToolTrace(log, format) {
4626
+ const responseText = log.responseText;
4627
+ if (responseText === null) return false;
4628
+ switch (format) {
4629
+ case "anthropic":
4630
+ return responseText.includes("tool_use");
4631
+ case "openai":
4632
+ return responseText.includes("tool_calls") || responseText.includes("function_call");
4633
+ case "unknown":
4634
+ return false;
4849
4635
  }
4850
- return /* @__PURE__ */ jsxRuntimeExports.jsx(VirtualizedTurnGroupList, { ...props });
4851
- }
4852
- function VirtualizedTurnGroupList(props) {
4853
- const { turnGroups } = props;
4854
- const [pendingFocusRequest, setPendingFocusRequest] = reactExports.useState(
4855
- null
4856
- );
4857
- const virtualizer = useWindowVirtualizer({
4858
- count: turnGroups.length,
4859
- estimateSize: () => ESTIMATED_TURN_HEIGHT,
4860
- overscan: VIRTUAL_OVERSCAN,
4861
- getItemKey: (index) => turnGroups[index]?.turnIndex ?? index
4862
- });
4863
- const turnIndexByLogId = reactExports.useMemo(() => buildLogTurnIndex(turnGroups), [turnGroups]);
4864
- reactExports.useEffect(() => {
4865
- const handleLogFocusRequest = (event) => {
4866
- const request = readLogFocusRequest(event);
4867
- if (request === null) return;
4868
- if (request.source === "virtualizer") return;
4869
- const turnIndex = turnIndexByLogId.get(request.logId);
4870
- if (turnIndex === void 0) return;
4871
- virtualizer.scrollToIndex(turnIndex, { align: "center" });
4872
- setPendingFocusRequest((previous) => ({
4873
- ...request,
4874
- turnIndex,
4875
- nonce: previous === null ? 1 : previous.nonce + 1
4876
- }));
4877
- };
4878
- window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4879
- return () => {
4880
- window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4881
- };
4882
- }, [turnIndexByLogId, virtualizer]);
4883
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
4884
- "div",
4885
- {
4886
- className: "relative w-full",
4887
- style: { height: `${virtualizer.getTotalSize()}px` },
4888
- "data-virtualized-turn-list": "true",
4889
- children: virtualizer.getVirtualItems().map((virtualItem) => {
4890
- const turnGroup = turnGroups[virtualItem.index];
4891
- if (turnGroup === void 0) return null;
4892
- const itemFocusRequest = pendingFocusRequest !== null && pendingFocusRequest.turnIndex === virtualItem.index ? pendingFocusRequest : void 0;
4893
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
4894
- "div",
4895
- {
4896
- "data-index": virtualItem.index,
4897
- ref: virtualizer.measureElement,
4898
- className: "absolute left-0 top-0 w-full",
4899
- style: { transform: `translateY(${virtualItem.start}px)` },
4900
- children: renderTurnGroup(turnGroup, props, itemFocusRequest)
4901
- },
4902
- virtualItem.key
4903
- );
4904
- })
4905
- }
4906
- );
4907
- }
4908
- const CandidateResponseSchema = object({
4909
- candidates: array(KnowledgeCandidateSchema)
4910
- });
4911
- const CandidatePromotionResponseSchema = object({
4912
- candidate: KnowledgeCandidateSchema
4913
- });
4914
- const CandidatePromotionFailureResponseSchema = object({
4915
- error: string().optional(),
4916
- candidate: KnowledgeCandidateSchema.optional()
4917
- });
4918
- const CandidateUpdateResponseSchema = object({
4919
- candidate: KnowledgeCandidateSchema
4920
- });
4921
- const CANDIDATE_STATUS_CLASSES = {
4922
- draft: "border-amber-500/30 bg-amber-500/10 text-amber-500",
4923
- promoted: "border-emerald-500/30 bg-emerald-500/10 text-emerald-500",
4924
- failed: "border-destructive/30 bg-destructive/10 text-destructive"
4925
- };
4926
- function formatElapsed$1(ms) {
4927
- if (ms === null) return "-";
4928
- if (ms < 1e3) return `${String(ms)}ms`;
4929
- return `${(ms / 1e3).toFixed(1)}s`;
4930
- }
4931
- function formatTimeRange$1(startedAt, endedAt, timeDisplayFormat) {
4932
- if (startedAt === null || endedAt === null) return null;
4933
- return formatTimestampRange(startedAt, endedAt, timeDisplayFormat);
4934
- }
4935
- function formatCandidateCount(count) {
4936
- return `${String(count)} candidate${count === 1 ? "" : "s"}`;
4937
- }
4938
- function getLogAnchor(logId) {
4939
- return `log-${String(logId)}`;
4940
4636
  }
4941
- function jumpToLog(logId) {
4942
- const anchor = getLogAnchor(logId);
4943
- const target = document.getElementById(anchor);
4944
- window.history.replaceState(null, "", `#${anchor}`);
4945
- if (!(target instanceof HTMLElement)) return;
4946
- target.scrollIntoView({ block: "center", behavior: "smooth" });
4947
- target.focus({ preventScroll: true });
4637
+ function isTurnCollapsible(entryCount, isComplete, isPending) {
4638
+ return entryCount > 1 && isComplete && !isPending;
4948
4639
  }
4949
- function firstFailureLog(logs) {
4950
- for (const log of logs) {
4951
- if (log.responseStatus !== null && log.responseStatus >= 400) return log;
4640
+ function buildTurnGroups(logs) {
4641
+ const groups = [];
4642
+ let entries = [];
4643
+ let turnIndex = 0;
4644
+ for (let index = 0; index < logs.length; index += 1) {
4645
+ const log = logs[index];
4646
+ if (log === void 0) continue;
4647
+ entries.push({ log, stopReason: extractStopReason(log), sessionLogNumber: index + 1 });
4648
+ const current = entries[entries.length - 1];
4649
+ if (current !== void 0 && isTurnBoundary(current.stopReason)) {
4650
+ groups.push({ entries, turnIndex });
4651
+ entries = [];
4652
+ turnIndex += 1;
4653
+ }
4952
4654
  }
4953
- return null;
4655
+ if (entries.length > 0) groups.push({ entries, turnIndex });
4656
+ return groups;
4954
4657
  }
4955
- function slowestLog(logs) {
4956
- let current = null;
4957
- let currentElapsed = -1;
4958
- for (const log of logs) {
4959
- if (log.elapsedMs === null) continue;
4960
- if (log.elapsedMs > currentElapsed) {
4961
- current = log;
4962
- currentElapsed = log.elapsedMs;
4658
+ function buildValidPredecessors(groups) {
4659
+ const predecessors = /* @__PURE__ */ new Map();
4660
+ for (const group of groups) {
4661
+ for (let index = 1; index < group.logs.length; index += 1) {
4662
+ const current = group.logs[index];
4663
+ const previous = group.logs[index - 1];
4664
+ if (current === void 0 || previous === void 0) continue;
4665
+ const currentFormat = resolveLogFormat(current);
4666
+ const previousFormat = resolveLogFormat(previous);
4667
+ if (currentFormat === "unknown" || currentFormat !== previousFormat) continue;
4668
+ predecessors.set(current.id, previous);
4963
4669
  }
4964
4670
  }
4965
- return current;
4671
+ return predecessors;
4966
4672
  }
4967
- function firstToolLog(logs) {
4968
- for (const log of logs) {
4969
- if (extractToolTraceEvents(log).length > 0) return log;
4673
+ function parseJsonResponse(responseText) {
4674
+ if (responseText === null) return null;
4675
+ try {
4676
+ const parsed = JSON.parse(responseText);
4677
+ if (typeof parsed === "string") {
4678
+ return JSON.parse(parsed);
4679
+ }
4680
+ return parsed;
4681
+ } catch {
4682
+ return null;
4970
4683
  }
4971
- return null;
4972
4684
  }
4973
- function buildDisplayNumberByLogId$1(logs) {
4974
- const result = /* @__PURE__ */ new Map();
4975
- for (let index = 0; index < logs.length; index += 1) {
4976
- const log = logs[index];
4977
- if (log === void 0) continue;
4978
- result.set(log.id, index + 1);
4979
- }
4980
- return result;
4685
+ function previewValue(value) {
4686
+ if (value === void 0 || value === null) return null;
4687
+ const raw = typeof value === "string" ? value : JSON.stringify(value);
4688
+ if (raw === void 0) return null;
4689
+ const normalized = raw.replace(/\s+/g, " ").trim();
4690
+ if (normalized.length === 0) return null;
4691
+ return normalized.length > PREVIEW_LIMIT ? `${normalized.slice(0, PREVIEW_LIMIT - 1)}...` : normalized;
4981
4692
  }
4982
- function displayLogNumber(logId, displayNumberByLogId) {
4983
- return String(displayNumberByLogId.get(logId) ?? logId);
4693
+ function copyValue(value) {
4694
+ if (value === void 0 || value === null) return null;
4695
+ if (typeof value === "string") return value.length > 0 ? value : null;
4696
+ const raw = JSON.stringify(value, null, 2);
4697
+ return raw === void 0 || raw.length === 0 ? null : raw;
4984
4698
  }
4985
- function buildTraceInsights(input) {
4986
- const insights = [
4987
- {
4988
- kind: "session",
4989
- title: `${String(input.summary.llmCallCount)} LLM call${input.summary.llmCallCount === 1 ? "" : "s"}`,
4990
- detail: `Scope ${input.scopeId}`,
4991
- logId: input.logs[0]?.id ?? null
4992
- }
4993
- ];
4994
- const toolLog = firstToolLog(input.logs);
4995
- if (input.summary.toolCallCount > 0 && toolLog !== null) {
4996
- insights.push({
4997
- kind: "tool",
4998
- title: `${String(input.summary.toolCallCount)} tool call${input.summary.toolCallCount === 1 ? "" : "s"}`,
4999
- detail: `First tool evidence at #${displayLogNumber(toolLog.id, input.displayNumberByLogId)}`,
5000
- logId: toolLog.id
5001
- });
5002
- }
5003
- const failure = firstFailureLog(input.logs);
5004
- if (failure !== null) {
5005
- insights.push({
5006
- kind: "failure",
5007
- title: `Failure #${String(failure.responseStatus)}`,
5008
- detail: `First failed request is #${displayLogNumber(failure.id, input.displayNumberByLogId)}`,
5009
- logId: failure.id
4699
+ function extractAnthropicToolTraceEvents(log) {
4700
+ const parsed = parseJsonResponse(log.responseText);
4701
+ const content = safeGetOwnProperty(parsed, "content");
4702
+ if (!Array.isArray(content)) return [];
4703
+ const events = [];
4704
+ for (const block of content) {
4705
+ const type = safeGetOwnProperty(block, "type");
4706
+ if (type !== "tool_use") continue;
4707
+ const name = safeGetOwnProperty(block, "name");
4708
+ if (typeof name !== "string" || name.length === 0) continue;
4709
+ const input = safeGetOwnProperty(block, "input");
4710
+ events.push({
4711
+ id: `${String(log.id)}-anthropic-tool-${String(events.length)}`,
4712
+ logId: log.id,
4713
+ index: events.length,
4714
+ provider: "anthropic",
4715
+ name,
4716
+ argumentsText: copyValue(input),
4717
+ argumentsPreview: previewValue(input)
5010
4718
  });
5011
4719
  }
5012
- const slowest = slowestLog(input.logs);
5013
- if (slowest !== null && input.summary.maxElapsedMs !== null) {
5014
- insights.push({
5015
- kind: "slow",
5016
- title: `Slowest ${formatElapsed$1(input.summary.maxElapsedMs)}`,
5017
- detail: `Max latency observed at #${displayLogNumber(slowest.id, input.displayNumberByLogId)}`,
5018
- logId: slowest.id
5019
- });
4720
+ return events;
4721
+ }
4722
+ function extractOpenAIToolTraceEvents(log) {
4723
+ const parsed = parseJsonResponse(log.responseText);
4724
+ const choices = safeGetOwnProperty(parsed, "choices");
4725
+ if (!Array.isArray(choices)) return [];
4726
+ const events = [];
4727
+ for (const choice of choices) {
4728
+ const message = safeGetOwnProperty(choice, "message");
4729
+ const toolCalls = safeGetOwnProperty(message, "tool_calls");
4730
+ if (!Array.isArray(toolCalls)) continue;
4731
+ for (const call of toolCalls) {
4732
+ const fn = safeGetOwnProperty(call, "function");
4733
+ const name = safeGetOwnProperty(fn, "name");
4734
+ if (typeof name !== "string" || name.length === 0) continue;
4735
+ const args = safeGetOwnProperty(fn, "arguments");
4736
+ events.push({
4737
+ id: `${String(log.id)}-openai-tool-${String(events.length)}`,
4738
+ logId: log.id,
4739
+ index: events.length,
4740
+ provider: "openai",
4741
+ name,
4742
+ argumentsText: copyValue(args),
4743
+ argumentsPreview: previewValue(args)
4744
+ });
4745
+ }
5020
4746
  }
5021
- if (input.candidates.length > 0) {
5022
- const draftCount = input.candidates.filter((candidate) => candidate.status === "draft").length;
5023
- const promotedCount = input.candidates.filter(
5024
- (candidate) => candidate.status === "promoted"
5025
- ).length;
5026
- insights.push({
5027
- kind: "candidate",
5028
- title: `${String(input.candidates.length)} memory candidate${input.candidates.length === 1 ? "" : "s"}`,
5029
- detail: `${String(draftCount)} draft / ${String(promotedCount)} promoted`,
5030
- logId: input.candidates[0]?.logIds[0] ?? null
5031
- });
4747
+ const output = safeGetOwnProperty(parsed, "output");
4748
+ if (Array.isArray(output)) {
4749
+ for (const item of output) {
4750
+ if (safeGetOwnProperty(item, "type") !== "function_call") continue;
4751
+ const name = safeGetOwnProperty(item, "name");
4752
+ if (typeof name !== "string" || name.length === 0) continue;
4753
+ const args = safeGetOwnProperty(item, "arguments");
4754
+ events.push({
4755
+ id: `${String(log.id)}-openai-tool-${String(events.length)}`,
4756
+ logId: log.id,
4757
+ index: events.length,
4758
+ provider: "openai",
4759
+ name,
4760
+ argumentsText: copyValue(args),
4761
+ argumentsPreview: previewValue(args)
4762
+ });
4763
+ }
5032
4764
  }
5033
- return insights;
4765
+ return events;
5034
4766
  }
5035
- function insightIcon(kind) {
5036
- switch (kind) {
5037
- case "session":
5038
- return /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3.5 text-blue-400" });
5039
- case "tool":
5040
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-sky-400/70" });
5041
- case "slow":
5042
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3.5 text-amber-400" });
5043
- case "failure":
5044
- return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3.5 text-destructive" });
5045
- case "candidate":
5046
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5 text-emerald-400" });
4767
+ function extractToolTraceEvents(log) {
4768
+ const cached = toolTraceCache.get(log);
4769
+ if (cached !== void 0 && cached.apiFormat === log.apiFormat && cached.responseText === log.responseText) {
4770
+ return cached.events;
5047
4771
  }
5048
- }
5049
- function TraceInsights({ insights }) {
5050
- if (insights.length === 0) return null;
5051
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1.5 border-t border-border/70 pt-2 md:grid-cols-2 xl:grid-cols-3", children: insights.map((insight) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
5052
- "button",
5053
- {
5054
- type: "button",
5055
- className: cn(
5056
- "flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs",
5057
- "text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground",
5058
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
5059
- ),
5060
- onClick: () => {
5061
- if (insight.logId !== null) jumpToLog(insight.logId);
5062
- },
5063
- disabled: insight.logId === null,
5064
- children: [
5065
- insightIcon(insight.kind),
5066
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0", children: [
5067
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate font-medium text-foreground/90", children: insight.title }),
5068
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate font-mono text-[10px]", children: insight.detail })
5069
- ] })
5070
- ]
5071
- },
5072
- `${insight.kind}-${insight.title}`
5073
- )) });
5074
- }
5075
- function candidateStatusLabel(status) {
5076
- switch (status) {
5077
- case "draft":
5078
- return "Draft";
5079
- case "promoted":
5080
- return "Promoted";
5081
- case "failed":
5082
- return "Failed";
4772
+ const format = resolveLogFormat(log);
4773
+ if (!responseMayContainToolTrace(log, format)) {
4774
+ const events2 = [];
4775
+ toolTraceCache.set(log, {
4776
+ apiFormat: log.apiFormat,
4777
+ responseText: log.responseText,
4778
+ events: events2
4779
+ });
4780
+ return events2;
5083
4781
  }
5084
- }
5085
- function candidatePromoteLabel(candidate, promoting) {
5086
- if (promoting) return "Promoting";
5087
- switch (candidate.status) {
5088
- case "draft":
5089
- return "Promote";
5090
- case "failed":
5091
- return "Retry";
5092
- case "promoted":
5093
- return "Promoted";
4782
+ let events;
4783
+ switch (format) {
4784
+ case "anthropic":
4785
+ events = extractAnthropicToolTraceEvents(log);
4786
+ break;
4787
+ case "openai":
4788
+ events = extractOpenAIToolTraceEvents(log);
4789
+ break;
4790
+ case "unknown":
4791
+ events = [];
4792
+ break;
5094
4793
  }
4794
+ toolTraceCache.set(log, {
4795
+ apiFormat: log.apiFormat,
4796
+ responseText: log.responseText,
4797
+ events
4798
+ });
4799
+ return events;
5095
4800
  }
5096
- function redactionLabel(candidate) {
5097
- if (!candidate.redaction.redacted) return "No sensitive pattern matched";
5098
- return `Redacted ${candidate.redaction.patterns.join(", ")}`;
5099
- }
5100
- function previewText(value, maxLength) {
5101
- const normalized = value.replace(/\s+/g, " ").trim();
5102
- if (normalized.length <= maxLength) return normalized;
5103
- return `${normalized.slice(0, maxLength - 3)}...`;
5104
- }
5105
- function logRangeLabel(logIds, displayNumberByLogId) {
5106
- const first = logIds[0];
5107
- const last = logIds[logIds.length - 1];
5108
- if (first === void 0) return "No evidence logs";
5109
- const firstLabel = displayLogNumber(first, displayNumberByLogId);
5110
- if (last === void 0 || first === last) return `#${firstLabel}`;
5111
- return `#${firstLabel}-#${displayLogNumber(last, displayNumberByLogId)}`;
5112
- }
5113
- function tagsToText(tags) {
5114
- return tags.join(", ");
5115
- }
5116
- function textToTags(value) {
5117
- return value.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0);
5118
- }
5119
- function CandidateItem({
5120
- candidate,
5121
- isPromoting,
5122
- isUpdating,
5123
- onPromoteCandidate,
5124
- onUpdateCandidate,
5125
- displayNumberByLogId
5126
- }) {
5127
- const [editing, setEditing] = reactExports.useState(false);
5128
- const [draftType, setDraftType] = reactExports.useState(candidate.type);
5129
- const [draftTitle, setDraftTitle] = reactExports.useState(candidate.title);
5130
- const [draftContent, setDraftContent] = reactExports.useState(candidate.content);
5131
- const [draftTags, setDraftTags] = reactExports.useState(tagsToText(candidate.tags));
5132
- const [localError, setLocalError] = reactExports.useState(null);
5133
- const canPromote = candidate.status !== "promoted";
5134
- const canEdit = candidate.status !== "promoted";
5135
- const resetDraft = reactExports.useCallback(() => {
5136
- setDraftType(candidate.type);
5137
- setDraftTitle(candidate.title);
5138
- setDraftContent(candidate.content);
5139
- setDraftTags(tagsToText(candidate.tags));
5140
- setLocalError(null);
5141
- }, [candidate.content, candidate.tags, candidate.title, candidate.type]);
5142
- const saveDraft = reactExports.useCallback(async () => {
5143
- const title = draftTitle.trim();
5144
- const content = draftContent.trim();
5145
- const tags = textToTags(draftTags);
5146
- if (title.length === 0) {
5147
- setLocalError("Title is required.");
5148
- return;
4801
+ function buildTraceSummary(logs, slowResponseThresholdSeconds, knowledgeCandidateCount = 0) {
4802
+ let failedCallCount = 0;
4803
+ let pendingCallCount = 0;
4804
+ let slowCallCount = 0;
4805
+ let totalInputTokens = 0;
4806
+ let totalOutputTokens = 0;
4807
+ let totalCacheCreationInputTokens = 0;
4808
+ let totalCacheReadInputTokens = 0;
4809
+ let totalElapsedMs = 0;
4810
+ let maxElapsedMs = null;
4811
+ let toolCallCount = 0;
4812
+ for (const log of logs) {
4813
+ if (log.responseStatus === null) {
4814
+ pendingCallCount += 1;
4815
+ } else if (log.responseStatus >= 400) {
4816
+ failedCallCount += 1;
5149
4817
  }
5150
- if (content.length === 0) {
5151
- setLocalError("Content is required.");
5152
- return;
4818
+ if (log.elapsedMs !== null && slowResponseThresholdSeconds > 0 && log.elapsedMs > slowResponseThresholdSeconds * 1e3) {
4819
+ slowCallCount += 1;
5153
4820
  }
5154
- if (tags.length === 0) {
5155
- setLocalError("At least one tag is required.");
5156
- return;
4821
+ if (log.inputTokens !== null) totalInputTokens += log.inputTokens;
4822
+ if (log.outputTokens !== null) totalOutputTokens += log.outputTokens;
4823
+ if (log.cacheCreationInputTokens !== null) {
4824
+ totalCacheCreationInputTokens += log.cacheCreationInputTokens;
5157
4825
  }
5158
- setLocalError(null);
5159
- const saved = await onUpdateCandidate(candidate.id, {
5160
- type: draftType,
5161
- title,
5162
- content,
5163
- tags
5164
- });
5165
- if (saved) setEditing(false);
5166
- }, [candidate.id, draftContent, draftTags, draftTitle, draftType, onUpdateCandidate]);
5167
- if (editing) {
5168
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border border-border bg-card shadow-sm rounded-[8px] px-2.5 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-2", children: [
5169
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
5170
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
5171
- "select",
5172
- {
5173
- value: draftType,
5174
- disabled: isUpdating,
5175
- onChange: (event) => {
5176
- const parsed = KnowledgeCandidateSchema.shape.type.safeParse(
5177
- event.currentTarget.value
5178
- );
5179
- if (parsed.success) setDraftType(parsed.data);
5180
- },
5181
- className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground h-7 rounded-md px-2 text-xs",
5182
- "aria-label": "Candidate type",
5183
- children: [
5184
- /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "episode", children: "episode" }),
5185
- /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "procedure", children: "procedure" }),
5186
- /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "preference", children: "preference" }),
5187
- /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "project-fact", children: "project-fact" })
5188
- ]
5189
- }
5190
- ),
5191
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5192
- "input",
5193
- {
5194
- value: draftTitle,
5195
- disabled: isUpdating,
5196
- onChange: (event) => setDraftTitle(event.currentTarget.value),
5197
- className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground h-7 min-w-[220px] flex-1 rounded-md px-2 text-xs",
5198
- "aria-label": "Candidate title"
5199
- }
5200
- )
5201
- ] }),
5202
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5203
- "textarea",
5204
- {
5205
- value: draftContent,
5206
- disabled: isUpdating,
5207
- onChange: (event) => setDraftContent(event.currentTarget.value),
5208
- className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground min-h-28 rounded-md px-2 py-1.5 font-mono text-[11px] leading-relaxed",
5209
- "aria-label": "Candidate content"
5210
- }
5211
- ),
5212
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5213
- "input",
5214
- {
5215
- value: draftTags,
5216
- disabled: isUpdating,
5217
- onChange: (event) => setDraftTags(event.currentTarget.value),
5218
- className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground h-7 rounded-md px-2 text-xs",
5219
- "aria-label": "Candidate tags"
5220
- }
5221
- ),
5222
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
5223
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground", children: [
5224
- /* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "size-3" }),
5225
- "Saving reruns Inspector redaction before promotion."
5226
- ] }),
5227
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
5228
- localError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-destructive", children: localError }),
5229
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
5230
- Button,
5231
- {
5232
- type: "button",
5233
- variant: "ghost",
5234
- size: "sm",
5235
- className: "h-7 gap-1.5 px-2 text-xs",
5236
- disabled: isUpdating,
5237
- onClick: () => {
5238
- resetDraft();
5239
- setEditing(false);
5240
- },
5241
- children: [
5242
- /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3.5" }),
5243
- "Cancel"
5244
- ]
5245
- }
5246
- ),
5247
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
5248
- Button,
5249
- {
5250
- type: "button",
5251
- variant: "outline",
5252
- size: "sm",
5253
- className: "h-7 gap-1.5 px-2 text-xs",
5254
- disabled: isUpdating,
5255
- onClick: () => {
5256
- void saveDraft();
5257
- },
5258
- children: [
5259
- isUpdating ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { className: "size-3.5" }),
5260
- "Save"
5261
- ]
5262
- }
5263
- )
5264
- ] })
5265
- ] }) });
4826
+ if (log.cacheReadInputTokens !== null) {
4827
+ totalCacheReadInputTokens += log.cacheReadInputTokens;
4828
+ }
4829
+ if (log.elapsedMs !== null) {
4830
+ totalElapsedMs += log.elapsedMs;
4831
+ maxElapsedMs = maxElapsedMs === null ? log.elapsedMs : Math.max(maxElapsedMs, log.elapsedMs);
4832
+ }
4833
+ toolCallCount += extractToolTraceEvents(log).length;
5266
4834
  }
5267
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border/80 bg-background/60 px-2.5 py-2", children: [
5268
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
5269
- /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-5 px-1.5 text-[10px] font-mono", children: candidate.type }),
5270
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", title: candidate.title, children: candidate.title }),
5271
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5272
- Badge,
5273
- {
5274
- variant: "outline",
5275
- className: cn(
5276
- "h-5 shrink-0 px-1.5 text-[10px] font-mono",
5277
- CANDIDATE_STATUS_CLASSES[candidate.status]
5278
- ),
5279
- children: candidateStatusLabel(candidate.status)
5280
- }
5281
- )
5282
- ] }),
5283
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5284
- "p",
5285
- {
5286
- className: "mt-1 text-[11px] leading-relaxed text-muted-foreground",
5287
- title: candidate.content,
5288
- children: previewText(candidate.content, 360)
5289
- }
5290
- ),
5291
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-muted-foreground", children: [
5292
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [
5293
- /* @__PURE__ */ jsxRuntimeExports.jsx(FileSearch, { className: "size-3" }),
5294
- logRangeLabel(candidate.logIds, displayNumberByLogId)
5295
- ] }),
5296
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [
5297
- /* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "size-3" }),
5298
- redactionLabel(candidate)
5299
- ] }),
5300
- candidate.status === "promoted" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-emerald-500", children: [
5301
- /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "size-3" }),
5302
- candidate.openClawMemoryId ?? "OpenClaw"
5303
- ] }),
5304
- candidate.status === "failed" && candidate.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-destructive", children: [
5305
- /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3" }),
5306
- candidate.error
5307
- ] })
5308
- ] }),
5309
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1.5", children: [
5310
- candidate.logIds.map((logId) => {
5311
- const displayNumber = displayLogNumber(logId, displayNumberByLogId);
5312
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
5313
- "a",
5314
- {
5315
- href: `#${getLogAnchor(logId)}`,
5316
- onClick: (event) => {
5317
- event.preventDefault();
5318
- jumpToLog(logId);
5319
- },
5320
- className: "rounded border border-blue-400/25 px-1.5 py-0.5 font-mono text-[10px] text-blue-400 underline-offset-2 transition-colors hover:bg-blue-400/10 hover:text-blue-300 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5321
- "aria-label": `Jump to evidence log ${displayNumber}`,
5322
- title: `Log ID ${String(logId)}`,
5323
- children: [
5324
- "#",
5325
- displayNumber
5326
- ]
5327
- },
5328
- logId
5329
- );
5330
- }),
5331
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
5332
- canEdit && /* @__PURE__ */ jsxRuntimeExports.jsxs(
5333
- Button,
5334
- {
5335
- type: "button",
5336
- variant: "ghost",
5337
- size: "sm",
5338
- className: "h-6 gap-1.5 px-2 text-[10px]",
5339
- disabled: isUpdating || isPromoting,
5340
- onClick: () => setEditing(true),
5341
- children: [
5342
- /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
5343
- "Review"
5344
- ]
5345
- }
5346
- ),
5347
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
5348
- Button,
5349
- {
5350
- type: "button",
5351
- variant: canPromote ? "outline" : "ghost",
5352
- size: "sm",
5353
- className: "h-6 gap-1.5 px-2 text-[10px]",
5354
- onClick: () => onPromoteCandidate(candidate.id),
5355
- disabled: !canPromote || isPromoting || isUpdating,
5356
- children: [
5357
- isPromoting ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3 animate-spin" }) : canPromote ? /* @__PURE__ */ jsxRuntimeExports.jsx(CloudUpload, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "size-3" }),
5358
- candidatePromoteLabel(candidate, isPromoting)
5359
- ]
5360
- }
5361
- )
5362
- ] })
5363
- ] });
4835
+ return {
4836
+ llmCallCount: logs.length,
4837
+ toolCallCount,
4838
+ failedCallCount,
4839
+ pendingCallCount,
4840
+ slowCallCount,
4841
+ totalInputTokens,
4842
+ totalOutputTokens,
4843
+ totalCacheCreationInputTokens,
4844
+ totalCacheReadInputTokens,
4845
+ totalElapsedMs,
4846
+ maxElapsedMs,
4847
+ startedAt: logs[0]?.timestamp ?? null,
4848
+ endedAt: logs[logs.length - 1]?.timestamp ?? null,
4849
+ knowledgeCandidateCount
4850
+ };
5364
4851
  }
5365
- function CandidateList({
5366
- candidates,
5367
- promotingCandidateIds,
5368
- updatingCandidateIds,
5369
- onPromoteCandidate,
5370
- onUpdateCandidate,
5371
- displayNumberByLogId
5372
- }) {
5373
- if (candidates.length === 0) return null;
5374
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1.5", children: candidates.map((candidate) => /* @__PURE__ */ jsxRuntimeExports.jsx(
5375
- CandidateItem,
5376
- {
5377
- candidate,
5378
- isPromoting: promotingCandidateIds.has(candidate.id),
5379
- isUpdating: updatingCandidateIds.has(candidate.id),
5380
- onPromoteCandidate,
5381
- onUpdateCandidate,
5382
- displayNumberByLogId
5383
- },
5384
- candidate.id
5385
- )) });
4852
+ function formatElapsed$2(ms) {
4853
+ if (ms < 1e3) return `${ms}ms`;
4854
+ return `${(ms / 1e3).toFixed(1)}s`;
5386
4855
  }
5387
- function AgentTraceSummary({
5388
- logs,
5389
- scopeId,
4856
+ const TurnGroup = reactExports.memo(function TurnGroup2({
4857
+ entries,
4858
+ viewMode,
4859
+ strip,
5390
4860
  slowResponseThresholdSeconds,
5391
- showRollupMetrics,
5392
- timeDisplayFormat
4861
+ cacheTrends,
4862
+ onCompareWithPrevious,
4863
+ comparisonPredecessors,
4864
+ turnIndex = 0,
4865
+ timeDisplayFormat,
4866
+ pendingFocusRequest,
4867
+ showLogClientMetadata = true
5393
4868
  }) {
5394
- const [candidates, setCandidates] = reactExports.useState([]);
5395
- const [candidatesExpanded, setCandidatesExpanded] = reactExports.useState(true);
5396
- const [candidateState, setCandidateState] = reactExports.useState({
5397
- status: "idle",
5398
- error: null
5399
- });
5400
- const [promotingCandidateIds, setPromotingCandidateIds] = reactExports.useState(
5401
- () => /* @__PURE__ */ new Set()
5402
- );
5403
- const [updatingCandidateIds, setUpdatingCandidateIds] = reactExports.useState(
5404
- () => /* @__PURE__ */ new Set()
5405
- );
5406
- const hasCandidates = candidates.length > 0;
5407
- const summary = reactExports.useMemo(
5408
- () => buildTraceSummary(logs, slowResponseThresholdSeconds, candidates.length),
5409
- [candidates.length, logs, slowResponseThresholdSeconds]
5410
- );
5411
- const displayNumberByLogId = reactExports.useMemo(() => buildDisplayNumberByLogId$1(logs), [logs]);
5412
- const traceInsights = reactExports.useMemo(
5413
- () => buildTraceInsights({ logs, scopeId, summary, candidates, displayNumberByLogId }),
5414
- [candidates, displayNumberByLogId, logs, scopeId, summary]
5415
- );
5416
- const showElapsedSummary = showRollupMetrics || summary.maxElapsedMs !== null;
5417
- const timeRange = reactExports.useMemo(
5418
- () => formatTimeRange$1(summary.startedAt, summary.endedAt, timeDisplayFormat),
5419
- [summary.endedAt, summary.startedAt, timeDisplayFormat]
5420
- );
5421
- const createCandidates = reactExports.useCallback(() => {
5422
- if (logs.length === 0 || candidateState.status === "loading") return;
5423
- setCandidateState({ status: "loading", error: null });
5424
- void (async () => {
5425
- try {
5426
- const response = await fetch(
5427
- `/api/knowledge/sessions/${encodeURIComponent(scopeId)}/candidates`,
5428
- { method: "POST" }
5429
- );
5430
- if (!response.ok) {
5431
- const message = await readApiError(
5432
- response,
5433
- `Candidate generation failed with ${String(response.status)}`
5434
- );
5435
- setCandidateState({ status: "failed", error: message });
5436
- return;
5437
- }
5438
- const parsed = await parseJsonResponse$1(response, CandidateResponseSchema);
5439
- setCandidates(parsed.candidates);
5440
- setCandidatesExpanded(parsed.candidates.length > 0);
5441
- setCandidateState(
5442
- parsed.candidates.length > 0 ? { status: "ready", error: null } : { status: "failed", error: "No candidate was generated for this trace scope." }
5443
- );
5444
- } catch (error) {
5445
- setCandidateState({
5446
- status: "failed",
5447
- error: error instanceof Error ? error.message : "Candidate response was invalid"
5448
- });
5449
- }
5450
- })();
5451
- }, [candidateState.status, logs.length, scopeId]);
5452
- const promoteCandidate = reactExports.useCallback((candidateId) => {
5453
- setPromotingCandidateIds((current) => {
5454
- if (current.has(candidateId)) return current;
5455
- const next = new Set(current);
5456
- next.add(candidateId);
5457
- return next;
5458
- });
5459
- void (async () => {
5460
- try {
5461
- const response = await fetch(
5462
- `/api/knowledge/candidates/${encodeURIComponent(candidateId)}/promote`,
5463
- { method: "POST" }
5464
- );
5465
- const raw = await response.json().catch(() => null);
5466
- const parsed = CandidatePromotionResponseSchema.safeParse(raw);
5467
- if (response.ok && parsed.success) {
5468
- setCandidates(
5469
- (current) => current.map(
5470
- (candidate) => candidate.id === parsed.data.candidate.id ? parsed.data.candidate : candidate
5471
- )
5472
- );
5473
- setCandidateState({ status: "ready", error: null });
5474
- return;
5475
- }
5476
- const failure = CandidatePromotionFailureResponseSchema.safeParse(raw);
5477
- if (failure.success && failure.data.candidate !== void 0) {
5478
- setCandidates(
5479
- (current) => current.map(
5480
- (candidate) => candidate.id === failure.data.candidate?.id ? failure.data.candidate : candidate
5481
- )
5482
- );
5483
- }
5484
- setCandidateState({
5485
- status: "failed",
5486
- error: failure.success ? failure.data.error ?? "Candidate promotion failed" : `Candidate promotion failed with ${String(response.status)}`
5487
- });
5488
- } catch (error) {
5489
- setCandidateState({
5490
- status: "failed",
5491
- error: error instanceof Error ? error.message : "Candidate promotion failed"
5492
- });
5493
- } finally {
5494
- setPromotingCandidateIds((current) => {
5495
- const next = new Set(current);
5496
- next.delete(candidateId);
5497
- return next;
5498
- });
5499
- }
5500
- })();
5501
- }, []);
5502
- const updateCandidate = reactExports.useCallback(
5503
- async (candidateId, update) => {
5504
- setUpdatingCandidateIds((current) => {
5505
- if (current.has(candidateId)) return current;
5506
- const next = new Set(current);
5507
- next.add(candidateId);
5508
- return next;
4869
+ const lastIdx = entries.length - 1;
4870
+ const lastStop = entries[lastIdx]?.stopReason ?? null;
4871
+ const isComplete = lastStop !== null ? isTurnBoundary(lastStop) : false;
4872
+ const isPending = entries[lastIdx]?.log.responseStatus === null;
4873
+ const isSingleLog = entries.length === 1;
4874
+ const collapsible = isTurnCollapsible(entries.length, isComplete, isPending);
4875
+ const [collapsed, setCollapsed] = reactExports.useState(false);
4876
+ const prevCompleteRef = reactExports.useRef(false);
4877
+ reactExports.useEffect(() => {
4878
+ if (!collapsible) {
4879
+ setCollapsed(false);
4880
+ } else if (isComplete && !prevCompleteRef.current) {
4881
+ setCollapsed(true);
4882
+ }
4883
+ prevCompleteRef.current = isComplete;
4884
+ }, [collapsible, isComplete]);
4885
+ reactExports.useEffect(() => {
4886
+ const handleLogFocusRequest = (event) => {
4887
+ const request = readLogFocusRequest(event);
4888
+ if (request === null) return;
4889
+ if (!entries.some((entry) => entry.log.id === request.logId)) return;
4890
+ if (collapsible) setCollapsed(false);
4891
+ };
4892
+ window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4893
+ return () => {
4894
+ window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4895
+ };
4896
+ }, [collapsible, entries]);
4897
+ reactExports.useEffect(() => {
4898
+ if (pendingFocusRequest === void 0) return void 0;
4899
+ if (!entries.some((entry) => entry.log.id === pendingFocusRequest.logId)) return void 0;
4900
+ if (collapsible) setCollapsed(false);
4901
+ const timer = window.setTimeout(() => {
4902
+ dispatchLogFocusRequest({
4903
+ logId: pendingFocusRequest.logId,
4904
+ tab: pendingFocusRequest.tab,
4905
+ source: "virtualizer"
5509
4906
  });
5510
- try {
5511
- const response = await fetch(
5512
- `/api/knowledge/candidates/${encodeURIComponent(candidateId)}`,
5513
- {
5514
- method: "PATCH",
5515
- headers: { "Content-Type": "application/json" },
5516
- body: JSON.stringify(update)
5517
- }
5518
- );
5519
- if (!response.ok) {
5520
- const message = await readApiError(
5521
- response,
5522
- `Candidate update failed with ${String(response.status)}`
5523
- );
5524
- setCandidateState({ status: "failed", error: message });
5525
- return false;
5526
- }
5527
- const parsed = await parseJsonResponse$1(response, CandidateUpdateResponseSchema);
5528
- setCandidates(
5529
- (current) => current.map(
5530
- (candidate) => candidate.id === parsed.candidate.id ? parsed.candidate : candidate
5531
- )
5532
- );
5533
- setCandidateState({ status: "ready", error: null });
5534
- return true;
5535
- } catch (error) {
5536
- setCandidateState({
5537
- status: "failed",
5538
- error: error instanceof Error ? error.message : "Candidate update failed"
5539
- });
5540
- return false;
5541
- } finally {
5542
- setUpdatingCandidateIds((current) => {
5543
- const next = new Set(current);
5544
- next.delete(candidateId);
5545
- return next;
5546
- });
4907
+ }, 0);
4908
+ return () => window.clearTimeout(timer);
4909
+ }, [collapsible, entries, pendingFocusRequest]);
4910
+ const toggleCollapse = reactExports.useCallback(() => {
4911
+ if (collapsible) setCollapsed((prev) => !prev);
4912
+ }, [collapsible]);
4913
+ const aggregate = reactExports.useMemo(() => {
4914
+ let totalInput = 0;
4915
+ let totalOutput = 0;
4916
+ let maxElapsed = null;
4917
+ let hasTokens = false;
4918
+ for (const e of entries) {
4919
+ if (e.log.inputTokens !== null) {
4920
+ totalInput += e.log.inputTokens;
4921
+ hasTokens = true;
5547
4922
  }
5548
- },
5549
- []
4923
+ if (e.log.outputTokens !== null) {
4924
+ totalOutput += e.log.outputTokens;
4925
+ hasTokens = true;
4926
+ }
4927
+ if (e.log.elapsedMs !== null) {
4928
+ maxElapsed = maxElapsed === null ? e.log.elapsedMs : Math.max(maxElapsed, e.log.elapsedMs);
4929
+ }
4930
+ }
4931
+ return {
4932
+ totalInput,
4933
+ totalOutput,
4934
+ hasTokens,
4935
+ maxElapsed
4936
+ };
4937
+ }, [entries, lastIdx]);
4938
+ const bgClass = turnIndex % 2 === 0 ? "bg-white/[0.008]" : "bg-cyan-300/[0.008]";
4939
+ const aggregateIsSlow = aggregate.maxElapsed !== null && slowResponseThresholdSeconds > 0 && aggregate.maxElapsed > slowResponseThresholdSeconds * 1e3;
4940
+ const toolEventsByLogId = reactExports.useMemo(() => {
4941
+ const events = /* @__PURE__ */ new Map();
4942
+ for (const entry of entries) {
4943
+ const extracted = extractToolTraceEvents(entry.log);
4944
+ if (extracted.length > 0) events.set(entry.log.id, extracted);
4945
+ }
4946
+ return events;
4947
+ }, [entries]);
4948
+ const [layoutVersion, setLayoutVersion] = reactExports.useState(0);
4949
+ const containerRef = reactExports.useRef(null);
4950
+ reactExports.useEffect(() => {
4951
+ const el = containerRef.current;
4952
+ if (!el) return;
4953
+ let raf = 0;
4954
+ const ro = new ResizeObserver(() => {
4955
+ window.cancelAnimationFrame(raf);
4956
+ raf = window.requestAnimationFrame(() => setLayoutVersion((v) => v + 1));
4957
+ });
4958
+ ro.observe(el);
4959
+ return () => {
4960
+ ro.disconnect();
4961
+ window.cancelAnimationFrame(raf);
4962
+ };
4963
+ }, []);
4964
+ const firstLogId = entries[0]?.log.id ?? turnIndex;
4965
+ const turnLabel = `Turn ${String(turnIndex + 1)}`;
4966
+ const containmentStyle = reactExports.useMemo(
4967
+ () => ({
4968
+ contentVisibility: "auto",
4969
+ containIntrinsicSize: collapsed ? "48px" : "160px"
4970
+ }),
4971
+ [collapsed]
5550
4972
  );
5551
- if (logs.length === 0) return null;
5552
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "border border-border bg-card shadow-sm mb-2 rounded-[8px] px-3 py-2", children: [
5553
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs", children: [
5554
- showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 font-semibold text-foreground", children: [
5555
- /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3.5 text-blue-400" }),
5556
- summary.llmCallCount,
5557
- " LLM"
5558
- ] }),
5559
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5560
- /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-sky-400/70" }),
5561
- summary.toolCallCount,
5562
- " tools"
5563
- ] }),
5564
- showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5565
- /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-emerald-400" }),
5566
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
5567
- formatTokens(summary.totalInputTokens),
5568
- " / ",
5569
- formatTokens(summary.totalOutputTokens)
5570
- ] })
5571
- ] }),
5572
- (summary.totalCacheCreationInputTokens > 0 || summary.totalCacheReadInputTokens > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5573
- /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-purple-400" }),
5574
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
5575
- "KV Cache +",
5576
- formatTokens(summary.totalCacheCreationInputTokens),
5577
- " / ~",
5578
- formatTokens(summary.totalCacheReadInputTokens)
5579
- ] })
5580
- ] }),
5581
- showElapsedSummary && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5582
- /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3.5" }),
5583
- showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: formatElapsed$1(summary.totalElapsedMs) }),
5584
- summary.maxElapsedMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono text-muted-foreground/70", children: [
5585
- "max ",
5586
- formatElapsed$1(summary.maxElapsedMs)
5587
- ] })
5588
- ] }),
5589
- showRollupMetrics && timeRange !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-muted-foreground/70", children: timeRange }),
5590
- (summary.failedCallCount > 0 || summary.pendingCallCount > 0 || summary.slowCallCount > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-amber-400", children: [
5591
- /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3.5" }),
5592
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
5593
- summary.failedCallCount,
5594
- " fail / ",
5595
- summary.pendingCallCount,
5596
- " pending /",
5597
- " ",
5598
- summary.slowCallCount,
5599
- " slow"
5600
- ] })
5601
- ] }),
5602
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
5603
- hasCandidates && /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-6 px-2 text-[10px] font-mono", children: formatCandidateCount(summary.knowledgeCandidateCount) }),
5604
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
5605
- Button,
4973
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4974
+ "div",
4975
+ {
4976
+ ref: containerRef,
4977
+ style: containmentStyle,
4978
+ tabIndex: collapsed ? void 0 : 0,
4979
+ role: collapsed ? void 0 : "group",
4980
+ "aria-label": collapsed ? void 0 : turnLabel,
4981
+ "data-nav-id": collapsed ? void 0 : `turn-${String(firstLogId)}`,
4982
+ className: cn(
4983
+ "rounded-[8px] transition-[background-color,box-shadow]",
4984
+ isPending ? "bg-amber-300/[0.018] shadow-[0_0_0_1px_rgba(252,211,77,0.035)]" : "",
4985
+ !collapsed && "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
4986
+ ),
4987
+ children: collapsed ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
4988
+ "div",
5606
4989
  {
5607
- type: "button",
5608
- variant: "outline",
5609
- size: "sm",
5610
- className: "h-7 gap-1.5 px-2 text-xs",
5611
- onClick: createCandidates,
5612
- disabled: candidateState.status === "loading",
4990
+ "data-nav-id": `turn-collapsed-${String(firstLogId)}`,
4991
+ "data-nav-action": "expand",
4992
+ role: "button",
4993
+ tabIndex: 0,
4994
+ className: "flex items-stretch cursor-pointer focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none rounded-lg",
4995
+ onClick: toggleCollapse,
4996
+ onKeyDown: (e) => {
4997
+ if (e.key === "Enter" || e.key === " ") {
4998
+ e.preventDefault();
4999
+ toggleCollapse();
5000
+ }
5001
+ },
5613
5002
  children: [
5614
- candidateState.status === "loading" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5" }),
5615
- "Candidate"
5003
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 shrink-0 flex items-start pt-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground/50 font-mono tabular-nums leading-none select-none", children: turnIndex + 1 }) }),
5004
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-6 shrink-0 flex flex-col items-center pt-1.5 pb-1.5", children: [
5005
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center h-[calc(0.75rem-8px)]" }),
5006
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "size-2 rounded-full bg-cyan-300/45" }),
5007
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex justify-center min-h-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-px bg-muted-foreground/14 h-full" }) }),
5008
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5009
+ "span",
5010
+ {
5011
+ className: cn(
5012
+ "size-2 rounded-full",
5013
+ isComplete ? "bg-amber-300/45" : "bg-muted-foreground/25"
5014
+ )
5015
+ }
5016
+ )
5017
+ ] }),
5018
+ entries.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
5019
+ "div",
5020
+ {
5021
+ className: cn(
5022
+ "flex-1 min-w-0 mb-0.5 rounded-[8px] py-1.5 px-3 flex items-center gap-3 text-xs",
5023
+ "bg-card/80 transition-colors hover:bg-card",
5024
+ bgClass
5025
+ ),
5026
+ children: [
5027
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-[10px] font-semibold uppercase tracking-[0.14em] text-muted-foreground/70", children: turnLabel }),
5028
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5029
+ "span",
5030
+ {
5031
+ className: "text-blue-300/85 font-mono font-semibold tabular-nums shrink-0",
5032
+ title: `Log IDs ${String(entries[0]?.log.id ?? "?")} ~ ${String(
5033
+ entries[lastIdx]?.log.id ?? "?"
5034
+ )}`,
5035
+ children: [
5036
+ "#",
5037
+ entries[0]?.sessionLogNumber ?? "?",
5038
+ " ~ #",
5039
+ entries[lastIdx]?.sessionLogNumber ?? "?"
5040
+ ]
5041
+ }
5042
+ ),
5043
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground shrink-0", children: [
5044
+ entries.length,
5045
+ " request",
5046
+ entries.length > 1 ? "s" : ""
5047
+ ] }),
5048
+ aggregate.maxElapsed !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
5049
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
5050
+ "span",
5051
+ {
5052
+ className: cn(
5053
+ "flex items-center gap-1 shrink-0",
5054
+ aggregateIsSlow ? "text-amber-400" : "text-muted-foreground"
5055
+ ),
5056
+ children: [
5057
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
5058
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed$2(aggregate.maxElapsed) }),
5059
+ aggregateIsSlow && /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Slow response" })
5060
+ ]
5061
+ }
5062
+ ) }),
5063
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: aggregateIsSlow ? `Slow response: ${formatElapsed$2(
5064
+ aggregate.maxElapsed
5065
+ )} exceeds ${formatElapsed$2(slowResponseThresholdSeconds * 1e3)}` : "Slowest request in this turn" })
5066
+ ] }) }),
5067
+ aggregate.hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 shrink-0", children: [
5068
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
5069
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
5070
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-400", children: [
5071
+ "IN ",
5072
+ formatTokens(aggregate.totalInput)
5073
+ ] }),
5074
+ " / ",
5075
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-amber-400", children: [
5076
+ "OUT ",
5077
+ formatTokens(aggregate.totalOutput)
5078
+ ] })
5079
+ ] })
5080
+ ] }),
5081
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 min-w-0" }),
5082
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5083
+ "span",
5084
+ {
5085
+ className: "bg-white/[0.045] inline-flex size-7 shrink-0 items-center justify-center rounded-md text-slate-200",
5086
+ "aria-hidden": "true",
5087
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4 shrink-0" })
5088
+ }
5089
+ )
5090
+ ]
5091
+ }
5092
+ )
5616
5093
  ]
5617
5094
  }
5618
- ),
5619
- hasCandidates && /* @__PURE__ */ jsxRuntimeExports.jsx(
5620
- Button,
5621
- {
5622
- type: "button",
5623
- variant: "ghost",
5624
- size: "icon",
5625
- className: "size-7 text-muted-foreground",
5626
- onClick: () => setCandidatesExpanded((value) => !value),
5627
- "aria-expanded": candidatesExpanded,
5628
- "aria-label": candidatesExpanded ? "Collapse memory candidates" : "Expand memory candidates",
5629
- children: candidatesExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3.5" })
5630
- }
5095
+ ) : (
5096
+ /* ---- Expanded: full entries ---- */
5097
+ entries.map((entry, visibleIdx) => {
5098
+ const { log, stopReason: reason } = entry;
5099
+ const isTurnStart = visibleIdx === 0;
5100
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-stretch", children: [
5101
+ isTurnStart ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 shrink-0 flex items-start pt-1.5", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground/50 font-mono tabular-nums leading-none select-none", children: turnIndex + 1 }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-5 shrink-0" }),
5102
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5103
+ ThreadConnector,
5104
+ {
5105
+ stopReason: reason,
5106
+ isPending: log.responseStatus === null,
5107
+ isFirst: visibleIdx === 0,
5108
+ isTurnStart,
5109
+ isOnlyEntry: isSingleLog,
5110
+ crabIndex: log.id % 12,
5111
+ collapsible: collapsible && isTurnStart,
5112
+ onToggle: toggleCollapse
5113
+ }
5114
+ ),
5115
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("flex-1 min-w-0 mb-0.5 rounded-[8px]", bgClass), children: [
5116
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5117
+ LogEntry,
5118
+ {
5119
+ log,
5120
+ displayNumber: entry.sessionLogNumber,
5121
+ viewMode,
5122
+ strip,
5123
+ slowResponseThresholdSeconds,
5124
+ timeDisplayFormat,
5125
+ cacheTrend: cacheTrends?.get(log.id) ?? null,
5126
+ showClientMetadata: showLogClientMetadata,
5127
+ onCompareWithPrevious: comparisonPredecessors.has(log.id) ? onCompareWithPrevious : void 0
5128
+ }
5129
+ ),
5130
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEvents, { events: toolEventsByLogId.get(log.id) ?? [] })
5131
+ ] })
5132
+ ] }, log.id);
5133
+ })
5631
5134
  )
5632
- ] }),
5633
- candidateState.status === "failed" && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-xs text-destructive", children: candidateState.error }),
5634
- /* @__PURE__ */ jsxRuntimeExports.jsx(TraceInsights, { insights: traceInsights }),
5635
- candidatesExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx(
5636
- CandidateList,
5637
- {
5638
- candidates,
5639
- promotingCandidateIds,
5640
- updatingCandidateIds,
5641
- onPromoteCandidate: promoteCandidate,
5642
- onUpdateCandidate: updateCandidate,
5643
- displayNumberByLogId
5644
- }
5645
- )
5646
- ] });
5135
+ }
5136
+ );
5137
+ });
5138
+ const VIRTUALIZE_TURN_THRESHOLD = 24;
5139
+ const ESTIMATED_TURN_HEIGHT = 132;
5140
+ const VIRTUAL_OVERSCAN = 8;
5141
+ function shouldVirtualizeTurnGroups(turnCount) {
5142
+ return turnCount > VIRTUALIZE_TURN_THRESHOLD;
5647
5143
  }
5648
- function computeStats(logs) {
5649
- let totalInput = 0;
5650
- let totalOutput = 0;
5144
+ function buildLogTurnIndex(turnGroups) {
5145
+ const result = /* @__PURE__ */ new Map();
5146
+ for (let index = 0; index < turnGroups.length; index++) {
5147
+ const group = turnGroups[index];
5148
+ if (group === void 0) continue;
5149
+ for (const entry of group.entries) {
5150
+ result.set(entry.log.id, index);
5151
+ }
5152
+ }
5153
+ return result;
5154
+ }
5155
+ function renderTurnGroup(turnGroup, props, pendingFocusRequest) {
5156
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
5157
+ TurnGroup,
5158
+ {
5159
+ entries: turnGroup.entries,
5160
+ viewMode: props.viewMode,
5161
+ strip: props.strip,
5162
+ slowResponseThresholdSeconds: props.slowResponseThresholdSeconds,
5163
+ timeDisplayFormat: props.timeDisplayFormat,
5164
+ cacheTrends: props.cacheTrends,
5165
+ onCompareWithPrevious: props.onCompareWithPrevious,
5166
+ comparisonPredecessors: props.comparisonPredecessors,
5167
+ turnIndex: turnGroup.turnIndex,
5168
+ pendingFocusRequest,
5169
+ showLogClientMetadata: props.showLogClientMetadata
5170
+ },
5171
+ turnGroup.turnIndex
5172
+ );
5173
+ }
5174
+ function TurnGroupList(props) {
5175
+ if (!shouldVirtualizeTurnGroups(props.turnGroups.length)) {
5176
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: props.turnGroups.map((turnGroup) => renderTurnGroup(turnGroup, props)) });
5177
+ }
5178
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(VirtualizedTurnGroupList, { ...props });
5179
+ }
5180
+ function VirtualizedTurnGroupList(props) {
5181
+ const { turnGroups } = props;
5182
+ const [pendingFocusRequest, setPendingFocusRequest] = reactExports.useState(
5183
+ null
5184
+ );
5185
+ const virtualizer = useWindowVirtualizer({
5186
+ count: turnGroups.length,
5187
+ estimateSize: () => ESTIMATED_TURN_HEIGHT,
5188
+ overscan: VIRTUAL_OVERSCAN,
5189
+ getItemKey: (index) => turnGroups[index]?.turnIndex ?? index
5190
+ });
5191
+ const turnIndexByLogId = reactExports.useMemo(() => buildLogTurnIndex(turnGroups), [turnGroups]);
5192
+ reactExports.useEffect(() => {
5193
+ const handleLogFocusRequest = (event) => {
5194
+ const request = readLogFocusRequest(event);
5195
+ if (request === null) return;
5196
+ if (request.source === "virtualizer") return;
5197
+ const turnIndex = turnIndexByLogId.get(request.logId);
5198
+ if (turnIndex === void 0) return;
5199
+ virtualizer.scrollToIndex(turnIndex, { align: "center" });
5200
+ setPendingFocusRequest((previous) => ({
5201
+ ...request,
5202
+ turnIndex,
5203
+ nonce: previous === null ? 1 : previous.nonce + 1
5204
+ }));
5205
+ };
5206
+ window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
5207
+ return () => {
5208
+ window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
5209
+ };
5210
+ }, [turnIndexByLogId, virtualizer]);
5211
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
5212
+ "div",
5213
+ {
5214
+ className: "relative w-full",
5215
+ style: { height: `${virtualizer.getTotalSize()}px` },
5216
+ "data-virtualized-turn-list": "true",
5217
+ children: virtualizer.getVirtualItems().map((virtualItem) => {
5218
+ const turnGroup = turnGroups[virtualItem.index];
5219
+ if (turnGroup === void 0) return null;
5220
+ const itemFocusRequest = pendingFocusRequest !== null && pendingFocusRequest.turnIndex === virtualItem.index ? pendingFocusRequest : void 0;
5221
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
5222
+ "div",
5223
+ {
5224
+ "data-index": virtualItem.index,
5225
+ ref: virtualizer.measureElement,
5226
+ className: "absolute left-0 top-0 w-full",
5227
+ style: { transform: `translateY(${virtualItem.start}px)` },
5228
+ children: renderTurnGroup(turnGroup, props, itemFocusRequest)
5229
+ },
5230
+ virtualItem.key
5231
+ );
5232
+ })
5233
+ }
5234
+ );
5235
+ }
5236
+ const CandidateResponseSchema = object({
5237
+ candidates: array(KnowledgeCandidateSchema)
5238
+ });
5239
+ const CandidatePromotionResponseSchema = object({
5240
+ candidate: KnowledgeCandidateSchema
5241
+ });
5242
+ const CandidatePromotionFailureResponseSchema = object({
5243
+ error: string().optional(),
5244
+ candidate: KnowledgeCandidateSchema.optional()
5245
+ });
5246
+ const CandidateUpdateResponseSchema = object({
5247
+ candidate: KnowledgeCandidateSchema
5248
+ });
5249
+ const CANDIDATE_STATUS_CLASSES = {
5250
+ draft: "border-amber-500/30 bg-amber-500/10 text-amber-500",
5251
+ promoted: "border-emerald-500/30 bg-emerald-500/10 text-emerald-500",
5252
+ failed: "border-destructive/30 bg-destructive/10 text-destructive"
5253
+ };
5254
+ function formatElapsed$1(ms) {
5255
+ if (ms === null) return "-";
5256
+ if (ms < 1e3) return `${String(ms)}ms`;
5257
+ return `${(ms / 1e3).toFixed(1)}s`;
5258
+ }
5259
+ function formatTimeRange$1(startedAt, endedAt, timeDisplayFormat) {
5260
+ if (startedAt === null || endedAt === null) return null;
5261
+ return formatTimestampRange(startedAt, endedAt, timeDisplayFormat);
5262
+ }
5263
+ function formatCandidateCount(count) {
5264
+ return `${String(count)} candidate${count === 1 ? "" : "s"}`;
5265
+ }
5266
+ function getLogAnchor(logId) {
5267
+ return `log-${String(logId)}`;
5268
+ }
5269
+ function jumpToLog(logId) {
5270
+ const anchor = getLogAnchor(logId);
5271
+ const target = document.getElementById(anchor);
5272
+ window.history.replaceState(null, "", `#${anchor}`);
5273
+ if (!(target instanceof HTMLElement)) return;
5274
+ target.scrollIntoView({ block: "center", behavior: "smooth" });
5275
+ target.focus({ preventScroll: true });
5276
+ }
5277
+ function firstFailureLog(logs) {
5651
5278
  for (const log of logs) {
5652
- if (log.inputTokens !== null) totalInput += log.inputTokens;
5653
- if (log.outputTokens !== null) totalOutput += log.outputTokens;
5279
+ if (log.responseStatus !== null && log.responseStatus >= 400) return log;
5654
5280
  }
5655
- return { totalInputTokens: totalInput, totalOutputTokens: totalOutput };
5281
+ return null;
5656
5282
  }
5657
- function collectProviders(logs) {
5658
- const buckets = /* @__PURE__ */ new Map();
5283
+ function slowestLog(logs) {
5284
+ let current = null;
5285
+ let currentElapsed = -1;
5659
5286
  for (const log of logs) {
5660
- if (log.model === null) continue;
5661
- const provider = detectProvider(log.model);
5662
- if (provider === "unknown") continue;
5663
- let models = buckets.get(provider);
5664
- if (models === void 0) {
5665
- models = /* @__PURE__ */ new Set();
5666
- buckets.set(provider, models);
5287
+ if (log.elapsedMs === null) continue;
5288
+ if (log.elapsedMs > currentElapsed) {
5289
+ current = log;
5290
+ currentElapsed = log.elapsedMs;
5667
5291
  }
5668
- models.add(log.model);
5669
5292
  }
5670
- return [...buckets.entries()].map(([provider, models]) => ({
5671
- provider,
5672
- models: [...models]
5673
- }));
5293
+ return current;
5674
5294
  }
5675
- function getFirstClientPid(logs) {
5295
+ function firstToolLog(logs) {
5676
5296
  for (const log of logs) {
5677
- const pid = log.clientPid;
5678
- if (pid !== null && pid !== void 0) return pid;
5297
+ if (extractToolTraceEvents(log).length > 0) return log;
5679
5298
  }
5680
5299
  return null;
5681
5300
  }
5682
- function getFirstClientProjectFolder(logs) {
5683
- for (const log of logs) {
5684
- const projectFolder = log.clientProjectFolder;
5685
- if (projectFolder !== null && projectFolder !== void 0 && projectFolder !== "") {
5686
- return projectFolder;
5301
+ function buildDisplayNumberByLogId$1(logs) {
5302
+ const result = /* @__PURE__ */ new Map();
5303
+ for (let index = 0; index < logs.length; index += 1) {
5304
+ const log = logs[index];
5305
+ if (log === void 0) continue;
5306
+ result.set(log.id, index + 1);
5307
+ }
5308
+ return result;
5309
+ }
5310
+ function displayLogNumber(logId, displayNumberByLogId) {
5311
+ return String(displayNumberByLogId.get(logId) ?? logId);
5312
+ }
5313
+ function buildTraceInsights(input) {
5314
+ const insights = [
5315
+ {
5316
+ kind: "session",
5317
+ title: `${String(input.summary.llmCallCount)} LLM call${input.summary.llmCallCount === 1 ? "" : "s"}`,
5318
+ detail: `Scope ${input.scopeId}`,
5319
+ logId: input.logs[0]?.id ?? null
5687
5320
  }
5321
+ ];
5322
+ const toolLog = firstToolLog(input.logs);
5323
+ if (input.summary.toolCallCount > 0 && toolLog !== null) {
5324
+ insights.push({
5325
+ kind: "tool",
5326
+ title: `${String(input.summary.toolCallCount)} tool call${input.summary.toolCallCount === 1 ? "" : "s"}`,
5327
+ detail: `First tool evidence at #${displayLogNumber(toolLog.id, input.displayNumberByLogId)}`,
5328
+ logId: toolLog.id
5329
+ });
5688
5330
  }
5689
- return null;
5331
+ const failure = firstFailureLog(input.logs);
5332
+ if (failure !== null) {
5333
+ insights.push({
5334
+ kind: "failure",
5335
+ title: `Failure #${String(failure.responseStatus)}`,
5336
+ detail: `First failed request is #${displayLogNumber(failure.id, input.displayNumberByLogId)}`,
5337
+ logId: failure.id
5338
+ });
5339
+ }
5340
+ const slowest = slowestLog(input.logs);
5341
+ if (slowest !== null && input.summary.maxElapsedMs !== null) {
5342
+ insights.push({
5343
+ kind: "slow",
5344
+ title: `Slowest ${formatElapsed$1(input.summary.maxElapsedMs)}`,
5345
+ detail: `Max latency observed at #${displayLogNumber(slowest.id, input.displayNumberByLogId)}`,
5346
+ logId: slowest.id
5347
+ });
5348
+ }
5349
+ if (input.candidates.length > 0) {
5350
+ const draftCount = input.candidates.filter((candidate) => candidate.status === "draft").length;
5351
+ const promotedCount = input.candidates.filter(
5352
+ (candidate) => candidate.status === "promoted"
5353
+ ).length;
5354
+ insights.push({
5355
+ kind: "candidate",
5356
+ title: `${String(input.candidates.length)} memory candidate${input.candidates.length === 1 ? "" : "s"}`,
5357
+ detail: `${String(draftCount)} draft / ${String(promotedCount)} promoted`,
5358
+ logId: input.candidates[0]?.logIds[0] ?? null
5359
+ });
5360
+ }
5361
+ return insights;
5690
5362
  }
5691
- const ConversationGroup = reactExports.memo(function({
5692
- group,
5693
- viewMode = "simple",
5694
- strip,
5695
- slowResponseThresholdSeconds,
5696
- cacheTrends,
5697
- onCompareWithPrevious,
5698
- comparisonPredecessors,
5699
- onClearGroup,
5700
- showClientIdentity = true,
5701
- showProcessMetadata = true,
5702
- showLogClientMetadata = true,
5703
- defaultExpanded = false,
5704
- timeDisplayFormat,
5705
- expanded: controlledExpanded,
5706
- onExpandedChange
5707
- }) {
5708
- const [internalExpanded, setInternalExpanded] = reactExports.useState(defaultExpanded);
5709
- const expanded = controlledExpanded ?? internalExpanded;
5710
- const setExpanded = reactExports.useCallback(
5711
- (nextExpanded) => {
5712
- if (onExpandedChange !== void 0) {
5713
- onExpandedChange(nextExpanded);
5714
- return;
5715
- }
5716
- setInternalExpanded(nextExpanded);
5363
+ function insightIcon(kind) {
5364
+ switch (kind) {
5365
+ case "session":
5366
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3.5 text-blue-400" });
5367
+ case "tool":
5368
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-sky-400/70" });
5369
+ case "slow":
5370
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3.5 text-amber-400" });
5371
+ case "failure":
5372
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3.5 text-destructive" });
5373
+ case "candidate":
5374
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5 text-emerald-400" });
5375
+ }
5376
+ }
5377
+ function TraceInsights({ insights }) {
5378
+ if (insights.length === 0) return null;
5379
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1.5 border-t border-border/70 pt-2 md:grid-cols-2 xl:grid-cols-3", children: insights.map((insight) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
5380
+ "button",
5381
+ {
5382
+ type: "button",
5383
+ className: cn(
5384
+ "flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs",
5385
+ "text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground",
5386
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
5387
+ ),
5388
+ onClick: () => {
5389
+ if (insight.logId !== null) jumpToLog(insight.logId);
5390
+ },
5391
+ disabled: insight.logId === null,
5392
+ children: [
5393
+ insightIcon(insight.kind),
5394
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0", children: [
5395
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate font-medium text-foreground/90", children: insight.title }),
5396
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate font-mono text-[10px]", children: insight.detail })
5397
+ ] })
5398
+ ]
5717
5399
  },
5718
- [onExpandedChange]
5719
- );
5720
- reactExports.useEffect(() => {
5721
- if (!defaultExpanded || controlledExpanded !== void 0) return;
5722
- setInternalExpanded(true);
5723
- }, [controlledExpanded, defaultExpanded]);
5724
- const stats = reactExports.useMemo(() => computeStats(group.logs), [group.logs]);
5725
- const providers = reactExports.useMemo(() => collectProviders(group.logs), [group.logs]);
5726
- const startTime = group.logs[0]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
5727
- const endTime = group.logs[group.logs.length - 1]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
5728
- const isLoading = group.logs.some((log) => log.responseStatus === null);
5729
- const clientPid = reactExports.useMemo(() => getFirstClientPid(group.logs), [group.logs]);
5730
- const clientProjectFolder = reactExports.useMemo(() => getFirstClientProjectFolder(group.logs), [group.logs]);
5731
- const turnGroups = reactExports.useMemo(() => buildTurnGroups(group.logs), [group.logs]);
5732
- reactExports.useEffect(() => {
5733
- const handleLogFocusRequest = (event) => {
5734
- const request = readLogFocusRequest(event);
5735
- if (request === null) return;
5736
- if (!group.logs.some((log) => log.id === request.logId)) return;
5737
- setExpanded(true);
5738
- };
5739
- window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
5740
- return () => {
5741
- window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
5742
- };
5743
- }, [group.logs, setExpanded]);
5744
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2", children: [
5745
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5746
- ConversationHeader,
5747
- {
5748
- conversationId: group.conversationId,
5749
- startTime,
5750
- endTime,
5751
- totalCalls: group.logs.length,
5752
- totalInputTokens: stats.totalInputTokens,
5753
- totalOutputTokens: stats.totalOutputTokens,
5754
- expanded,
5755
- onToggle: () => setExpanded(!expanded),
5756
- providers,
5757
- isLoading,
5758
- userAgent: group.logs[0]?.userAgent ?? null,
5759
- clientPid,
5760
- clientProjectFolder,
5761
- showClientIdentity,
5762
- showProcessMetadata,
5763
- timeDisplayFormat,
5764
- onClear: () => onClearGroup(group.logs.map((l) => l.id))
5765
- }
5766
- ),
5767
- expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
5400
+ `${insight.kind}-${insight.title}`
5401
+ )) });
5402
+ }
5403
+ function candidateStatusLabel(status) {
5404
+ switch (status) {
5405
+ case "draft":
5406
+ return "Draft";
5407
+ case "promoted":
5408
+ return "Promoted";
5409
+ case "failed":
5410
+ return "Failed";
5411
+ }
5412
+ }
5413
+ function candidatePromoteLabel(candidate, promoting) {
5414
+ if (promoting) return "Promoting";
5415
+ switch (candidate.status) {
5416
+ case "draft":
5417
+ return "Promote";
5418
+ case "failed":
5419
+ return "Retry";
5420
+ case "promoted":
5421
+ return "Promoted";
5422
+ }
5423
+ }
5424
+ function redactionLabel(candidate) {
5425
+ if (!candidate.redaction.redacted) return "No sensitive pattern matched";
5426
+ return `Redacted ${candidate.redaction.patterns.join(", ")}`;
5427
+ }
5428
+ function previewText(value, maxLength) {
5429
+ const normalized = value.replace(/\s+/g, " ").trim();
5430
+ if (normalized.length <= maxLength) return normalized;
5431
+ return `${normalized.slice(0, maxLength - 3)}...`;
5432
+ }
5433
+ function logRangeLabel(logIds, displayNumberByLogId) {
5434
+ const first = logIds[0];
5435
+ const last = logIds[logIds.length - 1];
5436
+ if (first === void 0) return "No evidence logs";
5437
+ const firstLabel = displayLogNumber(first, displayNumberByLogId);
5438
+ if (last === void 0 || first === last) return `#${firstLabel}`;
5439
+ return `#${firstLabel}-#${displayLogNumber(last, displayNumberByLogId)}`;
5440
+ }
5441
+ function tagsToText(tags) {
5442
+ return tags.join(", ");
5443
+ }
5444
+ function textToTags(value) {
5445
+ return value.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0);
5446
+ }
5447
+ function CandidateItem({
5448
+ candidate,
5449
+ isPromoting,
5450
+ isUpdating,
5451
+ onPromoteCandidate,
5452
+ onUpdateCandidate,
5453
+ displayNumberByLogId
5454
+ }) {
5455
+ const [editing, setEditing] = reactExports.useState(false);
5456
+ const [draftType, setDraftType] = reactExports.useState(candidate.type);
5457
+ const [draftTitle, setDraftTitle] = reactExports.useState(candidate.title);
5458
+ const [draftContent, setDraftContent] = reactExports.useState(candidate.content);
5459
+ const [draftTags, setDraftTags] = reactExports.useState(tagsToText(candidate.tags));
5460
+ const [localError, setLocalError] = reactExports.useState(null);
5461
+ const canPromote = candidate.status !== "promoted";
5462
+ const canEdit = candidate.status !== "promoted";
5463
+ const resetDraft = reactExports.useCallback(() => {
5464
+ setDraftType(candidate.type);
5465
+ setDraftTitle(candidate.title);
5466
+ setDraftContent(candidate.content);
5467
+ setDraftTags(tagsToText(candidate.tags));
5468
+ setLocalError(null);
5469
+ }, [candidate.content, candidate.tags, candidate.title, candidate.type]);
5470
+ const saveDraft = reactExports.useCallback(async () => {
5471
+ const title = draftTitle.trim();
5472
+ const content = draftContent.trim();
5473
+ const tags = textToTags(draftTags);
5474
+ if (title.length === 0) {
5475
+ setLocalError("Title is required.");
5476
+ return;
5477
+ }
5478
+ if (content.length === 0) {
5479
+ setLocalError("Content is required.");
5480
+ return;
5481
+ }
5482
+ if (tags.length === 0) {
5483
+ setLocalError("At least one tag is required.");
5484
+ return;
5485
+ }
5486
+ setLocalError(null);
5487
+ const saved = await onUpdateCandidate(candidate.id, {
5488
+ type: draftType,
5489
+ title,
5490
+ content,
5491
+ tags
5492
+ });
5493
+ if (saved) setEditing(false);
5494
+ }, [candidate.id, draftContent, draftTags, draftTitle, draftType, onUpdateCandidate]);
5495
+ if (editing) {
5496
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border border-border bg-card shadow-sm rounded-[8px] px-2.5 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-2", children: [
5497
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
5498
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5499
+ "select",
5500
+ {
5501
+ value: draftType,
5502
+ disabled: isUpdating,
5503
+ onChange: (event) => {
5504
+ const parsed = KnowledgeCandidateSchema.shape.type.safeParse(
5505
+ event.currentTarget.value
5506
+ );
5507
+ if (parsed.success) setDraftType(parsed.data);
5508
+ },
5509
+ className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground h-7 rounded-md px-2 text-xs",
5510
+ "aria-label": "Candidate type",
5511
+ children: [
5512
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "episode", children: "episode" }),
5513
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "procedure", children: "procedure" }),
5514
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "preference", children: "preference" }),
5515
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "project-fact", children: "project-fact" })
5516
+ ]
5517
+ }
5518
+ ),
5519
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5520
+ "input",
5521
+ {
5522
+ value: draftTitle,
5523
+ disabled: isUpdating,
5524
+ onChange: (event) => setDraftTitle(event.currentTarget.value),
5525
+ className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground h-7 min-w-[220px] flex-1 rounded-md px-2 text-xs",
5526
+ "aria-label": "Candidate title"
5527
+ }
5528
+ )
5529
+ ] }),
5768
5530
  /* @__PURE__ */ jsxRuntimeExports.jsx(
5769
- AgentTraceSummary,
5531
+ "textarea",
5770
5532
  {
5771
- logs: group.logs,
5772
- scopeId: group.conversationId,
5773
- slowResponseThresholdSeconds,
5774
- showRollupMetrics: false,
5775
- timeDisplayFormat
5533
+ value: draftContent,
5534
+ disabled: isUpdating,
5535
+ onChange: (event) => setDraftContent(event.currentTarget.value),
5536
+ className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground min-h-28 rounded-md px-2 py-1.5 font-mono text-[11px] leading-relaxed",
5537
+ "aria-label": "Candidate content"
5776
5538
  }
5777
5539
  ),
5778
5540
  /* @__PURE__ */ jsxRuntimeExports.jsx(
5779
- TurnGroupList,
5541
+ "input",
5780
5542
  {
5781
- turnGroups,
5782
- viewMode,
5783
- strip,
5784
- slowResponseThresholdSeconds,
5785
- timeDisplayFormat,
5786
- cacheTrends,
5787
- onCompareWithPrevious,
5788
- comparisonPredecessors,
5789
- showLogClientMetadata
5543
+ value: draftTags,
5544
+ disabled: isUpdating,
5545
+ onChange: (event) => setDraftTags(event.currentTarget.value),
5546
+ className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground h-7 rounded-md px-2 text-xs",
5547
+ "aria-label": "Candidate tags"
5548
+ }
5549
+ ),
5550
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
5551
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground", children: [
5552
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "size-3" }),
5553
+ "Saving reruns Inspector redaction before promotion."
5554
+ ] }),
5555
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
5556
+ localError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-destructive", children: localError }),
5557
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5558
+ Button,
5559
+ {
5560
+ type: "button",
5561
+ variant: "ghost",
5562
+ size: "sm",
5563
+ className: "h-7 gap-1.5 px-2 text-xs",
5564
+ disabled: isUpdating,
5565
+ onClick: () => {
5566
+ resetDraft();
5567
+ setEditing(false);
5568
+ },
5569
+ children: [
5570
+ /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3.5" }),
5571
+ "Cancel"
5572
+ ]
5573
+ }
5574
+ ),
5575
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5576
+ Button,
5577
+ {
5578
+ type: "button",
5579
+ variant: "outline",
5580
+ size: "sm",
5581
+ className: "h-7 gap-1.5 px-2 text-xs",
5582
+ disabled: isUpdating,
5583
+ onClick: () => {
5584
+ void saveDraft();
5585
+ },
5586
+ children: [
5587
+ isUpdating ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { className: "size-3.5" }),
5588
+ "Save"
5589
+ ]
5590
+ }
5591
+ )
5592
+ ] })
5593
+ ] }) });
5594
+ }
5595
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border/80 bg-background/60 px-2.5 py-2", children: [
5596
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
5597
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-5 px-1.5 text-[10px] font-mono", children: candidate.type }),
5598
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", title: candidate.title, children: candidate.title }),
5599
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5600
+ Badge,
5601
+ {
5602
+ variant: "outline",
5603
+ className: cn(
5604
+ "h-5 shrink-0 px-1.5 text-[10px] font-mono",
5605
+ CANDIDATE_STATUS_CLASSES[candidate.status]
5606
+ ),
5607
+ children: candidateStatusLabel(candidate.status)
5790
5608
  }
5791
5609
  )
5792
- ] })
5793
- ] });
5794
- });
5795
- function CrabLogo({ className }) {
5796
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
5797
- "svg",
5798
- {
5799
- viewBox: "0 0 24 24",
5800
- fill: "none",
5801
- stroke: "currentColor",
5802
- strokeWidth: "1.5",
5803
- strokeLinecap: "round",
5804
- strokeLinejoin: "round",
5805
- "aria-hidden": "true",
5806
- className: cn("inline-block size-5", className),
5807
- children: [
5808
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z" }),
5809
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
5810
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" }),
5811
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
5812
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" }),
5813
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
5814
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
5815
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
5816
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
5817
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
5818
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
5819
- ]
5820
- }
5821
- );
5822
- }
5823
- function SvgShell({
5824
- className,
5825
- style,
5826
- d,
5827
- eyeStalks,
5828
- eyes,
5829
- legs,
5830
- extras
5831
- }) {
5832
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
5833
- "svg",
5834
- {
5835
- viewBox: "0 0 24 24",
5836
- fill: "none",
5837
- stroke: "currentColor",
5838
- strokeWidth: "1.5",
5839
- strokeLinecap: "round",
5840
- strokeLinejoin: "round",
5841
- "aria-hidden": "true",
5842
- style,
5843
- className: cn("inline-block size-5", className),
5844
- children: [
5845
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d }),
5846
- eyeStalks,
5847
- eyes,
5848
- legs,
5849
- extras
5850
- ]
5851
- }
5852
- );
5853
- }
5854
- const StdLegs = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5855
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
5856
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
5857
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
5858
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
5859
- ] });
5860
- const ShortLegs = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5861
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7", y1: "16", x2: "5.5", y2: "18.5" }),
5862
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "17", x2: "8.5", y2: "19.5" }),
5863
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "17", x2: "15.5", y2: "19.5" }),
5864
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17", y1: "16", x2: "18.5", y2: "18.5" })
5865
- ] });
5866
- function Crab1({ className }) {
5867
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
5868
- SvgShell,
5869
- {
5870
- className,
5871
- d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z",
5872
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5873
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
5874
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
5875
- ] }),
5876
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5877
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
5878
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" })
5610
+ ] }),
5611
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5612
+ "p",
5613
+ {
5614
+ className: "mt-1 text-[11px] leading-relaxed text-muted-foreground",
5615
+ title: candidate.content,
5616
+ children: previewText(candidate.content, 360)
5617
+ }
5618
+ ),
5619
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-muted-foreground", children: [
5620
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [
5621
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FileSearch, { className: "size-3" }),
5622
+ logRangeLabel(candidate.logIds, displayNumberByLogId)
5879
5623
  ] }),
5880
- legs: StdLegs,
5881
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5882
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
5883
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" })
5884
- ] })
5885
- }
5886
- );
5887
- }
5888
- function Crab2({ className }) {
5889
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
5890
- SvgShell,
5891
- {
5892
- className,
5893
- d: "M4 13 C4 7 7.5 5.5 12 5.5 C16.5 5.5 20 7 20 13 C20 17.5 16.5 19 12 19 C7.5 19 4 17.5 4 13 Z",
5894
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5895
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "6.5", x2: "9.5", y2: "4.5" }),
5896
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "6.5", x2: "14.5", y2: "4.5" })
5624
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [
5625
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "size-3" }),
5626
+ redactionLabel(candidate)
5897
5627
  ] }),
5898
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5899
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4", r: "1.1", fill: "currentColor", stroke: "none" }),
5900
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4", r: "1.1", fill: "currentColor", stroke: "none" })
5628
+ candidate.status === "promoted" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-emerald-500", children: [
5629
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "size-3" }),
5630
+ candidate.openClawMemoryId ?? "OpenClaw"
5901
5631
  ] }),
5902
- legs: ShortLegs,
5903
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5904
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C4 9.5 2.5 10 3 12 C3.5 13.5 4.5 13 5 12" }),
5905
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20 9.5 21.5 10 21 12 C20.5 13.5 19.5 13 19 12" })
5632
+ candidate.status === "failed" && candidate.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-destructive", children: [
5633
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3" }),
5634
+ candidate.error
5906
5635
  ] })
5907
- }
5908
- );
5636
+ ] }),
5637
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1.5", children: [
5638
+ candidate.logIds.map((logId) => {
5639
+ const displayNumber = displayLogNumber(logId, displayNumberByLogId);
5640
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
5641
+ "a",
5642
+ {
5643
+ href: `#${getLogAnchor(logId)}`,
5644
+ onClick: (event) => {
5645
+ event.preventDefault();
5646
+ jumpToLog(logId);
5647
+ },
5648
+ className: "rounded border border-blue-400/25 px-1.5 py-0.5 font-mono text-[10px] text-blue-400 underline-offset-2 transition-colors hover:bg-blue-400/10 hover:text-blue-300 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
5649
+ "aria-label": `Jump to evidence log ${displayNumber}`,
5650
+ title: `Log ID ${String(logId)}`,
5651
+ children: [
5652
+ "#",
5653
+ displayNumber
5654
+ ]
5655
+ },
5656
+ logId
5657
+ );
5658
+ }),
5659
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
5660
+ canEdit && /* @__PURE__ */ jsxRuntimeExports.jsxs(
5661
+ Button,
5662
+ {
5663
+ type: "button",
5664
+ variant: "ghost",
5665
+ size: "sm",
5666
+ className: "h-6 gap-1.5 px-2 text-[10px]",
5667
+ disabled: isUpdating || isPromoting,
5668
+ onClick: () => setEditing(true),
5669
+ children: [
5670
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
5671
+ "Review"
5672
+ ]
5673
+ }
5674
+ ),
5675
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5676
+ Button,
5677
+ {
5678
+ type: "button",
5679
+ variant: canPromote ? "outline" : "ghost",
5680
+ size: "sm",
5681
+ className: "h-6 gap-1.5 px-2 text-[10px]",
5682
+ onClick: () => onPromoteCandidate(candidate.id),
5683
+ disabled: !canPromote || isPromoting || isUpdating,
5684
+ children: [
5685
+ isPromoting ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3 animate-spin" }) : canPromote ? /* @__PURE__ */ jsxRuntimeExports.jsx(CloudUpload, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "size-3" }),
5686
+ candidatePromoteLabel(candidate, isPromoting)
5687
+ ]
5688
+ }
5689
+ )
5690
+ ] })
5691
+ ] });
5909
5692
  }
5910
- function Crab3({ className }) {
5911
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
5912
- SvgShell,
5693
+ function CandidateList({
5694
+ candidates,
5695
+ promotingCandidateIds,
5696
+ updatingCandidateIds,
5697
+ onPromoteCandidate,
5698
+ onUpdateCandidate,
5699
+ displayNumberByLogId
5700
+ }) {
5701
+ if (candidates.length === 0) return null;
5702
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1.5", children: candidates.map((candidate) => /* @__PURE__ */ jsxRuntimeExports.jsx(
5703
+ CandidateItem,
5913
5704
  {
5914
- className,
5915
- d: "M3 13.5 C3 8 7 6 12 6 C17 6 21 8 21 13.5 C21 17 17 19 12 19 C7 19 3 17 3 13.5 Z",
5916
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5917
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7", y1: "7", x2: "6.5", y2: "4.5" }),
5918
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17", y1: "7", x2: "17.5", y2: "4.5" })
5919
- ] }),
5920
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5921
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "6.5", cy: "4", r: "0.8", fill: "currentColor", stroke: "none" }),
5922
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "17.5", cy: "4", r: "0.8", fill: "currentColor", stroke: "none" })
5923
- ] }),
5924
- legs: StdLegs,
5925
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5926
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M4 12 C2.5 10.5 1 11 1.5 13 C2 14.5 3.5 14 4.5 13" }),
5927
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M20 12 C21.5 10.5 23 11 22.5 13 C22 14.5 20.5 14 19.5 13" })
5928
- ] })
5929
- }
5705
+ candidate,
5706
+ isPromoting: promotingCandidateIds.has(candidate.id),
5707
+ isUpdating: updatingCandidateIds.has(candidate.id),
5708
+ onPromoteCandidate,
5709
+ onUpdateCandidate,
5710
+ displayNumberByLogId
5711
+ },
5712
+ candidate.id
5713
+ )) });
5714
+ }
5715
+ function AgentTraceSummary({
5716
+ logs,
5717
+ scopeId,
5718
+ slowResponseThresholdSeconds,
5719
+ showRollupMetrics,
5720
+ timeDisplayFormat
5721
+ }) {
5722
+ const [candidates, setCandidates] = reactExports.useState([]);
5723
+ const [candidatesExpanded, setCandidatesExpanded] = reactExports.useState(true);
5724
+ const [candidateState, setCandidateState] = reactExports.useState({
5725
+ status: "idle",
5726
+ error: null
5727
+ });
5728
+ const [promotingCandidateIds, setPromotingCandidateIds] = reactExports.useState(
5729
+ () => /* @__PURE__ */ new Set()
5730
+ );
5731
+ const [updatingCandidateIds, setUpdatingCandidateIds] = reactExports.useState(
5732
+ () => /* @__PURE__ */ new Set()
5733
+ );
5734
+ const hasCandidates = candidates.length > 0;
5735
+ const summary = reactExports.useMemo(
5736
+ () => buildTraceSummary(logs, slowResponseThresholdSeconds, candidates.length),
5737
+ [candidates.length, logs, slowResponseThresholdSeconds]
5738
+ );
5739
+ const displayNumberByLogId = reactExports.useMemo(() => buildDisplayNumberByLogId$1(logs), [logs]);
5740
+ const traceInsights = reactExports.useMemo(
5741
+ () => buildTraceInsights({ logs, scopeId, summary, candidates, displayNumberByLogId }),
5742
+ [candidates, displayNumberByLogId, logs, scopeId, summary]
5743
+ );
5744
+ const showElapsedSummary = showRollupMetrics || summary.maxElapsedMs !== null;
5745
+ const timeRange = reactExports.useMemo(
5746
+ () => formatTimeRange$1(summary.startedAt, summary.endedAt, timeDisplayFormat),
5747
+ [summary.endedAt, summary.startedAt, timeDisplayFormat]
5748
+ );
5749
+ const createCandidates = reactExports.useCallback(() => {
5750
+ if (logs.length === 0 || candidateState.status === "loading") return;
5751
+ setCandidateState({ status: "loading", error: null });
5752
+ void (async () => {
5753
+ try {
5754
+ const response = await fetch(
5755
+ `/api/knowledge/sessions/${encodeURIComponent(scopeId)}/candidates`,
5756
+ { method: "POST" }
5757
+ );
5758
+ if (!response.ok) {
5759
+ const message = await readApiError(
5760
+ response,
5761
+ `Candidate generation failed with ${String(response.status)}`
5762
+ );
5763
+ setCandidateState({ status: "failed", error: message });
5764
+ return;
5765
+ }
5766
+ const parsed = await parseJsonResponse$1(response, CandidateResponseSchema);
5767
+ setCandidates(parsed.candidates);
5768
+ setCandidatesExpanded(parsed.candidates.length > 0);
5769
+ setCandidateState(
5770
+ parsed.candidates.length > 0 ? { status: "ready", error: null } : { status: "failed", error: "No candidate was generated for this trace scope." }
5771
+ );
5772
+ } catch (error) {
5773
+ setCandidateState({
5774
+ status: "failed",
5775
+ error: error instanceof Error ? error.message : "Candidate response was invalid"
5776
+ });
5777
+ }
5778
+ })();
5779
+ }, [candidateState.status, logs.length, scopeId]);
5780
+ const promoteCandidate = reactExports.useCallback((candidateId) => {
5781
+ setPromotingCandidateIds((current) => {
5782
+ if (current.has(candidateId)) return current;
5783
+ const next = new Set(current);
5784
+ next.add(candidateId);
5785
+ return next;
5786
+ });
5787
+ void (async () => {
5788
+ try {
5789
+ const response = await fetch(
5790
+ `/api/knowledge/candidates/${encodeURIComponent(candidateId)}/promote`,
5791
+ { method: "POST" }
5792
+ );
5793
+ const raw = await response.json().catch(() => null);
5794
+ const parsed = CandidatePromotionResponseSchema.safeParse(raw);
5795
+ if (response.ok && parsed.success) {
5796
+ setCandidates(
5797
+ (current) => current.map(
5798
+ (candidate) => candidate.id === parsed.data.candidate.id ? parsed.data.candidate : candidate
5799
+ )
5800
+ );
5801
+ setCandidateState({ status: "ready", error: null });
5802
+ return;
5803
+ }
5804
+ const failure = CandidatePromotionFailureResponseSchema.safeParse(raw);
5805
+ if (failure.success && failure.data.candidate !== void 0) {
5806
+ setCandidates(
5807
+ (current) => current.map(
5808
+ (candidate) => candidate.id === failure.data.candidate?.id ? failure.data.candidate : candidate
5809
+ )
5810
+ );
5811
+ }
5812
+ setCandidateState({
5813
+ status: "failed",
5814
+ error: failure.success ? failure.data.error ?? "Candidate promotion failed" : `Candidate promotion failed with ${String(response.status)}`
5815
+ });
5816
+ } catch (error) {
5817
+ setCandidateState({
5818
+ status: "failed",
5819
+ error: error instanceof Error ? error.message : "Candidate promotion failed"
5820
+ });
5821
+ } finally {
5822
+ setPromotingCandidateIds((current) => {
5823
+ const next = new Set(current);
5824
+ next.delete(candidateId);
5825
+ return next;
5826
+ });
5827
+ }
5828
+ })();
5829
+ }, []);
5830
+ const updateCandidate = reactExports.useCallback(
5831
+ async (candidateId, update) => {
5832
+ setUpdatingCandidateIds((current) => {
5833
+ if (current.has(candidateId)) return current;
5834
+ const next = new Set(current);
5835
+ next.add(candidateId);
5836
+ return next;
5837
+ });
5838
+ try {
5839
+ const response = await fetch(
5840
+ `/api/knowledge/candidates/${encodeURIComponent(candidateId)}`,
5841
+ {
5842
+ method: "PATCH",
5843
+ headers: { "Content-Type": "application/json" },
5844
+ body: JSON.stringify(update)
5845
+ }
5846
+ );
5847
+ if (!response.ok) {
5848
+ const message = await readApiError(
5849
+ response,
5850
+ `Candidate update failed with ${String(response.status)}`
5851
+ );
5852
+ setCandidateState({ status: "failed", error: message });
5853
+ return false;
5854
+ }
5855
+ const parsed = await parseJsonResponse$1(response, CandidateUpdateResponseSchema);
5856
+ setCandidates(
5857
+ (current) => current.map(
5858
+ (candidate) => candidate.id === parsed.candidate.id ? parsed.candidate : candidate
5859
+ )
5860
+ );
5861
+ setCandidateState({ status: "ready", error: null });
5862
+ return true;
5863
+ } catch (error) {
5864
+ setCandidateState({
5865
+ status: "failed",
5866
+ error: error instanceof Error ? error.message : "Candidate update failed"
5867
+ });
5868
+ return false;
5869
+ } finally {
5870
+ setUpdatingCandidateIds((current) => {
5871
+ const next = new Set(current);
5872
+ next.delete(candidateId);
5873
+ return next;
5874
+ });
5875
+ }
5876
+ },
5877
+ []
5930
5878
  );
5931
- }
5932
- function Crab4({ className }) {
5933
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
5934
- SvgShell,
5935
- {
5936
- className,
5937
- d: "M6 14 C6 8 9 5.5 12 5.5 C15 5.5 18 8 18 14 C18 18 15 20 12 20 C9 20 6 18 6 14 Z",
5938
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5939
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "6.5", x2: "10", y2: "3.5" }),
5940
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "6.5", x2: "14", y2: "3.5" })
5941
- ] }),
5942
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5943
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "10", cy: "3", r: "0.7", fill: "currentColor", stroke: "none" }),
5944
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14", cy: "3", r: "0.7", fill: "currentColor", stroke: "none" })
5879
+ if (logs.length === 0) return null;
5880
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "border border-border bg-card shadow-sm mb-2 rounded-[8px] px-3 py-2", children: [
5881
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs", children: [
5882
+ showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 font-semibold text-foreground", children: [
5883
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3.5 text-blue-400" }),
5884
+ summary.llmCallCount,
5885
+ " LLM"
5945
5886
  ] }),
5946
- legs: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5947
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7", y1: "17", x2: "5", y2: "20.5" }),
5948
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "18.5", x2: "8.5", y2: "21.5" }),
5949
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "18.5", x2: "15.5", y2: "21.5" }),
5950
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17", y1: "17", x2: "19", y2: "20.5" })
5887
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5888
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-sky-400/70" }),
5889
+ summary.toolCallCount,
5890
+ " tools"
5951
5891
  ] }),
5952
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5953
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M6.5 11 C5 8.5 3 9 3.5 11.5 C4 13.5 5 13 6 12" }),
5954
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M17.5 11 C19 8.5 21 9 20.5 11.5 C20 13.5 19 13 18 12" })
5955
- ] })
5956
- }
5957
- );
5958
- }
5959
- function Crab5({ className }) {
5960
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
5961
- SvgShell,
5962
- {
5963
- className,
5964
- d: "M5 13 C5 9 8 7 12 7 L13 4 L14 7 C16 7 19 9 19 13 C19 16 16 18 13 18 L12 21 L11 18 C8 18 5 16 5 13 Z",
5965
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5966
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
5967
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
5892
+ showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5893
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-emerald-400" }),
5894
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
5895
+ formatTokens(summary.totalInputTokens),
5896
+ " / ",
5897
+ formatTokens(summary.totalOutputTokens)
5898
+ ] })
5968
5899
  ] }),
5969
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5970
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.8", fill: "currentColor", stroke: "none" }),
5971
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.8", fill: "currentColor", stroke: "none" })
5900
+ (summary.totalCacheCreationInputTokens > 0 || summary.totalCacheReadInputTokens > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5901
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-purple-400" }),
5902
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
5903
+ "KV Cache +",
5904
+ formatTokens(summary.totalCacheCreationInputTokens),
5905
+ " / ~",
5906
+ formatTokens(summary.totalCacheReadInputTokens)
5907
+ ] })
5972
5908
  ] }),
5973
- legs: StdLegs,
5974
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5975
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
5976
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" })
5977
- ] })
5978
- }
5979
- );
5980
- }
5981
- function Crab6({ className }) {
5982
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
5983
- SvgShell,
5984
- {
5985
- className,
5986
- d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z",
5987
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5988
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
5989
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
5909
+ showElapsedSummary && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5910
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3.5" }),
5911
+ showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: formatElapsed$1(summary.totalElapsedMs) }),
5912
+ summary.maxElapsedMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono text-muted-foreground/70", children: [
5913
+ "max ",
5914
+ formatElapsed$1(summary.maxElapsedMs)
5915
+ ] })
5990
5916
  ] }),
5991
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5992
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
5993
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "13.5", y1: "4", x2: "15.5", y2: "5" })
5917
+ showRollupMetrics && timeRange !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-muted-foreground/70", children: timeRange }),
5918
+ (summary.failedCallCount > 0 || summary.pendingCallCount > 0 || summary.slowCallCount > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-amber-400", children: [
5919
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3.5" }),
5920
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
5921
+ summary.failedCallCount,
5922
+ " fail / ",
5923
+ summary.pendingCallCount,
5924
+ " pending /",
5925
+ " ",
5926
+ summary.slowCallCount,
5927
+ " slow"
5928
+ ] })
5994
5929
  ] }),
5995
- legs: StdLegs,
5996
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
5997
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
5998
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" })
5999
- ] })
6000
- }
6001
- );
5930
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
5931
+ hasCandidates && /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-6 px-2 text-[10px] font-mono", children: formatCandidateCount(summary.knowledgeCandidateCount) }),
5932
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5933
+ Button,
5934
+ {
5935
+ type: "button",
5936
+ variant: "outline",
5937
+ size: "sm",
5938
+ className: "h-7 gap-1.5 px-2 text-xs",
5939
+ onClick: createCandidates,
5940
+ disabled: candidateState.status === "loading",
5941
+ children: [
5942
+ candidateState.status === "loading" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5" }),
5943
+ "Candidate"
5944
+ ]
5945
+ }
5946
+ ),
5947
+ hasCandidates && /* @__PURE__ */ jsxRuntimeExports.jsx(
5948
+ Button,
5949
+ {
5950
+ type: "button",
5951
+ variant: "ghost",
5952
+ size: "icon",
5953
+ className: "size-7 text-muted-foreground",
5954
+ onClick: () => setCandidatesExpanded((value) => !value),
5955
+ "aria-expanded": candidatesExpanded,
5956
+ "aria-label": candidatesExpanded ? "Collapse memory candidates" : "Expand memory candidates",
5957
+ children: candidatesExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3.5" })
5958
+ }
5959
+ )
5960
+ ] }),
5961
+ candidateState.status === "failed" && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-xs text-destructive", children: candidateState.error }),
5962
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TraceInsights, { insights: traceInsights }),
5963
+ candidatesExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx(
5964
+ CandidateList,
5965
+ {
5966
+ candidates,
5967
+ promotingCandidateIds,
5968
+ updatingCandidateIds,
5969
+ onPromoteCandidate: promoteCandidate,
5970
+ onUpdateCandidate: updateCandidate,
5971
+ displayNumberByLogId
5972
+ }
5973
+ )
5974
+ ] });
6002
5975
  }
6003
- function Crab7({ className }) {
6004
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
6005
- SvgShell,
6006
- {
6007
- className,
6008
- d: "M5.5 13.5 C5.5 10 8.5 8 12 8 C15.5 8 18.5 10 18.5 13.5 C18.5 16 15.5 17.5 12 17.5 C8.5 17.5 5.5 16 5.5 13.5 Z",
6009
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6010
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "8", x2: "10", y2: "6" }),
6011
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "8", x2: "14", y2: "6" })
6012
- ] }),
6013
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6014
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "6", x2: "11", y2: "6.5" }),
6015
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "13", y1: "6", x2: "15", y2: "6.5" })
6016
- ] }),
6017
- legs: ShortLegs,
6018
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6019
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M6 11.5 C4.5 10.5 3 11 3.5 12.5 C4 13.5 5 13 5.5 12.5" }),
6020
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M18 11.5 C19.5 10.5 21 11 20.5 12.5 C20 13.5 19 13 18.5 12.5" })
6021
- ] })
6022
- }
6023
- );
5976
+ function computeStats(logs) {
5977
+ let totalInput = 0;
5978
+ let totalOutput = 0;
5979
+ for (const log of logs) {
5980
+ if (log.inputTokens !== null) totalInput += log.inputTokens;
5981
+ if (log.outputTokens !== null) totalOutput += log.outputTokens;
5982
+ }
5983
+ return { totalInputTokens: totalInput, totalOutputTokens: totalOutput };
6024
5984
  }
6025
- function Crab8({ className }) {
6026
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
6027
- SvgShell,
6028
- {
6029
- className,
6030
- d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z",
6031
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6032
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
6033
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
6034
- ] }),
6035
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6036
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.7", fill: "currentColor", stroke: "none" }),
6037
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.7", fill: "currentColor", stroke: "none" })
6038
- ] }),
6039
- legs: StdLegs,
6040
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6041
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
6042
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" }),
6043
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "8.5", y1: "4", x2: "10.5", y2: "4.5" }),
6044
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15.5", y1: "4", x2: "13.5", y2: "4.5" }),
6045
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9 15.5 C10 14.5 14 14.5 15 15.5" })
6046
- ] })
5985
+ function collectProviders(logs) {
5986
+ const buckets = /* @__PURE__ */ new Map();
5987
+ for (const log of logs) {
5988
+ if (log.model === null) continue;
5989
+ const provider = detectProvider(log.model);
5990
+ if (provider === "unknown") continue;
5991
+ let models = buckets.get(provider);
5992
+ if (models === void 0) {
5993
+ models = /* @__PURE__ */ new Set();
5994
+ buckets.set(provider, models);
6047
5995
  }
6048
- );
5996
+ models.add(log.model);
5997
+ }
5998
+ return [...buckets.entries()].map(([provider, models]) => ({
5999
+ provider,
6000
+ models: [...models]
6001
+ }));
6049
6002
  }
6050
- function Crab9({ className }) {
6051
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
6052
- SvgShell,
6053
- {
6054
- className,
6055
- d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z",
6056
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6057
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
6058
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
6059
- ] }),
6060
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6061
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "1.2", fill: "none", stroke: "currentColor" }),
6062
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "1.2", fill: "none", stroke: "currentColor" })
6063
- ] }),
6064
- legs: StdLegs,
6065
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6066
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
6067
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" }),
6068
- /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "12", cy: "15", rx: "2", ry: "1.5", fill: "currentColor", stroke: "none" })
6069
- ] })
6070
- }
6071
- );
6003
+ function getFirstClientPid(logs) {
6004
+ for (const log of logs) {
6005
+ const pid = log.clientPid;
6006
+ if (pid !== null && pid !== void 0) return pid;
6007
+ }
6008
+ return null;
6072
6009
  }
6073
- function Crab10({ className }) {
6074
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
6075
- SvgShell,
6076
- {
6077
- className,
6078
- d: "M4.5 13 C4.5 8 7.5 6 12 6 C16.5 6 19.5 8 19.5 13 C19.5 17 16.5 18.5 12 18.5 C7.5 18.5 4.5 17 4.5 13 Z",
6079
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6080
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "7", x2: "9", y2: "5" }),
6081
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "7", x2: "15", y2: "5" })
6082
- ] }),
6083
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6084
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9", cy: "4.5", r: "1", fill: "currentColor", stroke: "none" }),
6085
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "15", cy: "4.5", r: "1", fill: "currentColor", stroke: "none" })
6086
- ] }),
6087
- legs: StdLegs,
6088
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6089
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9 2 9.5 2.5 12 C3 14 4 13 5 12" }),
6090
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9 22 9.5 21.5 12 C21 14 20 13 19 12" }),
6091
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9 14.5 C10 16 14 16 15 14.5" }),
6092
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "7", cy: "13", r: "1", fill: "currentColor", stroke: "none", opacity: "0.3" }),
6093
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "17", cy: "13", r: "1", fill: "currentColor", stroke: "none", opacity: "0.3" })
6094
- ] })
6010
+ function getFirstClientProjectFolder(logs) {
6011
+ for (const log of logs) {
6012
+ const projectFolder = log.clientProjectFolder;
6013
+ if (projectFolder !== null && projectFolder !== void 0 && projectFolder !== "") {
6014
+ return projectFolder;
6095
6015
  }
6096
- );
6016
+ }
6017
+ return null;
6097
6018
  }
6098
- function Crab11({ className }) {
6099
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
6100
- SvgShell,
6019
+ const ConversationGroup = reactExports.memo(function({
6020
+ group,
6021
+ viewMode = "simple",
6022
+ strip,
6023
+ slowResponseThresholdSeconds,
6024
+ cacheTrends,
6025
+ onCompareWithPrevious,
6026
+ comparisonPredecessors,
6027
+ onClearGroup,
6028
+ showClientIdentity = true,
6029
+ showProcessMetadata = true,
6030
+ showLogClientMetadata = true,
6031
+ defaultExpanded = false,
6032
+ timeDisplayFormat,
6033
+ expanded: controlledExpanded,
6034
+ onExpandedChange
6035
+ }) {
6036
+ const [internalExpanded, setInternalExpanded] = reactExports.useState(defaultExpanded);
6037
+ const expanded = controlledExpanded ?? internalExpanded;
6038
+ const setExpanded = reactExports.useCallback(
6039
+ (nextExpanded) => {
6040
+ if (onExpandedChange !== void 0) {
6041
+ onExpandedChange(nextExpanded);
6042
+ return;
6043
+ }
6044
+ setInternalExpanded(nextExpanded);
6045
+ },
6046
+ [onExpandedChange]
6047
+ );
6048
+ reactExports.useEffect(() => {
6049
+ if (!defaultExpanded || controlledExpanded !== void 0) return;
6050
+ setInternalExpanded(true);
6051
+ }, [controlledExpanded, defaultExpanded]);
6052
+ const stats = reactExports.useMemo(() => computeStats(group.logs), [group.logs]);
6053
+ const providers = reactExports.useMemo(() => collectProviders(group.logs), [group.logs]);
6054
+ const startTime = group.logs[0]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
6055
+ const endTime = group.logs[group.logs.length - 1]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
6056
+ const isLoading = group.logs.some((log) => log.responseStatus === null);
6057
+ const clientPid = reactExports.useMemo(() => getFirstClientPid(group.logs), [group.logs]);
6058
+ const clientProjectFolder = reactExports.useMemo(() => getFirstClientProjectFolder(group.logs), [group.logs]);
6059
+ const turnGroups = reactExports.useMemo(() => buildTurnGroups(group.logs), [group.logs]);
6060
+ reactExports.useEffect(() => {
6061
+ const handleLogFocusRequest = (event) => {
6062
+ const request = readLogFocusRequest(event);
6063
+ if (request === null) return;
6064
+ if (!group.logs.some((log) => log.id === request.logId)) return;
6065
+ setExpanded(true);
6066
+ };
6067
+ window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
6068
+ return () => {
6069
+ window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
6070
+ };
6071
+ }, [group.logs, setExpanded]);
6072
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2", children: [
6073
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6074
+ ConversationHeader,
6075
+ {
6076
+ conversationId: group.conversationId,
6077
+ startTime,
6078
+ endTime,
6079
+ totalCalls: group.logs.length,
6080
+ totalInputTokens: stats.totalInputTokens,
6081
+ totalOutputTokens: stats.totalOutputTokens,
6082
+ expanded,
6083
+ onToggle: () => setExpanded(!expanded),
6084
+ providers,
6085
+ isLoading,
6086
+ userAgent: group.logs[0]?.userAgent ?? null,
6087
+ clientPid,
6088
+ clientProjectFolder,
6089
+ showClientIdentity,
6090
+ showProcessMetadata,
6091
+ timeDisplayFormat,
6092
+ onClear: () => onClearGroup(group.logs.map((l) => l.id))
6093
+ }
6094
+ ),
6095
+ expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
6096
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6097
+ AgentTraceSummary,
6098
+ {
6099
+ logs: group.logs,
6100
+ scopeId: group.conversationId,
6101
+ slowResponseThresholdSeconds,
6102
+ showRollupMetrics: false,
6103
+ timeDisplayFormat
6104
+ }
6105
+ ),
6106
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6107
+ TurnGroupList,
6108
+ {
6109
+ turnGroups,
6110
+ viewMode,
6111
+ strip,
6112
+ slowResponseThresholdSeconds,
6113
+ timeDisplayFormat,
6114
+ cacheTrends,
6115
+ onCompareWithPrevious,
6116
+ comparisonPredecessors,
6117
+ showLogClientMetadata
6118
+ }
6119
+ )
6120
+ ] })
6121
+ ] });
6122
+ });
6123
+ function CrabLogo({ className }) {
6124
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
6125
+ "svg",
6101
6126
  {
6102
- className,
6103
- d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z",
6104
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6105
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5.5" }),
6106
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5.5" })
6107
- ] }),
6108
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6109
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7.5", y1: "5.5", x2: "16.5", y2: "5.5" }),
6110
- /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "7", y: "5", width: "4", height: "2", rx: "0.5", fill: "currentColor", stroke: "none" }),
6111
- /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "13", y: "5", width: "4", height: "2", rx: "0.5", fill: "currentColor", stroke: "none" })
6112
- ] }),
6113
- legs: StdLegs,
6114
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6127
+ viewBox: "0 0 24 24",
6128
+ fill: "none",
6129
+ stroke: "currentColor",
6130
+ strokeWidth: "1.5",
6131
+ strokeLinecap: "round",
6132
+ strokeLinejoin: "round",
6133
+ "aria-hidden": "true",
6134
+ className: cn("inline-block size-5", className),
6135
+ children: [
6136
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z" }),
6115
6137
  /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
6116
6138
  /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" }),
6117
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9.5 15 C10.5 16 13.5 16 14.5 15" })
6118
- ] })
6119
- }
6120
- );
6121
- }
6122
- function Crab12({ className }) {
6123
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
6124
- SvgShell,
6125
- {
6126
- className,
6127
- d: "M7 13.5 C7 10.5 9.5 9 12 9 C14.5 9 17 10.5 17 13.5 C17 16 14.5 17.5 12 17.5 C9.5 17.5 7 16 7 13.5 Z",
6128
- eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6129
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10.5", y1: "9.5", x2: "10.5", y2: "8" }),
6130
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "13.5", y1: "9.5", x2: "13.5", y2: "8" })
6131
- ] }),
6132
- eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6133
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "10.5", cy: "7.5", r: "0.8", fill: "currentColor", stroke: "none" }),
6134
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "13.5", cy: "7.5", r: "0.8", fill: "currentColor", stroke: "none" })
6135
- ] }),
6136
- legs: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6137
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "8", y1: "16", x2: "6.5", y2: "18" }),
6138
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "17", x2: "9.5", y2: "19" }),
6139
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "17", x2: "14.5", y2: "19" }),
6140
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "16", y1: "16", x2: "17.5", y2: "18" })
6141
- ] }),
6142
- extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6143
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M7.5 12 C6.5 11 5.5 11.5 6 13 C6.5 14 7 13.5 7.5 13" }),
6144
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M16.5 12 C17.5 11 18.5 11.5 18 13 C17.5 14 17 13.5 16.5 13" })
6145
- ] })
6139
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
6140
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" }),
6141
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
6142
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
6143
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
6144
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
6145
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
6146
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
6147
+ ]
6146
6148
  }
6147
6149
  );
6148
6150
  }
6149
- const crabVariants = [
6150
- Crab1,
6151
- Crab2,
6152
- Crab3,
6153
- Crab4,
6154
- Crab5,
6155
- Crab6,
6156
- Crab7,
6157
- Crab8,
6158
- Crab9,
6159
- Crab10,
6160
- Crab11,
6161
- Crab12
6162
- ];
6163
6151
  const McpLogoPng = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAQAAAAi5ZK2AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfpCxsJDCnsge2KAAAM2ElEQVR42u2de3AW1RnGnyQQDARDwiUFpHIVMHIpYgFRB2nLxQrC0FIDiCKDWNqBUqBlai8wvciIpaRTWh3QDqCDIIjKDL0IaBC5SKlysWJArgqh3BMICYFs/wgpGUi+8579dr89e/Z5zp/59j2755fn3d2z5wJQFEVRFEVRoVQSm+AG1UFrtEdT1EcjNEA5SlCMIhxDAQoJ3S4lIwf9cD+6ozXq1vKbYuzHNmxEPo6xwcKtungIS3EajkYpwLPoyqYLp3KwACe1cFcvezATmWzEMOk+rMBV18CrSjHy0IqNGQb1wMa4cV8vZfgzGrNRTVYm8nDFQ+SV5QymIIWNa6ZGaz6y6ZR/I4cNbJrSkOcb8MpyCVPYzCapKz71GXllWY5b2dhm6EGcTwhyBw52oyUbPHgNx6WEIXfg4CDuYKMHq0kevI/rlhP4Ghs+OE1ERcKRO3BwEh3Z+NFC7sDBATQngMRrcoDIHTj4iE/yUXJ5VVlhYsMkWezy+a6urgRbsAP7cRincQGpqI+maI+O6IMuSHYRbwIW0X/mJvZTeAH9ah1CATTCaKxBmWbUi7iTOMxEXoCncIsodgvM1ezo2cFPMebdy89qfyHL0vxSN5FQzHL5ajRxVU8v7NO4cWQRjCnIy/DDOOq6FcvENS0gGjMS+wUMjvvdZ5awrsv4KvEE7/Iz+Lontc4Q1vc8AQXt8rO4x7Oa54hqLOLI2aBdfrenHVyviWqdQUx2uLxS6fhMUO8ugrLD5VXqLfpmfxdh2eHyKi0S1P5b4rLF5ZVqiiJl/Z8SmD0ur9TzgnNoQWi2uLxSt+Gy8ixGEpvZLs9AN3RGqsYRa5Xn8SeCM9XlDfFT7Ll2bDnWYYRwOMZjyjP5kOjMdPkDOHpTjPX4iuDILOWL2znCMxH5IyitMc4+ZAuO3q08nyYEaFpiHxbjYWyDIMm/qDyje4kwPMgdOBihjDBNeU7fJcQwIXewTnBzUJ3Vk8Rozr18mOAtuxT1FFH6KmNMJkhTkH+7lse3G0s7RZyuygjPEGVYEntV6aSIlKOM8CvCDEtirywVyFDE6q2M8WPiDJPLJcMgBiljPEWg4XG5AwfTlfEmKWPkEml4XO7gCBooI85TRvlm8I1ax3CX68w8PYeB2KGFfEWM6Yo3qgy5uKj8VU/lLz5HG2QjHRlIA1CCIhTjBI6ggg43LbGXYYggZj3lgkYVtbwaluITvIlf4gHhNEomdheJXfpeXoX8EVHUAR4sY1CKfEyN5gibMCIHXvJsBYur2IDxSCNy05HXx1mPFy/5L2ZFZb6rGR2uusiByb6sWlOMZ5FOl5vocqAuDvm2XNExjLV5t52wuhyY7vM6Ve+jA11uksuB5glYcLgIY+hyc1yeJBj87E35q6BXkC5PAHJgdgKXI9xlzzt8eF0OjE/wypQH7Li7h9nl4wJYjPQ4utPlwSF/LKD1Z8+gC10eLeQOHHyJ1nR54pGPCniVadmcGyK3xOVVZYvWnNpqb5hBIv+L1hCJAdiuhXyVcpT6dV3GSLylhXyxy7YrxHYU4CBO4yKABmiCNuiIe9DMVbT5mMp7eWJc/rgLl1dgC6bE2NelM6Zim4uoDzOxm5nYS/GicNOuznhJc0X5k2HprIkS8gos1cTSSrgYodFbhkQ5sR/F/a7aqD8Oa9TSny43x+UblDNialcmVovr2WP2qOYoIV8V57tREuaK6/o+E7sJid2bO+00Ya0HTPV6lFy+3LNWmymsMZfIg+1w9fZ5ep6ozp3mjaKjy90rGetF9d5H5Ha4vFLZOB62zYGIPH6NEk2NqEvk4U/s1V/f8gX1DyJye5ADQC/BGfyRyO1I7Nf1rvIcPiZye1xeqaGCua6ZNiMf7sOSAolD3h+v4otrH2RewYPCo1JxWnkmQ4jcROTZNcyGWSMcN7PQ5KUJw4zc33t5G+xHzfu3S7655wqmPRG5ccgPxxjkqN7DvbnyfDYRuVmJvY1iYMQEQYxCxRkVEnlYXF5ZdguibFLEKI/6S9pQg1x+Ow4IYrZSxlmqjJFGl4fF5dLVJBcoYzQl8jDcy3XestV7tbcl8vC43IGD3spo6gEVtxF5mJCXCu7H6u6ZRlFE/nAoE7sDB8sEEd9URqkTfuT2P7FXlSuiZQb2KKKU0OXhcbmDGYKYdZWrTO8l8nDcyx04mCeKqh5IsYaJPRyJ3cEfhHFnePTPQ+ShQQ68r4z1NJEHkdj9Q95KcN7didwmlwOzBC2dTOT2uByoj5PBPcbR5UEgly0tPo3I7XE5kI1zgoVOWhO5PS4HXhHE/IDI7XE5MFIU9QdEbo/LO6NIEPWS98Mn/F04pD+R16oWOCiK+4LXyMf4irwnin1ErrtWzGtGIW+MXcJvdB4v/H87LvqIvL1yWG90XZ6J7UHNpVvoI/KGKPDxS5q/Lm/ry5e06i7/SBi5HHd5izxVI/nqIpcM6aXL1WW+1z7v6SPyJ0I7RMJvl+sgP+H9uLihviFvp5FDmNhrL096/34+2Jf3cgBYw8TuAfI3/OiHu8MXl+tsMM97ee3lKBr7AT1JuVuwG+Sp2EvkcSMvR1+/PqbO9Bw5MIn38rgTu69rP6fFeJd2hzwFn/OJPU6XO5gNX5VTy6iNk66QA4+KLqoCY5nYay0L4bs6YedN1e5EJ5fRdogu60dM7DE+/SZk+lIqxmMzrl5br2wzxrvbCA5Ad9FlraTLay2vJ3YV2DS0Rds4VzuYI7isQ1q9TNG6l79uzsK/8ldAyffhYRoRwz0qJgLIgT6CC/uHMcjpck/0c8Gl9aHLbUIO/FN5aflEbhfyOoJBfrlEbhNyydf5C0jnvdwm5MAY5eWtInK7kAOzlRc4mYndLuTAq8pL7KGMMTzUXTGGdrj6qc2KS7yq7O27E1fYxx4ufaK4yCOK41M0ofg7dYHIRVIh2xr3gyBdbpxUW8+8ozh+PV0ePqkmSK1RJPdLhrj890Qu1ynFxa6PeXRTQ76X871cS6rPqh/GPDrLiJc0ulxTqgVtj8U8OglneS8Pn9RrHjaMefwbvJeHT4vj/JY+iMjDJ/UQip8oIqyNcewyJnYT9T3lpf9NEaEJPqvlyEV0uZnqKpjVkqWIkV3DTuKlmusfE3kClYwzygaYIIiSi03XxuA7OIF5aE7kJuttZRPsQpIoUga64W601l75mF0xCZdkYdvBPtZP5AGom6Ahtvu2brnfiZ3IXfbKye7rdHmo9AtBc5zyYftIujxAtRONcduIFLrcJm3wpdmDdfm/iDy2BgqbZnpoXE7kSiUJOy8qMNWD2nJwlC43QY+Km+i5OF/fHhD0ARJ5grpjt4qbaT1auM4oU1DGxG6Oev2/71xdCpEr7Jqtro5YpwWELk+AXtZqsHz01ojdDHM1PU7kCVEznNDEko8RuEXQ0bsAJZqRiTxhGqQ5FdGBg3NYgsfRpoZoDTEIv8N/tCNGDnlSwPU/hxkujyzCPhzBOZQiBY3QFB3i6Lidp7lhZSbe0VorcyVGoZwer1JdbHHlTC8LE3vC1US8CLg/xf+XtDqEfLPa4BiRR0/dBXsGhzOxE3kMdcGXCUc+h8iDT/IFCQR+BROJ3ARlaw5DcF8uYAiRm6J6yHPRXaNb9mhvUknkPmuocuGC+MoS1Cdy89QSK3wCvg8PaZ8NkSdM/YT7hsvLRcwSfKwh8kBVB08LN/JSlxIscNUzT+QBKBlDsC1O4OeR53LkDZEHqN7Ic9VRW4a3kav92EbkxigF38BcbEO5CMBhLME45Tz3yCJPChn8dPRBF7RHB3RARrVtvC6hGIewHwUowBYcirMW/e/lubhCZybunyALmR7/6zKxR05ETuRETuRETuRsNCKniJwicorIKSKniJwicorIKSKniJyqGfnHmvsyE3nIlRJzHxi63ErlEXnUNI6JPWrKUO7rTJdbp98QedSUjWIm9qjpGSKPnrYysUdNzYRLEBO5RRrLxF6p5AhB7yz4zUqMtn+CUpSgZyt/sZpz0mzTWuXU5sbRaIgoOT1T8fdCnCZ026RC2lJzQ25CD4EKFX9PwWLUI/RoQQe+hbdcrEBFGazRovf0tdFwe1SUJVyx5u90u03aKOx7J3aLNEP8lY1J3ho10hg3Q7dbo59pjJwhdkvUAMeJPXr6jtY2Ary3W6Jfaw2CptutUJLmJgLEboXSsUkLO5O8JQ9079LtxE7sxE7sxE7sFqkhH+mIXV1WhG43DMoD7OPZZNHD/gWnPUUR+4BwX2wyeQMAijEYH4h/fS+hRw97c0K3B/tAvCf6ZRmh26OLGCpy+wE2VfQe6XLYTFHD/h6bKGrYy9CDDWQr9vwakV/FE2wce5WGl29CfgrD2TC2qy+W4/w14Hsxy5aVKvjFSN1CLVAPp1DEpqAoiqIoyiz9D9lYMumhgrvLAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI1LTExLTI3VDA5OjEyOjQxKzAwOjAwvIZCxQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNS0xMS0yN1QwOToxMjo0MSswMDowMM3b+nkAAAAASUVORK5CYII=";
6164
6152
  function McpLogo({ className }) {
6165
6153
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -11024,7 +11012,7 @@ function SummaryChip({
11024
11012
  "span",
11025
11013
  {
11026
11014
  className: cn(
11027
- "border border-border bg-muted/40 inline-flex h-7 items-center gap-1 rounded-md px-2 text-xs text-muted-foreground",
11015
+ "bg-white/[0.045] inline-flex h-7 items-center gap-1 rounded-md px-2 text-xs text-muted-foreground",
11028
11016
  className
11029
11017
  ),
11030
11018
  children: [
@@ -11092,7 +11080,7 @@ function IdeGroupSection({
11092
11080
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
11093
11081
  "section",
11094
11082
  {
11095
- className: cn("border border-border bg-card shadow-sm relative isolate rounded-[8px]"),
11083
+ className: cn("bg-card/75 shadow-sm relative isolate rounded-[8px]"),
11096
11084
  "data-ide-group": group.id,
11097
11085
  children: [
11098
11086
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -11103,7 +11091,7 @@ function IdeGroupSection({
11103
11091
  onClick: onToggleIde,
11104
11092
  "aria-expanded": !collapsed,
11105
11093
  children: [
11106
- group.client === "unknown" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "border border-border bg-muted/40 inline-flex size-10 shrink-0 items-center justify-center rounded-[8px] text-slate-200", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { className: "size-5" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ClientLogo, { client: group.client, className: "size-10 rounded-[8px] text-cyan-100" }),
11094
+ group.client === "unknown" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-white/[0.045] inline-flex size-10 shrink-0 items-center justify-center rounded-[8px] text-slate-200", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Monitor, { className: "size-5" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ClientLogo, { client: group.client, className: "size-10 rounded-[8px] text-cyan-100" }),
11107
11095
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0 flex-1", children: [
11108
11096
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
11109
11097
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -11114,7 +11102,7 @@ function IdeGroupSection({
11114
11102
  children: group.label
11115
11103
  }
11116
11104
  ),
11117
- /* @__PURE__ */ jsxRuntimeExports.jsx("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", children: "IDE" })
11105
+ /* @__PURE__ */ jsxRuntimeExports.jsx("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", children: "IDE" })
11118
11106
  ] }),
11119
11107
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
11120
11108
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
@@ -11138,11 +11126,11 @@ function IdeGroupSection({
11138
11126
  /* @__PURE__ */ jsxRuntimeExports.jsx(SummaryChip, { label: "IN", value: formatTokenCount(totals.inputTokens) }),
11139
11127
  /* @__PURE__ */ jsxRuntimeExports.jsx(SummaryChip, { label: "OUT", value: formatTokenCount(totals.outputTokens) })
11140
11128
  ] }),
11141
- /* @__PURE__ */ jsxRuntimeExports.jsx("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", children: collapsed ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" }) })
11129
+ /* @__PURE__ */ jsxRuntimeExports.jsx("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", children: collapsed ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" }) })
11142
11130
  ]
11143
11131
  }
11144
11132
  ),
11145
- !collapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3 border-t border-white/10 px-3 pb-3 pt-3 sm:px-4", children: group.processes.map((processGroup) => /* @__PURE__ */ jsxRuntimeExports.jsx(
11133
+ !collapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-3 px-3 pb-3 pt-2 sm:px-4", children: group.processes.map((processGroup) => /* @__PURE__ */ jsxRuntimeExports.jsx(
11146
11134
  ProcessGroupSection,
11147
11135
  {
11148
11136
  group: processGroup,
@@ -11168,8 +11156,8 @@ function ProcessGroupSection({
11168
11156
  const logCount = group.logs.length;
11169
11157
  const processLabel = group.clientPid !== null && group.clientPid !== void 0 ? `PID ${group.clientPid}` : "process";
11170
11158
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "relative pl-5", children: [
11171
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute bottom-0 left-2 top-0 w-px bg-white/12", "aria-hidden": "true" }),
11172
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-border bg-card shadow-sm overflow-hidden rounded-[8px]", children: [
11159
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute bottom-1 left-2 top-1 w-px bg-white/[0.055]", "aria-hidden": "true" }),
11160
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-black/[0.10] overflow-hidden rounded-[8px]", children: [
11173
11161
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
11174
11162
  "button",
11175
11163
  {
@@ -11178,7 +11166,7 @@ function ProcessGroupSection({
11178
11166
  onClick: onToggle,
11179
11167
  "aria-expanded": !collapsed,
11180
11168
  children: [
11181
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "border border-border bg-muted/40 inline-flex size-8 shrink-0 items-center justify-center rounded-md text-amber-100", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { className: "size-4" }) }),
11169
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "bg-white/[0.045] inline-flex size-8 shrink-0 items-center justify-center rounded-md text-amber-100", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Cpu, { className: "size-4" }) }),
11182
11170
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0 flex-1", children: [
11183
11171
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
11184
11172
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-xs font-semibold tabular-nums text-slate-100", children: processLabel }),
@@ -11211,11 +11199,11 @@ function ProcessGroupSection({
11211
11199
  /* @__PURE__ */ jsxRuntimeExports.jsx(SummaryChip, { label: "IN", value: formatTokenCount(totals.inputTokens) }),
11212
11200
  /* @__PURE__ */ jsxRuntimeExports.jsx(SummaryChip, { label: "OUT", value: formatTokenCount(totals.outputTokens) })
11213
11201
  ] }),
11214
- /* @__PURE__ */ jsxRuntimeExports.jsx("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", children: collapsed ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" }) })
11202
+ /* @__PURE__ */ jsxRuntimeExports.jsx("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", children: collapsed ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" }) })
11215
11203
  ]
11216
11204
  }
11217
11205
  ),
11218
- !collapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2 border-t border-white/10 p-2 sm:p-3", children: group.sessions.map((session) => /* @__PURE__ */ jsxRuntimeExports.jsx(
11206
+ !collapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2 p-2 pt-1 sm:p-3 sm:pt-2", children: group.sessions.map((session) => /* @__PURE__ */ jsxRuntimeExports.jsx(
11219
11207
  ConversationGroup,
11220
11208
  {
11221
11209
  group: session,