@agent-native/core 0.221.0 → 0.222.0-nightly-20261028092719

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.
@@ -4,6 +4,7 @@ import { parseBase64DataUrl } from "../shared/data-url.js";
4
4
  import { trackEvent } from "./analytics.js";
5
5
  import { agentNativePath } from "./api-path.js";
6
6
  import { readClientAppState, writeClientAppState, } from "./application-state.js";
7
+ import { getBrowserTabId } from "./browser-tab-id.js";
7
8
  import { isInBuilderFrame, isTrustedBuilderMessage, sendToBuilderChat, } from "./builder-frame.js";
8
9
  import { trackRoutedTurnSubmitted } from "./chat/turn-submitted-telemetry.js";
9
10
  import { DEFAULT_TURN_SURFACE } from "./chat/turn-submitted.js";
@@ -662,7 +663,11 @@ function dispatchAgentChatRunning(isRunning, tabId) {
662
663
  if (typeof window === "undefined")
663
664
  return;
664
665
  window.dispatchEvent(new CustomEvent("agentNative.chatRunning", {
665
- detail: { isRunning, ...(tabId ? { tabId } : {}) },
666
+ detail: {
667
+ isRunning,
668
+ ...(tabId ? { tabId } : {}),
669
+ browserTabId: getBrowserTabId(),
670
+ },
666
671
  }));
667
672
  }
668
673
  function normalizeAgentChatRequestMode(value) {
@@ -15,6 +15,8 @@ export declare function isWorkspaceRuntime(): boolean;
15
15
  * keep the basename supplied by the server instead of guessing from the URL.
16
16
  */
17
17
  export declare function configureClientRouterBasename(): boolean;
18
+ export declare function resolveExternalEmbedTargetAppBasePath(target: string): string | undefined;
19
+ export declare function resolveAppBasePath(): string;
18
20
  export declare function appBasePath(): string;
19
21
  export declare function isWorkspaceAppPath(path: string): boolean;
20
22
  export declare function appMountPath(appLocalRoute: string): string;
@@ -1,4 +1,5 @@
1
1
  import { FRAMEWORK_INTERNAL_ROUTE_PREFIX, matchesPathPrefix, normalizeFrameworkRoutePrefix, SERVER_ROUTE_PREFIXES, toPublicFrameworkPath, } from "../shared/framework-route-prefix.js";
2
+ import { resolveExternalEmbedTargetBasePath, resolveWorkspaceAppMountPath, } from "../shared/mcp-app-widget-embed.js";
2
3
  import { isTruthyRuntimeValue } from "../shared/runtime-config.js";
3
4
  import { injectedAgentNativeConfig } from "./app-config.js";
4
5
  import { initializeAgentNativeClient } from "./client-bootstrap.js";
@@ -69,10 +70,6 @@ function frameworkMarkerIndex(pathname) {
69
70
  }
70
71
  return -1;
71
72
  }
72
- function isFrameworkSegment(segment) {
73
- return (`/${segment}` === FRAMEWORK_INTERNAL_ROUTE_PREFIX ||
74
- `/${segment}` === frameworkRoutePrefix());
75
- }
76
73
  function pathDerivedBasePath() {
77
74
  if (typeof window === "undefined" ||
78
75
  typeof window.location?.pathname !== "string") {
@@ -106,7 +103,8 @@ function hasExplicitWorkspaceRootPath() {
106
103
  return false;
107
104
  const config = window;
108
105
  const env = clientEnv();
109
- return (isDeclaredRootPath(config.__AGENT_NATIVE_CONFIG__?.workspaceAppPath) ||
106
+ return (config.__AGENT_NATIVE_CONFIG__?.explicitRootPath === true ||
107
+ isDeclaredRootPath(config.__AGENT_NATIVE_CONFIG__?.workspaceAppPath) ||
110
108
  isDeclaredRootPath(env?.VITE_APP_BASE_PATH ?? env?.APP_BASE_PATH));
111
109
  }
112
110
  export function isWorkspaceRuntime() {
@@ -125,38 +123,21 @@ function workspacePathBasePath(fallbackPath = "") {
125
123
  const pathname = window.location?.pathname;
126
124
  if (typeof pathname !== "string")
127
125
  return "";
128
- const routerBasePath = routerContextBasePath();
129
- if (routerBasePath && pathMatchesBasePath(pathname, routerBasePath)) {
130
- return routerBasePath;
131
- }
132
- const configuredWorkspacePath = workspaceAppConfiguredPath();
133
- if (configuredWorkspacePath &&
134
- pathMatchesBasePath(pathname, configuredWorkspacePath)) {
135
- return configuredWorkspacePath;
136
- }
137
126
  const segment = pathname.split("/").find(Boolean);
127
+ const mounts = workspaceAppMountPaths();
128
+ const resolved = resolveWorkspaceAppMountPath(pathname, {
129
+ routerBasePath: routerContextBasePath(),
130
+ workspaceAppPath: workspaceAppConfiguredPath(),
131
+ workspaceAppMountPaths: mounts ? [...mounts] : undefined,
132
+ fallbackPath,
133
+ explicitRootPath: hasExplicitWorkspaceRootPath(),
134
+ frameworkRoutePrefix: frameworkRoutePrefix(),
135
+ });
136
+ if (resolved !== undefined)
137
+ return resolved;
138
138
  if (!segment) {
139
- if (hasExplicitWorkspaceRootPath())
140
- return "";
141
- if (fallbackPath)
142
- return fallbackPath;
143
139
  throw new WorkspaceAppMountResolutionError("Cannot resolve workspace app mount path without explicit mount metadata.");
144
140
  }
145
- if (isFrameworkSegment(segment) || segment === "api") {
146
- return fallbackPath;
147
- }
148
- if (hasExplicitWorkspaceRootPath())
149
- return "";
150
- const mounts = workspaceAppMountPaths();
151
- const matchingMount = mounts
152
- ? [...mounts]
153
- .filter((mount) => pathMatchesBasePath(pathname, mount))
154
- .sort((a, b) => b.length - a.length)[0]
155
- : undefined;
156
- if (matchingMount)
157
- return matchingMount;
158
- if (fallbackPath)
159
- return fallbackPath;
160
141
  throw new WorkspaceAppMountResolutionError(mounts
161
142
  ? "Cannot resolve workspace app mount path because the current URL matches no projected mount."
162
143
  : "Cannot resolve workspace app mount path without explicit mount metadata.");
@@ -188,34 +169,70 @@ export function configureClientRouterBasename() {
188
169
  context.basename = routerBasePath;
189
170
  return true;
190
171
  }
172
+ export function resolveExternalEmbedTargetAppBasePath(target) {
173
+ const mounts = workspaceAppMountPaths();
174
+ return resolveExternalEmbedTargetBasePath(target, {
175
+ workspaceRuntime: isWorkspaceRuntime(),
176
+ routerBasePath: routerContextBasePath(),
177
+ workspaceAppPath: workspaceAppConfiguredPath(),
178
+ workspaceAppMountPaths: mounts ? [...mounts] : undefined,
179
+ explicitRootPath: hasExplicitWorkspaceRootPath(),
180
+ frameworkRoutePrefix: frameworkRoutePrefix(),
181
+ });
182
+ }
191
183
  function externalEmbedTargetBasePath() {
192
184
  if (typeof window === "undefined")
193
- return "";
194
- const target = window.__AGENT_NATIVE_EXTERNAL_EMBED?.target;
195
- if (typeof target !== "string" || !target.startsWith("/"))
196
- return "";
197
- try {
198
- const url = new URL(target, "http://agent-native.invalid");
199
- const markerIndex = frameworkMarkerIndex(url.pathname);
200
- if (markerIndex > 0) {
201
- return normalizeBasePath(url.pathname.slice(0, markerIndex));
202
- }
203
- if (isWorkspaceRuntime()) {
204
- const segment = url.pathname.split("/").find(Boolean);
205
- if (segment && !isFrameworkSegment(segment) && segment !== "api") {
206
- return normalizeBasePath(segment);
207
- }
208
- }
185
+ return undefined;
186
+ const externalEmbed = window.__AGENT_NATIVE_EXTERNAL_EMBED;
187
+ if (!externalEmbed || typeof externalEmbed !== "object")
188
+ return undefined;
189
+ const config = externalEmbed;
190
+ const target = config.target;
191
+ if (typeof target !== "string" || !target.startsWith("/")) {
192
+ return undefined;
209
193
  }
210
- catch {
211
- return "";
194
+ const basePath = resolveExternalEmbedTargetAppBasePath(target);
195
+ const pathname = window.location.pathname;
196
+ if (!pathname.startsWith("/")) {
197
+ return basePath;
198
+ }
199
+ const storedPathState = config.__agentNativePathState;
200
+ let pathState = storedPathState && typeof storedPathState === "object"
201
+ ? storedPathState
202
+ : undefined;
203
+ if (!pathState ||
204
+ pathState.target !== target ||
205
+ typeof pathState.initialPathname !== "string" ||
206
+ typeof pathState.navigated !== "boolean") {
207
+ const nextPathState = {
208
+ target,
209
+ initialPathname: pathname,
210
+ navigated: false,
211
+ };
212
+ config.__agentNativePathState = nextPathState;
213
+ pathState = nextPathState;
214
+ }
215
+ else if (pathState.initialPathname !== pathname) {
216
+ pathState.navigated = true;
212
217
  }
213
- return "";
218
+ // Keep the target for handoff, then follow later route changes.
219
+ if (!pathState.navigated || !pathname.startsWith("/"))
220
+ return basePath;
221
+ const mounts = workspaceAppMountPaths();
222
+ const currentMount = resolveWorkspaceAppMountPath(pathname, {
223
+ workspaceAppMountPaths: mounts ? [...mounts] : undefined,
224
+ frameworkRoutePrefix: frameworkRoutePrefix(),
225
+ });
226
+ if (currentMount !== undefined && currentMount !== basePath) {
227
+ return currentMount;
228
+ }
229
+ if (basePath && !pathMatchesBasePath(pathname, basePath))
230
+ return undefined;
231
+ return basePath;
214
232
  }
215
- export function appBasePath() {
216
- initializeAgentNativeClient();
233
+ export function resolveAppBasePath() {
217
234
  const externalEmbed = externalEmbedTargetBasePath();
218
- if (externalEmbed)
235
+ if (externalEmbed !== undefined)
219
236
  return externalEmbed;
220
237
  const configured = configuredBasePath();
221
238
  const derived = pathDerivedBasePath();
@@ -228,6 +245,10 @@ export function appBasePath() {
228
245
  return configured;
229
246
  return derived || workspacePathBasePath(configured);
230
247
  }
248
+ export function appBasePath() {
249
+ initializeAgentNativeClient();
250
+ return resolveAppBasePath();
251
+ }
231
252
  function workspaceAppMountPaths() {
232
253
  const projected = typeof window === "undefined"
233
254
  ? undefined
@@ -1,6 +1,6 @@
1
1
  import { agentNativePath } from "./api-path.js";
2
2
  import { assertAgentNativeApiEnabled } from "./api-surface.js";
3
- import { getBrowserTabId } from "./browser-tab-id.js";
3
+ import { getBrowserTabId, isBrowserTabIdForCurrentDocument, } from "./browser-tab-id.js";
4
4
  import { recordStateWrite } from "./recorder.js";
5
5
  const APP_STATE_KEY_PATTERN = /^[a-zA-Z0-9_:-]+$/;
6
6
  const pendingMutations = new Map();
@@ -18,10 +18,18 @@ function buildHeaders(requestSource) {
18
18
  if (typeof window !== "undefined") {
19
19
  headers["X-Agent-Native-Browser-Tab"] = getBrowserTabId();
20
20
  }
21
- if (requestSource)
22
- headers["X-Request-Source"] = requestSource;
21
+ Object.assign(headers, requestSourceHeader(requestSource));
23
22
  return headers;
24
23
  }
24
+ function requestSourceHeader(requestSource) {
25
+ if (!requestSource)
26
+ return {};
27
+ return {
28
+ "X-Request-Source": isBrowserTabIdForCurrentDocument(requestSource)
29
+ ? getBrowserTabId()
30
+ : requestSource,
31
+ };
32
+ }
25
33
  function browserTabHeaders() {
26
34
  return typeof window === "undefined"
27
35
  ? undefined
@@ -239,9 +247,7 @@ export async function deleteClientAppState(key, options = {}) {
239
247
  headers: {
240
248
  "X-Agent-Native-CSRF": "1",
241
249
  ...(browserTabHeaders() ?? {}),
242
- ...(options.requestSource
243
- ? { "X-Request-Source": options.requestSource }
244
- : {}),
250
+ ...requestSourceHeader(options.requestSource),
245
251
  },
246
252
  keepalive: options.keepalive,
247
253
  signal: options.signal,
@@ -1 +1,3 @@
1
1
  export declare function getBrowserTabId(): string;
2
+ export declare function resolveBrowserTabIdAlias(tabId?: string): string | undefined;
3
+ export declare function isBrowserTabIdForCurrentDocument(tabId?: string): boolean;
@@ -1,7 +1,18 @@
1
- import { appBasePath, isWorkspaceRuntime } from "./api-path.js";
2
- let cached;
1
+ import { appBasePath, isWorkspaceRuntime, WorkspaceAppMountResolutionError, } from "./api-path.js";
3
2
  const STORAGE_KEY = "agent-native:browser-tab-id";
4
3
  const SAFE_BROWSER_TAB_ID_RE = /^[A-Za-z0-9_-]{1,96}$/;
4
+ const BROWSER_TAB_ID_STATE_SYMBOL = Symbol.for("@agent-native/core/client/browser-tab-id");
5
+ const browserTabIdGlobal = globalThis;
6
+ // Vite can load optimized package and deep-import copies in the same tab.
7
+ const browserTabIdState = browserTabIdGlobal[BROWSER_TAB_ID_STATE_SYMBOL] ??
8
+ (browserTabIdGlobal[BROWSER_TAB_ID_STATE_SYMBOL] = {
9
+ ids: new Map(),
10
+ aliases: new Map(),
11
+ pendingId: null,
12
+ });
13
+ browserTabIdState.pendingId ??= null;
14
+ const browserTabIdAliases = (browserTabIdState.aliases ??= new Map());
15
+ let serverCached;
5
16
  function storageKey() {
6
17
  const appScope = isWorkspaceRuntime() ? appBasePath() : "";
7
18
  return appScope ? `${STORAGE_KEY}:${appScope}` : STORAGE_KEY;
@@ -21,28 +32,117 @@ function shouldReuseStoredTabId() {
21
32
  return navigation?.type === "reload" || navigation?.type === "back_forward";
22
33
  }
23
34
  export function getBrowserTabId() {
24
- if (cached)
25
- return cached;
26
35
  if (typeof window === "undefined") {
27
- cached = generate();
36
+ serverCached ??= generate();
37
+ return serverCached;
38
+ }
39
+ let key;
40
+ try {
41
+ key = storageKey();
42
+ }
43
+ catch {
44
+ return getOrCreatePendingId();
45
+ }
46
+ const cached = browserTabIdState.ids.get(key);
47
+ if (cached) {
48
+ const pending = browserTabIdState.pendingId;
49
+ if (pending && pending !== cached) {
50
+ browserTabIdAliases.set(pending, { storageKey: key, id: cached });
51
+ }
52
+ browserTabIdState.pendingId = null;
28
53
  return cached;
29
54
  }
55
+ let existing;
56
+ let storageReadable = true;
30
57
  try {
31
- const key = storageKey();
32
- const existing = sessionStorage.getItem(key);
58
+ existing = sessionStorage.getItem(key);
59
+ }
60
+ catch {
61
+ existing = null;
62
+ storageReadable = false;
63
+ }
64
+ const pending = browserTabIdState.pendingId;
65
+ if (pending) {
33
66
  if (existing &&
34
67
  SAFE_BROWSER_TAB_ID_RE.test(existing) &&
35
68
  shouldReuseStoredTabId()) {
36
- cached = existing;
69
+ browserTabIdState.pendingId = null;
70
+ browserTabIdState.ids.set(key, existing);
71
+ browserTabIdAliases.set(pending, { storageKey: key, id: existing });
37
72
  return existing;
38
73
  }
39
- const id = generate();
74
+ browserTabIdState.pendingId = null;
75
+ try {
76
+ sessionStorage.setItem(key, pending);
77
+ }
78
+ catch {
79
+ browserTabIdState.ids.set(key, pending);
80
+ return pending;
81
+ }
82
+ browserTabIdState.ids.set(key, pending);
83
+ return pending;
84
+ }
85
+ if (!storageReadable)
86
+ return getOrCreateMemoryId(key);
87
+ if (existing &&
88
+ SAFE_BROWSER_TAB_ID_RE.test(existing) &&
89
+ shouldReuseStoredTabId()) {
90
+ browserTabIdState.ids.set(key, existing);
91
+ return existing;
92
+ }
93
+ const id = generate();
94
+ try {
40
95
  sessionStorage.setItem(key, id);
41
- cached = id;
42
- return id;
43
96
  }
44
97
  catch {
45
- cached = generate();
46
- return cached;
98
+ browserTabIdState.ids.set(key, id);
99
+ return id;
100
+ }
101
+ browserTabIdState.ids.set(key, id);
102
+ return id;
103
+ }
104
+ export function resolveBrowserTabIdAlias(tabId) {
105
+ if (typeof tabId !== "string")
106
+ return undefined;
107
+ const alias = browserTabIdAliases.get(tabId);
108
+ if (!alias)
109
+ return undefined;
110
+ return browserTabIdState.ids.get(alias.storageKey) === alias.id
111
+ ? alias.id
112
+ : undefined;
113
+ }
114
+ export function isBrowserTabIdForCurrentDocument(tabId) {
115
+ if (typeof tabId !== "string")
116
+ return false;
117
+ if (browserTabIdState.pendingId === tabId)
118
+ return true;
119
+ for (const knownTabId of browserTabIdState.ids.values()) {
120
+ if (knownTabId === tabId)
121
+ return true;
47
122
  }
123
+ const alias = browserTabIdAliases.get(tabId);
124
+ if (!alias || resolveBrowserTabIdAlias(tabId) === undefined)
125
+ return false;
126
+ let currentStorageKey;
127
+ try {
128
+ currentStorageKey = storageKey();
129
+ }
130
+ catch (error) {
131
+ if (error instanceof WorkspaceAppMountResolutionError)
132
+ return false;
133
+ throw error;
134
+ }
135
+ return currentStorageKey === alias.storageKey;
136
+ }
137
+ function getOrCreateMemoryId(key) {
138
+ const cached = browserTabIdState.ids.get(key);
139
+ if (cached)
140
+ return cached;
141
+ const id = generate();
142
+ browserTabIdState.ids.set(key, id);
143
+ return id;
144
+ }
145
+ function getOrCreatePendingId() {
146
+ browserTabIdState.pendingId ??= generate();
147
+ return browserTabIdState.pendingId;
48
148
  }
@@ -1,4 +1,4 @@
1
- export { useCollaborativeDoc, isReconcileLeadClient, emailToColor, emailToName, dedupeCollabUsersByEmail, type CollabUser, type UseCollaborativeDocOptions, type UseCollaborativeDocResult, type CollabInitializationErrorCategory, type CollabInitializationState, type CollaborativeDocSyncResult, } from "../../collab/client.js";
1
+ export { useCollaborativeDoc, isReconcileLeadClient, emailToColor, emailToName, dedupeCollabUsersByEmail, type CollabUser, type UseCollaborativeDocOptions, type UseCollaborativeDocResult, type CollabInitializationErrorCategory, type CollabInitializationState, type CollaborativeDocSyncResult, type CollabUpdateAck, } from "../../collab/client.js";
2
2
  export { AGENT_CLIENT_ID } from "../../collab/agent-identity.js";
3
3
  export { usePresence, toNormalized, fromNormalized, type OtherPresence, type PresencePayload, type UsePresenceResult, type NormalizedPoint, } from "../../collab/presence.js";
4
4
  export { useFollowUser, type UseFollowUserOptions, type UseFollowUserResult, type ViewportDescriptor, } from "../../collab/follow-mode.js";