@mcp-use/client 2.0.0-beta.1 → 2.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/auth/browser.d.ts +8 -4
  3. package/dist/auth/browser.d.ts.map +1 -1
  4. package/dist/auth/session-store.d.ts +5 -0
  5. package/dist/auth/session-store.d.ts.map +1 -1
  6. package/dist/index-browser.d.ts +1 -1
  7. package/dist/index-browser.d.ts.map +1 -1
  8. package/dist/index-browser.js +49 -151
  9. package/dist/index-browser.js.map +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +22 -161
  13. package/dist/index.js.map +1 -1
  14. package/dist/react/index.js +294 -214
  15. package/dist/react/index.js.map +1 -1
  16. package/dist/react/types.d.ts +9 -3
  17. package/dist/react/types.d.ts.map +1 -1
  18. package/dist/react/useMcp-operations.d.ts.map +1 -1
  19. package/dist/react/useMcp.d.ts.map +1 -1
  20. package/dist/react/view/ViewRenderer.d.ts +2 -1
  21. package/dist/react/view/ViewRenderer.d.ts.map +1 -1
  22. package/dist/react/view/inject-openai-file-apis.d.ts +6 -0
  23. package/dist/react/view/inject-openai-file-apis.d.ts.map +1 -0
  24. package/dist/react/view/types.d.ts +16 -0
  25. package/dist/react/view/types.d.ts.map +1 -1
  26. package/dist/react/view/use-display-mode.d.ts +1 -0
  27. package/dist/react/view/use-display-mode.d.ts.map +1 -1
  28. package/dist/telemetry/index.d.ts +1 -1
  29. package/dist/telemetry/index.d.ts.map +1 -1
  30. package/dist/telemetry/tel-fetch.d.ts +0 -6
  31. package/dist/telemetry/tel-fetch.d.ts.map +1 -1
  32. package/dist/telemetry/telemetry-node.d.ts.map +1 -1
  33. package/dist/telemetry/telemetry.d.ts +0 -8
  34. package/dist/telemetry/telemetry.d.ts.map +1 -1
  35. package/dist/transport/http.d.ts +6 -6
  36. package/package.json +2 -2
@@ -1473,7 +1473,7 @@ var HttpConnector = class extends BaseConnector {
1473
1473
  };
1474
1474
 
1475
1475
  // src/utils/version.ts
1476
- var VERSION = "2.0.0-beta.0";
1476
+ var VERSION = "2.0.0-beta.9";
1477
1477
  function getPackageVersion() {
1478
1478
  return VERSION;
1479
1479
  }
@@ -1849,6 +1849,14 @@ var OAuthSessionStore = class _OAuthSessionStore {
1849
1849
  async getTokenEndpoint() {
1850
1850
  return (await this.discoveryState())?.authorizationServerMetadata?.token_endpoint ?? null;
1851
1851
  }
1852
+ /**
1853
+ * Return the protected-resource URL selected during OAuth discovery.
1854
+ * Consumers can persist it and reuse it for server-side refresh exchanges.
1855
+ */
1856
+ async getResource() {
1857
+ const resource = (await this.discoveryState())?.resourceMetadata?.resource;
1858
+ return typeof resource === "string" ? resource : null;
1859
+ }
1852
1860
  };
1853
1861
 
1854
1862
  // src/auth/browser.ts
@@ -1966,20 +1974,19 @@ var BrowserOAuthClientProvider = class {
1966
1974
  * therefore never alters fetch behavior for other servers, other
1967
1975
  * connections, or the rest of the page.
1968
1976
  *
1969
- * When this provider is not configured to proxy OAuth requests (no
1970
- * `oauthProxyUrl`, or `proxyOAuthRequests` disabled), the provided
1971
- * `baseFetch` is returned as-is (or `undefined` when none is given, letting
1972
- * the SDK fall back to its default `fetch`).
1977
+ * OAuth metadata is always fetched with `cache: "no-store"`, including in
1978
+ * direct mode. Authorization servers commonly vary CORS headers by Origin;
1979
+ * bypassing the browser HTTP cache prevents a revalidated response cached
1980
+ * for another localhost origin from poisoning discovery. When OAuth proxying
1981
+ * is disabled or no `oauthProxyUrl` is configured, all requests still go
1982
+ * directly to their original URLs.
1973
1983
  *
1974
1984
  * @param baseFetch - The fetch used for non-OAuth requests and for the
1975
1985
  * underlying proxy calls. Defaults to the global `fetch`.
1976
1986
  */
1977
1987
  getProxyFetch(baseFetch) {
1978
- if (!this.proxyOAuthRequests || !this.oauthProxyUrl) {
1979
- return baseFetch;
1980
- }
1981
1988
  const base = baseFetch ?? globalThis.fetch.bind(globalThis);
1982
- const oauthProxyUrl = this.oauthProxyUrl;
1989
+ const oauthProxyUrl = this.proxyOAuthRequests && this.oauthProxyUrl ? this.oauthProxyUrl : void 0;
1983
1990
  const discoveredEndpoints = /* @__PURE__ */ new Set();
1984
1991
  let restoredDiscovery = false;
1985
1992
  return async (input, init) => {
@@ -1992,6 +1999,12 @@ var BrowserOAuthClientProvider = class {
1992
1999
  return await base(input, init);
1993
2000
  }
1994
2001
  const isMetadata = pathname.includes("/.well-known/");
2002
+ if (!oauthProxyUrl) {
2003
+ return await base(
2004
+ isMetadata ? url : input,
2005
+ isMetadata ? { ...init, cache: "no-store" } : init
2006
+ );
2007
+ }
1995
2008
  if (!restoredDiscovery) {
1996
2009
  restoredDiscovery = true;
1997
2010
  const metadata = (await this.discoveryState())?.authorizationServerMetadata;
@@ -2026,7 +2039,8 @@ var BrowserOAuthClientProvider = class {
2026
2039
  if (isMetadata) {
2027
2040
  const response2 = await base(proxyEndpoint, {
2028
2041
  ...init,
2029
- method: "GET"
2042
+ method: "GET",
2043
+ cache: "no-store"
2030
2044
  });
2031
2045
  try {
2032
2046
  const metadata = await response2.clone().json();
@@ -2083,7 +2097,16 @@ var BrowserOAuthClientProvider = class {
2083
2097
  }
2084
2098
  async saveClientInformation(clientInformation, ctx) {
2085
2099
  if (this.staticClientInfo) return;
2086
- return this.session.saveClientInformation(clientInformation, ctx);
2100
+ const { client_secret: discardedClientSecret, ...publicClientInformation } = clientInformation;
2101
+ if (discardedClientSecret) {
2102
+ console.info(
2103
+ `[${this.storageKeyPrefix}] Discarded client_secret returned for a public browser OAuth client.`
2104
+ );
2105
+ }
2106
+ return this.session.saveClientInformation(
2107
+ publicClientInformation,
2108
+ ctx
2109
+ );
2087
2110
  }
2088
2111
  codeVerifier() {
2089
2112
  return this.session.codeVerifier();
@@ -2113,6 +2136,10 @@ var BrowserOAuthClientProvider = class {
2113
2136
  getTokenEndpoint() {
2114
2137
  return this.session.getTokenEndpoint();
2115
2138
  }
2139
+ /** Return the protected-resource URL selected during OAuth discovery. */
2140
+ getResource() {
2141
+ return this.session.getResource();
2142
+ }
2116
2143
  /**
2117
2144
  * Return the stored public OAuth client ID. Browser providers do not retain
2118
2145
  * client secrets.
@@ -2370,62 +2397,6 @@ async function telFetch(url, init) {
2370
2397
  }
2371
2398
  var POSTHOG_HOST = "https://eu.i.posthog.com";
2372
2399
  var POSTHOG_API_KEY = "phc_lyTtbYwvkdSbrcMQNPiKiiRWrrM1seyKIMjycSvItEI";
2373
- var SCARF_GATEWAY_URL = "https://mcpuse.gateway.scarf.sh/events-ts";
2374
- var SCARF_GATEWAY_BEACON_URL = "https://mcpuse.gateway.scarf.sh/simple/";
2375
- var SCARF_BEACON_MAX_URL = 1800;
2376
- var SCARF_BEACON_TRUNCATED_KEYS = /* @__PURE__ */ new Set(["query", "response", "messages"]);
2377
- function stringifyScarfValue(value) {
2378
- if (value === null || value === void 0) return "";
2379
- if (typeof value === "string") return value;
2380
- if (typeof value === "number" || typeof value === "boolean") {
2381
- return String(value);
2382
- }
2383
- try {
2384
- return JSON.stringify(value);
2385
- } catch {
2386
- return String(value);
2387
- }
2388
- }
2389
- function buildScarfBeaconUrl(properties, baseUrl = SCARF_GATEWAY_BEACON_URL) {
2390
- const params = new URLSearchParams();
2391
- for (const [key, value] of Object.entries(properties)) {
2392
- let serialized = stringifyScarfValue(value);
2393
- if (SCARF_BEACON_TRUNCATED_KEYS.has(key) && serialized.length > 120) {
2394
- serialized = `${serialized.slice(0, 117)}...`;
2395
- }
2396
- if (serialized !== "") params.set(key, serialized);
2397
- }
2398
- let url = `${baseUrl}${baseUrl.includes("?") ? "&" : "?"}${params.toString()}`;
2399
- if (url.length <= SCARF_BEACON_MAX_URL) return url;
2400
- const keys = [...params.keys()].sort(
2401
- (a, b) => (params.get(b)?.length ?? 0) - (params.get(a)?.length ?? 0)
2402
- );
2403
- for (const key of keys) {
2404
- if (url.length <= SCARF_BEACON_MAX_URL) break;
2405
- if (key === "event" || key === "user_id") continue;
2406
- params.delete(key);
2407
- url = `${baseUrl}${baseUrl.includes("?") ? "&" : "?"}${params.toString()}`;
2408
- }
2409
- return url.slice(0, SCARF_BEACON_MAX_URL);
2410
- }
2411
- function captureScarfPost(properties, endpoint) {
2412
- return telFetch(endpoint, {
2413
- method: "POST",
2414
- headers: { "Content-Type": "application/json" },
2415
- keepalive: true,
2416
- body: JSON.stringify(properties)
2417
- });
2418
- }
2419
- function captureScarfBeacon(properties, endpoint) {
2420
- try {
2421
- const url = buildScarfBeaconUrl(properties, endpoint);
2422
- const img = new Image();
2423
- img.referrerPolicy = "no-referrer-when-downgrade";
2424
- img.src = url;
2425
- } catch {
2426
- }
2427
- return Promise.resolve();
2428
- }
2429
2400
  function capturePostHog(params) {
2430
2401
  const host = params.host ?? POSTHOG_HOST;
2431
2402
  const apiKey = params.apiKey ?? POSTHOG_API_KEY;
@@ -2442,13 +2413,6 @@ function capturePostHog(params) {
2442
2413
  })
2443
2414
  });
2444
2415
  }
2445
- function captureScarf(properties, endpoint = SCARF_GATEWAY_URL) {
2446
- if (typeof window !== "undefined") {
2447
- const beaconEndpoint = endpoint === SCARF_GATEWAY_URL ? SCARF_GATEWAY_BEACON_URL : endpoint;
2448
- return captureScarfBeacon(properties, beaconEndpoint);
2449
- }
2450
- return captureScarfPost(properties, endpoint);
2451
- }
2452
2416
 
2453
2417
  // src/telemetry/telemetry.ts
2454
2418
  function generateUUID() {
@@ -2459,18 +2423,6 @@ function secureRandomString() {
2459
2423
  globalThis.crypto.getRandomValues(array);
2460
2424
  return Array.from(array, (v) => v.toString(16).padStart(2, "0")).join("");
2461
2425
  }
2462
- function isVersionGreater(a, b) {
2463
- const parse = (v) => v.split("-")[0].split(".").map((n) => parseInt(n, 10) || 0);
2464
- const pa = parse(a);
2465
- const pb = parse(b);
2466
- const len = Math.max(pa.length, pb.length);
2467
- for (let i = 0; i < len; i++) {
2468
- const da = pa[i] ?? 0;
2469
- const db = pb[i] ?? 0;
2470
- if (da !== db) return da > db;
2471
- }
2472
- return false;
2473
- }
2474
2426
  var USER_ID_STORAGE_KEY = "mcp_use_user_id";
2475
2427
  var PROJECT_API_KEY = "phc_lyTtbYwvkdSbrcMQNPiKiiRWrrM1seyKIMjycSvItEI";
2476
2428
  var HOST = "https://eu.i.posthog.com";
@@ -2499,10 +2451,7 @@ function createLocalStorageBackend() {
2499
2451
  localStorage.setItem(USER_ID_STORAGE_KEY, id);
2500
2452
  } catch {
2501
2453
  }
2502
- },
2503
- // Package-download dedup is node/Scarf-oriented; localStorage unused.
2504
- getDownloadedVersion: () => null,
2505
- setDownloadedVersion: () => void 0
2454
+ }
2506
2455
  };
2507
2456
  }
2508
2457
  function detectRuntimeEnvironment() {
@@ -2570,17 +2519,13 @@ var Telemetry = class _Telemetry {
2570
2519
  _currUserId = null;
2571
2520
  _telemetryEnabled = false;
2572
2521
  _pending = /* @__PURE__ */ new Set();
2573
- _scarfEnabled = false;
2574
2522
  _runtimeEnvironment;
2575
2523
  _storageCapability;
2576
2524
  _storage;
2577
- /** True when node entry installed fs storage (package-download dedup). */
2578
- _fsBacked;
2579
2525
  _source;
2580
2526
  _productVersion;
2581
2527
  constructor() {
2582
2528
  this._runtimeEnvironment = detectRuntimeEnvironment();
2583
- this._fsBacked = configuredStorage !== null;
2584
2529
  this._storage = configuredStorage ?? createLocalStorageBackend() ?? null;
2585
2530
  this._storageCapability = this._storage ? "persistent" : "session-only";
2586
2531
  this._source = readSourceHint() || this._runtimeEnvironment;
@@ -2599,14 +2544,6 @@ var Telemetry = class _Telemetry {
2599
2544
  "Anonymized telemetry enabled. Set MCP_USE_ANONYMIZED_TELEMETRY=false to disable."
2600
2545
  );
2601
2546
  this._telemetryEnabled = true;
2602
- this._scarfEnabled = true;
2603
- if (this._fsBacked) {
2604
- setTimeout(() => {
2605
- this.trackPackageDownload({ triggered_by: "initialization" }).catch(
2606
- (e) => logger.debug(`Failed to track package download: ${e}`)
2607
- );
2608
- }, 0);
2609
- }
2610
2547
  }
2611
2548
  }
2612
2549
  get runtimeEnvironment() {
@@ -2638,7 +2575,7 @@ var Telemetry = class _Telemetry {
2638
2575
  this._productVersion = version;
2639
2576
  }
2640
2577
  get isEnabled() {
2641
- return this._telemetryEnabled || this._scarfEnabled;
2578
+ return this._telemetryEnabled;
2642
2579
  }
2643
2580
  get userId() {
2644
2581
  if (this._currUserId) return this._currUserId;
@@ -2661,7 +2598,7 @@ var Telemetry = class _Telemetry {
2661
2598
  return this._currUserId;
2662
2599
  }
2663
2600
  async capture(event) {
2664
- if (!this._telemetryEnabled && !this._scarfEnabled) return;
2601
+ if (!this._telemetryEnabled) return;
2665
2602
  const currentUserId = this.userId;
2666
2603
  const properties = {
2667
2604
  ...event.properties,
@@ -2670,52 +2607,15 @@ var Telemetry = class _Telemetry {
2670
2607
  source: this._source,
2671
2608
  runtime: this._runtimeEnvironment
2672
2609
  };
2673
- if (this._telemetryEnabled) {
2674
- const p = capturePostHog({
2675
- host: HOST,
2676
- apiKey: PROJECT_API_KEY,
2677
- event: event.name,
2678
- distinctId: currentUserId,
2679
- properties
2680
- });
2681
- this._pending.add(p);
2682
- void p.finally(() => this._pending.delete(p));
2683
- }
2684
- if (this._scarfEnabled) {
2685
- const p = captureScarf({
2686
- ...properties,
2687
- user_id: currentUserId,
2688
- event: event.name
2689
- });
2690
- this._pending.add(p);
2691
- void p.finally(() => this._pending.delete(p));
2692
- }
2693
- }
2694
- async trackPackageDownload(properties) {
2695
- if (!this._scarfEnabled || !this._fsBacked || !this._storage) return;
2696
- const currentVersion = getPackageVersion();
2697
- const saved = this._storage.getDownloadedVersion();
2698
- let firstDownload = false;
2699
- let shouldTrack = false;
2700
- if (!saved) {
2701
- shouldTrack = true;
2702
- firstDownload = true;
2703
- this._storage.setDownloadedVersion(currentVersion);
2704
- } else if (isVersionGreater(currentVersion, saved)) {
2705
- shouldTrack = true;
2706
- this._storage.setDownloadedVersion(currentVersion);
2707
- }
2708
- if (!shouldTrack) return;
2709
- await captureScarf({
2710
- ...properties || {},
2711
- mcp_use_version: currentVersion,
2712
- user_id: this.userId,
2713
- event: "package_download",
2714
- first_download: firstDownload,
2715
- language: "typescript",
2716
- source: this._source,
2717
- runtime: this._runtimeEnvironment
2610
+ const p = capturePostHog({
2611
+ host: HOST,
2612
+ apiKey: PROJECT_API_KEY,
2613
+ event: event.name,
2614
+ distinctId: currentUserId,
2615
+ properties
2718
2616
  });
2617
+ this._pending.add(p);
2618
+ void p.finally(() => this._pending.delete(p));
2719
2619
  }
2720
2620
  async trackAgentExecution(data) {
2721
2621
  if (!this.isEnabled) return;
@@ -4384,6 +4284,7 @@ function useMcp(options) {
4384
4284
  headers: headersOption,
4385
4285
  proxyConfig,
4386
4286
  oauthProxyUrl: oauthProxyUrlOption,
4287
+ connectionMode,
4387
4288
  autoProxyFallback = false,
4388
4289
  logLevel: logLevelOption = "silent",
4389
4290
  autoRetry = false,
@@ -4407,6 +4308,7 @@ function useMcp(options) {
4407
4308
  oauth: oauthOptions
4408
4309
  } = options;
4409
4310
  const transportType = "http";
4311
+ const requestedProxyAddress = proxyConfig?.proxyAddress;
4410
4312
  const oauthClientId = oauthOptions?.clientId?.trim() || void 0;
4411
4313
  const oauthClientMetadataUrl = oauthOptions?.clientMetadataUrl?.trim() || void 0;
4412
4314
  const oauthScope = oauthOptions?.scope?.trim() || void 0;
@@ -4454,6 +4356,9 @@ function useMcp(options) {
4454
4356
  );
4455
4357
  const oauthClientConfig = derivedOAuthClientConfig;
4456
4358
  const autoProxyFallbackConfig = useMemo(() => {
4359
+ if (connectionMode === "direct" || connectionMode === "proxy") {
4360
+ return { enabled: false, proxyAddress: void 0 };
4361
+ }
4457
4362
  if (!autoProxyFallback) {
4458
4363
  return { enabled: false, proxyAddress: void 0 };
4459
4364
  }
@@ -4469,7 +4374,7 @@ function useMcp(options) {
4469
4374
  enabled: autoProxyFallback.enabled !== false && Boolean(proxyAddress),
4470
4375
  proxyAddress
4471
4376
  };
4472
- }, [autoProxyFallback, proxyConfig]);
4377
+ }, [autoProxyFallback, connectionMode, proxyConfig]);
4473
4378
  const autoReconnectConfig = useMemo(() => {
4474
4379
  if (autoReconnect === false) {
4475
4380
  return {
@@ -4505,20 +4410,33 @@ function useMcp(options) {
4505
4410
  const [effectiveProxyConfig, setEffectiveProxyConfig] = useState(void 0);
4506
4411
  useEffect(() => {
4507
4412
  setEffectiveProxyConfig(void 0);
4508
- }, [url, proxyConfig]);
4413
+ }, [
4414
+ url,
4415
+ requestedProxyAddress,
4416
+ connectionMode,
4417
+ autoProxyFallbackConfig.proxyAddress
4418
+ ]);
4509
4419
  const activeProxyConfig = useMemo(() => {
4510
- if (!effectiveProxyConfig?.proxyAddress) {
4511
- return proxyConfig;
4420
+ const hasCurrentAutoFallback = autoProxyFallbackConfig.enabled && effectiveProxyConfig?.proxyAddress === autoProxyFallbackConfig.proxyAddress;
4421
+ if (hasCurrentAutoFallback && effectiveProxyConfig) {
4422
+ const latestHeaders = proxyConfig?.headers ?? {};
4423
+ return {
4424
+ ...effectiveProxyConfig,
4425
+ headers: {
4426
+ ...latestHeaders,
4427
+ ...effectiveProxyConfig.headers ?? {}
4428
+ }
4429
+ };
4512
4430
  }
4513
- const latestHeaders = proxyConfig?.headers ?? {};
4514
- return {
4515
- ...effectiveProxyConfig,
4516
- headers: {
4517
- ...latestHeaders,
4518
- ...effectiveProxyConfig.headers ?? {}
4519
- }
4520
- };
4521
- }, [effectiveProxyConfig, proxyConfig]);
4431
+ const startsDirect = connectionMode === "auto" || connectionMode === "direct" || connectionMode === void 0 && autoProxyFallbackConfig.enabled;
4432
+ return startsDirect ? void 0 : proxyConfig;
4433
+ }, [
4434
+ effectiveProxyConfig,
4435
+ proxyConfig,
4436
+ connectionMode,
4437
+ autoProxyFallbackConfig.enabled,
4438
+ autoProxyFallbackConfig.proxyAddress
4439
+ ]);
4522
4440
  const gatewayUrl = activeProxyConfig?.proxyAddress;
4523
4441
  const proxyHeaders = activeProxyConfig?.headers ?? {};
4524
4442
  const effectiveOAuthUrl = useMemo(() => {
@@ -4850,8 +4768,9 @@ function useMcp(options) {
4850
4768
  clientInfo: mergedClientInfo,
4851
4769
  // Pass a fetch that scopes OAuth-proxy routing to this server's
4852
4770
  // transport/auth calls. getProxyFetch wraps `customFetch` (e.g. the
4853
- // OAuth retry fetch for scope step-up) when proxying, or returns it
4854
- // unchanged otherwise. Never mutates the global fetch.
4771
+ // OAuth retry fetch for scope step-up), bypasses the browser cache
4772
+ // for OAuth metadata, and optionally routes OAuth through the BFF.
4773
+ // It never mutates the global fetch.
4855
4774
  ...(() => {
4856
4775
  const scopedFetch = authProviderRef.current?.getProxyFetch?.(customFetch) ?? customFetch;
4857
4776
  return scopedFetch ? { fetch: scopedFetch } : {};
@@ -5052,12 +4971,18 @@ function useMcp(options) {
5052
4971
  if (tokens?.access_token) {
5053
4972
  const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
5054
4973
  let tokenEndpoint = null;
4974
+ let resource = null;
5055
4975
  let clientCreds = null;
5056
4976
  try {
5057
4977
  tokenEndpoint = await authProviderRef.current.getTokenEndpoint?.() ?? null;
5058
4978
  } catch {
5059
4979
  tokenEndpoint = null;
5060
4980
  }
4981
+ try {
4982
+ resource = await authProviderRef.current.getResource?.() ?? null;
4983
+ } catch {
4984
+ resource = null;
4985
+ }
5061
4986
  try {
5062
4987
  clientCreds = await authProviderRef.current.getClientCredentials?.() ?? null;
5063
4988
  } catch {
@@ -5074,6 +4999,7 @@ function useMcp(options) {
5074
4999
  refresh_token: tokens.refresh_token,
5075
5000
  scope: tokens.scope,
5076
5001
  ...tokenEndpoint ? { token_endpoint: tokenEndpoint } : {},
5002
+ ...resource ? { resource } : {},
5077
5003
  ...clientCreds?.client_id ? { client_id: clientCreds.client_id } : {},
5078
5004
  ...clientCreds?.client_secret ? { client_secret: clientCreds.client_secret } : {}
5079
5005
  });
@@ -5318,19 +5244,20 @@ function useMcp(options) {
5318
5244
  addLog("info", "Triggering fresh OAuth authorization...");
5319
5245
  const parsedUrl = new URL(url);
5320
5246
  const baseUrl = parsedUrl.origin + parsedUrl.pathname.replace(/\/+$/, "");
5321
- try {
5322
- await auth2(freshAuthProvider, {
5323
- serverUrl: baseUrl,
5324
- fetchFn: freshAuthProvider.getProxyFetch?.()
5325
- });
5247
+ const authResult = await auth2(freshAuthProvider, {
5248
+ serverUrl: baseUrl,
5249
+ fetchFn: freshAuthProvider.getProxyFetch?.()
5250
+ });
5251
+ if (authResult === "AUTHORIZED") {
5326
5252
  addLog("info", "OAuth flow completed (tokens obtained)");
5327
- } catch (err) {
5328
- addLog(
5329
- "info",
5330
- "OAuth flow initiated (popup/redirect):",
5331
- err instanceof Error ? err.message : "Redirecting..."
5332
- );
5253
+ connectingRef.current = false;
5254
+ connectRef.current?.();
5255
+ return;
5333
5256
  }
5257
+ if (authResult !== "REDIRECT") {
5258
+ throw new Error(`Unexpected OAuth auth() result: ${authResult}`);
5259
+ }
5260
+ addLog("info", "OAuth authorization redirect initiated");
5334
5261
  const newAuthUrl = freshAuthProvider.getLastAttemptedAuthUrl?.();
5335
5262
  if (newAuthUrl) {
5336
5263
  setAuthUrl(newAuthUrl);
@@ -5396,11 +5323,8 @@ function useMcp(options) {
5396
5323
  }
5397
5324
  } catch (authError) {
5398
5325
  if (!isMountedRef.current) return;
5399
- setState("pending_auth");
5400
- addLog(
5401
- "error",
5402
- `Manual authentication failed: ${authError instanceof Error ? authError.message : String(authError)}`
5403
- );
5326
+ const error2 = authError instanceof Error ? authError : new Error(String(authError));
5327
+ failConnection(`Manual authentication failed: ${error2.message}`, error2);
5404
5328
  }
5405
5329
  } else if (currentState === "authenticating") {
5406
5330
  addLog(
@@ -5608,6 +5532,7 @@ function useMcp(options) {
5608
5532
  // Triggers reconnection when proxy fallback changes OAuth URL
5609
5533
  proxyConfig,
5610
5534
  // Triggers reconnection when proxy config (including headers) changes
5535
+ autoProxyFallbackConfig.proxyAddress,
5611
5536
  providedAuthProvider
5612
5537
  ]);
5613
5538
  const retryRef = useRef(retry);
@@ -6881,6 +6806,7 @@ import React2, {
6881
6806
  memo,
6882
6807
  useCallback as useCallback6,
6883
6808
  useEffect as useEffect5,
6809
+ useMemo as useMemo3,
6884
6810
  useRef as useRef4,
6885
6811
  useState as useState4
6886
6812
  } from "react";
@@ -6903,6 +6829,53 @@ function parseCustomProps(customProps) {
6903
6829
  return parsed;
6904
6830
  }
6905
6831
 
6832
+ // src/react/view/inject-openai-file-apis.ts
6833
+ var OPENAI_FILE_APIS_SCRIPT = `<script>
6834
+ (function () {
6835
+ var files = new Map();
6836
+ window.openai = window.openai || {};
6837
+ window.openai.uploadFile = async function (file) {
6838
+ var fileId = crypto.randomUUID();
6839
+ files.set(fileId, file);
6840
+ return { fileId: fileId };
6841
+ };
6842
+ window.openai.getFileDownloadUrl = async function (ref) {
6843
+ var file = files.get(ref.fileId);
6844
+ if (!file) {
6845
+ throw new Error("File not found: " + ref.fileId);
6846
+ }
6847
+ return { downloadUrl: URL.createObjectURL(file) };
6848
+ };
6849
+ })();
6850
+ </script>`;
6851
+ function injectOpenAiFileApis(html) {
6852
+ if (html.includes("<head>")) {
6853
+ return html.replace("<head>", "<head>" + OPENAI_FILE_APIS_SCRIPT);
6854
+ }
6855
+ if (html.includes("<HEAD>")) {
6856
+ return html.replace("<HEAD>", "<HEAD>" + OPENAI_FILE_APIS_SCRIPT);
6857
+ }
6858
+ if (html.includes("<html>")) {
6859
+ return html.replace(
6860
+ "<html>",
6861
+ "<html><head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6862
+ );
6863
+ }
6864
+ if (html.includes("<HTML>")) {
6865
+ return html.replace(
6866
+ "<HTML>",
6867
+ "<HTML><head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6868
+ );
6869
+ }
6870
+ if (html.includes("<!DOCTYPE") || html.includes("<!doctype")) {
6871
+ return html.replace(
6872
+ /(<!DOCTYPE[^>]*>|<!doctype[^>]*>)/i,
6873
+ "$1<head>" + OPENAI_FILE_APIS_SCRIPT + "</head>"
6874
+ );
6875
+ }
6876
+ return OPENAI_FILE_APIS_SCRIPT + html;
6877
+ }
6878
+
6906
6879
  // src/react/view/resolve-view-resource.ts
6907
6880
  function resolveViewResource(options) {
6908
6881
  const { resourceResult, listingResource, cspMode, resourceUri } = options;
@@ -7252,9 +7225,9 @@ function buildSandboxProxyBlobHtml(search) {
7252
7225
  // src/react/view/use-display-mode.ts
7253
7226
  import { useCallback as useCallback5, useEffect as useEffect4 } from "react";
7254
7227
  var SHELL_BASE = "w-full h-full min-h-0 bg-background flex flex-col [&:fullscreen]:h-full [&:fullscreen]:w-full [&:fullscreen]:bg-background";
7255
- var WIDGET_FULLSCREEN_OVERLAY_CLASSES = `fixed inset-0 z-[100] ${SHELL_BASE}`;
7228
+ var WIDGET_FULLSCREEN_OVERLAY_CLASSES = `fixed inset-0 z-[200] ${SHELL_BASE}`;
7256
7229
  var WIDGET_PIP_SHELL_CLASSES = [
7257
- "fixed top-4 left-1/2 -translate-x-1/2 z-[100]",
7230
+ "fixed top-4 left-1/2 -translate-x-1/2 z-[200]",
7258
7231
  "rounded-3xl w-full min-w-[300px] h-[400px]",
7259
7232
  "shadow-2xl border overflow-hidden",
7260
7233
  "bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80",
@@ -7312,7 +7285,8 @@ function useViewDisplayModeControls({
7312
7285
  }
7313
7286
  var VIEW_DIMENSIONS = {
7314
7287
  PIP_MAX_WIDTH: 700,
7315
- DEFAULT_HEIGHT: 400
7288
+ DEFAULT_HEIGHT: 400,
7289
+ FULLSCREEN_HEADER_HEIGHT: 50
7316
7290
  };
7317
7291
 
7318
7292
  // src/react/view/view-detection.ts
@@ -7339,8 +7313,28 @@ var DEFAULT_HOST_CAPABILITIES = {
7339
7313
  serverTools: {},
7340
7314
  serverResources: {},
7341
7315
  logging: {},
7342
- updateModelContext: { text: {} }
7316
+ updateModelContext: { text: {} },
7317
+ // ponytail: always advertised; bridge.onmessage no-ops when onMessage unset
7318
+ message: { text: {} }
7343
7319
  };
7320
+ function CloseIcon() {
7321
+ return /* @__PURE__ */ React2.createElement(
7322
+ "svg",
7323
+ {
7324
+ width: "14",
7325
+ height: "14",
7326
+ viewBox: "0 0 24 24",
7327
+ fill: "none",
7328
+ stroke: "currentColor",
7329
+ strokeWidth: "2",
7330
+ strokeLinecap: "round",
7331
+ strokeLinejoin: "round",
7332
+ "aria-hidden": true
7333
+ },
7334
+ /* @__PURE__ */ React2.createElement("path", { d: "M18 6 6 18" }),
7335
+ /* @__PURE__ */ React2.createElement("path", { d: "m6 6 12 12" })
7336
+ );
7337
+ }
7344
7338
  function waitForSandboxProxyReady(iframe) {
7345
7339
  return new Promise((resolve) => {
7346
7340
  const listener = (event) => {
@@ -7401,6 +7395,10 @@ function ViewRendererBase({
7401
7395
  onResourceResolved,
7402
7396
  wrapTransport,
7403
7397
  toolCallTimeout = DEFAULT_TOOL_CALL_TIMEOUT,
7398
+ mockOpenAiFileApis = false,
7399
+ onInlineHeightChange,
7400
+ fullscreenHeader,
7401
+ renderFullscreenClose,
7404
7402
  className,
7405
7403
  testId = "mcp-app-frame",
7406
7404
  invoking,
@@ -7409,6 +7407,9 @@ function ViewRendererBase({
7409
7407
  const iframeRef = useRef4(null);
7410
7408
  const bridgeRef = useRef4(null);
7411
7409
  const containerRef = useRef4(null);
7410
+ const pendingBlobRevocationsRef = useRef4(
7411
+ /* @__PURE__ */ new Map()
7412
+ );
7412
7413
  const connectionRef = useRef4(
7413
7414
  source.kind === "live" ? source.connection : null
7414
7415
  );
@@ -7422,12 +7423,19 @@ function ViewRendererBase({
7422
7423
  );
7423
7424
  const [internalDisplayMode, setInternalDisplayMode] = useState4("inline");
7424
7425
  const displayMode = displayModeProp ?? internalDisplayMode;
7425
- const hostContextRef = useRef4(hostContext);
7426
- hostContextRef.current = hostContext;
7426
+ const effectiveHostContext = useMemo3(() => {
7427
+ if (!hostContext) return hostContext;
7428
+ if (hostContext.displayMode === displayMode) return hostContext;
7429
+ return { ...hostContext, displayMode };
7430
+ }, [hostContext, displayMode]);
7431
+ const hostContextRef = useRef4(effectiveHostContext);
7432
+ hostContextRef.current = effectiveHostContext;
7427
7433
  const onMessageRef = useRef4(onMessage);
7428
7434
  onMessageRef.current = onMessage;
7429
7435
  const toolInputRef = useRef4(toolInput);
7430
7436
  toolInputRef.current = toolInput;
7437
+ const partialToolInputRef = useRef4(partialToolInput);
7438
+ partialToolInputRef.current = partialToolInput;
7431
7439
  const toolOutputRef = useRef4(toolOutput);
7432
7440
  toolOutputRef.current = toolOutput;
7433
7441
  const customPropsRef = useRef4(customProps);
@@ -7446,10 +7454,14 @@ function ViewRendererBase({
7446
7454
  onReadyRef.current = onReady;
7447
7455
  const onLifecycleChangeRef = useRef4(onLifecycleChange);
7448
7456
  onLifecycleChangeRef.current = onLifecycleChange;
7457
+ const onInlineHeightChangeRef = useRef4(onInlineHeightChange);
7458
+ onInlineHeightChangeRef.current = onInlineHeightChange;
7449
7459
  const sandboxUrlRef = useRef4(sandboxUrl);
7450
7460
  sandboxUrlRef.current = sandboxUrl;
7451
7461
  const cspModeRef = useRef4(cspMode);
7452
7462
  cspModeRef.current = cspMode;
7463
+ const mockOpenAiFileApisRef = useRef4(mockOpenAiFileApis);
7464
+ mockOpenAiFileApisRef.current = mockOpenAiFileApis;
7453
7465
  const resolveSandboxUrl = useCallback6((next) => {
7454
7466
  const custom = sandboxUrlRef.current;
7455
7467
  if (custom) {
@@ -7553,10 +7565,18 @@ function ViewRendererBase({
7553
7565
  }, [source.kind, liveResourceUri, preloadedHtml, cspMode, resolveSandboxUrl]);
7554
7566
  useEffect5(() => {
7555
7567
  const url = activeSandboxUrl;
7568
+ if (!url || url.protocol !== "blob:") return;
7569
+ const pending = pendingBlobRevocationsRef.current.get(url.href);
7570
+ if (pending) {
7571
+ clearTimeout(pending);
7572
+ pendingBlobRevocationsRef.current.delete(url.href);
7573
+ }
7556
7574
  return () => {
7557
- if (url?.protocol === "blob:") {
7575
+ const timer = setTimeout(() => {
7558
7576
  URL.revokeObjectURL(url.href);
7559
- }
7577
+ pendingBlobRevocationsRef.current.delete(url.href);
7578
+ }, 1e3);
7579
+ pendingBlobRevocationsRef.current.set(url.href, timer);
7560
7580
  };
7561
7581
  }, [activeSandboxUrl]);
7562
7582
  const isBlobSandbox = activeSandboxUrl?.protocol === "blob:";
@@ -7617,7 +7637,14 @@ function ViewRendererBase({
7617
7637
  iframe.setAttribute("allow", allowAttribute);
7618
7638
  }
7619
7639
  const readyPromise = waitForSandboxProxyReady(iframe);
7620
- iframe.src = activeSandboxUrl.href;
7640
+ if (activeSandboxUrl.protocol === "blob:") {
7641
+ const response = await fetch(activeSandboxUrl.href);
7642
+ const sandboxHtml = await response.text();
7643
+ if (disposed) return;
7644
+ iframe.srcdoc = sandboxHtml;
7645
+ } else {
7646
+ iframe.src = activeSandboxUrl.href;
7647
+ }
7621
7648
  await readyPromise;
7622
7649
  if (disposed) return;
7623
7650
  const capabilities = {
@@ -7703,7 +7730,10 @@ function ViewRendererBase({
7703
7730
  height
7704
7731
  }) => {
7705
7732
  if (displayModeRef.current !== "inline") return;
7706
- if (height !== void 0) setInlineHeight(height);
7733
+ if (height !== void 0) {
7734
+ setInlineHeight(height);
7735
+ onInlineHeightChangeRef.current?.(height);
7736
+ }
7707
7737
  };
7708
7738
  const initPromise = hookInitialized(bridge);
7709
7739
  let transport = new PostMessageTransport(
@@ -7716,7 +7746,7 @@ function ViewRendererBase({
7716
7746
  await bridge.connect(transport);
7717
7747
  if (disposed) return;
7718
7748
  await bridge.sendSandboxResourceReady({
7719
- html: resolved.html,
7749
+ html: mockOpenAiFileApisRef.current ? injectOpenAiFileApis(resolved.html) : resolved.html,
7720
7750
  csp: resolved.csp,
7721
7751
  permissions: resolved.permissions
7722
7752
  });
@@ -7725,11 +7755,18 @@ function ViewRendererBase({
7725
7755
  bridgeRef.current = bridge;
7726
7756
  setInitCount((c) => c + 1);
7727
7757
  onLifecycleChangeRef.current?.({ status: "initialized" });
7728
- const mergedArgs = {
7729
- ...toolInputRef.current,
7730
- ...parseCustomProps(customPropsRef.current)
7731
- };
7732
- bridge.sendToolInput({ arguments: mergedArgs });
7758
+ const currentPartialToolInput = partialToolInputRef.current;
7759
+ if (currentPartialToolInput) {
7760
+ bridge.sendToolInputPartial({
7761
+ arguments: currentPartialToolInput
7762
+ });
7763
+ } else {
7764
+ const mergedArgs = {
7765
+ ...toolInputRef.current,
7766
+ ...parseCustomProps(customPropsRef.current)
7767
+ };
7768
+ bridge.sendToolInput({ arguments: mergedArgs });
7769
+ }
7733
7770
  const toolResultPayload = buildToolResultPayload(
7734
7771
  toolOutputRef.current,
7735
7772
  customPropsRef.current
@@ -7778,13 +7815,14 @@ function ViewRendererBase({
7778
7815
  cspMode,
7779
7816
  viewId,
7780
7817
  wrapTransport,
7781
- toolCallTimeout
7818
+ toolCallTimeout,
7819
+ mockOpenAiFileApis
7782
7820
  ]);
7783
7821
  useEffect5(() => {
7784
7822
  const bridge = bridgeRef.current;
7785
- if (!bridge || initCount === 0 || !hostContext) return;
7786
- bridge.setHostContext(hostContext);
7787
- }, [hostContext, initCount]);
7823
+ if (!bridge || initCount === 0 || !effectiveHostContext) return;
7824
+ bridge.setHostContext(effectiveHostContext);
7825
+ }, [effectiveHostContext, initCount]);
7788
7826
  useEffect5(() => {
7789
7827
  const bridge = bridgeRef.current;
7790
7828
  if (!bridge || initCount === 0 || !partialToolInput) return;
@@ -7792,13 +7830,13 @@ function ViewRendererBase({
7792
7830
  }, [initCount, partialToolInput]);
7793
7831
  useEffect5(() => {
7794
7832
  const bridge = bridgeRef.current;
7795
- if (!bridge || initCount === 0) return;
7833
+ if (!bridge || initCount === 0 || partialToolInput) return;
7796
7834
  const mergedArgs = {
7797
7835
  ...toolInput,
7798
7836
  ...parseCustomProps(customProps)
7799
7837
  };
7800
7838
  bridge.sendToolInput({ arguments: mergedArgs });
7801
- }, [initCount, toolInput, customProps]);
7839
+ }, [initCount, toolInput, partialToolInput, customProps]);
7802
7840
  useEffect5(() => {
7803
7841
  const bridge = bridgeRef.current;
7804
7842
  if (!bridge || initCount === 0) return;
@@ -7822,6 +7860,7 @@ function ViewRendererBase({
7822
7860
  const timer = setTimeout(() => setShowSpinner(false), 300);
7823
7861
  return () => clearTimeout(timer);
7824
7862
  }, [initCount, showSpinner]);
7863
+ const showHostBorder = resolved !== null && resolved.prefersBorder && displayMode !== "fullscreen";
7825
7864
  if (loadError) {
7826
7865
  return /* @__PURE__ */ React2.createElement("div", { className }, /* @__PURE__ */ React2.createElement("div", { className: "border border-red-200/50 dark:border-red-800/50 bg-red-50/30 dark:bg-red-950/20 rounded-lg p-4" }, /* @__PURE__ */ React2.createElement("p", { className: "text-sm text-red-600 dark:text-red-400" }, "Failed to load view: ", loadError)));
7827
7866
  }
@@ -7839,29 +7878,69 @@ function ViewRendererBase({
7839
7878
  "div",
7840
7879
  {
7841
7880
  ref: containerRef,
7842
- className: containerClassName,
7881
+ className: isFullscreen ? `${containerClassName} flex flex-col` : containerClassName,
7843
7882
  style: isPip ? {
7844
7883
  height: VIEW_DIMENSIONS.DEFAULT_HEIGHT,
7845
7884
  maxWidth: VIEW_DIMENSIONS.PIP_MAX_WIDTH,
7846
7885
  zIndex: 100
7847
7886
  } : isFullscreen ? { zIndex: 100 } : void 0
7848
7887
  },
7849
- (isFullscreen || isPip) && /* @__PURE__ */ React2.createElement(
7888
+ isFullscreen && // ponytail: inspector Tailwind may not emit client arbitrary classes
7889
+ // (h-[50px], grid-cols-[auto_1fr_auto]) — use inline layout instead.
7890
+ /* @__PURE__ */ React2.createElement(
7891
+ "header",
7892
+ {
7893
+ className: "grid shrink-0 items-center border-b border-zinc-200 bg-background px-3 dark:border-zinc-700",
7894
+ style: {
7895
+ height: VIEW_DIMENSIONS.FULLSCREEN_HEADER_HEIGHT,
7896
+ gridTemplateColumns: "auto 1fr auto"
7897
+ }
7898
+ },
7899
+ renderFullscreenClose ? renderFullscreenClose({
7900
+ onClick: () => void handleDisplayModeChange("inline"),
7901
+ "data-testid": "debugger-exit-fullscreen-button",
7902
+ "aria-label": "Exit fullscreen"
7903
+ }) : /* @__PURE__ */ React2.createElement(
7904
+ "button",
7905
+ {
7906
+ type: "button",
7907
+ "data-testid": "debugger-exit-fullscreen-button",
7908
+ "aria-label": "Exit fullscreen",
7909
+ className: "flex size-8 cursor-pointer items-center justify-center rounded-full border border-zinc-200 bg-background text-foreground shadow-sm hover:bg-muted dark:border-zinc-700",
7910
+ onClick: () => void handleDisplayModeChange("inline")
7911
+ },
7912
+ /* @__PURE__ */ React2.createElement(CloseIcon, null)
7913
+ ),
7914
+ /* @__PURE__ */ React2.createElement("div", { className: "flex min-w-0 items-center justify-center gap-2 px-2" }, fullscreenHeader?.iconUrl ? /* @__PURE__ */ React2.createElement(
7915
+ "img",
7916
+ {
7917
+ src: fullscreenHeader.iconUrl,
7918
+ alt: "",
7919
+ className: "size-6 shrink-0 rounded-md object-contain"
7920
+ }
7921
+ ) : null, /* @__PURE__ */ React2.createElement("span", { className: "truncate text-sm font-medium text-foreground" }, fullscreenHeader?.title ?? toolName)),
7922
+ /* @__PURE__ */ React2.createElement("div", { className: "size-8 shrink-0", "aria-hidden": true })
7923
+ ),
7924
+ isPip && (renderFullscreenClose ? /* @__PURE__ */ React2.createElement("div", { className: "absolute right-3 top-3", style: { zIndex: 110 } }, renderFullscreenClose({
7925
+ onClick: () => void handleDisplayModeChange("inline"),
7926
+ "data-testid": "debugger-exit-pip-button",
7927
+ "aria-label": "Exit picture-in-picture"
7928
+ })) : /* @__PURE__ */ React2.createElement(
7850
7929
  "button",
7851
7930
  {
7852
7931
  type: "button",
7853
- "data-testid": isFullscreen ? "debugger-exit-fullscreen-button" : "debugger-exit-pip-button",
7854
- "aria-label": isFullscreen ? "Exit fullscreen" : "Exit picture-in-picture",
7855
- className: "absolute right-3 top-3 z-[110] flex size-8 items-center justify-center rounded-full border border-border bg-background/90 text-lg leading-none text-foreground shadow-sm backdrop-blur-sm hover:bg-background",
7932
+ "data-testid": "debugger-exit-pip-button",
7933
+ "aria-label": "Exit picture-in-picture",
7934
+ className: "absolute right-3 top-3 z-[110] flex size-8 cursor-pointer items-center justify-center rounded-full border border-border bg-background/90 text-foreground shadow-sm backdrop-blur-sm hover:bg-background",
7856
7935
  style: { zIndex: 110 },
7857
7936
  onClick: () => void handleDisplayModeChange("inline")
7858
7937
  },
7859
- "\xD7"
7860
- ),
7938
+ /* @__PURE__ */ React2.createElement(CloseIcon, null)
7939
+ )),
7861
7940
  /* @__PURE__ */ React2.createElement(
7862
7941
  "div",
7863
7942
  {
7864
- className: isFullscreen || isPip ? "relative w-full h-full min-h-0 flex flex-1 flex-col" : "relative w-full flex flex-1 justify-center items-center"
7943
+ className: isFullscreen ? "relative flex min-h-0 w-full flex-1 flex-col" : isPip ? "relative w-full h-full min-h-0 flex flex-1 flex-col" : "relative w-full flex flex-1 justify-center items-center"
7865
7944
  },
7866
7945
  showSpinner && /* @__PURE__ */ React2.createElement("div", { className: "flex absolute inset-0 items-center justify-center z-10" }, /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-muted-foreground" }, "Loading\u2026")),
7867
7946
  !isPip && !isFullscreen && (invoking || invoked) && /* @__PURE__ */ React2.createElement("div", { className: "absolute -top-8 left-2 z-10 whitespace-nowrap pointer-events-none text-xs text-muted-foreground" }, invoking && !toolOutput ? invoking : invoked),
@@ -7878,10 +7957,7 @@ function ViewRendererBase({
7878
7957
  {
7879
7958
  ref: iframeRef,
7880
7959
  title: `MCP App: ${toolName}`,
7881
- className: "w-full h-full border-0 bg-transparent",
7882
- style: {
7883
- border: resolved.prefersBorder && displayMode !== "fullscreen" ? void 0 : "none"
7884
- }
7960
+ className: showHostBorder ? "w-full h-full bg-transparent border border-border rounded-xl" : "w-full h-full bg-transparent border-0"
7885
7961
  }
7886
7962
  )
7887
7963
  )
@@ -7902,6 +7978,10 @@ function viewRendererAreEqual(prev, next) {
7902
7978
  if (prev.hostContext !== next.hostContext) return false;
7903
7979
  if (prev.hostCapabilities !== next.hostCapabilities) return false;
7904
7980
  if (prev.cspMode !== next.cspMode) return false;
7981
+ if (prev.mockOpenAiFileApis !== next.mockOpenAiFileApis) return false;
7982
+ if (prev.onInlineHeightChange !== next.onInlineHeightChange) return false;
7983
+ if (prev.fullscreenHeader !== next.fullscreenHeader) return false;
7984
+ if (prev.renderFullscreenClose !== next.renderFullscreenClose) return false;
7905
7985
  if (prev.className !== next.className) return false;
7906
7986
  if (prev.onReady !== next.onReady) return false;
7907
7987
  if (prev.onLifecycleChange !== next.onLifecycleChange) return false;