Skip to content

Add a config option to select plaintext delivery for Multi-Agent V2 task messages #46939

Open

Description

@jerryan999

What variant of Codex are you using?

CLI (codex-cli 0.155.1) on macOS. The tool schemas are shared, so the same behaviour shows up in the app and the IDE extension.

What feature would you like to see?

Multi-Agent V2 marks the message parameter of all three collaboration tools as encrypted:

// codex-rs/core/src/tools/handlers/multi_agents_spec.rs (0.155.1)
// spawn_agent  -> line 633
("message".to_string(),
 JsonSchema::string(Some("Initial plain-text task for the new agent."))
     .with_encrypted()),
// send_message   -> line 198
// followup_task  -> line 231

The model then returns ciphertext, and the handler picks encrypted delivery unless the call is marked plaintext:

// codex-rs/core/src/tools/handlers/multi_agents_v2.rs:54
if matches!(source, crate::tools::context::ToolCallSource::DirectPlaintextMessage) {
    AgentMessage::Plaintext(message)
} else {
    AgentMessage::Encrypted(message)
}

The payload ends up in InterAgentCommunication::encrypted_content, with content left empty (codex-rs/protocol/src/protocol.rs:807).

Plaintext delivery is already reachable in the local orchestration path. #35845 taught the client to classify a collaboration call carrying encrypted_function_args: [] as ToolCallSource::DirectPlaintextMessage, which routes to AgentMessage::Plaintext. But that marker is produced by the server, so a user cannot choose it, and there is no config key either: MultiAgentV2ConfigToml (codex-rs/features/src/feature_configs.rs:255) and MultiAgentV2Config (codex-rs/core/src/config/mod.rs:1298) expose concurrency, wait timeouts, usage-hint text, tool namespace, metadata visibility and model-override settings — nothing about payload representation.

Please add a documented, supported knob, for example:

[features.multi_agent_v2]
message_delivery = "plaintext"   # "encrypted" (today's default) | "plaintext"

with these semantics:

  • encrypted (default) — unchanged behaviour for everyone who is happy with it, including first-party users.
  • plaintext — the collaboration payload is serialized as readable text so the parent tool call, the rollout, and the child-side AgentMessage all carry the task text, while the child still receives a structured InterAgentCommunication.
  • Selectable through config.toml, documented in the config reference, and usable without maintaining a fork.

Rationale for wanting the option rather than a behaviour change:

Additional information

Local observation that motivated this request: with a V2 catalog entry, parent rollouts record {"message":"gAAAA..."} for spawn_agent / send_message / followup_task and no readable copy survives; with a V1 catalog entry, the identical calls record the task text verbatim.

Related:

If maintainers consider this fully covered by #28058, closing it as a duplicate is fine. It is filed separately because #28058 is scoped as a regression report about auditability, whereas this is a request for a supported configuration option that lets users choose the delivery representation.

Activity

  1. added
    enhancementNew feature or request
    CLIIssues related to the Codex CLI
    subagentIssues involving subagents or multi-agent features
    configIssues involving config.toml, config keys, config merging, or config updates
    custom-modelIssues related to custom model providers (including local models)
    on Sep 21, 2026
  2. chiuwill87 commented on Oct 1, 2026

    @chiuwill87

    Additional use case: validate the actual delegated task before native child creation.

    Released 0.159.3 still selects DirectPlaintextMessage only when a collaboration response carries explicit empty encrypted_function_args: router.rs. The inspected V2 configuration structs expose no delivery/plaintext setting. This agrees with the control gap described here.

    A metadata-only PreToolUse observation against 0.159.2 saw an opaque message with none of our task-contract labels. Explicit model/effort/task-name fields alone cannot validate the task scope, owned files, prohibited actions, verifier, or stop condition. This is an observed hook-boundary limitation, not an assertion about the private encryption implementation.

    Requested supported interface: either authenticated readable canonical task content at the pre-child enforcement boundary, bound to the actual native invocation, or equivalent trusted host-side validation before allocation. A second agent-supplied task copy or merely relabeling opaque bytes would not establish that binding.

    Acceptance: compliant bound task accepted; missing/unreadable/mismatched task denied before any child exists; child runtime identity verified separately. Mandatory validator failure denial is an additional dependency tracked in #41979.

    Could maintainers identify the supported producer/resolver owner and whether an existing transport control can provide this boundary? Source inspection and observed 0.159.3 worker metadata do not establish a working plaintext route; no 0.159.3 live plaintext proof is claimed.

  3. congee949 commented on Oct 3, 2026

    @congee949

    Additional real-provider evidence from Codex App's bundled CLI 0.159.2 on macOS. A V1 control successfully delegated research tasks from a ChatGPT-backed GPT lead to explicit Opus and Grok children; the corresponding encrypted V2 assignments were rejected before reaching the third-party providers.

    Tested topology and failure

    • Bundled CLI: 0.159.2; the separate standalone 0.160.0 was not used.
    • Gateway: official Magpie 0.1.702, commit 0a10be9cf9949b664897559293c6b43c52dc82ef.
    • Native openai provider through Magpie's /backend-api/codex.
    • Lead: group/auto-gpt-6-1-sol, actually served by a ChatGPT account as codex/gpt-6.1-sol.
    • Children: pure third-party Opus and Grok groups, explicitly selected with fork_turns:"none".

    On 2026-10-03, the two real V2 spawn_agent.message values were sealed gAAAAA… payloads. Magpie returned HTTP 400 before selecting a third-party provider. A GPT child control executed its task and produced a verified file. This isolates the failure to the assignment representation/provider boundary rather than the absence of a general dispatch mechanism.

    Magpie #619 already handles a different case: a destination group that contains a readable ChatGPT account. These pure third-party targets remain outside that repair.

    Real V1 control

    At 13:15:58–13:19:21 Asia/Shanghai on 2026-10-03, an isolated run of the same bundled 0.159.2:

    • copied the existing model catalog without removing entries;
    • set only the GPT lead group's multi_agent_version to "v1";
    • disabled the effective features.multi_agent_v2, enabled agents, and started a fresh root;
    • spawned two native V1 children with fork_context=false and explicit model overrides.
    Role Verified gateway provider/model
    Lead codex/gpt-6.1-sol
    Opus child cursor/claude-opus-5-5
    Grok child grok/grok-4.7-build-fast

    Both children actually read research material, wrote Chinese analyses with source references, ran Python, and generated unpredictable nonce/SHA256 artifacts. Both then completed a separate send_input follow-up. The lead read all artifacts; an independent checker recomputed all four hashes successfully. Persistent gateway records were filtered by the root and child thread IDs, so concurrent experiments were excluded. The third-party children were not silently replaced by GPT.

    Root and child metadata/turn contexts recorded V1, and the actual spawn task arguments were readable. This is a successful native task and follow-up control, not a synthetic empty-array marker.

    An additional run at 13:40:15–13:44:29 Asia/Shanghai verified actual online research: the Opus child executed gh api against official Codex PRs #26210/#35845, while the Grok child fetched Magpie #141 and this issue. Both produced analyses and source files; the lead waited and read them. Independent re-fetches matched the four objects' number/title/state/URL/update time/excerpt, and all source-file hashes matched. Gateway records confirmed the same three real provider/model identities.

    Research-output errors were retained separately: Opus wrote two incorrect PR update timestamps; Grok corrected two rejected floating-point tool arguments and over-inferred release status from an open issue body. Successful delivery and online execution do not certify every statement in the generated reports.

    Non-OpenAI V2 lead: partial control

    An additional isolated Opus-led V2 run successfully spawned Opus/Grok research children, received their automatic final results, and triggered a Grok follow-up. However, the Grok child did not expose send_message with the original catalog, so proactive messaging did not pass.

    A separate diagnostic changed only the two third-party catalog entries from missing multi_agent_version to "v2". The child received a readable task, but produced no new tool/artifact result before an SSE idle timeout; that attempt was stopped. It is retained as a failed diagnostic, not a working plaintext policy.

    Requested supported V2 path

    Please make the plaintext policy govern task production and classification, not only the receiving AgentMessage variant:

    1. A supported way for the lead to obtain readable parameters for spawn_agent, send_message, and followup_task, while preserving default encrypted OpenAI-to-OpenAI behavior.
    2. A documented legal entry point: a server-supported reserved collaboration schema/policy, or an officially supported ordinary-tool/Code Mode alternative. Removing encryption annotations from the reserved schema is not established as a deployable client fix.
    3. Consistent classification across direct calls, custom namespaces, Code Mode, streaming and replay, followed by provider-compatible message adaptation.
    4. Explicit rejection before child allocation or message enqueue when the representation is unreadable by the selected target.
    5. Separate acceptance for initial tasks, follow-ups, proactive messages, automatic final notifications, and actual provider/model identity.

    The inspected 0.159.2 path still requires explicit empty encrypted_function_args and the reserved collaboration namespace for DirectPlaintextMessage; Code Mode is not classified as plaintext there. Marking already sealed bytes as plaintext cannot recover the task.

    This request is cross-referenced with #34833 and #36376. The V1 workaround changes the collaboration protocol and tools, so it does not replace the requested V2 control.

    All runtime tests used isolated configuration/history. No installed binary, gateway configuration, model group, or global feature was changed. An isolated app-server config/read also verified trusted project configuration precedence, but native App UI initiation was not exercised: the available UI tool refused access to com.openai.codex. The successful evidence is explicitly bundled-backend execution, not a claim of complete UI or V2 lifecycle acceptance.

  4. cgasgarth commented on Oct 5, 2026

    @cgasgarth

    Verified a small local plaintext-V2 prototype on October 4, 2026, built from official rust-v0.160.0 source, without adopting a third-party fork.

    The source patch changes two files:

    1. When an explicit local environment flag is enabled, omit the encryption annotation on message for spawn_agent, send_message, and followup_task.
    2. Route these newly generated arguments into the existing AgentMessage::Plaintext path, including Code Mode calls, rather than requiring the server-supplied empty encrypted-argument marker.

    The test configuration uses the ordinary agents namespace, not the reserved collaboration schema. An actual GPT request through VibeProxy accepted this schema and returned readable function arguments (HTTP 200). This is producer-side plaintext selection, not decryption or relabeling sealed bytes.

    Real execution results:

    Test Result
    GPT Sol → Claude Fable, Claude Opus, GPT Luna All three children completed initial shell/file tasks, messages, and follow-up shell/file tasks
    Claude Opus → GPT Luna Initial task, messages, and follow-up completed
    App-server JSON-RPC: GPT Sol → Opus → GPT Luna Grandchild wrote a nonce-bearing computed artifact; both parents verified it and received results

    Children used fork_turns="none". Independent checks validated the generated files. Saved runtime records confirmed the requested model IDs, vibeproxy provider, and V2 for all nine sessions; 34 inter-agent input records contained zero encrypted content parts. All 12 existing multi-agent schema tests passed with the flag disabled.

    Patch, launch wrapper, reproducible real-request probes, and sanitized verification summary:
    https://github.com/cgasgarth/skills/tree/e5b3134/patches/v2-plaintext

    Scope: isolated CLI and app-server backend execution, not a claim that desktop UI initiation has been verified. The installed app was not modified or restarted during these tests. Its CODEX_CLI_PATH override can select the external backend. Existing encrypted history is not decrypted. Images, compaction, resumed encrypted chats, and other providers were not tested. This remains a local prototype, not a supported upstream config setting.

    A supported upstream plaintext delivery policy covering both schema production and message classification would remove the need for this patch. Related: #37197 and #34833.

  5. cgasgarth commented on Oct 5, 2026

    @cgasgarth

    Confirmed a configuration-only solution on October 5, 2026, using the official signed Codex 0.160.0 backend and VibeProxy's bundled CLIProxyAPI 8.0.4. This supersedes my earlier local-build prototype.

    In VibeProxy's persistent config, enable the existing setting:

    codex:
      optimize-multi-agent-v2: true

    In Codex, enable features.multi_agent_v2.enabled = true, use the normal collaboration namespace, and mark the model catalog entries multi_agent_version: "v2". That last step matters: Codex's V2 child-tool visibility check requires the child model's catalog entry to advertise V2. A global V2 feature override alone can spawn the child while leaving its collaboration tools unavailable. Restart the app after changing the catalog.

    Verified through two actual desktop-app chats, created with the native create_thread tool:

    Direction Initial task Explicit send_message Same-child follow-up
    GPT Sol → Claude Opus Executed shell arithmetic; correct result Separate MESSAGE reached the parent Executed arithmetic and delivered another separate MESSAGE
    Claude Opus → GPT Sol Executed shell arithmetic; correct result Separate MESSAGE reached the parent Executed arithmetic and delivered another separate MESSAGE

    These were app chats, not CLI-only or standalone app-server probes. Native thread creation, follow-up messaging, thread reads, and waits remained available. No custom Codex binary, app-bundle patch, signing workaround, or test suite is needed.

    The earlier locally built Codex backend was incompatible with the packaged app's native-tool peer authorization (missing-code-signing-identity), matching #34583. It has been removed. Keeping the official signed backend avoids that regression.

    The existing proxy implementation removes task-encryption annotations and adapts the namespace and inter-agent inputs. It does not decrypt old sealed tasks. Sources:

    This confirms the two directions above on this setup; it is not a claim about all proxies or all providers.

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 CLIconfigIssues involving config.toml, config keys, config merging, or config updatescustom-modelIssues related to custom model providers (including local models)enhancementNew feature or requestsubagentIssues 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