mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
## Why Multi-agent v2 `send_message` deliveries already reach the receiving model as typed `agent_message` items with encrypted content. Child-completion notifications are generated by Codex itself, so their content is plaintext and previously fell back to a serialized JSON envelope inside an assistant message. With plaintext `input_text` supported for `agent_message`, both delivery paths can use the same model-visible type while preserving explicit author and recipient metadata. ## What changed - add plaintext `input_text` support to `AgentMessageInputContent` and regenerate the affected app-server schemas - preserve `InterAgentCommunication` as structured mailbox input instead of converting it to assistant text - record delivered communications as typed `agent_message` history items - persist a dedicated rollout item so local delivery metadata such as `trigger_turn` remains available without leaking into the Responses request - reconstruct typed agent messages on resume and preserve fork-turn truncation behavior - remove request-time assistant-content parsing - preserve plaintext and encrypted inter-agent deliveries in stage-one memory inputs - normalize and link plaintext and encrypted agent messages in rollout traces without treating inbound messages as child results - cover the real MultiAgent V2 child-completion path end to end with deterministic mailbox synchronization ## Verification - `just test -p codex-core plaintext_multi_agent_v2_completion_sends_agent_message` - `just test -p codex-core input_queue_drains_mailbox_in_delivery_order record_initial_history_reconstructs_typed_inter_agent_message fork_turn_positions_use_inter_agent_delivery_metadata` - `just test -p codex-memories-write serializes_inter_agent_communications_for_memory` - `just test -p codex-rollout-trace agent_messages_preserve_routing_and_content sub_agent_started_activity_creates_spawn_edge` - `just test -p codex-rollout-trace agent_result_edge_falls_back_to_child_thread_without_result_message` - `just test -p codex-protocol -p codex-rollout -p codex-app-server-protocol`
192 lines
6.9 KiB
Rust
192 lines
6.9 KiB
Rust
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
|
|
use crate::payload::RawPayloadId;
|
|
|
|
use super::AgentPath;
|
|
use super::AgentThreadId;
|
|
use super::CodeCellId;
|
|
use super::CodexTurnId;
|
|
use super::CompactionId;
|
|
use super::ConversationItemId;
|
|
use super::EdgeId;
|
|
use super::InferenceCallId;
|
|
use super::ModelVisibleCallId;
|
|
use super::ToolCallId;
|
|
use super::session::ExecutionWindow;
|
|
|
|
/// One logical transcript item or transcript boundary.
|
|
///
|
|
/// The reducer builds conversation items primarily from inference request and
|
|
/// response payloads. Runtime objects can be listed in `produced_by`, but they
|
|
/// must not rewrite what the item body says the model saw. Structural items,
|
|
/// such as compaction markers, live in the same ordered list so conversation
|
|
/// views can show where the live history changed.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct ConversationItem {
|
|
pub item_id: ConversationItemId,
|
|
pub thread_id: AgentThreadId,
|
|
/// Runtime activation that first introduced this item locally, when known.
|
|
pub codex_turn_id: Option<CodexTurnId>,
|
|
pub first_seen_at_unix_ms: i64,
|
|
pub role: ConversationRole,
|
|
/// Codex channel for assistant/tool content, when the item is channel-specific.
|
|
pub channel: Option<ConversationChannel>,
|
|
pub kind: ConversationItemKind,
|
|
/// Routing metadata carried by a Responses `agent_message` item.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
pub agent_message: Option<AgentMessageMetadata>,
|
|
pub body: ConversationBody,
|
|
/// Protocol/model `call_id` for function/custom tool call and output items.
|
|
pub call_id: Option<ModelVisibleCallId>,
|
|
/// Runtime or control-plane objects that caused this conversation item to exist.
|
|
pub produced_by: Vec<ProducerRef>,
|
|
}
|
|
|
|
/// Sender and destination identities attached to a model-visible agent message.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct AgentMessageMetadata {
|
|
/// Agent path that authored the message.
|
|
pub author: AgentPath,
|
|
/// Agent path that received the message.
|
|
pub recipient: AgentPath,
|
|
}
|
|
|
|
/// Model-visible role assigned to a conversation item.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum ConversationRole {
|
|
System,
|
|
Developer,
|
|
User,
|
|
Assistant,
|
|
Tool,
|
|
}
|
|
|
|
/// Codex channel for model-visible content.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum ConversationChannel {
|
|
Analysis,
|
|
Commentary,
|
|
Final,
|
|
/// Remote compaction summaries are reintroduced as assistant summary-channel content.
|
|
Summary,
|
|
}
|
|
|
|
/// Responses item category after normalization into the reduced transcript.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum ConversationItemKind {
|
|
Message,
|
|
Reasoning,
|
|
FunctionCall,
|
|
FunctionCallOutput,
|
|
CustomToolCall,
|
|
CustomToolCallOutput,
|
|
/// Structural marker inserted where live history was replaced by compaction.
|
|
CompactionMarker,
|
|
}
|
|
|
|
/// Ordered content parts for a reduced conversation item.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct ConversationBody {
|
|
/// Renderable model-visible parts. Raw payload refs are used when the bytes
|
|
/// are too large or too structured for the normal conversation path.
|
|
pub parts: Vec<ConversationPart>,
|
|
}
|
|
|
|
/// One model-visible part inside a conversation item.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case", tag = "type")]
|
|
pub enum ConversationPart {
|
|
Text {
|
|
text: String,
|
|
},
|
|
/// A model-provided summary of content whose full form may also be present.
|
|
///
|
|
/// Reasoning summaries are not interchangeable with raw reasoning text:
|
|
/// both can be present in one payload, and replay/debug tooling needs to
|
|
/// preserve which representation the model actually returned.
|
|
Summary {
|
|
text: String,
|
|
},
|
|
/// Opaque model-visible content that is intentionally not decoded here.
|
|
///
|
|
/// Reasoning can be carried as `encrypted_content` with no readable text.
|
|
/// Keeping that blob inline makes it part of item identity, unlike a raw
|
|
/// payload reference whose ID changes every time the same item is replayed
|
|
/// in a later inference request.
|
|
Encoded {
|
|
label: String,
|
|
value: String,
|
|
},
|
|
/// Small JSON-ish body represented by a summary plus a raw ref.
|
|
Json {
|
|
summary: String,
|
|
raw_payload_id: RawPayloadId,
|
|
},
|
|
Code {
|
|
language: String,
|
|
source: String,
|
|
},
|
|
/// Large or uncommon payload that should be lazy-loaded from details UI.
|
|
PayloadRef {
|
|
label: String,
|
|
raw_payload_id: RawPayloadId,
|
|
},
|
|
}
|
|
|
|
/// Explanation for where a conversation item came from.
|
|
///
|
|
/// This is deliberately plural at the call site: a function output can be both
|
|
/// model-visible conversation and the product of a runtime tool call.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case", tag = "type")]
|
|
pub enum ProducerRef {
|
|
UserInput,
|
|
Inference { inference_call_id: InferenceCallId },
|
|
Tool { tool_call_id: ToolCallId },
|
|
CodeCell { code_cell_id: CodeCellId },
|
|
InteractionEdge { edge_id: EdgeId },
|
|
Compaction { compaction_id: CompactionId },
|
|
Harness,
|
|
}
|
|
|
|
/// One outbound inference request and its response metadata.
|
|
///
|
|
/// Full upstream request/response bodies live behind raw payload refs. The
|
|
/// request/response item ID lists are the reduced, model-visible snapshot.
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct InferenceCall {
|
|
pub inference_call_id: InferenceCallId,
|
|
pub thread_id: AgentThreadId,
|
|
pub codex_turn_id: CodexTurnId,
|
|
pub execution: ExecutionWindow,
|
|
pub model: String,
|
|
pub provider_name: String,
|
|
/// Responses API response id, used by follow-up `previous_response_id` requests.
|
|
pub response_id: Option<String>,
|
|
/// Request id returned by HTTP/proxy/engine infrastructure.
|
|
pub upstream_request_id: Option<String>,
|
|
/// Complete ordered input snapshot sent with this request.
|
|
pub request_item_ids: Vec<ConversationItemId>,
|
|
/// Ordered output items produced by this response.
|
|
pub response_item_ids: Vec<ConversationItemId>,
|
|
/// Runtime tool calls whose model-visible call item came from this response.
|
|
pub tool_call_ids_started_by_response: Vec<ToolCallId>,
|
|
pub usage: Option<TokenUsage>,
|
|
pub raw_request_payload_id: RawPayloadId,
|
|
/// Full upstream response payload. `None` while running or after pre-stream failures.
|
|
pub raw_response_payload_id: Option<RawPayloadId>,
|
|
}
|
|
|
|
/// Token usage summary for one inference call.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub struct TokenUsage {
|
|
pub input_tokens: u64,
|
|
pub cached_input_tokens: u64,
|
|
pub output_tokens: u64,
|
|
pub reasoning_output_tokens: u64,
|
|
}
|