lash_remote_protocol/protocol/
registry_errors.rs1pub 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}