fix(browser): return malformed capture errors as tool failures

This commit is contained in:
LukeParkerDev 2026-09-03 19:33:09 +10:00
parent 7ec54a9f89
commit 5686be02af
2 changed files with 35 additions and 11 deletions

View file

@ -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<string, unknown> {
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<string, unknown>
}
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

View file

@ -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") })