@tonyclaw/agent-inspector 2.0.4 → 2.0.6

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 (61) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-BCH_fsLm.js → CompareDrawer-DDmqSAfl.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-Cxpdziwd.js +101 -0
  4. package/.output/public/assets/ReplayDialog-Bt5DGzlh.js +1 -0
  5. package/.output/public/assets/RequestAnatomy-BxX3_N9S.js +1 -0
  6. package/.output/public/assets/ResponseView-Bl_5S9gZ.js +1 -0
  7. package/.output/public/assets/StreamingChunkSequence-RJMwNf6F.js +1 -0
  8. package/.output/public/assets/_sessionId-b4isaoDp.js +1 -0
  9. package/.output/public/assets/index-BZ4x5UI6.js +1 -0
  10. package/.output/public/assets/{index-CobXD0yH.css → index-C624DUk9.css} +1 -1
  11. package/.output/public/assets/{json-viewer-BrzjD7qI.js → json-viewer-CRL_gWEZ.js} +1 -1
  12. package/.output/public/assets/{main-mgxeUdZQ.js → main-CKnTJ4-O.js} +6 -6
  13. package/.output/server/_libs/lucide-react.mjs +181 -114
  14. package/.output/server/{_sessionId-C4xsxIWm.mjs → _sessionId-B-x9fRY3.mjs} +3 -3
  15. package/.output/server/_ssr/{CompareDrawer-DuWEpqQ7.mjs → CompareDrawer-BQVNsAY2.mjs} +6 -6
  16. package/.output/server/_ssr/{ProxyViewerContainer-Cckz5qKu.mjs → ProxyViewerContainer-CYm2Dw19.mjs} +766 -122
  17. package/.output/server/_ssr/{ReplayDialog-BDRcr8E5.mjs → ReplayDialog-CaMQBc79.mjs} +240 -14
  18. package/.output/server/_ssr/{RequestAnatomy-BoO2_Ij0.mjs → RequestAnatomy--P5arRH2.mjs} +236 -66
  19. package/.output/server/_ssr/{ResponseView-DZiPBxvO.mjs → ResponseView-RtFwNvgD.mjs} +8 -8
  20. package/.output/server/_ssr/{StreamingChunkSequence-D-be7KEL.mjs → StreamingChunkSequence-B5HPkzab.mjs} +3 -3
  21. package/.output/server/_ssr/{index-5RImHKfu.mjs → index-CZIKZU43.mjs} +2 -2
  22. package/.output/server/_ssr/index.mjs +2 -2
  23. package/.output/server/_ssr/{json-viewer-aJhb93ZK.mjs → json-viewer-d4obyRaA.mjs} +3 -3
  24. package/.output/server/_ssr/{router-Dgkv5nKP.mjs → router-DGPt3MUc.mjs} +145 -71
  25. package/.output/server/_tanstack-start-manifest_v-BzH4pNaI.mjs +4 -0
  26. package/.output/server/index.mjs +64 -64
  27. package/package.json +1 -1
  28. package/src/components/OnboardingBanner.tsx +11 -19
  29. package/src/components/ProxyViewer.tsx +1 -1
  30. package/src/components/providers/ProviderCard.tsx +6 -20
  31. package/src/components/providers/SettingsDialog.tsx +95 -2
  32. package/src/components/proxy-viewer/AgentTraceSummary.tsx +639 -38
  33. package/src/components/proxy-viewer/CompareDrawer.tsx +4 -2
  34. package/src/components/proxy-viewer/LogEntry.tsx +4 -4
  35. package/src/components/proxy-viewer/LogEntryHeader.tsx +15 -25
  36. package/src/components/proxy-viewer/ReplayDialog.tsx +190 -8
  37. package/src/components/proxy-viewer/ResponseView.tsx +2 -2
  38. package/src/components/proxy-viewer/ToolTraceEvents.tsx +37 -16
  39. package/src/components/proxy-viewer/TurnGroup.tsx +14 -2
  40. package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +196 -45
  41. package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +92 -67
  42. package/src/components/proxy-viewer/anatomy/types.ts +15 -13
  43. package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +2 -2
  44. package/src/components/proxy-viewer/log-formats/anthropic.ts +1 -1
  45. package/src/components/proxy-viewer/log-formats/openai.ts +1 -1
  46. package/src/components/proxy-viewer/log-formats/types.ts +1 -1
  47. package/src/components/proxy-viewer/replayComparison.ts +131 -0
  48. package/src/components/proxy-viewer/useKeyboardNavigation.ts +64 -22
  49. package/src/components/proxy-viewer/viewerState.ts +14 -2
  50. package/src/components/ui/json-viewer.tsx +1 -1
  51. package/src/knowledge/candidateStore.ts +32 -1
  52. package/src/routes/api/knowledge.candidates.$candidateId.ts +50 -0
  53. package/src/routes/api/knowledge.sessions.$sessionId.candidates.ts +12 -2
  54. package/.output/public/assets/ProxyViewerContainer-D85_UANk.js +0 -101
  55. package/.output/public/assets/ReplayDialog-DTeaHHit.js +0 -1
  56. package/.output/public/assets/RequestAnatomy-DZ8grAih.js +0 -1
  57. package/.output/public/assets/ResponseView-Cldm6RCi.js +0 -1
  58. package/.output/public/assets/StreamingChunkSequence-3x4p-yT7.js +0 -1
  59. package/.output/public/assets/_sessionId-YqWFBu6d.js +0 -1
  60. package/.output/public/assets/index-BIw2H6jO.js +0 -1
  61. package/.output/server/_tanstack-start-manifest_v-B8rrWXjr.mjs +0 -4
@@ -1,5 +1,5 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
- import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as DEFAULT_TIME_DISPLAY_FORMAT, b as RuntimeConfigSchema, r as requestFormatForPath, e as createPendingProviderTestResults, P as ProviderTestResultsSchema, f as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, T as TimeDisplayFormatSchema, g as getSessionPath, h as ProviderConfigSchema, K as KnowledgeCandidateSchema, s as stripClaudeCodeBillingHeader, d as safeGetOwnProperty, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, c as AnthropicRequestSchema } from "./router-Dgkv5nKP.mjs";
2
+ import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as DEFAULT_TIME_DISPLAY_FORMAT, b as RuntimeConfigSchema, r as requestFormatForPath, e as createPendingProviderTestResults, P as ProviderTestResultsSchema, f as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, T as TimeDisplayFormatSchema, g as getSessionPath, h as ProviderConfigSchema, K as KnowledgeCandidateSchema, s as stripClaudeCodeBillingHeader, d as safeGetOwnProperty, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, c as AnthropicRequestSchema } from "./router-DGPt3MUc.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";
@@ -9,7 +9,7 @@ import { R as Root, T as Trigger$2, C as Content, a as Close, b as Title, P as P
9
9
  import { d as diffJson, a as diffLines } from "../_libs/diff.mjs";
10
10
  import { u as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
11
11
  import { R as Root2, T as Trigger, I as Icon, V as Value, P as Portal, C as Content2, a as Viewport, b as Item, c as ItemIndicator, d as ItemText, S as ScrollUpButton, e as ScrollDownButton } from "../_libs/radix-ui__react-select.mjs";
12
- import { C as Check, X, P as Plus, D as Download, S as Settings, A as ArrowLeft, a as Copy, b as ChevronDown, U as Upload, c as Scan, d as CircleAlert, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, T as Trash2, W as Wrench, i as TriangleAlert, B as Brain, j as EyeOff, k as Eye, R as RotateCw, l as Pencil, m as Minus, n as CircleCheckBig, O as OctagonAlert, G as Globe, F as FileTerminal, o as Radio, p as ChevronsUp, q as ChevronsDown, r as FileDiff, H as History, s as RotateCcw, t as GitCompareArrows, u as CircleQuestionMark, v as Server, w as Gauge, x as Lock, y as Wifi, z as WifiOff, I as ArrowUp, J as ArrowDown, K as Rows3, N as Columns2 } from "../_libs/lucide-react.mjs";
12
+ import { C as Check, X, P as Plus, D as Download, S as Settings, A as ArrowLeft, a as Copy, b as ChevronDown, U as Upload, c as Scan, d as CircleAlert, T as Terminal, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, i as Trash2, W as Wrench, j as TriangleAlert, B as Brain, k as EyeOff, l as Eye, R as RotateCw, m as Pencil, n as Minus, o as CircleCheckBig, p as CircleX, q as ShieldCheck, r as RefreshCw, s as Save, F as FileSearch, t as CircleCheck, u as CloudUpload, O as OctagonAlert, G as Globe, v as FileTerminal, w as Radio, x as ChevronsUp, y as ChevronsDown, z as FileDiff, H as History, I as RotateCcw, J as GitCompareArrows, K as CircleQuestionMark, N as Server, Q as Gauge, V as Lock, Y as Wifi, _ as WifiOff, $ as ArrowUp, a0 as ArrowDown, a1 as Rows3, a2 as Columns2 } from "../_libs/lucide-react.mjs";
13
13
  import { u as union, d as object, a as array, l as literal, b as string, n as number, c as boolean, _ as _enum } from "../_libs/zod.mjs";
14
14
  import { P as Provider, R as Root3, T as Trigger$1, a as Portal$1, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
15
15
  import { R as Root2$1, L as List, T as Trigger$3, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
@@ -179,27 +179,20 @@ function OnboardingBanner() {
179
179
  "aria-label": "Onboarding tip",
180
180
  className: "mx-4 mt-2 mb-1 flex items-start gap-3 rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-sm",
181
181
  children: [
182
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 min-w-0", children: [
183
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium text-amber-600 dark:text-amber-400 mb-1", children: "Quick tour of the log tabs" }),
184
- /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "space-y-0.5 text-muted-foreground text-xs leading-relaxed", children: [
182
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
183
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-1 font-medium text-amber-600 dark:text-amber-400", children: "Agent Inspector is ready" }),
184
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "space-y-0.5 text-xs leading-relaxed text-muted-foreground", children: [
185
185
  /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
186
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Request" }),
187
- " / ",
188
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Response" }),
189
- " — structured views of what the proxy sent and received."
186
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Trace" }),
187
+ ": requests, responses, streaming chunks, tools, and timing."
190
188
  ] }),
191
189
  /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
192
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Headers" }),
193
- " request and response headers after proxy processing."
190
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Replay" }),
191
+ ": resend captured requests and compare provider behavior."
194
192
  ] }),
195
193
  /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
196
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Raw Headers" }),
197
- " / ",
198
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Raw Request" }),
199
- " /",
200
- " ",
201
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Raw Response" }),
202
- " — exact bytes from the upstream provider (visible in Full mode)."
194
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Memory" }),
195
+ ": create reviewable candidates before promotion to OpenClaw."
203
196
  ] })
204
197
  ] })
205
198
  ] }),
@@ -210,7 +203,7 @@ function OnboardingBanner() {
210
203
  onClick: () => {
211
204
  void markSeen();
212
205
  },
213
- className: "inline-flex items-center gap-1.5 text-xs h-8 px-3 rounded-md border border-amber-500/40 text-amber-700 dark:text-amber-300 hover:bg-amber-500/10 transition-colors shrink-0",
206
+ className: "inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-amber-500/40 px-3 text-xs text-amber-700 transition-colors hover:bg-amber-500/10 dark:text-amber-300",
214
207
  "aria-label": "Dismiss onboarding tip",
215
208
  children: [
216
209
  /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }),
@@ -225,7 +218,7 @@ function OnboardingBanner() {
225
218
  onClick: () => {
226
219
  void markSeen();
227
220
  },
228
- className: "text-muted-foreground hover:text-foreground transition-colors shrink-0 p-1 -m-1",
221
+ className: "-m-1 shrink-0 p-1 text-muted-foreground transition-colors hover:text-foreground",
229
222
  "aria-label": "Dismiss",
230
223
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3.5" })
231
224
  }
@@ -302,7 +295,7 @@ function getStatusCategory(status) {
302
295
  if (status >= 500) return "server_error";
303
296
  return "pending";
304
297
  }
305
- const version = "2.0.4";
298
+ const version = "2.0.6";
306
299
  const packageJson = {
307
300
  version
308
301
  };
@@ -1363,27 +1356,27 @@ function useCopyFeedback(text) {
1363
1356
  return { copied, copy };
1364
1357
  }
1365
1358
  const LazyCompareDrawer = reactExports.lazy(
1366
- () => import("./CompareDrawer-DuWEpqQ7.mjs").then((m) => ({ default: m.CompareDrawer }))
1359
+ () => import("./CompareDrawer-BQVNsAY2.mjs").then((m) => ({ default: m.CompareDrawer }))
1367
1360
  );
1368
1361
  const LazyReplayDialog = reactExports.lazy(
1369
- () => import("./ReplayDialog-BDRcr8E5.mjs").then((m) => ({ default: m.ReplayDialog }))
1362
+ () => import("./ReplayDialog-CaMQBc79.mjs").then((m) => ({ default: m.ReplayDialog }))
1370
1363
  );
1371
1364
  const LazyRequestAnatomy = reactExports.lazy(
1372
- () => import("./RequestAnatomy-BoO2_Ij0.mjs").then((m) => ({ default: m.RequestAnatomy }))
1365
+ () => import("./RequestAnatomy--P5arRH2.mjs").then((m) => ({ default: m.RequestAnatomy }))
1373
1366
  );
1374
1367
  const LazyResponseView = reactExports.lazy(
1375
- () => import("./ResponseView-DZiPBxvO.mjs").then((m) => ({ default: m.ResponseView }))
1368
+ () => import("./ResponseView-RtFwNvgD.mjs").then((m) => ({ default: m.ResponseView }))
1376
1369
  );
1377
1370
  const LazyStreamingChunkSequence = reactExports.lazy(
1378
- () => import("./StreamingChunkSequence-D-be7KEL.mjs").then((m) => ({
1371
+ () => import("./StreamingChunkSequence-B5HPkzab.mjs").then((m) => ({
1379
1372
  default: m.StreamingChunkSequence
1380
1373
  }))
1381
1374
  );
1382
1375
  const LazyJsonViewer = reactExports.lazy(
1383
- () => import("./json-viewer-aJhb93ZK.mjs").then((m) => ({ default: m.JsonViewer }))
1376
+ () => import("./json-viewer-d4obyRaA.mjs").then((m) => ({ default: m.JsonViewer }))
1384
1377
  );
1385
1378
  const LazyJsonViewerFromString = reactExports.lazy(
1386
- () => import("./json-viewer-aJhb93ZK.mjs").then((m) => ({ default: m.JsonViewerFromString }))
1379
+ () => import("./json-viewer-d4obyRaA.mjs").then((m) => ({ default: m.JsonViewerFromString }))
1387
1380
  );
1388
1381
  const HIGHLIGHT_DURATION_MS = 1200;
1389
1382
  const MAX_HIGHLIGHT_ATTEMPTS = 12;
@@ -1903,25 +1896,19 @@ const LogEntryHeader = reactExports.memo(function({
1903
1896
  )
1904
1897
  ] })
1905
1898
  ] }),
1906
- log.cacheCreationInputTokens !== null && log.cacheCreationInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1907
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
1908
- /* @__PURE__ */ jsxRuntimeExports.jsx(CacheTrendIndicator, { trend: cacheTrend?.creation ?? null }),
1909
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
1910
- "Cache +",
1911
- formatTokens(log.cacheCreationInputTokens)
1912
- ] })
1913
- ] }) }),
1914
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Tokens cached for reuse, reducing future API cost" })
1899
+ log.cacheCreationInputTokens !== null && log.cacheCreationInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
1900
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CacheTrendIndicator, { trend: cacheTrend?.creation ?? null }),
1901
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
1902
+ "KV Cache +",
1903
+ formatTokens(log.cacheCreationInputTokens)
1904
+ ] })
1915
1905
  ] }),
1916
- log.cacheReadInputTokens !== null && log.cacheReadInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1917
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
1918
- /* @__PURE__ */ jsxRuntimeExports.jsx(CacheTrendIndicator, { trend: cacheTrend?.read ?? null }),
1919
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
1920
- "Cache ~",
1921
- formatTokens(log.cacheReadInputTokens)
1922
- ] })
1923
- ] }) }),
1924
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Tokens served from cache, reducing API cost" })
1906
+ log.cacheReadInputTokens !== null && log.cacheReadInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
1907
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CacheTrendIndicator, { trend: cacheTrend?.read ?? null }),
1908
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
1909
+ "KV Cache ~",
1910
+ formatTokens(log.cacheReadInputTokens)
1911
+ ] })
1925
1912
  ] }),
1926
1913
  messageCount !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
1927
1914
  /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3" }),
@@ -2583,7 +2570,7 @@ const LogEntry = reactExports.memo(function({
2583
2570
  viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "headers", children: "Headers" }),
2584
2571
  shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw-request", children: "Raw Request" }),
2585
2572
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "request", children: "Request" }),
2586
- anatomySegments !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "anatomy", children: "Anatomy" }),
2573
+ anatomySegments !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "anatomy", children: "Context" }),
2587
2574
  viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" }),
2588
2575
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Response" })
2589
2576
  ] }),
@@ -2600,7 +2587,7 @@ const LogEntry = reactExports.memo(function({
2600
2587
  {
2601
2588
  rawBody: log.rawRequestBody,
2602
2589
  displayedBody: displayedRequestBody,
2603
- emptyLabel: "No transformation applied raw and sent request bodies are identical."
2590
+ emptyLabel: "No transformation applied; raw and sent request bodies are identical."
2604
2591
  }
2605
2592
  ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: requestJsonRef, children: displayedRequestBody === null ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No request body" }) : requestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2606
2593
  LazyJsonViewer,
@@ -2640,7 +2627,7 @@ const LogEntry = reactExports.memo(function({
2640
2627
  {
2641
2628
  rawHeaders: log.rawHeaders,
2642
2629
  headers: log.headers,
2643
- emptyLabel: "No transformation applied raw and processed headers are identical."
2630
+ emptyLabel: "No transformation applied; raw and processed headers are identical."
2644
2631
  }
2645
2632
  ) : log.headers && Object.keys(log.headers).length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 font-mono text-xs", children: Object.entries(log.headers).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
2646
2633
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-600 dark:text-blue-400 font-semibold shrink-0", children: [
@@ -2776,23 +2763,39 @@ function ThreadConnector({
2776
2763
  ) })
2777
2764
  ] });
2778
2765
  }
2779
- function ToolTraceEvents({ events }) {
2780
- if (events.length === 0) return null;
2781
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-3 mb-2 grid gap-1.5", children: events.map((event) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
2766
+ function ToolTraceEventRow({ event }) {
2767
+ const argumentCopy = useCopyFeedback(event.argumentsText);
2768
+ const canCopyArguments = event.argumentsText !== null;
2769
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
2782
2770
  "div",
2783
2771
  {
2784
- className: "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",
2772
+ 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",
2785
2773
  children: [
2786
2774
  /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 shrink-0 text-sky-400/70" }),
2787
2775
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-foreground/80", children: event.name }),
2788
2776
  event.argumentsPreview !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
2789
2777
  /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 shrink-0 text-muted-foreground/60" }),
2790
2778
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate font-mono text-muted-foreground", children: event.argumentsPreview })
2791
- ] })
2779
+ ] }),
2780
+ canCopyArguments && /* @__PURE__ */ jsxRuntimeExports.jsx(
2781
+ "button",
2782
+ {
2783
+ type: "button",
2784
+ 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",
2785
+ onClick: argumentCopy.copy,
2786
+ "aria-label": argumentCopy.copied ? "Copied tool arguments" : "Copy tool arguments",
2787
+ title: argumentCopy.copied ? "Copied tool arguments" : "Copy tool arguments",
2788
+ children: argumentCopy.copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
2789
+ }
2790
+ )
2792
2791
  ]
2793
2792
  },
2794
2793
  event.id
2795
- )) });
2794
+ );
2795
+ }
2796
+ function ToolTraceEvents({ events }) {
2797
+ if (events.length === 0) return null;
2798
+ 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)) });
2796
2799
  }
2797
2800
  const PREVIEW_LIMIT = 180;
2798
2801
  function shouldRenderConversationContent(standalone, expanded) {
@@ -2852,6 +2855,12 @@ function previewValue(value) {
2852
2855
  if (normalized.length === 0) return null;
2853
2856
  return normalized.length > PREVIEW_LIMIT ? `${normalized.slice(0, PREVIEW_LIMIT - 1)}...` : normalized;
2854
2857
  }
2858
+ function copyValue(value) {
2859
+ if (value === void 0 || value === null) return null;
2860
+ if (typeof value === "string") return value.length > 0 ? value : null;
2861
+ const raw = JSON.stringify(value, null, 2);
2862
+ return raw === void 0 || raw.length === 0 ? null : raw;
2863
+ }
2855
2864
  function extractAnthropicToolTraceEvents(log) {
2856
2865
  const parsed = parseJsonResponse(log.responseText);
2857
2866
  const content = safeGetOwnProperty(parsed, "content");
@@ -2862,13 +2871,15 @@ function extractAnthropicToolTraceEvents(log) {
2862
2871
  if (type !== "tool_use") continue;
2863
2872
  const name = safeGetOwnProperty(block, "name");
2864
2873
  if (typeof name !== "string" || name.length === 0) continue;
2874
+ const input = safeGetOwnProperty(block, "input");
2865
2875
  events.push({
2866
2876
  id: `${String(log.id)}-anthropic-tool-${String(events.length)}`,
2867
2877
  logId: log.id,
2868
2878
  index: events.length,
2869
2879
  provider: "anthropic",
2870
2880
  name,
2871
- argumentsPreview: previewValue(safeGetOwnProperty(block, "input"))
2881
+ argumentsText: copyValue(input),
2882
+ argumentsPreview: previewValue(input)
2872
2883
  });
2873
2884
  }
2874
2885
  return events;
@@ -2886,13 +2897,15 @@ function extractOpenAIToolTraceEvents(log) {
2886
2897
  const fn = safeGetOwnProperty(call, "function");
2887
2898
  const name = safeGetOwnProperty(fn, "name");
2888
2899
  if (typeof name !== "string" || name.length === 0) continue;
2900
+ const args = safeGetOwnProperty(fn, "arguments");
2889
2901
  events.push({
2890
2902
  id: `${String(log.id)}-openai-tool-${String(events.length)}`,
2891
2903
  logId: log.id,
2892
2904
  index: events.length,
2893
2905
  provider: "openai",
2894
2906
  name,
2895
- argumentsPreview: previewValue(safeGetOwnProperty(fn, "arguments"))
2907
+ argumentsText: copyValue(args),
2908
+ argumentsPreview: previewValue(args)
2896
2909
  });
2897
2910
  }
2898
2911
  }
@@ -3055,17 +3068,27 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
3055
3068
  window.cancelAnimationFrame(raf);
3056
3069
  };
3057
3070
  }, []);
3071
+ const firstLogId = entries[0]?.log.id ?? turnIndex;
3072
+ const turnLabel = `Turn ${String(turnIndex + 1)}`;
3058
3073
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
3059
3074
  "div",
3060
3075
  {
3061
3076
  ref: containerRef,
3062
- className: cn("border rounded-lg", isPending ? "border-amber-500/10" : "border-transparent"),
3077
+ tabIndex: collapsed ? void 0 : 0,
3078
+ role: collapsed ? void 0 : "group",
3079
+ "aria-label": collapsed ? void 0 : turnLabel,
3080
+ "data-nav-id": collapsed ? void 0 : `turn-${String(firstLogId)}`,
3081
+ className: cn(
3082
+ "border rounded-lg",
3083
+ isPending ? "border-amber-500/10" : "border-transparent",
3084
+ !collapsed && "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
3085
+ ),
3063
3086
  children: collapsed ? (
3064
3087
  /* ---- Collapsed: dual-crab (+ summary card for multi-log turns) ---- */
3065
3088
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
3066
3089
  "div",
3067
3090
  {
3068
- "data-nav-id": `turn-collapsed-${entries[0]?.log.id ?? turnIndex}`,
3091
+ "data-nav-id": `turn-collapsed-${String(firstLogId)}`,
3069
3092
  "data-nav-action": "expand",
3070
3093
  role: "button",
3071
3094
  tabIndex: 0,
@@ -3244,6 +3267,21 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
3244
3267
  const CandidateResponseSchema = object({
3245
3268
  candidates: array(KnowledgeCandidateSchema)
3246
3269
  });
3270
+ const CandidatePromotionResponseSchema = object({
3271
+ candidate: KnowledgeCandidateSchema
3272
+ });
3273
+ const CandidatePromotionFailureResponseSchema = object({
3274
+ error: string().optional(),
3275
+ candidate: KnowledgeCandidateSchema.optional()
3276
+ });
3277
+ const CandidateUpdateResponseSchema = object({
3278
+ candidate: KnowledgeCandidateSchema
3279
+ });
3280
+ const CANDIDATE_STATUS_CLASSES = {
3281
+ draft: "border-amber-500/30 bg-amber-500/10 text-amber-500",
3282
+ promoted: "border-emerald-500/30 bg-emerald-500/10 text-emerald-500",
3283
+ failed: "border-destructive/30 bg-destructive/10 text-destructive"
3284
+ };
3247
3285
  function formatElapsed(ms) {
3248
3286
  if (ms === null) return "-";
3249
3287
  if (ms < 1e3) return `${String(ms)}ms`;
@@ -3267,36 +3305,420 @@ function jumpToLog(logId) {
3267
3305
  target.scrollIntoView({ block: "center", behavior: "smooth" });
3268
3306
  target.focus({ preventScroll: true });
3269
3307
  }
3270
- function CandidateList({ candidates }) {
3271
- if (candidates.length === 0) return null;
3272
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1.5", children: candidates.map((candidate) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
3273
- "div",
3308
+ function firstFailureLog(logs) {
3309
+ for (const log of logs) {
3310
+ if (log.responseStatus !== null && log.responseStatus >= 400) return log;
3311
+ }
3312
+ return null;
3313
+ }
3314
+ function slowestLog(logs) {
3315
+ let current = null;
3316
+ let currentElapsed = -1;
3317
+ for (const log of logs) {
3318
+ if (log.elapsedMs === null) continue;
3319
+ if (log.elapsedMs > currentElapsed) {
3320
+ current = log;
3321
+ currentElapsed = log.elapsedMs;
3322
+ }
3323
+ }
3324
+ return current;
3325
+ }
3326
+ function firstToolLog(logs) {
3327
+ for (const log of logs) {
3328
+ if (extractToolTraceEvents(log).length > 0) return log;
3329
+ }
3330
+ return null;
3331
+ }
3332
+ function buildTraceInsights(input) {
3333
+ const insights = [
3334
+ {
3335
+ kind: "session",
3336
+ title: `${String(input.summary.llmCallCount)} LLM call${input.summary.llmCallCount === 1 ? "" : "s"}`,
3337
+ detail: `Scope ${input.scopeId}`,
3338
+ logId: input.logs[0]?.id ?? null
3339
+ }
3340
+ ];
3341
+ const toolLog = firstToolLog(input.logs);
3342
+ if (input.summary.toolCallCount > 0 && toolLog !== null) {
3343
+ insights.push({
3344
+ kind: "tool",
3345
+ title: `${String(input.summary.toolCallCount)} tool call${input.summary.toolCallCount === 1 ? "" : "s"}`,
3346
+ detail: `First tool evidence at #${String(toolLog.id)}`,
3347
+ logId: toolLog.id
3348
+ });
3349
+ }
3350
+ const failure = firstFailureLog(input.logs);
3351
+ if (failure !== null) {
3352
+ insights.push({
3353
+ kind: "failure",
3354
+ title: `Failure #${String(failure.responseStatus)}`,
3355
+ detail: `First failed request is #${String(failure.id)}`,
3356
+ logId: failure.id
3357
+ });
3358
+ }
3359
+ const slowest = slowestLog(input.logs);
3360
+ if (slowest !== null && input.summary.maxElapsedMs !== null) {
3361
+ insights.push({
3362
+ kind: "slow",
3363
+ title: `Slowest ${formatElapsed(input.summary.maxElapsedMs)}`,
3364
+ detail: `Max latency observed at #${String(slowest.id)}`,
3365
+ logId: slowest.id
3366
+ });
3367
+ }
3368
+ if (input.candidates.length > 0) {
3369
+ const draftCount = input.candidates.filter((candidate) => candidate.status === "draft").length;
3370
+ const promotedCount = input.candidates.filter(
3371
+ (candidate) => candidate.status === "promoted"
3372
+ ).length;
3373
+ insights.push({
3374
+ kind: "candidate",
3375
+ title: `${String(input.candidates.length)} memory candidate${input.candidates.length === 1 ? "" : "s"}`,
3376
+ detail: `${String(draftCount)} draft / ${String(promotedCount)} promoted`,
3377
+ logId: input.candidates[0]?.logIds[0] ?? null
3378
+ });
3379
+ }
3380
+ return insights;
3381
+ }
3382
+ function insightIcon(kind) {
3383
+ switch (kind) {
3384
+ case "session":
3385
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3.5 text-blue-400" });
3386
+ case "tool":
3387
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-sky-400/70" });
3388
+ case "slow":
3389
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3.5 text-amber-400" });
3390
+ case "failure":
3391
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3.5 text-destructive" });
3392
+ case "candidate":
3393
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5 text-emerald-400" });
3394
+ }
3395
+ }
3396
+ function TraceInsights({ insights }) {
3397
+ if (insights.length === 0) return null;
3398
+ 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(
3399
+ "button",
3274
3400
  {
3275
- className: "rounded-md border border-border/80 bg-background/60 px-2.5 py-2",
3401
+ type: "button",
3402
+ className: cn(
3403
+ "flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs",
3404
+ "text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground",
3405
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
3406
+ ),
3407
+ onClick: () => {
3408
+ if (insight.logId !== null) jumpToLog(insight.logId);
3409
+ },
3410
+ disabled: insight.logId === null,
3276
3411
  children: [
3277
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
3278
- /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-5 px-1.5 text-[10px] font-mono", children: candidate.type }),
3279
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", title: candidate.title, children: candidate.title }),
3280
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 font-mono text-[10px] text-muted-foreground", children: candidate.status })
3412
+ insightIcon(insight.kind),
3413
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0", children: [
3414
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate font-medium text-foreground/90", children: insight.title }),
3415
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate font-mono text-[10px]", children: insight.detail })
3416
+ ] })
3417
+ ]
3418
+ },
3419
+ `${insight.kind}-${insight.title}`
3420
+ )) });
3421
+ }
3422
+ function candidateStatusLabel(status) {
3423
+ switch (status) {
3424
+ case "draft":
3425
+ return "Draft";
3426
+ case "promoted":
3427
+ return "Promoted";
3428
+ case "failed":
3429
+ return "Failed";
3430
+ }
3431
+ }
3432
+ function candidatePromoteLabel(candidate, promoting) {
3433
+ if (promoting) return "Promoting";
3434
+ switch (candidate.status) {
3435
+ case "draft":
3436
+ return "Promote";
3437
+ case "failed":
3438
+ return "Retry";
3439
+ case "promoted":
3440
+ return "Promoted";
3441
+ }
3442
+ }
3443
+ function redactionLabel(candidate) {
3444
+ if (!candidate.redaction.redacted) return "No sensitive pattern matched";
3445
+ return `Redacted ${candidate.redaction.patterns.join(", ")}`;
3446
+ }
3447
+ function previewText(value, maxLength) {
3448
+ const normalized = value.replace(/\s+/g, " ").trim();
3449
+ if (normalized.length <= maxLength) return normalized;
3450
+ return `${normalized.slice(0, maxLength - 3)}...`;
3451
+ }
3452
+ function logRangeLabel(logIds) {
3453
+ const first = logIds[0];
3454
+ const last = logIds[logIds.length - 1];
3455
+ if (first === void 0) return "No evidence logs";
3456
+ if (last === void 0 || first === last) return `#${String(first)}`;
3457
+ return `#${String(first)}-#${String(last)}`;
3458
+ }
3459
+ function tagsToText(tags) {
3460
+ return tags.join(", ");
3461
+ }
3462
+ function textToTags(value) {
3463
+ return value.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0);
3464
+ }
3465
+ function CandidateItem({
3466
+ candidate,
3467
+ isPromoting,
3468
+ isUpdating,
3469
+ onPromoteCandidate,
3470
+ onUpdateCandidate
3471
+ }) {
3472
+ const [editing, setEditing] = reactExports.useState(false);
3473
+ const [draftType, setDraftType] = reactExports.useState(candidate.type);
3474
+ const [draftTitle, setDraftTitle] = reactExports.useState(candidate.title);
3475
+ const [draftContent, setDraftContent] = reactExports.useState(candidate.content);
3476
+ const [draftTags, setDraftTags] = reactExports.useState(tagsToText(candidate.tags));
3477
+ const [localError, setLocalError] = reactExports.useState(null);
3478
+ const canPromote = candidate.status !== "promoted";
3479
+ const canEdit = candidate.status !== "promoted";
3480
+ const resetDraft = reactExports.useCallback(() => {
3481
+ setDraftType(candidate.type);
3482
+ setDraftTitle(candidate.title);
3483
+ setDraftContent(candidate.content);
3484
+ setDraftTags(tagsToText(candidate.tags));
3485
+ setLocalError(null);
3486
+ }, [candidate.content, candidate.tags, candidate.title, candidate.type]);
3487
+ const saveDraft = reactExports.useCallback(async () => {
3488
+ const title = draftTitle.trim();
3489
+ const content = draftContent.trim();
3490
+ const tags = textToTags(draftTags);
3491
+ if (title.length === 0) {
3492
+ setLocalError("Title is required.");
3493
+ return;
3494
+ }
3495
+ if (content.length === 0) {
3496
+ setLocalError("Content is required.");
3497
+ return;
3498
+ }
3499
+ if (tags.length === 0) {
3500
+ setLocalError("At least one tag is required.");
3501
+ return;
3502
+ }
3503
+ setLocalError(null);
3504
+ const saved = await onUpdateCandidate(candidate.id, {
3505
+ type: draftType,
3506
+ title,
3507
+ content,
3508
+ tags
3509
+ });
3510
+ if (saved) setEditing(false);
3511
+ }, [candidate.id, draftContent, draftTags, draftTitle, draftType, onUpdateCandidate]);
3512
+ if (editing) {
3513
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md border border-border/80 bg-background/60 px-2.5 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-2", children: [
3514
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
3515
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3516
+ "select",
3517
+ {
3518
+ value: draftType,
3519
+ disabled: isUpdating,
3520
+ onChange: (event) => {
3521
+ const parsed = KnowledgeCandidateSchema.shape.type.safeParse(
3522
+ event.currentTarget.value
3523
+ );
3524
+ if (parsed.success) setDraftType(parsed.data);
3525
+ },
3526
+ className: "h-7 rounded-md border border-input bg-background px-2 text-xs",
3527
+ "aria-label": "Candidate type",
3528
+ children: [
3529
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "episode", children: "episode" }),
3530
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "procedure", children: "procedure" }),
3531
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "preference", children: "preference" }),
3532
+ /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "project-fact", children: "project-fact" })
3533
+ ]
3534
+ }
3535
+ ),
3536
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3537
+ "input",
3538
+ {
3539
+ value: draftTitle,
3540
+ disabled: isUpdating,
3541
+ onChange: (event) => setDraftTitle(event.currentTarget.value),
3542
+ className: "h-7 min-w-[220px] flex-1 rounded-md border border-input bg-background px-2 text-xs",
3543
+ "aria-label": "Candidate title"
3544
+ }
3545
+ )
3546
+ ] }),
3547
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3548
+ "textarea",
3549
+ {
3550
+ value: draftContent,
3551
+ disabled: isUpdating,
3552
+ onChange: (event) => setDraftContent(event.currentTarget.value),
3553
+ className: "min-h-28 rounded-md border border-input bg-background px-2 py-1.5 font-mono text-[11px] leading-relaxed",
3554
+ "aria-label": "Candidate content"
3555
+ }
3556
+ ),
3557
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3558
+ "input",
3559
+ {
3560
+ value: draftTags,
3561
+ disabled: isUpdating,
3562
+ onChange: (event) => setDraftTags(event.currentTarget.value),
3563
+ className: "h-7 rounded-md border border-input bg-background px-2 text-xs",
3564
+ "aria-label": "Candidate tags"
3565
+ }
3566
+ ),
3567
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
3568
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground", children: [
3569
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "size-3" }),
3570
+ "Saving reruns Inspector redaction before promotion."
3281
3571
  ] }),
3282
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 flex flex-wrap items-center gap-1.5", children: candidate.logIds.map((logId) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
3283
- "a",
3572
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
3573
+ localError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-destructive", children: localError }),
3574
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3575
+ Button,
3284
3576
  {
3285
- href: `#${getLogAnchor(logId)}`,
3286
- onClick: (event) => {
3287
- event.preventDefault();
3288
- jumpToLog(logId);
3577
+ type: "button",
3578
+ variant: "ghost",
3579
+ size: "sm",
3580
+ className: "h-7 gap-1.5 px-2 text-xs",
3581
+ disabled: isUpdating,
3582
+ onClick: () => {
3583
+ resetDraft();
3584
+ setEditing(false);
3289
3585
  },
3290
- 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",
3291
- "aria-label": `Jump to evidence log ${String(logId)}`,
3292
3586
  children: [
3293
- "#",
3294
- logId
3587
+ /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3.5" }),
3588
+ "Cancel"
3295
3589
  ]
3590
+ }
3591
+ ),
3592
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3593
+ Button,
3594
+ {
3595
+ type: "button",
3596
+ variant: "outline",
3597
+ size: "sm",
3598
+ className: "h-7 gap-1.5 px-2 text-xs",
3599
+ disabled: isUpdating,
3600
+ onClick: () => {
3601
+ void saveDraft();
3602
+ },
3603
+ children: [
3604
+ isUpdating ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { className: "size-3.5" }),
3605
+ "Save"
3606
+ ]
3607
+ }
3608
+ )
3609
+ ] })
3610
+ ] }) });
3611
+ }
3612
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border/80 bg-background/60 px-2.5 py-2", children: [
3613
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
3614
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-5 px-1.5 text-[10px] font-mono", children: candidate.type }),
3615
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", title: candidate.title, children: candidate.title }),
3616
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3617
+ Badge,
3618
+ {
3619
+ variant: "outline",
3620
+ className: cn(
3621
+ "h-5 shrink-0 px-1.5 text-[10px] font-mono",
3622
+ CANDIDATE_STATUS_CLASSES[candidate.status]
3623
+ ),
3624
+ children: candidateStatusLabel(candidate.status)
3625
+ }
3626
+ )
3627
+ ] }),
3628
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3629
+ "p",
3630
+ {
3631
+ className: "mt-1 text-[11px] leading-relaxed text-muted-foreground",
3632
+ title: candidate.content,
3633
+ children: previewText(candidate.content, 360)
3634
+ }
3635
+ ),
3636
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-muted-foreground", children: [
3637
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [
3638
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FileSearch, { className: "size-3" }),
3639
+ logRangeLabel(candidate.logIds)
3640
+ ] }),
3641
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [
3642
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "size-3" }),
3643
+ redactionLabel(candidate)
3644
+ ] }),
3645
+ candidate.status === "promoted" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-emerald-500", children: [
3646
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "size-3" }),
3647
+ candidate.openClawMemoryId ?? "OpenClaw"
3648
+ ] }),
3649
+ candidate.status === "failed" && candidate.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-destructive", children: [
3650
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3" }),
3651
+ candidate.error
3652
+ ] })
3653
+ ] }),
3654
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1.5", children: [
3655
+ candidate.logIds.map((logId) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
3656
+ "a",
3657
+ {
3658
+ href: `#${getLogAnchor(logId)}`,
3659
+ onClick: (event) => {
3660
+ event.preventDefault();
3661
+ jumpToLog(logId);
3296
3662
  },
3297
- logId
3298
- )) })
3299
- ]
3663
+ 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",
3664
+ "aria-label": `Jump to evidence log ${String(logId)}`,
3665
+ children: [
3666
+ "#",
3667
+ logId
3668
+ ]
3669
+ },
3670
+ logId
3671
+ )),
3672
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
3673
+ canEdit && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3674
+ Button,
3675
+ {
3676
+ type: "button",
3677
+ variant: "ghost",
3678
+ size: "sm",
3679
+ className: "h-6 gap-1.5 px-2 text-[10px]",
3680
+ disabled: isUpdating || isPromoting,
3681
+ onClick: () => setEditing(true),
3682
+ children: [
3683
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
3684
+ "Review"
3685
+ ]
3686
+ }
3687
+ ),
3688
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3689
+ Button,
3690
+ {
3691
+ type: "button",
3692
+ variant: canPromote ? "outline" : "ghost",
3693
+ size: "sm",
3694
+ className: "h-6 gap-1.5 px-2 text-[10px]",
3695
+ onClick: () => onPromoteCandidate(candidate.id),
3696
+ disabled: !canPromote || isPromoting || isUpdating,
3697
+ children: [
3698
+ 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" }),
3699
+ candidatePromoteLabel(candidate, isPromoting)
3700
+ ]
3701
+ }
3702
+ )
3703
+ ] })
3704
+ ] });
3705
+ }
3706
+ function CandidateList({
3707
+ candidates,
3708
+ promotingCandidateIds,
3709
+ updatingCandidateIds,
3710
+ onPromoteCandidate,
3711
+ onUpdateCandidate
3712
+ }) {
3713
+ if (candidates.length === 0) return null;
3714
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1.5", children: candidates.map((candidate) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3715
+ CandidateItem,
3716
+ {
3717
+ candidate,
3718
+ isPromoting: promotingCandidateIds.has(candidate.id),
3719
+ isUpdating: updatingCandidateIds.has(candidate.id),
3720
+ onPromoteCandidate,
3721
+ onUpdateCandidate
3300
3722
  },
3301
3723
  candidate.id
3302
3724
  )) });
@@ -3314,11 +3736,21 @@ function AgentTraceSummary({
3314
3736
  status: "idle",
3315
3737
  error: null
3316
3738
  });
3739
+ const [promotingCandidateIds, setPromotingCandidateIds] = reactExports.useState(
3740
+ () => /* @__PURE__ */ new Set()
3741
+ );
3742
+ const [updatingCandidateIds, setUpdatingCandidateIds] = reactExports.useState(
3743
+ () => /* @__PURE__ */ new Set()
3744
+ );
3317
3745
  const hasCandidates = candidates.length > 0;
3318
3746
  const summary = reactExports.useMemo(
3319
3747
  () => buildTraceSummary(logs, slowResponseThresholdSeconds, candidates.length),
3320
3748
  [candidates.length, logs, slowResponseThresholdSeconds]
3321
3749
  );
3750
+ const traceInsights = reactExports.useMemo(
3751
+ () => buildTraceInsights({ logs, scopeId, summary, candidates }),
3752
+ [candidates, logs, scopeId, summary]
3753
+ );
3322
3754
  const showElapsedSummary = showRollupMetrics || summary.maxElapsedMs !== null;
3323
3755
  const timeRange = reactExports.useMemo(
3324
3756
  () => formatTimeRange$1(summary.startedAt, summary.endedAt, timeDisplayFormat),
@@ -3344,7 +3776,9 @@ function AgentTraceSummary({
3344
3776
  const parsed = await parseJsonResponse$1(response, CandidateResponseSchema);
3345
3777
  setCandidates(parsed.candidates);
3346
3778
  setCandidatesExpanded(parsed.candidates.length > 0);
3347
- setCandidateState({ status: "ready", error: null });
3779
+ setCandidateState(
3780
+ parsed.candidates.length > 0 ? { status: "ready", error: null } : { status: "failed", error: "No candidate was generated for this trace scope." }
3781
+ );
3348
3782
  } catch (error) {
3349
3783
  setCandidateState({
3350
3784
  status: "failed",
@@ -3353,6 +3787,105 @@ function AgentTraceSummary({
3353
3787
  }
3354
3788
  })();
3355
3789
  }, [candidateState.status, logs.length, scopeId]);
3790
+ const promoteCandidate = reactExports.useCallback((candidateId) => {
3791
+ setPromotingCandidateIds((current) => {
3792
+ if (current.has(candidateId)) return current;
3793
+ const next = new Set(current);
3794
+ next.add(candidateId);
3795
+ return next;
3796
+ });
3797
+ void (async () => {
3798
+ try {
3799
+ const response = await fetch(
3800
+ `/api/knowledge/candidates/${encodeURIComponent(candidateId)}/promote`,
3801
+ { method: "POST" }
3802
+ );
3803
+ const raw = await response.json().catch(() => null);
3804
+ const parsed = CandidatePromotionResponseSchema.safeParse(raw);
3805
+ if (response.ok && parsed.success) {
3806
+ setCandidates(
3807
+ (current) => current.map(
3808
+ (candidate) => candidate.id === parsed.data.candidate.id ? parsed.data.candidate : candidate
3809
+ )
3810
+ );
3811
+ setCandidateState({ status: "ready", error: null });
3812
+ return;
3813
+ }
3814
+ const failure = CandidatePromotionFailureResponseSchema.safeParse(raw);
3815
+ if (failure.success && failure.data.candidate !== void 0) {
3816
+ setCandidates(
3817
+ (current) => current.map(
3818
+ (candidate) => candidate.id === failure.data.candidate?.id ? failure.data.candidate : candidate
3819
+ )
3820
+ );
3821
+ }
3822
+ setCandidateState({
3823
+ status: "failed",
3824
+ error: failure.success ? failure.data.error ?? "Candidate promotion failed" : `Candidate promotion failed with ${String(response.status)}`
3825
+ });
3826
+ } catch (error) {
3827
+ setCandidateState({
3828
+ status: "failed",
3829
+ error: error instanceof Error ? error.message : "Candidate promotion failed"
3830
+ });
3831
+ } finally {
3832
+ setPromotingCandidateIds((current) => {
3833
+ const next = new Set(current);
3834
+ next.delete(candidateId);
3835
+ return next;
3836
+ });
3837
+ }
3838
+ })();
3839
+ }, []);
3840
+ const updateCandidate = reactExports.useCallback(
3841
+ async (candidateId, update) => {
3842
+ setUpdatingCandidateIds((current) => {
3843
+ if (current.has(candidateId)) return current;
3844
+ const next = new Set(current);
3845
+ next.add(candidateId);
3846
+ return next;
3847
+ });
3848
+ try {
3849
+ const response = await fetch(
3850
+ `/api/knowledge/candidates/${encodeURIComponent(candidateId)}`,
3851
+ {
3852
+ method: "PATCH",
3853
+ headers: { "Content-Type": "application/json" },
3854
+ body: JSON.stringify(update)
3855
+ }
3856
+ );
3857
+ if (!response.ok) {
3858
+ const message = await readApiError(
3859
+ response,
3860
+ `Candidate update failed with ${String(response.status)}`
3861
+ );
3862
+ setCandidateState({ status: "failed", error: message });
3863
+ return false;
3864
+ }
3865
+ const parsed = await parseJsonResponse$1(response, CandidateUpdateResponseSchema);
3866
+ setCandidates(
3867
+ (current) => current.map(
3868
+ (candidate) => candidate.id === parsed.candidate.id ? parsed.candidate : candidate
3869
+ )
3870
+ );
3871
+ setCandidateState({ status: "ready", error: null });
3872
+ return true;
3873
+ } catch (error) {
3874
+ setCandidateState({
3875
+ status: "failed",
3876
+ error: error instanceof Error ? error.message : "Candidate update failed"
3877
+ });
3878
+ return false;
3879
+ } finally {
3880
+ setUpdatingCandidateIds((current) => {
3881
+ const next = new Set(current);
3882
+ next.delete(candidateId);
3883
+ return next;
3884
+ });
3885
+ }
3886
+ },
3887
+ []
3888
+ );
3356
3889
  if (logs.length === 0) return null;
3357
3890
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "mb-2 rounded-lg border border-border bg-muted/10 px-3 py-2", children: [
3358
3891
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs", children: [
@@ -3377,7 +3910,7 @@ function AgentTraceSummary({
3377
3910
  (summary.totalCacheCreationInputTokens > 0 || summary.totalCacheReadInputTokens > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
3378
3911
  /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-purple-400" }),
3379
3912
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
3380
- "+",
3913
+ "KV Cache +",
3381
3914
  formatTokens(summary.totalCacheCreationInputTokens),
3382
3915
  " / ~",
3383
3916
  formatTokens(summary.totalCacheReadInputTokens)
@@ -3436,7 +3969,17 @@ function AgentTraceSummary({
3436
3969
  )
3437
3970
  ] }),
3438
3971
  candidateState.status === "failed" && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-xs text-destructive", children: candidateState.error }),
3439
- candidatesExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx(CandidateList, { candidates })
3972
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TraceInsights, { insights: traceInsights }),
3973
+ candidatesExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx(
3974
+ CandidateList,
3975
+ {
3976
+ candidates,
3977
+ promotingCandidateIds,
3978
+ updatingCandidateIds,
3979
+ onPromoteCandidate: promoteCandidate,
3980
+ onUpdateCandidate: updateCandidate
3981
+ }
3982
+ )
3440
3983
  ] });
3441
3984
  }
3442
3985
  function computeStats(logs) {
@@ -4088,26 +4631,18 @@ function TestStatus({ result }) {
4088
4631
  }
4089
4632
  if (result.cacheCreationInputTokens !== void 0 && result.cacheCreationInputTokens > 0) {
4090
4633
  tokenParts.push(
4091
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
4092
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
4093
- "+",
4094
- result.cacheCreationInputTokens,
4095
- " cache"
4096
- ] }) }),
4097
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Tokens cached for reuse, reducing future API cost" })
4098
- ] }) }, "cache-create")
4634
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
4635
+ "KV Cache +",
4636
+ result.cacheCreationInputTokens
4637
+ ] }, "cache-create")
4099
4638
  );
4100
4639
  }
4101
4640
  if (result.cacheReadInputTokens !== void 0 && result.cacheReadInputTokens > 0) {
4102
4641
  tokenParts.push(
4103
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
4104
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
4105
- "~",
4106
- result.cacheReadInputTokens,
4107
- " cached"
4108
- ] }) }),
4109
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Tokens served from cache, reducing API cost" })
4110
- ] }) }, "cache-read")
4642
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
4643
+ "KV Cache ~",
4644
+ result.cacheReadInputTokens
4645
+ ] }, "cache-read")
4111
4646
  );
4112
4647
  }
4113
4648
  const displayTokens = [];
@@ -5344,7 +5879,8 @@ function SettingsDialog() {
5344
5879
  /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "flex-1 overflow-hidden", children: [
5345
5880
  /* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
5346
5881
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "providers", children: "Providers" }),
5347
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "proxy", children: "Proxy" })
5882
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "proxy", children: "Proxy" }),
5883
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "onboarding", children: "Onboarding" })
5348
5884
  ] }),
5349
5885
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 overflow-y-auto flex-1 pr-3", children: [
5350
5886
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "providers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -5363,12 +5899,89 @@ function SettingsDialog() {
5363
5899
  onTestingTimeLeftChange: handleTestingTimeLeftChange
5364
5900
  }
5365
5901
  ) }),
5366
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "proxy", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProxySettingsTab, {}) })
5902
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "proxy", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProxySettingsTab, {}) }),
5903
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "onboarding", children: /* @__PURE__ */ jsxRuntimeExports.jsx(OnboardingSettingsTab, {}) })
5367
5904
  ] })
5368
5905
  ] })
5369
5906
  ] })
5370
5907
  ] });
5371
5908
  }
5909
+ function CopyableSetupValue({
5910
+ id,
5911
+ label,
5912
+ value,
5913
+ copiedId,
5914
+ onCopy
5915
+ }) {
5916
+ const copied = copiedId === id;
5917
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border bg-muted/20 px-3 py-2", children: [
5918
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: label }),
5919
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
5920
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "min-w-0 flex-1 truncate font-mono text-xs text-foreground", children: value }),
5921
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5922
+ Button,
5923
+ {
5924
+ type: "button",
5925
+ variant: "ghost",
5926
+ size: "icon",
5927
+ className: "size-7 shrink-0",
5928
+ onClick: () => onCopy(id, value),
5929
+ "aria-label": copied ? `Copied ${label}` : `Copy ${label}`,
5930
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
5931
+ }
5932
+ )
5933
+ ] })
5934
+ ] });
5935
+ }
5936
+ function OnboardingSettingsTab() {
5937
+ const [copiedId, setCopiedId] = reactExports.useState(null);
5938
+ const origin = reactExports.useMemo(() => {
5939
+ if (typeof window === "undefined") return "http://localhost:25947";
5940
+ return window.location.origin;
5941
+ }, []);
5942
+ const values = reactExports.useMemo(
5943
+ () => [
5944
+ { id: "skill", label: "Codex skill", value: "agent-inspector onboard --force" },
5945
+ { id: "mcp", label: "MCP URL", value: `${origin}/api/mcp` },
5946
+ { id: "proxy", label: "Proxy URL", value: `${origin}/proxy` },
5947
+ { id: "anthropic", label: "Anthropic base", value: `ANTHROPIC_BASE_URL=${origin}/proxy` }
5948
+ ],
5949
+ [origin]
5950
+ );
5951
+ const handleCopy = reactExports.useCallback((id, value) => {
5952
+ void window.navigator.clipboard.writeText(value).then(() => {
5953
+ setCopiedId(id);
5954
+ setTimeout(() => setCopiedId(null), 1600);
5955
+ });
5956
+ }, []);
5957
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
5958
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
5959
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { className: "size-4 text-muted-foreground" }),
5960
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "Agent onboarding" })
5961
+ ] }),
5962
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid gap-2", children: values.map((item) => /* @__PURE__ */ jsxRuntimeExports.jsx(
5963
+ CopyableSetupValue,
5964
+ {
5965
+ id: item.id,
5966
+ label: item.label,
5967
+ value: item.value,
5968
+ copiedId,
5969
+ onCopy: handleCopy
5970
+ },
5971
+ item.id
5972
+ )) }),
5973
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-2 rounded-md border border-border bg-background px-3 py-2 text-xs text-muted-foreground", children: [
5974
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
5975
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-500" }),
5976
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Provider test creates a traceable memory probe session." })
5977
+ ] }),
5978
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
5979
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-500" }),
5980
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Captured sessions can produce reviewable memory candidates." })
5981
+ ] })
5982
+ ] })
5983
+ ] });
5984
+ }
5372
5985
  function ProxySettingsTab() {
5373
5986
  const {
5374
5987
  strip,
@@ -5566,20 +6179,40 @@ function safeItemAt(items, index) {
5566
6179
  }
5567
6180
  function isEditableTarget(target) {
5568
6181
  const tag = target.tagName;
5569
- if (tag === "INPUT" || tag === "TEXTAREA") return true;
6182
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
5570
6183
  if (target.isContentEditable) return true;
5571
6184
  return false;
5572
6185
  }
5573
- function useKeyboardNavigation(containerRef, wrapperRef) {
6186
+ function isInteractiveTarget(target) {
6187
+ const tag = target.tagName;
6188
+ if (tag === "BUTTON" || tag === "A") return true;
6189
+ if (isEditableTarget(target)) return true;
6190
+ return target.closest("[role='button'],[role='menuitem'],[role='option']") !== null;
6191
+ }
6192
+ function isInOpenOverlay(target) {
6193
+ if (target.closest("[role='dialog'],[role='menu'],[role='listbox']") !== null) return true;
6194
+ return document.querySelector("[role='dialog'],[role='menu'],[role='listbox']") !== null;
6195
+ }
6196
+ function hasActiveTextSelection() {
6197
+ const selection = window.getSelection();
6198
+ if (selection === null) return false;
6199
+ return selection.type === "Range" && selection.toString().length > 0;
6200
+ }
6201
+ function isLetterNavigationKey(event, key) {
6202
+ return event.key === key && event.shiftKey;
6203
+ }
6204
+ function useKeyboardNavigation(containerRef, wrapperRef, options = {}) {
5574
6205
  const rootRef = wrapperRef ?? containerRef;
6206
+ const pageWide = options.pageWide === true;
5575
6207
  const handleFocusContainer = reactExports.useCallback(
5576
6208
  (e) => {
5577
6209
  const container = containerRef.current;
6210
+ const root = rootRef.current;
5578
6211
  if (!container) return;
6212
+ if (!root) return;
5579
6213
  const target = e.target;
5580
6214
  if (!isFocusTarget(target)) return;
5581
- if (target !== rootRef.current) return;
5582
- if (!container.contains(target)) return;
6215
+ if (target !== root) return;
5583
6216
  const items = findNavItems(container);
5584
6217
  const first = safeItemAt(items, 0);
5585
6218
  if (first !== null) focusAndScroll(first);
@@ -5587,14 +6220,22 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
5587
6220
  [containerRef, rootRef]
5588
6221
  );
5589
6222
  reactExports.useEffect(() => {
5590
- const root = rootRef.current;
5591
- const container = containerRef.current;
5592
- if (!root || !container) return;
5593
6223
  const handleKeyDown = (e) => {
6224
+ if (e.altKey || e.ctrlKey || e.metaKey) return;
6225
+ const container = containerRef.current;
6226
+ if (!container) return;
5594
6227
  const target = e.target;
5595
6228
  if (!isFocusTarget(target)) return;
5596
- if (!container.contains(target)) return;
5597
6229
  if (isEditableTarget(target)) return;
6230
+ if (hasActiveTextSelection()) return;
6231
+ const isInsideContainer = container.contains(target);
6232
+ if (isInsideContainer && isInteractiveTarget(target) && !target.hasAttribute(NAV_ATTR)) {
6233
+ return;
6234
+ }
6235
+ if (!isInsideContainer) {
6236
+ if (!pageWide) return;
6237
+ if (isInteractiveTarget(target) || isInOpenOverlay(target)) return;
6238
+ }
5598
6239
  const items = findNavItems(container);
5599
6240
  if (items.length === 0) return;
5600
6241
  const current = findClosestNavItem(target, container);
@@ -5603,7 +6244,8 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
5603
6244
  switch (e.key) {
5604
6245
  case "ArrowUp":
5605
6246
  case "W": {
5606
- if (e.key === "W" && !e.shiftKey) break;
6247
+ if (e.shiftKey && e.key === "ArrowUp") break;
6248
+ if (e.key === "W" && !isLetterNavigationKey(e, "W")) break;
5607
6249
  e.preventDefault();
5608
6250
  const prevIdx = currentIdx > 0 ? currentIdx - 1 : currentIdx === -1 ? items.length - 1 : -1;
5609
6251
  if (prevIdx !== -1) {
@@ -5615,7 +6257,8 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
5615
6257
  }
5616
6258
  case "ArrowDown":
5617
6259
  case "S": {
5618
- if (e.key === "S" && !e.shiftKey) break;
6260
+ if (e.shiftKey && e.key === "ArrowDown") break;
6261
+ if (e.key === "S" && !isLetterNavigationKey(e, "S")) break;
5619
6262
  e.preventDefault();
5620
6263
  const nextIdx = currentIdx < items.length - 1 ? currentIdx + 1 : currentIdx === -1 ? 0 : -1;
5621
6264
  if (nextIdx !== -1) {
@@ -5627,7 +6270,8 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
5627
6270
  }
5628
6271
  case "ArrowLeft":
5629
6272
  case "A": {
5630
- if (e.key === "A" && !e.shiftKey) break;
6273
+ if (e.shiftKey && e.key === "ArrowLeft") break;
6274
+ if (e.key === "A" && !isLetterNavigationKey(e, "A")) break;
5631
6275
  if (current === null) break;
5632
6276
  e.preventDefault();
5633
6277
  const action = getAction(current);
@@ -5642,7 +6286,8 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
5642
6286
  }
5643
6287
  case "ArrowRight":
5644
6288
  case "D": {
5645
- if (e.key === "D" && !e.shiftKey) break;
6289
+ if (e.shiftKey && e.key === "ArrowRight") break;
6290
+ if (e.key === "D" && !isLetterNavigationKey(e, "D")) break;
5646
6291
  if (current === null) break;
5647
6292
  e.preventDefault();
5648
6293
  const action = getAction(current);
@@ -5670,13 +6315,11 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
5670
6315
  };
5671
6316
  document.addEventListener("keydown", handleKeyDown, { capture: true });
5672
6317
  return () => document.removeEventListener("keydown", handleKeyDown, { capture: true });
5673
- }, [containerRef, rootRef]);
6318
+ }, [containerRef, pageWide, rootRef]);
5674
6319
  reactExports.useEffect(() => {
5675
- const root = rootRef.current;
5676
- if (!root) return;
5677
- root.addEventListener("focus", handleFocusContainer);
5678
- return () => root.removeEventListener("focus", handleFocusContainer);
5679
- }, [handleFocusContainer, rootRef]);
6320
+ document.addEventListener("focusin", handleFocusContainer);
6321
+ return () => document.removeEventListener("focusin", handleFocusContainer);
6322
+ }, [handleFocusContainer]);
5680
6323
  }
5681
6324
  function truncateSessionId(id) {
5682
6325
  if (id.length <= 30) return id;
@@ -5862,7 +6505,7 @@ function ProxyViewer({
5862
6505
  );
5863
6506
  const logListRef = reactExports.useRef(null);
5864
6507
  const logListWrapperRef = reactExports.useRef(null);
5865
- useKeyboardNavigation(logListRef, logListWrapperRef);
6508
+ useKeyboardNavigation(logListRef, logListWrapperRef, { pageWide: true });
5866
6509
  reactExports.useEffect(() => {
5867
6510
  const perCrabDuration = 400;
5868
6511
  const startDelay = 50;
@@ -6392,5 +7035,6 @@ export {
6392
7035
  getStatusCategory as p,
6393
7036
  parseJsonText as q,
6394
7037
  resolveLogFormat as r,
6395
- safeJsonValue as s
7038
+ safeJsonValue as s,
7039
+ useProviders as u
6396
7040
  };