Skip to main content

lash_remote_protocol/protocol/
registry_errors.rs

1pub trait RemoteToolRegistry {
2    fn grants(&self) -> Vec<RemoteToolGrant>;
3
4    fn validate_registry(&self) -> Result<(), RemoteProtocolError> {
5        RemoteToolGrant::validate_all(&self.grants())
6    }
7}
8
9pub fn assert_remote_tool_registry_reopenable(
10    before: &dyn RemoteToolRegistry,
11    after_reopen: &dyn RemoteToolRegistry,
12) -> Result<(), RemoteProtocolError> {
13    let before_grants = before.grants();
14    let after_grants = after_reopen.grants();
15    RemoteToolGrant::validate_all(&before_grants)?;
16    RemoteToolGrant::validate_all(&after_grants)?;
17    let before_paths = remote_registry_call_paths(&before_grants)?;
18    let after_paths = remote_registry_call_paths(&after_grants)?;
19    if before_paths != after_paths {
20        return Err(RemoteProtocolError::RemoteToolRegistryReopenMismatch {
21            before_call_paths: before_paths,
22            after_call_paths: after_paths,
23        });
24    }
25    Ok(())
26}
27
28fn remote_registry_call_paths(
29    grants: &[RemoteToolGrant],
30) -> Result<Vec<String>, RemoteProtocolError> {
31    let mut call_paths = grants
32        .iter()
33        .map(RemoteToolGrant::call_path_bindings)
34        .collect::<Result<Vec<_>, _>>()?
35        .into_iter()
36        .flatten()
37        .collect::<Vec<_>>();
38    call_paths.sort();
39    Ok(call_paths)
40}
41
42fn require_non_empty(
43    type_name: &'static str,
44    field: &'static str,
45    value: &str,
46) -> Result<(), RemoteProtocolError> {
47    if value.trim().is_empty() {
48        Err(RemoteProtocolError::MissingRequiredField { type_name, field })
49    } else {
50        Ok(())
51    }
52}
53
54#[derive(Debug, thiserror::Error)]
55pub enum RemoteProtocolError {
56    #[error("unsupported remote protocol version {actual}; expected {expected}")]
57    UnsupportedProtocolVersion { actual: u32, expected: u32 },
58    #[error(
59        "mismatched protocol version in {parent}.{child}: got {child_version}, expected {parent_version}"
60    )]
61    MismatchedNestedProtocolVersion {
62        parent: &'static str,
63        child: &'static str,
64        parent_version: u32,
65        child_version: u32,
66    },
67    #[error("{type_name}.{field} is required")]
68    MissingRequiredField {
69        type_name: &'static str,
70        field: &'static str,
71    },
72    #[error("invalid {type_name}: {message}")]
73    InvalidEnvelope {
74        type_name: &'static str,
75        message: String,
76    },
77    #[error("invalid image blob `{id}`: {message}")]
78    InvalidImageBlob { id: String, message: String },
79    #[error("invalid attachment reference `{id}`: {message}")]
80    InvalidAttachmentRef { id: String, message: String },
81    #[error("turn input is not remote-safe: {0}")]
82    NonRemoteSafeTurnInput(String),
83    #[error("remote tool grant `{tool_name}` is missing required binding `{binding}`")]
84    MissingToolBinding { tool_name: String, binding: String },
85    #[error("invalid remote tool grant `{tool_name}`: {message}")]
86    InvalidToolGrant { tool_name: String, message: String },
87    #[error("duplicate remote tool call path `{call_path}`")]
88    DuplicateRemoteCallPath { call_path: String },
89    #[error(
90        "remote tool registry changed across reopen: before={before_call_paths:?}, after={after_call_paths:?}"
91    )]
92    RemoteToolRegistryReopenMismatch {
93        before_call_paths: Vec<String>,
94        after_call_paths: Vec<String>,
95    },
96    #[error("failed to serialize remote activity: {0}")]
97    ActivitySerialization(#[from] serde_json::Error),
98    #[error("failed to write remote activity: {0}")]
99    ActivityWrite(String),
100}