Client or integration
Codex App
Provider or upstream service
burnopenai (OpenAI-compatible Chat Completions upstream through the openai-chat adapter)
OpenCodex version
2.19.0 (also present by static inspection on current dev at c71c827)
Endpoint or capability
/v1/responses to openai-chat / Chat Completions translation; V2 collaboration spawn_agent tool calls
Current behaviour
With V2 collaboration enabled and a routed openai-chat parent, Codex supplies a complete spawn_agent schema whose required message property carries the Responses-only annotation encrypted: true.
OpenCodex preserves required: ["message"], but also forwards the non-standard annotation unchanged in tools[].function.parameters to the Chat Completions upstream. In the reproduced provider/model combination, the upstream then emits a spawn_agent call with zero argument bytes; OpenCodex normalizes that to {}, and Codex rejects it because message is missing. No child agent is created, and the parent retries.
One archived session produced 24 spawn_agent calls: 24/24 had arguments exactly {}, and all 24 corresponding tool results reported missing field message.
The generic openai-chat path still forwards the annotation on current dev; only the Zen-specific schema sanitizer currently drops it.
Expected behaviour
At the Responses to Chat Completions adapter boundary, OpenCodex should remove the Responses-only encrypted schema annotation before serializing Chat Completions tools, while preserving:
- required: ["message"] and all other supported schema constraints;
- legitimate tool property/definition names that are literally encrypted;
- literal data under keywords such as const, default, enum, and examples.
Native OpenAI Responses passthrough should continue to preserve the annotation. The routed parent should emit a valid, non-empty message and create the child once.
Minimal redacted request or reproduction
# Environment
# OpenCodex 2.19.0, macOS, V2 collaboration
# Parent: burnopenai/gpt-5.6-sol, reasoning effort xhigh
# 1. Start a fresh Codex App task using the routed parent.
# 2. Ask it to delegate exactly one self-contained task with spawn_agent.
# 3. Observe repeated empty spawn_agent calls and no child creation.
# The incoming schema at the adapter boundary contains:
{
"type": "object",
"properties": {
"message": { "type": "string", "encrypted": true }
},
"required": ["message"]
}
# On unmodified 2.19.0, createOpenAIChatAdapter(...).buildRequest(...)
# serializes the same message schema to tools[].function.parameters,
# including encrypted:true.
A controlled forced-tool A/B used the same upstream, model, prompt, and spawn_agent tool:
message schema contains encrypted:true -> arguments are empty ("" / {})
only the encrypted annotation removed -> {"message":"Reply exactly: READY"}
The result was the same for flat and namespace: collaboration tool shapes, which rules out namespace conversion as the differentiator.
Actual response or error
function_call name=spawn_agent namespace=collaboration arguments={}
function_call_output failed to parse function arguments: missing field message at line 1 column 2
# Redacted session totals:
spawn_agent calls: 24
arguments exactly {}: 24
missing-field-message errors: 24
children created: 0
Upstream documentation
No public provider or JSON Schema specification was found for the Codex encrypted annotation. It is identified in this repository as a Responses/ChatGPT-backend-only marker introduced by upstream OpenAI Codex, and the same compatibility class was confirmed and fixed for Google/Kiro in #85 and defensively handled by the Anthropic adapter.
The concrete client requirement is that Responses-private schema metadata must not change required Chat Completions tool-call arguments during protocol translation.
Suggested mapping or implementation notes
Sanitize tool parameter schemas once in the generic openai-chat adapter before provider-specific Chat Completions formatting. Strip encrypted only when it is a schema keyword, not when it is a property/definition name or literal JSON data. The existing Anthropic implementation already distinguishes schema-name maps and literal-value keywords and may be a useful precedent.
A focused regression should assert that nested annotations are removed, required: ["message"] survives, a property literally named encrypted survives, literal encrypted data survives, and the input schema is not mutated.
I am happy to prepare a focused PR with the adapter fix and behavior-contract regression test.
Additional context and attachments
Checks
Client or integration
Codex App
Provider or upstream service
burnopenai (OpenAI-compatible Chat Completions upstream through the openai-chat adapter)
OpenCodex version
2.19.0 (also present by static inspection on current dev at c71c827)
Endpoint or capability
/v1/responses to openai-chat / Chat Completions translation; V2 collaboration spawn_agent tool calls
Current behaviour
With V2 collaboration enabled and a routed openai-chat parent, Codex supplies a complete spawn_agent schema whose required message property carries the Responses-only annotation encrypted: true.
OpenCodex preserves required: ["message"], but also forwards the non-standard annotation unchanged in tools[].function.parameters to the Chat Completions upstream. In the reproduced provider/model combination, the upstream then emits a spawn_agent call with zero argument bytes; OpenCodex normalizes that to {}, and Codex rejects it because message is missing. No child agent is created, and the parent retries.
One archived session produced 24 spawn_agent calls: 24/24 had arguments exactly {}, and all 24 corresponding tool results reported missing field message.
The generic openai-chat path still forwards the annotation on current dev; only the Zen-specific schema sanitizer currently drops it.
Expected behaviour
At the Responses to Chat Completions adapter boundary, OpenCodex should remove the Responses-only encrypted schema annotation before serializing Chat Completions tools, while preserving:
Native OpenAI Responses passthrough should continue to preserve the annotation. The routed parent should emit a valid, non-empty message and create the child once.
Minimal redacted request or reproduction
A controlled forced-tool A/B used the same upstream, model, prompt, and spawn_agent tool:
The result was the same for flat and namespace: collaboration tool shapes, which rules out namespace conversion as the differentiator.
Actual response or error
function_call name=spawn_agent namespace=collaboration arguments={} function_call_output failed to parse function arguments: missing field message at line 1 column 2 # Redacted session totals: spawn_agent calls: 24 arguments exactly {}: 24 missing-field-message errors: 24 children created: 0Upstream documentation
No public provider or JSON Schema specification was found for the Codex encrypted annotation. It is identified in this repository as a Responses/ChatGPT-backend-only marker introduced by upstream OpenAI Codex, and the same compatibility class was confirmed and fixed for Google/Kiro in #85 and defensively handled by the Anthropic adapter.
The concrete client requirement is that Responses-private schema metadata must not change required Chat Completions tool-call arguments during protocol translation.
Suggested mapping or implementation notes
Sanitize tool parameter schemas once in the generic openai-chat adapter before provider-specific Chat Completions formatting. Strip encrypted only when it is a schema keyword, not when it is a property/definition name or literal JSON data. The existing Anthropic implementation already distinguishes schema-name maps and literal-value keywords and may be a useful precedent.
A focused regression should assert that nested annotations are removed, required: ["message"] survives, a property literally named encrypted survives, literal encrypted data survives, and the input schema is not mutated.
I am happy to prepare a focused PR with the adapter fix and behavior-contract regression test.
Additional context and attachments
Checks