package store import ( "context" "database/sql" "errors" "time" "g.fc.run/misc/byre/internal/api" ) type QueuedTurn struct { SessionID string Vendor api.AgentSessionVendor Actor string Text string CreatedAt time.Time } func (s *Store) SessionStarted(ctx context.Context, sessionID string) (bool, error) { started := false err := s.db.QueryRowContext(ctx, `SELECT EXISTS (SELECT 1 FROM events WHERE agent_session_id = ? AND kind = 'user.message')`, sessionID).Scan(&started) return started, err } func (s *Store) CountRunning(ctx context.Context) (map[api.AgentSessionVendor]int, error) { rows, err := s.db.QueryContext(ctx, `SELECT vendor, COUNT(*) FROM agent_sessions WHERE state = ? GROUP BY vendor`, string(api.AgentSessionStateRunning)) if err != nil { return nil, err } defer rows.Close() counts := map[api.AgentSessionVendor]int{} for rows.Next() { vendor := "" n := 0 err = rows.Scan(&vendor, &n) if err != nil { return nil, err } counts[api.AgentSessionVendor(vendor)] = n } return counts, rows.Err() } func (s *Store) EnqueueTurn(ctx context.Context, q QueuedTurn) error { return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error { _, err := tx.ExecContext(ctx, `INSERT INTO turn_queue (session_id, actor, text, created_at) VALUES (?, ?, ?, ?)`, q.SessionID, q.Actor, q.Text, formatTime(q.CreatedAt)) return err }) } func (s *Store) QueuedTurns(ctx context.Context) ([]QueuedTurn, error) { rows, err := s.db.QueryContext(ctx, ` SELECT q.session_id, s.vendor, q.actor, q.text, q.created_at FROM turn_queue q JOIN agent_sessions s ON s.id = q.session_id ORDER BY q.created_at `) if err != nil { return nil, err } defer rows.Close() turns := []QueuedTurn{} for rows.Next() { q := QueuedTurn{} vendor := "" createdAt := "" err = rows.Scan(&q.SessionID, &vendor, &q.Actor, &q.Text, &createdAt) if err != nil { return nil, err } q.Vendor = api.AgentSessionVendor(vendor) q.CreatedAt, err = parseTime(createdAt) if err != nil { return nil, err } turns = append(turns, q) } return turns, rows.Err() } func (s *Store) DeleteQueuedTurn(ctx context.Context, sessionID string) error { return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error { res, err := tx.ExecContext(ctx, `DELETE FROM turn_queue WHERE session_id = ?`, sessionID) if err != nil { return err } n, err := res.RowsAffected() if err != nil { return err } if n == 0 { return errors.Join(ErrNotFound, errors.New("no queued turn for session "+sessionID)) } return nil }) }