@tonyclaw/agent-inspector 3.0.24 → 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 (39) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-BC52FYwn.js → CompareDrawer-SZRUDQc9.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-BJvwOrSi.js +4234 -0
  4. package/.output/public/assets/{ReplayDialog-BcKsarKt.js → ReplayDialog-DgfmqiM3.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-DR0uGpCj.js → RequestAnatomy-CS6c7Qfn.js} +1 -1
  6. package/.output/public/assets/{ResponseView-kxAAPfmn.js → ResponseView-C0Ri_5gm.js} +2 -2
  7. package/.output/public/assets/{StreamingChunkSequence-YQR0PjQj.js → StreamingChunkSequence-7dYcHWyZ.js} +1 -1
  8. package/.output/public/assets/{_sessionId-DRl-Y9om.js → _sessionId-D-V84cgw.js} +1 -1
  9. package/.output/public/assets/{index-CQbrc-hi.js → index-CSIL7-MK.js} +3 -3
  10. package/.output/public/assets/index-CmeF5XXS.css +1 -0
  11. package/.output/public/assets/{index-CcSIBI4L.js → index-DgFWCxOs.js} +1 -1
  12. package/.output/public/assets/{index-ZrMgyEZV.js → index-DrVJsks2.js} +1 -1
  13. package/.output/public/assets/{json-viewer-CS5pwnEA.js → json-viewer-C9Ji3gYN.js} +1 -1
  14. package/.output/server/_libs/lucide-react.mjs +245 -217
  15. package/.output/server/_libs/three.mjs +26016 -0
  16. package/.output/server/{_sessionId-BLk2xPkR.mjs → _sessionId-BQ6YQOO1.mjs} +7 -6
  17. package/.output/server/_ssr/{CompareDrawer-Cw7Dseo3.mjs → CompareDrawer-CqNzIeMR.mjs} +8 -7
  18. package/.output/server/_ssr/{ProxyViewerContainer-D8SB0zyQ.mjs → ProxyViewerContainer-CMC7Vsbw.mjs} +1164 -405
  19. package/.output/server/_ssr/{ReplayDialog-B-YMhZkE.mjs → ReplayDialog-D9p_LaTr.mjs} +9 -8
  20. package/.output/server/_ssr/{RequestAnatomy-a9d8UTYZ.mjs → RequestAnatomy-B4ge3qNZ.mjs} +8 -7
  21. package/.output/server/_ssr/{ResponseView-PzaZS4CN.mjs → ResponseView-BCqMKApI.mjs} +8 -7
  22. package/.output/server/_ssr/{StreamingChunkSequence-C21_TCXk.mjs → StreamingChunkSequence-BA4zTLE4.mjs} +8 -7
  23. package/.output/server/_ssr/{index-CrCeNP7H.mjs → index-CzxhDjlq.mjs} +7 -6
  24. package/.output/server/_ssr/index.mjs +2 -2
  25. package/.output/server/_ssr/{json-viewer-Di2gxs9H.mjs → json-viewer-B3qV6iEJ.mjs} +8 -7
  26. package/.output/server/_ssr/{router-BJzdUbcH.mjs → router-CZe_R31M.mjs} +4 -4
  27. package/.output/server/{_tanstack-start-manifest_v-CGBTdAYC.mjs → _tanstack-start-manifest_v-B50VTLrH.mjs} +1 -1
  28. package/.output/server/index.mjs +68 -68
  29. package/package.json +3 -1
  30. package/src/components/ProxyViewer.tsx +2 -280
  31. package/src/components/inspector-pet/InspectorPet.tsx +405 -0
  32. package/src/components/inspector-pet/InspectorPetCrab3D.tsx +455 -0
  33. package/src/components/inspector-pet/inspectorPetLogic.ts +258 -0
  34. package/src/components/providers/ProviderForm.tsx +238 -163
  35. package/src/components/providers/ProvidersPanel.tsx +16 -6
  36. package/src/components/providers/providerFormCapabilities.ts +36 -0
  37. package/styles/globals.css +64 -28
  38. package/.output/public/assets/ProxyViewerContainer-WuChOrSS.js +0 -127
  39. package/.output/public/assets/index-DQeXi4Rv.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-BJzdUbcH.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,7 +7,9 @@ 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 { 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, e as Plus, B as BrainCircuit, L as LoaderCircle, f as Send, A as ArrowLeft, g as ChevronUp, h as Copy, i as ChevronsLeft, j as ChevronLeft, k as ChevronRight, l as ChevronsRight, m as Clapperboard, n as Flag, o as Siren, p as Timer, G as Gauge, q as Sun, E as Eye, M as Moon, r as ALargeSmall, s as Layers, t as FlaskConical, u as Sparkles, v as Activity, w as Beaker, N as Network, x as Terminal, y as Settings, z as Monitor, H as Rocket, J as PackageCheck, K as History, O as Scan, Q as CircleAlert, V as ArrowUpRight, W as ArrowDownRight, Y as Cpu, Z as FolderOpen, _ as Bell, $ as Clock3, a0 as ShieldAlert, a1 as EyeOff, a2 as ExternalLink, a3 as Clock, a4 as RotateCw, a5 as Pencil, a6 as MessageSquare, a7 as Zap, a8 as Wrench, a9 as Brain, aa as CircleCheckBig, ab as Minus, ac as CircleX, ad as ShieldCheck, ae as Save, af as FileSearch, ag as CloudUpload, ah as CircleQuestionMark, ai as Server, aj as Lock, ak as Wifi, al as WifiOff, am as OctagonAlert, an as Radio, ao as Globe, ap as FileTerminal, aq as ChevronsUp, ar as ChevronsDown, as as FileDiff, at as RotateCcw, au as GitCompareArrows, av as ArrowUp, aw as ArrowDown, ax as Ellipsis, ay as CodeXml, az as Blocks, aA as MousePointerClick, aB as FileText, aC as Rows3, aD as Columns2 } from "../_libs/lucide-react.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";
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";
13
15
  import { D as Dialog$1, a as DialogContent$1, b as DialogClose, c as DialogTitle$1, d as DialogDescription$1, e as DialogPortal$1, f as DialogOverlay$1, g as DialogTrigger$1 } from "../_libs/radix-ui__react-dialog.mjs";
@@ -2525,27 +2527,27 @@ function useCopyFeedback(text) {
2525
2527
  return { copied, copy };
2526
2528
  }
2527
2529
  const LazyCompareDrawer = reactExports.lazy(
2528
- () => import("./CompareDrawer-Cw7Dseo3.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-B-YMhZkE.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-a9d8UTYZ.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-PzaZS4CN.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-C21_TCXk.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-Di2gxs9H.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-Di2gxs9H.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";
9607
9639
  }
9608
- if (openaiBaseUrl.trim() && !isValidUrl(openaiBaseUrl.trim())) {
9609
- newErrors.openaiBaseUrl = "Invalid URL format";
9640
+ if (supportsOpenai && !supportsOpenaiChat && !supportsOpenaiResponses) {
9641
+ newErrors.openaiCapabilities = "Select Chat Completions, Responses, or both";
9610
9642
  }
9611
- if (openaiResponsesBaseUrl.trim() && !isValidUrl(openaiResponsesBaseUrl.trim())) {
9612
- newErrors.openaiResponsesBaseUrl = "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
+ }
9656
+ }
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,20 +12279,878 @@ function AgentLabDialog({ currentSessionId, logCount }) {
12134
12279
  ] })
12135
12280
  ] });
12136
12281
  }
12137
- const DEFAULT_QUESTION = "Analyze the current Inspector session. What looks abnormal, slow, or likely to hang?";
12138
- function providerSupportsPiAgent(provider) {
12139
- return provider.openaiResponsesBaseUrl !== void 0 || provider.openaiBaseUrl !== void 0 || provider.anthropicBaseUrl !== void 0;
12282
+ const INSPECTOR_PET_SIZE = 112;
12283
+ const INSPECTOR_PET_MARGIN = 14;
12284
+ const INSPECTOR_PET_EDGE_GAP = 20;
12285
+ const INSPECTOR_PET_MIN_STEP = 36;
12286
+ const INSPECTOR_PET_MAX_STEP = 92;
12287
+ const INSPECTOR_PET_SLEEP_AFTER_MS = 45e3;
12288
+ const INSPECTOR_PET_PANEL_WIDTH = 192;
12289
+ const INSPECTOR_PET_PANEL_HEIGHT = 68;
12290
+ const INSPECTOR_PET_PANEL_GAP = 10;
12291
+ const INSPECTOR_PET_VERTICAL_SAFE_MARGIN = 78;
12292
+ function viewportScale(viewport) {
12293
+ const scale = viewport.scale ?? 1;
12294
+ return Number.isFinite(scale) && scale > 0 ? scale : 1;
12295
+ }
12296
+ function scaledMetric(value, viewport) {
12297
+ return value * viewportScale(viewport);
12298
+ }
12299
+ function clampUnit(value) {
12300
+ return Math.min(Math.max(value, 0), 1);
12301
+ }
12302
+ function maximumPetX(viewport) {
12303
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12304
+ return Math.max(edgeGap, viewport.width - scaledMetric(INSPECTOR_PET_SIZE, viewport) - edgeGap);
12305
+ }
12306
+ function maximumPetY(viewport) {
12307
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12308
+ return Math.max(edgeGap, viewport.height - scaledMetric(INSPECTOR_PET_SIZE, viewport) - edgeGap);
12309
+ }
12310
+ function clampInspectorPetPosition(position, viewport) {
12311
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12312
+ return {
12313
+ x: Math.min(Math.max(position.x, edgeGap), maximumPetX(viewport)),
12314
+ y: Math.min(Math.max(position.y, edgeGap), maximumPetY(viewport))
12315
+ };
12140
12316
  }
12141
- function providerProtocolLabel(provider) {
12142
- if (provider.openaiResponsesBaseUrl !== void 0 && provider.openaiResponsesBaseUrl !== "") {
12143
- return "Responses";
12317
+ function nearestInspectorPetEdge(position, viewport) {
12318
+ const clamped = clampInspectorPetPosition(position, viewport);
12319
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12320
+ const distances = [
12321
+ { edge: "left", distance: clamped.x - edgeGap },
12322
+ { edge: "right", distance: maximumPetX(viewport) - clamped.x },
12323
+ { edge: "top", distance: clamped.y - edgeGap },
12324
+ { edge: "bottom", distance: maximumPetY(viewport) - clamped.y }
12325
+ ];
12326
+ return distances.reduce((best, current) => current.distance < best.distance ? current : best).edge;
12327
+ }
12328
+ function snapInspectorPetToEdge(position, viewport) {
12329
+ const clamped = clampInspectorPetPosition(position, viewport);
12330
+ const edge = nearestInspectorPetEdge(clamped, viewport);
12331
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12332
+ switch (edge) {
12333
+ case "left":
12334
+ return { x: edgeGap, y: clamped.y };
12335
+ case "right":
12336
+ return { x: maximumPetX(viewport), y: clamped.y };
12337
+ case "top":
12338
+ return { x: clamped.x, y: edgeGap };
12339
+ case "bottom":
12340
+ return { x: clamped.x, y: maximumPetY(viewport) };
12341
+ }
12342
+ }
12343
+ function randomInspectorPetEdgePosition(viewport, positionSample) {
12344
+ const maxX = maximumPetX(viewport);
12345
+ const maxY = maximumPetY(viewport);
12346
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12347
+ return {
12348
+ x: edgeGap + clampUnit(positionSample) * (maxX - edgeGap),
12349
+ y: maxY
12350
+ };
12351
+ }
12352
+ function nextInspectorPetWalkingPosition(position, viewport, stepSample, directionSample) {
12353
+ const clamped = snapInspectorPetToEdge(position, viewport);
12354
+ const step = scaledMetric(INSPECTOR_PET_MIN_STEP, viewport) + clampUnit(stepSample) * scaledMetric(INSPECTOR_PET_MAX_STEP - INSPECTOR_PET_MIN_STEP, viewport);
12355
+ const direction = directionSample >= 0.5 ? 1 : -1;
12356
+ const edge = nearestInspectorPetEdge(clamped, viewport);
12357
+ const maxX = maximumPetX(viewport);
12358
+ const maxY = maximumPetY(viewport);
12359
+ const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
12360
+ const minimumSafeY = Math.min(scaledMetric(INSPECTOR_PET_VERTICAL_SAFE_MARGIN, viewport), maxY);
12361
+ switch (edge) {
12362
+ case "left":
12363
+ return {
12364
+ x: edgeGap,
12365
+ y: Math.min(Math.max(clamped.y + step * direction, minimumSafeY), maxY)
12366
+ };
12367
+ case "right":
12368
+ return {
12369
+ x: maxX,
12370
+ y: Math.min(Math.max(clamped.y + step * direction, minimumSafeY), maxY)
12371
+ };
12372
+ case "top":
12373
+ return {
12374
+ x: Math.min(Math.max(clamped.x + step * direction, edgeGap), maxX),
12375
+ y: edgeGap
12376
+ };
12377
+ case "bottom":
12378
+ return {
12379
+ x: Math.min(Math.max(clamped.x + step * direction, edgeGap), maxX),
12380
+ y: maxY
12381
+ };
12144
12382
  }
12145
- if (provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "") {
12146
- return "Chat";
12383
+ }
12384
+ function inspectorPetPanelPosition(position, viewport) {
12385
+ const edge = nearestInspectorPetEdge(position, viewport);
12386
+ const petSize = scaledMetric(INSPECTOR_PET_SIZE, viewport);
12387
+ const panelWidth = scaledMetric(INSPECTOR_PET_PANEL_WIDTH, viewport);
12388
+ const panelHeight = scaledMetric(INSPECTOR_PET_PANEL_HEIGHT, viewport);
12389
+ const panelGap = scaledMetric(INSPECTOR_PET_PANEL_GAP, viewport);
12390
+ const margin = scaledMetric(INSPECTOR_PET_MARGIN, viewport);
12391
+ let x = position.x + petSize / 2 - panelWidth / 2;
12392
+ let y = position.y + petSize / 2 - panelHeight / 2;
12393
+ switch (edge) {
12394
+ case "left":
12395
+ x = position.x + petSize + panelGap;
12396
+ break;
12397
+ case "right":
12398
+ x = position.x - panelWidth - panelGap;
12399
+ break;
12400
+ case "top":
12401
+ y = position.y + petSize + panelGap;
12402
+ break;
12403
+ case "bottom":
12404
+ y = position.y - panelHeight - panelGap;
12405
+ break;
12147
12406
  }
12148
- return "Anthropic";
12407
+ return {
12408
+ x: Math.min(Math.max(x, margin), Math.max(margin, viewport.width - panelWidth - margin)),
12409
+ y: Math.min(Math.max(y, margin), Math.max(margin, viewport.height - panelHeight - margin))
12410
+ };
12149
12411
  }
12150
- function sortProviderBySource(providers) {
12412
+ function latestCapturedLog(logs) {
12413
+ let latest = null;
12414
+ for (const log of logs) {
12415
+ if (latest === null || log.id > latest.id) latest = log;
12416
+ }
12417
+ return latest;
12418
+ }
12419
+ function logNeedsAttention(log) {
12420
+ const hasHttpError = log.responseStatus !== null && log.responseStatus >= 400;
12421
+ const hasStreamError = log.error !== null && log.error !== void 0 && log.error !== "";
12422
+ return hasHttpError || hasStreamError;
12423
+ }
12424
+ function resolveInspectorPetStatus(logs, nowMs) {
12425
+ const pendingCount = logs.filter((log) => log.responseStatus === null).length;
12426
+ const capturedCount = logs.length;
12427
+ if (pendingCount > 0) {
12428
+ return {
12429
+ mood: "active",
12430
+ label: `${String(pendingCount)} request${pendingCount === 1 ? "" : "s"} in flight`,
12431
+ pendingCount,
12432
+ capturedCount
12433
+ };
12434
+ }
12435
+ const latest = latestCapturedLog(logs);
12436
+ if (latest !== null && logNeedsAttention(latest)) {
12437
+ return {
12438
+ mood: "attention",
12439
+ label: "Latest request needs attention",
12440
+ pendingCount,
12441
+ capturedCount
12442
+ };
12443
+ }
12444
+ if (latest === null) {
12445
+ return {
12446
+ mood: "idle",
12447
+ label: "Waiting for traffic",
12448
+ pendingCount,
12449
+ capturedCount
12450
+ };
12451
+ }
12452
+ const latestTimestamp = Date.parse(latest.timestamp);
12453
+ if (Number.isFinite(latestTimestamp) && nowMs - latestTimestamp >= INSPECTOR_PET_SLEEP_AFTER_MS) {
12454
+ return {
12455
+ mood: "sleeping",
12456
+ label: "All quiet",
12457
+ pendingCount,
12458
+ capturedCount
12459
+ };
12460
+ }
12461
+ return {
12462
+ mood: "idle",
12463
+ label: "Watching the session",
12464
+ pendingCount,
12465
+ capturedCount
12466
+ };
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
+ }
12798
+ const INSPECTOR_PET_ROAMING_STORAGE_KEY = "agent-inspector.pet.roaming";
12799
+ const INSPECTOR_PET_MIN_PAUSE_MS = 11e3;
12800
+ const INSPECTOR_PET_PAUSE_WINDOW_MS = 9e3;
12801
+ const INSPECTOR_PET_SCUTTLE_MS = 7200;
12802
+ const INSPECTOR_PET_PANEL_DISMISS_MS = 6e3;
12803
+ const INSPECTOR_PET_REACTION_MS = 760;
12804
+ const INSPECTOR_PET_DRAG_THRESHOLD = 4;
12805
+ const INSPECTOR_PET_TOP_LAYER_Z_INDEX = 2147483647;
12806
+ function readViewport() {
12807
+ if (typeof window === "undefined") return { width: 1280, height: 720, scale: 1 };
12808
+ const rawScale = window.getComputedStyle(document.documentElement).getPropertyValue("--inspector-ui-scale");
12809
+ const parsedScale = Number.parseFloat(rawScale);
12810
+ const scale = Number.isFinite(parsedScale) && parsedScale > 0 ? parsedScale : 1;
12811
+ return {
12812
+ width: Math.max(window.innerWidth, INSPECTOR_PET_MARGIN * 2),
12813
+ height: Math.max(window.innerHeight, INSPECTOR_PET_MARGIN * 2),
12814
+ scale
12815
+ };
12816
+ }
12817
+ function readStoredRoaming() {
12818
+ if (typeof window === "undefined") return true;
12819
+ try {
12820
+ return window.localStorage.getItem(INSPECTOR_PET_ROAMING_STORAGE_KEY) !== "0";
12821
+ } catch {
12822
+ return true;
12823
+ }
12824
+ }
12825
+ function moodDotClass(mood) {
12826
+ switch (mood) {
12827
+ case "active":
12828
+ return "bg-cyan-400 shadow-[0_0_9px_rgba(34,211,238,0.65)]";
12829
+ case "attention":
12830
+ return "bg-red-500 shadow-[0_0_9px_rgba(239,68,68,0.55)]";
12831
+ case "idle":
12832
+ return "bg-amber-400 shadow-[0_0_8px_rgba(251,191,36,0.45)]";
12833
+ case "sleeping":
12834
+ return "bg-muted-foreground/45";
12835
+ }
12836
+ }
12837
+ function moodMotionClass(mood) {
12838
+ switch (mood) {
12839
+ case "active":
12840
+ return "inspector-pet-active";
12841
+ case "attention":
12842
+ return "inspector-pet-attention";
12843
+ case "idle":
12844
+ return "";
12845
+ case "sleeping":
12846
+ return "inspector-pet-sleeping";
12847
+ }
12848
+ }
12849
+ function InspectorPet({ logs }) {
12850
+ const [portalHost, setPortalHost] = reactExports.useState(null);
12851
+ const [viewport, setViewport] = reactExports.useState(readViewport);
12852
+ const [position, setPosition] = reactExports.useState({
12853
+ x: INSPECTOR_PET_MARGIN,
12854
+ y: INSPECTOR_PET_MARGIN
12855
+ });
12856
+ const [clockMs, setClockMs] = reactExports.useState(() => Date.now());
12857
+ const [scuttling, setScuttling] = reactExports.useState(false);
12858
+ const [panelTick, setPanelTick] = reactExports.useState(0);
12859
+ const [panelVisible, setPanelVisible] = reactExports.useState(false);
12860
+ const [reactionTick, setReactionTick] = reactExports.useState(0);
12861
+ const [reactionVisible, setReactionVisible] = reactExports.useState(false);
12862
+ const [dragging, setDragging] = reactExports.useState(false);
12863
+ const [roaming, setRoaming] = reactExports.useState(readStoredRoaming);
12864
+ const [reduceMotion, setReduceMotion] = reactExports.useState(false);
12865
+ const dragStateRef = reactExports.useRef(null);
12866
+ const dragOccurredRef = reactExports.useRef(false);
12867
+ const status = reactExports.useMemo(() => resolveInspectorPetStatus(logs, clockMs), [clockMs, logs]);
12868
+ const panelPosition = reactExports.useMemo(
12869
+ () => inspectorPetPanelPosition(position, viewport),
12870
+ [position, viewport]
12871
+ );
12872
+ reactExports.useEffect(() => setPortalHost(document.body), []);
12873
+ reactExports.useEffect(() => {
12874
+ const nextViewport = readViewport();
12875
+ setViewport(nextViewport);
12876
+ setPosition(randomInspectorPetEdgePosition(nextViewport, Math.random()));
12877
+ }, []);
12878
+ reactExports.useEffect(() => {
12879
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
12880
+ const handleChange = (event) => setReduceMotion(event.matches);
12881
+ setReduceMotion(mediaQuery.matches);
12882
+ mediaQuery.addEventListener("change", handleChange);
12883
+ return () => mediaQuery.removeEventListener("change", handleChange);
12884
+ }, []);
12885
+ reactExports.useEffect(() => {
12886
+ const intervalId = window.setInterval(() => setClockMs(Date.now()), 15e3);
12887
+ return () => window.clearInterval(intervalId);
12888
+ }, []);
12889
+ reactExports.useEffect(() => {
12890
+ try {
12891
+ window.localStorage.setItem(INSPECTOR_PET_ROAMING_STORAGE_KEY, roaming ? "1" : "0");
12892
+ } catch {
12893
+ }
12894
+ }, [roaming]);
12895
+ reactExports.useEffect(() => {
12896
+ if (!roaming || dragging || reduceMotion) return void 0;
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);
12903
+ setPosition(
12904
+ (current) => nextInspectorPetWalkingPosition(current, viewport, Math.random(), Math.random())
12905
+ );
12906
+ scuttleTimeoutId = window.setTimeout(() => {
12907
+ setScuttling(false);
12908
+ scheduleScuttle();
12909
+ }, INSPECTOR_PET_SCUTTLE_MS);
12910
+ }, delay);
12911
+ };
12912
+ scheduleScuttle();
12913
+ return () => {
12914
+ if (pauseTimeoutId !== null) window.clearTimeout(pauseTimeoutId);
12915
+ if (scuttleTimeoutId !== null) window.clearTimeout(scuttleTimeoutId);
12916
+ };
12917
+ }, [dragging, reduceMotion, roaming, viewport]);
12918
+ reactExports.useEffect(() => {
12919
+ const handleResize = () => {
12920
+ const nextViewport = readViewport();
12921
+ setScuttling(false);
12922
+ setViewport(nextViewport);
12923
+ setPosition((current) => snapInspectorPetToEdge(current, nextViewport));
12924
+ };
12925
+ window.addEventListener("resize", handleResize);
12926
+ return () => window.removeEventListener("resize", handleResize);
12927
+ }, []);
12928
+ reactExports.useEffect(() => {
12929
+ if (!panelVisible) return void 0;
12930
+ const timeoutId = window.setTimeout(
12931
+ () => setPanelVisible(false),
12932
+ INSPECTOR_PET_PANEL_DISMISS_MS
12933
+ );
12934
+ return () => window.clearTimeout(timeoutId);
12935
+ }, [panelTick, panelVisible]);
12936
+ reactExports.useEffect(() => {
12937
+ if (!reactionVisible) return void 0;
12938
+ const timeoutId = window.setTimeout(() => setReactionVisible(false), INSPECTOR_PET_REACTION_MS);
12939
+ return () => window.clearTimeout(timeoutId);
12940
+ }, [reactionTick, reactionVisible]);
12941
+ const handlePet = reactExports.useCallback(() => {
12942
+ if (dragOccurredRef.current) {
12943
+ dragOccurredRef.current = false;
12944
+ return;
12945
+ }
12946
+ setClockMs(Date.now());
12947
+ setPanelTick((current) => current + 1);
12948
+ setPanelVisible(true);
12949
+ setReactionTick((current) => current + 1);
12950
+ setReactionVisible(true);
12951
+ }, []);
12952
+ const handlePointerDown = reactExports.useCallback((event) => {
12953
+ const rect = event.currentTarget.getBoundingClientRect();
12954
+ dragOccurredRef.current = false;
12955
+ dragStateRef.current = {
12956
+ pointerId: event.pointerId,
12957
+ offsetX: event.clientX - rect.left,
12958
+ offsetY: event.clientY - rect.top,
12959
+ startX: event.clientX,
12960
+ startY: event.clientY,
12961
+ moved: false
12962
+ };
12963
+ event.currentTarget.setPointerCapture(event.pointerId);
12964
+ setScuttling(false);
12965
+ setPanelVisible(false);
12966
+ setDragging(true);
12967
+ }, []);
12968
+ const handlePointerMove = reactExports.useCallback(
12969
+ (event) => {
12970
+ const dragState = dragStateRef.current;
12971
+ if (dragState === null || dragState.pointerId !== event.pointerId) return;
12972
+ const moved = dragState.moved || Math.abs(event.clientX - dragState.startX) >= INSPECTOR_PET_DRAG_THRESHOLD || Math.abs(event.clientY - dragState.startY) >= INSPECTOR_PET_DRAG_THRESHOLD;
12973
+ dragState.moved = moved;
12974
+ dragOccurredRef.current = moved;
12975
+ setPosition(
12976
+ clampInspectorPetPosition(
12977
+ {
12978
+ x: event.clientX - dragState.offsetX,
12979
+ y: event.clientY - dragState.offsetY
12980
+ },
12981
+ viewport
12982
+ )
12983
+ );
12984
+ },
12985
+ [viewport]
12986
+ );
12987
+ const handlePointerUp = reactExports.useCallback(
12988
+ (event) => {
12989
+ const dragState = dragStateRef.current;
12990
+ if (dragState === null || dragState.pointerId !== event.pointerId) return;
12991
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
12992
+ event.currentTarget.releasePointerCapture(event.pointerId);
12993
+ }
12994
+ dragOccurredRef.current = dragState.moved;
12995
+ dragStateRef.current = null;
12996
+ setDragging(false);
12997
+ setPosition((current) => snapInspectorPetToEdge(current, viewport));
12998
+ },
12999
+ [viewport]
13000
+ );
13001
+ const handlePointerCancel = reactExports.useCallback((event) => {
13002
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
13003
+ event.currentTarget.releasePointerCapture(event.pointerId);
13004
+ }
13005
+ dragStateRef.current = null;
13006
+ dragOccurredRef.current = false;
13007
+ setDragging(false);
13008
+ }, []);
13009
+ const handleToggleRoaming = reactExports.useCallback(() => {
13010
+ setScuttling(false);
13011
+ setRoaming((current) => !current);
13012
+ }, []);
13013
+ const petStyle = reactExports.useMemo(
13014
+ () => ({
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)",
13017
+ touchAction: "none"
13018
+ }),
13019
+ [dragging, position.x, position.y, scuttling]
13020
+ );
13021
+ const panelStyle = reactExports.useMemo(
13022
+ () => ({ left: panelPosition.x, top: panelPosition.y }),
13023
+ [panelPosition.x, panelPosition.y]
13024
+ );
13025
+ const roamingLabel = roaming ? "Pause pet roaming" : "Resume pet roaming";
13026
+ if (portalHost === null) return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, {});
13027
+ return reactDomExports.createPortal(
13028
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
13029
+ "div",
13030
+ {
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(
13054
+ "span",
13055
+ {
13056
+ className: cn(
13057
+ "relative z-10 flex size-24 items-center justify-center",
13058
+ reactionVisible && "inspector-pet-patted"
13059
+ ),
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,
13071
+ {
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
+ ]
13097
+ }
13098
+ )
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
+ )
13134
+ ]
13135
+ }
13136
+ ),
13137
+ portalHost
13138
+ );
13139
+ }
13140
+ const DEFAULT_QUESTION = "Analyze the current Inspector session. What looks abnormal, slow, or likely to hang?";
13141
+ function providerSupportsPiAgent(provider) {
13142
+ return provider.openaiResponsesBaseUrl !== void 0 || provider.openaiBaseUrl !== void 0 || provider.anthropicBaseUrl !== void 0;
13143
+ }
13144
+ function providerProtocolLabel(provider) {
13145
+ if (provider.openaiResponsesBaseUrl !== void 0 && provider.openaiResponsesBaseUrl !== "") {
13146
+ return "Responses";
13147
+ }
13148
+ if (provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "") {
13149
+ return "Chat";
13150
+ }
13151
+ return "Anthropic";
13152
+ }
13153
+ function sortProviderBySource(providers) {
12151
13154
  return providers.toSorted((left, right) => {
12152
13155
  const leftCompanyRank = left.source === "company" ? 0 : 1;
12153
13156
  const rightCompanyRank = right.source === "company" ? 0 : 1;
@@ -14561,256 +15564,12 @@ function BrandHeader({
14561
15564
  ] })
14562
15565
  ] });
14563
15566
  }
14564
- const FLOATING_LOGO_SIZE = 56;
14565
- const FLOATING_LOGO_MARGIN = 14;
14566
- const FLOATING_LOGO_MIN_DRIFT_MS = 5200;
14567
- const FLOATING_LOGO_DRIFT_WINDOW_MS = 2400;
14568
- const FLOATING_LOGO_MIN_STEP = 72;
14569
- const FLOATING_LOGO_MAX_STEP = 156;
14570
15567
  const DETAILS_PANEL_COLLAPSED_STORAGE_KEY = "agent-inspector.detailsPanelCollapsed";
14571
15568
  function readStoredDetailsPanelCollapsed() {
14572
15569
  if (typeof window === "undefined") return true;
14573
15570
  const stored = window.localStorage.getItem(DETAILS_PANEL_COLLAPSED_STORAGE_KEY);
14574
15571
  return stored === null || stored === "1";
14575
15572
  }
14576
- function clampFloatingLogoPosition(position) {
14577
- const maxX = Math.max(
14578
- FLOATING_LOGO_MARGIN,
14579
- window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14580
- );
14581
- const maxY = Math.max(
14582
- FLOATING_LOGO_MARGIN,
14583
- window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14584
- );
14585
- return {
14586
- x: Math.min(Math.max(position.x, FLOATING_LOGO_MARGIN), maxX),
14587
- y: Math.min(Math.max(position.y, FLOATING_LOGO_MARGIN), maxY)
14588
- };
14589
- }
14590
- function snapFloatingLogoToEdge(position) {
14591
- const clamped = clampFloatingLogoPosition(position);
14592
- const maxX = Math.max(
14593
- FLOATING_LOGO_MARGIN,
14594
- window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14595
- );
14596
- const maxY = Math.max(
14597
- FLOATING_LOGO_MARGIN,
14598
- window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14599
- );
14600
- const leftDistance = clamped.x - FLOATING_LOGO_MARGIN;
14601
- const rightDistance = maxX - clamped.x;
14602
- const topDistance = clamped.y - FLOATING_LOGO_MARGIN;
14603
- const bottomDistance = maxY - clamped.y;
14604
- const nearestDistance = Math.min(leftDistance, rightDistance, topDistance, bottomDistance);
14605
- if (nearestDistance === leftDistance) return { x: FLOATING_LOGO_MARGIN, y: clamped.y };
14606
- if (nearestDistance === rightDistance) return { x: maxX, y: clamped.y };
14607
- if (nearestDistance === topDistance) return { x: clamped.x, y: FLOATING_LOGO_MARGIN };
14608
- return { x: clamped.x, y: maxY };
14609
- }
14610
- function randomFloatingLogoEdgePosition() {
14611
- const maxX = Math.max(
14612
- FLOATING_LOGO_MARGIN,
14613
- window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14614
- );
14615
- const maxY = Math.max(
14616
- FLOATING_LOGO_MARGIN,
14617
- window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14618
- );
14619
- const randomX = FLOATING_LOGO_MARGIN + Math.random() * (maxX - FLOATING_LOGO_MARGIN);
14620
- const randomY = FLOATING_LOGO_MARGIN + Math.random() * (maxY - FLOATING_LOGO_MARGIN);
14621
- const edgeIndex = Math.floor(Math.random() * 4);
14622
- switch (edgeIndex) {
14623
- case 0:
14624
- return { x: FLOATING_LOGO_MARGIN, y: randomY };
14625
- case 1:
14626
- return { x: maxX, y: randomY };
14627
- case 2:
14628
- return { x: randomX, y: FLOATING_LOGO_MARGIN };
14629
- case 3:
14630
- return { x: randomX, y: maxY };
14631
- default:
14632
- return { x: maxX, y: FLOATING_LOGO_MARGIN };
14633
- }
14634
- }
14635
- function nearestFloatingLogoEdge(position) {
14636
- const clamped = clampFloatingLogoPosition(position);
14637
- const maxX = Math.max(
14638
- FLOATING_LOGO_MARGIN,
14639
- window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14640
- );
14641
- const maxY = Math.max(
14642
- FLOATING_LOGO_MARGIN,
14643
- window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14644
- );
14645
- const distances = [
14646
- { edge: "left", distance: clamped.x - FLOATING_LOGO_MARGIN },
14647
- { edge: "right", distance: maxX - clamped.x },
14648
- { edge: "top", distance: clamped.y - FLOATING_LOGO_MARGIN },
14649
- { edge: "bottom", distance: maxY - clamped.y }
14650
- ];
14651
- return distances.reduce((best, current) => current.distance < best.distance ? current : best).edge;
14652
- }
14653
- function nextFloatingLogoWalkingPosition(position) {
14654
- const clamped = snapFloatingLogoToEdge(position);
14655
- const maxX = Math.max(
14656
- FLOATING_LOGO_MARGIN,
14657
- window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14658
- );
14659
- const maxY = Math.max(
14660
- FLOATING_LOGO_MARGIN,
14661
- window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14662
- );
14663
- const step = FLOATING_LOGO_MIN_STEP + Math.random() * (FLOATING_LOGO_MAX_STEP - FLOATING_LOGO_MIN_STEP);
14664
- const direction = Math.random() > 0.5 ? 1 : -1;
14665
- const edge = nearestFloatingLogoEdge(clamped);
14666
- switch (edge) {
14667
- case "left": {
14668
- const nextY = clamped.y + step * direction;
14669
- if (nextY <= FLOATING_LOGO_MARGIN)
14670
- return { x: FLOATING_LOGO_MARGIN, y: FLOATING_LOGO_MARGIN };
14671
- if (nextY >= maxY) return { x: FLOATING_LOGO_MARGIN, y: maxY };
14672
- return { x: FLOATING_LOGO_MARGIN, y: nextY };
14673
- }
14674
- case "right": {
14675
- const nextY = clamped.y + step * direction;
14676
- if (nextY <= FLOATING_LOGO_MARGIN) return { x: maxX, y: FLOATING_LOGO_MARGIN };
14677
- if (nextY >= maxY) return { x: maxX, y: maxY };
14678
- return { x: maxX, y: nextY };
14679
- }
14680
- case "top": {
14681
- const nextX = clamped.x + step * direction;
14682
- if (nextX <= FLOATING_LOGO_MARGIN)
14683
- return { x: FLOATING_LOGO_MARGIN, y: FLOATING_LOGO_MARGIN };
14684
- if (nextX >= maxX) return { x: maxX, y: FLOATING_LOGO_MARGIN };
14685
- return { x: nextX, y: FLOATING_LOGO_MARGIN };
14686
- }
14687
- case "bottom": {
14688
- const nextX = clamped.x + step * direction;
14689
- if (nextX <= FLOATING_LOGO_MARGIN) return { x: FLOATING_LOGO_MARGIN, y: maxY };
14690
- if (nextX >= maxX) return { x: maxX, y: maxY };
14691
- return { x: nextX, y: maxY };
14692
- }
14693
- }
14694
- }
14695
- function FloatingInspectorLogo() {
14696
- const [position, setPosition] = reactExports.useState({
14697
- x: FLOATING_LOGO_MARGIN,
14698
- y: FLOATING_LOGO_MARGIN
14699
- });
14700
- const [motionTick, setMotionTick] = reactExports.useState(0);
14701
- const [dragging, setDragging] = reactExports.useState(false);
14702
- const dragStateRef = reactExports.useRef(null);
14703
- reactExports.useEffect(() => {
14704
- setPosition(randomFloatingLogoEdgePosition());
14705
- }, []);
14706
- reactExports.useEffect(() => {
14707
- if (dragging) return void 0;
14708
- let timeoutId = null;
14709
- const scheduleDrift = () => {
14710
- const delay = FLOATING_LOGO_MIN_DRIFT_MS + Math.random() * FLOATING_LOGO_DRIFT_WINDOW_MS;
14711
- timeoutId = window.setTimeout(() => {
14712
- setMotionTick((current) => current + 1);
14713
- setPosition((current) => nextFloatingLogoWalkingPosition(current));
14714
- scheduleDrift();
14715
- }, delay);
14716
- };
14717
- scheduleDrift();
14718
- return () => {
14719
- if (timeoutId !== null) {
14720
- window.clearTimeout(timeoutId);
14721
- }
14722
- };
14723
- }, [dragging]);
14724
- reactExports.useEffect(() => {
14725
- const handleResize = () => {
14726
- setPosition((current) => snapFloatingLogoToEdge(current));
14727
- };
14728
- window.addEventListener("resize", handleResize);
14729
- return () => window.removeEventListener("resize", handleResize);
14730
- }, []);
14731
- const handlePointerDown = reactExports.useCallback((event) => {
14732
- const rect = event.currentTarget.getBoundingClientRect();
14733
- dragStateRef.current = {
14734
- pointerId: event.pointerId,
14735
- offsetX: event.clientX - rect.left,
14736
- offsetY: event.clientY - rect.top
14737
- };
14738
- event.currentTarget.setPointerCapture(event.pointerId);
14739
- setDragging(true);
14740
- }, []);
14741
- const handlePointerMove = reactExports.useCallback((event) => {
14742
- const dragState = dragStateRef.current;
14743
- if (dragState === null || dragState.pointerId !== event.pointerId) return;
14744
- setPosition(
14745
- clampFloatingLogoPosition({
14746
- x: event.clientX - dragState.offsetX,
14747
- y: event.clientY - dragState.offsetY
14748
- })
14749
- );
14750
- }, []);
14751
- const handlePointerUp = reactExports.useCallback((event) => {
14752
- const dragState = dragStateRef.current;
14753
- if (dragState === null || dragState.pointerId !== event.pointerId) return;
14754
- if (event.currentTarget.hasPointerCapture(event.pointerId)) {
14755
- event.currentTarget.releasePointerCapture(event.pointerId);
14756
- }
14757
- dragStateRef.current = null;
14758
- setDragging(false);
14759
- setPosition((current) => snapFloatingLogoToEdge(current));
14760
- }, []);
14761
- const style = reactExports.useMemo(
14762
- () => ({
14763
- transform: `translate3d(${position.x}px, ${position.y}px, 0) rotate(${motionTick % 2 === 0 ? "-4deg" : "5deg"}) scale(${dragging ? "1.08" : "1"})`,
14764
- transition: dragging ? "none" : "transform 4800ms cubic-bezier(0.33, 1, 0.68, 1)",
14765
- touchAction: "none"
14766
- }),
14767
- [dragging, motionTick, position.x, position.y]
14768
- );
14769
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
14770
- "button",
14771
- {
14772
- type: "button",
14773
- className: cn(
14774
- "fixed left-0 top-0 z-40 flex size-14 items-center justify-center rounded-full 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:ring-1 focus-visible:ring-amber-300/55",
14775
- dragging ? "cursor-grabbing" : "cursor-grab"
14776
- ),
14777
- style,
14778
- onPointerDown: handlePointerDown,
14779
- onPointerMove: handlePointerMove,
14780
- onPointerUp: handlePointerUp,
14781
- onPointerCancel: handlePointerUp,
14782
- "aria-label": "Floating Inspector logo",
14783
- title: "Drag Inspector logo",
14784
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
14785
- "span",
14786
- {
14787
- className: cn(
14788
- "relative flex size-12 items-center justify-center",
14789
- !dragging && "floating-crab-body"
14790
- ),
14791
- "aria-hidden": "true",
14792
- children: [
14793
- /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "relative z-10 size-11 text-amber-500 drop-shadow-[0_0_15px_rgba(245,158,11,0.30)]" }),
14794
- /* @__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(
14795
- "span",
14796
- {
14797
- className: cn(
14798
- "floating-crab-leg h-2 w-0.5 rounded-full bg-current shadow-[0_0_7px_rgba(245,158,11,0.24)]",
14799
- leg < 3 ? "floating-crab-leg-left" : "floating-crab-leg-right",
14800
- dragging && "animate-none"
14801
- ),
14802
- style: {
14803
- animationDelay: `${leg % 2 === 0 ? 0 : 180}ms`
14804
- }
14805
- },
14806
- leg
14807
- )) })
14808
- ]
14809
- }
14810
- )
14811
- }
14812
- );
14813
- }
14814
15573
  function SessionContextLogButton({ label, logId }) {
14815
15574
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
14816
15575
  "button",
@@ -15379,7 +16138,7 @@ function ProxyViewer({
15379
16138
  ]
15380
16139
  }
15381
16140
  ),
15382
- detailsPanelCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx(FloatingInspectorLogo, {}),
16141
+ detailsPanelCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx(InspectorPet, { logs }),
15383
16142
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
15384
16143
  "div",
15385
16144
  {