chore: merge name and title (#17116)

Merge title and name concept to leverage the sqlite title column and
have more efficient queries

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
jif-oai
2026-04-09 18:44:26 +01:00
committed by GitHub
Unverified
parent c0b5d8d24a
commit 12f0e0b0eb
16 changed files with 539 additions and 145 deletions
+73 -63
View File
@@ -33,7 +33,7 @@ use crate::realtime_conversation::handle_close as handle_realtime_conversation_c
use crate::realtime_conversation::handle_start as handle_realtime_conversation_start;
use crate::realtime_conversation::handle_text as handle_realtime_conversation_text;
use crate::render_skills_section;
use crate::rollout::session_index;
use crate::rollout::find_thread_name_by_id;
use crate::session_prefix::format_subagent_notification_message;
use crate::skills_load_input_from_config;
use crate::stream_events_utils::HandleOutputCtx;
@@ -1096,6 +1096,26 @@ fn local_time_context() -> (String, String) {
}
}
async fn thread_title_from_state_db(
state_db: Option<&state_db::StateDbHandle>,
codex_home: &Path,
conversation_id: ThreadId,
) -> Option<String> {
if let Some(metadata) = state_db
&& let Some(metadata) = metadata.get_thread(conversation_id).await.ok().flatten()
{
let title = metadata.title.trim();
if !title.is_empty() && metadata.first_user_message.as_deref().map(str::trim) != Some(title)
{
return Some(title.to_string());
}
}
find_thread_name_by_id(codex_home, &conversation_id)
.await
.ok()
.flatten()
}
#[derive(Clone)]
pub(crate) struct SessionConfiguration {
/// Provider identifier ("openai", "openrouter", ...).
@@ -1882,19 +1902,12 @@ impl Session {
tx
};
let thread_name =
match session_index::find_thread_name_by_id(&config.codex_home, &conversation_id)
thread_title_from_state_db(state_db_ctx.as_ref(), &config.codex_home, conversation_id)
.instrument(info_span!(
"session_init.thread_name_lookup",
otel.name = "session_init.thread_name_lookup",
))
.await
{
Ok(name) => name,
Err(err) => {
warn!("Failed to read session index for thread name: {err}");
None
}
};
.await;
session_configuration.thread_name = thread_name.clone();
let state = SessionState::new(session_configuration.clone());
let managed_network_requirements_enabled = config.managed_network_requirements_enabled();
@@ -4848,7 +4861,6 @@ mod handlers {
use crate::review_prompts::resolve_review_request;
use crate::rollout::RolloutRecorder;
use crate::rollout::session_index;
use crate::tasks::CompactTask;
use crate::tasks::UndoTask;
use crate::tasks::UserShellCommandMode;
@@ -5528,13 +5540,25 @@ mod handlers {
.await;
}
/// Persists the thread name in the session index, updates in-memory state, and emits
/// a `ThreadNameUpdated` event on success.
///
/// This appends the name to `CODEX_HOME/sessions_index.jsonl` via `session_index::append_thread_name` for the
/// current `thread_id`, then updates `SessionConfiguration::thread_name`.
///
/// Returns an error event if the name is empty or session persistence is disabled.
async fn persist_thread_name_update(
sess: &Arc<Session>,
event: ThreadNameUpdatedEvent,
) -> anyhow::Result<EventMsg> {
let msg = EventMsg::ThreadNameUpdated(event);
let item = RolloutItem::EventMsg(msg.clone());
let recorder = {
let guard = sess.services.rollout.lock().await;
guard.clone()
}
.ok_or_else(|| anyhow::anyhow!("Session persistence is disabled; cannot rename thread."))?;
recorder.persist().await?;
recorder.record_items(std::slice::from_ref(&item)).await?;
recorder.flush().await?;
Ok(msg)
}
/// Persists the thread name in the rollout and state database, updates in-memory state, and
/// emits a `ThreadNameUpdated` event on success.
pub async fn set_thread_name(sess: &Arc<Session>, sub_id: String, name: String) {
let Some(name) = crate::util::normalize_thread_name(&name) else {
let event = Event {
@@ -5548,47 +5572,33 @@ mod handlers {
return;
};
let persistence_enabled = {
let rollout = sess.services.rollout.lock().await;
rollout.is_some()
};
if !persistence_enabled {
let event = Event {
id: sub_id,
msg: EventMsg::Error(ErrorEvent {
message: "Session persistence is disabled; cannot rename thread.".to_string(),
codex_error_info: Some(CodexErrorInfo::Other),
}),
};
sess.send_event_raw(event).await;
return;
let updated = ThreadNameUpdatedEvent {
thread_id: sess.conversation_id,
thread_name: Some(name.clone()),
};
if let Err(e) = sess.try_ensure_rollout_materialized().await {
let event = Event {
id: sub_id,
msg: EventMsg::Error(ErrorEvent {
message: format!("Failed to set thread name: {e}"),
codex_error_info: Some(CodexErrorInfo::Other),
}),
};
sess.send_event_raw(event).await;
return;
}
let msg = match persist_thread_name_update(sess, updated).await {
Ok(msg) => msg,
Err(err) => {
warn!("Failed to persist thread name update to rollout: {err}");
let event = Event {
id: sub_id,
msg: EventMsg::Error(ErrorEvent {
message: err.to_string(),
codex_error_info: Some(CodexErrorInfo::Other),
}),
};
sess.send_event_raw(event).await;
return;
}
};
let codex_home = sess.codex_home().await;
if let Err(e) =
session_index::append_thread_name(&codex_home, sess.conversation_id, &name).await
if let Some(state_db) = sess.services.state_db.as_deref()
&& let Err(err) = state_db
.update_thread_title(sess.conversation_id, &name)
.await
{
let event = Event {
id: sub_id,
msg: EventMsg::Error(ErrorEvent {
message: format!("Failed to set thread name: {e}"),
codex_error_info: Some(CodexErrorInfo::Other),
}),
};
sess.send_event_raw(event).await;
return;
warn!("Failed to update thread title in state db: {err}");
}
{
@@ -5596,14 +5606,14 @@ mod handlers {
state.session_configuration.thread_name = Some(name.clone());
}
sess.send_event_raw(Event {
id: sub_id,
msg: EventMsg::ThreadNameUpdated(ThreadNameUpdatedEvent {
thread_id: sess.conversation_id,
thread_name: Some(name),
}),
})
.await;
let codex_home = sess.codex_home().await;
if let Err(err) =
crate::rollout::append_thread_name(&codex_home, sess.conversation_id, &name).await
{
warn!("Failed to update legacy thread name index: {err}");
}
sess.deliver_event_raw(Event { id: sub_id, msg }).await;
}
pub async fn shutdown(sess: &Arc<Session>, sub_id: String) -> bool {