@trigger.dev/sdk 4.5.16 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/imports/ai-runtime-cjs.cjs.map +1 -1
- package/dist/commonjs/imports/ai-runtime.js +2 -0
- package/dist/commonjs/v3/ai-shared.d.ts +15 -0
- package/dist/commonjs/v3/ai-shared.js +35 -1
- package/dist/commonjs/v3/ai-shared.js.map +1 -1
- package/dist/commonjs/v3/ai.d.ts +311 -25
- package/dist/commonjs/v3/ai.js +1633 -380
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/auth.d.ts +10 -4
- package/dist/commonjs/v3/auth.js.map +1 -1
- package/dist/commonjs/v3/chat-client.d.ts +11 -2
- package/dist/commonjs/v3/chat-client.js +227 -111
- package/dist/commonjs/v3/chat-client.js.map +1 -1
- package/dist/commonjs/v3/chat-react.d.ts +88 -0
- package/dist/commonjs/v3/chat-react.js +130 -1
- package/dist/commonjs/v3/chat-react.js.map +1 -1
- package/dist/commonjs/v3/chat-server.d.ts +42 -6
- package/dist/commonjs/v3/chat-server.js +52 -7
- package/dist/commonjs/v3/chat-server.js.map +1 -1
- package/dist/commonjs/v3/chat.d.ts +92 -10
- package/dist/commonjs/v3/chat.js +328 -73
- package/dist/commonjs/v3/chat.js.map +1 -1
- package/dist/commonjs/v3/chatSnapshotIo.d.ts +2 -0
- package/dist/commonjs/v3/chatSnapshotIo.js +173 -0
- package/dist/commonjs/v3/chatSnapshotIo.js.map +1 -0
- package/dist/commonjs/v3/chatVersionSkew.d.ts +12 -0
- package/dist/commonjs/v3/chatVersionSkew.js +30 -0
- package/dist/commonjs/v3/chatVersionSkew.js.map +1 -0
- package/dist/commonjs/v3/externalDeploymentId.d.ts +23 -0
- package/dist/commonjs/v3/externalDeploymentId.js +43 -0
- package/dist/commonjs/v3/externalDeploymentId.js.map +1 -0
- package/dist/commonjs/v3/retry.d.ts +4 -0
- package/dist/commonjs/v3/retry.js +22 -13
- package/dist/commonjs/v3/retry.js.map +1 -1
- package/dist/commonjs/v3/sessions.d.ts +15 -2
- package/dist/commonjs/v3/sessions.js +12 -1
- package/dist/commonjs/v3/sessions.js.map +1 -1
- package/dist/commonjs/v3/shared.js +30 -36
- package/dist/commonjs/v3/shared.js.map +1 -1
- package/dist/commonjs/v3/test/index.d.ts +1 -0
- package/dist/commonjs/v3/test/index.js +3 -1
- package/dist/commonjs/v3/test/index.js.map +1 -1
- package/dist/commonjs/v3/test/mock-chat-agent.d.ts +47 -4
- package/dist/commonjs/v3/test/mock-chat-agent.js +116 -10
- package/dist/commonjs/v3/test/mock-chat-agent.js.map +1 -1
- package/dist/commonjs/v3/test/test-session-handle.js +6 -0
- package/dist/commonjs/v3/test/test-session-handle.js.map +1 -1
- package/dist/commonjs/v3/test/transcript-storage-tests.d.ts +40 -0
- package/dist/commonjs/v3/test/transcript-storage-tests.js +258 -0
- package/dist/commonjs/v3/test/transcript-storage-tests.js.map +1 -0
- package/dist/commonjs/v3/transcriptStorage.d.ts +261 -0
- package/dist/commonjs/v3/transcriptStorage.js +374 -0
- package/dist/commonjs/v3/transcriptStorage.js.map +1 -0
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/imports/ai-runtime.d.ts +2 -2
- package/dist/esm/imports/ai-runtime.js +2 -2
- package/dist/esm/imports/ai-runtime.js.map +1 -1
- package/dist/esm/v3/ai-shared.d.ts +15 -0
- package/dist/esm/v3/ai-shared.js +33 -0
- package/dist/esm/v3/ai-shared.js.map +1 -1
- package/dist/esm/v3/ai.d.ts +311 -25
- package/dist/esm/v3/ai.js +1625 -377
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/auth.d.ts +10 -4
- package/dist/esm/v3/auth.js.map +1 -1
- package/dist/esm/v3/chat-client.d.ts +11 -2
- package/dist/esm/v3/chat-client.js +228 -112
- package/dist/esm/v3/chat-client.js.map +1 -1
- package/dist/esm/v3/chat-react.d.ts +88 -0
- package/dist/esm/v3/chat-react.js +127 -1
- package/dist/esm/v3/chat-react.js.map +1 -1
- package/dist/esm/v3/chat-server.d.ts +42 -6
- package/dist/esm/v3/chat-server.js +53 -8
- package/dist/esm/v3/chat-server.js.map +1 -1
- package/dist/esm/v3/chat.d.ts +92 -10
- package/dist/esm/v3/chat.js +329 -74
- package/dist/esm/v3/chat.js.map +1 -1
- package/dist/esm/v3/chatSnapshotIo.d.ts +2 -0
- package/dist/esm/v3/chatSnapshotIo.js +165 -0
- package/dist/esm/v3/chatSnapshotIo.js.map +1 -0
- package/dist/esm/v3/chatVersionSkew.d.ts +12 -0
- package/dist/esm/v3/chatVersionSkew.js +27 -0
- package/dist/esm/v3/chatVersionSkew.js.map +1 -0
- package/dist/esm/v3/externalDeploymentId.d.ts +23 -0
- package/dist/esm/v3/externalDeploymentId.js +38 -0
- package/dist/esm/v3/externalDeploymentId.js.map +1 -0
- package/dist/esm/v3/retry.d.ts +4 -0
- package/dist/esm/v3/retry.js +14 -8
- package/dist/esm/v3/retry.js.map +1 -1
- package/dist/esm/v3/sessions.d.ts +15 -2
- package/dist/esm/v3/sessions.js +11 -1
- package/dist/esm/v3/sessions.js.map +1 -1
- package/dist/esm/v3/shared.js +17 -23
- package/dist/esm/v3/shared.js.map +1 -1
- package/dist/esm/v3/test/index.d.ts +1 -0
- package/dist/esm/v3/test/index.js +1 -0
- package/dist/esm/v3/test/index.js.map +1 -1
- package/dist/esm/v3/test/mock-chat-agent.d.ts +47 -4
- package/dist/esm/v3/test/mock-chat-agent.js +118 -12
- package/dist/esm/v3/test/mock-chat-agent.js.map +1 -1
- package/dist/esm/v3/test/test-session-handle.js +6 -0
- package/dist/esm/v3/test/test-session-handle.js.map +1 -1
- package/dist/esm/v3/test/transcript-storage-tests.d.ts +40 -0
- package/dist/esm/v3/test/transcript-storage-tests.js +255 -0
- package/dist/esm/v3/test/transcript-storage-tests.js.map +1 -0
- package/dist/esm/v3/transcriptStorage.d.ts +261 -0
- package/dist/esm/v3/transcriptStorage.js +360 -0
- package/dist/esm/v3/transcriptStorage.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/docs/ai-chat/actions.mdx +55 -23
- package/docs/ai-chat/anatomy.mdx +47 -27
- package/docs/ai-chat/backend.mdx +125 -48
- package/docs/ai-chat/background-injection.mdx +122 -19
- package/docs/ai-chat/client-protocol.mdx +5 -4
- package/docs/ai-chat/compaction.mdx +13 -7
- package/docs/ai-chat/custom-agents.mdx +23 -0
- package/docs/ai-chat/fast-starts.mdx +27 -20
- package/docs/ai-chat/frontend.mdx +19 -14
- package/docs/ai-chat/how-it-works.mdx +1 -1
- package/docs/ai-chat/lifecycle-hooks.mdx +6 -2
- package/docs/ai-chat/migrating-from-a-route-handler.mdx +16 -14
- package/docs/ai-chat/migrating-from-hydrate-messages.mdx +299 -0
- package/docs/ai-chat/patterns/database-persistence.mdx +24 -9
- package/docs/ai-chat/patterns/native-compaction.mdx +310 -0
- package/docs/ai-chat/patterns/persistence-and-replay.mdx +34 -23
- package/docs/ai-chat/patterns/skills.mdx +7 -10
- package/docs/ai-chat/patterns/version-upgrades.mdx +79 -6
- package/docs/ai-chat/pending-messages.mdx +3 -3
- package/docs/ai-chat/prompt-caching.mdx +23 -25
- package/docs/ai-chat/quick-start.mdx +11 -11
- package/docs/ai-chat/reference.mdx +69 -10
- package/docs/ai-chat/sessions.mdx +6 -1
- package/docs/ai-chat/side-channels.mdx +1 -1
- package/docs/ai-chat/testing.mdx +2 -1
- package/docs/ai-chat/tools.mdx +18 -13
- package/docs/ai-chat/transcript-storage.mdx +247 -0
- package/docs/ai-chat/upgrade-guide.mdx +2 -2
- package/docs/apikeys.mdx +27 -45
- package/docs/config/extensions/syncEnvVars.mdx +6 -0
- package/docs/deploy-environment-variables.mdx +23 -2
- package/docs/deployment/overview.mdx +4 -8
- package/docs/deployment/preview-branches.mdx +4 -4
- package/docs/deployment/version-skew-protection.mdx +62 -0
- package/docs/introduction.mdx +58 -152
- package/docs/manual-setup.mdx +7 -7
- package/docs/mcp-tools.mdx +9 -0
- package/docs/quick-start.mdx +3 -3
- package/docs/realtime/auth.mdx +1 -1
- package/docs/reports.mdx +157 -0
- package/docs/self-hosting/security.mdx +17 -0
- package/docs/tasks/scheduled.mdx +24 -0
- package/docs/triggering.mdx +1 -1
- package/docs/troubleshooting-zod.mdx +158 -0
- package/package.json +4 -4
- package/skills/trigger-authoring-chat-agent/SKILL.md +41 -29
- package/skills/trigger-chat-agent-advanced/SKILL.md +54 -19
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Zod"
|
|
3
|
+
description: "Resolve Zod typechecking, test, and deployment errors when upgrading to Trigger.dev v4.6."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
**Trigger.dev v4.6 uses Zod 4 by default. Zod 3.25.56 and later 3.x releases remain supported.**
|
|
7
|
+
|
|
8
|
+
Use this guide if typechecking, tests, or deployment fail after upgrading. The supported project dependency range is `^3.25.56 || ^4.0.0`.
|
|
9
|
+
|
|
10
|
+
| Your project's Zod dependency | What to do |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| No direct Zod dependency | No Zod-specific change is needed. Trigger.dev installs a compatible runtime dependency automatically. |
|
|
13
|
+
| Zod below 3.25.56 | Upgrade before using Trigger.dev v4.6. These versions are unsupported. |
|
|
14
|
+
| Zod 3.25.56 or later in the 3.x release line | Keep using your Zod 3 schemas with supported SDK APIs. Review the cross-major caveats below. |
|
|
15
|
+
| Zod 4.x | Supported and the recommended default. |
|
|
16
|
+
|
|
17
|
+
Passing your own supported Zod 3 schema to [`schemaTask`](/tasks/schemaTask) or `toolTask` remains supported. This does not make Zod 3 and Zod 4 schemas interchangeable when you compose or inspect them yourself.
|
|
18
|
+
|
|
19
|
+
## Typechecking, tests, or deployment fail after upgrading
|
|
20
|
+
|
|
21
|
+
An older Zod installation can be missing the entry points, types, or schema behavior that Trigger.dev v4.6 requires.
|
|
22
|
+
|
|
23
|
+
Expect TypeScript errors when an unsupported version is resolved, particularly with `skipLibCheck: false`. Tests may also fail when they import or execute schemas. Deployment loads your task code, so an incompatible runtime installation causes deployment errors even if your local tooling skips typechecking. A passing local test suite does not establish that an unsupported version is safe to deploy.
|
|
24
|
+
|
|
25
|
+
Symptoms include missing `zod/v4` or `zod/v4/core` exports, missing Zod types, incompatible generic parameters, and errors while loading tasks. The exact failure depends on the version and dependency tree; not every unsupported version fails at the same stage.
|
|
26
|
+
|
|
27
|
+
### Check the installed version
|
|
28
|
+
|
|
29
|
+
Inspect the resolved dependencies in the package that contains your tasks, not only the version range in `package.json`:
|
|
30
|
+
|
|
31
|
+
<CodeGroup>
|
|
32
|
+
```bash npm
|
|
33
|
+
npm ls zod
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```bash pnpm
|
|
37
|
+
pnpm why zod
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```bash bun
|
|
41
|
+
bun pm ls --all
|
|
42
|
+
```
|
|
43
|
+
</CodeGroup>
|
|
44
|
+
|
|
45
|
+
Check the lockfile and any dependency overrides or resolutions as well. An override can keep an old Zod version installed even after you update a direct dependency.
|
|
46
|
+
|
|
47
|
+
### Update Zod
|
|
48
|
+
|
|
49
|
+
Choose whether to move your application to Zod 4 or keep its existing Zod 3 schemas:
|
|
50
|
+
|
|
51
|
+
<Tabs>
|
|
52
|
+
<Tab title="Move to Zod 4">
|
|
53
|
+
Install the latest Zod 4 release:
|
|
54
|
+
|
|
55
|
+
<CodeGroup>
|
|
56
|
+
```bash npm
|
|
57
|
+
npm install zod@4
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```bash pnpm
|
|
61
|
+
pnpm add zod@4
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```bash bun
|
|
65
|
+
bun add zod@4
|
|
66
|
+
```
|
|
67
|
+
</CodeGroup>
|
|
68
|
+
|
|
69
|
+
Review [Zod's migration guide](https://zod.dev/v4/changelog) for changes to your own schemas and error handling.
|
|
70
|
+
</Tab>
|
|
71
|
+
<Tab title="Stay on Zod 3">
|
|
72
|
+
Install the latest Zod 3 patch rather than pinning the minimum supported version:
|
|
73
|
+
|
|
74
|
+
<CodeGroup>
|
|
75
|
+
```bash npm
|
|
76
|
+
npm install zod@3
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```bash pnpm
|
|
80
|
+
pnpm add zod@3
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```bash bun
|
|
84
|
+
bun add zod@3
|
|
85
|
+
```
|
|
86
|
+
</CodeGroup>
|
|
87
|
+
|
|
88
|
+
Your existing `import { z } from "zod"` continues to use Zod 3. Trigger.dev's own schemas use the Zod 4 implementation included in the supported Zod 3 package.
|
|
89
|
+
</Tab>
|
|
90
|
+
</Tabs>
|
|
91
|
+
|
|
92
|
+
Other dependencies can require a higher minimum than Trigger.dev. For example, an AI SDK dependency may require Zod 3.25.76 or Zod 4. Meet those peer requirements too; do not force a lower version across every dependency.
|
|
93
|
+
|
|
94
|
+
### Verify the update
|
|
95
|
+
|
|
96
|
+
- Commit the updated manifest and lockfile, and make sure CI uses them.
|
|
97
|
+
- Recheck the installed Zod versions in your local and deployment environments.
|
|
98
|
+
- Run your project's TypeScript checks and tests, including code that constructs or inspects schemas.
|
|
99
|
+
- Keep the CLI and SDK versions aligned using the [package upgrade guide](/upgrading-packages), then retry deployment.
|
|
100
|
+
|
|
101
|
+
Do not use `skipLibCheck` or ignored peer-dependency warnings as a compatibility fix. They do not change the runtime package that deployment loads. You do not need to add Zod as a direct dependency if your application does not import it.
|
|
102
|
+
|
|
103
|
+
## Parsing still fails with a supported Zod 3 version
|
|
104
|
+
|
|
105
|
+
Supporting a Zod 3 schema as an SDK input is different from nesting a Trigger.dev-exported Zod 4 schema inside a Zod 3 object. Cross-major composition can fail during typechecking or parsing.
|
|
106
|
+
|
|
107
|
+
For example, this mixes a Zod 3 object with a Zod 4 `RetryOptions` schema:
|
|
108
|
+
|
|
109
|
+
```ts incompatible-schemas.ts
|
|
110
|
+
import { z } from "zod"; // Project dependency is Zod 3.
|
|
111
|
+
import { RetryOptions } from "@trigger.dev/core/v3";
|
|
112
|
+
|
|
113
|
+
const schema = z.object({ retry: RetryOptions });
|
|
114
|
+
schema.parse({ retry: {} });
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Use Zod 4 for every schema in the composed object. The `zod/v4` entry point is available in both supported Zod 3 packages and Zod 4 packages:
|
|
118
|
+
|
|
119
|
+
```ts compatible-schemas.ts
|
|
120
|
+
import { z } from "zod/v4";
|
|
121
|
+
import { RetryOptions } from "@trigger.dev/core/v3";
|
|
122
|
+
|
|
123
|
+
const schema = z.object({ retry: RetryOptions });
|
|
124
|
+
schema.parse({ retry: {} });
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Alternatively, keep the Zod 3 and Trigger.dev schemas separate and call each schema's parser independently. You do not need to migrate unrelated application schemas to use this approach.
|
|
128
|
+
|
|
129
|
+
## An `instanceof` check stops matching
|
|
130
|
+
|
|
131
|
+
A Zod 4 error is not an instance of the Zod 3 `ZodError` constructor. A constructor check against your project's Zod 3 import can stop matching errors produced by Trigger.dev's schemas:
|
|
132
|
+
|
|
133
|
+
```ts mismatched-error-check.ts
|
|
134
|
+
import { z } from "zod"; // Project dependency is Zod 3.
|
|
135
|
+
import { RetryOptions } from "@trigger.dev/core/v3";
|
|
136
|
+
|
|
137
|
+
const result = RetryOptions.safeParse({ maxAttempts: "invalid" });
|
|
138
|
+
|
|
139
|
+
if (!result.success) {
|
|
140
|
+
console.log(result.error instanceof z.ZodError); // false
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Use the result returned by the schema you called instead of a constructor from another Zod installation:
|
|
145
|
+
|
|
146
|
+
```ts schema-error-handling.ts
|
|
147
|
+
import { RetryOptions } from "@trigger.dev/core/v3";
|
|
148
|
+
|
|
149
|
+
const result = RetryOptions.safeParse({ maxAttempts: "invalid" });
|
|
150
|
+
|
|
151
|
+
if (!result.success) {
|
|
152
|
+
console.error(result.error.issues);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The same caveat applies to checks such as `schema instanceof z.ZodObject`. Multiple installed copies can also have different constructors, even within the same major version. Prefer parsing and the returned validation result over inspecting classes or private fields such as `_def`.
|
|
157
|
+
|
|
158
|
+
These examples are not an exhaustive list of cross-major differences. If errors remain after updating, check which Zod implementation creates each schema and which code composes, parses, or inspects it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trigger.dev/sdk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"description": "trigger.dev Node.JS SDK",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@opentelemetry/api": "1.9.1",
|
|
71
71
|
"@opentelemetry/semantic-conventions": "1.41.1",
|
|
72
|
-
"@trigger.dev/core": "4.
|
|
72
|
+
"@trigger.dev/core": "4.6.0",
|
|
73
73
|
"uncrypto": "^0.1.3"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
@@ -82,13 +82,13 @@
|
|
|
82
82
|
"tshy": "^4.1.3",
|
|
83
83
|
"tsx": "4.17.0",
|
|
84
84
|
"typescript": "7.0.2",
|
|
85
|
-
"zod": "
|
|
85
|
+
"zod": "4.5.4"
|
|
86
86
|
},
|
|
87
87
|
"peerDependencies": {
|
|
88
88
|
"@ai-sdk/otel": ">=1.0.0-0 <2",
|
|
89
89
|
"ai": "^5.0.0 || ^6.0.0 || >=7.0.0-canary <8",
|
|
90
90
|
"react": "^18.0 || ^19.0",
|
|
91
|
-
"zod": "^3.
|
|
91
|
+
"zod": "^3.25.56 || ^4.0.0"
|
|
92
92
|
},
|
|
93
93
|
"peerDependenciesMeta": {
|
|
94
94
|
"@ai-sdk/otel": {
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
name: trigger-authoring-chat-agent
|
|
3
3
|
description: >
|
|
4
4
|
Author and run a durable AI chat agent with chat.agent from @trigger.dev/sdk/ai: the per-turn
|
|
5
|
-
run loop, why you MUST
|
|
6
|
-
vs calling chat.pipe(), the two server actions (chat.createStartSessionAction +
|
|
5
|
+
run loop, why you MUST take streamText from the run argument rather than importing it from ai,
|
|
6
|
+
returning a StreamTextResult vs calling chat.pipe(), the two server actions (chat.createStartSessionAction +
|
|
7
7
|
auth.createPublicToken), and wiring useChat to useTriggerChatTransport. Load this when building,
|
|
8
8
|
modifying, or debugging a chat backend (the agent task or its lifecycle hooks) or its React
|
|
9
9
|
transport, when declaring typed tools or custom data parts, or when migrating a plain AI SDK
|
|
@@ -47,10 +47,9 @@ import { anthropic } from "@ai-sdk/anthropic";
|
|
|
47
47
|
|
|
48
48
|
export const myChat = chat.agent({
|
|
49
49
|
id: "my-chat",
|
|
50
|
-
|
|
50
|
+
// `streamText` below is the SDK's, from the run argument. See "Common mistakes".
|
|
51
|
+
run: async ({ messages, signal, streamText }) =>
|
|
51
52
|
streamText({
|
|
52
|
-
// Spread this FIRST. See "Common mistakes".
|
|
53
|
-
...chat.toStreamTextOptions(),
|
|
54
53
|
model: anthropic("claude-sonnet-4-5"),
|
|
55
54
|
messages,
|
|
56
55
|
abortSignal: signal,
|
|
@@ -121,16 +120,22 @@ inside nested helpers, call `await chat.pipe(result)` from anywhere in the task
|
|
|
121
120
|
`run` resolve `void`.
|
|
122
121
|
|
|
123
122
|
```ts
|
|
123
|
+
import { chat, type ChatStreamText } from "@trigger.dev/sdk/ai";
|
|
124
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
125
|
+
import type { ModelMessage } from "ai";
|
|
126
|
+
|
|
124
127
|
export const agentChat = chat.agent({
|
|
125
128
|
id: "agent-chat",
|
|
126
|
-
run: async ({ messages }) => {
|
|
127
|
-
await runAgentLoop(messages); // don't return; pipe inside
|
|
129
|
+
run: async ({ messages, streamText }) => {
|
|
130
|
+
await runAgentLoop(messages, streamText); // don't return; pipe inside
|
|
128
131
|
},
|
|
129
132
|
});
|
|
130
133
|
|
|
131
|
-
|
|
134
|
+
// A loop factored out of `run` takes `streamText` as an argument, so it keeps the
|
|
135
|
+
// managed options. `ChatStreamText` (from `@trigger.dev/sdk/ai`) types the parameter.
|
|
136
|
+
// `chat.toStreamTextOptions()` is the alternative when threading it down is impractical.
|
|
137
|
+
async function runAgentLoop(messages: ModelMessage[], streamText: ChatStreamText) {
|
|
132
138
|
const result = streamText({
|
|
133
|
-
...chat.toStreamTextOptions(),
|
|
134
139
|
model: anthropic("claude-sonnet-4-5"),
|
|
135
140
|
messages,
|
|
136
141
|
});
|
|
@@ -138,10 +143,10 @@ async function runAgentLoop(messages: ModelMessage[]) {
|
|
|
138
143
|
}
|
|
139
144
|
```
|
|
140
145
|
|
|
141
|
-
### 2. Typed tools (declare on config AND
|
|
146
|
+
### 2. Typed tools (declare on config AND pass back)
|
|
142
147
|
|
|
143
148
|
Declare tools on `chat.agent({ tools })`, read them back typed from the `run()` payload, and pass
|
|
144
|
-
that set
|
|
149
|
+
that set as `tools`. One declaration flows everywhere.
|
|
145
150
|
|
|
146
151
|
```ts
|
|
147
152
|
import { tool, stepCountIs } from "ai";
|
|
@@ -158,11 +163,11 @@ const tools = {
|
|
|
158
163
|
export const myChat = chat.agent({
|
|
159
164
|
id: "my-chat",
|
|
160
165
|
tools, // so toModelOutput survives across turns
|
|
161
|
-
run: async ({ messages, tools, signal }) =>
|
|
166
|
+
run: async ({ messages, tools, signal, streamText }) =>
|
|
162
167
|
streamText({
|
|
163
|
-
...chat.toStreamTextOptions({ tools }), // same set, handed back typed
|
|
164
168
|
model: anthropic("claude-sonnet-4-5"),
|
|
165
169
|
messages,
|
|
170
|
+
tools, // same set, handed back typed
|
|
166
171
|
abortSignal: signal,
|
|
167
172
|
stopWhen: stepCountIs(15),
|
|
168
173
|
}),
|
|
@@ -203,8 +208,8 @@ export const myChat = chat
|
|
|
203
208
|
onTurnStart: async ({ uiMessages, writer }) => {
|
|
204
209
|
writer.write({ type: "data-turn-status", data: { status: "preparing" } });
|
|
205
210
|
},
|
|
206
|
-
run: async ({ messages, tools, signal }) =>
|
|
207
|
-
streamText({
|
|
211
|
+
run: async ({ messages, tools, signal, streamText }) =>
|
|
212
|
+
streamText({ model, messages, tools, abortSignal: signal }),
|
|
208
213
|
});
|
|
209
214
|
```
|
|
210
215
|
|
|
@@ -217,8 +222,9 @@ frontend, narrow `useChat` with `InferChatUIMessage<typeof myChat>` from `@trigg
|
|
|
217
222
|
`chat.agent` accepts hooks that fire in a fixed per-turn order:
|
|
218
223
|
|
|
219
224
|
```text
|
|
220
|
-
onValidateMessages ->
|
|
221
|
-
->
|
|
225
|
+
onValidateMessages -> storage.loadContext (or the deprecated hydrateMessages)
|
|
226
|
+
-> onChatStart (chat's first message only)
|
|
227
|
+
-> onTurnStart -> run() -> onBeforeTurnComplete -> onTurnComplete -> storage.save
|
|
222
228
|
```
|
|
223
229
|
|
|
224
230
|
`onBoot` fires once per worker process (every fresh boot, including continuation runs) and is where
|
|
@@ -228,13 +234,13 @@ first message. Suspend/resume use `onChatSuspend` / `onChatResume`. Config optio
|
|
|
228
234
|
`uiMessageStreamOptions`, and `exitAfterPreloadIdle`. There is no generic `retry`; `chat.agent`
|
|
229
235
|
runs with `maxAttempts: 1` internally.
|
|
230
236
|
|
|
231
|
-
Stop
|
|
237
|
+
Stop depends on it: the `signal` passed to `run` aborts on stop or cancel. Forward it as
|
|
232
238
|
`abortSignal` to `streamText`, or the Stop button updates the UI while the model keeps generating
|
|
233
239
|
server-side.
|
|
234
240
|
|
|
235
241
|
```ts
|
|
236
|
-
run: async ({ messages, signal }) =>
|
|
237
|
-
streamText({
|
|
242
|
+
run: async ({ messages, signal, streamText }) =>
|
|
243
|
+
streamText({ model, messages, abortSignal: signal, stopWhen: stepCountIs(15) });
|
|
238
244
|
```
|
|
239
245
|
|
|
240
246
|
### 6. Migrating from a plain AI SDK `streamText` route
|
|
@@ -243,24 +249,30 @@ There is no API route in this model. The transport replaces the route round-trip
|
|
|
243
249
|
|
|
244
250
|
- Delete the route handler. Move per-request auth into the two server actions from Setup step 2.
|
|
245
251
|
- Move the `streamText` call into `run`. It already receives pre-converted `ModelMessage[]`.
|
|
246
|
-
- Return the `StreamTextResult` (it auto-pipes) and
|
|
252
|
+
- Return the `StreamTextResult` (it auto-pipes) and take `streamText` from `run`'s argument, not from `ai`.
|
|
247
253
|
- On the client, swap the `api` URL for `useTriggerChatTransport`; `useChat` stays the same shape.
|
|
248
254
|
|
|
249
255
|
## Common mistakes
|
|
250
256
|
|
|
251
|
-
- **CRITICAL:
|
|
257
|
+
- **CRITICAL: calling the `streamText` imported from `ai`.**
|
|
252
258
|
```ts
|
|
253
259
|
// Wrong - compaction / steering / background injection silently no-op
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
260
|
+
import { streamText } from "ai";
|
|
261
|
+
run: async ({ messages, signal }) => streamText({ model, messages, abortSignal: signal });
|
|
262
|
+
// Correct - the run argument's streamText carries the managed options
|
|
263
|
+
run: async ({ messages, signal, streamText }) => streamText({ model, messages, abortSignal: signal });
|
|
257
264
|
```
|
|
258
|
-
|
|
259
|
-
injection,
|
|
260
|
-
telemetry.
|
|
265
|
+
The SDK's one carries the `prepareStep` behind compaction, mid-turn steering and background
|
|
266
|
+
injection, the system prompt from `chat.prompt()` or `chat.agent({ system })`, the registry-resolved
|
|
267
|
+
model, and telemetry. The imported one carries none of it, with no error.
|
|
268
|
+
`...chat.toStreamTextOptions()` does the same job by hand, and is what a custom agent has to use,
|
|
269
|
+
since it has no `run` argument. A `chat.headStart` route gets a bound `streamText` too, and there it
|
|
270
|
+
also owns `messages`, `prompt`, `stopWhen` and `abortSignal`. Spreading it and then re-setting
|
|
271
|
+
`tools` or `prepareStep` replaces the managed ones; the run argument's `streamText` merges `tools`
|
|
272
|
+
and composes `prepareStep` instead.
|
|
261
273
|
|
|
262
274
|
- **Declaring tools only on `streamText`.** Also declare them on `chat.agent({ tools })`, read them
|
|
263
|
-
back from `run`, and pass `
|
|
275
|
+
back from `run`, and pass that set as `tools`. Otherwise each tool's
|
|
264
276
|
`toModelOutput` runs on turn 1 but is dropped when history is re-converted on later turns.
|
|
265
277
|
|
|
266
278
|
- **Not forwarding `signal` for stop.** Without `abortSignal: signal`, Stop updates the UI but the
|
|
@@ -143,6 +143,12 @@ await waitUntilComplete();
|
|
|
143
143
|
5s timeout, before `onTurnComplete`). `chat.inject(messages)` queues `ModelMessage[]` that drain at
|
|
144
144
|
the next turn start or `prepareStep` boundary.
|
|
145
145
|
|
|
146
|
+
Two lanes, decided by role. A `role: "system"` message goes to the model's instructions, where it is
|
|
147
|
+
trusted like the system prompt, and applies to the next turn only. Any other role joins the
|
|
148
|
+
conversation and is untrusted by construction, so put checkable facts there and directives in the
|
|
149
|
+
system lane. The instructions lane reaches the model only through the managed `streamText` (or a
|
|
150
|
+
`chat.toStreamTextOptions()` spread), since that is where the SDK can set instructions.
|
|
151
|
+
|
|
146
152
|
```ts
|
|
147
153
|
export const myChat = chat.agent({
|
|
148
154
|
id: "my-chat",
|
|
@@ -154,8 +160,9 @@ export const myChat = chat.agent({
|
|
|
154
160
|
})()
|
|
155
161
|
);
|
|
156
162
|
},
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
registry,
|
|
164
|
+
run: async ({ messages, signal, streamText }) =>
|
|
165
|
+
streamText({ messages, abortSignal: signal, stopWhen: stepCountIs(15) }),
|
|
159
166
|
});
|
|
160
167
|
```
|
|
161
168
|
|
|
@@ -163,8 +170,9 @@ export const myChat = chat.agent({
|
|
|
163
170
|
|
|
164
171
|
`compaction.shouldCompact` decides when, `summarize` produces the summary that replaces the model
|
|
165
172
|
messages. UI messages are preserved by default (customize via `compactUIMessages`). The `prepareStep`
|
|
166
|
-
that performs inner-loop compaction
|
|
167
|
-
you pass after
|
|
173
|
+
that performs inner-loop compaction rides the managed `streamText`, which composes a `prepareStep`
|
|
174
|
+
you pass after it. Spreading `chat.toStreamTextOptions()` and then passing your own replaces it,
|
|
175
|
+
switching compaction off.
|
|
168
176
|
|
|
169
177
|
```ts
|
|
170
178
|
compaction: {
|
|
@@ -177,29 +185,53 @@ compaction: {
|
|
|
177
185
|
},
|
|
178
186
|
```
|
|
179
187
|
|
|
180
|
-
### 5. Actions:
|
|
181
|
-
|
|
182
|
-
`actionSchema` validates; `onAction`
|
|
183
|
-
`remove`, `getPendingToolCalls`, `extractNewToolResults`).
|
|
184
|
-
`onAction` only
|
|
185
|
-
to
|
|
188
|
+
### 5. Actions: edit state, and optionally answer
|
|
189
|
+
|
|
190
|
+
`actionSchema` validates; `onAction` edits via `chat.history` (`slice`, `replace`, `rollbackTo`,
|
|
191
|
+
`remove`, `getPendingToolCalls`, `extractNewToolResults`). An action fires `hydrateMessages` and
|
|
192
|
+
`onAction` only. Return nothing for an edit-only action: no model call, and the turn counter does not
|
|
193
|
+
advance. Return `chat.turn()` to answer after the edit: a turn runs on the edited history with
|
|
194
|
+
everything a turn has (the agent's system prompt and tools, steering, compaction, injected
|
|
195
|
+
instructions, `onTurnStart` and `onTurnComplete`, persistence), and `run()` receives it with
|
|
196
|
+
`trigger: "action-turn"`. `onAction` has no `streamText` argument, and returning a
|
|
197
|
+
`StreamTextResult`, string or `UIMessage` from it throws.
|
|
198
|
+
|
|
199
|
+
Persistence splits by model. With transcript storage (`storage` on `chat.agent`; the platform
|
|
200
|
+
snapshot by default), the runtime hands storage a changeset with `reason: "action"` after an action
|
|
201
|
+
that changed the conversation: an undo is one `truncateAfter`, a regenerate is a `truncateAfter`
|
|
202
|
+
followed by the new answer's `put` when the turn completes, and an edit is a `put` for the edited
|
|
203
|
+
id. With the deprecated `hydrateMessages` your store is the source of truth and the runtime does
|
|
204
|
+
not write, so mirror every mutation yourself: a regenerate is a delete and an insert, and the
|
|
205
|
+
answer that follows `chat.turn()` arrives through `onTurnComplete` like any turn's answer.
|
|
186
206
|
|
|
187
207
|
```ts
|
|
188
208
|
export const myChat = chat.agent({
|
|
189
209
|
id: "my-chat",
|
|
190
210
|
actionSchema: z.discriminatedUnion("type", [
|
|
191
211
|
z.object({ type: z.literal("undo") }),
|
|
212
|
+
z.object({ type: z.literal("regenerate") }),
|
|
192
213
|
z.object({ type: z.literal("rollback"), targetMessageId: z.string() }),
|
|
193
214
|
]),
|
|
194
215
|
onAction: async ({ action }) => {
|
|
195
|
-
if (action.type === "undo") chat.history.slice(0, -2);
|
|
216
|
+
if (action.type === "undo") chat.history.slice(0, -2); // edit only
|
|
196
217
|
if (action.type === "rollback") chat.history.rollbackTo(action.targetMessageId);
|
|
218
|
+
if (action.type === "regenerate") {
|
|
219
|
+
chat.history.slice(0, -1);
|
|
220
|
+
return chat.turn(); // answer the edited history
|
|
221
|
+
}
|
|
197
222
|
},
|
|
198
|
-
run: async ({ messages, signal }) =>
|
|
223
|
+
run: async ({ messages, signal, streamText }) =>
|
|
224
|
+
streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal }),
|
|
199
225
|
});
|
|
200
226
|
```
|
|
201
227
|
|
|
202
|
-
Send from the browser
|
|
228
|
+
Send from the browser through `useChat`, so the answer a turn produces renders like any turn:
|
|
229
|
+
`sendMessage(undefined, { body: { action: { type: "regenerate" } } })`, `regenerate({ body: { action } })`,
|
|
230
|
+
or `useChatActions({ sendMessage })` from `@trigger.dev/sdk/chat/react`. For regeneration, use
|
|
231
|
+
`regenerate()` from `useChat` (Vercel AI SDK) which removes the last assistant message before
|
|
232
|
+
streaming the new one; calling `sendMessage` with a regenerate action appends without removal,
|
|
233
|
+
leaving both answers visible. `transport.sendAction(chatId, action)` returns a raw stream the
|
|
234
|
+
caller must read and apply itself. Server-side, use
|
|
203
235
|
`agentChat.sendAction({ type: "rollback", targetMessageId: "msg-3" })`.
|
|
204
236
|
|
|
205
237
|
### 6. Fast starts: Head Start
|
|
@@ -210,18 +242,19 @@ must be **schema-only** (a module importing `ai` + `zod` only); heavy executes s
|
|
|
210
242
|
|
|
211
243
|
```ts
|
|
212
244
|
import { chat } from "@trigger.dev/sdk/chat-server";
|
|
213
|
-
import { streamText, stepCountIs } from "ai";
|
|
214
245
|
import { anthropic } from "@ai-sdk/anthropic";
|
|
215
246
|
import { headStartTools } from "@/lib/chat-tools/schemas";
|
|
216
247
|
|
|
217
248
|
export const chatHandler = chat.headStart({
|
|
218
249
|
agentId: "my-chat",
|
|
219
|
-
run
|
|
250
|
+
// `streamText` from the run argument owns `messages`, `prompt`, `stopWhen`
|
|
251
|
+
// and `abortSignal`: the handover needs `stopWhen: stepCountIs(1)` so the agent,
|
|
252
|
+
// not this handler, runs step 2 onward. Passing any of them is a type error.
|
|
253
|
+
run: async ({ streamText }) =>
|
|
220
254
|
streamText({
|
|
221
|
-
...helper.toStreamTextOptions({ tools: headStartTools }),
|
|
222
255
|
model: anthropic("claude-sonnet-4-6"),
|
|
223
256
|
system: "You are helpful.",
|
|
224
|
-
|
|
257
|
+
tools: headStartTools,
|
|
225
258
|
}),
|
|
226
259
|
});
|
|
227
260
|
// Next.js: export const POST = chatHandler; Transport: headStart: "/api/chat"
|
|
@@ -248,8 +281,10 @@ export const myChat = chat.agent({
|
|
|
248
281
|
### 8. Pending messages (mid-stream user input)
|
|
249
282
|
|
|
250
283
|
A message sent while a turn is streaming should NOT cancel the stream. Configure
|
|
251
|
-
`pendingMessages` (`shouldInject`, `prepare`, `onReceived`, `onInjected`) on the agent so the
|
|
252
|
-
|
|
284
|
+
`pendingMessages` (`shouldInject`, `prepare`, `onReceived`, `onInjected`) on the agent so the managed
|
|
285
|
+
`streamText`'s `prepareStep` folds them in at the next boundary. An injected steering message is part
|
|
286
|
+
of the conversation your hooks see, so it arrives in `uiMessages` and `newUIMessages` at
|
|
287
|
+
`onTurnComplete` and an app persisting from there stores it without extra work. On the frontend, `usePendingMessages`
|
|
253
288
|
returns `pending`, `steer(text)`, `queue(text)`, and `promoteToSteering(id)`; send via
|
|
254
289
|
`transport.sendPendingMessage(chatId, uiMessage, metadata?)`.
|
|
255
290
|
|