mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Encrypt multi-agent v2 message payloads (#26210)
## Why
Multi-agent v2 currently routes agent instructions through normal tool
arguments and inter-agent context. That means the parent model can emit
plaintext task text, Codex can persist it in history/rollouts, and the
recipient can receive it as ordinary assistant-message JSON.
This changes the v2 path so agent instructions stay encrypted between
model calls: Responses encrypts the `message` argument returned by the
model, Codex forwards only that ciphertext, and Responses decrypts it
internally for the recipient model.
## What changed
- Mark the v2 `message` parameter as encrypted for `spawn_agent`,
`send_message`, and `followup_task`.
- Treat multi-agent v2 tool `message` values as ciphertext
unconditionally.
- Store v2 inter-agent task text in
`InterAgentCommunication.encrypted_content` with empty plaintext
`content`.
- Convert encrypted inter-agent communications into the Responses
`agent_message` input item before sending the child request.
- Preserve `agent_message` items across history, rollout, compaction,
telemetry, and app-server schema paths.
- Leave multi-agent v1 unchanged.
## Message shape
The model still calls the v2 tools with a `message` argument, but that
value is now ciphertext:
```json
{
"name": "spawn_agent",
"arguments": {
"task_name": "worker",
"message": "<ciphertext>"
}
}
```
Codex stores the task as encrypted inter-agent communication:
```json
{
"author": "/root",
"recipient": "/root/worker",
"content": "",
"encrypted_content": "<ciphertext>",
"trigger_turn": true
}
```
When Codex builds the recipient request, it forwards the ciphertext
using the new Responses input item:
```json
{
"type": "agent_message",
"author": "/root",
"recipient": "/root/worker",
"content": [
{
"type": "encrypted_content",
"encrypted_content": "<ciphertext>"
}
]
}
```
Responses decrypts that item internally for the recipient model.
## Context impact
- Parent context no longer carries plaintext v2 agent task instructions
from these tool arguments.
- Codex rollout/history stores ciphertext for v2 agent instructions.
- Recipient requests receive an `agent_message` item instead of
assistant commentary JSON for encrypted task delivery.
- Plaintext completion/status notifications are still plaintext because
they are Codex-generated status messages, not encrypted model tool
arguments.
## Validation
- `just test -p codex-tools`
- `just test -p codex-protocol`
- `just test -p codex-rollout`
- `just test -p codex-rollout-trace`
- `just test -p codex-otel`
- `just write-app-server-schema`
This commit is contained in:
@@ -43,6 +43,9 @@ pub struct JsonSchema {
|
||||
pub schema_type: Option<JsonSchemaType>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub description: Option<String>,
|
||||
/// Responses-only marker for reviewed encrypted tool parameters.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub encrypted: Option<bool>,
|
||||
#[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
|
||||
pub enum_values: Option<Vec<JsonValue>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -90,6 +93,11 @@ impl JsonSchema {
|
||||
Self::typed(JsonSchemaPrimitiveType::String, description)
|
||||
}
|
||||
|
||||
pub fn with_encrypted(mut self) -> Self {
|
||||
self.encrypted = Some(true);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn number(description: Option<String>) -> Self {
|
||||
Self::typed(JsonSchemaPrimitiveType::Number, description)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,20 @@ fn parse_tool_input_schema_coerces_boolean_schemas() {
|
||||
assert_eq!(schema, JsonSchema::string(/*description*/ None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_schema_serializes_encrypted_marker() {
|
||||
let schema = JsonSchema::string(Some("Secret value".to_string())).with_encrypted();
|
||||
|
||||
assert_eq!(
|
||||
serde_json::to_value(schema).expect("serialize schema"),
|
||||
serde_json::json!({
|
||||
"type": "string",
|
||||
"description": "Secret value",
|
||||
"encrypted": true,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_tool_input_schema_infers_object_shape_and_defaults_properties() {
|
||||
// Example schema shape:
|
||||
|
||||
Reference in New Issue
Block a user