@agent-native/core 0.206.0-nightly-20261009124314 → 0.206.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/dist/action.d.ts +7 -1
- package/dist/app-config/extension-display-sources.d.ts +31 -0
- package/dist/app-config/extension-display-sources.js +45 -0
- package/dist/app-config/extensions.d.ts +7 -0
- package/dist/app-config/extensions.js +39 -0
- package/dist/app-config/index.d.ts +1 -0
- package/dist/app-config/index.js +1 -0
- package/dist/app-config/schema.d.ts +4 -0
- package/dist/app-config/schema.js +2 -0
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/agent-chat/index.js +1 -1
- package/dist/client/embed-auth.d.ts +2 -0
- package/dist/client/embed-auth.js +160 -12
- package/dist/client/host/index.d.ts +2 -2
- package/dist/client/host/index.js +2 -2
- package/dist/client/mcp-app-host.d.ts +1 -0
- package/dist/client/mcp-app-host.js +4 -1
- package/dist/client/session-replay.js +70 -6
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/client.js +2 -0
- package/dist/db/drizzle-config.js +2 -0
- package/dist/event-bus/bus.d.ts +6 -0
- package/dist/event-bus/bus.js +24 -1
- package/dist/event-bus/index.d.ts +1 -1
- package/dist/event-bus/index.js +1 -1
- package/dist/extensions/html-shell.d.ts +35 -2
- package/dist/extensions/html-shell.js +62 -5
- package/dist/extensions/iframe-csp.d.ts +20 -0
- package/dist/extensions/iframe-csp.js +30 -0
- package/dist/extensions/routes.js +17 -5
- package/dist/mcp/build-server.d.ts +15 -0
- package/dist/mcp/build-server.js +307 -91
- package/dist/mcp/connect-route.js +1 -0
- package/dist/mcp/embed-app.js +291 -31
- package/dist/mcp/oauth-route.js +3 -0
- package/dist/mcp/oauth-store.d.ts +2 -1
- package/dist/mcp/oauth-store.js +5 -6
- package/dist/mcp/oauth-token.d.ts +5 -0
- package/dist/mcp/oauth-token.js +17 -0
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/mcp/server.d.ts +2 -2
- package/dist/mcp/server.js +2 -2
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/index.d.ts +2 -2
- package/dist/org/workspace-app-access-request.d.ts +8 -0
- package/dist/org/workspace-app-access-request.js +20 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +6 -6
- package/dist/provider-api/actions/provider-api.d.ts +14 -14
- package/dist/resources/store.d.ts +22 -0
- package/dist/resources/store.js +88 -5
- package/dist/secrets/routes.d.ts +11 -11
- package/dist/server/action-routes.d.ts +2 -0
- package/dist/server/action-routes.js +187 -31
- package/dist/server/agent-chat/mcp-options.d.ts +3 -1
- package/dist/server/agent-chat-plugin.js +26 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-default-model-route.d.ts +1 -1
- package/dist/server/auth.js +11 -20
- package/dist/server/embed-route.js +5 -3
- package/dist/server/embed-session.d.ts +30 -1
- package/dist/server/embed-session.js +376 -30
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/request-context.d.ts +2 -0
- package/dist/server/request-context.js +25 -1
- package/dist/shared/embed-auth.d.ts +74 -0
- package/dist/shared/embed-auth.js +379 -10
- package/dist/triggers/actions/manage-automation.d.ts +2 -2
- package/dist/triggers/dispatcher.d.ts +11 -0
- package/dist/triggers/dispatcher.js +239 -50
- package/dist/triggers/index.d.ts +1 -1
- package/dist/triggers/index.js +1 -1
- package/docs/content/template-design-developers.mdx +1 -0
- package/package.json +4 -4
package/dist/action.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { type ActionChatUIConfig } from "./action-ui.js";
|
|
|
3
3
|
import type { ActionTool, AgentChatAttachment, AgentChatEvent } from "./agent/types.js";
|
|
4
4
|
import type { ActionAuditConfig } from "./audit/types.js";
|
|
5
5
|
import { type ActionAccessConfig } from "./authorization/action-access-runtime.js";
|
|
6
|
-
export type ActionCaller = "tool" | "http" | "frontend" | "mcp-widget" | "cli" | "mcp" | "webmcp" | "a2a" | "automation";
|
|
6
|
+
export type ActionCaller = "tool" | "http" | "frontend" | "mcp-widget" | "mcp-widget-write" | "cli" | "mcp" | "webmcp" | "a2a" | "automation";
|
|
7
7
|
export interface ActionAutomationContext {
|
|
8
8
|
triggerId: string;
|
|
9
9
|
triggerName: string;
|
|
@@ -38,6 +38,12 @@ export interface ActionRunContext {
|
|
|
38
38
|
actionName?: string;
|
|
39
39
|
/** Present only on frontend GETs authorized by a scoped directory-widget read capability. */
|
|
40
40
|
mcpDirectoryWidgetReadOnly?: true;
|
|
41
|
+
/** Present only on frontend mutations authorized by a scoped directory-widget write capability. */
|
|
42
|
+
mcpDirectoryWidgetWrite?: {
|
|
43
|
+
appId: string;
|
|
44
|
+
resourceIds: Record<string, string>;
|
|
45
|
+
actionNames: readonly string[];
|
|
46
|
+
};
|
|
41
47
|
threadId?: string;
|
|
42
48
|
runId?: string;
|
|
43
49
|
turnId?: string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the extension iframe loads for img-src / media-src when a deployment
|
|
3
|
+
* has not narrowed or widened them.
|
|
4
|
+
*
|
|
5
|
+
* This module has no imports on purpose: the client-rendered extension shell
|
|
6
|
+
* (`extensions/html-shell.ts`, used by `ExtensionViewer` and
|
|
7
|
+
* `InlineExtensionFrame` in the browser) builds its default CSP from this list,
|
|
8
|
+
* and must not pull the server app-config store into browser bundles.
|
|
9
|
+
*
|
|
10
|
+
* Frozen so an importing module cannot mutate the shared default. The schema
|
|
11
|
+
* hands out a fresh copy per parse, so a caller mutating its resolved config
|
|
12
|
+
* cannot reach this list either.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEFAULT_EXTENSION_DISPLAY_SOURCES: readonly string[];
|
|
15
|
+
/**
|
|
16
|
+
* One CSP source expression with no whitespace or `;`, so a configured value
|
|
17
|
+
* can extend `img-src` / `media-src` but can never terminate the directive and
|
|
18
|
+
* append a new one. Shared by the app-config schema and the CSP builder, so the
|
|
19
|
+
* value that is validated at config time is validated again where it is
|
|
20
|
+
* interpolated.
|
|
21
|
+
*/
|
|
22
|
+
export declare const EXTENSION_DISPLAY_SOURCE_PATTERN: RegExp;
|
|
23
|
+
export declare const EXTENSION_DISPLAY_SOURCE_MESSAGE = "must be one CSP source expression: 'self', 'none', a scheme such as https: or blob:, or an http(s) origin such as https://cdn.example.com";
|
|
24
|
+
export declare const EXTENSION_DISPLAY_SOURCE_NONE_MESSAGE = "'none' must be the only source in the list";
|
|
25
|
+
/**
|
|
26
|
+
* Returns why `sources` cannot be interpolated into an img-src / media-src
|
|
27
|
+
* directive, or `null` when it can. Mirrors the app-config schema: a non-empty
|
|
28
|
+
* list of single source expressions, with `'none'` only on its own (CSP drops
|
|
29
|
+
* `'none'` when it is mixed with other sources).
|
|
30
|
+
*/
|
|
31
|
+
export declare function extensionDisplaySourcesError(sources: unknown): string | null;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the extension iframe loads for img-src / media-src when a deployment
|
|
3
|
+
* has not narrowed or widened them.
|
|
4
|
+
*
|
|
5
|
+
* This module has no imports on purpose: the client-rendered extension shell
|
|
6
|
+
* (`extensions/html-shell.ts`, used by `ExtensionViewer` and
|
|
7
|
+
* `InlineExtensionFrame` in the browser) builds its default CSP from this list,
|
|
8
|
+
* and must not pull the server app-config store into browser bundles.
|
|
9
|
+
*
|
|
10
|
+
* Frozen so an importing module cannot mutate the shared default. The schema
|
|
11
|
+
* hands out a fresh copy per parse, so a caller mutating its resolved config
|
|
12
|
+
* cannot reach this list either.
|
|
13
|
+
*/
|
|
14
|
+
export const DEFAULT_EXTENSION_DISPLAY_SOURCES = Object.freeze(["'self'", "data:", "blob:"]);
|
|
15
|
+
/**
|
|
16
|
+
* One CSP source expression with no whitespace or `;`, so a configured value
|
|
17
|
+
* can extend `img-src` / `media-src` but can never terminate the directive and
|
|
18
|
+
* append a new one. Shared by the app-config schema and the CSP builder, so the
|
|
19
|
+
* value that is validated at config time is validated again where it is
|
|
20
|
+
* interpolated.
|
|
21
|
+
*/
|
|
22
|
+
export const EXTENSION_DISPLAY_SOURCE_PATTERN = /^(?:'self'|'none'|(?:https?|data|blob|mediastream):|https?:\/\/(?:\*\.)?[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*(?::\d{1,5})?)$/;
|
|
23
|
+
export const EXTENSION_DISPLAY_SOURCE_MESSAGE = "must be one CSP source expression: 'self', 'none', a scheme such as https: or blob:, or an http(s) origin such as https://cdn.example.com";
|
|
24
|
+
export const EXTENSION_DISPLAY_SOURCE_NONE_MESSAGE = "'none' must be the only source in the list";
|
|
25
|
+
/**
|
|
26
|
+
* Returns why `sources` cannot be interpolated into an img-src / media-src
|
|
27
|
+
* directive, or `null` when it can. Mirrors the app-config schema: a non-empty
|
|
28
|
+
* list of single source expressions, with `'none'` only on its own (CSP drops
|
|
29
|
+
* `'none'` when it is mixed with other sources).
|
|
30
|
+
*/
|
|
31
|
+
export function extensionDisplaySourcesError(sources) {
|
|
32
|
+
if (!Array.isArray(sources) || sources.length === 0) {
|
|
33
|
+
return "must be a non-empty list of CSP source expressions";
|
|
34
|
+
}
|
|
35
|
+
for (const source of sources) {
|
|
36
|
+
if (typeof source !== "string" ||
|
|
37
|
+
!EXTENSION_DISPLAY_SOURCE_PATTERN.test(source)) {
|
|
38
|
+
return `${JSON.stringify(source)} ${EXTENSION_DISPLAY_SOURCE_MESSAGE}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (sources.includes("'none'") && sources.length !== 1) {
|
|
42
|
+
return EXTENSION_DISPLAY_SOURCE_NONE_MESSAGE;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { DEFAULT_EXTENSION_DISPLAY_SOURCES } from "./extension-display-sources.js";
|
|
3
|
+
export { DEFAULT_EXTENSION_DISPLAY_SOURCES };
|
|
4
|
+
export declare const extensionsConfig: z.ZodObject<{
|
|
5
|
+
iframeImageSources: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
6
|
+
iframeMediaSources: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
7
|
+
}, z.core.$strip>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { DEFAULT_EXTENSION_DISPLAY_SOURCES, EXTENSION_DISPLAY_SOURCE_MESSAGE, EXTENSION_DISPLAY_SOURCE_NONE_MESSAGE, EXTENSION_DISPLAY_SOURCE_PATTERN, } from "./extension-display-sources.js";
|
|
3
|
+
/**
|
|
4
|
+
* SECURITY — these values are interpolated into the sandboxed extension
|
|
5
|
+
* iframe's Content-Security-Policy. A source expression is validated as a
|
|
6
|
+
* single token with no whitespace or `;`, so a configured value can extend
|
|
7
|
+
* `img-src` / `media-src` but can never terminate the directive and append a
|
|
8
|
+
* new one. `connect-src` is not configurable and stays `'self'`. A remote
|
|
9
|
+
* `img-src` / `media-src` origin is still an explicit egress permission:
|
|
10
|
+
* the browser requests that URL, so extension script can encode data into
|
|
11
|
+
* it. The host bridge is the permission-gated path for API calls, not a
|
|
12
|
+
* claim that these sources are egress-free.
|
|
13
|
+
*/
|
|
14
|
+
const cspSource = z
|
|
15
|
+
.string()
|
|
16
|
+
.regex(EXTENSION_DISPLAY_SOURCE_PATTERN, EXTENSION_DISPLAY_SOURCE_MESSAGE);
|
|
17
|
+
// The iframe shell builds its default CSP from this same list, so the declared
|
|
18
|
+
// default and the shipped policy cannot drift apart. It lives in an
|
|
19
|
+
// import-free module so the client-rendered shell can use it without pulling
|
|
20
|
+
// the app-config store into browser bundles.
|
|
21
|
+
export { DEFAULT_EXTENSION_DISPLAY_SOURCES };
|
|
22
|
+
function displaySources(doc) {
|
|
23
|
+
return (z
|
|
24
|
+
.array(cspSource)
|
|
25
|
+
.min(1)
|
|
26
|
+
// CSP drops a directive that mixes 'none' with other sources, so
|
|
27
|
+
// ["'none'", "https:"] reads as a deny-all that still allows https.
|
|
28
|
+
// Reject the combination instead of emitting a policy that does not
|
|
29
|
+
// say what the config says.
|
|
30
|
+
.refine((sources) => !sources.includes("'none'") || sources.length === 1, { error: EXTENSION_DISPLAY_SOURCE_NONE_MESSAGE })
|
|
31
|
+
// A fresh copy per parse: the shared default is frozen, and a caller
|
|
32
|
+
// mutating its resolved config must not reach the next parse.
|
|
33
|
+
.default(() => [...DEFAULT_EXTENSION_DISPLAY_SOURCES])
|
|
34
|
+
.meta({ doc }));
|
|
35
|
+
}
|
|
36
|
+
export const extensionsConfig = z.object({
|
|
37
|
+
iframeImageSources: displaySources("Replaces the image sources the sandboxed extension iframe may load. Defaults to 'self' data: blob:, which blocks every remote image; add https: or one origin to show product photos, avatars, or CDN assets. Every allowed remote origin is an explicit egress permission: the browser requests that URL. connect-src stays 'self'.").meta({ env: "AGENT_NATIVE_EXTENSION_IFRAME_IMAGE_SOURCES" }),
|
|
38
|
+
iframeMediaSources: displaySources("Replaces the media sources the sandboxed extension iframe may load, with the same syntax and the same default as `extensions.iframeImageSources`. A remote entry is an explicit egress permission: the browser requests that URL.").meta({ env: "AGENT_NATIVE_EXTENSION_IFRAME_MEDIA_SOURCES" }),
|
|
39
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { defineAppConfig, enterpriseAuthAdaptersBuilt, getAppConfig, resetAppConfigForTests, } from "./store.js";
|
|
2
2
|
export { resolveAppHomePath } from "./app-identity.js";
|
|
3
3
|
export { AppConfigurationError } from "./configuration-error.js";
|
|
4
|
+
export { DEFAULT_EXTENSION_DISPLAY_SOURCES } from "./extensions.js";
|
|
4
5
|
export { appConfigSchema, type AppConfig, type AppConfigInput, } from "./schema.js";
|
package/dist/app-config/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { defineAppConfig, enterpriseAuthAdaptersBuilt, getAppConfig, resetAppConfigForTests, } from "./store.js";
|
|
2
2
|
export { resolveAppHomePath } from "./app-identity.js";
|
|
3
3
|
export { AppConfigurationError } from "./configuration-error.js";
|
|
4
|
+
export { DEFAULT_EXTENSION_DISPLAY_SOURCES } from "./extensions.js";
|
|
4
5
|
export { appConfigSchema, } from "./schema.js";
|
|
@@ -71,6 +71,10 @@ export declare const appConfigSchema: z.ZodObject<{
|
|
|
71
71
|
workspaceOwnerEmails: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
72
72
|
defaultOwnerEmail: z.ZodOptional<z.ZodString>;
|
|
73
73
|
}, z.core.$strip>>;
|
|
74
|
+
extensions: z.ZodPrefault<z.ZodObject<{
|
|
75
|
+
iframeImageSources: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
76
|
+
iframeMediaSources: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
77
|
+
}, z.core.$strip>>;
|
|
74
78
|
integrations: z.ZodPrefault<z.ZodObject<{
|
|
75
79
|
durableDispatch: z.ZodOptional<z.ZodBoolean>;
|
|
76
80
|
allowUnverifiedWebhooks: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -6,6 +6,7 @@ import { analyticsConfig } from "./analytics.js";
|
|
|
6
6
|
import { appConfig } from "./app.js";
|
|
7
7
|
import { authConfig } from "./auth.js";
|
|
8
8
|
import { dispatchConfig } from "./dispatch.js";
|
|
9
|
+
import { extensionsConfig } from "./extensions.js";
|
|
9
10
|
import { integrationsConfig } from "./integrations.js";
|
|
10
11
|
import { launchDarklyConfig } from "./launchdarkly.js";
|
|
11
12
|
import { mcpConfig } from "./mcp.js";
|
|
@@ -26,6 +27,7 @@ export const appConfigSchema = z.object({
|
|
|
26
27
|
app: appConfig.prefault({}),
|
|
27
28
|
auth: authConfig.prefault({}),
|
|
28
29
|
dispatch: dispatchConfig.prefault({}),
|
|
30
|
+
extensions: extensionsConfig.prefault({}),
|
|
29
31
|
integrations: integrationsConfig.prefault({}),
|
|
30
32
|
launchDarkly: launchDarklyConfig.prefault({}),
|
|
31
33
|
mcp: mcpConfig.prefault({}),
|
|
@@ -13,7 +13,7 @@ export { useSendToAgentChat } from "../use-send-to-agent-chat.js";
|
|
|
13
13
|
export { DEFAULT_MODEL, CHAT_MODEL_SELECTION_CHANGED_EVENT, chatModelSelectionStorageKey, loadChatModelCatalog, useChatModels, type UseChatModelsResult, type UseChatModelsOptions, type PersistedModelSelection, type EngineModelGroup, } from "../use-chat-models.js";
|
|
14
14
|
export { fetchAgentEngineConfiguredState, useAgentEngineConfigured, type AgentEngineConfiguredState, type FetchAgentEngineConfiguredStateOptions, type UseAgentEngineConfiguredResult, } from "../use-agent-engine-configured.js";
|
|
15
15
|
export { AGENT_CHAT_AI_SETUP_REQUIRED_CODE, AgentChatAiSetupRequiredError, ensureAgentEngineReadiness, getAgentEngineReadiness, invalidateAgentEngineReadiness, isAgentChatAiSetupRequiredError, isLocalRuntimeEngine, requireAgentEngineConfiguredForDispatch, subscribeAgentEngineReadiness, LOCAL_RUNTIME_ENGINE_IDS, agentEngineStatusUrlForChatApi, type AgentEngineReadinessSource, } from "../agent-engine-readiness.js";
|
|
16
|
-
export { AGENT_NATIVE_MCP_APP_HOST_MESSAGE_TYPES, getMcpAppHostContext, initializeMcpAppHost, isMcpAppWidgetEmbed, isOpenAiMcpAppHost, openMcpAppHostLink, requestMcpAppDisplayMode, sendMcpAppHostMessage, updateMcpAppModelContext, useIsMcpAppWidgetEmbed, useMcpAppHostContext, type AgentNativeMcpAppHostMessageType, type McpAppDisplayMode, type McpAppHostChatMessage, type McpAppHostCapabilities, type McpAppHostContext, type McpAppHostInfo, type McpAppHostContextSnapshot, type McpAppModelContextContentPart, type McpAppModelContextUpdate, } from "../mcp-app-host.js";
|
|
16
|
+
export { AGENT_NATIVE_MCP_APP_HOST_MESSAGE_TYPES, getMcpAppHostContext, initializeMcpAppHost, isMcpAppWidgetEmbed, isOpenAiMcpAppHost, openMcpAppHostLink, requestMcpAppDisplayMode, sendMcpAppHostMessage, updateMcpAppModelContext, useIsMcpAppWidgetEmbed, useIsMcpDirectoryWidgetReadOnlyEmbed, useIsMcpDirectoryWidgetWriteEmbed, useMcpAppHostContext, type AgentNativeMcpAppHostMessageType, type McpAppDisplayMode, type McpAppHostChatMessage, type McpAppHostCapabilities, type McpAppHostContext, type McpAppHostInfo, type McpAppHostContextSnapshot, type McpAppModelContextContentPart, type McpAppModelContextUpdate, } from "../mcp-app-host.js";
|
|
17
17
|
export { buildDynamicAgentSuggestions, dedupeSuggestions, mergeAgentSuggestions, normalizeAgentDynamicSuggestionsConfig, useAgentDynamicSuggestions, useAgentDynamicSuggestionsResult, type AgentDynamicSuggestionContext, type AgentDynamicSuggestionsConfig, type AgentDynamicSuggestionsOption, } from "../dynamic-suggestions.js";
|
|
18
18
|
export { clearChatStorage } from "../chat/storage.js";
|
|
19
19
|
export { isAssistantChatHistoryVersion, coerceAssistantChatHistoryDate, type AssistantChatHistoryDate, type AssistantChatHistoryVersion, } from "../chat/assistant-chat-history-version.js";
|
|
@@ -13,7 +13,7 @@ export { useSendToAgentChat } from "../use-send-to-agent-chat.js";
|
|
|
13
13
|
export { DEFAULT_MODEL, CHAT_MODEL_SELECTION_CHANGED_EVENT, chatModelSelectionStorageKey, loadChatModelCatalog, useChatModels, } from "../use-chat-models.js";
|
|
14
14
|
export { fetchAgentEngineConfiguredState, useAgentEngineConfigured, } from "../use-agent-engine-configured.js";
|
|
15
15
|
export { AGENT_CHAT_AI_SETUP_REQUIRED_CODE, AgentChatAiSetupRequiredError, ensureAgentEngineReadiness, getAgentEngineReadiness, invalidateAgentEngineReadiness, isAgentChatAiSetupRequiredError, isLocalRuntimeEngine, requireAgentEngineConfiguredForDispatch, subscribeAgentEngineReadiness, LOCAL_RUNTIME_ENGINE_IDS, agentEngineStatusUrlForChatApi, } from "../agent-engine-readiness.js";
|
|
16
|
-
export { AGENT_NATIVE_MCP_APP_HOST_MESSAGE_TYPES, getMcpAppHostContext, initializeMcpAppHost, isMcpAppWidgetEmbed, isOpenAiMcpAppHost, openMcpAppHostLink, requestMcpAppDisplayMode, sendMcpAppHostMessage, updateMcpAppModelContext, useIsMcpAppWidgetEmbed, useMcpAppHostContext, } from "../mcp-app-host.js";
|
|
16
|
+
export { AGENT_NATIVE_MCP_APP_HOST_MESSAGE_TYPES, getMcpAppHostContext, initializeMcpAppHost, isMcpAppWidgetEmbed, isOpenAiMcpAppHost, openMcpAppHostLink, requestMcpAppDisplayMode, sendMcpAppHostMessage, updateMcpAppModelContext, useIsMcpAppWidgetEmbed, useIsMcpDirectoryWidgetReadOnlyEmbed, useIsMcpDirectoryWidgetWriteEmbed, useMcpAppHostContext, } from "../mcp-app-host.js";
|
|
17
17
|
export { buildDynamicAgentSuggestions, dedupeSuggestions, mergeAgentSuggestions, normalizeAgentDynamicSuggestionsConfig, useAgentDynamicSuggestions, useAgentDynamicSuggestionsResult, } from "../dynamic-suggestions.js";
|
|
18
18
|
export { clearChatStorage } from "../chat/storage.js";
|
|
19
19
|
export { isAssistantChatHistoryVersion, coerceAssistantChatHistoryDate, } from "../chat/assistant-chat-history-version.js";
|
|
@@ -11,12 +11,14 @@ export declare function getEmbedAuthToken(): string | null;
|
|
|
11
11
|
* allowed.
|
|
12
12
|
*/
|
|
13
13
|
export declare function hasMcpDirectoryWidgetCapabilityToken(): boolean;
|
|
14
|
+
export declare function hasMcpDirectoryWidgetWriteCapabilityToken(): boolean;
|
|
14
15
|
/**
|
|
15
16
|
* True when this document is the app nested in an MCP App widget shell (the
|
|
16
17
|
* chat bridge is active) on a directory-widget read capability, which the
|
|
17
18
|
* server limits to the widget's own resource reads.
|
|
18
19
|
*/
|
|
19
20
|
export declare function isMcpDirectoryWidgetReadOnlyEmbed(): boolean;
|
|
21
|
+
export declare function isMcpDirectoryWidgetWriteEmbed(): boolean;
|
|
20
22
|
export declare function isEmbedAuthActive(): boolean;
|
|
21
23
|
export declare function _resetEmbedAuthForTests(): void;
|
|
22
24
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EMBED_MODE_QUERY_PARAM, EMBED_START_PATH, EMBED_TARGET_HEADER, EMBED_TARGET_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, MCP_APP_CHAT_BRIDGE_QUERY_PARAM, } from "../shared/embed-auth.js";
|
|
2
|
-
import { isMcpDirectoryWidgetReadCapabilityScope } from "../shared/embed-auth.js";
|
|
1
|
+
import { EMBED_MODE_QUERY_PARAM, EMBED_START_PATH, EMBED_TARGET_HEADER, EMBED_TARGET_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, MCP_APP_CHAT_BRIDGE_QUERY_PARAM, MCP_DIRECTORY_WIDGET_SESSION_EXPIRED_HEADER, } from "../shared/embed-auth.js";
|
|
2
|
+
import { isMcpDirectoryWidgetReadCapabilityScope, isMcpDirectoryWidgetWriteCapabilityScope, } from "../shared/embed-auth.js";
|
|
3
3
|
import { FRAMEWORK_INTERNAL_ROUTE_PREFIX } from "../shared/framework-route-prefix.js";
|
|
4
4
|
import { MCP_APP_HOST_FILL_ATTRIBUTE } from "../shared/mcp-app-display.js";
|
|
5
5
|
import { EMBED_TOKEN_STORAGE_KEY, MCP_CHAT_BRIDGE_STORAGE_KEY, } from "../shared/mcp-app-widget-embed.js";
|
|
@@ -7,11 +7,16 @@ import { SIGN_IN_ENTRY_PATH, SIGN_IN_LEGACY_ENTRY_PATH, } from "../shared/sign-i
|
|
|
7
7
|
import { frameworkRoutePrefix } from "./api-path.js";
|
|
8
8
|
let installed = false;
|
|
9
9
|
let memoryToken = null;
|
|
10
|
+
let renewedFromUrlToken = null;
|
|
11
|
+
let pendingWidgetSessionRenewal = null;
|
|
12
|
+
let widgetSessionRenewalRequestId = 0;
|
|
10
13
|
let mcpChatBridgeActive = false;
|
|
11
14
|
let mcpChatBridgeScope = null;
|
|
12
15
|
const AUTH_FAILURE_COOLDOWN_MS = 60_000;
|
|
13
16
|
const GUARDED_METHODS = new Set(["GET", "HEAD"]);
|
|
14
17
|
const AUTH_FAILURE_HEADER = "x-agent-native-auth-circuit-breaker";
|
|
18
|
+
const EMBED_TOKEN_RENEWED_FROM_STORAGE_KEY = `${EMBED_TOKEN_STORAGE_KEY}:renewed-from`;
|
|
19
|
+
const EMBED_SESSION_RENEWAL_TIMEOUT_MS = 15_000;
|
|
15
20
|
const MCP_CHAT_BRIDGE_VIEWPORT_STYLE_ID = "agent-native-mcp-chat-bridge-viewport";
|
|
16
21
|
const MCP_CHAT_BRIDGE_VIEWPORT_HEIGHT = 560;
|
|
17
22
|
let pendingMcpChatBridgeViewportNotification = null;
|
|
@@ -43,7 +48,7 @@ export function readEmbedMcpChatBridgeFlagFromUrl() {
|
|
|
43
48
|
return value === "1" || value === "true";
|
|
44
49
|
}
|
|
45
50
|
function currentMcpChatBridgeScope(win) {
|
|
46
|
-
return
|
|
51
|
+
return getEmbedAuthToken() ?? memoryToken ?? storedToken(win);
|
|
47
52
|
}
|
|
48
53
|
function clearMcpChatBridge(win) {
|
|
49
54
|
mcpChatBridgeActive = false;
|
|
@@ -142,6 +147,31 @@ export function getEmbedAuthToken() {
|
|
|
142
147
|
return null;
|
|
143
148
|
const fromUrl = readTokenFromUrl(win);
|
|
144
149
|
if (fromUrl) {
|
|
150
|
+
let renewedFrom = renewedFromUrlToken;
|
|
151
|
+
if (!renewedFrom) {
|
|
152
|
+
try {
|
|
153
|
+
renewedFrom =
|
|
154
|
+
win.sessionStorage?.getItem(EMBED_TOKEN_RENEWED_FROM_STORAGE_KEY) ??
|
|
155
|
+
null;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
renewedFrom = null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (renewedFrom === fromUrl) {
|
|
162
|
+
const refreshed = memoryToken ?? storedToken(win);
|
|
163
|
+
if (refreshed && refreshed !== fromUrl)
|
|
164
|
+
return refreshed;
|
|
165
|
+
}
|
|
166
|
+
if (renewedFrom && renewedFrom !== fromUrl) {
|
|
167
|
+
renewedFromUrlToken = null;
|
|
168
|
+
try {
|
|
169
|
+
win.sessionStorage?.removeItem(EMBED_TOKEN_RENEWED_FROM_STORAGE_KEY);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// coercion-ok: cleanup is best-effort; the refreshed URL token still supersedes this marker.
|
|
173
|
+
}
|
|
174
|
+
}
|
|
145
175
|
storeToken(fromUrl, win);
|
|
146
176
|
return fromUrl;
|
|
147
177
|
}
|
|
@@ -160,7 +190,7 @@ function readEmbedTokenScope(token) {
|
|
|
160
190
|
return undefined;
|
|
161
191
|
}
|
|
162
192
|
}
|
|
163
|
-
let
|
|
193
|
+
let widgetScopeCache = null;
|
|
164
194
|
/**
|
|
165
195
|
* True when the embed credential is a directory-widget read capability, which
|
|
166
196
|
* only the MCP App widget flows mint. It stays readable after a client
|
|
@@ -173,13 +203,29 @@ export function hasMcpDirectoryWidgetCapabilityToken() {
|
|
|
173
203
|
const token = getEmbedAuthToken();
|
|
174
204
|
if (!token)
|
|
175
205
|
return false;
|
|
176
|
-
if (
|
|
177
|
-
|
|
206
|
+
if (widgetScopeCache?.token !== token) {
|
|
207
|
+
const scope = readEmbedTokenScope(token);
|
|
208
|
+
widgetScopeCache = {
|
|
178
209
|
token,
|
|
179
|
-
readOnly: isMcpDirectoryWidgetReadCapabilityScope(
|
|
210
|
+
readOnly: isMcpDirectoryWidgetReadCapabilityScope(scope),
|
|
211
|
+
writable: isMcpDirectoryWidgetWriteCapabilityScope(scope),
|
|
180
212
|
};
|
|
181
213
|
}
|
|
182
|
-
return
|
|
214
|
+
return widgetScopeCache.readOnly || widgetScopeCache.writable;
|
|
215
|
+
}
|
|
216
|
+
export function hasMcpDirectoryWidgetWriteCapabilityToken() {
|
|
217
|
+
const token = getEmbedAuthToken();
|
|
218
|
+
if (!token)
|
|
219
|
+
return false;
|
|
220
|
+
if (widgetScopeCache?.token !== token) {
|
|
221
|
+
const scope = readEmbedTokenScope(token);
|
|
222
|
+
widgetScopeCache = {
|
|
223
|
+
token,
|
|
224
|
+
readOnly: isMcpDirectoryWidgetReadCapabilityScope(scope),
|
|
225
|
+
writable: isMcpDirectoryWidgetWriteCapabilityScope(scope),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return widgetScopeCache.writable;
|
|
183
229
|
}
|
|
184
230
|
/**
|
|
185
231
|
* True when this document is the app nested in an MCP App widget shell (the
|
|
@@ -187,7 +233,21 @@ export function hasMcpDirectoryWidgetCapabilityToken() {
|
|
|
187
233
|
* server limits to the widget's own resource reads.
|
|
188
234
|
*/
|
|
189
235
|
export function isMcpDirectoryWidgetReadOnlyEmbed() {
|
|
190
|
-
|
|
236
|
+
const token = getEmbedAuthToken();
|
|
237
|
+
if (!token)
|
|
238
|
+
return false;
|
|
239
|
+
if (widgetScopeCache?.token !== token) {
|
|
240
|
+
const scope = readEmbedTokenScope(token);
|
|
241
|
+
widgetScopeCache = {
|
|
242
|
+
token,
|
|
243
|
+
readOnly: isMcpDirectoryWidgetReadCapabilityScope(scope),
|
|
244
|
+
writable: isMcpDirectoryWidgetWriteCapabilityScope(scope),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return widgetScopeCache.readOnly && isEmbedMcpChatBridgeActive();
|
|
248
|
+
}
|
|
249
|
+
export function isMcpDirectoryWidgetWriteEmbed() {
|
|
250
|
+
return (hasMcpDirectoryWidgetWriteCapabilityToken() && isEmbedMcpChatBridgeActive());
|
|
191
251
|
}
|
|
192
252
|
export function isEmbedAuthActive() {
|
|
193
253
|
const win = browserWindow();
|
|
@@ -303,7 +363,10 @@ export function _resetEmbedAuthForTests() {
|
|
|
303
363
|
}
|
|
304
364
|
installed = false;
|
|
305
365
|
memoryToken = null;
|
|
306
|
-
|
|
366
|
+
renewedFromUrlToken = null;
|
|
367
|
+
pendingWidgetSessionRenewal = null;
|
|
368
|
+
widgetSessionRenewalRequestId = 0;
|
|
369
|
+
widgetScopeCache = null;
|
|
307
370
|
mcpChatBridgeActive = false;
|
|
308
371
|
mcpChatBridgeScope = null;
|
|
309
372
|
authFailureCache.clear();
|
|
@@ -526,6 +589,63 @@ function requestUrlAndKey(input, init, win) {
|
|
|
526
589
|
shouldGuard: shouldGuardAuthFailure(method, url),
|
|
527
590
|
};
|
|
528
591
|
}
|
|
592
|
+
function requestWidgetSessionRenewal(win) {
|
|
593
|
+
if (pendingWidgetSessionRenewal)
|
|
594
|
+
return pendingWidgetSessionRenewal;
|
|
595
|
+
if (!win.parent || win.parent === win)
|
|
596
|
+
return Promise.resolve(false);
|
|
597
|
+
widgetSessionRenewalRequestId += 1;
|
|
598
|
+
const requestId = `embed-renewal-${Date.now()}-${widgetSessionRenewalRequestId}`;
|
|
599
|
+
const renewal = new Promise((resolve) => {
|
|
600
|
+
let settled = false;
|
|
601
|
+
let timeoutId = null;
|
|
602
|
+
const finish = (succeeded) => {
|
|
603
|
+
if (settled)
|
|
604
|
+
return;
|
|
605
|
+
settled = true;
|
|
606
|
+
if (timeoutId !== null)
|
|
607
|
+
win.clearTimeout(timeoutId);
|
|
608
|
+
win.removeEventListener("message", onMessage);
|
|
609
|
+
resolve(succeeded);
|
|
610
|
+
};
|
|
611
|
+
const onMessage = (event) => {
|
|
612
|
+
if (event.source !== win.parent)
|
|
613
|
+
return;
|
|
614
|
+
const message = event.data;
|
|
615
|
+
const data = message && typeof message === "object" ? message.data : null;
|
|
616
|
+
if (message?.type !== "agentNative.embedSessionRenewed" ||
|
|
617
|
+
data?.requestId !== requestId) {
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
if (data.ok !== true) {
|
|
621
|
+
finish(false);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
try {
|
|
625
|
+
win.parent.postMessage({ type: "agentNative.embedSessionRenewalApplied", requestId }, "*");
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
console.warn("[agent-native] could not acknowledge embedded session renewal", error);
|
|
629
|
+
}
|
|
630
|
+
finish(true);
|
|
631
|
+
};
|
|
632
|
+
win.addEventListener("message", onMessage);
|
|
633
|
+
timeoutId = win.setTimeout(() => finish(false), EMBED_SESSION_RENEWAL_TIMEOUT_MS);
|
|
634
|
+
try {
|
|
635
|
+
win.parent.postMessage({
|
|
636
|
+
type: "agentNative.embedSessionExpired",
|
|
637
|
+
data: { requestId },
|
|
638
|
+
}, "*");
|
|
639
|
+
}
|
|
640
|
+
catch {
|
|
641
|
+
finish(false);
|
|
642
|
+
}
|
|
643
|
+
}).finally(() => {
|
|
644
|
+
pendingWidgetSessionRenewal = null;
|
|
645
|
+
});
|
|
646
|
+
pendingWidgetSessionRenewal = renewal;
|
|
647
|
+
return renewal;
|
|
648
|
+
}
|
|
529
649
|
export function ensureEmbedAuthFetchInterceptor() {
|
|
530
650
|
const win = browserWindow();
|
|
531
651
|
if (!win)
|
|
@@ -534,7 +654,7 @@ export function ensureEmbedAuthFetchInterceptor() {
|
|
|
534
654
|
markEmbedMcpChatBridgeActive();
|
|
535
655
|
const urlToken = readTokenFromUrl(win);
|
|
536
656
|
if (urlToken) {
|
|
537
|
-
|
|
657
|
+
getEmbedAuthToken();
|
|
538
658
|
stripTokenFromUrl(win);
|
|
539
659
|
}
|
|
540
660
|
ensureMcpChatBridgeViewportClamp(win);
|
|
@@ -562,7 +682,35 @@ export function ensureEmbedAuthFetchInterceptor() {
|
|
|
562
682
|
if (token && sameOrigin(input, win)) {
|
|
563
683
|
[fetchInput, fetchInit] = withEmbedAuthHeaders(input, init, token, win);
|
|
564
684
|
}
|
|
565
|
-
const
|
|
685
|
+
const canRenewWidgetSession = Boolean(token) &&
|
|
686
|
+
isMcpDirectoryWidgetWriteEmbed() &&
|
|
687
|
+
request &&
|
|
688
|
+
sameOrigin(input, win);
|
|
689
|
+
let replayRequest = null;
|
|
690
|
+
let firstRequest = null;
|
|
691
|
+
if (canRenewWidgetSession) {
|
|
692
|
+
try {
|
|
693
|
+
firstRequest = new Request(fetchInput, fetchInit);
|
|
694
|
+
replayRequest = firstRequest.clone();
|
|
695
|
+
}
|
|
696
|
+
catch {
|
|
697
|
+
// coercion-ok: non-cloneable streamed writes retain their original response and are not replayed.
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
let response = firstRequest
|
|
701
|
+
? await originalFetch(firstRequest)
|
|
702
|
+
: await originalFetch(fetchInput, fetchInit);
|
|
703
|
+
if (response.status === 401 && canRenewWidgetSession) {
|
|
704
|
+
const sessionExpired = response.headers.get(MCP_DIRECTORY_WIDGET_SESSION_EXPIRED_HEADER) ===
|
|
705
|
+
"1";
|
|
706
|
+
if (sessionExpired && replayRequest && token) {
|
|
707
|
+
const renewed = await requestWidgetSessionRenewal(win);
|
|
708
|
+
if (renewed) {
|
|
709
|
+
const [retryInput, retryInit] = withEmbedAuthHeaders(replayRequest, undefined, token, win);
|
|
710
|
+
response = await originalFetch(retryInput, retryInit);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
566
714
|
if (request?.shouldGuard && isAuthFailureStatus(response.status)) {
|
|
567
715
|
await recordAuthFailure(request.key, response);
|
|
568
716
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export { initializeAgentNativeClient } from "../client-bootstrap.js";
|
|
2
2
|
export { agentNativeApiDisabledReason, AgentNativeApiDisabledError, setAgentNativeApiDisabled, } from "../api-surface.js";
|
|
3
|
-
export { ensureEmbedAuthFetchInterceptor, getEmbedAuthToken, isEmbedAuthActive, isEmbedMcpChatBridgeActive, isMcpDirectoryWidgetReadOnlyEmbed, } from "../embed-auth.js";
|
|
3
|
+
export { ensureEmbedAuthFetchInterceptor, getEmbedAuthToken, isEmbedAuthActive, isEmbedMcpChatBridgeActive, isMcpDirectoryWidgetReadOnlyEmbed, isMcpDirectoryWidgetWriteEmbed, } from "../embed-auth.js";
|
|
4
4
|
export { sendToFrame, onFrameMessage, requestUserInfo, getFrameOrigin, getFramePostMessageTargetOrigin, getCallbackOrigin, oauthRedirectUri, isInFrame, isTrustedFrameMessage, enterStyleEditing, enterTextEditing, exitSelectionMode, type UserInfo, } from "../frame.js";
|
|
5
5
|
export { getBuilderParentOrigin, isInBuilderFrame, sendToBuilderChat, shouldParentFrameOwnAgentPanel, isTrustedBuilderMessage, tryDelegateBuildRequestToBuilder, type BuilderChatMessage, } from "../builder-frame.js";
|
|
6
6
|
export { getClientSurface, type ClientSurface } from "../client-surface.js";
|
|
7
7
|
export { defineClientAction, type AgentNativeClientActionDefinition, type AgentNativeClientActionRunner, } from "../client-action.js";
|
|
8
8
|
export { DESKTOP_LOCAL_CODE_CHANGE_EVENT, requestDesktopLocalCodeChange, type DesktopLocalCodeChangeDetail, } from "../desktop-local-code-change.js";
|
|
9
|
-
export { buildSessionReplayIframeBootstrap, injectSessionReplayIframeBootstrap, } from "../../extensions/session-replay-iframe.js";
|
|
9
|
+
export { buildSessionReplayIframeBootstrap, injectSessionReplayIframeBootstrap, RRWEB_RECORD_IFRAME_CDN_URL, } from "../../extensions/session-replay-iframe.js";
|
|
10
10
|
export { SESSION_REPLAY_IFRAME_ATTRIBUTE, SESSION_REPLAY_IFRAME_PROBE, SESSION_REPLAY_IFRAME_START, SESSION_REPLAY_IFRAME_STOP, } from "../../session-replay-iframe-protocol.js";
|
|
11
11
|
export { AGENT_NATIVE_HOST_BRIDGE_VERSION, AGENT_NATIVE_HOST_MESSAGE_TYPES, announceAgentNativeFrameReady, createAgentNativeHostBridge, defaultAgentNativeHostCommands, onAgentNativeHostInit, readAgentNativeScreenContext, requestAgentNativeHostActions, requestAgentNativeHostContext, requestAgentNativeHostWebMcpTools, runAgentNativeHostAction, runAgentNativeHostWebMcpTool, sendAgentNativeHostCommand, type AgentNativeActionAvailability, type AgentNativeActionManifestEntry, type AgentNativeClientAction, type AgentNativeClientActionApprovalConfig, type AgentNativeClientActionGetter, type AgentNativeClientActionRuntime, type AgentNativeClientActions, type AgentNativeHostAuth, type AgentNativeHostAuthPayload, type AgentNativeHostBridge, type AgentNativeHostBridgeEvent, type AgentNativeHostBridgeOptions, type AgentNativeHostCapabilities, type AgentNativeHostCommandHandler, type AgentNativeHostCommandHandlers, type AgentNativeHostCommandRequest, type AgentNativeHostContext, type AgentNativeHostContextGetter, type AgentNativeHostInit, type AgentNativeHostMessageType, type AgentNativeHostRequestOptions, type AgentNativeHostResourceContext, type AgentNativeHostRouteContext, type AgentNativeHostSelectionContext, type AgentNativeHostSession, type AgentNativeJsonSchema, type AgentNativeScreenSnapshot, type AgentNativeScreenSnapshotOptions, type BuiltInAgentNativeHostCommand, } from "../host-bridge.js";
|
|
12
12
|
export { AgentNativeWebMcpUnsupportedError, createAgentNativeWebMcpClient, createAgentNativeWebMcpRegistration, initializeAgentNativeWebMcp, isAgentNativeWebMcpSupported, type AgentNativeWebMcpApprovalRequest, type AgentNativeWebMcpClient, type AgentNativeWebMcpClientOptions, type AgentNativeWebMcpRegistration, type AgentNativeWebMcpRegistrationOptions, type AgentNativeWebMcpTool, type AgentNativeWebMcpToolAnnotations, type AgentNativeWebMcpToolExecutionOptions, type AgentNativeWebMcpToolResult, } from "../webmcp.js";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export { initializeAgentNativeClient } from "../client-bootstrap.js";
|
|
2
2
|
export { agentNativeApiDisabledReason, AgentNativeApiDisabledError, setAgentNativeApiDisabled, } from "../api-surface.js";
|
|
3
|
-
export { ensureEmbedAuthFetchInterceptor, getEmbedAuthToken, isEmbedAuthActive, isEmbedMcpChatBridgeActive, isMcpDirectoryWidgetReadOnlyEmbed, } from "../embed-auth.js";
|
|
3
|
+
export { ensureEmbedAuthFetchInterceptor, getEmbedAuthToken, isEmbedAuthActive, isEmbedMcpChatBridgeActive, isMcpDirectoryWidgetReadOnlyEmbed, isMcpDirectoryWidgetWriteEmbed, } from "../embed-auth.js";
|
|
4
4
|
export { sendToFrame, onFrameMessage, requestUserInfo, getFrameOrigin, getFramePostMessageTargetOrigin, getCallbackOrigin, oauthRedirectUri, isInFrame, isTrustedFrameMessage, enterStyleEditing, enterTextEditing, exitSelectionMode, } from "../frame.js";
|
|
5
5
|
export { getBuilderParentOrigin, isInBuilderFrame, sendToBuilderChat, shouldParentFrameOwnAgentPanel, isTrustedBuilderMessage, tryDelegateBuildRequestToBuilder, } from "../builder-frame.js";
|
|
6
6
|
export { getClientSurface } from "../client-surface.js";
|
|
7
7
|
export { defineClientAction, } from "../client-action.js";
|
|
8
8
|
export { DESKTOP_LOCAL_CODE_CHANGE_EVENT, requestDesktopLocalCodeChange, } from "../desktop-local-code-change.js";
|
|
9
|
-
export { buildSessionReplayIframeBootstrap, injectSessionReplayIframeBootstrap, } from "../../extensions/session-replay-iframe.js";
|
|
9
|
+
export { buildSessionReplayIframeBootstrap, injectSessionReplayIframeBootstrap, RRWEB_RECORD_IFRAME_CDN_URL, } from "../../extensions/session-replay-iframe.js";
|
|
10
10
|
export { SESSION_REPLAY_IFRAME_ATTRIBUTE, SESSION_REPLAY_IFRAME_PROBE, SESSION_REPLAY_IFRAME_START, SESSION_REPLAY_IFRAME_STOP, } from "../../session-replay-iframe-protocol.js";
|
|
11
11
|
export { AGENT_NATIVE_HOST_BRIDGE_VERSION, AGENT_NATIVE_HOST_MESSAGE_TYPES, announceAgentNativeFrameReady, createAgentNativeHostBridge, defaultAgentNativeHostCommands, onAgentNativeHostInit, readAgentNativeScreenContext, requestAgentNativeHostActions, requestAgentNativeHostContext, requestAgentNativeHostWebMcpTools, runAgentNativeHostAction, runAgentNativeHostWebMcpTool, sendAgentNativeHostCommand, } from "../host-bridge.js";
|
|
12
12
|
export { AgentNativeWebMcpUnsupportedError, createAgentNativeWebMcpClient, createAgentNativeWebMcpRegistration, initializeAgentNativeWebMcp, isAgentNativeWebMcpSupported, } from "../webmcp.js";
|
|
@@ -64,6 +64,7 @@ export declare function useIsMcpAppWidgetEmbed(): boolean;
|
|
|
64
64
|
* instead of showing errors for requests that can never land.
|
|
65
65
|
*/
|
|
66
66
|
export declare function useIsMcpDirectoryWidgetReadOnlyEmbed(): boolean;
|
|
67
|
+
export declare function useIsMcpDirectoryWidgetWriteEmbed(): boolean;
|
|
67
68
|
export declare function isOpenAiMcpAppHost(): boolean;
|
|
68
69
|
export declare function initializeMcpAppHost(): Promise<boolean>;
|
|
69
70
|
export declare function sendMcpAppHostMessage(chat: McpAppHostChatMessage): Promise<boolean | null> | false;
|
|
@@ -2,7 +2,7 @@ import { useSyncExternalStore } from "react";
|
|
|
2
2
|
import { EMBED_MODE_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, MCP_APP_CHAT_BRIDGE_QUERY_PARAM, } from "../shared/embed-auth.js";
|
|
3
3
|
import { MCP_APP_HOST_FILL_ATTRIBUTE, mcpAppHostFillsContainer, } from "../shared/mcp-app-display.js";
|
|
4
4
|
import { MCP_APP_WIDGET_EMBED_ATTRIBUTE } from "../shared/mcp-app-widget-embed.js";
|
|
5
|
-
import { getEmbedAuthToken, hasMcpDirectoryWidgetCapabilityToken, isEmbedAuthActive, markEmbedMcpChatBridgeActive, isEmbedMcpChatBridgeActive, isMcpDirectoryWidgetReadOnlyEmbed, readEmbedMcpChatBridgeFlagFromUrl, } from "./embed-auth.js";
|
|
5
|
+
import { getEmbedAuthToken, hasMcpDirectoryWidgetCapabilityToken, isEmbedAuthActive, markEmbedMcpChatBridgeActive, isEmbedMcpChatBridgeActive, isMcpDirectoryWidgetReadOnlyEmbed, isMcpDirectoryWidgetWriteEmbed, readEmbedMcpChatBridgeFlagFromUrl, } from "./embed-auth.js";
|
|
6
6
|
import { getFrameOrigin } from "./frame.js";
|
|
7
7
|
export const AGENT_NATIVE_MCP_APP_HOST_MESSAGE_TYPES = {
|
|
8
8
|
HOST_CONTEXT: "agentNative.mcpHostContext",
|
|
@@ -410,6 +410,9 @@ export function useIsMcpAppWidgetEmbed() {
|
|
|
410
410
|
export function useIsMcpDirectoryWidgetReadOnlyEmbed() {
|
|
411
411
|
return useSyncExternalStore(() => () => { }, isMcpDirectoryWidgetReadOnlyEmbed, () => false);
|
|
412
412
|
}
|
|
413
|
+
export function useIsMcpDirectoryWidgetWriteEmbed() {
|
|
414
|
+
return useSyncExternalStore(() => () => { }, isMcpDirectoryWidgetWriteEmbed, () => false);
|
|
415
|
+
}
|
|
413
416
|
export function isOpenAiMcpAppHost() {
|
|
414
417
|
return (isInChildFrame() && isMcpAppBridgeEnabled() && readOpenAiBridge() !== null);
|
|
415
418
|
}
|
|
@@ -2005,7 +2005,38 @@ function installSessionReplayIframeBridge(state, options) {
|
|
|
2005
2005
|
const stopMessage = {
|
|
2006
2006
|
type: SESSION_REPLAY_IFRAME_STOP,
|
|
2007
2007
|
};
|
|
2008
|
-
const
|
|
2008
|
+
const recordingFrames = new Set();
|
|
2009
|
+
const startFrame = (iframe, retry = false) => {
|
|
2010
|
+
if (!state.active ||
|
|
2011
|
+
!iframe.hasAttribute(SESSION_REPLAY_IFRAME_ATTRIBUTE)) {
|
|
2012
|
+
return;
|
|
2013
|
+
}
|
|
2014
|
+
if (recordingFrames.has(iframe)) {
|
|
2015
|
+
if (retry)
|
|
2016
|
+
postSessionReplayIframeMessage(iframe, startMessage);
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
recordingFrames.add(iframe);
|
|
2020
|
+
postSessionReplayIframeMessage(iframe, startMessage);
|
|
2021
|
+
};
|
|
2022
|
+
const stopFrame = (iframe) => {
|
|
2023
|
+
if (!recordingFrames.delete(iframe))
|
|
2024
|
+
return;
|
|
2025
|
+
postSessionReplayIframeMessage(iframe, stopMessage);
|
|
2026
|
+
};
|
|
2027
|
+
const iframesIn = (node) => {
|
|
2028
|
+
if (node.nodeType !== 1 && node.nodeType !== 11)
|
|
2029
|
+
return [];
|
|
2030
|
+
const element = node;
|
|
2031
|
+
const frames = [];
|
|
2032
|
+
if (element.nodeType === 1 && element.localName === "iframe") {
|
|
2033
|
+
frames.push(element);
|
|
2034
|
+
}
|
|
2035
|
+
if (typeof element.querySelectorAll === "function") {
|
|
2036
|
+
frames.push(...Array.from(element.querySelectorAll("iframe")));
|
|
2037
|
+
}
|
|
2038
|
+
return frames;
|
|
2039
|
+
};
|
|
2009
2040
|
const onMessage = (event) => {
|
|
2010
2041
|
if (!state.active)
|
|
2011
2042
|
return;
|
|
@@ -2016,16 +2047,49 @@ function installSessionReplayIframeBridge(state, options) {
|
|
|
2016
2047
|
}
|
|
2017
2048
|
const iframe = markedSessionReplayIframeForSource(event.source);
|
|
2018
2049
|
if (iframe)
|
|
2019
|
-
|
|
2050
|
+
startFrame(iframe, true);
|
|
2020
2051
|
};
|
|
2021
2052
|
window.addEventListener("message", onMessage);
|
|
2053
|
+
const observer = typeof MutationObserver === "undefined" || !document.documentElement
|
|
2054
|
+
? null
|
|
2055
|
+
: new MutationObserver((records) => {
|
|
2056
|
+
for (const record of records) {
|
|
2057
|
+
if (record.type === "attributes") {
|
|
2058
|
+
const target = record.target;
|
|
2059
|
+
if (target.localName !== "iframe")
|
|
2060
|
+
continue;
|
|
2061
|
+
const iframe = target;
|
|
2062
|
+
if (iframe.hasAttribute(SESSION_REPLAY_IFRAME_ATTRIBUTE)) {
|
|
2063
|
+
startFrame(iframe);
|
|
2064
|
+
}
|
|
2065
|
+
else {
|
|
2066
|
+
stopFrame(iframe);
|
|
2067
|
+
}
|
|
2068
|
+
continue;
|
|
2069
|
+
}
|
|
2070
|
+
for (const removed of Array.from(record.removedNodes)) {
|
|
2071
|
+
for (const iframe of iframesIn(removed))
|
|
2072
|
+
stopFrame(iframe);
|
|
2073
|
+
}
|
|
2074
|
+
for (const added of Array.from(record.addedNodes)) {
|
|
2075
|
+
for (const iframe of iframesIn(added))
|
|
2076
|
+
startFrame(iframe);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
});
|
|
2080
|
+
observer?.observe(document.documentElement, {
|
|
2081
|
+
attributes: true,
|
|
2082
|
+
attributeFilter: [SESSION_REPLAY_IFRAME_ATTRIBUTE],
|
|
2083
|
+
childList: true,
|
|
2084
|
+
subtree: true,
|
|
2085
|
+
});
|
|
2022
2086
|
for (const iframe of markedSessionReplayIframes())
|
|
2023
|
-
|
|
2087
|
+
startFrame(iframe);
|
|
2024
2088
|
state.restoreIframeBridge = () => {
|
|
2025
2089
|
window.removeEventListener("message", onMessage);
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2090
|
+
observer?.disconnect();
|
|
2091
|
+
for (const iframe of recordingFrames)
|
|
2092
|
+
stopFrame(iframe);
|
|
2029
2093
|
state.restoreIframeBridge = null;
|
|
2030
2094
|
};
|
|
2031
2095
|
}
|