[apps] Improve app/list with force_fetch=true (#12745)

- [x] Improve app/list with force_fetch=true, we now keep cached
snapshot until both install apps and directory apps load.
This commit is contained in:
Matthew Zeng
2026-02-26 19:54:03 -08:00
committed by GitHub
parent 7e980d7db6
commit 6fe3dc2e22
3 changed files with 71 additions and 11 deletions

View File

@@ -5249,6 +5249,7 @@ impl CodexMessageProcessor {
connectors::list_cached_accessible_connectors_from_mcp_tools(&config),
connectors::list_cached_all_connectors(&config)
);
let cached_all_connectors = all_connectors.clone();
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
@@ -5275,6 +5276,19 @@ impl CodexMessageProcessor {
let app_list_deadline = tokio::time::Instant::now() + APP_LIST_LOAD_TIMEOUT;
let mut accessible_loaded = false;
let mut all_loaded = false;
let mut last_notified_apps = None;
if accessible_connectors.is_some() || all_connectors.is_some() {
let merged = connectors::with_app_enabled_state(
Self::merge_loaded_apps(
all_connectors.as_deref(),
accessible_connectors.as_deref(),
),
&config,
);
Self::send_app_list_updated_notification(&outgoing, merged.clone()).await;
last_notified_apps = Some(merged);
}
loop {
let result = match tokio::time::timeout_at(app_list_deadline, rx.recv()).await {
@@ -5331,14 +5345,30 @@ impl CodexMessageProcessor {
}
}
let showing_interim_force_refetch = force_refetch && !(accessible_loaded && all_loaded);
let all_connectors_for_update =
if showing_interim_force_refetch && cached_all_connectors.is_some() {
cached_all_connectors.as_deref()
} else {
all_connectors.as_deref()
};
let accessible_connectors_for_update =
if showing_interim_force_refetch && !accessible_loaded {
None
} else {
accessible_connectors.as_deref()
};
let merged = connectors::with_app_enabled_state(
Self::merge_loaded_apps(
all_connectors.as_deref(),
accessible_connectors.as_deref(),
all_connectors_for_update,
accessible_connectors_for_update,
),
&config,
);
Self::send_app_list_updated_notification(&outgoing, merged.clone()).await;
if last_notified_apps.as_ref() != Some(&merged) {
Self::send_app_list_updated_notification(&outgoing, merged.clone()).await;
last_notified_apps = Some(merged.clone());
}
if accessible_loaded && all_loaded {
match Self::paginate_apps(merged.as_slice(), start, limit) {

View File

@@ -994,6 +994,41 @@ async fn list_apps_force_refetch_patches_updates_from_cached_snapshots() -> Resu
let first_update = read_app_list_updated_notification(&mut mcp).await?;
assert_eq!(
first_update.data,
vec![
AppInfo {
id: "beta".to_string(),
name: "Beta App".to_string(),
description: Some("Beta v1".to_string()),
logo_url: None,
logo_url_dark: None,
distribution_channel: None,
branding: None,
app_metadata: None,
labels: None,
install_url: Some("https://chatgpt.com/apps/beta-app/beta".to_string()),
is_accessible: true,
is_enabled: true,
},
AppInfo {
id: "alpha".to_string(),
name: "Alpha".to_string(),
description: Some("Alpha v1".to_string()),
logo_url: None,
logo_url_dark: None,
distribution_channel: None,
branding: None,
app_metadata: None,
labels: None,
install_url: Some("https://chatgpt.com/apps/alpha/alpha".to_string()),
is_accessible: false,
is_enabled: true,
},
]
);
let second_update = read_app_list_updated_notification(&mut mcp).await?;
assert_eq!(
second_update.data,
vec![
AppInfo {
id: "alpha".to_string(),
@@ -1040,8 +1075,8 @@ async fn list_apps_force_refetch_patches_updates_from_cached_snapshots() -> Resu
is_accessible: false,
is_enabled: true,
}];
let second_update = read_app_list_updated_notification(&mut mcp).await?;
assert_eq!(second_update.data, expected_final);
let third_update = read_app_list_updated_notification(&mut mcp).await?;
assert_eq!(third_update.data, expected_final);
let refetch_response: JSONRPCResponse = timeout(
DEFAULT_TIMEOUT,

View File

@@ -298,12 +298,7 @@ fn merge_directory_app(existing: &mut DirectoryApp, incoming: DirectoryApp) {
.as_deref()
.map(|value| !value.trim().is_empty())
.unwrap_or(false);
let existing_description_present = existing
.description
.as_deref()
.map(|value| !value.trim().is_empty())
.unwrap_or(false);
if !existing_description_present && incoming_description_present {
if incoming_description_present {
existing.description = description;
}