Repository navigation
Conversation
…olua#2224) On a fresh install or after deleting %APPDATA%\9router, rootCA.key and rootCA.crt are absent. server.js tried to readFileSync them and called process.exit(1) on failure, making the MITM server permanently broken without an obvious fix path for the user. Root cause: generateRootCA() existed in cert/rootCA.js but was never called at startup — only accessible via the UI 'Generate CA' button, which itself requires the server to already be running. Fix: - cert/rootCA.js: adds ensureRootCASync() — same logic as generateRootCA() but truly synchronous (node-forge RSA is blocking), callable at module load time in CommonJS scripts without an async wrapper - server.js: calls ensureRootCASync() before the readFileSync block; if generation fails the error is reported and the process exits cleanly; also regenerates automatically when an existing cert is expired/corrupt 5 unit tests cover: generate-when-absent, idempotent-when-valid, regenerate-when-corrupt, generate-when-partial (key only), mkdir-when-dir-missing.
…patch Kiro / CodeWhisperer rejects Claude model IDs that use dot notation for the version number (e.g. claude-sonnet-4.5) and requires dash notation (claude-sonnet-4-5). The upstream dispatch was forwarding the 9router dot-format ID directly, producing INVALID_MODEL_ID (HTTP 400) for every Claude model while non-Claude models (deepseek-3.2, glm-5) succeeded. Add toKiroModelId() to kiroConstants.js that replaces dots with dashes in Claude model IDs only, leaving all other model IDs unchanged. Apply the conversion in both OpenAI→Kiro and Claude→Kiro translators right after resolveKiroModel() strips the synthetic suffixes. Fixes decolua#2308. Related: decolua#2257.
…alls
When the MITM dashboard button is clicked multiple times rapidly, or when
auto-restart races with a manual start, two concurrent startServer() calls
could both pass the serverProcess check (process not yet spawned) and race
to spawn two MITM servers. The second spawn fails silently and leaves the
state in an inconsistent position.
Add a module-level mitmStarting boolean that is set to true at the start
of the spawn path and cleared in a finally block on all exit paths. A
second caller that arrives while mitmStarting is true receives an explicit
"MITM server is already starting" error rather than a silent race.
The flag is reset via finally{} so crashes or thrown errors during startup
never leave it permanently set, avoiding the permanent-lock scenario
described in issue decolua#2310.
Fixes decolua#2310. Related: decolua#2286.
Gemini's function_declarations schema validator rejects any property that contains multipleOf, returning: 400 Invalid JSON payload received. Unknown name "multipleOf" at 'request.tools[0].function_declarations[N].parameters.properties[K].value' multipleOf is a valid JSON Schema keyword (used to constrain numeric fields to multiples of a value) but is not part of Gemini's subset of supported schema properties. Add it to UNSUPPORTED_SCHEMA_CONSTRAINTS alongside minLength/maxLength/exclusiveMinimum/exclusiveMaximum so it is removed recursively from all function declaration schemas before the request reaches the Gemini backend. Fixes decolua#2309.
…ields before forwarding openaiResponsesToOpenAIRequest already deleted input, instructions, store, include, prompt_cache_key and reasoning when converting the Responses API format to Chat Completions. But it left client_metadata, background and truncation in the outgoing body. When Codex sends a request containing client_metadata to a non-OpenAI upstream (e.g. NVIDIA NIM), the upstream rejects with: "Validation: Unsupported parameter(s): `client_metadata`" (400) Fix: add the three missing deletes to the cleanup section. Closes decolua#2311
bloodf
pushed a commit
to bloodf/9router
that referenced
this pull request
Jul 3, 2026
bloodf
pushed a commit
to bloodf/durindoor
that referenced
this pull request
Jul 10, 2026
Translator regression asserting Responses->Chat translation drops client_metadata/background/truncation (Responses-API-only fields rejected by third-party chat providers with HTTP 400) while the plain OPENAI->OPENAI path preserves them (negative control). Logic already present in dev; locks behavior. Ported from decolua/9router#2318 @ edb20e143d
bloodf
pushed a commit
to bloodf/durindoor
that referenced
this pull request
Jul 10, 2026
Translator regression asserting Responses->Chat translation drops client_metadata/background/truncation (Responses-API-only fields rejected by third-party chat providers with HTTP 400) while the plain OPENAI->OPENAI path preserves them (negative control). Logic already present in dev; locks behavior. Ported from decolua/9router#2318 @ edb20e143d
bloodf
pushed a commit
to bloodf/durindoor
that referenced
this pull request
Jul 10, 2026
Translator regression asserting Responses->Chat translation drops client_metadata/background/truncation (Responses-API-only fields rejected by third-party chat providers with HTTP 400) while the plain OPENAI->OPENAI path preserves them (negative control). Logic already present in dev; locks behavior. Ported from decolua/9router#2318 @ edb20e143d
bloodf
added a commit
to bloodf/durindoor
that referenced
this pull request
Jul 10, 2026
* port(upstream): #2237 - salvage orphaned tool results across formats Non-lossy salvageOrphanedToolResults folds orphan tool output into user text (`[Tool result: ...]`) instead of dropping it, across messages[] (OpenAI role:tool, Claude tool_result blocks) and contents[] (Gemini/Antigravity functionResponse). Runs unconditionally in the request pipeline and after each compression stage (RTK/Headroom/PXPIPE) with fixMissingToolResponses to restore the tool-pairing invariant. Responses API function_call_output stays structurally stripped in openai-responses.js. Removes the obsolete shouldStripOrphanedToolResults gate; Gemini-family standalone functionResponse (no functionCall) is preserved. Ported from decolua/9router#2237 @ a32bda9e44 * port(upstream): #2279 - test doubled tool args collapse openai->claude Translator regression asserting the OpenAI->Claude response translator deduplicates doubled JSON tool arguments (same object emitted twice) into a single parseable input_json_delta, and emits message_stop exactly once. Logic (claudeFinishHandled finish guard + deduplicateDoubledJson) already present in dev; this locks the behavior. Ported from decolua/9router#2279 @ 1c9ad466ed * port(upstream): #2190 - test thinking stays out of visible content Translator regression asserting a Claude->OpenAI stream surfaces reasoning via the reasoning channel and never emits literal <think>/</think> text in delta.content. Logic (reasoningDelta channel, no think-tag content deltas) already present in dev; this locks the behavior. Ported from decolua/9router#2190 @ dc417f9b3f * port(upstream): #2318 - test strip Responses-only fields before forward Translator regression asserting Responses->Chat translation drops client_metadata/background/truncation (Responses-API-only fields rejected by third-party chat providers with HTTP 400) while the plain OPENAI->OPENAI path preserves them (negative control). Logic already present in dev; locks behavior. Ported from decolua/9router#2318 @ edb20e143d * fix(translator): skip salvage on native Gemini contents[] restore the Gemini-family (gemini/gemini-cli/antigravity/vertex) guard around salvageOrphanedToolResults in the translateRequest pipeline. the body still carries native contents[] at that point; salvage keys known functionCalls globally and rewrites any functionResponse whose id is not in that set into '[Tool result: ...]' text, dropping legitimate tool results before gemini->openai conversion can read them. fixes CI run 29122517702 (tests/unit/gemini-to-openai-function-response). --------- Co-authored-by: CortexOS <cortexos@localhost>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When Codex sends a
/v1/responsesrequest containingclient_metadatathrough a combo that routes to NVIDIA NIM (e.g.z-ai/glm-5.2), the upstream rejects with:{ "message": "Validation: Unsupported parameter(s): `client_metadata`", "type": "Bad Request", "code": 400 }Same class of issue can affect
backgroundandtruncation— both OpenAI Responses API-only fields that non-OpenAI upstreams reject.Root Cause
openaiResponsesToOpenAIRequest(open-sse/translator/request/openai-responses.js) converts the Responses API format to Chat Completions by spreadingbodyintoresult, then deleting known Responses-only fields. The existing cleanup deletedinput,instructions,store,include,prompt_cache_key, andreasoning— but missedclient_metadata,background, andtruncation.Because
result = { ...body }, any field not explicitly deleted is forwarded to the upstream, including provider-specific rejections.Fix
Three-line addition in the cleanup section:
Test
tests/unit/openai-responses-strip-fields.test.js— 6 tests, all pass:Closes #2311