Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 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
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 @@ -117,6 +117,10 @@ client_request_definitions! {
params: v2::ThreadArchiveParams,
response: v2::ThreadArchiveResponse,
},
ThreadSetName => "thread/name/set" {
params: v2::ThreadSetNameParams,
response: v2::ThreadSetNameResponse,
},
ThreadRollback => "thread/rollback" {
params: v2::ThreadRollbackParams,
response: v2::ThreadRollbackResponse,
Expand Down Expand Up @@ -545,6 +549,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 @@ -1150,6 +1150,19 @@ 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 ThreadSetNameResponse {}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
Expand Down Expand Up @@ -1964,6 +1977,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 @@ -80,6 +80,7 @@ Example (from OpenAI's official VSCode extension):
- `thread/list` — page through stored rollouts; supports cursor-based pagination and optional `modelProviders` filtering.
- `thread/loaded/list` — list the thread ids currently loaded in memory.
- `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/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.
- `turn/interrupt` — request cancellation of an in-flight turn by `(thread_id, turn_id)`; success is an empty `{}` response and the turn finishes with `status: "interrupted"`.
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 @@ -51,6 +51,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 @@ -991,6 +992,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
41 changes: 41 additions & 0 deletions codex-rs/app-server/src/codex_message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ use codex_app_server_protocol::ThreadLoadedListResponse;
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::ThreadStartParams;
use codex_app_server_protocol::ThreadStartResponse;
use codex_app_server_protocol::ThreadStartedNotification;
Expand Down Expand Up @@ -385,6 +387,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::ThreadRollback { request_id, params } => {
self.thread_rollback(request_id, params).await;
}
Expand Down Expand Up @@ -1549,6 +1554,42 @@ impl CodexMessageProcessor {
}
}

async fn thread_set_name(&self, request_id: RequestId, params: ThreadSetNameParams) {
let ThreadSetNameParams { thread_id, name } = params;
let trimmed = name.trim();
if trimmed.is_empty() {
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: trimmed.to_string(),
})
.await
{
self.send_internal_error(request_id, format!("failed to set thread name: {err}"))
.await;
return;
}

self.outgoing
.send_response(request_id, ThreadSetNameResponse {})
.await;
}

async fn thread_rollback(&mut self, request_id: RequestId, params: ThreadRollbackParams) {
let ThreadRollbackParams {
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 @@ -146,7 +146,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 @@ -325,6 +325,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 @@ -337,8 +338,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 @@ -937,15 +939,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 @@ -956,6 +961,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 @@ -965,7 +971,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 @@ -979,12 +985,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