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
- Start a session with
deepseek-v4-flash (models.json entry has
"multi_agent_version": "v2").
- Spawn a subagent with a simple task, e.g. "reply exactly: PONG".
- 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
Summary
When using a custom non-OpenAI provider (DeepSeek) with Codex multi-agent (subagents), tasks
dispatched via
spawn_agent/followup_taskare never seen by the subagent. The subagentinitializes, 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
0.145.0on Windowsconfig.toml:[model_providers.deepseek]withbase_url = "https://api.deepseek.com/"andwire_api = "responses"model = "deepseek-v4-flash",model_catalog_json = "~/.codex/models.json"(written by theofficial DeepSeek one-command setup, which sets
"multi_agent_version": "v2"in both entries)collaboration__spawn_agent/followup_task(MultiAgentV2 tool surface)Reproduction
deepseek-v4-flash(models.json entry has"multi_agent_version": "v2").executes the dispatched task.
followup_taskwith 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_messageuses
new_encrypted(author, recipient, vec![], message, trigger_turn)for everynon-
DirectPlaintextMessagesource, so the task text lives in theencrypted_contentcontent block.
codex-rs/core/src/client.rs— inbuild_responses_request, the!is_openaibranch onlyclears
FunctionCall.encrypted_function_args; it does not handleResponseItem::AgentMessagecontent, so theencrypted_contentblock is sent to theprovider API unchanged.
codex-rs/core/src/client_common.rs—get_formatted_input_for_requestpasses the itemsthrough 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_contentcontent 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 DeepSeeksubagents execute tasks correctly (verified with a
codex execspawn test returning theexpected 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_openaibranchto transform
ResponseItem::AgentMessagecontent: fold theencrypted_contentblock into asingle
InputText(header text + payload), mirroring the plaintext rendering already used bycommunication_from_tool_message'sDirectPlaintextMessagepath. This lets customwire_api = "responses"providers receive subagent task payloads while keeping V2 behavior.Related
custom
models.json+ non-OpenAI provider gaps in tool/content handling)