chio-kernel 0.1.2

Chio runtime kernel: capability validation, guard evaluation, receipt signing
Documentation
//! Tool-call evaluation surface.
//!
//! Defines the [`ToolEvaluator`] trait that names the four logical phases of
//! a tool-call evaluation (capability validation, guard pipeline, dispatch,
//! receipt signing). The public `ChioKernel::evaluate_tool_call().await`
//! entrypoint uses the async-native kernel path directly. The
//! [`BlockingToolEvaluator`] remains for compatibility surfaces that
//! intentionally enter the synchronous bridge.
//!
//! Futures dropped after budget admission are handled by the post-admission
//! drop guard: a cancellation receipt is recorded whenever dispatch was in
//! flight and runtime-admission reservations get an explicit fail-closed
//! disposition. Hard process death mid-dispatch remains the charter of the
//! dispatch-intent journal.

use crate::kernel::ChioKernel;
use crate::{
    ChioReceipt, ChioReceiptBody, KernelError, ToolCallRequest, ToolCallResponse,
    ToolInvocationCost, ToolServerOutput, Verdict,
};

/// The four logical phases of a tool-call evaluation, surfaced as an
/// async-capable trait so each phase can be replaced with an async-native
/// implementation without re-shaping the public surface.
///
/// The default [`BlockingToolEvaluator`] preserves the semantics of
/// `ChioKernel::evaluate_tool_call_sync_inner` by delegating to the
/// `evaluate_tool_call_sync` shim. The four step methods
/// (`validate_capability`, `run_guards`, `dispatch`, `sign_receipt`) default
/// to forwarding through the full synchronous pipeline; override them to swap
/// in async-native step bodies.
#[allow(async_fn_in_trait)]
pub trait ToolEvaluator: Send + Sync {
    /// Run the full evaluation pipeline for `request` against `kernel` and
    /// return the resulting `ToolCallResponse`.
    async fn evaluate(
        &self,
        kernel: &ChioKernel,
        request: &ToolCallRequest,
    ) -> Result<ToolCallResponse, KernelError>;

    /// Run the full evaluation pipeline with additional receipt metadata.
    async fn evaluate_with_metadata(
        &self,
        kernel: &ChioKernel,
        request: &ToolCallRequest,
        extra_metadata: Option<serde_json::Value>,
    ) -> Result<ToolCallResponse, KernelError> {
        let _ = extra_metadata;
        self.evaluate(kernel, request).await
    }

    /// Validate the capability token attached to `request`.
    async fn validate_capability(
        &self,
        kernel: &ChioKernel,
        request: &ToolCallRequest,
    ) -> Result<Verdict, KernelError> {
        let response = self.evaluate(kernel, request).await?;
        Ok(response.verdict)
    }

    /// Run the registered guard pipeline against `request`.
    async fn run_guards(
        &self,
        kernel: &ChioKernel,
        request: &ToolCallRequest,
    ) -> Result<Verdict, KernelError> {
        let response = self.evaluate(kernel, request).await?;
        Ok(response.verdict)
    }

    /// Direct phase dispatch is unavailable because it cannot retain the
    /// admission operation, compensation, outcome, and receipt as one durable
    /// lifecycle. Use [`ToolEvaluator::evaluate`] instead.
    ///
    /// This default denies every direct dispatch, not only monetary ones.
    /// Implementors that override `dispatch` are unaffected; those that relied on
    /// the default should read `docs/migrations/kernel-embedder-surface.md`.
    async fn dispatch(
        &self,
        kernel: &ChioKernel,
        request: &ToolCallRequest,
        has_monetary_grant: bool,
    ) -> Result<(ToolServerOutput, Option<ToolInvocationCost>), KernelError> {
        let _ = (kernel, request, has_monetary_grant);
        Err(KernelError::DirectDispatchUnavailable)
    }

    /// Sign the receipt for the (allow or deny) outcome of a tool call.
    ///
    /// Accepts a fully-constructed [`ChioReceiptBody`] plus the exact byte
    /// preimage its `content_hash` was derived from, and returns the signed
    /// [`ChioReceipt`]. The default body routes through
    /// `kernel.sign_receipt_via_channel` (the mpsc-backed signing task);
    /// producers wait on bounded backpressure, never on a receipt-log mutex.
    /// The signed receipt is byte-identical to the inline
    /// `build_and_sign_receipt` path, and equally fail-closed: both delegate to
    /// `chio_kernel_core::sign_receipt_with_handle`, which recomputes
    /// `content_hash` over `canonical_content` and refuses to sign on mismatch
    /// (WYSIWYS).
    async fn sign_receipt(
        &self,
        kernel: &ChioKernel,
        body: ChioReceiptBody,
        canonical_content: Vec<u8>,
    ) -> Result<ChioReceipt, KernelError> {
        kernel
            .sign_receipt_via_channel(body, canonical_content)
            .await
    }
}

/// Compatibility [`ToolEvaluator`] implementation: delegates the entire
/// pipeline to the synchronous flow on [`ChioKernel`].
///
/// Inside a multi-threaded tokio runtime the call is wrapped in
/// `tokio::task::block_in_place` so the worker thread is released back to
/// the scheduler while the synchronous body runs. Outside such a runtime
/// the call is direct; if that direct path enters a current-thread runtime,
/// the kernel bridge returns a typed sync-bridge incompatibility error before
/// dispatch side effects.
#[allow(dead_code)]
#[derive(Debug, Default, Clone, Copy)]
pub struct BlockingToolEvaluator;

impl ToolEvaluator for BlockingToolEvaluator {
    async fn evaluate(
        &self,
        kernel: &ChioKernel,
        request: &ToolCallRequest,
    ) -> Result<ToolCallResponse, KernelError> {
        // Reach into the existing synchronous pipeline. The wrapper isolates
        // the (potentially blocking) sync work from the async runtime when
        // we are inside one; otherwise it is a direct call.
        match tokio::runtime::Handle::try_current() {
            Ok(handle) if handle.runtime_flavor() == tokio::runtime::RuntimeFlavor::MultiThread => {
                tokio::task::block_in_place(|| kernel.evaluate_tool_call_sync(request))
            }
            _ => kernel.evaluate_tool_call_sync(request),
        }
    }

    async fn evaluate_with_metadata(
        &self,
        kernel: &ChioKernel,
        request: &ToolCallRequest,
        extra_metadata: Option<serde_json::Value>,
    ) -> Result<ToolCallResponse, KernelError> {
        match tokio::runtime::Handle::try_current() {
            Ok(handle) if handle.runtime_flavor() == tokio::runtime::RuntimeFlavor::MultiThread => {
                tokio::task::block_in_place(|| {
                    kernel.evaluate_tool_call_blocking_with_metadata(request, extra_metadata)
                })
            }
            _ => kernel.evaluate_tool_call_blocking_with_metadata(request, extra_metadata),
        }
    }
}