mirror of
https://github.com/openai/codex.git
synced 2026-11-13 06:45:07 +00:00
Support metadata staging for reserved thread IDs (#38819)
## Why Callers need to associate host-owned state with a thread before Core starts it. ## What changed - Add `ThreadManager::reserve_thread_id` and allow new threads to start with the reserved ID. Reject reserved IDs when resuming an existing thread. - Let thread stores stage metadata for a reserved ID. The local store merges staged values into the first successful metadata update and then removes them. - Clear staged metadata when an unmaterialized thread shuts down or is discarded, and require the state database for staging. ## Testing - Cover reserved ID generation and resume validation. - Cover metadata persistence, merging, rollout-compatible updates, cleanup, and invalid staging requests. GitOrigin-RevId: 3f9bc48f0f7764011cc90ea01349e497dda0a9e3
This commit is contained in:
1 parent
5ba12929f8
commit
fcdae21073
17 files changed
+650
-21
No files matched your search
@@ -58,6 +58,62 @@ fn thread_id_generator_defaults_to_standard_ids() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reserved_thread_id_is_used_without_changing_normal_id_generation() {
|
||||
let temp_dir = tempdir().expect("tempdir");
|
||||
let mut config = test_config().await;
|
||||
config.codex_home = temp_dir.path().join("codex-home").abs();
|
||||
config.cwd = config.codex_home.abs();
|
||||
std::fs::create_dir_all(&config.codex_home).expect("create codex home");
|
||||
|
||||
let generated_ids = [
|
||||
ThreadId::from_u128(/*value*/ 0x018f_0000_0000_7000_8000_0000_0000_0001),
|
||||
ThreadId::from_u128(/*value*/ 0x018f_0000_0000_7000_8000_0000_0000_0002),
|
||||
ThreadId::from_u128(/*value*/ 0x018f_0000_0000_7000_8000_0000_0000_0003),
|
||||
];
|
||||
let next_id = std::sync::atomic::AtomicUsize::new(0);
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()),
|
||||
)
|
||||
.with_thread_id_generator(move || generated_ids[next_id.fetch_add(1, Ordering::Relaxed)]);
|
||||
|
||||
let reserved_id = manager.reserve_thread_id();
|
||||
let mut reserved_options = StartThreadOptions::new(config.clone());
|
||||
reserved_options.reserved_thread_id = Some(reserved_id);
|
||||
let reserved = manager
|
||||
.start_thread(reserved_options)
|
||||
.await
|
||||
.expect("start reserved thread");
|
||||
let mut resumed_options = StartThreadOptions::new(config.clone());
|
||||
resumed_options.initial_history = InitialHistory::Resumed(ResumedHistory {
|
||||
conversation_id: reserved.thread_id,
|
||||
history: Arc::new(Vec::new()),
|
||||
rollout_path: None,
|
||||
});
|
||||
let resumed_id = manager.reserve_thread_id();
|
||||
resumed_options.reserved_thread_id = Some(resumed_id);
|
||||
let resume_error = manager
|
||||
.start_thread(resumed_options)
|
||||
.await
|
||||
.err()
|
||||
.expect("reject reserved ID for resume");
|
||||
let generated = manager
|
||||
.start_thread(StartThreadOptions::new(config.clone()))
|
||||
.await
|
||||
.expect("start generated thread");
|
||||
|
||||
assert_eq!(reserved.thread_id, generated_ids[0]);
|
||||
assert!(matches!(
|
||||
resume_error.details(),
|
||||
codex_protocol::error::CodexErrorDetails::InvalidRequest(message)
|
||||
if message == "reserved thread ID cannot be used when resuming a thread"
|
||||
));
|
||||
assert_eq!(generated.thread_id, generated_ids[2]);
|
||||
}
|
||||
|
||||
/// One custom ID factory supplies identifiers for roots, actual child agents, and forks.
|
||||
#[tokio::test]
|
||||
async fn thread_id_generator_applies_to_roots_children_and_forks() {
|
||||
|
||||
Reference in new issue
Block a user