skybridge 0.0.0-dev.faa58ad → 0.0.0-dev.faf20cc
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/README.md +17 -14
- package/dist/cli/build-helpers.d.ts +6 -2
- package/dist/cli/build-helpers.js +39 -14
- package/dist/cli/build-helpers.js.map +1 -1
- package/dist/cli/build-helpers.test.js +73 -5
- package/dist/cli/build-helpers.test.js.map +1 -1
- package/dist/cli/build-steps.d.ts +2 -0
- package/dist/cli/build-steps.js +73 -0
- package/dist/cli/build-steps.js.map +1 -0
- package/dist/cli/build-steps.test.js +52 -0
- package/dist/cli/build-steps.test.js.map +1 -0
- package/dist/cli/detect-port.js +0 -1
- package/dist/cli/detect-port.js.map +1 -1
- package/dist/cli/header.d.ts +1 -1
- package/dist/cli/resolve-skybridge-config.d.ts +5 -0
- package/dist/cli/{resolve-views-dir.js → resolve-skybridge-config.js} +4 -3
- package/dist/cli/resolve-skybridge-config.js.map +1 -0
- package/dist/cli/run-plain.d.ts +18 -0
- package/dist/cli/run-plain.js +90 -0
- package/dist/cli/run-plain.js.map +1 -0
- package/dist/cli/telemetry.js +2 -0
- package/dist/cli/telemetry.js.map +1 -1
- package/dist/cli/use-execute-steps.d.ts +1 -1
- package/dist/cli/use-nodemon.d.ts +25 -1
- package/dist/cli/use-nodemon.js +96 -63
- package/dist/cli/use-nodemon.js.map +1 -1
- package/dist/cli/use-typescript-check.d.ts +8 -2
- package/dist/cli/use-typescript-check.js +70 -67
- package/dist/cli/use-typescript-check.js.map +1 -1
- package/dist/commands/build.d.ts +0 -2
- package/dist/commands/build.js +2 -61
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.js +38 -6
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.js +2 -2
- package/dist/commands/start.js.map +1 -1
- package/dist/context-warnings.d.ts +5 -0
- package/dist/context-warnings.js +31 -0
- package/dist/context-warnings.js.map +1 -0
- package/dist/context-warnings.test.js +28 -0
- package/dist/context-warnings.test.js.map +1 -0
- package/dist/server/app.d.ts +194 -0
- package/dist/server/app.js +266 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/app.test.js +97 -0
- package/dist/server/app.test.js.map +1 -0
- package/dist/server/auth/discovery.d.ts +2 -1
- package/dist/server/auth/discovery.js +2 -2
- package/dist/server/auth/discovery.js.map +1 -1
- package/dist/server/auth/index.d.ts +35 -5
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/auth/providers/auth0.d.ts +25 -3
- package/dist/server/auth/providers/auth0.js +13 -8
- package/dist/server/auth/providers/auth0.js.map +1 -1
- package/dist/server/auth/providers/auth0.test.js +5 -3
- package/dist/server/auth/providers/auth0.test.js.map +1 -1
- package/dist/server/auth/providers/authplane.d.ts +49 -0
- package/dist/server/auth/providers/authplane.js +57 -0
- package/dist/server/auth/providers/authplane.js.map +1 -0
- package/dist/server/auth/providers/authplane.test.js +143 -0
- package/dist/server/auth/providers/authplane.test.js.map +1 -0
- package/dist/server/auth/providers/clerk.d.ts +40 -3
- package/dist/server/auth/providers/clerk.js +4 -1
- package/dist/server/auth/providers/clerk.js.map +1 -1
- package/dist/server/auth/providers/clerk.test.js +5 -3
- package/dist/server/auth/providers/clerk.test.js.map +1 -1
- package/dist/server/auth/providers/custom.d.ts +24 -6
- package/dist/server/auth/providers/custom.js +33 -11
- package/dist/server/auth/providers/custom.js.map +1 -1
- package/dist/server/auth/providers/custom.test.js +71 -17
- package/dist/server/auth/providers/custom.test.js.map +1 -1
- package/dist/server/auth/providers/descope.d.ts +32 -3
- package/dist/server/auth/providers/descope.js +12 -2
- package/dist/server/auth/providers/descope.js.map +1 -1
- package/dist/server/auth/providers/descope.test.js +42 -18
- package/dist/server/auth/providers/descope.test.js.map +1 -1
- package/dist/server/auth/providers/stytch.d.ts +16 -3
- package/dist/server/auth/providers/stytch.js +4 -1
- package/dist/server/auth/providers/stytch.js.map +1 -1
- package/dist/server/auth/providers/verify-spy.d.ts +12 -0
- package/dist/server/auth/providers/verify-spy.js +21 -0
- package/dist/server/auth/providers/verify-spy.js.map +1 -0
- package/dist/server/auth/providers/workos.d.ts +28 -5
- package/dist/server/auth/providers/workos.js +6 -2
- package/dist/server/auth/providers/workos.js.map +1 -1
- package/dist/server/auth/security-schemes.d.ts +23 -0
- package/dist/server/auth/security-schemes.js +64 -0
- package/dist/server/auth/security-schemes.js.map +1 -0
- package/dist/server/auth/security-schemes.test.d.ts +1 -0
- package/dist/server/auth/security-schemes.test.js +99 -0
- package/dist/server/auth/security-schemes.test.js.map +1 -0
- package/dist/server/auth/setup.d.ts +3 -1
- package/dist/server/auth/setup.js +66 -26
- package/dist/server/auth/setup.js.map +1 -1
- package/dist/server/auth/setup.test.js +349 -20
- package/dist/server/auth/setup.test.js.map +1 -1
- package/dist/server/auth/verify.d.ts +42 -6
- package/dist/server/auth/verify.js +27 -3
- package/dist/server/auth/verify.js.map +1 -1
- package/dist/server/auth/verify.test.js +65 -0
- package/dist/server/auth/verify.test.js.map +1 -1
- package/dist/server/auth-extra.test-d.js +130 -0
- package/dist/server/auth-extra.test-d.js.map +1 -0
- package/dist/server/auth.d.ts +43 -6
- package/dist/server/auth.js +4 -5
- package/dist/server/auth.js.map +1 -1
- package/dist/server/build-manifest.test.js +7 -6
- package/dist/server/build-manifest.test.js.map +1 -1
- package/dist/server/content-helpers.d.ts +1 -1
- package/dist/server/content-helpers.js.map +1 -1
- package/dist/server/express.d.ts +23 -3
- package/dist/server/express.js +38 -32
- package/dist/server/express.js.map +1 -1
- package/dist/server/express.test.js +220 -103
- package/dist/server/express.test.js.map +1 -1
- package/dist/server/file-ref.d.ts +1 -1
- package/dist/server/file-ref.js +1 -1
- package/dist/server/file-ref.js.map +1 -1
- package/dist/server/host.d.ts +2 -0
- package/dist/server/host.js +11 -0
- package/dist/server/host.js.map +1 -0
- package/dist/server/host.test.d.ts +1 -0
- package/dist/server/host.test.js +13 -0
- package/dist/server/host.test.js.map +1 -0
- package/dist/server/index.d.ts +16 -9
- package/dist/server/index.js +9 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp-errors.test.d.ts +1 -0
- package/dist/server/mcp-errors.test.js +41 -0
- package/dist/server/mcp-errors.test.js.map +1 -0
- package/dist/server/middleware.d.ts +35 -10
- package/dist/server/middleware.js +33 -0
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/middleware.test-d.js +1 -1
- package/dist/server/middleware.test-d.js.map +1 -1
- package/dist/server/middleware.test.js +242 -134
- package/dist/server/middleware.test.js.map +1 -1
- package/dist/server/register-tool.test.d.ts +1 -0
- package/dist/server/register-tool.test.js +110 -0
- package/dist/server/register-tool.test.js.map +1 -0
- package/dist/server/server.d.ts +242 -128
- package/dist/server/server.js +263 -274
- package/dist/server/server.js.map +1 -1
- package/dist/server/skills-integration.test.d.ts +1 -0
- package/dist/server/skills-integration.test.js +86 -0
- package/dist/server/skills-integration.test.js.map +1 -0
- package/dist/server/skills.d.ts +27 -0
- package/dist/server/skills.js +188 -0
- package/dist/server/skills.js.map +1 -0
- package/dist/server/skills.test.d.ts +1 -0
- package/dist/server/skills.test.js +243 -0
- package/dist/server/skills.test.js.map +1 -0
- package/dist/server/templateHelper.d.ts +0 -3
- package/dist/server/templateHelper.js.map +1 -1
- package/dist/server/templates.generated.js +4 -8
- package/dist/server/templates.generated.js.map +1 -1
- package/dist/server/tool-extra.test.d.ts +1 -0
- package/dist/server/tool-extra.test.js +88 -0
- package/dist/server/tool-extra.test.js.map +1 -0
- package/dist/server/view-resource-resolution.test.js +18 -13
- package/dist/server/view-resource-resolution.test.js.map +1 -1
- package/dist/server/viewsDevServer.js +4 -1
- package/dist/server/viewsDevServer.js.map +1 -1
- package/dist/test/utils.d.ts +9 -11
- package/dist/test/utils.js +220 -201
- package/dist/test/utils.js.map +1 -1
- package/dist/test/view.test.js +44 -40
- package/dist/test/view.test.js.map +1 -1
- package/dist/views/index.d.ts +10 -0
- package/dist/views/index.js +3 -0
- package/dist/views/index.js.map +1 -0
- package/dist/{web/plugin → views}/scan-views.js +1 -1
- package/dist/views/scan-views.js.map +1 -0
- package/dist/views/scan-views.test.d.ts +1 -0
- package/dist/views/scan-views.test.js.map +1 -0
- package/dist/views/validate-view.d.ts +1 -0
- package/dist/{web/plugin → views}/validate-view.js +2 -2
- package/dist/views/validate-view.js.map +1 -0
- package/dist/views/validate-view.test.d.ts +1 -0
- package/dist/views/validate-view.test.js.map +1 -0
- package/dist/web/bridges/adaptor.d.ts +8 -3
- package/dist/web/bridges/adaptor.js +100 -16
- package/dist/web/bridges/adaptor.js.map +1 -1
- package/dist/web/bridges/adaptor.test.js +78 -11
- package/dist/web/bridges/adaptor.test.js.map +1 -1
- package/dist/web/bridges/apps-sdk/bridge.js +1 -1
- package/dist/web/bridges/apps-sdk/bridge.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +4 -4
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +4 -4
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/get-adaptor.test.js +2 -2
- package/dist/web/bridges/get-adaptor.test.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +1 -1
- package/dist/web/bridges/mcp-app/bridge.js +15 -6
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.test.js +2 -2
- package/dist/web/bridges/mcp-app/bridge.test.js.map +1 -1
- package/dist/web/bridges/mcp-app/types.d.ts +19 -2
- package/dist/web/bridges/mcp-app/types.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.d.ts +2 -2
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js +1 -1
- package/dist/web/bridges/mcp-app/use-mcp-app-context.test.js.map +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.js +1 -1
- package/dist/web/bridges/mcp-app/view-tools.test.js.map +1 -1
- package/dist/web/bridges/types.d.ts +47 -16
- package/dist/web/bridges/types.js +1 -1
- package/dist/web/bridges/types.js.map +1 -1
- package/dist/web/components/modal-provider.d.ts +1 -1
- package/dist/web/create-store.test.js +2 -2
- package/dist/web/create-store.test.js.map +1 -1
- package/dist/web/data-llm.d.ts +1 -1
- package/dist/web/data-llm.js +3 -2
- package/dist/web/data-llm.js.map +1 -1
- package/dist/web/data-llm.test.js +17 -2
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +23 -19
- package/dist/web/generate-helpers.js +21 -17
- package/dist/web/generate-helpers.js.map +1 -1
- package/dist/web/generate-helpers.test-d.js +3 -3
- package/dist/web/generate-helpers.test-d.js.map +1 -1
- package/dist/web/generate-helpers.test.js +2 -2
- package/dist/web/generate-helpers.test.js.map +1 -1
- package/dist/web/hooks/index.d.ts +5 -1
- package/dist/web/hooks/index.js +5 -1
- package/dist/web/hooks/index.js.map +1 -1
- package/dist/web/hooks/openai-extensions.test.d.ts +1 -0
- package/dist/web/hooks/openai-extensions.test.js +107 -0
- package/dist/web/hooks/openai-extensions.test.js.map +1 -0
- package/dist/web/hooks/test/utils.d.ts +2 -1
- package/dist/web/hooks/test/utils.js +1 -1
- package/dist/web/hooks/test/utils.js.map +1 -1
- package/dist/web/hooks/use-call-tool.d.ts +2 -2
- package/dist/web/hooks/use-call-tool.js +15 -10
- package/dist/web/hooks/use-call-tool.js.map +1 -1
- package/dist/web/hooks/use-call-tool.test.js +15 -4
- package/dist/web/hooks/use-call-tool.test.js.map +1 -1
- package/dist/web/hooks/use-deep-link.d.ts +22 -0
- package/dist/web/hooks/use-deep-link.js +34 -0
- package/dist/web/hooks/use-deep-link.js.map +1 -0
- package/dist/web/hooks/use-display-mode.d.ts +1 -1
- package/dist/web/hooks/use-display-mode.js +1 -1
- package/dist/web/hooks/use-display-mode.js.map +1 -1
- package/dist/web/hooks/use-display-mode.test.js +1 -1
- package/dist/web/hooks/use-display-mode.test.js.map +1 -1
- package/dist/web/hooks/use-download.d.ts +1 -3
- package/dist/web/hooks/use-download.js +1 -2
- package/dist/web/hooks/use-download.js.map +1 -1
- package/dist/web/hooks/use-download.test.js +7 -7
- package/dist/web/hooks/use-download.test.js.map +1 -1
- package/dist/web/hooks/use-files.test.js +1 -1
- package/dist/web/hooks/use-files.test.js.map +1 -1
- package/dist/web/hooks/use-host.d.ts +44 -0
- package/dist/web/hooks/use-host.js +45 -0
- package/dist/web/hooks/use-host.js.map +1 -0
- package/dist/web/hooks/use-host.test.d.ts +1 -0
- package/dist/web/hooks/use-host.test.js +59 -0
- package/dist/web/hooks/use-host.test.js.map +1 -0
- package/dist/web/hooks/use-model-context.d.ts +44 -0
- package/dist/web/hooks/use-model-context.js +63 -0
- package/dist/web/hooks/use-model-context.js.map +1 -0
- package/dist/web/hooks/use-open-external.test.js +2 -2
- package/dist/web/hooks/use-open-external.test.js.map +1 -1
- package/dist/web/hooks/use-open-file.d.ts +20 -0
- package/dist/web/hooks/use-open-file.js +25 -0
- package/dist/web/hooks/use-open-file.js.map +1 -0
- package/dist/web/hooks/use-register-view-tool.d.ts +3 -4
- package/dist/web/hooks/use-register-view-tool.js +1 -1
- package/dist/web/hooks/use-register-view-tool.js.map +1 -1
- package/dist/web/hooks/use-request-close.test.js +1 -1
- package/dist/web/hooks/use-request-close.test.js.map +1 -1
- package/dist/web/hooks/use-request-modal.test.js +1 -1
- package/dist/web/hooks/use-request-modal.test.js.map +1 -1
- package/dist/web/hooks/use-request-size.d.ts +1 -1
- package/dist/web/hooks/use-request-size.js +1 -1
- package/dist/web/hooks/use-request-size.js.map +1 -1
- package/dist/web/hooks/use-request-size.test.js +1 -1
- package/dist/web/hooks/use-request-size.test.js.map +1 -1
- package/dist/web/hooks/use-send-follow-up-message.d.ts +8 -2
- package/dist/web/hooks/use-send-follow-up-message.js +6 -0
- package/dist/web/hooks/use-send-follow-up-message.js.map +1 -1
- package/dist/web/hooks/use-set-open-in-app-url.test.js +2 -8
- package/dist/web/hooks/use-set-open-in-app-url.test.js.map +1 -1
- package/dist/web/hooks/use-tool-info.d.ts +1 -20
- package/dist/web/hooks/use-tool-info.js +0 -1
- package/dist/web/hooks/use-tool-info.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test-d.js +0 -5
- package/dist/web/hooks/use-tool-info.test-d.js.map +1 -1
- package/dist/web/hooks/use-tool-info.test.js +2 -7
- package/dist/web/hooks/use-tool-info.test.js.map +1 -1
- package/dist/web/hooks/use-user.d.ts +3 -2
- package/dist/web/hooks/use-user.js +4 -3
- package/dist/web/hooks/use-user.js.map +1 -1
- package/dist/web/hooks/use-user.test.js +5 -3
- package/dist/web/hooks/use-user.test.js.map +1 -1
- package/dist/web/hooks/use-view-state.js +19 -9
- package/dist/web/hooks/use-view-state.js.map +1 -1
- package/dist/web/hooks/use-view-state.test.js +33 -2
- package/dist/web/hooks/use-view-state.test.js.map +1 -1
- package/dist/web/hooks/use-viewport.d.ts +20 -0
- package/dist/web/hooks/use-viewport.js +21 -0
- package/dist/web/hooks/use-viewport.js.map +1 -0
- package/dist/web/hooks/use-viewport.test.d.ts +1 -0
- package/dist/web/hooks/{use-layout.test.js → use-viewport.test.js} +12 -32
- package/dist/web/hooks/use-viewport.test.js.map +1 -0
- package/dist/web/mount-view.d.ts +3 -2
- package/dist/web/mount-view.js +5 -8
- package/dist/web/mount-view.js.map +1 -1
- package/package.json +32 -29
- package/dist/cli/resolve-views-dir.d.ts +0 -1
- package/dist/cli/resolve-views-dir.js.map +0 -1
- package/dist/web/hooks/use-layout.d.ts +0 -24
- package/dist/web/hooks/use-layout.js +0 -25
- package/dist/web/hooks/use-layout.js.map +0 -1
- package/dist/web/hooks/use-layout.test.js.map +0 -1
- package/dist/web/plugin/data-llm.test.js +0 -81
- package/dist/web/plugin/data-llm.test.js.map +0 -1
- package/dist/web/plugin/plugin.d.ts +0 -33
- package/dist/web/plugin/plugin.js +0 -189
- package/dist/web/plugin/plugin.js.map +0 -1
- package/dist/web/plugin/scan-views.js.map +0 -1
- package/dist/web/plugin/scan-views.test.js.map +0 -1
- package/dist/web/plugin/transform-data-llm.d.ts +0 -12
- package/dist/web/plugin/transform-data-llm.js +0 -96
- package/dist/web/plugin/transform-data-llm.js.map +0 -1
- package/dist/web/plugin/transform-data-llm.test.js +0 -81
- package/dist/web/plugin/transform-data-llm.test.js.map +0 -1
- package/dist/web/plugin/validate-view.d.ts +0 -1
- package/dist/web/plugin/validate-view.js.map +0 -1
- package/dist/web/plugin/validate-view.test.js.map +0 -1
- /package/dist/{web/hooks/use-layout.test.d.ts → cli/build-steps.test.d.ts} +0 -0
- /package/dist/{web/plugin/data-llm.test.d.ts → context-warnings.test.d.ts} +0 -0
- /package/dist/{web/plugin/scan-views.test.d.ts → server/app.test.d.ts} +0 -0
- /package/dist/{web/plugin/validate-view.test.d.ts → server/auth/providers/authplane.test.d.ts} +0 -0
- /package/dist/{web/plugin/transform-data-llm.test.d.ts → server/auth-extra.test-d.d.ts} +0 -0
- /package/dist/{web/plugin → views}/scan-views.d.ts +0 -0
- /package/dist/{web/plugin → views}/scan-views.test.js +0 -0
- /package/dist/{web/plugin → views}/validate-view.test.js +0 -0
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { ResultSchema } from "@modelcontextprotocol/core";
|
|
2
|
+
import * as z from "zod/v4";
|
|
3
|
+
import { warnOnLargeViewState } from "../../context-warnings.js";
|
|
1
4
|
import { AppsSdkBridge } from "./apps-sdk/bridge.js";
|
|
2
5
|
import { McpAppBridge } from "./mcp-app/bridge.js";
|
|
3
6
|
import { NotSupportedError } from "./types.js";
|
|
4
7
|
const STORAGE_PREFIX = "sb:";
|
|
5
8
|
const MAX_STORAGE_ENTRIES = 200;
|
|
9
|
+
function isImage(mimeType) {
|
|
10
|
+
return mimeType?.startsWith("image/") ?? false;
|
|
11
|
+
}
|
|
6
12
|
function findStorageKey(viewUUID) {
|
|
7
13
|
const suffix = `:${viewUUID}`;
|
|
8
14
|
for (let i = 0; i < localStorage.length; i++) {
|
|
@@ -37,11 +43,13 @@ export class HostAdaptor {
|
|
|
37
43
|
_viewState = null;
|
|
38
44
|
viewStateListeners = new Set();
|
|
39
45
|
_viewUUID = null;
|
|
46
|
+
_modelContext = null;
|
|
40
47
|
_polyfillDisplay = { mode: "inline" };
|
|
41
48
|
polyfillDisplayListeners = new Set();
|
|
42
49
|
polyfillDisplayStore;
|
|
43
50
|
polyfillViewStateStore;
|
|
44
51
|
unsubscribeViewUUID = null;
|
|
52
|
+
unsubscribeModelContext = null;
|
|
45
53
|
constructor() {
|
|
46
54
|
this.mcp = McpAppBridge.getInstance();
|
|
47
55
|
let overlayStores = null;
|
|
@@ -75,20 +83,29 @@ export class HostAdaptor {
|
|
|
75
83
|
viewState: overlayStores?.viewState ?? this.polyfillViewStateStore,
|
|
76
84
|
};
|
|
77
85
|
this.subscribeToViewUUID();
|
|
86
|
+
this.unsubscribeModelContext = this.mcp.subscribe("openai/modelContext")(() => {
|
|
87
|
+
if (this.mcp.getSnapshot("openai/modelContext") === null) {
|
|
88
|
+
this._modelContext = null;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
78
91
|
}
|
|
79
92
|
/** @internal Release any subscriptions held on the underlying bridges. */
|
|
80
93
|
cleanup() {
|
|
81
94
|
this.unsubscribeViewUUID?.();
|
|
82
95
|
this.unsubscribeViewUUID = null;
|
|
96
|
+
this.unsubscribeModelContext?.();
|
|
97
|
+
this.unsubscribeModelContext = null;
|
|
83
98
|
}
|
|
84
99
|
// ---- Adaptor interface ----
|
|
85
100
|
getHostContextStore = (key) => this.stores[key];
|
|
86
|
-
callTool = async (name, args) => {
|
|
101
|
+
callTool = async (name, args, options) => {
|
|
102
|
+
const timeout = options?.timeout;
|
|
103
|
+
if (timeout !== undefined && (!Number.isFinite(timeout) || timeout <= 0)) {
|
|
104
|
+
throw new RangeError("Tool call timeout must be a positive, finite number of milliseconds.");
|
|
105
|
+
}
|
|
87
106
|
const app = await this.mcp.getApp();
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
arguments: args ?? undefined,
|
|
91
|
-
});
|
|
107
|
+
const params = { name, arguments: args ?? undefined };
|
|
108
|
+
const response = await app.callServerTool(params, { timeout });
|
|
92
109
|
return {
|
|
93
110
|
content: response.content,
|
|
94
111
|
structuredContent: response.structuredContent ?? {},
|
|
@@ -115,7 +132,8 @@ export class HostAdaptor {
|
|
|
115
132
|
await app.sendSizeChanged(size);
|
|
116
133
|
};
|
|
117
134
|
sendFollowUpMessage = async (prompt, options) => {
|
|
118
|
-
|
|
135
|
+
const target = options?.target;
|
|
136
|
+
if (this.openai && typeof prompt === "string" && target !== "new") {
|
|
119
137
|
await this.openai.sendFollowUpMessage({
|
|
120
138
|
prompt,
|
|
121
139
|
scrollToBottom: options?.scrollToBottom,
|
|
@@ -123,10 +141,18 @@ export class HostAdaptor {
|
|
|
123
141
|
return;
|
|
124
142
|
}
|
|
125
143
|
const app = await this.mcp.getApp();
|
|
126
|
-
|
|
144
|
+
if (target === "new" &&
|
|
145
|
+
!app.getHostCapabilities()?.experimental?.["openai/message"]) {
|
|
146
|
+
throw new NotSupportedError("sendFollowUpMessage", "the host does not advertise openai/message, so it can't open a new conversation");
|
|
147
|
+
}
|
|
148
|
+
const result = await app.sendMessage({
|
|
127
149
|
role: "user",
|
|
128
|
-
content: [{ type: "text", text: prompt }],
|
|
150
|
+
content: typeof prompt === "string" ? [{ type: "text", text: prompt }] : prompt,
|
|
151
|
+
...(target === "new" && { _meta: { "openai/message": { target } } }),
|
|
129
152
|
});
|
|
153
|
+
if (result?.isError) {
|
|
154
|
+
throw new Error("The host rejected the follow-up message.");
|
|
155
|
+
}
|
|
130
156
|
};
|
|
131
157
|
openExternal = (href, options) => {
|
|
132
158
|
if (this.openai) {
|
|
@@ -153,6 +179,7 @@ export class HostAdaptor {
|
|
|
153
179
|
const modelContent = typeof stateOrUpdater === "function"
|
|
154
180
|
? stateOrUpdater(this.openai.widgetState?.modelContent ?? null)
|
|
155
181
|
: stateOrUpdater;
|
|
182
|
+
warnOnLargeViewState(modelContent, "setWidgetState");
|
|
156
183
|
await this.openai.setWidgetState({
|
|
157
184
|
privateContent: {},
|
|
158
185
|
...this.openai.widgetState,
|
|
@@ -163,6 +190,7 @@ export class HostAdaptor {
|
|
|
163
190
|
const newState = typeof stateOrUpdater === "function"
|
|
164
191
|
? stateOrUpdater(this._viewState)
|
|
165
192
|
: stateOrUpdater;
|
|
193
|
+
warnOnLargeViewState(newState, "setViewState");
|
|
166
194
|
// update local state immediately so successive calls see fresh state
|
|
167
195
|
this._viewState = newState;
|
|
168
196
|
this.viewStateListeners.forEach((l) => {
|
|
@@ -170,22 +198,72 @@ export class HostAdaptor {
|
|
|
170
198
|
});
|
|
171
199
|
this.persistToLocalStorage(newState);
|
|
172
200
|
try {
|
|
173
|
-
|
|
174
|
-
await app.updateModelContext({
|
|
175
|
-
structuredContent: newState,
|
|
176
|
-
content: [{ type: "text", text: JSON.stringify(newState) }],
|
|
177
|
-
});
|
|
201
|
+
await this.sendModelContext();
|
|
178
202
|
}
|
|
179
203
|
catch (error) {
|
|
180
204
|
console.error("Failed to update view state in MCP App.", error);
|
|
181
205
|
}
|
|
182
206
|
};
|
|
207
|
+
updateModelContext = async (params) => {
|
|
208
|
+
const app = await this.mcp.getApp();
|
|
209
|
+
const capabilities = app.getHostCapabilities();
|
|
210
|
+
if (!capabilities?.updateModelContext &&
|
|
211
|
+
!capabilities?.experimental?.["openai/modelContext"]) {
|
|
212
|
+
throw new NotSupportedError("updateModelContext", "the host does not advertise updateModelContext");
|
|
213
|
+
}
|
|
214
|
+
const previous = this._modelContext;
|
|
215
|
+
this._modelContext = params;
|
|
216
|
+
const result = await this.sendModelContext().catch((error) => {
|
|
217
|
+
if (this._modelContext === params) {
|
|
218
|
+
this._modelContext = previous;
|
|
219
|
+
}
|
|
220
|
+
throw error;
|
|
221
|
+
});
|
|
222
|
+
const parsed = z
|
|
223
|
+
.object({ updateId: z.string() })
|
|
224
|
+
.safeParse(result._meta?.["openai/modelContext"]);
|
|
225
|
+
return parsed.success ? parsed.data.updateId : undefined;
|
|
226
|
+
};
|
|
227
|
+
async sendModelContext() {
|
|
228
|
+
const viewState = this.openai ? null : this._viewState;
|
|
229
|
+
const extra = this._modelContext;
|
|
230
|
+
const collisions = Object.keys(extra?.structuredContent ?? {}).filter((key) => viewState !== null && key in viewState);
|
|
231
|
+
if (collisions.length > 0) {
|
|
232
|
+
console.warn(`skybridge: view state overrides model context keys ${collisions.join(", ")}.`);
|
|
233
|
+
}
|
|
234
|
+
const structuredContent = { ...extra?.structuredContent, ...viewState };
|
|
235
|
+
const app = await this.mcp.getApp();
|
|
236
|
+
return app.updateModelContext({
|
|
237
|
+
...(Object.keys(structuredContent).length > 0 && { structuredContent }),
|
|
238
|
+
content: [
|
|
239
|
+
...(viewState === null
|
|
240
|
+
? []
|
|
241
|
+
: [
|
|
242
|
+
{
|
|
243
|
+
type: "text",
|
|
244
|
+
text: JSON.stringify(viewState),
|
|
245
|
+
annotations: { audience: ["assistant"] },
|
|
246
|
+
},
|
|
247
|
+
]),
|
|
248
|
+
...(extra?.content ?? []),
|
|
249
|
+
],
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
openFile = async (path) => {
|
|
253
|
+
const app = await this.mcp.getApp();
|
|
254
|
+
if (!app.getHostCapabilities()?.experimental?.["openai/files"]) {
|
|
255
|
+
throw new NotSupportedError("openFile", "the host does not advertise openai/files");
|
|
256
|
+
}
|
|
257
|
+
await app.request({ method: "openai/files/open", params: { path } }, ResultSchema);
|
|
258
|
+
};
|
|
183
259
|
uploadFile = async (file, options) => {
|
|
184
260
|
if (!this.openai) {
|
|
185
261
|
throw new NotSupportedError("uploadFile");
|
|
186
262
|
}
|
|
187
263
|
const metadata = await this.openai.uploadFile(file, options);
|
|
188
|
-
|
|
264
|
+
if (isImage(metadata.mimeType ?? file.type)) {
|
|
265
|
+
await this.trackFileIds(metadata.fileId);
|
|
266
|
+
}
|
|
189
267
|
return metadata;
|
|
190
268
|
};
|
|
191
269
|
getFileDownloadUrl = (file) => {
|
|
@@ -202,8 +280,14 @@ export class HostAdaptor {
|
|
|
202
280
|
throw new Error("selectFiles is not supported by the current host version.");
|
|
203
281
|
}
|
|
204
282
|
const files = await this.openai.selectFiles();
|
|
205
|
-
|
|
206
|
-
|
|
283
|
+
const imageIds = [];
|
|
284
|
+
for (const file of files) {
|
|
285
|
+
if (isImage(file.mimeType)) {
|
|
286
|
+
imageIds.push(file.fileId);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (imageIds.length > 0) {
|
|
290
|
+
await this.trackFileIds(...imageIds);
|
|
207
291
|
}
|
|
208
292
|
return files;
|
|
209
293
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adaptor.js","sourceRoot":"","sources":["../../../src/web/bridges/adaptor.ts"],"names":[],"mappings":"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","sourcesContent":["import { AppsSdkBridge } from \"./apps-sdk/bridge.js\";\nimport type { AppsSdkWidgetState } from \"./apps-sdk/types.js\";\nimport { McpAppBridge } from \"./mcp-app/bridge.js\";\nimport type {\n Adaptor,\n AnyViewToolHandler,\n CallToolResponse,\n DownloadParams,\n DownloadResult,\n FileMetadata,\n HostContext,\n HostContextStore,\n OpenExternalOptions,\n RequestDisplayMode,\n RequestModalOptions,\n RequestSizeOptions,\n SendFollowUpMessageOptions,\n SetViewStateAction,\n UploadFileOptions,\n ViewToolConfig,\n} from \"./types.js\";\nimport { NotSupportedError } from \"./types.js\";\n\nconst STORAGE_PREFIX = \"sb:\";\nconst MAX_STORAGE_ENTRIES = 200;\n\nfunction findStorageKey(viewUUID: string): string | undefined {\n const suffix = `:${viewUUID}`;\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(STORAGE_PREFIX) && key.endsWith(suffix)) {\n return key;\n }\n }\n return undefined;\n}\n\n/**\n * @internal\n * Single composite implementation of {@link Adaptor}. Composes the MCP App\n * bridge (always present) with an optional `window.openai` overlay. Per-method\n * routing rules are encoded inline.\n */\nexport class HostAdaptor implements Adaptor {\n private static instance: HostAdaptor | null = null;\n\n static getInstance(): HostAdaptor {\n if (!HostAdaptor.instance) {\n HostAdaptor.instance = new HostAdaptor();\n }\n return HostAdaptor.instance;\n }\n\n static resetInstance(): void {\n HostAdaptor.instance?.cleanup();\n HostAdaptor.instance = null;\n }\n\n private readonly mcp: McpAppBridge;\n private readonly openai: typeof window.openai | null = null;\n\n private readonly stores: { [K in keyof HostContext]: HostContextStore<K> };\n\n private _viewState: HostContext[\"viewState\"] = null;\n private readonly viewStateListeners = new Set<() => void>();\n private _viewUUID: string | null = null;\n\n private _polyfillDisplay: HostContext[\"display\"] = { mode: \"inline\" };\n private readonly polyfillDisplayListeners = new Set<() => void>();\n\n private readonly polyfillDisplayStore: HostContextStore<\"display\">;\n private readonly polyfillViewStateStore: HostContextStore<\"viewState\">;\n\n private unsubscribeViewUUID: (() => void) | null = null;\n\n constructor() {\n this.mcp = McpAppBridge.getInstance();\n\n let overlayStores: ReturnType<AppsSdkBridge[\"createOverlayStores\"]> | null =\n null;\n if (typeof window !== \"undefined\" && window.openai !== undefined) {\n this.openai = window.openai;\n overlayStores = AppsSdkBridge.getInstance().createOverlayStores();\n }\n\n // Built once so that getHostContextStore returns stable references — required\n // by useSyncExternalStore to avoid resubscribing on every render.\n this.polyfillDisplayStore = {\n subscribe: (onChange: () => void) => {\n this.polyfillDisplayListeners.add(onChange);\n return () => {\n this.polyfillDisplayListeners.delete(onChange);\n };\n },\n getSnapshot: () => this._polyfillDisplay,\n };\n this.polyfillViewStateStore = {\n subscribe: (onChange: () => void) => {\n this.viewStateListeners.add(onChange);\n return () => {\n this.viewStateListeners.delete(onChange);\n };\n },\n getSnapshot: () => this._viewState,\n };\n\n this.stores = {\n ...this.mcp.createContextStores(),\n display: overlayStores?.display ?? this.polyfillDisplayStore,\n viewState: overlayStores?.viewState ?? this.polyfillViewStateStore,\n };\n\n this.subscribeToViewUUID();\n }\n\n /** @internal Release any subscriptions held on the underlying bridges. */\n public cleanup(): void {\n this.unsubscribeViewUUID?.();\n this.unsubscribeViewUUID = null;\n }\n\n // ---- Adaptor interface ----\n\n public getHostContextStore = <K extends keyof HostContext>(\n key: K,\n ): HostContextStore<K> => this.stores[key];\n\n public callTool = async <\n ToolArgs extends Record<string, unknown> | null = null,\n ToolResponse extends CallToolResponse = CallToolResponse,\n >(\n name: string,\n args: ToolArgs,\n ): Promise<ToolResponse> => {\n const app = await this.mcp.getApp();\n const response = await app.callServerTool({\n name,\n arguments: args ?? undefined,\n });\n return {\n content: response.content,\n structuredContent: response.structuredContent ?? {},\n isError: response.isError ?? false,\n meta: response._meta ?? {},\n } as ToolResponse;\n };\n\n public registerViewTool = (\n config: ViewToolConfig,\n handler: AnyViewToolHandler,\n ): (() => void) => {\n return this.mcp.registerViewTool(config, handler);\n };\n\n public requestDisplayMode = async (mode: RequestDisplayMode) => {\n const app = await this.mcp.getApp();\n return app.requestDisplayMode({ mode });\n };\n\n public requestClose = async (): Promise<void> => {\n if (this.openai) {\n await this.openai.requestClose();\n }\n const app = await this.mcp.getApp();\n await app.requestTeardown();\n };\n\n public requestSize = async (size: RequestSizeOptions): Promise<void> => {\n const app = await this.mcp.getApp();\n await app.sendSizeChanged(size);\n };\n\n public sendFollowUpMessage = async (\n prompt: string,\n options?: SendFollowUpMessageOptions,\n ): Promise<void> => {\n if (this.openai) {\n await this.openai.sendFollowUpMessage({\n prompt,\n scrollToBottom: options?.scrollToBottom,\n });\n return;\n }\n const app = await this.mcp.getApp();\n await app.sendMessage({\n role: \"user\",\n content: [{ type: \"text\", text: prompt }],\n });\n };\n\n public openExternal = (href: string, options?: OpenExternalOptions): void => {\n if (this.openai) {\n this.openai.openExternal({ href, ...options });\n return;\n }\n this.mcp\n .getApp()\n .then((app) => app.openLink({ url: href }))\n .catch((err) => {\n console.error(\"Failed to open external link:\", err);\n });\n };\n\n public download = async (params: DownloadParams): Promise<DownloadResult> => {\n const app = await this.mcp.getApp();\n if (!app.getHostCapabilities()?.downloadFile) {\n console.error(\n \"[skybridge] download: host does not support ui/download-file\",\n );\n return { isError: true };\n }\n return app.downloadFile(params);\n };\n\n public setViewState = async (\n stateOrUpdater: SetViewStateAction,\n ): Promise<void> => {\n if (this.openai) {\n const modelContent =\n typeof stateOrUpdater === \"function\"\n ? stateOrUpdater(this.openai.widgetState?.modelContent ?? null)\n : stateOrUpdater;\n await this.openai.setWidgetState({\n privateContent: {},\n ...this.openai.widgetState,\n modelContent,\n });\n return;\n }\n\n const newState =\n typeof stateOrUpdater === \"function\"\n ? stateOrUpdater(this._viewState)\n : stateOrUpdater;\n\n // update local state immediately so successive calls see fresh state\n this._viewState = newState;\n this.viewStateListeners.forEach((l) => {\n l();\n });\n\n this.persistToLocalStorage(newState);\n\n try {\n const app = await this.mcp.getApp();\n await app.updateModelContext({\n structuredContent: newState,\n content: [{ type: \"text\", text: JSON.stringify(newState) }],\n });\n } catch (error) {\n console.error(\"Failed to update view state in MCP App.\", error);\n }\n };\n\n public uploadFile = async (\n file: File,\n options?: UploadFileOptions,\n ): Promise<FileMetadata> => {\n if (!this.openai) {\n throw new NotSupportedError(\"uploadFile\");\n }\n const metadata = await this.openai.uploadFile(file, options);\n await this.trackFileIds(metadata.fileId);\n return metadata;\n };\n\n public getFileDownloadUrl = (\n file: FileMetadata,\n ): Promise<{ downloadUrl: string }> => {\n if (!this.openai) {\n return Promise.reject(new NotSupportedError(\"getFileDownloadUrl\"));\n }\n return this.openai.getFileDownloadUrl(file);\n };\n\n public selectFiles = async (): Promise<FileMetadata[]> => {\n if (!this.openai) {\n throw new NotSupportedError(\"selectFiles\");\n }\n if (!this.openai.selectFiles) {\n throw new Error(\n \"selectFiles is not supported by the current host version.\",\n );\n }\n const files = await this.openai.selectFiles();\n if (files.length > 0) {\n await this.trackFileIds(...files.map((f) => f.fileId));\n }\n return files;\n };\n\n public openModal = (options: RequestModalOptions): void => {\n if (this.openai) {\n this.openai.requestModal(options);\n return;\n }\n this._polyfillDisplay = { mode: \"modal\", params: options.params };\n this.polyfillDisplayListeners.forEach((l) => {\n l();\n });\n };\n\n public setOpenInAppUrl = (href: string): Promise<void> => {\n if (!this.openai) {\n return Promise.reject(new NotSupportedError(\"setOpenInAppUrl\"));\n }\n const trimmed = href.trim();\n if (!trimmed) {\n return Promise.reject(new Error(\"The href parameter is required.\"));\n }\n return this.openai.setOpenInAppUrl({ href: trimmed });\n };\n\n public closeModal = (): void => {\n if (this.openai) {\n return; // host owns modal lifecycle\n }\n this._polyfillDisplay = { mode: \"inline\" };\n this.polyfillDisplayListeners.forEach((l) => {\n l();\n });\n };\n\n private async trackFileIds(...fileIds: string[]): Promise<void> {\n if (!this.openai) {\n return;\n }\n const current = this.openai.widgetState;\n const state: AppsSdkWidgetState = current\n ? { ...current }\n : { modelContent: {}, privateContent: {} };\n if (!state.imageIds) {\n state.imageIds = [];\n }\n state.imageIds.push(...fileIds);\n await this.openai.setWidgetState(state);\n }\n\n // ---- viewState persistence helpers ----\n\n private subscribeToViewUUID(): void {\n this.unsubscribeViewUUID = this.mcp.subscribe(\"toolResult\")(() => {\n const toolResult = this.mcp.getSnapshot(\"toolResult\");\n const viewUUID = (\n toolResult?._meta as Record<string, unknown> | undefined\n )?.viewUUID as string | undefined;\n if (viewUUID && viewUUID !== this._viewUUID) {\n this._viewUUID = viewUUID;\n this.restoreFromLocalStorage(viewUUID);\n }\n });\n }\n\n private restoreFromLocalStorage(viewUUID: string): void {\n try {\n const existingKey = findStorageKey(viewUUID);\n if (existingKey) {\n const stored = localStorage.getItem(existingKey);\n if (stored !== null) {\n this._viewState = JSON.parse(stored);\n this.viewStateListeners.forEach((l) => {\n l();\n });\n }\n }\n } catch (err) {\n console.error(err);\n }\n }\n\n protected persistToLocalStorage(state: Record<string, unknown> | null): void {\n if (!this._viewUUID || state === null) {\n return;\n }\n try {\n const oldKey = findStorageKey(this._viewUUID);\n if (oldKey) {\n localStorage.removeItem(oldKey);\n }\n const newKey = `${STORAGE_PREFIX}${Date.now()}:${this._viewUUID}`;\n localStorage.setItem(newKey, JSON.stringify(state));\n const keys: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(STORAGE_PREFIX)) {\n keys.push(key);\n }\n }\n if (keys.length <= MAX_STORAGE_ENTRIES) {\n return;\n }\n keys.sort();\n const toRemove = keys.slice(0, keys.length - MAX_STORAGE_ENTRIES);\n for (const key of toRemove) {\n localStorage.removeItem(key);\n }\n } catch (err) {\n console.error(err);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"adaptor.js","sourceRoot":"","sources":["../../../src/web/bridges/adaptor.ts"],"names":[],"mappings":"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","sourcesContent":["import { ResultSchema } from \"@modelcontextprotocol/core\";\nimport * as z from \"zod/v4\";\nimport { warnOnLargeViewState } from \"../../context-warnings.js\";\nimport { AppsSdkBridge } from \"./apps-sdk/bridge.js\";\nimport type { AppsSdkWidgetState } from \"./apps-sdk/types.js\";\nimport { McpAppBridge } from \"./mcp-app/bridge.js\";\nimport type {\n Adaptor,\n AnyViewToolHandler,\n CallToolOptions,\n CallToolResponse,\n DownloadParams,\n DownloadResult,\n FileMetadata,\n FollowUpMessage,\n HostContext,\n HostContextStore,\n ModelContextParams,\n OpenExternalOptions,\n RequestDisplayMode,\n RequestModalOptions,\n RequestSizeOptions,\n SendFollowUpMessageOptions,\n SetViewStateAction,\n UploadFileOptions,\n ViewToolConfig,\n} from \"./types.js\";\nimport { NotSupportedError } from \"./types.js\";\n\nconst STORAGE_PREFIX = \"sb:\";\nconst MAX_STORAGE_ENTRIES = 200;\n\nfunction isImage(mimeType: string | undefined): boolean {\n return mimeType?.startsWith(\"image/\") ?? false;\n}\n\nfunction findStorageKey(viewUUID: string): string | undefined {\n const suffix = `:${viewUUID}`;\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(STORAGE_PREFIX) && key.endsWith(suffix)) {\n return key;\n }\n }\n return undefined;\n}\n\n/**\n * @internal\n * Single composite implementation of {@link Adaptor}. Composes the MCP App\n * bridge (always present) with an optional `window.openai` overlay. Per-method\n * routing rules are encoded inline.\n */\nexport class HostAdaptor implements Adaptor {\n private static instance: HostAdaptor | null = null;\n\n static getInstance(): HostAdaptor {\n if (!HostAdaptor.instance) {\n HostAdaptor.instance = new HostAdaptor();\n }\n return HostAdaptor.instance;\n }\n\n static resetInstance(): void {\n HostAdaptor.instance?.cleanup();\n HostAdaptor.instance = null;\n }\n\n private readonly mcp: McpAppBridge;\n private readonly openai: typeof window.openai | null = null;\n\n private readonly stores: { [K in keyof HostContext]: HostContextStore<K> };\n\n private _viewState: HostContext[\"viewState\"] = null;\n private readonly viewStateListeners = new Set<() => void>();\n private _viewUUID: string | null = null;\n private _modelContext: ModelContextParams | null = null;\n\n private _polyfillDisplay: HostContext[\"display\"] = { mode: \"inline\" };\n private readonly polyfillDisplayListeners = new Set<() => void>();\n\n private readonly polyfillDisplayStore: HostContextStore<\"display\">;\n private readonly polyfillViewStateStore: HostContextStore<\"viewState\">;\n\n private unsubscribeViewUUID: (() => void) | null = null;\n private unsubscribeModelContext: (() => void) | null = null;\n\n constructor() {\n this.mcp = McpAppBridge.getInstance();\n\n let overlayStores: ReturnType<AppsSdkBridge[\"createOverlayStores\"]> | null =\n null;\n if (typeof window !== \"undefined\" && window.openai !== undefined) {\n this.openai = window.openai;\n overlayStores = AppsSdkBridge.getInstance().createOverlayStores();\n }\n\n // Built once so that getHostContextStore returns stable references — required\n // by useSyncExternalStore to avoid resubscribing on every render.\n this.polyfillDisplayStore = {\n subscribe: (onChange: () => void) => {\n this.polyfillDisplayListeners.add(onChange);\n return () => {\n this.polyfillDisplayListeners.delete(onChange);\n };\n },\n getSnapshot: () => this._polyfillDisplay,\n };\n this.polyfillViewStateStore = {\n subscribe: (onChange: () => void) => {\n this.viewStateListeners.add(onChange);\n return () => {\n this.viewStateListeners.delete(onChange);\n };\n },\n getSnapshot: () => this._viewState,\n };\n\n this.stores = {\n ...this.mcp.createContextStores(),\n display: overlayStores?.display ?? this.polyfillDisplayStore,\n viewState: overlayStores?.viewState ?? this.polyfillViewStateStore,\n };\n\n this.subscribeToViewUUID();\n this.unsubscribeModelContext = this.mcp.subscribe(\"openai/modelContext\")(\n () => {\n if (this.mcp.getSnapshot(\"openai/modelContext\") === null) {\n this._modelContext = null;\n }\n },\n );\n }\n\n /** @internal Release any subscriptions held on the underlying bridges. */\n public cleanup(): void {\n this.unsubscribeViewUUID?.();\n this.unsubscribeViewUUID = null;\n this.unsubscribeModelContext?.();\n this.unsubscribeModelContext = null;\n }\n\n // ---- Adaptor interface ----\n\n public getHostContextStore = <K extends keyof HostContext>(\n key: K,\n ): HostContextStore<K> => this.stores[key];\n\n public callTool = async <\n ToolArgs extends Record<string, unknown> | null = null,\n ToolResponse extends CallToolResponse = CallToolResponse,\n >(\n name: string,\n args: ToolArgs,\n options?: CallToolOptions,\n ): Promise<ToolResponse> => {\n const timeout = options?.timeout;\n if (timeout !== undefined && (!Number.isFinite(timeout) || timeout <= 0)) {\n throw new RangeError(\n \"Tool call timeout must be a positive, finite number of milliseconds.\",\n );\n }\n const app = await this.mcp.getApp();\n const params = { name, arguments: args ?? undefined };\n const response = await app.callServerTool(params, { timeout });\n return {\n content: response.content,\n structuredContent: response.structuredContent ?? {},\n isError: response.isError ?? false,\n meta: response._meta ?? {},\n } as ToolResponse;\n };\n\n public registerViewTool = (\n config: ViewToolConfig,\n handler: AnyViewToolHandler,\n ): (() => void) => {\n return this.mcp.registerViewTool(config, handler);\n };\n\n public requestDisplayMode = async (mode: RequestDisplayMode) => {\n const app = await this.mcp.getApp();\n return app.requestDisplayMode({ mode });\n };\n\n public requestClose = async (): Promise<void> => {\n if (this.openai) {\n await this.openai.requestClose();\n }\n const app = await this.mcp.getApp();\n await app.requestTeardown();\n };\n\n public requestSize = async (size: RequestSizeOptions): Promise<void> => {\n const app = await this.mcp.getApp();\n await app.sendSizeChanged(size);\n };\n\n public sendFollowUpMessage = async (\n prompt: FollowUpMessage,\n options?: SendFollowUpMessageOptions,\n ): Promise<void> => {\n const target = options?.target;\n if (this.openai && typeof prompt === \"string\" && target !== \"new\") {\n await this.openai.sendFollowUpMessage({\n prompt,\n scrollToBottom: options?.scrollToBottom,\n });\n return;\n }\n const app = await this.mcp.getApp();\n if (\n target === \"new\" &&\n !app.getHostCapabilities()?.experimental?.[\"openai/message\"]\n ) {\n throw new NotSupportedError(\n \"sendFollowUpMessage\",\n \"the host does not advertise openai/message, so it can't open a new conversation\",\n );\n }\n const result = await app.sendMessage({\n role: \"user\",\n content:\n typeof prompt === \"string\" ? [{ type: \"text\", text: prompt }] : prompt,\n ...(target === \"new\" && { _meta: { \"openai/message\": { target } } }),\n });\n if (result?.isError) {\n throw new Error(\"The host rejected the follow-up message.\");\n }\n };\n\n public openExternal = (href: string, options?: OpenExternalOptions): void => {\n if (this.openai) {\n this.openai.openExternal({ href, ...options });\n return;\n }\n this.mcp\n .getApp()\n .then((app) => app.openLink({ url: href }))\n .catch((err) => {\n console.error(\"Failed to open external link:\", err);\n });\n };\n\n public download = async (params: DownloadParams): Promise<DownloadResult> => {\n const app = await this.mcp.getApp();\n if (!app.getHostCapabilities()?.downloadFile) {\n console.error(\n \"[skybridge] download: host does not support ui/download-file\",\n );\n return { isError: true };\n }\n return app.downloadFile(params);\n };\n\n public setViewState = async (\n stateOrUpdater: SetViewStateAction,\n ): Promise<void> => {\n if (this.openai) {\n const modelContent =\n typeof stateOrUpdater === \"function\"\n ? stateOrUpdater(this.openai.widgetState?.modelContent ?? null)\n : stateOrUpdater;\n warnOnLargeViewState(modelContent, \"setWidgetState\");\n await this.openai.setWidgetState({\n privateContent: {},\n ...this.openai.widgetState,\n modelContent,\n });\n return;\n }\n\n const newState =\n typeof stateOrUpdater === \"function\"\n ? stateOrUpdater(this._viewState)\n : stateOrUpdater;\n warnOnLargeViewState(newState, \"setViewState\");\n\n // update local state immediately so successive calls see fresh state\n this._viewState = newState;\n this.viewStateListeners.forEach((l) => {\n l();\n });\n\n this.persistToLocalStorage(newState);\n\n try {\n await this.sendModelContext();\n } catch (error) {\n console.error(\"Failed to update view state in MCP App.\", error);\n }\n };\n\n public updateModelContext = async (\n params: ModelContextParams,\n ): Promise<string | undefined> => {\n const app = await this.mcp.getApp();\n const capabilities = app.getHostCapabilities();\n if (\n !capabilities?.updateModelContext &&\n !capabilities?.experimental?.[\"openai/modelContext\"]\n ) {\n throw new NotSupportedError(\n \"updateModelContext\",\n \"the host does not advertise updateModelContext\",\n );\n }\n const previous = this._modelContext;\n this._modelContext = params;\n const result = await this.sendModelContext().catch((error: unknown) => {\n if (this._modelContext === params) {\n this._modelContext = previous;\n }\n throw error;\n });\n const parsed = z\n .object({ updateId: z.string() })\n .safeParse(result._meta?.[\"openai/modelContext\"]);\n return parsed.success ? parsed.data.updateId : undefined;\n };\n\n private async sendModelContext() {\n const viewState = this.openai ? null : this._viewState;\n const extra = this._modelContext;\n const collisions = Object.keys(extra?.structuredContent ?? {}).filter(\n (key) => viewState !== null && key in viewState,\n );\n if (collisions.length > 0) {\n console.warn(\n `skybridge: view state overrides model context keys ${collisions.join(\", \")}.`,\n );\n }\n const structuredContent = { ...extra?.structuredContent, ...viewState };\n const app = await this.mcp.getApp();\n return app.updateModelContext({\n ...(Object.keys(structuredContent).length > 0 && { structuredContent }),\n content: [\n ...(viewState === null\n ? []\n : [\n {\n type: \"text\" as const,\n text: JSON.stringify(viewState),\n annotations: { audience: [\"assistant\" as const] },\n },\n ]),\n ...(extra?.content ?? []),\n ],\n });\n }\n\n public openFile = async (path: string): Promise<void> => {\n const app = await this.mcp.getApp();\n if (!app.getHostCapabilities()?.experimental?.[\"openai/files\"]) {\n throw new NotSupportedError(\n \"openFile\",\n \"the host does not advertise openai/files\",\n );\n }\n await app.request(\n { method: \"openai/files/open\", params: { path } },\n ResultSchema,\n );\n };\n\n public uploadFile = async (\n file: File,\n options?: UploadFileOptions,\n ): Promise<FileMetadata> => {\n if (!this.openai) {\n throw new NotSupportedError(\"uploadFile\");\n }\n const metadata = await this.openai.uploadFile(file, options);\n if (isImage(metadata.mimeType ?? file.type)) {\n await this.trackFileIds(metadata.fileId);\n }\n return metadata;\n };\n\n public getFileDownloadUrl = (\n file: FileMetadata,\n ): Promise<{ downloadUrl: string }> => {\n if (!this.openai) {\n return Promise.reject(new NotSupportedError(\"getFileDownloadUrl\"));\n }\n return this.openai.getFileDownloadUrl(file);\n };\n\n public selectFiles = async (): Promise<FileMetadata[]> => {\n if (!this.openai) {\n throw new NotSupportedError(\"selectFiles\");\n }\n if (!this.openai.selectFiles) {\n throw new Error(\n \"selectFiles is not supported by the current host version.\",\n );\n }\n const files = await this.openai.selectFiles();\n const imageIds: string[] = [];\n for (const file of files) {\n if (isImage(file.mimeType)) {\n imageIds.push(file.fileId);\n }\n }\n if (imageIds.length > 0) {\n await this.trackFileIds(...imageIds);\n }\n return files;\n };\n\n public openModal = (options: RequestModalOptions): void => {\n if (this.openai) {\n this.openai.requestModal(options);\n return;\n }\n this._polyfillDisplay = { mode: \"modal\", params: options.params };\n this.polyfillDisplayListeners.forEach((l) => {\n l();\n });\n };\n\n public setOpenInAppUrl = (href: string): Promise<void> => {\n if (!this.openai) {\n return Promise.reject(new NotSupportedError(\"setOpenInAppUrl\"));\n }\n const trimmed = href.trim();\n if (!trimmed) {\n return Promise.reject(new Error(\"The href parameter is required.\"));\n }\n return this.openai.setOpenInAppUrl({ href: trimmed });\n };\n\n public closeModal = (): void => {\n if (this.openai) {\n return; // host owns modal lifecycle\n }\n this._polyfillDisplay = { mode: \"inline\" };\n this.polyfillDisplayListeners.forEach((l) => {\n l();\n });\n };\n\n private async trackFileIds(...fileIds: string[]): Promise<void> {\n if (!this.openai) {\n return;\n }\n const current = this.openai.widgetState;\n const state: AppsSdkWidgetState = current\n ? { ...current }\n : { modelContent: {}, privateContent: {} };\n if (!state.imageIds) {\n state.imageIds = [];\n }\n state.imageIds.push(...fileIds);\n await this.openai.setWidgetState(state);\n }\n\n // ---- viewState persistence helpers ----\n\n private subscribeToViewUUID(): void {\n this.unsubscribeViewUUID = this.mcp.subscribe(\"toolResult\")(() => {\n const toolResult = this.mcp.getSnapshot(\"toolResult\");\n const viewUUID = (\n toolResult?._meta as Record<string, unknown> | undefined\n )?.viewUUID as string | undefined;\n if (viewUUID && viewUUID !== this._viewUUID) {\n this._viewUUID = viewUUID;\n this.restoreFromLocalStorage(viewUUID);\n }\n });\n }\n\n private restoreFromLocalStorage(viewUUID: string): void {\n try {\n const existingKey = findStorageKey(viewUUID);\n if (existingKey) {\n const stored = localStorage.getItem(existingKey);\n if (stored !== null) {\n this._viewState = JSON.parse(stored);\n this.viewStateListeners.forEach((l) => {\n l();\n });\n }\n }\n } catch (err) {\n console.error(err);\n }\n }\n\n protected persistToLocalStorage(state: Record<string, unknown> | null): void {\n if (!this._viewUUID || state === null) {\n return;\n }\n try {\n const oldKey = findStorageKey(this._viewUUID);\n if (oldKey) {\n localStorage.removeItem(oldKey);\n }\n const newKey = `${STORAGE_PREFIX}${Date.now()}:${this._viewUUID}`;\n localStorage.setItem(newKey, JSON.stringify(state));\n const keys: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key?.startsWith(STORAGE_PREFIX)) {\n keys.push(key);\n }\n }\n if (keys.length <= MAX_STORAGE_ENTRIES) {\n return;\n }\n keys.sort();\n const toRemove = keys.slice(0, keys.length - MAX_STORAGE_ENTRIES);\n for (const key of toRemove) {\n localStorage.removeItem(key);\n }\n } catch (err) {\n console.error(err);\n }\n }\n}\n"]}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { VIEW_STATE_WARNING_TOKENS } from "../../context-warnings.js";
|
|
2
3
|
import { HostAdaptor } from "./adaptor.js";
|
|
3
4
|
import { AppsSdkBridge } from "./apps-sdk/bridge.js";
|
|
4
5
|
import { McpAppBridge } from "./mcp-app/bridge.js";
|
|
5
6
|
import { NotSupportedError } from "./types.js";
|
|
7
|
+
function stubApp(adaptor, app) {
|
|
8
|
+
adaptor.mcp.getApp = () => Promise.resolve(app);
|
|
9
|
+
}
|
|
6
10
|
describe("HostAdaptor", () => {
|
|
7
11
|
beforeEach(() => {
|
|
8
12
|
McpAppBridge.resetInstance();
|
|
9
13
|
AppsSdkBridge.resetInstance();
|
|
10
|
-
vi.stubGlobal("skybridge", {
|
|
14
|
+
vi.stubGlobal("skybridge", {});
|
|
11
15
|
vi.stubGlobal("parent", { postMessage: vi.fn() });
|
|
12
16
|
localStorage.clear();
|
|
13
17
|
});
|
|
@@ -34,7 +38,7 @@ describe("HostAdaptor", () => {
|
|
|
34
38
|
expect(fakeApp.callServerTool).toHaveBeenCalledWith({
|
|
35
39
|
name: "greet",
|
|
36
40
|
arguments: { name: "alice" },
|
|
37
|
-
});
|
|
41
|
+
}, { timeout: undefined });
|
|
38
42
|
expect(result.structuredContent).toEqual({ ok: true });
|
|
39
43
|
expect(result.meta).toEqual({ x: 1 });
|
|
40
44
|
});
|
|
@@ -51,7 +55,7 @@ describe("HostAdaptor", () => {
|
|
|
51
55
|
expect(r.isError).toBe(true);
|
|
52
56
|
expect(fakeApp.downloadFile).not.toHaveBeenCalled();
|
|
53
57
|
});
|
|
54
|
-
it("sendFollowUpMessage
|
|
58
|
+
it("sendFollowUpMessage uses window.openai for plain text and ui/message for content blocks or a target", async () => {
|
|
55
59
|
const sendFollowUpMessage = vi.fn().mockResolvedValue(undefined);
|
|
56
60
|
vi.stubGlobal("openai", { sendFollowUpMessage });
|
|
57
61
|
let adaptor = new HostAdaptor();
|
|
@@ -66,13 +70,43 @@ describe("HostAdaptor", () => {
|
|
|
66
70
|
vi.stubGlobal("openai", undefined);
|
|
67
71
|
adaptor = new HostAdaptor();
|
|
68
72
|
const sendMessage = vi.fn().mockResolvedValue(undefined);
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
const getHostCapabilities = () => ({
|
|
74
|
+
experimental: { "openai/message": {} },
|
|
75
|
+
});
|
|
76
|
+
stubApp(adaptor, { sendMessage });
|
|
71
77
|
await adaptor.sendFollowUpMessage("hi");
|
|
72
78
|
expect(sendMessage).toHaveBeenCalledWith({
|
|
73
79
|
role: "user",
|
|
74
80
|
content: [{ type: "text", text: "hi" }],
|
|
75
81
|
});
|
|
82
|
+
McpAppBridge.resetInstance();
|
|
83
|
+
AppsSdkBridge.resetInstance();
|
|
84
|
+
vi.stubGlobal("openai", { sendFollowUpMessage });
|
|
85
|
+
adaptor = new HostAdaptor();
|
|
86
|
+
stubApp(adaptor, { sendMessage, getHostCapabilities });
|
|
87
|
+
const item = {
|
|
88
|
+
type: "text",
|
|
89
|
+
text: "M6 hex bolt",
|
|
90
|
+
_meta: { "openai/title": "Hex bolt" },
|
|
91
|
+
};
|
|
92
|
+
await adaptor.sendFollowUpMessage([item], { target: "new" });
|
|
93
|
+
expect(sendMessage).toHaveBeenLastCalledWith({
|
|
94
|
+
role: "user",
|
|
95
|
+
content: [item],
|
|
96
|
+
_meta: { "openai/message": { target: "new" } },
|
|
97
|
+
});
|
|
98
|
+
expect(sendFollowUpMessage).toHaveBeenCalledTimes(1);
|
|
99
|
+
});
|
|
100
|
+
it("openFile sends openai/files/open only on hosts advertising openai/files", async () => {
|
|
101
|
+
const adaptor = new HostAdaptor();
|
|
102
|
+
const request = vi.fn().mockResolvedValue({});
|
|
103
|
+
let capabilities = {};
|
|
104
|
+
stubApp(adaptor, { request, getHostCapabilities: () => capabilities });
|
|
105
|
+
await expect(adaptor.openFile("/tmp/part.stl")).rejects.toThrow("openai/files");
|
|
106
|
+
expect(request).not.toHaveBeenCalled();
|
|
107
|
+
capabilities = { experimental: { "openai/files": {} } };
|
|
108
|
+
await adaptor.openFile("/tmp/part.stl");
|
|
109
|
+
expect(request).toHaveBeenCalledWith({ method: "openai/files/open", params: { path: "/tmp/part.stl" } }, expect.anything());
|
|
76
110
|
});
|
|
77
111
|
it("openExternal routes to window.openai only when redirectUrl: false", async () => {
|
|
78
112
|
const openExternal = vi.fn();
|
|
@@ -105,17 +139,33 @@ describe("HostAdaptor", () => {
|
|
|
105
139
|
});
|
|
106
140
|
it("uploadFile delegates to window.openai.uploadFile and tracks fileId in widgetState", async () => {
|
|
107
141
|
const setWidgetState = vi.fn().mockResolvedValue(undefined);
|
|
108
|
-
const uploadFile = vi
|
|
109
|
-
|
|
110
|
-
|
|
142
|
+
const uploadFile = vi.fn().mockResolvedValue({
|
|
143
|
+
fileId: "abc",
|
|
144
|
+
fileName: "x",
|
|
145
|
+
mimeType: "image/png",
|
|
146
|
+
});
|
|
111
147
|
vi.stubGlobal("openai", { uploadFile, setWidgetState, widgetState: null });
|
|
112
148
|
const adaptor = new HostAdaptor();
|
|
113
|
-
const file = new File([], "x");
|
|
149
|
+
const file = new File([], "x", { type: "image/png" });
|
|
114
150
|
const r = await adaptor.uploadFile(file, { library: true });
|
|
115
151
|
expect(uploadFile).toHaveBeenCalledWith(file, { library: true });
|
|
116
152
|
expect(r.fileId).toBe("abc");
|
|
117
153
|
expect(setWidgetState).toHaveBeenCalledWith(expect.objectContaining({ imageIds: ["abc"] }));
|
|
118
154
|
});
|
|
155
|
+
it("uploadFile does not track a non-image fileId in widgetState.imageIds", async () => {
|
|
156
|
+
const setWidgetState = vi.fn().mockResolvedValue(undefined);
|
|
157
|
+
const uploadFile = vi.fn().mockResolvedValue({
|
|
158
|
+
fileId: "sediment://file_txt",
|
|
159
|
+
fileName: "notes.txt",
|
|
160
|
+
mimeType: "text/plain",
|
|
161
|
+
});
|
|
162
|
+
vi.stubGlobal("openai", { uploadFile, setWidgetState, widgetState: null });
|
|
163
|
+
const adaptor = new HostAdaptor();
|
|
164
|
+
await adaptor.uploadFile(new File([], "notes.txt", { type: "text/plain" }));
|
|
165
|
+
for (const call of setWidgetState.mock.calls) {
|
|
166
|
+
expect(call[0].imageIds ?? []).not.toContain("sediment://file_txt");
|
|
167
|
+
}
|
|
168
|
+
});
|
|
119
169
|
it("setViewState uses window.openai.setWidgetState when present", async () => {
|
|
120
170
|
const setWidgetState = vi.fn().mockResolvedValue(undefined);
|
|
121
171
|
vi.stubGlobal("openai", { setWidgetState, widgetState: null });
|
|
@@ -123,6 +173,17 @@ describe("HostAdaptor", () => {
|
|
|
123
173
|
await adaptor.setViewState({ count: 1 });
|
|
124
174
|
expect(setWidgetState).toHaveBeenCalledWith(expect.objectContaining({ modelContent: { count: 1 } }));
|
|
125
175
|
});
|
|
176
|
+
it("warns when setViewState reaches 20K tokens", async () => {
|
|
177
|
+
const setWidgetState = vi.fn().mockResolvedValue(undefined);
|
|
178
|
+
vi.stubGlobal("openai", { setWidgetState, widgetState: null });
|
|
179
|
+
const adaptor = new HostAdaptor();
|
|
180
|
+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
181
|
+
await adaptor.setViewState({
|
|
182
|
+
longText: "x".repeat(VIEW_STATE_WARNING_TOKENS * 4),
|
|
183
|
+
});
|
|
184
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("setWidgetState is persisting"));
|
|
185
|
+
warnSpy.mockRestore();
|
|
186
|
+
});
|
|
126
187
|
it("setViewState falls back to MCP updateModelContext + localStorage when window.openai is absent", async () => {
|
|
127
188
|
vi.stubGlobal("openai", undefined);
|
|
128
189
|
const adaptor = new HostAdaptor();
|
|
@@ -136,7 +197,13 @@ describe("HostAdaptor", () => {
|
|
|
136
197
|
await adaptor.setViewState({ count: 2 });
|
|
137
198
|
expect(updateModelContext).toHaveBeenCalledWith({
|
|
138
199
|
structuredContent: { count: 2 },
|
|
139
|
-
content: [
|
|
200
|
+
content: [
|
|
201
|
+
{
|
|
202
|
+
type: "text",
|
|
203
|
+
text: JSON.stringify({ count: 2 }),
|
|
204
|
+
annotations: { audience: ["assistant"] },
|
|
205
|
+
},
|
|
206
|
+
],
|
|
140
207
|
});
|
|
141
208
|
const keys = [];
|
|
142
209
|
for (let i = 0; i < localStorage.length; i++) {
|
|
@@ -169,7 +236,7 @@ describe("HostAdaptor", () => {
|
|
|
169
236
|
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
170
237
|
expect(adaptor._polyfillDisplay).toEqual({ mode: "inline" });
|
|
171
238
|
});
|
|
172
|
-
it("getHostContextStore: display/viewState route to
|
|
239
|
+
it("getHostContextStore: display/viewState route to ChatGPT overlay when present, MCP otherwise", () => {
|
|
173
240
|
vi.stubGlobal("openai", {
|
|
174
241
|
view: { mode: "fullscreen" },
|
|
175
242
|
widgetState: { modelContent: { count: 5 }, privateContent: {} },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adaptor.test.js","sourceRoot":"","sources":["../../../src/web/bridges/adaptor.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { HostAdaptor } from \"./adaptor.js\";\nimport { AppsSdkBridge } from \"./apps-sdk/bridge.js\";\nimport { McpAppBridge } from \"./mcp-app/bridge.js\";\nimport { NotSupportedError } from \"./types.js\";\n\ndescribe(\"HostAdaptor\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", { hostType: \"apps-sdk\" });\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n localStorage.clear();\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n localStorage.clear();\n });\n\n it(\"callTool always routes to mcp.callServerTool\", async () => {\n vi.stubGlobal(\"openai\", { callTool: vi.fn() });\n const adaptor = new HostAdaptor();\n const fakeApp = {\n callServerTool: vi.fn().mockResolvedValue({\n content: [],\n structuredContent: { ok: true },\n isError: false,\n _meta: { x: 1 },\n }),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n\n const result = await adaptor.callTool(\"greet\", { name: \"alice\" });\n expect(fakeApp.callServerTool).toHaveBeenCalledWith({\n name: \"greet\",\n arguments: { name: \"alice\" },\n });\n expect(result.structuredContent).toEqual({ ok: true });\n expect(result.meta).toEqual({ x: 1 });\n });\n\n it(\"download returns isError when host lacks downloadFile capability\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const fakeApp = {\n getHostCapabilities: () => ({}),\n downloadFile: vi.fn(),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n const r = await adaptor.download({ contents: [] });\n expect(r.isError).toBe(true);\n expect(fakeApp.downloadFile).not.toHaveBeenCalled();\n });\n\n it(\"sendFollowUpMessage routes to window.openai only when scrollToBottom is set\", async () => {\n const sendFollowUpMessage = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { sendFollowUpMessage });\n let adaptor = new HostAdaptor();\n await adaptor.sendFollowUpMessage(\"hi\", { scrollToBottom: false });\n expect(sendFollowUpMessage).toHaveBeenCalledWith({\n prompt: \"hi\",\n scrollToBottom: false,\n });\n\n // Default case falls through to MCP\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n const sendMessage = vi.fn().mockResolvedValue(undefined);\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue({ sendMessage });\n await adaptor.sendFollowUpMessage(\"hi\");\n expect(sendMessage).toHaveBeenCalledWith({\n role: \"user\",\n content: [{ type: \"text\", text: \"hi\" }],\n });\n });\n\n it(\"openExternal routes to window.openai only when redirectUrl: false\", async () => {\n const openExternal = vi.fn();\n vi.stubGlobal(\"openai\", { openExternal });\n let adaptor = new HostAdaptor();\n adaptor.openExternal(\"https://x\", { redirectUrl: false });\n expect(openExternal).toHaveBeenCalledWith({\n href: \"https://x\",\n redirectUrl: false,\n });\n\n // Default case falls through to MCP openLink\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n const openLink = vi.fn();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue({ openLink });\n adaptor.openExternal(\"https://x\");\n await new Promise((r) => setTimeout(r, 0));\n expect(openLink).toHaveBeenCalledWith({ url: \"https://x\" });\n });\n\n it(\"Apps-SDK-exclusive methods throw NotSupportedError when window.openai is null\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n await expect(adaptor.uploadFile(new File([], \"x\"))).rejects.toThrow(\n NotSupportedError,\n );\n await expect(adaptor.selectFiles()).rejects.toThrow(NotSupportedError);\n await expect(adaptor.getFileDownloadUrl({ fileId: \"x\" })).rejects.toThrow(\n NotSupportedError,\n );\n await expect(adaptor.setOpenInAppUrl(\"https://x\")).rejects.toThrow(\n NotSupportedError,\n );\n });\n\n it(\"uploadFile delegates to window.openai.uploadFile and tracks fileId in widgetState\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n const uploadFile = vi\n .fn()\n .mockResolvedValue({ fileId: \"abc\", fileName: \"x\" });\n vi.stubGlobal(\"openai\", { uploadFile, setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n const file = new File([], \"x\");\n const r = await adaptor.uploadFile(file, { library: true });\n expect(uploadFile).toHaveBeenCalledWith(file, { library: true });\n expect(r.fileId).toBe(\"abc\");\n expect(setWidgetState).toHaveBeenCalledWith(\n expect.objectContaining({ imageIds: [\"abc\"] }),\n );\n });\n\n it(\"setViewState uses window.openai.setWidgetState when present\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n await adaptor.setViewState({ count: 1 });\n expect(setWidgetState).toHaveBeenCalledWith(\n expect.objectContaining({ modelContent: { count: 1 } }),\n );\n });\n\n it(\"setViewState falls back to MCP updateModelContext + localStorage when window.openai is absent\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const updateModelContext = vi.fn().mockResolvedValue(undefined);\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi\n .fn()\n .mockResolvedValue({ updateModelContext });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any)._viewUUID = \"view-1\";\n await adaptor.setViewState({ count: 2 });\n expect(updateModelContext).toHaveBeenCalledWith({\n structuredContent: { count: 2 },\n content: [{ type: \"text\", text: JSON.stringify({ count: 2 }) }],\n });\n const keys: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key !== null) {\n keys.push(key);\n }\n }\n expect(keys.some((k) => k.endsWith(\":view-1\"))).toBe(true);\n });\n\n it(\"openModal/closeModal: delegates to window.openai.requestModal when present, polyfill state otherwise\", () => {\n const requestModal = vi.fn();\n vi.stubGlobal(\"openai\", { requestModal });\n let adaptor = new HostAdaptor();\n adaptor.openModal({ title: \"Hi\" });\n expect(requestModal).toHaveBeenCalledWith({ title: \"Hi\" });\n expect(() => adaptor.closeModal()).not.toThrow();\n\n // Polyfill path\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n adaptor.openModal({ params: { x: 1 } });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._polyfillDisplay).toEqual({\n mode: \"modal\",\n params: { x: 1 },\n });\n adaptor.closeModal();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._polyfillDisplay).toEqual({ mode: \"inline\" });\n });\n\n it(\"getHostContextStore: display/viewState route to Apps SDK overlay when present, MCP otherwise\", () => {\n vi.stubGlobal(\"openai\", {\n view: { mode: \"fullscreen\" },\n widgetState: { modelContent: { count: 5 }, privateContent: {} },\n });\n let adaptor = new HostAdaptor();\n expect(adaptor.getHostContextStore(\"display\").getSnapshot()).toEqual({\n mode: \"fullscreen\",\n });\n expect(adaptor.getHostContextStore(\"viewState\").getSnapshot()).toEqual({\n count: 5,\n });\n // Other keys (theme) always flow through MCP; default fallback when no notification\n expect(adaptor.getHostContextStore(\"theme\").getSnapshot()).toBe(\"light\");\n\n // Polyfill / local-state path\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n expect(adaptor.getHostContextStore(\"display\").getSnapshot()).toEqual({\n mode: \"inline\",\n });\n expect(adaptor.getHostContextStore(\"viewState\").getSnapshot()).toBe(null);\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"adaptor.test.js","sourceRoot":"","sources":["../../../src/web/bridges/adaptor.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport { VIEW_STATE_WARNING_TOKENS } from \"../../context-warnings.js\";\nimport { HostAdaptor } from \"./adaptor.js\";\nimport { AppsSdkBridge } from \"./apps-sdk/bridge.js\";\nimport { McpAppBridge } from \"./mcp-app/bridge.js\";\nimport { NotSupportedError } from \"./types.js\";\n\nfunction stubApp(adaptor: HostAdaptor, app: object) {\n (\n adaptor as unknown as { mcp: { getApp: () => Promise<object> } }\n ).mcp.getApp = () => Promise.resolve(app);\n}\n\ndescribe(\"HostAdaptor\", () => {\n beforeEach(() => {\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"skybridge\", {});\n vi.stubGlobal(\"parent\", { postMessage: vi.fn() });\n localStorage.clear();\n });\n afterEach(() => {\n vi.unstubAllGlobals();\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n localStorage.clear();\n });\n\n it(\"callTool always routes to mcp.callServerTool\", async () => {\n vi.stubGlobal(\"openai\", { callTool: vi.fn() });\n const adaptor = new HostAdaptor();\n const fakeApp = {\n callServerTool: vi.fn().mockResolvedValue({\n content: [],\n structuredContent: { ok: true },\n isError: false,\n _meta: { x: 1 },\n }),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n\n const result = await adaptor.callTool(\"greet\", { name: \"alice\" });\n expect(fakeApp.callServerTool).toHaveBeenCalledWith(\n {\n name: \"greet\",\n arguments: { name: \"alice\" },\n },\n { timeout: undefined },\n );\n expect(result.structuredContent).toEqual({ ok: true });\n expect(result.meta).toEqual({ x: 1 });\n });\n\n it(\"download returns isError when host lacks downloadFile capability\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const fakeApp = {\n getHostCapabilities: () => ({}),\n downloadFile: vi.fn(),\n };\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue(fakeApp);\n const r = await adaptor.download({ contents: [] });\n expect(r.isError).toBe(true);\n expect(fakeApp.downloadFile).not.toHaveBeenCalled();\n });\n\n it(\"sendFollowUpMessage uses window.openai for plain text and ui/message for content blocks or a target\", async () => {\n const sendFollowUpMessage = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { sendFollowUpMessage });\n let adaptor = new HostAdaptor();\n await adaptor.sendFollowUpMessage(\"hi\", { scrollToBottom: false });\n expect(sendFollowUpMessage).toHaveBeenCalledWith({\n prompt: \"hi\",\n scrollToBottom: false,\n });\n\n // Default case falls through to MCP\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n const sendMessage = vi.fn().mockResolvedValue(undefined);\n const getHostCapabilities = () => ({\n experimental: { \"openai/message\": {} },\n });\n stubApp(adaptor, { sendMessage });\n await adaptor.sendFollowUpMessage(\"hi\");\n expect(sendMessage).toHaveBeenCalledWith({\n role: \"user\",\n content: [{ type: \"text\", text: \"hi\" }],\n });\n\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", { sendFollowUpMessage });\n adaptor = new HostAdaptor();\n stubApp(adaptor, { sendMessage, getHostCapabilities });\n const item = {\n type: \"text\" as const,\n text: \"M6 hex bolt\",\n _meta: { \"openai/title\": \"Hex bolt\" },\n };\n await adaptor.sendFollowUpMessage([item], { target: \"new\" });\n expect(sendMessage).toHaveBeenLastCalledWith({\n role: \"user\",\n content: [item],\n _meta: { \"openai/message\": { target: \"new\" } },\n });\n expect(sendFollowUpMessage).toHaveBeenCalledTimes(1);\n });\n\n it(\"openFile sends openai/files/open only on hosts advertising openai/files\", async () => {\n const adaptor = new HostAdaptor();\n const request = vi.fn().mockResolvedValue({});\n let capabilities: Record<string, unknown> = {};\n stubApp(adaptor, { request, getHostCapabilities: () => capabilities });\n\n await expect(adaptor.openFile(\"/tmp/part.stl\")).rejects.toThrow(\n \"openai/files\",\n );\n expect(request).not.toHaveBeenCalled();\n\n capabilities = { experimental: { \"openai/files\": {} } };\n await adaptor.openFile(\"/tmp/part.stl\");\n expect(request).toHaveBeenCalledWith(\n { method: \"openai/files/open\", params: { path: \"/tmp/part.stl\" } },\n expect.anything(),\n );\n });\n\n it(\"openExternal routes to window.openai only when redirectUrl: false\", async () => {\n const openExternal = vi.fn();\n vi.stubGlobal(\"openai\", { openExternal });\n let adaptor = new HostAdaptor();\n adaptor.openExternal(\"https://x\", { redirectUrl: false });\n expect(openExternal).toHaveBeenCalledWith({\n href: \"https://x\",\n redirectUrl: false,\n });\n\n // Default case falls through to MCP openLink\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n const openLink = vi.fn();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi.fn().mockResolvedValue({ openLink });\n adaptor.openExternal(\"https://x\");\n await new Promise((r) => setTimeout(r, 0));\n expect(openLink).toHaveBeenCalledWith({ url: \"https://x\" });\n });\n\n it(\"Apps-SDK-exclusive methods throw NotSupportedError when window.openai is null\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n await expect(adaptor.uploadFile(new File([], \"x\"))).rejects.toThrow(\n NotSupportedError,\n );\n await expect(adaptor.selectFiles()).rejects.toThrow(NotSupportedError);\n await expect(adaptor.getFileDownloadUrl({ fileId: \"x\" })).rejects.toThrow(\n NotSupportedError,\n );\n await expect(adaptor.setOpenInAppUrl(\"https://x\")).rejects.toThrow(\n NotSupportedError,\n );\n });\n\n it(\"uploadFile delegates to window.openai.uploadFile and tracks fileId in widgetState\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n const uploadFile = vi.fn().mockResolvedValue({\n fileId: \"abc\",\n fileName: \"x\",\n mimeType: \"image/png\",\n });\n vi.stubGlobal(\"openai\", { uploadFile, setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n const file = new File([], \"x\", { type: \"image/png\" });\n const r = await adaptor.uploadFile(file, { library: true });\n expect(uploadFile).toHaveBeenCalledWith(file, { library: true });\n expect(r.fileId).toBe(\"abc\");\n expect(setWidgetState).toHaveBeenCalledWith(\n expect.objectContaining({ imageIds: [\"abc\"] }),\n );\n });\n\n it(\"uploadFile does not track a non-image fileId in widgetState.imageIds\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n const uploadFile = vi.fn().mockResolvedValue({\n fileId: \"sediment://file_txt\",\n fileName: \"notes.txt\",\n mimeType: \"text/plain\",\n });\n vi.stubGlobal(\"openai\", { uploadFile, setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n\n await adaptor.uploadFile(new File([], \"notes.txt\", { type: \"text/plain\" }));\n\n for (const call of setWidgetState.mock.calls) {\n expect(call[0].imageIds ?? []).not.toContain(\"sediment://file_txt\");\n }\n });\n\n it(\"setViewState uses window.openai.setWidgetState when present\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n await adaptor.setViewState({ count: 1 });\n expect(setWidgetState).toHaveBeenCalledWith(\n expect.objectContaining({ modelContent: { count: 1 } }),\n );\n });\n\n it(\"warns when setViewState reaches 20K tokens\", async () => {\n const setWidgetState = vi.fn().mockResolvedValue(undefined);\n vi.stubGlobal(\"openai\", { setWidgetState, widgetState: null });\n const adaptor = new HostAdaptor();\n const warnSpy = vi.spyOn(console, \"warn\").mockImplementation(() => {});\n\n await adaptor.setViewState({\n longText: \"x\".repeat(VIEW_STATE_WARNING_TOKENS * 4),\n });\n\n expect(warnSpy).toHaveBeenCalledWith(\n expect.stringContaining(\"setWidgetState is persisting\"),\n );\n\n warnSpy.mockRestore();\n });\n\n it(\"setViewState falls back to MCP updateModelContext + localStorage when window.openai is absent\", async () => {\n vi.stubGlobal(\"openai\", undefined);\n const adaptor = new HostAdaptor();\n const updateModelContext = vi.fn().mockResolvedValue(undefined);\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any).mcp.getApp = vi\n .fn()\n .mockResolvedValue({ updateModelContext });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n (adaptor as any)._viewUUID = \"view-1\";\n await adaptor.setViewState({ count: 2 });\n expect(updateModelContext).toHaveBeenCalledWith({\n structuredContent: { count: 2 },\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ count: 2 }),\n annotations: { audience: [\"assistant\"] },\n },\n ],\n });\n const keys: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key !== null) {\n keys.push(key);\n }\n }\n expect(keys.some((k) => k.endsWith(\":view-1\"))).toBe(true);\n });\n\n it(\"openModal/closeModal: delegates to window.openai.requestModal when present, polyfill state otherwise\", () => {\n const requestModal = vi.fn();\n vi.stubGlobal(\"openai\", { requestModal });\n let adaptor = new HostAdaptor();\n adaptor.openModal({ title: \"Hi\" });\n expect(requestModal).toHaveBeenCalledWith({ title: \"Hi\" });\n expect(() => adaptor.closeModal()).not.toThrow();\n\n // Polyfill path\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n adaptor.openModal({ params: { x: 1 } });\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._polyfillDisplay).toEqual({\n mode: \"modal\",\n params: { x: 1 },\n });\n adaptor.closeModal();\n // biome-ignore lint/suspicious/noExplicitAny: test seam\n expect((adaptor as any)._polyfillDisplay).toEqual({ mode: \"inline\" });\n });\n\n it(\"getHostContextStore: display/viewState route to ChatGPT overlay when present, MCP otherwise\", () => {\n vi.stubGlobal(\"openai\", {\n view: { mode: \"fullscreen\" },\n widgetState: { modelContent: { count: 5 }, privateContent: {} },\n });\n let adaptor = new HostAdaptor();\n expect(adaptor.getHostContextStore(\"display\").getSnapshot()).toEqual({\n mode: \"fullscreen\",\n });\n expect(adaptor.getHostContextStore(\"viewState\").getSnapshot()).toEqual({\n count: 5,\n });\n // Other keys (theme) always flow through MCP; default fallback when no notification\n expect(adaptor.getHostContextStore(\"theme\").getSnapshot()).toBe(\"light\");\n\n // Polyfill / local-state path\n McpAppBridge.resetInstance();\n AppsSdkBridge.resetInstance();\n vi.stubGlobal(\"openai\", undefined);\n adaptor = new HostAdaptor();\n expect(adaptor.getHostContextStore(\"display\").getSnapshot()).toEqual({\n mode: \"inline\",\n });\n expect(adaptor.getHostContextStore(\"viewState\").getSnapshot()).toBe(null);\n });\n});\n"]}
|
|
@@ -4,7 +4,7 @@ export class AppsSdkBridge {
|
|
|
4
4
|
static instance = null;
|
|
5
5
|
static getInstance() {
|
|
6
6
|
if (window.openai === undefined) {
|
|
7
|
-
throw new Error("Apps SDK Bridge requires window.openai (
|
|
7
|
+
throw new Error("Apps SDK Bridge requires window.openai (ChatGPT runtime).");
|
|
8
8
|
}
|
|
9
9
|
if (AppsSdkBridge.instance === null) {
|
|
10
10
|
AppsSdkBridge.instance = new AppsSdkBridge();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../../../src/web/bridges/apps-sdk/bridge.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,sBAAsB,GAEvB,MAAM,YAAY,CAAC;AAEpB,qFAAqF;AACrF,MAAM,OAAO,aAAa;IAChB,MAAM,CAAC,QAAQ,GAAyB,IAAI,CAAC;IAE9C,MAAM,CAAC,WAAW;QACvB,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../../../src/web/bridges/apps-sdk/bridge.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,sBAAsB,GAEvB,MAAM,YAAY,CAAC;AAEpB,qFAAqF;AACrF,MAAM,OAAO,aAAa;IAChB,MAAM,CAAC,QAAQ,GAAyB,IAAI,CAAC;IAE9C,MAAM,CAAC,WAAW;QACvB,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,2DAA2D,CAC5D,CAAC;QACJ,CAAC;QACD,IAAI,aAAa,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACpC,aAAa,CAAC,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;QAC/C,CAAC;QACD,OAAO,aAAa,CAAC,QAAQ,CAAC;IAChC,CAAC;IAEM,MAAM,CAAC,aAAa;QACzB,IAAI,aAAa,CAAC,QAAQ,EAAE,CAAC;YAC3B,aAAa,CAAC,QAAQ,GAAG,IAAI,CAAC;QAChC,CAAC;IACH,CAAC;IAIM,SAAS,CACd,SAAmE;QAEnE,MAAM,IAAI,GAAsC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;YACtE,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAChB,OAAO,CAAC,QAAoB,EAAE,EAAE;YAC9B,MAAM,eAAe,GAAG,CAAC,KAAsB,EAAE,EAAE;gBACjD,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CACjC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,CACjD,CAAC;gBACF,IAAI,CAAC,iBAAiB,EAAE,CAAC;oBACvB,OAAO;gBACT,CAAC;gBACD,QAAQ,EAAE,CAAC;YACb,CAAC,CAAC;YAEF,MAAM,CAAC,gBAAgB,CAAC,sBAAsB,EAAE,eAAe,EAAE;gBAC/D,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;YAEH,OAAO,GAAG,EAAE;gBACV,MAAM,CAAC,mBAAmB,CAAC,sBAAsB,EAAE,eAAe,CAAC,CAAC;YACtE,CAAC,CAAC;QACJ,CAAC,CAAC;IACJ,CAAC;IAEM,WAAW,GAAG,CAAiC,GAAM,EAAE,EAAE;QAC9D,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,+EAA+E,GAAG,8CAA8C,CACjI,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC,CAAC;IAEK,mBAAmB;QAIxB,OAAO;YACL,OAAO,EAAE;gBACP,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;gBACjC,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;aAC5C;YACD,SAAS,EAAE;gBACT,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;gBACxC,WAAW,EAAE,GAAG,EAAE,CAChB,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,EAAE,YAAY,IAAI,IAAI;aACxD;SACF,CAAC;IACJ,CAAC","sourcesContent":["import type { Bridge, HostContextStore, Subscribe } from \"../types.js\";\nimport {\n type AppsSdkContext,\n SET_GLOBALS_EVENT_TYPE,\n type SetGlobalsEvent,\n} from \"./types.js\";\n\n/** @internal Singleton bridge over `window.openai` events. Used by `HostAdaptor`. */\nexport class AppsSdkBridge implements Bridge<AppsSdkContext> {\n private static instance: AppsSdkBridge | null = null;\n\n public static getInstance(): AppsSdkBridge {\n if (window.openai === undefined) {\n throw new Error(\n \"Apps SDK Bridge requires window.openai (ChatGPT runtime).\",\n );\n }\n if (AppsSdkBridge.instance === null) {\n AppsSdkBridge.instance = new AppsSdkBridge();\n }\n return AppsSdkBridge.instance;\n }\n\n public static resetInstance(): void {\n if (AppsSdkBridge.instance) {\n AppsSdkBridge.instance = null;\n }\n }\n\n public subscribe(key: keyof AppsSdkContext): Subscribe;\n public subscribe(keys: readonly (keyof AppsSdkContext)[]): Subscribe;\n public subscribe(\n keyOrKeys: keyof AppsSdkContext | readonly (keyof AppsSdkContext)[],\n ): Subscribe {\n const keys: readonly (keyof AppsSdkContext)[] = Array.isArray(keyOrKeys)\n ? keyOrKeys\n : [keyOrKeys];\n return (onChange: () => void) => {\n const handleSetGlobal = (event: SetGlobalsEvent) => {\n const hasRelevantChange = keys.some(\n (key) => event.detail.globals[key] !== undefined,\n );\n if (!hasRelevantChange) {\n return;\n }\n onChange();\n };\n\n window.addEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal, {\n passive: true,\n });\n\n return () => {\n window.removeEventListener(SET_GLOBALS_EVENT_TYPE, handleSetGlobal);\n };\n };\n }\n\n public getSnapshot = <K extends keyof AppsSdkContext>(key: K) => {\n if (window.openai === undefined) {\n throw new Error(\n `window.openai is not available. Make sure you're calling the hook requiring ${key} within the OpenAI iFrame skybridge runtime.`,\n );\n }\n\n return window.openai[key];\n };\n\n public createOverlayStores(): {\n display: HostContextStore<\"display\">;\n viewState: HostContextStore<\"viewState\">;\n } {\n return {\n display: {\n subscribe: this.subscribe(\"view\"),\n getSnapshot: () => this.getSnapshot(\"view\"),\n },\n viewState: {\n subscribe: this.subscribe(\"widgetState\"),\n getSnapshot: () =>\n this.getSnapshot(\"widgetState\")?.modelContent ?? null,\n },\n };\n }\n}\n"]}
|