* fix: tool description null sanitization, clipboard HTTP fallback fixes
T10 - Sanitize tool.description null in claude-to-openai translator
- claude-to-openai.ts: tool.description defaults to empty string when null/undefined
- claude-to-openai.ts: filter out tools with empty/missing names
- Prevents 400 validation errors on providers like NVIDIA NIM (issue #276)
T11 - Fix copy buttons to work on HTTP/non-HTTPS deployments
- Add src/shared/utils/clipboard.ts with HTTPS+HTTP (execCommand) dual fallback
- Migrate useCopyToClipboard.ts to use shared utility
- Migrate ConsoleLogViewer.tsx, RequestLoggerV2.tsx to shared utility
- Migrate HomePageClient.tsx, endpoint/page.tsx, GetStarted.tsx
- Migrate DefaultToolCard.tsx to shared utility
- Fixes copy buttons when OmniRoute runs behind HTTP proxy (issue #296)
T02 - Verified SSE [DONE] sentinel handling already correct
- sseParser.ts filters [DONE] on line 13 (no change needed)
- stream.ts uses doneSent flag to prevent duplicate sentinel
- bypassHandler.ts correctly separates streaming/non-streaming responses
Issue triage comments posted to #340, #341, #344
* feat: DB read cache + Accept header stream negotiation (T09/T01)
T09 - In-memory TTL cache for hot DB read paths
- Add src/lib/db/readCache.ts with TTL cache (5s settings/connections, 30s pricing)
- Eliminates redundant SQLite reads on concurrent requests
- Integrate invalidation in settings.ts updateSettings() and updatePricing()
- Integrate invalidation in providers.ts create/update/delete operations
- Export getCachedSettings, getCachedPricing, getCachedProviderConnections,
invalidateDbCache via localDb.ts for consumer migration
- Cache auto-busts on any write, preserving data consistency
T01 - Accept header stream negotiation
- src/sse/handlers/chat.ts: detect Accept: text/event-stream header
- Override body.stream=true when Accept header indicates streaming client
- Enables curl, httpx and SDK clients that use HTTP headers instead of JSON
body field to trigger streaming responses
- Logs Accept override at DEBUG level for observability
* fix: auto-advance quota window on expiry to prevent stale blocking (T08)
T08 - Quota Window Rolling Auto-Advance
- quotaCache.ts: add windowDurationMs field to QuotaCacheEntry interface
(optional field that callers can set when they know the window duration)
- Add advancedWindowResetAt() helper: if entry.nextResetAt is in the past,
eagerly returns { exhausted: false } so requests are unblocked immediately
- isAccountQuotaExhausted() now uses advancedWindowResetAt() instead of
the previous inline date check, and optimistically clears entry.exhausted
flag to avoid re-checking the same stale entry on the next request
Before: exhausted accounts with an expired resetAt would wait up to 5
minutes for the background refresh before accepting new requests.
After: the first request after resetAt passes will be immediately accepted
and will trigger a quota refresh on the next background tick.
* feat: manual OAuth token refresh UI (T12)
T12 - Manual Token Refresh UI
- Add POST /api/providers/[id]/refresh endpoint
- Validates connection exists and is OAuth type
- Calls getAccessToken() (same helper used in auto-refresh)
- Persists new credentials via updateProviderCredentials()
- Returns { success, expiresAt, refreshedAt } on success
- Update providers/[id]/page.tsx
- handleRefreshToken() with loading state (refreshingId)
- Pass onRefreshToken + isRefreshing props to ConnectionRow
- ConnectionRow: add optional onRefreshToken/isRefreshing props
- ConnectionRow: tokenMinsLeft state via lazy init (Date.now() in
getter fn, not in render body - satisfies react-hooks/purity)
- Token expiry badge: red 'expired' | amber '~Xm' (<30min) | hidden
- 'Token' button (amber) next to 'Retest' for OAuth connections
- Add en.json i18n: tokenRefreshed, tokenRefreshFailed
* Initial plan
* feat: integrate wildcardRouter into model alias resolution (T13)
T13 - Wildcard Model Routing
- Import resolveWildcardAlias from wildcardRouter.ts into model.ts
- In getModelInfoCore(), after exact alias check fails, try glob wildcard
alias matching (e.g., 'claude-sonnet-*' alias → 'anthropic/claude-sonnet-4')
- Returns { provider, model, extendedContext, wildcardPattern } on match
- Falls back to MODEL_TO_PROVIDERS lookup and openai default as before
* fix: clipboard cleanup and tool validation
* feat: media page UX + T04 playground uploads + T03 HuggingFace/Vertex AI
Media Page (MediaPageClient.tsx):
- Render images inline (img tags from b64_json or url)
- Show transcription as plain readable text (not raw JSON)
- Amber banner for credential errors with link to /dashboard/providers
- Detect empty transcription result and show credentials hint
- Provider credential hint below selector for non-local providers
- Extended provider/model lists: HuggingFace, Qwen TTS, Inworld, Cartesia, PlayHT, AssemblyAI
T04 - Playground File Uploads (playground/page.tsx):
- Audio file upload panel for transcription endpoint (multipart/form-data)
- Image upload panel for vision models (gpt-4o, claude-3, gemini, pixtral, llava...)
- Auto-detect vision models by name heuristic
- Inject uploaded images as base64 image_url in chat messages
- Inline image rendering for image generation results
- Readable text view for transcription results with copy button
- Preview thumbnails for attached images with individual remove
T03 - HuggingFace + Vertex AI Providers:
- HuggingFace: frontend providers.ts + backend providerRegistry.ts
Uses HuggingFace Router OpenAI-compatible endpoint
- Vertex AI: frontend providers.ts + backend providerRegistry.ts
Uses gemini format with generateContent API (urlBuilder fallback)
T07 - API Key Round-Robin: VERIFIED already implemented in auth.ts
fill-first, round-robin, p2c, random, least-used, cost-optimized strategies
* feat: T05 task-aware routing + fix#302 stream override + fix#73 claude provider fallback
T05 - Task-Aware Smart Routing:
- New open-sse/services/taskAwareRouter.ts:
Detects 7 task types: coding, creative, analysis, vision, summarization,
background, chat from system/user message content and images
Configurable taskModelMap per task type, stats tracking
applyTaskAwareRouting() integrates with existing chat pipeline
- New src/app/api/settings/task-routing/route.ts:
GET/PUT/POST API for task routing config + reset-stats + detect action
Persists config via updateSettings('taskRouting')
- Integration in src/sse/handlers/chat.ts:
applyTaskAwareRouting() called after policy enforcement, before combo resolve
Logs task type detection and model overrides
Fix#302 - OpenAI SDK stream=False drops tool_calls:
- src/sse/handlers/chat.ts T01 Accept header negotiation:
Changed condition from 'body.stream !== true' to 'body.stream === undefined'
OpenAI Python SDK sends 'Accept: application/json, text/event-stream' in every
request, even stream=False — the old code was incorrectly forcing stream=true,
causing tool_calls to be dropped from non-streaming responses
Fix#73 - Claude Haiku routed to OpenAI provider instead of Antigravity:
- open-sse/services/model.ts getModelInfoCore():
Added heuristic prefix detection before the blind 'openai' fallback:
claude-* models → antigravity (Anthropic) provider
gemini-*/gemma-* models → gemini provider
Closes: #73, partially addresses #302
* fix: token counts 0 (#74), model import dup (#180), model route fallback (#73)
fix#74 - Token counts always 0 for Antigravity/Claude streaming:
- open-sse/utils/usageTracking.ts extractUsage():
Add handler for 'message_start' SSE event which carries INPUT tokens in
Antigravity/Claude streaming:
{ type: 'message_start', message: { usage: { input_tokens: N } } }
This event was completely unhandled, causing ALL input token counts to be
dropped for every Antigravity/Claude streaming request
fix#180 - Model import shows duplicates with no visual feedback:
- src/shared/components/ModelSelectModal.tsx:
Added addedModelValues prop (string[]) to receive already-added model values
Models already in the combo now shown with ✓ indicator + green highlight
Makes it visually clear which models are already added vs new
- src/app/(dashboard)/dashboard/combos/page.tsx:
Pass addedModelValues={models.map(m => m.model)} to ModelSelectModal
* Harden clipboard UX and Claude tool normalization (#360)
* Initial plan
* chore: plan updates for clipboard and translator fixes
* fix: clipboard cleanup, copy feedback, and claude tool validation
---------
Co-authored-by: openai-code-agent[bot] <242516109+Codex@users.noreply.github.com>
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: openai-code-agent[bot] <242516109+Codex@users.noreply.github.com>
- fix(oauth): restore iFlow clientSecret default — was empty string, now uses the valid public key (#339)
- fix(mitm): compile src/mitm/*.ts to JS during prepublish so server.js exists in npm bundle (#335)
- fix(gemini-cli): graceful projectId fallback — warn + empty string instead of hard 500 error (#338)
- feat(models): add gpt5.4 to Codex; add claude-sonnet-4, claude-opus-4.6, deepseek-v3.2, minimax-m2.1, qwen3-coder-next, auto to Kiro (#334)
- fix(electron): sync electron/package.json version to 2.3.13 (#323)
- feat(scoring): add tierPriority (0.05) to ScoringWeights Zod schema and combos/auto API route
- cline.ts: add decodeURIComponent before base64 decode to handle URL-encoded codes
- cline.ts: populate name = firstName+lastName || email in mapTokens
- oauth/exchange route: normalize name=email for all providers on exchange/poll/poll-callback
- Fixes: accounts showing Account #ID instead of email in providers dashboard
- Add Dashboard > Debug > Agents page: grid of 14 built-in CLI agents
with installation status, version detection, protocol badges
- Add custom agent support: users can register any CLI tool via form
- Expand registry from 5 to 14 built-in agents (aider, opencode, cline,
qwen-code, forge, amazon-q, interpreter, cursor-cli, warp)
- Add 60-second detection cache to avoid repeated execSync calls
- API: GET lists all agents, POST adds custom/refreshes, DELETE removes
- Settings schema: add customAgents array field
- Fix missing themeCoral in settings namespace for all 30 languages
- Add agents sidebar key in all 30 languages
- Add agents page i18n namespace (en + pt-BR)
- Add model playground page to dashboard (provider/model/endpoint selectors, Monaco editor, streaming, abort)
- Add CLI fingerprint matching (per-provider header/body ordering to match native CLI binaries)
- Add ACP module (CLI agent discovery, process spawner, session manager, API endpoint)
- Add playground to sidebar debug section with i18n support
- Close#192 and #200 as stale (v1.8.1, no repro info)
- Add last_used_at column to provider_connections CREATE TABLE schema
- Add ensureProviderConnectionsColumns migration for existing databases
- Add last_used_at to INSERT and UPDATE SQL in providers.ts
- Add last_used_at to JSON migration INSERT in core.ts
- Add zod to serverExternalPackages in next.config.mjs (#217)
Fixes#218: Round-robin routing strategy now correctly persists
the lastUsedAt timestamp, allowing rotation between accounts.
Fixes#217: zod module is now properly included in standalone/Docker
builds by declaring it as a server external package.
Eliminate `any` usage across the codebase by introducing proper generics,
typed interfaces (StatementLike, DbLike, PromptRow, etc.), and helper
conversion functions (toNumber, toString, parseVariables). Add
comprehensive Zod validation schemas for API endpoint inputs to enforce
runtime type safety alongside compile-time checks.
Introduce `normalizeQuotaResponse` and `normalizeCombosResponse` helpers
to handle varying API response shapes (array vs wrapped object)
consistently across MCP server and advanced tools. Add optional `meta`
field to checkQuotaOutput schema and update sourceEndpoints to reflect
current API routes.
Register 8 new advanced MCP tools (simulate_route, set_budget_guard,
set_resilience_profile, test_combo, get_provider_metrics,
best_combo_for_task, explain_route, get_session_snapshot) with their
handler implementations. Add A2A smart routing skill that routes
prompts through the OmniRoute pipeline with routing explanation,
cost envelope, and resilience trace metadata.
Add stdio-based MCP server support to OmniRoute CLI, enabling AI agents
in VS Code, Cursor, Claude Desktop, and Copilot to interact with
OmniRoute tools (health, combos, quota, routing). Update help text,
gitignore vscode-extension subproject, and include MCP/A2A strategy report.
- URL-encode proxy credentials to handle special characters in passwords
- Decode URL-encoded credentials during legacy proxy migration
- Fix HTTPS proxy default port (443 instead of 8080) in frontend and migration
- Add dispatcher cache invalidation when proxy config changes
- Cast proxy port to number for SQLite INTEGER column in proxy logger
- Fix redundant .replace("//", "") in migration protocol parsing
- Copy postinstall script in Dockerfile before npm install
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, the upgrade detection logic renamed the entire DB file when it
found a schema_migrations table (from older versions), causing data loss.
Now checks if the DB actually contains data (provider_connections) before
deciding to rename. If data exists, drops only the old migration tracking
table and lets the new CREATE TABLE IF NOT EXISTS schema take over.
- Updated .env.example to include optional production ports for API and dashboard.
- Modified docker-compose files to utilize dynamic port configuration.
- Introduced runtime-env.mjs for centralized port resolution and environment variable management.
- Refactored run-next.mjs and run-standalone.mjs to leverage new runtime port handling.
- Enhanced route.ts and apiBridgeServer.ts to utilize dynamic ports for improved API integration.
- Updated OAuth configuration to reflect changes in port management.
- Added API_HOST variable to .env.example for enhanced host configuration.
- Updated package.json scripts to utilize a new run-next.mjs script for development and production.
- Introduced run-next.mjs to manage Next.js server execution with dynamic port handling.
- Enhanced route.ts files to normalize API base URLs and improve configuration checks.
- Updated apiBridgeServer.ts to use API_HOST for server initialization.
- Expanded global TypeScript definitions to include API_HOST.
- Updated .env.example to include optional split ports for API and dashboard.
- Modified docker-compose files to dynamically use the configured ports.
- Introduced a new script (run-standalone.mjs) for running the server with environment-specific ports.
- Implemented an API bridge server to handle OpenAI-compatible routes when using split ports.
- Updated README and CLI tool documentation to reflect changes in port usage and configuration.
- Enhanced various components to utilize the new port configuration, ensuring backward compatibility.
Wrap fs.mkdirSync(DATA_DIR) in try/catch so OmniRoute doesn't crash
when the user's home directory is not writable (e.g. restricted
environments, npm global install with different user).
Prints a clear warning with DATA_DIR env var recommendation.
Root cause: createProviderConnection() upsert logic for codex deduplicates
by workspaceId only (chatgpt_account_id from JWT). Two Pro Business accounts
from the same Team workspace resolve to the same workspaceId, causing the
second to silently overwrite the first.
Fix: compound uniqueness check using workspaceId + email, so different users
within the same workspace create separate connections. Includes backward-compat
fallback for old connections without email (they'll still be updated).
- Use Anthropic's registered redirect_uri (platform.claude.com/oauth/code/callback) instead of dynamic server URL
- Add missing OAuth scopes: user:sessions:claude_code, user:mcp_servers (5 total, matching Claude CLI)
- Make redirect_uri configurable via CLAUDE_CODE_REDIRECT_URI env var
- Override redirectUri in both buildAuthUrl and exchangeToken to use config value
Next.js inlines process.env at build time, causing clientSecret defaults to
be baked as empty strings. Desktop/CLI OAuth secrets are not confidential
per Google's documentation.
Fixes: client_secret is missing error on VPS deployment
- Add GET/PATCH endpoints for retrieving and updating API key permissions
- Move API key management from Endpoint page to dedicated API Manager page
- Add allowed_models column to database schema for model-specific access
- Implement caching layer for improved API key validation performance
- Add modelsUrl field to RegistryEntry interface for custom models endpoints
- Configure kilocode provider with dedicated models URL (https://api.kilo.ai/api/openrouter/models)
- Expand kilocode model list from 8 to 26 models including free options:
- openrouter/free (Free Models Router)
- stepfun/step-3.5-flash:free
- arcee-ai/trinity-large-preview:free
- Additional Qwen, DeepSeek, Llama, Mistral, Grok, and Kimi models
- Update validateOpenAILikeProvider to accept custom modelsUrl parameter
- Fix models URL derivation for base URLs ending with /chat/completions
- Add kilocode config to PROVIDER_MODELS_CONFIG