Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
0a6d2d5
adding session naming with /rename and metadata storage in SessionMeta
pap-openai Jan 5, 2026
bfc7a3e
fix Restore writer file handle on rename failure
pap-openai Jan 6, 2026
27bbc02
changing attribute to name + support command with arg
pap-openai Jan 9, 2026
41e7e02
fix
pap-openai Jan 9, 2026
9f6aac5
Adding session_configured_event & /status output of session name
pap-openai Jan 9, 2026
4d6e184
+docs
pap-openai Jan 9, 2026
3fd2ccc
resume work wip
pap-openai Jan 9, 2026
bbab77f
Merge branch 'resume-name' into session-naming-clean
pap-openai Jan 9, 2026
a0f9f68
safe session_name + always showing session name in /status
pap-openai Jan 9, 2026
eebd0a2
adding appexitinfo hint codex resume <name> if set
pap-openai Jan 9, 2026
32d4278
uses thread_name instead of session_name
pap-openai Jan 12, 2026
aa4d062
adds shell escaping for exit hint info instead of restrictive session…
pap-openai Jan 12, 2026
3186b17
moving to index file instead of sessionmeta
pap-openai Jan 12, 2026
8d57df0
removing name from SessionMeta
pap-openai Jan 13, 2026
d9c675b
ThreadNameUpdated instead of SessionMetaUpdated
pap-openai Jan 13, 2026
1e568e6
moving crate up
pap-openai Jan 13, 2026
b385eb8
reverting changes in codex-rs/core/src/rollout/list.rs
pap-openai Jan 13, 2026
2da5bf2
revert rollout changes
pap-openai Jan 13, 2026
e62459e
adding test for resume command with id
pap-openai Jan 13, 2026
b8de956
adding hint on uuid or name on exit
pap-openai Jan 13, 2026
9de9d3e
naming clarity
pap-openai Jan 13, 2026
61a0477
documenting public function + thread_id rename
pap-openai Jan 13, 2026
00ec8d1
app-server v2 + thread_id
pap-openai Jan 13, 2026
4c32273
Merge main into session-naming-clean
pap-openai Jan 15, 2026
c12882c
adding thread_name in fork
pap-openai Jan 15, 2026
cb22c11
Merge branch 'main' into session-naming-clean
pap-openai Jan 16, 2026
8456127
Merge branch 'main' into session-naming-clean
pap-openai Jan 16, 2026
b4c12d6
Merge branch 'main' into session-naming-clean
pap-openai Jan 18, 2026
9369962
merge
pap-openai Jan 18, 2026
66dac4a
Merge branch 'main' into session-naming-clean
pap-openai Jan 19, 2026
ebf5168
Merge remote-tracking branch 'origin/main' into session-naming-clean
jif-oai Jan 19, 2026
34648ea
adressing jif's comment
pap-openai Jan 21, 2026
842431f
Merge branch 'main' into session-naming-clean
pap-openai Jan 21, 2026
e572fa4
Merge branch 'main' into session-naming-clean
pap-openai Jan 21, 2026
7b7592e
rever tui2 changes
pap-openai Jan 21, 2026
7dce954
revert tui2 changes for rename
pap-openai Jan 21, 2026
f943f6a
Merge branch 'main' into session-naming-clean
pap-openai Jan 25, 2026
701b69b
Merge branch 'main' into session-naming-clean
pap-openai Jan 25, 2026
b27b049
remove usage of original_config_do_not_use
pap-openai Jan 25, 2026
f6e40d5
Merge branch 'session-naming-clean' of github.com:openai/codex into s…
pap-openai Jan 26, 2026
8458bee
persistence enabled smaller scope
pap-openai Jan 26, 2026
51b32f6
adding tests for multiple or no entries
pap-openai Jan 26, 2026
b859ba1
adding hint for resume on renaming
pap-openai Jan 26, 2026
4aa8c6a
Merge remote-tracking branch 'origin' into session-naming-clean
pap-openai Jan 26, 2026
35e4a0d
Merge branch 'main' into session-naming-clean
pap-openai Jan 28, 2026
84324c0
add rename tooltip
pap-openai Jan 28, 2026
2eb7459
Merge branch 'main' into session-naming-clean
pap-openai Jan 28, 2026
00c061f
fix two atomic writes
pap-openai Jan 28, 2026
99e0835
Merge branch 'session-naming-clean' of github.com:openai/codex into s…
pap-openai Jan 28, 2026
5d90138
Merge branch 'main' into session-naming-clean
pap-openai Jan 29, 2026
38b9d8b
Merge branch 'main' into session-naming-clean
pap-openai Jan 29, 2026
e71c628
fix
pap-openai Jan 29, 2026
6899728
Merge branch 'session-naming-clean' of github.com:openai/codex into s…
pap-openai Jan 29, 2026
e021ed2
Merge branch 'main' into session-naming-clean
pap-openai Jan 30, 2026
112e5b6
fix
pap-openai Jan 30, 2026
e7abfec
Merge branch 'main' into session-naming-clean
pap-openai Jan 30, 2026
9998ca8
Merge branch 'main' into session-naming-clean
pap-openai Jan 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ client_request_definitions! {
params: v2::ThreadArchiveParams,
response: v2::ThreadArchiveResponse,
},
ThreadSetName => "thread/name/set" {
params: v2::ThreadSetNameParams,
response: v2::ThreadSetNameResponse,
},
ThreadUnarchive => "thread/unarchive" {
params: v2::ThreadUnarchiveParams,
response: v2::ThreadUnarchiveResponse,
Expand Down Expand Up @@ -598,6 +602,7 @@ server_notification_definitions! {
/// NEW NOTIFICATIONS
Error => "error" (v2::ErrorNotification),
ThreadStarted => "thread/started" (v2::ThreadStartedNotification),
ThreadNameUpdated => "thread/name/updated" (v2::ThreadNameUpdatedNotification),
ThreadTokenUsageUpdated => "thread/tokenUsage/updated" (v2::ThreadTokenUsageUpdatedNotification),
TurnStarted => "turn/started" (v2::TurnStartedNotification),
TurnCompleted => "turn/completed" (v2::TurnCompletedNotification),
Expand Down
23 changes: 23 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1295,13 +1295,26 @@ pub struct ThreadArchiveParams {
#[ts(export_to = "v2/")]
pub struct ThreadArchiveResponse {}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadSetNameParams {
pub thread_id: String,
pub name: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadUnarchiveParams {
pub thread_id: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadSetNameResponse {}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
Expand Down Expand Up @@ -2285,6 +2298,16 @@ pub struct ThreadStartedNotification {
pub thread: Thread,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct ThreadNameUpdatedNotification {
pub thread_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
pub thread_name: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
Expand Down
1 change: 1 addition & 0 deletions codex-rs/app-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Example (from OpenAI's official VSCode extension):
- `thread/loaded/list` — list the thread ids currently loaded in memory.
- `thread/read` — read a stored thread by id without resuming it; optionally include turns via `includeTurns`.
- `thread/archive` — move a thread’s rollout file into the archived directory; returns `{}` on success.
- `thread/name/set` — set or update a thread’s user-facing name; returns `{}` on success. Thread names are not required to be unique; name lookups resolve to the most recently updated thread.
- `thread/unarchive` — move an archived rollout file back into the sessions directory; returns the restored `thread` on success.
- `thread/rollback` — drop the last N turns from the agent’s in-memory context and persist a rollback marker in the rollout so future resumes see the pruned history; returns the updated `thread` (with `turns` populated) on success.
- `turn/start` — add user input to a thread and begin Codex generation; responds with the initial `turn` object and streams `turn/started`, `item/*`, and `turn/completed` notifications.
Expand Down
12 changes: 12 additions & 0 deletions codex-rs/app-server/src/bespoke_event_handling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use codex_app_server_protocol::ServerNotification;
use codex_app_server_protocol::ServerRequestPayload;
use codex_app_server_protocol::TerminalInteractionNotification;
use codex_app_server_protocol::ThreadItem;
use codex_app_server_protocol::ThreadNameUpdatedNotification;
use codex_app_server_protocol::ThreadRollbackResponse;
use codex_app_server_protocol::ThreadTokenUsage;
use codex_app_server_protocol::ThreadTokenUsageUpdatedNotification;
Expand Down Expand Up @@ -1097,6 +1098,17 @@ pub(crate) async fn apply_bespoke_event_handling(
outgoing.send_response(request_id, response).await;
}
}
EventMsg::ThreadNameUpdated(thread_name_event) => {
if let ApiVersion::V2 = api_version {
let notification = ThreadNameUpdatedNotification {
thread_id: thread_name_event.thread_id.to_string(),
thread_name: thread_name_event.thread_name,
};
outgoing
.send_server_notification(ServerNotification::ThreadNameUpdated(notification))
.await;
}
}
EventMsg::TurnDiff(turn_diff_event) => {
handle_turn_diff(
conversation_id,
Expand Down
35 changes: 35 additions & 0 deletions codex-rs/app-server/src/codex_message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ use codex_app_server_protocol::ThreadReadResponse;
use codex_app_server_protocol::ThreadResumeParams;
use codex_app_server_protocol::ThreadResumeResponse;
use codex_app_server_protocol::ThreadRollbackParams;
use codex_app_server_protocol::ThreadSetNameParams;
use codex_app_server_protocol::ThreadSetNameResponse;
use codex_app_server_protocol::ThreadSortKey;
use codex_app_server_protocol::ThreadSourceKind;
use codex_app_server_protocol::ThreadStartParams;
Expand Down Expand Up @@ -416,6 +418,9 @@ impl CodexMessageProcessor {
ClientRequest::ThreadArchive { request_id, params } => {
self.thread_archive(request_id, params).await;
}
ClientRequest::ThreadSetName { request_id, params } => {
self.thread_set_name(request_id, params).await;
}
ClientRequest::ThreadUnarchive { request_id, params } => {
self.thread_unarchive(request_id, params).await;
}
Expand Down Expand Up @@ -1780,6 +1785,36 @@ impl CodexMessageProcessor {
}
}

async fn thread_set_name(&self, request_id: RequestId, params: ThreadSetNameParams) {
let ThreadSetNameParams { thread_id, name } = params;
let Some(name) = codex_core::util::normalize_thread_name(&name) else {
self.send_invalid_request_error(
request_id,
"thread name must not be empty".to_string(),
)
.await;
return;
};

let (_, thread) = match self.load_thread(&thread_id).await {
Ok(v) => v,
Err(error) => {
self.outgoing.send_error(request_id, error).await;
return;
}
};

if let Err(err) = thread.submit(Op::SetThreadName { name }).await {
self.send_internal_error(request_id, format!("failed to set thread name: {err}"))
.await;
return;
}

self.outgoing
.send_response(request_id, ThreadSetNameResponse {})
.await;
Comment on lines +1813 to +1815
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don’t acknowledge rename before it succeeds

The handler sends a success response immediately after enqueueing Op::SetThreadName, but the core implementation can still fail later (e.g., persistence disabled or a write error when appending to the session index), in which case it emits an Error event and never sends ThreadNameUpdated (see codex-rs/core/src/codex.rs around lines 2953–2979). That means JSON-RPC clients that rely on the response will think the rename succeeded even though it didn’t persist, and resume-by-name will silently fail. Consider waiting for the ThreadNameUpdated/error event (similar to thread_rollback) or pre-checking persistence and returning an error response.

Useful? React with 👍 / 👎.

Comment on lines +1813 to +1815
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return an error when rename fails in core

The thread/name/set handler sends a success response immediately after enqueueing Op::SetThreadName, but the core handler can still fail later (e.g., persistence disabled or session_index.jsonl write errors) and emits an Error event instead of a ThreadNameUpdated event (see codex-rs/core/src/codex.rs around the set_thread_name error branches). In those cases clients get {} even though the rename didn’t persist, so UIs may update state incorrectly unless they separately watch for error notifications. Consider waiting for a ThreadNameUpdated/Error event to reply, or pre-check persistence and IO failures so the RPC response accurately reflects success.

Useful? React with 👍 / 👎.

Comment on lines +1813 to +1815
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don’t acknowledge rename before it succeeds

The app-server replies success immediately after enqueuing Op::SetThreadName, but the actual rename can still fail later (e.g., session persistence disabled or I/O error appending the session index in codex-rs/core/src/codex.rs around the set_thread_name handler). In those cases the client will already have received {} as a success response even though an Error event is emitted and the name is not updated, which can leave the UI/state inconsistent. Consider deferring the response until a ThreadNameUpdated event arrives for this request, or translating the failure Error into a request error response.

Useful? React with 👍 / 👎.

}

async fn thread_unarchive(&mut self, request_id: RequestId, params: ThreadUnarchiveParams) {
// TODO(jif) mostly rewrite this using sqlite after phase 1
let thread_id = match ThreadId::from_string(&params.thread_id) {
Expand Down
36 changes: 29 additions & 7 deletions codex-rs/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ struct CompletionCommand {

#[derive(Debug, Parser)]
struct ResumeCommand {
/// Conversation/session id (UUID). When provided, resumes this session.
/// Conversation/session id (UUID) or thread name. UUIDs take precedence if it parses.
/// If omitted, use --last to pick the most recent recorded session.
#[arg(value_name = "SESSION_ID")]
session_id: Option<String>,
Expand Down Expand Up @@ -323,6 +323,7 @@ fn format_exit_messages(exit_info: AppExitInfo, color_enabled: bool) -> Vec<Stri
let AppExitInfo {
token_usage,
thread_id: conversation_id,
thread_name,
..
} = exit_info;

Expand All @@ -335,8 +336,9 @@ fn format_exit_messages(exit_info: AppExitInfo, color_enabled: bool) -> Vec<Stri
codex_core::protocol::FinalOutput::from(token_usage)
)];

if let Some(session_id) = conversation_id {
let resume_cmd = format!("codex resume {session_id}");
if let Some(resume_cmd) =
codex_core::util::resume_command(thread_name.as_deref(), conversation_id)
{
let command = if color_enabled {
resume_cmd.cyan().to_string()
} else {
Expand Down Expand Up @@ -1028,15 +1030,18 @@ mod tests {
app_server
}

fn sample_exit_info(conversation: Option<&str>) -> AppExitInfo {
fn sample_exit_info(conversation_id: Option<&str>, thread_name: Option<&str>) -> AppExitInfo {
let token_usage = TokenUsage {
output_tokens: 2,
total_tokens: 2,
..Default::default()
};
AppExitInfo {
token_usage,
thread_id: conversation.map(ThreadId::from_string).map(Result::unwrap),
thread_id: conversation_id
.map(ThreadId::from_string)
.map(Result::unwrap),
thread_name: thread_name.map(str::to_string),
update_action: None,
exit_reason: ExitReason::UserRequested,
}
Expand All @@ -1047,6 +1052,7 @@ mod tests {
let exit_info = AppExitInfo {
token_usage: TokenUsage::default(),
thread_id: None,
thread_name: None,
update_action: None,
exit_reason: ExitReason::UserRequested,
};
Expand All @@ -1056,7 +1062,7 @@ mod tests {

#[test]
fn format_exit_messages_includes_resume_hint_without_color() {
let exit_info = sample_exit_info(Some("123e4567-e89b-12d3-a456-426614174000"));
let exit_info = sample_exit_info(Some("123e4567-e89b-12d3-a456-426614174000"), None);
let lines = format_exit_messages(exit_info, false);
assert_eq!(
lines,
Expand All @@ -1070,12 +1076,28 @@ mod tests {

#[test]
fn format_exit_messages_applies_color_when_enabled() {
let exit_info = sample_exit_info(Some("123e4567-e89b-12d3-a456-426614174000"));
let exit_info = sample_exit_info(Some("123e4567-e89b-12d3-a456-426614174000"), None);
let lines = format_exit_messages(exit_info, true);
assert_eq!(lines.len(), 2);
assert!(lines[1].contains("\u{1b}[36m"));
}

#[test]
fn format_exit_messages_prefers_thread_name() {
let exit_info = sample_exit_info(
Some("123e4567-e89b-12d3-a456-426614174000"),
Some("my-thread"),
);
let lines = format_exit_messages(exit_info, false);
assert_eq!(
lines,
vec![
"Token usage: total=2 input=0 output=2".to_string(),
"To continue this session, run codex resume my-thread".to_string(),
]
);
}

#[test]
fn resume_model_flag_applies_when_no_root_flags() {
let interactive =
Expand Down
Loading
Loading