diff --git a/packages/plugin-browser/src/index.ts b/packages/plugin-browser/src/index.ts index ec197735f96..fea1a0f6c0c 100644 --- a/packages/plugin-browser/src/index.ts +++ b/packages/plugin-browser/src/index.ts @@ -110,7 +110,7 @@ export default Plugin.define({ if (!browser) return yield* new Tool.Error({ message: - "[browser.disconnected] No desktop browser is connected to this session. Open this session in the desktop app, enable Browser pane, and wait for it to connect. Then call browser.tabs.list({}). Repeating browser actions while disconnected will not help.", + "[browser.disconnected] No desktop browser is connected to this session. Open this session in the desktop app, enable the experimental browser setting, and wait for it to connect. Then call browser.tabs.list({}). Repeating browser actions while disconnected will not help.", }) const tab = "tabID" in action ? browser.state.tabs.find((tab) => tab.id === action.tabID) : undefined if ("tabID" in action && !tab) @@ -172,7 +172,16 @@ export default Plugin.define({ ) const value = result.files.length ? { - ...requireObject(result.value), + ...(yield* Schema.decodeUnknownEffect(Schema.JsonObject)(result.value).pipe( + Effect.mapError( + (error) => + new Tool.Error({ + message: + "Browser returned malformed file output. Check desktop/server plugin compatibility and report the invalid response; do not repeat the capture to repair a protocol error.", + error, + }), + ), + )), files: result.files.map((file) => ({ id: file.id, name: file.name, @@ -245,15 +254,6 @@ export default Plugin.define({ }), }) -function requireObject(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) - throw new Tool.Error({ - message: - "Browser returned malformed file output. Check desktop/server plugin compatibility and report the invalid response; do not repeat the capture to repair a protocol error.", - }) - return value as Record -} - function normalizeAction(action: Browser.Action): Browser.Action { if (action.type !== "navigate" && action.type !== "tabs.open") return action if (action.type === "tabs.open" && action.url === undefined) return action diff --git a/packages/sdk/test/browser-plugin.test.ts b/packages/sdk/test/browser-plugin.test.ts index d38bb887921..20381b24a98 100644 --- a/packages/sdk/test/browser-plugin.test.ts +++ b/packages/sdk/test/browser-plugin.test.ts @@ -195,6 +195,30 @@ test( expect(yield* Fiber.join(invalid.pending).pipe(Effect.flip)).toMatchObject({ message: expect.stringContaining("Check that the desktop and server plugin use compatible versions"), }) + const malformed = yield* host.command({ type: "screenshot", tabID: tab.id }) + yield* host.rpc.result( + { + ...malformed.input, + outcome: { + type: "success", + result: { + value: null, + files: [ + { + id: Browser.FileID.make(`file_${crypto.randomUUID()}`), + name: "screenshot.png", + mime: "image/png", + data: png, + }, + ], + }, + }, + }, + { location: host.location }, + ) + expect((yield* Fiber.join(malformed.pending).pipe(Effect.flip)).message).toContain( + "Browser returned malformed file output", + ) const missing = yield* run('return await tools.browser.click({ref:"e1"})') expect(missing.metadata).toMatchObject({ error: true }) expect(missing.output).toMatchObject({ output: expect.stringContaining("tabID") })