github diegosouzapw/OmniRoute v3.8.40

6 hours ago

[3.8.40] — TBD

In development — bullets added per PR; finalized at release.

✨ New Features

  • feat(compression): relevance extractive engine — a new opt-in compression engine that scores each sentence by term-overlap (Jaccard) with the user's last query minus a length/boilerplate penalty, greedily keeps the most relevant within a budget, and reconstructs the original order. Pure-string, deterministic, ReDoS-safe (char-code tokenization, no RegExp over user input), fail-open, default off. Ideal for trimming long pasted RAG context / tool output to what's relevant. Sentences carrying real signal (digits/URLs/errors/code/paths) are never dropped; overlapThreshold/budgetPercent/boilerplateWeight are configurable. Tier-2 item of the compression feature-extraction roadmap (#7). (#5289)
  • feat(compression): hard-budget mode — compress to ≤ N tokens — a deterministic post-pass (targetTokens / targetRatio, default unset → no-op) that trims a body to a token budget. It ranks sentences/lines by average scoreToken ascending and drops the lowest-saliency ones until the body fits (measured by the exact cl100k countTextTokens), preserving original order. Lines carrying real signal (digits, URLs, Error:-family, code fences, stack at-frames, multi-segment paths, key=value) are never dropped; the budget is distributed proportionally across messages so the total stays ≤ target; an unreachable target (all-preserved) surfaces a validationWarnings note instead of failing silently. Does NOT touch the estimateCompressionTokens budget-gate estimator. Tier-3 item of the compression feature-extraction roadmap (#17). (#5288, follow-up #5291)
  • feat(compression): result memoization for deterministic engines (opt-in) — caches (input, config) → result for provably pure, stateless modes (lite/standard/rtk and stacked pipelines of {lite,caveman,rtk}) to skip recompute on the hot path. Opt-in via memoizeCompressionResults (default off → zero behavior change). Conservative opt-in whitelist (stateful ccr/session-dedup — which write the cross-request CCR store — and model-backed ultra/aggressive/llmlingua are never cached), principal-scoped (skipped without a principal, so no cross-principal body leak), and clone-on-store + clone-on-read. Tier-3 item of the compression feature-extraction roadmap (#21). (#5286)
  • feat(compression): inline transparency annotation — surfaces tokens=847→312; rules: filler×8, dedup×2 derived from existing compression stats. The X-OmniRoute-Compression response header is extended append-only (the mode; source=X prefix stays byte-identical, so existing header parsers don't break) and the compression studio cockpit shows a matching badge. Zero new computation — it aggregates the rulesApplied/techniquesUsed already on the stats. Tier-3 item of the compression feature-extraction roadmap (#18). (#5284)
  • feat(compression): saliency heatmap in the compression studio — the preview studio can now color each token by saliency: ultra per-token scoreToken (0–1, green→red gradient) or universal kept/removed from the existing diff. A dry-run visualization behind a toggle (no cost on a normal preview; backward-compatible when off). Completes the visualization half of roadmap item #13 (the A/B comparison shipped in #5080). (#5285)
  • feat(compression): composite-command splitter for RTK detectioncd /x && git status now detects as git-status (previously the whole string was treated as one command and matched no filter). A quote-aware top-level tokenizer splits on &&/||/; (never inside quotes or $(…)/backtick subshells) and feeds the last segment to RTK command detection, so every RTK filter/renderer fires on commands wrapped in cd … &&/||/; chains. O(n), no RegExp over the command (ReDoS-safe). Tier-3 item of the compression feature-extraction roadmap (#16). (#5283)
  • feat(mcp): omniroute_tool_search tool + one-line TS signatures — new MCP tool that does lexical keyword search over every MCP tool's name/description and returns the top matches as compact one-line TypeScript signatures (~half the JSON-schema token cost), so agents discover tools on demand instead of carrying all ~88 schemas every turn. Search is ReDoS-safe (substring scoring, never new RegExp on the query) and deterministic; tools/list stays complete (no hidden tools). Adds the read:tools scope. Tier-1 item of the compression feature-extraction roadmap. (#5269)
  • feat(compression): RTK semantic command-output renderers (opt-in) — adds a second, opt-in compaction layer to the RTK engine that rewrites structured command output into a far more compact semantic form: git diff → file headers + @@ hunks + changed lines only; an all-green pytest/jest/vitest/eslint run → its one-line summary; terraform/tofu planPlan: +N ~M -K plus the resource list; kubectl/aws JSON arrays → a minimal table. Each renderer is conservative (no-op when the shape doesn't match) and the integration is fail-open; the test-green renderer never collapses output that carries any failure signal. Gated by RtkConfig.enableRenderers (default off → zero behavioral change). Eighth item of the compression feature-extraction roadmap. (#5268)
  • feat(compression): QuantumLock cache-prefix stabilization (opt-in, default off) — recovers upstream prompt-cache hits that a volatile fragment in the system prompt would otherwise bust. When a caller injects a session UUID, unix timestamp, request-id, JWT, API-key shape, or long hex digest into the role:system message every turn, the longest common prefix across turns ends at that changing byte → the whole system prompt after it is re-billed and re-processed each turn. QuantumLock replaces each non-semantic volatile fragment with a positional, value-independent placeholder ⟦Q{i}⟧ and appends the real values in a delimited ⟦QUANTUMLOCK⟧ tail. The rewrite is sent to the model (lossless — not restored), so the system-prompt body becomes byte-identical across turns and the provider caches the long stable prefix while only the small tail differs. Opt-in, default off, applied only for caching providers (isCachingProvider && config.quantumLock.enabled); bounded ReDoS-safe patterns; idempotent; no date/time patterns (semantically meaningful — explicit non-goal). Studio gets a toggle + a "🔒 N volatile fragment(s) stabilized" dry-run badge. Seventh item of the compression feature-extraction roadmap (bench: #5080, gate: #5127, fuzzy: #5143, ionizer: #5148, TOON: #5163, CCR ranged: #5187, risk-gate: #5243). (#5260)
  • kilocode: anonymous (no-auth) access to Kilo Code's free models, mirroring the opencode/mimocode pattern. With no Kilo account connected, requests now fall back to the gateway's anonymous tier (Authorization: Bearer anonymous on api.kilo.ai/api/openrouter) so the free models work without signup; a connected OAuth account is still used unchanged for the paid tier (#5259, #4019 — thanks @Theadd for the reference implementation)
  • feat(logging): call-log correlation ID (end-to-end) — every request now gets a unique correlation id, returned in the X-Correlation-Id response header, persisted in call_logs (migration 109), filterable via /api/usage/call-logs, and surfaced in the dashboard request logger (per-chunk stream timestamps + active-requests-first sort). This is the safe, cohesive core subset of the larger #5275 — landed on its own so the low-risk value isn't blocked by the parts of that PR still under review. (#5279 — thanks @hartmark)
  • feat(providers): Microsoft 365 Copilot individual provider — adds the copilot-m365-web provider (the 237th), wiring the M365 BizChat framing/connection helpers into a selectable web-session provider backed by m365.cloud.microsoft/chat for individual Microsoft 365 plans. Builds on the M365 pure-framing groundwork from #4696. Regression guard: tests/unit/copilot-m365-web-executor.test.ts. (#5302 — thanks @skyzea1)

🔧 Bug Fixes

  • ci(docker): re-point the Docker Hub / GHCR :latest (and :latest-web) tags to the just-published release. On a release: released event the freshly-created git tag is often not yet visible to git fetch --tags when docker-publish runs, so the :latest-promotion gate built its candidate set purely from git tag -l and resolved the highest semver to the previous version — leaving latest one release behind (3.8.39 published, latest still 3.8.38). The decision now lives in scripts/ci/should-promote-latest.sh, which folds the current VERSION into the candidate set before picking the highest stable semver, making promotion independent of tag-sync timing (a patch published after a higher minor still won't grab latest). Regression guard: tests/unit/build/should-promote-latest-5301.test.ts (#5301)
  • command-code: treat a non-positive max_tokens/max_completion_tokens (e.g. Zoo Code's -1 "let the server choose") as "no limit" — omit the field instead of forcing it to 1. clampMaxTokens previously did Math.max(1, …), so a client -1 was sent upstream as max_tokens: 1, truncating the response to a single token (the observed completion_tokens: 1, content: null, reasoning_content: "The" with finish_reason: stop). Now any value ≤ 0 is dropped so Command Code applies the model's native default; positive values are still floored and clamped to the 200k ceiling. Regression guard: tests/unit/command-code-maxtokens-negative-5166.test.ts (#5166 — thanks @Stazyu)
  • fix(auth): compare-and-swap guard on the OAuth refresh persist — under multi-agent load, the per-connection refresh mutex makes [network refresh + DB write] atomic for one connection, but it does not protect against a third writer (a sibling request, a concurrent HealthCheck, or a replica) landing a fresher refresh_token rotation on the same connection_id between the staleness read and the persist. Overwriting that fresher row reverts the sibling's rotation; the next caller then loads the now-consumed token, Auth0/Anthropic flag it as refresh_token_reused, and the whole token family gets revoked (the 1352× claude/aa5dd5cf invalidation storm). getAccessToken now re-reads the row's current refresh_token immediately before persisting (inside the mutex) and skips the write when it has rotated past the token the caller presented — the caller still receives the freshly-issued access token, only the DB overwrite is skipped. Opt-in via runWithCasGuard (no active guard ⇒ byte-identical behavior); skip/persist counters exposed via getCasGuardStats(). Regression guard: tests/unit/token-refresh-cas-guard-4038.test.ts. (#4038 — thanks @KooshaPari for the root-cause diagnosis)
  • mcp: break the schemas/tools.ts ↔ schemas/toolSearch.ts import cycle introduced when the tool_search defs (#5269) were extracted into their own module — toolSearch.ts imported McpToolDefinition from tools.ts while tools.ts imported toolSearchTool from toolSearch.ts, failing check:cycles on release/v3.8.40. The shared AuditLevel + McpToolDefinition types now live in a leaf schemas/toolDefinition.ts that both import; tools.ts re-exports them for backward compatibility.
  • compression (analytics): record attempted-but-no-op compression runs so Stacked is no longer invisible when it saves nothing. Previously a compression_analytics row was written only on a net-positive saving, so a Stacked (RTK→Caveman) pipeline that ran on already-compact context produced no row — indistinguishable from "never dispatched" (byMode.stacked.count stayed flat while Ultra climbed). Such runs are now recorded with skip_reason and surfaced as a per-mode skipped count plus totalSkipped/bySkipReason in the analytics summary and the Mode Breakdown; the existing net-saving totals/averages are unchanged (skip rows are excluded from them) (#4268 — thanks @abdulkadirozyurt, @androw)
  • cli (tray): fix omniroute server --tray showing no tray on macOS/Linux with no error printed. The wired Unix tray path loaded systray2 through an inline loader that called require("module") inside an ESM .mjs file ("type":"module") → ReferenceError: require is not defined, silently swallowed (regressed in v3.8.34); even if it had loaded, systray2 isn't in node_modules (it's lazily installed into ~/.omniroute/runtime). The loader now delegates to the runtime loader, the icon path (icon.png) is corrected, isTemplateIcon is false (the full-color icon rendered as a white square under macOS template mode), and tray start failures are surfaced to stderr instead of being swallowed (#4605 — thanks @ProgMEM-CC)
  • agent-bridge (antigravity): unwrap the cloudcode-pa .request envelope when converting Antigravity IDE requests. The real IDE sends cloudcode-pa.googleapis.com/v1internal:generateContent with the Gemini request nested under .request ({ project, model, request: { contents, systemInstruction, generationConfig } }), but the bridge read those fields at the top level — yielding an empty conversation, so prompts hung mid-execution. The legacy /v1beta/models/<model>:generateContent top-level shape still works (#4294 — thanks @shabeer)
  • dashboard: add a GitHub releases fallback to the "Update Available" lookup. After the v3.8.28 fix added an npm-registry HTTP fallback, the banner could still stay hidden on networks that reach GitHub (where the news feed already loads) but not registry.npmjs.org. resolveLatestVersion() now tries npm CLI → npm registry → GitHub releases (/repos/diegosouzapw/OmniRoute/releases/latest) before giving up, and logs a warning only when all three fail (#4100)
  • command-code: omit max_tokens when the client omits it so the upstream applies the model's native default, fixing 400 "expected <=200000" on /alpha/generate for high-cap models; an explicit oversized client value is clamped to the 200k endpoint ceiling (#5221 — thanks @adivekar-utexas)
  • combo: wire session stickiness into the round-robin dispatch path. Multi-turn conversations from clients that send no session id (Codex CLI, Claude Code, most OpenAI-compatible tools) were rotated to a different connection on every turn by round-robin combos, busting the upstream prompt-cache → cold high-reasoning starts, intermittent 504s and throughput collapse under concurrency. The weighted/priority paths already honored per-conversation stickiness; the round-robin handler returned before reaching it. Round-robin now starts the rotation at the conversation's sticky connection (failover to the other targets is preserved), and different conversations still spread across connections — only intra-conversation rotation is removed (#5248, #3825 — thanks @bypanghu, @jpsn123, @xz-dev)
  • kiro: replace the synthesized trailing "Continue" turn with a neutral filler ("...") — when an OpenAI→Kiro request ends on an assistant/tool turn, the translator synthesizes the protocol-required trailing user turn, and the literal word "Continue" could be read by Kiro/CodeWhisperer as a real user instruction and trigger unintended agent action. A trailing tool-result turn is still promoted as-is (it already collapses to a real user turn); only the assistant-text-ending case is affected. Regression guards: tests/unit/kiro-continue-filler-5231.test.ts. (#5231)
  • combo: advance to the next combo target on a 400 "requested model is not supported" instead of hard-failing. The 400 guard in the priority strategy treated MODEL_CAPACITY as a block-fallback reason, so a combo that hit a provider lacking a specific model returned a hard 400 even when other targets (different providers) supported it. Such 400s now fall through to the next target. (#5249 — thanks @Chewji9875)
  • dashboard: disabled no-auth providers no longer vanish from the All Providers page. Disabling a no-auth provider (the "No authentication required" toggle, which adds it to blockedProviders) silently removed its card because the page dropped blocked no-auth entries from its render list — the only way back was buried under Settings → Security → Blocked Providers. The page now partitions no-auth entries: visible providers render as before, blocked ones appear in a "Disabled" sub-group with an Enable button that un-blocks them in place. Aggregates, counts and /v1/models still consume the visible-only list (blocked providers stay out of routing). Regression guard: tests/unit/noauth-blocked-partition-5183.test.ts. (#5183, follow-up from #5166 — thanks @WslzGmzs)
  • dashboard: add a parent /dashboard/context page so RSC prefetches of the compression-context hub no longer 404. The route only had sub-routes (settings, combos, ultra, …) and no parent page, so the App Router returned 404 for the bare segment. The parent now redirects to its canonical sub-route (/dashboard/context/settings), honoring a legacy ?tab= query for deep links. Regression guard: tests/unit/dashboard/context-parent-redirect-5298.test.ts (#5298 — thanks @KooshaPari)
  • i18n: add the missing sidebar.gamificationGroup message across all 42 locales — the Gamification sidebar group referenced a titleKey that existed in no locale, logging MISSING_MESSAGE: sidebar.gamificationGroup (en) at runtime (the group still rendered via its titleFallback). The key is now present everywhere so the warning is gone and locale coverage is unaffected (#5298 — thanks @KooshaPari)
  • api(stream): /v1/chat/completions no longer returns SSE for a non-stream OpenAI-compatible request when stream is omitted and the client sends Accept: application/json, text/event-stream — the Vercel AI SDK / OpenAI SDK non-stream signature (doGenerate()/generateText()), which then failed with Invalid JSON response (Unexpected token 'd', "data: {"id"...). The route-level Accept override (#302) and resolveStreamFlag now treat an Accept header that explicitly lists application/json as a JSON opt-in even when it also lists text/event-stream; only a pure Accept: text/event-stream (no application/json) still opts an omitted-stream request into SSE, and an explicit body stream value always wins. The shared decision now lives in acceptHeaderForcesStream. Regression guard: tests/unit/sse-nonstream-accept-5305.test.ts. (#5305 — thanks @md-riaz)
  • providers: drop the retired GPT‑5.2 / GPT‑4.5 models from the direct ChatGPT‑web and Codex surfaces (OpenAI removed them there), so OmniRoute stops advertising/routing models that no longer exist. Scoped on purpose to those two providers — third‑party proxies that still expose the ids are untouched. (#5280 — thanks @backryun)
  • codex: drop the deprecated local_shell hosted tool type before forwarding to OpenAI's Responses API, resolving the omni-combo 400 "The local_shell tool is no longer supported." spike. Inbound Responses local_shell is still accepted and mapped to a caller-side Chat shell function for compatibility. (#5250, #5256 — thanks @KooshaPari)
  • antigravity: retry excluded accounts via the fallback LRU. The combo same-model retry loop accumulates excluded Antigravity connection ids after account-level failures, but auth selection only treated a single excludeConnectionId as a fallback scenario — once exclusions accumulated through excludedConnectionIds, selection could fall back to normal sticky/priority behavior instead of LRU-selecting the next eligible account for the same model/family. Any non-empty accumulated exclude set is now treated as fallback mode. Builds on the family-scoped lockout work in #5180 (v3.8.39). (#5222 — thanks @Ardem2025)
  • grok-cli: strip unsupported sampling params (presencePenalty, frequencyPenalty, logprobs, topLogprobs) before sending to the Grok Build API, fixing 400 'Model does not support parameter presencePenalty' when clients (MiMoCode, Cursor, etc.) send OpenAI-style params. (#5273 — thanks @fulorgnas)
  • grok-cli: accept the full ~/.grok/auth.json object in the dashboard import-token endpoint. The oauthImportTokenSchema only accepted a bare string token while the UI sends the whole auth.json object → 400 Bad Request; the schema now accepts the object and stores the original under providerSpecificData.rawAuthJson for diagnostics and token refresh. (#5258 — thanks @fulorgnas)
  • qoder: coalesce concurrent PAT→job-token exchanges per PAT so high-concurrency / multi-agent bursts no longer stampede openapi.qoder.sh/api/v1/jobToken/exchange before the first exchange populates the completed-token cache; the shared exchange is also decoupled from any single caller's AbortSignal so one aborted waiter can't cancel it for the others. (#5254, #5265 — thanks @KooshaPari)
  • proxy: scope the fallback reachability cache by normalized target URL instead of by hostname, so a failed probe for one endpoint on a shared API host no longer suppresses a later probe for a different endpoint on that same host for the full TTL (host fallback is preserved for malformed URLs). (#5261 — thanks @KooshaPari)
  • proxy: cache failed fast-fail health probes with a short negative TTL instead of the full positive health TTL, so a single transient timeout/load blip no longer marks a working residential SOCKS5 proxy unreachable for the whole window (#5109 regression coverage added). (#5255 — thanks @KooshaPari)
  • mcp: forward HTTP auth to internal tool fetches so MCP tools that call back into the local API surface carry the caller's authorization. (#5218 — thanks @KooshaPari)
  • logging: preserve the outbound provider request headers in the detailed call-log Provider Request payload (previously the upstream response headers were shown there). chatCore now keeps executor-returned request headers when wrapping streaming and non-streaming responses; response headers stay scoped to the Response. (#5257 — thanks @rdself)
  • sse: scope textual <think>/<thinking> tag extraction so generic OpenAI-compatible paths don't rewrite prompt-format content into reasoning_content; an explicit opt-in keeps tag-native families (DeepSeek-R1, QwQ) working while Antigravity/Agy stay excluded by provider/model prefix. (#5224 — thanks @rdself)
  • mcp: break the schemas/tools.ts ↔ schemas/toolSearch.ts import cycle introduced when the tool_search defs (#5269) were extracted into their own module — toolSearch.ts imported McpToolDefinition from tools.ts while tools.ts imported toolSearchTool from toolSearch.ts, failing check:cycles on release/v3.8.40. The shared AuditLevel + McpToolDefinition types now live in a leaf schemas/toolDefinition.ts that both import; tools.ts re-exports them for backward compatibility. (#5282)
  • compression (analytics): record attempted-but-no-op compression runs so Stacked is no longer invisible when it saves nothing. A compression_analytics row was previously written only on a net-positive saving, so a Stacked pipeline that ran on already-compact context produced no row — indistinguishable from "never dispatched". Such runs are now recorded with skip_reason and surfaced as a per-mode skipped count plus totalSkipped/bySkipReason; net-saving totals/averages are unchanged (skip rows excluded). (#5277, #4268 — thanks @abdulkadirozyurt, @androw)
  • cli (tray): fix omniroute server --tray showing no tray on macOS/Linux with no error printed. The Unix tray path loaded systray2 through an inline loader that called require("module") inside an ESM .mjs file → ReferenceError: require is not defined, silently swallowed (regressed in v3.8.34); even if loaded, systray2 is lazily installed into ~/.omniroute/runtime, not node_modules. The loader now delegates to the runtime loader, the icon path is corrected, isTemplateIcon is false (the full-color icon rendered as a white square under macOS template mode), and tray start failures surface to stderr. (#5276, #4605 — thanks @ProgMEM-CC)
  • agent-bridge (antigravity): unwrap the cloudcode-pa .request envelope when converting Antigravity IDE requests. The real IDE sends cloudcode-pa.googleapis.com/v1internal:generateContent with the Gemini request nested under .request, but the bridge read those fields at the top level — yielding an empty conversation, so prompts hung mid-execution. The legacy /v1beta/models/<model>:generateContent top-level shape still works. (#5267, #4294 — thanks @shabeer)
  • dashboard: add a GitHub releases fallback to the "Update Available" lookup. After the v3.8.28 npm-registry fallback, the banner could still stay hidden on networks that reach GitHub but not registry.npmjs.org. resolveLatestVersion() now tries npm CLI → npm registry → GitHub releases before giving up. (#5266, #4100)
  • command-code: omit max_tokens when the client omits it so the upstream applies the model's native default, fixing 400 "expected <=200000" on /alpha/generate for high-cap models; an explicit oversized client value is clamped to the 200k endpoint ceiling. (#5221 — thanks @adivekar-utexas)

🔒 Security

  • authz: require auth for the /v1beta/* Gemini-compatible client API. next.config.mjs rewrote /v1beta/:path*/api/v1beta/:path*, but src/proxy.ts didn't match /v1beta before the rewrite and classifyRoute() didn't classify /api/v1beta/* as client API — so unauthenticated /v1beta/models/...:generateContent traffic could reach the model-serving route without the central client-API auth policy. Both alias and rewritten forms are now classified CLIENT_API, enforcing Bearer auth when REQUIRE_API_KEY is enabled. (#5274 — thanks @rdself)
  • sentinel: security hardening pass across request handling. (#5241 — thanks @iamedwardngo)
  • providers: refresh impersonation User-Agents + TLS fingerprint profiles to current real-client versions; several had drifted or were inconsistent across files, a bot-detection/blocking risk. (#5237 — thanks @backryun)
  • authz (public origin): centralize browser-mutation origin validation into src/server/origin/publicOrigin.ts and wire it through the authz pipeline, replacing the per-route same-origin-only check that 403'd dashboard mutations when served behind a reverse proxy on a different public origin. The module resolves the allowed public origin from configured base-URL env vars or trusted forwarded headers (only when OMNIROUTE_TRUST_PROXY is set and the peer is loopback/LAN via peer-stamp), validates Sec-Fetch-Site metadata, and sanitizes Host/Forwarded inputs (rejects control chars, userinfo, path/query in Host). Regression guards: tests/unit/authz/public-origin.test.ts + tests/unit/authz/pipeline.test.ts. (#5278 — thanks @Thinkscape / @abodera)

📝 Maintenance

  • docs: reorganize docs/, run an accuracy audit, and drop Node 20 from the supported matrix (rebased onto the current release tip). (#5262)
  • providers: remove the discontinued Gemini CLI channel — Google shut it down on 2026-06-18; the supported migration path is Antigravity. (#5246 — thanks @rdself)
  • dashboard: add more provider icons from Lobehub. (#5220 — thanks @backryun)
  • deps: resolve npm install warnings, fix a runtime Cannot find module crash on omniroute serve (the runtime-env module was missing from the npm files allow-list), and clear the 4 moderate npm audit advisories. (#5252 — thanks @yunaamelia), (#5230, #5227)
  • docker: harden the base image against container-scan CVEs and skip comment lines in the #4076 builder heap-ordering check. (#5229, #5233)
  • ci: Trivy advisory scan now ignores unfixed CVEs to cut Security-tab noise. (#5235)
  • test(combo): reconcile the #4279 stop-guard test with the #5249 advance policy. (#5300)
  • test(targets): add 14 unit tests for the shared combo/targetExhaustion.ts handler (provider-exhausted / connection-error / transient rate-limited classification), registered in the vitest discovery list. (#5296 — thanks @KooshaPari)

What's Changed

Full Changelog: v3.8.39...v3.8.40

Don't miss a new OmniRoute release

NewReleases is sending notifications on new releases.