feat(cli): server-selected deploy build path - #4803
Conversation
…feature flags Adds GET /api/v1/projects/:projectRef/:env/deploy-settings, authenticated with the environment API key, returning the build path the CLI should use (depot, native, native_local_bundle) and where it came from. The path is resolved from four new enum feature flags, settable globally and per organization through the existing admin UIs: deployBuildPath plus deployBuildPathPreview/Staging/Production. Precedence: native unavailable on this install, project opt-out (disableNativeBuildServer), org env-type flag, org flag, global env-type flag, global flag, then depot.
…ays otherwise Before building or uploading anything, deploy fetches the deploy settings for the target environment and dispatches on the returned build path. Explicit flags skip the round-trip: --native-build-server, --local-bundle, --local-build, --detach, and the new --depot-build. If the request fails or times out (3s) the CLI falls back to Depot and says so.
…uns off the native path The deploy settings request now makes one attempt so the 3s timeout is the real upper bound instead of ~10s of retries against an aborted signal. A 404 (server without the endpoint) falls back to Depot silently since that is the behaviour that server expects. When the server picks the native build server but --dry-run is set, the CLI stays on Depot, because the native path has no dry-run mode and would deploy for real.
🦋 Changeset detectedLatest commit: 5d4573e The changes in this PR will be included in the next version bump. This PR includes changesets to release 27 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review. 📜 Recent review details⏰ Context from checks skipped due to timeout. (50)
🧰 Additional context used📓 Path-based instructions (11)**Import subpaths only** (never root).📄 CodeRabbit inference engine (AGENTS.md) Files:
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:📄 CodeRabbit inference engine (AGENTS.md) Files:
Add crumbs as you write code — not just when debugging. Mark lines with📄 CodeRabbit inference engine (AGENTS.md) Files:
Implement `deploy.ts` command in `src/commands/` for production deployment📄 CodeRabbit inference engine (packages/cli-v3/CLAUDE.md) Files:
Use zod for validation in packages/core and apps/webapp📄 CodeRabbit inference engine (.github/copilot-instructions.md) Files:
CLI command definitions should be located in `src/commands/`📄 CodeRabbit inference engine (packages/cli-v3/CLAUDE.md) Files:
Never import the root package (`@trigger.dev/core`). Always use subpath imports such as `@trigger.dev/core/v3`, `@trigger.dev/core/v3/utils`, `@trigger.dev/core/logger`, or `@trigger.dev/core/schemas`📄 CodeRabbit inference engine (packages/core/CLAUDE.md) Files:
Use function declarations instead of default exports📄 CodeRabbit inference engine (.github/copilot-instructions.md) Files:
MDX documentation pages must include frontmatter with title (required), description (required), and sidebarTitle (optional) in YAML format📄 CodeRabbit inference engine (docs/CLAUDE.md) Files:
Use types over interfaces for TypeScript📄 CodeRabbit inference engine (.github/copilot-instructions.md) Files:
When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc) Files:
🔇 Additional comments (3)
WalkthroughThe change adds deploy build-path feature flags and server-side resolution. A new deploy-settings API route validates access, resolves project and environment settings, and returns the selected build path. The CLI retrieves these settings, supports explicit native and Depot options, applies local-bundle and detach rules, handles timeouts and fallbacks, and dispatches based on the final path. The response schema accepts informational source strings. Tests, documentation, rate limiting, and release metadata are updated. 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 10 files. (2 skipped: 2 unsupported.) Full details: Description checkExplanation The description clearly explains the server-selected deploy build path, precedence, fallback behavior, flags, and endpoint. It omits the required issue reference, checklist, testing steps, changelog section, and screenshots section.
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Observability mapAs of 20/100 over 450 measured of 468 entry points (base 20, no change) What this PR changed
FIX FIRST
AUDIT 3 of 50 sensitive mutations record an actor. 47 without one. What the score is made ofThe score and findings here are report-only and never gate the merge. Separately, a required test suite keeps this tool's symbol and route lists in sync with the code they name, and can fail a pull request that renames or removes a symbol they reference, or that adds the first route with a segment they anticipate. Each failure names the list to edit. The rules and their reasons: internal-packages/observability-map/README.md. |
--native-build is the visible way to force the native build server; --native-build-server stays as a hidden alias. --detach no longer implies native: it only works on the native paths, so a deploy that resolves to Depot with --detach set fails with a pointer to --native-build.
@trigger.dev/build
trigger.dev
@trigger.dev/core
@trigger.dev/python
@trigger.dev/react-hooks
@trigger.dev/redis-worker
@trigger.dev/rsc
@trigger.dev/schema-to-json
@trigger.dev/sdk
commit: |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/core/src/v3/schemas/api.ts (1)
842-865: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd crumbs for the deploy-settings flow.
The new cross-layer flow has no
//@Crumbsmarker or `// `#region` `@crumbsblock. Add temporary crumbs with an approved namespace, then remove them withagentcrumbs stripbefore merge.
packages/core/src/v3/schemas/api.ts#L842-L865: add schema-definition crumbs.apps/webapp/app/v3/featureFlags.ts#L41-L46: add feature-flag-resolution crumbs.apps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts#L25-L78: add endpoint-resolution crumbs.apps/webapp/app/services/deploymentApiPaths.server.ts#L7-L7: add rate-limit-selection crumbs.packages/cli-v3/src/apiClient.ts#L490-L501: add deploy-settings request crumbs.packages/cli-v3/src/commands/deploy.ts#L1280-L1337: add build-path-selection crumbs.As per coding guidelines: “Add crumbs as you write code — not just when debugging.”
Source: Coding guidelines
apps/webapp/test/deployBuildPath.test.ts (1)
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove this test next to its source file.
apps/webapp/test/deployBuildPath.test.tsis separate fromapps/webapp/app/v3/deployBuildPath.ts. Move the test toapps/webapp/app/v3/deployBuildPath.test.ts.As per coding guidelines, “Test files go next to source files.”
Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: f591214f-2672-46b2-9174-46c33758e488
📒 Files selected for processing (9)
.changeset/deploy-build-path-settings.mdapps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.tsapps/webapp/app/services/deploymentApiPaths.server.tsapps/webapp/app/v3/deployBuildPath.tsapps/webapp/app/v3/featureFlags.tsapps/webapp/test/deployBuildPath.test.tspackages/cli-v3/src/apiClient.tspackages/cli-v3/src/commands/deploy.tspackages/core/src/v3/schemas/api.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.
📜 Review details
⏰ Context from checks skipped due to timeout. (48)
- GitHub Check: report
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
- GitHub Check: sdk-compat / Node.js 26.4 (warp-ubuntu-latest-x64-4x)
- GitHub Check: sdk-compat / Node.js 20.20 (warp-ubuntu-latest-x64-4x)
- GitHub Check: obsmap / 🧪 Unit Tests: Observability Map
- GitHub Check: sdk-compat / Node.js 24.18 (warp-ubuntu-latest-x64-4x)
- GitHub Check: sdk-compat / Deno Runtime
- GitHub Check: e2e / 🧪 CLI v3 tests (warp-ubuntu-latest-x64-4x - pnpm)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: e2e / 🧪 CLI v3 tests (warp-ubuntu-latest-x64-4x - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (warp-windows-latest-x64-8x - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (warp-windows-latest-x64-8x - pnpm)
- GitHub Check: packages / 🧪 Unit Tests: Packages (2, 3)
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
- GitHub Check: sdk-compat / Bun Runtime
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
- GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
- GitHub Check: internal / 🧪 Unit Tests: Internal
- GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
- GitHub Check: runops-guard / runops-guard
- GitHub Check: fk-cascade-guard / fk-cascade-guard
- GitHub Check: typecheck / typecheck
- GitHub Check: code-quality / code-quality
- GitHub Check: 🛡️ E2E Auth Tests (full)
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (19)
Use Remix flat-file route conventions with dot-separated segments; for example, `api.v1.tasks.$taskId.trigger.ts` maps to `/api/v1/tasks/:taskId/trigger`.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts
New code must target Run Engine V2 through the singleton in `app/v3/runEngine.server.ts`; do not reintroduce V1 execution paths. V1 branches may only reject or finalize gracefully with a clean 4xx.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/v3/featureFlags.tsapps/webapp/app/v3/deployBuildPath.ts
Never use `request.signal` to detect client disconnects. Use `getRequestAbortSignal()` from `app/services/httpAsyncStorage.server.ts`, which is wired to Express response close events.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/v3/featureFlags.tsapps/webapp/app/services/deploymentApiPaths.server.tsapps/webapp/app/v3/deployBuildPath.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts
We use vitest exclusively. **Never mock anything** - use testcontainers instead.
📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/webapp/test/deployBuildPath.test.ts
Test files must not import `app/env.server.ts`; pass configuration as options instead.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/test/deployBuildPath.test.ts
For dashboard changes, visually verify the running Remix app with Chrome DevTools MCP, using snapshots, screenshots, interaction, and console-message checks as appropriate.
📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)
Files:
apps/webapp/app/v3/featureFlags.tsapps/webapp/app/services/deploymentApiPaths.server.tsapps/webapp/app/v3/deployBuildPath.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts
**Import subpaths only** (never root).
📄 CodeRabbit inference engine (AGENTS.md)
Files:
packages/core/src/v3/schemas/api.ts
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:
📄 CodeRabbit inference engine (AGENTS.md)
Files:
packages/core/src/v3/schemas/api.tsapps/webapp/app/v3/featureFlags.tsapps/webapp/app/services/deploymentApiPaths.server.tsapps/webapp/app/v3/deployBuildPath.tsapps/webapp/test/deployBuildPath.test.tspackages/cli-v3/src/commands/deploy.tspackages/cli-v3/src/apiClient.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts
Add crumbs as you write code — not just when debugging. Mark lines with
📄 CodeRabbit inference engine (AGENTS.md)
Files:
packages/core/src/v3/schemas/api.tsapps/webapp/app/v3/featureFlags.tsapps/webapp/app/services/deploymentApiPaths.server.tsapps/webapp/app/v3/deployBuildPath.tsapps/webapp/test/deployBuildPath.test.tspackages/cli-v3/src/commands/deploy.tspackages/cli-v3/src/apiClient.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts
Implement `deploy.ts` command in `src/commands/` for production deployment
📄 CodeRabbit inference engine (packages/cli-v3/CLAUDE.md)
Files:
packages/cli-v3/src/commands/deploy.ts
Use zod for validation in packages/core and apps/webapp
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
packages/core/src/v3/schemas/api.tsapps/webapp/app/v3/featureFlags.tsapps/webapp/app/services/deploymentApiPaths.server.tsapps/webapp/app/v3/deployBuildPath.tsapps/webapp/test/deployBuildPath.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts
CLI command definitions should be located in `src/commands/`
📄 CodeRabbit inference engine (packages/cli-v3/CLAUDE.md)
Files:
packages/cli-v3/src/commands/deploy.ts
Never import the root package (`@trigger.dev/core`). Always use subpath imports such as `@trigger.dev/core/v3`, `@trigger.dev/core/v3/utils`, `@trigger.dev/core/logger`, or `@trigger.dev/core/schemas`
📄 CodeRabbit inference engine (packages/core/CLAUDE.md)
Files:
packages/core/src/v3/schemas/api.ts
Do not import `env.server.ts` directly or indirectly into test files; instead pass environment-dependent values through options/parameters to make code testable
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Files:
apps/webapp/test/deployBuildPath.test.ts
Access environment variables through the `env` export of `env.server.ts` instead of directly accessing `process.env`
📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)
Files:
apps/webapp/app/v3/featureFlags.tsapps/webapp/app/services/deploymentApiPaths.server.tsapps/webapp/app/v3/deployBuildPath.tsapps/webapp/test/deployBuildPath.test.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts
Use vitest for all tests in the Trigger.dev repository
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
apps/webapp/test/deployBuildPath.test.ts
Use function declarations instead of default exports
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
packages/core/src/v3/schemas/api.tsapps/webapp/app/v3/featureFlags.tsapps/webapp/app/services/deploymentApiPaths.server.tsapps/webapp/app/v3/deployBuildPath.tsapps/webapp/test/deployBuildPath.test.tspackages/cli-v3/src/commands/deploy.tspackages/cli-v3/src/apiClient.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts
Use types over interfaces for TypeScript
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
packages/core/src/v3/schemas/api.tsapps/webapp/app/v3/featureFlags.tsapps/webapp/app/services/deploymentApiPaths.server.tsapps/webapp/app/v3/deployBuildPath.tsapps/webapp/test/deployBuildPath.test.tspackages/cli-v3/src/commands/deploy.tspackages/cli-v3/src/apiClient.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts
When creating or editing OTEL metrics (counters, histograms, gauges), ensure metric attributes have low cardinality by using only enums, booleans, bounded error codes, or bounded shard IDs
📄 CodeRabbit inference engine (.cursor/rules/otel-metrics.mdc)
Files:
packages/core/src/v3/schemas/api.tsapps/webapp/app/v3/featureFlags.tsapps/webapp/app/services/deploymentApiPaths.server.tsapps/webapp/app/v3/deployBuildPath.tsapps/webapp/test/deployBuildPath.test.tspackages/cli-v3/src/commands/deploy.tspackages/cli-v3/src/apiClient.tsapps/webapp/app/routes/api.v1.projects.$projectRef.$env.deploy-settings.ts
🧠 Learnings (1)
📚 Learning: 2026-06-04T18:16:35.386Z
Learnt from: nicktrn
Repo: triggerdotdev/trigger.dev PR: 3836
File: apps/supervisor/src/backpressure/backpressureMonitor.ts:3-5
Timestamp: 2026-06-04T18:16:35.386Z
Learning: When reviewing TypeScript in this repo, apply the rule “prefer type aliases over interfaces” only to data/object shapes and union/intersection type modeling. If an interface is being used as a behavioral contract for collaborators to implement (e.g., method-shape interfaces that define required behavior, such as `BackpressureLogger` / `BackpressureSignalSource` in `apps/supervisor/src/backpressure/backpressureMonitor.ts`), keep it as an `interface` and do not flag it as a type-alias-vs-interface violation.
Applied to files:
packages/core/src/v3/schemas/api.ts
🔇 Additional comments (3)
apps/webapp/app/v3/deployBuildPath.ts (1)
1-76: LGTM!apps/webapp/test/deployBuildPath.test.ts (1)
2-109: LGTM!.changeset/deploy-build-path-settings.md (1)
1-6: LGTM!
…ng it Like --detach, --local-bundle now only applies when the build path is the native build server, whether chosen by --native-build or by the server, and errors on Depot. The server can still select native_local_bundle directly via the feature flags.
|
Preview Deployment
|
…dry run local --local-bundle, --detach and --dry-run are applied after the build path is resolved, whether a flag or the server chose it. A native dry run now always bundles locally on the Depot path (it previously deployed for real with --native-build --dry-run), and --local-bundle --dry-run stays on the local bundle path, which supports dry runs.
5dd4bfa to
b190c54
Compare
…ings fetch Conflicts now name --native-build instead of the hidden alias, and --depot-build conflicts with --detach up front. The deploy settings timeout is 5s (still a single attempt): the endpoint answers in ~350ms but TCP connect stalls of over a second were observed, and a spurious timeout would silently move a deploy to Depot.
…etch fails With --detach or --local-bundle the intent is unambiguous, so a failed or missing deploy-settings response now falls back to the native build server instead of Depot, where those flags would have errored. The response's source field is accepted as any string so newer servers can add values without breaking older CLIs. --local-build and --from-bundle reject --detach/--local-bundle up front, the chosen path is announced as "Building on the native build server", and the new flags are in the CLI reference docs.
…ings An unrelated invalid key in Project.buildSettings made the whole parse fail and silently dropped disableNativeBuildServer. Adds a route test and more resolver cases (cold registry, non-object org blobs, env-type keys never leaking across environment types).
Both flags only make sense on the native build server path, so they now demand the explicit --native-build instead of depending on what the server would choose. The check runs before any network call, and the deploy settings fetch keeps a plain Depot fallback since these flags never reach it anymore.
…oymentService
The endpoint responds with `{ build_path }`; the resolution source is logged
server-side instead of sent to the CLI. The resolver moves into
DeploymentService.getDeploySettings as a neverthrow ResultAsync and no longer
reads the project's disableNativeBuildServer setting, which only applies to
the GitHub integration. Drops the docs snippet change, shortens the changeset
and trims comments.
…solver DeploymentService.getDeploySettings now also checks that the URL's project ref and env slug belong to the authenticated environment, returning `environment_mismatch` / `failed_to_load_global_flags` so the route is a single `.match`, and rethrows a thrown Response like the sibling deployment routes. The CLI's server/flag/fallback decision moves to deploy/buildPath.ts with an injected fetch so the 404-silent and warn-on-failure branches are unit tested.
## Summary 4 new features, 12 improvements, 5 bug fixes. ## Improvements - `trigger.dev deploy` now asks the server whether to build with Depot or the native build server unless `--native-build`, `--depot-build`, or `--local-build` is passed, so the native build server can be rolled out per organization without a CLI change. `--local-bundle` and `--detach` now require `--native-build`. ([#4803](#4803)) - Add an experimental `--local-bundle` deploy flag that runs the install and bundling steps on your machine and uploads only the build output; the image is still built remotely. Useful when your project's install step needs tooling or credentials that only exist locally. ([#4331](#4331)) - Send the CLI version header on all API requests so deployments are attributable to a CLI version ([#4778](#4778)) - A message that arrives mid-turn and is not injected into that turn is now answered as the next turn, instead of being dropped. This is what the `pendingMessages` docs have always described, and it applies to the default too: configuring `pendingMessages` without a `shouldInject` declines every batch, which previously meant every mid-turn message was lost with no error at either end. ([#4795](#4795)) ```ts chat.agent({ id: "my-chat", pendingMessages: { onReceived: ({ message }) => logger.info("arrived mid-turn", { id: message.id }), // Only interrupt once the agent has started calling tools. shouldInject: ({ steps }) => steps.length > 0, }, run: async ({ messages, signal }) => streamText({ model, messages, abortSignal: signal, // Required for injection. Without it nothing injects, and every // mid-turn message is answered as the next turn instead. ...chat.toStreamTextOptions(), }), }); ``` A declined message keeps its place in the queue, so it survives a crash and is answered by whichever run picks the conversation up. An injected one is consumed at the moment it is injected, so it is never also answered as a later turn. - Browser chats now keep the active turn open across page reloads when older completion records are replayed. ([#4643](#4643)) - Add `chat.endAndContinue()` so fully hand-rolled custom chat agents can hand a conversation off to a fresh run on the latest deployed task version while preserving unconsumed Session input. ([#4647](#4647)) - Custom chat agents now validate and parse client data declared with `chat.withClientData({ schema })` before passing it to agent code. ([#4646](#4646)) ## Bug fixes - Fixes a case where a chat could silently lose a message. If a message arrived while the agent was between turns and a stop arrived after it, the cursor the next boot resumed from could point past that message, so it was never answered and no error was raised. This affected `chat.agent`, not just custom agents. ([#4644](#4644)) Fixes a recovered answer being cut off. After a crash the agent replays the message it had not answered yet, but it was replaying the stop that arrived after that message too, so the turn answering it was aborted the moment it began. A stop is now only applied to the turn that was live when it arrived. That holds however the stop got there: sent after the last completed turn, or sent to a chat whose most recent turn was completed by an older version of the SDK. One limitation to know about: the recovered answer is persisted correctly, but a chat page that stayed open across the crash keeps showing the partial answer it had already received. Reload the page to see the full recovered answer. Also fixes a retried send being answered twice. When a send was retried and its idempotency claim was lost, the agent could consume the same message a second time. Custom agent loops can now inspect pending chat input without consuming it, and consume one record at a time, with `chat.messages.hasPending()` and `chat.messages.next()`. Records carry stable identifiers so a redelivery is recognisable. ```ts if (await chat.messages.hasPending()) { const record = await chat.messages.next({ timeoutInSeconds: 0 }); if (record) handle(record.payload); } ``` `hasPending()` answers for messages alone, so a message sitting behind a stop, or behind a record this version of the SDK does not recognise, still reports as pending and is still delivered. Anything the agent has no consumer for is discarded rather than left where it would make every message queued behind it undeliverable. `chat.messages.next()` returning `undefined` means no message became consumable before the timeout. `chat.writeTurnComplete()`'s `sessionInEventId` is the cursor that is safe to resume from, not the sequence of the record the turn answered. It is held back behind any message still waiting to be handled, so a value below the record you just handled is expected. - Fixed a chat agent hanging after an interrupted turn: when a run was killed mid-answer (out of memory, crash, or eviction) and only the one message it was answering was still outstanding, the new run never replied to it. That message is now re-answered on the new run. ([#4768](#4768)) - Fix chat transport discarding the next turn after stopping generation. `skipToTurnComplete` is now reset when a new message or action is sent, so a message sent after `stopGeneration` streams normally instead of leaving the chat stuck in a streaming state. ([#4744](#4744)) - Fixes a message sent while the agent was mid-answer being lost if the run then crashed. The cursor written at the end of each turn could point past a message that had arrived during that turn but had not been answered yet, so the next boot skipped it and no error was raised anywhere. Such a message is now held until a turn actually takes it. ([#4795](#4795)) This also removes the in-memory buffer those messages used to sit in, on both `chat.agent` and `chat.createSession()`, so a message waiting for its turn is durable rather than only present in the worker that received it. ## Server changes These changes affect the self-hosted Docker image and Trigger.dev Cloud: - Self-hosted instances can now disable the admin dashboard and user impersonation entirely. See the self-hosting docs for the new setting. ([#4774](#4774)) - The dashboard has two new themes, Black and White, plus appearance options for stronger colors and underlined links. ([#4547](#4547)) - Deployment logs no longer jump to the bottom while you are reading earlier output. Scroll up to pause auto-scroll, and scroll back down or use the new scroll-to-bottom button in the log header to resume following. ([#4776](#4776)) - Customize the runs list: show, hide, and reorder columns, and add smart columns that pull a value straight out of a run's payload, metadata, or output. Your column choices are saved in the page URL, so you can share a view, bookmark it, or save it straight to your favorites. ([#4652](#4652)) - Stop the browser offering to autofill or save environment variable values as saved credentials. ([#4777](#4777)) - Cut webapp CPU usage by about a quarter on the routes that workers call most, freeing headroom at the same request rate. Detailed event-loop blocking traces are no longer recorded by default, because producing them was itself a large part of that cost. ([#4746](#4746)) - When a runs list or runs.list API request spans too much data to complete, it now returns a clear, actionable error asking you to narrow the time range, instead of failing with a generic error. ([#4773](#4773)) - Improved the performance and reliability of the runs list and the runs.list API, especially for large projects and filtered views. ([#4763](#4763)) - New Vercel connections now get version skew protection turned on automatically, so each run uses the task version its deployment shipped with. Automatic atomic deployments are deprecated and no longer offered when you connect a project, but stay available in your Vercel integration settings. ([#4741](#4741)) - The Staging branch setting now shows an upgrade prompt on plans that don't include a Staging environment, instead of looking editable and then silently doing nothing when saved. ([#4784](#4784)) <details> <summary>Raw changeset output</summary> # Releases ## @trigger.dev/build@4.5.13 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.13` ## trigger.dev@4.5.13 ### Patch Changes - `trigger.dev deploy` now asks the server whether to build with Depot or the native build server unless `--native-build`, `--depot-build`, or `--local-build` is passed, so the native build server can be rolled out per organization without a CLI change. `--local-bundle` and `--detach` now require `--native-build`. ([#4803](#4803)) - Add an experimental `--local-bundle` deploy flag that runs the install and bundling steps on your machine and uploads only the build output; the image is still built remotely. Useful when your project's install step needs tooling or credentials that only exist locally. ([#4331](#4331)) - Send the CLI version header on all API requests so deployments are attributable to a CLI version ([#4778](#4778)) - Updated dependencies: - `@trigger.dev/core@4.5.13` - `@trigger.dev/build@4.5.13` - `@trigger.dev/schema-to-json@4.5.13` ## @trigger.dev/core@4.5.13 ### Patch Changes - `trigger.dev deploy` now asks the server whether to build with Depot or the native build server unless `--native-build`, `--depot-build`, or `--local-build` is passed, so the native build server can be rolled out per organization without a CLI change. `--local-bundle` and `--detach` now require `--native-build`. ([#4803](#4803)) - Add an experimental `--local-bundle` deploy flag that runs the install and bundling steps on your machine and uploads only the build output; the image is still built remotely. Useful when your project's install step needs tooling or credentials that only exist locally. ([#4331](#4331)) - A message that arrives mid-turn and is not injected into that turn is now answered as the next turn, instead of being dropped. This is what the `pendingMessages` docs have always described, and it applies to the default too: configuring `pendingMessages` without a `shouldInject` declines every batch, which previously meant every mid-turn message was lost with no error at either end. ([#4795](#4795)) ```ts chat.agent({ id: "my-chat", pendingMessages: { onReceived: ({ message }) => logger.info("arrived mid-turn", { id: message.id }), // Only interrupt once the agent has started calling tools. shouldInject: ({ steps }) => steps.length > 0, }, run: async ({ messages, signal }) => streamText({ model, messages, abortSignal: signal, // Required for injection. Without it nothing injects, and every // mid-turn message is answered as the next turn instead. ...chat.toStreamTextOptions(), }), }); ``` A declined message keeps its place in the queue, so it survives a crash and is answered by whichever run picks the conversation up. An injected one is consumed at the moment it is injected, so it is never also answered as a later turn. - Fixes a case where a chat could silently lose a message. If a message arrived while the agent was between turns and a stop arrived after it, the cursor the next boot resumed from could point past that message, so it was never answered and no error was raised. This affected `chat.agent`, not just custom agents. ([#4644](#4644)) Fixes a recovered answer being cut off. After a crash the agent replays the message it had not answered yet, but it was replaying the stop that arrived after that message too, so the turn answering it was aborted the moment it began. A stop is now only applied to the turn that was live when it arrived. That holds however the stop got there: sent after the last completed turn, or sent to a chat whose most recent turn was completed by an older version of the SDK. One limitation to know about: the recovered answer is persisted correctly, but a chat page that stayed open across the crash keeps showing the partial answer it had already received. Reload the page to see the full recovered answer. Also fixes a retried send being answered twice. When a send was retried and its idempotency claim was lost, the agent could consume the same message a second time. Custom agent loops can now inspect pending chat input without consuming it, and consume one record at a time, with `chat.messages.hasPending()` and `chat.messages.next()`. Records carry stable identifiers so a redelivery is recognisable. ```ts if (await chat.messages.hasPending()) { const record = await chat.messages.next({ timeoutInSeconds: 0 }); if (record) handle(record.payload); } ``` `hasPending()` answers for messages alone, so a message sitting behind a stop, or behind a record this version of the SDK does not recognise, still reports as pending and is still delivered. Anything the agent has no consumer for is discarded rather than left where it would make every message queued behind it undeliverable. `chat.messages.next()` returning `undefined` means no message became consumable before the timeout. `chat.writeTurnComplete()`'s `sessionInEventId` is the cursor that is safe to resume from, not the sequence of the record the turn answered. It is held back behind any message still waiting to be handled, so a value below the record you just handled is expected. ## @trigger.dev/python@4.5.13 ### Patch Changes - Updated dependencies: - `@trigger.dev/sdk@4.5.13` - `@trigger.dev/core@4.5.13` - `@trigger.dev/build@4.5.13` ## @trigger.dev/react-hooks@4.5.13 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.13` ## @trigger.dev/redis-worker@4.5.13 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.13` ## @trigger.dev/rsc@4.5.13 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.13` ## @trigger.dev/schema-to-json@4.5.13 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.5.13` ## @trigger.dev/sdk@4.5.13 ### Patch Changes - Fixed a chat agent hanging after an interrupted turn: when a run was killed mid-answer (out of memory, crash, or eviction) and only the one message it was answering was still outstanding, the new run never replied to it. That message is now re-answered on the new run. ([#4768](#4768)) - Browser chats now keep the active turn open across page reloads when older completion records are replayed. ([#4643](#4643)) - Add `chat.endAndContinue()` so fully hand-rolled custom chat agents can hand a conversation off to a fresh run on the latest deployed task version while preserving unconsumed Session input. ([#4647](#4647)) - Fix chat transport discarding the next turn after stopping generation. `skipToTurnComplete` is now reset when a new message or action is sent, so a message sent after `stopGeneration` streams normally instead of leaving the chat stuck in a streaming state. ([#4744](#4744)) - Custom chat agents now validate and parse client data declared with `chat.withClientData({ schema })` before passing it to agent code. ([#4646](#4646)) - Fixes a message sent while the agent was mid-answer being lost if the run then crashed. The cursor written at the end of each turn could point past a message that had arrived during that turn but had not been answered yet, so the next boot skipped it and no error was raised anywhere. Such a message is now held until a turn actually takes it. ([#4795](#4795)) This also removes the in-memory buffer those messages used to sit in, on both `chat.agent` and `chat.createSession()`, so a message waiting for its turn is durable rather than only present in the worker that received it. - A message that arrives mid-turn and is not injected into that turn is now answered as the next turn, instead of being dropped. This is what the `pendingMessages` docs have always described, and it applies to the default too: configuring `pendingMessages` without a `shouldInject` declines every batch, which previously meant every mid-turn message was lost with no error at either end. ([#4795](#4795)) ```ts chat.agent({ id: "my-chat", pendingMessages: { onReceived: ({ message }) => logger.info("arrived mid-turn", { id: message.id }), // Only interrupt once the agent has started calling tools. shouldInject: ({ steps }) => steps.length > 0, }, run: async ({ messages, signal }) => streamText({ model, messages, abortSignal: signal, // Required for injection. Without it nothing injects, and every // mid-turn message is answered as the next turn instead. ...chat.toStreamTextOptions(), }), }); ``` A declined message keeps its place in the queue, so it survives a crash and is answered by whichever run picks the conversation up. An injected one is consumed at the moment it is injected, so it is never also answered as a later turn. - Fixes a case where a chat could silently lose a message. If a message arrived while the agent was between turns and a stop arrived after it, the cursor the next boot resumed from could point past that message, so it was never answered and no error was raised. This affected `chat.agent`, not just custom agents. ([#4644](#4644)) Fixes a recovered answer being cut off. After a crash the agent replays the message it had not answered yet, but it was replaying the stop that arrived after that message too, so the turn answering it was aborted the moment it began. A stop is now only applied to the turn that was live when it arrived. That holds however the stop got there: sent after the last completed turn, or sent to a chat whose most recent turn was completed by an older version of the SDK. One limitation to know about: the recovered answer is persisted correctly, but a chat page that stayed open across the crash keeps showing the partial answer it had already received. Reload the page to see the full recovered answer. Also fixes a retried send being answered twice. When a send was retried and its idempotency claim was lost, the agent could consume the same message a second time. Custom agent loops can now inspect pending chat input without consuming it, and consume one record at a time, with `chat.messages.hasPending()` and `chat.messages.next()`. Records carry stable identifiers so a redelivery is recognisable. ```ts if (await chat.messages.hasPending()) { const record = await chat.messages.next({ timeoutInSeconds: 0 }); if (record) handle(record.payload); } ``` `hasPending()` answers for messages alone, so a message sitting behind a stop, or behind a record this version of the SDK does not recognise, still reports as pending and is still delivered. Anything the agent has no consumer for is discarded rather than left where it would make every message queued behind it undeliverable. `chat.messages.next()` returning `undefined` means no message became consumable before the timeout. `chat.writeTurnComplete()`'s `sessionInEventId` is the cursor that is safe to resume from, not the sequence of the record the turn answered. It is held back behind any message still waiting to be handled, so a value below the record you just handled is expected. - Updated dependencies: - `@trigger.dev/core@4.5.13` </details> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
The CLI now asks the server which build path to use before it builds or uploads anything, so native builds can be rolled out per organization and per environment type without a CLI release.
The path comes from four enum feature flags, editable in the global and per-org admin flag UIs:
deployBuildPathanddeployBuildPathPreview/Staging/Production. Unset everywhere keeps current behaviour unchanged; CLIs older than this release never call the endpoint and keep their current behaviour.