mirror of
https://github.com/openai/codex.git
synced 2026-11-14 06:48:26 +00:00
Reuse guardian session across approvals (#14668)
## Summary - reuse a guardian subagent session across approvals so reviews keep a stable prompt cache key and avoid one-shot startup overhead - clear the guardian child history before each review so prior guardian decisions do not leak into later approvals - include the `smart_approvals` -> `guardian_approval` feature flag rename in the same PR to minimize release latency on a very tight timeline - add regression coverage for prompt-cache-key reuse without prior-review prompt bleed ## Request - Bug/enhancement request: internal guardian prompt-cache and latency improvement request --------- Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
1 parent
ba463a9dc7
commit
6fdeb1d602
26 files changed
+3132
-1504
No files matched your search
+67
-62
@@ -129,18 +129,19 @@ enum ThreadInteractiveRequest {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct SmartApprovalsMode {
|
||||
struct GuardianApprovalsMode {
|
||||
approval_policy: AskForApproval,
|
||||
approvals_reviewer: ApprovalsReviewer,
|
||||
sandbox_policy: SandboxPolicy,
|
||||
}
|
||||
|
||||
/// Enabling the Smart Approvals experiment in the TUI should also switch the
|
||||
/// current `/approvals` settings to the matching Smart Approvals mode. Users
|
||||
/// Enabling the Guardian Approvals experiment in the TUI should also switch
|
||||
/// the current `/approvals` settings to the matching Guardian Approvals mode.
|
||||
/// Users
|
||||
/// can still change `/approvals` afterward; this just assumes that opting into
|
||||
/// the experiment means they want guardian review enabled immediately.
|
||||
fn smart_approvals_mode() -> SmartApprovalsMode {
|
||||
SmartApprovalsMode {
|
||||
fn guardian_approvals_mode() -> GuardianApprovalsMode {
|
||||
GuardianApprovalsMode {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
approvals_reviewer: ApprovalsReviewer::GuardianSubagent,
|
||||
sandbox_policy: SandboxPolicy::new_workspace_write_policy(),
|
||||
@@ -896,7 +897,7 @@ impl App {
|
||||
return;
|
||||
}
|
||||
|
||||
let smart_approvals_mode = smart_approvals_mode();
|
||||
let guardian_approvals_preset = guardian_approvals_mode();
|
||||
let mut next_config = self.config.clone();
|
||||
let active_profile = self.active_profile.clone();
|
||||
let scoped_segments = |key: &str| {
|
||||
@@ -916,9 +917,9 @@ impl App {
|
||||
let mut approvals_reviewer_override = None;
|
||||
let mut sandbox_policy_override = None;
|
||||
let mut feature_updates_to_apply = Vec::with_capacity(updates.len());
|
||||
// Smart Approvals owns `approvals_reviewer`, but disabling the feature
|
||||
// from inside a profile should not silently clear a value configured at
|
||||
// the root scope.
|
||||
// Guardian Approvals owns `approvals_reviewer`, but disabling the
|
||||
// feature from inside a profile should not silently clear a value
|
||||
// configured at the root scope.
|
||||
let (root_approvals_reviewer_blocks_profile_disable, profile_approvals_reviewer_configured) = {
|
||||
let effective_config = next_config.config_layer_stack.effective_config();
|
||||
let root_blocks_disable = effective_config
|
||||
@@ -949,7 +950,7 @@ impl App {
|
||||
&& root_approvals_reviewer_blocks_profile_disable
|
||||
{
|
||||
self.chat_widget.add_error_message(
|
||||
"Cannot disable Smart Approvals in this profile because `approvals_reviewer` is configured outside the active profile.".to_string(),
|
||||
"Cannot disable Guardian Approvals in this profile because `approvals_reviewer` is configured outside the active profile.".to_string(),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
@@ -972,13 +973,17 @@ impl App {
|
||||
// Persist the reviewer setting so future sessions keep the
|
||||
// experiment's matching `/approvals` mode until the user
|
||||
// changes it explicitly.
|
||||
feature_config.approvals_reviewer = smart_approvals_mode.approvals_reviewer;
|
||||
feature_config.approvals_reviewer =
|
||||
guardian_approvals_preset.approvals_reviewer;
|
||||
feature_edits.push(ConfigEdit::SetPath {
|
||||
segments: scoped_segments("approvals_reviewer"),
|
||||
value: smart_approvals_mode.approvals_reviewer.to_string().into(),
|
||||
value: guardian_approvals_preset
|
||||
.approvals_reviewer
|
||||
.to_string()
|
||||
.into(),
|
||||
});
|
||||
if previous_approvals_reviewer != smart_approvals_mode.approvals_reviewer {
|
||||
permissions_history_label = Some("Smart Approvals");
|
||||
if previous_approvals_reviewer != guardian_approvals_preset.approvals_reviewer {
|
||||
permissions_history_label = Some("Guardian Approvals");
|
||||
}
|
||||
} else if !effective_enabled {
|
||||
if profile_approvals_reviewer_configured || self.active_profile.is_none() {
|
||||
@@ -995,22 +1000,22 @@ impl App {
|
||||
}
|
||||
if feature == Feature::GuardianApproval && effective_enabled {
|
||||
// The feature flag alone is not enough for the live session.
|
||||
// We also align approval policy + sandbox to the Smart
|
||||
// Approvals preset so enabling the experiment immediately makes
|
||||
// guardian review observable in the current thread.
|
||||
// We also align approval policy + sandbox to the Guardian
|
||||
// Approvals preset so enabling the experiment immediately
|
||||
// makes guardian review observable in the current thread.
|
||||
if !self.try_set_approval_policy_on_config(
|
||||
&mut feature_config,
|
||||
smart_approvals_mode.approval_policy,
|
||||
"Failed to enable Smart Approvals",
|
||||
"failed to set smart approvals approval policy on staged config",
|
||||
guardian_approvals_preset.approval_policy,
|
||||
"Failed to enable Guardian Approvals",
|
||||
"failed to set guardian approvals approval policy on staged config",
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if !self.try_set_sandbox_policy_on_config(
|
||||
&mut feature_config,
|
||||
smart_approvals_mode.sandbox_policy.clone(),
|
||||
"Failed to enable Smart Approvals",
|
||||
"failed to set smart approvals sandbox policy on staged config",
|
||||
guardian_approvals_preset.sandbox_policy.clone(),
|
||||
"Failed to enable Guardian Approvals",
|
||||
"failed to set guardian approvals sandbox policy on staged config",
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -1024,8 +1029,8 @@ impl App {
|
||||
value: "workspace-write".into(),
|
||||
},
|
||||
]);
|
||||
approval_policy_override = Some(smart_approvals_mode.approval_policy);
|
||||
sandbox_policy_override = Some(smart_approvals_mode.sandbox_policy.clone());
|
||||
approval_policy_override = Some(guardian_approvals_preset.approval_policy);
|
||||
sandbox_policy_override = Some(guardian_approvals_preset.sandbox_policy.clone());
|
||||
}
|
||||
next_config = feature_config;
|
||||
feature_updates_to_apply.push((feature, effective_enabled));
|
||||
@@ -1063,10 +1068,10 @@ impl App {
|
||||
{
|
||||
tracing::error!(
|
||||
error = %err,
|
||||
"failed to set smart approvals sandbox policy on chat config"
|
||||
"failed to set guardian approvals sandbox policy on chat config"
|
||||
);
|
||||
self.chat_widget
|
||||
.add_error_message(format!("Failed to enable Smart Approvals: {err}"));
|
||||
.add_error_message(format!("Failed to enable Guardian Approvals: {err}"));
|
||||
}
|
||||
|
||||
if approval_policy_override.is_some()
|
||||
@@ -5376,11 +5381,11 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn update_feature_flags_enabling_guardian_selects_smart_approvals() -> Result<()> {
|
||||
async fn update_feature_flags_enabling_guardian_selects_guardian_approvals() -> Result<()> {
|
||||
let (mut app, mut app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
let smart_approvals = smart_approvals_mode();
|
||||
let guardian_approvals = guardian_approvals_mode();
|
||||
|
||||
app.update_feature_flags(vec![(Feature::GuardianApproval, true)])
|
||||
.await;
|
||||
@@ -5394,11 +5399,11 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
app.config.approvals_reviewer,
|
||||
smart_approvals.approvals_reviewer
|
||||
guardian_approvals.approvals_reviewer
|
||||
);
|
||||
assert_eq!(
|
||||
app.config.permissions.approval_policy.value(),
|
||||
smart_approvals.approval_policy
|
||||
guardian_approvals.approval_policy
|
||||
);
|
||||
assert_eq!(
|
||||
app.chat_widget
|
||||
@@ -5406,7 +5411,7 @@ mod tests {
|
||||
.permissions
|
||||
.approval_policy
|
||||
.value(),
|
||||
smart_approvals.approval_policy
|
||||
guardian_approvals.approval_policy
|
||||
);
|
||||
assert_eq!(
|
||||
app.chat_widget
|
||||
@@ -5414,11 +5419,11 @@ mod tests {
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.get(),
|
||||
&smart_approvals.sandbox_policy
|
||||
&guardian_approvals.sandbox_policy
|
||||
);
|
||||
assert_eq!(
|
||||
app.chat_widget.config_ref().approvals_reviewer,
|
||||
smart_approvals.approvals_reviewer
|
||||
guardian_approvals.approvals_reviewer
|
||||
);
|
||||
assert_eq!(app.runtime_approval_policy_override, None);
|
||||
assert_eq!(app.runtime_sandbox_policy_override, None);
|
||||
@@ -5426,9 +5431,9 @@ mod tests {
|
||||
op_rx.try_recv(),
|
||||
Ok(Op::OverrideTurnContext {
|
||||
cwd: None,
|
||||
approval_policy: Some(smart_approvals.approval_policy),
|
||||
approvals_reviewer: Some(smart_approvals.approvals_reviewer),
|
||||
sandbox_policy: Some(smart_approvals.sandbox_policy.clone()),
|
||||
approval_policy: Some(guardian_approvals.approval_policy),
|
||||
approvals_reviewer: Some(guardian_approvals.approvals_reviewer),
|
||||
sandbox_policy: Some(guardian_approvals.sandbox_policy.clone()),
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
@@ -5448,10 +5453,10 @@ mod tests {
|
||||
.map(|line| line.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
assert!(rendered.contains("Permissions updated to Smart Approvals"));
|
||||
assert!(rendered.contains("Permissions updated to Guardian Approvals"));
|
||||
|
||||
let config = std::fs::read_to_string(codex_home.path().join("config.toml"))?;
|
||||
assert!(config.contains("smart_approvals = true"));
|
||||
assert!(config.contains("guardian_approval = true"));
|
||||
assert!(config.contains("approvals_reviewer = \"guardian_subagent\""));
|
||||
assert!(config.contains("approval_policy = \"on-request\""));
|
||||
assert!(config.contains("sandbox_mode = \"workspace-write\""));
|
||||
@@ -5465,7 +5470,7 @@ mod tests {
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml = "approvals_reviewer = \"guardian_subagent\"\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\n\n[features]\nsmart_approvals = true\n";
|
||||
let config_toml = "approvals_reviewer = \"guardian_subagent\"\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\n\n[features]\nguardian_approval = true\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
let user_config = toml::from_str::<TomlValue>(config_toml)?;
|
||||
app.config.config_layer_stack = app
|
||||
@@ -5542,7 +5547,7 @@ mod tests {
|
||||
assert!(rendered.contains("Permissions updated to Default"));
|
||||
|
||||
let config = std::fs::read_to_string(codex_home.path().join("config.toml"))?;
|
||||
assert!(!config.contains("smart_approvals = true"));
|
||||
assert!(!config.contains("guardian_approval = true"));
|
||||
assert!(!config.contains("approvals_reviewer ="));
|
||||
assert!(config.contains("approval_policy = \"on-request\""));
|
||||
assert!(config.contains("sandbox_mode = \"workspace-write\""));
|
||||
@@ -5555,7 +5560,7 @@ mod tests {
|
||||
let (mut app, _app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
let smart_approvals = smart_approvals_mode();
|
||||
let guardian_approvals = guardian_approvals_mode();
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml = "approvals_reviewer = \"user\"\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
@@ -5574,15 +5579,15 @@ mod tests {
|
||||
assert!(app.config.features.enabled(Feature::GuardianApproval));
|
||||
assert_eq!(
|
||||
app.config.approvals_reviewer,
|
||||
smart_approvals.approvals_reviewer
|
||||
guardian_approvals.approvals_reviewer
|
||||
);
|
||||
assert_eq!(
|
||||
app.chat_widget.config_ref().approvals_reviewer,
|
||||
smart_approvals.approvals_reviewer
|
||||
guardian_approvals.approvals_reviewer
|
||||
);
|
||||
assert_eq!(
|
||||
app.config.permissions.approval_policy.value(),
|
||||
smart_approvals.approval_policy
|
||||
guardian_approvals.approval_policy
|
||||
);
|
||||
assert_eq!(
|
||||
app.chat_widget
|
||||
@@ -5590,15 +5595,15 @@ mod tests {
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.get(),
|
||||
&smart_approvals.sandbox_policy
|
||||
&guardian_approvals.sandbox_policy
|
||||
);
|
||||
assert_eq!(
|
||||
op_rx.try_recv(),
|
||||
Ok(Op::OverrideTurnContext {
|
||||
cwd: None,
|
||||
approval_policy: Some(smart_approvals.approval_policy),
|
||||
approvals_reviewer: Some(smart_approvals.approvals_reviewer),
|
||||
sandbox_policy: Some(smart_approvals.sandbox_policy.clone()),
|
||||
approval_policy: Some(guardian_approvals.approval_policy),
|
||||
approvals_reviewer: Some(guardian_approvals.approvals_reviewer),
|
||||
sandbox_policy: Some(guardian_approvals.sandbox_policy.clone()),
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
@@ -5611,7 +5616,7 @@ mod tests {
|
||||
|
||||
let config = std::fs::read_to_string(codex_home.path().join("config.toml"))?;
|
||||
assert!(config.contains("approvals_reviewer = \"guardian_subagent\""));
|
||||
assert!(config.contains("smart_approvals = true"));
|
||||
assert!(config.contains("guardian_approval = true"));
|
||||
assert!(config.contains("approval_policy = \"on-request\""));
|
||||
assert!(config.contains("sandbox_mode = \"workspace-write\""));
|
||||
Ok(())
|
||||
@@ -5624,7 +5629,7 @@ mod tests {
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml = "approvals_reviewer = \"user\"\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\n\n[features]\nsmart_approvals = true\n";
|
||||
let config_toml = "approvals_reviewer = \"user\"\napproval_policy = \"on-request\"\nsandbox_mode = \"workspace-write\"\n\n[features]\nguardian_approval = true\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
let user_config = toml::from_str::<TomlValue>(config_toml)?;
|
||||
app.config.config_layer_stack = app
|
||||
@@ -5671,7 +5676,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let config = std::fs::read_to_string(codex_home.path().join("config.toml"))?;
|
||||
assert!(!config.contains("smart_approvals = true"));
|
||||
assert!(!config.contains("guardian_approval = true"));
|
||||
assert!(!config.contains("approvals_reviewer ="));
|
||||
Ok(())
|
||||
}
|
||||
@@ -5682,7 +5687,7 @@ mod tests {
|
||||
let (mut app, _app_event_rx, mut op_rx) = make_test_app_with_channels().await;
|
||||
let codex_home = tempdir()?;
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
let smart_approvals = smart_approvals_mode();
|
||||
let guardian_approvals = guardian_approvals_mode();
|
||||
app.active_profile = Some("guardian".to_string());
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml = "profile = \"guardian\"\napprovals_reviewer = \"user\"\n";
|
||||
@@ -5702,19 +5707,19 @@ mod tests {
|
||||
assert!(app.config.features.enabled(Feature::GuardianApproval));
|
||||
assert_eq!(
|
||||
app.config.approvals_reviewer,
|
||||
smart_approvals.approvals_reviewer
|
||||
guardian_approvals.approvals_reviewer
|
||||
);
|
||||
assert_eq!(
|
||||
app.chat_widget.config_ref().approvals_reviewer,
|
||||
smart_approvals.approvals_reviewer
|
||||
guardian_approvals.approvals_reviewer
|
||||
);
|
||||
assert_eq!(
|
||||
op_rx.try_recv(),
|
||||
Ok(Op::OverrideTurnContext {
|
||||
cwd: None,
|
||||
approval_policy: Some(smart_approvals.approval_policy),
|
||||
approvals_reviewer: Some(smart_approvals.approvals_reviewer),
|
||||
sandbox_policy: Some(smart_approvals.sandbox_policy.clone()),
|
||||
approval_policy: Some(guardian_approvals.approval_policy),
|
||||
approvals_reviewer: Some(guardian_approvals.approvals_reviewer),
|
||||
sandbox_policy: Some(guardian_approvals.sandbox_policy.clone()),
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
@@ -5763,7 +5768,7 @@ approvals_reviewer = "user"
|
||||
approvals_reviewer = "guardian_subagent"
|
||||
|
||||
[profiles.guardian.features]
|
||||
smart_approvals = true
|
||||
guardian_approval = true
|
||||
"#;
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
let user_config = toml::from_str::<TomlValue>(config_toml)?;
|
||||
@@ -5824,7 +5829,7 @@ smart_approvals = true
|
||||
assert!(rendered.contains("Permissions updated to Default"));
|
||||
|
||||
let config = std::fs::read_to_string(codex_home.path().join("config.toml"))?;
|
||||
assert!(!config.contains("smart_approvals = true"));
|
||||
assert!(!config.contains("guardian_approval = true"));
|
||||
assert!(!config.contains("guardian_subagent"));
|
||||
assert_eq!(
|
||||
toml::from_str::<TomlValue>(&config)?
|
||||
@@ -5843,7 +5848,7 @@ smart_approvals = true
|
||||
app.config.codex_home = codex_home.path().to_path_buf();
|
||||
app.active_profile = Some("guardian".to_string());
|
||||
let config_toml_path = AbsolutePathBuf::try_from(codex_home.path().join("config.toml"))?;
|
||||
let config_toml = "profile = \"guardian\"\napprovals_reviewer = \"guardian_subagent\"\n\n[features]\nsmart_approvals = true\n";
|
||||
let config_toml = "profile = \"guardian\"\napprovals_reviewer = \"guardian_subagent\"\n\n[features]\nguardian_approval = true\n";
|
||||
std::fs::write(config_toml_path.as_path(), config_toml)?;
|
||||
let user_config = toml::from_str::<TomlValue>(config_toml)?;
|
||||
app.config.config_layer_stack = app
|
||||
@@ -5894,7 +5899,7 @@ smart_approvals = true
|
||||
);
|
||||
|
||||
let config = std::fs::read_to_string(codex_home.path().join("config.toml"))?;
|
||||
assert!(config.contains("smart_approvals = true"));
|
||||
assert!(config.contains("guardian_approval = true"));
|
||||
assert_eq!(
|
||||
toml::from_str::<TomlValue>(&config)?
|
||||
.as_table()
|
||||
|
||||
Reference in new issue
Block a user