Skip to content
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/fuzz_command_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ use mentat::security::SecurityPolicy;
fuzz_target!(|data: &[u8]| {
if let Ok(s) = std::str::from_utf8(data) {
let policy = SecurityPolicy::default();
let _ = policy.validate_command_execution(s, false);
let _ = policy.validate_command_execution(s);
}
});
72 changes: 59 additions & 13 deletions src/agent/loop_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2178,6 +2178,7 @@ pub(crate) async fn agent_turn(
temperature,
silent,
approval,
None, // approval_adapter
channel_name,
channel_reply_target,
multimodal_config,
Expand Down Expand Up @@ -2330,6 +2331,7 @@ pub(crate) async fn run_tool_call_loop(
temperature: f64,
silent: bool,
approval: Option<&ApprovalManager>,
approval_adapter: Option<&dyn crate::approval::ChannelApprovalAdapter>,
channel_name: &str,
channel_reply_target: Option<&str>,
multimodal_config: &crate::config::MultimodalConfig,
Expand Down Expand Up @@ -3132,24 +3134,34 @@ pub(crate) async fn run_tool_call_loop(
// ── Approval hook ────────────────────────────────
if let Some(mgr) = approval {
if mgr.needs_approval(&tool_name) {
let request = ApprovalRequest {
tool_name: tool_name.clone(),
arguments: tool_args.clone(),
};

// Interactive CLI: prompt the operator.
// Non-interactive (channels): auto-deny since no operator
// is present to approve.
let decision = if mgr.is_non_interactive() {
let request =
ApprovalRequest::new(tool_name.clone(), tool_args.clone(), channel_name);

// Route through adapter when available; otherwise fall
// back to the legacy is_non_interactive/prompt_cli path.
let decision = if let Some(adapter) = approval_adapter {
mgr.request_approval(adapter, &request).await
} else if mgr.is_non_interactive() {
ApprovalResponse::No
} else {
mgr.prompt_cli(&request)
};

mgr.record_decision(&tool_name, &tool_args, decision, channel_name);
// record_decision is only needed for the legacy path —
// request_approval already records internally.
if approval_adapter.is_none() {
mgr.record_decision(&tool_name, &tool_args, decision, channel_name);
}

if decision == ApprovalResponse::No {
let denied = "Denied by user.".to_string();
if decision == ApprovalResponse::No || decision == ApprovalResponse::Timeout {
let denied = if decision == ApprovalResponse::Timeout {
format!(
"Approval timed out after {}s.",
mgr.approval_timeout().as_secs()
)
} else {
"Denied by user.".to_string()
};
runtime_trace::record_event(
"tool_call_result",
Some(channel_name),
Expand Down Expand Up @@ -3807,12 +3819,18 @@ pub async fn run(
builder.build(&ctx).unwrap_or_default()
};

// ── Approval manager (supervised mode) ───────────────────────
// ── Approval manager + adapter (supervised mode) ──────────────
let approval_manager = if interactive {
Some(ApprovalManager::from_config(&config.autonomy))
} else {
None
};
let cli_approval_adapter: Option<Box<dyn crate::approval::ChannelApprovalAdapter>> =
if interactive {
Some(Box::new(crate::approval::CliApprovalAdapter))
} else {
None
};
let channel_name = if interactive { "cli" } else { "daemon" };
let memory_session_id = session_state_file.as_deref().and_then(|path| {
let raw = path.to_string_lossy().trim().to_string();
Expand Down Expand Up @@ -3931,6 +3949,7 @@ pub async fn run(
effective_temperature,
false,
approval_manager.as_ref(),
cli_approval_adapter.as_deref(),
channel_name,
None,
&config.multimodal,
Expand Down Expand Up @@ -4232,6 +4251,7 @@ pub async fn run(
turn_temperature,
true,
approval_manager.as_ref(),
cli_approval_adapter.as_deref(),
channel_name,
None,
&config.multimodal,
Expand Down Expand Up @@ -5755,6 +5775,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -5810,6 +5831,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&multimodal,
Expand Down Expand Up @@ -5859,6 +5881,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -5907,6 +5930,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -5962,6 +5986,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&multimodal,
Expand Down Expand Up @@ -6017,6 +6042,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&multimodal,
Expand Down Expand Up @@ -6073,6 +6099,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&multimodal,
Expand Down Expand Up @@ -6127,6 +6154,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&multimodal,
Expand Down Expand Up @@ -6181,6 +6209,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&multimodal,
Expand Down Expand Up @@ -6318,6 +6347,7 @@ mod tests {
0.0,
true,
Some(&approval_mgr),
None, // approval_adapter
"telegram",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -6392,6 +6422,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"telegram",
Some("chat-42"),
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -6458,6 +6489,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"telegram",
Some("chat-42"),
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -6519,6 +6551,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -6592,6 +6625,7 @@ mod tests {
0.0,
true,
Some(&approval_mgr),
None, // approval_adapter
"telegram",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -6656,6 +6690,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -6740,6 +6775,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -6801,6 +6837,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"cli",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -6888,6 +6925,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"telegram",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -6957,6 +6995,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"telegram",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -7028,6 +7067,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"telegram",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -7103,6 +7143,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"telegram",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -7187,6 +7228,7 @@ mod tests {
0.0,
true,
None,
None, // approval_adapter
"telegram",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -9268,6 +9310,7 @@ Let me check the result."#;
0.0,
true,
None,
None, // approval_adapter
"telegram",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -9426,6 +9469,7 @@ Let me check the result."#;
0.0,
true,
None,
None, // approval_adapter
"test",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -9508,6 +9552,7 @@ Let me check the result."#;
0.0,
true,
None,
None, // approval_adapter
"test",
None,
&crate::config::MultimodalConfig::default(),
Expand Down Expand Up @@ -9567,6 +9612,7 @@ Let me check the result."#;
0.0,
true,
None,
None, // approval_adapter
"test",
None,
&crate::config::MultimodalConfig::default(),
Expand Down
Loading
Loading