codetether_agent/session/tasks/
state.rs1use chrono::{DateTime, Utc};
4use std::collections::BTreeMap;
5
6use super::event::{SessionTaskStatus, TaskEvent};
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: SessionTaskStatus,
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.values().filter(|t| t.status.is_open()).collect()
50 }
51
52 fn apply(&mut self, ev: &TaskEvent) {
53 match ev {
54 TaskEvent::GoalSet {
55 at,
56 objective,
57 success_criteria,
58 forbidden,
59 ..
60 } => {
61 self.goal = Some(Goal {
62 objective: objective.clone(),
63 success_criteria: success_criteria.clone(),
64 forbidden: forbidden.clone(),
65 set_at: *at,
66 last_reaffirmed_at: *at,
67 });
68 }
69 TaskEvent::GoalReaffirmed { at, .. } => {
70 if let Some(g) = self.goal.as_mut() {
71 g.last_reaffirmed_at = *at;
72 }
73 self.tool_calls_since_reaffirm = 0;
74 self.errors_since_reaffirm = 0;
75 }
76 TaskEvent::GoalCleared { .. } => {
77 self.goal = None;
78 }
79 TaskEvent::TaskAdded {
80 id,
81 content,
82 parent_id,
83 ..
84 } => {
85 self.tasks.insert(
86 id.clone(),
87 Task {
88 id: id.clone(),
89 content: content.clone(),
90 parent_id: parent_id.clone(),
91 status: SessionTaskStatus::Pending,
92 last_note: None,
93 },
94 );
95 }
96 TaskEvent::TaskStatus {
97 id, status, note, ..
98 } => {
99 if let Some(t) = self.tasks.get_mut(id) {
100 t.status = status.clone();
101 t.last_note = note.clone();
102 }
103 }
104 TaskEvent::DriftDetected { .. } => {}
105 }
106 }
107}