@tonyclaw/agent-inspector 2.1.13 → 2.1.14

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 (81) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/CompareDrawer-DjgjIFx7.js +1 -0
  3. package/.output/public/assets/ProxyViewerContainer-CWUQZLYy.js +106 -0
  4. package/.output/public/assets/{ReplayDialog-sBA1KAYD.js → ReplayDialog-CU0Tbb2c.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-BjlBT-Cy.js → RequestAnatomy-BNahe83D.js} +1 -1
  6. package/.output/public/assets/ResponseView-DSOnGqi6.js +3 -0
  7. package/.output/public/assets/{StreamingChunkSequence-D2_SMhlE.js → StreamingChunkSequence-BEKTDklB.js} +1 -1
  8. package/.output/public/assets/_sessionId-Cif8JZdn.js +1 -0
  9. package/.output/public/assets/index-DWOkqdCa.js +14 -0
  10. package/.output/public/assets/index-D_WfwzUi.js +1 -0
  11. package/.output/public/assets/index-DtLuQrs0.css +1 -0
  12. package/.output/public/assets/json-viewer-C2JpgcW0.js +1 -0
  13. package/.output/public/assets/{main-Dtspb4Ui.js → main-CSONBwwn.js} +2 -2
  14. package/.output/server/_libs/lucide-react.mjs +20 -20
  15. package/.output/server/_libs/react-markdown.mjs +90 -2
  16. package/.output/server/{_sessionId-CXDcLuvi.mjs → _sessionId-CPAa37n5.mjs} +6 -57
  17. package/.output/server/_ssr/{CompareDrawer-CMoCAoeq.mjs → CompareDrawer-ceW5VxMo.mjs} +17 -62
  18. package/.output/server/_ssr/{ProxyViewerContainer-BW2vVCBN.mjs → ProxyViewerContainer-CDfEE_w-.mjs} +872 -688
  19. package/.output/server/_ssr/{ReplayDialog-ChXL1t8H.mjs → ReplayDialog-V0s_eEbR.mjs} +8 -59
  20. package/.output/server/_ssr/{RequestAnatomy-DtKzIlfU.mjs → RequestAnatomy-f1ccwR9d.mjs} +6 -57
  21. package/.output/server/_ssr/{ResponseView-B5I8drzc.mjs → ResponseView-BIRrqG4H.mjs} +138 -60
  22. package/.output/server/_ssr/{StreamingChunkSequence-DvwjQNcO.mjs → StreamingChunkSequence-V3JFjCgX.mjs} +13 -58
  23. package/.output/server/_ssr/{index-D_ZHtRfl.mjs → index-DsykulzS.mjs} +6 -57
  24. package/.output/server/_ssr/index.mjs +2 -2
  25. package/.output/server/_ssr/json-viewer-Dcnm0Ivf.mjs +478 -0
  26. package/.output/server/_ssr/{router-DhL9Wp3N.mjs → router-4bdm6Mt2.mjs} +674 -173
  27. package/.output/server/{_tanstack-start-manifest_v-BHgoAmxZ.mjs → _tanstack-start-manifest_v-Xp4CO64V.mjs} +1 -1
  28. package/.output/server/index.mjs +76 -62
  29. package/package.json +1 -1
  30. package/src/components/ProxyViewerContainer.tsx +25 -17
  31. package/src/components/providers/ImportWizardDialog.tsx +7 -1
  32. package/src/components/providers/ProviderCard.tsx +10 -1
  33. package/src/components/providers/ProviderForm.tsx +157 -41
  34. package/src/components/providers/ProvidersPanel.tsx +5 -1
  35. package/src/components/proxy-viewer/AnswerMarkdown.tsx +8 -3
  36. package/src/components/proxy-viewer/CompareDrawer.tsx +20 -6
  37. package/src/components/proxy-viewer/ConversationGroup.tsx +11 -15
  38. package/src/components/proxy-viewer/LogEntry.tsx +31 -11
  39. package/src/components/proxy-viewer/LogEntryHeader.tsx +111 -40
  40. package/src/components/proxy-viewer/RequestToolsPanel.tsx +19 -13
  41. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +9 -3
  42. package/src/components/proxy-viewer/TurnGroup.tsx +53 -7
  43. package/src/components/proxy-viewer/TurnGroupList.tsx +153 -0
  44. package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +74 -2
  45. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +13 -7
  46. package/src/components/proxy-viewer/formats/index.tsx +10 -3
  47. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +185 -17
  48. package/src/components/proxy-viewer/log-formats/openai.ts +107 -15
  49. package/src/components/proxy-viewer/logFocus.ts +15 -2
  50. package/src/components/proxy-viewer/requestTools.ts +7 -3
  51. package/src/components/proxy-viewer/viewerState.ts +72 -3
  52. package/src/components/ui/json-viewer-bulk.ts +41 -6
  53. package/src/components/ui/json-viewer.tsx +9 -8
  54. package/src/contracts/index.ts +15 -1
  55. package/src/contracts/openai.ts +94 -0
  56. package/src/lib/providerContract.ts +1 -0
  57. package/src/lib/providerModelMetadata.ts +7 -1
  58. package/src/lib/stopReason.ts +12 -0
  59. package/src/lib/upstreamUrl.ts +46 -0
  60. package/src/mcp/previewExtractor.ts +73 -3
  61. package/src/mcp/server.ts +2 -0
  62. package/src/mcp/toolHandlers.ts +2 -0
  63. package/src/proxy/constants.ts +2 -0
  64. package/src/proxy/formats/openai/handler.ts +40 -9
  65. package/src/proxy/formats/openai/index.ts +7 -0
  66. package/src/proxy/formats/openai/schemas.ts +15 -1
  67. package/src/proxy/formats/openai/stream.ts +529 -135
  68. package/src/proxy/formats/registry.ts +9 -1
  69. package/src/proxy/providerImporters.ts +45 -5
  70. package/src/proxy/providers.ts +31 -11
  71. package/src/proxy/schemas.ts +6 -0
  72. package/src/proxy/toolSchemaWarnings.ts +25 -3
  73. package/src/proxy/upstream.ts +18 -37
  74. package/src/routes/api/providers.$providerId.ts +1 -0
  75. package/src/routes/api/providers.ts +2 -0
  76. package/.output/public/assets/CompareDrawer-Cz_1vIpR.js +0 -1
  77. package/.output/public/assets/ProxyViewerContainer-7QSiluMf.js +0 -117
  78. package/.output/public/assets/ResponseView-PtEKzml9.js +0 -1
  79. package/.output/public/assets/_sessionId-DZfB4ruK.js +0 -1
  80. package/.output/public/assets/index-B-QQLbpz.js +0 -1
  81. package/.output/public/assets/index-CmtfjQPv.css +0 -1
@@ -1,16 +1,15 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
- import { I as InspectorGroupsListResponseSchema, C as CapturedLogSchema, D as DEFAULT_CAPTURE_MODE, a as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, b as DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS, c as DEFAULT_TIME_DISPLAY_FORMAT, d as RuntimeConfigSchema, p as packageJson, P as ProviderConfigSchema, g as apiFormatForPath, l as GroupEvidenceExportResultSchema, m as DeleteInspectorGroupResponseSchema, r as createPendingProviderTestResults, t as ProviderTestResultsSchema, u as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, v as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, T as TimeDisplayFormatSchema, h as getSessionPath, f as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, e as AnthropicRequestSchema, s as safeGetOwnProperty, w as resolveProviderContextWindow, j as AlertSummarySchema, k as AlertListResponseSchema, G as GroupEvidenceReadResponseSchema, q as maskApiKey, n as providerHasContextMetadata, o as findProviderModelMetadata, K as KnowledgeCandidateSchema, i as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema, x as isPlainRecord } from "./router-DhL9Wp3N.mjs";
2
+ import { I as InspectorGroupsListResponseSchema, C as CapturedLogSchema, D as DEFAULT_CAPTURE_MODE, a as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, b as DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS, c as DEFAULT_TIME_DISPLAY_FORMAT, d as RuntimeConfigSchema, p as packageJson, P as ProviderConfigSchema, i as apiFormatForPath, n as GroupEvidenceExportResultSchema, o as DeleteInspectorGroupResponseSchema, v as createPendingProviderTestResults, w as ProviderTestResultsSchema, x as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, y as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, T as TimeDisplayFormatSchema, j as getSessionPath, l as AlertSummarySchema, m as AlertListResponseSchema, G as GroupEvidenceReadResponseSchema, t as maskApiKey, q as providerHasContextMetadata, r as findProviderModelMetadata, s as safeGetOwnProperty, K as KnowledgeCandidateSchema, f as parseOpenAIResponse, g as parseOpenAIResponsesResponse, O as OpenAIRequestSchema, h as OpenAIResponsesRequestSchema, A as AnthropicResponseSchema$1, e as AnthropicRequestSchema, z as resolveProviderContextWindow, u as buildUpstreamUrl, k as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema, B as isPlainRecord } from "./router-4bdm6Mt2.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";
6
6
  import { t as twMerge } from "../_libs/tailwind-merge.mjs";
7
7
  import { R as Root, T as Trigger$1, C as Content, a as Close, b as Title, D as Description, P as Portal$2, O as Overlay } from "../_libs/radix-ui__react-dialog.mjs";
8
8
  import { c as cva } from "../_libs/class-variance-authority.mjs";
9
- import { d as diffJson, a as diffLines } from "../_libs/diff.mjs";
10
9
  import { u as useWindowVirtualizer, a as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
10
+ import { d as diffJson, a as diffLines } from "../_libs/diff.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 { I as Info, C as ChevronDown, a as Check, X, U as Upload, D as Download, F as FileBraces, T as Trash2, P as Plus, A as ArrowLeft, b as ChevronUp, c as Copy, d as ChevronsLeft, e as ChevronLeft, f as ChevronRight, g as ChevronsRight, h as Clapperboard, S as Siren, G as Gauge, L as LoaderCircle, R as RefreshCw, i as Layers, j as Settings, k as Scan, l as CircleAlert, m as Terminal, n as ArrowUpRight, o as ArrowDownRight, p as User, q as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, W as Wrench, r as TriangleAlert, B as Brain, s as Bell, t as Clock3, u as ShieldAlert, v as EyeOff, w as Eye, x as RotateCw, y as Pencil, z as CircleX, H as ShieldCheck, J as Save, K as FileSearch, N as CircleCheck, O as CloudUpload, Q as OctagonAlert, V as Radio, Y as Globe, _ as FileTerminal, $ as ChevronsUp, a0 as ChevronsDown, a1 as FileDiff, a2 as History, a3 as RotateCcw, a4 as Search, a5 as GitCompareArrows, a6 as Minus, a7 as CircleCheckBig, a8 as ArrowUp, a9 as ArrowDown, aa as CircleQuestionMark, ab as Server, ac as Lock, ad as Wifi, ae as WifiOff, af as Ellipsis, ag as CodeXml, ah as Blocks, ai as MousePointerClick, aj as FileText, ak as Rows3, al as Columns2 } from "../_libs/lucide-react.mjs";
13
- import { M as Markdown } from "../_libs/react-markdown.mjs";
12
+ import { I as Info, C as ChevronDown, a as Check, X, U as Upload, D as Download, F as FileBraces, T as Trash2, P as Plus, A as ArrowLeft, b as ChevronUp, c as Copy, d as ChevronsLeft, e as ChevronLeft, f as ChevronRight, g as ChevronsRight, h as Clapperboard, S as Siren, G as Gauge, L as LoaderCircle, R as RefreshCw, i as Layers, j as Settings, k as Scan, l as CircleAlert, m as Terminal, n as ArrowUpRight, o as ArrowDownRight, p as User, q as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, W as Wrench, r as TriangleAlert, B as Brain, s as Bell, t as Clock3, u as ShieldAlert, v as EyeOff, w as Eye, x as RotateCw, y as Pencil, z as CircleX, H as ShieldCheck, J as Save, K as FileSearch, N as CircleCheck, O as CloudUpload, Q as Minus, V as CircleCheckBig, Y as CircleQuestionMark, _ as Server, $ as Lock, a0 as Wifi, a1 as WifiOff, a2 as OctagonAlert, a3 as Radio, a4 as Globe, a5 as FileTerminal, a6 as ChevronsUp, a7 as ChevronsDown, a8 as FileDiff, a9 as History, aa as RotateCcw, ab as Search, ac as GitCompareArrows, ad as ArrowUp, ae as ArrowDown, af as Ellipsis, ag as CodeXml, ah as Blocks, ai as MousePointerClick, aj as FileText, ak as Rows3, al as Columns2 } from "../_libs/lucide-react.mjs";
14
13
  import { u as union, d as object, a as array, l as literal, b as string, n as number, c as boolean, _ as _enum } from "../_libs/zod.mjs";
15
14
  import { S as Slot } from "../_libs/radix-ui__react-slot.mjs";
16
15
  import { R as Root2$1, L as List, T as Trigger$2, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
@@ -1078,9 +1077,9 @@ function contentToText(content) {
1078
1077
  if (block === null || typeof block !== "object") continue;
1079
1078
  const b = block;
1080
1079
  const type = b.type;
1081
- if (type === "text" && typeof b.text === "string") {
1080
+ if ((type === "text" || type === "input_text" || type === "output_text") && typeof b.text === "string") {
1082
1081
  parts.push(b.text);
1083
- } else if (type === "image_url") {
1082
+ } else if (type === "image_url" || type === "input_image") {
1084
1083
  parts.push("[image]");
1085
1084
  }
1086
1085
  }
@@ -1110,15 +1109,54 @@ function toolsToText(tools) {
1110
1109
  const parts = [];
1111
1110
  for (const tool of tools) {
1112
1111
  if (tool === null || typeof tool !== "object") continue;
1113
- const t = tool;
1114
- const fn = t.function;
1115
- if (fn === void 0) continue;
1116
- if (typeof fn.name === "string") parts.push(fn.name);
1117
- if (typeof fn.description === "string") parts.push(fn.description);
1118
- if (fn.parameters !== void 0) parts.push(JSON.stringify(fn.parameters));
1112
+ const fn = safeGetOwnProperty(tool, "function");
1113
+ const name = safeGetOwnProperty(fn, "name") ?? safeGetOwnProperty(tool, "name");
1114
+ const description = safeGetOwnProperty(fn, "description") ?? safeGetOwnProperty(tool, "description");
1115
+ const parameters = safeGetOwnProperty(fn, "parameters") ?? safeGetOwnProperty(tool, "parameters");
1116
+ if (typeof name === "string") parts.push(name);
1117
+ if (typeof description === "string") parts.push(description);
1118
+ if (parameters !== void 0) parts.push(JSON.stringify(parameters));
1119
1119
  }
1120
1120
  return parts.join("\n");
1121
1121
  }
1122
+ function countResponsesInput(input) {
1123
+ if (typeof input === "string") return input.length > 0 ? 1 : 0;
1124
+ if (Array.isArray(input)) return input.length;
1125
+ if (input === void 0 || input === null) return null;
1126
+ return 1;
1127
+ }
1128
+ function responsesInputItemToText(input) {
1129
+ if (typeof input === "string") return input;
1130
+ const directText = safeGetOwnProperty(input, "text");
1131
+ if (typeof directText === "string") return directText;
1132
+ const contentText = contentToText(safeGetOwnProperty(input, "content"));
1133
+ if (contentText.length > 0) return contentText;
1134
+ const outputText = contentToText(safeGetOwnProperty(input, "output"));
1135
+ if (outputText.length > 0) return outputText;
1136
+ const argumentsText = safeGetOwnProperty(input, "arguments");
1137
+ return typeof argumentsText === "string" ? argumentsText : "";
1138
+ }
1139
+ function responsesInputRole(input) {
1140
+ const role = safeGetOwnProperty(input, "role");
1141
+ if (role === "system" || role === "user" || role === "assistant" || role === "tool") {
1142
+ return role;
1143
+ }
1144
+ const type = safeGetOwnProperty(input, "type");
1145
+ if (type === "function_call_output") return "tool";
1146
+ if (type === "message") return "user";
1147
+ return "user";
1148
+ }
1149
+ function responsesToolNames(response) {
1150
+ const output = safeGetOwnProperty(response, "output");
1151
+ if (!Array.isArray(output)) return [];
1152
+ const names = [];
1153
+ for (const item of output) {
1154
+ if (safeGetOwnProperty(item, "type") !== "function_call") continue;
1155
+ const name = safeGetOwnProperty(item, "name");
1156
+ if (typeof name === "string" && name.length > 0) names.push(name);
1157
+ }
1158
+ return names;
1159
+ }
1122
1160
  function segment(role, label, text, path) {
1123
1161
  return {
1124
1162
  role,
@@ -1135,7 +1173,16 @@ const openAILogFormatAdapter = {
1135
1173
  if (rawBody === null) return emptyRequestAnalysis(rawBody);
1136
1174
  try {
1137
1175
  const result = OpenAIRequestSchema.safeParse(JSON.parse(rawBody));
1138
- if (!result.success) return emptyRequestAnalysis(rawBody);
1176
+ if (!result.success) {
1177
+ const responsesResult = OpenAIResponsesRequestSchema.safeParse(JSON.parse(rawBody));
1178
+ if (!responsesResult.success) return emptyRequestAnalysis(rawBody);
1179
+ return {
1180
+ parsed: responsesResult.data,
1181
+ comparisonValue: responsesResult.data,
1182
+ messageCount: countResponsesInput(responsesResult.data.input),
1183
+ toolCount: responsesResult.data.tools !== void 0 && responsesResult.data.tools.length > 0 ? responsesResult.data.tools.length : null
1184
+ };
1185
+ }
1139
1186
  return {
1140
1187
  parsed: result.data,
1141
1188
  comparisonValue: result.data,
@@ -1149,7 +1196,15 @@ const openAILogFormatAdapter = {
1149
1196
  analyzeResponse(responseText) {
1150
1197
  if (responseText === null) return EMPTY_RESPONSE_ANALYSIS;
1151
1198
  const parsed = parseOpenAIResponse(responseText);
1152
- if (parsed === null) return EMPTY_RESPONSE_ANALYSIS;
1199
+ if (parsed === null) {
1200
+ const responsesParsed = parseOpenAIResponsesResponse(responseText);
1201
+ if (responsesParsed === null) return EMPTY_RESPONSE_ANALYSIS;
1202
+ const toolNames2 = responsesToolNames(responsesParsed);
1203
+ return {
1204
+ parsed: responsesParsed,
1205
+ toolNames: toolNames2.length > 0 ? toolNames2 : null
1206
+ };
1207
+ }
1153
1208
  const toolNames = parsed.choices[0]?.message?.tool_calls?.map((toolCall) => toolCall.function?.name ?? "").filter((name) => name !== "") ?? [];
1154
1209
  return {
1155
1210
  parsed,
@@ -1160,6 +1215,9 @@ const openAILogFormatAdapter = {
1160
1215
  if (parsed === null || typeof parsed !== "object") return null;
1161
1216
  const body = parsed;
1162
1217
  const segments = [];
1218
+ if (typeof body.instructions === "string" && body.instructions.length > 0) {
1219
+ segments.push(segment("system", "instructions", body.instructions, "/instructions"));
1220
+ }
1163
1221
  if (Array.isArray(body.messages)) {
1164
1222
  body.messages.forEach((message, index) => {
1165
1223
  if (message === null || typeof message !== "object") return;
@@ -1169,6 +1227,17 @@ const openAILogFormatAdapter = {
1169
1227
  segments.push(segment(role, `[${index}] ${role}`, text, `/messages/${index}`));
1170
1228
  });
1171
1229
  }
1230
+ if (!Array.isArray(body.messages)) {
1231
+ if (typeof body.input === "string" && body.input.length > 0) {
1232
+ segments.push(segment("user", "input", body.input, "/input"));
1233
+ } else if (Array.isArray(body.input)) {
1234
+ body.input.forEach((item, index) => {
1235
+ const role = responsesInputRole(item);
1236
+ const text = responsesInputItemToText(item);
1237
+ segments.push(segment(role, `[${index}] ${role}`, text, `/input/${index}`));
1238
+ });
1239
+ }
1240
+ }
1172
1241
  if (Array.isArray(body.tools) && body.tools.length > 0) {
1173
1242
  const text = toolsToText(body.tools);
1174
1243
  segments.push(segment("tools", "tools", text, "/tools"));
@@ -1418,6 +1487,35 @@ function groupLogsByConversation(logs) {
1418
1487
  result.sort((a, b) => (a.logs[0]?.timestamp ?? "").localeCompare(b.logs[0]?.timestamp ?? ""));
1419
1488
  return result;
1420
1489
  }
1490
+ const LOG_FOCUS_REQUEST_EVENT = "agent-inspector:focus-log";
1491
+ function dispatchLogFocusRequest(request) {
1492
+ window.dispatchEvent(new CustomEvent(LOG_FOCUS_REQUEST_EVENT, { detail: request }));
1493
+ }
1494
+ function readLogFocusRequest(event) {
1495
+ if (typeof CustomEvent === "undefined" || !(event instanceof CustomEvent)) return null;
1496
+ const detail = event.detail;
1497
+ const logId = safeGetOwnProperty(detail, "logId");
1498
+ if (typeof logId !== "number" || !Number.isInteger(logId) || logId < 0) return null;
1499
+ const tab = safeGetOwnProperty(detail, "tab");
1500
+ switch (tab) {
1501
+ case "anatomy":
1502
+ case "request":
1503
+ break;
1504
+ case void 0:
1505
+ break;
1506
+ default:
1507
+ return null;
1508
+ }
1509
+ const source = safeGetOwnProperty(detail, "source");
1510
+ switch (source) {
1511
+ case "virtualizer":
1512
+ return { logId, tab: tab ?? "request", source };
1513
+ case void 0:
1514
+ return { logId, tab: tab ?? "request" };
1515
+ default:
1516
+ return null;
1517
+ }
1518
+ }
1421
1519
  function isRecord(value) {
1422
1520
  return typeof value === "object" && value !== null && !Array.isArray(value);
1423
1521
  }
@@ -1438,6 +1536,16 @@ function extractStopReason(log) {
1438
1536
  if (Array.isArray(json.choices) && json.choices.length > 0 && isRecord(json.choices[0]) && typeof json.choices[0].finish_reason === "string" && json.choices[0].finish_reason === "stop") {
1439
1537
  return "stop";
1440
1538
  }
1539
+ if (Array.isArray(json.output)) {
1540
+ for (const item of json.output) {
1541
+ if (isRecord(item) && item.type === "function_call") {
1542
+ return "tool_use";
1543
+ }
1544
+ }
1545
+ if (json.status === "completed") {
1546
+ return "stop";
1547
+ }
1548
+ }
1441
1549
  return null;
1442
1550
  } catch {
1443
1551
  return null;
@@ -1798,8 +1906,38 @@ function getInteriorCrabVariantIndex(seed) {
1798
1906
  function getInteriorCrabVariant(seed) {
1799
1907
  return getCrabVariant(getInteriorCrabVariantIndex(seed));
1800
1908
  }
1801
- function getJsonExpansionPolicy(_value) {
1802
- return { depth: Number.POSITIVE_INFINITY };
1909
+ const FULL_EXPAND_NODE_LIMIT = 400;
1910
+ const STRUCTURE_EXPAND_NODE_LIMIT = 2e3;
1911
+ function isJsonArray(value) {
1912
+ return Array.isArray(value);
1913
+ }
1914
+ function countJsonNodesUpTo(value, limit) {
1915
+ const stack = [value];
1916
+ let count = 0;
1917
+ while (stack.length > 0) {
1918
+ const current = stack.pop();
1919
+ if (current === void 0) continue;
1920
+ count += 1;
1921
+ if (count > limit) return count;
1922
+ if (current === null || typeof current !== "object") continue;
1923
+ if (isJsonArray(current)) {
1924
+ for (const child of current) {
1925
+ stack.push(child);
1926
+ }
1927
+ continue;
1928
+ }
1929
+ for (const key of Object.keys(current)) {
1930
+ const child = current[key];
1931
+ if (child !== void 0) stack.push(child);
1932
+ }
1933
+ }
1934
+ return count;
1935
+ }
1936
+ function getJsonExpansionPolicy(value) {
1937
+ const nodeCount = countJsonNodesUpTo(value, STRUCTURE_EXPAND_NODE_LIMIT + 1);
1938
+ if (nodeCount <= FULL_EXPAND_NODE_LIMIT) return { depth: Number.POSITIVE_INFINITY };
1939
+ if (nodeCount <= STRUCTURE_EXPAND_NODE_LIMIT) return { depth: 3 };
1940
+ return { depth: 2 };
1803
1941
  }
1804
1942
  function parseJsonText(text) {
1805
1943
  try {
@@ -1813,7 +1951,7 @@ function useJsonBulkExpansion(text) {
1813
1951
  const parsed = reactExports.useMemo(() => text === null ? null : parseJsonText(text), [text]);
1814
1952
  const parsedData = parsed?.kind === "json" ? parsed.data : null;
1815
1953
  const policy = reactExports.useMemo(
1816
- () => parsedData === null ? null : getJsonExpansionPolicy(),
1954
+ () => parsedData === null ? null : getJsonExpansionPolicy(parsedData),
1817
1955
  [parsedData]
1818
1956
  );
1819
1957
  const [isExpanded, setIsExpanded] = reactExports.useState(false);
@@ -1961,27 +2099,27 @@ function useCopyFeedback(text) {
1961
2099
  return { copied, copy };
1962
2100
  }
1963
2101
  const LazyCompareDrawer = reactExports.lazy(
1964
- () => import("./CompareDrawer-CMoCAoeq.mjs").then((m) => ({ default: m.CompareDrawer }))
2102
+ () => import("./CompareDrawer-ceW5VxMo.mjs").then((m) => ({ default: m.CompareDrawer }))
1965
2103
  );
1966
2104
  const LazyReplayDialog = reactExports.lazy(
1967
- () => import("./ReplayDialog-ChXL1t8H.mjs").then((m) => ({ default: m.ReplayDialog }))
2105
+ () => import("./ReplayDialog-V0s_eEbR.mjs").then((m) => ({ default: m.ReplayDialog }))
1968
2106
  );
1969
2107
  const LazyRequestAnatomy = reactExports.lazy(
1970
- () => import("./RequestAnatomy-DtKzIlfU.mjs").then((m) => ({ default: m.RequestAnatomy }))
2108
+ () => import("./RequestAnatomy-f1ccwR9d.mjs").then((m) => ({ default: m.RequestAnatomy }))
1971
2109
  );
1972
2110
  const LazyResponseView = reactExports.lazy(
1973
- () => import("./ResponseView-B5I8drzc.mjs").then((m) => ({ default: m.ResponseView }))
2111
+ () => import("./ResponseView-BIRrqG4H.mjs").then((m) => ({ default: m.ResponseView }))
1974
2112
  );
1975
2113
  const LazyStreamingChunkSequence = reactExports.lazy(
1976
- () => import("./StreamingChunkSequence-DvwjQNcO.mjs").then((m) => ({
2114
+ () => import("./StreamingChunkSequence-V3JFjCgX.mjs").then((m) => ({
1977
2115
  default: m.StreamingChunkSequence
1978
2116
  }))
1979
2117
  );
1980
2118
  const LazyJsonViewer = reactExports.lazy(
1981
- () => Promise.resolve().then(() => jsonViewer).then((m) => ({ default: m.JsonViewer }))
2119
+ () => import("./json-viewer-Dcnm0Ivf.mjs").then((m) => ({ default: m.JsonViewer }))
1982
2120
  );
1983
2121
  const LazyJsonViewerFromString = reactExports.lazy(
1984
- () => Promise.resolve().then(() => jsonViewer).then((m) => ({ default: m.JsonViewerFromString }))
2122
+ () => import("./json-viewer-Dcnm0Ivf.mjs").then((m) => ({ default: m.JsonViewerFromString }))
1985
2123
  );
1986
2124
  const HIGHLIGHT_DURATION_MS = 1200;
1987
2125
  const MAX_HIGHLIGHT_ATTEMPTS = 12;
@@ -2405,12 +2543,35 @@ function Badge({
2405
2543
  );
2406
2544
  }
2407
2545
  const STATUS_BADGE_CLASSES = {
2408
- success: "bg-emerald-500/15 text-emerald-400 border-emerald-500/25",
2409
- client_error: "bg-amber-500/15 text-amber-400 border-amber-500/25",
2410
- server_error: "bg-rose-500/15 text-rose-400 border-rose-500/25",
2411
- pending: "bg-muted text-muted-foreground border-border"
2546
+ success: "bg-emerald-400/[0.08] text-emerald-200 border-emerald-300/25",
2547
+ client_error: "bg-amber-300/[0.10] text-amber-100 border-amber-300/30",
2548
+ server_error: "bg-rose-400/[0.10] text-rose-100 border-rose-300/30",
2549
+ pending: "bg-cyan-300/[0.08] text-cyan-100 border-cyan-300/25"
2550
+ };
2551
+ const STATUS_CUE_CLASSES = {
2552
+ success: {
2553
+ frame: "border-emerald-300/20 bg-emerald-300/[0.06] text-emerald-100",
2554
+ dot: "bg-emerald-300 shadow-[0_0_14px_rgba(110,231,183,0.8)]",
2555
+ rail: "bg-emerald-300/45 shadow-[0_0_22px_rgba(110,231,183,0.28)]"
2556
+ },
2557
+ client_error: {
2558
+ frame: "border-amber-300/25 bg-amber-300/[0.08] text-amber-100",
2559
+ dot: "bg-amber-200 shadow-[0_0_14px_rgba(252,211,77,0.8)]",
2560
+ rail: "bg-amber-300/50 shadow-[0_0_22px_rgba(252,211,77,0.28)]"
2561
+ },
2562
+ server_error: {
2563
+ frame: "border-rose-300/25 bg-rose-300/[0.08] text-rose-100",
2564
+ dot: "bg-rose-300 shadow-[0_0_14px_rgba(253,164,175,0.8)]",
2565
+ rail: "bg-rose-300/50 shadow-[0_0_22px_rgba(253,164,175,0.28)]"
2566
+ },
2567
+ pending: {
2568
+ frame: "border-cyan-300/25 bg-cyan-300/[0.08] text-cyan-100",
2569
+ dot: "bg-cyan-200 shadow-[0_0_14px_rgba(103,232,249,0.8)]",
2570
+ rail: "bg-cyan-300/50 shadow-[0_0_22px_rgba(103,232,249,0.28)]"
2571
+ }
2412
2572
  };
2413
2573
  const HEADER_ACTION_BUTTON_CLASS = "size-8 border-white/10 bg-[#0a0d11] text-muted-foreground shadow-none hover:border-cyan-300/30 hover:bg-cyan-400/10 hover:text-cyan-100";
2574
+ const TELEMETRY_CHIP_CLASS = "flex h-7 items-center gap-1 rounded-md border border-white/10 bg-black/20 px-2 text-xs shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]";
2414
2575
  function formatElapsed$3(ms) {
2415
2576
  if (ms < 1e3) return `${ms}ms`;
2416
2577
  return `${(ms / 1e3).toFixed(1)}s`;
@@ -2419,6 +2580,18 @@ function formatTokenRate$1(tokensPerSecond) {
2419
2580
  if (tokensPerSecond >= 10) return `${tokensPerSecond.toFixed(0)}/s`;
2420
2581
  return `${tokensPerSecond.toFixed(1)}/s`;
2421
2582
  }
2583
+ function statusCueLabel(category, status) {
2584
+ switch (category) {
2585
+ case "success":
2586
+ return status === null ? "OK" : String(status);
2587
+ case "client_error":
2588
+ return status === null ? "WARN" : String(status);
2589
+ case "server_error":
2590
+ return status === null ? "FAIL" : String(status);
2591
+ case "pending":
2592
+ return "ROLL";
2593
+ }
2594
+ }
2422
2595
  function CacheTrendIndicator({ trend }) {
2423
2596
  if (trend === null) return null;
2424
2597
  const isUp = trend.direction === "up";
@@ -2456,6 +2629,8 @@ const LogEntryHeader = reactExports.memo(function({
2456
2629
  const hasClientPid = log.clientPid !== null && log.clientPid !== void 0;
2457
2630
  const hasClientProjectFolder = log.clientProjectFolder !== null && log.clientProjectFolder !== void 0 && log.clientProjectFolder !== "";
2458
2631
  const hasClientCwd = log.clientCwd !== null && log.clientCwd !== void 0 && log.clientCwd !== "";
2632
+ const statusCue = STATUS_CUE_CLASSES[statusCategory];
2633
+ const statusLabel2 = statusCueLabel(statusCategory, log.responseStatus);
2459
2634
  return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
2460
2635
  "div",
2461
2636
  {
@@ -2465,9 +2640,9 @@ const LogEntryHeader = reactExports.memo(function({
2465
2640
  "data-nav-id": `log-${log.id}`,
2466
2641
  "data-nav-action": expanded ? "collapse" : "expand",
2467
2642
  className: cn(
2468
- "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 px-3 py-1.5 cursor-pointer transition-colors",
2469
- expanded ? "bg-[#0c1015]" : "hover:bg-muted/30",
2470
- "select-none",
2643
+ "group/header relative isolate flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 overflow-hidden px-3 py-2 cursor-pointer transition-[background-color,border-color,box-shadow]",
2644
+ expanded ? "bg-[linear-gradient(90deg,#0f1720_0%,#090d12_50%,#07090d_100%)] shadow-[inset_0_1px_0_rgba(255,255,255,0.06)]" : "bg-[#080b10] hover:bg-[#0b1016]",
2645
+ "select-none before:pointer-events-none before:absolute before:inset-x-0 before:top-0 before:h-px before:bg-white/10",
2471
2646
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
2472
2647
  ),
2473
2648
  onClick: onToggle,
@@ -2478,25 +2653,48 @@ const LogEntryHeader = reactExports.memo(function({
2478
2653
  }
2479
2654
  },
2480
2655
  children: [
2481
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-400/80 font-mono text-xs font-semibold tabular-nums shrink-0", children: [
2482
- "#",
2483
- log.id
2484
- ] }),
2485
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
2656
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2657
+ "span",
2658
+ {
2659
+ className: cn(
2660
+ "absolute left-0 top-2 bottom-2 w-0.5 rounded-r-full transition-colors",
2661
+ statusCue.rail
2662
+ ),
2663
+ "aria-hidden": "true"
2664
+ }
2665
+ ),
2666
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
2667
+ "span",
2668
+ {
2669
+ className: cn(
2670
+ "inline-flex h-8 shrink-0 items-center gap-2 rounded-md border px-2 font-mono shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]",
2671
+ statusCue.frame
2672
+ ),
2673
+ children: [
2674
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("size-1.5 rounded-full", statusCue.dot), "aria-hidden": "true" }),
2675
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] font-semibold uppercase tracking-normal text-current/65", children: "Shot" }),
2676
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs font-semibold tabular-nums", children: [
2677
+ "#",
2678
+ log.id
2679
+ ] })
2680
+ ]
2681
+ }
2682
+ ),
2683
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex h-7 items-center gap-1 rounded-md border border-white/10 bg-black/20 px-2 text-muted-foreground text-xs shrink-0", children: [
2486
2684
  /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
2487
2685
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", title: log.timestamp, children: formatTimestamp$2(log.timestamp, timeDisplayFormat) })
2488
2686
  ] }),
2489
- statusCategory !== "success" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
2687
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
2490
2688
  Badge,
2491
2689
  {
2492
2690
  variant: "outline",
2493
2691
  className: cn(
2494
- "text-[10px] px-1.5 py-0 h-5 font-mono tabular-nums gap-1",
2692
+ "h-6 gap-1 rounded-md px-2 py-0 font-mono text-[10px] font-semibold uppercase tabular-nums shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]",
2495
2693
  STATUS_BADGE_CLASSES[statusCategory]
2496
2694
  ),
2497
2695
  children: [
2498
- statusCategory === "server_error" ? /* @__PURE__ */ jsxRuntimeExports.jsx(OctagonAlert, { className: "size-3", "aria-label": "Server error" }) : statusCategory === "client_error" ? /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Client error" }) : statusCategory === "pending" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin", "aria-label": "Pending" }) : null,
2499
- log.responseStatus
2696
+ statusCategory === "success" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3", "aria-label": "Success" }) : statusCategory === "server_error" ? /* @__PURE__ */ jsxRuntimeExports.jsx(OctagonAlert, { className: "size-3", "aria-label": "Server error" }) : statusCategory === "client_error" ? /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Client error" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3 animate-spin", "aria-label": "Pending" }),
2697
+ statusLabel2
2500
2698
  ]
2501
2699
  }
2502
2700
  ),
@@ -2505,7 +2703,7 @@ const LogEntryHeader = reactExports.memo(function({
2505
2703
  Badge,
2506
2704
  {
2507
2705
  variant: "outline",
2508
- className: "text-[10px] px-1.5 py-0 h-5 font-mono tabular-nums gap-1 bg-amber-500/15 text-amber-400 border-amber-500/25",
2706
+ className: "h-6 gap-1 rounded-md border-amber-300/30 bg-amber-300/[0.10] px-2 py-0 font-mono text-[10px] tabular-nums text-amber-100",
2509
2707
  children: [
2510
2708
  /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Tool schema warnings" }),
2511
2709
  warningCount
@@ -2515,11 +2713,20 @@ const LogEntryHeader = reactExports.memo(function({
2515
2713
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: warningCount === 1 ? "1 tool schema warning" : `${warningCount} tool schema warnings` })
2516
2714
  ] }),
2517
2715
  log.elapsedMs !== null && (isSlowResponse ? /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
2518
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs text-amber-400 shrink-0", children: [
2519
- /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
2520
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed$3(log.elapsedMs) }),
2521
- /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Slow response" })
2522
- ] }) }),
2716
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
2717
+ "span",
2718
+ {
2719
+ className: cn(
2720
+ TELEMETRY_CHIP_CLASS,
2721
+ "shrink-0 border-amber-300/25 text-amber-200"
2722
+ ),
2723
+ children: [
2724
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
2725
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed$3(log.elapsedMs) }),
2726
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Slow response" })
2727
+ ]
2728
+ }
2729
+ ) }),
2523
2730
  /* @__PURE__ */ jsxRuntimeExports.jsxs(TooltipContent, { children: [
2524
2731
  "Slow response: ",
2525
2732
  formatElapsed$3(log.elapsedMs),
@@ -2527,12 +2734,12 @@ const LogEntryHeader = reactExports.memo(function({
2527
2734
  " ",
2528
2735
  formatElapsed$3(slowResponseThresholdSeconds * 1e3)
2529
2736
  ] })
2530
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs text-muted-foreground shrink-0", children: [
2737
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground"), children: [
2531
2738
  /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
2532
2739
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed$3(log.elapsedMs) })
2533
2740
  ] })),
2534
2741
  log.streaming && firstChunkLabel !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
2535
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs text-cyan-400 shrink-0", children: [
2742
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-cyan-300"), children: [
2536
2743
  /* @__PURE__ */ jsxRuntimeExports.jsx(Radio, { className: "size-3" }),
2537
2744
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
2538
2745
  "first ",
@@ -2542,13 +2749,13 @@ const LogEntryHeader = reactExports.memo(function({
2542
2749
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Time to first streaming chunk" })
2543
2750
  ] }),
2544
2751
  log.streaming && tokenRateLabel !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
2545
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs text-emerald-400 shrink-0", children: [
2752
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-emerald-300"), children: [
2546
2753
  /* @__PURE__ */ jsxRuntimeExports.jsx(Gauge, { className: "size-3" }),
2547
2754
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: tokenRateLabel })
2548
2755
  ] }) }),
2549
2756
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Output token throughput after first chunk" })
2550
2757
  ] }),
2551
- hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-center gap-1 text-xs sm:shrink-0", children: [
2758
+ hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn(TELEMETRY_CHIP_CLASS, "min-w-0 sm:shrink-0"), children: [
2552
2759
  /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
2553
2760
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 flex-wrap items-center gap-x-1 font-mono tabular-nums", children: [
2554
2761
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -2580,32 +2787,32 @@ const LogEntryHeader = reactExports.memo(function({
2580
2787
  )
2581
2788
  ] })
2582
2789
  ] }),
2583
- log.cacheCreationInputTokens !== null && log.cacheCreationInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
2790
+ log.cacheCreationInputTokens !== null && log.cacheCreationInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn(TELEMETRY_CHIP_CLASS, "shrink-0"), children: [
2584
2791
  /* @__PURE__ */ jsxRuntimeExports.jsx(CacheTrendIndicator, { trend: cacheTrend?.creation ?? null }),
2585
2792
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
2586
2793
  "KV Cache +",
2587
2794
  formatTokens(log.cacheCreationInputTokens)
2588
2795
  ] })
2589
2796
  ] }),
2590
- log.cacheReadInputTokens !== null && log.cacheReadInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
2797
+ log.cacheReadInputTokens !== null && log.cacheReadInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn(TELEMETRY_CHIP_CLASS, "shrink-0"), children: [
2591
2798
  /* @__PURE__ */ jsxRuntimeExports.jsx(CacheTrendIndicator, { trend: cacheTrend?.read ?? null }),
2592
2799
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
2593
2800
  "KV Cache ~",
2594
2801
  formatTokens(log.cacheReadInputTokens)
2595
2802
  ] })
2596
2803
  ] }),
2597
- messageCount !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
2804
+ messageCount !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground"), children: [
2598
2805
  /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3" }),
2599
2806
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: messageCount })
2600
2807
  ] }),
2601
- toolCount !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
2808
+ toolCount !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn(TELEMETRY_CHIP_CLASS, "shrink-0 text-muted-foreground"), children: [
2602
2809
  /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3" }),
2603
2810
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: toolCount })
2604
2811
  ] }),
2605
2812
  log.origin !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
2606
2813
  "span",
2607
2814
  {
2608
- className: "hidden xl:flex items-center gap-1 text-muted-foreground text-xs shrink-0",
2815
+ className: cn(TELEMETRY_CHIP_CLASS, "hidden shrink-0 text-muted-foreground xl:flex"),
2609
2816
  title: `Origin: ${log.origin}`,
2610
2817
  children: [
2611
2818
  /* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "size-3" }),
@@ -2614,7 +2821,7 @@ const LogEntryHeader = reactExports.memo(function({
2614
2821
  }
2615
2822
  ),
2616
2823
  (hasClientPid || hasClientProjectFolder) && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
2617
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "hidden xl:flex items-center gap-1 text-purple-400/80 text-xs shrink-0", children: [
2824
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn(TELEMETRY_CHIP_CLASS, "hidden shrink-0 text-purple-300 xl:flex"), children: [
2618
2825
  /* @__PURE__ */ jsxRuntimeExports.jsx(FileTerminal, { className: "size-3" }),
2619
2826
  hasClientProjectFolder ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: log.clientProjectFolder }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
2620
2827
  "PID ",
@@ -2740,365 +2947,6 @@ function CollapsibleContent({
2740
2947
  }) {
2741
2948
  return /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent$1, { "data-slot": "collapsible-content", ...props });
2742
2949
  }
2743
- function classifyValue(value) {
2744
- if (value === null) return "null";
2745
- if (Array.isArray(value)) return "array";
2746
- switch (typeof value) {
2747
- case "string":
2748
- return "string";
2749
- case "number":
2750
- return "number";
2751
- case "boolean":
2752
- return "boolean";
2753
- case "object":
2754
- return "object";
2755
- case "bigint":
2756
- case "symbol":
2757
- case "undefined":
2758
- case "function":
2759
- return "object";
2760
- }
2761
- }
2762
- function isExpandable(value) {
2763
- return value !== null && (Array.isArray(value) || typeof value === "object");
2764
- }
2765
- function getEntries(value) {
2766
- if (Array.isArray(value)) {
2767
- return value.map((item, index) => [String(index), item]);
2768
- }
2769
- if (typeof value === "object" && value !== null) {
2770
- return Object.entries(value);
2771
- }
2772
- return [];
2773
- }
2774
- const STRING_TRUNCATE_LIMIT = 120;
2775
- function StringValue({ text }) {
2776
- const [expanded, setExpanded] = reactExports.useState(false);
2777
- const isLong = text.length > STRING_TRUNCATE_LIMIT;
2778
- if (!isLong) {
2779
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 break-all", children: [
2780
- '"',
2781
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "prose prose-sm dark:prose-invert inline max-w-none [&_p]:inline [&_p]:my-0 [&_code]:text-emerald-300 [&_a]:text-emerald-300", children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text }) }),
2782
- '"'
2783
- ] });
2784
- }
2785
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 break-all", children: [
2786
- '"',
2787
- expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2788
- "span",
2789
- {
2790
- className: "cursor-pointer prose prose-sm dark:prose-invert inline max-w-none [&_p]:inline [&_p]:my-0 [&_code]:text-emerald-300 [&_a]:text-emerald-300",
2791
- onClick: (e) => {
2792
- e.stopPropagation();
2793
- setExpanded(false);
2794
- },
2795
- onKeyDown: (e) => {
2796
- if (e.key === "Enter" || e.key === " ") {
2797
- e.stopPropagation();
2798
- setExpanded(false);
2799
- }
2800
- },
2801
- role: "button",
2802
- tabIndex: 0,
2803
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { children: text })
2804
- }
2805
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { delayDuration: 300, children: [
2806
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
2807
- TooltipTrigger,
2808
- {
2809
- onClick: (e) => {
2810
- e.stopPropagation();
2811
- setExpanded(true);
2812
- },
2813
- className: "text-left cursor-pointer",
2814
- children: [
2815
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: text.slice(0, STRING_TRUNCATE_LIMIT) }),
2816
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-emerald-400/50", children: "…" })
2817
- ]
2818
- }
2819
- ),
2820
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
2821
- TooltipContent,
2822
- {
2823
- side: "bottom",
2824
- className: "max-w-md text-xs p-2 break-words whitespace-pre-wrap",
2825
- children: [
2826
- text.slice(0, 500),
2827
- text.length > 500 ? "…" : ""
2828
- ]
2829
- }
2830
- )
2831
- ] }),
2832
- '"'
2833
- ] });
2834
- }
2835
- function PrimitiveValue({ value }) {
2836
- if (value === null) {
2837
- return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-rose-400 italic", children: "null" });
2838
- }
2839
- switch (typeof value) {
2840
- case "string":
2841
- return /* @__PURE__ */ jsxRuntimeExports.jsx(StringValue, { text: value });
2842
- case "number":
2843
- return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-amber-400", children: value });
2844
- case "boolean":
2845
- return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-blue-400", children: value ? "true" : "false" });
2846
- case "object":
2847
- case "bigint":
2848
- case "symbol":
2849
- case "undefined":
2850
- case "function":
2851
- return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: JSON.stringify(value) });
2852
- }
2853
- }
2854
- function CopyValueButton({ value }) {
2855
- const [copied, setCopied] = reactExports.useState(false);
2856
- function handleCopy(e) {
2857
- e.stopPropagation();
2858
- void copyTextToClipboard(JSON.stringify(value, null, 2)).then((success) => {
2859
- if (!success) return;
2860
- setCopied(true);
2861
- setTimeout(() => {
2862
- setCopied(false);
2863
- }, 2e3);
2864
- });
2865
- }
2866
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
2867
- "button",
2868
- {
2869
- type: "button",
2870
- onClick: handleCopy,
2871
- className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground hover:text-foreground transition-colors",
2872
- "aria-label": copied ? "Copied JSON" : "Copy JSON",
2873
- title: "Copy JSON",
2874
- children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3" })
2875
- }
2876
- );
2877
- }
2878
- function CopyButton({ value }) {
2879
- const [copied, setCopied] = reactExports.useState(false);
2880
- function handleCopy(e) {
2881
- e.stopPropagation();
2882
- void copyTextToClipboard(JSON.stringify(value, null, 2)).then((success) => {
2883
- if (!success) return;
2884
- setCopied(true);
2885
- setTimeout(() => {
2886
- setCopied(false);
2887
- }, 2e3);
2888
- });
2889
- }
2890
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
2891
- "button",
2892
- {
2893
- type: "button",
2894
- onClick: handleCopy,
2895
- className: "opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0",
2896
- "aria-label": copied ? "Copied JSON value" : "Copy JSON value",
2897
- title: "Copy to clipboard",
2898
- children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3 text-green-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3 text-muted-foreground" })
2899
- }
2900
- );
2901
- }
2902
- function ExpandCollapseButton({
2903
- onClick
2904
- }) {
2905
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
2906
- "button",
2907
- {
2908
- type: "button",
2909
- onClick,
2910
- className: "opacity-0 group-hover/row:opacity-100 hover:bg-muted p-0.5 rounded transition-opacity shrink-0",
2911
- title: "Expand all descendants",
2912
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsDown, { className: "size-3.5 text-muted-foreground" })
2913
- }
2914
- );
2915
- }
2916
- const JsonNode = reactExports.memo(function JsonNode2({
2917
- name,
2918
- value,
2919
- level,
2920
- defaultExpandDepth,
2921
- isArrayItem,
2922
- path,
2923
- expandTargetPath,
2924
- anatomyPaths
2925
- }) {
2926
- const isAncestorOfTarget = reactExports.useMemo(() => {
2927
- if (expandTargetPath === null) return false;
2928
- if (path === "") return expandTargetPath.length > 0;
2929
- return expandTargetPath === path || expandTargetPath.startsWith(`${path}/`);
2930
- }, [expandTargetPath, path]);
2931
- const initialExpanded = level < defaultExpandDepth || isAncestorOfTarget && isExpandable(value);
2932
- const [expanded, setExpanded] = reactExports.useState(initialExpanded);
2933
- reactExports.useEffect(() => {
2934
- if (isAncestorOfTarget && isExpandable(value) && !expanded) {
2935
- setExpanded(true);
2936
- }
2937
- }, [expandTargetPath]);
2938
- const [childResetKey, setChildResetKey] = reactExports.useState(0);
2939
- const [childDepthOverride, setChildDepthOverride] = reactExports.useState(null);
2940
- const expandable = isExpandable(value);
2941
- const fullyExpanded = childDepthOverride === Number.POSITIVE_INFINITY;
2942
- const entries = reactExports.useMemo(() => getEntries(value), [value]);
2943
- const hasExpandableChild = reactExports.useMemo(
2944
- () => entries.some(([, child]) => isExpandable(child)),
2945
- [entries]
2946
- );
2947
- const dataType = classifyValue(value);
2948
- const openBracket = dataType === "array" ? "[" : "{";
2949
- const closeBracket = dataType === "array" ? "]" : "}";
2950
- const hasAnatomyPath = anatomyPaths !== null && anatomyPaths.has(path);
2951
- function expandAllDeep() {
2952
- setExpanded(true);
2953
- setChildDepthOverride(Number.POSITIVE_INFINITY);
2954
- setChildResetKey((k) => k + 1);
2955
- }
2956
- function collapseToDefault() {
2957
- setExpanded(false);
2958
- setChildDepthOverride(0);
2959
- setChildResetKey((k) => k + 1);
2960
- }
2961
- function handleRowToggle() {
2962
- if (fullyExpanded) {
2963
- collapseToDefault();
2964
- } else {
2965
- setExpanded(!expanded);
2966
- }
2967
- }
2968
- function handleExpandAll(e) {
2969
- e.stopPropagation();
2970
- expandAllDeep();
2971
- }
2972
- const effectiveChildDepth = childDepthOverride ?? defaultExpandDepth;
2973
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn(level > 0 && "border-l border-border/50 ml-2"), children: [
2974
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
2975
- "div",
2976
- {
2977
- className: cn(
2978
- "flex items-start gap-1 py-0.5 px-1 -ml-1 rounded-sm group/row",
2979
- expandable && "cursor-pointer hover:bg-muted/50"
2980
- ),
2981
- "data-anatomy-path": hasAnatomyPath ? path : void 0,
2982
- onClick: expandable ? handleRowToggle : void 0,
2983
- onKeyDown: expandable ? (e) => {
2984
- if (e.key === "Enter" || e.key === " ") {
2985
- e.preventDefault();
2986
- handleRowToggle();
2987
- }
2988
- } : void 0,
2989
- onDoubleClick: expandable && hasExpandableChild ? () => expandAllDeep() : void 0,
2990
- role: expandable ? "button" : void 0,
2991
- tabIndex: expandable ? 0 : void 0,
2992
- children: [
2993
- expandable ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-4 h-5 flex items-center justify-center shrink-0", children: expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 text-muted-foreground" }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-4 shrink-0" }),
2994
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("shrink-0", isArrayItem ? "text-muted-foreground" : "text-cyan-400"), children: isArrayItem ? name : `"${name}"` }),
2995
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground shrink-0", children: expandable ? "" : ":" }),
2996
- expandable ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground", children: [
2997
- openBracket,
2998
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground/60 text-xs", children: [
2999
- " ",
3000
- entries.length,
3001
- " ",
3002
- entries.length === 1 ? "item" : "items",
3003
- " ",
3004
- closeBracket
3005
- ] })
3006
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(PrimitiveValue, { value }) }),
3007
- expandable && hasExpandableChild && !fullyExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandCollapseButton, { onClick: handleExpandAll }),
3008
- /* @__PURE__ */ jsxRuntimeExports.jsx(CopyButton, { value })
3009
- ]
3010
- }
3011
- ),
3012
- expandable && expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pl-4", children: [
3013
- entries.map(([key, childValue]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3014
- JsonNode2,
3015
- {
3016
- name: key,
3017
- value: childValue,
3018
- level: level + 1,
3019
- defaultExpandDepth: effectiveChildDepth,
3020
- isArrayItem: dataType === "array",
3021
- path: path === "" ? `/${key}` : `${path}/${key}`,
3022
- expandTargetPath,
3023
- anatomyPaths
3024
- },
3025
- key
3026
- )),
3027
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground py-0.5 px-1", children: closeBracket })
3028
- ] }, childResetKey)
3029
- ] });
3030
- });
3031
- function JsonViewer({
3032
- data,
3033
- defaultExpandDepth = 0,
3034
- className,
3035
- showCopy = false,
3036
- bulkDepth: controlledBulkDepth,
3037
- bulkRevision: controlledBulkRevision,
3038
- anatomyPaths = null,
3039
- expandToPath = null
3040
- }) {
3041
- const expandable = isExpandable(data);
3042
- const entries = reactExports.useMemo(() => getEntries(data), [data]);
3043
- const bulkDepth = controlledBulkDepth ?? defaultExpandDepth;
3044
- const bulkRevision = controlledBulkRevision ?? 0;
3045
- if (!expandable) {
3046
- return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("font-mono text-xs leading-relaxed", className), children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
3047
- /* @__PURE__ */ jsxRuntimeExports.jsx(PrimitiveValue, { value: data }),
3048
- showCopy && /* @__PURE__ */ jsxRuntimeExports.jsx(CopyValueButton, { value: data })
3049
- ] }) }) });
3050
- }
3051
- const dataType = classifyValue(data);
3052
- const isArray = dataType === "array";
3053
- return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("font-mono text-xs leading-relaxed", className), children: [
3054
- showCopy && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-2 flex items-center justify-end gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(CopyValueButton, { value: data }) }),
3055
- entries.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-white/10 bg-black/20 px-3 py-2 text-muted-foreground", children: [
3056
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-cyan-300", children: isArray ? "[]" : "{}" }),
3057
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-2 text-[10px] uppercase", children: "0 items" })
3058
- ] }),
3059
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: entries.map(([key, childValue]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3060
- JsonNode,
3061
- {
3062
- name: key,
3063
- value: childValue,
3064
- level: 0,
3065
- defaultExpandDepth: bulkDepth,
3066
- isArrayItem: isArray,
3067
- path: `/${key}`,
3068
- expandTargetPath: expandToPath,
3069
- anatomyPaths
3070
- },
3071
- key
3072
- )) }, bulkRevision)
3073
- ] }) });
3074
- }
3075
- const JsonViewerFromString = reactExports.memo(function JsonViewerFromString2({
3076
- text,
3077
- defaultExpandDepth = 0,
3078
- className,
3079
- bulkDepth,
3080
- bulkRevision
3081
- }) {
3082
- const parsed = reactExports.useMemo(() => parseJsonText(text), [text]);
3083
- if (parsed.kind === "json") {
3084
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
3085
- JsonViewer,
3086
- {
3087
- data: parsed.data,
3088
- defaultExpandDepth,
3089
- className,
3090
- bulkDepth,
3091
- bulkRevision
3092
- }
3093
- );
3094
- }
3095
- return /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: cn("font-mono text-xs whitespace-pre-wrap break-words", className), children: text });
3096
- });
3097
- const jsonViewer = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
3098
- __proto__: null,
3099
- JsonViewer,
3100
- JsonViewerFromString
3101
- }, Symbol.toStringTag, { value: "Module" }));
3102
2950
  function ScrollArea({
3103
2951
  className,
3104
2952
  children,
@@ -3253,10 +3101,16 @@ function ToolRow({ tool, called }) {
3253
3101
  }
3254
3102
  )
3255
3103
  ] }),
3256
- /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 border-t border-white/10 bg-white/[0.02] px-2.5 py-2", children: [
3104
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CollapsibleContent, { children: open && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2 border-t border-white/10 bg-white/[0.02] px-2.5 py-2", children: [
3257
3105
  tool.description !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: tool.description }),
3258
3106
  /* @__PURE__ */ jsxRuntimeExports.jsx(RequiredParameters, { names: tool.requiredParameters }),
3259
- tool.schema !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "max-h-64 rounded-md border border-white/10 bg-[#06080b] p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(JsonViewer, { data: safeJsonValue(tool.schema), defaultExpandDepth: 0 }) })
3107
+ tool.schema !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(ScrollArea, { className: "max-h-64 rounded-md border border-white/10 bg-[#06080b] p-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3108
+ reactExports.Suspense,
3109
+ {
3110
+ fallback: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: "Loading JSON..." }),
3111
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(LazyJsonViewer, { data: safeJsonValue(tool.schema), defaultExpandDepth: 0 })
3112
+ }
3113
+ ) })
3260
3114
  ] }) })
3261
3115
  ] }) });
3262
3116
  }
@@ -3369,26 +3223,6 @@ const RequestTools = reactExports.memo(function RequestTools2({
3369
3223
  ] })
3370
3224
  ] });
3371
3225
  });
3372
- const LOG_FOCUS_REQUEST_EVENT = "agent-inspector:focus-log";
3373
- function dispatchLogFocusRequest(request) {
3374
- window.dispatchEvent(new CustomEvent(LOG_FOCUS_REQUEST_EVENT, { detail: request }));
3375
- }
3376
- function readLogFocusRequest(event) {
3377
- if (typeof CustomEvent === "undefined" || !(event instanceof CustomEvent)) return null;
3378
- const detail = event.detail;
3379
- const logId = safeGetOwnProperty(detail, "logId");
3380
- if (typeof logId !== "number" || !Number.isInteger(logId) || logId < 0) return null;
3381
- const tab = safeGetOwnProperty(detail, "tab");
3382
- switch (tab) {
3383
- case "anatomy":
3384
- case "request":
3385
- return { logId, tab };
3386
- case void 0:
3387
- return { logId, tab: "request" };
3388
- default:
3389
- return null;
3390
- }
3391
- }
3392
3226
  function shouldShowRawRequestTab(apiFormat, viewMode, strip) {
3393
3227
  return apiFormat === "anthropic" && viewMode === "full" && strip;
3394
3228
  }
@@ -3500,10 +3334,12 @@ function parseAnthropicTool(tool) {
3500
3334
  }
3501
3335
  function parseOpenAITool(tool) {
3502
3336
  const fn = safeGetOwnProperty(tool, "function");
3503
- const name = safeGetOwnProperty(fn, "name");
3337
+ const toolType = safeGetOwnProperty(tool, "type");
3338
+ if (fn === void 0 && toolType !== "function") return null;
3339
+ const name = safeGetOwnProperty(fn, "name") ?? safeGetOwnProperty(tool, "name");
3504
3340
  if (typeof name !== "string" || name.length === 0) return null;
3505
- const description = safeGetOwnProperty(fn, "description");
3506
- const schema = safeGetOwnProperty(fn, "parameters") ?? null;
3341
+ const description = safeGetOwnProperty(fn, "description") ?? safeGetOwnProperty(tool, "description");
3342
+ const schema = safeGetOwnProperty(fn, "parameters") ?? safeGetOwnProperty(tool, "parameters") ?? null;
3507
3343
  const normalizedDescription = typeof description === "string" ? description : null;
3508
3344
  return {
3509
3345
  name,
@@ -3576,7 +3412,7 @@ const FULL_LOG_HYDRATION_TIMEOUT_MS = 15e3;
3576
3412
  const BODY_CHUNK_TIMEOUT_MS = 15e3;
3577
3413
  const BODY_CHUNK_BYTES = 256 * 1024;
3578
3414
  const CHUNKED_BODY_THRESHOLD_BYTES = 1024 * 1024;
3579
- const TAB_TRIGGER_CLASS = "h-9 flex-none rounded-none border-0 border-b-2 border-transparent bg-transparent px-3 text-xs font-semibold text-muted-foreground shadow-none hover:bg-cyan-400/[0.04] hover:text-foreground data-[state=active]:border-cyan-300 data-[state=active]:bg-transparent data-[state=active]:text-cyan-100 data-[state=active]:shadow-none";
3415
+ const TAB_TRIGGER_CLASS = "h-9 flex-none rounded-none border-0 border-b-2 border-transparent bg-transparent px-3 text-xs font-semibold text-muted-foreground shadow-none transition-colors hover:bg-cyan-400/[0.05] hover:text-foreground data-[state=active]:border-cyan-300 data-[state=active]:bg-cyan-300/[0.04] data-[state=active]:text-cyan-100 data-[state=active]:shadow-none";
3580
3416
  function createEmptyBodyPreviewState(totalBytes = null) {
3581
3417
  return {
3582
3418
  status: "idle",
@@ -3627,7 +3463,7 @@ function ExpandedPanel({
3627
3463
  "div",
3628
3464
  {
3629
3465
  className: cn(
3630
- "inspector-scrollbar mx-3 mb-3 max-h-[72vh] overflow-auto rounded-md border border-white/10 bg-[#06080b] p-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]",
3466
+ "inspector-scrollbar mx-3 mb-3 max-h-[72vh] overflow-auto rounded-md border border-cyan-300/10 bg-[#05070a] p-3 shadow-[0_16px_45px_rgba(0,0,0,0.24),inset_0_1px_0_rgba(255,255,255,0.05)]",
3631
3467
  className
3632
3468
  ),
3633
3469
  children
@@ -3786,6 +3622,7 @@ const LogEntry = reactExports.memo(function({
3786
3622
  );
3787
3623
  const requestJsonRef = reactExports.useRef(null);
3788
3624
  const logIdRef = reactExports.useRef(log.id);
3625
+ const bodyHydrationInFlightRef = reactExports.useRef(false);
3789
3626
  const displayLog = hydratedLog ?? log;
3790
3627
  const useChunkedBody = hydratedLog === null && shouldUseChunkedBodyLoading(log);
3791
3628
  const requestPreviewBody = bodyPreviewTextForView(useChunkedBody, requestPreview);
@@ -3817,10 +3654,10 @@ const LogEntry = reactExports.memo(function({
3817
3654
  requestPreviewShouldParse ? displayedRequestBody : null
3818
3655
  );
3819
3656
  const rawRequestExpansion = useJsonBulkExpansion(
3820
- requestPreviewShouldParse ? rawRequestBodyForView : null
3657
+ activeTab === "raw-request" && requestPreviewShouldParse ? rawRequestBodyForView : null
3821
3658
  );
3822
3659
  const responseExpansion = useJsonBulkExpansion(
3823
- responsePreviewShouldParse ? responseTextForView : null
3660
+ activeTab === "raw" && responsePreviewShouldParse ? responseTextForView : null
3824
3661
  );
3825
3662
  const headersText = reactExports.useMemo(
3826
3663
  () => displayLog.headers && Object.keys(displayLog.headers).length > 0 ? JSON.stringify(displayLog.headers, null, 2) : null,
@@ -3993,6 +3830,7 @@ const LogEntry = reactExports.memo(function({
3993
3830
  }, [anatomySegments, expanded]);
3994
3831
  reactExports.useEffect(() => {
3995
3832
  logIdRef.current = log.id;
3833
+ bodyHydrationInFlightRef.current = false;
3996
3834
  setHydratedLog(null);
3997
3835
  setBodyHydrationState("idle");
3998
3836
  setRequestPreview(createEmptyBodyPreviewState(log.rawRequestBodyBytes ?? null));
@@ -4001,8 +3839,9 @@ const LogEntry = reactExports.memo(function({
4001
3839
  reactExports.useEffect(() => {
4002
3840
  if (!expanded) return;
4003
3841
  if (!shouldHydrateLogBody(log)) return;
4004
- if (hydratedLog !== null || bodyHydrationState !== "idle") return;
3842
+ if (hydratedLog !== null || bodyHydrationInFlightRef.current) return;
4005
3843
  let cancelled = false;
3844
+ bodyHydrationInFlightRef.current = true;
4006
3845
  setBodyHydrationState("loading");
4007
3846
  void fetchJsonWithTimeout(
4008
3847
  `/api/logs/${String(log.id)}`,
@@ -4010,17 +3849,19 @@ const LogEntry = reactExports.memo(function({
4010
3849
  FULL_LOG_HYDRATION_TIMEOUT_MS
4011
3850
  ).then((fullLog) => {
4012
3851
  if (cancelled) return;
3852
+ bodyHydrationInFlightRef.current = false;
4013
3853
  setHydratedLog(fullLog);
4014
3854
  setBodyHydrationState("idle");
4015
3855
  }).catch(() => {
4016
3856
  if (cancelled) return;
3857
+ bodyHydrationInFlightRef.current = false;
4017
3858
  setBodyHydrationState("failed");
4018
3859
  });
4019
3860
  return () => {
4020
3861
  cancelled = true;
3862
+ bodyHydrationInFlightRef.current = false;
4021
3863
  };
4022
3864
  }, [
4023
- bodyHydrationState,
4024
3865
  expanded,
4025
3866
  hydratedLog,
4026
3867
  log.bodyContentMode,
@@ -4060,10 +3901,27 @@ const LogEntry = reactExports.memo(function({
4060
3901
  "div",
4061
3902
  {
4062
3903
  className: cn(
4063
- "mb-2 overflow-hidden rounded-md border bg-[#090a0d] transition-[background-color,border-color,box-shadow]",
4064
- expanded ? "border-cyan-300/20 shadow-[0_18px_60px_rgba(0,0,0,0.28),inset_0_1px_0_rgba(255,255,255,0.05)]" : "border-border/70 hover:border-border"
3904
+ "relative mb-2 overflow-hidden rounded-lg border bg-[#07090d] transition-[background-color,border-color,box-shadow]",
3905
+ expanded ? "border-cyan-300/25 shadow-[0_24px_80px_rgba(0,0,0,0.34),0_0_0_1px_rgba(103,232,249,0.04),inset_0_1px_0_rgba(255,255,255,0.06)]" : "border-white/10 shadow-[0_6px_24px_rgba(0,0,0,0.16)] hover:border-cyan-300/15"
4065
3906
  ),
4066
3907
  children: [
3908
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3909
+ "div",
3910
+ {
3911
+ className: cn(
3912
+ "pointer-events-none absolute inset-y-3 left-1 w-px rounded-full",
3913
+ expanded ? "bg-cyan-300/45 shadow-[0_0_20px_rgba(103,232,249,0.28)]" : "bg-white/10"
3914
+ ),
3915
+ "aria-hidden": "true"
3916
+ }
3917
+ ),
3918
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3919
+ "div",
3920
+ {
3921
+ className: "pointer-events-none absolute inset-x-3 top-0 h-px bg-gradient-to-r from-transparent via-white/15 to-transparent",
3922
+ "aria-hidden": "true"
3923
+ }
3924
+ ),
4067
3925
  /* @__PURE__ */ jsxRuntimeExports.jsx(
4068
3926
  LogEntryHeader,
4069
3927
  {
@@ -4082,186 +3940,195 @@ const LogEntry = reactExports.memo(function({
4082
3940
  }
4083
3941
  }
4084
3942
  ),
4085
- expanded && /* @__PURE__ */ jsxRuntimeExports.jsx(
3943
+ expanded && /* @__PURE__ */ jsxRuntimeExports.jsxs(
4086
3944
  "div",
4087
3945
  {
4088
- className: "border-t border-white/10 bg-[#050607]/80 pt-2",
3946
+ className: "relative border-t border-cyan-300/10 bg-[linear-gradient(180deg,#06090d_0%,#040506_100%)] pt-2",
4089
3947
  onClick: (e) => e.stopPropagation(),
4090
3948
  onKeyDown: (e) => e.stopPropagation(),
4091
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "gap-2", children: [
4092
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
4093
- TabsList,
3949
+ children: [
3950
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3951
+ "div",
4094
3952
  {
4095
- variant: "line",
4096
- className: "inspector-scrollbar mx-3 h-auto w-[calc(100%-1.5rem)] justify-start overflow-x-auto rounded-none border-b border-white/10 bg-transparent p-0",
4097
- children: [
4098
- viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "raw-headers", children: "Raw Headers" }),
4099
- viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "headers", children: "Headers" }),
4100
- anatomySegments !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "anatomy", children: "Context" }),
4101
- shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "raw-request", children: "Raw Request" }),
4102
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "request", children: "Request" }),
4103
- viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "raw", children: "Raw Response" }),
4104
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "parsed", children: "Response" })
4105
- ]
3953
+ className: "pointer-events-none absolute inset-x-0 top-0 h-12 bg-gradient-to-b from-cyan-300/[0.035] to-transparent",
3954
+ "aria-hidden": "true"
4106
3955
  }
4107
3956
  ),
4108
- shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-request", children: activeTab === "raw-request" && /* @__PURE__ */ jsxRuntimeExports.jsxs(ExpandedPanel, { children: [
4109
- useChunkedBody && /* @__PURE__ */ jsxRuntimeExports.jsx(
4110
- BodyPreviewNotice,
4111
- {
4112
- label: "Request",
4113
- state: requestPreview,
4114
- expectedBytes: log.rawRequestBodyBytes ?? null,
4115
- onLoadMore: () => loadBodyChunk("request", requestPreview.offset)
4116
- }
4117
- ),
4118
- rawRequestBodyForView === null ? /* @__PURE__ */ jsxRuntimeExports.jsx(EmptyState, { children: useChunkedBody ? "Request preview is loading" : "No request body" }) : rawRequestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4119
- LazyJsonViewer,
4120
- {
4121
- data: rawRequestExpansion.parsedData,
4122
- bulkDepth: rawRequestExpansion.bulkDepth,
4123
- bulkRevision: rawRequestExpansion.bulkRevision
4124
- }
4125
- ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RawTextBlock, { children: rawRequestBodyForView })
4126
- ] }) }),
4127
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "request", children: activeTab === "request" && /* @__PURE__ */ jsxRuntimeExports.jsxs(ExpandedPanel, { children: [
4128
- bodyHydrationState === "loading" && /* @__PURE__ */ jsxRuntimeExports.jsx(NoticeBlock, { tone: "loading", children: "Loading full request and response bodies..." }),
4129
- bodyHydrationState === "failed" && /* @__PURE__ */ jsxRuntimeExports.jsx(NoticeBlock, { tone: "warning", children: "Full request and response bodies could not be loaded. Compact metadata is still available." }),
4130
- useChunkedBody && /* @__PURE__ */ jsxRuntimeExports.jsx(
4131
- BodyPreviewNotice,
3957
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "gap-2", children: [
3958
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3959
+ TabsList,
4132
3960
  {
4133
- label: "Request",
4134
- state: requestPreview,
4135
- expectedBytes: log.rawRequestBodyBytes ?? null,
4136
- onLoadMore: () => loadBodyChunk("request", requestPreview.offset)
3961
+ variant: "line",
3962
+ className: "inspector-scrollbar relative mx-3 h-auto w-[calc(100%-1.5rem)] justify-start overflow-x-auto rounded-none border-b border-cyan-300/10 bg-black/10 p-0",
3963
+ children: [
3964
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "raw-headers", children: "Raw Headers" }),
3965
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "headers", children: "Headers" }),
3966
+ anatomySegments !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "anatomy", children: "Context" }),
3967
+ shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "raw-request", children: "Raw Request" }),
3968
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "request", children: "Request" }),
3969
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "raw", children: "Raw Response" }),
3970
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { className: TAB_TRIGGER_CLASS, value: "parsed", children: "Response" })
3971
+ ]
4137
3972
  }
4138
3973
  ),
4139
- warnings.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(NoticeBlock, { tone: "warning", children: [
4140
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 font-semibold text-amber-300", children: [
4141
- /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3.5" }),
4142
- "Tool schema warnings"
3974
+ shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-request", children: activeTab === "raw-request" && /* @__PURE__ */ jsxRuntimeExports.jsxs(ExpandedPanel, { children: [
3975
+ useChunkedBody && /* @__PURE__ */ jsxRuntimeExports.jsx(
3976
+ BodyPreviewNotice,
3977
+ {
3978
+ label: "Request",
3979
+ state: requestPreview,
3980
+ expectedBytes: log.rawRequestBodyBytes ?? null,
3981
+ onLoadMore: () => loadBodyChunk("request", requestPreview.offset)
3982
+ }
3983
+ ),
3984
+ rawRequestBodyForView === null ? /* @__PURE__ */ jsxRuntimeExports.jsx(EmptyState, { children: useChunkedBody ? "Request preview is loading" : "No request body" }) : rawRequestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3985
+ LazyJsonViewer,
3986
+ {
3987
+ data: rawRequestExpansion.parsedData,
3988
+ bulkDepth: rawRequestExpansion.bulkDepth,
3989
+ bulkRevision: rawRequestExpansion.bulkRevision
3990
+ }
3991
+ ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RawTextBlock, { children: rawRequestBodyForView })
3992
+ ] }) }),
3993
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "request", children: activeTab === "request" && /* @__PURE__ */ jsxRuntimeExports.jsxs(ExpandedPanel, { children: [
3994
+ bodyHydrationState === "loading" && /* @__PURE__ */ jsxRuntimeExports.jsx(NoticeBlock, { tone: "loading", children: "Loading full request and response bodies..." }),
3995
+ bodyHydrationState === "failed" && /* @__PURE__ */ jsxRuntimeExports.jsx(NoticeBlock, { tone: "warning", children: "Full request and response bodies could not be loaded. Compact metadata is still available." }),
3996
+ useChunkedBody && /* @__PURE__ */ jsxRuntimeExports.jsx(
3997
+ BodyPreviewNotice,
3998
+ {
3999
+ label: "Request",
4000
+ state: requestPreview,
4001
+ expectedBytes: log.rawRequestBodyBytes ?? null,
4002
+ onLoadMore: () => loadBodyChunk("request", requestPreview.offset)
4003
+ }
4004
+ ),
4005
+ warnings.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(NoticeBlock, { tone: "warning", children: [
4006
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 font-semibold text-amber-300", children: [
4007
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3.5" }),
4008
+ "Tool schema warnings"
4009
+ ] }),
4010
+ /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "mt-2 space-y-1 text-muted-foreground", children: warnings.map((warning) => /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: warning }, warning)) })
4143
4011
  ] }),
4144
- /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "mt-2 space-y-1 text-muted-foreground", children: warnings.map((warning) => /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: warning }, warning)) })
4145
- ] }),
4146
- /* @__PURE__ */ jsxRuntimeExports.jsx(
4147
- RequestTools,
4148
- {
4149
- summary: requestTools,
4150
- calledToolNames: responseAnalysis.toolNames ?? []
4151
- }
4152
- ),
4153
- requestDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4154
- RequestDiffContent,
4155
- {
4156
- rawBody: displayLog.rawRequestBody,
4157
- displayedBody: displayedRequestBody,
4158
- emptyLabel: "No transformation applied; raw and sent request bodies are identical."
4159
- }
4160
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: requestJsonRef, children: displayedRequestBody === null ? /* @__PURE__ */ jsxRuntimeExports.jsx(EmptyState, { children: useChunkedBody ? "Request preview is loading" : "No request body" }) : requestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4161
- LazyJsonViewer,
4162
- {
4163
- data: requestExpansion.parsedData,
4164
- bulkDepth: requestExpansion.bulkDepth,
4165
- bulkRevision: requestExpansion.bulkRevision,
4166
- anatomyPaths,
4167
- expandToPath
4168
- }
4169
- ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RawTextBlock, { children: displayedRequestBody }) })
4170
- ] }) }),
4171
- anatomySegments !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "anatomy", children: activeTab === "anatomy" && /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandedPanel, { className: "p-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4172
- LazyRequestAnatomy,
4173
- {
4174
- parsed: requestExpansion.parsedData,
4175
- inputTokens: displayLog.inputTokens ?? null,
4176
- model: displayLog.model,
4177
- segments: anatomySegments,
4178
- onSegmentActivate: jumpToAnatomySegment
4179
- }
4180
- ) }) }) }),
4181
- viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "headers", children: activeTab === "headers" && /* @__PURE__ */ jsxRuntimeExports.jsxs(ExpandedPanel, { children: [
4182
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end gap-2 mb-2", children: shouldShowHeadersDiffButton(
4183
- viewMode,
4184
- displayLog.rawHeaders !== void 0 && Object.keys(displayLog.rawHeaders).length > 0
4185
- ) && /* @__PURE__ */ jsxRuntimeExports.jsx(
4186
- DiffToggleButton,
4187
- {
4188
- active: headersDiff,
4189
- onClick: (e) => {
4190
- e.stopPropagation();
4191
- setHeadersDiff(!headersDiff);
4012
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
4013
+ RequestTools,
4014
+ {
4015
+ summary: requestTools,
4016
+ calledToolNames: responseAnalysis.toolNames ?? []
4192
4017
  }
4193
- }
4194
- ) }),
4195
- headersDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4196
- HeadersDiffContent,
4197
- {
4198
- rawHeaders: displayLog.rawHeaders,
4199
- headers: displayLog.headers,
4200
- emptyLabel: "No transformation applied; raw and processed headers are identical."
4201
- }
4202
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx(HeaderRows, { headers: displayLog.headers, emptyLabel: "No headers captured" })
4203
- ] }) }),
4204
- viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-headers", children: activeTab === "raw-headers" && /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandedPanel, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4205
- HeaderRows,
4206
- {
4207
- headers: displayLog.rawHeaders,
4208
- emptyLabel: "No raw headers captured"
4209
- }
4210
- ) }) }),
4211
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: activeTab === "raw" && /* @__PURE__ */ jsxRuntimeExports.jsxs(ExpandedPanel, { className: "space-y-3", children: [
4212
- useChunkedBody && /* @__PURE__ */ jsxRuntimeExports.jsx(
4213
- BodyPreviewNotice,
4214
- {
4215
- label: "Response",
4216
- state: responsePreview,
4217
- expectedBytes: log.responseTextBytes ?? null,
4218
- onLoadMore: () => loadBodyChunk("response", responsePreview.offset)
4219
- }
4220
- ),
4221
- displayLog.error !== void 0 && displayLog.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(NoticeBlock, { tone: "danger", title: "SSE Error", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: displayLog.error }) }),
4222
- responseTextForView !== null && responsePreviewShouldParse ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4223
- LazyJsonViewerFromString,
4224
- {
4225
- text: responseTextForView,
4226
- defaultExpandDepth: 0,
4227
- bulkDepth: responseExpansion.bulkDepth,
4228
- bulkRevision: responseExpansion.bulkRevision
4229
- }
4230
- ) }) : responseTextForView !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(RawTextBlock, { children: responseTextForView }) : /* @__PURE__ */ jsxRuntimeExports.jsx(EmptyState, { children: useChunkedBody ? "Response preview is loading" : "No response" }),
4231
- displayLog.streaming === true && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4232
- LazyStreamingChunkSequence,
4233
- {
4234
- logId: displayLog.id,
4235
- truncated: displayLog.streamingChunksPath !== null
4236
- }
4237
- ) })
4238
- ] }) }),
4239
- /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: activeTab === "parsed" && /* @__PURE__ */ jsxRuntimeExports.jsxs(ExpandedPanel, { children: [
4240
- useChunkedBody && /* @__PURE__ */ jsxRuntimeExports.jsx(
4241
- BodyPreviewNotice,
4018
+ ),
4019
+ requestDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4020
+ RequestDiffContent,
4021
+ {
4022
+ rawBody: displayLog.rawRequestBody,
4023
+ displayedBody: displayedRequestBody,
4024
+ emptyLabel: "No transformation applied; raw and sent request bodies are identical."
4025
+ }
4026
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: requestJsonRef, children: displayedRequestBody === null ? /* @__PURE__ */ jsxRuntimeExports.jsx(EmptyState, { children: useChunkedBody ? "Request preview is loading" : "No request body" }) : requestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4027
+ LazyJsonViewer,
4028
+ {
4029
+ data: requestExpansion.parsedData,
4030
+ bulkDepth: requestExpansion.bulkDepth,
4031
+ bulkRevision: requestExpansion.bulkRevision,
4032
+ anatomyPaths,
4033
+ expandToPath
4034
+ }
4035
+ ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RawTextBlock, { children: displayedRequestBody }) })
4036
+ ] }) }),
4037
+ anatomySegments !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "anatomy", children: activeTab === "anatomy" && /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandedPanel, { className: "p-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4038
+ LazyRequestAnatomy,
4242
4039
  {
4243
- label: "Response",
4244
- state: responsePreview,
4245
- expectedBytes: log.responseTextBytes ?? null,
4246
- onLoadMore: () => loadBodyChunk("response", responsePreview.offset)
4040
+ parsed: requestExpansion.parsedData,
4041
+ inputTokens: displayLog.inputTokens ?? null,
4042
+ model: displayLog.model,
4043
+ segments: anatomySegments,
4044
+ onSegmentActivate: jumpToAnatomySegment
4247
4045
  }
4248
- ),
4249
- responsePreviewIsActive && !responsePreviewShouldParse ? /* @__PURE__ */ jsxRuntimeExports.jsx(RawTextBlock, { children: responseTextForView }) : /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4250
- LazyResponseView,
4046
+ ) }) }) }),
4047
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "headers", children: activeTab === "headers" && /* @__PURE__ */ jsxRuntimeExports.jsxs(ExpandedPanel, { children: [
4048
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end gap-2 mb-2", children: shouldShowHeadersDiffButton(
4049
+ viewMode,
4050
+ displayLog.rawHeaders !== void 0 && Object.keys(displayLog.rawHeaders).length > 0
4051
+ ) && /* @__PURE__ */ jsxRuntimeExports.jsx(
4052
+ DiffToggleButton,
4053
+ {
4054
+ active: headersDiff,
4055
+ onClick: (e) => {
4056
+ e.stopPropagation();
4057
+ setHeadersDiff(!headersDiff);
4058
+ }
4059
+ }
4060
+ ) }),
4061
+ headersDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
4062
+ HeadersDiffContent,
4063
+ {
4064
+ rawHeaders: displayLog.rawHeaders,
4065
+ headers: displayLog.headers,
4066
+ emptyLabel: "No transformation applied; raw and processed headers are identical."
4067
+ }
4068
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(HeaderRows, { headers: displayLog.headers, emptyLabel: "No headers captured" })
4069
+ ] }) }),
4070
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-headers", children: activeTab === "raw-headers" && /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandedPanel, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4071
+ HeaderRows,
4251
4072
  {
4252
- responseText: responseTextForView,
4253
- responseStatus: displayLog.responseStatus,
4254
- streaming: displayLog.streaming,
4255
- inputTokens: displayLog.inputTokens,
4256
- outputTokens: displayLog.outputTokens,
4257
- cacheCreationInputTokens: displayLog.cacheCreationInputTokens,
4258
- cacheReadInputTokens: displayLog.cacheReadInputTokens,
4259
- apiFormat: resolvedFormat,
4260
- error: displayLog.error
4073
+ headers: displayLog.rawHeaders,
4074
+ emptyLabel: "No raw headers captured"
4261
4075
  }
4262
- ) })
4263
- ] }) })
4264
- ] })
4076
+ ) }) }),
4077
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: activeTab === "raw" && /* @__PURE__ */ jsxRuntimeExports.jsxs(ExpandedPanel, { className: "space-y-3", children: [
4078
+ useChunkedBody && /* @__PURE__ */ jsxRuntimeExports.jsx(
4079
+ BodyPreviewNotice,
4080
+ {
4081
+ label: "Response",
4082
+ state: responsePreview,
4083
+ expectedBytes: log.responseTextBytes ?? null,
4084
+ onLoadMore: () => loadBodyChunk("response", responsePreview.offset)
4085
+ }
4086
+ ),
4087
+ displayLog.error !== void 0 && displayLog.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(NoticeBlock, { tone: "danger", title: "SSE Error", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: displayLog.error }) }),
4088
+ responseTextForView !== null && responsePreviewShouldParse ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4089
+ LazyJsonViewerFromString,
4090
+ {
4091
+ text: responseTextForView,
4092
+ defaultExpandDepth: 0,
4093
+ bulkDepth: responseExpansion.bulkDepth,
4094
+ bulkRevision: responseExpansion.bulkRevision
4095
+ }
4096
+ ) }) : responseTextForView !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(RawTextBlock, { children: responseTextForView }) : /* @__PURE__ */ jsxRuntimeExports.jsx(EmptyState, { children: useChunkedBody ? "Response preview is loading" : "No response" }),
4097
+ displayLog.streaming === true && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4098
+ LazyStreamingChunkSequence,
4099
+ {
4100
+ logId: displayLog.id,
4101
+ truncated: displayLog.streamingChunksPath !== null
4102
+ }
4103
+ ) })
4104
+ ] }) }),
4105
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: activeTab === "parsed" && /* @__PURE__ */ jsxRuntimeExports.jsxs(ExpandedPanel, { children: [
4106
+ useChunkedBody && /* @__PURE__ */ jsxRuntimeExports.jsx(
4107
+ BodyPreviewNotice,
4108
+ {
4109
+ label: "Response",
4110
+ state: responsePreview,
4111
+ expectedBytes: log.responseTextBytes ?? null,
4112
+ onLoadMore: () => loadBodyChunk("response", responsePreview.offset)
4113
+ }
4114
+ ),
4115
+ responsePreviewIsActive && !responsePreviewShouldParse ? /* @__PURE__ */ jsxRuntimeExports.jsx(RawTextBlock, { children: responseTextForView }) : /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4116
+ LazyResponseView,
4117
+ {
4118
+ responseText: responseTextForView,
4119
+ responseStatus: displayLog.responseStatus,
4120
+ streaming: displayLog.streaming,
4121
+ inputTokens: displayLog.inputTokens,
4122
+ outputTokens: displayLog.outputTokens,
4123
+ cacheCreationInputTokens: displayLog.cacheCreationInputTokens,
4124
+ cacheReadInputTokens: displayLog.cacheReadInputTokens,
4125
+ apiFormat: resolvedFormat,
4126
+ error: displayLog.error
4127
+ }
4128
+ ) })
4129
+ ] }) })
4130
+ ] })
4131
+ ]
4265
4132
  }
4266
4133
  )
4267
4134
  ]
@@ -4383,6 +4250,19 @@ function ToolTraceEvents({ events }) {
4383
4250
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-3 mb-2 grid gap-1.5", children: events.map((event) => /* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEventRow, { event }, event.id)) });
4384
4251
  }
4385
4252
  const PREVIEW_LIMIT = 180;
4253
+ const toolTraceCache = /* @__PURE__ */ new WeakMap();
4254
+ function responseMayContainToolTrace(log, format) {
4255
+ const responseText = log.responseText;
4256
+ if (responseText === null) return false;
4257
+ switch (format) {
4258
+ case "anthropic":
4259
+ return responseText.includes("tool_use");
4260
+ case "openai":
4261
+ return responseText.includes("tool_calls") || responseText.includes("function_call");
4262
+ case "unknown":
4263
+ return false;
4264
+ }
4265
+ }
4386
4266
  function shouldRenderConversationContent(standalone, expanded) {
4387
4267
  return standalone || expanded;
4388
4268
  }
@@ -4494,18 +4374,59 @@ function extractOpenAIToolTraceEvents(log) {
4494
4374
  });
4495
4375
  }
4496
4376
  }
4377
+ const output = safeGetOwnProperty(parsed, "output");
4378
+ if (Array.isArray(output)) {
4379
+ for (const item of output) {
4380
+ if (safeGetOwnProperty(item, "type") !== "function_call") continue;
4381
+ const name = safeGetOwnProperty(item, "name");
4382
+ if (typeof name !== "string" || name.length === 0) continue;
4383
+ const args = safeGetOwnProperty(item, "arguments");
4384
+ events.push({
4385
+ id: `${String(log.id)}-openai-tool-${String(events.length)}`,
4386
+ logId: log.id,
4387
+ index: events.length,
4388
+ provider: "openai",
4389
+ name,
4390
+ argumentsText: copyValue(args),
4391
+ argumentsPreview: previewValue(args)
4392
+ });
4393
+ }
4394
+ }
4497
4395
  return events;
4498
4396
  }
4499
4397
  function extractToolTraceEvents(log) {
4398
+ const cached = toolTraceCache.get(log);
4399
+ if (cached !== void 0 && cached.apiFormat === log.apiFormat && cached.responseText === log.responseText) {
4400
+ return cached.events;
4401
+ }
4500
4402
  const format = resolveLogFormat(log);
4403
+ if (!responseMayContainToolTrace(log, format)) {
4404
+ const events2 = [];
4405
+ toolTraceCache.set(log, {
4406
+ apiFormat: log.apiFormat,
4407
+ responseText: log.responseText,
4408
+ events: events2
4409
+ });
4410
+ return events2;
4411
+ }
4412
+ let events;
4501
4413
  switch (format) {
4502
4414
  case "anthropic":
4503
- return extractAnthropicToolTraceEvents(log);
4415
+ events = extractAnthropicToolTraceEvents(log);
4416
+ break;
4504
4417
  case "openai":
4505
- return extractOpenAIToolTraceEvents(log);
4418
+ events = extractOpenAIToolTraceEvents(log);
4419
+ break;
4506
4420
  case "unknown":
4507
- return [];
4421
+ events = [];
4422
+ break;
4508
4423
  }
4424
+ toolTraceCache.set(log, {
4425
+ apiFormat: log.apiFormat,
4426
+ responseText: log.responseText,
4427
+ events
4428
+ });
4429
+ return events;
4509
4430
  }
4510
4431
  function buildTraceSummary(logs, slowResponseThresholdSeconds, knowledgeCandidateCount = 0) {
4511
4432
  let failedCallCount = 0;
@@ -4571,7 +4492,8 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
4571
4492
  onCompareWithPrevious,
4572
4493
  comparisonPredecessors,
4573
4494
  turnIndex = 0,
4574
- timeDisplayFormat
4495
+ timeDisplayFormat,
4496
+ pendingFocusRequest
4575
4497
  }) {
4576
4498
  const lastIdx = entries.length - 1;
4577
4499
  const lastStop = entries[lastIdx]?.stopReason ?? null;
@@ -4601,6 +4523,19 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
4601
4523
  window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4602
4524
  };
4603
4525
  }, [collapsible, entries]);
4526
+ reactExports.useEffect(() => {
4527
+ if (pendingFocusRequest === void 0) return void 0;
4528
+ if (!entries.some((entry) => entry.log.id === pendingFocusRequest.logId)) return void 0;
4529
+ if (collapsible) setCollapsed(false);
4530
+ const timer = window.setTimeout(() => {
4531
+ dispatchLogFocusRequest({
4532
+ logId: pendingFocusRequest.logId,
4533
+ tab: pendingFocusRequest.tab,
4534
+ source: "virtualizer"
4535
+ });
4536
+ }, 0);
4537
+ return () => window.clearTimeout(timer);
4538
+ }, [collapsible, entries, pendingFocusRequest]);
4604
4539
  const toggleCollapse = reactExports.useCallback(() => {
4605
4540
  if (collapsible) setCollapsed((prev) => !prev);
4606
4541
  }, [collapsible]);
@@ -4639,7 +4574,7 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
4639
4574
  }, [entries]);
4640
4575
  const StartCrab = reactExports.useMemo(() => getCrabVariant(entries[0]?.log.id ?? 0), [entries]);
4641
4576
  const EndCrab = reactExports.useMemo(() => getCrabVariant(entries[lastIdx]?.log.id ?? 0), [entries, lastIdx]);
4642
- const bgClass = turnIndex % 2 === 0 ? "bg-muted/10" : "bg-muted/25";
4577
+ const bgClass = turnIndex % 2 === 0 ? "bg-[#070a0f]" : "bg-[#090d12]";
4643
4578
  const aggregateIsSlow = aggregate.maxElapsed !== null && slowResponseThresholdSeconds > 0 && aggregate.maxElapsed > slowResponseThresholdSeconds * 1e3;
4644
4579
  const toolEventsByLogId = reactExports.useMemo(() => {
4645
4580
  const events = /* @__PURE__ */ new Map();
@@ -4667,17 +4602,25 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
4667
4602
  }, []);
4668
4603
  const firstLogId = entries[0]?.log.id ?? turnIndex;
4669
4604
  const turnLabel = `Turn ${String(turnIndex + 1)}`;
4605
+ const containmentStyle = reactExports.useMemo(
4606
+ () => ({
4607
+ contentVisibility: "auto",
4608
+ containIntrinsicSize: collapsed ? "48px" : "160px"
4609
+ }),
4610
+ [collapsed]
4611
+ );
4670
4612
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
4671
4613
  "div",
4672
4614
  {
4673
4615
  ref: containerRef,
4616
+ style: containmentStyle,
4674
4617
  tabIndex: collapsed ? void 0 : 0,
4675
4618
  role: collapsed ? void 0 : "group",
4676
4619
  "aria-label": collapsed ? void 0 : turnLabel,
4677
4620
  "data-nav-id": collapsed ? void 0 : `turn-${String(firstLogId)}`,
4678
4621
  className: cn(
4679
- "border rounded-lg",
4680
- isPending ? "border-amber-500/10" : "border-transparent",
4622
+ "border rounded-lg transition-[border-color,box-shadow]",
4623
+ isPending ? "border-amber-300/15 shadow-[0_0_0_1px_rgba(252,211,77,0.04)]" : "border-transparent",
4681
4624
  !collapsed && "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
4682
4625
  ),
4683
4626
  children: collapsed ? (
@@ -4763,7 +4706,7 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
4763
4706
  "div",
4764
4707
  {
4765
4708
  className: cn(
4766
- "flex-1 min-w-0 mb-0.5 rounded-lg border border-border py-1 px-3 flex items-center gap-3 text-xs",
4709
+ "flex-1 min-w-0 mb-0.5 rounded-lg border border-cyan-300/10 py-1.5 px-3 flex items-center gap-3 text-xs shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]",
4767
4710
  bgClass
4768
4711
  ),
4769
4712
  children: [
@@ -4861,6 +4804,103 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
4861
4804
  }
4862
4805
  );
4863
4806
  });
4807
+ const VIRTUALIZE_TURN_THRESHOLD = 24;
4808
+ const ESTIMATED_TURN_HEIGHT = 132;
4809
+ const VIRTUAL_OVERSCAN$1 = 8;
4810
+ function shouldVirtualizeTurnGroups(turnCount) {
4811
+ return turnCount > VIRTUALIZE_TURN_THRESHOLD;
4812
+ }
4813
+ function buildLogTurnIndex(turnGroups) {
4814
+ const result = /* @__PURE__ */ new Map();
4815
+ for (let index = 0; index < turnGroups.length; index++) {
4816
+ const group = turnGroups[index];
4817
+ if (group === void 0) continue;
4818
+ for (const entry of group.entries) {
4819
+ result.set(entry.log.id, index);
4820
+ }
4821
+ }
4822
+ return result;
4823
+ }
4824
+ function renderTurnGroup(turnGroup, props, pendingFocusRequest) {
4825
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4826
+ TurnGroup,
4827
+ {
4828
+ entries: turnGroup.entries,
4829
+ viewMode: props.viewMode,
4830
+ strip: props.strip,
4831
+ slowResponseThresholdSeconds: props.slowResponseThresholdSeconds,
4832
+ timeDisplayFormat: props.timeDisplayFormat,
4833
+ cacheTrends: props.cacheTrends,
4834
+ onCompareWithPrevious: props.onCompareWithPrevious,
4835
+ comparisonPredecessors: props.comparisonPredecessors,
4836
+ turnIndex: turnGroup.turnIndex,
4837
+ pendingFocusRequest
4838
+ },
4839
+ turnGroup.turnIndex
4840
+ );
4841
+ }
4842
+ function TurnGroupList(props) {
4843
+ if (!shouldVirtualizeTurnGroups(props.turnGroups.length)) {
4844
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: props.turnGroups.map((turnGroup) => renderTurnGroup(turnGroup, props)) });
4845
+ }
4846
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(VirtualizedTurnGroupList, { ...props });
4847
+ }
4848
+ function VirtualizedTurnGroupList(props) {
4849
+ const { turnGroups } = props;
4850
+ const [pendingFocusRequest, setPendingFocusRequest] = reactExports.useState(
4851
+ null
4852
+ );
4853
+ const virtualizer = useWindowVirtualizer({
4854
+ count: turnGroups.length,
4855
+ estimateSize: () => ESTIMATED_TURN_HEIGHT,
4856
+ overscan: VIRTUAL_OVERSCAN$1,
4857
+ getItemKey: (index) => turnGroups[index]?.turnIndex ?? index
4858
+ });
4859
+ const turnIndexByLogId = reactExports.useMemo(() => buildLogTurnIndex(turnGroups), [turnGroups]);
4860
+ reactExports.useEffect(() => {
4861
+ const handleLogFocusRequest = (event) => {
4862
+ const request = readLogFocusRequest(event);
4863
+ if (request === null) return;
4864
+ if (request.source === "virtualizer") return;
4865
+ const turnIndex = turnIndexByLogId.get(request.logId);
4866
+ if (turnIndex === void 0) return;
4867
+ virtualizer.scrollToIndex(turnIndex, { align: "center" });
4868
+ setPendingFocusRequest((previous) => ({
4869
+ ...request,
4870
+ turnIndex,
4871
+ nonce: previous === null ? 1 : previous.nonce + 1
4872
+ }));
4873
+ };
4874
+ window.addEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4875
+ return () => {
4876
+ window.removeEventListener(LOG_FOCUS_REQUEST_EVENT, handleLogFocusRequest);
4877
+ };
4878
+ }, [turnIndexByLogId, virtualizer]);
4879
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4880
+ "div",
4881
+ {
4882
+ className: "relative w-full",
4883
+ style: { height: `${virtualizer.getTotalSize()}px` },
4884
+ "data-virtualized-turn-list": "true",
4885
+ children: virtualizer.getVirtualItems().map((virtualItem) => {
4886
+ const turnGroup = turnGroups[virtualItem.index];
4887
+ if (turnGroup === void 0) return null;
4888
+ const itemFocusRequest = pendingFocusRequest !== null && pendingFocusRequest.turnIndex === virtualItem.index ? pendingFocusRequest : void 0;
4889
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
4890
+ "div",
4891
+ {
4892
+ "data-index": virtualItem.index,
4893
+ ref: virtualizer.measureElement,
4894
+ className: "absolute left-0 top-0 w-full",
4895
+ style: { transform: `translateY(${virtualItem.start}px)` },
4896
+ children: renderTurnGroup(turnGroup, props, itemFocusRequest)
4897
+ },
4898
+ virtualItem.key
4899
+ );
4900
+ })
4901
+ }
4902
+ );
4903
+ }
4864
4904
  const CandidateResponseSchema = object({
4865
4905
  candidates: array(KnowledgeCandidateSchema)
4866
4906
  });
@@ -5703,21 +5743,19 @@ const ConversationGroup = reactExports.memo(function({
5703
5743
  timeDisplayFormat
5704
5744
  }
5705
5745
  ),
5706
- turnGroups.map((tg) => /* @__PURE__ */ jsxRuntimeExports.jsx(
5707
- TurnGroup,
5746
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5747
+ TurnGroupList,
5708
5748
  {
5709
- entries: tg.entries,
5749
+ turnGroups,
5710
5750
  viewMode,
5711
5751
  strip,
5712
5752
  slowResponseThresholdSeconds,
5713
5753
  timeDisplayFormat,
5714
5754
  cacheTrends,
5715
5755
  onCompareWithPrevious,
5716
- comparisonPredecessors,
5717
- turnIndex: tg.turnIndex
5718
- },
5719
- tg.turnIndex
5720
- ))
5756
+ comparisonPredecessors
5757
+ }
5758
+ )
5721
5759
  ] })
5722
5760
  ] });
5723
5761
  });
@@ -7449,6 +7487,7 @@ const ExternalProviderSchema = object({
7449
7487
  format: _enum(["anthropic", "openai"]),
7450
7488
  anthropicBaseUrl: string(),
7451
7489
  openaiBaseUrl: string(),
7490
+ openaiResponsesBaseUrl: string().optional().default(""),
7452
7491
  models: array(string()),
7453
7492
  sourceTool: _enum(["claude-code", "opencode", "mimo-code"]),
7454
7493
  alreadyExists: boolean()
@@ -7545,6 +7584,7 @@ function ImportWizardDialog({
7545
7584
  format: p.format,
7546
7585
  anthropicBaseUrl: p.anthropicBaseUrl,
7547
7586
  openaiBaseUrl: p.openaiBaseUrl,
7587
+ openaiResponsesBaseUrl: p.openaiResponsesBaseUrl,
7548
7588
  models: p.models,
7549
7589
  createdAt: now,
7550
7590
  updatedAt: now
@@ -7624,7 +7664,7 @@ function ImportWizardDialog({
7624
7664
  p.models.slice(0, 4).join(", "),
7625
7665
  p.models.length > 4 ? ` +${p.models.length - 4} more` : ""
7626
7666
  ] }),
7627
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground mt-0.5 truncate", children: p.format === "anthropic" ? p.anthropicBaseUrl : p.openaiBaseUrl }),
7667
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground mt-0.5 truncate", children: p.format === "anthropic" ? p.anthropicBaseUrl : p.openaiResponsesBaseUrl !== "" ? p.openaiResponsesBaseUrl : p.openaiBaseUrl }),
7628
7668
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground mt-0.5 font-mono", children: p.apiKey.length > 8 ? `${p.apiKey.slice(0, 4)}••••${p.apiKey.slice(-4)}` : "••••" })
7629
7669
  ] })
7630
7670
  ]
@@ -8069,12 +8109,17 @@ function ProviderCard({
8069
8109
  ] }),
8070
8110
  provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [
8071
8111
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 text-xs text-muted-foreground", children: [
8072
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: "OpenAI:" }),
8112
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: "OpenAI Chat:" }),
8073
8113
  " ",
8074
8114
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: provider.openaiBaseUrl })
8075
8115
  ] }),
8076
8116
  testResults !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: testResults.openai })
8077
8117
  ] }),
8118
+ provider.openaiResponsesBaseUrl !== void 0 && provider.openaiResponsesBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex min-w-0 flex-col gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 text-xs text-muted-foreground", children: [
8119
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: "OpenAI Responses:" }),
8120
+ " ",
8121
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: provider.openaiResponsesBaseUrl })
8122
+ ] }) }),
8078
8123
  testResults?.testedAt !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [
8079
8124
  /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
8080
8125
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
@@ -8166,12 +8211,17 @@ const ZHIPU_CODING_PROVIDER_KEYWORDS = [
8166
8211
  "z.ai coding",
8167
8212
  "zai coding"
8168
8213
  ];
8214
+ const ANTHROPIC_MESSAGES_ENDPOINT = "/v1/messages";
8215
+ const OPENAI_CHAT_COMPLETIONS_ENDPOINT = "/v1/chat/completions";
8216
+ const OPENAI_RESPONSES_ENDPOINT = "/v1/responses";
8169
8217
  const KNOWN_PROVIDER_PRESETS = {
8170
8218
  deepseek: {
8171
8219
  openaiBaseUrl: "https://api.deepseek.com"
8172
8220
  },
8173
8221
  minimax: {
8174
8222
  anthropicBaseUrl: "https://api.minimaxi.com/anthropic",
8223
+ openaiBaseUrl: "https://api.minimaxi.com",
8224
+ openaiResponsesBaseUrl: "https://api.minimaxi.com",
8175
8225
  apiDocsUrl: "https://platform.minimaxi.com/docs/api-reference/api-overview"
8176
8226
  },
8177
8227
  "zhipu coding": {
@@ -8345,6 +8395,27 @@ function readPositiveIntegerInput(value) {
8345
8395
  if (!Number.isInteger(parsed) || parsed <= 0) return null;
8346
8396
  return parsed;
8347
8397
  }
8398
+ function stripEndpointSuffix(value, endpoint) {
8399
+ if (value === void 0) return "";
8400
+ const trimmed = value.trim();
8401
+ const normalized = trimmed.replace(/\/+$/, "");
8402
+ return normalized.endsWith(endpoint) ? normalized.slice(0, -endpoint.length).replace(/\/+$/, "") : trimmed;
8403
+ }
8404
+ function previewEndpointUrl(baseUrl, endpoint) {
8405
+ const trimmed = baseUrl.trim();
8406
+ return trimmed === "" ? null : buildUpstreamUrl(trimmed, endpoint);
8407
+ }
8408
+ function EndpointUrlPreview({
8409
+ baseUrl,
8410
+ endpoint
8411
+ }) {
8412
+ const finalUrl = previewEndpointUrl(baseUrl, endpoint);
8413
+ if (finalUrl === null) return null;
8414
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border/70 bg-muted/35 px-3 py-2 text-xs", children: [
8415
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground", children: "Final URL" }),
8416
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "break-all font-mono text-foreground", children: finalUrl })
8417
+ ] });
8418
+ }
8348
8419
  function ProviderForm({ provider, onSubmit, onCancel }) {
8349
8420
  const [name, setName] = reactExports.useState(provider?.name ?? "");
8350
8421
  const [apiKey, setApiKey] = reactExports.useState(provider?.apiKey ?? "");
@@ -8361,8 +8432,15 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
8361
8432
  )
8362
8433
  );
8363
8434
  const [activeTab, setActiveTab] = reactExports.useState("anthropic");
8364
- const [anthropicBaseUrl, setAnthropicBaseUrl] = reactExports.useState(provider?.anthropicBaseUrl ?? "");
8365
- const [openaiBaseUrl, setOpenaiBaseUrl] = reactExports.useState(provider?.openaiBaseUrl ?? "");
8435
+ const [anthropicBaseUrl, setAnthropicBaseUrl] = reactExports.useState(
8436
+ stripEndpointSuffix(provider?.anthropicBaseUrl, ANTHROPIC_MESSAGES_ENDPOINT)
8437
+ );
8438
+ const [openaiBaseUrl, setOpenaiBaseUrl] = reactExports.useState(
8439
+ stripEndpointSuffix(provider?.openaiBaseUrl, OPENAI_CHAT_COMPLETIONS_ENDPOINT)
8440
+ );
8441
+ const [openaiResponsesBaseUrl, setOpenaiResponsesBaseUrl] = reactExports.useState(
8442
+ stripEndpointSuffix(provider?.openaiResponsesBaseUrl, OPENAI_RESPONSES_ENDPOINT)
8443
+ );
8366
8444
  const [apiDocsUrl, setApiDocsUrl] = reactExports.useState(provider?.apiDocsUrl ?? "");
8367
8445
  const [modelMetadataUrl, setModelMetadataUrl] = reactExports.useState(provider?.modelMetadataUrl ?? "");
8368
8446
  const [source, setSource] = reactExports.useState(provider?.source);
@@ -8385,6 +8463,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
8385
8463
  }, [openModelDropdown]);
8386
8464
  const [manualAnthropicUrlOverride, setManualAnthropicUrlOverride] = reactExports.useState(false);
8387
8465
  const [manualOpenaiUrlOverride, setManualOpenaiUrlOverride] = reactExports.useState(false);
8466
+ const [manualOpenaiResponsesUrlOverride, setManualOpenaiResponsesUrlOverride] = reactExports.useState(false);
8388
8467
  const isMiniMax = name.toLowerCase().includes("minimax");
8389
8468
  const isAlibaba = name.toLowerCase().includes("alibaba");
8390
8469
  const isZhipu = [...ZHIPU_PROVIDER_KEYWORDS, ...ZHIPU_CODING_PROVIDER_KEYWORDS].some(
@@ -8406,13 +8485,21 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
8406
8485
  const nextModels = (provider.models?.length ?? 0) > 0 ? provider.models : [""];
8407
8486
  setModels(nextModels);
8408
8487
  setModelMetadataDrafts(createMetadataDrafts(provider, nextModels));
8409
- setAnthropicBaseUrl(provider.anthropicBaseUrl ?? "");
8410
- setOpenaiBaseUrl(provider.openaiBaseUrl ?? "");
8488
+ setAnthropicBaseUrl(
8489
+ stripEndpointSuffix(provider.anthropicBaseUrl, ANTHROPIC_MESSAGES_ENDPOINT)
8490
+ );
8491
+ setOpenaiBaseUrl(
8492
+ stripEndpointSuffix(provider.openaiBaseUrl, OPENAI_CHAT_COMPLETIONS_ENDPOINT)
8493
+ );
8494
+ setOpenaiResponsesBaseUrl(
8495
+ stripEndpointSuffix(provider.openaiResponsesBaseUrl, OPENAI_RESPONSES_ENDPOINT)
8496
+ );
8411
8497
  setApiDocsUrl(provider.apiDocsUrl ?? "");
8412
8498
  setModelMetadataUrl(provider.modelMetadataUrl ?? "");
8413
8499
  setSource(provider.source);
8414
8500
  setManualAnthropicUrlOverride(false);
8415
8501
  setManualOpenaiUrlOverride(false);
8502
+ setManualOpenaiResponsesUrlOverride(false);
8416
8503
  }
8417
8504
  }, [provider]);
8418
8505
  reactExports.useEffect(() => {
@@ -8425,6 +8512,9 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
8425
8512
  if (preset.openaiBaseUrl !== void 0 && !manualOpenaiUrlOverride) {
8426
8513
  setOpenaiBaseUrl(preset.openaiBaseUrl);
8427
8514
  }
8515
+ if (preset.openaiResponsesBaseUrl !== void 0 && !manualOpenaiResponsesUrlOverride) {
8516
+ setOpenaiResponsesBaseUrl(preset.openaiResponsesBaseUrl);
8517
+ }
8428
8518
  if (preset.apiDocsUrl !== void 0 && !apiDocsUrl) {
8429
8519
  setApiDocsUrl(preset.apiDocsUrl);
8430
8520
  }
@@ -8458,6 +8548,9 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
8458
8548
  if (openaiBaseUrl.trim() && !isValidUrl(openaiBaseUrl.trim())) {
8459
8549
  newErrors.openaiBaseUrl = "Invalid URL format";
8460
8550
  }
8551
+ if (openaiResponsesBaseUrl.trim() && !isValidUrl(openaiResponsesBaseUrl.trim())) {
8552
+ newErrors.openaiResponsesBaseUrl = "Invalid URL format";
8553
+ }
8461
8554
  if (modelMetadataUrl.trim() && !isValidUrl(modelMetadataUrl.trim())) {
8462
8555
  newErrors.modelMetadataUrl = "Invalid URL format";
8463
8556
  }
@@ -8474,7 +8567,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
8474
8567
  newErrors[`modelMetadata.${index}.outputLimit`] = "Output limit should not exceed context window";
8475
8568
  }
8476
8569
  });
8477
- if (!anthropicBaseUrl.trim() && !openaiBaseUrl.trim()) {
8570
+ if (!anthropicBaseUrl.trim() && !openaiBaseUrl.trim() && !openaiResponsesBaseUrl.trim()) {
8478
8571
  newErrors.format = "At least one format URL (Anthropic or OpenAI) is required";
8479
8572
  }
8480
8573
  setErrors(newErrors);
@@ -8590,6 +8683,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
8590
8683
  models: models.map((m) => m.trim()).filter((m) => m !== ""),
8591
8684
  anthropicBaseUrl: anthropicBaseUrl.trim() || void 0,
8592
8685
  openaiBaseUrl: openaiBaseUrl.trim() || void 0,
8686
+ openaiResponsesBaseUrl: openaiResponsesBaseUrl.trim() || void 0,
8593
8687
  apiDocsUrl: apiDocsUrl.trim() || void 0,
8594
8688
  modelMetadataUrl: modelMetadataUrl.trim() || void 0,
8595
8689
  modelMetadata: buildModelMetadata(),
@@ -8902,41 +8996,86 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
8902
8996
  ] }),
8903
8997
  activeTab === "anthropic" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
8904
8998
  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-anthropic-base-url", className: "text-sm font-medium", children: "Anthropic Base URL" }),
8905
- /* @__PURE__ */ jsxRuntimeExports.jsx(
8906
- "input",
8907
- {
8908
- id: "provider-anthropic-base-url",
8909
- type: "text",
8910
- value: anthropicBaseUrl,
8911
- onChange: (e) => {
8912
- setManualAnthropicUrlOverride(true);
8913
- setAnthropicBaseUrl(e.target.value);
8914
- },
8915
- placeholder: "https://api.anthropic.com",
8916
- className: "w-full rounded-md border border-input bg-background px-3 py-2 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"
8917
- }
8918
- ),
8999
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 overflow-hidden rounded-md border border-input bg-background ring-offset-background focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
9000
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9001
+ "input",
9002
+ {
9003
+ id: "provider-anthropic-base-url",
9004
+ type: "text",
9005
+ value: anthropicBaseUrl,
9006
+ onChange: (e) => {
9007
+ setManualAnthropicUrlOverride(true);
9008
+ setAnthropicBaseUrl(e.target.value);
9009
+ },
9010
+ placeholder: "https://api.anthropic.com",
9011
+ className: "min-w-0 flex-1 bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
9012
+ }
9013
+ ),
9014
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l bg-muted px-3 py-2 font-mono text-xs text-muted-foreground", children: ANTHROPIC_MESSAGES_ENDPOINT })
9015
+ ] }),
8919
9016
  errors.anthropicBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.anthropicBaseUrl }),
8920
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Anthropic-compatible endpoint URL. Leave empty if this provider does not support Anthropic format." })
9017
+ /* @__PURE__ */ jsxRuntimeExports.jsx(EndpointUrlPreview, { baseUrl: anthropicBaseUrl, endpoint: ANTHROPIC_MESSAGES_ENDPOINT }),
9018
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Anthropic-compatible base URL. Leave empty if this provider does not support Anthropic format." })
8921
9019
  ] }),
8922
- activeTab === "openai" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
8923
- /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-openai-base-url", className: "text-sm font-medium", children: "OpenAI Base URL" }),
8924
- /* @__PURE__ */ jsxRuntimeExports.jsx(
8925
- "input",
8926
- {
8927
- id: "provider-openai-base-url",
8928
- type: "text",
8929
- value: openaiBaseUrl,
8930
- onChange: (e) => {
8931
- setManualOpenaiUrlOverride(true);
8932
- setOpenaiBaseUrl(e.target.value);
8933
- },
8934
- placeholder: "https://api.openai.com/v1",
8935
- className: "w-full rounded-md border border-input bg-background px-3 py-2 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"
8936
- }
8937
- ),
8938
- errors.openaiBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiBaseUrl }),
8939
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "OpenAI-compatible endpoint URL. Leave empty if this provider does not support OpenAI format." })
9020
+ activeTab === "openai" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
9021
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
9022
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-openai-base-url", className: "text-sm font-medium", children: "OpenAI Chat Base URL" }),
9023
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 overflow-hidden rounded-md border border-input bg-background ring-offset-background focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
9024
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9025
+ "input",
9026
+ {
9027
+ id: "provider-openai-base-url",
9028
+ type: "text",
9029
+ value: openaiBaseUrl,
9030
+ onChange: (e) => {
9031
+ setManualOpenaiUrlOverride(true);
9032
+ setOpenaiBaseUrl(e.target.value);
9033
+ },
9034
+ placeholder: "https://api.openai.com",
9035
+ className: "min-w-0 flex-1 bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
9036
+ }
9037
+ ),
9038
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l bg-muted px-3 py-2 font-mono text-xs text-muted-foreground", children: OPENAI_CHAT_COMPLETIONS_ENDPOINT })
9039
+ ] }),
9040
+ errors.openaiBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiBaseUrl }),
9041
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9042
+ EndpointUrlPreview,
9043
+ {
9044
+ baseUrl: openaiBaseUrl,
9045
+ endpoint: OPENAI_CHAT_COMPLETIONS_ENDPOINT
9046
+ }
9047
+ ),
9048
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "OpenAI Chat Completions-compatible base URL. Leave empty if this provider only supports the Responses API." })
9049
+ ] }),
9050
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
9051
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-openai-responses-base-url", className: "text-sm font-medium", children: "OpenAI Responses Base URL" }),
9052
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 overflow-hidden rounded-md border border-input bg-background ring-offset-background focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
9053
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9054
+ "input",
9055
+ {
9056
+ id: "provider-openai-responses-base-url",
9057
+ type: "text",
9058
+ value: openaiResponsesBaseUrl,
9059
+ onChange: (e) => {
9060
+ setManualOpenaiResponsesUrlOverride(true);
9061
+ setOpenaiResponsesBaseUrl(e.target.value);
9062
+ },
9063
+ placeholder: "https://api.openai.com",
9064
+ className: "min-w-0 flex-1 bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
9065
+ }
9066
+ ),
9067
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l bg-muted px-3 py-2 font-mono text-xs text-muted-foreground", children: OPENAI_RESPONSES_ENDPOINT })
9068
+ ] }),
9069
+ errors.openaiResponsesBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiResponsesBaseUrl }),
9070
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9071
+ EndpointUrlPreview,
9072
+ {
9073
+ baseUrl: openaiResponsesBaseUrl,
9074
+ endpoint: OPENAI_RESPONSES_ENDPOINT
9075
+ }
9076
+ ),
9077
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Optional Responses-compatible base URL. Set only when the provider supports the Responses API." })
9078
+ ] })
8940
9079
  ] }),
8941
9080
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
8942
9081
  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-api-docs-url", className: "text-sm font-medium", children: "API Docs URL" }),
@@ -8983,6 +9122,7 @@ function createProviderPayload(data) {
8983
9122
  models: data.models,
8984
9123
  anthropicBaseUrl: (data.anthropicBaseUrl?.length ?? 0) > 0 ? data.anthropicBaseUrl : void 0,
8985
9124
  openaiBaseUrl: (data.openaiBaseUrl?.length ?? 0) > 0 ? data.openaiBaseUrl : void 0,
9125
+ openaiResponsesBaseUrl: (data.openaiResponsesBaseUrl?.length ?? 0) > 0 ? data.openaiResponsesBaseUrl : void 0,
8986
9126
  apiDocsUrl: (data.apiDocsUrl?.length ?? 0) > 0 ? data.apiDocsUrl : void 0,
8987
9127
  modelMetadataUrl: (data.modelMetadataUrl?.length ?? 0) > 0 ? data.modelMetadataUrl : void 0,
8988
9128
  modelMetadata: data.modelMetadata,
@@ -9272,7 +9412,7 @@ function ProvidersPanel({
9272
9412
  await refreshProviderMetadata(updated.id, data.modelMetadataUrl, false);
9273
9413
  }
9274
9414
  refreshProviders();
9275
- const criticalFieldsChanged = (data.apiKey ?? "") !== (editingProvider.apiKey ?? "") || JSON.stringify(data.models) !== JSON.stringify(editingProvider.models) || (data.anthropicBaseUrl ?? "") !== (editingProvider.anthropicBaseUrl ?? "") || (data.openaiBaseUrl ?? "") !== (editingProvider.openaiBaseUrl ?? "");
9415
+ const criticalFieldsChanged = (data.apiKey ?? "") !== (editingProvider.apiKey ?? "") || JSON.stringify(data.models) !== JSON.stringify(editingProvider.models) || (data.anthropicBaseUrl ?? "") !== (editingProvider.anthropicBaseUrl ?? "") || (data.openaiBaseUrl ?? "") !== (editingProvider.openaiBaseUrl ?? "") || (data.openaiResponsesBaseUrl ?? "") !== (editingProvider.openaiResponsesBaseUrl ?? "");
9276
9416
  if (criticalFieldsChanged) {
9277
9417
  await runTest(updated.id);
9278
9418
  }
@@ -11259,6 +11399,13 @@ const CONTEXT_USAGE_THRESHOLDS = {
11259
11399
  watch: WATCH_USAGE_PERCENT,
11260
11400
  danger: DANGER_USAGE_PERCENT
11261
11401
  };
11402
+ const snapshotCache = /* @__PURE__ */ new WeakMap();
11403
+ function providerSignature(providers) {
11404
+ return providers.map((provider) => {
11405
+ const metadata = provider.modelMetadata?.map((entry) => `${entry.model}:${String(entry.contextWindow ?? "")}`).join(",") ?? "";
11406
+ return `${provider.id}:${provider.updatedAt}:${provider.models.join(",")}:${metadata}`;
11407
+ }).join("|");
11408
+ }
11262
11409
  function parseRequestBody(rawBody) {
11263
11410
  if (rawBody === null) return null;
11264
11411
  try {
@@ -11308,6 +11455,25 @@ function snapshotFromLog(log, providers) {
11308
11455
  roleTotalTokens: roleSegments.reduce((sum, segment2) => sum + segment2.size, 0)
11309
11456
  };
11310
11457
  }
11458
+ function cachedSnapshotFromLog(log, providers, providersSignature) {
11459
+ const cached = snapshotCache.get(log);
11460
+ if (cached !== void 0 && cached.id === log.id && cached.rawRequestBody === log.rawRequestBody && cached.inputTokens === log.inputTokens && cached.model === log.model && cached.path === log.path && cached.apiFormat === log.apiFormat && cached.timestamp === log.timestamp && cached.providersSignature === providersSignature) {
11461
+ return cached.snapshot;
11462
+ }
11463
+ const snapshot = snapshotFromLog(log, providers);
11464
+ snapshotCache.set(log, {
11465
+ id: log.id,
11466
+ rawRequestBody: log.rawRequestBody,
11467
+ inputTokens: log.inputTokens,
11468
+ model: log.model,
11469
+ path: log.path,
11470
+ apiFormat: log.apiFormat,
11471
+ timestamp: log.timestamp,
11472
+ providersSignature,
11473
+ snapshot
11474
+ });
11475
+ return snapshot;
11476
+ }
11311
11477
  function isHigherPeak(candidate, current) {
11312
11478
  if (candidate.usagePercent !== null && current.usagePercent !== null) {
11313
11479
  return candidate.usagePercent > current.usagePercent;
@@ -11341,11 +11507,21 @@ function trendPercent(first, latest) {
11341
11507
  if (first.usagePercent === null || latest.usagePercent === null) return null;
11342
11508
  return (latest.usagePercent - first.usagePercent) * 100;
11343
11509
  }
11510
+ function logsInAscendingIdOrder(logs) {
11511
+ for (let index = 1; index < logs.length; index++) {
11512
+ const previous = logs[index - 1];
11513
+ const current = logs[index];
11514
+ if (previous === void 0 || current === void 0) continue;
11515
+ if (previous.id > current.id) return [...logs].sort((left, right) => left.id - right.id);
11516
+ }
11517
+ return logs;
11518
+ }
11344
11519
  function buildSessionContextSummary(logs, providers = []) {
11345
- const sortedLogs = [...logs].sort((left, right) => left.id - right.id);
11520
+ const sortedLogs = logsInAscendingIdOrder(logs);
11521
+ const providersSignature = providerSignature(providers);
11346
11522
  const byModel = /* @__PURE__ */ new Map();
11347
11523
  for (const log of sortedLogs) {
11348
- const snapshot = snapshotFromLog(log, providers);
11524
+ const snapshot = cachedSnapshotFromLog(log, providers, providersSignature);
11349
11525
  if (snapshot === null) continue;
11350
11526
  const existing = byModel.get(snapshot.model);
11351
11527
  if (existing === void 0) {
@@ -12730,7 +12906,7 @@ function filterLogs(logs, selectedSession, selectedModel) {
12730
12906
  return true;
12731
12907
  });
12732
12908
  }
12733
- const DEBOUNCE_MS = 50;
12909
+ const DEBOUNCE_MS = 100;
12734
12910
  const HASH_SCROLL_ATTEMPTS = 12;
12735
12911
  const HASH_HIGHLIGHT_MS = 1800;
12736
12912
  const MAX_CLIENT_LOGS = 500;
@@ -12787,6 +12963,7 @@ function ProxyViewerContainer({
12787
12963
  const [logPage, setLogPage] = reactExports.useState(null);
12788
12964
  const [sessionPageLoading, setSessionPageLoading] = reactExports.useState(initialSessionId !== void 0);
12789
12965
  const [sessionMemberships, setSessionMemberships] = reactExports.useState([]);
12966
+ const [, startLogTransition] = reactExports.useTransition();
12790
12967
  const eventSourceRef = reactExports.useRef(null);
12791
12968
  const reconnectTimeoutRef = reactExports.useRef(null);
12792
12969
  const handledHashRef = reactExports.useRef(null);
@@ -12805,22 +12982,28 @@ function ProxyViewerContainer({
12805
12982
  const updates = pendingUpdatesRef.current;
12806
12983
  pendingUpdatesRef.current = [];
12807
12984
  if (updates.length === 0) return;
12808
- setAllLogs((prev) => {
12809
- let next = prev;
12810
- for (const log of updates) {
12811
- const idx = logIndexRef.current.get(log.id);
12812
- if (idx !== void 0) {
12813
- next = [...next.slice(0, idx), log, ...next.slice(idx + 1)];
12814
- } else {
12815
- logIndexRef.current.set(log.id, next.length);
12816
- next = [...next, log];
12985
+ startLogTransition(() => {
12986
+ setAllLogs((prev) => {
12987
+ const next = [...prev];
12988
+ for (const log of updates) {
12989
+ const indexedPosition = logIndexRef.current.get(log.id);
12990
+ if (indexedPosition !== void 0 && next[indexedPosition]?.id === log.id) {
12991
+ next[indexedPosition] = log;
12992
+ continue;
12993
+ }
12994
+ const existingPosition = next.findIndex((entry) => entry.id === log.id);
12995
+ if (existingPosition >= 0) {
12996
+ next[existingPosition] = log;
12997
+ continue;
12998
+ }
12999
+ next.push(log);
12817
13000
  }
12818
- }
12819
- next = trimClientLogs(next);
12820
- logIndexRef.current = buildLogIndex(next);
12821
- return next;
13001
+ const trimmed = trimClientLogs(next);
13002
+ logIndexRef.current = buildLogIndex(trimmed);
13003
+ return trimmed;
13004
+ });
12822
13005
  });
12823
- }, []);
13006
+ }, [startLogTransition]);
12824
13007
  const scheduleUpdate = reactExports.useCallback(
12825
13008
  (log) => {
12826
13009
  pendingUpdatesRef.current.push(log);
@@ -13142,9 +13325,10 @@ export {
13142
13325
  Badge as B,
13143
13326
  CONTEXT_USAGE_THRESHOLDS as C,
13144
13327
  Dialog as D,
13145
- JsonViewer as E,
13328
+ LazyJsonViewer as E,
13146
13329
  safeJsonValue as F,
13147
- JsonViewerFromString as J,
13330
+ parseJsonText as G,
13331
+ LazyJsonViewerFromString as L,
13148
13332
  ProxyViewerContainer as P,
13149
13333
  ROLE_COLOR_CLASSES as R,
13150
13334
  SegmentBar as S,