Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
8 changes: 2 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,6 @@ NEXT_PUBLIC_CLOUD_URL=
# debugging or when routing through a corporate mirror. Used by:
# open-sse/services/usage.ts.
#OMNIROUTE_CROF_USAGE_URL=https://crof.ai/usage_api/
#OMNIROUTE_GEMINI_CLI_USAGE_URL=https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist
#OMNIROUTE_CODEWHISPERER_BASE_URL=https://codewhisperer.us-east-1.amazonaws.com
#OMNIROUTE_OPENCODE_QUOTA_URL=https://opencode.ai/zen/go/v1/quota
#OMNIROUTE_OPENCODE_GO_QUOTA_URL=https://api.z.ai/api/monitor/usage/quota/limit
Expand Down Expand Up @@ -708,7 +707,7 @@ CODEX_OAUTH_CLIENT_ID=app_EMoamEEZ73f0CkXaXp7hrann
# Used by: open-sse/executors/theoldllm.ts. Default: 30000 (30s).
# THEOLDLLM_NAV_TIMEOUT_MS=30000

# ── Gemini / Gemini CLI / Antigravity / Windsurf (all Google-based) ──
# ── Gemini / Antigravity / Windsurf (all Google-based) ──
# These providers ship public OAuth client_id/secret values (or Firebase Web
# keys) embedded in their public CLIs/binaries. Defaults are baked into the
# code via open-sse/utils/publicCreds.ts — leave the env vars unset to use
Expand All @@ -717,8 +716,6 @@ CODEX_OAUTH_CLIENT_ID=app_EMoamEEZ73f0CkXaXp7hrann
#
# GEMINI_OAUTH_CLIENT_ID=
# GEMINI_OAUTH_CLIENT_SECRET=
# GEMINI_CLI_OAUTH_CLIENT_ID=
# GEMINI_CLI_OAUTH_CLIENT_SECRET=
# ANTIGRAVITY_OAUTH_CLIENT_ID=
# ANTIGRAVITY_OAUTH_CLIENT_SECRET=
# WINDSURF_FIREBASE_API_KEY=
Expand Down Expand Up @@ -806,7 +803,7 @@ GITHUB_OAUTH_CLIENT_ID=Iv1.b507a08c87ecfe98
# VISION_BRIDGE_API_KEY=

# ─────────────────────────────────────────────────────────────────────────────
# ⚠️ GOOGLE OAUTH (Antigravity, Gemini CLI) & OTHER PROVIDERS — REMOTE SERVERS
# ⚠️ GOOGLE OAUTH (Antigravity) & OTHER PROVIDERS — REMOTE SERVERS
# ─────────────────────────────────────────────────────────────────────────────
# The default Client IDs above ONLY work when OmniRoute runs on localhost.
# For remote/VPS hosting (including Docker containers on remote servers):
Expand Down Expand Up @@ -861,7 +858,6 @@ KIRO_USER_AGENT="AWS-SDK-JS/3.0.0 kiro-ide/1.0.0"
QODER_USER_AGENT="Qoder-Cli"
QWEN_USER_AGENT="QwenCode/0.19.3 (linux; x64)"
CURSOR_USER_AGENT="Cursor/3.4"
GEMINI_CLI_USER_AGENT="google-api-nodejs-client/10.3.0"

# Override Codex client version sent in headers independently of the
# CODEX_USER_AGENT string. Used by: open-sse/config/codexClient.ts.
Expand Down
2 changes: 1 addition & 1 deletion @omniroute/opencode-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ Every field is optional. Defaults mirror v0.1.0 behaviour so existing `opencode.
| `combos` | `boolean` | `true` | Discover `/api/combos` and surface them as pseudo-models with LCD capabilities. Combos are keyed under the `combo/<slug>` namespace and labelled `Combo: <name>` in the model picker so they're distinguishable from raw provider/model pairs. |
| `enrichment` | `boolean` | `true` | Pull display names from `/api/pricing/models` AND per-million-token pricing (`input`, `output`, `cached` → `cacheRead`, `cache_creation` → `cacheWrite`) from `/api/pricing`, then overlay both onto the live catalog (so the UI shows `Claude 4.7 Opus` with `cost.input: 5`, `cost.output: 25` instead of raw IDs and zeroed cost). |
| `compressionMetadata` | `boolean` | `false` | Pull `/api/context/combos` so combo names get tagged with their compression pipeline, e.g. `Combo: claude-primary [rtk🟡 → caveman🟠]`. Intensity tokens render as traffic-light emoji (🟢 lite/minimal · 🟡 standard · 🟠 aggressive/full · 🔴 ultra) so the picker advertises "how compressed" each combo is at a glance. |
| `providerTag` | `boolean` | `true` | Prepend a short upstream-provider label to the enriched display name with `" - "` separator, so `cc/claude-opus-4-7 → Claude - Claude Opus 4.7` differs visibly from `kr/claude-opus-4-7 → Kiro - Claude Opus 4.7` in the OC TUI model picker. Label resolution: use `/api/pricing/models[<alias>].name` verbatim when ≤8 chars (e.g. `Claude`, `Kiro`, `Codex`, `Qwen`), otherwise fall back to `UPPER(alias)` (e.g. `GitHub Models` → `GHM`, `Gemini-cli` → `GEMINI-CLI`). Idempotent. Combos intentionally skipped (the `Combo: ` prefix already conveys multi-upstream). |
| `providerTag` | `boolean` | `true` | Prepend a short upstream-provider label to the enriched display name with `" - "` separator, so `cc/claude-opus-4-7 → Claude - Claude Opus 4.7` differs visibly from `kr/claude-opus-4-7 → Kiro - Claude Opus 4.7` in the OC TUI model picker. Label resolution: use `/api/pricing/models[<alias>].name` verbatim when ≤8 chars (e.g. `Claude`, `Kiro`, `Codex`, `Qwen`), otherwise fall back to `UPPER(alias)` (e.g. `GitHub Models` → `GHM`, `Gemini` → `GEMINI`). Idempotent. Combos intentionally skipped (the `Combo: ` prefix already conveys multi-upstream). |
| `usableOnly` | `boolean` | `false` | Read `/api/providers` and filter the catalog to providers that have at least one connection with `isActive: true` AND `testStatus: 'active'`. Subtract-filter semantics: providers unknown to BOTH the pricing-models catalog AND the connection table pass through (so synthetic prefixes like `agentrouter/*` survive). On fetch failure the filter is disabled for the refresh — never hides the whole catalog. |
| `diskCache` | `boolean` | `true` | Persist the last successful `/v1/models` + `/api/combos` + enrichment + connections + compression snapshot to `${OPENCODE_DATA_DIR ?? ~/.local/share/opencode}/plugins/omniroute-<providerId>.json`. On a subsequent cold start where `/v1/models` throws (network down / IP whitelist drop / 5xx) the static block hydrates from the snapshot so OC's model picker survives offline. Soft-fail on read/write — never blocks publishing. |
| `geminiSanitization` | `boolean` | `true` | Strip `$schema`/`$ref`/`additionalProperties` from tool params when the model id matches `gemini` |
Expand Down
11 changes: 5 additions & 6 deletions @omniroute/opencode-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1225,15 +1225,15 @@ export interface OmniRouteEnrichmentEntry {
cacheWrite?: number;
};
/**
* Provider alias prefix seen in `/v1/models` ids (e.g. `cc`, `gemini-cli`).
* Provider alias prefix seen in `/v1/models` ids (e.g. `cc`, `gemini`).
* Populated by `defaultOmniRouteEnrichmentFetcher` from
* `/api/pricing/models` keys. Drives the `usableOnly` alias↔canonical
* resolution.
*/
providerAlias?: string;
/**
* Canonical provider id used by `/api/providers` connections (e.g.
* `claude`, `gemini-cli`, `kiro`). Populated from the per-provider
* `claude`, `gemini`, `kiro`). Populated from the per-provider
* `entry.id` field inside `/api/pricing/models`.
*/
providerCanonical?: string;
Expand Down Expand Up @@ -2046,7 +2046,7 @@ export function formatCompressionPipeline(pipeline: OmniRouteCompressionStep[]):
export interface OmniRouteProviderConnection {
/** Connection UUID. */
id: string;
/** Canonical provider id, e.g. `claude`, `gemini-cli`, `kiro`. Matches `entry.id` in `/api/pricing/models`. */
/** Canonical provider id, e.g. `claude`, `gemini`, `kiro`. Matches `entry.id` in `/api/pricing/models`. */
provider: string;
/** Connection auth flavor, e.g. `apikey`, `oauth`, `cookie`. */
authType?: string;
Expand Down Expand Up @@ -2125,7 +2125,7 @@ export const defaultOmniRouteProvidersFetcher: OmniRouteProvidersFetcher = async
* walk only the namespaced keys to derive the alias↔canonical mapping).
*
* Returns:
* - `aliases`: set of alias prefixes safe to keep (e.g. `cc`, `gemini-cli`).
* - `aliases`: set of alias prefixes safe to keep (e.g. `cc`, `gemini`).
* - `canonicals`: set of canonical provider ids (e.g. `claude`, `kiro`).
*
* Callers should treat membership in EITHER set as "usable" — raw model
Expand Down Expand Up @@ -2174,7 +2174,7 @@ export function usableProviderAliasSet(
}
// Always include every usable canonical as an alias too — handles the
// common case where `/v1/models` ids use the canonical id directly
// (e.g. `gemini-cli/gemini-1.5-pro`).
// (e.g. `gemini/gemini-1.5-pro`).
for (const canonical of usableCanonicals) aliases.add(canonical);
return { aliases, canonicals: usableCanonicals, knownAliases };
}
Expand Down Expand Up @@ -3174,7 +3174,6 @@ export function sanitizeGeminiToolSchemas(payload: unknown): unknown {
* `gemini-2.5-flash`, etc.)
* - `models/gemini-…` (Google Generative AI canonical id form)
* - `google-vertex/gemini-…` (OpenCode + AI-SDK Vertex routing prefix)
* - `gemini-cli/…` (real OmniRoute alias surfaced on b35 prod `/v1/models`)
*
* Liberal by design: a false positive (cleaning a payload that didn't
* need cleaning) costs only a structuredClone + one walk; a false negative
Expand Down
11 changes: 4 additions & 7 deletions @omniroute/opencode-plugin/tests/config-shim.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1311,9 +1311,9 @@ test("config: providerTag (default-on) prepends '<provider> - ' to enriched raw-
"gemini-3-flash",
{
name: "Gemini 3 Flash",
providerAlias: "gemini-cli",
providerCanonical: "gemini-cli",
providerDisplayName: "Gemini-cli",
providerAlias: "gemini",
providerCanonical: "gemini",
providerDisplayName: "Gemini",
},
],
])
Expand All @@ -1335,10 +1335,7 @@ test("config: providerTag (default-on) prepends '<provider> - ' to enriched raw-
entry.models["opencode-omniroute/claude-sonnet-4-6"].name,
"Claude - Claude Sonnet 4.6"
);
assert.equal(
entry.models["opencode-omniroute/gemini-3-flash"].name,
"Gemini-cli - Gemini 3 Flash"
);
assert.equal(entry.models["opencode-omniroute/gemini-3-flash"].name, "Gemini - Gemini 3 Flash");
// Combos stay untouched — `Combo: ` prefix already conveys multi-upstream.
assert.equal(entry.models["opencode-omniroute/claude-tier"].name, "Claude Tier");
});
Expand Down
4 changes: 2 additions & 2 deletions @omniroute/opencode-plugin/tests/gemini-sanitize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ test("shouldSanitizeForGemini: google-vertex/gemini-1.5-flash → true", () => {
assert.equal(shouldSanitizeForGemini({ model: "google-vertex/gemini-1.5-flash" }), true);
});

test("shouldSanitizeForGemini: gemini-cli/gemini-2.5-pro → true (real OmniRoute alias)", () => {
assert.equal(shouldSanitizeForGemini({ model: "gemini-cli/gemini-2.5-pro" }), true);
test("shouldSanitizeForGemini: gemini/gemini-2.5-pro → true", () => {
assert.equal(shouldSanitizeForGemini({ model: "gemini/gemini-2.5-pro" }), true);
});

test("shouldSanitizeForGemini: claude-sonnet-4 → false", () => {
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ Zod schemas, and unit tests aligned when editing.

### Provider Categories

- **Free** (4): Qoder AI, Qwen Code, Gemini CLI (deprecated), Kiro AI
- **Free** (3): Qoder AI, Qwen Code, Kiro AI
- **OAuth** (14): Claude Code, Antigravity, Codex, GitHub Copilot, Cursor, Kimi Coding, Kilo Code, Cline, Qwen (⚠️ free tier discontinued 2026-04-15), Kiro, Qoder, Gemini, Windsurf (v3.8), GitLab Duo (v3.8)
- **API Key** (120+): OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Perplexity,
Together, Fireworks, Cerebras, Cohere, NVIDIA, Nebius, SiliconFlow, Hyperbolic,
Expand All @@ -291,7 +291,7 @@ Providers are registered in `src/shared/constants/providers.ts` with Zod validat
### Executors (`open-sse/executors/`)

Provider-specific request executors: `base.ts`, `default.ts`, `cursor.ts`, `codex.ts`,
`antigravity.ts`, `github.ts`, `gemini-cli.ts`, `kiro.ts`, `qoder.ts`, `vertex.ts`,
`antigravity.ts`, `github.ts`, `kiro.ts`, `qoder.ts`, `vertex.ts`,
`cloudflare-ai.ts`, `opencode.ts`, `pollinations.ts`, `puter.ts`.

#### Executor Internals
Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ Result: 4 layers of fallback = zero downtime
> Recent highlights from **v3.8.20 → v3.8.39**. Full history in [`CHANGELOG.md`](CHANGELOG.md).

- **⚖️ Quota-Share routing** — a dedicated combo strategy that spreads load across accounts by _available quota_: Deficit-Round-Robin scheduling, per-connection `max_concurrent` with cooldown-wait queueing, multi-window usage buckets (5h / 7d / per-model), per-(key,model) caps, session stickiness for prompt-cache integrity, and proactive saturation from upstream token-usage headers. → [Resilience Guide](docs/architecture/RESILIENCE_GUIDE.md)
- **🤖 One-command CLI/agent setup** — a dedicated `setup-*` command configures each coding tool to route through OmniRoute (Claude Code, Codex, Cline, Continue, Cursor, Roo Code, Kilo Code, Crush, Goose, Qwen Code, Aider, OpenCode, Gemini CLI); `omniroute launch` / `omniroute launch-codex` are zero-config launchers. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
- **🤖 One-command CLI/agent setup** — a dedicated `setup-*` command configures each coding tool to route through OmniRoute (Claude Code, Codex, Cline, Continue, Cursor, Roo Code, Kilo Code, Crush, Goose, Qwen Code, Aider, OpenCode); `omniroute launch` / `omniroute launch-codex` are zero-config launchers. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
- **🛰️ Remote mode** — drive a remote OmniRoute from any machine with scoped access tokens (`omniroute connect` / `omniroute contexts` / `omniroute tokens`), plus an `omniroute login antigravity` helper that runs Google "native/desktop" OAuth on your own machine and pastes a credential blob into a remote/VPS install (where the loopback redirect is unreachable). → [Remote Mode](docs/guides/REMOTE-MODE.md)
- **🧭 Smarter auto-routing** — OpenRouter-style `auto/<category>:<tier>` combos (e.g. `auto/coding:fast`, `auto/reasoning:pro`), a **Fusion** strategy (fan out to a panel of models in parallel, then synthesize via a judge), **task-aware routing** (best-fit connection per task type), per-request `X-Route-Model` override, live Arena-ELO + models.dev model intelligence, per-step account allowlists, provider-wildcard combo steps, nested combo-ref execution, sticky weighted selection, and `web_search`-aware routing. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
- **🗜️ Pluggable compression** — an async pipeline of **9 composable engines** with Compression Studios, an LLMLingua-2 ONNX engine and a heuristic/SLM two-tier **Ultra**, RTK, delegated Anthropic Context Editing, **Output Styles** (output-axis steering: terse-prose / less-code / terse-CJK), an **adaptive context-budget dial** (escalate only as far as needed to fit the context window), per-request `x-omniroute-compression` control, an opt-in offline eval harness, one-click **Headroom** proxy lifecycle management from the dashboard (Docker sidecar supported), a synthetic **compression playground** (Play lanes + A/B Compare with USD-capped fidelity verdicts), an opt-in **per-step fidelity gate** that rejects a lossy engine before it degrades the prompt, a **best-of-N candidate encoder** (GCF vs TOON — keep whichever is shorter, with an A/B bytes/token table in the studio), **CCR ranged/grep/stats retrieval** (pull an exact byte/line slice or summary of a stored block instead of re-expanding it), and a unified panel with named profiles + an active-profile selector. → [Compression](docs/compression/COMPRESSION_ENGINES.md)
Expand All @@ -317,7 +317,6 @@ Result: 4 layers of fallback = zero downtime
<tr>
<td align="center" width="120"><a href="https://github.com/anthropics/claude-code"><img src="./public/providers/claude.svg" width="52" alt="Claude Code"/><br/><b>Claude Code</b></a></td>
<td align="center" width="120"><a href="https://github.com/openai/codex"><img src="./public/providers/codex.svg" width="52" alt="Codex CLI"/><br/><b>Codex CLI</b></a></td>
<td align="center" width="120"><a href="https://github.com/google-gemini/gemini-cli"><img src="./public/providers/gemini-cli.svg" width="52" alt="Gemini CLI"/><br/><b>Gemini CLI</b></a></td>
<td align="center" width="120"><img src="./public/providers/cursor.png" width="52" alt="Cursor"/><br/><b>Cursor</b></td>
<td align="center" width="120"><img src="./public/providers/copilot.png" width="52" alt="Copilot"/><br/><b>Copilot</b></td>
<td align="center" width="120"><img src="./public/providers/continue.png" width="52" alt="Continue"/><br/><b>Continue</b></td>
Expand Down Expand Up @@ -364,7 +363,6 @@ Result: 4 layers of fallback = zero downtime
</tr>
<tr>
<td align="center" width="150"><img src="https://img.shields.io/badge/Cloudflare_AI-F38020?style=flat-square&logo=cloudflare&logoColor=white" alt="Cloudflare AI"/><br/><sub>50+ models<br/>10K neurons/day</sub></td>
<td align="center" width="150"><img src="https://img.shields.io/badge/Gemini_CLI-8E75B2?style=flat-square&logo=googlegemini&logoColor=white" alt="Gemini CLI"/><br/><sub>gemini-3-flash<br/>180K/mo free</sub></td>
<td align="center" width="150"><img src="https://img.shields.io/badge/NVIDIA_NIM-76B900?style=flat-square&logo=nvidia&logoColor=white" alt="NVIDIA NIM"/><br/><sub>129 models<br/>~40 RPM free</sub></td>
<td align="center" width="150"><img src="https://img.shields.io/badge/Cerebras-F15A29?style=flat-square" alt="Cerebras"/><br/><sub>Qwen3 235B<br/>1M tokens/day</sub></td>
</tr>
Expand Down
2 changes: 0 additions & 2 deletions bin/cli/commands/registry.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ import { registerSetupCrush } from "./setup-crush.mjs";
import { registerSetupGoose } from "./setup-goose.mjs";
import { registerSetupQwen } from "./setup-qwen.mjs";
import { registerSetupAider } from "./setup-aider.mjs";
import { registerSetupGemini } from "./setup-gemini.mjs";
import { registerConnect } from "./connect.mjs";
import { registerContexts } from "./contexts.mjs";
import { registerTokens } from "./tokens.mjs";
Expand Down Expand Up @@ -154,7 +153,6 @@ export function registerCommands(program) {
registerSetupGoose(program);
registerSetupQwen(program);
registerSetupAider(program);
registerSetupGemini(program);
registerConnect(program);
registerContexts(program);
registerTokens(program);
Expand Down
Loading
Loading