@trigger.dev/sdk 4.5.15 → 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.
Files changed (159) hide show
  1. package/dist/commonjs/imports/ai-runtime-cjs.cjs.map +1 -1
  2. package/dist/commonjs/imports/ai-runtime.js +2 -0
  3. package/dist/commonjs/v3/ai-shared.d.ts +15 -0
  4. package/dist/commonjs/v3/ai-shared.js +35 -1
  5. package/dist/commonjs/v3/ai-shared.js.map +1 -1
  6. package/dist/commonjs/v3/ai.d.ts +311 -25
  7. package/dist/commonjs/v3/ai.js +1633 -380
  8. package/dist/commonjs/v3/ai.js.map +1 -1
  9. package/dist/commonjs/v3/auth.d.ts +10 -4
  10. package/dist/commonjs/v3/auth.js.map +1 -1
  11. package/dist/commonjs/v3/chat-client.d.ts +11 -2
  12. package/dist/commonjs/v3/chat-client.js +227 -111
  13. package/dist/commonjs/v3/chat-client.js.map +1 -1
  14. package/dist/commonjs/v3/chat-react.d.ts +88 -0
  15. package/dist/commonjs/v3/chat-react.js +130 -1
  16. package/dist/commonjs/v3/chat-react.js.map +1 -1
  17. package/dist/commonjs/v3/chat-server.d.ts +42 -6
  18. package/dist/commonjs/v3/chat-server.js +52 -7
  19. package/dist/commonjs/v3/chat-server.js.map +1 -1
  20. package/dist/commonjs/v3/chat.d.ts +92 -10
  21. package/dist/commonjs/v3/chat.js +328 -73
  22. package/dist/commonjs/v3/chat.js.map +1 -1
  23. package/dist/commonjs/v3/chatSnapshotIo.d.ts +2 -0
  24. package/dist/commonjs/v3/chatSnapshotIo.js +173 -0
  25. package/dist/commonjs/v3/chatSnapshotIo.js.map +1 -0
  26. package/dist/commonjs/v3/chatVersionSkew.d.ts +12 -0
  27. package/dist/commonjs/v3/chatVersionSkew.js +30 -0
  28. package/dist/commonjs/v3/chatVersionSkew.js.map +1 -0
  29. package/dist/commonjs/v3/externalDeploymentId.d.ts +23 -0
  30. package/dist/commonjs/v3/externalDeploymentId.js +43 -0
  31. package/dist/commonjs/v3/externalDeploymentId.js.map +1 -0
  32. package/dist/commonjs/v3/retry.d.ts +4 -0
  33. package/dist/commonjs/v3/retry.js +22 -13
  34. package/dist/commonjs/v3/retry.js.map +1 -1
  35. package/dist/commonjs/v3/sessions.d.ts +15 -2
  36. package/dist/commonjs/v3/sessions.js +12 -1
  37. package/dist/commonjs/v3/sessions.js.map +1 -1
  38. package/dist/commonjs/v3/shared.js +30 -36
  39. package/dist/commonjs/v3/shared.js.map +1 -1
  40. package/dist/commonjs/v3/test/index.d.ts +1 -0
  41. package/dist/commonjs/v3/test/index.js +3 -1
  42. package/dist/commonjs/v3/test/index.js.map +1 -1
  43. package/dist/commonjs/v3/test/mock-chat-agent.d.ts +47 -4
  44. package/dist/commonjs/v3/test/mock-chat-agent.js +116 -10
  45. package/dist/commonjs/v3/test/mock-chat-agent.js.map +1 -1
  46. package/dist/commonjs/v3/test/test-session-handle.js +6 -0
  47. package/dist/commonjs/v3/test/test-session-handle.js.map +1 -1
  48. package/dist/commonjs/v3/test/transcript-storage-tests.d.ts +40 -0
  49. package/dist/commonjs/v3/test/transcript-storage-tests.js +258 -0
  50. package/dist/commonjs/v3/test/transcript-storage-tests.js.map +1 -0
  51. package/dist/commonjs/v3/transcriptStorage.d.ts +261 -0
  52. package/dist/commonjs/v3/transcriptStorage.js +374 -0
  53. package/dist/commonjs/v3/transcriptStorage.js.map +1 -0
  54. package/dist/commonjs/version.js +1 -1
  55. package/dist/esm/imports/ai-runtime.d.ts +2 -2
  56. package/dist/esm/imports/ai-runtime.js +2 -2
  57. package/dist/esm/imports/ai-runtime.js.map +1 -1
  58. package/dist/esm/v3/ai-shared.d.ts +15 -0
  59. package/dist/esm/v3/ai-shared.js +33 -0
  60. package/dist/esm/v3/ai-shared.js.map +1 -1
  61. package/dist/esm/v3/ai.d.ts +311 -25
  62. package/dist/esm/v3/ai.js +1625 -377
  63. package/dist/esm/v3/ai.js.map +1 -1
  64. package/dist/esm/v3/auth.d.ts +10 -4
  65. package/dist/esm/v3/auth.js.map +1 -1
  66. package/dist/esm/v3/chat-client.d.ts +11 -2
  67. package/dist/esm/v3/chat-client.js +228 -112
  68. package/dist/esm/v3/chat-client.js.map +1 -1
  69. package/dist/esm/v3/chat-react.d.ts +88 -0
  70. package/dist/esm/v3/chat-react.js +127 -1
  71. package/dist/esm/v3/chat-react.js.map +1 -1
  72. package/dist/esm/v3/chat-server.d.ts +42 -6
  73. package/dist/esm/v3/chat-server.js +53 -8
  74. package/dist/esm/v3/chat-server.js.map +1 -1
  75. package/dist/esm/v3/chat.d.ts +92 -10
  76. package/dist/esm/v3/chat.js +329 -74
  77. package/dist/esm/v3/chat.js.map +1 -1
  78. package/dist/esm/v3/chatSnapshotIo.d.ts +2 -0
  79. package/dist/esm/v3/chatSnapshotIo.js +165 -0
  80. package/dist/esm/v3/chatSnapshotIo.js.map +1 -0
  81. package/dist/esm/v3/chatVersionSkew.d.ts +12 -0
  82. package/dist/esm/v3/chatVersionSkew.js +27 -0
  83. package/dist/esm/v3/chatVersionSkew.js.map +1 -0
  84. package/dist/esm/v3/externalDeploymentId.d.ts +23 -0
  85. package/dist/esm/v3/externalDeploymentId.js +38 -0
  86. package/dist/esm/v3/externalDeploymentId.js.map +1 -0
  87. package/dist/esm/v3/retry.d.ts +4 -0
  88. package/dist/esm/v3/retry.js +14 -8
  89. package/dist/esm/v3/retry.js.map +1 -1
  90. package/dist/esm/v3/sessions.d.ts +15 -2
  91. package/dist/esm/v3/sessions.js +11 -1
  92. package/dist/esm/v3/sessions.js.map +1 -1
  93. package/dist/esm/v3/shared.js +17 -23
  94. package/dist/esm/v3/shared.js.map +1 -1
  95. package/dist/esm/v3/test/index.d.ts +1 -0
  96. package/dist/esm/v3/test/index.js +1 -0
  97. package/dist/esm/v3/test/index.js.map +1 -1
  98. package/dist/esm/v3/test/mock-chat-agent.d.ts +47 -4
  99. package/dist/esm/v3/test/mock-chat-agent.js +118 -12
  100. package/dist/esm/v3/test/mock-chat-agent.js.map +1 -1
  101. package/dist/esm/v3/test/test-session-handle.js +6 -0
  102. package/dist/esm/v3/test/test-session-handle.js.map +1 -1
  103. package/dist/esm/v3/test/transcript-storage-tests.d.ts +40 -0
  104. package/dist/esm/v3/test/transcript-storage-tests.js +255 -0
  105. package/dist/esm/v3/test/transcript-storage-tests.js.map +1 -0
  106. package/dist/esm/v3/transcriptStorage.d.ts +261 -0
  107. package/dist/esm/v3/transcriptStorage.js +360 -0
  108. package/dist/esm/v3/transcriptStorage.js.map +1 -0
  109. package/dist/esm/version.js +1 -1
  110. package/docs/ai-chat/actions.mdx +55 -23
  111. package/docs/ai-chat/anatomy.mdx +47 -27
  112. package/docs/ai-chat/backend.mdx +125 -48
  113. package/docs/ai-chat/background-injection.mdx +122 -19
  114. package/docs/ai-chat/client-protocol.mdx +5 -4
  115. package/docs/ai-chat/compaction.mdx +13 -7
  116. package/docs/ai-chat/custom-agents.mdx +23 -0
  117. package/docs/ai-chat/fast-starts.mdx +27 -20
  118. package/docs/ai-chat/frontend.mdx +19 -14
  119. package/docs/ai-chat/how-it-works.mdx +1 -1
  120. package/docs/ai-chat/lifecycle-hooks.mdx +6 -2
  121. package/docs/ai-chat/migrating-from-a-route-handler.mdx +16 -14
  122. package/docs/ai-chat/migrating-from-hydrate-messages.mdx +299 -0
  123. package/docs/ai-chat/patterns/database-persistence.mdx +24 -9
  124. package/docs/ai-chat/patterns/native-compaction.mdx +310 -0
  125. package/docs/ai-chat/patterns/persistence-and-replay.mdx +34 -23
  126. package/docs/ai-chat/patterns/skills.mdx +7 -10
  127. package/docs/ai-chat/patterns/version-upgrades.mdx +79 -6
  128. package/docs/ai-chat/pending-messages.mdx +3 -3
  129. package/docs/ai-chat/prompt-caching.mdx +23 -25
  130. package/docs/ai-chat/quick-start.mdx +11 -11
  131. package/docs/ai-chat/reference.mdx +69 -10
  132. package/docs/ai-chat/sessions.mdx +6 -1
  133. package/docs/ai-chat/side-channels.mdx +1 -1
  134. package/docs/ai-chat/testing.mdx +2 -1
  135. package/docs/ai-chat/tools.mdx +18 -13
  136. package/docs/ai-chat/transcript-storage.mdx +247 -0
  137. package/docs/ai-chat/upgrade-guide.mdx +2 -2
  138. package/docs/apikeys.mdx +27 -45
  139. package/docs/config/extensions/custom.mdx +14 -1
  140. package/docs/config/extensions/syncEnvVars.mdx +6 -0
  141. package/docs/deploy-environment-variables.mdx +23 -2
  142. package/docs/deployment/overview.mdx +4 -8
  143. package/docs/deployment/preview-branches.mdx +4 -4
  144. package/docs/deployment/version-skew-protection.mdx +62 -0
  145. package/docs/github-integration.mdx +1 -1
  146. package/docs/introduction.mdx +58 -152
  147. package/docs/manual-setup.mdx +7 -7
  148. package/docs/mcp-tools.mdx +9 -0
  149. package/docs/quick-start.mdx +3 -3
  150. package/docs/realtime/auth.mdx +1 -1
  151. package/docs/reports.mdx +157 -0
  152. package/docs/self-hosting/security.mdx +17 -0
  153. package/docs/tasks/scheduled.mdx +24 -0
  154. package/docs/triggering.mdx +1 -1
  155. package/docs/troubleshooting-zod.mdx +158 -0
  156. package/docs/troubleshooting.mdx +1 -1
  157. package/package.json +4 -4
  158. package/skills/trigger-authoring-chat-agent/SKILL.md +41 -29
  159. package/skills/trigger-chat-agent-advanced/SKILL.md +54 -19
@@ -0,0 +1,360 @@
1
+ import { apiClientManager, pageTranscriptEntries, } from "@trigger.dev/core/v3";
2
+ import { readChatSnapshot, writeChatSnapshot } from "./chatSnapshotIo.js";
3
+ export function emptyTranscriptState() {
4
+ return { entries: [], state: null };
5
+ }
6
+ /**
7
+ * Apply changes to a transcript, in order. Pure: returns a new state and
8
+ * never mutates the input. Replaying the same changes converges, which is
9
+ * what lets a storage retry a failed write without checking what landed.
10
+ */
11
+ export function reduceTranscriptChanges(prev, changes) {
12
+ let entries = prev.entries.slice();
13
+ let state = prev.state;
14
+ for (const change of changes) {
15
+ switch (change.op) {
16
+ case "put": {
17
+ const message = change.message;
18
+ const entry = { id: message.id, final: change.final ?? true, message };
19
+ const idx = entries.findIndex((e) => e.id === message.id);
20
+ if (idx === -1)
21
+ entries.push(entry);
22
+ else
23
+ entries[idx] = entry;
24
+ break;
25
+ }
26
+ case "remove": {
27
+ entries = entries.filter((e) => e.id !== change.id);
28
+ break;
29
+ }
30
+ case "truncateAfter": {
31
+ const idx = entries.findIndex((e) => e.id === change.afterId);
32
+ if (idx !== -1)
33
+ entries = entries.slice(0, idx + 1);
34
+ break;
35
+ }
36
+ case "state": {
37
+ state = change.value ?? null;
38
+ break;
39
+ }
40
+ }
41
+ }
42
+ return { entries, state };
43
+ }
44
+ /**
45
+ * The content identity the transcript shadow compares messages by. @internal
46
+ */
47
+ export function fingerprintMessage(message) {
48
+ return JSON.stringify(message);
49
+ }
50
+ /**
51
+ * Build the shadow for `messages`. An id is non-final when this save says so
52
+ * (`nonFinalIds`) or when it was non-final before and its content has not
53
+ * changed: a partial answer stays partial until the message itself changes.
54
+ */
55
+ export function createTranscriptShadow(messages, nonFinalIds, previous) {
56
+ const ids = [];
57
+ const fingerprints = new Map();
58
+ const nonFinal = new Set();
59
+ for (const message of messages) {
60
+ const fingerprint = fingerprintMessage(message);
61
+ ids.push(message.id);
62
+ fingerprints.set(message.id, fingerprint);
63
+ if (nonFinalIds?.has(message.id) ||
64
+ (previous?.nonFinal.has(message.id) && previous.fingerprints.get(message.id) === fingerprint)) {
65
+ nonFinal.add(message.id);
66
+ }
67
+ }
68
+ return { ids, fingerprints, nonFinal };
69
+ }
70
+ /**
71
+ * Derive the changes that take `shadow` to `next`.
72
+ *
73
+ * The common prefix (by id) is compared message by message and a changed
74
+ * message becomes an in-place `put`. Past the prefix, everything the shadow
75
+ * still had is dropped with one `truncateAfter` on the last common id (or
76
+ * one `remove` per message when there is no common prefix) and everything
77
+ * `next` has is appended with `put`, in order. Applying the result with
78
+ * {@link reduceTranscriptChanges} reproduces `next` exactly, including
79
+ * order, for any two lists.
80
+ */
81
+ export function diffTranscript(shadow, next, options = {}) {
82
+ const nextShadow = createTranscriptShadow(next, options.nonFinalIds, shadow);
83
+ const changes = [];
84
+ const put = (message) => {
85
+ const final = nextShadow.nonFinal.has(message.id) ? false : undefined;
86
+ changes.push(final === undefined ? { op: "put", message } : { op: "put", message, final });
87
+ };
88
+ let lcp = 0;
89
+ while (lcp < shadow.ids.length && lcp < next.length && shadow.ids[lcp] === next[lcp].id) {
90
+ lcp++;
91
+ }
92
+ for (let i = 0; i < lcp; i++) {
93
+ const message = next[i];
94
+ if (shadow.fingerprints.get(message.id) !== nextShadow.fingerprints.get(message.id) ||
95
+ shadow.nonFinal.has(message.id) !== nextShadow.nonFinal.has(message.id)) {
96
+ put(message);
97
+ }
98
+ }
99
+ if (shadow.ids.length > lcp) {
100
+ if (lcp > 0) {
101
+ changes.push({ op: "truncateAfter", afterId: shadow.ids[lcp - 1] });
102
+ }
103
+ else {
104
+ for (const id of shadow.ids)
105
+ changes.push({ op: "remove", id });
106
+ }
107
+ }
108
+ for (let i = lcp; i < next.length; i++) {
109
+ put(next[i]);
110
+ }
111
+ return { changes, shadow: nextShadow };
112
+ }
113
+ /**
114
+ * Roughly how many messages the snapshot keeps once a conversation has
115
+ * compacted. Raising it costs bytes on every save; lowering it shortens how far
116
+ * back a conversation can be rendered from the platform's own store.
117
+ */
118
+ const SNAPSHOT_KEEP_MESSAGES = 100;
119
+ /**
120
+ * The built-in storage: the transcript in the platform's object store, read by
121
+ * the Sessions dashboard and the transcript API.
122
+ *
123
+ * One object per conversation, rewritten each turn, holding the transcript plus
124
+ * the runtime state. Once a conversation has been compacted, it keeps roughly
125
+ * the last hundred messages rather than all of them, so the object stops
126
+ * growing with the conversation. An app that needs the full history keeps its
127
+ * own transcript storage.
128
+ */
129
+ export function snapshotTranscriptStorage() {
130
+ return {
131
+ async load(scope, opts) {
132
+ if (opts?.limit !== undefined || opts?.before !== undefined) {
133
+ const page = await readTranscriptPage(scope.chatId, opts);
134
+ if (page)
135
+ return page;
136
+ }
137
+ const snapshot = await readChatSnapshot(scope.chatId);
138
+ const full = snapshot
139
+ ? { entries: snapshot.messages, state: snapshot.state }
140
+ : emptyTranscriptState();
141
+ return {
142
+ ...pageEntries(full.entries, opts),
143
+ state: full.state,
144
+ cursors: snapshot
145
+ ? { lastOutEventId: snapshot.lastOutEventId, lastInEventId: snapshot.lastInEventId }
146
+ : undefined,
147
+ };
148
+ },
149
+ async save(ctx, changeset) {
150
+ const entries = trimTranscriptForSnapshot(changeset.transcript.entries, changeset.transcript.state, { keep: SNAPSHOT_KEEP_MESSAGES });
151
+ await writeChatSnapshot(ctx.chatId, {
152
+ version: 2,
153
+ savedAt: Date.now(),
154
+ messages: entries,
155
+ state: normalizeRuntimeStateForWindow(changeset.transcript.state, entries.map((entry) => entry.id)),
156
+ lastOutEventId: changeset.cursors?.lastOutEventId,
157
+ lastInEventId: changeset.cursors?.lastInEventId,
158
+ });
159
+ },
160
+ };
161
+ }
162
+ /**
163
+ * A page of the platform transcript read server-side, for a render read with
164
+ * `limit`/`before` from an app server holding a secret key. `undefined` when
165
+ * the call is not possible from here (a run's public token cannot use the
166
+ * endpoint), so the caller falls back to reading the whole blob.
167
+ *
168
+ * Carries no runtime `state`: the endpoint serves the transcript only, so a
169
+ * paged result always reports `state: null`. The model lane is restored from
170
+ * the whole-blob read instead, which is reachable only with private auth.
171
+ */
172
+ async function readTranscriptPage(chatId, opts) {
173
+ try {
174
+ const page = await apiClientManager.clientOrThrow().getSessionTranscript(chatId, opts);
175
+ return {
176
+ messages: page.messages,
177
+ state: null,
178
+ cursors: page.cursors,
179
+ nextCursor: page.nextCursor,
180
+ };
181
+ }
182
+ catch {
183
+ return undefined;
184
+ }
185
+ }
186
+ /** The storage `chat.agent` uses when none is configured: {@link snapshotTranscriptStorage}. */
187
+ export const defaultStorage = snapshotTranscriptStorage();
188
+ function pageEntries(all, opts) {
189
+ const page = pageTranscriptEntries(all, opts);
190
+ return {
191
+ messages: page.entries.map((e) => e.message),
192
+ nextCursor: page.nextCursor,
193
+ nonFinalIds: page.entries.filter((e) => !e.final).map((e) => e.id),
194
+ };
195
+ }
196
+ /**
197
+ * A storage that keeps every transcript in process memory. The reference
198
+ * implementation for the conformance tests, and a way to inspect exactly
199
+ * what the runtime hands a storage.
200
+ */
201
+ export function memoryTranscriptStorage() {
202
+ const transcripts = new Map();
203
+ const changesets = [];
204
+ return {
205
+ changesets,
206
+ transcript(chatId) {
207
+ return transcripts.get(chatId);
208
+ },
209
+ async load(scope, opts) {
210
+ const stored = transcripts.get(scope.chatId);
211
+ if (!stored)
212
+ return { messages: [], state: null, cursors: undefined, nextCursor: undefined };
213
+ return {
214
+ ...pageEntries(stored.entries, opts),
215
+ state: stored.state,
216
+ cursors: stored.cursors,
217
+ };
218
+ },
219
+ async save(ctx, changeset) {
220
+ changesets.push({ ctx, changeset });
221
+ const prev = transcripts.get(ctx.chatId);
222
+ const next = reduceTranscriptChanges(prev ?? emptyTranscriptState(), changeset.changes);
223
+ transcripts.set(ctx.chatId, { ...next, cursors: changeset.cursors ?? prev?.cursors });
224
+ },
225
+ };
226
+ }
227
+ /**
228
+ * The slice of a transcript worth storing, which is the last `keep` messages
229
+ * once a conversation has compacted, and all of it before that.
230
+ *
231
+ * Expressed relative to the compaction watermark rather than as a plain tail,
232
+ * because everything after the watermark is live context that the next boot
233
+ * converts and must never be dropped. The runtime stamps the watermark at the
234
+ * newest message on each compacted save, so in practice the two coincide and
235
+ * this keeps the last `keep`; the watermark-relative form is what keeps it
236
+ * correct if a watermark ever lands earlier in the transcript.
237
+ *
238
+ * Without a watermark nothing is dropped: every entry is still context.
239
+ *
240
+ * This is what stops the object rewritten each turn from growing with the
241
+ * conversation. An app that needs the whole history keeps its own transcript
242
+ * storage.
243
+ */
244
+ export function trimTranscriptForSnapshot(entries, state, opts) {
245
+ const throughId = parseTranscriptRuntimeState(state)?.compaction?.throughId;
246
+ if (throughId === undefined || throughId === "")
247
+ return entries;
248
+ const idx = entries.findIndex((entry) => entry.id === throughId);
249
+ if (idx === -1)
250
+ return entries;
251
+ // Keep the watermark entry itself, so a restored lane can still find it.
252
+ const start = Math.max(0, idx + 1 - Math.max(1, opts.keep));
253
+ return start === 0 ? entries : entries.slice(start);
254
+ }
255
+ /**
256
+ * Rewrite runtime state so it still describes the transcript window being
257
+ * persisted.
258
+ *
259
+ * A compaction watermark that has moved out of the window becomes `""`, which
260
+ * is how a restored lane says "the compacted messages cover everything before
261
+ * this window". Left as-is, the watermark would name an entry the window does
262
+ * not contain, and restoring the lane would discard the compacted messages and
263
+ * re-convert the window from scratch — an agent that has silently forgotten the
264
+ * conversation. Injections anchored outside the window go too: the span they
265
+ * belonged to is already inside the compacted lane.
266
+ */
267
+ export function normalizeRuntimeStateForWindow(state, windowIds) {
268
+ const runtimeState = parseTranscriptRuntimeState(state);
269
+ if (!runtimeState)
270
+ return state;
271
+ const ids = new Set(windowIds);
272
+ const compaction = runtimeState.compaction;
273
+ const watermarkOutside = compaction !== undefined && compaction.throughId !== "" && !ids.has(compaction.throughId);
274
+ const injections = runtimeState.injections?.filter((injection) => injection.afterId === "" || ids.has(injection.afterId));
275
+ if (!watermarkOutside && injections?.length === runtimeState.injections?.length) {
276
+ return state;
277
+ }
278
+ return {
279
+ ...runtimeState,
280
+ ...(compaction
281
+ ? {
282
+ compaction: {
283
+ modelMessages: compaction.modelMessages,
284
+ throughId: watermarkOutside ? "" : compaction.throughId,
285
+ },
286
+ }
287
+ : {}),
288
+ ...(injections ? { injections } : {}),
289
+ };
290
+ }
291
+ export function parseTranscriptRuntimeState(value) {
292
+ if (!value || typeof value !== "object")
293
+ return undefined;
294
+ const record = value;
295
+ if (record.v !== 1)
296
+ return undefined;
297
+ const out = { v: 1 };
298
+ const compaction = record.compaction;
299
+ if (compaction &&
300
+ typeof compaction === "object" &&
301
+ Array.isArray(compaction.modelMessages) &&
302
+ typeof compaction.throughId === "string") {
303
+ out.compaction = {
304
+ modelMessages: compaction.modelMessages,
305
+ throughId: compaction.throughId,
306
+ };
307
+ }
308
+ if (Array.isArray(record.injections)) {
309
+ out.injections = record.injections.flatMap((entry) => {
310
+ const inj = entry;
311
+ return inj && typeof inj.afterId === "string" && Array.isArray(inj.messages)
312
+ ? [{ afterId: inj.afterId, messages: inj.messages }]
313
+ : [];
314
+ });
315
+ }
316
+ if (Array.isArray(record.queued) && record.queued.length > 0) {
317
+ out.queued = record.queued;
318
+ }
319
+ return out;
320
+ }
321
+ /**
322
+ * Rebuild the model lane for a transcript at boot. Uses the persisted
323
+ * compacted lane while the transcript still contains its `throughId`
324
+ * boundary, then converts the rest of the transcript, re-inserting persisted
325
+ * injections after the messages they followed.
326
+ */
327
+ export async function restoreModelLane(messages, state, convert) {
328
+ const lane = [];
329
+ let start = 0;
330
+ let compacted = false;
331
+ if (state?.compaction) {
332
+ const throughId = state.compaction.throughId;
333
+ const idx = throughId === "" ? -1 : messages.findIndex((m) => m.id === throughId);
334
+ if (idx !== -1 || throughId === "") {
335
+ lane.push(...state.compaction.modelMessages);
336
+ start = idx + 1;
337
+ compacted = true;
338
+ }
339
+ }
340
+ const anchored = (state?.injections ?? [])
341
+ .map((inj) => ({
342
+ inj,
343
+ idx: inj.afterId === "" ? -1 : messages.findIndex((m) => m.id === inj.afterId),
344
+ }))
345
+ .filter(({ inj, idx }) => (inj.afterId === "" ? start === 0 : idx >= start))
346
+ .sort((a, b) => a.idx - b.idx);
347
+ let cursor = start;
348
+ for (const { inj, idx } of anchored) {
349
+ if (idx + 1 > cursor) {
350
+ lane.push(...(await convert(messages.slice(cursor, idx + 1))));
351
+ cursor = idx + 1;
352
+ }
353
+ lane.push(...inj.messages);
354
+ }
355
+ if (cursor < messages.length) {
356
+ lane.push(...(await convert(messages.slice(cursor))));
357
+ }
358
+ return { messages: lane, compacted, injections: anchored.map(({ inj }) => inj) };
359
+ }
360
+ //# sourceMappingURL=transcriptStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcriptStorage.js","sourceRoot":"","sources":["../../../src/v3/transcriptStorage.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export const VERSION = "4.5.15";
1
+ export const VERSION = "4.6.0";
2
2
  //# sourceMappingURL=version.js.map
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: "Actions"
3
3
  sidebarTitle: "Actions"
4
- description: "Custom commands sent from the frontend that mutate chat state without consuming a turn undo, rollback, edit, regenerate."
4
+ description: "Custom commands sent from the frontend that mutate chat state without consuming a turn: undo, rollback, edit, regenerate."
5
5
  ---
6
6
 
7
7
  ## Overview
@@ -44,7 +44,7 @@ export const myChat = chat.agent({
44
44
  // returning void → side-effect-only, no model call
45
45
  },
46
46
 
47
- run: async ({ messages, signal }) => {
47
+ run: async ({ messages, signal, streamText }) => {
48
48
  return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
49
49
  },
50
50
  });
@@ -52,36 +52,68 @@ export const myChat = chat.agent({
52
52
 
53
53
  **Lifecycle flow:** Wake → parse action against `actionSchema` → `hydrateMessages` (if set) → **`onAction`** → apply `chat.history` mutations → emit `trigger:turn-complete` → wait for next message.
54
54
 
55
- ## Returning a model response from an action
55
+ When `onAction` returns `chat.turn()`, the flow continues instead of emitting `trigger:turn-complete`: the edit is snapshotted, then a turn runs on the edited history with `trigger: "action-turn"`, so `onTurnStart`, `run()`, `onBeforeTurnComplete` and `onTurnComplete` all fire and the answer is persisted like any turn's. See [Answering after an action](#answering-after-an-action).
56
56
 
57
- `onAction` can return a `StreamTextResult`, `string`, or `UIMessage` to produce a response. The returned stream is auto-piped to the frontend just like a normal turn, but the rest of the turn machinery (`onTurnStart`, `onTurnComplete`, etc.) still does not fire.
57
+ ## Answering after an action
58
+
59
+ An action is a state edit. To answer after the edit, return `chat.turn()`: the edit is applied and snapshotted, then a turn runs on the edited history exactly as a message turn does. `onTurnStart`, `run()`, `onBeforeTurnComplete` and `onTurnComplete` fire, the turn counter advances, and the answer gets everything a turn has: the agent's system prompt and tools, steering, compaction, injected instructions and persistence.
58
60
 
59
61
  ```ts
60
- onAction: async ({ action, messages }) => {
61
- if (action.type === "regenerate") {
62
- chat.history.slice(0, -1); // drop the last assistant
63
- return streamText({
64
- model: anthropic("claude-sonnet-4-5"),
65
- messages,
66
- stopWhen: stepCountIs(15),
67
- });
62
+ onAction: async ({ action }) => {
63
+ switch (action.type) {
64
+ case "undo":
65
+ chat.history.slice(0, -2);
66
+ return; // edit only, no turn
67
+
68
+ case "regenerate":
69
+ chat.history.slice(0, -1);
70
+ return chat.turn(); // answer the edited history
71
+
72
+ case "retry-formal":
73
+ chat.history.slice(0, -1);
74
+ chat.inject([{ role: "system", content: "Answer formally this time." }]);
75
+ return chat.turn(); // with a one-shot instruction
68
76
  }
69
- // other actions return void → side-effect only
70
77
  }
71
78
  ```
72
79
 
73
- This is useful for actions that both mutate state and want a fresh model response (regenerate-from-here, retry-with-different-style). Persistence is your responsibility inside `onAction` itself; you have access to the streamed response object.
80
+ `run()` receives the edited history with no incoming user message, the same shape as a `regenerate-message` turn, and its `trigger` is `"action-turn"`, so a `run()` that returns early on `"action"` (the pre-May behaviour, when actions invoked `run()` directly) still answers. Returning anything other than `chat.turn()` or nothing is an error; a response can no longer be returned from `onAction` directly.
81
+
82
+ ### Actions and persistence
83
+
84
+ An action that returns nothing does not fire `onTurnComplete`, and that is where an app that owns its own transcript normally writes. What that means depends on which persistence model you use.
85
+
86
+ **Transcript storage** (the default, or your own `storage`): nothing to do. After an action that changed the conversation, the runtime hands the storage a changeset with `reason: "action"`. An undo is one `truncateAfter`; a regenerate is a `truncateAfter` followed by the new answer's `put` when the turn completes; an edit is a `put` for the edited id. The changeset carries the same resume cursors as the last turn. See [Transcript storage](/ai-chat/transcript-storage#what-the-runtime-saves). An action that returns `chat.turn()` is followed by a turn, which persists its answer the way every turn does.
87
+
88
+ **Your own store through the deprecated `hydrateMessages`**: the runtime deliberately does not write, because your store is the source of truth. A history edit lives only in the running worker until you persist it, and a continuation rehydrates from your store, not from what the worker had in memory. Mirror each edit in your store, not only additions: a regenerate is a delete *and* an insert. The answer that follows `chat.turn()` reaches your store through `onTurnComplete`, like any turn's answer.
89
+
90
+ ```ts
91
+ onAction: async ({ action, chatId }) => {
92
+ if (action.type === "undo") {
93
+ chat.history.slice(0, -2);
94
+ await db.deleteLastExchange(chatId); // the rollback is yours to persist
95
+ }
96
+ if (action.type === "regenerate") {
97
+ chat.history.slice(0, -1);
98
+ await db.deleteLastAssistant(chatId); // the delete half
99
+ return chat.turn(); // the insert half arrives in onTurnComplete
100
+ }
101
+ },
102
+ onTurnComplete: async ({ chatId, newUIMessages }) => {
103
+ await db.saveMessages(chatId, newUIMessages);
104
+ },
105
+ ```
74
106
 
75
107
  ## Gating actions on HITL state
76
108
 
77
109
  If you have a [human-in-the-loop](/ai-chat/patterns/human-in-the-loop) tool waiting on `addToolOutput`, you usually want to refuse competing actions like `regenerate` until the answer arrives. [`chat.history.getPendingToolCalls()`](/ai-chat/backend#chat-history) gives you exactly that signal:
78
110
 
79
111
  ```ts
80
- onAction: async ({ action, messages, signal }) => {
112
+ onAction: async ({ action }) => {
81
113
  if (action.type === "regenerate") {
82
114
  if (chat.history.getPendingToolCalls().length > 0) return; // gated
83
115
  chat.history.slice(0, -1);
84
- return streamText({ model: anthropic("claude-sonnet-4-5"), messages, abortSignal: signal });
116
+ return chat.turn();
85
117
  }
86
118
  },
87
119
  ```
@@ -89,10 +121,10 @@ onAction: async ({ action, messages, signal }) => {
89
121
  ## Sending actions from the frontend
90
122
 
91
123
  ```ts
92
- // Browser TriggerChatTransport
124
+ // Browser: TriggerChatTransport
93
125
  const stream = await transport.sendAction(chatId, { type: "undo" });
94
126
 
95
- // Server AgentChat
127
+ // Server: AgentChat
96
128
  const stream = await agentChat.sendAction({ type: "rollback", targetMessageId: "msg-3" });
97
129
  ```
98
130
 
@@ -104,8 +136,8 @@ The action payload is validated against `actionSchema` on the backend; invalid a
104
136
 
105
137
  ## See also
106
138
 
107
- - [`chat.history`](/ai-chat/backend#chat-history) the imperative API actions use to mutate state
108
- - [Sending actions from the frontend](/ai-chat/frontend#sending-actions) `transport.sendAction` ergonomics
109
- - [`hydrateMessages`](/ai-chat/lifecycle-hooks#hydratemessages) fires before `onAction` when set
110
- - [Branching conversations](/ai-chat/patterns/branching-conversations) pairs action handlers with backend-controlled history
111
- - [Human-in-the-loop](/ai-chat/patterns/human-in-the-loop) gating fresh actions while a tool is waiting
139
+ - [`chat.history`](/ai-chat/backend#chat-history): the imperative API actions use to mutate state
140
+ - [Sending actions from the frontend](/ai-chat/frontend#sending-actions): sending actions through `useChat` so a turn that follows one renders like any turn
141
+ - [`hydrateMessages`](/ai-chat/lifecycle-hooks#hydratemessages): fires before `onAction` when set
142
+ - [Branching conversations](/ai-chat/patterns/branching-conversations): pairs action handlers with backend-controlled history
143
+ - [Human-in-the-loop](/ai-chat/patterns/human-in-the-loop): gating fresh actions while a tool is waiting
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  title: "Anatomy of an agent"
3
3
  sidebarTitle: "Anatomy"
4
- description: "The moving parts of a chat agent — the agent task, the session, the frontend transport — and which page covers each."
4
+ description: "The three parts of a chat agent — the agent task that runs the turn loop, the durable session that carries messages, and the frontend transport — what each does and where it's documented."
5
5
  ---
6
6
 
7
- **A chat agent is three parts: a long-lived agent task that runs the turn loop, a durable Session carrying messages in and the response stream out, and a frontend transport that plugs the session into `useChat`.** The pages in this section each own one part of that picture. This page is the map if you'd rather read mechanics end to end, skip to [How it works](/ai-chat/how-it-works).
7
+ **A chat agent is three parts: an agent task that runs the turn loop, a durable session that carries messages in and streams the response out, and a frontend transport that connects the session to `useChat`.** This page is your map of the Building agents section what each part does, how a single message flows through them, and which page to open next.
8
8
 
9
9
  ```mermaid
10
10
  flowchart LR
@@ -14,31 +14,57 @@ flowchart LR
14
14
  OUT -- "streamed response" --> FE
15
15
  ```
16
16
 
17
- Everything below maps onto one annotated agent:
17
+ ## Follow one message
18
+
19
+ A single turn moves through all three parts in order:
20
+
21
+ 1. The **frontend transport** sends the user's message to the session's inbound stream (`.in`).
22
+ 2. The **agent task** wakes on the new message, runs your turn loop, and streams the model's response into the session's outbound stream (`.out`).
23
+ 3. The **frontend transport** reads `.out` and renders tokens into `useChat` as they arrive.
24
+
25
+ The session sits in the middle so both sides never have to be online at the same moment — the task keeps writing to `.out` even if the browser reloads mid-stream.
26
+
27
+ ## The three parts
28
+
29
+ ### The agent task
30
+
31
+ `chat.agent()` is the code you write: a long-lived agent whose `run` function is the turn loop. Messages arrive accumulated, you call `streamText`, and the returned stream is piped back to the frontend for you. [Tools](/ai-chat/tools) the model can call and [lifecycle hooks](/ai-chat/lifecycle-hooks) that fire around each turn both hang off the same config. See [Backend](/ai-chat/backend) for the full `chat.agent()` surface.
32
+
33
+ ### The session
34
+
35
+ The conversation lives in a [session](/ai-chat/sessions), not in the task's memory: a pair of durable streams — `.in` and `.out` — keyed on your `chatId`. Because the session is durable, a conversation survives page refreshes, deploys, and the run boundaries between turns. The task can restart and pick up the same session. [How it works](/ai-chat/how-it-works) covers the mechanics of what survives and why.
36
+
37
+ ### The frontend transport
38
+
39
+ One hook — `useTriggerChatTransport` — connects the Vercel AI SDK's `useChat` to the agent's session. There are no API routes to write: the transport talks to the session directly using a scoped token. See [Frontend](/ai-chat/frontend) for wiring, tokens, and starting sessions.
40
+
41
+ ## An annotated agent
42
+
43
+ Everything above maps onto one task:
18
44
 
19
45
  ```ts trigger/my-agent.ts
20
46
  import { chat } from "@trigger.dev/sdk/ai";
21
- import { streamText, stepCountIs } from "ai";
47
+ import { stepCountIs } from "ai";
22
48
  import { anthropic } from "@ai-sdk/anthropic";
23
49
 
24
50
  export const myAgent = chat.agent({
25
51
  id: "my-agent",
26
52
 
27
- // Tools declared on the config survive history re-conversion
28
- // across turns — see Tools.
53
+ // Tools the model can call, declared on the config so they
54
+ // survive history re-conversion across turns — see Tools.
29
55
  tools: { searchDocs },
30
56
 
31
- // Hooks fire around each turn: validation, persistence,
32
- // post-turn work — see Lifecycle hooks.
57
+ // A lifecycle hook: fires after each turn, here to persist
58
+ // the response — see Lifecycle hooks.
33
59
  onTurnComplete: async ({ responseMessage }) => {
34
60
  await db.messages.save(responseMessage);
35
61
  },
36
62
 
37
- // The turn loop. Messages arrive accumulated; you stream back.
38
- // Options, levels, and alternatives — see Backend.
39
- run: async ({ messages, tools, signal }) =>
63
+ // The turn loop. Messages arrive accumulated; the returned
64
+ // stream is piped back to the frontend — see Backend.
65
+ run: async ({ messages, tools, signal, streamText }) =>
40
66
  streamText({
41
- ...chat.toStreamTextOptions({ tools }),
67
+ tools,
42
68
  model: anthropic("claude-sonnet-4-5"),
43
69
  messages,
44
70
  abortSignal: signal,
@@ -47,21 +73,15 @@ export const myAgent = chat.agent({
47
73
  });
48
74
  ```
49
75
 
50
- The frontend side is one hook — `useTriggerChatTransport` connects `useChat` to the agent's session, no API routes ([Frontend](/ai-chat/frontend)). Underneath, the conversation lives on a [Session](/ai-chat/sessions): a pair of durable streams keyed on your `chatId` that survives refreshes, deploys, and run boundaries.
76
+ ## Related pages
51
77
 
52
- ## Where each part is covered
78
+ Beyond the three core parts:
53
79
 
54
- | Part | Page |
55
- | ----------------------------------------------------- | ---------------------------------------------- |
56
- | `chat.agent()` options, the turn loop, piping | [Backend](/ai-chat/backend) |
57
- | Hooks around each turn (`onTurnComplete`, hydration) | [Lifecycle hooks](/ai-chat/lifecycle-hooks) |
58
- | Declaring tools, typed payloads, `toModelOutput` | [Tools](/ai-chat/tools) |
59
- | `useChat` wiring, tokens, starting sessions | [Frontend](/ai-chat/frontend) |
60
- | Driving a chat from your server instead of a browser | [Server-side chat](/ai-chat/server-chat) |
61
- | The durable substrate under every agent | [Sessions](/ai-chat/sessions) |
62
- | Per-run typed state inside the loop | [chat.local](/ai-chat/chat-local) |
63
- | Type-safe payloads, client data, and messages | [Types](/ai-chat/types) |
64
- | Building without the managed lifecycle | [Custom agents](/ai-chat/custom-agents) |
65
- | End-to-end mechanics: what survives a refresh and why | [How it works](/ai-chat/how-it-works) |
80
+ | Page | What it covers |
81
+ | ------------------------------------------- | ------------------------------------------------------- |
82
+ | [Server-side chat](/ai-chat/server-chat) | Driving a chat from your backend instead of a browser |
83
+ | [chat.local](/ai-chat/chat-local) | Per-run typed state inside the turn loop |
84
+ | [Types](/ai-chat/types) | Type-safe payloads, client data, and messages |
85
+ | [Custom agents](/ai-chat/custom-agents) | Building without the managed lifecycle |
66
86
 
67
- Beyond this section: [Features](/ai-chat/fast-starts) covers opt-in capabilities (Head Start, compaction, steering, actions), and [Patterns](/ai-chat/patterns/sub-agents) covers production recipes (sub-agents, HITL approvals, persistence, recovery).
87
+ [Features](/ai-chat/fast-starts) covers opt-in capabilities (Head Start, compaction, steering, actions), and [Patterns](/ai-chat/patterns/sub-agents) covers production recipes (sub-agents, HITL approvals, persistence, recovery).