@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.
Files changed (52) hide show
  1. package/.output/cli.js +10 -0
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-CUqYtaH3.js → CompareDrawer-NEdpe1wl.js} +1 -1
  4. package/.output/public/assets/ProxyViewerContainer-Dwhi4Q1c.js +115 -0
  5. package/.output/public/assets/{ReplayDialog-CPXNEqVg.js → ReplayDialog-DijzJwaQ.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-qkj8QgkY.js → RequestAnatomy-CYbosGYy.js} +1 -1
  7. package/.output/public/assets/{ResponseView-BPNLZpuw.js → ResponseView-B3dPM63X.js} +1 -1
  8. package/.output/public/assets/{StreamingChunkSequence-DGs-YFXI.js → StreamingChunkSequence-DYDx3xRY.js} +1 -1
  9. package/.output/public/assets/_sessionId-b1KDnByv.js +1 -0
  10. package/.output/public/assets/index-780zTVwp.css +1 -0
  11. package/.output/public/assets/index-CUyRKgII.js +1 -0
  12. package/.output/public/assets/{main-j3vS8rmP.js → main-N-sjHJUM.js} +2 -2
  13. package/.output/server/_libs/lucide-react.mjs +27 -27
  14. package/.output/server/{_sessionId-Bc5Nohhe.mjs → _sessionId-BR46y_bO.mjs} +2 -2
  15. package/.output/server/_ssr/{CompareDrawer-D0UewydM.mjs → CompareDrawer-DDxpmdPs.mjs} +3 -3
  16. package/.output/server/_ssr/{ProxyViewerContainer-BEUWt2eX.mjs → ProxyViewerContainer-B1zAefzz.mjs} +304 -55
  17. package/.output/server/_ssr/{ReplayDialog-zaN-xMOw.mjs → ReplayDialog-DleMFvMx.mjs} +4 -4
  18. package/.output/server/_ssr/{RequestAnatomy-BMomYged.mjs → RequestAnatomy-B-P9tC3D.mjs} +3 -3
  19. package/.output/server/_ssr/{ResponseView-DSoHCyAM.mjs → ResponseView-DWfGn0i3.mjs} +3 -3
  20. package/.output/server/_ssr/{StreamingChunkSequence-pt3pZOOL.mjs → StreamingChunkSequence-C8kFF6xJ.mjs} +3 -3
  21. package/.output/server/_ssr/{index-BCTKxcgV.mjs → index-DzENi2RO.mjs} +2 -2
  22. package/.output/server/_ssr/index.mjs +2 -2
  23. package/.output/server/_ssr/{router-DpaDa5q5.mjs → router-Dy1VEjxy.mjs} +434 -80
  24. package/.output/server/{_tanstack-start-manifest_v-EsubwO0A.mjs → _tanstack-start-manifest_v-CyzuAD8A.mjs} +1 -1
  25. package/.output/server/index.mjs +55 -55
  26. package/package.json +1 -1
  27. package/src/cli/templates/codex-skill-onboard.ts +5 -0
  28. package/src/cli/templates/skill-onboard.ts +5 -0
  29. package/src/components/OnboardingBanner.tsx +4 -0
  30. package/src/components/ProxyViewer.tsx +5 -0
  31. package/src/components/providers/ProviderCard.tsx +178 -38
  32. package/src/components/providers/ProviderForm.tsx +107 -16
  33. package/src/components/providers/ProvidersPanel.tsx +21 -3
  34. package/src/components/providers/SettingsDialog.tsx +76 -2
  35. package/src/lib/providerModelMetadata.ts +67 -3
  36. package/src/lib/providerTestContract.ts +13 -0
  37. package/src/lib/runtimeConfig.ts +8 -0
  38. package/src/lib/useOnboarding.ts +3 -0
  39. package/src/lib/useStripConfig.ts +22 -0
  40. package/src/proxy/config.ts +4 -0
  41. package/src/proxy/formats/index.ts +1 -0
  42. package/src/proxy/formats/openai/index.ts +1 -0
  43. package/src/proxy/formats/openai/zhipuProvider.ts +31 -0
  44. package/src/proxy/providerImporters.ts +47 -0
  45. package/src/proxy/providers.ts +55 -0
  46. package/src/proxy/upstream.ts +25 -0
  47. package/src/routes/api/config.ts +7 -0
  48. package/src/routes/api/providers.$providerId.test.log.ts +28 -12
  49. package/.output/public/assets/ProxyViewerContainer-CX0-R0nn.js +0 -114
  50. package/.output/public/assets/_sessionId-CXtbrFTj.js +0 -1
  51. package/.output/public/assets/index-DrppF281.js +0 -1
  52. package/.output/public/assets/index-DynnYt7S.css +0 -1
@@ -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_CAPTURE_MODE, a as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, b as DEFAULT_TIME_DISPLAY_FORMAT, c as RuntimeConfigSchema, P as ProviderConfigSchema, r as requestFormatForPath, i as createPendingProviderTestResults, j as ProviderTestResultsSchema, k as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, T as TimeDisplayFormatSchema, m as maskApiKey, f as providerHasContextMetadata, h as findProviderModelMetadata, g as getSessionPath, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, d as AnthropicRequestSchema, s as safeGetOwnProperty, l as resolveProviderContextWindow, K as KnowledgeCandidateSchema, e as stripClaudeCodeBillingHeader, n as isPlainRecord } from "./router-DpaDa5q5.mjs";
2
+ import { C as CapturedLogSchema, D as DEFAULT_CAPTURE_MODE, a as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, b as DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS, c as DEFAULT_TIME_DISPLAY_FORMAT, d as RuntimeConfigSchema, P as ProviderConfigSchema, r as requestFormatForPath, j as createPendingProviderTestResults, k as ProviderTestResultsSchema, l as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, n as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, T as TimeDisplayFormatSchema, m as maskApiKey, h as providerHasContextMetadata, i as findProviderModelMetadata, g as getSessionPath, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, e as AnthropicRequestSchema, s as safeGetOwnProperty, o as resolveProviderContextWindow, K as KnowledgeCandidateSchema, f as stripClaudeCodeBillingHeader, q as isPlainRecord } from "./router-Dy1VEjxy.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 useWindowVirtualizer, a 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, T as Terminal, e as ChevronUp, E as EyeOff, f as Eye, g as ExternalLink, R as RotateCw, h as Clock, i as Pencil, j as Trash2, L as LoaderCircle, k as ArrowUpRight, l as ArrowDownRight, m as ChevronRight, n as User, M as MessageSquare, Z as Zap, W as Wrench, o as TriangleAlert, B as Brain, p as Minus, q as CircleCheckBig, r as CircleQuestionMark, s as Server, G as Gauge, t as Lock, u as Wifi, v as WifiOff, w as CircleX, x as ShieldCheck, y as RefreshCw, z as Save, F as FileSearch, H as CircleCheck, I as CloudUpload, O as OctagonAlert, J as Globe, K as FileTerminal, N as Radio, Q as ChevronsUp, V as ChevronsDown, Y as FileDiff, _ as History, $ as RotateCcw, a0 as Search, a1 as GitCompareArrows, a2 as ArrowUp, a3 as ArrowDown, a4 as Ellipsis, a5 as CodeXml, a6 as Blocks, a7 as MousePointerClick, a8 as FileText, a9 as Rows3, aa as Columns2 } from "../_libs/lucide-react.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, T as Terminal, e as ChevronUp, E as EyeOff, f as Eye, g as ExternalLink, R as RotateCw, h as Clock, i as ChevronRight, j as Pencil, k as Trash2, L as LoaderCircle, l as ArrowUpRight, m as ArrowDownRight, n as User, M as MessageSquare, Z as Zap, W as Wrench, o as TriangleAlert, B as Brain, p as Minus, q as CircleCheckBig, r as CircleX, s as ShieldCheck, t as RefreshCw, u as Save, F as FileSearch, v as CircleCheck, w as CloudUpload, O as OctagonAlert, G as Globe, x as FileTerminal, y as Radio, z as ChevronsUp, H as ChevronsDown, I as FileDiff, J as History, K as RotateCcw, N as Search, Q as GitCompareArrows, V as CircleQuestionMark, Y as Server, _ as Gauge, $ as Lock, a0 as Wifi, a1 as WifiOff, a2 as ArrowUp, a3 as ArrowDown, a4 as Ellipsis, a5 as CodeXml, a6 as Blocks, a7 as MousePointerClick, a8 as FileText, a9 as Rows3, aa as Columns2 } from "../_libs/lucide-react.mjs";
13
13
  import { M as Markdown } from "../_libs/react-markdown.mjs";
14
14
  import { u as union, d as object, a as array, l as literal, b as string, n as number, c as boolean, _ as _enum } from "../_libs/zod.mjs";
15
15
  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";
@@ -75,11 +75,13 @@ function useStripConfig() {
75
75
  const strip = response.data?.stripClaudeCodeBillingHeader ?? false;
76
76
  const captureMode = response.data?.captureMode ?? DEFAULT_CAPTURE_MODE;
77
77
  const slowResponseThresholdSeconds = response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS;
78
+ const providerTestTimeoutSeconds = response.data?.providerTestTimeoutSeconds ?? DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS;
78
79
  const timeDisplayFormat = response.data?.timeDisplayFormat ?? DEFAULT_TIME_DISPLAY_FORMAT;
79
80
  const optimisticConfig = (patch) => ({
80
81
  stripClaudeCodeBillingHeader: response.data?.stripClaudeCodeBillingHeader ?? false,
81
82
  hasSeenOnboarding: response.data?.hasSeenOnboarding ?? false,
82
83
  slowResponseThresholdSeconds: response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
84
+ providerTestTimeoutSeconds: response.data?.providerTestTimeoutSeconds ?? DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS,
83
85
  timeDisplayFormat: response.data?.timeDisplayFormat ?? DEFAULT_TIME_DISPLAY_FORMAT,
84
86
  captureMode,
85
87
  ...patch
@@ -113,15 +115,28 @@ function useStripConfig() {
113
115
  revalidate: false
114
116
  });
115
117
  };
118
+ const setProviderTestTimeoutSeconds = async (next) => {
119
+ await globalMutate(
120
+ STRIP_CONFIG_SWR_KEY,
121
+ setRuntimeConfig({ providerTestTimeoutSeconds: next }),
122
+ {
123
+ optimisticData: optimisticConfig({ providerTestTimeoutSeconds: next }),
124
+ rollbackOnError: true,
125
+ revalidate: false
126
+ }
127
+ );
128
+ };
116
129
  return {
117
130
  strip,
118
131
  captureMode,
119
132
  slowResponseThresholdSeconds,
133
+ providerTestTimeoutSeconds,
120
134
  timeDisplayFormat,
121
135
  isLoading: response.isLoading,
122
136
  isError: response.error !== void 0,
123
137
  setStrip,
124
138
  setSlowResponseThresholdSeconds,
139
+ setProviderTestTimeoutSeconds,
125
140
  setTimeDisplayFormat
126
141
  };
127
142
  }
@@ -164,6 +179,7 @@ function useOnboarding() {
164
179
  stripClaudeCodeBillingHeader: response.data?.stripClaudeCodeBillingHeader ?? false,
165
180
  hasSeenOnboarding: true,
166
181
  slowResponseThresholdSeconds: response.data?.slowResponseThresholdSeconds ?? DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS,
182
+ providerTestTimeoutSeconds: response.data?.providerTestTimeoutSeconds ?? DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS,
167
183
  timeDisplayFormat: response.data?.timeDisplayFormat ?? DEFAULT_TIME_DISPLAY_FORMAT,
168
184
  captureMode
169
185
  },
@@ -234,6 +250,12 @@ function OnboardingBanner() {
234
250
  " ",
235
251
  /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "agent-inspector-onboard" }),
236
252
  "."
253
+ ] }),
254
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
255
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Container networking" }),
256
+ ": if the AI tool runs outside this Inspector container or host, replace ",
257
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "localhost" }),
258
+ " with a reachable address."
237
259
  ] })
238
260
  ] })
239
261
  ] }),
@@ -420,7 +442,7 @@ function useProviders() {
420
442
  mutate: response.mutate
421
443
  };
422
444
  }
423
- const version = "2.0.20";
445
+ const version = "2.0.22";
424
446
  const packageJson = {
425
447
  version
426
448
  };
@@ -1481,19 +1503,19 @@ function useCopyFeedback(text) {
1481
1503
  return { copied, copy };
1482
1504
  }
1483
1505
  const LazyCompareDrawer = reactExports.lazy(
1484
- () => import("./CompareDrawer-D0UewydM.mjs").then((m) => ({ default: m.CompareDrawer }))
1506
+ () => import("./CompareDrawer-DDxpmdPs.mjs").then((m) => ({ default: m.CompareDrawer }))
1485
1507
  );
1486
1508
  const LazyReplayDialog = reactExports.lazy(
1487
- () => import("./ReplayDialog-zaN-xMOw.mjs").then((m) => ({ default: m.ReplayDialog }))
1509
+ () => import("./ReplayDialog-DleMFvMx.mjs").then((m) => ({ default: m.ReplayDialog }))
1488
1510
  );
1489
1511
  const LazyRequestAnatomy = reactExports.lazy(
1490
- () => import("./RequestAnatomy-BMomYged.mjs").then((m) => ({ default: m.RequestAnatomy }))
1512
+ () => import("./RequestAnatomy-B-P9tC3D.mjs").then((m) => ({ default: m.RequestAnatomy }))
1491
1513
  );
1492
1514
  const LazyResponseView = reactExports.lazy(
1493
- () => import("./ResponseView-DSoHCyAM.mjs").then((m) => ({ default: m.ResponseView }))
1515
+ () => import("./ResponseView-DWfGn0i3.mjs").then((m) => ({ default: m.ResponseView }))
1494
1516
  );
1495
1517
  const LazyStreamingChunkSequence = reactExports.lazy(
1496
- () => import("./StreamingChunkSequence-pt3pZOOL.mjs").then((m) => ({
1518
+ () => import("./StreamingChunkSequence-C8kFF6xJ.mjs").then((m) => ({
1497
1519
  default: m.StreamingChunkSequence
1498
1520
  }))
1499
1521
  );
@@ -5599,6 +5621,75 @@ function hasSuccessField(result) {
5599
5621
  function isNotConfiguredState(result) {
5600
5622
  return Object.prototype.hasOwnProperty.call(result, "notConfigured");
5601
5623
  }
5624
+ function hasText(value) {
5625
+ return value !== void 0 && value.trim() !== "";
5626
+ }
5627
+ function formatHeaders(headers) {
5628
+ if (headers === void 0) {
5629
+ return void 0;
5630
+ }
5631
+ const lines = Object.entries(headers).map(([name, value]) => `${name}: ${value}`);
5632
+ if (lines.length === 0) {
5633
+ return void 0;
5634
+ }
5635
+ return lines.join("\n");
5636
+ }
5637
+ function hasHeaderDetails(headers) {
5638
+ return formatHeaders(headers) !== void 0;
5639
+ }
5640
+ function hasDiagnosticDetails(result) {
5641
+ const debug = result.debug;
5642
+ return hasText(result.error?.hint) || hasText(result.error?.details) || hasText(result.rawResponse) || hasText(debug?.requestUrl) || hasText(debug?.requestBody) || hasText(debug?.responseBody) || hasHeaderDetails(debug?.requestHeaders) || hasHeaderDetails(debug?.responseHeaders) || debug?.responseStatus !== void 0;
5643
+ }
5644
+ function DiagnosticSection({
5645
+ title,
5646
+ children
5647
+ }) {
5648
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
5649
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium text-foreground/80", children: title }),
5650
+ children
5651
+ ] });
5652
+ }
5653
+ function DiagnosticPre({ value }) {
5654
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "max-h-40 overflow-auto whitespace-pre-wrap break-all rounded-md bg-background/80 p-2 font-mono text-[11px] leading-relaxed text-foreground/80", children: value });
5655
+ }
5656
+ function ProviderTestDetails({ result }) {
5657
+ const debug = result.debug;
5658
+ const requestUrl = debug?.requestUrl;
5659
+ const requestMethod = debug?.requestMethod ?? "POST";
5660
+ const requestHeaders = formatHeaders(debug?.requestHeaders);
5661
+ const responseHeaders = formatHeaders(debug?.responseHeaders);
5662
+ const responseStatus = debug?.responseStatus !== void 0 ? `${debug.responseStatus}${hasText(debug.responseStatusText) ? ` ${debug.responseStatusText}` : ""}` : void 0;
5663
+ const responseBody = debug?.responseBody ?? result.rawResponse;
5664
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-h-80 max-w-full space-y-2 overflow-auto rounded-md border bg-muted/50 p-2 text-xs text-muted-foreground shadow-sm sm:w-[520px]", children: [
5665
+ hasText(result.error?.hint) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Hint", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: result.error.hint }) }),
5666
+ hasText(result.error?.details) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Error", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: result.error.details }) }),
5667
+ hasText(requestUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Request", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md bg-background/80 p-2 font-mono text-[11px] break-all text-foreground/80", children: [
5668
+ requestMethod,
5669
+ " ",
5670
+ requestUrl
5671
+ ] }) }),
5672
+ requestHeaders !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Request Headers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: requestHeaders }) }),
5673
+ hasText(debug?.requestBody) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Request Body", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: debug.requestBody }) }),
5674
+ hasText(responseStatus) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Response Status", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md bg-background/80 p-2 font-mono text-[11px] text-foreground/80", children: responseStatus }) }),
5675
+ responseHeaders !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Response Headers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: responseHeaders }) }),
5676
+ hasText(responseBody) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Response Body", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: responseBody }) })
5677
+ ] });
5678
+ }
5679
+ function ProviderFormatTestStatus({
5680
+ results
5681
+ }) {
5682
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [
5683
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
5684
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground", children: "Non-stream" }),
5685
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: results.nonStreaming })
5686
+ ] }),
5687
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-col gap-1", children: [
5688
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground", children: "Stream" }),
5689
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: results.streaming })
5690
+ ] })
5691
+ ] });
5692
+ }
5602
5693
  function getErrorIcon(type) {
5603
5694
  const iconProps = { className: "size-3", strokeWidth: 2 };
5604
5695
  switch (type) {
@@ -5686,9 +5777,8 @@ function TestStatus({ result }) {
5686
5777
  }
5687
5778
  const error = result.error;
5688
5779
  const errorMessage = error?.message ?? "Connection failed";
5689
- const errorHint = error?.hint;
5690
- const errorDetails = error?.details;
5691
5780
  const errorType = error?.type ?? "unknown";
5781
+ const hasDetails = hasDiagnosticDetails(result);
5692
5782
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 shrink-0", children: [
5693
5783
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-xs text-red-600 max-w-[200px]", children: [
5694
5784
  getErrorIcon(errorType),
@@ -5696,23 +5786,22 @@ function TestStatus({ result }) {
5696
5786
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: errorMessage }) }),
5697
5787
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Connection test failed" })
5698
5788
  ] }) }),
5699
- errorDetails !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
5700
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5701
- "button",
5702
- {
5703
- type: "button",
5704
- onClick: () => setShowDetails(!showDetails),
5705
- className: "shrink-0 text-muted-foreground hover:text-foreground transition-colors",
5706
- children: showDetails ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "size-3" })
5707
- }
5708
- ) }),
5709
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: showDetails ? "Hide error details" : "Show detailed error information" })
5710
- ] }) })
5789
+ hasDetails && /* @__PURE__ */ jsxRuntimeExports.jsxs(
5790
+ "button",
5791
+ {
5792
+ type: "button",
5793
+ onClick: () => setShowDetails((value) => !value),
5794
+ "aria-expanded": showDetails,
5795
+ "aria-label": showDetails ? "Hide provider test diagnostics" : "Show provider test diagnostics",
5796
+ className: "inline-flex h-5 shrink-0 items-center gap-1 rounded border border-border/70 bg-background/70 px-1.5 text-[10px] text-muted-foreground transition-colors hover:text-foreground",
5797
+ children: [
5798
+ showDetails ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "size-3" }),
5799
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: showDetails ? "Hide" : "Details" })
5800
+ ]
5801
+ }
5802
+ )
5711
5803
  ] }),
5712
- showDetails && errorDetails !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground bg-muted/50 rounded p-2 max-w-[300px]", children: [
5713
- errorHint !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-1", children: errorHint }),
5714
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono whitespace-pre-wrap break-all text-red-400/80", children: errorDetails })
5715
- ] })
5804
+ showDetails && hasDetails && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderTestDetails, { result })
5716
5805
  ] });
5717
5806
  }
5718
5807
  function formatTimeAgo(isoString) {
@@ -5886,21 +5975,21 @@ function ProviderCard({
5886
5975
  }
5887
5976
  )
5888
5977
  ] }),
5889
- provider.anthropicBaseUrl !== void 0 && provider.anthropicBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
5890
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground min-w-0 flex-1", children: [
5978
+ provider.anthropicBaseUrl !== void 0 && provider.anthropicBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [
5979
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 text-xs text-muted-foreground", children: [
5891
5980
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: "Anthropic:" }),
5892
5981
  " ",
5893
5982
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: provider.anthropicBaseUrl })
5894
5983
  ] }),
5895
- testResults && /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: testResults.anthropic.nonStreaming })
5984
+ testResults !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: testResults.anthropic })
5896
5985
  ] }),
5897
- provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
5898
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground min-w-0 flex-1", children: [
5986
+ provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [
5987
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 text-xs text-muted-foreground", children: [
5899
5988
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium", children: "OpenAI:" }),
5900
5989
  " ",
5901
5990
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: provider.openaiBaseUrl })
5902
5991
  ] }),
5903
- testResults && /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: testResults.openai.nonStreaming })
5992
+ testResults !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: testResults.openai })
5904
5993
  ] }),
5905
5994
  testResults?.testedAt !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [
5906
5995
  /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
@@ -5917,7 +6006,7 @@ function ProviderCard({
5917
6006
  onClick: () => setShowModelResults((value) => !value),
5918
6007
  className: "text-xs text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1",
5919
6008
  children: [
5920
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: showModelResults ? "▾" : "" }),
6009
+ showModelResults ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3" }),
5921
6010
  "Model Test Results (",
5922
6011
  Object.keys(testResults.models).length,
5923
6012
  ")"
@@ -5932,8 +6021,8 @@ function ProviderCard({
5932
6021
  ] }) }),
5933
6022
  /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { children: Object.entries(testResults.models).map(([modelName, modelResult]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-border/50 last:border-0", children: [
5934
6023
  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1 px-2 font-medium", children: modelName }),
5935
- provider.anthropicBaseUrl !== void 0 && provider.anthropicBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1 px-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: modelResult.anthropic.nonStreaming }) }),
5936
- provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1 px-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: modelResult.openai.nonStreaming }) })
6024
+ provider.anthropicBaseUrl !== void 0 && provider.anthropicBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1 px-2 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: modelResult.anthropic }) }),
6025
+ provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1 px-2 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: modelResult.openai }) })
5937
6026
  ] }, modelName)) })
5938
6027
  ] }) })
5939
6028
  ] }),
@@ -5985,19 +6074,73 @@ function ProviderCard({
5985
6074
  }
5986
6075
  );
5987
6076
  }
6077
+ const ZHIPU_PROVIDER_KEYWORDS = ["zhipu", "zhipuai", "bigmodel", "z.ai", "zai", "glm"];
6078
+ const ZHIPU_CODING_PROVIDER_KEYWORDS = [
6079
+ "zhipu coding",
6080
+ "glm coding",
6081
+ "bigmodel coding",
6082
+ "z.ai coding",
6083
+ "zai coding"
6084
+ ];
5988
6085
  const KNOWN_PROVIDER_PRESETS = {
5989
6086
  deepseek: {
5990
- format: "openai",
5991
- baseUrl: "https://api.deepseek.com"
6087
+ openaiBaseUrl: "https://api.deepseek.com"
5992
6088
  },
5993
6089
  minimax: {
5994
- format: "anthropic",
5995
- baseUrl: "https://api.minimaxi.com/anthropic",
6090
+ anthropicBaseUrl: "https://api.minimaxi.com/anthropic",
5996
6091
  apiDocsUrl: "https://platform.minimaxi.com/docs/api-reference/api-overview"
5997
6092
  },
6093
+ "zhipu coding": {
6094
+ anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
6095
+ openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
6096
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start"
6097
+ },
6098
+ "glm coding": {
6099
+ anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
6100
+ openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
6101
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start"
6102
+ },
6103
+ "bigmodel coding": {
6104
+ anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
6105
+ openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
6106
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start"
6107
+ },
6108
+ "z.ai coding": {
6109
+ anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
6110
+ openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
6111
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start"
6112
+ },
6113
+ "zai coding": {
6114
+ anthropicBaseUrl: "https://open.bigmodel.cn/api/anthropic",
6115
+ openaiBaseUrl: "https://open.bigmodel.cn/api/coding/paas/v4",
6116
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/coding-plan/quick-start"
6117
+ },
6118
+ zhipu: {
6119
+ openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
6120
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction"
6121
+ },
6122
+ zhipuai: {
6123
+ openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
6124
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction"
6125
+ },
6126
+ bigmodel: {
6127
+ openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
6128
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction"
6129
+ },
6130
+ "z.ai": {
6131
+ openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
6132
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction"
6133
+ },
6134
+ zai: {
6135
+ openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
6136
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction"
6137
+ },
6138
+ glm: {
6139
+ openaiBaseUrl: "https://open.bigmodel.cn/api/paas/v4",
6140
+ apiDocsUrl: "https://docs.bigmodel.cn/cn/guide/develop/openai/introduction"
6141
+ },
5998
6142
  alibaba: {
5999
- format: "openai",
6000
- baseUrl: "https://dashscope.aliyuncs.com/compatible-mode"
6143
+ openaiBaseUrl: "https://dashscope.aliyuncs.com/compatible-mode"
6001
6144
  }
6002
6145
  };
6003
6146
  const MINIMAX_MODELS = [
@@ -6011,6 +6154,20 @@ const MINIMAX_MODELS = [
6011
6154
  "MiniMax M2"
6012
6155
  ];
6013
6156
  const ALIBABA_MODELS = ["glm-5", "glm-5.1", "glm-5.2", "qwen3.6-plus", "qwen3.7-max"];
6157
+ const ZHIPU_MODELS = [
6158
+ "glm-5.2",
6159
+ "glm-5.1",
6160
+ "glm-5",
6161
+ "glm-5-turbo",
6162
+ "glm-4.7",
6163
+ "glm-4.7-flashx",
6164
+ "glm-4.6",
6165
+ "glm-4.5",
6166
+ "glm-4.5-air",
6167
+ "glm-4.5-x",
6168
+ "glm-4.5-airx",
6169
+ "glm-4.5-flash"
6170
+ ];
6014
6171
  function emptyMetadataDraft() {
6015
6172
  return { contextWindow: "", outputLimit: "" };
6016
6173
  }
@@ -6081,6 +6238,10 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
6081
6238
  const [manualOpenaiUrlOverride, setManualOpenaiUrlOverride] = reactExports.useState(false);
6082
6239
  const isMiniMax = name.toLowerCase().includes("minimax");
6083
6240
  const isAlibaba = name.toLowerCase().includes("alibaba");
6241
+ const isZhipu = [...ZHIPU_PROVIDER_KEYWORDS, ...ZHIPU_CODING_PROVIDER_KEYWORDS].some(
6242
+ (keyword) => name.toLowerCase().includes(keyword)
6243
+ );
6244
+ const suggestedModels = isMiniMax ? MINIMAX_MODELS : isZhipu ? ZHIPU_MODELS : isAlibaba ? ALIBABA_MODELS : [];
6084
6245
  const hasContextMetadata = provider?.modelMetadata?.some((metadata) => metadata.contextWindow !== void 0) ?? false;
6085
6246
  function handleCopy() {
6086
6247
  navigator.clipboard.writeText(apiKey).catch(() => {
@@ -6108,10 +6269,11 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
6108
6269
  const lowerName = name.toLowerCase();
6109
6270
  for (const [keyword, preset] of Object.entries(KNOWN_PROVIDER_PRESETS)) {
6110
6271
  if (lowerName.includes(keyword)) {
6111
- if (preset.format === "anthropic" && !manualAnthropicUrlOverride) {
6112
- setAnthropicBaseUrl(preset.baseUrl);
6113
- } else if (preset.format === "openai" && !manualOpenaiUrlOverride) {
6114
- setOpenaiBaseUrl(preset.baseUrl);
6272
+ if (preset.anthropicBaseUrl !== void 0 && !manualAnthropicUrlOverride) {
6273
+ setAnthropicBaseUrl(preset.anthropicBaseUrl);
6274
+ }
6275
+ if (preset.openaiBaseUrl !== void 0 && !manualOpenaiUrlOverride) {
6276
+ setOpenaiBaseUrl(preset.openaiBaseUrl);
6115
6277
  }
6116
6278
  if (preset.apiDocsUrl !== void 0 && !apiDocsUrl) {
6117
6279
  setApiDocsUrl(preset.apiDocsUrl);
@@ -6122,6 +6284,9 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
6122
6284
  if (keyword === "alibaba" && models.length === 1 && models[0] === "") {
6123
6285
  setModels([ALIBABA_MODELS[0] ?? ""]);
6124
6286
  }
6287
+ if ((ZHIPU_PROVIDER_KEYWORDS.includes(keyword) || ZHIPU_CODING_PROVIDER_KEYWORDS.includes(keyword)) && models.length === 1 && models[0] === "") {
6288
+ setModels([ZHIPU_MODELS[0] ?? ""]);
6289
+ }
6125
6290
  break;
6126
6291
  }
6127
6292
  }
@@ -6295,7 +6460,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
6295
6460
  }
6296
6461
  ),
6297
6462
  errors.modelMetadataUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.modelMetadataUrl }),
6298
- modelMetadataUrl.trim() === "" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md border border-border bg-muted/20 px-3 py-2 text-xs text-muted-foreground", children: "Built-in limits cover DeepSeek v4 pro/flash and MiniMax M3/M2.7/M2.5/M2.1. Use a registry URL for private or custom model limits." }),
6463
+ modelMetadataUrl.trim() === "" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md border border-border bg-muted/20 px-3 py-2 text-xs text-muted-foreground", children: "Built-in limits cover DeepSeek v4 pro/flash, MiniMax M3/M2.7/M2.5/M2.1, and GLM coding models. Use a registry URL for private or custom model limits." }),
6299
6464
  provider !== void 0 && !hasContextMetadata && modelMetadataUrl.trim() === "" && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-amber-400", children: "This provider has no matching context metadata yet." }),
6300
6465
  modelMetadataUrl.trim() !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Agent Inspector will refresh model limits from this JSON registry after save." })
6301
6466
  ] }),
@@ -6368,11 +6533,11 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
6368
6533
  type: "text",
6369
6534
  value: m,
6370
6535
  onChange: (e) => updateModel(i, e.target.value),
6371
- placeholder: isMiniMax || isAlibaba ? "Type or select a model..." : "Model name",
6536
+ placeholder: suggestedModels.length > 0 ? "Type or select a model..." : "Model name",
6372
6537
  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"
6373
6538
  }
6374
6539
  ),
6375
- (isMiniMax || isAlibaba) && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6540
+ suggestedModels.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
6376
6541
  /* @__PURE__ */ jsxRuntimeExports.jsx(
6377
6542
  "button",
6378
6543
  {
@@ -6383,7 +6548,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
6383
6548
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" })
6384
6549
  }
6385
6550
  ),
6386
- openModelDropdown === i && /* @__PURE__ */ jsxRuntimeExports.jsx("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", children: (isMiniMax ? MINIMAX_MODELS : ALIBABA_MODELS).map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx(
6551
+ openModelDropdown === i && /* @__PURE__ */ jsxRuntimeExports.jsx("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", children: suggestedModels.map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx(
6387
6552
  "button",
6388
6553
  {
6389
6554
  type: "button",
@@ -6581,7 +6746,6 @@ const ModelMetadataRefreshResponseSchema = object({
6581
6746
  missingModels: array(string()),
6582
6747
  sourceUrl: string()
6583
6748
  });
6584
- const TEST_TIMEOUT_SECONDS = 30;
6585
6749
  const NETWORK_ERROR_MESSAGE = "Network error: could not reach the server. Is the proxy running?";
6586
6750
  function createProviderPayload(data) {
6587
6751
  return {
@@ -6596,6 +6760,10 @@ function createProviderPayload(data) {
6596
6760
  source: data.source
6597
6761
  };
6598
6762
  }
6763
+ function getProviderTestTimeoutSeconds(provider, timeoutSecondsPerModel) {
6764
+ const modelCount = provider?.models?.map((model) => model.trim()).filter((model) => model !== "").length ?? 0;
6765
+ return Math.max(1, modelCount) * timeoutSecondsPerModel;
6766
+ }
6599
6767
  async function persistProviderTestLog(providerId, results) {
6600
6768
  if (results.models === void 0) {
6601
6769
  return;
@@ -6638,6 +6806,7 @@ function ProvidersPanel({
6638
6806
  const [refreshingMetadataProviders, setRefreshingMetadataProviders] = reactExports.useState(
6639
6807
  /* @__PURE__ */ new Set()
6640
6808
  );
6809
+ const { providerTestTimeoutSeconds } = useStripConfig();
6641
6810
  const listScrollRef = reactExports.useRef(null);
6642
6811
  const highlightTimeoutRef = reactExports.useRef(null);
6643
6812
  const providers = externalProviders ?? [];
@@ -6733,7 +6902,9 @@ function ProvidersPanel({
6733
6902
  updateTestResults(providerId, createPendingProviderTestResults());
6734
6903
  updateTestingState(providerId, true);
6735
6904
  const controller = new AbortController();
6736
- let remaining = TEST_TIMEOUT_SECONDS;
6905
+ const provider = providers.find((candidate) => candidate.id === providerId);
6906
+ const timeoutSeconds = getProviderTestTimeoutSeconds(provider, providerTestTimeoutSeconds);
6907
+ let remaining = timeoutSeconds;
6737
6908
  setTestingTimeLeft(providerId, remaining);
6738
6909
  const intervalId = setInterval(() => {
6739
6910
  remaining--;
@@ -6775,7 +6946,13 @@ function ProvidersPanel({
6775
6946
  updateTestingState(providerId, false);
6776
6947
  }
6777
6948
  },
6778
- [setTestingTimeLeft, updateTestResults, updateTestingState]
6949
+ [
6950
+ providerTestTimeoutSeconds,
6951
+ providers,
6952
+ setTestingTimeLeft,
6953
+ updateTestResults,
6954
+ updateTestingState
6955
+ ]
6779
6956
  );
6780
6957
  const refreshProviderMetadata = reactExports.useCallback(
6781
6958
  async (providerId, url, showError = true) => {
@@ -7225,6 +7402,7 @@ function StorageSettingsTab() {
7225
7402
  const [stats, setStats] = reactExports.useState(null);
7226
7403
  const [error, setError] = reactExports.useState(null);
7227
7404
  const [loading, setLoading] = reactExports.useState(false);
7405
+ const [copiedId, setCopiedId] = reactExports.useState(null);
7228
7406
  const refresh = reactExports.useCallback(async () => {
7229
7407
  setLoading(true);
7230
7408
  setError(null);
@@ -7250,6 +7428,12 @@ function StorageSettingsTab() {
7250
7428
  reactExports.useEffect(() => {
7251
7429
  void refresh();
7252
7430
  }, [refresh]);
7431
+ const handleCopy = reactExports.useCallback((id, value) => {
7432
+ void window.navigator.clipboard.writeText(value).then(() => {
7433
+ setCopiedId(id);
7434
+ setTimeout(() => setCopiedId(null), 1600);
7435
+ });
7436
+ }, []);
7253
7437
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
7254
7438
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-3", children: [
7255
7439
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
@@ -7259,6 +7443,26 @@ function StorageSettingsTab() {
7259
7443
  /* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => void refresh(), children: loading ? "Refreshing..." : "Refresh" })
7260
7444
  ] }),
7261
7445
  stats !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-2 text-xs", children: [
7446
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
7447
+ CopyableSetupValue,
7448
+ {
7449
+ id: "log-dir",
7450
+ label: "Log storage path",
7451
+ value: stats.logDir,
7452
+ copiedId,
7453
+ onCopy: handleCopy
7454
+ }
7455
+ ),
7456
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
7457
+ CopyableSetupValue,
7458
+ {
7459
+ id: "chunk-dir",
7460
+ label: "Streaming chunks storage path",
7461
+ value: stats.chunkDir,
7462
+ copiedId,
7463
+ onCopy: handleCopy
7464
+ }
7465
+ ),
7262
7466
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border bg-muted/20 px-3 py-2", children: [
7263
7467
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium", children: "In-memory logs" }),
7264
7468
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 font-mono text-muted-foreground", children: [
@@ -7268,7 +7472,6 @@ function StorageSettingsTab() {
7268
7472
  ] }),
7269
7473
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border bg-muted/20 px-3 py-2", children: [
7270
7474
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium", children: "Log files" }),
7271
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 break-all font-mono text-muted-foreground", children: stats.logDir }),
7272
7475
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 font-mono text-muted-foreground", children: [
7273
7476
  stats.logFileCount.toLocaleString(),
7274
7477
  " files / ",
@@ -7277,7 +7480,6 @@ function StorageSettingsTab() {
7277
7480
  ] }),
7278
7481
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border bg-muted/20 px-3 py-2", children: [
7279
7482
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium", children: "Streaming chunks" }),
7280
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 break-all font-mono text-muted-foreground", children: stats.chunkDir }),
7281
7483
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 font-mono text-muted-foreground", children: [
7282
7484
  stats.chunkFileCount.toLocaleString(),
7283
7485
  " files / ",
@@ -7378,6 +7580,10 @@ function OnboardingSettingsTab() {
7378
7580
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
7379
7581
  /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5 text-emerald-500" }),
7380
7582
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Captured sessions can produce reviewable memory candidates." })
7583
+ ] }),
7584
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-2", children: [
7585
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "mt-0.5 size-3.5 text-emerald-500" }),
7586
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "If your AI tool runs in another container or host, replace localhost with an address reachable from that tool and verify the network/firewall path." })
7381
7587
  ] })
7382
7588
  ] })
7383
7589
  ] });
@@ -7387,10 +7593,12 @@ function ProxySettingsTab() {
7387
7593
  strip,
7388
7594
  captureMode,
7389
7595
  slowResponseThresholdSeconds,
7596
+ providerTestTimeoutSeconds,
7390
7597
  timeDisplayFormat,
7391
7598
  isLoading,
7392
7599
  setStrip,
7393
7600
  setSlowResponseThresholdSeconds,
7601
+ setProviderTestTimeoutSeconds,
7394
7602
  setTimeDisplayFormat
7395
7603
  } = useStripConfig();
7396
7604
  const [error, setError] = reactExports.useState(null);
@@ -7424,6 +7632,20 @@ function ProxySettingsTab() {
7424
7632
  },
7425
7633
  [setSlowResponseThresholdSeconds]
7426
7634
  );
7635
+ const handleProviderTestTimeoutChange = reactExports.useCallback(
7636
+ async (next) => {
7637
+ setError(null);
7638
+ setPending(true);
7639
+ try {
7640
+ await setProviderTestTimeoutSeconds(next);
7641
+ } catch (err) {
7642
+ setError(err instanceof Error ? err.message : String(err));
7643
+ } finally {
7644
+ setPending(false);
7645
+ }
7646
+ },
7647
+ [setProviderTestTimeoutSeconds]
7648
+ );
7427
7649
  const handleTimeDisplayFormatChange = reactExports.useCallback(
7428
7650
  async (next) => {
7429
7651
  setError(null);
@@ -7513,6 +7735,32 @@ function ProxySettingsTab() {
7513
7735
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: "seconds" })
7514
7736
  ] })
7515
7737
  ] }),
7738
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
7739
+ /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-test-timeout", className: "text-sm font-semibold", children: "Provider test timeout" }),
7740
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Provider Test waits this many seconds per configured model before aborting slow probes. The full test window scales with model count." }),
7741
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
7742
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
7743
+ "input",
7744
+ {
7745
+ id: "provider-test-timeout",
7746
+ type: "number",
7747
+ min: 1,
7748
+ max: MAX_PROVIDER_TEST_TIMEOUT_SECONDS,
7749
+ step: 1,
7750
+ value: providerTestTimeoutSeconds,
7751
+ disabled: isLoading || pending,
7752
+ onChange: (e) => {
7753
+ const next = Number(e.currentTarget.value);
7754
+ if (!Number.isInteger(next)) return;
7755
+ if (next < 1 || next > MAX_PROVIDER_TEST_TIMEOUT_SECONDS) return;
7756
+ void handleProviderTestTimeoutChange(next);
7757
+ },
7758
+ 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"
7759
+ }
7760
+ ),
7761
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground", children: "seconds / model" })
7762
+ ] })
7763
+ ] }),
7516
7764
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
7517
7765
  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "time-display-format", className: "text-sm font-semibold", children: "Time display" }),
7518
7766
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Controls timestamps in session summaries, conversation headers, and log rows." }),
@@ -9434,7 +9682,8 @@ function ProxyViewer({
9434
9682
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-2", children: [
9435
9683
  /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "ANTHROPIC_BASE_URL=http://localhost:25947/proxy <your-tool>" }),
9436
9684
  /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "OPENAI_BASE_URL=http://localhost:25947/proxy <your-tool>" })
9437
- ] })
9685
+ ] }),
9686
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mx-auto max-w-xl text-xs leading-relaxed text-muted-foreground", children: "Container note: if your AI tool runs in a different container or host than Agent Inspector, replace localhost with an address reachable from that tool and verify the network/firewall path." })
9438
9687
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
9439
9688
  "div",
9440
9689
  {