Skip to main content

chio_kernel/kernel/evaluation/
sync_evaluation_wrapper.rs

1use super::*;
2
3impl ChioKernel {
4    pub fn evaluate_tool_call_blocking(
5        &self,
6        request: &ToolCallRequest,
7    ) -> Result<ToolCallResponse, KernelError> {
8        self.evaluate_tool_call_sync_inner(request, None, None)
9    }
10
11    /// Crate-private sync entrypoint invoked by the
12    /// [`crate::kernel::evaluator::ToolEvaluator`] default
13    /// implementation. Wraps the long-form
14    /// `evaluate_tool_call_sync_inner` so the trait body does
15    /// not need to plumb the `session_filesystem_roots` /
16    /// `extra_metadata` parameters; both default to `None` on this path,
17    /// matching the previous direct delegation from
18    /// `evaluate_tool_call`.
19    pub(crate) fn evaluate_tool_call_sync(
20        &self,
21        request: &ToolCallRequest,
22    ) -> Result<ToolCallResponse, KernelError> {
23        self.evaluate_tool_call_sync_inner(request, None, None)
24    }
25
26    pub fn evaluate_tool_call_blocking_with_metadata(
27        &self,
28        request: &ToolCallRequest,
29        extra_metadata: Option<serde_json::Value>,
30    ) -> Result<ToolCallResponse, KernelError> {
31        self.evaluate_tool_call_sync_inner(request, None, extra_metadata)
32    }
33
34    #[doc(hidden)]
35    fn evaluate_tool_call_sync_inner(
36        &self,
37        request: &ToolCallRequest,
38        session_filesystem_roots: Option<&[String]>,
39        extra_metadata: Option<serde_json::Value>,
40    ) -> Result<ToolCallResponse, KernelError> {
41        self.evaluate_tool_call_sync_with_session_context(
42            request,
43            session_filesystem_roots,
44            extra_metadata,
45            None,
46        )
47    }
48
49    /// Evaluate a tool call sync path with access to the owning session,
50    /// so the kernel can tag the resulting receipt with the session's
51    /// tenant_id (multi-tenant receipt isolation).
52    ///
53    /// `session_id` is the session that authenticated the caller, used only
54    /// to resolve the tenant from `auth_context().enterprise_identity`. The
55    /// tenant_id is NEVER read from `request` itself -- accepting a caller-
56    /// provided tenant would defeat the isolation guarantee.
57    pub(crate) fn evaluate_tool_call_sync_with_session_context(
58        &self,
59        request: &ToolCallRequest,
60        session_filesystem_roots: Option<&[String]>,
61        extra_metadata: Option<serde_json::Value>,
62        session_id: Option<&SessionId>,
63    ) -> Result<ToolCallResponse, KernelError> {
64        block_on_async_tool_dispatch(self.evaluate_tool_call_async_with_session_context(
65            request,
66            session_filesystem_roots,
67            extra_metadata,
68            session_id,
69            PreflightHoldDisposition::ReverseForRetry,
70        ))
71    }
72
73    /// Pre-execution authorization gate for callers that execute the tool
74    /// themselves (the sidecar mediated `/v1/evaluate` route).
75    ///
76    /// Runs the full pre-dispatch verification pipeline (capability, DPoP,
77    /// governed intent, approval token, guards, runtime admission), reserves
78    /// the pre-execution budget hold and KEEPS IT OPEN, and mints a fresh
79    /// execution nonce. It never dispatches a tool server, never consumes a
80    /// presented nonce, and never signs a completed or settled spend. The
81    /// returned receipt is intentionally non-authoritative: the hold is
82    /// reserved, not reconciled, so `is_authoritative_spend_receipt` rejects it.
83    ///
84    /// The reserved open hold is what enforces `max_total_cost` against
85    /// concurrent authorizations: a second authorization for a grant whose
86    /// budget is already fully reserved is denied. The caller presents the
87    /// minted nonce to the real tool server, which verifies and consumes it and
88    /// reconciles the reserved hold at the execution site.
89    ///
90    /// The request MUST NOT carry a presented execution nonce: this entry point
91    /// mints nonces, it does not settle them. The invariant is enforced here
92    /// fail-closed: a request with a presented nonce is rejected with
93    /// [`KernelError::ReservingAuthorizationRejectsPresentedNonce`] rather than
94    /// silently skipping the reserve path (a presented nonce makes
95    /// `execution_nonce_preflight_required` return false) and falling through to
96    /// dispatch, which is the opposite of the documented reserve behavior.
97    pub fn authorize_tool_call_reserving_blocking_with_metadata(
98        &self,
99        request: &ToolCallRequest,
100        extra_metadata: Option<serde_json::Value>,
101    ) -> Result<ToolCallResponse, KernelError> {
102        if request.execution_nonce.is_some() {
103            return Err(KernelError::ReservingAuthorizationRejectsPresentedNonce);
104        }
105        block_on_async_tool_dispatch(self.evaluate_tool_call_async_with_session_context(
106            request,
107            None,
108            extra_metadata,
109            None,
110            PreflightHoldDisposition::ReserveForCaller,
111        ))
112    }
113}