diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index f982373d56..0384f36a25 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2070,6 +2070,8 @@ dependencies = [ "base64 0.22.1", "chrono", "clap", + "codex-agent-message-board-client", + "codex-agent-message-board-extension", "codex-analytics", "codex-app-server-daemon", "codex-app-server-protocol", @@ -2951,11 +2953,11 @@ dependencies = [ "bm25", "chrono", "codex-agent-graph-store", - "codex-agent-message-board-client", "codex-agent-message-board-extension", "codex-agent-roles", "codex-analytics", "codex-api", + "codex-app-server", "codex-app-server-protocol", "codex-apply-patch", "codex-async-utils", @@ -5944,6 +5946,7 @@ dependencies = [ "base64 0.22.1", "codex-agent-graph-store", "codex-analytics", + "codex-app-server", "codex-arg0", "codex-attachment-store", "codex-config", diff --git a/codex-rs/app-server/Cargo.toml b/codex-rs/app-server/Cargo.toml index cde6f7afc5..21add6dc8c 100644 --- a/codex-rs/app-server/Cargo.toml +++ b/codex-rs/app-server/Cargo.toml @@ -25,6 +25,9 @@ doctest = false workspace = true [dependencies] +codex-agent-message-board-client = { workspace = true } +codex-agent-message-board-extension = { workspace = true } +codex-message-board = { workspace = true } codex-utils-process = { workspace = true } anyhow = { workspace = true } base64 = { workspace = true } @@ -133,7 +136,6 @@ codex-windows-sandbox = { workspace = true } [dev-dependencies] zip = { workspace = true } -codex-message-board = { workspace = true } codex-uds = { workspace = true } app_test_support = { workspace = true } axum = { workspace = true, default-features = false, features = [ diff --git a/codex-rs/core/src/agent_message_board.rs b/codex-rs/app-server/src/agent_message_board.rs similarity index 91% rename from codex-rs/core/src/agent_message_board.rs rename to codex-rs/app-server/src/agent_message_board.rs index cb2a6c53cd..6b9054e6fb 100644 --- a/codex-rs/core/src/agent_message_board.rs +++ b/codex-rs/app-server/src/agent_message_board.rs @@ -1,19 +1,17 @@ -//! Bridges the board extension to the selected controller, clock and active-turn services. +//! Supplies board host capabilities through the public Core runtime interfaces. //! //! The extension owns backend construction, storage and tool registration. This adapter //! selects configuration and supplies host capabilities. It never starts or restores //! recipients during lookup. Notifications use ordinary agent-message scheduling. -use crate::CodexThread; -use crate::ThreadManager; -use crate::config::Config; -use crate::context::AgentMessageBoardNotification; -use crate::context::ContextualUserFragment; -use crate::tools::MULTI_AGENT_V2_NAMESPACE_DESCRIPTION; use chrono::DateTime; use chrono::Utc; use codex_agent_message_board_client::RemoteAgentMessageBoard; use codex_agent_message_board_extension::BoardBackend; +use codex_core::CodexThread; +use codex_core::MULTI_AGENT_V2_NAMESPACE_DESCRIPTION; +use codex_core::ThreadManager; +use codex_core::config::Config; use codex_extension_api::ExtensionRegistryBuilder; use codex_extension_api::ThreadStartInput; use codex_features::Feature; @@ -206,12 +204,24 @@ impl MessageBoardHost for LocalBoardHost { Err(error) => return Err(error), }; let recipient_path = self.registered_path(&recipient).await?; - let notice = AgentMessageBoardNotification(post); + let suffix = if post.truncated { + "\n[Use read_post for the rest.]" + } else { + "" + }; + let content = format!( + "Message Type: CHANNEL_POST\nSender: {}\nChannel: {}\nMessage ID: {}\nThread ID: {}\nPayload:\n{}{suffix}", + post.metadata.author, + post.metadata.channel_name, + post.metadata.message_id, + post.metadata.thread_id, + post.text_preview, + ); let communication = InterAgentCommunication::new( - notice.0.metadata.author.clone(), + post.metadata.author.clone(), recipient_path, Vec::new(), - notice.render().into_text().unwrap_or_default(), + content, /*trigger_turn*/ false, ); // Remote metadata is untrusted. Skip an oversized notice without closing diff --git a/codex-rs/app-server/src/extensions.rs b/codex-rs/app-server/src/extensions.rs index 733df70ed5..ff31e9e395 100644 --- a/codex-rs/app-server/src/extensions.rs +++ b/codex-rs/app-server/src/extensions.rs @@ -83,7 +83,7 @@ pub(crate) fn thread_extensions( codex_queue_extension::install(&mut builder, queue_service); } codex_history_notes_extension::install(&mut builder, auth_manager.clone()); - codex_core::install_agent_message_board(&mut builder, thread_manager.clone()); + crate::install_agent_message_board(&mut builder, thread_manager.clone()); if let Some(state_db) = state_db { codex_goal_extension::install_with_backend( &mut builder, diff --git a/codex-rs/app-server/src/lib.rs b/codex-rs/app-server/src/lib.rs index 2ad3aa8cc3..4eee045679 100644 --- a/codex-rs/app-server/src/lib.rs +++ b/codex-rs/app-server/src/lib.rs @@ -95,6 +95,9 @@ fn is_unsupported_untrusted_approval_policy_error(err: &std::io::Error) -> bool ) } +mod agent_message_board; +pub use agent_message_board::install_agent_message_board; + mod analytics_utils; mod app_info; mod app_server_tracing; diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index fcbc849a7c..f456872236 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -72,7 +72,6 @@ codex-skills-extension = { workspace = true } codex-state = { workspace = true } codex-agent-message-board-extension = { workspace = true } codex-message-board = { workspace = true } -codex-agent-message-board-client = { workspace = true } codex-terminal-detection = { workspace = true } codex-thread-store = { workspace = true } codex-tools = { workspace = true } @@ -146,6 +145,7 @@ openssl-sys = { workspace = true, features = ["vendored"] } openssl-sys = { workspace = true, features = ["vendored"] } [dev-dependencies] +codex-app-server = { workspace = true } assert_cmd = { workspace = true } assert_matches = { workspace = true } codex-exec-server-test-support = { workspace = true } diff --git a/codex-rs/core/src/context/agent_message_board_notification.rs b/codex-rs/core/src/context/agent_message_board_notification.rs index b56ef296b9..c6136735b3 100644 --- a/codex-rs/core/src/context/agent_message_board_notification.rs +++ b/codex-rs/core/src/context/agent_message_board_notification.rs @@ -1,12 +1,11 @@ -//! Attributed discussion notices with bounded previews. +//! Recognizes legacy board notices in saved user-role messages. New notices use agent mail. use super::ContextualUserFragment; -use codex_message_board::PostPreview; use codex_protocol::models::ContentItem; use codex_protocol::models::ContentItemKind; use codex_protocol::models::input_text; -pub(crate) struct AgentMessageBoardNotification(pub(crate) PostPreview); +pub(crate) struct AgentMessageBoardNotification; impl ContextualUserFragment for AgentMessageBoardNotification { fn role(&self) -> &'static str { @@ -26,19 +25,6 @@ impl ContextualUserFragment for AgentMessageBoardNotification { ) } fn body(&self) -> ContentItem { - let post = &self.0; - let suffix = if post.truncated { - "\n[Use read_post for the rest.]" - } else { - "" - }; - input_text(format!( - "Message Type: CHANNEL_POST\nSender: {}\nChannel: {}\nMessage ID: {}\nThread ID: {}\nPayload:\n{}{suffix}", - post.metadata.author, - post.metadata.channel_name, - post.metadata.message_id, - post.metadata.thread_id, - post.text_preview, - )) + input_text(String::new()) } } diff --git a/codex-rs/core/src/lib.rs b/codex-rs/core/src/lib.rs index 82276a9299..906e74b345 100644 --- a/codex-rs/core/src/lib.rs +++ b/codex-rs/core/src/lib.rs @@ -81,8 +81,6 @@ pub use agent::types::SpawnAgentOptions; pub use rollout_budget::RolloutBudgetReminder; mod agent_communication; pub use agent_communication::PendingAgentMessage; -mod agent_message_board; -pub use agent_message_board::install_agent_message_board; mod attestation; mod code_mode_host; mod codex_delegate; @@ -192,6 +190,7 @@ pub use thread_manager::build_models_manager; pub use thread_manager::local_agent_graph_store_from_state_db; pub use thread_manager::passthrough_image_store; pub use thread_store_builder::ThreadStoreBuilder; +pub use tools::MULTI_AGENT_V2_NAMESPACE_DESCRIPTION; pub use tools::handlers::WaitForEnvironmentToolConfig; pub use web_search::web_search_action_detail; pub use windows_sandbox_read_grants::grant_read_root_non_elevated; diff --git a/codex-rs/core/src/tools/mod.rs b/codex-rs/core/src/tools/mod.rs index 3344ed84b3..eebc88af8b 100644 --- a/codex-rs/core/src/tools/mod.rs +++ b/codex-rs/core/src/tools/mod.rs @@ -41,7 +41,7 @@ use codex_utils_output_truncation::TruncationPolicy; use codex_utils_output_truncation::formatted_truncate_text; use codex_utils_output_truncation::truncate_text; pub(crate) use executed_tool_calls::ExecutedToolCalls; -pub(crate) use multi_agent_tool::MULTI_AGENT_V2_NAMESPACE_DESCRIPTION; +pub use multi_agent_tool::MULTI_AGENT_V2_NAMESPACE_DESCRIPTION; pub use router::ToolRouter; /// Legacy boundaries such as hook payloads, telemetry tags, and Responses tool diff --git a/codex-rs/core/src/tools/multi_agent_tool.rs b/codex-rs/core/src/tools/multi_agent_tool.rs index 2b271af752..6eac80992d 100644 --- a/codex-rs/core/src/tools/multi_agent_tool.rs +++ b/codex-rs/core/src/tools/multi_agent_tool.rs @@ -15,7 +15,8 @@ use codex_tools::ToolSpec; use futures::future::BoxFuture; use std::sync::Arc; -pub(crate) const MULTI_AGENT_V2_NAMESPACE_DESCRIPTION: &str = +/// Shared namespace description for native tools and host extensions. +pub const MULTI_AGENT_V2_NAMESPACE_DESCRIPTION: &str = "Tools for spawning and managing sub-agents."; pub(super) fn multi_agent_v2_handler( diff --git a/codex-rs/core/tests/common/Cargo.toml b/codex-rs/core/tests/common/Cargo.toml index cb5c222039..6dedb2f13e 100644 --- a/codex-rs/core/tests/common/Cargo.toml +++ b/codex-rs/core/tests/common/Cargo.toml @@ -12,6 +12,7 @@ doctest = false workspace = true [dependencies] +codex-app-server = { workspace = true } anyhow = { workspace = true } assert_cmd = { workspace = true } base64 = { workspace = true } diff --git a/codex-rs/core/tests/common/test_codex.rs b/codex-rs/core/tests/common/test_codex.rs index 3cb6b51c1b..722d2cf514 100644 --- a/codex-rs/core/tests/common/test_codex.rs +++ b/codex-rs/core/tests/common/test_codex.rs @@ -866,7 +866,7 @@ impl TestCodexBuilder { .or_else(|| codex_utils_cargo_bin::cargo_bin("codex-code-mode-host").ok()); let thread_manager = Arc::new_cyclic(|manager| { let mut extensions = self.extensions.to_builder(); - codex_core::install_agent_message_board(&mut extensions, manager.clone()); + codex_app_server::install_agent_message_board(&mut extensions, manager.clone()); if config.features.enabled(Feature::GuardianV2) { codex_guardian_v2::install(&mut extensions, auth_manager.clone(), manager.clone()); } else { diff --git a/codex-rs/core/tests/suite/scenarios_agent_message_board.rs b/codex-rs/core/tests/suite/scenarios_agent_message_board.rs index 88fa3f45d2..4dbd32f443 100644 --- a/codex-rs/core/tests/suite/scenarios_agent_message_board.rs +++ b/codex-rs/core/tests/suite/scenarios_agent_message_board.rs @@ -1060,7 +1060,10 @@ async fn codex_instances_share_board_with_fixed_access() -> anyhow::Result<()> { let guardian_board = LocalBoard::new(SqliteConfig::new_for_testing(alias.try_into()?), board_id); let mut extensions = ExtensionRegistryBuilder::new(); - codex_core::install_agent_message_board(&mut extensions, Arc::downgrade(&test.thread_manager)); + codex_app_server::install_agent_message_board( + &mut extensions, + Arc::downgrade(&test.thread_manager), + ); let extensions = Arc::new(extensions.build()); let mut instances = Vec::new(); for (index, (connection, source)) in [ diff --git a/codex-rs/ext/agent-message-board/README.md b/codex-rs/ext/agent-message-board/README.md index 7f0ff61069..ec64c2fffb 100644 --- a/codex-rs/ext/agent-message-board/README.md +++ b/codex-rs/ext/agent-message-board/README.md @@ -43,7 +43,7 @@ creating channels and changing another agent's subscriptions return permission errors. Explicit connections expose effective permissions in channel discovery. Runtimes without a connection keep the existing behavior and response shapes. -Install `codex_core::install_agent_message_board` in the runtime's extension +Install `codex_app_server::install_agent_message_board` in the runtime's extension registry. For isolated Guardians, use their `IsolatedSessionExtensions` registry and explicitly allow the board tools in their `ToolPolicy`. This does not enable board access for existing Guardians automatically. Connected runtimes use unique