Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
de822a8
fix(bridge): fail closed when enforced tool catalog is absent
oocheol Sep 24, 2026
394e47d
fix(bridge): retain unscoped null catalog compatibility
oocheol Sep 24, 2026
8fc5a00
test(bridge): assert nested tool refusal errors
oocheol Sep 24, 2026
763dc3a
fix(combo): refuse first dispatch when send budget is exhausted
oocheol Sep 24, 2026
272ddf2
fix(combo): preserve classified 413 on denied later hop
oocheol Sep 24, 2026
0a8efee
fix(combo): cap explicit Retry-After cooldowns at one day
oocheol Sep 24, 2026
cf50975
docs(combo): align translated cooldown ceilings
oocheol Sep 24, 2026
ad4bf1c
docs(combo): clarify cooldown contract and regression intent
oocheol Sep 24, 2026
8db5862
feat(combos): explicit last-resort cooldown policy for failover
abhisheksharma2411 Sep 24, 2026
395be72
fix(combos): address review on the last-resort cooldown policy
abhisheksharma2411 Sep 24, 2026
9179c25
test(layout): register combo-last-resort.test.ts
lidge-jun Sep 24, 2026
f131422
fix(combos): keep omitted reasoningEffortMode and imageInput on PUT
lidge-jun Sep 24, 2026
af2f502
docs(combos): state the 24h Retry-After cap and last-resort fields in…
lidge-jun Sep 24, 2026
44dd38f
test(combos): pin lastResort and cooldownWaitPolicy carry-over on PUT
lidge-jun Sep 24, 2026
c1e3c62
fix(combos): honor the last-resort policy on the post-failure hop
lidge-jun Sep 24, 2026
3fe2a55
fix(combos): validate targets before carrying lastResort over on PUT
lidge-jun Sep 24, 2026
8a7cf99
docs(combos): describe last-resort targets as emergency-only under th…
lidge-jun Sep 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/fr/guides/combos.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ Les échecs d’un combo se répartissent entre ceux qui entraînent un **bascul

Une cible sautée entre en temps de recharge pendant 60 secondes par défaut. Si la réponse en amont inclut un
valeur `Retry-After` valide, opencodex l’utilise à la place. Les secondes numériques et les valeurs de date HTTP sont
accepté, et chaque temps de recharge est limité à 10 minutes.
accepté, et un délai explicite `Retry-After` est plafonné à 24 heures ; les autres temps de recharge restent plafonnés à 10 minutes.

La requête actuelle ne réessaye jamais la même cible tentée. Les demandes ultérieures l'ignorent jusqu'à ce qu'il soit
le temps de recharge expire. S’il ne reste aucune cible éligible, le proxy renvoie HTTP 503 avec
Expand Down Expand Up @@ -355,7 +355,7 @@ exécution d'une instance opencodex qui reçoit des requêtes de modèle.
Chaque cible est actuellement inéligible : par exemple, son fournisseur est désactivé, il est en phase de refroidissement,
elle a déjà été tentée pour cette requête, ou une tâche v2 chiffrée l'exclut. Vérifier la cible
état du fournisseur et erreurs récentes en amont. Pour les temps de recharge, attendez la valeur par défaut de 60 secondes ou la
délai indiqué par `Retry-After` en amont (jamais plus de 10 minutes), puis réessayez.
délai indiqué par `Retry-After` en amont (au maximum 24 heures pour un `Retry-After` explicite, contre 10 minutes pour les autres), puis réessayez.

### Pourquoi mon alias a-t-il été rejeté ?

Expand Down
4 changes: 4 additions & 0 deletions docs-site/src/content/docs/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,10 @@ code-mode `exec` has the call converted into the matching `tools.<helper>(...)`
`exec`. A catalog that genuinely declares the bare goal tool keeps it, and a catalog that declares
neither the tool nor `exec` still rejects the call as undeclared.

For routed Responses turns, an explicit tool-enforcement policy also rejects client tool calls if
the request's declared-tool catalog is unavailable. An empty declared catalog rejects every client
tool call; Chat and Anthropic clients retain their own tool-validation responsibility.

Routed code-mode turns are also told the host's rules for the nested helpers before the first
call: `tools.apply_patch` takes one string that opens and closes with the bare patch marker lines,
the isolate has no `import`, and long-running commands are polled through `write_stdin`. When a
Expand Down
57 changes: 52 additions & 5 deletions docs-site/src/content/docs/guides/combos.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,17 +221,58 @@ Combo failures are divided into **hop** failures and **terminal** failures.
| First tool call of a Responses turn run by an in-process adapter (`runTurn`) that the current request did not declare, before any output or replay-unsafe side effect | Cool the target and hop with the same tool catalog. After visible output or a replay-unsafe side effect the refusal is final. Chat Completions and Anthropic Messages requests are unchanged. |
| Any other unclassified error | Stop and return the error. |

If the shared request send budget refuses the first target, the combo returns a local 429
`request_send_budget_exhausted` without contacting a provider. If it refuses a later target,
the combo returns the last real upstream failure without sending to that target.

When `cooldownMs` is unset, a hopped target uses an upstream fallback: 5 seconds for request-rate
429s with upstream code `1302` or `1305`, and 60 seconds otherwise. When it is set, `cooldownMs`
applies whenever no usable upstream `Retry-After` or Codex reset signal exists, including those
request-rate 429s. Numeric `Retry-After` seconds and HTTP-date values are accepted, and every
cooldown is capped at 10 minutes. The precedence is, from strongest to weakest, explicit
request-rate 429s. Numeric `Retry-After` seconds and HTTP-date values are accepted. Explicit
server delays are capped at 24 hours; reset-derived, configured, and fallback cooldowns are capped
at 10 minutes. The precedence is, from strongest to weakest, explicit
`Retry-After` → Codex reset headers (`x-codex-primary-reset-at`, `x-codex-secondary-reset-at`, or
`x-codex-tertiary-reset-at`) → the combo's `cooldownMs` (when set) → the 5-second request-rate
fallback for upstream rate-limit codes `1302`/`1305` → the 60-second default. A valid immediate
`Retry-After: 0` remains an immediate upstream directive rather than being replaced by a configured
cooldown.

### Last-resort targets

A brief cooldown on a preferred target otherwise routes straight to whatever
comes next in the list — including a target you only ever wanted used in an
emergency. Mark it, and tell the combo to wait first:

```json
{
"strategy": "failover",
"cooldownWaitPolicy": "before-last-resort",
"waitForCooldownMs": 10000,
"targets": [
{ "provider": "provider-a", "model": "model-a" },
{ "provider": "provider-b", "model": "model-b" },
{ "provider": "provider-c", "model": "model-c", "lastResort": true }
]
}
```

With the policy set, selection tries the normal targets first. If they are only
cooling and the earliest cooldown expires inside `waitForCooldownMs`, the
request waits for that instead of dispatching the last-resort target. The policy
needs a nonzero `waitForCooldownMs`; at the default `0` there is no wait to
take, so the last resort is dispatched as before.

**The policy only ever defers.** When no normal target can be reached — every
one cooling past the budget, already attempted, or ruled out — the last-resort
target is dispatched as usual. A policy that could withhold it would turn a
fallback into an outage, which is worse than the premature routing it prevents.
The same applies to a combo whose targets are *all* marked `lastResort`: it
dispatches normally.

`lastResort` is inert unless `cooldownWaitPolicy` is set, and both are omitted
by default, so existing combos are unaffected. Only the exact string
`before-last-resort` opts in.

The current request never retries the same attempted target. Later requests skip a cooled target until its
cooldown expires; request-local compatibility rejections do not cool the target. A `Retry-After` HTTP-date that is already in the past is also preserved as an
immediate upstream directive, just like `Retry-After: 0`. Set `waitForCooldownMs` to allow a later
Expand Down Expand Up @@ -390,6 +431,10 @@ value to change it. An explicit `cooldownMs` (even `60000`) is persisted as-is b
the request-rate fallback. A stored `cooldownMs` can only be removed by editing the configuration file;
`waitForCooldownMs` resets to its default when a `PUT` explicitly sends `0`, because the sparse
serializer omits that default. Omission preserves both values and the dashboard does not expose them yet.
Omitting `defaultEffortMode`, `reasoningEffortMode`, `imageInput`, or `cooldownWaitPolicy` likewise
keeps the stored value, and a re-sent target without `lastResort` keeps that target's flag (matched by
provider and model). The dashboard always sends `imageInput` and `reasoningEffortMode`, so switching
them back to `auto` or `strict` there still replaces the stored value.

For the complete persisted configuration, see [Configuration](/reference/configuration/).

Expand All @@ -416,12 +461,14 @@ Combos are stored in the top-level `combos` object, keyed by combo id:

| Field | Required | Default | Rules |
| --- | --- | --- | --- |
| `targets` | Yes | — | Non-empty ordered array of configured `{ provider, model, weight? }` targets. Duplicate provider/model pairs are rejected. |
| `targets` | Yes | — | Non-empty ordered array of configured `{ provider, model, weight?, lastResort? }` targets. Duplicate provider/model pairs are rejected. |
| `targets[].weight` | No | `1` | Integer from 1 to 10,000. Used by round-robin and random; ignored by failover, least-used, and reset-window. |
| `targets[].lastResort` | No | `false` | Marks an emergency-only target. Inert unless `cooldownWaitPolicy` is set. Never makes a target permanently ineligible: when no normal target can be reached it is dispatched as usual. |
| `strategy` | No | `"failover"` | `"failover"`, `"round-robin"`, `"random"`, `"least-used"`, or `"reset-window"`. |
| `stickyLimit` | No | `1` | Integer from 1 to 100 successful requests per round-robin selection. Applies only to round-robin. |
| `cooldownMs` | No | unset → upstream fallback (5 s for request-rate 429 codes `1302`/`1305`, otherwise 60 s) | Integer from 1 to 600000. When set, applies as the per-target cooldown whenever no usable upstream `Retry-After` or Codex reset signal exists, including request-rate 429s; when unset, uses the upstream fallback. |
| `waitForCooldownMs` | No | `0` | Integer from 0 to 600000. Maximum time to wait for the earliest eligible cooling target before returning `combo_unavailable`; abort cancels the wait. |
| `cooldownWaitPolicy` | No | unset | `"before-last-resort"` defers targets marked `lastResort` while a normal target is cooling and that cooldown fits inside `waitForCooldownMs`. Only that exact string opts in. The deferral wait and the ordinary wait share one `waitForCooldownMs` budget per selection attempt. |
| `defaultEffort` | No | `null` | `low`, `medium`, `high`, `xhigh`, `max`, or `ultra`; resolved against each target's advertised ladder. |
| `defaultEffortMode` | No | `"fallback"` | `"fallback"` preserves explicit caller effort. `"force"` overrides valid caller effort, requires a valid non-null default, and can increase cost and latency. |
| `reasoningEffortMode` | No | `"strict"` | `"strict"` intersects every known target ladder, so one target advertising no effort control empties the combo's picker. `"adaptive"` excludes those empty ladders from the published intersection. At dispatch, explicit empty or adaptive unknown ladders remove unsupported effort/thinking controls while preserving supported non-effort reasoning fields such as `reasoning.summary`; known non-empty targets keep existing effort resolution. |
Expand All @@ -445,8 +492,8 @@ it has already been attempted for this request, or an encrypted v2 task excludes
provider state and recent upstream errors. For cooldowns, follow an observed `Retry-After` value first;
Codex reset headers also take precedence over `cooldownMs`.
If neither upstream signal is usable, the configured `cooldownMs` applies, or the upstream fallback applies
when it is unset (5 seconds for request-rate codes `1302`/`1305`, otherwise 60 seconds); every cooldown is
capped at 10 minutes.
when it is unset (5 seconds for request-rate codes `1302`/`1305`, otherwise 60 seconds). Explicit
`Retry-After` delays are capped at 24 hours; the other cooldowns are capped at 10 minutes.

### Why was my alias rejected?

Expand Down
Loading
Loading