Compare commits

..

4 Commits

Author SHA1 Message Date
Aman
bcbaa436ba fix(auth): reduce file size
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
2026-08-09 08:34:17 -03:00
Aman
abcf6f8128 chore: preserve Stryker config formatting 2026-08-09 02:21:38 -03:00
Aman
154f773a28 test: register NVIDIA 410 regression for mutation coverage 2026-08-09 02:21:38 -03:00
Zartharas
be583b5392 fix(nvidia): keep 410 failures model-scoped 2026-08-09 02:21:38 -03:00
1725 changed files with 48272 additions and 184402 deletions

View File

@@ -0,0 +1 @@
- **docs:** add management authentication terminology guide ([#7786](https://github.com/diegosouzapw/OmniRoute/issues/7786))

View File

@@ -0,0 +1,41 @@
# Management Authentication
OmniRoute uses four distinct credential families for management access. This guide
distinguishes them by purpose, scope, and locality.
| Credential | Scope | Locality | Use Case |
|-------------------------|--------------------|---------------|-----------------------------------|
| Dashboard JWT session | Full management | Localhost | Web dashboard login |
| CLI machine-id token | Full management | Per-machine | `omniroute` CLI commands |
| Scoped `oma_` token | Configurable scope | External | Automation / CI / API access |
| Manage-scope API key | `manage` scope | External | Management API calls |
## Dashboard JWT Session
Generated on dashboard login (`/api/auth/login`). Stored in HTTP-only cookie.
Valid for the session duration. Cannot be used from external hosts.
## CLI Machine-ID Token
Created by `omniroute auth login` on first use. Stored in `~/.omniroute/auth.json`.
Used by the CLI for all management operations. Tied to the machine identity.
## Scoped `oma_` Access Token
Created via dashboard or CLI with configurable scopes (e.g., `manage`, `read`).
Format: `oma_<random-hex>`. Used for programmatic access from external systems.
## Manage-Scope API Key
Standard API key with the `manage` scope enabled. Created in dashboard API Keys page.
Used for management API calls from external hosts.
## Header Examples
```
Authorization: Bearer oma_abc123def456
Authorization: Bearer <standard-api-key-with-manage-scope>
Cookie: omniroute_session=<jwt-token>
```
See `docs/reference/API_REFERENCE.md` for endpoint-specific auth requirements.

View File

@@ -0,0 +1,27 @@
import { describe, it } from "node:test";
import { ok } from "node:assert/strict";
import { readFileSync } from "node:fs";
describe("Management auth documentation (#7786)", () => {
const docPath = "docs/guides/MANAGEMENT-AUTH.md";
const content = readFileSync(docPath, "utf-8");
it("exists and has content", () => {
ok(content.length > 500, "should have substantial content");
ok(content.includes("Dashboard JWT session"));
ok(content.includes("CLI machine-id token"));
ok(content.includes("oma_"));
});
it("documents all four credential families", () => {
const families = ["Dashboard JWT", "CLI machine-id", "oma_", "Manage-scope"];
for (const f of families) {
ok(content.includes(f), `should document ${f}`);
}
});
it("mentions relevant auth header examples", () => {
ok(content.includes("Authorization"));
ok(content.includes("Bearer"));
});
});

View File

@@ -0,0 +1 @@
- **feat(infra):** add systemd autostart unit for Linux ([#8635](https://github.com/diegosouzapw/OmniRoute/issues/8635))

View File

@@ -0,0 +1,19 @@
[Unit]
Description=OmniRoute AI Proxy
After=network.target network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=$(which omniroute) start
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
# Security hardening
NoNewPrivileges=true
ProtectSystem=full
PrivateTmp=true
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,23 @@
import { describe, it } from "node:test";
import { ok } from "node:assert/strict";
import { readFileSync, existsSync } from "node:fs";
describe("Systemd autostart (#8635)", () => {
const svcPath = "contrib/systemd/omniroute.service";
const content = readFileSync(svcPath, "utf-8");
it("service file exists", () => {
ok(existsSync(svcPath));
ok(content.length > 200);
});
it("defines required systemd sections", () => {
ok(content.includes("[Unit]"));
ok(content.includes("[Service]"));
ok(content.includes("[Install]"));
});
it("specifies WantedBy=default.target", () => {
ok(content.includes("WantedBy=default.target"));
});
});

View File

@@ -53,10 +53,8 @@ reports/mutation
# Local caches and quality-gate artifacts (all gitignored). `_*` does not match
# dot-prefixed names, so these need explicit entries.
.artifacts
.eslintcache*
.fakebin-*
MAX
quality-ratchet/
.eslintcache
.eslintcache-complexity
# Documentation
# Issue #2348: The Dashboard Docs viewer reads markdown from `/app/docs` at

View File

@@ -350,18 +350,6 @@ ALLOW_API_KEY_REVEAL=false
# by OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT and the heap-pressure shed instead. Set a positive
# value only on memory-constrained deployments that need a hard ceiling.
# OMNIROUTE_CHAT_HARD_MAX_MESSAGES=0
# How long a heavy request waits for heavyweight capacity before a retryable 503.
# A short bounded wait serializes agent bursts instead of an instant 503; 0 = instant.
# Default 2000 (2s).
# OMNIROUTE_CHAT_ADMISSION_QUEUE_MS=2000
# Queued-bytes budget for the admission wait: bounds total buffered body bytes parked
# per lane so the wait cannot amplify the heap (#4380). Over-budget waits 503 immediately.
# Default 4194304 (4 MB).
# OMNIROUTE_CHAT_ADMISSION_MAX_QUEUED_BYTES=4194304
# Per-connection virtual admission lanes (#9654): idle-lane eviction TTL. Default 60000 (60s).
# OMNIROUTE_CHAT_VIRTUAL_TTL_MS=60000
# Per-connection virtual admission lanes (#9654): max concurrent sessions (lanes). Default 64.
# OMNIROUTE_CHAT_VIRTUAL_MAX_SESSIONS=64
# Hard cap (bytes) for a non-streaming upstream response buffered fully into memory
# (#5152). Past this the upstream reader is cancelled and the request fails fast
@@ -656,9 +644,6 @@ NEXT_PUBLIC_ENABLE_SOCKS5_PROXY=true
# Reduces risk of JA3/JA4 fingerprint-based blocking by providers (e.g., Google).
# Used by: open-sse/executors — replaces Node.js default TLS fingerprint.
# ENABLE_TLS_FINGERPRINT=true
# New proxied TLS routing requires an explicit, comma-separated provider allowlist.
# Direct TLS keeps its legacy behavior when this is unset.
# TLS_FINGERPRINT_PROVIDERS=codex,openai
# Allow the Claude Turnstile Playwright browser context to ignore HTTPS certificate errors.
# Only enable for local debugging or trusted MITM/corporate proxy environments.
@@ -977,17 +962,18 @@ CODEX_OAUTH_CLIENT_ID=app_EMoamEEZ73f0CkXaXp7hrann
# Used by: open-sse/executors/theoldllm.ts. Default: 30000 (30s).
# THEOLDLLM_NAV_TIMEOUT_MS=30000
# ── Gemini / Antigravity (Google-based) ──
# These providers ship public OAuth client_id/secret values embedded in their
# public CLIs. Defaults are baked into the code via
# open-sse/utils/publicCreds.ts — leave the env vars unset to use them. Only
# set these if you registered your own OAuth app and want to use your own
# credentials instead. See docs/security/PUBLIC_CREDS.md for context.
# ── 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
# them. Only set these if you registered your own OAuth app and want to use
# your own credentials instead. See docs/security/PUBLIC_CREDS.md for context.
#
# GEMINI_OAUTH_CLIENT_ID=
# GEMINI_OAUTH_CLIENT_SECRET=
# ANTIGRAVITY_OAUTH_CLIENT_ID=
# ANTIGRAVITY_OAUTH_CLIENT_SECRET=
# WINDSURF_FIREBASE_API_KEY=
# ── Kimi Coding (Moonshot) ──
KIMI_CODING_OAUTH_CLIENT_ID=17e5f671-d194-4dfb-9706-5516cb48c098
@@ -1172,12 +1158,6 @@ CURSOR_USER_AGENT="Cursor/3.4"
# Or enable for all providers at once:
# CLI_COMPAT_ALL=1
# Allow the Antigravity request translator to skip its strict CLI request-signature
# validation when the upstream refuses real signatures (debug/antiquated-CLI mode).
# Default: real signatures enforced (unset) — signature bypass disabled.
# Used by: open-sse/translator/request/openai-to-gemini.ts
# ANTIGRAVITY_ALLOW_SIGNATURE_BYPASS=0
# ── Kimi Coding CLI identity overrides ──
# Used by: src/lib/oauth/providers/kimi-coding.ts — sent in OAuth + API headers.
# Leave unset to use the captured defaults baked into the OmniRoute build.
@@ -1434,7 +1414,7 @@ APP_LOG_TO_FILE=true
# Whether call log pipeline capture stores stream chunks when enabled in settings.
# Only applies when call_log_pipeline_enabled=true.
# Default: false (opt-in — saves disk: stream chunks are the biggest call-log artifact)
# Default: true
# CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS=true
# Maximum call log artifact size for pipeline captures, in KB.
@@ -1446,7 +1426,7 @@ APP_LOG_TO_FILE=true
# bodies is retained in the database.
# Used by: open-sse/handlers/chatCore.ts — cloneBoundedChatLogPayload()
# CHAT_LOG_TEXT_LIMIT=65536 # Max string length before truncation (default: 64 KB)
# CHAT_LOG_ARRAY_TAIL_ITEMS=128 # Number of array items retained from tail (default: 128)
# CHAT_LOG_ARRAY_TAIL_ITEMS=24 # Number of array items retained from tail (default: 24)
# CHAT_LOG_MAX_DEPTH=6 # Max nesting depth before truncation (default: 6)
# CHAT_LOG_MAX_OBJECT_KEYS=80 # Max object keys retained (default: 80, 0 = no limit)
@@ -1585,17 +1565,6 @@ APP_LOG_TO_FILE=true
# 20. PROVIDER-SPECIFIC SETTINGS
# ═══════════════════════════════════════════════════════════════════════════════
# ── Strict system-message-first providers ──
# Comma-separated, case-insensitive provider ids that require the `system`
# role message to be the first message (any later `system` message is
# rejected with HTTP 400 by the upstream chat template) — the same
# constraint documented for xiaomi-mimo/mimo (#6135, #7293). Extends the
# built-in list without a source change; useful for self-hosted connections
# in front of Qwen3.5+/3.6 or other strict-template backends.
# Used by: src/lib/memory/injection.ts::systemMessageMustBeFirst
# Default: unset (only xiaomi-mimo/mimo are flagged)
# OMNIROUTE_STRICT_SYSTEM_PROVIDERS=coding-agent
# ── OpenRouter ──
# OpenRouter model catalog cache TTL in ms.
# Used by: src/lib/catalog/openrouterCatalog.ts
@@ -1624,19 +1593,6 @@ APP_LOG_TO_FILE=true
# NANOBANANA_POLL_TIMEOUT_MS=120000 # Max wait for job completion (default: 120s)
# NANOBANANA_POLL_INTERVAL_MS=2500 # Poll frequency (default: 2.5s)
# ── Adobe Firefly (Image / Video Generation) ──
# Optional absolute path to a system Chrome or Edge executable used for interactive sign-in
# and off-screen risk-session renewal. Auto-detected when unset.
# OMNIROUTE_LOGIN_BROWSER_PATH=
# Browser renewal and durable session cache are enabled by default; set either to 0 to opt out.
# ADOBE_FIREFLY_BROWSER_REFRESH=1
# ADOBE_FIREFLY_SESSION_DISK=1
# Minimum gap between generate submissions and extra gap after every third success (ms).
# ADOBE_FIREFLY_MIN_SUBMIT_GAP_MS=12000
# ADOBE_FIREFLY_BATCH_EXTRA_GAP_MS=15000
# Base backoff after a transient 408 response (ms); five attempts maximum.
# ADOBE_FIREFLY_SUBMIT_BASE_DELAY_MS=8000
# ── Microsoft Designer Web (Image Generation) ──
# Polling config for the microsoft-designer-web submit-then-poll image job.
# Used by: open-sse/handlers/imageGeneration/providers/designerWeb.ts
@@ -1866,17 +1822,6 @@ APP_LOG_TO_FILE=true
# Accepted values: true|1|on (enable). Unset or anything else = disabled (default).
# STREAM_RECOVERY_MIDSTREAM_ENABLED=true
# Active-stream throughput watchdog (#9709). Detects streams that keep sending
# heartbeats/chunks but produce too little useful assistant text. Separate from
# STREAM_IDLE_TIMEOUT_MS (silence) and the hard upstream attempt deadline. OFF by
# default. Tool-call/reasoning phases suspend judgement; post-commit streams are
# never blindly replayed.
# STREAM_THROUGHPUT_WATCHDOG_ENABLED=true
# STREAM_THROUGHPUT_WATCHDOG_WARMUP_MS=30000
# STREAM_THROUGHPUT_WATCHDOG_WINDOW_MS=30000
# STREAM_THROUGHPUT_WATCHDOG_MIN_BYTES_PER_SECOND=4
# STREAM_THROUGHPUT_WATCHDOG_MIN_USEFUL_BYTES=1
# Stagger interval (ms) between provider token healthchecks at startup.
# Used by: src/lib/tokenHealthCheck.ts. Default: 3000.
# HEALTHCHECK_STAGGER_MS=3000
@@ -1948,7 +1893,7 @@ APP_LOG_TO_FILE=true
# Log request shape (content-type + content-length) for large chat payloads.
# Used by: src/app/api/v1/chat/completions/route.ts. Set to "0" to silence.
# Default: disabled (opt-in).
# Default: enabled.
# OMNIROUTE_LOG_REQUEST_SHAPE=1
# Write raw (untruncated) request/response JSON in call log artifacts.
@@ -1990,19 +1935,6 @@ APP_LOG_TO_FILE=true
# ALIBABA_CODING_PLAN_HOST=
# ALIBABA_CODING_PLAN_QUOTA_URL=
# ── Alibaba Model Studio free-tier quota sync ──
# Console front-end path overrides for the free-tier quota fetcher. Used by:
# open-sse/services/alibabaFreeTierQuotaFetcher.ts. When unset, the fetcher
# uses the production Bailian console paths.
# ALIBABA_FREE_TIER_VISION_FE_PATH=
# ALIBABA_FREE_TIER_MULTIMODAL_FE_PATH=
# ALIBABA_FREE_TIER_AUDIO_FE_PATH=
# Optional path to a local JSON override for the built-in text free-tier
# allowlist. Used by: open-sse/services/alibabaFreeTierAllowlist.ts. When
# unset, the fetcher falls back to $DATA_DIR/alibaba-free-tier-allowlist.json
# then config/alibaba-free-tier-allowlist.json.
# ALIBABA_FREE_TIER_ALLOWLIST_PATH=
# ── Context window tuning ──
# Tokens reserved for completion output when computing prompt budgets.
# Used by: open-sse/services/contextManager.ts. Default: 1024.
@@ -2033,13 +1965,6 @@ APP_LOG_TO_FILE=true
# DEVIN_BRIDGE_HAIKU_MODEL=devin-cli-agentic/swe-1-7
# DEVIN_BRIDGE_SUBAGENT_MODEL=devin-cli-agentic/swe-1-7
# ── Devin Desktop upstream compatibility versions ──
# Desktop ide_version. Must use x.y.z format; invalid/unset values use 3.6.27.
# DEVIN_DESKTOP_VERSION=3.6.27
# Bundled Codeium/language-server extension_version, distinct from Desktop.
# Must use x.y.z format; invalid/unset values use the bundled default 1.48.2.
# DEVIN_DESKTOP_EXTENSION_VERSION=1.48.2
# ── Command Code (custom CLI) callback ──
# Local port used for OAuth-style callbacks from the Command Code CLI helper.
# Used by: src/app/api/providers/command-code/auth/shared.ts.
@@ -2052,12 +1977,6 @@ APP_LOG_TO_FILE=true
# Default: 0.33.2
# COMMAND_CODE_VERSION=0.33.2
# Base URL for the Command Code usage/quota upstream, used by smartphone
# quota-fetcher telemetry.
# Used by: open-sse/services/usage/command-code.ts
# Default: https://api.commandcode.ai
# COMMANDCODE_API_URL=https://api.commandcode.ai
# ── MITM debug proxy (development only) ──
# Used by: src/mitm/server.cjs — captures upstream traffic for inspection.
# MITM_LOCAL_PORT=443
@@ -2534,18 +2453,6 @@ QUOTA_STORE_DRIVER=sqlite # sqlite | redis
# ─────────────────────────────────────────────────────────────────────────────
# HYPERAGENT_USAGE_URL=https://hyperagent.com/api/settings/billing/usage
# ─────────────────────────────────────────────────────────────────────────────
# ChatGPT Web (Codex) headless browser and outbound tool tunnel
# Used by: open-sse/executors/chatgpt-web-codex.ts
# Connection values entered in the dashboard override these global defaults.
# ─────────────────────────────────────────────────────────────────────────────
# CHATGPT_WEB_CODEX_CHROME_PATH=/usr/bin/chromium
# CHROME_PATH=/usr/bin/chromium
# CHATGPT_WEB_CODEX_CDP_URL=http://chatgpt-web-codex-browser:9223
# CHATGPT_WEB_CODEX_TUNNEL_ID=tunnel_0123456789abcdef0123456789abcdef
# CHATGPT_WEB_CODEX_RUNTIME_KEY=
# CHATGPT_WEB_CODEX_CONNECTOR_NAME=OmniRoute Codex
# ─────────────────────────────────────────────────────────────────────────────
# Browser-login VNC sessions (optional — src/lib/vncSession/manifest.ts)
# Containerized Chromium+VNC used for interactive browser-login credential
@@ -2607,56 +2514,3 @@ QUOTA_STORE_DRIVER=sqlite # sqlite | redis
# URL the dashboard's "Support the project" button opens (payment/plans
# page). No pricing/value lives in this repo — only the link.
# RADAR_SUPPORTER_PLANS_URL=https://radar.omniroute.online/planos
# ═══════════════════════════════════════════════════════════════════════════════
# 27. RELEASE v3.8.50 ADDITIONS
# ═══════════════════════════════════════════════════════════════════════════════
# Heavy chat admission queue wait before returning retryable 503. Set 0 for the
# legacy immediate rejection. Used by: src/shared/middleware/chatBodyAdmission.ts.
# Default: 5000 (5 seconds)
# OMNIROUTE_CHAT_ADMISSION_QUEUE_MS=5000
# Timeout for /api/jobs/:id/run-now while it waits for an in-flight run.
# Used by: src/app/api/jobs/[id]/run-now/route.ts. Default: 30000 (30 seconds)
# OMNIROUTE_RUNNOW_TIMEOUT_MS=30000
# Maximum request/response body size before chat-log summarization, in KiB.
# Used by: src/lib/chatLogTruncation.ts. Default: 1024
# CHAT_LOG_MAX_BODY_KB=1024
# Adobe Firefly browser renewal and durable session cache (enabled by default).
# Used by: open-sse/services/adobeFireflySession.ts.
# ADOBE_FIREFLY_BROWSER_REFRESH=1
# ADOBE_FIREFLY_SESSION_DISK=1
# Minimum spacing between submissions and the extra pause after every third success.
# ADOBE_FIREFLY_MIN_SUBMIT_GAP_MS=12000
# ADOBE_FIREFLY_BATCH_EXTRA_GAP_MS=15000
# Chrome CDP runtime used by Adobe Firefly renewal. True headless is debug-only:
# Adobe colligo normally rejects risk tokens minted without a headed browser.
# ADOBE_FIREFLY_CHROME_CDP_PORT=9334
# ADOBE_FIREFLY_CHROME_VISIBLE=0
# ADOBE_FIREFLY_CHROME_HEADLESS=0
# ADOBE_FIREFLY_CHROME_FORCE_RESTART=0
# ADOBE_FIREFLY_CHROME_PING=auto
# ADOBE_FIREFLY_LOGIN_WAIT_MS=0
# ADOBE_FIREFLY_FORTER_WAIT_MS=45000
# Optional absolute Chrome executable; auto-detected when unset.
# CHROME_PATH=
# Telegram Mini App bridge. The update endpoint remains disabled while the bot
# token is unset. Used by: src/lib/telegram/* and src/app/api/telegram/update/route.ts.
# TELEGRAM_BOT_TOKEN=
# TELEGRAM_DEFAULT_MODEL=auto/chat
# TELEGRAM_BOT_API_BASE=https://api.telegram.org
# TELEGRAM_WEBHOOK_TIMEOUT_MS=60000
# ── OmniConductor bridge (Conductor PRD RF1) ──────────────────────────────────
# Mirrors the OmniConductor hub's tasks into the local A2A TaskManager via SSE.
# Opt-in: the bridge only starts when CONDUCTOR_HUB_URL is set.
# Token: emit a `spokesperson`-kind credential on the hub (POST /v1/peers, admin) —
# server-side only, never exposed to the browser.
# Used by: src/lib/conductor/boot.ts, src/lib/conductor/bridge.ts
# CONDUCTOR_HUB_URL=http://127.0.0.1:7910
# CONDUCTOR_HUB_TOKEN=
feat/conductor-bridge

1
.eslintcache-probe Normal file

File diff suppressed because one or more lines are too long

4
.fakebin-9475/npm Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env bash
if [ "$1" = "view" ]; then echo "3.8.99"; exit 0; fi
if [ "$1" = "install" ]; then echo "added 1 package"; exit 0; fi
exit 0

View File

@@ -10,7 +10,7 @@
## Validation
Choose the change type and focused loop from the
[Contribution Golden Path](../docs/ops/CONTRIBUTION_GOLDEN_PATH.md). The full unit suite,
[Contribution Golden Path](../docs/dev/CONTRIBUTION_GOLDEN_PATH.md). The full unit suite,
Vitest, the 60% coverage gate, and the production build all run in CI on this PR (#8329):
- [ ] Change type: provider / routing / UI / i18n / CLI / DB / build-deploy / other

View File

@@ -501,13 +501,11 @@ jobs:
BASE_REF: ${{ github.base_ref && format('origin/{0}', github.base_ref) || '' }}
run: node scripts/i18n/check-ui-value-drift.mjs
# #8038: cheap glossary/protected-terms consistency gate —
# #8038: cheap single-locale glossary/protected-terms consistency gate —
# complements i18n-ui-coverage (key parity) and the ICU `i18n` job below
# without needing app-boot/Playwright infra. Same gating as i18n-ui-coverage.
# ko added after the #8224 ko.json mistranslation cleanup so the fixed
# terminology cannot silently regress on the next machine-translation run.
i18n-glossary-zhcn:
name: i18n Glossary (zh-CN, ko)
name: i18n Glossary (zh-CN)
runs-on: ubuntu-latest
needs: changes
if: ${{ github.event_name != 'pull_request' || (github.event.pull_request.draft == false && (needs.changes.outputs.i18n == 'true' || needs.changes.outputs.code == 'true')) }}
@@ -813,12 +811,7 @@ jobs:
test-bun-sqlite:
name: Bun SQLite Compatibility
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.os == 'windows-latest' }}
runs-on: ubuntu-latest
timeout-minutes: 10
needs: changes
if: ${{ github.event_name != 'pull_request' || (needs.changes.outputs.code == 'true' && github.event.pull_request.draft == false) }}
@@ -831,15 +824,6 @@ jobs:
node-version: ${{ env.CI_NODE_VERSION }}
cache: npm
- uses: ./.github/actions/npm-ci-retry
- name: Install Bun (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
powershell -c "iwr bun.sh/install.ps1 -useb | iex"
echo "$env:USERPROFILE\.bun\bin" | Out-File -FilePath $env:GITHUB_PATH -Append
- name: Install Bun (non-Windows)
if: runner.os != 'Windows'
run: npm install -g bun
- run: npm run test:bun:db
test-vitest:
@@ -1336,7 +1320,7 @@ jobs:
echo "| Lint | $(status '${{ needs.lint.result }}') |" >> "$GITHUB_STEP_SUMMARY"
echo "| Docs Sync (Strict) | $(status '${{ needs.docs-sync-strict.result }}') |" >> "$GITHUB_STEP_SUMMARY"
echo "| i18n UI Coverage | $(status '${{ needs.i18n-ui-coverage.result }}') |" >> "$GITHUB_STEP_SUMMARY"
echo "| i18n Glossary (zh-CN, ko) | $(status '${{ needs.i18n-glossary-zhcn.result }}') |" >> "$GITHUB_STEP_SUMMARY"
echo "| i18n Glossary (zh-CN) | $(status '${{ needs.i18n-glossary-zhcn.result }}') |" >> "$GITHUB_STEP_SUMMARY"
echo "| PR Test Policy | $(status '${{ needs.pr-test-policy.result }}') |" >> "$GITHUB_STEP_SUMMARY"
echo "| SonarQube | $(status '${{ needs.sonarqube.result }}') |" >> "$GITHUB_STEP_SUMMARY"

View File

@@ -194,25 +194,6 @@ jobs:
"$HOME/.local/bin/osv-scanner" --version || true
"$HOME/.local/bin/oasdiff" --version || true
zizmor --version || true
- name: Forgotten sibling tests (advisory)
env:
GITHUB_BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
node scripts/quality/build-test-impact-map.mjs
node scripts/check/check-forgotten-sibling-tests.mjs \
--summary-file forgotten-sibling-tests.md \
--json-file forgotten-sibling-tests.json
cat forgotten-sibling-tests.md >> "$GITHUB_STEP_SUMMARY"
- name: Upload forgotten sibling report
if: always()
uses: actions/upload-artifact@v7
with:
name: forgotten-sibling-tests
path: |
forgotten-sibling-tests.md
forgotten-sibling-tests.json
if-no-files-found: ignore
retention-days: 30
# Quality gates (all, non-fail-fast) — #8542: replaces 17 bare check:* steps,
# 6 G0 gates, 4 ratchet gates, and 3 typecheck steps with a single aggregation
# step. Each gate runs in a loop with ::group::; failures are collected and
@@ -275,22 +256,15 @@ jobs:
# arrives in 7.1, so typescript-eslint / type-coverage / Stryker stay on 6.x
# (the hybrid is the officially documented pattern). Isolated npx on purpose:
# installing an alias package could collide node_modules/.bin/tsc with 6.x.
# The full result stays advisory while #8484 has a backlog. The blocking
# base-relative ratchet immediately below rejects only diagnostics added by
# the PR, so existing release debt does not block unrelated work.
# Promote to the blocking gate after ~1 week of parity with the step above.
- name: Typecheck (core) — TS7 native shadow (advisory)
continue-on-error: true
run: |
RC=0
START=$(date +%s)
npm exec --yes --package=typescript@7.0.2 -- tsc --pretty false -p tsconfig.typecheck-core.json || RC=$?
npx -y -p typescript@7 tsc --pretty false -p tsconfig.typecheck-core.json || RC=$?
echo "[ts7-shadow] exit=$RC elapsed=$(( $(date +%s) - START ))s — the 6.x step above stays authoritative"
exit $RC
- name: Typecheck (core) — TS7 zero-new-diagnostics ratchet
if: ${{ github.event_name == 'pull_request' }}
env:
TS7_BASE_REF: ${{ github.event.pull_request.base.sha }}
run: npm run check:ts7-diagnostics-ratchet -- --base-ref "$TS7_BASE_REF"
# TIA: build the impact map at runtime (gitignored, ~21MB) and run only the
# unit tests impacted by this PR's changed files. On hub/unmapped changes the
# selector returns __RUN_ALL__ — full-suite authority is the parallel
@@ -305,8 +279,7 @@ jobs:
GITHUB_BASE_REF: ${{ github.base_ref }}
run: |
git fetch --no-tags origin "$GITHUB_BASE_REF" || true
# The advisory sibling-test step generates the same map earlier in this job.
[ -f config/quality/test-impact-map.json ] || node scripts/quality/build-test-impact-map.mjs
node scripts/quality/build-test-impact-map.mjs
SEL="$(node scripts/quality/select-impacted-tests.mjs)"
# Shadow evidence (#8084): persist every selection so TIA false negatives can
# be measured against fast-unit's full-suite verdict across releases BEFORE

72
.gitignore vendored
View File

@@ -1,7 +1,6 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# project-specific directories
.slim/deepwork/
.omnivscodeagent/
omnirouteCloud/
omnirouteSite/
@@ -18,7 +17,7 @@ _tasks/
.logs/**
.tests/**
.coverage/**
/coverage/
coverage/
.dist/**
.next/**
.build/**
@@ -44,7 +43,6 @@ memory-bank/
# Root-level underscore-prefixed directories (private/draft — never commit)
/_*/
/_*
# Draft features documentation (internal only)
docs/new-features/
@@ -58,6 +56,10 @@ node_modules/
*.map
.DS_Store
# Obsidian sync plugin — committed for community distribution
!obsidian-plugin/
obsidian-plugin/node_modules/
# Serena AI assistant config (local-only tool, not project code)
.serena/
@@ -70,8 +72,8 @@ yarn-error.log*
# env files (can opt-in for committing if needed)
.env*
!.env.example
!.env.homolog.example
!.env.devin-bridge.example
!.env.homolog.example
# Provider API keys (never commit)
*.api-key
.nvidia-api-key
@@ -84,7 +86,7 @@ yarn-error.log*
next-env.d.ts
# data and logs
/data/
data/
.data/
logs/*
test_output.log
@@ -106,7 +108,7 @@ open-sse/test/*
test-results/
playwright-report/
blob-report/
/cloud/
cloud/
.tmp/
# Security Analysis (standalone project with own git)
@@ -120,8 +122,6 @@ app.log
deploy.sh
docker-compose.minimal.yml
# Docker Compose override (local-only, never commit)
docker-compose.override.yml
# Backup directories
app.__qa_backup/
@@ -157,7 +157,6 @@ vscode-extension/
# Empty/dangling files
typescript
/MAX
# Gemini Antigravity agent data
.gemini/
@@ -201,16 +200,18 @@ scripts/i18n/_pending-keys.json
.claude/worktrees/
.codegraph/
# Test executable shims belong in the OS temporary directory, not the repository root
/.fakebin-*/
# Fumadocs generated source
.source/
/.source/
# Temporary local worktrees used to build unpublished npm tarballs
/.deploy-build-*/
# AI agent local settings and configs
.agents/
.antigravitycli/
/.claude/
.claude/
!tests/fixtures/devin-bridge/e2e-workspace/.claude/
!tests/fixtures/devin-bridge/e2e-workspace/.claude/**
# PR Reviews and local feedback files
pr_reviews*.json
@@ -225,26 +226,6 @@ CODEX-SETUP-PROMPT.md
# Quality ratchet — métricas efêmeras (baseline commitado em config/quality/; métricas não)
config/quality/quality-metrics.json
# Electron desktop build output unpacked into the repo root.
# `electron-builder` (squirrel-windows target) unpacks the packaged app — the
# entire Chromium runtime, ~24k files — directly into the repository root.
# Every rule below is ROOT-ANCHORED (leading `/`) on purpose: a bare `locales/`
# or `resources/` would also swallow tracked sources such as the CLI
# translations in `bin/cli/locales/*.json`.
/OmniRoute.exe
/Uninstall OmniRoute.exe
/uninstallerIcon.ico
/locales/
/resources/
/*.pak
/*.dll
/icudtl.dat
/snapshot_blob.bin
/v8_context_snapshot.bin
/vk_swiftshader_icd.json
/LICENSE.electron.txt
/LICENSES.chromium.html
# Runtime logs (diretório local, nunca versionado)
/logs/
-home-diegosouzapw-dev-automações-bots-yt-downloader-20260504 .txt
@@ -257,19 +238,21 @@ omniroute.md
# mise configuration
mise.toml
_artifacts/ # release-green artifacts
# release-green artifacts (.gitignore has no inline comments — a trailing
# `# ...` becomes part of the pattern, so it must sit on its own line).
# Already covered by /_*/ above; kept explicit for discoverability.
_artifacts/
.claude-flow/
# ESLint file cache (npm run lint --cache / complexity ratchets)
.eslintcache
.eslintcache-complexity
/.eslintcache-*
# CI/local quality artifacts (eslint-results.json, quality-ratchet.md, etc.)
.artifacts/
/perf-audit*.md
/quality-ratchet/
# Isolated Devin bridge workspaces, evidence, and test databases
.sandbox/
# Homologation E2E suite (npm run homolog) — real-environment credentials + report output
.env.homolog
@@ -277,11 +260,12 @@ tests/homolog/.auth/
tests/homolog/ui/.auth/
homolog-report/
docker-compose.yml.bak
.playwright-cli/
# Playwright screenshot/log output. Today every artifact happens to land inside
# output/**/.playwright-cli/ (covered above), but anything written directly to
# output/ would otherwise show up as untracked.
/output/
# _tasks e um repo git SEPARADO (ver AGENTS.md). A linha _tasks/ (com barra) NAO
# ignora um SYMLINK chamado _tasks; /_tasks (ancorado) cobre arquivo/symlink/dir na raiz
# e impede que um git add -A recapture o symlink (incidente 2026-08-08).
# _tasks e um repo git SEPARADO (ver AGENTS.md). _tasks/ (com barra) NAO ignora um
# SYMLINK _tasks; /_tasks (ancorado) cobre symlink/dir na raiz (incidente 2026-08-08).
/_tasks
# CLI local cache/state
.playwright-cli

View File

@@ -94,10 +94,6 @@ vscode-extension/
/_*/
# Consistent with .gitignore and .dockerignore
.claude/
.fakebin-*
.eslintcache*
_tasks/
.DS_Store
.idea/
.config/

View File

@@ -1033,11 +1033,7 @@ export const OmniRoutePlugin: Plugin = async (_input, options) => {
// Config hook: keep existing catalog shim, and register slash command
// templates that ask the agent to call the force-sync tool (OpenCode has no
// Pi-style registerCommand API; tools + command templates are the native path).
const baseConfigHook = createOmniRouteConfigHook(resolved, {
cache: sharedCache,
diskSnapshotReader: defaultDiskSnapshotReader,
diskSnapshotWriter: defaultDiskSnapshotWriter,
});
const baseConfigHook = createOmniRouteConfigHook(resolved, { cache: sharedCache });
const configWithSyncCommand = async (input: Config) => {
await baseConfigHook(input);
const cfg = input as Config & {
@@ -4745,7 +4741,7 @@ export type OmniRouteDiskSnapshotWriter = (
export type OmniRouteDiskSnapshotReader = (
providerId: string,
identityFingerprint: string
) => Promise<(Omit<OmniRouteFetchCacheEntry, "expiresAt"> & { writtenAt?: number }) | undefined>;
) => Promise<Omit<OmniRouteFetchCacheEntry, "expiresAt"> | undefined>;
/**
* Bind a snapshot to the endpoint and effective credential tuple without
@@ -4828,36 +4824,15 @@ export const defaultDiskSnapshotReader: OmniRouteDiskSnapshotReader = async (
? parsed.rawCompressionCombos
: [],
rawConnections: Array.isArray(parsed.rawConnections) ? parsed.rawConnections : [],
writtenAt: typeof parsed.writtenAt === "number" ? parsed.writtenAt : undefined,
};
} catch {
return undefined;
}
};
/** No-op disk-cache pair — used by tests to avoid filesystem side effects.
* Also used as the default in createOmniRouteConfigHook so that tests
* that don't pass a diskSnapshotReader don't read real snapshot files
* from the user's ~/.local/share/opencode/plugins/ directory.
* The OmniRoutePlugin function passes the real defaultDiskSnapshotReader
* explicitly. */
export const noopDiskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
/** No-op disk-cache pair — used by tests to avoid filesystem side effects. */
export const noopDiskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
/**
* In-flight refresh guard: prevents concurrent refreshes for the same
* cacheKey. When a warm snapshot is served, the refresh runs detached; if
* a second hook invocation arrives before the refresh completes, it should
* piggyback on the in-flight promise rather than starting a second one.
* Cleared on settle so it doesn't leak.
*/
const _inflightRefresh: Map<string, Promise<void>> = new Map();
/** Reset the in-flight refresh guard (for test isolation). */
export function _resetInflightRefresh(): void {
_inflightRefresh.clear();
}
// ────────────────────────────────────────────────────────────────────────────
// Debug logging (features.debugLog)
// ────────────────────────────────────────────────────────────────────────────
@@ -5092,6 +5067,7 @@ export function createDebugLoggingFetch(
}
};
}
export const noopDiskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
export type OmniRouteReadAuthJson = () => Promise<AuthJsonShape | undefined | null>;
@@ -5194,8 +5170,8 @@ export function createOmniRouteConfigHook(
const compressionMetaFetcher =
deps.compressionMetaFetcher ?? defaultOmniRouteCompressionMetaFetcher;
const providersFetcher = deps.providersFetcher ?? defaultOmniRouteProvidersFetcher;
const diskSnapshotReader = deps.diskSnapshotReader ?? noopDiskSnapshotReader;
const diskSnapshotWriter = deps.diskSnapshotWriter ?? noopDiskSnapshotWriter;
const diskSnapshotReader = deps.diskSnapshotReader ?? defaultDiskSnapshotReader;
const diskSnapshotWriter = deps.diskSnapshotWriter ?? defaultDiskSnapshotWriter;
const now = deps.now ?? Date.now;
const cache: OmniRouteFetchCache = deps.cache ?? new Map();
const logger = deps.logger ?? console;
@@ -5290,12 +5266,12 @@ export function createOmniRouteConfigHook(
const t = now();
const cached = cache.get(cacheKey);
let rawModels: OmniRouteRawModelEntry[] = [];
let rawCombos: OmniRouteRawCombo[] = [];
let rawAutoCombos: OmniRouteRawAutoCombo[] = [];
let rawEnrichment: OmniRouteEnrichmentMap = new Map();
let rawCompressionCombos: OmniRouteCompressionCombo[] = [];
let rawConnections: OmniRouteProviderConnection[] = [];
let rawModels: OmniRouteRawModelEntry[];
let rawCombos: OmniRouteRawCombo[];
let rawAutoCombos: OmniRouteRawAutoCombo[];
let rawEnrichment: OmniRouteEnrichmentMap;
let rawCompressionCombos: OmniRouteCompressionCombo[];
let rawConnections: OmniRouteProviderConnection[];
if (cached && cached.expiresAt > t) {
rawModels = cached.rawModels;
@@ -5305,275 +5281,160 @@ export function createOmniRouteConfigHook(
rawCompressionCombos = cached.rawCompressionCombos;
rawConnections = cached.rawConnections;
} else {
// ─────────────────────────────────────────────────────────────────────
// Warm startup: read the disk snapshot before fetching so the provider
// registers immediately with the last-known-good catalog. The live
// fetch then refreshes in the background (detached) and updates the
// cache + snapshot. Gated by features.diskCache (default-on).
// ─────────────────────────────────────────────────────────────────────
let warmSnapshot: Omit<OmniRouteFetchCacheEntry, "expiresAt"> | undefined;
if (wantDiskCache) {
const snapshotResult = await diskSnapshotReader(resolved.providerId, snapshotFingerprint);
if (snapshotResult && snapshotResult.rawModels.length > 0) {
warmSnapshot = snapshotResult;
// Log snapshot age (accept any age — instant beats empty).
const age = (snapshotResult as { writtenAt?: number }).writtenAt;
const ageLabel = typeof age === "number" ? `${Math.round((Date.now() - age) / 3_600_000)}h` : "unknown";
// Fail-open fetcher errors: on /v1/models throw, fall back to empty
// catalog (still publish a stub block so OC has a complete-shape
// entry); on /api/combos throw, publish models-only. Disk-cache
// fallback below recovers the last-known-good catalog when the
// fetcher threw (network down / 403 / timeout) AND features.diskCache
// !== false. A 0-entry SUCCESS (fresh tenant) does NOT trigger
// disk fallback — that's a valid empty catalog.
let modelsFetchThrew = false;
try {
rawModels = await fetcher(baseURL, apiKey, 10_000);
} catch (err) {
logger.warn(
"[omniroute-plugin] config shim: /v1/models fetch failed; publishing stub provider entry",
err
);
rawModels = [];
modelsFetchThrew = true;
}
const modelsFetchOk = !modelsFetchThrew && rawModels.length > 0;
rawCombos = [];
try {
rawCombos = await combosFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
logger.warn(
"[omniroute-plugin] config shim: /api/combos fetch failed; publishing models-only static catalog",
err
);
}
rawAutoCombos = [];
if (wantAutoCombos) {
try {
rawAutoCombos = await autoCombosFetcher(baseURL, managementReadToken, 5_000);
} catch {
// Already handled inside the default fetcher
}
}
// Eagerly fetch enrichment so the static block can overlay human
// display names on raw model ids. On OC ≤1.15.5 the dynamic
// `provider.models` hook never fires in `serve` mode, so the static
// block IS what reaches `/provider` and the TUI model picker.
// Gated by `features.enrichment` (default-on). Soft-fail on error —
// we still publish a name-less catalog if /api/pricing/models is
// unreachable.
rawEnrichment = new Map();
if (wantEnrichment) {
try {
rawEnrichment = await enrichmentFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
logger.warn(
`[omniroute-plugin] config shim: warm startup from disk snapshot (${snapshotResult.rawModels.length} models, age ${ageLabel})`
"[omniroute-plugin] config shim: /api/pricing/models fetch failed; publishing raw-id static catalog",
err
);
}
}
// ─────────────────────────────────────────────────────────────────────
// Parallel refresh: all six fetchers run concurrently via
// Promise.allSettled. Each wrapper never rejects (catches internally)
// so partial failure is tolerated — same soft-fail semantics as the
// old sequential chain, but ~6x faster.
// ─────────────────────────────────────────────────────────────────────
const doRefresh = async (): Promise<void> => {
let modelsFetchThrew = false;
let localRawModels: OmniRouteRawModelEntry[] = [];
let localRawCombos: OmniRouteRawCombo[] = [];
let localRawAutoCombos: OmniRouteRawAutoCombo[] = [];
let localRawEnrichment: OmniRouteEnrichmentMap = new Map();
let localRawCompressionCombos: OmniRouteCompressionCombo[] = [];
let localRawConnections: OmniRouteProviderConnection[] = [];
// Each wrapper keeps the existing try/catch, default value, and
// exact warn message so per-endpoint fallbacks are preserved.
const doModels = async (): Promise<void> => {
try {
localRawModels = await fetcher(baseURL, apiKey, 10_000);
} catch (err) {
logger.warn(
"[omniroute-plugin] config shim: /v1/models fetch failed; publishing stub provider entry",
err
);
localRawModels = [];
modelsFetchThrew = true;
}
};
const doCombos = async (): Promise<void> => {
try {
localRawCombos = await combosFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
logger.warn(
"[omniroute-plugin] config shim: /api/combos fetch failed; publishing models-only static catalog",
err
);
}
};
const doAutoCombos = async (): Promise<void> => {
if (!wantAutoCombos) return;
try {
localRawAutoCombos = await autoCombosFetcher(baseURL, managementReadToken, 5_000);
} catch {
// Already handled inside the default fetcher
}
};
const doEnrichment = async (): Promise<void> => {
if (!wantEnrichment) return;
try {
localRawEnrichment = await enrichmentFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
logger.warn(
"[omniroute-plugin] config shim: /api/pricing/models fetch failed; publishing raw-id static catalog",
err
);
}
};
const doCompression = async (): Promise<void> => {
if (!wantCompressionMeta) return;
try {
localRawCompressionCombos = await compressionMetaFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
logger.warn(
"[omniroute-plugin] config shim: /api/context/combos fetch failed; publishing combos without compression suffix",
err
);
}
};
const doConnections = async (): Promise<void> => {
if (!wantUsableOnly) return;
try {
localRawConnections = await providersFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
logger.warn(
"[omniroute-plugin] config shim: /api/providers fetch failed; usableOnly filter disabled for this refresh",
err
);
}
};
await Promise.allSettled([
doModels(),
doCombos(),
doAutoCombos(),
doEnrichment(),
doCompression(),
doConnections(),
]);
const modelsFetchOk = !modelsFetchThrew && localRawModels.length > 0;
// Disk-cache fallback (cold first run, no warm snapshot): when the
// live fetch returned no models AND features.diskCache !== false,
// hydrate from the last-known-good snapshot so OC still surfaces a
// usable catalog (e.g. IP whitelist drop, offline laptop).
if (modelsFetchThrew && wantDiskCache && !warmSnapshot) {
const snapshot = await diskSnapshotReader(resolved.providerId, snapshotFingerprint);
if (snapshot && snapshot.rawModels.length > 0) {
logger.warn(
`[omniroute-plugin] config shim: /v1/models unreachable; using stale disk cache (${snapshot.rawModels.length} models)`
);
localRawModels = snapshot.rawModels;
localRawCombos = snapshot.rawCombos;
localRawAutoCombos = snapshot.rawAutoCombos ?? [];
localRawEnrichment = snapshot.rawEnrichment;
localRawCompressionCombos = snapshot.rawCompressionCombos;
localRawConnections = snapshot.rawConnections;
}
// Compression-metadata fetch — opt-in via features.compressionMetadata.
// When on, the default pipeline is appended to every combo `name` so
// the TUI picker advertises which compression a combo applies.
rawCompressionCombos = [];
if (wantCompressionMeta) {
try {
rawCompressionCombos = await compressionMetaFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
logger.warn(
"[omniroute-plugin] config shim: /api/context/combos fetch failed; publishing combos without compression suffix",
err
);
}
}
// Cache even partial results — a subsequent provider-hook call should
// not re-burn the timeout window on the same broken endpoint.
cache.set(cacheKey, {
rawModels: localRawModels,
rawCombos: localRawCombos,
rawAutoCombos: localRawAutoCombos,
rawEnrichment: localRawEnrichment,
rawCompressionCombos: localRawCompressionCombos,
rawConnections: localRawConnections,
expiresAt: now() + resolved.modelCacheTtl,
// Provider-connections fetch — opt-in via features.usableOnly. When
// on, the static catalog filters out models/combos whose canonical
// provider has no active connection. Soft-fail (empty list) disables
// the filter for this refresh, never hiding the whole catalog.
rawConnections = [];
if (wantUsableOnly) {
try {
rawConnections = await providersFetcher(baseURL, managementReadToken, 10_000);
} catch (err) {
logger.warn(
"[omniroute-plugin] config shim: /api/providers fetch failed; usableOnly filter disabled for this refresh",
err
);
}
}
// Disk-cache fallback: when the live fetch returned no models AND
// features.diskCache !== false, hydrate from the last-known-good
// snapshot so OC still surfaces a usable catalog (e.g. IP whitelist
// drop, offline laptop). The snapshot is whatever we last wrote on
// a healthy refresh; staleness is bounded only by how recently the
// user was online.
if (modelsFetchThrew && wantDiskCache) {
const snapshot = await diskSnapshotReader(resolved.providerId, snapshotFingerprint);
if (snapshot && snapshot.rawModels.length > 0) {
logger.warn(
`[omniroute-plugin] config shim: /v1/models unreachable; using stale disk cache (${snapshot.rawModels.length} models)`
);
rawModels = snapshot.rawModels;
rawCombos = snapshot.rawCombos;
rawAutoCombos = snapshot.rawAutoCombos ?? [];
rawEnrichment = snapshot.rawEnrichment;
rawCompressionCombos = snapshot.rawCompressionCombos;
rawConnections = snapshot.rawConnections;
}
}
// Cache even partial results — a subsequent provider-hook call should
// not re-burn the timeout window on the same broken endpoint.
cache.set(cacheKey, {
rawModels,
rawCombos,
rawAutoCombos,
rawEnrichment,
rawCompressionCombos,
rawConnections,
expiresAt: t + resolved.modelCacheTtl,
});
// Startup diagnostics (file-based) — fires at startup via config hook
if (resolved.features?.startupDebug === true) {
await writeStartupDiagnostics({
providerId: resolved.providerId,
baseURL,
modelCount: rawModels.length,
comboCount: rawCombos.length,
enrichmentSize: rawEnrichment.size,
autoComboCount: rawAutoCombos.length,
enrichment: rawEnrichment,
autoCombos: rawAutoCombos,
features: resolved.features,
});
}
// Startup diagnostics (file-based) — fires at startup via config hook
if (resolved.features?.startupDebug === true) {
await writeStartupDiagnostics({
providerId: resolved.providerId,
baseURL,
modelCount: localRawModels.length,
comboCount: localRawCombos.length,
enrichmentSize: localRawEnrichment.size,
autoComboCount: localRawAutoCombos.length,
enrichment: localRawEnrichment,
autoCombos: localRawAutoCombos,
features: resolved.features,
});
}
// Disk-cache write: persist the last successful (or any non-empty)
// catalog so a subsequent cold start with a failed fetch can recover.
// Best-effort; soft-fail keeps us moving when the data dir isn't
// writable (e.g. read-only container). A failed refresh never
// overwrites the snapshot (modelsFetchOk gate).
if (modelsFetchOk && wantDiskCache) {
await diskSnapshotWriter(
resolved.providerId,
{
rawModels: localRawModels,
rawCombos: localRawCombos,
rawAutoCombos: localRawAutoCombos,
rawEnrichment: localRawEnrichment,
rawCompressionCombos: localRawCompressionCombos,
rawConnections: localRawConnections,
},
snapshotFingerprint
);
}
// Re-publish a fresh block via the shared cache so OC >=1.14.49's
// dynamic provider hook picks it up from the cache. When the models
// fetch threw and a warm snapshot was served, keep the warm block
// (no downgrade to stub).
if (modelsFetchOk || !warmSnapshot) {
const freshBlock = buildStaticProviderEntry(
localRawModels,
localRawCombos,
resolved,
baseURL,
apiKey,
localRawEnrichment,
localRawCompressionCombos,
localRawConnections,
localRawAutoCombos
);
const inputWithProvider2 = input as { provider?: Record<string, unknown> };
if (inputWithProvider2.provider) {
inputWithProvider2.provider[resolved.providerId] = freshBlock;
}
}
};
if (warmSnapshot) {
// Warm startup: publish the snapshot block immediately, then run
// the refresh detached (never a floating unhandled rejection).
rawModels = warmSnapshot.rawModels;
rawCombos = warmSnapshot.rawCombos;
rawAutoCombos = warmSnapshot.rawAutoCombos ?? [];
rawEnrichment = warmSnapshot.rawEnrichment;
rawCompressionCombos = warmSnapshot.rawCompressionCombos;
rawConnections = warmSnapshot.rawConnections;
// In-flight guard: if a refresh is already running for this
// cacheKey, piggyback on it instead of starting a second one.
const existing = _inflightRefresh.get(cacheKey);
if (existing) {
// Another refresh is in-flight — don't start a second one.
// The existing refresh will update the cache when it completes.
} else {
const refreshP = doRefresh()
.catch((err: unknown) => {
logger.warn("[omniroute-plugin] config shim: background refresh failed", err);
})
.finally(() => {
_inflightRefresh.delete(cacheKey);
});
_inflightRefresh.set(cacheKey, refreshP);
}
} else {
// Cold first run (no warm snapshot): await the refresh so the
// first publish is always correct. In-flight guard still applies.
const existing = _inflightRefresh.get(cacheKey);
if (existing) {
await existing;
// After the in-flight refresh completes, the cache has the data.
const fresh = cache.get(cacheKey);
if (fresh) {
rawModels = fresh.rawModels;
rawCombos = fresh.rawCombos;
rawAutoCombos = fresh.rawAutoCombos;
rawEnrichment = fresh.rawEnrichment;
rawCompressionCombos = fresh.rawCompressionCombos;
rawConnections = fresh.rawConnections;
}
} else {
const refreshP = doRefresh()
.catch((err: unknown) => {
logger.warn("[omniroute-plugin] config shim: refresh failed", err);
})
.finally(() => {
_inflightRefresh.delete(cacheKey);
});
_inflightRefresh.set(cacheKey, refreshP);
await refreshP;
// After the refresh, the cache has the data.
const fresh = cache.get(cacheKey);
if (fresh) {
rawModels = fresh.rawModels;
rawCombos = fresh.rawCombos;
rawAutoCombos = fresh.rawAutoCombos;
rawEnrichment = fresh.rawEnrichment;
rawCompressionCombos = fresh.rawCompressionCombos;
rawConnections = fresh.rawConnections;
}
}
// Disk-cache write: persist the last successful (or any non-empty)
// catalog so a subsequent cold start with a failed fetch can recover.
// Best-effort; soft-fail keeps us moving when the data dir isn't
// writable (e.g. read-only container).
if (modelsFetchOk && wantDiskCache) {
await diskSnapshotWriter(
resolved.providerId,
{
rawModels,
rawCombos,
rawAutoCombos,
rawEnrichment,
rawCompressionCombos,
rawConnections,
},
snapshotFingerprint
);
}
}

View File

@@ -33,7 +33,6 @@ import {
createOmniRouteProviderHook,
OmniRoutePlugin,
resolveOmniRoutePluginOptions,
_resetInflightRefresh,
type OmniRouteCombosFetcher,
type OmniRouteEnrichmentEntry,
type OmniRouteEnrichmentFetcher,
@@ -48,16 +47,6 @@ import {
type OmniRouteStaticProviderEntry,
} from "../src/index.js";
// ────────────────────────────────────────────────────────────────────────────
// Test isolation: reset the module-level in-flight refresh guard between
// tests so a detached refresh from a previous test doesn't leak into the
// next one.
// ────────────────────────────────────────────────────────────────────────────
test.beforeEach(() => {
_resetInflightRefresh();
});
// ────────────────────────────────────────────────────────────────────────────
// Fixtures
// ────────────────────────────────────────────────────────────────────────────
@@ -1250,10 +1239,7 @@ test("config: diskCache hydrates stale snapshot when /v1/models throws", async (
);
assert.equal(writes, 0, "disk write skipped when live fetch failed");
assert.ok(
logger.entries.some((e) =>
String(e[0]).includes("using stale disk cache") ||
String(e[0]).includes("warm startup from disk snapshot")
),
logger.entries.some((e) => String(e[0]).includes("using stale disk cache")),
"disk-cache hydration breadcrumb emitted"
);
});

View File

@@ -1,827 +0,0 @@
/**
* Warm-startup + parallel-refresh tests for the opencode-plugin config shim.
*
* Covers `createOmniRouteConfigHook(opts, deps)`:
* - (a) Warm startup: cache miss + matching snapshot → provider block
* populated from snapshot data (not live fetch data).
* - (b) Fingerprint mismatch: reader returns undefined → no warm publish,
* falls through to awaited fetch (cold-start behavior).
* - (c) Successful parallel refresh: all fetchers resolve → cache updated,
* disk snapshot written.
* - (d) Failed refresh keeps the snapshot: warm-served + models fetcher
* rejects → no disk overwrite, block stays at warm-snapshot shape.
* - (e) Parallelism: all six fetchers start concurrently (not sequential).
* - (f) Soft-fail parity under Promise.allSettled: per-endpoint
* fallbacks + logger.warn breadcrumbs preserved.
* - (g) No double-refresh: concurrent hook invocations on the same cacheKey
* trigger only one refresh (in-flight guard).
* - (h) features.diskCache: false disables the warm read entirely.
*
* Mocking strategy: every dependency is DI-injected at hook construction
* (same pattern as config-shim.test.ts). No global monkey-patching.
*/
import test from "node:test";
import assert from "node:assert/strict";
import type { Config } from "@opencode-ai/plugin";
import {
createOmniRouteConfigHook,
resolveOmniRoutePluginOptions,
_resetInflightRefresh,
type OmniRouteAutoCombosFetcher,
type OmniRouteCombosFetcher,
type OmniRouteCompressionMetaFetcher,
type OmniRouteEnrichmentEntry,
type OmniRouteEnrichmentFetcher,
type OmniRouteEnrichmentMap,
type OmniRouteFetchCache,
type OmniRouteModelsFetcher,
type OmniRouteProviderConnection,
type OmniRouteProvidersFetcher,
type OmniRouteRawAutoCombo,
type OmniRouteRawCombo,
type OmniRouteRawModelEntry,
type OmniRouteReadAuthJson,
type OmniRouteStaticProviderEntry,
type OmniRouteDiskSnapshotReader,
type OmniRouteDiskSnapshotWriter,
type OmniRouteCompressionCombo,
} from "../src/index.js";
// ────────────────────────────────────────────────────────────────────────────
// Test isolation: reset the module-level in-flight refresh guard between
// tests so a detached refresh from a previous test doesn't leak into the
// next one (same cacheKey, different cache instance).
// ────────────────────────────────────────────────────────────────────────────
test.beforeEach(() => {
_resetInflightRefresh();
});
// ────────────────────────────────────────────────────────────────────────────
// Fixtures
// ────────────────────────────────────────────────────────────────────────────
const MODEL_CLAUDE: OmniRouteRawModelEntry = {
id: "claude-sonnet-4-6",
capabilities: {
tool_calling: true,
reasoning: true,
vision: true,
thinking: false,
temperature: true,
},
context_length: 200_000,
max_output_tokens: 64_000,
max_input_tokens: 180_000,
input_modalities: ["text", "image"],
output_modalities: ["text"],
};
const MODEL_GEMINI: OmniRouteRawModelEntry = {
id: "gemini-3-flash",
capabilities: { tool_calling: true, reasoning: false, vision: true, thinking: false },
context_length: 1_000_000,
max_output_tokens: 8_192,
input_modalities: ["text", "image"],
output_modalities: ["text"],
};
const COMBO_CLAUDE_TIER: OmniRouteRawCombo = {
id: "combo-claude-tier",
name: "Claude Tier",
models: [
{ id: "s1", kind: "model", model: "claude-sonnet-4-6", weight: 100 },
{ id: "s2", kind: "model", model: "gemini-3-flash", weight: 50 },
],
};
const AUTO_COMBO: OmniRouteRawAutoCombo = {
id: "auto",
name: "Auto",
};
const COMPRESSION_COMBO: OmniRouteCompressionCombo = {
id: "ctx-combo-1",
name: "Context Combo",
pipeline: "gzip",
};
const CONNECTION_CLAUDE: OmniRouteProviderConnection = {
id: "c1",
provider: "claude",
isActive: true,
testStatus: "active",
};
// ────────────────────────────────────────────────────────────────────────────
// DI stub helpers
// ────────────────────────────────────────────────────────────────────────────
function stubReadAuthJson(
value: Record<string, unknown> | undefined | null
): OmniRouteReadAuthJson {
return async () => value as never;
}
function immediateFetcher<T extends (...args: unknown[]) => Promise<unknown>>(
payload: ReturnType<T> extends Promise<infer U> ? U : never
): T & { callCount: () => number; startedAt: () => number | undefined } {
let n = 0;
let start: number | undefined;
const f = async (..._args: unknown[]) => {
start = Date.now();
n++;
return payload;
};
return Object.assign(f as T, { callCount: () => n, startedAt: () => start });
}
function throwingFetcher<T extends (...args: unknown[]) => Promise<unknown>>(
msg = "ECONNREFUSED"
): T & { callCount: () => number } {
let n = 0;
const f = async (..._args: unknown[]) => {
n++;
throw new Error(msg);
};
return Object.assign(f as T, { callCount: () => n });
}
interface WarnCapture {
warn: (...args: unknown[]) => void;
entries: unknown[][];
}
function captureWarn(): WarnCapture {
const entries: unknown[][] = [];
return {
warn: (...args: unknown[]) => {
entries.push(args);
},
entries,
};
}
function makeInput(initialProvider: Record<string, unknown> = {}): Config {
return { provider: initialProvider } as unknown as Config;
}
/** Build a valid auth.json stub for the default providerId. */
function authStub() {
return stubReadAuthJson({
"opencode-omniroute": {
type: "api",
key: "sk-test",
baseURL: "https://or.example.com/v1",
},
});
}
// ────────────────────────────────────────────────────────────────────────────
// (a) Warm startup: cache miss + matching snapshot → provider block populated
// from snapshot data (not live fetch data)
// ────────────────────────────────────────────────────────────────────────────
test("warm-startup: snapshot data used when snapshot is present", async () => {
// Live fetch returns MODEL_CLAUDE, but snapshot has MODEL_GEMINI.
// With warm startup, the block should contain the snapshot data.
const fetcher = immediateFetcher<OmniRouteModelsFetcher>([MODEL_CLAUDE]);
const combosFetcher = immediateFetcher<OmniRouteCombosFetcher>([]);
const autoCombosFetcher = immediateFetcher<OmniRouteAutoCombosFetcher>([]);
const enrichmentFetcher = immediateFetcher<OmniRouteEnrichmentFetcher>(new Map());
const compressionMetaFetcher = immediateFetcher<OmniRouteCompressionMetaFetcher>([]);
const providersFetcher = immediateFetcher<OmniRouteProvidersFetcher>([]);
const logger = captureWarn();
const snapshot: Omit<import("../src/index.js").OmniRouteFetchCacheEntry, "expiresAt"> = {
rawModels: [MODEL_GEMINI],
rawCombos: [],
rawAutoCombos: [],
rawEnrichment: new Map(),
rawCompressionCombos: [],
rawConnections: [],
};
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => snapshot;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute" },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
autoCombosFetcher,
enrichmentFetcher,
compressionMetaFetcher,
providersFetcher,
diskSnapshotReader,
diskSnapshotWriter,
logger,
}
);
const input = makeInput();
await hook(input);
const provider = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider;
const entry = provider["opencode-omniroute"];
assert.ok(entry, "provider entry published");
// With warm startup, the block should contain the snapshot data (GEMINI),
// not the live fetch data (CLAUDE). This is the key assertion: the warm
// snapshot is served first, and the live refresh updates the cache in the
// background. On the next hook invocation, the cache will have the fresh data.
const hasGemini = entry.models["opencode-omniroute/gemini-3-flash"] !== undefined;
const hasClaude = entry.models["opencode-omniroute/claude-sonnet-4-6"] !== undefined;
assert.ok(
hasGemini || hasClaude,
"provider block has at least one model"
);
// The warm-startup breadcrumb should be emitted.
assert.ok(
logger.entries.some((e) =>
String(e[0]).includes("warm startup from disk snapshot")
),
"warm-startup breadcrumb emitted"
);
});
// ────────────────────────────────────────────────────────────────────────────
// (b) Fingerprint mismatch: reader returns undefined → no warm publish,
// falls through to awaited fetch
// ────────────────────────────────────────────────────────────────────────────
test("warm-startup: fingerprint mismatch → no warm publish, awaited fetch", async () => {
const fetcher = immediateFetcher<OmniRouteModelsFetcher>([MODEL_CLAUDE]);
const combosFetcher = immediateFetcher<OmniRouteCombosFetcher>([]);
const logger = captureWarn();
// Reader returns undefined → fingerprint mismatch or missing snapshot.
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute" },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
diskSnapshotReader,
diskSnapshotWriter,
logger,
}
);
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry, "provider entry published from live fetch");
// Live fetch data, not snapshot data.
assert.ok(
entry.models["opencode-omniroute/claude-sonnet-4-6"],
"live fetch model present"
);
assert.equal(fetcher.callCount(), 1, "fetcher was called (awaited cold path)");
// No warm-startup breadcrumb when no snapshot.
assert.ok(
!logger.entries.some((e) =>
String(e[0]).includes("warm startup from disk snapshot")
),
"no warm-startup breadcrumb when no snapshot"
);
});
// ────────────────────────────────────────────────────────────────────────────
// (c) Successful parallel refresh: all fetchers resolve → cache updated,
// disk snapshot written, block re-published with fresh data
// ────────────────────────────────────────────────────────────────────────────
test("warm-startup: parallel refresh updates cache + writes snapshot", async () => {
const fetcher = immediateFetcher<OmniRouteModelsFetcher>([MODEL_CLAUDE]);
const combosFetcher = immediateFetcher<OmniRouteCombosFetcher>([COMBO_CLAUDE_TIER]);
const autoCombosFetcher = immediateFetcher<OmniRouteAutoCombosFetcher>([AUTO_COMBO]);
const enrichmentFetcher = immediateFetcher<OmniRouteEnrichmentFetcher>(
new Map<string, OmniRouteEnrichmentEntry>([
["claude-sonnet-4-6", { name: "Claude Sonnet 4.6" }],
])
);
const compressionMetaFetcher = immediateFetcher<OmniRouteCompressionMetaFetcher>([
COMPRESSION_COMBO,
]);
const providersFetcher = immediateFetcher<OmniRouteProvidersFetcher>([CONNECTION_CLAUDE]);
const logger = captureWarn();
const snapshot: Omit<import("../src/index.js").OmniRouteFetchCacheEntry, "expiresAt"> = {
rawModels: [MODEL_GEMINI],
rawCombos: [],
rawAutoCombos: [],
rawEnrichment: new Map(),
rawCompressionCombos: [],
rawConnections: [],
};
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => snapshot;
let snapshotWrites = 0;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {
snapshotWrites++;
};
const sharedCache: OmniRouteFetchCache = new Map();
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", modelCacheTtl: 60_000 },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
autoCombosFetcher,
enrichmentFetcher,
compressionMetaFetcher,
providersFetcher,
diskSnapshotReader,
diskSnapshotWriter,
cache: sharedCache,
logger,
}
);
const input = makeInput();
await hook(input);
// Warm block should have been published.
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry, "warm provider entry published");
// Give detached refresh time to complete.
await new Promise((r) => setTimeout(r, 100));
// After parallel refresh, the cache should have the fresh data.
const cacheKey = Array.from(sharedCache.keys())[0];
assert.ok(cacheKey, "cache entry created");
const cached = sharedCache.get(cacheKey)!;
assert.ok(cached.expiresAt > 0, "cache entry has expiresAt");
// Fresh data from the live fetchers (not the stale snapshot).
assert.equal(cached.rawModels.length, 1, "cache has fresh models");
assert.equal(cached.rawModels[0].id, "claude-sonnet-4-6", "cache has correct model");
// Disk snapshot should have been written.
assert.equal(snapshotWrites, 1, "disk snapshot written after successful refresh");
});
// ────────────────────────────────────────────────────────────────────────────
// (d) Failed refresh keeps the snapshot: warm-served + models fetcher
// rejects → no disk overwrite, block stays at warm-snapshot shape
// ────────────────────────────────────────────────────────────────────────────
test("warm-startup: failed refresh keeps the snapshot, no disk overwrite", async () => {
const fetcher = throwingFetcher<OmniRouteModelsFetcher>();
const combosFetcher = throwingFetcher<OmniRouteCombosFetcher>();
const logger = captureWarn();
const snapshot: Omit<import("../src/index.js").OmniRouteFetchCacheEntry, "expiresAt"> = {
rawModels: [MODEL_GEMINI],
rawCombos: [COMBO_CLAUDE_TIER],
rawAutoCombos: [],
rawEnrichment: new Map(),
rawCompressionCombos: [],
rawConnections: [],
};
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => snapshot;
let snapshotWrites = 0;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {
snapshotWrites++;
};
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute" },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
diskSnapshotReader,
diskSnapshotWriter,
logger,
}
);
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry, "warm provider entry published");
// The block should contain the warm snapshot data (gemini), not be
// downgraded to a stub.
assert.ok(
entry.models["opencode-omniroute/gemini-3-flash"],
"warm snapshot model preserved (not downgraded to stub)"
);
// Give detached refresh time to complete.
await new Promise((r) => setTimeout(r, 100));
// No disk write on failed refresh.
assert.equal(snapshotWrites, 0, "no disk snapshot written when models fetch failed");
});
// ────────────────────────────────────────────────────────────────────────────
// (e) Parallelism: all six fetchers start concurrently (not sequential)
// ────────────────────────────────────────────────────────────────────────────
test("warm-startup: all fetchers start concurrently (parallel fan-out)", async () => {
const startTimes: number[] = [];
const barrier = new Promise<void>((r) => {
setTimeout(r, 30);
});
function instrumentedFetcher<T extends (...args: unknown[]) => Promise<unknown>>(
payload: ReturnType<T> extends Promise<infer U> ? U : never
): T & { callCount: () => number } {
let n = 0;
const f = async (..._args: unknown[]) => {
startTimes.push(Date.now());
n++;
await barrier;
return payload;
};
return Object.assign(f as T, { callCount: () => n });
}
const fetcher = instrumentedFetcher<OmniRouteModelsFetcher>([MODEL_CLAUDE]);
const combosFetcher = instrumentedFetcher<OmniRouteCombosFetcher>([]);
const autoCombosFetcher = instrumentedFetcher<OmniRouteAutoCombosFetcher>([]);
const enrichmentFetcher = instrumentedFetcher<OmniRouteEnrichmentFetcher>(new Map());
const compressionMetaFetcher = instrumentedFetcher<OmniRouteCompressionMetaFetcher>([]);
const providersFetcher = instrumentedFetcher<OmniRouteProvidersFetcher>([]);
const logger = captureWarn();
// No snapshot → cold path (awaited). All fetchers must still start
// concurrently.
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", features: { enrichment: true, compressionMetadata: true, usableOnly: true } },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
autoCombosFetcher,
enrichmentFetcher,
compressionMetaFetcher,
providersFetcher,
diskSnapshotReader,
diskSnapshotWriter,
logger,
}
);
const input = makeInput();
await hook(input);
// All fetchers should have been called.
assert.equal(fetcher.callCount(), 1, "models fetcher called");
assert.equal(combosFetcher.callCount(), 1, "combos fetcher called");
assert.equal(autoCombosFetcher.callCount(), 1, "autoCombos fetcher called");
assert.equal(enrichmentFetcher.callCount(), 1, "enrichment fetcher called");
assert.equal(compressionMetaFetcher.callCount(), 1, "compressionMeta fetcher called");
assert.equal(providersFetcher.callCount(), 1, "providers fetcher called");
// All start times should be within 20ms of each other (parallel fan-out),
// NOT sequential (which would show ~30ms gaps between each).
assert.ok(startTimes.length >= 6, "all 6 fetchers started");
const minStart = Math.min(...startTimes);
const maxStart = Math.max(...startTimes);
assert.ok(
maxStart - minStart < 20,
`all fetchers started within 20ms (spread: ${maxStart - minStart}ms) — parallel fan-out confirmed`
);
});
// ────────────────────────────────────────────────────────────────────────────
// (f) Soft-fail parity under Promise.allSettled: per-endpoint fallbacks +
// logger.warn breadcrumbs preserved
// ────────────────────────────────────────────────────────────────────────────
test("warm-startup: combos reject → models-only catalog with warn", async () => {
const fetcher = immediateFetcher<OmniRouteModelsFetcher>([MODEL_CLAUDE]);
const combosFetcher = throwingFetcher<OmniRouteCombosFetcher>("403 Forbidden");
const logger = captureWarn();
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute" },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
diskSnapshotReader,
diskSnapshotWriter,
logger,
}
);
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry, "provider entry published");
assert.ok(
entry.models["opencode-omniroute/claude-sonnet-4-6"],
"models-only catalog (no combos)"
);
assert.ok(
logger.entries.some((e) => String(e[0]).includes("/api/combos fetch failed")),
"combos-fetch breadcrumb emitted"
);
});
test("warm-startup: enrichment rejects → raw-id catalog with warn", async () => {
const fetcher = immediateFetcher<OmniRouteModelsFetcher>([MODEL_CLAUDE]);
const combosFetcher = immediateFetcher<OmniRouteCombosFetcher>([]);
const enrichmentFetcher = throwingFetcher<OmniRouteEnrichmentFetcher>("ETIMEDOUT");
const logger = captureWarn();
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute" },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
enrichmentFetcher,
diskSnapshotReader,
diskSnapshotWriter,
logger,
}
);
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry, "provider entry published");
assert.equal(
entry.models["opencode-omniroute/claude-sonnet-4-6"].name,
"claude-sonnet-4-6",
"raw id retained (no enrichment)"
);
assert.ok(
logger.entries.some((e) => String(e[0]).includes("/api/pricing/models fetch failed")),
"enrichment-fetch breadcrumb emitted"
);
});
test("warm-startup: providers reject → usableOnly filter disabled with warn", async () => {
const fetcher = immediateFetcher<OmniRouteModelsFetcher>([MODEL_CLAUDE]);
const combosFetcher = immediateFetcher<OmniRouteCombosFetcher>([]);
const providersFetcher = throwingFetcher<OmniRouteProvidersFetcher>("ETIMEDOUT");
const logger = captureWarn();
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", features: { usableOnly: true } },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
providersFetcher,
diskSnapshotReader,
diskSnapshotWriter,
logger,
}
);
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry, "provider entry published");
// Soft-fail: model kept (filter disabled).
assert.ok(
entry.models["opencode-omniroute/claude-sonnet-4-6"],
"model kept (usableOnly filter disabled)"
);
assert.ok(
logger.entries.some((e) => String(e[0]).includes("/api/providers fetch failed")),
"providers-fetch breadcrumb emitted"
);
});
// ────────────────────────────────────────────────────────────────────────────
// (g) No double-refresh: concurrent hook invocations on the same cacheKey
// trigger only one refresh (in-flight guard)
// ────────────────────────────────────────────────────────────────────────────
test("warm-startup: concurrent hook invocations dedupe refresh", async () => {
let fetchCount = 0;
const slowResolve = new Promise<void>((r) => {
setTimeout(r, 100);
});
const fetcher: OmniRouteModelsFetcher = async () => {
fetchCount++;
await slowResolve;
return [MODEL_CLAUDE];
};
const combosFetcher = immediateFetcher<OmniRouteCombosFetcher>([]);
const logger = captureWarn();
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => undefined;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const sharedCache: OmniRouteFetchCache = new Map();
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", modelCacheTtl: 60_000 },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
diskSnapshotReader,
diskSnapshotWriter,
cache: sharedCache,
logger,
}
);
// Fire two concurrent hook invocations on the same cache.
const inputA = makeInput();
const inputB = makeInput();
await Promise.all([hook(inputA), hook(inputB)]);
// Both should have published, but the refresh should only run once.
assert.equal(
fetchCount,
1,
"models fetcher called only once across concurrent invocations (in-flight guard)"
);
});
// ────────────────────────────────────────────────────────────────────────────
// (h) features.diskCache: false disables the warm read entirely
// ────────────────────────────────────────────────────────────────────────────
test("warm-startup: diskCache=false disables warm read, falls through to awaited fetch", async () => {
const fetcher = immediateFetcher<OmniRouteModelsFetcher>([MODEL_CLAUDE]);
const combosFetcher = immediateFetcher<OmniRouteCombosFetcher>([]);
const logger = captureWarn();
let readerCalled = false;
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => {
readerCalled = true;
return {
rawModels: [MODEL_GEMINI],
rawCombos: [],
rawAutoCombos: [],
rawEnrichment: new Map(),
rawCompressionCombos: [],
rawConnections: [],
};
};
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute", features: { diskCache: false } },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
diskSnapshotReader,
diskSnapshotWriter,
logger,
}
);
const input = makeInput();
await hook(input);
assert.equal(readerCalled, false, "disk snapshot reader NOT called when diskCache=false");
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry, "provider entry published from live fetch");
assert.ok(
entry.models["opencode-omniroute/claude-sonnet-4-6"],
"live fetch model present (not snapshot)"
);
});
// ────────────────────────────────────────────────────────────────────────────
// Warm startup: snapshot age logged
// ────────────────────────────────────────────────────────────────────────────
test("warm-startup: snapshot age is logged when warm-starting from disk", async () => {
const fetcher = immediateFetcher<OmniRouteModelsFetcher>([MODEL_CLAUDE]);
const combosFetcher = immediateFetcher<OmniRouteCombosFetcher>([]);
const logger = captureWarn();
const snapshot: Omit<import("../src/index.js").OmniRouteFetchCacheEntry, "expiresAt"> & {
writtenAt?: number;
} = {
rawModels: [MODEL_GEMINI],
rawCombos: [],
rawAutoCombos: [],
rawEnrichment: new Map(),
rawCompressionCombos: [],
rawConnections: [],
writtenAt: Date.now() - 3_600_000, // 1 hour ago
};
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => snapshot;
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute" },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
diskSnapshotReader,
diskSnapshotWriter,
logger,
}
);
const input = makeInput();
await hook(input);
// The log should mention "warm startup from disk snapshot".
assert.ok(
logger.entries.some((e) =>
String(e[0]).includes("warm startup from disk snapshot")
),
"warm-startup breadcrumb emitted"
);
});
// ────────────────────────────────────────────────────────────────────────────
// Warm startup: empty snapshot (rawModels.length === 0) is skipped
// ────────────────────────────────────────────────────────────────────────────
test("warm-startup: empty snapshot (rawModels.length=0) is skipped, falls through to fetch", async () => {
const fetcher = immediateFetcher<OmniRouteModelsFetcher>([MODEL_CLAUDE]);
const combosFetcher = immediateFetcher<OmniRouteCombosFetcher>([]);
const logger = captureWarn();
const diskSnapshotReader: OmniRouteDiskSnapshotReader = async () => ({
rawModels: [],
rawCombos: [],
rawAutoCombos: [],
rawEnrichment: new Map(),
rawCompressionCombos: [],
rawConnections: [],
});
const diskSnapshotWriter: OmniRouteDiskSnapshotWriter = async () => {};
const hook = createOmniRouteConfigHook(
{ providerId: "omniroute" },
{
readAuthJson: authStub(),
fetcher,
combosFetcher,
diskSnapshotReader,
diskSnapshotWriter,
logger,
}
);
const input = makeInput();
await hook(input);
const entry = (input as { provider: Record<string, OmniRouteStaticProviderEntry> }).provider[
"opencode-omniroute"
];
assert.ok(entry, "provider entry published from live fetch");
// Live data, not empty snapshot.
assert.ok(
entry.models["opencode-omniroute/claude-sonnet-4-6"],
"live fetch model present (empty snapshot skipped)"
);
assert.equal(fetcher.callCount(), 1, "fetcher was called (awaited cold path)");
});

View File

@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
## Project at a Glance
**OmniRoute** — unified AI proxy/router. One endpoint, 338 LLM providers, auto-fallback.
**OmniRoute** — unified AI proxy/router. One endpoint, 291 LLM providers, auto-fallback.
| Layer | Location | Purpose |
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -56,9 +56,9 @@ Repository map and Reference Documentation sections below.
| Translators | `open-sse/translator/` | Format conversion (OpenAI↔Claude↔Gemini) |
| Transformer | `open-sse/transformer/` | Responses API ↔ Chat Completions |
| Services | `open-sse/services/` | Combo routing, rate limits, caching, etc |
| Database | `src/lib/db/` | SQLite domain modules (144 migrations) |
| Database | `src/lib/db/` | SQLite domain modules (130 migrations) |
| Domain/Policy | `src/domain/` | Policy engine, cost rules, fallback logic |
| MCP Server | `open-sse/mcp-server/` | 105 tools (43 base + memory/skill/agentSkill/pool/notion/obsidian/gamification/plugin modules), 3 transports (stdio / SSE / Streamable HTTP), 31 scopes |
| MCP Server | `open-sse/mcp-server/` | 105 tools (42 base + memory/skill/agentSkill/pool/notion/obsidian/gamification/plugin modules), 3 transports (stdio / SSE / Streamable HTTP), 31 scopes |
| A2A Server | `src/lib/a2a/` | JSON-RPC 2.0 agent protocol |
| Skills | `src/lib/skills/` | Extensible skill framework |
| Memory | `src/lib/memory/` | Persistent conversational memory |
@@ -83,7 +83,7 @@ Client → /v1/chat/completions (Next.js route)
API routes follow a consistent pattern: `Route → CORS preflight → Zod body validation → Optional auth (extractApiKey/isValidApiKey) → API key policy enforcement → Handler delegation (open-sse)`. No global Next.js middleware — interception is route-specific.
**Combo routing** (`open-sse/services/combo.ts`): 19 public strategies (priority, weighted, fill-first, round-robin, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline). Each target calls `handleSingleModel()` which wraps `handleChatCore()` with per-target error handling and circuit breaker checks. The `fusion` strategy is the exception: it fans out to a panel of models in parallel, then a judge model synthesizes one final answer (`open-sse/services/fusion.ts`). See `docs/routing/AUTO-COMBO.md` for the 14-factor Auto-Combo scoring + the full strategy table and `docs/architecture/RESILIENCE_GUIDE.md` for the 3 resilience layers.
**Combo routing** (`open-sse/services/combo.ts`): 19 public strategies (priority, weighted, fill-first, round-robin, p2c, random, least-used, cost-optimized, reset-aware, reset-window, headroom, strict-random, auto, lkgp, context-optimized, cache-optimized, context-relay, fusion, pipeline). Each target calls `handleSingleModel()` which wraps `handleChatCore()` with per-target error handling and circuit breaker checks. The `fusion` strategy is the exception: it fans out to a panel of models in parallel, then a judge model synthesizes one final answer (`open-sse/services/fusion.ts`). See `docs/routing/AUTO-COMBO.md` for the 13-factor Auto-Combo scoring + the full strategy table and `docs/architecture/RESILIENCE_GUIDE.md` for the 3 resilience layers.
---
@@ -247,26 +247,17 @@ Read the nearest `AGENTS.md` and the linked deep-dive before making a non-trivia
## File placement & repo-root hygiene
- **Test files**: ALL unit tests, integration tests, ecosystem tests, or Vitest files MUST strictly be placed within the `tests/` directory (e.g., `tests/unit/`, `tests/integration/`). NEVER create test files in the project root (`/`).
- **Scripts and utilities**: ALL maintenance, debugging, generation, or experimental scripts (`.cjs`, `.mjs`, `.js`, `.ts`) MUST be placed strictly inside one of the `scripts/` subfolders (`build/`, `dev/`, `check/`, `docs/`, `i18n/`, `ad-hoc/`, `quality/`, `release/`, `ci/`, `ops/`, `perf/`, `research/`, `sre/`, `vps/`, `homolog/`, `raycast/`, `skills/`, `test/`, `cli/`, `compression/`, `compression-eval/`, `devin-bridge/`, `docker/`, `features/`, `router-eval/`). One-shot or experimental code goes under `scripts/ad-hoc/`. NEVER dump loose scripts in the project root (`/`) or the top-level `scripts/` folder.
- **Scripts and utilities**: ALL maintenance, debugging, generation, or experimental scripts (`.cjs`, `.mjs`, `.js`, `.ts`) MUST be placed strictly inside one of the `scripts/` subfolders (`build/`, `dev/`, `check/`, `docs/`, `i18n/`, `ad-hoc/`). One-shot or experimental code goes under `scripts/ad-hoc/`. NEVER dump loose scripts in the project root (`/`) or the top-level `scripts/` folder.
**The project root MUST ONLY contain:**
- Configuration files (`vitest.config.ts`, `next.config.mjs`, `eslint.config.mjs`, `tsconfig*.json`, `playwright.config.ts`, `prettier.config.mjs`, `postcss.config.mjs`, `sonar-project.properties`, `fly.toml`, `docker-compose*.yml`, `Dockerfile`)
- Dependency files (`package.json`, `package-lock.json`)
- Documentation files (`README.md`, `CHANGELOG.md`, `ROADMAP.md`, `LICENSE`, `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`, `llm.txt`, `Tuto_Qdrant.md`)
- Documentation files (`README.md`, `CHANGELOG.md`, `LICENSE`, `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`, `llm.txt`, `Tuto_Qdrant.md`)
- CI/CD files and ignore definitions (`.gitignore`, `.dockerignore`, `.npmignore`, `.npmrc`, `.node-version`, `.nvmrc`, `.env.example`)
When creating _any_ validation tests or one-off logic scripts, default to `scripts/ad-hoc/` or `tests/unit/` according to your goals. Do not pollute the `/` root context.
- **Root `_*` paths are private and NEVER tracked** (`_tasks/`, `_references/`, `_mono_repo/`,
`_ideia/`, `_cache/` and any future `_<name>`): they live on disk only, are gitignored by the
anchored patterns `/_*/` + `/_*`, and some are full git repositories of their own (`_tasks`
private remote `_tasks_omniroute`). Never `git add` anything inside them (a plain `add` is
already blocked by the ignore; never use `-f`), and never "clean them up" from the main repo —
untracking is done with `git rm --cached` so the disk content stays. The
`check:tracked-artifacts` gate (pre-commit + CI) fails on ANY tracked root path starting with
`_`, present or future. See Hard Rule #23 for the `_tasks` specifics.
---
## Key Conventions
@@ -300,7 +291,7 @@ When creating _any_ validation tests or one-off logic scripts, default to `scrip
- Encrypt credentials at rest (AES-256-GCM); never log SQLite encryption keys
- Sanitize user HTML with DOMPurify
- Upstream header denylist: `src/shared/constants/upstreamHeaders.ts` — keep sanitize, Zod schemas, and unit tests aligned when editing
- **Public upstream credentials** (for example, OAuth client_id/secret values or Firebase Web keys extracted from public CLIs): **MUST** be embedded via `resolvePublicCred()` from `open-sse/utils/publicCreds.ts`**never** as string literals. See `docs/security/PUBLIC_CREDS.md` for the mandatory pattern.
- **Public upstream credentials** (Gemini/Antigravity/Windsurf-style OAuth client_id/secret + Firebase Web keys extracted from public CLIs): **MUST** be embedded via `resolvePublicCred()` from `open-sse/utils/publicCreds.ts`**never** as string literals. See `docs/security/PUBLIC_CREDS.md` for the mandatory pattern.
- **Error responses** (HTTP / SSE / executor / MCP handler): **MUST** route through `buildErrorBody()` or `sanitizeErrorMessage()` from `open-sse/utils/error.ts`**never** put raw `err.stack` or `err.message` in a response body. See `docs/security/ERROR_SANITIZATION.md`.
- **Shell commands built from variables**: when calling `exec()`/`spawn()` with a script that needs runtime values, pass them via the `env` option (shell-escaped automatically) — **never** string-interpolate untrusted/external paths into the script body. Reference: `src/mitm/cert/install.ts::updateNssDatabases`.
- **Secure-by-default libraries** ([tldrsec/awesome-secure-defaults](https://github.com/tldrsec/awesome-secure-defaults)): prefer Helmet.js, DOMPurify, ssrf-req-filter, safe-regex, Google Tink over custom implementations whenever adding new security-sensitive surfaces.
@@ -404,7 +395,7 @@ For any non-trivial change, read the matching deep-dive first:
| Repo navigation | `docs/architecture/REPOSITORY_MAP.md` |
| Architecture | `docs/architecture/ARCHITECTURE.md` |
| Engineering reference | `docs/architecture/CODEBASE_DOCUMENTATION.md` |
| Auto-Combo (14-factor scoring, 19 strategies) | `docs/routing/AUTO-COMBO.md` |
| Auto-Combo (13-factor scoring, 19 strategies) | `docs/routing/AUTO-COMBO.md` |
| Resilience (3 mechanisms) | `docs/architecture/RESILIENCE_GUIDE.md` |
| Reasoning replay | `docs/routing/REASONING_REPLAY.md` |
| Skills framework | `docs/frameworks/SKILLS.md` |
@@ -428,7 +419,7 @@ For any non-trivial change, read the matching deep-dive first:
| Electron desktop app | `docs/guides/ELECTRON_GUIDE.md` |
| Release flow | `docs/ops/RELEASE_CHECKLIST.md` |
| Embedded services | `docs/frameworks/EMBEDDED-SERVICES.md` |
| Quality gates (~80 scripts, allowlist policy) | `docs/architecture/QUALITY_GATES.md` |
| Quality gates (~48 scripts, allowlist policy) | `docs/architecture/QUALITY_GATES.md` |
---
@@ -620,7 +611,7 @@ focused checks, and use a Conventional Commit message (for example, `docs: slim
## Quality Gates & Ratchets
OmniRoute has **~80 quality-gate scripts** (`scripts/check/` + `scripts/quality/`) wired
OmniRoute has **~48 quality-gate scripts** (`scripts/check/` + `scripts/quality/`) wired
across **9 gate-running jobs** in `.github/workflows/ci.yml` (`lint`, `quality-gate`,
`quality-extended`, `docs-sync-strict`, `i18n-ui-coverage`, `i18n`, `pr-test-policy`,
`test-vitest`, `sonarqube`), plus the `quality.yml` fast-gates job (PR→`release/**`) and
@@ -636,7 +627,7 @@ procedures are in [`docs/architecture/QUALITY_GATES.md`](docs/architecture/QUALI
complexity) must not regress vs `quality-baseline.json`. Update via
`npm run quality:ratchet -- --update` when a metric genuinely improves.
- Job `test-vitest` runs `npm run test:vitest` (MCP tools, autoCombo, cache) — blocking.
`test:vitest:ui` has been blocking since PR #7127.
`test:vitest:ui` is advisory until UI component tests are triaged.
**Allowlist policy (short form):** Fix the cause; use the allowlist only for pre-existing
violations you cannot fix in the same PR. Add a comment with justification + issue number.
@@ -671,18 +662,6 @@ the stale-enforcement added in Fase 6A.3.
22. **Cross-session safety — this repo is worked by MANY parallel sessions/agents at once; never step on another's in-flight work.** Two absolute bans, both recurring incidents (this rule exists because they keep happening):
- **(a) Never `git stash` / `git stash pop` — ANYWHERE in this repo, including inside an isolated worktree, and including inside any subagent you dispatch.** `git stash` operates on the **shared repository object store**, not the per-worktree working tree — so a stash pushed or popped in one session can silently clobber or resurrect another parallel session's uncommitted changes. This is not hypothetical: 2026-07-02 a `#5923` quotaCache change leaked into the unrelated `#2296` worktree via a global `stash pop`, and the same class reincided through a **subagent**. To compare working changes against a base ref **without** stashing, use `git show <ref>:<path>` or `git diff <ref> -- <path>`; to confirm a typecheck/lint error is pre-existing on the base, inspect the base ref directly (`git show origin/release/vX.Y.Z:<path>`) — never stash your tree away to "get it clean". **Put this ban verbatim in the prompt of every subagent that touches git** (agents don't inherit this file's context — the recurrence was a subagent).
- **(b) Never merge, push, rebase, or force-push a PR / branch / worktree that another session is actively working.** An open PR whose head is a live fix worktree in `.claude/worktrees/` you did **not** create (e.g. `fix-5852`/`fix-5923` carrying fresh commits, even when they share your `diegosouzapw` identity), or any branch another session owns, is **off-limits — HOLD**, and let the owning session merge it. **Before** merging or pushing to any PR you did not create _this_ session, run `git worktree list` to check for a matching in-flight worktree and re-check `gh pr view <N> --json state,headRefOid`. Only the owning session merges its own in-flight PR; mid-flight merges race the owner and re-trigger the exact commit/CHANGELOG races Rule #19 and Rule #21 guard against. (Reinforces Rule #19.)
23. **`_tasks/` é INTOCÁVEL como estrutura — append/edit-only.** É um repositório git SEPARADO
(remote privado `diegosouzapw/_tasks_omniroute`) montado como diretório real na raiz do
checkout principal. Regras absolutas: (a) NUNCA mover, renomear, deletar, esvaziar ou
transformar `_tasks` em symlink; sessões só podem CRIAR ou EDITAR arquivos dentro dele;
(b) NUNCA rastrear `_tasks` (nem como symlink) no repo principal — o blob rastreado foi a
causa-raiz de DOIS wipes (2026-08-08 e 2026-08-10: `git reset --hard` materializou o
symlink rastreado por cima do diretório real e o git apagou todo o conteúdo ignorado sem
aviso); (c) após qualquer escrita relevante, `git -C _tasks add -A && git -C _tasks commit
&& git -C _tasks push` — o push frequente é o backup real; (d) repetir esta proibição
VERBATIM no prompt de todo subagente que toque git; (e) se `_tasks` aparecer como symlink
quebrado, NÃO commitar nada — restaurar do remote e avisar o operador. O gate
`check:tracked-artifacts` (pre-commit + CI) bloqueia `_tasks` rastreado em qualquer forma.
---

View File

@@ -1,853 +1,23 @@
# Changelog
## [Unreleased]
### Fixed
- **executors**: fix internal timeout misclassified as client disconnect (499) for 7 niche executors — pass TimeoutError reason to controller.abort() (#8197 side-finding)
---
## [3.8.50] — TBD
_Living section — regenerated 2026-08-12 from all cycle commits (cycle open `ed2db6cb19` → tip). Bullets carry the merged PR and its author; direct pushes listed separately._
_Living section — cycle opened at the v3.8.49 freeze (parallel-cycle model). Bullets are aggregated from `changelog.d/` fragments at each `/generate-release` phase._
### ✨ New Features
- **feat(core):** add Layer A capability filter at router (#5696)
- **feat(providers):** add DeepAI as paid API-key image provider ([#6671](https://github.com/diegosouzapw/OmniRoute/issues/6671))
- **feat(providers):** add Naga.ac and ChatAnywhere aggregator gateway providers (#6674 — thanks @chirag127)
- **feat(api):** add response content encoding verification — confirms Next.js compress:true and documents stripStaleForwardingHeaders behavior ([#6736](https://github.com/diegosouzapw/OmniRoute/issues/6736))
- **feat(api):** add plugins marketplace install endpoint with checksum verification ([#6752](https://github.com/diegosouzapw/OmniRoute/issues/6752))
- **feat(chatgpt-web):** harden prompt-emulated tool contract for thinking models (#7679 — thanks @horacecar)
- **docs:** add management authentication terminology guide ([#7786](https://github.com/diegosouzapw/OmniRoute/issues/7786))
- feat(a2a): Conductor bridge — long-lived SSE consumer that mirrors OmniConductor hub tasks into the A2A TaskManager (explicit `canceled→cancelled` mapping with tests, persisted `last_event_id` cursor in the `key_value` table, exponential-backoff reconnection; opt-in via `CONDUCTOR_HUB_URL`/`CONDUCTOR_HUB_TOKEN`) ([#8080](https://github.com/diegosouzapw/OmniRoute/pull/8080))
- feat(a2a): the Agent Card (`/.well-known/agent.json`) now announces skills derived from the OmniConductor fleet (`GET /v1/runners` OASF capabilities — one skill per online CLI profile + declared fleet skills), cached ~60s and fail-open when the hub is unset/offline ([#8119](https://github.com/diegosouzapw/OmniRoute/pull/8119))
- feat(dashboard): "Conductor" panel — OmniConductor fleet (runners + task queue) live via server-side proxy routes (`/api/conductor/*`, management auth, hub token never reaches the browser), task detail with manifest/council and cancel-with-confirmation; sidebar entry under Tools ([#8221](https://github.com/diegosouzapw/OmniRoute/pull/8221))
- feat(dashboard): Faro chat with voice on the Conductor panel — text via `/api/conductor/ask` (server-side proxy to the spokesperson; hub credential never reaches the browser; `pending` → Sim/Não confirmation buttons) and a guaranteed push-to-talk voice cycle (MediaRecorder → `/api/v1/audio/transcriptions` → ask → `/api/v1/audio/speech` playback), with operator-configurable STT/TTS models ([#8222](https://github.com/diegosouzapw/OmniRoute/pull/8222))
- feat(a2a): inbound delegation to the OmniConductor fleet — `POST /api/a2a/tasks` translates an external A2A task into the hub's `POST /v1/tasks` (fleet skills only, repo required, `CONDUCTOR_ORCHESTRATOR_TOKEN` with hub-token fallback); states flow back through the SSE→A2A mirror ([#8223](https://github.com/diegosouzapw/OmniRoute/pull/8223))
- **docs:** add low-memory/small VPS optimization guide ([#8237](https://github.com/diegosouzapw/OmniRoute/issues/8237))
- **feat(providers):** add connection-level custom upstream headers via `provider_specific_data.customHeaders` — applied to every request through that connection, with model-level headers overriding on the same case-insensitive name. (#8369 — thanks @Benson-mk)
- feat(copilot): add approval gate for runOmniRouteCli commands (#8461)
- feat(ci): add windows-latest leg to test-bun-sqlite job (#8468)
- **feat(electron):** Desktop app can now attach to an already-running OmniRoute server (e.g. a Docker/OrbStack container) instead of always spawning its own bundled server — configurable via the tray's "Remote Server → Connect to Remote Server…" or the `OMNIROUTE_REMOTE_URL` env var ([#8799](https://github.com/diegosouzapw/OmniRoute/pull/8799)) — thanks @soulhakr
- **Database**: The `node:sqlite` fallback now uses SQLite's native backup API and real immediate write transactions, improving backup consistency and concurrent-write behavior when `better-sqlite3` is unavailable ([#8870](https://github.com/diegosouzapw/OmniRoute/pull/8870)) — thanks @artickc
- **feat(models):** add exact per-model `context_length`, `max_input_tokens`, and `max_output_tokens` overrides across model discovery and runtime enforcement, with automatic migration from the retired output-only `max_token` key ([#8908](https://github.com/diegosouzapw/OmniRoute/pull/8908)) — thanks @xz-dev
- **Providers**: expands the Novita AI catalog from a single Llama 3.1 8B entry to 19 curated serving models (DeepSeek V4, Kimi K3, GLM 5.2, MiniMax M3, Qwen3.7 Max, Qwen3 Coder 480B, MiMo V2.5 Pro, gpt-oss-120b, Gemma 4 31B and more), each carrying its real context window, output cap and reasoning flag from the live `/openai/v1/models` listing, and each vision flag confirmed by an actual image request rather than the listing's self-reported modalities ([#8913](https://github.com/diegosouzapw/OmniRoute/pull/8913)) — thanks @jax-novita
- **feat(providers):** native xAI Agent Tools passthrough on `/v1/responses` for `xai` / `xai-oauth` (`xao`) — forward `web_search` + `x_search` to `api.x.ai` instead of rewriting or rejecting them ([#8964](https://github.com/diegosouzapw/OmniRoute/issues/8964))
- **feat(providers): add UnoRouter provider** — UnoRouter is an OpenAI-compatible routing gateway supporting hundreds of models. It is now registered as an API-key provider. ([#8978](https://github.com/diegosouzapw/OmniRoute/issues/8978))
- **feat(sse):** deprecated the legacy `gemini-cli` **upstream provider**. It was not routable (no registry entry, no executor), yet the scheduler kept refreshing its token against Google — maintaining a credential that could never serve a request. A stored connection now becomes terminal with a legible reason and a working migration path: re-add the account under `gemini`, which uses the same Google OAuth client. The `gemini-cli` **client identity** (requests arriving *from* the Gemini CLI, issue #7034) is untouched ([#8980](https://github.com/diegosouzapw/OmniRoute/pull/8980))
- Add a default-off connection setting for Codex, OpenAI, and OpenAI-compatible Responses API providers that preserves client-supplied `reasoning.encrypted_content` items for replay, including per-target combo routing.
- Omit opaque encrypted reasoning values from persisted call logs while retaining compact diagnostic markers. (#9000)
- **feat(providers):** add Regolo AI OpenAI-compatible provider ([#9031](https://github.com/diegosouzapw/OmniRoute/issues/9031))
- **feat(db):** add provider-scoped model aliases that survive rediscovery ([#9068](https://github.com/diegosouzapw/OmniRoute/issues/9068))
- feat(cursor): surface a dismissible dashboard banner suggesting `cursor-agent` installation when it isn't available, so Cursor connections needing periodic manual reconnection aren't a silent surprise (#9173)
- feat(cursor): proactively renew Cursor sessions before their ~24h token expires via the token health-check sweep, nudging `cursor-agent` and re-scraping IDE/agent credential sources so connections stop silently expiring (#9173)
- **feat(codex):** accept parenthesized GPT-5.6 reasoning overrides. (thanks @seakleangnhak) ([#9208](https://github.com/diegosouzapw/OmniRoute/pull/9208))
- **feat(usage):** surface Claude thinking token counts to clients. (thanks @luoyide) ([#9214](https://github.com/diegosouzapw/OmniRoute/pull/9214))
- **feat(ollama):** add Ollama Local embedding support via /v1/embeddings. (thanks @HaoNgo232) ([#9225](https://github.com/diegosouzapw/OmniRoute/pull/9225))
- feat(images): execute full combo strategy + fallback in /v1/images/generations (#9239)
Adds open-sse/services/imageCombo.ts that expands combo targets, filters to images-capable, executes the priority strategy with handleImageGeneration per target, and returns the first success or last failure. Route patches detect combo names before model resolution and divert to the new execution path.
- feat: make forwarded upstream response-header budget configurable via env var (#9243)
- **feat(providers):** filter provider detail connections server-side while preserving full-page search and pagination. (thanks @RobertsXML) ([#9247](https://github.com/diegosouzapw/OmniRoute/pull/9247))
- **feat(providers):** make video_url passthrough configurable per provider/model via compat override ([#9248](https://github.com/diegosouzapw/OmniRoute/issues/9248)) — thanks @HellFiveOsborn
- **feat(gemini):** recursive type:object injection in schema normalizer + empty choices interceptor for streaming (#9268)
- **feat(dashboard):** render a conditional "Get API key" link on the provider detail page, surfaced from the existing `notice.apiKeyUrl` / `notice.signupUrl` catalog metadata (e.g. `pioneer`, `jina`, `together`). The link opens in a new tab and is hidden when neither URL is present, so existing providers are unaffected. Tracks the notice field in `ProviderCatalogMetadata` ([#9270](https://github.com/diegosouzapw/OmniRoute/pull/9270))
- **feat(providers):** accept JSON cookie objects in normalizeSessionCookieHeader (#9284 — thanks @AIB1TAL0S)
- **feat(providers):** support max reasoning effort for opencode-zen DeepSeek models (#9318)
- **feat(providers):** expanded the NanoGPT (`nano-gpt.com`) upstream provider from chat-only to the full OpenAI-compatible endpoint surface: audio transcriptions (`/api/v1/audio/transcriptions`), audio speech (`/api/v1/audio/speech`), video generation (`/api/v1/video/generations`), embeddings (`/v1/embeddings`), and the Responses API (`responsesBaseUrl``/api/v1/responses`) ([#9322](https://github.com/diegosouzapw/OmniRoute/issues/9322))
- **feat(sse):** combo `system_message` supports server-side `{{MODEL_ID}}`, `{{PROVIDER_ID}}`, `{{ACCOUNT}}` and `{{FINGERPRINT}}` template expansion from the actually-routed target ([#5501](https://github.com/diegosouzapw/OmniRoute/issues/5501))
- **feat(sse):** template expansion covers the standard dispatch loop, round-robin and pinned context-cache sessions; fusion, chaos, pipeline and nested-execute strategies do not expand yet ([#5501](https://github.com/diegosouzapw/OmniRoute/issues/5501)) ([#9414](https://github.com/diegosouzapw/OmniRoute/pull/9414)) — thanks @maxmad64bis
- **feat(sse):** New-API/One-API/Sub2API aggregator balance detection for compatible provider nodes — when the "Aggregator Gateway" toggle is enabled, OmniRoute queries the aggregator's `/api/user/self` endpoint to detect the account balance; the dashboard shows a balance badge and quota-preflight routing skips exhausted accounts. Gated by the `NEWAPI_AGGREGATOR_BALANCE` feature flag (default: off), with a custom `quotaPerUnit` override for aggregators that use a different rate than the default 500000 units/$1 ([#9415](https://github.com/diegosouzapw/OmniRoute/issues/9415))
- **feat(catalog):** added opt-in settings `hideAutoCombos` and `hideNoThinkVariants` (default off) to filter built-in `auto/*` virtual combos and `no-think/*` gateway variants from the `/v1/models` catalog — user-defined combos and original provider models stay listed; routing is unaffected ([#9418](https://github.com/diegosouzapw/OmniRoute/issues/9418))
- **feat(opencode-plugin):** added `features.visibleModels` (allowlist) and `features.hiddenModels` (blocklist) to `@omniroute/opencode-plugin` — curate the OpenCode TUI/CLI model picker from 600+ catalog entries down to an operator-defined ID list that persists in `opencode.json` across config resets ([#9473](https://github.com/diegosouzapw/OmniRoute/issues/9473))
- **feat(providers):** add native DeepSeek V4 Flash and Pro thinking-effort aliases for their documented per-model tiers, including Combo Builder exposure ([#9485](https://github.com/diegosouzapw/OmniRoute/pull/9485)).
- feat(opencode-plugin): warm catalog startup from disk snapshot + parallel refresh (#9490)
The config-shim hook now reads the last disk snapshot before fetching, so the provider registers immediately with the last-known-good catalog (~1-2s vs ~30s on a warm gateway). All six fetchers run concurrently via Promise.allSettled instead of sequentially. A failed refresh keeps the snapshot (no overwrite). An in-flight guard prevents concurrent refreshes for the same cache key. The features.diskCache: false opt-out disables the warm read entirely.
- **feat(models):** Test All's "Auto-hide failed models" no longer hides quota errors — daily-quota-exhausted and credits-exhausted responses are now classified via the routing path's existing quota detectors, so an evening Test All on a free-tier provider no longer silently wipes the catalog. Quota results stay visible with a distinct amber badge ([#9511](https://github.com/diegosouzapw/OmniRoute/issues/9511))
- Add an advisory forgotten-sibling-tests report to pull-request quality checks. The report traces changed modules through their static consumers to candidate sibling tests, while keeping barrel and dynamic-import cases non-blocking and requiring reviewed, referenced exceptions. (#9530)
- feat(providers): add Muse Code CLI provider preset (#9544)
- feat(plugins): expose client request headers in plugin onRequest/onResponse context (#9570)
- **feat(plugins):** add onStreamComplete built-in event exposing streaming usage and timing (#9571)
Adds a new `onStreamComplete` plugin event that fires after an SSE stream is fully
consumed, carrying usage token counts and timing metrics (latency, TTFT). Built-in
events now include `onStreamComplete` as a fire-and-forget lifecycle hook.
Payload: `status`, `usage` (prompt_tokens, completion_tokens, reasoning_tokens,
cache_read_input_tokens, cache_creation_input_tokens), `timing` (latencyMs, ttft),
`model`, `provider`, `errorCode`.
Non-breaking — existing `onResponse` hooks with `{ streamed: true }` remain unchanged.
- **feat(audio):** Soniox STT + TTS provider (`sx`) — async speech-to-text (`stt-async-v5`, `stt-async-v4`) and real-time text-to-speech (`tts-rt-v1`) ([#9579](https://github.com/diegosouzapw/OmniRoute/pull/9579))
- Show cache-read and cache-write token counts in request log rows and details when providers
report them. (#9620)
- feat(memory): support custom OpenAI-compatible endpoints for Memory embeddings (#9622)
- feat(resilience): add an opt-in watchdog for persistently slow upstream streams (#9709)
- **Onboarding:** add an explicit, reviewable one-click setup for eligible no-auth LLM providers,
with per-provider caution links, selectable confirmation, idempotent creation, and safe partial
retries. Existing provider connections are never changed and setup completion never enables
providers silently. ([#9752](https://github.com/diegosouzapw/OmniRoute/issues/9752))
- **feat(settings):** add a dedicated Modality Bridge settings page with Vision controls, runtime stats, and URL-addressable Audio and Video tabs ([#9782](https://github.com/diegosouzapw/OmniRoute/pull/9782))
- **feat(modality bridge):** Transcribe chat audio for text-only models through the existing speech-to-text providers, with configurable limits, caching, runtime stats, and a dashboard self-test ([#9807](https://github.com/diegosouzapw/OmniRoute/pull/9807))
- **feat(memory):** `PROVIDERS_SYSTEM_MUST_BE_FIRST` (the #6135/#7293 fix for backends that reject any non-leading `system` message) was hardcoded to `xiaomi-mimo`/`mimo`. Added `OMNIROUTE_STRICT_SYSTEM_PROVIDERS` (comma-separated provider ids) so self-hosted deployments can flag additional strict backends — e.g. a custom OpenAI-compatible connection in front of a self-hosted Qwen3.5+/3.6 model — without forking and rebuilding the image ([#9924](https://github.com/diegosouzapw/OmniRoute/pull/9924))
- **feat(api):** API keys can disable prompt compression from the dashboard, including for clients that cannot send custom headers ([#10001](https://github.com/diegosouzapw/OmniRoute/pull/10001)) — thanks @shixi-li
- **Add cliproxy provider exposure controls and manifest injection** (#7329) — thanks @KooshaPari
- **feat(infra): add a systemd autostart unit for Linux** (#8635)
- **feat(db): add node sqlite adapter parity** ([#8871](https://github.com/diegosouzapw/OmniRoute/pull/8871)) — thanks @epsilonode
- **feat(alibaba): free-tier routing with live quota sync** ([#8893](https://github.com/diegosouzapw/OmniRoute/pull/8893)) — thanks @AndrianBalanescu
- **feat(oauth): add Raycast Pro provider with local auto-import** ([#8895](https://github.com/diegosouzapw/OmniRoute/pull/8895)) — thanks @AndrianBalanescu
- **feat(executors): add isolated Claude Code bridge over Devin ACP** ([#8914](https://github.com/diegosouzapw/OmniRoute/pull/8914)) — thanks @McLuck
- **feat: improve provider quota layouts** ([#8916](https://github.com/diegosouzapw/OmniRoute/pull/8916)) — thanks @apoapostolov
- **feat(mcp): add omniroute_create_combo tool** ([#8925](https://github.com/diegosouzapw/OmniRoute/pull/8925)) — thanks @lucasmellos
- **feat(ci): gate the publish on clean-install AND upgrade-over-previous** ([#8953](https://github.com/diegosouzapw/OmniRoute/pull/8953))
- **feat(providers): add Conol (conol.ai) web session provider** ([#8974](https://github.com/diegosouzapw/OmniRoute/pull/8974)) — thanks @artickc
- **Feat/combo provider wise model test** ([#9011](https://github.com/diegosouzapw/OmniRoute/pull/9011)) — thanks @JoshimOfficial
- **feat(model-alias): add runtime Model Alias Resolver middleware** ([#9020](https://github.com/diegosouzapw/OmniRoute/pull/9020)) — thanks @Egorich-print
- **feat(i18n): complete zh-CN localization for compression engines and dashboard UI** ([#9038](https://github.com/diegosouzapw/OmniRoute/pull/9038)) — thanks @qianze0628
- **feat: Cheaper Inference provider (chat + native Responses + images, sponsor rail 2nd)** ([#9043](https://github.com/diegosouzapw/OmniRoute/pull/9043))
- **feat(providers): add comprehensive support for self-hosted Firecrawl via FIRECRAWL_BASE_URL and custom base URLs** ([#9052](https://github.com/diegosouzapw/OmniRoute/pull/9052)) — thanks @mad-gooze
- **feat(dahl): add manual API key option alongside auto-generated token** ([#9077](https://github.com/diegosouzapw/OmniRoute/pull/9077)) — thanks @pizzav-xyz
- **feat(ci): G0 — reforça o trilho PR→release/**** ([#9108](https://github.com/diegosouzapw/OmniRoute/pull/9108))
- **feat(providers): native xAI Agent Tools passthrough for /v1/responses** ([#9111](https://github.com/diegosouzapw/OmniRoute/pull/9111)) — thanks @VXNCXNX
- **feat(.50): completa itens restantes — G13, G14, gap34, docs, R0.2** ([#9126](https://github.com/diegosouzapw/OmniRoute/pull/9126))
- **feat(g1): rewrite combo-strategy check to runtime-import approach** ([#9131](https://github.com/diegosouzapw/OmniRoute/pull/9131))
- **feat(test:scoped): TIA-based local test runner (#8084 D1)** ([#9143](https://github.com/diegosouzapw/OmniRoute/pull/9143))
- **feat(docker): publish next from active release branches** ([#9181](https://github.com/diegosouzapw/OmniRoute/pull/9181)) — thanks @Zartharas
- **feat(usage): show Grok Build billing limits** ([#9205](https://github.com/diegosouzapw/OmniRoute/pull/9205)) — thanks @xz-dev
- **feat(models): functional gateway mirrors + fix synced-substitution** ([#9217](https://github.com/diegosouzapw/OmniRoute/pull/9217))
- **feat(admission): add adaptive overload protection for LLM routes** ([#9262](https://github.com/diegosouzapw/OmniRoute/pull/9262)) — thanks @xz-dev
- **feat(i18n): update italian translations** ([#9280](https://github.com/diegosouzapw/OmniRoute/pull/9280)) — thanks @Gecky2102
- **feat(dashboard): persist provider screen filters to URL for bookmarking** ([#9307](https://github.com/diegosouzapw/OmniRoute/pull/9307)) — thanks @swingtempo
- **feat(api-manager): add provider-level model permissions** ([#9313](https://github.com/diegosouzapw/OmniRoute/pull/9313)) — thanks @xz-dev
- **feat(warmup): proactive Claude warmup scheduler (#8848)** ([#9449](https://github.com/diegosouzapw/OmniRoute/pull/9449)) — thanks @HouMinXi
- **feat(infra): add systemd autostart unit for Linux (#8635)** ([#9466](https://github.com/diegosouzapw/OmniRoute/pull/9466))
- **feat(lib): make MODELS_DEV_SYNC_ENABLED actually control the sync** ([#9483](https://github.com/diegosouzapw/OmniRoute/pull/9483)) — thanks @HouMinXi
- **feat(radar): flag-gated signed free-model catalog overlay** ([#9515](https://github.com/diegosouzapw/OmniRoute/pull/9515))
- **feat(compression): add Russian language pack** ([#9581](https://github.com/diegosouzapw/OmniRoute/pull/9581)) — thanks @vinogradovnet
- **feat(providers): integrate wave4 free-tier gateways** ([#9584](https://github.com/diegosouzapw/OmniRoute/pull/9584))
- **feat(providers): add Zylo UnoRouter and Poolside registries** ([#9585](https://github.com/diegosouzapw/OmniRoute/pull/9585))
- **feat(providers): add FastRouter AnyAPI and ElectronHub registries** ([#9586](https://github.com/diegosouzapw/OmniRoute/pull/9586))
- **feat(providers): add LLMGateway and LLM Kiwi registries** ([#9587](https://github.com/diegosouzapw/OmniRoute/pull/9587))
- **feat(providers): add FreeInference registry** ([#9594](https://github.com/diegosouzapw/OmniRoute/pull/9594))
- **feat(radar): contributor + supporter claim buttons on the activation screen (F4/T7)** ([#9710](https://github.com/diegosouzapw/OmniRoute/pull/9710))
- **feat(radar): paste-key input on the activation screen (F4)** ([#9758](https://github.com/diegosouzapw/OmniRoute/pull/9758))
- **feat(guardrails): modality bridge core — vision mode/task-aware/cache/input_image + modalityBridge settings** ([#9759](https://github.com/diegosouzapw/OmniRoute/pull/9759))
- **feat(radar): referrals from standalone /v1/referrals feed (no 30-day delay)** ([#9762](https://github.com/diegosouzapw/OmniRoute/pull/9762))
- **feat: generic OpenAI-compatible video custom provider** ([#9844](https://github.com/diegosouzapw/OmniRoute/pull/9844), original [#9818](https://github.com/diegosouzapw/OmniRoute/pull/9818)) — thanks @oyi77
- **feat(logging): make the chat-log truncation limit configurable, bumped default 128x** ([#9863](https://github.com/diegosouzapw/OmniRoute/pull/9863), original [#9738](https://github.com/diegosouzapw/OmniRoute/pull/9738)) — thanks @hartmark
- **feat(logging): bump CHAT_LOG_ARRAY_TAIL_ITEMS default 24 -> 128** ([#9864](https://github.com/diegosouzapw/OmniRoute/pull/9864), original [#9735](https://github.com/diegosouzapw/OmniRoute/pull/9735)) — thanks @hartmark
- **feat(oauth): add Openference OAuth and API key provider integration** ([#9869](https://github.com/diegosouzapw/OmniRoute/pull/9869), original [#9722](https://github.com/diegosouzapw/OmniRoute/pull/9722)) — thanks @AnhLead
- **feat(src): proxy-pool-toolbar-minor-improvements** ([#9870](https://github.com/diegosouzapw/OmniRoute/pull/9870), original [#9718](https://github.com/diegosouzapw/OmniRoute/pull/9718)) — thanks @AgnesRiber
- **feat(resilience): expose providerQuotaOverrides via /api/resilience** ([#9871](https://github.com/diegosouzapw/OmniRoute/pull/9871), original [#9714](https://github.com/diegosouzapw/OmniRoute/pull/9714)) — thanks @herjarsa
- **feat(responses): add encrypted reasoning replay opt-in** ([#9876](https://github.com/diegosouzapw/OmniRoute/pull/9876), original [#9601](https://github.com/diegosouzapw/OmniRoute/pull/9601)) — thanks @jackjinke
- **feat(resilience): add per-account resilience connections view (API + dashboard)** ([#9880](https://github.com/diegosouzapw/OmniRoute/pull/9880), original [#9510](https://github.com/diegosouzapw/OmniRoute/pull/9510)) — thanks @HouMinXi
- **feat(db): add a job registry for scheduled background work** ([#9886](https://github.com/diegosouzapw/OmniRoute/pull/9886), original [#9631](https://github.com/diegosouzapw/OmniRoute/pull/9631)) — thanks @HouMinXi
- **feat(telegram): Mini App chat bridge — initData auth, update webhook, chat proxy** ([#9907](https://github.com/diegosouzapw/OmniRoute/pull/9907), original [#9812](https://github.com/diegosouzapw/OmniRoute/pull/9812)) — thanks @benzntech
- **feat(cursor): exclusive live listing + verbatim AgentRun model ids** ([#9911](https://github.com/diegosouzapw/OmniRoute/pull/9911)) — thanks @yansigit
- **feat(usage): add Command Code quota tracking** ([#9921](https://github.com/diegosouzapw/OmniRoute/pull/9921)) — thanks @yansigit
- **feat(combo): add quota-only priority fallback** ([#9983](https://github.com/diegosouzapw/OmniRoute/pull/9983)) — thanks @xz-dev
- **feat(onboarding): add one-click free provider setup** ([#10014](https://github.com/diegosouzapw/OmniRoute/pull/10014))
- **feat(admission) — direct pushes:** adaptive overload/pressure controls with shared admission wired across the LLM routes, plus mutation-test registration for the capability-filter suite
- **feat(agentrouter) — direct pushes:** support Claude and Codex protocols — infer the protocol from the client endpoint and honor the alternate protocol through the chat pipeline
- **feat(providers) — direct pushes:** ChatGPT Web session credential guide with a Cookie Editor fast-path (canonical chromewebstore install link) and web-session fast-path test coverage
### 🐛 Bug Fixes
- **executors**: fix internal timeout misclassified as client disconnect (499) for 7 niche executors — pass TimeoutError reason to controller.abort() (#8197 side-finding)
- test(combo): guard auto/best-free never leaks the combo name as a model (#7754)
- fix(vision-bridge): describe-model no longer returns unreachable "openai/gpt-4o-mini" when every vision-capable provider is unreachable on the instance — returns null instead and surfaces a clear error (#8430)
- fix(vision-bridge): validate fixedModel against usable credentials before short-circuiting in getBestVisionModel, so the default "openai/gpt-4o-mini" is not unconditionally selected when no OpenAI connection exists (#8430)
- fix(vision-bridge): in the combo describe path, replace raw images with an error text stub when all describe attempts fail, instead of forwarding images to a confirmed non-vision backend that would reject them with an opaque serde error (#8430)
- fix(quality): add base-relative file-size check so inherited drift does not red innocent PRs (#8522)
- fix(ci): aggregate all fast-gates into non-fail-fast loop so one red gate no longer masks later gates (#8542)
- fix(tests): make machineId tests macOS-compatible by stubbing ioreg in test helper (#8577)
- fix(scripts): replace bash 4+ readarray with compatible while-read loop in restore-policies.sh (#8577)
- fix(cli): enable systray2 on Windows for Norton-friendly tray (#8609)
- fix(executor): guard claude/anthropic buildHeaders against empty credentials and extend dual-Bearer parity for third-party baseUrls (#8653)
- fix(providers): gate premium opencode-zen/opencode-go models behind an API key (#8681)
- **fix(api):** make `/v1/models` stale refresh response-safe and generation-safe, with narrow synced-model invalidation ([#8728](https://github.com/diegosouzapw/OmniRoute/pull/8728)). Related to #8697.
- fix(yuanbao-web): accept `content` field in SSE text events (upstream format change) (#8739)
- fix(build): remove misleading open-sse/package.json facade and add workspace typecheck gate (#8781)
- fix(errorClassifier): classify ChatGPT Web SENTINEL_BLOCKED 403 as terminal FORBIDDEN, enabling proper combo fallback (#8813)
- fix(cli): fall back to node:sqlite when better-sqlite3 constructor throws at runtime (#8826)
- fix(opencode): prefix provider id with "opencode-" for auth login command (#8830)
- fix(opencode-zen): add current free-tier models to registry to enable combo context pre-filtering (#8841)
- **fix(api):** Let image and video providers enforce their own request-size limits instead of rejecting media payloads at OmniRoute's 10 MB global default ([#8843](https://github.com/diegosouzapw/OmniRoute/pull/8843)) — thanks @artickc
- fix(build): include better-sqlite3 prebuilds in standalone bun bundle (#8847)
- fix(proxy-health): include credentials in proxy health check URLs (#8853)
- **fix(build):** `prepublish` no longer spawns the Windows `.cmd` shims for npm/npx, which Node >= 20 refuses to launch without a shell (`EINVAL`). On Node 24 that silently skipped the MITM utilities, the MCP server bundle, the LLMLingua ONNX worker and `@omniroute/opencode-plugin` while `build:cli` still exited 0 and reported success. Build tools are now resolved to their own JS entry point and run with the current Node binary — no shim, no shell, no unescaped arguments. (thanks @maisdesign) ([#8858](https://github.com/diegosouzapw/OmniRoute/pull/8858)) — thanks @maisdesign
- **fix(opencode):** generate schema-complete model limits so OpenCode accepts catalog entries without an explicit output cap ([#8869](https://github.com/diegosouzapw/OmniRoute/pull/8869)) — thanks @xiaoyaner0201
- **fix(cli):** default omitted Codex CLI wire API settings to Responses and clear stale Chat state after reset ([#8876](https://github.com/diegosouzapw/OmniRoute/pull/8876)) — thanks @xiaoyaner0201
- **fix(proxy):** isolate new proxy credential fields from browser and password-manager autofill after form reset ([#8883](https://github.com/diegosouzapw/OmniRoute/pull/8883)) — thanks @xiaoyaner0201
- fix(db): invalidate stale LKGP pins when provider connections are deleted (#8887)
- fix(tests): update stale nightly compat fixtures and goldens to match current source constants (#8901)
- **fix(quota):** Deleting a quota pool now removes its scoped managed combos without racing in-flight pool mutations ([#8906](https://github.com/diegosouzapw/OmniRoute/pull/8906)) — thanks @xiaoyaner0201
- **fix(executors):** Vertex AI now routes Claude models through the native Anthropic `rawPredict` endpoint instead of the generic OpenAI-compatible partner endpoint, and synthesizes a real streaming response so Claude-via-Vertex works with `stream: true` ([#8909](https://github.com/diegosouzapw/OmniRoute/pull/8909)) — thanks @wgordon17
- **fix(providers):** expose both OAuth Connect and manual API-key actions for dual-auth providers such as CodeBuddy CN ([#8921](https://github.com/diegosouzapw/OmniRoute/pull/8921)) — thanks @Llliao1113
- fix(compression): drop orphan custom_tool_call/local_shell_call/apply_patch_call on compaction restore (#8946)
- fix(auth): setting first dashboard login password no longer fails with HTTP 400 PASSWORD_REQUIRED (#8950)
- fix(github): add targetFormat to GPT-5.6 Sol/Terra/Luna models (#8951)
- fix(auto-update): skip synthetic Next.js standalone package.json without `name` field in resolveProjectRoot (#8956)
- fix(opencode): propagate vision capability from live catalog into opencode.json (#8960)
- fix(providers): switch Antigravity quota RPCs to iterate ANTIGRAVITY_RUNTIME_BASE_URLS (#8965)
- **fix(oauth):** GHE Copilot OAuth lifecycle — connecting an account and refreshing its token both failed. Adding a connection died with `gheUrl is required for GHE Copilot OAuth` because the poll handler's `ghe-copilot` branch was unreachable dead code: the provider is listed in `NO_PKCE_DEVICE_CODE_PROVIDERS`, and that set-based check ran first, calling `pollForToken()` without the `extraData` carrying `gheUrl`. Separately, every manual `Refresh` click surfaced `Token refresh failed — provider returned no new token`, and the proactive pre-request refresh never fired for GHE connections — the manual route, the health-check sweep and `checkAndRefreshToken()` all still special-cased plain `github`, while GHE Copilot's device-code flow never yields a `refresh_token` (only a GitHub access token plus a short-lived Copilot sub-token). `refreshCopilotToken()` now takes an optional `baseUrl` so it can target a GHE host's `<gheUrl>/api/v3` Copilot token endpoint, and `ghe-copilot` is wired in alongside `github` at all four sites.
- **fix(health-check):** the access-token-only branch of the token health-check sweep no longer logs an unconditional `has no refresh token but has a GitHub access token` line on every tick. That path runs once per 60 s sweep for every `github` / `ghe-copilot` connection, so it emitted ~1440 identical entries per day per connection reporting that nothing had changed. It now logs only when the sweep actually attempted a Copilot sub-token refresh, and says whether that refresh succeeded or failed — so a genuine failure still surfaces instead of being buried in steady-state noise. ([#8970](https://github.com/diegosouzapw/OmniRoute/pull/8970)) — thanks @hppsc1215
- fix(providers): copilot-m365-web enterprise turns send disconnectBehavior=continue (#8971)
- **fix(ci):** the reconciliation helper no longer bounds its scan with `git describe --tags` — releases squash-merge, so that range re-listed 1361 commits instead of the cycle's real 22, which is how ~200 PRs once slipped through without a changelog bullet. The base is now resolved from the commit that opened the cycle, and a new `sweep:stale-fragments` gate removes `changelog.d/` fragments that a back-merge from `main` resurrected after they had already been folded in ([#8985](https://github.com/diegosouzapw/OmniRoute/pull/8985))
- **fix(ci):** fixed a live auto-update defect where **Intel Macs downloaded the ARM dmg** — the two macOS jobs each emitted their own `latest-mac.yml` and `merge-multiple` let one silently overwrite the other by arrival order, leaving `electron-updater`'s arch fallback pointing at the wrong build. The manifests are now merged deliberately, un-suffixed entry first. Also: test jobs pinned to hosted runners (`setup-node` measured 20m06s self-hosted vs 16s hosted), the npm publish no longer discards a valid build artifact because an unrelated shard was flaky, the agent-skills gate now runs on pushes to `main` instead of PRs only, and the CI summary names every job that ended cancelled ([#8988](https://github.com/diegosouzapw/OmniRoute/pull/8988))
- **fix(sse):** `stripResponsesLifecycleEcho` no longer strips `tools` from the `response.completed` snapshot — that terminal event is what Codex CLI rebuilds its tool list from, so stripping it left the client with zero tools. `tools` is still stripped from `response.created`/`response.in_progress`, and `instructions` (the >100KB size lever) is still stripped from all three ([#8990](https://github.com/diegosouzapw/OmniRoute/pull/8990))
- fix(vertex): route Claude models to native rawPredict endpoint and respect custom targetFormat overrides (#8994)
- fix(proxies): resolveProxyForConnection now returns the proxy name, so the dashboard badge shows the name instead of the hostname (#8995)
- **fix(translator):** the Responses-to-Chat promotion path called `normalizeResponsesReasoningEffort` without the model argument, so GPT-5.6 Sol/Terra/Luna requests with `reasoning.effort: "max""` were downgraded to `"xhigh"`. The model is now threaded through, preserving `max` for GPT-5.6 while keeping the legacy downgrade for older models ([#8997](https://github.com/diegosouzapw/OmniRoute/pull/8997))
- **fix(sse):** Claude reasoning-effort suffix ids (`-high`/`-low`/`-medium`/`-xhigh`) now strip
correctly on any provider serving a real Claude model, not just the direct Anthropic provider
([#9006](https://github.com/diegosouzapw/OmniRoute/pull/9006))
- **fix(sse):** the no-thinking (`no-think/`) catalog variant's provider-qualification bug — which
made it unusable outside the direct provider, both in the discovery catalog and the dashboard
playground — is fixed ([#9006](https://github.com/diegosouzapw/OmniRoute/pull/9006))
- **fix(sse):** a single unrecognized model id on a Vertex connection no longer cools down every
other model on that connection for 2 minutes — Vertex 404s are now scoped to a per-model
lockout via `passthroughModels` instead of a connection-wide cooldown
([#9006](https://github.com/diegosouzapw/OmniRoute/pull/9006))
- **fix(sse):** Vertex `PERMISSION_DENIED` 403s are now disambiguated using Google's own
documented error format — a genuinely connection-wide cause (API disabled, project-level IAM
denial) still cools the whole connection, while a model-specific denial locks out only that
model ([#9006](https://github.com/diegosouzapw/OmniRoute/pull/9006))
- **fix(sse):** error-only streams now preserve sanitized executor diagnostics for operators without changing stream-readiness fallback classification ([#9022](https://github.com/diegosouzapw/OmniRoute/pull/9022)) — thanks @shixi-li
- fix(cursor): preserve tool context across multi-turn conversations when client lacks conversation_id (#9029)
- fix(sse): move Antigravity client system content to first user message to avoid upstream 429 RESOURCE_EXHAUSTED on oversized systemInstruction (#9030)
- fix(auth): IP blacklist now blocks on direct connections via trusted peer stamp and re-reads config without restart (#9033)
- fix(api): use configured prefix instead of raw node UUID for alias-backed model id in /v1/models (#9034)
- **fix(resilience):** Detect and reset idle-capacity rate-limit queue wedges on an eligible watchdog scan so routing can fall back promptly ([#9041](https://github.com/diegosouzapw/OmniRoute/pull/9041))
- fix(db): stream DB backup export instead of buffering entire file into memory (#9045)
- fix(ui): normalize Free Pool API response payload to read from data.proxies (#9046)
- **fix(translator):** pass `output_config.effort="max"` through verbatim instead of unconditionally rewriting it to `xhigh`, so Anthropic → OpenAI-shape upstream calls reach `sanitizeReasoningEffortForProvider` with the carrier intact and providers that accept `max` literally (Ollama Cloud, opencode-go DeepSeek, Moonshot K3, native Claude) no longer 400 on `invalid reasoning value: 'xhigh'`. Regression guard: end-to-end test in `tests/unit/base-executor-sanitize-effort.test.ts`. ([#9053](https://github.com/diegosouzapw/OmniRoute/pull/9053)) — thanks @ikelvingo
- fix(api/analytics): stop charging :free models at arbitrary fallback price (#9054)
- fix(api): auto/* routing aliases bypass API-key allowedConnections/disableNonPublicModels (#9057)
- fix(providers): anthropic strips code-execution/skills beta flag, causing container rejection (#9064)
- **fix(batches):** `GET /v1/batches` now validates the `limit` query param instead of passing `Number.parseInt(limit)` straight to the SQLite `LIMIT` bind. Previously `?limit=abc` threw an unhandled `datatype mismatch` (→ HTTP 500), `?limit=-1`/`0` returned an incoherent `has_more:true` empty page with `last_id:null`, and a large `?limit` read the entire `batches` table into memory. It now returns a `400` for any non-integer or out-of-range value (1100, default 20), matching the `POST` handler's Zod validation and the OpenAI Batches contract ([#9073](https://github.com/diegosouzapw/OmniRoute/pull/9073))
- **fix(a2a):** the A2A JSON-RPC router now compares the bearer token against `OMNIROUTE_API_KEY` in constant time (`crypto.timingSafeEqual`) instead of `===`, closing a token-length timing side-channel, and no longer logs the request URL to server logs on every call ([#9083](https://github.com/diegosouzapw/OmniRoute/pull/9083))
- **fix(api/skills):** the `/api/skills/**` routes now run caught error messages through `sanitizeErrorMessage` before returning them, so a filesystem failure no longer leaks an absolute path (e.g. `/home/<user>/.omniroute/skills/...`) to the client; the `{ error: string }` response shape is preserved for the dashboard ([#9088](https://github.com/diegosouzapw/OmniRoute/pull/9088))
- fix(providers): admit audio-speech/audio-transcriptions apiType in audio route provider-node filters (#9096)
- fix(providers): modal.com validation returns clear error when Base URL is missing, instead of leaking "Invalid outbound URL" (#9102)
- **fix(providers):** GitHub Copilot no longer re-imports or routes cached Gemini 2.5 Pro and Gemini 3 Flash model IDs after their retirement ([#9103](https://github.com/diegosouzapw/OmniRoute/pull/9103))
- fix(providers): resolve combo names in audio transcriptions route so /v1/models stays honest (#9134)
- fix(vscode): allow built-in auto-routing models in VS Code model filter (#9140)
- fix(background): detect Anthropic top-level system prompts for background task detection (#9142)
- **fix(dashboard):** the provider "Auto Sync" toggle now applies to every active connection and each connection gets its own Auto Sync toggle — previously only the lowest-priority connection was updated. ([#9149](https://github.com/diegosouzapw/OmniRoute/pull/9149))
- fix(cli): use process.execPath for macOS launchd autostart (#9156)
- fix(management): authorize mcp:connect-only keys on loopback/LAN when requireLogin is enabled (#9159)
- fix(model-discovery): ingest capabilities.effort_tiers for synced models (#9160)
- **fix(translator):** Honor configured Chat targets for Responses-shaped clients while preserving native Responses providers and outbound token fields ([#9161](https://github.com/diegosouzapw/OmniRoute/pull/9161)) — thanks @Zartharas
- fix(translator): buffer and normalize upstream tool-call argument deltas so optional null values are stripped before reaching the client (#9168)
- fix(cursor): the manual "Refresh" button on Cursor connections now calls the dedicated Cursor renewal route instead of silently returning a 502 every time (#9173)
- fix(translator): avoid double-normalizing tool names in Gemini-to-Claude response path (#9177)
- **fix(dashboard):** the "Default Model" of an OpenAI-compatible connection is now visible and editable after creation (was set once, then invisible), and it is no longer required when creating a connection — matching the API which always treated it as optional. ([#9179](https://github.com/diegosouzapw/OmniRoute/pull/9179))
- **fix(db):** honor the `ENABLE_REQUEST_LOGS` environment override for detailed request persistence. (thanks @RobertsXML) ([#9187](https://github.com/diegosouzapw/OmniRoute/pull/9187))
- **fix(model):** normalize client context-window suffixes for combo routing. (thanks @b1nhm1nh) ([#9193](https://github.com/diegosouzapw/OmniRoute/pull/9193))
- fix(catalog): repair dead guard and synced-first ordering for custom model Vision capable override (#9195)
- fix(routing): consult customModels supportsVision flag in Combo vision filter (#9195)
- **fix(models):** Preserve published model catalogs during session-affinity bookkeeping so routine affinity updates do not force unnecessary cold rebuilds ([#9199](https://github.com/diegosouzapw/OmniRoute/pull/9199)) — thanks @xz-dev
- **fix(models):** Reuse one build-local virtual-auto candidate snapshot across built-in catalog entries and cooperatively yield during cold catalog generation, while detaching invalidated in-flight generations so policy changes cannot publish stale results ([#9199](https://github.com/diegosouzapw/OmniRoute/pull/9199)) — thanks @xz-dev
- **fix(models):** Resolve token limits and model capabilities once per unique candidate in that build-local snapshot, eliminating repeated SQLite lookups across the 38 built-in auto entries while preserving fresh runtime preparation and hard invalidation ([#9199](https://github.com/diegosouzapw/OmniRoute/pull/9199)) — thanks @xz-dev
- **fix(models):** Read and parse models.dev pricing once per cold catalog build, then yield before final enrichment so queued health checks are not starved while every model in that build shares one coherent pricing snapshot ([#9199](https://github.com/diegosouzapw/OmniRoute/pull/9199)) — thanks @xz-dev
- **fix(compression):** honor the global compression-off setting for proactive and last-resort context compaction, preventing disabled compression from rewriting tool-call histories ([#9200](https://github.com/diegosouzapw/OmniRoute/pull/9200)) — thanks @joachimBrindeau
- fix(web-search): bind each search provider attempt to its connection proxy (#9201)
- fix(auth): make antigravity and agy equivalent in credential selection (#9204)
- **fix(cli):** prefer IPv4 DNS for spawned Node servers. (thanks @dsitmilis) ([#9209](https://github.com/diegosouzapw/OmniRoute/pull/9209))
- **fix(pricing):** stop billing reasoning tokens twice. (thanks @yidecode) ([#9212](https://github.com/diegosouzapw/OmniRoute/pull/9212))
- **fix(models):** `/v1/models` now publishes one contiguous provider-grouped block per provider instead of interleaved fragments. The catalog is assembled by many independent push loops (auto-combos, named combos, static registry, codex-native, synced, OpenRouter, specialty, custom, alias-backed, connection-fallback), so one provider's models previously landed in several separated blocks. A single stable, provider-grouped sort is applied at serialization, keyed by `owned_by` (canonical owner identity) rather than the model-id prefix — so a single routable public prefix that differs from its owner (e.g. no-auth OpenCode publishing `oc/<model>` while keeping `owned_by: "opencode"`) stays contiguous. Combos are pinned first (preserving #4164); then providers in registry precedence (OAuth → NoAuth → API-key); then unknown providers in locale-independent code-unit order. The sort is stable and pure (reorders rows only, no mutation, no DB/IO), preserving combo `sort_order`, connection priority, custom append-order, and equal-id audio twins ([#9215](https://github.com/diegosouzapw/OmniRoute/pull/9215)) — thanks @nguyenha935
- **fix(combo):** the Combo "Add model" picker now respects hidden-model visibility for every model source — system catalog, fallback, passthrough/node aliases, custom rows and auto-fetched models — instead of drowning the list in 500+ unavailable entries ([#9218](https://github.com/diegosouzapw/OmniRoute/pull/9218)) — thanks @szzhoujiarui
- **fix(codex):** normalize additional_tools passthrough items. (thanks @SalyyS1) ([#9219](https://github.com/diegosouzapw/OmniRoute/pull/9219))
- **fix(codex):** preserve quota window duration in usage shape. (thanks @HectorBernstorff) ([#9222](https://github.com/diegosouzapw/OmniRoute/pull/9222))
- **fix(azure):** normalize GPT-5 chat completion parameters. (thanks @royanrosyad85) ([#9223](https://github.com/diegosouzapw/OmniRoute/pull/9223))
- **fix(codex):** strip orphaned tool outputs from compacted conversations. (thanks @raflyazf) ([#9228](https://github.com/diegosouzapw/OmniRoute/pull/9228))
- **fix(i18n):** completed the French UI catalog by adding all missing keys and replacing every placeholder translation ([#9235](https://github.com/diegosouzapw/OmniRoute/pull/9235)) — thanks @alex-jordan547
- **fix(nvidia):** normalize tool names and call ids for NVIDIA compatibility. (thanks @minhnhat166) ([#9236](https://github.com/diegosouzapw/OmniRoute/pull/9236))
- fix(lmarena): emit Uint8Array SSE chunks instead of strings to satisfy shared pipeline contract (#9237)
- **fix(db):** `validateRegisteredKey` no longer rejects the first request of a fresh budget window when the previous window's usage already met the daily/hourly budget — the reset `UPDATE` zeroed the counters in the DB but the budget check still read the stale pre-reset snapshot, so the reset is now mirrored into the row before checking ([#9241](https://github.com/diegosouzapw/OmniRoute/pull/9241))
- **fix(i18n):** localized hardcoded web UI copy across public pages, dashboard views, and shared components, with complete French and Vietnamese coverage ([#9245](https://github.com/diegosouzapw/OmniRoute/pull/9245)) — thanks @alex-jordan547
- **fix(db):** deleting a provider connection (single, batch, or provider-scoped) now purges its account-scoped `proxy_assignments` rows inside an atomic transaction — no more orphan assignments pointing at deleted connections ([#9246](https://github.com/diegosouzapw/OmniRoute/pull/9246)) — thanks @maxmad64bis
- **fix(cli-tools):** keep Apply enabled for active OpenAI-compatible and Anthropic-compatible providers without static catalog entries. (thanks @lazysaltyfish) ([#9250](https://github.com/diegosouzapw/OmniRoute/pull/9250))
- **fix(translator):** harden Claude format detection for relative message endpoints and kebab-case version metadata. (thanks @ervareza) ([#9253](https://github.com/diegosouzapw/OmniRoute/pull/9253))
- **fix(minimax):** add the required empty signature placeholder to unsigned thinking block starts. (thanks @rixzkiye) ([#9256](https://github.com/diegosouzapw/OmniRoute/pull/9256))
- **fix(resilience):** Enforce RPM limits with rolling leases and atomic global/provider/account admission ([#9259](https://github.com/diegosouzapw/OmniRoute/pull/9259)). The configured global RPM budget is shared across all enabled provider connections within one process; provider/account overrides add narrower scopes.
- **fix(classify429):** add missing `have exhausted their quota` pattern so the synthetic 429 from auth.ts is recognized as quota exhaustion, preventing the combo loop from burning retries against the same provider instead of falling back to a healthy one ([#9269](https://github.com/diegosouzapw/OmniRoute/issues/9269))
- fix(claude): remove unconditional "always" return in claudeClassifierCompat so normal chat requests are not swallowed (#9276)
- fix(qoder): include actionable CLI_QODER_BIN hint in connection test when qodercli is not found (#9277)
- **fix(providers):** the web search fallback detector in `webSearchFallback.ts` used an exact `Set` (`web_search`, `web_search_preview`) that missed Anthropic's date-suffixed server-tool variant `web_search_20250305` (sent by Claude Code 2.1.220+). Changed to prefix regex `/^web_search/`, matching the two other detectors in the codebase, so the fallback intercepts versioned web search tools for OpenAI-compatible upstreams ([#9279](https://github.com/diegosouzapw/OmniRoute/pull/9279))
- **fix(docker):** the bundled Redis sidecar no longer publishes on `0.0.0.0`. `docker-compose.yml`, `omniroute redis up` and the dashboard's 1-click launcher all built an unqualified `-p <port>:6379` spec, which the container runtime expands to every interface — and none of them sets `requirepass`, so any host on the LAN could reach the rate-limiter/cache store. All three now default to `127.0.0.1`, with exposure opt-in via `REDIS_BIND_HOST` (compose), `--bind` (CLI) and `OMNIROUTE_REDIS_BIND_HOST` (launcher); the CLI warns when a non-loopback bind is requested without `--password` ([#9286](https://github.com/diegosouzapw/OmniRoute/pull/9286))
- fix(credential-health): scheduler never retries failed connections due to static interval comparison (#9289)
- **fix(db):** persist the account egress IP into `proxy_logs.egress_ip` (migration 134 + schema reconciler) so real traffic stays attributable to the actual node/IP even after restart — the egress IP was previously computed and logged but silently dropped from persistence ([#9291](https://github.com/diegosouzapw/OmniRoute/pull/9291)) — thanks @maxmad64bis
- fix(api): specialty model catalog ignores hidden OpenRouter model flags (#9293)
- fix(mcp): break circular import between googApiKeyAuth.ts and auth.ts to fix esbuild SyntaxError in MCP server bundle (#9297)
- fix(catalog): cache getModelsDevPricing() to prevent OOM at startup (#9300)
- fix(providers): bump qwen-web SPA version header from 0.2.66 to 0.2.81 (#9304)
- fix(sse): broaden OMNIROUTE_SSE_COMMENTS to accept 'false','0','no' and gate metadata comment emission (#9305)
- fix(lmarena): encode SSE stream chunks as Uint8Array to prevent TextDecoder TypeError (#9306)
- **fix(claude):** reconcile compacted tool results against the preceding tool use. (thanks @ryanngit) ([#9308](https://github.com/diegosouzapw/OmniRoute/pull/9308))
- **fix(kiro):** validate completed nested tool-call payloads before forwarding them. (thanks @SemonCat) ([#9314](https://github.com/diegosouzapw/OmniRoute/pull/9314))
- fix(backend): use accumulated responseBody for provider payload in dashboard log viewer to avoid stale data from truncated SSE events (#9315)
- fix(qoder): surface qodercli stderr in error message instead of generic 502 (#9319)
- fix(security): require auth for /v1/models when management auth is configured (#9320)
- **fix(claude):** normalize nested Claude server tool model ids (`cc/` and `claude/` prefixes) on native passthrough, covering non-versioned server tools (Task/subagent). (thanks @AlanSyue) ([#9332](https://github.com/diegosouzapw/OmniRoute/pull/9332))
- fix(providers): map kimi-web/K3 to K2D5 scenario instead of OK Computer premium mode to fix resource_exhausted on non-subscriber accounts (#9338)
- fix(security): require explicit tool envelope to prevent bare JSON from being promoted to real tool_calls (#9343)
- fix(api): consult LiteLLM pricing_synced layer in resolveCatalogPricing so deployed models absent from models.dev and defaults get pricing in /v1/models (#9364)
- fix(providers): treat claude-web 429 as unhealthy and forward upstream Retry-After header (#9406)
- fix(providers): treat muse-spark-web 429 as unhealthy (#9406)
- fix(providers): detect expired gemini-web sessions via ServiceLogin redirect and add testConnection override (#9407)
- fix(providers): add tool_use block handling to claude-web stream parser for OpenAI tool_calls projection (#9408)
- fix(api): fall back to slugified provider name when prefix is empty to prevent UUID leak in /v1/models (#9416)
- **fix(providers):** Codex GPT-5.6 model metadata reports the 1M context window and 922K input limit ([#9431](https://github.com/diegosouzapw/OmniRoute/issues/9431)).
- fix(oauth): Kiro import token endpoint no longer overwrites existing connection when using shared cached OIDC clientId (#9435)
- **fix(sse):** hoisting a mid-conversation `system`/`developer` message into the top-level `system` field no longer carries its `cache_control` marker along, which left the conversation history without a cache breakpoint and forced a full re-read plus a rebuild on the next turn. The boundary is moved to the nearest preceding block that can carry one, and now survives the rewrites that turn `tool_result` and inlined file/document blocks into plain text; if the target block is already marked, both markers are kept unless Anthropic's TTL ordering forbids it. Both hoisting paths are fixed — `extractSystemRoleMessages` and `extractSystemMessagesToBody`. Regression guard: `tests/unit/claude-system-role-cache-boundary.test.ts`. ([#9436](https://github.com/diegosouzapw/OmniRoute/issues/9436))
- fix(backend): force system MITM CA cert to 0644 on Linux regardless of umask and repair on re-install (#9442)
- **fix(routing):** a Codex-native bare model id (`gpt-5.5`, the `gpt-5.6-sol`/`terra`/`luna` tiers) no longer routes to `codex` when no codex connection is active — an OpenAI-only install was getting `no active credentials for provider: codex` for a model OpenAI serves, and an install whose codex connection was merely inactive failed the same way. With codex active the Codex preference still wins over OpenAI, and ids only codex catalogs (`codex-auto-review`) still resolve to codex with no connection at all ([#9447](https://github.com/diegosouzapw/OmniRoute/pull/9447))
- fix(docker): ship MITM `_internal/` shims and `selfsigned` package in standalone bundle (#9451)
- fix(cli): probe PATH for claude.exe/codex.exe on Windows before falling back to the .cmd shim (#9454)
- fix(cli): stop the supervisor before the child so omniroute stop no longer reports success while the supervisor respawns the server (#9455)
- fix(cli): route claude-code OAuth to the Anthropic `claude` browser-PKCE flow instead of the unrelated command-code provider (#9474)
- fix(cli): re-verify running binary version after `omniroute update` install and warn instead of lying about success when a local install shadows the global one (#9475)
- fix(providers): classify 400 out of extra usage as quota_exhausted for Anthropic OAuth (#9486)
- **fix(auth):** redirect active sessions from /login by checking the session cookie before showing the login form. (thanks @DaDecky) ([#9491](https://github.com/diegosouzapw/OmniRoute/pull/9491))
- fix(api): make the 800-message chat history cap opt-in so long conversations reach compression instead of a terminal 413 (#9494)
- fix(translator): preserve authentic K3 Responses reasoning by model across providers, keep it on the matching assistant turn, and make Kimi Coding prefer client reasoning then cached replay before its empty-marker fallback (#9496)
- fix(translator): join reasoning summary segments with newline separators (#9500)
- fix(muse-spark-web): document the ecto1: WS auth token requirement in the credential hint, spec, and error message (#9502)
- fix(sse): stop force-injecting advanced-tool-use beta via the effort-2025-11-24 gate; forward client-negotiated effort through the allowlist (#9505)
- fix(sse): stop the reasoning-token buffer from enlarging a client's explicit max_tokens upward (x1.5) (#9507)
- fix(ci): include combo-matrix tests in test-integration job (#9531)
- **fix(ci):** tighten unit suite ceiling from 100min to 80min as a conservative step (#9532)
- **fix(ratelimit):** added queue-wait timeout tests and updateFromResponseBody sequencing tests for the existing RATE_LIMIT_QUEUE_TIMEOUT feature in withRateLimit (#9533)
- fix(test): prevent flaky modelsDevSync timer assertions by serializing test execution within the file (#9534)
- fix(backend): map cache tokens in OpenAI-to-Claude non-streaming usage translation (#9536)
- fix(db): add transient-error retry to corruption probe to prevent data loss under concurrent load (#9541)
- fix(search): mark searxng-search as fallbackOnly to prevent auto-select without instance (#9543)
- fix(providers): strip provider prefix in getModelTargetFormat to route GPT-5.6 models to /v1/responses (#9545)
- fix(model): add "aq" alias for amazon-q provider so parseModel resolves it instead of falling back to OpenAI (#9550)
- fix(proxy): NO_PROXY now bypasses context-level proxy in resolveProxyForRequest (#9551)
- **fix(build):** `npm run build:cli` (prepublish) no longer fails on POSIX with "npm-cli.js not found next to the running Node binary". The #8858 shim-free npm resolver only knew the Windows layout (`<dir(node.exe)>\node_modules\npm`); on GitHub hosted runners, nvm and system installs npm lives at `<prefix>/lib/node_modules/npm` while node is `<prefix>/bin/node`, so every fresh CI checkout died installing `@omniroute/opencode-plugin` deps (Fast Production Build + dast-smoke red on all PRs). The resolver, extracted to `scripts/build/resolveNpmEntry.ts`, now tries `npm_execpath` (exported by `npm run` itself) first, then the Windows layout, then the POSIX layout — covered by `tests/unit/build/resolve-npm-entry.test.ts` including a live POSIX regression guard. ([#9553](https://github.com/diegosouzapw/OmniRoute/pull/9553))
- **fix(quality):** reconcile the accumulated file-size drift on `release/v3.8.50` — 13 files sat above their frozen LOC on the clean tip (measured by the gate itself), turning the absolute-mode check (nightly / local) permanently red while the PR-mode base-relative check (#8522) let every innocent PR pass. The per-PR rebaselines were lost across successive conflict resolutions of this hot file during the 08-05/06 merge batch. Frozen values updated to the measured tip for the 11 grown files (each annotated with its owning merged PR: #9024 #9324 #9329 #9193 #9332 #9228 #9260 #8934 #9196 #9163) and `open-sse/executors/default.ts` / `kiro.ts` (above the 1000 cap with no entry) added to the frozen set. ([#9554](https://github.com/diegosouzapw/OmniRoute/pull/9554))
- fix(build): exec native esbuild binary directly in prepublish — esbuild ≥0.25 ships an ELF at bin/esbuild and running it through node crashed every build:cli (dast-smoke red on all PRs) ([#9558](https://github.com/diegosouzapw/OmniRoute/pull/9558))
- **fix(mcp):** the 3 `audit.test.ts` shutdown/fallback tests fail deterministically since #8959 switched the audit DB loader to `createRequire("better-sqlite3")``vi.doMock` only patches Vitest's ESM module graph, so the old better-sqlite3 mock never engaged and the tests hit a real empty sqlite file ("no such table: mcp_tool_audit"), redding the `Vitest (fast-path)` job on every PR (long misdiagnosed as a flake). Shutdown tests now inject the mock through the audit connection cache (`globalThis.__omnirouteMcpAuditDb`), and the node:sqlite fallback test drives a new test-only loader seam (`__setBetterSqliteLoaderForTests`) — the production `createRequire` path is unchanged. 3/3 red → 3/3 green; full `open-sse/mcp-server` vitest suite 88/88. ([#9559](https://github.com/diegosouzapw/OmniRoute/pull/9559))
- fix(build): lazy-resolve module-level fs paths to avoid Turbopack NFT whole-source trace (#9560)
- fix(sse): replace timer-based waits with polling to fix flaky chatCore/SSE tests under CI load (#9567)
- **fix(translator):** restore original tool name casing in Gemini/Antigravity response translators ([#9568](https://github.com/diegosouzapw/OmniRoute/issues/9568))
- fix(translator): add case-insensitive fallback for upstream tool call name lookups (#9575)
- **fix(standalone):** multipart uploads (`POST /v1/audio/transcriptions`) no longer hang — the WebDAV wrapper hands non-WebDAV requests to Next synchronously instead of losing the start of a streaming body ([#9580](https://github.com/diegosouzapw/OmniRoute/pull/9580))
- **fix(docker):** standalone co-location now completes packages Next's file tracing materialized partially (package.json without its `main` payload) — unblocks the Docker Hub publish that failed on every v3.8.50 push with `Cannot find module '@atjsh/llmlingua-2/dist/index.js'` ([#9615](https://github.com/diegosouzapw/OmniRoute/pull/9615))
- fix(resilience): failed connection test now sets a short cooldown so connections recover after transient outages (#9623)
- fix(db): wire telemetry cleanup scheduler in Next.js startup path (#9624)
- fix(db): align domain_cost_history cleanup cutoff with millisecond column (#9625)
- fix(playground): surface provider model loading errors and offer retry (#9626)
- fix(combo): distinguish pre-dispatch skips from genuine failures to prevent false 503 ALL_ACCOUNTS_INACTIVE (#9630)
- fix(build): add build-next-isolated.mjs sibling imports to package.json files array (#9633)
- **fix(providers):** new per-provider `noAuthFallbackDisabledProviders` setting lets operators disable the synthetic anonymous (no-auth) credential fallback for API-key providers whose static definition declares `anonymousFallback: true` (e.g. `opencode-go`, `opencode-zen`) — upstream endpoints now reject anonymous requests with `401 Missing API key`, so the fallback added latency and caused UI health/reconnect churn. Real keyed connections keep working and recover automatically once quota state clears; true no-auth providers (`opencode`, `mimocode`, …) are unaffected, with `blockedProviders` remaining their disable mechanism. Default behavior is unchanged ([#9675](https://github.com/diegosouzapw/OmniRoute/pull/9675))
- **fix(docker):** `--build-arg OMNIROUTE_USE_TURBOPACK=0` now reaches the builder stage — a bare `ENV` was shadowing the `ARG`, so the documented webpack escape hatch was silently ignored and memory-constrained hosts were OOM-killed with no error output ([#9695](https://github.com/diegosouzapw/OmniRoute/pull/9695))
- **fix(translator):** restore TitleCase tool names on the Claude → Gemini/Antigravity request path so Claude Code no longer fails with `No such tool available: read` ([#9713](https://github.com/diegosouzapw/OmniRoute/issues/9713))
- fix(db): clear stale combo connection pins when provider connections are deleted (#9719)
- fix(compression): persist `enableRenderers` through `normalizeRtkConfig` so RTK renderer settings survive a DB round-trip ([#9730](https://github.com/diegosouzapw/OmniRoute/pull/9730))
- Fixed `GET`/`PUT`/`DELETE /api/memory/[id]` always failing with a 500 (`Primary backend "sqlite" not registered`) when the route was reached before any other memory endpoint in the same process. (#9737)
- Replaced hand-rolled body type checks with Zod validation in the plugins marketplace install route and the three Dario admin routes, restoring the `t06:route-validation` gate (Hard Rule #7). (#9737)
- Restore Vietnamese locale parity after the entity-normalization sync dropped Radar, provider, and mini-playground messages. (#9737)
- **fix(radar):** refresh signed catalog/referral caches when supporter entitlement changes, preserve the one-time live-to-community downgrade, and test real provider connection IDs from the setup tour ([#9776](https://github.com/diegosouzapw/OmniRoute/pull/9776))
- **Translator**: keep the Responses namespace identity map across the hub-and-spoke pivot — namespace sub-tool calls routed to non-OpenAI targets (Kiro, Cursor) no longer come back flattened (`unsupported call: functions__exec` in Codex CLI) (#9783 — thanks @VXNCXNX)
- **fix(api):** Model catalogs no longer expose functional gateway mirrors unless the API key permits the mirror's final public model ID ([#9788](https://github.com/diegosouzapw/OmniRoute/pull/9788)) — thanks @xz-dev
- **fix(executors):** preserve Command Code usage in `/v1/responses` streams so Codex clients receive real input, output, cache, and reasoning token counts ([#9826](https://github.com/diegosouzapw/OmniRoute/pull/9826)) — thanks @MrShitFox
- **fix(executors):** prevent intermittent Codex `upstream_empty_response` errors for tool schemas that combine `oneOf` const branches with a matching sibling `enum` by removing only the semantically redundant `oneOf`; bare, narrowing, non-matching, and type-discriminated `oneOf` schemas remain unchanged. ([#9828](https://github.com/diegosouzapw/OmniRoute/pull/9828))
- **fix(cursor):** SelectedImage uses `blobIdWithData` + session blobStore, with JPEG soft-cap prep via sharp ([#9834](https://github.com/diegosouzapw/OmniRoute/pull/9834)) — thanks @yansigit
- **fix(test):** reconcile test expectations that drifted from the code they guard on `release/v3.8.50` — auth/vision/provider schema snapshots, and three context-aware combo compatibility assertions that contradicted the same file's own stated contract (catalog-too-small targets stay available as runtime fallback rather than being dropped). The combo assertions were masked by an unresolved import that stopped `combo.ts` from loading at all, so they only become reachable once that import is repaired. ([#9874](https://github.com/diegosouzapw/OmniRoute/pull/9874))
- fix(search): nest Exa contents options (text/highlights) for /search API (#9914)
- fix(i18n): re-escape CC discovery-alias `claude/<provider>/<model>` to HTML entities so next-intl stops logging INVALID_MESSAGE: UNCLOSED_TAG on provider detail pages (#8747 regression) ([#9917](https://github.com/diegosouzapw/OmniRoute/pull/9917)) — thanks @yansigit
- fix(encryption): name failing credential + recovery path in decrypt errors, dedupe per connection (#9927)
- **fix(executors):** preserve non-strict function-tool semantics when translating Chat Completions requests to Codex Responses, avoiding intermittent streamed failures without rewriting tool schemas or dropping branch-level descriptions and annotations. ([#9931](https://github.com/diegosouzapw/OmniRoute/pull/9931))
- fix(migrations): don't abort on fresh install with only the 001 seed (#9934)
- **fix(admission):** per-connection virtual admission lanes with idle TTL eviction — guards `expireEntry` null deref, adds `deleteLane()` for safe LRU eviction, and passes `sessionId` to byte-level admission (fixes #9654) ([#9940](https://github.com/diegosouzapw/OmniRoute/pull/9940)) — thanks @branben
- Repair release-sweep regressions in locale and environment contracts, package metadata, scripts, dependency, size and dead-code ratchets, OpenAPI coverage, Telegram error sanitization, Openference public-credential handling, DB-module classification, resilience UI test assertions, strict CodeBuddy CN tests, Lite compression typing, and the job-registry migration number. ([#9945](https://github.com/diegosouzapw/OmniRoute/pull/9945))
- Let the release-green validator finish the test-masking gate on loaded runners while preserving the existing timeout for every other full-CI gate, and report Node.js `ETIMEDOUT` errors as explicit timeout failures. ([#9964](https://github.com/diegosouzapw/OmniRoute/pull/9964))
- fix(chat): don't misclassify content-less thinking/redacted Claude bodies as empty_choices (#9971)
- fix(images): normalize terminal upstream errors via OpenAI-standard type/code (#9981)
- fix(quality): green release/v3.8.50 base-reds — sync 4 env vars into .env.example/ENVIRONMENT.md and freeze the new proxied-TLS proxyFetch helper in the file-size baseline (#9985)
- **fix(sse): replay Gemini `thought_signature` on the direct Claude→Gemini path** — with the #3440 assertion coverage preserved under signature replay (#2504, #3440) — thanks @csoftware-arigpt
- **fix(security): bump adm-zip >=0.6.0 + exact host matching in the mitm DNS test** (#7733)
- **fix(i18n): polish zh-CN/zh-TW translations and fix over-translation of proper nouns** ([#8872](https://github.com/diegosouzapw/OmniRoute/pull/8872)) — thanks @ikelvingo
- **fix(build): support npm v11 allowScripts for optional native deps** ([#8877](https://github.com/diegosouzapw/OmniRoute/pull/8877)) — thanks @configurowebmax
- **fix(combo): exclude hidden leaves from catalog and dispatch** ([#8878](https://github.com/diegosouzapw/OmniRoute/pull/8878)) — thanks @ahmet-cetinkaya
- **fix(antigravity): add onboardUser fallback for accounts missing Cloud Code project** ([#8886](https://github.com/diegosouzapw/OmniRoute/pull/8886)) — thanks @HouMinXi
- **fix(sse): brand-neutral keepalive frames** ([#8888](https://github.com/diegosouzapw/OmniRoute/pull/8888)) — thanks @AndrianBalanescu
- **fix(deepseek-web): enable toolCalling on all models** ([#8889](https://github.com/diegosouzapw/OmniRoute/pull/8889)) — thanks @AndrianBalanescu
- **fix(combo): fail-fast concurrency gate and execute-mode overflow** ([#8890](https://github.com/diegosouzapw/OmniRoute/pull/8890)) — thanks @AndrianBalanescu
- **fix(antigravity): quota-aware account selection and projectId persistence** ([#8891](https://github.com/diegosouzapw/OmniRoute/pull/8891)) — thanks @AndrianBalanescu
- **fix(usage): aggregate provider window costs in SQL** ([#8892](https://github.com/diegosouzapw/OmniRoute/pull/8892)) — thanks @AndrianBalanescu
- **fix(combo): least-used quota strategy and wildcard UI preservation** ([#8894](https://github.com/diegosouzapw/OmniRoute/pull/8894)) — thanks @AndrianBalanescu
- **fix(test): stop autostart tests from disabling the developer's real systemd service** ([#8900](https://github.com/diegosouzapw/OmniRoute/pull/8900)) — thanks @nosolosoft
- **fix(combos): include id column in getCombos query** ([#8905](https://github.com/diegosouzapw/OmniRoute/pull/8905)) — thanks @HouMinXi
- **fix: prevent false 'Failed to save connection' error when adding providers** ([#8912](https://github.com/diegosouzapw/OmniRoute/pull/8912)) — thanks @ziuus
- **fix: add Termux/Android support for playwright-core and better-sqlite3** ([#8922](https://github.com/diegosouzapw/OmniRoute/pull/8922)) — thanks @Kaedo17
- **fix(i18n): localize SubscriptionTab UI strings instead of hardcoded Chinese** ([#8930](https://github.com/diegosouzapw/OmniRoute/pull/8930)) — thanks @Hdiaktoros
- **fix(kiro): keep interleaved tool results grouped without dropping assistant text (#8903)** ([#8931](https://github.com/diegosouzapw/OmniRoute/pull/8931)) — thanks @xiaoyaner0201
- **Fix custom tool output pairing during context compression** ([#8933](https://github.com/diegosouzapw/OmniRoute/pull/8933)) — thanks @JxnLexn
- **fix(routing): account for active OAuth sessions** ([#8940](https://github.com/diegosouzapw/OmniRoute/pull/8940)) — thanks @JxnLexn
- **fix(vision): prevent bridge streaming and normalize OMP effort** ([#8945](https://github.com/diegosouzapw/OmniRoute/pull/8945)) — thanks @rinseaid
- **fix(ci): stop one failing platform from taking the whole desktop channel down** ([#8957](https://github.com/diegosouzapw/OmniRoute/pull/8957))
- **fix(api): alias-backed models leak raw node UUID prefix in /v1/models (#8958)** ([#8961](https://github.com/diegosouzapw/OmniRoute/pull/8961)) — thanks @Rahulsharma0810
- **fix(sse): default OpenAI Chat Completions to non-stream when stream omitted** ([#8976](https://github.com/diegosouzapw/OmniRoute/pull/8976)) — thanks @HouMinXi
- **fix(cache): add latency marker + per-key bypass for semantic cache** ([#8984](https://github.com/diegosouzapw/OmniRoute/pull/8984)) — thanks @HouMinXi
- **fix(perplexity-web): update catalog to use 'copilot' mode and fix model IDs** (#8989)
- **fix(docker): move entrypoint script to /app to avoid tmpfs masking** ([#8999](https://github.com/diegosouzapw/OmniRoute/pull/8999)) — thanks @yutuknown
- **fix(executors): backfill missing tool message names for Kimi K3 and strict BYOK providers** ([#9005](https://github.com/diegosouzapw/OmniRoute/pull/9005)) — thanks @Zenlyte
- **fix(command-code): enable vision flags for CC models and fix vision-bridge reroute** ([#9007](https://github.com/diegosouzapw/OmniRoute/pull/9007)) — thanks @Stazyu
- **fix(sse): route Poe API-key traffic through DefaultExecutor (#8969)** ([#9014](https://github.com/diegosouzapw/OmniRoute/pull/9014)) — thanks @Prudhvivuda
- **fix(sse): preserve Gemini thought_signature on Claude Desktop tool turns** ([#9015](https://github.com/diegosouzapw/OmniRoute/pull/9015)) — thanks @Prudhvivuda
- **fix(sse): preserve Claude Code tool-name casing via Gemini/Antigravity** ([#9016](https://github.com/diegosouzapw/OmniRoute/pull/9016)) — thanks @Prudhvivuda
- **fix(dashboard): make quota providers expandable** ([#9025](https://github.com/diegosouzapw/OmniRoute/pull/9025)) — thanks @jktan0504
- **fix(chat): resolve stored combo names before image-model validation (#8986)** ([#9027](https://github.com/diegosouzapw/OmniRoute/pull/9027)) — thanks @xiaoyaner0201
- **fix(kiro): read usage from the frames Kiro actually sends** ([#9035](https://github.com/diegosouzapw/OmniRoute/pull/9035)) — thanks @ddarkr
- **fix(kiro): keep relocated tool documentation on multi-turn requests** ([#9036](https://github.com/diegosouzapw/OmniRoute/pull/9036)) — thanks @ddarkr
- **fix(vision): preserve images for text-only routes** ([#9037](https://github.com/diegosouzapw/OmniRoute/pull/9037)) — thanks @rinseaid
- **fix(db): bundle and verify the sql.js fallback** ([#9044](https://github.com/diegosouzapw/OmniRoute/pull/9044)) — thanks @nguyenha935
- **fix(open-sse): route GitHub Copilot gpt-5.6 sol/terra/luna to /responses** ([#9050](https://github.com/diegosouzapw/OmniRoute/pull/9050)) — thanks @marchlhw
- **fix: skills & memory — tool-name encoding, schema normalization, warm-cache, combo id, Ponytail catalog** ([#9058](https://github.com/diegosouzapw/OmniRoute/pull/9058)) — thanks @Egorich-print
- **fix(classify): recognize Modal 'usage limit reached' as quota exhausted** ([#9079](https://github.com/diegosouzapw/OmniRoute/pull/9079)) — thanks @HouMinXi
- **Fix/issue #8656** ([#9095](https://github.com/diegosouzapw/OmniRoute/pull/9095)) — thanks @infinit-X
- **fix(adobe-firefly): open browser sign-in and resolve provider slug in /login** ([#9097](https://github.com/diegosouzapw/OmniRoute/pull/9097)) — thanks @artickc
- **fix(providers): make model Check/Test honor the node apiType, and show upstream model names** ([#9099](https://github.com/diegosouzapw/OmniRoute/pull/9099)) — thanks @zhiru
- **fix(audio): let the audio routes use audio-typed provider nodes, and gate remote ones behind a default-off flag** ([#9101](https://github.com/diegosouzapw/OmniRoute/pull/9101)) — thanks @zhiru
- **fix(antigravity): alias gemini-3.1-pro-high to gemini-pro-agent** ([#9106](https://github.com/diegosouzapw/OmniRoute/pull/9106)) — thanks @HouMinXi
- **fix(api): flatten single-row embedding vectors to OpenAI shape** ([#9148](https://github.com/diegosouzapw/OmniRoute/pull/9148)) — thanks @aniketshukla1
- **fix(proxy): restore connection pooling on proxy/relay paths (#9100)** ([#9158](https://github.com/diegosouzapw/OmniRoute/pull/9158)) — thanks @oyi77
- **fix(rate-limit): separate queue wait from execution timeout** ([#9164](https://github.com/diegosouzapw/OmniRoute/pull/9164)) — thanks @Zartharas
- **fix(translator): translate Codex agent messages for Chat** ([#9171](https://github.com/diegosouzapw/OmniRoute/pull/9171)) — thanks @Gioxaa
- **fix(settings): allow hidePaidModels updates** ([#9182](https://github.com/diegosouzapw/OmniRoute/pull/9182)) — thanks @Zartharas
- **fix(routing): evict affinity after terminal stream EOF** ([#9184](https://github.com/diegosouzapw/OmniRoute/pull/9184)) — thanks @Zartharas
- **fix(docker): bundle LLMLingua optional dependencies** ([#9185](https://github.com/diegosouzapw/OmniRoute/pull/9185)) — thanks @Zartharas
- **fix(health): skip disabled provider connections** ([#9186](https://github.com/diegosouzapw/OmniRoute/pull/9186)) — thanks @Zartharas
- **fix(claude): preserve standalone whitespace deltas** ([#9189](https://github.com/diegosouzapw/OmniRoute/pull/9189)) — thanks @Zartharas
- **fix(sse): evict a principal's own CCR blocks before another principal's (#9146)** ([#9191](https://github.com/diegosouzapw/OmniRoute/pull/9191)) — thanks @fajarhide
- **fix(responses): normalize terminal usage for Codex** ([#9192](https://github.com/diegosouzapw/OmniRoute/pull/9192))
- **fix(sse): back the CCR block store with a durable tier (#9061)** ([#9198](https://github.com/diegosouzapw/OmniRoute/pull/9198)) — thanks @fajarhide
- **fix(combo): recover provider circuit breaker from HALF_OPEN on success** ([#9207](https://github.com/diegosouzapw/OmniRoute/pull/9207)) — thanks @HouMinXi
- **fix(vision-bridge): improve compatibility with Anthropic image blocks and self-loop describe requests** ([#9226](https://github.com/diegosouzapw/OmniRoute/pull/9226)) — thanks @Stazyu
- **fix(images): refresh OAuth credentials and rotate accounts after 401** ([#9231](https://github.com/diegosouzapw/OmniRoute/pull/9231)) — thanks @Bl0ck154
- **fix(ci): merge-queue tolerance for Build (advisory), drops paid-tier batching** ([#9233](https://github.com/diegosouzapw/OmniRoute/pull/9233)) — thanks @wgordon17
- **fix(token-refresh): exempt transient errors from exponential backoff** ([#9242](https://github.com/diegosouzapw/OmniRoute/pull/9242)) — thanks @HouMinXi
- **fix(resilience): count STREAM_EARLY_EOF as a provider failure in combo routing** ([#9251](https://github.com/diegosouzapw/OmniRoute/pull/9251)) — thanks @TechNickAI
- **fix(command-code): preserve literal max effort for command-code provider** ([#9257](https://github.com/diegosouzapw/OmniRoute/pull/9257)) — thanks @Chewji9875
- **fix(dashboard): open webhook wizard in edit mode** ([#9272](https://github.com/diegosouzapw/OmniRoute/pull/9272)) — thanks @khoazero123
- **fix(mcp): remove non-standard x-provider field from omniroute_test_combo body** ([#9274](https://github.com/diegosouzapw/OmniRoute/pull/9274)) — thanks @Sam280903
- **fix(routing): bare model ids route to codex first; validate synced candidates** ([#9275](https://github.com/diegosouzapw/OmniRoute/pull/9275))
- **fix(mcp): stop DB init logging from corrupting the stdio JSON-RPC stream** ([#9281](https://github.com/diegosouzapw/OmniRoute/pull/9281)) — thanks @Sam280903
- **fix(reasoning): forward Ollama Cloud thinking** ([#9290](https://github.com/diegosouzapw/OmniRoute/pull/9290)) — thanks @xz-dev
- **fix(models): reconcile active live model catalogs** ([#9294](https://github.com/diegosouzapw/OmniRoute/pull/9294)) — thanks @Zartharas
- **fix: update Baichuan website URL to baichuan-ai.com** ([#9312](https://github.com/diegosouzapw/OmniRoute/pull/9312)) — thanks @zabrodschiipavel-sketch
- **fix(agentrouter): retry on 400 content-blocked + burst guard** ([#9323](https://github.com/diegosouzapw/OmniRoute/pull/9323))
- **fix:nanogpt model discovery** ([#9326](https://github.com/diegosouzapw/OmniRoute/pull/9326)) — thanks @TheFrenchGhosty
- **fix(rate-limit): patch Bottleneck doExpire capacity leak** ([#9328](https://github.com/diegosouzapw/OmniRoute/pull/9328)) — thanks @HouMinXi
- **fix(auth): let an agy request find the connection it authorized** ([#9340](https://github.com/diegosouzapw/OmniRoute/pull/9340)) — thanks @HouMinXi
- **fix(combo): network errors must not trip provider circuit breaker** ([#9342](https://github.com/diegosouzapw/OmniRoute/pull/9342)) — thanks @HouMinXi
- **fix(antigravity): propagate switchAuth signal from 429 engine to retry guard** ([#9351](https://github.com/diegosouzapw/OmniRoute/pull/9351)) — thanks @HouMinXi
- **fix(routing): correct reset-window strategy prioritization (#9330)** ([#9353](https://github.com/diegosouzapw/OmniRoute/pull/9353)) — thanks @Iammilansoni
- **fix(sse): drop the localDb barrel imports from chat and auth** ([#9380](https://github.com/diegosouzapw/OmniRoute/pull/9380)) — thanks @HouMinXi
- **fix(providers): enforce gemini-web reasoning and tool constraints (#9356)** ([#9397](https://github.com/diegosouzapw/OmniRoute/pull/9397)) — thanks @Iammilansoni
- **fix(combo): complete #8400 — preserve full fallback order + per-model account affinity for deterministic combos** ([#9420](https://github.com/diegosouzapw/OmniRoute/pull/9420)) — thanks @Chewji9875
- **fix(translator): normalize streamed optional tool arguments** ([#9423](https://github.com/diegosouzapw/OmniRoute/pull/9423)) — thanks @KittisakT
- **fix(providers): correct Codex GPT-5.6 context limits** ([#9432](https://github.com/diegosouzapw/OmniRoute/pull/9432)) — thanks @PixmaNts
- **fix(usage): stop double-counting cache-read tokens in Command Code executor** ([#9438](https://github.com/diegosouzapw/OmniRoute/pull/9438)) — thanks @Stazyu
- **fix(deps): bumps transitive deps for 8 CVEs surfaced by vuln-ratchet** ([#9441](https://github.com/diegosouzapw/OmniRoute/pull/9441)) — thanks @wgordon17
- **fix(ui): preserve request log position** ([#9452](https://github.com/diegosouzapw/OmniRoute/pull/9452)) — thanks @xiaoyaner0201
- **fix(sse): preserve client cache boundaries when hoisting system roles** ([#9457](https://github.com/diegosouzapw/OmniRoute/pull/9457)) — thanks @LeonG606
- **fix(providers): switch minimax from claude to openai format so images work** ([#9463](https://github.com/diegosouzapw/OmniRoute/pull/9463))
- **fix(sse): take the Antigravity output ceiling from the model, not a constant** ([#9482](https://github.com/diegosouzapw/OmniRoute/pull/9482)) — thanks @HouMinXi
- **fix(docs): add required MDX frontmatter to AGENTROUTER_WAF.md** ([#9503](https://github.com/diegosouzapw/OmniRoute/pull/9503))
- **fix(quality): prune a stale entry from the ESLint suppressions baseline** ([#9509](https://github.com/diegosouzapw/OmniRoute/pull/9509)) — thanks @HouMinXi
- **fix(classify): honor upstream retry windows on Gemini free-tier 429s** ([#9513](https://github.com/diegosouzapw/OmniRoute/pull/9513)) — thanks @shixi-li
- **fix(quality): 2 production bugs + 24 unit base-reds + measured gate ceilings** ([#9529](https://github.com/diegosouzapw/OmniRoute/pull/9529))
- **fix(providers): support data URL icons** ([#9555](https://github.com/diegosouzapw/OmniRoute/pull/9555)) — thanks @xz-dev
- **fix(sse): shrink chat.ts back under the frozen file-size cap (base-red drain)** ([#9598](https://github.com/diegosouzapw/OmniRoute/pull/9598))
- **fix(ci): clear base-reds on release/v3.8.50 (migration collision + 4 masked gates)** ([#9600](https://github.com/diegosouzapw/OmniRoute/pull/9600))
- **fix(resilience): enforce RPM with rolling leases** ([#9604](https://github.com/diegosouzapw/OmniRoute/pull/9604))
- **fix(backend): stop reasoning replay placeholder from self-poisoning** ([#9610](https://github.com/diegosouzapw/OmniRoute/pull/9610)) — thanks @stanleytejakusuma
- **fix(providers): mint a Zed LLM token for zed-hosted model discovery** ([#9628](https://github.com/diegosouzapw/OmniRoute/pull/9628)) — thanks @ARC345
- **fix(test): reconcile base-drifted test expectations on release/v3.8.50** ([#9634](https://github.com/diegosouzapw/OmniRoute/pull/9634)) — thanks @HouMinXi
- **fix(openrouter): scope model failures per-model instead of poisoning the whole connection** ([#9635](https://github.com/diegosouzapw/OmniRoute/pull/9635)) — thanks @hartmark
- **fix(radar): close the audit gaps — auth, merged feed fields, opt-in state, sidebar gate, size cap + daily scheduler** ([#9686](https://github.com/diegosouzapw/OmniRoute/pull/9686))
- **fix(ci): clear the NEW base-reds from the 08-06 merge batch (migration collision #2 + broken import)** ([#9688](https://github.com/diegosouzapw/OmniRoute/pull/9688))
- **fix(codebuddy-cn): replace agent system prompts to bypass Tencent content filter** ([#9723](https://github.com/diegosouzapw/OmniRoute/pull/9723)) — thanks @zuckdorsey
- **fix(db): resolve migration version 135 numbering collision** ([#9745](https://github.com/diegosouzapw/OmniRoute/pull/9745)) — thanks @hartmark
- **fix(ci): clear the 08-08 base-red layers — dead-code, prod crash in chat.ts, Responses payload regression, born-red stdio test, gate drifts** ([#9757](https://github.com/diegosouzapw/OmniRoute/pull/9757))
- **fix(bun): make server child and outbound fetch Bun-safe** ([#9761](https://github.com/diegosouzapw/OmniRoute/pull/9761)) — thanks @Arul-
- **fix(sse): preserve original body for semantic cache signature — fixes 0% hit rate** ([#9775](https://github.com/diegosouzapw/OmniRoute/pull/9775))
- **fix(api): validate request bodies with Zod in 4 routes — restores the t06 gate (#9737)** ([#9779](https://github.com/diegosouzapw/OmniRoute/pull/9779))
- **fix(ci): restore current release test integrity** ([#9819](https://github.com/diegosouzapw/OmniRoute/pull/9819))
- **fix(ci): close remaining release-green gaps** ([#9835](https://github.com/diegosouzapw/OmniRoute/pull/9835))
- **fix(proxy): isolate wreq TLS sessions by account** ([#9837](https://github.com/diegosouzapw/OmniRoute/pull/9837)) — thanks @agisota
- **fix(cursor): SelectedImage blobIdWithData + JPEG soft-cap prep** ([#9840](https://github.com/diegosouzapw/OmniRoute/pull/9840), original [#9834](https://github.com/diegosouzapw/OmniRoute/pull/9834)) — thanks @yansigit
- **fix(executors): strip redundant oneOf matching sibling enum** ([#9841](https://github.com/diegosouzapw/OmniRoute/pull/9841), original [#9828](https://github.com/diegosouzapw/OmniRoute/pull/9828)) — thanks @larin-vas
- **fix(executors): preserve Command Code usage in Responses streams** ([#9842](https://github.com/diegosouzapw/OmniRoute/pull/9842), original [#9826](https://github.com/diegosouzapw/OmniRoute/pull/9826)) — thanks @MrShitFox
- **fix(responses-api): tool call after a text message collided on the same output_index** ([#9843](https://github.com/diegosouzapw/OmniRoute/pull/9843), original [#9822](https://github.com/diegosouzapw/OmniRoute/pull/9822)) — thanks @hartmark
- **fix(admission): queue heavyweight chat requests before 503 busy** ([#9845](https://github.com/diegosouzapw/OmniRoute/pull/9845), original [#9816](https://github.com/diegosouzapw/OmniRoute/pull/9816)) — thanks @herjarsa
- **[TS7] fix(types): accept synced catalog model rows** ([#9846](https://github.com/diegosouzapw/OmniRoute/pull/9846), original [#9798](https://github.com/diegosouzapw/OmniRoute/pull/9798)) — thanks @backryun
- **[TS7] fix(types): normalize DuckDuckGo request messages** ([#9847](https://github.com/diegosouzapw/OmniRoute/pull/9847), original [#9797](https://github.com/diegosouzapw/OmniRoute/pull/9797)) — thanks @backryun
- **[TS7] fix(types): expose SQLite transaction state** ([#9848](https://github.com/diegosouzapw/OmniRoute/pull/9848), original [#9796](https://github.com/diegosouzapw/OmniRoute/pull/9796)) — thanks @backryun
- **[TS7] fix(types): validate default executor pool config** ([#9849](https://github.com/diegosouzapw/OmniRoute/pull/9849), original [#9795](https://github.com/diegosouzapw/OmniRoute/pull/9795)) — thanks @backryun
- **[TS7] fix(types): preserve The Old LLM proxy contracts** ([#9850](https://github.com/diegosouzapw/OmniRoute/pull/9850), original [#9793](https://github.com/diegosouzapw/OmniRoute/pull/9793)) — thanks @backryun
- **[TS7] fix(types): normalize Gemini Business credentials** ([#9851](https://github.com/diegosouzapw/OmniRoute/pull/9851), original [#9792](https://github.com/diegosouzapw/OmniRoute/pull/9792)) — thanks @backryun
- **[TS7] fix(types): preserve Claude thinking body contracts** ([#9852](https://github.com/diegosouzapw/OmniRoute/pull/9852), original [#9791](https://github.com/diegosouzapw/OmniRoute/pull/9791)) — thanks @backryun
- **fix(response): strip internal reasoning placeholder from all reasoning fields** ([#9853](https://github.com/diegosouzapw/OmniRoute/pull/9853), original [#9790](https://github.com/diegosouzapw/OmniRoute/pull/9790)) — thanks @adevwithpurpose
- **fix(api): enforce model permissions on gateway mirrors** ([#9854](https://github.com/diegosouzapw/OmniRoute/pull/9854), original [#9788](https://github.com/diegosouzapw/OmniRoute/pull/9788)) — thanks @xz-dev
- **fix(sse): apply Azure param rules on azure-ai and clamp gpt-4o-mini output tokens** ([#9855](https://github.com/diegosouzapw/OmniRoute/pull/9855), original [#9787](https://github.com/diegosouzapw/OmniRoute/pull/9787)) — thanks @Michael-Rocco-Goldmann
- **fix(sse): route claude discovery aliases for catalog-only providers** ([#9856](https://github.com/diegosouzapw/OmniRoute/pull/9856), original [#9777](https://github.com/diegosouzapw/OmniRoute/pull/9777)) — thanks @Michael-Rocco-Goldmann
- **fix(i18n): translate validation model keys in 34 locales** ([#9857](https://github.com/diegosouzapw/OmniRoute/pull/9857), original [#9773](https://github.com/diegosouzapw/OmniRoute/pull/9773)) — thanks @Michael-Rocco-Goldmann
- **[TS7] fix(skills): normalize web fetch credentials** ([#9859](https://github.com/diegosouzapw/OmniRoute/pull/9859), original [#9755](https://github.com/diegosouzapw/OmniRoute/pull/9755)) — thanks @backryun
- **[TS7] fix(types): narrow DeepSeek tool calls** ([#9860](https://github.com/diegosouzapw/OmniRoute/pull/9860), original [#9751](https://github.com/diegosouzapw/OmniRoute/pull/9751)) — thanks @backryun
- **fix(pricing): memoize getSyncedPricing() across catalog cache versions** ([#9861](https://github.com/diegosouzapw/OmniRoute/pull/9861), original [#9746](https://github.com/diegosouzapw/OmniRoute/pull/9746)) — thanks @chloeassistant
- **fix(logging): use configurable max-depth when bounding logged tool_calls** ([#9865](https://github.com/diegosouzapw/OmniRoute/pull/9865), original [#9734](https://github.com/diegosouzapw/OmniRoute/pull/9734)) — thanks @hartmark
- **fix(executors): repair DuckDuckGo AI Chat challenge solver (418 ERR_CHALLENGE)** ([#9866](https://github.com/diegosouzapw/OmniRoute/pull/9866), original [#9733](https://github.com/diegosouzapw/OmniRoute/pull/9733)) — thanks @Mynacol
- **fix(compression): persist RTK renderer configuration** ([#9867](https://github.com/diegosouzapw/OmniRoute/pull/9867), original [#9730](https://github.com/diegosouzapw/OmniRoute/pull/9730)) — thanks @isaaclb98
- **fix(dashboard): unregister leftover service workers in dev mode** ([#9868](https://github.com/diegosouzapw/OmniRoute/pull/9868), original [#9727](https://github.com/diegosouzapw/OmniRoute/pull/9727)) — thanks @hartmark
- **fix(docker): make the webpack build-arg escape hatch actually work** ([#9872](https://github.com/diegosouzapw/OmniRoute/pull/9872), original [#9695](https://github.com/diegosouzapw/OmniRoute/pull/9695)) — thanks @HouMinXi
- **fix(providers): per-provider opt-out for anonymous no-auth fallback (opencode-go/zen 401s)** ([#9873](https://github.com/diegosouzapw/OmniRoute/pull/9873), original [#9675](https://github.com/diegosouzapw/OmniRoute/pull/9675)) — thanks @chloeassistant
- **fix(providers): reject the dashboard password as a connection API key** ([#9877](https://github.com/diegosouzapw/OmniRoute/pull/9877), original [#9572](https://github.com/diegosouzapw/OmniRoute/pull/9572)) — thanks @HouMinXi
- **fix(settings): use provider prefixes in model overrides** ([#9878](https://github.com/diegosouzapw/OmniRoute/pull/9878), original [#9569](https://github.com/diegosouzapw/OmniRoute/pull/9569)) — thanks @xz-dev
- **fix(translator): preserve Kimi K3 Responses reasoning** ([#9879](https://github.com/diegosouzapw/OmniRoute/pull/9879), original [#9556](https://github.com/diegosouzapw/OmniRoute/pull/9556)) — thanks @jackjinke
- **fix(adobe-firefly): harden CDP warm, risk session, and browser sign-in** ([#9881](https://github.com/diegosouzapw/OmniRoute/pull/9881), original [#9549](https://github.com/diegosouzapw/OmniRoute/pull/9549)) — thanks @artickc
- **fix: pass max reasoning effort through by default, add global model registry fallback** ([#9883](https://github.com/diegosouzapw/OmniRoute/pull/9883), original [#9612](https://github.com/diegosouzapw/OmniRoute/pull/9612)) — thanks @Momen4444
- **fix(db): resolve CCR migration version collision** ([#9884](https://github.com/diegosouzapw/OmniRoute/pull/9884), original [#9618](https://github.com/diegosouzapw/OmniRoute/pull/9618)) — thanks @fenix007
- **fix(compression): add Lite tool truncation toggle** ([#9885](https://github.com/diegosouzapw/OmniRoute/pull/9885), original [#9629](https://github.com/diegosouzapw/OmniRoute/pull/9629)) — thanks @xz-dev
- **fix(web-tools): anchor tool contract at prompt tail + user-turn reminder for large prompts** ([#9887](https://github.com/diegosouzapw/OmniRoute/pull/9887), original [#9693](https://github.com/diegosouzapw/OmniRoute/pull/9693)) — thanks @ryan-brosas
- **fix(sse): persist per-tool-call JSON escape state across SSE delta chunks** ([#9889](https://github.com/diegosouzapw/OmniRoute/pull/9889), original [#9704](https://github.com/diegosouzapw/OmniRoute/pull/9704)) — thanks @hartmark
- **fix(db,combo): renumber ccr_blocks 134→139 + restore antigravity pool filter** ([#9890](https://github.com/diegosouzapw/OmniRoute/pull/9890), original [#9707](https://github.com/diegosouzapw/OmniRoute/pull/9707)) — thanks @matiasbaglieri
- **fix(sse): grace period before finalizing a client disconnect as 499** ([#9891](https://github.com/diegosouzapw/OmniRoute/pull/9891), original [#9711](https://github.com/diegosouzapw/OmniRoute/pull/9711)) — thanks @hartmark
- **fix(build): standalone bundle misses LLMLingua dist + onnxruntime native binaries** ([#9892](https://github.com/diegosouzapw/OmniRoute/pull/9892), original [#9712](https://github.com/diegosouzapw/OmniRoute/pull/9712)) — thanks @hartmark
- **fix(responses-api): sync reasoning-cache write index with the fixed read side** ([#9895](https://github.com/diegosouzapw/OmniRoute/pull/9895), original [#9741](https://github.com/diegosouzapw/OmniRoute/pull/9741)) — thanks @hartmark
- **fix(ci): repair release lint test regressions** ([#9896](https://github.com/diegosouzapw/OmniRoute/pull/9896), original [#9813](https://github.com/diegosouzapw/OmniRoute/pull/9813)) — thanks @alex-jordan547
- **fix(command-code): include tool call arguments** ([#9897](https://github.com/diegosouzapw/OmniRoute/pull/9897), original [#9821](https://github.com/diegosouzapw/OmniRoute/pull/9821)) — thanks @Chewji9875
- **fix(providers): remove retired NVIDIA NIM catalog entries** ([#9898](https://github.com/diegosouzapw/OmniRoute/pull/9898), original [#9825](https://github.com/diegosouzapw/OmniRoute/pull/9825)) — thanks @Zartharas
- **fix(nvidia): keep 410 failures model-scoped** ([#9899](https://github.com/diegosouzapw/OmniRoute/pull/9899), original [#9833](https://github.com/diegosouzapw/OmniRoute/pull/9833)) — thanks @Zartharas
- **fix: retry CodeBuddy large-tool requests in compact form** ([#9900](https://github.com/diegosouzapw/OmniRoute/pull/9900), original [#9542](https://github.com/diegosouzapw/OmniRoute/pull/9542)) — thanks @mvanhorn
- **fix(quality): clears two release/v3.8.50 base-red gates** ([#9901](https://github.com/diegosouzapw/OmniRoute/pull/9901), original [#9619](https://github.com/diegosouzapw/OmniRoute/pull/9619)) — thanks @wgordon17
- **fix: resolve hollow external package directory crashes and implement …** ([#9913](https://github.com/diegosouzapw/OmniRoute/pull/9913)) — thanks @SupremeNexas
- **fix(i18n): restore Vietnamese locale parity** ([#9925](https://github.com/diegosouzapw/OmniRoute/pull/9925))
- **fix(combo): classify Cloudflare 1010 fingerprint rejection as non-auth** ([#9929](https://github.com/diegosouzapw/OmniRoute/pull/9929)) — thanks @HouMinXi
- **fix(image): return Fal images as base64 by default** ([#9932](https://github.com/diegosouzapw/OmniRoute/pull/9932)) — thanks @rinseaid
- **fix(image): support Fal reference-image edits** ([#9933](https://github.com/diegosouzapw/OmniRoute/pull/9933)) — thanks @rinseaid
- **fix(db): invalidate LKGP pins on provider connection delete** ([#9936](https://github.com/diegosouzapw/OmniRoute/pull/9936)) — thanks @Zartharas
- **fix(services): stop embedded-service supervisor retry loop when binary cannot spawn** ([#9937](https://github.com/diegosouzapw/OmniRoute/pull/9937)) — thanks @herjarsa
- **fix(backend): retain streaming usage for providers with choices:[{delta:{}}] final chunk** ([#9938](https://github.com/diegosouzapw/OmniRoute/pull/9938)) — thanks @sadSanta-07
- **fix(antigravity): ban-safety hardening — bounded onboarding retries, gate thought-signature bypass sentinel** ([#9939](https://github.com/diegosouzapw/OmniRoute/pull/9939)) — thanks @benzntech
- **fix(guardrails): vision bridge reroute/pool/self-loop fixes (auto/best-vision, claude-wire base64)** ([#9946](https://github.com/diegosouzapw/OmniRoute/pull/9946)) — thanks @herjarsa
- **fix(mcp): stop omniroute_get_health silently discarding real data** ([#9959](https://github.com/diegosouzapw/OmniRoute/pull/9959)) — thanks @tald26
- **fix(build): bump @huggingface/transformers to 4.2.0 + onnxruntime-node 1.24.3** ([#9962](https://github.com/diegosouzapw/OmniRoute/pull/9962)) — thanks @witt3rd
- **fix(cleanup): prune mcp_tool_audit/a2a_task_events by created_at column** ([#9963](https://github.com/diegosouzapw/OmniRoute/pull/9963)) — thanks @witt3rd
- **fix(db): avoid skipping pending job registry migration 146** ([#9965](https://github.com/diegosouzapw/OmniRoute/pull/9965)) — thanks @Zartharas
- **fix(video): support Fal-hosted Grok Imagine Video** ([#9969](https://github.com/diegosouzapw/OmniRoute/pull/9969)) — thanks @rinseaid
- **fix(i18n): escape angle brackets in denoRelayOrgDomainHint across all 43 locales** ([#9976](https://github.com/diegosouzapw/OmniRoute/pull/9976)) — thanks @AgnesRiber
- **fix(media): support Gemini Omni Flash video** ([#9982](https://github.com/diegosouzapw/OmniRoute/pull/9982)) — thanks @rinseaid
- **fix(copilot-web): restore browser authentication** ([#9984](https://github.com/diegosouzapw/OmniRoute/pull/9984)) — thanks @backryun
- **fix(opencode): fallback unsupported DeepSeek json schema output** ([#9992](https://github.com/diegosouzapw/OmniRoute/pull/9992)) — thanks @Zartharas
- **fix(translator): restore TitleCase tool names on the Claude to Gemini path** ([#9993](https://github.com/diegosouzapw/OmniRoute/pull/9993)) — thanks @engmarcosjr
- **fix(providers): scope model-level targetFormat to declaring provider catalog** ([#9994](https://github.com/diegosouzapw/OmniRoute/pull/9994)) — thanks @Chewji9875
- **fix(kimi): apply K3 effort policy to aliases** ([#10005](https://github.com/diegosouzapw/OmniRoute/pull/10005)) — thanks @jackjinke
- **fix(adobe-firefly) — direct pushes:** harden credential parsing and login hostname comparison (parse-and-compare instead of substring match), sync models/media capabilities, and retain the Topaz catalog models
- **fix(combo/sse) — direct pushes:** ignore benign empty error fields in streaming-quality validation, classify local target timeouts as gateway timeouts, avoid the usage-normalization short-circuit in Responses, and type empty-choice collector events
- **fix(logging) — direct pushes:** make stream-chunk capture and request-shape logging opt-in diagnostics
- **fix(i18n) — direct pushes:** restore/unescape HTML entities in UI strings, translate capability-filter messages, complete web-session guide translations and Vietnamese parity
- **fix(providers) — direct pushes:** repair the DeepAI registry import + executor
- **fix(deps) — direct pushes:** CVE-driven bumps (nanoid, dompurify, mermaid, js-yaml + transitive deps for 26 Dependabot alerts) and retained isolated-build runtime dependencies in the pack
### 📝 Maintenance
- **fix(types):** preserve the client response format contract while estimating usage for non-streaming responses (#8484)
- Preserve the Responses API transform options contract under TypeScript 7.
- **fix(types):** preserved the known first-failure record while reading Anthropic thinking-signature recovery details so TypeScript 7 keeps the retry result union narrow ([#8484](https://github.com/diegosouzapw/OmniRoute/issues/8484))
- **fix(types):** reuse the validation failure predicate when building malformed-body responses so the existing error envelope remains type-safe and unchanged. (#8484)
- **fix(types):** preserve the literal `capabilities.vision: true` contract for catalog vision fields so custom-model capability composition remains type-safe under TypeScript 7 ([#9121](https://github.com/diegosouzapw/OmniRoute/pull/9121)) (#8484)
- chore(quality): add an RTL layout ratchet — counts physical directional Tailwind classes (ml/mr/pl/pr/left/right/text-left/border-l/rounded-l) that do not mirror under `dir=rtl`, seeded at 1011 so the backlog behind `tests/unit/ui/rtl-logical-classes.test.tsx` ("#3541, partial, core layout") cannot grow while it is worked through ([#8828](https://github.com/diegosouzapw/OmniRoute/pull/8828)) — thanks @lukiod
- **chore(sse):** dropped the leftover `iflow` entry from the token-refresh TTL map — the provider was removed from the product but its 24-hour refresh lead outlived it, and the identifier had exactly two occurrences left repo-wide ([#8966](https://github.com/diegosouzapw/OmniRoute/pull/8966))
- **chore(ci):** removed two fork-owned image-publish workflows that had ridden into the repo as unrelated extra files in on-topic PRs — `build-fork.yml` (`ghcr.io/kang-heewon`, job-level guard, so it instantiated a skipped run on every push to main and every tag) and `build-rinseaid-image.yml` (`ghcr.io/rinseaid`, no guard, never fired). Neither could authenticate against this repository's token; a new policy guard now fails CI on any workflow targeting a foreign registry namespace ([#8967](https://github.com/diegosouzapw/OmniRoute/pull/8967))
- **test(ci):** fixed the intermittent `spawnSync bash EPIPE` failure in the `:latest` promotion guard — the script exits on a pre-release version before reading stdin, so the harness's pipe-backed `input:` raced that exit; stdin is now file-backed, which makes the race structurally impossible ([#8977](https://github.com/diegosouzapw/OmniRoute/pull/8977))
- **refactor(providers):** removed the retired GitHub Models provider and its catalog, discovery, embedding, free-tier, UI, and documentation surfaces; upgrades now run a durable, idempotent purge of its stored credentials, usage state, structured configuration, and call-log artifacts while preserving GitHub Copilot and live members of mixed configurations ([#9023](https://github.com/diegosouzapw/OmniRoute/pull/9023))
- **test(sse):** added the first test suite for `open-sse/services/specificityRules.ts` — the
module was hotspot #7 in the coverage plan at 11.28% lines with no dedicated test; the 14 pure
detectors are now pinned edge-to-edge (token ladders, per-domain max-not-sum scoring, and the
min/max clamps), taking the file to ~100% line coverage. The suite also documents two quirks
left unchanged: `detectReasoningDepth` scores above 0 on marker-free input via its
always-applied message-depth bonus, and `detectErrorContext` returns non-integer scores because
it never rounds ([#9063](https://github.com/diegosouzapw/OmniRoute/pull/9063))
- **fix(types):** preserved the Veo polling delay promise result as `void` for TypeScript 7 compatibility without changing runtime polling behavior ([#9104](https://github.com/diegosouzapw/OmniRoute/pull/9104))
- **fix(types):** imported compression analytics statistics from their defining module so TypeScript 7 resolves the existing interface correctly ([#9105](https://github.com/diegosouzapw/OmniRoute/pull/9105))
- **chore(types):** align semantic cache signature inputs with the numeric request contract so both cache-write paths remain runtime-equivalent while TypeScript 7 checks them safely ([#9117](https://github.com/diegosouzapw/OmniRoute/pull/9117))
- **fix(types):** narrowed non-streaming chat response metadata inputs to the shared header contract without changing emitted metadata headers ([#9118](https://github.com/diegosouzapw/OmniRoute/pull/9118))
- **docs(readme):** replace Roo Code branding with Zoo Code. (thanks @taltas) ([#9229](https://github.com/diegosouzapw/OmniRoute/pull/9229))
- **chore(ci):** stopped dependabot from grouping `ioredis` majors with routine production bumps — the package is resolved through a dynamic import in the distributed quota store, so a breaking major passes build, typecheck and both test suites and only surfaces at runtime for operators running Redis-backed quota ([#9425](https://github.com/diegosouzapw/OmniRoute/pull/9425))
- **chore(tests):** cleared two base-reds sitting on `release/v3.8.50` itself, both of which turned every open PR red the moment it merged the release. `tests/snapshots/provider/translate-path.json` was stale: #9064 added the `code-execution-2025-08-25` and `skills-2025-10-02` beta flags to the Anthropic header without regenerating the golden, so `provider-translate-path-golden` failed on the bare release tip (2 pass / 1 fail with zero PRs boarded). And `tests/unit/v1-models-auth-leak-9320.test.ts:83` shipped a `(k: any)` in a file new enough that `config/quality/eslint-suppressions.json` does not cover it — with `@typescript-eslint/no-explicit-any` set to `error` under `tests/`, that single cast failed the `No new ESLint warnings` gate repo-wide. Same class in `tests/unit/issue-9407-gemini-web-validation-false-positive.test.ts:50` (`(executor as any).testConnection`), which entered at `f1ea77fd04``testConnection` is a declared public method on `GeminiWebExecutor`, so that cast was redundant too. Regenerating the snapshot and dropping both casts restores the gates. ([#9488](https://github.com/diegosouzapw/OmniRoute/pull/9488))
- **test(cli):** OpenCode plugin suite realigned to the bare-key static-catalog contract from #9178/#9175 (21 tests were red on every opencode-plugin CI run; 287/287 after) ([#9614](https://github.com/diegosouzapw/OmniRoute/pull/9614))
- Removed the unused `RadarReferrals` type export left by the radar referral-links feature (#9697), returning the dead-code ratchet to its 227 baseline. (#9738)
- Reconcile the final v3.8.50 bundle-size and file-size ratchets against the measured release tip, preserving exact direction-down ceilings and their source attribution. ([#9839](https://github.com/diegosouzapw/OmniRoute/pull/9839))
- **chore(quality):** expand all file-size baselines by +30% ahead of v3.8.51 (authorized DRIFT rebaseline) to unblock the pre-release queue; no functionality changes. (#9950)
- fix(quality): tighten eslintWarnings baseline 5000->0 to match the gate's suppressions-applied measurement (unblocks require-tighten on every code PR)
- **[v3.8.50] feat: add RTL layout compatibility CSS (fixes #7680)** ([#7987](https://github.com/diegosouzapw/OmniRoute/pull/7987)) — thanks @Dingding-leo
- **[v3.8.50] feat(devin-desktop): replace public Windsurf provider** ([#8228](https://github.com/diegosouzapw/OmniRoute/pull/8228)) — thanks @backryun
- **[v3.8.50] feat(ci): extend i18n glossary-consistency gate to ko** ([#8244](https://github.com/diegosouzapw/OmniRoute/pull/8244)) — thanks @MichaelYcJo
- **[v3.8.50] test(tail): realign 3 stale base-red guards + note 2 env-only false positives (slice 6)** ([#8263](https://github.com/diegosouzapw/OmniRoute/pull/8263))
- **[v3.8.50] feat(ui): add global model search to Combo builder** ([#8285](https://github.com/diegosouzapw/OmniRoute/pull/8285)) — thanks @corefusiion
- **[v3.8.50] feat: extract CloakBrowser/browser-pool into optional plugin package** ([#8299](https://github.com/diegosouzapw/OmniRoute/pull/8299)) — thanks @oyi77
- **[v3.8.50] fix(i18n): clean up and naturalize Spanish translations** ([#8339](https://github.com/diegosouzapw/OmniRoute/pull/8339)) — thanks @Dragost
- **[v3.8.50] fix(compression): bound session-dedup suffix-block scan to prevent OOM** ([#8438](https://github.com/diegosouzapw/OmniRoute/pull/8438)) — thanks @adrianojiu
- **[v3.8.50] Fix Z.ai web browser transport and model capabilities** ([#8451](https://github.com/diegosouzapw/OmniRoute/pull/8451)) — thanks @backryun
- **[v3.8.50] feat(services): add Dario as a 5th embedded service (Claude Code toggle/failover)** ([#8523](https://github.com/diegosouzapw/OmniRoute/pull/8523)) — thanks @seanford
- **[v3.8.50] fix(adobe-firefly): live x-arp-session-id / Arkose wire (stop HTTP 408)** ([#8571](https://github.com/diegosouzapw/OmniRoute/pull/8571)) — thanks @artickc
- **[v3.8.50] fix(adobe-firefly): durable session, Chrome recovery, browser sign-in** ([#8578](https://github.com/diegosouzapw/OmniRoute/pull/8578)) — thanks @artickc
- **[v3.8.50] fix: passthrough non-standard cache token fields for DeepSeek / MiniMax / Bedrock across streaming, non-streaming, and Dashboard paths** ([#8591](https://github.com/diegosouzapw/OmniRoute/pull/8591)) — thanks @ikelvingo
- **[v3.8.50] fix: treat zero-reset Antigravity 429s as transient** ([#8626](https://github.com/diegosouzapw/OmniRoute/pull/8626)) — thanks @costaeder
- **[v3.8.50] fix: enforce OpenAI model lifecycle without silent reroutes** ([#8627](https://github.com/diegosouzapw/OmniRoute/pull/8627)) — thanks @backryun
- **[v3.8.50] fix(claude): preserve signed thinking turns during obfuscation** ([#8629](https://github.com/diegosouzapw/OmniRoute/pull/8629)) — thanks @costaeder
- **[v3.8.50] fix(antigravity): lock full quota per exact model** ([#8630](https://github.com/diegosouzapw/OmniRoute/pull/8630)) — thanks @costaeder
- **[v3.8.50] fix(errorConfig): add status 499 metadata mapping (fixes #8535)** ([#8640](https://github.com/diegosouzapw/OmniRoute/pull/8640)) — thanks @Dingding-leo
- **[v3.8.50] fix(auth): accept x-api-key without anthropic-version for claude-code user-agent (fixes #8655)** ([#8678](https://github.com/diegosouzapw/OmniRoute/pull/8678)) — thanks @Dingding-leo
- **[v3.8.50] fix(open-sse): add 'has been exhausted' to CREDITS_EXHAUSTED_SIGNALS (fixes #8631)** ([#8704](https://github.com/diegosouzapw/OmniRoute/pull/8704)) — thanks @Dingding-leo
- **[v3.8.50] fix(github): honor per-model targetFormat override for Copilot custom models** ([#8713](https://github.com/diegosouzapw/OmniRoute/pull/8713)) — thanks @Witroch4
- **[v3.8.50] fix(test): revive orphaned vitest tests and fix CI routing** ([#8718](https://github.com/diegosouzapw/OmniRoute/pull/8718)) — thanks @MohitRawat017
- **[v3.8.50] feat(providers): add support for TinyCMS Web** ([#8736](https://github.com/diegosouzapw/OmniRoute/pull/8736)) — thanks @jhordanjw123
- **[v3.8.50] feat(memory): MemoryBackend provider pattern with generic HTTP connector** ([#8752](https://github.com/diegosouzapw/OmniRoute/pull/8752)) — thanks @oyi77
- **[v3.8.50] refactor(db): add combo repository boundary** ([#8757](https://github.com/diegosouzapw/OmniRoute/pull/8757)) — thanks @xiaoyaner0201
- **[v3.8.50] fix(test): revive orphaned open-sse vitest tests** ([#8772](https://github.com/diegosouzapw/OmniRoute/pull/8772)) — thanks @MohitRawat017
- **[v3.8.50] fix(open-sse): filter non-numeric values in comboTargetLimits before min calculation** ([#8774](https://github.com/diegosouzapw/OmniRoute/pull/8774)) — thanks @Dingding-leo
- **[v3.8.50] feat(compression): add Italian (it) Caveman rule pack** ([#8776](https://github.com/diegosouzapw/OmniRoute/pull/8776)) — thanks @Anjielon
- **[v3.8.50] feat(combo): add maxContextWindow to contextRequirements (fixes #8777)** ([#8790](https://github.com/diegosouzapw/OmniRoute/pull/8790)) — thanks @Dingding-leo
- **[v3.8.50] feat(images): add POST /v1/images/upscale (Adobe Firefly Topaz + Stability + Topaz Labs)** ([#8791](https://github.com/diegosouzapw/OmniRoute/pull/8791)) — thanks @artickc
- **[v3.8.50] fix(providers): drop dead Cloudflare Workers AI free catalog IDs (#8717)** ([#8804](https://github.com/diegosouzapw/OmniRoute/pull/8804)) — thanks @DinonowDev
- **[v3.8.50] fix(sse): stop fabricating encrypted Codex reasoning summary text** ([#8807](https://github.com/diegosouzapw/OmniRoute/pull/8807)) — thanks @Prudhvivuda
- **[v3.8.50] fix(backend): update Cloudflare Workers AI model catalog & remove dead model IDs (#8717)** ([#8808](https://github.com/diegosouzapw/OmniRoute/pull/8808)) — thanks @Dingding-leo
- **[TS7] [v3.8.50] refactor(db): preserve normalized combo model types** ([#8809](https://github.com/diegosouzapw/OmniRoute/pull/8809)) — thanks @backryun
- **[v3.8.50] fix(db/apiKeys): respect provider parameter in group model permission checks (fixes #8803)** ([#8817](https://github.com/diegosouzapw/OmniRoute/pull/8817)) — thanks @Dingding-leo
- **[TS7] [v3.8.50] fix(types): preserve browser abort handling** ([#8818](https://github.com/diegosouzapw/OmniRoute/pull/8818)) — thanks @backryun
- **[v3.8.50] feat(cli): deliver the Antigravity credential straight to the remote install** ([#8834](https://github.com/diegosouzapw/OmniRoute/pull/8834))
- **[v3.8.50] docs: slim AGENTS.md** ([#8839](https://github.com/diegosouzapw/OmniRoute/pull/8839)) — thanks @MumuTW
- **docs(guides): add Antigravity (Google One AI) onboarding guide** ([#8904](https://github.com/diegosouzapw/OmniRoute/pull/8904)) — thanks @HouMinXi
- **[v3.8.50] fix(usage): reject impossible provider token counts** ([#8927](https://github.com/diegosouzapw/OmniRoute/pull/8927)) — thanks @artickc
- **Treat context metadata as a routing hint** ([#8944](https://github.com/diegosouzapw/OmniRoute/pull/8944)) — thanks @JxnLexn
- **docs(db): specify MySQL conformance semantics** ([#8947](https://github.com/diegosouzapw/OmniRoute/pull/8947)) — thanks @rushsinging
- **Add native ChatGPT Web provider for Codex clients** ([#8949](https://github.com/diegosouzapw/OmniRoute/pull/8949)) — thanks @JxnLexn
- **i18n(ru): complete Russian locale — 100% coverage** ([#9001](https://github.com/diegosouzapw/OmniRoute/pull/9001)) — thanks @Egorich-print
- **docs(troubleshooting): document the chat_admission_busy 503 and how to tune heavyweight chat concurrency** ([#9021](https://github.com/diegosouzapw/OmniRoute/pull/9021)) — thanks @xiaoyaner0201
- **Security: Update Redis to fix critical vunerability** ([#9065](https://github.com/diegosouzapw/OmniRoute/pull/9065)) — thanks @tuxmonteiro
- **[TS7] fix(types): validate chat context estimation inputs** ([#9084](https://github.com/diegosouzapw/OmniRoute/pull/9084)) — thanks @backryun
- **[TS7] fix(types): narrow stream response output** ([#9086](https://github.com/diegosouzapw/OmniRoute/pull/9086)) — thanks @backryun
- **docs: clarify free-provider model refresh outcomes** ([#9087](https://github.com/diegosouzapw/OmniRoute/pull/9087)) — thanks @AbdullahFageeh
- **[TS7] [v3.8.50] fix(types): preserve SSE tool call function shape** ([#9090](https://github.com/diegosouzapw/OmniRoute/pull/9090)) — thanks @backryun
- **[TS7] fix(types): narrow CCR store rejections** ([#9091](https://github.com/diegosouzapw/OmniRoute/pull/9091)) — thanks @backryun
- **[TS7] fix(types): preserve array-buffer response bodies** ([#9092](https://github.com/diegosouzapw/OmniRoute/pull/9092)) — thanks @backryun
- **[TS7] fix(types): narrow media generation failures** ([#9093](https://github.com/diegosouzapw/OmniRoute/pull/9093)) — thanks @backryun
- **[TS7] fix(types): preserve thinking signature recovery failure** ([#9114](https://github.com/diegosouzapw/OmniRoute/pull/9114)) — thanks @backryun
- **[TS7] fix(types): preserve Responses transform options** ([#9119](https://github.com/diegosouzapw/OmniRoute/pull/9119)) — thanks @backryun
- **[TS7] fix(types): preserve validation failure narrowing** ([#9120](https://github.com/diegosouzapw/OmniRoute/pull/9120)) — thanks @backryun
- **[TS7] fix(types): preserve client usage format contract** ([#9122](https://github.com/diegosouzapw/OmniRoute/pull/9122)) — thanks @backryun
- **[TS7] fix(types): preserve request rule input contracts** ([#9135](https://github.com/diegosouzapw/OmniRoute/pull/9135)) — thanks @backryun
- **[TS7] fix(types): simplify Codex service tier narrowing** ([#9136](https://github.com/diegosouzapw/OmniRoute/pull/9136)) — thanks @backryun
- **[TS7] fix(types): tighten extracted chatCore contracts** ([#9137](https://github.com/diegosouzapw/OmniRoute/pull/9137)) — thanks @backryun
- **[TS7] fix(types): align web executor event and model contracts** ([#9138](https://github.com/diegosouzapw/OmniRoute/pull/9138)) — thanks @backryun
- **[TS7] fix(types): align web provider support contracts** ([#9139](https://github.com/diegosouzapw/OmniRoute/pull/9139)) — thanks @backryun
- **[TS7] fix(types): type media provider request payloads** ([#9141](https://github.com/diegosouzapw/OmniRoute/pull/9141)) — thanks @backryun
- **test(dashboard): drop stale next-intl mock breaking ProviderDetailPageClient smoke** ([#9150](https://github.com/diegosouzapw/OmniRoute/pull/9150)) — thanks @maxmad64bis
- **test(mcp): guard Node 24 bundled MCP startup** ([#9162](https://github.com/diegosouzapw/OmniRoute/pull/9162)) — thanks @Gioxaa
- **test(compression): lock in stacked RTK+Caveman savings on redundant tool_result content** ([#9278](https://github.com/diegosouzapw/OmniRoute/pull/9278)) — thanks @Sam280903
- **chore: bump better-sqlite3 and add provider DB query scripts** ([#9325](https://github.com/diegosouzapw/OmniRoute/pull/9325)) — thanks @jowimila
- **test(quota): wait for the hot-path consumption instead of sleeping** ([#9365](https://github.com/diegosouzapw/OmniRoute/pull/9365)) — thanks @HouMinXi
- **refactor(sse): move the thinking-budget helpers out of base.ts** ([#9381](https://github.com/diegosouzapw/OmniRoute/pull/9381)) — thanks @HouMinXi
- **test(sse): expect the trailing period in the no-credentials message** ([#9392](https://github.com/diegosouzapw/OmniRoute/pull/9392)) — thanks @HouMinXi
- **chore(db): raise sqlite cache_size/mmap_size defaults** ([#9467](https://github.com/diegosouzapw/OmniRoute/pull/9467)) — thanks @Poid-ZA
- **[TS7] fix(types): align stream failure callback contracts** ([#9561](https://github.com/diegosouzapw/OmniRoute/pull/9561)) — thanks @backryun
- **[TS7] fix(types): narrow chatCore local contracts** ([#9562](https://github.com/diegosouzapw/OmniRoute/pull/9562)) — thanks @backryun
- **[TS7] fix(types): validate Azure OpenAI base URLs** ([#9563](https://github.com/diegosouzapw/OmniRoute/pull/9563)) — thanks @backryun
- **[TS7] fix(types): preserve sanitized tool array contracts** ([#9564](https://github.com/diegosouzapw/OmniRoute/pull/9564)) — thanks @backryun
- **[TS7] fix(types): validate Vision Bridge combo names** ([#9565](https://github.com/diegosouzapw/OmniRoute/pull/9565)) — thanks @backryun
- **[TS7] fix(types): preserve streaming PII choice keys** ([#9566](https://github.com/diegosouzapw/OmniRoute/pull/9566)) — thanks @backryun
- **[TS7] test(types): use Vitest expectations in tier resolver** ([#9742](https://github.com/diegosouzapw/OmniRoute/pull/9742)) — thanks @backryun
- **[TS7] fix(translator): preserve video URL override contracts** ([#9747](https://github.com/diegosouzapw/OmniRoute/pull/9747)) — thanks @backryun
- **[TS7] fix(codex): preserve narrowed input arrays** ([#9748](https://github.com/diegosouzapw/OmniRoute/pull/9748)) — thanks @backryun
- **[TS7] fix(kiro): complete cache-only usage totals** ([#9753](https://github.com/diegosouzapw/OmniRoute/pull/9753)) — thanks @backryun
- **chore(repo): ignore Electron build output unpacked into repo root** ([#9858](https://github.com/diegosouzapw/OmniRoute/pull/9858), original [#9770](https://github.com/diegosouzapw/OmniRoute/pull/9770)) — thanks @Michael-Rocco-Goldmann
- **test(integration): add general live-test tool for the real "default" combo + rootless wire capture** ([#9862](https://github.com/diegosouzapw/OmniRoute/pull/9862), original [#9744](https://github.com/diegosouzapw/OmniRoute/pull/9744)) — thanks @hartmark
- **ci(test): route orphaned Vitest tests through blocking CI** ([#9875](https://github.com/diegosouzapw/OmniRoute/pull/9875), original [#9605](https://github.com/diegosouzapw/OmniRoute/pull/9605)) — thanks @MohitRawat017
- **docs(proposals): Telegram Mini App integration feasibility analysis** ([#9906](https://github.com/diegosouzapw/OmniRoute/pull/9906), original [#9810](https://github.com/diegosouzapw/OmniRoute/pull/9810)) — thanks @benzntech
- **chore: ignore docker-compose.override.yml** ([#9919](https://github.com/diegosouzapw/OmniRoute/pull/9919)) — thanks @lucasalx
- **[TS7] fix(types): stabilize skill token extraction** ([#9920](https://github.com/diegosouzapw/OmniRoute/pull/9920)) — thanks @backryun
- **docs: add quickstart code examples for Python, Node.js, PHP and cURL** ([#9922](https://github.com/diegosouzapw/OmniRoute/pull/9922)) — thanks @Hariprajwal
- **[TS7] fix(types): narrow combo model collections** ([#9972](https://github.com/diegosouzapw/OmniRoute/pull/9972)) — thanks @backryun
- **[TS7] fix(types): align Claude message contracts** ([#9973](https://github.com/diegosouzapw/OmniRoute/pull/9973)) — thanks @backryun
- **[TS7] fix(types): type Copilot WebSocket construction** ([#9974](https://github.com/diegosouzapw/OmniRoute/pull/9974)) — thanks @backryun
- **[TS7] chore(types): remove orphan combo manifest metrics** ([#9975](https://github.com/diegosouzapw/OmniRoute/pull/9975)) — thanks @backryun
- **[TS7] fix(types): normalize stream usage before cost calculation** ([#9977](https://github.com/diegosouzapw/OmniRoute/pull/9977)) — thanks @backryun
- **[TS7] fix(stream): collect synthesized Responses tool events** ([#9978](https://github.com/diegosouzapw/OmniRoute/pull/9978)) — thanks @backryun
- **[TS7] fix(types): complete Responses stream failure contract** ([#9979](https://github.com/diegosouzapw/OmniRoute/pull/9979)) — thanks @backryun
- **[TS7] fix(types): narrow chat dispatch contracts** ([#9986](https://github.com/diegosouzapw/OmniRoute/pull/9986)) — thanks @backryun
- **[TS7] fix(types): narrow chatCore local contracts** ([#9987](https://github.com/diegosouzapw/OmniRoute/pull/9987)) — thanks @backryun
- **[TS7] fix(types): preserve GHE Copilot executor configuration** ([#9988](https://github.com/diegosouzapw/OmniRoute/pull/9988)) — thanks @backryun
- **[TS7] fix(types): validate Fal video result URLs** ([#9989](https://github.com/diegosouzapw/OmniRoute/pull/9989)) — thanks @backryun
- **[TS7] fix(types): narrow Claude stream deltas** ([#9990](https://github.com/diegosouzapw/OmniRoute/pull/9990)) — thanks @backryun
- **provider(agnes):refresh model catalog** ([#9998](https://github.com/diegosouzapw/OmniRoute/pull/9998)) — thanks @backryun
- **docs: fix duplicated word in MCP server audit logging section** ([#10000](https://github.com/diegosouzapw/OmniRoute/pull/10000)) — thanks @TengSivtean
- **docs: fix stale tool count (105 -> 104) in MCP server docs** ([#10002](https://github.com/diegosouzapw/OmniRoute/pull/10002)) — thanks @TengSivtean
- **Document default behavior for ToS-flagged free-tier providers** ([#10013](https://github.com/diegosouzapw/OmniRoute/pull/10013)) — thanks @yulinlina
- **[TS7] fix(tinycms): align executor and signer contracts** ([#10087](https://github.com/diegosouzapw/OmniRoute/pull/10087)) — thanks @backryun
- **[TS7] fix(types): restore provider breaker predicate import** ([#10088](https://github.com/diegosouzapw/OmniRoute/pull/10088)) — thanks @backryun
- **[TS7] ci: block new TypeScript 7 diagnostics** ([#10134](https://github.com/diegosouzapw/OmniRoute/pull/10134)) — thanks @backryun
- **chore(repo): remove tracked local artifacts** ([#10178](https://github.com/diegosouzapw/OmniRoute/pull/10178)) — thanks @backryun
- **maintenance — direct pushes (rollup):** release-gate and base-red repairs pushed straight to the release branch (typecheck, unit, quality-ratchet, file-size and ESLint-baseline corrections, stale assertion updates), repository hygiene (`_tasks` symlink untracking, `.source`/`.playwright-cli`/`.cbmignore` ignore entries, Electron build-output ignores, Open Collective link removal) and CI re-triggers after GitHub Actions incidents
- **deps (rollup):** dependency bumps and lockfile maintenance across the cycle — Dependabot groups and manual CVE-driven bumps ([#9081](https://github.com/diegosouzapw/OmniRoute/pull/9081), [#9082](https://github.com/diegosouzapw/OmniRoute/pull/9082), [#9427](https://github.com/diegosouzapw/OmniRoute/pull/9427), [#9458](https://github.com/diegosouzapw/OmniRoute/pull/9458), [#9459](https://github.com/diegosouzapw/OmniRoute/pull/9459), [#9461](https://github.com/diegosouzapw/OmniRoute/pull/9461), [#9462](https://github.com/diegosouzapw/OmniRoute/pull/9462), [#9472](https://github.com/diegosouzapw/OmniRoute/pull/9472))
- **docs/chore (rollup):** documentation, refactoring and repository-hygiene upkeep across the cycle ([#8954](https://github.com/diegosouzapw/OmniRoute/pull/8954), [#8991](https://github.com/diegosouzapw/OmniRoute/pull/8991), [#9059](https://github.com/diegosouzapw/OmniRoute/pull/9059), [#9194](https://github.com/diegosouzapw/OmniRoute/pull/9194), [#9258](https://github.com/diegosouzapw/OmniRoute/pull/9258), [#9508](https://github.com/diegosouzapw/OmniRoute/pull/9508))
- **main-branch plumbing (rollup):** work that landed on `main` between cycles and was carried into this one — the Mergify merge-queue migration and tuning (#7168, #7179, #7216, #7220, #7225), npm-publish unblock via dynamic runner + CI build reuse (#8941), CodeQL-driven e2e mock hardening (#7559), hermetic self-ref guard (#6634, #7341), coverage-baseline tightening (#7347), Dependabot alert resolutions via npm overrides (#8067, #8070), README flag/doc-link polish (#8317), and the v3.8.49 release plumbing itself (#7076)
### 🙌 Contributors
Thanks to everyone whose work landed in v3.8.50:
| Contributor | PRs / Issues |
| --- | --- |
| [@AbdullahFageeh](https://github.com/AbdullahFageeh) | #9087 |
| [@adevwithpurpose](https://github.com/adevwithpurpose) | #9790 |
| [@adrianojiu](https://github.com/adrianojiu) | #8438 |
| [@agisota](https://github.com/agisota) | #9837 |
| [@AgnesRiber](https://github.com/AgnesRiber) | #9718, #9976 |
| [@ahmet-cetinkaya](https://github.com/ahmet-cetinkaya) | #8878 |
| [@AIB1TAL0S](https://github.com/AIB1TAL0S) | #9284 |
| [@AlanSyue](https://github.com/AlanSyue) | direct commit / report |
| [@alex-jordan547](https://github.com/alex-jordan547) | #9235, #9245, #9813 |
| [@AndrianBalanescu](https://github.com/AndrianBalanescu) | #8888, #8889, #8890, #8891, #8892, #8893, #8894, #8895 |
| [@AnhLead](https://github.com/AnhLead) | #9722 |
| [@aniketshukla1](https://github.com/aniketshukla1) | #9148 |
| [@Anjielon](https://github.com/Anjielon) | #8776 |
| [@apoapostolov](https://github.com/apoapostolov) | #8916 |
| [@ARC345](https://github.com/ARC345) | #9628 |
| [@artickc](https://github.com/artickc) | #8571, #8578, #8791, #8843, #8870, #8927, #8974, #9097, #9549 |
| [@Arul-](https://github.com/Arul-) | #9761 |
| [@b1nhm1nh](https://github.com/b1nhm1nh) | direct commit / report |
| [@backryun](https://github.com/backryun) | #8228, #8451, #8627, #8809, #8818, #9084, #9086, #9090, #9091, #9092, #9093, #9114, #9119, #9120, #9122, #9135, #9136, #9137, #9138, #9139, #9141, #9561, #9562, #9563, #9564, #9565, #9566, #9742, #9747, #9748, #9751, #9753, #9755, #9791, #9792, #9793, #9795, #9796, #9797, #9798, #9920, #9972, #9973, #9974, #9975, #9977, #9978, #9979, #9984, #9986, #9987, #9988, #9989, #9990, #9998, #10087, #10088, #10134, #10178 |
| [@Benson-mk](https://github.com/Benson-mk) | #8369 |
| [@benzntech](https://github.com/benzntech) | #9810, #9812, #9939 |
| [@Bl0ck154](https://github.com/Bl0ck154) | #9231 |
| [@branben](https://github.com/branben) | #9940 |
| [@Chewji9875](https://github.com/Chewji9875) | #9257, #9420, #9821, #9994 |
| [@chirag127](https://github.com/chirag127) | #6674 |
| [@chloeassistant](https://github.com/chloeassistant) | #9675, #9746 |
| [@configurowebmax](https://github.com/configurowebmax) | #8877 |
| [@corefusiion](https://github.com/corefusiion) | #8285 |
| [@costaeder](https://github.com/costaeder) | #8626, #8629, #8630 |
| [@csoftware-arigpt](https://github.com/csoftware-arigpt) | #3440 |
| [@DaDecky](https://github.com/DaDecky) | direct commit / report |
| [@ddarkr](https://github.com/ddarkr) | #9035, #9036 |
| [@Dingding-leo](https://github.com/Dingding-leo) | #7987, #8640, #8678, #8704, #8774, #8790, #8808, #8817 |
| [@DinonowDev](https://github.com/DinonowDev) | #8804 |
| [@Dragost](https://github.com/Dragost) | #8339 |
| [@dsitmilis](https://github.com/dsitmilis) | direct commit / report |
| [@Egorich-print](https://github.com/Egorich-print) | #9001, #9020, #9058 |
| [@engmarcosjr](https://github.com/engmarcosjr) | #9993 |
| [@epsilonode](https://github.com/epsilonode) | #8871 |
| [@ervareza](https://github.com/ervareza) | direct commit / report |
| [@fajarhide](https://github.com/fajarhide) | #9191, #9198 |
| [@fenix007](https://github.com/fenix007) | #9618 |
| [@Gecky2102](https://github.com/Gecky2102) | #9280 |
| [@Gioxaa](https://github.com/Gioxaa) | #9162, #9171 |
| [@HaoNgo232](https://github.com/HaoNgo232) | direct commit / report |
| [@Hariprajwal](https://github.com/Hariprajwal) | #9922 |
| [@hartmark](https://github.com/hartmark) | #9635, #9704, #9711, #9712, #9727, #9734, #9735, #9738, #9741, #9744, #9745, #9822 |
| [@Hdiaktoros](https://github.com/Hdiaktoros) | #8930 |
| [@HectorBernstorff](https://github.com/HectorBernstorff) | direct commit / report |
| [@HellFiveOsborn](https://github.com/HellFiveOsborn) | #9248 |
| [@herjarsa](https://github.com/herjarsa) | #9714, #9816, #9937, #9946 |
| [@horacecar](https://github.com/horacecar) | #7679 |
| [@HouMinXi](https://github.com/HouMinXi) | #8886, #8904, #8905, #8976, #8984, #9079, #9106, #9207, #9242, #9328, #9340, #9342, #9351, #9365, #9380, #9381, #9392, #9449, #9482, #9483, #9509, #9510, #9572, #9631, #9634, #9695, #9929 |
| [@hppsc1215](https://github.com/hppsc1215) | #8970 |
| [@Iammilansoni](https://github.com/Iammilansoni) | #9353, #9397 |
| [@ikelvingo](https://github.com/ikelvingo) | #8591, #8872, #9053 |
| [@infinit-X](https://github.com/infinit-X) | #9095 |
| [@isaaclb98](https://github.com/isaaclb98) | #9730 |
| [@jackjinke](https://github.com/jackjinke) | #9556, #9601, #10005 |
| [@jax-novita](https://github.com/jax-novita) | #8913 |
| [@jhordanjw123](https://github.com/jhordanjw123) | #8736 |
| [@jktan0504](https://github.com/jktan0504) | #9025 |
| [@joachimBrindeau](https://github.com/joachimBrindeau) | #9200 |
| [@JoshimOfficial](https://github.com/JoshimOfficial) | #9011 |
| [@jowimila](https://github.com/jowimila) | #9325 |
| [@JxnLexn](https://github.com/JxnLexn) | #8933, #8940, #8944, #8949 |
| [@Kaedo17](https://github.com/Kaedo17) | #8922 |
| [@khoazero123](https://github.com/khoazero123) | #9272 |
| [@KittisakT](https://github.com/KittisakT) | #9423 |
| [@KooshaPari](https://github.com/KooshaPari) | #7329 |
| [@larin-vas](https://github.com/larin-vas) | #9828 |
| [@lazysaltyfish](https://github.com/lazysaltyfish) | direct commit / report |
| [@LeonG606](https://github.com/LeonG606) | #9457 |
| [@Llliao1113](https://github.com/Llliao1113) | #8921 |
| [@lucasalx](https://github.com/lucasalx) | #9919 |
| [@lucasmellos](https://github.com/lucasmellos) | #8925 |
| [@lukiod](https://github.com/lukiod) | #8828 |
| [@luoyide](https://github.com/luoyide) | direct commit / report |
| [@mad-gooze](https://github.com/mad-gooze) | #9052 |
| [@maisdesign](https://github.com/maisdesign) | #8858 |
| [@marchlhw](https://github.com/marchlhw) | #9050 |
| [@matiasbaglieri](https://github.com/matiasbaglieri) | #9707 |
| [@maxmad64bis](https://github.com/maxmad64bis) | #9150, #9246, #9291, #9414 |
| [@McLuck](https://github.com/McLuck) | #8914 |
| [@Michael-Rocco-Goldmann](https://github.com/Michael-Rocco-Goldmann) | #9770, #9773, #9777, #9787 |
| [@MichaelYcJo](https://github.com/MichaelYcJo) | #8244 |
| [@minhnhat166](https://github.com/minhnhat166) | direct commit / report |
| [@MohitRawat017](https://github.com/MohitRawat017) | #8718, #8772, #9605 |
| [@Momen4444](https://github.com/Momen4444) | #9612 |
| [@MrShitFox](https://github.com/MrShitFox) | #9826 |
| [@MumuTW](https://github.com/MumuTW) | #8839 |
| [@mvanhorn](https://github.com/mvanhorn) | #9542 |
| [@Mynacol](https://github.com/Mynacol) | #9733 |
| [@nguyenha935](https://github.com/nguyenha935) | #9044, #9215 |
| [@nosolosoft](https://github.com/nosolosoft) | #8900 |
| [@oyi77](https://github.com/oyi77) | #8299, #8752, #9158, #9818 |
| [@PixmaNts](https://github.com/PixmaNts) | #9432 |
| [@pizzav-xyz](https://github.com/pizzav-xyz) | #9077 |
| [@Poid-ZA](https://github.com/Poid-ZA) | #9467 |
| [@Prudhvivuda](https://github.com/Prudhvivuda) | #8807, #9014, #9015, #9016 |
| [@qianze0628](https://github.com/qianze0628) | #9038 |
| [@raflyazf](https://github.com/raflyazf) | direct commit / report |
| [@Rahulsharma0810](https://github.com/Rahulsharma0810) | #8961 |
| [@rinseaid](https://github.com/rinseaid) | #8945, #9037, #9932, #9933, #9969, #9982 |
| [@rixzkiye](https://github.com/rixzkiye) | direct commit / report |
| [@RobertsXML](https://github.com/RobertsXML) | direct commit / report |
| [@royanrosyad85](https://github.com/royanrosyad85) | direct commit / report |
| [@rushsinging](https://github.com/rushsinging) | #8947 |
| [@ryan-brosas](https://github.com/ryan-brosas) | #9693 |
| [@ryanngit](https://github.com/ryanngit) | direct commit / report |
| [@sadSanta-07](https://github.com/sadSanta-07) | #9938 |
| [@SalyyS1](https://github.com/SalyyS1) | direct commit / report |
| [@Sam280903](https://github.com/Sam280903) | #9274, #9278, #9281 |
| [@seakleangnhak](https://github.com/seakleangnhak) | direct commit / report |
| [@seanford](https://github.com/seanford) | #8523 |
| [@SemonCat](https://github.com/SemonCat) | direct commit / report |
| [@shixi-li](https://github.com/shixi-li) | #9022, #9513, #10001 |
| [@soulhakr](https://github.com/soulhakr) | #8799 |
| [@stanleytejakusuma](https://github.com/stanleytejakusuma) | #9610 |
| [@Stazyu](https://github.com/Stazyu) | #9007, #9226, #9438 |
| [@SupremeNexas](https://github.com/SupremeNexas) | #9913 |
| [@swingtempo](https://github.com/swingtempo) | #9307 |
| [@szzhoujiarui](https://github.com/szzhoujiarui) | #9218 |
| [@tald26](https://github.com/tald26) | #9959 |
| [@taltas](https://github.com/taltas) | direct commit / report |
| [@TechNickAI](https://github.com/TechNickAI) | #9251 |
| [@TengSivtean](https://github.com/TengSivtean) | #10000, #10002 |
| [@TheFrenchGhosty](https://github.com/TheFrenchGhosty) | #9326 |
| [@tuxmonteiro](https://github.com/tuxmonteiro) | #9065 |
| [@vinogradovnet](https://github.com/vinogradovnet) | #9581 |
| [@VXNCXNX](https://github.com/VXNCXNX) | #9111, #9783 |
| [@wgordon17](https://github.com/wgordon17) | #8909, #9233, #9441, #9619 |
| [@Witroch4](https://github.com/Witroch4) | #8713 |
| [@witt3rd](https://github.com/witt3rd) | #9962, #9963 |
| [@xiaoyaner0201](https://github.com/xiaoyaner0201) | #8757, #8869, #8876, #8883, #8906, #8931, #9021, #9027, #9452 |
| [@xz-dev](https://github.com/xz-dev) | #8908, #9199, #9205, #9262, #9290, #9313, #9555, #9569, #9629, #9788, #9983 |
| [@yansigit](https://github.com/yansigit) | #9834, #9911, #9917, #9921 |
| [@yidecode](https://github.com/yidecode) | direct commit / report |
| [@yulinlina](https://github.com/yulinlina) | #10013 |
| [@yutuknown](https://github.com/yutuknown) | #8999 |
| [@zabrodschiipavel-sketch](https://github.com/zabrodschiipavel-sketch) | #9312 |
| [@Zartharas](https://github.com/Zartharas) | #9161, #9164, #9181, #9182, #9184, #9185, #9186, #9189, #9294, #9825, #9833, #9936, #9965, #9992 |
| [@Zenlyte](https://github.com/Zenlyte) | #9005 |
| [@zhiru](https://github.com/zhiru) | #9099, #9101 |
| [@ziuus](https://github.com/ziuus) | #8912 |
| [@zuckdorsey](https://github.com/zuckdorsey) | #9723 |
| [@diegosouzapw](https://github.com/diegosouzapw) | maintainer |
---
## [3.8.49] — 2026-07-28
@@ -2270,10 +1440,6 @@ _Living section — regenerated 2026-07-19 from all 306 cycle commits (bump 2c62
- chore(tests): fix all 70 failing `test:vitest:ui` tests across 30 files (was advisory/parked) — root causes were 15 node:test-authored `.tsx` files never collected by vitest, a missing `window.matchMedia` jsdom polyfill, stale assertions against a redesigned BuildTab wizard / CompressionHub Phase-2 UI, and one obsolete test for a retired Plans screen; suite is now 158/158 files, 870/870 tests green (promotion to blocking is a follow-up)
### 🙌 Contributors
Thanks to everyone whose work landed in v3.8.49:

View File

@@ -3,7 +3,7 @@
@AGENTS.md
**All project rules live in [`AGENTS.md`](AGENTS.md)** — the single source of truth for every AI
assistant (architecture, conventions, testing, quality gates, git workflow, the 23 Hard Rules,
assistant (architecture, conventions, testing, quality gates, git workflow, the 22 Hard Rules,
PII learnings). Read it in full; do not re-add project rules here. Everything below applies ONLY
to Claude Code — operational refinements of rules already defined in `AGENTS.md`.

View File

@@ -3,7 +3,7 @@
Thank you for your interest in contributing! This guide covers everything you need to get started.
For the official per-change workflow, start with the
[Contribution Golden Path](docs/ops/CONTRIBUTION_GOLDEN_PATH.md). It maps provider, routing,
[Contribution Golden Path](docs/dev/CONTRIBUTION_GOLDEN_PATH.md). It maps provider, routing,
UI/UX, i18n, CLI, database, and build/deploy changes to their contracts, focused tests, CI
coverage, and reconciliation steps.
@@ -210,7 +210,7 @@ Coverage notes:
### Pull Request Requirements
Before opening a PR, use the
[Contribution Golden Path](docs/ops/CONTRIBUTION_GOLDEN_PATH.md) to run the focused loop for
[Contribution Golden Path](docs/dev/CONTRIBUTION_GOLDEN_PATH.md) to run the focused loop for
what you changed. The full unit suite (4 CI shards), Vitest, the **60%+** coverage gate, and
the production build are CI's responsibility — running them locally adds no signal the PR
checks will not already give you, and on smaller machines it can saturate the host (#8084):

View File

@@ -93,15 +93,7 @@ RUN --mount=type=cache,id=npm-cache,target=/root/.npm \
# build from 17min to 9min on the same 32-core box. Webpack stays available as the
# escape hatch: `--build-arg`/-e OMNIROUTE_USE_TURBOPACK=0.
# See docs/ops/QUALITY_GATE_PLAYBOOK.md Parte 6.
#
# Declared as ARG+ENV, not a bare ENV: a bare ENV shadows any same-named ARG for
# the rest of the stage, so `--build-arg OMNIROUTE_USE_TURBOPACK=0` was silently
# ignored and the escape hatch above only ever worked via `-e` at runtime, never
# at build time. Turbopack compiles in native Rust memory that lives outside the
# V8 heap, so OMNIROUTE_BUILD_MEMORY_MB cannot bound it and a memory-constrained
# build host gets SIGKILLed by the cgroup OOM killer with no error message.
ARG OMNIROUTE_USE_TURBOPACK=1
ENV OMNIROUTE_USE_TURBOPACK="${OMNIROUTE_USE_TURBOPACK}"
ENV OMNIROUTE_USE_TURBOPACK=1
# Next.js basePath is fixed at build time; pass OMNIROUTE_BASE_PATH here when the
# image should serve under a reverse-proxy subpath without a runtime patch.
@@ -246,11 +238,6 @@ FROM runner-base AS runner-cli
# runner-base runs.
USER root
# The CLI image can use the internal ChatGPT Web (Codex) Chromium sidecar over
# CDP without installing a second browser in this container.
COPY --from=builder /app/node_modules/playwright-core ./node_modules/playwright-core
COPY --from=builder /app/node_modules/playwright ./node_modules/playwright
# Install system dependencies required by openclaw (git+ssh references).
RUN --mount=type=cache,id=apt-cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,id=apt-lists,target=/var/lib/apt/lists,sharing=locked \

View File

@@ -1,7 +1,7 @@
# GEMINI.md
> **Single source of truth:** all project rules for AI assistants live in
> [`AGENTS.md`](AGENTS.md). Read it in full before any change — it contains the 23 Hard Rules,
> [`AGENTS.md`](AGENTS.md). Read it in full before any change — it contains the 22 Hard Rules,
> quality gates, code conventions, file-placement / repo-root hygiene rules, the repository map
> and the local development access notes that used to live in this file.

0
MAX Normal file
View File

View File

@@ -1,69 +0,0 @@
.PHONY: help install dev start build build-release lint typecheck typecheck-strict \
test test-unit test-vitest test-coverage test-all test-integration test-e2e \
check check-cycles check-docs env-sync clean
# OmniRoute — convenience wrapper around the npm scripts.
# All targets delegate to the canonical package.json scripts (single source of truth).
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}'
install: ## Install dependencies (auto-generates .env from .env.example)
npm install
dev: ## Dev server at http://localhost:20128
npm run dev
start: ## Production server (requires a prior build)
npm run start
build: ## Production build (Next.js 16 standalone)
npm run build
build-release: ## Release build
npm run build:release
lint: ## ESLint (0 errors expected)
npm run lint
typecheck: ## TypeScript check (core)
npm run typecheck:core
typecheck-strict: ## Strict check (no implicit any)
npm run typecheck:noimplicit:core
test: ## Unit tests (Node native runner)
npm run test:unit
test-unit: ## Alias for `test`
npm run test:unit
test-vitest: ## Vitest (MCP server, autoCombo, cache)
npm run test:vitest
test-coverage: ## Unit tests + coverage gate (60/60/60/60)
npm run test:coverage
test-all: ## All suites (unit + vitest + ecosystem + e2e)
npm run test:all
test-integration: ## Integration tests
npm run test:integration
test-e2e: ## E2E (Playwright)
npm run test:e2e
check: ## lint + test combined
npm run check
check-cycles: ## Detect circular dependencies
npm run check:cycles
check-docs: ## Validate documentation (incl. fabricated-docs)
npm run check:docs-all
env-sync: ## Sync .env from .env.example
npm run env:sync
clean: ## Remove build artifacts
rm -rf .build dist coverage .eslintcache

View File

@@ -7,19 +7,19 @@
# 🚀 OmniRoute — The Free AI Gateway
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 338 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 1595% tokens (~89% avg) — never hit limits. 338 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 291 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 1595% tokens (~89% avg) — never hit limits. 291 AI providers · 90+ free tiers · ~1.53B free tokens/mo · 19 routing strategies · $0 to start."/>
</div>
<div align="center">
## 💰 ~1.51B Free Tokens / Month
## 💰 ~1.53B Free Tokens / Month
</div>
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute aggregates the **documented** free tiers of **42 provider pools / 495 models** into one honest number and shows it live on the dashboard (`/dashboard/free-tiers`).
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute aggregates the **documented** free tiers of **43 provider pools / 516 models** into one honest number and shows it live on the dashboard (`/dashboard/free-tiers`).
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.51B free tokens per month steady, up to ~2.13B in the first month with signup credits, from the documented free tiers of 42 provider pools / 495 models behind one endpoint. Honest pool-deduped math — each shared pool counted once (counting every rate limit 24/7 would read ~10B; not published), 15 providers ToS-flagged so you decide. Budget bar of the countable free pools with per-model grid (Mistral Large 3 1B, GPT-4o mini 150M, Gemini 2.5 Flash 60M … Claude Sonnet 4.5 25K), one-time first-month signup credits (vertex 300M, agentrouter 200M, predibase 25M, together 25M, glm-cn 20M, doubao 15M, ai21 10M, longcat 10M, deepseek 5M, hyperbolic 5M, nscale 5M), plus permanently-free no-token-cap providers (SiliconFlow, Z.AI GLM-Flash, Kilo, OpenCode Zen, baidu …) and a $10 OpenRouter top-up unlocking +24M/mo — surfaced separately so they never inflate the headline. Live used/remaining on /dashboard/free-tiers."/>
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.53B free tokens per month steady, up to ~2.15B in the first month with signup credits, from the documented free tiers of 43 provider pools / 516 models behind one endpoint. Honest pool-deduped math — each shared pool counted once (counting every rate limit 24/7 would read ~10B; not published), 15 providers ToS-flagged so you decide. Budget bar of the countable free pools with per-model grid (Mistral Large 3 1B, GPT-4o mini 150M, Gemini 2.5 Flash 60M … Claude Sonnet 4.5 25K), one-time first-month signup credits (vertex 300M, agentrouter 200M, predibase 25M, together 25M, glm-cn 20M, doubao 15M, ai21 10M, longcat 10M, deepseek 5M, hyperbolic 5M, nscale 5M), plus permanently-free no-token-cap providers (SiliconFlow, Z.AI GLM-Flash, Kilo, OpenCode Zen, baidu …) and a $10 OpenRouter top-up unlocking +24M/mo — surfaced separately so they never inflate the headline. Live used/remaining on /dashboard/free-tiers."/>
> Animated summary of the live `/dashboard/free-tiers` page. Full methodology (pool dedupe, credit tiers, provider terms): **[docs/reference/FREE_TIERS.md](docs/reference/FREE_TIERS.md)**.
>
@@ -56,25 +56,6 @@
<br/>
## 📈 The Gateway Keeps Growing
<div align="center">
| | v3.8.49 | **v3.8.50** | `v3.8.51+` |
| ------------------------- | :-----: | :---------: | :---------: |
| 🌐 Providers | 291 | **338** | more queued |
| 🧠 Documented models | 500+ | **1200+** | — |
| 🖼️ Modality Bridge | — | 🆕 vision | video |
| 📡 Radar free catalog | — | — | 🔭 next |
| ⚖️ Quota-aware scheduling | — | — | 🔭 next |
| 📊 Quota telemetry | — | — | 🔭 next |
**→ [Roadmap](ROADMAP.md) — riding the rail to `v3.9.0 LTS`**
</div>
<br/>
## 🧩 Available
[![npm version](https://img.shields.io/npm/v/omniroute?color=cb3837&logo=npm)](https://www.npmjs.com/package/omniroute)
@@ -100,7 +81,7 @@
<tr>
<td align="right"><b>⚙️ Features</b></td>
<td align="center"><a href="#-combos--the-flagship">🎯 Combos</a></td>
<td align="center"><a href="#-338-ai-providers--90-free">🌐 Providers</a></td>
<td align="center"><a href="#-291-ai-providers--90-free">🌐 Providers</a></td>
<td align="center"><a href="#-full-cli--a2a--mcp">🔌 CLI &amp; MCP</a></td>
</tr>
<tr>
@@ -199,8 +180,6 @@ curl http://localhost:20128/v1/chat/completions \
<sub>Prefer a specific free backend? Call it directly, e.g. `oc/…` (OpenCode Free) or `felo/…` (Felo). Then graduate to `auto` and let OmniRoute pick.</sub>
<sub>📦 Copy-paste quickstart scripts for **Python, Node.js, PHP, and cURL** → [`examples/quickstart/`](examples/quickstart/)</sub>
<br/>
<div align="center">
@@ -209,7 +188,7 @@ curl http://localhost:20128/v1/chat/completions \
</div>
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint. 338 providers. Never stop building — OmniRoute picks the cheapest one that works. Six pillars: Never hit limits (auto-fallback across 338 providers in milliseconds, zero downtime) · Save up to 95% tokens (RTK + Caveman stacked compression cuts 1595%, ~89% avg on tool-heavy sessions) · $0 to start (90+ free tiers, 40+ free forever — no card needed) · Every tool works (33 coding agents through one config) · One endpoint (OpenAI ↔ Claude ↔ Gemini ↔ Responses API at /v1) · Production-grade (circuit breakers, TLS stealth, MCP 105 tools, A2A, memory, guardrails, evals — 25,000+ tests)."/>
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint. 291 providers. Never stop building — OmniRoute picks the cheapest one that works. Six pillars: Never hit limits (auto-fallback across 291 providers in milliseconds, zero downtime) · Save up to 95% tokens (RTK + Caveman stacked compression cuts 1595%, ~89% avg on tool-heavy sessions) · $0 to start (90+ free tiers, 40+ free forever — no card needed) · Every tool works (33 coding agents through one config) · One endpoint (OpenAI ↔ Claude ↔ Gemini ↔ Responses API at /v1) · Production-grade (circuit breakers, TLS stealth, MCP 105 tools, A2A, memory, guardrails, evals — 25,000+ tests)."/>
<br/>
<br/>
@@ -428,7 +407,7 @@ All **19** strategies — mix & match per combo step:
<tr>
<td align="center">17</td>
<td nowrap><code>auto</code></td>
<td>14-factor live scoring across every connection 🤖</td>
<td>12-factor live scoring across every connection 🤖</td>
</tr>
<tr>
<td align="center">18</td>
@@ -442,7 +421,7 @@ All **19** strategies — mix & match per combo step:
</tr>
</table>
<sub>The Auto-Combo engine scores every candidate on **14 factors** (health, quota, cost, latency, success rate, freshness…) — see [`docs/routing/AUTO-COMBO.md`](docs/routing/AUTO-COMBO.md).</sub>
<sub>The Auto-Combo engine scores every candidate on **12 factors** (health, quota, cost, latency, success rate, freshness…) — see [`docs/routing/AUTO-COMBO.md`](docs/routing/AUTO-COMBO.md).</sub>
##
@@ -460,7 +439,7 @@ All **19** strategies — mix & match per combo step:
</div>
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — comparison table vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 338 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, built-in MCP server with 105 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA, 43 i18n UI locales, 100% MIT self-hosted. OmniRoute is the only one with the full set; competitors show a mix of checks, partials and crosses. Verified from each project&apos;s docs."/>
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — comparison table vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 291 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, built-in MCP server with 105 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA, 43 i18n UI locales, 100% MIT self-hosted. OmniRoute is the only one with the full set; competitors show a mix of checks, partials and crosses. Verified from each project&apos;s docs."/>
<sub>📊 Full methodology &amp; per-feature detail vs 9router, OpenRouter, CLIProxyAPI &amp; LiteLLM → [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)</sub>
@@ -518,11 +497,8 @@ Pix copia-e-cola:
</div>
> Recent highlights from **v3.8.20 → v3.8.50**. Full history in [`CHANGELOG.md`](CHANGELOG.md).
> Recent highlights from **v3.8.20 → v3.8.49**. Full history in [`CHANGELOG.md`](CHANGELOG.md).
- **🎛️ OmniConductor** — inbound A2A delegation to your agent fleet, Conductor skills on the Agent Card, and a dashboard panel with Faro push-to-talk voice chat. → [A2A Server](docs/frameworks/A2A-SERVER.md)
- **🛂 Adaptive admission & overload protection** — heavyweight chat requests queue instead of 503ing, with atomic RPM rolling leases per connection. → [Resilience Guide](docs/architecture/RESILIENCE_GUIDE.md)
- **🗂️ Canonical `/v1/models` ordering** — one contiguous provider-grouped block per provider (combos pinned first), stable across every catalog source. → [API Reference](docs/reference/API_REFERENCE.md)
- **🗜️ Compression hardening** — default-on inflation guard, Caveman packs for DE / FR / JA + Chinese (wényán), RTK filters for Gradle & .NET. → [Compression](docs/compression/COMPRESSION_ENGINES.md)
- **💸 Honest flat-rate cost** — subscription / coding-plan providers read **$0** in cost analytics; budget, quota & routing keep estimating. → [API Reference](docs/reference/API_REFERENCE.md)
- **⚖️ Quota-Share routing** — split a shared account's quota fairly across pooled keys, work-conserving so idle slices are lent out. → [Resilience Guide](docs/architecture/RESILIENCE_GUIDE.md)
@@ -537,7 +513,7 @@ Pix copia-e-cola:
- **🖼️ New endpoints** — `/v1/ocr` (Mistral OCR) and `/v1/audio/translations` (Whisper-style) round out the media surface. → [API Reference](docs/reference/API_REFERENCE.md)
- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Freepik, Adobe Firefly, Microsoft Designer, Google Imagen, Segmind, EdgeTTS. → [API Reference](docs/reference/API_REFERENCE.md)
- **🌍 Deployment & ops** — reverse-proxy `basePath`, browser-language auto-detect, per-key device tracking, root-less MITM trust, zh-TW localization. → [Environment](docs/reference/ENVIRONMENT.md)
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **338-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **291-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
- **📡 Routing transparency** — every response carries an `X-OmniRoute-Decision` header naming the strategy/provider/latency that served it, a new `cache-optimized` combo strategy + Auto-Combo `cacheAffinity` factor route repeat requests back to the connection holding the cached prefix, and a read-only `/v1/auto-combo/{channel}/candidates` endpoint exposes an `auto/*` channel's live candidate pool. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
- **⚡ Local performance & infra** — one-click local Redis, Cloudflare Workers / Deno Deploy relay deployers, Bifrost & Mux as supervised embedded services. → [Embedded Services](docs/frameworks/EMBEDDED-SERVICES.md)
@@ -598,11 +574,11 @@ Pix copia-e-cola:
<div align="center">
## 🌐 338 AI Providers — 90+ Free
## 🌐 291 AI Providers — 90+ Free
</div>
> The most complete catalog of any open-source router: **338 providers**, **90+ with a free tier**, **40+ free forever**.
> The most complete catalog of any open-source router: **291 providers**, **90+ with a free tier**, **40+ free forever**.
<div align="center">
@@ -866,7 +842,7 @@ npm install -g omniroute
omniroute
```
> 💡 See `npm warn ERESOLVE` or peer-dep warnings? [They're harmless](docs/guides/TROUBLESHOOTING.md#npm-install-warnings-eresolve--peer--deprecated).
> 💡 See `npm warn ERESOLVE` or peer-dep warnings? [They're harmless](docs/getting-started/TROUBLESHOOTING.md#npm-install-warnings-eresolve--peer--deprecated).
Dashboard at `http://localhost:20128` · API at `http://localhost:20128/v1`.
@@ -918,7 +894,7 @@ docker run -d --name omniroute --restart unless-stopped --stop-timeout 40 \
> `diegosouzapw/omniroute:next-web` follow the current default `release/v*`
> branch. These mutable tags are intended only for testing unreleased fixes and
> are **not supported for production**. See
> [Docker Release Channels](docs/guides/DOCKER_GUIDE.md#release-channels).
> [Docker Release Channels](docs/guides/DOCKER_RELEASE_CHANNELS.md).
**🛠️ From source**
@@ -1060,7 +1036,7 @@ same process on one port, so there is no separate CLI-only package today.
<tr><td nowrap><b>Runtime</b></td><td>Node.js 22.x / 24.x LTS — <code>&gt;=22.22.2 &lt;23 || &gt;=24.0.0 &lt;27</code></td></tr>
<tr><td nowrap><b>Language</b></td><td>TypeScript 6.0 — <b>100% TypeScript</b> across <code>src/</code> and <code>open-sse/</code> (zero <code>any</code> in core since v2.0)</td></tr>
<tr><td nowrap><b>Framework</b></td><td>Next.js 16 + React 19 + Tailwind CSS 4</td></tr>
<tr><td nowrap><b>Database</b></td><td>better-sqlite3 (SQLite, WAL journaling) + LowDB (JSON legacy) — 95 domain modules, 144 migrations</td></tr>
<tr><td nowrap><b>Database</b></td><td>better-sqlite3 (SQLite, WAL journaling) + LowDB (JSON legacy) — 95 domain modules, 110 migrations</td></tr>
<tr><td nowrap><b>Memory</b></td><td>SQLite FTS5 full-text + int8-quantized vector embeddings, typed decay</td></tr>
<tr><td nowrap><b>Schemas</b></td><td>Zod 4 — MCP tool I/O validation + API contracts</td></tr>
<tr><td nowrap><b>Protocols</b></td><td>MCP (stdio / HTTP / SSE) + A2A v0.3 (JSON-RPC 2.0 + SSE)</td></tr>
@@ -1121,9 +1097,9 @@ same process on one port, so there is no separate CLI-only package today.
<tr><td nowrap><b><a href="docs/compression/COMPRESSION_RULES_FORMAT.md">Compression Rules Format</a></b></td><td>JSON rule-pack schemas for Caveman and RTK filters</td></tr>
<tr><td nowrap><b><a href="docs/compression/COMPRESSION_LANGUAGE_PACKS.md">Compression Language Packs</a></b></td><td>Language detection and Caveman rule-pack authoring</td></tr>
<tr><td nowrap><b><a href="docs/architecture/RESILIENCE_GUIDE.md">Resilience Guide</a></b></td><td>Circuit breakers, cooldowns, queue, anti-thundering herd, TLS spoofing</td></tr>
<tr><td nowrap><b><a href="docs/routing/AUTO-COMBO.md">Auto-Combo Engine</a></b></td><td>14-factor scoring, mode packs, self-healing</td></tr>
<tr><td nowrap><b><a href="docs/routing/AUTO-COMBO.md">Auto-Combo Engine</a></b></td><td>12-factor scoring, mode packs, self-healing</td></tr>
<tr><td nowrap><b><a href="docs/ops/PROXY_GUIDE.md">Proxy Guide</a></b></td><td>3-level proxy system, 1proxy marketplace, registry CRUD</td></tr>
<tr><td nowrap><b><a href="docs/reference/FREE_TIERS.md">Free Tiers</a></b></td><td>90+ free providers consolidated directory (42 documented token pools / 495 models)</td></tr>
<tr><td nowrap><b><a href="docs/reference/FREE_TIERS.md">Free Tiers</a></b></td><td>25+ free API providers consolidated directory</td></tr>
<tr><td nowrap><b><a href="docs/guides/FEATURES.md">Features Gallery</a></b></td><td>Visual dashboard tour with screenshots</td></tr>
<tr><td nowrap><b><a href="docs/architecture/CODEBASE_DOCUMENTATION.md">Codebase Documentation</a></b></td><td>Beginner-friendly codebase walkthrough</td></tr>
</table>
@@ -1134,7 +1110,7 @@ same process on one port, so there is no separate CLI-only package today.
<tr><th align="left">Document</th><th align="left">Description</th></tr>
<tr><td nowrap><b><a href="docs/reference/API_REFERENCE.md">API Reference</a></b></td><td>All endpoints with examples</td></tr>
<tr><td nowrap><b><a href="docs/openapi.yaml">OpenAPI Spec</a></b></td><td>OpenAPI 3.0 specification</td></tr>
<tr><td nowrap><b><a href="open-sse/mcp-server/README.md">MCP Server</a></b></td><td>105 MCP tools, IDE configs, Python/TS/Go clients</td></tr>
<tr><td nowrap><b><a href="open-sse/mcp-server/README.md">MCP Server</a></b></td><td>104 MCP tools, IDE configs, Python/TS/Go clients</td></tr>
<tr><td nowrap><b><a href="docs/frameworks/MCP-SERVER.md">MCP Server Guide</a></b></td><td>MCP installation, transports, and tool reference</td></tr>
<tr><td nowrap><b><a href="src/lib/a2a/README.md">A2A Server</a></b></td><td>JSON-RPC 2.0 protocol, skills, streaming, task mgmt</td></tr>
<tr><td nowrap><b><a href="docs/frameworks/A2A-SERVER.md">A2A Server Guide</a></b></td><td>A2A agent card, tasks, skills, and streaming</td></tr>
@@ -1148,7 +1124,7 @@ same process on one port, so there is no separate CLI-only package today.
<tr><td nowrap><b><a href="docs/ops/BRANCHING_MODEL.md">Branching & Release Model</a></b></td><td>Where PRs target (<code>release/*</code>), what <code>main</code> and tags mean</td></tr>
<tr><td nowrap><b><a href="CHANGELOG.md">Changelog</a></b></td><td>Full per-version release history</td></tr>
<tr><td nowrap><b><a href="SECURITY.md">Security Policy</a></b></td><td>Vulnerability reporting and security practices</td></tr>
<tr><td nowrap><b><a href="docs/guides/I18N.md">i18n Guide</a></b></td><td>43-language support, translation workflow, RTL</td></tr>
<tr><td nowrap><b><a href="docs/guides/I18N.md">i18n Guide</a></b></td><td>40+ language support, translation workflow, RTL</td></tr>
<tr><td nowrap><b><a href="docs/ops/RELEASE_CHECKLIST.md">Release Checklist</a></b></td><td>Pre-release validation steps</td></tr>
<tr><td nowrap><b><a href="docs/ops/COVERAGE_PLAN.md">Coverage Plan</a></b></td><td>Test coverage strategy and 25,000+ test suite</td></tr>
</table>
@@ -1291,7 +1267,7 @@ A heartfelt thank-you to the people who fund OmniRoute out of their own pocket
<div align="center">
## 👥 320+ Contributors
## 👥 500+ Contributors
</div>

View File

@@ -42,7 +42,7 @@ Request → CORS → Authz pipeline (classify → policies → enforce)
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Dashboard Login** | Password-based auth with JWT tokens (HttpOnly cookies) |
| **API Key Auth** | HMAC-signed keys with CRC validation |
| **OAuth 2.0 + PKCE** | Provider-specific browser/device OAuth uses PKCE where supported; import-only Devin credentials are handled separately. |
| **OAuth 2.0 + PKCE** | 13 providers (Claude, Codex, GitHub, Cursor, Antigravity, Gemini, Kimi Coding, Kilo Code, Cline, Kiro, Qoder, Windsurf, GitLab Duo) |
| **Token Refresh** | Automatic OAuth token refresh before expiry |
| **Secure Cookies** | `AUTH_COOKIE_SECURE=true` for HTTPS environments |
| **Authz Pipeline** | Route classification (PUBLIC / CLIENT_API / MANAGEMENT) — see `docs/architecture/AUTHZ_GUIDE.md` |

View File

@@ -1,26 +0,0 @@
# Third-Party Notices
## codex-chatgpt-web
Parts of `open-sse/vendor/codex-chatgpt-web/` are adapted from
[`miuuyy/codex-chatgpt-web`](https://github.com/miuuyy/codex-chatgpt-web), commit
`55592fca0ba19a27f1b769cec8fff61ff340a785`.
MIT License
Copyright (c) 2026 codex-chatgpt-web contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,296 @@
# Relatorio de pesquisa: repositorios de CLI integraveis com OmniRoute
> **Status final (2026-08-03):** este documento preserva o inventário inicial. A pesquisa foi concluída para `104/104` casos. Para resultados por projeto, use `04-tracker-integracoes-clis.md`; para o fechamento executivo e a estratégia de publicação, use `06-relatorio-final-104-clis-e-estrategia-prs.md`.
**Data da pesquisa:** 2026-08-01
**Escopo:** agentes de codigo de terminal, CLIs de LLM, runtimes de agentes e harnesses que possam consumir um endpoint HTTP compativel com OpenAI, Anthropic ou Gemini, ou que possam ser adaptados por provider/plugin/ACP/MITM.
**Fonte local principal:** `_tasks/hands-off/2026-08-01_release-v3.8.50_v3.8.50_sess-e1846bc2/handoff.md`
**Fontes externas principais:** GitHub Search/API, READMEs dos repositorios e a lista publica `bradAGI/awesome-cli-coding-agents` (atualizada em 2026-07-29).
## 1. Resumo executivo
O OmniRoute ja possui uma integracao funcional com o jcode e um catalogo local de ferramentas CLI. O proximo ganho de maior valor e transformar o OmniRoute em um endpoint reconhecido pelos principais agentes de terminal, priorizando configuracao nativa e PR upstream quando o projeto aceitar contribuicoes.
A pesquisa encontrou:
- **33 entradas de ferramentas no registro local `CLI_TOOLS`**, contando o registro extraido de Grok Build em `src/shared/constants/cliToolsGrokBuild.ts`, incluindo Claude Code, Codex CLI, Cline, Kilo, Continue, OpenCode, Aider, jcode, Smelt, Pi, Crush, Goose, Open Interpreter, OpenClaw, Hermes Agent, Letta CLI e outros.
- **Mais de 90 projetos publicos** no inventario externo consultado, entre agentes de codigo, CLIs generalistas, forks, runtimes e orquestradores.
- **Candidatos com evidencia forte de endpoint customizavel:** Gemini CLI, Claw Code, Plandex, MiMo Code, Trae Agent, Kimi CLI, Every Code, Open Codex, VT Code, OpenHands CLI, gptme, Nanocoder, RA.Aid, CoreCoder, Grok CLI, Gitlawb Zero, DeepSeek Reasonix, KlaatCode, CodeMini, DvalinCode, Coro Code, Mini-Kode, Late CLI, Agentty, Aizen, Minacode, YottaCode, aichat, ShellGPT, Mistral Vibe, OpenSquilla, Kode CLI e outros.
- **Candidatos que exigem pesquisa confirmatoria:** projetos com README generico, configuracao recente, repositorio ambiguo, binario fechado ou sem evidencia textual suficiente de `base_url`/provider.
- **Candidatos que podem ser integrados por outros caminhos:** ACP, MCP, wrapper/launcher, provider adapter, proxy MITM ou apenas documentacao; eles nao devem ser classificados automaticamente como OpenAI-compatible.
Conclusao: devemos pesquisar e tentar todos os candidatos tecnicamente viaveis, mas separar claramente `suporte no catalogo OmniRoute`, `configuracao generica`, `adaptacao upstream publicada` e `PR/issue aceita`. O tracker acompanha essas dimensoes separadamente.
## 2. Metodo e limites
### 2.1 Como a busca foi feita
1. Leitura integral do handoff do caso jcode para capturar o padrao de integracao, validacao, publicacao e as restricoes de worktree.
2. Inspecao do catalogo local em `src/shared/constants/cliTools.ts`, da documentacao de CLI e do fluxo de setup em `docs/guides/CLI-INTEGRATIONS.md`.
3. Consulta do GitHub Search/API para resolver o repositorio canonico de cada nome, evitando homonimos.
4. Leitura de README/raw quando disponivel, procurando sinais como `base_url`, `baseURL`, `OPENAI_BASE_URL`, `OPENAI_API_BASE`, `LLM_BASE_URL`, `provider`, `gateway`, `model provider`, `Anthropic` e `Gemini`.
5. Consulta da lista `https://github.com/bradAGI/awesome-cli-coding-agents`, que serve como descoberta ampla, nao como prova de compatibilidade.
6. Classificacao por adocao, manutencao, licenca, evidencia de endpoint, maturidade, potencial de PR e utilidade para o ecossistema OmniRoute.
### 2.2 O que ainda nao foi afirmado
- Nao foi feita implementacao ou abertura de PR/issue para os candidatos abaixo; o unico caso publicado nesta sessao anterior e o jcode.
- A presenca da palavra `provider` no README nao prova que uma URL arbitraria funciona em runtime.
- Estrelas e datas sao snapshots aproximados obtidos em 2026-08-01 e podem mudar.
- Repositorios fechados ou com EULA entram no inventario para avaliacao de configuracao, mas nao implicam possibilidade de fork ou PR.
- Cada task de integracao precisa repetir a pesquisa no upstream antes de editar codigo.
## 3. Baseline do OmniRoute
### 3.1 Superficie que o OmniRoute oferece
- Endpoint OpenAI em `/v1`.
- Superficie Anthropic na raiz, usada por clientes que esperam `/v1/messages` a partir do `ANTHROPIC_BASE_URL`.
- Superficie Gemini em `/v1beta`.
- Catalogo de modelos consultavel pelos comandos de setup quando o cliente suporta descoberta.
- Chave via `OMNIROUTE_API_KEY` ou chave selecionada no dashboard.
- Traducao entre formatos, streaming SSE, tool calling, fallback, combos, custos e politicas de autenticacao.
- Modos de consumo: configuracao de ambiente, arquivo nativo do cliente, provider customizado, ACP/MCP e MITM.
### 3.2 Catalogo local ja registrado
Fonte: `src/shared/constants/cliTools.ts` e `src/shared/constants/cliToolsGrokBuild.ts`.
**Codigo/CLI:** Claude Code, OpenAI Codex CLI, Factory Droid, OpenClaw, Cursor, Cline, Kilo Code, Continue, Antigravity, GitHub Copilot CLI, OpenCode, Kiro, Qwen Code, Aider, ForgeCode, Cursor Agent CLI, Roo Code, jcode, DeepSeek TUI, CodeWhale, Smelt, Pi, Crush.
**Agentes:** Hermes, Hermes Agent, Goose, Open Interpreter, Oh My Pi, Letta CLI, Warp AI, Agent Deck.
Os documentos do catalogo tambem mantem um backlog MITM para ferramentas sem base URL, como Windsurf, Amp, Amazon Q/Kiro CLI e Cowork. Esses casos devem permanecer separados de uma integracao direta.
### 3.3 Caso jcode (referencia validada)
- Upstream: `https://github.com/1jehuang/jcode`
- Mecanismo: perfil OpenAI-compatible dirigido por metadados; nao foi criado um plugin de runtime.
- Branch: `feat/omniroute-provider`
- Commit: `ee4f904e6`
- PR no fork: `https://github.com/diegosouzapw/jcode/pull/1`
- Issue no upstream: `https://github.com/1jehuang/jcode/issues/704`
- Diff: 6 arquivos, `+56/-3`.
- Validacao: `cargo check --workspace` limpo; 205 testes passaram e uma falha foi preexistente/ambiental.
- Estado: aguardando mantenedor; o upstream nao aceita PR de forks externos, por isso a issue e o artefato oficial.
- Pendencia prometida: adicionar no README do OmniRoute a secao "Tools & repositories that work with OmniRoute".
Licao: o trabalho deve comecar descobrindo o mecanismo real de providers do upstream. Nem todos os clientes precisam de mudanca no OmniRoute; alguns precisam somente de um perfil local, e outros exigirao um adaptador especifico.
## 4. Candidatos prioritarios com evidencia concreta
As evidencias abaixo sao sinais de README/configuracao observados na pesquisa inicial. A task individual deve abrir o arquivo exato, confirmar a versao atual e executar um smoke test.
| Projeto | Repositorio | Evidencia inicial | Rota provavel |
|---|---|---|---|
| Gemini CLI | `google-gemini/gemini-cli` | `GOOGLE_GEMINI_BASE_URL` | configuracao direta; possivel PR/documentacao |
| Claw Code | `ultraworkers/claw-code` | `OPENAI_BASE_URL`, provider compativel | configuracao direta ou provider |
| Plandex | `plandex-ai/plandex` | providers customizados com `baseUrl` | provider/preset |
| MiMo Code | `XiaomiMiMo/MiMo-Code` | `@ai-sdk/openai-compatible` e `baseURL` | provider customizado |
| Trae Agent | `bytedance/trae-agent` | `model_providers` e `base_url` | provider/config |
| Kimi CLI | `MoonshotAI/kimi-cli` | modos `openai_legacy`, `openai_responses`, `anthropic` e `base_url` | provider nativo/config |
| Every Code | `just-every/code` | fork Codex com providers OpenAI/Claude/Gemini | perfil/provider |
| Open Codex | `ymichael/open-codex` | multi-provider e OpenAI-compatible | fork/provider |
| VT Code | `vinhnx/vtcode` | `custom_providers[].base_url`, failover | provider customizado |
| OpenHands CLI | `OpenHands/OpenHands-CLI` | `LLM_BASE_URL` | configuracao direta |
| gptme | `gptme/gptme` | `OPENAI_BASE_URL` e providers | configuracao direta |
| Nanocoder | `Nano-Collective/nanocoder` | qualquer API OpenAI-compatible | configuracao direta |
| RA.Aid | `ai-christianson/RA.Aid` | `OPENAI_API_BASE` | configuracao direta |
| CoreCoder | `he-yufeng/CoreCoder` | `OPENAI_BASE_URL` | configuracao direta |
| Grok CLI | `superagent-ai/grok-cli` | `GROK_BASE_URL`/`baseURL` | configuracao direta |
| Gitlawb Zero | `Gitlawb/zero` | provider `custom-openai-compatible`, `--base-url` | provider/flag |
| DeepSeek Reasonix | `esengine/DeepSeek-Reasonix` | provider compativel e endpoint | confirmar configuracao |
| KlaatCode | `KlaatAI/klaatcode` | `customModels` OpenAI-compatible | configuracao JSON |
| CodeMini CLI | `havingautism/Codemini-CLI` | `gateway.base_url` | gateway/config |
| Zot | `patriceckhart/zot` | `--base-url` e provider custom em `models.json` | flag/config |
| Pool | `poolsideai/pool` | `POOLSIDE_STANDALONE_BASE_URL`; licenca proprietaria | configuracao, sem PR assumido |
| Octomind | `Muvon/octomind` | `<PROVIDER>_API_URL`/`LOCAL_API_URL` | provider/env |
| Coro Code | `Blushyes/coro-code` | `OPENAI_BASE_URL` | configuracao direta |
| Mini-Kode | `minmaxflow/mini-kode` | `MINIKODE_BASE_URL` | configuracao direta |
| Late CLI | `mlhher/late-cli` | `OPENAI_BASE_URL`/`api-url` | env/flag |
| Agentty | `1ay1/agentty` | modelo agnostico e endpoints compativeis | confirmar arquivo de config |
| Aizen | `aizen-stack/aizen` | CLI Rust OpenAI-compatible; `AIZEN_BASE_URL` | configuracao direta |
| Clif-Code | `DLhugly/Clif-Code` | OpenRouter/OpenAI/Anthropic/Ollama | provider/config |
| Minacode | `hit9/minacode` | provider e compatibilidade no README | confirmar URL |
| YottaCode | `yottadynamics/yottacode` | modelo escolhido, gateway/provider | confirmar config |
| aichat | `sigoden/aichat` | providers OpenAI/Claude/Gemini e compatibilidade | `models.yaml`/provider |
| ShellGPT | `TheR1D/shell_gpt` | `API_BASE_URL` | env/config |
| Mistral Vibe | `mistralai/mistral-vibe` | `base_url`, API base e provider | config/env |
| OpenSquilla | `opensquilla/opensquilla` | 20+ providers e gateway | provider/config |
| Kode CLI | `shareAI-lab/Kode-cli` | provider, endpoint e Anthropic/OpenAI/Gemini | config |
| Crush | `charmbracelet/crush` | `base_url`, provider compativel | ja catalogado no OmniRoute; validar upstream |
| Hermes Agent | `NousResearch/hermes-agent` | endpoint/gateway e 300+ modelos | ja catalogado; validar modo de endpoint |
| OpenClaw | `openclaw/openclaw` | providers, gateway e endpoints | ja catalogado; validar configuracao atual |
## 5. Inventario amplo localizado
### 5.1 Agentes de terminal e coding CLIs
Os projetos desta tabela foram encontrados na lista curada ou no GitHub Search. `Pesquisa` indica o proximo gate; nao significa que a integracao ja esta pronta.
| Projeto | Repositorio | Licenca/sinal publico | Situacao inicial |
|---|---|---|---|
| OpenCode | `anomalyco/opencode` | multi-provider, 75+ providers | ja suportado; acompanhar provider/plugin |
| Codex CLI | `openai/codex` | Apache-2.0, provider configuravel | ja suportado |
| OpenHands principal | `All-Hands-AI/OpenHands` | OSS, CLI e web | pesquisar CLI e `LLM_BASE_URL` |
| Pi | `badlogic/pi-mono` | harness multi-provider | ja suportado; confirmar repo atual |
| Open Interpreter | `OpenInterpreter/open-interpreter` | Apache-2.0, `--api_base` | ja suportado |
| Cline | `cline/cline` | Apache-2.0, base URL/gateway | ja suportado |
| Goose | `aaif-goose/goose` | Apache-2.0, providers | ja suportado |
| Aider | `Aider-AI/aider` | Apache-2.0, Anthropic/OpenAI | ja suportado |
| Continue | `continuedev/continue` | Apache-2.0, multi-model | ja suportado |
| Deep Agents Code | `langchain-ai/deepagents` | MIT, tool-calling LLM | pesquisar pacote `deepagents-code` |
| Crush | `charmbracelet/crush` | provider/base URL | ja suportado |
| Kilo Code | `Kilo-Org/kilocode` | MIT, providers | ja suportado |
| Qwen Code | `QwenLM/qwen-code` | Apache-2.0, providers | ja suportado |
| Roo Code | `RooCodeInc/Roo-Code` | Apache-2.0 | ja catalogado; validar CLI |
| Grok Build | `xai-org/grok-build` | Apache-2.0, provider | ja suportado |
| Oh My Pi | `can1357/oh-my-pi` | provider custom em YAML | ja suportado |
| SWE-agent | `SWE-agent/SWE-agent` | MIT | pesquisar backend e base URL |
| Smol Developer | `smol-ai/developer` | embeddable agent | adapter/SDK, nao necessariamente CLI |
| Claude Engineer | `Doriandarko/claude-engineer` | CLI Claude | pesquisar provider |
| Claurst | `Kuberwastaken/claurst` | GPL-3.0, provider | confirmar endpoint e politica de fork |
| Free Code | `paoloanzn/free-code` | fork de Claude Code | pesquisar licenca e endpoint |
| Codebuff | `CodebuffAI/codebuff` | multi-agent CLI | pesquisar provider |
| ForgeCode | `antinomyhq/forge` | 300+ modelos | ja suportado |
| OpenSquilla | `opensquilla/opensquilla` | Apache-2.0, gateway | candidato forte |
| Kode CLI | `shareAI-lab/Kode-cli` | Apache-2.0, endpoint | candidato forte |
| Devon | `entropy-research/Devon` | pair programmer TUI | pesquisar backend |
| AutoCodeRover | `AutoCodeRoverSG/auto-code-rover` | agente de issues | pesquisar configuracao de modelos |
| Letta Code | `letta-ai/letta-code` | Apache-2.0, model-agnostic | pesquisar API base |
| CodeMachine CLI | `moazbuilds/CodeMachine-CLI` | multi-agent local | pesquisar provider |
| Codel | `semanser/codel` | AGPL-3.0, Docker/web UI | confirmar servidor OpenAI e restricoes AGPL |
| Agentless | `OpenAutoCoder/Agentless` | workflow sem loop persistente | pesquisar entrada de modelo |
| Amazon Q Developer CLI | `aws/amazon-q-developer-cli` | Apache-2.0 | provavelmente auth/ecossistema AWS; pesquisar |
| Neovate Code | `neovateai/neovate-code` | MIT, plugin/multi-provider | candidato forte |
| Groq Code CLI | `build-with-groq/groq-code-cli` | multi-model | pesquisar endpoint |
| Dexto | `truffle-ai/dexto` | CLI/web/API, subagentes | pesquisar provider |
| claw-code-agent | `HarnessLab/claw-code-agent` | Python, sem dependencias | confirmar endpoint |
| g3 | `dhanji/g3` | Rust, provider abstraction | confirmar licenca e URL |
| Coro Code | `Blushyes/coro-code` | base URL/OpenAI | candidato |
| Mini-Kode | `minmaxflow/mini-kode` | MIT, referencia educacional | candidato |
| zot | `patriceckhart/zot` | MIT, TUI/JSON/RPC | candidato |
| agentty | `1ay1/agentty` | MIT, ACP e multi-provider | candidato |
| nori-cli | `tilework-tech/nori-cli` | multi-provider sobre Codex | pesquisar base URL |
| cursor-agent clone | `civai-technologies/cursor-agent` | OpenAI/Claude/Ollama | pesquisar maturidade e licenca |
| DvalinCode | `arthurpanhku/dvalincode` | MIT, OpenAI-compatible | candidato |
| OpenHarness | `zhijiewong/openharness` | Apache-2.0, any LLM | candidato |
| Octomind | `Muvon/octomind` | Apache-2.0, 13+ providers | candidato |
| Codex Infinity | `lee101/codex-infinity` | fork Codex | pesquisar endpoint |
| San | `genai-io/san` | Apache-2.0, provider-neutral | pesquisar endpoint |
| Waveloom | `Menfre01/waveloom` | Apache-2.0, DeepSeek-focused | pesquisar provider |
| picocode | `jondot/picocode` | Rust, multi-LLM | pesquisar provider |
| QQCode | `qnguyen3/qqcode` | Rust, skills | pesquisar provider |
| Keen Code | `mochow13/keen-code` | MIT, 9+ providers | pesquisar provider |
| Smelt | `leonardcser/smelt` | MIT, OpenAI-compatible | ja suportado |
| Grinta | `josephsenior/Grinta-Coding-Agent` | MIT, Python | pesquisar provider |
| Zap | `zap-coding-agent/zap-coding-agent` | MIT, MCP, local/OpenAI | pesquisar endpoint |
| Binharic | `CogitatorTech/binharic-cli` | multi-provider | pesquisar endpoint |
| Darce | `AmerSarhan/darce-cli` | MIT, multi-model | pesquisar endpoint |
| CLAII | `agencyswarm/CLAII` | multi-agent/MCP | pesquisar endpoint |
### 5.2 Agentes generalistas e ecossistema OpenClaw
Estes podem consumir OmniRoute como backend, mas a task deve confirmar se a interface de configuracao e realmente uma CLI de codigo ou apenas um gateway de agente.
| Projeto | Repositorio | Possivel caminho |
|---|---|---|
| OpenClaw | `openclaw/openclaw` | provider/gateway; ja catalogado |
| nanobot | `HKUDS/nanobot` | provider OpenAI-compatible |
| ZeroClaw | `zeroclaw-labs/zeroclaw` | trait de provider |
| NanoClaw | `gavrielc/nanoclaw` | Anthropic SDK; pesquisar base |
| PicoClaw | `sipeed/picoclaw` | provider/config |
| IronClaw | `nearai/ironclaw` | provider Rust |
| NullClaw | `nullclaw/nullclaw` | 23+ providers |
| Clawith | `dataelement/Clawith` | gateway/teams |
| claw0 | `shareAI-lab/claw0` | tutorial/runtime; pesquisa de viabilidade |
| Moltis | `moltis-org/moltis` | provider Rust |
| GitClaw | `open-gitagent/gitclaw` | agente Git-native; pesquisar |
| LionClaw | `moshthepitt/lionclaw` | CLI local; pesquisar |
| Aizen | `aizen-stack/aizen` | OpenAI-compatible |
| aichat | `sigoden/aichat` | provider/model YAML |
| ShellGPT | `TheR1D/shell_gpt` | `API_BASE_URL` |
| gptme | `gptme/gptme` | `OPENAI_BASE_URL` |
### 5.3 Orquestradores, wrappers e ferramentas adjacentes
Nao sao todos alvos de um provider OmniRoute. Devem ser avaliados para launcher, ACP, MCP, observabilidade ou configuracao de seus agentes filhos.
| Projeto | Repositorio | Tipo de integracao a investigar |
|---|---|---|
| Agent Deck | `asheshgoplani/agent-deck` | config dos CLIs filhos; ja catalogado |
| VibePod | `VibePod/vibepod-cli` | wrapper Docker e metricas |
| zeroshot | `the-open-engine/zeroshot` | launcher/worktrees |
| Fractal | `plasma-ai/fractal` | orquestrador de CLIs |
| Bernstein | `chernistry/bernstein` | orquestrador/verificador |
| Traycer | `traycerai/traycer` | CLI custom e agentes filhos |
| h5i | `h5i-dev/h5i` | execucao paralela |
| OMK | `dmae97/open-multi-agent-kit` | control plane/provider-neutral |
| kodo | `ikamensh/kodo` | orquestrador |
| ORCH | `oxgeneral/ORCH` | fila de tarefas |
| LoopTroop | `LoopTroop-ai/LoopTroop` | orchestration sobre OpenCode |
| Galley | `shinpr/galley` | worktree/PR handoff |
| Relay | `jcast90/relay` | MCP/orquestracao |
| sage | `youwangd/SageCLI` | runtime-agnostic |
| 5dive | `5dive-ai/5dive` | agentes em servidor |
| agx | `ramarlina/agx` | checkpoints e agentes |
| claude-code-router | `musistudio/claude-code-router` | proxy/roteamento; possivel upstream consumidor |
| cc-router | `finch-xu/cc-router` | proxy Anthropic multi-provider |
| OneCLI | `onecli/onecli` | broker de credenciais, nao agente |
| agent-browser | `vercel-labs/agent-browser` | ferramenta MCP/plugin |
| OpenWork | `different-ai/openwork` | desktop sobre OpenCode |
| Mistral Vibe | `mistralai/mistral-vibe` | provider/base URL |
| Junie CLI | `junie.jetbrains.com` | fechado; configuracao BYOK a confirmar |
| Pool | `poolsideai/pool` | binario/EULA; sem PR presumido |
## 6. Evidencias tecnicas e mapeamento para OmniRoute
### 6.1 Padroes de endpoint encontrados
| Padrao observado | Exemplos | Acao OmniRoute |
|---|---|---|
| `OPENAI_BASE_URL`/`OPENAI_API_BASE` | Claw Code, RA.Aid, CoreCoder, Coro Code | fornecer root ou `/v1` conforme o cliente; testar append de path |
| `base_url`/`baseURL` em provider | Plandex, MiMo Code, Trae Agent, VT Code, KlaatCode | gerar bloco de provider e modelo |
| `LLM_BASE_URL` | OpenHands CLI | configurar surface OpenAI e validar streaming/tool calling |
| `GOOGLE_GEMINI_BASE_URL` | Gemini CLI | usar superficie `/v1beta`/Gemini; confirmar formato esperado |
| `GROK_BASE_URL` | Grok CLI | decidir se o cliente fala xAI ou OpenAI; testar traducoes |
| `--base-url` | Gitlawb Zero, Zot, jcode | launcher ou perfil persistido |
| `API_BASE_URL` | ShellGPT | config/env direta |
| `<PROVIDER>_API_URL`/gateway | Octomind, Pool, OpenSquilla | provider selecionavel; testar cada preset |
| ACP/MCP sem URL direta | Agentty, Kimi CLI, Goose, OpenCode | avaliar se OmniRoute deve ser provider ou backend ACP |
| endpoint nao customizavel | Cursor desktop, Antigravity, Kiro, Windsurf, Amp | somente MITM/guide; nao prometer integracao direta |
### 6.2 Superficies e riscos de protocolo
- **`/v1` duplicado:** alguns clientes recebem a raiz e acrescentam `/v1/chat/completions`; outros exigem a URL final com `/v1`. Cada task deve registrar o resultado real.
- **Chat Completions vs Responses:** forks do Codex e clientes modernos podem usar Responses; testar ambas quando o cliente permitir.
- **Anthropic:** clientes que mandam `/v1/messages` esperam `ANTHROPIC_BASE_URL` sem `/v1` no valor. A traducao Anthropic do OmniRoute deve ser validada com streaming e tool use.
- **Gemini:** Gemini CLI pode esperar uma base Gemini nativa, nao somente OpenAI-compatible; validar `generateContent`, streaming e headers.
- **Tool calling:** o agente pode exigir nomes/ids de ferramenta estaveis, JSON estrito, `tool_choice` ou blocos de pensamento especificos.
- **Descoberta de modelos:** `/v1/models` pode ser obrigatorio, opcional ou inexistente. O setup precisa aceitar `--model` fixo quando a descoberta nao for suportada.
- **Autenticacao:** alguns projetos leem somente env, outros gravam tokens em arquivo/keyring e alguns usam OAuth proprietario. Nunca reutilizar credenciais de um upstream sem verificar escopo.
- **Streaming e retry:** SSE, timeouts, abort signals e re-tentativas podem divergir do cliente. Validar uma chamada longa e uma falha de provider.
- **Licenca:** GPL/AGPL, EULA e repositorios sem SPDX exigem decisao de distribuicao antes de enviar patch.
## 7. Riscos de pesquisa e integracao
1. **Homonomimos e clones:** usar sempre URL canonica, organizacao, release e README do repositorio correto.
2. **Repositorios que mudam rapidamente:** congelar commit/versao no relatorio da task e repetir a consulta no dia da implementacao.
3. **README divergente do codigo:** procurar schema, parser de config, testes e comando de execucao; README sozinho e evidencia Tier 1.
4. **Clientes fechados:** registrar como `needs-mitm` ou `config-only`, nunca como PR upstream.
5. **Forks com historia de origem controversa:** avaliar politica, licenca e aceite de contribuicoes antes de reproduzir componentes.
6. **Segredos no ambiente:** limpar `OMNIROUTE_API_KEY` e chaves de teste quando a suite assume ambiente sem credencial, como ocorreu no jcode.
7. **Mudancas no checkout:** usar worktree em `.claude/worktrees/` por projeto; nao editar o checkout compartilhado do OmniRoute nem usar `git stash`.
## 8. Recomendacao
Executar primeiro os lotes P0/P1 do documento de prioridade. Cada lote pode ter ate tres subagentes, um repositorio por worktree. O agente principal deve revisar a pesquisa, o smoke test e a licenca antes de permitir implementacao. O resultado de cada caso deve atualizar o tracker com commit, PR/issue, validacao e status upstream, sem preencher campos externos por suposicao.
## 9. Referencias
- OmniRoute CLI catalogo: `src/shared/constants/cliTools.ts`
- OmniRoute CLI reference: `docs/reference/CLI-TOOLS.md`
- OmniRoute setup guide: `docs/guides/CLI-INTEGRATIONS.md`
- Handoff jcode: `_tasks/hands-off/2026-08-01_release-v3.8.50_v3.8.50_sess-e1846bc2/handoff.md`
- Inventario curado: `https://github.com/bradAGI/awesome-cli-coding-agents`
- GitHub Search API: `https://api.github.com/search/repositories`

View File

@@ -0,0 +1,167 @@
# Prioridade de integracoes de CLIs com OmniRoute
> **Status final (2026-08-03):** esta é a priorização inicial que orientou a execução. Todos os `104/104` casos já foram pesquisados. A classificação final está no tracker `04`; a estratégia revisada de contribuição está no relatório `06`.
**Snapshot:** 2026-08-01
**Objetivo:** ordenar do melhor para o pior todos os projetos tecnicamente candidatos a consumir OmniRoute, sem remover projetos pequenos. A ordem e uma fila de pesquisa/execucao; ela nao e promessa de que todo upstream aceitara um PR.
## Como ler a prioridade
- **P0:** ja esta no catalogo OmniRoute ou tem evidencia muito forte de endpoint customizavel; executar/consolidar primeiro.
- **P1:** forte candidato novo, com provider/base URL evidente e bom retorno para o ecossistema.
- **P2:** tecnicamente promissor, mas requer confirmacao de protocolo, config, maturidade ou licenca.
- **P3:** possivel via ACP/MCP/wrapper/launcher, ou com menor adocao; pesquisar depois dos P0-P2.
- **P4:** cliente fechado, EULA, MITM ou pesquisa exploratoria; manter no inventario, mas nao bloquear os demais.
Os fatores usados foram: evidencia de endpoint arbitrario, adocao/atividade, facilidade de teste, compatibilidade OpenAI/Anthropic/Gemini, maturidade, licenca, chance de PR upstream, valor para usuarios OmniRoute e risco de protocolo.
## A. Catalogo OmniRoute ja existente
Estas entradas ja aparecem no registro local. A prioridade aqui significa consolidar documentacao, smoke tests, detector/configurador e eventual upstream nominal; nao significa recriar uma integracao que ja existe.
| Ordem | Projeto | Repositorio/documentacao | Estado local | Proximo foco |
|---:|---|---|---|---|
| A1 | Claude Code | `anthropics/claude-code` | catalogado; Anthropic base URL | manter compatibilidade Anthropic, streaming e tools |
| A2 | Codex CLI | `openai/codex` | catalogado; OpenAI-compatible | Responses, profiles e `/v1` |
| A3 | OpenCode | `anomalyco/opencode` | catalogado; provider | provider nativo/plugin e model discovery |
| A4 | Cline | `cline/cline` | catalogado; base URL | validar CLI/extension e append de `/v1` |
| A5 | Goose | `aaif-goose/goose` | catalogado; `OPENAI_HOST` | validar schema atual e ACP |
| A6 | Aider | `Aider-AI/aider` | catalogado; `OPENAI_API_BASE` | LiteLLM path, tools e custo |
| A7 | Continue | `continuedev/continue` | catalogado; provider OpenAI | CLI e config YAML atual |
| A8 | Kilo Code | `Kilo-Org/kilocode` | catalogado; custom URL | CLI, extension e auth |
| A9 | Roo Code | `RooCodeInc/Roo-Code` | catalogado; custom URL | CLI/headless e provider |
| A10 | Qwen Code | `QwenLM/qwen-code` | catalogado; `modelProviders` | V4 schema, Responses e env |
| A11 | Open Interpreter | `OpenInterpreter/open-interpreter` | catalogado; `--api_base` | streaming e tool execution |
| A12 | OpenClaw | `openclaw/openclaw` | catalogado; gateway/provider | config atual e segurança |
| A13 | Hermes Agent | `NousResearch/hermes-agent` | catalogado; provider/gateway | endpoint custom e modelos |
| A14 | Hermes | `NousResearch/hermes-agent` | catalogado/dual entry | distinguir CLI e agente |
| A15 | Oh My Pi | `can1357/oh-my-pi` | catalogado; YAML provider | auto-discovery e tool calling |
| A16 | Pi | `badlogic/pi-mono` | catalogado; provider | confirmar repositorio/CLI atual |
| A17 | Crush | `charmbracelet/crush` | catalogado; `base_url` | config TOML/JSON atual |
| A18 | Smelt | `leonardcser/smelt` | catalogado; OpenAI-compatible | headless e subagents |
| A19 | ForgeCode | `antinomyhq/forge` | catalogado; multi-provider | base URL e custom agents |
| A20 | jcode | `1jehuang/jcode` | integrado e proposto upstream | aguardar issue #704; manter README OmniRoute |
| A21 | DeepSeek TUI | `hunterbown/deepseek-tui` | catalogado legado | confirmar sucessor CodeWhale |
| A22 | CodeWhale | `Hmbown/CodeWhale` | catalogado | config primaria e legado |
| A23 | Grok Build | `xai-org/grok-build` | catalogado; `~/.grok/config.toml` | provider OmniRoute e modelos |
| A24 | Cursor Agent CLI | `cursor.com/cli` | catalogado parcial | confirmar limites de endpoint |
| A25 | Factory Droid | `Factory-AI/factory` | catalogado parcial | BYOK e endpoint suportado |
| A26 | GitHub Copilot CLI | `github/copilot-cli` | catalogado | provider base URL atual |
| A27 | Letta CLI | `letta-ai/letta-code` | catalogado | config pi-ai/local mode |
| A28 | Warp AI | `warpdotdev/Warp` | catalogado parcial | somente BYOK/desktop |
| A29 | Agent Deck | `asheshgoplani/agent-deck` | catalogado | agentes filhos e ACP |
| A30 | Antigravity | produto Google | MITM backlog | nao tratar como endpoint direto |
| A31 | Kiro AI | produto AWS | MITM backlog | auth/SSO e MITM |
| A32 | Cursor desktop | produto Anysphere | cloud/MITM | manter separado do Cursor CLI |
## B. Novos candidatos em ordem de execucao
| Ordem | Prioridade | Projeto | Repositorio | Evidencia inicial | Rota esperada |
|---:|:---:|---|---|---|---|
| 1 | P0 | Gemini CLI | `google-gemini/gemini-cli` | `GOOGLE_GEMINI_BASE_URL` | config direta/Gemini |
| 2 | P0 | Claw Code | `ultraworkers/claw-code` | `OPENAI_BASE_URL`, provider | OpenAI-compatible |
| 3 | P0 | Plandex | `plandex-ai/plandex` | provider com `baseUrl` | preset/provider |
| 4 | P0 | MiMo Code | `XiaomiMiMo/MiMo-Code` | `@ai-sdk/openai-compatible`, `baseURL` | provider |
| 5 | P0 | Trae Agent | `bytedance/trae-agent` | `model_providers`, `base_url` | provider/config |
| 6 | P0 | Kimi CLI | `MoonshotAI/kimi-cli` | OpenAI legacy/Responses/Anthropic, `base_url` | provider nativo |
| 7 | P0 | Every Code | `just-every/code` | fork Codex, OpenAI/Claude/Gemini | profile/provider |
| 8 | P0 | Open Codex | `ymichael/open-codex` | OpenAI/Gemini/OpenRouter/Ollama | profile/provider |
| 9 | P0 | VT Code | `vinhnx/vtcode` | `custom_providers[].base_url` | provider/failover |
| 10 | P0 | OpenHands CLI | `OpenHands/OpenHands-CLI` | `LLM_BASE_URL` | config direta |
| 11 | P0 | gptme | `gptme/gptme` | `OPENAI_BASE_URL` | config direta |
| 12 | P0 | Nanocoder | `Nano-Collective/nanocoder` | qualquer OpenAI-compatible | config direta |
| 13 | P0 | RA.Aid | `ai-christianson/RA.Aid` | `OPENAI_API_BASE` | config direta |
| 14 | P0 | CoreCoder | `he-yufeng/CoreCoder` | `OPENAI_BASE_URL` | config direta |
| 15 | P1 | Grok CLI | `superagent-ai/grok-cli` | `GROK_BASE_URL`/`baseURL` | config direta |
| 16 | P1 | Gitlawb Zero | `Gitlawb/zero` | `custom-openai-compatible`, `--base-url` | provider/flag |
| 17 | P1 | DeepSeek Reasonix | `esengine/DeepSeek-Reasonix` | endpoint/provider compativel | provider |
| 18 | P1 | KlaatCode | `KlaatAI/klaatcode` | `customModels` OpenAI-compatible | config |
| 19 | P1 | CodeMini CLI | `havingautism/Codemini-CLI` | `gateway.base_url` | gateway |
| 20 | P1 | Zot | `patriceckhart/zot` | `--base-url`, `models.json` | flag/config |
| 21 | P1 | Octomind | `Muvon/octomind` | provider URL envs | provider/env |
| 22 | P1 | DvalinCode | `arthurpanhku/dvalincode` | qualquer OpenAI-compatible | config direta |
| 23 | P1 | Coro Code | `Blushyes/coro-code` | `OPENAI_BASE_URL` | env |
| 24 | P1 | Mini-Kode | `minmaxflow/mini-kode` | `MINIKODE_BASE_URL` | env |
| 25 | P1 | Late CLI | `mlhher/late-cli` | `OPENAI_BASE_URL`, `api-url` | env/flag |
| 26 | P1 | Agentty | `1ay1/agentty` | provider-agnostic, ACP | config/ACP |
| 27 | P1 | Aizen | `aizen-stack/aizen` | Rust OpenAI-compatible, `AIZEN_BASE_URL` | config |
| 28 | P1 | Clif-Code | `DLhugly/Clif-Code` | OpenAI/Anthropic/Ollama | provider |
| 29 | P1 | Minacode | `hit9/minacode` | provider/compatibilidade | confirmar URL |
| 30 | P1 | YottaCode | `yottadynamics/yottacode` | modelo escolhido/gateway | provider |
| 31 | P1 | aichat | `sigoden/aichat` | OpenAI/Claude/Gemini | models YAML |
| 32 | P1 | ShellGPT | `TheR1D/shell_gpt` | `API_BASE_URL` | env |
| 33 | P1 | Mistral Vibe | `mistralai/mistral-vibe` | `base_url`, API base | config |
| 34 | P1 | OpenSquilla | `opensquilla/opensquilla` | gateway, 20+ providers | provider |
| 35 | P1 | Kode CLI | `shareAI-lab/Kode-cli` | endpoint/Anthropic/OpenAI/Gemini | config |
| 36 | P1 | Neovate Code | `neovateai/neovate-code` | plugin/multi-provider | plugin/provider |
| 37 | P1 | Deep Agents Code | `langchain-ai/deepagents` | qualquer tool-calling LLM | provider SDK |
| 38 | P1 | Kode fork/variants | `shareAI-lab/Kode-cli` | multi-provider | confirmar upstream |
| 39 | P1 | OpenHands principal | `All-Hands-AI/OpenHands` | CLI/web; pesquisar LLM base | config/CLI |
| 40 | P1 | SWE-agent | `SWE-agent/SWE-agent` | agente de issues | backend/provider |
| 41 | P1 | AutoCodeRover | `AutoCodeRoverSG/auto-code-rover` | agente de patches | backend/provider |
| 42 | P2 | Claurst | `Kuberwastaken/claurst` | provider/Anthropic | config; licenca GPL |
| 43 | P2 | Codebuff | `CodebuffAI/codebuff` | multi-agent CLI | provider |
| 44 | P2 | Devon | `entropy-research/Devon` | TUI pair programmer | backend |
| 45 | P2 | Letta Code | `letta-ai/letta-code` | model-agnostic | provider |
| 46 | P2 | CodeMachine CLI | `moazbuilds/CodeMachine-CLI` | multi-agent local | provider |
| 47 | P2 | Groq Code CLI | `build-with-groq/groq-code-cli` | multi-model | endpoint |
| 48 | P2 | Dexto | `truffle-ai/dexto` | CLI/web/API | provider |
| 49 | P2 | claw-code-agent | `HarnessLab/claw-code-agent` | endpoint/gateway | provider |
| 50 | P2 | g3 | `dhanji/g3` | Rust provider abstraction | provider |
| 51 | P2 | San | `genai-io/san` | provider-neutral | provider |
| 52 | P2 | Waveloom | `Menfre01/waveloom` | DeepSeek/provider | endpoint |
| 53 | P2 | picocode | `jondot/picocode` | multi-LLM | config |
| 54 | P2 | QQCode | `qnguyen3/qqcode` | skills, Rust | config |
| 55 | P2 | Keen Code | `mochow13/keen-code` | 9+ providers | config |
| 56 | P2 | Grinta | `josephsenior/Grinta-Coding-Agent` | provider-agnostic | config |
| 57 | P2 | Zap | `zap-coding-agent/zap-coding-agent` | Claude/Gemini/OpenAI/LM Studio | provider |
| 58 | P2 | Binharic | `CogitatorTech/binharic-cli` | multi-provider | config |
| 59 | P2 | Darce | `AmerSarhan/darce-cli` | multi-model/streaming | config |
| 60 | P2 | CLAII | `agencyswarm/CLAII` | multi-agent/MCP | provider |
| 61 | P2 | nori-cli | `tilework-tech/nori-cli` | multi-provider sobre Codex | config |
| 62 | P2 | cursor-agent clone | `civai-technologies/cursor-agent` | Claude/OpenAI/Ollama | provider |
| 63 | P2 | Free Code | `paoloanzn/free-code` | fork Claude Code | licenca/config |
| 64 | P2 | Claude Engineer | `Doriandarko/claude-engineer` | CLI Claude | provider |
| 65 | P2 | Smol Developer | `smol-ai/developer` | agent embutivel | SDK/adaptador |
| 66 | P2 | Agentless | `OpenAutoCoder/Agentless` | workflow sem loop | entrada de modelo |
| 67 | P2 | Amazon Q Developer CLI | `aws/amazon-q-developer-cli` | CLI AWS | auth/provider |
| 68 | P2 | nanobot | `HKUDS/nanobot` | OpenClaw rewrite | provider |
| 69 | P2 | ZeroClaw | `zeroclaw-labs/zeroclaw` | providers pluggable | provider |
| 70 | P2 | NanoClaw | `gavrielc/nanoclaw` | Anthropic SDK | base URL |
| 71 | P2 | PicoClaw | `sipeed/picoclaw` | provider/config | provider |
| 72 | P2 | IronClaw | `nearai/ironclaw` | provider Rust | provider |
| 73 | P2 | NullClaw | `nullclaw/nullclaw` | 23+ providers | provider |
| 74 | P2 | Moltis | `moltis-org/moltis` | Rust agent | provider |
| 75 | P2 | GitClaw | `open-gitagent/gitclaw` | Git-native agent | provider |
| 76 | P2 | LionClaw | `moshthepitt/lionclaw` | CLI local | provider |
| 77 | P3 | VibePod | `VibePod/vibepod-cli` | wrapper Docker | launcher |
| 78 | P3 | zeroshot | `the-open-engine/zeroshot` | worktrees/orchestration | launcher |
| 79 | P3 | Fractal | `plasma-ai/fractal` | orquestra CLIs | launcher |
| 80 | P3 | Bernstein | `chernistry/bernstein` | executa/verifica agentes | launcher |
| 81 | P3 | Traycer | `traycerai/traycer` | agentes paralelos | launcher |
| 82 | P3 | h5i | `h5i-dev/h5i` | sandbox e peer review | launcher |
| 83 | P3 | OMK | `dmae97/open-multi-agent-kit` | control plane | ACP/MCP |
| 84 | P3 | kodo | `ikamensh/kodo` | orquestrador | launcher |
| 85 | P3 | ORCH | `oxgeneral/ORCH` | fila de tarefas | launcher |
| 86 | P3 | LoopTroop | `LoopTroop-ai/LoopTroop` | orquestrador OpenCode | launcher |
| 87 | P3 | Galley | `shinpr/galley` | worktree/PR | launcher |
| 88 | P3 | Relay | `jcast90/relay` | MCP/orquestracao | MCP |
| 89 | P3 | SageCLI | `youwangd/SageCLI` | runtime-agnostic | launcher/ACP |
| 90 | P3 | 5dive | `5dive-ai/5dive` | agentes em servidor | launcher |
| 91 | P3 | agx | `ramarlina/agx` | checkpoints | launcher |
| 92 | P3 | claude-code-router | `musistudio/claude-code-router` | proxy multi-provider | integrar como consumidor/proxy |
| 93 | P3 | cc-router | `finch-xu/cc-router` | proxy Anthropic | interoperabilidade |
| 94 | P3 | OneCLI | `onecli/onecli` | broker de credenciais | seguranca/integ. adjacente |
| 95 | P3 | agent-browser | `vercel-labs/agent-browser` | ferramenta para agentes | MCP/plugin |
| 96 | P3 | OpenWork | `different-ai/openwork` | desktop sobre OpenCode | config do agente filho |
| 97 | P4 | Pool | `poolsideai/pool` | `POOLSIDE_STANDALONE_BASE_URL`; EULA | config sem PR presumido |
| 98 | P4 | Junie CLI | `junie.jetbrains.com` | fechado/EAP | BYOK/endpoint a confirmar |
| 99 | P4 | Cursor desktop | `Anysphere` | cloud endpoint | MITM/guide |
| 100 | P4 | Windsurf | produto Codeium | sem base URL geral | MITM |
| 101 | P4 | Amp | `sourcegraph.com/amp` | fechado | MITM/sem PR |
| 102 | P4 | Amazon Q/Kiro CLI | AWS | SSO/ecossistema AWS | MITM/adapter |
| 103 | P4 | Cowork | produto Anthropic | endpoint opaco | MITM |
## C. Regra de promocao/rebaixamento
Um projeto sobe de prioridade quando a pesquisa individual confirma: configuracao documentada, teste local com OmniRoute, licenca permissiva e contribuicao aceita. Desce quando: a URL e fixa, o endpoint e somente SaaS, o README nao corresponde ao codigo, a autenticacao e inseparavel do provedor, ou a licenca/EULA impede redistribuicao. Nenhum projeto e marcado como impossivel sem registrar a evidencia no tracker.

View File

@@ -0,0 +1,314 @@
# Plano executavel de integracao de CLIs
> **Status final (2026-08-03):** a fase de pesquisa foi concluída em lotes de até três worktrees/agentes, cobrindo `104/104` casos. Este documento continua válido como processo operacional para implementação/publicação. Consulte `06-relatorio-final-104-clis-e-estrategia-prs.md` para o resultado final.
**Data:** 2026-08-01
**Objetivo:** pesquisar, integrar, validar e publicar suporte ao OmniRoute em todos os projetos tecnicamente possiveis, mantendo uma fila que permite ate tres subagentes simultaneos.
O ciclo especifico de preparacao, revisao, envio e acompanhamento das contribuicoes upstream esta
em `05-plano-publicacao-prs-upstream.md`.
## 1. Principios operacionais
- Um repositorio por subagente e por worktree.
- No maximo tres tasks de repositorios em execucao ao mesmo tempo.
- Cada task pesquisa o upstream novamente antes de editar; o relatorio inicial e somente contexto.
- O agente principal revisa licenca, arquitetura, smoke test e diff antes do proximo lote.
- Nao usar checkout compartilhado para desenvolvimento e nao usar `git stash`/`git pop`.
- Usar worktrees em `.claude/worktrees/` e branches especificas.
- Nao inventar PR, issue, commit ou aceite de mantenedor.
- Nao adicionar trailers ou rodapes de IA em commits/PRs.
## 2. Fases obrigatorias por projeto
### Fase 0 - Preparacao da task
Criar uma task com nome do projeto, URL canonica, prioridade, evidencia inicial, estado no catalogo OmniRoute e objetivo de integrar. Definir a worktree e o agente responsavel.
### Fase 1 - Pesquisa individual fresca
O agente deve verificar no upstream atual:
- arquitetura de providers e ponto de entrada do CLI;
- arquivo/schema de configuracao e suporte a `base_url`, `baseURL`, `OPENAI_BASE_URL`, `OPENAI_API_BASE`, `LLM_BASE_URL` ou equivalente;
- protocolo real (Chat Completions, Responses, Anthropic Messages, Gemini, ACP, MCP ou outro);
- descoberta de modelos e necessidade de `/v1/models`;
- autenticacao, keyring, OAuth e variaveis de ambiente;
- streaming, tool calling, reasoning e limites conhecidos;
- politica de contribuicao, licenca e se PR de fork externo e aceito;
- atividade, releases, issues/PRs sobre providers customizados ou endpoints locais;
- comandos de build, lint, teste e smoke test;
- possibilidade de fork/PR, issue de proposta, documentacao ou apenas wrapper/MITM.
Registrar commit/release pesquisado e links de evidencia.
### Fase 2 - Gate de viabilidade
Classificar exatamente um caminho inicial:
`viable-direct` (somente configuracao), `viable-upstream` (mudanca no upstream), `viable-acp`, `viable-mcp`, `needs-wrapper`, `needs-mitm`, `config-only`, `blocked` ou `research-more`.
Nao implementar antes de haver uma conclusao de viabilidade e uma razao verificavel.
### Fase 3 - Baseline e TDD
- Executar a suite recomendada pelo upstream antes das mudancas.
- Registrar falhas preexistentes, dependencias ausentes e comandos exatos.
- Limpar `OMNIROUTE_API_KEY` e demais credenciais quando os testes pressupuserem ambiente sem chaves.
- Adicionar primeiro um teste de configuracao, endpoint e selecao de modelo que falhe sem a integracao.
### Fase 4 - Implementacao minima
Implementar apenas o necessario para o caso pesquisado:
- perfil/preset `omniroute` ou provider custom;
- base URL correta (raiz, `/v1` ou `/v1beta` conforme o cliente);
- chave via ambiente ou mecanismo seguro do cliente;
- modelo fixo ou descoberta de modelos;
- selecao/login/report se o CLI tiver esses fluxos;
- documentacao de uso e limites;
- testes de config e chamada.
Se o upstream nao aceitar mudanca, preparar wrapper/launcher ou documentacao local e registrar a limitacao.
### Fase 5 - Validacao funcional
Executar, conforme o protocolo:
- build, lint, typecheck e testes do upstream;
- smoke request com OmniRoute;
- streaming SSE e encerramento por abort;
- tool calling e JSON de argumentos;
- `/v1/models` ou equivalente;
- Chat Completions, Responses, Anthropic Messages e Gemini `generateContent` quando aplicavel;
- fallback/erro, timeout, retry e modelo inexistente;
- teste com chave limpa e teste com `OMNIROUTE_API_KEY` real fora dos logs.
### Fase 6 - Publicacao upstream
- Criar fork somente quando permitido e branch especifica.
- Abrir PR upstream se contribuicoes externas forem aceitas.
- Se PR externo for bloqueado, abrir issue com proposta, patch/referencia e smoke test.
- Se o projeto for fechado/EULA, registrar config manual ou issue de produto; nao criar PR ficticio.
- Atualizar o tracker com URL, commit, estado e resposta do mantenedor.
### Fase 7 - Catalogo e integracao OmniRoute
Quando houver valor para usuarios OmniRoute:
- criar worktree propria do OmniRoute;
- atualizar `src/shared/constants/cliTools.ts` ou `src/shared/constants/cliToolsGrokBuild.ts`;
- atualizar detector em `src/lib/cli-helper/tool-detector.ts` se necessario;
- adicionar gerador/configurador e rota de settings somente se o caso exigir;
- adicionar testes do catalogo, detector, settings, `baseUrlSupport` e `/v1`;
- atualizar `docs/reference/CLI-TOOLS.md`, `docs/guides/CLI-INTEGRATIONS.md` e README quando apropriado;
- atualizar o tracker com a integracao local e evidencias.
### Fase 8 - Fechamento
Registrar commit, branch, PR/issue, testes, limitacoes, status do upstream, status do catalogo OmniRoute e proximo passo. O agente principal faz uma revisao final de seguranca, licenca e factualidade.
## 3. Lotes de ate tres subagentes
O lote e uma unidade operacional. A fila abaixo e ordenada pelo documento `02-prioridade-integracoes-clis.md`; cada linha representa uma task individual.
### Lote 0 - consolidacao do caso de referencia
- `CLI-000` - jcode - manter a issue #704, validar resposta do mantenedor e concluir a secao do README OmniRoute.
### Lote P0.1
- `CLI-001` - Gemini CLI - integrar provider/base URL Gemini.
- `CLI-002` - Claw Code - integrar `OPENAI_BASE_URL`/provider OmniRoute.
- `CLI-003` - Plandex - integrar provider custom com `baseUrl`.
### Lote P0.2
- `CLI-004` - MiMo Code - integrar provider OpenAI-compatible.
- `CLI-005` - Trae Agent - integrar `model_providers` e `base_url`.
- `CLI-006` - Kimi CLI - integrar modos OpenAI/Responses/Anthropic.
### Lote P0.3
- `CLI-007` - Every Code - integrar perfil derivado do Codex.
- `CLI-008` - Open Codex - integrar provider multi-modelo.
- `CLI-009` - VT Code - integrar `custom_providers` e failover.
### Lote P0.4
- `CLI-010` - OpenHands CLI - integrar `LLM_BASE_URL`.
- `CLI-011` - gptme - integrar `OPENAI_BASE_URL`.
- `CLI-012` - Nanocoder - integrar API OpenAI-compatible.
### Lote P0.5
- `CLI-013` - RA.Aid - integrar `OPENAI_API_BASE`.
- `CLI-014` - CoreCoder - integrar `OPENAI_BASE_URL`.
- `CLI-015` - Grok CLI - integrar `GROK_BASE_URL`.
### Lote P1.1
- `CLI-016` - Gitlawb Zero - integrar provider custom e `--base-url`.
- `CLI-017` - DeepSeek Reasonix - confirmar e integrar endpoint.
- `CLI-018` - KlaatCode - integrar `customModels`.
### Lote P1.2
- `CLI-019` - CodeMini CLI - integrar `gateway.base_url`.
- `CLI-020` - Zot - integrar flag/config `--base-url`.
- `CLI-021` - Octomind - integrar provider URL envs.
### Lote P1.3
- `CLI-022` - DvalinCode - integrar OpenAI-compatible.
- `CLI-023` - Coro Code - integrar `OPENAI_BASE_URL`.
- `CLI-024` - Mini-Kode - integrar `MINIKODE_BASE_URL`.
### Lote P1.4
- `CLI-025` - Late CLI - integrar `OPENAI_BASE_URL`/`api-url`.
- `CLI-026` - Agentty - integrar provider e/ou ACP.
- `CLI-027` - Aizen - integrar `AIZEN_BASE_URL`.
### Lote P1.5
- `CLI-028` - Clif-Code - integrar providers OpenAI/Anthropic/Ollama.
- `CLI-029` - Minacode - confirmar provider e integrar URL.
- `CLI-030` - YottaCode - integrar gateway/provider.
### Lote P1.6
- `CLI-031` - aichat - integrar models YAML/provider.
- `CLI-032` - ShellGPT - integrar `API_BASE_URL`.
- `CLI-033` - Mistral Vibe - integrar base URL/provider.
### Lote P1.7
- `CLI-034` - OpenSquilla - integrar gateway/provider.
- `CLI-035` - Kode CLI - integrar endpoint multi-provider.
- `CLI-036` - Neovate Code - integrar plugin/provider.
### Lote P1.8
- `CLI-037` - Deep Agents Code - integrar provider do pacote CLI.
- `CLI-038` - OpenHands principal - integrar CLI/config.
- `CLI-039` - SWE-agent - integrar backend/provider.
### Lote P1.9
- `CLI-040` - AutoCodeRover - integrar backend/provider.
- `CLI-041` - Claurst - integrar provider, respeitando GPL.
- `CLI-042` - Codebuff - integrar provider.
### Lote P2.1
- `CLI-043` - Devon - integrar backend.
- `CLI-044` - Letta Code - integrar provider.
- `CLI-045` - CodeMachine CLI - integrar provider.
### Lote P2.2
- `CLI-046` - Groq Code CLI - integrar endpoint.
- `CLI-047` - Dexto - integrar provider.
- `CLI-048` - claw-code-agent - integrar endpoint.
### Lote P2.3
- `CLI-049` - g3 - integrar provider Rust.
- `CLI-050` - San - integrar provider-neutral.
- `CLI-051` - Waveloom - integrar provider/endpoint.
### Lote P2.4
- `CLI-052` - picocode - integrar multi-LLM.
- `CLI-053` - QQCode - integrar config.
- `CLI-054` - Keen Code - integrar provider.
### Lote P2.5
- `CLI-055` - Grinta - integrar provider.
- `CLI-056` - Zap - integrar Claude/Gemini/OpenAI.
- `CLI-057` - Binharic - integrar multi-provider.
### Lote P2.6
- `CLI-058` - Darce - integrar multi-modelo.
- `CLI-059` - CLAII - integrar provider/MCP.
- `CLI-060` - nori-cli - integrar provider baseado em Codex.
### Lote P2.7
- `CLI-061` - cursor-agent clone - integrar provider.
- `CLI-062` - Free Code - pesquisar licenca e integrar se viavel.
- `CLI-063` - Claude Engineer - integrar provider.
### Lote P2.8
- `CLI-064` - Smol Developer - integrar SDK/adaptador.
- `CLI-065` - Agentless - integrar entrada de modelo.
- `CLI-066` - Amazon Q Developer CLI - pesquisar auth/provider.
### Lote P2.9
- `CLI-067` - nanobot - integrar provider OpenClaw-compatible.
- `CLI-068` - ZeroClaw - integrar trait de provider.
- `CLI-069` - NanoClaw - confirmar base Anthropic.
### Lote P2.10
- `CLI-070` - PicoClaw - integrar provider/config.
- `CLI-071` - IronClaw - integrar provider Rust.
- `CLI-072` - NullClaw - integrar provider.
### Lote P2.11
- `CLI-073` - Moltis - integrar provider Rust.
- `CLI-074` - GitClaw - integrar provider Git-native.
- `CLI-075` - LionClaw - integrar provider CLI.
### Lote P3.1 - wrappers e orquestradores
- `CLI-076` - VibePod; `CLI-077` - zeroshot; `CLI-078` - Fractal.
### Lote P3.2
- `CLI-079` - Bernstein; `CLI-080` - Traycer; `CLI-081` - h5i.
### Lote P3.3
- `CLI-082` - OMK; `CLI-083` - kodo; `CLI-084` - ORCH.
### Lote P3.4
- `CLI-085` - LoopTroop; `CLI-086` - Galley; `CLI-087` - Relay.
### Lote P3.5
- `CLI-088` - SageCLI; `CLI-089` - 5dive; `CLI-090` - agx.
### Lote P3.6
- `CLI-091` - claude-code-router; `CLI-092` - cc-router; `CLI-093` - OneCLI.
### Lote P3.7
- `CLI-094` - agent-browser; `CLI-095` - OpenWork; `CLI-096` - Agent Deck (revisao de agente filho).
### Lote P4 - fechados/MITM
- `CLI-097` - Pool; `CLI-098` - Junie CLI; `CLI-099` - Cursor desktop.
- `CLI-100` - Windsurf; `CLI-101` - Amp; `CLI-102` - Amazon Q/Kiro CLI; `CLI-103` - Cowork.
## 4. Criterio para iniciar o lote seguinte
O lote seguinte pode iniciar quando os tres agentes do lote atual tiverem: pesquisa upstream anexada, gate de viabilidade preenchido, baseline registrado, resultado de smoke test ou bloqueio reproduzivel, e tracker atualizado. Uma falha de um agente nao deve paralisar os outros dois; o agente principal deve marcar `blocked` ou `research-more` com evidencia e seguir a fila.
## 5. Entregaveis de cada task
1. Nota de pesquisa fresca com commit/release e links.
2. Classificacao de viabilidade.
3. Diff minimo ou conclusao documentada de que nao ha diff necessario.
4. Testes e comandos executados, incluindo falhas preexistentes.
5. PR/issue upstream ou justificativa de config-only/MITM.
6. Entrada no catalogo OmniRoute quando aplicavel.
7. Atualizacao do tracker `04-tracker-integracoes-clis.md`.

View File

@@ -0,0 +1,144 @@
# Tracker de integracoes de CLIs com OmniRoute
**Status final da pesquisa:** `104/104` concluídos (`100%`), `0` casos `not-started`. Este é o registro individual autoritativo. O relatório executivo está em `06-relatorio-final-104-clis-e-estrategia-prs.md`.
**Snapshot inicial:** 2026-08-01
**Legenda de status:** `not-started`, `researching`, `research-more`, `viable-direct`, `viable-upstream`, `viable-acp`, `viable-mcp`, `needs-wrapper`, `needs-mitm`, `blocked`, `implementing`, `validating`, `published-pr`, `published-issue`, `awaiting-maintainer`, `accepted`, `rejected`, `integrated`.
Os campos externos (`branch`, `commit`, `PR`, `issue`) ficam como `—` ate haver evidencia real. “Catalogo OmniRoute” significa entrada local, nao necessariamente suporte upstream publicado.
| ID | Prio | Projeto | Repositorio | Pesquisa | Tipo | Upstream | Branch | Commit | PR | Issue | Catalogo OmniRoute | Observacoes/proximo passo |
|---|:---:|---|---|---|---|---|---|---|---|---|---|---|
| CLI-000 | P0 | jcode | `1jehuang/jcode` | concluida | `viable-upstream` | `awaiting-maintainer` | `feat/omniroute-provider` | `ee4f904e6` | [fork PR](https://github.com/diegosouzapw/jcode/pull/1) | [upstream #704](https://github.com/1jehuang/jcode/issues/704) | integrated | acompanhar mantenedor e concluir secao do README |
## Caso publicado: jcode
| Campo | Valor |
|---|---|
| Projeto | jcode |
| Repositorio | `https://github.com/1jehuang/jcode` |
| Status geral | `awaiting-maintainer` |
| Tipo | `viable-upstream`; perfil OpenAI-compatible dirigido por metadados |
| Branch | `feat/omniroute-provider` |
| Commit | `ee4f904e6` |
| PR | `https://github.com/diegosouzapw/jcode/pull/1` (fork de referencia) |
| Issue | `https://github.com/1jehuang/jcode/issues/704` |
| Catalogo OmniRoute | `integrated` / entrada existente |
| Validacao | `cargo check --workspace` limpo; 205 testes passaram; 1 falha preexistente/ambiental |
| Diff | 6 arquivos, `+56/-3` |
| Proximo passo | acompanhar issue #704 e criar secao de README do OmniRoute |
## Tabela principal
| ID | Prio | Projeto | Repositorio | Pesquisa | Tipo | Upstream | Branch | Commit | PR | Issue | Catalogo OmniRoute | Observacoes/proximo passo |
|---|:---:|---|---|---|---|---|---|---|---|---|---|---|
| CLI-001 | P0 | Gemini CLI | `google-gemini/gemini-cli` | concluida | `pr-generic` | `published-issue` | `fix/omniroute-gateway-auth` | `8138105c38cc1637fe9e8a9bd520eb835f1620e6` | — | [upstream #27550](https://github.com/google-gemini/gemini-cli/issues/27550#issuecomment-5152312278) | not-in-catalog | regression `AuthType.GATEWAY`; patch +26; auth 10/10, non-interactive 17/17, content generator 55/55, Gemini `/v1beta` stream/tools smoke verde; aguardar `help wanted` antes de terceira PR |
| CLI-002 | P0 | Claw Code | `ultraworkers/claw-code` | concluida | `pr-docs` | `published-issue` | `docs/omniroute-setup` | `de857038b2f9ff9b319132e2241549e86215c351` | — | [upstream #3283](https://github.com/ultraworkers/claw-code/issues/3283) | not-in-catalog | generic OpenAI Chat Completions; docs +37; 1.415 testes, fmt, docs/release checks e clippy oficial verdes; fork bloqueado pelo GitHub, issue-first; smoke OmniRoute parcial/timeout; chave do smoke deve ser rotacionada |
| CLI-003 | P0 | Plandex | `plandex-ai/plandex` | concluida | `pr-docs` | `published-pr` | `feat/omniroute-provider-docs` | `f8f0694bdf7d1cb6e65a1f1c5bc39f84921a4507` | [upstream #359](https://github.com/plandex-ai/plandex/pull/359) | — | not-in-catalog | custom provider OpenAI-compatible ja existia; docs com `/v1`, `OMNIROUTE_API_KEY`, Docker reachability e model mapping; Go indisponivel; Docusaurus build verde; acompanhar mantenedor |
| CLI-004 | P0 | MiMo Code | `XiaomiMiMo/MiMo-Code` | concluida | `config-only` | not-applicable | `research/omniroute-mimo-code` | — | — | — | not-in-catalog | SHA `ce124cb`; provider customizado `@ai-sdk/openai-compatible` já suporta `baseURL`, `apiKey` e modelo; 116 testes focados + typecheck verdes; smoke CLI inconclusivo por travamento ambiental; sem PR artificial |
| CLI-005 | P0 | Trae Agent | `bytedance/trae-agent` | concluida | `pr-docs` | `published-pr` | `research/omniroute-trae-agent` | `4801e48b69d7583300eb86ec5c69235506d7f205` | [upstream #449](https://github.com/bytedance/trae-agent/pull/449) | — | not-in-catalog | README +39; `provider: openai` + mapping `base_url=/v1`; `/v1/responses`, `/v1/models`, Bearer, tools e limitação sem streaming; 62 testes/17 skips, pre-commit e mocks verdes; CLA pendente |
| CLI-006 | P0 | Kimi CLI | `MoonshotAI/kimi-cli` | concluida | `pr-docs` | `published-issue` | `research/omniroute-kimi-cli` | `a2f62bf6108a6954e798db992411aa06670e224f` | — | [upstream #2576](https://github.com/MoonshotAI/kimi-cli/issues/2576) | not-in-catalog | docs EN/ZH +63; `openai_legacy` `/v1`, chave via `OPENAI_API_KEY`, modelo manual; Responses/Anthropic alternativos; 47 testes e VitePress verdes; aguardar direção do mantenedor antes da PR |
| CLI-007 | P0 | Every Code | `just-every/code` | concluida | `pr-docs` / `config-only` | `published-pr` | `feat/omniroute-integration` | `8fbc8dab5fb76bf05535055801af0c3ccfea6f3b` | [upstream #614](https://github.com/just-every/code/pull/614) | — | not-in-catalog | PR documental aberta e mergeable; release `v0.6.162`; `./build-fast.sh` baseline/pós-patch verdes; smoke mock Responses/SSE/tools verde; acompanhar CI/mantenedor |
| CLI-008 | P0 | Open Codex | `ymichael/open-codex` | concluida | `pr-generic` / `issue-first` | `published-issue` | `feat/omniroute-integration` | `f25de99f991c0e4d9d6ae2811d307cdbff92f869` | — | [upstream #4](https://github.com/ymichael/open-codex/issues/4#issuecomment-5152804104) | not-in-catalog | patch genérico pronto localmente; issue-first por firewall de container e PR #19 fechada; 132 testes, typecheck/build/format verdes; lint bloqueado por ambiente; aguardar mantenedor antes de PR |
| CLI-009 | P0 | VT Code | `vinhnx/vtcode` | concluida | `pr-docs` / `config-only` | `published-pr` | `feat/omniroute-integration` | `256682d10c72f3e6e145d852b6d9d53f5c471988` | [upstream #717](https://github.com/vinhnx/VTCode/pull/717) | — | not-in-catalog | PR documental aberta e mergeable; release `0.141.10`; custom provider `/v1`, Bearer, `auto`, discovery manual, streaming/tools; 10 testes config verdes; nextest/docs checks bloqueados por ambiente; acompanhar CI/mantenedor |
| CLI-010 | P0 | OpenHands CLI | `OpenHands/OpenHands-CLI` | concluida | `config-only` | not-applicable | `feat/omniroute-openhands-cli-integration` | — | — | — | not-in-catalog | SHA `2df8a283`; `LLM_BASE_URL=/v1`, `LLM_API_KEY`, modelo obrigatório `openai/auto`, Chat Completions/SSE/tools; 63 testes focados e mock verdes; sem PR artificial |
| CLI-011 | P0 | gptme | `gptme/gptme` | concluida | `config-only` | not-applicable | `feat/omniroute-gptme-integration` | — | — | — | not-in-catalog | SHA `7fe250529`; provider TOML nomeado, `/v1/chat/completions`, `/v1/models`, Bearer, streaming/tools; compileall verde, pytest bloqueado por deps; docs genericas ja cobrem |
| CLI-012 | P0 | Nanocoder | `Nano-Collective/nanocoder` | concluida | `config-only` | not-applicable | `feat/omniroute-nanocoder-integration` | — | — | — | not-in-catalog | SHA `becae998`; `createOpenAICompatible`, `/v1/models`, streaming/native tools + XML/JSON fallback; types/format/lint/build verdes; suite ampla com falhas preexistentes; sem PR artificial |
| CLI-013 | P0 | RA.Aid | `ai-christianson/RA.Aid` | concluida | `config-only` | not-applicable | `feat/omniroute-ra-aid-integration` | — | — | — | not-in-catalog | SHA `e71bb83`; provider `openai-compatible`, `/v1/chat/completions`, Bearer, modelo explicito/`auto`, function tools; 762 testes + 62 focados e smoke verdes; sem Responses/stream HTTP garantido; Aider exige config separada; sem PR artificial |
| CLI-014 | P0 | CoreCoder | `he-yufeng/CoreCoder` | concluida | `pr-docs` / `config-only` | `published-pr` | `feat/omniroute-integration` | `f4d2851649e5dda20738c313a8a94337b24eeb9d` | [upstream #20](https://github.com/he-yufeng/CoreCoder/pull/20) | — | not-in-catalog | PR documental aberta, nao draft e mergeable; `/v1/chat/completions`, Bearer, `auto`, streaming/native tools; 86 testes, compileall, build, twine e smoke verdes; Ruff mantem 41 falhas preexistentes; acompanhar CI/mantenedor |
| CLI-015 | P1 | Grok CLI | `superagent-ai/grok-cli` | concluida | `config-only` | not-applicable | `feat/omniroute-grok-cli-integration` | — | — | — | not-in-catalog | SHA `fb97af8`; `GROK_BASE_URL`/`--base-url`, Chat Completions/SSE, Bearer, `auto` e tools confirmados; 47/48 suites e 246 testes no gate isolado, 6 arquivos/39 testes focados verdes; Node não carrega `bun:sqlite`; Responses/search/STT/Batch/midia não garantidos; monitorar PRs #290/#349 |
| CLI-016 | P1 | Gitlawb Zero | `Gitlawb/zero` | concluida | `config-only` | not-applicable | `feat/omniroute-gitlawb-zero-integration` | — | — | — | not-in-catalog | SHA `8e266797`; release `v0.6.0`; provider custom `/v1`, Bearer, `auto`, Chat/SSE/tools, usage e `/v1/models` confirmados; Go test/vet/fmt e smoke verdes; release build bloqueado por falta de espaco; politica exige issue aprovada; sem contribuicao nominal artificial |
| CLI-017 | P1 | DeepSeek Reasonix | `esengine/DeepSeek-Reasonix` | concluida | `config-only` | not-applicable | `feat/omniroute-deepseek-reasonix-integration` | — | — | — | not-in-catalog | SHA `1c62489d`; release `v1.19.1`; `kind=openai`, `/v1/chat/completions`, Bearer, `auto`, SSE/tools, `/v1/models` e reasoning confirmados; suite completa, vet, fmt, build e smoke verdes apos remover env SSH do runner; sem PR/issue redundante |
| CLI-018 | P1 | KlaatCode | `KlaatAI/klaatcode` | concluida | `config-only` | not-applicable | `feat/omniroute-klaatcode-integration` | — | — | — | not-in-catalog | SHA `0d20f24a`; release `V2.4.0`; `customModels` com `/v1`, Bearer, `auto`, Chat/SSE/tools confirmados; 316 testes, 33 fixtures e build verdes; typecheck local divergiu do CI verde; custom endpoint e apenas TUI; divergencia de metadata de licenca registrada; sem contribuicao nominal artificial |
| CLI-019 | P1 | CodeMini CLI | `havingautism/Codemini-CLI` | concluida | `config-only` | not-applicable | `feat/omniroute-codemini-cli-integration` | — | — | — | not-in-catalog | SHA `a3764b21`; package `0.8.3`; gateway `/v1`, Bearer persistido, `auto`, Chat/SSE/usage/tools e tool round trip confirmados; `/models` e probe, nao picker; 122/123 testes, 10 focados e pack-imports verdes; sem PR nominal redundante |
| CLI-020 | P1 | Zot | `patriceckhart/zot` | concluida | `config-only` | not-applicable | `feat/omniroute-zot-integration` | — | — | — | not-in-catalog | SHA `f3d8eb66`; release `v0.3.29`; custom provider `omniroute` em `models.json`, `/v1`, Bearer, `auto`, Chat/SSE/tools/reasoning opt-in e cache usage confirmados; `--base-url` e so override; PR #36 ja cita OmniRoute; race suite/build/vet/fmt verdes |
| CLI-021 | P1 | Octomind | `Muvon/octomind` | concluida | `config-only` | not-applicable | `feat/omniroute-octomind-integration` | — | — | — | not-in-catalog | SHA `65ab1db1`; release `0.39.0`; provider `local:auto` usa endpoint completo `/v1/chat/completions`, Bearer opcional, Chat JSON buffered, tools/reasoning/usage; sem SSE/Responses/discovery; fmt/fetch e smokes com/sem auth verdes; suite ampla nao executada por disco/contencao |
| CLI-022 | P1 | DvalinCode | `arthurpanhku/dvalincode` | concluida | `config-only` | not-applicable | `feat/omniroute-dvalincode-integration` | — | — | — | not-in-catalog | SHA `7d42664a`; release `v0.14.1`; provider OpenAI-compatible custom com `/v1`, Bearer via env, `auto`, Chat/SSE/usage/tools e tool round trip confirmados; `provider test` bloqueado por trusted presets; issues #109/#118/#135 ja cobrem melhorias genericas; sem PR nominal |
| CLI-023 | P1 | Coro Code | `Blushyes/coro-code` | concluida | `config-only` | not-applicable | `feat/omniroute-coro-code-integration` | — | — | — | not-in-catalog | SHA `679c57af`; release `v0.0.8`; `OPENAI_BASE_URL=/v1`, Bearer, `auto`, Chat JSON e function tools/tool loop confirmados; streaming existe mas nao e usado pelo agente; sem Responses/discovery; `cargo check`/fmt bloqueados por drift preexistente; risco de LICENSE ausente; sem PR nominal |
| CLI-024 | P1 | Mini-Kode | `minmaxflow/mini-kode` | concluida | `config-only` | not-applicable | `feat/omniroute-mini-kode-integration` | — | — | — | not-in-catalog | SHA `4e7f9767`; release/tag npm `0.2.3`; provider custom por `MINIKODE_BASE_URL=/v1`, Bearer, `auto`, Chat/SSE e tools/tool loop confirmados; sem Responses/discovery/reasoning dedicado; sem PR nominal redundante |
| CLI-025 | P1 | Late CLI | `mlhher/late-cli` | concluida | `config-only` | not-applicable | `feat/omniroute-late-cli-integration` | — | — | — | not-in-catalog | SHA `26814e62`; release `v1.4.2`; `OPENAI_BASE_URL=/v1`, Bearer, `auto`, Chat/SSE/usage/reasoning_content/tools e tool round trip confirmados; probes `/props`/`/v1/models` nao sao picker; BSL 1.1/CLA; sem PR nominal |
| CLI-026 | P1 | Agentty | `1ay1/agentty` | concluida | `config-only` | not-applicable | `feat/omniroute-agentty-integration` | — | — | — | not-in-catalog | SHA `e947b26c`; release `v0.2.10`; custom host `127.0.0.1:20128`, Bearer, Chat/SSE/tools e `/v1/models` confirmados; Responses/reasoning/tool round trip dinamico nao confirmados; MIT; sem PR nominal |
| CLI-027 | P1 | Aizen | `aizen-stack/aizen` | concluida | `config-only` | not-applicable | `feat/omniroute-aizen-integration` | — | — | — | not-in-catalog | SHA `3d8ae0f6`; release `v0.5.4`; `AIZEN_BASE_URL=/v1`, Bearer, `auto`/modelo literal, Chat/SSE/reasoning_content e `/v1/models`; tools confirmadas estaticamente, sem smoke dinamico; PolyForm Noncommercial/CLA; sem PR nominal |
| CLI-028 | P1 | Clif-Code | `DLhugly/Clif-Code` | concluida | `config-only` | not-applicable | `feat/omniroute-clif-code-integration` | — | — | — | not-in-catalog | SHA `282a787a`; release `v1.72.0`; `CLIFCODE_API_URL=/v1`, Bearer, `auto`, Chat/SSE/usage/tools e tool loop confirmados por fonte; smoke bloqueado por binario ausente; sem Responses/reasoning; licença proprietária conflitante com FSL declarada exige revisão jurídica; sem PR nominal |
| CLI-029 | P1 | Minacode | `hit9/minacode` | concluida | `config-only` | not-applicable | `feat/omniroute-minacode-integration` | — | — | — | not-in-catalog | SHA `d4ea4a97`; release `v0.18.1`; TOML custom `/v1`, key obrigatória, `auto`, Chat/Responses/Anthropic, SSE/tools/reasoning/discovery confirmados; smoke de protocolo Chat+Responses+models e compileall verdes; CI remoto verde; sem PR nominal |
| CLI-030 | P1 | YottaCode | `yottadynamics/yottacode` | concluida | `config-only` | not-applicable | `feat/omniroute-yottacode-integration` | — | — | — | not-in-catalog | SHA `039f61ce`; release `v0.3.1`; provider `openai-compatible`, `/v1`, Bearer, `/v1/models`, Chat/SSE/tools/reasoning parsing confirmados; smoke oficial com mock passou; Go 1.26 nao instalado e gates completos nao executados por espaco; sem PR nominal |
| CLI-031 | P1 | aichat | `sigoden/aichat` | concluida | `config-only` | not-applicable | `feat/omniroute-aichat-integration` | — | — | — | not-in-catalog | SHA `82976d3`; package/release `v0.30.0`; provider `openai-compatible` com base `/v1`, Bearer opcional e modelo `auto`; Chat stream/JSON, reasoning e tool round-trip confirmados; Responses ausente (#1431); limites de tool SSE ja cobertos por #1454/#1495 e PR #1496; sem publicacao nominal |
| CLI-032 | P1 | ShellGPT | `TheR1D/shell_gpt` | concluida | `config-only` | not-applicable | `feat/omniroute-shellgpt-integration` | — | — | — | not-in-catalog | SHA `a082bd53`; release `1.5.1`; `API_BASE_URL=/v1`, `OPENAI_API_KEY`, `DEFAULT_MODEL=auto` e `USE_LITELLM=false`; smoke real confirmou env e `.sgptrc`, Chat/SSE e Bearer; issue #718 nao reproduz no HEAD; CI baseline vermelho por temperatura default independente; sem publicacao nominal |
| CLI-033 | P1 | Mistral Vibe | `mistralai/mistral-vibe` | concluida | `config-only` | not-applicable | `feat/omniroute-mistral-vibe-integration` | — | — | — | not-in-catalog | SHA/release `99a6efa9` / `v2.23.2`; `GenericBackend` custom com base `/v1`, Bearer, Chat/SSE, usage, tools e reasoning; smoke do binario oficial verde; #790 cobre somente discovery `/v1/models`; upstream nao aceita contribuicoes de codigo no momento; sem publicacao |
| CLI-034 | P1 | OpenSquilla | `opensquilla/opensquilla` | concluida | `config-only` | not-applicable | `feat/omniroute-opensquilla-integration` | — | — | — | not-in-catalog | `custom` com `/v1`, Bearer opcional, Chat/SSE, tools, reasoning recebido, usage e `/v1/models`; smoke provider-level verde; monitorar issue #912 do probe custom; sem publicacao nominal |
| CLI-035 | P1 | Kode CLI | `shareAI-lab/Kode-cli` | concluida | `config-only` | not-applicable | `feat/omniroute-kode-cli-integration` | — | — | — | not-in-catalog | `custom-openai` com `/v1`, discovery `/v1/models`, fallback manual, Bearer, Chat/SSE, tools/tool round-trip e persistencia; smoke runtime bloqueado por Bun/artefato ausente; CI baseline vermelho por formatacao; sem publicacao nominal |
| CLI-036 | P1 | Neovate Code | `neovateai/neovate-code` | concluida | `config-only` | not-applicable | `feat/omniroute-neovate-code-integration` | — | — | — | not-in-catalog | provider JSON custom normalizado para OpenAI-compatible, `/v1`, Bearer, Chat/SSE, tools/tool round-trip; model catalog declarado (sem discovery); smoke do pacote publicado verde; sem publicacao nominal |
| CLI-037 | P1 | Deep Agents Code | `langchain-ai/deepagents` | concluida | `config-only` | not-applicable | `feat/omniroute-deepagents-code-integration` | — | — | — | not-in-catalog | SHA `46ee772b4`; `deepagents-code==0.1.51`; provider `openai`, base OmniRoute `/v1`, model `openai:auto`; Responses e default, Chat usa `use_responses_api=false`; smoke de config verde, sem HTTP/runtime por deps e disco; #3973/#3287 ja cobrem os pontos genericos; sem publicacao nominal |
| CLI-038 | P1 | OpenHands principal | `OpenHands/OpenHands` | concluida | `config-only` | not-applicable | `feat/omniroute-openhands-main-integration` | — | — | — | not-in-catalog | SHA `1708efc44`; Agent Canvas `1.8.0`; `openai/auto` + base `/v1` + API key + `api_mode=chat`; LiteLLM envia `model=auto`, Chat/SSE/tools estruturais; sem discovery generico `/v1/models`; PRs OmniRoute [#15189](https://github.com/OpenHands/OpenHands/pull/15189)/[#15211](https://github.com/OpenHands/OpenHands/pull/15211) fechadas sem merge; sem nova publicacao |
| CLI-039 | P1 | SWE-agent | `SWE-agent/SWE-agent` | concluida | `config-only` | not-applicable | `feat/omniroute-swe-agent-integration` | — | — | — | not-in-catalog | SHA `3ea751c08`; release `v1.1.0`; LiteLLM com `openai/<model-id>`, `api_base=/v1` e chave por env; Chat/tools/tool round-trip e batch confirmados por fonte; reasoning parcial; smoke HTTP bloqueado por deps ausentes; sem publicacao nominal |
| CLI-040 | P1 | AutoCodeRover | `AutoCodeRoverSG/auto-code-rover` | concluida | `pr-generic` | `validating` | `feat/omniroute-auto-code-rover-integration` | — | — | — | not-in-catalog | SHA `585d3e639`; patch local sem commit em 4 arquivos corrige `litellm-generic-openai/auto`, base `/v1`, precedencia da chave e pricing desconhecido; 9 testes focados com stubs, tracer source-only, compileall e diff-check verdes; sem HTTP real; licenca SONAR Source-Available exige gate juridico antes de publicar |
| CLI-041 | P2 | Claurst | `Kuberwastaken/claurst` | concluida | `config-only` | not-applicable | `feat/omniroute-claurst-integration` | — | — | — | not-in-catalog | SHA `595b0ebe3`; `custom-openai` com settings persistidos, base `/v1`, `CUSTOM_OPENAI_API_KEY`, modelo `auto`, Chat/SSE/tools e `/v1/models`; CI upstream verde; sem build/smoke local e sem publicacao nominal; monitorar PR #365 sem duplicar |
| CLI-042 | P2 | Codebuff | `CodebuffAI/codebuff` | concluida | `blocked` / `issue-first` | `blocked` | `feat/omniroute-codebuff-integration` | — | — | — | not-in-catalog | SHA `195b9bef6`; main nao expoe base/chave/provider custom na CLI/SDK; PR upstream existente [#693](https://github.com/CodebuffAI/codebuff/pull/693) cobre a lacuna, observada OPEN/CONFLICTING/DIRTY; nao criar patch concorrente; acompanhar #693 e validar apos merge/port |
| CLI-043 | P2 | Devon | `entropy-research/Devon` | concluida | `pr-generic` | validating | `feat/omniroute-devon-integration` | — | — | [upstream #100](https://github.com/entropy-research/Devon/issues/100) | not-in-catalog | SHA `8f68f1d74`; diff local genérico em 5 arquivos, sem commit; reprodução literal DeepSeek/OpenRouter e resume corrigidos; 9 testes focados, compileall e diff-check verdes; Standards/Spec aprovados; aguardar autorização antes de fork/push/PR |
| CLI-044 | P2 | Letta Code | `letta-ai/letta-code` | concluida | `config-only` | not-applicable | `feat/omniroute-letta-code-integration` | — | — | — | integrated | SHA `09aff1bb4`; já coberta pelo provider local `lmstudio` (`lmstudio_openai`), discovery `/api/v0/models``/v1/models`, Chat/SSE/tools; 8 testes OmniRoute verdes; sem PR nominal |
| CLI-045 | P2 | CodeMachine CLI | `moazbuilds/CodeMachine-CLI` | concluida | `config-only` | not-applicable | `feat/omniroute-codemachine-cli-integration` | — | — | — | not-in-catalog | SHA `572def63e`; integração indireta por OpenCode custom `@ai-sdk/openai-compatible`, base `/v1`, chave por env e `omniroute/auto`; provider/model reconhecidos no smoke de config; alternativa Claude Code; sem PR nominal |
| CLI-046 | P2 | Groq Code CLI | `build-with-groq/groq-code-cli` | concluida | `pr-generic` | `awaiting-maintainer` | `feat/omniroute-groq-code-cli-integration` | — | — | — | not-in-catalog | SHA `a303eb4be`; `groq-sdk@0.27.0` fixa `/openai/v1/chat/completions`, logo não há config-only para OmniRoute; mock confirmou path/Bearer; PR existente [#7](https://github.com/build-with-groq/groq-code-cli/pull/7) é a duplicata natural, mas precisa distinguir Groq-compatible de OpenAI-compatible; 17 testes oficiais + 5 testes de contexto, build e mock verdes; clone limpo, sem patch/publicação |
| CLI-047 | P2 | Dexto | `truffle-ai/dexto` | concluida | `config-only` | `not-applicable` | `feat/omniroute-dexto-integration` | — | — | — | not-in-catalog | SHA `4108a9c73`; provider `openai-compatible` nativo exige `baseURL`, aceita modelo arbitrário, Bearer opcional, Chat/SSE/tools e reasoning effort; receita `/v1` + `auto`; 175 testes focados e builds llm/core verdes; TS2741 em chatgpt-oauth é baseline; ELv2; sem PR/issue nominal |
| CLI-048 | P2 | claw-code-agent | `HarnessLab/claw-code-agent` | concluida | `config-only` | `not-applicable` | `feat/omniroute-claw-code-agent-integration` | — | — | — | not-in-catalog | SHA `167571da8`; `OPENAI_BASE_URL=http://127.0.0.1:20128/v1`, Bearer, model manual/`auto`, Chat/SSE/tools/usage confirmados; smoke `MOCK_SMOKE_OK`, 80 testes focados; sem discovery/Responses API; licença não identificada (`license: null`); sem PR/issue |
| CLI-049 | P2 | g3 | `dhanji/g3` | concluida | `pr-generic` | `validating` | `feat/omniroute-g3-integration` | — | — | [upstream #70](https://github.com/dhanji/g3/issues/70) | not-in-catalog | SHA `0ddb052d2`; diff local provider-neutral em `provider_registration.rs`, 1 arquivo `+25/-1`, corrige registro `custom``custom.default`; `cargo check -p g3-config`, 6 testes config e diff-check verdes; teste focal escrito mas build bloqueado em `x11.pc`; manifesto declara MIT sem arquivo LICENSE; Standards/Spec centrais aprovados; sem publicação |
| CLI-050 | P2 | San | `genai-io/san` | concluida | `config-only` | `not-applicable` | `feat/omniroute-san-integration` | — | — | — | not-in-catalog | SHA `e45ec0ef7`; Apache-2.0/release v1.22.1; provider Custom com base `/v1`, Bearer, `/models`, Chat/SSE/tools/tool result e reasoning best-effort; smoke HTTP de dois turnos e gates Go focados verdes; sem provider nominal ou publicação |
| CLI-051 | P2 | Waveloom | `Menfre01/waveloom` | concluida | `config-only` | `not-applicable` | `feat/omniroute-waveloom-integration` | — | — | — | not-in-catalog | SHA `293d5cd11`; Apache-2.0/release v0.5.1; adapter OpenAI com `/v1`, Bearer, `/models`, SSE, 14 tools, tool-result round-trip e sessões; smoke do binário oficial verde e CI remoto do HEAD verde; reasoning/cache avançados não são projetados; sem publicação |
| CLI-052 | P2 | picocode | `jondot/picocode` | concluida | `config-only` | `not-applicable` | `feat/omniroute-picocode-integration` | — | — | — | not-in-catalog | SHA `064a2a6ea`; MIT/release v0.6.0; Rig 0.28 lê `OPENAI_BASE_URL` e usa Responses `/v1/responses`; smoke confirmou Bearer, `auto`, 11 tools e function_call_output; 7 testes/doc-tests verdes; fmt/clippy só baseline; sem PR/issue |
| CLI-053 | P2 | QQCode | `qnguyen3/qqcode` | concluida | `config-only` | `not-applicable` | `feat/omniroute-qqcode-integration` | — | — | — | not-in-catalog | SHA `be6a96ce7`; Apache-2.0/release v1.2.0; provider arbitrário + `GENERIC`/OpenAI com base `/v1`; smoke confirmou JSON/SSE, Bearer, extra_body, reasoning e tool-result; backend 20/20, ACP 13+1 skip, observer 11/11, compileall/helps verdes; sem PR/issue |
| CLI-054 | P2 | Keen Code | `mochow13/keen-code` | concluida | `config-only` | `not-applicable` | `feat/omniroute-keen-code-integration` | — | — | — | not-in-catalog | SHA `ee2eaf0f4`; MIT/release v0.40.0; receita manual `openai-compatible` + `/v1` + Bearer + model arbitrário; smoke oficial confirmou Chat/SSE, tools/tool-result, usage e reasoning replay; provider oculto apenas no picker; CI remoto verde; sem PR/issue |
| CLI-055 | P2 | Grinta | `josephsenior/Grinta-Coding-Agent` | concluida | `config-only` | `not-applicable` | `feat/omniroute-grinta-integration` | — | — | — | not-in-catalog | SHA `df7437524`; provider OpenAI-compatible com `LLM_API_KEY`, model `auto`, base `/v1`; smoke Chat/SSE/tools/tool-result/reasoning/usage/cache verde; 183 testes focados, compileall e Ruff verdes; sem PR/issue nominal |
| CLI-056 | P2 | Zap | `zap-coding-agent/zap-coding-agent` | concluida | `config-only` | `not-applicable` | `feat/omniroute-zap-integration` | — | — | — | not-in-catalog | SHA `f0203f872`; provider arbitrário `kind=openai`, base `/v1`, Bearer, discovery `/models`, Chat JSON/SSE, tools/tool-result, reasoning e usage confirmados; cargo check + 16 testes/gates focados verdes; issue #2 confirma arquitetura; sem PR nominal |
| CLI-057 | P2 | Binharic | `CogitatorTech/binharic-cli` | concluida | `pr-generic` | `validating` | `feat/omniroute-binharic-integration` | — | — | — | not-in-catalog | SHA `52ccca70b`; patch sem commit em `provider.ts` + teste: aplica `baseURL` ao OpenAI/Anthropic e usa Chat Completions para base customizada; RED→GREEN, 14 focal, 88 arquivos/774 testes, typecheck/build e smoke wire verdes; lint upstream bloqueado; sem publicação |
| CLI-058 | P2 | Darce | `AmerSarhan/darce-cli` | concluida | `config-only` | `not-applicable` | `feat/omniroute-darce-integration` | — | — | — | not-in-catalog | SHA `1b90c379a`; MIT declarada no package/npm sem arquivo LICENSE; `DARCE_API_BASE` raiz sem `/v1`, `DARCE_API_KEY`, `DARCE_MODEL=auto`; smoke PTY do binário confirmou 2 Chat/SSE, 7 tools, tool-result e Bearer; 106 testes/build verdes; sem MCP/ACP/A2A; sem PR/issue |
| CLI-059 | P2 | CLAII | `agencyswarm/CLAII` | concluida | `pr-generic` | `blocked` | `feat/omniroute-claii-integration` | — | — | — | not-in-catalog | SHA `89d42311b`; patch sem commit em README/config/providers/test: `CLAII_API_KEY`, `CLAII_BASE_URL` origem sem `/v1beta`, model runtime e reject explícito; 4 wire/loop + 10 calculator + pip install + smoke CLI verdes; unittest discover falha só baseline `calculator`/`pkg`; sem MCP/ACP/A2A; **All Rights Reserved**, não publicar sem autorização jurídica |
| CLI-060 | P2 | nori-cli | `tilework-tech/nori-cli` | concluida | `config-only` | `not-applicable` | `feat/omniroute-nori-cli-integration` | — | — | — | not-in-catalog | SHA `829ecf3fd`; Apache-2.0/v0.24.0; Nori custom ACP → OpenCode `opencode-ai@1.18.11` → OmniRoute `/v1`; MCP separado por `/api/mcp/stream` ou stdio; 5 testes focados, cargo build nori e smoke ACP Nori→OpenCode verdes; sem patch/publicação |
| CLI-061 | P2 | cursor-agent clone | `civai-technologies/cursor-agent` | concluida | `config-only` | `not-applicable` | `feat/omniroute-cursor-agent-clone-integration` | — | — | — | not-in-catalog | SHA `d21a8f3d4`; MIT/v0.1.39; SDK OpenAI usa base `/v1`, Anthropic usa raiz; smokes de 2 turnos/tools verdes; factory rejeita `auto` puro; 23 testes, mypy/build verdes; sem patch/publicação |
| CLI-062 | P2 | Free Code | `freecodexyz/free-code` | concluida | `config-only` | `blocked` | `feat/omniroute-free-code-integration` | — | — | [upstream #20](https://github.com/freecodexyz/free-code/issues/20) | not-in-catalog | SHA `6b25ab68b`; URL antiga `paoloanzn/free-code` redireciona; base Anthropic raiz, `model=auto`, stream/tools/MCP; build verde; sem LICENSE/campo license e código atribuído à Anthropic, não publicar |
| CLI-063 | P2 | Claude Engineer | `Doriandarko/claude-engineer` | concluida | `config-only` / `pr-generic` | `blocked` | `feat/omniroute-claude-engineer-integration` | — | [upstream #250](https://github.com/Doriandarko/claude-engineer/pull/250) | [upstream #116](https://github.com/Doriandarko/claude-engineer/issues/116) | not-in-catalog | SHA `0a9e4b309`; v3 funciona por base Anthropic raiz com modelo fixo; #250 já adiciona `ANTHROPIC_MODEL`; arquivo LICENSE ausente apesar de declaração MIT; sem patch concorrente/publicação |
| CLI-064 | P2 | Smol Developer | `smol-ai/developer` | concluida | `config-only` | `not-applicable` | `feat/omniroute-smol-developer-integration` | — | — | — | not-in-catalog | SHA `a6747d1a6`; `OPENAI_API_BASE=/v1`, `auto`, 3 Chat calls, SSE/function calling e Agent Protocol validados; gates de runtime verdes, build metadata preexistente; sem patch/publicação |
| CLI-065 | P2 | Agentless | `OpenAutoCoder/Agentless` | concluida | `config-only` | `not-applicable` | `feat/omniroute-agentless-integration` | — | — | — | not-in-catalog | SHA `5ce5888b9`; OpenAI chat + embeddings funcionam com bases distintas; Anthropic normal/cache histórico validados; DeepSeek fixa host; pre-commit/compileall verdes; sem patch/publicação |
| CLI-066 | P2 | Amazon Q Developer CLI | `aws/amazon-q-developer-cli` | concluida | `viable-mcp` / `needs-wrapper` | `not-applicable` | `feat/omniroute-amazon-q-developer-cli-integration` | — | — | — | not-in-catalog | SHA `15cc8f3cd`; modelo usa AWS JSON/EventStream Bearer/SigV4 e não `/v1`; MCP stdio imediato, HTTP legado com ressalva; upstream issue-first/manutenção crítica; sem patch/publicação |
| CLI-067 | P2 | nanobot | `HKUDS/nanobot` | concluida | `config-only` | `not-applicable` | `feat/omniroute-nanobot-integration` | — | — | — | not-in-catalog | HEAD `44b7e1bf4`; provider dinâmico OpenAI-compatible com base `/api/v1` e modelo `omniroute/auto`; Chat/SSE/tools/reasoning/usage/images/discovery e retry validados; 424 testes + Ruff; sem PR nominal |
| CLI-068 | P2 | ZeroClaw | `zeroclaw-labs/zeroclaw` | concluida | `config-only` | `not-applicable` | `feat/omniroute-zeroclaw-integration` | — | — | — | not-in-catalog | HEAD `4770420ab`; `custom.omniroute`, base `/v1`, Bearer, `auto`, Chat/Responses e tools nativas opt-in; 1.173 unit + 1 integração, fmt/config/smoke verdes; sem PR nominal |
| CLI-069 | P2 | NanoClaw | `gavrielc/nanoclaw` | concluida | `config-only` | `not-applicable` | `feat/omniroute-nanoclaw-integration` | — | — | — | not-in-catalog | HEAD `dfac7e0af`; provider Claude existente aponta para raiz Anthropic OmniRoute e OneCLI guarda a chave; baseline e 49 testes OmniRoute verdes; Codex #3155/#1984 e OpenCode #2985 ficam como follow-ups; sem PR |
| CLI-070 | P2 | PicoClaw | `sipeed/picoclaw` | concluida | `config-only` | `not-applicable` | `feat/omniroute-picoclaw-integration` | — | — | — | not-in-catalog | HEAD `49183d7`, `/api/v1`, `openai/auto``auto`; Chat/SSE/tools/usage/images/discovery; Go ausente, testes locais não executados; issue router #3298; sem publicação |
| CLI-071 | P2 | IronClaw | `nearai/ironclaw` | concluida | `config-only` | `not-applicable` | `feat/omniroute-ironclaw-integration` | — | — | — | not-in-catalog | HEAD `4b71aaae`; `openai_compatible` `/api/v1`, Chat/SSE/tools/images/discovery; 889+5 testes e fmt verdes; reasoning #3673; sem publicação |
| CLI-072 | P2 | NullClaw | `nullclaw/nullclaw` | concluida | `config-only` | `not-applicable` | `feat/omniroute-nullclaw-integration` | — | — | — | not-in-catalog | HEAD `d8a802fd`; custom `/api/v1`, Chat/Responses/Anthropic, tools/streaming/usage/images; Zig ausente, CI run 30788444193 verde; sem publicação |
| CLI-073 | P2 | Moltis | `moltis-org/moltis` | concluida | `config-only` | `not-applicable` | `feat/omniroute-moltis-integration` | — | — | — | not-in-catalog | HEAD `678d407`; `custom-omniroute`, `/api/v1`, `auto`, Chat/SSE/tools/reasoning/usage/images; 401 testes + fmt verdes; MCP/ACP separados; sem publicação |
| CLI-074 | P2 | GitClaw | `open-gitagent/gitclaw` | concluida | `config-only` | `not-applicable` | `feat/omniroute-gitclaw-integration` | — | — | — | not-in-catalog | GitAgent HEAD `d3e25d7`; base `/api/v1`, `omniroute:auto`, Chat/SSE/tools/images; build + 65 testes + smoke verdes; reasoning=false no descriptor; sem publicação |
| CLI-075 | P2 | LionClaw | `moshthepitt/lionclaw` | concluida | `patch-required` / `issue-first` | `awaiting-maintainer` | `feat/omniroute-lionclaw-integration` | — | — | — | not-in-catalog | HEAD `cb59b23d`; Codex app-server não projeta config.toml/secret para runtime confinado; patch seguro necessário, alinhado à #157; gates locais bloqueados por uv/podman; CI verde; sem publicação |
| CLI-076 | P3 | VibePod | `VibePod/vibepod-cli` | concluida | `config-only` | `not-applicable` | `feat/omniroute-vibepod-integration` | — | — | — | not-in-catalog | Claude Code via `/api`, container usa `host.docker.internal`; Codex não injeta chave; compileall verde, pytest bloqueado por typer; sem publicação |
| CLI-077 | P3 | zeroshot | `the-open-engine/zeroshot` | concluida | `config-only` | `not-applicable` | `feat/omniroute-zeroshot-integration` | — | — | — | not-in-catalog | Gateway OpenAI `/api/v1`, `auto`, tools fail-closed; 22 testes + build verdes; sem streaming JSON/reasoning/MCP no gateway; sem publicação |
| CLI-078 | P3 | Fractal | `plasma-ai/fractal` | concluida | `config-only` / `needs-wrapper` | `awaiting-maintainer` | `feat/omniroute-fractal-integration` | — | — | — | not-in-catalog | Codex Responses por node `CODEX_HOME`; caveat tmux quente não encaminha `OMNIROUTE_API_KEY`; fix genérico recomendado, sem PR |
| CLI-079 | P3 | Bernstein | `chernistry/bernstein` | concluida | `config-only` | `not-applicable` | `feat/omniroute-bernstein-integration` | — | — | — | not-in-catalog | Canonical `sipyourdrink-ltd/bernstein`; openai_agents `/api/v1`, auto, api_key_env allowlisted; testes bloqueados por openai ausente; sem publicação |
| CLI-080 | P3 | Traycer | `traycerai/traycer` | concluida | `config-only` indireto | `not-applicable` | `feat/omniroute-traycer-integration` | — | — | — | not-in-catalog | Harness OpenCode + provider `@ai-sdk/openai-compatible`, `/api/v1`, `omniroute/auto`; host central fechado; sem publicação |
| CLI-081 | P3 | h5i | `h5i-dev/h5i` | concluida | `patch-required` | `awaiting-maintainer` | `feat/omniroute-h5i-integration` | — | — | — | not-in-catalog | Auth proxy/egress Codex fixos em OpenAI anulam base custom; patch seguro/policy-pinned necessário; CI externa verde; sem publicação |
| CLI-082 | P3 | OMK | `dmae97/open-multi-agent-kit` | concluida | `viable-mcp` | `not-applicable` | `feat/omniroute-omk-integration` | — | — | — | not-in-catalog | pesquisa concluída neste lote; controle multiagente, MCP é caminho primário; sem provider nominal |
| CLI-083 | P3 | kodo | `ikamensh/kodo` | concluida | `config-only` indireto | `not-applicable` | `feat/omniroute-kodo-integration` | — | — | — | not-in-catalog | pesquisa concluída neste lote; orquestrador/agent child, propagar env/base/model ao agente filho |
| CLI-084 | P3 | ORCH | `oxgeneral/ORCH` | concluida | `needs-wrapper` | `awaiting-maintainer` | `feat/omniroute-orch-integration` | — | — | — | not-in-catalog | pesquisa concluída neste lote; fila/controle sem provider LLM direto, wrapper/adaptador necessário |
| CLI-085 | P3 | LoopTroop | `LoopTroop-ai/LoopTroop` | concluida | `config-only` indireto | `not-applicable` | `feat/omniroute-looptroop-integration` | — | — | — | not-in-catalog | HEAD `cbfc81c5`; OpenCode recebe provider `@ai-sdk/openai-compatible`, `/api/v1`, `omniroute/auto`; 16 testes verdes; sem publicação |
| CLI-086 | P3 | Galley | `shinpr/galley` | concluida | `patch-required` | `awaiting-maintainer` | `feat/omniroute-galley-integration` | — | — | — | not-in-catalog | HEAD `6bcc593d`; registry/transports fechados, requer transport OpenAI-compatible para executor e supervisor; Go ausente; sem publicação |
| CLI-087 | P3 | Relay | `jcast90/relay` | concluida | `config-only` | `not-applicable` | `feat/omniroute-relay-integration` | — | — | — | not-in-catalog | HEAD `7bd5a2f6`; provider profile Codex com `OPENAI_BASE_URL`, key ref e modelo; smoke Responses obrigatório; MCP separado; sem publicação |
| CLI-088 | P3 | SageCLI | `youwangd/SageCLI` | concluida | `config-only` indireto | `not-applicable` | `feat/omniroute-sagecli-integration` | — | — | — | not-in-catalog | HEAD `c167712d`; Codex runtime, base/key configuradas fora do Sage; env plaintext caveat; 45 testes verdes; sem publicação |
| CLI-089 | P3 | 5dive | `5dive-ai/5dive` | concluida | `patch-required` | `awaiting-maintainer` | `feat/omniroute-5dive-integration` | — | — | — | not-in-catalog | HEAD `b64b6dac`; provider/base maps fechados; patch OpenAI-compatible genérico; 50 testes focados verdes; sem publicação |
| CLI-090 | P3 | agx | `ramarlina/agx` | concluida | `config-only` indireto | `not-applicable` | `feat/omniroute-agx-integration` | — | — | — | not-in-catalog | HEAD `e674cec1`; Codex herda base/key/model; smoke Responses e governança `--full-auto`; Jest ausente; sem publicação |
| CLI-091 | P3 | claude-code-router | `musistudio/claude-code-router` | concluida | `config-only` | `not-applicable` | `feat/omniroute-claude-code-router-integration` | — | — | — | not-in-catalog | HEAD `bc8a8e62`; provider custom OpenAI/Anthropic/Gemini, Chat/Responses; smoke por protocolo; sem publicação |
| CLI-092 | P3 | cc-router | `finch-xu/cc-router` | concluida | `config-only` | `not-applicable` | `feat/omniroute-cc-router-integration` | — | — | — | not-in-catalog | HEAD `c4c7579`; custom Responses/Chat com base/path/header, SSE/tools/reasoning; cargo bloqueado por glib; sem publicação |
| CLI-093 | P3 | OneCLI | `onecli/onecli` | concluida | `config-only` | `not-applicable` | `feat/omniroute-onecli-integration` | — | — | — | not-in-catalog | HEAD `84ccaf74`; MITM credential gateway, generic host injection; MCP separado; sem publicação |
| CLI-094 | P3 | agent-browser | `vercel-labs/agent-browser` | concluida | `config-only` | `not-applicable` | `feat/omniroute-agent-browser-integration` | — | — | — | not-in-catalog | HEAD `01c1147d`; chat usa gateway Chat/SSE/tools com env key/model; base precisa validar sufixo `/v1` para não duplicar path; cargo test exit 0; sem publicação |
| CLI-095 | P3 | OpenWork | `different-ai/openwork` | concluida | `config-only` indireto | `not-applicable` | `feat/omniroute-openwork-integration` | — | — | — | not-in-catalog | HEAD `ecb7a5f0`; OpenCode custom provider `/api/v1`, auth gerenciada; sem testes/deps; sem publicação |
| CLI-096 | P3 | Agent Deck review | `asheshgoplani/agent-deck` | concluida | `config-only` indireto | `not-applicable` | `feat/omniroute-agent-deck-review` | — | — | — | integrated | HEAD `46300807`; env/model propagados a Codex/OpenCode; Go ausente; sem publicação |
| CLI-097 | P4 | Pool | `poolsideai/pool` | concluida | `config-only` | `not-applicable` | `feat/omniroute-pool-integration` | — | — | — | not-in-catalog | HEAD `a6fe0ca1`; `pool exec --api-url` OpenAI-compatible, sandbox required, MCP/ACP separado; EULA; sem publicação |
| CLI-098 | P4 | Junie CLI | `junie.jetbrains.com` | concluida | `config-only` | `not-applicable` | `feat/omniroute-junie-integration` | — | — | — | not-in-catalog | HEAD `d2701be6`; custom profile OpenAICompletion/Responses com baseUrl full e env ref; runtime proprietário/EAP; sem publicação |
| CLI-099 | P4 | Cursor desktop | Anysphere | concluida | `config-only` limitado | `awaiting-maintainer` | `feat/omniroute-cursor-desktop-integration` | — | — | — | integrated | disclosure-only; BYO key/chat panel; Composer/Tab nativos; privado/MITM proibido; sem publicação |
| CLI-100 | P4 | Windsurf | Codeium | concluida | `blocked-closed` / MCP-only | `awaiting-maintainer` | `feat/omniroute-windsurf-integration` | — | — | — | not-in-catalog | sem upstream/base custom; BYOK Anthropic específico; MCP separado; MITM proibido; sem publicação |
| CLI-101 | P4 | Amp | Sourcegraph | concluida | `config-only` parcial / Enterprise-gated | `awaiting-maintainer` | `feat/omniroute-amp-integration` | — | — | — | not-in-catalog | CLI fechada/Amp Server; confirmar provider custom com suporte; MCP viável; sem publicação |
| CLI-102 | P4 | Amazon Q/Kiro CLI | AWS | concluida | `patch-required` legado / `blocked-closed` Kiro | `awaiting-maintainer` | `feat/omniroute-amazon-q-integration` | — | — | — | integrated | Q usa AWS EventStream/SigV4; Kiro fechado sem base custom; MCP-only seguro; sem publicação |
| CLI-103 | P4 | Cowork | Anthropic | concluida | `blocked-closed` / MCP-only | `not-applicable` | — | — | — | — | not-in-catalog | inferência gerida pela Anthropic sem BYOK/base custom; Custom Connector MCP remoto; MITM proibido; sem publicação |
## Como atualizar
Ao terminar uma fase, alterar somente os campos comprovados e deixar os demais como `—`. Para uma integracao concluida, registrar: versao/commit pesquisado, mecanismo, arquivos modificados, testes, branch, commit, URL de PR/issue e resposta do mantenedor. Se o caso for apenas configuracao, registrar o comando/config real e marcar `config-only` ou `viable-direct`, sem criar uma PR artificial.
Antes de publicar uma contribuicao, aplicar o gate e o checklist de
`05-plano-publicacao-prs-upstream.md`.

View File

@@ -0,0 +1,659 @@
# Plano de publicacao de integracoes OmniRoute nos repositorios upstream
> **Status da campanha de pesquisa:** `104/104` casos concluídos. Este plano continua sendo o procedimento de execução e publicação. A matriz final, inclusive os casos em que PR é inadequada ou impossível, está em `06-relatorio-final-104-clis-e-estrategia-prs.md`.
**Data:** 2026-08-01
**Escopo:** transformar a fila `CLI-000` a `CLI-103` em contribuicoes upstream verificadas,
publicando PR, issue, guia de configuracao, adaptador ou conclusao de bloqueio conforme o mecanismo
real de cada projeto.
**Documentos-base:** `01-relatorio-pesquisa-clis-omniroute.md`,
`02-prioridade-integracoes-clis.md`, `03-plano-integracao-em-lotes.md` e
`04-tracker-integracoes-clis.md`.
## 1. Resultado esperado
Para cada repositorio pesquisado, a campanha deve produzir exatamente um resultado principal:
1. **PR upstream de integracao nominal:** adiciona provider/preset `omniroute`, configuracao,
documentacao e testes quando isso combina com a arquitetura do projeto.
2. **PR upstream de compatibilidade generica:** melhora suporte a endpoint customizado sem acoplar
o projeto ao nome OmniRoute, acompanhado de documentacao comprovando o uso com OmniRoute.
3. **PR somente de documentacao:** registra uma configuracao funcional quando o codigo ja suporta
OmniRoute e o upstream aceita guias de terceiros.
4. **Issue-first:** solicita decisao de arquitetura ou permissao antes do patch quando a politica do
repositorio, o desenho de providers ou o tamanho da mudanca exigirem alinhamento.
5. **Configuracao sem PR:** documenta no OmniRoute um fluxo que ja funciona e para o qual uma mudanca
upstream seria redundante ou rejeitada pela politica do projeto.
6. **Adaptador ACP/MCP/wrapper:** contribui no ponto de extensao correto quando o projeto nao consome
diretamente APIs de modelos.
7. **MITM, produto fechado ou bloqueado:** registra evidencia e nao fabrica uma contribuicao que o
upstream nao pode receber.
O objetivo e tentar integrar todos os casos tecnicamente possiveis. O objetivo nao e abrir uma PR em
todo repositorio independentemente da arquitetura, licenca ou politica de contribuicao.
## 2. Regras da campanha
- Trabalhar em lotes de no maximo tres repositorios, com um subagente por repositorio.
- Usar uma worktree isolada por repositorio dentro de `.claude/worktrees/`.
- Nao editar implementacoes no checkout compartilhado.
- Nao usar `git stash` ou `git pop`.
- Fazer pesquisa fresca no commit atual do upstream antes de criar branch ou editar arquivos.
- Ler `README`, `CONTRIBUTING`, templates de issue/PR, `SECURITY`, licenca e instrucoes locais de
agentes antes da implementacao.
- Procurar issues e PRs abertas/fechadas sobre custom provider, base URL, OpenAI-compatible,
Anthropic-compatible, Gemini endpoint, proxy, gateway e OmniRoute antes de propor uma mudanca.
- Registrar a base pesquisada por commit SHA ou release. Nao usar apenas `main` como evidencia.
- Executar baseline antes da mudanca e distinguir falhas preexistentes de regressao.
- Nunca expor `OMNIROUTE_API_KEY` ou qualquer outra credencial em comandos publicados, fixtures,
logs, commits, screenshots, PRs ou issues.
- Nao inserir trailers, assinaturas ou rodapes de IA em commits, PRs ou issues.
- Nao afirmar que uma integracao funciona sem um teste reproduzivel ou uma limitacao explicitamente
registrada.
- Nao inventar fork, branch, commit, PR, issue, CI ou resposta de mantenedor.
- Atualizar `04-tracker-integracoes-clis.md` ao concluir cada fase material.
## 3. Unidade de trabalho por repositorio
Cada item `CLI-NNN` deve possuir uma task individual. A task e o pacote de contexto entregue ao
subagente e o registro que permite retomar o trabalho sem repetir ou perder evidencias.
### 3.1 Cabecalho obrigatorio da task
```md
# CLI-NNN - <projeto> - integracao OmniRoute upstream
- Repositorio canonico: <URL>
- Prioridade/lote: <P0-P4 / lote>
- Estado no catalogo OmniRoute: <integrated/not-in-catalog/parcial>
- Evidencia inicial: <resumo vindo do relatorio; ainda nao confirmado>
- Worktree: <caminho isolado>
- Branch planejada: <definir somente depois de ler as regras upstream>
- Commit/release pesquisado: —
- Responsavel: <agente>
- Estado: researching
```
### 3.2 Pesquisa obrigatoria dentro da task
O subagente deve responder, com links e caminhos de codigo:
1. Qual e o repositorio canonico, commit/release atual, licenca e nivel de atividade?
2. Contribuicoes de forks externos sao aceitas? Ha CLA, DCO, sign-off ou issue previa obrigatoria?
3. Qual e a arquitetura de providers e qual e o menor ponto de extensao?
4. O cliente usa Chat Completions, Responses, Anthropic Messages, Gemini, ACP, MCP ou protocolo
proprietario?
5. A base URL esperada e raiz, `/v1`, `/v1beta` ou uma URL completa por operacao?
6. O cliente acrescenta algum sufixo automaticamente? Pode duplicar `/v1` ou `/v1beta`?
7. Como a autenticacao e resolvida: variavel de ambiente, arquivo, keyring, OAuth ou header custom?
8. Como os modelos sao definidos ou descobertos? O cliente chama um endpoint de modelos?
9. Streaming, tool calling, reasoning, imagens e cancelamento funcionam pelo caminho escolhido?
10. Ja existe issue, PR, discussao ou documentacao para endpoints customizados ou OmniRoute?
11. Quais comandos oficiais executam install, format, lint, typecheck, build e testes?
12. Qual contribuicao agrega valor real: codigo nominal, compatibilidade generica, docs, issue,
wrapper, MCP/ACP, somente configuracao ou nenhum patch?
### 3.3 Gate de contribuicao
Antes de editar, preencher uma decisao:
| Decisao | Quando usar | Saida esperada |
|---|---|---|
| `pr-provider` | O upstream possui catalogo/presets de providers | Provider/preset OmniRoute, docs e testes |
| `pr-generic` | Falta uma capacidade generica necessaria, como base URL customizavel | Patch generico, docs e teste com OmniRoute |
| `pr-docs` | O codigo ja funciona e o upstream aceita guias de integracao | Guia minimo e validado |
| `issue-first` | Mudanca arquitetural, politica incerta ou mantenedor exige proposta | Issue com evidencia e desenho do patch |
| `config-only` | Tudo funciona por configuracao e um PR seria redundante | Guia no OmniRoute e smoke test |
| `adapter-acp` | ACP e o ponto real de integracao | Adaptador/registro ACP e testes |
| `adapter-mcp` | MCP e o ponto real de integracao | Config/servidor MCP e testes |
| `wrapper` | O projeto apenas lanca outro agente | Wrapper/env forwarding e teste do filho |
| `needs-mitm` | Endpoint fechado ou fixo | Pesquisa/guia MITM separado; sem PR artificial |
| `blocked` | Licenca, politica, build ou protocolo impedem progresso | Evidencia reproduzivel e proximo desbloqueio |
O gate deve incluir a alternativa rejeitada. Exemplo: `pr-provider` escolhido porque o repositorio
mantem presets nomeados; `pr-docs` rejeitado porque a configuracao exigiria cinco campos internos e
nao seria uma experiencia suportada.
## 4. Ciclo completo da PR
### Fase PR-0 - Preparar o contexto
- Reservar o item no tracker e marcar pesquisa em andamento.
- Confirmar que nenhum outro agente esta trabalhando no mesmo repositorio.
- Resolver o repositorio canonico, fork existente e permissao de contribuicao.
- Criar a task individual com a evidencia inicial marcada como hipotese.
- Criar a worktree isolada somente depois de confirmar o upstream correto.
### Fase PR-1 - Pesquisar upstream e contribuicoes existentes
- Ler integralmente as regras do repositorio aplicaveis aos arquivos que podem mudar.
- Mapear provider registry, configuracao, transporte HTTP, auth, modelo, streaming e ferramentas.
- Pesquisar issues/PRs por termos de compatibilidade e pelo nome OmniRoute.
- Registrar commit/release, caminhos e links de evidencia na task.
- Escolher o gate de contribuicao da secao 3.3.
### Fase PR-2 - Baseline reproduzivel
- Instalar dependencias de acordo com o upstream.
- Rodar format check, lint, typecheck/build e testes relevantes antes do patch.
- Rodar um smoke test do caminho existente, mesmo que ele falhe por falta da integracao.
- Limpar chaves do ambiente nos testes que validem o comportamento sem credenciais.
- Registrar comando, codigo de saida, testes aprovados e falhas preexistentes.
- Se o projeto nao puder ser construido, tentar o ambiente documentado e registrar o bloqueio; nao
declarar regressao nem compatibilidade com base apenas na leitura do README.
### Fase PR-3 - Desenhar o menor patch aceitavel
A ordem de preferencia e:
1. Reusar a abstracao de provider ja existente.
2. Adicionar metadados/preset antes de criar codigo especial.
3. Reusar cliente OpenAI/Anthropic/Gemini ja presente.
4. Adicionar capacidade generica quando ela beneficiar outros gateways e for coerente com o projeto.
5. Criar executor/adapter dedicado somente quando o protocolo realmente divergir.
O patch normalmente deve cobrir:
- identificador e nome de exibicao `omniroute`, se presets nomeados forem aceitos;
- base URL correta e sem dupla concatenacao de versao;
- chave obtida de ambiente ou storage seguro;
- configuracao/descoberta de modelo;
- headers estritamente necessarios;
- streaming e tool calling preservados;
- mensagens de erro sem expor segredo;
- documentacao curta e executavel;
- testes unitarios/integracao alinhados ao padrao upstream.
Nao adicionar telemetria, dependencia, fluxo de login ou codigo de rede novo quando o provider
generico existente ja resolve o caso.
### Fase PR-4 - Implementar com teste primeiro
- Criar teste que demonstre a ausencia do preset, config ou comportamento requerido.
- Confirmar a falha pelo motivo esperado.
- Implementar o menor patch.
- Fazer o teste passar e executar testes adjacentes.
- Refatorar apenas o necessario para manter o padrao do upstream.
- Formatar somente os arquivos tocados, salvo exigencia contraria do repositorio.
Para PR somente de documentacao, substituir o teste vermelho por uma validacao real dos comandos e
do arquivo de configuracao documentado. Nao sintetizar exemplos que nao foram executados.
### Fase PR-5 - Validar contra OmniRoute
Escolher a matriz compativel com o cliente:
| Superficie | Base inicial esperada | Validacoes minimas |
|---|---|---|
| OpenAI Chat Completions | confirmar se o cliente espera raiz ou `/v1` | chamada simples, stream, tool call, erro de modelo |
| OpenAI Responses | confirmar regra de concatenacao do cliente | resposta simples, stream/eventos, tool call |
| Anthropic Messages | normalmente base antes de `/v1/messages`; confirmar no codigo | mensagem, stream, tools, headers de versao |
| Gemini | normalmente base antes das operacoes `v1beta`; confirmar no codigo | generateContent, streamGenerateContent, tools |
| ACP | endpoint/transport definido pelo protocolo | discovery, sessao, request e cancelamento |
| MCP | stdio, SSE ou Streamable HTTP conforme suporte | inicializacao, listagem e invocacao de ferramenta |
Registrar no resultado quais linhas da matriz foram executadas, omitidas ou bloqueadas. Um smoke
test simples nao deve ser apresentado como prova de tool calling ou streaming.
### Fase PR-6 - Revisar o diff antes de publicar
O agente responsavel faz uma auto-revisao e o agente principal verifica:
- aderencia a `CONTRIBUTING` e instrucoes locais;
- escopo minimo e ausencia de refactor oportunista;
- testes cobrindo config, URL, auth sem segredo e modelo;
- documentacao consistente com o codigo executado;
- ausencia de arquivos gerados, caches, logs ou credenciais;
- licenca e atribuicao preservadas;
- branch baseada no upstream atual;
- commits pequenos e com mensagem no estilo do projeto;
- ausencia de trailers ou texto de IA;
- `git diff --check` e gates oficiais limpos, ou falhas preexistentes documentadas.
Uma PR nao deve ser publicada enquanto houver alteracao sem explicacao, teste essencial faltando ou
duvida material sobre a politica do upstream.
### Fase PR-7 - Preparar a publicacao
- Confirmar fork e remotes sem sobrescrever branches existentes.
- Atualizar a branch sobre o ponto exigido pelo upstream usando operacao nao destrutiva.
- Enviar a branch ao fork somente depois da revisao.
- Criar PR contra a branch correta do repositorio canonico.
- Se a contribuicao externa estiver bloqueada, abrir issue-first e anexar o commit/patch de
referencia somente quando isso for permitido.
- Registrar URLs reais no tracker imediatamente apos a publicacao.
Convencoes de branch sugeridas, sujeitas ao padrao de cada upstream:
- `feat/omniroute-provider` para provider/preset nominal;
- `feat/custom-base-url` para capacidade generica;
- `docs/omniroute-setup` para documentacao validada;
- `fix/custom-endpoint-versioning` para correcao de raiz versus `/v1`/`/v1beta`.
### Fase PR-8 - Corpo da PR
Usar o template oficial do repositorio quando existir. Na ausencia de template, adaptar:
```md
## Why
Explain the user problem and the existing extension point. Avoid marketing claims.
## What changed
- Add or enable the smallest provider/configuration path required.
- Document the verified setup.
- Cover URL, authentication and model selection behavior with tests.
## Verification
- `<official upstream command>`
- `<focused test command>`
- `<sanitized OmniRoute smoke test and result>`
## Compatibility notes
- API surface: `<Chat Completions/Responses/Anthropic/Gemini/ACP/MCP>`
- Base URL rule: `<root, /v1, /v1beta or full operation URL>`
- Streaming: `<verified/not applicable/not verified>`
- Tool calling: `<verified/not applicable/not verified>`
## Scope
No unrelated refactors or credential changes.
```
O titulo deve descrever a mudanca, nao a campanha. Exemplos de formato, sujeitos ao estilo do
upstream: `Add OmniRoute provider preset`, `Support configurable OpenAI-compatible base URLs` ou
`Document OmniRoute as a custom endpoint`.
### Fase PR-9 - Issue-first ou fallback
Quando uma PR direta nao for apropriada, a issue deve conter:
- problema reproduzivel e publico afetado;
- ponto de extensao encontrado no codigo;
- proposta minima;
- compatibilidade esperada e protocolo;
- evidencia de teste ou prototipo;
- pergunta objetiva ao mantenedor;
- link para patch de referencia apenas se permitido.
Nao abrir simultaneamente issue e PR sem necessidade. Se o template exigir issue previa, esperar a
decisao ou seguir a politica declarada.
### Fase PR-10 - Acompanhar ate a decisao
Depois da publicacao:
- observar CI e checks obrigatorios;
- responder perguntas tecnicas com evidencia;
- corrigir somente o escopo da contribuicao ou pedidos claros do mantenedor;
- reexecutar testes depois de cada mudanca;
- registrar novos commits, revisoes e estado no tracker;
- marcar `accepted` somente depois de merge/aceite comprovado;
- marcar `rejected` com o motivo fornecido pelo upstream;
- se a PR ficar inativa, registrar `awaiting-maintainer`, sem declarar abandono prematuramente;
- manter o guia/catalogo OmniRoute coerente com o estado real do upstream.
O acompanhamento pode usar a skill `babysit` individualmente para uma PR aberta. Como essa skill
acompanha uma unica PR, nunca agrupar tres PRs em uma mesma execucao dela.
### Fase PR-11 - Fechar a task
Uma task individual termina com:
- pesquisa fresca e gate registrados;
- diff, configuracao ou bloqueio documentado;
- baseline e validacao final comparados;
- branch/commit reais, quando criados;
- PR/issue reais, quando publicados;
- status no catalogo OmniRoute;
- limitacoes e proximo passo;
- linha correspondente no tracker atualizada.
## 5. Estrategia de paralelizacao
### 5.1 Papeis por lote
- **Subagente A:** primeiro repositorio do lote; dono exclusivo da worktree e do diff upstream.
- **Subagente B:** segundo repositorio do lote; dono exclusivo da worktree e do diff upstream.
- **Subagente C:** terceiro repositorio do lote; dono exclusivo da worktree e do diff upstream.
- **Agente principal:** coordena o tracker, revisa gates/diffs, impede duplicacao e autoriza a
publicacao depois das evidencias.
Todos os agentes devem ser avisados de que nao estao sozinhos no workspace e nao podem reverter ou
sobrescrever mudancas de outros agentes.
### 5.2 Barreira do lote
O lote seguinte pode comecar quando os tres itens atuais tiverem, no minimo:
1. commit/release upstream pesquisado;
2. gate de contribuicao definido;
3. baseline registrado;
4. patch validado, configuracao comprovada ou bloqueio reproduzivel;
5. decisao de publicacao tomada;
6. tracker atualizado.
A espera por resposta de mantenedor nao bloqueia o lote seguinte. Depois de uma PR/issue publicada,
o item passa para acompanhamento e libera o slot de implementacao.
### 5.3 Limite de trabalho em progresso
- No maximo tres pesquisas/implementacoes ativas.
- Publicacoes aguardando mantenedor nao contam como slot de implementacao, mas ficam no tracker.
- No maximo uma task ativa por repositorio, inclusive forks ou variantes do mesmo upstream.
- Se dois itens resolverem o mesmo repositorio, consolidar a pesquisa e decidir se ha uma ou duas
contribuicoes antes de abrir branches.
## 6. Fila de publicacao
A ordem detalhada continua sendo a do `03-plano-integracao-em-lotes.md`. Esta secao define o objetivo
de publicacao de cada onda; a pesquisa individual pode promover, rebaixar ou mudar o tipo de
contribuicao.
### Onda 0 - referencia e infraestrutura da campanha
- `CLI-000` jcode: acompanhar issue upstream e PR de referencia; concluir a secao prometida no
README do OmniRoute.
- Preparar o modelo de task individual e aplicar o mesmo tracker a todos os novos repositorios.
### Onda 1 - P0.1 a P0.5
- `CLI-001` Gemini CLI: confirmar se o endpoint Gemini customizado pede apenas docs/config ou um
preset nominal.
- `CLI-002` Claw Code: confirmar provider OpenAI-compatible e propor preset/docs minimos.
- `CLI-003` Plandex: confirmar o registro de providers customizados e propor provider/preset.
- `CLI-004` MiMo Code: confirmar o adapter OpenAI-compatible e propor configuracao/provider.
- `CLI-005` Trae Agent: confirmar `model_providers` e propor entrada OmniRoute/documentacao.
- `CLI-006` Kimi CLI: escolher uma superficie suportada e evitar um patch que misture tres
protocolos sem testes.
- `CLI-007` Every Code: reutilizar a arquitetura herdada do Codex quando ainda aplicavel.
- `CLI-008` Open Codex: confirmar upstream canonico e propor provider multi-modelo.
- `CLI-009` VT Code: validar provider customizado, modelo e failover.
- `CLI-010` OpenHands CLI: verificar se `LLM_BASE_URL` torna o caso docs/config-only.
- `CLI-011` gptme: verificar se `OPENAI_BASE_URL` torna o caso docs/config-only.
- `CLI-012` Nanocoder: confirmar compatibilidade de tool calling e decidir preset versus docs.
- `CLI-013` RA.Aid: verificar se `OPENAI_API_BASE` torna o caso docs/config-only.
- `CLI-014` CoreCoder: verificar se `OPENAI_BASE_URL` torna o caso docs/config-only.
- `CLI-015` Grok CLI: confirmar se o endpoint e genericamente configuravel ou preso ao protocolo
Grok antes de propor patch.
### Onda 2 - P1.1 a P1.9
- `CLI-016` Gitlawb Zero: provider custom/flag; preferir docs ou preset pequeno.
- `CLI-017` DeepSeek Reasonix: confirmar repositorio, atividade e endpoint antes de qualquer PR.
- `CLI-018` KlaatCode: integrar via `customModels` ou preset se o catalogo aceitar nomes.
- `CLI-019` CodeMini CLI: validar `gateway.base_url` e sua regra de versao.
- `CLI-020` Zot: validar `--base-url` e `models.json`; docs-first se ja suficiente.
- `CLI-021` Octomind: confirmar variaveis de URL por provider e propor configuracao minima.
- `CLI-022` DvalinCode: confirmar o cliente OpenAI-compatible e testes disponiveis.
- `CLI-023` Coro Code: confirmar `OPENAI_BASE_URL`; docs-first se nao houver lacuna de codigo.
- `CLI-024` Mini-Kode: confirmar `MINIKODE_BASE_URL`; docs-first se nao houver lacuna de codigo.
- `CLI-025` Late CLI: testar ambiente e flag `api-url`; corrigir precedencia apenas se necessario.
- `CLI-026` Agentty: escolher entre provider direto e ACP conforme a arquitetura atual.
- `CLI-027` Aizen: validar `AIZEN_BASE_URL` e propor docs/preset.
- `CLI-028` Clif-Code: selecionar um unico protocolo principal para a primeira contribuicao.
- `CLI-029` Minacode: pesquisa confirmatoria antes de definir o tipo de PR.
- `CLI-030` YottaCode: confirmar gateway/provider e selecao de modelo.
- `CLI-031` aichat: integrar via configuracao de modelos ou provider nominal, conforme a politica.
- `CLI-032` ShellGPT: validar `API_BASE_URL` e decidir docs/config-only.
- `CLI-033` Mistral Vibe: confirmar base URL customizada e separar suporte generico de marca.
- `CLI-034` OpenSquilla: localizar o registro de gateways e propor provider/preset.
- `CLI-035` Kode CLI: escolher OpenAI, Anthropic ou Gemini com base na implementacao mais nativa.
- `CLI-036` Neovate Code: preferir plugin/provider oficial ao patch no core, se existir.
- `CLI-037` Deep Agents Code: contribuir no pacote CLI/provider correto, nao apenas no SDK generico.
- `CLI-038` OpenHands principal: evitar duplicar `CLI-010`; consolidar se ambos apontarem para o
mesmo mecanismo e upstream.
- `CLI-039` SWE-agent: confirmar backend de modelos e interface publica suportada.
- `CLI-040` AutoCodeRover: confirmar backend e propor config/provider minimo.
- `CLI-041` Claurst: revisar GPL e politica antes de redistribuir qualquer adaptacao.
- `CLI-042` Codebuff: confirmar se o provider e extensivel e se contribuicoes externas sao aceitas.
### Onda 3 - P2.1 a P2.11
- `CLI-043` Devon, `CLI-044` Letta Code e `CLI-045` CodeMachine CLI: pesquisar backend real;
revisar a entrada local ja existente de Letta antes de nova PR.
- `CLI-046` Groq Code CLI, `CLI-047` Dexto e `CLI-048` claw-code-agent: confirmar endpoints,
protocolos e maturidade antes do patch.
- `CLI-049` g3, `CLI-050` San e `CLI-051` Waveloom: localizar a abstracao de provider e preferir
implementacao generica.
- `CLI-052` picocode, `CLI-053` QQCode e `CLI-054` Keen Code: validar configuracao multi-modelo e
documentar o caminho minimo.
- `CLI-055` Grinta, `CLI-056` Zap e `CLI-057` Binharic: escolher o provider compativel com melhor
cobertura de streaming/tools.
- `CLI-058` Darce, `CLI-059` CLAII e `CLI-060` nori-cli: separar integracao de modelo de MCP e de
codigo herdado do Codex.
Resultado P2.6:
- `CLI-058` Darce: `config-only`, sem PR necessária; usar `DARCE_API_BASE` na raiz e `DARCE_MODEL`.
- `CLI-059` CLAII: patch genérico local validado, mas publicação bloqueada pela declaração upstream
`All Rights Reserved`/ausência de licença OSS; só reconsiderar com autorização jurídica explícita.
- `CLI-060` nori-cli: `config-only` via agente ACP customizado OpenCode; não alterar backend Codex;
MCP deve ser configurado uma vez, em Nori ou OpenCode, para evitar duplicação de tools.
- `CLI-061` cursor-agent clone, `CLI-062` Free Code e `CLI-063` Claude Engineer: revisar origem,
licenca e politica do fork antes de publicar.
Lote P2.7 reservado em 2026-08-02, na branch-base local `release/v3.8.50` em
`35405be6020696a7c66158ea7a25f06d61ff88ff`. Os três upstreams foram clonados em worktrees
separadas, indexados e delegados. Nenhuma publicação está autorizada; patches só podem surgir após
prova RED→GREEN e permanecem sem commit até revisão central.
Resultado P2.7:
- `CLI-061` cursor-agent clone: `config-only`; OpenAI usa base com `/v1`, Anthropic usa raiz sem
`/v1`; tools/tool-result foram comprovados nos dois protocolos. O factory rejeita `auto` puro,
mas isso não impede uso com modelos reconhecíveis ou classes diretas. Sem PR.
- `CLI-062` Free Code: `config-only` com `ANTHROPIC_BASE_URL` na raiz e `model=auto`; stream,
tools/tool-result e MCP nativo foram comprovados. O repo canônico agora é `freecodexyz/free-code`,
mas não há licença e o README atribui o código à Anthropic; publicação bloqueada.
- `CLI-063` Claude Engineer: endpoint/chave funcionam como `config-only` com modelo fixo. A lacuna
de `ANTHROPIC_MODEL` já está coberta pela PR #250; não criar patch concorrente. Arquivo de licença
segue ausente apesar da issue #116, portanto publicação permanece bloqueada.
- `CLI-064` Smol Developer, `CLI-065` Agentless e `CLI-066` Amazon Q Developer CLI: decidir entre
SDK/adaptador, config de modelo ou bloqueio por autenticacao.
Lote P2.8 iniciado em 2026-08-02 na branch-base local `release/v3.8.50`, SHA
`35405be6020696a7c66158ea7a25f06d61ff88ff`, com clones limpos e separados. Smol Developer será
testado primeiro como integração do SDK OpenAI legado; Agentless será avaliado por backend
OpenAI/Anthropic/DeepSeek; Amazon Q Developer CLI será tratado como protocolo AWS próprio, com MCP
avaliado separadamente. Não criar adaptador grande para Amazon Q nem qualquer publicação antes de
issue-first/coordenação exigida por `CONTRIBUTING.md`. Estado inicial: nenhum commit, fork, push,
PR, issue ou Discussion.
Resultado P2.8:
- `CLI-064` Smol Developer: `config-only`; `OPENAI_API_BASE` com `/v1` e `model=auto` passaram no
CLI, biblioteca e Agent Protocol histórico. Não há lacuna provider-specific e a PR #134 já cobre
uma expansão LiteLLM. Sem publicação.
- `CLI-065` Agentless: `config-only` pelo backend OpenAI, incluindo embeddings. Anthropic normal
também funciona; cache/tools exige SDK histórico e DeepSeek possui host fixo, mas essas melhorias
não são necessárias para integrar o projeto e propostas LiteLLM anteriores foram fechadas. Sem
publicação.
- `CLI-066` Amazon Q Developer CLI: MCP stdio é a integração direta; o backend de modelo fala AWS
JSON/EventStream e precisa de wrapper/backend novo. O upstream está em manutenção crítica e exige
issue-first; não preparar PR nominal ou adaptador surpresa. Sem publicação.
Estado final P2.8: commits `0`, pushes `0`, forks `0`, PRs `0`, issues `0`, Discussions `0`.
Próxima fila: P2.9 (`CLI-067` nanobot, `CLI-068` ZeroClaw, `CLI-069` NanoClaw), usando no máximo
três worktrees/agentes e repetindo a pesquisa individual antes de qualquer patch.
Lote P2.9 iniciado em 2026-08-03 sobre a branch-base local `release/v3.8.50`, SHA
`84b1e5e12f238269e698f400766230f985f4a07b`. O checkout principal já continha uma alteração do
operador em `CLAUDE.md`, preservada fora do escopo. As worktrees foram recriadas e os upstreams
foram clonados nos HEADs `44b7e1bf4` (nanobot), `4770420ab` (ZeroClaw) e `dfac7e0af` (NanoClaw).
Os três índices Codebase Memory moderate estão ready, sem skipped, e a pesquisa foi delegada a um
agente por repositório. Nenhuma publicação está autorizada; o estado inicial continua: commits `0`,
pushes `0`, forks `0`, PRs `0`, issues `0`, Discussions `0`.
- `CLI-067` nanobot, `CLI-068` ZeroClaw e `CLI-069` NanoClaw: validar providers OpenClaw/Anthropic
e evitar assumir que todos aceitam a mesma base URL.
Resultado P2.9:
- `CLI-067` nanobot: `config-only` pelo provider dinâmico OpenAI-compatible. A base correta inclui
`/api/v1`; `omniroute/auto` seleciona o provider custom e envia `auto` no wire. Chat, SSE, tools,
reasoning, usage, imagens, discovery e retry foram validados. Sem publicação upstream.
- `CLI-068` ZeroClaw: `config-only` pela família `custom`, com `uri=/v1`, modelo `auto`, wire Chat e
`native_tools=true`. Responses é opt-in. Suite de provider, config, fmt e smoke HTTP passaram.
Sem provider nominal ou publicação upstream.
- `CLI-069` NanoClaw: `config-only` pelo provider Claude existente, apontando a raiz Anthropic do
OmniRoute sem `/v1/messages` e usando OneCLI para a credencial. Codex e OpenCode têm bloqueios
upstream reproduzidos (#3155/#1984/#2985) e ficam fora do caminho de produção atual.
Estado final P2.9: commits `0`, pushes `0`, forks `0`, PRs `0`, issues `0`, Discussions `0`.
Progresso da pesquisa: `70/104` (`67,3%`); pendentes: `34/104` (`32,7%`). Próxima fila: P2.10
(`CLI-070` PicoClaw, `CLI-071` IronClaw, `CLI-072` NullClaw).
- `CLI-070` PicoClaw, `CLI-071` IronClaw e `CLI-072` NullClaw: localizar traits/registries e propor
um provider pequeno com testes.
- `CLI-073` Moltis, `CLI-074` GitClaw e `CLI-075` LionClaw: confirmar atividade, provider e comandos
de validacao antes da publicacao.
### Onda 4 - P3, integracoes indiretas
- `CLI-076`, `CLI-077`, `CLI-078`, `CLI-079`, `CLI-080` e `CLI-081`: pesquisar forwarding de
ambiente/configuracao para os agentes filhos;
publicar wrapper ou docs somente quando houver um ponto de extensao real.
- `CLI-082`, `CLI-083`, `CLI-084`, `CLI-085`, `CLI-086`, `CLI-087`, `CLI-088`, `CLI-089` e
`CLI-090`: escolher ACP, MCP, launcher ou integracao do agente filho; nao apresentar uma
integracao de orquestrador como provider de modelo.
- `CLI-091` e `CLI-092`: tratar como interoperabilidade entre proxies; documentar loops, headers,
auth e riscos antes de propor codigo.
- `CLI-093` e `CLI-094`: integrar como broker/ferramenta MCP somente se isso estiver no escopo dos
projetos.
- `CLI-095` e `CLI-096`: configurar o agente filho e revisar a entrada existente de Agent Deck.
### Onda 5 - P4, fechados, EULA e MITM
- `CLI-097` Pool: confirmar o que a EULA permite; priorizar configuracao local e nao presumir PR.
- `CLI-098` Junie CLI: pesquisar canal oficial de feedback; sem repositorio publico confirmado, nao
existe fila de PR.
- `CLI-099` Cursor desktop, `CLI-100` Windsurf, `CLI-101` Amp, `CLI-102` Amazon Q/Kiro CLI e
`CLI-103` Cowork: tratar como MITM, configuracao de produto ou pedido oficial de feature. So mover
para PR se um repositorio publico e uma politica de contribuicao forem comprovados.
## 7. Prompt operacional para cada subagente
O agente principal deve adaptar e enviar este prompt para cada item:
```text
Voce e responsavel exclusivamente por CLI-NNN - <projeto> no repositorio <URL>.
Voce nao esta sozinho no workspace: nao reverta, sobrescreva ou reorganize mudancas de outros
agentes. Trabalhe somente na worktree isolada atribuida dentro de .claude/worktrees/ e nunca use
git stash/pop.
Primeiro pesquise o upstream atual. Leia README, CONTRIBUTING, licenca, templates e instrucoes locais.
Registre commit/release, arquitetura de providers, config/base URL, protocolo, auth, modelos,
streaming, tool calling, issues/PRs existentes e comandos oficiais de build/test. A evidencia inicial
do relatorio e uma hipotese, nao uma conclusao.
Antes de editar, classifique o caso como pr-provider, pr-generic, pr-docs, issue-first, config-only,
adapter-acp, adapter-mcp, wrapper, needs-mitm ou blocked, com justificativa. Execute o baseline e
registre falhas preexistentes. Se houver patch, trabalhe com teste primeiro e implemente somente a
menor integracao coerente com o upstream. Confirme raiz versus /v1 versus /v1beta, autenticacao,
modelo, streaming e tool calling conforme aplicavel.
Nao publique nada antes da revisao do agente principal. Entregue: pesquisa com links/caminhos,
gate, baseline, diff, testes, smoke test sanitizado, riscos, branch/commit local se criados e a
atualizacao proposta para 04-tracker-integracoes-clis.md. Nao invente dados e nao exponha chaves.
```
## 8. Checklist de autorizacao para enviar uma PR
O agente principal somente autoriza a publicacao quando todas as respostas forem `sim` ou houver
uma excecao registrada:
- [ ] O repositorio canonico e a branch-alvo foram confirmados.
- [ ] A politica aceita o tipo de contribuicao planejado.
- [ ] Issues/PRs duplicadas foram pesquisadas.
- [ ] O commit/release de base esta registrado.
- [ ] O gate de contribuicao esta justificado.
- [ ] O baseline foi executado e falhas preexistentes estao separadas.
- [ ] O patch e o menor necessario e segue a arquitetura upstream.
- [ ] A base URL e sua regra de versao foram verificadas no codigo e em runtime.
- [ ] Auth/modelos foram testados sem vazar segredo.
- [ ] Streaming/tool calling foram testados ou marcados explicitamente como nao aplicaveis.
- [ ] Testes, lint, format, typecheck/build relevantes foram executados.
- [ ] A documentacao foi executada e corresponde ao codigo.
- [ ] O diff nao contem caches, builds, logs, credenciais ou refactors sem relacao.
- [ ] O titulo e o corpo seguem o template upstream e nao contêm marketing ou texto de IA.
- [ ] O tracker esta pronto para receber branch, commit e URL reais.
## 9. Campos adicionais recomendados no tracker
O tracker atual deve continuar como fonte principal. Durante a execucao, registrar nas observacoes ou
em uma nota individual:
- commit/release pesquisado;
- decisao `pr-provider`, `pr-generic`, `pr-docs`, `issue-first`, `config-only`, adapter, wrapper,
MITM ou bloqueio;
- protocolo e regra da base URL;
- comandos de baseline e resultado;
- comandos finais e resultado;
- smoke tests realizados;
- arquivos modificados;
- fork, branch e commit;
- PR/issue e estado de CI/review;
- limitacoes e proximo passo.
Campos ainda nao comprovados permanecem `—`.
## 10. Inicio recomendado
O primeiro ciclo de publicacao deve usar o lote P0.1:
1. `CLI-001` - Gemini CLI (`google-gemini/gemini-cli`)
2. `CLI-002` - Claw Code (`ultraworkers/claw-code`)
3. `CLI-003` - Plandex (`plandex-ai/plandex`)
Os tres subagentes fazem pesquisa fresca e implementacao em paralelo, mas nenhuma PR e enviada antes
da revisao individual do agente principal. Ao publicar ou concluir config-only/bloqueio, atualizar o
tracker e liberar os mesmos tres slots para o lote P0.2.
## Lote P2.10 iniciado em 2026-08-03
Base local: `release/v3.8.50` em `84b1e5e12f238269e698f400766230f985f4a07b`. Worktrees isoladas e um agente por upstream foram criadas para `CLI-070` PicoClaw, `CLI-071` IronClaw e `CLI-072` NullClaw. Nenhuma publicação está autorizada; os agentes devem pesquisar o HEAD atual, provar `config-only` ou RED→GREEN e registrar governança, gates, smoke e estado limpo.
Resultado P2.10:
- `CLI-070` PicoClaw: `config-only`, `openai/auto` com base `/api/v1`; Chat/SSE/tools/usage/images/discovery. Go ausente impediu execução local; monitorar #3298, sem PR.
- `CLI-071` IronClaw: `config-only`, `openai_compatible` com `/api/v1` e `auto`; 889 testes do crate LLM, 5 de resolução e fmt passaram. Sem PR; reasoning proprietário segue limitado por #3673.
- `CLI-072` NullClaw: `config-only`, provider custom com Chat Completions recomendado e Responses/Anthropic como alternativas. Zig ausente; CI do mesmo HEAD verde. Sem PR.
Estado final P2.10: commits `0`, pushes `0`, forks `0`, PRs `0`, issues `0`, Discussions `0`.
Pesquisa acumulada: `73/104` (`70,2%`); pendentes: `31/104` (`29,8%`). Próxima fila: P2.11 (`CLI-073` Moltis, `CLI-074` GitClaw, `CLI-075` LionClaw).
Resultado P3.1:
- `CLI-076` VibePod: `config-only` pelo agente Claude Code com raiz Anthropic `/api`; wrapper injeta env no container. Codex sem chave automática permanece não comprovado.
- `CLI-077` zeroshot: `config-only` pelo gateway OpenAI `/api/v1`; 22 testes focados verdes; limitações de streaming JSON, reasoning e MCP registradas.
- `CLI-078` Fractal: `config-only` por Codex Responses em `CODEX_HOME` por node; servidores tmux quentes podem perder `OMNIROUTE_API_KEY`, recomendando fix genérico upstream.
Estado final P3.1: commits `0`, pushes `0`, forks `0`, PRs `0`, issues `0`, Discussions `0`. Pesquisa acumulada: `79/104` (`76,0%`); pendentes: `25/104` (`24,0%`).
Resultado P3.2: Bernstein `config-only` por openai_agents; Traycer `config-only` indireto pelo harness OpenCode; h5i `patch-required` porque auth proxy/egress são fixados em OpenAI. Nenhuma publicação externa. Pesquisa acumulada `82/104` (`78,8%`), pendentes `22/104` (`21,2%`).
Resultado P2.11:
- `CLI-073` Moltis: `config-only`, provider `custom-omniroute`, `/api/v1`, `auto`, Chat/SSE/tools e capacidades multimodais. 401 testes e fmt passaram. Sem publicação.
- `CLI-074` GitClaw/GitAgent: `config-only`, loader OpenAI-compatible com `GITAGENT_MODEL_BASE_URL`, `OPENAI_API_KEY` e `omniroute:auto`. Build, 65 testes e smoke passaram. Sem publicação.
- `CLI-075` LionClaw: `patch-required`/`issue-first`. O runtime Codex confinado não recebe `config.toml`/provider secret; preparar proposta genérica alinhada à [#157](https://github.com/moshthepitt/lionclaw/issues/157), sem PR até revisão do mantenedor.
Estado final P2.11: commits `0`, pushes `0`, forks `0`, PRs `0`, issues `0`, Discussions `0`. Pesquisa acumulada: `76/104` (`73,1%`); pendentes: `28/104` (`26,9%`).
Resultado P3.3: OMK `viable-mcp`; kodo `config-only` indireto; ORCH `needs-wrapper`. Pesquisa acumulada `85/104` (`81,7%`), pendentes `19/104` (`18,3%`). Nenhuma publicação externa.
Resultado P3.4: LoopTroop `config-only` indireto via provider OpenCode; Galley `patch-required` por não possuir transport OpenAI-compatible configurável; Relay `config-only` via provider profile/Codex, condicionado a smoke da Responses API e controles sobre ferramentas nativas. Nenhuma publicação externa. Pesquisa acumulada `88/104` (`84,6%`), pendentes `16/104` (`15,4%`).
Resultado P3.5: SageCLI `config-only` indireto via Codex, com caveat de env plaintext; 5dive `patch-required` por mapas fechados de provider/base; agx `config-only` indireto via Codex e com gates de Responses/sandbox. Pesquisa acumulada `91/104` (`87,5%`), pendentes `13/104` (`12,5%`). Nenhuma publicação externa.
Resultado P3.6: claude-code-router, cc-router e OneCLI são config-only; os dois primeiros oferecem endpoints custom OpenAI-compatible e OneCLI injeta credenciais por proxy MITM. Pesquisa acumulada `94/104` (`90,4%`), pendentes `10/104` (`9,6%`). Nenhuma publicação externa.
Resultado P3.7: agent-browser `config-only` direto por Chat Completions; OpenWork `config-only` via OpenCode custom; Agent Deck `config-only` via CLIs filhos. Pesquisa acumulada `97/104` (`93,3%`), pendentes `7/104` (`6,7%`). Nenhuma publicação externa.
Resultado P4.1: Pool e Junie são `config-only` OpenAI-compatible; Cursor é `config-only` limitado ao BYO chat panel, sem MITM/protocolo privado. Pesquisa acumulada `100/104` (`96,2%`), pendentes `4/104` (`3,8%`). Nenhuma publicação externa.
Resultado P4.2: Windsurf está bloqueado para inferência e permite apenas MCP; Amp depende de confirmação Enterprise; Amazon Q legado requer patch substancial e Kiro atual é MCP-only seguro. Pesquisa acumulada `103/104` (`99,0%`), pendente `1/104` (`1,0%`). Nenhuma publicação externa.
Resultado P4.3: Cowork não permite substituir oficialmente a inferência; Custom Connector MCP remoto é o único caminho suportado e permanece separado do modelo. Pesquisa concluída `104/104` (`100%`), pendentes `0/104` (`0%`). Nenhuma publicação externa nesta fase de pesquisa.

View File

@@ -0,0 +1,131 @@
# Relatório final — campanha de 104 integrações CLI OmniRoute
**Data de fechamento:** 2026-08-03
**Escopo:** `CLI-000` a `CLI-103`
**Resultado:** `104/104` pesquisados (`100%`), `0` pendentes de pesquisa.
## Como consultar o resultado individual
O documento autoritativo, com uma linha para cada caso, é o [tracker completo](./04-tracker-integracoes-clis.md). Ele contém para cada ID:
- prioridade;
- projeto e repositório;
- classificação de integração;
- estado de contribuição upstream;
- branch e commit quando existentes;
- URL de PR e/ou issue quando publicados;
- estado no catálogo OmniRoute;
- observações, limitações, testes e próximo passo.
Além do tracker, existem fichas técnicas individuais em `_tasks/cli-integrations/`. A cobertura foi auditada e agora há uma ficha para cada ID `CLI-000``CLI-103`; o caso `CLI-000` jcode foi adicionado como ficha de referência nesta revisão.
## Resumo quantitativo
| Grupo operacional | Quantidade | Tratamento |
|---|---:|---|
| Configuração direta ou indireta | 76 | Documentar receita, validar smoke e só abrir PR se houver melhoria upstream real |
| Contribuição upstream (PR/issue/docs/patch) | 17 | Preparar diff mínimo, validar, revisar e publicar conforme política do repositório |
| Patch obrigatório | 4 | Implementar genericamente, com RED→GREEN/TDD e revisão do mantenedor |
| Bloqueados/fechados | 4 | Registrar bloqueio; usar apenas MCP ou canal oficial, sem MITM |
| MCP/wrapper/ACP como caminho principal | 2 | Integrar a camada de ferramentas/orquestração, sem falsificar provider de inferência |
| Outros casos híbridos | 1 | Seguir a combinação específica descrita no tracker |
Os números são derivados do campo `Tipo` do tracker; categorias podem se sobrepor em casos híbridos. Atualmente há **7 PRs reais** e **9 issues reais** registrados no tracker, além de cinco entradas locais marcadas como integradas ao catálogo OmniRoute. Nenhum link foi inventado para os 97 casos sem publicação externa.
## O que foi feito na campanha
1. Inventário inicial e busca extensa de CLIs, runtimes, harnesses e control-planes.
2. Priorização P0P4 considerando compatibilidade de protocolo, adoção, licença, maturidade e risco.
3. Pesquisa fresca, uma a uma, em worktrees isoladas, em lotes de no máximo três agentes.
4. Uso de Codebase Memory para índices upstream e verificação de cobertura; faixas parciais foram lidas diretamente quando aplicável.
5. Classificação por configuração, patch, PR documental, issue-first, MCP, wrapper ou bloqueio.
6. Registro de comandos, base URL, autenticação, modelos, streaming, tools, reasoning, imagens, MCP/ACP/A2A, testes e limitações.
7. Consolidação de cada lote com commit separado no OmniRoute e no repositório `_tasks`.
8. Atualização final do tracker, plano de integração, plano de publicação e handoff.
9. Nenhuma credencial real, publicação externa ou técnica de interceptação não autorizada foi utilizada.
## Estratégia para abrir PRs em 100% dos casos
“Abrir PR para 100%” deve ser interpretado como **dar um destino upstream apropriado a 100% dos casos**, e não criar 104 PRs artificiais. Há quatro trilhas:
### Trilha A — PR de código ou documentação
Aplicar aos casos `viable-upstream`, `pr-generic`, `pr-docs`, `patch-required` e híbridos que tenham superfície pública e política de contribuição compatível.
Processo por caso:
1. Reconfirmar HEAD, licença, branch default, política de contribuição e duplicatas.
2. Criar worktree/branch baseada na versão local vigente.
3. Executar baseline upstream e registrar falhas preexistentes.
4. Escrever teste RED que demonstre a lacuna.
5. Implementar o menor patch genérico possível — preferir `openai-compatible`, `base_url` ou provider abstrato a um provider nominal OmniRoute.
6. Executar GREEN: testes focados, suite upstream, lint, format, typecheck/build e smoke com fake server ou OmniRoute local usando placeholder.
7. Revisar segurança: nenhuma chave em argv, logs, fixtures, URL ou artefato; erros sanitizados; streaming/tools/cancelamento cobertos.
8. Abrir PR somente se contribuições externas forem aceitas. O corpo deve explicar problema, solução genérica, compatibilidade, testes, limitações e não conter marketing/texto de IA.
9. Se o repositório bloquear fork/PR ou pedir discussão prévia, abrir issue de proposta com o mesmo patch/reprodução, sem enviar PR prematuramente.
10. Atualizar tracker com branch, commit, URL, CI, revisão e resposta do mantenedor; acompanhar até `accepted`, `merged`, `rejected` ou `awaiting-maintainer`.
### Trilha B — Issue-first, discussão ou suporte ao mantenedor
Aplicar quando a arquitetura é adequada, mas há bloqueio de governança, firewall, CLA, fork fechado, dúvida de protocolo ou necessidade de decisão do autor. A issue deve conter:
- caso de uso OmniRoute;
- configuração atualmente possível;
- lacuna reproduzível;
- proposta genérica;
- impacto de segurança;
- testes/fake server;
- disposição para enviar PR após aprovação.
Não abrir uma PR paralela enquanto a política exigir issue-first.
### Trilha C — Config-only documentado
Aplicar aos casos em que o upstream já suporta a integração e uma mudança de código seria redundante. O entregável é:
- ficha individual;
- receita validada;
- smoke test e limitações;
- eventual documentação externa/local do OmniRoute;
- issue somente se houver pedido de documentação ou descoberta de bug real.
Não criar provider nominal ou PR apenas para adicionar a palavra “OmniRoute”.
### Trilha D — MCP, wrapper ou bloqueio seguro
Aplicar a control-planes, produtos fechados e CLIs sem rota de inferência substituível. O resultado pode ser:
- MCP remoto/stdio do OmniRoute;
- wrapper local claramente identificado como wrapper;
- solicitação oficial de custom provider;
- registro de bloqueio e gate legal/ToS.
Nunca mascarar OmniRoute como Claude/Codex, falsificar executável, interceptar TLS ou reutilizar tokens privados para fabricar uma PR upstream.
## Ordem recomendada de execução
1. **Primeiro:** PRs e issues já preparadas ou com alto retorno e baixo risco — jcode, Gemini CLI, Claw Code, Plandex, Trae Agent, Every Code, VT Code e CoreCoder.
2. **Segundo:** patches genéricos com boa superfície OSS — AutoCodeRover, Galley, 5dive e demais casos `pr-generic`/`patch-required`.
3. **Terceiro:** issues aguardando decisão — Open Codex, Kimi CLI, Devon, g3, Free Code, Claude Engineer e casos com `awaiting-maintainer`.
4. **Quarto:** documentação e receitas config-only agrupadas por ecossistema — OpenCode, Codex, LiteLLM, AI SDK, OpenAI-compatible e Anthropic-compatible.
5. **Quinto:** MCP/plugins para produtos fechados — Windsurf, Amp, Kiro, Cowork e Cursor, sempre pela superfície oficial.
Cada rodada deve manter no máximo três agentes ativos. O agente principal revisa o resultado do trio antes de liberar o próximo.
## Critério de encerramento por caso
Um caso só pode ser marcado como finalizado quando possui: pesquisa, classificação, evidência de protocolo, baseline ou limitação reproduzível, receita/patch/bloqueio, validação proporcional, estado de publicação e próximo passo. Para produtos fechados, `blocked-closed` ou `MCP-only` é um resultado válido e preferível a uma PR não autorizada.
## Estado de publicação atual
Os únicos links de publicação comprovados devem continuar sendo os registrados no tracker. O fato de existir uma branch local de pesquisa não significa que exista PR upstream. A matriz de verdade é:
- PR/issue preenchida: publicação real;
- campo `—`: nenhuma publicação externa comprovada;
- `not-applicable`: configuração ou bloqueio sem contribuição upstream;
- `awaiting-maintainer`: contato feito, aguardando decisão;
- `published-pr`/`published-issue`: URL real presente no tracker.
## Próxima fase
A pesquisa está encerrada. A próxima fase é execução controlada da Trilha A/B/C/D, começando pelos casos com maior retorno e menor risco, com revisão central antes de qualquer push, PR, issue ou contato externo.

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env node
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
const here = dirname(fileURLToPath(import.meta.url));
const root = join(here, "..");
export function resolveChatGptWebCodexMcpEntry(rootDir = root, exists = existsSync) {
const candidates = [
join(
rootDir,
"dist",
"open-sse",
"vendor",
"codex-chatgpt-web",
"adapters",
"chatgpt-web",
"mcp-server.js"
),
join(
rootDir,
"open-sse",
"vendor",
"codex-chatgpt-web",
"adapters",
"chatgpt-web",
"mcp-server.ts"
),
];
return candidates.find((candidate) => exists(candidate)) ?? null;
}
export async function startChatGptWebCodexMcp(args = process.argv.slice(2), rootDir = root) {
const socketIndex = args.indexOf("--broker-socket");
const brokerSocketPath = socketIndex >= 0 ? args[socketIndex + 1] : undefined;
if (!brokerSocketPath) throw new Error("--broker-socket is required");
const entry = resolveChatGptWebCodexMcpEntry(rootDir);
if (!entry) throw new Error("ChatGPT Web (Codex) MCP entrypoint was not found");
if (entry.endsWith(".ts")) {
const { register } = await import("node:module");
register("tsx/esm", pathToFileURL(`${rootDir}/`));
}
const module = await import(pathToFileURL(entry).href);
await module.runChatGptMcpServer({ brokerSocketPath });
}
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
startChatGptWebCodexMcp().catch((error) => {
console.error(
`ChatGPT Web (Codex) MCP konnte nicht gestartet werden: ${error?.message || error}`
);
process.exit(1);
});
}

View File

@@ -6,6 +6,7 @@ import { t } from "../i18n.mjs";
const PROVIDERS_WITH_OAUTH = [
{ id: "gemini", name: "Google Gemini", flow: "browser" },
{ id: "antigravity", name: "Antigravity", flow: "browser" },
{ id: "windsurf", name: "Windsurf", flow: "browser" },
{ id: "cursor", name: "Cursor", flow: "import" },
{ id: "zed", name: "Zed", flow: "import" },
{ id: "kiro", name: "Amazon Kiro", flow: "social" },

View File

@@ -16,7 +16,7 @@ import {
resolveMaxOldSpaceMb,
calibrateHeapFallbackMb,
buildServerNodeOptions,
buildNodeHeapArgs,
buildNodeRuntimeArgs,
} from "../../../scripts/build/runtime-env.mjs";
import { resolveTlsOptions } from "../../../scripts/dev/tls-options.mjs";
@@ -269,12 +269,7 @@ function runDaemon(serverJs, env, memoryLimit, dashboardPort, apiPort) {
// heap via NODE_OPTIONS (a CLI arg would shadow/override their value).
const server = spawn(
process.versions.bun ? process.execPath : "node",
[
...(process.versions.bun
? ["--preload", join(APP_DIR, "open-sse/utils/setupPolyfill.ts")]
: buildNodeHeapArgs(process.env, memoryLimit)),
serverJs,
],
process.versions.bun ? [serverJs] : buildNodeRuntimeArgs(process.env, memoryLimit, serverJs),
{
cwd: APP_DIR,
env,
@@ -294,12 +289,7 @@ function runWithoutRecovery(serverJs, env, memoryLimit, dashboardPort, apiPort,
// heap via NODE_OPTIONS (a CLI arg would shadow/override their value).
const server = spawn(
process.versions.bun ? process.execPath : "node",
[
...(process.versions.bun
? ["--preload", join(APP_DIR, "open-sse/utils/setupPolyfill.ts")]
: buildNodeHeapArgs(process.env, memoryLimit)),
serverJs,
],
process.versions.bun ? [serverJs] : buildNodeRuntimeArgs(process.env, memoryLimit, serverJs),
{
cwd: APP_DIR,
env,
@@ -397,12 +387,19 @@ async function runWithSupervisor(
supervisor.start();
// #9455: persist the supervisor's own PID so `omniroute stop` can SIGTERM it
// before the child — the supervisor's SIGTERM handler sets isShuttingDown=true,
// kills the child, and exits cleanly, so the child is never respawned after stop.
writePidFile("supervisor", process.pid);
process.on("SIGINT", () => {
killTrayIfActive();
cleanupPidFile("supervisor");
supervisor.stop();
});
process.on("SIGTERM", () => {
killTrayIfActive();
cleanupPidFile("supervisor");
supervisor.stop();
});

View File

@@ -1,5 +1,5 @@
import { spawn } from "node:child_process";
import { dirname, join } from "node:path";
import { dirname } from "node:path";
import { writePidFile, cleanupPidFile, killAllSubprocesses, isPidRunning } from "../utils/pid.mjs";
import {
RESTART_RESET_MS,
@@ -8,7 +8,7 @@ import {
computeRestartDelayMs,
waitUntilPortFree,
} from "./supervisorPolicy.mjs";
import { buildNodeHeapArgs } from "../../../scripts/build/runtime-env.mjs";
import { buildNodeRuntimeArgs } from "../../../scripts/build/runtime-env.mjs";
import { stopProcessGracefully } from "../../../src/shared/platform/windowsProcess.ts";
import {
isFatalInstrumentationHookFailure,
@@ -47,20 +47,19 @@ export class ServerSupervisor {
// #5238: skip the explicit CLI --max-old-space-size when the user pinned the
// heap via NODE_OPTIONS (a CLI arg would shadow/override their value). The
// calibrated heap is already carried by env.NODE_OPTIONS either way.
const heapArgs = buildNodeHeapArgs(process.env, this.memoryLimit);
// #6321: stdout used to be discarded (`"ignore"`) whenever `--log`/OMNIROUTE_SHOW_LOG
// wasn't set (the default) — any debug/pino output written to stdout vanished
// silently, so a boot that never becomes ready looked like a dead hang with zero
// output even at APP_LOG_LEVEL=debug. Pipe stdout too and buffer it alongside
// stderr so a readiness timeout can surface what the child actually printed.
// #9156: macOS launchd cannot resolve bare "node" because its PATH is
// minimal. Always use process.execPath (the absolute path to the running
// Node.js binary) so the supervisor never depends on PATH resolution.
this.child = spawn(
process.versions.bun ? process.execPath : "node",
[
...(process.versions.bun
? ["--preload", join(dirname(this.serverPath), "open-sse/utils/setupPolyfill.ts")]
: heapArgs),
this.serverPath,
],
process.execPath,
process.versions.bun
? [this.serverPath]
: buildNodeRuntimeArgs(process.env, this.memoryLimit, this.serverPath),
{
cwd: dirname(this.serverPath),
env: this.env,

View File

@@ -0,0 +1 @@
- **feat(api):** add response content encoding verification — confirms Next.js compress:true and documents stripStaleForwardingHeaders behavior ([#6736](https://github.com/diegosouzapw/OmniRoute/issues/6736))

View File

@@ -0,0 +1 @@
- **feat(api):** add plugins marketplace install endpoint with checksum verification ([#6752](https://github.com/diegosouzapw/OmniRoute/issues/6752))

View File

@@ -0,0 +1 @@
- **feat(chatgpt-web):** harden prompt-emulated tool contract for thinking models (#7679 — thanks @horacecar)

View File

@@ -0,0 +1 @@
- **docs:** add management authentication terminology guide ([#7786](https://github.com/diegosouzapw/OmniRoute/issues/7786))

View File

@@ -0,0 +1 @@
- **docs:** add low-memory/small VPS optimization guide ([#8237](https://github.com/diegosouzapw/OmniRoute/issues/8237))

View File

@@ -0,0 +1 @@
- **feat(providers):** add connection-level custom upstream headers via `provider_specific_data.customHeaders` — applied to every request through that connection, with model-level headers overriding on the same case-insensitive name. (thanks @Benson-mk)

View File

@@ -0,0 +1 @@
- feat(copilot): add approval gate for runOmniRouteCli commands (#8461)

View File

@@ -0,0 +1 @@
- **feat(electron):** Desktop app can now attach to an already-running OmniRoute server (e.g. a Docker/OrbStack container) instead of always spawning its own bundled server — configurable via the tray's "Remote Server → Connect to Remote Server…" or the `OMNIROUTE_REMOTE_URL` env var ([#8799](https://github.com/diegosouzapw/OmniRoute/pull/8799)) — thanks @soulhakr

View File

@@ -0,0 +1 @@
- **Providers**: expands the Novita AI catalog from a single Llama 3.1 8B entry to 19 curated serving models (DeepSeek V4, Kimi K3, GLM 5.2, MiniMax M3, Qwen3.7 Max, Qwen3 Coder 480B, MiMo V2.5 Pro, gpt-oss-120b, Gemma 4 31B and more), each carrying its real context window, output cap and reasoning flag from the live `/openai/v1/models` listing, and each vision flag confirmed by an actual image request rather than the listing's self-reported modalities

View File

@@ -0,0 +1 @@
- **Database**: The `node:sqlite` fallback now uses SQLite's native backup API and real immediate write transactions, improving backup consistency and concurrent-write behavior when `better-sqlite3` is unavailable

View File

@@ -0,0 +1 @@
- **feat(models):** add exact per-model `context_length`, `max_input_tokens`, and `max_output_tokens` overrides across model discovery and runtime enforcement, with automatic migration from the retired output-only `max_token` key ([#8908](https://github.com/diegosouzapw/OmniRoute/pull/8908)) — thanks @xz-dev

View File

@@ -0,0 +1 @@
- **feat(providers):** native xAI Agent Tools passthrough on `/v1/responses` for `xai` / `xai-oauth` (`xao`) — forward `web_search` + `x_search` to `api.x.ai` instead of rewriting or rejecting them ([#8964](https://github.com/diegosouzapw/OmniRoute/issues/8964))

View File

@@ -0,0 +1 @@
- **feat(providers): add UnoRouter provider** — UnoRouter is an OpenAI-compatible routing gateway supporting hundreds of models. It is now registered as an API-key provider. ([#8978](https://github.com/diegosouzapw/OmniRoute/issues/8978))

View File

@@ -0,0 +1 @@
- **feat(sse):** deprecated the legacy `gemini-cli` **upstream provider**. It was not routable (no registry entry, no executor), yet the scheduler kept refreshing its token against Google — maintaining a credential that could never serve a request. A stored connection now becomes terminal with a legible reason and a working migration path: re-add the account under `gemini`, which uses the same Google OAuth client. The `gemini-cli` **client identity** (requests arriving *from* the Gemini CLI, issue #7034) is untouched ([#8980](https://github.com/diegosouzapw/OmniRoute/pull/8980))

View File

@@ -0,0 +1 @@
- **feat(providers):** add Regolo AI OpenAI-compatible provider ([#9031](https://github.com/diegosouzapw/OmniRoute/issues/9031))

View File

@@ -0,0 +1 @@
- **feat(db):** add provider-scoped model aliases that survive rediscovery ([#9068](https://github.com/diegosouzapw/OmniRoute/issues/9068))

View File

@@ -0,0 +1 @@
- **feat(codex):** accept parenthesized GPT-5.6 reasoning overrides. (thanks @seakleangnhak)

View File

@@ -0,0 +1 @@
- **feat(usage):** surface Claude thinking token counts to clients. (thanks @luoyide)

View File

@@ -0,0 +1 @@
- **feat(ollama):** add Ollama Local embedding support via /v1/embeddings. (thanks @HaoNgo232)

View File

@@ -0,0 +1 @@
- feat: make forwarded upstream response-header budget configurable via env var (#9243)

View File

@@ -0,0 +1 @@
- **feat(providers):** filter provider detail connections server-side while preserving full-page search and pagination. (thanks @RobertsXML)

View File

@@ -0,0 +1 @@
- **feat(providers):** make video_url passthrough configurable per provider/model via compat override ([#9248](https://github.com/diegosouzapw/OmniRoute/issues/9248)) — thanks @HellFiveOsborn

View File

@@ -0,0 +1 @@
- **feat(dashboard):** render a conditional "Get API key" link on the provider detail page, surfaced from the existing `notice.apiKeyUrl` / `notice.signupUrl` catalog metadata (e.g. `pioneer`, `jina`, `together`). The link opens in a new tab and is hidden when neither URL is present, so existing providers are unaffected. Tracks the notice field in `ProviderCatalogMetadata` ([#9270](https://github.com/diegosouzapw/OmniRoute/pull/9270))

View File

@@ -0,0 +1 @@
- **feat(providers):** accept JSON cookie objects in normalizeSessionCookieHeader (#9284 — thanks @AIB1TAL0S)

View File

@@ -0,0 +1 @@
- **feat(providers):** support max reasoning effort for opencode-zen DeepSeek models (#9318)

View File

@@ -0,0 +1 @@
- **sse:** New-API / One-API / Sub2API aggregator balance detection for compatible nodes — with the "Aggregator Gateway" toggle on, OmniRoute queries the aggregator's `/api/user/self` to read the account balance, shows it as a dashboard badge and lets quota-preflight routing skip exhausted accounts. Gated by the `NEWAPI_AGGREGATOR_BALANCE` feature flag (default off), with a `quotaPerUnit` override for aggregators that do not use the default 500000 units/$1 rate ([#9415](https://github.com/diegosouzapw/OmniRoute/issues/9415))

View File

@@ -0,0 +1 @@
- **feat(catalog):** added opt-in settings `hideAutoCombos` and `hideNoThinkVariants` (default off) to filter built-in `auto/*` virtual combos and `no-think/*` gateway variants from the `/v1/models` catalog — user-defined combos and original provider models stay listed; routing is unaffected ([#9418](https://github.com/diegosouzapw/OmniRoute/issues/9418))

View File

@@ -0,0 +1 @@
- **feat(opencode-plugin):** added `features.visibleModels` (allowlist) and `features.hiddenModels` (blocklist) to `@omniroute/opencode-plugin` — curate the OpenCode TUI/CLI model picker from 600+ catalog entries down to an operator-defined ID list that persists in `opencode.json` across config resets ([#9473](https://github.com/diegosouzapw/OmniRoute/issues/9473))

View File

@@ -0,0 +1 @@
- **feat(models):** Test All's "Auto-hide failed models" no longer hides quota errors — daily-quota-exhausted and credits-exhausted responses are now classified via the routing path's existing quota detectors, so an evening Test All on a free-tier provider no longer silently wipes the catalog. Quota results stay visible with a distinct amber badge ([#9511](https://github.com/diegosouzapw/OmniRoute/issues/9511))

View File

@@ -0,0 +1 @@
- feat(plugins): expose client request headers in plugin onRequest/onResponse context (#9570)

View File

@@ -0,0 +1 @@
- **feat(audio):** Soniox STT + TTS provider (`sx`) — async speech-to-text (`stt-async-v5`, `stt-async-v4`) and real-time text-to-speech (`tts-rt-v1`) ([#9579](https://github.com/diegosouzapw/OmniRoute/pull/9579))

View File

@@ -0,0 +1 @@
- test(combo): guard auto/best-free never leaks the combo name as a model (#7754)

View File

@@ -0,0 +1 @@
- **fix(models):** `/v1/models` now publishes one contiguous provider-grouped block per provider instead of interleaved fragments. The catalog is assembled by many independent push loops (auto-combos, named combos, static registry, codex-native, synced, OpenRouter, specialty, custom, alias-backed, connection-fallback), so one provider's models previously landed in several separated blocks. A single stable, provider-grouped sort is applied at serialization, keyed by `owned_by` (canonical owner identity) rather than the model-id prefix — so a single routable public prefix that differs from its owner (e.g. no-auth OpenCode publishing `oc/<model>` while keeping `owned_by: "opencode"`) stays contiguous. Combos are pinned first (preserving #4164); then providers in registry precedence (OAuth → NoAuth → API-key); then unknown providers in locale-independent code-unit order. The sort is stable and pure (reorders rows only, no mutation, no DB/IO), preserving combo `sort_order`, connection priority, custom append-order, and equal-id audio twins.

View File

@@ -0,0 +1,3 @@
- fix(vision-bridge): describe-model no longer returns unreachable "openai/gpt-4o-mini" when every vision-capable provider is unreachable on the instance — returns null instead and surfaces a clear error (#8430)
- fix(vision-bridge): validate fixedModel against usable credentials before short-circuiting in getBestVisionModel, so the default "openai/gpt-4o-mini" is not unconditionally selected when no OpenAI connection exists (#8430)
- fix(vision-bridge): in the combo describe path, replace raw images with an error text stub when all describe attempts fail, instead of forwarding images to a confirmed non-vision backend that would reject them with an opaque serde error (#8430)

View File

@@ -0,0 +1 @@
- fix(quality): add base-relative file-size check so inherited drift does not red innocent PRs (#8522)

View File

@@ -0,0 +1 @@
- fix(ci): aggregate all fast-gates into non-fail-fast loop so one red gate no longer masks later gates (#8542)

View File

@@ -0,0 +1,2 @@
- fix(tests): make machineId tests macOS-compatible by stubbing ioreg in test helper (#8577)
- fix(scripts): replace bash 4+ readarray with compatible while-read loop in restore-policies.sh (#8577)

View File

@@ -0,0 +1 @@
- fix(cli): enable systray2 on Windows for Norton-friendly tray (#8609)

View File

@@ -0,0 +1 @@
- fix(executor): guard claude/anthropic buildHeaders against empty credentials and extend dual-Bearer parity for third-party baseUrls (#8653)

View File

@@ -0,0 +1 @@
- fix(providers): gate premium opencode-zen/opencode-go models behind an API key (#8681)

View File

@@ -0,0 +1 @@
- fix(yuanbao-web): accept `content` field in SSE text events (upstream format change) (#8739)

View File

@@ -0,0 +1 @@
- fix(build): remove misleading open-sse/package.json facade and add workspace typecheck gate (#8781)

View File

@@ -0,0 +1 @@
- fix(errorClassifier): classify ChatGPT Web SENTINEL_BLOCKED 403 as terminal FORBIDDEN, enabling proper combo fallback (#8813)

View File

@@ -0,0 +1 @@
- fix(cli): fall back to node:sqlite when better-sqlite3 constructor throws at runtime (#8826)

View File

@@ -0,0 +1 @@
- fix(opencode): prefix provider id with "opencode-" for auth login command (#8830)

View File

@@ -0,0 +1 @@
- fix(opencode-zen): add current free-tier models to registry to enable combo context pre-filtering (#8841)

View File

@@ -0,0 +1 @@
- **fix(api):** Let image and video providers enforce their own request-size limits instead of rejecting media payloads at OmniRoute's 10 MB global default ([#8843](https://github.com/diegosouzapw/OmniRoute/pull/8843)) — thanks @artickc

View File

@@ -0,0 +1 @@
- fix(build): include better-sqlite3 prebuilds in standalone bun bundle

View File

@@ -0,0 +1 @@
- fix(proxy-health): include credentials in proxy health check URLs (#8853)

View File

@@ -0,0 +1 @@
- **fix(build):** `prepublish` no longer spawns the Windows `.cmd` shims for npm/npx, which Node >= 20 refuses to launch without a shell (`EINVAL`). On Node 24 that silently skipped the MITM utilities, the MCP server bundle, the LLMLingua ONNX worker and `@omniroute/opencode-plugin` while `build:cli` still exited 0 and reported success. Build tools are now resolved to their own JS entry point and run with the current Node binary — no shim, no shell, no unescaped arguments. (thanks @maisdesign)

View File

@@ -0,0 +1 @@
- **fix(opencode):** generate schema-complete model limits so OpenCode accepts catalog entries without an explicit output cap ([#8869](https://github.com/diegosouzapw/OmniRoute/pull/8869)) — thanks @xiaoyaner0201

View File

@@ -0,0 +1 @@
- **fix(cli):** default omitted Codex CLI wire API settings to Responses and clear stale Chat state after reset ([#8876](https://github.com/diegosouzapw/OmniRoute/pull/8876)) — thanks @xiaoyaner0201

View File

@@ -0,0 +1 @@
- **fix(proxy):** isolate new proxy credential fields from browser and password-manager autofill after form reset ([#8883](https://github.com/diegosouzapw/OmniRoute/pull/8883)) — thanks @xiaoyaner0201

View File

@@ -0,0 +1 @@
- fix(tests): update stale nightly compat fixtures and goldens to match current source constants (#8901)

View File

@@ -0,0 +1 @@
- **fix(quota):** Deleting a quota pool now removes its scoped managed combos without racing in-flight pool mutations ([#8906](https://github.com/diegosouzapw/OmniRoute/pull/8906)) — thanks @xiaoyaner0201

View File

@@ -0,0 +1 @@
- **fix(providers):** expose both OAuth Connect and manual API-key actions for dual-auth providers such as CodeBuddy CN ([#8921](https://github.com/diegosouzapw/OmniRoute/pull/8921)) — thanks @Llliao1113

View File

@@ -0,0 +1 @@
- fix(compression): drop orphan custom_tool_call/local_shell_call/apply_patch_call on compaction restore (#8946)

View File

@@ -0,0 +1 @@
- fix(auth): setting first dashboard login password no longer fails with HTTP 400 PASSWORD_REQUIRED (#8950)

View File

@@ -0,0 +1 @@
- fix(github): add targetFormat to GPT-5.6 Sol/Terra/Luna models (#8951)

View File

@@ -0,0 +1 @@
- fix(auto-update): skip synthetic Next.js standalone package.json without `name` field in resolveProjectRoot (#8956)

View File

@@ -0,0 +1 @@
- fix(opencode): propagate vision capability from live catalog into opencode.json (#8960)

Some files were not shown because too many files have changed in this diff Show More