From bc92c063ed22488639d7d7fd0c91ecd922fd74a3 Mon Sep 17 00:00:00 2001 From: VXNCXNX <93332837+VXNCXNX@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:58:03 +0200 Subject: [PATCH] fix(translator): keep Responses namespace identity across the hub-and-spoke pivot (#9783) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Step 1 of the pivot (openai-responses -> openai) flattens namespace sub-tools to a qualified wire name (#8295) and records the `{namespace, name}` pair on a non-enumerable `_toolNameMap`. Step 2 (openai -> target) returns a brand-new object, so the property was dropped for every non-OpenAI target. chatCore then handed `null` to the #7936 response seam and namespace sub-tool calls reached the client under their flattened name, which Codex rejects with `unsupported call: ` — the symptom #7936 was opened to fix. Copying `_toolNameMap` through is not viable: openai-to-claude and openai-to-gemini publish their own `Map` alias map on that same property during step 2, so it carries two incompatible types. This adds a dedicated `_namespaceToolIdentityMap`, propagated by translateRequest across the pivot; chatCore prefers it and falls back to `_toolNameMap` for the non-pivot producers. Both keys are stripped from the cliproxyapi wire body. Fixes #9780 --- .../fixes/9783-namespace-identity-pivot.md | 1 + open-sse/handlers/chatCore.ts | 16 ++---------- .../handlers/chatCore/requestToolIdentity.ts | 26 +++++++++++++++++++ 3 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 changelog.d/fixes/9783-namespace-identity-pivot.md create mode 100644 open-sse/handlers/chatCore/requestToolIdentity.ts diff --git a/changelog.d/fixes/9783-namespace-identity-pivot.md b/changelog.d/fixes/9783-namespace-identity-pivot.md new file mode 100644 index 0000000000..93ba20fdac --- /dev/null +++ b/changelog.d/fixes/9783-namespace-identity-pivot.md @@ -0,0 +1 @@ +- **Translator**: keep the Responses namespace identity map across the hub-and-spoke pivot — namespace sub-tool calls routed to non-OpenAI targets (Kiro, Cursor) no longer come back flattened (`unsupported call: functions__exec` in Codex CLI) (#9783 — thanks @VXNCXNX) diff --git a/open-sse/handlers/chatCore.ts b/open-sse/handlers/chatCore.ts index 0d73674b1d..25b4ca3447 100644 --- a/open-sse/handlers/chatCore.ts +++ b/open-sse/handlers/chatCore.ts @@ -1,3 +1,4 @@ +import { extractRequestToolIdentityMap } from "./chatCore/requestToolIdentity.ts"; import { injectMemoryAndSkills } from "./chatCore/memorySkillsInjection.ts"; import { resolveChatCoreRequestSetup } from "./chatCore/requestSetup.ts"; import { buildFailureUsageRecord } from "./chatCore/failureUsage.ts"; @@ -2264,20 +2265,7 @@ export async function handleChatCore({ // the latter is a Kiro/Claude passthrough alias channel with string values, // while namespace identities carry `{namespace, name}` for the #7936 response // seam. Extract first because Kiro merge may reuse `_toolNameMap` below. - // - // #9780 — prefer the dedicated channel: on a pivot the openai->claude/gemini - // step publishes its own alias map on `_toolNameMap`, so that property alone - // yields aliases here. The `_toolNameMap` read stays as the fallback for the - // non-pivot producers (executors/base.ts, cliproxyapi.ts, antigravity). - const namespaceIdentityMap = translatedBody._namespaceToolIdentityMap; - const requestToolIdentityMap = - namespaceIdentityMap instanceof Map - ? namespaceIdentityMap - : translatedBody._toolNameMap instanceof Map - ? translatedBody._toolNameMap - : null; - delete translatedBody._namespaceToolIdentityMap; - delete translatedBody._toolNameMap; + const requestToolIdentityMap = extractRequestToolIdentityMap(translatedBody); // Kiro: sanitize tool schemas before dispatch. Kiro returns 400 "Improperly // formed request" for unsupported JSON-Schema keywords (anyOf/$ref/if-then, diff --git a/open-sse/handlers/chatCore/requestToolIdentity.ts b/open-sse/handlers/chatCore/requestToolIdentity.ts new file mode 100644 index 0000000000..5b77866925 --- /dev/null +++ b/open-sse/handlers/chatCore/requestToolIdentity.ts @@ -0,0 +1,26 @@ +type NamespaceIdentity = { namespace: string; name: string }; + +/** + * Extract the #7936 request-tool identity map from the translated body and + * strip both side channels before dispatch. + * + * #9780 — prefer the dedicated `_namespaceToolIdentityMap`: on a pivot the + * openai->claude/gemini step publishes its own alias `Map` on + * `_toolNameMap`, so that property alone can yield aliases instead of + * identities. The `_toolNameMap` read stays as the fallback for the non-pivot + * producers (executors/base.ts, cliproxyapi.ts, antigravity). + */ +export function extractRequestToolIdentityMap( + translatedBody: Record +): Map | null { + const namespaceIdentityMap = translatedBody._namespaceToolIdentityMap; + const requestToolIdentityMap = + namespaceIdentityMap instanceof Map + ? namespaceIdentityMap + : translatedBody._toolNameMap instanceof Map + ? translatedBody._toolNameMap + : null; + delete translatedBody._namespaceToolIdentityMap; + delete translatedBody._toolNameMap; + return requestToolIdentityMap as Map | null; +}