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