feat(sdk): add bulk replay to api and sdk (#4105)

## Summary

Adds SDK and API support for run bulk actions. You can now create bulk
cancel or replay actions from `@trigger.dev/sdk` using run IDs or the
same filters as `runs.list()`, then retrieve, list, poll, or abort the
action by its `bulk_` handle.

Tests, docs, changesets added.

## Design

The dashboard bulk action service now accepts structured filters instead
of reading directly from a dashboard request, so the dashboard and API
share the same creation path. Replay actions created through the API are
attributed with the existing `api` trigger source, while
dashboard-created actions keep `dashboard`.

The SDK exposes the new surface under `runs.bulk.*`, including
`targetRegion` for replay region overrides and cursor pagination for
listing bulk actions.

## Filters and runIds

Nuance on filters. If `filter` is provided, it MUST have at least one
key. This is to remove the footgun of passing no filter and selecting
all runs.

```typescript
   { action: "cancel", runIds: ["run_1"] } // valid
   { action: "cancel", runIds: [] } // invalid, min(1)
   { action: "cancel", filter: { status: "FAILED" } } // valid
   { action: "cancel", filter: {} } // invalid
   { action: "cancel", filter: {}, runIds: ["run_1"] } // invalid
```
This commit is contained in:
Chris Arderne
2026-07-07 15:43:30 +01:00
committed by GitHub
parent d59743bd35
commit aa74e68c71
21 changed files with 1765 additions and 56 deletions
@@ -0,0 +1,5 @@
-- Backs the per-environment concurrent-replay limit: count of PENDING REPLAY groups.
-- Not partial (e.g. WHERE status = 'PENDING' AND type = 'REPLAY') as wouldn't be used
-- with the bind params from prisma.
CREATE INDEX CONCURRENTLY IF NOT EXISTS "BulkActionGroup_environmentId_status_type_idx"
ON "BulkActionGroup" ("environmentId", "status", "type");
@@ -2521,6 +2521,11 @@ model BulkActionGroup {
// the INCLUDE/fillfactor indexes elsewhere in this schema). `migrate dev` will report
// drift here; do NOT accept its drop/recreate — keep the hand-written migration.
@@index([environmentId, type, dedupeKey])
// Backs the per-environment concurrent-replay limit (count of PENDING REPLAY groups).
// Plain composite (not partial) so Prisma's parameterized count reliably uses it; the
// migration creates it CONCURRENTLY to avoid locking writes. See migration
// 20260702120000_bulk_action_group_pending_replay_index.
@@index([environmentId, status, type])
}
enum BulkActionType {