`0.147.0-alpha.4`: OpenAI parent still sends encrypted V2 task to non-OpenAI child after #35845
Summary
0.147.0-alpha.4 still cannot deliver a MultiAgentV2 task from an OpenAI parent to a correctly selected non-OpenAI child, despite the plaintext collaboration support merged in #35845.
The OpenAI parent returns a collaboration.spawn_agent call whose message is actual ciphertext and whose encrypted_function_args field is null. Codex therefore retains encrypted delivery. The non-OpenAI Responses child is spawned with the correct role, provider, model, and multi_agent_version = "v2", but it receives an agent_message whose visible text stops after Payload: and whose assignment exists only as encrypted_content. The child reports that no task was received and does not execute it.
#35845 adds plaintext delivery only when the function call explicitly carries encrypted_function_args: []. It does not cover this cross-provider topology, because an OpenAI parent emits an encrypted message rather than the empty plaintext marker.
Environment
- macOS:
Darwin 25.5.0 arm64 arm - Codex App:
26.727.40816 - Bundled CLI:
0.146.0-alpha.9.2 - Latest alpha control: official
0.147.0-alpha.4arm64 release asset - Parent: OpenAI /
gpt-5.6-sol - Child: DeepSeek /
deepseek-v4-flash - Child provider wire API:
responses - Child model catalog: DeepSeek's current official Codex catalog metadata
- Fork mode:
none
The same result was reproduced through Codex Desktop and through a clean persistent codex exec run. The payload-level details below are from the 0.147.0-alpha.4 run.
Release provenance
#35845 was merged as commit 03edf16f0bce2c454fc9a8ddb382e9c23c114f7f. The official rust-v0.147.0-alpha.4 tag contains that commit; GitHub's commit-to-tag comparison reports the tag ahead of the merge commit with no commits behind it.
The tested binary was the official codex-aarch64-apple-darwin.tar.gz release asset. Its downloaded SHA-256 was faff3eb0d029647cc10aa8cad085018359fc622d99037e90988b58b606a68152, matching the digest published by the GitHub release API.
Relevant sanitized configuration
[agents.deepseek_flash]
description = "DeepSeek V4 Flash subagent"
config_file = "agents/deepseek-flash.toml"
[model_providers.deepseek]
name = "deepseek"
base_url = "https://api.deepseek.com/"
wire_api = "responses"
# Authentication omitted.
agents/deepseek-flash.toml:
model = "deepseek-v4-flash"
model_provider = "deepseek"
model_catalog_json = "~/.codex/models.json"
model_reasoning_effort = "high"
Relevant official catalog fields:
{
"slug": "deepseek-v4-flash",
"multi_agent_version": "v2",
"supports_reasoning_summaries": true,
"minimal_client_version": "0.144.0"
}
DeepSeek's current Codex setup source:
https://cdn.deepseek.com/api-docs/codex-deepseek-setup-en.sh
Minimal reproduction
- Configure the non-OpenAI child role above.
- Start a persistent Codex session with an OpenAI
gpt-5.6-solparent. - Ask the parent to call
collaboration.spawn_agentwith:
{
"agent_type": "deepseek_flash",
"fork_turns": "none",
"task_name": "cross_provider_v2_canary",
"message": "nonce CROSS_PROVIDER_V2; run pwd with the shell; return the nonce and real output"
}
- Wait for the child.
A direct control on the bundled 0.146.0-alpha.9.2 succeeds: starting deepseek-v4-flash directly and asking it to run pwd produces the expected shell output. In the 0.147.0-alpha.4 child run, the same DeepSeek role also completes model turns and collaboration tool calls while reporting that no task arrived. Together these controls verify that the provider/model channel is operational independently of the task handoff.
Actual behavior on 0.147.0-alpha.4
The runtime excerpts below retain the actual task name and thread IDs. Complete ciphertext and local paths are omitted.
The child session metadata is correct:
{
"cli_version": "0.147.0-alpha.4",
"model_provider": "deepseek",
"agent_role": "deepseek_flash",
"multi_agent_version": "v2"
}
The child turn context is also correct:
{
"model": "deepseek-v4-flash",
"effort": "high",
"multi_agent_version": "v2"
}
However, the parent function-call item contains:
{
"name": "spawn_agent",
"namespace": "collaboration",
"encrypted_function_args": null,
"arguments": {
"agent_type": "deepseek_flash",
"fork_turns": "none",
"task_name": "alpha147_ds_v2_canary",
"message": "gAAAAA..."
}
}
The child's first inter-agent item is:
{
"type": "agent_message",
"author": "/root",
"recipient": "/root/alpha147_ds_v2_canary",
"content": [
{
"type": "input_text",
"text": "Message Type: NEW_TASK\nTask name: /root/alpha147_ds_v2_canary\nSender: /root\nPayload:\n"
},
{
"type": "encrypted_content",
"encrypted_content": "gAAAAA..."
}
]
}
The child sees no usable assignment, reports that no task payload arrived, and performs no requested pwd call. Depending on the continuation path, the parent can subsequently fail with:
stream disconnected before completion: Encrypted function output content could not be decrypted or decoded.
Thread IDs for internal correlation:
- Parent:
019fb970-d912-7192-a013-73199c7716ca - Child:
019fb970-f385-7ac1-aa54-991befb99055
No API keys, complete ciphertext, local paths, or unrelated prompt history are included here.
Why #35845 does not cover this case
#35845 correctly introduces plaintext collaboration delivery when a function call has encrypted_function_args: []. Its regression test creates that empty marker synthetically and verifies that the child receives a structured plaintext NEW_TASK message.
In the real OpenAI-parent to non-OpenAI-child flow above, the parent response has encrypted_function_args: null and the message argument is already ciphertext. The new DirectPlaintextMessage branch is therefore not selected, and the custom child still receives opaque encrypted_content.
This appears to leave no provider-aware path that can materialize the task for a non-OpenAI target child.
Expected behavior
When agent_type resolves to a non-OpenAI child provider, Codex should do one of the following:
- Deliver the V2 task through the provider-neutral plaintext path introduced by #35845; or
- Fail before spawning with a clear error explaining that encrypted cross-provider delegation is unsupported.
Silently creating a correctly routed child with an unreadable task is the worst outcome because it appears successful at the spawn layer and can leave the parent waiting or retrying.
Suggested regression test
Add an end-to-end V2 test with:
- an OpenAI parent function call;
- a registered non-OpenAI Responses child role;
fork_turns = "none";- a nonce-bearing task;
- an assertion that the child receives the nonce as usable input text and executes a deterministic tool call.
The test should exercise the real provider selection boundary, not only a synthetic encrypted_function_args: [] event. If plaintext delivery cannot be supported, assert a fail-fast unsupported-provider error and that no empty-task child is created.
Related issues and change
- #34833 — cross-provider child cannot consume encrypted assignment on 0.145
- #36321 — DeepSeek child receives an empty task on 0.146 Desktop
- #33267 — parent-side encrypted function-output decode failure
- #26753 — encrypted collaboration tools require backend support
- #35845 — plaintext collaboration tool messages; does not cover this encrypted OpenAI-parent path
The distinct contribution of this report is a post-#35845 reproduction on 0.147.0-alpha.4, plus the runtime evidence showing exactly why the new plaintext branch is not selected.
10 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Independent Windows confirmation:
0.146.0-alpha.9.2deepseek-v4-flashresponsesThe child is created with the correct role, provider, and model, but its plaintext
NEW_TASKenvelope stops afterPayload:. The actual assignment remains in anencrypted_contentitem, and the child behaves as if no task was assigned.Control tests:
My Windows reproduction was originally reported in #36387. I am closing it as a duplicate so the cross-platform evidence can be tracked here.
I can also reproduce this issue.
Environment: Ubuntu 26.04 LTS, codex-cli 0.146.0.
When spawning a subagent via MultiAgentV2, the child is created correctly but never receives the actual task prompt — its
NEW_TASKenvelope stops afterPayload:, leaving the child with no assignment to execute. The same result appears with bothfork_turns: "none"andfork_turns: "all".reproduced the same
same issue
Additional data point from the same topology (OpenAI parent -> non-OpenAI child).
Why a local request-layer transform cannot fix this case: when the OpenAI parent returns the spawn call,
messageis real ciphertext withencrypted_function_args: null. The local client cannot decrypt it, so any transform that movesencrypted_contentinto plaintext would feed ciphertext to the non-OpenAI child. The plaintext marker introduced by #35845 (encrypted_function_args: []->DirectPlaintextMessage) is simply not emitted by OpenAI parents in this topology.Suggested upstream direction: when a collaboration tool call targets a child whose provider is not OpenAI, the parent side should force/request the plaintext marker (so
communication_from_tool_messageuses theDirectPlaintextMessagepath), or the Responses service should decrypt cross-provider deliveries. This is not fixable purely on the non-OpenAI child side.We validated the non-OpenAI -> non-OpenAI case separately and published the patch, wire evidence, and write-up here: https://github.com/CCanxue/codex-deepseek-subagent-fix
reproduce this issue too
DeepSeek Subagent Task Body Becomes Actual Ciphertext in Codex 0.147, Which a Local Format-Conversion Proxy Cannot Handle
Summary
On Windows, when an OpenAI model is used as the primary agent and DeepSeek is configured as the
codingsubagent through the Responses API, the DeepSeek subagent does not receive the task body passed throughspawn_agent.After locally capturing the first request sent to the subagent by Codex CLI
0.147.0-alpha.1.2, I found that:agent_messageandencrypted_content;encrypted_contenthas agAAAAA...-style form instead of directly readable plaintext task content;This differs from earlier reports involving Codex 0.146, where the value inside
encrypted_contentwas still plaintext. As a result, a local proxy that simply rewritesencrypted_contentasinput_textcan no longer solve the issue.Environment
0.147.0-alpha.1.2v24.11.02.54.0.windows.1deepseek-v4-flashresponses127.0.0.1:8787Sanitized Configuration
DeepSeek provider configuration in
config.toml:Relevant configuration in
agents/coding.toml:The API key is supplied only through an environment variable. This issue does not include any credential values.
Steps to Reproduce
1. Start a local capture-only proxy
The proxy listens only on the loopback interface, forwards requests exclusively to the official DeepSeek API, and performs no payload transformation:
2. Temporarily change the provider address
No other provider settings, primary-agent model settings,
wire_api, orenv_keyvalues are changed.3. Start a fresh Codex CLI session
Do not reuse an existing session, so the new process reloads
config.toml.4. Send a minimal task
Actual Result
The DeepSeek subagent does not return the probe marker. Instead, it responds with a message similar to:
The local proxy observed four requests of the same type. To avoid exposing prompts or code, the proxy retained only the following sanitized metadata:
All four requests produced the same classification result.
Expected Result
The DeepSeek subagent should receive the task body sent by the parent agent through
spawn_agent,followup_task, orsend_message, and return:Observed Facts
127.0.0.1:8787and was forwarded to the official DeepSeek API.agent_messageand oneencrypted_contentitem.encrypted_contentvalue had the form ofgAAAAA...ciphertext.--unwrap-encrypted.Possible Cause
The following is an inference based on the captured request metadata:
encrypted_content. Instead, it appears to produce an encrypted payload that requires processing by an OpenAI coordination layer.Difference from Earlier Reports
In earlier reproductions involving Codex 0.146, the content type was named
encrypted_content, but its value was still readable plaintext task content. A proxy could therefore convert it toinput_text.In the current Codex 0.147 environment, the same field contains an unreadable
gAAAAA...-style payload. This may reflect a version-level behavior change or a difference caused by the particular OpenAI primary-agent and runtime combination.Request for Maintainer Guidance
Could the Codex or DeepSeek maintainers please confirm:
encrypted_contentbecoming actual ciphertext in Codex 0.147 is expected behavior;message(role="user")withinput_textwhen invoking a non-OpenAI subagent;Current Workarounds
Thanks for the capture. The
gAAAAA...ciphertext with an OpenAI parent matches what we found: in that topology the value is real Fernet ciphertext, so a proxy that only rewritesencrypted_contentto plaintext cannot work. The fix needs the parent side to emit the plaintext marker; the #35845 path is only entered withencrypted_function_args == Some([]), which OpenAI parents do not produce. #37197 documents this gap and an opt-in delivery policy.For comparison, with a non-OpenAI parent the stored value is plaintext, which is why the plain-user-message patch works there. Write-up: https://github.com/CCanxue/codex-deepseek-subagent-fix
Status update from CodexHub Beta4.1 work (sanitized; no credentials or raw ciphertext included).
We independently verified the boundary described here. In an OpenAI-parent -> non-OpenAI-child topology, the V2 handoff can contain a real Fernet-like ciphertext value in
encrypted_content(thegAAAA...shape), rather than the plaintext assignment. The child may be created with the correct provider/model but has no usable task body. This is not recoverable in a downstream Gateway: rewritingagent_message, moving the field, or removing schema annotations cannot decrypt the assignment or reconstruct the original text.What CodexHub Beta4.1 has implemented at the Gateway boundary:
encryptedschema annotations from the third-party tool surface.This is an adapter for ordinary tool-calling providers; it does not require, or claim, native V2 capability in the external model. It can cover the external-coordinator/tool-call side when the task arguments are available as plaintext. It cannot make an OpenAI-parent ciphertext handoff readable to a third-party child.
The remaining fix must therefore be upstream on the producer/delivery side: select a provider-aware, provider-neutral plaintext delivery path for non-OpenAI targets (while retaining encrypted delivery for OpenAI -> OpenAI), or decrypt/re-encode before the non-OpenAI request. The same policy needs to cover spawn, follow-up, and send-message, and unsupported combinations should fail closed before creating an unusable child.
CodexHub's current Beta4.1 candidate has the Gateway adapter and regression coverage, but we are not claiming the full cross-provider V2 lifecycle gate is complete until this upstream delivery dependency is resolved and a fresh real-provider lifecycle run passes.