feat: make it possible to set disable_response_storage = true in config.toml (#714)

https://github.com/openai/codex/pull/642 introduced support for the
`--disable-response-storage` flag, but if you are a ZDR customer, it is
tedious to set this every time, so this PR makes it possible to set this
once in `config.toml` and be done with it.

Incidentally, this tidies things up such that now `init_codex()` takes
only one parameter: `Config`.
This commit is contained in:
Michael Bolin
2025-04-28 15:39:34 -07:00
committed by GitHub
parent cca1122ddc
commit e7ad9449ea
7 changed files with 41 additions and 33 deletions

View File

@@ -15,10 +15,7 @@ use tokio::sync::Notify;
/// Returns the wrapped [`Codex`] **and** the `SessionInitialized` event that /// Returns the wrapped [`Codex`] **and** the `SessionInitialized` event that
/// is received as a response to the initial `ConfigureSession` submission so /// is received as a response to the initial `ConfigureSession` submission so
/// that callers can surface the information to the UI. /// that callers can surface the information to the UI.
pub async fn init_codex( pub async fn init_codex(config: Config) -> anyhow::Result<(CodexWrapper, Event, Arc<Notify>)> {
config: Config,
disable_response_storage: bool,
) -> anyhow::Result<(CodexWrapper, Event, Arc<Notify>)> {
let ctrl_c = notify_on_sigint(); let ctrl_c = notify_on_sigint();
let codex = CodexWrapper::new(Codex::spawn(ctrl_c.clone())?); let codex = CodexWrapper::new(Codex::spawn(ctrl_c.clone())?);
let init_id = codex let init_id = codex
@@ -27,7 +24,7 @@ pub async fn init_codex(
instructions: config.instructions.clone(), instructions: config.instructions.clone(),
approval_policy: config.approval_policy, approval_policy: config.approval_policy,
sandbox_policy: config.sandbox_policy, sandbox_policy: config.sandbox_policy,
disable_response_storage, disable_response_storage: config.disable_response_storage,
}) })
.await?; .await?;

View File

@@ -21,6 +21,13 @@ pub struct Config {
pub approval_policy: AskForApproval, pub approval_policy: AskForApproval,
#[serde(default)] #[serde(default)]
pub sandbox_policy: SandboxPolicy, pub sandbox_policy: SandboxPolicy,
/// Disable server-side response storage (sends the full conversation
/// context with every request). Currently necessary for OpenAI customers
/// who have opted into Zero Data Retention (ZDR).
#[serde(default)]
pub disable_response_storage: bool,
/// System instructions. /// System instructions.
pub instructions: Option<String>, pub instructions: Option<String>,
} }
@@ -31,6 +38,7 @@ pub struct ConfigOverrides {
pub model: Option<String>, pub model: Option<String>,
pub approval_policy: Option<AskForApproval>, pub approval_policy: Option<AskForApproval>,
pub sandbox_policy: Option<SandboxPolicy>, pub sandbox_policy: Option<SandboxPolicy>,
pub disable_response_storage: Option<bool>,
} }
impl Config { impl Config {
@@ -50,6 +58,7 @@ impl Config {
model, model,
approval_policy, approval_policy,
sandbox_policy, sandbox_policy,
disable_response_storage,
} = overrides; } = overrides;
if let Some(model) = model { if let Some(model) = model {
@@ -61,6 +70,9 @@ impl Config {
if let Some(sandbox_policy) = sandbox_policy { if let Some(sandbox_policy) = sandbox_policy {
cfg.sandbox_policy = sandbox_policy; cfg.sandbox_policy = sandbox_policy;
} }
if let Some(disable_response_storage) = disable_response_storage {
cfg.disable_response_storage = disable_response_storage;
}
Ok(cfg) Ok(cfg)
} }

View File

@@ -56,10 +56,14 @@ pub async fn run_main(cli: Cli) -> anyhow::Result<()> {
// the user for approval. // the user for approval.
approval_policy: Some(AskForApproval::Never), approval_policy: Some(AskForApproval::Never),
sandbox_policy: sandbox_policy.map(Into::into), sandbox_policy: sandbox_policy.map(Into::into),
disable_response_storage: if disable_response_storage {
Some(true)
} else {
None
},
}; };
let config = Config::load_with_overrides(overrides)?; let config = Config::load_with_overrides(overrides)?;
let (codex_wrapper, event, ctrl_c) = let (codex_wrapper, event, ctrl_c) = codex_wrapper::init_codex(config).await?;
codex_wrapper::init_codex(config, disable_response_storage).await?;
let codex = Arc::new(codex_wrapper); let codex = Arc::new(codex_wrapper);
info!("Codex initialized with event: {event:?}"); info!("Codex initialized with event: {event:?}");

View File

@@ -81,6 +81,11 @@ pub async fn run_main(cli: Cli) -> anyhow::Result<()> {
model: cli.model.clone(), model: cli.model.clone(),
approval_policy: cli.approval_policy.map(Into::into), approval_policy: cli.approval_policy.map(Into::into),
sandbox_policy: cli.sandbox_policy.map(Into::into), sandbox_policy: cli.sandbox_policy.map(Into::into),
disable_response_storage: if cli.disable_response_storage {
Some(true)
} else {
None
},
}; };
let config = Config::load_with_overrides(overrides)?; let config = Config::load_with_overrides(overrides)?;
@@ -104,7 +109,7 @@ async fn codex_main(cli: Cli, cfg: Config, ctrl_c: Arc<Notify>) -> anyhow::Resul
instructions: cfg.instructions, instructions: cfg.instructions,
approval_policy: cfg.approval_policy, approval_policy: cfg.approval_policy,
sandbox_policy: cfg.sandbox_policy, sandbox_policy: cfg.sandbox_policy,
disable_response_storage: cli.disable_response_storage, disable_response_storage: cfg.disable_response_storage,
}, },
}; };

View File

@@ -37,7 +37,6 @@ impl App<'_> {
initial_prompt: Option<String>, initial_prompt: Option<String>,
show_git_warning: bool, show_git_warning: bool,
initial_images: Vec<std::path::PathBuf>, initial_images: Vec<std::path::PathBuf>,
disable_response_storage: bool,
) -> Self { ) -> Self {
let (app_event_tx, app_event_rx) = channel(); let (app_event_tx, app_event_rx) = channel();
let scroll_event_helper = ScrollEventHelper::new(app_event_tx.clone()); let scroll_event_helper = ScrollEventHelper::new(app_event_tx.clone());
@@ -81,7 +80,6 @@ impl App<'_> {
app_event_tx.clone(), app_event_tx.clone(),
initial_prompt.clone(), initial_prompt.clone(),
initial_images, initial_images,
disable_response_storage,
); );
let app_state = if show_git_warning { let app_state = if show_git_warning {

View File

@@ -49,7 +49,6 @@ impl ChatWidget<'_> {
app_event_tx: Sender<AppEvent>, app_event_tx: Sender<AppEvent>,
initial_prompt: Option<String>, initial_prompt: Option<String>,
initial_images: Vec<std::path::PathBuf>, initial_images: Vec<std::path::PathBuf>,
disable_response_storage: bool,
) -> Self { ) -> Self {
let (codex_op_tx, mut codex_op_rx) = unbounded_channel::<Op>(); let (codex_op_tx, mut codex_op_rx) = unbounded_channel::<Op>();
@@ -62,15 +61,14 @@ impl ChatWidget<'_> {
// Create the Codex asynchronously so the UI loads as quickly as possible. // Create the Codex asynchronously so the UI loads as quickly as possible.
let config_for_agent_loop = config.clone(); let config_for_agent_loop = config.clone();
tokio::spawn(async move { tokio::spawn(async move {
let (codex, session_event, _ctrl_c) = let (codex, session_event, _ctrl_c) = match init_codex(config_for_agent_loop).await {
match init_codex(config_for_agent_loop, disable_response_storage).await { Ok(vals) => vals,
Ok(vals) => vals, Err(e) => {
Err(e) => { // TODO: surface this error to the user.
// TODO: surface this error to the user. tracing::error!("failed to initialize codex: {e}");
tracing::error!("failed to initialize codex: {e}"); return;
return; }
} };
};
// Forward the captured `SessionInitialized` event that was consumed // Forward the captured `SessionInitialized` event that was consumed
// inside `init_codex()` so it can be rendered in the UI. // inside `init_codex()` so it can be rendered in the UI.

View File

@@ -39,6 +39,11 @@ pub fn run_main(cli: Cli) -> std::io::Result<()> {
model: cli.model.clone(), model: cli.model.clone(),
approval_policy: cli.approval_policy.map(Into::into), approval_policy: cli.approval_policy.map(Into::into),
sandbox_policy: cli.sandbox_policy.map(Into::into), sandbox_policy: cli.sandbox_policy.map(Into::into),
disable_response_storage: if cli.disable_response_storage {
Some(true)
} else {
None
},
}; };
#[allow(clippy::print_stderr)] #[allow(clippy::print_stderr)]
match Config::load_with_overrides(overrides) { match Config::load_with_overrides(overrides) {
@@ -134,19 +139,8 @@ fn run_ratatui_app(
let mut terminal = tui::init()?; let mut terminal = tui::init()?;
terminal.clear()?; terminal.clear()?;
let Cli { let Cli { prompt, images, .. } = cli;
prompt, let mut app = App::new(config.clone(), prompt, show_git_warning, images);
images,
disable_response_storage,
..
} = cli;
let mut app = App::new(
config,
prompt,
show_git_warning,
images,
disable_response_storage,
);
// Bridge log receiver into the AppEvent channel so latest log lines update the UI. // Bridge log receiver into the AppEvent channel so latest log lines update the UI.
{ {