Skip to content

feat(web-search): live xai executor with opt-in x_search (#2188 L7) - #2242

Merged
lidge-jun merged 6 commits into
devfrom
codex/sidecar-xai-executor
Aug 21, 2026
Merged

feat(web-search): live xai executor with opt-in x_search (#2188 L7)#2242
lidge-jun merged 6 commits into
devfrom
codex/sidecar-xai-executor

Conversation

@lidge-jun

Copy link
Copy Markdown
Owner

Summary

Layer 7 of the #2188 follow-up chain (parent: #2238). The xAI web-search executor goes live:

  • src/web-search/xai-executor.ts: runXaiWebSearch POSTs the EXACT-origin-pinned api.x.ai Responses endpoint (parsed url.origin comparison — a prefix check admitted lookalike hosts, review-caught Critical) with the stored Grok OAuth credential, hosted web_search + opt-in x_search (doc-validated: ≤20 handles, allow XOR exclude, ISO dates), include: web_search_call.action.sources, and reasoning.effort (probe-verified). redirect: "manual", never-throws, 401/403 entitlement-distinct errors, byte-bounded SSE reducer.
  • Sources = url_citation annotations ∪ web_search_call.action.sources, deduped; custom_tool_call items (live x_search reality) and skeleton action tolerated per the devlog 003 captures.
  • planWebSearch's xai arm goes live fail-closed (no credential → no plan; invalid persisted xSearch → no plan); SidecarPlan.xaiSidecar → core.ts handoff → loop arm that FAILS CLOSED when the sidecar is absent (review-caught High: the fallthrough would have reached the forward-header executor). No Codex pool outcome recording on this arm.
  • xSearch config block: PUT-validated (400 on doc-limit violations), GET/PUT round-tripped (review-caught High), null clears.
  • Registry activates the backend on stored-OAuth presence; grok catalog rows become web-search candidates.

Live E2E through the real executor: web_search → 130 chars + 12 sources; x_search with allowed_x_handles: ["xai"] → 492 chars.

Design doc: devlog/_plan/260820_sidecar_selection_unification/070_layer7_xai_executor.md (rev 2) + 003 research.

Verification

  • Remote full gate at exact head (lidge): 13792 pass / 15 skip / 0 fail across 873 files (491s); typecheck clean.
  • 12 focused tests incl. lookalike-origin bearer containment, loop-level fail-closed regression (zero fetches, zero pool recordings, invariant error replayed), SSE fixtures from live captures, xSearch round-trip/400.
  • Independent read-only review (terra): 1 Critical + 2 High found and fixed, follow-up demanded loop regression added — final VERDICT: pass.

Checklist

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner August 21, 2026 01:51
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@lidge-jun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

Limit details: You’ve used all 10 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5e830d7b-f2be-4e23-941f-6d112e1e8278

📥 Commits

Reviewing files that changed from the base of the PR and between d367927 and a03f51a.

📒 Files selected for processing (29)
  • docs-site/src/content/docs/fr/guides/sidecars.md
  • docs-site/src/content/docs/fr/reference/configuration/server.md
  • docs-site/src/content/docs/guides/sidecars.md
  • docs-site/src/content/docs/ja/guides/sidecars.md
  • docs-site/src/content/docs/ja/reference/configuration/server.md
  • docs-site/src/content/docs/ko/guides/sidecars.md
  • docs-site/src/content/docs/ko/reference/configuration/server.md
  • docs-site/src/content/docs/reference/configuration/server.md
  • docs-site/src/content/docs/ru/guides/sidecars.md
  • docs-site/src/content/docs/ru/reference/configuration/server.md
  • docs-site/src/content/docs/tr/guides/sidecars.md
  • docs-site/src/content/docs/tr/reference/configuration/server.md
  • docs-site/src/content/docs/zh-cn/guides/sidecars.md
  • docs-site/src/content/docs/zh-cn/reference/configuration/server.md
  • docs-site/src/content/docs/zh-tw/guides/sidecars.md
  • docs-site/src/content/docs/zh-tw/reference/configuration/server.md
  • src/server/management/config-routes.ts
  • src/server/management/web-search-sidecar-options.ts
  • src/server/responses/core.ts
  • src/types/config.ts
  • src/web-search/backends.ts
  • src/web-search/index.ts
  • src/web-search/loop.ts
  • src/web-search/xai-executor.ts
  • structure/04_transports-and-sidecars.md
  • structure/05_gui-and-management-api.md
  • tests/sidecar-settings-web-search-gate.test.ts
  • tests/web-search-backend-union.test.ts
  • tests/xai-web-search.test.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the enhancement New feature or request label Aug 21, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0f2d670c0e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/web-search/xai-executor.ts Outdated
Comment thread src/server/management/config-routes.ts
Comment thread src/server/management/config-routes.ts Outdated
Comment thread src/web-search/xai-executor.ts Outdated
Comment thread src/types/config.ts

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 0f2d670. The Codex findings are confirmed in the current code and are merge blockers:

  1. runXaiWebSearch does not attach the existing cancelBodyOnAbort guard after fetchWithResetRetry resolves, leaving the abort-before-reader race uncontained.
  2. parseXaiResponsesSSE breaks on the byte bound but only releases the reader lock; it must cancel the upstream body so repeated oversized streams cannot retain transport resources.
  3. the management PUT mutates config.webSearchSidecar fields before xSearch validation. An invalid xSearch therefore returns 400 after changing live process state.
  4. supplied malformed xSearch fields are silently omitted by lift/type checks. Invalid handle arrays, dates, or enabled values must be rejected rather than broadening the requested search while returning 200.
  5. the public docs and the type comment still describe xai as inert even though this PR makes the executor live and adds user-visible xSearch configuration.

Please stage and validate the complete webSearch candidate before committing it, add malformed-input/no-partial-mutation regressions, mirror the established abort/body-cancellation pattern, cover the oversized-stream cancellation path, and update the English source docs plus non-contradictory translations. This stacked PR is also downstream of #2238, which currently has unresolved requested changes, so it cannot advance independently.

lidge-jun added a commit that referenced this pull request Aug 21, 2026
Split the opt-in DTO into a write boolean vs read tri-state; record the concrete #2238 (3) and #2242 (5) review blockers in doc 120; recast doc 150 as the final aggregate gate with the full GUI/i18n/docs chain; replace temporal API-key rows with exact wire+tier assertions; state the explicit wp9->wp8->wp11->wp10 execution sequence.
@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 62 / 80

#2188 L7임. 베이스가 지금 dev가 아니라 codex/sidecar-backend-union (#2238 L6). L1부터 안 들어가면 리베이스 지옥임. 지금 dev HEAD 826a1b7a4 resolveSidecarBackend (src/web-search/index.ts:104-108)가 openai | anthropic만. OcxWebSearchSidecarConfig.backend (src/types/config.ts:796)도 그 둘. types.ts:81이 그 타입을 re-export함. planWebSearch에 xai 암 없음. src/web-search/backends.ts 파일 자체가 없음. 루프 분기는 아직 anthropic vs 포워드 이분법 (src/web-search/loop.ts:659-661). 이 PR이 L6 이너트 유니온 위에 runXaiWebSearch를 살림.

실행기 본체는 방향 맞음. 저장된 Grok OAuth만. findXaiSidecarProviderproviders["xai"] + account set. disabled / key-auth / needsReauth면 plan 없음. 루프가 backend === "xai"인데 xaiSidecar 없으면 포워드 실행기로 안 떨어짐. forward-secret 헤더가 나감. 리뷰 High 맞음. origin은 new URL(baseUrl).origin === "https://api.x.ai" exact. prefix면 api.x.ai.evil 탐. redirect: "manual". never-throws. 401/403 entitlement. SSE는 annotation ∪ web_search_call.action.sources 디듑. custom_tool_call / 스켈레톤 action 허용. 테스트가 lookalike origin + missing sidecar + 핸들 20/XOR/ISO를 잠금.

근데 x_search를 사이드카 실행기에 넣음. src/web-search/xai-executor.ts tools[{ type: "web_search" }, ...(options.xSearch ? [buildXSearchTool(options)] : [])]임. webSearchSidecar.xSearch 블록이 src/types/config.ts에 생기고 GET/PUT /api/sidecar-settings가 라운드트립함. 지금 dev GET (src/server/management/config-routes.ts:511-522)은 model/backend/streamRoutedModelOutput만. #2190 불변식이 호스트 툴은 기존 xAI OAuth 페이지만, #2188이랑 안 섞음. L6 리뷰가 이미 "L7에서 x_search를 사이드카 실행기에 넣지 말 것"이라고 씀. 코드가 그거 함. 충돌임. ㅋㅋ web_search 실행기는 살아도 됨. x_search 옵트인은 #2190으로 빼라.

src/types/config.ts를 직접 넓힘. 스플릿 캠페인이 이 파일을 삼키면 리베이스하지 말고 닫고 다시 짜라. 지금은 사이드카 스택이 그 파일을 이미 만지고 있어서 L1→L6 순서가 우선임. 닫을 중복 아님. #2217/#2227 modelWireDefaults.wire 안 건드림. 지금 HEAD 826a1b7a4 위에 바로 머지하지 말 것. 부모 #2238 먼저.

2.28 블로커 아님. 기본 Grok 구멍은 #2240 쪽 (#2217 첫 턴, #2237 둘째 턴). 이 PR은 사이드카 백엔드임. 패스스루 Responses가 아님. 레지스트리 modelWireDefaults (src/providers/registry.ts:1030-1042) 안 만짐.

해결방안: L1-L6 dev 머지 후 리타깃. x_search 블록/툴/GET 라운드트립은 이 PR에서 빼고 #2190에만. web_search 실행기 + fail-closed + origin pin은 유지. 스플릿이 config.ts를 삼키면 닫고 다시 짜라.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun

Copy link
Copy Markdown
Owner Author

All five blockers addressed in b2c2054: (1) runXaiWebSearch attaches cancelBodyOnAbort right after fetchWithResetRetry resolves, mirroring the OpenAI/Anthropic executors; (2) parseXaiResponsesSSE cancels the upstream body at the byte bound instead of only releasing the reader lock, with an oversized-stream regression; (3) the management PUT stages and validates the complete webSearch candidate before committing — invalid xSearch no longer mutates live state (no-partial-mutation regression); (4) malformed xSearch handle arrays/dates/enabled values are rejected with 400 rather than silently broadened (malformed-input regressions); (5) English docs + type comment no longer call xai inert, translations (fr/ja/ko/ru/tr/zh-cn/zh-tw) updated consistently. Fresh local: 47/0 across four suites + 12/0, tsc clean, docs build 393 pages. Ready for re-review.

@lidge-jun
lidge-jun requested a review from Ingwannu August 21, 2026 03:16

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed exact head b2c2054b59d84490cab801dfd8af75e3e9c491a0. The five prior code blockers are fixed: the xAI body receives the abort guard immediately after fetch, overflow cancels the reader, the full management update is staged before commit, malformed handle/date input is rejected instead of broadened, and the xAI/xSearch documentation is synchronized across locales.

Independent local verification on this head: 52 focused tests passed; bun run typecheck and bun run privacy:scan passed; the docs site built all 393 pages.

I am not approving this SHA yet because GitHub currently reports the stacked PR as CONFLICTING / DIRTY: its parent #2238 advanced after this head. Please rebase onto the current codex/sidecar-backend-union head, resolve the stack conflict, and rerun exact-head CI. The previous finding threads can be closed as fixed, but approval belongs on the rebased SHA.

@lidge-jun
lidge-jun force-pushed the codex/sidecar-backend-union branch from d9a1fbc to 7d015ba Compare August 21, 2026 03:32
runXaiWebSearch POSTs the pinned api.x.ai Responses endpoint with the
stored Grok OAuth credential, hosted web_search (+ opt-in x_search with
doc-validated handle/date limits), and reduces the SSE stream to
text + deduped sources from url_citation annotations unioned with
web_search_call action.sources. custom_tool_call items and skeleton
action are tolerated per live captures. planWebSearch's xai arm goes
live fail-closed; the loop dispatches it without touching forward
headers; the registry activates the backend on OAuth presence; PUT
validates the xSearch block before persisting.
…, round-trip xSearch

Origin pinning by prefix admitted https://api.x.ai.evil — parse and
compare url.origin, falling back to the canonical endpoint (Critical).
A missing xaiSidecar on the xai loop arm now yields an error outcome
instead of falling through to the forward-header OpenAI executor
(High). GET and the PUT response now carry the persisted xSearch block
so a dashboard reload cannot lose visible configuration (High). Tests:
lookalike-origin bearer containment, xSearch 400/round-trip/clear.
Attach abort-driven body cancellation immediately after the xAI fetch, cancel oversized SSE readers upstream, and cover transport release.

Stage and validate complete web-search management candidates before commit; reject malformed xSearch types without partial mutation or broadened searches.

Document the live Grok OAuth/x_search backend across canonical docs, locales, type comments, and structure sources. Evidence: 47 focused tests pass; bun x tsc --noEmit; docs Astro build 393 pages.
…er rebase

Cross-layer reconciliation: #2209 introduced the (backend, model) pair contract typed as openai|anthropic; #2238 widened the configured union to five backends. The options module now re-exports WebSearchBackendId and passes config into isActive, so inert arms simply never produce rows. tsc clean; 48/0 across the three web-search suites.
@lidge-jun
lidge-jun force-pushed the codex/sidecar-xai-executor branch from 3f574c3 to a03f51a Compare August 21, 2026 03:39
@lidge-jun
lidge-jun changed the base branch from codex/sidecar-backend-union to dev August 21, 2026 03:39
@lidge-jun
lidge-jun merged commit ef867c9 into dev Aug 21, 2026
2 of 3 checks passed
lidge-jun added a commit that referenced this pull request Aug 21, 2026
Split the opt-in DTO into a write boolean vs read tri-state; record the concrete #2238 (3) and #2242 (5) review blockers in doc 120; recast doc 150 as the final aggregate gate with the full GUI/i18n/docs chain; replace temporal API-key rows with exact wire+tier assertions; state the explicit wp9->wp8->wp11->wp10 execution sequence.
lidge-jun added a commit that referenced this pull request Aug 21, 2026
…#2245)

* feat(web-search): exa executor and the non-LLM search lane (#2188 L9)

runExaWebSearch POSTs api.exa.ai/search with the operator key and maps
ranked results to a digest the routed model synthesizes from. The key
never rides the SidecarPlan — core.ts reads it from config at unpack
time — and the executor scrubs the literal key from every error string
(pattern-based redaction cannot know an arbitrary operator key;
canary-tested). Plan, loop, and registry arms fail closed without the
key. docs-site gains the explicit-only backend table.

* fix(web-search): scrub the exa key before truncating error bodies

Reviewer blocker (L9 round 2): error(t.slice(0,200)) truncated before the literal-key scrub, so a key straddling the 200-char boundary left an unscrubbable prefix in the returned tool error. Scrub first, then slice. Adds truncation-boundary and fetch-rejection canaries; 9/9 focused tests, tsc and privacy:scan green.

* docs(devlog): integration merge-train roadmap 100-150 (chat default, global order, opt-in switch)

Amends the 260820 unit with the audited (3-round sol-medium, round-3 PASS) roadmap: 100 chat-default regression as an atomic #2227+tier-policy unit with a 5-row regression matrix and the E2E reasoning-streaming proof; 110 global cross-train merge order and 21-PR triage matrix (#2072 deferred, #2217 RESHAPE); 120 sidecar L1-L9 merge execution with the fresh blocker inventory; 130 atomic xai Responses opt-in switch (single provider id, auth-mode-scoped sections, virtual PATCH field); 140 release prep; 150 blocking lidge final gate. DeepSeek explicitly out of scope per user decision.

* docs(devlog): fold C-gate blockers into roadmap 100-150

Split the opt-in DTO into a write boolean vs read tri-state; record the concrete #2238 (3) and #2242 (5) review blockers in doc 120; recast doc 150 as the final aggregate gate with the full GUI/i18n/docs chain; replace temporal API-key rows with exact wire+tier assertions; state the explicit wp9->wp8->wp11->wp10 execution sequence.

* docs(devlog): doc 100 API-key opt-in row preserves current tier forwarding

C-gate round 2: current dev forwards caller service_tier verbatim on the API-key + explicit openai-responses route (fastPolicyForModel proof). The tier drop is an OAuth-route policy only; the API-key row now states preserve-current semantics, consistent with doc 130.

* docs(devlog): wp9 execution record — all six chain blockers resolved and pushed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants