@tonyclaw/agent-inspector 2.0.11 → 2.0.12

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 (47) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-C9OWdxHM.js → CompareDrawer-ClM_uVRy.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-CTNNzXSa.js +114 -0
  4. package/.output/public/assets/{ReplayDialog-CwE6I1Pe.js → ReplayDialog-7rp7N_KJ.js} +1 -1
  5. package/.output/public/assets/RequestAnatomy-sL2FFo_N.js +1 -0
  6. package/.output/public/assets/ResponseView-Cjlf3b6-.js +1 -0
  7. package/.output/public/assets/{StreamingChunkSequence-CLcLZ7-W.js → StreamingChunkSequence-Ctx3sT3r.js} +1 -1
  8. package/.output/public/assets/_sessionId-DEWzDQ8-.js +1 -0
  9. package/.output/public/assets/index-DGIIhZTn.js +1 -0
  10. package/.output/public/assets/index-D_nZj9Vt.css +1 -0
  11. package/.output/public/assets/{main-D1Xanzu7.js → main-FvIQopAy.js} +2 -2
  12. package/.output/server/_libs/lucide-react.mjs +51 -39
  13. package/.output/server/{_sessionId-D3IyPpws.mjs → _sessionId-BDSgtrsI.mjs} +2 -2
  14. package/.output/server/_ssr/{CompareDrawer-BIMThqxy.mjs → CompareDrawer-Cqvv0I3x.mjs} +3 -3
  15. package/.output/server/_ssr/{ProxyViewerContainer-BiBdKWtj.mjs → ProxyViewerContainer-C0YEoG8N.mjs} +1611 -183
  16. package/.output/server/_ssr/{ReplayDialog-D_wiDyM2.mjs → ReplayDialog-CrsynUKI.mjs} +4 -4
  17. package/.output/server/_ssr/RequestAnatomy-CR_JUkcL.mjs +688 -0
  18. package/.output/server/_ssr/{ResponseView-Drk5ghmL.mjs → ResponseView-Dba2Et69.mjs} +3 -3
  19. package/.output/server/_ssr/{StreamingChunkSequence-F889rmG0.mjs → StreamingChunkSequence-BIMHLstI.mjs} +2 -2
  20. package/.output/server/_ssr/{index-CaPniq9z.mjs → index-DveIUTm5.mjs} +2 -2
  21. package/.output/server/_ssr/index.mjs +2 -2
  22. package/.output/server/_ssr/{router-C7S8FRth.mjs → router-D0yXVG0Q.mjs} +85 -22
  23. package/.output/server/{_tanstack-start-manifest_v-CVTkFOdT.mjs → _tanstack-start-manifest_v-pe0xJFuQ.mjs} +1 -1
  24. package/.output/server/index.mjs +62 -62
  25. package/package.json +1 -1
  26. package/src/components/ProxyViewer.tsx +453 -52
  27. package/src/components/providers/ProviderCard.tsx +45 -2
  28. package/src/components/providers/ProviderForm.tsx +245 -80
  29. package/src/components/providers/ProvidersPanel.tsx +7 -1
  30. package/src/components/proxy-viewer/ConversationGroup.tsx +18 -1
  31. package/src/components/proxy-viewer/LogEntry.tsx +25 -0
  32. package/src/components/proxy-viewer/TurnGroup.tsx +15 -0
  33. package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +99 -79
  34. package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +22 -4
  35. package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +196 -0
  36. package/src/components/proxy-viewer/logFocus.ts +33 -0
  37. package/src/lib/providerModelMetadata.ts +64 -12
  38. package/src/lib/utils.ts +7 -0
  39. package/src/proxy/providers.ts +25 -0
  40. package/src/routes/api/providers.ts +3 -0
  41. package/.output/public/assets/ProxyViewerContainer-CNzay4u8.js +0 -114
  42. package/.output/public/assets/RequestAnatomy-DEehC3yz.js +0 -1
  43. package/.output/public/assets/ResponseView-CE5UsuUq.js +0 -1
  44. package/.output/public/assets/_sessionId-DE__tPTo.js +0 -1
  45. package/.output/public/assets/index-BFO4Jmw5.js +0 -1
  46. package/.output/public/assets/index-CDzZ7l_S.css +0 -1
  47. package/.output/server/_ssr/RequestAnatomy-5UiMTESr.mjs +0 -1383
@@ -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, h as createPendingProviderTestResults, P as ProviderTestResultsSchema, i as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, T as TimeDisplayFormatSchema, g as getSessionPath, j as ProviderConfigSchema, e as providerHasContextMetadata, f as findProviderModelMetadata, K as KnowledgeCandidateSchema, d as stripClaudeCodeBillingHeader, s as safeGetOwnProperty, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, c as AnthropicRequestSchema } from "./router-C7S8FRth.mjs";
2
+ import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as DEFAULT_TIME_DISPLAY_FORMAT, b as RuntimeConfigSchema, P as ProviderConfigSchema, r as requestFormatForPath, h as createPendingProviderTestResults, i as ProviderTestResultsSchema, j as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, T as TimeDisplayFormatSchema, g as getSessionPath, e as providerHasContextMetadata, f as findProviderModelMetadata, s as safeGetOwnProperty, K as KnowledgeCandidateSchema, d as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, c as AnthropicRequestSchema, k as resolveProviderContextWindow, l as isPlainRecord } from "./router-D0yXVG0Q.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, 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 Search, K as GitCompareArrows, N as CircleQuestionMark, Q as Server, V as Gauge, Y as Lock, _ as Wifi, $ as WifiOff, a0 as ArrowUp, a1 as ArrowDown, a2 as Ellipsis, a3 as CodeXml, a4 as Blocks, a5 as MousePointerClick, a6 as FileText, a7 as Rows3, a8 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 ArrowUpRight, o as ArrowDownRight, p as Minus, q as CircleCheckBig, r as CircleX, s as ShieldCheck, t as RefreshCw, u as Save, F as FileSearch, v as CircleCheck, w as CloudUpload, O as OctagonAlert, G as Globe, x as FileTerminal, y as Radio, z as ChevronsUp, H as ChevronsDown, I as FileDiff, J as History, K as RotateCcw, N as Search, Q as GitCompareArrows, V as CircleQuestionMark, Y as Server, _ as Gauge, $ as Lock, a0 as Wifi, a1 as WifiOff, a2 as ArrowUp, a3 as ArrowDown, a4 as Ellipsis, a5 as CodeXml, a6 as Blocks, a7 as MousePointerClick, a8 as FileText, a9 as Rows3, aa as Columns2 } from "../_libs/lucide-react.mjs";
13
13
  import { M as Markdown } from "../_libs/react-markdown.mjs";
14
14
  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";
15
15
  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";
@@ -291,6 +291,12 @@ function formatTokens(count) {
291
291
  if (count >= 1e6) return (count / 1e6).toFixed(1).replace(/\.0$/, "") + "M";
292
292
  return (count / 1e3).toFixed(1).replace(/\.0$/, "") + "K";
293
293
  }
294
+ function formatContextWindowTokens(count) {
295
+ if (count >= 1024 * 1024) {
296
+ return (count / (1024 * 1024)).toFixed(1).replace(/\.0$/, "") + "M";
297
+ }
298
+ return (count / 1024).toFixed(1).replace(/\.0$/, "") + "K";
299
+ }
294
300
  function getStatusCategory(status) {
295
301
  if (status === null) return "pending";
296
302
  if (status >= 200 && status < 300) return "success";
@@ -298,7 +304,31 @@ function getStatusCategory(status) {
298
304
  if (status >= 500) return "server_error";
299
305
  return "pending";
300
306
  }
301
- const version = "2.0.11";
307
+ async function fetcher(url) {
308
+ return fetchJson(
309
+ url,
310
+ ProviderConfigSchema.array(),
311
+ void 0,
312
+ (response) => `Failed to fetch ${url}: ${response.status}`
313
+ );
314
+ }
315
+ function useProviders() {
316
+ const response = useSWR(
317
+ "/api/providers",
318
+ fetcher,
319
+ {
320
+ revalidateOnFocus: false,
321
+ revalidateIfStale: false
322
+ }
323
+ );
324
+ return {
325
+ providers: response.data ?? [],
326
+ isLoading: response.isLoading,
327
+ isError: response.error,
328
+ mutate: response.mutate
329
+ };
330
+ }
331
+ const version = "2.0.12";
302
332
  const packageJson = {
303
333
  version
304
334
  };
@@ -1359,19 +1389,19 @@ function useCopyFeedback(text) {
1359
1389
  return { copied, copy };
1360
1390
  }
1361
1391
  const LazyCompareDrawer = reactExports.lazy(
1362
- () => import("./CompareDrawer-BIMThqxy.mjs").then((m) => ({ default: m.CompareDrawer }))
1392
+ () => import("./CompareDrawer-Cqvv0I3x.mjs").then((m) => ({ default: m.CompareDrawer }))
1363
1393
  );
1364
1394
  const LazyReplayDialog = reactExports.lazy(
1365
- () => import("./ReplayDialog-D_wiDyM2.mjs").then((m) => ({ default: m.ReplayDialog }))
1395
+ () => import("./ReplayDialog-CrsynUKI.mjs").then((m) => ({ default: m.ReplayDialog }))
1366
1396
  );
1367
1397
  const LazyRequestAnatomy = reactExports.lazy(
1368
- () => import("./RequestAnatomy-5UiMTESr.mjs").then((m) => ({ default: m.RequestAnatomy }))
1398
+ () => import("./RequestAnatomy-CR_JUkcL.mjs").then((m) => ({ default: m.RequestAnatomy }))
1369
1399
  );
1370
1400
  const LazyResponseView = reactExports.lazy(
1371
- () => import("./ResponseView-Drk5ghmL.mjs").then((m) => ({ default: m.ResponseView }))
1401
+ () => import("./ResponseView-Dba2Et69.mjs").then((m) => ({ default: m.ResponseView }))
1372
1402
  );
1373
1403
  const LazyStreamingChunkSequence = reactExports.lazy(
1374
- () => import("./StreamingChunkSequence-F889rmG0.mjs").then((m) => ({
1404
+ () => import("./StreamingChunkSequence-BIMHLstI.mjs").then((m) => ({
1375
1405
  default: m.StreamingChunkSequence
1376
1406
  }))
1377
1407
  );
@@ -2986,6 +3016,26 @@ function resolveLogFormat(log) {
2986
3016
  const pathFormat = requestFormatForPath(log.path);
2987
3017
  return pathFormat === "unknown" ? log.apiFormat : pathFormat;
2988
3018
  }
3019
+ const LOG_FOCUS_REQUEST_EVENT = "agent-inspector:focus-log";
3020
+ function dispatchLogFocusRequest(request) {
3021
+ window.dispatchEvent(new CustomEvent(LOG_FOCUS_REQUEST_EVENT, { detail: request }));
3022
+ }
3023
+ function readLogFocusRequest(event) {
3024
+ if (typeof CustomEvent === "undefined" || !(event instanceof CustomEvent)) return null;
3025
+ const detail = event.detail;
3026
+ const logId = safeGetOwnProperty(detail, "logId");
3027
+ if (typeof logId !== "number" || !Number.isInteger(logId) || logId < 0) return null;
3028
+ const tab = safeGetOwnProperty(detail, "tab");
3029
+ switch (tab) {
3030
+ case "anatomy":
3031
+ case "request":
3032
+ return { logId, tab };
3033
+ case void 0:
3034
+ return { logId, tab: "request" };
3035
+ default:
3036
+ return null;
3037
+ }
3038
+ }
2989
3039
  function shouldShowRawRequestTab(apiFormat, viewMode, strip) {
2990
3040
  return apiFormat === "anthropic" && viewMode === "full" && strip;
2991
3041
  }
@@ -3161,6 +3211,14 @@ function parseRequestTools(parsed) {
3161
3211
  function TabFallback() {
3162
3212
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-1", "aria-hidden": "true" });
3163
3213
  }
3214
+ function resolveFocusedTab(tab, anatomySegments) {
3215
+ switch (tab) {
3216
+ case "anatomy":
3217
+ return anatomySegments === null ? "request" : "anatomy";
3218
+ case "request":
3219
+ return "request";
3220
+ }
3221
+ }
3164
3222
  function DiffToggleButton({
3165
3223
  active,
3166
3224
  onClick
@@ -3358,6 +3416,19 @@ const LogEntry = reactExports.memo(function({
3358
3416
  if (!expanded) return;
3359
3417
  setActiveTab(anatomySegments !== null ? "anatomy" : "request");
3360
3418
  }, [anatomySegments, expanded]);
3419
+ reactExports.useEffect(() => {
3420
+ const handleLogFocusRequest = (event) => {
3421
+ const request = readLogFocusRequest(event);
3422
+ if (request === null) return;
3423
+ if (request.logId !== log.id) return;
3424
+ setExpanded(true);
3425
+ setActiveTab(resolveFocusedTab(request.tab, anatomySegments));
3426
+ };
3427
+ window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
3428
+ return () => {
3429
+ window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
3430
+ };
3431
+ }, [anatomySegments, log.id]);
3361
3432
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(TooltipProvider, { children: [
3362
3433
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-border rounded-lg mb-1 overflow-hidden", children: [
3363
3434
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -3828,6 +3899,18 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
3828
3899
  }
3829
3900
  prevCompleteRef.current = isComplete;
3830
3901
  }, [collapsible, isComplete]);
3902
+ reactExports.useEffect(() => {
3903
+ const handleLogFocusRequest = (event) => {
3904
+ const request = readLogFocusRequest(event);
3905
+ if (request === null) return;
3906
+ if (!entries.some((entry) => entry.log.id === request.logId)) return;
3907
+ if (collapsible) setCollapsed(false);
3908
+ };
3909
+ window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
3910
+ return () => {
3911
+ window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
3912
+ };
3913
+ }, [collapsible, entries]);
3831
3914
  const toggleCollapse = reactExports.useCallback(() => {
3832
3915
  if (collapsible) setCollapsed((prev) => !prev);
3833
3916
  }, [collapsible]);
@@ -4836,6 +4919,19 @@ const ConversationGroup = reactExports.memo(function({
4836
4919
  const isLoading = group.logs.some((log) => log.responseStatus === null);
4837
4920
  const showTraceRollupMetrics = standalone && !hasPinnedSessionContext;
4838
4921
  const turnGroups = reactExports.useMemo(() => buildTurnGroups(group.logs), [group.logs]);
4922
+ reactExports.useEffect(() => {
4923
+ const handleLogFocusRequest = (event) => {
4924
+ if (standalone) return;
4925
+ const request = readLogFocusRequest(event);
4926
+ if (request === null) return;
4927
+ if (!group.logs.some((log) => log.id === request.logId)) return;
4928
+ setExpanded(true);
4929
+ };
4930
+ window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4931
+ return () => {
4932
+ window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4933
+ };
4934
+ }, [group.logs, standalone]);
4839
4935
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2", children: [
4840
4936
  !standalone && /* @__PURE__ */ jsxRuntimeExports.jsx(
4841
4937
  ConversationHeader,
@@ -5531,6 +5627,22 @@ function formatTimeAgo(isoString) {
5531
5627
  const diffDay = Math.floor(diffHr / 24);
5532
5628
  return `${diffDay}d ago`;
5533
5629
  }
5630
+ function getModelSourceLink(sourceUrl) {
5631
+ if (sourceUrl === void 0 || sourceUrl.trim() === "") return null;
5632
+ try {
5633
+ const parsed = new URL(sourceUrl);
5634
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
5635
+ const hostname = parsed.hostname.toLowerCase();
5636
+ const isHuggingFace = hostname === "huggingface.co" || hostname.endsWith(".huggingface.co");
5637
+ return {
5638
+ label: isHuggingFace ? "HF" : "Source",
5639
+ title: isHuggingFace ? "Open Hugging Face model source" : "Open model metadata source",
5640
+ url: sourceUrl
5641
+ };
5642
+ } catch {
5643
+ return null;
5644
+ }
5645
+ }
5534
5646
  function ProviderCard({
5535
5647
  provider,
5536
5648
  testResults,
@@ -5592,13 +5704,28 @@ function ProviderCard({
5592
5704
  ] }),
5593
5705
  provider.models !== void 0 && provider.models.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1", children: provider.models.map((m) => {
5594
5706
  const metadata = findProviderModelMetadata(provider, m);
5707
+ const sourceLink = getModelSourceLink(metadata?.sourceUrl);
5595
5708
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
5596
5709
  "span",
5597
5710
  {
5598
5711
  className: "inline-flex items-center gap-1 text-xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground",
5599
5712
  children: [
5600
5713
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: m }),
5601
- metadata !== null && metadata.contextWindow !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-foreground/80", children: formatTokens(metadata.contextWindow) })
5714
+ metadata !== null && metadata.contextWindow !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-foreground/80", children: formatContextWindowTokens(metadata.contextWindow) }),
5715
+ sourceLink !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
5716
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5717
+ "a",
5718
+ {
5719
+ href: sourceLink.url,
5720
+ target: "_blank",
5721
+ rel: "noopener noreferrer",
5722
+ className: "inline-flex items-center gap-0.5 rounded border border-border/70 bg-background/70 px-1 font-mono text-[10px] leading-4 text-muted-foreground transition-colors hover:text-foreground",
5723
+ "aria-label": sourceLink.title,
5724
+ children: sourceLink.label
5725
+ }
5726
+ ) }),
5727
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: sourceLink.title })
5728
+ ] }) })
5602
5729
  ]
5603
5730
  },
5604
5731
  m
@@ -5782,7 +5909,35 @@ const MINIMAX_MODELS = [
5782
5909
  "MiniMax M2.1-highspeed",
5783
5910
  "MiniMax M2"
5784
5911
  ];
5785
- const ALIBABA_MODELS = ["glm-5", "glm-5.1", "qwen3.6-plus", "qwen3.7-max"];
5912
+ const ALIBABA_MODELS = ["glm-5", "glm-5.1", "glm-5.2", "qwen3.6-plus", "qwen3.7-max"];
5913
+ function emptyMetadataDraft() {
5914
+ return { contextWindow: "", outputLimit: "" };
5915
+ }
5916
+ function normalizeModelName(value) {
5917
+ return value.trim().toLowerCase().replace(/\s+/g, "-");
5918
+ }
5919
+ function findModelMetadata(provider, model) {
5920
+ const normalized = normalizeModelName(model);
5921
+ return provider?.modelMetadata?.find(
5922
+ (metadata) => normalizeModelName(metadata.model) === normalized
5923
+ ) ?? null;
5924
+ }
5925
+ function createMetadataDrafts(provider, models) {
5926
+ return models.map((model) => {
5927
+ const metadata = findModelMetadata(provider, model);
5928
+ return {
5929
+ contextWindow: metadata?.contextWindow?.toString() ?? "",
5930
+ outputLimit: metadata?.outputLimit?.toString() ?? ""
5931
+ };
5932
+ });
5933
+ }
5934
+ function readPositiveIntegerInput(value) {
5935
+ const trimmed = value.trim();
5936
+ if (trimmed === "") return null;
5937
+ const parsed = Number(trimmed);
5938
+ if (!Number.isInteger(parsed) || parsed <= 0) return null;
5939
+ return parsed;
5940
+ }
5786
5941
  function ProviderForm({ provider, onSubmit, onCancel }) {
5787
5942
  const [name, setName] = reactExports.useState(provider?.name ?? "");
5788
5943
  const [apiKey, setApiKey] = reactExports.useState(provider?.apiKey ?? "");
@@ -5792,6 +5947,12 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
5792
5947
  const [models, setModels] = reactExports.useState(
5793
5948
  initialModels !== void 0 && initialModels.length > 0 ? initialModels : [""]
5794
5949
  );
5950
+ const [modelMetadataDrafts, setModelMetadataDrafts] = reactExports.useState(
5951
+ () => createMetadataDrafts(
5952
+ provider,
5953
+ initialModels !== void 0 && initialModels.length > 0 ? initialModels : [""]
5954
+ )
5955
+ );
5795
5956
  const [activeTab, setActiveTab] = reactExports.useState("anthropic");
5796
5957
  const [anthropicBaseUrl, setAnthropicBaseUrl] = reactExports.useState(provider?.anthropicBaseUrl ?? "");
5797
5958
  const [openaiBaseUrl, setOpenaiBaseUrl] = reactExports.useState(provider?.openaiBaseUrl ?? "");
@@ -5830,7 +5991,9 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
5830
5991
  if (provider) {
5831
5992
  setName(provider.name);
5832
5993
  setApiKey(provider.apiKey);
5833
- setModels((provider.models?.length ?? 0) > 0 ? provider.models : [""]);
5994
+ const nextModels = (provider.models?.length ?? 0) > 0 ? provider.models : [""];
5995
+ setModels(nextModels);
5996
+ setModelMetadataDrafts(createMetadataDrafts(provider, nextModels));
5834
5997
  setAnthropicBaseUrl(provider.anthropicBaseUrl ?? "");
5835
5998
  setOpenaiBaseUrl(provider.openaiBaseUrl ?? "");
5836
5999
  setApiDocsUrl(provider.apiDocsUrl ?? "");
@@ -5882,6 +6045,19 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
5882
6045
  if (modelMetadataUrl.trim() && !isValidUrl(modelMetadataUrl.trim())) {
5883
6046
  newErrors.modelMetadataUrl = "Invalid URL format";
5884
6047
  }
6048
+ modelMetadataDrafts.forEach((draft, index) => {
6049
+ const contextWindow = readPositiveIntegerInput(draft.contextWindow);
6050
+ const outputLimit = readPositiveIntegerInput(draft.outputLimit);
6051
+ if (draft.contextWindow.trim() !== "" && contextWindow === null) {
6052
+ newErrors[`modelMetadata.${index}.contextWindow`] = "Context window must be a positive integer";
6053
+ }
6054
+ if (draft.outputLimit.trim() !== "" && outputLimit === null) {
6055
+ newErrors[`modelMetadata.${index}.outputLimit`] = "Output limit must be a positive integer";
6056
+ }
6057
+ if (contextWindow !== null && outputLimit !== null && outputLimit > contextWindow) {
6058
+ newErrors[`modelMetadata.${index}.outputLimit`] = "Output limit should not exceed context window";
6059
+ }
6060
+ });
5885
6061
  if (!anthropicBaseUrl.trim() && !openaiBaseUrl.trim()) {
5886
6062
  newErrors.format = "At least one format URL (Anthropic or OpenAI) is required";
5887
6063
  }
@@ -5896,6 +6072,55 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
5896
6072
  return false;
5897
6073
  }
5898
6074
  }
6075
+ function updateModel(index, value) {
6076
+ setModels((prev) => {
6077
+ const next = [...prev];
6078
+ next[index] = value;
6079
+ return next;
6080
+ });
6081
+ }
6082
+ function updateMetadataDraft(index, patch) {
6083
+ setModelMetadataDrafts((prev) => {
6084
+ const next = [...prev];
6085
+ next[index] = { ...next[index] ?? emptyMetadataDraft(), ...patch };
6086
+ return next;
6087
+ });
6088
+ }
6089
+ function removeModel(index) {
6090
+ setModels((prev) => prev.filter((_, idx) => idx !== index));
6091
+ setModelMetadataDrafts((prev) => prev.filter((_, idx) => idx !== index));
6092
+ }
6093
+ function addModel() {
6094
+ setModels((prev) => [...prev, ""]);
6095
+ setModelMetadataDrafts((prev) => [...prev, emptyMetadataDraft()]);
6096
+ }
6097
+ function buildModelMetadata() {
6098
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6099
+ const metadata = [];
6100
+ models.forEach((model, index) => {
6101
+ const trimmedModel = model.trim();
6102
+ if (trimmedModel === "") return;
6103
+ const draft = modelMetadataDrafts[index] ?? emptyMetadataDraft();
6104
+ const contextWindow = readPositiveIntegerInput(draft.contextWindow);
6105
+ const outputLimit = readPositiveIntegerInput(draft.outputLimit);
6106
+ if (contextWindow === null && outputLimit === null) return;
6107
+ const existing = findModelMetadata(provider, trimmedModel);
6108
+ if (existing !== null && existing.contextWindow === (contextWindow ?? void 0) && existing.outputLimit === (outputLimit ?? void 0)) {
6109
+ metadata.push({ ...existing, model: trimmedModel });
6110
+ return;
6111
+ }
6112
+ const entry = {
6113
+ model: trimmedModel,
6114
+ source: "manual",
6115
+ updatedAt: now
6116
+ };
6117
+ if (contextWindow !== null) entry.contextWindow = contextWindow;
6118
+ if (outputLimit !== null) entry.outputLimit = outputLimit;
6119
+ metadata.push(entry);
6120
+ });
6121
+ if (metadata.length > 0) return metadata;
6122
+ return provider?.modelMetadata !== void 0 ? [] : void 0;
6123
+ }
5899
6124
  function handleSubmit(e) {
5900
6125
  e.preventDefault();
5901
6126
  if (!validate()) return;
@@ -5909,6 +6134,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
5909
6134
  openaiBaseUrl: openaiBaseUrl.trim() || void 0,
5910
6135
  apiDocsUrl: apiDocsUrl.trim() || void 0,
5911
6136
  modelMetadataUrl: modelMetadataUrl.trim() || void 0,
6137
+ modelMetadata: buildModelMetadata(),
5912
6138
  source
5913
6139
  });
5914
6140
  } finally {
@@ -6021,98 +6247,129 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
6021
6247
  "Models ",
6022
6248
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
6023
6249
  ] }),
6024
- models.map((m, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
6025
- "div",
6026
- {
6027
- ref: (el) => {
6028
- modelRowRefs.current[i] = el;
6029
- },
6030
- className: "flex items-center gap-2",
6031
- children: [
6032
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [
6033
- /* @__PURE__ */ jsxRuntimeExports.jsx(
6034
- "input",
6035
- {
6036
- type: "text",
6037
- value: m,
6038
- onChange: (e) => {
6039
- setModels((prev) => {
6040
- const next = [...prev];
6041
- next[i] = e.target.value;
6042
- return next;
6043
- });
6044
- },
6045
- placeholder: isMiniMax || isAlibaba ? "Type or select a model..." : "Model name",
6046
- className: "w-full rounded-md border border-input bg-background px-4 py-3 pr-8 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
6047
- }
6048
- ),
6049
- (isMiniMax || isAlibaba) && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6050
- /* @__PURE__ */ jsxRuntimeExports.jsx(
6051
- "button",
6052
- {
6053
- type: "button",
6054
- onClick: () => setOpenModelDropdown(openModelDropdown === i ? null : i),
6055
- className: "absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors p-1 z-10",
6056
- "aria-label": "Show model suggestions",
6057
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" })
6058
- }
6059
- ),
6060
- openModelDropdown === i && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-0 right-0 top-full mt-1 z-50 bg-popover border border-border rounded-md shadow-md max-h-48 overflow-y-auto", children: (isMiniMax ? MINIMAX_MODELS : ALIBABA_MODELS).map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx(
6250
+ models.map((m, i) => {
6251
+ const draft = modelMetadataDrafts[i] ?? emptyMetadataDraft();
6252
+ const contextWindowError = errors[`modelMetadata.${i}.contextWindow`];
6253
+ const outputLimitError = errors[`modelMetadata.${i}.outputLimit`];
6254
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
6255
+ "div",
6256
+ {
6257
+ ref: (el) => {
6258
+ modelRowRefs.current[i] = el;
6259
+ },
6260
+ className: "space-y-2 rounded-md border border-border bg-muted/10 p-2",
6261
+ children: [
6262
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
6263
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [
6264
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6265
+ "input",
6266
+ {
6267
+ type: "text",
6268
+ value: m,
6269
+ onChange: (e) => updateModel(i, e.target.value),
6270
+ placeholder: isMiniMax || isAlibaba ? "Type or select a model..." : "Model name",
6271
+ className: "w-full rounded-md border border-input bg-background px-4 py-3 pr-8 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
6272
+ }
6273
+ ),
6274
+ (isMiniMax || isAlibaba) && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6275
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6276
+ "button",
6277
+ {
6278
+ type: "button",
6279
+ onClick: () => setOpenModelDropdown(openModelDropdown === i ? null : i),
6280
+ className: "absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors p-1 z-10",
6281
+ "aria-label": "Show model suggestions",
6282
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" })
6283
+ }
6284
+ ),
6285
+ openModelDropdown === i && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-0 right-0 top-full mt-1 z-50 bg-popover border border-border rounded-md shadow-md max-h-48 overflow-y-auto", children: (isMiniMax ? MINIMAX_MODELS : ALIBABA_MODELS).map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx(
6286
+ "button",
6287
+ {
6288
+ type: "button",
6289
+ onClick: () => {
6290
+ updateModel(i, opt);
6291
+ setOpenModelDropdown(null);
6292
+ },
6293
+ className: "w-full text-left px-3 py-2 text-sm hover:bg-muted transition-colors",
6294
+ children: opt
6295
+ },
6296
+ opt
6297
+ )) })
6298
+ ] })
6299
+ ] }),
6300
+ models.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx(
6061
6301
  "button",
6062
6302
  {
6063
6303
  type: "button",
6064
- onClick: () => {
6065
- setModels((prev) => {
6066
- const next = [...prev];
6067
- next[i] = opt;
6068
- return next;
6069
- });
6070
- setOpenModelDropdown(null);
6071
- },
6072
- className: "w-full text-left px-3 py-2 text-sm hover:bg-muted transition-colors",
6073
- children: opt
6074
- },
6075
- opt
6076
- )) })
6077
- ] })
6078
- ] }),
6079
- models.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx(
6080
- "button",
6081
- {
6082
- type: "button",
6083
- onClick: () => setModels((prev) => prev.filter((_, idx) => idx !== i)),
6084
- className: "text-muted-foreground hover:text-destructive transition-colors p-1 shrink-0",
6085
- "aria-label": "Remove model",
6086
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
6087
- "svg",
6088
- {
6089
- xmlns: "http://www.w3.org/2000/svg",
6090
- width: "16",
6091
- height: "16",
6092
- viewBox: "0 0 24 24",
6093
- fill: "none",
6094
- stroke: "currentColor",
6095
- strokeWidth: "2",
6096
- strokeLinecap: "round",
6097
- strokeLinejoin: "round",
6098
- children: [
6099
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 6h18" }),
6100
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
6101
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
6102
- ]
6304
+ onClick: () => removeModel(i),
6305
+ className: "text-muted-foreground hover:text-destructive transition-colors p-1 shrink-0",
6306
+ "aria-label": "Remove model",
6307
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
6308
+ "svg",
6309
+ {
6310
+ xmlns: "http://www.w3.org/2000/svg",
6311
+ width: "16",
6312
+ height: "16",
6313
+ viewBox: "0 0 24 24",
6314
+ fill: "none",
6315
+ stroke: "currentColor",
6316
+ strokeWidth: "2",
6317
+ strokeLinecap: "round",
6318
+ strokeLinejoin: "round",
6319
+ children: [
6320
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 6h18" }),
6321
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
6322
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
6323
+ ]
6324
+ }
6325
+ )
6103
6326
  }
6104
6327
  )
6105
- }
6106
- )
6107
- ]
6108
- },
6109
- i
6110
- )),
6328
+ ] }),
6329
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-2 sm:grid-cols-2", children: [
6330
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "space-y-1 text-xs text-muted-foreground", children: [
6331
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Context Window" }),
6332
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6333
+ "input",
6334
+ {
6335
+ type: "number",
6336
+ min: 1,
6337
+ step: 1e3,
6338
+ value: draft.contextWindow,
6339
+ onChange: (e) => updateMetadataDraft(i, { contextWindow: e.target.value }),
6340
+ placeholder: "e.g. 1048576",
6341
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs text-foreground ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px]"
6342
+ }
6343
+ ),
6344
+ contextWindowError !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-destructive", children: contextWindowError })
6345
+ ] }),
6346
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "space-y-1 text-xs text-muted-foreground", children: [
6347
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Output Limit" }),
6348
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6349
+ "input",
6350
+ {
6351
+ type: "number",
6352
+ min: 1,
6353
+ step: 1e3,
6354
+ value: draft.outputLimit,
6355
+ onChange: (e) => updateMetadataDraft(i, { outputLimit: e.target.value }),
6356
+ placeholder: "e.g. 8192",
6357
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs text-foreground ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px]"
6358
+ }
6359
+ ),
6360
+ outputLimitError !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-destructive", children: outputLimitError })
6361
+ ] })
6362
+ ] })
6363
+ ]
6364
+ },
6365
+ i
6366
+ );
6367
+ }),
6111
6368
  /* @__PURE__ */ jsxRuntimeExports.jsx(
6112
6369
  "button",
6113
6370
  {
6114
6371
  type: "button",
6115
- onClick: () => setModels((prev) => [...prev, ""]),
6372
+ onClick: addModel,
6116
6373
  className: "text-xs text-primary hover:underline flex items-center gap-1",
6117
6374
  children: "+ Add Model"
6118
6375
  }
@@ -6234,6 +6491,7 @@ function createProviderPayload(data) {
6234
6491
  openaiBaseUrl: (data.openaiBaseUrl?.length ?? 0) > 0 ? data.openaiBaseUrl : void 0,
6235
6492
  apiDocsUrl: (data.apiDocsUrl?.length ?? 0) > 0 ? data.apiDocsUrl : void 0,
6236
6493
  modelMetadataUrl: (data.modelMetadataUrl?.length ?? 0) > 0 ? data.modelMetadataUrl : void 0,
6494
+ modelMetadata: data.modelMetadata,
6237
6495
  source: data.source
6238
6496
  };
6239
6497
  }
@@ -6771,30 +7029,6 @@ function ProvidersPanel({
6771
7029
  )
6772
7030
  ] });
6773
7031
  }
6774
- async function fetcher(url) {
6775
- return fetchJson(
6776
- url,
6777
- ProviderConfigSchema.array(),
6778
- void 0,
6779
- (response) => `Failed to fetch ${url}: ${response.status}`
6780
- );
6781
- }
6782
- function useProviders() {
6783
- const response = useSWR(
6784
- "/api/providers",
6785
- fetcher,
6786
- {
6787
- revalidateOnFocus: false,
6788
- revalidateIfStale: false
6789
- }
6790
- );
6791
- return {
6792
- providers: response.data ?? [],
6793
- isLoading: response.isLoading,
6794
- isError: response.error,
6795
- mutate: response.mutate
6796
- };
6797
- }
6798
7032
  function SettingsDialog() {
6799
7033
  const [open, setOpen] = reactExports.useState(false);
6800
7034
  const [activeTab, setActiveTab] = reactExports.useState("providers");
@@ -7284,6 +7518,851 @@ function useKeyboardNavigation(containerRef, wrapperRef, options = {}) {
7284
7518
  return () => document.removeEventListener("focusin", handleFocusContainer);
7285
7519
  }, [handleFocusContainer]);
7286
7520
  }
7521
+ const ANATOMY_ROLE_LABELS = {
7522
+ system: "System",
7523
+ user: "User",
7524
+ assistant: "Assistant",
7525
+ tool: "Tool Results",
7526
+ tools: "Tool Definitions"
7527
+ };
7528
+ const ROLE_COLOR_CLASSES = {
7529
+ system: "bg-sky-500/70",
7530
+ user: "bg-emerald-500/70",
7531
+ assistant: "bg-violet-500/70",
7532
+ tool: "bg-sky-400/55",
7533
+ tools: "bg-slate-500/70"
7534
+ };
7535
+ const ROLE_FOCUS_RING = {
7536
+ system: "focus-visible:ring-sky-300",
7537
+ user: "focus-visible:ring-emerald-300",
7538
+ assistant: "focus-visible:ring-violet-300",
7539
+ tool: "focus-visible:ring-sky-300",
7540
+ tools: "focus-visible:ring-slate-300"
7541
+ };
7542
+ const MAX_VISIBLE_SEGMENTS = 12;
7543
+ const MIN_SEGMENT_PERCENT = 1;
7544
+ const TOOLTIP_PREVIEW_LIMIT = 80;
7545
+ const LABEL_TRUNCATE_LIMIT = 24;
7546
+ function truncateLabel(label) {
7547
+ if (label.length <= LABEL_TRUNCATE_LIMIT) return label;
7548
+ return `${label.slice(0, LABEL_TRUNCATE_LIMIT - 3)}...`;
7549
+ }
7550
+ function truncatePreview(text) {
7551
+ const singleLine = text.replace(/\s+/g, " ").trim();
7552
+ if (singleLine.length <= TOOLTIP_PREVIEW_LIMIT) return singleLine;
7553
+ return `${singleLine.slice(0, TOOLTIP_PREVIEW_LIMIT)}...`;
7554
+ }
7555
+ function formatPercent$1(value) {
7556
+ if (value >= 10) return `${value.toFixed(0)}%`;
7557
+ if (value >= 1) return `${value.toFixed(1)}%`;
7558
+ if (value > 0) return "<1%";
7559
+ return "0%";
7560
+ }
7561
+ const SegmentBar = reactExports.memo(function SegmentBar2({
7562
+ segments,
7563
+ totalTokens,
7564
+ showLabels = true,
7565
+ onActivate
7566
+ }) {
7567
+ const total = reactExports.useMemo(() => {
7568
+ if (totalTokens > 0) return totalTokens;
7569
+ return segments.reduce((sum, segment2) => sum + segment2.size, 0);
7570
+ }, [segments, totalTokens]);
7571
+ const visibleSegments = segments.slice(0, MAX_VISIBLE_SEGMENTS);
7572
+ const overflowSegments = segments.slice(MAX_VISIBLE_SEGMENTS);
7573
+ const overflowSize = overflowSegments.reduce((sum, segment2) => sum + segment2.size, 0);
7574
+ const overflowCount = overflowSegments.length;
7575
+ const overflowStartIndex = MAX_VISIBLE_SEGMENTS;
7576
+ const overflowEndIndex = overflowStartIndex + overflowCount - 1;
7577
+ const hasOverflow = overflowCount > 0;
7578
+ const interactive = onActivate !== void 0;
7579
+ const visibleTotal = reactExports.useMemo(
7580
+ () => visibleSegments.reduce((sum, segment2) => sum + segment2.size, 0),
7581
+ [visibleSegments]
7582
+ );
7583
+ const ariaLabel = reactExports.useMemo(
7584
+ () => `Request context: ~${formatTokens(total)} tokens across ${segments.length} segment${segments.length === 1 ? "" : "s"}`,
7585
+ [segments.length, total]
7586
+ );
7587
+ if (segments.length === 0 || total <= 0) {
7588
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { role: "img", "aria-label": ariaLabel, className: "h-6" });
7589
+ }
7590
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { delayDuration: 150, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { role: "img", "aria-label": ariaLabel, className: "flex flex-col gap-1.5", children: [
7591
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
7592
+ "div",
7593
+ {
7594
+ className: "relative flex h-6 w-full overflow-hidden rounded border border-border/40 bg-muted/30",
7595
+ "data-testid": "anatomy-segment-bar",
7596
+ children: [
7597
+ visibleSegments.map((segment2, index) => {
7598
+ const rawPercent = total > 0 ? segment2.size / total * 100 : 0;
7599
+ const percent = Math.max(MIN_SEGMENT_PERCENT, rawPercent);
7600
+ const segmentClassName = cn(
7601
+ "h-full border-r border-background/80 last:border-r-0",
7602
+ interactive ? "opacity-90 hover:opacity-100 focus:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-background" : "opacity-90",
7603
+ ROLE_COLOR_CLASSES[segment2.role],
7604
+ interactive ? ROLE_FOCUS_RING[segment2.role] : ""
7605
+ );
7606
+ const segmentStyle = { width: `${percent}%` };
7607
+ const ariaText = `${segment2.label}, ${formatPercent$1(rawPercent)}, ~${formatTokens(
7608
+ segment2.size
7609
+ )} tokens`;
7610
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
7611
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: interactive ? /* @__PURE__ */ jsxRuntimeExports.jsx(
7612
+ "button",
7613
+ {
7614
+ type: "button",
7615
+ role: "button",
7616
+ tabIndex: 0,
7617
+ onClick: () => onActivate(segment2),
7618
+ onKeyDown: (event) => {
7619
+ if (event.key === "Enter" || event.key === " ") {
7620
+ event.preventDefault();
7621
+ onActivate(segment2);
7622
+ }
7623
+ },
7624
+ "data-anatomy-path": segment2.path,
7625
+ "aria-label": ariaText,
7626
+ className: segmentClassName,
7627
+ style: segmentStyle
7628
+ }
7629
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { "aria-label": ariaText, className: segmentClassName, style: segmentStyle }) }),
7630
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TooltipContent, { side: "bottom", className: "max-w-sm text-xs p-2 space-y-0.5", children: [
7631
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-semibold", children: [
7632
+ segment2.label,
7633
+ " - ",
7634
+ formatPercent$1(rawPercent),
7635
+ " - ~",
7636
+ formatTokens(segment2.size),
7637
+ " ",
7638
+ "tokens"
7639
+ ] }),
7640
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground", children: ANATOMY_ROLE_LABELS[segment2.role] }),
7641
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-muted-foreground", children: [
7642
+ segment2.characters.toLocaleString(),
7643
+ " chars"
7644
+ ] }),
7645
+ segment2.text.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground/90 break-words whitespace-pre-wrap", children: truncatePreview(segment2.text) })
7646
+ ] })
7647
+ ] }, `${segment2.path}-${index}`);
7648
+ }),
7649
+ hasOverflow && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
7650
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
7651
+ "div",
7652
+ {
7653
+ role: "img",
7654
+ "aria-label": `${overflowCount} additional segments`,
7655
+ className: "flex h-full items-center justify-center bg-muted-foreground/30 px-1.5 text-[10px] font-mono text-background",
7656
+ style: {
7657
+ width: `${Math.max(MIN_SEGMENT_PERCENT, overflowSize / total * 100)}%`
7658
+ },
7659
+ children: [
7660
+ "... +",
7661
+ overflowCount
7662
+ ]
7663
+ }
7664
+ ) }),
7665
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TooltipContent, { side: "bottom", className: "text-xs", children: [
7666
+ overflowCount,
7667
+ " more segment",
7668
+ overflowCount === 1 ? "" : "s",
7669
+ " (indices",
7670
+ " ",
7671
+ overflowStartIndex,
7672
+ "-",
7673
+ overflowEndIndex,
7674
+ ")"
7675
+ ] })
7676
+ ] })
7677
+ ]
7678
+ }
7679
+ ),
7680
+ showLabels && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex w-full gap-1 text-[10px] text-muted-foreground", children: [
7681
+ visibleSegments.map((segment2, index) => {
7682
+ const rawPercent = total > 0 ? segment2.size / total * 100 : 0;
7683
+ const percent = Math.max(MIN_SEGMENT_PERCENT, rawPercent);
7684
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
7685
+ "div",
7686
+ {
7687
+ className: "flex flex-col gap-0.5 truncate",
7688
+ style: { width: `${percent}%` },
7689
+ title: `${segment2.label} - ${formatPercent$1(rawPercent)} - ~${formatTokens(
7690
+ segment2.size
7691
+ )} tokens`,
7692
+ children: [
7693
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate font-mono text-foreground/80", children: truncateLabel(segment2.label) }),
7694
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate font-mono text-muted-foreground/70", children: [
7695
+ formatPercent$1(rawPercent),
7696
+ " - ~",
7697
+ formatTokens(segment2.size)
7698
+ ] })
7699
+ ]
7700
+ },
7701
+ `label-${segment2.path}-${index}`
7702
+ );
7703
+ }),
7704
+ hasOverflow && /* @__PURE__ */ jsxRuntimeExports.jsxs(
7705
+ "div",
7706
+ {
7707
+ className: "flex flex-col gap-0.5 truncate text-muted-foreground",
7708
+ style: { width: `${Math.max(MIN_SEGMENT_PERCENT, overflowSize / total * 100)}%` },
7709
+ children: [
7710
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate font-mono text-foreground/60", children: [
7711
+ "... +",
7712
+ overflowCount
7713
+ ] }),
7714
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate font-mono text-muted-foreground/60", children: [
7715
+ "~",
7716
+ formatTokens(overflowSize)
7717
+ ] })
7718
+ ]
7719
+ }
7720
+ )
7721
+ ] }),
7722
+ visibleTotal < total * 0.1 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-0" })
7723
+ ] }) });
7724
+ });
7725
+ const WATCH_USAGE_PERCENT = 0.75;
7726
+ const DANGER_USAGE_PERCENT = 0.9;
7727
+ const LONG_TOOL_SCHEMA_TOKENS = 8e3;
7728
+ const LARGE_ROLE_PERCENT = 0.35;
7729
+ const DUPLICATE_MIN_TOKENS = 40;
7730
+ const DUPLICATE_MIN_CHARS = 160;
7731
+ const DUPLICATE_NESTED_MAX_DEPTH = 5;
7732
+ const DUPLICATE_NESTED_MAX_CANDIDATES_PER_SEGMENT = 8;
7733
+ const HISTORY_BLOAT_SEGMENTS = 18;
7734
+ const HISTORY_BLOAT_TOKENS = 6e4;
7735
+ const HISTORY_BLOAT_PERCENT = 0.7;
7736
+ const SYSTEM_HEAVY_TOKENS = 3e3;
7737
+ const SYSTEM_HEAVY_PERCENT = 0.3;
7738
+ const DUPLICATE_PREVIEW_CHARS = 160;
7739
+ const DUPLICATE_NESTED_FIELD_NAMES = /* @__PURE__ */ new Set([
7740
+ "answer",
7741
+ "body",
7742
+ "content",
7743
+ "data",
7744
+ "error",
7745
+ "message",
7746
+ "markdown",
7747
+ "output",
7748
+ "response",
7749
+ "result",
7750
+ "stderr",
7751
+ "stdout",
7752
+ "text",
7753
+ "transcript"
7754
+ ]);
7755
+ const EXPLICIT_CONTEXT_WINDOW_FIELDS = [
7756
+ "context_window",
7757
+ "contextWindow",
7758
+ "context_length",
7759
+ "contextLength",
7760
+ "max_context_length",
7761
+ "maxContextLength",
7762
+ "max_input_tokens",
7763
+ "maxInputTokens"
7764
+ ];
7765
+ const OUTPUT_RESERVE_FIELDS = [
7766
+ "max_tokens",
7767
+ "maxTokens",
7768
+ "max_completion_tokens",
7769
+ "maxCompletionTokens",
7770
+ "max_output_tokens",
7771
+ "maxOutputTokens"
7772
+ ];
7773
+ const MODEL_CONTEXT_RULES = [
7774
+ {
7775
+ family: "OpenAI GPT-4.1",
7776
+ tokens: 1047576,
7777
+ mode: "prefix",
7778
+ patterns: ["gpt-4.1"]
7779
+ },
7780
+ {
7781
+ family: "OpenAI GPT-4o",
7782
+ tokens: 128e3,
7783
+ mode: "prefix",
7784
+ patterns: ["gpt-4o"]
7785
+ },
7786
+ {
7787
+ family: "OpenAI o-series",
7788
+ tokens: 128e3,
7789
+ mode: "prefix",
7790
+ patterns: ["o1", "o3", "o4"]
7791
+ },
7792
+ {
7793
+ family: "Anthropic Claude",
7794
+ tokens: 2e5,
7795
+ mode: "prefix",
7796
+ patterns: ["claude-"]
7797
+ },
7798
+ {
7799
+ family: "DeepSeek V4",
7800
+ tokens: 1048576,
7801
+ mode: "prefix",
7802
+ patterns: ["deepseek-v4-pro", "deepseek-v4-flash"]
7803
+ },
7804
+ {
7805
+ family: "DeepSeek",
7806
+ tokens: 64e3,
7807
+ mode: "prefix",
7808
+ patterns: ["deepseek-"]
7809
+ },
7810
+ {
7811
+ family: "MiniMax M3",
7812
+ tokens: 1048576,
7813
+ mode: "includes",
7814
+ patterns: ["minimax-m3"]
7815
+ },
7816
+ {
7817
+ family: "MiniMax M2.7",
7818
+ tokens: 204800,
7819
+ mode: "includes",
7820
+ patterns: ["minimax-m2.7"]
7821
+ },
7822
+ {
7823
+ family: "MiniMax M2.5/M2.1",
7824
+ tokens: 196608,
7825
+ mode: "includes",
7826
+ patterns: ["minimax-m2.5", "minimax-m2.1"]
7827
+ },
7828
+ {
7829
+ family: "GLM-5.2",
7830
+ tokens: 1048576,
7831
+ mode: "prefix",
7832
+ patterns: ["glm-5.2"]
7833
+ },
7834
+ {
7835
+ family: "GLM-5",
7836
+ tokens: 202752,
7837
+ mode: "prefix",
7838
+ patterns: ["glm-5", "glm-5.1"]
7839
+ }
7840
+ ];
7841
+ const ROLE_LABELS = {
7842
+ system: "System",
7843
+ user: "User messages",
7844
+ assistant: "Assistant messages",
7845
+ tool: "Tool results",
7846
+ tools: "Tool definitions"
7847
+ };
7848
+ function readPositiveInteger(value) {
7849
+ if (typeof value === "number") {
7850
+ if (Number.isFinite(value) && value > 0) return Math.floor(value);
7851
+ return null;
7852
+ }
7853
+ if (typeof value === "string") {
7854
+ const parsed = Number(value);
7855
+ if (Number.isFinite(parsed) && parsed > 0) return Math.floor(parsed);
7856
+ }
7857
+ return null;
7858
+ }
7859
+ function readPositiveIntegerField(parsed, fields) {
7860
+ for (const field of fields) {
7861
+ const value = readPositiveInteger(safeGetOwnProperty(parsed, field));
7862
+ if (value !== null) return value;
7863
+ }
7864
+ return null;
7865
+ }
7866
+ function readRequestModel(parsed) {
7867
+ const value = safeGetOwnProperty(parsed, "model");
7868
+ if (typeof value !== "string") return null;
7869
+ const trimmed = value.trim();
7870
+ return trimmed === "" ? null : trimmed;
7871
+ }
7872
+ function matchesRule(model, rule) {
7873
+ switch (rule.mode) {
7874
+ case "prefix":
7875
+ return rule.patterns.some((pattern) => model.startsWith(pattern));
7876
+ case "includes":
7877
+ return rule.patterns.some((pattern) => model.includes(pattern));
7878
+ }
7879
+ }
7880
+ function resolveContextWindow(model, parsed, providers = []) {
7881
+ const explicitWindow = readPositiveIntegerField(parsed, EXPLICIT_CONTEXT_WINDOW_FIELDS);
7882
+ if (explicitWindow !== null) {
7883
+ return {
7884
+ tokens: explicitWindow,
7885
+ label: "Request metadata",
7886
+ source: "request"
7887
+ };
7888
+ }
7889
+ const providerWindow = resolveProviderContextWindow(model, providers);
7890
+ if (providerWindow !== null) {
7891
+ return {
7892
+ tokens: providerWindow.tokens,
7893
+ label: `${providerWindow.providerName} config`,
7894
+ source: "provider"
7895
+ };
7896
+ }
7897
+ if (model === null) {
7898
+ return {
7899
+ tokens: null,
7900
+ label: "Unknown model",
7901
+ source: "unknown"
7902
+ };
7903
+ }
7904
+ const normalized = model.trim().toLowerCase().replace(/\s+/g, "-");
7905
+ for (const rule of MODEL_CONTEXT_RULES) {
7906
+ if (matchesRule(normalized, rule)) {
7907
+ return {
7908
+ tokens: rule.tokens,
7909
+ label: `${rule.family} local rule`,
7910
+ source: "model-rule"
7911
+ };
7912
+ }
7913
+ }
7914
+ return {
7915
+ tokens: null,
7916
+ label: "No local rule",
7917
+ source: "unknown"
7918
+ };
7919
+ }
7920
+ function clampPercent(value) {
7921
+ if (value < 0) return 0;
7922
+ if (value > 1) return 1;
7923
+ return value;
7924
+ }
7925
+ function riskLevel(usagePercent) {
7926
+ if (usagePercent === null) return "unknown";
7927
+ if (usagePercent >= DANGER_USAGE_PERCENT) return "danger";
7928
+ if (usagePercent >= WATCH_USAGE_PERCENT) return "watch";
7929
+ return "ok";
7930
+ }
7931
+ function sumSegments(segments, role) {
7932
+ return segments.filter((segment2) => segment2.role === role).reduce((sum, segment2) => sum + segment2.size, 0);
7933
+ }
7934
+ function buildRoleUsages(segments, total) {
7935
+ const roles = ["system", "user", "assistant", "tool", "tools"];
7936
+ return roles.map((role) => {
7937
+ const tokens = sumSegments(segments, role);
7938
+ return {
7939
+ role,
7940
+ label: ROLE_LABELS[role],
7941
+ tokens,
7942
+ percent: total > 0 ? tokens / total : 0
7943
+ };
7944
+ }).filter((usage) => usage.tokens > 0).sort((a, b) => b.tokens - a.tokens);
7945
+ }
7946
+ function normalizeDuplicateText(text) {
7947
+ return text.replace(/\s+/g, " ").trim().toLowerCase();
7948
+ }
7949
+ function duplicatePreview(text) {
7950
+ const singleLine = text.replace(/\s+/g, " ").trim();
7951
+ if (singleLine.length <= DUPLICATE_PREVIEW_CHARS) return singleLine;
7952
+ return `${singleLine.slice(0, DUPLICATE_PREVIEW_CHARS - 3)}...`;
7953
+ }
7954
+ function duplicateSegmentRef(candidate) {
7955
+ return {
7956
+ role: candidate.role,
7957
+ label: candidate.label,
7958
+ path: candidate.path,
7959
+ tokens: candidate.tokens,
7960
+ characters: candidate.characters
7961
+ };
7962
+ }
7963
+ function duplicateCandidateFromSegment(segment2) {
7964
+ return {
7965
+ role: segment2.role,
7966
+ label: segment2.label,
7967
+ path: segment2.path,
7968
+ tokens: segment2.size,
7969
+ characters: segment2.characters,
7970
+ text: segment2.text
7971
+ };
7972
+ }
7973
+ function jsonLikeText(text) {
7974
+ const trimmed = text.trim();
7975
+ return trimmed.startsWith("{") || trimmed.startsWith("[");
7976
+ }
7977
+ function parseJsonLikeText(text) {
7978
+ if (!jsonLikeText(text)) return null;
7979
+ try {
7980
+ const parsed = JSON.parse(text);
7981
+ return parsed;
7982
+ } catch {
7983
+ return null;
7984
+ }
7985
+ }
7986
+ function normalizePathFieldName(field) {
7987
+ return field.toLowerCase().replace(/[^a-z0-9]/g, "");
7988
+ }
7989
+ function lastNamedPathField(path) {
7990
+ for (let index = path.length - 1; index >= 0; index -= 1) {
7991
+ const field = path[index] ?? "";
7992
+ if (field.startsWith("[") && field.endsWith("]")) continue;
7993
+ return field;
7994
+ }
7995
+ return null;
7996
+ }
7997
+ function nestedDuplicateFieldPath(path) {
7998
+ const field = lastNamedPathField(path);
7999
+ if (field === null) return false;
8000
+ return DUPLICATE_NESTED_FIELD_NAMES.has(normalizePathFieldName(field));
8001
+ }
8002
+ function formatNestedDuplicatePath(path) {
8003
+ return path.map((part) => part.startsWith("[") ? part : `.${part}`).join("").replace(/^\./, "");
8004
+ }
8005
+ function duplicateCandidateFromNestedText(segment2, text, path) {
8006
+ const characters = countCharacters(text);
8007
+ if (characters < DUPLICATE_MIN_CHARS) return null;
8008
+ const tokens = estimateTokens(text);
8009
+ if (tokens < DUPLICATE_MIN_TOKENS) return null;
8010
+ return {
8011
+ role: segment2.role,
8012
+ label: `${segment2.label} ${formatNestedDuplicatePath(path)}`,
8013
+ path: segment2.path,
8014
+ tokens,
8015
+ characters,
8016
+ text
8017
+ };
8018
+ }
8019
+ function collectNestedDuplicateCandidates({
8020
+ value,
8021
+ segment: segment2,
8022
+ path,
8023
+ depth,
8024
+ candidates
8025
+ }) {
8026
+ if (candidates.length >= DUPLICATE_NESTED_MAX_CANDIDATES_PER_SEGMENT) return;
8027
+ if (depth > DUPLICATE_NESTED_MAX_DEPTH) return;
8028
+ if (typeof value === "string") {
8029
+ if (!nestedDuplicateFieldPath(path)) return;
8030
+ const candidate = duplicateCandidateFromNestedText(segment2, value, path);
8031
+ if (candidate !== null) candidates.push(candidate);
8032
+ return;
8033
+ }
8034
+ if (Array.isArray(value)) {
8035
+ value.forEach(
8036
+ (item, index) => collectNestedDuplicateCandidates({
8037
+ value: item,
8038
+ segment: segment2,
8039
+ path: [...path, `[${String(index)}]`],
8040
+ depth: depth + 1,
8041
+ candidates
8042
+ })
8043
+ );
8044
+ return;
8045
+ }
8046
+ if (!isPlainRecord(value)) return;
8047
+ Object.entries(value).forEach(
8048
+ ([key, child]) => collectNestedDuplicateCandidates({
8049
+ value: child,
8050
+ segment: segment2,
8051
+ path: [...path, key],
8052
+ depth: depth + 1,
8053
+ candidates
8054
+ })
8055
+ );
8056
+ }
8057
+ function duplicateCandidatesForSegment(segment2) {
8058
+ const segmentCandidate = duplicateCandidateFromSegment(segment2);
8059
+ const parsed = parseJsonLikeText(segment2.text);
8060
+ if (parsed === null) return [segmentCandidate];
8061
+ const candidates = [];
8062
+ collectNestedDuplicateCandidates({
8063
+ value: parsed,
8064
+ segment: segment2,
8065
+ path: [],
8066
+ depth: 0,
8067
+ candidates
8068
+ });
8069
+ const normalizedSegment = normalizeDuplicateText(segment2.text);
8070
+ const seen = /* @__PURE__ */ new Set([normalizedSegment]);
8071
+ const uniqueNested = candidates.filter((candidate) => {
8072
+ const normalized = normalizeDuplicateText(candidate.text);
8073
+ if (normalized === "") return false;
8074
+ if (seen.has(normalized)) return false;
8075
+ seen.add(normalized);
8076
+ return true;
8077
+ });
8078
+ return [segmentCandidate, ...uniqueNested];
8079
+ }
8080
+ function duplicateContextGroups(segments) {
8081
+ const buckets = /* @__PURE__ */ new Map();
8082
+ for (const segment2 of segments) {
8083
+ const candidates = duplicateCandidatesForSegment(segment2);
8084
+ for (const candidate of candidates) {
8085
+ if (candidate.tokens < DUPLICATE_MIN_TOKENS) continue;
8086
+ if (candidate.characters < DUPLICATE_MIN_CHARS) continue;
8087
+ const normalized = normalizeDuplicateText(candidate.text);
8088
+ if (normalized === "") continue;
8089
+ const existing = buckets.get(normalized);
8090
+ if (existing === void 0) {
8091
+ buckets.set(normalized, {
8092
+ tokens: candidate.tokens,
8093
+ candidates: [candidate]
8094
+ });
8095
+ continue;
8096
+ }
8097
+ buckets.set(normalized, {
8098
+ tokens: existing.tokens + candidate.tokens,
8099
+ candidates: [...existing.candidates, candidate]
8100
+ });
8101
+ }
8102
+ }
8103
+ const groups = [];
8104
+ for (const bucket of buckets.values()) {
8105
+ if (bucket.candidates.length < 2) continue;
8106
+ const firstCandidate = bucket.candidates[0] ?? null;
8107
+ if (firstCandidate === null) continue;
8108
+ const repeatedTokens = bucket.tokens - firstCandidate.tokens;
8109
+ groups.push({
8110
+ key: `${firstCandidate.path}:${firstCandidate.label}:${String(
8111
+ bucket.candidates.length
8112
+ )}:${String(bucket.tokens)}`,
8113
+ count: bucket.candidates.length,
8114
+ totalTokens: bucket.tokens,
8115
+ repeatedTokens,
8116
+ preview: duplicatePreview(firstCandidate.text),
8117
+ firstLabel: firstCandidate.label,
8118
+ segments: bucket.candidates.map(duplicateSegmentRef)
8119
+ });
8120
+ }
8121
+ return groups.sort(
8122
+ (left, right) => right.repeatedTokens - left.repeatedTokens || right.totalTokens - left.totalTokens
8123
+ );
8124
+ }
8125
+ function duplicateDiagnostic(duplicateGroups) {
8126
+ if (duplicateGroups.length === 0) return null;
8127
+ const repeatedGroups = duplicateGroups.length;
8128
+ const repeatedSegments = duplicateGroups.reduce((sum, group) => sum + group.count, 0);
8129
+ const repeatedTokens = duplicateGroups.reduce((sum, group) => sum + group.repeatedTokens, 0);
8130
+ return {
8131
+ kind: "duplicate",
8132
+ severity: repeatedTokens >= LONG_TOOL_SCHEMA_TOKENS ? "danger" : "watch",
8133
+ title: "Duplicate context",
8134
+ detail: `${String(repeatedSegments)} repeated blocks across ${String(
8135
+ repeatedGroups
8136
+ )} group${repeatedGroups === 1 ? "" : "s"}, ~${String(repeatedTokens)} repeated tokens.`
8137
+ };
8138
+ }
8139
+ function duplicateReclaimableTokens(duplicateGroups) {
8140
+ return duplicateGroups.reduce((sum, group) => sum + group.repeatedTokens, 0);
8141
+ }
8142
+ function diagnosticsForRolePressure(roleUsages, total, messageSegmentCount) {
8143
+ const diagnostics = [];
8144
+ const toolUsage = roleUsages.find((usage) => usage.role === "tools") ?? null;
8145
+ if (toolUsage !== null && (toolUsage.tokens >= LONG_TOOL_SCHEMA_TOKENS || toolUsage.percent >= LARGE_ROLE_PERCENT && toolUsage.tokens >= SYSTEM_HEAVY_TOKENS)) {
8146
+ diagnostics.push({
8147
+ kind: "tool-schema",
8148
+ severity: toolUsage.tokens >= LONG_TOOL_SCHEMA_TOKENS ? "danger" : "watch",
8149
+ title: "Large tool schema",
8150
+ detail: `Tool definitions take ${Math.round(toolUsage.percent * 100)}% of request context.`
8151
+ });
8152
+ }
8153
+ const systemUsage = roleUsages.find((usage) => usage.role === "system") ?? null;
8154
+ if (systemUsage !== null && systemUsage.tokens >= SYSTEM_HEAVY_TOKENS && systemUsage.percent >= SYSTEM_HEAVY_PERCENT) {
8155
+ diagnostics.push({
8156
+ kind: "system-heavy",
8157
+ severity: "watch",
8158
+ title: "Large system prompt",
8159
+ detail: `System instructions take ${Math.round(systemUsage.percent * 100)}% of request context.`
8160
+ });
8161
+ }
8162
+ const historyTokens = roleUsages.filter((usage) => usage.role === "user" || usage.role === "assistant" || usage.role === "tool").reduce((sum, usage) => sum + usage.tokens, 0);
8163
+ const historyPercent = total > 0 ? historyTokens / total : 0;
8164
+ if (messageSegmentCount >= HISTORY_BLOAT_SEGMENTS && (historyTokens >= HISTORY_BLOAT_TOKENS || historyPercent >= HISTORY_BLOAT_PERCENT)) {
8165
+ diagnostics.push({
8166
+ kind: "history-bloat",
8167
+ severity: historyPercent >= 0.85 ? "danger" : "watch",
8168
+ title: "Conversation history growth",
8169
+ detail: `${String(messageSegmentCount)} message blocks take ${Math.round(
8170
+ historyPercent * 100
8171
+ )}% of request context.`
8172
+ });
8173
+ }
8174
+ return diagnostics;
8175
+ }
8176
+ function contextHealth({
8177
+ risk,
8178
+ contextWindow,
8179
+ diagnostics,
8180
+ duplicateGroups
8181
+ }) {
8182
+ const diagnosticKinds = new Set(diagnostics.map((diagnostic) => diagnostic.kind));
8183
+ let opportunityCount = diagnosticKinds.size;
8184
+ if (risk === "watch" || risk === "danger") opportunityCount += 1;
8185
+ if (contextWindow.source === "unknown") opportunityCount += 1;
8186
+ const hasDangerDiagnostic = diagnostics.some((diagnostic) => diagnostic.severity === "danger");
8187
+ const level = risk === "danger" || hasDangerDiagnostic ? "risk" : opportunityCount > 0 ? "optimizable" : "ok";
8188
+ return {
8189
+ level,
8190
+ opportunityCount,
8191
+ reclaimableTokens: duplicateReclaimableTokens(duplicateGroups)
8192
+ };
8193
+ }
8194
+ function analyzeContextIntelligence({
8195
+ segments,
8196
+ inputTokens,
8197
+ parsed,
8198
+ model,
8199
+ providers = []
8200
+ }) {
8201
+ const totalEstimatedTokens = segments.reduce((sum, segment2) => sum + segment2.size, 0);
8202
+ const requestModel = model ?? readRequestModel(parsed);
8203
+ const estimatedInputTokens = inputTokens ?? totalEstimatedTokens;
8204
+ const outputReserveTokens = readPositiveIntegerField(parsed, OUTPUT_RESERVE_FIELDS);
8205
+ const contextWindow = resolveContextWindow(requestModel, parsed, providers);
8206
+ const reservedTokens = outputReserveTokens ?? 0;
8207
+ const windowUsedTokens = contextWindow.tokens === null ? null : estimatedInputTokens + reservedTokens;
8208
+ const remainingInputTokens = contextWindow.tokens === null ? null : contextWindow.tokens - estimatedInputTokens;
8209
+ const remainingAfterReserveTokens = contextWindow.tokens === null ? null : contextWindow.tokens - estimatedInputTokens - reservedTokens;
8210
+ const usagePercent = contextWindow.tokens === null || windowUsedTokens === null ? null : clampPercent(windowUsedTokens / contextWindow.tokens);
8211
+ const roleUsages = buildRoleUsages(segments, totalEstimatedTokens);
8212
+ const largestRole = roleUsages[0] ?? null;
8213
+ const messageSegmentCount = segments.filter((segment2) => segment2.role !== "tools").length;
8214
+ const duplicateGroups = duplicateContextGroups(segments);
8215
+ const duplicate = duplicateDiagnostic(duplicateGroups);
8216
+ const diagnostics = diagnosticsForRolePressure(
8217
+ roleUsages,
8218
+ totalEstimatedTokens,
8219
+ messageSegmentCount
8220
+ );
8221
+ if (duplicate !== null) diagnostics.unshift(duplicate);
8222
+ const risk = riskLevel(usagePercent);
8223
+ return {
8224
+ model: requestModel,
8225
+ contextWindow,
8226
+ estimatedInputTokens,
8227
+ outputReserveTokens,
8228
+ windowUsedTokens,
8229
+ remainingInputTokens,
8230
+ remainingAfterReserveTokens,
8231
+ usagePercent,
8232
+ riskLevel: risk,
8233
+ roleUsages,
8234
+ largestRole,
8235
+ diagnostics,
8236
+ duplicateGroups,
8237
+ health: contextHealth({
8238
+ risk,
8239
+ contextWindow,
8240
+ diagnostics,
8241
+ duplicateGroups
8242
+ })
8243
+ };
8244
+ }
8245
+ const CONTEXT_USAGE_THRESHOLDS = {
8246
+ watch: WATCH_USAGE_PERCENT,
8247
+ danger: DANGER_USAGE_PERCENT
8248
+ };
8249
+ function parseRequestBody(rawBody) {
8250
+ if (rawBody === null) return null;
8251
+ try {
8252
+ const parsed = JSON.parse(rawBody);
8253
+ return parsed;
8254
+ } catch {
8255
+ return null;
8256
+ }
8257
+ }
8258
+ function roleUsageSegment(usage) {
8259
+ return {
8260
+ role: usage.role,
8261
+ label: usage.label,
8262
+ size: usage.tokens,
8263
+ characters: usage.tokens * 4,
8264
+ text: "",
8265
+ path: `role:${usage.role}`
8266
+ };
8267
+ }
8268
+ function snapshotFromLog(log, providers) {
8269
+ const parsed = parseRequestBody(log.rawRequestBody);
8270
+ if (parsed === null) return null;
8271
+ const adapter = getLogFormatAdapter(resolveLogFormat(log));
8272
+ const segments = adapter.anatomySegments(parsed);
8273
+ if (segments === null) return null;
8274
+ const intelligence = analyzeContextIntelligence({
8275
+ segments,
8276
+ inputTokens: log.inputTokens ?? null,
8277
+ parsed,
8278
+ model: log.model,
8279
+ providers
8280
+ });
8281
+ const model = intelligence.model ?? log.model ?? "Unknown model";
8282
+ const roleSegments = intelligence.roleUsages.map(roleUsageSegment);
8283
+ return {
8284
+ logId: log.id,
8285
+ model,
8286
+ timestamp: log.timestamp,
8287
+ contextWindow: intelligence.contextWindow,
8288
+ estimatedInputTokens: intelligence.estimatedInputTokens,
8289
+ windowUsedTokens: intelligence.windowUsedTokens,
8290
+ usagePercent: intelligence.usagePercent,
8291
+ riskLevel: intelligence.riskLevel,
8292
+ health: intelligence.health,
8293
+ diagnostics: intelligence.diagnostics.map((diagnostic) => diagnostic.title),
8294
+ roleSegments,
8295
+ roleTotalTokens: roleSegments.reduce((sum, segment2) => sum + segment2.size, 0)
8296
+ };
8297
+ }
8298
+ function isHigherPeak(candidate, current) {
8299
+ if (candidate.usagePercent !== null && current.usagePercent !== null) {
8300
+ return candidate.usagePercent > current.usagePercent;
8301
+ }
8302
+ if (candidate.usagePercent !== null && current.usagePercent === null) return true;
8303
+ if (candidate.usagePercent === null && current.usagePercent !== null) return false;
8304
+ return candidate.estimatedInputTokens > current.estimatedInputTokens;
8305
+ }
8306
+ function riskRank(risk) {
8307
+ switch (risk) {
8308
+ case "danger":
8309
+ return 4;
8310
+ case "watch":
8311
+ return 3;
8312
+ case "unknown":
8313
+ return 2;
8314
+ case "ok":
8315
+ return 1;
8316
+ }
8317
+ }
8318
+ function compareModelSummary(left, right) {
8319
+ const riskDelta = riskRank(right.latest.riskLevel) - riskRank(left.latest.riskLevel);
8320
+ if (riskDelta !== 0) return riskDelta;
8321
+ const leftPeak = left.peak.usagePercent ?? -1;
8322
+ const rightPeak = right.peak.usagePercent ?? -1;
8323
+ const peakDelta = rightPeak - leftPeak;
8324
+ if (peakDelta !== 0) return peakDelta;
8325
+ return right.latest.logId - left.latest.logId;
8326
+ }
8327
+ function trendPercent(first, latest) {
8328
+ if (first.usagePercent === null || latest.usagePercent === null) return null;
8329
+ return (latest.usagePercent - first.usagePercent) * 100;
8330
+ }
8331
+ function buildSessionContextSummary(logs, providers = []) {
8332
+ const sortedLogs = [...logs].sort((left, right) => left.id - right.id);
8333
+ const byModel = /* @__PURE__ */ new Map();
8334
+ for (const log of sortedLogs) {
8335
+ const snapshot = snapshotFromLog(log, providers);
8336
+ if (snapshot === null) continue;
8337
+ const existing = byModel.get(snapshot.model);
8338
+ if (existing === void 0) {
8339
+ byModel.set(snapshot.model, {
8340
+ model: snapshot.model,
8341
+ requestCount: 1,
8342
+ first: snapshot,
8343
+ latest: snapshot,
8344
+ peak: snapshot
8345
+ });
8346
+ continue;
8347
+ }
8348
+ byModel.set(snapshot.model, {
8349
+ model: existing.model,
8350
+ requestCount: existing.requestCount + 1,
8351
+ first: existing.first,
8352
+ latest: snapshot,
8353
+ peak: isHigherPeak(snapshot, existing.peak) ? snapshot : existing.peak
8354
+ });
8355
+ }
8356
+ const models = [...byModel.values()].map((entry) => ({
8357
+ model: entry.model,
8358
+ requestCount: entry.requestCount,
8359
+ first: entry.first,
8360
+ latest: entry.latest,
8361
+ peak: entry.peak,
8362
+ trendPercent: trendPercent(entry.first, entry.latest)
8363
+ })).sort(compareModelSummary);
8364
+ return { models };
8365
+ }
7287
8366
  function truncateSessionId(id) {
7288
8367
  if (id.length <= 30) return id;
7289
8368
  return id.slice(0, 12) + "…" + id.slice(-12);
@@ -7311,6 +8390,142 @@ function getFirstUserAgent(logs) {
7311
8390
  }
7312
8391
  return null;
7313
8392
  }
8393
+ function resolveSessionContextScope({
8394
+ pinnedSessionId,
8395
+ selectedSession,
8396
+ sessions,
8397
+ logs
8398
+ }) {
8399
+ if (pinnedSessionId !== void 0) {
8400
+ const scopedLogs = logs.filter((log) => log.sessionId === pinnedSessionId);
8401
+ if (scopedLogs.length === 0) return null;
8402
+ return { label: pinnedSessionId, logs: scopedLogs, showBackLink: true };
8403
+ }
8404
+ if (selectedSession !== "__all__") {
8405
+ const scopedLogs = logs.filter((log) => log.sessionId === selectedSession);
8406
+ if (scopedLogs.length === 0) return null;
8407
+ return { label: selectedSession, logs: scopedLogs, showBackLink: true };
8408
+ }
8409
+ const singleSessionId = sessions.length === 1 ? sessions[0] ?? null : null;
8410
+ if (singleSessionId !== null) {
8411
+ const allVisibleLogsBelongToSession = logs.every((log) => log.sessionId === singleSessionId);
8412
+ if (allVisibleLogsBelongToSession) {
8413
+ return { label: singleSessionId, logs: [...logs], showBackLink: false };
8414
+ }
8415
+ }
8416
+ const allVisibleLogsAreSessionless = sessions.length === 0 && logs.length > 0 && logs.every((log) => log.sessionId === null);
8417
+ if (allVisibleLogsAreSessionless) {
8418
+ return { label: "Visible requests", logs: [...logs], showBackLink: false };
8419
+ }
8420
+ return null;
8421
+ }
8422
+ function formatPercent(value) {
8423
+ if (value === null) return "Unknown";
8424
+ const percent = value * 100;
8425
+ if (percent >= 10) return `${percent.toFixed(0)}%`;
8426
+ if (percent >= 1) return `${percent.toFixed(1)}%`;
8427
+ if (percent > 0) return "<1%";
8428
+ return "0%";
8429
+ }
8430
+ function formatTrend(value) {
8431
+ if (value === null) return "n/a";
8432
+ if (Math.abs(value) < 0.1) return "0%";
8433
+ const sign = value > 0 ? "+" : "";
8434
+ return `${sign}${value.toFixed(1)}%`;
8435
+ }
8436
+ function progressPercent(value) {
8437
+ if (value === null || !Number.isFinite(value)) return 0;
8438
+ return Math.min(Math.max(value * 100, 0), 100);
8439
+ }
8440
+ function peakMarkerPercent(latestPercent, peakPercent) {
8441
+ if (peakPercent === null) return null;
8442
+ const latest = progressPercent(latestPercent);
8443
+ const peak = progressPercent(peakPercent);
8444
+ if (peak <= 0) return null;
8445
+ if (Math.abs(peak - latest) < 0.5) return null;
8446
+ return peak;
8447
+ }
8448
+ function sessionRiskBarClass(risk) {
8449
+ switch (risk) {
8450
+ case "danger":
8451
+ return "bg-red-400";
8452
+ case "watch":
8453
+ return "bg-amber-300";
8454
+ case "unknown":
8455
+ return "bg-muted-foreground/50";
8456
+ case "ok":
8457
+ return "bg-emerald-400";
8458
+ }
8459
+ }
8460
+ function formatContextWindow(summary) {
8461
+ const tokens = summary.latest.contextWindow.tokens;
8462
+ if (tokens === null) return "Window unknown";
8463
+ return `Window ${formatContextWindowTokens(tokens)}`;
8464
+ }
8465
+ function formatLogReference(logId) {
8466
+ return `#${String(logId)}`;
8467
+ }
8468
+ function sessionRiskClass(risk) {
8469
+ switch (risk) {
8470
+ case "danger":
8471
+ return "text-red-300";
8472
+ case "watch":
8473
+ return "text-amber-200";
8474
+ case "unknown":
8475
+ return "text-muted-foreground";
8476
+ case "ok":
8477
+ return "text-emerald-300";
8478
+ }
8479
+ }
8480
+ function sessionHealthClass(level) {
8481
+ switch (level) {
8482
+ case "risk":
8483
+ return "border-red-400/30 bg-red-500/10 text-red-100";
8484
+ case "optimizable":
8485
+ return "border-amber-400/25 bg-amber-400/8 text-amber-100";
8486
+ case "ok":
8487
+ return "border-emerald-400/25 bg-emerald-400/8 text-emerald-100";
8488
+ }
8489
+ }
8490
+ function sessionHealthLabel(level) {
8491
+ switch (level) {
8492
+ case "risk":
8493
+ return "Risk";
8494
+ case "optimizable":
8495
+ return "Optimizable";
8496
+ case "ok":
8497
+ return "OK";
8498
+ }
8499
+ }
8500
+ const LOG_FOCUS_RETRY_LIMIT = 10;
8501
+ const LOG_FOCUS_RETRY_DELAY_MS = 80;
8502
+ function highlightLogTarget(target) {
8503
+ target.setAttribute("data-deep-link-highlight", "true");
8504
+ window.setTimeout(() => {
8505
+ target.removeAttribute("data-deep-link-highlight");
8506
+ }, 1800);
8507
+ }
8508
+ function focusLogTarget(logId) {
8509
+ const target = document.getElementById(`log-${String(logId)}`);
8510
+ if (!(target instanceof HTMLElement)) return false;
8511
+ target.scrollIntoView({ block: "center", behavior: "smooth" });
8512
+ target.focus({ preventScroll: true });
8513
+ highlightLogTarget(target);
8514
+ return true;
8515
+ }
8516
+ function openLogContext(logId) {
8517
+ dispatchLogFocusRequest({ logId, tab: "anatomy" });
8518
+ let attempts = 0;
8519
+ const tryFocus = () => {
8520
+ dispatchLogFocusRequest({ logId, tab: "anatomy" });
8521
+ if (focusLogTarget(logId)) return;
8522
+ attempts += 1;
8523
+ if (attempts < LOG_FOCUS_RETRY_LIMIT) {
8524
+ window.setTimeout(tryFocus, LOG_FOCUS_RETRY_DELAY_MS);
8525
+ }
8526
+ };
8527
+ window.setTimeout(tryFocus, 0);
8528
+ }
7314
8529
  function CopyableCommand({ command }) {
7315
8530
  const [copied, setCopied] = reactExports.useState(false);
7316
8531
  const handleCopy = reactExports.useCallback(() => {
@@ -7372,12 +8587,202 @@ function McpReadyBadge() {
7372
8587
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { sideOffset: 8, className: "max-w-[320px] text-left leading-relaxed", children: "Coding agents can inspect logs, replay requests, test providers, and debug sessions through MCP at /api/mcp." })
7373
8588
  ] }) });
7374
8589
  }
8590
+ function SessionContextLogButton({ label, logId }) {
8591
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
8592
+ "button",
8593
+ {
8594
+ type: "button",
8595
+ onClick: () => openLogContext(logId),
8596
+ className: "inline-flex h-5 items-center rounded px-1 font-mono text-[10px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
8597
+ title: `Open Context for ${formatLogReference(logId)}`,
8598
+ children: [
8599
+ label,
8600
+ " ",
8601
+ formatLogReference(logId)
8602
+ ]
8603
+ }
8604
+ );
8605
+ }
8606
+ function SessionContextProgressBar({
8607
+ latestPercent,
8608
+ peakPercent,
8609
+ risk,
8610
+ onOpenContext
8611
+ }) {
8612
+ const latest = progressPercent(latestPercent);
8613
+ const peakMarker = peakMarkerPercent(latestPercent, peakPercent);
8614
+ const ariaLabel = peakPercent === null ? `Latest context window usage ${formatPercent(latestPercent)}` : `Latest context window usage ${formatPercent(latestPercent)}, peak ${formatPercent(
8615
+ peakPercent
8616
+ )}`;
8617
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
8618
+ "button",
8619
+ {
8620
+ type: "button",
8621
+ onClick: onOpenContext,
8622
+ className: "group block w-full rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
8623
+ "aria-label": `${ariaLabel}. Open latest Context details`,
8624
+ title: "Open latest Context details",
8625
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
8626
+ "span",
8627
+ {
8628
+ className: "relative block h-2.5 overflow-hidden rounded-full border border-border/70 bg-background transition-colors group-hover:border-foreground/30",
8629
+ role: "meter",
8630
+ "aria-label": ariaLabel,
8631
+ "aria-valuemin": 0,
8632
+ "aria-valuemax": 100,
8633
+ "aria-valuenow": Math.round(latest),
8634
+ children: [
8635
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
8636
+ "span",
8637
+ {
8638
+ className: cn("block h-full rounded-full transition-[width]", sessionRiskBarClass(risk)),
8639
+ style: { width: `${latest}%` }
8640
+ }
8641
+ ),
8642
+ peakMarker !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
8643
+ "span",
8644
+ {
8645
+ className: "absolute top-0 h-full w-0.5 rounded-full bg-amber-100/75 shadow-[0_0_0_1px_rgba(0,0,0,0.25)]",
8646
+ style: { left: `calc(${peakMarker}% - 0.5px)` },
8647
+ "aria-hidden": "true"
8648
+ }
8649
+ )
8650
+ ]
8651
+ }
8652
+ )
8653
+ }
8654
+ );
8655
+ }
8656
+ function SessionModelContextRow({ summary }) {
8657
+ const trend = summary.trendPercent;
8658
+ const TrendIcon = trend === null || Math.abs(trend) < 0.1 ? null : trend > 0 ? ArrowUpRight : ArrowDownRight;
8659
+ const diagnosticLabel = summary.latest.diagnostics[0] ?? null;
8660
+ const latestUsage = formatPercent(summary.latest.usagePercent);
8661
+ const peakUsage = formatPercent(summary.peak.usagePercent);
8662
+ const openLatestContext = () => {
8663
+ openLogContext(summary.latest.logId);
8664
+ };
8665
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-x-4 gap-y-2 border-t border-border/60 py-2 first:border-t-0 lg:grid-cols-[minmax(180px,1fr)_minmax(260px,1fr)_minmax(260px,1.4fr)]", children: [
8666
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
8667
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
8668
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
8669
+ "button",
8670
+ {
8671
+ type: "button",
8672
+ onClick: openLatestContext,
8673
+ className: "min-w-0 truncate rounded-sm text-left font-mono font-semibold text-foreground transition-colors hover:text-cyan-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
8674
+ title: `Open latest Context for ${summary.model}`,
8675
+ children: summary.model
8676
+ }
8677
+ ),
8678
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
8679
+ "span",
8680
+ {
8681
+ className: cn(
8682
+ "inline-flex h-5 shrink-0 items-center rounded border px-1.5 font-mono text-[10px]",
8683
+ sessionHealthClass(summary.latest.health.level)
8684
+ ),
8685
+ children: sessionHealthLabel(summary.latest.health.level)
8686
+ }
8687
+ )
8688
+ ] }),
8689
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-[11px] text-muted-foreground", children: [
8690
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
8691
+ summary.requestCount,
8692
+ " req"
8693
+ ] }),
8694
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: formatContextWindow(summary) }),
8695
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", title: summary.latest.contextWindow.label, children: summary.latest.contextWindow.label })
8696
+ ] })
8697
+ ] }),
8698
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 space-y-2 text-xs", children: [
8699
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
8700
+ SessionContextProgressBar,
8701
+ {
8702
+ latestPercent: summary.latest.usagePercent,
8703
+ peakPercent: summary.peak.usagePercent,
8704
+ risk: summary.latest.riskLevel,
8705
+ onOpenContext: openLatestContext
8706
+ }
8707
+ ),
8708
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
8709
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
8710
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Latest" }),
8711
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("mt-0.5 font-mono", sessionRiskClass(summary.latest.riskLevel)), children: latestUsage }),
8712
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SessionContextLogButton, { label: "Log", logId: summary.latest.logId })
8713
+ ] }),
8714
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
8715
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Peak" }),
8716
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("mt-0.5 font-mono", sessionRiskClass(summary.peak.riskLevel)), children: peakUsage }),
8717
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SessionContextLogButton, { label: "Log", logId: summary.peak.logId })
8718
+ ] }),
8719
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
8720
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Trend" }),
8721
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-0.5 flex items-center gap-1 font-mono text-foreground", children: [
8722
+ TrendIcon !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
8723
+ TrendIcon,
8724
+ {
8725
+ className: cn(
8726
+ "size-3",
8727
+ trend !== null && trend > 0 ? "text-amber-300" : "text-emerald-300"
8728
+ )
8729
+ }
8730
+ ),
8731
+ formatTrend(trend)
8732
+ ] }),
8733
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-5", "aria-hidden": "true" })
8734
+ ] })
8735
+ ] })
8736
+ ] }),
8737
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 space-y-1.5", children: [
8738
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
8739
+ SegmentBar,
8740
+ {
8741
+ segments: summary.latest.roleSegments,
8742
+ totalTokens: summary.latest.roleTotalTokens,
8743
+ showLabels: false
8744
+ }
8745
+ ),
8746
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-[11px] text-muted-foreground", children: [
8747
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
8748
+ "~",
8749
+ formatTokens(summary.latest.estimatedInputTokens),
8750
+ " input"
8751
+ ] }),
8752
+ summary.latest.windowUsedTokens !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
8753
+ "~",
8754
+ formatTokens(summary.latest.windowUsedTokens),
8755
+ " used"
8756
+ ] }),
8757
+ diagnosticLabel !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: diagnosticLabel })
8758
+ ] })
8759
+ ] })
8760
+ ] });
8761
+ }
8762
+ function SessionContextSummaryPanel({
8763
+ summary
8764
+ }) {
8765
+ if (summary.models.length === 0) return null;
8766
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-3 border-t border-border/70 pt-2", children: [
8767
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [
8768
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[11px] font-semibold uppercase tracking-normal text-muted-foreground", children: "Session Context" }),
8769
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-mono text-[10px] text-muted-foreground", children: [
8770
+ summary.models.length,
8771
+ " model",
8772
+ summary.models.length === 1 ? "" : "s"
8773
+ ] })
8774
+ ] }),
8775
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "max-h-52 overflow-y-auto pr-1", children: summary.models.map((modelSummary) => /* @__PURE__ */ jsxRuntimeExports.jsx(SessionModelContextRow, { summary: modelSummary }, modelSummary.model)) })
8776
+ ] });
8777
+ }
7375
8778
  function SessionContextBar({
7376
- sessionId,
8779
+ label,
7377
8780
  logs,
7378
8781
  totalIn,
7379
8782
  totalOut,
7380
- timeDisplayFormat
8783
+ timeDisplayFormat,
8784
+ contextSummary,
8785
+ showBackLink
7381
8786
  }) {
7382
8787
  const [copied, setCopied] = reactExports.useState(false);
7383
8788
  const timeRange = reactExports.useMemo(
@@ -7391,55 +8796,58 @@ function SessionContextBar({
7391
8796
  setTimeout(() => setCopied(false), 2e3);
7392
8797
  });
7393
8798
  }, []);
7394
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 flex items-center gap-3 border border-border rounded-md bg-muted/20 px-3 py-2 text-xs", children: [
7395
- /* @__PURE__ */ jsxRuntimeExports.jsx(
7396
- "a",
7397
- {
7398
- href: "/",
7399
- className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
7400
- "aria-label": "Back to all sessions",
7401
- title: "Back to all sessions",
7402
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { className: "size-3.5" })
7403
- }
7404
- ),
7405
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
7406
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
7407
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-purple-400/90 truncate", title: sessionId, children: truncateSessionId(sessionId) }),
7408
- userAgent !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
7409
- "span",
7410
- {
7411
- className: "font-mono text-muted-foreground truncate max-w-[220px]",
7412
- title: userAgent,
7413
- children: userAgent
7414
- }
7415
- )
7416
- ] }),
7417
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-muted-foreground", children: [
7418
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
7419
- logs.length,
7420
- " request",
7421
- logs.length !== 1 ? "s" : ""
8799
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 border border-border rounded-md bg-muted/20 px-3 py-2 text-xs", children: [
8800
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
8801
+ showBackLink && /* @__PURE__ */ jsxRuntimeExports.jsx(
8802
+ "a",
8803
+ {
8804
+ href: "/",
8805
+ className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
8806
+ "aria-label": "Back to all sessions",
8807
+ title: "Back to all sessions",
8808
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { className: "size-3.5" })
8809
+ }
8810
+ ),
8811
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
8812
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
8813
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-purple-400/90 truncate", title: label, children: truncateSessionId(label) }),
8814
+ userAgent !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
8815
+ "span",
8816
+ {
8817
+ className: "font-mono text-muted-foreground truncate max-w-[220px]",
8818
+ title: userAgent,
8819
+ children: userAgent
8820
+ }
8821
+ )
7422
8822
  ] }),
7423
- timeRange !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: timeRange }),
7424
- (totalIn > 0 || totalOut > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
7425
- formatTokens(totalIn),
7426
- " in / ",
7427
- formatTokens(totalOut),
7428
- " out"
8823
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-muted-foreground", children: [
8824
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
8825
+ logs.length,
8826
+ " request",
8827
+ logs.length !== 1 ? "s" : ""
8828
+ ] }),
8829
+ timeRange !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: timeRange }),
8830
+ (totalIn > 0 || totalOut > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
8831
+ formatTokens(totalIn),
8832
+ " in / ",
8833
+ formatTokens(totalOut),
8834
+ " out"
8835
+ ] })
7429
8836
  ] })
7430
- ] })
8837
+ ] }),
8838
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
8839
+ "button",
8840
+ {
8841
+ type: "button",
8842
+ onClick: handleCopyLink,
8843
+ className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
8844
+ "aria-label": copied ? "Copied session link" : "Copy session link",
8845
+ title: copied ? "Copied session link" : "Copy session link",
8846
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
8847
+ }
8848
+ )
7431
8849
  ] }),
7432
- /* @__PURE__ */ jsxRuntimeExports.jsx(
7433
- "button",
7434
- {
7435
- type: "button",
7436
- onClick: handleCopyLink,
7437
- className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
7438
- "aria-label": copied ? "Copied session link" : "Copy session link",
7439
- title: copied ? "Copied session link" : "Copy session link",
7440
- children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
7441
- }
7442
- )
8850
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SessionContextSummaryPanel, { summary: contextSummary })
7443
8851
  ] });
7444
8852
  }
7445
8853
  function ProxyViewer({
@@ -7461,6 +8869,7 @@ function ProxyViewer({
7461
8869
  pinnedSessionId
7462
8870
  }) {
7463
8871
  const { totalIn, totalOut } = reactExports.useMemo(() => computeTokenSummary(logs), [logs]);
8872
+ const { providers } = useProviders();
7464
8873
  const [exporting, setExporting] = reactExports.useState(false);
7465
8874
  const [comparePair, setComparePair] = reactExports.useState(null);
7466
8875
  const [crabEntrancePhase, setCrabEntrancePhase] = reactExports.useState(
@@ -7503,7 +8912,20 @@ function ProxyViewer({
7503
8912
  setComparePair(null);
7504
8913
  }, []);
7505
8914
  const groups = reactExports.useMemo(() => groupLogsByConversation(logs), [logs]);
7506
- const hasPinnedSessionContext = pinnedSessionId !== void 0;
8915
+ const sessionContextScope = reactExports.useMemo(
8916
+ () => resolveSessionContextScope({ pinnedSessionId, selectedSession, sessions, logs }),
8917
+ [logs, pinnedSessionId, selectedSession, sessions]
8918
+ );
8919
+ const sessionContextLogs = sessionContextScope?.logs ?? [];
8920
+ const hasSessionContext = sessionContextScope !== null && sessionContextLogs.length > 0;
8921
+ const { totalIn: sessionContextTotalIn, totalOut: sessionContextTotalOut } = reactExports.useMemo(
8922
+ () => computeTokenSummary(sessionContextLogs),
8923
+ [sessionContextLogs]
8924
+ );
8925
+ const sessionContextSummary = reactExports.useMemo(
8926
+ () => buildSessionContextSummary(sessionContextLogs, providers),
8927
+ [sessionContextLogs, providers]
8928
+ );
7507
8929
  const cacheTrends = reactExports.useMemo(() => computeCacheTrends(groups), [groups]);
7508
8930
  const comparisonPredecessors = reactExports.useMemo(() => buildValidPredecessors(groups), [groups]);
7509
8931
  const handleCompareWithPrevious = reactExports.useCallback(
@@ -7571,14 +8993,16 @@ function ProxyViewer({
7571
8993
  ] }),
7572
8994
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {}) })
7573
8995
  ] }),
7574
- pinnedSessionId !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
8996
+ hasSessionContext && sessionContextScope !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
7575
8997
  SessionContextBar,
7576
8998
  {
7577
- sessionId: pinnedSessionId,
7578
- logs,
7579
- totalIn,
7580
- totalOut,
7581
- timeDisplayFormat
8999
+ label: sessionContextScope.label,
9000
+ logs: sessionContextLogs,
9001
+ totalIn: sessionContextTotalIn,
9002
+ totalOut: sessionContextTotalOut,
9003
+ timeDisplayFormat,
9004
+ contextSummary: sessionContextSummary,
9005
+ showBackLink: sessionContextScope.showBackLink
7582
9006
  }
7583
9007
  ),
7584
9008
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
@@ -7617,7 +9041,7 @@ function ProxyViewer({
7617
9041
  )
7618
9042
  ] }),
7619
9043
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1" }),
7620
- !hasPinnedSessionContext && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
9044
+ !hasSessionContext && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
7621
9045
  logs.length,
7622
9046
  " request",
7623
9047
  logs.length !== 1 ? "s" : "",
@@ -7689,7 +9113,7 @@ function ProxyViewer({
7689
9113
  comparisonPredecessors,
7690
9114
  onClearGroup,
7691
9115
  standalone: groups.length === 1,
7692
- hasPinnedSessionContext,
9116
+ hasPinnedSessionContext: hasSessionContext,
7693
9117
  timeDisplayFormat
7694
9118
  },
7695
9119
  group.id
@@ -7976,12 +9400,14 @@ function ProxyViewerContainer({
7976
9400
  ] });
7977
9401
  }
7978
9402
  export {
9403
+ ANATOMY_ROLE_LABELS as A,
7979
9404
  Badge as B,
7980
- Collapsible as C,
9405
+ CONTEXT_USAGE_THRESHOLDS as C,
7981
9406
  Dialog as D,
7982
9407
  JsonViewerFromString as J,
7983
9408
  ProxyViewerContainer as P,
7984
- ScrollArea as S,
9409
+ ROLE_COLOR_CLASSES as R,
9410
+ SegmentBar as S,
7985
9411
  Tabs as T,
7986
9412
  getConversationId as a,
7987
9413
  DialogContent as b,
@@ -7998,13 +9424,15 @@ export {
7998
9424
  TooltipTrigger as m,
7999
9425
  TooltipContent as n,
8000
9426
  Button as o,
8001
- countCharacters as p,
8002
- estimateTokens as q,
9427
+ analyzeContextIntelligence as p,
9428
+ formatContextWindowTokens as q,
8003
9429
  resolveLogFormat as r,
8004
9430
  getStatusCategory as s,
8005
- CollapsibleTrigger as t,
9431
+ Collapsible as t,
8006
9432
  useProviders as u,
8007
- CollapsibleContent as v,
8008
- JsonViewer as w,
8009
- safeJsonValue as x
9433
+ CollapsibleTrigger as v,
9434
+ CollapsibleContent as w,
9435
+ ScrollArea as x,
9436
+ JsonViewer as y,
9437
+ safeJsonValue as z
8010
9438
  };