Skip to main content

a3s_code_core/agent_api/
governance_facade.rs

1use super::*;
2
3impl AgentSession {
4    /// Install or replace a runtime budget guard. Takes effect on the
5    /// next `send` / `stream` call (the guard is consulted at agent-
6    /// loop build time, not on the live execution). Setting `None`
7    /// clears the override so `config.budget_guard` takes over again.
8    ///
9    /// This is the entry point SDKs use to wire a host-supplied guard
10    /// after the session has already been constructed — useful when
11    /// the guard's transport (e.g. a JS callable) cannot live inside
12    /// the value-typed `SessionOptions`.
13    pub fn set_budget_guard(
14        &self,
15        guard: Option<Arc<dyn crate::budget::BudgetGuard>>,
16    ) -> crate::error::Result<()> {
17        self.close_handle.mutate_immediate(|| {
18            let mut slot = self
19                .runtime_budget_guard
20                .lock()
21                .unwrap_or_else(|p| p.into_inner());
22            *slot = guard;
23            drop(slot);
24            // Delegated children own a pre-built TaskExecutor. Refresh its parent
25            // context so the next run cannot bypass a runtime-installed ledger.
26            // SkillTool likewise owns a child AgentConfig captured at registration
27            // time, so it must be refreshed from the same runtime source of truth.
28            self.refresh_task_delegation_tools();
29            self.refresh_skill_tools();
30        })
31    }
32
33    /// Return the currently-installed runtime budget guard, if any.
34    /// `None` means the loop falls back to `config.budget_guard`.
35    pub fn budget_guard(&self) -> Option<Arc<dyn crate::budget::BudgetGuard>> {
36        self.runtime_budget_guard
37            .lock()
38            .unwrap_or_else(|p| p.into_inner())
39            .clone()
40    }
41
42    /// Return pending HITL tool confirmations for this session.
43    pub async fn pending_confirmations(&self) -> Vec<PendingConfirmationInfo> {
44        HitlControl::from_session(self)
45            .pending_confirmations()
46            .await
47    }
48
49    /// Resolve a pending HITL tool confirmation.
50    ///
51    /// Returns `Ok(true)` when a pending confirmation was found and completed,
52    /// `Ok(false)` when the tool ID is not pending or HITL is not configured.
53    pub async fn confirm_tool_use(
54        &self,
55        tool_id: &str,
56        approved: bool,
57        reason: Option<String>,
58    ) -> Result<bool> {
59        HitlControl::from_session(self)
60            .confirm_tool_use(tool_id, approved, reason)
61            .await
62    }
63
64    /// Cancel all pending HITL confirmations for this session.
65    pub async fn cancel_confirmations(&self) -> usize {
66        HitlControl::from_session(self).cancel_confirmations().await
67    }
68
69    /// Return structured verification reports recorded for this session.
70    pub fn verification_reports(&self) -> Vec<crate::verification::VerificationReport> {
71        VerificationRuntime::from_session(self).reports()
72    }
73
74    /// Return a structured summary of all verification reports recorded for this session.
75    pub fn verification_summary(&self) -> crate::verification::VerificationSummary {
76        VerificationRuntime::from_session(self).summary()
77    }
78
79    /// Return a concise human-readable verification summary for this session.
80    pub fn verification_summary_text(&self) -> String {
81        VerificationRuntime::from_session(self).summary_text()
82    }
83
84    /// Add externally produced verification reports to this session's completion evidence.
85    pub fn record_verification_reports(
86        &self,
87        reports: impl IntoIterator<Item = crate::verification::VerificationReport>,
88    ) {
89        VerificationRuntime::from_session(self).record(reports);
90    }
91
92    /// Register a hook for lifecycle event interception.
93    pub fn register_hook(&self, hook: crate::hooks::Hook) -> crate::error::Result<()> {
94        self.close_handle
95            .mutate_immediate(|| HookControl::from_session(self).register_hook(hook))
96    }
97
98    /// Unregister a hook by ID.
99    pub fn unregister_hook(
100        &self,
101        hook_id: &str,
102    ) -> crate::error::Result<Option<crate::hooks::Hook>> {
103        self.close_handle
104            .mutate_immediate(|| HookControl::from_session(self).unregister_hook(hook_id))
105    }
106
107    /// Register a handler for a specific hook.
108    pub fn register_hook_handler(
109        &self,
110        hook_id: &str,
111        handler: Arc<dyn crate::hooks::HookHandler>,
112    ) -> crate::error::Result<()> {
113        self.close_handle.mutate_immediate(|| {
114            HookControl::from_session(self).register_hook_handler(hook_id, handler)
115        })
116    }
117
118    /// Unregister a hook handler by hook ID.
119    pub fn unregister_hook_handler(&self, hook_id: &str) -> crate::error::Result<()> {
120        self.close_handle
121            .mutate_immediate(|| HookControl::from_session(self).unregister_hook_handler(hook_id))
122    }
123
124    /// Get the number of registered hooks.
125    pub fn hook_count(&self) -> usize {
126        HookControl::from_session(self).hook_count()
127    }
128
129    /// Run verification commands through the session's tool execution path.
130    pub async fn verify_commands(
131        &self,
132        subject: &str,
133        commands: &[crate::verification::VerificationCommand],
134    ) -> Result<crate::verification::VerificationReport> {
135        VerificationRuntime::from_session(self)
136            .verify_commands(subject, commands)
137            .await
138    }
139
140    /// Return project-aware verification command presets for this workspace.
141    pub fn verification_presets(&self) -> Vec<crate::verification::VerificationPreset> {
142        VerificationRuntime::from_session(self).presets()
143    }
144}