Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 45 additions & 0 deletions docs-site/src/content/docs/guides/combos.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,51 @@ order. Weights and `stickyLimit` do not affect this strategy.

This ranking and provider exclusion before dispatch require fresh model-inference limits that apply to the current single API key as a whole. OAuth/current-account summaries, caller-forward routes, multiple keys, and snapshots with changed credentials or destinations are display-only for this early decision. The same applies when `Authorization`, `x-api-key`, or `x-goog-api-key` headers override credentials; search-only and MCP-only windows are excluded. If no eligible target has an applicable reset, configuration order wins. Account selection and retries still enforce their normal limits.

### Advisory quota signals for JEV

Quota evidence is separately opt-in **per JEV Combo**, for all three decision methods:

```bash
ocx combo set coding --decision-quota-signals on --json
ocx combo set coding --decision-quota-tiers '{"moderate":40}' --json
```

Only enable this on an existing `strategy: "jev"` Combo. `decisionQuotaSignals: true` adds
subscription quota tiers to the decision request, not a hard eligibility rule. The decision
backend is asked to prefer healthier quota among adequate options; it still chooses only from
configured eligible targets and effort allowlists. Unknown is not exhausted, and ordinary
fallback, cooldown, account selection and provider limits remain unchanged.

Defaults are `limited: 70` and `nearlyExhausted: 90`, inclusive percentages used. There is no
moderate tier unless you set it explicitly (for example, `moderate: 40`). Partial overrides merge
with those defaults, then all thresholds must be finite, within 0–100 and strictly ascending.

Evidence comes only from memory snapshots published by existing quota collectors, accepted
response headers and credential/account owners. Reading advisory quota evidence never resolves
credentials, loads auth stores or quota files, reads a keychain, probes upstream, or starts a
refresher. Normal decision-backend credential resolution is unchanged. Cold, missing, stale (over
30 minutes), future, invalid or reset-expired evidence is unknown. Each account uses its worst valid
relevant five-hour, weekly, monthly or structurally model-scoped window. An enabled usable
Anthropic/Codex pool uses its healthiest account tier; unknown ties healthy. A pool is nearly
exhausted only if every usable account has known nearly-exhausted evidence. Reserve models
without separately attributable bars remain unknown. Active-account summaries, multiple-key
display reports, credits and API/Spark/USD meters are not quota evidence for this decision.
The separate Anthropic · Pool 2 (`anthropic2`) account pool is not a source of this evidence, so its targets stay unknown.

:::caution[Additional decision data]
Opting in sends quota tiers, and available used percentage/window/reset delay, to your selected
decision backend, including hosted TypeSafe. No account ids, tokens or credential fingerprints
are sent or logged by this feature. Local decision records retain only bounded tier counts and
the selected tier. If quota would exceed the 64 KiB request cap, it is removed before retrying
serialization; an ordinary oversized decision still fails open.
:::

`--decision-quota-signals off` disables evidence while retaining configured tiers; `-` clears
the switch. `--decision-quota-tiers -` clears overrides, restoring defaults. API `null` clears;
API `false` disables the switch. Omitted fields survive JEV updates, target replacement and
rename, including saves through the existing editor. Switching away from JEV removes both
quota settings. No new editor controls or background quota work are enabled.

### Decision method

`strategy: "jev"` asks a decision backend to choose the first eligible target and a compatible
Expand Down
7 changes: 7 additions & 0 deletions docs-site/src/content/docs/reference/cli/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,13 @@ opencodex route (for example `ollama/qwen3:4b`) that answers the same choice as
this combo or any JEV combo. Omitting both uses TypeSafe. `--decision-timeout <ms|->` sets the
decision deadline (1000–120000, default 4000); `-` clears any of the three.

`ocx combo set <id> --decision-quota-signals on|off|-` controls per-Combo advisory evidence.
`--decision-quota-tiers '{"limited":80}'` sets partial threshold overrides; `-` clears them.
Both are JEV-only. Omitted policy survives `--targets-file` replacement, `--targets` with `--strategy jev`, and rename (`--targets` alone keeps the existing failover default). Off retains thresholds;
switching away from JEV removes both settings. This adds quota data to the selected decision
backend, not probes, new account selection or hard exclusions. See
[quota defaults, freshness and privacy](/guides/combos/#advisory-quota-signals-for-jev).

`ocx combo test [--combo <id>] [--decision-provider <provider|jev> | --decision-model <route>]
[--decision-timeout <ms>]` sends one synthetic decision probe through a saved combo's method or an
unsaved selection and reports the gate, backend, and latency; it may spend one decision call.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,13 @@ namespace, and cannot use reserved bare native families such as `gpt-*`, `o1-*`,
| `decisionProvider?` | `string` | `"jev"` | `strategy: "jev"` only. `"jev"` (the same as omitting it, and stored as omission) is the TypeSafe decision service, valid without a provider row; any other value must name a configured provider with `adapter: "jev-decision"` and a full HTTPS decision `baseUrl` (any path) or a local HTTP `/systemone` endpoint. Userinfo, query strings, and fragments are refused. |
| `decisionModel?` | `string` | unset | `strategy: "jev"` only, mutually exclusive with `decisionProvider`. An ordinary opencodex route (for example `ollama/qwen3:4b`) asked to pick one offered option as JSON. It runs with the selected provider's stored credentials, never the caller's, and cannot resolve to this combo, any JEV combo, or a `jev-decision` row. |
| `decisionTimeoutMs?` | `number` | `4000` | `strategy: "jev"` only. Decision deadline before failing open, 1000–120000 ms. |
| `decisionQuotaSignals?` | `boolean` | `false` | JEV only. Explicit `true` sends loaded advisory quota evidence to the selected backend; never changes hard eligibility or account selection. |
| `decisionQuotaTiers?` | `{ moderate?: number; limited?: number; nearlyExhausted?: number }` | `{ limited: 70, nearlyExhausted: 90 }` | JEV only. Partial overrides merge with defaults; finite 0–100 values must be strictly ascending. `moderate` is absent unless explicit. Does not enable signals by itself. |

Quota settings, privacy and unknown-pool semantics are described in
[Advisory quota signals for JEV](/guides/combos/#advisory-quota-signals-for-jev).
Omitted settings survive JEV API updates/rename; `null` clears, `false` disables signals without
clearing tiers, and switching to another strategy removes both settings.

```json
{
Expand Down
8 changes: 4 additions & 4 deletions scripts/test-layout/layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@
"codex-account-label.test.ts": "codex-integration",
"codex-account-mode-state.test.ts": "gui",
"codex-account-namespaces.test.ts": "codex-integration",
"codex-account-selection-preferences.test.ts": "codex-integration",
"codex-account-selection-preferences.test.ts": "codex-integration", "codex-account-store-quota-isolation.test.ts": "codex-integration",
"codex-account-store-refresh-classification.test.ts": "codex-integration",
"codex-account-store.test.ts": "codex-integration",
"codex-account-threshold-api.test.ts": "codex-integration",
Expand Down Expand Up @@ -851,7 +851,7 @@
"debug-settings.test.ts": "server",
"debug.test.ts": "lib",
"decision-discovery.test.ts": "server",
"server-jev-model-decision-e2e.test.ts": "server",
"server-jev-model-decision-e2e.test.ts": "server", "server-jev-model-quota-budget.test.ts": "server",
"decision-routes.test.ts": "server",
"deepinfra-provider.test.ts": "providers",
"deepseek-inbound-wire.test.ts": "providers",
Expand Down Expand Up @@ -1057,7 +1057,7 @@
"key-login-preserves-model-costs.test.ts": "oauth",
"jev-decision.test.ts": "routing", "jev-decision-destination.test.ts": "routing", "jev-service-exchange.test.ts": "routing",
"jev-decision-provider-combo.test.ts": "routing",
"jev-typesafe-golden.test.ts": "routing",
"jev-typesafe-golden.test.ts": "routing", "jev-quota-signals.test.ts": "routing", "jev-quota-config.test.ts": "routing", "jev-quota-publication.test.ts": "routing", "jev-quota-codex-reset.test.ts": "routing", "jev-quota-anthropic2.test.ts": "routing",
"jev-model-backend.test.ts": "routing",
"jev-provider.test.ts": "providers",
"keyring-smoke.test.ts": "ci-workflows",
Expand Down Expand Up @@ -1307,7 +1307,7 @@
"oauth-refresh-lock-multiprocess.test.ts": "oauth",
"oauth-refresh.test.ts": "oauth",
"oauth-status-privacy.test.ts": "oauth",
"oauth-store-multi.test.ts": "oauth",
"oauth-store-multi.test.ts": "oauth", "oauth-store-quota-isolation.test.ts": "oauth",
"oauth-tos-warning.test.ts": "gui",
"oauth-upsert-preserves-api-key.test.ts": "oauth",
"ocx-launcher-runtime.test.ts": "cli",
Expand Down
4 changes: 3 additions & 1 deletion skills/ocx/references/01_surface_agents-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ JSON mode: `payload`.

### `ocx combo set`

Usage: `ocx combo set <id> [--targets <provider/model[:weight],...> | --targets-file <FILE|->] [--strategy <failover|round-robin|random|least-used|reset-window|jev>] [--sticky <1-100|->] [--effort <low|medium|high|xhigh|max|ultra|->] [--effort-mode <fallback|force|->] [--alias <name|->] [--native-alias [on|off]] [--display-name <label|->] [--decision-provider <provider|-> | --decision-model <route|->] [--decision-timeout <ms|->] [--rename-from <id>] [--image-input <auto|disabled>] [--reasoning-effort-mode <strict|adaptive>] [--json]`
Usage: `ocx combo set <id> [--targets <provider/model[:weight],...> | --targets-file <FILE|->] [--strategy <failover|round-robin|random|least-used|reset-window|jev>] [--sticky <1-100|->] [--effort <low|medium|high|xhigh|max|ultra|->] [--effort-mode <fallback|force|->] [--alias <name|->] [--native-alias [on|off]] [--display-name <label|->] [--decision-provider <provider|-> | --decision-model <route|->] [--decision-timeout <ms|->] [--decision-quota-signals <on|off|->] [--decision-quota-tiers <JSON|->] [--rename-from <id>] [--image-input <auto|disabled>] [--reasoning-effort-mode <strict|adaptive>] [--json]`

Create, update or rename a combo.

Expand All @@ -770,6 +770,8 @@ State-changing: yes.
| `--decision-provider` | string | JEV provider; - clears. |
| `--decision-model` | string | JEV route; - clears; exclusive with provider. |
| `--decision-timeout` | string | JEV deadline 1000-120000 ms; - clears. |
| `--decision-quota-signals` | string | JEV advisory quota on/off; - clears. |
| `--decision-quota-tiers` | string | Partial JSON thresholds; - clears. |
| `--rename-from` | string | Existing combo ID to rename. |
| `--json` | boolean | Emit structured JSON. |
| `--native-alias` | string | Bare legacy flag means on; explicit on/off preserves presence, including false. |
Expand Down
4 changes: 2 additions & 2 deletions src/cli/capabilities-agents-routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,10 @@ export const AGENT_ROUTING_CAPABILITIES: readonly Capability[] = [
},
{
command: ["combo","set"],
usage: "ocx combo set <id> [--targets <provider/model[:weight],...> | --targets-file <FILE|->] [--strategy <failover|round-robin|random|least-used|reset-window|jev>] [--sticky <1-100|->] [--effort <low|medium|high|xhigh|max|ultra|->] [--effort-mode <fallback|force|->] [--alias <name|->] [--native-alias [on|off]] [--display-name <label|->] [--decision-provider <provider|-> | --decision-model <route|->] [--decision-timeout <ms|->] [--rename-from <id>] [--image-input <auto|disabled>] [--reasoning-effort-mode <strict|adaptive>] [--json]",
usage: "ocx combo set <id> [--targets <provider/model[:weight],...> | --targets-file <FILE|->] [--strategy <failover|round-robin|random|least-used|reset-window|jev>] [--sticky <1-100|->] [--effort <low|medium|high|xhigh|max|ultra|->] [--effort-mode <fallback|force|->] [--alias <name|->] [--native-alias [on|off]] [--display-name <label|->] [--decision-provider <provider|-> | --decision-model <route|->] [--decision-timeout <ms|->] [--decision-quota-signals <on|off|->] [--decision-quota-tiers <JSON|->] [--rename-from <id>] [--image-input <auto|disabled>] [--reasoning-effort-mode <strict|adaptive>] [--json]",
summary: "Create, update or rename a combo.",
routes: [{"method":"GET","path":"/api/combos"},{"method":"PUT","path":"/api/combos"}],
flags: [{"name":"--targets","value":"string","summary":"Ordered provider/model[:weight] list; required for creation."},{"name":"--strategy","value":"string","summary":"Routing strategy."},{"name":"--sticky","value":"string","summary":"Round-robin limit 1-100; - resets."},{"name":"--effort","value":"string","summary":"Default effort; - clears."},{"name":"--effort-mode","value":"string","summary":"fallback or force; - resets to fallback."},{"name":"--alias","value":"string","summary":"Alias; - clears."},{"name":"--display-name","value":"string","summary":"Label; - clears."},{"name":"--decision-provider","value":"string","summary":"JEV provider; - clears."},{"name":"--decision-model","value":"string","summary":"JEV route; - clears; exclusive with provider."},{"name":"--decision-timeout","value":"string","summary":"JEV deadline 1000-120000 ms; - clears."},{"name":"--rename-from","value":"string","summary":"Existing combo ID to rename."},{"name":"--json","value":"boolean","summary":"Emit structured JSON."},{"name":"--native-alias","value":"string","summary":"Bare legacy flag means on; explicit on/off preserves presence, including false."},{"name":"--targets-file","value":"string","summary":"Bounded ordered target JSON array, exclusive with --targets; preserves effort/profile/last-resort metadata."},{"name":"--image-input","value":"string","summary":"auto or disabled; explicit auto clears the previous disabled choice."},{"name":"--reasoning-effort-mode","value":"string","summary":"strict or adaptive, distinct from --effort-mode fallback/force."}],
flags: [{"name":"--targets","value":"string","summary":"Ordered provider/model[:weight] list; required for creation."},{"name":"--strategy","value":"string","summary":"Routing strategy."},{"name":"--sticky","value":"string","summary":"Round-robin limit 1-100; - resets."},{"name":"--effort","value":"string","summary":"Default effort; - clears."},{"name":"--effort-mode","value":"string","summary":"fallback or force; - resets to fallback."},{"name":"--alias","value":"string","summary":"Alias; - clears."},{"name":"--display-name","value":"string","summary":"Label; - clears."},{"name":"--decision-provider","value":"string","summary":"JEV provider; - clears."},{"name":"--decision-model","value":"string","summary":"JEV route; - clears; exclusive with provider."},{"name":"--decision-timeout","value":"string","summary":"JEV deadline 1000-120000 ms; - clears."},{"name":"--decision-quota-signals","value":"string","summary":"JEV advisory quota on/off; - clears."},{"name":"--decision-quota-tiers","value":"string","summary":"Partial JSON thresholds; - clears."},{"name":"--rename-from","value":"string","summary":"Existing combo ID to rename."},{"name":"--json","value":"boolean","summary":"Emit structured JSON."},{"name":"--native-alias","value":"string","summary":"Bare legacy flag means on; explicit on/off preserves presence, including false."},{"name":"--targets-file","value":"string","summary":"Bounded ordered target JSON array, exclusive with --targets; preserves effort/profile/last-resort metadata."},{"name":"--image-input","value":"string","summary":"auto or disabled; explicit auto clears the previous disabled choice."},{"name":"--reasoning-effort-mode","value":"string","summary":"strict or adaptive, distinct from --effort-mode fallback/force."}],
mutates: true,
json: "payload",
details: ["create and update are aliases of set. Existing fields are read before writing; --targets replaces targets.","force overrides valid client effort and may increase cost and latency.","Only the implemented target grammar is supported; full GUI target metadata and routing-profile writes are separate tasks.","New file/flag conflicts refuse before transport. Partial edits preserve target metadata; explicit false/default values override server carry. Turning a native alias off may also require clearing its incompatible alias.","Read/merge/write uses one target and remains an upsert without revision CAS. Saved-but-incomplete catalog outcomes return nonzero with their receipt."],
Expand Down
8 changes: 7 additions & 1 deletion src/cli/combo-result.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { normalizeJevQuotaTiers } from "../combos/jev-quota-config";
import { parseComboTargets } from "./combo-input";
import { printCatalogResult } from "./catalog-command-result";

Expand Down Expand Up @@ -33,7 +34,12 @@ function publicCombo(value: unknown): Record<string, unknown> {
} else if (Object.hasOwn(RANGES, key)) {
const [min, max] = RANGES[key]!;
if (typeof child !== "number" || !Number.isSafeInteger(child) || child < min || child > max) invalid();
} else if (key === "nativeAlias") {
} else if (key === "decisionQuotaTiers") {
const tiers = normalizeJevQuotaTiers(child);
if (!tiers) invalid();
result[key] = tiers;
continue;
} else if (key === "nativeAlias" || key === "decisionQuotaSignals") {
if (typeof child !== "boolean") invalid();
} else invalid();
result[key] = child;
Expand Down
Loading
Loading