Skip to main content

chio_cross_protocol/
execution.rs

1use chio_core::capability::{
2    governance::{GovernedApprovalToken, GovernedTransactionIntent, ThresholdApprovalProposal},
3    scope::ModelMetadata,
4    supplemental_authorization::OpaqueSupplementalAuthorization,
5    token::CapabilityToken,
6};
7use chio_kernel::dpop;
8use chio_kernel::{ChioKernel, SignedExecutionNonce, ToolCallRequest, ToolCallResponse};
9use serde_json::{json, Value};
10
11use crate::capability_bridge::{CrossProtocolCapabilityEnvelope, CrossProtocolCapabilityRef};
12use crate::discovery::DiscoveryProtocol;
13use crate::error::BridgeError;
14use crate::routing::{route_selection_metadata, RouteSelectionEvidence};
15
16/// Kernel-bound execution request for a bridged hop.
17#[derive(Debug, Clone)]
18pub struct CrossProtocolExecutionRequest {
19    pub origin_request_id: String,
20    pub kernel_request_id: String,
21    pub target_protocol: DiscoveryProtocol,
22    pub target_server_id: String,
23    pub target_tool_name: String,
24    pub agent_id: String,
25    pub arguments: Value,
26    pub capability: CapabilityToken,
27    pub source_envelope: Value,
28    pub dpop_proof: Option<dpop::DpopProof>,
29    pub execution_nonce: Option<SignedExecutionNonce>,
30    pub governed_intent: Option<GovernedTransactionIntent>,
31    pub approval_token: Option<GovernedApprovalToken>,
32    pub approval_tokens: Vec<GovernedApprovalToken>,
33    pub threshold_approval_proposal: Option<ThresholdApprovalProposal>,
34    pub supplemental_authorization: Option<OpaqueSupplementalAuthorization>,
35    pub model_metadata: Option<ModelMetadata>,
36}
37
38/// Build the exact kernel request for a bridged execution.
39///
40/// Keeping this projection in one place prevents protocol executors from
41/// selecting one approval, dropping the rest, or reconstructing signed
42/// capability fields.
43pub fn kernel_tool_call_request(request: &CrossProtocolExecutionRequest) -> ToolCallRequest {
44    ToolCallRequest {
45        request_id: request.kernel_request_id.clone(),
46        capability: request.capability.clone(),
47        tool_name: request.target_tool_name.clone(),
48        server_id: request.target_server_id.clone(),
49        agent_id: request.agent_id.clone(),
50        arguments: request.arguments.clone(),
51        dpop_proof: request.dpop_proof.clone(),
52        execution_nonce: request.execution_nonce.clone(),
53        governed_intent: request.governed_intent.clone(),
54        approval_token: request.approval_token.clone(),
55        approval_tokens: request.approval_tokens.clone(),
56        threshold_approval_proposal: request.threshold_approval_proposal.clone(),
57        supplemental_authorization: request.supplemental_authorization.clone(),
58        model_metadata: request.model_metadata.clone(),
59        federated_origin_kernel_id: None,
60    }
61}
62
63/// Fully prepared target-protocol request handed to a protocol-specific executor.
64pub struct CrossProtocolTargetRequest<'a> {
65    pub kernel: &'a ChioKernel,
66    pub execution: &'a CrossProtocolExecutionRequest,
67    pub source_protocol: DiscoveryProtocol,
68    pub bridge_id: &'a str,
69    pub capability_ref: &'a CrossProtocolCapabilityRef,
70    pub capability_envelope: &'a CrossProtocolCapabilityEnvelope,
71    pub route_selection: &'a RouteSelectionEvidence,
72    pub projected_request: &'a Value,
73}
74
75/// One target-side hop emitted by a target protocol executor.
76#[derive(Debug, Clone)]
77pub struct TargetExecutionHop {
78    pub protocol: DiscoveryProtocol,
79    pub request_id: String,
80    pub receipt_id: Option<String>,
81}
82
83/// Result returned by a target-protocol executor.
84pub struct CrossProtocolTargetExecution {
85    pub response: ToolCallResponse,
86    pub protocol_result: Option<Value>,
87    pub protocol_notifications: Vec<Value>,
88    pub route_hops: Vec<TargetExecutionHop>,
89}
90
91/// Pluggable executor for a non-native target protocol.
92pub trait TargetProtocolExecutor: Send + Sync {
93    fn target_protocol(&self) -> DiscoveryProtocol;
94
95    fn execute(
96        &self,
97        request: CrossProtocolTargetRequest<'_>,
98    ) -> Result<CrossProtocolTargetExecution, BridgeError>;
99}
100
101/// Default non-native protocol executor for OpenAI-shaped function-call
102/// projections.
103#[derive(Debug, Default, Clone, Copy)]
104pub struct OpenAiTargetExecutor;
105
106impl TargetProtocolExecutor for OpenAiTargetExecutor {
107    fn target_protocol(&self) -> DiscoveryProtocol {
108        DiscoveryProtocol::OpenAi
109    }
110
111    fn execute(
112        &self,
113        request: CrossProtocolTargetRequest<'_>,
114    ) -> Result<CrossProtocolTargetExecution, BridgeError> {
115        let route_metadata = metadata_with_source_receipt_context(
116            route_selection_metadata(request.route_selection)?,
117            &request.execution.source_envelope,
118        )?;
119        let response = request
120            .kernel
121            .evaluate_tool_call_blocking_with_metadata(
122                &kernel_tool_call_request(request.execution),
123                Some(route_metadata),
124            )
125            .map_err(BridgeError::Kernel)?;
126
127        let receipt_ref = response.receipt.id.clone();
128        let output = render_protocol_output(&response.output, response.reason.as_deref());
129
130        Ok(CrossProtocolTargetExecution {
131            response,
132            protocol_result: Some(json!({
133                "type": "function_call_output",
134                "call_id": request.execution.origin_request_id,
135                "output": output,
136                "receipt_ref": receipt_ref,
137            })),
138            protocol_notifications: Vec::new(),
139            route_hops: vec![
140                TargetExecutionHop {
141                    protocol: DiscoveryProtocol::OpenAi,
142                    request_id: format!("{}:openai", request.execution.kernel_request_id),
143                    receipt_id: None,
144                },
145                TargetExecutionHop {
146                    protocol: DiscoveryProtocol::Native,
147                    request_id: request.execution.kernel_request_id.clone(),
148                    receipt_id: Some(receipt_ref),
149                },
150            ],
151        })
152    }
153}
154
155pub fn metadata_with_source_receipt_context(
156    mut metadata: Value,
157    source_envelope: &Value,
158) -> Result<Value, BridgeError> {
159    let Some(receipt_context) = source_envelope.get("receipt_context").cloned() else {
160        return Ok(metadata);
161    };
162    let Some(metadata_obj) = metadata.as_object_mut() else {
163        return Err(BridgeError::InvalidRequest(
164            "receipt metadata must be a JSON object".to_string(),
165        ));
166    };
167    metadata_obj.insert("receipt_context".to_string(), receipt_context);
168    Ok(metadata)
169}
170
171fn render_protocol_output(
172    output: &Option<chio_kernel::ToolCallOutput>,
173    reason: Option<&str>,
174) -> String {
175    match output {
176        Some(chio_kernel::ToolCallOutput::Value(value)) => value
177            .as_str()
178            .map(str::to_string)
179            .unwrap_or_else(|| serde_json::to_string(value).unwrap_or_else(|_| "{}".to_string())),
180        Some(chio_kernel::ToolCallOutput::Stream(stream)) => serde_json::to_string(
181            &stream
182                .chunks
183                .iter()
184                .map(|chunk| chunk.data.clone())
185                .collect::<Vec<_>>(),
186        )
187        .unwrap_or_else(|_| "[]".to_string()),
188        None => reason.unwrap_or("{}").to_string(),
189    }
190}