From aa0bfceff4cae6a9f562afbacfb9092c6d1aa166 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Aug 2026 11:02:02 +0100 Subject: [PATCH] chore: release v4.5.13 (#4769) ## 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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4331)) - Send the CLI version header on all API requests so deployments are attributable to a CLI version ([#4778](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4647)) - Custom chat agents now validate and parse client data declared with `chat.withClientData({ schema })` before passing it to agent code. ([#4646](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4774)) - The dashboard has two new themes, Black and White, plus appearance options for stronger colors and underlined links. ([#4547](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4652)) - Stop the browser offering to autofill or save environment variable values as saved credentials. ([#4777](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4773)) - Improved the performance and reliability of the runs list and the runs.list API, especially for large projects and filtered views. ([#4763](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4784))
Raw changeset output # 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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4331)) - Send the CLI version header on all API requests so deployments are attributable to a CLI version ([#4778](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4768)) - Browser chats now keep the active turn open across page reloads when older completion records are replayed. ([#4643](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4744)) - Custom chat agents now validate and parse client data declared with `chat.withClientData({ schema })` before passing it to agent code. ([#4646](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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`
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/brave-otters-recover.md | 5 -- .changeset/calm-chat-reconnects.md | 5 -- .../chat-custom-agent-end-and-continue.md | 5 -- .changeset/deploy-build-path-settings.md | 6 -- .changeset/fluffy-pans-argue.md | 5 -- .changeset/local-bundle-deploy.md | 6 -- .changeset/quiet-chats-validate.md | 5 -- .changeset/quiet-floors-hold.md | 7 --- .changeset/spry-steers-defer.md | 28 --------- .changeset/tidy-mailboxes-wait.md | 25 -------- .changeset/violet-buses-tease.md | 5 -- .../admin-dashboard-enabled-flag.md | 6 -- .../appearance-themes-and-options.md | 6 -- .../deployment-logs-follow-scroll.md | 6 -- .../env-var-inputs-autocomplete-off.md | 6 -- .../reduce-webapp-cpu-on-worker-routes.md | 6 -- .../runs-list-column-customization.md | 6 -- .../runs-list-query-limit-error.md | 6 -- .server-changes/runs-list-read-isolation.md | 6 -- .server-changes/vercel-staging-gating.md | 6 -- .../vercel-version-skew-protection.md | 6 -- hosting/k8s/helm/Chart.yaml | 4 +- packages/build/CHANGELOG.md | 7 +++ packages/build/package.json | 4 +- packages/cli-v3/CHANGELOG.md | 12 ++++ packages/cli-v3/package.json | 8 +-- packages/core/CHANGELOG.md | 52 ++++++++++++++++ packages/core/package.json | 2 +- packages/python/CHANGELOG.md | 9 +++ packages/python/package.json | 12 ++-- packages/react-hooks/CHANGELOG.md | 7 +++ packages/react-hooks/package.json | 4 +- packages/redis-worker/CHANGELOG.md | 7 +++ packages/redis-worker/package.json | 4 +- packages/rsc/CHANGELOG.md | 7 +++ packages/rsc/package.json | 4 +- packages/schema-to-json/CHANGELOG.md | 7 +++ packages/schema-to-json/package.json | 2 +- packages/trigger-sdk/CHANGELOG.md | 62 +++++++++++++++++++ packages/trigger-sdk/package.json | 4 +- pnpm-lock.yaml | 22 +++---- 41 files changed, 205 insertions(+), 197 deletions(-) delete mode 100644 .changeset/brave-otters-recover.md delete mode 100644 .changeset/calm-chat-reconnects.md delete mode 100644 .changeset/chat-custom-agent-end-and-continue.md delete mode 100644 .changeset/deploy-build-path-settings.md delete mode 100644 .changeset/fluffy-pans-argue.md delete mode 100644 .changeset/local-bundle-deploy.md delete mode 100644 .changeset/quiet-chats-validate.md delete mode 100644 .changeset/quiet-floors-hold.md delete mode 100644 .changeset/spry-steers-defer.md delete mode 100644 .changeset/tidy-mailboxes-wait.md delete mode 100644 .changeset/violet-buses-tease.md delete mode 100644 .server-changes/admin-dashboard-enabled-flag.md delete mode 100644 .server-changes/appearance-themes-and-options.md delete mode 100644 .server-changes/deployment-logs-follow-scroll.md delete mode 100644 .server-changes/env-var-inputs-autocomplete-off.md delete mode 100644 .server-changes/reduce-webapp-cpu-on-worker-routes.md delete mode 100644 .server-changes/runs-list-column-customization.md delete mode 100644 .server-changes/runs-list-query-limit-error.md delete mode 100644 .server-changes/runs-list-read-isolation.md delete mode 100644 .server-changes/vercel-staging-gating.md delete mode 100644 .server-changes/vercel-version-skew-protection.md diff --git a/.changeset/brave-otters-recover.md b/.changeset/brave-otters-recover.md deleted file mode 100644 index f5effb4ce..000000000 --- a/.changeset/brave-otters-recover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@trigger.dev/sdk": patch ---- - -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. diff --git a/.changeset/calm-chat-reconnects.md b/.changeset/calm-chat-reconnects.md deleted file mode 100644 index a1af4aa43..000000000 --- a/.changeset/calm-chat-reconnects.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@trigger.dev/sdk": patch ---- - -Browser chats now keep the active turn open across page reloads when older completion records are replayed. diff --git a/.changeset/chat-custom-agent-end-and-continue.md b/.changeset/chat-custom-agent-end-and-continue.md deleted file mode 100644 index 6d7deffee..000000000 --- a/.changeset/chat-custom-agent-end-and-continue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@trigger.dev/sdk": patch ---- - -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. diff --git a/.changeset/deploy-build-path-settings.md b/.changeset/deploy-build-path-settings.md deleted file mode 100644 index e42f9b2e7..000000000 --- a/.changeset/deploy-build-path-settings.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"trigger.dev": patch -"@trigger.dev/core": patch ---- - -`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`. diff --git a/.changeset/fluffy-pans-argue.md b/.changeset/fluffy-pans-argue.md deleted file mode 100644 index 5bbe81a0f..000000000 --- a/.changeset/fluffy-pans-argue.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@trigger.dev/sdk": patch ---- - -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. diff --git a/.changeset/local-bundle-deploy.md b/.changeset/local-bundle-deploy.md deleted file mode 100644 index cb9cfdfee..000000000 --- a/.changeset/local-bundle-deploy.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@trigger.dev/core": patch -"trigger.dev": patch ---- - -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. diff --git a/.changeset/quiet-chats-validate.md b/.changeset/quiet-chats-validate.md deleted file mode 100644 index 9eba1990c..000000000 --- a/.changeset/quiet-chats-validate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@trigger.dev/sdk": patch ---- - -Custom chat agents now validate and parse client data declared with `chat.withClientData({ schema })` before passing it to agent code. diff --git a/.changeset/quiet-floors-hold.md b/.changeset/quiet-floors-hold.md deleted file mode 100644 index 154da2ab5..000000000 --- a/.changeset/quiet-floors-hold.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@trigger.dev/sdk": patch ---- - -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. - -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. diff --git a/.changeset/spry-steers-defer.md b/.changeset/spry-steers-defer.md deleted file mode 100644 index 01a85c8bb..000000000 --- a/.changeset/spry-steers-defer.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -"@trigger.dev/sdk": patch -"@trigger.dev/core": patch ---- - -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. - -```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. diff --git a/.changeset/tidy-mailboxes-wait.md b/.changeset/tidy-mailboxes-wait.md deleted file mode 100644 index 610c48ed8..000000000 --- a/.changeset/tidy-mailboxes-wait.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -"@trigger.dev/core": patch -"@trigger.dev/sdk": patch ---- - -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. - -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. diff --git a/.changeset/violet-buses-tease.md b/.changeset/violet-buses-tease.md deleted file mode 100644 index 191cff776..000000000 --- a/.changeset/violet-buses-tease.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"trigger.dev": patch ---- - -Send the CLI version header on all API requests so deployments are attributable to a CLI version diff --git a/.server-changes/admin-dashboard-enabled-flag.md b/.server-changes/admin-dashboard-enabled-flag.md deleted file mode 100644 index 7fe33ef3e..000000000 --- a/.server-changes/admin-dashboard-enabled-flag.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: feature ---- - -Self-hosted instances can now disable the admin dashboard and user impersonation entirely. See the self-hosting docs for the new setting. diff --git a/.server-changes/appearance-themes-and-options.md b/.server-changes/appearance-themes-and-options.md deleted file mode 100644 index 9f4637763..000000000 --- a/.server-changes/appearance-themes-and-options.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: feature ---- - -The dashboard has two new themes, Black and White, plus appearance options for stronger colors and underlined links. diff --git a/.server-changes/deployment-logs-follow-scroll.md b/.server-changes/deployment-logs-follow-scroll.md deleted file mode 100644 index d5fcf23ce..000000000 --- a/.server-changes/deployment-logs-follow-scroll.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: feature ---- - -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. diff --git a/.server-changes/env-var-inputs-autocomplete-off.md b/.server-changes/env-var-inputs-autocomplete-off.md deleted file mode 100644 index a17457cbd..000000000 --- a/.server-changes/env-var-inputs-autocomplete-off.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: improvement ---- - -Stop the browser offering to autofill or save environment variable values as saved credentials. diff --git a/.server-changes/reduce-webapp-cpu-on-worker-routes.md b/.server-changes/reduce-webapp-cpu-on-worker-routes.md deleted file mode 100644 index 95a2ce05c..000000000 --- a/.server-changes/reduce-webapp-cpu-on-worker-routes.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: improvement ---- - -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. diff --git a/.server-changes/runs-list-column-customization.md b/.server-changes/runs-list-column-customization.md deleted file mode 100644 index 400b1eec2..000000000 --- a/.server-changes/runs-list-column-customization.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: feature ---- - -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. diff --git a/.server-changes/runs-list-query-limit-error.md b/.server-changes/runs-list-query-limit-error.md deleted file mode 100644 index 1bd16673e..000000000 --- a/.server-changes/runs-list-query-limit-error.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: improvement ---- - -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. diff --git a/.server-changes/runs-list-read-isolation.md b/.server-changes/runs-list-read-isolation.md deleted file mode 100644 index 5c4116357..000000000 --- a/.server-changes/runs-list-read-isolation.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: improvement ---- - -Improved the performance and reliability of the runs list and the runs.list API, especially for large projects and filtered views. diff --git a/.server-changes/vercel-staging-gating.md b/.server-changes/vercel-staging-gating.md deleted file mode 100644 index c0000314e..000000000 --- a/.server-changes/vercel-staging-gating.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: fix ---- - -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. diff --git a/.server-changes/vercel-version-skew-protection.md b/.server-changes/vercel-version-skew-protection.md deleted file mode 100644 index 08c67b61a..000000000 --- a/.server-changes/vercel-version-skew-protection.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -area: webapp -type: improvement ---- - -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. diff --git a/hosting/k8s/helm/Chart.yaml b/hosting/k8s/helm/Chart.yaml index 854b2915e..a600a59ea 100644 --- a/hosting/k8s/helm/Chart.yaml +++ b/hosting/k8s/helm/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: trigger description: The official Trigger.dev Helm chart type: application -version: 4.5.12 -appVersion: v4.5.12 +version: 4.5.13 +appVersion: v4.5.13 home: https://trigger.dev sources: - https://github.com/triggerdotdev/trigger.dev diff --git a/packages/build/CHANGELOG.md b/packages/build/CHANGELOG.md index 00c8b2244..c22326cb9 100644 --- a/packages/build/CHANGELOG.md +++ b/packages/build/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/build +## 4.5.13 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.5.13` + ## 4.5.12 ### Patch Changes diff --git a/packages/build/package.json b/packages/build/package.json index 0e4554692..2690c7395 100644 --- a/packages/build/package.json +++ b/packages/build/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/build", - "version": "4.5.12", + "version": "4.5.13", "description": "trigger.dev build extensions", "license": "MIT", "publishConfig": { @@ -79,7 +79,7 @@ }, "dependencies": { "@prisma/config": "^6.10.0", - "@trigger.dev/core": "workspace:4.5.12", + "@trigger.dev/core": "workspace:4.5.13", "mlly": "^1.7.1", "pkg-types": "^1.1.3", "tinyglobby": "^0.2.2", diff --git a/packages/cli-v3/CHANGELOG.md b/packages/cli-v3/CHANGELOG.md index 3339d3bbd..98d3cb339 100644 --- a/packages/cli-v3/CHANGELOG.md +++ b/packages/cli-v3/CHANGELOG.md @@ -1,5 +1,17 @@ # 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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4331)) +- Send the CLI version header on all API requests so deployments are attributable to a CLI version ([#4778](https://github.com/triggerdotdev/trigger.dev/pull/4778)) +- Updated dependencies: + - `@trigger.dev/core@4.5.13` + - `@trigger.dev/build@4.5.13` + - `@trigger.dev/schema-to-json@4.5.13` + ## 4.5.12 ### Patch Changes diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index d88807753..6474d6077 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -1,6 +1,6 @@ { "name": "trigger.dev", - "version": "4.5.12", + "version": "4.5.13", "description": "A Command-Line Interface for Trigger.dev projects", "type": "module", "license": "MIT", @@ -83,9 +83,9 @@ "@opentelemetry/api-logs": "0.218.0", "@opentelemetry/instrumentation": "0.218.0", "@s2-dev/streamstore": "^0.25.0", - "@trigger.dev/build": "workspace:4.5.12", - "@trigger.dev/core": "workspace:4.5.12", - "@trigger.dev/schema-to-json": "workspace:4.5.12", + "@trigger.dev/build": "workspace:4.5.13", + "@trigger.dev/core": "workspace:4.5.13", + "@trigger.dev/schema-to-json": "workspace:4.5.13", "ansi-escapes": "^7.0.0", "braces": "^3.0.3", "c12": "^1.11.1", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index b46f7e62d..6f8f227b7 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,57 @@ # internal-platform +## 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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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. + ## 4.5.12 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index 9514e6882..a10ad9793 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/core", - "version": "4.5.12", + "version": "4.5.13", "description": "Core code used across the Trigger.dev SDK and platform", "license": "MIT", "publishConfig": { diff --git a/packages/python/CHANGELOG.md b/packages/python/CHANGELOG.md index 06f23a736..e21c6bab4 100644 --- a/packages/python/CHANGELOG.md +++ b/packages/python/CHANGELOG.md @@ -1,5 +1,14 @@ # @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` + ## 4.5.12 ### Patch Changes diff --git a/packages/python/package.json b/packages/python/package.json index 9bbba866b..fe92293fd 100644 --- a/packages/python/package.json +++ b/packages/python/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/python", - "version": "4.5.12", + "version": "4.5.13", "description": "Python runtime and build extension for Trigger.dev", "license": "MIT", "publishConfig": { @@ -45,7 +45,7 @@ "check-exports": "attw --pack ." }, "dependencies": { - "@trigger.dev/core": "workspace:4.5.12", + "@trigger.dev/core": "workspace:4.5.13", "tinyexec": "^0.3.2" }, "devDependencies": { @@ -55,12 +55,12 @@ "typescript": "catalog:", "tsx": "4.17.0", "@arethetypeswrong/cli": "^0.18.5", - "@trigger.dev/build": "workspace:4.5.12", - "@trigger.dev/sdk": "workspace:4.5.12" + "@trigger.dev/build": "workspace:4.5.13", + "@trigger.dev/sdk": "workspace:4.5.13" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^4.5.12", - "@trigger.dev/build": "workspace:^4.5.12" + "@trigger.dev/sdk": "workspace:^4.5.13", + "@trigger.dev/build": "workspace:^4.5.13" }, "engines": { "node": ">=18.20.0" diff --git a/packages/react-hooks/CHANGELOG.md b/packages/react-hooks/CHANGELOG.md index b564925c4..c7be6ebe4 100644 --- a/packages/react-hooks/CHANGELOG.md +++ b/packages/react-hooks/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/react-hooks +## 4.5.13 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.5.13` + ## 4.5.12 ### Patch Changes diff --git a/packages/react-hooks/package.json b/packages/react-hooks/package.json index 520e8067b..3e5ec85d3 100644 --- a/packages/react-hooks/package.json +++ b/packages/react-hooks/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/react-hooks", - "version": "4.5.12", + "version": "4.5.13", "description": "trigger.dev react hooks", "license": "MIT", "publishConfig": { @@ -37,7 +37,7 @@ "check-exports": "attw --pack ." }, "dependencies": { - "@trigger.dev/core": "workspace:^4.5.12", + "@trigger.dev/core": "workspace:^4.5.13", "swr": "^2.2.5" }, "devDependencies": { diff --git a/packages/redis-worker/CHANGELOG.md b/packages/redis-worker/CHANGELOG.md index 9bb5b5200..7b9f37f8c 100644 --- a/packages/redis-worker/CHANGELOG.md +++ b/packages/redis-worker/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/redis-worker +## 4.5.13 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.5.13` + ## 4.5.12 ### Patch Changes diff --git a/packages/redis-worker/package.json b/packages/redis-worker/package.json index 30facf5c0..7e78adfb1 100644 --- a/packages/redis-worker/package.json +++ b/packages/redis-worker/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/redis-worker", - "version": "4.5.12", + "version": "4.5.13", "description": "Redis worker for trigger.dev", "license": "MIT", "publishConfig": { @@ -23,7 +23,7 @@ "test": "vitest --sequence.concurrent=false --no-file-parallelism" }, "dependencies": { - "@trigger.dev/core": "workspace:4.5.12", + "@trigger.dev/core": "workspace:4.5.13", "nanoid": "^5.1.16", "p-limit": "^6.2.0", "seedrandom": "^3.0.5", diff --git a/packages/rsc/CHANGELOG.md b/packages/rsc/CHANGELOG.md index 8ec12d614..892f926a9 100644 --- a/packages/rsc/CHANGELOG.md +++ b/packages/rsc/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/rsc +## 4.5.13 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.5.13` + ## 4.5.12 ### Patch Changes diff --git a/packages/rsc/package.json b/packages/rsc/package.json index 9a9e2d832..bfdafddde 100644 --- a/packages/rsc/package.json +++ b/packages/rsc/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/rsc", - "version": "4.5.12", + "version": "4.5.13", "description": "trigger.dev rsc", "license": "MIT", "publishConfig": { @@ -37,7 +37,7 @@ "check-exports": "attw --pack ." }, "dependencies": { - "@trigger.dev/core": "workspace:^4.5.12", + "@trigger.dev/core": "workspace:^4.5.13", "mlly": "^1.7.1", "react": "19.0.0-rc.1", "react-dom": "19.0.0-rc.1" diff --git a/packages/schema-to-json/CHANGELOG.md b/packages/schema-to-json/CHANGELOG.md index d6649bd43..76c3b1560 100644 --- a/packages/schema-to-json/CHANGELOG.md +++ b/packages/schema-to-json/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/schema-to-json +## 4.5.13 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.5.13` + ## 4.5.12 ### Patch Changes diff --git a/packages/schema-to-json/package.json b/packages/schema-to-json/package.json index 4d46d0299..186f6fc68 100644 --- a/packages/schema-to-json/package.json +++ b/packages/schema-to-json/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/schema-to-json", - "version": "4.5.12", + "version": "4.5.13", "description": "Convert various schema validation libraries to JSON Schema", "license": "MIT", "publishConfig": { diff --git a/packages/trigger-sdk/CHANGELOG.md b/packages/trigger-sdk/CHANGELOG.md index b676f238d..4760e9610 100644 --- a/packages/trigger-sdk/CHANGELOG.md +++ b/packages/trigger-sdk/CHANGELOG.md @@ -1,5 +1,67 @@ # @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](https://github.com/triggerdotdev/trigger.dev/pull/4768)) +- Browser chats now keep the active turn open across page reloads when older completion records are replayed. ([#4643](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/4744)) +- Custom chat agents now validate and parse client data declared with `chat.withClientData({ schema })` before passing it to agent code. ([#4646](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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](https://github.com/triggerdotdev/trigger.dev/pull/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` + ## 4.5.12 ### Patch Changes diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index 417d3644a..2e05e204f 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/sdk", - "version": "4.5.12", + "version": "4.5.13", "description": "trigger.dev Node.JS SDK", "license": "MIT", "publishConfig": { @@ -77,7 +77,7 @@ "dependencies": { "@opentelemetry/api": "1.9.1", "@opentelemetry/semantic-conventions": "1.41.1", - "@trigger.dev/core": "workspace:4.5.12", + "@trigger.dev/core": "workspace:4.5.13", "uncrypto": "^0.1.3" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7a0a3e5d..a41a3b207 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1481,7 +1481,7 @@ importers: specifier: ^6.10.0 version: 6.19.0(magicast@0.3.5) '@trigger.dev/core': - specifier: workspace:4.5.12 + specifier: workspace:4.5.13 version: link:../core mlly: specifier: ^1.7.1 @@ -1545,13 +1545,13 @@ importers: specifier: ^0.25.0 version: 0.25.0(supports-color@10.0.0) '@trigger.dev/build': - specifier: workspace:4.5.12 + specifier: workspace:4.5.13 version: link:../build '@trigger.dev/core': - specifier: workspace:4.5.12 + specifier: workspace:4.5.13 version: link:../core '@trigger.dev/schema-to-json': - specifier: workspace:4.5.12 + specifier: workspace:4.5.13 version: link:../schema-to-json ansi-escapes: specifier: ^7.0.0 @@ -1878,7 +1878,7 @@ importers: packages/python: dependencies: '@trigger.dev/core': - specifier: workspace:4.5.12 + specifier: workspace:4.5.13 version: link:../core tinyexec: specifier: ^0.3.2 @@ -1888,10 +1888,10 @@ importers: specifier: ^0.18.5 version: 0.18.5 '@trigger.dev/build': - specifier: workspace:4.5.12 + specifier: workspace:4.5.13 version: link:../build '@trigger.dev/sdk': - specifier: workspace:4.5.12 + specifier: workspace:4.5.13 version: link:../trigger-sdk '@types/node': specifier: 24.13.3 @@ -1912,7 +1912,7 @@ importers: packages/react-hooks: dependencies: '@trigger.dev/core': - specifier: workspace:^4.5.12 + specifier: workspace:^4.5.13 version: link:../core react: specifier: 18.3.1 @@ -1946,7 +1946,7 @@ importers: packages/redis-worker: dependencies: '@trigger.dev/core': - specifier: workspace:4.5.12 + specifier: workspace:4.5.13 version: link:../core cron-parser: specifier: ^4.9.0 @@ -1992,7 +1992,7 @@ importers: packages/rsc: dependencies: '@trigger.dev/core': - specifier: workspace:^4.5.12 + specifier: workspace:^4.5.13 version: link:../core mlly: specifier: ^1.7.1 @@ -2078,7 +2078,7 @@ importers: specifier: 1.41.1 version: 1.41.1 '@trigger.dev/core': - specifier: workspace:4.5.12 + specifier: workspace:4.5.13 version: link:../core react: specifier: 18.3.1