mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Add thread archive CLI commands (#25021)
## Problem Saved threads can already be archived through app-server RPCs, but the command line did not expose direct archive or unarchive commands. ## Solution Add `codex archive <thread>` and `codex unarchive <thread>`, resolving UUIDs or exact thread names before calling the existing `thread/archive` and `thread/unarchive` RPCs. The commands support scoped remote flags so callers can target remote app-server endpoints when archiving or unarchiving threads. This also fixes a long-standing bug in `codex resume <thread id>` and `codex fork <thread id>` that I found when testing the new commands. These operations shouldn't be allowed on archived sessions. They now fail with an error that tells the user to run `codex unarchive <thread id>` first. ## Verification Added app-server coverage for rejecting archived thread resume by id and checking that the error includes the matching `codex unarchive <thread id>` command.
This commit is contained in:
committed by
GitHub
Unverified
parent
e0435afb72
commit
3e7baa00e4
@@ -1308,7 +1308,7 @@ impl ThreadRequestProcessor {
|
||||
params: ThreadArchiveParams,
|
||||
) -> Result<(ThreadArchiveResponse, Vec<String>), JSONRPCErrorError> {
|
||||
let thread_id = ThreadId::from_string(¶ms.thread_id)
|
||||
.map_err(|err| invalid_request(format!("invalid thread id: {err}")))?;
|
||||
.map_err(|err| invalid_request(format!("invalid session id: {err}")))?;
|
||||
|
||||
let mut thread_ids = vec![thread_id];
|
||||
if let Some(state_db_ctx) = self.state_db.as_ref() {
|
||||
@@ -1317,7 +1317,7 @@ impl ThreadRequestProcessor {
|
||||
.await
|
||||
.map_err(|err| {
|
||||
internal_error(format!(
|
||||
"failed to list spawned descendants for thread id {thread_id}: {err}"
|
||||
"failed to list spawned descendants for session {thread_id}: {err}"
|
||||
))
|
||||
})?;
|
||||
let mut seen = HashSet::from([thread_id]);
|
||||
@@ -1629,7 +1629,7 @@ impl ThreadRequestProcessor {
|
||||
params: ThreadUnarchiveParams,
|
||||
) -> Result<(ThreadUnarchiveResponse, String), JSONRPCErrorError> {
|
||||
let thread_id = ThreadId::from_string(¶ms.thread_id)
|
||||
.map_err(|err| invalid_request(format!("invalid thread id: {err}")))?;
|
||||
.map_err(|err| invalid_request(format!("invalid session id: {err}")))?;
|
||||
|
||||
let fallback_provider = self.config.model_provider_id.clone();
|
||||
let stored_thread = self
|
||||
@@ -2968,7 +2968,7 @@ impl ThreadRequestProcessor {
|
||||
let existing_thread_id = match ThreadId::from_string(thread_id) {
|
||||
Ok(id) => id,
|
||||
Err(err) => {
|
||||
return Err(invalid_request(format!("invalid thread id: {err}")));
|
||||
return Err(invalid_request(format!("invalid session id: {err}")));
|
||||
}
|
||||
};
|
||||
let params = StoreReadThreadParams {
|
||||
@@ -2979,7 +2979,15 @@ impl ThreadRequestProcessor {
|
||||
self.thread_store.read_thread(params).await
|
||||
};
|
||||
|
||||
result.map_err(thread_store_resume_read_error)
|
||||
let stored_thread = result.map_err(thread_store_resume_read_error)?;
|
||||
if stored_thread.archived_at.is_some() {
|
||||
let thread_id = stored_thread.thread_id;
|
||||
return Err(invalid_request(format!(
|
||||
"session {thread_id} is archived. Run `codex unarchive {thread_id}` to unarchive it first."
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(stored_thread)
|
||||
}
|
||||
|
||||
async fn stored_thread_to_initial_history(
|
||||
@@ -3964,7 +3972,7 @@ fn thread_store_archive_error(operation: &str, err: ThreadStoreError) -> JSONRPC
|
||||
ThreadStoreError::Unsupported {
|
||||
operation: unsupported_operation,
|
||||
} => unsupported_thread_store_operation(unsupported_operation),
|
||||
err => internal_error(format!("failed to {operation} thread: {err}")),
|
||||
err => internal_error(format!("failed to {operation} session: {err}")),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user