Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: triggerdotdev/trigger.dev
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: e9ac98b
Choose a base ref
...
head repository: triggerdotdev/trigger.dev
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: d189ce1
Choose a head ref
  • 19 commits
  • 104 files changed
  • 8 contributors

Commits on Jul 23, 2026

  1. chore(deps): bump tar to 7.5.19 (#4345)

    Pins `tar` to `7.5.19` via a root `pnpm.overrides` entry, replacing a
    stale range override (`tar@>=7 <7.5.11`) that no longer matched any
    installed copy.
    
    The single override collapses all resolved `tar` copies onto one
    version:
    
    - `packages/cli-v3` — direct dependency (was 7.5.13)
    - `@kubernetes/client-node` (apps/supervisor) — transitive (was 7.5.13)
    - `cacache` — transitive (was 6.2.1)
    - `giget` — transitive (was 6.2.1)
    
    No source changes; cli-v3's published `^7.5.13` spec already permits
    `7.5.19`, so no changeset is needed.
    claude[bot] authored Jul 23, 2026
    Configuration menu
    Copy the full SHA
    3c82248 View commit details
    Browse the repository at this point in the history
  2. fix(docker): stop the container entrypoint printing database connecti…

    …on strings in logs (#4346)
    
    ## Summary
    
    The container entrypoint runs under `set -x`, which echoes every command
    to the logs with its variables expanded. Several startup guards
    reference full database connection strings, so the DSN (including the
    password) was printed to the container logs on every boot. This turns
    tracing off around those lines so connection strings are never traced,
    while leaving migration behavior and ordinary startup logging unchanged.
    
    ## Fix
    
    The leaking lines are the `[ -n "$RUN_OPS_DATABASE_URL" ]` and `[ -n
    "$RUN_OPS_LEGACY_DIRECT_URL" ]` guards, and the ClickHouse block (its `[
    -n "$CLICKHOUSE_URL" ]` guard plus the lines that build `GOOSE_DBSTRING`
    from `CLICKHOUSE_URL`). `set -x` prints each of these with the
    credential expanded. Tracing is now disabled around each region and
    restored afterward, so non-secret tracing is preserved everywhere else.
    The existing legacy-migration subshell already protected its own command
    body; this adds the missing protection for the guards and the ClickHouse
    block.
    
    ```sh
    { set +x; } 2>/dev/null
    if [ -n "$RUN_OPS_DATABASE_URL" ]; then
      set -x
      ...
    ```
    
    ## Verification
    
    Built the webapp image and ran it with dummy sentinel connection strings
    whose password token is `S3NTINEL_PW_DoNotLog`, then grepped the boot
    logs.
    
    Before (unmodified), the token appears in the traced guards:
    
    ```
    + [ -n postgresql://user:S3NTINEL_PW_DoNotLog@fake-host:6432/run-ops ]
    + [ -n postgresql://user:S3NTINEL_PW_DoNotLog@fake-host:5432/legacy ]
    + [ -n https://default:S3NTINEL_PW_DoNotLog@fake-host:8443 ]
    ```
    
    After, `grep S3NTINEL_PW_DoNotLog` on the same run returns nothing, and
    the normal "skipping ... migrations" lines still log.
    ericallam authored Jul 23, 2026
    Configuration menu
    Copy the full SHA
    88ca009 View commit details
    Browse the repository at this point in the history
  3. feat(supervisor): add prometheus metric for outbound http requests (#…

    …4350)
    
    Adds Prometheus metrics so the supervisor's outbound HTTP calls are
    observable - including client-side failures that previously only
    surfaced as a log line.
    
    - `supervisor_outbound_request_total{name, method, status, outcome}` -
    counts every outbound request. `outcome` separates a transport failure
    (`network_error`), an HTTP error response (`http_error`), a response
    that failed schema validation (`invalid_response`), and success (`ok`).
    - `supervisor_outbound_request_duration_seconds{name, outcome}` -
    latency histogram. Leaner labels than the counter (no `status`) to avoid
    bucket×label cardinality; buckets match the existing dequeue-latency
    histogram since these calls share the same retrying HTTP client and
    long-poll envelope.
    
    Coverage:
    - The warm-start request (a one-off `fetch`) - instrumented inline; the
    response status code is now also included in the failure log (it was
    previously dropped).
    - All worker API client calls (`SupervisorHttpClient`: dequeue, run
    attempt start/complete, heartbeats, snapshots, continue, suspend,
    debug-log, connect) - routed through a single instrumented `request()`
    helper that reports via an optional `onHttpRequestComplete` callback on
    the client, which the supervisor wires into the counter + histogram.
    
    Low cardinality by design: `name` is a **static per-endpoint label**
    (e.g. `dequeue`, `start_run_attempt`), never the interpolated URL - so
    no run/snapshot IDs land in labels, mirroring the templated `route`
    labels on the inbound HTTP server.
    
    Registered on the existing metrics registry, exposed on `/metrics` with
    no new wiring. Internal-only change (no package release needed), so the
    changelog note is a single `.server-changes` entry.
    nicktrn authored Jul 23, 2026
    Configuration menu
    Copy the full SHA
    722e240 View commit details
    Browse the repository at this point in the history

Commits on Jul 24, 2026

  1. perf(database): index BatchTaskRun on (runtimeEnvironmentId, createdA…

    …t, id) for the batches list (#4361)
    
    ## Summary
    
    The batches list page orders by `createdAt DESC, id DESC` filtered by
    environment and a created-at window, but the only supporting index on
    `BatchTaskRun` was `(runtimeEnvironmentId, id)`. That index can't
    satisfy the `createdAt` ordering, so on environments with a large number
    of batches the query fell back to a full table scan and in-memory sort,
    which could run long enough to hit the statement timeout.
    
    ## Fix
    
    Adds `(runtimeEnvironmentId, createdAt DESC, id DESC)` on
    `BatchTaskRun`. The query now reads straight from the index in order
    with no sort step, returning a page with only a handful of heap fetches
    instead of scanning the whole environment slice.
    
    The migration uses `CREATE INDEX CONCURRENTLY IF NOT EXISTS`, so it
    takes no table lock and is a no-op if the index already exists.
    ericallam authored Jul 24, 2026
    Configuration menu
    Copy the full SHA
    9c85e0e View commit details
    Browse the repository at this point in the history
  2. perf(webapp): clamp list-endpoint page size to 100 (#4360)

    ## Summary
    
    Several list endpoints accepted an unbounded page size (`perPage` /
    `per_page` / `pageSize`). An unbounded page lets one request pull an
    arbitrarily large result set and do a proportional amount of work, which
    is a poor default for a shared API.
    
    This clamps the page size to 100 on every list endpoint that was
    uncapped, matching the existing cap on `api.v1.runs` and
    `api.v1.sessions`. Clamping rather than rejecting keeps existing clients
    working: a request for a larger page returns up to 100 items and offset
    pagination continues from there.
    
    ## Endpoints capped
    
    - `api.v1.schedules` (`perPage`)
    - `api.v1.queues` (`perPage`)
    - `resources.…versions` (`per_page`)
    - `resources.…queues` (`per_page`)
    - `admin.api.v1.…engine.report` (`per_page`)
    - `admin.api.v1.llm-models` (`pageSize`)
    
    Already capped, left as-is: `api.v1.runs`, `api.v1.sessions`,
    `api.v1.deployments`.
    ericallam authored Jul 24, 2026
    Configuration menu
    Copy the full SHA
    7188eec View commit details
    Browse the repository at this point in the history
  3. feat(supervisor): configurable warm-start dispatch url (#4362)

    Adds an optional `TRIGGER_WARM_START_DISPATCH_URL`. The warm-start
    dispatch request uses it when set, otherwise falls back to
    `TRIGGER_WARM_START_URL`, so the dispatch target can differ from the
    default warm-start URL. No behavior change when unset.
    nicktrn authored Jul 24, 2026
    Configuration menu
    Copy the full SHA
    bf41c5d View commit details
    Browse the repository at this point in the history
  4. Configuration menu
    Copy the full SHA
    109e245 View commit details
    Browse the repository at this point in the history
  5. fix(sdk): preserve partial assistant message on chat stream failure (#…

    …4348)
    
    ## Summary
    
    When a `chat.agent` (or `chat.createSession`) turn's model stream fails
    mid-response (e.g. a transport timeout like `UND_ERR_BODY_TIMEOUT`), the
    assistant output that already streamed was dropped: `onTurnComplete`
    fired with `responseMessage: undefined`, and the manual loop's
    `turn.complete()` rethrew without keeping the partial. Apps that
    register `hydrateMessages` are hit hardest, since boot-time tail-replay
    recovery is off by design.
    
    This preserves the streamed-so-far assistant output while still
    reporting the turn as errored, so persistence and recovery keep it.
    
    ## Scope of behavior change
    
    Only the **error path** changes. Successful turns are unaffected: the
    same chunks stream to the client in the same order, and
    backpressure/cancel behave as before. Everything here is a correctness
    improvement on a turn that hit a source-stream failure.
    
    ## What it does
    
    Follow-up to #4304 (`chat.pipeAndCapture`), extending the same
    partial-recovery to the two loops that lacked it:
    
    - **`chat.agent`**: taps the response stream (via a `TransformStream`,
    so pass-through backpressure and cancel are preserved) to buffer chunks,
    and on a source-stream failure reconstructs the partial (preferring the
    `onFinish` message). It's surfaced on the error-path `onTurnComplete`
    (`responseMessage`, `rawResponseMessage`, `uiMessages`, `newUIMessages`,
    `newMessages`) and committed to the accumulator so the next turn and the
    reboot snapshot keep it.
    - **`chat.createSession` / `turn.complete()`**: the reconstructed
    partial is accumulated (so `turn.uiMessages` reflects it and the caller
    can persist after catching) before `turn.complete()` rethrows.
    
    `onBeforeTurnComplete` stays skipped on the error path (it hands out a
    writer for a stream that has already broken).
    
    ## Correctness properties (each covered by a regression test)
    
    Each test below was confirmed to fail without its fix:
    
    - The recovered partial reaches `onTurnComplete` and the next turn's
    accumulated messages.
    - An already-committed (possibly enriched) response is not overwritten
    if a post-response hook then throws.
    - Incomplete tool parts are cleaned from the recovered partial (text
    kept), so the UI and model views agree and the next turn isn't poisoned.
    - A prior turn's model-only compaction survives an errored turn (append
    only the new tail, don't reconvert the full history).
    - A reconstructed fragment that reuses an existing message id does not
    clobber the complete message.
    - Queued `chat.response` data parts are folded into the recovered
    partial, matching the success path.
    - `newMessages` (model delta) stays symmetric with `newUIMessages`.
    
    ## Tests
    
    New `chat-agent-source-stream-error.test.ts` covers the cases above. The
    full `@trigger.dev/sdk` unit suite passes and the package build is green
    across all supported runtimes (Node 20 to 26, Bun, Deno, Cloudflare
    Workers).
    matt-aitken authored Jul 24, 2026
    Configuration menu
    Copy the full SHA
    be45cf9 View commit details
    Browse the repository at this point in the history

Commits on Jul 26, 2026

  1. feat(webapp): read realtime run rows from the primary, not the replica (

    #4378)
    
    ## Summary
    
    The realtime runs feed hydrates run rows from read replicas, which means
    it needs a replica-lag gate to avoid serving a run's previous state
    right after a write. Setting
    `REALTIME_BACKEND_NATIVE_RUN_READS_FROM_PRIMARY=1` reads those rows from
    each run store's primary instead, so there is no lag to gate against: no
    probe, no wake delay, no stale-read retries. Off by default, so nothing
    changes unless you set it.
    
    ## Design
    
    The run stores already decide replica-vs-primary from the *brand* on the
    read client they are handed: a branded replica keeps the read on the
    owning store's replica, an unbranded writer escalates it to that store's
    own primary. So this is a one-line choice at the hydrator, and it stays
    correct across topologies. With the run-ops split on, each leg lands on
    its own writer and the caller's client is never forwarded across
    databases; with the split off, it is the single database's primary.
    
    ```ts
    const runReader = new RunHydrator({
      readClient: runReadsFromPrimary ? prisma : $replica,
      runStore,
    });
    ```
    
    The same flag skips constructing the lag estimator, since probing a
    replica the feed no longer reads would be measuring the wrong thing.
    
    Independently, `AuroraReplicaLagSource` detected Aurora by letting
    `aurora_replica_status()` fail, on the assumption that the app-level
    catch made that free. It isn't: an unresolvable function is a query
    error the driver reports to the error log on every sample, so a
    non-Aurora replica produced a continuous stream of error events while
    the estimator quietly fell through to its next candidate. It now
    resolves the function with `to_regproc` and memoizes the answer, so the
    unparseable call never reaches the wire.
    ericallam authored Jul 26, 2026
    Configuration menu
    Copy the full SHA
    d390624 View commit details
    Browse the repository at this point in the history

Commits on Jul 27, 2026

  1. chore(ci): remove per-repo Dependabot alert workflows (#4384)

    ## ✅ Checklist
    
    - [x] I have followed every step in the [contributing
    guide](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md)
    - [x] The PR title follows the convention.
    - [ ] I ran and tested the code works — n/a, this PR only deletes two
    workflow files
    
    ---
    
    ## Summary
    
    **Before:** two scheduled workflows in this repo posted Dependabot
    digests to Slack — a critical-alert check every morning at 08:00 UTC,
    and a summary of all open alerts on Mondays at 08:00 UTC.
    
    **After:** neither runs. This reporting is handled centrally now, so the
    two in-repo workflows were duplicating it.
    
    **How:** deletes `.github/workflows/dependabot-critical-alerts.yml` and
    `.github/workflows/dependabot-weekly-summary.yml`. Both were
    self-contained — inline shell, no shared scripts or composite actions —
    so nothing else in `.github/` referenced them.
    
    Dependabot itself is unchanged: `.github/dependabot.yml`, alerts, and
    version updates all keep working. This removes only the two Slack
    notifiers.
    
    The `ENABLE_DEPENDABOT_ALERTS` repository variable existed only to
    switch these two workflows off. Nothing else reads it, so it can be
    removed from the repository settings if it's set.
    
    ---
    
    ## Testing
    
    No runtime code changes — this PR only removes two scheduled workflow
    files. Verified that nothing else in the repo references either
    filename, either workflow name, or the `ENABLE_DEPENDABOT_ALERTS`
    variable.
    
    ---
    
    ## Changelog
    
    Removed the two in-repo scheduled workflows that posted Dependabot
    digests to Slack.
    
    Co-authored-by: Claude <noreply@anthropic.com>
    claude[bot] and claude authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    4d8b5d6 View commit details
    Browse the repository at this point in the history
  2. fix(webapp): remove unawaited task list metrics promises (#4380)

    <!-- ccr-slack-attribution -->
    _Requested via [Slack
    thread](https://triggerdotdev.slack.com/archives/C097ZHVKZFA/p1785082528841609)_
    
    `TaskListPresenter` created promises that nothing ever consumed. Two of
    the three deferred metrics promises it returned had no reader, no
    `await` and no `.catch()`, so when the query behind one of them failed
    the rejection had nowhere to go.
    
    ## Before / After
    
    **Before**
    
    - `TaskListPresenter.call()` returned four things: `tasks`, `activity`,
    `runningStats` and `durations`. Its only caller reads `tasks` and
    `runningStats`.
    - Every load of the tasks page therefore fired two ClickHouse queries
    whose results were thrown away.
    - If either of those two queries failed, the resulting promise rejection
    was unhandled — nothing was awaiting it and nothing had attached an
    error handler, so it surfaced as an unhandled rejection at the process
    level rather than as an error anyone could attribute to a request.
    
    **After**
    
    - `TaskListPresenter.call()` returns `tasks` and `runningStats` only.
    - Two fewer queries run per tasks-page load.
    - There is no longer an unconsumed promise that can reject without a
    handler. `runningStats` is awaited by its caller, so its failures
    continue to be handled the way they always were.
    
    Nothing changes on screen: the tasks page renders `hourlyActivity` and
    `runningStates`, and neither of the removed values fed either of those.
    
    ## How
    
    The removed values were verified unreferenced before deleting anything:
    
    - `TaskListPresenter` has exactly one caller,
    `UnifiedTaskListPresenter`, which reads `taskResult.tasks` and
    `taskResult.runningStats` and nothing else.
    - No file anywhere in the repo — app code, tests, or type re-exports —
    reads an `activity` or `durations` field off the presenter's result.
    - `UnifiedTaskListPresenter` builds its own
    `unifiedTaskListHourlyActivity` query for the 24h chart the page
    actually renders, which is what made the presenter's separate 7-day
    daily activity data redundant.
    - `getDailyTaskActivity` and `getAverageDurations` on
    `ClickHouseEnvironmentMetricsRepository` had no callers other than the
    two lines being deleted, so they and their now-orphaned helpers and
    types were removed too.
    
    Changes:
    
    - `apps/webapp/app/presenters/v3/TaskListPresenter.server.ts` — drop the
    `activity` and `durations` fields (both from the main return and from
    the no-current-worker early return) and the two repository calls behind
    them. Drop the unreferenced `TaskActivity` type alias. The "don't await
    this" comment on the remaining `runningStats` promise now spells out
    that the caller has to consume it.
    - `apps/webapp/app/services/environmentMetricsRepository.server.ts` —
    remove `getDailyTaskActivity` and `getAverageDurations` from the
    `EnvironmentMetricsRepository` interface and its ClickHouse
    implementation, along with `fillInDailyTaskActivity` and the
    `DailyTaskActivity` / `AverageDurations` types.
    
    `getCurrentRunningStats` is the control that shows the diagnosis is
    right. It throws on query failure in exactly the same way as the two
    removed methods — `if (queryError) throw queryError` — but it never
    produced an unhandled rejection, because `UnifiedTaskListPresenter`
    passes its promise into a `Promise.all(...).then(...)` chain that the
    route then awaits. Same failure mode, opposite outcome, and the only
    difference is whether anything consumes the promise.
    
    Follow-ups, not in this PR:
    
    - `AgentListPresenter` returns three sparkline promises in the same
    shape and they look similarly unconsumed. Left alone here to keep this
    change reviewable.
    - With these two callers gone, the `getTaskActivity` and
    `getAverageDurations` query builders in `@internal/clickhouse` have no
    remaining callers in this repo. Whether to remove them is a separate
    call for someone who owns that package.
    
    ## ✅ Checklist
    
    - [x] I have followed every step in the [contributing
    guide](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md)
    - [x] The PR title follows the convention.
    - [x] I ran and tested the code works
    
    ---
    
    ## Testing
    
    - `pnpm run typecheck --filter webapp` — passes. This is the meaningful
    check here: it proves nothing still references the removed fields,
    methods or types.
    - `pnpm run format` and `pnpm run lint:fix` — clean, no changes
    produced.
    - No test file referenced the removed symbols, so no test needed
    updating.
    
    ---
    
    ## Changelog
    
    Server-only change, so this carries a `.server-changes/` note rather
    than a changeset:
    `.server-changes/task-list-remove-unused-metrics-queries.md`.
    
    > The tasks page no longer runs two queries whose results were never
    displayed, cutting wasted work on every page load and removing a source
    of hidden server errors
    
    ---
    
    ## Screenshots
    
    _No visual change — the removed data was never rendered._
    
    Co-authored-by: Claude <noreply@anthropic.com>
    claude[bot] and claude authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    d91818f View commit details
    Browse the repository at this point in the history
  3. chore(deps): bump express-rate-limit and ip-address (#4391)

    **Before:** `ip-address` resolved twice in `pnpm-lock.yaml` — `8.1.0`
    under `@jsonhero/json-infer-types`, and `10.0.1` under
    `express-rate-limit`.
    
    **After:** a single `ip-address@10.2.0` entry, shared by both chains.
    
    **How:** `express-rate-limit@8.2.1` pinned `ip-address` to an exact
    version, so the parent itself had to move — `8.5.1` onwards declares a
    range instead, and `@modelcontextprotocol/sdk` already allows `^8.2.1`,
    so scoping that parent to `^8.6.0` lets `ip-address` resolve on its own.
    `@jsonhero/json-infer-types` caps `ip-address` at `^8.1.0` and is
    already at its latest published release, so that chain gets a scoped
    override instead of a parent bump. `jsbn` and `sprintf-js` drop out of
    the tree as a side effect.
    
    Both overrides are parent-scoped, so the `cli-v3` chain is deliberately
    untouched: it resolves `@modelcontextprotocol/sdk` 1.25.2, which
    declares `express-rate-limit ^7.5.0` and pulls in no `ip-address` at
    all.
    
    `pnpm-lock.yaml` regenerated. `package.json` and `pnpm-lock.yaml` are
    the only two files changed.
    
    Nothing in the repo imports `ip-address` or `express-rate-limit`
    directly. Both chains are transitive under `apps/webapp` —
    `@jsonhero/schema-infer` (used by `TestTaskPresenter.server.ts`) and
    `@vercel/sdk` — so no published `@trigger.dev/*` package is affected.
    
    ---
    
    ## Testing
    
    - `pnpm install --lockfile-only` regenerates cleanly, and `pnpm install
    --frozen-lockfile --lockfile-only` passes, so the lockfile matches the
    manifests.
    - Package churn is limited to the intended set: `express-rate-limit`
    8.2.1 to 8.6.0, `ip-address` 8.1.0 and 10.0.1 collapsing to 10.2.0, and
    `jsbn` / `sprintf-js` removed. No other resolution moved.
    - `@jsonhero/json-infer-types` only calls `new Address4()` / `new
    Address6()` inside a try/catch to classify strings. Ran that exact logic
    against both `8.1.0` and `10.2.0` over 27 inputs (v4, v6, zone IDs,
    CIDR, IPv4-mapped, malformed, empty, non-strings): identical results in
    all 27. Both are still CJS named exports in `10.2.0`, with the same
    `engines` floor.
    - Drove the real `inferSchema()` path from `@jsonhero/schema-infer` with
    `ip-address` forced to `10.2.0`; it still detects `ipv4` and `ipv6`
    formats correctly.
    - `express-rate-limit` 8.6.0 keeps the same `express` peer range (`>=
    4.11`) and the same node floor as 8.2.1. Its new `debug` dependency
    resolves to a version already present in the tree.
    - `oxfmt --check` passes on the modified `package.json`.
    - Both bumped versions clear the repo's `minimumReleaseAge` window; the
    newest `express-rate-limit` (8.6.1) and `ip-address` (10.2.1+) releases
    do not yet, which is why this lands on 8.6.0 and 10.2.0.
    - Not run here: a full monorepo install, typecheck and test suite. No
    TypeScript changed, and neither package leaks types into ours —
    `ip-address` is not referenced in `json-infer-types`' or
    `schema-infer`'s declaration files — so CI should be the judge of the
    wider suite.
    
    ---
    
    ## Changelog
    
    Routine dependency maintenance, no behaviour change. No changeset or
    `.server-changes/` entry: the diff touches only the root `package.json`
    and `pnpm-lock.yaml`, not `packages/*`, `integrations/*`, `apps/webapp/`
    or `apps/supervisor/`.
    
    Co-authored-by: Claude <noreply@anthropic.com>
    Co-authored-by: nicktrn <55853254+nicktrn@users.noreply.github.com>
    3 people authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    72c2b2c View commit details
    Browse the repository at this point in the history
  4. feat(webapp): gate SSO on an entitlement instead of the Enterprise pl…

    …an (#4393)
    
    The SSO & Directory Sync settings page decided access by comparing the
    organization's plan code against the literal string `"enterprise"`. The
    webapp now reads a `hasSso` entitlement from plan limits.
    
    ## Changes
    
    - **`settings.sso` route** — `planAllowsSso` reads `limits.hasSso`
    rather than the plan code; the loader and the action gate on a shared
    `getSsoEntitlement` helper.
    - **`platform.v3.server`** — new `getSsoEntitlement(orgId)` returning
    `entitled | not_entitled | unknown`, behind a new SWR cache namespace
    (60s fresh / 120s stale, memory + Redis). This replaces an uncached
    billing round-trip that previously ran on every settings load, so the
    page gets cheaper than it was.
    - **`directorySyncEffects`** — the entitlement is now checked before
    applying membership effects, per organization and memoised across a
    batch.
    - **`@trigger.dev/platform` 1.2.0 → 1.3.0** — required, see below.
    
    ## Behaviour worth reviewing
    
    **Revocation now stops SCIM.** Previously the plan check existed only on
    the settings page, so an org that lost access kept receiving
    directory-sync pushes indefinitely; only the config UI froze. Provision
    *and* deprovision are gated, so a revoked entitlement can't remove
    members either.
    
    **An unreadable entitlement throws instead of skipping.** Effects are
    idempotent and the worker retries, so retrying is lossless where
    dropping would silently lose a directory change. It's raised at `warn`
    level so a transient billing blip doesn't page anyone.
    
    **The login path is deliberately untouched.** A hard entitlement check
    there turns a billing outage into a login outage. Consequence: an org
    that loses the entitlement keeps its existing SSO logins working until
    the connection is removed. Gating sign-in is a separate decision.
    
    **Self-hosted is unaffected.** With no billing service configured the
    helper returns `entitled`, leaving plugin presence and the kill switch
    as the only gates — a self-hoster who installed the plugin isn't locked
    out of it.
    
    ## The dependency bump is load-bearing
    
    The `Limits` schema is a plain `z.object`, so it *strips* unknown keys.
    On 1.2.0 the `hasSso` field was silently discarded during parsing and
    read as `undefined` no matter what billing sent — a structural accessor
    would not have helped. Verified against both builds:
    
    ```
    1.2.0 → parsed: true | hasSso survives: false
    1.3.0 → parsed: true | hasSso survives: true
    ```
    
    This PR therefore cannot merge before 1.3.0 is published, which it now
    is.
    
    ## Testing
    
    `apps/webapp/test/directorySyncEffects.server.test.ts` — 7 tests over
    the gate: applies when entitled, skips provision and deprovision when
    not, throws a warn-level retryable error when unreadable, resolves once
    per org across a batch, and gates per org so one unentitled org doesn't
    block another.
    
    `pnpm run typecheck --filter webapp` passes (18/18), oxfmt and oxlint
    clean.
    matt-aitken authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    269470f View commit details
    Browse the repository at this point in the history
  5. chore: ignore local docs/superpowers planning docs (#4395)

    Adds a gitignore rule for `**/docs/superpowers/` so locally-generated
    planning and design scratch docs under that path aren't committed;
    preventive only, no-op for existing tree.
    nicktrn authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    e8a2dbd View commit details
    Browse the repository at this point in the history
  6. feat(webapp): live-update the runs list on task pages (#4377)

    ## Summary
    
    The runs list on a task's page now updates live, matching the main Runs
    page. Run rows update their status, duration, and cost in place as runs
    progress, and a "N new runs" button appears in the header when newer
    runs come in so you can pull them into the list without a manual
    refresh. This applies to both standard and scheduled task pages.
    
    ## Design
    
    It reuses the Runs page's polling hook. A task page scopes its runs by
    the task in the URL path rather than a `tasks` query filter, so the hook
    now takes an optional task slug and scopes new-run detection to it. The
    "new runs" button sits in the header, outside the deferred runs table,
    so the count is lifted to the page and the click action is passed
    through a ref. That keeps the table streaming on first load instead of
    blocking the header on the runs query.
    
    When newer runs come in, a `1 new run` button appears in the task page
    header, to the left of the time filter. Clicking it pulls the new runs
    into the list.
    samejr authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    3e53404 View commit details
    Browse the repository at this point in the history
  7. feat(core,sdk): support additional environment API keys (#4387)

    ## Summary
    
    Additional environment API keys can use SDK APIs that require public
    access tokens. The SDK detects the additional-key format and asks the
    Trigger.dev server to mint scoped tokens instead of attempting to sign
    them locally.
    
    Root environment keys retain their existing local-signing behavior.
    Trigger and batch clients also prefer server-issued tokens returned in
    response headers while preserving compatibility with older servers.
    
    ## Deployment notes
    
    This package update is safe to publish before servers expose additional
    key creation. Existing root keys continue to use the current path, while
    an additional key used with an older server fails with an actionable
    upgrade error.
    carderne authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    efd0ee8 View commit details
    Browse the repository at this point in the history
  8. feat(webapp): favorite pages and sidebar customization (#4375)

    ## Summary
    
    Favorite any dashboard page and it appears in a new "Favorites" section
    at the top of the side menu. The star next to the page title (or
    Option+F) saves the exact view, filters and tabs included, with a name
    derived from the URL ("Runs: Completed successfully, last 7d", "Run:
    05hrqq9n") that you can rename inline from each item's hover menu.
    
    The sidebar is customizable too: "Customize sidebar" (on section header
    menus and in each "More" menu) opens a modal where you can reorder
    sections, drag items into a new order, hide items behind a per-section
    "More" popover, and rename or remove favorites. Changes apply on
    Confirm, Reset restores the default layout without touching favorites,
    and everything is stored per user in dashboard preferences.
    
    ## Screenshots
    
    | Favorites in the side menu | Customize sidebar modal |
    | --- | --- |
    | ![Favorites section with rename and remove
    menu](https://raw.githubusercontent.com/triggerdotdev/trigger.dev/d56f073dc517e2073b01d8eff880183539638f03/favorites-side-menu.png)
    | ![Customize sidebar
    modal](https://raw.githubusercontent.com/triggerdotdev/trigger.dev/d56f073dc517e2073b01d8eff880183539638f03/customize-sidebar-modal.png)
    |
    
    ![Favorite star and tooltip in the page
    header](https://raw.githubusercontent.com/triggerdotdev/trigger.dev/d56f073dc517e2073b01d8eff880183539638f03/star-tooltip.png)
    
    ## Design notes
    
    - Favorite links carry a small marker search param so the favorite, not
    its identical main menu item, highlights as active. Markers from shared
    or stale links are cleaned on load, and changing any filter hands the
    highlight back to the regular menu item.
    - Preference writes are serialized with a row lock: several writers
    (debounced collapse and width saves, favorite toggles, the customize
    modal) can land concurrently and would otherwise clobber each other's
    read-modify-write of the JSON column.
    - Option+F is matched on `event.code` with a raw listener because macOS
    reports Option-modified letters as symbols, which the `event.key` based
    shortcut hook can't capture.
    
    Verified end-to-end in the browser: star toggle and shortcut, instant
    section appearance, inline rename and staged modal removal, filter-aware
    labels and unique active states, shared-link normalization, drag
    reordering, and persistence across reloads.
    samejr authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    d30ee6e View commit details
    Browse the repository at this point in the history
  9. feat(webapp): Improve the Integrations page layout (#4379)

    ## Summary
    
    The project Integrations page now uses the same settings layout as the
    org SSO page: a centered column of titled rows with dividers, instead of
    headings over bordered boxes. GitHub, Vercel and build settings read as
    one consistent list, and the page titles itself "Integrations".
    
    Confirmations persist rather than vanishing once you move past them
    (`GitHub app: Installed`, `Vercel project: Connected`), plan-gated rows
    offer an Upgrade button instead of a dead toggle, a disabled toggle
    explains why in place and highlights the control that unlocks it, and
    warnings are rows with a hazard icon and their recovery action on the
    right. Copy throughout leads with the outcome instead of restating the
    field label.
    
    Two fixes along the way: a nested `<form>` in the Vercel panel that
    failed hydration and silently truncated the page, and every settings row
    carrying a few pixels more space above its title than below its
    description.
    
    ### Before
    <img width="1160" height="1972" alt="CleanShot 2026-07-26 at 21 56
    42@2x"
    src="https://github.com/user-attachments/assets/ed0fd676-36d8-4eb7-a16e-827a24f007d9"
    />
    
    
    ### After
    <img width="1358" height="4455" alt="CleanShot 2026-07-26 at 19 14
    28@2x"
    src="https://github.com/user-attachments/assets/6a635e6a-c0eb-4a4c-a68f-fcde4d25e8a6"
    />
    samejr authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    73eb4c5 View commit details
    Browse the repository at this point in the history
  10. chore: release v4.5.8 (#4364)

    ## Summary
    2 new features, 9 improvements, 3 bug fixes.
    
    ## Highlights
    
    - Allow additional environment API keys to create scoped public access
    tokens through the Trigger.dev API. Use server-issued public access
    tokens for batch operations so environment-scoped API keys can read
    batch results.
    ([#4387](#4387))
    
    ## Improvements
    - Preserve the partial assistant message when a chat turn's model stream
    fails mid-response. `chat.agent` now passes the recovered partial to
    `onTurnComplete`, and `chat.createSession`'s `turn.complete()` keeps it
    before rethrowing, instead of dropping the streamed-so-far output.
    ([#4348](#4348))
    
    ## Server changes
    
    These changes affect the self-hosted Docker image and Trigger.dev Cloud:
    
    - Favorite any dashboard page to a new Favorites section in the side
    menu, and customize the sidebar by renaming favorites, hiding items, and
    reordering items and sections.
    ([#4375](#4375))
    - List API endpoints now clamp the page size to a maximum of 100.
    Requests asking for a larger page size return up to 100 items and keep
    paginating, rather than pulling an unbounded page.
    ([#4360](#4360))
    - Organizations without billing alerts now get default spend alert
    thresholds, so you're notified before usage grows unexpectedly. The
    billing limit page no longer pre-selects an option before you've set a
    limit and prompts you to configure one. Alert previews now update
    immediately after you change your billing limit.
    ([#4328](#4328))
    - When you create a Personal Access Token, the generated token now shows
    its first and last few characters instead of being fully hidden, so you
    can confirm you copied the right value.
    ([#4363](#4363))
    - Add metrics to the realtime backend that measure how often a single
    changed run is served to multiple subscriptions in one batch.
    ([#4341](#4341))
    - Realtime run subscriptions can now be configured to read run data
    straight from the primary database, so a run's latest state is never
    served from a lagging replica. Off by default; replica reads are
    unchanged unless you turn it on.
    ([#4378](#4378))
    - SSO and Directory Sync are no longer restricted to Enterprise plans —
    get in touch and we can turn them on for your organization whatever plan
    you're on.
    ([#4393](#4393))
    - Improved supervisor observability: it now reports metrics for its
    outbound requests, making failed calls to upstream services easier to
    monitor.
    ([#4350](#4350))
    - The runs list on a task's page now updates live — run statuses change
    and newly triggered runs appear without a manual refresh, matching the
    main Runs page.
    ([#4377](#4377))
    - Speed up the Batches list page for environments with a large number of
    batches, which could previously time out while loading.
    ([#4361](#4361))
    - Container startup no longer prints database and ClickHouse connection
    strings (with credentials) to the logs.
    ([#4346](#4346))
    - The tasks page no longer runs two queries whose results were never
    displayed, cutting wasted work on every page load and removing a source
    of hidden server errors
    ([#4380](#4380))
    
    <details>
    <summary>Raw changeset output</summary>
    
    # Releases
    ## @trigger.dev/build@4.5.8
    
    ### Patch Changes
    
    - Updated dependencies:
      - `@trigger.dev/core@4.5.8`
    ## trigger.dev@4.5.8
    
    ### Patch Changes
    
    - Updated dependencies:
      - `@trigger.dev/core@4.5.8`
      - `@trigger.dev/build@4.5.8`
      - `@trigger.dev/schema-to-json@4.5.8`
    ## @trigger.dev/core@4.5.8
    
    ### Patch Changes
    
    - Allow additional environment API keys to create scoped public access
    tokens through the Trigger.dev API. Use server-issued public access
    tokens for batch operations so environment-scoped API keys can read
    batch results.
    ([#4387](#4387))
    ## @trigger.dev/python@4.5.8
    
    ### Patch Changes
    
    - Updated dependencies:
      - `@trigger.dev/sdk@4.5.8`
      - `@trigger.dev/core@4.5.8`
      - `@trigger.dev/build@4.5.8`
    ## @trigger.dev/react-hooks@4.5.8
    
    ### Patch Changes
    
    - Updated dependencies:
      - `@trigger.dev/core@4.5.8`
    ## @trigger.dev/redis-worker@4.5.8
    
    ### Patch Changes
    
    - Updated dependencies:
      - `@trigger.dev/core@4.5.8`
    ## @trigger.dev/rsc@4.5.8
    
    ### Patch Changes
    
    - Updated dependencies:
      - `@trigger.dev/core@4.5.8`
    ## @trigger.dev/schema-to-json@4.5.8
    
    ### Patch Changes
    
    - Updated dependencies:
      - `@trigger.dev/core@4.5.8`
    ## @trigger.dev/sdk@4.5.8
    
    ### Patch Changes
    
    - Preserve the partial assistant message when a chat turn's model stream
    fails mid-response. `chat.agent` now passes the recovered partial to
    `onTurnComplete`, and `chat.createSession`'s `turn.complete()` keeps it
    before rethrowing, instead of dropping the streamed-so-far output.
    ([#4348](#4348))
    - Allow additional environment API keys to create scoped public access
    tokens through the Trigger.dev API. Use server-issued public access
    tokens for batch operations so environment-scoped API keys can read
    batch results.
    ([#4387](#4387))
    - Updated dependencies:
      - `@trigger.dev/core@4.5.8`
    
    </details>
    
    Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
    github-actions[bot] authored Jul 27, 2026
    Configuration menu
    Copy the full SHA
    d189ce1 View commit details
    Browse the repository at this point in the history
Loading