Skip to content

fix(hosted-tools): one effective-transport decision, one hosted declaration - #925

Merged
lidge-jun merged 2 commits into
devfrom
codex/hosted-tools-followup
Aug 3, 2026
Merged

fix(hosted-tools): one effective-transport decision, one hosted declaration#925
lidge-jun merged 2 commits into
devfrom
codex/hosted-tools-followup

Conversation

@lidge-jun

Copy link
Copy Markdown
Owner

Follow-up to #924. A branch review after that PR merged found two defects in the hosted-tool preference path; both are reproduced here and fixed with red-green coverage.

Forward-auth validation diverged from routing

modelPreferHostedToolsConfigError() made two transport decisions and only one of them was correct.

The wire check asked providerMatchesRegistryTransport() whether the config still points at the registry's documented endpoint — right, because a preserveCustomDestination row reused under a different endpoint keeps its own adapter at runtime. The forward-auth check sitting above it read registry.authKind unconditionally.

That matters because auth follows the same rule as the adapter: routedProviderConfig() preserves the configured authMode for a custom destination too. So a config for such a provider with authMode: "forward" and modelPreferHostedTools validated clean, while preferConfiguredHostedTools() — which runs only on the non-forward branch of openai-responses.ts — never applied the preference. The user gets a config the runtime silently ignores.

Both checks now start from one registryTransportMatches decision, computed once instead of twice.

Multi-container restoration emitted the hosted tool twice

#924 made stripping walk every additional_tools container, and made restoration walk them too. But tool declarations are request-scoped, not container-scoped — the containers are separate carriers for a single tool set. Restoring into each stripped container put image_generation on the wire once per container.

Restoring into the first stripped container only keeps the capability that #924 was fixing, without the duplication.

Evidence

  • Ablation: reverting both source files to dev while keeping the new tests fails exactly two tests, one per defect. Restoring them: 174 pass / 0 fail across the two files.
  • 248 pass / 0 fail across config, openai-responses-passthrough, management-provider-validation, openai-api-virtual-models.
  • bun x tsc --noEmit exit 0.

Attribution

The feature these fix is @Eleven-is-cool's implementation from #616, integrated by @Ingwannu in #837 and merged as #924. This PR only repairs the two defects, and the second one is a defect in the fix I asked for during that review — not in the original contribution.

…er validation

Two corrections to what #924 landed.

**The multi-container restoration was wrong.** #924 took CodeRabbit's finding
that restoration reached only the first stripped `additional_tools` container
and fixed it by restoring into every stripped container. That overcorrects:
`hasHostedImageGenDeclaration` a few lines above is satisfied by a declaration
in ANY container, so the code already treats tool declarations as
request-scoped. Restoring into each container puts `image_generation` on the
wire twice. Restore into the first stripped container only — the capability is
neither lost nor duplicated, and the test now asserts exactly one declaration
rather than "at least one per container".

**The forward-auth check disagreed with the wire check.** #924 taught the wire
check that a `preserveCustomDestination` row reused under a different endpoint
keeps its own adapter. The forward-auth check three lines earlier still read
`registry.authKind`. So a config naming such a row with `authMode: "forward"`
and a custom endpoint passed the auth check on the registry's value, while at
runtime `preferConfiguredHostedTools()` never runs — it is on the non-forward
branch. Both checks now share one `registryTransportMatches` decision.

Driven red: the forward-auth config loaded clean before the fix. 245 tests
pass across the four affected files, typecheck clean.
…ration

Follow-up to #924. Two defects a branch review found after that PR merged,
both reproduced before fixing.

Forward-auth validation diverged from routing. The wire check already asked
`providerMatchesRegistryTransport()` whether the config still points at the
registry's documented endpoint, but the forward-auth check above it read
`registry.authKind` unconditionally. A `preserveCustomDestination` row reused
under a custom endpoint keeps its own auth at runtime, so a forward-auth config
carrying `modelPreferHostedTools` validated clean while
`preferConfiguredHostedTools()` — which runs only on the non-forward branch —
never applied it. Both checks now start from the same decision, computed once
instead of twice.

Multi-container restoration emitted the hosted tool twice. #924 made stripping
walk every `additional_tools` container and made restoration walk them too. Tool
declarations are request-scoped: the containers are separate carriers for one
tool set, so restoring into each put `image_generation` on the wire once per
stripped container. Restore into the first stripped container only, which keeps
the capability without duplicating it.

Both driven red against dev: reverting the two source files fails exactly two
tests, one per defect. 248 pass across the four affected files, typecheck clean.
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

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

Next review available in: 28 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 83228c5e-5c37-4902-94d7-74e902338a0f

📥 Commits

Reviewing files that changed from the base of the PR and between df82431 and cf5910a.

📒 Files selected for processing (4)
  • src/adapters/openai-responses.ts
  • src/config.ts
  • tests/config.test.ts
  • tests/openai-responses-passthrough.test.ts

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

@github-actions github-actions Bot added the bug Something isn't working label Aug 3, 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: cf5910ab10

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

// one tool set, so a single hosted declaration covers the request. An earlier
// revision restored into every stripped container and put `image_generation` on
// the wire twice; review caught it.
const firstStripped = Math.min(...strippedAdditionalToolsIndices);

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 Avoid spreading the unbounded stripped-index set

When a request contains sufficiently many matching additional_tools containers, Math.min(...strippedAdditionalToolsIndices) passes every index as a function argument and eventually throws RangeError: Maximum call stack size exceeded before the request is dispatched. This is reachable within the Responses endpoint's 256 MiB admitted-body limit; track the first stripped index during the existing scan or obtain it through iteration instead of spreading an unbounded set.

Useful? React with 👍 / 👎.

@lidge-jun
lidge-jun merged commit 3e56207 into dev Aug 3, 2026
17 checks passed
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.

1 participant