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