From fb350d1e7d52c4c3b42f230a4715ee4adf314f08 Mon Sep 17 00:00:00 2001 From: stevenlee-oai Date: Mon, 13 Jul 2026 22:45:47 +0000 Subject: [PATCH] Attach connector caches to diagnostic uploads (#32891) ## What changed - Include the active Codex Apps tools cache and connector directory cache, when available, in diagnostic uploads that include logs. - Resolve cache paths from the current authentication context and use stable attachment filenames. - List both optional cache files in the upload consent dialog. ## Testing - Add coverage for selecting existing active cache files, including a directory cache without an account ID. - Update consent dialog snapshots to include the optional attachments. GitOrigin-RevId: 1d4ffbf2bcb96653f03dc25f91d80dd65038d585 --- .../request_processors/feedback_processor.rs | 167 +++++++++++++++++- .../connectors/src/connector_runtime/mod.rs | 10 ++ codex-rs/connectors/src/directory_cache.rs | 3 +- codex-rs/connectors/src/lib.rs | 1 + codex-rs/feedback/src/lib.rs | 4 + codex-rs/tui/src/bottom_pane/feedback_view.rs | 12 ++ ...ck_upload_consent_lists_doctor_report.snap | 4 +- ...sts_windows_sandbox_log_when_included.snap | 4 +- ...s__feedback_good_result_consent_popup.snap | 2 + ..._tests__feedback_upload_consent_popup.snap | 3 +- 10 files changed, 197 insertions(+), 13 deletions(-) diff --git a/codex-rs/app-server/src/request_processors/feedback_processor.rs b/codex-rs/app-server/src/request_processors/feedback_processor.rs index 5f488f72c3..cb67117613 100644 --- a/codex-rs/app-server/src/request_processors/feedback_processor.rs +++ b/codex-rs/app-server/src/request_processors/feedback_processor.rs @@ -1,4 +1,9 @@ use super::*; +use codex_connectors::ConnectorDirectoryCacheContext; +use codex_connectors::ConnectorDirectoryCacheKey; +use codex_connectors::connector_runtime_cache_path; +use codex_feedback::CODEX_APP_DIRECTORY_CACHE_ATTACHMENT_FILENAME; +use codex_feedback::CODEX_APPS_TOOLS_CACHE_ATTACHMENT_FILENAME; #[cfg(target_os = "windows")] use codex_feedback::WINDOWS_SANDBOX_LOG_ATTACHMENT_FILENAME; @@ -70,17 +75,16 @@ impl FeedbackRequestProcessor { None => None, }; - if let Some(chatgpt_user_id) = self - .auth_manager - .auth_cached() - .and_then(|auth| auth.get_chatgpt_user_id()) + let auth = self.auth_manager.auth_cached(); + if let Some(chatgpt_user_id) = auth + .as_ref() + .and_then(codex_login::CodexAuth::get_chatgpt_user_id) { tracing::info!(target: "feedback_tags", chatgpt_user_id); } - if let Some(account_id) = self - .auth_manager - .auth_cached() - .and_then(|auth| auth.get_account_id()) + if let Some(account_id) = auth + .as_ref() + .and_then(codex_login::CodexAuth::get_account_id) { tracing::info!(target: "feedback_tags", account_id); } @@ -197,6 +201,15 @@ impl FeedbackRequestProcessor { { attachment_paths.push(sandbox_log_attachment); } + for cache_attachment in tool_cache_feedback_attachments( + self.config.codex_home.as_path(), + &self.config.chatgpt_base_url, + auth.as_ref(), + ) { + if seen_attachment_paths.insert(cache_attachment.path.clone()) { + attachment_paths.push(cache_attachment); + } + } } if let Some(extra_log_files) = extra_log_files { for extra_log_file in extra_log_files { @@ -272,6 +285,47 @@ impl FeedbackRequestProcessor { } } +fn tool_cache_feedback_attachments( + codex_home: &Path, + chatgpt_base_url: &str, + auth: Option<&CodexAuth>, +) -> Vec { + let mut attachments = Vec::with_capacity(2); + let tools_cache_path = connector_runtime_cache_path(codex_home, auth); + if tools_cache_path.is_file() { + attachments.push(FeedbackAttachmentPath { + path: tools_cache_path, + attachment_filename_override: Some( + CODEX_APPS_TOOLS_CACHE_ATTACHMENT_FILENAME.to_string(), + ), + }); + } + + let Some(auth) = auth.filter(|auth| auth.uses_codex_backend()) else { + return attachments; + }; + let directory_cache_context = ConnectorDirectoryCacheContext::new( + codex_home.to_path_buf(), + ConnectorDirectoryCacheKey::new( + chatgpt_base_url.to_string(), + auth.get_account_id(), + auth.get_chatgpt_user_id(), + auth.is_workspace_account(), + ), + ); + let directory_cache_path = directory_cache_context.cache_path(); + if directory_cache_path.is_file() { + attachments.push(FeedbackAttachmentPath { + path: directory_cache_path, + attachment_filename_override: Some( + CODEX_APP_DIRECTORY_CACHE_ATTACHMENT_FILENAME.to_string(), + ), + }); + } + + attachments +} + fn auto_review_rollout_filename(thread_id: ThreadId) -> String { format!("auto-review-rollout-{thread_id}.jsonl") } @@ -292,11 +346,106 @@ fn windows_sandbox_log_attachment(_codex_home: &Path) -> Option>(); + + assert_eq!( + attachments, + vec![ + ( + tools_cache_path, + Some(CODEX_APPS_TOOLS_CACHE_ATTACHMENT_FILENAME.to_string()), + ), + ( + directory_cache_path, + Some(CODEX_APP_DIRECTORY_CACHE_ATTACHMENT_FILENAME.to_string()), + ), + ] + ); + } + + #[test] + fn tool_cache_feedback_attachments_include_directory_cache_without_account_id() { + let codex_home = tempfile::tempdir().expect("create tempdir"); + let auth = CodexAuth::Headers(codex_login::AuthHeaders::new( + reqwest::header::HeaderMap::new(), + )); + let directory_cache_context = ConnectorDirectoryCacheContext::new( + codex_home.path().to_path_buf(), + ConnectorDirectoryCacheKey::new( + "https://chatgpt.com/backend-api".to_string(), + /*account_id*/ None, + auth.get_chatgpt_user_id(), + auth.is_workspace_account(), + ), + ); + let directory_cache_path = directory_cache_context.cache_path(); + std::fs::create_dir_all( + directory_cache_path + .parent() + .expect("directory cache parent"), + ) + .expect("create directory cache directory"); + std::fs::write(&directory_cache_path, b"directory").expect("write directory cache"); + + let attachments = tool_cache_feedback_attachments( + codex_home.path(), + "https://chatgpt.com/backend-api", + Some(&auth), + ) + .into_iter() + .map(|attachment| (attachment.path, attachment.attachment_filename_override)) + .collect::>(); + + assert_eq!( + attachments, + vec![( + directory_cache_path, + Some(CODEX_APP_DIRECTORY_CACHE_ATTACHMENT_FILENAME.to_string()), + )] + ); + } + + #[cfg(target_os = "windows")] #[test] fn windows_sandbox_log_attachment_uses_current_log() { let codex_home = tempfile::tempdir().expect("create tempdir"); diff --git a/codex-rs/connectors/src/connector_runtime/mod.rs b/codex-rs/connectors/src/connector_runtime/mod.rs index be2949e9f7..acddc82015 100644 --- a/codex-rs/connectors/src/connector_runtime/mod.rs +++ b/codex-rs/connectors/src/connector_runtime/mod.rs @@ -6,6 +6,7 @@ //! owned by the connector metadata store, not by this module. use std::collections::HashMap; +use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use std::sync::Mutex; @@ -75,6 +76,15 @@ pub fn connector_runtime_context_key(auth: Option<&CodexAuth>) -> ConnectorRunti } } +/// Returns the persisted connector runtime tools cache path for the active auth identity. +pub fn connector_runtime_cache_path(codex_home: &Path, auth: Option<&CodexAuth>) -> PathBuf { + let identity = ConnectorRuntimeIdentity { + codex_home: codex_home.to_path_buf(), + key: connector_runtime_context_key(auth), + }; + tools_cache_path(&identity) +} + /// One atomically published connector runtime state. /// /// Tools remain raw and in response order. Local and managed configuration is diff --git a/codex-rs/connectors/src/directory_cache.rs b/codex-rs/connectors/src/directory_cache.rs index beaa21e144..abaa8b049e 100644 --- a/codex-rs/connectors/src/directory_cache.rs +++ b/codex-rs/connectors/src/directory_cache.rs @@ -26,7 +26,8 @@ impl ConnectorDirectoryCacheContext { } } - pub(crate) fn cache_path(&self) -> PathBuf { + /// Returns the persisted connector directory cache path for this identity. + pub fn cache_path(&self) -> PathBuf { let cache_key_json = serde_json::to_string(&self.cache_key).unwrap_or_default(); let cache_key_hash = sha1_hex(&cache_key_json); self.codex_home diff --git a/codex-rs/connectors/src/lib.rs b/codex-rs/connectors/src/lib.rs index 8d072c6d67..e98c894f50 100644 --- a/codex-rs/connectors/src/lib.rs +++ b/codex-rs/connectors/src/lib.rs @@ -36,6 +36,7 @@ pub use connector_runtime::ConnectorRuntimeFetchTicket; pub use connector_runtime::ConnectorRuntimeManager; pub use connector_runtime::ConnectorRuntimePayload; pub use connector_runtime::ConnectorRuntimeSnapshot; +pub use connector_runtime::connector_runtime_cache_path; pub use connector_runtime::connector_runtime_context_key; pub use directory_cache::ConnectorDirectoryCacheContext; pub use plugin_config::parse_plugin_app_config; diff --git a/codex-rs/feedback/src/lib.rs b/codex-rs/feedback/src/lib.rs index a8864690bd..375b10aa6d 100644 --- a/codex-rs/feedback/src/lib.rs +++ b/codex-rs/feedback/src/lib.rs @@ -31,6 +31,10 @@ pub use feedback_diagnostics::FeedbackDiagnostics; /// Filename used for the redacted `codex doctor --json` feedback attachment. pub const DOCTOR_REPORT_ATTACHMENT_FILENAME: &str = "codex-doctor-report.json"; +/// Filename used for the raw Codex Apps MCP tools cache feedback attachment. +pub const CODEX_APPS_TOOLS_CACHE_ATTACHMENT_FILENAME: &str = "codex-apps-tools-cache.json"; +/// Filename used for the raw connector directory cache feedback attachment. +pub const CODEX_APP_DIRECTORY_CACHE_ATTACHMENT_FILENAME: &str = "codex-app-directory-cache.json"; /// Filename used for the Windows sandbox log feedback attachment. pub const WINDOWS_SANDBOX_LOG_ATTACHMENT_FILENAME: &str = "windows-sandbox.log"; const DEFAULT_MAX_BYTES: usize = 4 * 1024 * 1024; // 4 MiB diff --git a/codex-rs/tui/src/bottom_pane/feedback_view.rs b/codex-rs/tui/src/bottom_pane/feedback_view.rs index ce380a867d..15874531bc 100644 --- a/codex-rs/tui/src/bottom_pane/feedback_view.rs +++ b/codex-rs/tui/src/bottom_pane/feedback_view.rs @@ -1,3 +1,5 @@ +use codex_feedback::CODEX_APP_DIRECTORY_CACHE_ATTACHMENT_FILENAME; +use codex_feedback::CODEX_APPS_TOOLS_CACHE_ATTACHMENT_FILENAME; use codex_feedback::DOCTOR_REPORT_ATTACHMENT_FILENAME; use codex_feedback::FEEDBACK_DIAGNOSTICS_ATTACHMENT_FILENAME; use codex_feedback::FeedbackDiagnostics; @@ -510,6 +512,16 @@ pub(crate) fn feedback_upload_consent_params( DOCTOR_REPORT_ATTACHMENT_FILENAME.into(), ]) .into(), + Line::from(vec![ + " • ".into(), + format!("{CODEX_APPS_TOOLS_CACHE_ATTACHMENT_FILENAME} (if available)").into(), + ]) + .into(), + Line::from(vec![ + " • ".into(), + format!("{CODEX_APP_DIRECTORY_CACHE_ATTACHMENT_FILENAME} (if available)").into(), + ]) + .into(), ]; if include_windows_sandbox_log { header_lines.push( diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__feedback_view__tests__feedback_upload_consent_lists_doctor_report.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__feedback_view__tests__feedback_upload_consent_lists_doctor_report.snap index e91d736ecd..c5abceac69 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__feedback_view__tests__feedback_upload_consent_lists_doctor_report.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__feedback_view__tests__feedback_upload_consent_lists_doctor_report.snap @@ -1,6 +1,6 @@ --- source: tui/src/bottom_pane/feedback_view.rs -assertion_line: 716 +assertion_line: 728 expression: rendered --- Upload logs? @@ -8,5 +8,7 @@ Upload logs? The following files will be sent: • codex-logs.log • codex-doctor-report.json + • codex-apps-tools-cache.json (if available) + • codex-app-directory-cache.json (if available) • rollout.jsonl • auto-review-rollout.jsonl diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__feedback_view__tests__feedback_upload_consent_lists_windows_sandbox_log_when_included.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__feedback_view__tests__feedback_upload_consent_lists_windows_sandbox_log_when_included.snap index 741505d5f5..68474648aa 100644 --- a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__feedback_view__tests__feedback_upload_consent_lists_windows_sandbox_log_when_included.snap +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__feedback_view__tests__feedback_upload_consent_lists_windows_sandbox_log_when_included.snap @@ -1,6 +1,6 @@ --- source: tui/src/bottom_pane/feedback_view.rs -assertion_line: 734 +assertion_line: 746 expression: rendered --- Upload logs? @@ -8,6 +8,8 @@ Upload logs? The following files will be sent: • codex-logs.log • codex-doctor-report.json + • codex-apps-tools-cache.json (if available) + • codex-app-directory-cache.json (if available) • windows-sandbox.log • rollout.jsonl • auto-review-rollout.jsonl diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__feedback_good_result_consent_popup.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__feedback_good_result_consent_popup.snap index cdeedc7b78..1dc370c703 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__feedback_good_result_consent_popup.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__feedback_good_result_consent_popup.snap @@ -7,6 +7,8 @@ expression: popup The following files will be sent: • codex-logs.log • codex-doctor-report.json + • codex-apps-tools-cache.json (if available) + • codex-app-directory-cache.json (if available) • auto-review-rollout-thread-1.jsonl • codex-connectivity-diagnostics.txt diff --git a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__feedback_upload_consent_popup.snap b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__feedback_upload_consent_popup.snap index b860cf8fb3..d3aa1624fa 100644 --- a/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__feedback_upload_consent_popup.snap +++ b/codex-rs/tui/src/chatwidget/snapshots/codex_tui__chatwidget__tests__feedback_upload_consent_popup.snap @@ -1,6 +1,5 @@ --- source: tui/src/chatwidget/tests/popups_and_settings.rs -assertion_line: 2562 expression: popup --- Upload logs? @@ -8,6 +7,8 @@ expression: popup The following files will be sent: • codex-logs.log • codex-doctor-report.json + • codex-apps-tools-cache.json (if available) + • codex-app-directory-cache.json (if available) • windows-sandbox.log • auto-review-rollout-thread-1.jsonl • codex-connectivity-diagnostics.txt