diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 77ea94f1c7..dc90a32597 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -174,6 +174,7 @@ use crate::protocol::RequestUserInputEvent; use crate::protocol::ReviewDecision; use crate::protocol::SandboxPolicy; use crate::protocol::SessionConfiguredEvent; +use crate::protocol::SessionNetworkProxyRuntime; use crate::protocol::SkillDependencies as ProtocolSkillDependencies; use crate::protocol::SkillErrorInfo; use crate::protocol::SkillInterface as ProtocolSkillInterface; @@ -1070,6 +1071,14 @@ impl Session { })?), None => None, }; + let session_network_proxy = network_proxy.as_ref().map(|started| { + let proxy = started.proxy(); + SessionNetworkProxyRuntime { + http_addr: proxy.http_addr().to_string(), + socks_addr: proxy.socks_addr().to_string(), + admin_addr: proxy.admin_addr().to_string(), + } + }); let services = SessionServices { mcp_connection_manager: Arc::new(RwLock::new(McpConnectionManager::default())), @@ -1151,6 +1160,7 @@ impl Session { history_log_id, history_entry_count, initial_messages, + network_proxy: session_network_proxy, rollout_path, }), }) diff --git a/codex-rs/exec/tests/event_processor_with_json_output.rs b/codex-rs/exec/tests/event_processor_with_json_output.rs index 8c1c73e579..f349e4835f 100644 --- a/codex-rs/exec/tests/event_processor_with_json_output.rs +++ b/codex-rs/exec/tests/event_processor_with_json_output.rs @@ -98,6 +98,7 @@ fn session_configured_produces_thread_started_event() { history_log_id: 0, history_entry_count: 0, initial_messages: None, + network_proxy: None, rollout_path: Some(rollout_path), }), ); diff --git a/codex-rs/mcp-server/src/outgoing_message.rs b/codex-rs/mcp-server/src/outgoing_message.rs index e512eedbd7..1bf98534b0 100644 --- a/codex-rs/mcp-server/src/outgoing_message.rs +++ b/codex-rs/mcp-server/src/outgoing_message.rs @@ -307,6 +307,7 @@ mod tests { history_log_id: 1, history_entry_count: 1000, initial_messages: None, + network_proxy: None, rollout_path: Some(rollout_file.path().to_path_buf()), }), }; @@ -348,6 +349,7 @@ mod tests { history_log_id: 1, history_entry_count: 1000, initial_messages: None, + network_proxy: None, rollout_path: Some(rollout_file.path().to_path_buf()), }; let event = Event { @@ -413,6 +415,7 @@ mod tests { history_log_id: 1, history_entry_count: 1000, initial_messages: None, + network_proxy: None, rollout_path: Some(rollout_file.path().to_path_buf()), }; let event = Event { diff --git a/codex-rs/network-proxy/src/proxy.rs b/codex-rs/network-proxy/src/proxy.rs index 46936dd530..02276e8ee5 100644 --- a/codex-rs/network-proxy/src/proxy.rs +++ b/codex-rs/network-proxy/src/proxy.rs @@ -234,6 +234,18 @@ impl NetworkProxy { NetworkProxyBuilder::default() } + pub fn http_addr(&self) -> SocketAddr { + self.http_addr + } + + pub fn socks_addr(&self) -> SocketAddr { + self.socks_addr + } + + pub fn admin_addr(&self) -> SocketAddr { + self.admin_addr + } + pub fn apply_to_env(&self, env: &mut HashMap) { // Enforce proxying for all child processes when configured. We always override to ensure // the proxy is actually used even if the caller passed conflicting environment variables. diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index 462730f982..8f6e998792 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -2265,6 +2265,13 @@ pub struct SkillsListEntry { pub errors: Vec, } +#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS, PartialEq, Eq)] +pub struct SessionNetworkProxyRuntime { + pub http_addr: String, + pub socks_addr: String, + pub admin_addr: String, +} + #[derive(Debug, Clone, Deserialize, Serialize, JsonSchema, TS)] pub struct SessionConfiguredEvent { pub session_id: ThreadId, @@ -2306,6 +2313,11 @@ pub struct SessionConfiguredEvent { #[serde(skip_serializing_if = "Option::is_none")] pub initial_messages: Option>, + /// Runtime proxy bind addresses, when the managed proxy was started for this session. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[ts(optional)] + pub network_proxy: Option, + /// Path in which the rollout is stored. Can be `None` for ephemeral threads #[serde(skip_serializing_if = "Option::is_none")] pub rollout_path: Option, @@ -2696,6 +2708,7 @@ mod tests { history_log_id: 0, history_entry_count: 0, initial_messages: None, + network_proxy: None, rollout_path: Some(rollout_file.path().to_path_buf()), }), }; diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 6294df8347..d18ec23544 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -2353,6 +2353,7 @@ impl App { history_log_id: 0, history_entry_count: 0, initial_messages: None, + network_proxy: None, rollout_path: thread.rollout_path(), }), }; @@ -3042,6 +3043,7 @@ mod tests { history_log_id: 0, history_entry_count: 0, initial_messages: None, + network_proxy: None, rollout_path: Some(PathBuf::new()), }; Arc::new(new_session_info( @@ -3096,6 +3098,7 @@ mod tests { history_log_id: 0, history_entry_count: 0, initial_messages: None, + network_proxy: None, rollout_path: Some(PathBuf::new()), }), }); @@ -3142,6 +3145,7 @@ mod tests { history_log_id: 0, history_entry_count: 0, initial_messages: None, + network_proxy: None, rollout_path: Some(PathBuf::new()), }; diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 644bcae8c7..86b9c55436 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -613,6 +613,8 @@ pub(crate) struct ChatWidget { current_rollout_path: Option, // Current working directory (if known) current_cwd: Option, + // Runtime network proxy bind addresses from SessionConfigured. + session_network_proxy: Option, // Shared latch so we only warn once about invalid status-line item IDs. status_line_invalid_items_warned: Arc, // Cached git branch name for the status line (None if unknown). @@ -1015,6 +1017,7 @@ impl ChatWidget { self.bottom_pane .set_history_metadata(event.history_log_id, event.history_entry_count); self.set_skills(None); + self.session_network_proxy = event.network_proxy.clone(); self.thread_id = Some(event.session_id); self.thread_name = event.thread_name.clone(); self.forked_from = event.forked_from_id; @@ -2658,6 +2661,7 @@ impl ChatWidget { feedback_audience, current_rollout_path: None, current_cwd, + session_network_proxy: None, status_line_invalid_items_warned, status_line_branch: None, status_line_branch_cwd: None, @@ -2822,6 +2826,7 @@ impl ChatWidget { feedback_audience, current_rollout_path: None, current_cwd, + session_network_proxy: None, status_line_invalid_items_warned, status_line_branch: None, status_line_branch_cwd: None, @@ -2975,6 +2980,7 @@ impl ChatWidget { feedback_audience, current_rollout_path: None, current_cwd, + session_network_proxy: None, status_line_invalid_items_warned, status_line_branch: None, status_line_branch_cwd: None, @@ -4239,7 +4245,10 @@ impl ChatWidget { } pub(crate) fn add_debug_config_output(&mut self) { - self.add_to_history(crate::debug_config::new_debug_config_output(&self.config)); + self.add_to_history(crate::debug_config::new_debug_config_output( + &self.config, + self.session_network_proxy.as_ref(), + )); } fn open_status_line_setup(&mut self) { diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 57f9183090..3b8d1d3b79 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -164,6 +164,7 @@ async fn resumed_initial_messages_render_history() { message: "assistant reply".to_string(), }), ]), + network_proxy: None, rollout_path: Some(rollout_file.path().to_path_buf()), }; @@ -226,6 +227,7 @@ async fn replayed_user_message_preserves_text_elements_and_local_images() { text_elements: text_elements.clone(), local_images: local_images.clone(), })]), + network_proxy: None, rollout_path: Some(rollout_file.path().to_path_buf()), }; @@ -338,6 +340,7 @@ async fn submission_preserves_text_elements_and_local_images() { history_log_id: 0, history_entry_count: 0, initial_messages: None, + network_proxy: None, rollout_path: Some(rollout_file.path().to_path_buf()), }; chat.handle_codex_event(Event { @@ -417,6 +420,7 @@ async fn submission_prefers_selected_duplicate_skill_path() { history_log_id: 0, history_entry_count: 0, initial_messages: None, + network_proxy: None, rollout_path: Some(rollout_file.path().to_path_buf()), }; chat.handle_codex_event(Event { @@ -1106,6 +1110,7 @@ async fn make_chatwidget_manual( feedback_audience: FeedbackAudience::External, current_rollout_path: None, current_cwd: None, + session_network_proxy: None, status_line_invalid_items_warned: Arc::new(AtomicBool::new(false)), status_line_branch: None, status_line_branch_cwd: None, @@ -2971,6 +2976,7 @@ async fn plan_slash_command_with_args_submits_prompt_in_plan_mode() { history_log_id: 0, history_entry_count: 0, initial_messages: None, + network_proxy: None, rollout_path: None, }; chat.handle_codex_event(Event { diff --git a/codex-rs/tui/src/debug_config.rs b/codex-rs/tui/src/debug_config.rs index 908ee1d24a..95e207a90c 100644 --- a/codex-rs/tui/src/debug_config.rs +++ b/codex-rs/tui/src/debug_config.rs @@ -8,11 +8,34 @@ use codex_core::config_loader::RequirementSource; use codex_core::config_loader::ResidencyRequirement; use codex_core::config_loader::SandboxModeRequirement; use codex_core::config_loader::WebSearchModeRequirement; +use codex_core::protocol::SessionNetworkProxyRuntime; use ratatui::style::Stylize; use ratatui::text::Line; -pub(crate) fn new_debug_config_output(config: &Config) -> PlainHistoryCell { - PlainHistoryCell::new(render_debug_config_lines(&config.config_layer_stack)) +pub(crate) fn new_debug_config_output( + config: &Config, + session_network_proxy: Option<&SessionNetworkProxyRuntime>, +) -> PlainHistoryCell { + let mut lines = render_debug_config_lines(&config.config_layer_stack); + let started_network_proxy_state = match session_network_proxy { + Some(proxy) => format!( + "Some(http_addr={}, socks_addr={}, admin_addr={})", + proxy.http_addr, proxy.socks_addr, proxy.admin_addr + ), + None => "None".to_string(), + }; + + lines.push("".into()); + lines.push("Session runtime:".bold().into()); + lines.push( + format!( + " - network_proxy (Option): \ +{started_network_proxy_state}" + ) + .into(), + ); + + PlainHistoryCell::new(lines) } fn render_debug_config_lines(stack: &ConfigLayerStack) -> Vec> {