`0.147.0-alpha.4`: OpenAI parent still sends encrypted V2 task to non-OpenAI child after #35845

Open 💬 10 comments Opened Jul 31, 2026 by Qian9921
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

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.4 arm64 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

  1. Configure the non-OpenAI child role above.
  2. Start a persistent Codex session with an OpenAI gpt-5.6-sol parent.
  3. Ask the parent to call collaboration.spawn_agent with:
{
  "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"
}
  1. 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:

  1. Deliver the V2 task through the provider-neutral plaintext path introduced by #35845; or
  2. 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.

View original on GitHub ↗

10 Comments

github-actions[bot] contributor · 27 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #36321
  • #35932

Powered by Codex Action

EliotOK · 27 days ago

Independent Windows confirmation:

  • Codex Desktop / bundled CLI: 0.146.0-alpha.9.2
  • Platform: Windows
  • Parent: OpenAI-backed model
  • Child provider/model: DeepSeek / deepseek-v4-flash
  • Child wire API: responses

The child is created with the correct role, provider, and model, but its plaintext NEW_TASK envelope stops after Payload:. The actual assignment remains in an encrypted_content item, and the child behaves as if no task was assigned.

Control tests:

  1. An OpenAI-backed child receives the same assignment correctly.
  2. The DeepSeek provider and model work when the assignment is supplied through a temporary plaintext file.
  3. DeepSeek authentication, Responses API access, tool calls, and workspace access therefore appear operational.

My Windows reproduction was originally reported in #36387. I am closing it as a duplicate so the cross-platform evidence can be tracked here.

mirever · 27 days ago

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_TASK envelope stops after Payload:, leaving the child with no assignment to execute. The same result appears with both fork_turns: "none" and fork_turns: "all".

robin-liquidium · 26 days ago

reproduced the same

boogie-ben · 25 days ago

same issue

CCanxue · 25 days ago

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, message is real ciphertext with encrypted_function_args: null. The local client cannot decrypt it, so any transform that moves encrypted_content into 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_message uses the DirectPlaintextMessage path), 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

Utopia-V · 23 days ago

reproduce this issue too

Leo12707 · 22 days ago

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 coding subagent through the Responses API, the DeepSeek subagent does not receive the task body passed through spawn_agent.

After locally capturing the first request sent to the subagent by Codex CLI 0.147.0-alpha.1.2, I found that:

  • The request still contains agent_message and encrypted_content;
  • The value of encrypted_content has a gAAAAA...-style form instead of directly readable plaintext task content;
  • The plaintext probe marker is not present in that field;
  • The DeepSeek subagent ultimately responds that it did not receive a specific task.

This differs from earlier reports involving Codex 0.146, where the value inside encrypted_content was still plaintext. As a result, a local proxy that simply rewrites encrypted_content as input_text can no longer solve the issue.

Environment

  • OS: Windows NT 10.0.22631
  • PowerShell: 5.1.22621.6133
  • Codex CLI: 0.147.0-alpha.1.2
  • Node.js: v24.11.0
  • Git: 2.54.0.windows.1
  • Primary agent: OpenAI model
  • Subagent: deepseek-v4-flash
  • Provider wire API: responses
  • Local proxy test address: 127.0.0.1:8787

Sanitized Configuration

DeepSeek provider configuration in config.toml:

[model_providers.deepseek]
name = "DeepSeek"
base_url = "https://api.deepseek.com/"
wire_api = "responses"
env_key = "DEEPSEEK_API_KEY"
requires_openai_auth = false

Relevant configuration in agents/coding.toml:

name = "coding"
model = "deepseek-v4-flash"
model_provider = "deepseek"
model_reasoning_effort = "high"

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:

node .\deepseek-proxy.mjs `
  --host 127.0.0.1 `
  --port 8787 `
  --upstream https://api.deepseek.com `
  --logdir .\logs-test

2. Temporarily change the provider address

base_url = "http://127.0.0.1:8787"

No other provider settings, primary-agent model settings, wire_api, or env_key values 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

Create a subagent using the custom coding agent configuration.

Use fork_turns: "none".

Task:
Do not modify any files.
Reply with only:

DEEPSEEK_TASK_RECEIVED

Actual Result

The DeepSeek subagent does not return the probe marker. Instead, it responds with a message similar to:

The message I received only contains the Coding Agent role definition.
No specific task content was included.

The local proxy observed four requests of the same type. To avoid exposing prompts or code, the proxy retained only the following sanitized metadata:

{
  "method": "POST",
  "path": "/responses",
  "has_authorization": true,
  "has_agent_message": true,
  "encrypted_content_count": 1,
  "encrypted_content_kinds": ["fernet_like_ciphertext"],
  "contains_probe_marker": false,
  "status": 200
}

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, or send_message, and return:

DEEPSEEK_TASK_RECEIVED

Observed Facts

  1. A fresh Codex CLI process reloaded the provider configuration pointing to the local proxy.
  2. The request passed through 127.0.0.1:8787 and was forwarded to the official DeepSeek API.
  3. The request contained agent_message and one encrypted_content item.
  4. The encrypted_content value had the form of gAAAAA... ciphertext.
  5. The plaintext probe marker was not present in that field.
  6. DeepSeek returned HTTP 200, but semantically the subagent still did not receive the task.
  7. The issue is reproducible without enabling --unwrap-encrypted.

Possible Cause

The following is an inference based on the captured request metadata:

  • MultiAgentV2 in Codex 0.147 may no longer place the plaintext task directly inside encrypted_content. Instead, it appears to produce an encrypted payload that requires processing by an OpenAI coordination layer.
  • A non-OpenAI provider may not have access to the corresponding decryption or unpacking capability, so DeepSeek receives only an empty task envelope.
  • A local proxy can rewrite the JSON message structure, but it cannot recover the plaintext task from actual ciphertext.

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 to input_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:

  1. Whether encrypted_content becoming actual ciphertext in Codex 0.147 is expected behavior;
  2. How a non-OpenAI provider is expected to obtain the subagent task body;
  3. Whether Codex can send a standard message(role="user") with input_text when invoking a non-OpenAI subagent;
  4. Whether there is a configurable plaintext task-transport mode for cross-provider subagents;
  5. If encrypted transport is required, whether Codex can unpack the payload internally before sending a standard message to the third-party provider;
  6. Whether Codex can return an explicit protocol error when a subagent cannot read the task body, instead of allowing the model to continue with an empty task.

Current Workarounds

  • Use OpenAI Terra or another OpenAI model for the subagent;
  • Use a workspace file as a temporary task bridge, clearly marking it as a fallback;
  • Avoid using DeepSeek as a message-based MultiAgentV2 subagent until cross-provider task transport is officially supported.
CCanxue · 17 days ago

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 rewrites encrypted_content to plaintext cannot work. The fix needs the parent side to emit the plaintext marker; the #35845 path is only entered with encrypted_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

NOirBRight · 14 days ago

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 (the gAAAA... 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: rewriting agent_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:

  • V2 collaboration namespace -> ordinary third-party function declarations.
  • Deterministic, collision-safe tool aliases, with reverse mapping for function calls/results.
  • Removal of Official-only encrypted schema annotations from the third-party tool surface.
  • Streaming/non-streaming and history adapter coverage.
  • Official OpenAI passthrough remains opaque and unchanged.

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.