mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add join key for MAv2 inter-agent messages (#28561)
## Summary This keeps inter-agent communication on the existing raw response item path and adds a join key for MAv2 tool calls. MAv2 `spawn_agent`, `send_message`, and `followup_task` now stamp the originating tool call id into `ResponseItemMetadata.source_call_id` on the raw `ResponseItem::AgentMessage`. App-server clients can join that raw item back to the existing tool/activity event by call id, while using the raw agent message's existing sender, receiver, and content fields. No new app-server `ThreadItem` or notification type is added. ## Tests - `just fmt` - `just write-app-server-schema` - `just test -p codex-protocol` - `just test -p codex-app-server-protocol` - `just test -p codex-core multi_agent_v2_spawn_returns_path_and_send_message_accepts_relative_path` - `just test -p codex-core multi_agent_v2_followup_task_completion_notifies_parent_on_every_turn` - `just fix -p codex-protocol` - `just fix -p codex-app-server-protocol` - `just fix -p codex-core`
This commit is contained in:
@@ -2940,6 +2940,12 @@
|
||||
},
|
||||
"ResponseItemMetadata": {
|
||||
"properties": {
|
||||
"source_call_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"turn_id": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
+6
@@ -15424,6 +15424,12 @@
|
||||
},
|
||||
"ResponseItemMetadata": {
|
||||
"properties": {
|
||||
"source_call_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"turn_id": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
+6
@@ -11877,6 +11877,12 @@
|
||||
},
|
||||
"ResponseItemMetadata": {
|
||||
"properties": {
|
||||
"source_call_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"turn_id": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
+6
@@ -1035,6 +1035,12 @@
|
||||
},
|
||||
"ResponseItemMetadata": {
|
||||
"properties": {
|
||||
"source_call_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"turn_id": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -1106,6 +1106,12 @@
|
||||
},
|
||||
"ResponseItemMetadata": {
|
||||
"properties": {
|
||||
"source_call_id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"turn_id": {
|
||||
"type": [
|
||||
"string",
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type ResponseItemMetadata = { turn_id?: string, };
|
||||
export type ResponseItemMetadata = { turn_id?: string, source_call_id?: string, };
|
||||
|
||||
@@ -238,6 +238,7 @@ fn build_compacted_history_preserves_user_message_metadata() {
|
||||
message: "first user message".to_string(),
|
||||
metadata: Some(ResponseItemMetadata {
|
||||
turn_id: Some("turn-1".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
}],
|
||||
"summary text",
|
||||
|
||||
@@ -1054,6 +1054,7 @@ fn record_items_truncates_function_call_output_content() {
|
||||
},
|
||||
metadata: Some(ResponseItemMetadata {
|
||||
turn_id: Some("turn-1".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
@@ -1606,6 +1606,7 @@ async fn reconstruct_history_uses_replacement_history_verbatim() {
|
||||
phase: None,
|
||||
metadata: Some(ResponseItemMetadata {
|
||||
turn_id: Some("compact-turn".to_string()),
|
||||
..Default::default()
|
||||
}),
|
||||
};
|
||||
let replacement_history = vec![
|
||||
|
||||
@@ -1195,6 +1195,11 @@ async fn multi_agent_v2_spawn_returns_path_and_send_message_accepts_relative_pat
|
||||
&& communication.other_recipients.is_empty()
|
||||
&& communication.content.is_empty()
|
||||
&& communication.encrypted_content.as_deref() == Some("encrypted-spawn-message")
|
||||
&& communication
|
||||
.metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.source_call_id.as_deref())
|
||||
== Some("call-1")
|
||||
&& communication.trigger_turn
|
||||
)
|
||||
}));
|
||||
@@ -1222,6 +1227,11 @@ async fn multi_agent_v2_spawn_returns_path_and_send_message_accepts_relative_pat
|
||||
&& communication.other_recipients.is_empty()
|
||||
&& communication.content.is_empty()
|
||||
&& communication.encrypted_content.as_deref() == Some("encrypted-send-message")
|
||||
&& communication
|
||||
.metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.source_call_id.as_deref())
|
||||
== Some("call-1")
|
||||
&& !communication.trigger_turn
|
||||
)
|
||||
}));
|
||||
@@ -2027,6 +2037,23 @@ async fn multi_agent_v2_followup_task_completion_notifies_parent_on_every_turn()
|
||||
.await
|
||||
.expect("followup_task should succeed");
|
||||
|
||||
assert!(manager.captured_ops().iter().any(|(id, op)| {
|
||||
*id == agent_id
|
||||
&& matches!(
|
||||
op,
|
||||
Op::InterAgentCommunication { communication }
|
||||
if communication.author == AgentPath::root()
|
||||
&& communication.recipient == worker_path
|
||||
&& communication.encrypted_content.as_deref() == Some("continue")
|
||||
&& communication
|
||||
.metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.source_call_id.as_deref())
|
||||
== Some("call-1")
|
||||
&& communication.trigger_turn
|
||||
)
|
||||
}));
|
||||
|
||||
let second_turn = thread.codex.session.new_default_turn().await;
|
||||
thread
|
||||
.codex
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
use super::*;
|
||||
use crate::tools::context::FunctionToolOutput;
|
||||
use crate::turn_timing::now_unix_timestamp_ms;
|
||||
use codex_protocol::models::ResponseItemMetadata;
|
||||
use codex_protocol::protocol::InterAgentCommunication;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
@@ -99,8 +100,12 @@ pub(crate) async fn handle_message_string_tool(
|
||||
.session_source
|
||||
.get_agent_path()
|
||||
.unwrap_or_else(AgentPath::root);
|
||||
let communication =
|
||||
let mut communication =
|
||||
communication_from_tool_message(author, receiver_agent_path.clone(), message);
|
||||
communication
|
||||
.metadata
|
||||
.get_or_insert_with(ResponseItemMetadata::default)
|
||||
.source_call_id = Some(call_id.clone());
|
||||
let result = session
|
||||
.services
|
||||
.agent_control
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::tools::handlers::multi_agents_spec::SpawnAgentToolOptions;
|
||||
use crate::tools::handlers::multi_agents_spec::create_spawn_agent_tool_v2;
|
||||
use crate::turn_timing::now_unix_timestamp_ms;
|
||||
use codex_protocol::AgentPath;
|
||||
use codex_protocol::models::ResponseItemMetadata;
|
||||
use codex_protocol::protocol::Op;
|
||||
use codex_tools::ToolSpec;
|
||||
|
||||
@@ -117,8 +118,12 @@ async fn handle_spawn_agent(
|
||||
.session_source
|
||||
.get_agent_path()
|
||||
.unwrap_or_else(AgentPath::root);
|
||||
let communication =
|
||||
let mut communication =
|
||||
communication_from_tool_message(author, new_agent_path.clone(), message);
|
||||
communication
|
||||
.metadata
|
||||
.get_or_insert_with(ResponseItemMetadata::default)
|
||||
.source_call_id = Some(call_id.clone());
|
||||
Op::InterAgentCommunication { communication }
|
||||
}
|
||||
initial_operation => initial_operation,
|
||||
|
||||
@@ -1088,6 +1088,9 @@ async fn encrypted_multi_agent_v2_spawn_sends_agent_message_to_child() -> Result
|
||||
"type": "agent_message",
|
||||
"author": "/root",
|
||||
"recipient": "/root/worker",
|
||||
"metadata": {
|
||||
"source_call_id": SPAWN_CALL_ID,
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
|
||||
@@ -909,6 +909,9 @@ pub struct ResponseItemMetadata {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub turn_id: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(optional)]
|
||||
pub source_call_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema, TS)]
|
||||
@@ -2072,6 +2075,7 @@ mod tests {
|
||||
fn response_item_metadata(turn_id: &str) -> ResponseItemMetadata {
|
||||
ResponseItemMetadata {
|
||||
turn_id: Some(turn_id.to_string()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user