package runtime import ( "database/sql" "errors" "net/url" "os" "strings" "codex-agent-manager/internal/codexhome" _ "modernc.org/sqlite" ) type Store struct { CodexHome string } func (s Store) Snapshot() (Snapshot, error) { statePath, err := codexhome.ResolveInside(s.CodexHome, "state_5.sqlite") if err != nil { return Snapshot{}, err } goalsPath, err := codexhome.ResolveInside(s.CodexHome, "goals_1.sqlite") if err != nil { return Snapshot{}, err } stateExists := fileExists(statePath) goalsExists := fileExists(goalsPath) sources := SourceMap{ "state": sqliteSource("state", statePath, stateExists), "goals": sqliteSource("goals", goalsPath, goalsExists), } snapshot := Snapshot{ Threads: []Thread{}, SpawnEdges: []SpawnEdge{}, Goals: []Goal{}, Source: aggregateSource(sources), Sources: sources, } if stateExists { db, err := openReadonlySQLite(statePath) if err != nil { return Snapshot{}, err } defer db.Close() snapshot.Threads, err = readThreads(db, statePath) if err != nil { return Snapshot{}, err } snapshot.SpawnEdges, err = readSpawnEdges(db, statePath) if err != nil { return Snapshot{}, err } } if goalsExists { db, err := openReadonlySQLite(goalsPath) if err != nil { return Snapshot{}, err } defer db.Close() snapshot.Goals, err = readGoals(db, goalsPath) if err != nil { return Snapshot{}, err } } return snapshot, nil } func sqliteSource(name string, path string, exists bool) SourceEvidence { if exists { return SourceEvidence{Kind: "sqlite_readonly", Path: path, Confidence: "high"} } return SourceEvidence{ Kind: "sqlite_missing", Path: path, Confidence: "low", Message: name + " SQLite file was not found; returning empty data for that source.", } } func aggregateSource(sources SourceMap) SourceEvidence { state := sources["state"] goals := sources["goals"] if state.Kind == "sqlite_readonly" && goals.Kind == "sqlite_readonly" { return SourceEvidence{Kind: "sqlite_readonly", Confidence: "high"} } if state.Kind == "sqlite_missing" && goals.Kind == "sqlite_missing" { return SourceEvidence{ Kind: "sqlite_missing", Confidence: "low", Message: "Codex SQLite files were not found; returning an empty read-only snapshot.", } } return SourceEvidence{ Kind: "sqlite_partial", Confidence: "partial", Message: "One Codex SQLite source is missing; available data was read from existing sources only.", } } func openReadonlySQLite(path string) (*sql.DB, error) { uri := url.URL{Scheme: "file", Path: path} query := uri.Query() query.Set("mode", "ro") query.Set("immutable", "1") uri.RawQuery = query.Encode() return sql.Open("sqlite", uri.String()) } func readThreads(db *sql.DB, sourcePath string) ([]Thread, error) { rows, err := db.Query(`SELECT id, role, status, created_at, updated_at FROM threads ORDER BY created_at, id`) if err != nil { if isMissingTable(err) { return []Thread{}, nil } return nil, err } defer rows.Close() var threads []Thread for rows.Next() { var item Thread if err := rows.Scan(&item.ID, &item.Role, &item.Status, &item.CreatedAt, &item.UpdatedAt); err != nil { return nil, err } item.Source = SourceEvidence{Kind: "sqlite_table", Path: sourcePath, Confidence: "high"} threads = append(threads, item) } return threads, rows.Err() } func readSpawnEdges(db *sql.DB, sourcePath string) ([]SpawnEdge, error) { rows, err := db.Query(`SELECT from_thread_id, to_thread_id, reason, created_at FROM thread_spawn_edges ORDER BY created_at, from_thread_id, to_thread_id`) if err != nil { if isMissingTable(err) { return []SpawnEdge{}, nil } return nil, err } defer rows.Close() var edges []SpawnEdge for rows.Next() { var item SpawnEdge if err := rows.Scan(&item.FromThreadID, &item.ToThreadID, &item.Reason, &item.CreatedAt); err != nil { return nil, err } item.Source = SourceEvidence{Kind: "sqlite_table", Path: sourcePath, Confidence: "high"} edges = append(edges, item) } return edges, rows.Err() } func readGoals(db *sql.DB, sourcePath string) ([]Goal, error) { rows, err := db.Query(`SELECT thread_id, goal, status, updated_at FROM thread_goals ORDER BY updated_at, thread_id`) if err != nil { if isMissingTable(err) { return []Goal{}, nil } return nil, err } defer rows.Close() var goals []Goal for rows.Next() { var item Goal if err := rows.Scan(&item.ThreadID, &item.Goal, &item.Status, &item.UpdatedAt); err != nil { return nil, err } item.Source = SourceEvidence{Kind: "sqlite_table", Path: sourcePath, Confidence: "high"} goals = append(goals, item) } return goals, rows.Err() } func fileExists(path string) bool { info, err := os.Stat(path) return err == nil && !info.IsDir() } func isMissingTable(err error) bool { return err != nil && (strings.Contains(err.Error(), "no such table") || errors.Is(err, sql.ErrNoRows)) }