Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
166 commits
Select commit Hold shift + click to select a range
7a098a0
chore(release): open v3.8.45 development cycle
diegosouzapw Jul 4, 2026
bb62c2a
chore(release): parallel-cycle flow — sync-next-cycle script + Hard R…
diegosouzapw Jul 4, 2026
0757503
perf(test): tsx/esm loader + tsx 4.23 + órfãos recuperados + CI via n…
diegosouzapw Jul 5, 2026
5a4bde1
ci: dedup heavy pipeline — compat to nightly, coverage folded into un…
diegosouzapw Jul 5, 2026
059dbe9
feat(quality): no-new-warnings por PR — ESLint bulk suppressions + li…
diegosouzapw Jul 5, 2026
1e59d14
docs(changelog): v3.8.45 bullets for the tests+quality+CI pipeline ov…
diegosouzapw Jul 5, 2026
2d5bd41
fix(api): stabilize relay SSRF-guard binding for minified builds (#61…
diegosouzapw Jul 5, 2026
c04ce38
fix(mcp): forward extra context through static tool loops (#6178) (#6…
diegosouzapw Jul 5, 2026
8cb7f00
fix(services): 9Router embed route + pre-spawn port probe (#6205) (#6…
diegosouzapw Jul 5, 2026
201908d
fix(backend): system-first memory injection for strict providers (#61…
diegosouzapw Jul 5, 2026
670d502
fix(auth): clear error for stale-key decryption failures (#6148) (#6226)
diegosouzapw Jul 5, 2026
cbc16af
fix(backend): record reasoning source for zero-metered reasoning mode…
diegosouzapw Jul 5, 2026
adde9e4
fix(providers): refresh stale NVIDIA NIM model registry (#6108) (#6223)
diegosouzapw Jul 5, 2026
1473261
fix(backend): distinct max_input_tokens for GPT-family models (#6191)…
diegosouzapw Jul 5, 2026
cf6c279
fix(oauth): extract keychain-import-only guard to restore file-size f…
diegosouzapw Jul 5, 2026
e44f125
fix(dashboard): stop model-test error freezing the page (React #31 ob…
diegosouzapw Jul 5, 2026
8a7b62e
fix(dashboard): remove the always-on Auto-Routing (combo) banner from…
diegosouzapw Jul 5, 2026
f2ad9b2
fix(cline): force upstream streaming for Cline/ClinePass (streaming-o…
diegosouzapw Jul 5, 2026
b3a2cfe
fix(providers): correct Kiro model catalog to real upstream ids (#6170)
diegosouzapw Jul 5, 2026
286fdf8
fix(sse): surface ChatGPT-web image silent-drop as an accurate error …
diegosouzapw Jul 5, 2026
6816bcd
fix(dashboard): providers page data-timeout guard + live-ws standalon…
diegosouzapw Jul 5, 2026
6a12ba0
fix(translator): strip reasoning param for nvidia z-ai/glm-5.2 (#6181)
kanztu Jul 5, 2026
0ed6780
fix: add nvidia to PROVIDER_TOOL_LIMITS (1536) to prevent tool trunca…
LuisAlejandroVega Jul 5, 2026
5e3a95b
feat(provider): add Claude 5 Sonnet to Claude Web provider (#6200) (#…
Iammilansoni Jul 5, 2026
b074c6d
fix(cli): detect POSIX auto-set HOSTNAME via os.hostname() to fix bin…
Theadd Jul 5, 2026
dc7eeba
feat(sse): surface Kiro adaptive-thinking reasoning as reasoning_cont…
VXNCXNX Jul 5, 2026
b1e2725
fix(chatcore): exempt opencode client from the default 128-tool trunc…
DKotsyuba Jul 5, 2026
e755c5a
fix(providers): refresh GitHub Copilot catalog (#6154)
backryun Jul 5, 2026
00c74ea
chore(quality): rebaseline kiro-translator file-size debt from #6213
diegosouzapw Jul 5, 2026
74546ed
fix(doctor): resolve two false-positive WARNs (#6162) (#6163)
arssnndr Jul 5, 2026
44e85a7
fix(doubao-web): switch provider to Dola global (#6235)
backryun Jul 5, 2026
b834c74
fix(providers): register zed in OAuth PROVIDERS to fix Unknown provid…
anki1kr Jul 5, 2026
5acfbe8
fix(oauth): align zed in OAUTH_PROVIDER_IDS + config enum after #6078…
diegosouzapw Jul 5, 2026
0585701
fix(mitm): strip colons from macOS cert fingerprint before keychain m…
rianonehub Jul 5, 2026
9b986fa
docs(architecture): sync stale DB-layer counts (45+/55 → 95+/110+) in…
diegosouzapw Jul 5, 2026
9827ae6
fix(api): count tool_use/tool_result/thinking blocks in count_tokens …
diegosouzapw Jul 5, 2026
9ebb53e
fix(antigravity): strip trailing assistant prefill turn for Vertex Cl…
diegosouzapw Jul 5, 2026
7e2b839
fix(security): require management auth for mutable cloud routes (#623…
Jul 5, 2026
0e0ca7e
fix(dashboard): use connection.id (UUID) not connection.provider (cat…
KooshaPari Jul 5, 2026
f26aa16
feat(rankings): add 'Configured Only' filter to Free Provider Ranking…
Iammilansoni Jul 5, 2026
c347abb
fix(i18n): add 118 missing Italian translations (#6212)
serverless83 Jul 5, 2026
58abebd
test(dashboard): realign #6145 onboarding-href guard to the #6166 hel…
diegosouzapw Jul 5, 2026
826a66f
feat(providers): add Yuanbao (web) cookie-session provider (#6196) (#…
diegosouzapw Jul 5, 2026
5531fc7
feat(providers): route built-in agentrouter through dynamic CC wire i…
diegosouzapw Jul 5, 2026
776a7a3
feat(providers): bulk-add API keys for Cloudflare Workers AI (#6174) …
diegosouzapw Jul 5, 2026
cefbcfb
feat(dashboard): routing/settings UX clarity — share %, Cloud Sync re…
diegosouzapw Jul 5, 2026
1044821
feat(combo): add option to disable session stickiness (#6168) (#6252)
diegosouzapw Jul 5, 2026
149b086
feat(docker): OMNIROUTE_NO_SUDO env flag for root-less MITM cert trus…
diegosouzapw Jul 5, 2026
9285dc1
feat(providers): add Requesty as an OpenAI-compatible gateway provide…
diegosouzapw Jul 5, 2026
4d330da
fix(providers): remove deprecated MiMo v2 entries (#6248)
backryun Jul 5, 2026
9899a6d
fix(github-skills): add missing import, add unit tests, fix settings …
Moseyuh333 Jul 5, 2026
f237c07
Fix/5976 continued (#6216)
hartmark Jul 5, 2026
fc16dcd
feat(dashboard): filter Free Provider Rankings by configured/availabl…
diegosouzapw Jul 5, 2026
143b7b1
ci: unblock test jobs from the Build gate (start at minute 0) (#6275)
diegosouzapw Jul 5, 2026
f35839f
ci(build): switch Next.js production build to Turbopack (1.9x faster)…
diegosouzapw Jul 5, 2026
046093b
feat(build): make Turbopack the default bundler for dev and build (#6…
diegosouzapw Jul 5, 2026
c26984e
feat(docker): build the image with Turbopack (v3.8.27 panic gone on N…
diegosouzapw Jul 5, 2026
bfd8a65
ci: opt-in self-hosted VPS runners for the release window (anti-queue…
diegosouzapw Jul 5, 2026
8a2b522
docs(changelog): restore v3.8.45/v3.8.44 sections eaten by the #6193 …
diegosouzapw Jul 5, 2026
faf68a2
fix(dashboard): null-guard connection in EditConnectionModal base-URL…
diegosouzapw Jul 5, 2026
509fd54
chore(release-green): clear test-masking + docs-all HARD reds for the…
diegosouzapw Jul 5, 2026
fecf888
fix(quality): clear the cycle's 11 net-new ESLint errors + make valid…
diegosouzapw Jul 5, 2026
bf1481f
fix(skills): generate the missing omni-github-skills registry entry +…
diegosouzapw Jul 5, 2026
265d93f
fix(combo): restrict the #6216 empty-stream failover to truly empty b…
diegosouzapw Jul 5, 2026
dc5ae96
chore(quality): prune stale ESLint suppressions (4,273 -> 4,233)
diegosouzapw Jul 5, 2026
f680aac
fix(proxy): #6246 stop the v3.8.44 proxy IP-leak + over-deactivation …
diegosouzapw Jul 5, 2026
b6ffe8c
fix(proxy): make "Test All" read-only + add bulk enable/disable (#624…
diegosouzapw Jul 5, 2026
ddd5464
fix(resilience): evict sticky affinity on pinned-account failover (#6…
diegosouzapw Jul 5, 2026
01ce92a
fix(sse): drop commentary-phase text in Responses passthrough (#6199)…
diegosouzapw Jul 5, 2026
234956d
fix: bug-fix sweep — log path, AgentBridge DNS, opencode-go headers, …
diegosouzapw Jul 5, 2026
8e33393
fix(docker): add id= to BuildKit cache mounts for strict builders (#6…
karimalsalah Jul 5, 2026
aabefc8
fix(sse): strip zero-width markers from streamed tool-call arguments …
DKotsyuba Jul 5, 2026
efc92c6
ci(quality): merge-integrity fast-gates + pre-flight hermetic mode (#…
diegosouzapw Jul 5, 2026
1ad8b3b
fix(a2a): finish the #6186 catalog-count update — 3 hardcoded 22s lef…
diegosouzapw Jul 5, 2026
5c953d1
fix(quality): type the 7 net-new 'as any' casts from #6292 (Lint red …
diegosouzapw Jul 5, 2026
dd12539
fix(api): Zod-validate POST /api/github-skills + document new gate en…
diegosouzapw Jul 5, 2026
ffe825b
fix(quality): clear the 2 remaining heavy-gate reds on the release tip
diegosouzapw Jul 6, 2026
6f41775
fix(security): 405 method-first for /api/keys/{id}/devices (dast-smok…
diegosouzapw Jul 6, 2026
6c1d597
fix(mitm): test suite and CI must never mutate the OS trust store (OM…
diegosouzapw Jul 6, 2026
b74c63a
ci(vps): hermetic nightly pre-flight on the release runner (descoped:…
diegosouzapw Jul 6, 2026
5ecca12
chore(quality): v3.8.45 cycle-close file-size rebaseline (Phase 0 dri…
diegosouzapw Jul 6, 2026
264dda7
chore(quality): v3.8.45 cycle-close cognitive/cyclomatic rebaseline (…
diegosouzapw Jul 6, 2026
192f38d
chore(release): open the v3.8.46 cycle (parallel-cycle model, cut at …
diegosouzapw Jul 6, 2026
1f419cf
chore(release): sync main (v3.8.45 close) into release/v3.8.46 — para…
diegosouzapw Jul 6, 2026
ab8b41b
docs(i18n): sync finalized [3.8.45] CHANGELOG section into 42 mirrors…
diegosouzapw Jul 6, 2026
5d07bdd
perf(release-green): run the 4 slow suites concurrently — pre-flight …
diegosouzapw Jul 6, 2026
c043034
test(ci): quarantine concurrency-sensitive flakes into a serial pass …
diegosouzapw Jul 6, 2026
f3d285b
fix(ci): sync-next-cycle — widen git() maxBuffer (ENOBUFS on >1MiB CH…
diegosouzapw Jul 6, 2026
0776f83
docs(changelog): v3.8.46 bullets for the release-process improvements…
diegosouzapw Jul 6, 2026
a69547a
fix(sse): coerce tool-call function schema root type:null to "object"…
diegosouzapw Jul 6, 2026
a0ab693
fix(docker): make MITM manager Turbopack stub opt-in so npm/Electron/…
diegosouzapw Jul 6, 2026
0785cd2
feat(cerebras): add Gemma 4 31B model (#6331)
backryun Jul 6, 2026
8853b25
fix(dashboard): trust provider topology live state (#6322)
xz-dev Jul 6, 2026
fb3da7c
fix(live-ws): reject on bind failure instead of crashing the process …
vinayakkulkarni Jul 6, 2026
b796f2b
feat(providers): link web session guide to provider site (#6316)
jordansilly77-stack Jul 6, 2026
049bad4
fix(internal): use explicit internal key selection for dashboard prob…
jmengit Jul 6, 2026
49795c2
fix(api): dynamic import for MITM + fix Turbopack over-bundling warni…
Iammilansoni Jul 6, 2026
0086f17
fix(api): filter specialty model catalogs (#6303)
makcimbx Jul 6, 2026
f60090b
fix(providers): venice-web static-catalog fallback for models listing…
diegosouzapw Jul 6, 2026
6fff4d6
fix(auth): dedup Codex OAuth import by workspace AND user id (#6301) …
diegosouzapw Jul 6, 2026
db7a6c2
fix(db): migration safety abort — add bypass hint + memoize to stop c…
diegosouzapw Jul 6, 2026
e3d29d1
fix(cli): register reset-password subcommand + non-TTY stdin path (#6…
diegosouzapw Jul 6, 2026
76b1b04
fix(sse): do not inflate probe-sized max_tokens in reasoning buffer (…
diegosouzapw Jul 6, 2026
c50a83a
fix(providers): resolve qodercli via cliRuntime on Windows (#6263) (#…
diegosouzapw Jul 6, 2026
f1a02f6
fix(startup): best-effort self-heal for corrupted Turbopack dev cache…
diegosouzapw Jul 6, 2026
d98a315
fix(resilience): combo falls back to compat-rejected healthy targets …
diegosouzapw Jul 6, 2026
53aa6d9
fix(providers): add redacted WS debug logging to copilot-m365-web (#6…
diegosouzapw Jul 6, 2026
4a2172e
fix(i18n): translate provider connection-status filter labels across …
diegosouzapw Jul 6, 2026
cd4a720
fix(providers): bound GitLab Duo tool-exchange prompt to avoid 422 (#…
diegosouzapw Jul 6, 2026
2f4b793
fix(api): provider-models route — redirect→local-catalog fallback + c…
diegosouzapw Jul 6, 2026
a2fabdd
fix(api): return JSON 404 for unknown /v1/* routes (#6405) (#6435)
chirag127 Jul 6, 2026
c8e94d7
fix(chatCore): align non-streaming body.model with X-OmniRoute-Model …
chirag127 Jul 6, 2026
6ea7c68
fix(api): env-var master keys see full /v1/models catalog (#6406) (#6…
chirag127 Jul 6, 2026
ac96c0d
fix(completions): echo requested body.model on /v1/completions to mat…
chirag127 Jul 6, 2026
d11bf52
fix(api): coalesce concurrent GET /v1/models to one builder run (#640…
chirag127 Jul 6, 2026
fa3a09c
fix(api): echo X-OmniRoute-Compression response header (#6422) (#6441)
chirag127 Jul 6, 2026
d12ac37
fix(api): reject non-JSON Content-Type on /v1/chat/completions with 4…
chirag127 Jul 6, 2026
a7e0ddd
fix(chat): validate scalar params before provider lookup (#6412) (#6437)
chirag127 Jul 6, 2026
a73c6ca
fix(sse): reject non-string `model` with 400 before resolver (#6407) …
chirag127 Jul 6, 2026
465f0a0
fix(combo): advance round-robin pointer past the served model (port f…
diegosouzapw Jul 6, 2026
7a7a72c
fix(network): enable Happy Eyeballs on direct egress (port from 9rout…
diegosouzapw Jul 6, 2026
1636ace
fix(executors): strip client context_management on 400 (port from 9ro…
diegosouzapw Jul 6, 2026
f0b085e
fix(executors): inject reasoning_content for native Kimi provider (po…
diegosouzapw Jul 6, 2026
4b1c185
fix(executors): strip client_metadata on the OpenCode path (port from…
diegosouzapw Jul 6, 2026
58bb2cd
fix(executors): strip nested reasoning_content for Mistral (port from…
diegosouzapw Jul 6, 2026
cfbc2c2
fix(translator): suppress </think> marker for Antigravity client (por…
diegosouzapw Jul 6, 2026
d415cc0
fix(translator): strip thinking for NVIDIA glm-5.2 (port from 9router…
diegosouzapw Jul 6, 2026
bd65eeb
fix(executors): strip client_metadata for NVIDIA requests (port from …
diegosouzapw Jul 6, 2026
259d9b0
fix(headroom): detect python managed by mise/pyenv/asdf/conda (port f…
diegosouzapw Jul 6, 2026
2ecaae7
fix(translator): preserve co-located functionResponse parts in gemini…
diegosouzapw Jul 6, 2026
b67f2c5
fix(dashboard): disambiguate colliding passthrough model aliases (por…
diegosouzapw Jul 6, 2026
437ca48
feat(providers): add Huancheng Public API (hcnsec) OpenAI-compatible …
diegosouzapw Jul 6, 2026
c6a8007
feat(providers): add DigitalOcean AI as an OpenAI-compatible provider…
diegosouzapw Jul 6, 2026
b7ac526
feat(dashboard): 'Open <host>' link in Add session cookie modal (#626…
diegosouzapw Jul 6, 2026
c7c7d47
feat(sse): per-connection routing override (native vs CLIProxyAPI) (#…
diegosouzapw Jul 6, 2026
ecf3d3a
feat(ci): check:test-masking flags inline-reimplemented prod conditio…
diegosouzapw Jul 6, 2026
8db5a66
feat(combo): sequential 'pipeline' combo strategy (#6297) (#6396)
diegosouzapw Jul 6, 2026
62b1bc9
feat(api): standardize effort + thinking request params (#6241) (#6398)
diegosouzapw Jul 6, 2026
f5147d0
feat(providers): copilot-m365-web enterprise (work) tier support (#63…
diegosouzapw Jul 6, 2026
18fa0b2
feat(providers): Gemini tool-calling end-to-end on /v1beta (#6222) (#…
diegosouzapw Jul 6, 2026
58f53e3
feat(proxy): native proxy-pool round-robin / egress IP rotation (#636…
diegosouzapw Jul 6, 2026
3cc48ed
fix(oauth): preserve Kiro IDC region in SSO-cache auto-import (#6113)
diegosouzapw Jul 6, 2026
0433594
feat(providers): add Zed hosted LLM aggregator (native-app sign-in) —…
diegosouzapw Jul 6, 2026
a03c22e
fix(api): accept mode:'caveman' + stacked default pipeline yields 0% …
chirag127 Jul 7, 2026
62a74fa
fix(mitm): redact Set-Cookie in sanitizeHeaders to prevent session-to…
developerjillur Jul 7, 2026
27867e5
fix(providers): treat recoverable Antigravity/Cloud-Code 403s as proj…
developerjillur Jul 7, 2026
17da3b6
fix(api-manager): preserve combos in model fallback (#6443)
jmengit Jul 7, 2026
b681308
feat(api): add hidePaidModels setting to filter paid-only models from…
chirag127 Jul 7, 2026
9570805
fix(codex): isolate Spark quota and stabilize quota UI (#6336)
xz-dev Jul 7, 2026
69d2b31
Swingtempo/fixwindowscodex (#6312)
swingtempo Jul 7, 2026
e45e6c3
feat: add TinyFish Fetch support to web-fetch provider and update rel…
dtybnrj Jul 7, 2026
f6b4926
feat(glm): add team plan quota settings for glm-cn connections (#6351)
hao3039032 Jul 7, 2026
958260a
Add Codex reset-credit redemption flow (#6361)
JxnLexn Jul 7, 2026
c3cef78
fix(compression): unknown engine names surface validationErrors inste…
chirag127 Jul 7, 2026
9a33cda
fix(compression): add intra-message dedup to session-dedup engine (#6…
chirag127 Jul 7, 2026
526048d
fix(ui): prevent silent overwrite of existing API key connections on …
dilneiss Jul 7, 2026
0d3d31c
feat(sse): provider-family auto combos auto/glm, auto/minimax, auto/z…
diegosouzapw Jul 7, 2026
4f294c0
feat(sse): exclude paid-only models from auto/* candidate pool when h…
diegosouzapw Jul 7, 2026
d776a42
fix(api): invalidate /v1/models + specialty catalogs on DB writes; fi…
diegosouzapw Jul 7, 2026
a533a31
fix(agentSkills): generator honors an absolute outputDir (#6366 regre…
diegosouzapw Jul 7, 2026
ceff705
fix(security): unbiased crypto digits for the doubao synthetic device…
diegosouzapw Jul 7, 2026
8fb3a2c
chore(quality): v3.8.46 pre-flight — type test anys, rebaseline cycle…
diegosouzapw Jul 7, 2026
b1d5f20
chore(release): open v3.8.47 development cycle
diegosouzapw Jul 7, 2026
d7bb537
feat: add headroom integration + resource optimizations for container…
oyi77 Jul 7, 2026
5610bfd
fix: optimize token healthcheck sweep and fix Dockerfile build syntax
oyi77 Jul 7, 2026
3dc31e5
perf: reduce sweep CPU with batch limit, stagger, lightweight health …
oyi77 Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 10 additions & 0 deletions .omo/run-continuation/ses_105eba89fffeuccNC88SJJBRTV.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"sessionID": "ses_105eba89fffeuccNC88SJJBRTV",
"updatedAt": "2026-06-24T20:01:32.931Z",
"sources": {
"background-task": {
"state": "idle",
"updatedAt": "2026-06-24T20:01:32.931Z"
}
}
}
153 changes: 153 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ connection continue serving other models.
- **2 spaces**, semicolons, double quotes, 100 char width, es5 trailing commas (enforced by lint-staged via Prettier)
- **Imports**: external → internal (`@/`, `@omniroute/open-sse`) → relative
- **Naming**: files=camelCase/kebab, components=PascalCase, constants=UPPER_SNAKE
- **ESLint**: `no-eval`, `no-implied-eval`, `no-new-func` = error everywhere; `no-explicit-any` = warn in `open-sse/` and `tests/`
- **ESLint**: `no-eval`, `no-implied-eval`, `no-new-func` = error everywhere; `no-explicit-any` = **error** in `open-sse/` and `tests/` (since #6218 — pre-existing violations are frozen in `config/quality/eslint-suppressions.json`, new ones must be fixed; `npm run lint` applies the suppressions and is what CI runs)
- **TypeScript**: `strict: false`, target ES2022, module esnext, resolution bundler. Prefer explicit types.

### Database
Expand Down Expand Up @@ -543,7 +543,7 @@ the stale-enforcement added in Fase 6A.3.
21. **Release-freeze — the FROZEN release branch belongs to the release captain; development does NOT stop (parallel-cycle model, 2026-07-04).** `/generate-release` opens a marker issue labeled `release-freeze` at the start of reconciliation (Phase 0a), **immediately cuts the next cycle's branch `release/vX+1` from the frozen tip (Phase 0a.0b — bump + living release PR + re-home of open PRs)**, and closes the freeze once the release PR squash-merges to `main`. Before merging **any** PR, every campaign workflow (`/review-issues`, `/review-prs`, `/implement-features`, `/green-prs`, `/port-upstream-*`) **MUST** check `gh issue list --repo diegosouzapw/OmniRoute --label release-freeze --state open` — if a freeze is active: **NEVER merge into the frozen `release/vX.Y.Z` named in the freeze title**; instead resolve the ACTIVE development branch (the **highest** `release/v*` by semver — normally `release/vX+1`, announced in a freeze-issue comment) and **retarget the PR there** (`gh pr edit <N> --base release/vX+1`, then VERIFY with `gh pr view <N> --json baseRefName` — the edit fails silently) and merge normally. **HOLD only when the highest release/v\* branch IS the frozen one** (the short window before 0a.0b completes, or a pre-parallel-cycle release) — in that case leave the PR ready and open, tell the operator, and resume when the next branch appears or the freeze lifts. The just-shipped fixes reach `release/vX+1` via the Phase 5 sync-back (`scripts/release/sync-next-cycle.mjs`); do not try to sync mid-release. This is a **coordination signal, not a permission lock**: the release captain and the campaign sessions share the `diegosouzapw` identity, so a GitHub branch-protection lock cannot distinguish them — only this honored marker prevents the mid-release commit races that forced full CHANGELOG re-reconciliation in v3.8.40/v3.8.41 (a parallel campaign advanced `release/vX.Y.Z` by 34 commits mid-run). The release captain's own reconciliation/cycle-open pushes are exempt — they _are_ the release. Fixes that must land during a freeze (a homologation finding) follow the post-merge read-only rule: land on `main` first via `fix/release-vX.Y.Z-*`. **⛔ ONLY `/generate-release` may raise a release-freeze, and ONLY at its Phase 0a (start of generating a new version) — lifted at Phase 12c after the squash-merge to `main`.** No campaign, session, or agent may open a `release-freeze` marker at any other time — a freeze is **never** a mid-development coordination tool. If a session ever believes a freeze is genuinely, unavoidably necessary outside the `/generate-release` flow, it **MUST first ask the operator (`diegosouzapw`) in chat, explicitly alert "estou criando um freeze" and get an explicit yes** — never open, extend, or re-open a `release-freeze` autonomously. Conversely, do **not** close/lift an active `/generate-release` freeze to unblock campaign merges: it protects the captain's single clean CI run and auto-lifts at Phase 12c — closing it early re-triggers the exact commit race it prevents. Verify a freeze is legitimate before acting on it: an open `release-freeze` whose title/body references an **OPEN** release PR (`gh pr view <N> --json state`) is the authorized captain freeze — hold, don't touch.
22. **Cross-session safety — this repo is worked by MANY parallel sessions/agents at once; never step on another's in-flight work.** Two absolute bans, both recurring incidents (this rule exists because they keep happening):
- **(a) Never `git stash` / `git stash pop` — ANYWHERE in this repo, including inside an isolated worktree, and including inside any subagent you dispatch.** `git stash` operates on the **shared repository object store**, not the per-worktree working tree — so a stash pushed or popped in one session can silently clobber or resurrect another parallel session's uncommitted changes. This is not hypothetical: 2026-07-02 a `#5923` quotaCache change leaked into the unrelated `#2296` worktree via a global `stash pop`, and the same class reincided through a **subagent**. To compare working changes against a base ref **without** stashing, use `git show <ref>:<path>` or `git diff <ref> -- <path>`; to confirm a typecheck/lint error is pre-existing on the base, inspect the base ref directly (`git show origin/release/vX.Y.Z:<path>`) — never stash your tree away to "get it clean". **Put this ban verbatim in the prompt of every subagent that touches git** (agents don't inherit this file's context — the recurrence was a subagent).
- **(b) Never merge, push, rebase, or force-push a PR / branch / worktree that another session is actively working.** An open PR whose head is a live fix worktree in `.claude/worktrees/` you did **not** create (e.g. `fix-5852`/`fix-5923` carrying fresh commits, even when they share your `diegosouzapw` identity), or any branch another session owns, is **off-limits — HOLD**, and let the owning session merge it. **Before** merging or pushing to any PR you did not create *this* session, run `git worktree list` to check for a matching in-flight worktree and re-check `gh pr view <N> --json state,headRefOid`. Only the owning session merges its own in-flight PR; mid-flight merges race the owner and re-trigger the exact commit/CHANGELOG races Rule #19 and Rule #21 guard against. (Reinforces Rule #19.)
- **(b) Never merge, push, rebase, or force-push a PR / branch / worktree that another session is actively working.** An open PR whose head is a live fix worktree in `.claude/worktrees/` you did **not** create (e.g. `fix-5852`/`fix-5923` carrying fresh commits, even when they share your `diegosouzapw` identity), or any branch another session owns, is **off-limits — HOLD**, and let the owning session merge it. **Before** merging or pushing to any PR you did not create _this_ session, run `git worktree list` to check for a matching in-flight worktree and re-check `gh pr view <N> --json state,headRefOid`. Only the owning session merges its own in-flight PR; mid-flight merges race the owner and re-trigger the exact commit/CHANGELOG races Rule #19 and Rule #21 guard against. (Reinforces Rule #19.)

---

Expand Down
26 changes: 17 additions & 9 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ RUN --mount=type=cache,id=npm-cache,target=/root/.npm \
# See docs/ops/QUALITY_GATE_PLAYBOOK.md Parte 6.
ENV OMNIROUTE_USE_TURBOPACK=1

# Docker containers cannot run the MITM/Agent-Bridge stack (no host DNS/cert
# access), so keep @/mitm/manager on the graceful stub (#3390). This flag is
# Docker-only: npm/Electron/VPS builds must bundle the REAL manager (#6344).
ENV OMNIROUTE_MITM_STUB=1

# Raise the V8 heap ceiling for the build. The webpack production optimization
# pass needs more than V8's default ceiling (~2 GB) for a codebase this size; a
# memory-constrained Docker build otherwise dies with "FATAL ERROR: ... JavaScript
Expand Down Expand Up @@ -103,7 +108,7 @@ ENV NODE_OPTIONS="--max-old-space-size=${OMNIROUTE_MEMORY_MB}"

# Data directory inside Docker — must match the volume mount in docker-compose.yml
ENV DATA_DIR=/app/data
RUN mkdir -p /app/data
RUN mkdir -p /app/data && chown -R node:node /app

# `npm run build` (build-next-isolated → assembleStandalone) bundles ALL runtime
# files into .build/next/standalone/ — .next, node_modules, migrations, scripts,
Expand All @@ -113,23 +118,26 @@ RUN mkdir -p /app/data
# The old per-module overrides were therefore pure duplication and were removed
# (build-output-isolation cleanup). See scripts/build/assembleStandalone.mjs
# (EXTRA_MODULE_ENTRIES) for the single source of truth.
COPY --from=builder /app/.build/next/standalone ./
COPY --chown=node:node --from=builder /app/.build/next/standalone ./
# better-sqlite3 is the one exception still copied explicitly: assembleStandalone
# only syncs its native build/ dir; the JS wrapper (lib/, package.json) is left to
# Next.js tracing. bootstrap-env requires SQLite BEFORE the standalone server
# starts, so guarantee the complete package independent of trace behaviour.
COPY --from=builder /app/node_modules/better-sqlite3 ./node_modules/better-sqlite3
COPY --chown=node:node --from=builder /app/node_modules/better-sqlite3 ./node_modules/better-sqlite3
# migrations land at <standalone>/migrations via assembleStandalone; point the runtime at them.
ENV OMNIROUTE_MIGRATIONS_DIR=/app/migrations

# Docker healthcheck script — not traced by Next.js standalone output, so copy
# it explicitly. The HEALTHCHECK CMD references it as `node healthcheck.mjs`.
COPY --from=builder /app/scripts/dev/healthcheck.mjs ./healthcheck.mjs

# Hand /app over to the baked-in `node` non-root user (UID/GID 1000) so the
# runtime process never holds root privileges. The chown happens after all
# COPYs so it covers files originally owned by root in the builder stage.
RUN chown -R node:node /app
COPY --chown=node:node --from=builder /app/scripts/dev/healthcheck.mjs ./healthcheck.mjs

# Install Python + pipx for Headroom CLI (required for headroom proxy management)
# Install as root, then ensure pipx binaries are accessible to node user
ENV PIPX_BIN_DIR=/usr/local/bin
ENV PIPX_HOME=/opt/pipx
RUN apt-get update && apt-get install -y --no-install-recommends python3 python3-pip python3-venv pipx \
&& pipx install --global headroom-ai \
&& rm -rf /var/lib/apt/lists/*

EXPOSE 20128

Expand Down
35 changes: 28 additions & 7 deletions bin/cli/commands/launch-codex.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ const STRIPPED_CODEX_ENV_KEYS = [
/** Placeholder so codex's `env_key` is always satisfied when the backend is open. */
const NO_AUTH_SENTINEL = "omniroute-no-auth";

// On Windows the `codex` binary is an npm `.cmd` shim that `spawn` cannot resolve
// without a shell (bare "codex" → ENOENT). Mirror the qodercli Windows fix (#6263):
// spawn `codex.cmd` through a shell on win32, and the bare binary elsewhere.
export function resolveCodexSpawn(platform) {
if (platform === "win32") {
return { command: "codex.cmd", shell: true };
}
return { command: "codex", shell: undefined };
}

function stripTrailingSlash(value) {
let s = String(value);
let end = s.length;
Expand Down Expand Up @@ -126,10 +136,10 @@ export async function runLaunchCodexCommand(opts = {}, codexArgs = []) {

if (!(await healthCheck(baseUrl))) {
console.error(
(t("launch.notRunning") || "OmniRoute is not reachable at {port}. Start it with 'omniroute serve'.").replace(
"{port}",
baseUrl
)
(
t("launch.notRunning") ||
"OmniRoute is not reachable at {port}. Start it with 'omniroute serve'."
).replace("{port}", baseUrl)
);
return 1;
}
Expand All @@ -142,7 +152,12 @@ export async function runLaunchCodexCommand(opts = {}, codexArgs = []) {
const env = buildCodexEnv(process.env, authToken);

return await new Promise((resolve) => {
const child = spawn("codex", extraArgs, { env, stdio: "inherit" });
const { command: codexLaunch, shell: shellValue } = resolveCodexSpawn(process.platform);
const child = spawn(codexLaunch, extraArgs, {
env,
stdio: "inherit",
shell: shellValue,
});
child.on("error", (err) => {
if (err?.code === "ENOENT") {
console.error(
Expand All @@ -165,10 +180,16 @@ export function registerLaunchCodex(program) {
t("launchCodex.description") || "Launch Codex CLI pointed at OmniRoute (local or remote VPS)"
)
.option("--port <port>", "Local OmniRoute port (ignored when --remote is set)", "20128")
.option("--remote <url>", "Remote OmniRoute base URL, e.g. http://192.168.0.15:20128 (overrides --port + context)")
.option(
"--remote <url>",
"Remote OmniRoute base URL, e.g. http://192.168.0.15:20128 (overrides --port + context)"
)
.option("--profile <name>", "Codex profile to activate (passed as --profile <name>)")
.option("-p, --p <name>", "Alias for --profile")
.option("--api-key <key>", "OmniRoute API key (overrides OMNIROUTE_API_KEY env var for this invocation)")
.option(
"--api-key <key>",
"OmniRoute API key (overrides OMNIROUTE_API_KEY env var for this invocation)"
)
.allowUnknownOption(true)
.allowExcessArguments(true)
.argument("[codexArgs...]", "arguments passed through to the codex binary")
Expand Down
10 changes: 10 additions & 0 deletions bin/omniroute.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Special bypasses (handled before Commander):
* --mcp Start MCP server over stdio
* reset-encrypted-columns Recovery tool for broken encrypted credentials
* reset-password Reset the admin/management password
*
* All other commands are routed through Commander (bin/cli/program.mjs).
*/
Expand Down Expand Up @@ -210,6 +211,15 @@ if (process.argv.includes("reset-encrypted-columns")) {
process.exit(exitCode ?? 0);
}

if (process.argv.includes("reset-password")) {
// bin/reset-password.mjs self-executes its `main()` on import and calls
// process.exit() on completion/error. Await a never-resolving promise so
// control never falls through to Commander (which would then reject
// `reset-password` as an unknown command). See #6261.
await import(pathToFileURL(join(ROOT, "bin", "reset-password.mjs")).href);
await new Promise(() => {});
}

try {
const { createProgram } = await import(
pathToFileURL(join(ROOT, "bin", "cli", "program.mjs")).href
Expand Down
104 changes: 79 additions & 25 deletions bin/reset-password.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
*
* Usage:
* node bin/reset-password.mjs
* npx omniroute reset-password
* omniroute reset-password
*
* Non-interactive / scripted usage (piped stdin, e.g. CI or Docker):
* printf 'NewPass123\nNewPass123\n' | omniroute reset-password
* printf 'NewPass123' | omniroute reset-password --password-stdin
*
* Resets the admin password for OmniRoute.
* Prompts for a new password and updates the database directly.
* Prompts for a new password (interactive TTY) or reads it from stdin
* (non-TTY) and updates the database directly.
*
* @module bin/reset-password
*/
Expand All @@ -21,19 +26,61 @@ import { readManagementPasswordState, resetManagementPassword } from "./cli/sqli
const DATA_DIR = resolveDataDir();
const DB_PATH = resolveStoragePath(DATA_DIR);

const rl = createInterface({
input: process.stdin,
output: process.stdout,
});

function ask(question) {
return new Promise((resolve) => rl.question(question, resolve));
const MIN_PASSWORD_LENGTH = 8;

/** Read the entire stdin stream as a UTF-8 string (used for non-TTY input). */
function readAllStdin() {
return new Promise((resolve) => {
let data = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => {
data += chunk;
});
process.stdin.on("end", () => resolve(data));
process.stdin.on("error", () => resolve(data));
// Resuming is implied by attaching a 'data' listener, but be explicit so a
// paused stream (some spawn setups) still flows to EOF.
process.stdin.resume();
});
}

function exitWithError(message) {
console.error(message);
rl.close();
process.exit(1);
/**
* Obtain the new password (and its confirmation).
*
* - `--password-stdin`: the ENTIRE stdin is the password, no confirmation.
* - non-TTY stdin (piped): read all of stdin once; first line is the password,
* second line — when present — is the confirmation, else the first line is
* reused (a single-line pipe means "no separate confirmation").
* - interactive TTY: two sequential prompts (unchanged behavior).
*
* The non-TTY path exists because two sequential `rl.question` promises never
* settle under a piped EOF — the second read blocks forever, so the reset was
* silently never applied (#6258).
*/
async function collectPassword() {
if (process.argv.includes("--password-stdin")) {
const raw = await readAllStdin();
const password = raw.replace(/[\r\n]+$/, "");
return { password, confirm: password };
}

if (!process.stdin.isTTY) {
const raw = await readAllStdin();
const lines = raw.split(/\r?\n/);
const password = lines[0] ?? "";
const confirm = lines[1] ? lines[1] : password;
return { password, confirm };
}

const rl = createInterface({ input: process.stdin, output: process.stdout });
try {
const ask = (question) => new Promise((resolve) => rl.question(question, resolve));
const password = await ask("Enter new password (min 8 chars): ");
const confirm = await ask("Confirm new password: ");
return { password, confirm };
} finally {
rl.close();
}
}

console.log("\n🔑 OmniRoute — Password Reset\n");
Expand All @@ -54,27 +101,34 @@ async function main() {
console.log("ℹ️ No password is currently set.");
}

const password = await ask("Enter new password (min 8 chars): ");
const { password, confirm } = await collectPassword();

if (!password || password.length < 8) {
exitWithError("\n❌ Password must be at least 8 characters.\n");
if (!password || password.length < MIN_PASSWORD_LENGTH) {
console.error(`\n❌ Password must be at least ${MIN_PASSWORD_LENGTH} characters.\n`);
process.exit(1);
}

const confirm = await ask("Confirm new password: ");

if (password !== confirm) {
exitWithError("\n❌ Passwords do not match.\n");
console.error("\n❌ Passwords do not match.\n");
process.exit(1);
}

await resetManagementPassword(password, DB_PATH);
rl.close();

console.log("\n✅ Password reset successfully!");
console.log(" Restart OmniRoute for changes to take effect.\n");
}

main().catch((err) => {
console.error(`\n❌ Error: ${err.message}\n`);
rl.close();
process.exit(1);
});
main()
.then(() => {
// Explicit exit(0) so a caller that imports this module (bin/omniroute.mjs
// routes `omniroute reset-password` here) terminates cleanly instead of
// hanging / exiting with code 13 on an unsettled wrapper await. On POSIX,
// console.log to a pipe is synchronous, so the success line is already
// flushed by the time we exit.
process.exit(0);
})
.catch((err) => {
console.error(`\n❌ Error: ${err.message}\n`);
process.exit(1);
});
Loading