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; +}