harn-vm 0.10.49

Async bytecode virtual machine for the Harn programming language
Documentation
//! Policy normalization at the agent-tool dispatch boundary.

/// Execution-policy gate for one tool dispatch: the tool/capability/
/// side-effect ceilings plus the per-tool argument allow-lists.
///
/// `policy_machinery_active` is the dispatch fast-path gate (see
/// `host_agent_dispatch_tool_call`): when false, no execution-policy scope is
/// installed, so `enforce_current_policy_for_tool` would return `Ok(())`
/// unconditionally and `enforce_tool_arg_constraints` would iterate the empty
/// constraint list of `CapabilityPolicy::default()` — skipping both is
/// behavior-preserving and avoids building that default policy per call.
pub(super) struct DispatchPolicy<'a> {
    active: bool,
    annotations: Option<&'a crate::tool_annotations::ToolAnnotations>,
}

impl<'a> DispatchPolicy<'a> {
    pub(super) fn new(
        active: bool,
        annotations: Option<&'a crate::tool_annotations::ToolAnnotations>,
    ) -> Self {
        Self {
            active,
            annotations,
        }
    }

    pub(super) fn enforce(
        &self,
        tool_name: &str,
        tool_args: &serde_json::Value,
        side_effect_grant: Option<&crate::orchestration::SideEffectCeilingGrant>,
    ) -> Result<(), crate::orchestration::PolicyDenial> {
        if !self.active {
            return Ok(());
        }
        crate::orchestration::enforce_current_policy_for_tool_with_annotations_and_side_effect_grant(
            tool_name,
            self.annotations,
            side_effect_grant,
        )?;
        crate::orchestration::enforce_tool_arg_constraints(
            &crate::orchestration::current_execution_policy().unwrap_or_default(),
            tool_name,
            tool_args,
        )
    }
}

pub(super) fn tool_denial_from_policy(
    policy_denial: crate::orchestration::PolicyDenial,
    tool_name: &str,
) -> crate::agent_events::ToolDenial {
    let side_effect_ceiling = policy_denial.side_effect_ceiling.map(|violation| {
        crate::agent_events::SideEffectCeilingDetails {
            ceiling: violation.ceiling,
            required_level: violation.required_level,
            tool: tool_name.to_string(),
            remedy: crate::agent_events::SideEffectCeilingRemedy::RaiseSideEffectCeiling,
        }
    });
    let denial = if policy_denial.gate == crate::agent_events::DenialGate::ArgConstraint {
        crate::agent_events::ToolDenial::retryable(
            policy_denial.gate,
            policy_denial.capability,
            policy_denial.reason,
        )
    } else {
        crate::agent_events::ToolDenial::terminal(
            policy_denial.gate,
            policy_denial.capability,
            policy_denial.reason,
        )
    };
    match side_effect_ceiling {
        Some(details) => denial.with_side_effect_ceiling(details),
        None => denial,
    }
}