fix(kilocode): downgrade json_schema response_format for DeepSeek V4 Flash (400 regression) - #10454
fix(kilocode): downgrade json_schema response_format for DeepSeek V4 Flash (400 regression)#10454benzntech wants to merge 2189 commits into
Conversation
…-three-defects fix(logging): make stream-chunk capture and request-shape logging opt-in
…r-guide-ui feat(providers): add Cookie Editor fast-path to web session credential guide
…apw#9964) Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
…windows-ci-coverage feat(ci): add windows-latest leg to test-bun-sqlite job (diegosouzapw#8468)
…gpt-endpoint-surface feat: expose full NanoGPT endpoint surface (diegosouzapw#9322)
…ni-schema-recursive-type-empty-choices feat(gemini): recursive schema type:object + empty choices interceptor (diegosouzapw#9268)
…3b-9424 # Conflicts: # config/quality/file-size-baseline.json
…iegosouzapw#9917) * fix(i18n): re-escape CC discovery-alias angle brackets for next-intl Restore diegosouzapw#8747 HTML-entity escaping for claude/<provider>/<model> in the three CC discovery-alias message keys so next-intl stops logging INVALID_MESSAGE: UNCLOSED_TAG on provider detail pages after the bulk entity-unescape regression. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(i18n): align conflict context with release * fix(i18n): cover localized CC alias placeholders --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
…r-a-capability-filter feat(core): add Layer A capability filter at router (diegosouzapw#5696)
…lector, stats, test button) (diegosouzapw#9782) * feat(i18n): modality bridge page strings (en + synced locales) * feat(dashboard): ModalityBridgeVisionTab + stats row + test button * feat(dashboard): Modality Bridge settings page with vision/audio/video tabs + sidebar entry * feat(dashboard): relocate vision bridge card to link + media-providers shortcuts * docs(guardrails): document Modality Bridge dashboard * chore: preserve upstream formatting after base merge * fix(modality-bridge): satisfy i18n quality gates * fix(i18n): preserve canonical Chinese glossary terms * fix(modality-bridge): clear dashboard quality regressions * fix(settings): use catalog-only modality labels * fix(i18n): isolate modality bridge availability copy * chore(i18n): prepare conflict-free Modality Bridge base sync * docs(modality-bridge): align migration note with dead-code decision * fix(i18n): sync capability filter locales after release merge * fix(i18n): restore canonical Traditional Chinese glossary --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…iegosouzapw#9922) Add examples/quickstart/ with minimal copy-paste scripts that let new users get a response from a local OmniRoute server in under a minute, without needing to read the full docs first. Files added: - examples/quickstart/python_requests.py (requests library) - examples/quickstart/nodejs_axios.js (axios) - examples/quickstart/curl_terminal.sh (bash one-liner) - examples/quickstart/php_curl.php (cURL extension) - examples/quickstart/README.md (table + key-settings cheatsheet) README.md: add one sub-line pointer to examples/quickstart/ below the existing zero-config curl snippet, matching the surrounding <sub> style.
…iegosouzapw#9924) * fix(memory): allow OMNIROUTE_STRICT_SYSTEM_PROVIDERS to extend the system-first provider list PROVIDERS_SYSTEM_MUST_BE_FIRST (added in diegosouzapw#6225 for diegosouzapw#6135) gates both the memory-injection placement fix and the diegosouzapw#7293 hoistLeadingSystemMessage translator fix, but was hardcoded to xiaomi-mimo/mimo only. Self-hosted deployments routing other strict backends (e.g. a custom OpenAI-compatible connection in front of a self-hosted Qwen3.5+/3.6 model, whose chat template rejects any non-leading system message the same way) had no way to opt in without forking and rebuilding the image. Adds OMNIROUTE_STRICT_SYSTEM_PROVIDERS (comma-separated, case-insensitive provider ids) to extend the built-in set at read time, mirroring the injectable-env pattern already used in src/lib/memory/typedDecay.ts. No behavior change for anyone who doesn't set it. * chore: fix changelog fragment PR number
…ta:{}}] final chunk (diegosouzapw#9938)
…egosouzapw#9959) process.uptime() returns a number, but the handler ran it through a string-only toString() helper that fell back to "unknown" for anything that wasn't already a string -- so every real uptime value was discarded, 100% reproducibly. Also stop masking upstream fetch failures as fake healthy defaults: when /api/monitoring/health, /api/resilience, or /api/rate-limits can't be reached, the tool now reports which source failed (via a new optional `degraded` field) instead of returning zeros/empty arrays indistinguishable from genuine "no data". Regression coverage dispatches through the real MCP handler (client.callTool) rather than asserting on the mock directly, since the prior mock-only tests could never have caught either bug.
…mn (diegosouzapw#9963) Both tables (002_mcp_a2a_tables.sql) store their row timestamp in created_at; the cleanup queries used WHERE timestamp < ? which does not exist, so every boot-time cleanup logged: Error cleaning mcp_tool_audit: SqliteError: no such column: timestamp Error cleaning a2a_task_events: SqliteError: no such column: timestamp and retention pruning for these two tables never ran. Fix the DELETE columns and align the log labels/doc comments with the real table names. Adds source-level invariant tests (cleanup-column-fix.test.mjs) asserting the created_at column for both tables.
…w#10101) loadEnvFile() took everything after the first '=', so 'KEY=value # note' stored the comment text as part of the value. The shipped .env/.env.example do exactly that for QUOTA_STORE_DRIVER, so every install ran with QUOTA_STORE_DRIVER='sqlite # sqlite | redis'. Consumers compare with '===' (storeFactory.ts), so a user following the annotation in .env.example and writing 'QUOTA_STORE_DRIVER=redis # ...' got driver !== 'redis', fell through to SQLite, and saw no warning — the existing 'no Redis URL configured' warning is inside the redis branch and never fires. parseEnvValue() adopts dotenv semantics: quoted values verbatim (a '#' inside quotes is data), unquoted values cut at the first whitespace-preceded '#', so 'pass#word' survives. .env.example moves the annotation to its own line. Closes diegosouzapw#10100
…iegosouzapw#10086) Probed live: /chat/completions answers HTTP 200 with no Authorization header (kilo-auto/free routed to stepfun/step-3.7-flash). A real key still raises limits, so this matches the ovhcloud/pollinations pattern of authType: "optional" rather than "apikey". Fixes diegosouzapw#10068
…pw#10090) checkNativeBinary only probed the node-gyp layout (build/Release/better_sqlite3.node), which exists only when better-sqlite3 is compiled locally. Installs that resolve a prebuilt binary — the normal case for `npm i -g omniroute` — ship prebuilds/<platform>-<arch>.node instead, so the check never found a binary and warned "better-sqlite3 native binary was not found" on every such install, next to real warnings. Probe both layouts and report both in the failure details. prebuiltBinaryName() mirrors the prebuild-install lookup, including the linuxmusl- prefix for musl-based Linux. Closes diegosouzapw#10083
…shape (diegosouzapw#10091) GET /api/openapi/spec answers with a compact catalog ({ info, servers, tags, endpoints[], schemas }) rather than an OpenAPI document, while dist/docs/openapi.yaml is a real spec. The CLI only read spec.paths, so against a live server 'openapi endpoints' and 'openapi paths' printed nothing and 'openapi validate' reported 'missing openapi/swagger version field' — with 318 endpoints sitting in spec.endpoints. Normalize both shapes through extractEndpoints()/extractPaths() and let validateBasic() accept a catalog that carries endpoints[] instead of a version field. Path Item members that are not operations (parameters, summary, description, servers, $ref) are no longer emitted as fake operations. Closes diegosouzapw#10082
…ts (diegosouzapw#10092) Three commands turned a transport failure into something that reads as real state: - `keys add` aborted on any 4xx. `/api/v1/providers/keys` is not mounted on the shipped server, so a 404 stranded the user with "HTTP 404" while the SQLite fallback directly below it — which works — was unreachable whenever the server was up. New isRouteUnavailableStatus() (404/405/501) lets the caller fall through; genuine client errors (400/401/403/409/422/429) stay fatal. - `providers test-all` reported every OAuth connection as FAILED because getProviderApiKey() throws for non-apikey connections by design — and persisted that verdict to provider_connections.test_status, marking healthy OAuth providers broken. Those connections are now skipped. An "unsupported" probe result (no recipe in PROVIDER_TEST_CONFIGS) is likewise a CLI gap, not a provider failure, so it no longer overwrites a good test_status. - `combo list` printed "No combos configured" when /api/combos returned non-2xx, which is indistinguishable from genuine emptiness. It now reports the status and exits non-zero. Refs diegosouzapw#10081
…ack (diegosouzapw#10097) loadAvailableProviders() always returned COMMON_PROVIDERS, so 'omniroute keys add <provider>' rejected ~290 of the ~296 catalog providers with 'Unknown provider' and 'providers available' under-reported the catalog by ~98%. Two independent causes, either sufficient on its own: 1. extractProviderBlocks() required 'typescript' at runtime, but it is only a devDependency — absent from every published/global install. The require failure was swallowed and the parse returned []. 2. The parser read src/shared/constants/providers.ts, which after the god-file decomposition contains only re-exports plus an empty 'FREE_PROVIDERS = {}'. Even with typescript present it yielded zero entries. Replace the AST parse with a dependency-free, string/comment-aware brace walk (these files are pure data literals) and walk src/shared/constants/providers/** instead of the barrel. An explicit catalogPath / OMNIROUTE_PROVIDER_CATALOG_PATH still wins, and the COMMON_PROVIDERS fallback still applies when no catalog is present. The walk is also hardened against an unbalanced literal (diegosouzapw#10093): it recovers the entries before the damage and terminates, instead of looping forever on a reset regex lastIndex. Closes diegosouzapw#10080
…ink + discount-first banner copy (diegosouzapw#10240) Moonshot approved a 15% extra-credits offer for new users' first top-up, attached to a dedicated tracked link issued for OmniRoute (valid through 2026-09-30). The dashboard banner and the three README API platform placements now use that link, and the banner description leads with the discount in all 43 locales, keeping the commitment made when the offer was requested. The README CTA gains the 15% mention. A code comment marks the strings to revisit after 2026-09-30 if the offer is not renewed. Co-authored-by: backryun <bakryun0718@proton.me>
…nzntech) - buildSourceUpdateScript: pnpm install/build, named autoupdate/<version> branch (patchable), pm2 restart retained, no auto-cherry-pick (Hermes applies the fix patch as a manual step) - /api/system/version source flow: same pnpm + named-branch treatment - CLI update --apply/--dry-run: source-install aware (git+pnpm, never npm install -g which would clobber the link) - bin/omniroute.mjs notifier: source-aware update hint - pnpm-workspace.yaml: fix allowBuilds placeholders (ERR_PNPM_IGNORED_BUILDS) - tests: updated source-script assertions to pnpm/named-branch - DOCUMENTATION.md: maintenance manual (update procedure, patch set, rollback, stop-patching switch)
… route The /api/settings/quota/state route imports clearProviderQuotaState(connectionId, model) but providerQuotaState.ts only defined clearProviderQuota(connectionId) — a build blocker and a bug carried in open PR diegosouzapw#10126. Adds the model-scoped variant on the (connection_id, model) keyed table.
…S (env-doc-sync gate) The source-aware CLI update (update.mjs) reads AUTO_UPDATE_GIT_REMOTE via process.env, which the check-env-doc-sync extractor now catches. Documented both auto-update knobs in .env.example + ENVIRONMENT.md.
…base - combo.ts: import getCachedProviderConnectionById via @/lib/localDb alias (the PR's ../../src/lib/localDb.js path fails to resolve in Turbopack app-route builds; every sibling uses the alias) - combo.ts: drop duplicated max-concurrency gate block (rebase merge artifact returning null instead of stopProtectedPriorityTarget) - eslint-suppressions.json: allow the barrel import in combo.ts (matches sibling concurrencyCaps/quotaExhaustionCutoff suppressions) - pnpm-workspace.yaml: allow opencode-ai build script (release base dep)
…ly dir A linked/source install's standalone dist/ ships its own package.json, so the walk-up stopped at dist/ and the git-based auto-update reported 'Not a git repository'. Now prefers a .git checkout anywhere above, falling back to the first valid package.json dir (preserving the release base's synthetic standalone-package.json guard).
- package.json: declare playwright-core + remark-gfm as direct deps (npm's hoisting had masked them; pnpm's isolated layout cannot resolve them from transitive-only positions) — both are directly imported in source - prepublish.ts: use runBuildTool() for the ChatGPT-Web (Codex) MCP bridge bundle instead of raw npx esbuild, which fails with status 127 under pnpm's isolated node_modules - assembleStandalone.mjs: remove pre-existing symlink at destination before recursive copy (ERR_FS_CP_DIR_TO_NON_DIR with pnpm's symlinked layout) - pnpm-workspace.yaml: allow opencode-ai build script - pnpm-lock.yaml: regenerated for pnpm (includes omniroute link override)
…rojectRoot Standalone chunks replace the project root with the /ROOT placeholder (outputFileTracingRoot), so the compiled default startDir (/ROOT/src/lib/system) does not exist at runtime — the git-based auto-update reported 'Not a git repository'. When the start dir is the unreachable /ROOT placeholder, walk up from process.cwd() instead (server.js chdirs to the real standalone dir).
…encies pnpm 11 reads onlyBuiltDependencies (array), not just the allowBuilds map — both packages' build scripts were silently ignored, leaving the Playwright browser download and bun binary setup unrun.
…st-before-deploy gate)
…RCE_EXHAUSTED fix)
…commits excluded)
The OpenAPI spec and the CLI (omniroute providers rotate, generated api-commands) both use PATCH /api/providers/[id], but the route only implemented PUT — PATCH requests returned 405 and key rotation via the CLI silently failed while reporting success (the DB-write fallback only catches thrown exceptions, not non-OK HTTP responses). Add a PATCH handler delegating to the PUT handler: both apply the same partial-update schema, so the semantics are identical. Regression test proves the PATCH export exists and delegates into the shared auth path; verified to fail without the fix.
…gression) kilocode's DeepSeek V4 Flash rejects ANY response_format — both json_schema AND json_object 400 with 'Invalid input: response_format' (verified live 2026-08-15 via the Hindsight fact-extraction path on kilocode/deepseek/deepseek-v4-flash). The default executor's applyJsonSchemaFallback only covered openai-compatible-* providers and only downgraded json_schema -> json_object, so kilocode forwarded the unsupported format raw. Same bug class as the opencode fix diegosouzapw#9992. For kilocode: strip response_format entirely and inject the schema (or a plain 'valid JSON only' instruction for json_object) into the system prompt. openai-compatible-* keeps the existing json_schema downgrade and json_object passthrough (they accept both). Regression tests: kilocode json_schema is stripped + schema-injected; kilocode json_object is stripped + JSON-only instruction; both verified to fail without the fix (sabotage: 2 fail). All 49 executor-default-base tests pass.
4a9af38 to
a0b2000
Compare
|
Updated the fix after live verification: kilocode's DeepSeek rejects BOTH json_schema AND json_object response_format (both 400 with Refined fix (force-pushed): for Regression tests now cover: kilocode json_schema → stripped + schema-injected system message; kilocode json_object → stripped + JSON-only instruction. Sabotage-verified (2 fail without the fix). All 49 executor-default-base tests pass. |
Problem
kilocode/deepseek/deepseek-v4-flashrejectsresponse_format: {type: "json_schema"}with HTTP 400Invalid input: response_format(verified live 2026-08-15 via the Hindsight fact-extraction path —upstream_details.param: response_format). Any client using Structured Output against kilocode's DeepSeek fails.The default executor's
applyJsonSchemaFallback(which downgrades json_schema → json_object with the schema injected into the system prompt) is gated onprovider.startsWith("openai-compatible-")only — so kilocode forwarded the unsupported format raw. Same bug class as the opencode fix in #9992, which covered the opencode/opencode-zen DeepSeek route but not kilocode's.Fix
Extend
applyJsonSchemaFallback's gate to includekilocode. json_schema is downgraded to json_object, with the JSON schema injected into the system prompt — identical to the existing openai-compatible-* behavior. Native-Structured-Output providers (openai etc.) keep the native json_schema path untouched.Regression test
tests/unit/executor-default-base.test.ts: new case asserts a kilocode request with json_schema response_format is downgraded to json_object + schema-injected system message, and the original body is not mutated. Verified to fail without the fix (sabotage run: 1 fail) and pass with it. All 48 executor-default-base tests pass.