@tonyclaw/agent-inspector 3.0.8 → 3.0.10
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-Bth_Ywyt.js → CompareDrawer-BxLP7jmX.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-D_2ez29e.js +106 -0
- package/.output/public/assets/{ReplayDialog-C2YYAuTe.js → ReplayDialog-Btm-wONI.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-8k9dAX2j.js → RequestAnatomy-C9eZJN9E.js} +1 -1
- package/.output/public/assets/{ResponseView-DEL6JcXm.js → ResponseView-9R2MW1kp.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-DngmoR9j.js → StreamingChunkSequence-zxqGpFfv.js} +1 -1
- package/.output/public/assets/{_sessionId-4vk9RMZ5.js → _sessionId-C0tPTQlC.js} +1 -1
- package/.output/public/assets/agent-inspector-58K1_MsC.ico +0 -0
- package/.output/public/assets/index-BIgsQvaF.css +1 -0
- package/.output/public/assets/index-DgVWrzsQ.js +1 -0
- package/.output/public/assets/{index-DINLvnm9.js → index-WiDkyjq8.js} +1 -1
- package/.output/public/assets/{json-viewer-CH5dvS2Y.js → json-viewer-BAmkfRDE.js} +1 -1
- package/.output/public/assets/{main-GWkZn4xy.js → main-2feHoEI6.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +2 -2
- package/.output/server/_libs/radix-ui__react-dialog.mjs +2 -2
- package/.output/server/{_sessionId-EgsjdkN7.mjs → _sessionId-BkoyescL.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-CaM8FYuD.mjs → CompareDrawer-irfFdkJ8.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-EJIogcGd.mjs → ProxyViewerContainer-CcJr9STz.mjs} +574 -324
- package/.output/server/_ssr/{ReplayDialog-BPpJ2-q8.mjs → ReplayDialog-CJnmXQGT.mjs} +3 -3
- package/.output/server/_ssr/{RequestAnatomy-DeQAWnZx.mjs → RequestAnatomy-CupP0Wdb.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-Dshm8iQ7.mjs → ResponseView-Bp-0t5cd.mjs} +2 -2
- package/.output/server/_ssr/{StreamingChunkSequence-32yIU2hn.mjs → StreamingChunkSequence-BXSn4kMH.mjs} +2 -2
- package/.output/server/_ssr/{index-XRcx9blU.mjs → index-KVltJBV8.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-C0aX5rqC.mjs → json-viewer-EYQ5eG2Q.mjs} +2 -2
- package/.output/server/_ssr/{router-BI-Mvxz9.mjs → router-_cAQddXC.mjs} +39 -36
- package/.output/server/{_tanstack-start-manifest_v-jKScklL_.mjs → _tanstack-start-manifest_v-BfiF5iov.mjs} +1 -1
- package/.output/server/index.mjs +72 -65
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +194 -29
- package/src/components/ProxyViewerContainer.tsx +145 -3
- package/src/components/clients/ClientLogo.tsx +16 -1
- package/src/components/providers/ProviderCard.tsx +83 -91
- package/src/components/providers/ProvidersPanel.tsx +7 -7
- package/src/components/providers/SettingsDialog.tsx +17 -22
- package/src/components/proxy-viewer/TurnGroup.tsx +0 -16
- package/src/components/proxy-viewer/log-formats/anthropic.ts +5 -5
- package/src/components/proxy-viewer/log-formats/openai.ts +7 -10
- package/src/components/proxy-viewer/requestTools.ts +5 -2
- package/src/routes/api/logs.ts +14 -5
- package/src/routes/api/providers.$providerId.test.log.ts +28 -24
- package/.output/public/assets/ProxyViewerContainer-C73EU9Jq.js +0 -106
- package/.output/public/assets/index-DBEB1rHe.css +0 -1
- package/.output/public/assets/index-LPIGiUMP.js +0 -1
|
@@ -4,8 +4,15 @@ import ClaudeCodeLogoSvg from "../../assets/IDE/claudecode-color.svg";
|
|
|
4
4
|
import CodexLogoSvg from "../../assets/IDE/codex-color.svg";
|
|
5
5
|
import OpenCodeLogoSvg from "../../assets/IDE/opencode.svg";
|
|
6
6
|
import MiMoCodeLogoSvg from "../../assets/IDE/xiaomimimo.svg";
|
|
7
|
+
import AgentInspectorLogo from "../../assets/agent-inspector.ico";
|
|
7
8
|
|
|
8
|
-
export type ClientApp =
|
|
9
|
+
export type ClientApp =
|
|
10
|
+
| "agent-inspector"
|
|
11
|
+
| "codex"
|
|
12
|
+
| "opencode"
|
|
13
|
+
| "mimo-code"
|
|
14
|
+
| "claude-code"
|
|
15
|
+
| "unknown";
|
|
9
16
|
|
|
10
17
|
export type ClientAppDetectionInput = {
|
|
11
18
|
userAgent?: string | null;
|
|
@@ -14,6 +21,7 @@ export type ClientAppDetectionInput = {
|
|
|
14
21
|
};
|
|
15
22
|
|
|
16
23
|
const CLIENT_LABELS: Record<Exclude<ClientApp, "unknown">, string> = {
|
|
24
|
+
"agent-inspector": "Inspector",
|
|
17
25
|
codex: "Codex",
|
|
18
26
|
opencode: "OpenCode",
|
|
19
27
|
"mimo-code": "MiMo Code",
|
|
@@ -38,6 +46,9 @@ export function detectClientApp(input: ClientAppDetectionInput): ClientApp {
|
|
|
38
46
|
normalize(input.clientProjectFolder),
|
|
39
47
|
].join(" ");
|
|
40
48
|
|
|
49
|
+
if (matchesAny(haystack, ["agent-inspector", "agent inspector", "provider-test"])) {
|
|
50
|
+
return "agent-inspector";
|
|
51
|
+
}
|
|
41
52
|
if (matchesAny(haystack, ["mimocode", "mimo-code", "mimo code"])) return "mimo-code";
|
|
42
53
|
if (matchesAny(haystack, ["opencode", "open-code", "open code"])) return "opencode";
|
|
43
54
|
if (matchesAny(haystack, ["claude-code", "claude code", "claudecode"])) return "claude-code";
|
|
@@ -50,6 +61,7 @@ export function detectClientApp(input: ClientAppDetectionInput): ClientApp {
|
|
|
50
61
|
|
|
51
62
|
export function clientAppLabel(client: ClientApp): string | null {
|
|
52
63
|
switch (client) {
|
|
64
|
+
case "agent-inspector":
|
|
53
65
|
case "codex":
|
|
54
66
|
case "opencode":
|
|
55
67
|
case "mimo-code":
|
|
@@ -61,6 +73,7 @@ export function clientAppLabel(client: ClientApp): string | null {
|
|
|
61
73
|
}
|
|
62
74
|
|
|
63
75
|
const CLIENT_LOGOS: Record<Exclude<ClientApp, "unknown">, string> = {
|
|
76
|
+
"agent-inspector": AgentInspectorLogo,
|
|
64
77
|
codex: CodexLogoSvg,
|
|
65
78
|
opencode: OpenCodeLogoSvg,
|
|
66
79
|
"mimo-code": MiMoCodeLogoSvg,
|
|
@@ -68,6 +81,7 @@ const CLIENT_LOGOS: Record<Exclude<ClientApp, "unknown">, string> = {
|
|
|
68
81
|
};
|
|
69
82
|
|
|
70
83
|
const CLIENT_LOGO_FRAMES: Record<Exclude<ClientApp, "unknown">, string> = {
|
|
84
|
+
"agent-inspector": "border-orange-200/25 bg-orange-300/[0.12]",
|
|
71
85
|
codex: "border-cyan-200/20 bg-cyan-300/[0.08]",
|
|
72
86
|
opencode: "border-sky-200/25 bg-sky-300/[0.12]",
|
|
73
87
|
"mimo-code": "border-orange-200/25 bg-orange-400/[0.12]",
|
|
@@ -75,6 +89,7 @@ const CLIENT_LOGO_FRAMES: Record<Exclude<ClientApp, "unknown">, string> = {
|
|
|
75
89
|
};
|
|
76
90
|
|
|
77
91
|
const CLIENT_LOGO_IMAGES: Record<Exclude<ClientApp, "unknown">, string> = {
|
|
92
|
+
"agent-inspector": "p-0.5 drop-shadow-[0_0_8px_rgba(251,146,60,0.35)]",
|
|
78
93
|
codex: "p-0.5",
|
|
79
94
|
opencode: "p-1 drop-shadow-[0_0_8px_rgba(125,211,252,0.35)]",
|
|
80
95
|
"mimo-code": "p-0.5 drop-shadow-[0_0_8px_rgba(255,105,0,0.35)]",
|
|
@@ -31,13 +31,6 @@ import {
|
|
|
31
31
|
providerHasContextMetadata,
|
|
32
32
|
} from "../../lib/providerModelMetadata";
|
|
33
33
|
import { formatContextWindowTokens } from "../../lib/utils";
|
|
34
|
-
import {
|
|
35
|
-
PATH_V1_CHAT_COMPLETIONS,
|
|
36
|
-
PATH_V1_MESSAGES,
|
|
37
|
-
PATH_V1_RESPONSES,
|
|
38
|
-
previewUpstreamUrl,
|
|
39
|
-
type UpstreamUrlDiagnostic,
|
|
40
|
-
} from "../../lib/upstreamUrl";
|
|
41
34
|
import type {
|
|
42
35
|
ProviderFormatTestResults,
|
|
43
36
|
ProviderTestErrorType as ErrorType,
|
|
@@ -225,51 +218,16 @@ function ProviderFormatTestStatus({
|
|
|
225
218
|
);
|
|
226
219
|
}
|
|
227
220
|
|
|
228
|
-
function
|
|
229
|
-
diagnostic,
|
|
230
|
-
}: {
|
|
231
|
-
diagnostic: UpstreamUrlDiagnostic;
|
|
232
|
-
}): JSX.Element {
|
|
233
|
-
return (
|
|
234
|
-
<div className={diagnostic.severity === "warning" ? "text-amber-200" : "text-muted-foreground"}>
|
|
235
|
-
{diagnostic.message}
|
|
236
|
-
</div>
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
function ProviderEndpointSummary({
|
|
221
|
+
function ProviderFormatStatusBlock({
|
|
241
222
|
label,
|
|
242
|
-
baseUrl,
|
|
243
|
-
endpoint,
|
|
244
|
-
providerName,
|
|
245
223
|
children,
|
|
246
224
|
}: {
|
|
247
225
|
label: string;
|
|
248
|
-
baseUrl: string;
|
|
249
|
-
endpoint: string;
|
|
250
|
-
providerName: string;
|
|
251
226
|
children?: ReactNode;
|
|
252
227
|
}): JSX.Element {
|
|
253
|
-
const preview = previewUpstreamUrl(baseUrl, endpoint, { providerName });
|
|
254
|
-
|
|
255
228
|
return (
|
|
256
|
-
<div className="
|
|
257
|
-
<div className="
|
|
258
|
-
<span className="font-medium text-muted-foreground">{label}</span>
|
|
259
|
-
<code className="min-w-0 break-all font-mono text-foreground/80">{baseUrl}</code>
|
|
260
|
-
<span className="text-[10px] uppercase text-muted-foreground/80">Final</span>
|
|
261
|
-
<code className="min-w-0 break-all font-mono text-foreground">{preview.finalUrl}</code>
|
|
262
|
-
</div>
|
|
263
|
-
{preview.diagnostics.length > 0 && (
|
|
264
|
-
<div className="space-y-1 text-xs">
|
|
265
|
-
{preview.diagnostics.map((diagnostic) => (
|
|
266
|
-
<EndpointDiagnosticLine
|
|
267
|
-
key={`${diagnostic.severity}:${diagnostic.message}`}
|
|
268
|
-
diagnostic={diagnostic}
|
|
269
|
-
/>
|
|
270
|
-
))}
|
|
271
|
-
</div>
|
|
272
|
-
)}
|
|
229
|
+
<div className="min-w-0 space-y-2 rounded-md border border-border/60 bg-muted/15 p-2">
|
|
230
|
+
<div className="text-xs font-medium text-foreground/80">{label}</div>
|
|
273
231
|
{children}
|
|
274
232
|
</div>
|
|
275
233
|
);
|
|
@@ -293,9 +251,38 @@ function providerTestTargets(provider: ProviderConfig): string[] {
|
|
|
293
251
|
return targets;
|
|
294
252
|
}
|
|
295
253
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
254
|
+
type CapabilityChipTone = "active" | "quiet" | "response";
|
|
255
|
+
|
|
256
|
+
function CapabilityChip({
|
|
257
|
+
label,
|
|
258
|
+
enabled,
|
|
259
|
+
tone,
|
|
260
|
+
}: {
|
|
261
|
+
label: string;
|
|
262
|
+
enabled: boolean;
|
|
263
|
+
tone?: CapabilityChipTone;
|
|
264
|
+
}): JSX.Element {
|
|
265
|
+
const resolvedTone: CapabilityChipTone = tone ?? (enabled ? "active" : "quiet");
|
|
266
|
+
const className =
|
|
267
|
+
resolvedTone === "response" && enabled
|
|
268
|
+
? "border-sky-300/30 bg-sky-300/[0.09] text-sky-100"
|
|
269
|
+
: enabled
|
|
270
|
+
? "border-border/70 bg-muted/25 text-foreground/80"
|
|
271
|
+
: "border-border/50 bg-transparent text-muted-foreground/70";
|
|
272
|
+
return (
|
|
273
|
+
<span className={`inline-flex items-center gap-1 rounded border px-1.5 py-0.5 ${className}`}>
|
|
274
|
+
{enabled ? <CheckCircle className="size-3" /> : <Minus className="size-3" />}
|
|
275
|
+
{label}
|
|
276
|
+
</span>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function ProviderCapabilitySummary({ provider }: { provider: ProviderConfig }): JSX.Element {
|
|
281
|
+
const hasAnthropic = providerHasEndpoint(provider.anthropicBaseUrl);
|
|
282
|
+
const hasChat = providerHasEndpoint(provider.openaiBaseUrl);
|
|
283
|
+
const hasResponses = providerHasEndpoint(provider.openaiResponsesBaseUrl);
|
|
284
|
+
|
|
285
|
+
if (!hasAnthropic && !hasChat && !hasResponses) {
|
|
299
286
|
return (
|
|
300
287
|
<div className="text-xs text-muted-foreground">
|
|
301
288
|
Test requires at least one configured endpoint.
|
|
@@ -305,16 +292,15 @@ function ProviderTestCoverage({ provider }: { provider: ProviderConfig }): JSX.E
|
|
|
305
292
|
|
|
306
293
|
return (
|
|
307
294
|
<div className="flex min-w-0 flex-wrap items-center gap-1.5 text-xs text-muted-foreground">
|
|
308
|
-
<span>
|
|
309
|
-
{
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
<span>non-stream + stream</span>
|
|
295
|
+
<span>Capabilities</span>
|
|
296
|
+
{hasAnthropic && <CapabilityChip label="Anthropic" enabled={true} />}
|
|
297
|
+
{hasChat && <CapabilityChip label="OpenAI Chat" enabled={true} />}
|
|
298
|
+
<CapabilityChip
|
|
299
|
+
label={hasResponses ? "OpenAI Responses" : "No OpenAI Responses"}
|
|
300
|
+
enabled={hasResponses}
|
|
301
|
+
tone="response"
|
|
302
|
+
/>
|
|
303
|
+
<span>tested non-stream + stream</span>
|
|
318
304
|
</div>
|
|
319
305
|
);
|
|
320
306
|
}
|
|
@@ -762,7 +748,7 @@ export function ProviderCard({
|
|
|
762
748
|
</div>
|
|
763
749
|
|
|
764
750
|
<div className="flex flex-col gap-2 text-xs text-muted-foreground sm:flex-row sm:items-center sm:justify-between">
|
|
765
|
-
<
|
|
751
|
+
<ProviderCapabilitySummary provider={provider} />
|
|
766
752
|
<div className="flex flex-wrap items-center gap-2">
|
|
767
753
|
<span>Key configured</span>
|
|
768
754
|
<span>{provider.authHeader === "x-api-key" ? "x-api-key" : "Bearer"}</span>
|
|
@@ -838,43 +824,49 @@ export function ProviderCard({
|
|
|
838
824
|
</div>
|
|
839
825
|
)}
|
|
840
826
|
|
|
841
|
-
{providerHasEndpoint(provider.anthropicBaseUrl)
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
827
|
+
{(providerHasEndpoint(provider.anthropicBaseUrl) ||
|
|
828
|
+
providerHasEndpoint(provider.openaiBaseUrl) ||
|
|
829
|
+
providerHasEndpoint(provider.openaiResponsesBaseUrl)) && (
|
|
830
|
+
<div className="grid gap-2 lg:grid-cols-3">
|
|
831
|
+
{providerHasEndpoint(provider.anthropicBaseUrl) && (
|
|
832
|
+
<ProviderFormatStatusBlock label="Anthropic">
|
|
833
|
+
{testResults !== undefined && (
|
|
834
|
+
<ProviderFormatTestStatus results={testResults.anthropic} />
|
|
835
|
+
)}
|
|
836
|
+
{testResults === undefined && (
|
|
837
|
+
<div className="text-xs text-muted-foreground">Not tested yet</div>
|
|
838
|
+
)}
|
|
839
|
+
</ProviderFormatStatusBlock>
|
|
850
840
|
)}
|
|
851
|
-
</ProviderEndpointSummary>
|
|
852
|
-
)}
|
|
853
841
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
842
|
+
{providerHasEndpoint(provider.openaiBaseUrl) && (
|
|
843
|
+
<ProviderFormatStatusBlock label="OpenAI Chat">
|
|
844
|
+
{testResults !== undefined && (
|
|
845
|
+
<ProviderFormatTestStatus results={testResults.openaiChat} />
|
|
846
|
+
)}
|
|
847
|
+
{testResults === undefined && (
|
|
848
|
+
<div className="text-xs text-muted-foreground">Not tested yet</div>
|
|
849
|
+
)}
|
|
850
|
+
</ProviderFormatStatusBlock>
|
|
863
851
|
)}
|
|
864
|
-
</ProviderEndpointSummary>
|
|
865
|
-
)}
|
|
866
852
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
853
|
+
{providerHasEndpoint(provider.openaiResponsesBaseUrl) && (
|
|
854
|
+
<ProviderFormatStatusBlock label="OpenAI Responses">
|
|
855
|
+
{testResults !== undefined && (
|
|
856
|
+
<ProviderFormatTestStatus results={testResults.openaiResponses} />
|
|
857
|
+
)}
|
|
858
|
+
{testResults === undefined && (
|
|
859
|
+
<div className="text-xs text-muted-foreground">Not tested yet</div>
|
|
860
|
+
)}
|
|
861
|
+
</ProviderFormatStatusBlock>
|
|
876
862
|
)}
|
|
877
|
-
|
|
863
|
+
|
|
864
|
+
{!providerHasEndpoint(provider.openaiResponsesBaseUrl) && (
|
|
865
|
+
<div className="min-w-0 rounded-md border border-border/50 bg-transparent p-2 text-xs text-muted-foreground/75">
|
|
866
|
+
OpenAI Responses not configured
|
|
867
|
+
</div>
|
|
868
|
+
)}
|
|
869
|
+
</div>
|
|
878
870
|
)}
|
|
879
871
|
|
|
880
872
|
{testResults?.models !== undefined && Object.keys(testResults.models).length > 0 && (
|
|
@@ -535,7 +535,7 @@ export function ProvidersPanel({
|
|
|
535
535
|
if (showForm || editingProvider) {
|
|
536
536
|
return (
|
|
537
537
|
<div className="space-y-4">
|
|
538
|
-
<div className="
|
|
538
|
+
<div className="sticky top-0 z-10 flex items-center justify-between gap-3 rounded-[8px] bg-background/95 px-1 py-1 backdrop-blur">
|
|
539
539
|
<h3 className="text-lg font-medium">
|
|
540
540
|
{editingProvider ? "Edit Provider" : "Add New Provider"}
|
|
541
541
|
</h3>
|
|
@@ -554,7 +554,7 @@ export function ProvidersPanel({
|
|
|
554
554
|
|
|
555
555
|
return (
|
|
556
556
|
<div className="space-y-4">
|
|
557
|
-
<div className="
|
|
557
|
+
<div className="sticky top-0 z-10 flex items-center justify-between gap-3 rounded-[8px] bg-background/95 px-1 py-1 backdrop-blur">
|
|
558
558
|
<h3 className="text-lg font-medium">Providers</h3>
|
|
559
559
|
<div className="flex items-center gap-2">
|
|
560
560
|
<TooltipProvider>
|
|
@@ -622,7 +622,7 @@ export function ProvidersPanel({
|
|
|
622
622
|
</div>
|
|
623
623
|
|
|
624
624
|
{configPath !== null && (
|
|
625
|
-
<div className="
|
|
625
|
+
<div className="flex items-center gap-2 rounded-[8px] bg-muted/15 px-3 py-2 text-xs text-muted-foreground">
|
|
626
626
|
<span className="shrink-0">Config:</span>
|
|
627
627
|
<span className="font-mono truncate" title={configPath}>
|
|
628
628
|
{configPath}
|
|
@@ -674,7 +674,7 @@ export function ProvidersPanel({
|
|
|
674
674
|
</div>
|
|
675
675
|
) : (
|
|
676
676
|
<div className="space-y-3">
|
|
677
|
-
<div className="
|
|
677
|
+
<div className="inline-flex w-fit gap-1 rounded-[8px] bg-muted/20 p-1">
|
|
678
678
|
{(["all", "personal", "company"] as const).map((tab) => (
|
|
679
679
|
<TooltipProvider key={tab}>
|
|
680
680
|
<Tooltip>
|
|
@@ -682,10 +682,10 @@ export function ProvidersPanel({
|
|
|
682
682
|
<button
|
|
683
683
|
type="button"
|
|
684
684
|
onClick={() => setSourceFilter(tab)}
|
|
685
|
-
className={`px-3 py-
|
|
685
|
+
className={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${
|
|
686
686
|
sourceFilter === tab
|
|
687
|
-
? "
|
|
688
|
-
: "
|
|
687
|
+
? "bg-background text-foreground shadow-sm"
|
|
688
|
+
: "text-muted-foreground hover:text-foreground"
|
|
689
689
|
}`}
|
|
690
690
|
>
|
|
691
691
|
{tab === "all" ? "All" : tab === "personal" ? "Personal" : "Company"}
|
|
@@ -74,13 +74,13 @@ export function SettingsDialog(): JSX.Element {
|
|
|
74
74
|
<span className="sr-only">Settings</span>
|
|
75
75
|
</Button>
|
|
76
76
|
</DialogTrigger>
|
|
77
|
-
<DialogContent className="
|
|
77
|
+
<DialogContent className="flex max-h-[80vh] max-w-2xl flex-col overflow-hidden">
|
|
78
78
|
<DialogHeader>
|
|
79
79
|
<DialogTitle>Settings</DialogTitle>
|
|
80
80
|
</DialogHeader>
|
|
81
81
|
|
|
82
82
|
<Tabs value={activeTab} onValueChange={setActiveTab} className="flex-1 overflow-hidden">
|
|
83
|
-
<TabsList>
|
|
83
|
+
<TabsList className="w-full justify-start bg-muted/35">
|
|
84
84
|
<TabsTrigger value="providers">Providers</TabsTrigger>
|
|
85
85
|
<TabsTrigger value="proxy">Proxy</TabsTrigger>
|
|
86
86
|
<TabsTrigger value="storage">Storage</TabsTrigger>
|
|
@@ -88,7 +88,7 @@ export function SettingsDialog(): JSX.Element {
|
|
|
88
88
|
<TabsTrigger value="onboarding">Onboarding</TabsTrigger>
|
|
89
89
|
</TabsList>
|
|
90
90
|
|
|
91
|
-
<div className="mt-4 flex-1 overflow-x-hidden overflow-y-auto pr-
|
|
91
|
+
<div className="mt-4 flex-1 overflow-x-hidden overflow-y-auto pr-2">
|
|
92
92
|
<TabsContent value="providers">
|
|
93
93
|
<ProvidersPanel
|
|
94
94
|
externalProviders={providers}
|
|
@@ -198,7 +198,7 @@ function StorageSettingsTab(): JSX.Element {
|
|
|
198
198
|
return (
|
|
199
199
|
<div className="space-y-4">
|
|
200
200
|
<div className="flex items-center justify-between gap-3">
|
|
201
|
-
<div>
|
|
201
|
+
<div className="min-w-0">
|
|
202
202
|
<h3 className="text-sm font-semibold">Log storage</h3>
|
|
203
203
|
<p className="text-xs text-muted-foreground">
|
|
204
204
|
Inspect local log and streaming chunk footprint before exporting or clearing sessions.
|
|
@@ -232,25 +232,25 @@ function StorageSettingsTab(): JSX.Element {
|
|
|
232
232
|
copiedId={copiedId}
|
|
233
233
|
onCopy={handleCopy}
|
|
234
234
|
/>
|
|
235
|
-
<div className="
|
|
235
|
+
<div className="rounded-md bg-muted/20 px-3 py-2">
|
|
236
236
|
<div className="font-medium">In-memory logs</div>
|
|
237
237
|
<div className="mt-1 font-mono text-muted-foreground">
|
|
238
238
|
{stats.memoryCount.toLocaleString()} loaded
|
|
239
239
|
</div>
|
|
240
240
|
</div>
|
|
241
|
-
<div className="
|
|
241
|
+
<div className="rounded-md bg-muted/20 px-3 py-2">
|
|
242
242
|
<div className="font-medium">Log files</div>
|
|
243
243
|
<div className="mt-1 font-mono text-muted-foreground">
|
|
244
244
|
{stats.logFileCount.toLocaleString()} files / {formatBytes(stats.logBytes)}
|
|
245
245
|
</div>
|
|
246
246
|
</div>
|
|
247
|
-
<div className="
|
|
247
|
+
<div className="rounded-md bg-muted/20 px-3 py-2">
|
|
248
248
|
<div className="font-medium">Streaming chunks</div>
|
|
249
249
|
<div className="mt-1 font-mono text-muted-foreground">
|
|
250
250
|
{stats.chunkFileCount.toLocaleString()} files / {formatBytes(stats.chunkBytes)}
|
|
251
251
|
</div>
|
|
252
252
|
</div>
|
|
253
|
-
<div className="
|
|
253
|
+
<div className="rounded-md bg-muted/20 px-3 py-2">
|
|
254
254
|
<div className="font-medium">Session archives</div>
|
|
255
255
|
<div className="mt-1 font-mono text-muted-foreground">
|
|
256
256
|
{stats.sessionArchiveFileCount.toLocaleString()} files /{" "}
|
|
@@ -280,7 +280,7 @@ function CopyableSetupValue({
|
|
|
280
280
|
}): JSX.Element {
|
|
281
281
|
const copied = copiedId === id;
|
|
282
282
|
return (
|
|
283
|
-
<div className="border border-border bg-muted/
|
|
283
|
+
<div className="min-w-0 rounded-md border border-border/45 bg-muted/15 px-3 py-2 transition-colors hover:bg-muted/25">
|
|
284
284
|
<div className="mb-1 text-xs font-medium text-muted-foreground">{label}</div>
|
|
285
285
|
<div className="flex min-w-0 items-center gap-2">
|
|
286
286
|
<code className="min-w-0 flex-1 truncate font-mono text-xs text-foreground">{value}</code>
|
|
@@ -383,7 +383,7 @@ function OnboardingSettingsTab(): JSX.Element {
|
|
|
383
383
|
/>
|
|
384
384
|
))}
|
|
385
385
|
</div>
|
|
386
|
-
<div className="grid gap-2 rounded-md
|
|
386
|
+
<div className="grid gap-2 rounded-md bg-muted/15 px-3 py-2 text-xs text-muted-foreground">
|
|
387
387
|
<div className="flex items-center gap-2">
|
|
388
388
|
<Check className="size-3.5 text-emerald-500" />
|
|
389
389
|
<span>Provider test creates a traceable memory probe session.</span>
|
|
@@ -574,7 +574,7 @@ function McpSettingsTab(): JSX.Element {
|
|
|
574
574
|
<Terminal className="size-4 text-muted-foreground" />
|
|
575
575
|
<h3 className="text-sm font-semibold">MCP access</h3>
|
|
576
576
|
</div>
|
|
577
|
-
<div className="grid gap-2 rounded-md
|
|
577
|
+
<div className="grid gap-2 rounded-md bg-muted/15 px-3 py-2 text-xs text-muted-foreground">
|
|
578
578
|
<div>
|
|
579
579
|
Use Streamable HTTP when the client supports URL-based MCP. Use the stdio bridge only for
|
|
580
580
|
stdio-only clients. Configure one entry for the same Inspector instance.
|
|
@@ -680,12 +680,8 @@ function ProxySettingsTab(): JSX.Element {
|
|
|
680
680
|
<div className="space-y-1">
|
|
681
681
|
<h3 className="text-sm font-semibold">Claude Code billing header</h3>
|
|
682
682
|
<p className="text-xs text-muted-foreground">
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
Improves prefix-cache hit rates against third-party upstreams. The default at startup is
|
|
686
|
-
controlled by the <code>AGENT_INSPECTOR_STRIP_CLAUDE_CODE_BILLING_HEADER</code>{" "}
|
|
687
|
-
environment variable; your choice is persisted to <code>config.json</code> and overrides
|
|
688
|
-
the env var for subsequent requests.
|
|
683
|
+
Strip Claude Code's synthetic billing block before upstream forwarding. This can improve
|
|
684
|
+
prefix-cache hits with third-party providers and is persisted to <code>config.json</code>.
|
|
689
685
|
</p>
|
|
690
686
|
</div>
|
|
691
687
|
|
|
@@ -710,8 +706,8 @@ function ProxySettingsTab(): JSX.Element {
|
|
|
710
706
|
Slow response threshold
|
|
711
707
|
</label>
|
|
712
708
|
<p className="text-xs text-muted-foreground">
|
|
713
|
-
|
|
714
|
-
|
|
709
|
+
Show a slow marker when elapsed time exceeds this many seconds. Use <code>0</code> to
|
|
710
|
+
disable it.
|
|
715
711
|
</p>
|
|
716
712
|
<div className="flex items-center gap-2">
|
|
717
713
|
<input
|
|
@@ -739,8 +735,7 @@ function ProxySettingsTab(): JSX.Element {
|
|
|
739
735
|
Provider test timeout
|
|
740
736
|
</label>
|
|
741
737
|
<p className="text-xs text-muted-foreground">
|
|
742
|
-
Provider Test
|
|
743
|
-
The full test window scales with model count.
|
|
738
|
+
Abort slow Provider Test probes after this many seconds per model.
|
|
744
739
|
</p>
|
|
745
740
|
<div className="flex items-center gap-2">
|
|
746
741
|
<input
|
|
@@ -768,7 +763,7 @@ function ProxySettingsTab(): JSX.Element {
|
|
|
768
763
|
Time display
|
|
769
764
|
</label>
|
|
770
765
|
<p className="text-xs text-muted-foreground">
|
|
771
|
-
Controls timestamps in session summaries,
|
|
766
|
+
Controls timestamps in session summaries, headers, and log rows.
|
|
772
767
|
</p>
|
|
773
768
|
<select
|
|
774
769
|
id="time-display-format"
|
|
@@ -217,13 +217,6 @@ export const TurnGroup = memo(function TurnGroup({
|
|
|
217
217
|
}
|
|
218
218
|
}}
|
|
219
219
|
>
|
|
220
|
-
{/* Turn number */}
|
|
221
|
-
<div className="w-5 shrink-0 flex items-start pt-1.5">
|
|
222
|
-
<span className="text-[10px] text-muted-foreground/50 font-mono tabular-nums leading-none select-none">
|
|
223
|
-
{turnIndex + 1}
|
|
224
|
-
</span>
|
|
225
|
-
</div>
|
|
226
|
-
|
|
227
220
|
<div className="w-6 shrink-0 flex flex-col items-center pt-1.5 pb-1.5">
|
|
228
221
|
<div className="flex justify-center h-[calc(0.75rem-8px)]" />
|
|
229
222
|
<CollapsedCrab
|
|
@@ -344,15 +337,6 @@ export const TurnGroup = memo(function TurnGroup({
|
|
|
344
337
|
|
|
345
338
|
return (
|
|
346
339
|
<div key={log.id} className="flex items-stretch">
|
|
347
|
-
{isTurnStart ? (
|
|
348
|
-
<div className="w-5 shrink-0 flex items-start pt-1.5">
|
|
349
|
-
<span className="text-[10px] text-muted-foreground/50 font-mono tabular-nums leading-none select-none">
|
|
350
|
-
{turnIndex + 1}
|
|
351
|
-
</span>
|
|
352
|
-
</div>
|
|
353
|
-
) : (
|
|
354
|
-
<div className="w-5 shrink-0" />
|
|
355
|
-
)}
|
|
356
340
|
<ThreadConnector
|
|
357
341
|
stopReason={reason}
|
|
358
342
|
isPending={log.responseStatus === null}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AnthropicRequestSchema, AnthropicResponseSchema } from "../../../contracts/anthropic";
|
|
2
2
|
import type { AnatomySegment } from "../anatomy/types";
|
|
3
3
|
import { countCharacters, estimateTokens } from "../anatomy/tokenEstimate";
|
|
4
|
+
import { parseRequestTools } from "../requestTools";
|
|
4
5
|
import type { LogFormatAdapter } from "./types";
|
|
5
6
|
import { emptyRequestAnalysis, EMPTY_RESPONSE_ANALYSIS } from "./types";
|
|
6
7
|
|
|
@@ -113,16 +114,15 @@ export const anthropicLogFormatAdapter: LogFormatAdapter = {
|
|
|
113
114
|
analyzeRequest(rawBody) {
|
|
114
115
|
if (rawBody === null) return emptyRequestAnalysis(rawBody);
|
|
115
116
|
try {
|
|
116
|
-
const
|
|
117
|
+
const parsedBody: unknown = JSON.parse(rawBody);
|
|
118
|
+
const toolCount = parseRequestTools(parsedBody)?.tools.length ?? null;
|
|
119
|
+
const result = AnthropicRequestSchema.safeParse(parsedBody);
|
|
117
120
|
if (!result.success) return emptyRequestAnalysis(rawBody);
|
|
118
121
|
return {
|
|
119
122
|
parsed: result.data,
|
|
120
123
|
comparisonValue: result.data,
|
|
121
124
|
messageCount: result.data.messages.length,
|
|
122
|
-
toolCount
|
|
123
|
-
result.data.tools !== undefined && result.data.tools.length > 0
|
|
124
|
-
? result.data.tools.length
|
|
125
|
-
: null,
|
|
125
|
+
toolCount,
|
|
126
126
|
};
|
|
127
127
|
} catch {
|
|
128
128
|
return emptyRequestAnalysis(rawBody);
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import { safeGetOwnProperty } from "../../../lib/objectUtils";
|
|
8
8
|
import type { AnatomySegment } from "../anatomy/types";
|
|
9
9
|
import { countCharacters, estimateTokens } from "../anatomy/tokenEstimate";
|
|
10
|
+
import { parseRequestTools } from "../requestTools";
|
|
10
11
|
import type { LogFormatAdapter } from "./types";
|
|
11
12
|
import { emptyRequestAnalysis, EMPTY_RESPONSE_ANALYSIS } from "./types";
|
|
12
13
|
|
|
@@ -146,28 +147,24 @@ export const openAILogFormatAdapter: LogFormatAdapter = {
|
|
|
146
147
|
analyzeRequest(rawBody) {
|
|
147
148
|
if (rawBody === null) return emptyRequestAnalysis(rawBody);
|
|
148
149
|
try {
|
|
149
|
-
const
|
|
150
|
+
const parsedBody: unknown = JSON.parse(rawBody);
|
|
151
|
+
const toolCount = parseRequestTools(parsedBody)?.tools.length ?? null;
|
|
152
|
+
const result = OpenAIRequestSchema.safeParse(parsedBody);
|
|
150
153
|
if (!result.success) {
|
|
151
|
-
const responsesResult = OpenAIResponsesRequestSchema.safeParse(
|
|
154
|
+
const responsesResult = OpenAIResponsesRequestSchema.safeParse(parsedBody);
|
|
152
155
|
if (!responsesResult.success) return emptyRequestAnalysis(rawBody);
|
|
153
156
|
return {
|
|
154
157
|
parsed: responsesResult.data,
|
|
155
158
|
comparisonValue: responsesResult.data,
|
|
156
159
|
messageCount: countResponsesInput(responsesResult.data.input),
|
|
157
|
-
toolCount
|
|
158
|
-
responsesResult.data.tools !== undefined && responsesResult.data.tools.length > 0
|
|
159
|
-
? responsesResult.data.tools.length
|
|
160
|
-
: null,
|
|
160
|
+
toolCount,
|
|
161
161
|
};
|
|
162
162
|
}
|
|
163
163
|
return {
|
|
164
164
|
parsed: result.data,
|
|
165
165
|
comparisonValue: result.data,
|
|
166
166
|
messageCount: result.data.messages.length,
|
|
167
|
-
toolCount
|
|
168
|
-
result.data.tools !== undefined && result.data.tools.length > 0
|
|
169
|
-
? result.data.tools.length
|
|
170
|
-
: null,
|
|
167
|
+
toolCount,
|
|
171
168
|
};
|
|
172
169
|
} catch {
|
|
173
170
|
return emptyRequestAnalysis(rawBody);
|
|
@@ -145,8 +145,11 @@ function parseAnthropicTool(tool: unknown): RequestToolDefinition | null {
|
|
|
145
145
|
function parseOpenAITool(tool: unknown): RequestToolDefinition | null {
|
|
146
146
|
const fn = safeGetOwnProperty(tool, "function");
|
|
147
147
|
const toolType = safeGetOwnProperty(tool, "type");
|
|
148
|
-
if (fn === undefined && toolType !== "
|
|
149
|
-
const name =
|
|
148
|
+
if (fn === undefined && typeof toolType !== "string") return null;
|
|
149
|
+
const name =
|
|
150
|
+
safeGetOwnProperty(fn, "name") ??
|
|
151
|
+
safeGetOwnProperty(tool, "name") ??
|
|
152
|
+
(typeof toolType === "string" ? toolType : undefined);
|
|
150
153
|
if (typeof name !== "string" || name.length === 0) return null;
|
|
151
154
|
const description =
|
|
152
155
|
safeGetOwnProperty(fn, "description") ?? safeGetOwnProperty(tool, "description");
|
package/src/routes/api/logs.ts
CHANGED
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
searchLogsPage,
|
|
15
15
|
} from "../../proxy/logSearch";
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
export const DeleteLogsBodySchema = z
|
|
18
18
|
.object({
|
|
19
|
-
ids: z.array(z.number().int().
|
|
19
|
+
ids: z.array(z.number().int().nonnegative()).optional(),
|
|
20
20
|
})
|
|
21
21
|
.optional();
|
|
22
22
|
|
|
@@ -114,12 +114,12 @@ export const Route = createFileRoute("/api/logs")({
|
|
|
114
114
|
);
|
|
115
115
|
},
|
|
116
116
|
DELETE: async ({ request }: { request: Request }) => {
|
|
117
|
-
let body: z.infer<typeof
|
|
117
|
+
let body: z.infer<typeof DeleteLogsBodySchema> = undefined;
|
|
118
118
|
try {
|
|
119
119
|
const raw = await request.text();
|
|
120
120
|
if (raw !== "") {
|
|
121
121
|
const parsed: unknown = JSON.parse(raw);
|
|
122
|
-
const result =
|
|
122
|
+
const result = DeleteLogsBodySchema.safeParse(parsed);
|
|
123
123
|
if (!result.success) {
|
|
124
124
|
return Response.json(
|
|
125
125
|
{ error: "Invalid request body", details: result.error.format() },
|
|
@@ -132,7 +132,16 @@ export const Route = createFileRoute("/api/logs")({
|
|
|
132
132
|
return Response.json({ error: "Invalid JSON body" }, { status: 400 });
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
if (body?.ids !== undefined
|
|
135
|
+
if (body?.ids !== undefined) {
|
|
136
|
+
if (body.ids.length === 0) {
|
|
137
|
+
return Response.json({
|
|
138
|
+
success: true,
|
|
139
|
+
cleared: 0,
|
|
140
|
+
logFilesRewritten: 0,
|
|
141
|
+
chunkFilesDeleted: 0,
|
|
142
|
+
sessionArchiveLogsDeleted: 0,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
136
145
|
const result = await clearPersistedLogsByIds(body.ids);
|
|
137
146
|
return Response.json({
|
|
138
147
|
success: true,
|