feat(session-anchors): implement S-2 session anchor persistence and auto-anchoring
This commit is contained in:
parent
8f4faaafa3
commit
a9e94fc154
7 changed files with 616 additions and 1 deletions
|
|
@ -492,6 +492,49 @@ fn session_metrics_load(
|
|||
state.session_db.load_session_metrics(&project_id, limit)
|
||||
}
|
||||
|
||||
// --- Session anchor commands ---
|
||||
|
||||
#[tauri::command]
|
||||
fn session_anchors_save(
|
||||
state: State<'_, AppState>,
|
||||
anchors: Vec<session::SessionAnchorRecord>,
|
||||
) -> Result<(), String> {
|
||||
state.session_db.save_session_anchors(&anchors)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn session_anchors_load(
|
||||
state: State<'_, AppState>,
|
||||
project_id: String,
|
||||
) -> Result<Vec<session::SessionAnchorRecord>, String> {
|
||||
state.session_db.load_session_anchors(&project_id)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn session_anchor_delete(
|
||||
state: State<'_, AppState>,
|
||||
id: String,
|
||||
) -> Result<(), String> {
|
||||
state.session_db.delete_session_anchor(&id)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn session_anchors_clear(
|
||||
state: State<'_, AppState>,
|
||||
project_id: String,
|
||||
) -> Result<(), String> {
|
||||
state.session_db.delete_project_anchors(&project_id)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn session_anchor_update_type(
|
||||
state: State<'_, AppState>,
|
||||
id: String,
|
||||
anchor_type: String,
|
||||
) -> Result<(), String> {
|
||||
state.session_db.update_anchor_type(&id, &anchor_type)
|
||||
}
|
||||
|
||||
// --- File browser commands (Files tab) ---
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
|
|
@ -810,6 +853,11 @@ pub fn run() {
|
|||
project_agent_state_load,
|
||||
session_metric_save,
|
||||
session_metrics_load,
|
||||
session_anchors_save,
|
||||
session_anchors_load,
|
||||
session_anchor_delete,
|
||||
session_anchors_clear,
|
||||
session_anchor_update_type,
|
||||
cli_get_group,
|
||||
pick_directory,
|
||||
open_url,
|
||||
|
|
|
|||
|
|
@ -171,7 +171,20 @@ impl SessionDb {
|
|||
error_message TEXT
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_session_metrics_project
|
||||
ON session_metrics(project_id);"
|
||||
ON session_metrics(project_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS session_anchors (
|
||||
id TEXT PRIMARY KEY,
|
||||
project_id TEXT NOT NULL,
|
||||
message_id TEXT NOT NULL,
|
||||
anchor_type TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
estimated_tokens INTEGER NOT NULL,
|
||||
turn_index INTEGER NOT NULL DEFAULT 0,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_session_anchors_project
|
||||
ON session_anchors(project_id);"
|
||||
).map_err(|e| format!("Migration (v3 tables) failed: {e}"))?;
|
||||
|
||||
Ok(())
|
||||
|
|
@ -597,6 +610,91 @@ impl SessionDb {
|
|||
|
||||
Ok(metrics)
|
||||
}
|
||||
|
||||
// --- Session anchors ---
|
||||
|
||||
pub fn save_session_anchors(&self, anchors: &[SessionAnchorRecord]) -> Result<(), String> {
|
||||
if anchors.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn.prepare(
|
||||
"INSERT OR REPLACE INTO session_anchors (id, project_id, message_id, anchor_type, content, estimated_tokens, turn_index, created_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)"
|
||||
).map_err(|e| format!("Prepare anchor insert failed: {e}"))?;
|
||||
|
||||
for anchor in anchors {
|
||||
stmt.execute(params![
|
||||
anchor.id,
|
||||
anchor.project_id,
|
||||
anchor.message_id,
|
||||
anchor.anchor_type,
|
||||
anchor.content,
|
||||
anchor.estimated_tokens,
|
||||
anchor.turn_index,
|
||||
anchor.created_at,
|
||||
]).map_err(|e| format!("Insert anchor failed: {e}"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_session_anchors(&self, project_id: &str) -> Result<Vec<SessionAnchorRecord>, String> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, project_id, message_id, anchor_type, content, estimated_tokens, turn_index, created_at FROM session_anchors WHERE project_id = ?1 ORDER BY turn_index ASC"
|
||||
).map_err(|e| format!("Query anchors failed: {e}"))?;
|
||||
|
||||
let anchors = stmt.query_map(params![project_id], |row| {
|
||||
Ok(SessionAnchorRecord {
|
||||
id: row.get(0)?,
|
||||
project_id: row.get(1)?,
|
||||
message_id: row.get(2)?,
|
||||
anchor_type: row.get(3)?,
|
||||
content: row.get(4)?,
|
||||
estimated_tokens: row.get(5)?,
|
||||
turn_index: row.get(6)?,
|
||||
created_at: row.get(7)?,
|
||||
})
|
||||
}).map_err(|e| format!("Query anchors failed: {e}"))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("Read anchor row failed: {e}"))?;
|
||||
|
||||
Ok(anchors)
|
||||
}
|
||||
|
||||
pub fn delete_session_anchor(&self, id: &str) -> Result<(), String> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute("DELETE FROM session_anchors WHERE id = ?1", params![id])
|
||||
.map_err(|e| format!("Delete anchor failed: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_project_anchors(&self, project_id: &str) -> Result<(), String> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute("DELETE FROM session_anchors WHERE project_id = ?1", params![project_id])
|
||||
.map_err(|e| format!("Delete project anchors failed: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_anchor_type(&self, id: &str, anchor_type: &str) -> Result<(), String> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"UPDATE session_anchors SET anchor_type = ?2 WHERE id = ?1",
|
||||
params![id, anchor_type],
|
||||
).map_err(|e| format!("Update anchor type failed: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SessionAnchorRecord {
|
||||
pub id: String,
|
||||
pub project_id: String,
|
||||
pub message_id: String,
|
||||
pub anchor_type: String,
|
||||
pub content: String,
|
||||
pub estimated_tokens: i64,
|
||||
pub turn_index: i64,
|
||||
pub created_at: i64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue