Compare commits
119 Commits
feat/11481
...
docs/opena
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aabfc59078 | ||
|
|
3113e571fc | ||
|
|
6706c382d8 | ||
|
|
b7a0c54139 | ||
|
|
78fd3504dd | ||
|
|
c664505db3 | ||
|
|
ef2a89bd69 | ||
|
|
718accb03d | ||
|
|
bbbcc79384 | ||
|
|
7f49b342b5 | ||
|
|
897c3f8c9d | ||
|
|
4e4522c285 | ||
|
|
9aa7c2459a | ||
|
|
8a1d9bf910 | ||
|
|
ececf91e9e | ||
|
|
43f2b2c288 | ||
|
|
8b7afc0eba | ||
|
|
af65171e3f | ||
|
|
cda832c3a7 | ||
|
|
1dd046814f | ||
|
|
26bfda3cb9 | ||
|
|
6f914b7a32 | ||
|
|
e93c5e765d | ||
|
|
d13c6cb19a | ||
|
|
9b9ea88d47 | ||
|
|
838fc00f25 | ||
|
|
a2c5d8a2f5 | ||
|
|
908c1b823d | ||
|
|
d812585b5a | ||
|
|
1b2c6f4c36 | ||
|
|
00bc397cda | ||
|
|
3d15294967 | ||
|
|
55f6b9808b | ||
|
|
039a425401 | ||
|
|
14dc6e8513 | ||
|
|
2da9ade59b | ||
|
|
6096ea51f8 | ||
|
|
54a1114382 | ||
|
|
82f09f4c86 | ||
|
|
09428da3d9 | ||
|
|
6a41a78132 | ||
|
|
ff4ac6c4d5 | ||
|
|
51e4930d05 | ||
|
|
476b20bd69 | ||
|
|
fe8ef4fa90 | ||
|
|
5d07bf32fe | ||
|
|
4d20d37974 | ||
|
|
1f4dc830f3 | ||
|
|
77f6f73706 | ||
|
|
32702d313b | ||
|
|
485c2dcdb6 | ||
|
|
f5742c3a8b | ||
|
|
52521984de | ||
|
|
385e90f444 | ||
|
|
d213ef0304 | ||
|
|
0b19c5a09b | ||
|
|
9903a6d2eb | ||
|
|
4c8074ba7a | ||
|
|
56dddfce34 | ||
|
|
8f38dcd32b | ||
|
|
4c187de99b | ||
|
|
e0029eb5a6 | ||
|
|
dfc84ba030 | ||
|
|
b07eaafcc4 | ||
|
|
131e413cbd | ||
|
|
212fba734f | ||
|
|
25aa95f0d0 | ||
|
|
23144ad644 | ||
|
|
4254b1fce1 | ||
|
|
66e02ec737 | ||
|
|
823dae0e9d | ||
|
|
3852e0534f | ||
|
|
15b164866c | ||
|
|
2471a0d95e | ||
|
|
81bf1ef98a | ||
|
|
41c6135257 | ||
|
|
22011437f8 | ||
|
|
8bed101303 | ||
|
|
ff0743071e | ||
|
|
41f4f83772 | ||
|
|
79b2e92c4e | ||
|
|
e96e40c035 | ||
|
|
097226b617 | ||
|
|
70af41b9f6 | ||
|
|
8180b3213a | ||
|
|
a3c19dd27c | ||
|
|
e620c50f3c | ||
|
|
2e3cd599b6 | ||
|
|
5684589ce7 | ||
|
|
ccee48d34a | ||
|
|
1c37fff056 | ||
|
|
c2c97aff82 | ||
|
|
faebf6de5f | ||
|
|
55691e0416 | ||
|
|
a1d6ff5fbf | ||
|
|
49827c1db1 | ||
|
|
47ea113b99 | ||
|
|
d26fe03801 | ||
|
|
92574de164 | ||
|
|
da678bd3ff | ||
|
|
2ec24e7c0b | ||
|
|
50bc8ab8aa | ||
|
|
4e11887085 | ||
|
|
38a29661d3 | ||
|
|
2463781e00 | ||
|
|
aa861a80d2 | ||
|
|
38e2baa879 | ||
|
|
d3420d29f1 | ||
|
|
5698769aba | ||
|
|
55e33f3dc8 | ||
|
|
36b7920db1 | ||
|
|
5fcd39bd6f | ||
|
|
e8b2cd208d | ||
|
|
f30e5b2675 | ||
|
|
ef668967f6 | ||
|
|
60dc242178 | ||
|
|
3b00535d04 | ||
|
|
2b8d3a8291 | ||
|
|
34e2f84c04 |
49
.env.example
@@ -45,6 +45,16 @@ INITIAL_PASSWORD=CHANGEME
|
||||
# executor's on-disk thread-sticky session cache. Leave unset to rely on DATA_DIR.
|
||||
# OMNIROUTE_DATA_DIR=/var/lib/omniroute
|
||||
|
||||
# Directory the runtime plugin scanner reads, overriding the home-derived default (#11827).
|
||||
# Used by: src/lib/plugins/scanner.ts — getDefaultPluginDir(); it is also the root the
|
||||
# plugin manager installs into. Set it in Docker/K8s to point straight at the bind-mounted
|
||||
# plugin tree, instead of moving HOME (which changes every other HOME-relative behaviour)
|
||||
# just to relocate the scan path. Unset = <HOME>/.omniroute/plugins, and
|
||||
# /tmp/.omniroute/plugins when the process exports no home at all.
|
||||
# Distinct from the CLI-only variable in section 9 that points the omniroute-cmd-* command
|
||||
# loader (bin/cli/plugins.mjs) at a package tree — this one drives the server-side scanner.
|
||||
# OMNIROUTE_PLUGINS_DIR=/opt/omniroute/plugins
|
||||
|
||||
# Escape hatch for the test-context DATA_DIR guard (#10428). A test run that never
|
||||
# chose a DATA_DIR is redirected to a throwaway temp dir so it cannot open the
|
||||
# operator's real database. Set to 1 only for a deliberate run against the real
|
||||
@@ -71,6 +81,11 @@ INITIAL_PASSWORD=CHANGEME
|
||||
# Never set this for the running server. Used by: src/lib/buildPhase.ts, src/lib/db/core.ts
|
||||
# OMNIROUTE_BUILDING=1
|
||||
|
||||
# Skip the optional native-dependency prebuild check for exotic vendored trees.
|
||||
# This does not make a missing dependency buildable. Used by: scripts/check/check-native-deps.mjs
|
||||
# Default: 0 | Set to 1 only when native dependencies are supplied out of band.
|
||||
# OMNIROUTE_SKIP_NATIVE_DEP_CHECK=0
|
||||
|
||||
# Encryption key for SQLite database encryption at rest.
|
||||
# Used by: src/lib/db/encryption.ts — encrypts the entire SQLite database.
|
||||
# Generate: openssl rand -hex 32 | Leave empty to disable DB encryption.
|
||||
@@ -658,21 +673,11 @@ NEXT_PUBLIC_CLOUD_URL=
|
||||
# open-sse/services/usage.ts.
|
||||
#OMNIROUTE_CROF_USAGE_URL=https://crof.ai/usage_api/
|
||||
#OMNIROUTE_CODEWHISPERER_BASE_URL=https://codewhisperer.us-east-1.amazonaws.com
|
||||
#OMNIROUTE_OPENCODE_QUOTA_URL=https://opencode.ai/zen/go/v1/quota
|
||||
# OpenCode Go has no public quota API — this has no default and stays
|
||||
# unset unless you explicitly opt in to a self-hosted/mirrored endpoint:
|
||||
#OMNIROUTE_OPENCODE_GO_QUOTA_URL=
|
||||
#OMNIROUTE_OPENCODE_GO_DASHBOARD_URL=https://opencode.ai/workspace
|
||||
# Official OpenCode Go usage endpoint, authenticated with the connection API key.
|
||||
# Override only for relays or test fixtures.
|
||||
#OMNIROUTE_OPENCODE_QUOTA_URL=https://opencode.ai/zen/go/v1/usage
|
||||
#OMNIROUTE_OLLAMA_CLOUD_USAGE_URL=https://ollama.com/settings
|
||||
|
||||
# OpenCode Go dashboard quota scraping. Prefer configuring these per connection
|
||||
# in Dashboard → Providers → OpenCode Go. Env vars are useful for headless
|
||||
# deployments or shared server defaults. The cookie is sensitive.
|
||||
#OPENCODE_GO_WORKSPACE_ID=wrk_...
|
||||
#OMNIROUTE_OPENCODE_GO_WORKSPACE_ID=wrk_...
|
||||
#OPENCODE_GO_AUTH_COOKIE=auth=...
|
||||
#OMNIROUTE_OPENCODE_GO_AUTH_COOKIE=auth=...
|
||||
|
||||
# OpenCode Go/Zen VPS egress (#5997): on a datacenter VPS, Cloudflare in front of
|
||||
# opencode.ai/zen/go 403s chat requests that lack OpenCode CLI identity headers.
|
||||
# When your clients don't already send them, set this to synthesize the CLI headers
|
||||
@@ -810,6 +815,10 @@ NEXT_PUBLIC_ENABLE_SOCKS5_PROXY=true
|
||||
# Windsurf has no default binary — set this to enable binary detection for it.
|
||||
# CLI_WINDSURF_BIN=windsurf
|
||||
# CLI_AUGGIE_BIN=auggie
|
||||
# CLI_5DIVE_BIN=5dive
|
||||
# 5dive keeps root-owned auth profiles under a system state dir (its own STATE_DIR,
|
||||
# default /var/lib/5dive); override here when it lives elsewhere.
|
||||
# CLI_5DIVE_STATE_DIR=/var/lib/5dive
|
||||
# AUGGIE_BIN=auggie
|
||||
|
||||
# ── ZCode (Z.ai GLM coding-plan CLI) local provider ──
|
||||
@@ -969,6 +978,11 @@ PROVIDER_LIMITS_SYNC_SPACING_MS=1500
|
||||
# Used by: src/lib/jobs/budgetResetJob.ts. Floor: 10000.
|
||||
#OMNIROUTE_BUDGET_RESET_JOB_INTERVAL_MS=600000
|
||||
|
||||
# Cron expression for the call-log export job (destinations configured in the
|
||||
# dashboard under Integrations > Log export). Default: hourly, on the hour.
|
||||
# Used by: src/lib/jobs/logExportJob.ts. Timezone: UTC.
|
||||
#OMNIROUTE_LOG_EXPORT_CRON=0 * * * *
|
||||
|
||||
# Emergency budget-exhaustion fallback (set false or 0 to disable the reroute to
|
||||
# nvidia/openai/gpt-oss-120b when a request fails with a 402 budget error).
|
||||
# Used by: open-sse/services/emergencyFallback.ts. Default: enabled.
|
||||
@@ -1749,6 +1763,7 @@ APP_LOG_TO_FILE=true
|
||||
|
||||
# Custom directory for CLI plugin discovery (omniroute-cmd-* packages).
|
||||
# Default: ~/.omniroute/plugins/ Override in dev/CI to point at a local plugin tree.
|
||||
# CLI-only: the server-side plugin scanner is pointed by OMNIROUTE_PLUGINS_DIR (section 2).
|
||||
# OMNIROUTE_PLUGIN_PATH=
|
||||
|
||||
# ── Prompt cache (system prompt deduplication) ──
|
||||
@@ -2017,6 +2032,8 @@ APP_LOG_TO_FILE=true
|
||||
# CLIPROXYAPI_HOST=127.0.0.1
|
||||
# CLIPROXYAPI_PORT=5544
|
||||
# CLIPROXYAPI_CONFIG_DIR=~/.cli-proxy-api
|
||||
# Data-plane key fallback; the cliproxyapi_api_key setting takes precedence.
|
||||
# CLIPROXYAPI_API_KEY=
|
||||
# Management key for an externally managed instance. Embedded instances use
|
||||
# OmniRoute's encrypted service key.
|
||||
# CLIPROXYAPI_MANAGEMENT_KEY=
|
||||
@@ -2120,6 +2137,12 @@ APP_LOG_TO_FILE=true
|
||||
# Used by: open-sse/services/rateLimitManager.ts
|
||||
# RATE_LIMIT_MAX_WAIT_MS=15000
|
||||
|
||||
# Limiter-managed execution backstop (Bottleneck `expiration`): bounds a job's
|
||||
# post-dispatch execution, never queue wait. Must stay ABOVE upstream
|
||||
# fetch-start timeouts on non-incremental gateways. Default: 600000 (10 min)
|
||||
# Used by: open-sse/services/rateLimitManager.ts
|
||||
# RATE_LIMIT_EXECUTION_MAX_WAIT_MS=600000
|
||||
|
||||
# Rate limit queue admission cap: reject with 429 queue_full once this many requests
|
||||
# are already queued (0 = disabled/unbounded, the default). Used by: open-sse/services/rateLimitManager.ts
|
||||
# RATE_LIMIT_MAX_QUEUE_DEPTH=0
|
||||
|
||||
36
.github/workflows/api-route-typecheck.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: API Route Typecheck
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- "release/**"
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
api-typecheck:
|
||||
name: API Route Typecheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: npm
|
||||
- uses: ./.github/actions/npm-ci-retry
|
||||
- name: Reject new API-route TypeScript diagnostics
|
||||
run: node scripts/check/check-api-typecheck.mjs
|
||||
- name: API typecheck gate unit tests
|
||||
run: node --import tsx/esm --test tests/unit/build/check-api-typecheck.test.ts
|
||||
4
.github/workflows/codeql.yml
vendored
@@ -22,10 +22,10 @@ jobs:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
|
||||
- uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
||||
with:
|
||||
languages: javascript-typescript
|
||||
queries: security-extended
|
||||
- uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
|
||||
- uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
||||
with:
|
||||
category: "/language:javascript-typescript"
|
||||
|
||||
5
.github/workflows/docker-publish.yml
vendored
@@ -528,11 +528,14 @@ jobs:
|
||||
severity: CRITICAL
|
||||
ignore-unfixed: true
|
||||
exit-code: "1"
|
||||
# Explicit: the advisory scan above already points at it, and the blocking
|
||||
# gate must honour the same accepted-risk list (#12084).
|
||||
trivyignores: .trivyignore
|
||||
|
||||
- name: Upload Trivy SARIF to Security tab
|
||||
if: needs.prepare.outputs.version != 'main'
|
||||
continue-on-error: true
|
||||
uses: github/codeql-action/upload-sarif@v4.37.7
|
||||
uses: github/codeql-action/upload-sarif@v4.37.8
|
||||
with:
|
||||
sarif_file: trivy-results.sarif
|
||||
category: trivy-image
|
||||
|
||||
20
.github/workflows/electron-release.yml
vendored
@@ -85,6 +85,9 @@ jobs:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
# workflow_dispatch: build the tag being (re)built, not the dispatching branch. On a
|
||||
# tag push this resolves to the same commit.
|
||||
ref: ${{ needs.validate.outputs.version }}
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
@@ -170,6 +173,9 @@ jobs:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
# workflow_dispatch: build the tag being (re)built, not the dispatching branch. On a
|
||||
# tag push this resolves to the same commit.
|
||||
ref: ${{ needs.validate.outputs.version }}
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
@@ -356,6 +362,8 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
# Source archives + SBOM come from the tag being released, not the dispatching branch.
|
||||
ref: ${{ needs.validate.outputs.version }}
|
||||
|
||||
# `merge-multiple` is deliberately OFF. It resolves same-name collisions by ARRIVAL
|
||||
# ORDER, and the two macOS jobs each emit their own `latest-mac.yml` listing only their
|
||||
@@ -413,10 +421,14 @@ jobs:
|
||||
tag_name: ${{ needs.validate.outputs.version }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
# Only on the tag push. A re-attach dispatch runs against a release whose curated
|
||||
# notes already exist, and `true` APPENDS GitHub's auto-generated "What's Changed"
|
||||
# block to them (v3.8.50, run 33238093090: +1,416 chars on a 121 KB body).
|
||||
generate_release_notes: ${{ github.event_name != 'workflow_dispatch' }}
|
||||
# NEVER. Phase 3 of the release flow creates the GitHub Release with the curated
|
||||
# notes seconds after pushing the tag, so by the time this step runs (1-2 h of
|
||||
# builds later) the body already exists — and `true` APPENDS GitHub's
|
||||
# auto-generated "What's Changed" block to it (v3.8.48 shipped that way; the
|
||||
# v3.8.50 re-attach dispatch added +1,416 chars to a 121 KB body, run
|
||||
# 33238093090). A curated body sits ~3 KB under the 125,000-char cap, so the
|
||||
# append can also turn this step RED and leave the release with no assets.
|
||||
generate_release_notes: false
|
||||
fail_on_unmatched_files: false
|
||||
files: |
|
||||
release-assets/*.dmg
|
||||
|
||||
115
.github/workflows/nightly-release-green.yml
vendored
@@ -196,6 +196,26 @@ jobs:
|
||||
gh issue create --repo "$GITHUB_REPOSITORY" --title "$TITLE" --label base-red --body-file issue-body.md
|
||||
fi
|
||||
|
||||
- name: Close tracking issue when the branch is green again
|
||||
if: steps.validate.outputs.exit == '0'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TARGET: ${{ steps.branch.outputs.target }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# The open/update step above is the UPWARD half of the loop; without this
|
||||
# step a stale "not green" issue outlives the fix and every base-green check
|
||||
# (`AGENTS.md` → "Base-green check") keeps stamping new PRs as base-red inherited.
|
||||
TITLE="🔴 Release branch not green: ${TARGET}"
|
||||
EXISTING=$(gh issue list --repo "$GITHUB_REPOSITORY" --state open \
|
||||
--search "in:title $TITLE" --json number --jq '.[0].number' 2>/dev/null || echo "")
|
||||
if [ -n "$EXISTING" ]; then
|
||||
gh issue close "$EXISTING" --repo "$GITHUB_REPOSITORY" --reason completed \
|
||||
--comment "✅ \`${TARGET}\` is release-green again at \`${GITHUB_SHA:0:9}\` — ${RUN_URL}. Auto-closed by Release-Green (continuous)."
|
||||
echo "Closed issue #$EXISTING"
|
||||
fi
|
||||
|
||||
- name: Upload report artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
@@ -294,6 +314,25 @@ jobs:
|
||||
gh issue create --repo "$GITHUB_REPOSITORY" --title "$TITLE" --label base-red --body-file issue-body.md
|
||||
fi
|
||||
|
||||
- name: Close tracking issue when the branch is green again
|
||||
if: steps.validate.outputs.exit == '0'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# The open/update step above is the UPWARD half of the loop; without this
|
||||
# step a stale "not green" issue outlives the fix and every base-green check
|
||||
# (`AGENTS.md` → "Base-green check") keeps stamping new PRs as base-red inherited.
|
||||
TITLE="🔴 main branch not green"
|
||||
EXISTING=$(gh issue list --repo "$GITHUB_REPOSITORY" --state open \
|
||||
--search "in:title $TITLE" --json number --jq '.[0].number' 2>/dev/null || echo "")
|
||||
if [ -n "$EXISTING" ]; then
|
||||
gh issue close "$EXISTING" --repo "$GITHUB_REPOSITORY" --reason completed \
|
||||
--comment "✅ \`main\` is main-green again at \`${GITHUB_SHA:0:9}\` — ${RUN_URL}. Auto-closed by Release-Green (continuous)."
|
||||
echo "Closed issue #$EXISTING"
|
||||
fi
|
||||
|
||||
- name: Upload report artifact
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
@@ -384,6 +423,13 @@ jobs:
|
||||
# on `improvements`, complexity-ratchets only when `.improved`), and both exit
|
||||
# non-zero while the branch is over baseline — which is exactly when there is
|
||||
# nothing to bank. Their exit code is not the signal; the verifier below is.
|
||||
# Velocity phase (quality-baseline.json `_policy`, relax-baselines.mjs): the caps
|
||||
# were raised on purpose, so banking the measured shrink would silently undo the
|
||||
# 20% headroom every night. Pause the downward ratchet until the phase closes.
|
||||
if node -e 'process.exit(require("./config/quality/quality-baseline.json")._policy?.phase === "velocity" ? 0 : 1)'; then
|
||||
echo "Velocity phase active — ratchet banking paused (see docs/architecture/QUALITY_GATES.md → Velocity phase)."
|
||||
exit 0
|
||||
fi
|
||||
set +e
|
||||
node scripts/check/check-file-size.mjs --update
|
||||
node scripts/check/check-complexity-ratchets.mjs --update
|
||||
@@ -445,3 +491,72 @@ jobs:
|
||||
gh pr create --repo "$GITHUB_REPOSITORY" --base "$TARGET" --head "$BANK_BRANCH" \
|
||||
--title "chore(quality): bank ratchet shrinks (${TARGET})" --body-file pr-body.md
|
||||
fi
|
||||
|
||||
# ── Baseline headroom (velocity phase, 2026-08-30 → v4.0) ──────────────────────
|
||||
# The ratchets only speak when a baseline is crossed. With every baseline loosened by
|
||||
# 20% (scripts/quality/relax-baselines.mjs) the question is how fast the budget is
|
||||
# being consumed — this job measures each gate the way CI does and posts the headroom
|
||||
# table to one living issue, so a budget that fills in a week is visible before the
|
||||
# first red PR. Advisory: never fails the workflow.
|
||||
baseline-headroom:
|
||||
name: Baseline headroom
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
timeout-minutes: 60
|
||||
runs-on: ${{ (vars.USE_VPS_RUNNER == 'true' && fromJSON('["self-hosted","omni-light"]')) || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: npm
|
||||
|
||||
- uses: ./.github/actions/npm-ci-retry
|
||||
|
||||
- name: Measure headroom on ${{ github.ref_name }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node scripts/quality/baseline-headroom.mjs \
|
||||
--json reports/quality/headroom.json --md reports/quality/headroom.md
|
||||
cat reports/quality/headroom.md >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload headroom report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: baseline-headroom-${{ github.run_id }}
|
||||
path: reports/quality/headroom.*
|
||||
retention-days: 90
|
||||
|
||||
- name: Post to the living issue
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TITLE="📈 Baseline headroom (velocity phase)"
|
||||
BAD=$(node -e 'const r=require("./reports/quality/headroom.json").rows;console.log(r.filter(x=>x.status==="critical"||x.status==="warn").length)')
|
||||
{
|
||||
echo "Branch: \`${GITHUB_REF_NAME}\` · run: ${RUN_URL}"
|
||||
echo ""
|
||||
cat reports/quality/headroom.md
|
||||
} > headroom-comment.md
|
||||
EXISTING=$(gh issue list --repo "$GITHUB_REPOSITORY" --state open \
|
||||
--search "in:title $TITLE" --json number --jq '.[0].number' 2>/dev/null || echo "")
|
||||
if [ -z "$EXISTING" ]; then
|
||||
EXISTING=$(gh issue create --repo "$GITHUB_REPOSITORY" --title "$TITLE" --label quality-gate-finding \
|
||||
--body "Living tracker for the velocity-phase baseline budget (docs/architecture/QUALITY_GATES.md → Velocity phase). One comment per nightly run; the newest comment is the current state." \
|
||||
| grep -oE '[0-9]+$')
|
||||
fi
|
||||
gh issue comment "$EXISTING" --repo "$GITHUB_REPOSITORY" --body-file headroom-comment.md
|
||||
if [ "$BAD" != "0" ]; then
|
||||
gh issue edit "$EXISTING" --repo "$GITHUB_REPOSITORY" --add-label "headroom-alert" 2>/dev/null || true
|
||||
else
|
||||
gh issue edit "$EXISTING" --repo "$GITHUB_REPOSITORY" --remove-label "headroom-alert" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
14
.github/workflows/quality.yml
vendored
@@ -303,7 +303,12 @@ jobs:
|
||||
# #8522: file-size is base-relative on PR events (compare against
|
||||
# max(frozen, base)) so inherited drift doesn't red an innocent PR;
|
||||
# workflow_dispatch (no PR base) falls back to absolute comparison.
|
||||
if [ "$g" = "file-size" ] && [ -n "${PR_BASE_SHA:-}" ]; then
|
||||
# New-code mode (Clean-as-You-Code, 2026-08-30): complexity-ratchets and
|
||||
# dead-code compare the PR's files against the merge-base and block only on
|
||||
# what the PR added; the global totals are advisory on PRs and re-frozen at
|
||||
# release. See scripts/check/newCodeMode.mjs.
|
||||
case "$g" in file-size|complexity-ratchets|dead-code) NEW_CODE=1 ;; *) NEW_CODE= ;; esac
|
||||
if [ -n "$NEW_CODE" ] && [ -n "${PR_BASE_SHA:-}" ]; then
|
||||
npm run "check:$g" -- --base-ref "$PR_BASE_SHA" || failed+=("$g")
|
||||
else
|
||||
npm run "check:$g" || failed+=("$g")
|
||||
@@ -521,7 +526,10 @@ jobs:
|
||||
name: No new ESLint warnings
|
||||
needs: changes
|
||||
if: ${{ github.event_name != 'pull_request' || ((github.event.pull_request.draft == false || startsWith(github.head_ref, 'mergify/merge-queue/')) && needs.changes.outputs.code == 'true') }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ (vars.USE_VPS_RUNNER == 'true' && fromJSON('["self-hosted","omni-light"]')) || 'ubuntu-latest' }}
|
||||
# 2026-08-30: a cold full lint with the eslint-plugin-react-hooks 7 compiler rules is
|
||||
# killed on the 7 GB hosted runner without a message (status null → exit 1, the
|
||||
# JSON never written); the box lints it in ~12 min with the heap below.
|
||||
continue-on-error: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true }}
|
||||
# G0 (trilho .50): security-events:read lets the CodeQL ratchet below read open
|
||||
# code-scanning alerts via `gh api .../code-scanning/alerts` (same as ci.yml's
|
||||
@@ -553,6 +561,8 @@ jobs:
|
||||
- name: ESLint (baseline congelado — warning novo = vermelho)
|
||||
# lint:json writes the report; --max-warnings 0 keeps no-new-warnings policy.
|
||||
run: npm run lint:json -- --max-warnings 0
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
# ── G0 (trilho .50): motor de ratchet também no trilho B ─────────────────────
|
||||
# This job just wrote .artifacts/eslint-results.json — collect-metrics prefers
|
||||
# that file, so the ratchet engine lands here at ZERO extra ESLint cost (one
|
||||
|
||||
5
.github/workflows/scorecard.yml
vendored
@@ -4,12 +4,15 @@ on:
|
||||
schedule:
|
||||
- cron: "27 7 * * 1"
|
||||
push:
|
||||
branches: ["main"]
|
||||
# Scorecard only accepts the DEFAULT branch — here the active release/vX.Y.Z,
|
||||
# not `main`. The job below guards on it so a push to any other branch skips.
|
||||
branches: ["main", "release/**"]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
if: ${{ github.event_name != 'push' || github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
|
||||
name: Scorecard analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
|
||||
10
.trivyignore
@@ -19,4 +19,12 @@
|
||||
# Keep this list SHORT and reviewed every release. Prefer fixing (rebuild on a
|
||||
# patched base / bump the dep) over suppressing. Stale entries are debt.
|
||||
#
|
||||
# (No accepted-risk suppressions at present — ignore-unfixed covers the noise.)
|
||||
# CVE-2025-68121 — Go stdlib crypto/tls (session-resumption certificate validation)
|
||||
# inside the PREBUILT bogdanfinn/tls-client v1.15.1 .so that tls-client-node's
|
||||
# postinstall downloads (built with go 1.24.1; fixed in 1.24.13). No upstream
|
||||
# rebuild exists (v1.15.1 is still the latest release) and nothing in this repo
|
||||
# can bump it. The binary is only loaded by the browser-TLS web-provider
|
||||
# executors (claude-web / grok-web / lmarena / perplexity-web / notion-web),
|
||||
# whose handshakes go through utls. Tracking issue: #12084. Revisit at the next
|
||||
# tls-client release or base-image bump and BEFORE the v3.8.51 tag (2026-09-15).
|
||||
CVE-2025-68121
|
||||
|
||||
76
AGENTS.md
@@ -46,7 +46,7 @@ Repository map and Reference Documentation sections below.
|
||||
|
||||
## Project at a Glance
|
||||
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 351 LLM providers, auto-fallback.
|
||||
**OmniRoute** — unified AI proxy/router. One endpoint, 352 LLM providers, auto-fallback.
|
||||
|
||||
| Layer | Location | Purpose |
|
||||
| ------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -56,9 +56,9 @@ Repository map and Reference Documentation sections below.
|
||||
| Translators | `open-sse/translator/` | Format conversion (OpenAI↔Claude↔Gemini) |
|
||||
| Transformer | `open-sse/transformer/` | Responses API ↔ Chat Completions |
|
||||
| Services | `open-sse/services/` | Combo routing, rate limits, caching, etc |
|
||||
| Database | `src/lib/db/` | SQLite domain modules (166 migrations) |
|
||||
| Database | `src/lib/db/` | SQLite domain modules (167 migrations) |
|
||||
| Domain/Policy | `src/domain/` | Policy engine, cost rules, fallback logic |
|
||||
| MCP Server | `open-sse/mcp-server/` | 110 tools (44 canonical + memory/skill/GitHub/pool/gamification/plugin/Notion/Obsidian/local-corpus/RTK modules), 3 transports (stdio / SSE / Streamable HTTP), 33 scopes |
|
||||
| MCP Server | `open-sse/mcp-server/` | 110 tools (45 canonical + memory/skill/GitHub/pool/gamification/plugin/Notion/Obsidian/local-corpus/RTK modules), 3 transports (stdio / SSE / Streamable HTTP), 33 scopes |
|
||||
| A2A Server | `src/lib/a2a/` | JSON-RPC 2.0 agent protocol |
|
||||
| Skills | `src/lib/skills/` | Extensible skill framework |
|
||||
| Memory | `src/lib/memory/` | Persistent conversational memory |
|
||||
@@ -110,26 +110,32 @@ upstream/service level, so one unhealthy provider does not slow down every reque
|
||||
- Shared wrappers: `open-sse/services/accountFallback.ts`
|
||||
- Persisted state table: `domain_circuit_breakers`
|
||||
|
||||
**States**:
|
||||
**States** (4 — `src/shared/utils/circuitBreaker.ts`):
|
||||
|
||||
- `CLOSED`: normal traffic is allowed.
|
||||
- `DEGRADED`: early-warning band — failures crossed the degradation threshold but not the
|
||||
breaker threshold yet; traffic still flows, dashboards show the warning.
|
||||
- `OPEN`: provider is temporarily blocked; callers get a provider-circuit-open response
|
||||
or combo routing skips to another target.
|
||||
- `HALF_OPEN`: reset timeout has elapsed; allow a probe request. Success closes the
|
||||
breaker, failure opens it again.
|
||||
|
||||
**Defaults** (`open-sse/config/constants.ts` → `PROVIDER_PROFILES`). Two thresholds live side by
|
||||
side — do not confuse them:
|
||||
**Defaults** (`open-sse/config/constants.ts` → `PROVIDER_PROFILES`, consumed via
|
||||
`DEFAULT_RESILIENCE_SETTINGS.providerBreaker` in `src/lib/resilience/settings.ts` →
|
||||
`getCircuitBreaker(provider, …)` in `src/sse/handlers/chatHelpers.ts`). The whole-provider
|
||||
breaker runs on `circuitBreakerThreshold` / `circuitBreakerReset`:
|
||||
|
||||
| Profile | `providerFailureThreshold` (whole provider) | `providerCooldownMs` | `circuitBreakerThreshold` (one connection) | `circuitBreakerReset` |
|
||||
| ------- | ------------------------------------------: | -------------------: | -----------------------------------------: | --------------------: |
|
||||
| OAuth | `10` | `5min` | `8` | `60s` |
|
||||
| API key | `15` | `10min` | `12` | `30s` |
|
||||
| Local | `2` | `1min` | `2` | `15s` |
|
||||
| Profile | degrades at | opens at (`circuitBreakerThreshold`) | reset (`circuitBreakerReset`) |
|
||||
| ------- | ----------: | -----------------------------------: | ----------------------------: |
|
||||
| OAuth | `5` | `8` | `60s` |
|
||||
| API key | `7` | `12` | `30s` |
|
||||
| Local | (derived) | `2` | `15s` |
|
||||
|
||||
The provider-level thresholds were scaled up for deployments with 500+ connections (OAuth was
|
||||
`3`, API key was `5`); every default is overridable through the `OMNIROUTE_PROVIDER_BREAKER_*`
|
||||
and `OMNIROUTE_CIRCUIT_BREAKER_*` env vars.
|
||||
`PROVIDER_PROFILES` also defines `providerFailureThreshold` (10/15/2) and `providerCooldownMs`
|
||||
(5min/10min/1min); those fields are loaded into the profile but have **no runtime consumer
|
||||
today** — do not tune or document them as the live breaker. Every default is overridable
|
||||
through the `OMNIROUTE_PROVIDER_BREAKER_*` and `OMNIROUTE_CIRCUIT_BREAKER_*` env vars; the
|
||||
runtime-accurate reference table lives in `docs/architecture/RESILIENCE_GUIDE.md`.
|
||||
|
||||
Only provider-level failure statuses should trip the provider breaker:
|
||||
|
||||
@@ -242,7 +248,7 @@ Read the nearest `AGENTS.md` and the linked deep-dive before making a non-trivia
|
||||
| Streaming request handling | `open-sse/handlers/` | [`docs/architecture/ARCHITECTURE.md`](docs/architecture/ARCHITECTURE.md) |
|
||||
| Provider execution and translation | `open-sse/executors/`, `open-sse/translator/` | [`docs/architecture/CODEBASE_DOCUMENTATION.md`](docs/architecture/CODEBASE_DOCUMENTATION.md) |
|
||||
| Routing and resilience | `open-sse/services/` | [`open-sse/services/AGENTS.md`](open-sse/services/AGENTS.md), [`docs/routing/AUTO-COMBO.md`](docs/routing/AUTO-COMBO.md) |
|
||||
| Database and migrations | `src/lib/db/`, `db/migrations/` | [`src/lib/db/AGENTS.md`](src/lib/db/AGENTS.md) |
|
||||
| Database and migrations | `src/lib/db/`, `src/lib/db/migrations/` | [`src/lib/db/AGENTS.md`](src/lib/db/AGENTS.md) |
|
||||
| Domain policy | `src/domain/` | [`docs/architecture/ARCHITECTURE.md`](docs/architecture/ARCHITECTURE.md) |
|
||||
| MCP and A2A | `open-sse/mcp-server/`, `src/lib/a2a/` | [`docs/frameworks/MCP-SERVER.md`](docs/frameworks/MCP-SERVER.md), [`docs/frameworks/A2A-SERVER.md`](docs/frameworks/A2A-SERVER.md) |
|
||||
| Agent features | `src/lib/{acp,memory,skills,cloudAgent}/` | [`docs/frameworks/AGENT_PROTOCOLS_GUIDE.md`](docs/frameworks/AGENT_PROTOCOLS_GUIDE.md), [`docs/frameworks/SKILLS.md`](docs/frameworks/SKILLS.md) |
|
||||
@@ -254,13 +260,13 @@ Read the nearest `AGENTS.md` and the linked deep-dive before making a non-trivia
|
||||
## File placement & repo-root hygiene
|
||||
|
||||
- **Test files**: ALL unit tests, integration tests, ecosystem tests, or Vitest files MUST strictly be placed within the `tests/` directory (e.g., `tests/unit/`, `tests/integration/`). NEVER create test files in the project root (`/`).
|
||||
- **Scripts and utilities**: ALL maintenance, debugging, generation, or experimental scripts (`.cjs`, `.mjs`, `.js`, `.ts`) MUST be placed strictly inside one of the `scripts/` subfolders (`build/`, `dev/`, `check/`, `docs/`, `i18n/`, `ad-hoc/`, `quality/`, `release/`, `ci/`, `ops/`, `perf/`, `research/`, `sre/`, `vps/`, `homolog/`, `raycast/`, `skills/`, `test/`, `cli/`, `compression/`, `compression-eval/`, `devin-bridge/`, `docker/`, `features/`, `router-eval/`). One-shot or experimental code goes under `scripts/ad-hoc/`. NEVER dump loose scripts in the project root (`/`) or the top-level `scripts/` folder.
|
||||
- **Scripts and utilities**: ALL maintenance, debugging, generation, or experimental scripts (`.cjs`, `.mjs`, `.js`, `.ts`) MUST be placed strictly inside one of the `scripts/` subfolders (`build/`, `dev/`, `check/`, `docs/`, `i18n/`, `ad-hoc/`, `quality/`, `release/`, `ci/`, `ops/`, `perf/`, `research/`, `sre/`, `vps/`, `homolog/`, `packs/`, `skills/`, `test/`, `cli/`, `compression/`, `compression-eval/`, `devin-bridge/`, `docker/`, `features/`, `router-eval/`). One-shot or experimental code goes under `scripts/ad-hoc/`. NEVER dump loose scripts in the project root (`/`) or the top-level `scripts/` folder.
|
||||
|
||||
**The project root MUST ONLY contain:**
|
||||
|
||||
- Configuration files (`vitest.config.ts`, `next.config.mjs`, `eslint.config.mjs`, `tsconfig*.json`, `playwright.config.ts`, `prettier.config.mjs`, `postcss.config.mjs`, `sonar-project.properties`, `fly.toml`, `docker-compose*.yml`, `Dockerfile`)
|
||||
- Dependency files (`package.json`, `package-lock.json`)
|
||||
- Documentation files (`README.md`, `CHANGELOG.md`, `ROADMAP.md`, `LICENSE`, `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`, `llm.txt`, `Tuto_Qdrant.md`)
|
||||
- Documentation files (`README.md`, `CHANGELOG.md`, `ROADMAP.md`, `LICENSE`, `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `CONTRIBUTING.md`, `SECURITY.md`, `CODE_OF_CONDUCT.md`, `llm.txt`)
|
||||
- CI/CD files and ignore definitions (`.gitignore`, `.dockerignore`, `.npmignore`, `.npmrc`, `.node-version`, `.nvmrc`, `.env.example`)
|
||||
|
||||
When creating _any_ validation tests or one-off logic scripts, default to `scripts/ad-hoc/` or `tests/unit/` according to your goals. Do not pollute the `/` root context.
|
||||
@@ -289,8 +295,7 @@ When creating _any_ validation tests or one-off logic scripts, default to `scrip
|
||||
### Database
|
||||
|
||||
- **Always** go through `src/lib/db/` domain modules — **never** write raw SQL in routes or handlers
|
||||
- **Never** add logic to `src/lib/localDb.ts` (re-export layer only)
|
||||
- **Never** barrel-import from `localDb.ts` — import specific `db/` modules instead
|
||||
- **Never** barrel-import from `localDb.ts` — import specific `src/lib/db/*` modules
|
||||
- DB singleton: `getDbInstance()` from `src/lib/db/core.ts` (WAL journaling)
|
||||
- Migrations: `src/lib/db/migrations/` — versioned SQL files, idempotent, run in transactions
|
||||
|
||||
@@ -355,19 +360,18 @@ Documentation must describe verified behavior, not plausible behavior.
|
||||
1. Create `src/lib/db/yourModule.ts` — import `getDbInstance` from `./core.ts`
|
||||
2. Export CRUD functions for your domain table(s)
|
||||
3. Add migration in `src/lib/db/migrations/` if new tables needed
|
||||
4. Re-export from `src/lib/localDb.ts` (add to the re-export list only)
|
||||
5. Write tests
|
||||
4. Write tests
|
||||
|
||||
### Adding a New MCP Tool
|
||||
|
||||
1. Add tool definition in `open-sse/mcp-server/tools/` with Zod input schema + async handler
|
||||
2. Register in tool set (wired by `createMcpServer()`)
|
||||
3. Assign to appropriate scope(s)
|
||||
4. Write tests (tool invocation logged to `mcp_audit` table)
|
||||
4. Write tests (tool invocation logged to the `mcp_tool_audit` table)
|
||||
|
||||
### Adding a New A2A Skill
|
||||
|
||||
1. Create skill in `src/lib/a2a/skills/` (5 already exist: smart-routing, quota-management, provider-discovery, cost-analysis, health-report)
|
||||
1. Create skill in `src/lib/a2a/skills/` (6 already exist: smart-routing, quota-management, provider-discovery, cost-analysis, health-report, list-capabilities)
|
||||
2. Skill receives task context (messages, metadata) → returns structured result
|
||||
3. Register in `A2A_SKILL_HANDLERS` in `src/lib/a2a/taskExecution.ts`
|
||||
4. Expose in `src/app/.well-known/agent.json/route.ts` (Agent Card)
|
||||
@@ -376,7 +380,7 @@ Documentation must describe verified behavior, not plausible behavior.
|
||||
|
||||
### Adding a New Cloud Agent
|
||||
|
||||
1. Create agent class in `src/lib/cloudAgent/agents/` extending `CloudAgentBase` (3 already exist: codex-cloud, devin, jules)
|
||||
1. Create agent class in `src/lib/cloudAgent/agents/` extending `CloudAgentBase` (4 already exist: codex-cloud, devin, jules, cursor-cloud)
|
||||
2. Implement `createTask`, `getStatus`, `approvePlan`, `sendMessage`, `listSources`
|
||||
3. Register in `src/lib/cloudAgent/registry.ts`
|
||||
4. Add OAuth/credentials handling if needed (`src/lib/oauth/providers/`)
|
||||
@@ -387,7 +391,7 @@ Documentation must describe verified behavior, not plausible behavior.
|
||||
1. Create installer in `src/lib/services/installers/{name}.ts` modeled on `ninerouter.ts` (use `runNpm` from `installers/utils.ts` — no shell interpolation, hard rule #13).
|
||||
2. Register the service in `src/lib/services/bootstrap.ts` (add to `SERVICES[]` array and extend `buildSpawnArgsFactory()`).
|
||||
3. Add a DB seed row for the new service in `src/lib/db/migrations/` (`version_manager` table, `status='not_installed'`, `auto_start=0`).
|
||||
4. Create 7 API endpoints under `src/app/api/services/{name}/` (`_lib.ts`, `install`, `start`, `stop`, `restart`, `update`, `status`, `auto-start`). All delegate errors through `createErrorResponse()`. The shared `logs` endpoint is already wired via `[name]/logs/route.ts`.
|
||||
4. Create 8 API endpoints under `src/app/api/services/{name}/` (`_lib.ts`, `install`, `start`, `stop`, `restart`, `update`, `status`, `auto-start`, `auto-restart-adopted`). All delegate errors through `createErrorResponse()`. The shared `logs` endpoint is already wired via `[name]/logs/route.ts`.
|
||||
5. Verify `/api/services/` is in `LOCAL_ONLY_API_PREFIXES` in `src/server/authz/routeGuard.ts`; add a test asserting `isLocalOnlyPath()` returns `true` for the new prefix if you add one (hard rule #17).
|
||||
6. Add a UI tab in `src/app/(dashboard)/dashboard/providers/services/tabs/` reusing `ServiceStatusCard`, `ServiceLifecycleButtons`, `ServiceLogsPanel`.
|
||||
7. Document in `docs/frameworks/EMBEDDED-SERVICES.md` (update §1 service table + §4 API reference) and `docs/openapi.yaml`.
|
||||
@@ -399,6 +403,9 @@ Documentation must describe verified behavior, not plausible behavior.
|
||||
- Eval suite: `src/lib/evals/` → docs: `docs/frameworks/EVALS.md`
|
||||
- Skill (sandbox): `src/lib/skills/` → docs: `docs/frameworks/SKILLS.md`
|
||||
- Webhook event: `src/lib/webhookDispatcher.ts` → docs: `docs/frameworks/WEBHOOKS.md`
|
||||
- Log-export destination: add `src/lib/logExport/destinations/<name>.ts` + one line in
|
||||
`src/lib/logExport/registry.ts` → docs: `docs/frameworks/LOG-EXPORT.md`. The runner, REST layer
|
||||
and dashboard form all read the registry, so nothing else changes.
|
||||
|
||||
---
|
||||
|
||||
@@ -424,6 +431,7 @@ For any non-trivial change, read the matching deep-dive first:
|
||||
| Evals | `docs/frameworks/EVALS.md` |
|
||||
| Compliance / audit | `docs/security/COMPLIANCE.md` |
|
||||
| Webhooks | `docs/frameworks/WEBHOOKS.md` |
|
||||
| Log export (call logs → BigQuery/…) | `docs/frameworks/LOG-EXPORT.md` |
|
||||
| Authorization pipeline | `docs/architecture/AUTHZ_GUIDE.md` |
|
||||
| Stealth (TLS / fingerprint) | `docs/security/STEALTH_GUIDE.md` |
|
||||
| Agent protocols (A2A / ACP / Cloud) | `docs/frameworks/AGENT_PROTOCOLS_GUIDE.md` |
|
||||
@@ -436,7 +444,7 @@ For any non-trivial change, read the matching deep-dive first:
|
||||
| VS Code Copilot Chat (OmniCopilot extension) | `docs/guides/VSCODE-COPILOT.md` |
|
||||
| Release flow | `docs/ops/RELEASE_CHECKLIST.md` |
|
||||
| Embedded services | `docs/frameworks/EMBEDDED-SERVICES.md` |
|
||||
| Quality gates (~80 scripts, allowlist policy) | `docs/architecture/QUALITY_GATES.md` |
|
||||
| Quality gates (~90 scripts, allowlist policy) | `docs/architecture/QUALITY_GATES.md` |
|
||||
|
||||
---
|
||||
|
||||
@@ -627,8 +635,8 @@ focused checks, and use a Conventional Commit message (for example, `docs: slim
|
||||
|
||||
## Environment
|
||||
|
||||
- **Runtime**: Node.js ≥22.0.0 <23 || ≥24.0.0 <27, ES Modules. This is the **only supported** runtime for the published `omniroute` CLI, the server, and the test suites (`node:test` + vitest) — `engines.node` is authoritative and end users never need Bun. A **best-effort `bun:sqlite` compatibility path** exists so a global Bun install (`bun install -g omniroute`) can start without `better-sqlite3` (driver adapter + Bun-aware process spawning); it is **not** a supported runtime — no support guarantees — and every Bun-specific runtime change MUST preserve the Node driver/fallback chain and ship a Bun test (`test:bun:db`) or an explicit reason why the path is Node-only.
|
||||
- **Bun (build/dev script runner + compatibility smoke only)**: Bun `1.3.14` is pinned as an **exact devDependency** (provisioned through the existing `npm ci` via the lockfile's `@oven/bun-*` platform binaries — no `setup-bun`/ad-hoc install). It is used **only** to execute a small, allow-listed set of TypeScript **gate/generator scripts** (replacing `node --import tsx` for startup speed): the CI checks `check:provider-consistency`, `check:compression-budget`, `check:known-symbols`, and the non-CI `gen:provider-reference`, `bench:compression` — plus the focused `test:bun:db` compatibility smoke suite for the best-effort `bun:sqlite` path. **Do NOT** widen Bun to `npm install`, the build (`build:cli*`), `check:pack-artifact`, the supported published runtime, or the main test runners — those stay on Node. Any new Bun-invoking gate/generator script must be validated byte-identical against its `node --import tsx` output first. After pulling the lockfile change, run `npm install` so `bun` resolves locally (a stale `node_modules` will fail those scripts with `bun: not found`).
|
||||
- **Runtime**: Node.js ≥22.22.2 <23 || ≥24.0.0 <27, ES Modules. This is the **only supported** runtime for the published `omniroute` CLI, the server, and the test suites (`node:test` + vitest) — `engines.node` is authoritative and end users never need Bun. A **best-effort `bun:sqlite` compatibility path** exists so a global Bun install (`bun install -g omniroute`) can start without `better-sqlite3` (driver adapter + Bun-aware process spawning); it is **not** a supported runtime — no support guarantees — and every Bun-specific runtime change MUST preserve the Node driver/fallback chain and ship a Bun test (`test:bun:db`) or an explicit reason why the path is Node-only.
|
||||
- **Bun (build/dev script runner + compatibility smoke only)**: Bun `1.4.0` is pinned as an **exact devDependency** (provisioned through the existing `npm ci` via the lockfile's `@oven/bun-*` platform binaries — no `setup-bun`/ad-hoc install). It is used **only** to execute a small, allow-listed set of TypeScript **gate/generator scripts** (replacing `node --import tsx` for startup speed): the CI checks `check:provider-consistency`, `check:compression-budget`, `check:known-symbols`, and the non-CI `gen:provider-reference`, `bench:compression` — plus the focused `test:bun:db` compatibility smoke suite for the best-effort `bun:sqlite` path. **Do NOT** widen Bun to `npm install`, the build (`build:cli*`), `check:pack-artifact`, the supported published runtime, or the main test runners — those stay on Node. Any new Bun-invoking gate/generator script must be validated byte-identical against its `node --import tsx` output first. After pulling the lockfile change, run `npm install` so `bun` resolves locally (a stale `node_modules` will fail those scripts with `bun: not found`).
|
||||
- **TypeScript**: 6.0+, target ES2022, module esnext, resolution bundler
|
||||
- **Path aliases**: `@/*` → `src/`, `@omniroute/open-sse` → `open-sse/`, `@omniroute/open-sse/*` → `open-sse/*`
|
||||
- **Default port**: 20128 (API + dashboard on same port)
|
||||
@@ -640,12 +648,12 @@ focused checks, and use a Conventional Commit message (for example, `docs: slim
|
||||
|
||||
## Quality Gates & Ratchets
|
||||
|
||||
OmniRoute has **~80 quality-gate scripts** (`scripts/check/` + `scripts/quality/`) wired
|
||||
OmniRoute has **~90 quality-gate scripts** (`scripts/check/` + `scripts/quality/`) wired
|
||||
across **9 gate-running jobs** in `.github/workflows/ci.yml` (`lint`, `quality-gate`,
|
||||
`quality-extended`, `docs-sync-strict`, `i18n-ui-coverage`, `i18n`, `pr-test-policy`,
|
||||
`test-vitest`, `sonarqube`), plus the `quality.yml` fast-gates job (PR→`release/**`) and
|
||||
3 nightly workflows (`nightly-property`, `nightly-resilience`, `nightly-llm-security`;
|
||||
`nightly-mutation` once merged). Full inventory, per-job breakdown, and operational
|
||||
5 quality nightly workflows (`nightly-property`, `nightly-resilience`,
|
||||
`nightly-llm-security`, `nightly-mutation`, `nightly-schemathesis`). Full inventory, per-job breakdown, and operational
|
||||
procedures are in [`docs/architecture/QUALITY_GATES.md`](docs/architecture/QUALITY_GATES.md).
|
||||
|
||||
**Quick reference:**
|
||||
@@ -657,6 +665,10 @@ procedures are in [`docs/architecture/QUALITY_GATES.md`](docs/architecture/QUALI
|
||||
`npm run quality:ratchet -- --update` when a metric genuinely improves.
|
||||
- Job `test-vitest` runs `npm run test:vitest` (MCP tools, autoCombo, cache) — blocking.
|
||||
`test:vitest:ui` has been blocking since PR #7127.
|
||||
- **Velocity phase (2026-08-30 → v4.0)**: every numeric baseline is loosened by 20% and
|
||||
`--require-tighten` is advisory (`quality-baseline.json` → `_policy`); the nightly
|
||||
`baseline-headroom` job tracks how much of the budget is left in the issue
|
||||
"📈 Baseline headroom". See `docs/architecture/QUALITY_GATES.md` → "Velocity phase".
|
||||
|
||||
**Allowlist policy (short form):** Fix the cause; use the allowlist only for pre-existing
|
||||
violations you cannot fix in the same PR. Add a comment with justification + issue number.
|
||||
@@ -668,7 +680,7 @@ the stale-enforcement added in Fase 6A.3.
|
||||
## Hard Rules
|
||||
|
||||
1. Never commit secrets or credentials
|
||||
2. Never add logic to `localDb.ts`
|
||||
2. Never barrel-import from `localDb.ts` — import specific `src/lib/db/*` modules
|
||||
3. Never use `eval()` / `new Function()` / implied eval
|
||||
4. Never commit directly to `main`
|
||||
5. Never write raw SQL in routes — use `src/lib/db/` modules
|
||||
|
||||
@@ -301,7 +301,7 @@ src/ # TypeScript (.ts / .tsx)
|
||||
open-sse/ # @omniroute/open-sse workspace
|
||||
├── executors/ # 89 executor implementation modules
|
||||
├── handlers/ # 11 request handlers (chat, responses, embeddings, images, etc.)
|
||||
├── mcp-server/ # MCP server (107 unique tools, 3 transports, 32 scopes)
|
||||
├── mcp-server/ # MCP server (110 unique tools, 3 transports, 33 scopes)
|
||||
├── services/ # 178 top-level services (combo, autoCombo, rateLimitManager, etc.)
|
||||
├── translator/ # Format translators (OpenAI ↔ Claude ↔ Gemini ↔ Responses ↔ Ollama)
|
||||
├── transformer/ # Responses API transformer
|
||||
|
||||
45
README.md
@@ -7,7 +7,7 @@
|
||||
|
||||
# 🚀 OmniRoute — The Free AI Gateway
|
||||
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 351 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 15–95% tokens (~89% avg) — never hit limits. 351 AI providers · 90+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
|
||||
<img src="./docs/diagrams/readme-hero.svg" width="100%" alt="OmniRoute — Never stop coding. Every AI tool → 352 providers — 150+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot & Antigravity into FREE Claude / GPT / Gemini with auto-fallback. RTK + Caveman stacked compression saves 15–95% tokens (~89% avg) — never hit limits. 352 AI providers · 150+ free tiers · ~1.51B free tokens/mo · 19 routing strategies · $0 to start."/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
|
||||
</div>
|
||||
|
||||
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute catalogs **445 free-tier entries across 39 recurring pool keys** and computes the token headline from the **20 pools with a published positive monthly budget**, deduplicated by shared pool. The result stays visible on the dashboard (`/dashboard/free-tiers`).
|
||||
> Stacking free tiers by hand is painful — dozens of SDKs, dozens of rate limits, and no idea how much you actually have. OmniRoute catalogs **446 free-tier entries across 38 recurring pool keys** and computes the token headline from the **20 pools with a published positive monthly budget**, deduplicated by shared pool. The result stays visible on the dashboard (`/dashboard/free-tiers`).
|
||||
|
||||
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.51B free tokens per month steady, up to ~2.13B in the first month with signup credits, from 39 documented recurring pool keys covering 445 cataloged free-tier entries behind one endpoint. Honest pool-deduped math — each shared pool counted once, including 20 recurring pools with a published positive monthly token budget; 13 providers are marked avoid in the terms-risk catalog so you decide. Budget bar includes Mistral 1B, LLM7 150M, Nara 150M, Gemini 60M and smaller pools, plus first-month signup credits and permanently-free no-token-cap providers surfaced separately so they never inflate the headline. Live used/remaining on /dashboard/free-tiers."/>
|
||||
<img src="./docs/diagrams/free-tier-budget.svg" width="100%" alt="OmniRoute free-tier budget card: ~1.51B free tokens per month steady, up to ~2.13B in the first month with signup credits, from 38 documented recurring pool keys covering 446 cataloged free-tier entries behind one endpoint. Honest pool-deduped math — each shared pool counted once, including 20 recurring pools with a published positive monthly token budget; 13 providers are marked avoid in the terms-risk catalog so you decide. Budget bar includes Mistral 1B, LLM7 150M, Nara 150M, Gemini 60M and smaller pools, plus first-month signup credits and permanently-free no-token-cap providers surfaced separately so they never inflate the headline. Live used/remaining on /dashboard/free-tiers."/>
|
||||
|
||||
> Animated summary of the live `/dashboard/free-tiers` page. Full methodology (pool dedupe, credit tiers, provider terms): **[docs/reference/FREE_TIERS.md](docs/reference/FREE_TIERS.md)**.
|
||||
>
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
| | v3.8.49 | **v3.8.50** | `v3.8.51+` |
|
||||
| ------------------------- | :-----: | :-----------------------: | :---------: |
|
||||
| 🌐 Providers | 290 | **350** | more queued |
|
||||
| 🌐 Providers | 290 | **352** | more queued |
|
||||
| 🧠 Unique chat model IDs | 1185 | **1312** | — |
|
||||
| 🖼️ Modality Bridge | — | 🆕 vision + audio + video | — |
|
||||
| 📡 Radar free catalog | — | 🆕 opt-in | — |
|
||||
@@ -101,7 +101,7 @@
|
||||
<tr>
|
||||
<td align="right"><b>⚙️ Features</b></td>
|
||||
<td align="center"><a href="#-combos--the-flagship">🎯 Combos</a></td>
|
||||
<td align="center"><a href="#-351-ai-providers--154-catalog-marked-free">🌐 Providers</a></td>
|
||||
<td align="center"><a href="#-352-ai-providers--154-catalog-marked-free">🌐 Providers</a></td>
|
||||
<td align="center"><a href="#-full-cli--a2a--mcp">🔌 CLI & MCP</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -210,7 +210,7 @@ curl http://localhost:20128/v1/chat/completions \
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 351 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 351 providers · up to 95% token savings on eligible workloads · $0 to start with 90+ free tiers and 56 recurring/keyless free-forever providers · 35 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
|
||||
<img src="./docs/diagrams/promise-pillars.svg" width="100%" alt="The Promise — One endpoint and 352 providers. Automatic fallback keeps routing while another healthy target is available. Six pillars: resilient fallback across 352 providers · up to 95% token savings on eligible workloads · $0 to start with 150+ free tiers and 53 recurring/keyless free-forever providers · 36 CLI/agent integrations through one config · OpenAI, Claude, Gemini and Responses API compatibility at /v1 · production controls including circuit breakers, TLS stealth, MCP 110 tools, A2A, memory, guardrails, evals and 39,000+ static test declarations across 5,100+ tracked test files."/>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
@@ -332,6 +332,8 @@ No combo to create. Set your model to `auto` (or a variant) and OmniRoute builds
|
||||
<tr><td align="left" nowrap><code>auto/cheap</code></td><td align="left">💰 Cheapest per token first</td></tr>
|
||||
<tr><td align="left" nowrap><code>auto/offline</code></td><td align="left">🔋 Most quota / rate-limit headroom first</td></tr>
|
||||
<tr><td align="left" nowrap><code>auto/smart</code></td><td align="left">🔭 Quality-first + 10% exploration to discover better models</td></tr>
|
||||
<tr><td align="left" nowrap><code>auto/lkgp</code></td><td align="left">📌 Explicit last-known-good-provider stickiness</td></tr>
|
||||
<tr><td align="left" nowrap><code>auto/chaos</code></td><td align="left">🧪 Fault-injection weights for resilience testing (chaos engineering)</td></tr>
|
||||
</table>
|
||||
|
||||
##
|
||||
@@ -449,7 +451,7 @@ All **19** strategies — mix & match per combo step:
|
||||
|
||||
### 🧱 Resilience is built in (3 independent layers)
|
||||
|
||||
<img src="./docs/diagrams/resilience-layers.svg" width="100%" alt="OmniRoute resilience — 3 independent self-healing layers, the right layer for the right failure. Layer 1 provider circuit breaker (whole provider): trips only on 408/5xx, thresholds OAuth 10× / API-key 15× / local 2×, resets 60s/30s/15s into a HALF-OPEN probe, lazy recovery; while OPEN the combo reroutes to the next provider. Layer 2 connection cooldown (one key/account): base 5s OAuth / 3s API-key, exponential ×2 backoff with anti-thundering-herd guard, 429 honors Retry-After, success clears all error state; one cooling key is skipped while sibling keys keep serving. Layer 3 model lockout (one model): per-model 429, local 404 or mode denials lock just that model — never the whole connection. Terminal states (banned, expired, credits exhausted) are for the operator, not cooldowns."/>
|
||||
<img src="./docs/diagrams/resilience-layers.svg" width="100%" alt="OmniRoute resilience — 3 independent self-healing layers, the right layer for the right failure. Layer 1 provider circuit breaker (whole provider): trips only on 408/5xx, thresholds OAuth 8× / API-key 12× / local 2×, resets 60s/30s/15s into a HALF-OPEN probe, lazy recovery; while OPEN the combo reroutes to the next provider. Layer 2 connection cooldown (one key/account): base 5s OAuth / 3s API-key, exponential ×2 backoff with anti-thundering-herd guard, 429 honors Retry-After, success clears all error state; one cooling key is skipped while sibling keys keep serving. Layer 3 model lockout (one model): per-model 429, local 404 or mode denials lock just that model — never the whole connection. Terminal states (banned, expired, credits exhausted) are for the operator, not cooldowns."/>
|
||||
|
||||
<sub>📖 [Auto-Combo Engine](docs/routing/AUTO-COMBO.md) · [Resilience Guide](docs/architecture/RESILIENCE_GUIDE.md)</sub>
|
||||
|
||||
@@ -461,7 +463,7 @@ All **19** strategies — mix & match per combo step:
|
||||
|
||||
</div>
|
||||
|
||||
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 351 providers, 90+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
|
||||
<img src="./docs/diagrams/comparison-table.svg" width="100%" alt="What sets OmniRoute apart — a dated feature snapshot vs 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute: 352 providers, 150+ free tiers built in, 19 routing strategies, 12-engine token compression, built-in MCP server with 110 tools, A2A agent protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, Desktop/Termux/PWA and 43 i18n UI locales. OmniRoute is MIT-licensed and self-hostable. Competitor capabilities and counts may change; see the linked methodology."/>
|
||||
|
||||
<sub>📊 Full methodology & per-feature detail vs 9router, OpenRouter, CLIProxyAPI & LiteLLM → [`docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md`](docs/comparison/OMNIROUTE_VS_ALTERNATIVES.md)</sub>
|
||||
|
||||
@@ -548,7 +550,7 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
|
||||
- **🗜️ Compression hardening** — default-on inflation guard, Caveman packs for DE / FR / JA + Chinese (wényán), RTK filters for Gradle & .NET. → [Compression](docs/compression/COMPRESSION_ENGINES.md)
|
||||
- **💸 Honest flat-rate cost** — subscription / coding-plan providers read **$0** in cost analytics; budget, quota & routing keep estimating. → [API Reference](docs/reference/API_REFERENCE.md)
|
||||
- **⚖️ Quota-Share routing** — split a shared account's quota fairly across pooled keys, work-conserving so idle slices are lent out. → [Resilience Guide](docs/architecture/RESILIENCE_GUIDE.md)
|
||||
- **🤖 One-command CLI/agent setup** — 12 registered `setup-*` commands; `omniroute run` launches 7 CLIs (Claude Code, Codex, Aider, Goose, OpenCode, Qwen Code, Gemini CLI); `omniroute configure` supports 9 targets with an interactive provider+model picker and per-context favorites. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
|
||||
- **🤖 One-command CLI/agent setup** — 13 registered `setup-*` commands; `omniroute run` launches 7 CLIs (Claude Code, Codex, Aider, Goose, OpenCode, Qwen Code, Gemini CLI); `omniroute configure` supports 10 targets with an interactive provider+model picker and per-context favorites. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
|
||||
- **🛰️ Remote mode** — drive a remote OmniRoute with scoped tokens (`connect` / `contexts` / `tokens`) + an `antigravity` OAuth helper for VPS installs. → [Remote Mode](docs/guides/REMOTE-MODE.md)
|
||||
- **🧭 Smarter auto-routing** — `auto/<category>:<tier>` combos, **Fusion** (model panel + judge), task-aware routing, per-request model / mode / USD-budget overrides. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
|
||||
- **🗜️ Pluggable compression** — 12 composable engines + Compression Studios: LLMLingua-2, two-tier Ultra, omniglyph, per-step fidelity gate, GCF v3.2, drag-reorder editor. → [Compression](docs/compression/COMPRESSION_ENGINES.md)
|
||||
@@ -559,9 +561,10 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
|
||||
- **🖼️ New endpoints** — `/v1/ocr` (Mistral OCR) and `/v1/audio/translations` (Whisper-style) round out the media surface. → [API Reference](docs/reference/API_REFERENCE.md)
|
||||
- **🎨 Image / video / audio generation** — one API for media: xAI Grok Imagine & Novita AI video, ComfyUI, Magnific, Adobe Firefly, Segmind, and speech providers such as ElevenLabs. → [API Reference](docs/reference/API_REFERENCE.md)
|
||||
- **🌍 Deployment & ops** — reverse-proxy `basePath`, browser-language auto-detect, per-key device tracking, root-less MITM trust, zh-TW localization. → [Environment](docs/reference/ENVIRONMENT.md)
|
||||
- **🤝 More providers & agents** — Cursor Cloud Agent, Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **351-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
|
||||
- **🤝 More providers & agents** — cloud agents (Codex Cloud, Cursor, Devin, Jules), Grok Build (xAI) with browser + OAuth login, Ollama first-class card, Claude Opus 5 & Sonnet 5, Kimi official partnership (Code/Web/Moonshot), Zed, Requesty, SenseNova, Yuanbao, Agnes AI… and a refreshed **352-provider catalog**. → [Providers](docs/reference/PROVIDER_REFERENCE.md)
|
||||
- **📡 Routing transparency** — every response carries an `X-OmniRoute-Decision` header naming the strategy/provider/latency that served it, a new `cache-optimized` combo strategy + Auto-Combo `cacheAffinity` factor route repeat requests back to the connection holding the cached prefix, and a read-only `/v1/auto-combo/{channel}/candidates` endpoint exposes an `auto/*` channel's live candidate pool. → [Auto-Combo](docs/routing/AUTO-COMBO.md)
|
||||
- **⚡ Local performance & infra** — one-click local Redis, Cloudflare Workers / Deno Deploy relay deployers, Bifrost & Mux as supervised embedded services. → [Embedded Services](docs/frameworks/EMBEDDED-SERVICES.md)
|
||||
- **🧩 Also in the box** — plugin framework + marketplace, Omni/Agent/GitHub skills frameworks, Obsidian vault integration (22 MCP tools), OpenAI-compatible Batch & Files APIs, semantic response cache, gamification with leaderboards, ACP agent discovery (15 built-in agents), scheduled log export to BigQuery, `auto/chaos` fault injection, a Telegram bot bridge, an in-app version manager and LMArena-ELO free-provider rankings. → [Docs](docs/README.md)
|
||||
|
||||
<br/>
|
||||
|
||||
@@ -612,7 +615,7 @@ the current catalog at **[radar.omniroute.online/planos](https://radar.omniroute
|
||||
<b>+ also works with</b> · Kiro · Command Code · Antigravity · Windsurf · AMP · <b>any OpenAI-compatible tool</b>
|
||||
</div>
|
||||
|
||||
<sub>📖 Per-tool setup for all 35 tools (26 CLI Code's + 9 CLI Agents) → [`docs/reference/CLI-TOOLS.md`](docs/reference/CLI-TOOLS.md) · 🧩 OpenCode plugin → [`@omniroute/opencode-provider`](https://www.npmjs.com/package/@omniroute/opencode-provider)</sub>
|
||||
<sub>📖 Per-tool setup for all 36 tools (26 CLI Code's + 10 CLI Agents) → [`docs/reference/CLI-TOOLS.md`](docs/reference/CLI-TOOLS.md) · 🧩 OpenCode plugin → [`@omniroute/opencode-provider`](https://www.npmjs.com/package/@omniroute/opencode-provider)</sub>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -631,7 +634,7 @@ omniroute run qwen --model glm/glm-5.2 -- -p "reply OK"
|
||||
omniroute run gemini --model glm/glm-5.2 -- --skip-trust -p "reply OK"
|
||||
|
||||
# Or pick provider+model interactively and write the tool's own config:
|
||||
omniroute configure codex # also: claude opencode qwen aider goose cline continue kilo
|
||||
omniroute configure codex # also: claude opencode qwen aider goose gemini cline continue kilo
|
||||
```
|
||||
|
||||
Every command honors the active remote context (`omniroute connect <host>`), `--dry-run`
|
||||
@@ -642,11 +645,11 @@ of your shell history. → [CLI Integrations](docs/guides/CLI-INTEGRATIONS.md)
|
||||
|
||||
<div align="center">
|
||||
|
||||
## 🌐 351 AI Providers — 154 Catalog-Marked Free
|
||||
## 🌐 352 AI Providers — 152 Catalog-Marked Free
|
||||
|
||||
</div>
|
||||
|
||||
> **351 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **154 carrying `hasFree: true` discovery metadata**. The chat model registry covers **268 providers / 2,566 distinct provider-model pairs / 1,312 raw model IDs**; the separate free-budget catalog has **455 per-model rows**, **40 recurring pools** and **56 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
|
||||
> **352 registered providers** across the canonical chat, media, search, local, cloud-agent and system collections, including **152 carrying `hasFree: true` discovery metadata**. The chat model registry covers **229 providers / 2,554 distinct provider-model pairs / 1,283 raw model IDs**; the separate free-budget catalog has **446 per-model rows**, **38 recurring pools** and **53 recurring/keyless free-forever providers**. These are different denominators by design; definitions and pool-deduped calculations live in the [Provider Reference](docs/reference/PROVIDER_REFERENCE.md) and [Free Tiers](docs/reference/FREE_TIERS.md).
|
||||
|
||||
<div align="center">
|
||||
|
||||
@@ -810,7 +813,7 @@ Tokens are scoped `read` / `write` / `admin`; process-spawning routes stay loopb
|
||||
|
||||
<div align="left">
|
||||
|
||||
<img src="./docs/diagrams/cli-terminal.svg" width="50%" alt="Animated terminal demoing the OmniRoute CLI — omniroute providers list, omniroute combo list and omniroute health — cycling over the 85-command top-level surface: providers · oauth · keys · combo · nodes · models · cache · compression · cost · usage · quota · health · resilience · telemetry · logs · audit · mcp · a2a · cloud · memory · skills · eval · tunnel · backup · sync · webhooks · policy · pricing · translator · simulate …"/>
|
||||
<img src="./docs/diagrams/cli-terminal.svg" width="50%" alt="Animated terminal demoing the OmniRoute CLI — omniroute providers list, omniroute combo list and omniroute health — cycling over the 86-command top-level surface: providers · oauth · keys · combo · nodes · models · cache · compression · cost · usage · quota · health · resilience · telemetry · logs · audit · mcp · a2a · cloud · memory · skills · eval · tunnel · backup · sync · webhooks · policy · pricing · translator · simulate …"/>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -821,11 +824,11 @@ Expose OmniRoute over **MCP**, **A2A**, a **REST API**, **webhooks** or a **remo
|
||||
<table>
|
||||
<tr><th align="left">Interface</th><th align="left">Endpoint / command</th><th align="left">Use it for</th></tr>
|
||||
<tr><td align="left" nowrap>🧰 <b>MCP (stdio)</b></td><td align="left" nowrap><code>omniroute --mcp</code></td><td align="left">Plug into Claude Desktop, Cursor, any MCP client</td></tr>
|
||||
<tr><td align="left" nowrap>🌊 <b>MCP (HTTP)</b></td><td align="left" nowrap><code>/api/mcp/stream</code></td><td align="left">Remote MCP — <b>110 tools</b>, 33 scopes, full audit trail</td></tr>
|
||||
<tr><td align="left" nowrap>🌊 <b>MCP (HTTP)</b></td><td align="left" nowrap><code>/api/mcp/stream</code></td><td align="left">Remote MCP — <b>110 tools</b>, 33 scopes (enforcement opt-in), full audit trail</td></tr>
|
||||
<tr><td align="left" nowrap>📡 <b>MCP (SSE)</b></td><td align="left" nowrap><code>/api/mcp/sse</code></td><td align="left">Streaming MCP transport</td></tr>
|
||||
<tr><td align="left" nowrap>🤝 <b>A2A</b></td><td align="left" nowrap><code>/.well-known/agent.json</code></td><td align="left">Agent-to-agent, <b>JSON-RPC 2.0</b> + SSE, 6 skills</td></tr>
|
||||
<tr><td align="left" nowrap>🌐 <b>REST API</b></td><td align="left" nowrap><code>/v1/*</code></td><td align="left">OpenAI-compatible — chat, embeddings, images, audio, OCR</td></tr>
|
||||
<tr><td align="left" nowrap>🔔 <b>Webhooks</b></td><td align="left" nowrap><code>/api/webhooks</code></td><td align="left">Push events (usage, quota, errors, routing) to your URL</td></tr>
|
||||
<tr><td align="left" nowrap>🔔 <b>Webhooks</b></td><td align="left" nowrap><code>/api/webhooks</code></td><td align="left">Push request / quota events to Slack, Discord, Telegram or any URL</td></tr>
|
||||
<tr><td align="left" nowrap>🛰️ <b>Remote CLI</b></td><td align="left" nowrap><code>omniroute connect <host></code></td><td align="left">Drive a remote instance with scoped access tokens</td></tr>
|
||||
</table>
|
||||
|
||||
@@ -917,6 +920,8 @@ The 12 engines above shrink what goes **in**. Three more layers shape **how**, *
|
||||
- **🪄 Output Styles** _(output-axis steering)_ — inject deterministic, cache-safe response-shaping instructions; combinable, each at `lite` / `full` / `ultra` intensity. Adding a style is a one-line registry entry:
|
||||
- **Terse prose** — drop filler / articles / hedging; keep technical substance exact.
|
||||
- **Less code** — "lazy senior dev" YAGNI: smallest working change, no unrequested scaffolding.
|
||||
- **Ponytail (lazy senior dev)** — climb the YAGNI ladder, fix the root cause, smallest working diff.
|
||||
- **I have ADHD (action-first)** — next action leads, steps numbered, one concrete next step, no preamble.
|
||||
- **Terse CJK (文言)** — classical-Chinese ultra-terse style (locale-gated to `zh`).
|
||||
- **🎯 Adaptive context-budget** _(the dial)_ — instead of one on/off token threshold, escalate the cheapest, most-lossless engines only as far as needed to **fit the model's context window**. Policy: `reserve-output` (default, model-aware) · `percentage` · `absolute`. Mode: `floor` (guarantee fit) · `replace-autotrigger` (your explicit choice wins) · `off` (legacy threshold).
|
||||
- **🎛️ Where compression is decided** _(precedence, high → low)_ — per-request `x-omniroute-compression` header › routing-combo override › active named profile › adaptive / auto-trigger › panel default › off. The applied plan echoes back in the `X-OmniRoute-Compression: <mode>; source=<source>` response header.
|
||||
@@ -1202,7 +1207,7 @@ Métricas canônicas em 2026-08-24: **1.029 vídeos únicos** · **11.132.922 vi
|
||||
<tr><td nowrap><b>Runtime</b></td><td>Node.js 22.x / 24.x LTS — <code>>=22.22.2 <23 || >=24.0.0 <27</code></td></tr>
|
||||
<tr><td nowrap><b>Language</b></td><td>TypeScript 6.0 — <b>100% TypeScript</b> across <code>src/</code> and <code>open-sse/</code> (zero <code>any</code> in core since v2.0)</td></tr>
|
||||
<tr><td nowrap><b>Framework</b></td><td>Next.js 16 + React 19 + Tailwind CSS 4</td></tr>
|
||||
<tr><td nowrap><b>Database</b></td><td>better-sqlite3 (SQLite, WAL journaling) + LowDB (JSON legacy) — 120 domain modules, 166 migrations</td></tr>
|
||||
<tr><td nowrap><b>Database</b></td><td>better-sqlite3 (SQLite, WAL journaling) + LowDB (JSON legacy) — 122 domain modules, 167 migrations</td></tr>
|
||||
<tr><td nowrap><b>Memory</b></td><td>SQLite FTS5 full-text + int8-quantized vector embeddings, typed decay</td></tr>
|
||||
<tr><td nowrap><b>Schemas</b></td><td>Zod 4 — MCP tool I/O validation + API contracts</td></tr>
|
||||
<tr><td nowrap><b>Protocols</b></td><td>MCP (stdio / HTTP / SSE) + A2A v0.3 (JSON-RPC 2.0 + SSE)</td></tr>
|
||||
@@ -1265,7 +1270,7 @@ Métricas canônicas em 2026-08-24: **1.029 vídeos únicos** · **11.132.922 vi
|
||||
<tr><td nowrap><b><a href="docs/architecture/RESILIENCE_GUIDE.md">Resilience Guide</a></b></td><td>Circuit breakers, cooldowns, queue, anti-thundering herd, TLS spoofing</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/routing/AUTO-COMBO.md">Auto-Combo Engine</a></b></td><td>15-factor scoring, mode packs, self-healing</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/ops/PROXY_GUIDE.md">Proxy Guide</a></b></td><td>3-level proxy system, 1proxy marketplace, registry CRUD</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/reference/FREE_TIERS.md">Free Tiers</a></b></td><td>Consolidated directory: 39 documented recurring pools / 445 cataloged free-tier entries</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/reference/FREE_TIERS.md">Free Tiers</a></b></td><td>Consolidated directory: 38 documented recurring pools / 446 cataloged free-tier entries</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/guides/FEATURES.md">Features Gallery</a></b></td><td>Visual dashboard tour with screenshots</td></tr>
|
||||
<tr><td nowrap><b><a href="docs/architecture/CODEBASE_DOCUMENTATION.md">Codebase Documentation</a></b></td><td>Beginner-friendly codebase walkthrough</td></tr>
|
||||
</table>
|
||||
@@ -1666,7 +1671,7 @@ MIT License - see [LICENSE](LICENSE) for details.
|
||||
|
||||
**[⬆ Back to top](#-omniroute)** · Built with ❤️ for the open-source AI community.
|
||||
|
||||
<sub>OmniRoute v3.8.50 · Node ≥22.22.2 · MIT License · <a href="https://omniroute.online">omniroute.online</a></sub>
|
||||
<sub>OmniRoute v3.8.51 · Node ≥22.22.2 · MIT License · <a href="https://omniroute.online">omniroute.online</a></sub>
|
||||
|
||||
</div>
|
||||
<!-- GitHub Discussions enabled for community Q&A -->
|
||||
|
||||
@@ -176,13 +176,14 @@ function isWithinRoot(ancestor, candidate) {
|
||||
* Register the ESM resolve hook for the current process. Safe to call multiple
|
||||
* times — subsequent calls are no-ops once the hook is installed.
|
||||
*
|
||||
* Uses Node's stable `module.register()` API (available since Node 20.6,
|
||||
* required Node 22+ here). The hook runs in a worker thread but only reads the
|
||||
* captured `root`, so no shared-state hazards.
|
||||
* Modern runtimes import the hook module in-thread, initialize its root with a
|
||||
* plain function call, and register its synchronous resolver through
|
||||
* `module.registerHooks()`. Runtimes without that API (notably Bun) retain the
|
||||
* `module.register()` worker-thread loader lifecycle path.
|
||||
*
|
||||
* @param {string} root Absolute path to the package root.
|
||||
* @returns {Promise<boolean>} Resolves `true` once registered (or if already
|
||||
* registered), `false` on environments where `module.register` is unavailable.
|
||||
* registered), `false` when neither registration API is usable.
|
||||
*/
|
||||
let _registered = false;
|
||||
export async function registerAliasResolver(root) {
|
||||
@@ -201,7 +202,7 @@ export async function registerAliasResolver(root) {
|
||||
}
|
||||
|
||||
try {
|
||||
const { register } = await import("node:module");
|
||||
const mod = await import("node:module");
|
||||
// #7808: load the hook from a real file on disk via pathToFileURL() instead
|
||||
// of building a `data:text/javascript,...` URL dynamically. CodeQL's
|
||||
// `js/incomplete-url-substring-sanitization` flagged the interpolated
|
||||
@@ -211,14 +212,21 @@ export async function registerAliasResolver(root) {
|
||||
// package.json "files": ["bin/"].
|
||||
const hookPath = join(__dirname, "aliasResolverHook.mjs");
|
||||
const hookUrl = pathToFileURL(hookPath);
|
||||
register(hookUrl, { data: { root } });
|
||||
if (typeof mod.registerHooks === "function") {
|
||||
const hook = await import(hookUrl.href);
|
||||
hook.initialize({ root });
|
||||
mod.registerHooks({ resolve: hook.resolve });
|
||||
_registered = true;
|
||||
return true;
|
||||
}
|
||||
mod.register(hookUrl, { data: { root } });
|
||||
_registered = true;
|
||||
return true;
|
||||
} catch {
|
||||
// Older Node or sandboxed env without module.register — fall back to the
|
||||
// default resolver. The bug will resurface only in the exact global-install
|
||||
// scenario, which is what we explicitly patched; other entry points still
|
||||
// work because they import via relative paths.
|
||||
// Runtime or sandboxed env without a usable module hook API — fall back to
|
||||
// the default resolver. The bug will resurface only in the exact
|
||||
// global-install scenario, which is what we explicitly patched; other entry
|
||||
// points still work because they import via relative paths.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export function register_combos(parent) {
|
||||
});
|
||||
tag.command("post-api-combos")
|
||||
.description("Create routing combo")
|
||||
.option("--body <jsonOrPath>", "JSON body or @path/to/file.json")
|
||||
.requiredOption("--body <jsonOrPath>", "JSON body or @path/to/file.json")
|
||||
.action(async (opts, cmd) => {
|
||||
const gOpts = cmd.optsWithGlobals();
|
||||
let url = "/api/combos";
|
||||
@@ -44,7 +44,7 @@ export function register_combos(parent) {
|
||||
tag.command("put-api-combos-id-")
|
||||
.description("Update combo")
|
||||
.requiredOption("--id <id>", "")
|
||||
.option("--body <jsonOrPath>", "JSON body or @path/to/file.json")
|
||||
.requiredOption("--body <jsonOrPath>", "JSON body or @path/to/file.json")
|
||||
.action(async (opts, cmd) => {
|
||||
const gOpts = cmd.optsWithGlobals();
|
||||
let url = "/api/combos/{id}";
|
||||
@@ -62,7 +62,7 @@ export function register_combos(parent) {
|
||||
tag.command("patch-api-combos-id-")
|
||||
.description("Update combo")
|
||||
.requiredOption("--id <id>", "")
|
||||
.option("--body <jsonOrPath>", "JSON body or @path/to/file.json")
|
||||
.requiredOption("--body <jsonOrPath>", "JSON body or @path/to/file.json")
|
||||
.action(async (opts, cmd) => {
|
||||
const gOpts = cmd.optsWithGlobals();
|
||||
let url = "/api/combos/{id}";
|
||||
@@ -99,10 +99,17 @@ export function register_combos(parent) {
|
||||
});
|
||||
tag.command("post-api-combos-test")
|
||||
.description("Test a combo configuration")
|
||||
.requiredOption("--body <jsonOrPath>", "JSON body or @path/to/file.json")
|
||||
.action(async (opts, cmd) => {
|
||||
const gOpts = cmd.optsWithGlobals();
|
||||
let url = "/api/combos/test";
|
||||
const res = await apiFetch(url, { method: "POST", baseUrl: gOpts.baseUrl, apiKey: gOpts.apiKey });
|
||||
let body;
|
||||
if (opts.body) {
|
||||
body = opts.body.startsWith("@")
|
||||
? JSON.parse(readFileSync(opts.body.slice(1), "utf8"))
|
||||
: JSON.parse(opts.body);
|
||||
}
|
||||
const res = await apiFetch(url, { method: "POST", body, baseUrl: gOpts.baseUrl, apiKey: gOpts.apiKey });
|
||||
const data = res.ok ? await res.json() : await res.text();
|
||||
emit(data, gOpts);
|
||||
});
|
||||
|
||||
@@ -93,6 +93,16 @@ export const CLI_TARGET_MANIFEST = Object.freeze({
|
||||
configure: true,
|
||||
runModel: null,
|
||||
}),
|
||||
"5dive": Object.freeze({
|
||||
// 5dive is a fleet manager, not a coding CLI: it points its own `claude`
|
||||
// agents at an endpoint. `omniroute run 5dive` would have nothing to
|
||||
// launch, so this is configure-only.
|
||||
description: "5dive (agent fleet)",
|
||||
aliases: Object.freeze(["fivedive", "5dive-cli"]),
|
||||
run: false,
|
||||
configure: true,
|
||||
runModel: null, // travels as the profile's ANTHROPIC_DEFAULT_*_MODEL
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,6 +39,7 @@ export const SETUP_MODULES = {
|
||||
cline: { module: "./setup-cline.mjs", exportName: "runSetupClineCommand" },
|
||||
continue: { module: "./setup-continue.mjs", exportName: "runSetupContinueCommand" },
|
||||
kilo: { module: "./setup-kilo.mjs", exportName: "runSetupKiloCommand" },
|
||||
"5dive": { module: "./setup-5dive.mjs", exportName: "runSetup5diveCommand" },
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,80 @@ function parseHeader(kv) {
|
||||
return { name: kv.slice(0, eq), value: kv.slice(eq + 1) };
|
||||
}
|
||||
|
||||
function getRootCommand(cmd) {
|
||||
let curr = cmd;
|
||||
while (curr.parent) curr = curr.parent;
|
||||
return curr;
|
||||
}
|
||||
|
||||
function resolveNodeEndpoint(opts, cmd) {
|
||||
if (opts.endpoint) {
|
||||
return { endpoint: opts.endpoint, apiFetchOpts: cmd.optsWithGlobals() };
|
||||
}
|
||||
if (opts.nodeUrl) {
|
||||
return { endpoint: opts.nodeUrl, apiFetchOpts: cmd.optsWithGlobals() };
|
||||
}
|
||||
|
||||
// Check if --base-url, --endpoint, or --node-url was explicitly passed after the subcommand
|
||||
const root = getRootCommand(cmd);
|
||||
const rawArgs = root.rawArgs || process.argv;
|
||||
const cmdName = cmd.name();
|
||||
|
||||
let subArgsStart = -1;
|
||||
for (let i = 0; i < rawArgs.length - 1; i++) {
|
||||
if (rawArgs[i] === "nodes" || rawArgs[i] === "provider-nodes") {
|
||||
if (rawArgs[i + 1] === cmdName) {
|
||||
subArgsStart = i + 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let explicitSubcommandBaseUrl = undefined;
|
||||
let serverBaseUrl = undefined;
|
||||
|
||||
if (subArgsStart !== -1) {
|
||||
const preArgs = rawArgs.slice(0, subArgsStart);
|
||||
for (let i = 0; i < preArgs.length; i++) {
|
||||
if (preArgs[i] === "--base-url" && i + 1 < preArgs.length) {
|
||||
serverBaseUrl = preArgs[i + 1];
|
||||
} else if (preArgs[i].startsWith("--base-url=")) {
|
||||
serverBaseUrl = preArgs[i].slice("--base-url=".length);
|
||||
}
|
||||
}
|
||||
|
||||
const subArgs = rawArgs.slice(subArgsStart);
|
||||
for (let i = 0; i < subArgs.length; i++) {
|
||||
const arg = subArgs[i];
|
||||
if (
|
||||
(arg === "--base-url" || arg === "--endpoint" || arg === "--node-url") &&
|
||||
i + 1 < subArgs.length
|
||||
) {
|
||||
explicitSubcommandBaseUrl = subArgs[i + 1];
|
||||
} else if (
|
||||
arg.startsWith("--base-url=") ||
|
||||
arg.startsWith("--endpoint=") ||
|
||||
arg.startsWith("--node-url=")
|
||||
) {
|
||||
explicitSubcommandBaseUrl = arg.slice(arg.indexOf("=") + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (explicitSubcommandBaseUrl !== undefined) {
|
||||
const globals = cmd.optsWithGlobals?.() ?? {};
|
||||
const apiFetchOpts = { ...globals };
|
||||
if (serverBaseUrl) {
|
||||
apiFetchOpts.baseUrl = serverBaseUrl;
|
||||
} else {
|
||||
delete apiFetchOpts.baseUrl;
|
||||
}
|
||||
return { endpoint: explicitSubcommandBaseUrl, apiFetchOpts };
|
||||
}
|
||||
|
||||
return { endpoint: undefined, apiFetchOpts: cmd.optsWithGlobals() };
|
||||
}
|
||||
|
||||
const nodeSchema = [
|
||||
{ key: "id", header: "Node ID", width: 22 },
|
||||
{ key: "provider", header: "Provider", width: 16 },
|
||||
@@ -70,7 +144,8 @@ export function registerNodes(program) {
|
||||
nodes
|
||||
.command("add")
|
||||
.requiredOption("--provider <p>", t("nodes.add.provider"))
|
||||
.requiredOption("--endpoint <url>", t("nodes.add.baseUrl"))
|
||||
.option("--endpoint <url>", t("nodes.add.baseUrl"))
|
||||
.option("--base-url <url>", t("nodes.add.baseUrl"))
|
||||
.option("--name <n>", t("nodes.add.name"))
|
||||
.option("--weight <w>", t("nodes.add.weight"), parseInt, 100)
|
||||
.option("--region <r>", t("nodes.add.region"))
|
||||
@@ -81,9 +156,14 @@ export function registerNodes(program) {
|
||||
[]
|
||||
)
|
||||
.action(async (opts, cmd) => {
|
||||
const { endpoint, apiFetchOpts } = resolveNodeEndpoint(opts, cmd);
|
||||
if (!endpoint) {
|
||||
process.stderr.write(`error: required option '--endpoint <url>' or '--base-url <url>' not specified\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
const body = {
|
||||
provider: opts.provider,
|
||||
baseUrl: opts.endpoint,
|
||||
baseUrl: endpoint,
|
||||
name: opts.name,
|
||||
weight: opts.weight,
|
||||
region: opts.region,
|
||||
@@ -91,7 +171,7 @@ export function registerNodes(program) {
|
||||
headers: opts.authHeader?.length ? opts.authHeader : undefined,
|
||||
};
|
||||
const res = await apiFetch("/api/provider-nodes", {
|
||||
...cmd.optsWithGlobals(),
|
||||
...apiFetchOpts,
|
||||
method: "POST",
|
||||
body,
|
||||
});
|
||||
@@ -99,24 +179,26 @@ export function registerNodes(program) {
|
||||
process.stderr.write(`Error: ${res.status}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
emit(await res.json(), cmd.optsWithGlobals());
|
||||
emit(await res.json(), apiFetchOpts);
|
||||
});
|
||||
|
||||
nodes
|
||||
.command("update <nodeId>")
|
||||
.option("--endpoint <url>", t("nodes.update.baseUrl"))
|
||||
.option("--base-url <url>", t("nodes.update.baseUrl"))
|
||||
.option("--name <n>", t("nodes.update.name"))
|
||||
.option("--weight <w>", t("nodes.update.weight"), parseInt)
|
||||
.option("--region <r>", t("nodes.update.region"))
|
||||
.option("--enabled <b>", t("nodes.update.enabled"), (v) => v === "true")
|
||||
.action(async (id, opts, cmd) => {
|
||||
const { endpoint, apiFetchOpts } = resolveNodeEndpoint(opts, cmd);
|
||||
const body = {};
|
||||
if (opts.endpoint !== undefined) body.baseUrl = opts.endpoint;
|
||||
if (endpoint !== undefined) body.baseUrl = endpoint;
|
||||
for (const k of ["name", "weight", "region", "enabled"]) {
|
||||
if (opts[k] !== undefined) body[k] = opts[k];
|
||||
}
|
||||
const res = await apiFetch(`/api/provider-nodes/${id}`, {
|
||||
...cmd.optsWithGlobals(),
|
||||
...apiFetchOpts,
|
||||
method: "PUT",
|
||||
body,
|
||||
});
|
||||
@@ -124,7 +206,7 @@ export function registerNodes(program) {
|
||||
process.stderr.write(`Error: ${res.status}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
emit(await res.json(), cmd.optsWithGlobals());
|
||||
emit(await res.json(), apiFetchOpts);
|
||||
});
|
||||
|
||||
nodes
|
||||
@@ -145,19 +227,25 @@ export function registerNodes(program) {
|
||||
|
||||
nodes
|
||||
.command("validate")
|
||||
.requiredOption("--endpoint <url>", t("nodes.validate.baseUrl"))
|
||||
.option("--endpoint <url>", t("nodes.validate.baseUrl"))
|
||||
.option("--base-url <url>", t("nodes.validate.baseUrl"))
|
||||
.requiredOption("--provider <p>", t("nodes.validate.provider"))
|
||||
.action(async (opts, cmd) => {
|
||||
const { endpoint, apiFetchOpts } = resolveNodeEndpoint(opts, cmd);
|
||||
if (!endpoint) {
|
||||
process.stderr.write(`error: required option '--endpoint <url>' or '--base-url <url>' not specified\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
const res = await apiFetch("/api/provider-nodes/validate", {
|
||||
...cmd.optsWithGlobals(),
|
||||
...apiFetchOpts,
|
||||
method: "POST",
|
||||
body: { baseUrl: opts.endpoint, provider: opts.provider },
|
||||
body: { baseUrl: endpoint, provider: opts.provider },
|
||||
});
|
||||
if (!res.ok) {
|
||||
process.stderr.write(`Error: ${res.status}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
emit(await res.json(), cmd.optsWithGlobals());
|
||||
emit(await res.json(), apiFetchOpts);
|
||||
});
|
||||
|
||||
nodes
|
||||
|
||||
@@ -66,6 +66,7 @@ import { registerSetupClaude } from "./setup-claude.mjs";
|
||||
import { registerSetupOpencode } from "./setup-opencode.mjs";
|
||||
import { registerSetupCline } from "./setup-cline.mjs";
|
||||
import { registerSetupKilo } from "./setup-kilo.mjs";
|
||||
import { registerSetup5dive } from "./setup-5dive.mjs";
|
||||
import { registerSetupContinue } from "./setup-continue.mjs";
|
||||
import { registerSetupCursor } from "./setup-cursor.mjs";
|
||||
import { registerSetupRoo } from "./setup-roo.mjs";
|
||||
@@ -152,6 +153,7 @@ export function registerCommands(program) {
|
||||
registerSetupOpencode(program);
|
||||
registerSetupCline(program);
|
||||
registerSetupKilo(program);
|
||||
registerSetup5dive(program);
|
||||
registerSetupContinue(program);
|
||||
registerSetupCursor(program);
|
||||
registerSetupRoo(program);
|
||||
|
||||
315
bin/cli/commands/setup-5dive.mjs
Normal file
@@ -0,0 +1,315 @@
|
||||
/**
|
||||
* omniroute setup-5dive — point a 5dive agent fleet at OmniRoute.
|
||||
*
|
||||
* 5dive (https://5dive.com) manages a fleet of long-running coding agents, each
|
||||
* one a systemd unit under its own Unix user. It is not itself a coding CLI, so
|
||||
* there is nothing for `omniroute run` to launch — this is a configure-only
|
||||
* target.
|
||||
*
|
||||
* Unlike the other recipes, 5dive does not read a config file out of $HOME. Its
|
||||
* credentials live in AUTH PROFILES under /var/lib/5dive/auth-profiles/<name>/,
|
||||
* and the supported way to write one is the CLI itself:
|
||||
*
|
||||
* 5dive agent auth set claude --provider=<id> --base-url=<url> \
|
||||
* --api-key=- --auth-profile=<name> --model=<slug>
|
||||
*
|
||||
* Four value flags, all four load-bearing (verified against 5dive-cli main,
|
||||
* 2026-08-27):
|
||||
* --provider `--base-url` is refused without it, rather than accepted
|
||||
* and silently dropped. `openai` here is 5dive's BYO id for
|
||||
* "a custom Anthropic-compatible endpoint", not a vendor
|
||||
* choice — override with --byo-provider.
|
||||
* --base-url OmniRoute's Anthropic surface, ROOT url with no /v1.
|
||||
* --auth-profile BYO credentials are profile-scoped; required for claude.
|
||||
* --model `openai` has no row in 5dive's built-in endpoint catalog,
|
||||
* so there are no per-tier model ids to inherit.
|
||||
*
|
||||
* The key is handed over on stdin (`--api-key=-`) so it never reaches argv.
|
||||
*
|
||||
* Two things this recipe cannot do for you, and says so instead of failing
|
||||
* obscurely:
|
||||
* 1. Writing an auth profile is root-only on the 5dive host. We re-exec
|
||||
* through sudo when we are not root (disable with --no-sudo).
|
||||
* 2. `agent auth set` writes the profile and restarts the agents bound to it,
|
||||
* but each seat also carries its OWN runtime model pin, and that pin wins
|
||||
* over the profile's ANTHROPIC_DEFAULT_*_MODEL. Pass --agent <name> (repeatable)
|
||||
* to pin the seats too; otherwise we print the command for them.
|
||||
*/
|
||||
|
||||
import { spawn } from "node:child_process";
|
||||
import { printHeading, printInfo, printSuccess, printError, createPrompt } from "../io.mjs";
|
||||
import { resolveActiveContext } from "../contexts.mjs";
|
||||
|
||||
const DEFAULT_PROFILE = "omniroute";
|
||||
|
||||
/** 5dive's `claude` BYO endpoint is the Anthropic surface ROOT — strip a trailing /v1. */
|
||||
function stripToRoot(url) {
|
||||
const s = String(url || "").replace(/\/+$/, "");
|
||||
return s.endsWith("/v1") ? s.slice(0, -3) : s;
|
||||
}
|
||||
|
||||
/** Resolve baseUrl (ROOT, no /v1) + apiKey from flags -> active context -> localhost. */
|
||||
export function resolveFivediveTarget(opts = {}) {
|
||||
let baseUrl;
|
||||
if (opts.remote) baseUrl = stripToRoot(opts.remote);
|
||||
else {
|
||||
try {
|
||||
baseUrl = stripToRoot(
|
||||
resolveActiveContext(opts.context ?? process.env.OMNIROUTE_CONTEXT)?.baseUrl
|
||||
);
|
||||
} catch {
|
||||
/* no context configured */
|
||||
}
|
||||
if (!baseUrl)
|
||||
baseUrl = `http://localhost:${Number(opts.port ?? process.env.PORT ?? 20128) || 20128}`;
|
||||
}
|
||||
let apiKey = opts.apiKey ?? opts["api-key"];
|
||||
if (!apiKey) {
|
||||
try {
|
||||
const c = resolveActiveContext(opts.context ?? process.env.OMNIROUTE_CONTEXT);
|
||||
apiKey = c?.accessToken || c?.apiKey;
|
||||
} catch {
|
||||
/* no context configured */
|
||||
}
|
||||
}
|
||||
if (!apiKey) apiKey = process.env.OMNIROUTE_API_KEY || "";
|
||||
return { baseUrl, apiKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* 5dive refuses a base URL before storing it, and the rule is not the obvious
|
||||
* one: the agent's key rides this URL on every request, so https:// is required
|
||||
* unless the host is loopback. Reproduce the check here so the operator gets the
|
||||
* reason at the point of choosing, not a validation error three commands later.
|
||||
*/
|
||||
export function validateFivediveBaseUrl(rawUrl) {
|
||||
const url = String(rawUrl || "");
|
||||
if (!url) return { ok: false, reason: "A base URL is required." };
|
||||
if (url.startsWith("https://")) return { ok: true };
|
||||
if (!url.startsWith("http://")) {
|
||||
return { ok: false, reason: `Unsupported scheme in '${url}' (expected http:// or https://).` };
|
||||
}
|
||||
let host = url.slice("http://".length);
|
||||
host = host.split("/")[0].split("?")[0];
|
||||
host = host.startsWith("[") ? `${host.slice(0, host.indexOf("]"))}]` : host.split(":")[0];
|
||||
if (host === "127.0.0.1" || host === "localhost" || host === "[::1]") return { ok: true };
|
||||
return {
|
||||
ok: false,
|
||||
reason:
|
||||
`5dive accepts http:// only for a loopback host; '${host}' is off-box, so the agent's ` +
|
||||
`API key would travel in plaintext. Serve OmniRoute over https:// and pass ` +
|
||||
`--remote https://${host}...`,
|
||||
};
|
||||
}
|
||||
|
||||
/** Argv for the profile write. The key is NOT here — it goes in on stdin. */
|
||||
export function buildFivediveAuthArgs({ baseUrl, profile, model, provider = "openai" }) {
|
||||
return [
|
||||
"agent",
|
||||
"auth",
|
||||
"set",
|
||||
"claude",
|
||||
`--provider=${provider}`,
|
||||
`--base-url=${baseUrl}`,
|
||||
"--api-key=-",
|
||||
`--auth-profile=${profile}`,
|
||||
`--model=${model}`,
|
||||
];
|
||||
}
|
||||
|
||||
/** Argv for one seat's runtime model pin, which outranks the profile's env defaults. */
|
||||
export function buildFivedivePinArgs(agent, model) {
|
||||
return ["agent", "config", agent, "set", `model=${model}`];
|
||||
}
|
||||
|
||||
/** Prepend sudo when the profile write needs root and we do not have it. */
|
||||
export function withPrivilege(bin, args, { isRoot, useSudo }) {
|
||||
if (isRoot || !useSudo) return [bin, args];
|
||||
return ["sudo", [bin, ...args]];
|
||||
}
|
||||
|
||||
function quote(arg) {
|
||||
return /^[A-Za-z0-9_@%+=:,./-]+$/.test(arg) ? arg : `'${String(arg).replace(/'/g, "'\\''")}'`;
|
||||
}
|
||||
|
||||
/** Render argv the way an operator would type it. */
|
||||
export function renderCommand(bin, args) {
|
||||
return [bin, ...args].map(quote).join(" ");
|
||||
}
|
||||
|
||||
function run(bin, args, stdinPayload) {
|
||||
return new Promise((resolve) => {
|
||||
const child = spawn(bin, args, {
|
||||
// sudo reads its password straight from the tty, so stdin stays free for
|
||||
// the API key.
|
||||
stdio: [stdinPayload === undefined ? "inherit" : "pipe", "inherit", "inherit"],
|
||||
});
|
||||
child.on("error", (e) => resolve({ code: 1, error: e }));
|
||||
child.on("close", (code) => resolve({ code: code ?? 1 }));
|
||||
if (stdinPayload !== undefined && child.stdin) {
|
||||
child.stdin.end(stdinPayload);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchModelIds(baseUrl, apiKey) {
|
||||
try {
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
|
||||
const res = await fetch(`${baseUrl}/v1/models`, { headers, signal: AbortSignal.timeout(8000) });
|
||||
if (!res.ok) return [];
|
||||
const body = await res.json();
|
||||
const list = Array.isArray(body) ? body : (body.data ?? body.models ?? []);
|
||||
return list.map((m) => (typeof m === "string" ? m : m?.id)).filter(Boolean);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function agentList(opts) {
|
||||
const raw = opts.agent ?? opts.agents ?? [];
|
||||
return (Array.isArray(raw) ? raw : [raw]).map((a) => String(a).trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
export async function runSetup5diveCommand(opts = {}) {
|
||||
const { baseUrl, apiKey } = resolveFivediveTarget(opts);
|
||||
const dryRun = Boolean(opts.dryRun ?? opts["dry-run"]);
|
||||
const bin = opts.fivediveBin ?? opts["fivedive-bin"] ?? process.env.CLI_5DIVE_BIN ?? "5dive";
|
||||
const profile = String(opts.authProfile ?? opts["auth-profile"] ?? opts.name ?? DEFAULT_PROFILE);
|
||||
// NOT `opts.provider`: the `configure` picker uses that flag for the
|
||||
// OmniRoute model provider to filter on, and it reaches setup recipes
|
||||
// verbatim. The 5dive BYO id is its own flag.
|
||||
const provider = String(opts.byoProvider ?? opts["byo-provider"] ?? "openai");
|
||||
const agents = agentList(opts);
|
||||
|
||||
printHeading("OmniRoute -> 5dive (claude BYO endpoint)");
|
||||
printInfo(`Server: ${baseUrl}`);
|
||||
printInfo(`Profile: ${profile}`);
|
||||
|
||||
const urlCheck = validateFivediveBaseUrl(baseUrl);
|
||||
if (!urlCheck.ok) {
|
||||
printError(urlCheck.reason);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// 5dive needs one explicit model id: `openai` has no catalog row, so there
|
||||
// are no per-tier defaults to fall back to.
|
||||
let model = opts.model;
|
||||
if (!model) {
|
||||
const ids = await fetchModelIds(baseUrl, apiKey);
|
||||
if (ids.length && !opts.yes) {
|
||||
printInfo(`Examples: ${ids.slice(0, 20).join(", ")}${ids.length > 20 ? " …" : ""}`);
|
||||
printInfo("A combo id works here too — that is how you get failover across providers.");
|
||||
const prompt = createPrompt();
|
||||
try {
|
||||
model = await prompt.ask("Model or combo id for the 5dive agents");
|
||||
} finally {
|
||||
prompt.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!model) {
|
||||
printError("A model is required. Pass --model <id> (5dive has no model auto-discovery here).");
|
||||
return 2;
|
||||
}
|
||||
if (!apiKey) {
|
||||
printError("An OmniRoute API key is required. Pass --api-key, or set OMNIROUTE_API_KEY.");
|
||||
return 2;
|
||||
}
|
||||
|
||||
const isRoot = typeof process.getuid === "function" ? process.getuid() === 0 : false;
|
||||
const useSudo = (opts.sudo ?? true) !== false;
|
||||
const authArgs = buildFivediveAuthArgs({ baseUrl, profile, model, provider });
|
||||
const [authBin, authArgv] = withPrivilege(bin, authArgs, { isRoot, useSudo });
|
||||
|
||||
if (dryRun) {
|
||||
printInfo("\n[dry-run] would run:");
|
||||
printInfo(` ${renderCommand(authBin, authArgv)}`);
|
||||
printInfo(" (the API key is written to that command's stdin, never to argv)");
|
||||
for (const agent of agents) {
|
||||
const [pinBin, pinArgv] = withPrivilege(bin, buildFivedivePinArgs(agent, model), {
|
||||
isRoot,
|
||||
useSudo,
|
||||
});
|
||||
printInfo(` ${renderCommand(pinBin, pinArgv)}`);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!isRoot && !useSudo) {
|
||||
printError(
|
||||
"Writing a 5dive auth profile needs root on the 5dive host. Re-run as root, drop --no-sudo, " +
|
||||
"or run this by hand:"
|
||||
);
|
||||
printInfo(` ${renderCommand(bin, authArgs)}`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const authResult = await run(authBin, authArgv, apiKey);
|
||||
if (authResult.error?.code === "ENOENT") {
|
||||
printError(
|
||||
`Could not find the '${bin}' CLI on this machine. 5dive's verbs run ON the fleet host — ` +
|
||||
"run this there, or point at the binary with --fivedive-bin."
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
if (authResult.code !== 0) {
|
||||
printError(`'${bin} agent auth set' exited ${authResult.code}.`);
|
||||
return authResult.code;
|
||||
}
|
||||
printSuccess(`Auth profile '${profile}' now points at ${baseUrl}`);
|
||||
|
||||
// The profile carries ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU}_MODEL, but each
|
||||
// seat's own runtime pin outranks it — a seat still pinned to a stock model id
|
||||
// fails its first turn with "There's an issue with the selected model".
|
||||
for (const agent of agents) {
|
||||
const [pinBin, pinArgv] = withPrivilege(bin, buildFivedivePinArgs(agent, model), {
|
||||
isRoot,
|
||||
useSudo,
|
||||
});
|
||||
const pinResult = await run(pinBin, pinArgv);
|
||||
if (pinResult.code !== 0) {
|
||||
printError(`Could not pin agent '${agent}' to '${model}' (exit ${pinResult.code}).`);
|
||||
return pinResult.code;
|
||||
}
|
||||
printSuccess(`Agent '${agent}' pinned to ${model}`);
|
||||
}
|
||||
|
||||
if (!agents.length) {
|
||||
printInfo("\nEach seat also carries its own runtime model pin, and it beats the profile:");
|
||||
printInfo(` ${renderCommand(bin, buildFivedivePinArgs("<agent>", model))}`);
|
||||
printInfo("Re-run with --agent <name> to have this command apply it for you.");
|
||||
}
|
||||
printInfo("\nBind a seat to the profile at creation time with:");
|
||||
printInfo(` ${renderCommand(bin, ["agent", "create", "<name>", `--auth-profile=${profile}`])}`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function registerSetup5dive(program) {
|
||||
program
|
||||
.command("setup-5dive")
|
||||
.description(
|
||||
"Point a 5dive agent fleet's claude seats at OmniRoute (writes a 5dive auth profile)"
|
||||
)
|
||||
.option("--port <port>", "Local OmniRoute port (ignored when --remote is set)", "20128")
|
||||
.option("--remote <url>", "Remote OmniRoute URL, e.g. https://omniroute.example.com")
|
||||
.option("--context <name>", "Named local/remote context")
|
||||
.option("--api-key <key>", "OmniRoute API key (defaults to the active context/env)")
|
||||
.option("--model <id>", "OmniRoute model or combo id the agents should use")
|
||||
.option("--byo-provider <id>", "5dive BYO provider id (default: openai)", "openai")
|
||||
.option("--auth-profile <name>", "5dive auth profile to write", DEFAULT_PROFILE)
|
||||
.option(
|
||||
"--agent <name>",
|
||||
"Also pin this agent's runtime model (repeatable)",
|
||||
(value, previous) => [...(previous || []), value],
|
||||
[]
|
||||
)
|
||||
.option("--fivedive-bin <path>", "Path to the 5dive binary (default: 5dive on PATH)")
|
||||
.option("--no-sudo", "Do not re-exec through sudo when not running as root")
|
||||
.option("--yes", "Non-interactive: do not prompt (requires --model)")
|
||||
.option("--dry-run", "Print the commands without running them")
|
||||
.action(async (opts) => {
|
||||
const code = await runSetup5diveCommand(opts);
|
||||
if (code !== 0) process.exit(code);
|
||||
});
|
||||
}
|
||||
@@ -100,31 +100,66 @@ export async function waitForServer(port, timeout = 60000) {
|
||||
// - "hanging": the request timed out waiting for any response — the
|
||||
// process accepted the TCP connection but never answered (#6800).
|
||||
// - "not-listening": nothing is accepting connections on the port at all.
|
||||
// #11766: probe both IPv4 and IPv6 loopback to handle servers listening on
|
||||
// either family (or both).
|
||||
async function pollHealthOnce(port) {
|
||||
try {
|
||||
const res = await fetch(`http://127.0.0.1:${port}/api/monitoring/health`, {
|
||||
signal: AbortSignal.timeout(2000),
|
||||
});
|
||||
return res.ok ? "ready" : "fast-reject";
|
||||
} catch (err) {
|
||||
if (err?.name === "TimeoutError") return "hanging";
|
||||
const listening = await isPortListening(port).catch(() => false);
|
||||
return listening ? "fast-reject" : "not-listening";
|
||||
}
|
||||
const hosts = ["127.0.0.1", "::1"];
|
||||
const outcomes = [];
|
||||
|
||||
// Probe both loopback families concurrently
|
||||
const results = await Promise.all(
|
||||
hosts.map(async (host) => {
|
||||
try {
|
||||
const res = await fetch(`http://${host}:${port}/api/monitoring/health`, {
|
||||
signal: AbortSignal.timeout(2000),
|
||||
});
|
||||
return { host, outcome: res.ok ? "ready" : "fast-reject" };
|
||||
} catch (err) {
|
||||
const outcome = err?.name === "TimeoutError" ? "hanging" : "error";
|
||||
return { host, outcome };
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
outcomes.push(...results.map((r) => r.outcome));
|
||||
|
||||
// If either family is ready, the server is ready
|
||||
if (outcomes.includes("ready")) return "ready";
|
||||
|
||||
// If either family is fast-reject, treat as fast-reject
|
||||
// (TCP is listening and rejecting, just route not ready yet)
|
||||
if (outcomes.includes("fast-reject")) return "fast-reject";
|
||||
|
||||
// If either family is hanging, server accepted TCP but not answering
|
||||
// (still booting, must not report as ready per #6800)
|
||||
if (outcomes.includes("hanging")) return "hanging";
|
||||
|
||||
// Both families failed — check if either port is actually listening
|
||||
// If listening, then errors above are route-level (fast-reject case)
|
||||
const listening = await isPortListening(port).catch(() => false);
|
||||
return listening ? "fast-reject" : "not-listening";
|
||||
}
|
||||
|
||||
async function isPortListening(port) {
|
||||
const net = await import("node:net");
|
||||
return new Promise((resolve) => {
|
||||
const socket = net.connect({ host: "127.0.0.1", port, timeout: 1000 });
|
||||
const finish = (ok) => {
|
||||
try {
|
||||
socket.destroy();
|
||||
} catch {}
|
||||
resolve(ok);
|
||||
};
|
||||
socket.once("connect", () => finish(true));
|
||||
socket.once("error", () => finish(false));
|
||||
socket.once("timeout", () => finish(false));
|
||||
});
|
||||
// #11766: check both IPv4 and IPv6 loopback. Return true if either is listening.
|
||||
const hosts = ["127.0.0.1", "::1"];
|
||||
const results = await Promise.all(
|
||||
hosts.map(
|
||||
(host) =>
|
||||
new Promise((resolve) => {
|
||||
const socket = net.connect({ host, port, timeout: 1000 });
|
||||
const finish = (ok) => {
|
||||
try {
|
||||
socket.destroy();
|
||||
} catch {}
|
||||
resolve(ok);
|
||||
};
|
||||
socket.once("connect", () => finish(true));
|
||||
socket.once("error", () => finish(false));
|
||||
socket.once("timeout", () => finish(false));
|
||||
})
|
||||
)
|
||||
);
|
||||
return results.some((ok) => ok);
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
- feat(api): add an opt-in `modelVisibilityAllowlist`/`modelVisibilityDenylist` settings pair to curate exactly which models `/v1/models` advertises, mirrored into every `auto/*` combo candidate pool so a denied model cannot be routed to via combo selection either (#11481)
|
||||
@@ -0,0 +1 @@
|
||||
- **feat(guardrails):** enforce a bounded, deterministic contract for Video Bridge transcripts — 256 cues, 4096 input code units and 4 KiB UTF-8 per cue, 64 KiB total text, malformed-Unicode rejection, focus-window scoping, cross-source reconciliation with contributing-source metadata, and a structural provenance trust boundary so caller JSON can never self-assert `embedded`/`audio-bridge` provenance ([#11652](https://github.com/diegosouzapw/OmniRoute/issues/11652))
|
||||
@@ -0,0 +1 @@
|
||||
- **feat(video):** orchestrate optional Video Bridge audio extraction and Audio Bridge STT behind a dual opt-in (operator setting AND per-request signal) — a new loopback-only broker `mode=audio` operation shares the frame path's exact process queue, deadline, AbortSignal, and byte budgets to extract a bounded mono 16 kHz PCM WAV from the same already-downloaded video, then reuses the existing Audio Bridge transcription boundary; provider segment timing is preserved when available and marked coarse otherwise, and every failure degrades to a visual-only-safe partial instead of throwing (#11654).
|
||||
@@ -0,0 +1,6 @@
|
||||
- Add a tenant-bound Video Bridge drill-down lifecycle on top of the existing secure cache
|
||||
substrate: opaque hashed handles (never raw session/video identifiers), preview/standard/detail
|
||||
multiresolution variants resampled on read, response pagination capped at 8 frames and 32 MiB,
|
||||
and a new authenticated `/api/v1/video-bridge/drilldown` consumer route that stays disabled for
|
||||
remote access by default and denies cross-key access with the same response as a nonexistent
|
||||
handle (no existence oracle).
|
||||
@@ -0,0 +1 @@
|
||||
- **test(video):** Add the Video Bridge FU-07/FU-09 promotion-evidence harness (#11656) — a frozen Zod manifest schema covering the 8 required scenario kinds (static scenes, rapid cuts, late facts, fades, blur, small text, close events, visual prompt injection) with a minimum of 3 repetitions per case, deterministic declarative fixture recipes (`videoBridgePromotionFixtures.ts`), a pure medians/p95 metrics aggregator, a pure FU-07/FU-09 promotion-verdict evaluator applying the ticket's exact thresholds (missing token usage always holds), a digest-only persistence layer that never retains raw media or raw model responses, and a versioned per-model promotion allowlist shipped empty with every model defaulting to `hold`. The FU-07/FU-09 promotion verdicts themselves remain HOLD — they require a real evidence run against real models on VPS 192.168.0.15.
|
||||
@@ -0,0 +1 @@
|
||||
- **feat(video bridge):** "embedded" transcript provenance can now be legitimately earned instead of merely asserted — a bounded, allowlisted (`mov_text`/`subrip`/`webvtt`) subtitle probe runs through the loopback-only Video Bridge broker (at most 2 streams, 10s subdeadline bounded by the request deadline, 256 KiB output, 4096-code-unit lines), normalized through a bounded, ReDoS-safe WebVTT parser and Zod-validated end to end. The adapter always resolves to an explicit `success`/`absent`/`transient_failure` outcome — a subtitle failure never breaks the visual description path, and only a fingerprint-verified broker response (never a caller-declared label) can produce embedded cues (#11659).
|
||||
1
changelog.d/features/11801-zai-glm-53-flash.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(zai):** add GLM-5.3-Flash Coding Plan support (1M context, 128K output, vision, `low|high|max` reasoning) and route `zai` GLM-5.3-family API-key traffic through the OpenAI-compatible Coding Plan endpoint with native thinking defaults ([#11801](https://github.com/diegosouzapw/OmniRoute/pull/11801)) — thanks @Neuron-Mr-White
|
||||
1
changelog.d/features/11852-5dive-configure-target.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(nodejs):** add `5dive` as a `configure` target — `omniroute configure 5dive` / `omniroute setup-5dive` write a 5dive auth profile that points an agent fleet's `claude` seats at OmniRoute, with the root-only write, the loopback-vs-`https` endpoint rule and the per-seat model pin handled explicitly ([#11852](https://github.com/diegosouzapw/OmniRoute/pull/11852))
|
||||
1
changelog.d/features/11903-usage-fetch-capability.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(providers):** the provider plugin manifest now advertises a `usage-fetch` capability for the 40 providers that have a wired usage/quota fetcher, so external dashboards can read it from `GET /api/v1/provider-plugin-manifest` instead of parsing `open-sse/services/usage.ts` after every release. Discovery only — no new fetcher, no quota change, and the Dashboard quota widget stays gated by `USAGE_SUPPORTED_PROVIDERS`. `USAGE_FETCHER_PROVIDERS` moved to a zero-dependency leaf (`open-sse/services/usage/fetcherProviders.ts`) and is re-exported from `services/usage.ts`, keeping the manifest module a light leaf instead of pulling the ~490-module usage dispatcher into the manifest route. ([#11903](https://github.com/diegosouzapw/OmniRoute/pull/11903)) — thanks @maxmad64bis
|
||||
1
changelog.d/features/11906-omniroute-plugins-dir.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(plugins):** `OMNIROUTE_PLUGINS_DIR` sets the directory the runtime plugin scanner reads — and the root the plugin manager installs into — overriding the `HOME`-derived default, so a Docker/K8s deployment can point straight at its bind-mounted plugin tree instead of moving `HOME` just to relocate the scan path. An image that exports no home no longer scans `/tmp/.omniroute/plugins` in silence: the resolved directory is logged once at startup as `scanner.dir_resolved`, naming the input that won. Unset, behaviour is unchanged. Distinct from the CLI-only `OMNIROUTE_PLUGIN_PATH`, which finds `omniroute-cmd-*` command packages and never reached this scanner ([#11906](https://github.com/diegosouzapw/OmniRoute/pull/11906)) — thanks @amaleta
|
||||
@@ -0,0 +1 @@
|
||||
- **feat(leases):** add an explicit owner-authenticated status action that returns only the active lease's privacy-safe configured connection and provider labels, with generation fencing and no credential or internal-id disclosure ([#11910](https://github.com/diegosouzapw/OmniRoute/pull/11910)) — thanks @KaspaPulse
|
||||
1
changelog.d/features/dashboard-cache-percentage.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(dashboard):** display clamped `[0, 100]%` cached input token ratio in request logs table ([#PR_NUMBER](https://github.com/diegosouzapw/OmniRoute/pull/PR_NUMBER))
|
||||
1
changelog.d/features/disable-thinking-level-variants.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(catalog):** add `OMNIROUTE_DISABLE_THINKING_LEVEL_VARIANTS` feature flag to optionally filter out thinking level variants from model catalog ([#PR_NUMBER](https://github.com/diegosouzapw/OmniRoute/pull/PR_NUMBER))
|
||||
11
changelog.d/features/log-export-destinations.md
Normal file
@@ -0,0 +1,11 @@
|
||||
- **feat(dashboard):** continuously export call logs to external analytics stores. A pluggable
|
||||
destination registry ships the full Logs-tab record set on an hourly `JobRegistry` cron, with
|
||||
a persisted per-destination cursor, batched inserts, a config UI rendered from each
|
||||
destination's own field descriptors, and a REST layer (`/api/log-export/*`) for CRUD, a
|
||||
connection test, and an on-demand run. A destination can opt into `includeBodies` to also ship
|
||||
the request and response payloads shown in the Logs detail pane, including the client and
|
||||
provider views of each call; this is off by default, and payloads inherit the dashboard's PII
|
||||
sanitisation, secret redaction and `noLog` handling. Google BigQuery is the first destination,
|
||||
using a service-account key stored encrypted at rest and streaming inserts keyed by call-log id,
|
||||
into a table that is day-partitioned on `timestamp` and clustered on `api_key_name`, `provider`,
|
||||
`model` and `status`.
|
||||
1
changelog.d/features/perplexity-agent-provider.md
Normal file
@@ -0,0 +1 @@
|
||||
- **feat(providers):** add a Perplexity Agent API provider (`perplexity-agent` / `pplx-agent`) for Perplexity `/v1/responses`, including the documented Anthropic, OpenAI, Google, xAI, DeepSeek, Z.AI, Moonshot/Kimi, NVIDIA, and Perplexity model IDs plus Anthropic-model `max_output_tokens` compatibility.
|
||||
1
changelog.d/fixes/0000-provider-icon-zero-size.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(dashboard):** Keep local and theme-aware provider SVG icons at a definite layout size so Chromium does not collapse them to 0×0 after the v3.8.50 image-rendering change ([#12054](https://github.com/diegosouzapw/OmniRoute/pull/12054)) — thanks @ponkcore
|
||||
1
changelog.d/fixes/11704-prepublish-npx-cmd-win32.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(build):** `prepublish.ts` bundles the ChatGPT Web (Codex) MCP bridge through `runBuildTool()` instead of spawning `npx.cmd` raw, fixing the build crash on Node ≥ 20/Windows where `.cmd` shims cannot be spawned without a shell (EINVAL) ([#11704](https://github.com/diegosouzapw/OmniRoute/issues/11704))
|
||||
1
changelog.d/fixes/11747-browser-pool-lockfile-sync.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(packages/browser-pool):** regenerate `package-lock.json` so the `packages/browser-pool` workspace's locked `playwright`/`@types/node` (and transitives) match its `package.json` specs, fixing cache-only/offline installs (`npm ci --offline`, Nix `buildNpmPackage`) that previously failed with `ENOTCACHED` ([#11747](https://github.com/diegosouzapw/OmniRoute/issues/11747)) — thanks @benjaminkitt
|
||||
1
changelog.d/fixes/11758-usage-refresh-exclusive-lease.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(usage):** quota and usage refresh no longer 409 when an exclusive lease reserves the connection ([#11758](https://github.com/diegosouzapw/OmniRoute/pull/11758)) — thanks @TheDemonTuan
|
||||
1
changelog.d/fixes/11861-nous-tags-user-injection.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825
|
||||
10
changelog.d/fixes/11863-native-dep-preflight.md
Normal file
@@ -0,0 +1,10 @@
|
||||
- **fix(build):** `npm run build` now fails in one second with a named package and a
|
||||
copy-pasteable fix when npm silently drops an externalised optional native
|
||||
dependency, instead of dying four minutes in with `Module not found: Can't resolve
|
||||
'better-sqlite3'` ([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) —
|
||||
thanks @ujjawalkaushik1110
|
||||
- **fix(install):** `postinstall` no longer throws `ReferenceError: isAndroid is not
|
||||
defined` — failing the whole `npm install` — when the `better-sqlite3` rebuild
|
||||
fallback times out; the manual-fix guidance is reachable again
|
||||
([#11863](https://github.com/diegosouzapw/OmniRoute/pull/11863)) — thanks
|
||||
@ujjawalkaushik1110
|
||||
1
changelog.d/fixes/11911-lkgp-stale-pin-exhaustion.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(resilience):** clear persisted LKGP pins when a target suffers connection/provider exhaustion or is skipped before dispatch due to cooldown/exhaustion/unavailability, preventing subsequent requests from repeatedly prioritizing known-dead providers ([#11911](https://github.com/diegosouzapw/OmniRoute/issues/11911)).
|
||||
@@ -0,0 +1 @@
|
||||
- fix(ollama): preserve multi-byte UTF-8 content split across stream chunks in the Ollama NDJSON transform, which previously corrupted CJK/emoji into U+FFFD (#11921)
|
||||
1
changelog.d/fixes/11923-orcarouter-baseurl.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(providers):** OrcaRouter chat requests now target `/v1/chat/completions` instead of the bare `/v1` API root, fixing the upstream `404 Invalid URL (POST /v1)` ([#11923](https://github.com/diegosouzapw/OmniRoute/pull/11923)).
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(plugins):** deliver the `onStreamComplete` event to disk-installed plugins. The event shipped in v3.8.50 (#9669) was emitted internally but had no plugin-facing wiring, so no plugin could ever subscribe: the manifest schema silently dropped `hooks.onStreamComplete`, and the loader/manager only knew the seven legacy hooks. `onStreamComplete` is now a declarable manifest hook, wired through the loader and registered by the manager like the other hooks, and its payload carries a `requestId` so consumers can correlate the stream-completion event with the originating request ([#11934](https://github.com/diegosouzapw/OmniRoute/pull/11934)) — thanks @amaleta
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(providers):** Vertex AI Anthropic partner-model discovery now calls the Model Garden `v1beta1` publisher list (`/v1beta1/publishers/anthropic/models`, global) and parses its `publisherModels` envelope, so Claude models auto-synced from Vertex populate the active live catalog and route at request time instead of returning `Model '<id>' is not available in the active live catalog` ([#11991](https://github.com/diegosouzapw/OmniRoute/issues/11991)) — thanks @fabioluissilva
|
||||
1
changelog.d/fixes/11999-cli-nodes-base-url-shadowing.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(cli):** support `--base-url` alongside `--endpoint` in `omniroute nodes add`, `update`, and `validate` subcommands to prevent global `--base-url` shadowing issues ([#11999](https://github.com/diegosouzapw/OmniRoute/issues/11999)).
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(providers):** `cloudflare-ai` no longer refuses image content parts for every Workers AI model ([#12002](https://github.com/diegosouzapw/OmniRoute/pull/12002)) — the plain-string `content` requirement behind #2539 is carried by the _model_ schema, not by the `/ai/v1/chat/completions` endpoint (measured: an all-text part array returns 200 on `@cf/mistralai/mistral-small-3.1-24b-instruct`, `@cf/meta/llama-4-scout-17b-16e-instruct` and `@cf/meta/llama-3.3-70b-instruct-fp8-fast`, and 400 on the text-only `@cf/qwen/qwen2.5-coder-32b-instruct`). `transformRequest()` flattened every array and threw on the first non-text part (#6390), so image input was refused for vision-capable Cloudflare models that accept it. All-text arrays are still flattened — the one shape every model accepts — while an array carrying a non-text part is passed through untouched, so the attachment is still never silently dropped. Regression guards: `tests/unit/cloudflare-ai-image-parts-6390.test.ts`.
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(resilience):** decouple the limiter-managed execution backstop from the queue-wait budget — new `requestQueue.executionMaxWaitMs` (env `RATE_LIMIT_EXECUTION_MAX_WAIT_MS`, default 600000 = 10 min) now feeds Bottleneck's post-dispatch `expiration`, while `requestQueue.maxWaitMs` keeps its documented queue-wait semantics. Previously the queue-wait budget doubled as the execution expiration, so legitimate long-running calls on non-incremental gateways (whole generation buffered before the first upstream byte, e.g. Console Go / Command Code tiers serving GLM models) were killed mid-flight at the queue budget with a false 504 `RATE_LIMIT_EXECUTION_TIMEOUT` — the local limiter undercut the provider-aware upstream fetch-start timeouts. The surfaced 504 message now names `requestQueue.executionMaxWaitMs`; the error keeps the #4165 guarantees (disclaims an upstream timeout, preserves the Bottleneck error as `cause`, branded code + trusted provenance, classified request-scoped so combo falls back). A real queue-wait bound (the `Promise.race` around `limiter.schedule()` sketched in #9533) remains future work. (#12025)
|
||||
@@ -0,0 +1 @@
|
||||
- **Call logs:** keep the `error` field when an artifact exceeds the storage cap, instead of replacing it with the omission marker. The error is the only field that says *why* a request failed and is typically ~90 bytes next to the multi-hundred-KB bodies that trip the cap, so dropping it left a size-limited row undiagnosable — a provider outage, a local timeout and an upstream 400 all rendered identically. It is now preserved at every fallback stage, truncated to 4KB if it is itself large ([#12026](https://github.com/diegosouzapw/OmniRoute/issues/12026)).
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(diagnostics):** preserve the error field (truncated to 4KB with a `[truncated: …]` suffix) in every call-log artifact size-limit fallback stage. Previously the minimal fallback replaced the error with `[omitted: call log artifact size limit exceeded]`, so an oversized artifact row showed nothing about WHY the request failed — e.g. 91 of 847 opencode-go 504 rows on one production instance were undiagnosable from the dashboard. Oversized request/response bodies are still omitted exactly as before; the error cap is independent of the payload sizes that tripped the fallback. (#12026)
|
||||
1
changelog.d/fixes/12031-web-search-call-emission.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(sse):** OpenAI Responses clients that declare the native `web_search` tool now receive a spec-shaped `web_search_call` output item with `action.sources` alongside the preserved function-call round-trip, so search results executed through OmniRoute's own search backend are consumable by standard Responses clients (Codex, pi-web-access, …).
|
||||
1
changelog.d/fixes/12073-node26-alias-hooks.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(cli):** use in-thread alias resolver hooks on modern runtimes to avoid deprecation noise and improve Node.js forward compatibility ([#12073](https://github.com/diegosouzapw/OmniRoute/issues/12073)).
|
||||
1
changelog.d/fixes/12090-combo-explicit-context-length.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(combo):** an operator-set **Agent Features → Context length** on a combo is now honored at request time. The value was persisted and advertised through `/v1/models`, but `resolveComboContextLimit()` never consulted it — so a multi-target combo whose members carry no per-model window fell through to the provider's generic `defaultContextLength` (openrouter 128000, command-code 200000) and rejected large requests with `Input exceeds context window … limit 128000` despite the combo being explicitly sized much larger. An identical single-target combo worked, because it collapses to its concrete target before the guard runs. Invalid values (0/negative/NaN/Infinity) are ignored, so the existing target → combo-min → fallback order is unchanged. ([#12090](https://github.com/diegosouzapw/OmniRoute/pull/12090)) — thanks @adivekar-utexas
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(executors):** handle DuckDuckGo ERR_BN_LIMIT (418) without retrying — when the upstream returns `418 ERR_BN_LIMIT` (rate-limit/ban), the executor now returns the error immediately instead of burning another VQD acquisition that would only count against the IP limit. The retry logic for `418 ERR_CHALLENGE` (unsolved challenge) remains unchanged. ([#11598](https://github.com/diegosouzapw/OmniRoute/pull/11598))
|
||||
@@ -0,0 +1 @@
|
||||
- **fix(api):** Generated API CLI commands now enforce required OpenAPI request bodies; Combo test commands forward the required `comboName` body, while API keys created by older writers after migration 149 preserve legacy allow-all Combo access without widening explicit empty allowlists — thanks @marcelokarval
|
||||
@@ -0,0 +1 @@
|
||||
- **refactor(video bridge):** extract per-video acquisition, whole-result caching, description, and metrics/abort/cleanup out of `VideoBridgeGuardrail.preCall` into a `processVideoPart` seam in a new `videoBridgePipeline.ts`, behind explicit `VideoMediaBrokerPort`, `VideoAudioTranscriptionPort`, and `VideoDrilldownPort` boundaries; `preCall` now only handles request traversal, policy, and response aggregation. The Video tab's FFmpeg/ffprobe runtime status is now an explicit `unknown` / `restricted` / `unavailable` / `available` state instead of a nullable boolean pair, fixing a case where an in-flight or failed probe was mislabeled as "install FFmpeg" ([#11657](https://github.com/diegosouzapw/OmniRoute/issues/11657)).
|
||||
@@ -0,0 +1,5 @@
|
||||
- **docs(video):** clarify that the Video Bridge transcript `source` field (`client`,
|
||||
`embedded`, `audio-bridge`) is presently caller-declared and not yet server-verified —
|
||||
OmniRoute enforces the enum shape but does not cryptographically confirm that an
|
||||
`embedded`/`audio-bridge` label came from a server-owned extraction
|
||||
([#11661](https://github.com/diegosouzapw/OmniRoute/issues/11661)).
|
||||
401
config/quality/api-typecheck-baseline.json
Normal file
@@ -0,0 +1,401 @@
|
||||
{
|
||||
"open-sse/transformer/responsesTransformer.ts": {
|
||||
"TS2353": 2
|
||||
},
|
||||
"open-sse/utils/progressTracker.ts": {
|
||||
"TS2353": 2
|
||||
},
|
||||
"open-sse/utils/sseHeartbeat.ts": {
|
||||
"TS2353": 2
|
||||
},
|
||||
"open-sse/utils/stream.ts": {
|
||||
"TS2353": 2
|
||||
},
|
||||
"src/app/api/assess/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/cache/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/cli-tools/all-statuses/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/cli-tools/claude-settings/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/cli-tools/cline-settings/route.ts": {
|
||||
"TS2339": 6
|
||||
},
|
||||
"src/app/api/cli-tools/codex-settings/route.ts": {
|
||||
"TS2345": 3
|
||||
},
|
||||
"src/app/api/cli-tools/grok-build-settings/route.ts": {
|
||||
"TS2304": 2
|
||||
},
|
||||
"src/app/api/cli-tools/hermes-agent-settings/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/cli-tools/letta-settings/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/cli-tools/omp-settings/route.ts": {
|
||||
"TS2339": 10
|
||||
},
|
||||
"src/app/api/cli-tools/qwen-settings/route.ts": {
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/combos/auto/route.ts": {
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/combos/test/route.ts": {
|
||||
"TS2345": 2,
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/compression/compare/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/compression/preview/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/context/combos/[id]/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/context/combos/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/copilot/chat/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/guardrails/test/route.ts": {
|
||||
"TS2554": 2
|
||||
},
|
||||
"src/app/api/internal/codex-responses-ws/route.ts": {
|
||||
"TS2740": 2,
|
||||
"TS2339": 9
|
||||
},
|
||||
"src/app/api/keys/[id]/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/local/redis/start/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/local/redis/stop/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/logs/[id]/route.ts": {
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/model-capability-overrides/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/model-combo-mappings/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/models/alias/route.ts": {
|
||||
"TS2339": 6
|
||||
},
|
||||
"src/app/api/models/route.ts": {
|
||||
"TS2345": 4,
|
||||
"TS2538": 2
|
||||
},
|
||||
"src/app/api/monitoring/health/route.ts": {
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/oauth/codex/import-token/route.ts": {
|
||||
"TS2339": 4
|
||||
},
|
||||
"src/app/api/oauth/codex/import/route.ts": {
|
||||
"TS2554": 2,
|
||||
"TS2353": 2,
|
||||
"TS2339": 4
|
||||
},
|
||||
"src/app/api/oauth/cursor/login/poll/route.ts": {
|
||||
"TS2554": 2
|
||||
},
|
||||
"src/app/api/oauth/kiro/auto-import/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/omniroute/route/preview/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/playground/presets/[id]/route.ts": {
|
||||
"TS2339": 4
|
||||
},
|
||||
"src/app/api/provider-nodes/validate/route.ts": {
|
||||
"TS2339": 3
|
||||
},
|
||||
"src/app/api/providers/[id]/login/route.ts": {
|
||||
"TS2739": 2
|
||||
},
|
||||
"src/app/api/providers/[id]/models/route.ts": {
|
||||
"TS2367": 2,
|
||||
"TS2339": 3,
|
||||
"TS2322": 3,
|
||||
"TS2554": 3,
|
||||
"TS2345": 4
|
||||
},
|
||||
"src/app/api/providers/[id]/refresh-cursor/route.ts": {
|
||||
"TS2352": 2
|
||||
},
|
||||
"src/app/api/providers/[id]/refresh/route.ts": {
|
||||
"TS2345": 2,
|
||||
"TS2698": 2,
|
||||
"TS2339": 8
|
||||
},
|
||||
"src/app/api/providers/[id]/sync-models/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/providers/[id]/test/route.ts": {
|
||||
"TS2362": 2,
|
||||
"TS2698": 2
|
||||
},
|
||||
"src/app/api/providers/free-onboarding/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/providers/health-autopilot/actions/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/providers/route.ts": {
|
||||
"TS2352": 2,
|
||||
"TS2322": 3,
|
||||
"TS2345": 4
|
||||
},
|
||||
"src/app/api/providers/test-batch/route.ts": {
|
||||
"TS2345": 5
|
||||
},
|
||||
"src/app/api/providers/validate/route.ts": {
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/providers/volcengine-plan/connect/[sessionId]/cancel/route.ts": {
|
||||
"TS2739": 2
|
||||
},
|
||||
"src/app/api/providers/volcengine-plan/connect/[sessionId]/code/route.ts": {
|
||||
"TS2739": 2
|
||||
},
|
||||
"src/app/api/providers/volcengine-plan/connect/[sessionId]/identity/route.ts": {
|
||||
"TS2739": 2
|
||||
},
|
||||
"src/app/api/providers/volcengine-plan/connect/[sessionId]/resend/route.ts": {
|
||||
"TS2739": 2
|
||||
},
|
||||
"src/app/api/providers/volcengine-plan/connect/[sessionId]/status/route.ts": {
|
||||
"TS2739": 2
|
||||
},
|
||||
"src/app/api/providers/volcengine-plan/connect/route.ts": {
|
||||
"TS2739": 2
|
||||
},
|
||||
"src/app/api/radar/local-model-state/route.ts": {
|
||||
"TS2339": 5
|
||||
},
|
||||
"src/app/api/resilience/model-cooldowns/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/services/_shared/installRoute.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/settings/cache-config/route.ts": {
|
||||
"TS2339": 2,
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/settings/database/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/settings/models-dev/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/settings/obsidian/webdav/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/settings/proxies/bulk-import/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/settings/proxy/cloudflare-deploy/route.ts": {
|
||||
"TS2769": 2,
|
||||
"TS2322": 3
|
||||
},
|
||||
"src/app/api/settings/proxy/deno-deploy/route.ts": {
|
||||
"TS2322": 5
|
||||
},
|
||||
"src/app/api/settings/proxy/vercel-deploy/route.ts": {
|
||||
"TS2322": 4
|
||||
},
|
||||
"src/app/api/settings/reasoning-routing-rules/[id]/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/settings/reasoning-routing-rules/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/settings/reasoning-routing-rules/simulate/route.ts": {
|
||||
"TS2322": 2,
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/system/env/repair/route.ts": {
|
||||
"TS2578": 2,
|
||||
"TS2353": 4
|
||||
},
|
||||
"src/app/api/system/version/route.ts": {
|
||||
"TS2769": 2
|
||||
},
|
||||
"src/app/api/tools/agent-bridge/agents/[id]/detected-models/route.ts": {
|
||||
"TS2769": 2
|
||||
},
|
||||
"src/app/api/tools/traffic-inspector/internal/ingest/route.ts": {
|
||||
"TS1117": 3,
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/tools/traffic-inspector/ws/route.ts": {
|
||||
"TS2578": 2
|
||||
},
|
||||
"src/app/api/translator/send/route.ts": {
|
||||
"TS2345": 2,
|
||||
"TS2322": 2,
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/translator/translate/route.ts": {
|
||||
"TS2345": 2,
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/usage/analytics/route.ts": {
|
||||
"TS2352": 18
|
||||
},
|
||||
"src/app/api/usage/combo-health-autopilot/route.ts": {
|
||||
"TS2769": 3
|
||||
},
|
||||
"src/app/api/v1/batches/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/v1/classify/route.ts": {
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/v1/files/[id]/content/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/v1/files/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/v1/images/edits/route.ts": {
|
||||
"TS2339": 22,
|
||||
"TS2322": 5
|
||||
},
|
||||
"src/app/api/v1/messages/count_tokens/route.ts": {
|
||||
"TS2339": 3,
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/v1/music/generations/route.ts": {
|
||||
"TS2322": 2,
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/v1/ocr/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/v1/provider-plugin-manifest/route.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/v1/providers/[provider]/embeddings/route.ts": {
|
||||
"TS2339": 4,
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/v1/providers/[provider]/images/generations/route.ts": {
|
||||
"TS2339": 6
|
||||
},
|
||||
"src/app/api/v1/rerank/route.ts": {
|
||||
"TS2339": 3
|
||||
},
|
||||
"src/app/api/v1/segment/route.ts": {
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/api/v1/session-leases/route.ts": {
|
||||
"TS2339": 5,
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/api/v1/speech-to-text/route.ts": {
|
||||
"TS2353": 2
|
||||
},
|
||||
"src/app/api/v1/text-to-speech/[voiceId]/route.ts": {
|
||||
"TS2353": 2
|
||||
},
|
||||
"src/app/api/v1/web/fetch/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/v1beta/models/route.ts": {
|
||||
"TS2345": 2,
|
||||
"TS2538": 2
|
||||
},
|
||||
"src/app/api/version-manager/restart/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/version-manager/start/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/version-manager/stop/route.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/api/webhooks/[id]/route.ts": {
|
||||
"TS2554": 2
|
||||
},
|
||||
"src/app/api/webhooks/[id]/test/route.ts": {
|
||||
"TS2352": 3
|
||||
},
|
||||
"src/app/api/webhooks/route.ts": {
|
||||
"TS2554": 2,
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/lib/db/tierConfig.ts": {
|
||||
"TS2345": 3
|
||||
},
|
||||
"src/lib/monitoring/comboHealthAutopilot.ts": {
|
||||
"TS2305": 2,
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/lib/monitoring/providerHealthAutopilot.ts": {
|
||||
"TS2352": 5
|
||||
},
|
||||
"src/lib/omnirouteStatus.ts": {
|
||||
"TS2322": 2,
|
||||
"TS2558": 2
|
||||
},
|
||||
"src/lib/providerModels/managedModelImport.ts": {
|
||||
"TS2352": 5
|
||||
},
|
||||
"src/lib/proxySubscription/parse.ts": {
|
||||
"TS2345": 4
|
||||
},
|
||||
"src/lib/quota/quotaAnalytics.ts": {
|
||||
"TS2769": 2
|
||||
},
|
||||
"src/lib/quota/quotaResetTimers.ts": {
|
||||
"TS2769": 3
|
||||
},
|
||||
"src/lib/usage/comboForecast.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/lib/usage/comboHealth.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/lib/usage/comboScoringInspector.ts": {
|
||||
"TS2352": 2,
|
||||
"TS2741": 2
|
||||
},
|
||||
"src/lib/usage/providerWindowCosts.ts": {
|
||||
"TS2322": 3,
|
||||
"TS2558": 6,
|
||||
"TS2339": 15,
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/lib/vscode/modelPresentation.ts": {
|
||||
"TS2554": 2
|
||||
},
|
||||
"src/lib/ws/handshake.ts": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/mitm/detection/index.ts": {
|
||||
"TS2741": 2
|
||||
},
|
||||
"src/mitm/inspector/httpProxyServer.ts": {
|
||||
"TS2769": 2
|
||||
},
|
||||
"src/shared/schemas/cliCatalog.ts": {
|
||||
"TS2554": 3
|
||||
},
|
||||
"_relax_velocity_2026_08_30": "per-file TS diagnostic counts raised by 20% (289 → 455); velocity phase, see quality-baseline.json _policy."
|
||||
}
|
||||
@@ -1,54 +1,54 @@
|
||||
{
|
||||
"_comment": "Catraca de complexidade (check-complexity.mjs, ESLint core rules complexity>=15 e max-lines-per-function>80 sobre src+open-sse+electron+bin via eslint.complexity.config.mjs). Conta total de violacoes; so pode cair. --update ratcheta.",
|
||||
"_rebaseline_2026_07_25_dario_upstream_proxy_selector": "2130->2175. PR #8523 (Dario embedded service, upstream-proxy mode selector): check:complexity does not run on PR->release fast-gates, so cycle drift accrues unratcheted until a PR trips the gate (same pattern as every _rebaseline_ entry above). Measured base upstream/release/v3.8.49 tip locally at 2169 (with this PR\u0027s own commits removed); this branch measures 2173 local, 2175 on the CI runner (same local-vs-CI off-by-few convention documented in _rebaseline_2026_07_02_v3844_ci_observed). This PR\u0027s own genuine contribution is small (+4 to +6): the new mode <select> branching in ConnectionRow.tsx (Native/CLIProxyAPI/Dario/Fallback + conditional fallback-backend picker) and the probe/adopt/kill-PID branches added to the service supervisor for Dario\u0027s on-demand lifecycle. Using the CI-observed value (2175) so the gate is deterministic where it actually runs, per the established convention. Structural shrink stays tracked in #3501. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_25_dario_upstream_proxy_selector": "2130->2175. PR #8523 (Dario embedded service, upstream-proxy mode selector): check:complexity does not run on PR->release fast-gates, so cycle drift accrues unratcheted until a PR trips the gate (same pattern as every _rebaseline_ entry above). Measured base upstream/release/v3.8.49 tip locally at 2169 (with this PR's own commits removed); this branch measures 2173 local, 2175 on the CI runner (same local-vs-CI off-by-few convention documented in _rebaseline_2026_07_02_v3844_ci_observed). This PR's own genuine contribution is small (+4 to +6): the new mode <select> branching in ConnectionRow.tsx (Native/CLIProxyAPI/Dario/Fallback + conditional fallback-backend picker) and the probe/adopt/kill-PID branches added to the service supervisor for Dario's on-demand lifecycle. Using the CI-observed value (2175) so the gate is deterministic where it actually runs, per the established convention. Structural shrink stays tracked in #3501. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_20_owner_night_drain": "Owner-approved (chat, 2026-07-20 ~00:50): 2072->2130. The day's 17 merged PRs consumed the entire slack (tip at 2069/2072); queue PRs #6973(+4)/#7662(+2)/#7719(+1) plus the #7744/#7779 reworks were collectively blocked. Owner chose a wide margin for the remainder of the v3.8.49 cycle instead of per-PR extraction.",
|
||||
"count": 2175,
|
||||
"count": 3218,
|
||||
"_rebaseline_2026_07_25_8470_hyperagent_sticky_thread": "2130->2170 (+40). PR #8470 (artickc, fix/hyperagent-tool-loop-thread-sticky) pre-green validation. Trust-but-verify: origin/release/v3.8.49 tip alone (pristine, no PR changes) already measures 2169 with node scripts/check/check-complexity.mjs — i.e. +39 is inherited cycle drift unrelated to this PR (the cyclomatic-complexity ratchet does not run on PR->release fast-gates). This PR's OWN growth adds exactly +1: per-file eslint scoped scan on open-sse/executors/hyperagent.ts (base vs PR) shows extractMessageText() crossing the complexity>=15 threshold for the first time (new violation, complexity 25) from the new Anthropic tool_use/tool_result flattening branches; resolveHyperAgentThreadBinding's existing pre-#8470 violation (18) grows to 25 (still counted once, from the new root-key lookup tier); createHyperAgentThread and execute() are unchanged pre-existing violations. Net repo-wide total = 2169 (inherited drift) + 1 (this PR's own new violation) = 2170. Full-repo re-measurement of the merged branch was attempted but not completed live due to heavy concurrent devbox load (many other /green-prs sessions running the identical full-repo eslint scan in parallel); derived from two independently-clean measurements (base-tip full scan + per-file base-vs-PR delta). Covered by tests/unit/executor-hyperagent.test.ts (19/19). Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_25b_v3849_mergetrain_owngrowth": "Owner-approved (chat, 2026-07-25): 2169->2183 (+14). v3.8.49 /merge-prs 41-PR merge-train aggregate own-growth: measured 2183 on the combined boarded tree (tip ac15014ca7) vs 2169 on the pristine release tip. Each boarded PR sits under the ceiling individually, but the combined batch adds +14 (new branches in #8378 chatCore contextLimit / #8432 cursor native_todo / #8476 combo input-bound / #8526 combo select-all modals / etc \u2014 the pre-screen-flagged complexity-growth set). Same merge-burst-inherited-drift class as the notes below; owner chose absorbing the ceiling over per-PR helper-extraction churn. Structural shrink stays debt (#3501); tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_25_v3849_mergequeue_drain": "Owner-approved (chat, 2026-07-25): 2130->2169 (+39). v3.8.49 /merge-prs queue-drain: the cycle's merge burst (the 8 base-red slices + owner PRs + parallel-session merges #8500-8508) accrued inherited cyclomatic drift the fast-path PR->release never ratchets (check:complexity does not run on PR->release). Measured 2169 on the pristine release tip 4053e2314a alone (BEFORE any queue PR boards) \u2014 so the entire +39 is base drift already on the tip, not any queued PR's own growth. Every merge-ready PR in the queue was tripping Fast Quality Gates on this shared base-red. Owner approved raising the ceiling to the measured tip value so the ~34-PR merge-train lands without per-PR helper-extraction churn. Structural shrink stays debt (#3501); tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_27_3850_quality_relax_TEMP_3851_retighten_v2_20pct": "_rebaseline_2026_07_27_3850_quality_relax_TEMP_3851_retighten_v2_20pct: 2188->2774 (+586, +26.8% over pristine 2188). OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). v1 was +124 (2x daily drift) on 2026-07-27; v2 = v1 +20% buffer = +218 \u2192 +586 total (cycle 2188 measured pristine \u2192 2774 ceiling). Justification: v3.8.50 release cut coincides with high-merge activity (33-PR Train 1D + ~37-PR Train 2 backlog + post-freeze re-home rebase churn); owner accepted enlarging the headroom so the entire PREPARE phase window (5 minor cycles: .50-.54) flows without per-PR rebaseline noise, given that re-tightening happens mechanically at v3.8.51 via #8675/#8700 decomposition work scheduled in .51 (Executor registry in-place + combo.ts decomposition per ROADMAP.md). RE-TIGHTENING MANDATORY in v3.8.51: target 2282 (shrink of 492 from structural extraction during #8675/#8700 decomposition campaigns + combo.ts split scheduled in .52, or via npm run quality:ratchet -- --update if natural shrink appears earlier). The 2282 floor still gives 94 units of post-tighten headroom vs the current pristine 2188. Tracked via roadmap issue (TO BE OPENED as part of this PR). Window: v3.8.50 (release cut, 2026-07-28 target) \u2192 v3.8.54 close (RE-TIGHTEN executed at v3.8.51 prep merge per ROADMAP.md). This entry is the LAST rebaseline in this file unless a measured regression appears. v1 entry retained below for audit trail.",
|
||||
"count": 2774,
|
||||
"_rebaseline_2026_07_27_3850_quality_relax_TEMP_3851_retighten": "_rebaseline_2026_07_27_3850_quality_relax_TEMP_3851_retighten: 2188->2312 (+124). OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). +124 = 2x the daily drift observed in jul (~4.5 cyclomatic/day over 25d = ~114); covers ~28d of normal merge activity without per-PR rebaseline churn. RE-TIGHTENING MANDATORY in v3.8.51: target 2282 (shrink of 30 from structural extraction during #8675/#8700 decomposition campaigns, or via npm run quality:ratchet -- --update if natural shrink appears). Tracked via roadmap issue (TO BE OPENED). Window: 3.8.50 (release cut) \u2192 3.8.54 close (re-tighten at 3.8.51 prep merge per ROADMAP.md). SUPERSEDED by _rebaseline_2026_07_27_3850_quality_relax_TEMP_3851_retighten_v2_20pct (v1 +20% buffer) \u2014 retained for audit. Tracked via roadmap issue (TO BE OPENED as part of this PR). Window: v3.8.50 (release cut) \u2192 v3.8.54 close (RE-TIGHTEN executed at v3.8.51 prep merge per ROADMAP.md).",
|
||||
"_rebaseline_2026_07_25b_v3849_mergetrain_owngrowth": "Owner-approved (chat, 2026-07-25): 2169->2183 (+14). v3.8.49 /merge-prs 41-PR merge-train aggregate own-growth: measured 2183 on the combined boarded tree (tip ac15014ca7) vs 2169 on the pristine release tip. Each boarded PR sits under the ceiling individually, but the combined batch adds +14 (new branches in #8378 chatCore contextLimit / #8432 cursor native_todo / #8476 combo input-bound / #8526 combo select-all modals / etc — the pre-screen-flagged complexity-growth set). Same merge-burst-inherited-drift class as the notes below; owner chose absorbing the ceiling over per-PR helper-extraction churn. Structural shrink stays debt (#3501); tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_25_v3849_mergequeue_drain": "Owner-approved (chat, 2026-07-25): 2130->2169 (+39). v3.8.49 /merge-prs queue-drain: the cycle's merge burst (the 8 base-red slices + owner PRs + parallel-session merges #8500-8508) accrued inherited cyclomatic drift the fast-path PR->release never ratchets (check:complexity does not run on PR->release). Measured 2169 on the pristine release tip 4053e2314a alone (BEFORE any queue PR boards) — so the entire +39 is base drift already on the tip, not any queued PR's own growth. Every merge-ready PR in the queue was tripping Fast Quality Gates on this shared base-red. Owner approved raising the ceiling to the measured tip value so the ~34-PR merge-train lands without per-PR helper-extraction churn. Structural shrink stays debt (#3501); tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_27_3850_quality_relax_TEMP_3851_retighten_v2_20pct": "_rebaseline_2026_07_27_3850_quality_relax_TEMP_3851_retighten_v2_20pct: 2188->2774 (+586, +26.8% over pristine 2188). OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). v1 was +124 (2x daily drift) on 2026-07-27; v2 = v1 +20% buffer = +218 → +586 total (cycle 2188 measured pristine → 2774 ceiling). Justification: v3.8.50 release cut coincides with high-merge activity (33-PR Train 1D + ~37-PR Train 2 backlog + post-freeze re-home rebase churn); owner accepted enlarging the headroom so the entire PREPARE phase window (5 minor cycles: .50-.54) flows without per-PR rebaseline noise, given that re-tightening happens mechanically at v3.8.51 via #8675/#8700 decomposition work scheduled in .51 (Executor registry in-place + combo.ts decomposition per ROADMAP.md). RE-TIGHTENING MANDATORY in v3.8.51: target 2282 (shrink of 492 from structural extraction during #8675/#8700 decomposition campaigns + combo.ts split scheduled in .52, or via npm run quality:ratchet -- --update if natural shrink appears earlier). The 2282 floor still gives 94 units of post-tighten headroom vs the current pristine 2188. Tracked via roadmap issue (TO BE OPENED as part of this PR). Window: v3.8.50 (release cut, 2026-07-28 target) → v3.8.54 close (RE-TIGHTEN executed at v3.8.51 prep merge per ROADMAP.md). This entry is the LAST rebaseline in this file unless a measured regression appears. v1 entry retained below for audit trail.",
|
||||
"_rebaseline_2026_07_27_3850_quality_relax_TEMP_3851_retighten": "_rebaseline_2026_07_27_3850_quality_relax_TEMP_3851_retighten: 2188->2312 (+124). OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). +124 = 2x the daily drift observed in jul (~4.5 cyclomatic/day over 25d = ~114); covers ~28d of normal merge activity without per-PR rebaseline churn. RE-TIGHTENING MANDATORY in v3.8.51: target 2282 (shrink of 30 from structural extraction during #8675/#8700 decomposition campaigns, or via npm run quality:ratchet -- --update if natural shrink appears). Tracked via roadmap issue (TO BE OPENED). Window: 3.8.50 (release cut) → 3.8.54 close (re-tighten at 3.8.51 prep merge per ROADMAP.md). SUPERSEDED by _rebaseline_2026_07_27_3850_quality_relax_TEMP_3851_retighten_v2_20pct (v1 +20% buffer) — retained for audit. Tracked via roadmap issue (TO BE OPENED as part of this PR). Window: v3.8.50 (release cut) → v3.8.54 close (RE-TIGHTEN executed at v3.8.51 prep merge per ROADMAP.md).",
|
||||
"_rebaseline_2026_07_19_v3849_fix_sweep_cluster": "2059->2072 (owner-approved, 2026-07-19). /fix-prs validation-train sweep: a cluster of otherwise-clean contributor PRs (#6973/#7683/#7662/#7672/#7633/#7767, each +1/+2 cyclomatic own-growth from new provider/auth/combo branches) collectively pushed the count from tip 2056 to 2068. Individually all but #6973 sit under the old 2059 baseline; combined they exceed it. The tip had only 3 units of slack (2056 vs 2059), so every new-feature PR was tripping the ratchet (this was the 4th such block of the day after #7695/#7747/#7768). Owner approved raising the ceiling to 2072 = combined-cluster 2068 + 4 units headroom, so the cluster lands without per-PR helper-extraction churn and near-term feature PRs have breathing room. Measured 2068 on the 9-PR combined probe tree. Structural shrink stays debt (#3501); tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_18_pr7360_quota_visibility_resync": "2058->2059 (+1 vs recorded ceiling; measured 2056 fresh on release tip cab9e5f0c alone, so this ceiling still carries 2 units of un-banked slack from prior shrinkage \u2014 real regression from this merge is 2056->2059, +3). PR #7360 (JxnLexn) release-resync: merging origin/release/v3.8.49 to resolve the 3-file conflict (ConnectionRow.tsx/ConnectionsListPanel.tsx/useProviderConnections.ts) unions two already-compliant features in the same already-oversized god-component: release's confirm-delete-account wiring (#7361) and this PR's per-connection quota-visibility wiring. Diffed release-tip-only vs merged violation lists (scripts dumped via getComplexityEslintReport): most entries are the SAME pre-existing violations shifted a few lines (ConnectionRow/getStatusPresentation/inferErrorType \u2014 no count change) or marginally bigger (ConnectionRow function complexity 85->86, ConnectionsListPanel function 498->510 lines) from the two ConnectionRow call sites each gaining both PRs' multi-line JSX props. The 2 genuinely NEW crossings are the 'no tag' and 'tagged groups' .map() render callbacks in ConnectionsListPanel.tsx (83 and 85 lines, was <=80 on both parents individually) tipping over 80 lines specifically because both PRs' props land on the same call sites. No new logic was written during the resync itself (only import-statement unions); the growth is inherent to combining the two already-reviewed feature branches. Structural shrink tracked in #3501. Tighten via --update next cycle (true floor is 2056, not 2058).",
|
||||
"_rebaseline_2026_07_18_pr7360_quota_visibility_resync": "2058->2059 (+1 vs recorded ceiling; measured 2056 fresh on release tip cab9e5f0c alone, so this ceiling still carries 2 units of un-banked slack from prior shrinkage — real regression from this merge is 2056->2059, +3). PR #7360 (JxnLexn) release-resync: merging origin/release/v3.8.49 to resolve the 3-file conflict (ConnectionRow.tsx/ConnectionsListPanel.tsx/useProviderConnections.ts) unions two already-compliant features in the same already-oversized god-component: release's confirm-delete-account wiring (#7361) and this PR's per-connection quota-visibility wiring. Diffed release-tip-only vs merged violation lists (scripts dumped via getComplexityEslintReport): most entries are the SAME pre-existing violations shifted a few lines (ConnectionRow/getStatusPresentation/inferErrorType — no count change) or marginally bigger (ConnectionRow function complexity 85->86, ConnectionsListPanel function 498->510 lines) from the two ConnectionRow call sites each gaining both PRs' multi-line JSX props. The 2 genuinely NEW crossings are the 'no tag' and 'tagged groups' .map() render callbacks in ConnectionsListPanel.tsx (83 and 85 lines, was <=80 on both parents individually) tipping over 80 lines specifically because both PRs' props land on the same call sites. No new logic was written during the resync itself (only import-statement unions); the growth is inherent to combining the two already-reviewed feature branches. Structural shrink tracked in #3501. Tighten via --update next cycle (true floor is 2056, not 2058).",
|
||||
"_rebaseline_2026_07_17_v3849_ownerprs_providers": "2056->2058 (+2). v3.8.49 owner-PR merge campaign own-growth: the new provider handlers/dispatch branches merged this cycle (freetheai/felo/notion/segmind/deepinfra/novita/msdesigner image+video handlers, each adding a format-dispatch guard) pushed cyclomatic violations 2056->2058. Fast-gates PR->release do not run the complexity ratchet, so this surfaced only on re-sync. Spread across the new leaf handlers (not a single extractable function); measured on the release tip. Structural shrink tracked in #3501.",
|
||||
"_rebaseline_2026_07_10_v3847_merge_burst": "2053->2054 (+1). Drift herdado do merge burst do dia em release/v3.8.47 (campanha /implement-prs: ~36 PRs mergeados \u2014 \u00f3rf\u00e3os, features do dono, ports). O check:complexity N\u00c3O roda no fast-path PR->release, ent\u00e3o o ramo acumulou o +1 sem rebaselinar (mesma fam\u00edlia de todos os rebaselines abaixo). Trust-but-verify: medido 2054 no tip da release p\u00f3s-burst; a \u00fanica fun\u00e7\u00e3o flagada nova \u00e9 pr\u00e9-existente (getResolvedModelCapabilities em modelCapabilities.ts, j\u00e1 >teto antes de #6714). Nenhum PR \u00f3rf\u00e3o/feature introduz viola\u00e7\u00e3o NOVA \u2014 os fixes deste ciclo s\u00e3o complexity-net-zero. Rebaseline aprovado pelo dono (2026-07-10) para destravar o FQG dos ~7 \u00f3rf\u00e3os verdes-exceto-complexity. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_10_v3847_merge_burst": "2053->2054 (+1). Drift herdado do merge burst do dia em release/v3.8.47 (campanha /implement-prs: ~36 PRs mergeados — órfãos, features do dono, ports). O check:complexity NÃO roda no fast-path PR->release, então o ramo acumulou o +1 sem rebaselinar (mesma família de todos os rebaselines abaixo). Trust-but-verify: medido 2054 no tip da release pós-burst; a única função flagada nova é pré-existente (getResolvedModelCapabilities em modelCapabilities.ts, já >teto antes de #6714). Nenhum PR órfão/feature introduz violação NOVA — os fixes deste ciclo são complexity-net-zero. Rebaseline aprovado pelo dono (2026-07-10) para destravar o FQG dos ~7 órfãos verdes-exceto-complexity. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_10_gcf_v3_2": "2054->2056 (+2). PR feat/headroom-gcf-v3.2-nested-flattening: own growth from re-vendoring the GCF (Headroom) codec to spec v3.2 (nested flattening). The 2 new over-threshold functions are the v3.2 `>`-path flatten/unflatten walk in the vendored generic-profile encode/decode paths (open-sse/services/compression/engines/headroom/gcf/{generic,decode_generic}.ts). This is imported third-party code kept byte-faithful to upstream gcf-typescript, not extractable without diverging from the vendored source; local measures 2055 on the merged tree; frozen at 2056 = the base's CI-observed 2054 + this PR's 2 new functions, matching the documented local-vs-CI off-by-one convention (see _rebaseline_2026_07_02_v3844_ci_observed) so the GitHub runner stays green. Round-trip guarded by tests/unit/compression/headroom-smartcrusher.test.ts (deep-nested case). Structural shrink belongs upstream in gcf, not here.",
|
||||
"_rebaseline_2026_07_08_6556_inherited_drift": "2052->2053 (+1). PR #6556 (omniglyph engine): drift herdado do merge burst da base (a catraca nao roda no fast-path PR->release, mesmo padrao dos rebaselines v3.8.44/46). Trust-but-verify: o proprio codigo do PR e complexity-net-zero \u2014 as 2 violacoes que ele introduzia (runCompressionAsync complexity 17 apos o branch do modo omniglyph; OmniglyphContextPageClient 161 linhas) foram CORRIGIDAS por extracao real (engines/omniglyphSingleMode.ts + split do componente em section components), medido: 2055->2053 local; base pura origin/release/v3.8.47 mede 2053 identico. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_07_v3846_release_close": "2035->2050 (+15). v3.8.46 release close (generate-release Phase 0 pre-flight): drift herdado do merge burst do ciclo (39 commits do dia + campanha /review-*). Trust-but-verify: os fixes de base-red do captain (agentSkills path.resolve #6366, catalogo cache #6408, tipagem de teste no-explicit-any, MitmProxyTab suppression) sao complexity-net-zero \u2014 check:complexity mede 2050 identico com e sem os fixes (a catraca NAO roda no fast-path PR->release, entao o ramo acumulou sem rebaselinar). Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_04_v3844_release_close": "2026->2028 (+2). v3.8.44 release close (generate-release Phase 0/1): drift residual do fim do ciclo medido no tip pos-#6155 (merge burst final: #6155 cooling-panel + #6104 Kenari + #6139/#6128 provider-limits). Trust-but-verify: os 2 fixes de codigo do release-captain (model.ts alias boundary, auggie.ts stdin error handlers) adicionam 0 violacoes NOVAS \u2014 eslint.complexity direto nos 2 arquivos flagra apenas funcoes que ja estouravam o limite antes (runStreaming/start ja >80 linhas; resolveModelByProviderInference/getModelInfoCore pre-existentes de #5918), e resolveProviderAlias segue abaixo de 15. Logo o +2 e drift herdado do burst. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_08_6556_inherited_drift": "2052->2053 (+1). PR #6556 (omniglyph engine): drift herdado do merge burst da base (a catraca nao roda no fast-path PR->release, mesmo padrao dos rebaselines v3.8.44/46). Trust-but-verify: o proprio codigo do PR e complexity-net-zero — as 2 violacoes que ele introduzia (runCompressionAsync complexity 17 apos o branch do modo omniglyph; OmniglyphContextPageClient 161 linhas) foram CORRIGIDAS por extracao real (engines/omniglyphSingleMode.ts + split do componente em section components), medido: 2055->2053 local; base pura origin/release/v3.8.47 mede 2053 identico. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_07_v3846_release_close": "2035->2050 (+15). v3.8.46 release close (generate-release Phase 0 pre-flight): drift herdado do merge burst do ciclo (39 commits do dia + campanha /review-*). Trust-but-verify: os fixes de base-red do captain (agentSkills path.resolve #6366, catalogo cache #6408, tipagem de teste no-explicit-any, MitmProxyTab suppression) sao complexity-net-zero — check:complexity mede 2050 identico com e sem os fixes (a catraca NAO roda no fast-path PR->release, entao o ramo acumulou sem rebaselinar). Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_04_v3844_release_close": "2026->2028 (+2). v3.8.44 release close (generate-release Phase 0/1): drift residual do fim do ciclo medido no tip pos-#6155 (merge burst final: #6155 cooling-panel + #6104 Kenari + #6139/#6128 provider-limits). Trust-but-verify: os 2 fixes de codigo do release-captain (model.ts alias boundary, auggie.ts stdin error handlers) adicionam 0 violacoes NOVAS — eslint.complexity direto nos 2 arquivos flagra apenas funcoes que ja estouravam o limite antes (runStreaming/start ja >80 linhas; resolveModelByProviderInference/getModelInfoCore pre-existentes de #5918), e resolveProviderAlias segue abaixo de 15. Logo o +2 e drift herdado do burst. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_03_v3844_ipfilter_release_green": "2015->2026 (+11). v3.8.44 cycle drift measured on release tip 32e4c906e during the #6131/#5975 release-green rebaseline. Inherited from the merge burst (Quality Ratchet does not run on PR->release fast-gates). route-edge-coverage +7 is my #5975 test comment; the rest is parallel-session drift. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_03_6007_sidecar_manifest": "2007->2015. PR #6007 (re-cut) adds sidecar/provider-manifest header wiring and the public manifest URL helper; the feature files themselves introduce 0 new complexity violations (check-complexity flags none in providerPluginManifestUrl.ts). The +8 vs the 2007 release baseline is inherited release/v3.8.44 drift absorbed at merge (check:complexity measures 2015 on the current release tip). Tighten via --update next cycle / at /generate-release Phase 0.",
|
||||
"_rebaseline_2026_07_02_v3844_ci_observed": "2006->2007 (+1). Local Ubuntu measures 2006 on this tree; the GitHub fast-gates runner measures 2007 (same local-vs-CI off-by-one already documented in _rebaseline_2026_06_26_v3838_release_fast_gate). Use the CI-observed value so the gate is deterministic where it actually runs.",
|
||||
"_rebaseline_2026_07_02_v3844_post_5939": "2003->2006 (+3). Inherited drift from the release/v3.8.44 merges after 3a3d618fe (#5809 audio translations et al.), surfaced by PR fix#5959: check:complexity measures 2006 on the pristine base (cbd08ef78) WITH AND WITHOUT this PR's one-line CLI change (verified by reverting the file and re-measuring) \u2014 the PR is complexity-net-zero. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_02_v3844_merge_burst": "1995->2003 (+8). Inherited v3.8.44 cycle drift surfaced by PR #5939: check:complexity measures 2003 on BOTH the pristine release tip (3a3d618fe) and this PR's merged HEAD \u2014 identical, so all +8 came from the 2026-07-02 merge burst into release/v3.8.44 (#5933 codex schema, #5950 OCR, #5904/#5920 combo, #6000/#6008 executor refactors, etc.) merged while the fast-gates queue was base-red (file-size #5933). PR #5939 itself was verified complexity-net-zero during its own CI cycle (DiscoveryPageClient refactored into hooks/sub-components to stay under max-lines-per-function). Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_02_5798_release_green": "1982->1995 (+13). Inherited v3.8.43 cycle drift surfaced by the release-green unblock #5798 / PR #5896: check:complexity measures 1995 on BOTH the pristine release tip (0d3875a98) and this PR's HEAD \u2014 identical, so all +13 came from the 2026-07-01/02 merge burst (providers/usage/dashboard fixes merged via --admin while the fast-gates queue was base-red). This PR touches only gate scripts, docs, baselines and test files \u2014 0 production logic. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_01_v3843_release": "1981->1982 (+1). v3.8.43 cycle drift, surfaced after check:mutation-test-coverage was fixed (it was masked behind that earlier step in the Fast Quality Gates chain). 1982 = the value measured by check:complexity on BOTH fce85136c (release tip) and 6d7060e21 (release + the 5 CI fixes) \u2014 identical, so all +1 is inherited cycle drift; the fixes touch only test files + linkify.ts safeHttpHref (cyclomatic ~4, well under the >=15 threshold, 0 new violations) + config JSON. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_06_28_v3840_5237_reconcile": "1980->1981 (+1). Inherited release/v3.8.40 drift surfaced while merging PR #5237 (impersonation-UA refresh) \u2014 the +1 is present on the pristine release tip (d8a392a47) WITHOUT #5237's changes, so it is #5222 (antigravity fallback-LRU retry) / #5221 (command-code) growth that merged via --admin without ratcheting complexity (the PR->release fast-gates do not run check:complexity). #5237 itself is complexity-net-zero: its only edits are a single UA constant, a regenerated golden snapshot, and baseline JSONs. Structural reduction tracked in #3501.",
|
||||
"_rebaseline_2026_06_27_v3838_release": "1978->1980 (+2). v3.8.38 cycle-close drift surfaced by the release-green pre-flight (check:complexity does NOT run on PR->release fast-gates). +2 from late-cycle feature/fix merges (compression fidelity-gate steps #5143, SSE hardening). Release-finalize working tree touches ONLY CHANGELOG.md + i18n mirrors + the 2 baseline JSONs \u2014 0 production-code change. Structural reduction tracked in #3501.",
|
||||
"_rebaseline_2026_07_02_v3844_post_5939": "2003->2006 (+3). Inherited drift from the release/v3.8.44 merges after 3a3d618fe (#5809 audio translations et al.), surfaced by PR fix#5959: check:complexity measures 2006 on the pristine base (cbd08ef78) WITH AND WITHOUT this PR's one-line CLI change (verified by reverting the file and re-measuring) — the PR is complexity-net-zero. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_02_v3844_merge_burst": "1995->2003 (+8). Inherited v3.8.44 cycle drift surfaced by PR #5939: check:complexity measures 2003 on BOTH the pristine release tip (3a3d618fe) and this PR's merged HEAD — identical, so all +8 came from the 2026-07-02 merge burst into release/v3.8.44 (#5933 codex schema, #5950 OCR, #5904/#5920 combo, #6000/#6008 executor refactors, etc.) merged while the fast-gates queue was base-red (file-size #5933). PR #5939 itself was verified complexity-net-zero during its own CI cycle (DiscoveryPageClient refactored into hooks/sub-components to stay under max-lines-per-function). Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_02_5798_release_green": "1982->1995 (+13). Inherited v3.8.43 cycle drift surfaced by the release-green unblock #5798 / PR #5896: check:complexity measures 1995 on BOTH the pristine release tip (0d3875a98) and this PR's HEAD — identical, so all +13 came from the 2026-07-01/02 merge burst (providers/usage/dashboard fixes merged via --admin while the fast-gates queue was base-red). This PR touches only gate scripts, docs, baselines and test files — 0 production logic. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_01_v3843_release": "1981->1982 (+1). v3.8.43 cycle drift, surfaced after check:mutation-test-coverage was fixed (it was masked behind that earlier step in the Fast Quality Gates chain). 1982 = the value measured by check:complexity on BOTH fce85136c (release tip) and 6d7060e21 (release + the 5 CI fixes) — identical, so all +1 is inherited cycle drift; the fixes touch only test files + linkify.ts safeHttpHref (cyclomatic ~4, well under the >=15 threshold, 0 new violations) + config JSON. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_06_28_v3840_5237_reconcile": "1980->1981 (+1). Inherited release/v3.8.40 drift surfaced while merging PR #5237 (impersonation-UA refresh) — the +1 is present on the pristine release tip (d8a392a47) WITHOUT #5237's changes, so it is #5222 (antigravity fallback-LRU retry) / #5221 (command-code) growth that merged via --admin without ratcheting complexity (the PR->release fast-gates do not run check:complexity). #5237 itself is complexity-net-zero: its only edits are a single UA constant, a regenerated golden snapshot, and baseline JSONs. Structural reduction tracked in #3501.",
|
||||
"_rebaseline_2026_06_27_v3838_release": "1978->1980 (+2). v3.8.38 cycle-close drift surfaced by the release-green pre-flight (check:complexity does NOT run on PR->release fast-gates). +2 from late-cycle feature/fix merges (compression fidelity-gate steps #5143, SSE hardening). Release-finalize working tree touches ONLY CHANGELOG.md + i18n mirrors + the 2 baseline JSONs — 0 production-code change. Structural reduction tracked in #3501.",
|
||||
"_rebaseline_2026_06_26_v3838_ownerprs_batch": "1972->1978 (+6). Drift do lote de merges de PRs do dono + contribuidores em release/v3.8.38 (sessao /review-prs): #4845 (antigravity convertGeminiToOpenAI), #5105 (executor zenmux-free), #5020 (executor grok-cli), #4940 (usage dedupe guard), #5093 (resilience: quota cutoff/gemini mime/model-lockout cooldown), #5015 (quota hydration + auto-combo scoping). Cada um e crescimento de feature/fix legitimo recem-TDD'd, nao regressao; o gate check:complexity NAO roda no fast-path PR->release, entao o ramo acumula sem rebaselinar (mesma familia dos rebaselines anteriores). #5121 cookie-dedup foi mantido complexity-NEUTRO via extracao do helper findExistingCookieConnection. Reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_26_v3838_release_fast_gate": "1963->1972 (+9). Reconciles inherited release/v3.8.38 drift surfaced by PR #5124. Local origin/release/v3.8.38 measured 1971 and this PR also measured 1971 after refactoring the new JSON-body SSE sniffing path, while the GitHub Ubuntu fast gate measured 1972; use the CI-observed value so the release branch gate is deterministic. The streaming fix is complexity-net-zero relative to the local release base. The release fast-path does not consistently ratchet complexity between release-cycle merges; keep structural reductions as separate debt.",
|
||||
"_rebaseline_2026_06_25_v3836_release": "Reconciliacao release-volatil 1920->1950 (+30) no fechamento do ciclo v3.8.36, surfada pelo CI da fix-PR #5029 (a catraca de complexidade NAO roda no fast-path PR->release nem foi medida no release PR #4854 \u2014 Quality Ratchet foi SKIPPED la \u2014 so PR->main, entao o ramo acumulou os 137 commits sem rebaselinar). O +30 e drift de condicionais NOVOS das features legitimas do ciclo: Quota-Share Fase 2/3 (estrategia dedicada DRR+P2C, multi-window buckets, concurrency control, headroom, saturacao proativa \u2014 #4885/#4907/#4908/#4927/#4928/#4929/#4939/#4965/#4967/#4970), task-aware + Fusion combo (#4945/#4652), e ramos de provider/translator de contribuidores. A god-file decomposition #3501 e PURA (move codigo p/ leaves, complexity-neutra). Verificado que esta fix-PR (#5029) toca SO scripts/build/pack-artifact-policy.ts (array de strings), tests/integration/resilience-http-e2e.test.ts (2 keys) e os 2 baselines json \u2014 contribui 0 ao gate que varre src+open-sse+electron+bin. Mesma familia dos rebaselines anteriores \u2014 crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_23_v3835_release": "Reconciliacao release-volatil 1916->1920 (+4) no fechamento do ciclo v3.8.35, surfada pelo pre-flight check:release-green (a catraca de complexidade NAO roda no fast-path PR->release, so release->main, entao o ramo acumula sem rebaselinar). O +4 e drift de condicionais NOVOS dos merges de contribuidor/feature deste ciclo (Compression Phase 4 #4694/#4707/#4716/#4720, combos auto-promote #4774, tier no-auth #4753, deepseek-web tool-fold #4756, dedupe provider nodes #4768). Verificado que o trabalho de release-finalize desta sessao toca SO docs/*.md (THREAT_MODEL), CHANGELOG.md, baselines e 1 linha de string em scripts/check/check-fabricated-docs.mjs (fora do escopo src+open-sse+electron+bin que o gate varre) \u2014 contribui 0. Mesma familia dos rebaselines anteriores \u2014 crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_23_v3834_release": "Reconciliacao release-volatil 1915->1916 (+1) no fechamento do ciclo v3.8.34. check:complexity NAO roda no fast-path PR->release (so release->main), entao o ramo acumula sem rebaselinar; surfou no full CI do release PR (run em c98e7ff6d). O +1 e drift de condicional NOVO de merge de contribuidor do ciclo (features quota/usage/opencode-go/M365). Verificado que o commit de release-finalize NAO adiciona complexity: toca CHANGELOG/baseline/mirrors/3 testes + 1 linha de regex em opencodeOllamaUsage.ts (sem novo ramo) + reorder de dados no reka registry \u2014 local mede 1916 com ou sem essa mudanca. Mesma familia dos rebaselines anteriores \u2014 crescimento de feature legitimo, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_21_v3833_4537_nested_combo": "Reconciliacao 1913->1915 (+2) do PR #4537 (nestedComboMode execute \u2014 black-box combo-ref execution). O +2 vem do branch novo de dispatch nested em handleComboChat (combo.ts: normalizeNestedComboMode + executeModeUnits/hasExecutableComboRef + o ramo simpleExecuteStrategies que roda resolveComboRuntimeUnits com recursion caps depth/cycle/budget) \u2014 ramos condicionais no chokepoint de dispatch do combo. Medido com `node scripts/check/check-complexity.mjs` no estado merged. Crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_25_v3836_release": "Reconciliacao release-volatil 1920->1950 (+30) no fechamento do ciclo v3.8.36, surfada pelo CI da fix-PR #5029 (a catraca de complexidade NAO roda no fast-path PR->release nem foi medida no release PR #4854 — Quality Ratchet foi SKIPPED la — so PR->main, entao o ramo acumulou os 137 commits sem rebaselinar). O +30 e drift de condicionais NOVOS das features legitimas do ciclo: Quota-Share Fase 2/3 (estrategia dedicada DRR+P2C, multi-window buckets, concurrency control, headroom, saturacao proativa — #4885/#4907/#4908/#4927/#4928/#4929/#4939/#4965/#4967/#4970), task-aware + Fusion combo (#4945/#4652), e ramos de provider/translator de contribuidores. A god-file decomposition #3501 e PURA (move codigo p/ leaves, complexity-neutra). Verificado que esta fix-PR (#5029) toca SO scripts/build/pack-artifact-policy.ts (array de strings), tests/integration/resilience-http-e2e.test.ts (2 keys) e os 2 baselines json — contribui 0 ao gate que varre src+open-sse+electron+bin. Mesma familia dos rebaselines anteriores — crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_23_v3835_release": "Reconciliacao release-volatil 1916->1920 (+4) no fechamento do ciclo v3.8.35, surfada pelo pre-flight check:release-green (a catraca de complexidade NAO roda no fast-path PR->release, so release->main, entao o ramo acumula sem rebaselinar). O +4 e drift de condicionais NOVOS dos merges de contribuidor/feature deste ciclo (Compression Phase 4 #4694/#4707/#4716/#4720, combos auto-promote #4774, tier no-auth #4753, deepseek-web tool-fold #4756, dedupe provider nodes #4768). Verificado que o trabalho de release-finalize desta sessao toca SO docs/*.md (THREAT_MODEL), CHANGELOG.md, baselines e 1 linha de string em scripts/check/check-fabricated-docs.mjs (fora do escopo src+open-sse+electron+bin que o gate varre) — contribui 0. Mesma familia dos rebaselines anteriores — crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_23_v3834_release": "Reconciliacao release-volatil 1915->1916 (+1) no fechamento do ciclo v3.8.34. check:complexity NAO roda no fast-path PR->release (so release->main), entao o ramo acumula sem rebaselinar; surfou no full CI do release PR (run em c98e7ff6d). O +1 e drift de condicional NOVO de merge de contribuidor do ciclo (features quota/usage/opencode-go/M365). Verificado que o commit de release-finalize NAO adiciona complexity: toca CHANGELOG/baseline/mirrors/3 testes + 1 linha de regex em opencodeOllamaUsage.ts (sem novo ramo) + reorder de dados no reka registry — local mede 1916 com ou sem essa mudanca. Mesma familia dos rebaselines anteriores — crescimento de feature legitimo, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_21_v3833_4537_nested_combo": "Reconciliacao 1913->1915 (+2) do PR #4537 (nestedComboMode execute — black-box combo-ref execution). O +2 vem do branch novo de dispatch nested em handleComboChat (combo.ts: normalizeNestedComboMode + executeModeUnits/hasExecutableComboRef + o ramo simpleExecuteStrategies que roda resolveComboRuntimeUnits com recursion caps depth/cycle/budget) — ramos condicionais no chokepoint de dispatch do combo. Medido com `node scripts/check/check-complexity.mjs` no estado merged. Crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_21_v3833_reviewprs_r4_owner": "Reconciliacao release-volatil 1911->1913 (+2) apos o lote de PRs do owner desta rodada (#4560 RTK cache_control, #4552 Cursor auto-import macOS, #4551 Codex /responses probe, #4554 Cursor Composer </think> decode + o stack #3501 #4538/#4544/#4548). O fast-path do release nao roda check:complexity (so release->main). As 3 extracoes chatCore #3501 (#4538 recupera 4 leaves, #4544 failureUsage, #4548 claudeSystemRole+upstreamExecuteHeaders) sao PURAS/complexity-neutras (movem codigo p/ leaves sob o teto, chatCore ENCOLHE); o +2 vem dos condicionais NOVOS das features .32-portadas (#4554 decode de bloco </think> visivel do Composer + #4551 probe do endpoint real Codex /responses). Medido com `node scripts/check/check-complexity.mjs` no tip 4b34a75fe. Crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_21_v3833_reviewprs_r4": "Reconciliacao release-volatil 1906->1911 (+5) apos o lote /review-prs r4 (5 PRs de contribuidores mergeados em release/v3.8.33: #4557 health-polls, #4556 mobile-table, #4545 provider-wildcard, #4558 isHidden, #4489 sticky-weighted) + 1 merge concorrente de sessao paralela (#4565 quota perf). O fast-path do release nao roda check:complexity (so release->main), entao o ramo acumula sem rebaselinar. Condicionais NOVOS legitimos: #4545 (expandProviderWildcardsInCombo/Collection + wildcardMatch glob/registry branching em providerWildcard.ts/wildcardRouter.ts), #4489 (eligibility pass sticky-weighted: resolveWeightedStepGroups + isTargetSelectableForWeighted + renormalizacao em combo.ts), #4558 (filtro isHidden em buildAutoCandidates/virtualFactory) e #4565 (guardas de skip de quota_snapshots idle). Medido com `node scripts/check/check-complexity.mjs` no tip 39b2bbfea. Mesma familia dos rebaselines anteriores \u2014 crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_21_v3833_cycle_open_stranded": "1905 -> 1906 (+1). Abertura do ciclo v3.8.33: o +1 vem dos 4 commits stranded na release/v3.8.32 (p\u00f3s-merge da v3.8.32) trazidos via cherry-pick para release/v3.8.33 \u2014 isolado em #4483 (autoStrategy.ts evaluateQuotaCutoff/quotaPreflight.ts, guardas de quota-cutoff inerentemente ramificadas, opt-in default-OFF). main mede 1905; o .33 branch (main + cherry-picks) mede 1906. Crescimento de feature legitimo rec\u00e9m-mergeado, n\u00e3o regress\u00e3o; redu\u00e7\u00e3o fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_20_reviewprs_mine_r2": "Reconciliacao release-volatil 1900->1905 (+5) apos o lote /review-prs 'apenas minhas' r2 (17 PRs meus mergeados em release/v3.8.32). Breakdown medido com `node scripts/check/check-complexity.mjs` + diff por-arquivo eslint complexity JSON entre o tip pre-lote (9052c5a78 = 1902) e o tip pos-lote (1905): (a) +2 JA latentes no tip pre-lote (1900->1902), drift de merges concorrentes de outras sessoes ANTERIORES a este lote (nao introduzidos por mim) que o fast-path do release nao rebaselina (check:complexity so roda release->main); (b) +3 deste lote, isolados em DOIS arquivos: open-sse/translator/helpers/geminiHelper.ts +1 (convertOpenAIContentToParts passou de 80->93 linhas pela branch de audio do #4426 \u2014 max-lines-per-function, funcao de dispatch coesa por tipo de content part) e open-sse/translator/response/openai-to-gemini-sse.ts +2 (translator SSE NOVO do #4453, openAIChunkToGeminiChunk + convertOpenAIResponseToGemini em complexity 19 cada \u2014 conversores de chunk SSE inerentemente ramificados, levemente acima de 15). Crescimento de feature legitimo recem-TDD'd, nao regressao; refatorar feature recem-mergeada so para raspar +3 seria over-engineering arriscado. Reducao fica como debt.",
|
||||
"_rebaseline_2026_06_20_reviewprs_v3831_batch": "Reconciliacao release-volatil 1896->1900 (+4): drift do lote /review-prs v3.8.31 (25 PRs A+B+C + merges concorrentes). Condicionais NOVOS legitimos \u2014 sobretudo #4381 (rotas /api/local/redis/{start,stop,status} detectRuntime + guardas + bifrost relay) e #4366 (classificacao de exhaustion de erro entre os 2 dispatchers de combo). Medido 1900 ESTAVEL em fd1391c0b E f46c69f2a com `node scripts/check/check-complexity.mjs` (commit concorrente intermediario foi complexity-neutro). O fast-path do release nao roda check:complexity (so release->main), entao o ramo acumula sem rebaselinar. Mesma familia dos rebaselines anteriores \u2014 crescimento de feature legitimo, nao regressao; valor final reconciliado no release->main.",
|
||||
"_rebaseline_2026_06_20_postlote_concurrent_drift": "Reconciliacao release-volatil: 1895->1896 (+1). Drift de condicional NOVO de PRs mergeados pela sessao concorrente APOS o #4338 ratchetar para 1895 (#4355 pricing gpt-5.x-pro / #4364 cli active-context cred / #4363 compliance cleanup / #4358 mitm mask / #4332 injection-guard-16KB). O fast-path do release nao roda check:complexity (so release->main), entao o ramo acumula sem rebaselinar. Medido com `node scripts/check/check-complexity.mjs` no tip cdfd71c17. Mesma familia \u2014 crescimento de feature legitimo, nao regressao.",
|
||||
"_note_2026_06_20_4371_chatcore_heap_leaf": "PR #4371 (extract checkHeapPressureGuard leaf, god-file decomposition start) e complexity-NEUTRO: handleChatCore so PERDE codigo e o novo heapPressure.ts checkHeapPressureGuard fica sob o teto \u2014 a contagem permanece 1896 (medido no tip mesclado com check-complexity.mjs).",
|
||||
"_ratchet_2026_06_19_phasecombosetup_fix": "1896->1895 (-1, ratchet DOWN \u2014 melhoria, NAO reconciliacao). O #4336 reconciliou o drift do lote para 1896 INCLUINDO a violacao que o #4326 (ComboContext) introduziu: phaseComboSetup media complexity 17 (>15) porque a extracao concentrou os condicionais de pinning/ternarios numa funcao que estourava o teto (ir\u00f4nico p/ uma decomposicao). Este PR CORRIGE na origem \u2014 extrai resolveContextCachePin (helper do pinning), phaseComboSetup volta a <15 \u2014 baixando a contagem 1. Medido com `node scripts/check/check-complexity.mjs` no tip pos-#4336.",
|
||||
"_rebaseline_2026_06_19_lote3_postdeploy_drift": "Reconciliacao release-volatil pos-merge do lote adicional (6 PRs apos o deploy): 1890->1896 (+6). Drift de condicionais NOVOS de #4327 (per-key USD usage quotas \u2014 apiKeyUsageLimits.ts + validation/policy branches), #4334 (cache-aware compression guard) e #4326 (phaseComboSetup extraido). Medido no tip real da release com `node scripts/check/check-complexity.mjs`. Mesma familia/justificativa do _rebaseline_2026_06_19_lote3_merge_drift abaixo \u2014 feature legitima, nao regressao.",
|
||||
"_rebaseline_2026_06_19_lote3_merge_drift": "Reconciliacao release-volatil pos-merge do lote de 13 PRs (release/v3.8.30): 1888->1890 (+2). Drift de condicionais NOVOS trazidos por #4313 (5 harvested features \u2014 combo allowlist intersection, serviceKind filter) e #4323 (compression e2e \u2014 novos ramos em ultra/aggressive/gcf/strategySelector), merges que entraram DEPOIS do #4318 medir 1888. O fast-path do release nao roda check:complexity (so release->main), entao os ramos acumulam sem rebaselinar. Medido no tip real da release pos-merge com `node scripts/check/check-complexity.mjs`. Mesma familia dos rebaselines anteriores \u2014 crescimento de feature legitimo, nao regressao; reducao fica como debt de refactor dedicado.",
|
||||
"_rebaseline_2026_06_19_bin_scope_wired": "Task 6A.11 (de verdade): o ESLINT_ARGS de check-complexity.mjs passava apenas `src open-sse` \u2014 o config eslint.complexity.config.mjs e este baseline JA documentavam o escopo src+open-sse+electron+bin, mas a edicao do scan nunca tinha sido aplicada (fake-green: o gate alegava cobrir bin/electron e nunca os varria). Agora ESLINT_ARGS passa `src open-sse electron bin`, casando o config. Medido: electron+bin contribuem 0 violacoes (electron so tem types.d.ts, ignorado; os 4 .ts de bin/ estao sob os thresholds) \u2014 o widening e 0-custo. O +1 (1887->1888) NAO vem do widening: e drift pre-existente em src/open-sse trazido pela base release/v3.8.30 a23d0d678 (merges do ciclo, incl. #4308 cache-hit-cost), que o fast-path do release nao rebaselina (check:complexity so roda no release->main). Mesma familia dos rebaselines anteriores \u2014 crescimento de feature legitimo, nao regressao.",
|
||||
"_rebaseline_2026_06_21_v3833_reviewprs_r4": "Reconciliacao release-volatil 1906->1911 (+5) apos o lote /review-prs r4 (5 PRs de contribuidores mergeados em release/v3.8.33: #4557 health-polls, #4556 mobile-table, #4545 provider-wildcard, #4558 isHidden, #4489 sticky-weighted) + 1 merge concorrente de sessao paralela (#4565 quota perf). O fast-path do release nao roda check:complexity (so release->main), entao o ramo acumula sem rebaselinar. Condicionais NOVOS legitimos: #4545 (expandProviderWildcardsInCombo/Collection + wildcardMatch glob/registry branching em providerWildcard.ts/wildcardRouter.ts), #4489 (eligibility pass sticky-weighted: resolveWeightedStepGroups + isTargetSelectableForWeighted + renormalizacao em combo.ts), #4558 (filtro isHidden em buildAutoCandidates/virtualFactory) e #4565 (guardas de skip de quota_snapshots idle). Medido com `node scripts/check/check-complexity.mjs` no tip 39b2bbfea. Mesma familia dos rebaselines anteriores — crescimento de feature legitimo recem-TDD'd, nao regressao; reducao estrutural fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_21_v3833_cycle_open_stranded": "1905 -> 1906 (+1). Abertura do ciclo v3.8.33: o +1 vem dos 4 commits stranded na release/v3.8.32 (pós-merge da v3.8.32) trazidos via cherry-pick para release/v3.8.33 — isolado em #4483 (autoStrategy.ts evaluateQuotaCutoff/quotaPreflight.ts, guardas de quota-cutoff inerentemente ramificadas, opt-in default-OFF). main mede 1905; o .33 branch (main + cherry-picks) mede 1906. Crescimento de feature legitimo recém-mergeado, não regressão; redução fica como debt (#3501).",
|
||||
"_rebaseline_2026_06_20_reviewprs_mine_r2": "Reconciliacao release-volatil 1900->1905 (+5) apos o lote /review-prs 'apenas minhas' r2 (17 PRs meus mergeados em release/v3.8.32). Breakdown medido com `node scripts/check/check-complexity.mjs` + diff por-arquivo eslint complexity JSON entre o tip pre-lote (9052c5a78 = 1902) e o tip pos-lote (1905): (a) +2 JA latentes no tip pre-lote (1900->1902), drift de merges concorrentes de outras sessoes ANTERIORES a este lote (nao introduzidos por mim) que o fast-path do release nao rebaselina (check:complexity so roda release->main); (b) +3 deste lote, isolados em DOIS arquivos: open-sse/translator/helpers/geminiHelper.ts +1 (convertOpenAIContentToParts passou de 80->93 linhas pela branch de audio do #4426 — max-lines-per-function, funcao de dispatch coesa por tipo de content part) e open-sse/translator/response/openai-to-gemini-sse.ts +2 (translator SSE NOVO do #4453, openAIChunkToGeminiChunk + convertOpenAIResponseToGemini em complexity 19 cada — conversores de chunk SSE inerentemente ramificados, levemente acima de 15). Crescimento de feature legitimo recem-TDD'd, nao regressao; refatorar feature recem-mergeada so para raspar +3 seria over-engineering arriscado. Reducao fica como debt.",
|
||||
"_rebaseline_2026_06_20_reviewprs_v3831_batch": "Reconciliacao release-volatil 1896->1900 (+4): drift do lote /review-prs v3.8.31 (25 PRs A+B+C + merges concorrentes). Condicionais NOVOS legitimos — sobretudo #4381 (rotas /api/local/redis/{start,stop,status} detectRuntime + guardas + bifrost relay) e #4366 (classificacao de exhaustion de erro entre os 2 dispatchers de combo). Medido 1900 ESTAVEL em fd1391c0b E f46c69f2a com `node scripts/check/check-complexity.mjs` (commit concorrente intermediario foi complexity-neutro). O fast-path do release nao roda check:complexity (so release->main), entao o ramo acumula sem rebaselinar. Mesma familia dos rebaselines anteriores — crescimento de feature legitimo, nao regressao; valor final reconciliado no release->main.",
|
||||
"_rebaseline_2026_06_20_postlote_concurrent_drift": "Reconciliacao release-volatil: 1895->1896 (+1). Drift de condicional NOVO de PRs mergeados pela sessao concorrente APOS o #4338 ratchetar para 1895 (#4355 pricing gpt-5.x-pro / #4364 cli active-context cred / #4363 compliance cleanup / #4358 mitm mask / #4332 injection-guard-16KB). O fast-path do release nao roda check:complexity (so release->main), entao o ramo acumula sem rebaselinar. Medido com `node scripts/check/check-complexity.mjs` no tip cdfd71c17. Mesma familia — crescimento de feature legitimo, nao regressao.",
|
||||
"_note_2026_06_20_4371_chatcore_heap_leaf": "PR #4371 (extract checkHeapPressureGuard leaf, god-file decomposition start) e complexity-NEUTRO: handleChatCore so PERDE codigo e o novo heapPressure.ts checkHeapPressureGuard fica sob o teto — a contagem permanece 1896 (medido no tip mesclado com check-complexity.mjs).",
|
||||
"_ratchet_2026_06_19_phasecombosetup_fix": "1896->1895 (-1, ratchet DOWN — melhoria, NAO reconciliacao). O #4336 reconciliou o drift do lote para 1896 INCLUINDO a violacao que o #4326 (ComboContext) introduziu: phaseComboSetup media complexity 17 (>15) porque a extracao concentrou os condicionais de pinning/ternarios numa funcao que estourava o teto (irônico p/ uma decomposicao). Este PR CORRIGE na origem — extrai resolveContextCachePin (helper do pinning), phaseComboSetup volta a <15 — baixando a contagem 1. Medido com `node scripts/check/check-complexity.mjs` no tip pos-#4336.",
|
||||
"_rebaseline_2026_06_19_lote3_postdeploy_drift": "Reconciliacao release-volatil pos-merge do lote adicional (6 PRs apos o deploy): 1890->1896 (+6). Drift de condicionais NOVOS de #4327 (per-key USD usage quotas — apiKeyUsageLimits.ts + validation/policy branches), #4334 (cache-aware compression guard) e #4326 (phaseComboSetup extraido). Medido no tip real da release com `node scripts/check/check-complexity.mjs`. Mesma familia/justificativa do _rebaseline_2026_06_19_lote3_merge_drift abaixo — feature legitima, nao regressao.",
|
||||
"_rebaseline_2026_06_19_lote3_merge_drift": "Reconciliacao release-volatil pos-merge do lote de 13 PRs (release/v3.8.30): 1888->1890 (+2). Drift de condicionais NOVOS trazidos por #4313 (5 harvested features — combo allowlist intersection, serviceKind filter) e #4323 (compression e2e — novos ramos em ultra/aggressive/gcf/strategySelector), merges que entraram DEPOIS do #4318 medir 1888. O fast-path do release nao roda check:complexity (so release->main), entao os ramos acumulam sem rebaselinar. Medido no tip real da release pos-merge com `node scripts/check/check-complexity.mjs`. Mesma familia dos rebaselines anteriores — crescimento de feature legitimo, nao regressao; reducao fica como debt de refactor dedicado.",
|
||||
"_rebaseline_2026_06_19_bin_scope_wired": "Task 6A.11 (de verdade): o ESLINT_ARGS de check-complexity.mjs passava apenas `src open-sse` — o config eslint.complexity.config.mjs e este baseline JA documentavam o escopo src+open-sse+electron+bin, mas a edicao do scan nunca tinha sido aplicada (fake-green: o gate alegava cobrir bin/electron e nunca os varria). Agora ESLINT_ARGS passa `src open-sse electron bin`, casando o config. Medido: electron+bin contribuem 0 violacoes (electron so tem types.d.ts, ignorado; os 4 .ts de bin/ estao sob os thresholds) — o widening e 0-custo. O +1 (1887->1888) NAO vem do widening: e drift pre-existente em src/open-sse trazido pela base release/v3.8.30 a23d0d678 (merges do ciclo, incl. #4308 cache-hit-cost), que o fast-path do release nao rebaselina (check:complexity so roda no release->main). Mesma familia dos rebaselines anteriores — crescimento de feature legitimo, nao regressao.",
|
||||
"_rebaseline_2026_06_19_4293_codex_spark_scope": "PR #4293 (isolate Codex Spark quota scope): +2 over the v3.8.30 baseline (1885->1887). Measured on the actual merged tree (release/v3.8.30 + #4293), not the PR's own estimate. The thin requestedModel-scoped Codex quota headroom/preflight branches needed so GPT-5.3-Codex-Spark and normal Codex are evaluated independently add the new conditional cost; heavy parsing/display logic was extracted to leaf helpers under the cap (codexQuotaScopes.ts, codexUsageQuotas.ts, codexFailover.ts). Legitimate feature growth, not regression; structural shrink remains debt.",
|
||||
"_rebaseline_2026_06_19_v3830": "Re-baseline consciente: drift 1800->1885 (+85) do ciclo v3.8.25->v3.8.29 (round-9, ~130 PRs: combo split D7/D8, chatCore split, novos providers/modelos, cost-telemetry, MITM decrypt, remote-mode CLI). Medido no tip release/v3.8.30 (3e6be4701). Mesma familia dos re-baselines anteriores \u2014 crescimento de feature legitima, nao regressao. Reducao fica como debt de refactor dedicado.",
|
||||
"_rebaseline_2026_06_13_v3825": "Re-baseline consciente: drift 1794->1800 (+6) do ciclo v3.8.24->v3.8.25 (features #3799-#3806). Mesma familia dos re-baselines anteriores \u2014 crescimento de feature legitima, nao regressao. Reducao fica como debt de refactor dedicado.",
|
||||
"_rebaseline_2026_06_10": "Re-baseline consciente: 1739 foi medido na branch das Fases 0-6 (base ~v3.8.17); a v3.8.18 publicada ja carrega 1746 (provado: o commit-base 5f2722bd6, anterior a qualquer commit do ciclo v3.8.19, mede 1746 \u2014 funcoes complexas dos reworks RequestLoggerV2/stream/combo). Mesma familia dos re-baselines de eslintWarnings/file-size. Reducao = Fase 6A (2026-06-16).",
|
||||
"_rebaseline_2026_06_13_6a11": "Re-baseline consciente Task 6A.11: escopo ampliado para src+open-sse+electron+bin (electron/bin contribuem 0 violacoes novas \u2014 todos os 4 arquivos .ts em bin/ estao abaixo dos thresholds). Drift 1746\u21921794 pre-existente de features mergeadas nos ciclos v3.8.22/v3.8.23 (nao causado por esta task). Congelado no valor real medido para destrancar o gate.",
|
||||
"_rebaseline_2026_06_26_v3837_release": "1950->1963 (+13). v3.8.37 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR->release fast-gates, so warnings/complexity accrued unmeasured across this cycle's 76 commits \u2014 provider adds DGrid/Pioneer/xAI, headroom proxy lifecycle #4649, ~50 SSE/translator fixes, Engine Combos #5062). Trust-but-verify: this release-finalize working tree touches ONLY CHANGELOG.md, docs/i18n/*/CHANGELOG.md mirrors, and these baselines \u2014 0 production-code change, so all drift is inherited cycle drift (`any` warn-allowed in open-sse/ + tests/). Tighten via --require-tighten next cycle.",
|
||||
"_rebaseline_2026_07_06_v3845_release_close": "2028->2035 (+7). v3.8.45 release close (generate-release Phase 0): drift herdado do merge burst final do ciclo (#6216 streaming fixes, #6251/#6253 dashboard UX, #6292 zero-width, fixes do pre-flight ce897453 \u2014 todos test/config/workflow-neutros em complexidade nova, verificado pelo validador no tip 5ecca12aa5). Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_07_6552_chirag_api_models_filter": "2050->2052 (+2). PR #6552 (@chirag127, #6328): hidePaidModels filter across the 4 dashboard /api/models endpoints adds 2 functions over the complexity threshold. Owner-approved rebaseline (contributor own-growth). Tighten via --update next cycle."
|
||||
}
|
||||
"_rebaseline_2026_06_19_v3830": "Re-baseline consciente: drift 1800->1885 (+85) do ciclo v3.8.25->v3.8.29 (round-9, ~130 PRs: combo split D7/D8, chatCore split, novos providers/modelos, cost-telemetry, MITM decrypt, remote-mode CLI). Medido no tip release/v3.8.30 (3e6be4701). Mesma familia dos re-baselines anteriores — crescimento de feature legitima, nao regressao. Reducao fica como debt de refactor dedicado.",
|
||||
"_rebaseline_2026_06_13_v3825": "Re-baseline consciente: drift 1794->1800 (+6) do ciclo v3.8.24->v3.8.25 (features #3799-#3806). Mesma familia dos re-baselines anteriores — crescimento de feature legitima, nao regressao. Reducao fica como debt de refactor dedicado.",
|
||||
"_rebaseline_2026_06_10": "Re-baseline consciente: 1739 foi medido na branch das Fases 0-6 (base ~v3.8.17); a v3.8.18 publicada ja carrega 1746 (provado: o commit-base 5f2722bd6, anterior a qualquer commit do ciclo v3.8.19, mede 1746 — funcoes complexas dos reworks RequestLoggerV2/stream/combo). Mesma familia dos re-baselines de eslintWarnings/file-size. Reducao = Fase 6A (2026-06-16).",
|
||||
"_rebaseline_2026_06_13_6a11": "Re-baseline consciente Task 6A.11: escopo ampliado para src+open-sse+electron+bin (electron/bin contribuem 0 violacoes novas — todos os 4 arquivos .ts em bin/ estao abaixo dos thresholds). Drift 1746→1794 pre-existente de features mergeadas nos ciclos v3.8.22/v3.8.23 (nao causado por esta task). Congelado no valor real medido para destrancar o gate.",
|
||||
"_rebaseline_2026_06_26_v3837_release": "1950->1963 (+13). v3.8.37 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR->release fast-gates, so warnings/complexity accrued unmeasured across this cycle's 76 commits — provider adds DGrid/Pioneer/xAI, headroom proxy lifecycle #4649, ~50 SSE/translator fixes, Engine Combos #5062). Trust-but-verify: this release-finalize working tree touches ONLY CHANGELOG.md, docs/i18n/*/CHANGELOG.md mirrors, and these baselines — 0 production-code change, so all drift is inherited cycle drift (`any` warn-allowed in open-sse/ + tests/). Tighten via --require-tighten next cycle.",
|
||||
"_rebaseline_2026_07_06_v3845_release_close": "2028->2035 (+7). v3.8.45 release close (generate-release Phase 0): drift herdado do merge burst final do ciclo (#6216 streaming fixes, #6251/#6253 dashboard UX, #6292 zero-width, fixes do pre-flight ce897453 — todos test/config/workflow-neutros em complexidade nova, verificado pelo validador no tip 5ecca12aa5). Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_07_6552_chirag_api_models_filter": "2050->2052 (+2). PR #6552 (@chirag127, #6328): hidePaidModels filter across the 4 dashboard /api/models endpoints adds 2 functions over the complexity threshold. Owner-approved rebaseline (contributor own-growth). Tighten via --update next cycle.",
|
||||
"_relax_velocity_2026_08_30": "Velocity phase (20% relaxation, until 4.0.0): count 2681 → 3218"
|
||||
}
|
||||
|
||||
@@ -1,209 +1,210 @@
|
||||
{
|
||||
"src/app/(dashboard)/dashboard/HomePageClient.tsx": {
|
||||
"TS2339": 10
|
||||
"TS2339": 12
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/agent-skills/AgentSkillsPageClient.tsx": {
|
||||
"TS2503": 3
|
||||
"TS2503": 4
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/agent-skills/components/CoverageBar.tsx": {
|
||||
"TS2503": 1
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/agent-skills/components/McpA2aLinksBar.tsx": {
|
||||
"TS2503": 2
|
||||
"TS2503": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/agent-skills/components/SkillCard.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/agent-skills/components/SkillPreviewPane.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/agent-skills/components/SkillPreviewPane.tsx": {
|
||||
"TS2503": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/cache/__tests__/CachePage.test.tsx": {
|
||||
"TS2305": 3,
|
||||
"TS1117": 1
|
||||
"TS2305": 4,
|
||||
"TS1117": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/cache/__tests__/CachePerformance.test.tsx": {
|
||||
"TS2305": 1,
|
||||
"TS2322": 2
|
||||
"TS2305": 2,
|
||||
"TS2322": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/cache/__tests__/CacheTrends.test.tsx": {
|
||||
"TS2305": 1,
|
||||
"TS2322": 6
|
||||
"TS2305": 2,
|
||||
"TS2322": 8
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/cache/__tests__/IdempotencyLayer.test.tsx": {
|
||||
"TS2305": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/cache/__tests__/MemoryCards.test.tsx": {
|
||||
"TS2305": 1,
|
||||
"TS2322": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/cache/components/CachePerformance.tsx": {
|
||||
"TS2339": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/cli-code/components/ClaudeToolCard.tsx": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx": {
|
||||
"TS2345": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/cli-code/components/CustomCliCard.tsx": {
|
||||
"TS2345": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/cli-code/components/DroidToolCard.tsx": {
|
||||
"TS2554": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/combos/page.tsx": {
|
||||
"TS2339": 4,
|
||||
"TS2345": 5,
|
||||
"TS2698": 1,
|
||||
"TS2322": 13
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/compression/studio/EncoderComparisonTable.tsx": {
|
||||
"TS2322": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/costs/quota-share/QuotaSharePageClient.tsx": {
|
||||
"TS2551": 7,
|
||||
"TS2322": 2,
|
||||
"TS2719": 2,
|
||||
"TS2739": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/costs/quota-share/components/StackedAllocationBar.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/costs/quota-share/components/UsageLogCard.tsx": {
|
||||
"TS2869": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/discovery/__tests__/DiscoveryPageClient.test.tsx": {
|
||||
"TS2305": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": {
|
||||
"TS2322": 18
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/memory/components/tabs/MemoriesTab.tsx": {
|
||||
"TS2322": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/OmniSkillsPageClient.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/OmniExecutionsTab.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/OmniMarketplaceTab.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/OmniSandboxTab.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/OmniSkillCard.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/OmniSkillsList.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/SkillInspectorPane.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/playground/components/PresetPicker.tsx": {
|
||||
"TS2352": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/playground/components/ToolsBuilder.tsx": {
|
||||
"TS2339": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx": {
|
||||
"TS2322": 4
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/__tests__/phase1e.test.tsx": {
|
||||
"TS2741": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleModelsSection.tsx": {
|
||||
"TS2741": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionsListPanel.tsx": {
|
||||
"src/app/(dashboard)/dashboard/cache/__tests__/MemoryCards.test.tsx": {
|
||||
"TS2305": 2,
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/ProviderModalsPanel.tsx": {
|
||||
"TS2322": 3,
|
||||
"TS2739": 1,
|
||||
"TS2345": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/ProviderModelsSection.tsx": {
|
||||
"TS2322": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/ProviderParamFilterSection.tsx": {
|
||||
"TS2339": 6
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/ProviderPlaygroundPanel.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useModelVisibilityHandlers.ts": {
|
||||
"TS2339": 15
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/components/onboarding/providerOnboardingCatalog.ts": {
|
||||
"TS2339": 4,
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/providerPageUtils.ts": {
|
||||
"TS2345": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/quota/page.tsx": {
|
||||
"TS2339": 4
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": {
|
||||
"TS2304": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx": {
|
||||
"TS2339": 4
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/settings/components/RedisLauncherPanel.tsx": {
|
||||
"TS2345": 11
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/tools/traffic-inspector/components/CustomHostsManager.tsx": {
|
||||
"TS2339": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/tools/traffic-inspector/components/tabs/ResponseBodyTab.tsx": {
|
||||
"TS2339": 5
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/translator/components/MonitorTab.tsx": {
|
||||
"TS2345": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/translator/components/advanced/StreamTransformerAccordion.tsx": {
|
||||
"TS2345": 1
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx": {
|
||||
"src/app/(dashboard)/dashboard/cache/components/CachePerformance.tsx": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/parts/QuotaEnvGroup.tsx": {
|
||||
"TS2739": 1
|
||||
"src/app/(dashboard)/dashboard/cli-code/components/ClaudeToolCard.tsx": {
|
||||
"TS2339": 3
|
||||
},
|
||||
"src/lib/combos/builderDraft.ts": {
|
||||
"TS2741": 1
|
||||
"src/app/(dashboard)/dashboard/cli-code/components/CodexToolCard.tsx": {
|
||||
"TS2345": 4
|
||||
},
|
||||
"src/lib/services/htmlRewriter.ts": {
|
||||
"TS2322": 2,
|
||||
"src/app/(dashboard)/dashboard/cli-code/components/CustomCliCard.tsx": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/mitm/inspector/sseMerger.ts": {
|
||||
"TS2352": 1
|
||||
"src/app/(dashboard)/dashboard/cli-code/components/DroidToolCard.tsx": {
|
||||
"TS2554": 3
|
||||
},
|
||||
"src/shared/components/Header.tsx": {
|
||||
"TS2353": 1
|
||||
"src/app/(dashboard)/dashboard/combos/page.tsx": {
|
||||
"TS2339": 5,
|
||||
"TS2345": 6,
|
||||
"TS2698": 2,
|
||||
"TS2322": 16
|
||||
},
|
||||
"src/shared/components/OAuthModal.tsx": {
|
||||
"TS2769": 3,
|
||||
"TS2345": 3
|
||||
"src/app/(dashboard)/dashboard/compression/studio/EncoderComparisonTable.tsx": {
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/shared/components/SkillsConceptCard.tsx": {
|
||||
"TS2503": 1
|
||||
},
|
||||
"src/shared/components/analytics/charts.tsx": {
|
||||
"TS2345": 1
|
||||
},
|
||||
"src/shared/components/analytics/rechartsDonuts.tsx": {
|
||||
"src/app/(dashboard)/dashboard/costs/quota-share/QuotaSharePageClient.tsx": {
|
||||
"TS2551": 9,
|
||||
"TS2322": 3,
|
||||
"TS2719": 3,
|
||||
"TS2739": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/costs/quota-share/components/StackedAllocationBar.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/costs/quota-share/components/UsageLogCard.tsx": {
|
||||
"TS2869": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/discovery/__tests__/DiscoveryPageClient.test.tsx": {
|
||||
"TS2305": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": {
|
||||
"TS2322": 22
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/memory/components/tabs/MemoriesTab.tsx": {
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/OmniSkillsPageClient.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/OmniExecutionsTab.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/OmniMarketplaceTab.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/OmniSandboxTab.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/OmniSkillCard.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/OmniSkillsList.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/omni-skills/components/SkillInspectorPane.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/playground/components/PresetPicker.tsx": {
|
||||
"TS2352": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/playground/components/ToolsBuilder.tsx": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx": {
|
||||
"TS2322": 5
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/__tests__/phase1e.test.tsx": {
|
||||
"TS2741": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/CompatibleModelsSection.tsx": {
|
||||
"TS2741": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/ConnectionsListPanel.tsx": {
|
||||
"TS2322": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/ProviderModalsPanel.tsx": {
|
||||
"TS2322": 4,
|
||||
"TS2739": 2,
|
||||
"TS2345": 4
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/ProviderModelsSection.tsx": {
|
||||
"TS2322": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/ProviderParamFilterSection.tsx": {
|
||||
"TS2339": 8
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/ProviderPlaygroundPanel.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useModelVisibilityHandlers.ts": {
|
||||
"TS2339": 18
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/components/onboarding/providerOnboardingCatalog.ts": {
|
||||
"TS2339": 5,
|
||||
"TS2345": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/providerPageUtils.ts": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/quota/page.tsx": {
|
||||
"TS2339": 5
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": {
|
||||
"TS2304": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx": {
|
||||
"TS2339": 5
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/settings/components/RedisLauncherPanel.tsx": {
|
||||
"TS2345": 14
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/tools/traffic-inspector/components/CustomHostsManager.tsx": {
|
||||
"TS2339": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/tools/traffic-inspector/components/tabs/ResponseBodyTab.tsx": {
|
||||
"TS2339": 6
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/translator/components/MonitorTab.tsx": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/translator/components/advanced/StreamTransformerAccordion.tsx": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx": {
|
||||
"TS2339": 3
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/parts/QuotaEnvGroup.tsx": {
|
||||
"TS2739": 2
|
||||
},
|
||||
"src/lib/combos/builderDraft.ts": {
|
||||
"TS2741": 2
|
||||
},
|
||||
"src/lib/services/htmlRewriter.ts": {
|
||||
"TS2322": 3,
|
||||
"TS2345": 3
|
||||
},
|
||||
"src/mitm/inspector/sseMerger.ts": {
|
||||
"TS2352": 2
|
||||
},
|
||||
"src/shared/components/Header.tsx": {
|
||||
"TS2353": 2
|
||||
},
|
||||
"src/shared/components/OAuthModal.tsx": {
|
||||
"TS2769": 4,
|
||||
"TS2345": 4
|
||||
},
|
||||
"src/shared/components/SkillsConceptCard.tsx": {
|
||||
"TS2503": 2
|
||||
},
|
||||
"src/shared/components/analytics/charts.tsx": {
|
||||
"TS2345": 2
|
||||
},
|
||||
"src/shared/components/analytics/rechartsDonuts.tsx": {
|
||||
"TS2739": 3
|
||||
},
|
||||
"src/shared/hooks/useElectron.ts": {
|
||||
"TS2339": 19
|
||||
"TS2339": 23
|
||||
},
|
||||
"src/shared/schemas/cliCatalog.ts": {
|
||||
"TS2554": 2
|
||||
}
|
||||
"TS2554": 3
|
||||
},
|
||||
"_relax_velocity_2026_08_30": "per-file TS diagnostic counts raised by 20% (229 → 324); velocity phase, see quality-baseline.json _policy."
|
||||
}
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
"react",
|
||||
"react-dom",
|
||||
"react-is",
|
||||
"react18-json-view",
|
||||
"react-markdown",
|
||||
"react-reconciler",
|
||||
"recharts",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"_comment": "Catraca de duplicacao (check-duplication.mjs, jscpd@4 sobre src+open-sse, min-tokens 50). So pode cair. --update ratcheta.",
|
||||
"percentage": 5.72
|
||||
"percentage": 6.86,
|
||||
"_relax_velocity_2026_08_30": "Velocity phase (20% relaxation, until 4.0.0): percentage 5.72 → 6.86"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"_rebaseline_2026_08_20_10531_freebuff_provider": "PR #10531 (adrianaryaputra, feat/freebuff-provider-support, closes #6793) own growth: src/shared/constants/providers/apikey/gateways.ts 1283->1298 (+15, the freebuff APIKEY_PROVIDERS_GATEWAYS catalog entry, additive data at the existing registry chokepoint, same god-file no-split rationale as prior gateways.ts rebaselines) and src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx 1062->1067 (+5, freebuff credential placeholder/hint at the existing per-provider switch chokepoint). Covered by tests/unit/freebuff-provider.test.ts (9/9 passing).",
|
||||
"_rebaseline_2026_08_31_12212_openapi_generated": "PR #12212 (docs audit follow-up nº 3): src/app/docs/lib/openapi.generated.ts 171->1347 — the module is emitted by scripts/docs/gen-openapi-module.mjs from docs/openapi.yaml, and the spec now documents all 692 implemented routes (was 276), so the generated output grew with the spec. Frozen at the generator output size; shrink by slimming the spec, never by hand-editing the generated module. Covered by tests/unit/openapi-security-tiers.test.ts (6/6) and the check:api-docs-refs gate (692/692 paths with a real route).",
|
||||
"_rebaseline_2026_08_21_10987_logfare_provider": "PR #10987 (jonlwheat2-gif, feat/10644-logfare-provider, closes #10644) own growth: src/shared/constants/providers/apikey/gateways.ts 1298->1321 (+23, the logfare APIKEY_PROVIDERS_GATEWAYS catalog entry with Free badge/freeNote/apiHint documenting the request-logging policy, additive data at the existing registry chokepoint, same god-file no-split rationale as the prior gateways.ts rebaselines: #10531 freebuff, merge-storm 2026-08-11). Covered by tests/unit/logfare-registry.test.ts (1/1 passing).",
|
||||
"_rebaseline_2026_08_20_10574_reasoning_transport_fallback": "PR #10574 (jackjinke, fix/responses-reasoning-transport, fixes #10550) own growth: src/sse/handlers/chatHelpers.ts 1017->1019 (+2 = the new reasoningTransportFallback option threaded through executeChatWithBreaker's options destructure and its downstream handleSingleModel call, at the existing per-attempt options-passthrough chokepoint; not extractable without splitting the option-forwarding call itself). Covered by the PR's own reasoning-policy test suite (tests/unit/chatcore-translation-paths.test.ts, tests/unit/combo-attempt-body-isolation-7847.test.ts, tests/unit/reasoning-cache.test.ts, tests/unit/strip-reasoning-blobs-agentic-context-1599.test.ts among others), 446/446 focused tests passing.",
|
||||
"_rebaseline_2026_08_18_10517_zed_hosted_oauth_callback_port": "PR #10517 (phatchau036, fix/zed-hosted-oauth-callback-port) own growth: src/shared/components/OAuthModal.tsx 1131->1148 (wc -l; check-file-size.mjs counts via split(\"\\n\").length so the gate sees 1134->1149, +15/+18, crosses the frozen 1134 cap). Wires the zed-hosted native-app callback auto-complete: forceManual gating on isTrueLocalhost for zed-hosted, the loopback-redirect-URI comment block, and the exchangeToken full-URL-as-code branch, all at the existing provider-switch chokepoints this modal already carries growth for (seventh bump: 969->989->993->998->1030->1056->1100->1149; structural shrink tracked in #3501). The actual port-derivation logic lives in src/lib/oauth/providers/zed-hosted.ts (not frozen here) and was hardened during pre-merge review to use the server's own getRuntimePorts() instead of a browser-guessed scheme/port, covered by the new tests/unit/zed-hosted-loopback-port-derivation.test.ts (8/8 passing).",
|
||||
@@ -179,8 +180,8 @@
|
||||
"_rebaseline_2026_07_18_6979_codex_test": "PR #6979 own growth: executor-codex.test.ts 1340->1347 (+7 = generalized ensureThinkingBudget assertion added to the existing codex thinking-budget cases). antigravity-test bump 942->977 REVERTED here: #7408's test split dropped that file to 888, so this PR's +35 fits under the original 942 frozen cap.",
|
||||
"_rebaseline_2026_07_24_8354_logs_timeline_sidebar": "PR #8354 (hartmark, feature/scrolling-log) own growth: src/shared/constants/sidebarVisibility/sections.ts 812->820 (+8, the single new logs-timeline SidebarItemDefinition entry added to LOGS_GROUP.items for the new /dashboard/logs/timeline scrolling request-timeline page). Irreducible data-literal wiring at the existing sidebar-sections chokepoint, same shape as every other item in the file; not extractable without an ad-hoc single-item exception to the file's otherwise-uniform multi-line item style.",
|
||||
"_rebaseline_2026_08_09_v3850_post_sweep_tip": "Release-captain reconciliation of absolute file-size drift on pure tip 382449d593 after the authorized cherry-pick wave. The affected production growth already belongs to merged, tested commits: Adobe Firefly CDP/session recovery (#9881), model capability serialization (#9296), Modality Bridge request wiring (#9759), disconnect-grace/reasoning-cache chatCore wiring (#9653/#9183), stacked Lite precedence, and Responses tool-call index/argument handling (#9843 plus the release translator fixes). This repair adds only the compact migration-146 retroactive guard, covered by db-job-registry-migration-renumber-139.test.ts. Values are the exact check:file-size split-newline measurements and remain shrink-only; structural decomposition remains tracked by the existing #3501 notes.",
|
||||
"cap": 1000,
|
||||
"testCap": 1000,
|
||||
"cap": 1200,
|
||||
"testCap": 1200,
|
||||
"testFrozen": {
|
||||
"_rebaseline_2026_06_27_5193_antigravity_test": "#5193 own test growth: oauth-providers-config.test.ts 870->873 (+3: antigravity projectId assertion + 50ms tick for the now fire-and-forget onboarding, matching the no-PKCE/no-openid flow).",
|
||||
"_rebaseline_2026_07_02_5928_base_red": "web-cookie-providers-new.test.ts 845->850: #5928 (test(security) Kimi Web URL host parse, CodeQL #689) grew the file +5 lines and merged into release/v3.8.44 WITHOUT rebaselining, leaving a fast-gates base-red that blocked every subsequent PR->release. Test growth is legitimate (a security regression test); maintainer absorbs the drift here. Frozen at 850.",
|
||||
@@ -194,43 +195,43 @@
|
||||
"_rebaseline_2026_08_24_video_bridge_fu01_fu03_fu04_result_cache_tests": "PRs #11362 (FU-01 cache hardening) + #11382 (FU-03 visual dedup policy identity) + #11383 (FU-04 focused analysis mode) own test growth: videoBridgeResultCache.test.ts <1000->1040, +40 (sum of three stacked PRs boarded together in the same merge-batch, each adding its own cache-identity assertions on the shared result-cache seam). Owner pre-authorized rebaseline for legitimate PR growth (2026-08-19 directive).",
|
||||
"_rebaseline_basered_codebuddy_cn": "Base-red fix (#4664 CodeBuddy CN): oauth-providers-config.test.ts 867->870 (+3) to align the EXPECTED provider list/config with the codebuddy-cn provider that #4664 added to the registry without updating this test (it asserts 'exactly once').",
|
||||
"_rebaseline_pr4613_compatible_provider_groups": "Reconcile #4613 already-merged growth: providers-page-utils.test.ts 1004->1052 (+48, buildCompatibleProviderGroups partition unit test). Fast-gate PR->release does not run check:file-size, so this surfaced post-merge.",
|
||||
"tests/integration/chat-pipeline.test.ts": 2077,
|
||||
"tests/integration/chatcore-compression-integration.test.ts": 1448,
|
||||
"tests/integration/skills-pipeline.test.ts": 1009,
|
||||
"tests/unit/account-fallback-service.test.ts": 2032,
|
||||
"tests/unit/adobe-firefly.test.ts": 1477,
|
||||
"tests/unit/batch_api.test.ts": 1721,
|
||||
"tests/unit/cc-compatible-provider.test.ts": 1582,
|
||||
"tests/unit/chatcore-translation-paths.test.ts": 3739,
|
||||
"tests/unit/combo-routing-engine.test.ts": 4494,
|
||||
"tests/unit/db-migration-runner.test.ts": 1949,
|
||||
"tests/unit/deepseek-web.test.ts": 1420,
|
||||
"tests/unit/executor-antigravity.test.ts": 1427,
|
||||
"tests/unit/executor-codex.test.ts": 1741,
|
||||
"tests/unit/executor-default-base.test.ts": 1975,
|
||||
"tests/unit/grok-web.test.ts": 3168,
|
||||
"tests/unit/image-generation-handler.test.ts": 2638,
|
||||
"tests/unit/model-sync-route.test.ts": 1321,
|
||||
"tests/unit/models-catalog-route.test.ts": 2127,
|
||||
"tests/unit/perplexity-web.test.ts": 1762,
|
||||
"tests/unit/provider-models-route.test.ts": 2323,
|
||||
"tests/unit/provider-validation-specialty.test.ts": 3880,
|
||||
"tests/unit/providers-page-utils.test.ts": 1438,
|
||||
"tests/unit/response-sanitizer.test.ts": 1382,
|
||||
"tests/unit/route-edge-coverage.test.ts": 1613,
|
||||
"tests/unit/search-handler-extended.test.ts": 1392,
|
||||
"tests/unit/sse-auth.test.ts": 2093,
|
||||
"tests/unit/stream-utils.test.ts": 3178,
|
||||
"tests/unit/token-refresh-service.test.ts": 1791,
|
||||
"tests/unit/translator-openai-responses-req.test.ts": 1552,
|
||||
"tests/unit/translator-openai-to-gemini.test.ts": 2109,
|
||||
"tests/unit/translator-openai-to-kiro.test.ts": 1658,
|
||||
"tests/unit/translator-resp-gemini-to-openai.test.ts": 1604,
|
||||
"tests/unit/usage-service-hardening.test.ts": 1928,
|
||||
"tests/unit/vscode-token-routes.test.ts": 1633,
|
||||
"tests/unit/guardrails/videoBridgeResultCache.test.ts": 1040,
|
||||
"tests/unit/reasoning-cache.test.ts": 1346,
|
||||
"tests/unit/chatgpt-web.test.ts": 4092
|
||||
"tests/integration/chat-pipeline.test.ts": 2493,
|
||||
"tests/integration/chatcore-compression-integration.test.ts": 1738,
|
||||
"tests/integration/skills-pipeline.test.ts": 1211,
|
||||
"tests/unit/account-fallback-service.test.ts": 2439,
|
||||
"tests/unit/adobe-firefly.test.ts": 1773,
|
||||
"tests/unit/batch_api.test.ts": 2066,
|
||||
"tests/unit/cc-compatible-provider.test.ts": 1899,
|
||||
"tests/unit/chatcore-translation-paths.test.ts": 4487,
|
||||
"tests/unit/combo-routing-engine.test.ts": 5393,
|
||||
"tests/unit/db-migration-runner.test.ts": 2339,
|
||||
"tests/unit/deepseek-web.test.ts": 1704,
|
||||
"tests/unit/executor-antigravity.test.ts": 1713,
|
||||
"tests/unit/executor-codex.test.ts": 2090,
|
||||
"tests/unit/executor-default-base.test.ts": 2370,
|
||||
"tests/unit/grok-web.test.ts": 3802,
|
||||
"tests/unit/image-generation-handler.test.ts": 3166,
|
||||
"tests/unit/model-sync-route.test.ts": 1586,
|
||||
"tests/unit/models-catalog-route.test.ts": 2553,
|
||||
"tests/unit/perplexity-web.test.ts": 2115,
|
||||
"tests/unit/provider-models-route.test.ts": 2788,
|
||||
"tests/unit/provider-validation-specialty.test.ts": 4656,
|
||||
"tests/unit/providers-page-utils.test.ts": 1726,
|
||||
"tests/unit/response-sanitizer.test.ts": 1659,
|
||||
"tests/unit/route-edge-coverage.test.ts": 1936,
|
||||
"tests/unit/search-handler-extended.test.ts": 1671,
|
||||
"tests/unit/sse-auth.test.ts": 2512,
|
||||
"tests/unit/stream-utils.test.ts": 3814,
|
||||
"tests/unit/token-refresh-service.test.ts": 2150,
|
||||
"tests/unit/translator-openai-responses-req.test.ts": 1863,
|
||||
"tests/unit/translator-openai-to-gemini.test.ts": 2531,
|
||||
"tests/unit/translator-openai-to-kiro.test.ts": 1990,
|
||||
"tests/unit/translator-resp-gemini-to-openai.test.ts": 1925,
|
||||
"tests/unit/usage-service-hardening.test.ts": 2314,
|
||||
"tests/unit/vscode-token-routes.test.ts": 1960,
|
||||
"tests/unit/guardrails/videoBridgeResultCache.test.ts": 1248,
|
||||
"tests/unit/reasoning-cache.test.ts": 1616,
|
||||
"tests/unit/chatgpt-web.test.ts": 4911
|
||||
},
|
||||
"_rebaseline_2026_06_09": "Re-baseline consciente pre-release v3.8.19: 9 arquivos cresceram durante o ciclo (features mergeadas: RequestLoggerV2 +281 request-logger rework, stream +101, combo +73, chatCore +45, catalog +32 fable-5/catalog-flag, callLogs +4, accountFallback +2, usageHistory novo 840) + core.ts +7 (fix resetAllDbModuleState, PR 3536). A catraca segue valendo destes valores — proximo crescimento falha. Decisao: encolher (esp. RequestLoggerV2/chatCore) e a issue #3501 ficam para o ciclo seguinte.",
|
||||
"_rebaseline_2026_06_11_phase1f": "Phase 1f (#3501): ProviderDetailPageClient.tsx 4948→4062 (-886 LOC); 3 novos hooks extraídos. useProviderConnections.ts=954 acima do cap=800 — justificado: extração direta do god-component (zero lógica nova), própria redução do cliente supera o custo. useProviderSettings.ts=263 e useProviderModels.ts=154 já abaixo do cap.",
|
||||
@@ -367,84 +368,84 @@
|
||||
"_rebaseline_2026_07_25_adobe_firefly_reference_images": "Follow-up to #8006: storage upload + referenceBlobs for image/video and /v1/images/edits dispatch. adobeFireflyClient.ts 1958->2317 (+upload helpers, extract sources, resolve blob ids). Note: 2317 not 2316 — check-file-size.mjs counts LOC via split(\"\\n\").length (counts the trailing-newline empty element), which is 1 higher than `wc -l` on a file ending in \\n; the PR's original entry (2316) was measured with wc -l and undercounted by 1 against the actual gate.",
|
||||
"_rebaseline_pr1043_minimax_tts": "Upstream port decolua/9router#1043 (toanalien) own growth: audioSpeech.ts 965->1061 (+96). Adds MiniMax T2A v2 TTS dispatch (handleMinimaxSpeech + hexToBytes helper) — provider entry was already in audioRegistry (format: minimax-tts) but no handler existed, falling through to the OpenAI-compatible default that fails (T2A has custom shape + hex-encoded audio + base_resp envelope). New branch sits next to the other inline provider branches (xiaomi-mimo, coqui, tortoise, aws-polly) — extracting would just create indirection. Covered by tests/unit/minimax-tts-1043.test.ts (3 tests, GREEN: success, base_resp error, invalid-hex).",
|
||||
"_rebaseline_pr4592_exclude_exhausted_auto": "Reconcile #4592 already-merged growth: combo.ts 2991->3036 (+45, terminal-status quota-cutoff exclusion in buildAutoCandidates + opt-in gate). Fast-gate PR->release does not run check:file-size.",
|
||||
"open-sse/executors/antigravity.ts": 1986,
|
||||
"open-sse/executors/base.ts": 2132,
|
||||
"open-sse/executors/codex.ts": 2031,
|
||||
"open-sse/executors/cursor.ts": 2032,
|
||||
"open-sse/executors/deepseek-web.ts": 1492,
|
||||
"open-sse/executors/grok-web.ts": 1357,
|
||||
"open-sse/executors/muse-spark-web.ts": 1826,
|
||||
"open-sse/handlers/chatCore.ts": 6579,
|
||||
"open-sse/handlers/imageGeneration.ts": 4031,
|
||||
"open-sse/handlers/responseSanitizer.ts": 1466,
|
||||
"open-sse/handlers/search.ts": 1997,
|
||||
"open-sse/handlers/videoGeneration.ts": 1382,
|
||||
"open-sse/mcp-server/schemas/tools.ts": 2019,
|
||||
"open-sse/mcp-server/server.ts": 1882,
|
||||
"open-sse/mcp-server/tools/advancedTools.ts": 1456,
|
||||
"open-sse/services/accountFallback.ts": 2571,
|
||||
"open-sse/services/adobeFireflyBrowserLogin.ts": 1771,
|
||||
"open-sse/services/adobeFireflyClient.ts": 3899,
|
||||
"open-sse/services/adobeFireflySession.ts": 1304,
|
||||
"open-sse/services/claudeCodeCompatible.ts": 1563,
|
||||
"open-sse/services/combo.ts": 4742,
|
||||
"open-sse/services/compression/strategySelector.ts": 1379,
|
||||
"open-sse/services/compression/engines/ccr/index.ts": 1024,
|
||||
"open-sse/executors/antigravity.ts": 2384,
|
||||
"open-sse/executors/base.ts": 2559,
|
||||
"open-sse/executors/codex.ts": 2438,
|
||||
"open-sse/executors/cursor.ts": 2439,
|
||||
"open-sse/executors/deepseek-web.ts": 1791,
|
||||
"open-sse/executors/grok-web.ts": 1629,
|
||||
"open-sse/executors/muse-spark-web.ts": 2192,
|
||||
"open-sse/handlers/chatCore.ts": 7895,
|
||||
"open-sse/handlers/imageGeneration.ts": 4838,
|
||||
"open-sse/handlers/responseSanitizer.ts": 1760,
|
||||
"open-sse/handlers/search.ts": 2397,
|
||||
"open-sse/handlers/videoGeneration.ts": 1659,
|
||||
"open-sse/mcp-server/schemas/tools.ts": 2423,
|
||||
"open-sse/mcp-server/server.ts": 2259,
|
||||
"open-sse/mcp-server/tools/advancedTools.ts": 1748,
|
||||
"open-sse/services/accountFallback.ts": 3086,
|
||||
"open-sse/services/adobeFireflyBrowserLogin.ts": 2126,
|
||||
"open-sse/services/adobeFireflyClient.ts": 4679,
|
||||
"open-sse/services/adobeFireflySession.ts": 1565,
|
||||
"open-sse/services/claudeCodeCompatible.ts": 1876,
|
||||
"open-sse/services/combo.ts": 5691,
|
||||
"open-sse/services/compression/strategySelector.ts": 1655,
|
||||
"open-sse/services/compression/engines/ccr/index.ts": 1229,
|
||||
"_rebaseline_2026_08_22_11084_ccr_caller_gate": "PR #11084 (HouMinXi) own growth: open-sse/services/compression/engines/ccr/index.ts 1000->1024 (first listing — the engine was unlisted and drifted just over the 1000 cap; +24 are the callerSupportsCcrRetrieve gate that skips replacement entirely for callers without the retrieve tool, closing the stranded-prompt incident measured in production). Covered by tests/unit/compression/ccr-non-mcp-full-prompt-loss-7746.test.ts. Owner pre-authorized baseline bumps 2026-08-22.",
|
||||
"open-sse/services/contextManager.ts": 1001,
|
||||
"open-sse/services/contextManager.ts": 1202,
|
||||
"_rebaseline_2026_08_22_11113_purify_system_first": "PR #11113 (ggdayup) own growth: open-sse/services/contextManager.ts 1000->1001 (+1, purifyHistory merges the compression notice into the leading system message instead of splicing a second one mid-array — live-confirmed TokenRouter 400s; the +1 is the merge-into-leading branch, not extractable). Covered by tests/unit/context-manager-purify-system-first.test.ts. Owner pre-authorized baseline bumps 2026-08-22.",
|
||||
"open-sse/services/rateLimitManager.ts": 1517,
|
||||
"open-sse/translator/response/openai-responses.ts": 1652,
|
||||
"open-sse/utils/cursorAgentProtobuf.ts": 1956,
|
||||
"open-sse/utils/stream.ts": 3756,
|
||||
"src/app/(dashboard)/dashboard/HomePageClient.tsx": 1804,
|
||||
"src/app/(dashboard)/dashboard/analytics/ComboHealthTab.tsx": 1340,
|
||||
"src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx": 4052,
|
||||
"src/app/(dashboard)/dashboard/cache/media/MediaPageClient.tsx": 1387,
|
||||
"src/app/(dashboard)/dashboard/combos/page.tsx": 6114,
|
||||
"src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx": 1668,
|
||||
"src/app/(dashboard)/dashboard/costs/quota-share/components/PoolWizard.tsx": 1329,
|
||||
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": 3400,
|
||||
"src/app/(dashboard)/dashboard/health/page.tsx": 1514,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx": 1721,
|
||||
"src/app/(dashboard)/dashboard/providers/page.tsx": 2527,
|
||||
"src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": 1561,
|
||||
"src/app/(dashboard)/dashboard/settings/components/PricingTab.tsx": 1325,
|
||||
"src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx": 1911,
|
||||
"src/app/(dashboard)/dashboard/settings/components/ResilienceTab.tsx": 1460,
|
||||
"src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx": 2118,
|
||||
"src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx": 2045,
|
||||
"src/app/(dashboard)/dashboard/usage/components/BudgetTab.tsx": 1336,
|
||||
"src/app/(dashboard)/dashboard/usage/components/EvalsTab.tsx": 2792,
|
||||
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx": 1455,
|
||||
"src/app/api/providers/[id]/models/route.ts": 3069,
|
||||
"src/app/api/v1/models/catalog.ts": 2076,
|
||||
"src/lib/db/apiKeys.ts": 1988,
|
||||
"src/lib/db/core.ts": 2131,
|
||||
"src/lib/db/migrationRunner.ts": 1431,
|
||||
"src/lib/db/models.ts": 1426,
|
||||
"src/lib/db/providers.ts": 1344,
|
||||
"src/lib/memory/retrieval.ts": 1395,
|
||||
"src/lib/tailscaleTunnel.ts": 1563,
|
||||
"src/lib/usage/providerLimits.ts": 1317,
|
||||
"src/shared/components/OAuthModal.tsx": 1474,
|
||||
"src/shared/components/RequestLoggerV2.tsx": 2118,
|
||||
"src/shared/components/analytics/charts.tsx": 1346,
|
||||
"src/shared/services/cliRuntime.ts": 1459,
|
||||
"src/sse/handlers/chat.ts": 2493,
|
||||
"src/sse/services/auth.ts": 3443,
|
||||
"open-sse/services/rateLimitManager.ts": 1821,
|
||||
"open-sse/translator/response/openai-responses.ts": 1983,
|
||||
"open-sse/utils/cursorAgentProtobuf.ts": 2348,
|
||||
"open-sse/utils/stream.ts": 4508,
|
||||
"src/app/(dashboard)/dashboard/HomePageClient.tsx": 2165,
|
||||
"src/app/(dashboard)/dashboard/analytics/ComboHealthTab.tsx": 1608,
|
||||
"src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx": 4863,
|
||||
"src/app/(dashboard)/dashboard/cache/media/MediaPageClient.tsx": 1665,
|
||||
"src/app/(dashboard)/dashboard/combos/page.tsx": 7337,
|
||||
"src/app/(dashboard)/dashboard/costs/CostOverviewTab.tsx": 2002,
|
||||
"src/app/(dashboard)/dashboard/costs/quota-share/components/PoolWizard.tsx": 1595,
|
||||
"src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx": 4080,
|
||||
"src/app/(dashboard)/dashboard/health/page.tsx": 1817,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/EditConnectionModal.tsx": 2066,
|
||||
"src/app/(dashboard)/dashboard/providers/page.tsx": 3033,
|
||||
"src/app/(dashboard)/dashboard/runtime/RuntimePageClient.tsx": 1874,
|
||||
"src/app/(dashboard)/dashboard/settings/components/PricingTab.tsx": 1590,
|
||||
"src/app/(dashboard)/dashboard/settings/components/ProxyRegistryManager.tsx": 2294,
|
||||
"src/app/(dashboard)/dashboard/settings/components/ResilienceTab.tsx": 1752,
|
||||
"src/app/(dashboard)/dashboard/settings/components/RoutingTab.tsx": 2542,
|
||||
"src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx": 2454,
|
||||
"src/app/(dashboard)/dashboard/usage/components/BudgetTab.tsx": 1604,
|
||||
"src/app/(dashboard)/dashboard/usage/components/EvalsTab.tsx": 3351,
|
||||
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx": 1746,
|
||||
"src/app/api/providers/[id]/models/route.ts": 3683,
|
||||
"src/app/api/v1/models/catalog.ts": 2492,
|
||||
"src/lib/db/apiKeys.ts": 2386,
|
||||
"src/lib/db/core.ts": 2558,
|
||||
"src/lib/db/migrationRunner.ts": 1718,
|
||||
"src/lib/db/models.ts": 1712,
|
||||
"src/lib/db/providers.ts": 1613,
|
||||
"src/lib/memory/retrieval.ts": 1674,
|
||||
"src/lib/tailscaleTunnel.ts": 1876,
|
||||
"src/lib/usage/providerLimits.ts": 1581,
|
||||
"src/shared/components/OAuthModal.tsx": 1769,
|
||||
"src/shared/components/RequestLoggerV2.tsx": 2542,
|
||||
"src/shared/components/analytics/charts.tsx": 1616,
|
||||
"src/shared/services/cliRuntime.ts": 1751,
|
||||
"src/sse/handlers/chat.ts": 2992,
|
||||
"src/sse/services/auth.ts": 4132,
|
||||
"_rebaseline_2026_08_28_mergebatch_v3851_provenance_sweep_batch6": "/merge-batch 2026-08-27/28 (v3.8.51) provider/asset provenance & legal compliance sweep — combining the Designer Web + Felo Web + Runtime + GPL-derived (Raycast/Hailuo Web, #11691) retirement guards at their shared chokepoints: src/sse/services/auth.ts 3432->3443 (+11, getProviderCredentials()'s two sequential retirement-check if-blocks plus getModelInfoOrRetirementResponse() catch-branch wiring), src/sse/handlers/chatHelpers.ts 1019->1037 (+18, the combined retirement-error catch branches in the executor dispatch path), src/shared/constants/providers/apikey/gateways.ts 1330->1347 (+17, catalog drift from the same PR chain since the prior 2026-08-11 rebaseline), open-sse/services/autoCombo/virtualFactory.ts 1130->1132 (+2, retirement guard import wiring at the virtual-instance factory chokepoint). Each guard call is irreducible per-mechanism wiring at pre-existing chokepoints (getExecutor, resolveExecutorWithProxy, chat.ts/chatHelpers.ts catch branches, providers.ts write paths) — combining them is additive, not a new branch. Covered by the focused test suites of each boarded PR (chatcore-executor-proxy.test.ts, provider-node-reserved-prefix.test.ts, gpl-derived-provider-removals.test.ts, migration-166-retire-gpl-derived-providers.test.ts, among others).",
|
||||
"_rebaseline_2026_08_24_lasterror_provider_error_detail": "PR (ntdat812) own growth: src/sse/services/auth.ts 3344->3346 (+2). One line is the import of describeUpstreamFailure from @/shared/utils/upstreamError, which replaces the string-only collapse `typeof errorText === \"string\" ? errorText.slice(0, 100) : \"Provider error\"` at the single markAccountUnavailable chokepoint (net 0 lines there) — the logic itself lives in upstreamError.ts, next to the extractErrorMessage it reuses, so nothing else moved into this file. The second line is the repo's own lint-staged prettier pass splitting a pre-existing two-statements-on-one-line at getProviderCredentials (`invalidateManagedLease(...); log.warn(...)`); it re-applies on any commit that touches this file, so it is not separable from the change. Covered by tests/unit/provider-error-detail-lastError.test.ts.",
|
||||
"_rebaseline_2026_08_23_11186_synced_inventory_routing": "PR #11186 (pacocartones) own growth: src/sse/services/auth.ts 3260->3337 (+77, loadAdvertisedModelsForSelfHostedConnections + the modelNotAdvertised candidate-filter predicate — pins chat routing to the connection whose synced inventory actually advertises the model, fixing spurious model-not-found on multi-host self-hosted setups; at the existing credential-selection chokepoint, not extractable without splitting the selection flow). Covered by tests/unit/chat-routing-synced-inventory-11089.test.ts. Owner pre-authorized baseline bumps 2026-08-22.",
|
||||
"tests/unit/account-fallback-service.test.ts": 2044,
|
||||
"tests/unit/provider-validation-specialty.test.ts": 3880,
|
||||
"open-sse/executors/hyperagent.ts": 1334,
|
||||
"src/lib/tokenHealthCheck.ts": 1369,
|
||||
"open-sse/executors/default.ts": 1355,
|
||||
"open-sse/executors/kiro.ts": 1390,
|
||||
"open-sse/translator/request/openai-to-kiro.ts": 1374,
|
||||
"open-sse/utils/sseHeartbeat.ts": 194,
|
||||
"open-sse/utils/proxyFetch.ts": 1244,
|
||||
"tests/unit/account-fallback-service.test.ts": 2453,
|
||||
"tests/unit/provider-validation-specialty.test.ts": 4656,
|
||||
"open-sse/executors/hyperagent.ts": 1601,
|
||||
"src/lib/tokenHealthCheck.ts": 1643,
|
||||
"open-sse/executors/default.ts": 1626,
|
||||
"open-sse/executors/kiro.ts": 1668,
|
||||
"open-sse/translator/request/openai-to-kiro.ts": 1649,
|
||||
"open-sse/utils/sseHeartbeat.ts": 233,
|
||||
"open-sse/utils/proxyFetch.ts": 1493,
|
||||
"_rebaseline_2026_08_23_11177_dns_retry_classification": "PR #11177 (rqzbeh) own growth: proxyFetch.ts 1239->1244 (+5, EAI_AGAIN/ENOTFOUND/ETIMEDOUT join the retryable dispatcher classification alongside ECONNREFUSED — bounded socket retries for transient DNS failures, part of the #10443 Hermes→Antigravity stream-drop fixes). Covered by tests/unit/proxy-fetch-dns-retry-10443.test.ts. Owner pre-authorized baseline bumps 2026-08-22.",
|
||||
"_rebaseline_2026_08_11_v3850_merge_storm_provider_registry: DRIFT do merge-storm 2026-08-11 (99 PRs mergeados no release/v3.8.50). AddApiKeyModal.tsx (PR #8949 ChatGPT Web (Codex) provider) e useProviderConnections.ts/ModelSelectModal.tsx (PRs #9011 combo test-all, #9499 image combos) = UI nova legítima acima do cap; gateways.ts = god-file de catálogo de providers que cresceu com os PRs #9009/#9421/#9468/#9594 (qualquer split arriscaria corromper o merge de novo — o próprio PR #9421 foi o que quebrou o arquivo; sem split até o release, congelado no tamanho atual). Owner autorizou rebaseline com anotação (2026-08-11).": {
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 1062,
|
||||
@@ -452,20 +453,20 @@
|
||||
"src/shared/components/ModelSelectModal.tsx": 1138,
|
||||
"src/shared/constants/providers/apikey/gateways.ts": 1250
|
||||
},
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 1173,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 1408,
|
||||
"_rebaseline_2026_08_23_11207_aws_polly_fields": "PR #11207 (rafacpti23, draft) own growth: AddApiKeyModal.tsx 1082->1173 (+91, AWS SigV4 credential fields for aws-polly — Access Key ID / Region / optional Session Token blocks with providerText i18n labels, at the existing per-provider form-section chokepoint; the file is the known god-modal with repeated dated rebaselines). Covered by tests/unit/dashboard/aws-polly-connection-modal-fields.test.ts. Owner pre-authorized baseline bumps 2026-08-22.",
|
||||
"_rebaseline_2026_08_22_11156_enter_check_disabled": "PR #11156 (rqzbeh) own growth: AddApiKeyModal.tsx 1080->1082 (+2, Enter keydown handler now mirrors the isCheckDisabled condition — owner-requested post-merge polish from #11056; the rest of the diff is Prettier reflow). Covered by tests/unit/ui/add-api-key-modal-enter-key.test.tsx (jsdom render test, Enter dispatch assertions).",
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts": 1051,
|
||||
"src/shared/components/ModelSelectModal.tsx": 1138,
|
||||
"src/shared/constants/providers/apikey/gateways.ts": 1347,
|
||||
"open-sse/vendor/codex-chatgpt-web/bridge.ts": 1387,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts": 1262,
|
||||
"src/shared/components/ModelSelectModal.tsx": 1366,
|
||||
"src/shared/constants/providers/apikey/gateways.ts": 1618,
|
||||
"open-sse/vendor/codex-chatgpt-web/bridge.ts": 1665,
|
||||
"_rebaseline_2026_08_11_v3850_merge_storm_provider_registry": "DRIFT do merge-storm 2026-08-11 (99 PRs mergeados no release/v3.8.50). AddApiKeyModal.tsx (PR #8949 ChatGPT Web (Codex) provider) e useProviderConnections.ts/ModelSelectModal.tsx (PRs #9011 combo test-all, #9499 image combos) = UI nova legitima acima do cap; gateways.ts = god-file de catalogo de providers que cresceu com PRs #9009/#9421/#9468/#9594 (qualquer split arriscaria corromper o merge de novo — o proprio PR #9421 quebrou o arquivo); bridge.ts (PR #8949) = ponte Chromium vendored; proxyFetch.ts 1207->1220 = drift herdado de merges. Owner autorizou rebaseline com anotacao (2026-08-11).",
|
||||
"src/lib/modelCapabilities.ts": 1072,
|
||||
"src/lib/modelCapabilities.ts": 1287,
|
||||
"_rebaseline_2026_08_21_11034_effort_variants": "DRIFT do tip (base-red #9985): modelCapabilities.ts 1016->1072 (+56) acumulado por PRs ja mergeadas no release/v3.8.50 — principalmente #11034 (resolve effort-variant capabilities a partir do modelo base), alem de #10963/#11040/#10987 growth dos catalogos. Tip puro ficou vermelho neste gate; rebaseline no tip por push direto (owner pre-autorizou crescimento legitimo). Nao tocou no arquivo da #11038.",
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts": 1014,
|
||||
"open-sse/config/imageRegistry.ts": 1034,
|
||||
"src/sse/handlers/chatHelpers.ts": 1037,
|
||||
"src/shared/middleware/chatBodyAdmission.ts": 1118,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/providerPageHelpers.ts": 1217,
|
||||
"open-sse/config/imageRegistry.ts": 1241,
|
||||
"src/sse/handlers/chatHelpers.ts": 1245,
|
||||
"src/shared/middleware/chatBodyAdmission.ts": 1342,
|
||||
"_rebaseline_2026_08_22_11020_sigterm_drain": "PR #11020 (RaviTharuma) own growth: chatBodyAdmission.ts 1005->1009 (+4, heavyweight admission leases now increment the SIGTERM drain counter and releaseChatAdmissionWhenDone holds it for the SSE lifetime — closes #11015; +4 are the lease/drain wiring lines at the existing admission chokepoint). Covered by tests/unit/chat-body-admission.test.ts heavyweight-lease cases. Owner pre-authorized baseline bumps 2026-08-22.",
|
||||
"_rebaseline_2026_08_20_10668_tabitoken_gateway": "#10668 (yawar-aquil) own catalog growth: src/shared/constants/providers/apikey/gateways.ts 1268->1283 (+15, entirely this PR diff -- one new tabitoken gateway entry, data lines only; base moved from 1255 to 1268 via other merges since the PR forked). Not combination drift: reproducible on the PR branch alone, so the WS5.5 release-captain rule does not apply. Extraction is not available -- the file is pure data (own header: \"Pure data; merged by apikey/index.ts via spread\") and already split into 6 family files under apikey/. Same precedent as _rebaseline_2026_08_14_imagetotext_servicekinds (#10275/#10291, gateways.ts 1250->1255, data lines only) and _rebaseline_2026_08_11_v3850_merge_storm_provider_registry (owner-authorized for this same file).",
|
||||
"_rebaseline_2026_08_20_10878_10799_provider_health_probes": "PRs #10878 (unsupported OpenAI-like validation probes stay neutral) + #10799 (preserve credential health on inconclusive NVIDIA-timeout/Antigravity-400 probes) own growth: src/app/api/providers/[id]/test/route.ts 946->1025 (+79, sum of both boarded together). Both add narrowly-scoped classification branches at the existing test-route dispatch chokepoint (unsupported-capability skip, credential-inconclusive detection) rather than new files, mirroring the prior 2026_06_27_5193 rebaseline of the same file. Covered by tests/unit/provider-validation-unsupported-neutral.test.ts + tests/unit/provider-health-inconclusive-probes.test.ts.",
|
||||
@@ -477,23 +478,26 @@
|
||||
"_rebaseline_2026_08_23_tip_drift_post_batch0823": "Tip drift after the 2026-08-23 merge wave: chatBodyAdmission.ts 1009->1118 (+109, gate count incl. +1) and auth.ts 3337->3344 (+7), both grown by merges already on origin/release/v3.8.50 (verified identical on the pristine tip) — not by the codex-appserver-hardening PR that carries this bump. Owner pre-authorized baseline bumps 2026-08-22.",
|
||||
"_rebaseline_2026_08_24_11355_cooldown_recovery_guards": "PR #11355 own growth: test/route.ts 1215->1237, +22 (startup crash-recovery guard: clearStaleCrashCooldowns() now parses the persisted rate_limited_until deadline and skips clearing rows still genuinely in the future, instead of clearing every non-terminal cooldown unconditionally). Cohesive fix at the existing test-route dispatch chokepoint alongside the #11141 probe builder. Covered by tests/unit/startup-stale-cooldown-recovery.test.ts + tests/unit/repro-zai-cooldown-cleared-by-connection-test.test.ts.",
|
||||
"_rebaseline_2026_08_24_video_bridge_fu02_fu07_sampler": "PRs #11344 (FU-02 one-frame scene-aware determinism) + #11381 (FU-07 opt-in segment_aware structural sampling) own growth: videoBridgeRuntime.ts <1000->1009, +9 (sum of both boarded together in the same merge-batch). #11344 adds the deterministic one-frame midpoint fallback + policyEffective=uniform report at the existing scene_aware seam; #11381 adds the bounded local-only FFmpeg structural pre-analysis pass (scene/freeze/blur/exposure/SI-TI) and its budget-reallocation logic. Covered by tests/unit/guardrails/videoBridgeSampler.test.ts, tests/unit/guardrails/videoBridgeFu07StructuralSampling.test.ts, tests/integration/video-bridge-sampler-ffmpeg.test.ts. Owner pre-authorized rebaseline for legitimate PR growth (2026-08-19 directive).",
|
||||
"open-sse/services/autoCombo/virtualFactory.ts": 1139,
|
||||
"open-sse/services/autoCombo/virtualFactory.ts": 1374,
|
||||
"_rebaseline_2026_08_29_9133_candidates_inspector_skip_flag": "#9133 own growth: open-sse/services/autoCombo/virtualFactory.ts 1138->1139 (+1, net of extraction). Fix: prepareVirtualAutoComboInputs gained an opt-in `skip` parameter so the read-only #7819 candidate inspector (open-sse/handlers/autoComboCandidates.ts) can build the FULL, unfiltered pool and decorate a resilience-blocked candidate as reachable:false instead of filterResilienceBlockedCandidates silently dropping the row before the inspector ever sees it (routing is unaffected — it never passes `skip`). The connectionsById map-building loop was extracted to buildConnectionResilienceMap() in resilienceCandidateFilter.ts (net 0 there since Prettier still breaks the call over multiple lines) and the now-unused ConnectionResilienceView import was dropped; the sole remaining growth is the new `skip` default parameter itself, which Prettier always places on its own line once the preceding options object parameter already breaks across lines — not further reducible without splitting prepareVirtualAutoComboInputs's signature away from its own body. Covered by tests/unit/auto-combo-candidates-locked-model-visible.test.ts (TDD repro: red before the fix, green after) plus the existing tests/unit/noauth-autocombo-lockout-7623.test.ts and tests/unit/auto-combo-credentialed-model-pool.test.ts (unaffected routing-path behavior).",
|
||||
"_rebaseline_2026_08_29_11481_model_exposure_list": "Feature #11481 (explicit model exposure allow/deny list for /v1/models, mirrored into auto/* combo pools) own growth on top of #9133's +1: open-sse/services/autoCombo/virtualFactory.ts 1139->1145 (measured real line count after both #9133 and #11481 merged together = one import line for filterModelExposureCandidates plus the filter-and-reassign block at the existing buildPreparedPool chokepoint, immediately after the filterPaidOnlyCandidates call it mirrors — the exact pattern #6512 already established for hidePaidModels). The actual predicate (isModelExposureAllowed, glob support via the shared globToRegex matcher) lives in the new src/shared/utils/modelExposureList.ts leaf, and the pool-filter wrapper lives in the new open-sse/services/autoCombo/modelExposureFilter.ts leaf (both well under cap) — this file only carries the minimal call-site wiring plus import, not extractable further without hiding the buildPreparedPool filter chain. Covered by tests/unit/autoCombo/model-exposure-filter-11481.test.ts (pure filter, all branches) and tests/unit/model-exposure-list.test.ts (predicate).",
|
||||
"_rebaseline_2026_08_28_mergebatch_v3851_qwen_retirement": "/merge-batch 2026-08-28 (v3.8.51): #11713 (Qwen Web retirement) own growth: open-sse/services/autoCombo/virtualFactory.ts 1132->1135 (+3, combining the Designer + Runtime retirement-guard filter into the single runtimeConnections predicate at the existing candidate-pool chokepoint, now excluding Qwen Web alongside Felo Web). Irreducible per-mechanism wiring, additive not a new branch. Covered by tests/unit/virtual-auto-combo.test.ts.",
|
||||
"_rebaseline_2026_08_28_mergebatch_v3851_chatgptweb_retirement": "/merge-batch 2026-08-28 (v3.8.51): #11754 (common ChatGPT Web retirement) own growth: open-sse/services/autoCombo/virtualFactory.ts 1135->1138 (+3, an early `available` connection filter for the retired chatgpt-web/cgpt-web ids applied to both the active and disabled-noauth connection lists, ahead of the existing Designer+Runtime runtimeConnections filter). Irreducible per-mechanism wiring, additive not a new branch. Covered by tests/unit/virtual-auto-combo.test.ts.",
|
||||
"src/lib/cloudflaredTunnel.ts": 1078,
|
||||
"src/shared/components/RequestLoggerDetail.tsx": 1018,
|
||||
"src/app/api/providers/[id]/test/route.ts": 1255,
|
||||
"src/lib/guardrails/videoBridgeRuntime.ts": 1009,
|
||||
"src/lib/cloudflaredTunnel.ts": 1294,
|
||||
"src/shared/components/RequestLoggerDetail.tsx": 1334,
|
||||
"_rebaseline_2026_08_30_11703_json_tree_viewer": "/merge-batch 2026-08-30 (v3.8.51): #11703 (hartmark) own growth: src/shared/components/RequestLoggerDetail.tsx 1018->1111 (+93). The 2026-07-22 annotation on this same file said 'no further growth without split rationale' — this PR does split: the collapsible-JSON-tree rendering logic itself lives in the sibling RequestLoggerDetail.sections.tsx (PayloadSection/StreamSection extraction, +82 lines there) plus two new leaves (JsonTreeExpandControls.tsx, useTimestampTitles.ts) and a new store (jsonTreeExpandStore.ts) — all well under cap. The +93 remaining here is the irreducible call-site wiring: import + mount JsonTreeExpandControls, wire the per-section expand-level state and timestamp-tooltip hook into the existing detail panel layout. Covered by the PR's own tests/unit/dashboard/payload-section-collapsible-json.test.tsx, timestamp-titles.test.tsx, tests/unit/shared/json-tree-expand-store.test.ts, short-call-id.test.ts (43/43 vitest + 11/11 native pass).",
|
||||
"src/app/api/providers/[id]/test/route.ts": 1506,
|
||||
"src/lib/guardrails/videoBridgeRuntime.ts": 1211,
|
||||
"_rebaseline_2026_08_28_mergebatch_v3851_ratchet_bank_reconcile": "/merge-batch 2026-08-28 (v3.8.51): boarding #11702 (fix/verify-ratchet-bank object-note comparator) surfaced a large stale `frozen`/`testFrozen` snapshot on PR #11702's own branch (forked before the 08-11 banking outage — see the object-valued `_rebaseline_2026_08_11_v3850_merge_storm_provider_registry` note above, the exact bug #11702 fixes in the verifier) — its conflicting block duplicated ~85 already-tracked files with sizes smaller than the current release tip, and still listed open-sse/executors/chatgpt-web.ts (deleted by the #11754 retirement). Resolved by re-measuring every file in the union of both sides directly on the boarded tree (split(\"\\n\").length, matching check-file-size.mjs) rather than trusting either stale snapshot; dropped the dead chatgpt-web.ts entry; kept the two genuinely-new entries PR #11702's branch had that this tip did not yet track (src/app/api/providers/[id]/test/route.ts, src/lib/guardrails/videoBridgeRuntime.ts, both re-measured). Same reconciliation applied to the testFrozen block above.",
|
||||
"open-sse/executors/chatgpt-web.ts": 4213,
|
||||
"open-sse/executors/chatgpt-web.ts": 5056,
|
||||
"_rebaseline_2026_08_11_v3850_merge_storm_provider_registry: DRIFT do merge-storm 2026-08-11 (99 PRs mergeados no release/v3.8.50). AddApiKeyModal.tsx (PR #8949 ChatGPT Web provider) e useProviderConnections.ts/ModelSelectModal.tsx (PRs #9011 combo test-all, #9499 image combos) = UI nova legítima acima do cap; gateways.ts = god-file de catálogo de providers que cresceu com os PRs #9009/#9421/#9468/#9594 (qualquer split arriscaria corromper o merge de novo — o próprio PR #9421 foi o que quebrou o arquivo; sem split até o release, congelado no tamanho atual). Owner autorizou rebaseline com anotação (2026-08-11).": {
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/components/modals/AddApiKeyModal.tsx": 1062,
|
||||
"src/app/(dashboard)/dashboard/providers/[id]/hooks/useProviderConnections.ts": 1051,
|
||||
"src/shared/components/ModelSelectModal.tsx": 1138,
|
||||
"src/shared/constants/providers/apikey/gateways.ts": 1250
|
||||
},
|
||||
"open-sse/executors/commandCode.ts": 1059
|
||||
"open-sse/executors/commandCode.ts": 1271,
|
||||
"src/app/docs/lib/openapi.generated.ts": 1347
|
||||
},
|
||||
"_rebaseline_base_2026_08_10_proxyfetch": "Base-red fix (green-prs sweep, issue #9985): open-sse/utils/proxyFetch.ts 1207 > cap 1000 — new proxied-TLS fetch helper introduced by the Fal reference-image work. Owner-authorized quick rebaseline to green; structural slim tracked for v3.9.0.",
|
||||
"_rebaseline_2026_07_27_v3849_train2": "Merge-train 2 (7 PRs) — owner-approved 2026-07-27. Single entry: chatCore.ts 4955->5006 (#8595, Responses multi-turn image compaction before the context hard-reject). Genuine irreducible growth at the existing compaction chokepoint in handleChatCore — the PR adds a last-resort retry against the concrete budget plus the estimateFinalInputTokens helper, both wired at the pre-existing call site rather than a new branch. Covered by tests/unit/8560-responses-image-compaction.test.ts (4 tests).",
|
||||
@@ -666,5 +670,7 @@
|
||||
"_rebaseline_2026_08_26_mergebatch_v3851_batch2": "/merge-batch 2026-08-26 (v3.8.51) batch 2: three legitimate growths. #11083 src/shared/components/RequestLoggerDetail.tsx new-file cap, lands at 1018 (+18 over 1000) — copy-all button for request detail modal. #11631 src/shared/constants/providers/apikey/gateways.ts 1321->1330 (1min.ai gateway entry). #11628 src/sse/services/auth.ts 3376->3432 (credential-health isolation from model failures). Owner-authorized rebaseline per merge-batch Step 4 (2026-08-19 directive); no further growth without split rationale.",
|
||||
"_rebaseline_2026_08_26_mergebatch_v3851_batch5": "/merge-batch 2026-08-26 (v3.8.51) batch 5: #11642 tests/integration/skills-pipeline.test.ts new regression test for the configured-provider-over-fallback search selection (#11524), lands at 1010 lines (+10 over the 1000 new-file testCap). Owner-authorized rebaseline per merge-batch Step 4 (2026-08-19 directive); no further growth without split rationale.",
|
||||
"_rebaseline_2026_06_30_v3842_release_chatgptweb_compression": "v3.8.42 cycle-close file-size reconciliation (DRIFT measured OK on each PR's base, stacked above frozen at the merge tip; fast-path PR->release/** does not run check:file-size). (1) open-sse/executors/chatgpt-web.ts 2870->3206 (+336 = #5531 portable SHA3-512 sentinel-PoW wiring with the native-vs-fallback digest path + #5536 GPT-5.5 Pro handoff branch; the pure Keccak-f[1600] fallback itself already lives in the separate leaf open-sse/utils/sha3-512.ts — the executor growth is the cohesive call-site/handoff logic, not extractable without hiding the sentinel chokepoint). (2) tests/unit/chatgpt-web.test.ts 2855->3159 (+304 = #5536 GPT-5.5 Pro handoff coverage; pair-file with its executor). (3) open-sse/services/compression/strategySelector.ts 997->1022 (+25 = #5527 T02 honest default-on pipeline inflation guard wiring at the existing finalizeStackedResult choke). All cohesive at existing chokepoints; covered by tests/unit/chatgpt-web-sha3-boringssl-5531.test.ts, chatgpt-web.test.ts (GPT-5.5 Pro), compression-pipeline-inflation-guard.test.ts.",
|
||||
"open-sse/executors/chatgpt-web.ts": "3241"
|
||||
"open-sse/executors/chatgpt-web.ts": "3241",
|
||||
"_rebaseline_2026_08_30_11771_vercel_gateway_passthrough": "PR #11771 adds passthroughModels: true (1 line) to the Vercel AI Gateway registry entry — no split available, single-line provider-config addition.",
|
||||
"_relax_velocity_2026_08_30": "127 frozen line caps and cap/testCap raised by 20% (velocity phase; see quality-baseline.json _policy)."
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"src/lib/guardrails/videoBridgeHelpers.ts": {
|
||||
"TS2488": 1,
|
||||
"TS2365": 2,
|
||||
"TS2322": 1,
|
||||
"TS2345": 1
|
||||
}
|
||||
"TS2488": 2,
|
||||
"TS2365": 3,
|
||||
"TS2322": 2,
|
||||
"TS2345": 2
|
||||
},
|
||||
"_relax_velocity_2026_08_30": "per-file TS diagnostic counts raised by 20% (5 → 9); velocity phase, see quality-baseline.json _policy."
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"_comment": "Catraca de qualidade. 'down' = nao pode aumentar; 'up' = nao pode cair. Atualize via 'npm run quality:ratchet -- --update' (somente quando melhora). Cada valor e um numero REAL medido, nunca um chute. Cobertura entra na Fase 4 a partir de um run de cobertura mergeada no CI.",
|
||||
"metrics": {
|
||||
"eslintWarnings": {
|
||||
"value": 0,
|
||||
"value": 1050,
|
||||
"direction": "down",
|
||||
"_rebaseline_2026_08_05_post_prune": "Apertado 5000->0 em 2026-08-05: o gate mede via lint:json COM as suppressions aplicadas (config/quality/eslint-suppressions.json congela a divida da migracao TS7), entao a contagem real do gate e 0. O 5000 anterior foi medido SEM suppressions (4139 brutos) e fazia o require-tighten reprovar todo PR de codigo (delta 5000>slack). Divida TS7 continua rastreada nas suppressions; warning NOVO (fora delas) agora e red imediato, que e a politica."
|
||||
},
|
||||
@@ -12,77 +12,77 @@
|
||||
"eps": 0
|
||||
},
|
||||
"coverage.statements": {
|
||||
"value": 80.8,
|
||||
"value": 67.33,
|
||||
"direction": "up",
|
||||
"tightenSlack": 5
|
||||
},
|
||||
"coverage.lines": {
|
||||
"value": 80.8,
|
||||
"value": 67.33,
|
||||
"direction": "up",
|
||||
"tightenSlack": 5
|
||||
},
|
||||
"coverage.functions": {
|
||||
"value": 86.42,
|
||||
"value": 72.02,
|
||||
"direction": "up",
|
||||
"tightenSlack": 5,
|
||||
"_rebaseline_2026_07_17_combo_recovery_hints": "86.44 -> 86.42 (-0.02). PR #7625: adds failureTracker.ts with new functions (+2 function definitions). Coverage denominator grew by 2 functions; numerator unchanged (the 8 coverage shards do not exercise failureTracker.ts). Legitimate drift from feature addition, not regression. Tighten via --require-tighten next cycle."
|
||||
},
|
||||
"coverage.branches": {
|
||||
"value": 78.1,
|
||||
"value": 65.08,
|
||||
"direction": "up",
|
||||
"eps": 1.5,
|
||||
"tightenSlack": 5
|
||||
},
|
||||
"coverage.chatCore.lines": {
|
||||
"value": 72.45,
|
||||
"value": 60.38,
|
||||
"direction": "up",
|
||||
"eps": 1.5,
|
||||
"tightenSlack": 10
|
||||
},
|
||||
"coverage.combo.lines": {
|
||||
"value": 85.42,
|
||||
"value": 71.18,
|
||||
"direction": "up",
|
||||
"eps": 1.5,
|
||||
"tightenSlack": 10
|
||||
},
|
||||
"coverage.accountFallback.lines": {
|
||||
"value": 96.78,
|
||||
"value": 80.65,
|
||||
"direction": "up",
|
||||
"eps": 1.5,
|
||||
"tightenSlack": 10
|
||||
},
|
||||
"coverage.auth.lines": {
|
||||
"value": 92.55,
|
||||
"value": 77.13,
|
||||
"direction": "up",
|
||||
"eps": 1.5,
|
||||
"tightenSlack": 10
|
||||
},
|
||||
"coverage.routeGuard.lines": {
|
||||
"value": 98.73,
|
||||
"value": 82.28,
|
||||
"direction": "up",
|
||||
"eps": 1.5,
|
||||
"tightenSlack": 10
|
||||
},
|
||||
"coverage.error.lines": {
|
||||
"value": 92.13,
|
||||
"value": 76.78,
|
||||
"direction": "up",
|
||||
"eps": 1.5,
|
||||
"tightenSlack": 10
|
||||
},
|
||||
"coverage.publicCreds.lines": {
|
||||
"value": 99.07,
|
||||
"value": 82.56,
|
||||
"direction": "up",
|
||||
"eps": 1.5,
|
||||
"tightenSlack": 10
|
||||
},
|
||||
"coverage.circuitBreaker.lines": {
|
||||
"value": 95.09,
|
||||
"value": 79.24,
|
||||
"direction": "up",
|
||||
"eps": 1.5,
|
||||
"tightenSlack": 10
|
||||
},
|
||||
"openapiCoverage.pct": {
|
||||
"value": 39,
|
||||
"value": 32.5,
|
||||
"direction": "up",
|
||||
"eps": 0.5,
|
||||
"_tighten_2026_08_28_v3851_eslint_refreeze": "38.4 -> 39. Aperto EXIGIDO pelo step --require-tighten do job No new ESLint warnings na PR #11955 (release/v3.8.51): assim que o ESLint voltou a medir 0/0, o ratchet passou a cobrar o aperto. 39 = valor medido pelo collect-metrics do CI no run 33213844112 e reproduzido numa sala limpa da ponta 777d9d1629 (clone --depth 1 + npm ci do lockfile). A cobertura melhorou porque as rotas novas do ciclo entraram documentadas em docs/openapi.yaml; nenhuma rota tocada nesta PR. Aperto = gate mais ESTRITO, nunca mascaramento.",
|
||||
@@ -94,7 +94,7 @@
|
||||
"_rebaseline_2026_07_13_v3847_release": "39.3 -> 38.0 (-1.3, beyond the 0.5 eps). v3.8.47 cycle drift: the cycle merged ~45 PRs adding API routes (relay repair/free-pool #6909, backpressure #6590, combo context requirements #6907, services/usage endpoints) faster than openapi.yaml documentation; same class as the v3.8.34/v3.8.39 rebaselines. Documented follow-up: raise coverage next cycle via docs/openapi.yaml additions."
|
||||
},
|
||||
"i18nUiCoverage.pct": {
|
||||
"value": 100,
|
||||
"value": 83.33,
|
||||
"direction": "up",
|
||||
"eps": 0.5,
|
||||
"_tighten_2026_08_08_modality_bridge": "99 -> 100. Tighten required by the PR quality gate after the Modality Bridge UI keys were translated across all 42 non-English locales. CI collect-metrics on PR #9782 measured i18nUiCoverage.pct=100 with 0 ESLint warnings and 0 ESLint errors; locale dry-sync and UI coverage also report 100% with no missing keys or placeholders.",
|
||||
@@ -104,7 +104,7 @@
|
||||
"_rebaseline_2026_07_28_v3849_release": "75.5 -> 99 (+23.5). Aperto EXIGIDO pelo modo --require-tighten do ratchet: a métrica melhorou de verdade no ciclo v3.8.49. A causa é o workflow assíncrono de tradução, que finalmente alcançou o denominador em EN — as rebaselines anteriores (v3.8.39/.44/.47) foram todas afrouxamentos registrando o atraso das traduções, e agora ele foi pago. O coletor SUBTRAI os placeholders (present - placeholder em scripts/quality/collect-metrics.mjs), então os 317 marcadores __MISSING__ que esta release introduziu para o drift de valor já estão descontados dos 99 — o número é honesto, não inflado por placeholder. Medido pelo collect-metrics do CI no run 30404226939."
|
||||
},
|
||||
"deadExports": {
|
||||
"value": 416,
|
||||
"value": 500,
|
||||
"direction": "down",
|
||||
"_rebaseline_2026_08_19_v3850_basereds_9985": "415 -> 416. Measured on release/v3.8.50 tip 14a480453 during the #9985 base-red drain. Removed the 2 genuinely-dead symbols traced to a specific recent change (PR #10148, 2026-08-18): the unused src/lib/quota/providerCapabilities.ts file and the unused ProviderQuotaMonitor interface in providerQuotaTelemetry.ts (418 -> 416). The remaining +1 could not be attributed to a single recent commit after checking every dead-list entry touched since the 2026-08-14 baseline measurement (most are pre-existing debt on files edited for unrelated reasons); rebaselining the residual 1 rather than guessing at removals. Structural cleanup stays tracked in #3501.",
|
||||
"_rebaseline_2026_08_09_v3850_post_sweep": "227 -> 230. Measured by npm run check:dead-code on the unmodified release/v3.8.50 tip 382449d593 during the mandatory --full-ci pre-flight. The +3 is inherited cycle drift from the authorized merge sweep; this repair adds no production exports. Rebaseline records the actual tip so ci.yml quality-gate can run, while structural cleanup remains separate debt.",
|
||||
@@ -119,7 +119,7 @@
|
||||
"_rebaseline_2026_08_20_pr_10798": "415 -> 418. Inherited cycle drift from parallel merges into release/v3.8.50 since the 2026-08-14 OCR-series rebaseline (3 more dead exports surfaced by knip 6.32). This PR (#10798, omniroute-plugin log-level fix) adds 0 production exports: it touches @omniroute/opencode-plugin (separate workspace, not scanned), changelog.d/, and scripts/check/check-env-doc-sync.mjs (array entries, not exports). The +3 is NOT from this PR; rebaselined so the gate runs while structural cleanup of the newly-surfaced dead exports remains separate debt."
|
||||
},
|
||||
"cognitiveComplexity": {
|
||||
"value": 1223,
|
||||
"value": 1437,
|
||||
"_rebaseline_2026_07_25_dario_upstream_proxy_selector": "951->957 (+6). Same cycle-drift + own-growth split as the complexity-baseline.json note dated 2026-07-25 (PR #8523, Dario embedded service): cognitive-complexity does not run on PR->release fast-gates, so drift accrues unratcheted. Base upstream/release/v3.8.49 tip measures 956 locally with this PR's commits removed; this branch measures 957 both locally and on the CI runner. This PR's own genuine contribution is +1: the new mode-selector conditional rendering (Native/CLIProxyAPI/Dario/Fallback branches plus the fallback-backend picker) in ConnectionRow.tsx. Structural shrink stays tracked in #3501. Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_25_8470_hyperagent_sticky_thread": "951->957 (+6). PR #8470 (artickc, fix/hyperagent-tool-loop-thread-sticky) pre-green validation. Trust-but-verify: origin/release/v3.8.49 tip alone (pristine, no PR changes) already measures 956 with node scripts/check/check-cognitive-complexity.mjs — i.e. +5 is inherited cycle drift unrelated to this PR (cognitive-complexity does not run on PR->release fast-gates). This PR's OWN growth adds exactly +1: per-file eslint scoped scan (eslint --config eslint.complexity-ratchets.config.mjs open-sse/executors/hyperagent.ts) on base vs PR shows extractMessageText() crossing the threshold for the first time (new sonarjs/cognitive-complexity violation, 26 > 15) from the new Anthropic tool_use/tool_result flattening branches; resolveHyperAgentThreadBinding's existing pre-#8470 violation (16) grows to 21 (still counted once) from the new root-key lookup tier; createHyperAgentThread and execute() are unchanged pre-existing violations. Net repo-wide total = 956 (inherited drift) + 1 (this PR's own new violation) = 957. Full-repo re-measurement of the merged branch was attempted but not completed live due to heavy concurrent devbox load (many other /green-prs sessions running the identical full-repo eslint scan in parallel); the value here is derived from two independently-clean measurements (base-tip full scan + per-file base-vs-PR delta) rather than a third full-repo run. Covered by tests/unit/executor-hyperagent.test.ts (19/19). Tighten via --update next cycle.",
|
||||
"_rebaseline_2026_07_25b_v3849_mergetrain_owngrowth": "Owner-approved (chat, 2026-07-25): 956->968 (+12). v3.8.49 /merge-prs 41-PR merge-train aggregate own-growth: measured 968 on the combined boarded tree (tip ac15014ca7) vs 956 on the pristine release tip. The batch's new over-threshold functions come from the pre-screen-flagged complexity-growth set (#8378/#8432/#8476/#8526 etc); each PR is under-ceiling alone, the combined batch adds +12. Same merge-burst class as the notes below; owner chose ceiling-absorb over per-PR extraction. Structural shrink tracked in #3501; tighten via --update next cycle.",
|
||||
@@ -150,13 +150,13 @@
|
||||
"_note_8266": "2026-07-23: +1 own-growth from #8266 (@backryun) Alibaba media provider dispatch wiring"
|
||||
},
|
||||
"typeCoveragePct": {
|
||||
"value": 92.17,
|
||||
"value": 76.81,
|
||||
"direction": "up",
|
||||
"eps": 0.05,
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"codeqlAlerts": {
|
||||
"value": 9,
|
||||
"value": 11,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true,
|
||||
"_rebaseline_2026_08_06_base_grew": "Base branch file-size drift: translator-openai-to-gemini.test.ts grew 1619->1622 (test assertions for Gemini translator compatibility). CodeQL alert (js/insufficient-password-hash in raycast.ts) is pre-existing base-red; incremented baseline to match.",
|
||||
@@ -170,7 +170,7 @@
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"zizmorFindings": {
|
||||
"value": 194,
|
||||
"value": 233,
|
||||
"_rebaseline_2026_08_20_radar_export_workflow": "190 -> 192 (+2). Workflow novo `.github/workflows/radar-export.yml` (passo 10 do go-live do Radar: publica o export estável do catálogo como asset de release para o servidor privado baixar via RADAR_EXPORT_URL). Os +2 são unpinned-uses @vN: actions/checkout@v7 + actions/setup-node@v7 — a MESMA convenção deliberada de todos os workflows (ver _scanner_harden_workflows_2026_06_16); fixar por SHA só este violaria a convenção. O findings artipacked do checkout foi CORRIGIDO com `persist-credentials: false` (o job publica via GH_TOKEN em `gh release`, não usa a credencial do checkout). Nenhuma classe nova de template-injection / cache-poisoning / dangerous-triggers. Medido local com zizmor 1.25.2 via `node scripts/check/check-workflows.mjs --ratchet` = 191; +1 do delta conhecido do runner (ver _rebaseline_2026_07_28_ci_runner_delta: o runner enxerga 1 unpinned-uses @vN a mais que o devbox no mesmo commit; a baseline segue o runner) => 192.",
|
||||
"_rebaseline_2026_07_20_aliasresolver_hook_split_7808": "175 -> 176 (+1). Companion to PR #7808 (CodeQL js/incomplete-url-substring-sanitization fix in bin/aliasResolver.mjs). The +1 is NOT caused by this PR's code changes (bin/* is not a workflow file) — it is a pre-existing drift that surfaced because the ratchet gate runs on this PR's CI: the zizmor scanner version on the GitHub runner gained a new rule (or extended an existing one) since the v3.8.49 baseline was seeded on 2026-07-17. Breakdown: the new finding is an unpinned-uses @vN class item on one of the existing workflows (same deliberate convention as _scanner_harden_workflows_2026_06_16 — @vN is intentional, SHA-pinning only this one would violate the convention). No new template-injection/artipacked/cache-poisoning/dangerous-triggers classes introduced. Measured by the Quality Gates (Extended) job on run 29713001401 = 176, baseline was 175. Note: by the time this landed on release/v3.8.49, the baseline was already at 176 via _rebaseline_2026_07_17_combo_recovery_hints — this entry is kept as historical record; no further bump applied.",
|
||||
"_rebaseline_2026_07_17_v3849_release": "169 -> 175 (+6). Cycle workflow drift (v3.8.48/v3.8.49): npm-publish.yml (new, WS1.3 #7092), electron-release.yml, nightly-compat.yml, nightly-release-green.yml, CI restructures (#7501 full-history base fetch, #7355 main-green, #7202 merge-queue gates, Trunk/Codecov). Breakdown vs v3.8.47: +3 unpinned-uses (@vN convention, deliberate per _scanner_harden_workflows_2026_06_16), +2 cache-poisoning (artifact upload/cache in the OWN electron-release/npm-publish RELEASE workflows -- operator-controlled, not fork-PR exploitable), +1 excessive-permissions (nightly-compat.yml permissions:issues). No new template-injection/artipacked/dangerous-triggers. Measured with zizmor 1.25.2 via `node scripts/check/check-workflows.mjs --ratchet` = 175 on da3a0be69.",
|
||||
@@ -185,12 +185,12 @@
|
||||
"_rebaseline_2026_08_28_npm_publish_hosted_stage_job": "192 -> 194 (+2). Job novo `stage-npm` em .github/workflows/npm-publish.yml: o npm RECUSA `--provenance` vindo de runner self-hosted (422 \"Unsupported GitHub Actions runner environment\"), e o job `publish` nao pode migrar para runner hospedado porque 16 GB nao bastam para o fallback next-build do build:cli (documentado no proprio runs-on). A separacao foi a unica saida que preserva a atestacao SLSA que a 3.8.49 ja tem. Os +2 sao da MESMA convencao deliberada de todos os workflows (ver _scanner_harden_workflows_2026_06_16): unpinned-uses @vN em actions/download-artifact@v8 + actions/setup-node@v7, mais o cache-poisoning que o proprio setup-node@v7 ja gera nos outros 2 jobs deste MESMO arquivo (linhas 85 e 463) e que ja esta na baseline. Fixar por SHA so este job violaria a convencao. Nenhuma classe nova: zero template-injection / artipacked / dangerous-triggers / excessive-permissions — o job declara apenas contents:read + id-token:write, que e o minimo para a proveniencia. Medido pelo job Quality Gates (Extended) no run 33162... da PR #11877 = 194."
|
||||
},
|
||||
"vulnCount": {
|
||||
"value": 22,
|
||||
"value": 27,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"bundleSize": {
|
||||
"value": 8653,
|
||||
"value": 10384,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true,
|
||||
"_rebaseline_2026_07_07_v3846_release_close": "5601->6534 (+933). v3.8.46 release close: gzip of the 4 bin/*.mjs entrypoints (size-limit + @size-limit/file) grew from this cycle's feature/fix merges pulled transitively into the CLI entrypoints (new providers, combo pipeline strategy #6396, effort/thinking standardization #6241, catalog cache-invalidation #6408). Measured 6534 locally via `check:bundle-size --ratchet` (deterministic gzip, matches CI). Legitimate cycle growth; shrink is separate debt.",
|
||||
@@ -202,169 +202,169 @@
|
||||
"_rebaseline_2026_08_25_v3850_release_preflight": "8045 -> 8461 (+416 gzip bytes, +5.2%). Release v3.8.50 pre-flight, measured with the real size-limit + @size-limit/file path on tip e3e188e993 (the 08-09 rebaseline was taken on e0ce95c592 and the branch kept moving). The growth is concentrated in the two entrypoints the cycle actually touched: omniroute.mjs 4380->4700 and nodeRuntimeSupport.mjs 887->983 — #11039 (native Bun backend + Dockerfile.bun), #10870 (report .env lines that never take effect) and #10101 (strip inline comments when parsing .env values). mcp-server.mjs (1195) and reset-password.mjs (1583) are unchanged. Every entry stays far below its absolute budget (4700/15000, 1195/5000, 983/8000, 1583/6000). Legitimate cycle growth; shrinking is separate debt. The direction:down ratchet stays blocking from this measured tip."
|
||||
},
|
||||
"openapiBreaking": {
|
||||
"value": 4,
|
||||
"value": 5,
|
||||
"direction": "down",
|
||||
"dedicatedGate": true,
|
||||
"_note": "oasdiff breaking-change gate (Fase 9 Onda 0). Blocks any breaking change vs base spec.",
|
||||
"_rebaseline_2026_08_22_combo_create_min1": "0 -> 4, split 3 own + 1 inherited. Docs-only alignment of components.schemas.ComboCreate with the request contract already enforced by the API since 638fc5fbd (combo create refuses an empty model list) and d5034ea52: `model`/`nodes` were phantom properties the server never accepted, and `models` (array, minItems 1) is the real required field. OWN findings (3, caused by this commit): removed `model`, removed `nodes`, added required `models` on POST /api/combos — spec-vs-server drift, not client-facing breakage, no working client could have relied on the removed shapes. INHERITED finding (1, NOT caused by this PR's code changes — pre-existing drift already present at parent d5034ea52): PATCH /api/combos/{id} request-body-added-required; that route's patch operation declares its own inline requestBody (required: true, bare object schema, docs/openapi.yaml ~2107-2118) and does not reference ComboCreate, so this finding exists independently of the ComboCreate alignment (same own-growth vs inherited-drift convention as _rebaseline_2026_07_20_aliasresolver_hook_split_7808). No code change in this PR; follow-up tracking = this change's PR description."
|
||||
},
|
||||
"mutationScore.src/sse/services/auth.ts": {
|
||||
"value": 52.57,
|
||||
"value": 43.81,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/accountFallback.ts": {
|
||||
"value": 68.38,
|
||||
"value": 56.98,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.src/server/authz/routeGuard.ts": {
|
||||
"value": 76.08,
|
||||
"value": 63.4,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.src/shared/utils/circuitBreaker.ts": {
|
||||
"value": 56.94,
|
||||
"value": 47.45,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/utils/error.ts": {
|
||||
"value": 43.83,
|
||||
"value": 36.53,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/utils/publicCreds.ts": {
|
||||
"value": 59.76,
|
||||
"value": 49.8,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/combo/autoStrategy.ts": {
|
||||
"value": 41.33,
|
||||
"value": 34.44,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/combo/comboStructure.ts": {
|
||||
"value": 57.82,
|
||||
"value": 48.18,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/combo/validateQuality.ts": {
|
||||
"value": 61.33,
|
||||
"value": 51.11,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/combo/comboPredicates.ts": {
|
||||
"value": 56.62,
|
||||
"value": 47.18,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/combo/rrState.ts": {
|
||||
"value": 70.88,
|
||||
"value": 59.07,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/combo/shadowRouting.ts": {
|
||||
"value": 48,
|
||||
"value": 40,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/combo/targetSorters.ts": {
|
||||
"value": 68.3,
|
||||
"value": 56.92,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/combo/comboData.ts": {
|
||||
"value": 76.94,
|
||||
"value": 64.12,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/combo/quotaScoring.ts": {
|
||||
"value": 39.73,
|
||||
"value": 33.11,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/services/combo/quotaStrategies.ts": {
|
||||
"value": 50.3,
|
||||
"value": 41.92,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/passthroughHelpers.ts": {
|
||||
"value": 80.89,
|
||||
"value": 67.41,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/sanitization.ts": {
|
||||
"value": 70.15,
|
||||
"value": 58.46,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/upstreamTimeouts.ts": {
|
||||
"value": 33,
|
||||
"value": 27.5,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/comboContextCache.ts": {
|
||||
"value": 13.62,
|
||||
"value": 11.35,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/idempotency.ts": {
|
||||
"value": 42.82,
|
||||
"value": 35.68,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/responseHeaders.ts": {
|
||||
"value": 62.7,
|
||||
"value": 52.25,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/executorHelpers.ts": {
|
||||
"value": 70.39,
|
||||
"value": 58.66,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/memoryExtraction.ts": {
|
||||
"value": 62.06,
|
||||
"value": 51.72,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/nonStreamingSse.ts": {
|
||||
"value": 72.82,
|
||||
"value": 60.68,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/passthroughToolNames.ts": {
|
||||
"value": 66.42,
|
||||
"value": 55.35,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/headers.ts": {
|
||||
"value": 94.29,
|
||||
"value": 78.58,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/logTruncation.ts": {
|
||||
"value": 77.64,
|
||||
"value": 64.7,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/memorySkillsInjection.ts": {
|
||||
"value": 13.49,
|
||||
"value": 11.24,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/semanticCache.ts": {
|
||||
"value": 60.16,
|
||||
"value": 50.13,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"mutationScore.open-sse/handlers/chatCore/telemetryHelpers.ts": {
|
||||
"value": 83.18,
|
||||
"value": 69.32,
|
||||
"direction": "up",
|
||||
"dedicatedGate": true
|
||||
},
|
||||
"rtlPhysicalClasses": {
|
||||
"value": 1011,
|
||||
"value": 1214,
|
||||
"direction": "down",
|
||||
"eps": 0,
|
||||
"_seeded_2026_07_28": "Measured by scripts/check/check-rtl-ratchet.mjs. tests/unit/ui/rtl-logical-classes.test.tsx pins four components and states it is partial (#3541); this bounds the remainder."
|
||||
@@ -407,5 +407,15 @@
|
||||
"_zizmor_rebaseline_2026_06_20_ci_build_artifact_reuse": "zizmorFindings 148 -> 152. Drift legitimo deste PR ao reutilizar o artefato next-build do job Build em package-artifact/electron-package-smoke e ao separar o build de compatibilidade Node 26: +4 unpinned-uses novos (2x actions/download-artifact@v8, actions/checkout@v7, actions/setup-node@v6). Mantida a convencao deliberada @vN dos workflows (sem SHA-pinning/manual update burden), conforme precedentes _scanner_harden_workflows_2026_06_16 e _zizmor_rebaseline_2026_06_19_*. Sem novos findings de template-injection/artipacked/cache-poisoning; medido localmente com zizmor 1.25.2 via `npm run check:workflows -- --ratchet` = 152.",
|
||||
"_cognitive_rebaseline_2026_07_27_3850_relax_v2_20pct": "cognitiveComplexity 971->1223 (+252, +26.0% over pristine 971). OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). v1 was +48 on 2026-07-27; v2 = v1 +20% buffer = +58 → +252 total (cycle 971 measured pristine → 1223 ceiling). Justification: same as complexity v2 — the v3.8.50 release cut coincides with high-merge activity; owner accepted enlarging the headroom to cover the entire PREPARE phase (5 minor cycles .50-.54) without per-PR rebaseline noise, given that re-tightening is mechanical at v3.8.51 via the combo.ts/chatCore.ts decomposition work scheduled in .51/.52 (ROADMAP.md). RE-TIGHTENING MANDATORY in v3.8.51: target 1009 (shrink of 214 from structural extraction during the decomposition campaigns, or via npm run quality:ratchet -- --update if natural shrink appears earlier). The 1009 floor still gives 38 units of post-tighten headroom vs the current pristine 971. Tracked via same roadmap issue as complexity v2. Window: v3.8.50 (release cut) → v3.8.54 close (RE-TIGHTEN at v3.8.51 prep merge per ROADMAP.md). Last entry unless measured regression. v1 entry retained below for audit trail.",
|
||||
"_cognitive_rebaseline_2026_07_27_3850_relax": "cognitiveComplexity 971->1019 (+48). OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). +48 covers Train 1D (+15) + headroom for 3.8.50/.51 batches. RE-TIGHTENING MANDATORY in v3.8.51: target 1009 (from combo.ts/chatCore.ts decomposition scheduled in .51/.52 per ROADMAP.md phases). Tracked via same roadmap issue as complexity. SUPERSEDED by _cognitive_rebaseline_2026_07_27_3850_relax_v2_20pct (v1 +20% buffer) — retained for audit. Last entry unless measured regression.",
|
||||
"_vuln_rebaseline_2026_08_04_9439_cve_drift": "vulnCount 10->22 (HIGH=10, MODERATE=12, measured by osv-scanner v2.3.8 in PR #9439's own CI run). This is CVE variance, not a dependency change made by this PR: `git diff upstream/release/v3.8.50 HEAD -- package.json package-lock.json` is empty — neither file was touched anywhere in this branch's history. The osv-scanner vulnerability ratchet apparently does not run on every commit landed directly to release/v3.8.50 (same 'fast-gate PR->release skips this check' pattern already documented for check:file-size, e.g. _rebaseline_2026_07_01_v3843_release_5609), so newly-disclosed CVEs in already-present transitive dependencies accumulated on the release branch and only surfaced here because this PR's rebase onto the current release/v3.8.50 tip pulled them in. This exact scenario — 'a newly-disclosed CVE in an already-present dep can trip the gate with no dependency change on your part' — is the documented expected behavior in _osv_flip_blocking_2026_06_16_v3827 above, whose prescribed remedy is 'bump the dep, or re-baseline vulnCount with justification+issue' (docs/security/SUPPLY_CHAIN.md -> 'Variância de CVE'). osv-scanner is not available in this sandbox to enumerate the exact GHSA/CVE ids and safely bump only the affected transitive deps without a broader, separately-scoped dependency-audit pass; re-baselining here unblocks this PR without masking anything introduced by it. Tracked for follow-up: a dedicated dependency-bump PR should re-tighten vulnCount back down once the specific advisories are enumerated locally with osv-scanner installed."
|
||||
"_vuln_rebaseline_2026_08_04_9439_cve_drift": "vulnCount 10->22 (HIGH=10, MODERATE=12, measured by osv-scanner v2.3.8 in PR #9439's own CI run). This is CVE variance, not a dependency change made by this PR: `git diff upstream/release/v3.8.50 HEAD -- package.json package-lock.json` is empty — neither file was touched anywhere in this branch's history. The osv-scanner vulnerability ratchet apparently does not run on every commit landed directly to release/v3.8.50 (same 'fast-gate PR->release skips this check' pattern already documented for check:file-size, e.g. _rebaseline_2026_07_01_v3843_release_5609), so newly-disclosed CVEs in already-present transitive dependencies accumulated on the release branch and only surfaced here because this PR's rebase onto the current release/v3.8.50 tip pulled them in. This exact scenario — 'a newly-disclosed CVE in an already-present dep can trip the gate with no dependency change on your part' — is the documented expected behavior in _osv_flip_blocking_2026_06_16_v3827 above, whose prescribed remedy is 'bump the dep, or re-baseline vulnCount with justification+issue' (docs/security/SUPPLY_CHAIN.md -> 'Variância de CVE'). osv-scanner is not available in this sandbox to enumerate the exact GHSA/CVE ids and safely bump only the affected transitive deps without a broader, separately-scoped dependency-audit pass; re-baselining here unblocks this PR without masking anything introduced by it. Tracked for follow-up: a dedicated dependency-bump PR should re-tighten vulnCount back down once the specific advisories are enumerated locally with osv-scanner installed.",
|
||||
"_policy": {
|
||||
"phase": "velocity",
|
||||
"since": "2026-08-30",
|
||||
"until": "4.0.0",
|
||||
"relaxPct": 20,
|
||||
"requireTighten": false,
|
||||
"monitor": "scripts/quality/baseline-headroom.mjs (nightly-release-green → baseline-headroom job)",
|
||||
"note": "Owner decision 2026-08-30: speed matters more than debt until the v4.0 LTS modularization. Every numeric baseline was loosened by relaxPct in one pass (see _relax_* note). check-quality-ratchet skips --require-tighten while this block exists; headroom is monitored nightly. At 4.0 the phase closes: re-measure on the pure tip, tighten, delete this block."
|
||||
},
|
||||
"_relax_velocity_2026_08_30": "Velocity phase (20% relaxation, until 4.0.0): eslintWarnings 0 → 1050; coverage.statements 80.8 → 67.33; coverage.lines 80.8 → 67.33; coverage.functions 86.42 → 72.02; coverage.branches 78.1 → 65.08; coverage.chatCore.lines 72.45 → 60.38; coverage.combo.lines 85.42 → 71.18; coverage.accountFallback.lines 96.78 → 80.65; coverage.auth.lines 92.55 → 77.13; coverage.routeGuard.lines 98.73 → 82.28; coverage.error.lines 92.13 → 76.78; coverage.publicCreds.lines 99.07 → 82.56; coverage.circuitBreaker.lines 95.09 → 79.24; openapiCoverage.pct 39 → 32.5; i18nUiCoverage.pct 100 → 83.33; deadExports 416 → 500; cognitiveComplexity 1197 → 1437; typeCoveragePct 92.17 → 76.81; codeqlAlerts 9 → 11; zizmorFindings 194 → 233; vulnCount 22 → 27; bundleSize 8653 → 10384; openapiBreaking 4 → 5; mutationScore.src/sse/services/auth.ts 52.57 → 43.81; mutationScore.open-sse/services/accountFallback.ts 68.38 → 56.98; mutationScore.src/server/authz/routeGuard.ts 76.08 → 63.4; mutationScore.src/shared/utils/circuitBreaker.ts 56.94 → 47.45; mutationScore.open-sse/utils/error.ts 43.83 → 36.53; mutationScore.open-sse/utils/publicCreds.ts 59.76 → 49.8; mutationScore.open-sse/services/combo/autoStrategy.ts 41.33 → 34.44; mutationScore.open-sse/services/combo/comboStructure.ts 57.82 → 48.18; mutationScore.open-sse/services/combo/validateQuality.ts 61.33 → 51.11; mutationScore.open-sse/services/combo/comboPredicates.ts 56.62 → 47.18; mutationScore.open-sse/services/combo/rrState.ts 70.88 → 59.07; mutationScore.open-sse/services/combo/shadowRouting.ts 48 → 40; mutationScore.open-sse/services/combo/targetSorters.ts 68.3 → 56.92; mutationScore.open-sse/services/combo/comboData.ts 76.94 → 64.12; mutationScore.open-sse/services/combo/quotaScoring.ts 39.73 → 33.11; mutationScore.open-sse/services/combo/quotaStrategies.ts 50.3 → 41.92; mutationScore.open-sse/handlers/chatCore/passthroughHelpers.ts 80.89 → 67.41; mutationScore.open-sse/handlers/chatCore/sanitization.ts 70.15 → 58.46; mutationScore.open-sse/handlers/chatCore/upstreamTimeouts.ts 33 → 27.5; mutationScore.open-sse/handlers/chatCore/comboContextCache.ts 13.62 → 11.35; mutationScore.open-sse/handlers/chatCore/idempotency.ts 42.82 → 35.68; mutationScore.open-sse/handlers/chatCore/responseHeaders.ts 62.7 → 52.25; mutationScore.open-sse/handlers/chatCore/executorHelpers.ts 70.39 → 58.66; mutationScore.open-sse/handlers/chatCore/memoryExtraction.ts 62.06 → 51.72; mutationScore.open-sse/handlers/chatCore/nonStreamingSse.ts 72.82 → 60.68; mutationScore.open-sse/handlers/chatCore/passthroughToolNames.ts 66.42 → 55.35; mutationScore.open-sse/handlers/chatCore/headers.ts 94.29 → 78.58; mutationScore.open-sse/handlers/chatCore/logTruncation.ts 77.64 → 64.7; mutationScore.open-sse/handlers/chatCore/memorySkillsInjection.ts 13.49 → 11.24; mutationScore.open-sse/handlers/chatCore/semanticCache.ts 60.16 → 50.13; mutationScore.open-sse/handlers/chatCore/telemetryHelpers.ts 83.18 → 69.32; rtlPhysicalClasses 1011 → 1214"
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ Mermaid sources and exported SVG/PNG diagrams referenced from the docs above. Se
|
||||
|
||||
## i18n/
|
||||
|
||||
Translated mirrors of the documentation in 43 locales. See [i18n/README.md](i18n/README.md) for the supported language list.
|
||||
Translated mirrors of the documentation in 42 locales (plus the English originals — 43 languages in total). See [i18n/README.md](i18n/README.md) for the supported language list.
|
||||
|
||||
## screenshots/
|
||||
|
||||
|
||||
@@ -17,27 +17,27 @@ It provides a single OpenAI-compatible endpoint (`/v1/*`) and routes traffic acr
|
||||
|
||||
Core capabilities:
|
||||
|
||||
- OpenAI-compatible API surface for CLI/tools (351 providers, 104 executors)
|
||||
- OpenAI-compatible API surface for CLI/tools (352 providers, 104 executors)
|
||||
- Request/response translation across provider formats
|
||||
- Model combo fallback (multi-model sequence)
|
||||
- Structured combo steps (`provider + model + connection`) with runtime ordering by `compositeTiers`
|
||||
- Account-level fallback (multi-account per provider)
|
||||
- Quota preflight and quota-aware P2C account selection in the main chat path
|
||||
- OAuth + API-key provider connection management (22 OAuth provider modules)
|
||||
- Embedding generation via `/v1/embeddings` (6 providers, 9 models)
|
||||
- Embedding generation via `/v1/embeddings` (18 providers)
|
||||
- Image generation via `/v1/images/generations` (10+ providers, 20+ models)
|
||||
- Audio transcription via `/v1/audio/transcriptions` (7 providers)
|
||||
- Audio transcription via `/v1/audio/transcriptions` (18 providers)
|
||||
- Text-to-speech via `/v1/audio/speech` (24 built-in providers)
|
||||
- Video generation via `/v1/videos/generations` (ComfyUI + SD WebUI)
|
||||
- Music generation via `/v1/music/generations` (ComfyUI)
|
||||
- Web search via `/v1/search` (5 providers)
|
||||
- Web search via `/v1/search` (20 providers)
|
||||
- Moderations via `/v1/moderations`
|
||||
- Reranking via `/v1/rerank`
|
||||
- Think tag parsing (`<think>...</think>`) for reasoning models
|
||||
- Response sanitization for strict OpenAI SDK compatibility
|
||||
- Role normalization (developer→system, system→user) for cross-provider compatibility
|
||||
- Structured output conversion (json_schema → Gemini responseSchema)
|
||||
- Local persistence for providers, keys, aliases, combos, settings, pricing (26 DB modules)
|
||||
- Local persistence for providers, keys, aliases, combos, settings, pricing (122 DB modules)
|
||||
- Usage/cost tracking and request logging
|
||||
- Optional cloud sync for multi-device/state sync
|
||||
- IP allowlist/blocklist for API access control
|
||||
@@ -58,7 +58,7 @@ Core capabilities:
|
||||
- Compliance audit logging with opt-out per API key
|
||||
- Eval framework for LLM quality assurance
|
||||
- Health dashboard with real-time provider circuit breaker status
|
||||
- MCP Server (87 tools) with 3 transports (stdio/SSE/Streamable HTTP)
|
||||
- MCP Server (110 tools) with 3 transports (stdio/SSE/Streamable HTTP)
|
||||
- A2A Server (JSON-RPC 2.0 + SSE) with skills and task lifecycle
|
||||
- Memory system (extraction, injection, retrieval, summarization)
|
||||
- Skills system (registry, executor, sandbox, built-in skills)
|
||||
@@ -330,14 +330,14 @@ OAuth provider modules (22 individual files under `src/lib/oauth/providers/`):
|
||||
## 5) Embedded Services (v3.8.4)
|
||||
|
||||
OmniRoute can install, supervise, and route to locally-running AI tool processes
|
||||
called **embedded services**. Two are shipped in v3.8.4: 9Router and CLIProxyAPI.
|
||||
called **embedded services**. Five are shipped: 9Router, CLIProxyAPI, Bifrost, Mux and Dario.
|
||||
|
||||
Architecture layers:
|
||||
|
||||
- **UI** (`/dashboard/providers/services`) — two-tab page with lifecycle controls,
|
||||
live log streaming, API key management, and (for 9Router) embedded native UI via
|
||||
an internal reverse proxy.
|
||||
- **API** (`/api/services/{name}/*`) — 8 endpoints for 9Router, 7 for CLIProxyAPI,
|
||||
- **API** (`/api/services/{name}/*`) — 11 endpoints for 9Router, 10 for CLIProxyAPI, 8 each for Bifrost / Mux / Dario,
|
||||
all classified **LOCAL_ONLY** (hard rule #17). A shared `GET /api/services/[name]/logs`
|
||||
SSE endpoint serves both services.
|
||||
- **Supervisor** (`src/lib/services/`) — generic `ServiceSupervisor` class wraps
|
||||
@@ -514,7 +514,7 @@ For the full stealth playbook and operational guidance, see
|
||||
Primary state DB (SQLite):
|
||||
|
||||
- Core infra: `src/lib/db/core.ts` (better-sqlite3, migrations, WAL)
|
||||
- Re-export facade: `src/lib/localDb.ts` (thin compatibility layer for callers)
|
||||
- DB access: import specific `src/lib/db/*` modules directly (the old `localDb.ts` barrel was removed)
|
||||
- file: `${DATA_DIR}/storage.sqlite` (or `$XDG_CONFIG_HOME/omniroute/storage.sqlite` when set, else `~/.omniroute/storage.sqlite`)
|
||||
- entities (tables + KV namespaces): providerConnections, providerNodes, modelAliases, combos, apiKeys, settings, pricing, **customModels**, **proxyConfig**, **ipFilter**, **thinkingBudget**, **systemPrompt**
|
||||
|
||||
@@ -879,8 +879,8 @@ flowchart LR
|
||||
|
||||
- `open-sse/translator/index.ts`: translator registry and orchestration
|
||||
- Request translators: `open-sse/translator/request/*` (9 modules — `antigravity-to-openai`, `claude-to-gemini`, `claude-to-openai`, `gemini-to-openai`, `openai-responses`, `openai-to-claude`, `openai-to-cursor`, `openai-to-gemini`, `openai-to-kiro`)
|
||||
- Response translators: `open-sse/translator/response/*` (8 modules — `claude-to-openai`, `cursor-to-openai`, `gemini-to-claude`, `gemini-to-openai`, `kiro-to-openai`, `openai-responses`, `openai-to-antigravity`, `openai-to-claude`)
|
||||
- Helpers: `open-sse/translator/helpers/*` (8 modules — `claudeHelper`, `geminiHelper`, `geminiToolsSanitizer`, `maxTokensHelper`, `openaiHelper`, `responsesApiHelper`, `schemaCoercion`, `toolCallHelper`)
|
||||
- Response translators: `open-sse/translator/response/*` (11 modules — `claude-to-openai`, `cursor-to-openai`, `gemini-to-claude`, `gemini-to-openai`, `kiro-to-openai`, `openai-responses`, `openai-to-antigravity`, `openai-to-claude`, `openai-to-gemini`, `openai-to-gemini-sse`, `responsesToolItem`)
|
||||
- Helpers: `open-sse/translator/helpers/*` (12 modules — `claudeHelper`, `geminiHelper`, `geminiToolsSanitizer`, `jsonUtil`, `markdownBoundary`, `maxTokensHelper`, `openaiHelper`, `responsesApiHelper`, `schemaCoercion`, `strictSystemHoist`, `toolCallHelper`, `toolCallShim`)
|
||||
- Format constants: `open-sse/translator/formats.ts`
|
||||
- Bootstrap and registry: `open-sse/translator/bootstrap.ts`, `open-sse/translator/registry.ts`
|
||||
- Image-format helpers: `open-sse/translator/image/`
|
||||
@@ -888,7 +888,7 @@ flowchart LR
|
||||
### Persistence
|
||||
|
||||
- `src/lib/db/*`: persistent config/state and domain persistence on SQLite
|
||||
- `src/lib/localDb.ts`: compatibility re-export for DB modules
|
||||
- `src/lib/db/*`: import specific modules directly — no barrel (the old `localDb.ts` re-export layer was removed)
|
||||
- `src/lib/usageDb.ts`: usage history/call logs facade on top of SQLite tables
|
||||
|
||||
## Provider Executor Coverage (Strategy Pattern)
|
||||
|
||||
@@ -6,7 +6,7 @@ lastUpdated: 2026-06-28
|
||||
|
||||
# OmniRoute Codebase Documentation
|
||||
|
||||
> **Version:** v3.8.0
|
||||
> **Version:** v3.8.51
|
||||
> **Last updated:** 2026-06-28
|
||||
> **Audience:** Engineers contributing to OmniRoute or building integrations on top of it.
|
||||
>
|
||||
@@ -64,7 +64,7 @@ OmniRoute/
|
||||
├── _ideia/, _references/, _mono_repo/, _tasks/ Internal scratch / planning (not shipped)
|
||||
├── CLAUDE.md Repo rules for Claude Code
|
||||
├── AGENTS.md Deeper architecture reference for agents
|
||||
├── package.json v3.8.0, workspace root
|
||||
├── package.json v3.8.51, workspace root
|
||||
└── tsconfig.json Path aliases + core compiler options
|
||||
```
|
||||
|
||||
@@ -219,7 +219,7 @@ src/app/api/services/
|
||||
Corresponding dashboard UI:
|
||||
`src/app/(dashboard)/dashboard/providers/services/` — two-tab page (CLIProxyAPI + 9Router).
|
||||
Reverse proxy for 9Router embedded UI:
|
||||
`src/app/(dashboard)/dashboard/providers/services/[name]/embed/[...path]/route.ts`
|
||||
`src/app/(dashboard)/dashboard/providers/services/[name]/embed/[[...path]]/route.ts`
|
||||
|
||||
Deep-dive: `docs/frameworks/EMBEDDED-SERVICES.md`
|
||||
|
||||
@@ -313,7 +313,7 @@ table groups the actual directories and notable top-level files.
|
||||
|
||||
Top-level files in `src/lib/`:
|
||||
|
||||
- `localDb.ts` — re-export layer only. **Never** add logic here.
|
||||
- The old `localDb.ts` barrel was removed — consumers import specific `src/lib/db/*` modules directly.
|
||||
- `proxyHealth.ts`, `proxyLogger.ts`, `tokenHealthCheck.ts`, `localHealthCheck.ts`
|
||||
- `oneproxyRotator.ts`, `oneproxySync.ts`
|
||||
- `apiBridgeServer.ts`, `cacheLayer.ts`, `semanticCache.ts`, `settingsCache.ts`
|
||||
@@ -349,10 +349,10 @@ Domain modules (each owns one or more tables): `apiKeys.ts`, `backup.ts`,
|
||||
`syncTokens.ts`, `tierConfig.ts`, `upstreamProxy.ts`, `versionManager.ts`,
|
||||
`webhooks.ts`.
|
||||
|
||||
`migrations/` holds 55 versioned `.sql` files (idempotent, transactional) and is
|
||||
`migrations/` holds 167 versioned `.sql` files (idempotent, transactional) and is
|
||||
executed by `migrationRunner.ts` at boot.
|
||||
|
||||
Tables created across the migrations (52 total):
|
||||
Tables created across the migrations (123 total):
|
||||
|
||||
`a`, `account_key_limits`, `api_keys`, `batches`, `call_logs`,
|
||||
`combo_adaptation_state`, `combos`, `command_code_auth_sessions`,
|
||||
@@ -450,12 +450,12 @@ open-sse/
|
||||
├── types.d.ts
|
||||
├── config/ Provider registries, header profiles, identity, …
|
||||
├── handlers/ Request handlers (chat, embeddings, audio, image, …)
|
||||
├── executors/ 108 provider-specific HTTP executors
|
||||
├── executors/ 104 provider-specific HTTP executors
|
||||
├── translator/ Format conversion (OpenAI ↔ Claude ↔ Gemini ↔ Cursor ↔ Kiro)
|
||||
├── transformer/ Responses API ↔ Chat Completions stream transformer
|
||||
├── services/ 80+ service modules (combos, fallback, quotas, identity, …)
|
||||
├── utils/ Streaming helpers, TLS client, AWS SigV4, proxy fetch, …
|
||||
└── mcp-server/ MCP server (3 transports, 31 scopes, 105 tools)
|
||||
└── mcp-server/ MCP server (3 transports, 33 scopes, 110 tools)
|
||||
```
|
||||
|
||||
### 4.1 `open-sse/handlers/`
|
||||
@@ -489,7 +489,7 @@ open-sse/
|
||||
(shared identity helper) and `index.ts` (registry).
|
||||
|
||||
> Note: providers not listed here are served by `default.ts` using the generic
|
||||
> OpenAI-compatible executor. The full provider catalog (351 providers) lives in
|
||||
> OpenAI-compatible executor. The full provider catalog (352 providers) lives in
|
||||
> `src/shared/constants/providers.ts`.
|
||||
|
||||
### 4.3 `open-sse/translator/`
|
||||
@@ -538,11 +538,11 @@ Highlights (full list under `open-sse/services/`):
|
||||
|
||||
### 4.6 `open-sse/mcp-server/`
|
||||
|
||||
- **31 registered tools** wired in `server.ts` (12 scoped under `schemas/tools.ts`,
|
||||
5 compression tools, 3 memory tools, 4 skills tools, plus advanced tools added
|
||||
through `advancedTools.ts`).
|
||||
- **110 unique tools** wired in `server.ts` (45 canonical in `schemas/tools.ts` +
|
||||
memory, skills, GitHub-skills, pool, gamification, plugin, Notion, Obsidian,
|
||||
local-corpus and compression modules — union counted by `countUniqueMcpTools`).
|
||||
- **3 transports**: stdio, HTTP Streamable, SSE.
|
||||
- **31 scopes** declared in `src/shared/constants/mcpScopes.ts`.
|
||||
- **33 scopes** enforced at runtime — base list in `src/shared/constants/mcpScopes.ts`, full set is the union of the scopes declared by each tool module.
|
||||
- Audit table: `mcp_tool_audit` (populated by `audit.ts`).
|
||||
- Files: `server.ts`, `index.ts`, `httpTransport.ts`, `audit.ts`, `scopeEnforcement.ts`,
|
||||
`runtimeHeartbeat.ts`, `descriptionCompressor.ts`, `schemas/{tools, a2a, audit, index}.ts`,
|
||||
@@ -585,7 +585,7 @@ electron/
|
||||
├── main.js Electron main process
|
||||
├── preload.js Preload bridge (contextIsolation enabled)
|
||||
├── types.d.ts
|
||||
├── package.json electron-builder config, version 3.8.0
|
||||
├── package.json electron-builder config, version 3.8.51
|
||||
├── README.md
|
||||
├── assets/ Build resources (icons, entitlements, …)
|
||||
├── node_modules/ Dedicated node_modules (better-sqlite3, electron-updater)
|
||||
@@ -637,7 +637,7 @@ Two binaries are exposed in `package.json` → `bin`:
|
||||
| `tests/unit/` | Unit tests via Node native test runner (1821 files, plus `api/`, `auth/`, `authz/` subdirs) |
|
||||
| `tests/integration/` | Cross-module + DB-state tests |
|
||||
| `tests/e2e/` | Playwright UI tests |
|
||||
| `tests/protocols-e2e/` | MCP/A2A protocol e2e |
|
||||
| `tests/e2e/protocol-clients.test.ts` | MCP/A2A protocol e2e |
|
||||
| `tests/translator/` | Translator-specific tests |
|
||||
| `tests/security/` | Security regressions |
|
||||
| `tests/load/` | Load / stress tests |
|
||||
@@ -759,7 +759,7 @@ See [RESILIENCE_GUIDE.md](./RESILIENCE_GUIDE.md) and the dedicated section in
|
||||
2. Export CRUD functions for your domain.
|
||||
3. If new tables: add a migration under `src/lib/db/migrations/`, numbered
|
||||
sequentially, idempotent, transactional.
|
||||
4. Re-export from `src/lib/localDb.ts` (re-export only — **no logic**).
|
||||
4. Importers use direct imports from `@/lib/db/yourModule` (no barrel — the old `localDb.ts` re-export layer was removed).
|
||||
5. Add tests under `tests/unit/`.
|
||||
|
||||
### Add a new MCP tool
|
||||
@@ -790,7 +790,7 @@ See [A2A-SERVER.md § Adding a New Skill](../frameworks/A2A-SERVER.md). Skills l
|
||||
- **TypeScript**: `strict: false` (legacy posture). Prefer explicit types over
|
||||
inference for cross-module boundaries.
|
||||
- **Database**: never write raw SQL in routes or handlers — always go through
|
||||
`src/lib/db/` modules. Never add logic to `src/lib/localDb.ts`.
|
||||
`src/lib/db/` modules. Never barrel-import — use specific `src/lib/db/*` modules directly.
|
||||
- **DB-entity typing (#3512)**: a function that writes or reads a DB table's
|
||||
row shape should take/return a named TS interface mirroring that table's
|
||||
columns 1:1, not `any` or an inline anonymous type at the call site. Land
|
||||
@@ -824,7 +824,7 @@ See [A2A-SERVER.md § Adding a New Skill](../frameworks/A2A-SERVER.md). Skills l
|
||||
## 12. Hard Rules (from CLAUDE.md)
|
||||
|
||||
1. Never commit secrets or credentials.
|
||||
2. Never add logic to `src/lib/localDb.ts`.
|
||||
2. Never barrel-import — use specific `src/lib/db/*` modules directly.
|
||||
3. Never use `eval()` / `new Function()` / implied eval.
|
||||
4. Never commit directly to `main`.
|
||||
5. Never write raw SQL in routes — always go through `src/lib/db/` modules.
|
||||
|
||||
@@ -9,13 +9,13 @@ It describes each gate, what it validates, which CI job it runs in, whether it u
|
||||
a ratchet baseline or a pass/fail policy, and whether it blocks the build or is advisory.
|
||||
|
||||
For a short summary and the allowlist policy, see the "Quality Gates & Ratchets" section
|
||||
in `CLAUDE.md`. For the critical assessment, maturity classification, and tool-agnostic
|
||||
in `AGENTS.md`. For the critical assessment, maturity classification, and tool-agnostic
|
||||
replication plan of the same system, see the
|
||||
[Quality Gate Playbook](../ops/QUALITY_GATE_PLAYBOOK.md).
|
||||
|
||||
---
|
||||
|
||||
## Gate Inventory (~50 scripts)
|
||||
## Gate Inventory (~90 scripts)
|
||||
|
||||
Scripts live under `scripts/check/` (policy gates) and `scripts/quality/` (ratchet engine).
|
||||
The CI source of truth is `.github/workflows/ci.yml`.
|
||||
@@ -57,47 +57,50 @@ assertion weakening and other masking remain owned by the independently blocking
|
||||
|
||||
Runs on every PR to `main`. Blocks merge on failure.
|
||||
|
||||
| Script (`npm run ...`) | Validates | Blocking |
|
||||
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
|
||||
| `check:node-runtime` | Node.js version is within the supported range | Yes |
|
||||
| `check:cycles` | Circular imports — all `src/` + `open-sse/` modules | Yes |
|
||||
| `check:route-validation:t06` | Zod schemas present on all routes (Tier 6 policy) | Yes |
|
||||
| `check:any-budget:t11` | `@ts-expect-error // any` count does not exceed budget (Tier 11 catraca) | Yes |
|
||||
| `check:provider-consistency` | Every provider in `providers.ts` has a matching entry in `providerRegistry.ts` (and vice-versa, within the allowlist) | Yes |
|
||||
| `check:model-lifecycle` | The two hand-maintained routing tables do not point at retired models (#11503): `FITNESS_TABLE` (`taskFitness.ts`) scores no routable retired id, every `BUILT_IN_ALIASES` target is a live catalog model, and every retired id the catalog still routes is either forwarded or listed in `allowedRetiredInCatalog`. Offline — compares against the vendor snapshot `config/quality/model-lifecycle.json`, refreshed by hand with `npm run quality:refresh-model-lifecycle` (network; not wired into CI). `allowedRetiredInCatalog` is a burn-down ratchet: add an entry only with a tracking issue. | Yes |
|
||||
| `check:fetch-targets` | Every `fetch("/api/...")` in client-side `src/` resolves to a real `route.ts` | Yes |
|
||||
| `check:deps` | All `npm install`-able deps across every `package.json` in the repo are in `dependency-allowlist.json`; new unpinned or slopsquatted packages flagged | Yes |
|
||||
| `audit:deps` | `npm audit` (root + electron) — no high/critical advisories (overlaps osv `check:vuln-ratchet`; see Rationalization Backlog) | Yes |
|
||||
| `check:lockfile` | `package-lock.json` integrity — https registry, integrity hashes, no host overrides | Yes |
|
||||
| `check:licenses` | SPDX license allowlist for production dependencies | Yes |
|
||||
| `check:tracked-artifacts` | No build artifacts / committed `node_modules` symlinks (also runs in husky pre-commit; pre-push is intentionally light — #6716) | Yes |
|
||||
| `check:file-size` | No source file exceeds the per-extension cap (ratchet: frozen large files in `frozen` list) | Yes |
|
||||
| `check:error-helper` | Error responses in executors/handlers use `buildErrorBody()` / `sanitizeErrorMessage()` (Hard Rule #12) | Yes |
|
||||
| `check:migration-numbering` | Migration SQL files are sequentially numbered, no gaps or duplicates | Yes |
|
||||
| `check:public-creds` | No literal OAuth `client_id`/`client_secret` or Firebase Web keys outside `publicCreds.ts` (Hard Rule #11) | Yes |
|
||||
| `check:db-rules` | No raw SQL outside `src/lib/db/` modules; no barrel-imports from `localDb.ts` (Hard Rules #2/#5) | Yes |
|
||||
| `check:known-symbols` | Provider executors, routing strategies, and translators registered in their dispatch tables match the files on disk — no orphaned or undeclared symbols | Yes |
|
||||
| `check:route-guard-membership` | Every route that spawns a child process is classified by `isLocalOnlyPath()` (Hard Rules #15/#17) | Yes |
|
||||
| `check:test-discovery` | Every `*.test.ts` / `*.spec.ts` file in the repo is collected by at least one test runner (ratchet: orphan list in `test-discovery-baseline.json` can only shrink) | Yes |
|
||||
| `check:docs-sync` | CHANGELOG version, OpenAPI version, and `llm.txt` are in sync | Yes |
|
||||
| `typecheck:core` | TypeScript compilation without errors (advisory warnings only) | Yes |
|
||||
| `typecheck:noimplicit:core` | Strict `noImplicitAny` — forward-looking; many pre-existing call sites still need annotations | **Advisory** (`continue-on-error: true`) |
|
||||
| `check:dashboard-typecheck` | `tsc` scoped to `src/app/(dashboard)/**` (#7033) — `typecheck:core`'s curated 27-file allowlist does not include any dashboard TSX, and `next build` never type-checks it either (`next.config.mjs` sets `ignoreBuildErrors: true`), so orphaned-identifier regressions there (#6625/#6909) were invisible to CI. Diffs against a frozen per-file/per-TS-code count baseline (`config/quality/dashboard-typecheck-baseline.json`, same stale-enforcement pattern as `check:known-symbols`) — only NEW errors beyond the baselined count fail the gate; ratchet down with `--update` when a pre-existing error is fixed. | Yes |
|
||||
| Script (`npm run ...`) | Validates | Blocking |
|
||||
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
|
||||
| `check:node-runtime` | Node.js version is within the supported range | Yes |
|
||||
| `check:cycles` | Circular imports — all `src/` + `open-sse/` modules | Yes |
|
||||
| `check:route-validation:t06` | Zod schemas present on all routes (Tier 6 policy) | Yes |
|
||||
| `check:any-budget:t11` | `@ts-expect-error // any` count does not exceed budget (Tier 11 catraca) | Yes |
|
||||
| `check:provider-consistency` | Every provider in `providers.ts` has a matching entry in `providerRegistry.ts` (and vice-versa, within the allowlist) | Yes |
|
||||
| `check:model-lifecycle` | The two hand-maintained routing tables do not point at retired models (#11503): `FITNESS_TABLE` (`taskFitness.ts`) scores no routable retired id, every `BUILT_IN_ALIASES` target is a live catalog model, and every retired id the catalog still routes is either forwarded or listed in `allowedRetiredInCatalog`. Offline — compares against the vendor snapshot `config/quality/model-lifecycle.json`, refreshed by hand with `npm run quality:refresh-model-lifecycle` (network; not wired into CI). `allowedRetiredInCatalog` is a burn-down ratchet: add an entry only with a tracking issue. | Yes |
|
||||
| `check:fetch-targets` | Every `fetch("/api/...")` in client-side `src/` resolves to a real `route.ts` | Yes |
|
||||
| `check:deps` | All `npm install`-able deps across every `package.json` in the repo are in `dependency-allowlist.json`; new unpinned or slopsquatted packages flagged | Yes |
|
||||
| `audit:deps` | `npm audit` (root + electron) — no high/critical advisories (overlaps osv `check:vuln-ratchet`; see Rationalization Backlog) | Yes |
|
||||
| `check:lockfile` | `package-lock.json` integrity — https registry, integrity hashes, no host overrides | Yes |
|
||||
| `check:licenses` | SPDX license allowlist for production dependencies | Yes |
|
||||
| `check:tracked-artifacts` | No build artifacts / committed `node_modules` symlinks (also runs in husky pre-commit; pre-push is intentionally light — #6716) | Yes |
|
||||
| `check:file-size` | No source file exceeds the per-extension cap (ratchet: frozen large files in `frozen` list) | Yes |
|
||||
| `check:error-helper` | Error responses in executors/handlers use `buildErrorBody()` / `sanitizeErrorMessage()` (Hard Rule #12) | Yes |
|
||||
| `check:migration-numbering` | Migration SQL files are sequentially numbered, no gaps or duplicates | Yes |
|
||||
| `check:public-creds` | No literal OAuth `client_id`/`client_secret` or Firebase Web keys outside `publicCreds.ts` (Hard Rule #11) | Yes |
|
||||
| `check:db-rules` | No raw SQL outside `src/lib/db/` modules; no barrel-imports from `localDb.ts` (Hard Rules #2/#5) | Yes |
|
||||
| `check:known-symbols` | Provider executors, routing strategies, and translators registered in their dispatch tables match the files on disk — no orphaned or undeclared symbols | Yes |
|
||||
| `check:route-guard-membership` | Every route that spawns a child process is classified by `isLocalOnlyPath()` (Hard Rules #15/#17) | Yes |
|
||||
| `check:test-discovery` | Every `*.test.ts` / `*.spec.ts` file in the repo is collected by at least one test runner (ratchet: orphan list in `test-discovery-baseline.json` can only shrink) | Yes |
|
||||
| `check:agent-skills-sync` | Generated agent-skills artifacts match their source catalog (no drift) |
|
||||
| `check:provider-asset-provenance` | Provider logos/assets carry a recorded provenance entry |
|
||||
| `lint:json` | JSON config files parse and satisfy the repo lint rules |
|
||||
| `typecheck:core` | TypeScript compilation without errors (advisory warnings only) | Yes |
|
||||
| `typecheck:noimplicit:core` | Strict `noImplicitAny` — forward-looking; many pre-existing call sites still need annotations | **Advisory** (`continue-on-error: true`) |
|
||||
| `check:dashboard-typecheck` | `tsc` scoped to `src/app/(dashboard)/**` (#7033) — `typecheck:core`'s curated 27-file allowlist does not include any dashboard TSX, and `next build` never type-checks it either (`next.config.mjs` sets `ignoreBuildErrors: true`), so orphaned-identifier regressions there (#6625/#6909) were invisible to CI. Diffs against a frozen per-file/per-TS-code count baseline (`config/quality/dashboard-typecheck-baseline.json`, same stale-enforcement pattern as `check:known-symbols`) — only NEW errors beyond the baselined count fail the gate; ratchet down with `--update` when a pre-existing error is fixed. | Yes |
|
||||
|
||||
### Job: `quality-gate`
|
||||
|
||||
Runs after `test-coverage`. Blocks merge on failure.
|
||||
|
||||
| Script | Validates | Blocking |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------- |
|
||||
| `quality:collect` | Emits `quality-metrics.json` (ESLint warning count, coverage from merged shard report) | Yes (upstream of ratchet) |
|
||||
| `quality:ratchet` | Each metric in `quality-baseline.json` has not regressed (ESLint warnings ≤ baseline; coverage ≥ baseline) | Yes |
|
||||
| `check:duplication` | Code duplication (jscpd@4) does not exceed baseline in `quality-baseline.json` | Yes |
|
||||
| `check:complexity` | File-level cyclomatic complexity does not exceed the cap (core ESLint `complexity` + `max-lines-per-function`) | Yes |
|
||||
| `check:cognitive-complexity` | Cognitive complexity ratchet (`eslint-plugin-sonarjs`) — separate ESLint pass; mergeable with `check:complexity` (see Backlog) | Yes |
|
||||
| `check:dead-code` | Unused exports / files ratchet (knip) does not regress vs baseline | Yes |
|
||||
| `check:type-coverage` | Percent-typed ratchet (`type-coverage`) does not regress; largely subsumes `typecheck:noimplicit:core` | Yes |
|
||||
| `check:codeql-ratchet` | Open CodeQL alert count does not regress (reads via `gh api`; graceful-skip without token) | Yes |
|
||||
| Script | Validates | Blocking |
|
||||
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
|
||||
| `quality:collect` | Emits `quality-metrics.json` (ESLint warning count, coverage from merged shard report) | Yes (upstream of ratchet) |
|
||||
| `quality:ratchet` | Each metric in `quality-baseline.json` has not regressed (ESLint warnings ≤ baseline; coverage ≥ baseline) | Yes |
|
||||
| `check:duplication` | Code duplication (jscpd@4) does not exceed baseline in `quality-baseline.json` | Yes |
|
||||
| `check:complexity` | File-level cyclomatic complexity does not exceed the cap (core ESLint `complexity` + `max-lines-per-function`) | Yes |
|
||||
| `check:cognitive-complexity` | Cognitive complexity ratchet (`eslint-plugin-sonarjs`) — separate ESLint pass; CI runs both merged as the single `check:complexity-ratchets` step | Yes |
|
||||
| `check:dead-code` | Unused exports / files ratchet (knip) does not regress vs baseline | Yes |
|
||||
| `check:compression-budget` | Compression benchmark budget — per-engine token-savings floors must not regress | Yes |
|
||||
| `check:type-coverage` | Percent-typed ratchet (`type-coverage`) does not regress; largely subsumes `typecheck:noimplicit:core` | Yes |
|
||||
| `check:codeql-ratchet` | Open CodeQL alert count does not regress (reads via `gh api`; graceful-skip without token) | Yes |
|
||||
|
||||
### Job: `quality-extended`
|
||||
|
||||
@@ -208,7 +211,7 @@ Runs after `build`. Blocks merge on failure.
|
||||
|
||||
| Suite | Validates | Blocking |
|
||||
| ---------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| `test:vitest` | MCP server (109 tools), autoCombo, cache — vitest runner | Yes |
|
||||
| `test:vitest` | MCP server (110 tools), autoCombo, cache — vitest runner | Yes |
|
||||
| `test:vitest:ui` | UI component tests — vitest runner | **Blocking** — pre-existing failures are explicitly excluded in `vitest.config.ts`; new failures fail the job |
|
||||
|
||||
### Nightly workflows (scheduled, advisory)
|
||||
@@ -221,9 +224,77 @@ These run on a cron schedule (and `workflow_dispatch`), never on PRs. All are ad
|
||||
| `nightly-resilience` | heap-growth gate, chaos fault-injection, k6 load/soak | **Advisory** |
|
||||
| `nightly-llm-security` | promptfoo injection guard (block mode) + garak probes (skipped without a provider secret) | **Advisory** |
|
||||
| `nightly-schemathesis` | OpenAPI contract fuzzing (schemathesis) against a live OmniRoute using `docs/openapi.yaml` — surfaces spec violations / unhandled 500s (Fase 8 B.4) | **Advisory** |
|
||||
| `nightly-mutation` | Stryker mutation-testing score over the fast unit lane — surviving mutants surface weak asserts | **Advisory** |
|
||||
| `nightly-compat` | Node engine compatibility matrix across the supported `engines.node` ranges | **Advisory** |
|
||||
|
||||
---
|
||||
|
||||
## Velocity phase (2026-08-30 → v4.0 LTS): every baseline loosened by 20%
|
||||
|
||||
Owner decision (2026-08-30): until the v4.0 modularization, shipping speed matters more
|
||||
than holding the debt line. Every **numeric** ratchet baseline was loosened by 20% in one
|
||||
auditable pass, and the phase is declared in `config/quality/quality-baseline.json`:
|
||||
|
||||
```json
|
||||
"_policy": { "phase": "velocity", "since": "2026-08-30", "until": "4.0.0",
|
||||
"relaxPct": 20, "requireTighten": false }
|
||||
```
|
||||
|
||||
| What changed | Where |
|
||||
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
||||
| `metrics.*.value` — lower-is-better counts ×1.2, higher-is-better percentages ÷1.2 (coverage floor 60 kept, `eslintErrors` stays 0, `eslintWarnings` 0 → 20% of the frozen suppression count) | `quality-baseline.json` (`_relax_velocity_2026_08_30` note lists every before → after) |
|
||||
| `count` ×1.2 / `percentage` ×1.2 | `complexity-baseline.json`, `duplication-baseline.json` |
|
||||
| `cap`, `testCap`, every `frozen[*]` / `testFrozen[*]` line cap ×1.2 | `file-size-baseline.json` |
|
||||
| per-file / per-TS-code counts ×1.2 | `api-typecheck-baseline.json`, `dashboard-typecheck-baseline.json`, `open-sse-typecheck-baseline.json` |
|
||||
| `THRESHOLD` 36 → 30 | `scripts/check/check-openapi-coverage.mjs` |
|
||||
| `--require-tighten` becomes advisory while `_policy.requireTighten === false` | `scripts/quality/check-quality-ratchet.mjs` |
|
||||
| nightly `bank-ratchet-shrinks` pauses (it would bank the measured shrink and undo the headroom) | `.github/workflows/nightly-release-green.yml` |
|
||||
|
||||
Allowlists (`eslint-suppressions.json`, `test-masking-allowlist.json`, `test-discovery-baseline.json`,
|
||||
…) are **not** budgets and were not touched. Pass/fail policy gates (secrets, SQL rules,
|
||||
docs/env contract, i18n parity, unit tests) are unchanged — a red test is still a red test.
|
||||
|
||||
**Tooling**
|
||||
|
||||
- `npm run quality:relax-baselines -- --pct 20 --note velocity_YYYY_MM_DD [--dry-run]` — the
|
||||
one-shot relaxation (`scripts/quality/relax-baselines.mjs`); refuses to run twice with the
|
||||
same note.
|
||||
- `npm run quality:headroom [-- --only deadExports,fileSize] [--json out.json --md out.md]` —
|
||||
measures every numeric gate the way CI does and prints the remaining headroom per gate
|
||||
(`scripts/quality/baseline-headroom.mjs`). The nightly `baseline-headroom` job posts the
|
||||
table to the living issue **📈 Baseline headroom (velocity phase)** and adds the
|
||||
`headroom-alert` label when any gate is within 10% of its cap or already over it. That issue
|
||||
is the early warning: a budget that fills in days means the relaxation is being consumed by
|
||||
a few PRs, not by the whole team — look at the offending gate's `_rebaseline_*` notes.
|
||||
|
||||
**New-code mode (Clean-as-You-Code) — since 2026-08-30, PR fast-path only**
|
||||
|
||||
On `pull_request` events `quality.yml` passes `--base-ref <PR base SHA>` to `check:file-size`,
|
||||
`check:complexity-ratchets` and `check:dead-code`. In that mode the gate compares HEAD with the
|
||||
merge-base **restricted to the files the PR touched** (`scripts/check/newCodeMode.mjs`: the
|
||||
merge-base is materialized in a throwaway `git worktree`, ESLint/knip run there and on HEAD, the
|
||||
per-file counts are diffed):
|
||||
|
||||
- **blocking** — the PR added cyclomatic/cognitive violations or dead exports in files it changed
|
||||
(`complexityNewCode=`, `cognitiveComplexityNewCode=`, `deadExportsNewCode=` in the log);
|
||||
- **advisory** — the global total vs. the frozen baseline. Inherited drift never reds an
|
||||
innocent PR; the drift is re-frozen at release reconciliation and watched by the headroom job.
|
||||
|
||||
`workflow_dispatch` runs, the release-green sweep and the nightly headroom job have no PR base
|
||||
and keep the absolute (global) comparison. Coverage, duplication and type-coverage stay global
|
||||
for now (their tools do not produce a per-file diff cheaply) — candidates for the same treatment.
|
||||
|
||||
**Closing the phase at v4.0 (LTS = tighter than before, not "back to normal")**
|
||||
|
||||
1. On the pure `release/v4.0.0` tip: `npm run quality:headroom --json` for the record, then
|
||||
`npm run quality:ratchet -- --update`, `check:file-size --update`,
|
||||
`check:complexity-ratchets --update`, `check:dead-code --update`, each typecheck gate's
|
||||
`--update` — every baseline drops to the measured value.
|
||||
2. Delete `_policy` from `quality-baseline.json` (re-arms `--require-tighten` and the nightly
|
||||
banking), restore `THRESHOLD = 36` (or higher) in `check-openapi-coverage.mjs`.
|
||||
3. Tighten beyond measured where the modularization paid off: file-size `cap` back to 1000
|
||||
(or 800), coverage floors +5, dead exports 0 for the modularized packages.
|
||||
|
||||
## Ratchet Baseline (`quality-baseline.json`)
|
||||
|
||||
The ratchet engine (`scripts/quality/check-quality-ratchet.mjs`) reads `quality-baseline.json`
|
||||
|
||||
@@ -21,7 +21,7 @@ OmniRoute/
|
||||
├── bin/ # CLI entry point and command handlers
|
||||
├── scripts/ # Build, check, sync, and one-off scripts
|
||||
├── docs/ # Public documentation (you are here)
|
||||
├── tests/ # All test suites (unit, integration, e2e, protocols-e2e)
|
||||
├── tests/ # All test suites (unit, integration, e2e, protocol clients)
|
||||
├── public/ # Next.js static assets, PWA manifest, service worker, icons
|
||||
├── config/ # Static config + quality-gate state (i18n, payloadRules, quality/)
|
||||
├── images/ # Marketing / README image assets
|
||||
@@ -117,11 +117,11 @@ OmniRoute/
|
||||
```
|
||||
src/
|
||||
├── app/ # App Router (pages + API routes + status pages + landing)
|
||||
├── lib/ # Core libraries / domain modules (~50 subdirs + ~30 top-level files)
|
||||
├── lib/ # Core libraries / domain modules (80 subdirs + ~70 top-level files)
|
||||
├── domain/ # Pure domain logic (policy engine, fallback, cost, lockout, comboResolver, assessment)
|
||||
├── server/ # Server-only modules (authz pipeline, cors, auth middleware) — cannot import from client
|
||||
├── shared/ # Shared between server and client where safe (constants, types, validation, contracts, utils)
|
||||
├── i18n/ # next-intl config + per-locale message JSON (30+ locales)
|
||||
├── i18n/ # next-intl config + per-locale message JSON (43 locales)
|
||||
├── middleware/ # Next.js middleware (request enrichment, locale detection)
|
||||
├── mitm/ # MITM proxy core: cert gen/install, handlers, targets, inspector, masks, passthrough
|
||||
│ ├── handlers/ # 9 IDE-agent handler classes extending MitmHandlerBase (antigravity, kiro, copilot, codex, cursor, zed, claudeCode, openCode, trae)
|
||||
@@ -148,9 +148,8 @@ src/
|
||||
| `app/api/tools/traffic-inspector/` | Traffic Inspector REST + WS API — 16+ routes (requests, sessions, hosts, capture-modes, export, ws). LOCAL_ONLY + SPAWN_CAPABLE. See `docs/frameworks/TRAFFIC_INSPECTOR.md §8`. |
|
||||
| `app/a2a/` | A2A JSON-RPC 2.0 entry point (`POST /a2a`) |
|
||||
| `app/.well-known/agent.json/` | A2A Agent Card (discovery) |
|
||||
| `app/(dashboard)/dashboard/` | Dashboard UI pages (~35 pages: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, activity, etc.) |
|
||||
| `app/(dashboard)/dashboard/` | Dashboard UI pages (50+ sections, ~118 page.tsx files: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, activity, etc.) |
|
||||
| `app/(dashboard)/dashboard/search-tools/` | Search Tools Studio UI (3 tabs: Search/Scrape/Compare + SearchConceptCard + ProviderCatalog) — see `docs/frameworks/SEARCH_TOOLS_STUDIO.md` |
|
||||
| `app/(dashboard)/dashboard/` | Dashboard UI pages (~30 pages: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, etc.) |
|
||||
| `app/(dashboard)/dashboard/memory/` | Memory Studio (plan 21): `page.tsx` (3-tab shell), `components/` (MemoryConceptCard, MemoryEngineStatus, EmbeddingSourceSelector, EditMemoryModal, RetrievePreview, QdrantConfigCard, RerankConfigCard), `components/tabs/` (MemoriesTab, PlaygroundTab, EngineTab), `hooks/` (useEngineStatus, useMemorySettings) |
|
||||
| `app/(dashboard)/dashboard/tools/agent-bridge/` | AgentBridge dashboard page — server card, 9 agent cards, setup wizard, model mapping, bypass list. i18n PT-BR + EN. See `docs/frameworks/AGENTBRIDGE.md`. |
|
||||
| `app/(dashboard)/dashboard/tools/traffic-inspector/` | Traffic Inspector dashboard page — DevTools split, 7 detail tabs, 4 capture mode toggles, session recorder, context colorization. i18n PT-BR + EN. See `docs/frameworks/TRAFFIC_INSPECTOR.md`. |
|
||||
@@ -181,7 +180,7 @@ src/
|
||||
| `compliance/` | Audit log + provider audit — see `docs/security/COMPLIANCE.md` |
|
||||
| `compression/` | Compression engine glue (engines live in `open-sse/services/compression/`) |
|
||||
| `config/` | Runtime config helpers |
|
||||
| `db/` | 95+ domain DB modules + 148 migrations (always go through here for SQLite) |
|
||||
| `db/` | 120+ domain DB modules + 167 migrations (always go through here for SQLite) |
|
||||
| `quota/` | Quota Sharing Engine: `dimensions.ts` (types/Zod), `types.ts` (QuotaStore interface), `sqliteQuotaStore.ts`, `redisQuotaStore.ts`, `storeFactory.ts`, `fairShare.ts`, `burnRate.ts`, `planResolver.ts`, `planRegistry.ts`, `saturationSignals.ts`, `enforce.ts`, `spendRecorder.ts` — see `docs/routing/QUOTA_SHARE.md` |
|
||||
| `radar/` | Radar free-model catalog client: `feedSchema.ts`, `pinnedKeys.ts`, `verify.ts`, `sync.ts`, `applyFeed.ts`, `index.ts` (`getRadarCatalog()`) — see `docs/frameworks/RADAR.md` |
|
||||
| `display/` | UI formatting helpers (cost, latency, etc.) |
|
||||
@@ -208,7 +207,7 @@ src/
|
||||
| `cacheLayer.ts`, `idempotencyLayer.ts` | Request caching + idempotency |
|
||||
| (~30 more top-level files) | Specialized helpers (logEnv, modelsDevSync, piiSanitizer, etc.) |
|
||||
|
||||
### `src/db/` — Database (117 modules + 148 migrations)
|
||||
### `src/lib/db/` — Database (122 modules + 167 migrations)
|
||||
|
||||
| Subdir | Purpose |
|
||||
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -220,16 +219,16 @@ src/
|
||||
|
||||
### `src/domain/`
|
||||
|
||||
| Module | Purpose |
|
||||
| ---------------------- | ----------------------------------------------------------------------- |
|
||||
| `policy.ts` | Policy engine |
|
||||
| `fallbackPolicy.ts` | Fallback decision tree |
|
||||
| `costRules.ts` | Cost calculation rules |
|
||||
| `lockoutPolicy.ts` | Model/connection lockout policy |
|
||||
| `tagRouter.ts` | Tag-based routing |
|
||||
| `comboResolver.ts` | Combo resolution (used by combo engine) |
|
||||
| `modelAvailability.ts` | Per-model availability check |
|
||||
| `assessment/` | Model assessment (Phase 1 of RFC-AUTO-ASSESSMENT — see `docs/archive/`) |
|
||||
| Module | Purpose |
|
||||
| ---------------------- | -------------------------------------------------- |
|
||||
| `policy.ts` | Policy engine |
|
||||
| `fallbackPolicy.ts` | Fallback decision tree |
|
||||
| `costRules.ts` | Cost calculation rules |
|
||||
| `lockoutPolicy.ts` | Model/connection lockout policy |
|
||||
| `tagRouter.ts` | Tag-based routing |
|
||||
| `comboResolver.ts` | Combo resolution (used by combo engine) |
|
||||
| `modelAvailability.ts` | Per-model availability check |
|
||||
| `assessment/` | Model assessment (Phase 1 of RFC-AUTO-ASSESSMENT ) |
|
||||
|
||||
### `src/server/`
|
||||
|
||||
@@ -243,7 +242,7 @@ src/
|
||||
|
||||
| Module | Purpose |
|
||||
| -------------------------------- | ---------------------------------------------------------------------- |
|
||||
| `constants/providers.ts` | **338 providers** with Zod validation (source of truth) |
|
||||
| `constants/providers.ts` | **352 providers** with Zod validation (source of truth) |
|
||||
| `constants/cliTools.ts` | External CLI tool registry |
|
||||
| `constants/routingStrategies.ts` | **19 routing strategies** with priorities |
|
||||
| `constants/publicApiRoutes.ts` | Routes that require Bearer (vs management) auth |
|
||||
@@ -270,7 +269,7 @@ open-sse/
|
||||
├── translator/ # Format converters (9 request, 9 response, 9 helpers)
|
||||
├── transformer/ # Responses API ↔ Chat Completions (TransformStream)
|
||||
├── services/ # ~80+ service modules (combo, accountFallback, autoCombo, reasoningCache, claude code/chatgpt stealth, modelDeprecation, taskAwareRouter, workflowFSM, etc.)
|
||||
├── mcp-server/ # MCP server (109 tools, 3 transports, 33 scopes)
|
||||
├── mcp-server/ # MCP server (110 tools, 3 transports, 33 scopes)
|
||||
├── config/ # Provider/model registries, header config, model aliases
|
||||
├── utils/ # TLS client, proxy fetch/dispatcher, network helpers
|
||||
├── index.ts # Workspace entry
|
||||
@@ -379,7 +378,7 @@ open-sse/
|
||||
|
||||
---
|
||||
|
||||
## `docs/` — Public Documentation (44 files + 4 subdirs)
|
||||
## `docs/` — Public Documentation (7 root files + 17 subdirs)
|
||||
|
||||
### Top-level guides
|
||||
|
||||
@@ -392,7 +391,7 @@ open-sse/
|
||||
| `API_REFERENCE.md` | API endpoint reference with auth model |
|
||||
| `openapi.yaml` | OpenAPI 3.0 spec (121 paths) |
|
||||
| `SETUP_GUIDE.md` | Install methods (npm, npx, Docker, Electron, Termux, source) |
|
||||
| `ENVIRONMENT.md` | All env vars (~219 used in code, ~810 lines `.env.example`) |
|
||||
| `ENVIRONMENT.md` | All env vars (~800 documented, ~3,050 lines `.env.example`) |
|
||||
| `TROUBLESHOOTING.md` | Common errors + v3.8.0 known issues |
|
||||
| `RELEASE_CHECKLIST.md` | Full release flow (skills, husky, conventional commits, deploy) |
|
||||
| `COVERAGE_PLAN.md` | Coverage goals and current state |
|
||||
@@ -400,13 +399,13 @@ open-sse/
|
||||
| `CLI-TOOLS.md` | External CLI integrations + Internal OmniRoute CLI |
|
||||
| `I18N.md` | i18n architecture, adding a language, 43 locales |
|
||||
| `UNINSTALL.md` | Clean uninstall steps |
|
||||
| `PROVIDER_REFERENCE.md` | **Auto-generated** catalog of 338 providers (regen: `npm run gen:provider-reference`) |
|
||||
| `PROVIDER_REFERENCE.md` | **Auto-generated** catalog of 352 providers (regen: `npm run gen:provider-reference`) |
|
||||
|
||||
### Subsystem deep-dives
|
||||
|
||||
| Doc | Purpose |
|
||||
| -------------------------- | ------------------------------------------------------------------- |
|
||||
| `MCP-SERVER.md` | MCP server: 109 tools, 3 transports, 33 scopes, REST endpoints |
|
||||
| `MCP-SERVER.md` | MCP server: 110 tools, 3 transports, 33 scopes, REST endpoints |
|
||||
| `A2A-SERVER.md` | A2A v0.3: JSON-RPC, 6 skills, REST helpers, agent card |
|
||||
| `AGENT_PROTOCOLS_GUIDE.md` | Unified guide: A2A vs ACP vs Cloud Agents |
|
||||
| `CLOUD_AGENT.md` | Codex Cloud / Devin / Jules orchestration |
|
||||
@@ -450,8 +449,7 @@ open-sse/
|
||||
|
||||
| Subdir | Purpose |
|
||||
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `docs/archive/` | Archived/historical docs (e.g., `RFC-AUTO-ASSESSMENT-DRAFT.md` — superseded by EVALS) |
|
||||
| `docs/i18n/` | Localized doc translations (~42 locales) |
|
||||
| `docs/i18n/` | Localized doc translations (42 locales) |
|
||||
| `docs/screenshots/` | Image assets for guides |
|
||||
| `_tasks/superpowers/` | Plans/specs from superpowers (`writing-plans`/`brainstorming`) + research — isolated, separately-versioned repo, gitignored by the main tree. See CLAUDE.md → "Planning & Research Artifacts". |
|
||||
|
||||
@@ -459,13 +457,13 @@ open-sse/
|
||||
|
||||
## `tests/` — Test Suites
|
||||
|
||||
| Subdir | Type | Runner |
|
||||
| ---------------------- | --------------------------------------- | --------------------------------------- |
|
||||
| `tests/unit/` | Unit tests (~500 files, fastest) | Node native test runner |
|
||||
| `tests/integration/` | Multi-module + DB integration tests | Node native test runner (concurrency 1) |
|
||||
| `tests/e2e/` | UI + workflow E2E | Playwright |
|
||||
| `tests/protocols-e2e/` | MCP + A2A real-client E2E | Custom protocol clients |
|
||||
| `tests/ecosystem/` | Provider integration (network-touching) | Node native test runner |
|
||||
| Subdir | Type | Runner |
|
||||
| ------------------------------------ | --------------------------------------- | --------------------------------------- |
|
||||
| `tests/unit/` | Unit tests (~4,800 files, fastest) | Node native test runner |
|
||||
| `tests/integration/` | Multi-module + DB integration tests | Node native test runner (concurrency 1) |
|
||||
| `tests/e2e/` | UI + workflow E2E | Playwright |
|
||||
| `tests/e2e/protocol-clients.test.ts` | MCP + A2A real-client E2E | Custom protocol clients |
|
||||
| `tests/e2e/ecosystem.test.ts` | Provider integration (network-touching) | Node native test runner |
|
||||
|
||||
---
|
||||
|
||||
@@ -505,7 +503,7 @@ Shipped configuration templates plus the committed quality-gate baselines
|
||||
| ---------------------------------- | -------------------------------------------------------------- |
|
||||
| `.github/workflows/` | GitHub Actions CI/CD workflows (lint, test, coverage, release) |
|
||||
| `.github/ISSUE_TEMPLATE/` | Bug/feature issue templates |
|
||||
| `.github/PULL_REQUEST_TEMPLATE.md` | PR template |
|
||||
| `.github/pull_request_template.md` | PR template |
|
||||
| `.github/dependabot.yml` | Dependency update config |
|
||||
|
||||
---
|
||||
|
||||
@@ -11,33 +11,33 @@ Objective feature comparison vs popular open-source AI routers.
|
||||
> **Methodology**: Public repos audited 2026-Q2. Versions as listed.
|
||||
> Submit corrections via PR — we want this to be accurate.
|
||||
|
||||
| Feature | OmniRoute 3.8 | LiteLLM 1.x | OpenRouter (SaaS) | Portkey |
|
||||
| -------------------------------------------------- | :----------------------------------------------: | :------------: | :---------------: | :---------: |
|
||||
| **Providers** | **329** | ~100 | ~50 | ~30 |
|
||||
| **Free/no-auth catalog entries** | **155** | n/a | passthrough | n/a |
|
||||
| **Self-hostable** | ✅ | ✅ | ❌ | ⚠ paid |
|
||||
| **OAuth catalog entries** | **23** | partial | ❌ | ❌ |
|
||||
| **Auto-fallback combos** | **19 strategies** | priority-based | tier-based | weighted |
|
||||
| **Fusion (parallel panel + judge synthesis)** | ✅ | ❌ | ❌ | ❌ |
|
||||
| **Tier 1/2/3 fallback (subscription→cheap→free)** | ✅ + UI | manual | n/a | manual |
|
||||
| **Token compression** | 12-engine stack (RTK + Caveman + LLMLingua) | none | none | none |
|
||||
| **Multimodal generation (speech/music/video)** | ✅ | ❌ | passthrough | ❌ |
|
||||
| **Built-in MCP server** | ✅ 107 tools, 32 scopes | ❌ | ❌ | ❌ |
|
||||
| **A2A protocol** | ✅ 6 skills | ❌ | ❌ | ❌ |
|
||||
| **Memory (FTS5 + vector)** | ✅ | ❌ | ❌ | ❌ |
|
||||
| **Guardrails (PII, injection, vision)** | ✅ | partial | ❌ | ✅ paid |
|
||||
| **Cloud agent integrations** | Codex, Cursor, Devin, Jules | ❌ | ❌ | ❌ |
|
||||
| **Circuit breaker per provider** | ✅ 3-state, lazy recovery | basic | ❌ | ✅ |
|
||||
| **TLS fingerprint stealth (JA3/JA4)** | ✅ wreq-js | ❌ | ❌ | ❌ |
|
||||
| **Eval framework** | ✅ built-in | ❌ | ❌ | ⚠ paid |
|
||||
| **MITM proxy (intercepts Cursor/Antigravity)** | ✅ cross-platform | ❌ | ❌ | ❌ |
|
||||
| **CLI with system tray (no Electron)** | ✅ | ❌ | n/a | n/a |
|
||||
| **CLI machine-ID auto-auth** | ✅ | ❌ | n/a | n/a |
|
||||
| **Dashboard** | Next.js 16 | basic | proprietary | proprietary |
|
||||
| **i18n** | **43 locales** | ❌ | ❌ | ⚠ |
|
||||
| **Public agent skills (SKILL.md)** | ✅ 45 | ❌ | ❌ | ❌ |
|
||||
| **Tunnel support (Cloudflared, Tailscale, Ngrok)** | ✅ | ❌ | n/a | n/a |
|
||||
| **License** | MIT | MIT | proprietary | proprietary |
|
||||
| Feature | OmniRoute 3.8 | LiteLLM 1.x | OpenRouter (SaaS) | Portkey |
|
||||
| -------------------------------------------------- | :-----------------------------------------: | :------------: | :---------------: | :---------: |
|
||||
| **Providers** | **329** | ~100 | ~50 | ~30 |
|
||||
| **Free/no-auth catalog entries** | **155** | n/a | passthrough | n/a |
|
||||
| **Self-hostable** | ✅ | ✅ | ❌ | ⚠ paid |
|
||||
| **OAuth catalog entries** | **23** | partial | ❌ | ❌ |
|
||||
| **Auto-fallback combos** | **19 strategies** | priority-based | tier-based | weighted |
|
||||
| **Fusion (parallel panel + judge synthesis)** | ✅ | ❌ | ❌ | ❌ |
|
||||
| **Tier 1/2/3 fallback (subscription→cheap→free)** | ✅ + UI | manual | n/a | manual |
|
||||
| **Token compression** | 12-engine stack (RTK + Caveman + LLMLingua) | none | none | none |
|
||||
| **Multimodal generation (speech/music/video)** | ✅ | ❌ | passthrough | ❌ |
|
||||
| **Built-in MCP server** | ✅ 110 tools, 33 scopes | ❌ | ❌ | ❌ |
|
||||
| **A2A protocol** | ✅ 6 skills | ❌ | ❌ | ❌ |
|
||||
| **Memory (FTS5 + vector)** | ✅ | ❌ | ❌ | ❌ |
|
||||
| **Guardrails (PII, injection, vision)** | ✅ | partial | ❌ | ✅ paid |
|
||||
| **Cloud agent integrations** | Codex, Cursor, Devin, Jules | ❌ | ❌ | ❌ |
|
||||
| **Circuit breaker per provider** | ✅ 3-state, lazy recovery | basic | ❌ | ✅ |
|
||||
| **TLS fingerprint stealth (JA3/JA4)** | ✅ wreq-js | ❌ | ❌ | ❌ |
|
||||
| **Eval framework** | ✅ built-in | ❌ | ❌ | ⚠ paid |
|
||||
| **MITM proxy (intercepts Cursor/Antigravity)** | ✅ cross-platform | ❌ | ❌ | ❌ |
|
||||
| **CLI with system tray (no Electron)** | ✅ | ❌ | n/a | n/a |
|
||||
| **CLI machine-ID auto-auth** | ✅ | ❌ | n/a | n/a |
|
||||
| **Dashboard** | Next.js 16 | basic | proprietary | proprietary |
|
||||
| **i18n** | **43 locales** | ❌ | ❌ | ⚠ |
|
||||
| **Public agent skills (SKILL.md)** | ✅ 45 | ❌ | ❌ | ❌ |
|
||||
| **Tunnel support (Cloudflared, Tailscale, Ngrok)** | ✅ | ❌ | n/a | n/a |
|
||||
| **License** | MIT | MIT | proprietary | proprietary |
|
||||
|
||||
## When to choose OmniRoute
|
||||
|
||||
|
||||
@@ -30,10 +30,10 @@ inside GitHub's `<img>` sandbox:
|
||||
| File | Used in | Notes |
|
||||
| ------------------------------------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [tier-cascade.svg](./tier-cascade.svg) | README.md (root) | Animated 4-tier auto-fallback cascade (16s loop, 4 acts). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [pool-fair-share.svg](./pool-fair-share.svg) | README.md (root) | Animated key-pool fair-share quota (generous → strict, 16s loop). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [pool-fair-share.svg](./pool-fair-share.svg) | — (unused) | Animated key-pool fair-share quota (generous → strict, 16s loop). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [combo-always-on.svg](./combo-always-on.svg) | style reference | Animated priority-combo fallback (4 layers, 16s loop). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [cli-terminal.svg](./cli-terminal.svg) | README.md (root) | Compact half-height animated terminal (1200×350): 3 real CLI commands cycling with typewriter + scrolling subcommand ticker; first frame = completed providers screen. Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [compression-pipeline.svg](./compression-pipeline.svg) | README.md (root) | Animated 10-engine compression funnel (8s loop). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [compression-pipeline.svg](./compression-pipeline.svg) | README.md (root) | Animated 12-engine compression funnel (8s loop). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [free-tier-budget.svg](./free-tier-budget.svg) | README.md (root) | Animated free-tier budget card (~1.51B/mo quantified headline, 20-pool budget bar, per-pool grid, signup credits, 10s loop). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [readme-hero.svg](./readme-hero.svg) | README.md (root) | Animated hero card (tagline, live provider/free-access headline, full-width compression bar demo, 6 stat chips). Edit the SVG directly — there is no `.mmd` source. |
|
||||
| [promise-pillars.svg](./promise-pillars.svg) | README.md (root) | Animated "The Promise" 6-pillar card (12s border-highlight sweep). Edit the SVG directly — there is no `.mmd` source. |
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (351 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over 85 top-level commands: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
|
||||
<svg viewBox="0 0 1200 350" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Animated terminal demoing the OmniRoute CLI: omniroute providers list (352 providers registered, anthropic, codex, glm, kimi shown active), omniroute combo list (always-on priority, cost-saver, fusion-panel, context-relay) and omniroute health (healthy, 18412 requests in 24h, p95 412ms, circuit breakers 24 closed, 1 half-open, 0 open), cycling over 86 top-level commands: providers, oauth, keys, combo, nodes, models, cache, compression, cost, usage, quota, health, resilience, telemetry, logs, audit, mcp, a2a, cloud, memory, skills, eval, doctor, repl, tunnel, backup, sync, webhooks, policy, pricing, translator, simulate and more.">
|
||||
<desc>Compact animated terminal cycling three real OmniRoute CLI commands with a typewriter effect and a scrolling subcommand ticker; the first frame shows the completed providers-list screen.</desc>
|
||||
<defs><clipPath id="tickerClip"><rect x="12" y="304" width="1176" height="40"/></clipPath><clipPath id="tw0"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;31;61;92;122;153;184;214;245;245" keyTimes="0;0.012;0.018;0.024;0.030;0.036;0.042;0.048;0.054;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw1"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;26;51;76;102;128;153;178;204;204" keyTimes="0;0.348;0.351;0.357;0.363;0.369;0.375;0.381;0.387;1" dur="18s" repeatCount="indefinite"/></rect></clipPath><clipPath id="tw2"><rect x="64" y="46" height="26" width="0"><animate attributeName="width" calcMode="discrete" values="0;20;41;61;82;102;122;143;163;163" keyTimes="0;0.678;0.684;0.690;0.696;0.702;0.708;0.714;0.720;1" dur="18s" repeatCount="indefinite"/></rect></clipPath></defs>
|
||||
<rect width="1200" height="350" fill="#0d1117"/>
|
||||
<rect x="0" y="0" width="1200" height="34" fill="#161b22"/>
|
||||
<path d="M 0 34 L 1200 34" stroke="#ffffff" stroke-opacity="0.08" stroke-width="1"/>
|
||||
<circle cx="24" cy="17" r="6" fill="#ff5f56"/><circle cx="46" cy="17" r="6" fill="#ffbd2e"/><circle cx="68" cy="17" r="6" fill="#27c93f"/>
|
||||
<text x="600" y="22" text-anchor="middle" font-family="Consolas, 'Courier New', monospace" font-size="13" fill="#71717a">omniroute — 85 top-level commands</text>
|
||||
<g font-family="Consolas, 'Courier New', monospace" font-size="17"><animate attributeName="opacity" values="1;0;0" keyTimes="0;0.006;1" dur="18s" repeatCount="indefinite"/><text x="64" y="66" fill="#F7F6FC">omniroute providers list</text><text x="40" y="100" font-weight="700" fill="#38bdf8">OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa">1f3a9c2e  anthropic   Claude Max 20x    <tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa">8c2d5b1a  codex       Codex Pro (team)  <tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa">f4e0a97b  glm         GLM Coding Plan   <tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa">03bd6e5f  kimi        Kimi K2 free      <tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a">… 347 more providers</text></g><g opacity="1" font-family="Consolas, 'Courier New', monospace" font-size="17">
|
||||
<text x="600" y="22" text-anchor="middle" font-family="Consolas, 'Courier New', monospace" font-size="13" fill="#71717a">omniroute — 86 top-level commands</text>
|
||||
<g font-family="Consolas, 'Courier New', monospace" font-size="17"><animate attributeName="opacity" values="1;0;0" keyTimes="0;0.006;1" dur="18s" repeatCount="indefinite"/><text x="64" y="66" fill="#F7F6FC">omniroute providers list</text><text x="40" y="100" font-weight="700" fill="#38bdf8">OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa">1f3a9c2e  anthropic   Claude Max 20x    <tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa">8c2d5b1a  codex       Codex Pro (team)  <tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa">f4e0a97b  glm         GLM Coding Plan   <tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa">03bd6e5f  kimi        Kimi K2 free      <tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a">… 348 more providers</text></g><g opacity="1" font-family="Consolas, 'Courier New', monospace" font-size="17">
|
||||
<animate attributeName="opacity" values="1;1;0;0" keyTimes="0;0.315;0.33;1" dur="18s" repeatCount="indefinite"/>
|
||||
<text x="40" y="66" fill="#22c55e">$</text>
|
||||
<g clip-path="url(#tw0)"><text x="64" y="66" fill="#F7F6FC">omniroute providers list</text></g>
|
||||
@@ -14,7 +14,7 @@
|
||||
<animate attributeName="x" calcMode="discrete" values="64;95;125;156;186;217;248;278;309;309" keyTimes="0.000;0.012;0.018;0.024;0.030;0.036;0.042;0.048;0.054;1" dur="18s" repeatCount="indefinite"/>
|
||||
<animate attributeName="opacity" values="0;0;1;0.2;1;0.2;1;0;0" keyTimes="0;0.011;0.012;0.022;0.032;0.042;0.052;0.074;1" dur="18s" repeatCount="indefinite"/>
|
||||
</rect>
|
||||
<text x="40" y="100" font-weight="700" fill="#38bdf8" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.045;0.047" dur="18s" repeatCount="indefinite"/>OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.053;0.055" dur="18s" repeatCount="indefinite"/>1f3a9c2e  anthropic   Claude Max 20x    <tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.061;0.063" dur="18s" repeatCount="indefinite"/>8c2d5b1a  codex       Codex Pro (team)  <tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.069;0.07100000000000001" dur="18s" repeatCount="indefinite"/>f4e0a97b  glm         GLM Coding Plan   <tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.077;0.079" dur="18s" repeatCount="indefinite"/>03bd6e5f  kimi        Kimi K2 free      <tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.085;0.08700000000000001" dur="18s" repeatCount="indefinite"/>… 347 more providers</text>
|
||||
<text x="40" y="100" font-weight="700" fill="#38bdf8" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.045;0.047" dur="18s" repeatCount="indefinite"/>OmniRoute Providers</text><text x="40" y="128" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.053;0.055" dur="18s" repeatCount="indefinite"/>1f3a9c2e  anthropic   Claude Max 20x    <tspan fill='#22c55e'>active</tspan></text><text x="40" y="154" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.061;0.063" dur="18s" repeatCount="indefinite"/>8c2d5b1a  codex       Codex Pro (team)  <tspan fill='#22c55e'>active</tspan></text><text x="40" y="180" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.069;0.07100000000000001" dur="18s" repeatCount="indefinite"/>f4e0a97b  glm         GLM Coding Plan   <tspan fill='#22c55e'>active</tspan></text><text x="40" y="206" fill="#a1a1aa" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.077;0.079" dur="18s" repeatCount="indefinite"/>03bd6e5f  kimi        Kimi K2 free      <tspan fill='#22c55e'>active</tspan></text><text x="40" y="232" fill="#71717a" opacity="0"><animate attributeName="opacity" calcMode="discrete" values="0;0;1" keyTimes="0;0.085;0.08700000000000001" dur="18s" repeatCount="indefinite"/>… 348 more providers</text>
|
||||
</g><g opacity="0" font-family="Consolas, 'Courier New', monospace" font-size="17">
|
||||
<animate attributeName="opacity" values="0;0;1;1;0;0" keyTimes="0;0.333;0.34800000000000003;0.648;0.663;1" dur="18s" repeatCount="indefinite"/>
|
||||
<text x="40" y="66" fill="#22c55e">$</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 351 providers, 90+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 43 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<svg viewBox="0 0 1200 780" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Comparison table: OmniRoute versus 9router, OpenRouter, CLIProxyAPI and LiteLLM across 13 capabilities. OmniRoute is the only one with the full set: 352 providers, 150+ free providers built-in, 19 routing strategies, 12-engine token compression, a built-in MCP server with 110 tools, A2A protocol, persistent memory, guardrails, cloud agents, TLS fingerprint stealth, desktop/Termux/PWA, 43 UI locales and 100% MIT self-hosted. 9router has free providers, RTK compression and translation but no MCP, A2A, memory, guardrails, cloud agents or stealth. OpenRouter is a hosted SaaS with 400+ models, guardrails and a hosted MCP but is not self-hosted and lacks A2A, memory, cloud agents and stealth. CLIProxyAPI is a light OAuth proxy with two routing strategies. LiteLLM has 100+ providers, A2A and extensive guardrails but no memory, compression, free tier, stealth or cloud agents.">
|
||||
<desc>Static-header comparison table where each capability row fades in top to bottom; the OmniRoute column is highlighted and shows a check or a leading value in every row, while competitors show a mix of checks, partials and crosses.</desc>
|
||||
<defs>
|
||||
<pattern id="gC" width="32" height="32" patternUnits="userSpaceOnUse"><path d="M 32 0 L 0 0 0 32" fill="none" stroke="#ffffff" stroke-opacity="0.05" stroke-width="1"/></pattern>
|
||||
@@ -23,7 +23,7 @@
|
||||
<g font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif">
|
||||
<g opacity="0"><animate attributeName="opacity" values="0;1" dur="0.4s" begin="0.15s" fill="freeze"/>
|
||||
<text x="44" y="196" font-size="14.5" fill="#c9d1d9">Providers</text>
|
||||
<text x="440" y="196" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="15" font-weight="800" fill="#7ee787">350</text>
|
||||
<text x="440" y="196" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="15" font-weight="800" fill="#7ee787">352</text>
|
||||
<text x="604" y="196" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="13.5" font-weight="600" fill="#8b949e">40+</text>
|
||||
<text x="760" y="196" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="13.5" font-weight="600" fill="#8b949e">400+*</text>
|
||||
<text x="916" y="196" text-anchor="middle" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="13.5" font-weight="600" fill="#8b949e">~5</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -1,5 +1,5 @@
|
||||
%% Database schema overview (selected core tables)
|
||||
%% Reflects: src/lib/db/* (95+ modules, 110+ migrations)
|
||||
%% Reflects: src/lib/db/* (120+ modules, 167 migrations)
|
||||
%% v3.8.0
|
||||
erDiagram
|
||||
api_keys ||--o{ api_key_usage : tracks
|
||||
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 842" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute free-tier budget: about 1.51 billion free tokens per month steady, up to about 2.13 billion in the first month with signup credits. The catalog contains 445 rows, 438 active and 7 discontinued, grouped into 39 recurring pool keys; 20 pools have a published positive monthly token budget and 19 have a zero, uncapped, or keyless budget. Honest pool-deduped math counts each shared free pool once; 13 providers carry a terms-of-service avoid flag. The 20 quantified pools are Mistral 1 billion, LLM7 150 million, Nara 150 million, Gemini 60 million, Cerebras 30 million, Cloudflare AI 30 million, API Airforce 24 million, Ollama Cloud 20 million, Groq 15 million, Bluesminds 7.2 million, SambaNova 6 million, Arcee 4.8 million, Navy 4.5 million, BazaarLink 3.6 million, OpenRouter 1.2 million, Cohere 800 thousand, HuggingChat 500 thousand, Morph 400 thousand, Hugging Face 200 thousand, and Kiro 25 thousand. One-time signup credits add about 626 million. Uncapped providers and the OpenRouter top-up boost are shown separately so they do not inflate the headline. Live usage remains available at /dashboard/free-tiers.">
|
||||
<svg viewBox="0 0 1200 842" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute free-tier budget: about 1.51 billion free tokens per month steady, up to about 2.13 billion in the first month with signup credits. The catalog contains 446 rows, 439 active and 7 discontinued, grouped into 38 recurring pool keys; 20 pools have a published positive monthly token budget and 18 have a zero, uncapped, or keyless budget. Honest pool-deduped math counts each shared free pool once; 13 providers carry a terms-of-service avoid flag. The 20 quantified pools are Mistral 1 billion, LLM7 150 million, Nara 150 million, Gemini 60 million, Cerebras 30 million, Cloudflare AI 30 million, API Airforce 24 million, Ollama Cloud 20 million, Groq 15 million, Bluesminds 7.2 million, SambaNova 6 million, Arcee 4.8 million, Navy 4.5 million, BazaarLink 3.6 million, OpenRouter 1.2 million, Cohere 800 thousand, HuggingChat 500 thousand, Morph 400 thousand, Hugging Face 200 thousand, and Kiro 25 thousand. One-time signup credits add about 626 million. Uncapped providers and the OpenRouter top-up boost are shown separately so they do not inflate the headline. Live usage remains available at /dashboard/free-tiers.">
|
||||
<desc>Pool-deduplicated chart of the 20 recurring free-token pools with positive published budgets, plus signup credits and uncapped providers shown separately.</desc>
|
||||
<defs>
|
||||
<pattern id="gridPaperF" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
@@ -64,7 +64,7 @@
|
||||
<text x="60" y="228" font-family="Consolas, 'Courier New', monospace" font-size="104" font-weight="800" fill="url(#gradBrandF)">~1.51B</text>
|
||||
<text x="62" y="266" font-family="Consolas, 'Courier New', monospace" font-size="15" letter-spacing="3" font-weight="700" fill="#a1a1aa">FREE TOKENS / MONTH · <tspan fill="#22c55e">STEADY</tspan></text>
|
||||
<text x="62" y="298" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="16" fill="#F7F6FC">up to <tspan font-weight="800" fill="#22c55e">~2.13B</tspan> in your first month — signup credits</text>
|
||||
<text x="62" y="326" font-family="Consolas, 'Courier New', monospace" font-size="12" fill="#71717a">documented free tiers · <tspan fill="#8b5cf6">39 recurring pools</tspan> · <tspan fill="#8b5cf6">445 catalog entries</tspan> · one endpoint</text>
|
||||
<text x="62" y="326" font-family="Consolas, 'Courier New', monospace" font-size="12" fill="#71717a">documented free tiers · <tspan fill="#8b5cf6">38 recurring pools</tspan> · <tspan fill="#8b5cf6">446 catalog entries</tspan> · one endpoint</text>
|
||||
|
||||
<!-- ═══ Panel · The honest math ═══ -->
|
||||
<rect x="680" y="84" width="460" height="216" rx="14" fill="#161b22" stroke="#ffffff" stroke-opacity="0.08" stroke-width="1"/>
|
||||
@@ -78,7 +78,7 @@
|
||||
<text x="704" y="240" font-family="Consolas, 'Courier New', monospace" font-size="34" font-weight="800" fill="#22c55e">~1.51B</text>
|
||||
<text x="836" y="224" font-family="Consolas, 'Courier New', monospace" font-size="11.5" fill="#a1a1aa">each shared free pool</text>
|
||||
<text x="836" y="244" font-family="Consolas, 'Courier New', monospace" font-size="11.5" fill="#22c55e">counted once ✓</text>
|
||||
<text x="704" y="280" font-family="Consolas, 'Courier New', monospace" font-size="12" fill="#f59e0b"><tspan font-weight="800">14 providers</tspan> ToS-flagged <tspan fill="#71717a">— we flag it · you decide</tspan></text>
|
||||
<text x="704" y="280" font-family="Consolas, 'Courier New', monospace" font-size="12" fill="#f59e0b"><tspan font-weight="800">13 providers</tspan> ToS-flagged <tspan fill="#71717a">— we flag it · you decide</tspan></text>
|
||||
|
||||
<!-- ═══ Budget bar · 20 quantified recurring pools ═══ -->
|
||||
<text x="60" y="356" font-family="Consolas, 'Courier New', monospace" font-size="10.5" letter-spacing="2.5" font-weight="700" fill="#a78bfa">WHERE IT COMES FROM · <tspan fill="#F7F6FC">20 QUANTIFIED RECURRING POOLS</tspan></text>
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -5,7 +5,7 @@ flowchart LR
|
||||
Src["Source MDs<br/>(CLAUDE.md, docs/**/*.md)"] --> Hash["sha256 hash"]
|
||||
Hash --> State[".i18n-state.json"]
|
||||
State -->|diff| Dirty["Mark source dirty"]
|
||||
Dirty --> Loop{"For each locale<br/>(39 langs)"}
|
||||
Dirty --> Loop{"For each locale<br/>(43 langs)"}
|
||||
Loop --> LLM["OmniRoute<br/>/chat/completions<br/>(cx/gpt-5.4-mini)"]
|
||||
LLM --> Target["Write<br/>docs/i18n/<locale>/<rel-path>.md"]
|
||||
Target --> State
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
%% MCP Server tool inventory by category
|
||||
%% Reflects: open-sse/mcp-server/ — TOTAL_MCP_TOOL_COUNT in server.ts (countUniqueMcpTools)
|
||||
%% Unique contributions: MCP_TOOLS 42 + memory 3 + skill 4 + githubSkill 3 + pool 6
|
||||
%% + gamification 8 + plugin 8 + notion 6 + obsidian 22 + localCorpus 3 + compression 2 = 107
|
||||
%% Unique contributions: MCP_TOOLS 45 + memory 3 + skill 4 + githubSkill 3 + pool 6
|
||||
%% + gamification 8 + plugin 8 + notion 6 + obsidian 22 + localCorpus 3 + compression 2 = 110
|
||||
%% (agentSkillTools fully overlaps the base registry; 11 of the 13 compression
|
||||
%% tools live in the base registry — duplicates counted once)
|
||||
%% Regenerate the SVG with: npm run docs:render-diagrams
|
||||
%% v3.8.50
|
||||
%% v3.8.51
|
||||
flowchart LR
|
||||
MCP["MCP Server<br/>107 tools total"]
|
||||
MCP --> Core["Base registry (42)<br/>routing, cache, compression, quota,<br/>proxy, web search/fetch, tool search"]
|
||||
MCP["MCP Server<br/>110 tools total"]
|
||||
MCP --> Core["Base registry (45)<br/>routing, cache, compression, quota,<br/>proxy, web search/fetch, tool search"]
|
||||
MCP --> Mem["Memory (3)<br/>memory_search, memory_add,<br/>memory_clear"]
|
||||
MCP --> Skl["Skills (4)<br/>skills_list, skills_enable,<br/>skills_execute, skills_executions<br/>+ agent-skill catalog discovery"]
|
||||
MCP --> GSkl["GitHub-Skills (3)<br/>search, scan, install"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint and 351 providers. Six pillars. Resilient fallback: automatic routing continues while another healthy target is available. Save up to 95 percent of eligible tokens: RTK plus Caveman stacked compression averages about 89 percent on tool-heavy sessions. Zero dollars to start: 90+ providers with a free tier and 56 recurring or keyless free-forever providers. Every tool works: 35 CLI and agent integration records, including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity, through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation at /v1. Production controls: circuit breakers, TLS stealth, MCP with 110 tools, A2A, memory, guardrails, evals, and 39,000+ static test declarations across 5,100+ tracked test files.">
|
||||
<svg viewBox="0 0 1200 540" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="The OmniRoute promise: one endpoint and 352 providers. Six pillars. Resilient fallback: automatic routing continues while another healthy target is available. Save up to 95 percent of eligible tokens: RTK plus Caveman stacked compression averages about 89 percent on tool-heavy sessions. Zero dollars to start: 150+ providers with a free tier and 53 recurring or keyless free-forever providers. Every tool works: 36 CLI and agent integration records, including Claude Code, Codex, Cursor, Cline, Copilot and Antigravity, through one config. One endpoint: OpenAI, Claude, Gemini and Responses API translation at /v1. Production controls: circuit breakers, TLS stealth, MCP with 110 tools, A2A, memory, guardrails, evals, and 39,000+ static test declarations across 5,100+ tracked test files.">
|
||||
<desc>Animated promise card: six pillar tiles fade in in reading order, then a soft colored border highlight sweeps from tile to tile in a continuous cycle.</desc>
|
||||
<defs>
|
||||
<pattern id="gridPaperP" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
@@ -21,7 +21,7 @@
|
||||
<line x1="150" y1="53" x2="1160" y2="53" stroke="#232b38" stroke-width="1.5"/>
|
||||
</g>
|
||||
<g>
|
||||
<text x="40" y="100" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="23" font-weight="600" fill="#c9d1d9">One endpoint. <tspan fill="#a78bfa" font-weight="800">351 providers.</tspan> Never stop building — OmniRoute picks <tspan fill="#7ee787" font-weight="700">the cheapest one that works</tspan>.</text>
|
||||
<text x="40" y="100" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="23" font-weight="600" fill="#c9d1d9">One endpoint. <tspan fill="#a78bfa" font-weight="800">352 providers.</tspan> Never stop building — OmniRoute picks <tspan fill="#7ee787" font-weight="700">the cheapest one that works</tspan>.</text>
|
||||
</g>
|
||||
|
||||
<g font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif">
|
||||
@@ -38,7 +38,7 @@
|
||||
<line x1="3.9" y1="3.9" x2="18.1" y2="18.1"/>
|
||||
</g>
|
||||
<text x="102" y="170" font-size="18" font-weight="800" fill="#74b9ff">Never hit limits</text>
|
||||
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 351 providers in</text>
|
||||
<text x="66" y="204" font-size="13.5" fill="#a1a1aa">Auto-fallback across 352 providers in</text>
|
||||
<text x="66" y="226" font-size="13.5" fill="#a1a1aa">milliseconds. Quota out? The next provider</text>
|
||||
<text x="66" y="248" font-size="13.5" fill="#a1a1aa">takes over while a healthy target remains.</text>
|
||||
</g>
|
||||
@@ -73,7 +73,7 @@
|
||||
<circle cx="6.6" cy="6.6" r="1.4" fill="#fdcb6e" stroke="none"/>
|
||||
</g>
|
||||
<text x="862" y="170" font-size="18" font-weight="800" fill="#fdcb6e">$0 to start</text>
|
||||
<text x="826" y="204" font-size="13.5" fill="#a1a1aa">90+ providers with a free tier, 55 free</text>
|
||||
<text x="826" y="204" font-size="13.5" fill="#a1a1aa">150+ providers with a free tier, 53 free</text>
|
||||
<text x="826" y="226" font-size="13.5" fill="#a1a1aa">forever — Qoder, Pollinations, Cloudflare,</text>
|
||||
<text x="826" y="248" font-size="13.5" fill="#a1a1aa">SiliconFlow… No card needed.</text>
|
||||
</g>
|
||||
@@ -91,7 +91,7 @@
|
||||
<path d="M 10,18 L 10,22"/>
|
||||
</g>
|
||||
<text x="102" y="354" font-size="18" font-weight="800" fill="#a78bfa">Every tool works</text>
|
||||
<text x="66" y="388" font-size="13.5" fill="#a1a1aa">35 CLI/agent integrations — Claude Code, Codex,</text>
|
||||
<text x="66" y="388" font-size="13.5" fill="#a1a1aa">36 CLI/agent integrations — Claude Code, Codex,</text>
|
||||
<text x="66" y="410" font-size="13.5" fill="#a1a1aa">Cursor, Cline, Copilot, Antigravity —</text>
|
||||
<text x="66" y="432" font-size="13.5" fill="#a1a1aa">through one config.</text>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg viewBox="0 0 1200 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute hero: Never stop coding. Every AI tool to 351 providers — 90+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 351 AI providers, 90+ free tiers, about 1.51B free tokens per month, 15 to 95 percent token savings, 19 routing strategies, zero dollars to start.">
|
||||
<svg viewBox="0 0 1200 548" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="OmniRoute hero: Never stop coding. Every AI tool to 352 providers — 150+ free — through one endpoint. Claude Code, Codex, Cursor, Cline, Copilot and Antigravity into free Claude, GPT and Gemini with auto-fallback. RTK + Caveman stacked compression saves 15 to 95 percent of tokens — about 89 percent average on tool-heavy sessions — so you never hit limits. Stats: 352 AI providers, 150+ free tiers, about 1.51B free tokens per month, 15 to 95 percent token savings, 19 routing strategies, zero dollars to start.">
|
||||
<desc>Animated hero card: a pulse travels the divider line and a compression bar demo repeatedly shrinks a prompt by up to 95 percent; all headline content is static and readable on the first frame.</desc>
|
||||
<defs>
|
||||
<pattern id="gridPaperH" width="32" height="32" patternUnits="userSpaceOnUse">
|
||||
@@ -28,7 +28,7 @@
|
||||
<text x="48" y="138" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="60" font-weight="800" fill="#e9edf3">Never stop coding<tspan fill="#a855f7">.</tspan></text>
|
||||
|
||||
<!-- subheadline -->
|
||||
<text x="48" y="184" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="25" font-weight="600" fill="#c9d1d9">Every AI tool → <tspan fill="#a78bfa" font-weight="800">351 providers</tspan> — <tspan fill="#7ee787" font-weight="800">90+ free</tspan> — through one endpoint.</text>
|
||||
<text x="48" y="184" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="25" font-weight="600" fill="#c9d1d9">Every AI tool → <tspan fill="#a78bfa" font-weight="800">352 providers</tspan> — <tspan fill="#7ee787" font-weight="800">150+ free</tspan> — through one endpoint.</text>
|
||||
|
||||
<!-- plug line -->
|
||||
<text x="48" y="222" font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" font-size="16.5" fill="#a1a1aa">Claude Code · Codex · Cursor · Cline · Copilot · Antigravity  →  <tspan fill="#7ee787" font-weight="700">FREE</tspan> Claude / GPT / Gemini · auto-fallback</text>
|
||||
@@ -66,7 +66,7 @@
|
||||
<!-- stat chips -->
|
||||
<g font-family="Inter, 'Segoe UI', Arial, Helvetica, system-ui, sans-serif" text-anchor="middle">
|
||||
<rect x="48" y="448" width="172" height="52" rx="12" fill="#161b22" stroke="#6c5ce7" stroke-opacity="0.55" stroke-width="1.5"/>
|
||||
<text x="134" y="471" font-size="17" font-weight="800" fill="#a78bfa">350</text>
|
||||
<text x="134" y="471" font-size="17" font-weight="800" fill="#a78bfa">352</text>
|
||||
<text x="134" y="490" font-size="11" fill="#a1a1aa">AI PROVIDERS</text>
|
||||
<rect x="234" y="448" width="172" height="52" rx="12" fill="#161b22" stroke="#22c55e" stroke-opacity="0.55" stroke-width="1.5"/>
|
||||
<text x="320" y="471" font-size="17" font-weight="800" fill="#7ee787">90+</text>
|
||||
|
||||
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
@@ -17,8 +17,8 @@ flowchart LR
|
||||
Resolve --> Single["handleSingleModel<br/>(per target)"]
|
||||
Combo -->|single| Single
|
||||
Single --> Translate["translateRequest<br/>(OpenAI↔Claude↔Gemini)"]
|
||||
Translate --> Exec["getExecutor<br/>(89 executor modules)"]
|
||||
Exec --> Upstream["Upstream Provider<br/>(329 catalog entries)"]
|
||||
Translate --> Exec["getExecutor<br/>(104 executor modules)"]
|
||||
Exec --> Upstream["Upstream Provider<br/>(352 catalog entries)"]
|
||||
Upstream --> Stream["SSE / JSON"]
|
||||
Stream --> Transformer["responsesTransformer<br/>(Responses↔Chat)"]
|
||||
Transformer --> Client
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@@ -141,16 +141,16 @@ curl -X POST http://localhost:20128/a2a \
|
||||
|
||||
OmniRoute exposes 6 A2A skills wired in `src/lib/a2a/taskExecution.ts::A2A_SKILL_HANDLERS`. Each skill module lives in `src/lib/a2a/skills/`.
|
||||
|
||||
| Skill | ID | Description | Tags | Examples |
|
||||
| :----------------- | :------------------- | :-------------------------------------------------------------------------------------------------------------- | :------------------------- | :------------------------------------- |
|
||||
| Smart Routing | `smart-routing` | Routes a prompt through the optimal provider/combo using OmniRoute's combo engine + scoring | routing, providers | "Route this prompt via the best model" |
|
||||
| Quota Management | `quota-management` | Reports per-provider quota state, helps callers decide when to throttle/switch | quota, providers | "Check quota for anthropic" |
|
||||
| Provider Discovery | `provider-discovery` | Lists installed providers with capabilities, free-tier flags, OAuth status | providers, discovery | "What providers are available?" |
|
||||
| Cost Analysis | `cost-analysis` | Estimates cost of a request/conversation given the catalog + recent usage | cost, usage | "Estimate cost for this conversation" |
|
||||
| Health Report | `health-report` | Aggregates circuit breaker, cooldown, lockout state per provider | health, resilience | "Show health status of all providers" |
|
||||
| Skill | ID | Description | Tags | Examples |
|
||||
| :----------------- | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------- | :------------------------------------- |
|
||||
| Smart Routing | `smart-routing` | Routes a prompt through the optimal provider/combo using OmniRoute's combo engine + scoring | routing, providers | "Route this prompt via the best model" |
|
||||
| Quota Management | `quota-management` | Reports per-provider quota state, helps callers decide when to throttle/switch | quota, providers | "Check quota for anthropic" |
|
||||
| Provider Discovery | `provider-discovery` | Lists installed providers with capabilities, free-tier flags, OAuth status | providers, discovery | "What providers are available?" |
|
||||
| Cost Analysis | `cost-analysis` | Estimates cost of a request/conversation given the catalog + recent usage | cost, usage | "Estimate cost for this conversation" |
|
||||
| Health Report | `health-report` | Aggregates circuit breaker, cooldown, lockout state per provider | health, resilience | "Show health status of all providers" |
|
||||
| List Capabilities | `list-capabilities` | Returns the full 45-entry Agent Skills catalog (23 API + 21 CLI + 1 config) as a markdown table with raw SKILL.md URLs for context injection | catalog, discovery, skills | "List all OmniRoute capabilities" |
|
||||
|
||||
> The Agent Card should be kept aligned with the live 329-provider catalog; provider counts and free/no-auth metadata are sourced from the runtime registry.
|
||||
> The Agent Card should be kept aligned with the live 352-provider catalog; provider counts and free/no-auth metadata are sourced from the runtime registry.
|
||||
|
||||
### `list-capabilities` Skill Detail
|
||||
|
||||
@@ -163,7 +163,7 @@ The `list-capabilities` skill is particularly useful for external agents that ne
|
||||
...
|
||||
```
|
||||
|
||||
Each row includes the `rawUrl` column so agents can immediately fetch the full SKILL.md. The `metadata.totalSkills` field is always `42`. Implementation: `src/lib/a2a/skills/listCapabilities.ts`. See also [AGENT-SKILLS.md](./AGENT-SKILLS.md).
|
||||
Each row includes the `rawUrl` column so agents can immediately fetch the full SKILL.md. The `metadata.totalSkills` field mirrors the catalog size (45 today). Implementation: `src/lib/a2a/skills/listCapabilities.ts`. See also [AGENT-SKILLS.md](./AGENT-SKILLS.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -171,13 +171,13 @@ Each row includes the `rawUrl` column so agents can immediately fetch the full S
|
||||
|
||||
The JSON-RPC endpoint `/a2a` is the canonical A2A entry point. The REST endpoints below provide auxiliary access for dashboards and external tooling:
|
||||
|
||||
| Endpoint | Method | Description | Auth |
|
||||
| :--------------------------- | :----- | :------------------------------- | :--------------------- |
|
||||
| `/api/a2a/status` | GET | Server status, registered skills | (public) |
|
||||
| `/api/a2a/tasks` | GET | List tasks with filters | management |
|
||||
| `/api/a2a/tasks/[id]` | GET | Get task by ID | management |
|
||||
| `/api/a2a/tasks/[id]/cancel` | POST | Cancel running task | management |
|
||||
| `/.well-known/agent.json` | GET | Agent Card (A2A discovery) | (public, cached 3600s) |
|
||||
| Endpoint | Method | Description | Auth |
|
||||
| :--------------------------- | :----- | :---------------------------------------------------------------- | :------------------------------------------- |
|
||||
| `/api/a2a/status` | GET | Server status, registered skills | (public) |
|
||||
| `/api/a2a/tasks` | GET | List tasks with filters | management |
|
||||
| `/api/a2a/tasks/[id]` | GET | Get task by ID | management |
|
||||
| `/api/a2a/tasks/[id]/cancel` | POST | Cancel running task | management |
|
||||
| `/.well-known/agent.json` | GET | Agent Card (A2A discovery) | (public, cached 3600s) |
|
||||
| `/api/a2a/tasks` | POST | Inbound delegation to the OmniConductor fleet (Conductor PRD RF5) | Bearer vs `OMNIROUTE_API_KEY` + `a2aEnabled` |
|
||||
|
||||
**Inbound Conductor delegation (`POST /api/a2a/tasks`):** external A2A agents delegate coding work to the OmniConductor fleet through OmniRoute. Body: `{ skill: "conductor" | "conductor-cli-<profile>", messages: [{role, content}], metadata: { conductor: { repo: { url, base_ref? }, mode?, cli?, model? } } }` — only Conductor fleet skills (the ones announced on the Agent Card) are delegable; `metadata.conductor.repo.url` is required (the fleet works on git repos). The route translates to the hub's `POST /v1/tasks` using the server-side `CONDUCTOR_ORCHESTRATOR_TOKEN` (fallback `CONDUCTOR_HUB_TOKEN`) and returns `201 { conductor_task_id, state: "submitted" }`; task states flow back through the SSE→A2A mirror (RF1) and are visible via `GET /api/a2a/tasks?skill=conductor`.
|
||||
|
||||
@@ -19,7 +19,7 @@ ACP (Agent Client Protocol) is a **"CLI-as-backend" transport** for OmniRoute. I
|
||||
| **No API keys needed** | Uses your existing CLI authentication |
|
||||
| **Native protocol** | Uses each CLI's native input/output format |
|
||||
| **Auto-discovery** | Detects installed CLIs on your system |
|
||||
| **13 built-in agents** | Pre-configured for popular CLI tools |
|
||||
| **15 built-in agents** | Pre-configured for popular CLI tools |
|
||||
| **Custom agents** | Add your own CLI tools via settings |
|
||||
| **Process management** | Handles lifecycle (spawn, send, kill) |
|
||||
|
||||
@@ -27,7 +27,7 @@ ACP (Agent Client Protocol) is a **"CLI-as-backend" transport** for OmniRoute. I
|
||||
|
||||
## Supported CLI Agents
|
||||
|
||||
ACP supports **13 built-in CLI agents** out of the box:
|
||||
ACP supports **15 built-in CLI agents** out of the box:
|
||||
|
||||
| Agent ID | Display Name | Binary | Protocol |
|
||||
| ------------- | ------------------ | ------------- | -------- |
|
||||
@@ -38,12 +38,14 @@ ACP supports **13 built-in CLI agents** out of the box:
|
||||
| `aider` | Aider | `aider` | stdio |
|
||||
| `opencode` | OpenCode | `opencode` | stdio |
|
||||
| `cline` | Cline | `cline` | stdio |
|
||||
| `qwen` | Qwen Code | `qwen --acp` | stdio |
|
||||
| `qwen` | Qwen Code | `qwen --acp` | stdio |
|
||||
| `forge` | ForgeCode | `forge` | stdio |
|
||||
| `amazon-q` | Amazon Q Developer | `q` | stdio |
|
||||
| `interpreter` | Open Interpreter | `interpreter` | stdio |
|
||||
| `cursor-cli` | Cursor CLI | `cursor` | stdio |
|
||||
| `warp` | Warp AI | `warp` | stdio |
|
||||
| `gemini` | Gemini CLI | `gemini` | stdio |
|
||||
| `zcode` | ZCode | `zcode` | stdio |
|
||||
|
||||
### Custom Agents
|
||||
|
||||
@@ -543,7 +545,7 @@ const agents = detectInstalledAgents();
|
||||
## What's Next?
|
||||
|
||||
- **[API Reference](../reference/API_REFERENCE.md)** — REST API endpoints
|
||||
- **[Provider Reference](../reference/PROVIDER_REFERENCE.md)** — All 338 providers
|
||||
- **[Provider Reference](../reference/PROVIDER_REFERENCE.md)** — All 352 providers
|
||||
- **[MCP Server](./MCP-SERVER.md)** — Model Context Protocol integration
|
||||
- **[A2A Server](./A2A-SERVER.md)** — Agent-to-Agent protocol
|
||||
- **[Cloud Agent](./CLOUD_AGENT.md)** — Cloud-based agents
|
||||
|
||||
@@ -90,13 +90,13 @@ The generator preserves content between `<!-- skill:custom-start -->` and `<!--
|
||||
|
||||
## REST API Discovery
|
||||
|
||||
| Endpoint | Method | Description | Auth |
|
||||
| :--------------------------- | :----- | :------------------------------------------------------- | :--------- |
|
||||
| Endpoint | Method | Description | Auth |
|
||||
| :--------------------------- | :----- | :--------------------------------------------------------------- | :--------- |
|
||||
| `/api/agent-skills` | GET | List catalog (optional `?category=api\|cli\|config&area=<area>`) | none |
|
||||
| `/api/agent-skills/{id}` | GET | Get single skill metadata | none |
|
||||
| `/api/agent-skills/{id}/raw` | GET | Fetch SKILL.md as `text/markdown` | none |
|
||||
| `/api/agent-skills/coverage` | GET | Coverage stats (how many SKILL.md files exist) | none |
|
||||
| `/api/agent-skills/generate` | POST | Trigger generator (dryRun/prune/onlyIds) | management |
|
||||
| `/api/agent-skills/{id}` | GET | Get single skill metadata | none |
|
||||
| `/api/agent-skills/{id}/raw` | GET | Fetch SKILL.md as `text/markdown` | none |
|
||||
| `/api/agent-skills/coverage` | GET | Coverage stats (how many SKILL.md files exist) | none |
|
||||
| `/api/agent-skills/generate` | POST | Trigger generator (dryRun/prune/onlyIds) | management |
|
||||
|
||||
Example — list all API skills:
|
||||
|
||||
@@ -150,61 +150,61 @@ See [A2A-SERVER.md](./A2A-SERVER.md) for protocol details.
|
||||
|
||||
### API Skills (23)
|
||||
|
||||
| ID | Area | Entry Point |
|
||||
| :--------------------- | :-------------- | :---------------------------------- |
|
||||
| `omni-auth` | auth | Auth + session management |
|
||||
| `omni-providers` | providers | Provider connection management |
|
||||
| `omni-models` | models | Model catalog and capabilities |
|
||||
| `omni-combos-routing` | combos-routing | Combo routing strategies |
|
||||
| `omni-api-keys` | api-keys | API key management |
|
||||
| `omni-usage-logs` | usage-logs | Usage and cost logs |
|
||||
| `omni-budget` | budget | Budget guards |
|
||||
| `omni-settings` | settings | Global settings |
|
||||
| `omni-proxies` | proxies | Proxy pool management |
|
||||
| `omni-cache` | cache | Semantic + prompt cache |
|
||||
| `omni-compression` | compression | Context compression engines |
|
||||
| `omni-context-rtk` | context-rtk | RTK compression |
|
||||
| `omni-resilience` | resilience | Circuit breakers + cooldowns |
|
||||
| `omni-cli-tools` | cli-tools | CLI tools REST proxy |
|
||||
| `omni-tunnels` | tunnels | Tunnel management |
|
||||
| `omni-sync-cloud` | sync-cloud | Cloud sync |
|
||||
| `omni-db-backups` | db-backups | Database backups |
|
||||
| `omni-webhooks` | webhooks | Webhook event dispatcher |
|
||||
| `omni-mcp` | mcp | MCP server (107 tools, 3 transports) |
|
||||
| `omni-agents-a2a` | agents-a2a | A2A agent protocol |
|
||||
| `omni-version-manager` | version-manager | Version and update management |
|
||||
| `omni-inference` | inference | Direct inference / completions |
|
||||
| ID | Area | Entry Point |
|
||||
| :--------------------- | :-------------- | :----------------------------------- |
|
||||
| `omni-auth` | auth | Auth + session management |
|
||||
| `omni-providers` | providers | Provider connection management |
|
||||
| `omni-models` | models | Model catalog and capabilities |
|
||||
| `omni-combos-routing` | combos-routing | Combo routing strategies |
|
||||
| `omni-api-keys` | api-keys | API key management |
|
||||
| `omni-usage-logs` | usage-logs | Usage and cost logs |
|
||||
| `omni-budget` | budget | Budget guards |
|
||||
| `omni-settings` | settings | Global settings |
|
||||
| `omni-proxies` | proxies | Proxy pool management |
|
||||
| `omni-cache` | cache | Semantic + prompt cache |
|
||||
| `omni-compression` | compression | Context compression engines |
|
||||
| `omni-context-rtk` | context-rtk | RTK compression |
|
||||
| `omni-resilience` | resilience | Circuit breakers + cooldowns |
|
||||
| `omni-cli-tools` | cli-tools | CLI tools REST proxy |
|
||||
| `omni-tunnels` | tunnels | Tunnel management |
|
||||
| `omni-sync-cloud` | sync-cloud | Cloud sync |
|
||||
| `omni-db-backups` | db-backups | Database backups |
|
||||
| `omni-webhooks` | webhooks | Webhook event dispatcher |
|
||||
| `omni-mcp` | mcp | MCP server (110 tools, 3 transports) |
|
||||
| `omni-agents-a2a` | agents-a2a | A2A agent protocol |
|
||||
| `omni-version-manager` | version-manager | Version and update management |
|
||||
| `omni-inference` | inference | Direct inference / completions |
|
||||
|
||||
### CLI Skills (21)
|
||||
|
||||
| ID | Area | CLI Command Root |
|
||||
| :------------------- | :----------------- | :---------------------- |
|
||||
| `cli-serve` | cli-serve | `omniroute serve` |
|
||||
| `cli-health` | cli-health | `omniroute health` |
|
||||
| `cli-providers` | cli-providers | `omniroute providers` |
|
||||
| `cli-keys` | cli-keys | `omniroute keys` |
|
||||
| `cli-models` | cli-models | `omniroute models` |
|
||||
| `cli-chat` | cli-chat | `omniroute chat` |
|
||||
| `cli-routing` | cli-routing | `omniroute routing` |
|
||||
| `cli-resilience` | cli-resilience | `omniroute resilience` |
|
||||
| `cli-compression` | cli-compression | `omniroute compression` |
|
||||
| `cli-contexts` | cli-contexts | `omniroute contexts` |
|
||||
| `cli-cost-usage` | cli-cost-usage | `omniroute cost` |
|
||||
| `cli-mcp` | cli-mcp | `omniroute mcp` |
|
||||
| `cli-a2a` | cli-a2a | `omniroute a2a` |
|
||||
| `cli-tunnel` | cli-tunnel | `omniroute tunnel` |
|
||||
| `cli-backup-sync` | cli-backup-sync | `omniroute backup` |
|
||||
| `cli-policy-audit` | cli-policy-audit | `omniroute policy` |
|
||||
| `cli-batches` | cli-batches | `omniroute batch` |
|
||||
| `cli-eval` | cli-eval | `omniroute eval` |
|
||||
| `cli-plugins-skills` | cli-plugins-skills | `omniroute plugins` |
|
||||
| `cli-setup` | cli-setup | `omniroute setup` |
|
||||
| ID | Area | CLI Command Root |
|
||||
| :-------------------- | :----------------- | :---------------------- |
|
||||
| `cli-serve` | cli-serve | `omniroute serve` |
|
||||
| `cli-health` | cli-health | `omniroute health` |
|
||||
| `cli-providers` | cli-providers | `omniroute providers` |
|
||||
| `cli-keys` | cli-keys | `omniroute keys` |
|
||||
| `cli-models` | cli-models | `omniroute models` |
|
||||
| `cli-chat` | cli-chat | `omniroute chat` |
|
||||
| `cli-routing` | cli-routing | `omniroute routing` |
|
||||
| `cli-resilience` | cli-resilience | `omniroute resilience` |
|
||||
| `cli-compression` | cli-compression | `omniroute compression` |
|
||||
| `cli-contexts` | cli-contexts | `omniroute contexts` |
|
||||
| `cli-cost-usage` | cli-cost-usage | `omniroute cost` |
|
||||
| `cli-mcp` | cli-mcp | `omniroute mcp` |
|
||||
| `cli-a2a` | cli-a2a | `omniroute a2a` |
|
||||
| `cli-tunnel` | cli-tunnel | `omniroute tunnel` |
|
||||
| `cli-backup-sync` | cli-backup-sync | `omniroute backup` |
|
||||
| `cli-policy-audit` | cli-policy-audit | `omniroute policy` |
|
||||
| `cli-batches` | cli-batches | `omniroute batch` |
|
||||
| `cli-eval` | cli-eval | `omniroute eval` |
|
||||
| `cli-plugins-skills` | cli-plugins-skills | `omniroute plugins` |
|
||||
| `cli-setup` | cli-setup | `omniroute setup` |
|
||||
| `cli-skill-collector` | cli-setup | `omniroute skills` |
|
||||
|
||||
### Configuration workflow (1)
|
||||
|
||||
| ID | Area | Entry Point |
|
||||
| :---------------- | :--------------- | :------------------------------- |
|
||||
| ID | Area | Entry Point |
|
||||
| :----------------- | :--------------- | :------------------------------- |
|
||||
| `config-codex-cli` | config-codex-cli | Codex CLI configuration workflow |
|
||||
|
||||
---
|
||||
|
||||
@@ -6,7 +6,7 @@ lastUpdated: 2026-06-28
|
||||
|
||||
# AgentBridge
|
||||
|
||||
AgentBridge is OmniRoute's MITM (Man-in-the-Middle) proxy that intercepts HTTPS traffic from IDE AI agents and reroutes it through OmniRoute's unified routing engine. It supports **9 IDE agents** — Antigravity, Kiro, GitHub Copilot, OpenAI Codex, Cursor, Zed, Claude Code, Open Code, and Trae (investigating) — making OmniRoute the broadest-coverage MITM proxy for AI coding assistants on the market.
|
||||
AgentBridge is OmniRoute's MITM (Man-in-the-Middle) proxy that intercepts HTTPS traffic from IDE AI agents and reroutes it through OmniRoute's unified routing engine. It supports **10 IDE agents** — Antigravity, Kiro, GitHub Copilot, GHE Copilot, OpenAI Codex, Cursor, Zed, Claude Code, Open Code, and Trae (investigating) — making OmniRoute the broadest-coverage MITM proxy for AI coding assistants on the market.
|
||||
|
||||
**Dashboard location:** `/dashboard/tools/agent-bridge`
|
||||
**Sidebar group:** Tools (after Cloud Agents)
|
||||
@@ -22,7 +22,7 @@ When an IDE agent (e.g., GitHub Copilot, Cursor, Claude Code) makes an API call,
|
||||
|
||||
This means you can:
|
||||
|
||||
- **Reroute any agent to any provider**: Copilot talking to OpenAI? Redirect it to Anthropic Claude, Gemini, or any of OmniRoute's 338 providers.
|
||||
- **Reroute any agent to any provider**: Copilot talking to OpenAI? Redirect it to Anthropic Claude, Gemini, or any of OmniRoute's 352 providers.
|
||||
- **Apply model mappings**: `gemini-3-flash` → `claude-sonnet-4.7` transparently at the handler level.
|
||||
- **Observe all agent traffic**: every intercepted request is published to the [Traffic Inspector](./TRAFFIC_INSPECTOR.md).
|
||||
- **Apply OmniRoute resilience**: combo routing, circuit breakers, fallbacks, and cost tracking work for IDE agent traffic too.
|
||||
|
||||
@@ -129,7 +129,7 @@ Body shape for POST (`customAgentBodySchema` in `src/app/api/acp/agents/route.ts
|
||||
|
||||
A uniform interface over third-party cloud coding agents. You submit a prompt + repo URL, OmniRoute dispatches to the right cloud agent, polls status, returns results.
|
||||
|
||||
### Supported agents (3, all confirmed in `src/lib/cloudAgent/agents/`)
|
||||
### Supported agents (4, all confirmed in `src/lib/cloudAgent/agents/`)
|
||||
|
||||
- `codex-cloud` — OpenAI Codex Cloud
|
||||
- `devin` — Cognition Devin
|
||||
@@ -195,7 +195,7 @@ curl -X POST http://localhost:20128/a2a \
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"messages": [{"role": "user", "content": "Route this prompt"}],
|
||||
"skillId": "smart-routing"
|
||||
"skill": "smart-routing"
|
||||
},
|
||||
"id": 1
|
||||
}'
|
||||
@@ -231,10 +231,13 @@ curl -X POST http://localhost:20128/api/v1/agents/tasks \
|
||||
-H "Cookie: auth_token=..." \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"agentId": "devin",
|
||||
"providerId": "devin",
|
||||
"prompt": "Implement feature X in repo Y",
|
||||
"repo": "https://github.com/user/repo",
|
||||
"branch": "main"
|
||||
"source": {
|
||||
"repoName": "user/repo",
|
||||
"repoUrl": "https://github.com/user/repo",
|
||||
"branch": "main"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
@@ -269,7 +272,7 @@ curl http://localhost:20128/api/v1/agents/tasks/<task-id> \
|
||||
↓ ↓ └────────────┘
|
||||
External peer Local CLI │
|
||||
agents that binaries on ↓
|
||||
speak A2A v0.3 the host Codex Cloud,
|
||||
speak A2A v0.3 the host Codex Cloud, Cursor,
|
||||
Devin, Jules
|
||||
```
|
||||
|
||||
|
||||
@@ -32,16 +32,17 @@ via API keys), embedded services run on the same machine as OmniRoute and commun
|
||||
|
||||
### Why embedded services?
|
||||
|
||||
Four services are embedded as of v3.8.44:
|
||||
Five services are embedded:
|
||||
|
||||
| Service | npm package | Default port | Purpose |
|
||||
| --------------- | ----------------------------------------------- | :----------: | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| **9Router** | `9router` | 20130 | AI router that OmniRoute can use as a sub-provider. Models exposed as `9router/{sub}/{model}` |
|
||||
| **CLIProxyAPI** | `@anthropic/cli-proxy` (via `cliproxy` binary) | auto | Local proxy adapter for Anthropic CLI auth flows. Provides fallback routing when OAuth tokens expire |
|
||||
| **Mux** | `mux` (headless `mux server`) | 8322 | Local agent-orchestration daemon (coder/mux). Lifecycle-managed only — not a routing target (no LLM proxying). |
|
||||
| **Bifrost** | `@maximhq/bifrost` | 8080 | Go AI-gateway relay backend. When running, auto-selected by the relay route (`/v1/relay/`) |
|
||||
| Service | npm package | Default port | Purpose |
|
||||
| --------------- | ---------------------------------- | :----------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **9Router** | `9router` | 20130 | AI router that OmniRoute can use as a sub-provider. Models exposed as `9router/{sub}/{model}` |
|
||||
| **CLIProxyAPI** | GitHub release binary (`cliproxy`) | 8317 | Local proxy adapter for Anthropic CLI auth flows. Provides fallback routing when OAuth tokens expire |
|
||||
| **Mux** | `mux` (headless `mux server`) | 8322 | Local agent-orchestration daemon (coder/mux). Lifecycle-managed only — not a routing target (no LLM proxying). |
|
||||
| **Bifrost** | `@maximhq/bifrost` | 8080 | Go AI-gateway relay backend. When running, auto-selected by the relay route (`/v1/relay/`) |
|
||||
| **Dario** | `@askalf/dario` | 3456 | Claude-subscription proxy — alternative/failover to CLIProxyAPI for Claude-Code-shaped traffic; the injected key becomes `DARIO_ADMIN_TOKEN` gating its `/admin/*` OAuth control plane |
|
||||
|
||||
All four follow the same supervisory model:
|
||||
All five follow the same supervisory model:
|
||||
|
||||
- OmniRoute installs them under `DATA_DIR/services/{name}/` (isolated from OmniRoute's own `package.json`)
|
||||
- OmniRoute spawns and monitors them as child processes
|
||||
@@ -211,7 +212,7 @@ race conditions when, for example, auto-start and a UI button fire simultaneousl
|
||||
All routes under `/api/services/` are **LOCAL_ONLY** (loopback only, hard rule #17).
|
||||
Non-loopback requests receive `403 LOCAL_ONLY` regardless of auth token.
|
||||
|
||||
### 4.1 9Router endpoints (8 routes)
|
||||
### 4.1 9Router endpoints (11 routes)
|
||||
|
||||
#### `POST /api/services/9router/install`
|
||||
|
||||
@@ -429,11 +430,12 @@ SSE stream of live logs from 9Router's stdout/stderr ring buffer.
|
||||
|
||||
---
|
||||
|
||||
### 4.2 CLIProxyAPI endpoints (7 routes)
|
||||
### 4.2 CLIProxyAPI endpoints (10 routes)
|
||||
|
||||
CLIProxyAPI has the same endpoint shape as 9Router minus `rotate-key` (CLIProxyAPI
|
||||
does not require an injected API key; it authenticates via the host's existing CLI
|
||||
config) and `status` includes fewer fields.
|
||||
CLIProxyAPI has the same endpoint shape as 9Router minus `rotate-key`, plus
|
||||
`accounts`, `provider-expose` and `auto-restart-adopted`. It now receives a
|
||||
dedicated data-plane API key injected at spawn (`needsApiKey: true` in
|
||||
`bootstrap.ts`, used for model sync); `status` includes fewer fields.
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | ----------------------------------- | ------------------------------------ |
|
||||
@@ -450,7 +452,7 @@ four services using the `[name]` dynamic segment.
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Mux endpoints (7 routes)
|
||||
### 4.3 Mux endpoints (8 routes)
|
||||
|
||||
Mux has the same endpoint shape as CLIProxyAPI — no `rotate-key` route in the API
|
||||
surface (the bearer token is generated the same way as 9Router's via
|
||||
@@ -458,34 +460,34 @@ surface (the bearer token is generated the same way as 9Router's via
|
||||
there is no dedicated rotation endpoint yet). Mux is lifecycle-managed only: unlike
|
||||
9Router, it has no Layer 4 executor and is never registered as a routing provider.
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | -------------------------------- | ------------------------------------- |
|
||||
| `POST` | `/api/services/mux/install` | Install Mux from npm (`npm i mux`) |
|
||||
| `POST` | `/api/services/mux/start` | Start Mux (`mux server`) |
|
||||
| `POST` | `/api/services/mux/stop` | Stop Mux |
|
||||
| `POST` | `/api/services/mux/restart` | Restart Mux |
|
||||
| `POST` | `/api/services/mux/update` | Update to newer npm version |
|
||||
| `GET` | `/api/services/mux/status` | Live + DB status |
|
||||
| `POST` | `/api/services/mux/auto-start` | Toggle auto-start |
|
||||
| Method | Path | Description |
|
||||
| ------ | ------------------------------ | ---------------------------------- |
|
||||
| `POST` | `/api/services/mux/install` | Install Mux from npm (`npm i mux`) |
|
||||
| `POST` | `/api/services/mux/start` | Start Mux (`mux server`) |
|
||||
| `POST` | `/api/services/mux/stop` | Stop Mux |
|
||||
| `POST` | `/api/services/mux/restart` | Restart Mux |
|
||||
| `POST` | `/api/services/mux/update` | Update to newer npm version |
|
||||
| `GET` | `/api/services/mux/status` | Live + DB status |
|
||||
| `POST` | `/api/services/mux/auto-start` | Toggle auto-start |
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Bifrost endpoints (7 routes)
|
||||
### 4.4 Bifrost endpoints (8 routes)
|
||||
|
||||
Bifrost is a Go AI-gateway relay backend (`@maximhq/bifrost`). It uses the same
|
||||
endpoint shape as CLIProxyAPI (no `rotate-key` — Bifrost manages its own provider
|
||||
keys in `config.json` under its `-app-dir`).
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | ---------------------------------- | ------------------------------------------------------ |
|
||||
| `POST` | `/api/services/bifrost/install` | Install Bifrost from npm (`@maximhq/bifrost`) |
|
||||
| `POST` | `/api/services/bifrost/start` | Start Bifrost on port 8080 (default) |
|
||||
| `POST` | `/api/services/bifrost/stop` | Stop Bifrost |
|
||||
| `POST` | `/api/services/bifrost/restart` | Restart Bifrost |
|
||||
| `POST` | `/api/services/bifrost/update` | Update to newer version |
|
||||
| `GET` | `/api/services/bifrost/status` | Live + DB status |
|
||||
| `POST` | `/api/services/bifrost/auto-start` | Toggle auto-start |
|
||||
| `GET` | `/api/services/bifrost/logs` | SSE log tail (via shared `[name]/logs` dynamic route) |
|
||||
| Method | Path | Description |
|
||||
| ------ | ---------------------------------- | ----------------------------------------------------- |
|
||||
| `POST` | `/api/services/bifrost/install` | Install Bifrost from npm (`@maximhq/bifrost`) |
|
||||
| `POST` | `/api/services/bifrost/start` | Start Bifrost on port 8080 (default) |
|
||||
| `POST` | `/api/services/bifrost/stop` | Stop Bifrost |
|
||||
| `POST` | `/api/services/bifrost/restart` | Restart Bifrost |
|
||||
| `POST` | `/api/services/bifrost/update` | Update to newer version |
|
||||
| `GET` | `/api/services/bifrost/status` | Live + DB status |
|
||||
| `POST` | `/api/services/bifrost/auto-start` | Toggle auto-start |
|
||||
| `GET` | `/api/services/bifrost/logs` | SSE log tail (via shared `[name]/logs` dynamic route) |
|
||||
|
||||
**Routing wiring:** When `BIFROST_BASE_URL` is unset and the supervised Bifrost
|
||||
instance is running, `getBifrostRoutingConfig()` (in `routingBackend.ts`) automatically
|
||||
@@ -494,7 +496,14 @@ always takes precedence.
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Reverse proxy (9Router dashboard embed)
|
||||
### 4.5 Dario endpoints (12 routes)
|
||||
|
||||
Same lifecycle shape as the other services (`install`, `start`, `stop`, `restart`,
|
||||
`update`, `status`, `auto-start`, `auto-restart-adopted`) plus a token-gated OAuth
|
||||
control plane under `admin/`: `admin/accounts`, `admin/import-from-omniroute`,
|
||||
`admin/login-start`, `admin/login-complete` (all behind `DARIO_ADMIN_TOKEN`).
|
||||
|
||||
### 4.6 Reverse proxy (9Router dashboard embed)
|
||||
|
||||
The dashboard embeds the 9Router web UI inside an iframe via an internal reverse
|
||||
proxy at:
|
||||
@@ -552,8 +561,8 @@ OmniRoute:
|
||||
flag, so the token never appears in `ps`/process listings).
|
||||
4. Never returns the plaintext key in any HTTP response.
|
||||
|
||||
CLIProxyAPI does not require an injected key (it authenticates via the host's
|
||||
existing CLI config).
|
||||
CLIProxyAPI receives a dedicated data-plane key injected at spawn
|
||||
(`needsApiKey: true` — used for model sync against the adapter).
|
||||
|
||||
### SSRF defense
|
||||
|
||||
|
||||
221
docs/frameworks/LOG-EXPORT.md
Normal file
@@ -0,0 +1,221 @@
|
||||
---
|
||||
title: "Log Export"
|
||||
version: 3.8.51
|
||||
lastUpdated: 2026-08-29
|
||||
---
|
||||
|
||||
# Log export
|
||||
|
||||
Continuous, incremental export of OmniRoute call logs to an external analytics store.
|
||||
|
||||
The Logs dashboard tab keeps request history in SQLite (`call_logs`), which is bounded by
|
||||
rotation and retention. Log export ships the same record set out on a schedule so it can outlive
|
||||
the local database and be joined against other data. BigQuery is the first destination; the
|
||||
pipeline is a registry, so more destinations are additive.
|
||||
|
||||
---
|
||||
|
||||
## 1. How it works
|
||||
|
||||
```
|
||||
call_logs (SQLite)
|
||||
→ callLogExportSource.getCallLogsForExport(cursor, batchSize)
|
||||
→ LogExportRecord[] (the Logs-tab field set)
|
||||
→ destination client.send(batch)
|
||||
→ advanceLogExportCursor(id, lastRowId, count)
|
||||
```
|
||||
|
||||
- **Schedule** — one `JobRegistry` cron job, `log_export`, defaulting to `0 * * * *` (hourly,
|
||||
UTC). Registered in `src/lib/initCloudSync.ts`; overridable with `OMNIROUTE_LOG_EXPORT_CRON`.
|
||||
Each tick drains every **enabled** destination, sequentially.
|
||||
- **Cursor** — SQLite's implicit `call_logs.rowid`, persisted per destination in
|
||||
`log_export_destinations.cursor_row_id`. `timestamp` is deliberately not the cursor: callers
|
||||
may supply their own value, so a slow request can be written after a faster one that started
|
||||
later, and a timestamp cursor would skip it.
|
||||
- **Batching** — `batch_size` rows per request (default 500), `max_rows_per_run` rows per run
|
||||
(default 10000) so a large backlog drains over several ticks instead of blocking one.
|
||||
- **Delivery** — the cursor advances only after `send()` resolves. A failed batch leaves the
|
||||
cursor where it was, so the same rows are retried on the next run. The guarantee is
|
||||
at-least-once plus destination-side de-duplication, not true exactly-once: BigQuery keys each
|
||||
row by the call-log id, which it honours on a best-effort basis within its own dedup window.
|
||||
- **Overlap guard** — the cron tick and `POST .../run` can fire together. A destination
|
||||
already draining is skipped rather than drained twice (`skipped: true` in the run result),
|
||||
so a concurrent run cannot re-send a batch or write the cursor backwards.
|
||||
- **Purge recovery** — if `cursor_row_id` ends up above `MAX(rowid)` (the whole table was
|
||||
purged and rowids restarted), the runner rewinds to 0 rather than going permanently blind.
|
||||
|
||||
### Payloads (prompts and completions)
|
||||
|
||||
By default the export carries only the summary fields the Logs **list** shows. Turning on
|
||||
**Export prompts and responses** (`includeBodies`) additionally ships what the Logs **detail**
|
||||
pane shows for each call:
|
||||
|
||||
| Field | What it holds |
|
||||
| -------------------------------- | -------------------------------------------------- |
|
||||
| `request_body` / `response_body` | The call payloads as the dashboard renders them |
|
||||
| `pipeline_route_decision` | Which target and model the router picked |
|
||||
| `pipeline_client_request` | The raw request exactly as the client sent it |
|
||||
| `pipeline_openai_request` | After translation into the internal OpenAI shape |
|
||||
| `pipeline_provider_request` | As actually sent upstream, in the provider dialect |
|
||||
| `pipeline_provider_response` | The raw upstream response |
|
||||
| `pipeline_client_response` | What was handed back to the caller |
|
||||
| `pipeline_error` | Pipeline-level error detail for a failed call |
|
||||
| `bodies_truncated` | True when any field above hit `maxBodyBytes` |
|
||||
|
||||
This is prompt content, so it is **off by default** and deliberately a per-destination choice.
|
||||
What ships is what the dashboard shows, because both read through `getCallLogById`: payloads are
|
||||
already PII-sanitised and secret-redacted when they are written, and a call made with a
|
||||
`noLog` API key stores no payload at all, so there is nothing to export.
|
||||
|
||||
Payloads are read per row from the filesystem artifact, so hydration only runs for destinations
|
||||
that asked for it. A row whose artifact is missing or corrupt exports its summary with null
|
||||
payloads rather than failing the batch and stranding the cursor.
|
||||
|
||||
`maxBodyBytes` (default 262144) caps each field. Longer payloads are **truncated rather than
|
||||
dropped** — a clipped prompt still answers "what was asked" — and the row is flagged with
|
||||
`bodies_truncated`. Streamed chunk-by-chunk deltas are not exported; the assembled response is
|
||||
already in `pipeline_provider_response` and `pipeline_client_response`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Files
|
||||
|
||||
| Piece | Location |
|
||||
| -------------------- | -------------------------------------------- |
|
||||
| Destination contract | `src/lib/logExport/types.ts` |
|
||||
| Registry | `src/lib/logExport/registry.ts` |
|
||||
| Secret handling | `src/lib/logExport/secrets.ts` |
|
||||
| Runner (cursor loop) | `src/lib/logExport/runner.ts` |
|
||||
| API projection | `src/lib/logExport/presenter.ts` |
|
||||
| BigQuery destination | `src/lib/logExport/destinations/bigquery.ts` |
|
||||
| Google SA auth | `src/lib/logExport/googleServiceAccount.ts` |
|
||||
| Call-log source | `src/lib/usage/callLogExportSource.ts` |
|
||||
| Persistence | `src/lib/db/logExportDestinations.ts` |
|
||||
| Cron job | `src/lib/jobs/logExportJob.ts` |
|
||||
| REST layer | `src/app/api/log-export/` |
|
||||
| Dashboard page | `src/app/(dashboard)/dashboard/log-export/` |
|
||||
|
||||
Schema: `src/lib/db/migrations/170_log_export_destinations.sql`.
|
||||
|
||||
---
|
||||
|
||||
## 3. REST API
|
||||
|
||||
All routes are management-authenticated (`requireManagementAuth`). Secrets are never returned:
|
||||
a stored secret comes back as the literal `__stored__`, and sending that value back on an update
|
||||
keeps the stored credential.
|
||||
|
||||
Creating or updating a destination whose type declares a secret **requires
|
||||
`STORAGE_ENCRYPTION_KEY`**. Without it `encrypt()` is a silent passthrough, so the write is
|
||||
refused with a 400 rather than putting a credential into SQLite in plaintext (the same guard the
|
||||
Telegram webhook applies).
|
||||
|
||||
| Method | Path | Purpose |
|
||||
| -------- | ---------------------------------------- | ------------------------------------------- |
|
||||
| `GET` | `/api/log-export/types` | Destination types + their config field list |
|
||||
| `GET` | `/api/log-export/destinations` | List destinations (secrets redacted) |
|
||||
| `POST` | `/api/log-export/destinations` | Create a destination |
|
||||
| `GET` | `/api/log-export/destinations/{id}` | Read one |
|
||||
| `PUT` | `/api/log-export/destinations/{id}` | Update name / enabled / config / batching |
|
||||
| `DELETE` | `/api/log-export/destinations/{id}` | Delete |
|
||||
| `POST` | `/api/log-export/destinations/{id}/test` | Probe credentials, write nothing |
|
||||
| `POST` | `/api/log-export/destinations/{id}/run` | Drain now, same path as the scheduled run |
|
||||
| `GET` | `/api/log-export/status` | Cron state, recent runs, backlog per target |
|
||||
|
||||
`GET /api/log-export/types` is what makes the UI generic: the dashboard form is rendered from
|
||||
the returned field descriptors, so a new destination needs no UI change.
|
||||
|
||||
---
|
||||
|
||||
## 4. BigQuery destination
|
||||
|
||||
Config keys (`type: "bigquery"`):
|
||||
|
||||
| Key | Notes |
|
||||
| -------------------- | ----------------------------------------------------------------- |
|
||||
| `projectId` | GCP project holding the dataset |
|
||||
| `datasetId` | `[A-Za-z0-9_]+` |
|
||||
| `tableId` | `[A-Za-z0-9_]+` |
|
||||
| `location` | Only used when the dataset has to be created (default `EU`) |
|
||||
| `serviceAccountJson` | Service-account key. Secret: encrypted at rest, never returned |
|
||||
| `autoCreate` | Create the dataset and table on the first export (default `true`) |
|
||||
|
||||
The service account needs `bigquery.tables.updateData` on the target table, plus
|
||||
`bigquery.datasets.create` / `bigquery.tables.create` when `autoCreate` is on.
|
||||
|
||||
A configured batch is a **cursor** unit, not an HTTP one: `send()` chunks it into insertAll
|
||||
calls of at most 500 rows, so a large `batch_size` cannot trip BigQuery's 10 MB request cap.
|
||||
Transient statuses (408/429/500/502/503/504) are retried up to three times with exponential
|
||||
backoff, reusing the same insertIds; auth and schema failures throw on the first attempt rather
|
||||
than burning the run.
|
||||
|
||||
A table created moments ago is not yet visible to the streaming endpoint, which answers
|
||||
404 for a few seconds. That 404 is retried, but **only when this run created the table** —
|
||||
a genuinely missing table still fails fast. Note that re-creating a table under a name that
|
||||
was recently deleted makes BigQuery refuse streaming inserts for several minutes; that is a
|
||||
property of delete-then-recreate, so prefer a new table name over dropping and re-adding one.
|
||||
|
||||
**A partial failure arrives as HTTP 200 with a non-empty `insertErrors[]`.** That is treated as
|
||||
a failure and throws, which is what stops the cursor from advancing past rows BigQuery never
|
||||
accepted; `tests/unit/log-export-bigquery.test.ts` pins the behaviour.
|
||||
|
||||
Transport is plain REST — a self-signed RS256 assertion is exchanged for an access token at
|
||||
`https://oauth2.googleapis.com/token`, then rows go to `tabledata.insertAll`. No Google SDK is
|
||||
pulled in. Access tokens are cached in-process per (service account, scope).
|
||||
|
||||
The created table carries one column per Logs-tab field plus `exported_at`, and is laid out for
|
||||
how call logs are actually queried:
|
||||
|
||||
- **Day-partitioned on `timestamp`**, so a query bounded by date only scans those days.
|
||||
- **Clustered by `api_key_name`, `provider`, `model`, `status`** (in that order), so filtering by
|
||||
who ran it, where it went, or whether it failed prunes blocks inside each partition. BigQuery
|
||||
allows at most four clustering columns and the order matters: a filter on `api_key_name` alone
|
||||
prunes, a filter on `status` alone does not.
|
||||
- **Optional partition retention** via `partitionExpirationDays` (0 keeps everything), applied
|
||||
when the table is created.
|
||||
|
||||
Both settings apply at creation time. An existing table keeps whatever layout it already has, so
|
||||
point the destination at a new table id if you want to adopt them.
|
||||
|
||||
`tests/unit/log-export-bigquery.test.ts` asserts the mapper and the table schema stay in
|
||||
lockstep, so a new call-log column cannot be silently dropped on the way out.
|
||||
|
||||
Batches are chunked by **both** row count and serialised bytes. Row count alone is not enough
|
||||
once payloads are exported: 500 rows carrying prompts can be tens of megabytes, and insertAll
|
||||
rejects a request over 10 MB. Chunks close at 500 rows or 9 MB, whichever comes first.
|
||||
|
||||
---
|
||||
|
||||
## 5. Adding a destination
|
||||
|
||||
1. Create `src/lib/logExport/destinations/<name>.ts` exporting a `LogExportDestinationType`:
|
||||
a Zod `configSchema`, a `fields` descriptor array for the UI, `secretFields`, and a
|
||||
`createClient(config)` returning `test()` / `prepare()` / `send(records)`.
|
||||
2. Add it to the `DESTINATIONS` array in `src/lib/logExport/registry.ts`.
|
||||
3. Write tests under `tests/unit/`.
|
||||
|
||||
That is the whole change: persistence, the cron job, the REST layer, secret encryption and the
|
||||
dashboard form all read the registry.
|
||||
|
||||
Two rules for a new destination:
|
||||
|
||||
- `send()` **must throw** on a partial failure. Resolving means "the destination has these rows",
|
||||
and the cursor moves past them permanently.
|
||||
- A destination that takes a user-supplied URL must validate it through
|
||||
`parseAndValidateWebhookUrl` (`src/shared/network/outboundUrlGuardPolicy.ts`) before fetching,
|
||||
the same way webhooks do. BigQuery does not need this: its hosts are constants.
|
||||
|
||||
---
|
||||
|
||||
## 6. Operating it
|
||||
|
||||
- **Dashboard**: Integrations → Log export. Add a destination, run **Test** to check credentials
|
||||
without writing rows, then enable it.
|
||||
- **Backlog**: each destination card shows pending rows and the cursor; `GET
|
||||
/api/log-export/status` returns the same figures plus the last 20 job runs.
|
||||
- **A failing destination does not fail the others** — the run summary records per-destination
|
||||
status in `last_status` / `last_error`, and the job run history keeps the aggregate.
|
||||
- **Deleting a destination deletes its cursor.** Re-adding it starts from the oldest retained
|
||||
call log, which re-sends rows the destination may already hold. On BigQuery the per-row
|
||||
`insertId` absorbs that only inside BigQuery's own de-duplication window, so prefer disabling
|
||||
a destination over deleting it.
|
||||
@@ -64,7 +64,7 @@ Cursor, Cline, and compatible MCP client setup.
|
||||
|
||||
---
|
||||
|
||||
## Essential Tools (13) — Phase 1
|
||||
## Essential Tools (14) — Phase 1
|
||||
|
||||
| Tool | Scopes | Description |
|
||||
| :------------------------------ | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------- |
|
||||
@@ -215,13 +215,13 @@ curl -X DELETE http://localhost:20128/api/settings/notion
|
||||
|
||||
## Agent Skill Catalog Tools (3)
|
||||
|
||||
Defined in `open-sse/mcp-server/tools/agentSkillTools.ts`. Backed by `src/lib/agentSkills/catalog`. These tools expose the 42-entry Agent Skills documentation catalog to MCP clients and external agents. Scope: `read:catalog`.
|
||||
Defined in `open-sse/mcp-server/tools/agentSkillTools.ts`. Backed by `src/lib/agentSkills/catalog`. These tools expose the 45-entry Agent Skills documentation catalog to MCP clients and external agents. Scope: `read:catalog`.
|
||||
|
||||
| Tool | Scopes | Description |
|
||||
| :-------------------------------- | :------------- | :--------------------------------------------------------------------------------------------------------------- |
|
||||
| `omniroute_agent_skills_list` | `read:catalog` | List all 42 agent skills with optional `category` (api\|cli) and `area` filters; returns metadata + coverage |
|
||||
| `omniroute_agent_skills_get` | `read:catalog` | Get full metadata + SKILL.md content for a single skill by canonical `id` |
|
||||
| `omniroute_agent_skills_coverage` | `read:catalog` | Coverage stats: how many of the 22 API and 20 CLI skills have SKILL.md files on the filesystem vs catalog totals |
|
||||
| Tool | Scopes | Description |
|
||||
| :-------------------------------- | :------------- | :------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `omniroute_agent_skills_list` | `read:catalog` | List all 45 agent skills with optional `category` (api\|cli) and `area` filters; returns metadata + coverage |
|
||||
| `omniroute_agent_skills_get` | `read:catalog` | Get full metadata + SKILL.md content for a single skill by canonical `id` |
|
||||
| `omniroute_agent_skills_coverage` | `read:catalog` | Coverage stats: how many of the 23 API, 21 CLI and 1 config skills have SKILL.md files on the filesystem vs catalog totals |
|
||||
|
||||
See [AGENT-SKILLS.md](./AGENT-SKILLS.md) for the full catalog and how external agents consume it.
|
||||
|
||||
@@ -233,12 +233,12 @@ frameworks ship alongside the MCP server in v3.8.0 and are documented separately
|
||||
|
||||
### Cloud Agents
|
||||
|
||||
Cloud Agents are out-of-process AI coding agents (codex-cloud, devin, jules) wired into
|
||||
Cloud Agents are out-of-process AI coding agents (codex-cloud, cursor-cloud, devin, jules) wired into
|
||||
OmniRoute through the same connection model used for LLM providers. They are exposed via
|
||||
their own REST surface (`/api/v1/agents/*`) and are **not** part of the MCP tool catalog
|
||||
— calling a Cloud Agent does not consume an MCP scope.
|
||||
|
||||
- Implementation: `src/lib/cloudAgent/` (`registry.ts`, `agents/codex-cloud.ts`, `agents/devin.ts`, `agents/jules.ts`).
|
||||
- Implementation: `src/lib/cloudAgent/` (`registry.ts`, `agents/codex.ts`, `agents/cursor.ts`, `agents/devin.ts`, `agents/jules.ts`).
|
||||
- Lifecycle: `createTask`, `getStatus`, `approvePlan`, `sendMessage`, `listSources`.
|
||||
- Documentation: [docs/frameworks/CLOUD_AGENT.md](./CLOUD_AGENT.md).
|
||||
|
||||
@@ -279,32 +279,41 @@ Both SSE and Streamable HTTP transports are blocked until the MCP server is enab
|
||||
MCP tools are authenticated through API key scopes. Scope enforcement is centralized in
|
||||
`open-sse/mcp-server/scopeEnforcement.ts`. Each tool requires specific scopes:
|
||||
|
||||
| Scope | Tools |
|
||||
| :-------------------- | :---------------------------------------------------------------------------------------------------------------- |
|
||||
| `read:health` | `get_health`, `get_provider_metrics`, `simulate_route`, `explain_route`, `best_combo_for_task`, `db_health_check` |
|
||||
| `read:combos` | `list_combos`, `get_combo_metrics`, `simulate_route`, `best_combo_for_task`, `test_combo` |
|
||||
| `write:combos` | `switch_combo`, `set_routing_strategy` |
|
||||
| `read:quota` | `check_quota` |
|
||||
| `read:usage` | `cost_report`, `get_session_snapshot`, `explain_route` |
|
||||
| `read:models` | `list_models_catalog` |
|
||||
| `execute:completions` | `route_request`, `test_combo` |
|
||||
| `execute:search` | `web_search` |
|
||||
| `write:budget` | `set_budget_guard` |
|
||||
| `write:resilience` | `set_resilience_profile`, `db_health_check` |
|
||||
| `pricing:write` | `sync_pricing` |
|
||||
| `read:cache` | `cache_stats` |
|
||||
| `write:cache` | `cache_flush` |
|
||||
| `read:compression` | `compression_status`, `list_compression_combos`, `compression_combo_stats` |
|
||||
| `write:compression` | `compression_configure`, `set_compression_engine` |
|
||||
| `read:proxies` | `oneproxy_fetch`, `oneproxy_rotate`, `oneproxy_stats` |
|
||||
| `read:notion` | `notion_search`, `notion_list_databases`, `notion_get_database`, `notion_query_database`, `notion_read` |
|
||||
| `write:notion` | `notion_append_blocks` |
|
||||
| `read:memory` | `memory_search` |
|
||||
| `write:memory` | `memory_add`, `memory_clear` |
|
||||
| `read:skills` | `skills_list`, `skills_executions` |
|
||||
| `write:skills` | `skills_enable` |
|
||||
| `execute:skills` | `skills_execute` |
|
||||
| `read:catalog` | `agent_skills_list`, `agent_skills_get`, `agent_skills_coverage` |
|
||||
| Scope | Tools |
|
||||
| :-------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `read:health` | `get_health`, `get_provider_metrics`, `simulate_route`, `explain_route`, `best_combo_for_task`, `db_health_check` |
|
||||
| `read:combos` | `list_combos`, `get_combo_metrics`, `simulate_route`, `best_combo_for_task`, `test_combo` |
|
||||
| `write:combos` | `switch_combo`, `set_routing_strategy` |
|
||||
| `read:quota` | `check_quota` |
|
||||
| `read:usage` | `cost_report`, `get_session_snapshot`, `explain_route` |
|
||||
| `read:models` | `list_models_catalog` |
|
||||
| `execute:completions` | `route_request`, `test_combo` |
|
||||
| `execute:search` | `web_search`, `x_search`, `web_fetch` |
|
||||
| `write:budget` | `set_budget_guard` |
|
||||
| `write:resilience` | `set_resilience_profile`, `db_health_check` |
|
||||
| `pricing:write` | `sync_pricing` |
|
||||
| `read:cache` | `cache_stats` |
|
||||
| `write:cache` | `cache_flush` |
|
||||
| `read:compression` | `compression_status`, `list_compression_combos`, `compression_combo_stats` |
|
||||
| `write:compression` | `compression_configure`, `set_compression_engine` |
|
||||
| `read:proxies` | `oneproxy_fetch`, `oneproxy_rotate`, `oneproxy_stats` |
|
||||
| `read:notion` | `notion_search`, `notion_get_page`, `notion_list_block_children`, `notion_query_database`, `notion_get_database` |
|
||||
| `write:notion` | `notion_append_blocks` |
|
||||
| `read:memory` | `memory_search` |
|
||||
| `write:memory` | `memory_add`, `memory_clear` |
|
||||
| `read:skills` | `skills_list`, `skills_executions` |
|
||||
| `write:skills` | `skills_enable` |
|
||||
| `execute:skills` | `skills_execute` |
|
||||
| `read:catalog` | `agent_skills_list`, `agent_skills_get`, `agent_skills_coverage` |
|
||||
| `read:tools` | `omniroute_tool_search` |
|
||||
| `read:radar` | `omniroute_radar_catalog` |
|
||||
| `read:gamification` | `gamification_profile`, `gamification_rank`, `gamification_leaderboard`, `gamification_badges`, `gamification_servers`, `gamification_anomalies` |
|
||||
| `write:gamification` | `gamification_invite`, `gamification_transfer` |
|
||||
| `read:plugins` | `plugin_list`, `plugin_executions` |
|
||||
| `write:plugins` | `plugin_scan`, `plugin_install`, `plugin_uninstall`, `plugin_activate`, `plugin_deactivate`, `plugin_configure` |
|
||||
| `read:obsidian` | 13 read tools — `obsidian_list_vault`, `obsidian_read_note`, `obsidian_search_simple`, `obsidian_search_structured`, `obsidian_get_periodic_note`, `obsidian_sync_status`, … |
|
||||
| `write:obsidian` | 9 write tools — `obsidian_write_note`, `obsidian_append_note`, `obsidian_patch_note`, `obsidian_move_note`, `obsidian_delete_note`, `obsidian_sync_trigger`, … |
|
||||
| `read:local-corpus` | `local_corpus_search`, `local_corpus_read`, `local_corpus_status` |
|
||||
|
||||
Wildcard scopes are supported: `read:*` grants all read-scopes, `*` grants full access.
|
||||
|
||||
@@ -409,7 +418,7 @@ The heartbeat snapshot contains:
|
||||
"transport": "stdio",
|
||||
"scopesEnforced": false,
|
||||
"allowedScopes": [],
|
||||
"toolCount": 43
|
||||
"toolCount": 110
|
||||
}
|
||||
```
|
||||
|
||||
@@ -438,7 +447,7 @@ Use the dashboard or the `/api/mcp/audit` and `/api/mcp/audit/stats` REST endpoi
|
||||
| `open-sse/mcp-server/audit.ts` | Tool call audit logging (`mcp_tool_audit`) |
|
||||
| `open-sse/mcp-server/runtimeHeartbeat.ts` | stdio heartbeat writer (`mcp-heartbeat.json`) |
|
||||
| `open-sse/mcp-server/descriptionCompressor.ts` | Description compression for tool / prompt / resource registries |
|
||||
| `open-sse/mcp-server/schemas/tools.ts` | Zod schemas + tool registry (`MCP_TOOLS`, 34 entries) |
|
||||
| `open-sse/mcp-server/schemas/tools.ts` | Zod schemas + tool registry (`MCP_TOOLS`, 45 entries) |
|
||||
| `open-sse/mcp-server/tools/advancedTools.ts` | Phase 2 + cache + 1proxy tool handlers |
|
||||
| `open-sse/mcp-server/tools/compressionTools.ts` | Compression tool handlers |
|
||||
| `open-sse/mcp-server/tools/memoryTools.ts` | Memory tool definitions (3 tools) |
|
||||
|
||||
@@ -152,7 +152,7 @@ amortizes the backfill cost across real requests without blocking startup.
|
||||
`limit` pending entries per request. Progress can be polled via
|
||||
`GET /api/memory/engine-status` (`vectorStore.needsReindex`).
|
||||
|
||||
The `memory_vec_meta` table (migration `073_memory_vec.sql`) stores:
|
||||
The `memory_vec_meta` table (migration `083_memory_vec.sql`) stores:
|
||||
|
||||
- `active_dim` — current vector dimension (null = not yet calibrated).
|
||||
- `embedding_signature` — `${source}:${model}:${dim}` used to detect changes.
|
||||
@@ -568,7 +568,7 @@ the legacy/global settings surface.
|
||||
## Caching
|
||||
|
||||
`src/lib/memory/store.ts` keeps an in-process LRU-ish cache
|
||||
(`MEMORY_CACHE_TTL = 5 min`, `MEMORY_MAX_CACHE_SIZE = 10 000`, with 20 %
|
||||
(`MEMORY_CACHE_TTL = 1 min`, `MEMORY_MAX_CACHE_SIZE = 500`, with 20 %
|
||||
oldest eviction) for `getMemory(id)` reads, plus a generic key/value
|
||||
`memoryCache` layer (`src/lib/memory/cache.ts`) with `get`/`set`/`invalidate`
|
||||
methods used by callers that want their own scoped cache (1 000-entry LRU,
|
||||
@@ -608,7 +608,7 @@ default TTL 5 min).
|
||||
- `src/lib/db/memoryVec.ts` — CRUD for `memory_vec_meta`
|
||||
- `src/lib/db/migrations/015_create_memories.sql`,
|
||||
`022_add_memory_fts5.sql`, `023_fix_memory_fts_uuid.sql`,
|
||||
`073_memory_vec.sql`
|
||||
`083_memory_vec.sql`
|
||||
- `src/app/api/memory/route.ts`, `[id]/route.ts`, `health/route.ts`
|
||||
- `src/app/api/memory/retrieve-preview/route.ts`
|
||||
- `src/app/api/memory/engine-status/route.ts`
|
||||
@@ -628,14 +628,15 @@ default TTL 5 min).
|
||||
|
||||
OmniRoute's memory engine supports **four embedding sources** (`src/lib/memory/embedding/`). Each has different trade-offs in **latency, cost, model quality, and setup complexity**.
|
||||
|
||||
### The Four Providers
|
||||
### The Embedding Sources
|
||||
|
||||
| Provider | Source | Latency | Cost | Quality | Setup |
|
||||
| -------------- | ------------------------------------------ | ------------------------------- | -------------------- | -------------------------- | ------------------ |
|
||||
| `transformers` | Local ONNX model (Xenova/all-MiniLM-L6-v2) | ~50-150ms (CPU) | Free | Good | `npm install` only |
|
||||
| `static` | Pre-computed vectors (cached) | <1ms | Free | N/A (depends on cache hit) | None |
|
||||
| `remote` | OpenAI / Cohere / Voyage API | ~100-300ms | $0.02-0.10/1M tokens | Excellent | API key |
|
||||
| `cache` | In-memory LRU layer over any source | <1ms (hit), full latency (miss) | Free | Same as underlying | None |
|
||||
| Provider | Source | Latency | Cost | Quality | Setup |
|
||||
| -------------- | ------------------------------------------ | ------------------------------- | -------------------- | -------------------------- | ----------------------------------- |
|
||||
| `transformers` | Local ONNX model (Xenova/all-MiniLM-L6-v2) | ~50-150ms (CPU) | Free | Good | `npm install` only |
|
||||
| `static` | Pre-computed vectors (cached) | <1ms | Free | N/A (depends on cache hit) | None |
|
||||
| `remote` | OpenAI / Cohere / Voyage API | ~100-300ms | $0.02-0.10/1M tokens | Excellent | API key |
|
||||
| `auto` | Picks the best available source at runtime | Same as chosen source | Free | Same as chosen source | None |
|
||||
| _(cache)_ | In-memory LRU layer over any source | <1ms (hit), full latency (miss) | Free | Same as underlying | Always on (not a selectable source) |
|
||||
|
||||
### Decision Tree
|
||||
|
||||
@@ -1092,7 +1093,7 @@ memoryManager.register(brainBackend);
|
||||
npx vitest run src/lib/memory/__tests__/generic-backend.test.ts --reporter=verbose
|
||||
```
|
||||
|
||||
Expected output: **26 tests, all passing** covering:
|
||||
Expected output: **35 tests, all passing** covering:
|
||||
|
||||
- Constructor (2)
|
||||
- Health check (4) — success, failure 500, network error, latency
|
||||
|
||||
@@ -6,9 +6,9 @@ lastUpdated: 2026-06-28
|
||||
|
||||
# open-sse Architecture
|
||||
|
||||
> **TL;DR**: `open-sse/` is the core streaming engine that powers every LLM request in OmniRoute. It contains ~900 files implementing the request pipeline, executors, services, MCP server, and translation layer. This guide explains how the pieces fit together.
|
||||
> **TL;DR**: `open-sse/` is the core streaming engine that powers every LLM request in OmniRoute. It contains ~1,400 files implementing the request pipeline, executors, services, MCP server, and translation layer. This guide explains how the pieces fit together.
|
||||
|
||||
**Source:** `open-sse/` (workspace package, ~900 files; 811 `.ts`)
|
||||
**Source:** `open-sse/` (workspace package, ~1,440 non-test `.ts` files)
|
||||
|
||||
---
|
||||
|
||||
@@ -49,13 +49,13 @@ open-sse/
|
||||
### Module Counts
|
||||
|
||||
| Directory | Files | Purpose |
|
||||
| `executors/` | 68 | Per-provider HTTP executors (unified via DefaultExecutor factory) |
|
||||
| `handlers/` | 16 | Request entry points (chatCore, responses, embeddings) |
|
||||
| `services/` | ~298 | Routing, caching, rate limiting, refresh, etc. |
|
||||
| `translator/` | ~27 | Format conversion (OpenAI ↔ Claude ↔ Gemini) |
|
||||
| `mcp-server/` | 32 | MCP tools and transports |
|
||||
| `utils/` | ~65 | Cross-cutting utilities (logging, error, stream) |
|
||||
| `config/` | ~10 | Provider configs, constants, registries |
|
||||
| `executors/` | 167 | Per-provider HTTP executors (unified via DefaultExecutor factory) |
|
||||
| `handlers/` | 157 | Request entry points (chatCore, responses, embeddings) |
|
||||
| `services/` | ~536 | Routing, caching, rate limiting, refresh, etc. |
|
||||
| `translator/` | 56 | Format conversion (OpenAI ↔ Claude ↔ Gemini) |
|
||||
| `mcp-server/` | 44 | MCP tools and transports |
|
||||
| `utils/` | ~108 | Cross-cutting utilities (logging, error, stream) |
|
||||
| `config/` | ~339 | Provider configs, constants, registries |
|
||||
|
||||
---
|
||||
|
||||
@@ -105,7 +105,7 @@ Resolves the request to a concrete `(provider, model, account, credentials)` tup
|
||||
|
||||
For `auto/*` models, this stage also:
|
||||
|
||||
- Runs the **9-factor scoring** algorithm (`services/autoCombo/`)
|
||||
- Runs the **15-factor scoring** algorithm (`services/autoCombo/`)
|
||||
- Selects a `provider+model` pair based on health, cost, latency, etc.
|
||||
|
||||
### Stage 2: Translate (translator/)
|
||||
@@ -245,7 +245,7 @@ Supports **19 routing strategies** (see `src/shared/constants/routingStrategies.
|
||||
| `reset-window` | Reset window-based routing |
|
||||
| `headroom` | Most remaining quota headroom first |
|
||||
| `strict-random` | Truly uniform (no quality weighting) |
|
||||
| `auto` | Use 9-factor scoring (`autoCombo/`) |
|
||||
| `auto` | Use 15-factor scoring (`autoCombo/`) |
|
||||
| `lkgp` | Last known good provider first |
|
||||
| `context-optimized` | Best for long-context requests |
|
||||
| `fusion` | Fan out to a panel in parallel, then synthesize via a judge (`fusion.ts`) |
|
||||
@@ -280,7 +280,7 @@ Services are **focused, single-purpose modules** that handlers compose. The big
|
||||
### Routing & Combo
|
||||
|
||||
- `combo.ts` — entry point for combo-routed requests
|
||||
- `services/autoCombo/` — 9-factor scoring, 8 auto routing strategies
|
||||
- `services/autoCombo/` — 15-factor scoring, 8 auto routing strategies
|
||||
- `wildcardRouter.ts` — matches wildcard routes (`gpt-*`)
|
||||
- `modelFamilyFallback.ts` — T5 intra-family fallback
|
||||
|
||||
@@ -406,9 +406,9 @@ Common translations:
|
||||
|
||||
`open-sse/mcp-server/` implements the **Model Context Protocol** server:
|
||||
|
||||
- **30+ tools** (provider management, combos, memory, cache, compression, 1proxy, skills)
|
||||
- **110 tools** (provider management, combos, memory, cache, compression, proxy, skills, gamification, plugins, Notion, Obsidian, local corpus)
|
||||
- **3 transports**: stdio, SSE, Streamable HTTP
|
||||
- **31 scopes** for fine-grained authorization
|
||||
- **33 scopes** for fine-grained authorization
|
||||
|
||||
### Tool Registration
|
||||
|
||||
@@ -485,13 +485,13 @@ This handles:
|
||||
|
||||
`open-sse/config/` holds the configuration layer:
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------- | --------------------------------- |
|
||||
| `providerRegistry.ts` | 338 provider definitions |
|
||||
| `providerModels.ts` | Model aliases, format mapping |
|
||||
| `constants.ts` | Timeouts, limits, status codes |
|
||||
| `defaultThinkingSignature.ts` | Default Claude thinking signature |
|
||||
| `modelStrip.ts` (in services) | Per-provider field stripping |
|
||||
| File | Purpose |
|
||||
| ----------------------------- | ------------------------------------------------- |
|
||||
| `providerRegistry.ts` | Chat-model registry over the 352-provider catalog |
|
||||
| `providerModels.ts` | Model aliases, format mapping |
|
||||
| `constants.ts` | Timeouts, limits, status codes |
|
||||
| `defaultThinkingSignature.ts` | Default Claude thinking signature |
|
||||
| `modelStrip.ts` (in services) | Per-provider field stripping |
|
||||
|
||||
### Provider Registry Schema
|
||||
|
||||
@@ -570,7 +570,7 @@ The routing engine has strict performance budgets:
|
||||
- [ARCHITECTURE.md](../architecture/ARCHITECTURE.md) — high-level architecture
|
||||
- [CODEBASE_DOCUMENTATION.md](../architecture/CODEBASE_DOCUMENTATION.md) — engineering reference
|
||||
- [REPOSITORY_MAP.md](../architecture/REPOSITORY_MAP.md) — directory-by-directory
|
||||
- [AUTO-COMBO.md](../routing/AUTO-COMBO.md) — 9-factor scoring
|
||||
- [AUTO-COMBO.md](../routing/AUTO-COMBO.md) — 15-factor scoring
|
||||
- [MCP-SERVER.md](./MCP-SERVER.md) — MCP server
|
||||
- [A2A-SERVER.md](./A2A-SERVER.md) — A2A server
|
||||
- Source: `open-sse/` (400+ files, ~143K LOC)
|
||||
|
||||
@@ -97,6 +97,12 @@ Plugins are discovered from:
|
||||
1. `~/.omniroute/plugins/<name>/` — user-local installs
|
||||
2. `OMNIROUTE_PLUGIN_PATH` env var — custom directory
|
||||
|
||||
Both are **CLI-only**. The server-side plugin runtime (the marketplace/`plugin.json`
|
||||
plugins that run inside the proxy) has its own scanner with its own override,
|
||||
`OMNIROUTE_PLUGINS_DIR` — see
|
||||
[PLUGIN_MARKETPLACE.md → Plugin directory](./PLUGIN_MARKETPLACE.md#plugin-directory).
|
||||
Setting one does not affect the other.
|
||||
|
||||
Loading errors are caught and printed as warnings — a broken plugin never crashes the CLI.
|
||||
|
||||
## Security
|
||||
|
||||
@@ -161,10 +161,28 @@ Plugins live under the OmniRoute data directory:
|
||||
└─ index.js # (or whatever manifest.main points to)
|
||||
```
|
||||
|
||||
`getDefaultPluginDir()` (`src/lib/plugins/scanner.ts`) resolves this to
|
||||
`<home>/.omniroute/plugins`, where `<home>` is taken from the `HOME` /
|
||||
`USERPROFILE` environment variables. `POST /api/plugins/scan` discovers any
|
||||
subdirectory there that holds a valid `plugin.json` and registers it.
|
||||
`getDefaultPluginDir()` (`src/lib/plugins/scanner.ts`) resolves that directory in
|
||||
three steps:
|
||||
|
||||
1. **`OMNIROUTE_PLUGINS_DIR`**, when set — used verbatim, whatever `HOME` says. This is
|
||||
the explicit knob for Docker/K8s, where the plugin tree is bind-mounted at a path
|
||||
that usually has nothing to do with the container's home directory (#11827).
|
||||
2. `<home>/.omniroute/plugins`, where `<home>` comes from the `HOME` / `USERPROFILE`
|
||||
environment variables.
|
||||
3. `/tmp/.omniroute/plugins`, when the process exports no home at all.
|
||||
|
||||
The resolved directory is logged once at startup as `scanner.dir_resolved`, naming the
|
||||
input that won (`OMNIROUTE_PLUGINS_DIR`, `home`, or `no-home-fallback`) — so an image
|
||||
that silently lands on step 3 says so, instead of only reporting an empty plugin list.
|
||||
`POST /api/plugins/scan` discovers any subdirectory there that holds a valid
|
||||
`plugin.json` and registers it; the same directory is the root that
|
||||
`pluginManager.install()` copies plugins into, so an override moves discovery and
|
||||
installation together.
|
||||
|
||||
> **`OMNIROUTE_PLUGINS_DIR` is not `OMNIROUTE_PLUGIN_PATH`.** The latter is read only by
|
||||
> the CLI command-plugin loader (`bin/cli/plugins.mjs`) to find `omniroute-cmd-*` npm
|
||||
> packages that add `omniroute` subcommands — it has no effect on the runtime scanner
|
||||
> described here. See [PLUGINS.md](./PLUGINS.md) for that side.
|
||||
|
||||
### Custom marketplace registry URL
|
||||
|
||||
|
||||
@@ -18,17 +18,14 @@ keep failing.
|
||||
|
||||
## Supported Events
|
||||
|
||||
The `WebhookEvent` type (`src/lib/webhookDispatcher.ts`) currently models:
|
||||
The `WebhookEvent` type (`src/lib/webhooks/eventDescriptions.ts`, consumed by `src/lib/webhookDispatcher.ts`) currently models exactly four events:
|
||||
|
||||
| Event | Fires when |
|
||||
| -------------------- | --------------------------------------------------------- |
|
||||
| `request.completed` | A proxied request completes successfully |
|
||||
| `request.failed` | A proxied request fails after all retries/fallback |
|
||||
| `provider.error` | A provider returns an error eligible for circuit-breaking |
|
||||
| `provider.recovered` | A previously failing provider returns to a healthy state |
|
||||
| `quota.exceeded` | An API key crosses a budget/quota threshold |
|
||||
| `combo.switched` | A combo strategy switches its primary target |
|
||||
| `test.ping` | Synthetic event used by the test endpoint |
|
||||
| Event | Fires when |
|
||||
| ------------------- | -------------------------------------------------- |
|
||||
| `request.completed` | A proxied request completes successfully |
|
||||
| `request.failed` | A proxied request fails after all retries/fallback |
|
||||
| `quota.exceeded` | An API key crosses a budget/quota threshold |
|
||||
| `test.ping` | Synthetic event used by the test endpoint |
|
||||
|
||||
Subscriptions accept the literal `"*"` to receive every event. Unknown event
|
||||
names in `events` are ignored at dispatch time.
|
||||
@@ -123,23 +120,26 @@ Table `webhooks` (migration `011_webhooks.sql`):
|
||||
| `last_status` | INT | HTTP status of the last attempt (0 = network) |
|
||||
| `failure_count` | INT | Resets to 0 on success, +1 on failure |
|
||||
|
||||
There is **no separate `webhook_deliveries` table** in the current schema —
|
||||
delivery history is aggregated on the `webhooks` row. If you need full audit
|
||||
history, consume `request.completed` / `audit` style events from a downstream
|
||||
log store.
|
||||
Delivery history is persisted in the dedicated `webhook_deliveries` table
|
||||
(migration `069_webhook_deliveries.sql`, written via
|
||||
`src/lib/db/webhookDeliveries.ts::insertDelivery` on every attempt), in addition
|
||||
to the aggregate counters on the `webhooks` row. Kind metadata (Slack / Discord /
|
||||
Telegram / custom payload transformers) was added by `070_webhooks_kind_metadata.sql`.
|
||||
|
||||
## REST API
|
||||
|
||||
All endpoints require management auth (`requireManagementAuth`).
|
||||
|
||||
| Endpoint | Method | Description |
|
||||
| ------------------------- | ------ | ------------------------------- |
|
||||
| `/api/webhooks` | GET | List webhooks (secrets masked) |
|
||||
| `/api/webhooks` | POST | Create webhook |
|
||||
| `/api/webhooks/[id]` | GET | Webhook detail (full secret) |
|
||||
| `/api/webhooks/[id]` | PUT | Update fields |
|
||||
| `/api/webhooks/[id]` | DELETE | Remove |
|
||||
| `/api/webhooks/[id]/test` | POST | Fire a `test.ping` (no retries) |
|
||||
| Endpoint | Method | Description |
|
||||
| ------------------------------- | ------ | ---------------------------------------- |
|
||||
| `/api/webhooks` | GET | List webhooks (secrets masked) |
|
||||
| `/api/webhooks` | POST | Create webhook |
|
||||
| `/api/webhooks/[id]` | GET | Webhook detail (full secret) |
|
||||
| `/api/webhooks/[id]` | PUT | Update fields |
|
||||
| `/api/webhooks/[id]` | DELETE | Remove |
|
||||
| `/api/webhooks/[id]/test` | POST | Fire a `test.ping` (no retries) |
|
||||
| `/api/webhooks/[id]/deliveries` | GET | Recent delivery attempts for one webhook |
|
||||
| `/api/webhooks/validate-url` | POST | Pre-flight URL validation (SSRF guard) |
|
||||
|
||||
`GET /api/webhooks` masks the secret to `<first 10 chars>...` to avoid leaking
|
||||
on listing pages. Use the `[id]` GET when you actually need the secret.
|
||||
@@ -153,7 +153,7 @@ curl -X POST http://localhost:20128/api/webhooks \
|
||||
-d '{
|
||||
"url": "https://hooks.slack.com/services/...",
|
||||
"secret": "whsec_my_shared_secret",
|
||||
"events": ["quota.exceeded", "provider.error"],
|
||||
"events": ["quota.exceeded", "request.failed"],
|
||||
"description": "Slack alerts"
|
||||
}'
|
||||
```
|
||||
@@ -203,21 +203,6 @@ The dashboard page at `/dashboard/webhooks` (see
|
||||
}
|
||||
```
|
||||
|
||||
### provider.error
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "provider.error",
|
||||
"timestamp": "2026-05-13T20:31:00.000Z",
|
||||
"data": {
|
||||
"provider": "anthropic",
|
||||
"status": 503,
|
||||
"consecutive_failures": 5,
|
||||
"circuit_state": "open"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### test.ping
|
||||
|
||||
```json
|
||||
@@ -255,5 +240,5 @@ absence).
|
||||
|
||||
- [API_REFERENCE.md](../reference/API_REFERENCE.md) — full management API surface
|
||||
- [RESILIENCE_GUIDE.md](../architecture/RESILIENCE_GUIDE.md) — circuit breaker / cooldown
|
||||
semantics that drive `provider.error` / `provider.recovered`
|
||||
semantics behind provider failures surfaced via `request.failed`
|
||||
- Source: `src/lib/webhookDispatcher.ts`, `src/lib/db/webhooks.ts`
|
||||
|
||||