@tonyclaw/agent-inspector 2.0.2 → 2.0.4
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.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-Bp7_x-5N.js → CompareDrawer-BCH_fsLm.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-D85_UANk.js +101 -0
- package/.output/public/assets/{ReplayDialog-DFHCd0yx.js → ReplayDialog-DTeaHHit.js} +1 -1
- package/.output/public/assets/RequestAnatomy-DZ8grAih.js +1 -0
- package/.output/public/assets/ResponseView-Cldm6RCi.js +1 -0
- package/.output/public/assets/{StreamingChunkSequence-Bjs4Lqwn.js → StreamingChunkSequence-3x4p-yT7.js} +1 -1
- package/.output/public/assets/_sessionId-YqWFBu6d.js +1 -0
- package/.output/public/assets/index-BIw2H6jO.js +1 -0
- package/.output/public/assets/index-CobXD0yH.css +1 -0
- package/.output/public/assets/{json-viewer-6uV_YXws.js → json-viewer-BrzjD7qI.js} +1 -1
- package/.output/public/assets/{main-FSGUGtEL.js → main-mgxeUdZQ.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +8 -8
- package/.output/server/{_sessionId-_bf9vUww.mjs → _sessionId-C4xsxIWm.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-DIth2DQM.mjs → CompareDrawer-DuWEpqQ7.mjs} +4 -4
- package/.output/server/_ssr/{ProxyViewerContainer-249bTH-T.mjs → ProxyViewerContainer-Cckz5qKu.mjs} +519 -89
- package/.output/server/_ssr/{ReplayDialog-C1aGx0y1.mjs → ReplayDialog-BDRcr8E5.mjs} +4 -4
- package/.output/server/_ssr/{RequestAnatomy-D2bCiEJn.mjs → RequestAnatomy-BoO2_Ij0.mjs} +5 -5
- package/.output/server/_ssr/{ResponseView-DP6k4Xs_.mjs → ResponseView-DZiPBxvO.mjs} +21 -17
- package/.output/server/_ssr/{StreamingChunkSequence-HyXZV-b5.mjs → StreamingChunkSequence-D-be7KEL.mjs} +3 -3
- package/.output/server/_ssr/{index-Bt47f9pn.mjs → index-5RImHKfu.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-Co-YRwUP.mjs → json-viewer-aJhb93ZK.mjs} +2 -2
- package/.output/server/_ssr/{router-to_OJirX.mjs → router-Dgkv5nKP.mjs} +38 -99
- package/.output/server/{_tanstack-start-manifest_v-Bd-2YRWo.mjs → _tanstack-start-manifest_v-B8rrWXjr.mjs} +1 -1
- package/.output/server/index.mjs +63 -63
- package/README.md +5 -2
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +25 -15
- package/src/components/ProxyViewerContainer.tsx +2 -1
- package/src/components/providers/SettingsDialog.tsx +45 -1
- package/src/components/proxy-viewer/AgentTraceSummary.tsx +276 -0
- package/src/components/proxy-viewer/AnswerMarkdown.tsx +16 -0
- package/src/components/proxy-viewer/ConversationGroup.tsx +18 -0
- package/src/components/proxy-viewer/ConversationHeader.tsx +6 -6
- package/src/components/proxy-viewer/LogEntry.tsx +5 -5
- package/src/components/proxy-viewer/LogEntryHeader.tsx +9 -14
- package/src/components/proxy-viewer/ResponseView.tsx +2 -6
- package/src/components/proxy-viewer/ToolTraceEvents.tsx +32 -0
- package/src/components/proxy-viewer/TurnGroup.tsx +15 -1
- package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +2 -2
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +6 -12
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +10 -14
- package/src/components/proxy-viewer/viewerState.ts +177 -0
- package/src/lib/runtimeConfig.ts +6 -0
- package/src/lib/timeDisplay.ts +22 -0
- package/src/lib/useOnboarding.ts +2 -0
- package/src/lib/useStripConfig.ts +16 -0
- package/src/proxy/chunkStorage.ts +3 -4
- package/src/proxy/config.ts +3 -0
- package/src/proxy/logger.ts +8 -15
- package/src/proxy/store.ts +8 -16
- package/src/routes/api/config.ts +5 -1
- package/src/routes/api/providers.$providerId.test.log.ts +0 -79
- package/.output/public/assets/ProxyViewerContainer-USuxPy-K.js +0 -101
- package/.output/public/assets/RequestAnatomy-ehyrskxt.js +0 -1
- package/.output/public/assets/ResponseView-BNGyc8e_.js +0 -1
- package/.output/public/assets/_sessionId-D_SeK_qp.js +0 -1
- package/.output/public/assets/index-BGGOWR7A.js +0 -1
- package/.output/public/assets/index-CIL46Z2y.css +0 -1
package/.output/server/_ssr/{ProxyViewerContainer-249bTH-T.mjs → ProxyViewerContainer-Cckz5qKu.mjs}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
|
|
2
|
-
import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as RuntimeConfigSchema, r as requestFormatForPath,
|
|
2
|
+
import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as DEFAULT_TIME_DISPLAY_FORMAT, b as RuntimeConfigSchema, r as requestFormatForPath, e as createPendingProviderTestResults, P as ProviderTestResultsSchema, f as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, T as TimeDisplayFormatSchema, g as getSessionPath, h as ProviderConfigSchema, K as KnowledgeCandidateSchema, s as stripClaudeCodeBillingHeader, d as safeGetOwnProperty, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, c as AnthropicRequestSchema } from "./router-Dgkv5nKP.mjs";
|
|
3
3
|
import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
|
|
4
4
|
import { J as JSZip } from "../_libs/jszip.mjs";
|
|
5
5
|
import { c as clsx } from "../_libs/clsx.mjs";
|
|
@@ -9,7 +9,7 @@ import { R as Root, T as Trigger$2, C as Content, a as Close, b as Title, P as P
|
|
|
9
9
|
import { d as diffJson, a as diffLines } from "../_libs/diff.mjs";
|
|
10
10
|
import { u as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
|
|
11
11
|
import { R as Root2, T as Trigger, I as Icon, V as Value, P as Portal, C as Content2, a as Viewport, b as Item, c as ItemIndicator, d as ItemText, S as ScrollUpButton, e as ScrollDownButton } from "../_libs/radix-ui__react-select.mjs";
|
|
12
|
-
import { C as Check, X, P as Plus, D as Download, S as Settings, A as ArrowLeft, a as Copy, b as ChevronDown, U as Upload, c as Scan, d as CircleAlert, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, T as Trash2, i as TriangleAlert, j as EyeOff, k as Eye, R as RotateCw, l as Pencil, m as Minus, n as CircleCheckBig, O as OctagonAlert,
|
|
12
|
+
import { C as Check, X, P as Plus, D as Download, S as Settings, A as ArrowLeft, a as Copy, b as ChevronDown, U as Upload, c as Scan, d as CircleAlert, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, T as Trash2, W as Wrench, i as TriangleAlert, B as Brain, j as EyeOff, k as Eye, R as RotateCw, l as Pencil, m as Minus, n as CircleCheckBig, O as OctagonAlert, G as Globe, F as FileTerminal, o as Radio, p as ChevronsUp, q as ChevronsDown, r as FileDiff, H as History, s as RotateCcw, t as GitCompareArrows, u as CircleQuestionMark, v as Server, w as Gauge, x as Lock, y as Wifi, z as WifiOff, I as ArrowUp, J as ArrowDown, K as Rows3, N as Columns2 } from "../_libs/lucide-react.mjs";
|
|
13
13
|
import { u as union, d as object, a as array, l as literal, b as string, n as number, c as boolean, _ as _enum } from "../_libs/zod.mjs";
|
|
14
14
|
import { P as Provider, R as Root3, T as Trigger$1, a as Portal$1, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
|
|
15
15
|
import { R as Root2$1, L as List, T as Trigger$3, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
|
|
@@ -17,7 +17,7 @@ import { S as Slot } from "../_libs/radix-ui__react-slot.mjs";
|
|
|
17
17
|
const ApiErrorSchema = object({
|
|
18
18
|
error: string()
|
|
19
19
|
});
|
|
20
|
-
async function parseJsonResponse(response, schema) {
|
|
20
|
+
async function parseJsonResponse$1(response, schema) {
|
|
21
21
|
const data = await response.json();
|
|
22
22
|
return schema.parse(data);
|
|
23
23
|
}
|
|
@@ -36,7 +36,7 @@ async function fetchJson(input, schema, init, errorFallback) {
|
|
|
36
36
|
const fallback = errorFallback?.(response) ?? `Request failed with status ${response.status}`;
|
|
37
37
|
throw new Error(await readApiError(response, fallback));
|
|
38
38
|
}
|
|
39
|
-
return parseJsonResponse(response, schema);
|
|
39
|
+
return parseJsonResponse$1(response, schema);
|
|
40
40
|
}
|
|
41
41
|
const STRIP_CONFIG_SWR_KEY = "/api/config";
|
|
42
42
|
async function fetcher$2(url) {
|
|
@@ -71,10 +71,12 @@ function useStripConfig() {
|
|
|
71
71
|
const { mutate: globalMutate } = useSWRConfig();
|
|
72
72
|
const strip = response.data?.stripClaudeCodeBillingHeader ?? false;
|
|
73
73
|
const slowResponseThresholdSeconds = response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS;
|
|
74
|
+
const timeDisplayFormat = response.data?.timeDisplayFormat ?? DEFAULT_TIME_DISPLAY_FORMAT;
|
|
74
75
|
const optimisticConfig = (patch) => ({
|
|
75
76
|
stripClaudeCodeBillingHeader: response.data?.stripClaudeCodeBillingHeader ?? false,
|
|
76
77
|
hasSeenOnboarding: response.data?.hasSeenOnboarding ?? false,
|
|
77
78
|
slowResponseThresholdSeconds: response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
79
|
+
timeDisplayFormat: response.data?.timeDisplayFormat ?? DEFAULT_TIME_DISPLAY_FORMAT,
|
|
78
80
|
...patch
|
|
79
81
|
});
|
|
80
82
|
const setStrip = async (next) => {
|
|
@@ -99,13 +101,22 @@ function useStripConfig() {
|
|
|
99
101
|
}
|
|
100
102
|
);
|
|
101
103
|
};
|
|
104
|
+
const setTimeDisplayFormat = async (next) => {
|
|
105
|
+
await globalMutate(STRIP_CONFIG_SWR_KEY, setRuntimeConfig({ timeDisplayFormat: next }), {
|
|
106
|
+
optimisticData: optimisticConfig({ timeDisplayFormat: next }),
|
|
107
|
+
rollbackOnError: true,
|
|
108
|
+
revalidate: false
|
|
109
|
+
});
|
|
110
|
+
};
|
|
102
111
|
return {
|
|
103
112
|
strip,
|
|
104
113
|
slowResponseThresholdSeconds,
|
|
114
|
+
timeDisplayFormat,
|
|
105
115
|
isLoading: response.isLoading,
|
|
106
116
|
isError: response.error !== void 0,
|
|
107
117
|
setStrip,
|
|
108
|
-
setSlowResponseThresholdSeconds
|
|
118
|
+
setSlowResponseThresholdSeconds,
|
|
119
|
+
setTimeDisplayFormat
|
|
109
120
|
};
|
|
110
121
|
}
|
|
111
122
|
const ONBOARDING_SWR_KEY = "/api/config";
|
|
@@ -145,7 +156,8 @@ function useOnboarding() {
|
|
|
145
156
|
optimisticData: {
|
|
146
157
|
stripClaudeCodeBillingHeader: response.data?.stripClaudeCodeBillingHeader ?? false,
|
|
147
158
|
hasSeenOnboarding: true,
|
|
148
|
-
slowResponseThresholdSeconds: response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS
|
|
159
|
+
slowResponseThresholdSeconds: response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
160
|
+
timeDisplayFormat: response.data?.timeDisplayFormat ?? DEFAULT_TIME_DISPLAY_FORMAT
|
|
149
161
|
},
|
|
150
162
|
rollbackOnError: true,
|
|
151
163
|
revalidate: false
|
|
@@ -261,6 +273,21 @@ async function exportLogsAsZip(logs) {
|
|
|
261
273
|
document.body.removeChild(anchor);
|
|
262
274
|
URL.revokeObjectURL(url);
|
|
263
275
|
}
|
|
276
|
+
function formatTimestamp(iso, format) {
|
|
277
|
+
switch (format) {
|
|
278
|
+
case "full":
|
|
279
|
+
return iso;
|
|
280
|
+
case "time":
|
|
281
|
+
return new Date(iso).toLocaleTimeString([], {
|
|
282
|
+
hour: "2-digit",
|
|
283
|
+
minute: "2-digit",
|
|
284
|
+
second: "2-digit"
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function formatTimestampRange(startedAt, endedAt, format) {
|
|
289
|
+
return `${formatTimestamp(startedAt, format)} - ${formatTimestamp(endedAt, format)}`;
|
|
290
|
+
}
|
|
264
291
|
function cn(...inputs) {
|
|
265
292
|
return twMerge(clsx(inputs));
|
|
266
293
|
}
|
|
@@ -275,7 +302,7 @@ function getStatusCategory(status) {
|
|
|
275
302
|
if (status >= 500) return "server_error";
|
|
276
303
|
return "pending";
|
|
277
304
|
}
|
|
278
|
-
const version = "2.0.
|
|
305
|
+
const version = "2.0.4";
|
|
279
306
|
const packageJson = {
|
|
280
307
|
version
|
|
281
308
|
};
|
|
@@ -469,10 +496,6 @@ const API_FORMAT_LABELS = {
|
|
|
469
496
|
openai: "OpenAI",
|
|
470
497
|
unknown: "Unknown"
|
|
471
498
|
};
|
|
472
|
-
function formatTimestamp(iso) {
|
|
473
|
-
const date = new Date(iso);
|
|
474
|
-
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
475
|
-
}
|
|
476
499
|
function ConversationHeader({
|
|
477
500
|
conversationId,
|
|
478
501
|
startTime,
|
|
@@ -486,6 +509,7 @@ function ConversationHeader({
|
|
|
486
509
|
hideApiFormat = false,
|
|
487
510
|
isLoading = false,
|
|
488
511
|
userAgent,
|
|
512
|
+
timeDisplayFormat,
|
|
489
513
|
onClear
|
|
490
514
|
}) {
|
|
491
515
|
const [confirmOpen, setConfirmOpen] = reactExports.useState(false);
|
|
@@ -559,11 +583,7 @@ function ConversationHeader({
|
|
|
559
583
|
),
|
|
560
584
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
|
|
561
585
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
562
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
563
|
-
formatTimestamp(startTime),
|
|
564
|
-
" - ",
|
|
565
|
-
formatTimestamp(endTime)
|
|
566
|
-
] })
|
|
586
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatTimestampRange(startTime, endTime, timeDisplayFormat) })
|
|
567
587
|
] }),
|
|
568
588
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
|
|
569
589
|
/* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3" }),
|
|
@@ -1343,27 +1363,27 @@ function useCopyFeedback(text) {
|
|
|
1343
1363
|
return { copied, copy };
|
|
1344
1364
|
}
|
|
1345
1365
|
const LazyCompareDrawer = reactExports.lazy(
|
|
1346
|
-
() => import("./CompareDrawer-
|
|
1366
|
+
() => import("./CompareDrawer-DuWEpqQ7.mjs").then((m) => ({ default: m.CompareDrawer }))
|
|
1347
1367
|
);
|
|
1348
1368
|
const LazyReplayDialog = reactExports.lazy(
|
|
1349
|
-
() => import("./ReplayDialog-
|
|
1369
|
+
() => import("./ReplayDialog-BDRcr8E5.mjs").then((m) => ({ default: m.ReplayDialog }))
|
|
1350
1370
|
);
|
|
1351
1371
|
const LazyRequestAnatomy = reactExports.lazy(
|
|
1352
|
-
() => import("./RequestAnatomy-
|
|
1372
|
+
() => import("./RequestAnatomy-BoO2_Ij0.mjs").then((m) => ({ default: m.RequestAnatomy }))
|
|
1353
1373
|
);
|
|
1354
1374
|
const LazyResponseView = reactExports.lazy(
|
|
1355
|
-
() => import("./ResponseView-
|
|
1375
|
+
() => import("./ResponseView-DZiPBxvO.mjs").then((m) => ({ default: m.ResponseView }))
|
|
1356
1376
|
);
|
|
1357
1377
|
const LazyStreamingChunkSequence = reactExports.lazy(
|
|
1358
|
-
() => import("./StreamingChunkSequence-
|
|
1378
|
+
() => import("./StreamingChunkSequence-D-be7KEL.mjs").then((m) => ({
|
|
1359
1379
|
default: m.StreamingChunkSequence
|
|
1360
1380
|
}))
|
|
1361
1381
|
);
|
|
1362
1382
|
const LazyJsonViewer = reactExports.lazy(
|
|
1363
|
-
() => import("./json-viewer-
|
|
1383
|
+
() => import("./json-viewer-aJhb93ZK.mjs").then((m) => ({ default: m.JsonViewer }))
|
|
1364
1384
|
);
|
|
1365
1385
|
const LazyJsonViewerFromString = reactExports.lazy(
|
|
1366
|
-
() => import("./json-viewer-
|
|
1386
|
+
() => import("./json-viewer-aJhb93ZK.mjs").then((m) => ({ default: m.JsonViewerFromString }))
|
|
1367
1387
|
);
|
|
1368
1388
|
const HIGHLIGHT_DURATION_MS = 1200;
|
|
1369
1389
|
const MAX_HIGHLIGHT_ATTEMPTS = 12;
|
|
@@ -1757,7 +1777,7 @@ const STATUS_BADGE_CLASSES = {
|
|
|
1757
1777
|
server_error: "bg-rose-500/15 text-rose-400 border-rose-500/25",
|
|
1758
1778
|
pending: "bg-muted text-muted-foreground border-border"
|
|
1759
1779
|
};
|
|
1760
|
-
function formatElapsed$
|
|
1780
|
+
function formatElapsed$2(ms) {
|
|
1761
1781
|
if (ms < 1e3) return `${ms}ms`;
|
|
1762
1782
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
1763
1783
|
}
|
|
@@ -1780,17 +1800,16 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
1780
1800
|
toolCount = null,
|
|
1781
1801
|
expanded,
|
|
1782
1802
|
onToggle,
|
|
1783
|
-
responseToolNames = null,
|
|
1784
1803
|
cacheTrend = null,
|
|
1785
1804
|
activeTab,
|
|
1786
1805
|
tabActions,
|
|
1787
1806
|
onReplay,
|
|
1788
|
-
slowResponseThresholdSeconds = 0
|
|
1807
|
+
slowResponseThresholdSeconds = 0,
|
|
1808
|
+
timeDisplayFormat
|
|
1789
1809
|
}) {
|
|
1790
1810
|
const statusCategory = getStatusCategory(log.responseStatus);
|
|
1791
1811
|
const isSlowResponse = log.elapsedMs !== null && slowResponseThresholdSeconds > 0 && log.elapsedMs > slowResponseThresholdSeconds * 1e3;
|
|
1792
1812
|
const hasTokens = log.inputTokens !== null || log.outputTokens !== null;
|
|
1793
|
-
const toolNamesJoined = reactExports.useMemo(() => responseToolNames?.join(", ") ?? null, [responseToolNames]);
|
|
1794
1813
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1795
1814
|
"div",
|
|
1796
1815
|
{
|
|
@@ -1819,7 +1838,7 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
1819
1838
|
] }),
|
|
1820
1839
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
|
|
1821
1840
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
1822
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: log.timestamp })
|
|
1841
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", title: log.timestamp, children: formatTimestamp(log.timestamp, timeDisplayFormat) })
|
|
1823
1842
|
] }),
|
|
1824
1843
|
log.model !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
1825
1844
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderLogo, { provider: detectProvider(log.model), className: "size-4" }) }) }),
|
|
@@ -1849,12 +1868,12 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
1849
1868
|
),
|
|
1850
1869
|
children: [
|
|
1851
1870
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
1852
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed$
|
|
1871
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed$2(log.elapsedMs) }),
|
|
1853
1872
|
isSlowResponse && /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Slow response" })
|
|
1854
1873
|
]
|
|
1855
1874
|
}
|
|
1856
1875
|
) }),
|
|
1857
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: isSlowResponse ? `Slow response: ${formatElapsed$
|
|
1876
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: isSlowResponse ? `Slow response: ${formatElapsed$2(log.elapsedMs)} exceeds ${formatElapsed$2(
|
|
1858
1877
|
slowResponseThresholdSeconds * 1e3
|
|
1859
1878
|
)}` : "Elapsed response time" })
|
|
1860
1879
|
] }),
|
|
@@ -1912,10 +1931,6 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
1912
1931
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3" }),
|
|
1913
1932
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: toolCount })
|
|
1914
1933
|
] }),
|
|
1915
|
-
responseToolNames !== null && responseToolNames.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-amber-400/80 text-xs shrink-0", children: [
|
|
1916
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3" }),
|
|
1917
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums truncate max-w-[160px]", children: toolNamesJoined })
|
|
1918
|
-
] }),
|
|
1919
1934
|
log.origin !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1920
1935
|
"span",
|
|
1921
1936
|
{
|
|
@@ -2403,6 +2418,7 @@ const LogEntry = reactExports.memo(function({
|
|
|
2403
2418
|
viewMode = "simple",
|
|
2404
2419
|
strip,
|
|
2405
2420
|
slowResponseThresholdSeconds,
|
|
2421
|
+
timeDisplayFormat,
|
|
2406
2422
|
cacheTrend = null,
|
|
2407
2423
|
onCompareWithPrevious
|
|
2408
2424
|
}) {
|
|
@@ -2419,10 +2435,6 @@ const LogEntry = reactExports.memo(function({
|
|
|
2419
2435
|
() => adapter.analyzeRequest(log.rawRequestBody),
|
|
2420
2436
|
[adapter, log.rawRequestBody]
|
|
2421
2437
|
);
|
|
2422
|
-
const responseAnalysis = reactExports.useMemo(
|
|
2423
|
-
() => adapter.analyzeResponse(log.responseText),
|
|
2424
|
-
[adapter, log.responseText]
|
|
2425
|
-
);
|
|
2426
2438
|
const strippedRequestBody = reactExports.useMemo(() => {
|
|
2427
2439
|
if (!strip || resolvedFormat !== "anthropic" || log.rawRequestBody === null) {
|
|
2428
2440
|
return null;
|
|
@@ -2555,9 +2567,9 @@ const LogEntry = reactExports.memo(function({
|
|
|
2555
2567
|
toolCount: requestAnalysis.toolCount,
|
|
2556
2568
|
expanded,
|
|
2557
2569
|
onToggle: () => setExpanded(!expanded),
|
|
2558
|
-
responseToolNames: responseAnalysis.toolNames,
|
|
2559
2570
|
cacheTrend,
|
|
2560
2571
|
slowResponseThresholdSeconds,
|
|
2572
|
+
timeDisplayFormat,
|
|
2561
2573
|
activeTab,
|
|
2562
2574
|
tabActions,
|
|
2563
2575
|
onReplay: onCompareWithPrevious === void 0 ? void 0 : () => {
|
|
@@ -2764,6 +2776,25 @@ function ThreadConnector({
|
|
|
2764
2776
|
) })
|
|
2765
2777
|
] });
|
|
2766
2778
|
}
|
|
2779
|
+
function ToolTraceEvents({ events }) {
|
|
2780
|
+
if (events.length === 0) return null;
|
|
2781
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-3 mb-2 grid gap-1.5", children: events.map((event) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2782
|
+
"div",
|
|
2783
|
+
{
|
|
2784
|
+
className: "flex min-w-0 items-center gap-2 rounded-md border border-border/70 bg-muted/20 px-2.5 py-1.5 text-xs",
|
|
2785
|
+
children: [
|
|
2786
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 shrink-0 text-sky-400/70" }),
|
|
2787
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-foreground/80", children: event.name }),
|
|
2788
|
+
event.argumentsPreview !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
2789
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 shrink-0 text-muted-foreground/60" }),
|
|
2790
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate font-mono text-muted-foreground", children: event.argumentsPreview })
|
|
2791
|
+
] })
|
|
2792
|
+
]
|
|
2793
|
+
},
|
|
2794
|
+
event.id
|
|
2795
|
+
)) });
|
|
2796
|
+
}
|
|
2797
|
+
const PREVIEW_LIMIT = 180;
|
|
2767
2798
|
function shouldRenderConversationContent(standalone, expanded) {
|
|
2768
2799
|
return standalone || expanded;
|
|
2769
2800
|
}
|
|
@@ -2801,7 +2832,135 @@ function buildValidPredecessors(groups) {
|
|
|
2801
2832
|
}
|
|
2802
2833
|
return predecessors;
|
|
2803
2834
|
}
|
|
2804
|
-
function
|
|
2835
|
+
function parseJsonResponse(responseText) {
|
|
2836
|
+
if (responseText === null) return null;
|
|
2837
|
+
try {
|
|
2838
|
+
const parsed = JSON.parse(responseText);
|
|
2839
|
+
if (typeof parsed === "string") {
|
|
2840
|
+
return JSON.parse(parsed);
|
|
2841
|
+
}
|
|
2842
|
+
return parsed;
|
|
2843
|
+
} catch {
|
|
2844
|
+
return null;
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
function previewValue(value) {
|
|
2848
|
+
if (value === void 0 || value === null) return null;
|
|
2849
|
+
const raw = typeof value === "string" ? value : JSON.stringify(value);
|
|
2850
|
+
if (raw === void 0) return null;
|
|
2851
|
+
const normalized = raw.replace(/\s+/g, " ").trim();
|
|
2852
|
+
if (normalized.length === 0) return null;
|
|
2853
|
+
return normalized.length > PREVIEW_LIMIT ? `${normalized.slice(0, PREVIEW_LIMIT - 1)}...` : normalized;
|
|
2854
|
+
}
|
|
2855
|
+
function extractAnthropicToolTraceEvents(log) {
|
|
2856
|
+
const parsed = parseJsonResponse(log.responseText);
|
|
2857
|
+
const content = safeGetOwnProperty(parsed, "content");
|
|
2858
|
+
if (!Array.isArray(content)) return [];
|
|
2859
|
+
const events = [];
|
|
2860
|
+
for (const block of content) {
|
|
2861
|
+
const type = safeGetOwnProperty(block, "type");
|
|
2862
|
+
if (type !== "tool_use") continue;
|
|
2863
|
+
const name = safeGetOwnProperty(block, "name");
|
|
2864
|
+
if (typeof name !== "string" || name.length === 0) continue;
|
|
2865
|
+
events.push({
|
|
2866
|
+
id: `${String(log.id)}-anthropic-tool-${String(events.length)}`,
|
|
2867
|
+
logId: log.id,
|
|
2868
|
+
index: events.length,
|
|
2869
|
+
provider: "anthropic",
|
|
2870
|
+
name,
|
|
2871
|
+
argumentsPreview: previewValue(safeGetOwnProperty(block, "input"))
|
|
2872
|
+
});
|
|
2873
|
+
}
|
|
2874
|
+
return events;
|
|
2875
|
+
}
|
|
2876
|
+
function extractOpenAIToolTraceEvents(log) {
|
|
2877
|
+
const parsed = parseJsonResponse(log.responseText);
|
|
2878
|
+
const choices = safeGetOwnProperty(parsed, "choices");
|
|
2879
|
+
if (!Array.isArray(choices)) return [];
|
|
2880
|
+
const events = [];
|
|
2881
|
+
for (const choice of choices) {
|
|
2882
|
+
const message = safeGetOwnProperty(choice, "message");
|
|
2883
|
+
const toolCalls = safeGetOwnProperty(message, "tool_calls");
|
|
2884
|
+
if (!Array.isArray(toolCalls)) continue;
|
|
2885
|
+
for (const call of toolCalls) {
|
|
2886
|
+
const fn = safeGetOwnProperty(call, "function");
|
|
2887
|
+
const name = safeGetOwnProperty(fn, "name");
|
|
2888
|
+
if (typeof name !== "string" || name.length === 0) continue;
|
|
2889
|
+
events.push({
|
|
2890
|
+
id: `${String(log.id)}-openai-tool-${String(events.length)}`,
|
|
2891
|
+
logId: log.id,
|
|
2892
|
+
index: events.length,
|
|
2893
|
+
provider: "openai",
|
|
2894
|
+
name,
|
|
2895
|
+
argumentsPreview: previewValue(safeGetOwnProperty(fn, "arguments"))
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
return events;
|
|
2900
|
+
}
|
|
2901
|
+
function extractToolTraceEvents(log) {
|
|
2902
|
+
const format = resolveLogFormat(log);
|
|
2903
|
+
switch (format) {
|
|
2904
|
+
case "anthropic":
|
|
2905
|
+
return extractAnthropicToolTraceEvents(log);
|
|
2906
|
+
case "openai":
|
|
2907
|
+
return extractOpenAIToolTraceEvents(log);
|
|
2908
|
+
case "unknown":
|
|
2909
|
+
return [];
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
function buildTraceSummary(logs, slowResponseThresholdSeconds, knowledgeCandidateCount = 0) {
|
|
2913
|
+
let failedCallCount = 0;
|
|
2914
|
+
let pendingCallCount = 0;
|
|
2915
|
+
let slowCallCount = 0;
|
|
2916
|
+
let totalInputTokens = 0;
|
|
2917
|
+
let totalOutputTokens = 0;
|
|
2918
|
+
let totalCacheCreationInputTokens = 0;
|
|
2919
|
+
let totalCacheReadInputTokens = 0;
|
|
2920
|
+
let totalElapsedMs = 0;
|
|
2921
|
+
let maxElapsedMs = null;
|
|
2922
|
+
let toolCallCount = 0;
|
|
2923
|
+
for (const log of logs) {
|
|
2924
|
+
if (log.responseStatus === null) {
|
|
2925
|
+
pendingCallCount += 1;
|
|
2926
|
+
} else if (log.responseStatus >= 400) {
|
|
2927
|
+
failedCallCount += 1;
|
|
2928
|
+
}
|
|
2929
|
+
if (log.elapsedMs !== null && slowResponseThresholdSeconds > 0 && log.elapsedMs > slowResponseThresholdSeconds * 1e3) {
|
|
2930
|
+
slowCallCount += 1;
|
|
2931
|
+
}
|
|
2932
|
+
if (log.inputTokens !== null) totalInputTokens += log.inputTokens;
|
|
2933
|
+
if (log.outputTokens !== null) totalOutputTokens += log.outputTokens;
|
|
2934
|
+
if (log.cacheCreationInputTokens !== null) {
|
|
2935
|
+
totalCacheCreationInputTokens += log.cacheCreationInputTokens;
|
|
2936
|
+
}
|
|
2937
|
+
if (log.cacheReadInputTokens !== null) {
|
|
2938
|
+
totalCacheReadInputTokens += log.cacheReadInputTokens;
|
|
2939
|
+
}
|
|
2940
|
+
if (log.elapsedMs !== null) {
|
|
2941
|
+
totalElapsedMs += log.elapsedMs;
|
|
2942
|
+
maxElapsedMs = maxElapsedMs === null ? log.elapsedMs : Math.max(maxElapsedMs, log.elapsedMs);
|
|
2943
|
+
}
|
|
2944
|
+
toolCallCount += extractToolTraceEvents(log).length;
|
|
2945
|
+
}
|
|
2946
|
+
return {
|
|
2947
|
+
llmCallCount: logs.length,
|
|
2948
|
+
toolCallCount,
|
|
2949
|
+
failedCallCount,
|
|
2950
|
+
pendingCallCount,
|
|
2951
|
+
slowCallCount,
|
|
2952
|
+
totalInputTokens,
|
|
2953
|
+
totalOutputTokens,
|
|
2954
|
+
totalCacheCreationInputTokens,
|
|
2955
|
+
totalCacheReadInputTokens,
|
|
2956
|
+
totalElapsedMs,
|
|
2957
|
+
maxElapsedMs,
|
|
2958
|
+
startedAt: logs[0]?.timestamp ?? null,
|
|
2959
|
+
endedAt: logs[logs.length - 1]?.timestamp ?? null,
|
|
2960
|
+
knowledgeCandidateCount
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
function formatElapsed$1(ms) {
|
|
2805
2964
|
if (ms < 1e3) return `${ms}ms`;
|
|
2806
2965
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
2807
2966
|
}
|
|
@@ -2813,7 +2972,8 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
2813
2972
|
cacheTrends,
|
|
2814
2973
|
onCompareWithPrevious,
|
|
2815
2974
|
comparisonPredecessors,
|
|
2816
|
-
turnIndex = 0
|
|
2975
|
+
turnIndex = 0,
|
|
2976
|
+
timeDisplayFormat
|
|
2817
2977
|
}) {
|
|
2818
2978
|
const lastIdx = entries.length - 1;
|
|
2819
2979
|
const lastStop = entries[lastIdx]?.stopReason ?? null;
|
|
@@ -2871,6 +3031,14 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
2871
3031
|
const EndCrab = reactExports.useMemo(() => getCrabVariant(entries[lastIdx]?.log.id ?? 0), [entries, lastIdx]);
|
|
2872
3032
|
const bgClass = turnIndex % 2 === 0 ? "bg-muted/10" : "bg-muted/25";
|
|
2873
3033
|
const aggregateIsSlow = aggregate.maxElapsed !== null && slowResponseThresholdSeconds > 0 && aggregate.maxElapsed > slowResponseThresholdSeconds * 1e3;
|
|
3034
|
+
const toolEventsByLogId = reactExports.useMemo(() => {
|
|
3035
|
+
const events = /* @__PURE__ */ new Map();
|
|
3036
|
+
for (const entry of entries) {
|
|
3037
|
+
const extracted = extractToolTraceEvents(entry.log);
|
|
3038
|
+
if (extracted.length > 0) events.set(entry.log.id, extracted);
|
|
3039
|
+
}
|
|
3040
|
+
return events;
|
|
3041
|
+
}, [entries]);
|
|
2874
3042
|
const [layoutVersion, setLayoutVersion] = reactExports.useState(0);
|
|
2875
3043
|
const containerRef = reactExports.useRef(null);
|
|
2876
3044
|
reactExports.useEffect(() => {
|
|
@@ -3001,14 +3169,14 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3001
3169
|
),
|
|
3002
3170
|
children: [
|
|
3003
3171
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
3004
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed(aggregate.maxElapsed) }),
|
|
3172
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed$1(aggregate.maxElapsed) }),
|
|
3005
3173
|
aggregateIsSlow && /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3", "aria-label": "Slow response" })
|
|
3006
3174
|
]
|
|
3007
3175
|
}
|
|
3008
3176
|
) }),
|
|
3009
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: aggregateIsSlow ? `Slow response: ${formatElapsed(
|
|
3177
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: aggregateIsSlow ? `Slow response: ${formatElapsed$1(
|
|
3010
3178
|
aggregate.maxElapsed
|
|
3011
|
-
)} exceeds ${formatElapsed(slowResponseThresholdSeconds * 1e3)}` : "Slowest request in this turn" })
|
|
3179
|
+
)} exceeds ${formatElapsed$1(slowResponseThresholdSeconds * 1e3)}` : "Slowest request in this turn" })
|
|
3012
3180
|
] }) }),
|
|
3013
3181
|
aggregate.hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 shrink-0", children: [
|
|
3014
3182
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
|
|
@@ -3052,23 +3220,225 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3052
3220
|
onToggle: toggleCollapse
|
|
3053
3221
|
}
|
|
3054
3222
|
),
|
|
3055
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3223
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("flex-1 min-w-0 mb-0.5 rounded-lg", bgClass), children: [
|
|
3224
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3225
|
+
LogEntry,
|
|
3226
|
+
{
|
|
3227
|
+
log,
|
|
3228
|
+
viewMode,
|
|
3229
|
+
strip,
|
|
3230
|
+
slowResponseThresholdSeconds,
|
|
3231
|
+
timeDisplayFormat,
|
|
3232
|
+
cacheTrend: cacheTrends?.get(log.id) ?? null,
|
|
3233
|
+
onCompareWithPrevious: comparisonPredecessors.has(log.id) ? onCompareWithPrevious : void 0
|
|
3234
|
+
}
|
|
3235
|
+
),
|
|
3236
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEvents, { events: toolEventsByLogId.get(log.id) ?? [] })
|
|
3237
|
+
] })
|
|
3066
3238
|
] }, log.id);
|
|
3067
3239
|
})
|
|
3068
3240
|
)
|
|
3069
3241
|
}
|
|
3070
3242
|
);
|
|
3071
3243
|
});
|
|
3244
|
+
const CandidateResponseSchema = object({
|
|
3245
|
+
candidates: array(KnowledgeCandidateSchema)
|
|
3246
|
+
});
|
|
3247
|
+
function formatElapsed(ms) {
|
|
3248
|
+
if (ms === null) return "-";
|
|
3249
|
+
if (ms < 1e3) return `${String(ms)}ms`;
|
|
3250
|
+
return `${(ms / 1e3).toFixed(1)}s`;
|
|
3251
|
+
}
|
|
3252
|
+
function formatTimeRange$1(startedAt, endedAt, timeDisplayFormat) {
|
|
3253
|
+
if (startedAt === null || endedAt === null) return null;
|
|
3254
|
+
return formatTimestampRange(startedAt, endedAt, timeDisplayFormat);
|
|
3255
|
+
}
|
|
3256
|
+
function formatCandidateCount(count) {
|
|
3257
|
+
return `${String(count)} candidate${count === 1 ? "" : "s"}`;
|
|
3258
|
+
}
|
|
3259
|
+
function getLogAnchor(logId) {
|
|
3260
|
+
return `log-${String(logId)}`;
|
|
3261
|
+
}
|
|
3262
|
+
function jumpToLog(logId) {
|
|
3263
|
+
const anchor = getLogAnchor(logId);
|
|
3264
|
+
const target = document.getElementById(anchor);
|
|
3265
|
+
window.history.replaceState(null, "", `#${anchor}`);
|
|
3266
|
+
if (!(target instanceof HTMLElement)) return;
|
|
3267
|
+
target.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
3268
|
+
target.focus({ preventScroll: true });
|
|
3269
|
+
}
|
|
3270
|
+
function CandidateList({ candidates }) {
|
|
3271
|
+
if (candidates.length === 0) return null;
|
|
3272
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1.5", children: candidates.map((candidate) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3273
|
+
"div",
|
|
3274
|
+
{
|
|
3275
|
+
className: "rounded-md border border-border/80 bg-background/60 px-2.5 py-2",
|
|
3276
|
+
children: [
|
|
3277
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
3278
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-5 px-1.5 text-[10px] font-mono", children: candidate.type }),
|
|
3279
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", title: candidate.title, children: candidate.title }),
|
|
3280
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 font-mono text-[10px] text-muted-foreground", children: candidate.status })
|
|
3281
|
+
] }),
|
|
3282
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 flex flex-wrap items-center gap-1.5", children: candidate.logIds.map((logId) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3283
|
+
"a",
|
|
3284
|
+
{
|
|
3285
|
+
href: `#${getLogAnchor(logId)}`,
|
|
3286
|
+
onClick: (event) => {
|
|
3287
|
+
event.preventDefault();
|
|
3288
|
+
jumpToLog(logId);
|
|
3289
|
+
},
|
|
3290
|
+
className: "rounded border border-blue-400/25 px-1.5 py-0.5 font-mono text-[10px] text-blue-400 underline-offset-2 transition-colors hover:bg-blue-400/10 hover:text-blue-300 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
3291
|
+
"aria-label": `Jump to evidence log ${String(logId)}`,
|
|
3292
|
+
children: [
|
|
3293
|
+
"#",
|
|
3294
|
+
logId
|
|
3295
|
+
]
|
|
3296
|
+
},
|
|
3297
|
+
logId
|
|
3298
|
+
)) })
|
|
3299
|
+
]
|
|
3300
|
+
},
|
|
3301
|
+
candidate.id
|
|
3302
|
+
)) });
|
|
3303
|
+
}
|
|
3304
|
+
function AgentTraceSummary({
|
|
3305
|
+
logs,
|
|
3306
|
+
scopeId,
|
|
3307
|
+
slowResponseThresholdSeconds,
|
|
3308
|
+
showRollupMetrics,
|
|
3309
|
+
timeDisplayFormat
|
|
3310
|
+
}) {
|
|
3311
|
+
const [candidates, setCandidates] = reactExports.useState([]);
|
|
3312
|
+
const [candidatesExpanded, setCandidatesExpanded] = reactExports.useState(true);
|
|
3313
|
+
const [candidateState, setCandidateState] = reactExports.useState({
|
|
3314
|
+
status: "idle",
|
|
3315
|
+
error: null
|
|
3316
|
+
});
|
|
3317
|
+
const hasCandidates = candidates.length > 0;
|
|
3318
|
+
const summary = reactExports.useMemo(
|
|
3319
|
+
() => buildTraceSummary(logs, slowResponseThresholdSeconds, candidates.length),
|
|
3320
|
+
[candidates.length, logs, slowResponseThresholdSeconds]
|
|
3321
|
+
);
|
|
3322
|
+
const showElapsedSummary = showRollupMetrics || summary.maxElapsedMs !== null;
|
|
3323
|
+
const timeRange = reactExports.useMemo(
|
|
3324
|
+
() => formatTimeRange$1(summary.startedAt, summary.endedAt, timeDisplayFormat),
|
|
3325
|
+
[summary.endedAt, summary.startedAt, timeDisplayFormat]
|
|
3326
|
+
);
|
|
3327
|
+
const createCandidates = reactExports.useCallback(() => {
|
|
3328
|
+
if (logs.length === 0 || candidateState.status === "loading") return;
|
|
3329
|
+
setCandidateState({ status: "loading", error: null });
|
|
3330
|
+
void (async () => {
|
|
3331
|
+
try {
|
|
3332
|
+
const response = await fetch(
|
|
3333
|
+
`/api/knowledge/sessions/${encodeURIComponent(scopeId)}/candidates`,
|
|
3334
|
+
{ method: "POST" }
|
|
3335
|
+
);
|
|
3336
|
+
if (!response.ok) {
|
|
3337
|
+
const message = await readApiError(
|
|
3338
|
+
response,
|
|
3339
|
+
`Candidate generation failed with ${String(response.status)}`
|
|
3340
|
+
);
|
|
3341
|
+
setCandidateState({ status: "failed", error: message });
|
|
3342
|
+
return;
|
|
3343
|
+
}
|
|
3344
|
+
const parsed = await parseJsonResponse$1(response, CandidateResponseSchema);
|
|
3345
|
+
setCandidates(parsed.candidates);
|
|
3346
|
+
setCandidatesExpanded(parsed.candidates.length > 0);
|
|
3347
|
+
setCandidateState({ status: "ready", error: null });
|
|
3348
|
+
} catch (error) {
|
|
3349
|
+
setCandidateState({
|
|
3350
|
+
status: "failed",
|
|
3351
|
+
error: error instanceof Error ? error.message : "Candidate response was invalid"
|
|
3352
|
+
});
|
|
3353
|
+
}
|
|
3354
|
+
})();
|
|
3355
|
+
}, [candidateState.status, logs.length, scopeId]);
|
|
3356
|
+
if (logs.length === 0) return null;
|
|
3357
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "mb-2 rounded-lg border border-border bg-muted/10 px-3 py-2", children: [
|
|
3358
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs", children: [
|
|
3359
|
+
showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 font-semibold text-foreground", children: [
|
|
3360
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3.5 text-blue-400" }),
|
|
3361
|
+
summary.llmCallCount,
|
|
3362
|
+
" LLM"
|
|
3363
|
+
] }),
|
|
3364
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
|
|
3365
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-sky-400/70" }),
|
|
3366
|
+
summary.toolCallCount,
|
|
3367
|
+
" tools"
|
|
3368
|
+
] }),
|
|
3369
|
+
showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
|
|
3370
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-emerald-400" }),
|
|
3371
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
|
|
3372
|
+
formatTokens(summary.totalInputTokens),
|
|
3373
|
+
" / ",
|
|
3374
|
+
formatTokens(summary.totalOutputTokens)
|
|
3375
|
+
] })
|
|
3376
|
+
] }),
|
|
3377
|
+
(summary.totalCacheCreationInputTokens > 0 || summary.totalCacheReadInputTokens > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
|
|
3378
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-purple-400" }),
|
|
3379
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
|
|
3380
|
+
"+",
|
|
3381
|
+
formatTokens(summary.totalCacheCreationInputTokens),
|
|
3382
|
+
" / ~",
|
|
3383
|
+
formatTokens(summary.totalCacheReadInputTokens)
|
|
3384
|
+
] })
|
|
3385
|
+
] }),
|
|
3386
|
+
showElapsedSummary && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
|
|
3387
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3.5" }),
|
|
3388
|
+
showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: formatElapsed(summary.totalElapsedMs) }),
|
|
3389
|
+
summary.maxElapsedMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono text-muted-foreground/70", children: [
|
|
3390
|
+
"max ",
|
|
3391
|
+
formatElapsed(summary.maxElapsedMs)
|
|
3392
|
+
] })
|
|
3393
|
+
] }),
|
|
3394
|
+
showRollupMetrics && timeRange !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-muted-foreground/70", children: timeRange }),
|
|
3395
|
+
(summary.failedCallCount > 0 || summary.pendingCallCount > 0 || summary.slowCallCount > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-amber-400", children: [
|
|
3396
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3.5" }),
|
|
3397
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
|
|
3398
|
+
summary.failedCallCount,
|
|
3399
|
+
" fail / ",
|
|
3400
|
+
summary.pendingCallCount,
|
|
3401
|
+
" pending /",
|
|
3402
|
+
" ",
|
|
3403
|
+
summary.slowCallCount,
|
|
3404
|
+
" slow"
|
|
3405
|
+
] })
|
|
3406
|
+
] }),
|
|
3407
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
|
|
3408
|
+
hasCandidates && /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-6 px-2 text-[10px] font-mono", children: formatCandidateCount(summary.knowledgeCandidateCount) }),
|
|
3409
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3410
|
+
Button,
|
|
3411
|
+
{
|
|
3412
|
+
type: "button",
|
|
3413
|
+
variant: "outline",
|
|
3414
|
+
size: "sm",
|
|
3415
|
+
className: "h-7 gap-1.5 px-2 text-xs",
|
|
3416
|
+
onClick: createCandidates,
|
|
3417
|
+
disabled: candidateState.status === "loading",
|
|
3418
|
+
children: [
|
|
3419
|
+
candidateState.status === "loading" ? /* @__PURE__ */ jsxRuntimeExports.jsx(LoaderCircle, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5" }),
|
|
3420
|
+
"Candidate"
|
|
3421
|
+
]
|
|
3422
|
+
}
|
|
3423
|
+
),
|
|
3424
|
+
hasCandidates && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3425
|
+
Button,
|
|
3426
|
+
{
|
|
3427
|
+
type: "button",
|
|
3428
|
+
variant: "ghost",
|
|
3429
|
+
size: "icon",
|
|
3430
|
+
className: "size-7 text-muted-foreground",
|
|
3431
|
+
onClick: () => setCandidatesExpanded((value) => !value),
|
|
3432
|
+
"aria-expanded": candidatesExpanded,
|
|
3433
|
+
"aria-label": candidatesExpanded ? "Collapse memory candidates" : "Expand memory candidates",
|
|
3434
|
+
children: candidatesExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3.5" })
|
|
3435
|
+
}
|
|
3436
|
+
)
|
|
3437
|
+
] }),
|
|
3438
|
+
candidateState.status === "failed" && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-xs text-destructive", children: candidateState.error }),
|
|
3439
|
+
candidatesExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx(CandidateList, { candidates })
|
|
3440
|
+
] });
|
|
3441
|
+
}
|
|
3072
3442
|
function computeStats(logs) {
|
|
3073
3443
|
let totalInput = 0;
|
|
3074
3444
|
let totalOutput = 0;
|
|
@@ -3087,7 +3457,9 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
3087
3457
|
onCompareWithPrevious,
|
|
3088
3458
|
comparisonPredecessors,
|
|
3089
3459
|
onClearGroup,
|
|
3090
|
-
standalone = false
|
|
3460
|
+
standalone = false,
|
|
3461
|
+
hasPinnedSessionContext = false,
|
|
3462
|
+
timeDisplayFormat
|
|
3091
3463
|
}) {
|
|
3092
3464
|
const [expanded, setExpanded] = reactExports.useState(false);
|
|
3093
3465
|
const stats = reactExports.useMemo(() => computeStats(group.logs), [group.logs]);
|
|
@@ -3095,6 +3467,7 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
3095
3467
|
const endTime = group.logs[group.logs.length - 1]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3096
3468
|
const mixed = hasMixedApiFormat(group.logs);
|
|
3097
3469
|
const isLoading = group.logs.some((log) => log.responseStatus === null);
|
|
3470
|
+
const showTraceRollupMetrics = standalone && !hasPinnedSessionContext;
|
|
3098
3471
|
const turnGroups = reactExports.useMemo(() => buildTurnGroups(group.logs), [group.logs]);
|
|
3099
3472
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2", children: [
|
|
3100
3473
|
!standalone && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -3112,23 +3485,37 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
3112
3485
|
hideApiFormat: mixed,
|
|
3113
3486
|
isLoading,
|
|
3114
3487
|
userAgent: group.logs[0]?.userAgent ?? null,
|
|
3488
|
+
timeDisplayFormat,
|
|
3115
3489
|
onClear: () => onClearGroup(group.logs.map((l) => l.id))
|
|
3116
3490
|
}
|
|
3117
3491
|
),
|
|
3118
|
-
shouldRenderConversationContent(standalone, expanded) && /* @__PURE__ */ jsxRuntimeExports.
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3492
|
+
shouldRenderConversationContent(standalone, expanded) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
3493
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3494
|
+
AgentTraceSummary,
|
|
3495
|
+
{
|
|
3496
|
+
logs: group.logs,
|
|
3497
|
+
scopeId: group.conversationId,
|
|
3498
|
+
slowResponseThresholdSeconds,
|
|
3499
|
+
showRollupMetrics: showTraceRollupMetrics,
|
|
3500
|
+
timeDisplayFormat
|
|
3501
|
+
}
|
|
3502
|
+
),
|
|
3503
|
+
turnGroups.map((tg) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3504
|
+
TurnGroup,
|
|
3505
|
+
{
|
|
3506
|
+
entries: tg.entries,
|
|
3507
|
+
viewMode,
|
|
3508
|
+
strip,
|
|
3509
|
+
slowResponseThresholdSeconds,
|
|
3510
|
+
timeDisplayFormat,
|
|
3511
|
+
cacheTrends,
|
|
3512
|
+
onCompareWithPrevious,
|
|
3513
|
+
comparisonPredecessors,
|
|
3514
|
+
turnIndex: tg.turnIndex
|
|
3515
|
+
},
|
|
3516
|
+
tg.turnIndex
|
|
3517
|
+
))
|
|
3518
|
+
] })
|
|
3132
3519
|
] });
|
|
3133
3520
|
});
|
|
3134
3521
|
function CrabLogo({ className }) {
|
|
@@ -4480,7 +4867,7 @@ function ProvidersPanel({
|
|
|
4480
4867
|
try {
|
|
4481
4868
|
const res = await fetch("/api/config/paths");
|
|
4482
4869
|
if (res.ok) {
|
|
4483
|
-
const data = await parseJsonResponse(res, ConfigPathsResponseSchema);
|
|
4870
|
+
const data = await parseJsonResponse$1(res, ConfigPathsResponseSchema);
|
|
4484
4871
|
setConfigPath(data.providerConfig);
|
|
4485
4872
|
}
|
|
4486
4873
|
} catch {
|
|
@@ -4559,7 +4946,7 @@ function ProvidersPanel({
|
|
|
4559
4946
|
signal: controller.signal
|
|
4560
4947
|
});
|
|
4561
4948
|
if (res.ok) {
|
|
4562
|
-
const results = await parseJsonResponse(res, ProviderTestResultsSchema);
|
|
4949
|
+
const results = await parseJsonResponse$1(res, ProviderTestResultsSchema);
|
|
4563
4950
|
updateTestResults(providerId, results);
|
|
4564
4951
|
await persistProviderTestLog(providerId, results);
|
|
4565
4952
|
} else {
|
|
@@ -4599,7 +4986,7 @@ function ProvidersPanel({
|
|
|
4599
4986
|
setError(await readApiError(res, "Failed to add provider"));
|
|
4600
4987
|
return;
|
|
4601
4988
|
}
|
|
4602
|
-
const newProvider = await parseJsonResponse(res, ProviderConfigSchema);
|
|
4989
|
+
const newProvider = await parseJsonResponse$1(res, ProviderConfigSchema);
|
|
4603
4990
|
setShowForm(false);
|
|
4604
4991
|
triggerHighlight(newProvider.id);
|
|
4605
4992
|
refreshProviders();
|
|
@@ -4622,7 +5009,7 @@ function ProvidersPanel({
|
|
|
4622
5009
|
setError(await readApiError(res, "Failed to update provider"));
|
|
4623
5010
|
return;
|
|
4624
5011
|
}
|
|
4625
|
-
const updated = await parseJsonResponse(res, ProviderConfigSchema);
|
|
5012
|
+
const updated = await parseJsonResponse$1(res, ProviderConfigSchema);
|
|
4626
5013
|
setEditingProvider(void 0);
|
|
4627
5014
|
triggerHighlight(updated.id);
|
|
4628
5015
|
refreshProviders();
|
|
@@ -4697,7 +5084,7 @@ function ProvidersPanel({
|
|
|
4697
5084
|
headers: { "Content-Type": "application/json" },
|
|
4698
5085
|
body: JSON.stringify(text)
|
|
4699
5086
|
});
|
|
4700
|
-
const data = await parseJsonResponse(res, ImportResponseSchema);
|
|
5087
|
+
const data = await parseJsonResponse$1(res, ImportResponseSchema);
|
|
4701
5088
|
if (res.ok && data.imported !== void 0 && data.imported > 0) {
|
|
4702
5089
|
refreshProviders();
|
|
4703
5090
|
setError(null);
|
|
@@ -4986,9 +5373,11 @@ function ProxySettingsTab() {
|
|
|
4986
5373
|
const {
|
|
4987
5374
|
strip,
|
|
4988
5375
|
slowResponseThresholdSeconds,
|
|
5376
|
+
timeDisplayFormat,
|
|
4989
5377
|
isLoading,
|
|
4990
5378
|
setStrip,
|
|
4991
|
-
setSlowResponseThresholdSeconds
|
|
5379
|
+
setSlowResponseThresholdSeconds,
|
|
5380
|
+
setTimeDisplayFormat
|
|
4992
5381
|
} = useStripConfig();
|
|
4993
5382
|
const [error, setError] = reactExports.useState(null);
|
|
4994
5383
|
const [pending, setPending] = reactExports.useState(false);
|
|
@@ -5020,6 +5409,20 @@ function ProxySettingsTab() {
|
|
|
5020
5409
|
},
|
|
5021
5410
|
[setSlowResponseThresholdSeconds]
|
|
5022
5411
|
);
|
|
5412
|
+
const handleTimeDisplayFormatChange = reactExports.useCallback(
|
|
5413
|
+
async (next) => {
|
|
5414
|
+
setError(null);
|
|
5415
|
+
setPending(true);
|
|
5416
|
+
try {
|
|
5417
|
+
await setTimeDisplayFormat(next);
|
|
5418
|
+
} catch (err) {
|
|
5419
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
5420
|
+
} finally {
|
|
5421
|
+
setPending(false);
|
|
5422
|
+
}
|
|
5423
|
+
},
|
|
5424
|
+
[setTimeDisplayFormat]
|
|
5425
|
+
);
|
|
5023
5426
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
5024
5427
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
5025
5428
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "Claude Code billing header" }),
|
|
@@ -5080,6 +5483,28 @@ function ProxySettingsTab() {
|
|
|
5080
5483
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: "seconds" })
|
|
5081
5484
|
] })
|
|
5082
5485
|
] }),
|
|
5486
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
5487
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "time-display-format", className: "text-sm font-semibold", children: "Time display" }),
|
|
5488
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Controls timestamps in session summaries, conversation headers, and log rows." }),
|
|
5489
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
5490
|
+
"select",
|
|
5491
|
+
{
|
|
5492
|
+
id: "time-display-format",
|
|
5493
|
+
value: timeDisplayFormat,
|
|
5494
|
+
disabled: isLoading || pending,
|
|
5495
|
+
onChange: (event) => {
|
|
5496
|
+
const parsed = TimeDisplayFormatSchema.safeParse(event.currentTarget.value);
|
|
5497
|
+
if (!parsed.success) return;
|
|
5498
|
+
void handleTimeDisplayFormatChange(parsed.data);
|
|
5499
|
+
},
|
|
5500
|
+
className: "h-8 rounded-md border border-input bg-background px-2 text-sm disabled:cursor-not-allowed disabled:opacity-50",
|
|
5501
|
+
children: [
|
|
5502
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "time", children: "Time only" }),
|
|
5503
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "full", children: "Full ISO" })
|
|
5504
|
+
]
|
|
5505
|
+
}
|
|
5506
|
+
)
|
|
5507
|
+
] }),
|
|
5083
5508
|
error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-destructive", children: [
|
|
5084
5509
|
"Failed to save: ",
|
|
5085
5510
|
error
|
|
@@ -5266,16 +5691,11 @@ function computeTokenSummary(logs) {
|
|
|
5266
5691
|
}
|
|
5267
5692
|
return { totalIn, totalOut };
|
|
5268
5693
|
}
|
|
5269
|
-
function formatTimeRange(logs) {
|
|
5694
|
+
function formatTimeRange(logs, timeDisplayFormat) {
|
|
5270
5695
|
const first = logs[0];
|
|
5271
5696
|
const last = logs[logs.length - 1];
|
|
5272
5697
|
if (first === void 0 || last === void 0) return null;
|
|
5273
|
-
|
|
5274
|
-
hour: "2-digit",
|
|
5275
|
-
minute: "2-digit",
|
|
5276
|
-
second: "2-digit"
|
|
5277
|
-
});
|
|
5278
|
-
return `${format(first.timestamp)} - ${format(last.timestamp)}`;
|
|
5698
|
+
return formatTimestampRange(first.timestamp, last.timestamp, timeDisplayFormat);
|
|
5279
5699
|
}
|
|
5280
5700
|
function getFirstUserAgent(logs) {
|
|
5281
5701
|
for (const log of logs) {
|
|
@@ -5350,10 +5770,14 @@ function SessionContextBar({
|
|
|
5350
5770
|
sessionId,
|
|
5351
5771
|
logs,
|
|
5352
5772
|
totalIn,
|
|
5353
|
-
totalOut
|
|
5773
|
+
totalOut,
|
|
5774
|
+
timeDisplayFormat
|
|
5354
5775
|
}) {
|
|
5355
5776
|
const [copied, setCopied] = reactExports.useState(false);
|
|
5356
|
-
const timeRange = reactExports.useMemo(
|
|
5777
|
+
const timeRange = reactExports.useMemo(
|
|
5778
|
+
() => formatTimeRange(logs, timeDisplayFormat),
|
|
5779
|
+
[logs, timeDisplayFormat]
|
|
5780
|
+
);
|
|
5357
5781
|
const userAgent = reactExports.useMemo(() => getFirstUserAgent(logs), [logs]);
|
|
5358
5782
|
const handleCopyLink = reactExports.useCallback(() => {
|
|
5359
5783
|
void window.navigator.clipboard.writeText(window.location.href).then(() => {
|
|
@@ -5426,6 +5850,7 @@ function ProxyViewer({
|
|
|
5426
5850
|
onViewModeChange,
|
|
5427
5851
|
strip,
|
|
5428
5852
|
slowResponseThresholdSeconds,
|
|
5853
|
+
timeDisplayFormat,
|
|
5429
5854
|
hideSessionFilter = false,
|
|
5430
5855
|
pinnedSessionId
|
|
5431
5856
|
}) {
|
|
@@ -5472,6 +5897,7 @@ function ProxyViewer({
|
|
|
5472
5897
|
setComparePair(null);
|
|
5473
5898
|
}, []);
|
|
5474
5899
|
const groups = reactExports.useMemo(() => groupLogsByConversation(logs), [logs]);
|
|
5900
|
+
const hasPinnedSessionContext = pinnedSessionId !== void 0;
|
|
5475
5901
|
const cacheTrends = reactExports.useMemo(() => computeCacheTrends(groups), [groups]);
|
|
5476
5902
|
const comparisonPredecessors = reactExports.useMemo(() => buildValidPredecessors(groups), [groups]);
|
|
5477
5903
|
const handleCompareWithPrevious = reactExports.useCallback(
|
|
@@ -5545,7 +5971,8 @@ function ProxyViewer({
|
|
|
5545
5971
|
sessionId: pinnedSessionId,
|
|
5546
5972
|
logs,
|
|
5547
5973
|
totalIn,
|
|
5548
|
-
totalOut
|
|
5974
|
+
totalOut,
|
|
5975
|
+
timeDisplayFormat
|
|
5549
5976
|
}
|
|
5550
5977
|
),
|
|
5551
5978
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
|
|
@@ -5584,7 +6011,7 @@ function ProxyViewer({
|
|
|
5584
6011
|
)
|
|
5585
6012
|
] }),
|
|
5586
6013
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1" }),
|
|
5587
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
|
|
6014
|
+
!hasPinnedSessionContext && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
|
|
5588
6015
|
logs.length,
|
|
5589
6016
|
" request",
|
|
5590
6017
|
logs.length !== 1 ? "s" : "",
|
|
@@ -5655,7 +6082,9 @@ function ProxyViewer({
|
|
|
5655
6082
|
onCompareWithPrevious: handleCompareWithPrevious,
|
|
5656
6083
|
comparisonPredecessors,
|
|
5657
6084
|
onClearGroup,
|
|
5658
|
-
standalone: groups.length === 1
|
|
6085
|
+
standalone: groups.length === 1,
|
|
6086
|
+
hasPinnedSessionContext,
|
|
6087
|
+
timeDisplayFormat
|
|
5659
6088
|
},
|
|
5660
6089
|
group.id
|
|
5661
6090
|
)) })
|
|
@@ -5913,7 +6342,7 @@ function ProxyViewerContainer({
|
|
|
5913
6342
|
}
|
|
5914
6343
|
})();
|
|
5915
6344
|
}, []);
|
|
5916
|
-
const { strip, slowResponseThresholdSeconds } = useStripConfig();
|
|
6345
|
+
const { strip, slowResponseThresholdSeconds, timeDisplayFormat } = useStripConfig();
|
|
5917
6346
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
5918
6347
|
error !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed top-4 right-4 bg-destructive text-destructive-foreground px-4 py-2 rounded-md text-sm z-50", children: error }),
|
|
5919
6348
|
/* @__PURE__ */ jsxRuntimeExports.jsx(OnboardingBanner, {}),
|
|
@@ -5933,6 +6362,7 @@ function ProxyViewerContainer({
|
|
|
5933
6362
|
onViewModeChange: setViewMode,
|
|
5934
6363
|
strip,
|
|
5935
6364
|
slowResponseThresholdSeconds,
|
|
6365
|
+
timeDisplayFormat,
|
|
5936
6366
|
hideSessionFilter: initialSessionId !== void 0,
|
|
5937
6367
|
pinnedSessionId: initialSessionId
|
|
5938
6368
|
}
|