@tonyclaw/agent-inspector 3.0.25 → 3.0.26

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 (37) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-BnvOPwpe.js → CompareDrawer-SZRUDQc9.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-BJvwOrSi.js +4234 -0
  4. package/.output/public/assets/{ReplayDialog-BJgoQWdW.js → ReplayDialog-DgfmqiM3.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-CpBZEHnM.js → RequestAnatomy-CS6c7Qfn.js} +1 -1
  6. package/.output/public/assets/{ResponseView-CWmyRb2M.js → ResponseView-C0Ri_5gm.js} +2 -2
  7. package/.output/public/assets/{StreamingChunkSequence-BWhC8bwx.js → StreamingChunkSequence-7dYcHWyZ.js} +1 -1
  8. package/.output/public/assets/{_sessionId-Cq-2OqUU.js → _sessionId-D-V84cgw.js} +1 -1
  9. package/.output/public/assets/{index-BKi9IRtV.js → index-CSIL7-MK.js} +3 -3
  10. package/.output/public/assets/index-CmeF5XXS.css +1 -0
  11. package/.output/public/assets/{index-Cf8O0Oos.js → index-DgFWCxOs.js} +1 -1
  12. package/.output/public/assets/{index-DdgTJ4QJ.js → index-DrVJsks2.js} +1 -1
  13. package/.output/public/assets/{json-viewer-Bqu_4hmm.js → json-viewer-C9Ji3gYN.js} +1 -1
  14. package/.output/server/_libs/three.mjs +26016 -0
  15. package/.output/server/{_sessionId-Ba7H3Gjw.mjs → _sessionId-BQ6YQOO1.mjs} +7 -6
  16. package/.output/server/_ssr/{CompareDrawer-aH59Ab6x.mjs → CompareDrawer-CqNzIeMR.mjs} +7 -6
  17. package/.output/server/_ssr/{ProxyViewerContainer-By-I0tJv.mjs → ProxyViewerContainer-CMC7Vsbw.mjs} +785 -311
  18. package/.output/server/_ssr/{ReplayDialog-BO4Vy0et.mjs → ReplayDialog-D9p_LaTr.mjs} +8 -7
  19. package/.output/server/_ssr/{RequestAnatomy-FtOr7vSS.mjs → RequestAnatomy-B4ge3qNZ.mjs} +7 -6
  20. package/.output/server/_ssr/{ResponseView-cvw6ajue.mjs → ResponseView-BCqMKApI.mjs} +7 -6
  21. package/.output/server/_ssr/{StreamingChunkSequence-Oc8S2kD6.mjs → StreamingChunkSequence-BA4zTLE4.mjs} +7 -6
  22. package/.output/server/_ssr/{index-BDRlQheR.mjs → index-CzxhDjlq.mjs} +7 -6
  23. package/.output/server/_ssr/index.mjs +2 -2
  24. package/.output/server/_ssr/{json-viewer-Cd6trhQr.mjs → json-viewer-B3qV6iEJ.mjs} +7 -6
  25. package/.output/server/_ssr/{router-bofP-Ekl.mjs → router-CZe_R31M.mjs} +4 -4
  26. package/.output/server/{_tanstack-start-manifest_v-BPuf86qB.mjs → _tanstack-start-manifest_v-B50VTLrH.mjs} +1 -1
  27. package/.output/server/index.mjs +68 -68
  28. package/package.json +3 -1
  29. package/src/components/inspector-pet/InspectorPet.tsx +66 -51
  30. package/src/components/inspector-pet/InspectorPetCrab3D.tsx +455 -0
  31. package/src/components/inspector-pet/inspectorPetLogic.ts +28 -43
  32. package/src/components/providers/ProviderForm.tsx +238 -163
  33. package/src/components/providers/ProvidersPanel.tsx +16 -6
  34. package/src/components/providers/providerFormCapabilities.ts +36 -0
  35. package/styles/globals.css +0 -61
  36. package/.output/public/assets/ProxyViewerContainer-DcqFqo1m.js +0 -127
  37. package/.output/public/assets/index-BMYuDL5X.css +0 -1
@@ -1,5 +1,5 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, R as React } from "../_libs/react.mjs";
2
- import { C as CapturedLogSchema, z as DeleteInspectorGroupsResponseSchema, 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, g as readStoredUiScalePreference, h as persistUiScalePreference, i as applyUiScalePreference, w as extractStopReason, p as packageJson, P as ProviderConfigSchema, U as UI_SCALE_STORAGE_KEY, j as normalizeUiScalePreference, q as apiFormatForPath, B as GroupEvidenceExportResultSchema, E as DeleteInspectorGroupResponseSchema, $ as PiAgentResponseSchema, v as isTurnBoundary, r as readStoredThemeMode, e as applyThemeMode, f as persistThemeMode, a2 as formatUiScale, x as AlertSummarySchema, y as AlertListResponseSchema, I as InspectorGroupsListResponseSchema, G as GroupEvidenceReadResponseSchema, V as createPendingProviderTestResults, W as ProviderTestResultsSchema, X as createFailedProviderTestResults, Y as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, Z as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, _ as TimeDisplayFormatSchema, T as THEME_MODE_STORAGE_KEY, n as normalizeThemeMode, H as maskApiKey, M as PATH_V1_MESSAGES, N as PATH_V1_CHAT_COMPLETIONS, Q as PATH_V1_RESPONSES, F as providerHasContextMetadata, l as parseOpenAIResponse, m as parseOpenAIResponsesResponse, O as OpenAIRequestSchema, o as OpenAIResponsesRequestSchema, A as AnthropicResponseSchema$1, k as AnthropicRequestSchema, s as safeGetOwnProperty, a0 as resolveProviderContextWindow, t as getSessionPath, J as findProviderModelMetadata, S as previewUpstreamUrl, K as KnowledgeCandidateSchema, a1 as isPlainRecord, u as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema } from "./router-bofP-Ekl.mjs";
2
+ import { C as CapturedLogSchema, z as DeleteInspectorGroupsResponseSchema, 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, g as readStoredUiScalePreference, h as persistUiScalePreference, i as applyUiScalePreference, w as extractStopReason, p as packageJson, P as ProviderConfigSchema, U as UI_SCALE_STORAGE_KEY, j as normalizeUiScalePreference, q as apiFormatForPath, B as GroupEvidenceExportResultSchema, E as DeleteInspectorGroupResponseSchema, $ as PiAgentResponseSchema, v as isTurnBoundary, r as readStoredThemeMode, e as applyThemeMode, f as persistThemeMode, a2 as formatUiScale, x as AlertSummarySchema, y as AlertListResponseSchema, I as InspectorGroupsListResponseSchema, G as GroupEvidenceReadResponseSchema, V as createPendingProviderTestResults, W as ProviderTestResultsSchema, X as createFailedProviderTestResults, Y as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, Z as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, _ as TimeDisplayFormatSchema, T as THEME_MODE_STORAGE_KEY, n as normalizeThemeMode, M as PATH_V1_MESSAGES, N as PATH_V1_CHAT_COMPLETIONS, Q as PATH_V1_RESPONSES, H as maskApiKey, F as providerHasContextMetadata, l as parseOpenAIResponse, m as parseOpenAIResponsesResponse, O as OpenAIRequestSchema, o as OpenAIResponsesRequestSchema, A as AnthropicResponseSchema$1, k as AnthropicRequestSchema, s as safeGetOwnProperty, a0 as resolveProviderContextWindow, t as getSessionPath, J as findProviderModelMetadata, S as previewUpstreamUrl, K as KnowledgeCandidateSchema, a1 as isPlainRecord, u as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema } from "./router-CZe_R31M.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";
@@ -7,6 +7,8 @@ import { t as twMerge } from "../_libs/tailwind-merge.mjs";
7
7
  import { c as cva } from "../_libs/class-variance-authority.mjs";
8
8
  import { u as useWindowVirtualizer, a as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
9
9
  import { d as diffJson, a as diffLines } from "../_libs/diff.mjs";
10
+ import { r as reactDomExports } from "../_libs/react-dom.mjs";
11
+ import { W as WebGLRenderer, S as SRGBColorSpace, A as ACESFilmicToneMapping, a as Scene, O as OrthographicCamera, M as MeshPhysicalMaterial, C as Color, G as Group, b as Mesh, c as CircleGeometry, d as MeshBasicMaterial, e as SphereGeometry, f as AmbientLight, H as HemisphereLight, D as DirectionalLight, P as PointLight, V as Vector3, g as CylinderGeometry } from "../_libs/three.mjs";
10
12
  import { T as TriangleAlert, C as CircleCheck, X, I as Info, a as ChevronDown, b as Check, P as PanelRightOpen, c as PanelRightClose, S as Search, U as Upload, D as Download, F as FileBraces, R as RefreshCw, d as Trash2, H as Heart, e as Pause, f as Play, g as Plus, B as BrainCircuit, L as LoaderCircle, h as Send, A as ArrowLeft, i as ChevronUp, j as Copy, k as ChevronsLeft, l as ChevronLeft, m as ChevronRight, n as ChevronsRight, o as Clapperboard, p as Flag, q as Siren, r as Timer, G as Gauge, s as Sun, E as Eye, M as Moon, t as ALargeSmall, u as Layers, v as FlaskConical, w as Sparkles, x as Activity, y as Beaker, N as Network, z as Terminal, J as Settings, K as Monitor, O as Rocket, Q as PackageCheck, V as History, W as Scan, Y as CircleAlert, Z as ArrowUpRight, _ as ArrowDownRight, $ as Cpu, a0 as FolderOpen, a1 as Bell, a2 as Clock3, a3 as ShieldAlert, a4 as EyeOff, a5 as ExternalLink, a6 as Clock, a7 as RotateCw, a8 as Pencil, a9 as MessageSquare, aa as Zap, ab as Wrench, ac as Brain, ad as CircleCheckBig, ae as Minus, af as CircleX, ag as ShieldCheck, ah as Save, ai as FileSearch, aj as CloudUpload, ak as CircleQuestionMark, al as Server, am as Lock, an as Wifi, ao as WifiOff, ap as OctagonAlert, aq as Radio, ar as Globe, as as FileTerminal, at as ChevronsUp, au as ChevronsDown, av as FileDiff, aw as RotateCcw, ax as GitCompareArrows, ay as ArrowUp, az as ArrowDown, aA as Ellipsis, aB as CodeXml, aC as Blocks, aD as MousePointerClick, aE as FileText, aF as Rows3, aG as Columns2 } from "../_libs/lucide-react.mjs";
11
13
  import { u as union, o as object, e as array, l as literal, n as number, d as boolean, b as string, _ as _enum } from "../_libs/zod.mjs";
12
14
  import { S as Select$1, a as SelectTrigger$1, b as SelectIcon, c as SelectValue$1, d as SelectPortal, e as SelectContent$1, f as SelectViewport, g as SelectItem$1, h as SelectItemIndicator, i as SelectItemText, j as SelectScrollUpButton$1, k as SelectScrollDownButton$1 } from "../_libs/radix-ui__react-select.mjs";
@@ -2525,27 +2527,27 @@ function useCopyFeedback(text) {
2525
2527
  return { copied, copy };
2526
2528
  }
2527
2529
  const LazyCompareDrawer = reactExports.lazy(
2528
- () => import("./CompareDrawer-aH59Ab6x.mjs").then((m) => ({ default: m.CompareDrawer }))
2530
+ () => import("./CompareDrawer-CqNzIeMR.mjs").then((m) => ({ default: m.CompareDrawer }))
2529
2531
  );
2530
2532
  const LazyReplayDialog = reactExports.lazy(
2531
- () => import("./ReplayDialog-BO4Vy0et.mjs").then((m) => ({ default: m.ReplayDialog }))
2533
+ () => import("./ReplayDialog-D9p_LaTr.mjs").then((m) => ({ default: m.ReplayDialog }))
2532
2534
  );
2533
2535
  const LazyRequestAnatomy = reactExports.lazy(
2534
- () => import("./RequestAnatomy-FtOr7vSS.mjs").then((m) => ({ default: m.RequestAnatomy }))
2536
+ () => import("./RequestAnatomy-B4ge3qNZ.mjs").then((m) => ({ default: m.RequestAnatomy }))
2535
2537
  );
2536
2538
  const LazyResponseView = reactExports.lazy(
2537
- () => import("./ResponseView-cvw6ajue.mjs").then((m) => ({ default: m.ResponseView }))
2539
+ () => import("./ResponseView-BCqMKApI.mjs").then((m) => ({ default: m.ResponseView }))
2538
2540
  );
2539
2541
  const LazyStreamingChunkSequence = reactExports.lazy(
2540
- () => import("./StreamingChunkSequence-Oc8S2kD6.mjs").then((m) => ({
2542
+ () => import("./StreamingChunkSequence-BA4zTLE4.mjs").then((m) => ({
2541
2543
  default: m.StreamingChunkSequence
2542
2544
  }))
2543
2545
  );
2544
2546
  const LazyJsonViewer = reactExports.lazy(
2545
- () => import("./json-viewer-Cd6trhQr.mjs").then((m) => ({ default: m.JsonViewer }))
2547
+ () => import("./json-viewer-B3qV6iEJ.mjs").then((m) => ({ default: m.JsonViewer }))
2546
2548
  );
2547
2549
  const LazyJsonViewerFromString = reactExports.lazy(
2548
- () => import("./json-viewer-Cd6trhQr.mjs").then((m) => ({ default: m.JsonViewerFromString }))
2550
+ () => import("./json-viewer-B3qV6iEJ.mjs").then((m) => ({ default: m.JsonViewerFromString }))
2549
2551
  );
2550
2552
  const HIGHLIGHT_DURATION_MS = 1200;
2551
2553
  const MAX_HIGHLIGHT_ATTEMPTS = 12;
@@ -8935,7 +8937,7 @@ function ProviderTestPill({
8935
8937
  testResults,
8936
8938
  isTesting
8937
8939
  }) {
8938
- const hasEndpoint = providerHasEndpoint$1(provider.anthropicBaseUrl) || providerHasEndpoint$1(provider.openaiBaseUrl) || providerHasEndpoint$1(provider.openaiResponsesBaseUrl);
8940
+ const hasEndpoint2 = providerHasEndpoint$1(provider.anthropicBaseUrl) || providerHasEndpoint$1(provider.openaiBaseUrl) || providerHasEndpoint$1(provider.openaiResponsesBaseUrl);
8939
8941
  const summary = summarizeProviderTestResults(provider, testResults);
8940
8942
  if (isTesting) {
8941
8943
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 rounded border border-cyan-300/25 bg-cyan-300/[0.08] px-2 py-1 text-xs text-cyan-100", children: [
@@ -8943,7 +8945,7 @@ function ProviderTestPill({
8943
8945
  "Testing"
8944
8946
  ] });
8945
8947
  }
8946
- if (!hasEndpoint) {
8948
+ if (!hasEndpoint2) {
8947
8949
  return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "rounded border border-border/70 bg-muted/20 px-2 py-1 text-xs text-muted-foreground", children: "No endpoint" });
8948
8950
  }
8949
8951
  if (testResults === void 0 || summary.total === 0) {
@@ -9254,6 +9256,25 @@ function ProviderCard({
9254
9256
  }
9255
9257
  );
9256
9258
  }
9259
+ function hasEndpoint(value) {
9260
+ return value !== void 0 && value.trim() !== "";
9261
+ }
9262
+ function deriveProviderCapabilities(provider) {
9263
+ const anthropic = hasEndpoint(provider?.anthropicBaseUrl);
9264
+ const openaiChat = hasEndpoint(provider?.openaiBaseUrl);
9265
+ const openaiResponses = hasEndpoint(provider?.openaiResponsesBaseUrl);
9266
+ return {
9267
+ anthropic,
9268
+ openai: openaiChat || openaiResponses,
9269
+ openaiChat,
9270
+ openaiResponses
9271
+ };
9272
+ }
9273
+ function endpointForCapability(enabled, value, clearWhenDisabled) {
9274
+ if (!enabled) return clearWhenDisabled ? "" : void 0;
9275
+ const trimmed = value.trim();
9276
+ return trimmed === "" ? void 0 : trimmed;
9277
+ }
9257
9278
  const ZHIPU_PROVIDER_KEYWORDS = ["zhipu", "zhipuai", "bigmodel", "z.ai", "zai", "glm"];
9258
9279
  const ZHIPU_CODING_PROVIDER_KEYWORDS = [
9259
9280
  "zhipu coding",
@@ -9477,6 +9498,7 @@ function EndpointUrlPreview({
9477
9498
  ] });
9478
9499
  }
9479
9500
  function ProviderForm({ provider, onSubmit, onCancel }) {
9501
+ const initialCapabilities = deriveProviderCapabilities(provider);
9480
9502
  const [name, setName] = reactExports.useState(provider?.name ?? "");
9481
9503
  const [apiKey, setApiKey] = reactExports.useState(provider?.apiKey ?? "");
9482
9504
  const [showApiKey, setShowApiKey] = reactExports.useState(false);
@@ -9491,7 +9513,12 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
9491
9513
  initialModels !== void 0 && initialModels.length > 0 ? initialModels : [""]
9492
9514
  )
9493
9515
  );
9494
- const [activeTab, setActiveTab] = reactExports.useState("anthropic");
9516
+ const [supportsAnthropic, setSupportsAnthropic] = reactExports.useState(initialCapabilities.anthropic);
9517
+ const [supportsOpenai, setSupportsOpenai] = reactExports.useState(initialCapabilities.openai);
9518
+ const [supportsOpenaiChat, setSupportsOpenaiChat] = reactExports.useState(initialCapabilities.openaiChat);
9519
+ const [supportsOpenaiResponses, setSupportsOpenaiResponses] = reactExports.useState(
9520
+ initialCapabilities.openaiResponses
9521
+ );
9495
9522
  const [anthropicBaseUrl, setAnthropicBaseUrl] = reactExports.useState(
9496
9523
  stripEndpointSuffix(provider?.anthropicBaseUrl, ANTHROPIC_MESSAGES_ENDPOINT)
9497
9524
  );
@@ -9545,6 +9572,11 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
9545
9572
  const nextModels = (provider.models?.length ?? 0) > 0 ? provider.models : [""];
9546
9573
  setModels(nextModels);
9547
9574
  setModelMetadataDrafts(createMetadataDrafts(provider, nextModels));
9575
+ const nextCapabilities = deriveProviderCapabilities(provider);
9576
+ setSupportsAnthropic(nextCapabilities.anthropic);
9577
+ setSupportsOpenai(nextCapabilities.openai);
9578
+ setSupportsOpenaiChat(nextCapabilities.openaiChat);
9579
+ setSupportsOpenaiResponses(nextCapabilities.openaiResponses);
9548
9580
  setAnthropicBaseUrl(
9549
9581
  stripEndpointSuffix(provider.anthropicBaseUrl, ANTHROPIC_MESSAGES_ENDPOINT)
9550
9582
  );
@@ -9602,14 +9634,32 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
9602
9634
  if (models.length === 0 || models.every((m) => !m.trim())) {
9603
9635
  newErrors.models = "At least one model is required";
9604
9636
  }
9605
- if (anthropicBaseUrl.trim() && !isValidUrl(anthropicBaseUrl.trim())) {
9606
- newErrors.anthropicBaseUrl = "Invalid URL format";
9637
+ if (!supportsAnthropic && !supportsOpenai) {
9638
+ newErrors.capabilities = "Select at least one API capability";
9639
+ }
9640
+ if (supportsOpenai && !supportsOpenaiChat && !supportsOpenaiResponses) {
9641
+ newErrors.openaiCapabilities = "Select Chat Completions, Responses, or both";
9607
9642
  }
9608
- if (openaiBaseUrl.trim() && !isValidUrl(openaiBaseUrl.trim())) {
9609
- newErrors.openaiBaseUrl = "Invalid URL format";
9643
+ if (supportsAnthropic) {
9644
+ if (anthropicBaseUrl.trim() === "") {
9645
+ newErrors.anthropicBaseUrl = "Anthropic base URL is required";
9646
+ } else if (!isValidUrl(anthropicBaseUrl.trim())) {
9647
+ newErrors.anthropicBaseUrl = "Invalid URL format";
9648
+ }
9649
+ }
9650
+ if (supportsOpenai && supportsOpenaiChat) {
9651
+ if (openaiBaseUrl.trim() === "") {
9652
+ newErrors.openaiBaseUrl = "OpenAI Chat base URL is required";
9653
+ } else if (!isValidUrl(openaiBaseUrl.trim())) {
9654
+ newErrors.openaiBaseUrl = "Invalid URL format";
9655
+ }
9610
9656
  }
9611
- if (openaiResponsesBaseUrl.trim() && !isValidUrl(openaiResponsesBaseUrl.trim())) {
9612
- newErrors.openaiResponsesBaseUrl = "Invalid URL format";
9657
+ if (supportsOpenai && supportsOpenaiResponses) {
9658
+ if (openaiResponsesBaseUrl.trim() === "") {
9659
+ newErrors.openaiResponsesBaseUrl = "OpenAI Responses base URL is required";
9660
+ } else if (!isValidUrl(openaiResponsesBaseUrl.trim())) {
9661
+ newErrors.openaiResponsesBaseUrl = "Invalid URL format";
9662
+ }
9613
9663
  }
9614
9664
  if (modelMetadataUrl.trim() && !isValidUrl(modelMetadataUrl.trim())) {
9615
9665
  newErrors.modelMetadataUrl = "Invalid URL format";
@@ -9627,9 +9677,6 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
9627
9677
  newErrors[`modelMetadata.${index}.outputLimit`] = "Output limit should not exceed context window";
9628
9678
  }
9629
9679
  });
9630
- if (!anthropicBaseUrl.trim() && !openaiBaseUrl.trim() && !openaiResponsesBaseUrl.trim()) {
9631
- newErrors.format = "At least one format URL (Anthropic or OpenAI) is required";
9632
- }
9633
9680
  setErrors(newErrors);
9634
9681
  return Object.keys(newErrors).length === 0;
9635
9682
  }
@@ -9736,14 +9783,27 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
9736
9783
  e.preventDefault();
9737
9784
  if (!validate()) return;
9738
9785
  setIsSubmitting(true);
9786
+ const clearDisabledEndpoints = provider !== void 0;
9739
9787
  try {
9740
9788
  onSubmit({
9741
9789
  name: name.trim(),
9742
9790
  apiKey: apiKey.trim(),
9743
9791
  models: models.map((m) => m.trim()).filter((m) => m !== ""),
9744
- anthropicBaseUrl: anthropicBaseUrl.trim() || void 0,
9745
- openaiBaseUrl: openaiBaseUrl.trim() || void 0,
9746
- openaiResponsesBaseUrl: openaiResponsesBaseUrl.trim() || void 0,
9792
+ anthropicBaseUrl: endpointForCapability(
9793
+ supportsAnthropic,
9794
+ anthropicBaseUrl,
9795
+ clearDisabledEndpoints
9796
+ ),
9797
+ openaiBaseUrl: endpointForCapability(
9798
+ supportsOpenai && supportsOpenaiChat,
9799
+ openaiBaseUrl,
9800
+ clearDisabledEndpoints
9801
+ ),
9802
+ openaiResponsesBaseUrl: endpointForCapability(
9803
+ supportsOpenai && supportsOpenaiResponses,
9804
+ openaiResponsesBaseUrl,
9805
+ clearDisabledEndpoints
9806
+ ),
9747
9807
  apiDocsUrl: apiDocsUrl.trim() || void 0,
9748
9808
  modelMetadataUrl: modelMetadataUrl.trim() || void 0,
9749
9809
  modelMetadata: buildModelMetadata(),
@@ -9772,6 +9832,205 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
9772
9832
  ),
9773
9833
  errors.name !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.name })
9774
9834
  ] }),
9835
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("fieldset", { className: "space-y-3", children: [
9836
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("legend", { className: "text-sm font-medium", children: [
9837
+ "Capabilities ",
9838
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
9839
+ ] }),
9840
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "divide-y divide-border overflow-hidden rounded-md border border-border", children: [
9841
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: supportsOpenai ? "bg-primary/[0.04] px-3 py-3" : "px-3 py-3", children: [
9842
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
9843
+ "label",
9844
+ {
9845
+ htmlFor: "provider-capability-openai",
9846
+ className: "flex cursor-pointer items-start gap-3",
9847
+ children: [
9848
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9849
+ "input",
9850
+ {
9851
+ id: "provider-capability-openai",
9852
+ type: "checkbox",
9853
+ checked: supportsOpenai,
9854
+ onChange: (event) => setSupportsOpenai(event.target.checked),
9855
+ className: "mt-0.5 size-4 shrink-0 accent-primary"
9856
+ }
9857
+ ),
9858
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0", children: [
9859
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-sm font-medium", children: "OpenAI" }),
9860
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-xs text-muted-foreground", children: "Chat Completions and Responses" })
9861
+ ] })
9862
+ ]
9863
+ }
9864
+ ),
9865
+ supportsOpenai && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-7 mt-3 flex flex-wrap gap-x-5 gap-y-2 border-t border-border/70 pt-3", children: [
9866
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
9867
+ "label",
9868
+ {
9869
+ htmlFor: "provider-capability-openai-chat",
9870
+ className: "flex cursor-pointer items-center gap-2 text-sm",
9871
+ children: [
9872
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9873
+ "input",
9874
+ {
9875
+ id: "provider-capability-openai-chat",
9876
+ type: "checkbox",
9877
+ checked: supportsOpenaiChat,
9878
+ onChange: (event) => setSupportsOpenaiChat(event.target.checked),
9879
+ className: "size-4 shrink-0 accent-primary"
9880
+ }
9881
+ ),
9882
+ "Chat Completions"
9883
+ ]
9884
+ }
9885
+ ),
9886
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
9887
+ "label",
9888
+ {
9889
+ htmlFor: "provider-capability-openai-responses",
9890
+ className: "flex cursor-pointer items-center gap-2 text-sm",
9891
+ children: [
9892
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9893
+ "input",
9894
+ {
9895
+ id: "provider-capability-openai-responses",
9896
+ type: "checkbox",
9897
+ checked: supportsOpenaiResponses,
9898
+ onChange: (event) => setSupportsOpenaiResponses(event.target.checked),
9899
+ className: "size-4 shrink-0 accent-primary"
9900
+ }
9901
+ ),
9902
+ "Responses"
9903
+ ]
9904
+ }
9905
+ )
9906
+ ] }),
9907
+ errors.openaiCapabilities !== void 0 && supportsOpenai && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ml-7 mt-2 text-xs text-destructive", children: errors.openaiCapabilities })
9908
+ ] }),
9909
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
9910
+ "label",
9911
+ {
9912
+ htmlFor: "provider-capability-anthropic",
9913
+ className: `flex cursor-pointer items-start gap-3 px-3 py-3 ${supportsAnthropic ? "bg-primary/[0.04]" : ""}`,
9914
+ children: [
9915
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9916
+ "input",
9917
+ {
9918
+ id: "provider-capability-anthropic",
9919
+ type: "checkbox",
9920
+ checked: supportsAnthropic,
9921
+ onChange: (event) => setSupportsAnthropic(event.target.checked),
9922
+ className: "mt-0.5 size-4 shrink-0 accent-primary"
9923
+ }
9924
+ ),
9925
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0", children: [
9926
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-sm font-medium", children: "Anthropic" }),
9927
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-xs text-muted-foreground", children: "Messages" })
9928
+ ] })
9929
+ ]
9930
+ }
9931
+ )
9932
+ ] }),
9933
+ errors.capabilities !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.capabilities })
9934
+ ] }),
9935
+ (supportsAnthropic || supportsOpenai && (supportsOpenaiChat || supportsOpenaiResponses)) && /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "space-y-5 border-l border-border pl-4", "aria-label": "API endpoints", children: [
9936
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-medium", children: "Endpoint Configuration" }),
9937
+ supportsAnthropic && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
9938
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-anthropic-base-url", className: "text-sm font-medium", children: [
9939
+ "Anthropic Messages Base URL ",
9940
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
9941
+ ] }),
9942
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
9943
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9944
+ "input",
9945
+ {
9946
+ id: "provider-anthropic-base-url",
9947
+ type: "text",
9948
+ value: anthropicBaseUrl,
9949
+ onChange: (event) => {
9950
+ setManualAnthropicUrlOverride(true);
9951
+ setAnthropicBaseUrl(event.target.value);
9952
+ },
9953
+ placeholder: "https://api.anthropic.com",
9954
+ className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
9955
+ }
9956
+ ),
9957
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: ANTHROPIC_MESSAGES_ENDPOINT })
9958
+ ] }),
9959
+ errors.anthropicBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.anthropicBaseUrl }),
9960
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9961
+ EndpointUrlPreview,
9962
+ {
9963
+ baseUrl: anthropicBaseUrl,
9964
+ endpoint: ANTHROPIC_MESSAGES_ENDPOINT,
9965
+ providerName: name
9966
+ }
9967
+ )
9968
+ ] }),
9969
+ supportsOpenai && supportsOpenaiChat && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
9970
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-openai-base-url", className: "text-sm font-medium", children: [
9971
+ "OpenAI Chat Base URL ",
9972
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
9973
+ ] }),
9974
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
9975
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9976
+ "input",
9977
+ {
9978
+ id: "provider-openai-base-url",
9979
+ type: "text",
9980
+ value: openaiBaseUrl,
9981
+ onChange: (event) => {
9982
+ setManualOpenaiUrlOverride(true);
9983
+ setOpenaiBaseUrl(event.target.value);
9984
+ },
9985
+ placeholder: "https://api.openai.com",
9986
+ className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
9987
+ }
9988
+ ),
9989
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: OPENAI_CHAT_COMPLETIONS_ENDPOINT })
9990
+ ] }),
9991
+ errors.openaiBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiBaseUrl }),
9992
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
9993
+ EndpointUrlPreview,
9994
+ {
9995
+ baseUrl: openaiBaseUrl,
9996
+ endpoint: OPENAI_CHAT_COMPLETIONS_ENDPOINT,
9997
+ providerName: name
9998
+ }
9999
+ )
10000
+ ] }),
10001
+ supportsOpenai && supportsOpenaiResponses && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
10002
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-openai-responses-base-url", className: "text-sm font-medium", children: [
10003
+ "OpenAI Responses Base URL ",
10004
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
10005
+ ] }),
10006
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
10007
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
10008
+ "input",
10009
+ {
10010
+ id: "provider-openai-responses-base-url",
10011
+ type: "text",
10012
+ value: openaiResponsesBaseUrl,
10013
+ onChange: (event) => {
10014
+ setManualOpenaiResponsesUrlOverride(true);
10015
+ setOpenaiResponsesBaseUrl(event.target.value);
10016
+ },
10017
+ placeholder: "https://api.openai.com",
10018
+ className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
10019
+ }
10020
+ ),
10021
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: OPENAI_RESPONSES_ENDPOINT })
10022
+ ] }),
10023
+ errors.openaiResponsesBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiResponsesBaseUrl }),
10024
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
10025
+ EndpointUrlPreview,
10026
+ {
10027
+ baseUrl: openaiResponsesBaseUrl,
10028
+ endpoint: OPENAI_RESPONSES_ENDPOINT,
10029
+ providerName: name
10030
+ }
10031
+ )
10032
+ ] })
10033
+ ] }),
9775
10034
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
9776
10035
  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-source", className: "text-sm font-medium", children: "Token Source" }),
9777
10036
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -10025,127 +10284,6 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
10025
10284
  ),
10026
10285
  errors.models !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.models })
10027
10286
  ] }),
10028
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
10029
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-border bg-muted/40 flex gap-1 rounded-[8px] p-1", children: [
10030
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
10031
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
10032
- "button",
10033
- {
10034
- type: "button",
10035
- onClick: () => setActiveTab("anthropic"),
10036
- className: `px-3 py-2 text-sm font-medium border-b-2 transition-colors ${activeTab === "anthropic" ? "border-primary text-primary" : "border-transparent text-muted-foreground hover:text-foreground"}`,
10037
- children: "Anthropic Format"
10038
- }
10039
- ) }),
10040
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Anthropic Messages API format" })
10041
- ] }) }),
10042
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
10043
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
10044
- "button",
10045
- {
10046
- type: "button",
10047
- onClick: () => setActiveTab("openai"),
10048
- className: `px-3 py-2 text-sm font-medium border-b-2 transition-colors ${activeTab === "openai" ? "border-primary text-primary" : "border-transparent text-muted-foreground hover:text-foreground"}`,
10049
- children: "OpenAI Format"
10050
- }
10051
- ) }),
10052
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "OpenAI Chat Completions API format" })
10053
- ] }) })
10054
- ] }),
10055
- errors.format !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.format })
10056
- ] }),
10057
- activeTab === "anthropic" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
10058
- /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-anthropic-base-url", className: "text-sm font-medium", children: "Anthropic Base URL" }),
10059
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
10060
- /* @__PURE__ */ jsxRuntimeExports.jsx(
10061
- "input",
10062
- {
10063
- id: "provider-anthropic-base-url",
10064
- type: "text",
10065
- value: anthropicBaseUrl,
10066
- onChange: (e) => {
10067
- setManualAnthropicUrlOverride(true);
10068
- setAnthropicBaseUrl(e.target.value);
10069
- },
10070
- placeholder: "https://api.anthropic.com",
10071
- className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
10072
- }
10073
- ),
10074
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: ANTHROPIC_MESSAGES_ENDPOINT })
10075
- ] }),
10076
- errors.anthropicBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.anthropicBaseUrl }),
10077
- /* @__PURE__ */ jsxRuntimeExports.jsx(
10078
- EndpointUrlPreview,
10079
- {
10080
- baseUrl: anthropicBaseUrl,
10081
- endpoint: ANTHROPIC_MESSAGES_ENDPOINT,
10082
- providerName: name
10083
- }
10084
- ),
10085
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Anthropic-compatible base URL. Leave empty if this provider does not support Anthropic format." })
10086
- ] }),
10087
- activeTab === "openai" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
10088
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
10089
- /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-openai-base-url", className: "text-sm font-medium", children: "OpenAI Chat Base URL" }),
10090
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
10091
- /* @__PURE__ */ jsxRuntimeExports.jsx(
10092
- "input",
10093
- {
10094
- id: "provider-openai-base-url",
10095
- type: "text",
10096
- value: openaiBaseUrl,
10097
- onChange: (e) => {
10098
- setManualOpenaiUrlOverride(true);
10099
- setOpenaiBaseUrl(e.target.value);
10100
- },
10101
- placeholder: "https://api.openai.com",
10102
- className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
10103
- }
10104
- ),
10105
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: OPENAI_CHAT_COMPLETIONS_ENDPOINT })
10106
- ] }),
10107
- errors.openaiBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiBaseUrl }),
10108
- /* @__PURE__ */ jsxRuntimeExports.jsx(
10109
- EndpointUrlPreview,
10110
- {
10111
- baseUrl: openaiBaseUrl,
10112
- endpoint: OPENAI_CHAT_COMPLETIONS_ENDPOINT,
10113
- providerName: name
10114
- }
10115
- ),
10116
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "OpenAI Chat Completions-compatible base URL. Leave empty if this provider only supports the Responses API." })
10117
- ] }),
10118
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
10119
- /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-openai-responses-base-url", className: "text-sm font-medium", children: "OpenAI Responses Base URL" }),
10120
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
10121
- /* @__PURE__ */ jsxRuntimeExports.jsx(
10122
- "input",
10123
- {
10124
- id: "provider-openai-responses-base-url",
10125
- type: "text",
10126
- value: openaiResponsesBaseUrl,
10127
- onChange: (e) => {
10128
- setManualOpenaiResponsesUrlOverride(true);
10129
- setOpenaiResponsesBaseUrl(e.target.value);
10130
- },
10131
- placeholder: "https://api.openai.com",
10132
- className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
10133
- }
10134
- ),
10135
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: OPENAI_RESPONSES_ENDPOINT })
10136
- ] }),
10137
- errors.openaiResponsesBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiResponsesBaseUrl }),
10138
- /* @__PURE__ */ jsxRuntimeExports.jsx(
10139
- EndpointUrlPreview,
10140
- {
10141
- baseUrl: openaiResponsesBaseUrl,
10142
- endpoint: OPENAI_RESPONSES_ENDPOINT,
10143
- providerName: name
10144
- }
10145
- ),
10146
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Optional Responses-compatible base URL. Set only when the provider supports the Responses API." })
10147
- ] })
10148
- ] }),
10149
10287
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
10150
10288
  /* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-api-docs-url", className: "text-sm font-medium", children: "API Docs URL" }),
10151
10289
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -10191,14 +10329,21 @@ const PROVIDER_DELETE_TIMEOUT_MS = 1e4;
10191
10329
  const PROVIDER_EXPORT_TIMEOUT_MS = 2e4;
10192
10330
  const PROVIDER_IMPORT_TIMEOUT_MS = 2e4;
10193
10331
  const PROVIDER_METADATA_TIMEOUT_MS = 3e4;
10194
- function createProviderPayload(data) {
10332
+ function endpointPayloadValue(value, preserveEmptyEndpoints) {
10333
+ if ((value?.length ?? 0) > 0) return value;
10334
+ return preserveEmptyEndpoints && value !== void 0 ? "" : void 0;
10335
+ }
10336
+ function createProviderPayload(data, preserveEmptyEndpoints = false) {
10195
10337
  return {
10196
10338
  name: data.name,
10197
10339
  apiKey: data.apiKey,
10198
10340
  models: data.models,
10199
- anthropicBaseUrl: (data.anthropicBaseUrl?.length ?? 0) > 0 ? data.anthropicBaseUrl : void 0,
10200
- openaiBaseUrl: (data.openaiBaseUrl?.length ?? 0) > 0 ? data.openaiBaseUrl : void 0,
10201
- openaiResponsesBaseUrl: (data.openaiResponsesBaseUrl?.length ?? 0) > 0 ? data.openaiResponsesBaseUrl : void 0,
10341
+ anthropicBaseUrl: endpointPayloadValue(data.anthropicBaseUrl, preserveEmptyEndpoints),
10342
+ openaiBaseUrl: endpointPayloadValue(data.openaiBaseUrl, preserveEmptyEndpoints),
10343
+ openaiResponsesBaseUrl: endpointPayloadValue(
10344
+ data.openaiResponsesBaseUrl,
10345
+ preserveEmptyEndpoints
10346
+ ),
10202
10347
  apiDocsUrl: (data.apiDocsUrl?.length ?? 0) > 0 ? data.apiDocsUrl : void 0,
10203
10348
  modelMetadataUrl: (data.modelMetadataUrl?.length ?? 0) > 0 ? data.modelMetadataUrl : void 0,
10204
10349
  modelMetadata: data.modelMetadata,
@@ -10481,7 +10626,7 @@ function ProvidersPanel({
10481
10626
  {
10482
10627
  method: "PUT",
10483
10628
  headers: { "Content-Type": "application/json" },
10484
- body: JSON.stringify(createProviderPayload(data))
10629
+ body: JSON.stringify(createProviderPayload(data, true))
10485
10630
  },
10486
10631
  () => "Failed to update provider"
10487
10632
  );
@@ -12134,11 +12279,11 @@ function AgentLabDialog({ currentSessionId, logCount }) {
12134
12279
  ] })
12135
12280
  ] });
12136
12281
  }
12137
- const INSPECTOR_PET_SIZE = 56;
12282
+ const INSPECTOR_PET_SIZE = 112;
12138
12283
  const INSPECTOR_PET_MARGIN = 14;
12139
- const INSPECTOR_PET_EDGE_PEEK = 18;
12140
- const INSPECTOR_PET_MIN_STEP = 72;
12141
- const INSPECTOR_PET_MAX_STEP = 156;
12284
+ const INSPECTOR_PET_EDGE_GAP = 20;
12285
+ const INSPECTOR_PET_MIN_STEP = 36;
12286
+ const INSPECTOR_PET_MAX_STEP = 92;
12142
12287
  const INSPECTOR_PET_SLEEP_AFTER_MS = 45e3;
12143
12288
  const INSPECTOR_PET_PANEL_WIDTH = 192;
12144
12289
  const INSPECTOR_PET_PANEL_HEIGHT = 68;
@@ -12155,33 +12300,27 @@ function clampUnit(value) {
12155
12300
  return Math.min(Math.max(value, 0), 1);
12156
12301
  }
12157
12302
  function maximumPetX(viewport) {
12158
- const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12159
- return Math.max(
12160
- -edgePeek,
12161
- viewport.width - scaledMetric(INSPECTOR_PET_SIZE, viewport) + edgePeek
12162
- );
12303
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12304
+ return Math.max(edgeGap, viewport.width - scaledMetric(INSPECTOR_PET_SIZE, viewport) - edgeGap);
12163
12305
  }
12164
12306
  function maximumPetY(viewport) {
12165
- const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12166
- return Math.max(
12167
- -edgePeek,
12168
- viewport.height - scaledMetric(INSPECTOR_PET_SIZE, viewport) + edgePeek
12169
- );
12307
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12308
+ return Math.max(edgeGap, viewport.height - scaledMetric(INSPECTOR_PET_SIZE, viewport) - edgeGap);
12170
12309
  }
12171
12310
  function clampInspectorPetPosition(position, viewport) {
12172
- const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12311
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12173
12312
  return {
12174
- x: Math.min(Math.max(position.x, -edgePeek), maximumPetX(viewport)),
12175
- y: Math.min(Math.max(position.y, -edgePeek), maximumPetY(viewport))
12313
+ x: Math.min(Math.max(position.x, edgeGap), maximumPetX(viewport)),
12314
+ y: Math.min(Math.max(position.y, edgeGap), maximumPetY(viewport))
12176
12315
  };
12177
12316
  }
12178
12317
  function nearestInspectorPetEdge(position, viewport) {
12179
12318
  const clamped = clampInspectorPetPosition(position, viewport);
12180
- const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12319
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12181
12320
  const distances = [
12182
- { edge: "left", distance: clamped.x + edgePeek },
12321
+ { edge: "left", distance: clamped.x - edgeGap },
12183
12322
  { edge: "right", distance: maximumPetX(viewport) - clamped.x },
12184
- { edge: "top", distance: clamped.y + INSPECTOR_PET_EDGE_PEEK },
12323
+ { edge: "top", distance: clamped.y - edgeGap },
12185
12324
  { edge: "bottom", distance: maximumPetY(viewport) - clamped.y }
12186
12325
  ];
12187
12326
  return distances.reduce((best, current) => current.distance < best.distance ? current : best).edge;
@@ -12189,33 +12328,26 @@ function nearestInspectorPetEdge(position, viewport) {
12189
12328
  function snapInspectorPetToEdge(position, viewport) {
12190
12329
  const clamped = clampInspectorPetPosition(position, viewport);
12191
12330
  const edge = nearestInspectorPetEdge(clamped, viewport);
12192
- const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12331
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12193
12332
  switch (edge) {
12194
12333
  case "left":
12195
- return { x: -edgePeek, y: clamped.y };
12334
+ return { x: edgeGap, y: clamped.y };
12196
12335
  case "right":
12197
12336
  return { x: maximumPetX(viewport), y: clamped.y };
12198
12337
  case "top":
12199
- return { x: clamped.x, y: -edgePeek };
12338
+ return { x: clamped.x, y: edgeGap };
12200
12339
  case "bottom":
12201
12340
  return { x: clamped.x, y: maximumPetY(viewport) };
12202
12341
  }
12203
12342
  }
12204
- function randomInspectorPetEdgePosition(viewport, edgeSample, ySample) {
12343
+ function randomInspectorPetEdgePosition(viewport, positionSample) {
12205
12344
  const maxX = maximumPetX(viewport);
12206
12345
  const maxY = maximumPetY(viewport);
12207
- const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12208
- const minimumSafeY = Math.min(scaledMetric(INSPECTOR_PET_VERTICAL_SAFE_MARGIN, viewport), maxY);
12209
- const randomY = minimumSafeY + clampUnit(ySample) * (maxY - minimumSafeY);
12210
- const edgeIndex = Math.min(1, Math.floor(clampUnit(edgeSample) * 2));
12211
- switch (edgeIndex) {
12212
- case 0:
12213
- return { x: -edgePeek, y: randomY };
12214
- case 1:
12215
- return { x: maxX, y: randomY };
12216
- default:
12217
- return { x: maxX, y: maxY };
12218
- }
12346
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12347
+ return {
12348
+ x: edgeGap + clampUnit(positionSample) * (maxX - edgeGap),
12349
+ y: maxY
12350
+ };
12219
12351
  }
12220
12352
  function nextInspectorPetWalkingPosition(position, viewport, stepSample, directionSample) {
12221
12353
  const clamped = snapInspectorPetToEdge(position, viewport);
@@ -12224,12 +12356,12 @@ function nextInspectorPetWalkingPosition(position, viewport, stepSample, directi
12224
12356
  const edge = nearestInspectorPetEdge(clamped, viewport);
12225
12357
  const maxX = maximumPetX(viewport);
12226
12358
  const maxY = maximumPetY(viewport);
12227
- const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12359
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12228
12360
  const minimumSafeY = Math.min(scaledMetric(INSPECTOR_PET_VERTICAL_SAFE_MARGIN, viewport), maxY);
12229
12361
  switch (edge) {
12230
12362
  case "left":
12231
12363
  return {
12232
- x: -edgePeek,
12364
+ x: edgeGap,
12233
12365
  y: Math.min(Math.max(clamped.y + step * direction, minimumSafeY), maxY)
12234
12366
  };
12235
12367
  case "right":
@@ -12239,12 +12371,12 @@ function nextInspectorPetWalkingPosition(position, viewport, stepSample, directi
12239
12371
  };
12240
12372
  case "top":
12241
12373
  return {
12242
- x: Math.min(Math.max(clamped.x + step * direction, -edgePeek), maxX),
12243
- y: -edgePeek
12374
+ x: Math.min(Math.max(clamped.x + step * direction, edgeGap), maxX),
12375
+ y: edgeGap
12244
12376
  };
12245
12377
  case "bottom":
12246
12378
  return {
12247
- x: Math.min(Math.max(clamped.x + step * direction, -edgePeek), maxX),
12379
+ x: Math.min(Math.max(clamped.x + step * direction, edgeGap), maxX),
12248
12380
  y: maxY
12249
12381
  };
12250
12382
  }
@@ -12333,12 +12465,344 @@ function resolveInspectorPetStatus(logs, nowMs) {
12333
12465
  capturedCount
12334
12466
  };
12335
12467
  }
12468
+ function createRod(start, end, radius, material) {
12469
+ const direction = end.clone().sub(start);
12470
+ const length = direction.length();
12471
+ const rod = new Mesh(new CylinderGeometry(radius, radius * 0.9, length, 12), material);
12472
+ rod.position.copy(start.clone().add(end).multiplyScalar(0.5));
12473
+ rod.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), direction.normalize());
12474
+ return rod;
12475
+ }
12476
+ function addClaw(root, side, shellMaterial, accentMaterial) {
12477
+ const clawScale = side < 0 ? 1.5 : 0.68;
12478
+ const clawGroup = new Group();
12479
+ const pincers = [];
12480
+ root.add(clawGroup);
12481
+ const shoulder = new Vector3(side * 1.2, 0.25, -0.05);
12482
+ const wrist = new Vector3(side * (1.65 + clawScale * 0.18), 0.5, 0);
12483
+ const arm = createRod(shoulder, wrist, 0.16 * clawScale, accentMaterial);
12484
+ clawGroup.add(arm);
12485
+ const palm = new Mesh(new SphereGeometry(0.55, 24, 16), shellMaterial);
12486
+ palm.position.set(side * (1.95 + clawScale * 0.16), 0.62, 0.05);
12487
+ palm.scale.set(1.05 * clawScale, 0.72 * clawScale, 0.55 * clawScale);
12488
+ clawGroup.add(palm);
12489
+ for (const pincerDirection of [-1, 1]) {
12490
+ const pincer = new Mesh(new SphereGeometry(0.36, 20, 14), accentMaterial);
12491
+ pincer.position.set(
12492
+ side * (2.22 + clawScale * 0.26),
12493
+ 0.62 + pincerDirection * 0.24 * clawScale,
12494
+ 0.08
12495
+ );
12496
+ pincer.scale.set(1.05 * clawScale, 0.34 * clawScale, 0.42 * clawScale);
12497
+ pincer.rotation.z = pincerDirection * side * 0.42;
12498
+ clawGroup.add(pincer);
12499
+ pincers.push({
12500
+ direction: pincerDirection,
12501
+ mesh: pincer,
12502
+ restRotationZ: pincer.rotation.z,
12503
+ restY: pincer.position.y
12504
+ });
12505
+ }
12506
+ return { group: clawGroup, pincers, scale: clawScale };
12507
+ }
12508
+ function pinchPulse(cycleTime, start, duration) {
12509
+ const progress = (cycleTime - start) / duration;
12510
+ if (progress <= 0 || progress >= 1) return 0;
12511
+ return Math.sin(progress * Math.PI);
12512
+ }
12513
+ function dominantClawGrip(elapsed) {
12514
+ const cycleTime = elapsed % 6.8;
12515
+ return Math.max(pinchPulse(cycleTime, 0.72, 0.82), pinchPulse(cycleTime, 1.72, 0.68));
12516
+ }
12517
+ function addEyes(root, shellMaterial, eyeMaterial) {
12518
+ const eyes = [];
12519
+ for (const side of [-1, 1]) {
12520
+ const stalkStart = new Vector3(side * 0.48, 0.68, 0.05);
12521
+ const stalkEnd = new Vector3(side * 0.1, 0.82, 0.07);
12522
+ const eyeGroup = new Group();
12523
+ eyeGroup.position.copy(stalkStart);
12524
+ eyeGroup.add(createRod(new Vector3(0, 0, 0), stalkEnd, 0.1, shellMaterial));
12525
+ const eye = new Mesh(new SphereGeometry(0.24, 20, 16), eyeMaterial);
12526
+ eye.position.copy(stalkEnd);
12527
+ eye.scale.set(1, 1, 0.85);
12528
+ eyeGroup.add(eye);
12529
+ const pupil = new Mesh(
12530
+ new SphereGeometry(0.105, 16, 12),
12531
+ new MeshPhysicalMaterial({ color: 1120295, roughness: 0.25 })
12532
+ );
12533
+ const pupilRest = new Vector3(stalkEnd.x, stalkEnd.y, stalkEnd.z + 0.2);
12534
+ pupil.position.copy(pupilRest);
12535
+ eyeGroup.add(pupil);
12536
+ root.add(eyeGroup);
12537
+ eyes.push({
12538
+ group: eyeGroup,
12539
+ phase: side < 0 ? 0 : Math.PI / 3,
12540
+ pupil,
12541
+ pupilRest
12542
+ });
12543
+ }
12544
+ return eyes;
12545
+ }
12546
+ function addLegs(root, legMaterial) {
12547
+ const legs = [];
12548
+ for (const side of [-1, 1]) {
12549
+ for (let index = 0; index < 4; index += 1) {
12550
+ const leg = new Group();
12551
+ const baseY = -0.3 - index * 0.22;
12552
+ leg.position.set(side * (0.8 + index * 0.12), baseY, -0.18);
12553
+ leg.add(
12554
+ createRod(
12555
+ new Vector3(0, 0, 0),
12556
+ new Vector3(side * (0.62 + index * 0.06), -0.34, 0),
12557
+ 0.095,
12558
+ legMaterial
12559
+ )
12560
+ );
12561
+ leg.add(
12562
+ createRod(
12563
+ new Vector3(side * (0.58 + index * 0.06), -0.31, 0),
12564
+ new Vector3(side * (1.02 + index * 0.08), -0.88 + index * 0.07, 0.04),
12565
+ 0.075,
12566
+ legMaterial
12567
+ )
12568
+ );
12569
+ root.add(leg);
12570
+ legs.push({ group: leg, phase: index * Math.PI + (side > 0 ? Math.PI / 2 : 0), side });
12571
+ }
12572
+ }
12573
+ return legs;
12574
+ }
12575
+ function createCrabScene(scene) {
12576
+ const shellMaterial = new MeshPhysicalMaterial({
12577
+ color: 16096779,
12578
+ roughness: 0.34,
12579
+ metalness: 0.04,
12580
+ clearcoat: 0.78,
12581
+ clearcoatRoughness: 0.18,
12582
+ emissive: new Color(0),
12583
+ emissiveIntensity: 0
12584
+ });
12585
+ const accentMaterial = new MeshPhysicalMaterial({
12586
+ color: 16486972,
12587
+ roughness: 0.42,
12588
+ metalness: 0.02,
12589
+ clearcoat: 0.52,
12590
+ clearcoatRoughness: 0.28
12591
+ });
12592
+ const legMaterial = new MeshPhysicalMaterial({
12593
+ color: 14251782,
12594
+ roughness: 0.48,
12595
+ clearcoat: 0.35,
12596
+ clearcoatRoughness: 0.35
12597
+ });
12598
+ const eyeMaterial = new MeshPhysicalMaterial({
12599
+ color: 16776171,
12600
+ roughness: 0.22,
12601
+ clearcoat: 0.8,
12602
+ clearcoatRoughness: 0.12
12603
+ });
12604
+ const root = new Group();
12605
+ root.rotation.x = -0.12;
12606
+ root.rotation.y = -0.08;
12607
+ root.scale.setScalar(1.08);
12608
+ scene.add(root);
12609
+ const shadow = new Mesh(
12610
+ new CircleGeometry(1.65, 40),
12611
+ new MeshBasicMaterial({ color: 1120295, transparent: true, opacity: 0.16, depthWrite: false })
12612
+ );
12613
+ shadow.position.set(0, -1.33, -0.65);
12614
+ shadow.scale.set(1.4, 0.34, 1);
12615
+ root.add(shadow);
12616
+ const body = new Mesh(new SphereGeometry(1, 36, 24), shellMaterial);
12617
+ body.position.set(0, 0.02, 0);
12618
+ body.scale.set(1.58, 0.92, 0.68);
12619
+ root.add(body);
12620
+ const lowerShell = new Mesh(new SphereGeometry(0.92, 28, 20), accentMaterial);
12621
+ lowerShell.position.set(0, -0.48, 0.22);
12622
+ lowerShell.scale.set(1.28, 0.42, 0.56);
12623
+ root.add(lowerShell);
12624
+ const dominantClaw = addClaw(root, -1, shellMaterial, accentMaterial);
12625
+ addClaw(root, 1, shellMaterial, accentMaterial);
12626
+ const eyes = addEyes(root, shellMaterial, eyeMaterial);
12627
+ const legs = addLegs(root, legMaterial);
12628
+ scene.add(new AmbientLight(16775149, 1.65));
12629
+ scene.add(new HemisphereLight(16775149, 1515571, 2.15));
12630
+ const keyLight = new DirectionalLight(16777215, 3.6);
12631
+ keyLight.position.set(3.5, 4.5, 6);
12632
+ scene.add(keyLight);
12633
+ const fillLight = new DirectionalLight(16498468, 1.35);
12634
+ fillLight.position.set(-4, 1.5, 4);
12635
+ scene.add(fillLight);
12636
+ const rimLight = new PointLight(16096779, 1.8, 12, 2);
12637
+ rimLight.position.set(0, 1.8, 4.5);
12638
+ scene.add(rimLight);
12639
+ return { bodyMaterial: shellMaterial, dominantClaw, eyes, legs, rimLight, root };
12640
+ }
12641
+ function moodColor(mood) {
12642
+ switch (mood) {
12643
+ case "active":
12644
+ return 2282478;
12645
+ case "attention":
12646
+ return 15680580;
12647
+ case "idle":
12648
+ return 16096779;
12649
+ case "sleeping":
12650
+ return 9741240;
12651
+ }
12652
+ }
12653
+ function isDisposableMesh(object2) {
12654
+ return object2 instanceof Mesh;
12655
+ }
12656
+ function disposeScene(scene) {
12657
+ const materials = /* @__PURE__ */ new Set();
12658
+ scene.traverse((object2) => {
12659
+ if (!isDisposableMesh(object2)) return;
12660
+ object2.geometry.dispose();
12661
+ const material = object2.material;
12662
+ if (Array.isArray(material)) {
12663
+ for (const item of material) materials.add(item);
12664
+ return;
12665
+ }
12666
+ materials.add(material);
12667
+ });
12668
+ for (const material of materials) material.dispose();
12669
+ }
12670
+ function InspectorPetCrab3D({
12671
+ mood,
12672
+ moving,
12673
+ reduceMotion
12674
+ }) {
12675
+ const canvasRef = reactExports.useRef(null);
12676
+ const moodRef = reactExports.useRef(mood);
12677
+ const movingRef = reactExports.useRef(moving);
12678
+ const reduceMotionRef = reactExports.useRef(reduceMotion);
12679
+ const [renderFailed, setRenderFailed] = reactExports.useState(false);
12680
+ reactExports.useEffect(() => {
12681
+ moodRef.current = mood;
12682
+ }, [mood]);
12683
+ reactExports.useEffect(() => {
12684
+ movingRef.current = moving;
12685
+ }, [moving]);
12686
+ reactExports.useEffect(() => {
12687
+ reduceMotionRef.current = reduceMotion;
12688
+ }, [reduceMotion]);
12689
+ reactExports.useEffect(() => {
12690
+ const canvas = canvasRef.current;
12691
+ if (canvas === null) return void 0;
12692
+ let renderer;
12693
+ try {
12694
+ renderer = new WebGLRenderer({
12695
+ alpha: true,
12696
+ antialias: true,
12697
+ canvas,
12698
+ powerPreference: "low-power",
12699
+ preserveDrawingBuffer: true
12700
+ });
12701
+ } catch {
12702
+ setRenderFailed(true);
12703
+ return void 0;
12704
+ }
12705
+ renderer.setClearColor(0, 0);
12706
+ renderer.outputColorSpace = SRGBColorSpace;
12707
+ renderer.toneMapping = ACESFilmicToneMapping;
12708
+ renderer.toneMappingExposure = 1.18;
12709
+ const scene = new Scene();
12710
+ const camera = new OrthographicCamera(-3.65, 3.65, 3.65, -3.65, 0.1, 20);
12711
+ camera.position.set(0, 0.05, 8);
12712
+ camera.lookAt(0, 0, 0);
12713
+ const parts = createCrabScene(scene);
12714
+ const startedAt = window.performance.now();
12715
+ let lastFrameAt = 0;
12716
+ const renderFrame = (timestamp) => {
12717
+ if (timestamp - lastFrameAt < 32) return;
12718
+ lastFrameAt = timestamp;
12719
+ const elapsed = (timestamp - startedAt) / 1e3;
12720
+ const motionAllowed = !reduceMotionRef.current;
12721
+ const gait = movingRef.current ? 1 : moodRef.current === "active" ? 0.42 : 0;
12722
+ const idleMotion = motionAllowed ? Math.sin(elapsed * 0.85) : 0;
12723
+ const stepMotion = motionAllowed ? Math.sin(elapsed * 3.4) : 0;
12724
+ const clawGrip = motionAllowed ? dominantClawGrip(elapsed) : 0;
12725
+ parts.root.position.y = idleMotion * 0.035 + Math.abs(stepMotion) * 0.055 * gait;
12726
+ parts.root.rotation.y = -0.08 + idleMotion * 0.055;
12727
+ parts.root.rotation.z = stepMotion * 0.028 * gait;
12728
+ parts.dominantClaw.group.rotation.z = -clawGrip * 0.035;
12729
+ for (const pincer of parts.dominantClaw.pincers) {
12730
+ pincer.mesh.position.y = pincer.restY - pincer.direction * parts.dominantClaw.scale * 0.16 * clawGrip;
12731
+ pincer.mesh.rotation.z = pincer.restRotationZ * (1 - clawGrip * 0.72);
12732
+ }
12733
+ for (const leg of parts.legs) {
12734
+ leg.group.rotation.z = motionAllowed && gait > 0 ? Math.sin(elapsed * 4.25 + leg.phase) * 0.2 * leg.side * gait : 0;
12735
+ leg.group.rotation.x = motionAllowed && gait > 0 ? stepMotion * 0.045 * gait : 0;
12736
+ }
12737
+ for (const eye of parts.eyes) {
12738
+ const gaze = motionAllowed ? Math.sin(elapsed * 0.72 + eye.phase) : 0;
12739
+ const lift = motionAllowed ? Math.cos(elapsed * 0.9 + eye.phase) : 0;
12740
+ eye.group.rotation.z = gaze * 0.075;
12741
+ eye.group.rotation.x = lift * 0.025;
12742
+ eye.pupil.position.set(
12743
+ eye.pupilRest.x + gaze * 0.055,
12744
+ eye.pupilRest.y + lift * 0.025,
12745
+ eye.pupilRest.z
12746
+ );
12747
+ }
12748
+ const color = moodColor(moodRef.current);
12749
+ parts.rimLight.color.setHex(color);
12750
+ parts.rimLight.intensity = moodRef.current === "attention" ? 3.2 : 1.8;
12751
+ parts.bodyMaterial.emissive.setHex(color);
12752
+ parts.bodyMaterial.emissiveIntensity = moodRef.current === "active" ? 0.12 : 0.035;
12753
+ renderer.render(scene, camera);
12754
+ };
12755
+ const resize = () => {
12756
+ const width = Math.max(canvas.clientWidth, 1);
12757
+ const height = Math.max(canvas.clientHeight, 1);
12758
+ renderer.setDrawingBufferSize(width, height, Math.min(window.devicePixelRatio, 2));
12759
+ renderer.render(scene, camera);
12760
+ };
12761
+ const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(resize);
12762
+ if (resizeObserver === null) {
12763
+ window.addEventListener("resize", resize);
12764
+ } else {
12765
+ resizeObserver.observe(canvas);
12766
+ }
12767
+ resize();
12768
+ const handleContextLost = (event) => {
12769
+ event.preventDefault();
12770
+ setRenderFailed(true);
12771
+ };
12772
+ canvas.addEventListener("webglcontextlost", handleContextLost);
12773
+ renderer.setAnimationLoop(renderFrame);
12774
+ return () => {
12775
+ renderer.setAnimationLoop(null);
12776
+ if (resizeObserver === null) {
12777
+ window.removeEventListener("resize", resize);
12778
+ } else {
12779
+ resizeObserver.disconnect();
12780
+ }
12781
+ canvas.removeEventListener("webglcontextlost", handleContextLost);
12782
+ disposeScene(scene);
12783
+ renderer.dispose();
12784
+ };
12785
+ }, []);
12786
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "relative flex size-24 items-center justify-center", "aria-hidden": "true", children: [
12787
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
12788
+ "canvas",
12789
+ {
12790
+ ref: canvasRef,
12791
+ className: renderFailed ? "hidden" : "block size-full bg-transparent",
12792
+ "data-inspector-pet-canvas": "true"
12793
+ }
12794
+ ),
12795
+ renderFailed && /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-22 text-amber-500 drop-shadow-[0_0_18px_rgba(245,158,11,0.28)]" })
12796
+ ] });
12797
+ }
12336
12798
  const INSPECTOR_PET_ROAMING_STORAGE_KEY = "agent-inspector.pet.roaming";
12337
- const INSPECTOR_PET_MIN_DRIFT_MS = 5200;
12338
- const INSPECTOR_PET_DRIFT_WINDOW_MS = 2400;
12799
+ const INSPECTOR_PET_MIN_PAUSE_MS = 11e3;
12800
+ const INSPECTOR_PET_PAUSE_WINDOW_MS = 9e3;
12801
+ const INSPECTOR_PET_SCUTTLE_MS = 7200;
12339
12802
  const INSPECTOR_PET_PANEL_DISMISS_MS = 6e3;
12340
12803
  const INSPECTOR_PET_REACTION_MS = 760;
12341
12804
  const INSPECTOR_PET_DRAG_THRESHOLD = 4;
12805
+ const INSPECTOR_PET_TOP_LAYER_Z_INDEX = 2147483647;
12342
12806
  function readViewport() {
12343
12807
  if (typeof window === "undefined") return { width: 1280, height: 720, scale: 1 };
12344
12808
  const rawScale = window.getComputedStyle(document.documentElement).getPropertyValue("--inspector-ui-scale");
@@ -12383,13 +12847,14 @@ function moodMotionClass(mood) {
12383
12847
  }
12384
12848
  }
12385
12849
  function InspectorPet({ logs }) {
12850
+ const [portalHost, setPortalHost] = reactExports.useState(null);
12386
12851
  const [viewport, setViewport] = reactExports.useState(readViewport);
12387
12852
  const [position, setPosition] = reactExports.useState({
12388
12853
  x: INSPECTOR_PET_MARGIN,
12389
12854
  y: INSPECTOR_PET_MARGIN
12390
12855
  });
12391
12856
  const [clockMs, setClockMs] = reactExports.useState(() => Date.now());
12392
- const [motionTick, setMotionTick] = reactExports.useState(0);
12857
+ const [scuttling, setScuttling] = reactExports.useState(false);
12393
12858
  const [panelTick, setPanelTick] = reactExports.useState(0);
12394
12859
  const [panelVisible, setPanelVisible] = reactExports.useState(false);
12395
12860
  const [reactionTick, setReactionTick] = reactExports.useState(0);
@@ -12404,10 +12869,11 @@ function InspectorPet({ logs }) {
12404
12869
  () => inspectorPetPanelPosition(position, viewport),
12405
12870
  [position, viewport]
12406
12871
  );
12872
+ reactExports.useEffect(() => setPortalHost(document.body), []);
12407
12873
  reactExports.useEffect(() => {
12408
12874
  const nextViewport = readViewport();
12409
12875
  setViewport(nextViewport);
12410
- setPosition(randomInspectorPetEdgePosition(nextViewport, Math.random(), Math.random()));
12876
+ setPosition(randomInspectorPetEdgePosition(nextViewport, Math.random()));
12411
12877
  }, []);
12412
12878
  reactExports.useEffect(() => {
12413
12879
  const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
@@ -12428,25 +12894,31 @@ function InspectorPet({ logs }) {
12428
12894
  }, [roaming]);
12429
12895
  reactExports.useEffect(() => {
12430
12896
  if (!roaming || dragging || reduceMotion) return void 0;
12431
- let timeoutId = null;
12432
- const scheduleDrift = () => {
12433
- const delay = INSPECTOR_PET_MIN_DRIFT_MS + Math.random() * INSPECTOR_PET_DRIFT_WINDOW_MS;
12434
- timeoutId = window.setTimeout(() => {
12435
- setMotionTick((current) => current + 1);
12897
+ let pauseTimeoutId = null;
12898
+ let scuttleTimeoutId = null;
12899
+ const scheduleScuttle = () => {
12900
+ const delay = INSPECTOR_PET_MIN_PAUSE_MS + Math.random() * INSPECTOR_PET_PAUSE_WINDOW_MS;
12901
+ pauseTimeoutId = window.setTimeout(() => {
12902
+ setScuttling(true);
12436
12903
  setPosition(
12437
12904
  (current) => nextInspectorPetWalkingPosition(current, viewport, Math.random(), Math.random())
12438
12905
  );
12439
- scheduleDrift();
12906
+ scuttleTimeoutId = window.setTimeout(() => {
12907
+ setScuttling(false);
12908
+ scheduleScuttle();
12909
+ }, INSPECTOR_PET_SCUTTLE_MS);
12440
12910
  }, delay);
12441
12911
  };
12442
- scheduleDrift();
12912
+ scheduleScuttle();
12443
12913
  return () => {
12444
- if (timeoutId !== null) window.clearTimeout(timeoutId);
12914
+ if (pauseTimeoutId !== null) window.clearTimeout(pauseTimeoutId);
12915
+ if (scuttleTimeoutId !== null) window.clearTimeout(scuttleTimeoutId);
12445
12916
  };
12446
12917
  }, [dragging, reduceMotion, roaming, viewport]);
12447
12918
  reactExports.useEffect(() => {
12448
12919
  const handleResize = () => {
12449
12920
  const nextViewport = readViewport();
12921
+ setScuttling(false);
12450
12922
  setViewport(nextViewport);
12451
12923
  setPosition((current) => snapInspectorPetToEdge(current, nextViewport));
12452
12924
  };
@@ -12489,6 +12961,7 @@ function InspectorPet({ logs }) {
12489
12961
  moved: false
12490
12962
  };
12491
12963
  event.currentTarget.setPointerCapture(event.pointerId);
12964
+ setScuttling(false);
12492
12965
  setPanelVisible(false);
12493
12966
  setDragging(true);
12494
12967
  }, []);
@@ -12533,135 +13006,136 @@ function InspectorPet({ logs }) {
12533
13006
  dragOccurredRef.current = false;
12534
13007
  setDragging(false);
12535
13008
  }, []);
13009
+ const handleToggleRoaming = reactExports.useCallback(() => {
13010
+ setScuttling(false);
13011
+ setRoaming((current) => !current);
13012
+ }, []);
12536
13013
  const petStyle = reactExports.useMemo(
12537
13014
  () => ({
12538
- transform: `translate3d(${String(position.x)}px, ${String(position.y)}px, 0) rotate(${motionTick % 2 === 0 ? "-4deg" : "5deg"}) scale(${dragging ? "1.08" : "1"})`,
12539
- transition: dragging ? "none" : "transform 4800ms cubic-bezier(0.33, 1, 0.68, 1)",
13015
+ transform: `translate3d(${String(position.x)}px, ${String(position.y)}px, 0) scale(${dragging ? "1.04" : "1"})`,
13016
+ transition: dragging ? "none" : scuttling ? `transform ${String(INSPECTOR_PET_SCUTTLE_MS)}ms cubic-bezier(0.22, 0.72, 0.32, 1)` : "transform 900ms cubic-bezier(0.22, 0.72, 0.32, 1)",
12540
13017
  touchAction: "none"
12541
13018
  }),
12542
- [dragging, motionTick, position.x, position.y]
13019
+ [dragging, position.x, position.y, scuttling]
12543
13020
  );
12544
13021
  const panelStyle = reactExports.useMemo(
12545
13022
  () => ({ left: panelPosition.x, top: panelPosition.y }),
12546
13023
  [panelPosition.x, panelPosition.y]
12547
13024
  );
12548
13025
  const roamingLabel = roaming ? "Pause pet roaming" : "Resume pet roaming";
12549
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
12550
- /* @__PURE__ */ jsxRuntimeExports.jsx(
12551
- "button",
13026
+ if (portalHost === null) return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, {});
13027
+ return reactDomExports.createPortal(
13028
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
13029
+ "div",
12552
13030
  {
12553
- type: "button",
12554
- className: cn(
12555
- "fixed left-0 top-0 z-30 flex size-14 items-center justify-center bg-transparent text-foreground outline-none transition-[filter,opacity] duration-200 hover:opacity-95 hover:drop-shadow-[0_0_24px_rgba(245,158,11,0.36)] focus-visible:drop-shadow-[0_0_12px_rgba(251,191,36,0.72)]",
12556
- dragging ? "cursor-grabbing" : "cursor-grab"
12557
- ),
12558
- style: petStyle,
12559
- "data-pet-mood": status.mood,
12560
- onClick: handlePet,
12561
- onPointerDown: handlePointerDown,
12562
- onPointerMove: handlePointerMove,
12563
- onPointerUp: handlePointerUp,
12564
- onPointerCancel: handlePointerCancel,
12565
- "aria-label": `Inspector pet: ${status.label}`,
12566
- title: status.label,
12567
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
12568
- "span",
12569
- {
12570
- className: cn(
12571
- "relative flex size-12 items-center justify-center",
12572
- reactionVisible && "inspector-pet-patted"
12573
- ),
12574
- "aria-hidden": "true",
12575
- children: [
12576
- /* @__PURE__ */ jsxRuntimeExports.jsx(
13031
+ className: "pointer-events-none fixed inset-0 isolate",
13032
+ style: { zIndex: INSPECTOR_PET_TOP_LAYER_Z_INDEX },
13033
+ "data-inspector-pet-layer": "true",
13034
+ children: [
13035
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
13036
+ "button",
13037
+ {
13038
+ type: "button",
13039
+ className: cn(
13040
+ "pointer-events-auto fixed left-0 top-0 z-10 flex size-28 items-center justify-center bg-transparent text-foreground outline-none transition-[filter,opacity] duration-200 hover:opacity-95 hover:drop-shadow-[0_0_28px_rgba(245,158,11,0.32)] focus-visible:drop-shadow-[0_0_14px_rgba(251,191,36,0.72)]",
13041
+ dragging ? "cursor-grabbing" : "cursor-grab"
13042
+ ),
13043
+ style: petStyle,
13044
+ "data-pet-mood": status.mood,
13045
+ "data-pet-moving": scuttling ? "true" : "false",
13046
+ onClick: handlePet,
13047
+ onPointerDown: handlePointerDown,
13048
+ onPointerMove: handlePointerMove,
13049
+ onPointerUp: handlePointerUp,
13050
+ onPointerCancel: handlePointerCancel,
13051
+ "aria-label": `Inspector pet: ${status.label}`,
13052
+ title: status.label,
13053
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
12577
13054
  "span",
12578
13055
  {
12579
13056
  className: cn(
12580
- "relative flex size-12 items-center justify-center",
12581
- moodMotionClass(status.mood)
13057
+ "relative z-10 flex size-24 items-center justify-center",
13058
+ reactionVisible && "inspector-pet-patted"
12582
13059
  ),
12583
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
12584
- "span",
12585
- {
12586
- className: cn(
12587
- "relative flex size-12 items-center justify-center",
12588
- !dragging && "floating-crab-body"
12589
- ),
12590
- children: [
12591
- /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "relative z-10 size-11 text-amber-500 drop-shadow-[0_0_15px_rgba(245,158,11,0.30)]" }),
12592
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "pointer-events-none absolute inset-x-0 bottom-0 z-0 flex items-end justify-center gap-1 text-amber-400/80", children: [0, 1, 2, 3, 4, 5].map((leg) => /* @__PURE__ */ jsxRuntimeExports.jsx(
12593
- "span",
13060
+ "aria-hidden": "true",
13061
+ children: [
13062
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
13063
+ "span",
13064
+ {
13065
+ className: cn(
13066
+ "relative flex size-24 items-center justify-center",
13067
+ moodMotionClass(status.mood)
13068
+ ),
13069
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "relative flex size-24 items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
13070
+ InspectorPetCrab3D,
12594
13071
  {
12595
- className: cn(
12596
- "floating-crab-leg h-2 w-0.5 rounded-full bg-current shadow-[0_0_7px_rgba(245,158,11,0.24)]",
12597
- leg < 3 ? "floating-crab-leg-left" : "floating-crab-leg-right",
12598
- dragging && "animate-none"
12599
- ),
12600
- style: { animationDelay: `${String(leg % 2 === 0 ? 0 : 180)}ms` }
12601
- },
12602
- leg
12603
- )) })
12604
- ]
12605
- }
12606
- )
12607
- }
12608
- ),
12609
- /* @__PURE__ */ jsxRuntimeExports.jsx(
12610
- "span",
12611
- {
12612
- className: cn(
12613
- "absolute right-0 top-1 size-2 rounded-full border border-background/90",
12614
- moodDotClass(status.mood),
12615
- status.mood === "active" && !reduceMotion && "animate-pulse"
12616
- )
13072
+ mood: status.mood,
13073
+ moving: scuttling && !dragging,
13074
+ reduceMotion
13075
+ }
13076
+ ) })
13077
+ }
13078
+ ),
13079
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
13080
+ "span",
13081
+ {
13082
+ className: cn(
13083
+ "absolute right-0 top-2 size-3 rounded-full border border-background/90",
13084
+ moodDotClass(status.mood),
13085
+ status.mood === "active" && !reduceMotion && "animate-pulse"
13086
+ )
13087
+ }
13088
+ ),
13089
+ reactionVisible && /* @__PURE__ */ jsxRuntimeExports.jsx(
13090
+ Heart,
13091
+ {
13092
+ className: "inspector-pet-heart absolute -right-2 -top-3 size-5 fill-amber-400 text-amber-400"
13093
+ },
13094
+ reactionTick
13095
+ )
13096
+ ]
12617
13097
  }
12618
- ),
12619
- reactionVisible && /* @__PURE__ */ jsxRuntimeExports.jsx(
12620
- Heart,
12621
- {
12622
- className: "inspector-pet-heart absolute -right-1 -top-2 size-3.5 fill-amber-400 text-amber-400"
12623
- },
12624
- reactionTick
12625
13098
  )
12626
- ]
12627
- }
12628
- )
12629
- }
12630
- ),
12631
- panelVisible && /* @__PURE__ */ jsxRuntimeExports.jsxs(
12632
- "div",
12633
- {
12634
- className: "fixed z-50 w-48 rounded-md border border-border/80 bg-popover/95 px-3 py-2 text-popover-foreground shadow-[0_14px_34px_rgba(0,0,0,0.28)] backdrop-blur",
12635
- style: panelStyle,
12636
- role: "status",
12637
- "aria-live": "polite",
12638
- children: [
12639
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
12640
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("size-2 shrink-0 rounded-full", moodDotClass(status.mood)) }),
12641
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", children: status.label }),
12642
- /* @__PURE__ */ jsxRuntimeExports.jsx(
12643
- "button",
12644
- {
12645
- type: "button",
12646
- className: "inline-flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none",
12647
- onClick: () => setRoaming((current) => !current),
12648
- "aria-label": roamingLabel,
12649
- title: roamingLabel,
12650
- children: roaming ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { className: "size-3.5" })
12651
- }
12652
- )
12653
- ] }),
12654
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex items-center justify-between gap-3 font-mono text-[10px] text-muted-foreground", children: [
12655
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
12656
- String(status.capturedCount),
12657
- " captured"
12658
- ] }),
12659
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: status.pendingCount > 0 ? `${String(status.pendingCount)} active` : roaming ? "Roaming" : "Resting" })
12660
- ] })
13099
+ }
13100
+ ),
13101
+ panelVisible && /* @__PURE__ */ jsxRuntimeExports.jsxs(
13102
+ "div",
13103
+ {
13104
+ className: "pointer-events-auto fixed z-20 w-48 rounded-md border border-border/80 bg-popover/95 px-3 py-2 text-popover-foreground shadow-[0_14px_34px_rgba(0,0,0,0.28)] backdrop-blur",
13105
+ style: panelStyle,
13106
+ role: "status",
13107
+ "aria-live": "polite",
13108
+ children: [
13109
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
13110
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("size-2 shrink-0 rounded-full", moodDotClass(status.mood)) }),
13111
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", children: status.label }),
13112
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
13113
+ "button",
13114
+ {
13115
+ type: "button",
13116
+ className: "inline-flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none",
13117
+ onClick: handleToggleRoaming,
13118
+ "aria-label": roamingLabel,
13119
+ title: roamingLabel,
13120
+ children: roaming ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { className: "size-3.5" })
13121
+ }
13122
+ )
13123
+ ] }),
13124
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex items-center justify-between gap-3 font-mono text-[10px] text-muted-foreground", children: [
13125
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
13126
+ String(status.capturedCount),
13127
+ " captured"
13128
+ ] }),
13129
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: status.pendingCount > 0 ? `${String(status.pendingCount)} active` : scuttling ? "Scuttling" : roaming ? "Resting" : "Paused" })
13130
+ ] })
13131
+ ]
13132
+ }
13133
+ )
12661
13134
  ]
12662
13135
  }
12663
- )
12664
- ] });
13136
+ ),
13137
+ portalHost
13138
+ );
12665
13139
  }
12666
13140
  const DEFAULT_QUESTION = "Analyze the current Inspector session. What looks abnormal, slow, or likely to hang?";
12667
13141
  function providerSupportsPiAgent(provider) {