feat: load from plugins (#12864)

Support loading plugins.

Plugins can now be enabled via [plugins.<name>] in config.toml. They are
loaded as first-class entities through PluginsManager, and their default
skills/ and .mcp.json contributions are integrated into the existing
skills and MCP flows.
This commit is contained in:
xl-openai
2026-03-01 10:50:56 -08:00
committed by GitHub
parent 6a673e7339
commit 752402c4fe
24 changed files with 1389 additions and 113 deletions

View File

@@ -11,8 +11,10 @@ use crate::error::CodexErr;
use crate::error::Result as CodexResult;
use crate::file_watcher::FileWatcher;
use crate::file_watcher::FileWatcherEvent;
use crate::mcp::McpManager;
use crate::models_manager::collaboration_mode_presets::CollaborationModesConfig;
use crate::models_manager::manager::ModelsManager;
use crate::plugins::PluginsManager;
use crate::protocol::Event;
use crate::protocol::EventMsg;
use crate::protocol::SessionConfiguredEvent;
@@ -132,6 +134,8 @@ pub(crate) struct ThreadManagerState {
auth_manager: Arc<AuthManager>,
models_manager: Arc<ModelsManager>,
skills_manager: Arc<SkillsManager>,
plugins_manager: Arc<PluginsManager>,
mcp_manager: Arc<McpManager>,
file_watcher: Arc<FileWatcher>,
session_source: SessionSource,
// Captures submitted ops for testing purpose when test mode is enabled.
@@ -147,7 +151,12 @@ impl ThreadManager {
collaboration_modes_config: CollaborationModesConfig,
) -> Self {
let (thread_created_tx, _) = broadcast::channel(THREAD_CREATED_CHANNEL_CAPACITY);
let skills_manager = Arc::new(SkillsManager::new(codex_home.clone()));
let plugins_manager = Arc::new(PluginsManager::new(codex_home.clone()));
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
let skills_manager = Arc::new(SkillsManager::new(
codex_home.clone(),
Arc::clone(&plugins_manager),
));
let file_watcher = build_file_watcher(codex_home.clone(), Arc::clone(&skills_manager));
Self {
state: Arc::new(ThreadManagerState {
@@ -160,6 +169,8 @@ impl ThreadManager {
collaboration_modes_config,
)),
skills_manager,
plugins_manager,
mcp_manager,
file_watcher,
auth_manager,
session_source,
@@ -199,7 +210,12 @@ impl ThreadManager {
set_thread_manager_test_mode_for_tests(true);
let auth_manager = AuthManager::from_auth_for_testing(auth);
let (thread_created_tx, _) = broadcast::channel(THREAD_CREATED_CHANNEL_CAPACITY);
let skills_manager = Arc::new(SkillsManager::new(codex_home.clone()));
let plugins_manager = Arc::new(PluginsManager::new(codex_home.clone()));
let mcp_manager = Arc::new(McpManager::new(Arc::clone(&plugins_manager)));
let skills_manager = Arc::new(SkillsManager::new(
codex_home.clone(),
Arc::clone(&plugins_manager),
));
let file_watcher = build_file_watcher(codex_home.clone(), Arc::clone(&skills_manager));
Self {
state: Arc::new(ThreadManagerState {
@@ -211,6 +227,8 @@ impl ThreadManager {
provider,
)),
skills_manager,
plugins_manager,
mcp_manager,
file_watcher,
auth_manager,
session_source: SessionSource::Exec,
@@ -229,6 +247,14 @@ impl ThreadManager {
self.state.skills_manager.clone()
}
pub fn plugins_manager(&self) -> Arc<PluginsManager> {
self.state.plugins_manager.clone()
}
pub fn mcp_manager(&self) -> Arc<McpManager> {
self.state.mcp_manager.clone()
}
pub fn subscribe_file_watcher(&self) -> broadcast::Receiver<FileWatcherEvent> {
self.state.file_watcher.subscribe()
}
@@ -557,7 +583,9 @@ impl ThreadManagerState {
persist_extended_history: bool,
metrics_service_name: Option<String>,
) -> CodexResult<NewThread> {
let watch_registration = self.file_watcher.register_config(&config);
let watch_registration = self
.file_watcher
.register_config(&config, self.skills_manager.as_ref());
let CodexSpawnOk {
codex, thread_id, ..
} = Codex::spawn(
@@ -565,6 +593,8 @@ impl ThreadManagerState {
auth_manager,
Arc::clone(&self.models_manager),
Arc::clone(&self.skills_manager),
Arc::clone(&self.plugins_manager),
Arc::clone(&self.mcp_manager),
Arc::clone(&self.file_watcher),
initial_history,
session_source,