merge(upstream): by-bucket sync with openai/codex@main

- Preserve TUI and core tool invariants (browser_*, agent_*, web_fetch gating, screenshot queue, UA/version)
- Incorporate upstream TUI notifications config and exec-tool flag mapping
- Resolve core config conflicts; align tests with forked Config shape

verify: scripts/upstream-merge/verify.sh ok; ./build-fast.sh ok
This commit is contained in:
just-every-code
2025-09-15 18:05:13 +00:00
3 changed files with 115 additions and 9 deletions
+78 -7
View File
@@ -6,6 +6,7 @@ use crate::config_types::GithubConfig;
use crate::config_types::ThemeName;
use crate::config_types::ThemeColors;
use crate::config_types::McpServerConfig;
use crate::config_types::Notifications;
use crate::config_types::SandboxWorkspaceWrite;
use crate::config_types::ShellEnvironmentPolicy;
use crate::config_types::ShellEnvironmentPolicyToml;
@@ -126,6 +127,10 @@ pub struct Config {
/// If unset the feature is disabled.
pub notify: Option<Vec<String>>,
/// TUI notifications preference. When set, the TUI will send OSC 9 notifications on approvals
/// and turn completions when not focused.
pub tui_notifications: Notifications,
/// The directory that should be treated as the current working directory
/// for the session. All relative paths inside the business-logic layer are
/// resolved against this path.
@@ -1502,7 +1507,10 @@ impl Config {
include_apply_patch_tool: include_apply_patch_tool.unwrap_or(false),
tools_web_search_request,
tools_web_search_allowed_domains,
use_experimental_streamable_shell_tool: false,
// Honor upstream opt-in switch name for our experimental streamable shell tool.
use_experimental_streamable_shell_tool: cfg
.experimental_use_exec_command_tool
.unwrap_or(false),
include_view_image_tool: include_view_image_tool_flag,
responses_originator_header,
debug: debug.unwrap_or(false),
@@ -1510,6 +1518,12 @@ impl Config {
using_chatgpt_auth,
github: cfg.github.unwrap_or_default(),
experimental_resume: cfg.experimental_resume,
// Surface TUI notifications preference from config when present.
tui_notifications: cfg
.tui
.as_ref()
.map(|t| t.notifications.clone())
.unwrap_or_default(),
};
Ok(config)
}
@@ -2099,9 +2113,13 @@ model_verbosity = "high"
tools_web_search_request: false,
tools_web_search_allowed_domains: None,
use_experimental_streamable_shell_tool: false,
include_view_image_tool: true,
responses_originator_header: "codex_cli_rs".to_string(),
debug: false,
using_chatgpt_auth: false,
github: GithubConfig::default(),
experimental_resume: None,
tui_notifications: Default::default(),
},
o3_profile_config
);
@@ -2158,9 +2176,13 @@ model_verbosity = "high"
tools_web_search_request: false,
tools_web_search_allowed_domains: None,
use_experimental_streamable_shell_tool: false,
include_view_image_tool: true,
responses_originator_header: "codex_cli_rs".to_string(),
debug: false,
using_chatgpt_auth: false,
github: GithubConfig::default(),
experimental_resume: None,
tui_notifications: Default::default(),
};
assert_eq!(expected_gpt3_profile_config, gpt3_profile_config);
@@ -2229,12 +2251,16 @@ model_verbosity = "high"
base_instructions: None,
include_plan_tool: false,
include_apply_patch_tool: false,
tools_web_search_request: false,
tools_web_search_allowed_domains: None,
use_experimental_streamable_shell_tool: false,
tools_web_search_request: false,
tools_web_search_allowed_domains: None,
use_experimental_streamable_shell_tool: false,
include_view_image_tool: true,
responses_originator_header: "codex_cli_rs".to_string(),
debug: false,
using_chatgpt_auth: false,
github: GithubConfig::default(),
experimental_resume: None,
tui_notifications: Default::default(),
};
assert_eq!(expected_zdr_profile_config, zdr_profile_config);
@@ -2291,11 +2317,13 @@ model_verbosity = "high"
include_apply_patch_tool: false,
tools_web_search_request: false,
responses_originator_header: "codex_cli_rs".to_string(),
preferred_auth_method: AuthMode::ChatGPT,
use_experimental_streamable_shell_tool: false,
include_view_image_tool: true,
disable_paste_burst: false,
use_experimental_reasoning_summary: false,
debug: false,
using_chatgpt_auth: false,
github: GithubConfig::default(),
experimental_resume: None,
tui_notifications: Default::default(),
};
assert_eq!(expected_gpt5_profile_config, gpt5_profile_config);
@@ -2373,3 +2401,46 @@ trust_level = "trusted"
// No test enforcing the presence of a standalone [projects] header.
}
#[cfg(test)]
mod notifications_tests {
use crate::config_types::Notifications;
use serde::Deserialize;
#[derive(Deserialize, Debug, PartialEq)]
struct TuiTomlTest {
notifications: Notifications,
}
#[derive(Deserialize, Debug, PartialEq)]
struct RootTomlTest {
tui: TuiTomlTest,
}
#[test]
fn test_tui_notifications_true() {
let toml = r#"
[tui]
notifications = true
"#;
let parsed: RootTomlTest = toml::from_str(toml).expect("deserialize notifications=true");
assert!(matches!(
parsed.tui.notifications,
Notifications::Enabled(true)
));
}
#[test]
fn test_tui_notifications_custom_array() {
let toml = r#"
[tui]
notifications = ["foo"]
"#;
let parsed: RootTomlTest =
toml::from_str(toml).expect("deserialize notifications=[\"foo\"]");
assert!(matches!(
parsed.tui.notifications,
Notifications::Custom(ref v) if v == &vec!["foo".to_string()]
));
}
}
+19
View File
@@ -126,6 +126,19 @@ pub enum HistoryPersistence {
None,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(untagged)]
pub enum Notifications {
Enabled(bool),
Custom(Vec<String>),
}
impl Default for Notifications {
fn default() -> Self {
Self::Enabled(false)
}
}
/// Collection of settings that are specific to the TUI.
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct Tui {
@@ -149,6 +162,11 @@ pub struct Tui {
#[serde(default)]
pub spinner: SpinnerSelection,
/// Enable desktop notifications from the TUI when the terminal is unfocused.
/// Defaults to `false`.
#[serde(default)]
pub notifications: Notifications,
/// Whether to use the terminal's Alternate Screen (full-screen) mode.
/// When false, Codex renders nothing and leaves the standard terminal
/// buffer visible; users can toggle back to Alternate Screen at runtime
@@ -170,6 +188,7 @@ impl Default for Tui {
show_reasoning: false,
stream: StreamConfig::default(),
spinner: SpinnerSelection::default(),
notifications: Notifications::default(),
alternate_screen: true,
}
}
+18 -2
View File
@@ -519,6 +519,9 @@ To have Codex use this script for notifications, you would configure it via `not
notify = ["python3", "/Users/mbolin/.codex/notify.py"]
```
> [!NOTE]
> Use `notify` for automation and integrations: Codex invokes your external program with a single JSON argument for each event, independent of the TUI. If you only want lightweight desktop notifications while using the TUI, prefer `tui.notifications`, which uses terminal escape codes and requires no external program. You can enable both; `tui.notifications` covers in‑TUI alerts (e.g., approval prompts), while `notify` is best for system‑level hooks or custom notifiers. Currently, `notify` emits only `agent-turn-complete`, whereas `tui.notifications` supports `agent-turn-complete` and `approval-requested` with optional filtering.
## history
By default, Codex CLI records messages sent to the model in `$CODEX_HOME/history.jsonl`. Note that on UNIX, the file permissions are set to `o600`, so it should only be readable and writable by the owner.
@@ -591,9 +594,21 @@ Options that are specific to the TUI.
```toml
[tui]
# More to come here
# Send desktop notifications when approvals are required or a turn completes.
# Defaults to false.
notifications = true
# You can optionally filter to specific notification types.
# Available types are "agent-turn-complete" and "approval-requested".
notifications = [ "agent-turn-complete", "approval-requested" ]
```
> [!NOTE]
> Codex emits desktop notifications using terminal escape codes. Not all terminals support these (notably, macOS Terminal.app and VS Code's terminal do not support custom notifications. iTerm2, Ghostty and WezTerm do support these notifications).
> [!NOTE]
> `tui.notifications` is built‑in and limited to the TUI session. For programmatic or cross‑environment notifications—or to integrate with OS‑specific notifiers—use the top‑level `notify` option to run an external program that receives event JSON. The two settings are independent and can be used together.
## Config reference
| Key | Type / Values | Notes |
@@ -631,7 +646,8 @@ Options that are specific to the TUI.
| `history.persistence` | `save-all` \| `none` | History file persistence (default: `save-all`). |
| `history.max_bytes` | number | Currently ignored (not enforced). |
| `file_opener` | `vscode` \| `vscode-insiders` \| `windsurf` \| `cursor` \| `none` | URI scheme for clickable citations (default: `vscode`). |
| `tui` | table | TUI‑specific options (reserved). |
| `tui` | table | TUI‑specific options. |
| `tui.notifications` | boolean \| array<string> | Enable desktop notifications in the tui (default: false). |
| `hide_agent_reasoning` | boolean | Hide model reasoning events. |
| `show_raw_agent_reasoning` | boolean | Show raw reasoning (when available). |
| `model_reasoning_effort` | `minimal` \| `low` \| `medium` \| `high` | Responses API reasoning effort. |