mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
## Why Extension lifecycle hooks sit on the host/extension boundary, but the current trait surface only allows synchronous callbacks. That forces extensions that need to seed, rehydrate, observe, or flush extension-owned state during thread and turn transitions to either block inside the callback or move async work into separate host plumbing. This PR makes those lifecycle callbacks awaitable so extension implementations can perform async work directly at the lifecycle point where the host already has the relevant session, thread, or turn stores available. ## What changed - Makes `ThreadLifecycleContributor` and `TurnLifecycleContributor` async in `codex-extension-api`. - Awaits thread start/resume/stop and turn start/stop/abort lifecycle callbacks from `codex-core`. - Updates the guardian and memories extensions to implement the async lifecycle trait surface. - Updates the existing lifecycle tests to use async contributor implementations. - Adds `async-trait` to the crates that now expose or implement these async object-safe lifecycle traits. ## Testing - Existing `codex-core` lifecycle tests were updated to cover async implementations for thread stop and turn abort ordering.
108 lines
3.5 KiB
Rust
108 lines
3.5 KiB
Rust
use std::sync::Arc;
|
|
|
|
use codex_core::config::Config;
|
|
use codex_extension_api::ConfigContributor;
|
|
use codex_extension_api::ContextContributor;
|
|
use codex_extension_api::ExtensionData;
|
|
use codex_extension_api::ExtensionRegistryBuilder;
|
|
use codex_extension_api::PromptFragment;
|
|
use codex_extension_api::ThreadLifecycleContributor;
|
|
use codex_extension_api::ThreadStartInput;
|
|
use codex_extension_api::ToolContributor;
|
|
use codex_features::Feature;
|
|
use codex_memories_read::build_memory_tool_developer_instructions;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
|
|
use crate::local::LocalMemoriesBackend;
|
|
use crate::tools;
|
|
|
|
/// Contributes Codex memory read-path prompt context and memory read tools.
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
pub(crate) struct MemoriesExtension;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(crate) struct MemoriesExtensionConfig {
|
|
pub(crate) enabled: bool,
|
|
pub(crate) codex_home: AbsolutePathBuf,
|
|
}
|
|
|
|
impl MemoriesExtensionConfig {
|
|
fn from_config(config: &Config) -> Self {
|
|
Self {
|
|
enabled: config.features.enabled(Feature::MemoryTool) && config.memories.use_memories,
|
|
codex_home: config.codex_home.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ContextContributor for MemoriesExtension {
|
|
fn contribute<'a>(
|
|
&'a self,
|
|
_session_store: &'a ExtensionData,
|
|
thread_store: &'a ExtensionData,
|
|
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Vec<PromptFragment>> + Send + 'a>> {
|
|
Box::pin(async move {
|
|
let Some(config) = thread_store.get::<MemoriesExtensionConfig>() else {
|
|
return Vec::new();
|
|
};
|
|
if !config.enabled {
|
|
return Vec::new();
|
|
}
|
|
|
|
build_memory_tool_developer_instructions(&config.codex_home)
|
|
.await
|
|
.map(PromptFragment::developer_policy)
|
|
.into_iter()
|
|
.collect()
|
|
})
|
|
}
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl ThreadLifecycleContributor<Config> for MemoriesExtension {
|
|
async fn on_thread_start(&self, input: ThreadStartInput<'_, Config>) {
|
|
input
|
|
.thread_store
|
|
.insert(MemoriesExtensionConfig::from_config(input.config));
|
|
}
|
|
}
|
|
|
|
impl ConfigContributor<Config> for MemoriesExtension {
|
|
fn on_config_changed(
|
|
&self,
|
|
_session_store: &ExtensionData,
|
|
thread_store: &ExtensionData,
|
|
_previous_config: &Config,
|
|
new_config: &Config,
|
|
) {
|
|
thread_store.insert(MemoriesExtensionConfig::from_config(new_config));
|
|
}
|
|
}
|
|
|
|
impl ToolContributor for MemoriesExtension {
|
|
fn tools(
|
|
&self,
|
|
_session_store: &ExtensionData,
|
|
thread_store: &ExtensionData,
|
|
) -> Vec<Arc<dyn codex_extension_api::ToolExecutor<codex_extension_api::ToolCall>>> {
|
|
let Some(config) = thread_store.get::<MemoriesExtensionConfig>() else {
|
|
return Vec::new();
|
|
};
|
|
if !config.enabled {
|
|
return Vec::new();
|
|
}
|
|
|
|
tools::memory_tools(LocalMemoriesBackend::from_codex_home(&config.codex_home))
|
|
}
|
|
}
|
|
|
|
/// Installs the memories extension contributors into the extension registry.
|
|
pub fn install(registry: &mut ExtensionRegistryBuilder<Config>) {
|
|
let extension = Arc::new(MemoriesExtension);
|
|
registry.thread_lifecycle_contributor(extension.clone());
|
|
registry.config_contributor(extension.clone());
|
|
registry.prompt_contributor(extension);
|
|
// Keep the read/retrieval tools out of app-server until that rollout is intentional.
|
|
// registry.tool_contributor(extension);
|
|
}
|