mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Revert state DB injection and agent graph store (#21481)
## Why Reverts #20689 to restore the previous optional state DB plumbing. The conflict resolution keeps the newer installation ID and session/thread identity changes that landed after #20689, while removing the mandatory state DB and agent graph store dependency from ThreadManager construction. ## What changed - Restored `Option<StateDbHandle>` through app-server, MCP server, prompt debug, and test entry points. - Removed the `codex-core` dependency on `codex-agent-graph-store` and reverted descendant lookup back to the existing state DB path when available. - Kept newer `installation_id` forwarding by passing it beside the optional DB handle. - Kept local thread-name updates working when the optional state DB handle is absent. ## Validation - `git diff --check` - `cargo test -p codex-thread-store` - `cargo test -p codex-state -p codex-rollout -p codex-app-server-protocol` - Attempted `env CARGO_INCREMENTAL=0 cargo test -p codex-core -p codex-app-server -p codex-app-server-client -p codex-mcp-server -p codex-thread-manager-sample -p codex-tui`; blocked locally by a rustc ICE while compiling `v8 v146.4.0` with `rustc 1.93.0 (254b59607 2026-01-19)` on `aarch64-apple-darwin`.
This commit is contained in:
committed by
GitHub
Unverified
parent
5bc33fe31f
commit
a8488fec5e
@@ -13,11 +13,11 @@ pub(super) async fn archive_thread(
|
||||
params: ArchiveThreadParams,
|
||||
) -> ThreadStoreResult<()> {
|
||||
let thread_id = params.thread_id;
|
||||
let state_db = store.state_db();
|
||||
let state_db_ctx = store.state_db().await;
|
||||
let rollout_path = find_thread_path_by_id_str(
|
||||
store.config.codex_home.as_path(),
|
||||
&thread_id.to_string(),
|
||||
Some(state_db.as_ref()),
|
||||
state_db_ctx.as_deref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ThreadStoreError::InvalidRequest {
|
||||
@@ -52,10 +52,11 @@ pub(super) async fn archive_thread(
|
||||
}
|
||||
})?;
|
||||
|
||||
let _ = store
|
||||
.state_db()
|
||||
.mark_archived(thread_id, archived_path.as_path(), Utc::now())
|
||||
.await;
|
||||
if let Some(ctx) = state_db_ctx {
|
||||
let _ = ctx
|
||||
.mark_archived(thread_id, archived_path.as_path(), Utc::now())
|
||||
.await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -74,15 +75,13 @@ mod tests {
|
||||
use crate::ThreadSortKey;
|
||||
use crate::ThreadStore;
|
||||
use crate::local::LocalThreadStore;
|
||||
use crate::local::test_support::init_test_state_db;
|
||||
use crate::local::test_support::test_config;
|
||||
use crate::local::test_support::test_store;
|
||||
use crate::local::test_support::write_session_file;
|
||||
|
||||
#[tokio::test]
|
||||
async fn archive_thread_moves_rollout_to_archived_collection() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(201);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let active_path =
|
||||
@@ -128,12 +127,21 @@ mod tests {
|
||||
async fn archive_thread_updates_sqlite_metadata_when_present() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(202);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let active_path =
|
||||
write_session_file(home.path(), "2025-01-03T12-00-00", uuid).expect("session file");
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
home.path().to_path_buf(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
runtime
|
||||
.mark_backfill_complete(/*last_watermark*/ None)
|
||||
.await
|
||||
.expect("backfill should be complete");
|
||||
let mut builder = codex_state::ThreadMetadataBuilder::new(
|
||||
thread_id,
|
||||
active_path.clone(),
|
||||
|
||||
@@ -22,12 +22,12 @@ pub(super) async fn create_thread(
|
||||
})?;
|
||||
let config = RolloutConfig {
|
||||
codex_home: store.config.codex_home.clone(),
|
||||
sqlite_home: store.sqlite_home(),
|
||||
sqlite_home: store.config.sqlite_home.clone(),
|
||||
cwd,
|
||||
model_provider_id: params.metadata.model_provider.clone(),
|
||||
generate_memories: matches!(params.metadata.memory_mode, ThreadMemoryMode::Enabled),
|
||||
};
|
||||
let state_db_ctx = Some(store.state_db());
|
||||
let state_db_ctx = store.state_db().await;
|
||||
let recorder = RolloutRecorder::new(
|
||||
&config,
|
||||
RolloutRecorderParams::new(
|
||||
|
||||
@@ -39,16 +39,16 @@ pub(super) async fn list_threads(
|
||||
SortDirection::Asc => codex_rollout::SortDirection::Asc,
|
||||
SortDirection::Desc => codex_rollout::SortDirection::Desc,
|
||||
};
|
||||
let state_db = store.state_db().await;
|
||||
let rollout_config = RolloutConfig {
|
||||
codex_home: store.config.codex_home.clone(),
|
||||
sqlite_home: store.sqlite_home(),
|
||||
sqlite_home: store.config.sqlite_home.clone(),
|
||||
cwd: store.config.codex_home.clone(),
|
||||
model_provider_id: store.config.default_model_provider_id.clone(),
|
||||
generate_memories: false,
|
||||
};
|
||||
let state_db_ctx = Some(store.state_db());
|
||||
let page = list_rollout_threads(
|
||||
state_db_ctx,
|
||||
state_db,
|
||||
&rollout_config,
|
||||
store.config.default_model_provider_id.as_str(),
|
||||
¶ms,
|
||||
@@ -80,13 +80,14 @@ pub(super) async fn list_threads(
|
||||
.map(|thread| thread.thread_id)
|
||||
.collect::<HashSet<_>>();
|
||||
let mut names = HashMap::<ThreadId, String>::with_capacity(thread_ids.len());
|
||||
let state_db_ctx = store.state_db();
|
||||
for &thread_id in &thread_ids {
|
||||
let Ok(Some(metadata)) = state_db_ctx.get_thread(thread_id).await else {
|
||||
continue;
|
||||
};
|
||||
if let Some(title) = distinct_thread_metadata_title(&metadata) {
|
||||
names.insert(thread_id, title);
|
||||
if let Some(state_db_ctx) = store.state_db().await {
|
||||
for &thread_id in &thread_ids {
|
||||
let Ok(Some(metadata)) = state_db_ctx.get_thread(thread_id).await else {
|
||||
continue;
|
||||
};
|
||||
if let Some(title) = distinct_thread_metadata_title(&metadata) {
|
||||
names.insert(thread_id, title);
|
||||
}
|
||||
}
|
||||
}
|
||||
if names.len() < thread_ids.len()
|
||||
@@ -107,9 +108,9 @@ pub(super) async fn list_threads(
|
||||
}
|
||||
|
||||
async fn list_rollout_threads(
|
||||
state_db_ctx: Option<codex_rollout::StateDbHandle>,
|
||||
state_db: Option<codex_rollout::StateDbHandle>,
|
||||
config: &RolloutConfig,
|
||||
default_model_provider: &str,
|
||||
default_model_provider_id: &str,
|
||||
params: &ListThreadsParams,
|
||||
cursor: Option<&codex_rollout::Cursor>,
|
||||
sort_key: codex_rollout::ThreadSortKey,
|
||||
@@ -117,7 +118,7 @@ async fn list_rollout_threads(
|
||||
) -> ThreadStoreResult<codex_rollout::ThreadsPage> {
|
||||
let page = if params.use_state_db_only && params.archived {
|
||||
RolloutRecorder::list_archived_threads_from_state_db(
|
||||
state_db_ctx.clone(),
|
||||
state_db,
|
||||
config,
|
||||
params.page_size,
|
||||
cursor,
|
||||
@@ -126,13 +127,13 @@ async fn list_rollout_threads(
|
||||
params.allowed_sources.as_slice(),
|
||||
params.model_providers.as_deref(),
|
||||
params.cwd_filters.as_deref(),
|
||||
default_model_provider,
|
||||
default_model_provider_id,
|
||||
params.search_term.as_deref(),
|
||||
)
|
||||
.await
|
||||
} else if params.use_state_db_only {
|
||||
RolloutRecorder::list_threads_from_state_db(
|
||||
state_db_ctx.clone(),
|
||||
state_db,
|
||||
config,
|
||||
params.page_size,
|
||||
cursor,
|
||||
@@ -141,13 +142,13 @@ async fn list_rollout_threads(
|
||||
params.allowed_sources.as_slice(),
|
||||
params.model_providers.as_deref(),
|
||||
params.cwd_filters.as_deref(),
|
||||
default_model_provider,
|
||||
default_model_provider_id,
|
||||
params.search_term.as_deref(),
|
||||
)
|
||||
.await
|
||||
} else if params.archived {
|
||||
RolloutRecorder::list_archived_threads(
|
||||
state_db_ctx.clone(),
|
||||
state_db,
|
||||
config,
|
||||
params.page_size,
|
||||
cursor,
|
||||
@@ -156,13 +157,13 @@ async fn list_rollout_threads(
|
||||
params.allowed_sources.as_slice(),
|
||||
params.model_providers.as_deref(),
|
||||
params.cwd_filters.as_deref(),
|
||||
default_model_provider,
|
||||
default_model_provider_id,
|
||||
params.search_term.as_deref(),
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
RolloutRecorder::list_threads(
|
||||
state_db_ctx,
|
||||
state_db,
|
||||
config,
|
||||
params.page_size,
|
||||
cursor,
|
||||
@@ -171,7 +172,7 @@ async fn list_rollout_threads(
|
||||
params.allowed_sources.as_slice(),
|
||||
params.model_providers.as_deref(),
|
||||
params.cwd_filters.as_deref(),
|
||||
default_model_provider,
|
||||
default_model_provider_id,
|
||||
params.search_term.as_deref(),
|
||||
)
|
||||
.await
|
||||
@@ -194,9 +195,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::ThreadStore;
|
||||
use crate::local::LocalThreadStore;
|
||||
use crate::local::test_support::init_test_state_db;
|
||||
use crate::local::test_support::test_config;
|
||||
use crate::local::test_support::test_store;
|
||||
use crate::local::test_support::write_archived_session_file;
|
||||
use crate::local::test_support::write_session_file;
|
||||
use crate::local::test_support::write_session_file_with;
|
||||
@@ -204,7 +203,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn list_threads_uses_default_provider_when_rollout_omits_provider() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
write_session_file_with(
|
||||
home.path(),
|
||||
home.path().join("sessions/2025/01/03"),
|
||||
@@ -239,13 +238,22 @@ mod tests {
|
||||
async fn list_threads_preserves_sqlite_title_search_results() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(103);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let rollout_path = home.path().join("rollout-title-search.jsonl");
|
||||
fs::write(&rollout_path, "").expect("placeholder rollout file");
|
||||
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
home.path().to_path_buf(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
runtime
|
||||
.mark_backfill_complete(/*last_watermark*/ None)
|
||||
.await
|
||||
.expect("backfill should be complete");
|
||||
let created_at = Utc::now();
|
||||
let mut builder = codex_state::ThreadMetadataBuilder::new(
|
||||
thread_id,
|
||||
@@ -259,10 +267,6 @@ mod tests {
|
||||
let mut metadata = builder.build(config.default_model_provider_id.as_str());
|
||||
metadata.title = "needle title".to_string();
|
||||
metadata.first_user_message = Some("plain preview".to_string());
|
||||
runtime
|
||||
.mark_backfill_complete(/*last_watermark*/ None)
|
||||
.await
|
||||
.expect("backfill should be complete");
|
||||
runtime
|
||||
.upsert_thread(&metadata)
|
||||
.await
|
||||
@@ -299,7 +303,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn list_threads_selects_active_or_archived_collection() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let active_uuid = Uuid::from_u128(105);
|
||||
let archived_uuid = Uuid::from_u128(106);
|
||||
write_session_file(home.path(), "2025-01-03T12-00-00", active_uuid)
|
||||
@@ -368,7 +372,7 @@ mod tests {
|
||||
async fn list_threads_returns_local_rollout_summary() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let store = LocalThreadStore::new(config.clone(), init_test_state_db(&config).await);
|
||||
let store = LocalThreadStore::new(config, /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(101);
|
||||
let path =
|
||||
write_session_file(home.path(), "2025-01-03T12-00-00", uuid).expect("session file");
|
||||
@@ -407,7 +411,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn list_threads_rejects_invalid_cursor() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
|
||||
let err = store
|
||||
.list_threads(ListThreadsParams {
|
||||
|
||||
@@ -66,12 +66,12 @@ pub(super) async fn resume_thread(
|
||||
})?;
|
||||
let config = RolloutConfig {
|
||||
codex_home: store.config.codex_home.clone(),
|
||||
sqlite_home: store.sqlite_home(),
|
||||
sqlite_home: store.config.sqlite_home.clone(),
|
||||
cwd,
|
||||
model_provider_id: params.metadata.model_provider.clone(),
|
||||
generate_memories: matches!(params.metadata.memory_mode, ThreadMemoryMode::Enabled),
|
||||
};
|
||||
let state_db_ctx = Some(store.state_db());
|
||||
let state_db_ctx = store.state_db().await;
|
||||
let recorder = RolloutRecorder::new(
|
||||
&config,
|
||||
RolloutRecorderParams::resume(
|
||||
|
||||
@@ -41,7 +41,7 @@ use crate::UpdateThreadMetadataParams;
|
||||
pub struct LocalThreadStore {
|
||||
pub(super) config: LocalThreadStoreConfig,
|
||||
live_recorders: Arc<Mutex<HashMap<ThreadId, RolloutRecorder>>>,
|
||||
state_db: StateDbHandle,
|
||||
state_db: Option<StateDbHandle>,
|
||||
}
|
||||
|
||||
/// Process-scoped configuration for local thread storage.
|
||||
@@ -51,6 +51,7 @@ pub struct LocalThreadStore {
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct LocalThreadStoreConfig {
|
||||
pub codex_home: PathBuf,
|
||||
pub sqlite_home: PathBuf,
|
||||
/// Provider used only when older local metadata does not contain one.
|
||||
pub default_model_provider_id: String,
|
||||
}
|
||||
@@ -59,6 +60,7 @@ impl LocalThreadStoreConfig {
|
||||
pub fn from_config(config: &impl codex_rollout::RolloutConfigView) -> Self {
|
||||
Self {
|
||||
codex_home: config.codex_home().to_path_buf(),
|
||||
sqlite_home: config.sqlite_home().to_path_buf(),
|
||||
default_model_provider_id: config.model_provider_id().to_string(),
|
||||
}
|
||||
}
|
||||
@@ -73,9 +75,8 @@ impl std::fmt::Debug for LocalThreadStore {
|
||||
}
|
||||
|
||||
impl LocalThreadStore {
|
||||
/// Create a local store from process-scoped local storage configuration and
|
||||
/// the caller-provided shared state DB handle.
|
||||
pub fn new(config: LocalThreadStoreConfig, state_db: StateDbHandle) -> Self {
|
||||
/// Create a local store using an already initialized state DB handle.
|
||||
pub fn new(config: LocalThreadStoreConfig, state_db: Option<StateDbHandle>) -> Self {
|
||||
Self {
|
||||
config,
|
||||
live_recorders: Arc::new(Mutex::new(HashMap::new())),
|
||||
@@ -84,14 +85,10 @@ impl LocalThreadStore {
|
||||
}
|
||||
|
||||
/// Return the state DB handle used by local rollout writers.
|
||||
pub fn state_db(&self) -> StateDbHandle {
|
||||
pub async fn state_db(&self) -> Option<StateDbHandle> {
|
||||
self.state_db.clone()
|
||||
}
|
||||
|
||||
pub(super) fn sqlite_home(&self) -> PathBuf {
|
||||
self.state_db.codex_home().to_path_buf()
|
||||
}
|
||||
|
||||
/// Read a local rollout-backed thread by path.
|
||||
pub async fn read_thread_by_rollout_path(
|
||||
&self,
|
||||
@@ -285,16 +282,14 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::ThreadEventPersistenceMode;
|
||||
use crate::ThreadPersistenceMetadata;
|
||||
use crate::local::test_support::init_test_state_db;
|
||||
use crate::local::test_support::test_config;
|
||||
use crate::local::test_support::test_store;
|
||||
use crate::local::test_support::write_archived_session_file;
|
||||
use crate::local::test_support::write_session_file;
|
||||
|
||||
#[tokio::test]
|
||||
async fn live_writer_lifecycle_writes_and_closes() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let thread_id = ThreadId::default();
|
||||
|
||||
store
|
||||
@@ -343,7 +338,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn create_thread_rejects_missing_cwd() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let thread_id = ThreadId::default();
|
||||
let mut params = create_thread_params(thread_id);
|
||||
params.metadata.cwd = None;
|
||||
@@ -363,7 +358,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn discard_thread_drops_unmaterialized_live_writer() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let thread_id = ThreadId::default();
|
||||
|
||||
store
|
||||
@@ -401,9 +396,8 @@ mod tests {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let thread_id = ThreadId::default();
|
||||
let state_db = init_test_state_db(&config).await;
|
||||
|
||||
let first_store = LocalThreadStore::new(config.clone(), state_db.clone());
|
||||
let first_store = LocalThreadStore::new(config.clone(), /*state_db*/ None);
|
||||
first_store
|
||||
.create_thread(create_thread_params(thread_id))
|
||||
.await
|
||||
@@ -432,7 +426,7 @@ mod tests {
|
||||
.await
|
||||
.expect("shutdown initial writer");
|
||||
|
||||
let resumed_store = LocalThreadStore::new(config, state_db);
|
||||
let resumed_store = LocalThreadStore::new(config, /*state_db*/ None);
|
||||
resumed_store
|
||||
.resume_thread(ResumeThreadParams {
|
||||
thread_id,
|
||||
@@ -463,7 +457,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn create_thread_rejects_duplicate_live_writer() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let thread_id = ThreadId::default();
|
||||
|
||||
store
|
||||
@@ -483,7 +477,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn resume_thread_rejects_duplicate_live_writer() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let thread_id = ThreadId::default();
|
||||
|
||||
store
|
||||
@@ -512,7 +506,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn resume_thread_rejects_missing_cwd() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = uuid::Uuid::from_u128(407);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let rollout_path =
|
||||
@@ -541,7 +535,7 @@ mod tests {
|
||||
async fn load_history_uses_live_writer_rollout_path() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let external_home = TempDir::new().expect("external temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = uuid::Uuid::from_u128(404);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let rollout_path = write_session_file(external_home.path(), "2025-01-04T10-00-00", uuid)
|
||||
@@ -590,7 +584,7 @@ mod tests {
|
||||
async fn read_thread_uses_live_writer_rollout_path_for_external_resume() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let external_home = TempDir::new().expect("external temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = uuid::Uuid::from_u128(406);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let rollout_path = write_session_file(external_home.path(), "2025-01-04T11-00-00", uuid)
|
||||
@@ -629,7 +623,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn load_history_uses_live_writer_rollout_path_for_archived_source() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = uuid::Uuid::from_u128(405);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let rollout_path = write_archived_session_file(home.path(), "2025-01-04T10-30-00", uuid)
|
||||
@@ -697,7 +691,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_thread_by_rollout_path_includes_history() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let thread_id = ThreadId::default();
|
||||
|
||||
store
|
||||
|
||||
@@ -176,12 +176,12 @@ async fn resolve_rollout_path(
|
||||
return Ok(Some(path));
|
||||
}
|
||||
|
||||
let state_db = store.state_db();
|
||||
let state_db_ctx = store.state_db().await;
|
||||
if include_archived {
|
||||
match find_thread_path_by_id_str(
|
||||
store.config.codex_home.as_path(),
|
||||
&thread_id.to_string(),
|
||||
Some(state_db.as_ref()),
|
||||
state_db_ctx.as_deref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ThreadStoreError::InvalidRequest {
|
||||
@@ -191,7 +191,7 @@ async fn resolve_rollout_path(
|
||||
None => find_archived_thread_path_by_id_str(
|
||||
store.config.codex_home.as_path(),
|
||||
&thread_id.to_string(),
|
||||
Some(state_db.as_ref()),
|
||||
state_db_ctx.as_deref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ThreadStoreError::InvalidRequest {
|
||||
@@ -202,7 +202,7 @@ async fn resolve_rollout_path(
|
||||
find_thread_path_by_id_str(
|
||||
store.config.codex_home.as_path(),
|
||||
&thread_id.to_string(),
|
||||
Some(state_db.as_ref()),
|
||||
state_db_ctx.as_deref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ThreadStoreError::InvalidRequest {
|
||||
@@ -260,7 +260,8 @@ async fn read_sqlite_metadata(
|
||||
store: &LocalThreadStore,
|
||||
thread_id: codex_protocol::ThreadId,
|
||||
) -> Option<ThreadMetadata> {
|
||||
store.state_db().get_thread(thread_id).await.ok().flatten()
|
||||
let runtime = store.state_db().await?;
|
||||
runtime.get_thread(thread_id).await.ok().flatten()
|
||||
}
|
||||
|
||||
async fn stored_thread_from_sqlite_metadata(
|
||||
@@ -414,9 +415,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::ThreadStore;
|
||||
use crate::local::LocalThreadStore;
|
||||
use crate::local::test_support::init_test_state_db;
|
||||
use crate::local::test_support::test_config;
|
||||
use crate::local::test_support::test_store;
|
||||
use crate::local::test_support::write_archived_session_file;
|
||||
use crate::local::test_support::write_session_file;
|
||||
use crate::local::test_support::write_session_file_with_fork;
|
||||
@@ -424,7 +423,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_thread_returns_active_rollout_summary() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(205);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let active_path =
|
||||
@@ -452,7 +451,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_thread_returns_rollout_path_summary() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(211);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let active_path =
|
||||
@@ -483,12 +482,17 @@ mod tests {
|
||||
async fn read_thread_by_rollout_path_prefers_sqlite_git_info() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(223);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let active_path =
|
||||
write_session_file(home.path(), "2025-01-03T12-00-00", uuid).expect("session file");
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
let mut builder = ThreadMetadataBuilder::new(
|
||||
thread_id,
|
||||
active_path.clone(),
|
||||
@@ -526,7 +530,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_thread_returns_archived_rollout_when_requested() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(207);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let archived_path = write_archived_session_file(home.path(), "2025-01-03T12-00-00", uuid)
|
||||
@@ -567,7 +571,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_thread_prefers_active_rollout_over_archived() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(208);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let active_path =
|
||||
@@ -592,7 +596,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_thread_returns_forked_from_id() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(209);
|
||||
let parent_uuid = Uuid::from_u128(210);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
@@ -625,12 +629,17 @@ mod tests {
|
||||
async fn read_thread_applies_sqlite_thread_name() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(212);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let rollout_path =
|
||||
write_session_file(home.path(), "2025-01-03T12-00-00", uuid).expect("session file");
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
let mut builder =
|
||||
ThreadMetadataBuilder::new(thread_id, rollout_path, Utc::now(), SessionSource::Cli);
|
||||
builder.model_provider = Some(config.default_model_provider_id.clone());
|
||||
@@ -660,8 +669,13 @@ mod tests {
|
||||
async fn read_thread_preserves_rollout_cwd_when_sqlite_metadata_exists() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
let uuid = Uuid::from_u128(224);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let day_dir = home.path().join("sessions/2025/01/03");
|
||||
@@ -730,7 +744,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_thread_uses_legacy_thread_name_when_sqlite_title_is_missing() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(213);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
write_session_file(home.path(), "2025-01-03T12-00-00", uuid).expect("session file");
|
||||
@@ -754,8 +768,6 @@ mod tests {
|
||||
async fn read_thread_uses_sqlite_metadata_for_rollout_without_user_preview() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(217);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let day_dir = home.path().join("sessions/2025/01/03");
|
||||
@@ -777,6 +789,13 @@ mod tests {
|
||||
});
|
||||
writeln!(file, "{meta}").expect("write session meta");
|
||||
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
let mut builder = ThreadMetadataBuilder::new(
|
||||
thread_id,
|
||||
rollout_path.clone(),
|
||||
@@ -819,13 +838,18 @@ mod tests {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let external = TempDir::new().expect("external temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(220);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let rollout_path =
|
||||
write_session_file(home.path(), "2025-01-03T12-00-00", uuid).expect("session file");
|
||||
let stale_path = external.path().join("missing-rollout.jsonl");
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
let mut builder = ThreadMetadataBuilder::new(
|
||||
thread_id,
|
||||
stale_path.clone(),
|
||||
@@ -863,8 +887,6 @@ mod tests {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let external = TempDir::new().expect("external temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(221);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let rollout_path =
|
||||
@@ -872,6 +894,13 @@ mod tests {
|
||||
let other_uuid = Uuid::from_u128(222);
|
||||
let stale_path = write_session_file(external.path(), "2025-01-04T12-00-00", other_uuid)
|
||||
.expect("other session file");
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
let mut builder =
|
||||
ThreadMetadataBuilder::new(thread_id, stale_path, Utc::now(), SessionSource::Cli);
|
||||
builder.model_provider = Some("wrong-sqlite-provider".to_string());
|
||||
@@ -903,7 +932,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_thread_uses_session_meta_for_rollout_without_user_preview_or_sqlite_metadata() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(218);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let day_dir = home.path().join("sessions/2025/01/03");
|
||||
@@ -958,13 +987,18 @@ mod tests {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let external = TempDir::new().expect("external temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(214);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let rollout_path = external
|
||||
.path()
|
||||
.join(format!("rollout-2025-01-03T12-00-00-{uuid}.jsonl"));
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
let mut builder = ThreadMetadataBuilder::new(
|
||||
thread_id,
|
||||
rollout_path.clone(),
|
||||
@@ -1011,15 +1045,20 @@ mod tests {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let external = TempDir::new().expect("external temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(216);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let rollout_path = external
|
||||
.path()
|
||||
.join(format!("rollout-2025-01-03T12-00-00-{uuid}.jsonl"));
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let mut builder =
|
||||
ThreadMetadataBuilder::new(thread_id, rollout_path, Utc::now(), SessionSource::Cli);
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
builder.archived_at = Some(Utc::now());
|
||||
let mut metadata = builder.build(config.default_model_provider_id.as_str());
|
||||
metadata.first_user_message = Some("Archived SQLite preview".to_string());
|
||||
@@ -1062,12 +1101,17 @@ mod tests {
|
||||
async fn read_thread_sqlite_fallback_loads_archived_history() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(219);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let archived_path = write_archived_session_file(home.path(), "2025-01-03T12-00-00", uuid)
|
||||
.expect("archived session file");
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
let mut builder = ThreadMetadataBuilder::new(
|
||||
thread_id,
|
||||
archived_path.clone(),
|
||||
@@ -1103,7 +1147,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn read_thread_fails_without_rollout() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(206);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
|
||||
|
||||
@@ -4,34 +4,18 @@ use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use codex_rollout::ARCHIVED_SESSIONS_SUBDIR;
|
||||
use codex_rollout::StateDbHandle;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::LocalThreadStore;
|
||||
use super::LocalThreadStoreConfig;
|
||||
|
||||
pub(super) fn test_config(codex_home: &Path) -> LocalThreadStoreConfig {
|
||||
LocalThreadStoreConfig {
|
||||
codex_home: codex_home.to_path_buf(),
|
||||
sqlite_home: codex_home.to_path_buf(),
|
||||
default_model_provider_id: "test-provider".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) async fn init_test_state_db(config: &LocalThreadStoreConfig) -> StateDbHandle {
|
||||
codex_state::StateRuntime::init(
|
||||
config.codex_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize")
|
||||
}
|
||||
|
||||
pub(super) async fn test_store(codex_home: &Path) -> LocalThreadStore {
|
||||
let config = test_config(codex_home);
|
||||
let state_db = init_test_state_db(&config).await;
|
||||
LocalThreadStore::new(config, state_db)
|
||||
}
|
||||
|
||||
pub(super) fn write_session_file(root: &Path, ts: &str, uuid: Uuid) -> std::io::Result<PathBuf> {
|
||||
write_session_file_with(
|
||||
root,
|
||||
|
||||
@@ -17,11 +17,11 @@ pub(super) async fn unarchive_thread(
|
||||
params: ArchiveThreadParams,
|
||||
) -> ThreadStoreResult<StoredThread> {
|
||||
let thread_id = params.thread_id;
|
||||
let state_db = store.state_db();
|
||||
let state_db_ctx = store.state_db().await;
|
||||
let archived_path = find_archived_thread_path_by_id_str(
|
||||
store.config.codex_home.as_path(),
|
||||
&thread_id.to_string(),
|
||||
Some(state_db.as_ref()),
|
||||
state_db_ctx.as_deref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ThreadStoreError::InvalidRequest {
|
||||
@@ -73,10 +73,11 @@ pub(super) async fn unarchive_thread(
|
||||
message: format!("failed to update unarchived thread timestamp: {err}"),
|
||||
})?;
|
||||
|
||||
let _ = store
|
||||
.state_db()
|
||||
.mark_unarchived(thread_id, restored_path.as_path())
|
||||
.await;
|
||||
if let Some(ctx) = state_db_ctx {
|
||||
let _ = ctx
|
||||
.mark_unarchived(thread_id, restored_path.as_path())
|
||||
.await;
|
||||
}
|
||||
|
||||
let item = read_thread_item_from_rollout(restored_path.clone())
|
||||
.await
|
||||
@@ -111,15 +112,13 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::ThreadStore;
|
||||
use crate::local::LocalThreadStore;
|
||||
use crate::local::test_support::init_test_state_db;
|
||||
use crate::local::test_support::test_config;
|
||||
use crate::local::test_support::test_store;
|
||||
use crate::local::test_support::write_archived_session_file;
|
||||
|
||||
#[tokio::test]
|
||||
async fn unarchive_thread_restores_rollout_and_returns_updated_thread() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(203);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let archived_path = write_archived_session_file(home.path(), "2025-01-03T13-00-00", uuid)
|
||||
@@ -150,12 +149,21 @@ mod tests {
|
||||
async fn unarchive_thread_updates_sqlite_metadata_when_present() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(204);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let archived_path = write_archived_session_file(home.path(), "2025-01-03T13-00-00", uuid)
|
||||
.expect("archived session file");
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
home.path().to_path_buf(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
runtime
|
||||
.mark_backfill_complete(/*last_watermark*/ None)
|
||||
.await
|
||||
.expect("backfill should be complete");
|
||||
let mut builder = codex_state::ThreadMetadataBuilder::new(
|
||||
thread_id,
|
||||
archived_path.clone(),
|
||||
|
||||
@@ -55,8 +55,9 @@ pub(super) async fn update_thread_metadata(
|
||||
.await?;
|
||||
}
|
||||
|
||||
let state_db_ctx = store.state_db().await;
|
||||
codex_rollout::state_db::reconcile_rollout(
|
||||
Some(store.state_db()).as_deref(),
|
||||
state_db_ctx.as_deref(),
|
||||
resolved_rollout_path.path.as_path(),
|
||||
store.config.default_model_provider_id.as_str(),
|
||||
/*builder*/ None,
|
||||
@@ -72,7 +73,11 @@ pub(super) async fn update_thread_metadata(
|
||||
|
||||
let resolved_git_info = match git_info {
|
||||
Some(git_info) => {
|
||||
let state_db = store.state_db();
|
||||
let Some(state_db) = store.state_db().await else {
|
||||
return Err(ThreadStoreError::Internal {
|
||||
message: format!("sqlite state db unavailable for thread {thread_id}"),
|
||||
});
|
||||
};
|
||||
let metadata =
|
||||
state_db
|
||||
.get_thread(thread_id)
|
||||
@@ -152,7 +157,11 @@ async fn apply_thread_git_info(
|
||||
branch: &Option<String>,
|
||||
origin_url: &Option<String>,
|
||||
) -> ThreadStoreResult<()> {
|
||||
let state_db = store.state_db();
|
||||
let Some(state_db) = store.state_db().await else {
|
||||
return Err(ThreadStoreError::Internal {
|
||||
message: format!("sqlite state db unavailable for thread {thread_id}"),
|
||||
});
|
||||
};
|
||||
let updated = state_db
|
||||
.update_thread_git_info(
|
||||
thread_id,
|
||||
@@ -232,17 +241,18 @@ async fn apply_thread_name(
|
||||
thread_id: ThreadId,
|
||||
name: String,
|
||||
) -> ThreadStoreResult<()> {
|
||||
let updated = store
|
||||
.state_db()
|
||||
.update_thread_title(thread_id, &name)
|
||||
.await
|
||||
.map_err(|err| ThreadStoreError::Internal {
|
||||
message: format!("failed to set thread name: {err}"),
|
||||
})?;
|
||||
if !updated {
|
||||
return Err(ThreadStoreError::Internal {
|
||||
message: format!("thread metadata unavailable before name update: {thread_id}"),
|
||||
});
|
||||
if let Some(state_db) = store.state_db().await {
|
||||
let updated = state_db
|
||||
.update_thread_title(thread_id, &name)
|
||||
.await
|
||||
.map_err(|err| ThreadStoreError::Internal {
|
||||
message: format!("failed to set thread name: {err}"),
|
||||
})?;
|
||||
if !updated {
|
||||
return Err(ThreadStoreError::Internal {
|
||||
message: format!("thread metadata unavailable before name update: {thread_id}"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
append_thread_name(store.config.codex_home.as_path(), thread_id, &name)
|
||||
@@ -300,11 +310,11 @@ async fn resolve_rollout_path(
|
||||
return Ok(ResolvedRolloutPath { path, archived });
|
||||
}
|
||||
|
||||
let state_db = store.state_db();
|
||||
let state_db_ctx = store.state_db().await;
|
||||
let active_path = find_thread_path_by_id_str(
|
||||
store.config.codex_home.as_path(),
|
||||
&thread_id.to_string(),
|
||||
Some(state_db.as_ref()),
|
||||
state_db_ctx.as_deref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ThreadStoreError::InvalidRequest {
|
||||
@@ -324,7 +334,7 @@ async fn resolve_rollout_path(
|
||||
find_archived_thread_path_by_id_str(
|
||||
store.config.codex_home.as_path(),
|
||||
&thread_id.to_string(),
|
||||
Some(state_db.as_ref()),
|
||||
state_db_ctx.as_deref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| ThreadStoreError::InvalidRequest {
|
||||
@@ -359,16 +369,14 @@ mod tests {
|
||||
use crate::ThreadPersistenceMetadata;
|
||||
use crate::ThreadStore;
|
||||
use crate::local::LocalThreadStore;
|
||||
use crate::local::test_support::init_test_state_db;
|
||||
use crate::local::test_support::test_config;
|
||||
use crate::local::test_support::test_store;
|
||||
use crate::local::test_support::write_archived_session_file;
|
||||
use crate::local::test_support::write_session_file;
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_thread_metadata_sets_name_on_active_rollout_and_indexes_name() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(301);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
write_session_file(home.path(), "2025-01-03T14-00-00", uuid).expect("session file");
|
||||
@@ -390,26 +398,24 @@ mod tests {
|
||||
.await
|
||||
.expect("find thread name");
|
||||
assert_eq!(latest_name.as_deref(), Some("A sharper name"));
|
||||
|
||||
let metadata = store
|
||||
.state_db()
|
||||
.get_thread(thread_id)
|
||||
.await
|
||||
.expect("get metadata")
|
||||
.expect("metadata");
|
||||
assert_eq!(metadata.title, "A sharper name");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_thread_metadata_sets_memory_mode_on_active_rollout() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(302);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let path =
|
||||
write_session_file(home.path(), "2025-01-03T14-30-00", uuid).expect("session file");
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
home.path().to_path_buf(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
|
||||
let thread = store
|
||||
.update_thread_metadata(UpdateThreadMetadataParams {
|
||||
thread_id,
|
||||
@@ -442,8 +448,13 @@ mod tests {
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let path =
|
||||
write_session_file(home.path(), "2025-01-03T18-30-00", uuid).expect("session file");
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
|
||||
store
|
||||
.update_thread_metadata(UpdateThreadMetadataParams {
|
||||
@@ -502,7 +513,7 @@ mod tests {
|
||||
async fn update_thread_metadata_uses_live_rollout_path_for_external_resume() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let external_home = TempDir::new().expect("external temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(307);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let path = write_session_file(external_home.path(), "2025-01-03T14-45-00", uuid)
|
||||
@@ -543,8 +554,13 @@ mod tests {
|
||||
async fn update_thread_metadata_sets_git_info() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config, runtime);
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config, Some(runtime));
|
||||
let uuid = Uuid::from_u128(309);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
write_session_file(home.path(), "2025-01-03T17-00-00", uuid).expect("session file");
|
||||
@@ -581,8 +597,13 @@ mod tests {
|
||||
async fn update_thread_metadata_partially_updates_git_info() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config, runtime);
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config, Some(runtime));
|
||||
let uuid = Uuid::from_u128(310);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
write_session_file(home.path(), "2025-01-03T17-30-00", uuid).expect("session file");
|
||||
@@ -634,8 +655,13 @@ mod tests {
|
||||
async fn update_thread_metadata_clears_git_info_fields() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
let uuid = Uuid::from_u128(311);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let path =
|
||||
@@ -799,7 +825,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn update_thread_metadata_rejects_mismatched_session_meta_id() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let filename_uuid = Uuid::from_u128(303);
|
||||
let metadata_uuid = Uuid::from_u128(304);
|
||||
let thread_id = ThreadId::from_string(&filename_uuid.to_string()).expect("valid thread id");
|
||||
@@ -831,7 +857,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn update_thread_metadata_rejects_multi_field_patch_without_partial_write() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let store = test_store(home.path()).await;
|
||||
let store = LocalThreadStore::new(test_config(home.path()), /*state_db*/ None);
|
||||
let uuid = Uuid::from_u128(305);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let path =
|
||||
@@ -866,12 +892,21 @@ mod tests {
|
||||
async fn update_thread_metadata_keeps_archived_thread_archived_in_sqlite() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(306);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let archived_path = write_archived_session_file(home.path(), "2025-01-03T16-00-00", uuid)
|
||||
.expect("archived session file");
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
home.path().to_path_buf(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
runtime
|
||||
.mark_backfill_complete(/*last_watermark*/ None)
|
||||
.await
|
||||
.expect("backfill should be complete");
|
||||
codex_rollout::state_db::reconcile_rollout(
|
||||
Some(runtime.as_ref()),
|
||||
archived_path.as_path(),
|
||||
@@ -920,12 +955,21 @@ mod tests {
|
||||
async fn update_thread_metadata_keeps_live_archived_thread_archived_in_sqlite() {
|
||||
let home = TempDir::new().expect("temp dir");
|
||||
let config = test_config(home.path());
|
||||
let runtime = init_test_state_db(&config).await;
|
||||
let store = LocalThreadStore::new(config.clone(), runtime.clone());
|
||||
let uuid = Uuid::from_u128(308);
|
||||
let thread_id = ThreadId::from_string(&uuid.to_string()).expect("valid thread id");
|
||||
let archived_path = write_archived_session_file(home.path(), "2025-01-03T16-30-00", uuid)
|
||||
.expect("archived session file");
|
||||
let runtime = codex_state::StateRuntime::init(
|
||||
home.path().to_path_buf(),
|
||||
config.default_model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should initialize");
|
||||
let store = LocalThreadStore::new(config.clone(), Some(runtime.clone()));
|
||||
runtime
|
||||
.mark_backfill_complete(/*last_watermark*/ None)
|
||||
.await
|
||||
.expect("backfill should be complete");
|
||||
codex_rollout::state_db::reconcile_rollout(
|
||||
Some(runtime.as_ref()),
|
||||
archived_path.as_path(),
|
||||
|
||||
Reference in New Issue
Block a user