re2-prod-supervisor-dispatch-url
7666 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
be45cf9e61 |
fix(sdk): preserve partial assistant message on chat stream failure (#4348)
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 5s
🚀 Publish Trigger.dev Docker / units (push) Failing after 5s
🚀 Publish Trigger.dev Docker / publish-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / publish-worker-v4 (push) Has been skipped
🚀 Publish Trigger.dev Docker / scan-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / scan-supervisor (push) Has been skipped
🦋 Changesets PR / Create Release PR (push) Has been cancelled
🚀 Publish Trigger.dev Docker / 📣 Dispatch main image (push) Has been cancelled
⚒️ Publish Worker (v4) / build (supervisor) (push) Has been cancelled
## 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).re2-prod-supervisor-dispatch-url |
||
|
|
109e245d56 | feat(webapp): show the first and last characters of a new PAT (#4363) | ||
|
|
bf41c5d5fc |
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. |
||
|
|
7188eecd83 |
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`. |
||
|
|
9c85e0ecdc |
perf(database): index BatchTaskRun on (runtimeEnvironmentId, createdAt, 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. |
||
|
|
722e240e4d |
feat(supervisor): add prometheus metric for outbound http requests (#4350)
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 5s
🚀 Publish Trigger.dev Docker / publish-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / publish-worker-v4 (push) Has been skipped
🚀 Publish Trigger.dev Docker / scan-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / scan-supervisor (push) Has been skipped
🚀 Publish Trigger.dev Docker / units (push) Failing after 6s
🦋 Changesets PR / Create Release PR (push) Has been cancelled
🚀 Publish Trigger.dev Docker / 📣 Dispatch main image (push) Has been cancelled
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.
|
||
|
|
88ca0091a9 |
fix(docker): stop the container entrypoint printing database connection strings in logs (#4346)
🚀 Publish Trigger.dev Docker / units (push) Failing after 11m53s
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 11m54s
🚀 Publish Trigger.dev Docker / publish-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / publish-worker-v4 (push) Has been skipped
🚀 Publish Trigger.dev Docker / scan-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / scan-supervisor (push) Has been skipped
🦋 Changesets PR / Create Release PR (push) Has been cancelled
🚀 Publish Trigger.dev Docker / 📣 Dispatch main image (push) Has been cancelled
## 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.
|
||
|
|
3c82248940 |
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. |
||
|
|
e9ac98b7a1 |
perf(run-store): route id-set reads to the owning store, not both DBs (#4342)
📚 Publish docs / publish (push) Has been cancelled
## Summary The split run-store's id-set read path (`#findRunsByIdSet`, used by the runs-list hydrate, the realtime hydrator, and engine sweeps) queried the new store for the entire id set and then probed the legacy store for the misses. A run's residency is a total function of its id (run-ops ids live in the new store, every other id in legacy), so each id belongs to exactly one store. Route each id to its owner and query each store only for its own ids, in parallel. Same result set, and while a split is active with most runs still on legacy it removes a wasted new-store query from every id-set read. ## Change `#findRunsByIdSet` now partitions the ids by `classifyResidency` and runs one bounded query per store (skipping an empty side), in parallel, mirroring `expireRunsBatch` and the single-run `#route`. `finalizeRows` still applies orderBy/take/skip globally over the merged set. This drops the id-set path's cross-store fallback, which existed to prefer the new-store copy when the same id was present in both stores. That collision cannot arise when each id maps to exactly one store (nothing writes a legacy-shaped id into the new store), so the fallback is dead code. The two id-set tests that asserted "new copy wins on collision" now assert the routing invariant: a legacy-shaped id resolves to the legacy store and the path never consults the new store. The open-predicate path (`#findRunsOpen`) is unchanged: an open `where` has no id to route on, so it still unions both stores and dedupes.v4.5.7 docs-release-2026-07-23 |
||
|
|
23d5771d56 |
feat(webapp): unconfigured billing limit UX and default billing alerts (#4328)
## Default billing alerts + billing limit page UX - New orgs get default billing alerts: $5, $100, $500, $1000, $2500. Existing orgs are backfilled by a billing-side data migration (companion [PR](https://github.com/triggerdotdev/cloud/pull/1657)). - The billing limit form starts with nothing selected for orgs that never set a limit — the save button appears once an option is picked. - The yellow banner now also shows on the billing limits page itself, asking to configure a limit. Hidden everywhere for members who can't manage billing. - Also fixes billing limit alert preview. Tests - `apps/webapp/test/billingLimitsRoute.test.ts` — dirty logic for empty/selected mode - `apps/webapp/test/billingAlertsDefaults.test.ts` — default values - `apps/webapp/test/billingAlertsFormat.test.ts` — preview after a limit change |
||
|
|
a2d382b2be |
feat(webapp): add emission fan-out metrics to the native realtime feed (#4341)
## Summary Adds two counters to the native realtime backend so we can see how much duplicate row serialization the change router does per batch. When a run changes it can match several held feeds at once (a run subscription plus one or more tag/list feeds), and today each matching feed serializes that run's wire value independently. These counters quantify that fan-out so we can decide whether a shared serialization step is worth it. ## What they measure - `realtime_native.emission_run_serializations`: total wire-value serializations performed across feeds per batch (what the current path does). - `realtime_native.emission_distinct_serializations`: distinct (columns, run) rows those serializations cover (what a serialize-once-per-batch step would do). Average feeds-per-run is `run_serializations / distinct_serializations`, and `1 - distinct / run_serializations` is the serialization work a shared step could save. Wired through a new optional `onEmissionFanout` callback on the router. No behavior change. |
||
|
|
aafc333523 |
chore: release v4.5.7 (#4319)
🚀 Publish Trigger.dev Docker / units (push) Failing after 5s
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 15s
🚀 Publish Trigger.dev Docker / scan-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / scan-supervisor (push) Has been skipped
🚀 Publish Trigger.dev Docker / publish-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / publish-worker-v4 (push) Has been skipped
🧭 Helm Chart Release / lint-and-test (push) Has been cancelled
🧭 Helm Chart Release / release (push) Has been cancelled
🚀 Publish Trigger.dev Docker / 📣 Dispatch main image (push) Has been cancelled
## Summary 5 improvements, 5 bug fixes. ## Improvements - Add `node-24` and `node-26` as supported `runtime` options in `trigger.config.ts`. The `experimental-node-24` and `experimental-node-26` names are now deprecated aliases and emit a deprecation warning; switch to `node-24` / `node-26` instead. ([#4337](https://github.com/triggerdotdev/trigger.dev/pull/4337)) ```ts import { defineConfig } from "@trigger.dev/sdk"; export default defineConfig({ runtime: "node-24", project: "<your-project-ref>", }); ``` - Avoid logging task run environment variable values at debug level ([#4336](https://github.com/triggerdotdev/trigger.dev/pull/4336)) - Custom chat agent loops get two ergonomic wins for owning the turn loop. ([#4304](https://github.com/triggerdotdev/trigger.dev/pull/4304)) `chat.writeTurnComplete()` now returns the turn boundary's resume cursors (`lastEventId` for the output stream and `sessionInEventId` for the input stream), so you can persist them straight from the task instead of round-tripping them back from the client. ```ts const { lastEventId, sessionInEventId } = await chat.writeTurnComplete(); await db.chats.update(chatId, { lastEventId, sessionInEventId }); ``` `chat.pipeAndCapture()` no longer throws when a stream is stopped or fails. It now returns a `PipeAndCaptureResult` whose `message` holds any partial output captured before the stop or failure, alongside a typed `status` (`"complete" | "aborted" | "error"`) and, on failure, the `error`. Read the message off the result: ```ts const { message, status, error } = await chat.pipeAndCapture(result, { signal, }); if (message) conversation.addResponse(message); if (status === "error") logger.error("turn failed", { error }); ``` Note: `pipeAndCapture` previously resolved to `UIMessage | undefined`. Update call sites to read `.message` from the returned result. - Suppress a build-time warning that could appear in Vite-based projects when the optional `@ai-sdk/otel` package is not installed. ([#4188](https://github.com/triggerdotdev/trigger.dev/pull/4188)) ## Bug fixes - Fixes intermittent `trigger dev` run crashes where a run could fail at boot with a cryptic `Cannot find module .../dev-run-worker.mjs` after a rebuild had cleaned up the build directory the run was launched against. Dev runs now retry cleanly instead of hard-crashing when their build directory is missing, the dev watchdog no longer removes the build tree of a still-running session, and a run assigned to a worker version that was superseded by a rebuild now fails fast with a clear message instead of silently hanging until it times out. ([#4276](https://github.com/triggerdotdev/trigger.dev/pull/4276)) ## Server changes These changes affect the self-hosted Docker image and Trigger.dev Cloud: - Refreshed the side menu: separate organization and account menus, a new project switcher, and the menu is now resizable by dragging its edge. The account Profile page has also been redesigned. ([#4066](https://github.com/triggerdotdev/trigger.dev/pull/4066)) - Allow different organization members to use the same development branch name without sharing or colliding with each other's branch environments. ([#4323](https://github.com/triggerdotdev/trigger.dev/pull/4323)) - Limit account settings email input to 254 characters. ([#4330](https://github.com/triggerdotdev/trigger.dev/pull/4330)) - Prevent duplicate Staging and Preview environments when account setup requests overlap ([#4261](https://github.com/triggerdotdev/trigger.dev/pull/4261)) - Fix the docs link on the empty Prompts page, which pointed to a page that no longer exists. ([#4247](https://github.com/triggerdotdev/trigger.dev/pull/4247)) <details> <summary>Raw changeset output</summary> # Releases ## @trigger.dev/build@4.5.7 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.7` ## trigger.dev@4.5.7 ### Patch Changes - Fixes intermittent `trigger dev` run crashes where a run could fail at boot with a cryptic `Cannot find module .../dev-run-worker.mjs` after a rebuild had cleaned up the build directory the run was launched against. Dev runs now retry cleanly instead of hard-crashing when their build directory is missing, the dev watchdog no longer removes the build tree of a still-running session, and a run assigned to a worker version that was superseded by a rebuild now fails fast with a clear message instead of silently hanging until it times out. ([#4276](https://github.com/triggerdotdev/trigger.dev/pull/4276)) - Add `node-24` and `node-26` as supported `runtime` options in `trigger.config.ts`. The `experimental-node-24` and `experimental-node-26` names are now deprecated aliases and emit a deprecation warning; switch to `node-24` / `node-26` instead. ([#4337](https://github.com/triggerdotdev/trigger.dev/pull/4337)) ```ts import { defineConfig } from "@trigger.dev/sdk"; export default defineConfig({ runtime: "node-24", project: "<your-project-ref>", }); ``` - Avoid logging task run environment variable values at debug level ([#4336](https://github.com/triggerdotdev/trigger.dev/pull/4336)) - Updated dependencies: - `@trigger.dev/core@4.5.7` - `@trigger.dev/build@4.5.7` - `@trigger.dev/schema-to-json@4.5.7` ## @trigger.dev/core@4.5.7 ### Patch Changes - Add `node-24` and `node-26` as supported `runtime` options in `trigger.config.ts`. The `experimental-node-24` and `experimental-node-26` names are now deprecated aliases and emit a deprecation warning; switch to `node-24` / `node-26` instead. ([#4337](https://github.com/triggerdotdev/trigger.dev/pull/4337)) ```ts import { defineConfig } from "@trigger.dev/sdk"; export default defineConfig({ runtime: "node-24", project: "<your-project-ref>", }); ``` ## @trigger.dev/python@4.5.7 ### Patch Changes - Updated dependencies: - `@trigger.dev/sdk@4.5.7` - `@trigger.dev/core@4.5.7` - `@trigger.dev/build@4.5.7` ## @trigger.dev/react-hooks@4.5.7 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.7` ## @trigger.dev/redis-worker@4.5.7 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.7` ## @trigger.dev/rsc@4.5.7 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.7` ## @trigger.dev/schema-to-json@4.5.7 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.7` ## @trigger.dev/sdk@4.5.7 ### Patch Changes - Custom chat agent loops get two ergonomic wins for owning the turn loop. ([#4304](https://github.com/triggerdotdev/trigger.dev/pull/4304)) `chat.writeTurnComplete()` now returns the turn boundary's resume cursors (`lastEventId` for the output stream and `sessionInEventId` for the input stream), so you can persist them straight from the task instead of round-tripping them back from the client. ```ts const { lastEventId, sessionInEventId } = await chat.writeTurnComplete(); await db.chats.update(chatId, { lastEventId, sessionInEventId }); ``` `chat.pipeAndCapture()` no longer throws when a stream is stopped or fails. It now returns a `PipeAndCaptureResult` whose `message` holds any partial output captured before the stop or failure, alongside a typed `status` (`"complete" | "aborted" | "error"`) and, on failure, the `error`. Read the message off the result: ```ts const { message, status, error } = await chat.pipeAndCapture(result, { signal, }); if (message) conversation.addResponse(message); if (status === "error") logger.error("turn failed", { error }); ``` Note: `pipeAndCapture` previously resolved to `UIMessage | undefined`. Update call sites to read `.message` from the returned result. - Suppress a build-time warning that could appear in Vite-based projects when the optional `@ai-sdk/otel` package is not installed. ([#4188](https://github.com/triggerdotdev/trigger.dev/pull/4188)) - Updated dependencies: - `@trigger.dev/core@4.5.7` </details> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>helm-v4.5.7 v.docker.4.5.7 |
||
|
|
b3b1441df9 |
fix(webapp): guard workload auth gate metric against dev HMR re-registration (#4339)
Wraps the workload_auth_gate_total Counter in the singleton helper (same pattern as reloadingRegistry.server.ts) so a dev hot reload doesn't crash with "A metric with the name workload_auth_gate_total has already been registered". No production behavior change. |
||
|
|
55a3bf2858 |
feat(core,cli): add node-24 and node-26 runtimes, deprecate experimental aliases (#4337)
## Summary
Adds `node-24` and `node-26` as first-class `runtime` options in
`trigger.config.ts`. Previously these Node versions were only reachable
via the `experimental-node-24` / `experimental-node-26` names.
Those experimental names are now **deprecated aliases**: they still
resolve to `node-24` / `node-26` for backwards compatibility, but
loading a config that uses them prints a deprecation warning pointing at
the new name.
```ts
export default defineConfig({
runtime: "node-24",
project: "<your-project-ref>",
});
```
## Details
- `ConfigRuntime` (the public config schema) now accepts `node-24` and
`node-26` directly; the internal `BuildRuntime` already supported them,
so base images and the deploy path are unchanged.
- `resolveBuildRuntime` passes the new names straight through and keeps
mapping the experimental aliases to their replacements.
- Renamed the runtime helper from `isExperimentalConfigRuntime` to
`isDeprecatedConfigRuntime` and added `deprecatedRuntimeReplacement` so
the CLI can name the replacement in its warning.
- Docs snippet updated to list the new versions and flag the deprecated
names.
|
||
|
|
14fa90672b |
chore: ignore .worktrees/ in the repo gitignore (#4334)
⚒️ Publish Worker (v4) / build (supervisor) (push) Has been cancelled
Add `.worktrees/` to the repo `.gitignore`. The pre-push hook runs `oxfmt --check .` and `oxlint .` over the whole tree, and those tools only read the in-repo ignore files (not a user's global gitignore). Local git-worktree checkouts placed under `.worktrees/` therefore got linted/formatted, failing the hook on unrelated code. Ignoring the directory keeps both tools out of worktree checkouts. No source changes.re2-test-supervisor-enforcement-mode-metric |
||
|
|
84add4ad3d |
feat(supervisor): export workload_token_enforcement_mode gauge (#4335)
Add a Prometheus gauge `workload_token_enforcement_mode` set to 1 for the active `WORKLOAD_TOKEN_ENFORCEMENT` value (`disabled`/`log`/`enforce`), emitted at startup on the shared registry. The existing mint/verify counters don't distinguish `log` from `enforce` (the verify outcome is recorded before the reject decision), so dashboards can't tell which mode a cluster is running. This gauge makes the active mode queryable at a glance. Supervisor typecheck passes. |
||
|
|
509a4597bd | fix(cli): redact task run env values from debug log (#4336) | ||
|
|
11d8a05fa6 |
fix(webapp): restore admin debug tooltip on Tasks and Runs, make its IDs copyable (#4332)
Restores the debug panel on the **Tasks** and **Runs** pages, and makes the data it shows copyable. Admin/impersonation only — no change for regular users, so there's no `.server-changes` <img width="909" height="1420" alt="CleanShot 2026-07-22 at 12 05 27@2x" src="https://github.com/user-attachments/assets/ce2da167-dc23-422f-83f3-4f4aee9ed32c" /> |
||
|
|
81eac67069 |
fix(webapp): correct docs link on the blank prompts page (#4247)
## Summary The empty-state panel on the Prompts page linked to a docs path that no longer exists, so the "Prompts docs" button returned a 404. It now points to the current prompts documentation at /docs/ai/prompts, matching the link already used in the page header. |
||
|
|
e7de120661 |
fix(webapp): remove Enterprise badge from SSO & Directory Sync menu item (#4333)
## What The organization side menu previously showed an "Enterprise" badge next to the SSO & Directory Sync item for any org not on the enterprise plan. That badge is now removed so the item renders without it. ## Screenshot (before) <img width="1428" height="649" alt="CleanShot 2026-07-10 at 08 24 41" src="https://github.com/user-attachments/assets/b9787363-972f-4dd5-bf61-486680f49f4c" /> |
||
|
|
7a14188663 |
fix(webapp): limit account email address length (#4330)
## Summary Limits user account email addresses to 254 characters in profile settings and onboarding. Oversized values are rejected before the uniqueness lookup, and the form fields enforce the same limit in the browser. ## Fix Both email update flows use a shared bounded email schema. Basic validation completes before the uniqueness lookup runs. |
||
|
|
a9815f745c | fix(cli): stop dev runs crashing when a rebuild removes an in-use build dir (#4276) | ||
|
|
bb34a2e224 |
fix(webapp): scope development branches to each member (#4323)
## Summary Allow each organization member to use the same development branch name without colliding with another member's environment. Fixes #4320. ## Fix Development branches now use the existing member-scoped project, slug, and organization-member key for upserts. Preview branches retain their project-wide shortcode behavior. New development branches receive distinct shortcodes while keeping their readable, member-scoped slugs. Existing branches continue to resolve through the member-scoped key, so this requires no migration or backfill. |
||
|
|
95307ba33c |
fix(webapp): tidy Usage page credits display (#4322)
Two small corrections to the organization **Usage** page credits display. ### 1. Label the credits panel "Credits" (was "Promo credits") The panel surfaces any credit balance, not only promo-code redemptions, so "Promo credits" is misleading when the credits come from another source. Renamed the heading to "Credits". ### 2. Don't show "Included usage" for Enterprise orgs Enterprise inherits the Pro plan's `includedUsage` value, so the Usage bar rendered an "Included usage: $50" tier marker for Enterprise organizations. Enterprise bills against prepaid credits rather than a per-month included-usage tier, so the marker was misleading. The `tierLimit` marker is now suppressed for Enterprise (`plan.type === "enterprise"`). Verified with `pnpm run typecheck --filter webapp`. |
||
|
|
0b2919465c |
feat(webapp): redesign the side menu project and organization menus (#4066)
Redesign of the main side menu: separates Projects and Accounts from the Organization menu and makes the menu resizable. **Main changes** - **Organization & Account menus**: the top-left is now a dedicated organization menu (Settings, Usage, Billing, Team, SSO, integrations), with a separate account menu beside it (Profile, PATs, Security, Logout). - **Project switcher**: a new Project section above the Environment selector. - **Resizable side menu**: drag the right edge to set a custom width (saved per user), or click the edge to collapse/expand. - **Environment selector**: reworked to match the Project menu, including dev-branch handling. - **Account Profile page**: redesigned into the Security page's row-and-divider layout. Preview URL: https://samejr-org-menu-update.triggerlabs.dev/ https://github.com/user-attachments/assets/9b199576-6037-4ea6-9bdb-3ee15265b8c2 |
||
|
|
e2d3b8388c |
feat(sdk): return lastEventId from writeTurnComplete and typed capture result (#4304)
## Summary
Two ergonomic additions for custom chat-agent loops that own the turn
loop (`chat.customAgent`, `chat.createSession`, and the hand-rolled
primitives).
`chat.writeTurnComplete()` now resolves to `{ lastEventId }`, the resume
cursor for the start of the next turn. A custom loop can persist it
straight from the task instead of round-tripping it back from the client
after the turn ends. The value was already produced internally by the
turn-complete write; the public wrapper simply discarded it.
`chat.pipeAndCapture()` no longer throws when a stream is stopped or
fails. It now resolves to a `PipeAndCaptureResult` carrying any partial
`message` captured before the stop or failure, a typed `status`
(`"complete" | "aborted" | "error"`), and the `error` on failure.
Previously a failed stream threw and the partial was lost, and an abort
was captured only when the AI SDK happened to fire `onFinish` in time.
```ts
const { message, status, error } = await chat.pipeAndCapture(result, { signal });
if (message) conversation.addResponse(message);
if (status === "error") logger.error("turn failed", { error });
const { lastEventId } = await chat.writeTurnComplete();
await db.chats.update(chatId, { lastEventId });
```
## Design
`pipeAndCapture` wraps the pipe in a `try/catch` and classifies the
outcome from the abort signal (a stop drains the source stream cleanly
rather than throwing) versus a thrown error. It also races the
`onFinish` capture against a timeout so a hard stop that prevents
`onFinish` from firing can't hang the caller. This mirrors the capture
path `chat.agent` already uses internally.
The `finishReason` from `onFinish` is surfaced too, since it was already
captured on the built-in path.
The internal `turn.complete()` helper keeps its existing contract: it
still returns `UIMessage | undefined`, still throws on a genuine stream
failure, and still discards output on a full run cancel.
## Breaking change
`chat.pipeAndCapture` previously resolved to `UIMessage | undefined`.
Call sites now read `.message` off the result. This is a young,
low-level API; the docs examples are updated in this PR.
|
||
|
|
6642c8b785 |
fix(webapp): prevent duplicate envs from provision race (#4261)
Fixes TRI-12078 ## Summary Prevents concurrent environment setup requests from creating duplicate Staging and Preview environments. ## Fix Adds database-enforced uniqueness for root Staging and Preview environments. If two requests race, the losing request loads the environment created by the winner and continues successfully instead of creating a duplicate or returning an error. |
||
|
|
d05f1a7398 |
chore(webapp): migrate from Remix compiler to Vite (#4188)
Replaces Remix compiler with the Vite plugin. The Express server (cluster, socket.io, ws) and the Docker image contract are unchanged. |
||
|
|
dc87b884e7 |
chore: upgrade to typescript 6 (#4310)
## Summary Upgrades the workspace to TypeScript 6.0.3 and applies the compiler, type, and build configuration changes required to preserve package layouts and existing runtime behavior, apart from correcting the HTTP status field used for deployment connection errors. ## Compatibility - Centralizes TypeScript 6.0.3 through the pnpm workspace catalog. - Replaces compiler options and module resolution modes that TypeScript 6 no longer accepts. - Restores explicit Node types where TypeScript 6 no longer includes them transitively. - Adds explicit declaration build roots that preserve each package's existing output layout. - Patches tsup to stop injecting the removed `baseUrl` option during declaration builds. - Uses type-only assertions for stricter typed-array and stream definitions without changing runtime behavior. - Reads the EventSource v3 HTTP status from `code`, so deployment connection errors include it correctly. - Keeps standalone CLI compatibility fixtures pinned to their existing TypeScript version and lockfiles. `turbo run typecheck` and the complete PR test suite are green. |
||
|
|
cbec61309a |
fix(webapp): fix promo page heading typography (#4311)
## What The `/promo` page heading rendered with overlapping lines — the two lines of "Promo codes are for new accounts" collided. ## Why The page used `Header2` stretched to display sizes (`sm:text-2xl md:text-3xl lg:text-4xl`), but `Header2` bakes in a fixed `leading-6` (24px). A 36px font in a 24px line box makes wrapped lines overlap. It only showed at `sm`+ widths and only on headings that wrap to 2+ lines, which is why it slipped through — the short single-line headings on the same page looked fine. ## Fix Switch both headings to `Header1` — the page-title primitive the sibling login pages (`login._index`, `login.magic`) already use for exactly this size. Add `leading-tight` (relative line-height, scales with font size, and this heading uniquely wraps to two lines) and `pb-4` to match the login pages' spacing convention. ## Testing Manually verified the signed-in view (`/promo` while logged in) renders as two clean, non-overlapping lines across breakpoints. Pure CSS/layout change — no automated test. |
||
|
|
325b906319 |
chore: release v4.5.6 (#4317)
🚀 Publish Trigger.dev Docker / units (push) Failing after 0s
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 20s
🚀 Publish Trigger.dev Docker / scan-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / scan-supervisor (push) Has been skipped
🚀 Publish Trigger.dev Docker / publish-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / publish-worker-v4 (push) Has been skipped
🧭 Helm Chart Release / release (push) Has been cancelled
🧭 Helm Chart Release / lint-and-test (push) Has been cancelled
🚀 Publish Trigger.dev Docker / 📣 Dispatch main image (push) Has been cancelled
## Summary 5 improvements, 9 bug fixes. ## Breaking changes - Self-hosted deployments no longer ship shared default credentials; fresh installs generate their own. If yours still uses a previously published default, set a unique value before upgrading, or set `ALLOW_INSECURE_DEFAULT_SECRETS=true` to keep booting while you migrate. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) ## Improvements - Require explicit browser approval for CLI and MCP login, with resilient polling while approval is pending. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Deployed task telemetry now reports the deployment identifier (e.g. `deployment_abc123`) in the `worker.id` attribute, instead of an opaque internal value. Upgrade to get the readable identifier in your own OpenTelemetry exporters. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Prevent prototype pollution when applying run metadata operations or reconstructing nested telemetry attributes, while preserving legitimate `constructor` and `prototype` fields. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Add helpers to mint and verify the deployment-scoped token used to authenticate run controllers to the platform. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) ## Server changes These changes affect the self-hosted Docker image and Trigger.dev Cloud: - Added optional request rate limiting for telemetry ingestion endpoints. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Background-worker deployment lookups are now scoped to the authenticated environment. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Updating a GitHub App installation from the callback flow is now scoped to your own organization, so an installation ID belonging to another organization can no longer be used to refresh that organization's installation record. The GitHub App installation session is also now single-use, so completing an installation callback invalidates its state and it can no longer be replayed. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Scope schedule and environment-variable writes to the caller's project and environment ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Reject compute snapshot callbacks that do not match the snapshot request that created them. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Require secret-key authentication to initialize the session out (agent→client) stream, matching the append route. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Live run and trace subscriptions now validate their identifiers more strictly and only return data from your own organization. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Window-function names in the query compiler are now validated against the allowlist, matching how other function calls are handled. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Authenticate run controllers to the platform with a signed, deployment-scoped token. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Verify that worker actions (starting, completing, and continuing a run, and reading its snapshots) target a run belonging to the caller's environment. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) <details> <summary>Raw changeset output</summary> # Releases ## @trigger.dev/build@4.5.6 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.6` ## trigger.dev@4.5.6 ### Patch Changes - Require explicit browser approval for CLI and MCP login, with resilient polling while approval is pending. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Deployed task telemetry now reports the deployment identifier (e.g. `deployment_abc123`) in the `worker.id` attribute, instead of an opaque internal value. Upgrade to get the readable identifier in your own OpenTelemetry exporters. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Updated dependencies: - `@trigger.dev/core@4.5.6` - `@trigger.dev/build@4.5.6` - `@trigger.dev/schema-to-json@4.5.6` ## @trigger.dev/core@4.5.6 ### Patch Changes - Prevent prototype pollution when applying run metadata operations or reconstructing nested telemetry attributes, while preserving legitimate `constructor` and `prototype` fields. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Require explicit browser approval for CLI and MCP login, with resilient polling while approval is pending. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) - Add helpers to mint and verify the deployment-scoped token used to authenticate run controllers to the platform. ([#4316](https://github.com/triggerdotdev/trigger.dev/pull/4316)) ## @trigger.dev/python@4.5.6 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.6` - `@trigger.dev/build@4.5.6` - `@trigger.dev/sdk@4.5.6` ## @trigger.dev/react-hooks@4.5.6 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.6` ## @trigger.dev/redis-worker@4.5.6 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.6` ## @trigger.dev/rsc@4.5.6 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.6` ## @trigger.dev/schema-to-json@4.5.6 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.6` ## @trigger.dev/sdk@4.5.6 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.6` </details> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>helm-v4.5.6 v.docker.4.5.6 v4.5.6 |
||
|
|
6997aeb05e |
fix: security release 2026-07-08 (#4316)
⚒️ Publish Worker (v4) / build (supervisor) (push) Has been cancelled
|
||
|
|
cc748422d8 | test(webapp): replace slow metadata replica guard with unit test (#4312) | ||
|
|
1cbe25bd1d |
chore: release v4.5.5 (#4267)
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 1s
🚀 Publish Trigger.dev Docker / scan-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / scan-supervisor (push) Has been skipped
🚀 Publish Trigger.dev Docker / publish-webapp (push) Has been skipped
🚀 Publish Trigger.dev Docker / publish-worker-v4 (push) Has been skipped
🚀 Publish Trigger.dev Docker / units (push) Failing after 0s
🧭 Helm Chart Release / lint-and-test (push) Has been cancelled
🧭 Helm Chart Release / release (push) Has been cancelled
🚀 Publish Trigger.dev Docker / 📣 Dispatch main image (push) Has been cancelled
## Summary 5 improvements, 5 bug fixes. ## Improvements - Add experimental Node.js 24 and 26 task runtimes. Set `runtime` to `experimental-node-24` or `experimental-node-26` in `trigger.config.ts`. ([#4085](https://github.com/triggerdotdev/trigger.dev/pull/4085)) - Add `defaultRegion` to the project GET and list API responses; null when unset. ([#4146](https://github.com/triggerdotdev/trigger.dev/pull/4146)) ## Server changes These changes affect the self-hosted Docker image and Trigger.dev Cloud: - Transient internal sync failures are now retried quietly instead of surfacing as errors. ([#4270](https://github.com/triggerdotdev/trigger.dev/pull/4270)) - Optionally route ClickHouse read traffic to a read replica while writes stay on the primary. Set `CLICKHOUSE_READER_URL` to move all reads, or target the busiest paths with `RUNS_LIST_CLICKHOUSE_URL` (runs list) and `EVENTS_READER_CLICKHOUSE_URL` (traces, spans, logs). All optional; unset keeps current behavior. ([#4081](https://github.com/triggerdotdev/trigger.dev/pull/4081)) - Remove the deprecated realtime stream write endpoint used by retired v3 task clients. ([#4250](https://github.com/triggerdotdev/trigger.dev/pull/4250)) - Fix batchTrigger requests that set a per-item idempotency key failing with an error instead of creating and deduplicating the runs ([#4271](https://github.com/triggerdotdev/trigger.dev/pull/4271)) - Speed up idempotency checks on `batchTrigger` calls that use idempotency keys. Large batches against a task with a big run history no longer degrade to multi-second lookups. ([#4255](https://github.com/triggerdotdev/trigger.dev/pull/4255)) - The "Preview branches" usage on the Limits page now counts only preview branches. ([#4283](https://github.com/triggerdotdev/trigger.dev/pull/4283)) - Avoid opening a redundant database connection pool when the legacy and primary databases are the same server, preventing connection usage from doubling. ([#4253](https://github.com/triggerdotdev/trigger.dev/pull/4253)) - Fix pages occasionally loading unstyled or failing to load during a deploy. The dashboard now reloads automatically to recover. ([#4282](https://github.com/triggerdotdev/trigger.dev/pull/4282)) <details> <summary>Raw changeset output</summary> # Releases ## @trigger.dev/build@4.5.5 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.5` ## trigger.dev@4.5.5 ### Patch Changes - Add experimental Node.js 24 and 26 task runtimes. Set `runtime` to `experimental-node-24` or `experimental-node-26` in `trigger.config.ts`. ([#4085](https://github.com/triggerdotdev/trigger.dev/pull/4085)) - Updated dependencies: - `@trigger.dev/core@4.5.5` - `@trigger.dev/build@4.5.5` - `@trigger.dev/schema-to-json@4.5.5` ## @trigger.dev/core@4.5.5 ### Patch Changes - Add experimental Node.js 24 and 26 task runtimes. Set `runtime` to `experimental-node-24` or `experimental-node-26` in `trigger.config.ts`. ([#4085](https://github.com/triggerdotdev/trigger.dev/pull/4085)) - Add `defaultRegion` to the project GET and list API responses; null when unset. ([#4146](https://github.com/triggerdotdev/trigger.dev/pull/4146)) ## @trigger.dev/python@4.5.5 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.5` - `@trigger.dev/build@4.5.5` - `@trigger.dev/sdk@4.5.5` ## @trigger.dev/react-hooks@4.5.5 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.5` ## @trigger.dev/redis-worker@4.5.5 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.5` ## @trigger.dev/rsc@4.5.5 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.5` ## @trigger.dev/schema-to-json@4.5.5 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.5` ## @trigger.dev/sdk@4.5.5 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.5` </details> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>v4.5.5 helm-v4.5.5 v.docker.4.5.5 |
||
|
|
d5f1696a97 |
ci(publish): unify image scanning across published images (#4306)
## What Run the shared Trivy image scan on every published image through a single reusable workflow. ## Changes - Generalise the image-scan workflow (`trivy-image-webapp.yml` -> `trivy-image.yml`) - it was already parameterised by `image-ref`; only the run-summary label was image-specific. - `publish-worker-v4.yml`: expose `version` + `image_repo` as workflow outputs (single-entry matrix, so unambiguous). - `publish.yml`: run the shared scan from each publish job (`scan-webapp`, `scan-supervisor`). Report-only (writes a table to the run summary), OS packages only (`vuln-type: os` - library deps stay with Dependabot), never blocks the publish. |
||
|
|
a7c734c223 |
test: caller-driven replica-lag + idempotency guards (stacked on #4284) (#4285)
## Stacked on #4284 — tests only This PR contains **only the tests** that guard the production fixes in #4284 (its base). Review #4284 first; this branch adds no production code. ## What Caller-driven replica-lag and idempotency guards for every fixed site: - Each guard **drives the real exported caller** (route loader/action, presenter `.call()`, service, or engine method) against a **real Postgres** with the owning replica frozen via the shared `laggingReplica` testcontainer primitive — never a store-seam reimplementation. - For a **fixed** site the guard goes **RED when the production change is reverted**; for a **tolerated read-view** site it's a caller-driven **GREEN** proof the miss self-heals (returns null/empty, no mutation, row live on primary). - The **global-scope idempotency** guard drives the real dedup + claim path through a **real `MollifierBuffer` over a Redis testcontainer** (real SETNX/poll/publish), and covers the cross-DB **andWait** waitpoint wiring and the **expired/failed clear-and-recreate** reacquire cases. Run with `vitest --no-file-parallelism` (testcontainers). Verified GREEN, and revert→RED verified per fixed site. |
||
|
|
ae96b6c175 |
fix: read-your-writes + global-scope idempotency correctness under the run-ops split (#4284)
## What & why
Two related correctness fixes for the run-ops DB split. Under the split,
run-store reads can route to a **lagging read replica**; a just-written
run/waitpoint/batch can then be missed, causing a wrong decision.
**1. Read-your-writes → owning primary.** Surfaced first as an
intermittent `wait.until({ idempotencyKey })` re-wait on retry. Auditing
the run-store read surface found the same class at sibling sites (some
gating mutations or returning spurious 404s, others
tolerable/self-healing). Reads that must observe their own writes now
route to the owning **primary**
(`findRun`/`findWaitpoint`/`findBatchTaskRunByFriendlyId` →
`*OnPrimary`, a primary re-read on a miss, or a retryable 404 where the
SDK polls). Read-view reads stay on the replica. All additive — the
happy path is unchanged.
**2. Global-scope idempotency across the split.** A `global`-scope key
carries no per-run salt, so the same `(env, task, key)` triggered
concurrently from parents resident on **different** run-ops DBs could
dedup-miss on each DB and create a duplicate (the per-DB unique index
can't enforce cross-DB uniqueness). Such triggers (global scope, or
scope-absent, while split is active) are serialized through the existing
Redis idempotency claim, the loser resolves the winner by id across both
DBs, and the claim is reacquired on the expired/failed
clear-and-recreate path. `run`/`attempt` scope embed the run id and
never contend.
## Stacked for review
This is the **base** of a 2-PR stack, split so review is easier:
- **This PR** — production code only (34 files).
- **Stacked tests PR →
https://github.com/triggerdotdev/trigger.dev/pull/4285** — the
caller-driven guards (55 test files) on top of this branch.
## Validation
Local run-ops split, **both 2-DB and 3-DB**, fresh boot on this branch:
SDK canary 64/71 (only the known concurrency/input-streams/s3 failures),
quarantine sweep **0 unexpected** (340 pass / 16 known / 4 local) in
each topology, dashboard e2e 0 failed. No product regressions.
|
||
|
|
cecdfd94be | fix: only count preview branches toward the preview branch limit (#4283) | ||
|
|
285666290f |
ci(webapp): wire the run-ops legacy guard into CI and add oxlint residency fences (#4279)
## What - Runs `apps/webapp/scripts/runOpsLegacyGuard.ts --check` as its own PR job (`runops-guard`), so code that reaches a run-graph table through the control-plane Prisma client instead of the RunStore fails the build. - Adds a `trigger-runops` oxlint plugin with two fast, in-editor rules scoped to `apps/webapp/app`: one for direct `prisma.taskRun`-style access, one for a control-plane client wired into a read-through slot. These are the cheap fence; the guard is the type-aware gate. - Fixes `CancelTaskRunService.callV1`: historical V1 runs are legacy-resident, so its two finalize writes now go through `runOpsLegacyPrisma` instead of the control-plane client (they'd miss the row once legacy is a separate database). - Regenerates the guard baseline, which had drifted stale (it referenced files deleted in an earlier PR). ## Why The guard existed but ran nowhere, so its baseline rotted and a real residency gap (the V1 cancel writes) sat undetected. Wiring it into CI turns it into a ratchet against new control-plane run-graph access. ## Verification Local, against a clean regen: `oxfmt --check`, `oxlint .`, `guard --check`, and `typecheck --filter webapp` all pass. Remaining baseline entries are 4 batch-results router reads through type-opaque `as PrismaReplicaClient` casts (correct at runtime, accepted) + 2 sanctioned legacy annotations. |
||
|
|
821972176d |
fix(run-store,webapp): correct split-database read routing, write residency, and batches list ordering (#4272)
## Summary Correctness and performance fixes for deployments that split run data across more than one database. Single-database / self-hosted deployments are unaffected (they collapse to a single read/write path). - **Batches list (dashboard):** for some organizations the Batches list could hide older batches or show them out of order. It now orders and paginates by creation time (with the id as a stable tiebreak), so every batch appears exactly once, newest first. The pagination cursor format changes; older in-flight cursors simply restart from the first page. - **Reads:** waitpoint and snapshot lookups that are keyed by a single run now read only the database that holds that run instead of querying both, removing redundant queries on hot paths (unblock, snapshot reads). - **Writes:** environment-scoped writes with no owning run (standalone wait tokens, waitpoint tags, idempotency-key resets) now land in the same database as that environment's runs, rather than defaulting to the other one. An idempotency-key reset also falls back to the other database when it matches nothing, so a reset still clears the key wherever the run actually lives. ## Notes Verified end-to-end against multi-database setups: run-keyed reads and env-scoped writes land on the correct database with no cross-database writes, and the batches list surfaces every batch in creation order. New tests cover the batches ordering/reachability and the write-residency routing. |
||
|
|
0ff0abd776 |
fix(webapp): recover from stale /build assets via a bounded reload (#4282)
## Problem The webapp's HTML references content-hashed `/build` assets, and each running instance contains exactly one build and returns 404 for asset hashes it doesn't have. During a rolling deploy a client can hold HTML from one build while a request for one of its assets is served by an instance on a different build → missing styles or a failed chunk load. ## What this does On a `/build` stylesheet/script/chunk load failure, the client does a **bounded full-document reload** (at most 2 per 5 minutes, tracked in `sessionStorage`) so the page reloads onto a single consistent build. That's the whole mechanism — no polling, no `fetch` interception, no blocking overlay, no form snapshotting. - `apps/webapp/app/components/StaleAssetRecovery.tsx` — authored as a typed, lint-checked function and serialized to an inline script via `.toString()` (so the logic is real, reviewable code, not an opaque string), injected before `<Links />`, production only. - Detection: capture-phase `error` listener for `<link>`/`<script>`/modulepreload failures under `/build/`, plus an `unhandledrejection` guard for dynamic-import failures. - Guards: once-per-page re-entrancy guard, the bounded reload budget, and a `navigator.onLine` check so it never reloads into an offline error page. - Unit tests in `StaleAssetRecovery.test.ts`. ## Relationship to #4260 Replaces the recovery introduced in #4260 (reverted in #4280) with a much smaller, reload-only approach — the previous version intercepted `fetch` and could turn a data request into a navigation, and showed a full-screen overlay on any asset error; this drops both. ## `/build-version` compatibility shim `apps/webapp/server.ts` adds a tiny `GET /build-version` endpoint (build id only, `no-store`). A previously-deployed client build polls it after an asset failure and reloads once it sees a newer build, so those older tabs recover in one reload instead of getting stuck. Temporary — safe to remove once older clients have cycled out. It deliberately does **not** re-add an `X-Build-Id` response header. ## Also Restores the `.server-changes` writing guidance in `.claude/rules/server-apps.md` (reverted alongside #4260). ## Self-hosting note Recovery is most reliable when your load balancer keeps a client on one instance for the duration of a deploy (short session stickiness) — the reload then lands on a consistent build in one hop. |
||
|
|
73d966ad22 |
chore(webapp): remove deprecated realtime stream write action (#4250)
Removes the deprecated realtime stream write action kept for retired v3 task clients. Supported clients use the targeted stream write routes, while the existing stream read loader remains unchanged. |
||
|
|
051d7080d6 |
Revert "fix(webapp): survive asset hash rotation across rolling deploys (#4260)" (#4280)
Standard `git revert` of #4260. Its client-side stale-asset recovery is net-negative during normal deploys: - The `fetch` interception treats any `?_data=` request (Remix loader **and** action traffic) as a navigation and, on a build-id mismatch, `location.assign`es the tab to the fetched URL — an open dashboard tab can be hard-navigated to a raw data URL during a rolling deploy, losing unsaved input. - Any transient `/build` asset error (a network blip, an extension, an unrelated failed dynamic import) blanks the page behind a full-screen overlay for ~60s before offering a manual reload. - It serialized form field values to `sessionStorage` to restore them across the reload. This returns the webapp to the pre-#4260 baseline as a fast, low-risk step. Follow-ups (separate PRs): - a minimal reload-only recovery to replace this, - restore the unrelated `.claude/rules/server-apps.md` docs tidy-up from #4260 (via cherry-pick), - a load-balancer stickiness change addressing the root cause. |
||
|
|
939c00782d | feat(webapp): show runtime versions in deployment lists (#4273) | ||
|
|
eccc8e3ae0 |
fix: .env.example file state DIRECT_URL without ref (#4275)
The `DIRECT_URL=${DATABASE_URL}` wasn't working in at least one user of
the var.
|
||
|
|
d7ec75d5ad |
feat(runtime): add experimental Node.js 24 and 26 task runtimes (#4085)
⚒️ Publish Worker (v4) / build (supervisor) (push) Has been cancelled
## Summary Adds experimental Node.js 24 and 26 task runtimes through the `experimental-node-24` and `experimental-node-26` config values. Existing runtime defaults and the `node`, `node-22`, and `bun` behavior remain unchanged. The unprefixed `node-24` and `node-26` config values remain unavailable until the runtimes are ready for general use. ## Design Experimental config values normalize to canonical runtime identifiers before build manifests are created, keeping deployment metadata and execution behavior consistent. Kubernetes task pods also use the runtime-default seccomp profile so modern Node.js versions fall back from io_uring to checkpoint-compatible system calls.re2-prod-supervisor-tri-11473 re2-test-supervisor-tri-11473 |
||
|
|
43250522a5 |
fix(run-store): fix batch idempotency lookup on the dedicated run-ops store (#4271)
## Summary `batchTrigger` requests that set a per-item `idempotencyKey` failed with a 500 when the run-store is split across databases: the per-item idempotency lookup errored before any run was created. Batches without per-item keys, single `trigger` idempotency, and batch-level (`idempotency-key` header) idempotency were unaffected. ## Root cause `findRunsByIdempotencyKeys` built its `UNION ALL` of per-key point-lookups with `@trigger.dev/database`'s `Prisma.sql` / `Prisma.join`, then executed it on whichever store client it was handed. On the dedicated run-ops store that client is a *separate* generated Prisma client, and a `Sql` object from a different generated client is not recognized: the bare `$queryRaw(Prisma.join(...))` form dropped the query text entirely (`Argument \`query\` is missing`). The tagged-template form is no better here: joining nested `Prisma.sql` fragments across the two clients mis-numbers the bound parameters (`syntax error at or near "$1"`). ## Fix Build the lookup as a plain parameterized string and run it via `$queryRawUnsafe` with positional placeholders and bound values, so it no longer depends on which generated client executes it. The query text contains only static SQL and integer placeholders; every value (`runtimeEnvironmentId`, `taskIdentifier`, each key) is bound, so it is not a raw-interpolation site. Same per-key point-lookup shape as before, no change on the single-client path. Verified end-to-end against a bundled build with the run-store split enabled: before the fix, `batchTrigger` with a per-item key 500s; after, it returns the runs and dedups correctly across fresh, repeat, and mixed batches. |
||
|
|
80cbc46bf6 |
fix(webapp): log transient Attio 5xx/429 at warn instead of error (#4270)
The signup → Attio sync (`attio.server.ts` `#assert`) logged every non-2xx response at `error` level and threw the same way regardless of status. Transient upstream failures (5xx/429) are retried by the common worker and self-heal, so treating them as errors created false alerts for something that isn't actually a bug. Now `#assert` splits the two cases: - **5xx / 429** — Logged at `warn` and thrown with `logLevel: "warn"`, so they continue to be retried but don't raise error-level alerts. This reuses the same pattern the worker already honors (`directorySyncEffects`). - **4xx** — Unchanged: logged at `error` and thrown, so genuine integration bugs (schema, permissions, auth, etc.) remain visible. There is no behavior change to retries or the signup flow. This is a server-only change. --------- Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> |
||
|
|
890dd66eb5 |
feat(webapp): route ClickHouse reads to an optional read replica (#4081)
## Summary Adds optional configuration to send ClickHouse read traffic to a separate instance (for example a read replica) while writes stay on the primary `CLICKHOUSE_URL`. This lets operators offload read load (runs list, traces, logs, queries) from the cluster that handles inserts. Fully backwards compatible: with nothing new set, every client resolves to `CLICKHOUSE_URL` exactly as before. ## What it adds - `CLICKHOUSE_READER_URL` (optional): a single reader endpoint that the read-only clients fall back to. Read clients resolve `<own URL> ?? CLICKHOUSE_READER_URL ?? CLICKHOUSE_URL`. The task-events client (which both inserts events and reads traces, spans, and logs) is built as a reader/writer pair so queries use the reader while inserts stay on `CLICKHOUSE_URL`. - `RUNS_LIST_CLICKHOUSE_URL` (optional): a dedicated client for the runs list (dashboard list, runs list API, live reload, child-status counts), so the highest-traffic read path can target its own instance. ## Safety Only read-only clients fall back to the reader: logs, query, admin, runs list, the pending-version lookup, and the realtime run-id resolver. The query page is constrained to read-only (the TSQL parser rejects anything that is not a `SELECT`, and a `readonly` setting is applied). The task-events client routes inserts to the writer and queries to the reader per method, so a write can never reach the reader. Pure-write clients (event inserts, replication) always use `CLICKHOUSE_URL`. Note: this PR targets a baseline branch rather than `main` so the diff stays scoped to the read-replica changes. It will be retargeted to `main` before merge. --------- Co-authored-by: Eric Allam <eallam@icloud.com> |
||
|
|
b902e65dfb |
chore: standardise internal node on 24.18.0 (#4254)
## Summary Updates the internal development, CI, and runtime-image Node version to 24.18.0. SDK compatibility coverage continues to include Node 20, 22, 24, and 26. The Node type definitions and the package-manager lockfiles now resolve against Node 24 types. |