@agent-native/core 0.22.19 → 0.22.20
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.
- package/dist/client/embed-auth.d.ts.map +1 -1
- package/dist/client/embed-auth.js +85 -3
- package/dist/client/embed-auth.js.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.d.ts +3 -0
- package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.js +86 -9
- package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +73 -5
- package/dist/deploy/build.js.map +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +40 -3
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +6 -3
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/mcp/embed-app.d.ts +2 -2
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +390 -17
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +37 -10
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/create-server.d.ts.map +1 -1
- package/dist/server/create-server.js +21 -7
- package/dist/server/create-server.js.map +1 -1
- package/dist/server/embed-route.d.ts.map +1 -1
- package/dist/server/embed-route.js +62 -21
- package/dist/server/embed-route.js.map +1 -1
- package/dist/server/security-headers.d.ts.map +1 -1
- package/dist/server/security-headers.js +9 -1
- package/dist/server/security-headers.js.map +1 -1
- package/dist/server/ssr-handler.d.ts +2 -0
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +66 -11
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +12 -0
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -0
- package/dist/shared/mcp-embed-headers.js +51 -0
- package/dist/shared/mcp-embed-headers.js.map +1 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +23 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/actions.md +15 -5
- package/docs/content/external-agents.md +53 -27
- package/docs/content/mcp-protocol.md +29 -4
- package/package.json +1 -1
package/dist/mcp/embed-app.js
CHANGED
|
@@ -2,8 +2,8 @@ import { MCP_APP_CHAT_BRIDGE_QUERY_PARAM } from "../shared/embed-auth.js";
|
|
|
2
2
|
const MCP_APP_IMPORT = "https://esm.sh/@modelcontextprotocol/ext-apps@1.7.2/app-with-deps";
|
|
3
3
|
export const MCP_APP_REQUEST_ORIGIN_CSP_SOURCE = "$requestOrigin";
|
|
4
4
|
const MCP_APP_WRAPPER_CHROME_HEIGHT = 44;
|
|
5
|
-
export const
|
|
6
|
-
export const
|
|
5
|
+
export const DEFAULT_MCP_APP_SHELL_HEIGHT = 560;
|
|
6
|
+
export const DEFAULT_MCP_APP_VIEWPORT_HEIGHT = DEFAULT_MCP_APP_SHELL_HEIGHT - MCP_APP_WRAPPER_CHROME_HEIGHT;
|
|
7
7
|
function attr(value) {
|
|
8
8
|
return String(value ?? "")
|
|
9
9
|
.replace(/&/g, "&")
|
|
@@ -19,6 +19,10 @@ export function embedApp(options = {}) {
|
|
|
19
19
|
const embedByDefault = options.embedByDefault !== false;
|
|
20
20
|
const height = Math.max(320, Math.min(900, options.height ?? DEFAULT_MCP_APP_SHELL_HEIGHT));
|
|
21
21
|
const viewportHeight = height - MCP_APP_WRAPPER_CHROME_HEIGHT;
|
|
22
|
+
const frameDomains = [
|
|
23
|
+
MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,
|
|
24
|
+
...(options.frameDomains ?? []),
|
|
25
|
+
];
|
|
22
26
|
return {
|
|
23
27
|
title,
|
|
24
28
|
...(options.description ? { description: options.description } : {}),
|
|
@@ -28,19 +32,19 @@ export function embedApp(options = {}) {
|
|
|
28
32
|
<meta charset="utf-8">
|
|
29
33
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
30
34
|
<style>
|
|
31
|
-
:root { color-scheme: light dark; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: Canvas; color: CanvasText; }
|
|
35
|
+
:root { color-scheme: light dark; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: Canvas; color: CanvasText; --agent-native-shell-height: ${height}px; --agent-native-viewport-height: ${viewportHeight}px; }
|
|
32
36
|
* { box-sizing: border-box; }
|
|
33
37
|
body { margin: 0; }
|
|
34
|
-
.shell { display: grid; gap: 8px; min-height:
|
|
38
|
+
.shell { display: grid; gap: 8px; min-height: var(--agent-native-shell-height); padding: 0; }
|
|
35
39
|
.bar { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 36px; padding: 6px 8px; border-bottom: 1px solid color-mix(in srgb, CanvasText 12%, Canvas); }
|
|
36
40
|
.title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 700; color: color-mix(in srgb, CanvasText 72%, Canvas); }
|
|
37
41
|
.actions { display: flex; align-items: center; gap: 6px; }
|
|
38
42
|
button { min-height: 28px; border: 1px solid color-mix(in srgb, CanvasText 14%, Canvas); border-radius: 7px; background: Canvas; color: CanvasText; cursor: pointer; font: inherit; font-size: 12px; font-weight: 700; padding: 0 9px; }
|
|
39
43
|
button:disabled { opacity: .55; cursor: default; }
|
|
40
|
-
.stage { position: relative; min-height:
|
|
41
|
-
iframe { display: block; width: 100%; height:
|
|
42
|
-
.message { display: grid; place-items: center; min-height:
|
|
43
|
-
.fallback { display: grid; align-content: center; justify-items: center; gap: 12px; min-height:
|
|
44
|
+
.stage { position: relative; min-height: var(--agent-native-viewport-height); }
|
|
45
|
+
iframe { display: block; width: 100%; height: var(--agent-native-viewport-height); border: 0; background: Canvas; }
|
|
46
|
+
.message { display: grid; place-items: center; min-height: var(--agent-native-viewport-height); padding: 18px; color: color-mix(in srgb, CanvasText 62%, Canvas); font-size: 13px; line-height: 1.45; text-align: center; }
|
|
47
|
+
.fallback { display: grid; align-content: center; justify-items: center; gap: 12px; min-height: var(--agent-native-viewport-height); padding: 24px; background: Canvas; color: CanvasText; text-align: center; }
|
|
44
48
|
.fallback-title { max-width: 440px; font-size: 14px; font-weight: 700; }
|
|
45
49
|
.fallback-copy { max-width: 520px; color: color-mix(in srgb, CanvasText 64%, Canvas); font-size: 13px; line-height: 1.45; }
|
|
46
50
|
.fallback-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; }
|
|
@@ -75,7 +79,8 @@ export function embedApp(options = {}) {
|
|
|
75
79
|
const startTool = body.dataset.startTool || "create_embed_session";
|
|
76
80
|
const embedByDefault = body.dataset.embedDefault !== "0";
|
|
77
81
|
const chatBridgeParam = ${JSON.stringify(MCP_APP_CHAT_BRIDGE_QUERY_PARAM)};
|
|
78
|
-
const
|
|
82
|
+
const defaultIntrinsicHeight = ${height};
|
|
83
|
+
const chromeHeight = ${MCP_APP_WRAPPER_CHROME_HEIGHT};
|
|
79
84
|
let app = null;
|
|
80
85
|
let openAiBridge = null;
|
|
81
86
|
let toolInput = {};
|
|
@@ -107,6 +112,40 @@ export function embedApp(options = {}) {
|
|
|
107
112
|
: {};
|
|
108
113
|
}
|
|
109
114
|
|
|
115
|
+
function finiteNumber(value) {
|
|
116
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0
|
|
117
|
+
? value
|
|
118
|
+
: null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function contextMaxHeight(context) {
|
|
122
|
+
if (!context || typeof context !== "object") return null;
|
|
123
|
+
return finiteNumber(context.maxHeight) ||
|
|
124
|
+
finiteNumber(context.containerDimensions && context.containerDimensions.maxHeight);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function visibleIntrinsicHeight() {
|
|
128
|
+
const context = hostState().context || {};
|
|
129
|
+
const hostMaxHeight = contextMaxHeight(context);
|
|
130
|
+
if (hostMaxHeight) return Math.floor(hostMaxHeight);
|
|
131
|
+
const viewportHeight = finiteNumber(window.visualViewport && window.visualViewport.height) ||
|
|
132
|
+
finiteNumber(window.innerHeight);
|
|
133
|
+
return Math.floor(viewportHeight || defaultIntrinsicHeight);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function applyIntrinsicHeight(nextHeight) {
|
|
137
|
+
const boundedHeight = Math.min(
|
|
138
|
+
defaultIntrinsicHeight,
|
|
139
|
+
Math.floor(nextHeight || defaultIntrinsicHeight)
|
|
140
|
+
);
|
|
141
|
+
const height = Math.max(320, boundedHeight);
|
|
142
|
+
const viewportHeight = Math.max(0, height - chromeHeight);
|
|
143
|
+
document.documentElement.style.setProperty("--agent-native-shell-height", height + "px");
|
|
144
|
+
document.documentElement.style.setProperty("--agent-native-viewport-height", viewportHeight + "px");
|
|
145
|
+
if (appFrame) appFrame.style.height = viewportHeight + "px";
|
|
146
|
+
return height;
|
|
147
|
+
}
|
|
148
|
+
|
|
110
149
|
function parseToolResult(params) {
|
|
111
150
|
if (!params) return {};
|
|
112
151
|
if (params.result && typeof params.result === "object") {
|
|
@@ -188,6 +227,270 @@ export function embedApp(options = {}) {
|
|
|
188
227
|
}
|
|
189
228
|
}
|
|
190
229
|
|
|
230
|
+
function isEmbedStartUrl(value) {
|
|
231
|
+
if (typeof value !== "string" || !value) return false;
|
|
232
|
+
try {
|
|
233
|
+
const url = new URL(value, window.location.href);
|
|
234
|
+
return /\\/_agent-native\\/embed\\/start$/.test(url.pathname);
|
|
235
|
+
} catch {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function localPathFromUrl(url, includeToken) {
|
|
241
|
+
const next = new URL(url.href);
|
|
242
|
+
if (!includeToken) next.searchParams.delete("__an_embed_token");
|
|
243
|
+
return next.pathname + next.search + next.hash;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function rewriteRootRelativeHtmlUrls(html, appOrigin) {
|
|
247
|
+
return String(html).replace(
|
|
248
|
+
/\\b(src|href|poster|action)\\s*=\\s*(["'])\\/(?!\\/)/gi,
|
|
249
|
+
(_match, name, quote) => String(name) + "=" + quote + appOrigin + "/"
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function removeHtmlCspMeta(html) {
|
|
254
|
+
return String(html).replace(
|
|
255
|
+
/<meta\\s+[^>]*http-equiv\\s*=\\s*(["'])?content-security-policy\\1?[^>]*>/gi,
|
|
256
|
+
""
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function embedConfigForAppUrl(appUrl) {
|
|
261
|
+
const sanitizedTarget = localPathFromUrl(appUrl, false);
|
|
262
|
+
return {
|
|
263
|
+
origin: appUrl.origin,
|
|
264
|
+
href: appUrl.href,
|
|
265
|
+
baseHref: appUrl.origin + appUrl.pathname,
|
|
266
|
+
target: sanitizedTarget,
|
|
267
|
+
token: appUrl.searchParams.get("__an_embed_token") || "",
|
|
268
|
+
chatBridgeActive: appUrl.searchParams.get(chatBridgeParam) === "1",
|
|
269
|
+
chatBridgeParam,
|
|
270
|
+
embedTokenParam: "__an_embed_token",
|
|
271
|
+
embedTargetHeader: "x-agent-native-embed-target"
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function installExternalEmbedRuntime(config) {
|
|
276
|
+
window.__AGENT_NATIVE_EXTERNAL_EMBED = config;
|
|
277
|
+
try {
|
|
278
|
+
if (config.target) {
|
|
279
|
+
window.history.replaceState(window.history.state, "", config.target);
|
|
280
|
+
}
|
|
281
|
+
} catch (_err) {}
|
|
282
|
+
try {
|
|
283
|
+
if (config.token) {
|
|
284
|
+
sessionStorage.setItem("agent-native:embed-auth-token", config.token);
|
|
285
|
+
}
|
|
286
|
+
if (config.chatBridgeActive && config.token) {
|
|
287
|
+
sessionStorage.setItem("agent-native:mcp-chat-bridge", config.token);
|
|
288
|
+
}
|
|
289
|
+
} catch (_err) {}
|
|
290
|
+
if (window.__agentNativeExternalEmbedRuntimeInstalled) return;
|
|
291
|
+
window.__agentNativeExternalEmbedRuntimeInstalled = true;
|
|
292
|
+
function appOrigin() {
|
|
293
|
+
try {
|
|
294
|
+
return new URL(config.origin).origin;
|
|
295
|
+
} catch (_err) {
|
|
296
|
+
return "";
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function targetPath() {
|
|
300
|
+
return config.target || location.pathname + location.search;
|
|
301
|
+
}
|
|
302
|
+
function rewrittenUrl(value, appendToken) {
|
|
303
|
+
const origin = appOrigin();
|
|
304
|
+
if (!origin) return null;
|
|
305
|
+
let url;
|
|
306
|
+
try {
|
|
307
|
+
url = new URL(value, location.href);
|
|
308
|
+
} catch (_err) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
if (url.origin !== location.origin && url.origin !== origin) return null;
|
|
312
|
+
if (url.origin !== origin) {
|
|
313
|
+
const app = new URL(origin);
|
|
314
|
+
url.protocol = app.protocol;
|
|
315
|
+
url.host = app.host;
|
|
316
|
+
}
|
|
317
|
+
if (appendToken && config.token && url.pathname === "/_agent-native/events") {
|
|
318
|
+
url.searchParams.set(config.embedTokenParam, config.token);
|
|
319
|
+
}
|
|
320
|
+
return url.toString();
|
|
321
|
+
}
|
|
322
|
+
function authHeaders(input, init) {
|
|
323
|
+
const headers = new Headers(
|
|
324
|
+
init && init.headers ? init.headers : input instanceof Request ? input.headers : undefined
|
|
325
|
+
);
|
|
326
|
+
if (config.token && !headers.has("Authorization")) {
|
|
327
|
+
headers.set("Authorization", "Bearer " + config.token);
|
|
328
|
+
}
|
|
329
|
+
if (!headers.has(config.embedTargetHeader)) {
|
|
330
|
+
headers.set(config.embedTargetHeader, targetPath());
|
|
331
|
+
}
|
|
332
|
+
return headers;
|
|
333
|
+
}
|
|
334
|
+
if (typeof fetch === "function") {
|
|
335
|
+
const originalFetch = fetch.bind(window);
|
|
336
|
+
window.fetch = function(input, init) {
|
|
337
|
+
const raw = input instanceof Request ? input.url : String(input);
|
|
338
|
+
const url = rewrittenUrl(raw, false);
|
|
339
|
+
if (!url) return originalFetch(input, init);
|
|
340
|
+
const nextInit = Object.assign({}, init || {}, {
|
|
341
|
+
headers: authHeaders(input, init),
|
|
342
|
+
credentials: "omit"
|
|
343
|
+
});
|
|
344
|
+
if (input instanceof Request) {
|
|
345
|
+
return originalFetch(new Request(url, input), nextInit);
|
|
346
|
+
}
|
|
347
|
+
return originalFetch(url, nextInit);
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
if (typeof XMLHttpRequest !== "undefined") {
|
|
351
|
+
const originalOpen = XMLHttpRequest.prototype.open;
|
|
352
|
+
const originalSend = XMLHttpRequest.prototype.send;
|
|
353
|
+
XMLHttpRequest.prototype.open = function(method, url) {
|
|
354
|
+
const rewritten = rewrittenUrl(url, false);
|
|
355
|
+
this.__agentNativeExternalEmbed = !!rewritten;
|
|
356
|
+
return originalOpen.call(
|
|
357
|
+
this,
|
|
358
|
+
method,
|
|
359
|
+
rewritten || url,
|
|
360
|
+
arguments.length > 2 ? arguments[2] : true,
|
|
361
|
+
arguments[3],
|
|
362
|
+
arguments[4]
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
XMLHttpRequest.prototype.send = function(body) {
|
|
366
|
+
if (this.__agentNativeExternalEmbed) {
|
|
367
|
+
try {
|
|
368
|
+
if (config.token) this.setRequestHeader("Authorization", "Bearer " + config.token);
|
|
369
|
+
this.setRequestHeader(config.embedTargetHeader, targetPath());
|
|
370
|
+
} catch (_err) {}
|
|
371
|
+
}
|
|
372
|
+
return originalSend.call(this, body);
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
if (typeof EventSource !== "undefined") {
|
|
376
|
+
const OriginalEventSource = EventSource;
|
|
377
|
+
window.EventSource = function(url, options) {
|
|
378
|
+
return new OriginalEventSource(rewrittenUrl(url, true) || url, options);
|
|
379
|
+
};
|
|
380
|
+
window.EventSource.prototype = OriginalEventSource.prototype;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function copyDocumentElementAttributes(source) {
|
|
385
|
+
const target = document.documentElement;
|
|
386
|
+
for (const attr of Array.from(target.attributes)) {
|
|
387
|
+
target.removeAttribute(attr.name);
|
|
388
|
+
}
|
|
389
|
+
for (const attr of Array.from(source.attributes)) {
|
|
390
|
+
target.setAttribute(attr.name, attr.value);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function importChildren(source, target) {
|
|
395
|
+
target.replaceChildren(
|
|
396
|
+
...Array.from(source.childNodes).map((node) => document.importNode(node, true))
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function isModuleScript(script) {
|
|
401
|
+
return (script.getAttribute("type") || "").trim().toLowerCase() === "module";
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function isRunnableClassicScript(script) {
|
|
405
|
+
const type = (script.getAttribute("type") || "").trim().toLowerCase();
|
|
406
|
+
return !type || type === "text/javascript" || type === "application/javascript";
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function runClassicScript(script) {
|
|
410
|
+
const next = document.createElement("script");
|
|
411
|
+
for (const attr of Array.from(script.attributes)) {
|
|
412
|
+
if (attr.name === "type") continue;
|
|
413
|
+
next.setAttribute(attr.name, attr.value);
|
|
414
|
+
}
|
|
415
|
+
if (script.src) {
|
|
416
|
+
next.src = script.src;
|
|
417
|
+
} else {
|
|
418
|
+
next.textContent = script.textContent || "";
|
|
419
|
+
}
|
|
420
|
+
document.body.appendChild(next);
|
|
421
|
+
next.remove();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function rootRelativeSpecifiersToAbsolute(code, appOrigin) {
|
|
425
|
+
return String(code).replace(/(["'])\\/(?!\\/)/g, "$1" + appOrigin + "/");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function moduleCodeToClassicAsync(code, appOrigin) {
|
|
429
|
+
return rootRelativeSpecifiersToAbsolute(code, appOrigin)
|
|
430
|
+
.replace(
|
|
431
|
+
/\\bimport\\s+\\*\\s+as\\s+([A-Za-z_$][\\w$]*)\\s+from\\s+(["'][^"']+["'])\\s*;?/g,
|
|
432
|
+
"const $1 = await import($2);"
|
|
433
|
+
)
|
|
434
|
+
.replace(/\\bimport\\s+(["'][^"']+["'])\\s*;?/g, "await import($1);")
|
|
435
|
+
.replace(/\\bimport\\((["'][^"']+["'])\\)\\s*;?/g, "await import($1);");
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function runModuleScriptAsClassic(script, appOrigin) {
|
|
439
|
+
const code = moduleCodeToClassicAsync(script.textContent || "", appOrigin);
|
|
440
|
+
const runner = document.createElement("script");
|
|
441
|
+
runner.textContent =
|
|
442
|
+
"(async()=>{" +
|
|
443
|
+
code +
|
|
444
|
+
"})().catch((err)=>{console.error('[agent-native] transplanted app module failed',err);document.body.setAttribute('data-agent-native-hydration-error',String(err&&err.message||err));});";
|
|
445
|
+
document.body.appendChild(runner);
|
|
446
|
+
runner.remove();
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function mountTransplantedHtml(html, appUrl) {
|
|
450
|
+
const config = embedConfigForAppUrl(appUrl);
|
|
451
|
+
installExternalEmbedRuntime(config);
|
|
452
|
+
const parsed = new DOMParser().parseFromString(
|
|
453
|
+
rewriteRootRelativeHtmlUrls(removeHtmlCspMeta(html), appUrl.origin),
|
|
454
|
+
"text/html"
|
|
455
|
+
);
|
|
456
|
+
const scripts = Array.from(parsed.querySelectorAll("script"));
|
|
457
|
+
copyDocumentElementAttributes(parsed.documentElement);
|
|
458
|
+
importChildren(parsed.head, document.head);
|
|
459
|
+
const base = document.createElement("base");
|
|
460
|
+
base.href = config.baseHref;
|
|
461
|
+
document.head.prepend(base);
|
|
462
|
+
importChildren(parsed.body, document.body);
|
|
463
|
+
for (const script of scripts) {
|
|
464
|
+
if (isRunnableClassicScript(script)) runClassicScript(script);
|
|
465
|
+
}
|
|
466
|
+
for (const script of scripts) {
|
|
467
|
+
if (isModuleScript(script)) runModuleScriptAsClassic(script, appUrl.origin);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
async function transplantAppDocument(src) {
|
|
472
|
+
clearFrameReadyTimer();
|
|
473
|
+
clearFrameLoadTimer();
|
|
474
|
+
appFrame = null;
|
|
475
|
+
lastFrameSrc = src;
|
|
476
|
+
setMessage("Loading app");
|
|
477
|
+
const response = await fetch(src, {
|
|
478
|
+
credentials: "omit",
|
|
479
|
+
redirect: "follow",
|
|
480
|
+
headers: { Accept: "text/html" }
|
|
481
|
+
});
|
|
482
|
+
if (!response.ok) {
|
|
483
|
+
throw new Error("Embedded app returned HTTP " + response.status + ".");
|
|
484
|
+
}
|
|
485
|
+
const html = await response.text();
|
|
486
|
+
const appUrl = new URL(response.url || src);
|
|
487
|
+
try {
|
|
488
|
+
window.history.replaceState(window.history.state, "", localPathFromUrl(appUrl, false));
|
|
489
|
+
} catch {}
|
|
490
|
+
mountTransplantedHtml(html, appUrl);
|
|
491
|
+
notifyHostHeightRepeatedly();
|
|
492
|
+
}
|
|
493
|
+
|
|
191
494
|
function wantsEmbed() {
|
|
192
495
|
if (toolInput.embed === false || toolInput.embed === "false") return false;
|
|
193
496
|
if (embedByDefault) return true;
|
|
@@ -336,6 +639,28 @@ export function embedApp(options = {}) {
|
|
|
336
639
|
return true;
|
|
337
640
|
}
|
|
338
641
|
|
|
642
|
+
function isClaudeMcpContentHost() {
|
|
643
|
+
try {
|
|
644
|
+
return /(^|\\.)claudemcpcontent\\.com$/i.test(window.location.hostname || "");
|
|
645
|
+
} catch {
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function isChatGptSandboxHost() {
|
|
651
|
+
try {
|
|
652
|
+
const host = window.location.hostname || "";
|
|
653
|
+
const appParam = new URL(window.location.href).searchParams.get("app");
|
|
654
|
+
return /(^|\\.)oaiusercontent\\.com$/i.test(host) || appParam === "chatgpt";
|
|
655
|
+
} catch {
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function shouldRenderControlledAppFrame() {
|
|
661
|
+
return !!openAiBridge || isChatGptSandboxHost();
|
|
662
|
+
}
|
|
663
|
+
|
|
339
664
|
function navigateToAppFrame(src) {
|
|
340
665
|
clearFrameReadyTimer();
|
|
341
666
|
clearFrameLoadTimer();
|
|
@@ -382,11 +707,19 @@ export function embedApp(options = {}) {
|
|
|
382
707
|
}
|
|
383
708
|
|
|
384
709
|
function notifyHostHeight() {
|
|
710
|
+
const height = applyIntrinsicHeight(visibleIntrinsicHeight());
|
|
385
711
|
if (!openAiBridge || typeof openAiBridge.notifyIntrinsicHeight !== "function") {
|
|
712
|
+
if (app && typeof app.sendSizeChanged === "function") {
|
|
713
|
+
try {
|
|
714
|
+
app.sendSizeChanged({ height });
|
|
715
|
+
} catch (err) {
|
|
716
|
+
console.warn("[agent-native] MCP host rejected size update", err);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
386
719
|
return;
|
|
387
720
|
}
|
|
388
721
|
try {
|
|
389
|
-
openAiBridge.notifyIntrinsicHeight({ height
|
|
722
|
+
openAiBridge.notifyIntrinsicHeight({ height });
|
|
390
723
|
} catch (err) {
|
|
391
724
|
console.warn("[agent-native] ChatGPT rejected intrinsic height update", err);
|
|
392
725
|
}
|
|
@@ -482,6 +815,22 @@ export function embedApp(options = {}) {
|
|
|
482
815
|
}
|
|
483
816
|
});
|
|
484
817
|
|
|
818
|
+
function notifyHostHeightSoon() {
|
|
819
|
+
requestAnimationFrame(() => notifyHostHeight());
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function notifyHostHeightRepeatedly() {
|
|
823
|
+
notifyHostHeight();
|
|
824
|
+
[0, 250, 1000, 2500].forEach((delay) => {
|
|
825
|
+
setTimeout(() => notifyHostHeight(), delay);
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
window.addEventListener("resize", notifyHostHeightSoon, { passive: true });
|
|
830
|
+
if (window.visualViewport) {
|
|
831
|
+
window.visualViewport.addEventListener("resize", notifyHostHeightSoon, { passive: true });
|
|
832
|
+
}
|
|
833
|
+
|
|
485
834
|
async function launchEmbed() {
|
|
486
835
|
if (!openUrl) {
|
|
487
836
|
setMessage("Open link was not available.");
|
|
@@ -497,6 +846,20 @@ export function embedApp(options = {}) {
|
|
|
497
846
|
try {
|
|
498
847
|
const selfNavigate = shouldSelfNavigateToApp();
|
|
499
848
|
const embedUrl = withChatBridgeParam(openUrl);
|
|
849
|
+
if (selfNavigate && isEmbedStartUrl(embedUrl)) {
|
|
850
|
+
if (isClaudeMcpContentHost()) {
|
|
851
|
+
await transplantAppDocument(embedUrl);
|
|
852
|
+
} else if (shouldRenderControlledAppFrame()) {
|
|
853
|
+
renderFrame(embedUrl);
|
|
854
|
+
} else {
|
|
855
|
+
navigateToAppFrame(embedUrl);
|
|
856
|
+
}
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
if (!selfNavigate && isEmbedStartUrl(embedUrl)) {
|
|
860
|
+
renderFrame(embedUrl);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
500
863
|
const result = await callEmbedSessionTool({
|
|
501
864
|
url: embedUrl,
|
|
502
865
|
chrome: typeof toolInput.chrome === "string" ? toolInput.chrome : "full"
|
|
@@ -508,7 +871,13 @@ export function embedApp(options = {}) {
|
|
|
508
871
|
return;
|
|
509
872
|
}
|
|
510
873
|
if (selfNavigate) {
|
|
511
|
-
|
|
874
|
+
if (isClaudeMcpContentHost()) {
|
|
875
|
+
await transplantAppDocument(data.startUrl);
|
|
876
|
+
} else if (shouldRenderControlledAppFrame()) {
|
|
877
|
+
renderFrame(data.startUrl);
|
|
878
|
+
} else {
|
|
879
|
+
navigateToAppFrame(data.startUrl);
|
|
880
|
+
}
|
|
512
881
|
} else {
|
|
513
882
|
renderFrame(data.startUrl);
|
|
514
883
|
}
|
|
@@ -618,7 +987,11 @@ export function embedApp(options = {}) {
|
|
|
618
987
|
|
|
619
988
|
async function startMcpAppsBridge() {
|
|
620
989
|
const { App } = await import("${MCP_APP_IMPORT}");
|
|
621
|
-
app = new App(
|
|
990
|
+
app = new App(
|
|
991
|
+
{ name: "Agent Native Embed", version: "1.0.0" },
|
|
992
|
+
{},
|
|
993
|
+
{ autoResize: false }
|
|
994
|
+
);
|
|
622
995
|
app.ontoolinput = (params) => {
|
|
623
996
|
toolInput = params.arguments || {};
|
|
624
997
|
};
|
|
@@ -631,10 +1004,12 @@ export function embedApp(options = {}) {
|
|
|
631
1004
|
};
|
|
632
1005
|
app.onhostcontextchanged = () => {
|
|
633
1006
|
updateDisplayButton();
|
|
1007
|
+
notifyHostHeight();
|
|
634
1008
|
sendHostContext();
|
|
635
1009
|
};
|
|
636
1010
|
await app.connect();
|
|
637
1011
|
updateDisplayButton();
|
|
1012
|
+
notifyHostHeight();
|
|
638
1013
|
sendHostContext();
|
|
639
1014
|
}
|
|
640
1015
|
|
|
@@ -646,16 +1021,14 @@ export function embedApp(options = {}) {
|
|
|
646
1021
|
</body>
|
|
647
1022
|
</html>`,
|
|
648
1023
|
csp: {
|
|
649
|
-
connectDomains: ["https://esm.sh"],
|
|
1024
|
+
connectDomains: ["https://esm.sh", MCP_APP_REQUEST_ORIGIN_CSP_SOURCE],
|
|
650
1025
|
resourceDomains: [
|
|
651
1026
|
"https://esm.sh",
|
|
652
1027
|
MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,
|
|
653
1028
|
...(options.frameDomains ?? []),
|
|
654
1029
|
],
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
...(options.frameDomains ?? []),
|
|
658
|
-
],
|
|
1030
|
+
baseUriDomains: [MCP_APP_REQUEST_ORIGIN_CSP_SOURCE],
|
|
1031
|
+
frameDomains,
|
|
659
1032
|
},
|
|
660
1033
|
prefersBorder: false,
|
|
661
1034
|
};
|