fix(core): bound web tool failures (#33259)

This commit is contained in:
Kit Langton 2026-06-21 21:37:25 +02:00 committed by GitHub
commit 69f1ec22e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 95 additions and 28 deletions

View file

@ -176,6 +176,25 @@ describe("WebFetchTool registration", () => {
}),
)
it.effect("returns an error result when HTML-to-Markdown conversion throws", () =>
Effect.gen(function* () {
reset()
respond = () =>
Effect.succeed(
new Response("<div>".repeat(10_000) + "content" + "</div>".repeat(10_000), {
headers: { "content-type": "text/html" },
}),
)
const registry = yield* ToolRegistry.Service
const url = "https://1.1.1.1/deep-html"
expect(yield* executeTool(registry, call({ url, format: "markdown" }))).toEqual({
type: "error",
value: `Unable to fetch ${url}`,
})
}),
)
it.effect("rejects declared and streamed oversized bodies", () =>
Effect.gen(function* () {
reset()

View file

@ -1,4 +1,4 @@
import { describe, expect, test } from "bun:test"
import { beforeEach, describe, expect, test } from "bun:test"
import { Effect, Layer, Schema } from "effect"
import { HttpClient, HttpClientResponse } from "effect/unstable/http"
import { PermissionV2 } from "@opencode-ai/core/permission"
@ -66,8 +66,14 @@ interface Request {
const requests: Request[] = []
const assertions: PermissionV2.AssertInput[] = []
let responseBody = payload("search results")
let makeResponse = () => new Response(responseBody, { status: 200 })
let config: WebSearchTool.Config = { enableExa: false, enableParallel: false }
beforeEach(() => {
responseBody = payload("search results")
makeResponse = () => new Response(responseBody, { status: 200 })
})
const http = Layer.succeed(
HttpClient.HttpClient,
HttpClient.make((request) =>
@ -78,7 +84,7 @@ const http = Layer.succeed(
headers: request.headers,
body: JSON.parse(new TextDecoder().decode(request.body.body)),
})
return HttpClientResponse.fromWeb(request, new Response(responseBody, { status: 200 }))
return HttpClientResponse.fromWeb(request, makeResponse())
}),
),
)
@ -270,7 +276,22 @@ describe("WebSearchTool registration", () => {
Effect.gen(function* () {
requests.length = 0
assertions.length = 0
responseBody = "x".repeat(WebSearchTool.MAX_RESPONSE_BYTES + 1)
let chunksRead = 0
let cancelled = false
makeResponse = () =>
new Response(
new ReadableStream({
pull(controller) {
chunksRead++
if (chunksRead === 10) throw new Error("response was not stopped at the byte limit")
controller.enqueue(new Uint8Array(64 * 1024))
},
cancel() {
cancelled = true
},
}),
{ status: 200 },
)
config = { provider: "exa", enableExa: false, enableParallel: false }
const registry = yield* ToolRegistry.Service
@ -281,6 +302,8 @@ describe("WebSearchTool registration", () => {
call: { type: "tool-call", id: "call-large-response", name: "websearch", input: { query: "too much" } },
}),
).toEqual({ type: "error", value: "Unable to search the web for too much" })
expect(chunksRead).toBeLessThan(10)
expect(cancelled).toBe(true)
}),
)
})