@agent-native/core 0.72.3 → 0.73.0
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +38 -0
- package/corpus/core/docs/content/skills-guide.md +14 -0
- package/corpus/core/docs/content/template-clips.md +7 -1
- package/corpus/core/package.json +6 -6
- package/corpus/core/src/a2a/client.ts +7 -3
- package/corpus/core/src/agent/durable-background.ts +25 -12
- package/corpus/core/src/cli/create.ts +1 -1
- package/corpus/core/src/cli/skills.ts +450 -19
- package/corpus/core/src/client/api-path.ts +28 -0
- package/corpus/core/src/client/embed-auth.ts +26 -0
- package/corpus/core/src/client/mcp-app-host.ts +6 -0
- package/corpus/core/src/client/mcp-apps/McpAppRenderer.tsx +227 -33
- package/corpus/core/src/client/resources/use-mcp-servers.ts +1 -0
- package/corpus/core/src/client/settings/DemoModeSection.tsx +3 -1
- package/corpus/core/src/demo/fetch-interceptor.ts +1 -1
- package/corpus/core/src/embedding/agent.ts +6 -1
- package/corpus/core/src/embedding/bridge.ts +34 -1
- package/corpus/core/src/mcp/build-server.ts +38 -10
- package/corpus/core/src/mcp/embed-app.ts +120 -45
- package/corpus/core/src/mcp/index.ts +5 -0
- package/corpus/core/src/mcp/org-directory.ts +70 -11
- package/corpus/core/src/mcp/server.ts +14 -0
- package/corpus/core/src/mcp-client/config.ts +12 -0
- package/corpus/core/src/mcp-client/index.ts +2 -0
- package/corpus/core/src/mcp-client/manager.ts +268 -9
- package/corpus/core/src/mcp-client/remote-store.ts +154 -0
- package/corpus/core/src/mcp-client/routes.ts +2 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +8 -0
- package/corpus/core/src/server/auth.ts +7 -0
- package/corpus/core/src/server/index.ts +1 -0
- package/corpus/core/src/server/onboarding-html.ts +95 -9
- package/corpus/core/src/shared/mcp-embed-headers.ts +44 -3
- package/corpus/core/src/templates/default/AGENTS.md +4 -0
- package/corpus/core/src/templates/default/package.json +2 -1
- package/corpus/core/src/templates/headless/AGENTS.md +4 -0
- package/corpus/core/src/templates/headless/package.json +1 -0
- package/corpus/core/src/templates/workspace-root/AGENTS.md +4 -0
- package/corpus/core/src/templates/workspace-root/README.md +4 -0
- package/corpus/core/src/templates/workspace-root/package.json +1 -0
- package/corpus/core/src/vite/client.ts +53 -0
- package/corpus/templates/analytics/actions/update-dashboard.ts +3 -4
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +144 -40
- package/corpus/templates/analytics/app/global.css +15 -13
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +9 -32
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +0 -26
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +0 -23
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/pivot.ts +65 -2
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +3 -4
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-charts-now-fill-sparse-template-time-series-suppor.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-rows-now-auto-fit-panels-so-one-two-or-three-chart.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-dashboard-traffic-tables-now-show-top-urls-and-shared-clips-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +5 -0
- package/corpus/templates/analytics/changelog/2026-06-24-first-party-template-dashboards-now-include-repeat-users-ret.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-24-retention-charts-now-break-out-1-day-and-7-day-return-rates-.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +256 -44
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +7 -2
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +35 -8
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +287 -42
- package/corpus/templates/assets/.agents/skills/a2a-assets/SKILL.md +17 -10
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +15 -6
- package/corpus/templates/assets/AGENTS.md +7 -5
- package/corpus/templates/assets/actions/generate-asset.ts +161 -0
- package/corpus/templates/assets/actions/list-assets.ts +4 -2
- package/corpus/templates/assets/actions/open-asset-picker.ts +84 -3
- package/corpus/templates/assets/app/routes/library.tsx +243 -40
- package/corpus/templates/calendar/changelog/2026-06-23-added-a-heads-up-explaining-google-s-app-not-verified-screen.md +1 -1
- package/corpus/templates/calendar/server/plugins/auth.ts +12 -0
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +7 -7
- package/corpus/templates/clips/AGENTS.md +6 -2
- package/corpus/templates/clips/app/components/capture-install-options.tsx +20 -4
- package/corpus/templates/clips/app/components/player/player-controls.tsx +62 -32
- package/corpus/templates/clips/app/lib/capture-install-options.ts +39 -5
- package/corpus/templates/clips/app/routes/download.tsx +2 -2
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -1
- package/corpus/templates/clips/changelog/2026-06-24-inline-slack-playback-controls-no-longer-shift-when-adjustin.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-console-log-stream-all-le.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-shared-clips-now-expose-their-full-fetch-xhr-network-request.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-24-the-chrome-extension-option-now-only-appears-on-supported-hosts.md +5 -0
- package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +13 -8
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/scripts/package.ts +15 -2
- package/corpus/templates/clips/chrome-extension/src/overlay.css +4 -25
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +1 -3
- package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +9 -0
- package/corpus/templates/clips/server/lib/public-agent-context.ts +67 -24
- package/corpus/templates/design/AGENTS.md +10 -5
- package/corpus/templates/design/actions/connect-assets-mcp.ts +131 -0
- package/corpus/templates/design/actions/insert-asset.ts +219 -0
- package/corpus/templates/design/actions/view-screen.ts +3 -1
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +1 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +53 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +5 -1
- package/corpus/templates/design/server/plugins/assets-mcp.ts +60 -0
- package/corpus/templates/mail/server/plugins/auth.ts +4 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +271 -197
- package/corpus/templates/plan/server/plan-content.ts +45 -2
- package/dist/a2a/client.d.ts +1 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +5 -3
- package/dist/a2a/client.js.map +1 -1
- package/dist/agent/durable-background.d.ts +9 -4
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +24 -11
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/cli/create.js +1 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +361 -19
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/api-path.d.ts.map +1 -1
- package/dist/client/api-path.js +27 -0
- package/dist/client/api-path.js.map +1 -1
- package/dist/client/embed-auth.d.ts.map +1 -1
- package/dist/client/embed-auth.js +27 -0
- package/dist/client/embed-auth.js.map +1 -1
- package/dist/client/mcp-app-host.d.ts.map +1 -1
- package/dist/client/mcp-app-host.js +5 -0
- package/dist/client/mcp-app-host.js.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.d.ts +1 -0
- package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.js +179 -33
- package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/client/resources/use-mcp-servers.d.ts +1 -0
- package/dist/client/resources/use-mcp-servers.d.ts.map +1 -1
- package/dist/client/resources/use-mcp-servers.js.map +1 -1
- package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
- package/dist/client/settings/DemoModeSection.js +3 -1
- package/dist/client/settings/DemoModeSection.js.map +1 -1
- package/dist/demo/fetch-interceptor.js +1 -1
- package/dist/demo/fetch-interceptor.js.map +1 -1
- package/dist/embedding/agent.d.ts +1 -1
- package/dist/embedding/agent.d.ts.map +1 -1
- package/dist/embedding/agent.js +1 -1
- package/dist/embedding/agent.js.map +1 -1
- package/dist/embedding/bridge.d.ts.map +1 -1
- package/dist/embedding/bridge.js +27 -1
- package/dist/embedding/bridge.js.map +1 -1
- package/dist/extensions/schema.d.ts +2 -2
- package/dist/mcp/build-server.d.ts +2 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +32 -9
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +120 -45
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/org-directory.d.ts +1 -0
- package/dist/mcp/org-directory.d.ts.map +1 -1
- package/dist/mcp/org-directory.js +59 -11
- package/dist/mcp/org-directory.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +9 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp-client/config.d.ts +12 -0
- package/dist/mcp-client/config.d.ts.map +1 -1
- package/dist/mcp-client/config.js.map +1 -1
- package/dist/mcp-client/index.d.ts +1 -1
- package/dist/mcp-client/index.d.ts.map +1 -1
- package/dist/mcp-client/index.js +1 -1
- package/dist/mcp-client/index.js.map +1 -1
- package/dist/mcp-client/manager.d.ts +2 -0
- package/dist/mcp-client/manager.d.ts.map +1 -1
- package/dist/mcp-client/manager.js +201 -9
- package/dist/mcp-client/manager.js.map +1 -1
- package/dist/mcp-client/remote-store.d.ts +27 -0
- package/dist/mcp-client/remote-store.d.ts.map +1 -1
- package/dist/mcp-client/remote-store.js +105 -0
- package/dist/mcp-client/remote-store.js.map +1 -1
- package/dist/mcp-client/routes.d.ts +1 -0
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +1 -0
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/provider-api/actions/github-repo-files.d.ts +2 -2
- package/dist/provider-api/actions/query-staged-dataset.d.ts +3 -3
- package/dist/provider-api/corpus-jobs.d.ts +8 -8
- package/dist/server/agent-chat-plugin.d.ts +2 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +8 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth.d.ts +7 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +1 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/onboarding-html.d.ts +15 -0
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +73 -9
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +3 -1
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +40 -3
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/sharing/actions/set-resource-visibility.d.ts +2 -2
- package/dist/sharing/actions/share-resource.d.ts +1 -1
- package/dist/sharing/schema.d.ts +1 -1
- package/dist/templates/default/AGENTS.md +4 -0
- package/dist/templates/default/package.json +2 -1
- package/dist/templates/headless/AGENTS.md +4 -0
- package/dist/templates/headless/package.json +1 -0
- package/dist/templates/workspace-root/AGENTS.md +4 -0
- package/dist/templates/workspace-root/README.md +4 -0
- package/dist/templates/workspace-root/package.json +1 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +44 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/skills-guide.md +14 -0
- package/docs/content/template-clips.md +7 -1
- package/package.json +6 -6
- package/src/templates/default/AGENTS.md +4 -0
- package/src/templates/default/package.json +2 -1
- package/src/templates/headless/AGENTS.md +4 -0
- package/src/templates/headless/package.json +1 -0
- package/src/templates/workspace-root/AGENTS.md +4 -0
- package/src/templates/workspace-root/README.md +4 -0
- package/src/templates/workspace-root/package.json +1 -0
|
@@ -114,6 +114,7 @@ export function embedApp(
|
|
|
114
114
|
const frameReadyMessageDelays = [0, 200, 500, 1500, 3000, 7000, 15000, 30000];
|
|
115
115
|
const frameReadyTimeoutMs = 45000;
|
|
116
116
|
const frameLoadTimeoutMs = 45000;
|
|
117
|
+
const maxEmbedSessionRefreshAttempts = 2;
|
|
117
118
|
const defaultOpenAiBridgeWaitMs = 200;
|
|
118
119
|
const chatGptOpenAiBridgeWaitMs = 5000;
|
|
119
120
|
const openAiBridgePollMs = 50;
|
|
@@ -121,6 +122,7 @@ export function embedApp(
|
|
|
121
122
|
const nativeBridgeRequestTimeoutMs = 30000;
|
|
122
123
|
const wrapperRequestTimeoutMs = 5000;
|
|
123
124
|
let app = null;
|
|
125
|
+
let appConnectPromise = null;
|
|
124
126
|
let openAiBridge = null;
|
|
125
127
|
let wrapperRequestId = 0;
|
|
126
128
|
const wrapperRequests = new Map();
|
|
@@ -134,6 +136,7 @@ export function embedApp(
|
|
|
134
136
|
let appFrameReadyTimer = null;
|
|
135
137
|
let appFrameLoadTimer = null;
|
|
136
138
|
let lastFrameSrc = "";
|
|
139
|
+
let embedSessionRefreshAttempts = 0;
|
|
137
140
|
|
|
138
141
|
function esc(value) {
|
|
139
142
|
return String(value ?? "")
|
|
@@ -269,9 +272,9 @@ export function embedApp(
|
|
|
269
272
|
record.deepLinkUrl,
|
|
270
273
|
record.deepLink,
|
|
271
274
|
record.openUrl,
|
|
272
|
-
record.url,
|
|
273
275
|
structuredOpenLinkUrl,
|
|
274
|
-
metaUrl
|
|
276
|
+
metaUrl,
|
|
277
|
+
record.url
|
|
275
278
|
]);
|
|
276
279
|
}
|
|
277
280
|
|
|
@@ -324,6 +327,12 @@ export function embedApp(
|
|
|
324
327
|
try { appFrame.contentWindow.postMessage(message, "*"); } catch {}
|
|
325
328
|
}
|
|
326
329
|
|
|
330
|
+
function notifyOuterMcpAppReady() {
|
|
331
|
+
try {
|
|
332
|
+
window.parent.postMessage({ type: "agentNative.embeddedAppReady" }, "*");
|
|
333
|
+
} catch {}
|
|
334
|
+
}
|
|
335
|
+
|
|
327
336
|
function nextWrapperRequestId() {
|
|
328
337
|
wrapperRequestId += 1;
|
|
329
338
|
return "mcp-wrapper-" + Date.now() + "-" + wrapperRequestId;
|
|
@@ -640,6 +649,21 @@ export function embedApp(
|
|
|
640
649
|
);
|
|
641
650
|
}
|
|
642
651
|
|
|
652
|
+
function importHeadChildrenWithBase(source, baseHref) {
|
|
653
|
+
const base = document.createElement("base");
|
|
654
|
+
base.href = baseHref;
|
|
655
|
+
const nodes = Array.from(source.childNodes).filter((node) => {
|
|
656
|
+
return !(
|
|
657
|
+
node.nodeType === 1 &&
|
|
658
|
+
String(node.nodeName || "").toLowerCase() === "base"
|
|
659
|
+
);
|
|
660
|
+
});
|
|
661
|
+
document.head.replaceChildren(
|
|
662
|
+
base,
|
|
663
|
+
...nodes.map((node) => document.importNode(node, true))
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
|
|
643
667
|
function isModuleScript(script) {
|
|
644
668
|
return (script.getAttribute("type") || "").trim().toLowerCase() === "module";
|
|
645
669
|
}
|
|
@@ -835,10 +859,7 @@ export function embedApp(
|
|
|
835
859
|
);
|
|
836
860
|
const scripts = Array.from(parsed.querySelectorAll("script"));
|
|
837
861
|
copyDocumentElementAttributes(parsed.documentElement);
|
|
838
|
-
|
|
839
|
-
const base = document.createElement("base");
|
|
840
|
-
base.href = config.baseHref;
|
|
841
|
-
document.head.prepend(base);
|
|
862
|
+
importHeadChildrenWithBase(parsed.head, config.baseHref);
|
|
842
863
|
importChildren(parsed.body, document.body);
|
|
843
864
|
installExternalOpenControl(appUrl);
|
|
844
865
|
for (const script of scripts) {
|
|
@@ -920,6 +941,7 @@ export function embedApp(
|
|
|
920
941
|
window.history.replaceState(window.history.state, "", localPathFromUrl(appUrl, false));
|
|
921
942
|
} catch {}
|
|
922
943
|
await mountTransplantedHtml(html, appUrl);
|
|
944
|
+
embedSessionRefreshAttempts = 0;
|
|
923
945
|
notifyHostHeightRepeatedly();
|
|
924
946
|
}
|
|
925
947
|
|
|
@@ -1159,6 +1181,7 @@ export function embedApp(
|
|
|
1159
1181
|
frame.addEventListener("load", () => {
|
|
1160
1182
|
if (appFrame !== frame) return;
|
|
1161
1183
|
clearFrameLoadTimer();
|
|
1184
|
+
notifyOuterMcpAppReady();
|
|
1162
1185
|
sendFrameReadyMessages(frame);
|
|
1163
1186
|
startFrameReadyTimer(frame);
|
|
1164
1187
|
});
|
|
@@ -1177,6 +1200,13 @@ export function embedApp(
|
|
|
1177
1200
|
renderFrameFallback();
|
|
1178
1201
|
return;
|
|
1179
1202
|
}
|
|
1203
|
+
if (embedSessionRefreshAttempts >= maxEmbedSessionRefreshAttempts) {
|
|
1204
|
+
renderAppLaunchError(
|
|
1205
|
+
"Embedded app session expired. Reopen the app to mint a fresh session."
|
|
1206
|
+
);
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
embedSessionRefreshAttempts += 1;
|
|
1180
1210
|
openStartUrl = "";
|
|
1181
1211
|
startedFor = "";
|
|
1182
1212
|
lastFrameSrc = "";
|
|
@@ -1193,19 +1223,6 @@ export function embedApp(
|
|
|
1193
1223
|
return true;
|
|
1194
1224
|
}
|
|
1195
1225
|
|
|
1196
|
-
// We have connected to a standards-track MCP Apps host (Codex, Cursor,
|
|
1197
|
-
// Claude over the SDK, our own renderer, …) through the postMessage
|
|
1198
|
-
// \`ui/*\` bridge rather than ChatGPT's \`window.openai\` global. These hosts
|
|
1199
|
-
// render the resource in a strict sandboxed iframe (typically
|
|
1200
|
-
// \`sandbox="allow-scripts"\`, opaque origin). Self-navigating that iframe to
|
|
1201
|
-
// the real app origin tears down the host bridge and loses the opaque-origin
|
|
1202
|
-
// auth context, which shows up as a permanent / flashing loading state.
|
|
1203
|
-
// Transplanting the app document into the shell keeps the bridge alive and
|
|
1204
|
-
// works under the opaque origin via embed-token auth, exactly like Claude.
|
|
1205
|
-
function isNativeMcpAppsBridgeHost() {
|
|
1206
|
-
return !!app && !openAiBridge;
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
1226
|
function shouldTransplantAppDocument() {
|
|
1210
1227
|
const render = renderModeSource();
|
|
1211
1228
|
const mode = render.mode;
|
|
@@ -1215,7 +1232,6 @@ export function embedApp(
|
|
|
1215
1232
|
return (
|
|
1216
1233
|
isClaudeMcpContentHost() ||
|
|
1217
1234
|
isChatGptSandboxHost() ||
|
|
1218
|
-
isNativeMcpAppsBridgeHost() ||
|
|
1219
1235
|
mode === "transplant" ||
|
|
1220
1236
|
render.frame === "transplant"
|
|
1221
1237
|
);
|
|
@@ -1240,10 +1256,29 @@ export function embedApp(
|
|
|
1240
1256
|
}
|
|
1241
1257
|
|
|
1242
1258
|
function shouldRenderControlledAppFrame() {
|
|
1243
|
-
return !!openAiBridge || isChatGptSandboxHost();
|
|
1259
|
+
return !!openAiBridge || !!app || isChatGptSandboxHost();
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
function shouldDirectRenderKnownAppRoute(src) {
|
|
1263
|
+
return !!app && !openAiBridge && !isEmbedStartUrl(src) && !shouldTransplantAppDocument();
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
function isCurrentFrameUrl(src) {
|
|
1267
|
+
try {
|
|
1268
|
+
return new URL(src, window.location.href).href === window.location.href;
|
|
1269
|
+
} catch {
|
|
1270
|
+
return false;
|
|
1271
|
+
}
|
|
1244
1272
|
}
|
|
1245
1273
|
|
|
1246
1274
|
function navigateToAppFrame(src) {
|
|
1275
|
+
if (isCurrentFrameUrl(src)) {
|
|
1276
|
+
clearFrameReadyTimer();
|
|
1277
|
+
clearFrameLoadTimer();
|
|
1278
|
+
setMessage("App opened");
|
|
1279
|
+
notifyHostHeightRepeatedly();
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1247
1282
|
clearFrameReadyTimer();
|
|
1248
1283
|
clearFrameLoadTimer();
|
|
1249
1284
|
appFrame = null;
|
|
@@ -1257,6 +1292,24 @@ export function embedApp(
|
|
|
1257
1292
|
}
|
|
1258
1293
|
}
|
|
1259
1294
|
|
|
1295
|
+
async function ensureHostAppConnected() {
|
|
1296
|
+
if (!app || typeof app.connect !== "function") return;
|
|
1297
|
+
if (!appConnectPromise) {
|
|
1298
|
+
appConnectPromise = Promise.resolve(app.connect())
|
|
1299
|
+
.then(async (value) => {
|
|
1300
|
+
// Let the host process ui/notifications/initialized before follow-up calls.
|
|
1301
|
+
await new Promise((resolve) => window.setTimeout(resolve, 0));
|
|
1302
|
+
await new Promise((resolve) => window.setTimeout(resolve, 0));
|
|
1303
|
+
return value;
|
|
1304
|
+
})
|
|
1305
|
+
.catch((err) => {
|
|
1306
|
+
appConnectPromise = null;
|
|
1307
|
+
throw err;
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
await appConnectPromise;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1260
1313
|
async function updateHostModelContext(data) {
|
|
1261
1314
|
const params = {};
|
|
1262
1315
|
if (Array.isArray(data && data.content)) params.content = data.content;
|
|
@@ -1271,6 +1324,7 @@ export function embedApp(
|
|
|
1271
1324
|
return { ok: true };
|
|
1272
1325
|
}
|
|
1273
1326
|
if (!app || typeof app.updateModelContext !== "function") return { ok: false };
|
|
1327
|
+
await ensureHostAppConnected();
|
|
1274
1328
|
await app.updateModelContext(params);
|
|
1275
1329
|
return { ok: true };
|
|
1276
1330
|
}
|
|
@@ -1356,6 +1410,7 @@ export function embedApp(
|
|
|
1356
1410
|
agentNativeModelContext: modelContext
|
|
1357
1411
|
});
|
|
1358
1412
|
} else if (app && typeof app.updateModelContext === "function") {
|
|
1413
|
+
await ensureHostAppConnected();
|
|
1359
1414
|
await app.updateModelContext(modelContext);
|
|
1360
1415
|
}
|
|
1361
1416
|
} catch (err) {
|
|
@@ -1372,6 +1427,7 @@ export function embedApp(
|
|
|
1372
1427
|
}
|
|
1373
1428
|
let result = null;
|
|
1374
1429
|
if (app && typeof app.sendMessage === "function") {
|
|
1430
|
+
await ensureHostAppConnected();
|
|
1375
1431
|
result = await app.sendMessage({
|
|
1376
1432
|
role: "user",
|
|
1377
1433
|
content
|
|
@@ -1412,8 +1468,10 @@ export function embedApp(
|
|
|
1412
1468
|
const data = event.data.data || {};
|
|
1413
1469
|
if (event.data.type === "agentNative.embeddedAppReady") {
|
|
1414
1470
|
appFrameReady = true;
|
|
1471
|
+
embedSessionRefreshAttempts = 0;
|
|
1415
1472
|
clearFrameLoadTimer();
|
|
1416
1473
|
clearFrameReadyTimer();
|
|
1474
|
+
notifyOuterMcpAppReady();
|
|
1417
1475
|
return;
|
|
1418
1476
|
}
|
|
1419
1477
|
if (event.data.type === "agentNative.embedSessionExpired") {
|
|
@@ -1454,7 +1512,7 @@ export function embedApp(
|
|
|
1454
1512
|
}
|
|
1455
1513
|
|
|
1456
1514
|
async function launchEmbed() {
|
|
1457
|
-
|
|
1515
|
+
let launchUrl = openStartUrl || openUrl;
|
|
1458
1516
|
if (!launchUrl) {
|
|
1459
1517
|
renderAppLaunchError("Open link was not available.");
|
|
1460
1518
|
return;
|
|
@@ -1463,11 +1521,11 @@ export function embedApp(
|
|
|
1463
1521
|
setMessage("Ready to open.");
|
|
1464
1522
|
return;
|
|
1465
1523
|
}
|
|
1466
|
-
if (startedFor === launchUrl) return;
|
|
1467
|
-
startedFor = launchUrl;
|
|
1468
1524
|
setMessage("Loading app");
|
|
1469
1525
|
try {
|
|
1470
1526
|
const selfNavigate = shouldSelfNavigateToApp();
|
|
1527
|
+
if (startedFor === launchUrl) return;
|
|
1528
|
+
startedFor = launchUrl;
|
|
1471
1529
|
const embedUrl = withChatBridgeParam(launchUrl);
|
|
1472
1530
|
if (selfNavigate && isEmbedStartUrl(embedUrl)) {
|
|
1473
1531
|
if (shouldTransplantAppDocument()) {
|
|
@@ -1483,6 +1541,10 @@ export function embedApp(
|
|
|
1483
1541
|
renderFrame(embedUrl);
|
|
1484
1542
|
return;
|
|
1485
1543
|
}
|
|
1544
|
+
if (shouldDirectRenderKnownAppRoute(embedUrl)) {
|
|
1545
|
+
renderFrame(embedUrl);
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1486
1548
|
const result = await callEmbedSessionTool(embedSessionArgsFor(embedUrl));
|
|
1487
1549
|
const data = parseToolResult(result);
|
|
1488
1550
|
if (typeof data.startUrl !== "string" || !data.startUrl) {
|
|
@@ -1640,7 +1702,7 @@ export function embedApp(
|
|
|
1640
1702
|
|
|
1641
1703
|
function createNativeMcpAppsBridge() {
|
|
1642
1704
|
let rpcId = 0;
|
|
1643
|
-
let
|
|
1705
|
+
let connectPromise = null;
|
|
1644
1706
|
let hostContext = {};
|
|
1645
1707
|
const pendingRequests = new Map();
|
|
1646
1708
|
|
|
@@ -1708,26 +1770,32 @@ export function embedApp(
|
|
|
1708
1770
|
return hostContext.protocolVersion || "mcp-apps-postmessage";
|
|
1709
1771
|
},
|
|
1710
1772
|
async connect() {
|
|
1711
|
-
if (
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1773
|
+
if (connectPromise) return await connectPromise;
|
|
1774
|
+
connectPromise = (async () => {
|
|
1775
|
+
window.addEventListener("message", onMessage, { passive: true });
|
|
1776
|
+
const result = await rpcRequest(
|
|
1777
|
+
"ui/initialize",
|
|
1778
|
+
{
|
|
1779
|
+
appInfo: { name: "Agent Native Embed", version: "1.0.0" },
|
|
1780
|
+
appCapabilities: {},
|
|
1781
|
+
protocolVersion: "2026-01-26"
|
|
1782
|
+
},
|
|
1783
|
+
nativeBridgeInitializeTimeoutMs
|
|
1784
|
+
);
|
|
1785
|
+
hostContext = objectValue(result);
|
|
1786
|
+
rpcNotify("ui/notifications/initialized", {});
|
|
1787
|
+
if (typeof nativeApp.onhostcontextchanged === "function") {
|
|
1788
|
+
nativeApp.onhostcontextchanged(hostContext);
|
|
1789
|
+
}
|
|
1790
|
+
return hostContext;
|
|
1791
|
+
})().catch((err) => {
|
|
1792
|
+
connectPromise = null;
|
|
1793
|
+
throw err;
|
|
1794
|
+
});
|
|
1795
|
+
return await connectPromise;
|
|
1729
1796
|
},
|
|
1730
1797
|
async callServerTool(request) {
|
|
1798
|
+
await nativeApp.connect();
|
|
1731
1799
|
const record = objectValue(request);
|
|
1732
1800
|
return await rpcRequest("tools/call", {
|
|
1733
1801
|
name: record.name,
|
|
@@ -1735,6 +1803,7 @@ export function embedApp(
|
|
|
1735
1803
|
});
|
|
1736
1804
|
},
|
|
1737
1805
|
async updateModelContext(params) {
|
|
1806
|
+
await nativeApp.connect();
|
|
1738
1807
|
return await rpcRequest("ui/update-model-context", objectValue(params));
|
|
1739
1808
|
},
|
|
1740
1809
|
async openLink(params) {
|
|
@@ -1749,12 +1818,14 @@ export function embedApp(
|
|
|
1749
1818
|
return opened ? { ok: true } : { isError: true };
|
|
1750
1819
|
},
|
|
1751
1820
|
async requestDisplayMode(params) {
|
|
1821
|
+
await nativeApp.connect();
|
|
1752
1822
|
return await rpcRequest("ui/request-display-mode", objectValue(params));
|
|
1753
1823
|
},
|
|
1754
1824
|
sendSizeChanged(params) {
|
|
1755
1825
|
rpcNotify("ui/notifications/size-changed", objectValue(params));
|
|
1756
1826
|
},
|
|
1757
1827
|
async sendMessage(params) {
|
|
1828
|
+
await nativeApp.connect();
|
|
1758
1829
|
return await rpcRequest("ui/message", objectValue(params));
|
|
1759
1830
|
}
|
|
1760
1831
|
};
|
|
@@ -1798,6 +1869,7 @@ export function embedApp(
|
|
|
1798
1869
|
|
|
1799
1870
|
async function startNativeMcpAppsBridge() {
|
|
1800
1871
|
app = createNativeMcpAppsBridge();
|
|
1872
|
+
appConnectPromise = null;
|
|
1801
1873
|
app.ontoolinput = (params) => {
|
|
1802
1874
|
toolInput = params.arguments || {};
|
|
1803
1875
|
};
|
|
@@ -1815,7 +1887,8 @@ export function embedApp(
|
|
|
1815
1887
|
notifyHostHeight();
|
|
1816
1888
|
sendHostContext();
|
|
1817
1889
|
};
|
|
1818
|
-
await
|
|
1890
|
+
await ensureHostAppConnected();
|
|
1891
|
+
notifyOuterMcpAppReady();
|
|
1819
1892
|
updateDisplayButton();
|
|
1820
1893
|
notifyHostHeight();
|
|
1821
1894
|
sendHostContext();
|
|
@@ -1828,6 +1901,7 @@ export function embedApp(
|
|
|
1828
1901
|
{},
|
|
1829
1902
|
{ autoResize: false }
|
|
1830
1903
|
);
|
|
1904
|
+
appConnectPromise = null;
|
|
1831
1905
|
app.ontoolinput = (params) => {
|
|
1832
1906
|
toolInput = params.arguments || {};
|
|
1833
1907
|
};
|
|
@@ -1845,7 +1919,8 @@ export function embedApp(
|
|
|
1845
1919
|
notifyHostHeight();
|
|
1846
1920
|
sendHostContext();
|
|
1847
1921
|
};
|
|
1848
|
-
await
|
|
1922
|
+
await ensureHostAppConnected();
|
|
1923
|
+
notifyOuterMcpAppReady();
|
|
1849
1924
|
updateDisplayButton();
|
|
1850
1925
|
notifyHostHeight();
|
|
1851
1926
|
sendHostContext();
|
|
@@ -146,6 +146,10 @@ function scopedCacheKey(
|
|
|
146
146
|
].join("|");
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
function serviceScopedCacheKey(origin: string, orgId: string): string {
|
|
150
|
+
return [origin, `service-org:${orgId}`].join("|");
|
|
151
|
+
}
|
|
152
|
+
|
|
149
153
|
/**
|
|
150
154
|
* Fetch the org's first-party sibling apps from the org directory.
|
|
151
155
|
*
|
|
@@ -162,6 +166,7 @@ function scopedCacheKey(
|
|
|
162
166
|
export async function fetchOrgApps(opts?: {
|
|
163
167
|
selfId?: string;
|
|
164
168
|
selfOrigin?: string;
|
|
169
|
+
serviceOrgId?: string;
|
|
165
170
|
env?: NodeJS.ProcessEnv;
|
|
166
171
|
}): Promise<OrgApp[]> {
|
|
167
172
|
const env = opts?.env ?? process.env;
|
|
@@ -182,24 +187,32 @@ export async function fetchOrgApps(opts?: {
|
|
|
182
187
|
let cacheKey: string | null = null;
|
|
183
188
|
let apps: OrgApp[] = [];
|
|
184
189
|
let ttl = EMPTY_TTL_MS;
|
|
190
|
+
const serviceOrgId = opts?.serviceOrgId?.trim();
|
|
191
|
+
if (serviceOrgId) {
|
|
192
|
+
const serviceCacheKey = serviceScopedCacheKey(origin, serviceOrgId);
|
|
193
|
+
const cached = cache.get(serviceCacheKey);
|
|
194
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
195
|
+
return stripSelf(cached.apps);
|
|
196
|
+
}
|
|
197
|
+
cacheKey = serviceCacheKey;
|
|
198
|
+
}
|
|
185
199
|
try {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
// peers already use. No new secret loading is invented here.
|
|
190
|
-
const { resolveA2ACallerAuth } = await import("../a2a/caller-auth.js");
|
|
191
|
-
const auth = await resolveA2ACallerAuth();
|
|
200
|
+
const auth = serviceOrgId
|
|
201
|
+
? await resolveOrgDirectoryServiceAuth(serviceOrgId)
|
|
202
|
+
: await resolveOrgDirectoryCallerAuth();
|
|
192
203
|
if (!auth.apiKey) {
|
|
193
204
|
// No signed token available (no A2A secret / no caller identity) — the
|
|
194
205
|
// directory requires the org bearer, so degrade silently to local-only.
|
|
195
206
|
return [];
|
|
196
207
|
}
|
|
197
208
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
209
|
+
if (!cacheKey) {
|
|
210
|
+
const now = Date.now();
|
|
211
|
+
cacheKey = scopedCacheKey(origin, auth);
|
|
212
|
+
const cached = cache.get(cacheKey);
|
|
213
|
+
if (cached && cached.expiresAt > now) {
|
|
214
|
+
return stripSelf(cached.apps);
|
|
215
|
+
}
|
|
203
216
|
}
|
|
204
217
|
|
|
205
218
|
const res = await fetch(`${origin}/_agent-native/org/apps`, {
|
|
@@ -236,3 +249,49 @@ export async function fetchOrgApps(opts?: {
|
|
|
236
249
|
export function _resetOrgDirectoryCache(): void {
|
|
237
250
|
cache.clear();
|
|
238
251
|
}
|
|
252
|
+
|
|
253
|
+
async function resolveOrgDirectoryCallerAuth(): Promise<{
|
|
254
|
+
apiKey?: string;
|
|
255
|
+
userEmail?: string;
|
|
256
|
+
orgId?: string;
|
|
257
|
+
orgDomain?: string;
|
|
258
|
+
}> {
|
|
259
|
+
// Reuse the existing A2A caller-auth: it reads userEmail + orgId from the
|
|
260
|
+
// request context, loads the org A2A secret via getOrgA2ASecret (falling
|
|
261
|
+
// back to the global A2A_SECRET env), and signs the same bearer JWT A2A
|
|
262
|
+
// peers already use. No new secret loading is invented for normal callers.
|
|
263
|
+
const { resolveA2ACallerAuth } = await import("../a2a/caller-auth.js");
|
|
264
|
+
return resolveA2ACallerAuth();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function resolveOrgDirectoryServiceAuth(orgId: string): Promise<{
|
|
268
|
+
apiKey?: string;
|
|
269
|
+
userEmail?: string;
|
|
270
|
+
orgId?: string;
|
|
271
|
+
orgDomain?: string;
|
|
272
|
+
}> {
|
|
273
|
+
const trimmedOrgId = orgId.trim();
|
|
274
|
+
if (!trimmedOrgId) return {};
|
|
275
|
+
let orgDomain: string | undefined;
|
|
276
|
+
let orgSecret: string | undefined;
|
|
277
|
+
try {
|
|
278
|
+
const { getOrgDomain, getOrgA2ASecret } = await import("../org/context.js");
|
|
279
|
+
orgDomain = (await getOrgDomain(trimmedOrgId)) ?? undefined;
|
|
280
|
+
orgSecret = (await getOrgA2ASecret(trimmedOrgId)) ?? undefined;
|
|
281
|
+
} catch {}
|
|
282
|
+
try {
|
|
283
|
+
const [{ signA2AToken }, { serviceIdentityEmail }] = await Promise.all([
|
|
284
|
+
import("../a2a/client.js"),
|
|
285
|
+
import("./connect-store.js"),
|
|
286
|
+
]);
|
|
287
|
+
const userEmail = serviceIdentityEmail("mcp-client", trimmedOrgId);
|
|
288
|
+
const apiKey = await signA2AToken(userEmail, orgDomain, orgSecret, {
|
|
289
|
+
expiresIn: "5m",
|
|
290
|
+
preferGlobalSecret: !orgSecret,
|
|
291
|
+
extraClaims: { org_id: trimmedOrgId },
|
|
292
|
+
});
|
|
293
|
+
return { apiKey, userEmail, orgId: trimmedOrgId, orgDomain };
|
|
294
|
+
} catch {
|
|
295
|
+
return { orgId: trimmedOrgId, orgDomain };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
@@ -103,6 +103,14 @@ function deriveRequestMeta(event: H3Event): MCPRequestMeta {
|
|
|
103
103
|
fullCatalogHeader === "1" ||
|
|
104
104
|
fullCatalogHeader === "true" ||
|
|
105
105
|
fullCatalogHeader === "yes";
|
|
106
|
+
const inlineAppsHeader = getRequestHeader(
|
|
107
|
+
event,
|
|
108
|
+
"x-agent-native-mcp-inline-apps",
|
|
109
|
+
)?.toLowerCase();
|
|
110
|
+
const inlineAppsRequested =
|
|
111
|
+
inlineAppsHeader === "1" ||
|
|
112
|
+
inlineAppsHeader === "true" ||
|
|
113
|
+
inlineAppsHeader === "yes";
|
|
106
114
|
const basePath = getConfiguredAppBasePath();
|
|
107
115
|
return {
|
|
108
116
|
origin,
|
|
@@ -111,6 +119,7 @@ function deriveRequestMeta(event: H3Event): MCPRequestMeta {
|
|
|
111
119
|
clientName,
|
|
112
120
|
clientHint,
|
|
113
121
|
...(fullCatalog ? { fullCatalog } : {}),
|
|
122
|
+
...(inlineAppsRequested ? { inlineMcpApps: true } : {}),
|
|
114
123
|
};
|
|
115
124
|
}
|
|
116
125
|
|
|
@@ -357,6 +366,11 @@ export async function handleMcpRequest(
|
|
|
357
366
|
const server = await createMCPServerForRequest(config, authResult.identity, {
|
|
358
367
|
...requestMeta,
|
|
359
368
|
fullSurface: authResult.fullSurface === true,
|
|
369
|
+
inlineMcpApps:
|
|
370
|
+
requestMeta.inlineMcpApps === true &&
|
|
371
|
+
authResult.identity?.firstPartyMcp === true
|
|
372
|
+
? true
|
|
373
|
+
: undefined,
|
|
360
374
|
// When the caller minted their token with --full-catalog (catalog_scope:
|
|
361
375
|
// "full" JWT claim), bypass the connector-catalog tier filter.
|
|
362
376
|
...(authResult.fullCatalog === true ? { fullCatalog: true } : {}),
|
|
@@ -45,6 +45,18 @@ export interface McpHttpServerConfig {
|
|
|
45
45
|
url: string;
|
|
46
46
|
/** Extra headers to send with every request (e.g. Authorization). */
|
|
47
47
|
headers?: Record<string, string>;
|
|
48
|
+
/**
|
|
49
|
+
* Trusted first-party Agent-Native app. This is set only by framework-owned
|
|
50
|
+
* org-scoped registrations, not by raw file/env config.
|
|
51
|
+
*/
|
|
52
|
+
firstParty?: boolean;
|
|
53
|
+
/** Canonical first-party app id from the org directory, e.g. `assets`. */
|
|
54
|
+
firstPartyAppId?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Org id the first-party server is trusted for. Runtime-only metadata set by
|
|
57
|
+
* framework-owned registrations; raw file/env config cannot provide it.
|
|
58
|
+
*/
|
|
59
|
+
firstPartyOrgId?: string;
|
|
48
60
|
/** Human-readable description (optional, shown in /mcp/status) */
|
|
49
61
|
description?: string;
|
|
50
62
|
}
|