feat: sort AI sessions by last prompt time - #24440
Conversation
There was a problem hiding this comment.
Pull request overview
Updates AI Bridge session listing to sort sessions by most recent user prompt time (with a fallback to session start time), exposes this value as last_active_at in the API, and updates the UI/docs accordingly.
Changes:
- Update
ListAIBridgeSessionsSQL to computelast_active_at(latest prompt timestamp) and sort byCOALESCE(last_active_at, started_at) DESC. - Expose
last_active_atthrough the Go SDK, generated TS types, Swagger, and docs; update the UI to display it. - Add a dedicated test case covering the new sort semantics.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| site/src/pages/AIBridgePage/ListSessionsPage/ListSessionsRow.tsx | Display last_active_at (fallback to started_at) in the sessions list UI. |
| site/src/api/typesGenerated.ts | Add last_active_at to the generated TS AIBridgeSession type. |
| enterprise/coderd/aibridge_test.go | Update ordering comment and add a new test validating sort-by-last-active behavior. |
| docs/reference/api/schemas.md | Document last_active_at in the schema examples/table. |
| docs/reference/api/aibridge.md | Add last_active_at to the endpoint response example. |
| codersdk/aibridge.go | Add LastActiveAt to codersdk.AIBridgeSession. |
| coderd/database/sqlc.yaml | Add sqlc type override for session_page.last_active_at. |
| coderd/database/queries/aibridge.sql | Implement prompt-based aggregation and sorting; return last_active_at. |
| coderd/database/queries.sql.go | Regenerate SQL output for new columns/order. |
| coderd/database/modelqueries.go | Scan the new last_active_at column. |
| coderd/database/db2sdk/db2sdk.go | Map DB last_active_at into the SDK session response. |
| coderd/apidoc/swagger.json | Add last_active_at to API schema. |
| coderd/apidoc/docs.go | Update embedded Swagger template for last_active_at. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
dannykopping
left a comment
There was a problem hiding this comment.
Can you also please add a negative test case such that when a session's root interception has no associated prompt we still get the expected result?
Previously, the sessions list was sorted by MIN(started_at) across interceptions, so sessions with old start times but recent activity would appear at the bottom. ListAIBridgeSessions now joins aibridge_user_prompts (via a pre-aggregated subquery to keep the join one-to-one) and sorts by COALESCE(MAX(prompt.created_at), MIN(started_at)) DESC. Sessions with prompts surface by last activity; sessions with no prompts fall back to their start time. The sort key is also exposed as last_active_at in the API response. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…ions query Replace the derived subquery (full-table GROUP BY over all prompts) with a LEFT JOIN LATERAL that fetches the latest prompt per interception via LIMIT 1. The existing (interception_id, created_at DESC) composite index makes this an index-only lookup per interception row instead of a scan over the entire aibridge_user_prompts table. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Existing test sessions were missing user prompts, which relied on the COALESCE(MAX(latest_prompt_at), MIN(started_at)) fallback for sort_at rather than exercising the prompt-based sort path. Add a prompt to each test session so the sort exercises last_active_at directly. Also add PromptlessSessionSortsByStartedAt to explicitly cover the fallback: a session with no prompts must still appear in results and sort by its started_at, not disappear due to a NULL sort_at in the HAVING row-value comparison. Document both behaviors in the SQL query comments.
b24d723 to
714936a
Compare
dannykopping
left a comment
There was a problem hiding this comment.
Looking good! A few things to correct but directionally making sense 👌
…ssions Addresses reviewer feedback asking why we need both last_active_at and sort_at. We don't — COALESCE(MAX(prompt.created_at), MIN(started_at)) produces a single non-nullable column that serves both purposes. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…ml override Explicit ::timestamptz cast lets sqlc infer time.Time directly, removing the need for the manual column override in sqlc.yaml. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
dannykopping
left a comment
There was a problem hiding this comment.
LGTM, thanks a bunch @jeremyruppel!
I think we also discussed making the date a bit more clear in the table:
"Timestamp" -> "Last prompt at" or something like that
Happy for this to be a follow-up
040d869 to
4fece9d
Compare
Previously, the sessions list sorted by
MIN(started_at)across interceptions, so sessions with old start times but recent activity would sink to the bottom of the list regardless of how recently they were used.ListAIBridgeSessionsnow sorts byCOALESCE(MAX(prompt.created_at), MIN(started_at)) DESC, exposed as the non-nullablelast_active_atfield. Sessions with prompts surface by last activity; sessions with no prompts fall back to their start time.The original implementation used two separate columns (
last_active_atas a nullable prompt timestamp andsort_atas the non-nullable cursor key). This revision collapses them into a singlelast_active_atthat is always set — simplifying the SQL, the Go conversion, the API type, and the frontend.🤖 Generated with Claude Code