Skip to content

MultiAgentV2 cross-provider subagent cannot consume encrypted task assignment #34833

Open

Description

@qqq-jump

Summary

With an OpenAI parent and a correctly selected non-OpenAI custom-provider child, MultiAgentV2 delivers the child assignment only as encrypted content that the custom-provider model cannot consume.

The spawned child metadata shows the intended custom role, provider, model, and MultiAgentV2 implementation, but the child sees an empty visible Payload: and reports that it has no assignment.

A same-provider OpenAI child consumes an otherwise equivalent encrypted assignment successfully.

Environment

  • Codex CLI: 0.145.0
  • Surface: Codex desktop connected to a remote Codex app-server over SSH
  • Multi-agent version: v2
  • Parent provider: OpenAI
  • Child role: registered custom agent
  • Child provider: non-OpenAI, OpenAI-compatible Chat Completions provider
  • Child model: Kimi K3

Reproduction

  1. Enable multi_agent_v2.
  2. Configure a registered custom child agent using a non-OpenAI provider and an OpenAI-compatible Chat Completions model.
  3. Start an OpenAI-backed parent.
  4. Spawn the custom child with a unique harmless marker and an explicit instruction to run pwd.
  5. Inspect the spawned session metadata and child handoff.

The session metadata confirms:

  • agent_role is the registered custom role.
  • multi_agent_version is v2.
  • model_provider is the intended custom provider.
  • model is the intended custom model.

The stored handoff displays an empty Payload: and contains an encrypted-content block. The child reports that it received no assignment and does not execute the marker or pwd.

In a related continuation path, the parent can terminate with:

Encrypted function output content could not be decrypted or decoded.

Control: same-provider child

Repeat the test with an OpenAI child.

Its stored handoff also displays an empty visible Payload: plus encrypted content, but the OpenAI child successfully consumes the assignment, reproduces the marker, runs pwd, and returns the expected path.

This indicates that general v2 dispatch works and that the problem is specific to the provider boundary.

Control: provider-neutral delivery

As a diagnostic only, provide the same assignment to the custom child through a single-use, permission-restricted, nonce-bound local file referenced by the custom agent's developer instructions.

The custom child then executes the marker and pwd successfully.

This confirms that:

  • The custom provider and model are operational.
  • Child provider/model selection works.
  • The task itself is valid.
  • Failure occurs when the assignment is carried only through the encrypted MultiAgentV2 field.

A fixed file inbox is not a safe permanent workaround because of stale-task, concurrency, cleanup, attribution, and local instruction-injection risks.

Expected behavior

Codex should make the assignment available to the selected child in a representation that its configured provider can consume.

Possible implementations include decrypting/translating the assignment within Codex before invoking a provider that does not support OpenAI encrypted tool parameters, or explicitly rejecting unsupported cross-provider delegation before spawning the child.

Actual behavior

The custom child is spawned with the correct role, provider, and model, but cannot consume the encrypted assignment and behaves as though no task was supplied.

Related issues

Additional notes

This is not a request to support a CPA-backed parent. The failing topology is specifically:

OpenAI parent -> registered non-OpenAI custom-provider child

Please let me know which narrowly scoped logs or sanitized session records would help. I have intentionally omitted authorization data, internal network details, complete encrypted blobs, and unrelated prompts.

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    app-serverIssues involving app server protocol or interfaces
    subagentIssues involving subagents or multi-agent features
    custom-modelIssues related to custom model providers (including local models)
    on Jul 22, 2026
  2. github-actions commented on Jul 22, 2026

    @github-actions
    Contributor

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

    Powered by Codex Action

  3. qqq-jump commented on Jul 22, 2026

    @qqq-jump
    Author

    Thanks — I reviewed #33551. It is closely related, but I do not think it is an exact duplicate.

    #33551 focuses on external Responses providers receiving the OpenAI-specific agent_message item type, which may be rejected even when the item is plaintext.

    This report reproduces with a custom provider using the OpenAI-compatible Chat Completions protocol. The child session is created with the correct custom role, provider, and model, but the assignment is available only through the encrypted MultiAgentV2 handoff and the child behaves as though no task was supplied. The same encrypted handoff is consumed successfully by an OpenAI child, while the same custom-provider child executes the task when it is delivered through a provider-neutral plaintext diagnostic.

    So the reports may share the same broader need for provider-aware inter-agent transport, but they exercise different provider protocols and failure points. I am leaving this open for maintainer triage and am happy for it to be consolidated if the fix for #33551 is intended to cover Chat Completions providers as well.

  4. ignatremizov commented on Jul 23, 2026

    @ignatremizov

    You can change model catalogue to force v1. Anyways duplicate of #28058

  5. ysxk commented on Jul 23, 2026

    @ysxk

    Corroboration from Codex desktop multi-role session

    Confirmed the same MultiAgentV2 cross-provider failure on Codex desktop / CLI 0.145.0-alpha.30, macOS Darwin 25.5.0 arm64, with project-local agents under .codex/agents/.

    Topology

    Observed behavior (Kimi K3 child)

    1. Parent spawn_agent(agent_type=frontend, ...) succeeds; child thread starts with correct role/model.
    2. Child history contains:
      Message Type: NEW_TASK
      Task name: /root/frontend_...
      Sender: /root
      Payload:
      
      followed by encrypted_content only (visible Payload: empty).
    3. Child immediately replies that no concrete task payload was assigned / parent has not sent a task, then stands by. 0 tool calls.
    4. Parent followup_task to the same child produces the same empty-payload handoff; child reasons "The parent agent hasn't sent a task yet" and stands by again.
    5. Control (same parent, same MultiAgentV2, same encrypted handoff shape): backend child on gpt-5.6-sol consumes the assignment and executes immediately.
    6. Control (provider-neutral plaintext): temporarily embedding the approved task into the frontend role developer_instructions makes the K3 child start working (reads repo). Same model channel is therefore operational; only the encrypted MultiAgentV2 assignment path fails.
    7. Control (solo role start): launching the frontend role outside multi-agent / with normal plaintext user turns also works. Failure is specific to MultiAgentV2 encrypted task delivery, not “K3 is down”.

    Follow-up

    Parent eventually completed the work by temporarily spawning the same frontend role on gpt-5.6-sol. Persistent project config remained on K3.

    Expected

    If Codex allows selecting a non-OpenAI provider for a registered child role, MultiAgentV2 should deliver spawn/followup/send_message assignments in a representation that provider can consume (decrypt/translate at the boundary to a standard plaintext message item), or fail fast with an explicit unsupported-cross-provider error before the child stands by forever.

    Related

    Happy to provide redacted rollout excerpts (parent + child) if useful; avoiding full ciphertext and internal network details here.

  6. qqq-jump commented on Jul 24, 2026

    @qqq-jump
    Author

    Thanks — V1 can indeed be forced and works as a workaround, but it gives up quite a bit of V2 functionality.

    My understanding is that #28058 is related but slightly different: it concerns the loss of a readable audit trail, whereas this issue concerns the encrypted handoff preventing a non-OpenAI sub-agent from receiving the task at all. The underlying mechanism may be the same, but the user-facing problems are different.

  7. CCanxue commented on Aug 3, 2026

    @CCanxue

    Additional data from codex-cli 0.146.0 source with a real DeepSeek child (wire-captured).

    For the non-OpenAI -> non-OpenAI direction, the task text only ever reaches the child inside encrypted_content; DeepSeek drops that block and the child sees an empty Payload:. Even after rewriting the agent_message content so the payload is plaintext input_text, DeepSeek still ignores the agent_message item in most runs. The task is only consumed reliably when delivered as a plain user message.

    For the OpenAI -> non-OpenAI direction, message is real ciphertext with encrypted_function_args: null, so a local request transform cannot decrypt it (see also #36376).

    We published a local patch that delivers V2 spawn / followup_task / send_message payloads as plain user input for non-OpenAI providers (OpenAI path unchanged), verified end-to-end: https://github.com/CCanxue/codex-deepseek-subagent-fix

  8. YukiagoTpf commented on Aug 6, 2026

    @YukiagoTpf

    Follow-up after validating the plaintext collaboration changes shipped in 0.147.0-alpha.1 via #35845:

    The cross-provider failure described here remains reproducible on a 0.147.0-alpha.4 source baseline.

    The alpha.1 change added a valid plaintext receiving path, but that path is entered only when encrypted_function_args == Some([]) and the tool namespace is exactly collaboration. The MultiAgentV2 schemas still unconditionally mark the message fields for spawn_agent, send_message, and followup_task with .with_encrypted(). An OpenAI parent therefore normally supplies actual encrypted arguments, the call remains Direct, and the child assignment is still constructed with InterAgentCommunication::new_encrypted(...).

    This leaves the original third-party-provider topology unresolved. It also does not cover custom or absent tool namespaces, does not expose an explicit delivery policy, and does not fail before creating a child that cannot consume the encrypted task.

    I implemented and tested a local source-level path with an opt-in plaintext delivery policy, a non-reserved tool namespace, end-to-end call classification, and a fail-closed guard. Targeted feature tests passed 34/34, targeted MultiAgentV2 tests passed 74/74, the release build succeeded, and a restarted Codex Desktop session successfully completed an OpenAI-parent to third-party-provider-child smoke test.

    The full analysis, affected source paths, sanitized configuration shape, and validation results are documented in #37197.

  9. CCanxue commented on Aug 6, 2026

    @CCanxue

    Thanks for the detailed analysis. It matches what we observed on the non-OpenAI parent side: the #35845 plaintext marker is only entered with encrypted_function_args == Some([]), which third-party parents do not emit, and even a plaintext agent_message item is dropped by DeepSeek (its Responses compatibility table only accepts message, function_call, function_call_output, reasoning, and web_search_call item types).

    Our local patch takes a simpler route for the non-OpenAI parent case: V2 spawn / followup_task / send_message payloads are delivered as plain UserInput (V1-style standard user messages), verified end-to-end with deepseek-v4-flash. It intentionally does not cover the OpenAI-parent -> third-party-child case, which still needs an opt-in plaintext delivery policy like the one you described; the fail-closed guard and end-to-end call classification sound like the right direction. Patch, wire evidence, and write-up: https://github.com/CCanxue/codex-deepseek-subagent-fix

  10. snoyiatk commented on Aug 11, 2026

    @snoyiatk

    +1 we need to merge/fix this issue so codex is really usable with open-weighted models

  11. NOirBRight commented on Aug 13, 2026

    @NOirBRight

    CodexHub Beta4.1 follow-up (sanitized; no credentials or raw ciphertext included).

    This issue remains an upstream producer/delivery limitation for the OpenAI-parent -> non-OpenAI-child direction. We reproduced that the child can be created correctly while the V2 assignment is available only through an agent_message/encrypted_content handoff containing ciphertext. A Gateway downstream of the parent cannot decrypt that value; simply converting the item shape to a normal function/message or removing an Official schema flag does not restore the task body.

    CodexHub Beta4.1 has completed the separate Gateway adaptation work: V2 namespace declarations are presented to third-party models as ordinary function tools, aliases are deterministic and reversible, streamed/non-streamed calls and history are adapted, and Official OpenAI traffic remains passthrough. This is an adapter for ordinary tool-calling models, not a claim that they natively understand Codex V2.

    The missing piece is provider-aware delivery on the upstream producer side: retain encrypted transport for OpenAI targets, but use a provider-neutral plaintext user/message path for non-OpenAI targets across spawn/follow-up/send-message, with a fail-closed error when unsupported. We are keeping the full external-V2 lifecycle gate open rather than marking it passed based only on tool declarations or a model catalog flag.

  12. ta9to commented on Aug 15, 2026

    @ta9to

    Reproduced on stable 0.147.0 with Vercel AI Gateway + DeepSeek

    I reproduced the OpenAI-parent -> non-OpenAI-child failure on the current stable CLI, with a Responses-compatible hosted gateway rather than a local provider.

    Environment

    • macOS
    • codex-cli 0.147.0
    • Parent: OpenAI gpt-5.6-sol / MultiAgentV2
    • Registered custom child role: deepseek_flash_0731
    • Child provider: Vercel AI Gateway
    • wire_api = "responses"
    • Child model: deepseek/deepseek-v4-flash-0731
    • Spawn used the registered role with fork_turns = "none"

    Observed

    1. The child thread was created successfully.
    2. Child session metadata showed the intended registered role and model_provider = "vercel_ai_gateway"; the child turn used the intended DeepSeek model.
    3. The parent-side spawn_agent.message was stored as encrypted content.
    4. The child model-visible input contained its developer instructions plus environment/plugin context, but not the delegated task.
    5. The child responded that it had received no task and asked for the concrete work item.
    6. A follow-up delivery did not reliably restore the missing assignment.

    This matches the empty/missing assignment behavior described here: provider/model selection succeeds, but the dynamic V2 handoff is not consumable across the provider boundary.

    Controls

    • A direct authenticated request to the same Vercel /v1/responses endpoint with the exact model returned the expected canary.
    • A direct Codex run using the same custom provider/model returned an exact marker successfully.
    • As a practical control/workaround, an OpenAI native relay child received the V2 assignment and invoked the same DeepSeek profile through a normal plaintext codex exec subprocess. DeepSeek then successfully created a minimal Python Hello, World! program, executed it, and verified exact stdout.

    That last control confirms that Vercel authentication, the DeepSeek model, Responses transport, tool execution, and file writes are all operational. The failure remains isolated to native MultiAgentV2 cross-provider task delivery.

  13. joenoon commented on Sep 22, 2026

    @joenoon

    Additional reproduction with Codex CLI 0.155.1 and Vercel AI Gateway, using an OpenAI model for both parent and child.

    Configuration

    • Model: gpt-6-astra, low reasoning effort
    • Custom provider: wire_api = "responses"
    • Endpoint: https://ai-gateway.vercel.sh/codex/v1
    • Gateway API key; no personal OpenAI authentication
    • Standalone codex exec, outside our application and App Server integration

    V2 reproduction

    We asked the parent to spawn exactly one child, have it read a harmless marker file, and return its contents.

    The child started, but its transcript contained an agent_message with a task header and encrypted_content. It replied, “What would you like me to work on?” without reading the file. The parent continued waiting until our test harness interrupted it at 180 seconds.

    Successful V1 control

    Using the same CLI, model, and endpoint, we supplied a temporary copy of the bundled model catalogue through model_catalog_json, changing:

    • multi_agent_version to "v1"
    • supports_search_tool to false

    The child then received the assignment as plain user input, read the marker file, and returned the correct result. The parent received it through native wait and completed normally.

    We did not modify Codex or rewrite network traffic. This was an isolated test; we have not adopted the catalogue override in our product.

    Related: #36586 documents the V1/tool-discovery workaround, which also succeeded in our test.

    Our case does not switch to a non-OpenAI model: both agents use an OpenAI model through Gateway. We have not isolated whether the failure occurs in Codex, Gateway translation, or the upstream endpoint.

    Is V2 task delivery expected to work in this configuration? Is there a supported fix or configuration we should test before relying on the V1 catalogue override?

    We can provide sanitized transcript excerpts and a minimal reproduction.

  14. cgasgarth commented on Oct 5, 2026

    @cgasgarth

    Verified a small producer-side plaintext V2 prototype on official Codex 0.160.0 (October 4, 2026). Using an ordinary agents namespace plus a two-file opt-in patch, real GPT → Fable/Opus/GPT tasks, Claude Opus → GPT tasks, follow-ups, proactive messages, and an app-server GPT → Opus → GPT chain all completed with independently verified files.

    This does not decrypt ciphertext or merely reclassify sealed bytes. The patch changes both message schemas and delivery selection so the generated tasks are readable from the start. All nine saved sessions recorded V2 and the requested models; their 34 inter-agent input records had no encrypted parts.

    Full evidence and limits: #46939 (comment)

    Source patch and reproducible probes: https://github.com/cgasgarth/skills/tree/e5b3134/patches/v2-plaintext

    This is isolated CLI/app-server backend proof, not desktop UI acceptance or an upstream-supported configuration. Existing encrypted history and other providers remain outside the tested scope.

  15. cgasgarth commented on Oct 5, 2026

    @cgasgarth

    Update: verified both GPT Sol → Claude Opus and Claude Opus → GPT Sol in actual desktop-app chats with the official signed Codex 0.160.0 backend. Initial tasks, explicit parent-directed messages, and same-child follow-ups all passed.

    VibeProxy's installed CLIProxyAPI 8.0.4 already supports codex.optimize-multi-agent-v2: true. Codex also needs V2 enabled and V2 model catalog entries for the children's collaboration tools. No custom Codex build or app patch is needed for this setup.

    The previous local-build approach broke native app tools because of macOS peer code-signature checks (#34583). It has been removed.

    Configuration, source links, results, and scope: #46939 (comment)

  16. jesus-rwp commented on Oct 17, 2026

    @jesus-rwp

    Cross-referencing the non-OpenAI parent → OpenAI-compatible child direction, which fails differently from the empty-payload case above: the child rejects the handoff with 400 {"code":"thinking_signature_invalid","message":"Encrypted function output content could not be decrypted or decoded."}.

    The gate added in #35845 is still encrypted_function_args.as_ref().is_some_and(Vec::is_empty) in ToolCall::direct_source() (codex-rs/core/src/tools/router.rs). Third-party parents that omit the field entirely (None) never enter the plaintext path, so agent_message_from_tool wraps the parsed plaintext message as AgentMessage::Encrypted. Treating None like [] (is_none_or(Vec::is_empty)) fixes that direction without a config switch; code-mode nested calls hit the same path because they build the call with encrypted_function_args: None.

    Repro, the two-line patch and test results are in #36586: #36586 (comment)

    Scope note: this only fixes the content-type/signature failure for children that accept agent_message. The separate DeepSeek behavior of dropping the whole agent_message item (#33551) still needs a user-message delivery path.

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

    appIssues related to the Codex desktop appapp-serverIssues involving app server protocol or interfacesbugSomething isn't workingcustom-modelIssues related to custom model providers (including local models)subagentIssues involving subagents or multi-agent features

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions