From 64c0e2fa1b9560171366e840d5336563a461f2b2 Mon Sep 17 00:00:00 2001 From: jif Date: Tue, 14 Jul 2026 10:16:45 +0000 Subject: [PATCH] Remove task messages from `list_agents` output (#33030) ## What changed - Return only each live agent's name and status from `list_agents`. - Stop tracking the most recent task message in agent metadata. GitOrigin-RevId: 91836fe8efad7ea6b7305f0b409081ec77419cfd --- codex-rs/core/src/agent/control.rs | 50 ++-------------- codex-rs/core/src/agent/control_tests.rs | 60 ------------------- codex-rs/core/src/agent/registry.rs | 29 --------- .../src/tools/handlers/multi_agents_spec.rs | 6 +- .../tools/handlers/multi_agents_spec_tests.rs | 2 +- .../src/tools/handlers/multi_agents_tests.rs | 19 +----- 6 files changed, 9 insertions(+), 157 deletions(-) diff --git a/codex-rs/core/src/agent/control.rs b/codex-rs/core/src/agent/control.rs index 0c8a4d0d96db..d2f49ae3ecb7 100644 --- a/codex-rs/core/src/agent/control.rs +++ b/codex-rs/core/src/agent/control.rs @@ -50,8 +50,6 @@ pub(crate) use self::execution::AgentExecutionGuard; use self::execution::AgentExecutionLimiter; use self::residency::V2Residency; -const ROOT_LAST_TASK_MESSAGE: &str = "Main thread"; - mod execution; mod legacy; mod residency; @@ -82,7 +80,6 @@ pub(crate) struct LiveAgent { pub(crate) struct ListedAgent { pub(crate) agent_name: String, pub(crate) agent_status: AgentStatus, - pub(crate) last_task_message: Option, } /// Control-plane handle for multi-agent operations. @@ -156,23 +153,12 @@ impl AgentControl { state: &Arc, input: Vec, ) -> CodexResult { - let last_task_message = non_empty_task_message(render_input_preview(&input)); - let result = self - .handle_thread_request_result( - agent_id, - state, - state.send_op(agent_id, input.into()).await, - ) - .await; - if result.is_ok() { - match last_task_message { - Some(last_task_message) => self - .state - .update_last_task_message(agent_id, last_task_message), - None => self.state.clear_last_task_message(agent_id), - } - } - result + self.handle_thread_request_result( + agent_id, + state, + state.send_op(agent_id, input.into()).await, + ) + .await } pub(crate) async fn send_inter_agent_communication( @@ -211,7 +197,6 @@ impl AgentControl { communication: InterAgentCommunication, context: AgentCommunicationContext, ) -> CodexResult { - let last_task_message = last_task_message_from_communication(&communication); let communication_for_log = crate::agent_communication::logging_enabled().then(|| communication.clone()); let result = self @@ -233,14 +218,6 @@ impl AgentControl { agent_id, ); } - if result.is_ok() { - match last_task_message { - Some(last_task_message) => self - .state - .update_last_task_message(agent_id, last_task_message), - None => self.state.clear_last_task_message(agent_id), - } - } result } @@ -417,7 +394,6 @@ impl AgentControl { agents.push(ListedAgent { agent_name: root_path.to_string(), agent_status: root_thread.agent_status().await, - last_task_message: Some(ROOT_LAST_TASK_MESSAGE.to_string()), }); } @@ -440,11 +416,9 @@ impl AgentControl { .as_ref() .map(ToString::to_string) .unwrap_or_else(|| thread_id.to_string()); - let last_task_message = metadata.last_task_message.clone(); agents.push(ListedAgent { agent_name, agent_status: thread.agent_status().await, - last_task_message, }); } @@ -562,7 +536,6 @@ impl AgentControl { agent_path, agent_nickname, agent_role, - last_task_message: None, }) } @@ -787,17 +760,6 @@ pub(crate) fn render_input_preview(input: &[UserInput]) -> String { .join("\n") } -fn last_task_message_from_communication(communication: &InterAgentCommunication) -> Option { - if communication.encrypted_content.is_some() { - return None; - } - non_empty_task_message(communication.content.clone()) -} - -fn non_empty_task_message(message: String) -> Option { - (!message.is_empty()).then_some(message) -} - fn thread_spawn_depth(session_source: &SessionSource) -> Option { match session_source { SessionSource::SubAgent(SubAgentSource::ThreadSpawn { depth, .. }) => Some(*depth), diff --git a/codex-rs/core/src/agent/control_tests.rs b/codex-rs/core/src/agent/control_tests.rs index 2199fcf8e8e6..3b14595f7cac 100644 --- a/codex-rs/core/src/agent/control_tests.rs +++ b/codex-rs/core/src/agent/control_tests.rs @@ -775,66 +775,6 @@ async fn resume_agent_from_rollout_does_not_reopen_v2_descendants() { assert_thread_not_loaded(&resumed_manager, reviewer_thread_id).await; } -#[tokio::test] -async fn encrypted_inter_agent_communication_clears_existing_last_task_message() { - let harness = AgentControlHarness::new().await; - let (parent_thread_id, _) = harness.start_thread().await; - let agent_path = AgentPath::try_from("/root/worker").expect("agent path"); - let spawned_agent = harness - .control - .spawn_agent_with_metadata( - harness.config.clone(), - text_input("old plaintext task"), - Some(SessionSource::SubAgent(SubAgentSource::ThreadSpawn { - parent_thread_id, - depth: 1, - agent_path: Some(agent_path.clone()), - agent_nickname: None, - agent_role: None, - })), - SpawnAgentOptions { - parent_thread_id: Some(parent_thread_id), - ..Default::default() - }, - ) - .await - .expect("spawn_agent should succeed"); - assert_eq!( - harness - .control - .state - .agent_metadata_for_thread(spawned_agent.thread_id) - .and_then(|metadata| metadata.last_task_message), - Some("old plaintext task".to_string()) - ); - - let communication = InterAgentCommunication::new_encrypted( - AgentPath::root(), - agent_path, - Vec::new(), - "encrypted-task".to_string(), - /*trigger_turn*/ true, - ); - harness - .control - .send_inter_agent_communication( - spawned_agent.thread_id, - communication, - AgentCommunicationContext::new(AgentCommunicationKind::Followup, ThreadId::new()), - ) - .await - .expect("send_inter_agent_communication should succeed"); - - assert_eq!( - harness - .control - .state - .agent_metadata_for_thread(spawned_agent.thread_id) - .and_then(|metadata| metadata.last_task_message), - None - ); -} - #[tokio::test] async fn spawn_agent_creates_thread_and_sends_prompt() { let harness = AgentControlHarness::new().await; diff --git a/codex-rs/core/src/agent/registry.rs b/codex-rs/core/src/agent/registry.rs index 43aca201bfa2..eac2f14ae481 100644 --- a/codex-rs/core/src/agent/registry.rs +++ b/codex-rs/core/src/agent/registry.rs @@ -38,7 +38,6 @@ pub(crate) struct AgentMetadata { pub(crate) agent_path: Option, pub(crate) agent_nickname: Option, pub(crate) agent_role: Option, - pub(crate) last_task_message: Option, } fn format_agent_nickname(name: &str, nickname_reset_count: usize) -> String { @@ -166,34 +165,6 @@ impl AgentRegistry { .collect() } - pub(crate) fn update_last_task_message(&self, thread_id: ThreadId, last_task_message: String) { - let mut active_agents = self - .active_agents - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(metadata) = active_agents - .agent_tree - .values_mut() - .find(|metadata| metadata.agent_id == Some(thread_id)) - { - metadata.last_task_message = Some(last_task_message); - } - } - - pub(crate) fn clear_last_task_message(&self, thread_id: ThreadId) { - let mut active_agents = self - .active_agents - .lock() - .unwrap_or_else(std::sync::PoisonError::into_inner); - if let Some(metadata) = active_agents - .agent_tree - .values_mut() - .find(|metadata| metadata.agent_id == Some(thread_id)) - { - metadata.last_task_message = None; - } - } - fn register_spawned_thread(&self, agent_metadata: AgentMetadata) { let Some(thread_id) = agent_metadata.agent_id else { return; diff --git a/codex-rs/core/src/tools/handlers/multi_agents_spec.rs b/codex-rs/core/src/tools/handlers/multi_agents_spec.rs index 88fa4daefbfd..d425c8796f76 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_spec.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_spec.rs @@ -460,13 +460,9 @@ fn list_agents_output_schema() -> Value { "agent_status": { "description": "Last known status of the agent.", "allOf": [agent_status_output_schema()] - }, - "last_task_message": { - "type": ["string", "null"], - "description": "Most recent user or inter-agent instruction received by the agent, when available." } }, - "required": ["agent_name", "agent_status", "last_task_message"], + "required": ["agent_name", "agent_status"], "additionalProperties": false }, "description": "Live agents visible in the current root thread tree." diff --git a/codex-rs/core/src/tools/handlers/multi_agents_spec_tests.rs b/codex-rs/core/src/tools/handlers/multi_agents_spec_tests.rs index 065c4742e9c9..521e744d5508 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_spec_tests.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_spec_tests.rs @@ -443,7 +443,7 @@ fn list_agents_tool_includes_path_prefix_and_agent_fields() { ); assert_eq!( output_schema.expect("list_agents output schema")["properties"]["agents"]["items"]["required"], - json!(["agent_name", "agent_status", "last_task_message"]) + json!(["agent_name", "agent_status"]) ); } diff --git a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs index a248fb863f30..a7e6989a2e38 100644 --- a/codex-rs/core/src/tools/handlers/multi_agents_tests.rs +++ b/codex-rs/core/src/tools/handlers/multi_agents_tests.rs @@ -180,7 +180,6 @@ struct ListAgentsResult { struct ListedAgentResult { agent_name: String, agent_status: serde_json::Value, - last_task_message: Option, } #[derive(Debug, Deserialize)] @@ -1546,7 +1545,7 @@ async fn multi_agent_v2_followup_task_rejects_root_target_from_child() { } #[tokio::test] -async fn multi_agent_v2_list_agents_returns_completed_status_without_encrypted_spawn_preview() { +async fn multi_agent_v2_list_agents_returns_completed_status() { let (mut session, mut turn) = make_session_and_context().await; let manager = thread_manager(); let root = manager @@ -1622,19 +1621,12 @@ async fn multi_agent_v2_list_agents_returns_completed_status_without_encrypted_s .map(|agent| agent.agent_name.as_str()) .collect::>(); assert_eq!(agent_names, vec!["/root", "/root/worker"]); - let root_agent = result - .agents - .iter() - .find(|agent| agent.agent_name == "/root") - .expect("root agent should be listed"); - assert_eq!(root_agent.last_task_message.as_deref(), Some("Main thread")); let worker = result .agents .iter() .find(|agent| agent.agent_name == "/root/worker") .expect("worker agent should be listed"); assert_eq!(worker.agent_status, json!({"completed": "done"})); - assert_eq!(worker.last_task_message, None); assert_eq!(success, Some(true)); } @@ -1720,7 +1712,6 @@ async fn multi_agent_v2_list_agents_filters_by_relative_path_prefix() { assert_eq!(result.agents.len(), 1); assert_eq!(result.agents[0].agent_name, worker_path.as_str()); - assert_eq!(result.agents[0].last_task_message.as_deref(), Some("build")); } #[tokio::test] @@ -1781,10 +1772,6 @@ async fn multi_agent_v2_list_agents_omits_closed_agents() { assert_eq!(result.agents.len(), 1); assert_eq!(result.agents[0].agent_name, "/root"); - assert_eq!( - result.agents[0].last_task_message.as_deref(), - Some("Main thread") - ); } #[tokio::test] @@ -1856,10 +1843,6 @@ async fn multi_agent_v2_list_agents_keeps_interrupted_resident_agents() { assert_eq!(result.agents.len(), 2); assert_eq!(result.agents[0].agent_name, "/root"); - assert_eq!( - result.agents[0].last_task_message.as_deref(), - Some("Main thread") - ); assert_eq!(result.agents[1].agent_name, agent_path.as_str()); }