Skip to content

Commit 5731998

Browse files
charley-oaicodex
andcommitted
Fix BTW restack drift on current main
Resolve the remaining API and test drift after restacking the /btw implementation onto current main. - align tui /btw with ForkSnapshot-based forking - adapt tui_app_server BTW attachment to the current session/turn cache model - make BTW fork banners deterministic even for unnamed parent threads - clean up the stale thread_manager test import from the cherry-pick Co-authored-by: Codex <noreply@openai.com>
1 parent 28680fc commit 5731998

File tree

7 files changed

+72
-102
lines changed

7 files changed

+72
-102
lines changed

codex-rs/core/src/thread_manager_tests.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use super::*;
2-
use crate::EventPersistenceMode;
32
use crate::codex::make_session_and_context;
43
use crate::config::test_config;
54
use crate::models_manager::collaboration_mode_presets::CollaborationModesConfig;

codex-rs/tui/src/app.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,7 +1803,7 @@ impl App {
18031803
thread_id,
18041804
config_snapshot.session_source.get_nickname(),
18051805
config_snapshot.session_source.get_agent_role(),
1806-
false,
1806+
/*is_closed*/ false,
18071807
);
18081808
}
18091809
}
@@ -2214,8 +2214,7 @@ impl App {
22142214
let next_btw_fork_banner_parent_label =
22152215
self.take_next_btw_fork_banner_parent_label(thread_id);
22162216
let mut chat_widget = ChatWidget::new_with_op_sender(init, codex_op_tx);
2217-
chat_widget
2218-
.set_next_fork_banner_parent_label(next_btw_fork_banner_parent_label);
2217+
chat_widget.set_next_fork_banner_parent_label(next_btw_fork_banner_parent_label);
22192218
self.replace_chat_widget(chat_widget);
22202219

22212220
self.reset_for_thread_switch(tui)?;

codex-rs/tui/src/app/btw.rs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,22 +138,35 @@ impl App {
138138
self.sync_active_agent_label();
139139
}
140140

141-
async fn fork_banner_parent_label(&self, parent_thread_id: ThreadId) -> Option<String> {
142-
if self.chat_widget.thread_id() == Some(parent_thread_id) {
143-
return self
141+
async fn fork_banner_parent_label(&self, parent_thread_id: ThreadId) -> String {
142+
if self.chat_widget.thread_id() == Some(parent_thread_id)
143+
&& let Some(thread_name) = self
144144
.chat_widget
145145
.thread_name()
146-
.filter(|name| !name.trim().is_empty());
146+
.filter(|name| !name.trim().is_empty())
147+
{
148+
return thread_name;
149+
}
150+
151+
if let Some(channel) = self.thread_event_channels.get(&parent_thread_id) {
152+
let store = channel.store.lock().await;
153+
if let Some(thread_name) =
154+
match store.session_configured.as_ref().map(|event| &event.msg) {
155+
Some(EventMsg::SessionConfigured(session)) => session
156+
.thread_name
157+
.clone()
158+
.filter(|name| !name.trim().is_empty()),
159+
_ => None,
160+
}
161+
{
162+
return thread_name;
163+
}
147164
}
148165

149-
let channel = self.thread_event_channels.get(&parent_thread_id)?;
150-
let store = channel.store.lock().await;
151-
match store.session_configured.as_ref().map(|event| &event.msg) {
152-
Some(EventMsg::SessionConfigured(session)) => session
153-
.thread_name
154-
.clone()
155-
.filter(|name| !name.trim().is_empty()),
156-
_ => None,
166+
if self.primary_thread_id == Some(parent_thread_id) {
167+
"main thread".to_string()
168+
} else {
169+
self.thread_label(parent_thread_id)
157170
}
158171
}
159172

@@ -193,8 +206,8 @@ impl App {
193206

194207
let fork_result = if self.chat_widget.agent_turn_running() {
195208
self.server
196-
.fork_thread_from_interrupted_snapshot(
197-
/*nth_user_message*/ usize::MAX,
209+
.fork_thread(
210+
ForkSnapshot::Interrupted,
198211
self.config.clone(),
199212
parent_rollout_path.clone(),
200213
/*persist_extended_history*/ false,
@@ -204,7 +217,7 @@ impl App {
204217
} else {
205218
self.server
206219
.fork_thread(
207-
/*nth_user_message*/ usize::MAX,
220+
/*snapshot*/ usize::MAX,
208221
self.config.clone(),
209222
parent_rollout_path.clone(),
210223
/*persist_extended_history*/ false,
@@ -217,7 +230,7 @@ impl App {
217230
Ok(forked) => {
218231
let child_thread_id = forked.thread_id;
219232
let next_fork_banner_parent_label =
220-
self.fork_banner_parent_label(parent_thread_id).await;
233+
Some(self.fork_banner_parent_label(parent_thread_id).await);
221234
self.attach_live_thread(
222235
child_thread_id,
223236
Arc::clone(&forked.thread),

codex-rs/tui/src/app/tests/btw.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ async fn setup_btw_parent_thread(app: &mut App, parent_message: Option<&str>) ->
414414
source: SessionSource::Cli,
415415
agent_nickname: None,
416416
agent_role: None,
417+
agent_path: None,
417418
model_provider: Some(app.config.model_provider_id.clone()),
418419
base_instructions: None,
419420
dynamic_tools: None,

codex-rs/tui_app_server/src/app/btw.rs

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -141,22 +141,32 @@ impl App {
141141
self.sync_active_agent_label();
142142
}
143143

144-
async fn fork_banner_parent_label(&self, parent_thread_id: ThreadId) -> Option<String> {
145-
if self.chat_widget.thread_id() == Some(parent_thread_id) {
146-
return self
144+
async fn fork_banner_parent_label(&self, parent_thread_id: ThreadId) -> String {
145+
if self.chat_widget.thread_id() == Some(parent_thread_id)
146+
&& let Some(thread_name) = self
147147
.chat_widget
148148
.thread_name()
149-
.filter(|name| !name.trim().is_empty());
149+
.filter(|name| !name.trim().is_empty())
150+
{
151+
return thread_name;
152+
}
153+
154+
if let Some(channel) = self.thread_event_channels.get(&parent_thread_id) {
155+
let store = channel.store.lock().await;
156+
if let Some(thread_name) = store
157+
.session
158+
.as_ref()
159+
.and_then(|session| session.thread_name.clone())
160+
.filter(|name| !name.trim().is_empty())
161+
{
162+
return thread_name;
163+
}
150164
}
151165

152-
let channel = self.thread_event_channels.get(&parent_thread_id)?;
153-
let store = channel.store.lock().await;
154-
match store.session_configured.as_ref().map(|event| &event.msg) {
155-
Some(EventMsg::SessionConfigured(session)) => session
156-
.thread_name
157-
.clone()
158-
.filter(|name| !name.trim().is_empty()),
159-
_ => None,
166+
if self.primary_thread_id == Some(parent_thread_id) {
167+
"main thread".to_string()
168+
} else {
169+
self.thread_label(parent_thread_id)
160170
}
161171
}
162172

@@ -221,17 +231,15 @@ impl App {
221231

222232
match fork_result {
223233
Ok(forked) => {
224-
let child_thread_id = forked.session_configured.session_id;
234+
let AppServerStartedThread { session, turns } = forked;
235+
let child_thread_id = session.thread_id;
225236
let next_fork_banner_parent_label =
226-
self.fork_banner_parent_label(parent_thread_id).await;
227-
self.enqueue_thread_event(
228-
child_thread_id,
229-
Event {
230-
id: String::new(),
231-
msg: EventMsg::SessionConfigured(forked.session_configured),
232-
},
233-
)
234-
.await?;
237+
Some(self.fork_banner_parent_label(parent_thread_id).await);
238+
let channel = self.ensure_thread_channel(child_thread_id);
239+
{
240+
let mut store = channel.store.lock().await;
241+
store.set_session(session, turns);
242+
}
235243
self.btw_threads.insert(
236244
child_thread_id,
237245
BtwThreadState {

codex-rs/tui_app_server/src/app_server_session.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ impl AppServerSession {
332332
) -> Result<AppServerStartedThread> {
333333
let request_id = self.next_request_id();
334334
let mut params =
335-
thread_fork_params_from_config(config, thread_id, self.thread_params_mode());
335+
thread_fork_params_from_config(&config, thread_id, self.thread_params_mode());
336336
params.path = path;
337337
let response: ThreadForkResponse = self
338338
.client
@@ -856,19 +856,19 @@ fn thread_resume_params_from_config(
856856
}
857857

858858
fn thread_fork_params_from_config(
859-
config: Config,
859+
config: &Config,
860860
thread_id: ThreadId,
861861
thread_params_mode: ThreadParamsMode,
862862
) -> ThreadForkParams {
863863
ThreadForkParams {
864864
thread_id: thread_id.to_string(),
865865
model: config.model.clone(),
866-
model_provider: thread_params_mode.model_provider_from_config(&config),
867-
cwd: thread_cwd_from_config(&config, thread_params_mode),
866+
model_provider: thread_params_mode.model_provider_from_config(config),
867+
cwd: thread_cwd_from_config(config, thread_params_mode),
868868
approval_policy: Some(config.permissions.approval_policy.value().into()),
869-
approvals_reviewer: approvals_reviewer_override_from_config(&config),
869+
approvals_reviewer: approvals_reviewer_override_from_config(config),
870870
sandbox: sandbox_mode_from_policy(config.permissions.sandbox_policy.get().clone()),
871-
config: config_request_overrides_from_config(&config),
871+
config: config_request_overrides_from_config(config),
872872
ephemeral: config.ephemeral,
873873
persist_extended_history: true,
874874
..ThreadForkParams::default()
@@ -1131,7 +1131,7 @@ mod tests {
11311131
let start = thread_start_params_from_config(&config, ThreadParamsMode::Remote);
11321132
let resume =
11331133
thread_resume_params_from_config(config.clone(), thread_id, ThreadParamsMode::Remote);
1134-
let fork = thread_fork_params_from_config(config, thread_id, ThreadParamsMode::Remote);
1134+
let fork = thread_fork_params_from_config(&config, thread_id, ThreadParamsMode::Remote);
11351135

11361136
assert_eq!(start.cwd, None);
11371137
assert_eq!(resume.cwd, None);

codex-rs/tui_app_server/src/chatwidget.rs

Lines changed: 3 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,57 +1939,7 @@ impl ChatWidget {
19391939
PlainHistoryCell::new(vec![line]),
19401940
)));
19411941
}
1942-
1943-
1944-
1945-
1946-
1947-
1948-
1949-
1950-
1951-
1952-
1953-
1954-
1955-
1956-
1957-
1958-
1959-
1960-
1961-
1962-
1963-
1964-
1965-
1966-
1967-
1968-
1969-
1970-
1971-
1972-
1973-
1974-
1975-
1976-
1977-
1978-
1979-
1980-
1981-
1982-
1983-
1984-
1985-
1986-
1987-
1988-
1989-
1990-
1991-
1992-
1942+
19931943
fn on_thread_name_updated(&mut self, event: codex_protocol::protocol::ThreadNameUpdatedEvent) {
19941944
if self.thread_id == Some(event.thread_id) {
19951945
self.thread_name = event.thread_name;
@@ -5395,7 +5345,7 @@ impl ChatWidget {
53955345
return None;
53965346
}
53975347
self.submit_op(AppCommand::run_user_shell_command(cmd.to_string()));
5398-
return;
5348+
return None;
53995349
}
54005350

54015351
for image_url in &remote_image_urls {
@@ -5528,7 +5478,7 @@ impl ChatWidget {
55285478
self.add_error_message(
55295479
"Thread model is unavailable. Wait for the thread to finish syncing or choose a model before sending input.".to_string(),
55305480
);
5531-
return;
5481+
return None;
55325482
}
55335483
let collaboration_mode = if self.collaboration_modes_enabled() {
55345484
self.active_collaboration_mask

0 commit comments

Comments
 (0)