@tonyclaw/agent-inspector 2.1.17 → 2.1.18

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 (37) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-B9sLBHw5.js → CompareDrawer-Fqqw5Lci.js} +1 -1
  3. package/.output/public/assets/{ProxyViewerContainer-DbWjc_BE.js → ProxyViewerContainer-CP_RzwW9.js} +31 -31
  4. package/.output/public/assets/{ReplayDialog-D9I9W9n3.js → ReplayDialog-hYhz_i6W.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-DGXK_Rii.js → RequestAnatomy-CTuWbtwC.js} +1 -1
  6. package/.output/public/assets/{ResponseView-df_JzwsS.js → ResponseView-CsZ7S5Gv.js} +2 -2
  7. package/.output/public/assets/{StreamingChunkSequence-i3DM5IKL.js → StreamingChunkSequence-PIbJMotU.js} +1 -1
  8. package/.output/public/assets/_sessionId-CUGA9qU3.js +1 -0
  9. package/.output/public/assets/{index-Dqc2r1ea.js → index-F7mynwuH.js} +1 -1
  10. package/.output/public/assets/index-G4dTUobw.js +1 -0
  11. package/.output/public/assets/index-gvYkgMPF.css +1 -0
  12. package/.output/public/assets/{json-viewer-D3QWQlSB.js → json-viewer-DCXtNmmD.js} +1 -1
  13. package/.output/public/assets/{main-ZhxhOCmF.js → main-BJZf7_Ib.js} +2 -2
  14. package/.output/server/{_sessionId-YFrP0lZP.mjs → _sessionId-DjSHDNh8.mjs} +2 -2
  15. package/.output/server/_ssr/{CompareDrawer-qC3adEeq.mjs → CompareDrawer-Dal52IFo.mjs} +2 -2
  16. package/.output/server/_ssr/{ProxyViewerContainer-DCZIJC2b.mjs → ProxyViewerContainer-CVqFZGff.mjs} +1923 -1918
  17. package/.output/server/_ssr/{ReplayDialog-CvX4XYCi.mjs → ReplayDialog-BOEgdMes.mjs} +3 -3
  18. package/.output/server/_ssr/{RequestAnatomy-HQfrxjO1.mjs → RequestAnatomy-gch7_vCV.mjs} +2 -2
  19. package/.output/server/_ssr/{ResponseView-QSlSD11e.mjs → ResponseView-lEWPy-H4.mjs} +2 -2
  20. package/.output/server/_ssr/{StreamingChunkSequence-DTAiwlnI.mjs → StreamingChunkSequence-DnudbJ1O.mjs} +2 -2
  21. package/.output/server/_ssr/{index-D_EzfXaN.mjs → index-Cza8zDrW.mjs} +2 -2
  22. package/.output/server/_ssr/index.mjs +2 -2
  23. package/.output/server/_ssr/{json-viewer-CrIX0e0Q.mjs → json-viewer-C2lIVEX3.mjs} +2 -2
  24. package/.output/server/_ssr/{router-__o2wrfO.mjs → router-bF9uzd1k.mjs} +545 -22
  25. package/.output/server/{_tanstack-start-manifest_v-DbqyLpsI.mjs → _tanstack-start-manifest_v-ErvW-mtr.mjs} +1 -1
  26. package/.output/server/index.mjs +70 -70
  27. package/package.json +1 -1
  28. package/src/components/proxy-viewer/ThreadConnector.tsx +72 -88
  29. package/src/components/proxy-viewer/TurnGroup.tsx +1 -0
  30. package/src/mcp/server.ts +275 -0
  31. package/src/mcp/toolHandlers.ts +380 -3
  32. package/src/proxy/logSearch.ts +3 -0
  33. package/src/proxy/store.ts +40 -15
  34. package/src/routes/api/logs.ts +8 -2
  35. package/.output/public/assets/_sessionId-CTpCHdFh.js +0 -1
  36. package/.output/public/assets/index-Blqvndcn.js +0 -1
  37. 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-bF9uzd1k.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";
@@ -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-Dal52IFo.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-BOEgdMes.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-gch7_vCV.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-lEWPy-H4.mjs").then((m) => ({ default: m.ResponseView }))
1972
1972
  );
1973
1973
  const LazyStreamingChunkSequence = reactExports.lazy(
1974
- () => import("./StreamingChunkSequence-DTAiwlnI.mjs").then((m) => ({
1974
+ () => import("./StreamingChunkSequence-DnudbJ1O.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-C2lIVEX3.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-C2lIVEX3.mjs").then((m) => ({ default: m.JsonViewerFromString }))
1983
1983
  );
1984
1984
  const HIGHLIGHT_DURATION_MS = 1200;
1985
1985
  const MAX_HIGHLIGHT_ATTEMPTS = 12;
@@ -4134,2032 +4134,2037 @@ const LogEntry = reactExports.memo(function({
4134
4134
  ) })
4135
4135
  ] });
4136
4136
  });
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
4137
+ function SvgShell({
4138
+ className,
4139
+ style,
4140
+ d,
4141
+ eyeStalks,
4142
+ eyes,
4143
+ legs,
4144
+ extras
4220
4145
  }) {
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
4146
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4266
- "div",
4147
+ "svg",
4267
4148
  {
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",
4149
+ viewBox: "0 0 24 24",
4150
+ fill: "none",
4151
+ stroke: "currentColor",
4152
+ strokeWidth: "1.5",
4153
+ strokeLinecap: "round",
4154
+ strokeLinejoin: "round",
4155
+ "aria-hidden": "true",
4156
+ style,
4157
+ className: cn("inline-block size-5", className),
4269
4158
  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
- )
4159
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d }),
4160
+ eyeStalks,
4161
+ eyes,
4162
+ legs,
4163
+ extras
4287
4164
  ]
4288
- },
4289
- event.id
4165
+ }
4290
4166
  );
4291
4167
  }
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;
4312
- }
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;
4168
+ const StdLegs = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4169
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
4170
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
4171
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
4172
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
4173
+ ] });
4174
+ const ShortLegs = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4175
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7", y1: "16", x2: "5.5", y2: "18.5" }),
4176
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "17", x2: "8.5", y2: "19.5" }),
4177
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "17", x2: "15.5", y2: "19.5" }),
4178
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17", y1: "16", x2: "18.5", y2: "18.5" })
4179
+ ] });
4180
+ function Crab1({ className }) {
4181
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4182
+ SvgShell,
4183
+ {
4184
+ className,
4185
+ 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",
4186
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4187
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
4188
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
4189
+ ] }),
4190
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4191
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
4192
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" })
4193
+ ] }),
4194
+ legs: StdLegs,
4195
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4196
+ /* @__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" }),
4197
+ /* @__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" })
4198
+ ] })
4326
4199
  }
4327
- }
4328
- if (entries.length > 0) groups.push({ entries, turnIndex });
4329
- return groups;
4200
+ );
4330
4201
  }
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);
4202
+ function Crab2({ className }) {
4203
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4204
+ SvgShell,
4205
+ {
4206
+ className,
4207
+ 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",
4208
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4209
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "6.5", x2: "9.5", y2: "4.5" }),
4210
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "6.5", x2: "14.5", y2: "4.5" })
4211
+ ] }),
4212
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4213
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4", r: "1.1", fill: "currentColor", stroke: "none" }),
4214
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4", r: "1.1", fill: "currentColor", stroke: "none" })
4215
+ ] }),
4216
+ legs: ShortLegs,
4217
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4218
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C4 9.5 2.5 10 3 12 C3.5 13.5 4.5 13 5 12" }),
4219
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20 9.5 21.5 10 21 12 C20.5 13.5 19.5 13 19 12" })
4220
+ ] })
4342
4221
  }
4343
- }
4344
- return predecessors;
4222
+ );
4345
4223
  }
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);
4224
+ function Crab3({ className }) {
4225
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4226
+ SvgShell,
4227
+ {
4228
+ className,
4229
+ 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",
4230
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4231
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7", y1: "7", x2: "6.5", y2: "4.5" }),
4232
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17", y1: "7", x2: "17.5", y2: "4.5" })
4233
+ ] }),
4234
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4235
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "6.5", cy: "4", r: "0.8", fill: "currentColor", stroke: "none" }),
4236
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "17.5", cy: "4", r: "0.8", fill: "currentColor", stroke: "none" })
4237
+ ] }),
4238
+ legs: StdLegs,
4239
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4240
+ /* @__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" }),
4241
+ /* @__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" })
4242
+ ] })
4352
4243
  }
4353
- return parsed;
4354
- } catch {
4355
- return null;
4356
- }
4244
+ );
4357
4245
  }
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;
4365
- }
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;
4246
+ function Crab4({ className }) {
4247
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4248
+ SvgShell,
4249
+ {
4250
+ className,
4251
+ 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",
4252
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4253
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "6.5", x2: "10", y2: "3.5" }),
4254
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "6.5", x2: "14", y2: "3.5" })
4255
+ ] }),
4256
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4257
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "10", cy: "3", r: "0.7", fill: "currentColor", stroke: "none" }),
4258
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14", cy: "3", r: "0.7", fill: "currentColor", stroke: "none" })
4259
+ ] }),
4260
+ legs: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4261
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7", y1: "17", x2: "5", y2: "20.5" }),
4262
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "18.5", x2: "8.5", y2: "21.5" }),
4263
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "18.5", x2: "15.5", y2: "21.5" }),
4264
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17", y1: "17", x2: "19", y2: "20.5" })
4265
+ ] }),
4266
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4267
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M6.5 11 C5 8.5 3 9 3.5 11.5 C4 13.5 5 13 6 12" }),
4268
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M17.5 11 C19 8.5 21 9 20.5 11.5 C20 13.5 19 13 18 12" })
4269
+ ] })
4270
+ }
4271
+ );
4371
4272
  }
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;
4273
+ function Crab5({ className }) {
4274
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4275
+ SvgShell,
4276
+ {
4277
+ className,
4278
+ 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",
4279
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4280
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
4281
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
4282
+ ] }),
4283
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4284
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.8", fill: "currentColor", stroke: "none" }),
4285
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.8", fill: "currentColor", stroke: "none" })
4286
+ ] }),
4287
+ legs: StdLegs,
4288
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4289
+ /* @__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" }),
4290
+ /* @__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" })
4291
+ ] })
4292
+ }
4293
+ );
4394
4294
  }
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
- });
4295
+ function Crab6({ className }) {
4296
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4297
+ SvgShell,
4298
+ {
4299
+ className,
4300
+ 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",
4301
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4302
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
4303
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
4304
+ ] }),
4305
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4306
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
4307
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "13.5", y1: "4", x2: "15.5", y2: "5" })
4308
+ ] }),
4309
+ legs: StdLegs,
4310
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4311
+ /* @__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" }),
4312
+ /* @__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" })
4313
+ ] })
4418
4314
  }
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
- });
4315
+ );
4316
+ }
4317
+ function Crab7({ className }) {
4318
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4319
+ SvgShell,
4320
+ {
4321
+ className,
4322
+ 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",
4323
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4324
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "8", x2: "10", y2: "6" }),
4325
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "8", x2: "14", y2: "6" })
4326
+ ] }),
4327
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4328
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "6", x2: "11", y2: "6.5" }),
4329
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "13", y1: "6", x2: "15", y2: "6.5" })
4330
+ ] }),
4331
+ legs: ShortLegs,
4332
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4333
+ /* @__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" }),
4334
+ /* @__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" })
4335
+ ] })
4436
4336
  }
4437
- }
4438
- return events;
4337
+ );
4439
4338
  }
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;
4339
+ function Crab8({ className }) {
4340
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4341
+ SvgShell,
4342
+ {
4343
+ className,
4344
+ 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",
4345
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4346
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
4347
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
4348
+ ] }),
4349
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4350
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.7", fill: "currentColor", stroke: "none" }),
4351
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.7", fill: "currentColor", stroke: "none" })
4352
+ ] }),
4353
+ legs: StdLegs,
4354
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4355
+ /* @__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" }),
4356
+ /* @__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" }),
4357
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "8.5", y1: "4", x2: "10.5", y2: "4.5" }),
4358
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15.5", y1: "4", x2: "13.5", y2: "4.5" }),
4359
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9 15.5 C10 14.5 14 14.5 15 15.5" })
4360
+ ] })
4361
+ }
4362
+ );
4473
4363
  }
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;
4364
+ function Crab9({ className }) {
4365
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4366
+ SvgShell,
4367
+ {
4368
+ className,
4369
+ 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",
4370
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4371
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
4372
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" })
4373
+ ] }),
4374
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4375
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "1.2", fill: "none", stroke: "currentColor" }),
4376
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "1.2", fill: "none", stroke: "currentColor" })
4377
+ ] }),
4378
+ legs: StdLegs,
4379
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4380
+ /* @__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" }),
4381
+ /* @__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" }),
4382
+ /* @__PURE__ */ jsxRuntimeExports.jsx("ellipse", { cx: "12", cy: "15", rx: "2", ry: "1.5", fill: "currentColor", stroke: "none" })
4383
+ ] })
4490
4384
  }
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;
4385
+ );
4386
+ }
4387
+ function Crab10({ className }) {
4388
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4389
+ SvgShell,
4390
+ {
4391
+ className,
4392
+ 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",
4393
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4394
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9.5", y1: "7", x2: "9", y2: "5" }),
4395
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14.5", y1: "7", x2: "15", y2: "5" })
4396
+ ] }),
4397
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4398
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9", cy: "4.5", r: "1", fill: "currentColor", stroke: "none" }),
4399
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "15", cy: "4.5", r: "1", fill: "currentColor", stroke: "none" })
4400
+ ] }),
4401
+ legs: StdLegs,
4402
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4403
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9 2 9.5 2.5 12 C3 14 4 13 5 12" }),
4404
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9 22 9.5 21.5 12 C21 14 20 13 19 12" }),
4405
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9 14.5 C10 16 14 16 15 14.5" }),
4406
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "7", cy: "13", r: "1", fill: "currentColor", stroke: "none", opacity: "0.3" }),
4407
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "17", cy: "13", r: "1", fill: "currentColor", stroke: "none", opacity: "0.3" })
4408
+ ] })
4498
4409
  }
4499
- if (log.cacheReadInputTokens !== null) {
4500
- totalCacheReadInputTokens += log.cacheReadInputTokens;
4410
+ );
4411
+ }
4412
+ function Crab11({ className }) {
4413
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4414
+ SvgShell,
4415
+ {
4416
+ className,
4417
+ 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",
4418
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4419
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5.5" }),
4420
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5.5" })
4421
+ ] }),
4422
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4423
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "7.5", y1: "5.5", x2: "16.5", y2: "5.5" }),
4424
+ /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "7", y: "5", width: "4", height: "2", rx: "0.5", fill: "currentColor", stroke: "none" }),
4425
+ /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { x: "13", y: "5", width: "4", height: "2", rx: "0.5", fill: "currentColor", stroke: "none" })
4426
+ ] }),
4427
+ legs: StdLegs,
4428
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4429
+ /* @__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" }),
4430
+ /* @__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" }),
4431
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9.5 15 C10.5 16 13.5 16 14.5 15" })
4432
+ ] })
4501
4433
  }
4502
- if (log.elapsedMs !== null) {
4503
- totalElapsedMs += log.elapsedMs;
4504
- maxElapsedMs = maxElapsedMs === null ? log.elapsedMs : Math.max(maxElapsedMs, log.elapsedMs);
4434
+ );
4435
+ }
4436
+ function Crab12({ className }) {
4437
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4438
+ SvgShell,
4439
+ {
4440
+ className,
4441
+ 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",
4442
+ eyeStalks: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4443
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10.5", y1: "9.5", x2: "10.5", y2: "8" }),
4444
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "13.5", y1: "9.5", x2: "13.5", y2: "8" })
4445
+ ] }),
4446
+ eyes: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4447
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "10.5", cy: "7.5", r: "0.8", fill: "currentColor", stroke: "none" }),
4448
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "13.5", cy: "7.5", r: "0.8", fill: "currentColor", stroke: "none" })
4449
+ ] }),
4450
+ legs: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4451
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "8", y1: "16", x2: "6.5", y2: "18" }),
4452
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "17", x2: "9.5", y2: "19" }),
4453
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "17", x2: "14.5", y2: "19" }),
4454
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "16", y1: "16", x2: "17.5", y2: "18" })
4455
+ ] }),
4456
+ extras: /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4457
+ /* @__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" }),
4458
+ /* @__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" })
4459
+ ] })
4505
4460
  }
4506
- toolCallCount += extractToolTraceEvents(log).length;
4461
+ );
4462
+ }
4463
+ const crabVariants = [
4464
+ Crab1,
4465
+ Crab2,
4466
+ Crab3,
4467
+ Crab4,
4468
+ Crab5,
4469
+ Crab6,
4470
+ Crab7,
4471
+ Crab8,
4472
+ Crab9,
4473
+ Crab10,
4474
+ Crab11,
4475
+ Crab12
4476
+ ];
4477
+ function getCrabVariant(index) {
4478
+ return crabVariants[Math.abs(index) % crabVariants.length] ?? Crab1;
4479
+ }
4480
+ function getInteriorCrabVariantIndex(seed) {
4481
+ const interiorCount = crabVariants.length - 2;
4482
+ if (interiorCount <= 0) return 0;
4483
+ const normalizedSeed = Number.isFinite(seed) ? Math.trunc(seed) : 0;
4484
+ return 1 + (normalizedSeed % interiorCount + interiorCount) % interiorCount;
4485
+ }
4486
+ function getInteriorCrabVariant(seed) {
4487
+ return getCrabVariant(getInteriorCrabVariantIndex(seed));
4488
+ }
4489
+ function boundaryTitle(stopReason) {
4490
+ switch (stopReason) {
4491
+ case "end_turn":
4492
+ return "End of Turn (Anthropic)";
4493
+ case "stop":
4494
+ return "End of Turn (OpenAI)";
4495
+ case "length":
4496
+ return "End of Turn (length limit)";
4497
+ case "tool_use":
4498
+ case null:
4499
+ return "End of Turn";
4507
4500
  }
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
4501
  }
4525
- function formatElapsed$2(ms) {
4526
- if (ms < 1e3) return `${ms}ms`;
4527
- return `${(ms / 1e3).toFixed(1)}s`;
4502
+ function crabShellClass(interactive) {
4503
+ return interactive ? "cursor-pointer transition-transform hover:scale-110" : "";
4528
4504
  }
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
4505
+ function ThreadConnector({
4506
+ stopReason,
4507
+ isPending,
4508
+ isFirst,
4509
+ isTurnStart,
4510
+ isOnlyEntry = false,
4511
+ crabIndex = 0,
4512
+ collapsible = false,
4513
+ onToggle
4541
4514
  }) {
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;
4515
+ const isBoundary = isTurnBoundary(stopReason);
4516
+ const isFusedBoundary = isOnlyEntry && isTurnStart && isBoundary;
4517
+ const isRunning = isPending && !isBoundary;
4518
+ const interactive = collapsible && onToggle !== void 0;
4519
+ const Crab = reactExports.useMemo(() => getCrabVariant(crabIndex), [crabIndex]);
4520
+ const FusedCrab = reactExports.useMemo(() => getInteriorCrabVariant(crabIndex), [crabIndex]);
4521
+ const handleCrabClick = (event) => {
4522
+ if (!interactive || onToggle === void 0) return;
4523
+ event.stopPropagation();
4524
+ onToggle();
4525
+ };
4526
+ const handleCrabKeyDown = (event) => {
4527
+ if (!interactive || onToggle === void 0) return;
4528
+ if (event.key !== "Enter" && event.key !== " ") return;
4529
+ event.preventDefault();
4530
+ onToggle();
4531
+ };
4532
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center w-6 shrink-0 pt-0.5 pb-0.5", children: [
4533
+ /* @__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" }) }),
4534
+ isFusedBoundary ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4535
+ "span",
4536
+ {
4537
+ role: interactive ? "button" : void 0,
4538
+ tabIndex: interactive ? 0 : void 0,
4539
+ title: "Single-request turn",
4540
+ className: crabShellClass(interactive),
4541
+ onClick: handleCrabClick,
4542
+ onKeyDown: handleCrabKeyDown,
4543
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4544
+ FusedCrab,
4545
+ {
4546
+ className: cn(
4547
+ "size-3.5 text-[#80FF00]",
4548
+ "animate-crab-settle",
4549
+ "drop-shadow-[0_0_4px_rgba(128,255,0,0.5)]"
4550
+ )
4551
+ }
4552
+ )
4595
4553
  }
4596
- if (e.log.outputTokens !== null) {
4597
- totalOutput += e.log.outputTokens;
4598
- hasTokens = true;
4554
+ ) : isBoundary ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4555
+ "span",
4556
+ {
4557
+ role: interactive ? "button" : void 0,
4558
+ tabIndex: interactive ? 0 : void 0,
4559
+ title: boundaryTitle(stopReason),
4560
+ className: crabShellClass(interactive),
4561
+ onClick: handleCrabClick,
4562
+ onKeyDown: handleCrabKeyDown,
4563
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4564
+ Crab,
4565
+ {
4566
+ className: cn(
4567
+ "size-3.5 text-amber-400",
4568
+ "animate-crab-settle",
4569
+ "drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]"
4570
+ )
4571
+ }
4572
+ )
4599
4573
  }
4600
- if (e.log.elapsedMs !== null) {
4601
- maxElapsed = maxElapsed === null ? e.log.elapsedMs : Math.max(maxElapsed, e.log.elapsedMs);
4574
+ ) : isTurnStart ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4575
+ "span",
4576
+ {
4577
+ role: interactive ? "button" : void 0,
4578
+ tabIndex: interactive ? 0 : void 0,
4579
+ title: "Start of turn",
4580
+ className: crabShellClass(interactive),
4581
+ onClick: handleCrabClick,
4582
+ onKeyDown: handleCrabKeyDown,
4583
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4584
+ Crab,
4585
+ {
4586
+ className: cn(
4587
+ "size-3.5 text-emerald-400",
4588
+ "animate-crab-appear",
4589
+ "drop-shadow-[0_0_4px_rgba(52,211,153,0.5)]"
4590
+ )
4591
+ }
4592
+ )
4602
4593
  }
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(
4594
+ ) : 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" }) }),
4595
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex justify-center min-h-0", children: !isBoundary && /* @__PURE__ */ jsxRuntimeExports.jsx(
4596
+ "div",
4597
+ {
4598
+ className: cn(
4599
+ "w-0.5 h-full",
4600
+ isPending ? "border-dashed bg-transparent border-l-2 border-muted-foreground/20" : "bg-muted-foreground/30"
4601
+ )
4602
+ }
4603
+ ) })
4604
+ ] });
4605
+ }
4606
+ function ToolTraceEventRow({ event }) {
4607
+ const argumentCopy = useCopyFeedback(event.argumentsText);
4608
+ const canCopyArguments = event.argumentsText !== null;
4609
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4647
4610
  "div",
4648
4611
  {
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
- }
4612
+ 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",
4613
+ children: [
4614
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 shrink-0 text-sky-400/70" }),
4615
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-foreground/80", children: event.name }),
4616
+ event.argumentsPreview !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
4617
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 shrink-0 text-muted-foreground/60" }),
4618
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate font-mono text-muted-foreground", children: event.argumentsPreview })
4619
+ ] }),
4620
+ canCopyArguments && /* @__PURE__ */ jsxRuntimeExports.jsx(
4621
+ "button",
4622
+ {
4623
+ type: "button",
4624
+ 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",
4625
+ onClick: argumentCopy.copy,
4626
+ "aria-label": argumentCopy.copied ? "Copied tool arguments" : "Copy tool arguments",
4627
+ title: argumentCopy.copied ? "Copied tool arguments" : "Copy tool arguments",
4628
+ children: argumentCopy.copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
4629
+ }
4630
+ )
4631
+ ]
4632
+ },
4633
+ event.id
4808
4634
  );
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
- }
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;
4826
4635
  }
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
- );
4636
+ function ToolTraceEvents({ events }) {
4637
+ if (events.length === 0) return null;
4638
+ 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)) });
4845
4639
  }
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)) });
4640
+ const PREVIEW_LIMIT = 180;
4641
+ const toolTraceCache = /* @__PURE__ */ new WeakMap();
4642
+ function responseMayContainToolTrace(log, format) {
4643
+ const responseText = log.responseText;
4644
+ if (responseText === null) return false;
4645
+ switch (format) {
4646
+ case "anthropic":
4647
+ return responseText.includes("tool_use");
4648
+ case "openai":
4649
+ return responseText.includes("tool_calls") || responseText.includes("function_call");
4650
+ case "unknown":
4651
+ return false;
4849
4652
  }
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
4653
  }
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 });
4654
+ function isTurnCollapsible(entryCount, isComplete, isPending) {
4655
+ return entryCount > 1 && isComplete && !isPending;
4948
4656
  }
4949
- function firstFailureLog(logs) {
4950
- for (const log of logs) {
4951
- if (log.responseStatus !== null && log.responseStatus >= 400) return log;
4657
+ function buildTurnGroups(logs) {
4658
+ const groups = [];
4659
+ let entries = [];
4660
+ let turnIndex = 0;
4661
+ for (let index = 0; index < logs.length; index += 1) {
4662
+ const log = logs[index];
4663
+ if (log === void 0) continue;
4664
+ entries.push({ log, stopReason: extractStopReason(log), sessionLogNumber: index + 1 });
4665
+ const current = entries[entries.length - 1];
4666
+ if (current !== void 0 && isTurnBoundary(current.stopReason)) {
4667
+ groups.push({ entries, turnIndex });
4668
+ entries = [];
4669
+ turnIndex += 1;
4670
+ }
4952
4671
  }
4953
- return null;
4672
+ if (entries.length > 0) groups.push({ entries, turnIndex });
4673
+ return groups;
4954
4674
  }
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;
4675
+ function buildValidPredecessors(groups) {
4676
+ const predecessors = /* @__PURE__ */ new Map();
4677
+ for (const group of groups) {
4678
+ for (let index = 1; index < group.logs.length; index += 1) {
4679
+ const current = group.logs[index];
4680
+ const previous = group.logs[index - 1];
4681
+ if (current === void 0 || previous === void 0) continue;
4682
+ const currentFormat = resolveLogFormat(current);
4683
+ const previousFormat = resolveLogFormat(previous);
4684
+ if (currentFormat === "unknown" || currentFormat !== previousFormat) continue;
4685
+ predecessors.set(current.id, previous);
4963
4686
  }
4964
4687
  }
4965
- return current;
4688
+ return predecessors;
4966
4689
  }
4967
- function firstToolLog(logs) {
4968
- for (const log of logs) {
4969
- if (extractToolTraceEvents(log).length > 0) return log;
4690
+ function parseJsonResponse(responseText) {
4691
+ if (responseText === null) return null;
4692
+ try {
4693
+ const parsed = JSON.parse(responseText);
4694
+ if (typeof parsed === "string") {
4695
+ return JSON.parse(parsed);
4696
+ }
4697
+ return parsed;
4698
+ } catch {
4699
+ return null;
4970
4700
  }
4971
- return null;
4972
4701
  }
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;
4702
+ function previewValue(value) {
4703
+ if (value === void 0 || value === null) return null;
4704
+ const raw = typeof value === "string" ? value : JSON.stringify(value);
4705
+ if (raw === void 0) return null;
4706
+ const normalized = raw.replace(/\s+/g, " ").trim();
4707
+ if (normalized.length === 0) return null;
4708
+ return normalized.length > PREVIEW_LIMIT ? `${normalized.slice(0, PREVIEW_LIMIT - 1)}...` : normalized;
4981
4709
  }
4982
- function displayLogNumber(logId, displayNumberByLogId) {
4983
- return String(displayNumberByLogId.get(logId) ?? logId);
4710
+ function copyValue(value) {
4711
+ if (value === void 0 || value === null) return null;
4712
+ if (typeof value === "string") return value.length > 0 ? value : null;
4713
+ const raw = JSON.stringify(value, null, 2);
4714
+ return raw === void 0 || raw.length === 0 ? null : raw;
4984
4715
  }
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
4716
+ function extractAnthropicToolTraceEvents(log) {
4717
+ const parsed = parseJsonResponse(log.responseText);
4718
+ const content = safeGetOwnProperty(parsed, "content");
4719
+ if (!Array.isArray(content)) return [];
4720
+ const events = [];
4721
+ for (const block of content) {
4722
+ const type = safeGetOwnProperty(block, "type");
4723
+ if (type !== "tool_use") continue;
4724
+ const name = safeGetOwnProperty(block, "name");
4725
+ if (typeof name !== "string" || name.length === 0) continue;
4726
+ const input = safeGetOwnProperty(block, "input");
4727
+ events.push({
4728
+ id: `${String(log.id)}-anthropic-tool-${String(events.length)}`,
4729
+ logId: log.id,
4730
+ index: events.length,
4731
+ provider: "anthropic",
4732
+ name,
4733
+ argumentsText: copyValue(input),
4734
+ argumentsPreview: previewValue(input)
5010
4735
  });
5011
4736
  }
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
- });
5020
- }
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
- });
5032
- }
5033
- return insights;
5034
- }
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" });
5047
- }
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
- )) });
4737
+ return events;
5074
4738
  }
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";
4739
+ function extractOpenAIToolTraceEvents(log) {
4740
+ const parsed = parseJsonResponse(log.responseText);
4741
+ const choices = safeGetOwnProperty(parsed, "choices");
4742
+ if (!Array.isArray(choices)) return [];
4743
+ const events = [];
4744
+ for (const choice of choices) {
4745
+ const message = safeGetOwnProperty(choice, "message");
4746
+ const toolCalls = safeGetOwnProperty(message, "tool_calls");
4747
+ if (!Array.isArray(toolCalls)) continue;
4748
+ for (const call of toolCalls) {
4749
+ const fn = safeGetOwnProperty(call, "function");
4750
+ const name = safeGetOwnProperty(fn, "name");
4751
+ if (typeof name !== "string" || name.length === 0) continue;
4752
+ const args = safeGetOwnProperty(fn, "arguments");
4753
+ events.push({
4754
+ id: `${String(log.id)}-openai-tool-${String(events.length)}`,
4755
+ logId: log.id,
4756
+ index: events.length,
4757
+ provider: "openai",
4758
+ name,
4759
+ argumentsText: copyValue(args),
4760
+ argumentsPreview: previewValue(args)
4761
+ });
4762
+ }
5083
4763
  }
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";
4764
+ const output = safeGetOwnProperty(parsed, "output");
4765
+ if (Array.isArray(output)) {
4766
+ for (const item of output) {
4767
+ if (safeGetOwnProperty(item, "type") !== "function_call") continue;
4768
+ const name = safeGetOwnProperty(item, "name");
4769
+ if (typeof name !== "string" || name.length === 0) continue;
4770
+ const args = safeGetOwnProperty(item, "arguments");
4771
+ events.push({
4772
+ id: `${String(log.id)}-openai-tool-${String(events.length)}`,
4773
+ logId: log.id,
4774
+ index: events.length,
4775
+ provider: "openai",
4776
+ name,
4777
+ argumentsText: copyValue(args),
4778
+ argumentsPreview: previewValue(args)
4779
+ });
4780
+ }
5094
4781
  }
4782
+ return events;
5095
4783
  }
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;
5149
- }
5150
- if (content.length === 0) {
5151
- setLocalError("Content is required.");
5152
- return;
5153
- }
5154
- if (tags.length === 0) {
5155
- setLocalError("At least one tag is required.");
5156
- return;
5157
- }
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
- ] }) });
4784
+ function extractToolTraceEvents(log) {
4785
+ const cached = toolTraceCache.get(log);
4786
+ if (cached !== void 0 && cached.apiFormat === log.apiFormat && cached.responseText === log.responseText) {
4787
+ return cached.events;
5266
4788
  }
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
- ] });
4789
+ const format = resolveLogFormat(log);
4790
+ if (!responseMayContainToolTrace(log, format)) {
4791
+ const events2 = [];
4792
+ toolTraceCache.set(log, {
4793
+ apiFormat: log.apiFormat,
4794
+ responseText: log.responseText,
4795
+ events: events2
4796
+ });
4797
+ return events2;
4798
+ }
4799
+ let events;
4800
+ switch (format) {
4801
+ case "anthropic":
4802
+ events = extractAnthropicToolTraceEvents(log);
4803
+ break;
4804
+ case "openai":
4805
+ events = extractOpenAIToolTraceEvents(log);
4806
+ break;
4807
+ case "unknown":
4808
+ events = [];
4809
+ break;
4810
+ }
4811
+ toolTraceCache.set(log, {
4812
+ apiFormat: log.apiFormat,
4813
+ responseText: log.responseText,
4814
+ events
4815
+ });
4816
+ return events;
5364
4817
  }
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
- )) });
4818
+ function buildTraceSummary(logs, slowResponseThresholdSeconds, knowledgeCandidateCount = 0) {
4819
+ let failedCallCount = 0;
4820
+ let pendingCallCount = 0;
4821
+ let slowCallCount = 0;
4822
+ let totalInputTokens = 0;
4823
+ let totalOutputTokens = 0;
4824
+ let totalCacheCreationInputTokens = 0;
4825
+ let totalCacheReadInputTokens = 0;
4826
+ let totalElapsedMs = 0;
4827
+ let maxElapsedMs = null;
4828
+ let toolCallCount = 0;
4829
+ for (const log of logs) {
4830
+ if (log.responseStatus === null) {
4831
+ pendingCallCount += 1;
4832
+ } else if (log.responseStatus >= 400) {
4833
+ failedCallCount += 1;
4834
+ }
4835
+ if (log.elapsedMs !== null && slowResponseThresholdSeconds > 0 && log.elapsedMs > slowResponseThresholdSeconds * 1e3) {
4836
+ slowCallCount += 1;
4837
+ }
4838
+ if (log.inputTokens !== null) totalInputTokens += log.inputTokens;
4839
+ if (log.outputTokens !== null) totalOutputTokens += log.outputTokens;
4840
+ if (log.cacheCreationInputTokens !== null) {
4841
+ totalCacheCreationInputTokens += log.cacheCreationInputTokens;
4842
+ }
4843
+ if (log.cacheReadInputTokens !== null) {
4844
+ totalCacheReadInputTokens += log.cacheReadInputTokens;
4845
+ }
4846
+ if (log.elapsedMs !== null) {
4847
+ totalElapsedMs += log.elapsedMs;
4848
+ maxElapsedMs = maxElapsedMs === null ? log.elapsedMs : Math.max(maxElapsedMs, log.elapsedMs);
4849
+ }
4850
+ toolCallCount += extractToolTraceEvents(log).length;
4851
+ }
4852
+ return {
4853
+ llmCallCount: logs.length,
4854
+ toolCallCount,
4855
+ failedCallCount,
4856
+ pendingCallCount,
4857
+ slowCallCount,
4858
+ totalInputTokens,
4859
+ totalOutputTokens,
4860
+ totalCacheCreationInputTokens,
4861
+ totalCacheReadInputTokens,
4862
+ totalElapsedMs,
4863
+ maxElapsedMs,
4864
+ startedAt: logs[0]?.timestamp ?? null,
4865
+ endedAt: logs[logs.length - 1]?.timestamp ?? null,
4866
+ knowledgeCandidateCount
4867
+ };
5386
4868
  }
5387
- function AgentTraceSummary({
5388
- logs,
5389
- scopeId,
4869
+ function formatElapsed$2(ms) {
4870
+ if (ms < 1e3) return `${ms}ms`;
4871
+ return `${(ms / 1e3).toFixed(1)}s`;
4872
+ }
4873
+ const TurnGroup = reactExports.memo(function TurnGroup2({
4874
+ entries,
4875
+ viewMode,
4876
+ strip,
5390
4877
  slowResponseThresholdSeconds,
5391
- showRollupMetrics,
5392
- timeDisplayFormat
4878
+ cacheTrends,
4879
+ onCompareWithPrevious,
4880
+ comparisonPredecessors,
4881
+ turnIndex = 0,
4882
+ timeDisplayFormat,
4883
+ pendingFocusRequest,
4884
+ showLogClientMetadata = true
5393
4885
  }) {
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;
4886
+ const lastIdx = entries.length - 1;
4887
+ const lastStop = entries[lastIdx]?.stopReason ?? null;
4888
+ const isComplete = lastStop !== null ? isTurnBoundary(lastStop) : false;
4889
+ const isPending = entries[lastIdx]?.log.responseStatus === null;
4890
+ const isSingleLog = entries.length === 1;
4891
+ const collapsible = isTurnCollapsible(entries.length, isComplete, isPending);
4892
+ const [collapsed, setCollapsed] = reactExports.useState(false);
4893
+ const prevCompleteRef = reactExports.useRef(false);
4894
+ reactExports.useEffect(() => {
4895
+ if (!collapsible) {
4896
+ setCollapsed(false);
4897
+ } else if (isComplete && !prevCompleteRef.current) {
4898
+ setCollapsed(true);
4899
+ }
4900
+ prevCompleteRef.current = isComplete;
4901
+ }, [collapsible, isComplete]);
4902
+ reactExports.useEffect(() => {
4903
+ const handleLogFocusRequest = (event) => {
4904
+ const request = readLogFocusRequest(event);
4905
+ if (request === null) return;
4906
+ if (!entries.some((entry) => entry.log.id === request.logId)) return;
4907
+ if (collapsible) setCollapsed(false);
4908
+ };
4909
+ window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4910
+ return () => {
4911
+ window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4912
+ };
4913
+ }, [collapsible, entries]);
4914
+ reactExports.useEffect(() => {
4915
+ if (pendingFocusRequest === void 0) return void 0;
4916
+ if (!entries.some((entry) => entry.log.id === pendingFocusRequest.logId)) return void 0;
4917
+ if (collapsible) setCollapsed(false);
4918
+ const timer = window.setTimeout(() => {
4919
+ dispatchLogFocusRequest({
4920
+ logId: pendingFocusRequest.logId,
4921
+ tab: pendingFocusRequest.tab,
4922
+ source: "virtualizer"
5509
4923
  });
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
- });
4924
+ }, 0);
4925
+ return () => window.clearTimeout(timer);
4926
+ }, [collapsible, entries, pendingFocusRequest]);
4927
+ const toggleCollapse = reactExports.useCallback(() => {
4928
+ if (collapsible) setCollapsed((prev) => !prev);
4929
+ }, [collapsible]);
4930
+ const aggregate = reactExports.useMemo(() => {
4931
+ let totalInput = 0;
4932
+ let totalOutput = 0;
4933
+ let maxElapsed = null;
4934
+ let hasTokens = false;
4935
+ for (const e of entries) {
4936
+ if (e.log.inputTokens !== null) {
4937
+ totalInput += e.log.inputTokens;
4938
+ hasTokens = true;
5547
4939
  }
5548
- },
5549
- []
4940
+ if (e.log.outputTokens !== null) {
4941
+ totalOutput += e.log.outputTokens;
4942
+ hasTokens = true;
4943
+ }
4944
+ if (e.log.elapsedMs !== null) {
4945
+ maxElapsed = maxElapsed === null ? e.log.elapsedMs : Math.max(maxElapsed, e.log.elapsedMs);
4946
+ }
4947
+ }
4948
+ return {
4949
+ totalInput,
4950
+ totalOutput,
4951
+ hasTokens,
4952
+ maxElapsed
4953
+ };
4954
+ }, [entries, lastIdx]);
4955
+ const bgClass = turnIndex % 2 === 0 ? "bg-white/[0.012]" : "bg-cyan-300/[0.012]";
4956
+ const aggregateIsSlow = aggregate.maxElapsed !== null && slowResponseThresholdSeconds > 0 && aggregate.maxElapsed > slowResponseThresholdSeconds * 1e3;
4957
+ const toolEventsByLogId = reactExports.useMemo(() => {
4958
+ const events = /* @__PURE__ */ new Map();
4959
+ for (const entry of entries) {
4960
+ const extracted = extractToolTraceEvents(entry.log);
4961
+ if (extracted.length > 0) events.set(entry.log.id, extracted);
4962
+ }
4963
+ return events;
4964
+ }, [entries]);
4965
+ const [layoutVersion, setLayoutVersion] = reactExports.useState(0);
4966
+ const containerRef = reactExports.useRef(null);
4967
+ reactExports.useEffect(() => {
4968
+ const el = containerRef.current;
4969
+ if (!el) return;
4970
+ let raf = 0;
4971
+ const ro = new ResizeObserver(() => {
4972
+ window.cancelAnimationFrame(raf);
4973
+ raf = window.requestAnimationFrame(() => setLayoutVersion((v) => v + 1));
4974
+ });
4975
+ ro.observe(el);
4976
+ return () => {
4977
+ ro.disconnect();
4978
+ window.cancelAnimationFrame(raf);
4979
+ };
4980
+ }, []);
4981
+ const firstLogId = entries[0]?.log.id ?? turnIndex;
4982
+ const turnLabel = `Turn ${String(turnIndex + 1)}`;
4983
+ const containmentStyle = reactExports.useMemo(
4984
+ () => ({
4985
+ contentVisibility: "auto",
4986
+ containIntrinsicSize: collapsed ? "48px" : "160px"
4987
+ }),
4988
+ [collapsed]
5550
4989
  );
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,
4990
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4991
+ "div",
4992
+ {
4993
+ ref: containerRef,
4994
+ style: containmentStyle,
4995
+ tabIndex: collapsed ? void 0 : 0,
4996
+ role: collapsed ? void 0 : "group",
4997
+ "aria-label": collapsed ? void 0 : turnLabel,
4998
+ "data-nav-id": collapsed ? void 0 : `turn-${String(firstLogId)}`,
4999
+ className: cn(
5000
+ "rounded-[8px] border transition-[border-color,box-shadow]",
5001
+ isPending ? "border-amber-300/15 shadow-[0_0_0_1px_rgba(252,211,77,0.04)]" : "border-transparent",
5002
+ !collapsed && "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
5003
+ ),
5004
+ children: collapsed ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
5005
+ "div",
5606
5006
  {
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",
5007
+ "data-nav-id": `turn-collapsed-${String(firstLogId)}`,
5008
+ "data-nav-action": "expand",
5009
+ role: "button",
5010
+ tabIndex: 0,
5011
+ 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",
5012
+ onClick: toggleCollapse,
5013
+ onKeyDown: (e) => {
5014
+ if (e.key === "Enter" || e.key === " ") {
5015
+ e.preventDefault();
5016
+ toggleCollapse();
5017
+ }
5018
+ },
5613
5019
  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"
5020
+ /* @__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 }) }),
5021
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-6 shrink-0 flex flex-col items-center pt-1.5 pb-1.5", children: [
5022
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center h-[calc(0.75rem-8px)]" }),
5023
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "size-2 rounded-full border border-cyan-300/45 bg-cyan-300/20" }),
5024
+ /* @__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" }) }),
5025
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5026
+ "span",
5027
+ {
5028
+ className: cn(
5029
+ "size-2 rounded-full border",
5030
+ isComplete ? "border-amber-300/45 bg-amber-300/20" : "border-muted-foreground/30 bg-muted/30"
5031
+ )
5032
+ }
5033
+ )
5034
+ ] }),
5035
+ entries.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
5036
+ "div",
5037
+ {
5038
+ className: cn(
5039
+ "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)]",
5040
+ "border border-border bg-card shadow-sm transition-colors hover:border-cyan-200/20",
5041
+ bgClass
5042
+ ),
5043
+ children: [
5044
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-[10px] font-semibold uppercase tracking-[0.14em] text-muted-foreground/70", children: turnLabel }),
5045
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5046
+ "span",
5047
+ {
5048
+ className: "text-blue-300/85 font-mono font-semibold tabular-nums shrink-0",
5049
+ title: `Log IDs ${String(entries[0]?.log.id ?? "?")} ~ ${String(
5050
+ entries[lastIdx]?.log.id ?? "?"
5051
+ )}`,
5052
+ children: [
5053
+ "#",
5054
+ entries[0]?.sessionLogNumber ?? "?",
5055
+ " ~ #",
5056
+ entries[lastIdx]?.sessionLogNumber ?? "?"
5057
+ ]
5058
+ }
5059
+ ),
5060
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground shrink-0", children: [
5061
+ entries.length,
5062
+ " request",
5063
+ entries.length > 1 ? "s" : ""
5064
+ ] }),
5065
+ aggregate.maxElapsed !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
5066
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
5067
+ "span",
5068
+ {
5069
+ className: cn(
5070
+ "flex items-center gap-1 shrink-0",
5071
+ aggregateIsSlow ? "text-amber-400" : "text-muted-foreground"
5072
+ ),
5073
+ children: [
5074
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
5075
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed$2(aggregate.maxElapsed) }),
5076
+ aggregateIsSlow && /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Slow response" })
5077
+ ]
5078
+ }
5079
+ ) }),
5080
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: aggregateIsSlow ? `Slow response: ${formatElapsed$2(
5081
+ aggregate.maxElapsed
5082
+ )} exceeds ${formatElapsed$2(slowResponseThresholdSeconds * 1e3)}` : "Slowest request in this turn" })
5083
+ ] }) }),
5084
+ aggregate.hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 shrink-0", children: [
5085
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
5086
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
5087
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-400", children: [
5088
+ "IN ",
5089
+ formatTokens(aggregate.totalInput)
5090
+ ] }),
5091
+ " / ",
5092
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-amber-400", children: [
5093
+ "OUT ",
5094
+ formatTokens(aggregate.totalOutput)
5095
+ ] })
5096
+ ] })
5097
+ ] }),
5098
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 min-w-0" }),
5099
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5100
+ "span",
5101
+ {
5102
+ className: "border border-input bg-background inline-flex size-7 shrink-0 items-center justify-center rounded-md text-slate-200",
5103
+ "aria-hidden": "true",
5104
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-4 shrink-0" })
5105
+ }
5106
+ )
5107
+ ]
5108
+ }
5109
+ )
5616
5110
  ]
5617
5111
  }
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
- }
5112
+ ) : (
5113
+ /* ---- Expanded: full entries ---- */
5114
+ entries.map((entry, visibleIdx) => {
5115
+ const { log, stopReason: reason } = entry;
5116
+ const isTurnStart = visibleIdx === 0;
5117
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-stretch", children: [
5118
+ 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" }),
5119
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5120
+ ThreadConnector,
5121
+ {
5122
+ stopReason: reason,
5123
+ isPending: log.responseStatus === null,
5124
+ isFirst: visibleIdx === 0,
5125
+ isTurnStart,
5126
+ isOnlyEntry: isSingleLog,
5127
+ crabIndex: log.id % 12,
5128
+ collapsible: collapsible && isTurnStart,
5129
+ onToggle: toggleCollapse
5130
+ }
5131
+ ),
5132
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("flex-1 min-w-0 mb-0.5 rounded-[8px]", bgClass), children: [
5133
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5134
+ LogEntry,
5135
+ {
5136
+ log,
5137
+ displayNumber: entry.sessionLogNumber,
5138
+ viewMode,
5139
+ strip,
5140
+ slowResponseThresholdSeconds,
5141
+ timeDisplayFormat,
5142
+ cacheTrend: cacheTrends?.get(log.id) ?? null,
5143
+ showClientMetadata: showLogClientMetadata,
5144
+ onCompareWithPrevious: comparisonPredecessors.has(log.id) ? onCompareWithPrevious : void 0
5145
+ }
5146
+ ),
5147
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEvents, { events: toolEventsByLogId.get(log.id) ?? [] })
5148
+ ] })
5149
+ ] }, log.id);
5150
+ })
5631
5151
  )
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
- ] });
5152
+ }
5153
+ );
5154
+ });
5155
+ const VIRTUALIZE_TURN_THRESHOLD = 24;
5156
+ const ESTIMATED_TURN_HEIGHT = 132;
5157
+ const VIRTUAL_OVERSCAN = 8;
5158
+ function shouldVirtualizeTurnGroups(turnCount) {
5159
+ return turnCount > VIRTUALIZE_TURN_THRESHOLD;
5647
5160
  }
5648
- function computeStats(logs) {
5649
- let totalInput = 0;
5650
- let totalOutput = 0;
5161
+ function buildLogTurnIndex(turnGroups) {
5162
+ const result = /* @__PURE__ */ new Map();
5163
+ for (let index = 0; index < turnGroups.length; index++) {
5164
+ const group = turnGroups[index];
5165
+ if (group === void 0) continue;
5166
+ for (const entry of group.entries) {
5167
+ result.set(entry.log.id, index);
5168
+ }
5169
+ }
5170
+ return result;
5171
+ }
5172
+ function renderTurnGroup(turnGroup, props, pendingFocusRequest) {
5173
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
5174
+ TurnGroup,
5175
+ {
5176
+ entries: turnGroup.entries,
5177
+ viewMode: props.viewMode,
5178
+ strip: props.strip,
5179
+ slowResponseThresholdSeconds: props.slowResponseThresholdSeconds,
5180
+ timeDisplayFormat: props.timeDisplayFormat,
5181
+ cacheTrends: props.cacheTrends,
5182
+ onCompareWithPrevious: props.onCompareWithPrevious,
5183
+ comparisonPredecessors: props.comparisonPredecessors,
5184
+ turnIndex: turnGroup.turnIndex,
5185
+ pendingFocusRequest,
5186
+ showLogClientMetadata: props.showLogClientMetadata
5187
+ },
5188
+ turnGroup.turnIndex
5189
+ );
5190
+ }
5191
+ function TurnGroupList(props) {
5192
+ if (!shouldVirtualizeTurnGroups(props.turnGroups.length)) {
5193
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: props.turnGroups.map((turnGroup) => renderTurnGroup(turnGroup, props)) });
5194
+ }
5195
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(VirtualizedTurnGroupList, { ...props });
5196
+ }
5197
+ function VirtualizedTurnGroupList(props) {
5198
+ const { turnGroups } = props;
5199
+ const [pendingFocusRequest, setPendingFocusRequest] = reactExports.useState(
5200
+ null
5201
+ );
5202
+ const virtualizer = useWindowVirtualizer({
5203
+ count: turnGroups.length,
5204
+ estimateSize: () => ESTIMATED_TURN_HEIGHT,
5205
+ overscan: VIRTUAL_OVERSCAN,
5206
+ getItemKey: (index) => turnGroups[index]?.turnIndex ?? index
5207
+ });
5208
+ const turnIndexByLogId = reactExports.useMemo(() => buildLogTurnIndex(turnGroups), [turnGroups]);
5209
+ reactExports.useEffect(() => {
5210
+ const handleLogFocusRequest = (event) => {
5211
+ const request = readLogFocusRequest(event);
5212
+ if (request === null) return;
5213
+ if (request.source === "virtualizer") return;
5214
+ const turnIndex = turnIndexByLogId.get(request.logId);
5215
+ if (turnIndex === void 0) return;
5216
+ virtualizer.scrollToIndex(turnIndex, { align: "center" });
5217
+ setPendingFocusRequest((previous) => ({
5218
+ ...request,
5219
+ turnIndex,
5220
+ nonce: previous === null ? 1 : previous.nonce + 1
5221
+ }));
5222
+ };
5223
+ window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
5224
+ return () => {
5225
+ window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
5226
+ };
5227
+ }, [turnIndexByLogId, virtualizer]);
5228
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
5229
+ "div",
5230
+ {
5231
+ className: "relative w-full",
5232
+ style: { height: `${virtualizer.getTotalSize()}px` },
5233
+ "data-virtualized-turn-list": "true",
5234
+ children: virtualizer.getVirtualItems().map((virtualItem) => {
5235
+ const turnGroup = turnGroups[virtualItem.index];
5236
+ if (turnGroup === void 0) return null;
5237
+ const itemFocusRequest = pendingFocusRequest !== null && pendingFocusRequest.turnIndex === virtualItem.index ? pendingFocusRequest : void 0;
5238
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
5239
+ "div",
5240
+ {
5241
+ "data-index": virtualItem.index,
5242
+ ref: virtualizer.measureElement,
5243
+ className: "absolute left-0 top-0 w-full",
5244
+ style: { transform: `translateY(${virtualItem.start}px)` },
5245
+ children: renderTurnGroup(turnGroup, props, itemFocusRequest)
5246
+ },
5247
+ virtualItem.key
5248
+ );
5249
+ })
5250
+ }
5251
+ );
5252
+ }
5253
+ const CandidateResponseSchema = object({
5254
+ candidates: array(KnowledgeCandidateSchema)
5255
+ });
5256
+ const CandidatePromotionResponseSchema = object({
5257
+ candidate: KnowledgeCandidateSchema
5258
+ });
5259
+ const CandidatePromotionFailureResponseSchema = object({
5260
+ error: string().optional(),
5261
+ candidate: KnowledgeCandidateSchema.optional()
5262
+ });
5263
+ const CandidateUpdateResponseSchema = object({
5264
+ candidate: KnowledgeCandidateSchema
5265
+ });
5266
+ const CANDIDATE_STATUS_CLASSES = {
5267
+ draft: "border-amber-500/30 bg-amber-500/10 text-amber-500",
5268
+ promoted: "border-emerald-500/30 bg-emerald-500/10 text-emerald-500",
5269
+ failed: "border-destructive/30 bg-destructive/10 text-destructive"
5270
+ };
5271
+ function formatElapsed$1(ms) {
5272
+ if (ms === null) return "-";
5273
+ if (ms < 1e3) return `${String(ms)}ms`;
5274
+ return `${(ms / 1e3).toFixed(1)}s`;
5275
+ }
5276
+ function formatTimeRange$1(startedAt, endedAt, timeDisplayFormat) {
5277
+ if (startedAt === null || endedAt === null) return null;
5278
+ return formatTimestampRange(startedAt, endedAt, timeDisplayFormat);
5279
+ }
5280
+ function formatCandidateCount(count) {
5281
+ return `${String(count)} candidate${count === 1 ? "" : "s"}`;
5282
+ }
5283
+ function getLogAnchor(logId) {
5284
+ return `log-${String(logId)}`;
5285
+ }
5286
+ function jumpToLog(logId) {
5287
+ const anchor = getLogAnchor(logId);
5288
+ const target = document.getElementById(anchor);
5289
+ window.history.replaceState(null, "", `#${anchor}`);
5290
+ if (!(target instanceof HTMLElement)) return;
5291
+ target.scrollIntoView({ block: "center", behavior: "smooth" });
5292
+ target.focus({ preventScroll: true });
5293
+ }
5294
+ function firstFailureLog(logs) {
5651
5295
  for (const log of logs) {
5652
- if (log.inputTokens !== null) totalInput += log.inputTokens;
5653
- if (log.outputTokens !== null) totalOutput += log.outputTokens;
5296
+ if (log.responseStatus !== null && log.responseStatus >= 400) return log;
5654
5297
  }
5655
- return { totalInputTokens: totalInput, totalOutputTokens: totalOutput };
5298
+ return null;
5656
5299
  }
5657
- function collectProviders(logs) {
5658
- const buckets = /* @__PURE__ */ new Map();
5300
+ function slowestLog(logs) {
5301
+ let current = null;
5302
+ let currentElapsed = -1;
5659
5303
  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);
5304
+ if (log.elapsedMs === null) continue;
5305
+ if (log.elapsedMs > currentElapsed) {
5306
+ current = log;
5307
+ currentElapsed = log.elapsedMs;
5667
5308
  }
5668
- models.add(log.model);
5669
5309
  }
5670
- return [...buckets.entries()].map(([provider, models]) => ({
5671
- provider,
5672
- models: [...models]
5673
- }));
5310
+ return current;
5674
5311
  }
5675
- function getFirstClientPid(logs) {
5312
+ function firstToolLog(logs) {
5676
5313
  for (const log of logs) {
5677
- const pid = log.clientPid;
5678
- if (pid !== null && pid !== void 0) return pid;
5314
+ if (extractToolTraceEvents(log).length > 0) return log;
5679
5315
  }
5680
5316
  return null;
5681
5317
  }
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;
5318
+ function buildDisplayNumberByLogId$1(logs) {
5319
+ const result = /* @__PURE__ */ new Map();
5320
+ for (let index = 0; index < logs.length; index += 1) {
5321
+ const log = logs[index];
5322
+ if (log === void 0) continue;
5323
+ result.set(log.id, index + 1);
5324
+ }
5325
+ return result;
5326
+ }
5327
+ function displayLogNumber(logId, displayNumberByLogId) {
5328
+ return String(displayNumberByLogId.get(logId) ?? logId);
5329
+ }
5330
+ function buildTraceInsights(input) {
5331
+ const insights = [
5332
+ {
5333
+ kind: "session",
5334
+ title: `${String(input.summary.llmCallCount)} LLM call${input.summary.llmCallCount === 1 ? "" : "s"}`,
5335
+ detail: `Scope ${input.scopeId}`,
5336
+ logId: input.logs[0]?.id ?? null
5687
5337
  }
5338
+ ];
5339
+ const toolLog = firstToolLog(input.logs);
5340
+ if (input.summary.toolCallCount > 0 && toolLog !== null) {
5341
+ insights.push({
5342
+ kind: "tool",
5343
+ title: `${String(input.summary.toolCallCount)} tool call${input.summary.toolCallCount === 1 ? "" : "s"}`,
5344
+ detail: `First tool evidence at #${displayLogNumber(toolLog.id, input.displayNumberByLogId)}`,
5345
+ logId: toolLog.id
5346
+ });
5688
5347
  }
5689
- return null;
5348
+ const failure = firstFailureLog(input.logs);
5349
+ if (failure !== null) {
5350
+ insights.push({
5351
+ kind: "failure",
5352
+ title: `Failure #${String(failure.responseStatus)}`,
5353
+ detail: `First failed request is #${displayLogNumber(failure.id, input.displayNumberByLogId)}`,
5354
+ logId: failure.id
5355
+ });
5356
+ }
5357
+ const slowest = slowestLog(input.logs);
5358
+ if (slowest !== null && input.summary.maxElapsedMs !== null) {
5359
+ insights.push({
5360
+ kind: "slow",
5361
+ title: `Slowest ${formatElapsed$1(input.summary.maxElapsedMs)}`,
5362
+ detail: `Max latency observed at #${displayLogNumber(slowest.id, input.displayNumberByLogId)}`,
5363
+ logId: slowest.id
5364
+ });
5365
+ }
5366
+ if (input.candidates.length > 0) {
5367
+ const draftCount = input.candidates.filter((candidate) => candidate.status === "draft").length;
5368
+ const promotedCount = input.candidates.filter(
5369
+ (candidate) => candidate.status === "promoted"
5370
+ ).length;
5371
+ insights.push({
5372
+ kind: "candidate",
5373
+ title: `${String(input.candidates.length)} memory candidate${input.candidates.length === 1 ? "" : "s"}`,
5374
+ detail: `${String(draftCount)} draft / ${String(promotedCount)} promoted`,
5375
+ logId: input.candidates[0]?.logIds[0] ?? null
5376
+ });
5377
+ }
5378
+ return insights;
5690
5379
  }
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);
5380
+ function insightIcon(kind) {
5381
+ switch (kind) {
5382
+ case "session":
5383
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3.5 text-blue-400" });
5384
+ case "tool":
5385
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-sky-400/70" });
5386
+ case "slow":
5387
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3.5 text-amber-400" });
5388
+ case "failure":
5389
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3.5 text-destructive" });
5390
+ case "candidate":
5391
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5 text-emerald-400" });
5392
+ }
5393
+ }
5394
+ function TraceInsights({ insights }) {
5395
+ if (insights.length === 0) return null;
5396
+ 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(
5397
+ "button",
5398
+ {
5399
+ type: "button",
5400
+ className: cn(
5401
+ "flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs",
5402
+ "text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground",
5403
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
5404
+ ),
5405
+ onClick: () => {
5406
+ if (insight.logId !== null) jumpToLog(insight.logId);
5407
+ },
5408
+ disabled: insight.logId === null,
5409
+ children: [
5410
+ insightIcon(insight.kind),
5411
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0", children: [
5412
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate font-medium text-foreground/90", children: insight.title }),
5413
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate font-mono text-[10px]", children: insight.detail })
5414
+ ] })
5415
+ ]
5717
5416
  },
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: [
5417
+ `${insight.kind}-${insight.title}`
5418
+ )) });
5419
+ }
5420
+ function candidateStatusLabel(status) {
5421
+ switch (status) {
5422
+ case "draft":
5423
+ return "Draft";
5424
+ case "promoted":
5425
+ return "Promoted";
5426
+ case "failed":
5427
+ return "Failed";
5428
+ }
5429
+ }
5430
+ function candidatePromoteLabel(candidate, promoting) {
5431
+ if (promoting) return "Promoting";
5432
+ switch (candidate.status) {
5433
+ case "draft":
5434
+ return "Promote";
5435
+ case "failed":
5436
+ return "Retry";
5437
+ case "promoted":
5438
+ return "Promoted";
5439
+ }
5440
+ }
5441
+ function redactionLabel(candidate) {
5442
+ if (!candidate.redaction.redacted) return "No sensitive pattern matched";
5443
+ return `Redacted ${candidate.redaction.patterns.join(", ")}`;
5444
+ }
5445
+ function previewText(value, maxLength) {
5446
+ const normalized = value.replace(/\s+/g, " ").trim();
5447
+ if (normalized.length <= maxLength) return normalized;
5448
+ return `${normalized.slice(0, maxLength - 3)}...`;
5449
+ }
5450
+ function logRangeLabel(logIds, displayNumberByLogId) {
5451
+ const first = logIds[0];
5452
+ const last = logIds[logIds.length - 1];
5453
+ if (first === void 0) return "No evidence logs";
5454
+ const firstLabel = displayLogNumber(first, displayNumberByLogId);
5455
+ if (last === void 0 || first === last) return `#${firstLabel}`;
5456
+ return `#${firstLabel}-#${displayLogNumber(last, displayNumberByLogId)}`;
5457
+ }
5458
+ function tagsToText(tags) {
5459
+ return tags.join(", ");
5460
+ }
5461
+ function textToTags(value) {
5462
+ return value.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0);
5463
+ }
5464
+ function CandidateItem({
5465
+ candidate,
5466
+ isPromoting,
5467
+ isUpdating,
5468
+ onPromoteCandidate,
5469
+ onUpdateCandidate,
5470
+ displayNumberByLogId
5471
+ }) {
5472
+ const [editing, setEditing] = reactExports.useState(false);
5473
+ const [draftType, setDraftType] = reactExports.useState(candidate.type);
5474
+ const [draftTitle, setDraftTitle] = reactExports.useState(candidate.title);
5475
+ const [draftContent, setDraftContent] = reactExports.useState(candidate.content);
5476
+ const [draftTags, setDraftTags] = reactExports.useState(tagsToText(candidate.tags));
5477
+ const [localError, setLocalError] = reactExports.useState(null);
5478
+ const canPromote = candidate.status !== "promoted";
5479
+ const canEdit = candidate.status !== "promoted";
5480
+ const resetDraft = reactExports.useCallback(() => {
5481
+ setDraftType(candidate.type);
5482
+ setDraftTitle(candidate.title);
5483
+ setDraftContent(candidate.content);
5484
+ setDraftTags(tagsToText(candidate.tags));
5485
+ setLocalError(null);
5486
+ }, [candidate.content, candidate.tags, candidate.title, candidate.type]);
5487
+ const saveDraft = reactExports.useCallback(async () => {
5488
+ const title = draftTitle.trim();
5489
+ const content = draftContent.trim();
5490
+ const tags = textToTags(draftTags);
5491
+ if (title.length === 0) {
5492
+ setLocalError("Title is required.");
5493
+ return;
5494
+ }
5495
+ if (content.length === 0) {
5496
+ setLocalError("Content is required.");
5497
+ return;
5498
+ }
5499
+ if (tags.length === 0) {
5500
+ setLocalError("At least one tag is required.");
5501
+ return;
5502
+ }
5503
+ setLocalError(null);
5504
+ const saved = await onUpdateCandidate(candidate.id, {
5505
+ type: draftType,
5506
+ title,
5507
+ content,
5508
+ tags
5509
+ });
5510
+ if (saved) setEditing(false);
5511
+ }, [candidate.id, draftContent, draftTags, draftTitle, draftType, onUpdateCandidate]);
5512
+ if (editing) {
5513
+ 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: [
5514
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
5515
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5516
+ "select",
5517
+ {
5518
+ value: draftType,
5519
+ disabled: isUpdating,
5520
+ onChange: (event) => {
5521
+ const parsed = KnowledgeCandidateSchema.shape.type.safeParse(
5522
+ event.currentTarget.value
5523
+ );
5524
+ if (parsed.success) setDraftType(parsed.data);
5525
+ },
5526
+ className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground h-7 rounded-md px-2 text-xs",
5527
+ "aria-label": "Candidate type",
5528
+ children: [
5529
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "episode", children: "episode" }),
5530
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "procedure", children: "procedure" }),
5531
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "preference", children: "preference" }),
5532
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "project-fact", children: "project-fact" })
5533
+ ]
5534
+ }
5535
+ ),
5536
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5537
+ "input",
5538
+ {
5539
+ value: draftTitle,
5540
+ disabled: isUpdating,
5541
+ onChange: (event) => setDraftTitle(event.currentTarget.value),
5542
+ 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",
5543
+ "aria-label": "Candidate title"
5544
+ }
5545
+ )
5546
+ ] }),
5768
5547
  /* @__PURE__ */ jsxRuntimeExports.jsx(
5769
- AgentTraceSummary,
5548
+ "textarea",
5770
5549
  {
5771
- logs: group.logs,
5772
- scopeId: group.conversationId,
5773
- slowResponseThresholdSeconds,
5774
- showRollupMetrics: false,
5775
- timeDisplayFormat
5550
+ value: draftContent,
5551
+ disabled: isUpdating,
5552
+ onChange: (event) => setDraftContent(event.currentTarget.value),
5553
+ 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",
5554
+ "aria-label": "Candidate content"
5776
5555
  }
5777
5556
  ),
5778
5557
  /* @__PURE__ */ jsxRuntimeExports.jsx(
5779
- TurnGroupList,
5558
+ "input",
5780
5559
  {
5781
- turnGroups,
5782
- viewMode,
5783
- strip,
5784
- slowResponseThresholdSeconds,
5785
- timeDisplayFormat,
5786
- cacheTrends,
5787
- onCompareWithPrevious,
5788
- comparisonPredecessors,
5789
- showLogClientMetadata
5560
+ value: draftTags,
5561
+ disabled: isUpdating,
5562
+ onChange: (event) => setDraftTags(event.currentTarget.value),
5563
+ className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground h-7 rounded-md px-2 text-xs",
5564
+ "aria-label": "Candidate tags"
5565
+ }
5566
+ ),
5567
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
5568
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground", children: [
5569
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "size-3" }),
5570
+ "Saving reruns Inspector redaction before promotion."
5571
+ ] }),
5572
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
5573
+ localError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-destructive", children: localError }),
5574
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5575
+ Button,
5576
+ {
5577
+ type: "button",
5578
+ variant: "ghost",
5579
+ size: "sm",
5580
+ className: "h-7 gap-1.5 px-2 text-xs",
5581
+ disabled: isUpdating,
5582
+ onClick: () => {
5583
+ resetDraft();
5584
+ setEditing(false);
5585
+ },
5586
+ children: [
5587
+ /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3.5" }),
5588
+ "Cancel"
5589
+ ]
5590
+ }
5591
+ ),
5592
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5593
+ Button,
5594
+ {
5595
+ type: "button",
5596
+ variant: "outline",
5597
+ size: "sm",
5598
+ className: "h-7 gap-1.5 px-2 text-xs",
5599
+ disabled: isUpdating,
5600
+ onClick: () => {
5601
+ void saveDraft();
5602
+ },
5603
+ children: [
5604
+ isUpdating ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { className: "size-3.5" }),
5605
+ "Save"
5606
+ ]
5607
+ }
5608
+ )
5609
+ ] })
5610
+ ] }) });
5611
+ }
5612
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border/80 bg-background/60 px-2.5 py-2", children: [
5613
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
5614
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-5 px-1.5 text-[10px] font-mono", children: candidate.type }),
5615
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", title: candidate.title, children: candidate.title }),
5616
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5617
+ Badge,
5618
+ {
5619
+ variant: "outline",
5620
+ className: cn(
5621
+ "h-5 shrink-0 px-1.5 text-[10px] font-mono",
5622
+ CANDIDATE_STATUS_CLASSES[candidate.status]
5623
+ ),
5624
+ children: candidateStatusLabel(candidate.status)
5790
5625
  }
5791
5626
  )
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" })
5627
+ ] }),
5628
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5629
+ "p",
5630
+ {
5631
+ className: "mt-1 text-[11px] leading-relaxed text-muted-foreground",
5632
+ title: candidate.content,
5633
+ children: previewText(candidate.content, 360)
5634
+ }
5635
+ ),
5636
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-muted-foreground", children: [
5637
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [
5638
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FileSearch, { className: "size-3" }),
5639
+ logRangeLabel(candidate.logIds, displayNumberByLogId)
5879
5640
  ] }),
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" })
5641
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [
5642
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "size-3" }),
5643
+ redactionLabel(candidate)
5897
5644
  ] }),
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" })
5645
+ candidate.status === "promoted" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-emerald-500", children: [
5646
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "size-3" }),
5647
+ candidate.openClawMemoryId ?? "OpenClaw"
5901
5648
  ] }),
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" })
5649
+ candidate.status === "failed" && candidate.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-destructive", children: [
5650
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3" }),
5651
+ candidate.error
5906
5652
  ] })
5907
- }
5908
- );
5653
+ ] }),
5654
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1.5", children: [
5655
+ candidate.logIds.map((logId) => {
5656
+ const displayNumber = displayLogNumber(logId, displayNumberByLogId);
5657
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
5658
+ "a",
5659
+ {
5660
+ href: `#${getLogAnchor(logId)}`,
5661
+ onClick: (event) => {
5662
+ event.preventDefault();
5663
+ jumpToLog(logId);
5664
+ },
5665
+ 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",
5666
+ "aria-label": `Jump to evidence log ${displayNumber}`,
5667
+ title: `Log ID ${String(logId)}`,
5668
+ children: [
5669
+ "#",
5670
+ displayNumber
5671
+ ]
5672
+ },
5673
+ logId
5674
+ );
5675
+ }),
5676
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
5677
+ canEdit && /* @__PURE__ */ jsxRuntimeExports.jsxs(
5678
+ Button,
5679
+ {
5680
+ type: "button",
5681
+ variant: "ghost",
5682
+ size: "sm",
5683
+ className: "h-6 gap-1.5 px-2 text-[10px]",
5684
+ disabled: isUpdating || isPromoting,
5685
+ onClick: () => setEditing(true),
5686
+ children: [
5687
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
5688
+ "Review"
5689
+ ]
5690
+ }
5691
+ ),
5692
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5693
+ Button,
5694
+ {
5695
+ type: "button",
5696
+ variant: canPromote ? "outline" : "ghost",
5697
+ size: "sm",
5698
+ className: "h-6 gap-1.5 px-2 text-[10px]",
5699
+ onClick: () => onPromoteCandidate(candidate.id),
5700
+ disabled: !canPromote || isPromoting || isUpdating,
5701
+ children: [
5702
+ 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" }),
5703
+ candidatePromoteLabel(candidate, isPromoting)
5704
+ ]
5705
+ }
5706
+ )
5707
+ ] })
5708
+ ] });
5909
5709
  }
5910
- function Crab3({ className }) {
5911
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
5912
- SvgShell,
5710
+ function CandidateList({
5711
+ candidates,
5712
+ promotingCandidateIds,
5713
+ updatingCandidateIds,
5714
+ onPromoteCandidate,
5715
+ onUpdateCandidate,
5716
+ displayNumberByLogId
5717
+ }) {
5718
+ if (candidates.length === 0) return null;
5719
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1.5", children: candidates.map((candidate) => /* @__PURE__ */ jsxRuntimeExports.jsx(
5720
+ CandidateItem,
5913
5721
  {
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
- }
5722
+ candidate,
5723
+ isPromoting: promotingCandidateIds.has(candidate.id),
5724
+ isUpdating: updatingCandidateIds.has(candidate.id),
5725
+ onPromoteCandidate,
5726
+ onUpdateCandidate,
5727
+ displayNumberByLogId
5728
+ },
5729
+ candidate.id
5730
+ )) });
5731
+ }
5732
+ function AgentTraceSummary({
5733
+ logs,
5734
+ scopeId,
5735
+ slowResponseThresholdSeconds,
5736
+ showRollupMetrics,
5737
+ timeDisplayFormat
5738
+ }) {
5739
+ const [candidates, setCandidates] = reactExports.useState([]);
5740
+ const [candidatesExpanded, setCandidatesExpanded] = reactExports.useState(true);
5741
+ const [candidateState, setCandidateState] = reactExports.useState({
5742
+ status: "idle",
5743
+ error: null
5744
+ });
5745
+ const [promotingCandidateIds, setPromotingCandidateIds] = reactExports.useState(
5746
+ () => /* @__PURE__ */ new Set()
5747
+ );
5748
+ const [updatingCandidateIds, setUpdatingCandidateIds] = reactExports.useState(
5749
+ () => /* @__PURE__ */ new Set()
5750
+ );
5751
+ const hasCandidates = candidates.length > 0;
5752
+ const summary = reactExports.useMemo(
5753
+ () => buildTraceSummary(logs, slowResponseThresholdSeconds, candidates.length),
5754
+ [candidates.length, logs, slowResponseThresholdSeconds]
5755
+ );
5756
+ const displayNumberByLogId = reactExports.useMemo(() => buildDisplayNumberByLogId$1(logs), [logs]);
5757
+ const traceInsights = reactExports.useMemo(
5758
+ () => buildTraceInsights({ logs, scopeId, summary, candidates, displayNumberByLogId }),
5759
+ [candidates, displayNumberByLogId, logs, scopeId, summary]
5760
+ );
5761
+ const showElapsedSummary = showRollupMetrics || summary.maxElapsedMs !== null;
5762
+ const timeRange = reactExports.useMemo(
5763
+ () => formatTimeRange$1(summary.startedAt, summary.endedAt, timeDisplayFormat),
5764
+ [summary.endedAt, summary.startedAt, timeDisplayFormat]
5765
+ );
5766
+ const createCandidates = reactExports.useCallback(() => {
5767
+ if (logs.length === 0 || candidateState.status === "loading") return;
5768
+ setCandidateState({ status: "loading", error: null });
5769
+ void (async () => {
5770
+ try {
5771
+ const response = await fetch(
5772
+ `/api/knowledge/sessions/${encodeURIComponent(scopeId)}/candidates`,
5773
+ { method: "POST" }
5774
+ );
5775
+ if (!response.ok) {
5776
+ const message = await readApiError(
5777
+ response,
5778
+ `Candidate generation failed with ${String(response.status)}`
5779
+ );
5780
+ setCandidateState({ status: "failed", error: message });
5781
+ return;
5782
+ }
5783
+ const parsed = await parseJsonResponse$1(response, CandidateResponseSchema);
5784
+ setCandidates(parsed.candidates);
5785
+ setCandidatesExpanded(parsed.candidates.length > 0);
5786
+ setCandidateState(
5787
+ parsed.candidates.length > 0 ? { status: "ready", error: null } : { status: "failed", error: "No candidate was generated for this trace scope." }
5788
+ );
5789
+ } catch (error) {
5790
+ setCandidateState({
5791
+ status: "failed",
5792
+ error: error instanceof Error ? error.message : "Candidate response was invalid"
5793
+ });
5794
+ }
5795
+ })();
5796
+ }, [candidateState.status, logs.length, scopeId]);
5797
+ const promoteCandidate = reactExports.useCallback((candidateId) => {
5798
+ setPromotingCandidateIds((current) => {
5799
+ if (current.has(candidateId)) return current;
5800
+ const next = new Set(current);
5801
+ next.add(candidateId);
5802
+ return next;
5803
+ });
5804
+ void (async () => {
5805
+ try {
5806
+ const response = await fetch(
5807
+ `/api/knowledge/candidates/${encodeURIComponent(candidateId)}/promote`,
5808
+ { method: "POST" }
5809
+ );
5810
+ const raw = await response.json().catch(() => null);
5811
+ const parsed = CandidatePromotionResponseSchema.safeParse(raw);
5812
+ if (response.ok && parsed.success) {
5813
+ setCandidates(
5814
+ (current) => current.map(
5815
+ (candidate) => candidate.id === parsed.data.candidate.id ? parsed.data.candidate : candidate
5816
+ )
5817
+ );
5818
+ setCandidateState({ status: "ready", error: null });
5819
+ return;
5820
+ }
5821
+ const failure = CandidatePromotionFailureResponseSchema.safeParse(raw);
5822
+ if (failure.success && failure.data.candidate !== void 0) {
5823
+ setCandidates(
5824
+ (current) => current.map(
5825
+ (candidate) => candidate.id === failure.data.candidate?.id ? failure.data.candidate : candidate
5826
+ )
5827
+ );
5828
+ }
5829
+ setCandidateState({
5830
+ status: "failed",
5831
+ error: failure.success ? failure.data.error ?? "Candidate promotion failed" : `Candidate promotion failed with ${String(response.status)}`
5832
+ });
5833
+ } catch (error) {
5834
+ setCandidateState({
5835
+ status: "failed",
5836
+ error: error instanceof Error ? error.message : "Candidate promotion failed"
5837
+ });
5838
+ } finally {
5839
+ setPromotingCandidateIds((current) => {
5840
+ const next = new Set(current);
5841
+ next.delete(candidateId);
5842
+ return next;
5843
+ });
5844
+ }
5845
+ })();
5846
+ }, []);
5847
+ const updateCandidate = reactExports.useCallback(
5848
+ async (candidateId, update) => {
5849
+ setUpdatingCandidateIds((current) => {
5850
+ if (current.has(candidateId)) return current;
5851
+ const next = new Set(current);
5852
+ next.add(candidateId);
5853
+ return next;
5854
+ });
5855
+ try {
5856
+ const response = await fetch(
5857
+ `/api/knowledge/candidates/${encodeURIComponent(candidateId)}`,
5858
+ {
5859
+ method: "PATCH",
5860
+ headers: { "Content-Type": "application/json" },
5861
+ body: JSON.stringify(update)
5862
+ }
5863
+ );
5864
+ if (!response.ok) {
5865
+ const message = await readApiError(
5866
+ response,
5867
+ `Candidate update failed with ${String(response.status)}`
5868
+ );
5869
+ setCandidateState({ status: "failed", error: message });
5870
+ return false;
5871
+ }
5872
+ const parsed = await parseJsonResponse$1(response, CandidateUpdateResponseSchema);
5873
+ setCandidates(
5874
+ (current) => current.map(
5875
+ (candidate) => candidate.id === parsed.candidate.id ? parsed.candidate : candidate
5876
+ )
5877
+ );
5878
+ setCandidateState({ status: "ready", error: null });
5879
+ return true;
5880
+ } catch (error) {
5881
+ setCandidateState({
5882
+ status: "failed",
5883
+ error: error instanceof Error ? error.message : "Candidate update failed"
5884
+ });
5885
+ return false;
5886
+ } finally {
5887
+ setUpdatingCandidateIds((current) => {
5888
+ const next = new Set(current);
5889
+ next.delete(candidateId);
5890
+ return next;
5891
+ });
5892
+ }
5893
+ },
5894
+ []
5930
5895
  );
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" })
5896
+ if (logs.length === 0) return null;
5897
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "border border-border bg-card shadow-sm mb-2 rounded-[8px] px-3 py-2", children: [
5898
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs", children: [
5899
+ showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 font-semibold text-foreground", children: [
5900
+ /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3.5 text-blue-400" }),
5901
+ summary.llmCallCount,
5902
+ " LLM"
5945
5903
  ] }),
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" })
5904
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5905
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-sky-400/70" }),
5906
+ summary.toolCallCount,
5907
+ " tools"
5951
5908
  ] }),
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" })
5909
+ showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5910
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-emerald-400" }),
5911
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
5912
+ formatTokens(summary.totalInputTokens),
5913
+ " / ",
5914
+ formatTokens(summary.totalOutputTokens)
5915
+ ] })
5968
5916
  ] }),
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" })
5917
+ (summary.totalCacheCreationInputTokens > 0 || summary.totalCacheReadInputTokens > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5918
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-purple-400" }),
5919
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
5920
+ "KV Cache +",
5921
+ formatTokens(summary.totalCacheCreationInputTokens),
5922
+ " / ~",
5923
+ formatTokens(summary.totalCacheReadInputTokens)
5924
+ ] })
5972
5925
  ] }),
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" })
5926
+ showElapsedSummary && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
5927
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3.5" }),
5928
+ showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: formatElapsed$1(summary.totalElapsedMs) }),
5929
+ summary.maxElapsedMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono text-muted-foreground/70", children: [
5930
+ "max ",
5931
+ formatElapsed$1(summary.maxElapsedMs)
5932
+ ] })
5990
5933
  ] }),
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" })
5934
+ showRollupMetrics && timeRange !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-muted-foreground/70", children: timeRange }),
5935
+ (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: [
5936
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3.5" }),
5937
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
5938
+ summary.failedCallCount,
5939
+ " fail / ",
5940
+ summary.pendingCallCount,
5941
+ " pending /",
5942
+ " ",
5943
+ summary.slowCallCount,
5944
+ " slow"
5945
+ ] })
5994
5946
  ] }),
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
- );
5947
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
5948
+ hasCandidates && /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-6 px-2 text-[10px] font-mono", children: formatCandidateCount(summary.knowledgeCandidateCount) }),
5949
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
5950
+ Button,
5951
+ {
5952
+ type: "button",
5953
+ variant: "outline",
5954
+ size: "sm",
5955
+ className: "h-7 gap-1.5 px-2 text-xs",
5956
+ onClick: createCandidates,
5957
+ disabled: candidateState.status === "loading",
5958
+ children: [
5959
+ candidateState.status === "loading" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5" }),
5960
+ "Candidate"
5961
+ ]
5962
+ }
5963
+ ),
5964
+ hasCandidates && /* @__PURE__ */ jsxRuntimeExports.jsx(
5965
+ Button,
5966
+ {
5967
+ type: "button",
5968
+ variant: "ghost",
5969
+ size: "icon",
5970
+ className: "size-7 text-muted-foreground",
5971
+ onClick: () => setCandidatesExpanded((value) => !value),
5972
+ "aria-expanded": candidatesExpanded,
5973
+ "aria-label": candidatesExpanded ? "Collapse memory candidates" : "Expand memory candidates",
5974
+ children: candidatesExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3.5" })
5975
+ }
5976
+ )
5977
+ ] }),
5978
+ candidateState.status === "failed" && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-xs text-destructive", children: candidateState.error }),
5979
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TraceInsights, { insights: traceInsights }),
5980
+ candidatesExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx(
5981
+ CandidateList,
5982
+ {
5983
+ candidates,
5984
+ promotingCandidateIds,
5985
+ updatingCandidateIds,
5986
+ onPromoteCandidate: promoteCandidate,
5987
+ onUpdateCandidate: updateCandidate,
5988
+ displayNumberByLogId
5989
+ }
5990
+ )
5991
+ ] });
6002
5992
  }
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
- );
5993
+ function computeStats(logs) {
5994
+ let totalInput = 0;
5995
+ let totalOutput = 0;
5996
+ for (const log of logs) {
5997
+ if (log.inputTokens !== null) totalInput += log.inputTokens;
5998
+ if (log.outputTokens !== null) totalOutput += log.outputTokens;
5999
+ }
6000
+ return { totalInputTokens: totalInput, totalOutputTokens: totalOutput };
6024
6001
  }
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
- ] })
6002
+ function collectProviders(logs) {
6003
+ const buckets = /* @__PURE__ */ new Map();
6004
+ for (const log of logs) {
6005
+ if (log.model === null) continue;
6006
+ const provider = detectProvider(log.model);
6007
+ if (provider === "unknown") continue;
6008
+ let models = buckets.get(provider);
6009
+ if (models === void 0) {
6010
+ models = /* @__PURE__ */ new Set();
6011
+ buckets.set(provider, models);
6047
6012
  }
6048
- );
6013
+ models.add(log.model);
6014
+ }
6015
+ return [...buckets.entries()].map(([provider, models]) => ({
6016
+ provider,
6017
+ models: [...models]
6018
+ }));
6049
6019
  }
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
- );
6020
+ function getFirstClientPid(logs) {
6021
+ for (const log of logs) {
6022
+ const pid = log.clientPid;
6023
+ if (pid !== null && pid !== void 0) return pid;
6024
+ }
6025
+ return null;
6072
6026
  }
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
- ] })
6027
+ function getFirstClientProjectFolder(logs) {
6028
+ for (const log of logs) {
6029
+ const projectFolder = log.clientProjectFolder;
6030
+ if (projectFolder !== null && projectFolder !== void 0 && projectFolder !== "") {
6031
+ return projectFolder;
6095
6032
  }
6096
- );
6033
+ }
6034
+ return null;
6097
6035
  }
6098
- function Crab11({ className }) {
6099
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
6100
- SvgShell,
6036
+ const ConversationGroup = reactExports.memo(function({
6037
+ group,
6038
+ viewMode = "simple",
6039
+ strip,
6040
+ slowResponseThresholdSeconds,
6041
+ cacheTrends,
6042
+ onCompareWithPrevious,
6043
+ comparisonPredecessors,
6044
+ onClearGroup,
6045
+ showClientIdentity = true,
6046
+ showProcessMetadata = true,
6047
+ showLogClientMetadata = true,
6048
+ defaultExpanded = false,
6049
+ timeDisplayFormat,
6050
+ expanded: controlledExpanded,
6051
+ onExpandedChange
6052
+ }) {
6053
+ const [internalExpanded, setInternalExpanded] = reactExports.useState(defaultExpanded);
6054
+ const expanded = controlledExpanded ?? internalExpanded;
6055
+ const setExpanded = reactExports.useCallback(
6056
+ (nextExpanded) => {
6057
+ if (onExpandedChange !== void 0) {
6058
+ onExpandedChange(nextExpanded);
6059
+ return;
6060
+ }
6061
+ setInternalExpanded(nextExpanded);
6062
+ },
6063
+ [onExpandedChange]
6064
+ );
6065
+ reactExports.useEffect(() => {
6066
+ if (!defaultExpanded || controlledExpanded !== void 0) return;
6067
+ setInternalExpanded(true);
6068
+ }, [controlledExpanded, defaultExpanded]);
6069
+ const stats = reactExports.useMemo(() => computeStats(group.logs), [group.logs]);
6070
+ const providers = reactExports.useMemo(() => collectProviders(group.logs), [group.logs]);
6071
+ const startTime = group.logs[0]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
6072
+ const endTime = group.logs[group.logs.length - 1]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
6073
+ const isLoading = group.logs.some((log) => log.responseStatus === null);
6074
+ const clientPid = reactExports.useMemo(() => getFirstClientPid(group.logs), [group.logs]);
6075
+ const clientProjectFolder = reactExports.useMemo(() => getFirstClientProjectFolder(group.logs), [group.logs]);
6076
+ const turnGroups = reactExports.useMemo(() => buildTurnGroups(group.logs), [group.logs]);
6077
+ reactExports.useEffect(() => {
6078
+ const handleLogFocusRequest = (event) => {
6079
+ const request = readLogFocusRequest(event);
6080
+ if (request === null) return;
6081
+ if (!group.logs.some((log) => log.id === request.logId)) return;
6082
+ setExpanded(true);
6083
+ };
6084
+ window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
6085
+ return () => {
6086
+ window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
6087
+ };
6088
+ }, [group.logs, setExpanded]);
6089
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2", children: [
6090
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6091
+ ConversationHeader,
6092
+ {
6093
+ conversationId: group.conversationId,
6094
+ startTime,
6095
+ endTime,
6096
+ totalCalls: group.logs.length,
6097
+ totalInputTokens: stats.totalInputTokens,
6098
+ totalOutputTokens: stats.totalOutputTokens,
6099
+ expanded,
6100
+ onToggle: () => setExpanded(!expanded),
6101
+ providers,
6102
+ isLoading,
6103
+ userAgent: group.logs[0]?.userAgent ?? null,
6104
+ clientPid,
6105
+ clientProjectFolder,
6106
+ showClientIdentity,
6107
+ showProcessMetadata,
6108
+ timeDisplayFormat,
6109
+ onClear: () => onClearGroup(group.logs.map((l) => l.id))
6110
+ }
6111
+ ),
6112
+ expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
6113
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6114
+ AgentTraceSummary,
6115
+ {
6116
+ logs: group.logs,
6117
+ scopeId: group.conversationId,
6118
+ slowResponseThresholdSeconds,
6119
+ showRollupMetrics: false,
6120
+ timeDisplayFormat
6121
+ }
6122
+ ),
6123
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6124
+ TurnGroupList,
6125
+ {
6126
+ turnGroups,
6127
+ viewMode,
6128
+ strip,
6129
+ slowResponseThresholdSeconds,
6130
+ timeDisplayFormat,
6131
+ cacheTrends,
6132
+ onCompareWithPrevious,
6133
+ comparisonPredecessors,
6134
+ showLogClientMetadata
6135
+ }
6136
+ )
6137
+ ] })
6138
+ ] });
6139
+ });
6140
+ function CrabLogo({ className }) {
6141
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
6142
+ "svg",
6101
6143
  {
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: [
6144
+ viewBox: "0 0 24 24",
6145
+ fill: "none",
6146
+ stroke: "currentColor",
6147
+ strokeWidth: "1.5",
6148
+ strokeLinecap: "round",
6149
+ strokeLinejoin: "round",
6150
+ "aria-hidden": "true",
6151
+ className: cn("inline-block size-5", className),
6152
+ children: [
6153
+ /* @__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
6154
  /* @__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
6155
  /* @__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
- ] })
6156
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
6157
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" }),
6158
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
6159
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
6160
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
6161
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
6162
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
6163
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
6164
+ ]
6146
6165
  }
6147
6166
  );
6148
6167
  }
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
6168
  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
6169
  function McpLogo({ className }) {
6165
6170
  return /* @__PURE__ */ jsxRuntimeExports.jsx(