feat(sdk): return lastEventId from writeTurnComplete and typed capture result#4304
Conversation
🦋 Changeset detectedLatest commit: a6d0ddb The changes in this PR will be included in the next version bump. This PR includes changesets to release 26 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 Run ID: 📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (6)
📜 Recent review details⏰ Context from checks skipped due to timeout. (31)
🧰 Additional context used📓 Path-based instructions (1)docs/**/*.mdx📄 CodeRabbit inference engine (docs/CLAUDE.md)
Files:
🧠 Learnings (4)📚 Learning: 2026-03-10T12:44:14.176ZApplied to files:
📚 Learning: 2026-04-30T20:30:29.458ZApplied to files:
📚 Learning: 2026-06-16T13:14:09.440ZApplied to files:
📚 Learning: 2026-06-16T13:14:14.382ZApplied to files:
🔇 Additional comments (1)
WalkthroughThe SDK updates 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 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 |
…e result
chat.writeTurnComplete() now resolves to { lastEventId }, the resume cursor
for the next turn, so a custom-agent loop can persist it straight from the
task instead of round-tripping it back from the client.
chat.pipeAndCapture() no longer throws when a stream is stopped or fails. It
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. The internal turn.complete() path keeps its existing
contract (returns UIMessage | undefined, still throws on a genuine stream
failure and discards output on full cancel).
3343ef5 to
335c087
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: |
…omplete lastEventId Adds unit tests for the new chat.pipeAndCapture result shape (complete / aborted-with-partial / error) and for chat.writeTurnComplete returning a resume cursor. Extends the in-memory session test harness so its .out writeControl projects a synthetic seq_num and its pipe propagates source-stream errors (mirroring production) instead of swallowing them.
…rden test pipe Address PR review: - pipeAndCapture now reconstructs the partial message from buffered chunks when a transport failure abandons the stream before onFinish fires, so the partial is preserved on the hard-error path, not just on abort. - writeTurnComplete also returns sessionInEventId (the committed .in consume cursor) alongside lastEventId, so a raw loop can correlate both turn-boundary cursors server-side. - The in-memory session test harness no longer hangs if stream setup throws before the read loop; stream setup now runs inside the guarded block. - Docs and the pipeAndCapture error-path test updated to match.
## 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](#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](#4336)) - Custom chat agent loops get two ergonomic wins for owning the turn loop. ([#4304](#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](#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](#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](#4066)) - Allow different organization members to use the same development branch name without sharing or colliding with each other's branch environments. ([#4323](#4323)) - Limit account settings email input to 254 characters. ([#4330](#4330)) - Prevent duplicate Staging and Preview environments when account setup requests overlap ([#4261](#4261)) - Fix the docs link on the empty Prompts page, which pointed to a page that no longer exists. ([#4247](#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](#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](#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](#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](#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](#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](#4188)) - Updated dependencies: - `@trigger.dev/core@4.5.7` </details> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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 aPipeAndCaptureResultcarrying any partialmessagecaptured before the stop or failure, a typedstatus("complete" | "aborted" | "error"), and theerroron failure. Previously a failed stream threw and the partial was lost, and an abort was captured only when the AI SDK happened to fireonFinishin time.Design
pipeAndCapturewraps the pipe in atry/catchand classifies the outcome from the abort signal (a stop drains the source stream cleanly rather than throwing) versus a thrown error. It also races theonFinishcapture against a timeout so a hard stop that preventsonFinishfrom firing can't hang the caller. This mirrors the capture pathchat.agentalready uses internally.The
finishReasonfromonFinishis surfaced too, since it was already captured on the built-in path.The internal
turn.complete()helper keeps its existing contract: it still returnsUIMessage | undefined, still throws on a genuine stream failure, and still discards output on a full run cancel.Breaking change
chat.pipeAndCapturepreviously resolved toUIMessage | undefined. Call sites now read.messageoff the result. This is a young, low-level API; the docs examples are updated in this PR.