Repository navigation
Description
What issue are you seeing?
Codex Multi-Agent V2 sends child-agent instructions as the OpenAI-specific Responses item type agent_message. External Responses-compatible providers such as Ollama do not recognize this item type and cannot decrypt its encrypted_content when present.
This prevents a V2 parent from spawning a native Codex agent whose configured model_provider is external. The failure is at the provider request boundary, before the external model can correctly receive the delegated task.
A captured comparison showed:
- Working V1 Kimi/GLM requests: every input item had
type: "message", with noencrypted_content. - Failing V2 GLM request: the final input item had
type: "agent_message". Even when that captured request had no encrypted payload, Ollama Responses still could not process the non-standard item type.
External-provider routing itself works under V1. The important distinction is that the Multi-Agent version is selected and stored by the root task's first parent turn, then inherited by the whole spawned agent tree:
- If a fresh task sends its first message using a V1-capable parent model such as Luna, the root task resolves to V1 and every subsequently spawned agent also uses V1.
- Switching models after the first message does not change the already stored Multi-Agent version.
- Starting the task with a catalog-forced V2 parent such as Sol or Terra therefore keeps the whole tree on V2, including external-provider children.
This is related to, but distinct from, #33284. That issue concerns plaintext visibility to local hooks before execution. This issue concerns wire-format compatibility with non-OpenAI model providers after dispatch.
What steps can reproduce the bug?
- Configure an external Responses provider, for example an Ollama Responses endpoint, in Codex configuration.
- Configure a native agent role in
~/.codex/agents/*.tomlwhosemodel_providerpoints to that external provider. - Enable Multi-Agent and Multi-Agent V2.
- Start a fresh task using a parent model for which the model catalog selects V2, and send the first message with that model.
- Invoke
spawn_agentfor the external-provider role. - Inspect the request sent to the external Responses endpoint.
The child instruction is serialized as an input item resembling:
{
"type": "agent_message",
"encrypted_content": "..."
}or as a plaintext agent_message item. Ollama cannot decrypt the encrypted form, and its Responses implementation does not accept the agent_message item type in either form.
Disabling the multi_agent_v2 feature flag alone may not avoid the issue when the model catalog forces V2 for the selected parent model.
The current workaround requires all of the following:
- Create a new task.
- Select a V1-capable parent model such as Luna before sending the first message.
- Set
multi_agent = trueandmulti_agent_v2 = false. - Keep using that task after its first turn has resolved to V1.
This makes the entire task and every child agent use V1. It is not a provider-specific fallback, and it does not allow an OpenAI parent to retain V2 while only an external-provider child uses a compatible transport.
Relevant implementation areas appear to include:
codex-rs/core/src/tools/handlers/multi_agents_spec.rscodex-rs/core/src/tools/handlers/multi_agents_v2.rscodex-rs/protocol/src/protocol.rs(InterAgentCommunication::to_model_input_item)codex-rs/core/src/client.rs(Responses request construction)
What is the expected behavior?
Multi-Agent V2 should remain usable when either endpoint in an inter-agent route uses a non-OpenAI provider.
The requested behavior is not to downgrade external agents to V1 inside an otherwise V2 task. The root task and the entire agent tree should retain V2 lifecycle and coordination semantics. Only the wire representation at a non-OpenAI provider boundary should be adapted.
A provider-aware compatibility behavior would be:
- Preserve the existing encrypted
agent_messagepath for OpenAI-to-OpenAI routes. - For a route involving a non-OpenAI provider, keep the task text plaintext and serialize the outbound instruction as a standard Responses
messageitem, for example:
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "..."
}
]
}- Apply the same provider-aware conversion to initial spawn instructions, follow-up/
send_messageinstructions, and child return messages as needed. - Preserve V2 lifecycle behavior such as task identity, graph state, wait, follow-up, and completion.
- Leave existing OpenAI-provider behavior unchanged.
- Allow catalog-forced V2 parents such as Sol or Terra to spawn explicitly configured external-provider roles without requiring the root task to start under Luna/V1.
Additional information
The compatibility decision should be made using the actual caller and recipient provider identities, not only the parent model name.
The current Luna-first workaround demonstrates that the configured external provider and native agent role can function when the payload uses standard V1 message items. It also demonstrates why that workaround is insufficient: it disables V2 for the entire root/child tree rather than making V2 interoperable with external Responses providers.
Activity
sfourdrinier commentedon Jul 19, 2026
I've also hit the same problem.
jingx8885 commentedon Jul 22, 2026
I traced this to two separate OpenAI-specific assumptions in Multi-Agent V2 and have a tested implementation available for maintainer review.
Root cause
messagearguments with the Responsesencryptedextension. A portable Responses provider cannot produce or consume that encrypted field.agent_messageinput item. External Responses-compatible providers generally accept only standardmessageitems.The second point is why merely disabling client-side encryption is insufficient.
Proposed compatibility mode
The branch adds an opt-in setting:
When omitted, it defaults to
true, so the existing OpenAI/Codex path is unchanged.When disabled:
spawn_agent,send_message, andfollowup_taskexpose ordinary string arguments without theencryptedschema extension;collaborationnamespace toagentsunless explicitly configured;agent_messageitems are converted to portable Responsesmessage/user/input_textitems;An explicit mode is needed because the parent model must see the tool argument schema before it chooses an
agent_type; the recipient provider is not yet known when that schema is generated.Implementation: https://github.com/jingx8885/codex/tree/codex/portable-inter-agent-messages
Commit: jingx8885@20f3bcc433
Validation
encryptedmarker;messageitem;agent_messageitem reaches the external provider.codex-features: 29/29 tests passed.codex-core: 2,978/3,035 tests passed. Of the remaining 57, 56 were test-environment failures caused by helper binaries not having been built at the time, and one unrelated image-budget test timed out. The helper binaries were subsequently built successfully.just fix -p codex-coreandjust fmtcompleted successfully.codex-featuresClippy run hit a transient missing Cargo fingerprint directory while rebuilding dependencies.The implementation was AI-assisted and independently reviewed against the final diff and targeted behavior.
Would a Codex maintainer confirm whether this opt-in compatibility approach matches the intended architecture and, if so, invite a pull request? I have not opened one because
docs/contributing.mdstates that uninvited external PRs are closed without review.ysxk commentedon Jul 23, 2026
Related desktop corroboration (external Responses 422)
Alongside the encrypted-assignment / Chat Completions case in #34833, we also hit the external Responses item-type failure on MultiAgentV2:
gpt-5.6-solon Codex desktop (CLI0.145.0-alpha.30)skills_openapiwithgrok-4.5gpt-5.6-solunblocked the work; restoringgrok-4.5reintroduces the failure.This matches the “V2 sends
agent_message/ encrypted OpenAI-specific items to external Responses providers” diagnosis here. Provider-aware conversion to a standardmessageitem at non-OpenAI boundaries (while keeping V2 lifecycle) would cover this path.nicezic commentedon Jul 24, 2026
Confirmed on Codex 0.144.6 / WSL2 with an OpenAI gpt-5.6-sol parent and a MiniMax-M3 child using an external Responses provider. Direct codex exec against MiniMax succeeds. Native V2 spawn and follow-up authenticate and complete, but return an empty result. The child rollout contains an empty visible Payload: followed only by encrypted_content. This reproduces the provider-boundary issue with MiniMax’s Responses-compatible endpoint.
CCanxue commentedon Aug 3, 2026
Confirmed on codex-cli 0.146.0 with DeepSeek (
wire_api = "responses",multi_agent_version = "v2"), with wire captures:agent_messageitem whose payload lives inencrypted_content; DeepSeek drops that block, so the child sees only an emptyPayload:header.agent_messagecontent is rewritten to plaintextinput_text, DeepSeek still ignores the item in most runs and the child replies that it has no task.usermessage is consumed reliably (spawn ->PONG, followup_task ->PONG2).This suggests upstream should render agent-message deliveries as plain user messages (or V1-style
UserInput) for non-OpenAI providers, not just foldencrypted_contentinto theagent_messageitem. Our local patch does exactly that: https://github.com/CCanxue/codex-deepseek-subagent-fixboogie-ben commentedon Aug 5, 2026
Working local workaround: proxy that rewrites
agent_message→messagefor non-OpenAI parentsEnvironment: macOS 15.2 (arm64), Codex Desktop 26.730.61639, bundled
codex-cli 0.147.0-alpha.1.2, custom DeepSeek provider (wire_api = "responses",multi_agent_version = "v2", modeldeepseek-v4-flash).Fully tested locally: subagents spawn, receive their task, execute it, and return results. Normal turns and SSE pass through unchanged.
Why it works: for a non-OpenAI parent,
encrypted_contentis plaintext — Codex stores the rawspawn_agentmessage there (InterAgentCommunication::new_encrypted()inprotocol.rs) and never rewritesagent_messageitems for non-OpenAI providers (client.rs). DeepSeek ignoresagent_messageitems, so the task never reaches the child. The proxy converts them to standardmessage/user items. Real Fernet ciphertext (OpenAI parents) always starts withgAAAAA; the proxy passes those through untouched.Setup:
node proxy.mjs # listens on 127.0.0.1:8787 -> https://api.deepseek.comRestart the session. The proxy is a single zero-dependency Node script — run it manually, wrap it as a Codex plugin, or manage it as an auto-start background process (e.g. launchd/systemd).
Two cases matched by
agent_message: ① parent → childNEW_TASKin the child's requests (critical, must be converted); ② child → parentFINAL_ANSWERreplayed in the parent's history on every later request (converted too, harmless).Limitation: works for any non-OpenAI parent → non-OpenAI child provider combination (DeepSeek, MiniMax, Kimi, etc.); OpenAI-parent ciphertext (#36376) cannot be decrypted locally.
Related:
0.147.0-alpha.4: OpenAI parent still sends encrypted V2 task to non-OpenAI child after #35845 #36376Proxy script (Node.js, zero dependencies):
CCanxue commentedon Aug 6, 2026
Thanks for sharing. The
gAAAAAFernet heuristic matches our finding: for non-OpenAI parents the value stored inencrypted_contentis locally plaintext, while OpenAI-parent assignments are real ciphertext that cannot be decrypted locally.We went with a core patch instead of a wire proxy: for non-OpenAI providers, V2 spawn / followup_task / send_message are delivered as plain
UserInput(standard user messages), so no request rewriting is needed for the non-OpenAI parent topology. Verified end-to-end with deepseek-v4-flash (spawn ->PONG, followup ->PONG2). The proxy is a nice zero-dependency alternative for users who cannot rebuild the CLI. Patch and evidence: https://github.com/CCanxue/codex-deepseek-subagent-fixbtmorgeson commentedon Aug 13, 2026
Sanitized corroboration from Windows with codex-cli 0.147.0 and a custom OpenAI-compatible Responses provider:
multi_agent_version: "v2"even thoughfeatures.multi_agent_v2 = false; the selected model catalog/session assignment controlled the active transport.spawn_agentcreated the child thread successfully.agent_messagewith aninput_textheader plus anencrypted_contentblock containing the task.invalid request body: Invalid 'input': value did not match any expected variant.encrypted_content, so changingfork_turnswould not remove this incompatibility.We have disabled both
multi_agentandmulti_agent_v2and added a fresh-process drift gate until provider-aware standardmessagedelivery or an explicit fail-fast compatibility check is available. No credentials, task text, repository paths, or ciphertext are included here.verex-technik commentedon Oct 18, 2026
Reproduction: GPT-6.1 Sol parent → Ollama/MLX subagents receive unreadable encrypted handoff
I can independently reproduce the issue described here using the Codex desktop app on Apple Silicon with Ollama and two different local MLX models.
Environment
gemma4:31b-mlxandqwen3.8:27b-mlx/v1/responses)Gemma 4 has 31.7B parameters, NVFP4 quantization, and a 262,144-token context window.
Minimal reproduction
Start a fresh Codex conversation with GPT-6.1 Sol as the parent.
Request exactly one subagent using
gemma4:31b-mlx.Pass only the following task:
Reply with exactly GEMMA_HANDOFF_OK_1842.Explicitly prohibit shell commands and file operations.
Use
fork_turns: "none"to avoid inheriting conversation history.Observe the spawned subagent's activity.
Repeat with
qwen3.8:27b-mlx.Actual behavior — Gemma 4
The subagent starts successfully but does not execute the simple requested task.
Instead, it:
gAAAAA...-prefixed string.The same abnormal behavior occurred in an earlier, unrelated delegation attempt.
Actual behavior — Qwen
The Qwen subagent explicitly reports:
It subsequently identifies the payload as a Fernet-style
gAAAAA...token and attempts to locate decryption mechanisms in its environment, including Codex configuration directories.It therefore also fails to follow the original no-tools instruction.
Control tests — direct execution works
Both local models work correctly when selected directly as the main model in the Codex App.
Additional tests with Gemma 4 succeeded:
/api/chat: valid structured function call./v1/responses: validfunction_callwith correct JSON arguments.Qwen also completed a substantial read-only project analysis when used directly.
This strongly suggests that the failure occurs during cross-provider subagent task delivery, rather than basic model inference or ordinary tool calling.
Expected behavior
The delegated task should reach the local subagent as readable text.
The subagent should return:
GEMMA_HANDOFF_OK_1842No filesystem inspection, shell commands, or attempted payload decryption should be necessary.
If the task cannot be delivered, Codex should return an explicit compatibility or protocol error rather than allowing the subagent to operate without a readable assignment.
Suspected cause
This appears consistent with the Multi-Agent V2
agent_message/encrypted_contentcompatibility issue documented here.The screenshots show the
gAAAAA...ciphertext pattern and Qwen's explicit report that the handoff is unreadable.I have not independently captured the raw provider request for this particular run, so I cannot establish whether the encryption originates in the parent tool arguments or the Codex delegation transport.
Additional concern
The failure is not merely an empty answer: local subagents may attempt unrelated filesystem and configuration inspection while trying to reconstruct the missing assignment.
A fail-closed error at the provider boundary would be preferable.
Available evidence
No raw ciphertext, credentials, secrets, or private repository contents are included.
Could the maintainers confirm whether provider-neutral plaintext delivery is planned for OpenAI-parent → external Responses-provider subagents, including Ollama/MLX models?