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: fa56d28 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 |
|
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 (1)
Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review. 📜 Recent review details⏰ Context from checks skipped due to timeout. (50)
🔇 Additional comments (2)
WalkthroughThe change adds external deployment ID support to chat session configuration and API schemas. The SDK resolves deployment IDs, preserves explicit 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 24 functions across 18 files. (1 skipped: 1 unsupported.) Full details: Description checkExplanation The description is detailed, on-topic, and explains deployment ID resolution, parked runs, pending-version reporting, upgrade behavior, compatibility with lockToVersion, and documentation changes. It does not include the template's issue reference, checklist, explicit Testing section, Changelog section, or Screenshots section, but the core change information is complete.
✨ Finishing Touches🧪 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
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.
b25e79b to
fa56d28
Compare
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.