package history import ( "context" "fmt" ) // ListAfter returns up to limit messages for sessionID strictly newer than after // (created_at > after) in oldest-first order, for forward incremental catch-up: a // client that reconnected passes the newest createdAt it has already seen and pulls // only what it missed, however many messages that is (the "before" cursor only walks // backwards and cannot recover a gap larger than one page). When platform is non-empty // the page is further restricted to messages carrying that source platform, exactly as // in List. rowid breaks created_at ties so messages written in the same millisecond // keep their insertion order. The returned slice is non-nil; limit is clamped to // [1, maxPageLimit]. func (s *Store) ListAfter(ctx context.Context, sessionID string, after int64, limit int, platform string) ([]Message, error) { limit = clampLimit(limit) query := `SELECT id, session_id, role, content, mode, mood, image, thinking, platform, device_id, sender_name, adapter_id, created_at FROM chat_messages WHERE session_id = ? AND created_at > ?` args := []any{sessionID, after} if platform != "" { query += ` AND platform = ?` args = append(args, platform) } query += ` ORDER BY created_at ASC, rowid ASC LIMIT ?` args = append(args, limit) rows, err := s.db.QueryContext(ctx, query, args...) if err != nil { return nil, fmt.Errorf("history: list after %s: %w", sessionID, err) } defer func() { _ = rows.Close() }() msgs := make([]Message, 0) for rows.Next() { m, err := scanMessage(rows) if err != nil { return nil, fmt.Errorf("history: scan list after %s: %w", sessionID, err) } msgs = append(msgs, m) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("history: iterate list after %s: %w", sessionID, err) } return msgs, nil }