Skip to content

feat: v0.2.0 — advanced routing services, cost analytics, pricing overhaul - #20

Merged
diegosouzapw merged 1 commit into
mainfrom
feature/v0.2.0-release
Feb 14, 2026
Merged

diegosouzapw merged 1 commit into
mainfrom
feature/v0.2.0-release

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Release v0.2.0

Highlights

  • 8 new open-sse services: account selector, context manager, IP filter, session manager, signature cache, system prompt, thinking budget, wildcard router
  • 6 new dashboard settings tabs: IP filter, system prompt, thinking budget, pricing (full-page redesign), rate limit status, sessions
  • Usage cost dashboard: provider cost donut, cost trend line overlay, model cost column, prominent cost stat card
  • Pricing API overhaul: merged model catalog (registry + custom + pricing-only)
  • 9 new unit tests for all new services
  • Version bump: 0.1.0 → 0.2.0
  • CHANGELOG updated with comprehensive release notes

Stats

  • 50 files changed, +4891 / -186 lines

…ricing overhaul

Added:
- 8 new open-sse services (account selector, IP filter, session manager, etc.)
- 6 new dashboard settings tabs (IP filter, system prompt, thinking budget, pricing)
- Usage cost dashboard with provider cost donut, cost trend line, model cost column
- Pricing API merging registry + custom + pricing-only models
- 9 unit tests for all new services

Changed:
- Usage analytics layout redesigned with prominent cost display
- DailyTrendChart upgraded to ComposedChart with dual Y-axes

Fixed:
- Pricing page now shows custom/imported models
- Icon rendering (material-symbols-rounded → outlined)
Copilot AI review requested due to automatic review settings February 14, 2026 16:51
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@github-actions

Copy link
Copy Markdown
Contributor

@codex review

@diegosouzapw
diegosouzapw merged commit b4501a3 into main Feb 14, 2026
3 checks passed
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @diegosouzapw, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This release introduces a suite of advanced routing and management services, significantly enhancing the platform's capabilities for intelligent request handling, cost optimization, and security. The dashboard has been expanded with new configuration options and detailed analytics, providing users with greater control and visibility over their AI interactions and expenditures. These changes collectively aim to improve efficiency, flexibility, and transparency in managing diverse AI models and providers.

Highlights

  • Advanced Routing Services: Introduced 8 new Open-SSE services including Account Selector for intelligent provider account selection, Context Manager for request context tracking, IP Filter for access control, Session Manager for persistent session tracking, Signature Cache for request deduplication, System Prompt for global prompt injection, Thinking Budget for token management, and Wildcard Router for pattern-based model routing.
  • Dashboard Enhancements: Added 6 new dashboard settings tabs for IP Filter, System Prompt, Thinking Budget, a full-page redesign for Pricing, Rate Limit Status, and Sessions. The Usage Cost dashboard now features a provider cost donut chart, a cost trend line overlay, a model cost column, and a prominent cost stat card.
  • Pricing API Overhaul: The pricing API has been significantly updated to merge model catalogs from registry, custom models, and pricing-only models, providing a comprehensive view for cost management.
  • Enhanced Rate Limiting: The rate limiting mechanism has been enhanced with a sliding-window algorithm and per-key quotas, alongside new per-model lockout tracking and improved error classification.
  • Testing and Documentation: Added 9 new unit tests covering all new services and updated architecture and codebase documentation to reflect the new features and API routes.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • CHANGELOG.md
    • Added 8 new Open-SSE services: Account Selector, Context Manager, IP Filter, Session Manager, Signature Cache, System Prompt, Thinking Budget, and Wildcard Router.
    • Enhanced Rate Limit Manager with a sliding-window algorithm and per-key quotas.
    • Added 6 new dashboard settings tabs: IP Filter, System Prompt, Thinking Budget, Pricing (redesigned), Rate Limit Status, and Sessions.
    • Introduced Usage & Cost Analytics features: Cost stat card, Provider Cost Donut, Daily Cost Trend overlay, and Model Table Cost column.
    • Overhauled Pricing API with a merged model catalog endpoint and new API endpoints for rate limits, sessions, and various settings.
    • Added a Cloud worker module for edge deployment.
    • Included unit tests for all new services.
    • Updated documentation including OpenAPI specification, enhanced restart.sh script, and architecture/codebase docs.
    • Fixed icon rendering from material-symbols-rounded to material-symbols-outlined.
    • Corrected pricing page to include custom/imported models.
    • Reorganized Usage analytics layout and upgraded the Daily trend chart to ComposedChart.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a massive suite of advanced features, including sophisticated routing services, cost analytics, and security hardening, along with 8 new services in open-sse and an overhaul of the pricing and routing UI. While the changes are extensive, well-structured, and include comprehensive unit tests, a security audit identified several critical and high-severity vulnerabilities. These primarily relate to missing authentication on new API routes, insecure handling of client IP addresses, and the ability for settings APIs to allow unauthenticated modification of core proxy behavior. The IP filtering logic is also susceptible to spoofing and contains a bug in CIDR matching for broad masks. Remediation of these security issues is essential before deployment, and there is a minor suggestion regarding documentation.

Comment on lines +21 to +46
export async function PUT(request) {
try {
const body = await request.json();

// Configure entire filter
if (body.enabled !== undefined || body.mode || body.blacklist || body.whitelist) {
configureIPFilter(body);
}

// Add/remove individual IPs
if (body.addBlacklist) addToBlacklist(body.addBlacklist);
if (body.removeBlacklist) removeFromBlacklist(body.removeBlacklist);
if (body.addWhitelist) addToWhitelist(body.addWhitelist);
if (body.removeWhitelist) removeFromWhitelist(body.removeWhitelist);

// Temp bans
if (body.tempBan) {
tempBanIP(body.tempBan.ip, body.tempBan.durationMs || 3600000, body.tempBan.reason || "Manual ban");
}
if (body.removeBan) removeTempBan(body.removeBan);

return NextResponse.json(getIPFilterConfig());
} catch (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

The API routes for managing sensitive system settings, such as IP filters, lack any authentication or authorization checks. Although the dashboard includes a 'Require login' setting, it is not enforced in these API handlers. An unauthenticated attacker could modify the proxy's configuration, potentially leading to unauthorized access or denial of service. This issue also applies to other settings routes like system-prompt and thinking-budget.

Comment on lines +227 to +236
function extractClientIP(req) {
return (
req.headers?.["x-forwarded-for"]?.split(",")[0].trim() ||
req.headers?.["x-real-ip"] ||
req.headers?.["cf-connecting-ip"] ||
req.socket?.remoteAddress ||
req.ip ||
"unknown"
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The extractClientIP function trusts client-supplied headers like X-Forwarded-For to determine the client's IP address. If the application is not deployed behind a trusted proxy that sanitizes these headers, an attacker can easily bypass the IP blacklist or spoof a whitelisted IP by providing a fake header. This undermines the security of the IP-based access control system.

const rangeNum = ipToNum(range);
if (ipNum === null || rangeNum === null) return false;
const maskBits = (-1 << (32 - mask)) >>> 0;
return (ipNum & maskBits) === (rangeNum & maskBits);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The matchesCIDR function contains a logic error when handling a mask of 0 (e.g., 0.0.0.0/0). In JavaScript, bitwise shift amounts are masked to 5 bits, so x << 32 is equivalent to x << 0. This results in a maskBits value of 0xFFFFFFFF for a /0 mask, causing it to match only the base IP instead of the entire internet. This could lead to security bypasses if /0 is used in a blacklist or unexpected blocks if used in a whitelist.

Suggested change
return (ipNum & maskBits) === (rangeNum & maskBits);
const maskBits = mask === 0 ? 0 : (-1 << (32 - mask)) >>> 0;

Comment on lines +16 to +28
export function wildcardMatch(model, pattern) {
if (!model || !pattern) return false;
if (pattern === "*") return true;
if (pattern === model) return true;

// Convert glob pattern to regex
const regexStr = pattern
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
.replace(/\*/g, ".*")
.replace(/\?/g, ".");
const regex = new RegExp(`^${regexStr}$`, "i");
return regex.test(model);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The wildcardMatch function constructs a regular expression from a user-supplied pattern by replacing * with .*. If a pattern contains multiple wildcards (e.g., *a*b*c*), it can lead to catastrophic backtracking when matched against certain inputs, causing a Regular Expression Denial of Service (ReDoS). Since these patterns can be configured via the settings API, this poses a risk to the availability of the proxy.

Comment on lines 111 to 115
@@ -16,20 +225,21 @@ export function getQuotaCooldown(backoffLevel = 0) {
* @param {number} status - HTTP status code
* @param {string} errorText - Error message text
* @param {number} backoffLevel - Current backoff level for exponential backoff
* @returns {{ shouldFallback: boolean, cooldownMs: number, newBackoffLevel?: number }}
* @param {string} [model] - Optional model name for model-level lockout
* @returns {{ shouldFallback: boolean, cooldownMs: number, newBackoffLevel?: number, reason?: string }}
*/
export function checkFallbackError(status, errorText, backoffLevel = 0) {
export function checkFallbackError(status, errorText, backoffLevel = 0, model = null) {
// Check error message FIRST - specific patterns take priority over status codes
if (errorText) {
const errorStr = typeof errorText === "string" ? errorText : JSON.stringify(errorText);
const lowerError = errorStr.toLowerCase();

if (lowerError.includes("no credentials")) {
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.notFound };
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.notFound, reason: RateLimitReason.AUTH_ERROR };
}

if (lowerError.includes("request not allowed")) {
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.requestNotAllowed };
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.requestNotAllowed, reason: RateLimitReason.RATE_LIMIT_EXCEEDED };
}

// Rate limit keywords - exponential backoff
@@ -41,24 +251,26 @@ export function checkFallbackError(status, errorText, backoffLevel = 0) {
lowerError.includes("overloaded")
) {
const newLevel = Math.min(backoffLevel + 1, BACKOFF_CONFIG.maxLevel);
const reason = classifyErrorText(errorStr);
return {
shouldFallback: true,
cooldownMs: getQuotaCooldown(backoffLevel),
newBackoffLevel: newLevel,
reason,
};
}
}

if (status === HTTP_STATUS.UNAUTHORIZED) {
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.unauthorized };
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.unauthorized, reason: RateLimitReason.AUTH_ERROR };
}

if (status === HTTP_STATUS.PAYMENT_REQUIRED || status === HTTP_STATUS.FORBIDDEN) {
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.paymentRequired };
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.paymentRequired, reason: RateLimitReason.QUOTA_EXHAUSTED };
}

if (status === HTTP_STATUS.NOT_FOUND) {
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.notFound };
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.notFound, reason: RateLimitReason.UNKNOWN };
}

// 429 - Rate limit with exponential backoff
@@ -68,10 +280,11 @@ export function checkFallbackError(status, errorText, backoffLevel = 0) {
shouldFallback: true,
cooldownMs: getQuotaCooldown(backoffLevel),
newBackoffLevel: newLevel,
reason: RateLimitReason.RATE_LIMIT_EXCEEDED,
};
}

// Transient errors
// Transient / server errors — DON'T increase backoff level
const transientStatuses = [
HTTP_STATUS.NOT_ACCEPTABLE,
HTTP_STATUS.REQUEST_TIMEOUT,
@@ -81,18 +294,20 @@ export function checkFallbackError(status, errorText, backoffLevel = 0) {
HTTP_STATUS.GATEWAY_TIMEOUT,
];
if (transientStatuses.includes(status)) {
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.transient };
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.transient, reason: RateLimitReason.SERVER_ERROR };
}

// 400 Bad Request - don't fallback (same request will fail on all accounts)
if (status === HTTP_STATUS.BAD_REQUEST) {
return { shouldFallback: false, cooldownMs: 0 };
return { shouldFallback: false, cooldownMs: 0, reason: RateLimitReason.UNKNOWN };
}

// All other errors - fallback with transient cooldown
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.transient };
return { shouldFallback: true, cooldownMs: COOLDOWN_MS.transient, reason: RateLimitReason.UNKNOWN };
}

// ─── Account State Management ───────────────────────────────────────────────

/**
* Check if account is currently unavailable (cooldown not expired)
*/
@@ -110,8 +325,6 @@ export function getUnavailableUntil(cooldownMs) {

/**
* Get the earliest rateLimitedUntil from a list of accounts
* @param {Array} accounts - Array of account objects with rateLimitedUntil
* @returns {string|null} Earliest rateLimitedUntil ISO string, or null
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The JSDoc comments for several utility functions in this file (getEarliestRateLimitedUntil, formatRetryAfter, resetAccountState, and applyErrorState) have been removed. While the function logic remains, removing the documentation makes the code harder to understand and maintain, especially for new contributors. Please consider restoring these JSDoc comments, including @param and @returns tags, to clarify the function signatures and their purpose.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This is a major feature release (v0.2.0) that adds advanced routing services, comprehensive cost analytics, and a completely redesigned pricing management system to OmniRoute.

Changes:

  • 8 new open-sse services for intelligent routing, session management, IP filtering, context compression, thinking budget control, system prompt injection, signature caching, and wildcard routing
  • 6 new dashboard settings tabs and usage monitoring components
  • Enhanced usage analytics with provider cost donut chart, cost trend overlay, and per-model cost tracking
  • Overhauled pricing API that merges three sources: registry models, custom models, and pricing-only models

Reviewed changes

Copilot reviewed 47 out of 50 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
package.json Version bump 0.1.0 → 0.2.0
tsconfig.json Added antigravity-manager-analysis to exclude list
next.config.mjs Added empty turbopack config
open-sse/services/wildcardRouter.js New: glob-style model routing with specificity ranking
open-sse/services/thinkingBudget.js New: token budget management for reasoning models (4 modes)
open-sse/services/systemPrompt.js New: global system prompt injection for all requests
open-sse/services/signatureCache.js New: 3-layer dynamic cache for thinking signatures
open-sse/services/sessionManager.js New: session fingerprinting and tracking
open-sse/services/ipFilter.js New: IP allowlist/blocklist with CIDR and wildcard support
open-sse/services/contextManager.js New: 3-layer context compression to prevent prompt length errors
open-sse/services/accountSelector.js New: P2C account selection with health scoring
open-sse/services/accountFallback.js Enhanced rate limiting with per-model lockouts and error classification
open-sse/services/rateLimitManager.js Added response body parsing for retry-after info
open-sse/config/constants.js Added RateLimitReason enum and BACKOFF_STEPS_MS
open-sse/translator/index.js Integrated thinking budget control into translation pipeline
src/sse/services/auth.js Updated markAccountUnavailable signature to include model parameter
src/app/api/settings/thinking-budget/route.js New: thinking budget configuration API
src/app/api/settings/system-prompt/route.js New: system prompt configuration API
src/app/api/settings/ip-filter/route.js New: IP filter configuration API
src/app/api/sessions/route.js New: active sessions API
src/app/api/rate-limits/route.js New: rate limit status API
src/app/api/pricing/models/route.js New: merged model catalog API (registry + custom + pricing)
src/app/(dashboard)/dashboard/settings/page.js Added AI and Pricing tabs
src/app/(dashboard)/dashboard/settings/components/* 4 new settings components (ThinkingBudget, SystemPrompt, IPFilter, Pricing)
src/app/(dashboard)/dashboard/usage/page.js Added Sessions tab and enhanced Limits tab
src/app/(dashboard)/dashboard/usage/components/* 2 new usage components (SessionsTab, RateLimitStatus)
src/shared/components/analytics/charts.js Enhanced with cost tracking: provider donut, cost trend line, model cost column
src/shared/components/UsageAnalytics.js Reorganized layout with cost-focused analytics
tests/unit/*.test.mjs 9 comprehensive unit test files for all new services
docs/* Updated architecture and codebase documentation
CHANGELOG.md Comprehensive v0.2.0 release notes
README.md Updated feature list with new capabilities
.gitignore Added cloud/ directory exclusion

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +8 to +31
export async function GET() {
try {
return NextResponse.json(getSystemPromptConfig());
} catch (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}

export async function PUT(request) {
try {
const body = await request.json();

if (body.prompt !== undefined && typeof body.prompt !== "string") {
return NextResponse.json({ error: "prompt must be a string" }, { status: 400 });
}

setSystemPromptConfig(body);
await updateSettings({ systemPrompt: body });

return NextResponse.json(getSystemPromptConfig());
} catch (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The system prompt injection API lacks authentication. An attacker could inject malicious system prompts that would be prepended to all user requests, potentially causing the AI to behave in harmful ways or leak sensitive information. This is a critical security vulnerability that must be addressed by adding proper authentication.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +38
if (isNaN(budget) || budget < 0 || budget > 131072) {
return NextResponse.json(
{ error: "customBudget must be between 0 and 131072" },
{ status: 400 }
);

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation allows customBudget to be 0 (line 34), but in the setCustomBudget function in thinkingBudget.js (line 110-112), a budget of 0 sets thinking type to "disabled". While this works correctly, it would be clearer if the validation comment or error message explicitly mentioned that 0 means "disabled" rather than just stating the range as "between 0 and 131072".

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +27
const regexStr = pattern
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
.replace(/\*/g, ".*")
.replace(/\?/g, ".");
const regex = new RegExp(`^${regexStr}$`, "i");
return regex.test(model);

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wildcard pattern conversion to regex (lines 22-26) could be vulnerable to ReDoS attacks if an attacker provides malicious patterns with many wildcards. Consider adding input validation to limit the number of wildcards allowed in a pattern (e.g., max 5 wildcards) or pattern length limits. While this is primarily used for admin-configured aliases, defense in depth is recommended.

Copilot uses AI. Check for mistakes.
Comment thread next.config.mjs
@@ -1,5 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The turbopack: {} configuration is added but appears to be an empty object. Turbopack is Next.js's new bundler (still in beta). If this is intentional to enable Turbopack in development, consider adding a comment explaining its purpose. If it's not being used, this line can be removed. Also verify that all features work correctly with Turbopack enabled, as it may have compatibility issues with some packages or features.

Suggested change
const nextConfig = {
const nextConfig = {
// Enable Turbopack (Next.js experimental bundler) in development with default settings.
// Verify compatibility of all features/packages before relying on this in production.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +46
export async function GET() {
try {
return NextResponse.json(getIPFilterConfig());
} catch (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}

export async function PUT(request) {
try {
const body = await request.json();

// Configure entire filter
if (body.enabled !== undefined || body.mode || body.blacklist || body.whitelist) {
configureIPFilter(body);
}

// Add/remove individual IPs
if (body.addBlacklist) addToBlacklist(body.addBlacklist);
if (body.removeBlacklist) removeFromBlacklist(body.removeBlacklist);
if (body.addWhitelist) addToWhitelist(body.addWhitelist);
if (body.removeWhitelist) removeFromWhitelist(body.removeWhitelist);

// Temp bans
if (body.tempBan) {
tempBanIP(body.tempBan.ip, body.tempBan.durationMs || 3600000, body.tempBan.reason || "Manual ban");
}
if (body.removeBan) removeTempBan(body.removeBan);

return NextResponse.json(getIPFilterConfig());
} catch (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IP filter configuration API (/api/settings/ip-filter) has no authentication or authorization checks. This is a critical security endpoint that can lock out all users or allow unauthorized access. This endpoint should require authentication (e.g., admin login) before allowing IP filter configuration changes. The same concern applies to /api/settings/thinking-budget and /api/settings/system-prompt routes.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +62
export async function GET() {
try {
const config = getThinkingBudgetConfig();
return NextResponse.json(config);
} catch (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}

export async function PUT(request) {
try {
const body = await request.json();

// Validate mode
const validModes = Object.values(ThinkingMode);
if (body.mode && !validModes.includes(body.mode)) {
return NextResponse.json(
{ error: `Invalid mode. Must be one of: ${validModes.join(", ")}` },
{ status: 400 }
);
}

// Validate customBudget
if (body.customBudget !== undefined) {
const budget = parseInt(body.customBudget, 10);
if (isNaN(budget) || budget < 0 || budget > 131072) {
return NextResponse.json(
{ error: "customBudget must be between 0 and 131072" },
{ status: 400 }
);
}
body.customBudget = budget;
}

// Validate effortLevel
const validEfforts = ["none", "low", "medium", "high"];
if (body.effortLevel && !validEfforts.includes(body.effortLevel)) {
return NextResponse.json(
{ error: `Invalid effortLevel. Must be one of: ${validEfforts.join(", ")}` },
{ status: 400 }
);
}

// Apply config in-memory
setThinkingBudgetConfig(body);

// Persist to settings DB
await updateSettings({ thinkingBudget: body });

return NextResponse.json(getThinkingBudgetConfig());
} catch (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}

Copilot AI Feb 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the IP filter endpoint, this thinking budget configuration API has no authentication. An unauthorized user could change thinking budgets for all requests, potentially increasing costs significantly or degrading model performance. Add authentication middleware to protect this endpoint.

Copilot uses AI. Check for mistakes.
@diegosouzapw
diegosouzapw deleted the feature/v0.2.0-release branch February 18, 2026 03:03
diegosouzapw added a commit that referenced this pull request Mar 10, 2026
TLS / Certificate Validation (#50)
- src/mitm/server.ts: rejectUnauthorized now defaults to true
  (opt-out via MITM_DISABLE_TLS_VERIFY=1 env var only)

Path Injection (#44, #41-#49)
- src/mitm/server.ts: safeLogPath() guards log filenames inside LOG_DIR
- src/lib/db/backup.ts: path.resolve() anchor + sep/slash guard on backupId
- src/shared/services/backupService.ts: safePath() helper anchors toolId
  and backupId within BACKUP_DIR (prevents path traversal)
- src/app/api/cli-tools/codex-profiles/route.ts: safeProfilePath() helper
  anchors profileId within PROFILES_DIR

Prototype Pollution (#18, #19, #20)
- src/lib/usage/usageHistory.ts: byModel/byAccount initialized with
  Object.create(null); assignments guarded with hasOwnProperty checks

Missing Workflow Permissions (#1-#8, #53)
- .github/workflows/ci.yml: added global permissions: contents: read

Dependabot CVE-2026-0540 (#16)
- dompurify updated to ^3.3.2 (fixes XSS vulnerability)
geekjapan added a commit to geekjapan/OmniRoute that referenced this pull request Jun 17, 2026
docs: require fork-internal PRs by default (Hard Rule cheaper-inference#20)
diegosouzapw added a commit that referenced this pull request Jun 27, 2026
… Rule #20

Rebased onto release/v3.8.39 (stripped v3.8.38 base-drift; the 3 intended
files only). Adds the regression guard tests/unit/pii-opt-in-default.test.ts
(definition defaults === "false" + behavioral pass-through), Hard Rule #20 in
CLAUDE.md, and corrects the AGENTS.md guardrails blurb to state pii-masker is
opt-in/OFF by default.
diegosouzapw added a commit that referenced this pull request Jun 27, 2026
… Rule #20 (#5159)

Integrated into release/v3.8.39. PII opt-in regression guard + Hard Rule #20; rebased to strip base-drift (+81/-1). 5/5 guard tests pass; flip-proof verified.
@diegosouzapw diegosouzapw mentioned this pull request Jun 28, 2026
diegosouzapw added a commit that referenced this pull request Jun 28, 2026
* chore(release): open v3.8.39 development cycle

* docs(changelog): backfill 5 v3.8.38 bullets merged after release finalize

These PRs squash-merged into release/v3.8.38 between the CHANGELOG finalize
(ff57be3) and the merge-to-main (ae6e234), so they shipped in the v3.8.38
tag but had no bullet:

- feat(compression): Ionizer engine (lossy JSON-array sampling + CCR) (#5148)
- fix(sse): preserve non-stream reasoning fields (#5155, @rdself)
- fix(i18n): add missing English UI labels (#5153, @rdself)
- test(combo): gated live smoke (#5151) + release-expectations refresh (#5150, @KooshaPari)

(#5129 exact-host Anthropic baseUrl is already covered by the #5130 bullet — same CodeQL #674.)
Synced 41 i18n CHANGELOG mirrors.

* feat(compression): TOON best-of-N candidate encoder + encoder A/B table (#5163)

Integrated into release/v3.8.39. TOON best-of-N candidate encoder (GCF default, fail-open). 17/17 unit tests pass on merge result; CI reds were base-stale + Quality Ratchet DRIFT.

* fix(zenmux): normalize vendor-prefixed GLM system roles (#5158)

Integrated into release/v3.8.39. ZenMux vendor-prefixed GLM system-role normalization; 12/12 role-normalizer tests pass on merge result. CI reds base-stale.

* [codex] fix xAI OAuth test and reasoning effort (#5157)

Integrated into release/v3.8.39. xAI reasoning-effort normalization (max/xhigh→high) + OAuth test config; 46/46 xai-translator tests pass on merge result. CI reds base-stale.

* docs(i18n): add Traditional Chinese (zh-TW) README and update zh-CN to latest (#5162)

Integrated into release/v3.8.39. Traditional Chinese (zh-TW) README + zh-CN refresh; docs-only.

* test(security): guard PII redaction stays opt-in (default off) + Hard Rule #20 (#5159)

Integrated into release/v3.8.39. PII opt-in regression guard + Hard Rule #20; rebased to strip base-drift (+81/-1). 5/5 guard tests pass; flip-proof verified.

* test(combo): deterministic context-relay universal-handoff coverage (closes phase-2 TODO) (#5168)

Integrated into release/v3.8.39. Deterministic context-relay universal-handoff coverage (3 tests); 3/3 pass on merge result.

* docs(i18n): full sync zh-TW and zh-CN README with canonical English v3.8.39 (#5171)

Integrated into release/v3.8.39. Full zh-TW docs tree + zh-CN sync with canonical English v3.8.39; docs-only.

* fix(serve): honour HOSTNAME from .env instead of hardcoding 0.0.0.0 (#5134) (#5170)

Integrated into release/v3.8.39. HOSTNAME env override in serve (#5134) + regression test (4/4, TDD flip-proof verified).

* fix(sse): resolve nameless deepseek-web tool blocks via parameter-schema match (#5154) (#5173)

Integrated into release/v3.8.39. Schema-based nameless deepseek-web tool-block resolution (#5154); 6/6 tests pass on merge result (incl. ambiguous/no-match negatives + named-tag no-regression).

* fix(sse): normalize array user content for Command Code to avoid upstream 400 (#5166) (#5174)

Integrated into release/v3.8.39. Normalize array user content for Command Code (#5166, user-array/400 symptom); 4/4 tests pass on merge result.

* fix(sse): defer </think> close so it never leaks before tool_calls (#5123) (#5175)

Integrated into release/v3.8.39. Defer </think> close so it never leaks before tool_calls (#5123); 4/4 tests pass (incl. #4633 no-regression). CHANGELOG synced to keep all 3 v3.8.39 fixes.

* fix(dashboard): use amber for home update-step warning icon (#5176)

Integrated into release/v3.8.39. Amber for home update-step warning icon; 1/1 UI test.

* fix(api): LAN/Tailscale dashboard — host-aware CSP + GET-exempt version route + combo field errors (#5083) (#5177)

Integrated into release/v3.8.39. Host-aware CSP (ReDoS/injection-safe host validation) + GET-exempt /api/system/version (POST/spawn stays LOCAL_ONLY, exact-match safe-methods-only) + COMBO_002 firstField. 44/44 tests + route-guard membership gate green. CHANGELOG synced to keep all 4 v3.8.39 fixes.

* fix(api): replace #5083 global middleware CSP with declarative ws: scheme (#5083)

Follow-up to PR #5177 (merged): that version implemented the LAN-CSP fix (Bug 1)
with a new global `src/middleware.ts` + `src/server/csp.ts`, which contradicts the
project's documented architecture — 'No global Next.js middleware — interception is
route-specific' (CLAUDE.md / AGENTS.md) — and was merged unverified (middleware vs
next.config header precedence was never confirmed in a real build).

This replaces that approach with the minimal, declarative equivalent:
  • next.config.mjs: connect-src now permits the bare `ws:` scheme (symmetric with the
    bare `wss:` already allowed) so the dashboard can reach its own Live WS server from
    a LAN/Tailscale host. No middleware.
  • Removes src/middleware.ts, src/server/csp.ts, and tests/unit/csp-host-aware.test.ts.
  • Adds tests/unit/csp-lan-ws-5083.test.ts (incl. a guard asserting src/middleware.ts
    does NOT exist, so the global-middleware approach cannot silently return).

Bugs 2 (GET-exempt /api/system/version) and 3 (COMBO_002 field surfacing) from #5177
are unaffected and remain in place.

Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>

* test(combo): end-to-end quota-share DRR routing-decision coverage (matrix parity) (#5179)

Integrated into release/v3.8.39. Quota-share DRR routing-decision coverage (matrix parity); 2/2 pass on merge result.

* feat(agent-bridge): graceful cert-install fallback with manual guide for containers (#4546) (#5178)

Integrated into release/v3.8.39. Agent-bridge graceful cert-install fallback + manual guide (#4546); 6/6 tests pass on merge result.

* fix(antigravity): family-scoped quota lockout (gemini/claude buckets) (#5180)

Integrated into release/v3.8.39 — family-scoped antigravity quota lockout. Rebased from v3.8.37 + validated (vitest 5/5, typecheck clean, full combo-matrix green, model-lockout 99/0). Same-model cross-account retry (chat.ts) deferred pending live antigravity VPS validation.

* fix(cli): force NODE_ENV to match dev/start run mode in custom Next server (#5189)

Integrated into release/v3.8.39. Force NODE_ENV to match dev/start run mode in custom Next server; 2/2 source-scan+ordering tests pass on merge result.

* feat(compression): CCR ranged/grep/stats retrieval (ReDoS-safe, backward-compat) (#5187)

Integrated into release/v3.8.39. CCR ranged/grep/stats retrieval (safe-regex ReDoS guard + length/match caps); 17/17 tests pass on merge result.

* docs(combo): sync all combo/routing-strategy docs to current state + document test coverage (#5185)

Integrated into release/v3.8.39. Combo/routing-strategy docs sync; docs-only.

* fix(mcp): return 404 (not 400) for unknown Streamable HTTP session id (#5169) (#5191)

* fix(api): respect blocked Auto (Zero-Config) provider in /v1/models catalog (#5192) (#5194)

* test(combo): deterministic context-relay codex quota-handoff coverage (closes last gap) (#5195)

* test(ci): wire antigravity-quota-family under test:vitest (fix test-discovery orphan) (#5196)

* fix(oauth): antigravity login no longer hangs — fire-and-forget onboarding + bounded post-exchange (#5193)

Antigravity OAuth hang fix (no-PKCE/no-openid + bounded post-exchange + exchange-500 fix). Includes #5200 (Koosha) revert + owner rebaseline to keep documented comments. Integrated into release/v3.8.39.

* feat(oauth): remote Antigravity login via local helper + paste-credentials (#5203)

Remote Antigravity login: local helper (omniroute login antigravity) + paste-credentials. Integrated into release/v3.8.39.

* fix(translator): accept Claude Messages shape in non-stream malformed-200 guard (#5156)

Integrated into release/v3.8.39

* fix(cli): default dev bundler to Turbopack (16.2.x panic no longer reproduces) (#5206)

Integrated into release/v3.8.39

* fix(cli): auto-calibrate server V8 heap from physical RAM (#5172) (#5213)

The server was spawned with a fixed --max-old-space-size=512 (omniroute serve)
or no heap flag at all (Electron), so RAM-rich boxes still OOM-crashed under
load (Ineffective mark-compacts near heap limit ~500MB) with many providers/
accounts and large model catalogs. New calibrateHeapFallbackMb(os.totalmem())
defaults the heap to ~35% of RAM clamped [512,4096], wired into serve.mjs and
electron/main.js. Explicit OMNIROUTE_MEMORY_MB still wins (#2939 unchanged).

Also addresses #5160 (same OOM root); #5152 (docker) benefits via the same knob.

Closes #5172

* fix(proxy): coalesce fast-fail health probes (#5208)

Integrated into release/v3.8.39

* fix(proxy): close dispatchers when clearing cache (#5202)

Integrated into release/v3.8.39

* fix(cli): raise dev server Node heap limit to 8GB to prevent OOM (#5198)

Integrated into release/v3.8.39

* fix(auth): allow synthetic no-auth fallback for mimocode (#5205)

Integrated into release/v3.8.39

* fix(oauth): preserve Antigravity refresh_token on empty/omitted upstream response (#3850) (#5214)

Google's OAuth refresh tokens are non-rotating: the refresh response usually
omits refresh_token and occasionally returns it as an empty string. The
Antigravity executor used `typeof tokens.refresh_token === "string" ? ... `
which accepts "" (typeof "" === "string") and overwrote the stored token with
empty, nulling it on first refresh. Now treats non-string OR empty as absent and
preserves credentials.refreshToken, matching refreshGoogleToken semantics.

Closes #3850

* fix(responses): normalize non-array input (#5204)

Integrated into release/v3.8.39

* fix(stream): normalize safety finish reasons via shared helper (#5197)

Integrated into release/v3.8.39

* fix(request-logger): never render negative '(-100%)' compression badge (#5201)

Integrated into release/v3.8.39

* fix(combo): reject empty responses api output (#5207)

Integrated into release/v3.8.39 — combo failover now rejects empty Responses API output (validateQuality). Baseline rebaseline dropped (main-measured drift; maintainer rebaselines at release).

* fix(pwa): prefer cached navigation before offline page (#5209)

Integrated into release/v3.8.39 — PWA service worker prefers cached navigation before offline page (#5165).

* chore(release): v3.8.39 — 2026-06-28

* chore(release): rebaseline openapi+i18n coverage ratchet drift for v3.8.39

---------

Co-authored-by: Arthur Bodera <abodera@gmail.com>
Co-authored-by: Nguyen Minh <lop123thcs@gmail.com>
Co-authored-by: lunkerchen <labanchen@gmail.com>
Co-authored-by: Ankit <177378174+anki1kr@users.noreply.github.com>
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: Ardem2025 <ardemb22@gmail.com>
Co-authored-by: backryun <bakryun0718@proton.me>
Co-authored-by: Anton <39598727+NomenAK@users.noreply.github.com>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
dimaslanjaka pushed a commit to dimaslanjaka/OmniRoute that referenced this pull request Jun 29, 2026
* chore(release): open v3.8.39 development cycle

* docs(changelog): backfill 5 v3.8.38 bullets merged after release finalize

These PRs squash-merged into release/v3.8.38 between the CHANGELOG finalize
(ff57be3) and the merge-to-main (ae6e234), so they shipped in the v3.8.38
tag but had no bullet:

- feat(compression): Ionizer engine (lossy JSON-array sampling + CCR) (cheaper-inference#5148)
- fix(sse): preserve non-stream reasoning fields (cheaper-inference#5155, @rdself)
- fix(i18n): add missing English UI labels (cheaper-inference#5153, @rdself)
- test(combo): gated live smoke (cheaper-inference#5151) + release-expectations refresh (cheaper-inference#5150, @KooshaPari)

(cheaper-inference#5129 exact-host Anthropic baseUrl is already covered by the cheaper-inference#5130 bullet — same CodeQL cheaper-inference#674.)
Synced 41 i18n CHANGELOG mirrors.

* feat(compression): TOON best-of-N candidate encoder + encoder A/B table (cheaper-inference#5163)

Integrated into release/v3.8.39. TOON best-of-N candidate encoder (GCF default, fail-open). 17/17 unit tests pass on merge result; CI reds were base-stale + Quality Ratchet DRIFT.

* fix(zenmux): normalize vendor-prefixed GLM system roles (cheaper-inference#5158)

Integrated into release/v3.8.39. ZenMux vendor-prefixed GLM system-role normalization; 12/12 role-normalizer tests pass on merge result. CI reds base-stale.

* [codex] fix xAI OAuth test and reasoning effort (cheaper-inference#5157)

Integrated into release/v3.8.39. xAI reasoning-effort normalization (max/xhigh→high) + OAuth test config; 46/46 xai-translator tests pass on merge result. CI reds base-stale.

* docs(i18n): add Traditional Chinese (zh-TW) README and update zh-CN to latest (cheaper-inference#5162)

Integrated into release/v3.8.39. Traditional Chinese (zh-TW) README + zh-CN refresh; docs-only.

* test(security): guard PII redaction stays opt-in (default off) + Hard Rule cheaper-inference#20 (cheaper-inference#5159)

Integrated into release/v3.8.39. PII opt-in regression guard + Hard Rule cheaper-inference#20; rebased to strip base-drift (+81/-1). 5/5 guard tests pass; flip-proof verified.

* test(combo): deterministic context-relay universal-handoff coverage (closes phase-2 TODO) (cheaper-inference#5168)

Integrated into release/v3.8.39. Deterministic context-relay universal-handoff coverage (3 tests); 3/3 pass on merge result.

* docs(i18n): full sync zh-TW and zh-CN README with canonical English v3.8.39 (cheaper-inference#5171)

Integrated into release/v3.8.39. Full zh-TW docs tree + zh-CN sync with canonical English v3.8.39; docs-only.

* fix(serve): honour HOSTNAME from .env instead of hardcoding 0.0.0.0 (cheaper-inference#5134) (cheaper-inference#5170)

Integrated into release/v3.8.39. HOSTNAME env override in serve (cheaper-inference#5134) + regression test (4/4, TDD flip-proof verified).

* fix(sse): resolve nameless deepseek-web tool blocks via parameter-schema match (cheaper-inference#5154) (cheaper-inference#5173)

Integrated into release/v3.8.39. Schema-based nameless deepseek-web tool-block resolution (cheaper-inference#5154); 6/6 tests pass on merge result (incl. ambiguous/no-match negatives + named-tag no-regression).

* fix(sse): normalize array user content for Command Code to avoid upstream 400 (cheaper-inference#5166) (cheaper-inference#5174)

Integrated into release/v3.8.39. Normalize array user content for Command Code (cheaper-inference#5166, user-array/400 symptom); 4/4 tests pass on merge result.

* fix(sse): defer </think> close so it never leaks before tool_calls (cheaper-inference#5123) (cheaper-inference#5175)

Integrated into release/v3.8.39. Defer </think> close so it never leaks before tool_calls (cheaper-inference#5123); 4/4 tests pass (incl. cheaper-inference#4633 no-regression). CHANGELOG synced to keep all 3 v3.8.39 fixes.

* fix(dashboard): use amber for home update-step warning icon (cheaper-inference#5176)

Integrated into release/v3.8.39. Amber for home update-step warning icon; 1/1 UI test.

* fix(api): LAN/Tailscale dashboard — host-aware CSP + GET-exempt version route + combo field errors (cheaper-inference#5083) (cheaper-inference#5177)

Integrated into release/v3.8.39. Host-aware CSP (ReDoS/injection-safe host validation) + GET-exempt /api/system/version (POST/spawn stays LOCAL_ONLY, exact-match safe-methods-only) + COMBO_002 firstField. 44/44 tests + route-guard membership gate green. CHANGELOG synced to keep all 4 v3.8.39 fixes.

* fix(api): replace cheaper-inference#5083 global middleware CSP with declarative ws: scheme (cheaper-inference#5083)

Follow-up to PR cheaper-inference#5177 (merged): that version implemented the LAN-CSP fix (Bug 1)
with a new global `src/middleware.ts` + `src/server/csp.ts`, which contradicts the
project's documented architecture — 'No global Next.js middleware — interception is
route-specific' (CLAUDE.md / AGENTS.md) — and was merged unverified (middleware vs
next.config header precedence was never confirmed in a real build).

This replaces that approach with the minimal, declarative equivalent:
  • next.config.mjs: connect-src now permits the bare `ws:` scheme (symmetric with the
    bare `wss:` already allowed) so the dashboard can reach its own Live WS server from
    a LAN/Tailscale host. No middleware.
  • Removes src/middleware.ts, src/server/csp.ts, and tests/unit/csp-host-aware.test.ts.
  • Adds tests/unit/csp-lan-ws-5083.test.ts (incl. a guard asserting src/middleware.ts
    does NOT exist, so the global-middleware approach cannot silently return).

Bugs 2 (GET-exempt /api/system/version) and 3 (COMBO_002 field surfacing) from cheaper-inference#5177
are unaffected and remain in place.

Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>

* test(combo): end-to-end quota-share DRR routing-decision coverage (matrix parity) (cheaper-inference#5179)

Integrated into release/v3.8.39. Quota-share DRR routing-decision coverage (matrix parity); 2/2 pass on merge result.

* feat(agent-bridge): graceful cert-install fallback with manual guide for containers (cheaper-inference#4546) (cheaper-inference#5178)

Integrated into release/v3.8.39. Agent-bridge graceful cert-install fallback + manual guide (cheaper-inference#4546); 6/6 tests pass on merge result.

* fix(antigravity): family-scoped quota lockout (gemini/claude buckets) (cheaper-inference#5180)

Integrated into release/v3.8.39 — family-scoped antigravity quota lockout. Rebased from v3.8.37 + validated (vitest 5/5, typecheck clean, full combo-matrix green, model-lockout 99/0). Same-model cross-account retry (chat.ts) deferred pending live antigravity VPS validation.

* fix(cli): force NODE_ENV to match dev/start run mode in custom Next server (cheaper-inference#5189)

Integrated into release/v3.8.39. Force NODE_ENV to match dev/start run mode in custom Next server; 2/2 source-scan+ordering tests pass on merge result.

* feat(compression): CCR ranged/grep/stats retrieval (ReDoS-safe, backward-compat) (cheaper-inference#5187)

Integrated into release/v3.8.39. CCR ranged/grep/stats retrieval (safe-regex ReDoS guard + length/match caps); 17/17 tests pass on merge result.

* docs(combo): sync all combo/routing-strategy docs to current state + document test coverage (cheaper-inference#5185)

Integrated into release/v3.8.39. Combo/routing-strategy docs sync; docs-only.

* fix(mcp): return 404 (not 400) for unknown Streamable HTTP session id (cheaper-inference#5169) (cheaper-inference#5191)

* fix(api): respect blocked Auto (Zero-Config) provider in /v1/models catalog (cheaper-inference#5192) (cheaper-inference#5194)

* test(combo): deterministic context-relay codex quota-handoff coverage (closes last gap) (cheaper-inference#5195)

* test(ci): wire antigravity-quota-family under test:vitest (fix test-discovery orphan) (cheaper-inference#5196)

* fix(oauth): antigravity login no longer hangs — fire-and-forget onboarding + bounded post-exchange (cheaper-inference#5193)

Antigravity OAuth hang fix (no-PKCE/no-openid + bounded post-exchange + exchange-500 fix). Includes cheaper-inference#5200 (Koosha) revert + owner rebaseline to keep documented comments. Integrated into release/v3.8.39.

* feat(oauth): remote Antigravity login via local helper + paste-credentials (cheaper-inference#5203)

Remote Antigravity login: local helper (omniroute login antigravity) + paste-credentials. Integrated into release/v3.8.39.

* fix(translator): accept Claude Messages shape in non-stream malformed-200 guard (cheaper-inference#5156)

Integrated into release/v3.8.39

* fix(cli): default dev bundler to Turbopack (16.2.x panic no longer reproduces) (cheaper-inference#5206)

Integrated into release/v3.8.39

* fix(cli): auto-calibrate server V8 heap from physical RAM (cheaper-inference#5172) (cheaper-inference#5213)

The server was spawned with a fixed --max-old-space-size=512 (omniroute serve)
or no heap flag at all (Electron), so RAM-rich boxes still OOM-crashed under
load (Ineffective mark-compacts near heap limit ~500MB) with many providers/
accounts and large model catalogs. New calibrateHeapFallbackMb(os.totalmem())
defaults the heap to ~35% of RAM clamped [512,4096], wired into serve.mjs and
electron/main.js. Explicit OMNIROUTE_MEMORY_MB still wins (cheaper-inference#2939 unchanged).

Also addresses cheaper-inference#5160 (same OOM root); cheaper-inference#5152 (docker) benefits via the same knob.

Closes cheaper-inference#5172

* fix(proxy): coalesce fast-fail health probes (cheaper-inference#5208)

Integrated into release/v3.8.39

* fix(proxy): close dispatchers when clearing cache (cheaper-inference#5202)

Integrated into release/v3.8.39

* fix(cli): raise dev server Node heap limit to 8GB to prevent OOM (cheaper-inference#5198)

Integrated into release/v3.8.39

* fix(auth): allow synthetic no-auth fallback for mimocode (cheaper-inference#5205)

Integrated into release/v3.8.39

* fix(oauth): preserve Antigravity refresh_token on empty/omitted upstream response (cheaper-inference#3850) (cheaper-inference#5214)

Google's OAuth refresh tokens are non-rotating: the refresh response usually
omits refresh_token and occasionally returns it as an empty string. The
Antigravity executor used `typeof tokens.refresh_token === "string" ? ... `
which accepts "" (typeof "" === "string") and overwrote the stored token with
empty, nulling it on first refresh. Now treats non-string OR empty as absent and
preserves credentials.refreshToken, matching refreshGoogleToken semantics.

Closes cheaper-inference#3850

* fix(responses): normalize non-array input (cheaper-inference#5204)

Integrated into release/v3.8.39

* fix(stream): normalize safety finish reasons via shared helper (cheaper-inference#5197)

Integrated into release/v3.8.39

* fix(request-logger): never render negative '(-100%)' compression badge (cheaper-inference#5201)

Integrated into release/v3.8.39

* fix(combo): reject empty responses api output (cheaper-inference#5207)

Integrated into release/v3.8.39 — combo failover now rejects empty Responses API output (validateQuality). Baseline rebaseline dropped (main-measured drift; maintainer rebaselines at release).

* fix(pwa): prefer cached navigation before offline page (cheaper-inference#5209)

Integrated into release/v3.8.39 — PWA service worker prefers cached navigation before offline page (cheaper-inference#5165).

* chore(release): v3.8.39 — 2026-06-28

* chore(release): rebaseline openapi+i18n coverage ratchet drift for v3.8.39

---------

Co-authored-by: Arthur Bodera <abodera@gmail.com>
Co-authored-by: Nguyen Minh <lop123thcs@gmail.com>
Co-authored-by: lunkerchen <labanchen@gmail.com>
Co-authored-by: Ankit <177378174+anki1kr@users.noreply.github.com>
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: Ardem2025 <ardemb22@gmail.com>
Co-authored-by: backryun <bakryun0718@proton.me>
Co-authored-by: Anton <39598727+NomenAK@users.noreply.github.com>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
diegosouzapw added a commit that referenced this pull request Jul 19, 2026
…peline (#7339) (#7736)

Phases 3-4 of #3384 (Phases 1-2 shipped DB schema + resolveInterceptSearch in
release/v3.8.47). Adds resolveInterceptFetch(provider, model) as a structural
twin of resolveInterceptSearch, and open-sse/services/webFetchInterception.ts
(mirroring webSearchFallback.ts) to rewrite a provider-native web_fetch tool
declaration into a synthetic omniroute_web_fetch function tool. The synthetic
tool call is dispatched through the existing handleToolCallExecution path
(same as omniroute_web_search) to a new web_fetch builtin skill handler that
resolves credentials and calls handleWebFetch() against /v1/web/fetch.

Strictly opt-in: with no interceptFetch DB row configured (the default), the
outgoing request body is byte-identical to pre-change behavior — no heuristic
default bypass like the interceptSearch sibling, to guarantee zero overhead
when disabled (Hard Rule #20).

Also ships the dashboard toggle (owner decision, overriding the plan's
backend-only recommendation): ProviderInterceptionSection.tsx on the provider
detail page, backed by GET/PUT/DELETE /api/providers/[id]/interception-rules,
covering both interceptSearch and interceptFetch from one control.

chatCore.ts touch is minimal (frozen file): one resolver call + one
prepareWebFetchFallbackBody call mirroring the existing interceptSearch block,
plus threading provider/model into the existing handleToolCallExecution call.
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
* chore(release): open v3.8.39 development cycle

* docs(changelog): backfill 5 v3.8.38 bullets merged after release finalize

These PRs squash-merged into release/v3.8.38 between the CHANGELOG finalize
(f6aa6c3) and the merge-to-main (e2efe2c), so they shipped in the v3.8.38
tag but had no bullet:

- feat(compression): Ionizer engine (lossy JSON-array sampling + CCR) (cheaper-inference#5148)
- fix(sse): preserve non-stream reasoning fields (cheaper-inference#5155, @rdself)
- fix(i18n): add missing English UI labels (cheaper-inference#5153, @rdself)
- test(combo): gated live smoke (cheaper-inference#5151) + release-expectations refresh (cheaper-inference#5150, @KooshaPari)

(cheaper-inference#5129 exact-host Anthropic baseUrl is already covered by the cheaper-inference#5130 bullet — same CodeQL cheaper-inference#674.)
Synced 41 i18n CHANGELOG mirrors.

* feat(compression): TOON best-of-N candidate encoder + encoder A/B table (cheaper-inference#5163)

Integrated into release/v3.8.39. TOON best-of-N candidate encoder (GCF default, fail-open). 17/17 unit tests pass on merge result; CI reds were base-stale + Quality Ratchet DRIFT.

* fix(zenmux): normalize vendor-prefixed GLM system roles (cheaper-inference#5158)

Integrated into release/v3.8.39. ZenMux vendor-prefixed GLM system-role normalization; 12/12 role-normalizer tests pass on merge result. CI reds base-stale.

* [codex] fix xAI OAuth test and reasoning effort (cheaper-inference#5157)

Integrated into release/v3.8.39. xAI reasoning-effort normalization (max/xhigh→high) + OAuth test config; 46/46 xai-translator tests pass on merge result. CI reds base-stale.

* docs(i18n): add Traditional Chinese (zh-TW) README and update zh-CN to latest (cheaper-inference#5162)

Integrated into release/v3.8.39. Traditional Chinese (zh-TW) README + zh-CN refresh; docs-only.

* test(security): guard PII redaction stays opt-in (default off) + Hard Rule cheaper-inference#20 (cheaper-inference#5159)

Integrated into release/v3.8.39. PII opt-in regression guard + Hard Rule cheaper-inference#20; rebased to strip base-drift (+81/-1). 5/5 guard tests pass; flip-proof verified.

* test(combo): deterministic context-relay universal-handoff coverage (closes phase-2 TODO) (cheaper-inference#5168)

Integrated into release/v3.8.39. Deterministic context-relay universal-handoff coverage (3 tests); 3/3 pass on merge result.

* docs(i18n): full sync zh-TW and zh-CN README with canonical English v3.8.39 (cheaper-inference#5171)

Integrated into release/v3.8.39. Full zh-TW docs tree + zh-CN sync with canonical English v3.8.39; docs-only.

* fix(serve): honour HOSTNAME from .env instead of hardcoding 0.0.0.0 (cheaper-inference#5134) (cheaper-inference#5170)

Integrated into release/v3.8.39. HOSTNAME env override in serve (cheaper-inference#5134) + regression test (4/4, TDD flip-proof verified).

* fix(sse): resolve nameless deepseek-web tool blocks via parameter-schema match (cheaper-inference#5154) (cheaper-inference#5173)

Integrated into release/v3.8.39. Schema-based nameless deepseek-web tool-block resolution (cheaper-inference#5154); 6/6 tests pass on merge result (incl. ambiguous/no-match negatives + named-tag no-regression).

* fix(sse): normalize array user content for Command Code to avoid upstream 400 (cheaper-inference#5166) (cheaper-inference#5174)

Integrated into release/v3.8.39. Normalize array user content for Command Code (cheaper-inference#5166, user-array/400 symptom); 4/4 tests pass on merge result.

* fix(sse): defer </think> close so it never leaks before tool_calls (cheaper-inference#5123) (cheaper-inference#5175)

Integrated into release/v3.8.39. Defer </think> close so it never leaks before tool_calls (cheaper-inference#5123); 4/4 tests pass (incl. cheaper-inference#4633 no-regression). CHANGELOG synced to keep all 3 v3.8.39 fixes.

* fix(dashboard): use amber for home update-step warning icon (cheaper-inference#5176)

Integrated into release/v3.8.39. Amber for home update-step warning icon; 1/1 UI test.

* fix(api): LAN/Tailscale dashboard — host-aware CSP + GET-exempt version route + combo field errors (cheaper-inference#5083) (cheaper-inference#5177)

Integrated into release/v3.8.39. Host-aware CSP (ReDoS/injection-safe host validation) + GET-exempt /api/system/version (POST/spawn stays LOCAL_ONLY, exact-match safe-methods-only) + COMBO_002 firstField. 44/44 tests + route-guard membership gate green. CHANGELOG synced to keep all 4 v3.8.39 fixes.

* fix(api): replace cheaper-inference#5083 global middleware CSP with declarative ws: scheme (cheaper-inference#5083)

Follow-up to PR cheaper-inference#5177 (merged): that version implemented the LAN-CSP fix (Bug 1)
with a new global `src/middleware.ts` + `src/server/csp.ts`, which contradicts the
project's documented architecture — 'No global Next.js middleware — interception is
route-specific' (CLAUDE.md / AGENTS.md) — and was merged unverified (middleware vs
next.config header precedence was never confirmed in a real build).

This replaces that approach with the minimal, declarative equivalent:
  • next.config.mjs: connect-src now permits the bare `ws:` scheme (symmetric with the
    bare `wss:` already allowed) so the dashboard can reach its own Live WS server from
    a LAN/Tailscale host. No middleware.
  • Removes src/middleware.ts, src/server/csp.ts, and tests/unit/csp-host-aware.test.ts.
  • Adds tests/unit/csp-lan-ws-5083.test.ts (incl. a guard asserting src/middleware.ts
    does NOT exist, so the global-middleware approach cannot silently return).

Bugs 2 (GET-exempt /api/system/version) and 3 (COMBO_002 field surfacing) from cheaper-inference#5177
are unaffected and remain in place.

Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>

* test(combo): end-to-end quota-share DRR routing-decision coverage (matrix parity) (cheaper-inference#5179)

Integrated into release/v3.8.39. Quota-share DRR routing-decision coverage (matrix parity); 2/2 pass on merge result.

* feat(agent-bridge): graceful cert-install fallback with manual guide for containers (cheaper-inference#4546) (cheaper-inference#5178)

Integrated into release/v3.8.39. Agent-bridge graceful cert-install fallback + manual guide (cheaper-inference#4546); 6/6 tests pass on merge result.

* fix(antigravity): family-scoped quota lockout (gemini/claude buckets) (cheaper-inference#5180)

Integrated into release/v3.8.39 — family-scoped antigravity quota lockout. Rebased from v3.8.37 + validated (vitest 5/5, typecheck clean, full combo-matrix green, model-lockout 99/0). Same-model cross-account retry (chat.ts) deferred pending live antigravity VPS validation.

* fix(cli): force NODE_ENV to match dev/start run mode in custom Next server (cheaper-inference#5189)

Integrated into release/v3.8.39. Force NODE_ENV to match dev/start run mode in custom Next server; 2/2 source-scan+ordering tests pass on merge result.

* feat(compression): CCR ranged/grep/stats retrieval (ReDoS-safe, backward-compat) (cheaper-inference#5187)

Integrated into release/v3.8.39. CCR ranged/grep/stats retrieval (safe-regex ReDoS guard + length/match caps); 17/17 tests pass on merge result.

* docs(combo): sync all combo/routing-strategy docs to current state + document test coverage (cheaper-inference#5185)

Integrated into release/v3.8.39. Combo/routing-strategy docs sync; docs-only.

* fix(mcp): return 404 (not 400) for unknown Streamable HTTP session id (cheaper-inference#5169) (cheaper-inference#5191)

* fix(api): respect blocked Auto (Zero-Config) provider in /v1/models catalog (cheaper-inference#5192) (cheaper-inference#5194)

* test(combo): deterministic context-relay codex quota-handoff coverage (closes last gap) (cheaper-inference#5195)

* test(ci): wire antigravity-quota-family under test:vitest (fix test-discovery orphan) (cheaper-inference#5196)

* fix(oauth): antigravity login no longer hangs — fire-and-forget onboarding + bounded post-exchange (cheaper-inference#5193)

Antigravity OAuth hang fix (no-PKCE/no-openid + bounded post-exchange + exchange-500 fix). Includes cheaper-inference#5200 (Koosha) revert + owner rebaseline to keep documented comments. Integrated into release/v3.8.39.

* feat(oauth): remote Antigravity login via local helper + paste-credentials (cheaper-inference#5203)

Remote Antigravity login: local helper (omniroute login antigravity) + paste-credentials. Integrated into release/v3.8.39.

* fix(translator): accept Claude Messages shape in non-stream malformed-200 guard (cheaper-inference#5156)

Integrated into release/v3.8.39

* fix(cli): default dev bundler to Turbopack (16.2.x panic no longer reproduces) (cheaper-inference#5206)

Integrated into release/v3.8.39

* fix(cli): auto-calibrate server V8 heap from physical RAM (cheaper-inference#5172) (cheaper-inference#5213)

The server was spawned with a fixed --max-old-space-size=512 (omniroute serve)
or no heap flag at all (Electron), so RAM-rich boxes still OOM-crashed under
load (Ineffective mark-compacts near heap limit ~500MB) with many providers/
accounts and large model catalogs. New calibrateHeapFallbackMb(os.totalmem())
defaults the heap to ~35% of RAM clamped [512,4096], wired into serve.mjs and
electron/main.js. Explicit OMNIROUTE_MEMORY_MB still wins (cheaper-inference#2939 unchanged).

Also addresses cheaper-inference#5160 (same OOM root); cheaper-inference#5152 (docker) benefits via the same knob.

Closes cheaper-inference#5172

* fix(proxy): coalesce fast-fail health probes (cheaper-inference#5208)

Integrated into release/v3.8.39

* fix(proxy): close dispatchers when clearing cache (cheaper-inference#5202)

Integrated into release/v3.8.39

* fix(cli): raise dev server Node heap limit to 8GB to prevent OOM (cheaper-inference#5198)

Integrated into release/v3.8.39

* fix(auth): allow synthetic no-auth fallback for mimocode (cheaper-inference#5205)

Integrated into release/v3.8.39

* fix(oauth): preserve Antigravity refresh_token on empty/omitted upstream response (cheaper-inference#3850) (cheaper-inference#5214)

Google's OAuth refresh tokens are non-rotating: the refresh response usually
omits refresh_token and occasionally returns it as an empty string. The
Antigravity executor used `typeof tokens.refresh_token === "string" ? ... `
which accepts "" (typeof "" === "string") and overwrote the stored token with
empty, nulling it on first refresh. Now treats non-string OR empty as absent and
preserves credentials.refreshToken, matching refreshGoogleToken semantics.

Closes cheaper-inference#3850

* fix(responses): normalize non-array input (cheaper-inference#5204)

Integrated into release/v3.8.39

* fix(stream): normalize safety finish reasons via shared helper (cheaper-inference#5197)

Integrated into release/v3.8.39

* fix(request-logger): never render negative '(-100%)' compression badge (cheaper-inference#5201)

Integrated into release/v3.8.39

* fix(combo): reject empty responses api output (cheaper-inference#5207)

Integrated into release/v3.8.39 — combo failover now rejects empty Responses API output (validateQuality). Baseline rebaseline dropped (main-measured drift; maintainer rebaselines at release).

* fix(pwa): prefer cached navigation before offline page (cheaper-inference#5209)

Integrated into release/v3.8.39 — PWA service worker prefers cached navigation before offline page (cheaper-inference#5165).

* chore(release): v3.8.39 — 2026-06-28

* chore(release): rebaseline openapi+i18n coverage ratchet drift for v3.8.39

---------

Co-authored-by: Arthur Bodera <abodera@gmail.com>
Co-authored-by: Nguyen Minh <lop123thcs@gmail.com>
Co-authored-by: lunkerchen <labanchen@gmail.com>
Co-authored-by: Ankit <177378174+anki1kr@users.noreply.github.com>
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: Ardem2025 <ardemb22@gmail.com>
Co-authored-by: backryun <bakryun0718@proton.me>
Co-authored-by: Anton <39598727+NomenAK@users.noreply.github.com>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…peline (cheaper-inference#7339) (cheaper-inference#7736)

Phases 3-4 of cheaper-inference#3384 (Phases 1-2 shipped DB schema + resolveInterceptSearch in
release/v3.8.47). Adds resolveInterceptFetch(provider, model) as a structural
twin of resolveInterceptSearch, and open-sse/services/webFetchInterception.ts
(mirroring webSearchFallback.ts) to rewrite a provider-native web_fetch tool
declaration into a synthetic omniroute_web_fetch function tool. The synthetic
tool call is dispatched through the existing handleToolCallExecution path
(same as omniroute_web_search) to a new web_fetch builtin skill handler that
resolves credentials and calls handleWebFetch() against /v1/web/fetch.

Strictly opt-in: with no interceptFetch DB row configured (the default), the
outgoing request body is byte-identical to pre-change behavior — no heuristic
default bypass like the interceptSearch sibling, to guarantee zero overhead
when disabled (Hard Rule cheaper-inference#20).

Also ships the dashboard toggle (owner decision, overriding the plan's
backend-only recommendation): ProviderInterceptionSection.tsx on the provider
detail page, backed by GET/PUT/DELETE /api/providers/[id]/interception-rules,
covering both interceptSearch and interceptFetch from one control.

chatCore.ts touch is minimal (frozen file): one resolver call + one
prepareWebFetchFallbackBody call mirroring the existing interceptSearch block,
plus threading provider/model into the existing handleToolCallExecution call.
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
* chore(release): open v3.8.39 development cycle

* docs(changelog): backfill 5 v3.8.38 bullets merged after release finalize

These PRs squash-merged into release/v3.8.38 between the CHANGELOG finalize
(ff57be3) and the merge-to-main (ae6e234), so they shipped in the v3.8.38
tag but had no bullet:

- feat(compression): Ionizer engine (lossy JSON-array sampling + CCR) (cheaper-inference#5148)
- fix(sse): preserve non-stream reasoning fields (cheaper-inference#5155, @rdself)
- fix(i18n): add missing English UI labels (cheaper-inference#5153, @rdself)
- test(combo): gated live smoke (cheaper-inference#5151) + release-expectations refresh (cheaper-inference#5150, @KooshaPari)

(cheaper-inference#5129 exact-host Anthropic baseUrl is already covered by the cheaper-inference#5130 bullet — same CodeQL cheaper-inference#674.)
Synced 41 i18n CHANGELOG mirrors.

* feat(compression): TOON best-of-N candidate encoder + encoder A/B table (cheaper-inference#5163)

Integrated into release/v3.8.39. TOON best-of-N candidate encoder (GCF default, fail-open). 17/17 unit tests pass on merge result; CI reds were base-stale + Quality Ratchet DRIFT.

* fix(zenmux): normalize vendor-prefixed GLM system roles (cheaper-inference#5158)

Integrated into release/v3.8.39. ZenMux vendor-prefixed GLM system-role normalization; 12/12 role-normalizer tests pass on merge result. CI reds base-stale.

* [codex] fix xAI OAuth test and reasoning effort (cheaper-inference#5157)

Integrated into release/v3.8.39. xAI reasoning-effort normalization (max/xhigh→high) + OAuth test config; 46/46 xai-translator tests pass on merge result. CI reds base-stale.

* docs(i18n): add Traditional Chinese (zh-TW) README and update zh-CN to latest (cheaper-inference#5162)

Integrated into release/v3.8.39. Traditional Chinese (zh-TW) README + zh-CN refresh; docs-only.

* test(security): guard PII redaction stays opt-in (default off) + Hard Rule cheaper-inference#20 (cheaper-inference#5159)

Integrated into release/v3.8.39. PII opt-in regression guard + Hard Rule cheaper-inference#20; rebased to strip base-drift (+81/-1). 5/5 guard tests pass; flip-proof verified.

* test(combo): deterministic context-relay universal-handoff coverage (closes phase-2 TODO) (cheaper-inference#5168)

Integrated into release/v3.8.39. Deterministic context-relay universal-handoff coverage (3 tests); 3/3 pass on merge result.

* docs(i18n): full sync zh-TW and zh-CN README with canonical English v3.8.39 (cheaper-inference#5171)

Integrated into release/v3.8.39. Full zh-TW docs tree + zh-CN sync with canonical English v3.8.39; docs-only.

* fix(serve): honour HOSTNAME from .env instead of hardcoding 0.0.0.0 (cheaper-inference#5134) (cheaper-inference#5170)

Integrated into release/v3.8.39. HOSTNAME env override in serve (cheaper-inference#5134) + regression test (4/4, TDD flip-proof verified).

* fix(sse): resolve nameless deepseek-web tool blocks via parameter-schema match (cheaper-inference#5154) (cheaper-inference#5173)

Integrated into release/v3.8.39. Schema-based nameless deepseek-web tool-block resolution (cheaper-inference#5154); 6/6 tests pass on merge result (incl. ambiguous/no-match negatives + named-tag no-regression).

* fix(sse): normalize array user content for Command Code to avoid upstream 400 (cheaper-inference#5166) (cheaper-inference#5174)

Integrated into release/v3.8.39. Normalize array user content for Command Code (cheaper-inference#5166, user-array/400 symptom); 4/4 tests pass on merge result.

* fix(sse): defer </think> close so it never leaks before tool_calls (cheaper-inference#5123) (cheaper-inference#5175)

Integrated into release/v3.8.39. Defer </think> close so it never leaks before tool_calls (cheaper-inference#5123); 4/4 tests pass (incl. cheaper-inference#4633 no-regression). CHANGELOG synced to keep all 3 v3.8.39 fixes.

* fix(dashboard): use amber for home update-step warning icon (cheaper-inference#5176)

Integrated into release/v3.8.39. Amber for home update-step warning icon; 1/1 UI test.

* fix(api): LAN/Tailscale dashboard — host-aware CSP + GET-exempt version route + combo field errors (cheaper-inference#5083) (cheaper-inference#5177)

Integrated into release/v3.8.39. Host-aware CSP (ReDoS/injection-safe host validation) + GET-exempt /api/system/version (POST/spawn stays LOCAL_ONLY, exact-match safe-methods-only) + COMBO_002 firstField. 44/44 tests + route-guard membership gate green. CHANGELOG synced to keep all 4 v3.8.39 fixes.

* fix(api): replace cheaper-inference#5083 global middleware CSP with declarative ws: scheme (cheaper-inference#5083)

Follow-up to PR cheaper-inference#5177 (merged): that version implemented the LAN-CSP fix (Bug 1)
with a new global `src/middleware.ts` + `src/server/csp.ts`, which contradicts the
project's documented architecture — 'No global Next.js middleware — interception is
route-specific' (CLAUDE.md / AGENTS.md) — and was merged unverified (middleware vs
next.config header precedence was never confirmed in a real build).

This replaces that approach with the minimal, declarative equivalent:
  • next.config.mjs: connect-src now permits the bare `ws:` scheme (symmetric with the
    bare `wss:` already allowed) so the dashboard can reach its own Live WS server from
    a LAN/Tailscale host. No middleware.
  • Removes src/middleware.ts, src/server/csp.ts, and tests/unit/csp-host-aware.test.ts.
  • Adds tests/unit/csp-lan-ws-5083.test.ts (incl. a guard asserting src/middleware.ts
    does NOT exist, so the global-middleware approach cannot silently return).

Bugs 2 (GET-exempt /api/system/version) and 3 (COMBO_002 field surfacing) from cheaper-inference#5177
are unaffected and remain in place.

Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>

* test(combo): end-to-end quota-share DRR routing-decision coverage (matrix parity) (cheaper-inference#5179)

Integrated into release/v3.8.39. Quota-share DRR routing-decision coverage (matrix parity); 2/2 pass on merge result.

* feat(agent-bridge): graceful cert-install fallback with manual guide for containers (cheaper-inference#4546) (cheaper-inference#5178)

Integrated into release/v3.8.39. Agent-bridge graceful cert-install fallback + manual guide (cheaper-inference#4546); 6/6 tests pass on merge result.

* fix(antigravity): family-scoped quota lockout (gemini/claude buckets) (cheaper-inference#5180)

Integrated into release/v3.8.39 — family-scoped antigravity quota lockout. Rebased from v3.8.37 + validated (vitest 5/5, typecheck clean, full combo-matrix green, model-lockout 99/0). Same-model cross-account retry (chat.ts) deferred pending live antigravity VPS validation.

* fix(cli): force NODE_ENV to match dev/start run mode in custom Next server (cheaper-inference#5189)

Integrated into release/v3.8.39. Force NODE_ENV to match dev/start run mode in custom Next server; 2/2 source-scan+ordering tests pass on merge result.

* feat(compression): CCR ranged/grep/stats retrieval (ReDoS-safe, backward-compat) (cheaper-inference#5187)

Integrated into release/v3.8.39. CCR ranged/grep/stats retrieval (safe-regex ReDoS guard + length/match caps); 17/17 tests pass on merge result.

* docs(combo): sync all combo/routing-strategy docs to current state + document test coverage (cheaper-inference#5185)

Integrated into release/v3.8.39. Combo/routing-strategy docs sync; docs-only.

* fix(mcp): return 404 (not 400) for unknown Streamable HTTP session id (cheaper-inference#5169) (cheaper-inference#5191)

* fix(api): respect blocked Auto (Zero-Config) provider in /v1/models catalog (cheaper-inference#5192) (cheaper-inference#5194)

* test(combo): deterministic context-relay codex quota-handoff coverage (closes last gap) (cheaper-inference#5195)

* test(ci): wire antigravity-quota-family under test:vitest (fix test-discovery orphan) (cheaper-inference#5196)

* fix(oauth): antigravity login no longer hangs — fire-and-forget onboarding + bounded post-exchange (cheaper-inference#5193)

Antigravity OAuth hang fix (no-PKCE/no-openid + bounded post-exchange + exchange-500 fix). Includes cheaper-inference#5200 (Koosha) revert + owner rebaseline to keep documented comments. Integrated into release/v3.8.39.

* feat(oauth): remote Antigravity login via local helper + paste-credentials (cheaper-inference#5203)

Remote Antigravity login: local helper (omniroute login antigravity) + paste-credentials. Integrated into release/v3.8.39.

* fix(translator): accept Claude Messages shape in non-stream malformed-200 guard (cheaper-inference#5156)

Integrated into release/v3.8.39

* fix(cli): default dev bundler to Turbopack (16.2.x panic no longer reproduces) (cheaper-inference#5206)

Integrated into release/v3.8.39

* fix(cli): auto-calibrate server V8 heap from physical RAM (cheaper-inference#5172) (cheaper-inference#5213)

The server was spawned with a fixed --max-old-space-size=512 (omniroute serve)
or no heap flag at all (Electron), so RAM-rich boxes still OOM-crashed under
load (Ineffective mark-compacts near heap limit ~500MB) with many providers/
accounts and large model catalogs. New calibrateHeapFallbackMb(os.totalmem())
defaults the heap to ~35% of RAM clamped [512,4096], wired into serve.mjs and
electron/main.js. Explicit OMNIROUTE_MEMORY_MB still wins (cheaper-inference#2939 unchanged).

Also addresses cheaper-inference#5160 (same OOM root); cheaper-inference#5152 (docker) benefits via the same knob.

Closes cheaper-inference#5172

* fix(proxy): coalesce fast-fail health probes (cheaper-inference#5208)

Integrated into release/v3.8.39

* fix(proxy): close dispatchers when clearing cache (cheaper-inference#5202)

Integrated into release/v3.8.39

* fix(cli): raise dev server Node heap limit to 8GB to prevent OOM (cheaper-inference#5198)

Integrated into release/v3.8.39

* fix(auth): allow synthetic no-auth fallback for mimocode (cheaper-inference#5205)

Integrated into release/v3.8.39

* fix(oauth): preserve Antigravity refresh_token on empty/omitted upstream response (cheaper-inference#3850) (cheaper-inference#5214)

Google's OAuth refresh tokens are non-rotating: the refresh response usually
omits refresh_token and occasionally returns it as an empty string. The
Antigravity executor used `typeof tokens.refresh_token === "string" ? ... `
which accepts "" (typeof "" === "string") and overwrote the stored token with
empty, nulling it on first refresh. Now treats non-string OR empty as absent and
preserves credentials.refreshToken, matching refreshGoogleToken semantics.

Closes cheaper-inference#3850

* fix(responses): normalize non-array input (cheaper-inference#5204)

Integrated into release/v3.8.39

* fix(stream): normalize safety finish reasons via shared helper (cheaper-inference#5197)

Integrated into release/v3.8.39

* fix(request-logger): never render negative '(-100%)' compression badge (cheaper-inference#5201)

Integrated into release/v3.8.39

* fix(combo): reject empty responses api output (cheaper-inference#5207)

Integrated into release/v3.8.39 — combo failover now rejects empty Responses API output (validateQuality). Baseline rebaseline dropped (main-measured drift; maintainer rebaselines at release).

* fix(pwa): prefer cached navigation before offline page (cheaper-inference#5209)

Integrated into release/v3.8.39 — PWA service worker prefers cached navigation before offline page (cheaper-inference#5165).

* chore(release): v3.8.39 — 2026-06-28

* chore(release): rebaseline openapi+i18n coverage ratchet drift for v3.8.39

---------

Co-authored-by: Arthur Bodera <abodera@gmail.com>
Co-authored-by: Nguyen Minh <lop123thcs@gmail.com>
Co-authored-by: lunkerchen <labanchen@gmail.com>
Co-authored-by: Ankit <177378174+anki1kr@users.noreply.github.com>
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: Ardem2025 <ardemb22@gmail.com>
Co-authored-by: backryun <bakryun0718@proton.me>
Co-authored-by: Anton <39598727+NomenAK@users.noreply.github.com>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
diegosouzapw added a commit that referenced this pull request Aug 6, 2026
…actually shipped

Four suites on the release tip pin a literal count that #9515 (radar) and #9009
(UnoRouter) moved. Each pin was re-derived from the source of truth, not relaxed:

- tests/unit/feature-flags-settings.test.ts 44 -> 45 flags. #9515 added
  RADAR_ENABLED to src/shared/constants/featureFlagDefinitions.ts (defaultValue
  'false', so Hard Rule #20's opt-in guarantee is untouched). 49/49.
- tests/unit/sidebar-costs-section.test.ts 5 -> 6 costs items. #9515 appended
  { id: 'radar', href: '/dashboard/radar' } to the costs section in
  sidebarVisibility/sections.ts; both the order list and the href list now carry
  it in its real (last) position. 7/7.
- tests/unit/providers-constants-split.test.ts 196 -> 197 APIKEY_PROVIDERS.
  #9009 added UnoRouter to the gateways family. The partition assertion moves
  with it, so the no-loss/no-dup invariant still holds. 4/4.
- tests/unit/i18n-vi-completeness.test.ts: fixed the CAUSE instead of the pin —
  translated the 3 keys the Vietnamese locale was missing
  (providers.onboardingProviderDescriptions.unorouter, agentBridge.noMappingsDesc,
  agentBridge.addMapping). 5/5.

Refs #9298
JunNanLYS added a commit to JunNanLYS/OmniRoute that referenced this pull request Aug 14, 2026
…e memory.db) (#16)

* feat(memory): standalone four-layer memory storage core

- New src/memory/ modules: db/core (lazy singleton + WAL), db/migrations
  (5 idempotent SQL files), l0/l1/l2/l3 repositories, operations
  (task_queue, task_lock, memory_settings, embedding_meta), retrieval/rrf
  (k=60 pure fusion), vectorStore (sqlite-vec best-effort).
- Public surface via src/memory/index.ts.
- 78 focused node:test cases under tests/unit/memory-core/ covering DB
  lifecycle (WAL PRAGMAs, idempotent migrations, independent file), all
  four layer CRUD/version/delete semantics, L2 max-15 cap, L3 singleton
  persona continuity, queue claim/DLQ/locks/settings, FTS5 retrieval,
  pure RRF fusion, owner scoping and SQL-injection safety.

* feat(dashboard): rewrite /dashboard/memory four-layer UI with settings

Replace legacy memories/playground/engine tabs with L0/L1/L2/L3/Settings tabs
that match the four-layer memory model. URL query params drive the active tab
and an optional lineage filter (survives reload/share); the chip exposes a
clear-filter action.

L0 surfaces raw, immutable conversation rows (session id, timestamp, role,
provider, model, collapsible content). Internal markers (is_internal /
combo_execution_key) are deliberately hidden. Recycle bin rows support
restore and double-confirmed permanent delete.

L1 lists distilled memory entries with seven localized type chips, priority,
content, scene link, version, last-modified-by, and an edited marker. Edit /
add / soft delete / permanent delete / restore / view-source / view-scene
actions wire to the new /api/memory/l1/* endpoints. Score is rendered only
when the API returns one.

L2 lists scenes (heat, times, summary, version, modifier, content, pending
status) with edit / delete / regenerate actions and links to atoms + persona.

L3 lists prompts (mode chat|code, content bounded to 2000 chars, version,
modifier) with edit / clear / regenerate / lineage links back to L1 and L2.

Settings picks the distillation model with an effective value, source-layer
badge (per-key → global → env → auto), provider/model override (scope self or
global), PUT/DELETE on /api/memory/distillation-model. Status card surfaces
running state, recent error, and DLQ retry. Sync-ed-models dropdown drives
the override picker. All hooks are abort-safe and typed; errors are surfaced
via state rather than thrown.

UI uses the Apple premium components (AppleCard / AppleButton / AppleSurface /
AppleField / AppleSelect / AppleStatusDot) consistent with the recent
provider-card / api-manager work. i18n keys added under memory.l0/l1/l2/l3/
distillation/common and tabs (l0/l1/l2/l3/settings). Legacy tabs and their
UI tests are removed.

* feat(sse,memory): pipeline L0 capture + 4-layer recall/injection hard cutover

Replace legacy extractFacts path with a new per-API-key pipeline that defaults
both capture and injection to OFF, with the legacy memoryEnabled flag honored
ONLY as an explicit migration path. L0 capture persists last user + assistant
visible text (no 64KB cap), strips code blocks, schedules async writes via
setImmediate (fail-open, no retry), enqueues L1 on success, and inserts two
stable idempotency-derived records per turn. The 4-layer recall facade
(L3 stable + L2 nav <=15 + L1 dynamic top-5 + static tools guide) is rendered
into the leading system suffix and a per-turn <relevant-memories> user prefix,
honoring cache-safe placement, system-must-be-first providers, and the GLM/o1
user-role fallback. Existing cheaper-inference#3890/cheaper-inference#6135/cheaper-inference#1701 tests remain green; new focused
tests cover the full surface.

* docs(memory): document v4.0 four-layer hard cutover

Rewrite MEMORY.md and the related doc surfaces (API_REFERENCE,
openapi, ENVIRONMENT, README, CLAUDE.md, CHANGELOG) to describe the
canonical four-layer architecture (L0 raw turns / L1 typed facts /
L2 navigation / L3 working singleton) on a standalone
<DATA_DIR>/memory.db WAL with no backup, reset-by-delete semantics.

Capture and injection are documented as default-off and per-API-key,
PII feature flags remain opt-in per Hard Rule cheaper-inference#20, and the new
x-omniroute-no-memory header is documented alongside the internal
marker. Distillation (selector chain + DLQ at
/api/memory/distillation/dlq), retention defaults, and the fork-only
publication notice are all captured.

The new API routes (/api/memory/{l0,l1,l2,l3,regenerate,
distillation-model,distillation/dlq}) replace the legacy memory CRUD
plus Qdrant/engine/health/reindex/retrieve-preview/summarize routes
with no migration or compatibility shim.

Scope: docs only. Source, tests, package metadata, license, third-party
notices and the in-progress src/lib/memory implementation are
intentionally untouched in this commit.

* feat(memory): distillation worker/selector/permit — native four-layer service

Native modules for the four-layer distillation service and worker. Scope is
strictly isolated:

  src/memory/distillation/**
    config.ts      — env resolution; doubly opt-in (ENABLED+INTERVAL>0);
                     rejects isBuildPhase/isCloud/isAutomatedTestProcess;
                     secret handling for internal marker HMAC.
    store.ts       — DistillationStore interface + InMemoryDistillationStore;
                     createDefaultDistillationStore() uses dynamic imports
                     for the future @/memory/db/repositories adapter and
                     falls back to in-memory dev/test.
    permit.ts      — ProcessPermitPool: nonblocking acquire, TTL auto
                     release, no permit leak.
    scheduler.ts   — pure helpers: warm-up ramp 1→2→4→5, idle 90s,
                     L2 90s delay + 15min/60min debounce, L3 immediate,
                     retry 5/15/45 max 3 → DLQ.
    selector.ts    — 4-tier chain (task hint, per-key, global, env, first
                     active). No silent fallback after selection.
    failure.ts     — classifier + retry/DLQ decision + sanitized error
                     messages (strips stacks/Bearer tokens/api keys/paths).
    internalMarker.ts — X-Omniroute-No-Memory + HMAC-SHA256(secret, nonce|time)
                     sign/verify, depth/call budget caps, loopback origin
                     verification (127.0.0.1, ::1, trusted tokens).
    lock.ts        — owner-level mutual exclusion (TTL 240s, renew 30s).
    usage.ts       — UsageBatcher (flushes on size or interval, never drops
                     on shutdown) + buildUsageRecord helper.
    executor.ts    — plain nonstream runner, breaker seam, is_internal
                     metadata, never trips production breaker.
    handlers.ts    — L0/L1/L2/L3 default handlers with actual orchestration
                     + JSON parsing + regex fallback for L0 evidence;
                     Tencent prompts behind dynamic import.
    worker.ts      — startDistillationWorker/stopDistillationWorker lifecycle,
                     setTimeout initial + setInterval env-driven, unref,
                     synchronous reentry guard, scope owner-lock, no silent
                     provider/model switch, breaker-open re-queues without
                     burning an attempt, model_unset/model_deleted/
                     credentials_invalid → DLQ.
    public.ts      — barrel re-exports.

  tests/unit/memory/distillation/
    config, permit, scheduler, store, selector, internalMarker, failure,
    lock, usage, handlers, worker-lifecycle — 11 focused unit test files
    covering env gates, lifecycle no-start, no-overlap, permit TTL,
    scheduling, selector 4-tier fallback, no runtime auto fallback,
    failure classification/backoff, queue transitions/DLQ, breaker skip,
    internal marker forgery/loopback/depth caps, lock renewal, usage
    batching, handler JSON parsing.

  tsconfig.distillation-only.json — focused project for typecheck
    coverage of the new files without dragging the rest of the monorepo.

Pure modules: every helper (config/scheduler/failure/selector/
internalMarker/permit/usage/lock) is side-effect-free and exercised
without a real DB. The store has a complete in-memory implementation so
the worker can be tested end-to-end with a fake.

Storage seam: DistillationStore is the only contract the repository
agent (@/memory/db) needs to satisfy. createDefaultDistillationStore()
probes the anticipated paths
  - @/memory/db/repositories/distillation (createDistillationStore)
  - @/memory/db/tasks/settings           (createDistillationStore)
and falls back to InMemoryDistillationStore when neither is shipped.

Not touched (per scope): src/memory/db/** (other agent), TencentDB-Agent-
Memory snapshot, chatCore/injection, API routes, UI, MCP/CLI, docs/
licenses, src/lib/memory (existing modules), graceful shutdown.
Wiring of startDistillationWorker/stopDistillationWorker into
src/instrumentation-node.ts and the stop hook is intentionally left for
the integration owner to land alongside the actual repository adapter.

* feat(memory): four-layer API surface (L0..L3 + distillation-model + DLQ)

Adds the new four-layer memory API surface over the existing /api/memory
namespace, replacing the legacy engine-status / health / reindex /
retrieve-preview / summarize / [id] / route routes with a strictly-typed
layered API. All storage access is gated behind a dependency-injected
service interface so the storage repository (TencentDB integration branch)
can be slotted in without touching route handlers.

New routes:

- /api/memory/l0                  GET list, POST import (no PUT)
- /api/memory/l0/[id]             GET / DELETE soft|permanent / POST op=restore
- /api/memory/l0?sessionId=...    POST session delete (soft|permanent)
- /api/memory/l1                  GET list|search, POST create
- /api/memory/l1/[id]             GET / PUT edit (expectedVersion) / DELETE / POST op=restore
- /api/memory/l2                  GET list, POST create
- /api/memory/l2/[id]             GET / PUT edit / DELETE / POST op=restore
- /api/memory/l2/[id]/regenerate  POST enqueue (409 after 15 errors)
- /api/memory/l3                  GET list, POST op=regenerate
- /api/memory/l3/[id]             GET / PUT upsert / DELETE soft|restore|permanent
- /api/memory/distillation-model  GET effective selector / PUT / DELETE
- /api/memory/distillation-model/dlq  GET list + status counts / POST op=retry

Architectural pieces:

- src/shared/schemas/memoryFourLayer.ts — Zod schemas (strict, 7 L1 types,
  optimistic version, distillation scopes, DLQ retry contract).
- src/memory/api/dependencies.ts — DI registry (service, provider-model
  validator, audit writer, task enqueuer). Default no-op service throws
  'not wired' so unconfigured deployments surface 503 instead of crashing.
- src/memory/api/handlers/_lib.ts — auth subject resolution, owner
  isolation (self callers cannot cross owner), management override,
  sanitized error envelopes, audit helper.

Security / hard rules:

- Route handlers contain NO raw SQL (only dependency-injected service calls).
- All error responses go through buildErrorBody / sanitizeErrorMessage.
- No stack traces / absolute paths in response bodies.
- Self API key callers cannot target a different owner via ?apiKeyId=...
  (403). Management callers may.
- Global distillation selector requires management scope (403 otherwise).
- Audit writes on every action (memory.l0.* / l1.* / l2.* / l3.* /
  distillation_model.* / distillation_dlq.*).

Migration:

- Legacy routes deleted (engine-status, health, reindex, retrieve-preview,
  summarize, [id]/route, root route).
- Legacy integration tests removed (memory-engine-status, memory-reindex,
  memory-retrieve-preview, memory-route-put, memory-summarize,
  memory-embedding-providers, memory-route unit).
- embedding-providers route KEEP-GATED: shared with the existing engine
  tab UI; not in the four-layer cutover scope. UI components referencing
  the deleted routes (EngineTab, MemoriesTab, PlaygroundTab) are out of
  scope per the four-layer spec; they will return 404 until the UI branch
  migrates.
- bin/migrate-legacy-memory.ts placeholder shipped so operators can
  discover the migration entrypoint.

Tests:

- tests/unit/memory-four-layer-schemas.test.ts — 7 L1 types, optimistic
  version required, distillation apiKeyId conditional, DLQ retry contract.
- tests/unit/memory-four-layer-deps.test.ts — DI registry swap.
- tests/unit/memory-four-layer-fake-service.ts — in-memory fake service
  shared by route tests.
- tests/integration/memory-four-layer-routes.test.ts — auth 401/403,
  owner isolation, validation, CRUD, L0 no PUT, optimistic conflict,
  regenerate 409 after 15 errors, distillation selector ladder, DLQ
  retry, sanitized error response, 503 when service not wired.

* feat(memory): selective port of TencentDB Agent Memory pure behavior modules

Adds `src/memory/tencent/**` — a snapshot-style, minimal port of pure
behavior modules from TencentDB Agent Memory (MIT, Copyright (C) 2026
Tencent). Source commit: fe3230f.

Ported modules (verbatim prompts, preserved regexes, preserved
behavior contracts):

- text/sanitize.ts        — L0 capture / text-cleaning helpers
                              (sanitizeText, stripCodeBlocks,
                               escapeXmlTags, sanitizeJsonForParse,
                               looksLikePromptInjection,
                               parseExtractionScenes, ...)
- text/chunking.ts        — head/tail extraction chunking
                              (applyOversizeStrategy)
- text/fts.ts             — FTS5 query/tokenize/BM25 helpers
                              → REIMPLEMENTED from scratch (no copy)
                              because the referenced upstream
                              'openclaw core hybrid.ts' has no
                              verifiable license in our source tree
- prompts/l1-extraction.ts — L1 scene+memory extraction prompts
- prompts/l1-dedup.ts       — L1 batch conflict-detection prompts
- prompts/scene-extraction.ts — L2 scene-extraction prompts
- prompts/persona-generation.ts — L3 persona / team-doctrine prompts
- prompts/offload-prompts.ts   — L1/L1.5/L2 offload prompts
- parsers/json-utils.ts       — tolerant JSON parsing
- parsers/l1-offload-parser.ts — L1 entry parser
- parsers/l15-parser.ts        — L1.5 task-judgment parser
- parsers/l2-offload-parser.ts — L2 MMD parser
- parsers/l1-dedup-parser.ts   — store|update|skip|merge parser
- parsers/scene-action-parser.ts — UPDATE/MERGE/CREATE parser
- recall/budget.ts              — RRF k=60 + recall char budget
- recall/memory-tools-guide.ts  — guide + ≤3-calls/turn budget
- scheduling/pipeline.ts        — L1/L2/L3 warmup + threshold logic

Each vendored file carries an ADAPTED FROM TencentDB Agent Memory (MIT)
header pointing to the exact upstream path, source commit, and local
modifications. The verbatim upstream MIT terms are at
src/memory/tencent/README/LICENSE.txt. The per-file modification log
is at src/memory/tencent/README/SOURCE.txt. The umbrella attribution
block is at THIRD_PARTY_NOTICES.md. LICENSE gets a single trailing
pointer line.

Out of scope (not ported): full LLM runner, storage adapters, host
hooks, MCP tools, ai-sdk / OpenClaw / @node-rs/jieba dependencies.

Tests (node:test, 148 cases, 36 suites):
  tests/unit/memory/tencent/{sanitize,chunking,fts,parsers,actions,
  recall,memory-tools-guide,scheduling,prompts}.test.ts

Coverage: code stripping, head/tail chunking, malformed JSON
repair, parser tolerance, dedup actions, scene action parse,
prompt-mode switch (chat vs code), L1/L2/L3 caps, recall budget,
RRF, prompt-injection defense, scheduling thresholds.

* feat(mcp,cli): four-layer memory hard cutover + owner-scoping

Replace the legacy flat MCP memory surface (omniroute_memory_search /
add / clear) and the legacy CLI flat verbs (search / add / clear / list /
get / delete / health) with a four-layer read surface:

  MCP tools (all require read:memory):
    omniroute_memory_l0_search  - vector/semantic search
    omniroute_memory_l1_search  - full-text/FTS5 search
    omniroute_memory_l2_read    - scene/pod read by id
    omniroute_memory_l3_read    - current persona read
    omniroute_memory_list       - cross-layer listing

  CLI verbs:
    memory l0 search <query> [--session --scene --limit]
    memory l1 search <query> [--session --scene --limit]
    memory l2 read <id>
    memory l3 read [--session]
    memory list [--session --scene --limit]
    memory settings get | set <key> <value> | reset
    memory distil status | retry-dlq [--yes]

Owner-scoping: input schemas accept session / scene / query / limit
filters only; cross-tenant apiKeyId / ownerId args are rejected by
assertCallerOwner() before a single byte of data crosses the wire.
The principal id is resolved via resolveMcpCallerApiKeyId() (HTTP
auth context -> env fallback) and falls back to extra.authInfo.clientId
through resolveCallerScopeContext(); 'anonymous' principals fail
closed. Writes are not exposed yet - write:memory is reserved.

Hard Rule #12: thrown errors are routed through sanitizeErrorMessage
on the MCP adapter and through an inline mirror in bin/cli/commands/
memory.mjs so we never print raw stack traces / absolute source paths.

Touches only the cutover scope (memoryTools.ts, server.ts minimal
error sanitization, mcpScopes.ts, memory.mjs, focused tests). Storage /
REST routes / UI / chatCore / instrumentation are untouched (the new
/api/memory/l{0,1,2,3}/* / /api/memory/list / /api/memory/settings /
/api/memory/distil/* routes are intentionally NOT added in this PR -
they will be reconciled after the cherry-pick). At runtime the new
tools / CLI commands fail closed with a sanitized 404/501.

A2A skills already had zero imports from @/lib/memory (verified by
tests/unit/mcp-memory-cutover.test.ts - the five skill entities
remain, memory fields neutral).

Focused tests:
  tests/unit/mcp-memory-cutover.test.ts   (new, 12 cases)
  tests/unit/memory-tools.test.ts         (rewritten, 9 cases)
  tests/unit/cli-memory-commands.test.ts  (rewritten, 13 cases)
  tests/unit/mcp-memory-tools-strategy.test.ts (trimmed, 6 cases)
  tests/integration/memory-pipeline.test.ts (legacy handlers replaced
    with createMemory / deleteMemory / retrieveMemories - chat
    pipeline behavior unchanged)

Suppression: cli-memory-commands.test.ts no-explicit-any count lowered
18 -> 3 (the rewrite removed the legacy test scaffolding's
untyped handlers / options).

Type-check (npm run typecheck:core): clean. Focused test suites
(tests/unit/mcp-memory-cutover.test.ts, tests/unit/memory-tools.test.ts,
tests/unit/cli-memory-commands.test.ts, tests/unit/mcp-memory-tools-strategy.test.ts,
tests/unit/mcp-tool-collections-shape.test.ts): all green. Full suite
is the parent agent's responsibility per the timebox.

* test(sse): track Codex CLI fingerprint version via getCodexClientVersion

The hardcoded 0.144.1 expectation drifted from the production constant
(DEFAULT_CODEX_CLIENT_VERSION is 0.146.0). Assert against the shared
getter so this test cannot silently drift again.

* feat(memory): finish v4.0 four-layer hard cutover

Remove the remaining v3 memory runtime (dashboard components/hooks,
settings/qdrant routes, shared schemas, db/memoryVec) and land the
four-layer runtime tail: standalone memory.db service + repositories +
distillation migrations 006-009, distillation apply/runtime/queue and
L1 scheduling seams, owner-scoped REST route adapters, MCP read tools,
CLI layer commands, chatCore capture/injection wiring, and the
provider-system-message hoist. Drops the Qdrant docker profile and
adds src/memory to the package workspaces.

* docs(memory): sync MEMORY.md, ENVIRONMENT.md, REPOSITORY_MAP and CLAUDE.md with the four-layer runtime

Rewrite the MEMORY.md spec against the shipped surface (src/memory paths,
7-type L1 taxonomy, owner-scoped REST routes, five MCP read tools, real
CLI commands, distillation selector/DLQ semantics). Replace the stale
ENVIRONMENT.md memory/Qdrant tables with the real env surface
(OMNIROUTE_MEMORY_CAPTURE/INJECTION_ENABLED + MEMORY_DISTILLATION_* +
MEMORY_MIGRATIONS_DIR) and mirror it in .env.example so the env-doc-sync
gate agrees. Update REPOSITORY_MAP and the CLAUDE.md project table, and
retire the superseded Qdrant links in cluster-decisions.

* test(memory): migrate v3-memory tests to the four-layer runtime

Retire the legacy mcp-memory-tools-strategy file (strategy mapping died
with src/lib/memory/settings.ts) and the v3 engine/embedding/vector/qdrant
suites; migrate chat-pipeline, memory-pipeline, performance-regression,
cli-memory-commands, memory-tools, route-scope and management-auth tests
onto the live src/memory runtime and the owner-scoped REST surface;
rewrite memory-four-layer-schemas against the shipped 7-type taxonomy;
harden the worker-lifecycle lease test against parallel-run starvation;
add dashboard/UI coverage for the layer tabs, distillation settings,
skills tab and cutover cleanup; drop the deleted v3 include from the
vitest config and cover the colocated dashboard memory tests.

* chore: retrigger CI

* chore: verify CI provisioning after first run

* feat(memory): finish Map distillation usage + L0 telemetry

Closes Map issue #2 by adding the missing owner-scoped surfaces
that the four-layer hard cutover was still exposing:

  * GET /api/memory/distillation-model/usage — token + USD
    accounting for the resolved owner, with aggregate totals and
    a sanitized error envelope.

  * GET /api/memory/l0/status — masked, owner-scoped capture
    telemetry (success/failure counters + last-event timestamps).
    NEVER returns message content, prompt tokens, secrets, or
    correlation payloads.

  * DistillationSettingsTab usage card (token/USD totals, recent
    model+task rows, empty/error states).

  * L0Tab capture status card (success/failure counts, last event
    timestamps, masked failure category).

  * New migration 010_l0_capture_telemetry.sql + l0CaptureTelemetry
    repository that owns the SQL and the failure-category allowlist.

  * Retention SQL moved from integration/retentionCleaner.ts into
    src/memory/db/repositories/retention.ts so the persistence seam
    stays inside src/memory/db/ (no raw SQL outside the repository).

TDD evidence:
  * tests/integration/memory-four-layer-routes.test.ts:
    - distillation usage listing is owner-scoped and returns
      aggregate totals (NEW)
    - L0 capture status is owner-scoped and exposes masked
      aggregate counters only (NEW)
    - 13/13 routes pass (12 prior + 2 new)
  * tests/unit/memory-four-layer-standalone-service.test.ts:
    - distillation usage listing remains scoped to the caller
      owner (NEW)
    - getL0CaptureStatus returns the masked counters for the
      caller owner only (NEW)
    - 9/9 service tests pass (7 prior + 2 new)
  * tests/unit/memory/retention.test.ts: exact-cutoff regression
    added (cutoff row kept, one-ms-older row removed); 7/7 pass.
  * tests/unit/memory-openapi-auth.test.ts: documented memory path
    count bumped 11 -> 13 (new usage + status endpoints).
  * tests/unit/DistillationSettingsTab.test.tsx: usage card
    rendering + totals assertion (NEW).
  * tests/unit/L0Tab.test.tsx: masked status card assertion (NEW).

Verification:
  * npm run typecheck:core — clean
  * npm run lint — zero new errors in our diff paths
  * node --import tsx/esm --test on memory suites — 100% pass
  * npx vitest run src/app/(dashboard)/dashboard/memory — 24/24
  * scripts/docs/gen-openapi-module.mjs — regenerated
  * scripts/check/check-docs-sync.mjs — PASS
  * scripts/check/check-openapi-coverage.mjs — PASS
  * scripts/check/check-openapi-security-tiers.mjs — PASS

Boundary compliance:
  * No raw SQL outside src/memory/db/repositories/
  * Telemetry MUST NOT block chat (failure path is fire-and-forget)
  * L0 message content + tokens + secrets + correlation payloads
    are never persisted in the telemetry table
  * L0 content policy unchanged — distillation inputs remain the
    single place where sanitization happens

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
jinnnyang pushed a commit to jinnnyang/OmniRoute that referenced this pull request Aug 28, 2026
This checkout is now an independent fork (jinnnyang/OmniRoute) whose mainline
is the `less` branch. Remove the rules that only made sense while tracking
diegosouzapw/OmniRoute, and keep every code-quality rule intact.

- AGENTS.md: drop the "Upstream contributions", "Base-green check" and
  "Worktree isolation" sections; rewrite Git Workflow around `less`.
- AGENTS.md: drop Hard Rules cheaper-inference#19 (worktree), cheaper-inference#21 (release-freeze) and
  cheaper-inference#22 (cross-session safety); renumber PII opt-in to cheaper-inference#19 and rewrite the
  `_tasks/` rule as cheaper-inference#20 (plain local-only gitignored dir, no remote).
- AGENTS.md: #4 now bans committing broken code to `less`; cheaper-inference#16 credits
  jinnnyang; Testing/Review focus lose their PR-flow and VPS assumptions.
- CLAUDE.md: merge the worktree/cross-session sections into one "Git" section
  that bans worktrees, drop the base-green section, de-hardcode paths.
- GEMINI.md/CLAUDE.md: 23 Hard Rules -> 20.
- GitNexus detect_changes base_ref: release/v3.8.50 -> less.
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
… Rule cheaper-inference#20 (cheaper-inference#5159)

Integrated into release/v3.8.39. PII opt-in regression guard + Hard Rule cheaper-inference#20; rebased to strip base-drift (+81/-1). 5/5 guard tests pass; flip-proof verified.
fouadSalkini added a commit to fouadSalkini/OmniRoute that referenced this pull request Sep 26, 2026
…dacted

Turns were read before the post-call PII masker and the streaming PII transform,
so /v1/me/sessions/{id}/messages could serve text the client received redacted.
saveRequestUsage now runs the same passes over the stored turn:

- user text: request PII masking (PII_REDACTION_ENABLED, processPII), which the
  upstream saw, and credential redaction;
- assistant text: response PII sanitization (PII_RESPONSE_SANITIZATION and its
  mode, sanitizePII), and credential redaction.

All of them stay opt-in (Hard Rule cheaper-inference#20): with every flag off the turn is stored
verbatim. The credential switch is now one exported helper shared with the masker.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
* chore(release): open v3.8.39 development cycle

* docs(changelog): backfill 5 v3.8.38 bullets merged after release finalize

These PRs squash-merged into release/v3.8.38 between the CHANGELOG finalize
(4abac1642) and the merge-to-main (6d4b6e0ba), so they shipped in the v3.8.38
tag but had no bullet:

- feat(compression): Ionizer engine (lossy JSON-array sampling + CCR) (cheaper-inference#5148)
- fix(sse): preserve non-stream reasoning fields (cheaper-inference#5155, @rdself)
- fix(i18n): add missing English UI labels (cheaper-inference#5153, @rdself)
- test(combo): gated live smoke (cheaper-inference#5151) + release-expectations refresh (cheaper-inference#5150, @KooshaPari)

(cheaper-inference#5129 exact-host Anthropic baseUrl is already covered by the cheaper-inference#5130 bullet — same CodeQL cheaper-inference#674.)
Synced 41 i18n CHANGELOG mirrors.

* feat(compression): TOON best-of-N candidate encoder + encoder A/B table (cheaper-inference#5163)

Integrated into release/v3.8.39. TOON best-of-N candidate encoder (GCF default, fail-open). 17/17 unit tests pass on merge result; CI reds were base-stale + Quality Ratchet DRIFT.

* fix(zenmux): normalize vendor-prefixed GLM system roles (cheaper-inference#5158)

Integrated into release/v3.8.39. ZenMux vendor-prefixed GLM system-role normalization; 12/12 role-normalizer tests pass on merge result. CI reds base-stale.

* [codex] fix xAI OAuth test and reasoning effort (cheaper-inference#5157)

Integrated into release/v3.8.39. xAI reasoning-effort normalization (max/xhigh→high) + OAuth test config; 46/46 xai-translator tests pass on merge result. CI reds base-stale.

* docs(i18n): add Traditional Chinese (zh-TW) README and update zh-CN to latest (cheaper-inference#5162)

Integrated into release/v3.8.39. Traditional Chinese (zh-TW) README + zh-CN refresh; docs-only.

* test(security): guard PII redaction stays opt-in (default off) + Hard Rule cheaper-inference#20 (cheaper-inference#5159)

Integrated into release/v3.8.39. PII opt-in regression guard + Hard Rule cheaper-inference#20; rebased to strip base-drift (+81/-1). 5/5 guard tests pass; flip-proof verified.

* test(combo): deterministic context-relay universal-handoff coverage (closes phase-2 TODO) (cheaper-inference#5168)

Integrated into release/v3.8.39. Deterministic context-relay universal-handoff coverage (3 tests); 3/3 pass on merge result.

* docs(i18n): full sync zh-TW and zh-CN README with canonical English v3.8.39 (cheaper-inference#5171)

Integrated into release/v3.8.39. Full zh-TW docs tree + zh-CN sync with canonical English v3.8.39; docs-only.

* fix(serve): honour HOSTNAME from .env instead of hardcoding 0.0.0.0 (cheaper-inference#5134) (cheaper-inference#5170)

Integrated into release/v3.8.39. HOSTNAME env override in serve (cheaper-inference#5134) + regression test (4/4, TDD flip-proof verified).

* fix(sse): resolve nameless deepseek-web tool blocks via parameter-schema match (cheaper-inference#5154) (cheaper-inference#5173)

Integrated into release/v3.8.39. Schema-based nameless deepseek-web tool-block resolution (cheaper-inference#5154); 6/6 tests pass on merge result (incl. ambiguous/no-match negatives + named-tag no-regression).

* fix(sse): normalize array user content for Command Code to avoid upstream 400 (cheaper-inference#5166) (cheaper-inference#5174)

Integrated into release/v3.8.39. Normalize array user content for Command Code (cheaper-inference#5166, user-array/400 symptom); 4/4 tests pass on merge result.

* fix(sse): defer </think> close so it never leaks before tool_calls (cheaper-inference#5123) (cheaper-inference#5175)

Integrated into release/v3.8.39. Defer </think> close so it never leaks before tool_calls (cheaper-inference#5123); 4/4 tests pass (incl. cheaper-inference#4633 no-regression). CHANGELOG synced to keep all 3 v3.8.39 fixes.

* fix(dashboard): use amber for home update-step warning icon (cheaper-inference#5176)

Integrated into release/v3.8.39. Amber for home update-step warning icon; 1/1 UI test.

* fix(api): LAN/Tailscale dashboard — host-aware CSP + GET-exempt version route + combo field errors (cheaper-inference#5083) (cheaper-inference#5177)

Integrated into release/v3.8.39. Host-aware CSP (ReDoS/injection-safe host validation) + GET-exempt /api/system/version (POST/spawn stays LOCAL_ONLY, exact-match safe-methods-only) + COMBO_002 firstField. 44/44 tests + route-guard membership gate green. CHANGELOG synced to keep all 4 v3.8.39 fixes.

* fix(api): replace cheaper-inference#5083 global middleware CSP with declarative ws: scheme (cheaper-inference#5083)

Follow-up to PR cheaper-inference#5177 (merged): that version implemented the LAN-CSP fix (Bug 1)
with a new global `src/middleware.ts` + `src/server/csp.ts`, which contradicts the
project's documented architecture — 'No global Next.js middleware — interception is
route-specific' (CLAUDE.md / AGENTS.md) — and was merged unverified (middleware vs
next.config header precedence was never confirmed in a real build).

This replaces that approach with the minimal, declarative equivalent:
  • next.config.mjs: connect-src now permits the bare `ws:` scheme (symmetric with the
    bare `wss:` already allowed) so the dashboard can reach its own Live WS server from
    a LAN/Tailscale host. No middleware.
  • Removes src/middleware.ts, src/server/csp.ts, and tests/unit/csp-host-aware.test.ts.
  • Adds tests/unit/csp-lan-ws-5083.test.ts (incl. a guard asserting src/middleware.ts
    does NOT exist, so the global-middleware approach cannot silently return).

Bugs 2 (GET-exempt /api/system/version) and 3 (COMBO_002 field surfacing) from cheaper-inference#5177
are unaffected and remain in place.

Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>

* test(combo): end-to-end quota-share DRR routing-decision coverage (matrix parity) (cheaper-inference#5179)

Integrated into release/v3.8.39. Quota-share DRR routing-decision coverage (matrix parity); 2/2 pass on merge result.

* feat(agent-bridge): graceful cert-install fallback with manual guide for containers (cheaper-inference#4546) (cheaper-inference#5178)

Integrated into release/v3.8.39. Agent-bridge graceful cert-install fallback + manual guide (cheaper-inference#4546); 6/6 tests pass on merge result.

* fix(antigravity): family-scoped quota lockout (gemini/claude buckets) (cheaper-inference#5180)

Integrated into release/v3.8.39 — family-scoped antigravity quota lockout. Rebased from v3.8.37 + validated (vitest 5/5, typecheck clean, full combo-matrix green, model-lockout 99/0). Same-model cross-account retry (chat.ts) deferred pending live antigravity VPS validation.

* fix(cli): force NODE_ENV to match dev/start run mode in custom Next server (cheaper-inference#5189)

Integrated into release/v3.8.39. Force NODE_ENV to match dev/start run mode in custom Next server; 2/2 source-scan+ordering tests pass on merge result.

* feat(compression): CCR ranged/grep/stats retrieval (ReDoS-safe, backward-compat) (cheaper-inference#5187)

Integrated into release/v3.8.39. CCR ranged/grep/stats retrieval (safe-regex ReDoS guard + length/match caps); 17/17 tests pass on merge result.

* docs(combo): sync all combo/routing-strategy docs to current state + document test coverage (cheaper-inference#5185)

Integrated into release/v3.8.39. Combo/routing-strategy docs sync; docs-only.

* fix(mcp): return 404 (not 400) for unknown Streamable HTTP session id (cheaper-inference#5169) (cheaper-inference#5191)

* fix(api): respect blocked Auto (Zero-Config) provider in /v1/models catalog (cheaper-inference#5192) (cheaper-inference#5194)

* test(combo): deterministic context-relay codex quota-handoff coverage (closes last gap) (cheaper-inference#5195)

* test(ci): wire antigravity-quota-family under test:vitest (fix test-discovery orphan) (cheaper-inference#5196)

* fix(oauth): antigravity login no longer hangs — fire-and-forget onboarding + bounded post-exchange (cheaper-inference#5193)

Antigravity OAuth hang fix (no-PKCE/no-openid + bounded post-exchange + exchange-500 fix). Includes cheaper-inference#5200 (Koosha) revert + owner rebaseline to keep documented comments. Integrated into release/v3.8.39.

* feat(oauth): remote Antigravity login via local helper + paste-credentials (cheaper-inference#5203)

Remote Antigravity login: local helper (omniroute login antigravity) + paste-credentials. Integrated into release/v3.8.39.

* fix(translator): accept Claude Messages shape in non-stream malformed-200 guard (cheaper-inference#5156)

Integrated into release/v3.8.39

* fix(cli): default dev bundler to Turbopack (16.2.x panic no longer reproduces) (cheaper-inference#5206)

Integrated into release/v3.8.39

* fix(cli): auto-calibrate server V8 heap from physical RAM (cheaper-inference#5172) (cheaper-inference#5213)

The server was spawned with a fixed --max-old-space-size=512 (omniroute serve)
or no heap flag at all (Electron), so RAM-rich boxes still OOM-crashed under
load (Ineffective mark-compacts near heap limit ~500MB) with many providers/
accounts and large model catalogs. New calibrateHeapFallbackMb(os.totalmem())
defaults the heap to ~35% of RAM clamped [512,4096], wired into serve.mjs and
electron/main.js. Explicit OMNIROUTE_MEMORY_MB still wins (cheaper-inference#2939 unchanged).

Also addresses cheaper-inference#5160 (same OOM root); cheaper-inference#5152 (docker) benefits via the same knob.

Closes cheaper-inference#5172

* fix(proxy): coalesce fast-fail health probes (cheaper-inference#5208)

Integrated into release/v3.8.39

* fix(proxy): close dispatchers when clearing cache (cheaper-inference#5202)

Integrated into release/v3.8.39

* fix(cli): raise dev server Node heap limit to 8GB to prevent OOM (cheaper-inference#5198)

Integrated into release/v3.8.39

* fix(auth): allow synthetic no-auth fallback for mimocode (cheaper-inference#5205)

Integrated into release/v3.8.39

* fix(oauth): preserve Antigravity refresh_token on empty/omitted upstream response (cheaper-inference#3850) (cheaper-inference#5214)

Google's OAuth refresh tokens are non-rotating: the refresh response usually
omits refresh_token and occasionally returns it as an empty string. The
Antigravity executor used `typeof tokens.refresh_token === "string" ? ... `
which accepts "" (typeof "" === "string") and overwrote the stored token with
empty, nulling it on first refresh. Now treats non-string OR empty as absent and
preserves credentials.refreshToken, matching refreshGoogleToken semantics.

Closes cheaper-inference#3850

* fix(responses): normalize non-array input (cheaper-inference#5204)

Integrated into release/v3.8.39

* fix(stream): normalize safety finish reasons via shared helper (cheaper-inference#5197)

Integrated into release/v3.8.39

* fix(request-logger): never render negative '(-100%)' compression badge (cheaper-inference#5201)

Integrated into release/v3.8.39

* fix(combo): reject empty responses api output (cheaper-inference#5207)

Integrated into release/v3.8.39 — combo failover now rejects empty Responses API output (validateQuality). Baseline rebaseline dropped (main-measured drift; maintainer rebaselines at release).

* fix(pwa): prefer cached navigation before offline page (cheaper-inference#5209)

Integrated into release/v3.8.39 — PWA service worker prefers cached navigation before offline page (cheaper-inference#5165).

* chore(release): v3.8.39 — 2026-06-28

* chore(release): rebaseline openapi+i18n coverage ratchet drift for v3.8.39

---------

Co-authored-by: Arthur Bodera <abodera@gmail.com>
Co-authored-by: Nguyen Minh <lop123thcs@gmail.com>
Co-authored-by: lunkerchen <labanchen@gmail.com>
Co-authored-by: Ankit <177378174+anki1kr@users.noreply.github.com>
Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com>
Co-authored-by: Ardem2025 <ardemb22@gmail.com>
Co-authored-by: backryun <bakryun0718@proton.me>
Co-authored-by: Anton <39598727+NomenAK@users.noreply.github.com>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: Wilson <pedbookmed@gmail.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…peline (cheaper-inference#7339) (cheaper-inference#7736)

Phases 3-4 of cheaper-inference#3384 (Phases 1-2 shipped DB schema + resolveInterceptSearch in
release/v3.8.47). Adds resolveInterceptFetch(provider, model) as a structural
twin of resolveInterceptSearch, and open-sse/services/webFetchInterception.ts
(mirroring webSearchFallback.ts) to rewrite a provider-native web_fetch tool
declaration into a synthetic omniroute_web_fetch function tool. The synthetic
tool call is dispatched through the existing handleToolCallExecution path
(same as omniroute_web_search) to a new web_fetch builtin skill handler that
resolves credentials and calls handleWebFetch() against /v1/web/fetch.

Strictly opt-in: with no interceptFetch DB row configured (the default), the
outgoing request body is byte-identical to pre-change behavior — no heuristic
default bypass like the interceptSearch sibling, to guarantee zero overhead
when disabled (Hard Rule cheaper-inference#20).

Also ships the dashboard toggle (owner decision, overriding the plan's
backend-only recommendation): ProviderInterceptionSection.tsx on the provider
detail page, backed by GET/PUT/DELETE /api/providers/[id]/interception-rules,
covering both interceptSearch and interceptFetch from one control.

chatCore.ts touch is minimal (frozen file): one resolver call + one
prepareWebFetchFallbackBody call mirroring the existing interceptSearch block,
plus threading provider/model into the existing handleToolCallExecution call.
QuangBlue pushed a commit to QuangBlue/OmniRoute that referenced this pull request Sep 27, 2026
sanitizePayloadPII deep-copied every log payload and resolved the
PII_RESPONSE_SANITIZATION flag from SQLite once per string, even though the
flag is off by default (Hard Rule cheaper-inference#20). The live pending-request preview runs
protectPayloadForLog on the full request body at every stage update, so in a
production inspector profile (4 x 90 s at peak) these per-string flag lookups
were ~32% of the router's main-thread busy time, blocking the event loop for
0.3-2 s on 300-800 KB requests.

Resolve the flag once per payload and return the payload untouched when it is
off; redactPayload still copies it and describes binary values, so the logged
output is unchanged. After deploying this, the same profile showed
protectPayloadForLog at 7% of busy time (was 43%) and time spent in event-loop
stalls fell from 42% to 11% at matched load.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants