@tonyclaw/agent-inspector 2.0.3 → 2.0.5
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-D5A4bTfV.js → CompareDrawer-3nRwtk8J.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-CbW5VRER.js +101 -0
- package/.output/public/assets/ReplayDialog-Cl62N9PI.js +1 -0
- package/.output/public/assets/RequestAnatomy-DgQWGvjs.js +1 -0
- package/.output/public/assets/ResponseView-Cvc-ct4E.js +1 -0
- package/.output/public/assets/StreamingChunkSequence-BCQaCAIe.js +1 -0
- package/.output/public/assets/_sessionId-CcD_aLGq.js +1 -0
- package/.output/public/assets/index-B_dffD3u.js +1 -0
- package/.output/public/assets/index-CX796gvi.css +1 -0
- package/.output/public/assets/{json-viewer-BbU0n8eM.js → json-viewer-IXejqXB0.js} +1 -1
- package/.output/public/assets/{main-CZT_F-gu.js → main-2NlGzgOe.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +181 -114
- package/.output/server/{_sessionId-B-s9P7fJ.mjs → _sessionId-DWCTasJU.mjs} +3 -3
- package/.output/server/_ssr/{CompareDrawer-C08L3UOO.mjs → CompareDrawer-DhrN1uC2.mjs} +6 -6
- package/.output/server/_ssr/{ProxyViewerContainer-CMWl3Ijy.mjs → ProxyViewerContainer-DRl51s_n.mjs} +910 -186
- package/.output/server/_ssr/{ReplayDialog-CPDo9_G5.mjs → ReplayDialog-BQT_ygxC.mjs} +240 -14
- package/.output/server/_ssr/{RequestAnatomy-D9wt_K1E.mjs → RequestAnatomy-DS2tZOgq.mjs} +5 -5
- package/.output/server/_ssr/{ResponseView-DXaL7nY3.mjs → ResponseView-e0kL2C3x.mjs} +25 -21
- package/.output/server/_ssr/{StreamingChunkSequence-B_hudZyb.mjs → StreamingChunkSequence-BJG-m7xs.mjs} +3 -3
- package/.output/server/_ssr/{index-CuE_BN86.mjs → index-Dea3OeRw.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-Ci6kkjde.mjs → json-viewer-DDU55MLK.mjs} +3 -3
- package/.output/server/_ssr/{router-BemxgIg7.mjs → router-Dl7oh0zx.mjs} +164 -82
- package/.output/server/_tanstack-start-manifest_v-m-FJNBVf.mjs +4 -0
- package/.output/server/index.mjs +70 -70
- package/package.json +1 -1
- package/src/components/OnboardingBanner.tsx +11 -19
- package/src/components/ProxyViewer.tsx +26 -16
- package/src/components/ProxyViewerContainer.tsx +2 -1
- package/src/components/providers/ProviderCard.tsx +6 -20
- package/src/components/providers/SettingsDialog.tsx +140 -3
- package/src/components/proxy-viewer/AgentTraceSummary.tsx +731 -72
- package/src/components/proxy-viewer/AnswerMarkdown.tsx +16 -0
- package/src/components/proxy-viewer/CompareDrawer.tsx +4 -2
- package/src/components/proxy-viewer/ConversationGroup.tsx +12 -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 +21 -36
- package/src/components/proxy-viewer/ReplayDialog.tsx +190 -8
- package/src/components/proxy-viewer/ResponseView.tsx +4 -8
- package/src/components/proxy-viewer/ToolTraceEvents.tsx +37 -17
- package/src/components/proxy-viewer/TurnGroup.tsx +18 -2
- 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/anthropic/ResponseView.tsx +2 -2
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +10 -14
- package/src/components/proxy-viewer/replayComparison.ts +131 -0
- package/src/components/proxy-viewer/useKeyboardNavigation.ts +64 -22
- package/src/components/proxy-viewer/viewerState.ts +14 -2
- package/src/knowledge/candidateStore.ts +32 -1
- 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/config.ts +3 -0
- package/src/routes/api/config.ts +5 -1
- package/src/routes/api/knowledge.candidates.$candidateId.ts +50 -0
- package/src/routes/api/knowledge.sessions.$sessionId.candidates.ts +12 -2
- package/.output/public/assets/ProxyViewerContainer-Da0jpBkp.js +0 -101
- package/.output/public/assets/ReplayDialog-CxUk_TF0.js +0 -1
- package/.output/public/assets/RequestAnatomy-DIlzjgjJ.js +0 -1
- package/.output/public/assets/ResponseView-DQCuKJ1G.js +0 -1
- package/.output/public/assets/StreamingChunkSequence-DHk4SGGL.js +0 -1
- package/.output/public/assets/_sessionId-dY1TTl7N.js +0 -1
- package/.output/public/assets/index-D7wwbwly.css +0 -1
- package/.output/public/assets/index-FqQZbfl2.js +0 -1
- package/.output/server/_tanstack-start-manifest_v--L1_b4sd.mjs +0 -4
package/.output/server/_ssr/{ProxyViewerContainer-CMWl3Ijy.mjs → ProxyViewerContainer-DRl51s_n.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-Dl7oh0zx.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,
|
|
12
|
+
import { C as Check, X, P as Plus, D as Download, S as Settings, A as ArrowLeft, a as Copy, b as ChevronDown, U as Upload, c as Scan, d as CircleAlert, T as Terminal, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, i as Trash2, W as Wrench, j as TriangleAlert, B as Brain, k as EyeOff, l as Eye, R as RotateCw, m as Pencil, n as Minus, o as CircleCheckBig, p as CircleX, q as ShieldCheck, r as RefreshCw, s as Save, F as FileSearch, t as CircleCheck, u as CloudUpload, O as OctagonAlert, G as Globe, v as FileTerminal, w as Radio, x as ChevronsUp, y as ChevronsDown, z as FileDiff, H as History, I as RotateCcw, J as GitCompareArrows, K as CircleQuestionMark, N as Server, Q as Gauge, V as Lock, Y as Wifi, _ as WifiOff, $ as ArrowUp, a0 as ArrowDown, a1 as Rows3, a2 as Columns2 } from "../_libs/lucide-react.mjs";
|
|
13
13
|
import { u as union, d as object, a as array, l as literal, b as string, n as number, c as boolean, _ as _enum } from "../_libs/zod.mjs";
|
|
14
14
|
import { P as Provider, R as Root3, T as Trigger$1, a as Portal$1, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
|
|
15
15
|
import { R as Root2$1, L as List, T as Trigger$3, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
|
|
@@ -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
|
|
@@ -167,27 +179,20 @@ function OnboardingBanner() {
|
|
|
167
179
|
"aria-label": "Onboarding tip",
|
|
168
180
|
className: "mx-4 mt-2 mb-1 flex items-start gap-3 rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-sm",
|
|
169
181
|
children: [
|
|
170
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "
|
|
171
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium text-amber-600 dark:text-amber-400
|
|
172
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "space-y-0.5 text-
|
|
182
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
183
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-1 font-medium text-amber-600 dark:text-amber-400", children: "Agent Inspector is ready" }),
|
|
184
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "space-y-0.5 text-xs leading-relaxed text-muted-foreground", children: [
|
|
173
185
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
|
|
174
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "
|
|
175
|
-
"
|
|
176
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Response" }),
|
|
177
|
-
" — structured views of what the proxy sent and received."
|
|
186
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Trace" }),
|
|
187
|
+
": requests, responses, streaming chunks, tools, and timing."
|
|
178
188
|
] }),
|
|
179
189
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
|
|
180
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "
|
|
181
|
-
"
|
|
190
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Replay" }),
|
|
191
|
+
": resend captured requests and compare provider behavior."
|
|
182
192
|
] }),
|
|
183
193
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
|
|
184
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "
|
|
185
|
-
"
|
|
186
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Raw Request" }),
|
|
187
|
-
" /",
|
|
188
|
-
" ",
|
|
189
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Raw Response" }),
|
|
190
|
-
" — exact bytes from the upstream provider (visible in Full mode)."
|
|
194
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Memory" }),
|
|
195
|
+
": create reviewable candidates before promotion to OpenClaw."
|
|
191
196
|
] })
|
|
192
197
|
] })
|
|
193
198
|
] }),
|
|
@@ -198,7 +203,7 @@ function OnboardingBanner() {
|
|
|
198
203
|
onClick: () => {
|
|
199
204
|
void markSeen();
|
|
200
205
|
},
|
|
201
|
-
className: "inline-flex items-center gap-1.5
|
|
206
|
+
className: "inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-amber-500/40 px-3 text-xs text-amber-700 transition-colors hover:bg-amber-500/10 dark:text-amber-300",
|
|
202
207
|
"aria-label": "Dismiss onboarding tip",
|
|
203
208
|
children: [
|
|
204
209
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }),
|
|
@@ -213,7 +218,7 @@ function OnboardingBanner() {
|
|
|
213
218
|
onClick: () => {
|
|
214
219
|
void markSeen();
|
|
215
220
|
},
|
|
216
|
-
className: "
|
|
221
|
+
className: "-m-1 shrink-0 p-1 text-muted-foreground transition-colors hover:text-foreground",
|
|
217
222
|
"aria-label": "Dismiss",
|
|
218
223
|
children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3.5" })
|
|
219
224
|
}
|
|
@@ -261,6 +266,21 @@ async function exportLogsAsZip(logs) {
|
|
|
261
266
|
document.body.removeChild(anchor);
|
|
262
267
|
URL.revokeObjectURL(url);
|
|
263
268
|
}
|
|
269
|
+
function formatTimestamp(iso, format) {
|
|
270
|
+
switch (format) {
|
|
271
|
+
case "full":
|
|
272
|
+
return iso;
|
|
273
|
+
case "time":
|
|
274
|
+
return new Date(iso).toLocaleTimeString([], {
|
|
275
|
+
hour: "2-digit",
|
|
276
|
+
minute: "2-digit",
|
|
277
|
+
second: "2-digit"
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function formatTimestampRange(startedAt, endedAt, format) {
|
|
282
|
+
return `${formatTimestamp(startedAt, format)} - ${formatTimestamp(endedAt, format)}`;
|
|
283
|
+
}
|
|
264
284
|
function cn(...inputs) {
|
|
265
285
|
return twMerge(clsx(inputs));
|
|
266
286
|
}
|
|
@@ -275,7 +295,7 @@ function getStatusCategory(status) {
|
|
|
275
295
|
if (status >= 500) return "server_error";
|
|
276
296
|
return "pending";
|
|
277
297
|
}
|
|
278
|
-
const version = "2.0.
|
|
298
|
+
const version = "2.0.5";
|
|
279
299
|
const packageJson = {
|
|
280
300
|
version
|
|
281
301
|
};
|
|
@@ -469,10 +489,6 @@ const API_FORMAT_LABELS = {
|
|
|
469
489
|
openai: "OpenAI",
|
|
470
490
|
unknown: "Unknown"
|
|
471
491
|
};
|
|
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
492
|
function ConversationHeader({
|
|
477
493
|
conversationId,
|
|
478
494
|
startTime,
|
|
@@ -486,6 +502,7 @@ function ConversationHeader({
|
|
|
486
502
|
hideApiFormat = false,
|
|
487
503
|
isLoading = false,
|
|
488
504
|
userAgent,
|
|
505
|
+
timeDisplayFormat,
|
|
489
506
|
onClear
|
|
490
507
|
}) {
|
|
491
508
|
const [confirmOpen, setConfirmOpen] = reactExports.useState(false);
|
|
@@ -559,11 +576,7 @@ function ConversationHeader({
|
|
|
559
576
|
),
|
|
560
577
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
|
|
561
578
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
562
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
563
|
-
formatTimestamp(startTime),
|
|
564
|
-
" - ",
|
|
565
|
-
formatTimestamp(endTime)
|
|
566
|
-
] })
|
|
579
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatTimestampRange(startTime, endTime, timeDisplayFormat) })
|
|
567
580
|
] }),
|
|
568
581
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
|
|
569
582
|
/* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3" }),
|
|
@@ -1343,27 +1356,27 @@ function useCopyFeedback(text) {
|
|
|
1343
1356
|
return { copied, copy };
|
|
1344
1357
|
}
|
|
1345
1358
|
const LazyCompareDrawer = reactExports.lazy(
|
|
1346
|
-
() => import("./CompareDrawer-
|
|
1359
|
+
() => import("./CompareDrawer-DhrN1uC2.mjs").then((m) => ({ default: m.CompareDrawer }))
|
|
1347
1360
|
);
|
|
1348
1361
|
const LazyReplayDialog = reactExports.lazy(
|
|
1349
|
-
() => import("./ReplayDialog-
|
|
1362
|
+
() => import("./ReplayDialog-BQT_ygxC.mjs").then((m) => ({ default: m.ReplayDialog }))
|
|
1350
1363
|
);
|
|
1351
1364
|
const LazyRequestAnatomy = reactExports.lazy(
|
|
1352
|
-
() => import("./RequestAnatomy-
|
|
1365
|
+
() => import("./RequestAnatomy-DS2tZOgq.mjs").then((m) => ({ default: m.RequestAnatomy }))
|
|
1353
1366
|
);
|
|
1354
1367
|
const LazyResponseView = reactExports.lazy(
|
|
1355
|
-
() => import("./ResponseView-
|
|
1368
|
+
() => import("./ResponseView-e0kL2C3x.mjs").then((m) => ({ default: m.ResponseView }))
|
|
1356
1369
|
);
|
|
1357
1370
|
const LazyStreamingChunkSequence = reactExports.lazy(
|
|
1358
|
-
() => import("./StreamingChunkSequence-
|
|
1371
|
+
() => import("./StreamingChunkSequence-BJG-m7xs.mjs").then((m) => ({
|
|
1359
1372
|
default: m.StreamingChunkSequence
|
|
1360
1373
|
}))
|
|
1361
1374
|
);
|
|
1362
1375
|
const LazyJsonViewer = reactExports.lazy(
|
|
1363
|
-
() => import("./json-viewer-
|
|
1376
|
+
() => import("./json-viewer-DDU55MLK.mjs").then((m) => ({ default: m.JsonViewer }))
|
|
1364
1377
|
);
|
|
1365
1378
|
const LazyJsonViewerFromString = reactExports.lazy(
|
|
1366
|
-
() => import("./json-viewer-
|
|
1379
|
+
() => import("./json-viewer-DDU55MLK.mjs").then((m) => ({ default: m.JsonViewerFromString }))
|
|
1367
1380
|
);
|
|
1368
1381
|
const HIGHLIGHT_DURATION_MS = 1200;
|
|
1369
1382
|
const MAX_HIGHLIGHT_ATTEMPTS = 12;
|
|
@@ -1780,17 +1793,16 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
1780
1793
|
toolCount = null,
|
|
1781
1794
|
expanded,
|
|
1782
1795
|
onToggle,
|
|
1783
|
-
responseToolNames = null,
|
|
1784
1796
|
cacheTrend = null,
|
|
1785
1797
|
activeTab,
|
|
1786
1798
|
tabActions,
|
|
1787
1799
|
onReplay,
|
|
1788
|
-
slowResponseThresholdSeconds = 0
|
|
1800
|
+
slowResponseThresholdSeconds = 0,
|
|
1801
|
+
timeDisplayFormat
|
|
1789
1802
|
}) {
|
|
1790
1803
|
const statusCategory = getStatusCategory(log.responseStatus);
|
|
1791
1804
|
const isSlowResponse = log.elapsedMs !== null && slowResponseThresholdSeconds > 0 && log.elapsedMs > slowResponseThresholdSeconds * 1e3;
|
|
1792
1805
|
const hasTokens = log.inputTokens !== null || log.outputTokens !== null;
|
|
1793
|
-
const toolNamesJoined = reactExports.useMemo(() => responseToolNames?.join(", ") ?? null, [responseToolNames]);
|
|
1794
1806
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1795
1807
|
"div",
|
|
1796
1808
|
{
|
|
@@ -1819,7 +1831,7 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
1819
1831
|
] }),
|
|
1820
1832
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
|
|
1821
1833
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
|
|
1822
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: log.timestamp })
|
|
1834
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", title: log.timestamp, children: formatTimestamp(log.timestamp, timeDisplayFormat) })
|
|
1823
1835
|
] }),
|
|
1824
1836
|
log.model !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
1825
1837
|
/* @__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" }) }) }),
|
|
@@ -1884,25 +1896,19 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
1884
1896
|
)
|
|
1885
1897
|
] })
|
|
1886
1898
|
] }),
|
|
1887
|
-
log.cacheCreationInputTokens !== null && log.cacheCreationInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1888
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
] })
|
|
1894
|
-
] }) }),
|
|
1895
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Tokens cached for reuse, reducing future API cost" })
|
|
1899
|
+
log.cacheCreationInputTokens !== null && log.cacheCreationInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
|
|
1900
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CacheTrendIndicator, { trend: cacheTrend?.creation ?? null }),
|
|
1901
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
|
|
1902
|
+
"KV Cache +",
|
|
1903
|
+
formatTokens(log.cacheCreationInputTokens)
|
|
1904
|
+
] })
|
|
1896
1905
|
] }),
|
|
1897
|
-
log.cacheReadInputTokens !== null && log.cacheReadInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1898
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
] })
|
|
1904
|
-
] }) }),
|
|
1905
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Tokens served from cache, reducing API cost" })
|
|
1906
|
+
log.cacheReadInputTokens !== null && log.cacheReadInputTokens > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
|
|
1907
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CacheTrendIndicator, { trend: cacheTrend?.read ?? null }),
|
|
1908
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
|
|
1909
|
+
"KV Cache ~",
|
|
1910
|
+
formatTokens(log.cacheReadInputTokens)
|
|
1911
|
+
] })
|
|
1906
1912
|
] }),
|
|
1907
1913
|
messageCount !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
|
|
1908
1914
|
/* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3" }),
|
|
@@ -1912,10 +1918,6 @@ const LogEntryHeader = reactExports.memo(function({
|
|
|
1912
1918
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3" }),
|
|
1913
1919
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: toolCount })
|
|
1914
1920
|
] }),
|
|
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
1921
|
log.origin !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
1920
1922
|
"span",
|
|
1921
1923
|
{
|
|
@@ -2403,6 +2405,7 @@ const LogEntry = reactExports.memo(function({
|
|
|
2403
2405
|
viewMode = "simple",
|
|
2404
2406
|
strip,
|
|
2405
2407
|
slowResponseThresholdSeconds,
|
|
2408
|
+
timeDisplayFormat,
|
|
2406
2409
|
cacheTrend = null,
|
|
2407
2410
|
onCompareWithPrevious
|
|
2408
2411
|
}) {
|
|
@@ -2419,10 +2422,6 @@ const LogEntry = reactExports.memo(function({
|
|
|
2419
2422
|
() => adapter.analyzeRequest(log.rawRequestBody),
|
|
2420
2423
|
[adapter, log.rawRequestBody]
|
|
2421
2424
|
);
|
|
2422
|
-
const responseAnalysis = reactExports.useMemo(
|
|
2423
|
-
() => adapter.analyzeResponse(log.responseText),
|
|
2424
|
-
[adapter, log.responseText]
|
|
2425
|
-
);
|
|
2426
2425
|
const strippedRequestBody = reactExports.useMemo(() => {
|
|
2427
2426
|
if (!strip || resolvedFormat !== "anthropic" || log.rawRequestBody === null) {
|
|
2428
2427
|
return null;
|
|
@@ -2555,9 +2554,9 @@ const LogEntry = reactExports.memo(function({
|
|
|
2555
2554
|
toolCount: requestAnalysis.toolCount,
|
|
2556
2555
|
expanded,
|
|
2557
2556
|
onToggle: () => setExpanded(!expanded),
|
|
2558
|
-
responseToolNames: responseAnalysis.toolNames,
|
|
2559
2557
|
cacheTrend,
|
|
2560
2558
|
slowResponseThresholdSeconds,
|
|
2559
|
+
timeDisplayFormat,
|
|
2561
2560
|
activeTab,
|
|
2562
2561
|
tabActions,
|
|
2563
2562
|
onReplay: onCompareWithPrevious === void 0 ? void 0 : () => {
|
|
@@ -2764,27 +2763,39 @@ function ThreadConnector({
|
|
|
2764
2763
|
) })
|
|
2765
2764
|
] });
|
|
2766
2765
|
}
|
|
2767
|
-
function
|
|
2768
|
-
|
|
2769
|
-
|
|
2766
|
+
function ToolTraceEventRow({ event }) {
|
|
2767
|
+
const argumentCopy = useCopyFeedback(event.argumentsText);
|
|
2768
|
+
const canCopyArguments = event.argumentsText !== null;
|
|
2769
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2770
2770
|
"div",
|
|
2771
2771
|
{
|
|
2772
|
-
className: "flex min-w-0 items-center gap-2 rounded-md border border-
|
|
2772
|
+
className: "group/tool-trace flex min-w-0 items-center gap-2 rounded-md border border-border/70 bg-muted/20 px-2.5 py-1.5 text-xs",
|
|
2773
2773
|
children: [
|
|
2774
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 shrink-0 text-
|
|
2775
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-
|
|
2776
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono text-muted-foreground", children: [
|
|
2777
|
-
"#",
|
|
2778
|
-
event.logId
|
|
2779
|
-
] }),
|
|
2774
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 shrink-0 text-sky-400/70" }),
|
|
2775
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-foreground/80", children: event.name }),
|
|
2780
2776
|
event.argumentsPreview !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
2781
2777
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3 shrink-0 text-muted-foreground/60" }),
|
|
2782
2778
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate font-mono text-muted-foreground", children: event.argumentsPreview })
|
|
2783
|
-
] })
|
|
2779
|
+
] }),
|
|
2780
|
+
canCopyArguments && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2781
|
+
"button",
|
|
2782
|
+
{
|
|
2783
|
+
type: "button",
|
|
2784
|
+
className: "ml-auto inline-flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground opacity-0 transition-opacity hover:bg-background/80 hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-hover/tool-trace:opacity-100 group-focus-within/tool-trace:opacity-100",
|
|
2785
|
+
onClick: argumentCopy.copy,
|
|
2786
|
+
"aria-label": argumentCopy.copied ? "Copied tool arguments" : "Copy tool arguments",
|
|
2787
|
+
title: argumentCopy.copied ? "Copied tool arguments" : "Copy tool arguments",
|
|
2788
|
+
children: argumentCopy.copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
|
|
2789
|
+
}
|
|
2790
|
+
)
|
|
2784
2791
|
]
|
|
2785
2792
|
},
|
|
2786
2793
|
event.id
|
|
2787
|
-
)
|
|
2794
|
+
);
|
|
2795
|
+
}
|
|
2796
|
+
function ToolTraceEvents({ events }) {
|
|
2797
|
+
if (events.length === 0) return null;
|
|
2798
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-3 mb-2 grid gap-1.5", children: events.map((event) => /* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEventRow, { event }, event.id)) });
|
|
2788
2799
|
}
|
|
2789
2800
|
const PREVIEW_LIMIT = 180;
|
|
2790
2801
|
function shouldRenderConversationContent(standalone, expanded) {
|
|
@@ -2844,6 +2855,12 @@ function previewValue(value) {
|
|
|
2844
2855
|
if (normalized.length === 0) return null;
|
|
2845
2856
|
return normalized.length > PREVIEW_LIMIT ? `${normalized.slice(0, PREVIEW_LIMIT - 1)}...` : normalized;
|
|
2846
2857
|
}
|
|
2858
|
+
function copyValue(value) {
|
|
2859
|
+
if (value === void 0 || value === null) return null;
|
|
2860
|
+
if (typeof value === "string") return value.length > 0 ? value : null;
|
|
2861
|
+
const raw = JSON.stringify(value, null, 2);
|
|
2862
|
+
return raw === void 0 || raw.length === 0 ? null : raw;
|
|
2863
|
+
}
|
|
2847
2864
|
function extractAnthropicToolTraceEvents(log) {
|
|
2848
2865
|
const parsed = parseJsonResponse(log.responseText);
|
|
2849
2866
|
const content = safeGetOwnProperty(parsed, "content");
|
|
@@ -2854,13 +2871,15 @@ function extractAnthropicToolTraceEvents(log) {
|
|
|
2854
2871
|
if (type !== "tool_use") continue;
|
|
2855
2872
|
const name = safeGetOwnProperty(block, "name");
|
|
2856
2873
|
if (typeof name !== "string" || name.length === 0) continue;
|
|
2874
|
+
const input = safeGetOwnProperty(block, "input");
|
|
2857
2875
|
events.push({
|
|
2858
2876
|
id: `${String(log.id)}-anthropic-tool-${String(events.length)}`,
|
|
2859
2877
|
logId: log.id,
|
|
2860
2878
|
index: events.length,
|
|
2861
2879
|
provider: "anthropic",
|
|
2862
2880
|
name,
|
|
2863
|
-
|
|
2881
|
+
argumentsText: copyValue(input),
|
|
2882
|
+
argumentsPreview: previewValue(input)
|
|
2864
2883
|
});
|
|
2865
2884
|
}
|
|
2866
2885
|
return events;
|
|
@@ -2878,13 +2897,15 @@ function extractOpenAIToolTraceEvents(log) {
|
|
|
2878
2897
|
const fn = safeGetOwnProperty(call, "function");
|
|
2879
2898
|
const name = safeGetOwnProperty(fn, "name");
|
|
2880
2899
|
if (typeof name !== "string" || name.length === 0) continue;
|
|
2900
|
+
const args = safeGetOwnProperty(fn, "arguments");
|
|
2881
2901
|
events.push({
|
|
2882
2902
|
id: `${String(log.id)}-openai-tool-${String(events.length)}`,
|
|
2883
2903
|
logId: log.id,
|
|
2884
2904
|
index: events.length,
|
|
2885
2905
|
provider: "openai",
|
|
2886
2906
|
name,
|
|
2887
|
-
|
|
2907
|
+
argumentsText: copyValue(args),
|
|
2908
|
+
argumentsPreview: previewValue(args)
|
|
2888
2909
|
});
|
|
2889
2910
|
}
|
|
2890
2911
|
}
|
|
@@ -2964,7 +2985,8 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
2964
2985
|
cacheTrends,
|
|
2965
2986
|
onCompareWithPrevious,
|
|
2966
2987
|
comparisonPredecessors,
|
|
2967
|
-
turnIndex = 0
|
|
2988
|
+
turnIndex = 0,
|
|
2989
|
+
timeDisplayFormat
|
|
2968
2990
|
}) {
|
|
2969
2991
|
const lastIdx = entries.length - 1;
|
|
2970
2992
|
const lastStop = entries[lastIdx]?.stopReason ?? null;
|
|
@@ -3046,17 +3068,27 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3046
3068
|
window.cancelAnimationFrame(raf);
|
|
3047
3069
|
};
|
|
3048
3070
|
}, []);
|
|
3071
|
+
const firstLogId = entries[0]?.log.id ?? turnIndex;
|
|
3072
|
+
const turnLabel = `Turn ${String(turnIndex + 1)}`;
|
|
3049
3073
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3050
3074
|
"div",
|
|
3051
3075
|
{
|
|
3052
3076
|
ref: containerRef,
|
|
3053
|
-
|
|
3077
|
+
tabIndex: collapsed ? void 0 : 0,
|
|
3078
|
+
role: collapsed ? void 0 : "group",
|
|
3079
|
+
"aria-label": collapsed ? void 0 : turnLabel,
|
|
3080
|
+
"data-nav-id": collapsed ? void 0 : `turn-${String(firstLogId)}`,
|
|
3081
|
+
className: cn(
|
|
3082
|
+
"border rounded-lg",
|
|
3083
|
+
isPending ? "border-amber-500/10" : "border-transparent",
|
|
3084
|
+
!collapsed && "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
|
|
3085
|
+
),
|
|
3054
3086
|
children: collapsed ? (
|
|
3055
3087
|
/* ---- Collapsed: dual-crab (+ summary card for multi-log turns) ---- */
|
|
3056
3088
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3057
3089
|
"div",
|
|
3058
3090
|
{
|
|
3059
|
-
"data-nav-id": `turn-collapsed-${
|
|
3091
|
+
"data-nav-id": `turn-collapsed-${String(firstLogId)}`,
|
|
3060
3092
|
"data-nav-action": "expand",
|
|
3061
3093
|
role: "button",
|
|
3062
3094
|
tabIndex: 0,
|
|
@@ -3219,6 +3251,7 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3219
3251
|
viewMode,
|
|
3220
3252
|
strip,
|
|
3221
3253
|
slowResponseThresholdSeconds,
|
|
3254
|
+
timeDisplayFormat,
|
|
3222
3255
|
cacheTrend: cacheTrends?.get(log.id) ?? null,
|
|
3223
3256
|
onCompareWithPrevious: comparisonPredecessors.has(log.id) ? onCompareWithPrevious : void 0
|
|
3224
3257
|
}
|
|
@@ -3234,55 +3267,458 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
|
|
|
3234
3267
|
const CandidateResponseSchema = object({
|
|
3235
3268
|
candidates: array(KnowledgeCandidateSchema)
|
|
3236
3269
|
});
|
|
3270
|
+
const CandidatePromotionResponseSchema = object({
|
|
3271
|
+
candidate: KnowledgeCandidateSchema
|
|
3272
|
+
});
|
|
3273
|
+
const CandidatePromotionFailureResponseSchema = object({
|
|
3274
|
+
error: string().optional(),
|
|
3275
|
+
candidate: KnowledgeCandidateSchema.optional()
|
|
3276
|
+
});
|
|
3277
|
+
const CandidateUpdateResponseSchema = object({
|
|
3278
|
+
candidate: KnowledgeCandidateSchema
|
|
3279
|
+
});
|
|
3280
|
+
const CANDIDATE_STATUS_CLASSES = {
|
|
3281
|
+
draft: "border-amber-500/30 bg-amber-500/10 text-amber-500",
|
|
3282
|
+
promoted: "border-emerald-500/30 bg-emerald-500/10 text-emerald-500",
|
|
3283
|
+
failed: "border-destructive/30 bg-destructive/10 text-destructive"
|
|
3284
|
+
};
|
|
3237
3285
|
function formatElapsed(ms) {
|
|
3238
3286
|
if (ms === null) return "-";
|
|
3239
3287
|
if (ms < 1e3) return `${String(ms)}ms`;
|
|
3240
3288
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
3241
3289
|
}
|
|
3242
|
-
function formatTimeRange$1(startedAt, endedAt) {
|
|
3290
|
+
function formatTimeRange$1(startedAt, endedAt, timeDisplayFormat) {
|
|
3243
3291
|
if (startedAt === null || endedAt === null) return null;
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3292
|
+
return formatTimestampRange(startedAt, endedAt, timeDisplayFormat);
|
|
3293
|
+
}
|
|
3294
|
+
function formatCandidateCount(count) {
|
|
3295
|
+
return `${String(count)} candidate${count === 1 ? "" : "s"}`;
|
|
3296
|
+
}
|
|
3297
|
+
function getLogAnchor(logId) {
|
|
3298
|
+
return `log-${String(logId)}`;
|
|
3250
3299
|
}
|
|
3251
|
-
function
|
|
3252
|
-
const
|
|
3300
|
+
function jumpToLog(logId) {
|
|
3301
|
+
const anchor = getLogAnchor(logId);
|
|
3302
|
+
const target = document.getElementById(anchor);
|
|
3303
|
+
window.history.replaceState(null, "", `#${anchor}`);
|
|
3253
3304
|
if (!(target instanceof HTMLElement)) return;
|
|
3254
3305
|
target.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
3255
3306
|
target.focus({ preventScroll: true });
|
|
3256
|
-
|
|
3257
|
-
|
|
3307
|
+
}
|
|
3308
|
+
function firstFailureLog(logs) {
|
|
3309
|
+
for (const log of logs) {
|
|
3310
|
+
if (log.responseStatus !== null && log.responseStatus >= 400) return log;
|
|
3258
3311
|
}
|
|
3312
|
+
return null;
|
|
3259
3313
|
}
|
|
3260
|
-
function
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3314
|
+
function slowestLog(logs) {
|
|
3315
|
+
let current = null;
|
|
3316
|
+
let currentElapsed = -1;
|
|
3317
|
+
for (const log of logs) {
|
|
3318
|
+
if (log.elapsedMs === null) continue;
|
|
3319
|
+
if (log.elapsedMs > currentElapsed) {
|
|
3320
|
+
current = log;
|
|
3321
|
+
currentElapsed = log.elapsedMs;
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
return current;
|
|
3325
|
+
}
|
|
3326
|
+
function firstToolLog(logs) {
|
|
3327
|
+
for (const log of logs) {
|
|
3328
|
+
if (extractToolTraceEvents(log).length > 0) return log;
|
|
3329
|
+
}
|
|
3330
|
+
return null;
|
|
3331
|
+
}
|
|
3332
|
+
function buildTraceInsights(input) {
|
|
3333
|
+
const insights = [
|
|
3334
|
+
{
|
|
3335
|
+
kind: "session",
|
|
3336
|
+
title: `${String(input.summary.llmCallCount)} LLM call${input.summary.llmCallCount === 1 ? "" : "s"}`,
|
|
3337
|
+
detail: `Scope ${input.scopeId}`,
|
|
3338
|
+
logId: input.logs[0]?.id ?? null
|
|
3339
|
+
}
|
|
3340
|
+
];
|
|
3341
|
+
const toolLog = firstToolLog(input.logs);
|
|
3342
|
+
if (input.summary.toolCallCount > 0 && toolLog !== null) {
|
|
3343
|
+
insights.push({
|
|
3344
|
+
kind: "tool",
|
|
3345
|
+
title: `${String(input.summary.toolCallCount)} tool call${input.summary.toolCallCount === 1 ? "" : "s"}`,
|
|
3346
|
+
detail: `First tool evidence at #${String(toolLog.id)}`,
|
|
3347
|
+
logId: toolLog.id
|
|
3348
|
+
});
|
|
3349
|
+
}
|
|
3350
|
+
const failure = firstFailureLog(input.logs);
|
|
3351
|
+
if (failure !== null) {
|
|
3352
|
+
insights.push({
|
|
3353
|
+
kind: "failure",
|
|
3354
|
+
title: `Failure #${String(failure.responseStatus)}`,
|
|
3355
|
+
detail: `First failed request is #${String(failure.id)}`,
|
|
3356
|
+
logId: failure.id
|
|
3357
|
+
});
|
|
3358
|
+
}
|
|
3359
|
+
const slowest = slowestLog(input.logs);
|
|
3360
|
+
if (slowest !== null && input.summary.maxElapsedMs !== null) {
|
|
3361
|
+
insights.push({
|
|
3362
|
+
kind: "slow",
|
|
3363
|
+
title: `Slowest ${formatElapsed(input.summary.maxElapsedMs)}`,
|
|
3364
|
+
detail: `Max latency observed at #${String(slowest.id)}`,
|
|
3365
|
+
logId: slowest.id
|
|
3366
|
+
});
|
|
3367
|
+
}
|
|
3368
|
+
if (input.candidates.length > 0) {
|
|
3369
|
+
const draftCount = input.candidates.filter((candidate) => candidate.status === "draft").length;
|
|
3370
|
+
const promotedCount = input.candidates.filter(
|
|
3371
|
+
(candidate) => candidate.status === "promoted"
|
|
3372
|
+
).length;
|
|
3373
|
+
insights.push({
|
|
3374
|
+
kind: "candidate",
|
|
3375
|
+
title: `${String(input.candidates.length)} memory candidate${input.candidates.length === 1 ? "" : "s"}`,
|
|
3376
|
+
detail: `${String(draftCount)} draft / ${String(promotedCount)} promoted`,
|
|
3377
|
+
logId: input.candidates[0]?.logIds[0] ?? null
|
|
3378
|
+
});
|
|
3379
|
+
}
|
|
3380
|
+
return insights;
|
|
3381
|
+
}
|
|
3382
|
+
function insightIcon(kind) {
|
|
3383
|
+
switch (kind) {
|
|
3384
|
+
case "session":
|
|
3385
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3.5 text-blue-400" });
|
|
3386
|
+
case "tool":
|
|
3387
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-sky-400/70" });
|
|
3388
|
+
case "slow":
|
|
3389
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3.5 text-amber-400" });
|
|
3390
|
+
case "failure":
|
|
3391
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3.5 text-destructive" });
|
|
3392
|
+
case "candidate":
|
|
3393
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(Brain, { className: "size-3.5 text-emerald-400" });
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
function TraceInsights({ insights }) {
|
|
3397
|
+
if (insights.length === 0) return null;
|
|
3398
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1.5 border-t border-border/70 pt-2 md:grid-cols-2 xl:grid-cols-3", children: insights.map((insight) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3399
|
+
"button",
|
|
3264
3400
|
{
|
|
3265
|
-
|
|
3401
|
+
type: "button",
|
|
3402
|
+
className: cn(
|
|
3403
|
+
"flex min-w-0 items-center gap-2 rounded-md px-2 py-1.5 text-left text-xs",
|
|
3404
|
+
"text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground",
|
|
3405
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
3406
|
+
),
|
|
3407
|
+
onClick: () => {
|
|
3408
|
+
if (insight.logId !== null) jumpToLog(insight.logId);
|
|
3409
|
+
},
|
|
3410
|
+
disabled: insight.logId === null,
|
|
3266
3411
|
children: [
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "
|
|
3270
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "
|
|
3412
|
+
insightIcon(insight.kind),
|
|
3413
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0", children: [
|
|
3414
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate font-medium text-foreground/90", children: insight.title }),
|
|
3415
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate font-mono text-[10px]", children: insight.detail })
|
|
3416
|
+
] })
|
|
3417
|
+
]
|
|
3418
|
+
},
|
|
3419
|
+
`${insight.kind}-${insight.title}`
|
|
3420
|
+
)) });
|
|
3421
|
+
}
|
|
3422
|
+
function candidateStatusLabel(status) {
|
|
3423
|
+
switch (status) {
|
|
3424
|
+
case "draft":
|
|
3425
|
+
return "Draft";
|
|
3426
|
+
case "promoted":
|
|
3427
|
+
return "Promoted";
|
|
3428
|
+
case "failed":
|
|
3429
|
+
return "Failed";
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
function candidatePromoteLabel(candidate, promoting) {
|
|
3433
|
+
if (promoting) return "Promoting";
|
|
3434
|
+
switch (candidate.status) {
|
|
3435
|
+
case "draft":
|
|
3436
|
+
return "Promote";
|
|
3437
|
+
case "failed":
|
|
3438
|
+
return "Retry";
|
|
3439
|
+
case "promoted":
|
|
3440
|
+
return "Promoted";
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
function redactionLabel(candidate) {
|
|
3444
|
+
if (!candidate.redaction.redacted) return "No sensitive pattern matched";
|
|
3445
|
+
return `Redacted ${candidate.redaction.patterns.join(", ")}`;
|
|
3446
|
+
}
|
|
3447
|
+
function previewText(value, maxLength) {
|
|
3448
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
3449
|
+
if (normalized.length <= maxLength) return normalized;
|
|
3450
|
+
return `${normalized.slice(0, maxLength - 3)}...`;
|
|
3451
|
+
}
|
|
3452
|
+
function logRangeLabel(logIds) {
|
|
3453
|
+
const first = logIds[0];
|
|
3454
|
+
const last = logIds[logIds.length - 1];
|
|
3455
|
+
if (first === void 0) return "No evidence logs";
|
|
3456
|
+
if (last === void 0 || first === last) return `#${String(first)}`;
|
|
3457
|
+
return `#${String(first)}-#${String(last)}`;
|
|
3458
|
+
}
|
|
3459
|
+
function tagsToText(tags) {
|
|
3460
|
+
return tags.join(", ");
|
|
3461
|
+
}
|
|
3462
|
+
function textToTags(value) {
|
|
3463
|
+
return value.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0);
|
|
3464
|
+
}
|
|
3465
|
+
function CandidateItem({
|
|
3466
|
+
candidate,
|
|
3467
|
+
isPromoting,
|
|
3468
|
+
isUpdating,
|
|
3469
|
+
onPromoteCandidate,
|
|
3470
|
+
onUpdateCandidate
|
|
3471
|
+
}) {
|
|
3472
|
+
const [editing, setEditing] = reactExports.useState(false);
|
|
3473
|
+
const [draftType, setDraftType] = reactExports.useState(candidate.type);
|
|
3474
|
+
const [draftTitle, setDraftTitle] = reactExports.useState(candidate.title);
|
|
3475
|
+
const [draftContent, setDraftContent] = reactExports.useState(candidate.content);
|
|
3476
|
+
const [draftTags, setDraftTags] = reactExports.useState(tagsToText(candidate.tags));
|
|
3477
|
+
const [localError, setLocalError] = reactExports.useState(null);
|
|
3478
|
+
const canPromote = candidate.status !== "promoted";
|
|
3479
|
+
const canEdit = candidate.status !== "promoted";
|
|
3480
|
+
const resetDraft = reactExports.useCallback(() => {
|
|
3481
|
+
setDraftType(candidate.type);
|
|
3482
|
+
setDraftTitle(candidate.title);
|
|
3483
|
+
setDraftContent(candidate.content);
|
|
3484
|
+
setDraftTags(tagsToText(candidate.tags));
|
|
3485
|
+
setLocalError(null);
|
|
3486
|
+
}, [candidate.content, candidate.tags, candidate.title, candidate.type]);
|
|
3487
|
+
const saveDraft = reactExports.useCallback(async () => {
|
|
3488
|
+
const title = draftTitle.trim();
|
|
3489
|
+
const content = draftContent.trim();
|
|
3490
|
+
const tags = textToTags(draftTags);
|
|
3491
|
+
if (title.length === 0) {
|
|
3492
|
+
setLocalError("Title is required.");
|
|
3493
|
+
return;
|
|
3494
|
+
}
|
|
3495
|
+
if (content.length === 0) {
|
|
3496
|
+
setLocalError("Content is required.");
|
|
3497
|
+
return;
|
|
3498
|
+
}
|
|
3499
|
+
if (tags.length === 0) {
|
|
3500
|
+
setLocalError("At least one tag is required.");
|
|
3501
|
+
return;
|
|
3502
|
+
}
|
|
3503
|
+
setLocalError(null);
|
|
3504
|
+
const saved = await onUpdateCandidate(candidate.id, {
|
|
3505
|
+
type: draftType,
|
|
3506
|
+
title,
|
|
3507
|
+
content,
|
|
3508
|
+
tags
|
|
3509
|
+
});
|
|
3510
|
+
if (saved) setEditing(false);
|
|
3511
|
+
}, [candidate.id, draftContent, draftTags, draftTitle, draftType, onUpdateCandidate]);
|
|
3512
|
+
if (editing) {
|
|
3513
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md border border-border/80 bg-background/60 px-2.5 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-2", children: [
|
|
3514
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
3515
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3516
|
+
"select",
|
|
3517
|
+
{
|
|
3518
|
+
value: draftType,
|
|
3519
|
+
disabled: isUpdating,
|
|
3520
|
+
onChange: (event) => {
|
|
3521
|
+
const parsed = KnowledgeCandidateSchema.shape.type.safeParse(
|
|
3522
|
+
event.currentTarget.value
|
|
3523
|
+
);
|
|
3524
|
+
if (parsed.success) setDraftType(parsed.data);
|
|
3525
|
+
},
|
|
3526
|
+
className: "h-7 rounded-md border border-input bg-background px-2 text-xs",
|
|
3527
|
+
"aria-label": "Candidate type",
|
|
3528
|
+
children: [
|
|
3529
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "episode", children: "episode" }),
|
|
3530
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "procedure", children: "procedure" }),
|
|
3531
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "preference", children: "preference" }),
|
|
3532
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "project-fact", children: "project-fact" })
|
|
3533
|
+
]
|
|
3534
|
+
}
|
|
3535
|
+
),
|
|
3536
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3537
|
+
"input",
|
|
3538
|
+
{
|
|
3539
|
+
value: draftTitle,
|
|
3540
|
+
disabled: isUpdating,
|
|
3541
|
+
onChange: (event) => setDraftTitle(event.currentTarget.value),
|
|
3542
|
+
className: "h-7 min-w-[220px] flex-1 rounded-md border border-input bg-background px-2 text-xs",
|
|
3543
|
+
"aria-label": "Candidate title"
|
|
3544
|
+
}
|
|
3545
|
+
)
|
|
3546
|
+
] }),
|
|
3547
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3548
|
+
"textarea",
|
|
3549
|
+
{
|
|
3550
|
+
value: draftContent,
|
|
3551
|
+
disabled: isUpdating,
|
|
3552
|
+
onChange: (event) => setDraftContent(event.currentTarget.value),
|
|
3553
|
+
className: "min-h-28 rounded-md border border-input bg-background px-2 py-1.5 font-mono text-[11px] leading-relaxed",
|
|
3554
|
+
"aria-label": "Candidate content"
|
|
3555
|
+
}
|
|
3556
|
+
),
|
|
3557
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3558
|
+
"input",
|
|
3559
|
+
{
|
|
3560
|
+
value: draftTags,
|
|
3561
|
+
disabled: isUpdating,
|
|
3562
|
+
onChange: (event) => setDraftTags(event.currentTarget.value),
|
|
3563
|
+
className: "h-7 rounded-md border border-input bg-background px-2 text-xs",
|
|
3564
|
+
"aria-label": "Candidate tags"
|
|
3565
|
+
}
|
|
3566
|
+
),
|
|
3567
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
3568
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-[10px] text-muted-foreground", children: [
|
|
3569
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "size-3" }),
|
|
3570
|
+
"Saving reruns Inspector redaction before promotion."
|
|
3271
3571
|
] }),
|
|
3272
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("
|
|
3273
|
-
|
|
3572
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
|
|
3573
|
+
localError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-destructive", children: localError }),
|
|
3574
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3575
|
+
Button,
|
|
3274
3576
|
{
|
|
3275
3577
|
type: "button",
|
|
3276
|
-
|
|
3277
|
-
|
|
3578
|
+
variant: "ghost",
|
|
3579
|
+
size: "sm",
|
|
3580
|
+
className: "h-7 gap-1.5 px-2 text-xs",
|
|
3581
|
+
disabled: isUpdating,
|
|
3582
|
+
onClick: () => {
|
|
3583
|
+
resetDraft();
|
|
3584
|
+
setEditing(false);
|
|
3585
|
+
},
|
|
3586
|
+
children: [
|
|
3587
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3.5" }),
|
|
3588
|
+
"Cancel"
|
|
3589
|
+
]
|
|
3590
|
+
}
|
|
3591
|
+
),
|
|
3592
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3593
|
+
Button,
|
|
3594
|
+
{
|
|
3595
|
+
type: "button",
|
|
3596
|
+
variant: "outline",
|
|
3597
|
+
size: "sm",
|
|
3598
|
+
className: "h-7 gap-1.5 px-2 text-xs",
|
|
3599
|
+
disabled: isUpdating,
|
|
3600
|
+
onClick: () => {
|
|
3601
|
+
void saveDraft();
|
|
3602
|
+
},
|
|
3278
3603
|
children: [
|
|
3279
|
-
"
|
|
3280
|
-
|
|
3604
|
+
isUpdating ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Save, { className: "size-3.5" }),
|
|
3605
|
+
"Save"
|
|
3281
3606
|
]
|
|
3607
|
+
}
|
|
3608
|
+
)
|
|
3609
|
+
] })
|
|
3610
|
+
] }) });
|
|
3611
|
+
}
|
|
3612
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border/80 bg-background/60 px-2.5 py-2", children: [
|
|
3613
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
3614
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-5 px-1.5 text-[10px] font-mono", children: candidate.type }),
|
|
3615
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", title: candidate.title, children: candidate.title }),
|
|
3616
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3617
|
+
Badge,
|
|
3618
|
+
{
|
|
3619
|
+
variant: "outline",
|
|
3620
|
+
className: cn(
|
|
3621
|
+
"h-5 shrink-0 px-1.5 text-[10px] font-mono",
|
|
3622
|
+
CANDIDATE_STATUS_CLASSES[candidate.status]
|
|
3623
|
+
),
|
|
3624
|
+
children: candidateStatusLabel(candidate.status)
|
|
3625
|
+
}
|
|
3626
|
+
)
|
|
3627
|
+
] }),
|
|
3628
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3629
|
+
"p",
|
|
3630
|
+
{
|
|
3631
|
+
className: "mt-1 text-[11px] leading-relaxed text-muted-foreground",
|
|
3632
|
+
title: candidate.content,
|
|
3633
|
+
children: previewText(candidate.content, 360)
|
|
3634
|
+
}
|
|
3635
|
+
),
|
|
3636
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10px] text-muted-foreground", children: [
|
|
3637
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
3638
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(FileSearch, { className: "size-3" }),
|
|
3639
|
+
logRangeLabel(candidate.logIds)
|
|
3640
|
+
] }),
|
|
3641
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
3642
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ShieldCheck, { className: "size-3" }),
|
|
3643
|
+
redactionLabel(candidate)
|
|
3644
|
+
] }),
|
|
3645
|
+
candidate.status === "promoted" && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-emerald-500", children: [
|
|
3646
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "size-3" }),
|
|
3647
|
+
candidate.openClawMemoryId ?? "OpenClaw"
|
|
3648
|
+
] }),
|
|
3649
|
+
candidate.status === "failed" && candidate.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 text-destructive", children: [
|
|
3650
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CircleX, { className: "size-3" }),
|
|
3651
|
+
candidate.error
|
|
3652
|
+
] })
|
|
3653
|
+
] }),
|
|
3654
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-1.5", children: [
|
|
3655
|
+
candidate.logIds.map((logId) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3656
|
+
"a",
|
|
3657
|
+
{
|
|
3658
|
+
href: `#${getLogAnchor(logId)}`,
|
|
3659
|
+
onClick: (event) => {
|
|
3660
|
+
event.preventDefault();
|
|
3661
|
+
jumpToLog(logId);
|
|
3282
3662
|
},
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3663
|
+
className: "rounded border border-blue-400/25 px-1.5 py-0.5 font-mono text-[10px] text-blue-400 underline-offset-2 transition-colors hover:bg-blue-400/10 hover:text-blue-300 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
3664
|
+
"aria-label": `Jump to evidence log ${String(logId)}`,
|
|
3665
|
+
children: [
|
|
3666
|
+
"#",
|
|
3667
|
+
logId
|
|
3668
|
+
]
|
|
3669
|
+
},
|
|
3670
|
+
logId
|
|
3671
|
+
)),
|
|
3672
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
|
|
3673
|
+
canEdit && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3674
|
+
Button,
|
|
3675
|
+
{
|
|
3676
|
+
type: "button",
|
|
3677
|
+
variant: "ghost",
|
|
3678
|
+
size: "sm",
|
|
3679
|
+
className: "h-6 gap-1.5 px-2 text-[10px]",
|
|
3680
|
+
disabled: isUpdating || isPromoting,
|
|
3681
|
+
onClick: () => setEditing(true),
|
|
3682
|
+
children: [
|
|
3683
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
|
|
3684
|
+
"Review"
|
|
3685
|
+
]
|
|
3686
|
+
}
|
|
3687
|
+
),
|
|
3688
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3689
|
+
Button,
|
|
3690
|
+
{
|
|
3691
|
+
type: "button",
|
|
3692
|
+
variant: canPromote ? "outline" : "ghost",
|
|
3693
|
+
size: "sm",
|
|
3694
|
+
className: "h-6 gap-1.5 px-2 text-[10px]",
|
|
3695
|
+
onClick: () => onPromoteCandidate(candidate.id),
|
|
3696
|
+
disabled: !canPromote || isPromoting || isUpdating,
|
|
3697
|
+
children: [
|
|
3698
|
+
isPromoting ? /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3 animate-spin" }) : canPromote ? /* @__PURE__ */ jsxRuntimeExports.jsx(CloudUpload, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheck, { className: "size-3" }),
|
|
3699
|
+
candidatePromoteLabel(candidate, isPromoting)
|
|
3700
|
+
]
|
|
3701
|
+
}
|
|
3702
|
+
)
|
|
3703
|
+
] })
|
|
3704
|
+
] });
|
|
3705
|
+
}
|
|
3706
|
+
function CandidateList({
|
|
3707
|
+
candidates,
|
|
3708
|
+
promotingCandidateIds,
|
|
3709
|
+
updatingCandidateIds,
|
|
3710
|
+
onPromoteCandidate,
|
|
3711
|
+
onUpdateCandidate
|
|
3712
|
+
}) {
|
|
3713
|
+
if (candidates.length === 0) return null;
|
|
3714
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1.5", children: candidates.map((candidate) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3715
|
+
CandidateItem,
|
|
3716
|
+
{
|
|
3717
|
+
candidate,
|
|
3718
|
+
isPromoting: promotingCandidateIds.has(candidate.id),
|
|
3719
|
+
isUpdating: updatingCandidateIds.has(candidate.id),
|
|
3720
|
+
onPromoteCandidate,
|
|
3721
|
+
onUpdateCandidate
|
|
3286
3722
|
},
|
|
3287
3723
|
candidate.id
|
|
3288
3724
|
)) });
|
|
@@ -3290,20 +3726,35 @@ function CandidateList({ candidates }) {
|
|
|
3290
3726
|
function AgentTraceSummary({
|
|
3291
3727
|
logs,
|
|
3292
3728
|
scopeId,
|
|
3293
|
-
slowResponseThresholdSeconds
|
|
3729
|
+
slowResponseThresholdSeconds,
|
|
3730
|
+
showRollupMetrics,
|
|
3731
|
+
timeDisplayFormat
|
|
3294
3732
|
}) {
|
|
3295
3733
|
const [candidates, setCandidates] = reactExports.useState([]);
|
|
3734
|
+
const [candidatesExpanded, setCandidatesExpanded] = reactExports.useState(true);
|
|
3296
3735
|
const [candidateState, setCandidateState] = reactExports.useState({
|
|
3297
3736
|
status: "idle",
|
|
3298
3737
|
error: null
|
|
3299
3738
|
});
|
|
3739
|
+
const [promotingCandidateIds, setPromotingCandidateIds] = reactExports.useState(
|
|
3740
|
+
() => /* @__PURE__ */ new Set()
|
|
3741
|
+
);
|
|
3742
|
+
const [updatingCandidateIds, setUpdatingCandidateIds] = reactExports.useState(
|
|
3743
|
+
() => /* @__PURE__ */ new Set()
|
|
3744
|
+
);
|
|
3745
|
+
const hasCandidates = candidates.length > 0;
|
|
3300
3746
|
const summary = reactExports.useMemo(
|
|
3301
3747
|
() => buildTraceSummary(logs, slowResponseThresholdSeconds, candidates.length),
|
|
3302
3748
|
[candidates.length, logs, slowResponseThresholdSeconds]
|
|
3303
3749
|
);
|
|
3750
|
+
const traceInsights = reactExports.useMemo(
|
|
3751
|
+
() => buildTraceInsights({ logs, scopeId, summary, candidates }),
|
|
3752
|
+
[candidates, logs, scopeId, summary]
|
|
3753
|
+
);
|
|
3754
|
+
const showElapsedSummary = showRollupMetrics || summary.maxElapsedMs !== null;
|
|
3304
3755
|
const timeRange = reactExports.useMemo(
|
|
3305
|
-
() => formatTimeRange$1(summary.startedAt, summary.endedAt),
|
|
3306
|
-
[summary.endedAt, summary.startedAt]
|
|
3756
|
+
() => formatTimeRange$1(summary.startedAt, summary.endedAt, timeDisplayFormat),
|
|
3757
|
+
[summary.endedAt, summary.startedAt, timeDisplayFormat]
|
|
3307
3758
|
);
|
|
3308
3759
|
const createCandidates = reactExports.useCallback(() => {
|
|
3309
3760
|
if (logs.length === 0 || candidateState.status === "loading") return;
|
|
@@ -3324,7 +3775,10 @@ function AgentTraceSummary({
|
|
|
3324
3775
|
}
|
|
3325
3776
|
const parsed = await parseJsonResponse$1(response, CandidateResponseSchema);
|
|
3326
3777
|
setCandidates(parsed.candidates);
|
|
3327
|
-
|
|
3778
|
+
setCandidatesExpanded(parsed.candidates.length > 0);
|
|
3779
|
+
setCandidateState(
|
|
3780
|
+
parsed.candidates.length > 0 ? { status: "ready", error: null } : { status: "failed", error: "No candidate was generated for this trace scope." }
|
|
3781
|
+
);
|
|
3328
3782
|
} catch (error) {
|
|
3329
3783
|
setCandidateState({
|
|
3330
3784
|
status: "failed",
|
|
@@ -3333,20 +3787,119 @@ function AgentTraceSummary({
|
|
|
3333
3787
|
}
|
|
3334
3788
|
})();
|
|
3335
3789
|
}, [candidateState.status, logs.length, scopeId]);
|
|
3790
|
+
const promoteCandidate = reactExports.useCallback((candidateId) => {
|
|
3791
|
+
setPromotingCandidateIds((current) => {
|
|
3792
|
+
if (current.has(candidateId)) return current;
|
|
3793
|
+
const next = new Set(current);
|
|
3794
|
+
next.add(candidateId);
|
|
3795
|
+
return next;
|
|
3796
|
+
});
|
|
3797
|
+
void (async () => {
|
|
3798
|
+
try {
|
|
3799
|
+
const response = await fetch(
|
|
3800
|
+
`/api/knowledge/candidates/${encodeURIComponent(candidateId)}/promote`,
|
|
3801
|
+
{ method: "POST" }
|
|
3802
|
+
);
|
|
3803
|
+
const raw = await response.json().catch(() => null);
|
|
3804
|
+
const parsed = CandidatePromotionResponseSchema.safeParse(raw);
|
|
3805
|
+
if (response.ok && parsed.success) {
|
|
3806
|
+
setCandidates(
|
|
3807
|
+
(current) => current.map(
|
|
3808
|
+
(candidate) => candidate.id === parsed.data.candidate.id ? parsed.data.candidate : candidate
|
|
3809
|
+
)
|
|
3810
|
+
);
|
|
3811
|
+
setCandidateState({ status: "ready", error: null });
|
|
3812
|
+
return;
|
|
3813
|
+
}
|
|
3814
|
+
const failure = CandidatePromotionFailureResponseSchema.safeParse(raw);
|
|
3815
|
+
if (failure.success && failure.data.candidate !== void 0) {
|
|
3816
|
+
setCandidates(
|
|
3817
|
+
(current) => current.map(
|
|
3818
|
+
(candidate) => candidate.id === failure.data.candidate?.id ? failure.data.candidate : candidate
|
|
3819
|
+
)
|
|
3820
|
+
);
|
|
3821
|
+
}
|
|
3822
|
+
setCandidateState({
|
|
3823
|
+
status: "failed",
|
|
3824
|
+
error: failure.success ? failure.data.error ?? "Candidate promotion failed" : `Candidate promotion failed with ${String(response.status)}`
|
|
3825
|
+
});
|
|
3826
|
+
} catch (error) {
|
|
3827
|
+
setCandidateState({
|
|
3828
|
+
status: "failed",
|
|
3829
|
+
error: error instanceof Error ? error.message : "Candidate promotion failed"
|
|
3830
|
+
});
|
|
3831
|
+
} finally {
|
|
3832
|
+
setPromotingCandidateIds((current) => {
|
|
3833
|
+
const next = new Set(current);
|
|
3834
|
+
next.delete(candidateId);
|
|
3835
|
+
return next;
|
|
3836
|
+
});
|
|
3837
|
+
}
|
|
3838
|
+
})();
|
|
3839
|
+
}, []);
|
|
3840
|
+
const updateCandidate = reactExports.useCallback(
|
|
3841
|
+
async (candidateId, update) => {
|
|
3842
|
+
setUpdatingCandidateIds((current) => {
|
|
3843
|
+
if (current.has(candidateId)) return current;
|
|
3844
|
+
const next = new Set(current);
|
|
3845
|
+
next.add(candidateId);
|
|
3846
|
+
return next;
|
|
3847
|
+
});
|
|
3848
|
+
try {
|
|
3849
|
+
const response = await fetch(
|
|
3850
|
+
`/api/knowledge/candidates/${encodeURIComponent(candidateId)}`,
|
|
3851
|
+
{
|
|
3852
|
+
method: "PATCH",
|
|
3853
|
+
headers: { "Content-Type": "application/json" },
|
|
3854
|
+
body: JSON.stringify(update)
|
|
3855
|
+
}
|
|
3856
|
+
);
|
|
3857
|
+
if (!response.ok) {
|
|
3858
|
+
const message = await readApiError(
|
|
3859
|
+
response,
|
|
3860
|
+
`Candidate update failed with ${String(response.status)}`
|
|
3861
|
+
);
|
|
3862
|
+
setCandidateState({ status: "failed", error: message });
|
|
3863
|
+
return false;
|
|
3864
|
+
}
|
|
3865
|
+
const parsed = await parseJsonResponse$1(response, CandidateUpdateResponseSchema);
|
|
3866
|
+
setCandidates(
|
|
3867
|
+
(current) => current.map(
|
|
3868
|
+
(candidate) => candidate.id === parsed.candidate.id ? parsed.candidate : candidate
|
|
3869
|
+
)
|
|
3870
|
+
);
|
|
3871
|
+
setCandidateState({ status: "ready", error: null });
|
|
3872
|
+
return true;
|
|
3873
|
+
} catch (error) {
|
|
3874
|
+
setCandidateState({
|
|
3875
|
+
status: "failed",
|
|
3876
|
+
error: error instanceof Error ? error.message : "Candidate update failed"
|
|
3877
|
+
});
|
|
3878
|
+
return false;
|
|
3879
|
+
} finally {
|
|
3880
|
+
setUpdatingCandidateIds((current) => {
|
|
3881
|
+
const next = new Set(current);
|
|
3882
|
+
next.delete(candidateId);
|
|
3883
|
+
return next;
|
|
3884
|
+
});
|
|
3885
|
+
}
|
|
3886
|
+
},
|
|
3887
|
+
[]
|
|
3888
|
+
);
|
|
3336
3889
|
if (logs.length === 0) return null;
|
|
3337
3890
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "mb-2 rounded-lg border border-border bg-muted/10 px-3 py-2", children: [
|
|
3338
3891
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2 text-xs", children: [
|
|
3339
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 font-semibold text-foreground", children: [
|
|
3892
|
+
showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 font-semibold text-foreground", children: [
|
|
3340
3893
|
/* @__PURE__ */ jsxRuntimeExports.jsx(MessageSquare, { className: "size-3.5 text-blue-400" }),
|
|
3341
3894
|
summary.llmCallCount,
|
|
3342
3895
|
" LLM"
|
|
3343
3896
|
] }),
|
|
3344
3897
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
|
|
3345
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-
|
|
3898
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3.5 text-sky-400/70" }),
|
|
3346
3899
|
summary.toolCallCount,
|
|
3347
3900
|
" tools"
|
|
3348
3901
|
] }),
|
|
3349
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
|
|
3902
|
+
showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
|
|
3350
3903
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-emerald-400" }),
|
|
3351
3904
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
|
|
3352
3905
|
formatTokens(summary.totalInputTokens),
|
|
@@ -3357,21 +3910,21 @@ function AgentTraceSummary({
|
|
|
3357
3910
|
(summary.totalCacheCreationInputTokens > 0 || summary.totalCacheReadInputTokens > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
|
|
3358
3911
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3.5 text-purple-400" }),
|
|
3359
3912
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
|
|
3360
|
-
"+",
|
|
3913
|
+
"KV Cache +",
|
|
3361
3914
|
formatTokens(summary.totalCacheCreationInputTokens),
|
|
3362
3915
|
" / ~",
|
|
3363
3916
|
formatTokens(summary.totalCacheReadInputTokens)
|
|
3364
3917
|
] })
|
|
3365
3918
|
] }),
|
|
3366
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
|
|
3919
|
+
showElapsedSummary && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1.5 text-muted-foreground", children: [
|
|
3367
3920
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3.5" }),
|
|
3368
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: formatElapsed(summary.totalElapsedMs) }),
|
|
3921
|
+
showRollupMetrics && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: formatElapsed(summary.totalElapsedMs) }),
|
|
3369
3922
|
summary.maxElapsedMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono text-muted-foreground/70", children: [
|
|
3370
3923
|
"max ",
|
|
3371
3924
|
formatElapsed(summary.maxElapsedMs)
|
|
3372
3925
|
] })
|
|
3373
3926
|
] }),
|
|
3374
|
-
timeRange !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-muted-foreground/70", children: timeRange }),
|
|
3927
|
+
showRollupMetrics && timeRange !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-muted-foreground/70", children: timeRange }),
|
|
3375
3928
|
(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: [
|
|
3376
3929
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3.5" }),
|
|
3377
3930
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
|
|
@@ -3385,10 +3938,7 @@ function AgentTraceSummary({
|
|
|
3385
3938
|
] })
|
|
3386
3939
|
] }),
|
|
3387
3940
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1" }),
|
|
3388
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
3389
|
-
summary.knowledgeCandidateCount,
|
|
3390
|
-
" memory"
|
|
3391
|
-
] }),
|
|
3941
|
+
hasCandidates && /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "outline", className: "h-6 px-2 text-[10px] font-mono", children: formatCandidateCount(summary.knowledgeCandidateCount) }),
|
|
3392
3942
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
3393
3943
|
Button,
|
|
3394
3944
|
{
|
|
@@ -3403,10 +3953,33 @@ function AgentTraceSummary({
|
|
|
3403
3953
|
"Candidate"
|
|
3404
3954
|
]
|
|
3405
3955
|
}
|
|
3956
|
+
),
|
|
3957
|
+
hasCandidates && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3958
|
+
Button,
|
|
3959
|
+
{
|
|
3960
|
+
type: "button",
|
|
3961
|
+
variant: "ghost",
|
|
3962
|
+
size: "icon",
|
|
3963
|
+
className: "size-7 text-muted-foreground",
|
|
3964
|
+
onClick: () => setCandidatesExpanded((value) => !value),
|
|
3965
|
+
"aria-expanded": candidatesExpanded,
|
|
3966
|
+
"aria-label": candidatesExpanded ? "Collapse memory candidates" : "Expand memory candidates",
|
|
3967
|
+
children: candidatesExpanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3.5" })
|
|
3968
|
+
}
|
|
3406
3969
|
)
|
|
3407
3970
|
] }),
|
|
3408
3971
|
candidateState.status === "failed" && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-2 text-xs text-destructive", children: candidateState.error }),
|
|
3409
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3972
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TraceInsights, { insights: traceInsights }),
|
|
3973
|
+
candidatesExpanded && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3974
|
+
CandidateList,
|
|
3975
|
+
{
|
|
3976
|
+
candidates,
|
|
3977
|
+
promotingCandidateIds,
|
|
3978
|
+
updatingCandidateIds,
|
|
3979
|
+
onPromoteCandidate: promoteCandidate,
|
|
3980
|
+
onUpdateCandidate: updateCandidate
|
|
3981
|
+
}
|
|
3982
|
+
)
|
|
3410
3983
|
] });
|
|
3411
3984
|
}
|
|
3412
3985
|
function computeStats(logs) {
|
|
@@ -3427,7 +4000,9 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
3427
4000
|
onCompareWithPrevious,
|
|
3428
4001
|
comparisonPredecessors,
|
|
3429
4002
|
onClearGroup,
|
|
3430
|
-
standalone = false
|
|
4003
|
+
standalone = false,
|
|
4004
|
+
hasPinnedSessionContext = false,
|
|
4005
|
+
timeDisplayFormat
|
|
3431
4006
|
}) {
|
|
3432
4007
|
const [expanded, setExpanded] = reactExports.useState(false);
|
|
3433
4008
|
const stats = reactExports.useMemo(() => computeStats(group.logs), [group.logs]);
|
|
@@ -3435,6 +4010,7 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
3435
4010
|
const endTime = group.logs[group.logs.length - 1]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
3436
4011
|
const mixed = hasMixedApiFormat(group.logs);
|
|
3437
4012
|
const isLoading = group.logs.some((log) => log.responseStatus === null);
|
|
4013
|
+
const showTraceRollupMetrics = standalone && !hasPinnedSessionContext;
|
|
3438
4014
|
const turnGroups = reactExports.useMemo(() => buildTurnGroups(group.logs), [group.logs]);
|
|
3439
4015
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2", children: [
|
|
3440
4016
|
!standalone && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -3452,6 +4028,7 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
3452
4028
|
hideApiFormat: mixed,
|
|
3453
4029
|
isLoading,
|
|
3454
4030
|
userAgent: group.logs[0]?.userAgent ?? null,
|
|
4031
|
+
timeDisplayFormat,
|
|
3455
4032
|
onClear: () => onClearGroup(group.logs.map((l) => l.id))
|
|
3456
4033
|
}
|
|
3457
4034
|
),
|
|
@@ -3461,7 +4038,9 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
3461
4038
|
{
|
|
3462
4039
|
logs: group.logs,
|
|
3463
4040
|
scopeId: group.conversationId,
|
|
3464
|
-
slowResponseThresholdSeconds
|
|
4041
|
+
slowResponseThresholdSeconds,
|
|
4042
|
+
showRollupMetrics: showTraceRollupMetrics,
|
|
4043
|
+
timeDisplayFormat
|
|
3465
4044
|
}
|
|
3466
4045
|
),
|
|
3467
4046
|
turnGroups.map((tg) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -3471,6 +4050,7 @@ const ConversationGroup = reactExports.memo(function({
|
|
|
3471
4050
|
viewMode,
|
|
3472
4051
|
strip,
|
|
3473
4052
|
slowResponseThresholdSeconds,
|
|
4053
|
+
timeDisplayFormat,
|
|
3474
4054
|
cacheTrends,
|
|
3475
4055
|
onCompareWithPrevious,
|
|
3476
4056
|
comparisonPredecessors,
|
|
@@ -4051,26 +4631,18 @@ function TestStatus({ result }) {
|
|
|
4051
4631
|
}
|
|
4052
4632
|
if (result.cacheCreationInputTokens !== void 0 && result.cacheCreationInputTokens > 0) {
|
|
4053
4633
|
tokenParts.push(
|
|
4054
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
" cache"
|
|
4059
|
-
] }) }),
|
|
4060
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Tokens cached for reuse, reducing future API cost" })
|
|
4061
|
-
] }) }, "cache-create")
|
|
4634
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
|
|
4635
|
+
"KV Cache +",
|
|
4636
|
+
result.cacheCreationInputTokens
|
|
4637
|
+
] }, "cache-create")
|
|
4062
4638
|
);
|
|
4063
4639
|
}
|
|
4064
4640
|
if (result.cacheReadInputTokens !== void 0 && result.cacheReadInputTokens > 0) {
|
|
4065
4641
|
tokenParts.push(
|
|
4066
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
" cached"
|
|
4071
|
-
] }) }),
|
|
4072
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Tokens served from cache, reducing API cost" })
|
|
4073
|
-
] }) }, "cache-read")
|
|
4642
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
|
|
4643
|
+
"KV Cache ~",
|
|
4644
|
+
result.cacheReadInputTokens
|
|
4645
|
+
] }, "cache-read")
|
|
4074
4646
|
);
|
|
4075
4647
|
}
|
|
4076
4648
|
const displayTokens = [];
|
|
@@ -5307,7 +5879,8 @@ function SettingsDialog() {
|
|
|
5307
5879
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "flex-1 overflow-hidden", children: [
|
|
5308
5880
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(TabsList, { children: [
|
|
5309
5881
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "providers", children: "Providers" }),
|
|
5310
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "proxy", children: "Proxy" })
|
|
5882
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "proxy", children: "Proxy" }),
|
|
5883
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "onboarding", children: "Onboarding" })
|
|
5311
5884
|
] }),
|
|
5312
5885
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 overflow-y-auto flex-1 pr-3", children: [
|
|
5313
5886
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "providers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -5326,19 +5899,98 @@ function SettingsDialog() {
|
|
|
5326
5899
|
onTestingTimeLeftChange: handleTestingTimeLeftChange
|
|
5327
5900
|
}
|
|
5328
5901
|
) }),
|
|
5329
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "proxy", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProxySettingsTab, {}) })
|
|
5902
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "proxy", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProxySettingsTab, {}) }),
|
|
5903
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "onboarding", children: /* @__PURE__ */ jsxRuntimeExports.jsx(OnboardingSettingsTab, {}) })
|
|
5330
5904
|
] })
|
|
5331
5905
|
] })
|
|
5332
5906
|
] })
|
|
5333
5907
|
] });
|
|
5334
5908
|
}
|
|
5909
|
+
function CopyableSetupValue({
|
|
5910
|
+
id,
|
|
5911
|
+
label,
|
|
5912
|
+
value,
|
|
5913
|
+
copiedId,
|
|
5914
|
+
onCopy
|
|
5915
|
+
}) {
|
|
5916
|
+
const copied = copiedId === id;
|
|
5917
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border bg-muted/20 px-3 py-2", children: [
|
|
5918
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-1 text-xs font-medium text-muted-foreground", children: label }),
|
|
5919
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
5920
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "min-w-0 flex-1 truncate font-mono text-xs text-foreground", children: value }),
|
|
5921
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
5922
|
+
Button,
|
|
5923
|
+
{
|
|
5924
|
+
type: "button",
|
|
5925
|
+
variant: "ghost",
|
|
5926
|
+
size: "icon",
|
|
5927
|
+
className: "size-7 shrink-0",
|
|
5928
|
+
onClick: () => onCopy(id, value),
|
|
5929
|
+
"aria-label": copied ? `Copied ${label}` : `Copy ${label}`,
|
|
5930
|
+
children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-500" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
|
|
5931
|
+
}
|
|
5932
|
+
)
|
|
5933
|
+
] })
|
|
5934
|
+
] });
|
|
5935
|
+
}
|
|
5936
|
+
function OnboardingSettingsTab() {
|
|
5937
|
+
const [copiedId, setCopiedId] = reactExports.useState(null);
|
|
5938
|
+
const origin = reactExports.useMemo(() => {
|
|
5939
|
+
if (typeof window === "undefined") return "http://localhost:25947";
|
|
5940
|
+
return window.location.origin;
|
|
5941
|
+
}, []);
|
|
5942
|
+
const values = reactExports.useMemo(
|
|
5943
|
+
() => [
|
|
5944
|
+
{ id: "skill", label: "Codex skill", value: "agent-inspector onboard --force" },
|
|
5945
|
+
{ id: "mcp", label: "MCP URL", value: `${origin}/api/mcp` },
|
|
5946
|
+
{ id: "proxy", label: "Proxy URL", value: `${origin}/proxy` },
|
|
5947
|
+
{ id: "anthropic", label: "Anthropic base", value: `ANTHROPIC_BASE_URL=${origin}/proxy` }
|
|
5948
|
+
],
|
|
5949
|
+
[origin]
|
|
5950
|
+
);
|
|
5951
|
+
const handleCopy = reactExports.useCallback((id, value) => {
|
|
5952
|
+
void window.navigator.clipboard.writeText(value).then(() => {
|
|
5953
|
+
setCopiedId(id);
|
|
5954
|
+
setTimeout(() => setCopiedId(null), 1600);
|
|
5955
|
+
});
|
|
5956
|
+
}, []);
|
|
5957
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
5958
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5959
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Terminal, { className: "size-4 text-muted-foreground" }),
|
|
5960
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "Agent onboarding" })
|
|
5961
|
+
] }),
|
|
5962
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid gap-2", children: values.map((item) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
5963
|
+
CopyableSetupValue,
|
|
5964
|
+
{
|
|
5965
|
+
id: item.id,
|
|
5966
|
+
label: item.label,
|
|
5967
|
+
value: item.value,
|
|
5968
|
+
copiedId,
|
|
5969
|
+
onCopy: handleCopy
|
|
5970
|
+
},
|
|
5971
|
+
item.id
|
|
5972
|
+
)) }),
|
|
5973
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-2 rounded-md border border-border bg-background px-3 py-2 text-xs text-muted-foreground", children: [
|
|
5974
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5975
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-500" }),
|
|
5976
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Provider test creates a traceable memory probe session." })
|
|
5977
|
+
] }),
|
|
5978
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
5979
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-500" }),
|
|
5980
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Captured sessions can produce reviewable memory candidates." })
|
|
5981
|
+
] })
|
|
5982
|
+
] })
|
|
5983
|
+
] });
|
|
5984
|
+
}
|
|
5335
5985
|
function ProxySettingsTab() {
|
|
5336
5986
|
const {
|
|
5337
5987
|
strip,
|
|
5338
5988
|
slowResponseThresholdSeconds,
|
|
5989
|
+
timeDisplayFormat,
|
|
5339
5990
|
isLoading,
|
|
5340
5991
|
setStrip,
|
|
5341
|
-
setSlowResponseThresholdSeconds
|
|
5992
|
+
setSlowResponseThresholdSeconds,
|
|
5993
|
+
setTimeDisplayFormat
|
|
5342
5994
|
} = useStripConfig();
|
|
5343
5995
|
const [error, setError] = reactExports.useState(null);
|
|
5344
5996
|
const [pending, setPending] = reactExports.useState(false);
|
|
@@ -5370,6 +6022,20 @@ function ProxySettingsTab() {
|
|
|
5370
6022
|
},
|
|
5371
6023
|
[setSlowResponseThresholdSeconds]
|
|
5372
6024
|
);
|
|
6025
|
+
const handleTimeDisplayFormatChange = reactExports.useCallback(
|
|
6026
|
+
async (next) => {
|
|
6027
|
+
setError(null);
|
|
6028
|
+
setPending(true);
|
|
6029
|
+
try {
|
|
6030
|
+
await setTimeDisplayFormat(next);
|
|
6031
|
+
} catch (err) {
|
|
6032
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
6033
|
+
} finally {
|
|
6034
|
+
setPending(false);
|
|
6035
|
+
}
|
|
6036
|
+
},
|
|
6037
|
+
[setTimeDisplayFormat]
|
|
6038
|
+
);
|
|
5373
6039
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
5374
6040
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
5375
6041
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-semibold", children: "Claude Code billing header" }),
|
|
@@ -5430,6 +6096,28 @@ function ProxySettingsTab() {
|
|
|
5430
6096
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: "seconds" })
|
|
5431
6097
|
] })
|
|
5432
6098
|
] }),
|
|
6099
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
|
|
6100
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "time-display-format", className: "text-sm font-semibold", children: "Time display" }),
|
|
6101
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Controls timestamps in session summaries, conversation headers, and log rows." }),
|
|
6102
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6103
|
+
"select",
|
|
6104
|
+
{
|
|
6105
|
+
id: "time-display-format",
|
|
6106
|
+
value: timeDisplayFormat,
|
|
6107
|
+
disabled: isLoading || pending,
|
|
6108
|
+
onChange: (event) => {
|
|
6109
|
+
const parsed = TimeDisplayFormatSchema.safeParse(event.currentTarget.value);
|
|
6110
|
+
if (!parsed.success) return;
|
|
6111
|
+
void handleTimeDisplayFormatChange(parsed.data);
|
|
6112
|
+
},
|
|
6113
|
+
className: "h-8 rounded-md border border-input bg-background px-2 text-sm disabled:cursor-not-allowed disabled:opacity-50",
|
|
6114
|
+
children: [
|
|
6115
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "time", children: "Time only" }),
|
|
6116
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: "full", children: "Full ISO" })
|
|
6117
|
+
]
|
|
6118
|
+
}
|
|
6119
|
+
)
|
|
6120
|
+
] }),
|
|
5433
6121
|
error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs text-destructive", children: [
|
|
5434
6122
|
"Failed to save: ",
|
|
5435
6123
|
error
|
|
@@ -5491,20 +6179,40 @@ function safeItemAt(items, index) {
|
|
|
5491
6179
|
}
|
|
5492
6180
|
function isEditableTarget(target) {
|
|
5493
6181
|
const tag = target.tagName;
|
|
5494
|
-
if (tag === "INPUT" || tag === "TEXTAREA") return true;
|
|
6182
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
5495
6183
|
if (target.isContentEditable) return true;
|
|
5496
6184
|
return false;
|
|
5497
6185
|
}
|
|
5498
|
-
function
|
|
6186
|
+
function isInteractiveTarget(target) {
|
|
6187
|
+
const tag = target.tagName;
|
|
6188
|
+
if (tag === "BUTTON" || tag === "A") return true;
|
|
6189
|
+
if (isEditableTarget(target)) return true;
|
|
6190
|
+
return target.closest("[role='button'],[role='menuitem'],[role='option']") !== null;
|
|
6191
|
+
}
|
|
6192
|
+
function isInOpenOverlay(target) {
|
|
6193
|
+
if (target.closest("[role='dialog'],[role='menu'],[role='listbox']") !== null) return true;
|
|
6194
|
+
return document.querySelector("[role='dialog'],[role='menu'],[role='listbox']") !== null;
|
|
6195
|
+
}
|
|
6196
|
+
function hasActiveTextSelection() {
|
|
6197
|
+
const selection = window.getSelection();
|
|
6198
|
+
if (selection === null) return false;
|
|
6199
|
+
return selection.type === "Range" && selection.toString().length > 0;
|
|
6200
|
+
}
|
|
6201
|
+
function isLetterNavigationKey(event, key) {
|
|
6202
|
+
return event.key === key && event.shiftKey;
|
|
6203
|
+
}
|
|
6204
|
+
function useKeyboardNavigation(containerRef, wrapperRef, options = {}) {
|
|
5499
6205
|
const rootRef = wrapperRef ?? containerRef;
|
|
6206
|
+
const pageWide = options.pageWide === true;
|
|
5500
6207
|
const handleFocusContainer = reactExports.useCallback(
|
|
5501
6208
|
(e) => {
|
|
5502
6209
|
const container = containerRef.current;
|
|
6210
|
+
const root = rootRef.current;
|
|
5503
6211
|
if (!container) return;
|
|
6212
|
+
if (!root) return;
|
|
5504
6213
|
const target = e.target;
|
|
5505
6214
|
if (!isFocusTarget(target)) return;
|
|
5506
|
-
if (target !==
|
|
5507
|
-
if (!container.contains(target)) return;
|
|
6215
|
+
if (target !== root) return;
|
|
5508
6216
|
const items = findNavItems(container);
|
|
5509
6217
|
const first = safeItemAt(items, 0);
|
|
5510
6218
|
if (first !== null) focusAndScroll(first);
|
|
@@ -5512,14 +6220,22 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
|
|
|
5512
6220
|
[containerRef, rootRef]
|
|
5513
6221
|
);
|
|
5514
6222
|
reactExports.useEffect(() => {
|
|
5515
|
-
const root = rootRef.current;
|
|
5516
|
-
const container = containerRef.current;
|
|
5517
|
-
if (!root || !container) return;
|
|
5518
6223
|
const handleKeyDown = (e) => {
|
|
6224
|
+
if (e.altKey || e.ctrlKey || e.metaKey) return;
|
|
6225
|
+
const container = containerRef.current;
|
|
6226
|
+
if (!container) return;
|
|
5519
6227
|
const target = e.target;
|
|
5520
6228
|
if (!isFocusTarget(target)) return;
|
|
5521
|
-
if (!container.contains(target)) return;
|
|
5522
6229
|
if (isEditableTarget(target)) return;
|
|
6230
|
+
if (hasActiveTextSelection()) return;
|
|
6231
|
+
const isInsideContainer = container.contains(target);
|
|
6232
|
+
if (isInsideContainer && isInteractiveTarget(target) && !target.hasAttribute(NAV_ATTR)) {
|
|
6233
|
+
return;
|
|
6234
|
+
}
|
|
6235
|
+
if (!isInsideContainer) {
|
|
6236
|
+
if (!pageWide) return;
|
|
6237
|
+
if (isInteractiveTarget(target) || isInOpenOverlay(target)) return;
|
|
6238
|
+
}
|
|
5523
6239
|
const items = findNavItems(container);
|
|
5524
6240
|
if (items.length === 0) return;
|
|
5525
6241
|
const current = findClosestNavItem(target, container);
|
|
@@ -5528,7 +6244,8 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
|
|
|
5528
6244
|
switch (e.key) {
|
|
5529
6245
|
case "ArrowUp":
|
|
5530
6246
|
case "W": {
|
|
5531
|
-
if (e.key === "
|
|
6247
|
+
if (e.shiftKey && e.key === "ArrowUp") break;
|
|
6248
|
+
if (e.key === "W" && !isLetterNavigationKey(e, "W")) break;
|
|
5532
6249
|
e.preventDefault();
|
|
5533
6250
|
const prevIdx = currentIdx > 0 ? currentIdx - 1 : currentIdx === -1 ? items.length - 1 : -1;
|
|
5534
6251
|
if (prevIdx !== -1) {
|
|
@@ -5540,7 +6257,8 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
|
|
|
5540
6257
|
}
|
|
5541
6258
|
case "ArrowDown":
|
|
5542
6259
|
case "S": {
|
|
5543
|
-
if (e.key === "
|
|
6260
|
+
if (e.shiftKey && e.key === "ArrowDown") break;
|
|
6261
|
+
if (e.key === "S" && !isLetterNavigationKey(e, "S")) break;
|
|
5544
6262
|
e.preventDefault();
|
|
5545
6263
|
const nextIdx = currentIdx < items.length - 1 ? currentIdx + 1 : currentIdx === -1 ? 0 : -1;
|
|
5546
6264
|
if (nextIdx !== -1) {
|
|
@@ -5552,7 +6270,8 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
|
|
|
5552
6270
|
}
|
|
5553
6271
|
case "ArrowLeft":
|
|
5554
6272
|
case "A": {
|
|
5555
|
-
if (e.key === "
|
|
6273
|
+
if (e.shiftKey && e.key === "ArrowLeft") break;
|
|
6274
|
+
if (e.key === "A" && !isLetterNavigationKey(e, "A")) break;
|
|
5556
6275
|
if (current === null) break;
|
|
5557
6276
|
e.preventDefault();
|
|
5558
6277
|
const action = getAction(current);
|
|
@@ -5567,7 +6286,8 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
|
|
|
5567
6286
|
}
|
|
5568
6287
|
case "ArrowRight":
|
|
5569
6288
|
case "D": {
|
|
5570
|
-
if (e.key === "
|
|
6289
|
+
if (e.shiftKey && e.key === "ArrowRight") break;
|
|
6290
|
+
if (e.key === "D" && !isLetterNavigationKey(e, "D")) break;
|
|
5571
6291
|
if (current === null) break;
|
|
5572
6292
|
e.preventDefault();
|
|
5573
6293
|
const action = getAction(current);
|
|
@@ -5595,13 +6315,11 @@ function useKeyboardNavigation(containerRef, wrapperRef) {
|
|
|
5595
6315
|
};
|
|
5596
6316
|
document.addEventListener("keydown", handleKeyDown, { capture: true });
|
|
5597
6317
|
return () => document.removeEventListener("keydown", handleKeyDown, { capture: true });
|
|
5598
|
-
}, [containerRef, rootRef]);
|
|
6318
|
+
}, [containerRef, pageWide, rootRef]);
|
|
5599
6319
|
reactExports.useEffect(() => {
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
return () => root.removeEventListener("focus", handleFocusContainer);
|
|
5604
|
-
}, [handleFocusContainer, rootRef]);
|
|
6320
|
+
document.addEventListener("focusin", handleFocusContainer);
|
|
6321
|
+
return () => document.removeEventListener("focusin", handleFocusContainer);
|
|
6322
|
+
}, [handleFocusContainer]);
|
|
5605
6323
|
}
|
|
5606
6324
|
function truncateSessionId(id) {
|
|
5607
6325
|
if (id.length <= 30) return id;
|
|
@@ -5616,16 +6334,11 @@ function computeTokenSummary(logs) {
|
|
|
5616
6334
|
}
|
|
5617
6335
|
return { totalIn, totalOut };
|
|
5618
6336
|
}
|
|
5619
|
-
function formatTimeRange(logs) {
|
|
6337
|
+
function formatTimeRange(logs, timeDisplayFormat) {
|
|
5620
6338
|
const first = logs[0];
|
|
5621
6339
|
const last = logs[logs.length - 1];
|
|
5622
6340
|
if (first === void 0 || last === void 0) return null;
|
|
5623
|
-
|
|
5624
|
-
hour: "2-digit",
|
|
5625
|
-
minute: "2-digit",
|
|
5626
|
-
second: "2-digit"
|
|
5627
|
-
});
|
|
5628
|
-
return `${format(first.timestamp)} - ${format(last.timestamp)}`;
|
|
6341
|
+
return formatTimestampRange(first.timestamp, last.timestamp, timeDisplayFormat);
|
|
5629
6342
|
}
|
|
5630
6343
|
function getFirstUserAgent(logs) {
|
|
5631
6344
|
for (const log of logs) {
|
|
@@ -5700,10 +6413,14 @@ function SessionContextBar({
|
|
|
5700
6413
|
sessionId,
|
|
5701
6414
|
logs,
|
|
5702
6415
|
totalIn,
|
|
5703
|
-
totalOut
|
|
6416
|
+
totalOut,
|
|
6417
|
+
timeDisplayFormat
|
|
5704
6418
|
}) {
|
|
5705
6419
|
const [copied, setCopied] = reactExports.useState(false);
|
|
5706
|
-
const timeRange = reactExports.useMemo(
|
|
6420
|
+
const timeRange = reactExports.useMemo(
|
|
6421
|
+
() => formatTimeRange(logs, timeDisplayFormat),
|
|
6422
|
+
[logs, timeDisplayFormat]
|
|
6423
|
+
);
|
|
5707
6424
|
const userAgent = reactExports.useMemo(() => getFirstUserAgent(logs), [logs]);
|
|
5708
6425
|
const handleCopyLink = reactExports.useCallback(() => {
|
|
5709
6426
|
void window.navigator.clipboard.writeText(window.location.href).then(() => {
|
|
@@ -5776,6 +6493,7 @@ function ProxyViewer({
|
|
|
5776
6493
|
onViewModeChange,
|
|
5777
6494
|
strip,
|
|
5778
6495
|
slowResponseThresholdSeconds,
|
|
6496
|
+
timeDisplayFormat,
|
|
5779
6497
|
hideSessionFilter = false,
|
|
5780
6498
|
pinnedSessionId
|
|
5781
6499
|
}) {
|
|
@@ -5787,7 +6505,7 @@ function ProxyViewer({
|
|
|
5787
6505
|
);
|
|
5788
6506
|
const logListRef = reactExports.useRef(null);
|
|
5789
6507
|
const logListWrapperRef = reactExports.useRef(null);
|
|
5790
|
-
useKeyboardNavigation(logListRef, logListWrapperRef);
|
|
6508
|
+
useKeyboardNavigation(logListRef, logListWrapperRef, { pageWide: true });
|
|
5791
6509
|
reactExports.useEffect(() => {
|
|
5792
6510
|
const perCrabDuration = 400;
|
|
5793
6511
|
const startDelay = 50;
|
|
@@ -5822,6 +6540,7 @@ function ProxyViewer({
|
|
|
5822
6540
|
setComparePair(null);
|
|
5823
6541
|
}, []);
|
|
5824
6542
|
const groups = reactExports.useMemo(() => groupLogsByConversation(logs), [logs]);
|
|
6543
|
+
const hasPinnedSessionContext = pinnedSessionId !== void 0;
|
|
5825
6544
|
const cacheTrends = reactExports.useMemo(() => computeCacheTrends(groups), [groups]);
|
|
5826
6545
|
const comparisonPredecessors = reactExports.useMemo(() => buildValidPredecessors(groups), [groups]);
|
|
5827
6546
|
const handleCompareWithPrevious = reactExports.useCallback(
|
|
@@ -5895,7 +6614,8 @@ function ProxyViewer({
|
|
|
5895
6614
|
sessionId: pinnedSessionId,
|
|
5896
6615
|
logs,
|
|
5897
6616
|
totalIn,
|
|
5898
|
-
totalOut
|
|
6617
|
+
totalOut,
|
|
6618
|
+
timeDisplayFormat
|
|
5899
6619
|
}
|
|
5900
6620
|
),
|
|
5901
6621
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
|
|
@@ -5934,7 +6654,7 @@ function ProxyViewer({
|
|
|
5934
6654
|
)
|
|
5935
6655
|
] }),
|
|
5936
6656
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1" }),
|
|
5937
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
|
|
6657
|
+
!hasPinnedSessionContext && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
|
|
5938
6658
|
logs.length,
|
|
5939
6659
|
" request",
|
|
5940
6660
|
logs.length !== 1 ? "s" : "",
|
|
@@ -6005,7 +6725,9 @@ function ProxyViewer({
|
|
|
6005
6725
|
onCompareWithPrevious: handleCompareWithPrevious,
|
|
6006
6726
|
comparisonPredecessors,
|
|
6007
6727
|
onClearGroup,
|
|
6008
|
-
standalone: groups.length === 1
|
|
6728
|
+
standalone: groups.length === 1,
|
|
6729
|
+
hasPinnedSessionContext,
|
|
6730
|
+
timeDisplayFormat
|
|
6009
6731
|
},
|
|
6010
6732
|
group.id
|
|
6011
6733
|
)) })
|
|
@@ -6263,7 +6985,7 @@ function ProxyViewerContainer({
|
|
|
6263
6985
|
}
|
|
6264
6986
|
})();
|
|
6265
6987
|
}, []);
|
|
6266
|
-
const { strip, slowResponseThresholdSeconds } = useStripConfig();
|
|
6988
|
+
const { strip, slowResponseThresholdSeconds, timeDisplayFormat } = useStripConfig();
|
|
6267
6989
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
6268
6990
|
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 }),
|
|
6269
6991
|
/* @__PURE__ */ jsxRuntimeExports.jsx(OnboardingBanner, {}),
|
|
@@ -6283,6 +7005,7 @@ function ProxyViewerContainer({
|
|
|
6283
7005
|
onViewModeChange: setViewMode,
|
|
6284
7006
|
strip,
|
|
6285
7007
|
slowResponseThresholdSeconds,
|
|
7008
|
+
timeDisplayFormat,
|
|
6286
7009
|
hideSessionFilter: initialSessionId !== void 0,
|
|
6287
7010
|
pinnedSessionId: initialSessionId
|
|
6288
7011
|
}
|
|
@@ -6312,5 +7035,6 @@ export {
|
|
|
6312
7035
|
getStatusCategory as p,
|
|
6313
7036
|
parseJsonText as q,
|
|
6314
7037
|
resolveLogFormat as r,
|
|
6315
|
-
safeJsonValue as s
|
|
7038
|
+
safeJsonValue as s,
|
|
7039
|
+
useProviders as u
|
|
6316
7040
|
};
|