feat: split memories part 2 (#19860)

Keep extracting memories out of core and moving the write trigger in the
app-server
This is temporary and it should move at the client level as a follow-up
This makes core fully independant from `codex-memories-write`

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
jif-oai
2026-04-28 13:03:28 +02:00
committed by GitHub
Unverified
parent fd36838cf3
commit 431ebeaef7
47 changed files with 1932 additions and 2735 deletions
@@ -241,11 +241,10 @@ use codex_core::ForkSnapshot;
use codex_core::NewThread;
use codex_core::RolloutRecorder;
use codex_core::SessionMeta;
use codex_core::StartThreadWithToolsOptions;
use codex_core::StartThreadOptions;
use codex_core::SteerInputError;
use codex_core::ThreadConfigSnapshot;
use codex_core::ThreadManager;
use codex_core::clear_memory_roots_contents;
use codex_core::config::Config;
use codex_core::config::ConfigOverrides;
use codex_core::config::NetworkProxyAuditMetadata;
@@ -315,6 +314,7 @@ use codex_mcp::discover_supported_scopes;
use codex_mcp::effective_mcp_servers;
use codex_mcp::read_mcp_resource as read_mcp_resource_without_thread;
use codex_mcp::resolve_oauth_scopes;
use codex_memories_write::clear_memory_roots_contents;
use codex_model_provider::ProviderAccountError;
use codex_model_provider::create_model_provider;
use codex_models_manager::collaboration_mode_presets::CollaborationModesConfig;
@@ -542,6 +542,7 @@ pub(crate) enum ApiVersion {
#[derive(Clone)]
struct ListenerTaskContext {
auth_manager: Arc<AuthManager>,
thread_manager: Arc<ThreadManager>,
thread_state_manager: ThreadStateManager,
outgoing: Arc<OutgoingMessageSender>,
@@ -2414,6 +2415,7 @@ impl CodexMessageProcessor {
);
typesafe_overrides.ephemeral = ephemeral;
let listener_task_context = ListenerTaskContext {
auth_manager: Arc::clone(&self.auth_manager),
thread_manager: Arc::clone(&self.thread_manager),
thread_state_manager: self.thread_state_manager.clone(),
outgoing: Arc::clone(&self.outgoing),
@@ -2613,6 +2615,7 @@ impl CodexMessageProcessor {
.collect()
};
let core_dynamic_tool_count = core_dynamic_tools.len();
let memory_config = Arc::new(config.clone());
let NewThread {
thread_id,
@@ -2621,7 +2624,7 @@ impl CodexMessageProcessor {
..
} = listener_task_context
.thread_manager
.start_thread_with_tools_and_service_name(StartThreadWithToolsOptions {
.start_thread_with_options(StartThreadOptions {
config,
initial_history: match session_start_source
.unwrap_or(codex_app_server_protocol::ThreadStartSource::Startup)
@@ -2633,6 +2636,7 @@ impl CodexMessageProcessor {
InitialHistory::Cleared
}
},
session_source: None,
dynamic_tools: core_dynamic_tools,
persist_extended_history,
metrics_service_name: service_name,
@@ -2665,6 +2669,14 @@ impl CodexMessageProcessor {
otel.name = "app_server.thread_start.config_snapshot",
))
.await;
codex_memories_write::start_memories_startup_task(
Arc::clone(&listener_task_context.thread_manager),
Arc::clone(&listener_task_context.auth_manager),
thread_id,
Arc::clone(&thread),
Arc::clone(&memory_config),
&config_snapshot.session_source,
);
let mut thread = build_thread_from_snapshot(
thread_id,
&config_snapshot,
@@ -3659,13 +3671,13 @@ impl CodexMessageProcessor {
params: ThreadLoadedListParams,
) -> Result<ThreadLoadedListResponse, JSONRPCErrorError> {
let ThreadLoadedListParams { cursor, limit } = params;
let mut data = self
let mut data: Vec<String> = self
.thread_manager
.list_thread_ids()
.await
.into_iter()
.map(|thread_id| thread_id.to_string())
.collect::<Vec<_>>();
.collect();
if data.is_empty() {
return Ok(ThreadLoadedListResponse {
@@ -4166,6 +4178,7 @@ impl CodexMessageProcessor {
let instruction_sources = Self::instruction_sources_from_config(&config).await;
let response_history = thread_history.clone();
let memory_config = Arc::new(config.clone());
match self
.thread_manager
@@ -4240,6 +4253,14 @@ impl CodexMessageProcessor {
/*has_live_in_progress_turn*/ false,
);
let config_snapshot = codex_thread.config_snapshot().await;
codex_memories_write::start_memories_startup_task(
Arc::clone(&self.thread_manager),
Arc::clone(&self.auth_manager),
thread_id,
Arc::clone(&codex_thread),
Arc::clone(&memory_config),
&config_snapshot.session_source,
);
let sandbox = thread_response_sandbox_policy(
&config_snapshot.permission_profile,
config_snapshot.cwd.as_path(),
@@ -4745,6 +4766,7 @@ impl CodexMessageProcessor {
let fallback_model_provider = config.model_provider_id.clone();
let instruction_sources = Self::instruction_sources_from_config(&config).await;
let fork_thread_store = configured_thread_store(&config);
let memory_config = Arc::new(config.clone());
let NewThread {
thread_id,
@@ -4840,6 +4862,14 @@ impl CodexMessageProcessor {
/*has_in_progress_turn*/ false,
);
let config_snapshot = forked_thread.config_snapshot().await;
codex_memories_write::start_memories_startup_task(
Arc::clone(&self.thread_manager),
Arc::clone(&self.auth_manager),
thread_id,
Arc::clone(&forked_thread),
Arc::clone(&memory_config),
&config_snapshot.session_source,
);
let sandbox = thread_response_sandbox_policy(
&config_snapshot.permission_profile,
config_snapshot.cwd.as_path(),
@@ -4878,7 +4908,6 @@ impl CodexMessageProcessor {
return;
}
};
self.analytics_events_client.track_response(
request_id.connection_id.0,
ClientResponse::ThreadFork {
@@ -7159,6 +7188,7 @@ impl CodexMessageProcessor {
) -> Result<EnsureConversationListenerResult, JSONRPCErrorError> {
Self::ensure_conversation_listener_task(
ListenerTaskContext {
auth_manager: Arc::clone(&self.auth_manager),
thread_manager: Arc::clone(&self.thread_manager),
thread_state_manager: self.thread_state_manager.clone(),
outgoing: Arc::clone(&self.outgoing),
@@ -7276,6 +7306,7 @@ impl CodexMessageProcessor {
) -> Result<(), JSONRPCErrorError> {
Self::ensure_listener_task_running_task(
ListenerTaskContext {
auth_manager: Arc::clone(&self.auth_manager),
thread_manager: Arc::clone(&self.thread_manager),
thread_state_manager: self.thread_state_manager.clone(),
outgoing: Arc::clone(&self.outgoing),
@@ -7324,6 +7355,7 @@ impl CodexMessageProcessor {
thread_state.set_listener(cancel_tx, &conversation)
};
let ListenerTaskContext {
auth_manager: _,
outgoing,
thread_manager,
thread_state_manager,