Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions codex-rs/core/src/tools/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
pub mod apply_patch;
pub(crate) mod collab;
mod dynamic;
mod grep_files;
mod js_repl;
mod list_dir;
mod mcp;
mod mcp_resource;
pub(crate) mod multi_agents;
mod plan;
mod read_file;
mod request_user_input;
Expand All @@ -20,14 +20,14 @@ use serde::Deserialize;

use crate::function_tool::FunctionCallError;
pub use apply_patch::ApplyPatchHandler;
pub use collab::CollabHandler;
pub use dynamic::DynamicToolHandler;
pub use grep_files::GrepFilesHandler;
pub use js_repl::JsReplHandler;
pub use js_repl::JsReplResetHandler;
pub use list_dir::ListDirHandler;
pub use mcp::McpHandler;
pub use mcp_resource::McpResourceHandler;
pub use multi_agents::MultiAgentHandler;
pub use plan::PlanHandler;
pub use read_file::ReadFileHandler;
pub use request_user_input::RequestUserInputHandler;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use codex_protocol::user_input::UserInput;
use serde::Deserialize;
use serde::Serialize;

pub struct CollabHandler;
pub struct MultiAgentHandler;

/// Minimum wait timeout to prevent tight polling loops from burning CPU.
pub(crate) const MIN_WAIT_TIMEOUT_MS: i64 = 10_000;
Expand All @@ -47,7 +47,7 @@ struct CloseAgentArgs {
}

#[async_trait]
impl ToolHandler for CollabHandler {
impl ToolHandler for MultiAgentHandler {
fn kind(&self) -> ToolKind {
ToolKind::Function
}
Expand Down Expand Up @@ -917,7 +917,7 @@ mod tests {
input: "hello".to_string(),
},
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("payload should be rejected");
};
assert_eq!(
Expand All @@ -937,7 +937,7 @@ mod tests {
"unknown_tool",
function_payload(json!({})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("tool should be rejected");
};
assert_eq!(
Expand All @@ -955,7 +955,7 @@ mod tests {
"spawn_agent",
function_payload(json!({"message": " "})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("empty message should be rejected");
};
assert_eq!(
Expand All @@ -978,7 +978,7 @@ mod tests {
"items": [{"type": "mention", "name": "drive", "path": "app://drive"}]
})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("message+items should be rejected");
};
assert_eq!(
Expand Down Expand Up @@ -1016,7 +1016,7 @@ mod tests {
"agent_type": "explorer"
})),
);
let output = CollabHandler
let output = MultiAgentHandler
.handle(invocation)
.await
.expect("spawn_agent should succeed");
Expand Down Expand Up @@ -1048,7 +1048,7 @@ mod tests {
"spawn_agent",
function_payload(json!({"message": "hello"})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("spawn should fail without a manager");
};
assert_eq!(
Expand All @@ -1074,7 +1074,7 @@ mod tests {
"spawn_agent",
function_payload(json!({"message": "hello"})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("spawn should fail when depth limit exceeded");
};
assert_eq!(
Expand All @@ -1094,7 +1094,7 @@ mod tests {
"send_input",
function_payload(json!({"id": ThreadId::new().to_string(), "message": ""})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("empty message should be rejected");
};
assert_eq!(
Expand All @@ -1118,7 +1118,7 @@ mod tests {
"items": [{"type": "mention", "name": "drive", "path": "app://drive"}]
})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("message+items should be rejected");
};
assert_eq!(
Expand All @@ -1138,7 +1138,7 @@ mod tests {
"send_input",
function_payload(json!({"id": "not-a-uuid", "message": "hi"})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("invalid id should be rejected");
};
let FunctionCallError::RespondToModel(msg) = err else {
Expand All @@ -1159,7 +1159,7 @@ mod tests {
"send_input",
function_payload(json!({"id": agent_id.to_string(), "message": "hi"})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("missing agent should be reported");
};
assert_eq!(
Expand All @@ -1186,7 +1186,7 @@ mod tests {
"interrupt": true
})),
);
CollabHandler
MultiAgentHandler
.handle(invocation)
.await
.expect("send_input should succeed");
Expand Down Expand Up @@ -1227,7 +1227,7 @@ mod tests {
]
})),
);
CollabHandler
MultiAgentHandler
.handle(invocation)
.await
.expect("send_input should succeed");
Expand Down Expand Up @@ -1267,7 +1267,7 @@ mod tests {
"resume_agent",
function_payload(json!({"id": "not-a-uuid"})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("invalid id should be rejected");
};
let FunctionCallError::RespondToModel(msg) = err else {
Expand All @@ -1288,7 +1288,7 @@ mod tests {
"resume_agent",
function_payload(json!({"id": agent_id.to_string()})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("missing agent should be reported");
};
assert_eq!(
Expand All @@ -1313,7 +1313,7 @@ mod tests {
function_payload(json!({"id": agent_id.to_string()})),
);

let output = CollabHandler
let output = MultiAgentHandler
.handle(invocation)
.await
.expect("resume_agent should succeed");
Expand Down Expand Up @@ -1382,7 +1382,7 @@ mod tests {
"resume_agent",
function_payload(json!({"id": agent_id.to_string()})),
);
let output = CollabHandler
let output = MultiAgentHandler
.handle(resume_invocation)
.await
.expect("resume_agent should succeed");
Expand All @@ -1405,7 +1405,7 @@ mod tests {
"send_input",
function_payload(json!({"id": agent_id.to_string(), "message": "hello"})),
);
let output = CollabHandler
let output = MultiAgentHandler
.handle(send_invocation)
.await
.expect("send_input should succeed after resume");
Expand Down Expand Up @@ -1450,7 +1450,7 @@ mod tests {
"resume_agent",
function_payload(json!({"id": ThreadId::new().to_string()})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("resume should fail when depth limit exceeded");
};
assert_eq!(
Expand Down Expand Up @@ -1479,7 +1479,7 @@ mod tests {
"timeout_ms": 0
})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("non-positive timeout should be rejected");
};
assert_eq!(
Expand All @@ -1497,7 +1497,7 @@ mod tests {
"wait",
function_payload(json!({"ids": ["invalid"]})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("invalid id should be rejected");
};
let FunctionCallError::RespondToModel(msg) = err else {
Expand All @@ -1515,7 +1515,7 @@ mod tests {
"wait",
function_payload(json!({"ids": []})),
);
let Err(err) = CollabHandler.handle(invocation).await else {
let Err(err) = MultiAgentHandler.handle(invocation).await else {
panic!("empty ids should be rejected");
};
assert_eq!(
Expand All @@ -1540,7 +1540,7 @@ mod tests {
"timeout_ms": 1000
})),
);
let output = CollabHandler
let output = MultiAgentHandler
.handle(invocation)
.await
.expect("wait should succeed");
Expand Down Expand Up @@ -1584,7 +1584,7 @@ mod tests {
"timeout_ms": MIN_WAIT_TIMEOUT_MS
})),
);
let output = CollabHandler
let output = MultiAgentHandler
.handle(invocation)
.await
.expect("wait should succeed");
Expand Down Expand Up @@ -1632,7 +1632,11 @@ mod tests {
})),
);

let early = timeout(Duration::from_millis(50), CollabHandler.handle(invocation)).await;
let early = timeout(
Duration::from_millis(50),
MultiAgentHandler.handle(invocation),
)
.await;
assert!(
early.is_err(),
"wait should not return before the minimum timeout clamp"
Expand Down Expand Up @@ -1677,7 +1681,7 @@ mod tests {
"timeout_ms": 1000
})),
);
let output = CollabHandler
let output = MultiAgentHandler
.handle(invocation)
.await
.expect("wait should succeed");
Expand Down Expand Up @@ -1717,7 +1721,7 @@ mod tests {
"close_agent",
function_payload(json!({"id": agent_id.to_string()})),
);
let output = CollabHandler
let output = MultiAgentHandler
.handle(invocation)
.await
.expect("close_agent should succeed");
Expand Down
20 changes: 10 additions & 10 deletions codex-rs/core/src/tools/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use crate::tools::handlers::SEARCH_TOOL_BM25_DEFAULT_LIMIT;
use crate::tools::handlers::SEARCH_TOOL_BM25_TOOL_NAME;
use crate::tools::handlers::apply_patch::create_apply_patch_freeform_tool;
use crate::tools::handlers::apply_patch::create_apply_patch_json_tool;
use crate::tools::handlers::collab::DEFAULT_WAIT_TIMEOUT_MS;
use crate::tools::handlers::collab::MAX_WAIT_TIMEOUT_MS;
use crate::tools::handlers::collab::MIN_WAIT_TIMEOUT_MS;
use crate::tools::handlers::multi_agents::DEFAULT_WAIT_TIMEOUT_MS;
use crate::tools::handlers::multi_agents::MAX_WAIT_TIMEOUT_MS;
use crate::tools::handlers::multi_agents::MIN_WAIT_TIMEOUT_MS;
use crate::tools::handlers::request_user_input_tool_description;
use crate::tools::registry::ToolRegistryBuilder;
use codex_protocol::config_types::WebSearchMode;
Expand Down Expand Up @@ -1405,14 +1405,14 @@ pub(crate) fn build_specs(
dynamic_tools: &[DynamicToolSpec],
) -> ToolRegistryBuilder {
use crate::tools::handlers::ApplyPatchHandler;
use crate::tools::handlers::CollabHandler;
use crate::tools::handlers::DynamicToolHandler;
use crate::tools::handlers::GrepFilesHandler;
use crate::tools::handlers::JsReplHandler;
use crate::tools::handlers::JsReplResetHandler;
use crate::tools::handlers::ListDirHandler;
use crate::tools::handlers::McpHandler;
use crate::tools::handlers::McpResourceHandler;
use crate::tools::handlers::MultiAgentHandler;
use crate::tools::handlers::PlanHandler;
use crate::tools::handlers::ReadFileHandler;
use crate::tools::handlers::RequestUserInputHandler;
Expand Down Expand Up @@ -1574,17 +1574,17 @@ pub(crate) fn build_specs(
builder.register_handler("view_image", view_image_handler);

if config.collab_tools {
let collab_handler = Arc::new(CollabHandler);
let multi_agent_handler = Arc::new(MultiAgentHandler);
builder.push_spec(create_spawn_agent_tool());
builder.push_spec(create_send_input_tool());
builder.push_spec(create_resume_agent_tool());
builder.push_spec(create_wait_tool());
builder.push_spec(create_close_agent_tool());
builder.register_handler("spawn_agent", collab_handler.clone());
builder.register_handler("send_input", collab_handler.clone());
builder.register_handler("resume_agent", collab_handler.clone());
builder.register_handler("wait", collab_handler.clone());
builder.register_handler("close_agent", collab_handler);
builder.register_handler("spawn_agent", multi_agent_handler.clone());
builder.register_handler("send_input", multi_agent_handler.clone());
builder.register_handler("resume_agent", multi_agent_handler.clone());
builder.register_handler("wait", multi_agent_handler.clone());
builder.register_handler("close_agent", multi_agent_handler);
}

if let Some(mcp_tools) = mcp_tools {
Expand Down
18 changes: 10 additions & 8 deletions codex-rs/tui/src/chatwidget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ use crate::bottom_pane::SelectionViewParams;
use crate::bottom_pane::custom_prompt_view::CustomPromptView;
use crate::bottom_pane::popup_consts::standard_popup_hint_line;
use crate::clipboard_paste::paste_image_to_temp_png;
use crate::collab;
use crate::collaboration_modes;
use crate::diff_render::display_path_for;
use crate::exec_cell::CommandOutput;
Expand All @@ -201,6 +200,7 @@ use crate::history_cell::WebSearchCell;
use crate::key_hint;
use crate::key_hint::KeyBinding;
use crate::markdown::append_markdown;
use crate::multi_agents;
use crate::render::Insets;
use crate::render::renderable::ColumnRenderable;
use crate::render::renderable::FlexRenderable;
Expand Down Expand Up @@ -4113,17 +4113,19 @@ impl ChatWidget {
EventMsg::ExitedReviewMode(review) => self.on_exited_review_mode(review),
EventMsg::ContextCompacted(_) => self.on_agent_message("Context compacted".to_owned()),
EventMsg::CollabAgentSpawnBegin(_) => {}
EventMsg::CollabAgentSpawnEnd(ev) => self.on_collab_event(collab::spawn_end(ev)),
EventMsg::CollabAgentSpawnEnd(ev) => self.on_collab_event(multi_agents::spawn_end(ev)),
EventMsg::CollabAgentInteractionBegin(_) => {}
EventMsg::CollabAgentInteractionEnd(ev) => {
self.on_collab_event(collab::interaction_end(ev))
self.on_collab_event(multi_agents::interaction_end(ev))
}
EventMsg::CollabWaitingBegin(ev) => self.on_collab_event(collab::waiting_begin(ev)),
EventMsg::CollabWaitingEnd(ev) => self.on_collab_event(collab::waiting_end(ev)),
EventMsg::CollabWaitingBegin(ev) => {
self.on_collab_event(multi_agents::waiting_begin(ev))
}
EventMsg::CollabWaitingEnd(ev) => self.on_collab_event(multi_agents::waiting_end(ev)),
EventMsg::CollabCloseBegin(_) => {}
EventMsg::CollabCloseEnd(ev) => self.on_collab_event(collab::close_end(ev)),
EventMsg::CollabResumeBegin(ev) => self.on_collab_event(collab::resume_begin(ev)),
EventMsg::CollabResumeEnd(ev) => self.on_collab_event(collab::resume_end(ev)),
EventMsg::CollabCloseEnd(ev) => self.on_collab_event(multi_agents::close_end(ev)),
EventMsg::CollabResumeBegin(ev) => self.on_collab_event(multi_agents::resume_begin(ev)),
EventMsg::CollabResumeEnd(ev) => self.on_collab_event(multi_agents::resume_end(ev)),
EventMsg::ThreadRolledBack(rollback) => {
if from_replay {
self.app_event_tx.send(AppEvent::ApplyThreadRollback {
Expand Down
Loading
Loading