@tonyclaw/agent-inspector 2.0.20 → 2.0.22
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/cli.js +10 -0
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-CUqYtaH3.js → CompareDrawer-NEdpe1wl.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-Dwhi4Q1c.js +115 -0
- package/.output/public/assets/{ReplayDialog-CPXNEqVg.js → ReplayDialog-DijzJwaQ.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-qkj8QgkY.js → RequestAnatomy-CYbosGYy.js} +1 -1
- package/.output/public/assets/{ResponseView-BPNLZpuw.js → ResponseView-B3dPM63X.js} +1 -1
- package/.output/public/assets/{StreamingChunkSequence-DGs-YFXI.js → StreamingChunkSequence-DYDx3xRY.js} +1 -1
- package/.output/public/assets/_sessionId-b1KDnByv.js +1 -0
- package/.output/public/assets/index-780zTVwp.css +1 -0
- package/.output/public/assets/index-CUyRKgII.js +1 -0
- package/.output/public/assets/{main-j3vS8rmP.js → main-N-sjHJUM.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +27 -27
- package/.output/server/{_sessionId-Bc5Nohhe.mjs → _sessionId-BR46y_bO.mjs} +2 -2
- package/.output/server/_ssr/{CompareDrawer-D0UewydM.mjs → CompareDrawer-DDxpmdPs.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-BEUWt2eX.mjs → ProxyViewerContainer-B1zAefzz.mjs} +304 -55
- package/.output/server/_ssr/{ReplayDialog-zaN-xMOw.mjs → ReplayDialog-DleMFvMx.mjs} +4 -4
- package/.output/server/_ssr/{RequestAnatomy-BMomYged.mjs → RequestAnatomy-B-P9tC3D.mjs} +3 -3
- package/.output/server/_ssr/{ResponseView-DSoHCyAM.mjs → ResponseView-DWfGn0i3.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-pt3pZOOL.mjs → StreamingChunkSequence-C8kFF6xJ.mjs} +3 -3
- package/.output/server/_ssr/{index-BCTKxcgV.mjs → index-DzENi2RO.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-DpaDa5q5.mjs → router-Dy1VEjxy.mjs} +434 -80
- package/.output/server/{_tanstack-start-manifest_v-EsubwO0A.mjs → _tanstack-start-manifest_v-CyzuAD8A.mjs} +1 -1
- package/.output/server/index.mjs +55 -55
- package/package.json +1 -1
- package/src/cli/templates/codex-skill-onboard.ts +5 -0
- package/src/cli/templates/skill-onboard.ts +5 -0
- package/src/components/OnboardingBanner.tsx +4 -0
- package/src/components/ProxyViewer.tsx +5 -0
- package/src/components/providers/ProviderCard.tsx +178 -38
- package/src/components/providers/ProviderForm.tsx +107 -16
- package/src/components/providers/ProvidersPanel.tsx +21 -3
- package/src/components/providers/SettingsDialog.tsx +76 -2
- package/src/lib/providerModelMetadata.ts +67 -3
- package/src/lib/providerTestContract.ts +13 -0
- package/src/lib/runtimeConfig.ts +8 -0
- package/src/lib/useOnboarding.ts +3 -0
- package/src/lib/useStripConfig.ts +22 -0
- package/src/proxy/config.ts +4 -0
- package/src/proxy/formats/index.ts +1 -0
- package/src/proxy/formats/openai/index.ts +1 -0
- package/src/proxy/formats/openai/zhipuProvider.ts +31 -0
- package/src/proxy/providerImporters.ts +47 -0
- package/src/proxy/providers.ts +55 -0
- package/src/proxy/upstream.ts +25 -0
- package/src/routes/api/config.ts +7 -0
- package/src/routes/api/providers.$providerId.test.log.ts +28 -12
- package/.output/public/assets/ProxyViewerContainer-CX0-R0nn.js +0 -114
- package/.output/public/assets/_sessionId-CXtbrFTj.js +0 -1
- package/.output/public/assets/index-DrppF281.js +0 -1
- package/.output/public/assets/index-DynnYt7S.css +0 -1
|
@@ -5,23 +5,78 @@ import { Eye, EyeOff, Copy, Check, ChevronDown } from "lucide-react";
|
|
|
5
5
|
import type { ProviderConfig, ProviderModelMetadata } from "../../lib/providerContract";
|
|
6
6
|
import { maskApiKey } from "../../lib/mask";
|
|
7
7
|
|
|
8
|
+
const ZHIPU_PROVIDER_KEYWORDS = ["zhipu", "zhipuai", "bigmodel", "z.ai", "zai", "glm"];
|
|
9
|
+
const ZHIPU_CODING_PROVIDER_KEYWORDS = [
|
|
10
|
+
"zhipu coding",
|
|
11
|
+
"glm coding",
|
|
12
|
+
"bigmodel coding",
|
|
13
|
+
"z.ai coding",
|
|
14
|
+
"zai coding",
|
|
15
|
+
];
|
|
16
|
+
|
|
8
17
|
// Known provider presets - maps provider name keywords to their API URLs
|
|
9
18
|
const KNOWN_PROVIDER_PRESETS: Record<
|
|
10
19
|
string,
|
|
11
|
-
{
|
|
20
|
+
{ anthropicBaseUrl?: string; openaiBaseUrl?: string; apiDocsUrl?: string }
|
|
12
21
|
> = {
|
|
13
22
|
deepseek: {
|
|
14
|
-
|
|
15
|
-
baseUrl: "https://api.deepseek.com",
|
|
23
|
+
openaiBaseUrl: "https://api.deepseek.com",
|
|
16
24
|
},
|
|
17
25
|
minimax: {
|
|
18
|
-
|
|
19
|
-
baseUrl: "https://api.minimaxi.com/anthropic",
|
|
26
|
+
anthropicBaseUrl: "https://api.minimaxi.com/anthropic",
|
|
20
27
|
apiDocsUrl: "https://platform.minimaxi.com/docs/api-reference/api-overview",
|
|
21
28
|
},
|
|
29
|
+
"zhipu coding": {
|
|
30
|
+
anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
31
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
32
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start",
|
|
33
|
+
},
|
|
34
|
+
"glm coding": {
|
|
35
|
+
anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
36
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
37
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start",
|
|
38
|
+
},
|
|
39
|
+
"bigmodel coding": {
|
|
40
|
+
anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
41
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
42
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start",
|
|
43
|
+
},
|
|
44
|
+
"z.ai coding": {
|
|
45
|
+
anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
46
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
47
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start",
|
|
48
|
+
},
|
|
49
|
+
"zai coding": {
|
|
50
|
+
anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
|
|
51
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
|
|
52
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start",
|
|
53
|
+
},
|
|
54
|
+
zhipu: {
|
|
55
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
56
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
57
|
+
},
|
|
58
|
+
zhipuai: {
|
|
59
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
60
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
61
|
+
},
|
|
62
|
+
bigmodel: {
|
|
63
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
64
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
65
|
+
},
|
|
66
|
+
"z.ai": {
|
|
67
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
68
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
69
|
+
},
|
|
70
|
+
zai: {
|
|
71
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
72
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
73
|
+
},
|
|
74
|
+
glm: {
|
|
75
|
+
openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
|
|
76
|
+
apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction",
|
|
77
|
+
},
|
|
22
78
|
alibaba: {
|
|
23
|
-
|
|
24
|
-
baseUrl: "https://dashscope.aliyuncs.com/compatible-mode",
|
|
79
|
+
openaiBaseUrl: "https://dashscope.aliyuncs.com/compatible-mode",
|
|
25
80
|
},
|
|
26
81
|
};
|
|
27
82
|
|
|
@@ -40,6 +95,22 @@ const MINIMAX_MODELS = [
|
|
|
40
95
|
// Alibaba model options
|
|
41
96
|
const ALIBABA_MODELS = ["glm-5", "glm-5.1", "glm-5.2", "qwen3.6-plus", "qwen3.7-max"];
|
|
42
97
|
|
|
98
|
+
// ZhipuAI / Z.AI model options
|
|
99
|
+
const ZHIPU_MODELS = [
|
|
100
|
+
"glm-5.2",
|
|
101
|
+
"glm-5.1",
|
|
102
|
+
"glm-5",
|
|
103
|
+
"glm-5-turbo",
|
|
104
|
+
"glm-4.7",
|
|
105
|
+
"glm-4.7-flashx",
|
|
106
|
+
"glm-4.6",
|
|
107
|
+
"glm-4.5",
|
|
108
|
+
"glm-4.5-air",
|
|
109
|
+
"glm-4.5-x",
|
|
110
|
+
"glm-4.5-airx",
|
|
111
|
+
"glm-4.5-flash",
|
|
112
|
+
];
|
|
113
|
+
|
|
43
114
|
type ProviderFormProps = {
|
|
44
115
|
provider?: ProviderConfig;
|
|
45
116
|
onSubmit: (data: {
|
|
@@ -151,6 +222,16 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
151
222
|
const isMiniMax = name.toLowerCase().includes("minimax");
|
|
152
223
|
// Check if Alibaba is detected
|
|
153
224
|
const isAlibaba = name.toLowerCase().includes("alibaba");
|
|
225
|
+
const isZhipu = [...ZHIPU_PROVIDER_KEYWORDS, ...ZHIPU_CODING_PROVIDER_KEYWORDS].some((keyword) =>
|
|
226
|
+
name.toLowerCase().includes(keyword),
|
|
227
|
+
);
|
|
228
|
+
const suggestedModels = isMiniMax
|
|
229
|
+
? MINIMAX_MODELS
|
|
230
|
+
: isZhipu
|
|
231
|
+
? ZHIPU_MODELS
|
|
232
|
+
: isAlibaba
|
|
233
|
+
? ALIBABA_MODELS
|
|
234
|
+
: [];
|
|
154
235
|
const hasContextMetadata =
|
|
155
236
|
provider?.modelMetadata?.some((metadata) => metadata.contextWindow !== undefined) ?? false;
|
|
156
237
|
|
|
@@ -182,10 +263,11 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
182
263
|
const lowerName = name.toLowerCase();
|
|
183
264
|
for (const [keyword, preset] of Object.entries(KNOWN_PROVIDER_PRESETS)) {
|
|
184
265
|
if (lowerName.includes(keyword)) {
|
|
185
|
-
if (preset.
|
|
186
|
-
setAnthropicBaseUrl(preset.
|
|
187
|
-
}
|
|
188
|
-
|
|
266
|
+
if (preset.anthropicBaseUrl !== undefined && !manualAnthropicUrlOverride) {
|
|
267
|
+
setAnthropicBaseUrl(preset.anthropicBaseUrl);
|
|
268
|
+
}
|
|
269
|
+
if (preset.openaiBaseUrl !== undefined && !manualOpenaiUrlOverride) {
|
|
270
|
+
setOpenaiBaseUrl(preset.openaiBaseUrl);
|
|
189
271
|
}
|
|
190
272
|
if (preset.apiDocsUrl !== undefined && !apiDocsUrl) {
|
|
191
273
|
setApiDocsUrl(preset.apiDocsUrl);
|
|
@@ -198,6 +280,15 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
198
280
|
if (keyword === "alibaba" && models.length === 1 && models[0] === "") {
|
|
199
281
|
setModels([ALIBABA_MODELS[0] ?? ""]);
|
|
200
282
|
}
|
|
283
|
+
// For ZhipuAI, auto-select the first model if not already set
|
|
284
|
+
if (
|
|
285
|
+
(ZHIPU_PROVIDER_KEYWORDS.includes(keyword) ||
|
|
286
|
+
ZHIPU_CODING_PROVIDER_KEYWORDS.includes(keyword)) &&
|
|
287
|
+
models.length === 1 &&
|
|
288
|
+
models[0] === ""
|
|
289
|
+
) {
|
|
290
|
+
setModels([ZHIPU_MODELS[0] ?? ""]);
|
|
291
|
+
}
|
|
201
292
|
break;
|
|
202
293
|
}
|
|
203
294
|
}
|
|
@@ -396,8 +487,8 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
396
487
|
)}
|
|
397
488
|
{modelMetadataUrl.trim() === "" && (
|
|
398
489
|
<div className="rounded-md border border-border bg-muted/20 px-3 py-2 text-xs text-muted-foreground">
|
|
399
|
-
Built-in limits cover DeepSeek v4 pro/flash
|
|
400
|
-
registry URL for private or custom model limits.
|
|
490
|
+
Built-in limits cover DeepSeek v4 pro/flash, MiniMax M3/M2.7/M2.5/M2.1, and GLM coding
|
|
491
|
+
models. Use a registry URL for private or custom model limits.
|
|
401
492
|
</div>
|
|
402
493
|
)}
|
|
403
494
|
{provider !== undefined && !hasContextMetadata && modelMetadataUrl.trim() === "" && (
|
|
@@ -472,11 +563,11 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
472
563
|
value={m}
|
|
473
564
|
onChange={(e) => updateModel(i, e.target.value)}
|
|
474
565
|
placeholder={
|
|
475
|
-
|
|
566
|
+
suggestedModels.length > 0 ? "Type or select a model..." : "Model name"
|
|
476
567
|
}
|
|
477
568
|
className="w-full rounded-md border border-input bg-background px-4 py-3 pr-8 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
|
|
478
569
|
/>
|
|
479
|
-
{
|
|
570
|
+
{suggestedModels.length > 0 && (
|
|
480
571
|
<>
|
|
481
572
|
<button
|
|
482
573
|
type="button"
|
|
@@ -488,7 +579,7 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
488
579
|
</button>
|
|
489
580
|
{openModelDropdown === i && (
|
|
490
581
|
<div className="absolute left-0 right-0 top-full mt-1 z-50 bg-popover border border-border rounded-md shadow-md max-h-48 overflow-y-auto">
|
|
491
|
-
{
|
|
582
|
+
{suggestedModels.map((opt) => (
|
|
492
583
|
<button
|
|
493
584
|
key={opt}
|
|
494
585
|
type="button"
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
ProviderTestResultsSchema,
|
|
20
20
|
type ProviderTestResults as TestResults,
|
|
21
21
|
} from "../../lib/providerTestContract";
|
|
22
|
+
import { useStripConfig } from "../../lib/useStripConfig";
|
|
22
23
|
|
|
23
24
|
export type { ProviderTestResults as TestResults } from "../../lib/providerTestContract";
|
|
24
25
|
|
|
@@ -41,7 +42,6 @@ const ModelMetadataRefreshResponseSchema = z.object({
|
|
|
41
42
|
sourceUrl: z.string(),
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
const TEST_TIMEOUT_SECONDS = 30;
|
|
45
45
|
const NETWORK_ERROR_MESSAGE = "Network error: could not reach the server. Is the proxy running?";
|
|
46
46
|
|
|
47
47
|
type ProviderFormData = {
|
|
@@ -70,6 +70,15 @@ function createProviderPayload(data: ProviderFormData) {
|
|
|
70
70
|
};
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
function getProviderTestTimeoutSeconds(
|
|
74
|
+
provider: ProviderConfig | undefined,
|
|
75
|
+
timeoutSecondsPerModel: number,
|
|
76
|
+
): number {
|
|
77
|
+
const modelCount =
|
|
78
|
+
provider?.models?.map((model) => model.trim()).filter((model) => model !== "").length ?? 0;
|
|
79
|
+
return Math.max(1, modelCount) * timeoutSecondsPerModel;
|
|
80
|
+
}
|
|
81
|
+
|
|
73
82
|
async function persistProviderTestLog(providerId: string, results: TestResults): Promise<void> {
|
|
74
83
|
if (results.models === undefined) {
|
|
75
84
|
return;
|
|
@@ -126,6 +135,7 @@ export function ProvidersPanel({
|
|
|
126
135
|
const [refreshingMetadataProviders, setRefreshingMetadataProviders] = useState<Set<string>>(
|
|
127
136
|
new Set(),
|
|
128
137
|
);
|
|
138
|
+
const { providerTestTimeoutSeconds } = useStripConfig();
|
|
129
139
|
const listScrollRef = useRef<HTMLDivElement>(null);
|
|
130
140
|
const highlightTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
131
141
|
|
|
@@ -239,9 +249,11 @@ export function ProvidersPanel({
|
|
|
239
249
|
|
|
240
250
|
// Create abort controller for this test request
|
|
241
251
|
const controller = new AbortController();
|
|
252
|
+
const provider = providers.find((candidate) => candidate.id === providerId);
|
|
253
|
+
const timeoutSeconds = getProviderTestTimeoutSeconds(provider, providerTestTimeoutSeconds);
|
|
242
254
|
|
|
243
255
|
// Start countdown
|
|
244
|
-
let remaining =
|
|
256
|
+
let remaining = timeoutSeconds;
|
|
245
257
|
setTestingTimeLeft(providerId, remaining);
|
|
246
258
|
const intervalId = setInterval(() => {
|
|
247
259
|
remaining--;
|
|
@@ -289,7 +301,13 @@ export function ProvidersPanel({
|
|
|
289
301
|
updateTestingState(providerId, false);
|
|
290
302
|
}
|
|
291
303
|
},
|
|
292
|
-
[
|
|
304
|
+
[
|
|
305
|
+
providerTestTimeoutSeconds,
|
|
306
|
+
providers,
|
|
307
|
+
setTestingTimeLeft,
|
|
308
|
+
updateTestResults,
|
|
309
|
+
updateTestingState,
|
|
310
|
+
],
|
|
293
311
|
);
|
|
294
312
|
|
|
295
313
|
const refreshProviderMetadata = useCallback(
|
|
@@ -8,6 +8,7 @@ import { ProvidersPanel } from "./ProvidersPanel";
|
|
|
8
8
|
import { useProviders } from "../../lib/useProviders";
|
|
9
9
|
import { useStripConfig } from "../../lib/useStripConfig";
|
|
10
10
|
import {
|
|
11
|
+
MAX_PROVIDER_TEST_TIMEOUT_SECONDS,
|
|
11
12
|
MAX_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
12
13
|
TimeDisplayFormatSchema,
|
|
13
14
|
type TimeDisplayFormat,
|
|
@@ -140,6 +141,7 @@ function StorageSettingsTab(): JSX.Element {
|
|
|
140
141
|
const [stats, setStats] = useState<LogStorageStats | null>(null);
|
|
141
142
|
const [error, setError] = useState<string | null>(null);
|
|
142
143
|
const [loading, setLoading] = useState(false);
|
|
144
|
+
const [copiedId, setCopiedId] = useState<string | null>(null);
|
|
143
145
|
|
|
144
146
|
const refresh = useCallback(async () => {
|
|
145
147
|
setLoading(true);
|
|
@@ -168,6 +170,13 @@ function StorageSettingsTab(): JSX.Element {
|
|
|
168
170
|
void refresh();
|
|
169
171
|
}, [refresh]);
|
|
170
172
|
|
|
173
|
+
const handleCopy = useCallback((id: string, value: string) => {
|
|
174
|
+
void window.navigator.clipboard.writeText(value).then(() => {
|
|
175
|
+
setCopiedId(id);
|
|
176
|
+
setTimeout(() => setCopiedId(null), 1600);
|
|
177
|
+
});
|
|
178
|
+
}, []);
|
|
179
|
+
|
|
171
180
|
return (
|
|
172
181
|
<div className="space-y-4">
|
|
173
182
|
<div className="flex items-center justify-between gap-3">
|
|
@@ -184,6 +193,20 @@ function StorageSettingsTab(): JSX.Element {
|
|
|
184
193
|
|
|
185
194
|
{stats !== null && (
|
|
186
195
|
<div className="grid gap-2 text-xs">
|
|
196
|
+
<CopyableSetupValue
|
|
197
|
+
id="log-dir"
|
|
198
|
+
label="Log storage path"
|
|
199
|
+
value={stats.logDir}
|
|
200
|
+
copiedId={copiedId}
|
|
201
|
+
onCopy={handleCopy}
|
|
202
|
+
/>
|
|
203
|
+
<CopyableSetupValue
|
|
204
|
+
id="chunk-dir"
|
|
205
|
+
label="Streaming chunks storage path"
|
|
206
|
+
value={stats.chunkDir}
|
|
207
|
+
copiedId={copiedId}
|
|
208
|
+
onCopy={handleCopy}
|
|
209
|
+
/>
|
|
187
210
|
<div className="rounded-md border border-border bg-muted/20 px-3 py-2">
|
|
188
211
|
<div className="font-medium">In-memory logs</div>
|
|
189
212
|
<div className="mt-1 font-mono text-muted-foreground">
|
|
@@ -192,14 +215,12 @@ function StorageSettingsTab(): JSX.Element {
|
|
|
192
215
|
</div>
|
|
193
216
|
<div className="rounded-md border border-border bg-muted/20 px-3 py-2">
|
|
194
217
|
<div className="font-medium">Log files</div>
|
|
195
|
-
<div className="mt-1 break-all font-mono text-muted-foreground">{stats.logDir}</div>
|
|
196
218
|
<div className="mt-1 font-mono text-muted-foreground">
|
|
197
219
|
{stats.logFileCount.toLocaleString()} files / {formatBytes(stats.logBytes)}
|
|
198
220
|
</div>
|
|
199
221
|
</div>
|
|
200
222
|
<div className="rounded-md border border-border bg-muted/20 px-3 py-2">
|
|
201
223
|
<div className="font-medium">Streaming chunks</div>
|
|
202
|
-
<div className="mt-1 break-all font-mono text-muted-foreground">{stats.chunkDir}</div>
|
|
203
224
|
<div className="mt-1 font-mono text-muted-foreground">
|
|
204
225
|
{stats.chunkFileCount.toLocaleString()} files / {formatBytes(stats.chunkBytes)}
|
|
205
226
|
</div>
|
|
@@ -314,6 +335,13 @@ function OnboardingSettingsTab(): JSX.Element {
|
|
|
314
335
|
<Check className="size-3.5 text-emerald-500" />
|
|
315
336
|
<span>Captured sessions can produce reviewable memory candidates.</span>
|
|
316
337
|
</div>
|
|
338
|
+
<div className="flex items-start gap-2">
|
|
339
|
+
<Check className="mt-0.5 size-3.5 text-emerald-500" />
|
|
340
|
+
<span>
|
|
341
|
+
If your AI tool runs in another container or host, replace localhost with an address
|
|
342
|
+
reachable from that tool and verify the network/firewall path.
|
|
343
|
+
</span>
|
|
344
|
+
</div>
|
|
317
345
|
</div>
|
|
318
346
|
</div>
|
|
319
347
|
);
|
|
@@ -324,10 +352,12 @@ function ProxySettingsTab(): JSX.Element {
|
|
|
324
352
|
strip,
|
|
325
353
|
captureMode,
|
|
326
354
|
slowResponseThresholdSeconds,
|
|
355
|
+
providerTestTimeoutSeconds,
|
|
327
356
|
timeDisplayFormat,
|
|
328
357
|
isLoading,
|
|
329
358
|
setStrip,
|
|
330
359
|
setSlowResponseThresholdSeconds,
|
|
360
|
+
setProviderTestTimeoutSeconds,
|
|
331
361
|
setTimeDisplayFormat,
|
|
332
362
|
} = useStripConfig();
|
|
333
363
|
const [error, setError] = useState<string | null>(null);
|
|
@@ -364,6 +394,21 @@ function ProxySettingsTab(): JSX.Element {
|
|
|
364
394
|
[setSlowResponseThresholdSeconds],
|
|
365
395
|
);
|
|
366
396
|
|
|
397
|
+
const handleProviderTestTimeoutChange = useCallback(
|
|
398
|
+
async (next: number) => {
|
|
399
|
+
setError(null);
|
|
400
|
+
setPending(true);
|
|
401
|
+
try {
|
|
402
|
+
await setProviderTestTimeoutSeconds(next);
|
|
403
|
+
} catch (err) {
|
|
404
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
405
|
+
} finally {
|
|
406
|
+
setPending(false);
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
[setProviderTestTimeoutSeconds],
|
|
410
|
+
);
|
|
411
|
+
|
|
367
412
|
const handleTimeDisplayFormatChange = useCallback(
|
|
368
413
|
async (next: TimeDisplayFormat) => {
|
|
369
414
|
setError(null);
|
|
@@ -449,6 +494,35 @@ function ProxySettingsTab(): JSX.Element {
|
|
|
449
494
|
</div>
|
|
450
495
|
</div>
|
|
451
496
|
|
|
497
|
+
<div className="space-y-1">
|
|
498
|
+
<label htmlFor="provider-test-timeout" className="text-sm font-semibold">
|
|
499
|
+
Provider test timeout
|
|
500
|
+
</label>
|
|
501
|
+
<p className="text-xs text-muted-foreground">
|
|
502
|
+
Provider Test waits this many seconds per configured model before aborting slow probes.
|
|
503
|
+
The full test window scales with model count.
|
|
504
|
+
</p>
|
|
505
|
+
<div className="flex items-center gap-2">
|
|
506
|
+
<input
|
|
507
|
+
id="provider-test-timeout"
|
|
508
|
+
type="number"
|
|
509
|
+
min={1}
|
|
510
|
+
max={MAX_PROVIDER_TEST_TIMEOUT_SECONDS}
|
|
511
|
+
step={1}
|
|
512
|
+
value={providerTestTimeoutSeconds}
|
|
513
|
+
disabled={isLoading || pending}
|
|
514
|
+
onChange={(e) => {
|
|
515
|
+
const next = Number(e.currentTarget.value);
|
|
516
|
+
if (!Number.isInteger(next)) return;
|
|
517
|
+
if (next < 1 || next > MAX_PROVIDER_TEST_TIMEOUT_SECONDS) return;
|
|
518
|
+
void handleProviderTestTimeoutChange(next);
|
|
519
|
+
}}
|
|
520
|
+
className="h-8 w-24 rounded-md border border-input bg-transparent px-2 text-sm font-mono disabled:cursor-not-allowed disabled:opacity-50"
|
|
521
|
+
/>
|
|
522
|
+
<span className="text-xs text-muted-foreground">seconds / model</span>
|
|
523
|
+
</div>
|
|
524
|
+
</div>
|
|
525
|
+
|
|
452
526
|
<div className="space-y-1">
|
|
453
527
|
<label htmlFor="time-display-format" className="text-sm font-semibold">
|
|
454
528
|
Time display
|
|
@@ -47,6 +47,10 @@ export const HF_MINIMAX_M3_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M3";
|
|
|
47
47
|
export const HF_MINIMAX_M2_7_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M2.7";
|
|
48
48
|
export const HF_MINIMAX_M2_5_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M2.5";
|
|
49
49
|
export const HF_MINIMAX_M2_1_URL = "https://huggingface.co/MiniMaxAI/MiniMax-M2.1";
|
|
50
|
+
export const ZHIPU_GLM_4_5_URL = "https://docs.bigmodel.cn/cn/guide/models/text/glm-4.5";
|
|
51
|
+
export const ZHIPU_GLM_4_6_URL = "https://docs.bigmodel.cn/cn/guide/models/text/glm-4.6";
|
|
52
|
+
export const ZHIPU_GLM_4_7_URL = "https://docs.bigmodel.cn/cn/guide/models/text/glm-4.7";
|
|
53
|
+
export const ZHIPU_GLM_5_TURBO_URL = "https://docs.bigmodel.cn/cn/guide/models/text/glm-5-turbo";
|
|
50
54
|
export const HF_GLM_5_URL = "https://huggingface.co/zai-org/GLM-5";
|
|
51
55
|
export const HF_GLM_5_1_URL = "https://huggingface.co/zai-org/GLM-5.1";
|
|
52
56
|
export const HF_GLM_5_2_URL = "https://huggingface.co/zai-org/GLM-5.2";
|
|
@@ -124,10 +128,63 @@ export const BUILTIN_PROVIDER_MODEL_REGISTRY: ProviderModelRegistry = {
|
|
|
124
128
|
],
|
|
125
129
|
},
|
|
126
130
|
{
|
|
127
|
-
name: "
|
|
128
|
-
aliases: ["glm", "zai", "zhipu", "bigmodel"],
|
|
129
|
-
baseUrls: [
|
|
131
|
+
name: "ZhipuAI",
|
|
132
|
+
aliases: ["glm", "zai", "zhipu", "zhipuai", "bigmodel"],
|
|
133
|
+
baseUrls: [
|
|
134
|
+
"https://open.bigmodel.cn/api/paas/v4",
|
|
135
|
+
"https://open.bigmodel.cn/api/coding/paas/v4",
|
|
136
|
+
"https://open.bigmodel.cn/api/anthropic",
|
|
137
|
+
],
|
|
130
138
|
models: [
|
|
139
|
+
{
|
|
140
|
+
id: "glm-4.5",
|
|
141
|
+
aliases: ["GLM-4.5"],
|
|
142
|
+
contextWindow: 131_072,
|
|
143
|
+
outputLimit: 98_304,
|
|
144
|
+
sourceUrl: ZHIPU_GLM_4_5_URL,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: "glm-4.5-air",
|
|
148
|
+
aliases: ["GLM-4.5-Air"],
|
|
149
|
+
contextWindow: 131_072,
|
|
150
|
+
outputLimit: 98_304,
|
|
151
|
+
sourceUrl: ZHIPU_GLM_4_5_URL,
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "glm-4.5-x",
|
|
155
|
+
aliases: ["GLM-4.5-X"],
|
|
156
|
+
contextWindow: 131_072,
|
|
157
|
+
outputLimit: 98_304,
|
|
158
|
+
sourceUrl: ZHIPU_GLM_4_5_URL,
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "glm-4.5-airx",
|
|
162
|
+
aliases: ["GLM-4.5-AirX"],
|
|
163
|
+
contextWindow: 131_072,
|
|
164
|
+
outputLimit: 98_304,
|
|
165
|
+
sourceUrl: ZHIPU_GLM_4_5_URL,
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: "glm-4.5-flash",
|
|
169
|
+
aliases: ["GLM-4.5-Flash"],
|
|
170
|
+
contextWindow: 131_072,
|
|
171
|
+
outputLimit: 98_304,
|
|
172
|
+
sourceUrl: ZHIPU_GLM_4_5_URL,
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: "glm-4.6",
|
|
176
|
+
aliases: ["GLM-4.6"],
|
|
177
|
+
contextWindow: 202_752,
|
|
178
|
+
outputLimit: 65_536,
|
|
179
|
+
sourceUrl: ZHIPU_GLM_4_6_URL,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: "glm-4.7",
|
|
183
|
+
aliases: ["GLM-4.7"],
|
|
184
|
+
contextWindow: 202_752,
|
|
185
|
+
outputLimit: 131_072,
|
|
186
|
+
sourceUrl: ZHIPU_GLM_4_7_URL,
|
|
187
|
+
},
|
|
131
188
|
{
|
|
132
189
|
id: "glm-5",
|
|
133
190
|
aliases: ["GLM-5"],
|
|
@@ -146,6 +203,13 @@ export const BUILTIN_PROVIDER_MODEL_REGISTRY: ProviderModelRegistry = {
|
|
|
146
203
|
contextWindow: 1_048_576,
|
|
147
204
|
sourceUrl: HF_GLM_5_2_URL,
|
|
148
205
|
},
|
|
206
|
+
{
|
|
207
|
+
id: "glm-5-turbo",
|
|
208
|
+
aliases: ["GLM-5-Turbo"],
|
|
209
|
+
contextWindow: 202_752,
|
|
210
|
+
outputLimit: 131_072,
|
|
211
|
+
sourceUrl: ZHIPU_GLM_5_TURBO_URL,
|
|
212
|
+
},
|
|
149
213
|
],
|
|
150
214
|
},
|
|
151
215
|
],
|
|
@@ -25,6 +25,17 @@ const ProviderTestContentSchema = z.object({
|
|
|
25
25
|
thinking: z.string().optional(),
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
+
const ProviderTestDebugSchema = z.object({
|
|
29
|
+
requestUrl: z.string().optional(),
|
|
30
|
+
requestMethod: z.string().optional(),
|
|
31
|
+
requestHeaders: z.record(z.string(), z.string()).optional(),
|
|
32
|
+
requestBody: z.string().optional(),
|
|
33
|
+
responseStatus: z.number().optional(),
|
|
34
|
+
responseStatusText: z.string().optional(),
|
|
35
|
+
responseHeaders: z.record(z.string(), z.string()).optional(),
|
|
36
|
+
responseBody: z.string().optional(),
|
|
37
|
+
});
|
|
38
|
+
|
|
28
39
|
export const ProviderTestResultSchema = z.object({
|
|
29
40
|
success: z.boolean(),
|
|
30
41
|
error: ProviderTestErrorSchema.optional(),
|
|
@@ -44,6 +55,7 @@ export const ProviderTestResultSchema = z.object({
|
|
|
44
55
|
})
|
|
45
56
|
.optional(),
|
|
46
57
|
requestHeaders: z.record(z.string(), z.string()).optional(),
|
|
58
|
+
debug: ProviderTestDebugSchema.optional(),
|
|
47
59
|
});
|
|
48
60
|
|
|
49
61
|
export const ProviderTestStateSchema = z.union([
|
|
@@ -70,6 +82,7 @@ export const ProviderTestResultsSchema = z.object({
|
|
|
70
82
|
});
|
|
71
83
|
|
|
72
84
|
export type ProviderTestErrorType = z.infer<typeof ProviderTestErrorTypeSchema>;
|
|
85
|
+
export type ProviderTestDebug = z.infer<typeof ProviderTestDebugSchema>;
|
|
73
86
|
export type ProviderTestResult = z.infer<typeof ProviderTestResultSchema>;
|
|
74
87
|
export type ProviderTestState = z.infer<typeof ProviderTestStateSchema>;
|
|
75
88
|
export type ProviderTestResults = z.infer<typeof ProviderTestResultsSchema>;
|
package/src/lib/runtimeConfig.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { z } from "zod";
|
|
|
2
2
|
|
|
3
3
|
export const DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS = 10;
|
|
4
4
|
export const MAX_SLOW_RESPONSE_THRESHOLD_SECONDS = 600;
|
|
5
|
+
export const DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS = 60;
|
|
6
|
+
export const MAX_PROVIDER_TEST_TIMEOUT_SECONDS = 600;
|
|
5
7
|
export const DEFAULT_TIME_DISPLAY_FORMAT = "time";
|
|
6
8
|
export const DEFAULT_CAPTURE_MODE = "simple";
|
|
7
9
|
|
|
@@ -28,6 +30,12 @@ export const RuntimeConfigSchema = z.object({
|
|
|
28
30
|
.min(0)
|
|
29
31
|
.max(MAX_SLOW_RESPONSE_THRESHOLD_SECONDS)
|
|
30
32
|
.default(DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS),
|
|
33
|
+
providerTestTimeoutSeconds: z
|
|
34
|
+
.number()
|
|
35
|
+
.int()
|
|
36
|
+
.min(1)
|
|
37
|
+
.max(MAX_PROVIDER_TEST_TIMEOUT_SECONDS)
|
|
38
|
+
.default(DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS),
|
|
31
39
|
timeDisplayFormat: TimeDisplayFormatSchema.default(DEFAULT_TIME_DISPLAY_FORMAT),
|
|
32
40
|
captureMode: CaptureModeSchema.default(DEFAULT_CAPTURE_MODE),
|
|
33
41
|
});
|
package/src/lib/useOnboarding.ts
CHANGED
|
@@ -2,6 +2,7 @@ import useSWR, { type SWRResponse, useSWRConfig } from "swr";
|
|
|
2
2
|
import { fetchJson } from "./apiClient";
|
|
3
3
|
import {
|
|
4
4
|
DEFAULT_CAPTURE_MODE,
|
|
5
|
+
DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS,
|
|
5
6
|
DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
6
7
|
DEFAULT_TIME_DISPLAY_FORMAT,
|
|
7
8
|
RuntimeConfigSchema,
|
|
@@ -73,6 +74,8 @@ export function useOnboarding(): UseOnboarding {
|
|
|
73
74
|
hasSeenOnboarding: true,
|
|
74
75
|
slowResponseThresholdSeconds:
|
|
75
76
|
response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
77
|
+
providerTestTimeoutSeconds:
|
|
78
|
+
response.data?.providerTestTimeoutSeconds ?? DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS,
|
|
76
79
|
timeDisplayFormat: response.data?.timeDisplayFormat ?? DEFAULT_TIME_DISPLAY_FORMAT,
|
|
77
80
|
captureMode,
|
|
78
81
|
},
|
|
@@ -2,6 +2,7 @@ import useSWR, { type SWRResponse, useSWRConfig } from "swr";
|
|
|
2
2
|
import { fetchJson } from "./apiClient";
|
|
3
3
|
import {
|
|
4
4
|
DEFAULT_CAPTURE_MODE,
|
|
5
|
+
DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS,
|
|
5
6
|
DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
6
7
|
DEFAULT_TIME_DISPLAY_FORMAT,
|
|
7
8
|
type CaptureMode,
|
|
@@ -43,11 +44,13 @@ export type UseStripConfig = {
|
|
|
43
44
|
strip: boolean;
|
|
44
45
|
captureMode: CaptureMode;
|
|
45
46
|
slowResponseThresholdSeconds: number;
|
|
47
|
+
providerTestTimeoutSeconds: number;
|
|
46
48
|
timeDisplayFormat: TimeDisplayFormat;
|
|
47
49
|
isLoading: boolean;
|
|
48
50
|
isError: boolean;
|
|
49
51
|
setStrip: (next: boolean) => Promise<void>;
|
|
50
52
|
setSlowResponseThresholdSeconds: (next: number) => Promise<void>;
|
|
53
|
+
setProviderTestTimeoutSeconds: (next: number) => Promise<void>;
|
|
51
54
|
setTimeDisplayFormat: (next: TimeDisplayFormat) => Promise<void>;
|
|
52
55
|
};
|
|
53
56
|
|
|
@@ -56,6 +59,7 @@ export type UseStripConfig = {
|
|
|
56
59
|
*
|
|
57
60
|
* - `strip` is the live value from the server (or the optimistic value mid-PATCH).
|
|
58
61
|
* - `slowResponseThresholdSeconds` controls the UI's slow-response indicator.
|
|
62
|
+
* - `providerTestTimeoutSeconds` controls each per-model Provider Test probe.
|
|
59
63
|
* - `setStrip(next)` PATCHes the server with optimistic update; on failure
|
|
60
64
|
* it throws and SWR rolls back the optimistic value.
|
|
61
65
|
*/
|
|
@@ -74,6 +78,8 @@ export function useStripConfig(): UseStripConfig {
|
|
|
74
78
|
const captureMode = response.data?.captureMode ?? DEFAULT_CAPTURE_MODE;
|
|
75
79
|
const slowResponseThresholdSeconds =
|
|
76
80
|
response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS;
|
|
81
|
+
const providerTestTimeoutSeconds =
|
|
82
|
+
response.data?.providerTestTimeoutSeconds ?? DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS;
|
|
77
83
|
const timeDisplayFormat = response.data?.timeDisplayFormat ?? DEFAULT_TIME_DISPLAY_FORMAT;
|
|
78
84
|
|
|
79
85
|
const optimisticConfig = (patch: RuntimeConfigPatch): RuntimeConfig => ({
|
|
@@ -81,6 +87,8 @@ export function useStripConfig(): UseStripConfig {
|
|
|
81
87
|
hasSeenOnboarding: response.data?.hasSeenOnboarding ?? false,
|
|
82
88
|
slowResponseThresholdSeconds:
|
|
83
89
|
response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
90
|
+
providerTestTimeoutSeconds:
|
|
91
|
+
response.data?.providerTestTimeoutSeconds ?? DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS,
|
|
84
92
|
timeDisplayFormat: response.data?.timeDisplayFormat ?? DEFAULT_TIME_DISPLAY_FORMAT,
|
|
85
93
|
captureMode,
|
|
86
94
|
...patch,
|
|
@@ -118,15 +126,29 @@ export function useStripConfig(): UseStripConfig {
|
|
|
118
126
|
});
|
|
119
127
|
};
|
|
120
128
|
|
|
129
|
+
const setProviderTestTimeoutSeconds = async (next: number): Promise<void> => {
|
|
130
|
+
await globalMutate(
|
|
131
|
+
STRIP_CONFIG_SWR_KEY,
|
|
132
|
+
setRuntimeConfig({ providerTestTimeoutSeconds: next }),
|
|
133
|
+
{
|
|
134
|
+
optimisticData: optimisticConfig({ providerTestTimeoutSeconds: next }),
|
|
135
|
+
rollbackOnError: true,
|
|
136
|
+
revalidate: false,
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
121
141
|
return {
|
|
122
142
|
strip,
|
|
123
143
|
captureMode,
|
|
124
144
|
slowResponseThresholdSeconds,
|
|
145
|
+
providerTestTimeoutSeconds,
|
|
125
146
|
timeDisplayFormat,
|
|
126
147
|
isLoading: response.isLoading,
|
|
127
148
|
isError: response.error !== undefined,
|
|
128
149
|
setStrip,
|
|
129
150
|
setSlowResponseThresholdSeconds,
|
|
151
|
+
setProviderTestTimeoutSeconds,
|
|
130
152
|
setTimeDisplayFormat,
|
|
131
153
|
};
|
|
132
154
|
}
|
package/src/proxy/config.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { getDataDir } from "./dataDir";
|
|
|
13
13
|
import {
|
|
14
14
|
CaptureModeSchema,
|
|
15
15
|
DEFAULT_CAPTURE_MODE,
|
|
16
|
+
DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS,
|
|
16
17
|
DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
17
18
|
DEFAULT_TIME_DISPLAY_FORMAT,
|
|
18
19
|
RuntimeConfigSchema,
|
|
@@ -98,6 +99,7 @@ function resolveInitialConfig(): RuntimeConfig {
|
|
|
98
99
|
stripClaudeCodeBillingHeader: true,
|
|
99
100
|
hasSeenOnboarding: false,
|
|
100
101
|
slowResponseThresholdSeconds: DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
102
|
+
providerTestTimeoutSeconds: DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS,
|
|
101
103
|
timeDisplayFormat: DEFAULT_TIME_DISPLAY_FORMAT,
|
|
102
104
|
captureMode,
|
|
103
105
|
};
|
|
@@ -108,6 +110,7 @@ function resolveInitialConfig(): RuntimeConfig {
|
|
|
108
110
|
stripClaudeCodeBillingHeader: false,
|
|
109
111
|
hasSeenOnboarding: false,
|
|
110
112
|
slowResponseThresholdSeconds: DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
|
|
113
|
+
providerTestTimeoutSeconds: DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS,
|
|
111
114
|
timeDisplayFormat: DEFAULT_TIME_DISPLAY_FORMAT,
|
|
112
115
|
captureMode,
|
|
113
116
|
};
|
|
@@ -200,6 +203,7 @@ function toPersistedConfig(value: RuntimeConfig): Record<string, unknown> {
|
|
|
200
203
|
stripClaudeCodeBillingHeader: value.stripClaudeCodeBillingHeader,
|
|
201
204
|
hasSeenOnboarding: value.hasSeenOnboarding,
|
|
202
205
|
slowResponseThresholdSeconds: value.slowResponseThresholdSeconds,
|
|
206
|
+
providerTestTimeoutSeconds: value.providerTestTimeoutSeconds,
|
|
203
207
|
timeDisplayFormat: value.timeDisplayFormat,
|
|
204
208
|
};
|
|
205
209
|
}
|