Flattening Codex `type:"namespace"` tool declarations into `namespace__name`
renames the tool but cannot rewrite the contract the client already handed the
model: Codex tells it to call `to=functions.collaboration.spawn_agent`. Under
`tool_mode: code_mode_only` (gpt-5.6-*) collaboration tools are the model's only
direct channel, so the rename left it unable to address them — it fell back to
`functions.exec` and looped on meaningless shell commands until the session
collapsed, with no upstream error anywhere. Fixes#4978.
Preserving is what the upstream actually wants. OAuth egress is unconditionally
chatgpt.com/backend-api/codex/responses (base_url is read only for API-key
accounts), i.e. the party that defines the extension; codex-rs builds one
request for both WS and HTTP and falls back WS->HTTP mid-session, so these
declarations already reach that host over HTTP — as they do today via the
responses-lite carrier and the WS/HTTP bridge, neither of which ever flattened.
Namespace names cannot be allowlisted: `features.multi_agent_v2.tool_namespace`
is user-configurable (openai/codex#31864 recommends renaming it to `agents`) and
MCP/connector namespaces are generated at runtime (mcp__codex_apps__gmail).
So the default is inverted rather than extended with more names.
- Preserve namespace declarations by default for OpenAI OAuth; the new
per-account `extra.openai_responses_flatten_namespaces` restores the previous
behavior for deployments routing OAuth traffic to a relay that rejects them.
- Keep `input[].namespace` on tool-call items for OAuth non-compact requests.
The upstream requires the round-trip ("Missing namespace for function_call
'...'. Round-trip the model's function_call item with its namespace field
included."), so preserving declarations while stripping the calls would 400 on
the second turn. The item-type allowlist mirrors the existing reactive strip.
- Keep compact on its current behavior: that endpoint rejects the field outright
("Unknown parameter: 'input[894].namespace'", #4761) and there is no evidence
either way about namespace declarations there, so this change does not widen
its surface. API-key accounts are untouched — their upstream is a standard
Responses API and the reactive retry only clears one index per round trip.
- Clear the flatten mapping at the start of each Forward attempt so an account
reached through failover cannot restore responses with the previous account's
mapping.
Admin UI gets an OAuth-only toggle (create/edit/bulk) plus zh/en copy noting the
compact exception.
WSv2 egress relays upstream events verbatim without the HTTP-path
namespace restore, so flattening requests that take the WSv2 branch
would surface flattened tool names the client cannot match. Resolve
the WS transport decision before flattening and skip flattening only
when the request will actually go WSv2 (passthrough accounts return
via HTTP before the WSv2 branch and still flatten).
Also check all type assertions in responses_namespace_test.go to
satisfy golangci-lint errcheck.