1use super::*;
2
3fn now_utc() -> String {
4 let d = std::time::SystemTime::now()
5 .duration_since(std::time::UNIX_EPOCH)
6 .unwrap_or_default();
7 let secs = d.as_secs();
8 let nanos = d.subsec_nanos();
9 let (days, rem) = (secs / 86_400, secs % 86_400);
10 let (h, rem) = (rem / 3_600, rem % 3_600);
11 let (m, s) = (rem / 60, rem % 60);
12 let (mut y, mut mo, mut day) = (1970i64, 1u32, 1u32);
13 let mut remaining = days as i64;
14 loop {
15 let yd = if y % 4 == 0 && (y % 100 != 0 || y % 400 == 0) {
16 366
17 } else {
18 365
19 };
20 if remaining < yd {
21 break;
22 }
23 remaining -= yd;
24 y += 1;
25 }
26 let leap = y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
27 let mdays = [
28 31,
29 if leap { 29 } else { 28 },
30 31,
31 30,
32 31,
33 30,
34 31,
35 31,
36 30,
37 31,
38 30,
39 31,
40 ];
41 for md in mdays {
42 if remaining < md as i64 {
43 break;
44 }
45 remaining -= md as i64;
46 mo += 1;
47 }
48 day += remaining as u32;
49 format!(
50 "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:09}",
51 y, mo, day, h, m, s, nanos
52 )
53}
54
55pub fn new_snapshot(
56 session_id: String,
57 cwd: PathBuf,
58 store_path: PathBuf,
59 model: String,
60 base_url: String,
61 backend: BackendKind,
62 reasoning_effort: Option<ReasoningEffort>,
63 sandbox_spec: Option<SandboxSpec>,
64 messages: Vec<Message>,
65) -> SessionSnapshot {
66 tracing::debug!(
67 session_id = %session_id,
68 cwd = %cwd.display(),
69 store_path = %store_path.display(),
70 model = %model,
71 backend = ?backend,
72 message_count = messages.len(),
73 sandboxed = sandbox_spec.is_some(),
74 "constructing new session snapshot"
75 );
76 let now = now_utc();
77 SessionSnapshot {
78 session_id,
79 cwd,
80 store_path,
81 model,
82 base_url,
83 backend,
84 reasoning_effort,
85 sandbox_spec,
86 messages,
87 last_response_duration_ms: None,
88 previous_response_duration_ms: None,
89 response_durations_ms: None,
90 timeline_json: None,
91 created_at: now.clone(),
92 updated_at: now,
93 }
94}
95
96pub fn refresh_snapshot(
97 snapshot: &SessionSnapshot,
98 messages: Vec<Message>,
99 last_response_duration_ms: Option<u64>,
100 previous_response_duration_ms: Option<u64>,
101 response_durations_ms: Option<Vec<Option<u64>>>,
102 timeline_json: Option<String>,
103) -> SessionSnapshot {
104 tracing::debug!(
105 session_id = %snapshot.session_id,
106 cwd = %snapshot.cwd.display(),
107 store_path = %snapshot.store_path.display(),
108 model = %snapshot.model,
109 backend = ?snapshot.backend,
110 message_count = messages.len(),
111 sandboxed = snapshot.sandbox_spec.is_some(),
112 "refreshing session snapshot"
113 );
114 SessionSnapshot {
115 session_id: snapshot.session_id.clone(),
116 cwd: snapshot.cwd.clone(),
117 store_path: snapshot.store_path.clone(),
118 model: snapshot.model.clone(),
119 base_url: snapshot.base_url.clone(),
120 backend: snapshot.backend,
121 reasoning_effort: snapshot.reasoning_effort.clone(),
122 sandbox_spec: snapshot.sandbox_spec.clone(),
123 messages,
124 last_response_duration_ms,
125 previous_response_duration_ms,
126 response_durations_ms,
127 timeline_json,
128 created_at: snapshot.created_at.clone(),
129 updated_at: now_utc(),
130 }
131}