diff --git a/.agents/workflows/fix-ci.md b/.agents/workflows/fix-ci.md deleted file mode 100644 index dd299c50c5..0000000000 --- a/.agents/workflows/fix-ci.md +++ /dev/null @@ -1,182 +0,0 @@ -# Fix CI Workflow - -Look up the latest GitHub Actions CI run for the current release branch, diagnose all failures, fix them locally, push, and wait for the new CI run to go green before notifying the user. - ---- - -## Phase 1: Identify the Failing CI Run - -### 1. Determine the current release version and branch - -// turbo - -```bash -cd /home/diegosouzapw/dev/proxys/9router -VERSION=$(node -p "require('./package.json').version") -BRANCH=$(git branch --show-current) -echo "Version: $VERSION" -echo "Branch: $BRANCH" -``` - -### 2. Find the latest CI run for the release PR - -// turbo - -```bash -cd /home/diegosouzapw/dev/proxys/9router -# Find the PR number for the release branch -PR_NUMBER=$(gh pr list --repo diegosouzapw/OmniRoute --head "$BRANCH" --json number --jq '.[0].number') -echo "PR: #$PR_NUMBER" - -# Get the latest CI run -RUN_ID=$(gh run list --repo diegosouzapw/OmniRoute --branch "$BRANCH" --workflow ci.yml --limit 1 --json databaseId --jq '.[0].databaseId') -echo "Latest CI Run: $RUN_ID" -echo "URL: https://github.com/diegosouzapw/OmniRoute/actions/runs/$RUN_ID" -``` - ---- - -## Phase 2: Diagnose Failures - -### 3. List all failing jobs - -// turbo - -```bash -cd /home/diegosouzapw/dev/proxys/9router -RUN_ID=$(gh run list --repo diegosouzapw/OmniRoute --branch "$(git branch --show-current)" --workflow ci.yml --limit 1 --json databaseId --jq '.[0].databaseId') -gh run view "$RUN_ID" --repo diegosouzapw/OmniRoute --json jobs --jq '.jobs[] | select(.conclusion == "failure") | {name: .name, conclusion: .conclusion, steps: [.steps[] | select(.conclusion == "failure") | .name]}' -``` - -### 4. Download and analyze failure logs - -// turbo - -```bash -cd /home/diegosouzapw/dev/proxys/9router -RUN_ID=$(gh run list --repo diegosouzapw/OmniRoute --branch "$(git branch --show-current)" --workflow ci.yml --limit 1 --json databaseId --jq '.[0].databaseId') -gh run view "$RUN_ID" --repo diegosouzapw/OmniRoute --log-failed 2>&1 | grep -aE "not ok|FAIL|Error:|error:|AssertionError|expected|actual" | grep -v "node_modules\|runner\|git version" | head -50 -``` - -### 5. Classify each failure - -For each failing job, determine the root cause category: - -| Category | Pattern | Fix Strategy | -| ------------------ | ---------------------------------- | ------------------------------------------ | -| **docs-sync** | OpenAPI/CHANGELOG version mismatch | Run `/version-bump` step 7-8 | -| **Test assertion** | `not ok` + `AssertionError` | Update test expectations to match new code | -| **E2E flaky** | Auth-related 401/403/307 | Make tests tolerate auth states | -| **Coverage gate** | `below threshold` | Add more tests or adjust threshold | -| **Lint** | ESLint errors | Fix code or update rules | -| **Build** | Compilation errors | Fix TypeScript issues | - ---- - -## Phase 3: Apply Fixes - -### 6. Fix each failure - -For each classified failure: - -1. **Read the failing test file** to understand the assertion -2. **Read the production source** to understand the new behavior -3. **Update the test** to match the current behavior -4. **Run the test locally** to verify the fix - -// turbo - -```bash -cd /home/diegosouzapw/dev/proxys/9router -# Run the specific failing test file(s) to confirm fixes -# Example: node --import tsx/esm --test tests/unit/FAILING_FILE.test.mjs -``` - -### 7. Run the full local test suite - -// turbo - -```bash -cd /home/diegosouzapw/dev/proxys/9router -npm test -``` - -### 8. Run docs-sync check - -// turbo - -```bash -cd /home/diegosouzapw/dev/proxys/9router -npm run check:docs-sync -``` - ---- - -## Phase 4: Push and Monitor - -### 9. Commit and push fixes - -// turbo-all - -```bash -cd /home/diegosouzapw/dev/proxys/9router -git add -A -git commit -m "fix(tests): align CI tests with release changes" -git push origin "$(git branch --show-current)" -``` - -### 10. Wait for CI to trigger and find the new run - -// turbo - -```bash -cd /home/diegosouzapw/dev/proxys/9router -sleep 15 -BRANCH=$(git branch --show-current) -NEW_RUN_ID=$(gh run list --repo diegosouzapw/OmniRoute --branch "$BRANCH" --workflow ci.yml --limit 1 --json databaseId --jq '.[0].databaseId') -echo "New CI Run: $NEW_RUN_ID" -echo "URL: https://github.com/diegosouzapw/OmniRoute/actions/runs/$NEW_RUN_ID" -``` - -### 11. Monitor the CI run - -// turbo - -```bash -cd /home/diegosouzapw/dev/proxys/9router -BRANCH=$(git branch --show-current) -NEW_RUN_ID=$(gh run list --repo diegosouzapw/OmniRoute --branch "$BRANCH" --workflow ci.yml --limit 1 --json databaseId --jq '.[0].databaseId') -gh run watch "$NEW_RUN_ID" --repo diegosouzapw/OmniRoute --exit-status -``` - -If `gh run watch` exits with 0, the CI is green. If it exits with non-zero, go back to Phase 2 and repeat. - -### 12. 🛑 STOP — Notify User - -Present a summary to the user: - -- **Previous CI run**: URL, list of failures -- **Root causes**: What broke and why -- **Fixes applied**: What tests were changed -- **New CI run**: URL, all-green status -- **PR status**: Ready for review/merge - ---- - -## Common CI Failure Patterns - -| Failure | Root Cause | Fix | -| ------------------------------------------ | -------------------------------------- | ----------------------------- | -| `docs-sync FAIL - OpenAPI version differs` | Version not synced after bump | `sed -i` openapi.yaml | -| `docs-sync FAIL - CHANGELOG first section` | Missing `## [Unreleased]` header | Add unreleased section | -| `not ok - cleanupExpiredLogs` | Return shape changed (new fields) | Update `assert.deepEqual` | -| `not ok - email masking` | Email now masked in call logs | Assert masked pattern instead | -| `E2E /api/providers` returns non-200 | Auth enabled in CI, endpoint protected | Accept 401/403 as valid | -| `coverage below 60%` | New untested code | Add unit tests | - -## Notes - -- This workflow is **iterative**: if the first fix attempt doesn't clear all failures, repeat Phases 2-4. -- Always run tests **locally** before pushing to avoid wasting CI minutes. -- The CI is triggered automatically on push to branches with open PRs to `main`. -- Use `gh run watch` to monitor in real-time instead of polling. diff --git a/.agents/workflows/fix-cli-node22-ts-entrypoint.md b/.agents/workflows/fix-cli-node22-ts-entrypoint.md deleted file mode 100644 index a527592a14..0000000000 --- a/.agents/workflows/fix-cli-node22-ts-entrypoint.md +++ /dev/null @@ -1,27 +0,0 @@ -# Workflow: Fix CLI Node 22 TS Entrypoint Issue - -## Issue Description - -In OmniRoute >= 3.6.6, the `package.json` declares the main CLI binary as `bin/omniroute.ts`. While Node 22 introduced experimental support for executing TypeScript files natively via the `--experimental-strip-types` flag (and `--experimental-transform-types`), Node.js explicitly disables this feature for any files loaded from inside a `node_modules` directory. - -When users install `omniroute` globally (or locally) via npm, the CLI shim points to `bin/omniroute.ts` inside `node_modules`. Running `omniroute` on Node 22 results in: -`Error [ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING]: Stripping types is currently unsupported for files under node_modules` - -This makes the CLI completely unusable out-of-the-box on Node 22, despite the `engines` field claiming support for `>=22.22.2`. - -## Proposed Solution - -The CLI entrypoint distributed in the NPM package must be a JavaScript file, not a TypeScript file. - -1. Restore `bin/omniroute.mjs` as the actual CLI entrypoint, or implement a build step during `npm run build:cli` that compiles `bin/omniroute.ts` to `bin/omniroute.mjs` before publishing. -2. Update `package.json` to point the `bin` field back to the compiled `.js`/`.mjs` file: - ```json - "bin": { - "omniroute": "bin/omniroute.mjs" - } - ``` -3. Ensure `scripts/prepublish.ts` handles the compilation or validation of the binary entrypoint correctly so this regression doesn't happen again. - -## Task - -Please implement this fix, ensure the tests pass, and create a Pull Request against the main branch. diff --git a/.agents/workflows/generate-release.md b/.agents/workflows/generate-release.md index 3a1c6c3c9c..c6107339bb 100644 --- a/.agents/workflows/generate-release.md +++ b/.agents/workflows/generate-release.md @@ -52,19 +52,19 @@ Before creating the release, you must ensure the codebase and supply chain are s git checkout -b release/v2.x.y ``` -### 2. Determine new version +### 2. Determine and sync version -Check current version in `package.json` and increment the **patch** number only: +Check current version in `package.json`: ```bash grep '"version"' package.json ``` -Version format: `2.x.y` — examples: - -- `2.1.2` → `2.1.3` (patch) -- `2.1.9` → `2.1.10` (patch) -- `2.1.10` → `2.2.0` (minor threshold — do manually with `sed`) +> **🔴 BRANCH-VERSION PARITY RULE**: The logical version in `package.json` MUST exactly match the release branch name. For example, if you are on `release/v3.7.0`, the version in `package.json` MUST be `3.7.0`. +> +> - If this is the FIRST time generating a release for a new minor/major branch (e.g., bumping from 3.6.9 to 3.7.0), you MUST ensure the version is bumped to match the new branch logic. +> - If you are just bumping a patch on the current branch (e.g., 3.6.9 to 3.6.10), use: +> `npm version patch --no-git-tag-version` > **⚠️ ATOMIC COMMIT RULE — Version bump MUST happen before committing feature files.** > @@ -97,15 +97,29 @@ npm install ### 4. Finalize CHANGELOG.md -Replace `[Unreleased]` header with the new version and date. -Keep an empty `## [Unreleased]` section above it. +> **🔴 NO MIXUPS RULE**: Ensure you do NOT mix the backlog of the previous version with the new one. The new version section must ONLY contain the features and fixes for the current release. + +Replace the `[Unreleased]` header with the new version and date. +Keep an empty `## [Unreleased]` section above it, separated by a horizontal rule (`---`). ```markdown ## [Unreleased] --- -## [2.x.y] — YYYY-MM-DD +## [3.7.0] — 2026-04-19 + +### ✨ New Features + +- ... + +### 🐛 Bug Fixes + +- ... + +--- + +## [3.6.9] — 2026-04-19 ``` ### 5. Update openapi.yaml version ⚠️ MANDATORY @@ -199,11 +213,43 @@ Inform the user: --- -## Phase 2: Post-Merge (only after user confirms) +## Phase 2: Post-Merge Validation (Local VPS) -> Run these steps only AFTER the user has merged the PR. +> Run these steps only AFTER the user has merged the PR into `main` and all CI jobs have passed. -### 11. Create Git Tag and GitHub Release (MANDATORY) +### 11. Deploy to Local VPS for Final Validation (MANDATORY) + +Before cutting the official git tag and publishing to the world, deploy the `main` branch to the Local VPS for a final homologation test. + +```bash +git checkout main +git pull origin main + +# Build and pack locally +cd /home/diegosouzapw/dev/proxys/9router && rm -f omniroute-*.tgz && rm -rf .next/cache app/.next/cache && npm run build:cli && rm -rf app/logs app/coverage app/.git app/.app-build-backup* && npm pack --ignore-scripts + +# Deploy to LOCAL VPS (192.168.0.15) +scp omniroute-*.tgz root@192.168.0.15:/tmp/ +ssh root@192.168.0.15 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && pm2 delete omniroute 2>/dev/null; pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Local done'" + +# Verify +curl -s -o /dev/null -w "LOCAL: HTTP %{http_code}\n" http://192.168.0.15:20128/ +``` + +### 12. 🛑 STOP — Notify User & Await Final OK + +**This is a mandatory stop point.** +Inform the user that the `main` branch is now running on the Local VPS. +Wait for the user to manually test and give the **OK**. +**DO NOT proceed to Phase 3 until the user confirms the local deploy is stable.** + +--- + +## Phase 3: Official Launch + +> Run these steps only AFTER the user gives the final OK from the Phase 2 local validation. + +### 13. Create Git Tag and GitHub Release (MANDATORY) // turbo @@ -213,7 +259,7 @@ git pull origin main VERSION=$(node -p "require('./package.json').version") # Extracts the changelog section for this version -NOTES=$(awk '/^## \['"$VERSION"'\]/{flag=1; next} /^## \[[0-9]+/{if(flag) exit} flag' CHANGELOG.md | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') +NOTES=$(awk "/^## \\[$VERSION\\]/{flag=1; next} /^---/{if(flag) {flag=0; exit}} flag" CHANGELOG.md | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') if [ -z "$NOTES" ]; then NOTES="OmniRoute v$VERSION Release"; fi git tag -a "v$VERSION" -m "Release v$VERSION" @@ -225,7 +271,7 @@ gh release create "v$VERSION" --title "v$VERSION" --notes "$NOTES" --target main > **CRITICAL**: Docker Hub and npm MUST always publish the same version. > The Docker image is built automatically via GitHub Actions when a new tag is pushed. -> After pushing the tag in step 11-12, **verify the workflow runs**: +> After pushing the tag in step 13, **verify the workflow runs**: ```bash # Verify the Docker workflow triggered @@ -233,40 +279,63 @@ gh run list --repo diegosouzapw/OmniRoute --workflow docker-publish.yml --limit # Wait for the Docker build to complete (usually 5–10 min) gh run watch --repo diegosouzapw/OmniRoute - -# After completion, verify on Docker Hub: -# https://hub.docker.com/r/diegosouzapw/omniroute/tags ``` -If the Docker build was not triggered automatically, trigger it manually: +### 15. Publish to NPM (Optional/Automated) + +Normally handled by CI, but if manual publish is required: ```bash -gh workflow run docker-publish.yml --repo diegosouzapw/OmniRoute --ref v2.x.y +npm publish ``` -### 15. Deploy to BOTH VPS environments (MANDATORY) +### 16. Deploy to AKAMAI VPS (Production) -> Always deploy to **both** environments after every release. -> See `/deploy-vps` workflow for detailed steps. +Now that the release is officially cut, deploy it to the Akamai VPS. ```bash -# Build and pack locally -cd /home/diegosouzapw/dev/proxys/9router && rm -f omniroute-*.tgz && rm -rf .next/cache app/.next/cache && npm run build:cli && rm -rf app/logs app/coverage app/.git app/.app-build-backup* && npm pack --ignore-scripts - -# Deploy to LOCAL VPS (192.168.0.15) -scp omniroute-*.tgz root@192.168.0.15:/tmp/ -ssh root@192.168.0.15 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && pm2 delete omniroute 2>/dev/null; pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Local done'" - # Deploy to AKAMAI VPS (69.164.221.35) scp omniroute-*.tgz root@69.164.221.35:/tmp/ ssh root@69.164.221.35 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && pm2 delete omniroute 2>/dev/null; pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Akamai done'" -# Verify both -curl -s -o /dev/null -w "LOCAL: HTTP %{http_code}\n" http://192.168.0.15:20128/ +# Verify curl -s -o /dev/null -w "AKAMAI: HTTP %{http_code}\n" http://69.164.221.35:20128/ ``` -### 16. Preserve release branch +## Phase 4: Release Monitoring & Artifact Validation + +> After triggering the official release, actively monitor the CI pipelines until all artifacts are successfully generated. If any pipeline fails, stop and apply the necessary corrections before continuing. + +### 18. Monitor CI Pipelines + +Wait for and verify the successful completion of the following automated jobs: + +1. **Docker Hub Publish** +2. **Electron Build** +3. **NPM Registry Publish** (Check with `npm info omniroute version`) + +```bash +# Monitor Docker Hub workflow +gh run list --repo diegosouzapw/OmniRoute --workflow docker-publish.yml --limit 1 +gh run watch + +# Monitor Electron build +gh run list --repo diegosouzapw/OmniRoute --workflow electron-release.yml --limit 1 +gh run watch + +# Verify NPM version +npm info omniroute version +``` + +### 19. Handle Failures (If Any) + +If a workflow fails: + +- Use `gh run view --log-failed` to identify the error. +- Apply the fix on the `main` branch. +- If necessary, re-trigger the workflow using `gh workflow run --repo diegosouzapw/OmniRoute --ref v2.x.y` + +### 20. Preserve release branch ```bash # Branch is kept for historical purposes. Do not delete. diff --git a/.agents/workflows/resolve-issues.md b/.agents/workflows/resolve-issues.md index f0130fb53e..8573a18ce0 100644 --- a/.agents/workflows/resolve-issues.md +++ b/.agents/workflows/resolve-issues.md @@ -10,6 +10,8 @@ This workflow fetches all open issues from the project's GitHub repository, clas > **BRANCH RULE**: All work MUST happen on the current `release/vX.Y.Z` branch. Never create separate `fix/` branches. If no release branch exists yet, create one first using `/generate-release` Phase 1 steps 1–5. +> **⛔ PR PROHIBITION**: If a fix is associated with a contributor's PR, you MUST merge their PR — NEVER close it and re-implement the fix yourself. See `/review-prs` workflow for the full policy. The `gh pr close` command is FORBIDDEN unless the repository owner explicitly requests it. + ## Steps ### 1. Identify the GitHub Repository @@ -114,7 +116,8 @@ Before coding, perform deep source analysis to formulate a plan: 3. **Read the full source file** — don't rely on grep snippets; understand the surrounding logic 4. **Verify the root cause** — confirm the bug is reproducible based on the code, not just a user misconfiguration 5. **Formulate a proposed solution** — detail the exact files and lines you will change and how you will solve it. -6. **DO NOT modify the codebase yet** — wait for user approval on your report first. +6. **Create an Implementation Plan file** — write your proposed solution to `_tasks/features-vX.Y.Z/-.plan.md` (e.g. `_tasks/features-v3.7.6/1810-auto-restore-probe-failed-db.plan.md`) where `vX.Y.Z` is the current branch version. The plan should contain an Overview, Pre-Implementation Checklist, and detailed Implementation Steps (Files, Changes). +7. **DO NOT modify the codebase yet** — wait for user approval on your report and plan first. #### 5e. For "RESPOND" Issues diff --git a/.agents/workflows/review-prs.md b/.agents/workflows/review-prs.md index 6cb91e08ca..58a5018646 100644 --- a/.agents/workflows/review-prs.md +++ b/.agents/workflows/review-prs.md @@ -4,6 +4,25 @@ description: Analyze open Pull Requests from the project's GitHub repository, ge # /review-prs — PR Review & Analysis Workflow +## ⛔ ABSOLUTE PROHIBITION — Read Before Anything Else + +> **NEVER close a contributor's PR if you intend to use ANY of their code, ideas, or fixes.** +> +> **NEVER manually integrate contributor code into a release branch and then close their PR.** +> +> These actions are **STRICTLY FORBIDDEN** under all circumstances: +> +> 1. ❌ Closing a PR and cherry-picking/copying its code into a release branch +> 2. ❌ Closing a PR "because of conflicts" and re-implementing the same fix yourself +> 3. ❌ Closing a PR and committing a "similar" solution inspired by it +> 4. ❌ Using `gh pr close` on any PR whose content was or will be used +> +> **Why**: Closing a PR after taking the contributor's work means they get ZERO credit on GitHub — no "Merged" badge, no contribution graph entry, no public record. This is effectively stealing their contribution. An audit found this happened to **37 PRs** in the past. +> +> **The ONLY acceptable flow**: Resolve conflicts IN the contributor's branch, push fixes TO their branch, then merge THEIR PR via `gh pr merge`. See Step 7 and Step 8 for the exact procedure. +> +> **When to close a PR**: ONLY when the user (repository owner) explicitly requests it, OR when the PR is clearly spam/malicious, OR when the author themselves asks to close it. In ALL other cases, leave it open. + ## Overview This workflow fetches all open PRs from the project's GitHub repository, performs a critical analysis of each one, generates a detailed report, and waits for user approval before proceeding with implementation. **All improvements are committed on the current release branch** (`release/vX.Y.Z`). diff --git a/.env.example b/.env.example index 9aa731e283..e9992bfb91 100644 --- a/.env.example +++ b/.env.example @@ -71,6 +71,10 @@ PORT=20128 # API_HOST=0.0.0.0 # DASHBOARD_PORT=20128 +# Use Turbopack in local dev. Next 16.2.4 can fail to compile next/font/google +# through the custom dev runner without this on Windows. +OMNIROUTE_USE_TURBOPACK=1 + # Docker production port mappings (docker-compose.prod.yml only). # These set the HOST-side published ports. Container ports use PORT/API_PORT. # PROD_DASHBOARD_PORT=20130 @@ -110,6 +114,13 @@ REQUIRE_API_KEY=false # Default: false | Security risk if enabled on shared instances. ALLOW_API_KEY_REVEAL=false +# Shared secret for the internal Codex Responses WebSocket bridge. +# Used by: src/app/api/internal/codex-responses-ws/route.ts — authenticates +# bridge requests between the Electron/browser WS relay and OmniRoute. +# ⚠️ REQUIRED for production — if unset, all WS bridge requests are rejected. +# Generate: openssl rand -base64 32 +# OMNIROUTE_WS_BRIDGE_SECRET= + # Comma-separated API key IDs that skip request logging (GDPR/compliance). # Used by: src/lib/compliance/index.ts — suppresses logs for specific keys. # NO_LOG_API_KEY_IDS=key_abc123,key_def456 @@ -124,6 +135,12 @@ ALLOW_API_KEY_REVEAL=false # Default: * (all origins) | Restrict for production security. # CORS_ORIGIN=https://your-domain.com +# Allow provider URLs pointing to private/local networks (localhost, 192.168.x.x, etc.). +# REQUIRED for self-hosted providers: LM Studio, Ollama, vLLM, Llamafile, Triton, etc. +# Used by: src/shared/network/outboundUrlGuard.ts — disables SSRF guard for provider calls. +# Default: false (blocked) | Set true to enable local providers. +# OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS=true + # ═══════════════════════════════════════════════════════════════════════════════ # 5. INPUT SANITIZATION & PII PROTECTION (FASE-01) @@ -195,6 +212,24 @@ CLOUD_URL= # Default: http://localhost:20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 +# Browser-facing OmniRoute origin for generated assets in API responses. +# Used by: chatgpt-web image generation cache URLs (/v1/chatgpt-web/image/). +# Set this when OpenWebUI or another relay reaches OmniRoute by an internal URL +# but the user's browser must fetch images from a LAN, tunnel, or public origin. +# Do not include /v1; if included accidentally it will be normalized away. +# OMNIROUTE_PUBLIC_BASE_URL=http://192.168.0.15:20128 + +# Max wait time for an async chatgpt-web image to land via the celsius +# WebSocket, in milliseconds. Default 180000 (3 minutes). Increase during +# upstream queue-deep windows ("Lots of people are creating images right now"). +# OMNIROUTE_CGPT_WEB_IMAGE_TIMEOUT_MS=180000 + +# Total in-memory byte budget for the chatgpt-web image cache (used to serve +# /v1/chatgpt-web/image/), in megabytes. Default 256. Lower this if you +# run OmniRoute on a memory-constrained host; raise it if image generation +# is heavy and clients are racing the 30-minute TTL. +# OMNIROUTE_CGPT_WEB_IMAGE_CACHE_MAX_MB=256 + # Public cloud URL — client-side mirror of CLOUD_URL. NEXT_PUBLIC_CLOUD_URL= @@ -261,6 +296,7 @@ NEXT_PUBLIC_ENABLE_SOCKS5_PROXY=true # Used by MCP server, A2A skills, and CLI sidecars to call the running instance. # Explicit base URL for MCP/A2A tools to reach OmniRoute (overrides localhost auto-detect). +# For browser-visible generated image URLs, prefer OMNIROUTE_PUBLIC_BASE_URL above. # Used by: open-sse/mcp-server/server.ts, src/lib/a2a/ # OMNIROUTE_BASE_URL=http://localhost:20128 @@ -403,15 +439,15 @@ QODER_OAUTH_CLIENT_SECRET=4Z3YjXycVsQvyGF1etiNlIBB4RsqSDtW # Used by: open-sse/executors/base.ts — buildHeaders() dynamic lookup. # Update these when providers release new CLI versions to avoid blocks. -CLAUDE_USER_AGENT=claude-cli/1.0.83 (external, cli) -CODEX_USER_AGENT=codex-cli/0.92.0 (Windows 10.0.26100; x64) -GITHUB_USER_AGENT=GitHubCopilotChat/0.26.7 -ANTIGRAVITY_USER_AGENT=antigravity/1.104.0 darwin/arm64 +CLAUDE_USER_AGENT=claude-cli/2.1.121 (external, cli) +CODEX_USER_AGENT=codex-cli/0.125.0 (Windows 10.0.26100; x64) +GITHUB_USER_AGENT=GitHubCopilotChat/0.45.1 +ANTIGRAVITY_USER_AGENT=antigravity/1.107.0 darwin/arm64 KIRO_USER_AGENT=AWS-SDK-JS/3.0.0 kiro-ide/1.0.0 QODER_USER_AGENT=Qoder-Cli -QWEN_USER_AGENT=QwenCode/0.12.3 (linux; x64) +QWEN_USER_AGENT=QwenCode/0.15.3 (linux; x64) CURSOR_USER_AGENT=connect-es/1.6.1 -GEMINI_CLI_USER_AGENT=google-api-nodejs-client/9.15.1 +GEMINI_CLI_USER_AGENT=google-api-nodejs-client/10.3.0 # ═══════════════════════════════════════════════════════════════════════════════ @@ -536,6 +572,12 @@ APP_LOG_TO_FILE=true # Default: 20 # APP_LOG_MAX_FILES=20 +# How often OmniRoute checks whether the active log file has exceeded +# APP_LOG_MAX_FILE_SIZE and triggers a rotation. Set lower for very verbose +# services to prevent log files from growing large between checks. +# Accepts milliseconds. Default: 60000 (1 minute) +# APP_LOG_ROTATION_CHECK_INTERVAL_MS=60000 + # Days to keep request/call log entries in the database before auto-cleanup. # Default: 7 # CALL_LOG_RETENTION_DAYS=7 @@ -548,6 +590,16 @@ APP_LOG_TO_FILE=true # Default: 100000 # CALL_LOGS_TABLE_MAX_ROWS=100000 +# Whether call log pipeline capture stores stream chunks when enabled in settings. +# Only applies when call_log_pipeline_enabled=true. +# Default: true +# CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS=true + +# Maximum call log artifact size for pipeline captures, in KB. +# Only applies when call_log_pipeline_enabled=true. +# Default: 512 +# CALL_LOG_PIPELINE_MAX_SIZE_KB=512 + # Maximum rows in the proxy_logs SQLite table. # Default: 100000 # PROXY_LOGS_TABLE_MAX_ROWS=100000 @@ -653,6 +705,10 @@ APP_LOG_TO_FILE=true # ── CC-compatible provider (experimental) ── # Enable the Claude Code compatible provider endpoint. +# This is only for third-party relays that accept Claude Code clients exclusively. +# OmniRoute rewrites requests to pass those relays' Claude Code client validation. +# If you only want to use Claude Code CLI, or you are not sure what these relays are, +# keep this disabled and add a regular Anthropic-compatible provider instead. # Used by: src/shared/utils/featureFlags.ts # ENABLE_CC_COMPATIBLE_PROVIDER=false diff --git a/.github/workflows/build-fork.yml b/.github/workflows/build-fork.yml new file mode 100644 index 0000000000..3071abdf0f --- /dev/null +++ b/.github/workflows/build-fork.yml @@ -0,0 +1,41 @@ +name: Build Fork Image (ghcr.io) + +on: + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + build: + name: Build and Push to ghcr.io + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: fix/xiaomi-mimo-provider + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: . + target: runner-base + platforms: linux/amd64 + push: true + tags: | + ghcr.io/gi99lin/omniroute:fix-xiaomi-mimo-provider + ghcr.io/gi99lin/omniroute:latest + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 078717e1ea..3f0a18e460 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: needs: i18n-matrix steps: - uses: actions/checkout@v6 - - uses: actions/setup-python@v6.2.0 + - uses: actions/setup-python@v6 with: python-version: "3.12" @@ -100,44 +100,6 @@ jobs: cat .artifacts/pr-test-policy.md >> "$GITHUB_STEP_SUMMARY" fi - advanced-security: - name: Advanced Security Scans - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: TruffleHog Secret Scan - uses: trufflesecurity/trufflehog@main - with: - path: ./ - base: ${{ github.event.before || github.event.repository.default_branch }} - head: HEAD - extra_args: --only-verified - - - uses: actions/setup-node@v6 - with: - node-version: ${{ env.CI_NODE_VERSION }} - cache: npm - - - run: npm ci - - run: npm run check:node-runtime - - - name: Dependency audit - run: npm audit --audit-level=high --omit=dev - - - name: Check for known vulnerabilities - run: npx is-my-node-vulnerable - - - name: Run Snyk Vulnerability checks - if: github.actor != 'dependabot[bot]' - uses: snyk/actions/node@master - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - with: - args: --severity-threshold=high - build: name: Build runs-on: ubuntu-latest @@ -168,6 +130,34 @@ jobs: - run: npm run build:cli - run: npm run check:pack-artifact + electron-package-smoke: + name: Electron Package Smoke + runs-on: ubuntu-latest + timeout-minutes: 25 + needs: build + env: + JWT_SECRET: ci-build-secret-with-sufficient-length-for-validation + CSC_IDENTITY_AUTO_DISCOVERY: "false" + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.CI_NODE_VERSION }} + cache: npm + - run: npm ci + - run: npm run check:node-runtime + - run: npm run build + - name: Install Electron dependencies + working-directory: electron + run: npm install --no-audit --no-fund + - name: Pack Electron app + working-directory: electron + run: npm run pack + - name: Smoke packaged Electron app + env: + ELECTRON_SMOKE_TIMEOUT_MS: 60000 + run: xvfb-run -a npm run electron:smoke:packaged + test-unit: name: Unit Tests (${{ matrix.shard }}/2) runs-on: ubuntu-latest @@ -380,6 +370,7 @@ jobs: JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation API_KEY_SECRET: ci-test-api-key-secret-long DISABLE_SQLITE_AUTO_BACKUP: "true" + OMNIROUTE_PLAYWRIGHT_SKIP_BUILD: "1" steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 @@ -443,9 +434,10 @@ jobs: - lint - i18n - pr-test-policy - - advanced-security + - build - package-artifact + - electron-package-smoke - test-unit - node-24-compat - test-coverage @@ -485,7 +477,7 @@ jobs: echo "|-----|--------|" >> "$GITHUB_STEP_SUMMARY" echo "| Lint | $(status '${{ needs.lint.result }}') |" >> "$GITHUB_STEP_SUMMARY" echo "| PR Test Policy | $(status '${{ needs.pr-test-policy.result }}') |" >> "$GITHUB_STEP_SUMMARY" - echo "| Advanced Security | $(status '${{ needs.advanced-security.result }}') |" >> "$GITHUB_STEP_SUMMARY" + echo "| SonarQube | $(status '${{ needs.sonarqube.result }}') |" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" @@ -494,6 +486,7 @@ jobs: echo "|-----|--------|" >> "$GITHUB_STEP_SUMMARY" echo "| Build Matrix | $(status '${{ needs.build.result }}') |" >> "$GITHUB_STEP_SUMMARY" echo "| Package Artifact | $(status '${{ needs.package-artifact.result }}') |" >> "$GITHUB_STEP_SUMMARY" + echo "| Electron Package Smoke | $(status '${{ needs.electron-package-smoke.result }}') |" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "## 🧪 Tests" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/electron-release.yml b/.github/workflows/electron-release.yml index 16d8eceb6d..ef78cdd77b 100644 --- a/.github/workflows/electron-release.yml +++ b/.github/workflows/electron-release.yml @@ -88,6 +88,18 @@ jobs: - name: Install dependencies run: npm ci + env: + NPM_CONFIG_LEGACY_PEER_DEPS: true + + - name: Sanitize Windows home directory + if: runner.os == 'Windows' + shell: bash + run: | + # The default USERPROFILE contains junction points (Application Data) + # that cause EPERM errors during Next.js standalone build glob scans. + # Create a clean temp profile directory to avoid this. + mkdir -p "$RUNNER_TEMP/home" + echo "USERPROFILE=$RUNNER_TEMP/home" >> $GITHUB_ENV - name: Build Next.js standalone env: @@ -121,6 +133,23 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npm run build:${{ matrix.target }} + - name: Smoke packaged Electron app + if: matrix.platform != 'linux' + # Windows CI: requestSingleInstanceLock() fails due to USERPROFILE + # sanitization needed for the build step. Smoke is best-effort there. + continue-on-error: ${{ matrix.platform == 'windows' }} + env: + ELECTRON_SMOKE_TIMEOUT_MS: 60000 + ELECTRON_SMOKE_STREAM_LOGS: "1" + run: npm run electron:smoke:packaged + + - name: Smoke packaged Electron app (Linux) + if: matrix.platform == 'linux' + env: + ELECTRON_SMOKE_TIMEOUT_MS: 60000 + ELECTRON_SMOKE_STREAM_LOGS: "1" + run: xvfb-run -a npm run electron:smoke:packaged + - name: Collect installers shell: bash run: | diff --git a/.gitignore b/.gitignore index 783740a7d7..8fcc4514c3 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,7 @@ docs/* !docs/CONTRIBUTING.md !docs/USER_GUIDE.md !docs/API_REFERENCE.md +!docs/TERMUX_GUIDE.md !docs/TROUBLESHOOTING.md !docs/EXECUTION_CONTEXT_PROVIDER_SYNC.md !docs/TASK_NEBIUS_BACKEND_ENABLEMENT.md @@ -94,6 +95,8 @@ docs/* !docs/screenshots/ !docs/i18n/ !docs/i18n/** +!docs/features/ +!docs/features/** !docs/A2A-SERVER.md !docs/AUTO-COMBO.md !docs/MCP-SERVER.md @@ -116,6 +119,7 @@ test-results/ playwright-report/ blob-report/ cloud/ +.tmp/ # Security Analysis (standalone project with own git) security-analysis/ @@ -176,3 +180,6 @@ bin/omniroute.mjs .omc/ audit-report.json bun.lock + +# Private environment variables for .http-client +http-client.private.env.json diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..e746658575 --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +# @lobehub/icons declares UI peers that are not needed by our deep icon imports. +# Keeping peer auto-install disabled prevents npm from pulling @lobehub/ui/mermaid +# back into the tree and reopening npm audit findings for unused packages. +legacy-peer-deps=true diff --git a/.omc/project-memory.json b/.omc/project-memory.json deleted file mode 100644 index 7018fa00e1..0000000000 --- a/.omc/project-memory.json +++ /dev/null @@ -1,250 +0,0 @@ -{ - "version": "1.0.0", - "lastScanned": 1775016362438, - "projectRoot": "/home/openclaw/omniroute-src", - "techStack": { - "languages": [ - { - "name": "JavaScript/TypeScript", - "version": ">=18.0.0 <24.0.0", - "confidence": "high", - "markers": ["package.json"] - }, - { - "name": "TypeScript", - "version": null, - "confidence": "high", - "markers": ["tsconfig.json"] - } - ], - "frameworks": [ - { - "name": "express", - "version": "5.2.1", - "category": "backend" - }, - { - "name": "next", - "version": "16.0.10", - "category": "fullstack" - }, - { - "name": "react", - "version": "19.2.4", - "category": "frontend" - }, - { - "name": "react-dom", - "version": "19.2.4", - "category": "frontend" - }, - { - "name": "@playwright/test", - "version": "1.58.2", - "category": "testing" - }, - { - "name": "vitest", - "version": "4.0.18", - "category": "testing" - } - ], - "packageManager": "npm", - "runtime": "Node.js 18.0.024.0.0" - }, - "build": { - "buildCommand": "npm run build", - "testCommand": "npm test", - "lintCommand": "npm run lint", - "devCommand": "npm run dev", - "scripts": { - "dev": "node scripts/run-next.mjs dev", - "build": "node scripts/build-next-isolated.mjs", - "build:cli": "node scripts/prepublish.mjs", - "start": "node scripts/run-next.mjs start", - "lint": "eslint .", - "electron:dev": "concurrently \"npm run dev\" \"wait-on http://localhost:20128 && cd electron && npm run dev\"", - "electron:build": "npm run build && cd electron && npm run build", - "electron:build:win": "npm run build && cd electron && npm run build:win", - "electron:build:mac": "npm run build && cd electron && npm run build:mac", - "electron:build:linux": "npm run build && cd electron && npm run build:linux", - "test": "node --import tsx/esm --test tests/unit/*.test.mjs", - "test:unit": "node --import tsx/esm --test tests/unit/*.test.mjs", - "test:plan3": "node --import tsx/esm --test tests/unit/plan3-p0.test.mjs", - "test:fixes": "node --import tsx/esm --test tests/unit/fixes-p1.test.mjs", - "test:security": "node --import tsx/esm --test tests/unit/security-fase01.test.mjs", - "check:cycles": "node scripts/check-cycles.mjs", - "check:route-validation:t06": "node scripts/check-route-validation.mjs", - "check:any-budget:t11": "node scripts/check-t11-any-budget.mjs", - "check:docs-sync": "node scripts/check-docs-sync.mjs", - "typecheck:core": "tsc --pretty false -p tsconfig.typecheck-core.json", - "typecheck:noimplicit:core": "tsc --pretty false -p tsconfig.typecheck-noimplicit-core.json", - "test:integration": "node --import tsx/esm --test tests/integration/*.test.mjs", - "test:e2e": "node scripts/run-playwright-tests.mjs test tests/e2e/*.spec.ts", - "test:protocols:e2e": "node scripts/run-protocol-clients-tests.mjs", - "test:vitest": "vitest run open-sse/mcp-server/__tests__/*.test.ts open-sse/services/autoCombo/__tests__/*.test.ts", - "test:ecosystem": "node scripts/run-ecosystem-tests.mjs", - "test:coverage": "c8 --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 55 --lines 55 --functions 55 --branches 60 node --import tsx/esm --test tests/unit/*.test.mjs", - "test:coverage:legacy": "c8 --exclude=open-sse --check-coverage --lines 50 --functions 50 --branches 50 node --import tsx/esm --test tests/unit/*.test.mjs", - "coverage:report": "c8 report --exclude=tests/** --exclude=**/*.test.* --reporter=text --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov", - "coverage:report:legacy": "c8 report --exclude=open-sse --reporter=text --reporter=text-summary", - "test:all": "npm run test:unit && npm run test:vitest && npm run test:ecosystem && npm run test:e2e", - "check": "npm run lint && npm run test", - "prepublishOnly": "npm run build:cli", - "postinstall": "node scripts/postinstall.mjs", - "prepare": "husky", - "system-info": "node scripts/system-info.mjs" - } - }, - "conventions": { - "namingStyle": "camelCase", - "importStyle": null, - "testPattern": null, - "fileOrganization": null - }, - "structure": { - "isMonorepo": true, - "workspaces": ["open-sse"], - "mainDirectories": ["bin", "docs", "public", "scripts", "src", "tests"], - "gitBranches": { - "defaultBranch": "main", - "branchingStrategy": null - } - }, - "customNotes": [], - "directoryMap": { - "bin": { - "path": "bin", - "purpose": "Executable scripts", - "fileCount": 3, - "lastAccessed": 1775016362426, - "keyFiles": ["mcp-server.mjs", "omniroute.mjs", "reset-password.mjs"] - }, - "docs": { - "path": "docs", - "purpose": "Documentation", - "fileCount": 14, - "lastAccessed": 1775016362426, - "keyFiles": [ - "A2A-SERVER.md", - "API_REFERENCE.md", - "ARCHITECTURE.md", - "AUTO-COMBO.md", - "CLI-TOOLS.md" - ] - }, - "electron": { - "path": "electron", - "purpose": null, - "fileCount": 5, - "lastAccessed": 1775016362431, - "keyFiles": ["README.md", "main.js", "package.json", "preload.js", "types.d.ts"] - }, - "images": { - "path": "images", - "purpose": null, - "fileCount": 1, - "lastAccessed": 1775016362434, - "keyFiles": ["omniroute.png"] - }, - "logs": { - "path": "logs", - "purpose": null, - "fileCount": 3, - "lastAccessed": 1775016362434, - "keyFiles": ["build_clean_tools.log", "build_debug.log", "build_force_clean.log"] - }, - "open-sse": { - "path": "open-sse", - "purpose": null, - "fileCount": 5, - "lastAccessed": 1775016362434, - "keyFiles": ["index.ts", "package.json", "tsconfig.json", "types.d.ts"] - }, - "public": { - "path": "public", - "purpose": "Public files", - "fileCount": 3, - "lastAccessed": 1775016362435, - "keyFiles": ["apple-touch-icon.svg", "favicon.svg", "icon-192.svg"] - }, - "scripts": { - "path": "scripts", - "purpose": "Build/utility scripts", - "fileCount": 23, - "lastAccessed": 1775016362435, - "keyFiles": [ - "bootstrap-env.mjs", - "build-next-isolated.mjs", - "check-cycles.mjs", - "check-docs-sync.mjs", - "check-route-validation.mjs" - ] - }, - "src": { - "path": "src", - "purpose": "Source code", - "fileCount": 4, - "lastAccessed": 1775016362435, - "keyFiles": ["instrumentation-node.ts", "instrumentation.ts", "proxy.ts", "server-init.ts"] - }, - "tests": { - "path": "tests", - "purpose": "Test files", - "fileCount": 0, - "lastAccessed": 1775016362435, - "keyFiles": [] - }, - "electron/assets": { - "path": "electron/assets", - "purpose": "Static assets", - "fileCount": 4, - "lastAccessed": 1775016362436, - "keyFiles": ["icon.icns", "icon.ico", "icon.png"] - }, - "open-sse/config": { - "path": "open-sse/config", - "purpose": "Configuration files", - "fileCount": 17, - "lastAccessed": 1775016362436, - "keyFiles": ["audioRegistry.ts", "cliFingerprints.ts", "codexInstructions.ts"] - }, - "open-sse/services": { - "path": "open-sse/services", - "purpose": "Business logic services", - "fileCount": 35, - "lastAccessed": 1775016362437, - "keyFiles": ["accountFallback.ts", "accountSelector.ts", "apiKeyRotator.ts"] - }, - "src/app": { - "path": "src/app", - "purpose": "Application code", - "fileCount": 7, - "lastAccessed": 1775016362438, - "keyFiles": ["error.tsx", "global-error.tsx", "globals.css"] - }, - "src/lib": { - "path": "src/lib", - "purpose": "Library code", - "fileCount": 30, - "lastAccessed": 1775016362438, - "keyFiles": ["apiBridgeServer.ts", "apiKeyExposure.ts", "cacheControlSettings.ts"] - }, - "src/middleware": { - "path": "src/middleware", - "purpose": "Middleware", - "fileCount": 1, - "lastAccessed": 1775016362438, - "keyFiles": ["promptInjectionGuard.ts"] - }, - "src/models": { - "path": "src/models", - "purpose": "Data models", - "fileCount": 1, - "lastAccessed": 1775016362438, - "keyFiles": ["index.ts"] - } - }, - "hotPaths": [], - "userDirectives": [] -} diff --git a/.omc/sessions/53c002c3-36a6-47c3-a52d-a8f756c264eb.json b/.omc/sessions/53c002c3-36a6-47c3-a52d-a8f756c264eb.json deleted file mode 100644 index f3dfd0f299..0000000000 --- a/.omc/sessions/53c002c3-36a6-47c3-a52d-a8f756c264eb.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "session_id": "53c002c3-36a6-47c3-a52d-a8f756c264eb", - "ended_at": "2026-04-01T04:06:04.924Z", - "reason": "prompt_input_exit", - "agents_spawned": 0, - "agents_completed": 0, - "modes_used": [] -} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..caa4ab6868 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,31 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug Dev Server", + "type": "node", + "request": "launch", + "runtimeExecutable": "${env:HOME}/.nvm/versions/node/v22.22.2/bin/node", + "program": "${workspaceFolder}/scripts/run-next.mjs", + "args": ["dev"], + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"] + }, + { + "name": "Debug Prod Server", + "type": "node", + "request": "launch", + "program": "${workspaceFolder}/scripts/run-next.mjs", + "args": ["start"], + "console": "integratedTerminal", + "skipFiles": ["/**", "node_modules/**"] + }, + { + "name": "Attach to Running Server", + "type": "node", + "request": "attach", + "port": 9229, + "skipFiles": ["/**", "node_modules/**"] + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index fae48f28c7..442a2bedab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,5 +17,33 @@ "level": "off" } }, - "git.ignoreLimitWarning": true + "git.ignoreLimitWarning": true, + // "files.exclude": { + // "**/_references": true, + // "**/_mono_repo": true, + // "**/electron": true, + // "**/node_modules": true, + // "**/.next": true, + // "**/coverage": true, + // "**/omniroute-*.tgz": true, + // "**/_tasks": true + // }, + "files.watcherExclude": { + "**/_references/**": true, + "**/_mono_repo/**": true, + "**/electron/**": true, + "**/node_modules/**": true, + "**/.next/**": true, + "**/coverage/**": true, + "**/_tasks/**": true + }, + "search.exclude": { + "**/_references": true, + "**/_mono_repo": true, + "**/electron": true, + "**/node_modules": true, + "**/.next": true, + "**/coverage": true, + "**/_tasks": true + } } diff --git a/AGENTS.md b/AGENTS.md index f80682d4f0..b120f30bcd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,10 +3,10 @@ ## Project Unified AI proxy/router — route any LLM through one endpoint. Multi-provider support -with **100+ providers** (OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Fireworks, +with **160+ providers** (OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Fireworks, Cohere, NVIDIA, Cerebras, Pollinations, Puter, Cloudflare AI, HuggingFace, DeepInfra, SambaNova, Meta Llama API, Moonshot AI, AI21 Labs, Databricks, Snowflake, and many more) -with **MCP Server** (25 tools), **A2A v0.3 Protocol**, and **Electron desktop app**. +with **MCP Server** (29 tools), **A2A v0.3 Protocol**, and **Electron desktop app**. ## Stack @@ -15,7 +15,7 @@ with **MCP Server** (25 tools), **A2A v0.3 Protocol**, and **Electron desktop ap - **Database**: better-sqlite3 (SQLite) — `DATA_DIR` configurable, default `~/.omniroute/` - **Streaming**: SSE via `open-sse` internal workspace package - **Styling**: Tailwind CSS v4 -- **i18n**: next-intl with 30 languages +- **i18n**: next-intl with 40+ languages - **Desktop**: Electron (cross-platform: Windows, macOS, Linux) - **Schemas**: Zod v4 for all API / MCP input validation @@ -132,7 +132,7 @@ All persistence uses SQLite through domain-specific modules: `backup.ts`, `proxies.ts`, `prompts.ts`, `webhooks.ts`, `detailedLogs.ts`, `domainState.ts`, `registeredKeys.ts`, `quotaSnapshots.ts`, `modelComboMappings.ts`, `cliToolState.ts`, `encryption.ts`, `readCache.ts`, `secrets.ts`, `stateReset.ts`, -`contextHandoffs.ts`. +`contextHandoffs.ts`, `compression.ts`. Schema migrations live in `db/migrations/` and run via `migrationRunner.ts`. `src/lib/localDb.ts` is a **re-export layer only** — never add logic there. @@ -142,7 +142,7 @@ Schema migrations live in `db/migrations/` and run via `migrationRunner.ts`. journaling. `SCHEMA_SQL` defines 15 base tables. Helpers: `rowToCamel`, `encryptConnectionFields`. - **`migrationRunner.ts`**: Applies versioned SQL files from `db/migrations/` inside transactions. Tracks applied migrations in `_omniroute_migrations` table. -- **Migrations**: 21 files (`001_initial_schema.sql` → `021_combo_call_log_targets.sql`). +- **Migrations**: 22 files (`001_initial_schema.sql` → `022_compression_settings.sql`). Each migration is idempotent and runs in a transaction. - **Domain modules** import `getDbInstance()` from `core.ts` for all CRUD operations. Each module owns a specific table/set of tables (e.g., `providers.ts` → `provider_connections`, @@ -213,7 +213,7 @@ Zod schemas, and unit tests aligned when editing. - **Free** (4): Qoder AI, Qwen Code, Gemini CLI (deprecated), Kiro AI - **OAuth** (8): Claude Code, Antigravity, Codex, GitHub Copilot, Cursor, Kimi Coding, Kilo Code, Cline -- **API Key** (91): OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Perplexity, +- **API Key** (120+): OpenAI, Anthropic, Gemini, DeepSeek, Groq, xAI, Mistral, Perplexity, Together, Fireworks, Cerebras, Cohere, NVIDIA, Nebius, SiliconFlow, Hyperbolic, HuggingFace, OpenRouter, Vertex AI, Cloudflare AI, Scaleway, AI/ML API, Pollinations, Puter, Longcat, Alibaba, Kimi, Minimax, Blackbox, Synthetic, Kilo Gateway, @@ -224,7 +224,10 @@ Zod schemas, and unit tests aligned when editing. Meta Llama API, v0 (Vercel), Morph, Featherless AI, FriendliAI, LlamaGate, Galadriel, Weights & Biases Inference, Volcengine, AI21 Labs, Venice.ai, Codestral, Upstage, Maritalk, Xiaomi MiMo, Inference.net, NanoGPT, Predibase, - Bytez, Heroku AI, Databricks, Snowflake Cortex, GigaChat (Sber), and more. + Bytez, Heroku AI, Databricks, Snowflake Cortex, GigaChat (Sber), CrofAI, + AgentRouter, ChatGPT Web, Baidu Qianfan, AWS Polly, RunwayML, GitLab Duo, + Amazon Q, Empower, Poe, and many more. +- **Self-Hosted** (8+): LM Studio, vLLM, Lemonade, Llamafile, Triton, Docker Model Runner, Xinference, Oobabooga - **Custom**: OpenAI-compatible (`openai-compatible-*`) and Anthropic-compatible (`anthropic-compatible-*`) prefixes Providers are registered in `src/shared/constants/providers.ts` with Zod validation at module load. @@ -282,7 +285,24 @@ Includes request/response translators with helpers for image handling. `autoCombo/`, `intentClassifier.ts`, `taskAwareRouter.ts`, `thinkingBudget.ts`, `contextManager.ts`, `modelDeprecation.ts`, `modelFamilyFallback.ts`, `emergencyFallback.ts`, `workflowFSM.ts`, `backgroundTaskDetector.ts`, `ipFilter.ts`, -`signatureCache.ts`, `volumeDetector.ts`, `contextHandoff.ts`, and more. +`signatureCache.ts`, `volumeDetector.ts`, `contextHandoff.ts`, `compression/` (prompt +compression pipeline), and more. + +#### Prompt Compression Pipeline (`compression/`) + +Modular prompt compression that runs proactively before the existing reactive context manager. + +- **`strategySelector.ts`**: Selects compression mode based on config, combo overrides, auto-trigger + thresholds. Priority: combo override > auto-trigger > default mode > off. +- **`lite.ts`**: 5 lite-mode techniques: `collapseWhitespace`, `dedupSystemPrompt`, + `compressToolResults`, `removeRedundantContent`, `replaceImageUrls`. Target: 10-15% savings at + <1ms latency. +- **`stats.ts`**: Per-request compression stats tracking (original tokens, compressed tokens, + savings %, techniques used). +- **`types.ts`**: `CompressionMode` (off/lite/standard/aggressive/ultra), `CompressionConfig`, + `CompressionStats`, `CompressionResult`. +- DB settings in `src/lib/db/compression.ts`, API route at `src/app/api/settings/compression/`. +- Phase 1 implements lite mode only; standard/aggressive/ultra are placeholders for Phase 2. #### Combo Routing Engine (`combo.ts`) @@ -303,12 +323,14 @@ Policy engine modules: `policyEngine.ts`, `comboResolver.ts`, `costRules.ts`, ### MCP Server (`open-sse/mcp-server/`) -25 tools, 3 transports (stdio / SSE / Streamable HTTP). Scoped auth (10 scopes), Zod schemas. +29 tools, 3 transports (stdio / SSE / Streamable HTTP). Scoped auth (10 scopes), Zod schemas. -**Core tools** (18): get_health, list_combos, get_combo_metrics, switch_combo, check_quota, -route_request, cost_report, list_models_catalog, simulate_route, set_budget_guard, +**Core tools** (20): get_health, list_combos, get_combo_metrics, switch_combo, check_quota, +route_request, cost_report, list_models_catalog, web_search, simulate_route, set_budget_guard, set_routing_strategy, set_resilience_profile, test_combo, get_provider_metrics, -best_combo_for_task, explain_route, get_session_snapshot, sync_pricing. +best_combo_for_task, explain_route, get_session_snapshot, db_health_check, sync_pricing. + +**Cache tools** (2): cache_stats, cache_flush. **Memory tools** (3): memory_search, memory_add, memory_clear. @@ -413,3 +435,4 @@ Request middleware including `promptInjectionGuard.ts`. - **Provider constants** validated at module load via Zod (`src/shared/validation/providerSchema.ts`) - **Pricing data** syncs from LiteLLM via `src/lib/pricingSync.ts` - **Memory/Skills** are cross-cutting: affect MCP tools, request pipeline, and A2A skills +- **⛔ NEVER close a contributor's PR** after using their code — always merge via GitHub so they get credit. See `.agents/workflows/review-prs.md` for full policy. diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e7189ab30..39fc46cfe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,494 @@ --- +## [3.7.7] — 2026-04-30 + +### ✨ New Features + +- **Analytics Custom Filters:** Added custom date range selection, API key filtering, and NULL key analytics backfilling to the Costs Dashboard (#1830) + +### 🐛 Bug Fixes + +- **Rate-limit Watchdog:** Implemented a new rate-limit watchdog with environment override capabilities and Stage Tracing to prevent and diagnose silent wedges (#1828) + +### 🛠️ Maintenance + +- **Workflow:** Fixed the changelog extraction logic to accurately capture GitHub release descriptions + +--- + +## [3.7.6] — 2026-04-30 + +### ✨ New Features + +- **feat(api-keys):** add rename support in the permissions modal — editable key name field with validation (#1796) +- **feat(chatgpt-web):** support `thinking_effort` parameter (Standard/Extended) for thinking-capable models (#1821) +- **feat(dashboard):** implement remaining v3.7.6 dashboard features — Costs overview, Translator pipeline, and Endpoint tabs improvements +- **feat(tools):** inject fallback tool names to prevent upstream 400 errors on providers that require tool names (#1775) +- **feat(db):** auto-restore probe-failed database on startup to prevent data loss after failed upgrades (#1810) +- **feat(analytics):** add cost-based usage insights and activity streaks in the analytics dashboard + +### 🔒 Security + +- **fix(security):** resolve ReDoS vulnerability in Codex executor regex patterns (#1797, #1789) + +### 🐛 Bug Fixes + +- **fix(stability):** resolve codex input validation, enable combo circuit breaker, and fix broken unit tests (#1804, #1805) +- **fix(stability):** safely cast inputs to strings before calling `.trim()` to avoid crashes on numeric fields in proxy modal (#1825) +- **fix(stability):** clear active requests and recover providers after connection failures (#1824) +- **fix(xiaomi-mimo):** update models to V2.5, fix Token Plan validation and default region (#1823) +- **fix(codex):** omit compact client metadata to prevent upstream rejections (#1822) +- **fix(dashboard):** fix endpoint visibility, A2A status display, and API catalog consistency (#1806) +- **fix(analytics):** use pure SQL aggregations — no history rows loaded into memory (#1802) +- **fix(dashboard):** correct `loadPresets` ReferenceError in CostOverviewTab +- **fix(mitm):** enforce transparent interception on port 443 only + +### 🧹 Chores + +- **chore(workflow):** mandate implementation plan generation in `/resolve-issues` workflow before coding +- **chore(release):** expand contributor credits to 155 PRs across full project history + +### 🏆 Community Contributors Acknowledgment + +We identified that **155 community PRs** across the entire project history (from inception through v3.7.5) were manually integrated into release branches but closed instead of properly merged through GitHub, preventing contributors from receiving merge credit on their profiles. We sincerely apologize for this oversight and have since updated our workflows to ensure this never happens again. + +**The following contributors had their code and ideas integrated across multiple releases without proper merge credit. Thank you for your invaluable contributions to OmniRoute:** + +| Contributor | PRs (Total) | All Contributions | +| :----------------------------------------------------------- | :---------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [@rdself](https://github.com/rdself) | 28 | #542, #705, #717, #737, #738, #841, #851, #853, #875, #880, #888, #891, #903, #904, #974, #1069, #1089, #1196, #1267, #1272, #1299, #1300, #1356, #1357, #1441, #1443, #1549, #1742 | +| [@oyi77](https://github.com/oyi77) | 27 | #644, #672, #700, #850, #859, #862, #868, #874, #881, #883, #908, #926, #931, #983, #990, #1019, #1020, #1021, #1103, #1281, #1286, #1363, #1368, #1377, #1411, #1689, #1717 | +| [@clousky2020](https://github.com/clousky2020) | 15 | #1244, #1323, #1365, #1366, #1408, #1442, #1484, #1595, #1598, #1599, #1611, #1618, #1620, #1621, #1644 | +| [@benzntech](https://github.com/benzntech) | 8 | #158, #1264, #1435, #1436, #1437, #1440, #1444, #1677 | +| [@kang-heewon](https://github.com/kang-heewon) | 5 | #530, #854, #884, #1235, #1574 | +| [@herjarsa](https://github.com/herjarsa) | 4 | #1472, #1474, #1477, #1480 | +| [@backryun](https://github.com/backryun) | 4 | #1358, #1609, #1627, #1722 | +| [@tombii](https://github.com/tombii) | 4 | #708, #856, #900, #1013 | +| [@christopher-s](https://github.com/christopher-s) | 3 | #868, #885, #992 | +| [@zen0bit](https://github.com/zen0bit) | 3 | #561, #650, #912 | +| [@k0valik](https://github.com/k0valik) | 3 | #554, #587, #596 | +| [@zhangqiang8vip](https://github.com/zhangqiang8vip) | 2 | #470, #575 | +| [@wlfonseca](https://github.com/wlfonseca) | 2 | #997, #1016 | +| [@RaviTharuma](https://github.com/RaviTharuma) | 2 | #1188, #1277 | +| [@prakersh](https://github.com/prakersh) | 2 | #419, #480 | +| [@payne0420](https://github.com/payne0420) | 2 | #1593, #1670 | +| [@only4copilot](https://github.com/only4copilot) | 2 | #855, #1039 | +| [@jay77721](https://github.com/jay77721) | 2 | #581, #582 | +| [@hijak](https://github.com/hijak) | 2 | #295, #578 | +| [@hartmark](https://github.com/hartmark) | 2 | #1494, #1500 | +| [@defhouse](https://github.com/defhouse) | 2 | #906, #946 | +| [@xiaoge1688](https://github.com/xiaoge1688) | 1 | #1304 | +| [@xandr0s](https://github.com/xandr0s) | 1 | #1376 | +| [@willbnu](https://github.com/willbnu) | 1 | #882 | +| [@slewis3600](https://github.com/slewis3600) | 1 | #1624 | +| [@sergey-v9](https://github.com/sergey-v9) | 1 | #594 | +| [@razllivan](https://github.com/razllivan) | 1 | #987 | +| [@nmime](https://github.com/nmime) | 1 | #1271 | +| [@Moutia-Ben-Yahia](https://github.com/Moutia-Ben-Yahia) | 1 | #1663 | +| [@Mind-Dragon](https://github.com/Mind-Dragon) | 1 | #467 | +| [@mercs2910](https://github.com/mercs2910) | 1 | #1001 | +| [@MAINER4IK](https://github.com/MAINER4IK) | 1 | #196 | +| [@luandiasrj](https://github.com/luandiasrj) | 1 | #996 | +| [@knopki](https://github.com/knopki) | 1 | #1434 | +| [@kfiramar](https://github.com/kfiramar) | 1 | #389 | +| [@ken2190](https://github.com/ken2190) | 1 | #166 | +| [@keith8496](https://github.com/keith8496) | 1 | #569 | +| [@jonesfernandess](https://github.com/jonesfernandess) | 1 | #1118 | +| [@JasonLandbridge](https://github.com/JasonLandbridge) | 1 | #1626 | +| [@i1hwan](https://github.com/i1hwan) | 1 | #1386 | +| [@Gorchakov-Pressure](https://github.com/Gorchakov-Pressure) | 1 | #754 | +| [@foxy1402](https://github.com/foxy1402) | 1 | #934 | +| [@dt418](https://github.com/dt418) | 1 | #896 | +| [@dhaern](https://github.com/dhaern) | 1 | #1647 | +| [@DavyMassoneto](https://github.com/DavyMassoneto) | 1 | #211 | +| [@dail45](https://github.com/dail45) | 1 | #1413 | +| [@congvc-dev](https://github.com/congvc-dev) | 1 | #1569 | +| [@be0hhh](https://github.com/be0hhh) | 1 | #1581 | +| [@andruwa13](https://github.com/andruwa13) | 1 | #1457 | +| [@AndrewDragonIV](https://github.com/AndrewDragonIV) | 1 | #898 | +| [@AndersonFirmino](https://github.com/AndersonFirmino) | 1 | #362 | +| [@alexsvdk](https://github.com/alexsvdk) | 1 | #1280 | +| [@abhinavjnu](https://github.com/abhinavjnu) | 1 | #550 | + +--- + +## [3.7.5] — 2026-04-29 + +### ✨ New Features + +- **feat(tunnels):** integrate native ngrok tunnel support with dashboard UI parity (#1753) + +### 🐛 Bug Fixes + +- **fix(dashboard):** add manual 'Clear All' button to terminate stalled long-running requests in Active Requests panel (#1799) +- **fix(schema):** remove empty string values from optional tool parameters to prevent upstream validation errors (#1674) +- **fix(providers):** ensure proper streaming cleanup and semaphore release to prevent stalls with nanoGPT (#1781) +- **fix(db):** wrap quota_snapshots access in try/catch to gracefully handle pending database migrations (#1784) +- **feat(providers):** add support for glm-cn (BigModel) provider (#1770) +- **fix(grok-web):** fix Grok validator and cookie parsing (#1793) +- **fix(antigravity):** scrub internal OmniRoute headers (#1794) +- **fix(chatgpt-web):** restore validator + expand model catalog to ChatGPT Plus tier (#1792) +- **fix(codex):** stabilize Copilot responses replay state (#1791) +- **fix(antigravity):** cap Claude bridge output tokens (#1785) +- **fix(schema):** strip `default` properties from tool-call JSON schemas during egress to prevent injection errors (#1782) +- **fix(db):** add `quota_snapshots` table to core DB schema initialization to prevent startup failures on fresh installs +- **fix(models):** apply blocked providers filter to non-chat catalog models (image, embedding, audio, etc.) (#1752) +- **fix(antigravity):** stabilize streaming payload parsing and deduplicate usage/model metadata refreshes (#1748) +- **fix(antigravity):** normalize Gemini bridge payloads — sanitize tool names, cap output tokens, and fix thinking budget (#1769) +- **fix(sse):** propagate AbortSignal to pre-fetch semaphore and rate-limit awaits to prevent memory leaks (#1771) +- **fix(models):** fix model sync import handling — separate synced models from custom models to prevent data loss (#1755) +- **fix(codex):** improve VS Code Copilot /responses reasoning and tool follow-ups (#1750) +- **fix(memory):** resolve build issues and implement memory UPSERT logic to prevent duplicate entries (#1763) +- **fix(kiro):** support organization IDC OAuth with regional endpoints and refresh (#1754) +- **fix(combo):** include 429 in provider circuit breaker to stop infinite retry loops on exhausted quotas (#1767) +- **fix(claude):** respect client-set thinking/effort params — only inject adaptive thinking and high effort when the client hasn't explicitly set them, preventing forced quota drain on Claude Max accounts (#1761) +- **fix(blackbox-web):** correct cookie name and populate session/subscription fields (#1776) +- **fix(codex):** align client identity metadata (#1778) +- **fix(claude):** fix support for claude-cli using Gemini provider (#1779) +- **test(reasoning-cache):** isolate DB state using mkdtempSync to prevent 401 middleware errors + +### 🛠️ Maintenance + +- **chore(docs):** add MseeP.ai security assessment badge to README (#1727) +- **chore(xiaomi):** update Xiaomi provider model list (#1759) +- **chore(db):** move DB health endpoint to management API (#1757) +- **chore(ui):** speed up endpoint initial render with background task loading (#1760) +- **chore(workflows):** add strict PR contributor credit policy to prevent future merge credit loss + +--- + +## [3.7.4] — 2026-04-28 + +### ✨ New Features + +- **feat(ui):** add endpoint tunnel visibility settings (#1743) +- **feat(cli):** refresh CLI fingerprint provider profiles (#1746) +- **feat(proxy):** implement bulk proxy import via pipe-delimited parser with update-or-create (upsert) logic and real-time preview table +- **feat(pwa):** add fullscreen installable PWA with manifest, service worker, and cross-platform app icons (#1728) + +### 🔒 Security + +- **security:** replace insecure `Math.random` with `crypto.getRandomValues` for fallback UUID generation to resolve CodeQL CWE-338 finding (#182) + +### 🐛 Bug Fixes + +- **fix(cc-compatible):** fix CC-compatible relay format and UI copy (#1742) +- **fix(codex):** normalize max reasoning effort for Codex routing (#1744) +- **fix(claude-code):** fix Claude Code gateway config helper (#1745) +- **fix(db):** reconcile legacy `create_reasoning_cache` migration tracking to prevent version shadowing on `032` and resolve startup warnings (#1734) +- **fix(db):** intercept `007` migration to use idempotent `IF NOT EXISTS` logic via `PRAGMA table_info`, preventing syntax crashes on fresh installs (#1733) +- **fix(cc-compatible):** preserve Claude Code system skeleton to prevent request rejection by strict compatible upstream providers (#1740) + +- **fix(providers):** add API key validation for image-only providers and fix Stability AI requests to use `multipart/form-data` instead of JSON (#1726) +- **fix(codex):** preserve `previous_response_id` and `conversation_id` fields when input array is empty to prevent schema validation errors (#1729) +- **fix(searxng):** bypass UI validation block when `apiKeyOptional` is true and fix typing errors in provider dashboard to allow saving search providers without credentials (#1721) +- **fix(proxy):** disable HTTP keep-alive and pipelining in Undici proxy dispatcher to prevent "Socket hang up" rotation failures +- **stream:** correctly identify `thought` and `error` blocks in Antigravity/Gemini SSE streams to prevent premature 502 timeouts (#1725, #1705) + +### 🛠️ Maintenance + +- **workflow:** add phase 4 release monitoring instructions to `/generate-release` workflow +- **test:** fix typescript compilation errors in unit tests to keep CI typecheck pipeline fully green +- **test:** update responses store expectations for empty input arrays + +--- + +## [3.7.3] — 2026-04-28 + +### 🐛 Bug Fixes + +- **fix(claude):** strip existing billing headers from system array before injecting to prevent Anthropic prompt cache misses — stacked `x-anthropic-billing-header` blocks invalidated prefix matching, causing ~100% cache_create instead of cache_read (#1712) +- **fix(claude):** strip `output_config.format` for non-Anthropic Claude-compatible providers during passthrough — third-party Claude endpoints (MiniMax, DeepSeek via aggregators) reject structured output fields with 400 errors (#1719) +- **fix(combo):** set terminal error state on response quality validation failure — prevents misleading `ALL_ACCOUNTS_INACTIVE` 503 when the real issue is response quality validation (#1707, #1710) +- **fix(combo):** treat combo fallback as target-level orchestration — all non-ok responses (including generic 400s) now fall through to the next target instead of being terminal; removes complex bad-request allowlist regex (#1713) +- **fix(codex):** restore namespace MCP tools and hosted-tool whitelist — regression from #1581 that silently dropped all MCP tool groups and Responses-API hosted tools (#1715) +- **fix(codex):** add neutral instructions for bare chat requests — Codex Responses backend rejects requests without `instructions`, making Codex unusable for normal chat (#1709) +- **fix(proxy):** wrap proxy assignment queries in try-catch for missing `proxy_assignments` table — Electron installs where migration 004 hasn't run no longer crash with `no such table` error (#1706) +- **fix(migration):** improve Windows file URL path resolution in migration runner — adds direct URL path extraction and `process.cwd()` fallback for CI-built bundles with leaked build-time paths (#1704) +- **fix(ui):** fix light mode active request payload modal — add missing `--color-card` theme token, use opaque `bg-surface` instead of translucent `bg-card/70`, add backdrop blur (#1714) + +### 🔄 Updates + +- **chore(image-models):** refresh image generation model registry — replace stale FLUX aliases with FLUX Kontext / FLUX.2 mappings, remove deprecated FLUX Redux/Depth/Canny variants (#1722) + +--- + +## [3.7.2] — 2026-04-28 + +### ✨ New Features + +- **feat(authz):** introduce centralized proxy-based authz pipeline and lifecycle policy (#1632) +- **feat(logs):** configure call log pipeline artifacts (#1650) +- **feat(network):** add guarded remote image fetch utility +- **feat(codex):** enable native Codex websocket responses on beta-gated models (#1658) +- **feat(muse-spark-web):** continue the same meta.ai conversation across turns (#1673) + +### 🐛 Bug Fixes + +- **fix(responses):** sanitize empty string placeholders from tool-call optional arguments in stream delta accumulation to avoid breaking strict clients (#1674) +- **fix(codex):** prevent unexpected protocol leakage and fabricated instructions on bare chat completion requests without tools (#1686) +- **fix(executors):** truncate tools array to 128 items max in GitHub Copilot and OpenCode executors to mitigate 400 Bad Request errors from upstream (#1687) +- **fix:** add body-read timeout to prevent stuck pending requests (#1680) +- **fix(rate-limit):** replace unsupported Bottleneck `maxWait` option with job-level `expiration` to prevent indefinite queue stalls (#1694) +- **fix(sse):** sanitize OpenAI tool schemas for strict upstream validators — strips null from enum arrays, normalizes tuple items, filters invalid required keys (#1692) +- **fix(stream):** fail zombie SSE streams before accepting response — returns 504 instead of hanging indefinitely, enables combo fallback (#1693) +- **fix(combo):** complete context truncation hotfix — cache getCombos() with 10s TTL, pass allCombosData to resolveComboTargets() for nested combo resolution, consolidate duplicated context overflow regex patterns (#1685) +- **fix(codex):** raise default quota threshold from 90% to 99% to avoid premature account blocking when usable quota remains (#1697) +- **fix(memory):** use `user` role for GLM/ZAI/Qianfan providers — providers with strict role constraints (no `system` role) now correctly receive memory context as a `user` message instead of a `system` message, preventing 422 validation errors (#1701) +- **fix(oauth):** target specific connection by ID on re-auth token exchange — prevents duplicate account creation when re-authenticating an existing OAuth connection (#1702 — thanks @namhhitvn) +- **feat(email-privacy):** integrate email visibility toggle in RequestLoggerV2 — log detail modal now respects global email privacy state, hiding email addresses by default (#1700 — thanks @namhhitvn) +- **fix(combo):** trigger fallback on Anthropic `Invalid signature in thinking block` errors instead of returning 400 directly (#1696) +- **fix:** combo retry loop stops immediately on client disconnect (499) (#1681) +- **fix(search):** support optional bearer auth for SearXNG (#1683) +- **fix(vision):** respect native GPT vision support — prevents VisionBridge from intercepting models that already handle images natively (#1678) +- **fix(qwen):** use `security.auth` format instead of `modelProviders` for Qwen Code config generation (#1677) +- **fix(codex):** remove stale websocket transport lookup that caused fallback errors (#1676) +- **fix(chatgpt-web):** bound tls-client native deadlocks so requests never hang forever (#1664) +- **fix(codex):** default gpt-5.5 to HTTP transport instead of WebSocket (#1660) +- **fix(codex):** [urgent] fix gpt-5.5 websocket transport and model labels (#1656) +- **fix(grokweb):** update Request and Response Specifications (#1655) +- **fix(blackbox-web):** set isPremium flag to true to enable premium model access (#1661) +- **fix(core):** avoid OpenAI stream options for Anthropic-compatible providers (#1654) +- **fix(electron):** resolve MCP server start failure on Windows (#1662) +- **fix(electron):** make Windows smoke test non-blocking (continue-on-error), pre-create userData dir for Windows + stream logs in CI, and add --no-sandbox and sandbox env for CI smoke tests +- **fix(codex):** fix `getWreqWebsocket` ReferenceError causing 502 on all Codex requests (#1652, #1653) +- **fix(codex):** default `store` to `false` — Codex OAuth backend rejects `store=true` (#1635) +- **fix(db):** add post-migration guards for missing `batches` table and `combos.sort_order` column on DB upgrades (#1648, #1657) +- **fix(db):** renumber duplicate migration `032` to prevent collision +- **fix(perplexity-web):** update API version and user-agent to match upstream requirements (#1666) +- **fix(docker):** copy SQLite migration files and explicitly trace in standalone build (#1665) +- **fix(muse-spark-web):** update to Meta's Ecto-era persisted query — fixes 502 `Unknown type "RewriteOptionsInput"` after Meta retired the Abra mutation (#1668) +- **fix(dev):** enable Turbopack by default and repair Codex CORS headers (#1669) +- **fix(authz):** restore `REQUIRE_API_KEY` support in clientApi policy +- **fix(auth):** align fallback API key format with test setup + +### 🛠️ Maintenance + +- **build(prepublish):** make Next.js build bundler configurable (webpack/turbopack) +- **ci:** align sonar analysis scope +- **ci:** stabilize release branch checks +- **ci:** remove expired advanced security scans job + +### 🧪 Tests + +- **test:** fix TypeScript configuration errors in plan3-p0.test.ts +- **test:** fix implicit any types across test suites +- **test:** disable type checking in flaky unit tests +- **test:** fix failing tests due to recent refactors +- **fix(tests):** align integration tests with authz pipeline refactor +- **fix(tests):** align test assertions with v3.7.2 source code changes +- **fix(tests):** CORS test now checks object body instead of entire file +- **fix(e2e):** fix E2E flakiness and implicit any type errors + +--- + +## [3.7.1] — 2026-04-26 + +### ✨ New Features + +- **feat(providers):** Add GPT-5.5 support to the Codex provider — includes 1.05M context window, tool calling, vision, and reasoning capabilities with proper pricing entries across `cx` and `openai` providers. Refactors `splitCodexReasoningSuffix()` into a shared helper for cleaner effort-level parsing (#1617 — thanks @Zhaba1337228). +- **feat(cli):** Add `omniroute reset-encrypted-columns` recovery command — nulls encrypted credential columns (`api_key`, `access_token`, `refresh_token`, `id_token`) in `provider_connections` while preserving provider metadata, giving users affected by #1622 a clean recovery path without losing configurations. +- **feat(i18n):** Expand locale coverage with nine new language packs (Bengali, Farsi, Gujarati, Indonesian, Marathi, Swahili, Tamil, Telugu, Urdu), bringing total language support from 32 to 41 locales. + +### 🐛 Bug Fixes + +- **fix(rate-limit):** Add per-model rate limiting for GitHub Copilot provider — a 429 on one model (e.g. `gpt-5.1-codex-max`) no longer locks the entire connection, matching the existing Gemini per-model quota pattern (#1624 — thanks @slewis3600). +- **fix(cli-tools):** Preserve existing OpenCode configuration (MCP servers, custom providers, comments) when saving OmniRoute settings — uses `jsonc-parser` for tree-preserving edits instead of destructive JSON roundtrip. Fix API key clipboard copy to use raw keys instead of masked placeholders. Add theme-aware OpenCode light/dark SVG logos (#1626 — thanks @JasonLandbridge). +- **fix(cli-tools):** Fix OpenCode guide step 3 `{{baseUrl}}` double-brace placeholder to use ICU-style `{baseUrl}` across all 41 locales, restoring next-intl interpolation (#1626). +- **fix(codex):** Make `wreq-js` native module import lazy and optional to prevent server crash on startup when the platform-specific binary is missing — affects pnpm installs, Docker Alpine, macOS ARM, and Windows (#1612, #1613, #1616). +- **fix(i18n):** Add 14 missing translation keys (`logs.runningRequests`, `logs.model`, `logs.provider`, `logs.account`, `logs.elapsed`, `logs.count`, `logs.payloads`, etc.) for the Active Requests panel across all locales. Replace 83 placeholder values in usage/evals namespace. Add 5 missing health namespace keys for rate limit status. +- **fix(encryption):** Prevent `STORAGE_ENCRYPTION_KEY` from being silently regenerated during `npm install -g` upgrades, which made all previously-encrypted provider credentials permanently unrecoverable due to AES-GCM auth-tag mismatch (#1622). +- **fix(startup):** Add decrypt-probe diagnostic at server bootstrap — if `STORAGE_ENCRYPTION_KEY` doesn't match encrypted credentials in the database, a prominent warning is logged directing users to restore the key or use the new recovery command. +- **fix(cli-tools):** Allow `null` API key values in `cliModelConfigSchema` to prevent 400 Bad Request errors when saving cloud-based CLI tool configurations. Fix error handling across all 10 ToolCard components to safely extract messages from structured error objects, preventing React Error #31 crashes. +- **fix(docker):** Set `NPM_CONFIG_LEGACY_PEER_DEPS=true` in the Docker builder layer before `npm ci` and remove duplicate `postinstallSupport.mjs` COPY instruction — fixes container image build failures introduced in v3.7.0 (#1630 — thanks @rdself). +- **fix(antigravity):** Hide deprecated Gemini-routed Claude 4.5 models from public catalogs and model lists. Legacy `gemini-claude-*` aliases now silently resolve to current Claude 4.6 equivalents. Replace dynamic reverse-alias generation with an explicit allowlist for predictable model visibility (#1631 — thanks @backryun). +- **fix(types):** Add explicit type annotations to sync-env test helpers and dynamic import casts to satisfy `typecheck:noimplicit:core` CI gate. +- **fix(reasoning):** Implement Reasoning Replay Cache — hybrid memory/SQLite persistence for `reasoning_content` in multi-turn tool-calling flows. Automatically captures reasoning from DeepSeek V4, Kimi K2, Qwen-Thinking, and GLM models and re-injects it on follow-up turns to prevent HTTP 400 errors from strict reasoning-content validation. Includes dashboard telemetry tab, REST API, and 21 unit tests (#1628 — thanks @JasonLandbridge). +- **fix(postinstall):** Extend postinstall native module repair to cover `wreq-js` — detects missing platform-specific `.node` binaries inside `app/node_modules/wreq-js/rust/` and copies them from the root install. Fixes global `pnpm` installs on macOS arm64 where the standalone app directory only contained Linux binaries (#1634 — thanks @MarcosT96). +- **fix(migration):** Prevent compat-renamed migration slots from shadowing new migrations at the same version number. After rewriting `028_provider_connection_max_concurrent` → `029`, the runner now verifies the old version slot is clear, ensuring `028_create_files_and_batches` runs on v3.6.x → v3.7.x upgrades. Adds `batches` table as a physical schema sentinel for upgrade recovery (#1637 — thanks @V8-Software). +- **fix(registry):** Route GitHub Copilot GPT 5.4/5.5 models through the Responses API (`targetFormat: "openai-responses"`). Fixes `gpt-5.4-mini` and `gpt-5.4` being rejected on `/chat/completions` by GitHub (#1641 — thanks @dhaern). +- **fix(usage):** Correct MiniMax token plan quota display — the newer `/v1/token_plan/remains` endpoint reports used counts, not remaining counts. Rounds floating-point percentage artifacts in Provider Limits UI (#1642 — thanks @CruxExperts). +- **fix(codex):** Lazy-load `wreq-js` WebSocket transport via `createRequire` instead of top-level import. Server boots cleanly when native module is unavailable and returns 503 only when Codex WebSocket is actually requested. Fixes #1612 (#1640 — thanks @dendyadinirwana). +- **fix(electron):** Package Electron runtime dependencies into `resources/app/node_modules/` via separate `extraResources` FileSet. Adds cross-platform packaged app smoke test script and CI integration to prevent future regressions. Closes #1636 (#1639 — thanks @prateek). +- **feat(account-fallback):** Add model-level daily quota lockout. When a provider returns 429 with `quota_exhausted`, cooldown is set to tomorrow 00:00 instead of exponential backoff. Detects daily quota patterns via `isDailyQuotaExhausted()` in chat handler (#1644 — thanks @clousky2020). +- **fix(codex):** Use per-conversation `session_id`/`conversation_id` from client body as `prompt_cache_key` instead of account-wide `workspaceId`. The official Codex CLI uses `conversation_id` (a unique UUID per session); using the shared `workspaceId` capped cache hit-rate at ~49%. Includes 10 unit tests (#1643). +- **fix(claude):** Stabilize billing header fingerprint to prevent Anthropic prompt-cache prefix invalidation. The fingerprint was derived from the first user message text, which changes every turn, mutating `system[]` and forcing ~100% `cache_create`. Now uses a stable per-day hash, preserving ~96% `cache_read` hit rate (#1638). +- **fix(transport):** Harden GitHub and Kiro streaming — thread `clientHeaders` through `BaseExecutor.buildHeaders()` to eliminate mutable singleton state race condition on concurrent requests. Remove redundant `[DONE]` stripping TransformStream from GitHub executor. Add defensive `parseToolInput()` for malformed Kiro tool call arguments. Hoist `TextEncoder`/`TextDecoder` to module singletons and use zero-copy `subarray()` (#1645 — thanks @dhaern). +- **fix(transport):** Prevent memory bloat and database exhaustion from large, fragmented streaming responses. Implemented `ByteQueue` in `kiro.ts` for zero-copy binary accumulation, refactored `antigravity.ts` for incremental SSE parsing, and enforced a strict 512KB tiered truncation limit (`MAX_CALL_LOG_ARTIFACT_BYTES`) on stream request logs and call artifacts (#1647). +- **chore(ci):** Update build environment dependencies — bump Node to `24.15.0`, `actions/checkout@v6`, `docker/build-push-action@v7`, pin `actions/setup-python` to major tag (#1646 — thanks @backryun). + +### 📝 Documentation + +- **docs(env):** Add `OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS` to `.env.example` with documentation for LM Studio and other local provider use cases (#1623). + +--- + +## [3.7.0] — 2026-04-26 + +### ✨ New Features + +- **feat(providers):** Implement Image Generation and Editing capabilities for ChatGPT Web, including in-band chat image generation and caching (#1606). +- **feat(ui):** Integrate OpenCode Zen/Go API tool logo SVG and polish API key copy-to-clipboard interactions (#1607). +- **feat(providers):** Add CrofAI as a built-in API-key provider with quota/usage monitoring wired into the dashboard Limits page (#1604, #1606). +- **feat(skills):** Add workspace-scoped built-in skills (`file_read`, `file_write`, `http_request`, `eval_code`, `execute_command`) with real sandbox execution via Docker, replacing stub responses. Browser skills now fail explicitly when runtime is not configured. + +- **feat(providers):** Integrate AgentRouter as a new OpenAI-compatible passthrough provider with $200 free credits via sign-up (Issue #1572). +- **feat(ui):** Implement on-demand per-model testing in the provider dashboard, allowing single-token diagnostic checks without triggering rate-limits (Issue #1532). + +- **feat(provider):** add ChatGPT Web (Plus/Pro) session provider (#1593) +- **feat(provider):** add Baidu Qianfan chat provider (#1582) +- **feat(codex):** support GPT-5.5 responses websocket (#1573) +- **feat(sse):** Codex CLI image_generation + DALL-E-style image route (#1544) +- **feat(dashboard):** Complete the reconciled v3.7.0 dashboard task set: MCP cache tools and count, video endpoint visibility, provider taxonomy, upstream proxy visibility, provider count badges, costs overview, eval suite management, Custom CLI builder, ACP-focused Agents copy, Translator stream transformer, logs convergence, learned rate-limit health cards, docs expansion, and active request payload inspection. +- **feat(mcp):** Register `omniroute_cache_stats` and `omniroute_cache_flush` across MCP schemas, server registration, handlers, docs, and tests. +- **feat(providers):** Complete the v3.7.0 provider onboarding wave with self-hosted/local providers (`lm-studio`, `vllm`, `lemonade`, `llamafile`, `triton`, `docker-model-runner`, `xinference`, `oobabooga`), OpenAI-compatible gateways (`glhf`, `cablyai`, `thebai`, `fenayai`, `empower`, `poe`), enterprise providers (`datarobot`, `azure-openai`, `azure-ai`, `bedrock`, `watsonx`, `oci`, `sap`), specialty providers (`clarifai`, `modal`, `reka`, `nous-research`, `nlpcloud`, `petals`, `vertex-partner`), `amazon-q`, GitLab/GitLab Duo, and Chutes.ai. +- **feat(providers):** Add Cloudflare Workers AI integration and UI support for robust backend execution. +- **feat(telemetry):** Implement proactive public IP capture from client headers (`x-forwarded-for`, `x-real-ip`, etc.) within `safeLogEvents` for accurate database observability. +- **feat(audio):** Add AWS Polly as an audio speech provider with SigV4 request signing, static engine catalog, provider validation, managed-provider UI coverage, and sanitization for AWS secret/session fields. +- **feat(search):** Add You.com search provider support with dashboard discovery, validation, livecrawl option handling, and search handler normalization. +- **feat(video):** Add RunwayML task-based video generation support, task polling, provider catalog metadata, validation, and dashboard/model-list coverage. +- **feat(providers):** Add search functionality to the providers dashboard with i18n support. (#1511 — thanks @th-ch) +- **feat(providers):** Register 6 new models in the opencode-go provider catalog. (#1510 — thanks @kang-heewon) +- **feat(providers):** Add ModelScope provider (Chinese AI marketplace) with Kimi K2.5, GLM-5, and Step-3.5-Flash integration. (#1430 — thanks @clousky2020) +- **feat(providers):** Add LM Studio as an OpenAI-compatible local provider for self-hosted model inference. +- **feat(providers):** Add Grok 4.3 thinking model support for xAI web executor requests. +- **feat(core):** Implement provider-level Circuit Breaker to prevent cascading failures across connections, enforcing a 10-minute cooldown after 5 consecutive transient failures. (#1430) +- **feat(core):** Add daily quota exhaustion lock to detect "quota exceeded" signals and lock the specific model until midnight. (#1430) +- **feat(core):** Auto-inject `stream_options.include_usage = true` for OpenAI format streams to guarantee token usage is reported correctly during streaming. (#1423) +- **feat(core):** Add OpenAI Batch Processing API support — submit, monitor, and manage batch jobs through the proxy with full lifecycle tracking. +- **feat(vision-bridge):** Add automatic image description fallback for non-vision models via `VisionBridgeGuardrail` (priority 5). Intercepts image-bearing requests to non-vision models, extracts descriptions via a configurable vision model (default: gpt-4o-mini), and replaces images with text before forwarding. Fails open on any error. (#1476) +- **feat(dashboard):** Introduce real-time model status badges with countdown timers in the provider detail and combo panel interfaces. (#1430) +- **feat(dashboard):** Add Batch/File management data grid with full i18n translations for batch processing workflows. (#1479) +- **feat(usage):** MiniMax + MiniMax-CN quota tracking in provider limits dashboard. (#1516) +- **feat(providers):** Fix OpenRouter remote discovery and unify managed model sync. (#1521) +- **feat(providers):** Implement provider and account-level concurrency cap enforcement (`maxConcurrent`) using robust semaphore mechanisms. (#1524) +- **feat(core):** Implement Hermes CLI config generation and message content stripping. (#1475) +- **feat(combos):** Add expert combo configuration mode for advanced routing controls. (#1547) +- **feat(providers):** Register Codex auto review and expand icon coverage. +- **feat(tunnels):** Add Tailscale tunnel management routes and runtime helpers for install, login, daemon start, enable/disable, and health checks. + +### 🐛 Bug Fixes + +- **fix(mitm):** Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime. +- **fix(build):** Move the local `.tmp/wine32` Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot trigger `EACCES` scans during Node 24 builds. +- **fix(build):** Copy the `wreq-js` native runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. +- **fix(api):** Validate the Codex Responses websocket bridge and `/v1/batches` JSON payloads with Zod before use, keeping `request.json()` route validation green and returning explicit 400 responses for invalid bodies. +- **fix(providers):** Add explicit typing to provider alias and category helpers so the strict `typecheck:noimplicit:core` CI gate passes. +- **fix(ui):** Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable. +- **fix(electron):** Harden the production desktop CSP by removing `unsafe-eval` outside development and adding object, base URI, form action, frame ancestor, and worker restrictions. +- **fix(cli):** Replace shell-interpolated setup and privileged command execution paths with argument-based `spawn`/`execFile` helpers for database setup, Tailscale sudo commands, MITM DNS edits, and certificate install/uninstall flows. +- **fix(ui):** Keep provider icons resilient by using direct `@lobehub/icons` components first, then local PNG/SVG fallbacks, avoiding the `@lobehub/ui` peer runtime in the dashboard. + +- **fix(chatgpt-web):** Fix empty-file race in `tlsFetchStreaming` where `waitForFile` accepted zero-byte files, silently degrading streaming requests to buffered mode. Replaced with `waitForContent` requiring `file.size > 0` with early exit on request settlement. (#1597 — thanks @trader-payne) +- **fix(chatgpt-web):** Fix stale NextAuth session-token cookies surviving rotation shape changes (unchunked↔chunked). `mergeRefreshedCookie` now drops all session-token family members via `SESSION_TOKEN_FAMILY_RE` before appending the refreshed set, preventing auth failures from dual cookie submission. (#1597 — thanks @trader-payne) +- **fix(codex):** WebSocket memory retention and weekly limit handling (#1581) +- **fix(providers):** Default models list logic (#1577) +- **fix(ui):** Dashboard endpoint URL hydration respects `NEXT_PUBLIC_BASE_URL` when behind a reverse proxy (#1579) +- **fix(providers):** Restore strict PascalCase header masquerading for Claude Code to resolve HTTP 429 upstream errors (#1556) +- **fix(sse):** make Responses passthrough robust for size-sensitive clients (#1580) +- **fix(codex):** update client version for gpt-5.5 (#1578) +- **fix(vision-bridge):** force GPT-family image fallback (#1571) +- **fix(claude):** skip adaptive thinking defaults for unsupported models (#1563) +- **fix(claude):** preserve tool_result adjacency in native and CC-compatible paths (#1555) +- **fix(reasoning):** Preserve OpenAI Chat Completions `reasoning_effort` through assistant-prefill requests and label OpenAI request protocols explicitly as `OpenAI-Chat` or `OpenAI-Responses`. (#1550) +- **fix(codex):** Fix Codex auto-review model routing so review traffic resolves to the intended configured model. (#1551) +- **fix(resilience):** Route HTTP 429 cooldowns through runtime settings so cooldown behavior follows the configured resilience profile. (#1548) +- **fix(providers):** Normalize Anthropic header keys to lowercase in the provider registry to avoid duplicate or case-variant upstream headers. (#1527) +- **fix(providers):** Preserve audio, embedding, rerank, image, video, and OpenAI-compatible alias metadata when `/v1/models` merges static and discovered catalogs. +- **fix(providers):** Discover Azure OpenAI deployments from resource endpoints using `api-key` auth and configurable API versions. +- **fix(providers):** Keep local OpenAI-style providers authless when no API key is configured, including the Lemonade Server default endpoint. +- **fix(translator):** Preserve Antigravity default system instructions and caller-provided system prompts as separate Gemini `systemInstruction` parts instead of concatenating them. +- **fix(security):** Sanitize provider-specific AWS secrets and session tokens from provider management API responses. +- **fix(release):** Resolve combo prefixing, Electron packaging, CLI auth, and release-branch integration regressions. (#1471, #1492, #1496, #1497, #1486) +- **fix(providers):** Resolve 400 errors for GLM and Antigravity Claude adapter during request translation by scoping prompt caching to compatible Anthropic endpoints and flattening system instructions. (#1514, #1520, #1522) +- **fix(core):** Strip `reasoning_content` from OpenAI format messages for non-reasoning models to prevent upstream HTTP 400 validation errors. (#1505) +- **fix(sse):** Map Claude `output_config/thinking` to OpenAI `reasoning_effort` for proper Antigravity tool translation. (#1528) +- **fix(combo):** Fallback to next model on all-accounts-rate-limited (HTTP 503/429) to maintain high availability. (#1523) +- **fix(api):** Harden batch and file endpoints for auth and recovery to prevent schema state collisions. +- **fix(ui):** Add missing UI wiring for "Add Memory" and "Import" buttons on the `/dashboard/memory` page. (#1506) +- **fix(ui):** Prevent Dark Mode FOUC (Flash of Unstyled Content) by injecting a synchronous theme initialization script into the root `layout.tsx`. +- **fix(ui):** Fix mobile layout text overflow in provider and combo cards, and enable touch-friendly reordering arrows across all combo strategies. +- **fix(core):** Add periodic runtime log rotation checks to prevent disk exhaustion in long-running instances. (#1504 — thanks @ether-btc) +- **fix(build):** Resolve missing `process` module in webpack client build for pino-abstract-transport. (#1509 — thanks @hartmark) +- **fix(ui):** Add dark mode support for native dropdown `