[codex] Route live thread writes through ThreadStore (#18882)

Begin migrating the thread write codepaths to ThreadStore.

This starts using ThreadStore inside of core session code, not only in
the app server code.

Rework the interfaces around thread recording/persistence. We're left
with the following:

* `ThreadManager`: owns the process-level registry of loaded threads and
handles cross-thread orchestration: start, resume, fork, lookup, remove,
and route ops to running CodexThreads.
* `CodexThread`: represents one loaded/running thread from the outside.
It is the handle app-server and callers use to submit ops, inspect
session metadata, and shut the thread down.
* `LiveThread`: session-owned persistence lifecycle handle for one
active thread. Core session code uses it to append rollout items,
materialize lazy persistence, flush, shutdown, discard init-failed
writers, and load that thread’s persisted history.
* `ThreadStore`: storage backend abstraction. It answers “how are
threads persisted, read, listed, updated, archived?” Local and remote
implementations live behind this trait.
* `LocalThreadStore`: local ThreadStore implementation. It owns the
file/sqlite-specific details and keeps RolloutRecorder as a local
implementation detail.

This is a few too many Thread abstractions for my liking, but they do
all represent different concepts / needs / layers.

Migration note: in places where the core code explicitly requires a
path, rather than a thread ID, throw an error if we're running with a
remote store.

Cover the new local live-writer lifecycle with focused tests and
preserve app-server thread-start behavior, including ephemeral pathless
sessions.
This commit is contained in:
Tom authored and GitHub committed 2026-04-23 10:17:09 -07:00
1 parent 3d3028a5a9
commit f1923a38b1
23 files changed
+1789 -889

No files matched your search

+24 -15
View File
@@ -1,6 +1,6 @@
use std::any::Any;
use async_trait::async_trait;
use codex_protocol::ThreadId;
use std::any::Any;
use crate::AppendThreadItemsParams;
use crate::ArchiveThreadParams;
@@ -8,11 +8,10 @@ use crate::CreateThreadParams;
use crate::ListThreadsParams;
use crate::LoadThreadHistoryParams;
use crate::ReadThreadParams;
use crate::ResumeThreadRecorderParams;
use crate::ResumeThreadParams;
use crate::StoredThread;
use crate::StoredThreadHistory;
use crate::ThreadPage;
use crate::ThreadRecorder;
use crate::ThreadStoreResult;
use crate::UpdateThreadMetadataParams;
@@ -23,21 +22,31 @@ pub trait ThreadStore: Any + Send + Sync {
/// make sense for a concrete store implementation.
fn as_any(&self) -> &dyn Any;
/// Creates a new thread and returns a live recorder for future appends.
async fn create_thread(
&self,
params: CreateThreadParams,
) -> ThreadStoreResult<Box<dyn ThreadRecorder>>;
/// Creates a new live thread.
async fn create_thread(&self, params: CreateThreadParams) -> ThreadStoreResult<()>;
/// Reopens a live recorder for an existing thread.
async fn resume_thread_recorder(
&self,
params: ResumeThreadRecorderParams,
) -> ThreadStoreResult<Box<dyn ThreadRecorder>>;
/// Reopens an existing thread for live appends.
async fn resume_thread(&self, params: ResumeThreadParams) -> ThreadStoreResult<()>;
/// Appends items to a stored thread outside the live-recorder path.
/// Appends items to a live thread.
async fn append_items(&self, params: AppendThreadItemsParams) -> ThreadStoreResult<()>;
/// Materializes the thread if persistence is lazy, then persists all queued items.
async fn persist_thread(&self, thread_id: ThreadId) -> ThreadStoreResult<()>;
/// Flushes all queued items and returns once they are durable/readable.
async fn flush_thread(&self, thread_id: ThreadId) -> ThreadStoreResult<()>;
/// Flushes pending items and closes the live thread writer.
async fn shutdown_thread(&self, thread_id: ThreadId) -> ThreadStoreResult<()>;
/// Discards the live thread writer without forcing pending in-memory items to become durable.
///
/// Core calls this when session initialization fails after a live writer has been created.
/// Implementations should release any live writer resources for the thread while preserving
/// already-durable thread data.
async fn discard_thread(&self, thread_id: ThreadId) -> ThreadStoreResult<()>;
/// Loads persisted history for resume, fork, rollback, and memory jobs.
async fn load_history(
&self,