mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-15 11:43:10 +03:00
fix(providers): drop image_generation for Codex Spark models regardless of plan (#6651)
This commit is contained in:
@@ -23,6 +23,7 @@ _Living section — bullets land here as PRs merge into `release/v3.8.47` (paral
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **fix(providers):** Codex Desktop requests to `gpt-5.3-codex-spark` failed with `[400]: Tool 'image_generation' is not supported with gpt-5.3-codex-spark`, even on paid-plan accounts ([#6651](https://github.com/diegosouzapw/OmniRoute/issues/6651)) — `CodexExecutor.transformRequest` (`open-sse/executors/codex.ts`) only dropped the Codex Desktop-injected `image_generation` hosted tool when `isCodexFreePlan()` matched the account's plan, with no awareness that Spark-scope Codex models reject `image_generation` upstream regardless of plan. `dropImageGeneration` now also drops it when `getCodexModelScope(model) === "spark"` (the existing Spark classifier from `open-sse/config/codexQuotaScopes.ts`), independent of account plan. Regression guard: `tests/unit/codex-spark-image-generation.test.ts`.
|
||||
- **fix(cli):** per-agent AgentBridge DNS toggle was broken for 8 of the 9 supported agents, and a failed MITM startup step could orphan the spawned proxy child — `addDNSEntry`/`removeDNSEntry` (`src/mitm/dns/dnsConfig.ts`) always resolved the legacy Antigravity default hosts regardless of which agent's toggle was flipped, so enabling DNS for Cursor/Codex/Claude Code/etc. silently added only `daily-cloudcode-pa.googleapis.com` while the DB recorded `dns_enabled=true` for the selected agent. Both functions now accept an optional `agentId` and resolve hosts via `ALL_TARGETS`; `POST /api/tools/agent-bridge/agents/[id]/dns` passes the route's `id` through and now returns 404 for an id that doesn't match a known target instead of silently falling back. Separately, `startMitmInternal()` (`src/mitm/manager.ts`) now wraps `generateCert()` (log + rethrow), the `provisionDnsEntries()` call, and the PID-file write in try/catch so a mid-startup failure can't orphan the already-spawned MITM child process. On Windows, `addDNSEntries`/`removeDNSEntries` also batch every missing/present entry into a single elevated PowerShell invocation instead of one UAC prompt per host line. Regression guard: `tests/unit/dns-config-generic.test.ts` (agent-specific resolution + batching), `tests/unit/agent-bridge-dns-route-validation.test.ts` (404 for unknown agent id). ([#6338](https://github.com/diegosouzapw/OmniRoute/pull/6338) — thanks @hamsa0x7)
|
||||
- **fix(guardrails):** Vision Bridge's individual-model auto-reroute (route an image-bearing request straight to a vision-capable model instead of describe-then-forward) could bypass a policy-restricted API key's model allowlist/budget ([#6640](https://github.com/diegosouzapw/OmniRoute/pull/6640)) — `VisionBridgeGuardrail.preCall()` (`src/lib/guardrails/visionBridge.ts`) swaps `body.model` to the best available vision-capable model, but that swap happens in the guardrail pipeline AFTER `chat.ts` already called `enforceApiKeyPolicy()` against the ORIGINAL model, so a key scoped to a narrow `allowedModels` list could still execute against an unvetted (and possibly costlier) vision model the reroute picked. `chat.ts` now re-validates any guardrail-driven model change against the same per-key allowlist (`isModelAllowedForKey`) before honoring it, falling back to the original already-approved model when the reroute target is not allowed. The reroute path also now honors an explicit `settings.visionBridgeModel` operator override (previously ignored, unlike the combo/describe path a few lines below it, which already respects it via `getVisionBridgeConfig`). Regression guard: `tests/unit/guardrails/visionBridge.test.ts` (22 tests). (thanks @herjarsa)
|
||||
- **fix(auth):** an API key restricted via `allowedModels`/`allowedCombos` could bypass that restriction entirely over the Codex Responses-over-WebSocket bridge ([#6564](https://github.com/diegosouzapw/OmniRoute/issues/6564)) — `prepare()` in `src/app/api/internal/codex-responses-ws/route.ts` authenticated the WS bridge's API key (`authenticate()`/`authorizeWebSocketHandshake()`) and honored `allowedConnections`, but never called `enforceApiKeyPolicy()`, the same model/combo policy gate the HTTP `/v1/responses` path enforces via `handleChat()` — so a key scoped to e.g. `combo/model-1.0` could still reach a direct Codex model like `gpt-5.5` through this transport, as long as an eligible Codex OAuth connection existed. The bridge's WS auth token arrives via query params (`api_key`/`token`/`access_token`), not a normal `Authorization` header, so a new `enforceCodexWsApiKeyPolicy()` builds an equivalent `Request` carrying an explicit `Authorization: Bearer <apiKey>` header and calls `enforceApiKeyPolicy()` against the CLIENT-requested model, before any Codex-specific model remapping or credential selection. Regression guard: `tests/unit/codex-ws-policy-enforcement-6564.test.ts` (a model-restricted key is rejected 403 before reaching credential selection; a combo-restricted key is rejected 403 requesting a disallowed combo; a key that DOES allow the requested model still proceeds past policy).
|
||||
|
||||
@@ -1129,7 +1129,11 @@ export class CodexExecutor extends BaseExecutor {
|
||||
// Cursor may include custom tools (e.g. ApplyPatch) that work locally but are
|
||||
// invalid upstream, and translation bugs can leave orphaned/empty tool_choice names.
|
||||
normalizeCodexTools(body, {
|
||||
dropImageGeneration: isCodexFreePlan(credentials?.providerSpecificData),
|
||||
// gpt-5.3-codex-spark (and other Spark-scope models) reject image_generation
|
||||
// upstream even on paid-plan accounts, so drop it independent of plan (#6651).
|
||||
dropImageGeneration:
|
||||
isCodexFreePlan(credentials?.providerSpecificData) ||
|
||||
getCodexModelScope(model) === "spark",
|
||||
preserveCustomTools: nativeCodexPassthrough,
|
||||
});
|
||||
|
||||
|
||||
61
tests/unit/codex-spark-image-generation.test.ts
Normal file
61
tests/unit/codex-spark-image-generation.test.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* #6651 — Codex Desktop injects the `image_generation` hosted tool into every
|
||||
* Responses API request. OmniRoute only dropped it for free-plan Codex
|
||||
* accounts (isCodexFreePlan). It did NOT drop it for gpt-5.3-codex-spark (and
|
||||
* other Spark-scope models), which reject `image_generation` upstream even on
|
||||
* paid-plan accounts, producing:
|
||||
* [400]: Tool 'image_generation' is not supported with gpt-5.3-codex-spark.
|
||||
*
|
||||
* Fix: CodexExecutor.transformRequest now also drops image_generation when
|
||||
* the target model resolves to the Spark quota scope
|
||||
* (getCodexModelScope(model) === "spark"), independent of plan.
|
||||
*/
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { CodexExecutor } = await import("../../open-sse/executors/codex.ts");
|
||||
|
||||
function buildBody() {
|
||||
return {
|
||||
_nativeCodexPassthrough: true,
|
||||
tools: [
|
||||
{ type: "image_generation", output_format: "png" },
|
||||
{ type: "function", name: "foo", parameters: { type: "object" } },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
test("#6651: CodexExecutor.transformRequest drops image_generation for gpt-5.3-codex-spark even on a paid-plan account", () => {
|
||||
const executor = new CodexExecutor();
|
||||
|
||||
// Paid-plan account (not free) — isCodexFreePlan() alone returns false, so
|
||||
// the fix must rely on the model-scope check to still drop the tool.
|
||||
const result = executor.transformRequest("gpt-5.3-codex-spark", buildBody(), false, {
|
||||
providerSpecificData: { workspacePlanType: "team" },
|
||||
}) as { tools: Array<{ type?: string }> };
|
||||
|
||||
assert.equal(
|
||||
result.tools.some((t) => t.type === "image_generation"),
|
||||
false,
|
||||
"image_generation must be dropped for gpt-5.3-codex-spark regardless of account plan (#6651)"
|
||||
);
|
||||
assert.equal(
|
||||
result.tools.some((t) => t.type === "function"),
|
||||
true,
|
||||
"the function tool must survive"
|
||||
);
|
||||
});
|
||||
|
||||
test("#6651: CodexExecutor.transformRequest still preserves image_generation for non-Spark models on paid plans", () => {
|
||||
const executor = new CodexExecutor();
|
||||
|
||||
const result = executor.transformRequest("gpt-5", buildBody(), false, {
|
||||
providerSpecificData: { workspacePlanType: "team" },
|
||||
}) as { tools: Array<{ type?: string }> };
|
||||
|
||||
assert.equal(
|
||||
result.tools.some((t) => t.type === "image_generation"),
|
||||
true,
|
||||
"image_generation must still be preserved for non-Spark models on paid plans"
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user