1use kernel::IdemKey;
27
28use super::kind::Command;
29use super::no_secret::NoSecret;
30use super::step::{
31 AgentAdding, AgentLoginStart, CityClosing, CitySettings, DeviceForgetting, DoorAnswer,
32 DoorOpening, DoorStep, PolicyChange, ProposalDecisions, RangeWrite, RulesWrite,
33 SecretForgetting, SessionNaming,
34};
35
36pub type WireCommand = Command<NoSecret>;
39
40impl<Secret> Command<Secret> {
41 pub fn idem(&self) -> Option<&IdemKey> {
45 match *self {
46 Self::Dispatch { ref idem, .. }
47 | Self::Wake { ref idem, .. }
48 | Self::OpenSession { ref idem, .. }
49 | Self::ProbeEndpoint { ref idem, .. }
50 | Self::ConfigureBuilding { ref idem, .. }
51 | Self::CreateBuilding { ref idem, .. }
52 | Self::RemoveBuilding { ref idem, .. }
53 | Self::Steer { ref idem, .. }
54 | Self::Cancel { ref idem, .. }
55 | Self::Halt { ref idem, .. }
56 | Self::Release { ref idem, .. }
57 | Self::BatchByBuilding { ref idem, .. }
58 | Self::Approve { ref idem, .. }
59 | Self::HandOff { ref idem, .. }
60 | Self::SetAutonomy { ref idem, .. }
61 | Self::Pursue { ref idem, .. }
62 | Self::PutDocument { ref idem, .. }
63 | Self::PutIdentity { ref idem, .. }
64 | Self::PutRules(RulesWrite { ref idem, .. })
65 | Self::ConfigureCity(CitySettings { ref idem, .. })
66 | Self::RestoreFile { ref idem, .. }
67 | Self::PutGuide { ref idem, .. }
68 | Self::PutSpine { ref idem, .. }
69 | Self::PutRange(RangeWrite { ref idem, .. })
70 | Self::DecideProposals(ProposalDecisions { ref idem, .. })
71 | Self::PutPreferences { ref idem, .. }
72 | Self::PutShelved { ref idem, .. }
73 | Self::AttachEndpoint { ref idem, .. }
74 | Self::SelectModel { ref idem, .. }
75 | Self::Reveal { ref idem, .. }
76 | Self::RestoreDiscard { ref idem, .. }
77 | Self::DoctorInstall { ref idem, .. }
78 | Self::DoctorRefresh { ref idem, .. }
79 | Self::ConnectToolkit { ref idem, .. }
80 | Self::NameSession(SessionNaming { ref idem, .. })
81 | Self::ChangeRunPolicy(PolicyChange { ref idem, .. })
82 | Self::OpenRemoteDoor(DoorOpening { ref idem, .. })
83 | Self::ConfirmRemoteDoor(DoorAnswer { ref idem, .. })
84 | Self::ReplaceCityKey(DoorStep { ref idem })
85 | Self::CloseRemoteDoor(DoorStep { ref idem })
86 | Self::PrivacyOperation(crate::PrivacyRequest { ref idem, .. })
87 | Self::ForgetSecret(SecretForgetting { ref idem, .. })
88 | Self::CloseCity(CityClosing { ref idem, .. })
89 | Self::AddAgent(AgentAdding { ref idem, .. })
90 | Self::AgentLogin(AgentLoginStart { ref idem, .. })
91 | Self::ForgetDevice(DeviceForgetting { ref idem, .. }) => Some(idem),
92 Self::PutSecret { .. } | Self::Auth { .. } => None,
93 }
94 }
95}
96
97impl From<WireCommand> for Command {
98 fn from(wire: WireCommand) -> Self {
101 match wire {
102 #[rustfmt::skip]
104 Command::Wake { source, subject, body, idem } => Self::Wake { source, subject, body, idem },
105 Command::Dispatch {
106 addr,
107 task,
108 goal,
109 policy,
110 idem,
111 session,
112 effort,
113 model,
114 } => Self::Dispatch {
115 addr,
116 task,
117 goal,
118 policy,
119 idem,
120 session,
121 effort,
122 model,
123 },
124 Command::ConfigureBuilding {
125 addr,
126 sandbox,
127 mcp,
128 desktop,
129 context_second_threshold,
130 idem,
131 } => Self::ConfigureBuilding {
132 addr,
133 sandbox,
134 mcp,
135 desktop,
136 context_second_threshold,
137 idem,
138 },
139 Command::ProbeEndpoint {
140 name,
141 base_url,
142 dialect,
143 secret,
144 auth_header,
145 tuning,
146 idem,
147 } => Self::ProbeEndpoint {
148 name,
149 base_url,
150 dialect,
151 secret,
152 auth_header,
153 tuning,
154 idem,
155 },
156 Command::AttachEndpoint {
157 name,
158 base_url,
159 dialect,
160 secret,
161 auth_header,
162 admit,
163 tuning,
164 idem,
165 } => Self::AttachEndpoint {
166 name,
167 base_url,
168 dialect,
169 secret,
170 auth_header,
171 admit,
172 tuning,
173 idem,
174 },
175 Command::SelectModel {
176 endpoint,
177 model,
178 tag,
179 context_tokens,
180 max_output_tokens,
181 input,
182 idem,
183 } => Self::SelectModel {
184 endpoint,
185 model,
186 tag,
187 context_tokens,
188 max_output_tokens,
189 input,
190 idem,
191 },
192 Command::OpenSession {
193 addr,
194 carry,
195 from,
196 idem,
197 } => Self::OpenSession {
198 addr,
199 carry,
200 from,
201 idem,
202 },
203 #[rustfmt::skip]
205 Command::CreateBuilding { addr, template, idem } =>
206 Self::CreateBuilding { addr, template, idem },
207 Command::PutSecret { value, .. } => match value {},
208 Command::Steer { run, text, idem } => Self::Steer { run, text, idem },
209 Command::Cancel { run, idem } => Self::Cancel { run, idem },
210 Command::Halt { scope, idem } => Self::Halt { scope, idem },
211 Command::Reveal { at, idem } => Self::Reveal { at, idem },
212 Command::RestoreDiscard { restoration, idem } => {
213 Self::RestoreDiscard { restoration, idem }
214 }
215 Command::RemoveBuilding { addr, idem } => Self::RemoveBuilding { addr, idem },
216 Command::DoctorInstall { item, idem } => Self::DoctorInstall { item, idem },
217 Command::DoctorRefresh { idem } => Self::DoctorRefresh { idem },
218 Command::ConnectToolkit { toolkit, idem } => Self::ConnectToolkit { toolkit, idem },
219 Command::Release { scope, idem } => Self::Release { scope, idem },
220 Command::BatchByBuilding { addr, idem } => Self::BatchByBuilding { addr, idem },
221 Command::Approve {
222 item,
223 verdict,
224 idem,
225 } => Self::Approve {
226 item,
227 verdict,
228 idem,
229 },
230 Command::HandOff { item, to, idem } => Self::HandOff { item, to, idem },
231 Command::Pursue { addr, step, idem } => Self::Pursue { addr, step, idem },
232 Command::SetAutonomy {
233 scope,
234 autonomy,
235 idem,
236 } => Self::SetAutonomy {
237 scope,
238 autonomy,
239 idem,
240 },
241 Command::PutDocument {
242 which,
243 base,
244 body,
245 idem,
246 } => Self::PutDocument {
247 which,
248 base,
249 body,
250 idem,
251 },
252 Command::PutIdentity { card, base, idem } => Self::PutIdentity { card, base, idem },
253 Command::PutRules(write) => Self::PutRules(write),
254 Command::RestoreFile { at, point, idem } => Self::RestoreFile { at, point, idem },
255 Command::PutGuide { progress, idem } => Self::PutGuide { progress, idem },
256 Command::ConfigureCity(settings) => Self::ConfigureCity(settings),
257 Command::PutRange(write) => Self::PutRange(write),
258 Command::DecideProposals(decisions) => Self::DecideProposals(decisions),
259 Command::PutSpine {
260 building,
261 which,
262 base,
263 body,
264 idem,
265 } => Self::PutSpine {
266 building,
267 which,
268 base,
269 body,
270 idem,
271 },
272 Command::PutPreferences { patch, idem } => Self::PutPreferences { patch, idem },
273 Command::NameSession(naming) => Self::NameSession(naming),
274 Command::ChangeRunPolicy(change) => Self::ChangeRunPolicy(change),
275 Command::OpenRemoteDoor(opening) => Self::OpenRemoteDoor(opening),
276 Command::ReplaceCityKey(step) => Self::ReplaceCityKey(step),
277 Command::ConfirmRemoteDoor(answer) => Self::ConfirmRemoteDoor(answer),
278 Command::CloseRemoteDoor(step) => Self::CloseRemoteDoor(step),
279 Command::PrivacyOperation(request) => Self::PrivacyOperation(request),
280 Command::ForgetSecret(forgetting) => Self::ForgetSecret(forgetting),
281 Command::CloseCity(closing) => Self::CloseCity(closing),
282 Command::AddAgent(adding) => Self::AddAgent(adding),
283 Command::AgentLogin(login) => Self::AgentLogin(login),
284 Command::ForgetDevice(forgetting) => Self::ForgetDevice(forgetting),
285 Command::PutShelved {
286 shelf,
287 name,
288 text,
289 idem,
290 } => Self::PutShelved {
291 shelf,
292 name,
293 text,
294 idem,
295 },
296 Command::Auth { token } => Self::Auth { token },
297 }
298 }
299}
300
301#[cfg(test)]
302#[allow(
303 clippy::float_arithmetic,
304 clippy::unwrap_used,
305 clippy::expect_used,
306 clippy::panic,
307 clippy::indexing_slicing,
308 clippy::string_slice,
309 clippy::arithmetic_side_effects,
310 reason = "test code"
311)]
312mod tests {
313 use super::*;
314 #[test]
318 fn a_wire_frame_carrying_put_secret_fails_to_decode() {
319 let json = r#"{"put_secret":{"realm":"anthropic","name":"api","value":"sk-nope"}}"#;
320 let decoded: Result<WireCommand, _> = serde_json::from_str(json);
321 let err = decoded.expect_err("PutSecret has no wire form");
322 assert!(
323 !err.to_string().contains("sk-nope"),
324 "the refusal must not echo the bytes it protects"
325 );
326 }
327}