Skip to content

Commit 71f82bb

Browse files
committed
feat: reapply fork behavior on rust-v0.145.0
1 parent b1ec315 commit 71f82bb

41 files changed

Lines changed: 1410 additions & 167 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

codex-rs/Cargo.lock

Lines changed: 130 additions & 130 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

codex-rs/app-server-protocol/src/protocol/v2/account.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,13 @@ pub struct GetAccountParams {
489489
/// themselves and call `account/login/start` with `chatgptAuthTokens`.
490490
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
491491
pub refresh_token: bool,
492+
493+
/// When `true`, reloads the auth snapshot from storage before returning.
494+
///
495+
/// This keeps long-lived clients in sync with `auth.json` updates without
496+
/// requiring a full app-server restart.
497+
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
498+
pub reload_auth_from_storage: bool,
492499
}
493500

494501
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
@@ -497,6 +504,8 @@ pub struct GetAccountParams {
497504
pub struct GetAccountResponse {
498505
pub account: Option<Account>,
499506
pub requires_openai_auth: bool,
507+
/// Whether this request reloaded a different auth snapshot from storage.
508+
pub auth_changed: bool,
500509
}
501510

502511
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]

codex-rs/app-server/src/message_processor.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ impl MessageProcessor {
311311
let pending_thread_unloads = Arc::new(Mutex::new(HashSet::new()));
312312
let thread_watch_manager =
313313
crate::thread_status::ThreadWatchManager::new_with_outgoing(outgoing.clone());
314+
let auth_transition_lock = Arc::new(Mutex::new(()));
314315
let thread_list_state_permit = Arc::new(Semaphore::new(/*permits*/ 1));
315316
let workspace_settings_cache =
316317
Arc::new(workspace_settings::WorkspaceSettingsCache::default());
@@ -336,6 +337,8 @@ impl MessageProcessor {
336337
outgoing.clone(),
337338
Arc::clone(&config),
338339
config_manager.clone(),
340+
thread_watch_manager.clone(),
341+
Arc::clone(&auth_transition_lock),
339342
);
340343
let apps_processor = AppsRequestProcessor::new(
341344
auth_manager.clone(),
@@ -422,6 +425,7 @@ impl MessageProcessor {
422425
Arc::clone(&pending_thread_unloads),
423426
thread_state_manager.clone(),
424427
thread_watch_manager.clone(),
428+
Arc::clone(&auth_transition_lock),
425429
Arc::clone(&thread_list_state_permit),
426430
thread_goal_processor.clone(),
427431
state_db.clone(),
@@ -440,6 +444,7 @@ impl MessageProcessor {
440444
pending_thread_unloads,
441445
thread_state_manager,
442446
thread_watch_manager,
447+
auth_transition_lock,
443448
thread_list_state_permit,
444449
Arc::clone(&skills_watcher),
445450
);

codex-rs/app-server/src/request_processors.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::auth_mode::auth_mode_to_api;
12
use crate::bespoke_event_handling::apply_bespoke_event_handling;
23
use crate::command_exec::CommandExecManager;
34
use crate::command_exec::StartCommandExecParams;
@@ -394,6 +395,7 @@ use codex_feedback::FeedbackUploadOptions;
394395
use codex_git_utils::git_diff_to_remote;
395396
use codex_git_utils::resolve_root_git_project_for_trust;
396397
use codex_login::AuthManager;
398+
use codex_login::AuthReloadStatus;
397399
use codex_login::CODEX_OPEN_APP_URL;
398400
use codex_login::CodexAuth;
399401
use codex_login::LoginSuccessPage;
@@ -517,6 +519,81 @@ use uuid::Uuid;
517519
#[cfg(test)]
518520
use codex_app_server_protocol::ServerRequest;
519521

522+
async fn reload_auth_from_storage_if_idle(
523+
auth_manager: &Arc<AuthManager>,
524+
thread_manager: &Arc<ThreadManager>,
525+
config_manager: &ConfigManager,
526+
outgoing: &OutgoingMessageSender,
527+
thread_watch_manager: &ThreadWatchManager,
528+
chatgpt_base_url: &str,
529+
reason: &str,
530+
) {
531+
if *thread_watch_manager.subscribe_running_turn_count().borrow() != 0 {
532+
return;
533+
}
534+
535+
let status = auth_manager.reload_with_status().await;
536+
match handle_auth_reload_status(
537+
status,
538+
auth_manager,
539+
thread_manager,
540+
config_manager,
541+
outgoing,
542+
chatgpt_base_url,
543+
reason,
544+
)
545+
.await
546+
{
547+
AuthReloadStatus::Reloaded { .. } => {}
548+
AuthReloadStatus::Failed => {
549+
warn!("failed to reload auth from storage before {reason}");
550+
}
551+
}
552+
}
553+
554+
async fn handle_auth_reload_status(
555+
status: AuthReloadStatus,
556+
auth_manager: &Arc<AuthManager>,
557+
thread_manager: &Arc<ThreadManager>,
558+
config_manager: &ConfigManager,
559+
outgoing: &OutgoingMessageSender,
560+
chatgpt_base_url: &str,
561+
reason: &str,
562+
) -> AuthReloadStatus {
563+
match status {
564+
AuthReloadStatus::Reloaded { changed } => {
565+
if changed {
566+
let invalidated_thread_count =
567+
thread_manager.invalidate_model_transport_caches().await;
568+
info!(
569+
"auth reloaded from storage before {reason}; invalidated model transport caches for {invalidated_thread_count} tracked thread(s)"
570+
);
571+
config_manager.replace_cloud_config_bundle_loader(
572+
Arc::clone(auth_manager),
573+
chatgpt_base_url.to_string(),
574+
);
575+
config_manager
576+
.sync_default_client_residency_requirement()
577+
.await;
578+
let auth = auth_manager.auth_cached();
579+
outgoing
580+
.send_server_notification(ServerNotification::AccountUpdated(
581+
AccountUpdatedNotification {
582+
auth_mode: auth
583+
.as_ref()
584+
.map(CodexAuth::api_auth_mode)
585+
.map(auth_mode_to_api),
586+
plan_type: auth.as_ref().and_then(CodexAuth::account_plan_type),
587+
},
588+
))
589+
.await;
590+
}
591+
AuthReloadStatus::Reloaded { changed }
592+
}
593+
AuthReloadStatus::Failed => AuthReloadStatus::Failed,
594+
}
595+
}
596+
520597
mod account_processor;
521598
mod apps_processor;
522599
mod bedrock_auth;

codex-rs/app-server/src/request_processors/account_processor.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ pub(crate) struct AccountRequestProcessor {
7373
outgoing: Arc<OutgoingMessageSender>,
7474
config: Arc<Config>,
7575
config_manager: ConfigManager,
76+
thread_watch_manager: ThreadWatchManager,
77+
auth_transition_lock: Arc<Mutex<()>>,
7678
active_login: Arc<Mutex<Option<ActiveLogin>>>,
7779
}
7880

@@ -83,13 +85,17 @@ impl AccountRequestProcessor {
8385
outgoing: Arc<OutgoingMessageSender>,
8486
config: Arc<Config>,
8587
config_manager: ConfigManager,
88+
thread_watch_manager: ThreadWatchManager,
89+
auth_transition_lock: Arc<Mutex<()>>,
8690
) -> Self {
8791
Self {
8892
auth_manager,
8993
thread_manager,
9094
outgoing,
9195
config,
9296
config_manager,
97+
thread_watch_manager,
98+
auth_transition_lock,
9399
active_login: Arc::new(Mutex::new(None)),
94100
}
95101
}
@@ -995,6 +1001,35 @@ impl AccountRequestProcessor {
9951001
params: GetAccountParams,
9961002
) -> Result<GetAccountResponse, JSONRPCErrorError> {
9971003
let do_refresh = params.refresh_token;
1004+
let mut auth_changed = false;
1005+
1006+
if params.reload_auth_from_storage {
1007+
let _auth_transition_guard = self.auth_transition_lock.lock().await;
1008+
if *self
1009+
.thread_watch_manager
1010+
.subscribe_running_turn_count()
1011+
.borrow()
1012+
== 0
1013+
{
1014+
let status = self.auth_manager.reload_with_status().await;
1015+
match handle_auth_reload_status(
1016+
status,
1017+
&self.auth_manager,
1018+
&self.thread_manager,
1019+
&self.config_manager,
1020+
&self.outgoing,
1021+
&self.config.chatgpt_base_url,
1022+
"account/get",
1023+
)
1024+
.await
1025+
{
1026+
AuthReloadStatus::Reloaded { changed } => auth_changed = changed,
1027+
AuthReloadStatus::Failed => {
1028+
return Err(internal_error("failed to reload auth from storage"));
1029+
}
1030+
}
1031+
}
1032+
}
9981033

9991034
self.refresh_token_if_requested(do_refresh).await;
10001035

@@ -1010,6 +1045,7 @@ impl AccountRequestProcessor {
10101045
Ok(GetAccountResponse {
10111046
account,
10121047
requires_openai_auth: account_state.requires_openai_auth,
1048+
auth_changed,
10131049
})
10141050
}
10151051

codex-rs/app-server/src/request_processors/thread_processor.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ pub(crate) struct ThreadRequestProcessor {
388388
pub(super) pending_thread_unloads: Arc<Mutex<HashSet<ThreadId>>>,
389389
pub(super) thread_state_manager: ThreadStateManager,
390390
pub(super) thread_watch_manager: ThreadWatchManager,
391+
pub(super) auth_transition_lock: Arc<Mutex<()>>,
391392
pub(super) thread_list_state_permit: Arc<Semaphore>,
392393
pub(super) thread_goal_processor: ThreadGoalRequestProcessor,
393394
pub(super) state_db: Option<StateDbHandle>,
@@ -421,6 +422,7 @@ impl ThreadRequestProcessor {
421422
pending_thread_unloads: Arc<Mutex<HashSet<ThreadId>>>,
422423
thread_state_manager: ThreadStateManager,
423424
thread_watch_manager: ThreadWatchManager,
425+
auth_transition_lock: Arc<Mutex<()>>,
424426
thread_list_state_permit: Arc<Semaphore>,
425427
thread_goal_processor: ThreadGoalRequestProcessor,
426428
state_db: Option<StateDbHandle>,
@@ -439,6 +441,7 @@ impl ThreadRequestProcessor {
439441
pending_thread_unloads,
440442
thread_state_manager,
441443
thread_watch_manager,
444+
auth_transition_lock,
442445
thread_list_state_permit,
443446
thread_goal_processor,
444447
state_db,
@@ -947,6 +950,18 @@ impl ThreadRequestProcessor {
947950
supports_openai_form_elicitation: bool,
948951
request_context: RequestContext,
949952
) -> Result<(), JSONRPCErrorError> {
953+
let _auth_transition_guard = self.auth_transition_lock.lock().await;
954+
reload_auth_from_storage_if_idle(
955+
&self.auth_manager,
956+
&self.thread_manager,
957+
&self.config_manager,
958+
&self.outgoing,
959+
&self.thread_watch_manager,
960+
&self.config.chatgpt_base_url,
961+
"thread/start",
962+
)
963+
.await;
964+
950965
let ThreadStartParams {
951966
model,
952967
model_provider,
@@ -3019,6 +3034,18 @@ impl ThreadRequestProcessor {
30193034
app_server_client_version: Option<String>,
30203035
supports_openai_form_elicitation: bool,
30213036
) -> Result<(), JSONRPCErrorError> {
3037+
let _auth_transition_guard = self.auth_transition_lock.lock().await;
3038+
reload_auth_from_storage_if_idle(
3039+
&self.auth_manager,
3040+
&self.thread_manager,
3041+
&self.config_manager,
3042+
&self.outgoing,
3043+
&self.thread_watch_manager,
3044+
&self.config.chatgpt_base_url,
3045+
"thread/resume",
3046+
)
3047+
.await;
3048+
30223049
if let Ok(thread_id) = ThreadId::from_string(&params.thread_id)
30233050
&& self
30243051
.pending_thread_unloads

codex-rs/app-server/src/request_processors/turn_processor.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pub(crate) struct TurnRequestProcessor {
9595
pending_thread_unloads: Arc<Mutex<HashSet<ThreadId>>>,
9696
thread_state_manager: ThreadStateManager,
9797
thread_watch_manager: ThreadWatchManager,
98+
auth_transition_lock: Arc<Mutex<()>>,
9899
thread_list_state_permit: Arc<Semaphore>,
99100
skills_watcher: Arc<SkillsWatcher>,
100101
}
@@ -150,6 +151,7 @@ impl TurnRequestProcessor {
150151
pending_thread_unloads: Arc<Mutex<HashSet<ThreadId>>>,
151152
thread_state_manager: ThreadStateManager,
152153
thread_watch_manager: ThreadWatchManager,
154+
auth_transition_lock: Arc<Mutex<()>>,
153155
thread_list_state_permit: Arc<Semaphore>,
154156
skills_watcher: Arc<SkillsWatcher>,
155157
) -> Self {
@@ -166,6 +168,7 @@ impl TurnRequestProcessor {
166168
pending_thread_unloads,
167169
thread_state_manager,
168170
thread_watch_manager,
171+
auth_transition_lock,
169172
thread_list_state_permit,
170173
skills_watcher,
171174
}
@@ -478,6 +481,7 @@ impl TurnRequestProcessor {
478481
app_server_client_version: Option<String>,
479482
supports_openai_form_elicitation: bool,
480483
) -> Result<TurnStartResponse, JSONRPCErrorError> {
484+
let _auth_transition_guard = self.auth_transition_lock.lock().await;
481485
let (thread_id, thread) =
482486
self.load_thread(&params.thread_id)
483487
.await
@@ -486,6 +490,16 @@ impl TurnRequestProcessor {
486490
})?;
487491
self.ensure_direct_input_allowed(&request_id, thread.as_ref())
488492
.await?;
493+
reload_auth_from_storage_if_idle(
494+
&self.auth_manager,
495+
&self.thread_manager,
496+
&self.config_manager,
497+
&self.outgoing,
498+
&self.thread_watch_manager,
499+
&self.config.chatgpt_base_url,
500+
"turn/start",
501+
)
502+
.await;
489503
if let Err(error) = Self::validate_v2_input_limit(&params.input) {
490504
self.track_error_response(
491505
&request_id,
@@ -583,6 +597,9 @@ impl TurnRequestProcessor {
583597
self.track_error_response(&request_id, &error, /*error_type*/ None);
584598
error
585599
})?;
600+
self.thread_watch_manager
601+
.note_turn_started(&thread_id.to_string())
602+
.await;
586603

587604
if turn_has_input {
588605
let config_snapshot = thread.config_snapshot().await;

codex-rs/config/src/types.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,14 @@ pub struct Tui {
779779
#[serde(default)]
780780
pub keymap: TuiKeymap,
781781

782+
/// Optional synthetic user-turn prompt injected after a turn fails with
783+
/// `UsageLimitExceeded`.
784+
///
785+
/// When unset, Codext uses the built-in default recovery prompt.
786+
/// When set to an empty string, Codext disables this automatic recovery turn.
787+
#[serde(default)]
788+
pub usage_limit_resume_prompt: Option<String>,
789+
782790
/// Startup tooltip availability NUX state persisted by the TUI.
783791
#[serde(default)]
784792
pub model_availability_nux: ModelAvailabilityNuxConfig,

codex-rs/core/config.schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3363,6 +3363,11 @@
33633363
"description": "Syntax highlighting theme name (kebab-case).\n\nWhen set, overrides automatic light/dark theme detection. Use `/theme` in the TUI or see `$CODEX_HOME/themes` for custom themes.",
33643364
"type": "string"
33653365
},
3366+
"usage_limit_resume_prompt": {
3367+
"default": null,
3368+
"description": "Optional synthetic user-turn prompt injected after a turn fails with `UsageLimitExceeded`.\n\nWhen unset, Codext uses the built-in default recovery prompt. When set to an empty string, Codext disables this automatic recovery turn.",
3369+
"type": "string"
3370+
},
33663371
"vim_mode_default": {
33673372
"default": false,
33683373
"description": "Start the composer in Vim mode (`Normal`) by default. Defaults to `false`.",

codex-rs/core/src/client.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,10 @@ impl ModelClient {
506506
.unwrap_or_else(std::sync::PoisonError::into_inner) = websocket_session;
507507
}
508508

509+
pub(crate) fn invalidate_cached_transport_state(&self) {
510+
self.store_cached_websocket_session(WebsocketSession::default());
511+
}
512+
509513
pub(crate) fn force_http_fallback(
510514
&self,
511515
session_telemetry: &SessionTelemetry,

0 commit comments

Comments
 (0)