feat: add anthropic web search support - #12
Open
darknessest wants to merge 2 commits into
Open
Conversation
darknessest
marked this pull request as ready for review
July 29, 2026 19:36
darknessest
marked this pull request as draft
July 29, 2026 19:55
darknessest
marked this pull request as ready for review
July 30, 2026 15:11
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds Anthropic as a fourth
websearch_citedprovider, backed by Anthropic's server-sideweb_search_20260318tool, pinned toallowed_callers: ["direct"].{ "provider": { "anthropic": { "options": { "websearch_cited": { "model": "claude-sonnet-4-6" } } } } }How
src/anthropic.tsfollows the existing provider shape (createAnthropicWebsearchClient→WebsearchClient), callsPOST /v1/messagesdirectly withfetch, and adds no dependencies.Auth, both supported by opencode:
x-api-key+anthropic-version.Authorization: Bearer,anthropic-beta: oauth-2025-04-20, and the Claude Code system block that these credentials require as the firstsystementry.The tool is pinned to
allowed_callers: ["direct"]rather than the version default. Onweb_search_20260209and later the default is["code_execution_20260120"](dynamic filtering), and a caller without code execution access gets HTTP 200 withcode_execution_tool_result_error: too_many_requests,web_search_requests: 0, and an uncited answer from model knowledge — a silent failure for a tool whose contract is cited search. Measured on Claude Pro/Max OAuth credentials: two dynamic-filtering attempts failed that way, bracketing a successfuldirectcall in between. Dynamic filtering is therefore not exposed as a config option.Formatting mirrors the Google provider's output contract:
web_search_result_locationcitations, deduplicated by URL, and rendered as[n]before any trailing whitespace so line breaks survive.Sources:list is appended only when at least one citation exists.web_search_tool_resultare the model narrating its intent to search, so they are dropped from the answer.web_search_tool_result_errorthrows with itserror_code, unless usable text was already produced — a later failed search should not discard an answer that is already grounded.stop_reasonofmax_tokens/pause_turnappends a truncation notice rather than silently returning a cut-off answer.Testing
bun checkclean,bun test:agent44 pass / 0 fail.POST https://api.anthropic.com/v1/messageswithweb_search_20260318+allowed_callers: ["direct"](trimmed, provenance recorded at the fixture), plus cases for narration,citations: null, non-web-search citation types, truncation, and search errors. A test asserts the tool definition keepsallowed_callers: ["direct"], so dropping it fails the suite.claude-sonnet-4-6andclaude-haiku-4-5-20251001; "What is the latest stable version of Bun?" returns a cited answer with aSources:list.Note
Independent of, and does not depend on, the OpenAI streaming fix PR; both branch from
main. They touchwebsearch.test.tsin different places, so whichever merges second may need a trivial rebase.