use chio_core::capability::{scope::ChioScope, token::CapabilityToken};
use chio_core::{canonical_json_bytes, sha256_hex};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::discovery::DiscoveryProtocol;
use crate::error::BridgeError;
pub const CROSS_PROTOCOL_AUTHORITY_PATH: &str = "cross_protocol_orchestrator";
pub const CROSS_PROTOCOL_CAPABILITY_ENVELOPE_SCHEMA: &str = "chio.cross-protocol-cap.v1";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CrossProtocolCapabilityRef {
pub chio_capability_id: String,
pub origin_protocol: DiscoveryProtocol,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub protocol_context: Option<Value>,
pub parent_capability_hash: String,
}
impl CrossProtocolCapabilityRef {
pub fn from_capability(
capability: &CapabilityToken,
origin_protocol: DiscoveryProtocol,
protocol_context: Option<Value>,
) -> Result<Self, BridgeError> {
Ok(Self {
chio_capability_id: capability.id.clone(),
origin_protocol,
protocol_context,
parent_capability_hash: parent_capability_hash(capability)?,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CrossProtocolCapabilityEnvelope {
pub schema: String,
pub capability_ref: CrossProtocolCapabilityRef,
pub target_protocol: DiscoveryProtocol,
pub attenuated_scope: ChioScope,
pub bridged_at: u64,
pub bridge_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProtocolHop {
pub protocol: DiscoveryProtocol,
pub request_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub receipt_id: Option<String>,
pub bridge_id: String,
pub timestamp: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CrossProtocolTraceContext {
pub trace_id: String,
pub hops: Vec<ProtocolHop>,
pub session_fingerprint: String,
}
pub trait CapabilityBridge: Send + Sync {
fn source_protocol(&self) -> DiscoveryProtocol;
fn extract_capability_ref(
&self,
request: &Value,
) -> Result<Option<CrossProtocolCapabilityRef>, BridgeError>;
fn inject_capability_ref(
&self,
envelope: &mut Value,
cap_ref: &CrossProtocolCapabilityRef,
) -> Result<(), BridgeError>;
fn protocol_context(&self, _request: &Value) -> Result<Option<Value>, BridgeError> {
Ok(None)
}
}
pub(crate) fn parent_capability_hash(capability: &CapabilityToken) -> Result<String, BridgeError> {
Ok(sha256_hex(&canonical_json_bytes(capability).map_err(
|error| BridgeError::Canonical(error.to_string()),
)?))
}
pub(crate) fn attenuate_scope_for_tool(
parent: &ChioScope,
server_id: &str,
tool_name: &str,
) -> ChioScope {
let grants = parent
.grants
.iter()
.filter_map(|grant| {
let server_matches = grant.server_id == server_id || grant.server_id == "*";
let tool_matches = grant.tool_name == tool_name || grant.tool_name == "*";
if !server_matches || !tool_matches {
return None;
}
let mut narrowed = grant.clone();
narrowed.server_id = server_id.to_string();
narrowed.tool_name = tool_name.to_string();
Some(narrowed)
})
.collect();
ChioScope {
grants,
resource_grants: vec![],
prompt_grants: vec![],
}
}