Skip to content

fix(responses): defer the serving-identity commit + passthrough findings (#2264 rebased) - #2273

Merged
lidge-jun merged 3 commits into
devfrom
codex/land-2264
Aug 21, 2026
Merged

fix(responses): defer the serving-identity commit + passthrough findings (#2264 rebased)#2273
lidge-jun merged 3 commits into
devfrom
codex/land-2264

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Aug 21, 2026

Copy link
Copy Markdown
Owner

Summary

Lands PR #2264 by @olddonkey (the four Ingwannu review findings on the landed #2258 series), rebased cleanly onto current dev with all three commits preserved.

Verification

  • Focused: 132/0 across six replay/recovery suites + 106/0 across seven implicated suites; tsc + privacy:scan green.
  • Full-suite authority: release host (lidge) aggregate gate before the release prep.

Checklist

Summary by CodeRabbit

  • Bug Fixes
    • Improved detection of official OpenAI Responses endpoints, reducing incorrect handling of lookalike hosts and unsupported paths.
    • Improved custom-tool passthrough for compatible OpenAI forwarding routes.
    • Reasoning replay state now updates only after successful responses, improving recovery across provider changes and failed requests.
    • Refreshed tool aliases on rebuilt requests to prevent stale tool references.
  • Improvements
    • Web search processing now reliably reports completed or incomplete final responses.
  • Tests
    • Added coverage for endpoint validation, passthrough behavior, replay recovery, and tool alias rebuilding.

olddonkey and others added 3 commits August 21, 2026 15:31
Three defects found in review of the Grok Responses series, plus one stale
comment. All confirmed against the code before fixing.

**One gate used the wrong predicate.** Custom-tool lowering was gated on
`provider.authMode !== "forward"` while every neighbouring gate uses
`!isCanonicalOpenAiForwardProvider`. A noncanonical forward provider therefore
skipped `rewriteRoutedCustomToolsForUpstream` but still ran namespace
lowering, so a namespace child that was a custom tool got promoted while
keeping `type: "custom"` and the gateway rejected it.

This repeats the mistake the same series documented elsewhere: forward auth
says nothing about which backend answers, because a noncanonical forward
provider never receives the caller's credentials. Both sides move together —
the adapter's lowering gate and core's converted-name collection — since
lowering names without restoring them is worse than not lowering at all.

**The OpenAI-operated classifier missed a legitimate base-URL form.** It
compared the normalized base URL for exact equality with
`https://api.openai.com/v1`, so a provider configured as
`baseUrl: "https://api.openai.com"` with `responsesPath: "/v1/responses"`
reaches the official endpoint yet was classified as routed. That is not
cosmetic: routed classification drops `content: null` from OpenAI-minted
encrypted reasoning and degrades native compaction blobs — this series'
own regression, in reverse. Both official forms are now accepted, still by
exact normalized match so a lookalike host cannot qualify.

**Request rebuilds left the namespace alias map stale.** Every recovery
rebuild replaces `request` without refreshing the alias map the response path
uses to restore private tool names, so a rebuild that changes the lowering
decision restores against a stale map. Refreshed from the rebuilt request on
every path that replaces it — the pre-existing OAuth-401 and image-413
rebuilds included, since the bug is in the rebuild pattern rather than in one
caller.

**`_stripReasoningEncryptedContent` is no longer only a route-switch flag.**
It is also set when an upstream rejects opaque state of unknown provenance.
The comment now names both producers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`updateReasoningReplayServingIdentity` compared and committed in one call, and
`bindRouteReasoningReplayScope` calls it before the request goes out — so the
candidate destination was recorded whether or not that request ever completed.

  turn 1  -> A succeeds                    record = A
  turn 2  -> B: A != B, strip A blobs      record = B   (committed too early)
             ... this request then fails (rate limit, transport, 5xx)
  turn 3  -> retry B: B == B, no strip
             but the transcript still carries A-minted blobs -> rejected

The opaque-blob recovery rescues turn 3, so this degraded rather than broke:
one wasted round trip and one turn of degraded reasoning on a path meant to be
deterministic. The record's meaning was the defect — it should mean "this
destination served this thread", and a request that never completed served
nothing.

Split the call in two. `reasoningReplayServingIdentityChanged` compares without
writing; `commitReasoningReplayServingIdentity` records, and runs only at a
successful terminal response. Bounded discipline is unchanged: same LRU/TTL and
byte accounting, same refusal to record without a durable identity dimension,
same fail-soft direction where no record still means keep the blobs.

For bridged transports a terminal means `completed` or `incomplete`. For
streamed passthrough it means a non-error upstream status before relay starts:
waiting for SSE completion would retain request state for the stream's
lifetime, and a later body failure does not undo that the destination accepted
and served the turn. That boundary is stated in the code rather than implied.

The two post-recovery re-records are gone — a successful recovery now reaches
the same terminal commit as any other success.

Regression test: A succeeds, an A->B turn strips and then fails, and the next B
request for the same thread still strips. Verified it fails against the old
code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e URL

isOpenAiOperatedResponsesDestination() matched on the base URL alone, so a
provider with baseUrl "https://api.openai.com" and a custom responsesPath such
as "/other" was classified as OpenAI-operated even though the adapter posts that
request to a non-Responses endpoint. That preserved OpenAI-only null-content and
reasoning semantics for a destination that never sees the official Responses API.

Resolve the effective endpoint with the adapter's own construction rules — a
configured responsesPath is appended verbatim, only the default branch runs the
/v1/responses suffix normalization — and require an exact normalized match on
https://api.openai.com/v1/responses. The conventional /v1 base and the bare
official origin still classify; lookalike hosts still do not.

Adds negative regressions for a custom non-Responses path on both official base
forms, plus positive coverage for the bare origin default and an explicit
/responses path.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zpLCh4eEms6un3VjapRgL
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner August 21, 2026 06:33
@lidge-jun lidge-jun added the bug Something isn't working label Aug 21, 2026
@lidge-jun
lidge-jun merged commit e86a6fb into dev Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cb6b13aa-ec73-4488-b91b-31eaec63db6d

📥 Commits

Reviewing files that changed from the base of the PR and between 1f2615e and 1999602.

📒 Files selected for processing (11)
  • src/adapters/openai-responses.ts
  • src/providers/openai-tiers.ts
  • src/responses/reasoning-replay-cache.ts
  • src/server/responses/core.ts
  • src/types/request.ts
  • src/web-search/loop.ts
  • structure/04_transports-and-sidecars.md
  • tests/openai-provider-option.test.ts
  • tests/openai-responses-passthrough.test.ts
  • tests/reasoning-replay-identity.test.ts
  • tests/responses-opaque-blob-recovery.test.ts

📝 Walkthrough

Walkthrough

The PR tightens OpenAI Responses destination detection, enables custom-tool rewriting for noncanonical forward routes, separates replay identity comparison from commit operations, and commits identities only after successful serving across passthrough, bridge, sidecar, run-turn, and adapter paths.

Changes

Responses routing and replay serving

Layer / File(s) Summary
Exact OpenAI destination and custom-tool routing
src/providers/openai-tiers.ts, src/adapters/openai-responses.ts, tests/openai-provider-option.test.ts, tests/openai-responses-passthrough.test.ts
Official OpenAI detection now matches the resolved Responses endpoint exactly. Noncanonical forward routes rewrite and restore namespaced custom tools.
Replay serving identity contract
src/responses/reasoning-replay-cache.ts, tests/reasoning-replay-identity.test.ts, tests/responses-opaque-blob-recovery.test.ts
Serving identity change detection no longer mutates state. Explicit commits record durable identity only after successful serving.
Serving-path integration and recovery
src/server/responses/core.ts, src/web-search/loop.ts, src/types/request.ts, structure/04_transports-and-sidecars.md, tests/responses-opaque-blob-recovery.test.ts
Responses paths refresh aliases after request rebuilds and commit identities at validated terminal or successful passthrough boundaries. Web-search bridging receives an optional completion callback. Documentation describes the updated replay behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: review-ready

Suggested reviewers: ingwannu, luvs01

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/land-2264

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.

@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: 1999602f02

ℹ️ 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".

return formatErrorResponse(502, "upstream_error", undeclaredToolCallMessage(undeclared));
}
}
commitReasoningReplayServingRoute();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Commit the route after JSON-to-SSE conversion

When a streaming client uses a provider configured for bounded JSON, this commits the new serving identity before responsesJsonToSseStream runs. If the upstream response exceeds the synthesized SSE item limit, the conversion returns a local 502 at lines 3823–3830, so the client retains its prior route's transcript even though the cache now records the new route; retrying the new route then keeps the prior route's opaque blobs instead of stripping them. Move the commit past all conversion failure paths, immediately before returning the successfully constructed response.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants