use super::{vm_to_json, InstalledPolicies, SessionPolicyGuard};
use crate::llm::permissions;
use crate::orchestration::{
enter_nested_execution_policy, pop_approval_policy, pop_execution_policy, push_approval_policy,
push_command_policy, push_execution_policy, CapabilityPolicy, NestedExecutionGuard,
NestedExecutionKind, ToolApprovalPolicy, NESTED_KIND_OPTION_KEY, NESTED_LABEL_OPTION_KEY,
};
use crate::value::{VmError, VmValue};
pub(crate) fn install_session_policy_guard(
opts_map: &crate::value::DictMap,
) -> Result<SessionPolicyGuard, VmError> {
let mut installed = InstalledPolicies::default();
match install_session_policies_inner(opts_map, &mut installed) {
Ok(()) => Ok(SessionPolicyGuard { installed }),
Err(error) => {
release_session_policies(&installed);
Err(error)
}
}
}
const SESSION_POLICY_OPTION_KEYS: [&str; 5] = [
"policy",
"approval_policy",
"command_policy",
"permissions",
"tool_precheck",
];
pub(crate) fn options_request_session_policies(opts_map: &crate::value::DictMap) -> bool {
SESSION_POLICY_OPTION_KEYS
.iter()
.any(|key| opts_map.get(*key).is_some())
}
fn install_session_policies_inner(
opts_map: &crate::value::DictMap,
installed: &mut InstalledPolicies,
) -> Result<(), VmError> {
if let Some(requested) = parse_capability_policy(opts_map.get("policy"))? {
let effective = match crate::orchestration::current_execution_policy() {
Some(outer) => outer.intersect(&requested).map_err(VmError::Runtime)?,
None => requested,
};
push_execution_policy(effective);
installed.pushed_execution = true;
}
if let Some(requested) = parse_approval_policy(opts_map.get("approval_policy"))? {
let effective = match crate::orchestration::current_approval_policy() {
Some(outer) => outer.intersect(&requested),
None => requested,
};
push_approval_policy(effective);
installed.pushed_approval = true;
}
if let Some(policy) = crate::orchestration::parse_command_policy_value(
opts_map.get("command_policy"),
"agent_loop.command_policy",
)? {
push_command_policy(policy);
installed.pushed_command = true;
}
if let Some(precheck) = crate::orchestration::parse_tool_precheck_value(
opts_map.get("tool_precheck"),
"agent_loop.tool_precheck",
)? {
crate::orchestration::push_tool_precheck(precheck);
installed.pushed_precheck = true;
}
if let Some(permissions) = permissions::parse_dynamic_permission_policy(
opts_map.get("permissions"),
"agent_loop.permissions",
)? {
permissions::push_dynamic_permission_policy(permissions);
installed.pushed_permissions = true;
}
Ok(())
}
pub(super) fn release_session_policies(installed: &InstalledPolicies) {
if installed.pushed_precheck {
crate::orchestration::pop_tool_precheck();
}
if installed.pushed_permissions {
permissions::pop_dynamic_permission_policy();
}
if installed.pushed_command {
crate::orchestration::pop_command_policy();
}
if installed.pushed_approval {
pop_approval_policy();
}
if installed.pushed_execution {
pop_execution_policy();
}
}
fn parse_capability_policy(value: Option<&VmValue>) -> Result<Option<CapabilityPolicy>, VmError> {
let Some(value) = value else { return Ok(None) };
if matches!(value, VmValue::Nil) {
return Ok(None);
}
serde_json::from_value::<CapabilityPolicy>(crate::llm::vm_value_to_json(value))
.map(Some)
.map_err(|error| VmError::Runtime(format!("agent_loop.policy: invalid policy: {error}")))
}
pub(super) fn install_session_nested_budget(
opts_map: &crate::value::DictMap,
session_id: &str,
) -> Result<NestedExecutionGuard, VmError> {
let requested = parse_capability_policy(opts_map.get("policy"))?;
let kind =
NestedExecutionKind::parse_or_default(opts_map.get(NESTED_KIND_OPTION_KEY).and_then(|v| {
match v {
VmValue::String(text) => Some(text.as_str()),
_ => None,
}
}));
let label = opts_map
.get(NESTED_LABEL_OPTION_KEY)
.and_then(|v| match v {
VmValue::String(text) if !text.trim().is_empty() => Some(text.to_string()),
_ => None,
})
.unwrap_or_else(|| session_id.to_string());
enter_nested_execution_policy(requested, kind, &label)
}
pub(super) fn build_nested_budget_denial(
session_id: &str,
prompt: &str,
error: &VmError,
) -> VmValue {
let (message, category) = match error {
VmError::CategorizedError { message, category } => (message.clone(), category.as_str()),
other => (other.to_string(), "tool_rejected"),
};
let _ = crate::agent_sessions::append_event(
session_id,
crate::llm::helpers::transcript_event(
"nested_execution_budget_denied",
"system",
"internal",
&message,
Some(serde_json::json!({
"category": category,
"session_id": session_id,
})),
),
);
let transcript_json = crate::agent_sessions::transcript(session_id)
.as_ref()
.map(vm_to_json)
.unwrap_or(serde_json::Value::Null);
let result = serde_json::json!({
"status": "blocked",
"final_status": "blocked",
"stop_reason": "nested_execution_budget_exhausted",
"error": {
"category": category,
"message": message,
},
"text": "",
"visible_text": "",
"private_reasoning": serde_json::Value::Null,
"thinking_summary": serde_json::Value::Null,
"llm": {"iterations": 0, "duration_ms": 0, "input_tokens": 0, "output_tokens": 0},
"tools": {"calls": [], "successful": [], "rejected": [], "mode": ""},
"transcript": transcript_json,
"trace": serde_json::Value::Null,
"tokens_used": 0,
"cost_usd": 0.0,
"session_id": session_id,
"task": prompt,
"daemon_state": serde_json::Value::Null,
"daemon_snapshot_path": serde_json::Value::Null,
});
crate::stdlib::json_to_vm_value(&result)
}
fn parse_approval_policy(value: Option<&VmValue>) -> Result<Option<ToolApprovalPolicy>, VmError> {
let Some(value) = value else { return Ok(None) };
if matches!(value, VmValue::Nil) {
return Ok(None);
}
serde_json::from_value::<ToolApprovalPolicy>(crate::llm::vm_value_to_json(value))
.map(Some)
.map_err(|error| {
VmError::Runtime(format!(
"agent_loop.approval_policy: invalid policy: {error}"
))
})
}