chore: unify conversation with thread name (#8830)

Done and verified by Codex + refactor feature of RustRover
This commit is contained in:
jif-oai
2026-01-09 11:37:07 +00:00
committed by pap
parent 137b26bd46
commit ef39b121f2
83 changed files with 1094 additions and 1203 deletions
+27 -39
View File
@@ -40,7 +40,7 @@ use crate::tui::scrolling::TranscriptScroll;
use crate::update_action::UpdateAction;
use codex_ansi_escape::ansi_escape_line;
use codex_core::AuthManager;
use codex_core::ConversationManager;
use codex_core::ThreadManager;
use codex_core::config::Config;
use codex_core::config::edit::ConfigEditsBuilder;
#[cfg(target_os = "windows")]
@@ -56,7 +56,7 @@ use codex_core::protocol::SessionSource;
use codex_core::protocol::SkillErrorInfo;
use codex_core::protocol::TokenUsage;
use codex_core::terminal::terminal_info;
use codex_protocol::ConversationId;
use codex_protocol::ThreadId;
use codex_protocol::openai_models::ModelPreset;
use codex_protocol::openai_models::ModelUpgrade;
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
@@ -91,7 +91,7 @@ use crate::history_cell::UpdateAvailableHistoryCell;
#[derive(Debug, Clone)]
pub struct AppExitInfo {
pub token_usage: TokenUsage,
pub conversation_id: Option<ConversationId>,
pub conversation_id: Option<ThreadId>,
pub update_action: Option<UpdateAction>,
/// ANSI-styled transcript lines to print after the TUI exits.
///
@@ -105,7 +105,7 @@ impl From<AppExitInfo> for codex_tui::AppExitInfo {
fn from(info: AppExitInfo) -> Self {
codex_tui::AppExitInfo {
token_usage: info.token_usage,
conversation_id: info.conversation_id,
thread_id: info.conversation_id,
update_action: info.update_action.map(Into::into),
}
}
@@ -113,7 +113,7 @@ impl From<AppExitInfo> for codex_tui::AppExitInfo {
fn session_summary(
token_usage: TokenUsage,
conversation_id: Option<ConversationId>,
conversation_id: Option<ThreadId>,
) -> Option<SessionSummary> {
if token_usage.is_zero() {
return None;
@@ -320,7 +320,7 @@ async fn handle_model_migration_prompt_if_needed(
}
pub(crate) struct App {
pub(crate) server: Arc<ConversationManager>,
pub(crate) server: Arc<ThreadManager>,
pub(crate) app_event_tx: AppEventSender,
pub(crate) chat_widget: ChatWidget,
pub(crate) auth_manager: Arc<AuthManager>,
@@ -387,7 +387,7 @@ impl App {
if let Some(conversation_id) = self.chat_widget.conversation_id() {
self.suppress_shutdown_complete = true;
self.chat_widget.submit_op(Op::Shutdown);
self.server.remove_conversation(&conversation_id).await;
self.server.remove_thread(&conversation_id).await;
}
}
@@ -407,11 +407,8 @@ impl App {
let (app_event_tx, mut app_event_rx) = unbounded_channel();
let app_event_tx = AppEventSender::new(app_event_tx);
let conversation_manager = Arc::new(ConversationManager::new(
auth_manager.clone(),
SessionSource::Cli,
));
let mut model = conversation_manager
let thread_manager = Arc::new(ThreadManager::new(auth_manager.clone(), SessionSource::Cli));
let mut model = thread_manager
.get_models_manager()
.get_model(&config.model, &config)
.await;
@@ -420,7 +417,7 @@ impl App {
&mut config,
model.as_str(),
&app_event_tx,
conversation_manager.get_models_manager(),
thread_manager.get_models_manager(),
)
.await;
if let Some(exit_info) = exit_info {
@@ -441,20 +438,16 @@ impl App {
initial_images: initial_images.clone(),
enhanced_keys_supported,
auth_manager: auth_manager.clone(),
models_manager: conversation_manager.get_models_manager(),
models_manager: thread_manager.get_models_manager(),
feedback: feedback.clone(),
is_first_run,
model: model.clone(),
};
ChatWidget::new(init, conversation_manager.clone())
ChatWidget::new(init, thread_manager.clone())
}
ResumeSelection::Resume(path) => {
let resumed = conversation_manager
.resume_conversation_from_rollout(
config.clone(),
path.clone(),
auth_manager.clone(),
)
let resumed = thread_manager
.resume_thread_from_rollout(config.clone(), path.clone(), auth_manager.clone())
.await
.wrap_err_with(|| {
format!("Failed to resume session from {}", path.display())
@@ -467,16 +460,12 @@ impl App {
initial_images: initial_images.clone(),
enhanced_keys_supported,
auth_manager: auth_manager.clone(),
models_manager: conversation_manager.get_models_manager(),
models_manager: thread_manager.get_models_manager(),
feedback: feedback.clone(),
is_first_run,
model: model.clone(),
};
ChatWidget::new_from_existing(
init,
resumed.conversation,
resumed.session_configured,
)
ChatWidget::new_from_existing(init, resumed.thread, resumed.session_configured)
}
};
@@ -503,7 +492,7 @@ impl App {
let copy_selection_shortcut = crate::transcript_copy_ui::detect_copy_selection_shortcut();
let mut app = Self {
server: conversation_manager.clone(),
server: thread_manager.clone(),
app_event_tx,
chat_widget,
auth_manager: auth_manager.clone(),
@@ -1400,7 +1389,7 @@ impl App {
);
match self
.server
.resume_conversation_from_rollout(
.resume_thread_from_rollout(
self.config.clone(),
path.clone(),
self.auth_manager.clone(),
@@ -1424,7 +1413,7 @@ impl App {
};
self.chat_widget = ChatWidget::new_from_existing(
init,
resumed.conversation,
resumed.thread,
resumed.session_configured,
);
if let Some(summary) = summary {
@@ -2087,14 +2076,14 @@ mod tests {
use crate::tui::scrolling::TranscriptLineMeta;
use codex_core::AuthManager;
use codex_core::CodexAuth;
use codex_core::ConversationManager;
use codex_core::ThreadManager;
use codex_core::config::ConfigBuilder;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::Event;
use codex_core::protocol::EventMsg;
use codex_core::protocol::SandboxPolicy;
use codex_core::protocol::SessionConfiguredEvent;
use codex_protocol::ConversationId;
use codex_protocol::ThreadId;
use insta::assert_snapshot;
use pretty_assertions::assert_eq;
use ratatui::prelude::Line;
@@ -2107,7 +2096,7 @@ mod tests {
let (chat_widget, app_event_tx, _rx, _op_rx) = make_chatwidget_manual_with_sender().await;
let config = chat_widget.config_ref().clone();
let current_model = "gpt-5.2-codex".to_string();
let server = Arc::new(ConversationManager::with_models_provider(
let server = Arc::new(ThreadManager::with_models_provider(
CodexAuth::from_api_key("Test API Key"),
config.model_provider.clone(),
));
@@ -2159,7 +2148,7 @@ mod tests {
let (chat_widget, app_event_tx, rx, op_rx) = make_chatwidget_manual_with_sender().await;
let config = chat_widget.config_ref().clone();
let current_model = "gpt-5.2-codex".to_string();
let server = Arc::new(ConversationManager::with_models_provider(
let server = Arc::new(ThreadManager::with_models_provider(
CodexAuth::from_api_key("Test API Key"),
config.model_provider.clone(),
));
@@ -2404,7 +2393,7 @@ mod tests {
let make_header = |is_first| {
let event = SessionConfiguredEvent {
session_id: ConversationId::new(),
session_id: ThreadId::new(),
model: "gpt-test".to_string(),
model_provider_id: "test-provider".to_string(),
approval_policy: AskForApproval::Never,
@@ -2442,7 +2431,7 @@ mod tests {
assert_eq!(user_count(&app.transcript_cells), 2);
app.backtrack.base_id = Some(ConversationId::new());
app.backtrack.base_id = Some(ThreadId::new());
app.backtrack.primed = true;
app.backtrack.nth_user_message = user_count(&app.transcript_cells).saturating_sub(1);
@@ -2697,7 +2686,7 @@ mod tests {
async fn new_session_requests_shutdown_for_previous_conversation() {
let (mut app, mut app_event_rx, mut op_rx) = make_test_app_with_channels().await;
let conversation_id = ConversationId::new();
let conversation_id = ThreadId::new();
let event = SessionConfiguredEvent {
session_id: conversation_id,
model: "gpt-test".to_string(),
@@ -2763,8 +2752,7 @@ mod tests {
total_tokens: 12,
..Default::default()
};
let conversation =
ConversationId::from_string("123e4567-e89b-12d3-a456-426614174000").unwrap();
let conversation = ThreadId::from_string("123e4567-e89b-12d3-a456-426614174000").unwrap();
let summary = session_summary(usage, Some(conversation)).expect("summary");
assert_eq!(
+13 -15
View File
@@ -9,7 +9,7 @@ use crate::pager_overlay::Overlay;
use crate::tui;
use crate::tui::TuiEvent;
use codex_core::protocol::ConversationPathResponseEvent;
use codex_protocol::ConversationId;
use codex_protocol::ThreadId;
use color_eyre::eyre::Result;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
@@ -21,13 +21,13 @@ pub(crate) struct BacktrackState {
/// True when Esc has primed backtrack mode in the main view.
pub(crate) primed: bool,
/// Session id of the base conversation to fork from.
pub(crate) base_id: Option<ConversationId>,
pub(crate) base_id: Option<ThreadId>,
/// Index in the transcript of the last user message.
pub(crate) nth_user_message: usize,
/// True when the transcript overlay is showing a backtrack preview.
pub(crate) overlay_preview_active: bool,
/// Pending fork request: (base_id, nth_user_message, prefill).
pub(crate) pending: Option<(ConversationId, usize, String)>,
pub(crate) pending: Option<(ThreadId, usize, String)>,
}
impl App {
@@ -99,7 +99,7 @@ impl App {
pub(crate) fn request_backtrack(
&mut self,
prefill: String,
base_id: ConversationId,
base_id: ThreadId,
nth_user_message: usize,
) {
self.backtrack.pending = Some((base_id, nth_user_message, prefill));
@@ -308,7 +308,7 @@ impl App {
}
/// Handle a ConversationHistory response while a backtrack is pending.
/// If it matches the primed base session, fork and switch to the new conversation.
/// If it matches the primed base session, fork and switch to the new thread.
pub(crate) async fn on_conversation_history_for_backtrack(
&mut self,
tui: &mut tui::Tui,
@@ -324,7 +324,7 @@ impl App {
Ok(())
}
/// Fork the conversation using provided history and switch UI/state accordingly.
/// Fork the thread using provided history and switch UI/state accordingly.
async fn fork_and_switch_to_new_conversation(
&mut self,
tui: &mut tui::Tui,
@@ -345,28 +345,26 @@ impl App {
}
}
/// Thin wrapper around ConversationManager::fork_conversation.
/// Thin wrapper around ThreadManager::fork_thread.
async fn perform_fork(
&self,
path: PathBuf,
nth_user_message: usize,
cfg: codex_core::config::Config,
) -> codex_core::error::Result<codex_core::NewConversation> {
self.server
.fork_conversation(nth_user_message, cfg, path)
.await
) -> codex_core::error::Result<codex_core::NewThread> {
self.server.fork_thread(nth_user_message, cfg, path).await
}
/// Install a forked conversation into the ChatWidget and update UI to reflect selection.
/// Install a forked thread into the ChatWidget and update UI to reflect selection.
fn install_forked_conversation(
&mut self,
tui: &mut tui::Tui,
cfg: codex_core::config::Config,
new_conv: codex_core::NewConversation,
new_conv: codex_core::NewThread,
nth_user_message: usize,
prefill: &str,
) {
let conv = new_conv.conversation;
let thread = new_conv.thread;
let session_configured = new_conv.session_configured;
let init = crate::chatwidget::ChatWidgetInit {
config: cfg,
@@ -382,7 +380,7 @@ impl App {
is_first_run: false,
};
self.chat_widget =
crate::chatwidget::ChatWidget::new_from_existing(init, conv, session_configured);
crate::chatwidget::ChatWidget::new_from_existing(init, thread, session_configured);
// Trim transcript up to the selected user message and re-render it.
self.trim_transcript_for_backtrack(nth_user_message);
self.render_transcript_once(tui);
+7 -10
View File
@@ -61,7 +61,7 @@ use codex_core::protocol::WarningEvent;
use codex_core::protocol::WebSearchBeginEvent;
use codex_core::protocol::WebSearchEndEvent;
use codex_core::skills::model::SkillMetadata;
use codex_protocol::ConversationId;
use codex_protocol::ThreadId;
use codex_protocol::account::PlanType;
use codex_protocol::approvals::ElicitationRequestEvent;
use codex_protocol::parse_command::ParsedCommand;
@@ -131,7 +131,7 @@ use codex_common::approval_presets::ApprovalPreset;
use codex_common::approval_presets::builtin_approval_presets;
use codex_core::AuthManager;
use codex_core::CodexAuth;
use codex_core::ConversationManager;
use codex_core::ThreadManager;
use codex_core::protocol::AskForApproval;
use codex_core::protocol::SandboxPolicy;
use codex_file_search::FileMatch;
@@ -311,7 +311,7 @@ pub(crate) struct ChatWidget {
current_status_header: String,
// Previous status header to restore after a transient stream retry.
retry_status_header: Option<String>,
conversation_id: Option<ConversationId>,
conversation_id: Option<ThreadId>,
frame_requester: FrameRequester,
// Whether to include the initial welcome banner on session configured
show_welcome_banner: bool,
@@ -1264,10 +1264,7 @@ impl ChatWidget {
}
}
pub(crate) fn new(
common: ChatWidgetInit,
conversation_manager: Arc<ConversationManager>,
) -> Self {
pub(crate) fn new(common: ChatWidgetInit, thread_manager: Arc<ThreadManager>) -> Self {
let ChatWidgetInit {
config,
frame_requester,
@@ -1285,7 +1282,7 @@ impl ChatWidget {
config.model = Some(model.clone());
let mut rng = rand::rng();
let placeholder = EXAMPLE_PROMPTS[rng.random_range(0..EXAMPLE_PROMPTS.len())].to_string();
let codex_op_tx = spawn_agent(config.clone(), app_event_tx.clone(), conversation_manager);
let codex_op_tx = spawn_agent(config.clone(), app_event_tx.clone(), thread_manager);
let mut widget = Self {
app_event_tx: app_event_tx.clone(),
@@ -1349,7 +1346,7 @@ impl ChatWidget {
/// Create a ChatWidget attached to an existing conversation (e.g., a fork).
pub(crate) fn new_from_existing(
common: ChatWidgetInit,
conversation: std::sync::Arc<codex_core::CodexConversation>,
conversation: std::sync::Arc<codex_core::CodexThread>,
session_configured: codex_core::protocol::SessionConfiguredEvent,
) -> Self {
let ChatWidgetInit {
@@ -3366,7 +3363,7 @@ impl ChatWidget {
.unwrap_or_default()
}
pub(crate) fn conversation_id(&self) -> Option<ConversationId> {
pub(crate) fn conversation_id(&self) -> Option<ThreadId> {
self.conversation_id
}
+16 -16
View File
@@ -1,8 +1,8 @@
use std::sync::Arc;
use codex_core::CodexConversation;
use codex_core::ConversationManager;
use codex_core::NewConversation;
use codex_core::CodexThread;
use codex_core::NewThread;
use codex_core::ThreadManager;
use codex_core::config::Config;
use codex_core::protocol::Event;
use codex_core::protocol::EventMsg;
@@ -18,17 +18,17 @@ use crate::app_event_sender::AppEventSender;
pub(crate) fn spawn_agent(
config: Config,
app_event_tx: AppEventSender,
server: Arc<ConversationManager>,
server: Arc<ThreadManager>,
) -> UnboundedSender<Op> {
let (codex_op_tx, mut codex_op_rx) = unbounded_channel::<Op>();
let app_event_tx_clone = app_event_tx;
tokio::spawn(async move {
let NewConversation {
conversation_id: _,
conversation,
let NewThread {
thread,
session_configured,
} = match server.new_conversation(config).await {
thread_id: _,
} = match server.start_thread(config).await {
Ok(v) => v,
#[allow(clippy::print_stderr)]
Err(err) => {
@@ -52,17 +52,17 @@ pub(crate) fn spawn_agent(
};
app_event_tx_clone.send(AppEvent::CodexEvent(ev));
let conversation_clone = conversation.clone();
let thread_clone = thread.clone();
tokio::spawn(async move {
while let Some(op) = codex_op_rx.recv().await {
let id = conversation_clone.submit(op).await;
let id = thread_clone.submit(op).await;
if let Err(e) = id {
tracing::error!("failed to submit op: {e}");
}
}
});
while let Ok(event) = conversation.next_event().await {
while let Ok(event) = thread.next_event().await {
app_event_tx_clone.send(AppEvent::CodexEvent(event));
}
});
@@ -70,11 +70,11 @@ pub(crate) fn spawn_agent(
codex_op_tx
}
/// Spawn agent loops for an existing conversation (e.g., a forked conversation).
/// Spawn agent loops for an existing thread (e.g., a forked thread).
/// Sends the provided `SessionConfiguredEvent` immediately, then forwards subsequent
/// events and accepts Ops for submission.
pub(crate) fn spawn_agent_from_existing(
conversation: std::sync::Arc<CodexConversation>,
thread: std::sync::Arc<CodexThread>,
session_configured: codex_core::protocol::SessionConfiguredEvent,
app_event_tx: AppEventSender,
) -> UnboundedSender<Op> {
@@ -89,17 +89,17 @@ pub(crate) fn spawn_agent_from_existing(
};
app_event_tx_clone.send(AppEvent::CodexEvent(ev));
let conversation_clone = conversation.clone();
let thread_clone = thread.clone();
tokio::spawn(async move {
while let Some(op) = codex_op_rx.recv().await {
let id = conversation_clone.submit(op).await;
let id = thread_clone.submit(op).await;
if let Err(e) = id {
tracing::error!("failed to submit op: {e}");
}
}
});
while let Ok(event) = conversation.next_event().await {
while let Ok(event) = thread.next_event().await {
app_event_tx_clone.send(AppEvent::CodexEvent(event));
}
});
+5 -5
View File
@@ -45,7 +45,7 @@ use codex_core::protocol::UndoCompletedEvent;
use codex_core::protocol::UndoStartedEvent;
use codex_core::protocol::ViewImageToolCallEvent;
use codex_core::protocol::WarningEvent;
use codex_protocol::ConversationId;
use codex_protocol::ThreadId;
use codex_protocol::account::PlanType;
use codex_protocol::openai_models::ModelPreset;
use codex_protocol::openai_models::ReasoningEffortPreset;
@@ -99,7 +99,7 @@ fn snapshot(percent: f64) -> RateLimitSnapshot {
async fn resumed_initial_messages_render_history() {
let (mut chat, mut rx, _ops) = make_chatwidget_manual(None).await;
let conversation_id = ConversationId::new();
let conversation_id = ThreadId::new();
let rollout_file = NamedTempFile::new().unwrap();
let configured = codex_core::protocol::SessionConfiguredEvent {
session_id: conversation_id,
@@ -311,7 +311,7 @@ async fn helpers_are_available_and_do_not_panic() {
let tx = AppEventSender::new(tx_raw);
let cfg = test_config().await;
let resolved_model = ModelsManager::get_model_offline(cfg.model.as_deref());
let conversation_manager = Arc::new(ConversationManager::with_models_provider(
let thread_manager = Arc::new(ThreadManager::with_models_provider(
CodexAuth::from_api_key("test"),
cfg.model_provider.clone(),
));
@@ -324,12 +324,12 @@ async fn helpers_are_available_and_do_not_panic() {
initial_images: Vec::new(),
enhanced_keys_supported: false,
auth_manager,
models_manager: conversation_manager.get_models_manager(),
models_manager: thread_manager.get_models_manager(),
feedback: codex_feedback::CodexFeedback::new(),
is_first_run: true,
model: resolved_model,
};
let mut w = ChatWidget::new(init, conversation_manager);
let mut w = ChatWidget::new(init, thread_manager);
// Basic construction sanity.
let _ = &mut w;
}
+3 -3
View File
@@ -19,7 +19,7 @@ use codex_core::config::ConfigOverrides;
use codex_core::config::find_codex_home;
use codex_core::config::load_config_as_toml_with_cli_overrides;
use codex_core::config::resolve_oss_provider;
use codex_core::find_conversation_path_by_id_str;
use codex_core::find_thread_path_by_id_str;
use codex_core::get_platform_sandbox;
use codex_core::protocol::AskForApproval;
use codex_protocol::config_types::SandboxMode;
@@ -450,7 +450,7 @@ async fn run_ratatui_app(
// Determine resume behavior: explicit id, then resume last, then picker.
let resume_selection = if let Some(id_str) = cli.resume_session_id.as_deref() {
match find_conversation_path_by_id_str(&config.codex_home, id_str).await? {
match find_thread_path_by_id_str(&config.codex_home, id_str).await? {
Some(path) => resume_picker::ResumeSelection::Resume(path),
None => {
error!("Error finding conversation path: {id_str}");
@@ -473,7 +473,7 @@ async fn run_ratatui_app(
}
} else if cli.resume_last {
let provider_filter = vec![config.model_provider_id.clone()];
match RolloutRecorder::list_conversations(
match RolloutRecorder::list_threads(
&config.codex_home,
1,
None,
+16 -16
View File
@@ -5,11 +5,11 @@ use std::sync::Arc;
use chrono::DateTime;
use chrono::Utc;
use codex_core::ConversationItem;
use codex_core::ConversationsPage;
use codex_core::Cursor;
use codex_core::INTERACTIVE_SESSION_SOURCES;
use codex_core::RolloutRecorder;
use codex_core::ThreadItem;
use codex_core::ThreadsPage;
use codex_core::path_utils;
use codex_protocol::items::TurnItem;
use color_eyre::eyre::Result;
@@ -61,7 +61,7 @@ enum BackgroundEvent {
PageLoaded {
request_token: usize,
search_token: Option<usize>,
page: std::io::Result<ConversationsPage>,
page: std::io::Result<ThreadsPage>,
},
}
@@ -89,7 +89,7 @@ pub async fn run_resume_picker(
let tx = loader_tx.clone();
tokio::spawn(async move {
let provider_filter = vec![request.default_provider.clone()];
let page = RolloutRecorder::list_conversations(
let page = RolloutRecorder::list_threads(
&request.codex_home,
PAGE_SIZE,
request.cursor.as_ref(),
@@ -415,7 +415,7 @@ impl PickerState {
self.pagination.loading = LoadingState::Idle;
}
fn ingest_page(&mut self, page: ConversationsPage) {
fn ingest_page(&mut self, page: ThreadsPage) {
if let Some(cursor) = page.next_cursor.clone() {
self.pagination.next_cursor = Some(cursor);
} else {
@@ -627,11 +627,11 @@ impl PickerState {
}
}
fn rows_from_items(items: Vec<ConversationItem>) -> Vec<Row> {
fn rows_from_items(items: Vec<ThreadItem>) -> Vec<Row> {
items.into_iter().map(|item| head_to_row(&item)).collect()
}
fn head_to_row(item: &ConversationItem) -> Row {
fn head_to_row(item: &ThreadItem) -> Row {
let created_at = item
.created_at
.as_deref()
@@ -1077,8 +1077,8 @@ mod tests {
]
}
fn make_item(path: &str, ts: &str, preview: &str) -> ConversationItem {
ConversationItem {
fn make_item(path: &str, ts: &str, preview: &str) -> ThreadItem {
ThreadItem {
path: PathBuf::from(path),
head: head_with_ts_and_user_text(ts, &[preview]),
created_at: Some(ts.to_string()),
@@ -1092,12 +1092,12 @@ mod tests {
}
fn page(
items: Vec<ConversationItem>,
items: Vec<ThreadItem>,
next_cursor: Option<Cursor>,
num_scanned_files: usize,
reached_scan_cap: bool,
) -> ConversationsPage {
ConversationsPage {
) -> ThreadsPage {
ThreadsPage {
items,
next_cursor,
num_scanned_files,
@@ -1144,13 +1144,13 @@ mod tests {
#[test]
fn rows_from_items_preserves_backend_order() {
// Construct two items with different timestamps and real user text.
let a = ConversationItem {
let a = ThreadItem {
path: PathBuf::from("/tmp/a.jsonl"),
head: head_with_ts_and_user_text("2025-01-01T00:00:00Z", &["A"]),
created_at: Some("2025-01-01T00:00:00Z".into()),
updated_at: Some("2025-01-01T00:00:00Z".into()),
};
let b = ConversationItem {
let b = ThreadItem {
path: PathBuf::from("/tmp/b.jsonl"),
head: head_with_ts_and_user_text("2025-01-02T00:00:00Z", &["B"]),
created_at: Some("2025-01-02T00:00:00Z".into()),
@@ -1166,7 +1166,7 @@ mod tests {
#[test]
fn row_uses_tail_timestamp_for_updated_at() {
let head = head_with_ts_and_user_text("2025-01-01T00:00:00Z", &["Hello"]);
let item = ConversationItem {
let item = ThreadItem {
path: PathBuf::from("/tmp/a.jsonl"),
head,
created_at: Some("2025-01-01T00:00:00Z".into()),
@@ -1351,7 +1351,7 @@ mod tests {
None,
);
let page = RolloutRecorder::list_conversations(
let page = RolloutRecorder::list_threads(
&state.codex_home,
PAGE_SIZE,
None,
+3 -3
View File
@@ -11,7 +11,7 @@ use codex_core::protocol::NetworkAccess;
use codex_core::protocol::SandboxPolicy;
use codex_core::protocol::TokenUsage;
use codex_core::protocol::TokenUsageInfo;
use codex_protocol::ConversationId;
use codex_protocol::ThreadId;
use codex_protocol::account::PlanType;
use ratatui::prelude::*;
use ratatui::style::Stylize;
@@ -74,7 +74,7 @@ pub(crate) fn new_status_output(
auth_manager: &AuthManager,
token_info: Option<&TokenUsageInfo>,
total_usage: &TokenUsage,
session_id: &Option<ConversationId>,
session_id: &Option<ThreadId>,
rate_limits: Option<&RateLimitSnapshotDisplay>,
plan_type: Option<PlanType>,
now: DateTime<Local>,
@@ -103,7 +103,7 @@ impl StatusHistoryCell {
auth_manager: &AuthManager,
token_info: Option<&TokenUsageInfo>,
total_usage: &TokenUsage,
session_id: &Option<ConversationId>,
session_id: &Option<ThreadId>,
rate_limits: Option<&RateLimitSnapshotDisplay>,
plan_type: Option<PlanType>,
now: DateTime<Local>,