mirror of
https://github.com/openai/codex.git
synced 2026-02-01 22:47:52 +00:00
Compare commits
1 Commits
1271d450b1
...
admin-poli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
561a3ea1ba |
@@ -9,7 +9,7 @@ pub fn create_config_summary_entries(config: &Config) -> Vec<(&'static str, Stri
|
||||
("workdir", config.cwd.display().to_string()),
|
||||
("model", config.model.clone()),
|
||||
("provider", config.model_provider_id.clone()),
|
||||
("approval", config.approval_policy.to_string()),
|
||||
("approval", (*config.approval_policy).to_string()),
|
||||
("sandbox", summarize_sandbox_policy(&config.sandbox_policy)),
|
||||
];
|
||||
if config.model_provider.wire_api == WireApi::Responses
|
||||
|
||||
@@ -64,6 +64,7 @@ use crate::client_common::Prompt;
|
||||
use crate::client_common::ResponseEvent;
|
||||
use crate::compact::collect_user_messages;
|
||||
use crate::config::Config;
|
||||
use crate::config::Constrained;
|
||||
use crate::config::types::ShellEnvironmentPolicy;
|
||||
use crate::context_manager::ContextManager;
|
||||
use crate::environment_context::EnvironmentContext;
|
||||
@@ -185,7 +186,7 @@ impl Codex {
|
||||
user_instructions,
|
||||
base_instructions: config.base_instructions.clone(),
|
||||
compact_prompt: config.compact_prompt.clone(),
|
||||
approval_policy: config.approval_policy,
|
||||
approval_policy: config.approval_policy.clone(),
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
cwd: config.cwd.clone(),
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
@@ -330,7 +331,7 @@ pub(crate) struct SessionConfiguration {
|
||||
compact_prompt: Option<String>,
|
||||
|
||||
/// When to escalate for approval for execution
|
||||
approval_policy: AskForApproval,
|
||||
approval_policy: Constrained<AskForApproval>,
|
||||
/// How to sandbox commands executed in the system
|
||||
sandbox_policy: SandboxPolicy,
|
||||
|
||||
@@ -355,7 +356,7 @@ pub(crate) struct SessionConfiguration {
|
||||
}
|
||||
|
||||
impl SessionConfiguration {
|
||||
pub(crate) fn apply(&self, updates: &SessionSettingsUpdate) -> Self {
|
||||
pub(crate) fn apply(&self, updates: &SessionSettingsUpdate) -> std::io::Result<Self> {
|
||||
let mut next_configuration = self.clone();
|
||||
if let Some(model) = updates.model.clone() {
|
||||
next_configuration.model = model;
|
||||
@@ -367,7 +368,7 @@ impl SessionConfiguration {
|
||||
next_configuration.model_reasoning_summary = summary;
|
||||
}
|
||||
if let Some(approval_policy) = updates.approval_policy {
|
||||
next_configuration.approval_policy = approval_policy;
|
||||
next_configuration.approval_policy.set(approval_policy)?;
|
||||
}
|
||||
if let Some(sandbox_policy) = updates.sandbox_policy.clone() {
|
||||
next_configuration.sandbox_policy = sandbox_policy;
|
||||
@@ -375,7 +376,7 @@ impl SessionConfiguration {
|
||||
if let Some(cwd) = updates.cwd.clone() {
|
||||
next_configuration.cwd = cwd;
|
||||
}
|
||||
next_configuration
|
||||
Ok(next_configuration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +441,7 @@ impl Session {
|
||||
base_instructions: session_configuration.base_instructions.clone(),
|
||||
compact_prompt: session_configuration.compact_prompt.clone(),
|
||||
user_instructions: session_configuration.user_instructions.clone(),
|
||||
approval_policy: session_configuration.approval_policy,
|
||||
approval_policy: *session_configuration.approval_policy,
|
||||
sandbox_policy: session_configuration.sandbox_policy.clone(),
|
||||
shell_environment_policy: config.shell_environment_policy.clone(),
|
||||
tools_config,
|
||||
@@ -548,7 +549,7 @@ impl Session {
|
||||
config.model_reasoning_summary,
|
||||
config.model_context_window,
|
||||
config.model_auto_compact_token_limit,
|
||||
config.approval_policy,
|
||||
*config.approval_policy,
|
||||
config.sandbox_policy.clone(),
|
||||
config.mcp_servers.keys().map(String::as_str).collect(),
|
||||
config.active_profile.clone(),
|
||||
@@ -589,7 +590,7 @@ impl Session {
|
||||
session_id: conversation_id,
|
||||
model: session_configuration.model.clone(),
|
||||
model_provider_id: config.model_provider_id.clone(),
|
||||
approval_policy: session_configuration.approval_policy,
|
||||
approval_policy: *session_configuration.approval_policy,
|
||||
sandbox_policy: session_configuration.sandbox_policy.clone(),
|
||||
cwd: session_configuration.cwd.clone(),
|
||||
reasoning_effort: session_configuration.model_reasoning_effort,
|
||||
@@ -730,7 +731,12 @@ impl Session {
|
||||
pub(crate) async fn update_settings(&self, updates: SessionSettingsUpdate) {
|
||||
let mut state = self.state.lock().await;
|
||||
|
||||
state.session_configuration = state.session_configuration.apply(&updates);
|
||||
match state.session_configuration.apply(&updates) {
|
||||
Ok(updated) => state.session_configuration = updated,
|
||||
Err(err) => {
|
||||
warn!(%err, "rejected session settings update");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn new_turn(&self, updates: SessionSettingsUpdate) -> Arc<TurnContext> {
|
||||
@@ -745,9 +751,16 @@ impl Session {
|
||||
) -> Arc<TurnContext> {
|
||||
let session_configuration = {
|
||||
let mut state = self.state.lock().await;
|
||||
let session_configuration = state.session_configuration.clone().apply(&updates);
|
||||
state.session_configuration = session_configuration.clone();
|
||||
session_configuration
|
||||
match state.session_configuration.clone().apply(&updates) {
|
||||
Ok(next) => {
|
||||
state.session_configuration = next.clone();
|
||||
next
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(%err, "rejected session settings update");
|
||||
state.session_configuration.clone()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut turn_context: TurnContext = Self::make_turn_context(
|
||||
@@ -2571,7 +2584,7 @@ mod tests {
|
||||
user_instructions: config.user_instructions.clone(),
|
||||
base_instructions: config.base_instructions.clone(),
|
||||
compact_prompt: config.compact_prompt.clone(),
|
||||
approval_policy: config.approval_policy,
|
||||
approval_policy: config.approval_policy.clone(),
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
cwd: config.cwd.clone(),
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
@@ -2770,7 +2783,7 @@ mod tests {
|
||||
user_instructions: config.user_instructions.clone(),
|
||||
base_instructions: config.base_instructions.clone(),
|
||||
compact_prompt: config.compact_prompt.clone(),
|
||||
approval_policy: config.approval_policy,
|
||||
approval_policy: config.approval_policy.clone(),
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
cwd: config.cwd.clone(),
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
@@ -2848,7 +2861,7 @@ mod tests {
|
||||
user_instructions: config.user_instructions.clone(),
|
||||
base_instructions: config.base_instructions.clone(),
|
||||
compact_prompt: config.compact_prompt.clone(),
|
||||
approval_policy: config.approval_policy,
|
||||
approval_policy: config.approval_policy.clone(),
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
cwd: config.cwd.clone(),
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
|
||||
@@ -50,9 +50,11 @@ use serde::Deserialize;
|
||||
use similar::DiffableStr;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::io::ErrorKind;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::config::profile::ConfigProfile;
|
||||
use toml::Value as TomlValue;
|
||||
@@ -73,6 +75,75 @@ pub(crate) const PROJECT_DOC_MAX_BYTES: usize = 32 * 1024; // 32 KiB
|
||||
|
||||
pub const CONFIG_TOML_FILE: &str = "config.toml";
|
||||
|
||||
type ConstraintValidator<T> = dyn Fn(&T) -> std::io::Result<()> + Send + Sync;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Constrained<T> {
|
||||
value: T,
|
||||
validator: Arc<ConstraintValidator<T>>,
|
||||
}
|
||||
|
||||
impl<T> Constrained<T> {
|
||||
pub fn new(
|
||||
initial_value: T,
|
||||
validator: impl Fn(&T) -> std::io::Result<()> + Send + Sync + 'static,
|
||||
) -> std::io::Result<Self> {
|
||||
let validator: Arc<ConstraintValidator<T>> = Arc::new(validator);
|
||||
validator(&initial_value)?;
|
||||
Ok(Self {
|
||||
value: initial_value,
|
||||
validator,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn allow_any(initial_value: T) -> Self {
|
||||
Self {
|
||||
value: initial_value,
|
||||
validator: Arc::new(|_| Ok(())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_set(&self, candidate: &T) -> std::io::Result<()> {
|
||||
(self.validator)(candidate)
|
||||
}
|
||||
|
||||
pub fn set(&mut self, value: T) -> std::io::Result<()> {
|
||||
(self.validator)(&value)?;
|
||||
self.value = value;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn as_ref(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for Constrained<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for Constrained<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Constrained")
|
||||
.field("value", &self.value)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> PartialEq for Constrained<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.value == other.value
|
||||
}
|
||||
}
|
||||
|
||||
/// Application configuration loaded from disk and merged with overrides.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Config {
|
||||
@@ -97,7 +168,7 @@ pub struct Config {
|
||||
pub model_provider: ModelProviderInfo,
|
||||
|
||||
/// Approval policy for executing commands.
|
||||
pub approval_policy: AskForApproval,
|
||||
pub approval_policy: Constrained<AskForApproval>,
|
||||
|
||||
pub sandbox_policy: SandboxPolicy,
|
||||
|
||||
@@ -1053,6 +1124,7 @@ impl Config {
|
||||
AskForApproval::default()
|
||||
}
|
||||
});
|
||||
let approval_policy = Constrained::allow_any(approval_policy);
|
||||
let did_user_set_custom_approval_policy_or_sandbox_mode = approval_policy_override
|
||||
.is_some()
|
||||
|| config_profile.approval_policy.is_some()
|
||||
@@ -1387,6 +1459,74 @@ mod tests {
|
||||
use std::time::Duration;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn constrained_allow_any_accepts_any_value() {
|
||||
let mut constrained = Constrained::allow_any(5);
|
||||
constrained.set(-10).expect("allow any accepts all values");
|
||||
assert_eq!(*constrained, -10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constrained_new_rejects_invalid_initial_value() {
|
||||
let result = Constrained::new(0, |value| {
|
||||
if *value > 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"must be positive",
|
||||
))
|
||||
}
|
||||
});
|
||||
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constrained_set_rejects_invalid_value_and_leaves_previous() {
|
||||
let mut constrained = Constrained::new(1, |value| {
|
||||
if *value > 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"must be positive",
|
||||
))
|
||||
}
|
||||
})
|
||||
.expect("initial value should be accepted");
|
||||
|
||||
let err = constrained
|
||||
.set(-5)
|
||||
.expect_err("negative values should be rejected");
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(*constrained, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constrained_can_set_allows_probe_without_setting() {
|
||||
let mut constrained = Constrained::new(1, |value| {
|
||||
if *value > 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"must be positive",
|
||||
))
|
||||
}
|
||||
})
|
||||
.expect("initial value should be accepted");
|
||||
|
||||
constrained
|
||||
.can_set(&2)
|
||||
.expect("can_set should accept positive value");
|
||||
let err = constrained
|
||||
.can_set(&-1)
|
||||
.expect_err("can_set should reject negative value");
|
||||
assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput);
|
||||
assert_eq!(*constrained, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_toml_parsing() {
|
||||
let history_with_persistence = r#"
|
||||
@@ -2960,7 +3100,7 @@ model_verbosity = "high"
|
||||
model_auto_compact_token_limit: Some(180_000),
|
||||
model_provider_id: "openai".to_string(),
|
||||
model_provider: fixture.openai_provider.clone(),
|
||||
approval_policy: AskForApproval::Never,
|
||||
approval_policy: Constrained::allow_any(AskForApproval::Never),
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
did_user_set_custom_approval_policy_or_sandbox_mode: true,
|
||||
forced_auto_mode_downgraded_on_windows: false,
|
||||
@@ -3034,7 +3174,7 @@ model_verbosity = "high"
|
||||
model_auto_compact_token_limit: Some(14_746),
|
||||
model_provider_id: "openai-chat-completions".to_string(),
|
||||
model_provider: fixture.openai_chat_completions_provider.clone(),
|
||||
approval_policy: AskForApproval::UnlessTrusted,
|
||||
approval_policy: Constrained::allow_any(AskForApproval::UnlessTrusted),
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
did_user_set_custom_approval_policy_or_sandbox_mode: true,
|
||||
forced_auto_mode_downgraded_on_windows: false,
|
||||
@@ -3123,7 +3263,7 @@ model_verbosity = "high"
|
||||
model_auto_compact_token_limit: Some(180_000),
|
||||
model_provider_id: "openai".to_string(),
|
||||
model_provider: fixture.openai_provider.clone(),
|
||||
approval_policy: AskForApproval::OnFailure,
|
||||
approval_policy: Constrained::allow_any(AskForApproval::OnFailure),
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
did_user_set_custom_approval_policy_or_sandbox_mode: true,
|
||||
forced_auto_mode_downgraded_on_windows: false,
|
||||
@@ -3198,7 +3338,7 @@ model_verbosity = "high"
|
||||
model_auto_compact_token_limit: Some(244_800),
|
||||
model_provider_id: "openai".to_string(),
|
||||
model_provider: fixture.openai_provider.clone(),
|
||||
approval_policy: AskForApproval::OnFailure,
|
||||
approval_policy: Constrained::allow_any(AskForApproval::OnFailure),
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
did_user_set_custom_approval_policy_or_sandbox_mode: true,
|
||||
forced_auto_mode_downgraded_on_windows: false,
|
||||
@@ -3539,7 +3679,7 @@ trust_level = "untrusted"
|
||||
|
||||
// Verify that untrusted projects get UnlessTrusted approval policy
|
||||
assert_eq!(
|
||||
config.approval_policy,
|
||||
*config.approval_policy,
|
||||
AskForApproval::UnlessTrusted,
|
||||
"Expected UnlessTrusted approval policy for untrusted project"
|
||||
);
|
||||
|
||||
@@ -1460,7 +1460,10 @@ async fn run_scenario(scenario: &ScenarioSpec) -> Result<()> {
|
||||
let model = model_override.unwrap_or("gpt-5.1");
|
||||
|
||||
let mut builder = test_codex().with_model(model).with_config(move |config| {
|
||||
config.approval_policy = approval_policy;
|
||||
config
|
||||
.approval_policy
|
||||
.set(approval_policy)
|
||||
.expect("set approval policy");
|
||||
config.sandbox_policy = sandbox_policy.clone();
|
||||
for feature in features {
|
||||
config.features.enable(feature);
|
||||
|
||||
@@ -60,7 +60,10 @@ async fn codex_delegate_forwards_exec_approval_and_proceeds_on_approval() {
|
||||
// Build a conversation configured to require approvals so the delegate
|
||||
// routes ExecApprovalRequest via the parent.
|
||||
let mut builder = test_codex().with_model("gpt-5.1").with_config(|config| {
|
||||
config.approval_policy = AskForApproval::OnRequest;
|
||||
config
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
config.sandbox_policy = SandboxPolicy::ReadOnly;
|
||||
});
|
||||
let test = builder.build(&server).await.expect("build test codex");
|
||||
@@ -136,7 +139,10 @@ async fn codex_delegate_forwards_patch_approval_and_proceeds_on_decision() {
|
||||
mount_sse_sequence(&server, vec![sse1, sse2]).await;
|
||||
|
||||
let mut builder = test_codex().with_model("gpt-5.1").with_config(|config| {
|
||||
config.approval_policy = AskForApproval::OnRequest;
|
||||
config
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
// Use a restricted sandbox so patch approval is required
|
||||
config.sandbox_policy = SandboxPolicy::ReadOnly;
|
||||
config.include_apply_patch_tool = true;
|
||||
|
||||
@@ -773,7 +773,10 @@ async fn handle_container_exec_autoapprove_from_config_records_tool_decision() {
|
||||
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.approval_policy = AskForApproval::OnRequest;
|
||||
config
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
config.sandbox_policy = SandboxPolicy::DangerFullAccess;
|
||||
})
|
||||
.build(&server)
|
||||
@@ -822,7 +825,10 @@ async fn handle_container_exec_user_approved_records_tool_decision() {
|
||||
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.approval_policy = AskForApproval::UnlessTrusted;
|
||||
config
|
||||
.approval_policy
|
||||
.set(AskForApproval::UnlessTrusted)
|
||||
.expect("set approval policy");
|
||||
})
|
||||
.build(&server)
|
||||
.await
|
||||
@@ -880,7 +886,10 @@ async fn handle_container_exec_user_approved_for_session_records_tool_decision()
|
||||
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.approval_policy = AskForApproval::UnlessTrusted;
|
||||
config
|
||||
.approval_policy
|
||||
.set(AskForApproval::UnlessTrusted)
|
||||
.expect("set approval policy");
|
||||
})
|
||||
.build(&server)
|
||||
.await
|
||||
@@ -938,7 +947,10 @@ async fn handle_sandbox_error_user_approves_retry_records_tool_decision() {
|
||||
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.approval_policy = AskForApproval::UnlessTrusted;
|
||||
config
|
||||
.approval_policy
|
||||
.set(AskForApproval::UnlessTrusted)
|
||||
.expect("set approval policy");
|
||||
})
|
||||
.build(&server)
|
||||
.await
|
||||
@@ -996,7 +1008,10 @@ async fn handle_container_exec_user_denies_records_tool_decision() {
|
||||
.await;
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.approval_policy = AskForApproval::UnlessTrusted;
|
||||
config
|
||||
.approval_policy
|
||||
.set(AskForApproval::UnlessTrusted)
|
||||
.expect("set approval policy");
|
||||
})
|
||||
.build(&server)
|
||||
.await
|
||||
@@ -1054,7 +1069,10 @@ async fn handle_sandbox_error_user_approves_for_session_records_tool_decision()
|
||||
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.approval_policy = AskForApproval::UnlessTrusted;
|
||||
config
|
||||
.approval_policy
|
||||
.set(AskForApproval::UnlessTrusted)
|
||||
.expect("set approval policy");
|
||||
})
|
||||
.build(&server)
|
||||
.await
|
||||
@@ -1113,7 +1131,10 @@ async fn handle_sandbox_error_user_denies_records_tool_decision() {
|
||||
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.approval_policy = AskForApproval::UnlessTrusted;
|
||||
config
|
||||
.approval_policy
|
||||
.set(AskForApproval::UnlessTrusted)
|
||||
.expect("set approval policy");
|
||||
})
|
||||
.build(&server)
|
||||
.await
|
||||
|
||||
@@ -573,7 +573,7 @@ async fn send_user_turn_with_no_changes_does_not_send_environment_context() -> a
|
||||
.await?;
|
||||
|
||||
let default_cwd = config.cwd.clone();
|
||||
let default_approval_policy = config.approval_policy;
|
||||
let default_approval_policy = *config.approval_policy;
|
||||
let default_sandbox_policy = config.sandbox_policy.clone();
|
||||
let default_model = config.model.clone();
|
||||
let default_effort = config.model_reasoning_effort;
|
||||
@@ -660,7 +660,7 @@ async fn send_user_turn_with_changes_sends_environment_context() -> anyhow::Resu
|
||||
.await?;
|
||||
|
||||
let default_cwd = config.cwd.clone();
|
||||
let default_approval_policy = config.approval_policy;
|
||||
let default_approval_policy = *config.approval_policy;
|
||||
let default_sandbox_policy = config.sandbox_policy.clone();
|
||||
let default_model = config.model.clone();
|
||||
let default_effort = config.model_reasoning_effort;
|
||||
|
||||
@@ -19,7 +19,7 @@ use tempfile::TempDir;
|
||||
fn resume_history(config: &codex_core::config::Config, previous_model: &str, rollout_path: &std::path::Path) -> InitialHistory {
|
||||
let turn_ctx = TurnContextItem {
|
||||
cwd: config.cwd.clone(),
|
||||
approval_policy: config.approval_policy,
|
||||
approval_policy: *config.approval_policy,
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
model: previous_model.to_string(),
|
||||
effort: config.model_reasoning_effort,
|
||||
|
||||
@@ -262,7 +262,7 @@ pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>) -> any
|
||||
}
|
||||
|
||||
let default_cwd = config.cwd.to_path_buf();
|
||||
let default_approval_policy = config.approval_policy;
|
||||
let default_approval_policy = *config.approval_policy;
|
||||
let default_sandbox_policy = config.sandbox_policy.clone();
|
||||
let default_model = config.model.clone();
|
||||
let default_effort = config.model_reasoning_effort;
|
||||
|
||||
@@ -2357,7 +2357,7 @@ impl ChatWidget {
|
||||
|
||||
/// Open a popup to choose the approvals mode (ask for approval policy + sandbox policy).
|
||||
pub(crate) fn open_approvals_popup(&mut self) {
|
||||
let current_approval = self.config.approval_policy;
|
||||
let current_approval = *self.config.approval_policy;
|
||||
let current_sandbox = self.config.sandbox_policy.clone();
|
||||
let mut items: Vec<SelectionItem> = Vec::new();
|
||||
let presets: Vec<ApprovalPreset> = builtin_approval_presets();
|
||||
@@ -2755,7 +2755,9 @@ impl ChatWidget {
|
||||
|
||||
/// Set the approval policy in the widget's config copy.
|
||||
pub(crate) fn set_approval_policy(&mut self, policy: AskForApproval) {
|
||||
self.config.approval_policy = policy;
|
||||
if let Err(err) = self.config.approval_policy.set(policy) {
|
||||
tracing::warn!(%err, "failed to set approval_policy on chat config");
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the sandbox policy in the widget's config copy.
|
||||
|
||||
@@ -1959,7 +1959,10 @@ fn approval_modal_exec_snapshot() {
|
||||
// Build a chat widget with manual channels to avoid spawning the agent.
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
// Ensure policy allows surfacing approvals explicitly (not strictly required for direct event).
|
||||
chat.config.approval_policy = AskForApproval::OnRequest;
|
||||
chat.config
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
// Inject an exec approval request to display the approval modal.
|
||||
let ev = ExecApprovalRequestEvent {
|
||||
call_id: "call-approve-cmd".into(),
|
||||
@@ -2007,7 +2010,10 @@ fn approval_modal_exec_snapshot() {
|
||||
#[test]
|
||||
fn approval_modal_exec_without_reason_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
chat.config.approval_policy = AskForApproval::OnRequest;
|
||||
chat.config
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
|
||||
let ev = ExecApprovalRequestEvent {
|
||||
call_id: "call-approve-cmd-noreason".into(),
|
||||
@@ -2040,7 +2046,10 @@ fn approval_modal_exec_without_reason_snapshot() {
|
||||
#[test]
|
||||
fn approval_modal_patch_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
chat.config.approval_policy = AskForApproval::OnRequest;
|
||||
chat.config
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
|
||||
// Build a small changeset and a reason/grant_root to exercise the prompt text.
|
||||
let mut changes = HashMap::new();
|
||||
@@ -2612,7 +2621,10 @@ fn apply_patch_full_flow_integration_like() {
|
||||
fn apply_patch_untrusted_shows_approval_modal() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual();
|
||||
// Ensure approval policy is untrusted (OnRequest)
|
||||
chat.config.approval_policy = AskForApproval::OnRequest;
|
||||
chat.config
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
|
||||
// Simulate a patch approval request from backend
|
||||
let mut changes = HashMap::new();
|
||||
@@ -2658,7 +2670,10 @@ fn apply_patch_request_shows_diff_summary() {
|
||||
let (mut chat, mut rx, _op_rx) = make_chatwidget_manual();
|
||||
|
||||
// Ensure we are in OnRequest so an approval is surfaced
|
||||
chat.config.approval_policy = AskForApproval::OnRequest;
|
||||
chat.config
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
|
||||
// Simulate backend asking to apply a patch adding two lines to README.md
|
||||
let mut changes = HashMap::new();
|
||||
|
||||
Reference in New Issue
Block a user