@agent-native/core 0.22.15 → 0.22.18

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 (46) hide show
  1. package/dist/client/embed-auth.d.ts.map +1 -1
  2. package/dist/client/embed-auth.js +161 -20
  3. package/dist/client/embed-auth.js.map +1 -1
  4. package/dist/client/frame.d.ts.map +1 -1
  5. package/dist/client/frame.js +1 -0
  6. package/dist/client/frame.js.map +1 -1
  7. package/dist/client/use-action.d.ts.map +1 -1
  8. package/dist/client/use-action.js +13 -0
  9. package/dist/client/use-action.js.map +1 -1
  10. package/dist/client/use-db-sync.d.ts.map +1 -1
  11. package/dist/client/use-db-sync.js +58 -3
  12. package/dist/client/use-db-sync.js.map +1 -1
  13. package/dist/client/use-db-sync.spec.js +27 -0
  14. package/dist/client/use-db-sync.spec.js.map +1 -1
  15. package/dist/deploy/build.d.ts +30 -0
  16. package/dist/deploy/build.d.ts.map +1 -1
  17. package/dist/deploy/build.js +31 -16
  18. package/dist/deploy/build.js.map +1 -1
  19. package/dist/mcp/build-server.d.ts.map +1 -1
  20. package/dist/mcp/build-server.js +29 -3
  21. package/dist/mcp/build-server.js.map +1 -1
  22. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  23. package/dist/mcp/builtin-tools.js +0 -1
  24. package/dist/mcp/builtin-tools.js.map +1 -1
  25. package/dist/mcp/embed-app.d.ts +2 -0
  26. package/dist/mcp/embed-app.d.ts.map +1 -1
  27. package/dist/mcp/embed-app.js +311 -35
  28. package/dist/mcp/embed-app.js.map +1 -1
  29. package/dist/server/auth.d.ts.map +1 -1
  30. package/dist/server/auth.js +9 -1
  31. package/dist/server/auth.js.map +1 -1
  32. package/dist/server/embed-route.d.ts.map +1 -1
  33. package/dist/server/embed-route.js +30 -7
  34. package/dist/server/embed-route.js.map +1 -1
  35. package/dist/server/embed-session.d.ts.map +1 -1
  36. package/dist/server/embed-session.js +11 -1
  37. package/dist/server/embed-session.js.map +1 -1
  38. package/dist/server/security-headers.d.ts +6 -1
  39. package/dist/server/security-headers.d.ts.map +1 -1
  40. package/dist/server/security-headers.js +10 -2
  41. package/dist/server/security-headers.js.map +1 -1
  42. package/dist/vite/client.d.ts.map +1 -1
  43. package/dist/vite/client.js +28 -0
  44. package/dist/vite/client.js.map +1 -1
  45. package/docs/content/external-agents.md +24 -0
  46. package/package.json +1 -1
@@ -1,6 +1,9 @@
1
1
  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
+ const MCP_APP_WRAPPER_CHROME_HEIGHT = 44;
5
+ export const DEFAULT_MCP_APP_VIEWPORT_HEIGHT = 720;
6
+ export const DEFAULT_MCP_APP_SHELL_HEIGHT = DEFAULT_MCP_APP_VIEWPORT_HEIGHT + MCP_APP_WRAPPER_CHROME_HEIGHT;
4
7
  function attr(value) {
5
8
  return String(value ?? "")
6
9
  .replace(/&/g, "&")
@@ -14,7 +17,8 @@ export function embedApp(options = {}) {
14
17
  const openLabel = options.openLabel ?? "Open in app";
15
18
  const startToolName = options.startToolName ?? "create_embed_session";
16
19
  const embedByDefault = options.embedByDefault !== false;
17
- const height = Math.max(320, Math.min(900, options.height ?? 900));
20
+ const height = Math.max(320, Math.min(900, options.height ?? DEFAULT_MCP_APP_SHELL_HEIGHT));
21
+ const viewportHeight = height - MCP_APP_WRAPPER_CHROME_HEIGHT;
18
22
  return {
19
23
  title,
20
24
  ...(options.description ? { description: options.description } : {}),
@@ -33,9 +37,14 @@ export function embedApp(options = {}) {
33
37
  .actions { display: flex; align-items: center; gap: 6px; }
34
38
  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; }
35
39
  button:disabled { opacity: .55; cursor: default; }
36
- .stage { position: relative; min-height: ${height - 44}px; }
37
- iframe { display: block; width: 100%; height: ${height - 44}px; border: 0; background: Canvas; }
38
- .message { display: grid; place-items: center; min-height: ${height - 44}px; padding: 18px; color: color-mix(in srgb, CanvasText 62%, Canvas); font-size: 13px; line-height: 1.45; text-align: center; }
40
+ .stage { position: relative; min-height: ${viewportHeight}px; }
41
+ iframe { display: block; width: 100%; height: ${viewportHeight}px; border: 0; background: Canvas; }
42
+ .message { display: grid; place-items: center; min-height: ${viewportHeight}px; padding: 18px; color: color-mix(in srgb, CanvasText 62%, Canvas); font-size: 13px; line-height: 1.45; text-align: center; }
43
+ .fallback { display: grid; align-content: center; justify-items: center; gap: 12px; min-height: ${viewportHeight}px; padding: 24px; background: Canvas; color: CanvasText; text-align: center; }
44
+ .fallback-title { max-width: 440px; font-size: 14px; font-weight: 700; }
45
+ .fallback-copy { max-width: 520px; color: color-mix(in srgb, CanvasText 64%, Canvas); font-size: 13px; line-height: 1.45; }
46
+ .fallback-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; }
47
+ .fallback-url { max-width: min(560px, 100%); overflow-wrap: anywhere; color: color-mix(in srgb, CanvasText 76%, Canvas); font-size: 12px; }
39
48
  </style>
40
49
  </head>
41
50
  <body
@@ -58,9 +67,6 @@ export function embedApp(options = {}) {
58
67
  </section>
59
68
  </main>
60
69
  <script type="module">
61
- import { App } from "${MCP_APP_IMPORT}";
62
-
63
- const app = new App({ name: "Agent Native Embed", version: "1.0.0" }, {});
64
70
  const body = document.body;
65
71
  const stage = document.querySelector("[data-stage]");
66
72
  const titleEl = document.querySelector("[data-title-label]");
@@ -69,10 +75,17 @@ export function embedApp(options = {}) {
69
75
  const startTool = body.dataset.startTool || "create_embed_session";
70
76
  const embedByDefault = body.dataset.embedDefault !== "0";
71
77
  const chatBridgeParam = ${JSON.stringify(MCP_APP_CHAT_BRIDGE_QUERY_PARAM)};
78
+ const intrinsicHeight = ${height};
79
+ let app = null;
80
+ let openAiBridge = null;
72
81
  let toolInput = {};
73
82
  let openUrl = "";
74
83
  let startedFor = "";
75
84
  let appFrame = null;
85
+ let appFrameReady = false;
86
+ let appFrameReadyTimer = null;
87
+ let appFrameLoadTimer = null;
88
+ let lastFrameSrc = "";
76
89
 
77
90
  function esc(value) {
78
91
  return String(value ?? "")
@@ -88,8 +101,20 @@ export function embedApp(options = {}) {
88
101
  try { return JSON.parse(value); } catch { return fallback; }
89
102
  }
90
103
 
104
+ function objectValue(value) {
105
+ return value && typeof value === "object" && !Array.isArray(value)
106
+ ? value
107
+ : {};
108
+ }
109
+
91
110
  function parseToolResult(params) {
92
111
  if (!params) return {};
112
+ if (params.result && typeof params.result === "object") {
113
+ return parseToolResult(params.result);
114
+ }
115
+ if (params.toolResult && typeof params.toolResult === "object") {
116
+ return parseToolResult(params.toolResult);
117
+ }
93
118
  if (params.structuredContent && typeof params.structuredContent === "object") {
94
119
  return params.structuredContent;
95
120
  }
@@ -106,10 +131,26 @@ export function embedApp(options = {}) {
106
131
  }
107
132
 
108
133
  function hostState() {
134
+ if (openAiBridge) {
135
+ return {
136
+ context: {
137
+ displayMode: openAiBridge.displayMode,
138
+ availableDisplayModes: typeof openAiBridge.requestDisplayMode === "function"
139
+ ? ["inline", "fullscreen", "pip"]
140
+ : [],
141
+ maxHeight: openAiBridge.maxHeight,
142
+ locale: openAiBridge.locale,
143
+ theme: openAiBridge.theme,
144
+ view: openAiBridge.view
145
+ },
146
+ capabilities: { openai: true },
147
+ version: openAiBridge.userAgent
148
+ };
149
+ }
109
150
  return {
110
- context: app.getHostContext ? app.getHostContext() : undefined,
111
- capabilities: app.getHostCapabilities ? app.getHostCapabilities() : undefined,
112
- version: app.getHostVersion ? app.getHostVersion() : undefined
151
+ context: app && app.getHostContext ? app.getHostContext() : undefined,
152
+ capabilities: app && app.getHostCapabilities ? app.getHostCapabilities() : undefined,
153
+ version: app && app.getHostVersion ? app.getHostVersion() : undefined
113
154
  };
114
155
  }
115
156
 
@@ -153,12 +194,23 @@ export function embedApp(options = {}) {
153
194
  }
154
195
 
155
196
  function supportedDisplayMode(mode) {
197
+ if (openAiBridge && typeof openAiBridge.requestDisplayMode === "function") {
198
+ return mode === "inline" || mode === "fullscreen" || mode === "pip";
199
+ }
156
200
  const modes = hostState().context && hostState().context.availableDisplayModes;
157
201
  return Array.isArray(modes) && modes.includes(mode);
158
202
  }
159
203
 
160
204
  async function requestHostDisplayMode(mode) {
161
- const result = await app.requestDisplayMode({ mode });
205
+ let result;
206
+ if (openAiBridge && typeof openAiBridge.requestDisplayMode === "function") {
207
+ result = await openAiBridge.requestDisplayMode({ mode });
208
+ } else {
209
+ if (!app || typeof app.requestDisplayMode !== "function") {
210
+ throw new Error("Display mode changes are not available in this host.");
211
+ }
212
+ result = await app.requestDisplayMode({ mode });
213
+ }
162
214
  updateDisplayButton();
163
215
  sendHostContext();
164
216
  return result;
@@ -183,14 +235,94 @@ export function embedApp(options = {}) {
183
235
  stage.innerHTML = '<div class="message">' + esc(message) + '</div>';
184
236
  }
185
237
 
238
+ function clearFrameReadyTimer() {
239
+ if (!appFrameReadyTimer) return;
240
+ clearTimeout(appFrameReadyTimer);
241
+ appFrameReadyTimer = null;
242
+ }
243
+
244
+ function clearFrameLoadTimer() {
245
+ if (!appFrameLoadTimer) return;
246
+ clearTimeout(appFrameLoadTimer);
247
+ appFrameLoadTimer = null;
248
+ }
249
+
250
+ function startFrameReadyTimer(frame) {
251
+ clearFrameReadyTimer();
252
+ appFrameReadyTimer = setTimeout(() => {
253
+ if (!appFrameReady && appFrame === frame) renderFrameFallback();
254
+ }, 7000);
255
+ }
256
+
257
+ function renderFrameFallback() {
258
+ clearFrameReadyTimer();
259
+ clearFrameLoadTimer();
260
+ appFrame = null;
261
+ stage.innerHTML =
262
+ '<div class="fallback">' +
263
+ '<div class="fallback-title">Open this app in its own tab</div>' +
264
+ '<div class="fallback-copy">This chat host did not allow the embedded app frame to load inline. You can still open the same app route through the host or use the URL below.</div>' +
265
+ '<div class="fallback-actions">' +
266
+ '<button type="button" data-fallback-open>Open app</button>' +
267
+ '<button type="button" data-fallback-retry>Try inline again</button>' +
268
+ '</div>' +
269
+ (openUrl ? '<a class="fallback-url" href="' + esc(openUrl) + '" target="_blank" rel="noreferrer">' + esc(openUrl) + '</a>' : '') +
270
+ '</div>';
271
+ const fallbackOpen = stage.querySelector("[data-fallback-open]");
272
+ const fallbackRetry = stage.querySelector("[data-fallback-retry]");
273
+ if (fallbackOpen) {
274
+ fallbackOpen.disabled = !openUrl;
275
+ fallbackOpen.onclick = () => {
276
+ if (openUrl) void openFallbackExternal();
277
+ };
278
+ }
279
+ if (fallbackRetry) {
280
+ fallbackRetry.disabled = !lastFrameSrc;
281
+ fallbackRetry.onclick = () => {
282
+ if (lastFrameSrc) renderFrame(lastFrameSrc);
283
+ };
284
+ }
285
+ }
286
+
287
+ async function openFallbackExternal() {
288
+ let url = openUrl;
289
+ try {
290
+ const embedUrl = withChatBridgeParam(openUrl);
291
+ const result = await callEmbedSessionTool({
292
+ url: embedUrl,
293
+ chrome: typeof toolInput.chrome === "string" ? toolInput.chrome : "full"
294
+ });
295
+ const data = parseToolResult(result);
296
+ if (typeof data.startUrl === "string" && data.startUrl) {
297
+ url = data.startUrl;
298
+ }
299
+ } catch (err) {
300
+ console.warn("[agent-native] MCP fallback could not mint a fresh app session", err);
301
+ }
302
+ await openHostLink({ url });
303
+ }
304
+
186
305
  function renderFrame(src) {
306
+ clearFrameReadyTimer();
307
+ clearFrameLoadTimer();
187
308
  const frame = document.createElement("iframe");
188
309
  frame.title = body.dataset.iframeTitle || "Agent Native app";
189
310
  frame.src = src;
190
311
  frame.allow = "clipboard-read; clipboard-write";
191
312
  appFrame = frame;
192
- frame.addEventListener("load", () => sendFrameReadyMessages(frame));
313
+ appFrameReady = false;
314
+ lastFrameSrc = src;
315
+ frame.addEventListener("load", () => {
316
+ if (appFrame !== frame) return;
317
+ clearFrameLoadTimer();
318
+ sendFrameReadyMessages(frame);
319
+ startFrameReadyTimer(frame);
320
+ });
193
321
  stage.replaceChildren(frame);
322
+ notifyHostHeight();
323
+ appFrameLoadTimer = setTimeout(() => {
324
+ if (!appFrameReady && appFrame === frame) renderFrameFallback();
325
+ }, 30000);
194
326
  }
195
327
 
196
328
  async function updateHostModelContext(data) {
@@ -199,13 +331,40 @@ export function embedApp(options = {}) {
199
331
  if (data && data.structuredContent && typeof data.structuredContent === "object") {
200
332
  params.structuredContent = data.structuredContent;
201
333
  }
334
+ if (openAiBridge && typeof openAiBridge.setWidgetState === "function") {
335
+ openAiBridge.setWidgetState({
336
+ ...objectValue(openAiBridge.widgetState),
337
+ agentNativeModelContext: params
338
+ });
339
+ return { ok: true };
340
+ }
341
+ if (!app || typeof app.updateModelContext !== "function") return { ok: false };
202
342
  await app.updateModelContext(params);
343
+ return { ok: true };
203
344
  }
204
345
 
205
346
  async function openHostLink(data) {
206
347
  const url = typeof (data && data.url) === "string" ? data.url : "";
207
348
  if (!url) return { isError: true };
208
- return await app.openLink({ url });
349
+ if (openAiBridge && typeof openAiBridge.openExternal === "function") {
350
+ return await openAiBridge.openExternal({ href: url, redirectUrl: false });
351
+ }
352
+ if (app && typeof app.openLink === "function") {
353
+ return await app.openLink({ url });
354
+ }
355
+ window.open(url, "_blank", "noopener,noreferrer");
356
+ return { ok: true };
357
+ }
358
+
359
+ function notifyHostHeight() {
360
+ if (!openAiBridge || typeof openAiBridge.notifyIntrinsicHeight !== "function") {
361
+ return;
362
+ }
363
+ try {
364
+ openAiBridge.notifyIntrinsicHeight({ height: intrinsicHeight });
365
+ } catch (err) {
366
+ console.warn("[agent-native] ChatGPT rejected intrinsic height update", err);
367
+ }
209
368
  }
210
369
 
211
370
  function respondToAppFrame(requestId, work) {
@@ -236,14 +395,29 @@ export function embedApp(options = {}) {
236
395
  const context = typeof chat.context === "string" ? chat.context : "";
237
396
  if (context.trim()) {
238
397
  try {
239
- await app.updateModelContext({
240
- content: [{ type: "text", text: context }]
241
- });
398
+ if (openAiBridge && typeof openAiBridge.setWidgetState === "function") {
399
+ openAiBridge.setWidgetState({
400
+ ...objectValue(openAiBridge.widgetState),
401
+ agentNativeChatContext: context
402
+ });
403
+ } else if (app && typeof app.updateModelContext === "function") {
404
+ await app.updateModelContext({
405
+ content: [{ type: "text", text: context }]
406
+ });
407
+ }
242
408
  } catch (err) {
243
409
  console.warn("[agent-native] MCP host rejected model context update", err);
244
410
  }
245
411
  }
246
412
  try {
413
+ if (openAiBridge && typeof openAiBridge.sendFollowUpMessage === "function") {
414
+ await openAiBridge.sendFollowUpMessage({
415
+ prompt: context.trim() ? context.trim() + "\\n\\n" + message : message,
416
+ scrollToBottom: true
417
+ });
418
+ return;
419
+ }
420
+ if (!app || typeof app.sendMessage !== "function") return;
247
421
  const result = await app.sendMessage({
248
422
  role: "user",
249
423
  content: [{ type: "text", text: message }]
@@ -260,6 +434,12 @@ export function embedApp(options = {}) {
260
434
  if (!appFrame || event.source !== appFrame.contentWindow) return;
261
435
  if (!event.data) return;
262
436
  const data = event.data.data || {};
437
+ if (event.data.type === "agentNative.embeddedAppReady") {
438
+ appFrameReady = true;
439
+ clearFrameLoadTimer();
440
+ clearFrameReadyTimer();
441
+ return;
442
+ }
263
443
  if (event.data.type === "agentNative.submitChat") {
264
444
  void sendHostChat(data);
265
445
  return;
@@ -291,12 +471,9 @@ export function embedApp(options = {}) {
291
471
  setMessage("Loading app");
292
472
  try {
293
473
  const embedUrl = withChatBridgeParam(openUrl);
294
- const result = await app.callServerTool({
295
- name: startTool,
296
- arguments: {
297
- url: embedUrl,
298
- chrome: typeof toolInput.chrome === "string" ? toolInput.chrome : "full"
299
- }
474
+ const result = await callEmbedSessionTool({
475
+ url: embedUrl,
476
+ chrome: typeof toolInput.chrome === "string" ? toolInput.chrome : "full"
300
477
  });
301
478
  const data = parseToolResult(result);
302
479
  if (!data.startUrl) {
@@ -311,11 +488,33 @@ export function embedApp(options = {}) {
311
488
  }
312
489
  }
313
490
 
491
+ async function callEmbedSessionTool(args) {
492
+ if (openAiBridge && typeof openAiBridge.callTool === "function") {
493
+ return await openAiBridge.callTool(startTool, args);
494
+ }
495
+ if (!app || typeof app.callServerTool !== "function") {
496
+ throw new Error("Host tool calls are not available.");
497
+ }
498
+ return await app.callServerTool({ name: startTool, arguments: args });
499
+ }
500
+
501
+ function updateHostOpenInAppUrl() {
502
+ if (!openAiBridge || !openUrl || typeof openAiBridge.setOpenInAppUrl !== "function") {
503
+ return;
504
+ }
505
+ try {
506
+ openAiBridge.setOpenInAppUrl({ href: openUrl });
507
+ } catch (err) {
508
+ console.warn("[agent-native] ChatGPT rejected open-in-app URL", err);
509
+ }
510
+ }
511
+
314
512
  function updateOpenButton() {
315
513
  openButton.disabled = !openUrl;
316
514
  openButton.onclick = () => {
317
- if (openUrl) void app.openLink({ url: openUrl });
515
+ if (openUrl) void openHostLink({ url: openUrl });
318
516
  };
517
+ updateHostOpenInAppUrl();
319
518
  }
320
519
 
321
520
  function updateTitle(data) {
@@ -323,29 +522,106 @@ export function embedApp(options = {}) {
323
522
  titleEl.textContent = String(label);
324
523
  }
325
524
 
326
- app.ontoolinput = (params) => {
327
- toolInput = params.arguments || {};
328
- };
329
- app.ontoolresult = (params) => {
525
+ function readOpenAiBridge() {
526
+ return window.openai && typeof window.openai === "object"
527
+ ? window.openai
528
+ : null;
529
+ }
530
+
531
+ function openAiToolResultParams(bridge) {
532
+ const params = {};
533
+ if (bridge && bridge.toolOutput !== undefined) {
534
+ if (bridge.toolOutput && typeof bridge.toolOutput === "object") {
535
+ params.structuredContent = bridge.toolOutput;
536
+ } else {
537
+ params.content = [{ type: "text", text: String(bridge.toolOutput) }];
538
+ }
539
+ }
540
+ if (bridge && bridge.toolResponseMetadata && typeof bridge.toolResponseMetadata === "object") {
541
+ params._meta = bridge.toolResponseMetadata;
542
+ }
543
+ return params;
544
+ }
545
+
546
+ function syncOpenAiBridge(bridge) {
547
+ if (!bridge) return false;
548
+ openAiBridge = bridge;
549
+ toolInput = objectValue(bridge.toolInput);
550
+ const params = openAiToolResultParams(bridge);
330
551
  const data = parseToolResult(params);
331
552
  openUrl = openLinkFrom(params, data);
332
553
  updateTitle(data);
333
554
  updateOpenButton();
334
- void launchEmbed();
335
- };
336
- app.onhostcontextchanged = () => {
337
555
  updateDisplayButton();
556
+ notifyHostHeight();
338
557
  sendHostContext();
339
- };
340
- await app.connect();
341
- updateDisplayButton();
342
- sendHostContext();
558
+ if (openUrl) {
559
+ void launchEmbed();
560
+ } else if (!appFrame) {
561
+ setMessage("Waiting for app result");
562
+ }
563
+ return true;
564
+ }
565
+
566
+ function waitForOpenAiBridge() {
567
+ const existing = readOpenAiBridge();
568
+ if (existing) return Promise.resolve(existing);
569
+ return new Promise((resolve) => {
570
+ let settled = false;
571
+ const finish = (bridge) => {
572
+ if (settled) return;
573
+ settled = true;
574
+ window.removeEventListener("openai:set_globals", onGlobals);
575
+ clearTimeout(timer);
576
+ resolve(bridge || readOpenAiBridge());
577
+ };
578
+ const onGlobals = () => finish(readOpenAiBridge());
579
+ const timer = setTimeout(() => finish(null), 200);
580
+ window.addEventListener("openai:set_globals", onGlobals, { passive: true });
581
+ });
582
+ }
583
+
584
+ window.addEventListener("openai:set_globals", () => {
585
+ const bridge = readOpenAiBridge();
586
+ if (bridge && (!appFrame || openAiBridge)) syncOpenAiBridge(bridge);
587
+ }, { passive: true });
588
+
589
+ async function startMcpAppsBridge() {
590
+ const { App } = await import("${MCP_APP_IMPORT}");
591
+ app = new App({ name: "Agent Native Embed", version: "1.0.0" }, {});
592
+ app.ontoolinput = (params) => {
593
+ toolInput = params.arguments || {};
594
+ };
595
+ app.ontoolresult = (params) => {
596
+ const data = parseToolResult(params);
597
+ openUrl = openLinkFrom(params, data);
598
+ updateTitle(data);
599
+ updateOpenButton();
600
+ void launchEmbed();
601
+ };
602
+ app.onhostcontextchanged = () => {
603
+ updateDisplayButton();
604
+ sendHostContext();
605
+ };
606
+ await app.connect();
607
+ updateDisplayButton();
608
+ sendHostContext();
609
+ }
610
+
611
+ const initialOpenAiBridge = await waitForOpenAiBridge();
612
+ if (!syncOpenAiBridge(initialOpenAiBridge)) {
613
+ await startMcpAppsBridge();
614
+ }
343
615
  </script>
344
616
  </body>
345
617
  </html>`,
346
618
  csp: {
347
619
  connectDomains: ["https://esm.sh"],
348
- resourceDomains: ["https://esm.sh"],
620
+ resourceDomains: [
621
+ "https://esm.sh",
622
+ MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,
623
+ ...(options.frameDomains ?? []),
624
+ ],
349
625
  frameDomains: [
350
626
  MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,
351
627
  ...(options.frameDomains ?? []),
@@ -1 +1 @@
1
- {"version":3,"file":"embed-app.js","sourceRoot":"","sources":["../../src/mcp/embed-app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,+BAA+B,EAAE,MAAM,yBAAyB,CAAC;AAE1E,MAAM,cAAc,GAClB,mEAAmE,CAAC;AAEtE,MAAM,CAAC,MAAM,iCAAiC,GAAG,gBAAgB,CAAC;AAalE,SAAS,IAAI,CAAC,KAAyB;IACrC,OAAO,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,UAA2B,EAAE;IAE7B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC;IAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,kBAAkB,CAAC;IAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,aAAa,CAAC;IACrD,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,sBAAsB,CAAC;IACtE,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,KAAK,KAAK,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC;IAEnE,OAAO;QACL,KAAK;QACL,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,IAAI,EAAE,GAAG,EAAE,CAAC;;;;;;;;;oDASoC,MAAM;;;;;;+CAMX,MAAM,GAAG,EAAE;oDACN,MAAM,GAAG,EAAE;iEACE,MAAM,GAAG,EAAE;;;;oBAIxD,IAAI,CAAC,KAAK,CAAC;uBACR,IAAI,CAAC,WAAW,CAAC;qBACnB,IAAI,CAAC,SAAS,CAAC;qBACf,IAAI,CAAC,aAAa,CAAC;wBAChB,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;;;;4CAIN,IAAI,CAAC,KAAK,CAAC;;;mDAGJ,IAAI,CAAC,SAAS,CAAC;;;;;;;;2BAQvC,cAAc;;;;;;;;;;8BAUX,IAAI,CAAC,SAAS,CAAC,+BAA+B,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAkRrE;QACJ,GAAG,EAAE;YACH,cAAc,EAAE,CAAC,gBAAgB,CAAC;YAClC,eAAe,EAAE,CAAC,gBAAgB,CAAC;YACnC,YAAY,EAAE;gBACZ,iCAAiC;gBACjC,GAAG,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;aAChC;SACF;QACD,aAAa,EAAE,KAAK;KACrB,CAAC;AACJ,CAAC","sourcesContent":["import type { ActionMcpAppResourceConfig } from \"../action.js\";\nimport { MCP_APP_CHAT_BRIDGE_QUERY_PARAM } from \"../shared/embed-auth.js\";\n\nconst MCP_APP_IMPORT =\n \"https://esm.sh/@modelcontextprotocol/ext-apps@1.7.2/app-with-deps\";\n\nexport const MCP_APP_REQUEST_ORIGIN_CSP_SOURCE = \"$requestOrigin\";\n\nexport interface EmbedAppOptions {\n title?: string;\n description?: string;\n iframeTitle?: string;\n openLabel?: string;\n embedByDefault?: boolean;\n startToolName?: string;\n frameDomains?: string[];\n height?: number;\n}\n\nfunction attr(value: string | undefined): string {\n return String(value ?? \"\")\n .replace(/&/g, \"&amp;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\nexport function embedApp(\n options: EmbedAppOptions = {},\n): ActionMcpAppResourceConfig {\n const title = options.title ?? \"Open app\";\n const iframeTitle = options.iframeTitle ?? \"Agent Native app\";\n const openLabel = options.openLabel ?? \"Open in app\";\n const startToolName = options.startToolName ?? \"create_embed_session\";\n const embedByDefault = options.embedByDefault !== false;\n const height = Math.max(320, Math.min(900, options.height ?? 900));\n\n return {\n title,\n ...(options.description ? { description: options.description } : {}),\n html: () => `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <style>\n :root { color-scheme: light dark; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: Canvas; color: CanvasText; }\n * { box-sizing: border-box; }\n body { margin: 0; }\n .shell { display: grid; gap: 8px; min-height: ${height}px; padding: 0; }\n .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); }\n .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); }\n .actions { display: flex; align-items: center; gap: 6px; }\n 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; }\n button:disabled { opacity: .55; cursor: default; }\n .stage { position: relative; min-height: ${height - 44}px; }\n iframe { display: block; width: 100%; height: ${height - 44}px; border: 0; background: Canvas; }\n .message { display: grid; place-items: center; min-height: ${height - 44}px; padding: 18px; color: color-mix(in srgb, CanvasText 62%, Canvas); font-size: 13px; line-height: 1.45; text-align: center; }\n </style>\n</head>\n<body\n data-app-title=\"${attr(title)}\"\n data-iframe-title=\"${attr(iframeTitle)}\"\n data-open-label=\"${attr(openLabel)}\"\n data-start-tool=\"${attr(startToolName)}\"\n data-embed-default=\"${embedByDefault ? \"1\" : \"0\"}\"\n>\n <main class=\"shell\">\n <div class=\"bar\">\n <div class=\"title\" data-title-label>${attr(title)}</div>\n <div class=\"actions\">\n <button type=\"button\" data-display hidden disabled>Fullscreen</button>\n <button type=\"button\" data-open disabled>${attr(openLabel)}</button>\n </div>\n </div>\n <section class=\"stage\" data-stage>\n <div class=\"message\">Preparing app</div>\n </section>\n </main>\n <script type=\"module\">\n import { App } from \"${MCP_APP_IMPORT}\";\n\n const app = new App({ name: \"Agent Native Embed\", version: \"1.0.0\" }, {});\n const body = document.body;\n const stage = document.querySelector(\"[data-stage]\");\n const titleEl = document.querySelector(\"[data-title-label]\");\n const openButton = document.querySelector(\"[data-open]\");\n const displayButton = document.querySelector(\"[data-display]\");\n const startTool = body.dataset.startTool || \"create_embed_session\";\n const embedByDefault = body.dataset.embedDefault !== \"0\";\n const chatBridgeParam = ${JSON.stringify(MCP_APP_CHAT_BRIDGE_QUERY_PARAM)};\n let toolInput = {};\n let openUrl = \"\";\n let startedFor = \"\";\n let appFrame = null;\n\n function esc(value) {\n return String(value ?? \"\")\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n }\n\n function parseJson(value, fallback) {\n if (value && typeof value === \"object\") return value;\n if (typeof value !== \"string\" || !value.trim()) return fallback;\n try { return JSON.parse(value); } catch { return fallback; }\n }\n\n function parseToolResult(params) {\n if (!params) return {};\n if (params.structuredContent && typeof params.structuredContent === \"object\") {\n return params.structuredContent;\n }\n const parts = Array.isArray(params.content) ? params.content : [];\n const textPart = parts.find((part) => part && part.type === \"text\" && typeof part.text === \"string\");\n return parseJson(textPart ? textPart.text : \"\", {});\n }\n\n function openLinkFrom(params, data) {\n const metaUrl = params && params._meta && params._meta[\"agent-native/openLink\"]\n ? params._meta[\"agent-native/openLink\"].webUrl\n : \"\";\n return metaUrl || data.url || data.deepLink || data.openUrl || \"\";\n }\n\n function hostState() {\n return {\n context: app.getHostContext ? app.getHostContext() : undefined,\n capabilities: app.getHostCapabilities ? app.getHostCapabilities() : undefined,\n version: app.getHostVersion ? app.getHostVersion() : undefined\n };\n }\n\n function sendToAppFrame(message) {\n if (!appFrame || !appFrame.contentWindow) return;\n try { appFrame.contentWindow.postMessage(message, \"*\"); } catch {}\n }\n\n function sendHostContext() {\n sendToAppFrame({ type: \"agentNative.mcpHostContext\", data: hostState() });\n }\n\n function sendFrameReadyMessages(frame) {\n const originPayload = { type: \"agentNative.frameOrigin\", origin: window.location.origin };\n [0, 200, 500, 1500].forEach((delay) => {\n setTimeout(() => {\n try { frame.contentWindow && frame.contentWindow.postMessage(originPayload, \"*\"); } catch {}\n sendHostContext();\n }, delay);\n });\n }\n\n function withChatBridgeParam(value) {\n if (typeof value !== \"string\" || !value) return value;\n try {\n const base = \"http://agent-native.invalid\";\n const url = value.startsWith(\"/\") ? new URL(value, base) : new URL(value);\n url.searchParams.set(chatBridgeParam, \"1\");\n return value.startsWith(\"/\")\n ? url.pathname + url.search + url.hash\n : url.toString();\n } catch {\n return value;\n }\n }\n\n function wantsEmbed() {\n if (toolInput.embed === false || toolInput.embed === \"false\") return false;\n if (embedByDefault) return true;\n return toolInput.embed === true || toolInput.embed === \"true\";\n }\n\n function supportedDisplayMode(mode) {\n const modes = hostState().context && hostState().context.availableDisplayModes;\n return Array.isArray(modes) && modes.includes(mode);\n }\n\n async function requestHostDisplayMode(mode) {\n const result = await app.requestDisplayMode({ mode });\n updateDisplayButton();\n sendHostContext();\n return result;\n }\n\n function updateDisplayButton() {\n const context = hostState().context || {};\n const nextMode = context.displayMode === \"fullscreen\" ? \"inline\" : \"fullscreen\";\n const supported = supportedDisplayMode(nextMode);\n displayButton.hidden = !supported;\n displayButton.disabled = !supported;\n displayButton.textContent = nextMode === \"fullscreen\" ? \"Fullscreen\" : \"Inline\";\n displayButton.onclick = () => {\n if (!supportedDisplayMode(nextMode)) return;\n void requestHostDisplayMode(nextMode).catch((err) => {\n console.warn(\"[agent-native] MCP host rejected display mode request\", err);\n });\n };\n }\n\n function setMessage(message) {\n stage.innerHTML = '<div class=\"message\">' + esc(message) + '</div>';\n }\n\n function renderFrame(src) {\n const frame = document.createElement(\"iframe\");\n frame.title = body.dataset.iframeTitle || \"Agent Native app\";\n frame.src = src;\n frame.allow = \"clipboard-read; clipboard-write\";\n appFrame = frame;\n frame.addEventListener(\"load\", () => sendFrameReadyMessages(frame));\n stage.replaceChildren(frame);\n }\n\n async function updateHostModelContext(data) {\n const params = {};\n if (Array.isArray(data && data.content)) params.content = data.content;\n if (data && data.structuredContent && typeof data.structuredContent === \"object\") {\n params.structuredContent = data.structuredContent;\n }\n await app.updateModelContext(params);\n }\n\n async function openHostLink(data) {\n const url = typeof (data && data.url) === \"string\" ? data.url : \"\";\n if (!url) return { isError: true };\n return await app.openLink({ url });\n }\n\n function respondToAppFrame(requestId, work) {\n if (!requestId) return;\n Promise.resolve(work)\n .then((result) => {\n sendToAppFrame({\n type: \"agentNative.mcpHost.response\",\n data: { requestId, ok: true, result }\n });\n })\n .catch((err) => {\n sendToAppFrame({\n type: \"agentNative.mcpHost.response\",\n data: {\n requestId,\n ok: false,\n error: err && err.message ? err.message : String(err)\n }\n });\n });\n }\n\n async function sendHostChat(chat) {\n if (!chat || chat.submit === false) return;\n const message = typeof chat.message === \"string\" ? chat.message : \"\";\n if (!message.trim()) return;\n const context = typeof chat.context === \"string\" ? chat.context : \"\";\n if (context.trim()) {\n try {\n await app.updateModelContext({\n content: [{ type: \"text\", text: context }]\n });\n } catch (err) {\n console.warn(\"[agent-native] MCP host rejected model context update\", err);\n }\n }\n try {\n const result = await app.sendMessage({\n role: \"user\",\n content: [{ type: \"text\", text: message }]\n });\n if (result && result.isError) {\n console.warn(\"[agent-native] MCP host rejected chat message\", result);\n }\n } catch (err) {\n console.warn(\"[agent-native] MCP host chat bridge failed\", err);\n }\n }\n\n window.addEventListener(\"message\", (event) => {\n if (!appFrame || event.source !== appFrame.contentWindow) return;\n if (!event.data) return;\n const data = event.data.data || {};\n if (event.data.type === \"agentNative.submitChat\") {\n void sendHostChat(data);\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.updateModelContext\") {\n respondToAppFrame(data.requestId, updateHostModelContext(data));\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.openLink\") {\n respondToAppFrame(data.requestId, openHostLink(data));\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.requestDisplayMode\") {\n respondToAppFrame(data.requestId, requestHostDisplayMode(data.mode));\n }\n });\n\n async function launchEmbed() {\n if (!openUrl) {\n setMessage(\"Open link was not available.\");\n return;\n }\n if (!wantsEmbed()) {\n setMessage(\"Ready to open.\");\n return;\n }\n if (startedFor === openUrl) return;\n startedFor = openUrl;\n setMessage(\"Loading app\");\n try {\n const embedUrl = withChatBridgeParam(openUrl);\n const result = await app.callServerTool({\n name: startTool,\n arguments: {\n url: embedUrl,\n chrome: typeof toolInput.chrome === \"string\" ? toolInput.chrome : \"full\"\n }\n });\n const data = parseToolResult(result);\n if (!data.startUrl) {\n startedFor = \"\";\n setMessage(data.error || \"This app can be opened, but not embedded from this MCP server.\");\n return;\n }\n renderFrame(data.startUrl);\n } catch (err) {\n startedFor = \"\";\n setMessage(err && err.message ? err.message : \"Could not launch embedded app.\");\n }\n }\n\n function updateOpenButton() {\n openButton.disabled = !openUrl;\n openButton.onclick = () => {\n if (openUrl) void app.openLink({ url: openUrl });\n };\n }\n\n function updateTitle(data) {\n const label = data.label || data.app || data.view || body.dataset.appTitle || \"App\";\n titleEl.textContent = String(label);\n }\n\n app.ontoolinput = (params) => {\n toolInput = params.arguments || {};\n };\n app.ontoolresult = (params) => {\n const data = parseToolResult(params);\n openUrl = openLinkFrom(params, data);\n updateTitle(data);\n updateOpenButton();\n void launchEmbed();\n };\n app.onhostcontextchanged = () => {\n updateDisplayButton();\n sendHostContext();\n };\n await app.connect();\n updateDisplayButton();\n sendHostContext();\n </script>\n</body>\n</html>`,\n csp: {\n connectDomains: [\"https://esm.sh\"],\n resourceDomains: [\"https://esm.sh\"],\n frameDomains: [\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...(options.frameDomains ?? []),\n ],\n },\n prefersBorder: false,\n };\n}\n"]}
1
+ {"version":3,"file":"embed-app.js","sourceRoot":"","sources":["../../src/mcp/embed-app.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ActionMcpAppResourceConfig } from \"../action.js\";\nimport { MCP_APP_CHAT_BRIDGE_QUERY_PARAM } from \"../shared/embed-auth.js\";\n\nconst MCP_APP_IMPORT =\n \"https://esm.sh/@modelcontextprotocol/ext-apps@1.7.2/app-with-deps\";\n\nexport const MCP_APP_REQUEST_ORIGIN_CSP_SOURCE = \"$requestOrigin\";\nconst MCP_APP_WRAPPER_CHROME_HEIGHT = 44;\nexport const DEFAULT_MCP_APP_VIEWPORT_HEIGHT = 720;\nexport const DEFAULT_MCP_APP_SHELL_HEIGHT =\n DEFAULT_MCP_APP_VIEWPORT_HEIGHT + MCP_APP_WRAPPER_CHROME_HEIGHT;\n\nexport interface EmbedAppOptions {\n title?: string;\n description?: string;\n iframeTitle?: string;\n openLabel?: string;\n embedByDefault?: boolean;\n startToolName?: string;\n frameDomains?: string[];\n height?: number;\n}\n\nfunction attr(value: string | undefined): string {\n return String(value ?? \"\")\n .replace(/&/g, \"&amp;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\");\n}\n\nexport function embedApp(\n options: EmbedAppOptions = {},\n): ActionMcpAppResourceConfig {\n const title = options.title ?? \"Open app\";\n const iframeTitle = options.iframeTitle ?? \"Agent Native app\";\n const openLabel = options.openLabel ?? \"Open in app\";\n const startToolName = options.startToolName ?? \"create_embed_session\";\n const embedByDefault = options.embedByDefault !== false;\n const height = Math.max(\n 320,\n Math.min(900, options.height ?? DEFAULT_MCP_APP_SHELL_HEIGHT),\n );\n const viewportHeight = height - MCP_APP_WRAPPER_CHROME_HEIGHT;\n\n return {\n title,\n ...(options.description ? { description: options.description } : {}),\n html: () => `<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <style>\n :root { color-scheme: light dark; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; background: Canvas; color: CanvasText; }\n * { box-sizing: border-box; }\n body { margin: 0; }\n .shell { display: grid; gap: 8px; min-height: ${height}px; padding: 0; }\n .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); }\n .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); }\n .actions { display: flex; align-items: center; gap: 6px; }\n 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; }\n button:disabled { opacity: .55; cursor: default; }\n .stage { position: relative; min-height: ${viewportHeight}px; }\n iframe { display: block; width: 100%; height: ${viewportHeight}px; border: 0; background: Canvas; }\n .message { display: grid; place-items: center; min-height: ${viewportHeight}px; padding: 18px; color: color-mix(in srgb, CanvasText 62%, Canvas); font-size: 13px; line-height: 1.45; text-align: center; }\n .fallback { display: grid; align-content: center; justify-items: center; gap: 12px; min-height: ${viewportHeight}px; padding: 24px; background: Canvas; color: CanvasText; text-align: center; }\n .fallback-title { max-width: 440px; font-size: 14px; font-weight: 700; }\n .fallback-copy { max-width: 520px; color: color-mix(in srgb, CanvasText 64%, Canvas); font-size: 13px; line-height: 1.45; }\n .fallback-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 8px; }\n .fallback-url { max-width: min(560px, 100%); overflow-wrap: anywhere; color: color-mix(in srgb, CanvasText 76%, Canvas); font-size: 12px; }\n </style>\n</head>\n<body\n data-app-title=\"${attr(title)}\"\n data-iframe-title=\"${attr(iframeTitle)}\"\n data-open-label=\"${attr(openLabel)}\"\n data-start-tool=\"${attr(startToolName)}\"\n data-embed-default=\"${embedByDefault ? \"1\" : \"0\"}\"\n>\n <main class=\"shell\">\n <div class=\"bar\">\n <div class=\"title\" data-title-label>${attr(title)}</div>\n <div class=\"actions\">\n <button type=\"button\" data-display hidden disabled>Fullscreen</button>\n <button type=\"button\" data-open disabled>${attr(openLabel)}</button>\n </div>\n </div>\n <section class=\"stage\" data-stage>\n <div class=\"message\">Preparing app</div>\n </section>\n </main>\n <script type=\"module\">\n const body = document.body;\n const stage = document.querySelector(\"[data-stage]\");\n const titleEl = document.querySelector(\"[data-title-label]\");\n const openButton = document.querySelector(\"[data-open]\");\n const displayButton = document.querySelector(\"[data-display]\");\n const startTool = body.dataset.startTool || \"create_embed_session\";\n const embedByDefault = body.dataset.embedDefault !== \"0\";\n const chatBridgeParam = ${JSON.stringify(MCP_APP_CHAT_BRIDGE_QUERY_PARAM)};\n const intrinsicHeight = ${height};\n let app = null;\n let openAiBridge = null;\n let toolInput = {};\n let openUrl = \"\";\n let startedFor = \"\";\n let appFrame = null;\n let appFrameReady = false;\n let appFrameReadyTimer = null;\n let appFrameLoadTimer = null;\n let lastFrameSrc = \"\";\n\n function esc(value) {\n return String(value ?? \"\")\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\");\n }\n\n function parseJson(value, fallback) {\n if (value && typeof value === \"object\") return value;\n if (typeof value !== \"string\" || !value.trim()) return fallback;\n try { return JSON.parse(value); } catch { return fallback; }\n }\n\n function objectValue(value) {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? value\n : {};\n }\n\n function parseToolResult(params) {\n if (!params) return {};\n if (params.result && typeof params.result === \"object\") {\n return parseToolResult(params.result);\n }\n if (params.toolResult && typeof params.toolResult === \"object\") {\n return parseToolResult(params.toolResult);\n }\n if (params.structuredContent && typeof params.structuredContent === \"object\") {\n return params.structuredContent;\n }\n const parts = Array.isArray(params.content) ? params.content : [];\n const textPart = parts.find((part) => part && part.type === \"text\" && typeof part.text === \"string\");\n return parseJson(textPart ? textPart.text : \"\", {});\n }\n\n function openLinkFrom(params, data) {\n const metaUrl = params && params._meta && params._meta[\"agent-native/openLink\"]\n ? params._meta[\"agent-native/openLink\"].webUrl\n : \"\";\n return metaUrl || data.url || data.deepLink || data.openUrl || \"\";\n }\n\n function hostState() {\n if (openAiBridge) {\n return {\n context: {\n displayMode: openAiBridge.displayMode,\n availableDisplayModes: typeof openAiBridge.requestDisplayMode === \"function\"\n ? [\"inline\", \"fullscreen\", \"pip\"]\n : [],\n maxHeight: openAiBridge.maxHeight,\n locale: openAiBridge.locale,\n theme: openAiBridge.theme,\n view: openAiBridge.view\n },\n capabilities: { openai: true },\n version: openAiBridge.userAgent\n };\n }\n return {\n context: app && app.getHostContext ? app.getHostContext() : undefined,\n capabilities: app && app.getHostCapabilities ? app.getHostCapabilities() : undefined,\n version: app && app.getHostVersion ? app.getHostVersion() : undefined\n };\n }\n\n function sendToAppFrame(message) {\n if (!appFrame || !appFrame.contentWindow) return;\n try { appFrame.contentWindow.postMessage(message, \"*\"); } catch {}\n }\n\n function sendHostContext() {\n sendToAppFrame({ type: \"agentNative.mcpHostContext\", data: hostState() });\n }\n\n function sendFrameReadyMessages(frame) {\n const originPayload = { type: \"agentNative.frameOrigin\", origin: window.location.origin };\n [0, 200, 500, 1500].forEach((delay) => {\n setTimeout(() => {\n try { frame.contentWindow && frame.contentWindow.postMessage(originPayload, \"*\"); } catch {}\n sendHostContext();\n }, delay);\n });\n }\n\n function withChatBridgeParam(value) {\n if (typeof value !== \"string\" || !value) return value;\n try {\n const base = \"http://agent-native.invalid\";\n const url = value.startsWith(\"/\") ? new URL(value, base) : new URL(value);\n url.searchParams.set(chatBridgeParam, \"1\");\n return value.startsWith(\"/\")\n ? url.pathname + url.search + url.hash\n : url.toString();\n } catch {\n return value;\n }\n }\n\n function wantsEmbed() {\n if (toolInput.embed === false || toolInput.embed === \"false\") return false;\n if (embedByDefault) return true;\n return toolInput.embed === true || toolInput.embed === \"true\";\n }\n\n function supportedDisplayMode(mode) {\n if (openAiBridge && typeof openAiBridge.requestDisplayMode === \"function\") {\n return mode === \"inline\" || mode === \"fullscreen\" || mode === \"pip\";\n }\n const modes = hostState().context && hostState().context.availableDisplayModes;\n return Array.isArray(modes) && modes.includes(mode);\n }\n\n async function requestHostDisplayMode(mode) {\n let result;\n if (openAiBridge && typeof openAiBridge.requestDisplayMode === \"function\") {\n result = await openAiBridge.requestDisplayMode({ mode });\n } else {\n if (!app || typeof app.requestDisplayMode !== \"function\") {\n throw new Error(\"Display mode changes are not available in this host.\");\n }\n result = await app.requestDisplayMode({ mode });\n }\n updateDisplayButton();\n sendHostContext();\n return result;\n }\n\n function updateDisplayButton() {\n const context = hostState().context || {};\n const nextMode = context.displayMode === \"fullscreen\" ? \"inline\" : \"fullscreen\";\n const supported = supportedDisplayMode(nextMode);\n displayButton.hidden = !supported;\n displayButton.disabled = !supported;\n displayButton.textContent = nextMode === \"fullscreen\" ? \"Fullscreen\" : \"Inline\";\n displayButton.onclick = () => {\n if (!supportedDisplayMode(nextMode)) return;\n void requestHostDisplayMode(nextMode).catch((err) => {\n console.warn(\"[agent-native] MCP host rejected display mode request\", err);\n });\n };\n }\n\n function setMessage(message) {\n stage.innerHTML = '<div class=\"message\">' + esc(message) + '</div>';\n }\n\n function clearFrameReadyTimer() {\n if (!appFrameReadyTimer) return;\n clearTimeout(appFrameReadyTimer);\n appFrameReadyTimer = null;\n }\n\n function clearFrameLoadTimer() {\n if (!appFrameLoadTimer) return;\n clearTimeout(appFrameLoadTimer);\n appFrameLoadTimer = null;\n }\n\n function startFrameReadyTimer(frame) {\n clearFrameReadyTimer();\n appFrameReadyTimer = setTimeout(() => {\n if (!appFrameReady && appFrame === frame) renderFrameFallback();\n }, 7000);\n }\n\n function renderFrameFallback() {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n appFrame = null;\n stage.innerHTML =\n '<div class=\"fallback\">' +\n '<div class=\"fallback-title\">Open this app in its own tab</div>' +\n '<div class=\"fallback-copy\">This chat host did not allow the embedded app frame to load inline. You can still open the same app route through the host or use the URL below.</div>' +\n '<div class=\"fallback-actions\">' +\n '<button type=\"button\" data-fallback-open>Open app</button>' +\n '<button type=\"button\" data-fallback-retry>Try inline again</button>' +\n '</div>' +\n (openUrl ? '<a class=\"fallback-url\" href=\"' + esc(openUrl) + '\" target=\"_blank\" rel=\"noreferrer\">' + esc(openUrl) + '</a>' : '') +\n '</div>';\n const fallbackOpen = stage.querySelector(\"[data-fallback-open]\");\n const fallbackRetry = stage.querySelector(\"[data-fallback-retry]\");\n if (fallbackOpen) {\n fallbackOpen.disabled = !openUrl;\n fallbackOpen.onclick = () => {\n if (openUrl) void openFallbackExternal();\n };\n }\n if (fallbackRetry) {\n fallbackRetry.disabled = !lastFrameSrc;\n fallbackRetry.onclick = () => {\n if (lastFrameSrc) renderFrame(lastFrameSrc);\n };\n }\n }\n\n async function openFallbackExternal() {\n let url = openUrl;\n try {\n const embedUrl = withChatBridgeParam(openUrl);\n const result = await callEmbedSessionTool({\n url: embedUrl,\n chrome: typeof toolInput.chrome === \"string\" ? toolInput.chrome : \"full\"\n });\n const data = parseToolResult(result);\n if (typeof data.startUrl === \"string\" && data.startUrl) {\n url = data.startUrl;\n }\n } catch (err) {\n console.warn(\"[agent-native] MCP fallback could not mint a fresh app session\", err);\n }\n await openHostLink({ url });\n }\n\n function renderFrame(src) {\n clearFrameReadyTimer();\n clearFrameLoadTimer();\n const frame = document.createElement(\"iframe\");\n frame.title = body.dataset.iframeTitle || \"Agent Native app\";\n frame.src = src;\n frame.allow = \"clipboard-read; clipboard-write\";\n appFrame = frame;\n appFrameReady = false;\n lastFrameSrc = src;\n frame.addEventListener(\"load\", () => {\n if (appFrame !== frame) return;\n clearFrameLoadTimer();\n sendFrameReadyMessages(frame);\n startFrameReadyTimer(frame);\n });\n stage.replaceChildren(frame);\n notifyHostHeight();\n appFrameLoadTimer = setTimeout(() => {\n if (!appFrameReady && appFrame === frame) renderFrameFallback();\n }, 30000);\n }\n\n async function updateHostModelContext(data) {\n const params = {};\n if (Array.isArray(data && data.content)) params.content = data.content;\n if (data && data.structuredContent && typeof data.structuredContent === \"object\") {\n params.structuredContent = data.structuredContent;\n }\n if (openAiBridge && typeof openAiBridge.setWidgetState === \"function\") {\n openAiBridge.setWidgetState({\n ...objectValue(openAiBridge.widgetState),\n agentNativeModelContext: params\n });\n return { ok: true };\n }\n if (!app || typeof app.updateModelContext !== \"function\") return { ok: false };\n await app.updateModelContext(params);\n return { ok: true };\n }\n\n async function openHostLink(data) {\n const url = typeof (data && data.url) === \"string\" ? data.url : \"\";\n if (!url) return { isError: true };\n if (openAiBridge && typeof openAiBridge.openExternal === \"function\") {\n return await openAiBridge.openExternal({ href: url, redirectUrl: false });\n }\n if (app && typeof app.openLink === \"function\") {\n return await app.openLink({ url });\n }\n window.open(url, \"_blank\", \"noopener,noreferrer\");\n return { ok: true };\n }\n\n function notifyHostHeight() {\n if (!openAiBridge || typeof openAiBridge.notifyIntrinsicHeight !== \"function\") {\n return;\n }\n try {\n openAiBridge.notifyIntrinsicHeight({ height: intrinsicHeight });\n } catch (err) {\n console.warn(\"[agent-native] ChatGPT rejected intrinsic height update\", err);\n }\n }\n\n function respondToAppFrame(requestId, work) {\n if (!requestId) return;\n Promise.resolve(work)\n .then((result) => {\n sendToAppFrame({\n type: \"agentNative.mcpHost.response\",\n data: { requestId, ok: true, result }\n });\n })\n .catch((err) => {\n sendToAppFrame({\n type: \"agentNative.mcpHost.response\",\n data: {\n requestId,\n ok: false,\n error: err && err.message ? err.message : String(err)\n }\n });\n });\n }\n\n async function sendHostChat(chat) {\n if (!chat || chat.submit === false) return;\n const message = typeof chat.message === \"string\" ? chat.message : \"\";\n if (!message.trim()) return;\n const context = typeof chat.context === \"string\" ? chat.context : \"\";\n if (context.trim()) {\n try {\n if (openAiBridge && typeof openAiBridge.setWidgetState === \"function\") {\n openAiBridge.setWidgetState({\n ...objectValue(openAiBridge.widgetState),\n agentNativeChatContext: context\n });\n } else if (app && typeof app.updateModelContext === \"function\") {\n await app.updateModelContext({\n content: [{ type: \"text\", text: context }]\n });\n }\n } catch (err) {\n console.warn(\"[agent-native] MCP host rejected model context update\", err);\n }\n }\n try {\n if (openAiBridge && typeof openAiBridge.sendFollowUpMessage === \"function\") {\n await openAiBridge.sendFollowUpMessage({\n prompt: context.trim() ? context.trim() + \"\\\\n\\\\n\" + message : message,\n scrollToBottom: true\n });\n return;\n }\n if (!app || typeof app.sendMessage !== \"function\") return;\n const result = await app.sendMessage({\n role: \"user\",\n content: [{ type: \"text\", text: message }]\n });\n if (result && result.isError) {\n console.warn(\"[agent-native] MCP host rejected chat message\", result);\n }\n } catch (err) {\n console.warn(\"[agent-native] MCP host chat bridge failed\", err);\n }\n }\n\n window.addEventListener(\"message\", (event) => {\n if (!appFrame || event.source !== appFrame.contentWindow) return;\n if (!event.data) return;\n const data = event.data.data || {};\n if (event.data.type === \"agentNative.embeddedAppReady\") {\n appFrameReady = true;\n clearFrameLoadTimer();\n clearFrameReadyTimer();\n return;\n }\n if (event.data.type === \"agentNative.submitChat\") {\n void sendHostChat(data);\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.updateModelContext\") {\n respondToAppFrame(data.requestId, updateHostModelContext(data));\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.openLink\") {\n respondToAppFrame(data.requestId, openHostLink(data));\n return;\n }\n if (event.data.type === \"agentNative.mcpHost.requestDisplayMode\") {\n respondToAppFrame(data.requestId, requestHostDisplayMode(data.mode));\n }\n });\n\n async function launchEmbed() {\n if (!openUrl) {\n setMessage(\"Open link was not available.\");\n return;\n }\n if (!wantsEmbed()) {\n setMessage(\"Ready to open.\");\n return;\n }\n if (startedFor === openUrl) return;\n startedFor = openUrl;\n setMessage(\"Loading app\");\n try {\n const embedUrl = withChatBridgeParam(openUrl);\n const result = await callEmbedSessionTool({\n url: embedUrl,\n chrome: typeof toolInput.chrome === \"string\" ? toolInput.chrome : \"full\"\n });\n const data = parseToolResult(result);\n if (!data.startUrl) {\n startedFor = \"\";\n setMessage(data.error || \"This app can be opened, but not embedded from this MCP server.\");\n return;\n }\n renderFrame(data.startUrl);\n } catch (err) {\n startedFor = \"\";\n setMessage(err && err.message ? err.message : \"Could not launch embedded app.\");\n }\n }\n\n async function callEmbedSessionTool(args) {\n if (openAiBridge && typeof openAiBridge.callTool === \"function\") {\n return await openAiBridge.callTool(startTool, args);\n }\n if (!app || typeof app.callServerTool !== \"function\") {\n throw new Error(\"Host tool calls are not available.\");\n }\n return await app.callServerTool({ name: startTool, arguments: args });\n }\n\n function updateHostOpenInAppUrl() {\n if (!openAiBridge || !openUrl || typeof openAiBridge.setOpenInAppUrl !== \"function\") {\n return;\n }\n try {\n openAiBridge.setOpenInAppUrl({ href: openUrl });\n } catch (err) {\n console.warn(\"[agent-native] ChatGPT rejected open-in-app URL\", err);\n }\n }\n\n function updateOpenButton() {\n openButton.disabled = !openUrl;\n openButton.onclick = () => {\n if (openUrl) void openHostLink({ url: openUrl });\n };\n updateHostOpenInAppUrl();\n }\n\n function updateTitle(data) {\n const label = data.label || data.app || data.view || body.dataset.appTitle || \"App\";\n titleEl.textContent = String(label);\n }\n\n function readOpenAiBridge() {\n return window.openai && typeof window.openai === \"object\"\n ? window.openai\n : null;\n }\n\n function openAiToolResultParams(bridge) {\n const params = {};\n if (bridge && bridge.toolOutput !== undefined) {\n if (bridge.toolOutput && typeof bridge.toolOutput === \"object\") {\n params.structuredContent = bridge.toolOutput;\n } else {\n params.content = [{ type: \"text\", text: String(bridge.toolOutput) }];\n }\n }\n if (bridge && bridge.toolResponseMetadata && typeof bridge.toolResponseMetadata === \"object\") {\n params._meta = bridge.toolResponseMetadata;\n }\n return params;\n }\n\n function syncOpenAiBridge(bridge) {\n if (!bridge) return false;\n openAiBridge = bridge;\n toolInput = objectValue(bridge.toolInput);\n const params = openAiToolResultParams(bridge);\n const data = parseToolResult(params);\n openUrl = openLinkFrom(params, data);\n updateTitle(data);\n updateOpenButton();\n updateDisplayButton();\n notifyHostHeight();\n sendHostContext();\n if (openUrl) {\n void launchEmbed();\n } else if (!appFrame) {\n setMessage(\"Waiting for app result\");\n }\n return true;\n }\n\n function waitForOpenAiBridge() {\n const existing = readOpenAiBridge();\n if (existing) return Promise.resolve(existing);\n return new Promise((resolve) => {\n let settled = false;\n const finish = (bridge) => {\n if (settled) return;\n settled = true;\n window.removeEventListener(\"openai:set_globals\", onGlobals);\n clearTimeout(timer);\n resolve(bridge || readOpenAiBridge());\n };\n const onGlobals = () => finish(readOpenAiBridge());\n const timer = setTimeout(() => finish(null), 200);\n window.addEventListener(\"openai:set_globals\", onGlobals, { passive: true });\n });\n }\n\n window.addEventListener(\"openai:set_globals\", () => {\n const bridge = readOpenAiBridge();\n if (bridge && (!appFrame || openAiBridge)) syncOpenAiBridge(bridge);\n }, { passive: true });\n\n async function startMcpAppsBridge() {\n const { App } = await import(\"${MCP_APP_IMPORT}\");\n app = new App({ name: \"Agent Native Embed\", version: \"1.0.0\" }, {});\n app.ontoolinput = (params) => {\n toolInput = params.arguments || {};\n };\n app.ontoolresult = (params) => {\n const data = parseToolResult(params);\n openUrl = openLinkFrom(params, data);\n updateTitle(data);\n updateOpenButton();\n void launchEmbed();\n };\n app.onhostcontextchanged = () => {\n updateDisplayButton();\n sendHostContext();\n };\n await app.connect();\n updateDisplayButton();\n sendHostContext();\n }\n\n const initialOpenAiBridge = await waitForOpenAiBridge();\n if (!syncOpenAiBridge(initialOpenAiBridge)) {\n await startMcpAppsBridge();\n }\n </script>\n</body>\n</html>`,\n csp: {\n connectDomains: [\"https://esm.sh\"],\n resourceDomains: [\n \"https://esm.sh\",\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...(options.frameDomains ?? []),\n ],\n frameDomains: [\n MCP_APP_REQUEST_ORIGIN_CSP_SOURCE,\n ...(options.frameDomains ?? []),\n ],\n },\n prefersBorder: false,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import crypto from "node:crypto";
2
2
  import { defineEventHandler, getMethod, getQuery, getRequestIP, setResponseHeader, setResponseStatus, getCookie, setCookie, deleteCookie, getHeader, } from "h3";
3
3
  import { EMBED_START_PATH } from "../shared/embed-auth.js";
4
+ import { EMBED_TARGET_HEADER } from "../shared/embed-auth.js";
4
5
  import { resolveEmbedSessionFromRequest } from "./embed-session.js";
5
6
  // In h3 v2, `event.req` IS the web Request — but in Nitro's dev server (srvx
6
7
  // runtime), event.url and event.req share the same underlying URL object.
@@ -764,7 +765,14 @@ function applyCorsHeaders(event) {
764
765
  setResponseHeader(event, "Vary", "Origin");
765
766
  setResponseHeader(event, "Access-Control-Allow-Credentials", "true");
766
767
  setResponseHeader(event, "Access-Control-Allow-Methods", "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS");
767
- setResponseHeader(event, "Access-Control-Allow-Headers", "Content-Type,Authorization,X-Requested-With,X-Request-Source,X-Agent-Native-CSRF");
768
+ setResponseHeader(event, "Access-Control-Allow-Headers", [
769
+ "Content-Type",
770
+ "Authorization",
771
+ "X-Requested-With",
772
+ "X-Request-Source",
773
+ "X-Agent-Native-CSRF",
774
+ EMBED_TARGET_HEADER,
775
+ ].join(","));
768
776
  return { hasOrigin: true, allowed: true };
769
777
  }
770
778
  function createAuthCorsHandler() {