Repository navigation
feat(sidecar): unify web-search and vision sidecar selection (auth slot + picker/probe filters) #2188
Description
Why
웹서치 사이드카와 비전 사이드카가 백엔드를 openai | anthropic 두 칸으로만 나누고, "Codex 전체 / Claude 전체"를 후보로 펼친다. 그 전제는 이미 깨져 있다.
- 웹서치는 호스트된 검색 도구를 실제로 실행하는 프로바이더/프로토콜이 따로 있다. 아무 LLM이나 백엔드가 될 수 없다 (Feature: let vision/web-search sidecar use any configured provider's model (drop openai|anthropic-only backend) #1276 유지자 노트, feat(sidecar): OpenCode Zen (opencode.ai/zen/go/v1) as web-search sidecar backend — natively serves hosted web_search, no ChatGPT/Claude quota #1616 Zen 사례).
- 비전은 프로브가 필요 없다. 대신 Codex 피커에 나타나기로 한 모델만 보고, 그다음 text-only를 비전 로직에서 빼면 된다.
- Luna / Haiku는 모델 설정에서 꺼져 있어도, 해당 auth 로그인만 되어 있으면 사이드카 후보로 열어 둔다.
이 이슈가 #414 #415 #1616 #1937을 대체한다. 백엔드를 Exa/Zen/Doubao로 하나씩 더하는 방식이 아니라, 선택 규칙을 먼저 고친다.
Auth (전역)
구현은 전역 두 플래그로 시작한다. 이름은 구현에서 isCodexAuth / isAnthropicAuth로 고정한다.
현재 코드에 이미 조각이 있다. 한곳으로 모은다.
- Codex/ChatGPT:
listOpenAiForwardSidecarCandidates(src/providers/openai-sidecar.ts) + forwardauthMode+ 로그인된 ChatGPT 계정.planWebSearchopenai 분기는openAiSidecar가 없으면 계획이 없다 (src/web-search/index.ts:192-193). - Anthropic:
findAnthropicSidecarProvider/findAnthropicVisionProvider—adapter === "anthropic"+authMode === "oauth"+ active accountneedsReauth !== true(src/web-search/index.ts:87-96,src/vision/index.ts:220-226).
isCodexAuth === true이면 gpt-5.6-luna는 모델 설정/피커에서 꺼져 있어도 웹서치·비전 후보에 남긴다. isAnthropicAuth === true이면 claude-haiku-4-5(현 baseline)도 같다.
지금 비전 쪽 baseline은 src/vision/eligibility.ts:37-40의 BASELINE_VISION_MODELS다 (openai: gpt-5.6-luna, anthropic: claude-haiku-4-5). 웹서치 기본 모델은 src/web-search/index.ts:14-16 (gpt-5.6-luna / claude-sonnet-5). 웹서치 Anthropic 기본값을 Haiku로 맞출지, Sonnet을 유지할지는 구현 PR에서 정하되, auth만으로 여는 고정 슬롯은 Luna와 Haiku다.
웹서치 사이드카
선행: 프로바이더 × 프로토콜 조사
아무 채팅 모델이나 webSearchSidecar.backend에 넣지 않는다. 서버가 검색을 실행하는 도구/엔드포인트가 있는 경우만 후보가 된다.
2026-08-20 조사로 확인한 후보군 (라이브 프로브 전, 문서 기준):
| 후보 | 프로토콜 | 도구/엔드포인트 | 비고 |
|---|---|---|---|
| ChatGPT / Codex (현 openai 백엔드) | Responses | { type: "web_search" } |
이미 src/web-search/executor.ts가 {baseUrl}/responses로 보낸다. Codex CLI는 cached/live web_search. |
| Claude (현 anthropic 백엔드) | Messages | web_search_20250305 / 최신 web_search_20260209 |
src/web-search/anthropic-executor.ts. Bedrock에는 없음. |
| Gemini | Gemini / Interactions | google_search (구 google_search_retrieval) |
Grounding with Google Search. 인용/groundingMetadata. API 키. 검색+다른 툴 동시 제한이 문서에 있음. |
| xAI Grok | Responses | { type: "web_search" } |
docs.x.ai/developers/tools/web-search. 옛 Live Search는 2026-01 deprecate. allowed_domains / excluded_domains. |
| OpenCode Zen / opencode-go | Responses | hosted web_search |
#1616이 2026-08-13에 POST …/zen/go/v1/responses + web_search_call 확인. ChatGPT 쿼터 안 씀. |
| Exa 등 전용 검색 벤더 | 자체 Search API | 검색 JSON → SidecarOutcome 매핑 |
#414. LLM이 아님. 프로브 대상은 "호스트된 검색 도구를 가진 LLM"과 별선. |
이 표는 후보일 뿐 구현 허가가 아니다. 라이브 프로브가 성공하고, 실행 로직이 들어온 뒤에만 active다.
거름망 (순서 고정)
- Codex 피커에 나타나기로 한 모델.
피커 집합은visibleNativeSlugs/desktopVisibleNativeSlugs(src/codex/catalog/metadata.ts:331,:371)와 관리 UI의listManagementModelRows(src/server/management/model-rows.ts:50)다.disabledModels에 가려진 줄은 여기서 탈락한다. 예외는 위 Luna/Haiku auth 슬롯뿐이다. - 라이브 프로브가 통과하고, 이 레포에 실행 로직이 들어온 active 모델만.
문서에 도구가 있다고 피커에 올리지 않는다. Geminigoogle_search, Grokweb_search, Zen hostedweb_search는 각각 프로브 + executor가 생긴 뒤에만 2번 망을 통과한다.
GUI에서 설정 가능한 웹서치 사이드카 모델 = 1 ∩ 2 (+ auth 시 Luna/Haiku). CLI 커맨드도 같은 집합을 쓴다.
개편이 필요한 현재 로직
OcxWebSearchSidecarConfig.backend가"openai" | "anthropic"만 받는다 (src/types/config.ts:787-796).resolveSidecarBackend는 explicit anthropic이 아니면 무조건 openai (src/web-search/index.ts:104-108). types 주석은 "unset이면 anthropic 우선"인데 코드와 어긋난다.- 실행기도 두 개다.
runWebSearch(ChatGPT forward/responses)와runAnthropicWebSearch. keyed/Gemini/Grok 경로가 없다. - 라우티드 프로바이더의 hosted
web_search는 파서에서 걷어지고 사이드카로 치환된다. 그 "Codex 전체 / Claude 전체 중 하나를 빌린다"는 모델이 개편 대상이다. - 관리 API는
PUT으로webSearch.model문자열을 거의 그대로 넣는다 (src/server/management/config-routes.ts:604-606). 비전처럼 거절 게이트가 없다.
비전 사이드카
프로브 없음. 거름망만.
- Codex 피커에 나타나기로 한 모델 — 웹서치 1번과 같은 집합 (
listManagementModelRows/visibleNativeSlugs). auth 시 Luna/Haiku는 여기 없어도 남긴다. - text-only를 비전 로직에서 제외.
이미 조각이 있다.isModelTextOnly(src/vision/index.ts:29-36)는noVisionModels이거나modelInputModalities에image가 없으면 true.modelAcceptsImageInput/isVisionEligibleModel(src/vision/eligibility.ts:112-147)은 "text-only로 증명되면 탈락, unknown은 통과"다.
GUI에 보여줄 값은 1번을 통과한 뒤 2번으로 한 번 더 거른 집합이다. "unknown이라서 통과"를 피커에 펼치지 말고, 피커에 있는 줄만 2번에 넣는다.
개편이 필요한 현재 로직
visionEligibleModelOptions(src/vision/eligibility.ts:201-229)는 enabled 백엔드의 전체 catalog candidate + baseline을 펼친다. 사실상 Codex 전체 / Claude 전체다. 피커 가시성과 분리되어 있지 않다.visionCandidateRows(src/server/management/vision-sidecar-options.ts:45-56)는listManagementModelRows에서disabled !== true만 본다. 피커 정책과 거의 같지만, auth-only Luna/Haiku와 "피커에 나타나기로 한 모델"을 한 함수로 고정하지 않았다.- 디스크라이버 실행은 여전히
vision/describe.ts가 ChatGPTforwardProvider.baseUrl/responses로만 가거나 Anthropic OAuth Messages로만 간다. #1937의 Doubao/volcengine 디스크라이버는 이 이슈의 2번 망을 통과한 뒤에만 후속이다. 이번 이슈는 선택 규칙을 먼저 고친다. resolveVisionBackend는 unset이면 Anthropic credential이 있을 때 anthropic (src/vision/index.ts:229-235). 웹서치resolveSidecarBackend와 반대다. 전역isCodexAuth/isAnthropicAuth로 맞춘다.
GUI / CLI
- GUI: 지금 대시보드 사이드카 칸 (
gui+PUT /api/sidecar-settings,config-routes.tswebSearch/vision). 옵션 리스트를 위 거름망 결과만 보여 준다. - CLI: 같은 집합을 쓰는 설정 커맨드를 만든다 (예:
ocx sidecar web-search …,ocx sidecar vision …). GUI만 열려 있고 CLI가 우회하면 거름망이 아니다. - 쓰기 게이트와 제안 리스트를 같은 함수에서 나온다. 비전의
visionDescriberIsProvablyBlind(vision-sidecar-options.ts:94)처럼, 웹서치도 리스트에 없는 모델을 persist하지 않는다. 예외는 auth 슬롯 Luna/Haiku.
하지 않는 것
- Exa/Zen/Gemini/Grok executor를 이 이슈 하나에서 다 구현하지 않는다. 조사 표 + 프로브 계약 + 거름망 + auth 슬롯 + GUI/CLI가 범위다.
- types.ts/config.ts 스플릿과 무관한 잔여 PR을 리베이스하지 않는다.
- [Bug] Web Search/Vision Sidecar Backend Fixed to OpenAI·Anthropic - Routing Turns Fail with 499/502 When Limits are Exhausted #398 graceful degradation 재구현 없음.
Acceptance
isCodexAuth/isAnthropicAuth가 한곳에서 계산되고 웹서치·비전이 공유한다.- Codex 로그인만 되어 있으면 Luna가 피커/설정에서 꺼져 있어도 양쪽 사이드카 후보에 있다. Anthropic 로그인이면 Haiku도 같다.
- 웹서치 GUI/CLI 후보는 (피커 가시 ∪ auth 슬롯) ∩ (프로브 성공 + executor 존재)다.
- 비전 GUI/CLI 후보는 (피커 가시 ∪ auth 슬롯) − text-only다. 프로브 없음.
backend: "openai" | "anthropic"만으로 Codex 전체/Claude 전체를 펼치는 경로가 관리 API와 CLI에서 사라진다.- feat(sidecar): add Exa and other search providers as web-search sidecar backends #414 feat(sidecar): investigate search-API-capable providers (e.g. Gemini) as web-search sidecar backends #415 feat(sidecar): OpenCode Zen (opencode.ai/zen/go/v1) as web-search sidecar backend — natively serves hosted web_search, no ChatGPT/Claude quota #1616 #1937은 이 이슈로 닫힌다. 후속 구현 PR은 이 이슈를 가리킨다.
Activity
coderabbitai commentedon Aug 20, 2026
🔗 Related PRs
#2162 - fix(anthropic): frame the opening turn so AgentRouter stops blocking non-English [merged]
#2187 - [WRONG BRANCH] release: sync dev into preview for 2.28.0 [closed]
#2186 - [WRONG BRANCH] release: promote dev to main for 2.28.0 [closed]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
lidge-jun commentedon Aug 20, 2026
리뷰 · 우선순위 66 / 80
사이드카를 이슈 네 개로 쪼개놓은 거 접고 한 규칙으로 가자는 거임. #414 #415 #1616 #1937 닫고 여기로 모은 방향 맞음. 구현 스펙이지 지금
dev핫픽스는 아님. 2.28 블로커도 아님.인증 슬롯이 핵심임. 코드엔
isCodexAuthContextUsable(src/codex/auth-context.ts:613)이 있고 Anthropic은findAnthropicSidecarProvider로 저장 OAuth를 봄. 로그인만 되어 있으면 Luna/Haiku를 모델 설정에서 숨겨도 후보로 남겨야 함. 지금 피커는visibleNativeSlugs/desktopVisibleNativeSlugs(src/codex/catalog/metadata.ts:331,371)라서 hidden이면 목록에서 사라짐.웹서치는 그 피커 ∩ (live-probe + executor)임. 지금
OcxWebSearchSidecarConfig.backend가"openai" | "anthropic"둘뿐임 (src/types/config.ts:787).resolveSidecarBackend도 그 둘만 돌려줌 (src/web-search/index.ts:104). ChatGPT Responsesweb_search, Claudeweb_search_*, Geminigoogle_search, Grokweb_search, Zen, Exa를 넣으려면 이 유니온부터 뜯어야 함.types.ts가 config를 re-export함 (src/types.ts:81). 스플릿 캠페인 겹치면 리베이스 말고 이 이슈 기준으로 새로 짜는 게 맞음.비전은 프로브 없음. text-only만 빼면 됨 (
isModelTextOnly,src/vision/eligibility.ts). 근데visionEligibleModelOptions(:201)는 카탈로그 전체+베이스라인을 돌림. 피커에 없는 모델이 비전 목록에 남는 구멍이 그거임. GUI+CLI가listManagementModelRows(src/server/management/model-rows.ts:50)랑 같은 필터를 써야 함.#2190이랑 섞지 말 것. 저건 Grok
x_search호스트 툴 서페이스고, 이건 웹서치/비전 사이드카 선택 규칙임. Grokweb_search를 백엔드 후보에 넣는 거랑 X Search 노출은 다른 선임.해결방안: 피커 집합을 한 함수로 뽑음. 웹서치 = 그 집합 ∩ 프로브된 executor, 비전 = 그 집합 − text-only. auth 슬롯이면 hidden Luna/Haiku는 후보에 남김.
backend문자열을 프로바이더별 슬롯으로 바꾸고resolveSidecarBackend/planWebSearch를 그 슬롯에 맞춤. 기존 사이드카 이슈는 다시 열지 말 것.이 댓글은 grok-bot이 작성했습니다
feat(sidecar): shared auth module with login-shaped flags and auth sl…
feat(sidecar): unified picker candidate set consumed by the vision op…
feat(web-search): backend registry and candidate intersection (#2188)
feat(management): web-search sidecar write gate and filtered option l…
feat(sidecar): unified picker candidate set for both sidecars (#2188 …
feat(web-search): backend registry and candidate intersection (#2188)
feat(web-search): backend registry and executor-runnable candidate set (
feat(management): web-search sidecar write gate and filtered option l…
feat(management): web-search sidecar write gates and filtered gui lis…
feat(cli): ocx agent sidecar --list prints the server's candidate sets (
feat(cli): ocx agent sidecar --list shares the server candidate sets (#…
9 remaining items
Merge pull request #2306 from lidge-jun/codex/vision-routed-sidecar
feat(vision): routed describer backend — options, gates, namespaced i…
feat(vision): routed describe executor via loopback self-fetch (lidge…
feat(sidecar): shared auth module with login-shaped flags and auth sl…
feat(sidecar): unified picker candidate set for both sidecars (lidge-…
feat(web-search): backend registry and executor-runnable candidate set (
feat(management): web-search sidecar write gates and filtered gui lis…
feat(cli): ocx agent sidecar --list shares the server candidate sets (l…
feat(web-search): inert backend union + routed web_search field fix (l…
feat(web-search): live xai executor with opt-in x_search (lidge-jun#2188
feat(web-search): live gemini executor on the Antigravity CCA transpo…
feat(web-search): exa executor and the non-LLM search lane (lidge-jun…
feat(vision): routed describer backend — options, gates, namespaced i…
feat(vision): routed describe executor via loopback self-fetch (lidge…
Merge pull request lidge-jun#2306 from lidge-jun/codex/vision-routed-…