mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
Remove unavailable task messages from list_agents
This commit is contained in:
@@ -48,8 +48,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;
|
||||
@@ -80,7 +78,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<String>,
|
||||
}
|
||||
|
||||
/// Control-plane handle for multi-agent operations.
|
||||
@@ -154,28 +151,12 @@ impl AgentControl {
|
||||
state: &Arc<ThreadManagerState>,
|
||||
initial_operation: Op,
|
||||
) -> CodexResult<String> {
|
||||
let last_task_message = match &initial_operation {
|
||||
Op::InterAgentCommunication { communication } => {
|
||||
last_task_message_from_communication(communication)
|
||||
}
|
||||
_ => non_empty_task_message(render_input_preview(&initial_operation)),
|
||||
};
|
||||
let result = self
|
||||
.handle_thread_request_result(
|
||||
agent_id,
|
||||
state,
|
||||
state.send_op(agent_id, initial_operation).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, initial_operation).await,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn send_inter_agent_communication(
|
||||
@@ -183,22 +164,11 @@ impl AgentControl {
|
||||
agent_id: ThreadId,
|
||||
communication: InterAgentCommunication,
|
||||
) -> CodexResult<String> {
|
||||
let last_task_message = last_task_message_from_communication(&communication);
|
||||
let state = self.upgrade()?;
|
||||
let op = Op::InterAgentCommunication { communication };
|
||||
self.ensure_execution_capacity_for_op(agent_id, &op).await?;
|
||||
let result = self
|
||||
.handle_thread_request_result(agent_id, &state, state.send_op(agent_id, op).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, op).await)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Interrupt the current task for an existing agent thread.
|
||||
@@ -374,7 +344,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()),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -397,11 +366,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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -530,7 +497,6 @@ impl AgentControl {
|
||||
agent_path,
|
||||
agent_nickname,
|
||||
agent_role,
|
||||
last_task_message: None,
|
||||
};
|
||||
Ok((session_source, agent_metadata))
|
||||
}
|
||||
@@ -729,17 +695,6 @@ pub(crate) fn render_input_preview(initial_operation: &Op) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn last_task_message_from_communication(communication: &InterAgentCommunication) -> Option<String> {
|
||||
if communication.encrypted_content.is_some() {
|
||||
return None;
|
||||
}
|
||||
non_empty_task_message(communication.content.clone())
|
||||
}
|
||||
|
||||
fn non_empty_task_message(message: String) -> Option<String> {
|
||||
(!message.is_empty()).then_some(message)
|
||||
}
|
||||
|
||||
fn thread_spawn_depth(session_source: &SessionSource) -> Option<i32> {
|
||||
match session_source {
|
||||
SessionSource::SubAgent(SubAgentSource::ThreadSpawn { depth, .. }) => Some(*depth),
|
||||
|
||||
@@ -761,62 +761,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)
|
||||
.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;
|
||||
|
||||
@@ -38,7 +38,6 @@ pub(crate) struct AgentMetadata {
|
||||
pub(crate) agent_path: Option<AgentPath>,
|
||||
pub(crate) agent_nickname: Option<String>,
|
||||
pub(crate) agent_role: Option<String>,
|
||||
pub(crate) last_task_message: Option<String>,
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -435,13 +435,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."
|
||||
|
||||
@@ -393,7 +393,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"])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -179,7 +179,6 @@ struct ListAgentsResult {
|
||||
struct ListedAgentResult {
|
||||
agent_name: String,
|
||||
agent_status: serde_json::Value,
|
||||
last_task_message: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -1512,7 +1511,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
|
||||
@@ -1586,19 +1585,12 @@ async fn multi_agent_v2_list_agents_returns_completed_status_without_encrypted_s
|
||||
.map(|agent| agent.agent_name.as_str())
|
||||
.collect::<Vec<_>>();
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -1686,7 +1678,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]
|
||||
@@ -1747,10 +1738,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]
|
||||
@@ -1822,10 +1809,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());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user