skybridge 2.0.4 → 2.1.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/README.md +9 -9
- package/dist/cli/run-plain.js +2 -1
- package/dist/cli/run-plain.js.map +1 -1
- package/dist/cli/use-nodemon.d.ts +11 -1
- package/dist/cli/use-nodemon.js +19 -5
- package/dist/cli/use-nodemon.js.map +1 -1
- package/dist/commands/dev.js +3 -3
- package/dist/commands/dev.js.map +1 -1
- package/dist/server/app.d.ts +22 -5
- package/dist/server/app.js +24 -13
- package/dist/server/app.js.map +1 -1
- package/dist/server/app.test.js +81 -0
- package/dist/server/app.test.js.map +1 -1
- package/dist/server/auth/providers/custom.d.ts +4 -2
- package/dist/server/auth/providers/custom.js.map +1 -1
- package/dist/server/auth/providers/workos.d.ts +3 -2
- package/dist/server/auth/providers/workos.js +2 -1
- package/dist/server/auth/providers/workos.js.map +1 -1
- package/dist/server/auth/verify.d.ts +5 -3
- package/dist/server/auth/verify.js.map +1 -1
- package/dist/server/auth/verify.test.js +16 -0
- package/dist/server/auth/verify.test.js.map +1 -1
- package/dist/server/events.d.ts +113 -0
- package/dist/server/events.js +318 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/events.test.d.ts +1 -0
- package/dist/server/events.test.js +172 -0
- package/dist/server/events.test.js.map +1 -0
- package/dist/server/express.d.ts +3 -2
- package/dist/server/express.js +17 -11
- package/dist/server/express.js.map +1 -1
- package/dist/server/express.test.js +45 -0
- package/dist/server/express.test.js.map +1 -1
- package/dist/server/form-input.d.ts +120 -0
- package/dist/server/form-input.js +89 -0
- package/dist/server/form-input.js.map +1 -0
- package/dist/server/index.d.ts +4 -2
- package/dist/server/index.js +3 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp-errors.test.js +188 -0
- package/dist/server/mcp-errors.test.js.map +1 -1
- package/dist/server/metric.d.ts +5 -3
- package/dist/server/metric.js +5 -5
- package/dist/server/metric.js.map +1 -1
- package/dist/server/register-tool.test.js +203 -0
- package/dist/server/register-tool.test.js.map +1 -1
- package/dist/server/server.d.ts +254 -10
- package/dist/server/server.js +262 -9
- package/dist/server/server.js.map +1 -1
- package/dist/server/tool-extra.test.js +79 -0
- package/dist/server/tool-extra.test.js.map +1 -1
- package/dist/web/bridges/adaptor.d.ts +14 -3
- package/dist/web/bridges/adaptor.js +164 -14
- package/dist/web/bridges/adaptor.js.map +1 -1
- package/dist/web/bridges/adaptor.test.js +62 -6
- 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/types.d.ts +2 -3
- package/dist/web/bridges/apps-sdk/types.js.map +1 -1
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.d.ts +3 -3
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js +3 -3
- package/dist/web/bridges/apps-sdk/use-apps-sdk-context.js.map +1 -1
- package/dist/web/bridges/mcp-app/bridge.d.ts +1 -1
- package/dist/web/bridges/mcp-app/bridge.js +9 -2
- package/dist/web/bridges/mcp-app/bridge.js.map +1 -1
- package/dist/web/bridges/mcp-app/types.d.ts +18 -3
- 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/types.d.ts +57 -4
- package/dist/web/bridges/types.js +1 -1
- package/dist/web/bridges/types.js.map +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 +6 -0
- package/dist/web/data-llm.test.js.map +1 -1
- package/dist/web/generate-helpers.d.ts +2 -2
- package/dist/web/generate-helpers.js +2 -2
- package/dist/web/generate-helpers.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/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 +13 -2
- 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-file-resource.d.ts +37 -0
- package/dist/web/hooks/use-file-resource.js +98 -0
- package/dist/web/hooks/use-file-resource.js.map +1 -0
- package/dist/web/hooks/use-file-resource.test.d.ts +1 -0
- package/dist/web/hooks/use-file-resource.test.js +65 -0
- package/dist/web/hooks/use-file-resource.test.js.map +1 -0
- package/dist/web/hooks/use-host.d.ts +27 -5
- package/dist/web/hooks/use-host.js +20 -5
- package/dist/web/hooks/use-host.js.map +1 -1
- 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-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 +1 -1
- 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-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/package.json +6 -5
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
import { ResultSchema } from "@modelcontextprotocol/core";
|
|
2
|
+
import * as z from "zod/v4";
|
|
1
3
|
import { warnOnLargeViewState } from "../../context-warnings.js";
|
|
2
4
|
import { AppsSdkBridge } from "./apps-sdk/bridge.js";
|
|
3
5
|
import { McpAppBridge } from "./mcp-app/bridge.js";
|
|
4
6
|
import { NotSupportedError } from "./types.js";
|
|
7
|
+
const ResourceMetaSchema = z.object({
|
|
8
|
+
writable: z.boolean().optional(),
|
|
9
|
+
etag: z.string().optional(),
|
|
10
|
+
});
|
|
11
|
+
const WriteResultSchema = z.discriminatedUnion("outcome", [
|
|
12
|
+
z.object({ outcome: z.enum(["saved", "conflict"]), etag: z.string() }),
|
|
13
|
+
z.object({ outcome: z.literal("too-large"), maxBytes: z.number() }),
|
|
14
|
+
]);
|
|
5
15
|
const STORAGE_PREFIX = "sb:";
|
|
6
16
|
const MAX_STORAGE_ENTRIES = 200;
|
|
7
17
|
function isImage(mimeType) {
|
|
@@ -41,11 +51,15 @@ export class HostAdaptor {
|
|
|
41
51
|
_viewState = null;
|
|
42
52
|
viewStateListeners = new Set();
|
|
43
53
|
_viewUUID = null;
|
|
54
|
+
_modelContext = null;
|
|
55
|
+
resourceListeners = new Map();
|
|
56
|
+
resourceApp;
|
|
44
57
|
_polyfillDisplay = { mode: "inline" };
|
|
45
58
|
polyfillDisplayListeners = new Set();
|
|
46
59
|
polyfillDisplayStore;
|
|
47
60
|
polyfillViewStateStore;
|
|
48
61
|
unsubscribeViewUUID = null;
|
|
62
|
+
unsubscribeModelContext = null;
|
|
49
63
|
constructor() {
|
|
50
64
|
this.mcp = McpAppBridge.getInstance();
|
|
51
65
|
let overlayStores = null;
|
|
@@ -79,20 +93,29 @@ export class HostAdaptor {
|
|
|
79
93
|
viewState: overlayStores?.viewState ?? this.polyfillViewStateStore,
|
|
80
94
|
};
|
|
81
95
|
this.subscribeToViewUUID();
|
|
96
|
+
this.unsubscribeModelContext = this.mcp.subscribe("openai/modelContext")(() => {
|
|
97
|
+
if (this.mcp.getSnapshot("openai/modelContext") === null) {
|
|
98
|
+
this._modelContext = null;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
82
101
|
}
|
|
83
102
|
/** @internal Release any subscriptions held on the underlying bridges. */
|
|
84
103
|
cleanup() {
|
|
85
104
|
this.unsubscribeViewUUID?.();
|
|
86
105
|
this.unsubscribeViewUUID = null;
|
|
106
|
+
this.unsubscribeModelContext?.();
|
|
107
|
+
this.unsubscribeModelContext = null;
|
|
87
108
|
}
|
|
88
109
|
// ---- Adaptor interface ----
|
|
89
110
|
getHostContextStore = (key) => this.stores[key];
|
|
90
|
-
callTool = async (name, args) => {
|
|
111
|
+
callTool = async (name, args, options) => {
|
|
112
|
+
const timeout = options?.timeout;
|
|
113
|
+
if (timeout !== undefined && (!Number.isFinite(timeout) || timeout <= 0)) {
|
|
114
|
+
throw new RangeError("Tool call timeout must be a positive, finite number of milliseconds.");
|
|
115
|
+
}
|
|
91
116
|
const app = await this.mcp.getApp();
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
arguments: args ?? undefined,
|
|
95
|
-
});
|
|
117
|
+
const params = { name, arguments: args ?? undefined };
|
|
118
|
+
const response = await app.callServerTool(params, { timeout });
|
|
96
119
|
return {
|
|
97
120
|
content: response.content,
|
|
98
121
|
structuredContent: response.structuredContent ?? {},
|
|
@@ -119,7 +142,8 @@ export class HostAdaptor {
|
|
|
119
142
|
await app.sendSizeChanged(size);
|
|
120
143
|
};
|
|
121
144
|
sendFollowUpMessage = async (prompt, options) => {
|
|
122
|
-
|
|
145
|
+
const target = options?.target;
|
|
146
|
+
if (this.openai && typeof prompt === "string" && target !== "new") {
|
|
123
147
|
await this.openai.sendFollowUpMessage({
|
|
124
148
|
prompt,
|
|
125
149
|
scrollToBottom: options?.scrollToBottom,
|
|
@@ -127,10 +151,18 @@ export class HostAdaptor {
|
|
|
127
151
|
return;
|
|
128
152
|
}
|
|
129
153
|
const app = await this.mcp.getApp();
|
|
130
|
-
|
|
154
|
+
if (target === "new" &&
|
|
155
|
+
!app.getHostCapabilities()?.experimental?.["openai/message"]) {
|
|
156
|
+
throw new NotSupportedError("sendFollowUpMessage", "the host does not advertise openai/message, so it can't open a new conversation");
|
|
157
|
+
}
|
|
158
|
+
const result = await app.sendMessage({
|
|
131
159
|
role: "user",
|
|
132
|
-
content: [{ type: "text", text: prompt }],
|
|
160
|
+
content: typeof prompt === "string" ? [{ type: "text", text: prompt }] : prompt,
|
|
161
|
+
...(target === "new" && { _meta: { "openai/message": { target } } }),
|
|
133
162
|
});
|
|
163
|
+
if (result?.isError) {
|
|
164
|
+
throw new Error("The host rejected the follow-up message.");
|
|
165
|
+
}
|
|
134
166
|
};
|
|
135
167
|
openExternal = (href, options) => {
|
|
136
168
|
if (this.openai) {
|
|
@@ -176,20 +208,135 @@ export class HostAdaptor {
|
|
|
176
208
|
});
|
|
177
209
|
this.persistToLocalStorage(newState);
|
|
178
210
|
try {
|
|
179
|
-
|
|
180
|
-
await app.updateModelContext({
|
|
181
|
-
structuredContent: newState,
|
|
182
|
-
content: [{ type: "text", text: JSON.stringify(newState) }],
|
|
183
|
-
});
|
|
211
|
+
await this.sendModelContext();
|
|
184
212
|
}
|
|
185
213
|
catch (error) {
|
|
186
214
|
console.error("Failed to update view state in MCP App.", error);
|
|
187
215
|
}
|
|
188
216
|
};
|
|
217
|
+
updateModelContext = async (params) => {
|
|
218
|
+
const app = await this.mcp.getApp();
|
|
219
|
+
const capabilities = app.getHostCapabilities();
|
|
220
|
+
if (!capabilities?.updateModelContext &&
|
|
221
|
+
!capabilities?.experimental?.["openai/modelContext"]) {
|
|
222
|
+
throw new NotSupportedError("updateModelContext", "the host does not advertise updateModelContext");
|
|
223
|
+
}
|
|
224
|
+
const previous = this._modelContext;
|
|
225
|
+
this._modelContext = params;
|
|
226
|
+
const result = await this.sendModelContext().catch((error) => {
|
|
227
|
+
if (this._modelContext === params) {
|
|
228
|
+
this._modelContext = previous;
|
|
229
|
+
}
|
|
230
|
+
throw error;
|
|
231
|
+
});
|
|
232
|
+
const parsed = z
|
|
233
|
+
.object({ updateId: z.string() })
|
|
234
|
+
.safeParse(result._meta?.["openai/modelContext"]);
|
|
235
|
+
return parsed.success ? parsed.data.updateId : undefined;
|
|
236
|
+
};
|
|
237
|
+
async sendModelContext() {
|
|
238
|
+
const viewState = this.openai ? null : this._viewState;
|
|
239
|
+
const extra = this._modelContext;
|
|
240
|
+
const collisions = Object.keys(extra?.structuredContent ?? {}).filter((key) => viewState !== null && key in viewState);
|
|
241
|
+
if (collisions.length > 0) {
|
|
242
|
+
console.warn(`skybridge: view state overrides model context keys ${collisions.join(", ")}.`);
|
|
243
|
+
}
|
|
244
|
+
const structuredContent = { ...extra?.structuredContent, ...viewState };
|
|
245
|
+
const app = await this.mcp.getApp();
|
|
246
|
+
return app.updateModelContext({
|
|
247
|
+
...(Object.keys(structuredContent).length > 0 && { structuredContent }),
|
|
248
|
+
content: [
|
|
249
|
+
...(viewState === null
|
|
250
|
+
? []
|
|
251
|
+
: [
|
|
252
|
+
{
|
|
253
|
+
type: "text",
|
|
254
|
+
text: JSON.stringify(viewState),
|
|
255
|
+
annotations: { audience: ["assistant"] },
|
|
256
|
+
},
|
|
257
|
+
]),
|
|
258
|
+
...(extra?.content ?? []),
|
|
259
|
+
],
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
openFile = async (path) => {
|
|
263
|
+
const app = await this.mcp.getApp();
|
|
264
|
+
if (!app.getHostCapabilities()?.experimental?.["openai/files"]) {
|
|
265
|
+
throw new NotSupportedError("openFile", "the host does not advertise openai/files");
|
|
266
|
+
}
|
|
267
|
+
await app.request({ method: "openai/files/open", params: { path } }, ResultSchema);
|
|
268
|
+
};
|
|
269
|
+
readResource = async (uri, representation) => {
|
|
270
|
+
const app = await this.mcp.getApp();
|
|
271
|
+
const { contents } = await app.readServerResource({
|
|
272
|
+
uri,
|
|
273
|
+
...(representation && {
|
|
274
|
+
_meta: { "openai/resource": { representation } },
|
|
275
|
+
}),
|
|
276
|
+
});
|
|
277
|
+
const item = contents.find((content) => content.uri === uri) ?? contents[0];
|
|
278
|
+
if (!item) {
|
|
279
|
+
throw new Error(`The host returned no contents for ${uri}.`);
|
|
280
|
+
}
|
|
281
|
+
const { data: meta } = ResourceMetaSchema.safeParse(item._meta?.["openai/resource"]);
|
|
282
|
+
return {
|
|
283
|
+
...("text" in item ? { text: item.text } : { blob: item.blob }),
|
|
284
|
+
mimeType: item.mimeType,
|
|
285
|
+
writable: meta?.writable ?? false,
|
|
286
|
+
etag: meta?.etag,
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
watchResource = (uri, onUpdate) => {
|
|
290
|
+
const listeners = this.resourceListeners.get(uri) ?? new Set();
|
|
291
|
+
if (listeners.size === 0) {
|
|
292
|
+
this.resourceListeners.set(uri, listeners);
|
|
293
|
+
this.getResourceApp()
|
|
294
|
+
.then((app) => app?.request({ method: "resources/subscribe", params: { uri } }, ResultSchema))
|
|
295
|
+
.catch((error) => {
|
|
296
|
+
console.warn(`Failed to subscribe to ${uri}.`, error);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
listeners.add(onUpdate);
|
|
300
|
+
return () => {
|
|
301
|
+
if (listeners.delete(onUpdate) && listeners.size === 0) {
|
|
302
|
+
this.resourceListeners.delete(uri);
|
|
303
|
+
this.getResourceApp()
|
|
304
|
+
.then((app) => app?.request({ method: "resources/unsubscribe", params: { uri } }, ResultSchema))
|
|
305
|
+
.catch(() => { });
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
writeResource = async (uri, content, ifMatch) => {
|
|
310
|
+
const app = await this.getResourceApp();
|
|
311
|
+
if (!app) {
|
|
312
|
+
throw new NotSupportedError("writeResource", "the host does not advertise openai/resource");
|
|
313
|
+
}
|
|
314
|
+
return app.request({
|
|
315
|
+
method: "openai/resources/write",
|
|
316
|
+
params: { uri, ...content, ...(ifMatch && { ifMatch }) },
|
|
317
|
+
}, WriteResultSchema);
|
|
318
|
+
};
|
|
319
|
+
getResourceApp() {
|
|
320
|
+
this.resourceApp ??= this.mcp.getApp().then((app) => {
|
|
321
|
+
if (!app.getHostCapabilities()?.experimental?.["openai/resource"]) {
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
app.setNotificationHandler("notifications/resources/updated", ({ params }) => {
|
|
325
|
+
this.resourceListeners.get(params.uri)?.forEach((l) => {
|
|
326
|
+
l();
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
return app;
|
|
330
|
+
});
|
|
331
|
+
return this.resourceApp;
|
|
332
|
+
}
|
|
189
333
|
uploadFile = async (file, options) => {
|
|
190
334
|
if (!this.openai) {
|
|
191
335
|
throw new NotSupportedError("uploadFile");
|
|
192
336
|
}
|
|
337
|
+
if (!this.openai.uploadFile) {
|
|
338
|
+
throw new NotSupportedError("uploadFile", "not available on the current host version");
|
|
339
|
+
}
|
|
193
340
|
const metadata = await this.openai.uploadFile(file, options);
|
|
194
341
|
if (isImage(metadata.mimeType ?? file.type)) {
|
|
195
342
|
await this.trackFileIds(metadata.fileId);
|
|
@@ -200,6 +347,9 @@ export class HostAdaptor {
|
|
|
200
347
|
if (!this.openai) {
|
|
201
348
|
return Promise.reject(new NotSupportedError("getFileDownloadUrl"));
|
|
202
349
|
}
|
|
350
|
+
if (!this.openai.getFileDownloadUrl) {
|
|
351
|
+
return Promise.reject(new NotSupportedError("getFileDownloadUrl", "not available on the current host version"));
|
|
352
|
+
}
|
|
203
353
|
return this.openai.getFileDownloadUrl(file);
|
|
204
354
|
};
|
|
205
355
|
selectFiles = async () => {
|
|
@@ -207,7 +357,7 @@ export class HostAdaptor {
|
|
|
207
357
|
throw new NotSupportedError("selectFiles");
|
|
208
358
|
}
|
|
209
359
|
if (!this.openai.selectFiles) {
|
|
210
|
-
throw new
|
|
360
|
+
throw new NotSupportedError("selectFiles", "not available on the current host version");
|
|
211
361
|
}
|
|
212
362
|
const files = await this.openai.selectFiles();
|
|
213
363
|
const imageIds = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adaptor.js","sourceRoot":"","sources":["../../../src/web/bridges/adaptor.ts"],"names":[],"mappings":"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","sourcesContent":["import { 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 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 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\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 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 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 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
|
+
{"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 type { App } from \"@modelcontextprotocol/ext-apps\";\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 FileResource,\n FileResourceContent,\n FileResourceWriteResult,\n FollowUpMessage,\n HostContext,\n HostContextStore,\n ModelContextParams,\n OpenExternalOptions,\n RequestDisplayMode,\n RequestModalOptions,\n RequestSizeOptions,\n ResourceRepresentation,\n SendFollowUpMessageOptions,\n SetViewStateAction,\n UploadFileOptions,\n ViewToolConfig,\n} from \"./types.js\";\nimport { NotSupportedError } from \"./types.js\";\n\nconst ResourceMetaSchema = z.object({\n writable: z.boolean().optional(),\n etag: z.string().optional(),\n});\n\nconst WriteResultSchema = z.discriminatedUnion(\"outcome\", [\n z.object({ outcome: z.enum([\"saved\", \"conflict\"]), etag: z.string() }),\n z.object({ outcome: z.literal(\"too-large\"), maxBytes: z.number() }),\n]);\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 private readonly resourceListeners = new Map<string, Set<() => void>>();\n private resourceApp?: Promise<App | undefined>;\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 readResource = async (\n uri: string,\n representation?: ResourceRepresentation,\n ): Promise<FileResource> => {\n const app = await this.mcp.getApp();\n const { contents } = await app.readServerResource({\n uri,\n ...(representation && {\n _meta: { \"openai/resource\": { representation } },\n }),\n });\n const item = contents.find((content) => content.uri === uri) ?? contents[0];\n if (!item) {\n throw new Error(`The host returned no contents for ${uri}.`);\n }\n const { data: meta } = ResourceMetaSchema.safeParse(\n item._meta?.[\"openai/resource\"],\n );\n return {\n ...(\"text\" in item ? { text: item.text } : { blob: item.blob }),\n mimeType: item.mimeType,\n writable: meta?.writable ?? false,\n etag: meta?.etag,\n };\n };\n\n public watchResource = (uri: string, onUpdate: () => void): (() => void) => {\n const listeners = this.resourceListeners.get(uri) ?? new Set<() => void>();\n if (listeners.size === 0) {\n this.resourceListeners.set(uri, listeners);\n this.getResourceApp()\n .then((app) =>\n app?.request(\n { method: \"resources/subscribe\", params: { uri } },\n ResultSchema,\n ),\n )\n .catch((error: unknown) => {\n console.warn(`Failed to subscribe to ${uri}.`, error);\n });\n }\n listeners.add(onUpdate);\n return () => {\n if (listeners.delete(onUpdate) && listeners.size === 0) {\n this.resourceListeners.delete(uri);\n this.getResourceApp()\n .then((app) =>\n app?.request(\n { method: \"resources/unsubscribe\", params: { uri } },\n ResultSchema,\n ),\n )\n .catch(() => {});\n }\n };\n };\n\n public writeResource = async (\n uri: string,\n content: FileResourceContent,\n ifMatch?: string,\n ): Promise<FileResourceWriteResult> => {\n const app = await this.getResourceApp();\n if (!app) {\n throw new NotSupportedError(\n \"writeResource\",\n \"the host does not advertise openai/resource\",\n );\n }\n return app.request(\n {\n method: \"openai/resources/write\",\n params: { uri, ...content, ...(ifMatch && { ifMatch }) },\n },\n WriteResultSchema,\n );\n };\n\n private getResourceApp(): Promise<App | undefined> {\n this.resourceApp ??= this.mcp.getApp().then((app) => {\n if (!app.getHostCapabilities()?.experimental?.[\"openai/resource\"]) {\n return undefined;\n }\n app.setNotificationHandler(\n \"notifications/resources/updated\",\n ({ params }) => {\n this.resourceListeners.get(params.uri)?.forEach((l) => {\n l();\n });\n },\n );\n return app;\n });\n return this.resourceApp;\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 if (!this.openai.uploadFile) {\n throw new NotSupportedError(\n \"uploadFile\",\n \"not available on the current host version\",\n );\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 if (!this.openai.getFileDownloadUrl) {\n return Promise.reject(\n new NotSupportedError(\n \"getFileDownloadUrl\",\n \"not available on the current host version\",\n ),\n );\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 NotSupportedError(\n \"selectFiles\",\n \"not available on 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"]}
|
|
@@ -4,6 +4,9 @@ import { HostAdaptor } from "./adaptor.js";
|
|
|
4
4
|
import { AppsSdkBridge } from "./apps-sdk/bridge.js";
|
|
5
5
|
import { McpAppBridge } from "./mcp-app/bridge.js";
|
|
6
6
|
import { NotSupportedError } from "./types.js";
|
|
7
|
+
function stubApp(adaptor, app) {
|
|
8
|
+
adaptor.mcp.getApp = () => Promise.resolve(app);
|
|
9
|
+
}
|
|
7
10
|
describe("HostAdaptor", () => {
|
|
8
11
|
beforeEach(() => {
|
|
9
12
|
McpAppBridge.resetInstance();
|
|
@@ -35,7 +38,7 @@ describe("HostAdaptor", () => {
|
|
|
35
38
|
expect(fakeApp.callServerTool).toHaveBeenCalledWith({
|
|
36
39
|
name: "greet",
|
|
37
40
|
arguments: { name: "alice" },
|
|
38
|
-
});
|
|
41
|
+
}, { timeout: undefined });
|
|
39
42
|
expect(result.structuredContent).toEqual({ ok: true });
|
|
40
43
|
expect(result.meta).toEqual({ x: 1 });
|
|
41
44
|
});
|
|
@@ -52,7 +55,7 @@ describe("HostAdaptor", () => {
|
|
|
52
55
|
expect(r.isError).toBe(true);
|
|
53
56
|
expect(fakeApp.downloadFile).not.toHaveBeenCalled();
|
|
54
57
|
});
|
|
55
|
-
it("sendFollowUpMessage
|
|
58
|
+
it("sendFollowUpMessage uses window.openai for plain text and ui/message for content blocks or a target", async () => {
|
|
56
59
|
const sendFollowUpMessage = vi.fn().mockResolvedValue(undefined);
|
|
57
60
|
vi.stubGlobal("openai", { sendFollowUpMessage });
|
|
58
61
|
let adaptor = new HostAdaptor();
|
|
@@ -67,13 +70,43 @@ describe("HostAdaptor", () => {
|
|
|
67
70
|
vi.stubGlobal("openai", undefined);
|
|
68
71
|
adaptor = new HostAdaptor();
|
|
69
72
|
const sendMessage = vi.fn().mockResolvedValue(undefined);
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
const getHostCapabilities = () => ({
|
|
74
|
+
experimental: { "openai/message": {} },
|
|
75
|
+
});
|
|
76
|
+
stubApp(adaptor, { sendMessage });
|
|
72
77
|
await adaptor.sendFollowUpMessage("hi");
|
|
73
78
|
expect(sendMessage).toHaveBeenCalledWith({
|
|
74
79
|
role: "user",
|
|
75
80
|
content: [{ type: "text", text: "hi" }],
|
|
76
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());
|
|
77
110
|
});
|
|
78
111
|
it("openExternal routes to window.openai only when redirectUrl: false", async () => {
|
|
79
112
|
const openExternal = vi.fn();
|
|
@@ -104,6 +137,23 @@ describe("HostAdaptor", () => {
|
|
|
104
137
|
await expect(adaptor.getFileDownloadUrl({ fileId: "x" })).rejects.toThrow(NotSupportedError);
|
|
105
138
|
await expect(adaptor.setOpenInAppUrl("https://x")).rejects.toThrow(NotSupportedError);
|
|
106
139
|
});
|
|
140
|
+
it("file methods throw NotSupportedError when window.openai lacks them", async () => {
|
|
141
|
+
vi.stubGlobal("openai", {});
|
|
142
|
+
const adaptor = new HostAdaptor();
|
|
143
|
+
const uploadError = await adaptor
|
|
144
|
+
.uploadFile(new File([], "x"))
|
|
145
|
+
.catch((e) => e);
|
|
146
|
+
expect(uploadError).toBeInstanceOf(NotSupportedError);
|
|
147
|
+
expect(uploadError.method).toBe("uploadFile");
|
|
148
|
+
const downloadError = await adaptor
|
|
149
|
+
.getFileDownloadUrl({ fileId: "x" })
|
|
150
|
+
.catch((e) => e);
|
|
151
|
+
expect(downloadError).toBeInstanceOf(NotSupportedError);
|
|
152
|
+
expect(downloadError.method).toBe("getFileDownloadUrl");
|
|
153
|
+
const selectError = await adaptor.selectFiles().catch((e) => e);
|
|
154
|
+
expect(selectError).toBeInstanceOf(NotSupportedError);
|
|
155
|
+
expect(selectError.method).toBe("selectFiles");
|
|
156
|
+
});
|
|
107
157
|
it("uploadFile delegates to window.openai.uploadFile and tracks fileId in widgetState", async () => {
|
|
108
158
|
const setWidgetState = vi.fn().mockResolvedValue(undefined);
|
|
109
159
|
const uploadFile = vi.fn().mockResolvedValue({
|
|
@@ -164,7 +214,13 @@ describe("HostAdaptor", () => {
|
|
|
164
214
|
await adaptor.setViewState({ count: 2 });
|
|
165
215
|
expect(updateModelContext).toHaveBeenCalledWith({
|
|
166
216
|
structuredContent: { count: 2 },
|
|
167
|
-
content: [
|
|
217
|
+
content: [
|
|
218
|
+
{
|
|
219
|
+
type: "text",
|
|
220
|
+
text: JSON.stringify({ count: 2 }),
|
|
221
|
+
annotations: { audience: ["assistant"] },
|
|
222
|
+
},
|
|
223
|
+
],
|
|
168
224
|
});
|
|
169
225
|
const keys = [];
|
|
170
226
|
for (let i = 0; i < localStorage.length; i++) {
|
|
@@ -197,7 +253,7 @@ describe("HostAdaptor", () => {
|
|
|
197
253
|
// biome-ignore lint/suspicious/noExplicitAny: test seam
|
|
198
254
|
expect(adaptor._polyfillDisplay).toEqual({ mode: "inline" });
|
|
199
255
|
});
|
|
200
|
-
it("getHostContextStore: display/viewState route to
|
|
256
|
+
it("getHostContextStore: display/viewState route to ChatGPT overlay when present, MCP otherwise", () => {
|
|
201
257
|
vi.stubGlobal("openai", {
|
|
202
258
|
view: { mode: "fullscreen" },
|
|
203
259
|
widgetState: { modelContent: { count: 5 }, privateContent: {} },
|