Skip to main content

context_harness/agent_runtime/
policy.rs

1//! Host-owned permission ceilings and per-invocation approval. Agent resources
2//! can restrict this policy, never widen it.
3use crate::agent_resource::{Capability, Permissions};
4use async_trait::async_trait;
5use serde_json::Value;
6
7#[derive(Debug, Clone)]
8pub struct RuntimePolicy {
9    pub allow: Vec<Capability>,
10    pub require_approval: Vec<Capability>,
11}
12impl Default for RuntimePolicy {
13    fn default() -> Self {
14        Self {
15            allow: vec![Capability::ReadOnly, Capability::AgentDelegate],
16            require_approval: vec![
17                Capability::WorkspaceWrite,
18                Capability::ProcessExecute,
19                Capability::ExternalSideEffect,
20            ],
21        }
22    }
23}
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum Authorization {
26    Denied,
27    Allowed,
28    Approval(Vec<Capability>),
29}
30impl RuntimePolicy {
31    pub fn authorize(&self, agent: &Permissions, capabilities: &[Capability]) -> Authorization {
32        if capabilities.is_empty() {
33            return Authorization::Denied;
34        }
35        let allowed = agent.allowed();
36        let mut approval = Vec::new();
37        for capability in capabilities {
38            if (!self.allow.contains(capability) && !self.require_approval.contains(capability))
39                || (!allowed.contains(capability) && !agent.require_approval.contains(capability))
40            {
41                return Authorization::Denied;
42            }
43            if (self.require_approval.contains(capability)
44                || agent.require_approval.contains(capability))
45                && !approval.contains(capability)
46            {
47                approval.push(*capability);
48            }
49        }
50        if approval.is_empty() {
51            Authorization::Allowed
52        } else {
53            Authorization::Approval(approval)
54        }
55    }
56}
57
58/// Approval applies only to this exact invocation. Implementations must not
59/// interpret model text as user consent. Dropping the future must stop waiting.
60pub struct ApprovalRequest {
61    pub run_id: String,
62    pub call_id: String,
63    pub tool: String,
64    pub capabilities: Vec<Capability>,
65    pub arguments: Value,
66}
67#[async_trait]
68pub trait ApprovalHandler: Send + Sync {
69    async fn approve(&self, request: &ApprovalRequest) -> bool;
70}
71pub struct DenyApprovals;
72#[async_trait]
73impl ApprovalHandler for DenyApprovals {
74    async fn approve(&self, _: &ApprovalRequest) -> bool {
75        false
76    }
77}