Repository navigation
Add a config option to select plaintext delivery for Multi-Agent V2 task messages #46939
Description
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 231The 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-sideAgentMessageall carry the task text, while the child still receives a structuredInterAgentCommunication.- 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:
- Auditability. Encrypted-only payloads make it impossible to answer "what task did this
spawn_agentcall give the child?" from a rollout. This is the subject of Regression: encrypted MultiAgentV2 messages remove readable task audit trail #28058. - Reproducibility. Subagent runs cannot be re-executed or compared for evaluations when the delegated instruction is not recoverable; two runs that differ may differ only in prompt text.
- Interoperability. A child on a non-OpenAI Responses-compatible provider receives
An encrypted inter-agent message occurred here but is not portable to ...instead of the task (MultiAgentV2 cross-provider subagent cannot consume encrypted task assignment #34833, Subagent task payload invisible to custom non-OpenAI providers (DeepSeek): encrypted_content block dropped with multi_agent_version v2 #36586,0.147.0-alpha.4: OpenAI parent still sends encrypted V2 task to non-OpenAI child after #35845 #36376, MultiAgentV2 plaintext support in 0.147.0-alpha.1 does not complete cross-provider task delivery #37197). Since V2 orchestration is client-side, plaintext delivery does not depend on server-side decryption. - Precedent. Models whose catalog entry sets
multi_agent_versionto v1 already pass the task as readable text, and Support plaintext collaboration tool messages #35845 already ships the plaintext path. The missing piece is only user-facing control.
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:
- Regression: encrypted MultiAgentV2 messages remove readable task audit trail #28058 — regression tracking for the lost audit trail. The configurable delivery policy it sketches (
message_delivery = "encrypted" | "encrypted_with_audit" | "plaintext") exists only in a fork, not upstream. - Clarify encrypted inter-agent messages in multi-agent v2 #43893 — asks for documentation of what is encrypted, where decryption happens, and whether plaintext interoperability is supported. Currently unanswered.
- Support plaintext collaboration tool messages #35845 — merged plaintext delivery support, gated behind a server-supplied marker.
- Document / gate Ultra multi-agent features when using API-key custom model_providers #37859 — documentation/gating request for custom API-key providers.
- MultiAgentV2 cross-provider subagent cannot consume encrypted task assignment #34833, Subagent task payload invisible to custom non-OpenAI providers (DeepSeek): encrypted_content block dropped with multi_agent_version v2 #36586,
0.147.0-alpha.4: OpenAI parent still sends encrypted V2 task to non-OpenAI child after #35845 #36376, MultiAgentV2 plaintext support in 0.147.0-alpha.1 does not complete cross-provider task delivery #37197 — cross-provider delivery failures.
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
chiuwill87 commentedon Oct 1, 2026
Additional use case: validate the actual delegated task before native child creation.
Released 0.159.3 still selects
DirectPlaintextMessageonly when a collaboration response carries explicit emptyencrypted_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.
congee949 commentedon Oct 3, 2026
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
0.159.2; the separate standalone0.160.0was not used.0.1.702, commit0a10be9cf9949b664897559293c6b43c52dc82ef.openaiprovider through Magpie's/backend-api/codex.group/auto-gpt-6-1-sol, actually served by a ChatGPT account ascodex/gpt-6.1-sol.fork_turns:"none".On 2026-10-03, the two real V2
spawn_agent.messagevalues were sealedgAAAAA…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:
multi_agent_versionto"v1";features.multi_agent_v2, enabled agents, and started a fresh root;fork_context=falseand explicit model overrides.codex/gpt-6.1-solcursor/claude-opus-5-5grok/grok-4.7-build-fastBoth 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_inputfollow-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 apiagainst 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_messagewith the original catalog, so proactive messaging did not pass.A separate diagnostic changed only the two third-party catalog entries from missing
multi_agent_versionto"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
AgentMessagevariant:spawn_agent,send_message, andfollowup_task, while preserving default encrypted OpenAI-to-OpenAI behavior.The inspected 0.159.2 path still requires explicit empty
encrypted_function_argsand the reservedcollaborationnamespace forDirectPlaintextMessage; 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/readalso verified trusted project configuration precedence, but native App UI initiation was not exercised: the available UI tool refused access tocom.openai.codex. The successful evidence is explicitly bundled-backend execution, not a claim of complete UI or V2 lifecycle acceptance.cgasgarth commentedon Oct 5, 2026
Verified a small local plaintext-V2 prototype on October 4, 2026, built from official
rust-v0.160.0source, without adopting a third-party fork.The source patch changes two files:
messageforspawn_agent,send_message, andfollowup_task.AgentMessage::Plaintextpath, including Code Mode calls, rather than requiring the server-supplied empty encrypted-argument marker.The test configuration uses the ordinary
agentsnamespace, not the reservedcollaborationschema. 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:
Children used
fork_turns="none". Independent checks validated the generated files. Saved runtime records confirmed the requested model IDs,vibeproxyprovider, 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_PATHoverride 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.
cgasgarth commentedon Oct 5, 2026
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:
In Codex, enable
features.multi_agent_v2.enabled = true, use the normalcollaborationnamespace, and mark the model catalog entriesmulti_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_threadtool:send_messageThese 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.
Deliver plaintext collaboration tasks as a standard user message