(dbPath: string = QODEX_SESSION_DB)
| 148 | private getMessagesStmt: Database.Statement; |
| 149 | |
| 150 | constructor(dbPath: string = QODEX_SESSION_DB) { |
| 151 | this.db = openDatabase(dbPath); |
| 152 | this.db.exec(SCHEMA); |
| 153 | |
| 154 | // Migration: older DBs have session_facts without a `scope` column. Add it, |
| 155 | // defaulting existing rows to 'project' (the only scope that existed before). |
| 156 | const factCols = this.db.prepare(`PRAGMA table_info(session_facts)`).all() as Array<{ name: string }>; |
| 157 | if (!factCols.some(c => c.name === 'scope')) { |
| 158 | this.db.exec(`ALTER TABLE session_facts ADD COLUMN scope TEXT NOT NULL DEFAULT 'project'`); |
| 159 | } |
| 160 | // Safe now that the column is guaranteed to exist (new schema or just-migrated). |
| 161 | this.db.exec(`CREATE INDEX IF NOT EXISTS idx_facts_scope ON session_facts(scope)`); |
| 162 | |
| 163 | this.initFactsFts(); |
| 164 | |
| 165 | this.insertSession = this.db.prepare(` |
| 166 | INSERT INTO sessions (id, cwd, model, title) VALUES (?, ?, ?, ?) |
| 167 | `); |
| 168 | // Bump turn_count only when this batch contains a user message |
| 169 | this.updateSessionWithTurn = this.db.prepare(` |
| 170 | UPDATE sessions SET |
| 171 | updated_at = CURRENT_TIMESTAMP, |
| 172 | total_input_tokens = total_input_tokens + ?, |
| 173 | total_output_tokens = total_output_tokens + ?, |
| 174 | total_cost_usd = total_cost_usd + ?, |
| 175 | turn_count = turn_count + 1, |
| 176 | title = COALESCE(title, ?) |
| 177 | WHERE id = ? |
| 178 | `); |
| 179 | // Update usage without bumping turn_count (for assistant/tool messages) |
| 180 | this.updateSessionNoTurn = this.db.prepare(` |
| 181 | UPDATE sessions SET |
| 182 | updated_at = CURRENT_TIMESTAMP, |
| 183 | total_input_tokens = total_input_tokens + ?, |
| 184 | total_output_tokens = total_output_tokens + ?, |
| 185 | total_cost_usd = total_cost_usd + ? |
| 186 | WHERE id = ? |
| 187 | `); |
| 188 | this.insertMessage = this.db.prepare(` |
| 189 | INSERT INTO messages (session_id, turn_number, role, content, tool_calls_json, tool_call_id, name) |
| 190 | VALUES (?, ?, ?, ?, ?, ?, ?) |
| 191 | `); |
| 192 | this.getMessagesStmt = this.db.prepare(` |
| 193 | SELECT * FROM messages WHERE session_id = ? ORDER BY turn_number ASC, id ASC |
| 194 | `); |
| 195 | } |
| 196 | |
| 197 | createSession(cwd: string, model: string): string { |
| 198 | const id = uuidv4(); |
nothing calls this directly
no test coverage detected