feat(core,sdk,webapp): version skew protection for chat.agent sessions - #4789
feat(core,sdk,webapp): version skew protection for chat.agent sessions#47890ski wants to merge 2 commits into
Conversation
🦋 Changeset detectedLatest commit: f8da67e 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:
WalkthroughThe change adds external deployment ID support to chat session configuration and API schemas. The SDK resolves deployment IDs, preserves explicit Merge Risk: 🟡 Moderate · up to This change pins chat sessions to deployments and adds pending-version and recovery behavior, but the current implementation can leave resumed chats waiting indefinitely or fail to deliver queued steering messages; callers may also bypass an action-level deployment pin in directly exposed actions. The PR is not merge-ready until these bounded correctness and policy risks are fixed or explicitly accepted. 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Full details: Description checkExplanation The description gives a detailed and relevant summary of the implementation, behavior, configuration, and documentation changes. However, it omits the required issue reference, checklist, Testing section, Changelog section, and Screenshots section from the repository template. Resolution Add the required template sections. Include Full details: Docstring CoverageExplanation Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 26 functions across 18 files. (3 skipped: 3 unsupported.)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 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 |
8ec56c3 to
847175f
Compare
@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: |
847175f to
b25e79b
Compare
| const winner = await runStore.findRun( | ||
| { id: fresh.currentRunId }, | ||
| { select: { status: true } }, | ||
| prisma | ||
| ); | ||
|
|
||
| return { | ||
| runId: fresh.currentRunId, | ||
| swapped: false, | ||
| pendingVersion: winner ? isPendingVersionStatus(winner.status) : false, |
There was a problem hiding this comment.
🟡 Unused database read in preempted swap path
The lost-race branch of swapSessionRun issues an extra runStore.findRun on the winning run only to compute pendingVersion. The sole caller, the end-and-continue route, discards that field, so each preempted upgrade makes a database round-trip whose result nothing reads.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
.changeset/chat-agent-version-skew-protection.md (1)
8-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd temporary crumb markers to every new code example.
Both files add code examples without the required crumb instrumentation.
.changeset/chat-agent-version-skew-protection.md#L8-L13: add a crumb marker to the TypeScript example.docs/deployment/version-skew-protection.mdx#L287-L295: add a crumb marker to the session-start example.docs/deployment/version-skew-protection.mdx#L303-L313: add a crumb marker to the transport example.docs/deployment/version-skew-protection.mdx#L321-L325: add a crumb marker to the opt-out example.docs/deployment/version-skew-protection.mdx#L333-L335: add a crumb marker to the upgrade example.As per coding guidelines,
**/*files must add crumbs while code is written.Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 01a6d9b1-93db-4d74-92ea-054afb1565ed
📒 Files selected for processing (2)
.changeset/chat-agent-version-skew-protection.mddocs/deployment/version-skew-protection.mdx
Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.
📜 Review details
⏰ Context from checks skipped due to timeout. (42)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (24, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (6, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (22, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (23, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (9, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (4, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (7, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (10, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (5, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (1, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (2, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (3, 24)
- GitHub Check: sdk-compat / Node.js 22.23 (warp-ubuntu-latest-x64-4x)
- GitHub Check: sdk-compat / Cloudflare Workers
- GitHub Check: e2e / 🧪 CLI v3 tests (warp-windows-latest-x64-8x - npm)
- GitHub Check: e2e / 🧪 CLI v3 tests (warp-ubuntu-latest-x64-4x - pnpm)
- GitHub Check: e2e / 🧪 CLI v3 tests (warp-windows-latest-x64-8x - pnpm)
- GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
- GitHub Check: e2e / 🧪 CLI v3 tests (warp-ubuntu-latest-x64-4x - npm)
- GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
- GitHub Check: internal / 🧪 Unit Tests: Internal
- GitHub Check: packages / 🧪 Unit Tests: Packages (2, 3)
- GitHub Check: runops-guard / runops-guard
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
- GitHub Check: code-quality / code-quality
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: 🛡️ E2E Auth Tests (full)
- GitHub Check: 🛡️ E2E Auth Tests (full)
- GitHub Check: Build and publish previews
🧰 Additional context used
📓 Path-based instructions (2)
Add crumbs as you write code — not just when debugging. Mark lines with
📄 CodeRabbit inference engine (AGENTS.md)
Files:
docs/deployment/version-skew-protection.mdx
MDX documentation pages must include frontmatter with title (required), description (required), and sidebarTitle (optional) in YAML format
📄 CodeRabbit inference engine (docs/CLAUDE.md)
Files:
docs/deployment/version-skew-protection.mdx
🧠 Learnings (1)
📚 Learning: 2026-04-30T20:30:29.458Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3226
File: docs/ai-chat/quick-start.mdx:13-13
Timestamp: 2026-04-30T20:30:29.458Z
Learning: In this repo’s documentation MDX files (`docs/**/*.mdx`), use `ts` and `tsx` (not `typescript`) as the code-fence language tags for TypeScript/TSX snippets. Do not flag `ts`/`tsx` code-fence language tags as incorrect in any docs MDX file, since this is the site-wide Mintlify-compatible convention.
Applied to files:
docs/deployment/version-skew-protection.mdx
🔇 Additional comments (2)
.changeset/chat-agent-version-skew-protection.md (1)
1-6: LGTM!docs/deployment/version-skew-protection.mdx (1)
279-285: LGTM!Also applies to: 297-301, 315-317, 329-331, 336-338
| }); | ||
| ``` | ||
|
|
||
| Messages sent while a chat waits on a deployment that is still building are stored and answered once it lands, and the transport emits a `run-pending-version` event so your UI can say so. `chat.requestUpgrade()` now clears the session's pin so the handoff can reach a new version, and accepts `{ externalDeploymentId }` to move to a specific one. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Keep external deployment pinning distinct from lockToVersion.
The documentation overstates what external deployment opt-out and chat.requestUpgrade() can change.
.changeset/chat-agent-version-skew-protection.md#L15: state thatchat.requestUpgrade()cannot overridelockToVersion.docs/deployment/version-skew-protection.mdx#L317-L327: clarify that these options disable external-deployment pinning only; they do not overridelockToVersion.
The PR objective states that lockToVersion remains authoritative and cannot be overridden.
📍 Affects 2 files
.changeset/chat-agent-version-skew-protection.md#L15-L15(this comment)docs/deployment/version-skew-protection.mdx#L317-L327
b25e79b to
fa56d28
Compare
A chat session now carries an external deployment id, so every run it
schedules lands on the deployment that matched the app build that
started the conversation: the first run, each continuation after an idle
suspend, and each recovery after a crash.
Public SDK surface:
- `triggerConfig.externalDeploymentId`, on every entry point that starts
a session (`sessions.start`, `chat.createStartSessionAction`,
`chat.headStart`, `chat.handover`, `AgentChat`). Normally omitted: it
is discovered wherever the session is started, with the same
precedence `trigger()` uses. `null` opts one chat out.
- `pendingVersion` on the session-create and `.in/append` responses, on
`ChatStartSessionResult` and on `StartSessionResult`, plus a new
`run-pending-version` transport event, so a chat waiting on a
deployment that is still building can say so instead of appearing to
stall.
- `chat.requestUpgrade({ externalDeploymentId })`. Called without a
target it now clears the session's pin, which is what makes upgrading
away from a pinned version possible at all, and the cleared pin is
persisted so the next continuation cannot bounce back.
- `SessionTriggerConfigInput` and `CreateSessionInput`, the caller-facing
forms of the trigger config and the create body.
Parking is the right failure mode here, and an improvement on
`lockToVersion`, which throws on session create and is swallowed on the
append path, leaving the chat hung with no run at all. `PENDING_VERSION`
is non-final, so a parked run is reused rather than re-triggered and
appended messages stay durable until the deployment lands.
`lockToVersion` is deliberately untouched: it still wins where both are
set, and `requestUpgrade()` still cannot escape it.
`AgentChat.ensureStarted` built its session config field by field and never copied these three across, so anyone who set them on `triggerConfig` got no error and no effect.
fa56d28 to
f8da67e
Compare
| const storedConfig = SessionTriggerConfigSchema.parse(session.triggerConfig); | ||
|
|
||
| // The upgrade's pin is persisted in the claim below, not applied to this run alone: the next | ||
| // continuation re-reads the stored config. `lockToVersion` is deliberately untouched. | ||
| const config = | ||
| reason === "upgrade" | ||
| ? { ...storedConfig, externalDeploymentId: params.externalDeploymentId ?? undefined } | ||
| : storedConfig; |
There was a problem hiding this comment.
🔍 Upgrade-cleared pin can be reinstated by a later start
requestUpgrade() clears and persists the session's external deployment id so continuations don't return to the rejected version. The cached path of findOrCreateSession overwrites the whole stored config on every sessions.start, so a client that re-starts (reload, reconnect, preload) while still on the rejected build re-pins the session to it and the agent upgrades again. Continuations via .in/append never call start, so they keep the cleared pin. The docs acknowledge this, so likely intended; confirm the escape guarantee is meant to hold across re-starts.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/trigger-sdk/src/v3/sessions.ts (1)
810-817: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winBound the post-wake record read in
wait().After
awaitWake()succeeds,sessionStreams.onceRecord(this.sessionId, "in")registers a waiter with no timeout. If no record is delivered,wait()can remain pending indefinitely. Pass a boundedtimeoutMsso the existing error branch can run.packages/trigger-sdk/src/v3/ai.ts (1)
3691-3709: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winResolve the router only when a claim is needed.
When
shouldInjectreturnstrueand the queue contains only entries fromChatMessageAccumulator.steer(),drainSteeringQueuecallschatInputRouter()before processing those entries.getChatSession()then throws outside a chat session, so local messages are not injected. Initialize the router inside theseqNumbranch and guardrouter.untake().docs/ai-chat/patterns/version-upgrades.mdx (1)
202-203: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winDocument deployment-pin precedence for continuations.
The continuation documentation should state that
externalDeploymentIdkeeps subsequent runs on the selected deployment, whilelockToVersionremains authoritative when both fields are set. Apply the same precedence clarification to the client protocol documentation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0276b430-0510-4122-8ba7-4c8ca090545d
📒 Files selected for processing (7)
docs/ai-chat/client-protocol.mdxdocs/ai-chat/patterns/version-upgrades.mdxdocs/ai-chat/reference.mdxpackages/trigger-sdk/src/v3/ai.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat.tspackages/trigger-sdk/src/v3/sessions.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/ai-chat/reference.mdx
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. (20)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (19, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (16, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (15, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (20, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (14, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (17, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (21, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (18, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (11, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (13, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (12, 24)
- GitHub Check: webapp / 🧪 Unit Tests: Webapp (8, 24)
- GitHub Check: e2e / 🧪 CLI v3 tests (warp-windows-latest-x64-8x - pnpm)
- GitHub Check: internal / 🧪 Unit Tests: Internal
- GitHub Check: e2e / 🧪 CLI v3 tests (warp-windows-latest-x64-8x - npm)
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (1, 2)
- GitHub Check: e2e-webapp / 🧪 E2E Tests: Webapp (2, 2)
- GitHub Check: packages / 🧪 Unit Tests: Packages (1, 3)
- GitHub Check: packages / 🧪 Unit Tests: Packages (3, 3)
- GitHub Check: 🛡️ E2E Auth Tests (full)
🧰 Additional context used
📓 Path-based instructions (11)
Always import from `@trigger.dev/sdk`. Never use `@trigger.dev/sdk/v3` or deprecated `client.defineJob`.
📄 CodeRabbit inference engine (AGENTS.md)
Files:
packages/trigger-sdk/src/v3/sessions.tspackages/trigger-sdk/src/v3/ai.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat.ts
We use vitest exclusively. **Never mock anything** - use testcontainers instead.
📄 CodeRabbit inference engine (AGENTS.md)
Files:
packages/trigger-sdk/src/v3/chat.test.ts
**Prefer static imports over dynamic imports.** Only use dynamic `import()` when:
📄 CodeRabbit inference engine (AGENTS.md)
Files:
packages/trigger-sdk/src/v3/sessions.tspackages/trigger-sdk/src/v3/ai.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat.ts
Add crumbs as you write code — not just when debugging. Mark lines with
📄 CodeRabbit inference engine (AGENTS.md)
Files:
docs/ai-chat/patterns/version-upgrades.mdxpackages/trigger-sdk/src/v3/sessions.tsdocs/ai-chat/client-protocol.mdxpackages/trigger-sdk/src/v3/ai.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat.ts
Always import from `@trigger.dev/sdk`. Never use `@trigger.dev/sdk/v3` (deprecated path alias)
📄 CodeRabbit inference engine (packages/trigger-sdk/CLAUDE.md)
Files:
packages/trigger-sdk/src/v3/sessions.tspackages/trigger-sdk/src/v3/ai.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat.ts
In the Trigger.dev SDK (packages/trigger-sdk), prefer isomorphic code like fetch and ReadableStream instead of Node.js-specific code
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
packages/trigger-sdk/src/v3/sessions.tspackages/trigger-sdk/src/v3/ai.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat.ts
Use vitest for all tests in the Trigger.dev repository
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
packages/trigger-sdk/src/v3/chat.test.ts
Use function declarations instead of default exports
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
packages/trigger-sdk/src/v3/sessions.tspackages/trigger-sdk/src/v3/ai.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat.ts
MDX documentation pages must include frontmatter with title (required), description (required), and sidebarTitle (optional) in YAML format
📄 CodeRabbit inference engine (docs/CLAUDE.md)
Files:
docs/ai-chat/patterns/version-upgrades.mdxdocs/ai-chat/client-protocol.mdx
Use types over interfaces for TypeScript
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
packages/trigger-sdk/src/v3/sessions.tspackages/trigger-sdk/src/v3/ai.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat.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/trigger-sdk/src/v3/sessions.tspackages/trigger-sdk/src/v3/ai.tspackages/trigger-sdk/src/v3/chat.test.tspackages/trigger-sdk/src/v3/chat.ts
🧠 Learnings (2)
📚 Learning: 2026-04-30T20:30:29.458Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3226
File: docs/ai-chat/quick-start.mdx:13-13
Timestamp: 2026-04-30T20:30:29.458Z
Learning: In this repo’s documentation MDX files (`docs/**/*.mdx`), use `ts` and `tsx` (not `typescript`) as the code-fence language tags for TypeScript/TSX snippets. Do not flag `ts`/`tsx` code-fence language tags as incorrect in any docs MDX file, since this is the site-wide Mintlify-compatible convention.
Applied to files:
docs/ai-chat/patterns/version-upgrades.mdx
📚 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/trigger-sdk/src/v3/sessions.ts
🔇 Additional comments (7)
packages/trigger-sdk/src/v3/sessions.ts (1)
56-68: LGTM!Also applies to: 114-119
packages/trigger-sdk/src/v3/ai.ts (2)
1505-1523: LGTM!Also applies to: 1866-1897, 1983-2032, 8968-9038, 9853-9943, 10012-10020, 11258-11294, 11359-11365
1577-1601: 🩺 Stability & AvailabilityNo change required.
CHAT_INPUT_ROUTESmarksmessagesas replayable.resumeFloor()scans all routes and remains below any queued message, including whenawaitWakefirst delivers astoporhandoverrecord. Non-replayable queued routes are intentionally excluded.packages/trigger-sdk/src/v3/chat.test.ts (1)
519-622: LGTM!packages/trigger-sdk/src/v3/chat.ts (1)
228-239: LGTM!Also applies to: 431-436, 1529-1543, 1600-1613, 1668-1680
docs/ai-chat/client-protocol.mdx (1)
957-961: LGTM!docs/ai-chat/patterns/version-upgrades.mdx (1)
4-18: LGTM!Also applies to: 30-48, 181-186, 189-201, 204-208, 218-218
Summary
A chat session now carries an external deployment id, so every run it schedules lands on the deployment that matched the app build that started the conversation: the first run, each continuation after an idle suspend, and each recovery after a crash. A chat started by one release of your app keeps talking to the agent build that release shipped with, however many turns that takes.
The id is resolved wherever the session is started, with the same precedence
trigger()already uses: an explicitexternalDeploymentId, thenconfigure(), thenTRIGGER_EXTERNAL_DEPLOYMENT_ID, then your platform's commit SHA whenTRIGGER_AUTOMATIC_SKEW_VERSION_PROTECTIONis1. If none of those yields a value, no id is sent and nothing changes: the chat's runs go to the current version exactly as they do today, and nothing parks.There is no chat-specific setup either way, so an app already pinning its task runs gets pinned chats with no code change:
Chats need the same two halves as tasks and no more: a deployment carrying an id, and an app that sends the same one. Pass
externalDeploymentId: nullontriggerConfigto opt one chat out while the rest of the app stays pinned.Pinning to an id you choose
Discovery covers the common case, a commit SHA your platform already exposes. To name the deployment yourself instead, deploy the agent under that id and give your app the same value:
npx trigger.dev@latest deploy --external-id "$RELEASE_ID"sessions.start({ triggerConfig: { externalDeploymentId } })andAgentChat'striggerConfigtake the same field. SettingTRIGGER_EXTERNAL_DEPLOYMENT_IDin the app's environment does the same job without touching code, and an explicit value wins over it.Waiting on a deployment
A run pinned to a deployment that has not landed yet parks rather than failing, and messages sent meanwhile are stored and answered once it arrives. That is a strict improvement on
lockToVersion, which throws on session create and is swallowed on the append path, leaving the chat hung with no run at all.PENDING_VERSIONis non-final, so the parked run is reused rather than re-triggered.The session-create and
.in/appendresponses now reportpendingVersion, and the transport emits a matching event, so the wait can read as a deploy in progress instead of a stall:Escaping the pin
chat.requestUpgrade()re-applied the stored pin, which made upgrading away from a pinned version impossible. It now clears the session's id and persists that, so the next continuation cannot bounce back, and takes a target when you want to move to a specific deployment instead:lockToVersionis deliberately untouched: it still wins where both are set, andrequestUpgrade()still cannot escape it.Docs: a new Chat sessions section on the version skew protection page, plus notes on the version-upgrades pattern page.