Skip to main content

kaizen/store/sqlite/
sessions.rs

1use super::rows::*;
2use super::*;
3
4impl Store {
5    pub fn upsert_session(&self, s: &SessionRecord) -> Result<()> {
6        self.conn.execute(
7            "INSERT INTO sessions (
8                id, agent, model, workspace, started_at_ms, ended_at_ms, status, trace_path,
9                start_commit, end_commit, branch, dirty_start, dirty_end, repo_binding_source,
10                prompt_fingerprint, parent_session_id, agent_version, os, arch,
11                repo_file_count, repo_total_loc
12             )
13             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15,
14                ?16, ?17, ?18, ?19, ?20, ?21)
15             ON CONFLICT(id) DO UPDATE SET
16               agent=excluded.agent, model=excluded.model, workspace=excluded.workspace,
17               started_at_ms=excluded.started_at_ms, ended_at_ms=excluded.ended_at_ms,
18               status=excluded.status, trace_path=excluded.trace_path,
19               start_commit=excluded.start_commit, end_commit=excluded.end_commit,
20               branch=excluded.branch, dirty_start=excluded.dirty_start,
21               dirty_end=excluded.dirty_end, repo_binding_source=excluded.repo_binding_source,
22               prompt_fingerprint=excluded.prompt_fingerprint,
23               parent_session_id=excluded.parent_session_id,
24               agent_version=excluded.agent_version, os=excluded.os, arch=excluded.arch,
25               repo_file_count=excluded.repo_file_count, repo_total_loc=excluded.repo_total_loc",
26            params![
27                s.id,
28                s.agent,
29                s.model,
30                s.workspace,
31                s.started_at_ms as i64,
32                s.ended_at_ms.map(|v| v as i64),
33                format!("{:?}", s.status),
34                s.trace_path,
35                s.start_commit,
36                s.end_commit,
37                s.branch,
38                s.dirty_start.map(bool_to_i64),
39                s.dirty_end.map(bool_to_i64),
40                s.repo_binding_source.clone().unwrap_or_default(),
41                s.prompt_fingerprint.as_deref(),
42                s.parent_session_id.as_deref(),
43                s.agent_version.as_deref(),
44                s.os.as_deref(),
45                s.arch.as_deref(),
46                s.repo_file_count.map(|v| v as i64),
47                s.repo_total_loc.map(|v| v as i64),
48            ],
49        )?;
50        self.conn.execute(
51            "INSERT INTO session_repo_binding (
52                session_id, start_commit, end_commit, branch, dirty_start, dirty_end, repo_binding_source
53             ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
54             ON CONFLICT(session_id) DO UPDATE SET
55                start_commit=excluded.start_commit,
56                end_commit=excluded.end_commit,
57                branch=excluded.branch,
58                dirty_start=excluded.dirty_start,
59                dirty_end=excluded.dirty_end,
60                repo_binding_source=excluded.repo_binding_source",
61            params![
62                s.id,
63                s.start_commit,
64                s.end_commit,
65                s.branch,
66                s.dirty_start.map(bool_to_i64),
67                s.dirty_end.map(bool_to_i64),
68                s.repo_binding_source.clone().unwrap_or_default(),
69            ],
70        )?;
71        Ok(())
72    }
73
74    /// Insert a minimal session row if none exists. Used by hook ingestion when
75    /// the first observed event is not `SessionStart` (hooks installed mid-session).
76    pub fn ensure_session_stub(
77        &self,
78        id: &str,
79        agent: &str,
80        workspace: &str,
81        started_at_ms: u64,
82    ) -> Result<()> {
83        self.conn.execute(
84            "INSERT OR IGNORE INTO sessions (
85                id, agent, model, workspace, started_at_ms, ended_at_ms, status, trace_path,
86                start_commit, end_commit, branch, dirty_start, dirty_end, repo_binding_source,
87                prompt_fingerprint, parent_session_id, agent_version, os, arch, repo_file_count, repo_total_loc
88             ) VALUES (?1, ?2, NULL, ?3, ?4, NULL, 'Running', '', NULL, NULL, NULL, NULL, NULL, '',
89                NULL, NULL, NULL, NULL, NULL, NULL, NULL)",
90            params![id, agent, workspace, started_at_ms as i64],
91        )?;
92        Ok(())
93    }
94
95    pub fn enrich_session_identity(
96        &self,
97        id: &str,
98        agent: Option<&str>,
99        model: Option<&str>,
100        trace_path: Option<&str>,
101    ) -> Result<()> {
102        self.conn.execute(
103            "UPDATE sessions SET agent = COALESCE(?2, agent), model = COALESCE(?3, model),
104             trace_path = CASE WHEN COALESCE(?4, '') = '' THEN trace_path ELSE ?4 END WHERE id = ?1",
105            params![id, agent, model, trace_path],
106        )?;
107        Ok(())
108    }
109
110    /// Update only status for existing session.
111    pub fn update_session_status(&self, id: &str, status: SessionStatus) -> Result<()> {
112        self.conn.execute(
113            "UPDATE sessions SET status = ?1 WHERE id = ?2",
114            params![format!("{:?}", status), id],
115        )?;
116        Ok(())
117    }
118}