@agent-native/core 0.221.0 → 0.221.1-nightly-20261028032829

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.
@@ -1747,6 +1747,14 @@ function getAppOriginClientConfigScript() {
1747
1747
  const workspaceAppPath =
1748
1748
  workspaceAppMountConfig?.currentPath ??
1749
1749
  (workspaceRuntime ? configuredWorkspaceAppPath : "");
1750
+ const explicitRootPath =
1751
+ workspaceAppMountConfig?.currentPath === "/" ||
1752
+ (typeof appConfig.app.basePath === "string" &&
1753
+ appConfig.app.basePath.trim().length > 0 &&
1754
+ normalizeAppBasePath(appConfig.app.basePath) === "");
1755
+ const frameworkRoutePrefix = (
1756
+ process.env.AGENT_NATIVE_CONFIG_RUNTIME_FRAMEWORK_ROUTE_PREFIX || ""
1757
+ ).trim();
1750
1758
  const config = {
1751
1759
  ...(appConfig.app.id ? { appId: appConfig.app.id } : {}),
1752
1760
  ...(appConfig.app.workspaceId
@@ -1764,6 +1772,8 @@ function getAppOriginClientConfigScript() {
1764
1772
  ...(workspaceAppMountConfig?.paths
1765
1773
  ? { workspaceAppMountPaths: workspaceAppMountConfig.paths }
1766
1774
  : {}),
1775
+ ...(explicitRootPath ? { explicitRootPath: true } : {}),
1776
+ ...(frameworkRoutePrefix ? { frameworkRoutePrefix } : {}),
1767
1777
  };
1768
1778
  const toUnicodeEscape = (character) =>
1769
1779
  String.fromCharCode(92) +
@@ -1,5 +1,6 @@
1
1
  import { MCP_APP_CHAT_BRIDGE_QUERY_PARAM } from "../shared/embed-auth.js";
2
2
  import { MCP_APP_HOST_FILL_ATTRIBUTE, MCP_APP_PANE_FILL_MAX_HEIGHT, } from "../shared/mcp-app-display.js";
3
+ import { EMBED_TOKEN_STORAGE_KEY, MCP_CHAT_BRIDGE_STORAGE_KEY, resolveMcpAppEmbedBasePath, } from "../shared/mcp-app-widget-embed.js";
3
4
  import { EXTERNAL_EMBED_WINDOW_KEY } from "../shared/route-chunk-recovery-bootstrap.js";
4
5
  import { HIDDEN_EMBED_URL } from "./embed-redaction.js";
5
6
  const MCP_APP_IMPORT = "https://esm.sh/@modelcontextprotocol/ext-apps@1.7.5/app-with-deps";
@@ -656,7 +657,28 @@ export function embedApp(options = {}) {
656
657
  );
657
658
  }
658
659
 
659
- function embedConfigForAppUrl(appUrl) {
660
+ function appOriginConfigForDocument(parsed) {
661
+ const script = parsed.querySelector("script[data-agent-native-app-origin-config]");
662
+ if (!script) return {};
663
+ const source = script.textContent || "";
664
+ const prefix = "window.__AGENT_NATIVE_CONFIG__=Object.assign({},window.__AGENT_NATIVE_CONFIG__,";
665
+ const suffix = ");";
666
+ if (!source.startsWith(prefix) || !source.endsWith(suffix)) {
667
+ throw new Error("Embedded app origin config is malformed.");
668
+ }
669
+ let appConfig;
670
+ try {
671
+ appConfig = JSON.parse(source.slice(prefix.length, -suffix.length));
672
+ } catch (_err) {
673
+ throw new Error("Embedded app origin config is not valid JSON.");
674
+ }
675
+ if (!appConfig || typeof appConfig !== "object" || Array.isArray(appConfig)) {
676
+ throw new Error("Embedded app origin config must be an object.");
677
+ }
678
+ return appConfig;
679
+ }
680
+
681
+ function embedConfigForAppUrl(appUrl, appConfig) {
660
682
  const sanitizedTarget = localPathFromUrl(appUrl, false);
661
683
  return {
662
684
  origin: appUrl.origin,
@@ -667,7 +689,8 @@ export function embedApp(options = {}) {
667
689
  chatBridgeActive: appUrl.searchParams.get(chatBridgeParam) === "1",
668
690
  chatBridgeParam,
669
691
  embedTokenParam: "__an_embed_token",
670
- embedTargetHeader: "x-agent-native-embed-target"
692
+ embedTargetHeader: "x-agent-native-embed-target",
693
+ appConfig
671
694
  };
672
695
  }
673
696
 
@@ -736,27 +759,108 @@ export function embedApp(options = {}) {
736
759
  window.history.replaceState(window.history.state, "", config.target);
737
760
  }
738
761
  } catch (_err) {}
762
+ // Keep transplanted auth storage on the same mount resolver as its reader.
763
+ var resolveMcpAppEmbedBasePath = ${resolveMcpAppEmbedBasePath.toString()};
764
+ function activeConfig() {
765
+ return window[${JSON.stringify(EXTERNAL_EMBED_WINDOW_KEY)}] || config;
766
+ }
767
+ function workspaceMountOptions(active) {
768
+ var appConfig = active.appConfig || {};
769
+ return {
770
+ workspaceRuntime: true,
771
+ routerBasePath: window.__reactRouterContext && window.__reactRouterContext.basename,
772
+ workspaceAppPath: appConfig.workspaceAppPath,
773
+ workspaceAppMountPaths: appConfig.workspaceAppMountPaths,
774
+ explicitRootPath: appConfig.explicitRootPath === true,
775
+ frameworkRoutePrefix: appConfig.frameworkRoutePrefix
776
+ };
777
+ }
778
+ function embedStorageKey(key) {
779
+ var active = activeConfig();
780
+ var appConfig = active.appConfig || {};
781
+ if (appConfig.workspaceRuntime !== true) return key;
782
+ var basePath = resolveMcpAppEmbedBasePath(
783
+ "external-target",
784
+ active.target || location.pathname,
785
+ workspaceMountOptions(active)
786
+ );
787
+ if (typeof basePath !== "string") return undefined;
788
+ return key + ":" + (basePath || "/");
789
+ }
739
790
  try {
740
791
  if (config.token) {
741
- sessionStorage.setItem("agent-native:embed-auth-token", config.token);
792
+ var tokenStorageKey = embedStorageKey(${JSON.stringify(EMBED_TOKEN_STORAGE_KEY)});
793
+ if (tokenStorageKey) sessionStorage.setItem(tokenStorageKey, config.token);
742
794
  }
743
795
  if (config.chatBridgeActive && config.token) {
744
- sessionStorage.setItem("agent-native:mcp-chat-bridge", config.token);
796
+ var bridgeStorageKey = embedStorageKey(${JSON.stringify(MCP_CHAT_BRIDGE_STORAGE_KEY)});
797
+ if (bridgeStorageKey) sessionStorage.setItem(bridgeStorageKey, config.token);
745
798
  }
746
799
  } catch (_err) {}
747
800
  if (window.__agentNativeExternalEmbedRuntimeInstalled) return;
748
801
  window.__agentNativeExternalEmbedRuntimeInstalled = true;
749
802
  function appOrigin() {
750
803
  try {
751
- return new URL(config.origin).origin;
804
+ return new URL(activeConfig().origin).origin;
752
805
  } catch (_err) {
753
806
  return "";
754
807
  }
755
808
  }
756
809
  function targetPath() {
757
- return config.target || location.pathname + location.search;
810
+ var active = activeConfig();
811
+ return active.target || location.pathname + location.search;
812
+ }
813
+ function authorizedTargetPath() {
814
+ var active = activeConfig();
815
+ var appConfig = active.appConfig || {};
816
+ var target = targetPath();
817
+ if (appConfig.workspaceRuntime !== true) return target;
818
+ var options = workspaceMountOptions(active);
819
+ var targetMount = resolveMcpAppEmbedBasePath(
820
+ "external-target",
821
+ active.target || location.pathname,
822
+ options
823
+ );
824
+ var currentMount = resolveMcpAppEmbedBasePath(
825
+ "workspace-path",
826
+ location.pathname,
827
+ Object.assign({}, options, { preferDeclaredMounts: true })
828
+ );
829
+ if (typeof targetMount !== "string" || targetMount !== currentMount) {
830
+ return null;
831
+ }
832
+ return target;
833
+ }
834
+ function requestBelongsToTargetMount(value, target) {
835
+ var active = activeConfig();
836
+ var appConfig = active.appConfig || {};
837
+ if (appConfig.workspaceRuntime !== true) return true;
838
+ var options = workspaceMountOptions(active);
839
+ var targetMount = resolveMcpAppEmbedBasePath(
840
+ "external-target",
841
+ target,
842
+ options
843
+ );
844
+ if (typeof targetMount !== "string") return false;
845
+ var parsedUrl;
846
+ try {
847
+ parsedUrl = { url: new URL(value, location.href) };
848
+ } catch (error) {
849
+ parsedUrl = { error: error };
850
+ }
851
+ if (!("url" in parsedUrl)) return false;
852
+ var url = parsedUrl.url;
853
+ const origin = appOrigin();
854
+ if (url.origin !== location.origin && url.origin !== origin) return false;
855
+ var requestMount = resolveMcpAppEmbedBasePath(
856
+ "workspace-path",
857
+ url.pathname,
858
+ Object.assign({}, options, { preferDeclaredMounts: true })
859
+ );
860
+ return typeof requestMount === "string" && requestMount === targetMount;
758
861
  }
759
862
  function rewrittenUrl(value, appendToken) {
863
+ var active = activeConfig();
760
864
  const origin = appOrigin();
761
865
  if (!origin) return null;
762
866
  let url;
@@ -771,20 +875,49 @@ export function embedApp(options = {}) {
771
875
  url.protocol = app.protocol;
772
876
  url.host = app.host;
773
877
  }
774
- if (appendToken && config.token && url.pathname === "/_agent-native/events") {
775
- url.searchParams.set(config.embedTokenParam, config.token);
878
+ const appConfig = active.appConfig || {};
879
+ var frameworkRoutePrefix = appConfig.frameworkRoutePrefix || "/_agent-native";
880
+ if (frameworkRoutePrefix.endsWith("/")) {
881
+ frameworkRoutePrefix = frameworkRoutePrefix.slice(0, -1);
882
+ }
883
+ const eventsPath = frameworkRoutePrefix + "/events";
884
+ const authorizedTarget = authorizedTargetPath();
885
+ if (
886
+ appendToken &&
887
+ authorizedTarget !== null &&
888
+ requestBelongsToTargetMount(value, authorizedTarget) &&
889
+ active.token &&
890
+ url.pathname.endsWith(eventsPath)
891
+ ) {
892
+ url.searchParams.set(active.embedTokenParam, active.token);
776
893
  }
777
894
  return url.toString();
778
895
  }
779
896
  function authHeaders(input, init) {
897
+ var active = activeConfig();
898
+ var authorizedTarget = authorizedTargetPath();
899
+ var requestUrl = input instanceof Request ? input.url : input;
900
+ if (
901
+ authorizedTarget !== null &&
902
+ !requestBelongsToTargetMount(requestUrl, authorizedTarget)
903
+ ) {
904
+ authorizedTarget = null;
905
+ }
780
906
  const headers = new Headers(
781
907
  init && init.headers ? init.headers : input instanceof Request ? input.headers : undefined
782
908
  );
783
- if (config.token && !headers.has("Authorization")) {
784
- headers.set("Authorization", "Bearer " + config.token);
909
+ if (
910
+ authorizedTarget !== null &&
911
+ active.token &&
912
+ !headers.has("Authorization")
913
+ ) {
914
+ headers.set("Authorization", "Bearer " + active.token);
785
915
  }
786
- if (!headers.has(config.embedTargetHeader)) {
787
- headers.set(config.embedTargetHeader, targetPath());
916
+ if (
917
+ authorizedTarget !== null &&
918
+ !headers.has(active.embedTargetHeader)
919
+ ) {
920
+ headers.set(active.embedTargetHeader, authorizedTarget);
788
921
  }
789
922
  return headers;
790
923
  }
@@ -810,6 +943,7 @@ export function embedApp(options = {}) {
810
943
  XMLHttpRequest.prototype.open = function(method, url) {
811
944
  const rewritten = rewrittenUrl(url, false);
812
945
  this.__agentNativeExternalEmbed = !!rewritten;
946
+ this.__agentNativeExternalEmbedUrl = url;
813
947
  return originalOpen.call(
814
948
  this,
815
949
  method,
@@ -822,8 +956,20 @@ export function embedApp(options = {}) {
822
956
  XMLHttpRequest.prototype.send = function(body) {
823
957
  if (this.__agentNativeExternalEmbed) {
824
958
  try {
825
- if (config.token) this.setRequestHeader("Authorization", "Bearer " + config.token);
826
- this.setRequestHeader(config.embedTargetHeader, targetPath());
959
+ var active = activeConfig();
960
+ var authorizedTarget = authorizedTargetPath();
961
+ if (
962
+ authorizedTarget !== null &&
963
+ requestBelongsToTargetMount(
964
+ this.__agentNativeExternalEmbedUrl,
965
+ authorizedTarget
966
+ )
967
+ ) {
968
+ if (active.token) {
969
+ this.setRequestHeader("Authorization", "Bearer " + active.token);
970
+ }
971
+ this.setRequestHeader(active.embedTargetHeader, authorizedTarget);
972
+ }
827
973
  } catch (_err) {}
828
974
  }
829
975
  return originalSend.call(this, body);
@@ -1056,12 +1202,15 @@ export function embedApp(options = {}) {
1056
1202
  }
1057
1203
 
1058
1204
  async function mountTransplantedHtml(html, appUrl) {
1059
- const config = embedConfigForAppUrl(appUrl);
1060
- installExternalEmbedRuntime(config);
1061
1205
  const parsed = new DOMParser().parseFromString(
1062
1206
  rewriteRootRelativeHtmlUrls(removeHtmlCspMeta(html), appUrl.origin),
1063
1207
  "text/html"
1064
1208
  );
1209
+ const config = embedConfigForAppUrl(
1210
+ appUrl,
1211
+ appOriginConfigForDocument(parsed)
1212
+ );
1213
+ installExternalEmbedRuntime(config);
1065
1214
  const scripts = Array.from(parsed.querySelectorAll("script"));
1066
1215
  copyDocumentElementAttributes(parsed.documentElement);
1067
1216
  importHeadChildrenWithBase(parsed.head, config.baseHref);
@@ -76,8 +76,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
78
  id?: undefined;
79
- deleted?: undefined;
80
79
  provider?: undefined;
80
+ deleted?: undefined;
81
81
  message?: undefined;
82
82
  providers: {
83
83
  id: string;
@@ -103,8 +103,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
103
103
  registered?: undefined;
104
104
  label?: undefined;
105
105
  } | {
106
- deleted?: undefined;
107
106
  provider?: undefined;
107
+ deleted?: undefined;
108
108
  count?: undefined;
109
109
  message?: undefined;
110
110
  providers?: undefined;
@@ -123,8 +123,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
123
123
  registered?: undefined;
124
124
  label?: undefined;
125
125
  } | {
126
- deleted?: undefined;
127
126
  provider?: undefined;
127
+ deleted?: undefined;
128
128
  count?: undefined;
129
129
  providers?: undefined;
130
130
  found?: undefined;
@@ -316,6 +316,7 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
316
316
  notes?: string | undefined;
317
317
  scope?: "org" | "user" | undefined;
318
318
  }, {
319
+ message?: undefined;
319
320
  deleted?: undefined;
320
321
  id?: undefined;
321
322
  providers: {
@@ -332,8 +333,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
332
333
  found?: undefined;
333
334
  registered?: undefined;
334
335
  label?: undefined;
335
- message?: undefined;
336
336
  } | {
337
+ message?: undefined;
337
338
  deleted?: undefined;
338
339
  id?: undefined;
339
340
  providers?: undefined;
@@ -342,8 +343,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
342
343
  provider: import("../custom-registry.js").CustomProviderConfig;
343
344
  registered?: undefined;
344
345
  label?: undefined;
345
- message?: undefined;
346
346
  } | {
347
+ message?: undefined;
347
348
  deleted?: undefined;
348
349
  providers?: undefined;
349
350
  count?: undefined;
@@ -352,8 +353,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
352
353
  id: string;
353
354
  registered?: undefined;
354
355
  label?: undefined;
355
- message?: undefined;
356
356
  } | {
357
+ message?: undefined;
357
358
  providers?: undefined;
358
359
  count?: undefined;
359
360
  provider?: undefined;
@@ -362,7 +363,6 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
362
363
  id: string;
363
364
  registered?: undefined;
364
365
  label?: undefined;
365
- message?: undefined;
366
366
  } | {
367
367
  deleted?: undefined;
368
368
  providers?: undefined;
@@ -8,5 +8,7 @@ export declare function resolvePublicAppOriginConfig(): {
8
8
  workspaceOAuthOrigin?: string;
9
9
  workspaceRuntime?: boolean;
10
10
  workspaceAppMountPaths?: string[];
11
+ frameworkRoutePrefix?: string;
12
+ explicitRootPath?: boolean;
11
13
  } | null;
12
14
  export declare function getAppOriginClientConfigScript(): string | null;
@@ -1,6 +1,7 @@
1
1
  import { getAppConfig, resolveAppHomePath } from "../app-config/index.js";
2
2
  import { safeJsonForHtml } from "../shared/agent-readable-resource.js";
3
3
  import { normalizeAppBasePath } from "./app-base-path.js";
4
+ import { FRAMEWORK_INTERNAL_ROUTE_PREFIX, getFrameworkRoutePrefix, } from "./framework-route-prefix.js";
4
5
  function workspaceAppMountConfigFromJson(value, workspaceAppId) {
5
6
  if (!value?.trim())
6
7
  return undefined;
@@ -50,6 +51,7 @@ function workspaceAppMountConfigFromJson(value, workspaceAppId) {
50
51
  }
51
52
  export function resolvePublicAppOriginConfig() {
52
53
  const config = getAppConfig();
54
+ const frameworkRoutePrefix = getFrameworkRoutePrefix();
53
55
  const workspaceRuntime = config.workspace.isWorkspace === true ||
54
56
  typeof config.workspace.appsJson === "string";
55
57
  const workspaceAppMountConfig = workspaceAppMountConfigFromJson(config.workspace.appsJson, config.app.workspaceId);
@@ -58,6 +60,10 @@ export function resolvePublicAppOriginConfig() {
58
60
  : undefined;
59
61
  const workspaceAppPath = workspaceAppMountConfig?.currentPath ??
60
62
  (workspaceRuntime ? configuredWorkspaceAppPath : undefined);
63
+ const explicitRootPath = workspaceAppMountConfig?.currentPath === "/" ||
64
+ (typeof config.app.basePath === "string" &&
65
+ config.app.basePath.trim().length > 0 &&
66
+ normalizeAppBasePath(config.app.basePath) === "");
61
67
  const resolved = {
62
68
  ...(config.app.id ? { appId: config.app.id } : {}),
63
69
  ...(config.app.workspaceId
@@ -76,6 +82,10 @@ export function resolvePublicAppOriginConfig() {
76
82
  ...(workspaceAppMountConfig?.paths
77
83
  ? { workspaceAppMountPaths: workspaceAppMountConfig.paths }
78
84
  : {}),
85
+ ...(explicitRootPath ? { explicitRootPath: true } : {}),
86
+ ...(frameworkRoutePrefix !== FRAMEWORK_INTERNAL_ROUTE_PREFIX
87
+ ? { frameworkRoutePrefix }
88
+ : {}),
79
89
  };
80
90
  return Object.keys(resolved).length > 0 ? resolved : null;
81
91
  }
@@ -1,5 +1,27 @@
1
1
  export declare const EMBED_TOKEN_STORAGE_KEY = "agent-native:embed-auth-token";
2
2
  export declare const MCP_CHAT_BRIDGE_STORAGE_KEY = "agent-native:mcp-chat-bridge";
3
+ export interface WorkspaceAppMountPathOptions {
4
+ explicitRootPath?: boolean;
5
+ fallbackPath?: string;
6
+ frameworkRoutePrefix?: string;
7
+ preferDeclaredMounts?: boolean;
8
+ routerBasePath?: unknown;
9
+ workspaceAppMountPaths?: unknown;
10
+ workspaceAppPath?: unknown;
11
+ }
12
+ /**
13
+ * Self-contained so its source can safely be embedded in browser scripts.
14
+ * Keep both paths here: a serialized function cannot close over module locals.
15
+ */
16
+ export declare function resolveMcpAppEmbedBasePath(mode: "workspace-path" | "external-target", value: unknown, options?: WorkspaceAppMountPathOptions & {
17
+ workspaceRuntime?: boolean;
18
+ }): string | undefined;
19
+ export declare function resolveWorkspaceAppMountPath(pathname: string, options?: WorkspaceAppMountPathOptions): string | undefined;
20
+ export declare function resolveExternalEmbedTargetBasePath(target: unknown, options?: WorkspaceAppMountPathOptions & {
21
+ workspaceRuntime?: boolean;
22
+ }): string | undefined;
23
+ export declare function embedTokenStorageKey(basePath: string | undefined, workspaceRuntime: boolean): string;
24
+ export declare function mcpChatBridgeStorageKey(basePath: string | undefined, workspaceRuntime: boolean): string;
3
25
  /**
4
26
  * Set on `<html>` for the life of a document that is an app nested in an MCP
5
27
  * App widget (a ChatGPT, Codex, or Claude card or side pane). Layout reads it
@@ -15,4 +37,4 @@ export declare const MCP_APP_WIDGET_EMBED_ATTRIBUTE = "data-agent-native-mcp-wid
15
37
  * embed credential in the URL, stored, or `embedded=1`); `mcp-app-host.spec.ts`
16
38
  * runs both over one table so they cannot drift.
17
39
  */
18
- export declare function getMcpAppWidgetEmbedBootScriptBody(): string;
40
+ export declare function getMcpAppWidgetEmbedBootScriptBody(basePath?: string, workspaceRuntime?: boolean): string;
@@ -1,7 +1,140 @@
1
1
  import { safeJsonForHtml } from "./agent-readable-resource.js";
2
2
  import { EMBED_MODE_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, MCP_APP_CHAT_BRIDGE_QUERY_PARAM, } from "./embed-auth.js";
3
+ import { EXTERNAL_EMBED_WINDOW_KEY } from "./route-chunk-recovery-bootstrap.js";
3
4
  export const EMBED_TOKEN_STORAGE_KEY = "agent-native:embed-auth-token";
4
5
  export const MCP_CHAT_BRIDGE_STORAGE_KEY = "agent-native:mcp-chat-bridge";
6
+ /**
7
+ * Self-contained so its source can safely be embedded in browser scripts.
8
+ * Keep both paths here: a serialized function cannot close over module locals.
9
+ */
10
+ export function resolveMcpAppEmbedBasePath(mode, value, options = {}) {
11
+ const resolveWorkspacePath = (pathname, options) => {
12
+ const normalize = (value) => {
13
+ if (typeof value !== "string")
14
+ return "";
15
+ const trimmed = value.trim();
16
+ if (!trimmed || trimmed === "/")
17
+ return "";
18
+ return `/${trimmed.replace(/^\/+/, "").replace(/\/+$/, "")}`;
19
+ };
20
+ const matches = (basePath) => Boolean(basePath) &&
21
+ (pathname === basePath || pathname.startsWith(`${basePath}/`));
22
+ const configuredPath = normalize(options.workspaceAppPath);
23
+ const explicitRootPath = options.explicitRootPath === true ||
24
+ (typeof options.workspaceAppPath === "string" &&
25
+ options.workspaceAppPath.trim().length > 0 &&
26
+ configuredPath === "");
27
+ const fallbackPath = normalize(options.fallbackPath);
28
+ const routerBasePath = normalize(options.routerBasePath);
29
+ const matchingHostPaths = [routerBasePath, configuredPath]
30
+ .filter((basePath) => basePath && matches(basePath))
31
+ .sort((left, right) => right.length - left.length);
32
+ const matchingHostPath = matchingHostPaths[0];
33
+ if (matchingHostPath && !options.preferDeclaredMounts) {
34
+ return matchingHostPath;
35
+ }
36
+ const declaredMounts = (Array.isArray(options.workspaceAppMountPaths)
37
+ ? options.workspaceAppMountPaths
38
+ : [])
39
+ .filter((value) => typeof value === "string")
40
+ .map(normalize)
41
+ .filter(Boolean);
42
+ const matchingMount = declaredMounts
43
+ .filter((basePath) => matches(basePath))
44
+ .sort((left, right) => right.length - left.length)[0];
45
+ if (matchingHostPath && options.preferDeclaredMounts) {
46
+ return [matchingHostPath, matchingMount]
47
+ .filter((basePath) => Boolean(basePath))
48
+ .sort((left, right) => right.length - left.length)[0];
49
+ }
50
+ const segment = pathname.split("/").find(Boolean);
51
+ if (!segment) {
52
+ if (explicitRootPath)
53
+ return "";
54
+ return fallbackPath || undefined;
55
+ }
56
+ const frameworkRoutePrefix = normalize(options.frameworkRoutePrefix) || "/_agent-native";
57
+ if (`/${segment}` === "/_agent-native" ||
58
+ `/${segment}` === frameworkRoutePrefix ||
59
+ segment === "api") {
60
+ if (options.preferDeclaredMounts) {
61
+ return explicitRootPath ? "" : undefined;
62
+ }
63
+ return fallbackPath;
64
+ }
65
+ if (matchingMount)
66
+ return matchingMount;
67
+ if (explicitRootPath)
68
+ return "";
69
+ if (options.preferDeclaredMounts &&
70
+ Array.isArray(options.workspaceAppMountPaths)) {
71
+ return undefined;
72
+ }
73
+ return fallbackPath || undefined;
74
+ };
75
+ if (mode === "workspace-path") {
76
+ return typeof value === "string"
77
+ ? resolveWorkspacePath(value, options)
78
+ : undefined;
79
+ }
80
+ if (typeof value !== "string" || !value.startsWith("/"))
81
+ return undefined;
82
+ const url = new URL(value, "http://agent-native.invalid");
83
+ const markers = [
84
+ options.frameworkRoutePrefix || "/_agent-native",
85
+ "/_agent-native",
86
+ ];
87
+ for (const marker of markers) {
88
+ for (let index = url.pathname.indexOf(marker); index > 0; index = url.pathname.indexOf(marker, index + 1)) {
89
+ const end = index + marker.length;
90
+ if (end === url.pathname.length || url.pathname[end] === "/") {
91
+ const prefix = url.pathname.slice(0, index).trim();
92
+ return prefix ? `/${prefix.replace(/^\/+|\/+$/g, "")}` : "";
93
+ }
94
+ }
95
+ }
96
+ if (!options.workspaceRuntime)
97
+ return undefined;
98
+ const segments = url.pathname.split("/").filter(Boolean);
99
+ const segment = segments[0];
100
+ const frameworkRoutePrefix = options.frameworkRoutePrefix || "/_agent-native";
101
+ const fallbackPath = segments.length > 1 &&
102
+ segment &&
103
+ `/${segment}` !== "/_agent-native" &&
104
+ `/${segment}` !== frameworkRoutePrefix &&
105
+ segment !== "api"
106
+ ? `/${segment}`
107
+ : "";
108
+ return resolveWorkspacePath(url.pathname, {
109
+ ...options,
110
+ fallbackPath,
111
+ preferDeclaredMounts: true,
112
+ });
113
+ }
114
+ export function resolveWorkspaceAppMountPath(pathname, options = {}) {
115
+ return resolveMcpAppEmbedBasePath("workspace-path", pathname, options);
116
+ }
117
+ export function resolveExternalEmbedTargetBasePath(target, options = {}) {
118
+ return resolveMcpAppEmbedBasePath("external-target", target, options);
119
+ }
120
+ function normalizedStorageBasePath(basePath) {
121
+ const trimmed = basePath?.trim();
122
+ if (!trimmed || trimmed === "/")
123
+ return "";
124
+ return `/${trimmed.replace(/^\/+/, "").replace(/\/+$/, "")}`;
125
+ }
126
+ function appScopedStorageKey(key, basePath, workspaceRuntime) {
127
+ if (!workspaceRuntime)
128
+ return key;
129
+ const normalizedBasePath = normalizedStorageBasePath(basePath);
130
+ return `${key}:${normalizedBasePath || "/"}`;
131
+ }
132
+ export function embedTokenStorageKey(basePath, workspaceRuntime) {
133
+ return appScopedStorageKey(EMBED_TOKEN_STORAGE_KEY, basePath, workspaceRuntime);
134
+ }
135
+ export function mcpChatBridgeStorageKey(basePath, workspaceRuntime) {
136
+ return appScopedStorageKey(MCP_CHAT_BRIDGE_STORAGE_KEY, basePath, workspaceRuntime);
137
+ }
5
138
  /**
6
139
  * Set on `<html>` for the life of a document that is an app nested in an MCP
7
140
  * App widget (a ChatGPT, Codex, or Claude card or side pane). Layout reads it
@@ -17,22 +150,62 @@ export const MCP_APP_WIDGET_EMBED_ATTRIBUTE = "data-agent-native-mcp-widget";
17
150
  * embed credential in the URL, stored, or `embedded=1`); `mcp-app-host.spec.ts`
18
151
  * runs both over one table so they cannot drift.
19
152
  */
20
- export function getMcpAppWidgetEmbedBootScriptBody() {
153
+ export function getMcpAppWidgetEmbedBootScriptBody(basePath, workspaceRuntime = false) {
154
+ const dynamicWorkspaceBasePath = workspaceRuntime && basePath === undefined;
155
+ const tokenStorageKey = dynamicWorkspaceBasePath
156
+ ? EMBED_TOKEN_STORAGE_KEY
157
+ : embedTokenStorageKey(basePath ?? "", workspaceRuntime);
158
+ const bridgeStorageKey = dynamicWorkspaceBasePath
159
+ ? MCP_CHAT_BRIDGE_STORAGE_KEY
160
+ : mcpChatBridgeStorageKey(basePath ?? "", workspaceRuntime);
21
161
  return `(function __anMcpWidgetBoot() {
22
162
  try {
23
163
  var w = window;
24
164
  if (w.parent === w) return;
165
+ var tokenStorageKey = ${safeJsonForHtml(tokenStorageKey)};
166
+ var bridgeStorageKey = ${safeJsonForHtml(bridgeStorageKey)};
167
+ var external = w[${safeJsonForHtml(EXTERNAL_EMBED_WINDOW_KEY)}];
168
+ var storageScopeAvailable = true;
169
+ ${dynamicWorkspaceBasePath
170
+ ? `var appConfig = w.__AGENT_NATIVE_CONFIG__ || {};
171
+ var runtimeConfig = appConfig.runtime && typeof appConfig.runtime === "object"
172
+ ? appConfig.runtime
173
+ : {};
174
+ var resolveMcpAppEmbedBasePath = ${resolveMcpAppEmbedBasePath.toString()};
175
+ var workspaceOptions = {
176
+ workspaceAppPath: appConfig.workspaceAppPath,
177
+ workspaceAppMountPaths: appConfig.workspaceAppMountPaths,
178
+ explicitRootPath: appConfig.explicitRootPath === true,
179
+ routerBasePath: w.__reactRouterContext && w.__reactRouterContext.basename,
180
+ frameworkRoutePrefix: runtimeConfig.frameworkRoutePrefix || appConfig.frameworkRoutePrefix
181
+ };
182
+ var basePath = external && typeof external.target === "string"
183
+ ? resolveMcpAppEmbedBasePath("external-target", external.target, Object.assign({ workspaceRuntime: true }, workspaceOptions))
184
+ : resolveMcpAppEmbedBasePath("workspace-path", w.location.pathname, workspaceOptions);
185
+ if (typeof basePath !== "string") {
186
+ storageScopeAvailable = false;
187
+ } else {
188
+ var normalizedBasePath = basePath.trim();
189
+ normalizedBasePath = !normalizedBasePath || normalizedBasePath === "/"
190
+ ? ""
191
+ : "/" + normalizedBasePath.replace(/^\\/+/, "").replace(/\\/+$/, "");
192
+ tokenStorageKey += ":" + (normalizedBasePath || "/");
193
+ bridgeStorageKey += ":" + (normalizedBasePath || "/");
194
+ }`
195
+ : ""}
25
196
  var params = new URLSearchParams(w.location.search);
26
197
  var stored = function (key) {
198
+ if (!storageScopeAvailable) return null;
27
199
  // coercion-ok: denied storage reads as absent, exactly as isEmbedMcpChatBridgeActive does.
28
200
  try { return w.sessionStorage.getItem(key); } catch (e) { return null; }
29
201
  };
30
- var token = params.get(${safeJsonForHtml(EMBED_TOKEN_QUERY_PARAM)}) || stored(${safeJsonForHtml(EMBED_TOKEN_STORAGE_KEY)});
202
+ var token = params.get(${safeJsonForHtml(EMBED_TOKEN_QUERY_PARAM)}) || stored(tokenStorageKey) || (external && typeof external.token === "string" ? external.token : "");
31
203
  var mode = params.get(${safeJsonForHtml(EMBED_MODE_QUERY_PARAM)});
32
- if (!token && mode !== "1" && mode !== "true") return;
33
204
  var flag = params.get(${safeJsonForHtml(MCP_APP_CHAT_BRIDGE_QUERY_PARAM)});
34
- var scope = stored(${safeJsonForHtml(MCP_CHAT_BRIDGE_STORAGE_KEY)});
35
- if (flag === "1" || flag === "true" || (scope && (!token || scope === token))) {
205
+ var externalBridgeActive = external && external.chatBridgeActive === true;
206
+ if (!token && mode !== "1" && mode !== "true" && !externalBridgeActive) return;
207
+ var scope = stored(bridgeStorageKey);
208
+ if (flag === "1" || flag === "true" || externalBridgeActive || (scope && (!token || scope === token))) {
36
209
  document.documentElement.setAttribute(${safeJsonForHtml(MCP_APP_WIDGET_EMBED_ATTRIBUTE)}, "1");
37
210
  }
38
211
  // coercion-ok: the attribute is a first-paint hint; isMcpAppWidgetEmbed sets it again once the app runs.