Area
Proxy and routing · Web-search sidecar
What are you trying to accomplish?
When the main model is routed through a third-party provider, the web-search sidecar currently has only two backends: openai (gpt-mini via the ChatGPT forward path) and anthropic (stored Claude OAuth). Both borrow an account the user may have very little quota on — and when that quota is exhausted, search turns degrade (the exact scenario from #398).
I'd like to point the web-search sidecar at a provider I actually have quota on: OpenCode Zen (https://opencode.ai/zen/go/v1, the built-in "opencode-go" provider), using e.g. deepseek-v4-flash to execute the search — with zero ChatGPT/Claude quota consumption.
What prevents this today?
OcxWebSearchSidecarConfig.backend is hardcoded to "openai" | "anthropic" (src/types.ts, src/web-search/index.ts resolveSidecarBackend); there is no keyed/custom-provider option.
- The hosted
web_search tool is unconditionally stripped for routed providers (src/responses/parser.ts buildTools), and the sidecar executor only knows how to POST to the ChatGPT forward endpoint with forwarded OAuth headers (src/web-search/executor.ts runWebSearch).
- I verified there is no config-only workaround (changing
webSearchSidecar.model, disabling the sidecar, or repointing the openai provider's baseUrl all fail — the latter breaks isCanonicalOpenAiForwardProvider).
Key fact: OpenCode Zen natively serves hosted web_search
Unlike a plain OpenAI-compatible chat gateway, OpenCode Zen executes the Responses API's hosted web_search tool server-side. Tested 2026-08-13 with a opencode-go API key (model deepseek-v4-flash):
POST https://opencode.ai/zen/go/v1/responses with tools: [{type: "web_search", max_results: 5}] + tool_choice: {type: "web_search"} → 200.
- The model ran real multi-step searches (
web_search_call with search and open_page actions) and returned a summary with source URLs.
- Streaming emits standard Responses SSE —
response.output_text.delta, response.output_text.done, response.completed (plus ping heartbeats) — exactly the event shapes parseSidecarSSE already handles.
- The gateway reports
"cost": "0" for these calls; either way, the spend lands on the user's Zen key, not ChatGPT/Claude.
This is the "provider-native search capability" category of #415 — Zen just isn't on the list there yet. It also fits #414's "decouple which account backs search" goal without adding a dedicated search vendor.
Proposed change (minimal, additive)
Add a third sidecar backend, e.g. backend: "keyed" with provider + model config, that POSTs to the named provider's {baseUrl}/responses with Authorization: Bearer <apiKey> and the same hosted tool + BASE_INSTRUCTION, reusing the existing loop, parseSidecarSSE, and format-result unchanged:
src/types.ts — extend OcxWebSearchSidecarConfig (backend: "keyed", provider?: string)
src/web-search/executor.ts — add runKeyedWebSearch() (~30 lines, mirrors runWebSearch with Bearer auth)
src/web-search/index.ts — planWebSearch keyed branch, resolving a configured key-auth openai-responses provider
src/web-search/loop.ts + src/server/responses/core.ts — thread the keyed provider through and dispatch
Roughly 60–80 additive lines; the ChatGPT forward path and anthropic path stay untouched.
Acceptance criteria
- Config like
webSearchSidecar: { backend: "keyed", provider: "opencode-go", model: "deepseek-v4-flash" } makes search runs on the Zen key with no ChatGPT/Claude quota involvement.
- Search cells, citations/sources, and the loop behavior match the existing
openai backend.
Related
#398 (original fixed-backend bug), #414 (dedicated search vendors like Exa), #415 (provider-native search, e.g. Gemini Grounding — Zen is a sibling case), #1276 (superseded umbrella; maintainer's note that arbitrary LLMs can't auto-back search is exactly why Zen's hosted tool is special).
Area
Proxy and routing · Web-search sidecar
What are you trying to accomplish?
When the main model is routed through a third-party provider, the web-search sidecar currently has only two backends:
openai(gpt-mini via the ChatGPT forward path) andanthropic(stored Claude OAuth). Both borrow an account the user may have very little quota on — and when that quota is exhausted, search turns degrade (the exact scenario from #398).I'd like to point the web-search sidecar at a provider I actually have quota on: OpenCode Zen (
https://opencode.ai/zen/go/v1, the built-in "opencode-go" provider), using e.g.deepseek-v4-flashto execute the search — with zero ChatGPT/Claude quota consumption.What prevents this today?
OcxWebSearchSidecarConfig.backendis hardcoded to"openai" | "anthropic"(src/types.ts,src/web-search/index.tsresolveSidecarBackend); there is no keyed/custom-provider option.web_searchtool is unconditionally stripped for routed providers (src/responses/parser.tsbuildTools), and the sidecar executor only knows how to POST to the ChatGPT forward endpoint with forwarded OAuth headers (src/web-search/executor.tsrunWebSearch).webSearchSidecar.model, disabling the sidecar, or repointing theopenaiprovider'sbaseUrlall fail — the latter breaksisCanonicalOpenAiForwardProvider).Key fact: OpenCode Zen natively serves hosted
web_searchUnlike a plain OpenAI-compatible chat gateway, OpenCode Zen executes the Responses API's hosted
web_searchtool server-side. Tested 2026-08-13 with aopencode-goAPI key (modeldeepseek-v4-flash):POST https://opencode.ai/zen/go/v1/responseswithtools: [{type: "web_search", max_results: 5}]+tool_choice: {type: "web_search"}→200.web_search_callwithsearchandopen_pageactions) and returned a summary with source URLs.response.output_text.delta,response.output_text.done,response.completed(pluspingheartbeats) — exactly the event shapesparseSidecarSSEalready handles."cost": "0"for these calls; either way, the spend lands on the user's Zen key, not ChatGPT/Claude.This is the "provider-native search capability" category of #415 — Zen just isn't on the list there yet. It also fits #414's "decouple which account backs search" goal without adding a dedicated search vendor.
Proposed change (minimal, additive)
Add a third sidecar backend, e.g.
backend: "keyed"withprovider+modelconfig, that POSTs to the named provider's{baseUrl}/responseswithAuthorization: Bearer <apiKey>and the same hosted tool +BASE_INSTRUCTION, reusing the existing loop,parseSidecarSSE, andformat-resultunchanged:src/types.ts— extendOcxWebSearchSidecarConfig(backend: "keyed",provider?: string)src/web-search/executor.ts— addrunKeyedWebSearch()(~30 lines, mirrorsrunWebSearchwith Bearer auth)src/web-search/index.ts—planWebSearchkeyed branch, resolving a configured key-authopenai-responsesprovidersrc/web-search/loop.ts+src/server/responses/core.ts— thread the keyed provider through and dispatchRoughly 60–80 additive lines; the ChatGPT forward path and anthropic path stay untouched.
Acceptance criteria
webSearchSidecar: { backend: "keyed", provider: "opencode-go", model: "deepseek-v4-flash" }makes search runs on the Zen key with no ChatGPT/Claude quota involvement.openaibackend.Related
#398 (original fixed-backend bug), #414 (dedicated search vendors like Exa), #415 (provider-native search, e.g. Gemini Grounding — Zen is a sibling case), #1276 (superseded umbrella; maintainer's note that arbitrary LLMs can't auto-back search is exactly why Zen's hosted tool is special).