codetether_agent/session/tasks/
state.rs1use chrono::{DateTime, Utc};
4use std::collections::BTreeMap;
5
6use super::event::{TaskEvent, TaskStatus};
7
8#[derive(Clone, Debug)]
10pub struct Goal {
11 pub objective: String,
12 pub success_criteria: Vec<String>,
13 pub forbidden: Vec<String>,
14 pub set_at: DateTime<Utc>,
15 pub last_reaffirmed_at: DateTime<Utc>,
16}
17
18#[derive(Clone, Debug)]
20pub struct Task {
21 pub id: String,
22 pub content: String,
23 pub parent_id: Option<String>,
24 pub status: TaskStatus,
25 pub last_note: Option<String>,
26}
27
28#[derive(Clone, Debug, Default)]
30pub struct TaskState {
31 pub goal: Option<Goal>,
32 pub tasks: BTreeMap<String, Task>,
33 pub tool_calls_since_reaffirm: u32,
34 pub errors_since_reaffirm: u32,
35}
36
37impl TaskState {
38 pub fn from_log(events: &[TaskEvent]) -> Self {
40 let mut state = Self::default();
41 for ev in events {
42 state.apply(ev);
43 }
44 state
45 }
46
47 pub fn open_tasks(&self) -> Vec<&Task> {
49 self.tasks
50 .values()
51 .filter(|t| matches!(t.status, TaskStatus::Pending | TaskStatus::InProgress))
52 .collect()
53 }
54
55 fn apply(&mut self, ev: &TaskEvent) {
56 match ev {
57 TaskEvent::GoalSet {
58 at,
59 objective,
60 success_criteria,
61 forbidden,
62 } => {
63 self.goal = Some(Goal {
64 objective: objective.clone(),
65 success_criteria: success_criteria.clone(),
66 forbidden: forbidden.clone(),
67 set_at: *at,
68 last_reaffirmed_at: *at,
69 });
70 }
71 TaskEvent::GoalReaffirmed { at, .. } => {
72 if let Some(g) = self.goal.as_mut() {
73 g.last_reaffirmed_at = *at;
74 }
75 self.tool_calls_since_reaffirm = 0;
76 self.errors_since_reaffirm = 0;
77 }
78 TaskEvent::GoalCleared { .. } => {
79 self.goal = None;
80 }
81 TaskEvent::TaskAdded {
82 id,
83 content,
84 parent_id,
85 ..
86 } => {
87 self.tasks.insert(
88 id.clone(),
89 Task {
90 id: id.clone(),
91 content: content.clone(),
92 parent_id: parent_id.clone(),
93 status: TaskStatus::Pending,
94 last_note: None,
95 },
96 );
97 }
98 TaskEvent::TaskStatus {
99 id, status, note, ..
100 } => {
101 if let Some(t) = self.tasks.get_mut(id) {
102 t.status = status.clone();
103 t.last_note = note.clone();
104 }
105 }
106 TaskEvent::DriftDetected { .. } => {}
107 }
108 }
109}