Skip to content

Subagent task payload invisible to custom non-OpenAI providers (DeepSeek): encrypted_content block dropped with multi_agent_version v2 #36586

Open

Description

@qum1nzhennan

Summary

When using a custom non-OpenAI provider (DeepSeek) with Codex multi-agent (subagents), tasks
dispatched via spawn_agent / followup_task are never seen by the subagent. The subagent
initializes, reads the workspace, and then replies that it has no task input ("no new task
input"), so every subagent run is a no-op.

Environment

  • Codex CLI 0.145.0 on Windows
  • Custom provider in config.toml: [model_providers.deepseek] with
    base_url = "https://api.deepseek.com/" and wire_api = "responses"
  • model = "deepseek-v4-flash", model_catalog_json = "~/.codex/models.json" (written by the
    official DeepSeek one-command setup, which sets "multi_agent_version": "v2" in both entries)
  • Multi-agent tools: collaboration__spawn_agent / followup_task (MultiAgentV2 tool surface)

Reproduction

  1. Start a session with deepseek-v4-flash (models.json entry has
    "multi_agent_version": "v2").
  2. Spawn a subagent with a simple task, e.g. "reply exactly: PONG".
  3. The subagent replies with a workspace status summary and asks what to work on; it never
    executes the dispatched task. followup_task with the same prompt also yields
    "no new task input".

Same setup with a native catalog model (e.g. gpt-5.6-sol) executes subagent tasks correctly.

Root cause

For MultiAgentV2, the task payload is always delivered through an
InterAgentCommunication::new_encrypted(..) record whose content is a
[input_text header, encrypted_content] pair:

  • codex-rs/core/src/tools/handlers/multi_agents_v2.rs — communication_from_tool_message
    uses new_encrypted(author, recipient, vec![], message, trigger_turn) for every
    non-DirectPlaintextMessage source, so the task text lives in the encrypted_content
    content block.
  • codex-rs/core/src/client.rs — in build_responses_request, the !is_openai branch only
    clears FunctionCall.encrypted_function_args; it does not handle
    ResponseItem::AgentMessage content, so the encrypted_content block is sent to the
    provider API unchanged.
  • codex-rs/core/src/client_common.rs — get_formatted_input_for_request passes the items
    through as-is.

For OpenAI endpoints the agent-message payload is decrypted/rendered server-side, which is why
native models work. The DeepSeek Responses API does not understand the encrypted_content
content block type and drops it, so the model only receives
Message Type: NEW_TASK\nTask name: ...\nSender: ...\nPayload: with an empty payload.

Workaround (validated)

Set "multi_agent_version": "v1" for the custom model entries in ~/.codex/models.json
(instead of "v2"). With V1 the spawn initial input is delivered as a plain user message
(SpawnInitialInput::UserInput) instead of an inter-agent communication, and DeepSeek
subagents execute tasks correctly (verified with a codex exec spawn test returning the
expected output). Restart the session after editing models.json.

Note: this means custom non-OpenAI models cannot currently use the MultiAgentV2 tool surface
at all.

Suggested fix

In build_responses_request (codex-rs/core/src/client.rs), extend the !is_openai branch
to transform ResponseItem::AgentMessage content: fold the encrypted_content block into a
single InputText (header text + payload), mirroring the plaintext rendering already used by
communication_from_tool_message's DirectPlaintextMessage path. This lets custom
wire_api = "responses" providers receive subagent task payloads while keeping V2 behavior.

Related

Activity

  1. added
    bugSomething isn't working
    CLIIssues related to the Codex CLI
    windows-osIssues related to Codex on Windows systems
    custom-modelIssues related to custom model providers (including local models)
    subagentIssues involving subagents or multi-agent features
    on Aug 2, 2026
  2. CCanxue commented on Aug 3, 2026

    @CCanxue

    Additional reproduction and a working local patch (codex-cli 0.146.0 source, deepseek-v4-flash, wire_api = "responses", multi_agent_version = "v2").

    Wire-level finding: even when the agent_message content is rewritten so the payload is plaintext input_text (no encrypted_content), DeepSeek still ignores the agent_message item in most runs and the child replies that it has no task. The task is only consumed reliably when it is delivered as a plain user message.

    Working local patch: for non-OpenAI providers, deliver V2 spawn / followup_task / send_message payloads as plain user input (UserInput), mirroring the V1 SpawnInitialInput::UserInput path. The OpenAI path keeps encrypted InterAgentCommunication unchanged. Verified end-to-end with the real provider: spawn_agent -> PONG, followup_task -> PONG2.

    The multi_agent_version: "v1" workaround does not work with deepseek-v4-flash: with DeepSeek's catalog (supports_search_tool: true), the V1 tool family is registered as Deferred behind tool_search. The tool_search tool is present in the request tools array, but deepseek-v4-flash does not recognize or call it (same family as #36382), so the V1 tools are unreachable and v1 stays broken for this model.

    Full patch, build/install steps, QA evidence and config templates (sanitized) are published here: https://github.com/CCanxue/codex-deepseek-subagent-fix

  3. liushiyao110 commented on Aug 4, 2026

    @liushiyao110

    Reproducing on Codex Desktop (macOS), app version 26.727.51351, with deepseek-v4-flash via the official one-command DeepSeek setup (wire_api = "responses", multi_agent_version: "v2" in models.json).

    Both spawn_agent (initial task) and followup_task fail the same way: the child initializes, reads the workspace, then replies "I don't see a task in this message yet" / "Ready when you are — just send over the task."

    Log evidence (rollout JSONL): the delivered agent_message contains an input_text envelope with an empty Payload: plus an encrypted_content block holding the actual task. The HTTP request POSTed to https://api.deepseek.com/responses carries that encrypted_content block untouched, so DeepSeek only sees the empty envelope. Normal user messages are plain input_text and work fine in the same session.

    This confirms the issue is not CLI-only — it also affects the Desktop app with a non-OpenAI model as the root agent. Happy to provide full rollout logs or test a proposed fix.

  4. trillox9 commented on Aug 4, 2026

    @trillox9

    Same issue on codex-cli 0.146.0 with the official DeepSeek setup

    Environment:

    • codex-cli 0.146.0 on Windows 11 (PowerShell)
    • model: deepseek-v4-flash
    • Provider configured exactly as in the official DeepSeek guide:
      wire_api = "responses", base_url = "https://api.deepseek.com/",
      model_catalog_json with multi_agent_version = "v2"

    Reproduction:

    1. The main agent calls collaboration__spawn_agent with a concrete task
      (e.g. "run Get-Date and return the output").
    2. The child agent starts, but instead of executing the task it replies
      with a generic greeting: "I'm ready to help. What would you like me to
      work on?"
    3. followup_task has the same behavior: the child keeps greeting and never
      receives/executes the follow-up.
    4. With fork_turns="all", the child receives no task at all and instead
      replays the parent's spawn calls from the inherited context, creating
      recursive spawn chains (I had to interrupt them manually).

    What I found in the logs:

    • The POST to https://api.deepseek.com/responses contains the task as an
      input item of type "agent_message", with the real payload inside
      "encrypted_content". The visible content stops at "Payload:" with an
      empty text.
    • DeepSeek's Responses API compatibility table only supports these input
      item types: message, function_call, function_call_output, reasoning,
      web_search_call. It explicitly says other types are ignored.
    • As a result, the model never sees the task and just greets the user.

    Expected behavior:

    • For non-OpenAI providers, subagent tasks should be delivered as a plain
      user message (like the community patch does), or Codex should fail fast
      with a clear "provider does not support agent_message input items"
      error instead of silently dropping the task.

    Happy to provide redacted request/response logs or test a patched build.

  5. CCanxue commented on Aug 6, 2026

    @CCanxue

    Thanks for the additional reproductions. trillox9's point about DeepSeek's Responses compatibility table (only message, function_call, function_call_output, reasoning, and web_search_call are accepted; other input item types are ignored) explains why folding encrypted_content into plaintext input_text inside the agent_message item is not enough: the whole item type is dropped, not just the encrypted block.

    For anyone who wants to test a fix now: we published a local patch for 0.146.0 that delivers V2 spawn / followup_task / send_message tasks as plain user messages for non-OpenAI providers (OpenAI path unchanged), verified on CLI and applicable to the Desktop bundled CLI. liushiyao110, the build/install steps in the repo cover the Desktop case as well. We also saw the same recursive spawn chains with fork_turns: all that trillox9 described, when the parent prompt leaks into the child context.

    Repo: https://github.com/CCanxue/codex-deepseek-subagent-fix

  6. TOV1C commented on Aug 11, 2026

    @TOV1C

    Root cause
    MultiAgentV2 delivers tasks through InterAgentCommunication: the plaintext content is empty, and the entire task text is placed in the encrypted_content content block. OpenAI endpoints decrypt and render this block server-side, which is why native models work; DeepSeek's Responses implementation does not recognize encrypted_content (community packet captures show that sending a request containing this content item directly returns a 400 / it gets dropped), so the sub-agent only sees an empty Payload:. This is the same family of issues as openai/codex #36493, #36321, #36586, #37237, and #37822.
    Verified workaround (currently in effect on this machine)
    Modify both entries in ~/.codex/models.json:

    • "multi_agent_version": "v2"
    • "multi_agent_version": "v1"
    • "supports_search_tool": true
  7. CCanxue commented on Aug 14, 2026

    @CCanxue

    Thanks for confirming the root cause and for the refined v1 workaround. We can confirm the mechanism: with supports_search_tool = false, V1 tools are registered with direct exposure instead of being deferred behind tool_search, which addresses the discoverability problem we hit earlier with deepseek-v4-flash. This is a config-only change, so it also works on the official unpatched binary (no rebuild needed). The tradeoff is losing the V2 tool family (followup_task, send_message, list_agents, interrupt_agent); if you need those, a local patch that keeps V2 and delivers payloads as plain user messages for non-OpenAI providers is verified end-to-end: https://github.com/CCanxue/codex-deepseek-subagent-fix

  8. HaoYue1027 commented on Aug 22, 2026

    @HaoYue1027

    Additional reproduction: Codex Desktop (Windows) + DeepSeek official setup, CLI 0.149.0 / app 26.818.5229.0 — confirms the same encrypted_content drop; workaround (multi_agent_version v1) matches your analysis.

    Environment

    • OS: Windows 11 (Build 26200), desktop Codex app 26.818.5229.0; embedded runtime codex-cli 0.149.0 (internal kernel string 0.149.0-alpha.4 from app\resources\codex.exe; UI version string differs — checked the binary, not the UI, per codex-desktop-diagnostics).
    • Model: deepseek-v4-flash-vision-exp; model_provider = "deepseek"; base_url = "https://api.deepseek.com/"; wire_api = "responses".
    • ~/.codex/models.json written by the official DeepSeek one-command setup: all three entries (deepseek-v4-flash, deepseek-v4-pro, deepseek-v4-flash-vision-exp) have "multi_agent_version": "v2".

    Reproduction (stable)

    1. In a Desktop session with the model above, call spawn_agent(task_name="acceptance", message="...", fork_turns="none") and wait_agent.
    2. list_agents shows the sub-agent completing with: “I only received session context; I don't see the concrete task content.”
    3. The stored agent_message in the session rollout (~/.codex/sessions/**/*.jsonl) is exactly:
      Message Type: NEW_TASK
      Task name: /root/acceptance
      Sender: /root
      Payload:
      
      — length 82 chars, Payload: is empty. Reproduced 3+ times across spawn_agent/followup_task/send_message, both before and after a full Desktop restart.

    Causal isolation

    • Same spawn works after editing models.json: setting "multi_agent_version": "v1" for the three DeepSeek entries delivers the task as plaintext user input and the sub-agent executes correctly (verified once, then rolled back).
    • Control: with model_provider="deepseek" unchanged, fork_turns=1 partially masks the issue (child receives forked history but still not the per-agent spawn message), while fork_turns="none" yields a fully empty agent — consistent with encrypted_content being dropped by the third-party endpoint.
    • No error is surfaced anywhere; spawn_agent returns success and followup_task returns silently empty.

    Boundary

    • Verified on Windows Desktop only; not tested on macOS/CLI in this report.
    • The workaround v1 is usable but loses the v2 tool surface (per your note) — I reverted it to keep the default v2 baseline while upstream decides.

    Request

    This repo already tracks it in #37237/#37822; adding my Windows Desktop + 0.149.0 data point. Expected: task body reaches the sub-agent (plaintext expansion before serialization, or a loud error for third-party providers). Actual: empty Payload: header, zero diagnostics. Please confirm and prioritize the suggested fix in build_responses_request (fold encrypted_content into InputText for non-OpenAI providers).

  9. maxronner commented on Aug 29, 2026

    @maxronner

    Reproduced on codex-cli 0.147.0 with the deepseek provider (default profile). All three DeepSeek model entries in the local models.json catalog are multi_agent_version=v2, and every spawned sub-agent receives a NEW_TASK envelope with an empty Payload: (task text lands in encrypted_content, which the DeepSeek Responses API drops). Sub-agents run setup then report 'no task input'. Inline/sequential fallback works; the parallel sub-agent path is blocked. Setting multi_agent_version=v1 for the DeepSeek entries is the workaround I'm applying.

  10. SuiltaPico commented on Sep 14, 2026

    @SuiltaPico

    Adds a Codex Desktop data point with one config variable I did not see called out in this thread: disable_response_storage = true.

    Environment (sanitized)

    Field Value
    App Codex Desktop 26.908.40834
    Bundled CLI 0.154.0-alpha.6.2
    OS Windows
    Provider custom deepseek
    Endpoint https://api.deepseek.com
    Wire API responses
    Model deepseek-v4-flash
    Reasoning high, summary concise
    disable_response_storage true
    Sandbox danger-full-access / elevated
    Token <REDACTED> (not included anywhere)

    Reproduction

    1. In a Desktop session, call spawn_agent with fork_turns = "none" and a nonce-only task:
    {
      "task_name": "probe",
      "fork_turns": "none",
      "message": "NONCE=4f7c2a; reply with only this NONCE."
    }
    1. Wait for the child.
    2. The child starts, reads the workspace, and reports that it received no task.
    3. followup_task to the same child with NONCE=9d1b3e shows the same behavior.

    Child rollout shape:

    [input_text]
    Message Type: NEW_TASK
    Task name: /root/probe
    Sender: /root
    Payload:
    
    [encrypted_content]
    NONCE=4f7c2a; reply with only this NONCE.
    

    The child model sees the header, but not the nonce.

    Why I am adding this

    This is the same failure family as this issue and #45233, but the Desktop build plus disable_response_storage = true is a slightly different configuration. I have not tested whether the bug persists with disable_response_storage = false; I did not want to change that flag without a clean control.

    One request for maintainers: please confirm whether disable_response_storage is part of the trigger, or whether it is incidental to the custom-provider path.

    Fix discussion

    The later comments here (especially @CCanxue and @OneCuriousLearner) make sense: folding encrypted_content into readable text inside agent_message may not be enough if the provider drops the whole agent_message item type. What I can confirm from this Desktop run is only the observed behavior:

    • spawn_agent returns success.
    • The child turn starts.
    • The payload is persisted as encrypted_content.
    • The child model does not receive a usable task.
    • No error is surfaced to the parent.

    If a provider-neutral plain user/message rewrite is the intended fix for non-OpenAI providers, it would help to also fail closed: a spawn should not report success if the child cannot see the payload.

    Related but probably separate

    While probing this, I also saw fork_turns = "all" copy an in-flight parent turn. The child replayed the parent's spawn actions, created depth-2 agents, and exhausted the 4-agent slot limit. That looks like fork-semantics / recursion control rather than the same encrypted-content drop. I can file it separately if useful.

    Workaround status

    I have not independently verified the multi_agent_version: v1 + supports_search_tool: false workaround on this exact Desktop build, so I am not claiming it works here. I am only adding the Desktop + disable_response_storage reproduction point.

  11. pluto1747-cybr commented on Sep 25, 2026

    @pluto1747-cybr

    Adding a 2026-09-25 data point on Codex Desktop 26.917.71314 / CLI 0.155.0-alpha.16.4 (macOS + Windows, DeepSeek Responses provider): the multi_agent_version: v1 workaround works, but the effective value is selected at thread creation and cached for that thread.

    Changing the model catalog to v1 is not enough by itself — it needs a cold start (so the process reloads the catalog) and a new thread. Threads created before the change keep turn_context -> multi_agent_version: v2 across restarts; new threads read v1 and subagents inherit v1. features.multi_agent_v2 = false did not change the effective path in our tests.

    This may matter for the fix/UX: a catalog edit silently does nothing for existing threads.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIbugSomething isn't workingcustom-modelIssues related to custom model providers (including local models)subagentIssues involving subagents or multi-agent featureswindows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions