-
Notifications
You must be signed in to change notification settings - Fork 412
feat(core): paginate progress run listings #2531
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@agent-native/core": minor | ||
| --- | ||
|
|
||
| Add keyset pagination to progress-run listing through `ListRunsOptions.before` and the `beforeStartedAt`/`beforeId` HTTP query parameters. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -63,8 +63,8 @@ async function ensureTable(): Promise<void> { | |
| // avoid ACCESS EXCLUSIVE lock contention in fresh background-worker processes. | ||
| await ensureTableExists("progress_runs", createSql); | ||
| await ensureIndexExists( | ||
| "idx_progress_runs_owner_status", | ||
| `CREATE INDEX IF NOT EXISTS idx_progress_runs_owner_status ON progress_runs (owner, status, started_at)`, | ||
| "idx_progress_runs_owner_status_started_id", | ||
| `CREATE INDEX IF NOT EXISTS idx_progress_runs_owner_status_started_id ON progress_runs (owner, status, started_at DESC, id DESC)`, | ||
|
Comment on lines
65
to
+67
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Add an ordering index for unfiltered pagination
Additional Info |
||
| ); | ||
| return; | ||
| } | ||
|
|
@@ -78,7 +78,7 @@ async function ensureTable(): Promise<void> { | |
| await client.execute(createSql); | ||
| try { | ||
| await client.execute( | ||
| `CREATE INDEX IF NOT EXISTS idx_progress_runs_owner_status ON progress_runs (owner, status, started_at)`, | ||
| `CREATE INDEX IF NOT EXISTS idx_progress_runs_owner_status_started_id ON progress_runs (owner, status, started_at DESC, id DESC)`, | ||
| ); | ||
| } catch { | ||
| // Index already exists or the dialect rejected a duplicate. | ||
|
|
@@ -331,9 +331,20 @@ export async function listRuns( | |
| let where = `owner = ?`; | ||
| const args: Array<string | number> = [owner]; | ||
| if (options.activeOnly) where += ` AND status = 'running'`; | ||
| if (options.before) { | ||
| const beforeStartedAt = Date.parse(options.before.startedAt); | ||
| if (!Number.isFinite(beforeStartedAt)) { | ||
| throw new TypeError("before.startedAt must be a valid timestamp"); | ||
| } | ||
| if (!options.before.id) { | ||
| throw new TypeError("before.id must be a non-empty string"); | ||
| } | ||
| where += ` AND (started_at < ? OR (started_at = ? AND id < ?))`; | ||
| args.push(beforeStartedAt, beforeStartedAt, options.before.id); | ||
| } | ||
| args.push(limit); | ||
| const { rows } = await client.execute({ | ||
| sql: `SELECT * FROM progress_runs WHERE ${where} ORDER BY started_at DESC LIMIT ?`, | ||
| sql: `SELECT * FROM progress_runs WHERE ${where} ORDER BY started_at DESC, id DESC LIMIT ?`, | ||
| args, | ||
| }); | ||
| return rows.map((r) => parseRow(r as Record<string, unknown>)); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Reject normalized invalid ISO cursor dates
Date.parseaccepts malformed calendar dates such as2026-02-30T00:00:00.000Zand normalizes them to a different finite timestamp. The route therefore accepts an invalid cursor and queries from the normalized date instead of returning the documented 400, which can silently skip or repeat rows. Require the documented ISO-8601 form and validate a round trip (or use a strict date validator), then add a test for an out-of-range calendar date.Additional Info