@copilotkit/react-core 1.70.0 → 1.70.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{copilotkit-CbZGwElm.d.mts → copilotkit--Jyzm6hS.d.mts} +50 -20
  2. package/dist/copilotkit--Jyzm6hS.d.mts.map +1 -0
  3. package/dist/{copilotkit-B7aFKfQR.mjs → copilotkit-B1jSvZeb.mjs} +395 -291
  4. package/dist/copilotkit-B1jSvZeb.mjs.map +1 -0
  5. package/dist/{copilotkit-BuzP0VeG.d.cts → copilotkit-BLYaCGcz.d.cts} +50 -20
  6. package/dist/copilotkit-BLYaCGcz.d.cts.map +1 -0
  7. package/dist/{copilotkit-BE76j111.cjs → copilotkit-DcFo270Y.cjs} +395 -291
  8. package/dist/copilotkit-DcFo270Y.cjs.map +1 -0
  9. package/dist/index.cjs +5 -4
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +2 -2
  12. package/dist/index.d.cts.map +1 -1
  13. package/dist/index.d.mts +2 -2
  14. package/dist/index.d.mts.map +1 -1
  15. package/dist/index.mjs +5 -4
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/index.umd.js +198 -207
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/v2/headless.cjs +58 -1
  20. package/dist/v2/headless.cjs.map +1 -1
  21. package/dist/v2/headless.mjs +59 -2
  22. package/dist/v2/headless.mjs.map +1 -1
  23. package/dist/v2/index.cjs +1 -1
  24. package/dist/v2/index.css +1 -1
  25. package/dist/v2/index.d.cts +1 -1
  26. package/dist/v2/index.d.mts +1 -1
  27. package/dist/v2/index.mjs +1 -1
  28. package/dist/v2/index.umd.js +395 -291
  29. package/dist/v2/index.umd.js.map +1 -1
  30. package/package.json +11 -8
  31. package/skills/react-core/SKILL.md +1 -1
  32. package/skills/react-core/references/attachments.md +20 -0
  33. package/skills/react-core/references/threads.md +1 -1
  34. package/dist/copilotkit-B7aFKfQR.mjs.map +0 -1
  35. package/dist/copilotkit-BE76j111.cjs.map +0 -1
  36. package/dist/copilotkit-BuzP0VeG.d.cts.map +0 -1
  37. package/dist/copilotkit-CbZGwElm.d.mts.map +0 -1
@@ -283,6 +283,62 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
283
283
  const useCopilotChatConfiguration = () => {
284
284
  return (0, react.useContext)(CopilotChatConfiguration);
285
285
  };
286
+ /**
287
+ * Reports modal open/close requests to the host, and — when `open` is
288
+ * supplied — makes the modal state of an already-established chat
289
+ * configuration **controlled** for the subtree it wraps.
290
+ *
291
+ * This is deliberately a scope component rather than another mode inside
292
+ * {@link CopilotChatConfigurationProvider}. The provider resolves modal state
293
+ * across a nested chain (own state, parent sync, drawer mutual-exclusion, the
294
+ * modal-closer registry); a controlled branch inside that resolution would add
295
+ * a fourth interacting mode. Overriding the context for the subtree instead
296
+ * leaves every one of those paths untouched:
297
+ *
298
+ * - `isModalOpen` is replaced with the host's `open`, so the rendered surface
299
+ * follows the prop from the very first frame (no open-then-close flash).
300
+ * - `setModalOpen` still calls the underlying setter, so the existing
301
+ * parent-sync and drawer mutual-exclusion side effects continue to run, and
302
+ * *then* reports the request through `onOpenChange`.
303
+ * - The wrapped setter is registered as the modal closer, so the drawer's
304
+ * mobile mutual-exclusion reaches the host instead of silently flipping
305
+ * state that nothing displays.
306
+ *
307
+ * A host that supplies `open` and ignores `onOpenChange` gets a modal pinned
308
+ * to `open`, which is the standard controlled-component contract. A host that
309
+ * supplies only `onOpenChange` is notified while the modal keeps managing
310
+ * itself.
311
+ *
312
+ * Renders `children` unchanged when no chat configuration is in scope.
313
+ */
314
+ const ControlledModalOpenScope = ({ children, open, onOpenChange }) => {
315
+ const parentConfig = (0, react.useContext)(CopilotChatConfiguration);
316
+ const parentSetModalOpen = parentConfig?.setModalOpen;
317
+ const registerModalCloser = parentConfig?.ɵregisterModalCloser;
318
+ const setModalOpen = (0, react.useCallback)((next) => {
319
+ parentSetModalOpen?.(next);
320
+ onOpenChange?.(next);
321
+ }, [parentSetModalOpen, onOpenChange]);
322
+ (0, react.useEffect)(() => {
323
+ if (!registerModalCloser) return;
324
+ return registerModalCloser(setModalOpen);
325
+ }, [registerModalCloser, setModalOpen]);
326
+ const configurationValue = (0, react.useMemo)(() => parentConfig ? {
327
+ ...parentConfig,
328
+ isModalOpen: open ?? parentConfig.isModalOpen,
329
+ setModalOpen
330
+ } : null, [
331
+ parentConfig,
332
+ open,
333
+ setModalOpen
334
+ ]);
335
+ if (!configurationValue) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children });
336
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfiguration.Provider, {
337
+ value: configurationValue,
338
+ children
339
+ });
340
+ };
341
+ ControlledModalOpenScope.displayName = "ControlledModalOpenScope";
286
342
 
287
343
  //#endregion
288
344
  //#region src/v2/lib/utils.ts
@@ -1876,7 +1932,6 @@ _radix_ui_react_dropdown_menu = __toESM(_radix_ui_react_dropdown_menu);
1876
1932
  newMessages: []
1877
1933
  };
1878
1934
  }
1879
- const PROTOCOL_VERSION = "2025-06-18";
1880
1935
  function buildSandboxHTML(extraCspDomains) {
1881
1936
  const baseScriptSrc = "'self' 'wasm-unsafe-eval' 'unsafe-inline' 'unsafe-eval' blob: data: http://localhost:* https://localhost:*";
1882
1937
  const baseFrameSrc = "* blob: data: http://localhost:* https://localhost:*";
@@ -1988,6 +2043,13 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
1988
2043
  }
1989
2044
  };
1990
2045
  const mcpAppsRequestQueue = new MCPAppsRequestQueue();
2046
+ const MCP_OPEN_LINK_BLOCKED_SCHEMES = new Set([
2047
+ "javascript:",
2048
+ "data:",
2049
+ "vbscript:",
2050
+ "blob:",
2051
+ "file:"
2052
+ ]);
1991
2053
  /**
1992
2054
  * Activity type for MCP Apps events - must match the middleware's MCPAppsActivityType
1993
2055
  */
@@ -2003,18 +2065,32 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2003
2065
  serverId: zod.z.string().optional(),
2004
2066
  toolInput: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
2005
2067
  });
2006
- function isRequest(msg) {
2007
- return "id" in msg && "method" in msg;
2008
- }
2009
- function isNotification(msg) {
2010
- return !("id" in msg) && "method" in msg;
2011
- }
2012
2068
  /**
2013
2069
  * MCP Apps Extension Activity Renderer
2014
2070
  *
2015
2071
  * Renders MCP Apps UI in a sandboxed iframe with full protocol support.
2016
2072
  * Fetches resource content on-demand via proxied MCP requests.
2017
2073
  */
2074
+ /**
2075
+ * Permissive `ui/message` schema. ext-apps restricts the request to
2076
+ * `role: "user"` with no `followUp`, but CopilotKit intentionally extends
2077
+ * `ui/message` with `role` ("user" | "assistant") and `followUp` (documented
2078
+ * behavior with dedicated tests). We register our own handler (instead of the
2079
+ * bridge's strict `onmessage`) so those extensions survive the migration.
2080
+ *
2081
+ * Going forward, widgets SHOULD pass the extensions under
2082
+ * `params._meta.copilotkit`; the top-level `role`/`followUp` fields are the
2083
+ * legacy channel, kept for backward compatibility and slated for deprecation.
2084
+ */
2085
+ const CopilotKitUiMessageSchema = zod.z.object({
2086
+ method: zod.z.literal("ui/message"),
2087
+ params: zod.z.object({
2088
+ role: zod.z.string().optional(),
2089
+ content: zod.z.array(zod.z.any()).optional(),
2090
+ followUp: zod.z.boolean().optional(),
2091
+ _meta: zod.z.record(zod.z.string(), zod.z.any()).optional()
2092
+ }).passthrough()
2093
+ });
2018
2094
  const MCPAppsActivityRenderer = function MCPAppsActivityRenderer({ content, agent }) {
2019
2095
  const { copilotkit } = useCopilotKit();
2020
2096
  const containerRef = (0, react.useRef)(null);
@@ -2028,41 +2104,12 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2028
2104
  contentRef.current = content;
2029
2105
  const agentRef = (0, react.useRef)(agent);
2030
2106
  agentRef.current = agent;
2107
+ const bridgeRef = (0, react.useRef)(null);
2031
2108
  const fetchStateRef = (0, react.useRef)({
2032
2109
  inProgress: false,
2033
2110
  promise: null,
2034
2111
  resourceUri: null
2035
2112
  });
2036
- const sendToIframe = (0, react.useCallback)((msg) => {
2037
- if (iframeRef.current?.contentWindow) {
2038
- console.log("[MCPAppsRenderer] Sending to iframe:", msg);
2039
- iframeRef.current.contentWindow.postMessage(msg, "*");
2040
- }
2041
- }, []);
2042
- const sendResponse = (0, react.useCallback)((id, result) => {
2043
- sendToIframe({
2044
- jsonrpc: "2.0",
2045
- id,
2046
- result
2047
- });
2048
- }, [sendToIframe]);
2049
- const sendErrorResponse = (0, react.useCallback)((id, code, message) => {
2050
- sendToIframe({
2051
- jsonrpc: "2.0",
2052
- id,
2053
- error: {
2054
- code,
2055
- message
2056
- }
2057
- });
2058
- }, [sendToIframe]);
2059
- const sendNotification = (0, react.useCallback)((method, params) => {
2060
- sendToIframe({
2061
- jsonrpc: "2.0",
2062
- method,
2063
- params: params || {}
2064
- });
2065
- }, [sendToIframe]);
2066
2113
  (0, react.useEffect)(() => {
2067
2114
  const { resourceUri, serverHash, serverId } = content;
2068
2115
  if (fetchStateRef.current.inProgress && fetchStateRef.current.resourceUri === resourceUri) {
@@ -2117,11 +2164,15 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2117
2164
  const container = containerRef.current;
2118
2165
  if (!container) return;
2119
2166
  let mounted = true;
2120
- let messageHandler = null;
2121
- let initialListener = null;
2167
+ let bridge = null;
2122
2168
  let createdIframe = null;
2123
2169
  const setup = async () => {
2124
2170
  try {
2171
+ const bridgeModule = await import("@modelcontextprotocol/ext-apps/app-bridge").catch((importErr) => {
2172
+ throw new Error("MCP Apps require '@modelcontextprotocol/ext-apps' and its peer '@modelcontextprotocol/sdk'. Install them with: npm install @modelcontextprotocol/ext-apps @modelcontextprotocol/sdk", { cause: importErr });
2173
+ });
2174
+ if (!mounted) return;
2175
+ const { AppBridge, PostMessageTransport } = bridgeModule;
2125
2176
  const iframe = document.createElement("iframe");
2126
2177
  createdIframe = iframe;
2127
2178
  iframe.style.width = "100%";
@@ -2132,151 +2183,108 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2132
2183
  iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
2133
2184
  iframe.setAttribute("data-testid", "mcp-app-iframe");
2134
2185
  iframe.setAttribute("title", "Interactive MCP application");
2135
- const sandboxReady = new Promise((resolve) => {
2136
- initialListener = (event) => {
2137
- if (event.source === iframe.contentWindow) {
2138
- if (event.data?.method === "ui/notifications/sandbox-proxy-ready") {
2139
- if (initialListener) {
2140
- window.removeEventListener("message", initialListener);
2141
- initialListener = null;
2142
- }
2143
- resolve();
2144
- }
2145
- }
2146
- };
2147
- window.addEventListener("message", initialListener);
2148
- });
2149
- if (!mounted) {
2150
- if (initialListener) {
2151
- window.removeEventListener("message", initialListener);
2152
- initialListener = null;
2153
- }
2154
- return;
2155
- }
2156
2186
  const cspDomains = fetchedResource._meta?.ui?.csp?.resourceDomains;
2157
2187
  iframe.srcdoc = buildSandboxHTML(cspDomains);
2158
2188
  iframeRef.current = iframe;
2159
2189
  container.appendChild(iframe);
2160
- await sandboxReady;
2161
- if (!mounted) return;
2162
- console.log("[MCPAppsRenderer] Sandbox proxy ready");
2163
- messageHandler = async (event) => {
2164
- if (event.source !== iframe.contentWindow) return;
2165
- const msg = event.data;
2166
- if (!msg || typeof msg !== "object" || msg.jsonrpc !== "2.0") return;
2167
- console.log("[MCPAppsRenderer] Received from iframe:", msg);
2168
- if (isRequest(msg)) switch (msg.method) {
2169
- case "ui/initialize":
2170
- sendResponse(msg.id, {
2171
- protocolVersion: PROTOCOL_VERSION,
2172
- hostInfo: {
2173
- name: "CopilotKit MCP Apps Host",
2174
- version: "1.0.0"
2175
- },
2176
- hostCapabilities: {
2177
- openLinks: {},
2178
- logging: {}
2179
- },
2180
- hostContext: {
2181
- theme: "light",
2182
- platform: "web"
2183
- }
2184
- });
2185
- break;
2186
- case "ui/message": {
2187
- const currentAgent = agentRef.current;
2188
- if (!currentAgent) {
2189
- console.warn("[MCPAppsRenderer] ui/message: No agent available");
2190
- sendResponse(msg.id, { isError: false });
2191
- break;
2192
- }
2193
- try {
2194
- const params = msg.params;
2195
- const role = params.role || "user";
2196
- const textContent = params.content?.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n") || "";
2197
- if (textContent) currentAgent.addMessage({
2198
- id: crypto.randomUUID(),
2199
- role,
2200
- content: textContent
2201
- });
2202
- sendResponse(msg.id, { isError: false });
2203
- if ((params.followUp ?? role === "user") && textContent) {
2204
- const capturedThreadId = currentAgent.threadId || "default";
2205
- mcpAppsRequestQueue.enqueue(currentAgent, () => ɵrunMcpFollowUp({
2206
- host: copilotkit,
2207
- agent: currentAgent,
2208
- capturedThreadId
2209
- })).catch((err) => console.error("[MCPAppsRenderer] ui/message agent run failed:", err));
2210
- }
2211
- } catch (err) {
2212
- console.error("[MCPAppsRenderer] ui/message error:", err);
2213
- sendResponse(msg.id, { isError: true });
2214
- }
2215
- break;
2216
- }
2217
- case "ui/open-link": {
2218
- const url = msg.params?.url;
2219
- if (url) {
2220
- window.open(url, "_blank", "noopener,noreferrer");
2221
- sendResponse(msg.id, { isError: false });
2222
- } else sendErrorResponse(msg.id, -32602, "Missing url parameter");
2223
- break;
2224
- }
2225
- case "tools/call": {
2226
- const { serverHash, serverId } = contentRef.current;
2227
- const currentAgent = agentRef.current;
2228
- if (!serverHash) {
2229
- sendErrorResponse(msg.id, -32603, "No server hash available for proxying");
2230
- break;
2231
- }
2232
- if (!currentAgent) {
2233
- sendErrorResponse(msg.id, -32603, "No agent available for proxying");
2234
- break;
2235
- }
2236
- try {
2237
- const runResult = await mcpAppsRequestQueue.enqueue(currentAgent, () => currentAgent.runAgent({ forwardedProps: { __proxiedMCPRequest: {
2238
- serverHash,
2239
- serverId,
2240
- method: "tools/call",
2241
- params: msg.params
2242
- } } }));
2243
- sendResponse(msg.id, runResult.result || {});
2244
- } catch (err) {
2245
- console.error("[MCPAppsRenderer] tools/call error:", err);
2246
- sendErrorResponse(msg.id, -32603, String(err));
2247
- }
2248
- break;
2249
- }
2250
- default: sendErrorResponse(msg.id, -32601, `Method not found: ${msg.method}`);
2251
- }
2252
- if (isNotification(msg)) switch (msg.method) {
2253
- case "ui/notifications/initialized":
2254
- console.log("[MCPAppsRenderer] Inner iframe initialized");
2255
- if (mounted) setIframeReady(true);
2256
- break;
2257
- case "ui/notifications/size-changed": {
2258
- const { width, height } = msg.params || {};
2259
- console.log("[MCPAppsRenderer] Size change:", {
2260
- width,
2261
- height
2262
- });
2263
- if (mounted) setIframeSize({
2264
- width: typeof width === "number" ? width : void 0,
2265
- height: typeof height === "number" ? height : void 0
2266
- });
2267
- break;
2268
- }
2269
- case "notifications/message":
2270
- console.log("[MCPAppsRenderer] App log:", msg.params);
2271
- break;
2272
- }
2273
- };
2274
- window.addEventListener("message", messageHandler);
2190
+ const win = iframe.contentWindow;
2191
+ if (!win) throw new Error("Sandbox iframe has no contentWindow");
2275
2192
  let html;
2276
2193
  if (fetchedResource.text) html = fetchedResource.text;
2277
2194
  else if (fetchedResource.blob) html = atob(fetchedResource.blob);
2278
2195
  else throw new Error("Resource has no text or blob content");
2279
- sendNotification("ui/notifications/sandbox-resource-ready", { html });
2196
+ bridge = new AppBridge(null, {
2197
+ name: "CopilotKit MCP Apps Host",
2198
+ version: "1.0.0"
2199
+ }, {
2200
+ openLinks: {},
2201
+ logging: {},
2202
+ message: { text: {} }
2203
+ }, { hostContext: {
2204
+ theme: "light",
2205
+ platform: "web"
2206
+ } });
2207
+ bridge.onsandboxready = () => {
2208
+ bridge?.sendSandboxResourceReady({ html });
2209
+ };
2210
+ bridge.setRequestHandler(CopilotKitUiMessageSchema, async (req) => {
2211
+ const currentAgent = agentRef.current;
2212
+ if (!currentAgent) {
2213
+ console.warn("[MCPAppsRenderer] ui/message: No agent available");
2214
+ return { isError: false };
2215
+ }
2216
+ try {
2217
+ const params = req.params;
2218
+ const ck = params._meta?.copilotkit ?? {};
2219
+ const role = ck.role || params.role || "user";
2220
+ const textContent = params.content?.filter((c) => c.type === "text" && c.text).map((c) => c.text).join("\n") || "";
2221
+ if (textContent) currentAgent.addMessage({
2222
+ id: crypto.randomUUID(),
2223
+ role,
2224
+ content: textContent
2225
+ });
2226
+ if ((ck.followUp ?? params.followUp ?? role === "user") && textContent) {
2227
+ const capturedThreadId = currentAgent.threadId || "default";
2228
+ mcpAppsRequestQueue.enqueue(currentAgent, () => ɵrunMcpFollowUp({
2229
+ host: copilotkit,
2230
+ agent: currentAgent,
2231
+ capturedThreadId
2232
+ })).catch((err) => console.error("[MCPAppsRenderer] ui/message agent run failed:", err));
2233
+ }
2234
+ return { isError: false };
2235
+ } catch (err) {
2236
+ console.error("[MCPAppsRenderer] ui/message error:", err);
2237
+ return { isError: true };
2238
+ }
2239
+ });
2240
+ bridge.onopenlink = async ({ url }) => {
2241
+ let parsed;
2242
+ try {
2243
+ parsed = new URL(url);
2244
+ } catch {
2245
+ console.warn("[MCPAppsRenderer] ui/open-link rejected: unparseable url");
2246
+ return { isError: true };
2247
+ }
2248
+ if (MCP_OPEN_LINK_BLOCKED_SCHEMES.has(parsed.protocol)) {
2249
+ console.warn("[MCPAppsRenderer] ui/open-link rejected: blocked scheme", parsed.protocol);
2250
+ return { isError: true };
2251
+ }
2252
+ window.open(url, "_blank", "noopener,noreferrer");
2253
+ return { isError: false };
2254
+ };
2255
+ bridge.oncalltool = async (params) => {
2256
+ const { serverHash, serverId } = contentRef.current;
2257
+ const currentAgent = agentRef.current;
2258
+ if (!serverHash) throw new Error("No server hash available for proxying");
2259
+ if (!currentAgent) throw new Error("No agent available for proxying");
2260
+ return (await mcpAppsRequestQueue.enqueue(currentAgent, () => currentAgent.runAgent({ forwardedProps: { __proxiedMCPRequest: {
2261
+ serverHash,
2262
+ serverId,
2263
+ method: "tools/call",
2264
+ params
2265
+ } } }))).result || { content: [] };
2266
+ };
2267
+ bridge.onsizechange = (p) => {
2268
+ if (!mounted) return;
2269
+ const { width, height } = p || {};
2270
+ setIframeSize({
2271
+ width: typeof width === "number" ? width : void 0,
2272
+ height: typeof height === "number" ? height : void 0
2273
+ });
2274
+ };
2275
+ bridge.oninitialized = () => {
2276
+ if (mounted) setIframeReady(true);
2277
+ };
2278
+ bridge.onloggingmessage = (p) => {
2279
+ console.log("[MCPAppsRenderer] App log:", p);
2280
+ };
2281
+ const transport = new PostMessageTransport(win, win);
2282
+ await bridge.connect(transport);
2283
+ if (!mounted) {
2284
+ await bridge.close();
2285
+ return;
2286
+ }
2287
+ bridgeRef.current = bridge;
2280
2288
  } catch (err) {
2281
2289
  console.error("[MCPAppsRenderer] Setup error:", err);
2282
2290
  if (mounted) setError(err instanceof Error ? err : new Error(String(err)));
@@ -2285,11 +2293,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2285
2293
  setup();
2286
2294
  return () => {
2287
2295
  mounted = false;
2288
- if (initialListener) {
2289
- window.removeEventListener("message", initialListener);
2290
- initialListener = null;
2291
- }
2292
- if (messageHandler) window.removeEventListener("message", messageHandler);
2296
+ bridgeRef.current = null;
2297
+ bridge?.close();
2293
2298
  if (createdIframe) {
2294
2299
  createdIframe.remove();
2295
2300
  createdIframe = null;
@@ -2299,9 +2304,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2299
2304
  }, [
2300
2305
  isLoading,
2301
2306
  fetchedResource,
2302
- sendNotification,
2303
- sendResponse,
2304
- sendErrorResponse
2307
+ copilotkit
2305
2308
  ]);
2306
2309
  (0, react.useEffect)(() => {
2307
2310
  if (iframeRef.current) {
@@ -2313,25 +2316,11 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
2313
2316
  }
2314
2317
  }, [iframeSize]);
2315
2318
  (0, react.useEffect)(() => {
2316
- if (iframeReady && content.toolInput) {
2317
- console.log("[MCPAppsRenderer] Sending tool input:", content.toolInput);
2318
- sendNotification("ui/notifications/tool-input", { arguments: content.toolInput });
2319
- }
2320
- }, [
2321
- iframeReady,
2322
- content.toolInput,
2323
- sendNotification
2324
- ]);
2319
+ if (iframeReady && content.toolInput) bridgeRef.current?.sendToolInput({ arguments: content.toolInput });
2320
+ }, [iframeReady, content.toolInput]);
2325
2321
  (0, react.useEffect)(() => {
2326
- if (iframeReady && content.result) {
2327
- console.log("[MCPAppsRenderer] Sending tool result:", content.result);
2328
- sendNotification("ui/notifications/tool-result", content.result);
2329
- }
2330
- }, [
2331
- iframeReady,
2332
- content.result,
2333
- sendNotification
2334
- ]);
2322
+ if (iframeReady && content.result) bridgeRef.current?.sendToolResult(content.result);
2323
+ }, [iframeReady, content.result]);
2335
2324
  const borderStyle = fetchedResource?._meta?.ui?.prefersBorder === true ? {
2336
2325
  borderRadius: "8px",
2337
2326
  backgroundColor: "#f9f9f9",
@@ -4050,7 +4039,8 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
4050
4039
  tool.name,
4051
4040
  tool.available,
4052
4041
  copilotkit,
4053
- JSON.stringify(extraDeps)
4042
+ JSON.stringify(extraDeps),
4043
+ JSON.stringify(tool.webmcp ?? null)
4054
4044
  ]);
4055
4045
  }
4056
4046
 
@@ -5505,7 +5495,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5505
5495
  * at the call site.
5506
5496
  */
5507
5497
  async function recordAnnotation(args) {
5508
- const { runtimeUrl, headers, type, payload, threadId, occurredAt } = args;
5498
+ const { runtimeUrl, headers, type, payload, threadId, occurredAt, fetch: fetchImplementation = globalThis.fetch } = args;
5509
5499
  const body = {
5510
5500
  type,
5511
5501
  threadId,
@@ -5513,7 +5503,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5513
5503
  ...payload !== void 0 ? { payload } : {},
5514
5504
  ...occurredAt !== void 0 ? { occurredAt } : {}
5515
5505
  };
5516
- const response = await fetch(`${runtimeUrl}/annotate`, {
5506
+ const response = await fetchImplementation(`${runtimeUrl}/annotate`, {
5517
5507
  method: "POST",
5518
5508
  headers: {
5519
5509
  "Content-Type": "application/json",
@@ -5582,6 +5572,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5582
5572
  ...input.data !== void 0 ? { data: input.data } : {}
5583
5573
  };
5584
5574
  return recordAnnotation({
5575
+ fetch: copilotkit.ɵruntimeFetch,
5585
5576
  runtimeUrl,
5586
5577
  headers: copilotkit.headers ?? {},
5587
5578
  type: "user_action",
@@ -5651,7 +5642,22 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5651
5642
 
5652
5643
  //#endregion
5653
5644
  //#region src/v2/hooks/use-attachments.tsx
5654
- /**
5645
+ const DEFAULT_MAX_SIZE = 20 * 1024 * 1024;
5646
+ /**
5647
+ * How many uploads run at once when `maxConcurrentUploads` is unset. One, because
5648
+ * `onUpload` is a public callback an app may have written expecting the previous
5649
+ * file to have finished — concurrency is something the app asks for.
5650
+ */
5651
+ const DEFAULT_MAX_CONCURRENT_UPLOADS = 1;
5652
+ /**
5653
+ * At least one upload at a time, whole files only; `NaN` or a non-number falls back to the
5654
+ * default, and `Infinity` means "no limit" — bounded in practice by how many files are queued.
5655
+ */
5656
+ function resolveMaxConcurrentUploads(configured) {
5657
+ if (typeof configured !== "number" || Number.isNaN(configured)) return DEFAULT_MAX_CONCURRENT_UPLOADS;
5658
+ return Math.max(1, Math.floor(configured));
5659
+ }
5660
+ /**
5655
5661
  * Hook that manages file attachment state — uploads, drag-and-drop, paste,
5656
5662
  * and lifecycle. All returned callbacks are referentially stable across
5657
5663
  * renders (via useCallback) to avoid destabilizing downstream memoization.
@@ -5666,10 +5672,62 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5666
5672
  configRef.current = config;
5667
5673
  const attachmentsRef = (0, react.useRef)([]);
5668
5674
  attachmentsRef.current = attachments;
5675
+ const uploadQueueRef = (0, react.useRef)([]);
5676
+ const activeWorkersRef = (0, react.useRef)(0);
5677
+ const uploadFile = (0, react.useCallback)(async (file, placeholder, cfg) => {
5678
+ try {
5679
+ let source;
5680
+ let uploadMetadata;
5681
+ if (cfg?.onUpload) {
5682
+ const { metadata: meta, ...uploadSource } = await cfg.onUpload(file);
5683
+ source = uploadSource;
5684
+ uploadMetadata = meta;
5685
+ } else source = {
5686
+ type: "data",
5687
+ value: await (0, _copilotkit_shared.readFileAsBase64)(file),
5688
+ mimeType: file.type
5689
+ };
5690
+ let thumbnail;
5691
+ if (placeholder.type === "video") thumbnail = await (0, _copilotkit_shared.generateVideoThumbnail)(file);
5692
+ setAttachments((prev) => prev.map((att) => att.id === placeholder.id ? {
5693
+ ...att,
5694
+ source,
5695
+ status: "ready",
5696
+ thumbnail,
5697
+ metadata: uploadMetadata
5698
+ } : att));
5699
+ } catch (error) {
5700
+ setAttachments((prev) => prev.filter((att) => att.id !== placeholder.id));
5701
+ console.error(`[CopilotKit] Failed to upload "${file.name}":`, error);
5702
+ cfg?.onUploadFailed?.({
5703
+ reason: "upload-failed",
5704
+ file,
5705
+ message: error instanceof Error ? error.message : `Failed to upload "${file.name}"`
5706
+ });
5707
+ }
5708
+ }, []);
5709
+ const drainUploadQueue = (0, react.useCallback)(async () => {
5710
+ activeWorkersRef.current++;
5711
+ try {
5712
+ for (;;) {
5713
+ const item = uploadQueueRef.current.shift();
5714
+ if (!item) return;
5715
+ try {
5716
+ await uploadFile(item.file, item.placeholder, item.cfg);
5717
+ } catch (error) {
5718
+ console.error("[CopilotKit] Upload worker error:", error);
5719
+ } finally {
5720
+ item.settle();
5721
+ }
5722
+ }
5723
+ } finally {
5724
+ activeWorkersRef.current--;
5725
+ }
5726
+ }, [uploadFile]);
5669
5727
  const processFiles = (0, react.useCallback)(async (files) => {
5670
5728
  const cfg = configRef.current;
5671
5729
  const accept = cfg?.accept ?? "*/*";
5672
- const maxSize = cfg?.maxSize ?? 20 * 1024 * 1024;
5730
+ const maxSize = cfg?.maxSize ?? DEFAULT_MAX_SIZE;
5673
5731
  const rejectedFiles = files.filter((file) => !(0, _copilotkit_shared.matchesAcceptFilter)(file, accept));
5674
5732
  for (const file of rejectedFiles) cfg?.onUploadFailed?.({
5675
5733
  reason: "invalid-type",
@@ -5677,6 +5735,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5677
5735
  message: `File "${file.name}" is not accepted. Supported types: ${accept}`
5678
5736
  });
5679
5737
  const validFiles = files.filter((file) => (0, _copilotkit_shared.matchesAcceptFilter)(file, accept));
5738
+ const queued = [];
5680
5739
  for (const file of validFiles) {
5681
5740
  if ((0, _copilotkit_shared.exceedsMaxSize)(file, maxSize)) {
5682
5741
  cfg?.onUploadFailed?.({
@@ -5686,53 +5745,37 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5686
5745
  });
5687
5746
  continue;
5688
5747
  }
5689
- const modality = (0, _copilotkit_shared.getModalityFromMimeType)(file.type);
5690
- const placeholderId = (0, _copilotkit_shared.randomUUID)();
5691
- const placeholder = {
5692
- id: placeholderId,
5693
- type: modality,
5694
- source: {
5695
- type: "data",
5696
- value: "",
5697
- mimeType: file.type
5698
- },
5699
- filename: file.name,
5700
- size: file.size,
5701
- status: "uploading"
5702
- };
5703
- setAttachments((prev) => [...prev, placeholder]);
5704
- try {
5705
- let source;
5706
- let uploadMetadata;
5707
- if (cfg?.onUpload) {
5708
- const { metadata: meta, ...uploadSource } = await cfg.onUpload(file);
5709
- source = uploadSource;
5710
- uploadMetadata = meta;
5711
- } else source = {
5712
- type: "data",
5713
- value: await (0, _copilotkit_shared.readFileAsBase64)(file),
5714
- mimeType: file.type
5715
- };
5716
- let thumbnail;
5717
- if (modality === "video") thumbnail = await (0, _copilotkit_shared.generateVideoThumbnail)(file);
5718
- setAttachments((prev) => prev.map((att) => att.id === placeholderId ? {
5719
- ...att,
5720
- source,
5721
- status: "ready",
5722
- thumbnail,
5723
- metadata: uploadMetadata
5724
- } : att));
5725
- } catch (error) {
5726
- setAttachments((prev) => prev.filter((att) => att.id !== placeholderId));
5727
- console.error(`[CopilotKit] Failed to upload "${file.name}":`, error);
5728
- cfg?.onUploadFailed?.({
5729
- reason: "upload-failed",
5730
- file,
5731
- message: error instanceof Error ? error.message : `Failed to upload "${file.name}"`
5732
- });
5733
- }
5748
+ queued.push({
5749
+ file,
5750
+ placeholder: {
5751
+ id: (0, _copilotkit_shared.randomUUID)(),
5752
+ type: (0, _copilotkit_shared.getModalityFromMimeType)(file.type),
5753
+ source: {
5754
+ type: "data",
5755
+ value: "",
5756
+ mimeType: file.type
5757
+ },
5758
+ filename: file.name,
5759
+ size: file.size,
5760
+ status: "uploading"
5761
+ }
5762
+ });
5734
5763
  }
5735
- }, []);
5764
+ if (queued.length === 0) return;
5765
+ setAttachments((prev) => [...prev, ...queued.map((q) => q.placeholder)]);
5766
+ const settled = queued.map(({ file, placeholder }) => new Promise((resolve) => {
5767
+ uploadQueueRef.current.push({
5768
+ file,
5769
+ placeholder,
5770
+ cfg,
5771
+ settle: resolve
5772
+ });
5773
+ }));
5774
+ const limit = resolveMaxConcurrentUploads(cfg?.maxConcurrentUploads);
5775
+ const toSpawn = Math.min(uploadQueueRef.current.length, Math.max(0, limit - activeWorkersRef.current));
5776
+ for (let i = 0; i < toSpawn; i++) drainUploadQueue();
5777
+ await Promise.all(settled);
5778
+ }, [drainUploadQueue]);
5736
5779
  const handleFileUpload = (0, react.useCallback)(async (e) => {
5737
5780
  if (!e.target.files?.length) return;
5738
5781
  try {
@@ -5844,9 +5887,9 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5844
5887
  * }
5845
5888
  * ```
5846
5889
  *
5847
- * @deprecated Legacy plural-container annotation compatibility only. New
5848
- * Intelligence runtimes assign one container with `ɵlearning.containerId` on
5849
- * `CopilotRuntime`.
5890
+ * @deprecated This hook supports only the legacy plural-container annotation.
5891
+ * Configure `getLearningContainerId` on `CopilotKitIntelligence` for new
5892
+ * Intelligence runtimes.
5850
5893
  */
5851
5894
  function useLearningContainers({ threadId, learningContainers }) {
5852
5895
  const { copilotkit } = useCopilotKit();
@@ -5860,8 +5903,10 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5860
5903
  const warnedMissingUrlRef = (0, react.useRef)(false);
5861
5904
  const runtimeUrlRef = (0, react.useRef)(copilotkit.runtimeUrl);
5862
5905
  const headersRef = (0, react.useRef)(copilotkit.headers ?? {});
5906
+ const runtimeFetchRef = (0, react.useRef)(copilotkit.ɵruntimeFetch);
5863
5907
  runtimeUrlRef.current = copilotkit.runtimeUrl;
5864
5908
  headersRef.current = copilotkit.headers ?? {};
5909
+ runtimeFetchRef.current = copilotkit.ɵruntimeFetch;
5865
5910
  const key = JSON.stringify(learningContainers);
5866
5911
  const defaultKey = JSON.stringify(DEFAULT_CONTAINERS);
5867
5912
  (0, react.useEffect)(() => {
@@ -5881,6 +5926,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5881
5926
  return;
5882
5927
  }
5883
5928
  recordAnnotation({
5929
+ fetch: copilotkit.ɵruntimeFetch,
5884
5930
  runtimeUrl,
5885
5931
  headers,
5886
5932
  type: "set_learning_containers",
@@ -5908,6 +5954,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5908
5954
  const capturedRuntimeUrl = runtimeUrlRef.current;
5909
5955
  const capturedHeaders = headersRef.current;
5910
5956
  if (capturedRuntimeUrl) recordAnnotation({
5957
+ fetch: runtimeFetchRef.current,
5911
5958
  runtimeUrl: capturedRuntimeUrl,
5912
5959
  headers: capturedHeaders,
5913
5960
  type: "set_learning_containers",
@@ -5950,9 +5997,9 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
5950
5997
  * }
5951
5998
  * ```
5952
5999
  *
5953
- * @deprecated Legacy plural-container annotation compatibility only. New
5954
- * Intelligence runtimes assign one container with `ɵlearning.containerId` on
5955
- * `CopilotRuntime`.
6000
+ * @deprecated This hook supports only the legacy plural-container annotation.
6001
+ * Configure `getLearningContainerId` on `CopilotKitIntelligence` for new
6002
+ * Intelligence runtimes.
5956
6003
  */
5957
6004
  function useLearningContainersInCurrentThread({ learningContainers }) {
5958
6005
  const threadId = useCopilotChatConfiguration()?.threadId;
@@ -9170,6 +9217,41 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
9170
9217
  });
9171
9218
  CopilotChatToggleButton.displayName = "CopilotChatToggleButton";
9172
9219
 
9220
+ //#endregion
9221
+ //#region src/v2/components/chat/modal-open-control.tsx
9222
+ const ModalOpenControlContext = (0, react.createContext)({});
9223
+ /**
9224
+ * Carries `open` / `onOpenChange` from a prebuilt surface down to the view that
9225
+ * owns the modal state.
9226
+ *
9227
+ * A context is required rather than plain props because `<CopilotSidebar>`
9228
+ * hands its view to `<CopilotChat>` as a `chatView` **component**. Threading a
9229
+ * value that changes (like `open`) through that component's identity would mint
9230
+ * a new element type on every toggle, and React unmounts and remounts the whole
9231
+ * chat subtree when the element type changes. That is the remount class of bug
9232
+ * already fixed for `<CopilotPopup>` on resize. Context keeps the override
9233
+ * identity stable while still re-rendering the view when `open` changes.
9234
+ */
9235
+ function ModalOpenControlProvider({ open, onOpenChange, children }) {
9236
+ const value = (0, react.useMemo)(() => ({
9237
+ open,
9238
+ onOpenChange
9239
+ }), [open, onOpenChange]);
9240
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalOpenControlContext.Provider, {
9241
+ value,
9242
+ children
9243
+ });
9244
+ }
9245
+ /**
9246
+ * Reads the controlled open state supplied by the surrounding prebuilt
9247
+ * surface. Returns an empty control (uncontrolled) when there is none.
9248
+ *
9249
+ * @returns The host's `open` / `onOpenChange` pair.
9250
+ */
9251
+ function useModalOpenControl() {
9252
+ return (0, react.useContext)(ModalOpenControlContext);
9253
+ }
9254
+
9173
9255
  //#endregion
9174
9256
  //#region src/v2/components/chat/CopilotModalHeader.tsx
9175
9257
  /**
@@ -9279,15 +9361,22 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
9279
9361
  const DEFAULT_SIDEBAR_WIDTH = 480;
9280
9362
  const SIDEBAR_TRANSITION_MS = 260;
9281
9363
  function CopilotSidebarView({ header, toggleButton, width, defaultOpen = true, position = "right", ...props }) {
9364
+ const { open, onOpenChange } = useModalOpenControl();
9365
+ const hasOpenControl = open !== void 0 || onOpenChange !== void 0;
9366
+ const internal = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotSidebarViewInternal, {
9367
+ header,
9368
+ toggleButton,
9369
+ width,
9370
+ position,
9371
+ ...props
9372
+ });
9282
9373
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
9283
- isModalDefaultOpen: defaultOpen,
9284
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotSidebarViewInternal, {
9285
- header,
9286
- toggleButton,
9287
- width,
9288
- position,
9289
- ...props
9290
- })
9374
+ isModalDefaultOpen: open ?? defaultOpen,
9375
+ children: hasOpenControl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ControlledModalOpenScope, {
9376
+ open,
9377
+ onOpenChange,
9378
+ children: internal
9379
+ }) : internal
9291
9380
  });
9292
9381
  }
9293
9382
  function CopilotSidebarViewInternal({ header, toggleButton, width, position = "right", ...props }) {
@@ -9351,7 +9440,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
9351
9440
  "data-position": position,
9352
9441
  className: cn("copilotKitSidebar copilotKitWindow", "cpk:fixed cpk:top-0 cpk:z-[1200] cpk:flex", position === "left" ? "cpk:left-0" : "cpk:right-0", "cpk:h-[100vh] cpk:h-[100dvh] cpk:max-h-screen", "cpk:w-full", position === "left" ? "cpk:border-r" : "cpk:border-l", "cpk:border-border cpk:bg-background cpk:text-foreground cpk:shadow-xl", "cpk:transition-transform cpk:duration-300 cpk:ease-out", isSidebarOpen ? "cpk:translate-x-0" : position === "left" ? "cpk:-translate-x-full cpk:pointer-events-none" : "cpk:translate-x-full cpk:pointer-events-none"),
9353
9442
  style: {
9354
- ["--sidebar-width"]: widthToCss(sidebarWidth),
9443
+ "--sidebar-width": widthToCss(sidebarWidth),
9355
9444
  paddingTop: "env(safe-area-inset-top)",
9356
9445
  paddingBottom: "env(safe-area-inset-bottom)"
9357
9446
  },
@@ -9413,17 +9502,24 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
9413
9502
  return `${fallback}px`;
9414
9503
  };
9415
9504
  function CopilotPopupView({ header, toggleButton, width, height, clickOutsideToClose, defaultOpen = true, className, ...restProps }) {
9505
+ const { open, onOpenChange } = useModalOpenControl();
9506
+ const hasOpenControl = open !== void 0 || onOpenChange !== void 0;
9507
+ const internal = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotPopupViewInternal, {
9508
+ header,
9509
+ toggleButton,
9510
+ width,
9511
+ height,
9512
+ clickOutsideToClose,
9513
+ className,
9514
+ ...restProps
9515
+ });
9416
9516
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChatConfigurationProvider, {
9417
- isModalDefaultOpen: defaultOpen,
9418
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotPopupViewInternal, {
9419
- header,
9420
- toggleButton,
9421
- width,
9422
- height,
9423
- clickOutsideToClose,
9424
- className,
9425
- ...restProps
9426
- })
9517
+ isModalDefaultOpen: open ?? defaultOpen,
9518
+ children: hasOpenControl ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ControlledModalOpenScope, {
9519
+ open,
9520
+ onOpenChange,
9521
+ children: internal
9522
+ }) : internal
9427
9523
  });
9428
9524
  }
9429
9525
  function CopilotPopupViewInternal({ header, toggleButton, width, height, clickOutsideToClose, className, ...restProps }) {
@@ -9556,7 +9652,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
9556
9652
 
9557
9653
  //#endregion
9558
9654
  //#region src/v2/components/chat/CopilotSidebar.tsx
9559
- function CopilotSidebar({ header, toggleButton, defaultOpen, width, position, ...chatProps }) {
9655
+ function CopilotSidebar({ header, toggleButton, defaultOpen, open, onOpenChange, width, position, ...chatProps }) {
9560
9656
  const { checkFeature } = useLicenseContext();
9561
9657
  const isSidebarLicensed = checkFeature("sidebar");
9562
9658
  (0, react.useEffect)(() => {
@@ -9582,11 +9678,15 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
9582
9678
  defaultOpen,
9583
9679
  position
9584
9680
  ]);
9585
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!isSidebarLicensed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineFeatureWarning, { featureName: "Sidebar" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChat, {
9586
- welcomeScreen: CopilotSidebarView.WelcomeScreen,
9587
- ...chatProps,
9588
- isModalDefaultOpen: defaultOpen,
9589
- chatView: SidebarViewOverride
9681
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!isSidebarLicensed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineFeatureWarning, { featureName: "Sidebar" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalOpenControlProvider, {
9682
+ open,
9683
+ onOpenChange,
9684
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChat, {
9685
+ welcomeScreen: CopilotSidebarView.WelcomeScreen,
9686
+ ...chatProps,
9687
+ isModalDefaultOpen: defaultOpen,
9688
+ chatView: SidebarViewOverride
9689
+ })
9590
9690
  })] });
9591
9691
  }
9592
9692
  CopilotSidebar.displayName = "CopilotSidebar";
@@ -9608,7 +9708,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
9608
9708
  });
9609
9709
  };
9610
9710
  const PopupViewOverrideWithStatics = Object.assign(PopupViewOverride, CopilotChatView_default);
9611
- function CopilotPopup({ header, toggleButton, defaultOpen, width, height, clickOutsideToClose, ...chatProps }) {
9711
+ function CopilotPopup({ header, toggleButton, defaultOpen, open, onOpenChange, width, height, clickOutsideToClose, ...chatProps }) {
9612
9712
  const { checkFeature } = useLicenseContext();
9613
9713
  const isPopupLicensed = checkFeature("popup");
9614
9714
  (0, react.useEffect)(() => {
@@ -9631,11 +9731,15 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
9631
9731
  ]);
9632
9732
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!isPopupLicensed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InlineFeatureWarning, { featureName: "Popup" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PopupShellPropsContext.Provider, {
9633
9733
  value: shellProps,
9634
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChat, {
9635
- welcomeScreen: CopilotPopupView_default.WelcomeScreen,
9636
- ...chatProps,
9637
- isModalDefaultOpen: defaultOpen,
9638
- chatView: PopupViewOverrideWithStatics
9734
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalOpenControlProvider, {
9735
+ open,
9736
+ onOpenChange,
9737
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopilotChat, {
9738
+ welcomeScreen: CopilotPopupView_default.WelcomeScreen,
9739
+ ...chatProps,
9740
+ isModalDefaultOpen: defaultOpen,
9741
+ chatView: PopupViewOverrideWithStatics
9742
+ })
9639
9743
  })
9640
9744
  })] });
9641
9745
  }
@@ -10952,7 +11056,7 @@ window.parent.postMessage({jsonrpc:"2.0",method:"ui/notifications/sandbox-proxy-
10952
11056
  switch (error.code) {
10953
11057
  case _copilotkit_shared.CopilotKitErrorCode.MISSING_PUBLIC_API_KEY_ERROR: return { primary: {
10954
11058
  label: "Show me how",
10955
- onClick: () => window.open("https://docs.copilotkit.ai/premium/overview#getting-access", "_blank", "noopener,noreferrer")
11059
+ onClick: () => window.open("https://docs.copilotkit.ai/intelligence/overview#plans-and-access", "_blank", "noopener,noreferrer")
10956
11060
  } };
10957
11061
  case _copilotkit_shared.CopilotKitErrorCode.UPGRADE_REQUIRED_ERROR: return { primary: {
10958
11062
  label: "Upgrade",