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