1use crate::messaging::AgentMessage;
2use crate::state::{AgentStateSnapshot, TodoItem};
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6#[derive(Debug, Default, Clone, Serialize, Deserialize)]
8pub struct StateDiff {
9 #[serde(default, skip_serializing_if = "Option::is_none")]
10 pub todos: Option<Vec<TodoItem>>,
11 #[serde(default, skip_serializing_if = "Option::is_none")]
12 pub files: Option<BTreeMap<String, String>>,
13 #[serde(default, skip_serializing_if = "Option::is_none")]
14 pub scratchpad: Option<BTreeMap<String, serde_json::Value>>,
15}
16
17#[derive(Debug, Default, Clone, Serialize, Deserialize)]
18pub struct Command {
19 #[serde(default)]
20 pub state: StateDiff,
21 #[serde(default)]
22 pub messages: Vec<AgentMessage>,
23}
24
25impl Command {
26 pub fn with_state(state: StateDiff) -> Self {
27 Self {
28 state,
29 ..Self::default()
30 }
31 }
32
33 pub fn with_messages(messages: Vec<AgentMessage>) -> Self {
34 Self {
35 messages,
36 ..Self::default()
37 }
38 }
39
40 pub fn apply_to(self, snapshot: &mut AgentStateSnapshot) {
41 if let Some(todos) = self.state.todos {
42 snapshot.todos = todos;
43 }
44 if let Some(files) = self.state.files {
45 for (path, content) in files {
46 snapshot.files.insert(path, content);
47 }
48 }
49 if let Some(scratch) = self.state.scratchpad {
50 for (key, value) in scratch {
51 snapshot.scratchpad.insert(key, value);
52 }
53 }
54 }
55}
56
57impl AgentStateSnapshot {
58 pub fn apply_command(&mut self, command: Command) {
59 command.apply_to(self);
60 }
61}