Merge branch 'main' into rhan/surface-updates

This commit is contained in:
rhan-oai
2026-03-19 13:42:37 -07:00
committed by GitHub
48 changed files with 4240 additions and 445 deletions
+3 -1
View File
@@ -1826,7 +1826,9 @@ impl Session {
code_mode_service: crate::tools::code_mode::CodeModeService::new(
config.js_repl_node_path.clone(),
),
environment: Arc::new(Environment),
environment: Arc::new(
Environment::create(config.experimental_exec_server_url.clone()).await?,
),
};
let js_repl = Arc::new(JsReplHandle::with_node_path(
config.js_repl_node_path.clone(),
+10 -2
View File
@@ -2455,7 +2455,11 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
true,
));
let network_approval = Arc::new(NetworkApprovalService::default());
let environment = Arc::new(codex_exec_server::Environment);
let environment = Arc::new(
codex_exec_server::Environment::create(None)
.await
.expect("create environment"),
);
let file_watcher = Arc::new(FileWatcher::noop());
let services = SessionServices {
@@ -3249,7 +3253,11 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
true,
));
let network_approval = Arc::new(NetworkApprovalService::default());
let environment = Arc::new(codex_exec_server::Environment);
let environment = Arc::new(
codex_exec_server::Environment::create(None)
.await
.expect("create environment"),
);
let file_watcher = Arc::new(FileWatcher::noop());
let services = SessionServices {
+38
View File
@@ -237,6 +237,7 @@ fn config_toml_deserializes_model_availability_nux() {
show_tooltips: true,
alternate_screen: AltScreenMode::default(),
status_line: None,
terminal_title: None,
theme: None,
model_availability_nux: ModelAvailabilityNuxConfig {
shown_count: HashMap::from([
@@ -921,6 +922,7 @@ fn tui_config_missing_notifications_field_defaults_to_enabled() {
show_tooltips: true,
alternate_screen: AltScreenMode::Auto,
status_line: None,
terminal_title: None,
theme: None,
model_availability_nux: ModelAvailabilityNuxConfig::default(),
}
@@ -4310,6 +4312,7 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
model_verbosity: None,
personality: Some(Personality::Pragmatic),
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
experimental_exec_server_url: None,
realtime_audio: RealtimeAudioConfig::default(),
experimental_realtime_start_instructions: None,
experimental_realtime_ws_base_url: None,
@@ -4348,6 +4351,7 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
tool_suggest: ToolSuggestConfig::default(),
tui_alternate_screen: AltScreenMode::Auto,
tui_status_line: None,
tui_terminal_title: None,
tui_theme: None,
otel: OtelConfig::default(),
},
@@ -4451,6 +4455,7 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
model_verbosity: None,
personality: Some(Personality::Pragmatic),
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
experimental_exec_server_url: None,
realtime_audio: RealtimeAudioConfig::default(),
experimental_realtime_start_instructions: None,
experimental_realtime_ws_base_url: None,
@@ -4489,6 +4494,7 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
tool_suggest: ToolSuggestConfig::default(),
tui_alternate_screen: AltScreenMode::Auto,
tui_status_line: None,
tui_terminal_title: None,
tui_theme: None,
otel: OtelConfig::default(),
};
@@ -4590,6 +4596,7 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
model_verbosity: None,
personality: Some(Personality::Pragmatic),
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
experimental_exec_server_url: None,
realtime_audio: RealtimeAudioConfig::default(),
experimental_realtime_start_instructions: None,
experimental_realtime_ws_base_url: None,
@@ -4628,6 +4635,7 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
tool_suggest: ToolSuggestConfig::default(),
tui_alternate_screen: AltScreenMode::Auto,
tui_status_line: None,
tui_terminal_title: None,
tui_theme: None,
otel: OtelConfig::default(),
};
@@ -4715,6 +4723,7 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
model_verbosity: Some(Verbosity::High),
personality: Some(Personality::Pragmatic),
chatgpt_base_url: "https://chatgpt.com/backend-api/".to_string(),
experimental_exec_server_url: None,
realtime_audio: RealtimeAudioConfig::default(),
experimental_realtime_start_instructions: None,
experimental_realtime_ws_base_url: None,
@@ -4753,6 +4762,7 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
tool_suggest: ToolSuggestConfig::default(),
tui_alternate_screen: AltScreenMode::Auto,
tui_status_line: None,
tui_terminal_title: None,
tui_theme: None,
otel: OtelConfig::default(),
};
@@ -5974,6 +5984,34 @@ experimental_realtime_start_instructions = "start instructions from config"
Ok(())
}
#[test]
fn experimental_exec_server_url_loads_from_config_toml() -> std::io::Result<()> {
let cfg: ConfigToml = toml::from_str(
r#"
experimental_exec_server_url = "http://127.0.0.1:8080"
"#,
)
.expect("TOML deserialization should succeed");
assert_eq!(
cfg.experimental_exec_server_url.as_deref(),
Some("http://127.0.0.1:8080")
);
let codex_home = TempDir::new()?;
let config = Config::load_from_base_config_with_overrides(
cfg,
ConfigOverrides::default(),
codex_home.path().to_path_buf(),
)?;
assert_eq!(
config.experimental_exec_server_url.as_deref(),
Some("http://127.0.0.1:8080")
);
Ok(())
}
#[test]
fn experimental_realtime_ws_base_url_loads_from_config_toml() -> std::io::Result<()> {
let cfg: ConfigToml = toml::from_str(
+19 -5
View File
@@ -60,7 +60,7 @@ pub enum ConfigEdit {
ClearPath { segments: Vec<String> },
}
/// Produces a config edit that sets `[tui] theme = "<name>"`.
/// Produces a config edit that sets `[tui].theme = "<name>"`.
pub fn syntax_theme_edit(name: &str) -> ConfigEdit {
ConfigEdit::SetPath {
segments: vec!["tui".to_string(), "theme".to_string()],
@@ -68,11 +68,12 @@ pub fn syntax_theme_edit(name: &str) -> ConfigEdit {
}
}
/// Produces a config edit that sets `[tui].status_line` to an explicit ordered list.
///
/// The array is written even when it is empty so "hide the status line" stays
/// distinct from "unset, so use defaults".
pub fn status_line_items_edit(items: &[String]) -> ConfigEdit {
let mut array = toml_edit::Array::new();
for item in items {
array.push(item.clone());
}
let array = items.iter().cloned().collect::<toml_edit::Array>();
ConfigEdit::SetPath {
segments: vec!["tui".to_string(), "status_line".to_string()],
@@ -80,6 +81,19 @@ pub fn status_line_items_edit(items: &[String]) -> ConfigEdit {
}
}
/// Produces a config edit that sets `[tui].terminal_title` to an explicit ordered list.
///
/// The array is written even when it is empty so "disabled title updates" stays
/// distinct from "unset, so use defaults".
pub fn terminal_title_items_edit(items: &[String]) -> ConfigEdit {
let array = items.iter().cloned().collect::<toml_edit::Array>();
ConfigEdit::SetPath {
segments: vec!["tui".to_string(), "terminal_title".to_string()],
value: TomlItem::Value(array.into()),
}
}
pub fn model_availability_nux_count_edits(shown_count: &HashMap<String, u32>) -> Vec<ConfigEdit> {
let mut shown_count_entries: Vec<_> = shown_count.iter().collect();
shown_count_entries.sort_unstable_by(|(left, _), (right, _)| left.cmp(right));
+15
View File
@@ -359,6 +359,11 @@ pub struct Config {
/// `current-dir`.
pub tui_status_line: Option<Vec<String>>,
/// Ordered list of terminal title item identifiers for the TUI.
///
/// When unset, the TUI defaults to: `project` and `spinner`.
pub tui_terminal_title: Option<Vec<String>>,
/// Syntax highlighting theme override (kebab-case name).
pub tui_theme: Option<String>,
@@ -493,6 +498,10 @@ pub struct Config {
/// Base URL for requests to ChatGPT (as opposed to the OpenAI API).
pub chatgpt_base_url: String,
/// Experimental / do not use. Overrides the URL used when connecting to
/// a remote exec server.
pub experimental_exec_server_url: Option<String>,
/// Machine-local realtime audio device preferences used by realtime voice.
pub realtime_audio: RealtimeAudioConfig,
@@ -1393,6 +1402,10 @@ pub struct ConfigToml {
/// Base URL override for the built-in `openai` model provider.
pub openai_base_url: Option<String>,
/// Experimental / do not use. Overrides the URL used when connecting to
/// a remote exec server.
pub experimental_exec_server_url: Option<String>,
/// Machine-local realtime audio device preferences used by realtime voice.
#[serde(default)]
pub audio: Option<RealtimeAudioToml>,
@@ -2745,6 +2758,7 @@ impl Config {
.chatgpt_base_url
.or(cfg.chatgpt_base_url)
.unwrap_or("https://chatgpt.com/backend-api/".to_string()),
experimental_exec_server_url: cfg.experimental_exec_server_url,
realtime_audio: cfg
.audio
.map_or_else(RealtimeAudioConfig::default, |audio| RealtimeAudioConfig {
@@ -2814,6 +2828,7 @@ impl Config {
.map(|t| t.alternate_screen)
.unwrap_or_default(),
tui_status_line: cfg.tui.as_ref().and_then(|t| t.status_line.clone()),
tui_terminal_title: cfg.tui.as_ref().and_then(|t| t.terminal_title.clone()),
tui_theme: cfg.tui.as_ref().and_then(|t| t.theme.clone()),
otel: {
let t: OtelConfigToml = cfg.otel.unwrap_or_default();
+7
View File
@@ -752,6 +752,13 @@ pub struct Tui {
#[serde(default)]
pub status_line: Option<Vec<String>>,
/// Ordered list of terminal title item identifiers.
///
/// When set, the TUI renders the selected items into the terminal window/tab title.
/// When unset, the TUI defaults to: `spinner` and `project`.
#[serde(default)]
pub terminal_title: Option<Vec<String>>,
/// Syntax highlighting theme name (kebab-case).
///
/// When set, overrides automatic light/dark theme detection.
+13 -2
View File
@@ -112,6 +112,7 @@ impl ToolOrchestrator {
let otel_tn = &tool_ctx.tool_name;
let otel_ci = &tool_ctx.call_id;
let otel_user = ToolDecisionSource::User;
let otel_automated_reviewer = ToolDecisionSource::AutomatedReviewer;
let otel_cfg = ToolDecisionSource::Config;
// 1) Approval
@@ -136,8 +137,13 @@ impl ToolOrchestrator {
network_approval_context: None,
};
let decision = tool.start_approval_async(req, approval_ctx).await;
let otel_source = if routes_approval_to_guardian(turn_ctx) {
otel_automated_reviewer.clone()
} else {
otel_user.clone()
};
otel.tool_decision(otel_tn, otel_ci, &decision, otel_user.clone());
otel.tool_decision(otel_tn, otel_ci, &decision, otel_source);
match decision {
ReviewDecision::Denied | ReviewDecision::Abort => {
@@ -286,7 +292,12 @@ impl ToolOrchestrator {
};
let decision = tool.start_approval_async(req, approval_ctx).await;
otel.tool_decision(otel_tn, otel_ci, &decision, otel_user);
let otel_source = if routes_approval_to_guardian(turn_ctx) {
otel_automated_reviewer
} else {
otel_user
};
otel.tool_decision(otel_tn, otel_ci, &decision, otel_source);
match decision {
ReviewDecision::Denied | ReviewDecision::Abort => {