@trigger.dev/sdk 0.0.0-prerelease-20260309160514 → 0.0.0-prerelease-20260324161542

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 (44) hide show
  1. package/dist/commonjs/v3/ai.d.ts +809 -20
  2. package/dist/commonjs/v3/ai.js +1193 -80
  3. package/dist/commonjs/v3/ai.js.map +1 -1
  4. package/dist/commonjs/v3/chat.d.ts +3 -1
  5. package/dist/commonjs/v3/chat.js +4 -1
  6. package/dist/commonjs/v3/chat.js.map +1 -1
  7. package/dist/commonjs/v3/index.d.ts +1 -0
  8. package/dist/commonjs/v3/index.js +2 -1
  9. package/dist/commonjs/v3/index.js.map +1 -1
  10. package/dist/commonjs/v3/prompt.d.ts +38 -0
  11. package/dist/commonjs/v3/prompt.js +150 -0
  12. package/dist/commonjs/v3/prompt.js.map +1 -0
  13. package/dist/commonjs/v3/promptManagement.d.ts +26 -0
  14. package/dist/commonjs/v3/promptManagement.js +180 -0
  15. package/dist/commonjs/v3/promptManagement.js.map +1 -0
  16. package/dist/commonjs/v3/prompts.d.ts +3 -0
  17. package/dist/commonjs/v3/prompts.js +15 -0
  18. package/dist/commonjs/v3/prompts.js.map +1 -0
  19. package/dist/commonjs/v3/runs.d.ts +1 -1
  20. package/dist/commonjs/v3/streams.js +45 -3
  21. package/dist/commonjs/v3/streams.js.map +1 -1
  22. package/dist/commonjs/version.js +1 -1
  23. package/dist/esm/v3/ai.d.ts +809 -20
  24. package/dist/esm/v3/ai.js +1193 -80
  25. package/dist/esm/v3/ai.js.map +1 -1
  26. package/dist/esm/v3/chat.d.ts +3 -1
  27. package/dist/esm/v3/chat.js +4 -1
  28. package/dist/esm/v3/chat.js.map +1 -1
  29. package/dist/esm/v3/index.d.ts +1 -0
  30. package/dist/esm/v3/index.js +1 -0
  31. package/dist/esm/v3/index.js.map +1 -1
  32. package/dist/esm/v3/prompt.d.ts +38 -0
  33. package/dist/esm/v3/prompt.js +147 -0
  34. package/dist/esm/v3/prompt.js.map +1 -0
  35. package/dist/esm/v3/promptManagement.d.ts +26 -0
  36. package/dist/esm/v3/promptManagement.js +170 -0
  37. package/dist/esm/v3/promptManagement.js.map +1 -0
  38. package/dist/esm/v3/prompts.d.ts +3 -0
  39. package/dist/esm/v3/prompts.js +3 -0
  40. package/dist/esm/v3/prompts.js.map +1 -0
  41. package/dist/esm/v3/streams.js +45 -3
  42. package/dist/esm/v3/streams.js.map +1 -1
  43. package/dist/esm/version.js +1 -1
  44. package/package.json +2 -2
@@ -1,7 +1,8 @@
1
1
  import { AnyTask, Task, type inferSchemaIn, type inferSchemaOut, type TaskIdentifier, type TaskOptions, type TaskSchema, type TaskWithSchema } from "@trigger.dev/core/v3";
2
- import type { ModelMessage, UIMessage, UIMessageChunk } from "ai";
2
+ import type { ModelMessage, UIMessage, UIMessageChunk, UIMessageStreamOptions, LanguageModelUsage } from "ai";
3
3
  import { Tool } from "ai";
4
4
  import { locals } from "./locals.js";
5
+ import type { ResolvedPrompt } from "./prompt.js";
5
6
  import { CHAT_MESSAGES_STREAM_ID, CHAT_STOP_STREAM_ID } from "./chat-constants.js";
6
7
  export type ToolCallExecutionOptions = {
7
8
  toolCallId: string;
@@ -110,6 +111,8 @@ export type ChatTaskWirePayload<TMessage extends UIMessage = UIMessage, TMetadat
110
111
  continuation?: boolean;
111
112
  /** The run ID of the previous run (only set when `continuation` is true). */
112
113
  previousRunId?: string;
114
+ /** Override idle timeout for this run (seconds). Set by transport.preload(). */
115
+ idleTimeoutInSeconds?: number;
113
116
  };
114
117
  /**
115
118
  * The payload shape passed to the `chatTask` run function.
@@ -159,7 +162,389 @@ export type ChatTaskSignals = {
159
162
  * The full payload passed to a `chatTask` run function.
160
163
  * Extends `ChatTaskPayload` (the wire payload) with abort signals.
161
164
  */
162
- export type ChatTaskRunPayload<TClientData = unknown> = ChatTaskPayload<TClientData> & ChatTaskSignals;
165
+ export type ChatTaskRunPayload<TClientData = unknown> = ChatTaskPayload<TClientData> & ChatTaskSignals & {
166
+ /** Token usage from the previous turn. Undefined on turn 0. */
167
+ previousTurnUsage?: LanguageModelUsage;
168
+ /** Cumulative token usage across all completed turns so far. */
169
+ totalUsage: LanguageModelUsage;
170
+ };
171
+ /** Convenience re-export of the AI SDK's `LanguageModelUsage` type. */
172
+ export type ChatTurnUsage = LanguageModelUsage;
173
+ /**
174
+ * Replace the accumulated conversation messages for the current run.
175
+ *
176
+ * Call from `onTurnStart` to compact before `run()` executes, or from
177
+ * `onTurnComplete` to compact before the next turn. Takes `UIMessage[]`
178
+ * and converts to `ModelMessage[]` internally.
179
+ */
180
+ declare function setChatMessages(uiMessages: UIMessage[]): void;
181
+ /** State stored in locals during prepareStep compaction. */
182
+ interface CompactionState {
183
+ summary: string;
184
+ baseResponseMessageCount: number;
185
+ }
186
+ /**
187
+ * Event passed to `summarize` callbacks.
188
+ */
189
+ export type SummarizeEvent = {
190
+ /** The current model messages to summarize. */
191
+ messages: ModelMessage[];
192
+ /** Full usage object from the triggering step/turn. */
193
+ usage?: LanguageModelUsage;
194
+ /** Cumulative token usage across all completed turns. Present in chat.task contexts. */
195
+ totalUsage?: LanguageModelUsage;
196
+ /** The chat session ID (if running inside a chat.task). */
197
+ chatId?: string;
198
+ /** The current turn number (0-indexed, if inside a chat.task). */
199
+ turn?: number;
200
+ /** Custom data from the frontend (if inside a chat.task). */
201
+ clientData?: unknown;
202
+ /**
203
+ * Where compaction is running:
204
+ * - `"inner"` — between tool-call steps (prepareStep)
205
+ * - `"outer"` — between turns
206
+ */
207
+ source?: "inner" | "outer";
208
+ /** The step number (0-indexed). Only present when `source` is `"inner"`. */
209
+ stepNumber?: number;
210
+ };
211
+ /**
212
+ * Event passed to `compactUIMessages` and `compactModelMessages` callbacks.
213
+ */
214
+ export type CompactMessagesEvent = {
215
+ /** The generated summary text. */
216
+ summary: string;
217
+ /** The current UI messages (full conversation). */
218
+ uiMessages: UIMessage[];
219
+ /** The current model messages (full conversation). */
220
+ modelMessages: ModelMessage[];
221
+ /** The chat session ID. */
222
+ chatId: string;
223
+ /** The current turn number (0-indexed). */
224
+ turn: number;
225
+ /** Custom data from the frontend. */
226
+ clientData?: unknown;
227
+ /**
228
+ * Where compaction is running:
229
+ * - `"inner"` — between tool-call steps (prepareStep)
230
+ * - `"outer"` — between turns
231
+ */
232
+ source: "inner" | "outer";
233
+ };
234
+ /**
235
+ * Options for the `compaction` field on `chat.task()`.
236
+ *
237
+ * Handles compaction automatically in both the inner loop (prepareStep, between
238
+ * tool-call steps) and the outer loop (between turns, for single-step responses
239
+ * where prepareStep never fires).
240
+ */
241
+ export type ChatTaskCompactionOptions = {
242
+ /** Decide whether to compact. Return true to trigger compaction. */
243
+ shouldCompact: (event: ShouldCompactEvent) => boolean | Promise<boolean>;
244
+ /** Generate a summary from the current messages. Return the summary text. */
245
+ summarize: (event: SummarizeEvent) => Promise<string>;
246
+ /**
247
+ * Transform UI messages after compaction (what gets persisted and displayed).
248
+ * Default: preserve all UI messages unchanged.
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * // Flatten to summary
253
+ * compactUIMessages: ({ summary }) => [{
254
+ * id: generateId(), role: "assistant",
255
+ * parts: [{ type: "text", text: `[Summary]\n\n${summary}` }],
256
+ * }],
257
+ *
258
+ * // Summary + keep last 4 messages
259
+ * compactUIMessages: ({ uiMessages, summary }) => [
260
+ * { id: generateId(), role: "assistant",
261
+ * parts: [{ type: "text", text: `[Summary]\n\n${summary}` }] },
262
+ * ...uiMessages.slice(-4),
263
+ * ],
264
+ * ```
265
+ */
266
+ compactUIMessages?: (event: CompactMessagesEvent) => UIMessage[] | Promise<UIMessage[]>;
267
+ /**
268
+ * Transform model messages after compaction (what gets sent to the LLM).
269
+ * Default: replace all with a single summary message.
270
+ *
271
+ * @example
272
+ * ```ts
273
+ * // Summary + keep last 2 model messages
274
+ * compactModelMessages: ({ modelMessages, summary }) => [
275
+ * { role: "user", content: summary },
276
+ * ...modelMessages.slice(-2),
277
+ * ],
278
+ * ```
279
+ */
280
+ compactModelMessages?: (event: CompactMessagesEvent) => ModelMessage[] | Promise<ModelMessage[]>;
281
+ };
282
+ /**
283
+ * Event passed to the `prepareMessages` hook.
284
+ */
285
+ export type PrepareMessagesEvent<TClientData = unknown> = {
286
+ /** The messages to transform. Return the transformed array. */
287
+ messages: ModelMessage[];
288
+ /** Why messages are being prepared. */
289
+ reason: "run" | "compaction-rebuild" | "compaction-result";
290
+ /** The chat session ID. */
291
+ chatId: string;
292
+ /** The current turn number (0-indexed). */
293
+ turn: number;
294
+ /** Custom data from the frontend. */
295
+ clientData?: TClientData;
296
+ };
297
+ /**
298
+ * Data shape for `data-compaction` stream chunks emitted during compaction.
299
+ * Use to type the `data` field when rendering compaction parts in the frontend.
300
+ */
301
+ export type CompactionChunkData = {
302
+ status: "compacting" | "complete";
303
+ totalTokens: number | undefined;
304
+ };
305
+ /**
306
+ * Event passed to the `onCompacted` callback.
307
+ */
308
+ export type CompactedEvent = {
309
+ /** The generated summary text. */
310
+ summary: string;
311
+ /** The messages that were compacted (pre-compaction). */
312
+ messages: ModelMessage[];
313
+ /** Number of messages before compaction. */
314
+ messageCount: number;
315
+ /** Token usage from the step that triggered compaction. */
316
+ usage: LanguageModelUsage;
317
+ /** Total token count that triggered compaction. */
318
+ totalTokens: number | undefined;
319
+ /** Input token count from the triggering step. */
320
+ inputTokens: number | undefined;
321
+ /** Output token count from the triggering step. */
322
+ outputTokens: number | undefined;
323
+ /** The step number where compaction occurred (0-indexed). */
324
+ stepNumber: number;
325
+ /** The chat session ID (if running inside a chat.task). */
326
+ chatId?: string;
327
+ /** The current turn number (if running inside a chat.task). */
328
+ turn?: number;
329
+ };
330
+ /**
331
+ * Event passed to `shouldCompact` callbacks.
332
+ */
333
+ export type ShouldCompactEvent = {
334
+ /** The current model messages (full conversation). */
335
+ messages: ModelMessage[];
336
+ /** Total token count from the triggering step/turn. */
337
+ totalTokens: number | undefined;
338
+ /** Input token count from the triggering step/turn. */
339
+ inputTokens: number | undefined;
340
+ /** Output token count from the triggering step/turn. */
341
+ outputTokens: number | undefined;
342
+ /** Full usage object from the triggering step/turn. */
343
+ usage?: LanguageModelUsage;
344
+ /** Cumulative token usage across all completed turns. Present in chat.task contexts. */
345
+ totalUsage?: LanguageModelUsage;
346
+ /** The chat session ID (if running inside a chat.task). */
347
+ chatId?: string;
348
+ /** The current turn number (0-indexed, if inside a chat.task). */
349
+ turn?: number;
350
+ /** Custom data from the frontend (if inside a chat.task). */
351
+ clientData?: unknown;
352
+ /**
353
+ * Where this check is running:
354
+ * - `"inner"` — between tool-call steps (prepareStep)
355
+ * - `"outer"` — between turns (after response, before onBeforeTurnComplete)
356
+ */
357
+ source?: "inner" | "outer";
358
+ /** The step number (0-indexed). Only present when `source` is `"inner"`. */
359
+ stepNumber?: number;
360
+ /** The steps array from prepareStep. Only present when `source` is `"inner"`. */
361
+ steps?: CompactionStep[];
362
+ };
363
+ /**
364
+ * Options for `chat.compaction()` — the high-level prepareStep factory.
365
+ */
366
+ export type CompactionOptions = {
367
+ /** Generate a summary from the current messages. Return the summary text. */
368
+ summarize: (messages: ModelMessage[]) => Promise<string>;
369
+ /** Token threshold — compact when totalTokens exceeds this. Ignored if `shouldCompact` is provided. */
370
+ threshold?: number;
371
+ /** Custom compaction trigger. When provided, used instead of `threshold`. */
372
+ shouldCompact?: (event: ShouldCompactEvent) => boolean | Promise<boolean>;
373
+ };
374
+ /** A step object as received in prepareStep's `steps` array. */
375
+ export type CompactionStep = {
376
+ usage: LanguageModelUsage;
377
+ finishReason: string;
378
+ content: Array<{
379
+ type: string;
380
+ toolCallId?: string;
381
+ }>;
382
+ response: {
383
+ messages: Array<any>;
384
+ };
385
+ };
386
+ /**
387
+ * Result of `chat.compact()`. Discriminated union so you can inspect
388
+ * what happened, but also directly compatible with prepareStep's return type.
389
+ *
390
+ * - `"skipped"` — no compaction needed (first step, boundary unsafe, or under threshold). Return `undefined` to prepareStep.
391
+ * - `"rebuilt"` — previous compaction exists, messages rebuilt from summary + new response messages.
392
+ * - `"compacted"` — compaction just happened, includes the generated summary.
393
+ */
394
+ export type CompactResult = {
395
+ type: "skipped";
396
+ } | {
397
+ type: "rebuilt";
398
+ messages: ModelMessage[];
399
+ } | {
400
+ type: "compacted";
401
+ messages: ModelMessage[];
402
+ summary: string;
403
+ };
404
+ /**
405
+ * Options for `chat.compact()` — the low-level compaction function.
406
+ */
407
+ export type CompactOptions = {
408
+ /** Generate a summary from the current messages. Return the summary text. */
409
+ summarize: (messages: ModelMessage[]) => Promise<string>;
410
+ /** Token threshold — compact when totalTokens exceeds this. Ignored if `shouldCompact` is provided. */
411
+ threshold?: number;
412
+ /** Custom compaction trigger. When provided, used instead of `threshold`. */
413
+ shouldCompact?: (event: ShouldCompactEvent) => boolean | Promise<boolean>;
414
+ };
415
+ /**
416
+ * Read the current compaction state. Returns the summary and base message count
417
+ * if compaction has occurred in this turn, or `undefined` if not.
418
+ *
419
+ * Use in a custom `prepareStep` to rebuild from a previous compaction:
420
+ * ```ts
421
+ * const state = chat.getCompactionState();
422
+ * if (state) {
423
+ * return { messages: [{ role: "user", content: state.summary }, ...newMsgs] };
424
+ * }
425
+ * ```
426
+ */
427
+ declare function getCompactionState(): CompactionState | undefined;
428
+ /**
429
+ * Low-level compaction for use inside a custom `prepareStep`.
430
+ *
431
+ * Handles the full decision tree: first step, already-compacted rebuild,
432
+ * boundary safety, threshold check, summarization, stream chunks, state
433
+ * storage, and accumulator update.
434
+ *
435
+ * Returns a `CompactResult` — inspect `result.type` to see what happened,
436
+ * or convert to a prepareStep return with `result.type === "skipped" ? undefined : result`.
437
+ *
438
+ * @example
439
+ * ```ts
440
+ * prepareStep: async ({ messages, steps }) => {
441
+ * // your custom logic here...
442
+ * const result = await chat.compact(messages, steps, {
443
+ * threshold: 80_000,
444
+ * summarize: async (msgs) => generateText({ model, messages: msgs }).then(r => r.text),
445
+ * });
446
+ * if (result.type === "compacted") {
447
+ * logger.info("Compacted!", { summary: result.summary });
448
+ * }
449
+ * return result.type === "skipped" ? undefined : result;
450
+ * },
451
+ * ```
452
+ */
453
+ declare function chatCompact(messages: ModelMessage[], steps: CompactionStep[], options: CompactOptions): Promise<CompactResult>;
454
+ /**
455
+ * Returns a `prepareStep` function that handles context compaction automatically.
456
+ *
457
+ * Monitors token usage between tool-call steps. When `totalTokens` exceeds
458
+ * the threshold, generates a summary via `summarize()`, replaces the message
459
+ * history, and emits `data-compaction` stream chunks for the frontend.
460
+ *
461
+ * @example
462
+ * ```ts
463
+ * return streamText({
464
+ * ...chat.toStreamTextOptions({ registry }),
465
+ * messages: chat.addCacheBreaks(messages),
466
+ * prepareStep: chat.compactionStep({
467
+ * threshold: 80_000,
468
+ * summarize: async (messages) => {
469
+ * return generateText({ model, messages: [...messages, { role: "user", content: "Summarize." }] })
470
+ * .then((r) => r.text);
471
+ * },
472
+ * }),
473
+ * tools: { ... },
474
+ * });
475
+ * ```
476
+ */
477
+ declare function chatCompactionStep(options: CompactionOptions): (args: {
478
+ messages: ModelMessage[];
479
+ steps: CompactionStep[];
480
+ }) => Promise<{
481
+ messages: ModelMessage[];
482
+ } | undefined>;
483
+ /**
484
+ * Checks whether it's safe to compact the message history. Returns `false`
485
+ * if any tool calls are in-flight (incomplete tool invocations without results).
486
+ *
487
+ * Call before `chat.setMessages()` to avoid corrupting tool-call state.
488
+ */
489
+ declare function isCompactionSafe(messages: UIMessage[]): boolean;
490
+ /**
491
+ * A resolved prompt stored via `chat.prompt.set()`. Either a full `ResolvedPrompt`
492
+ * from `prompts.define().resolve()`, or a lightweight wrapper around a plain string.
493
+ */
494
+ export type ChatPromptValue = ResolvedPrompt | {
495
+ text: string;
496
+ model: undefined;
497
+ config: undefined;
498
+ promptId: string;
499
+ version: number;
500
+ labels: string[];
501
+ toAISDKTelemetry: (additionalMetadata?: Record<string, string>) => {
502
+ experimental_telemetry: {
503
+ isEnabled: true;
504
+ metadata: Record<string, string>;
505
+ };
506
+ };
507
+ };
508
+ /**
509
+ * Store a resolved prompt (or plain string) for the current run.
510
+ * Call from any hook (`onPreload`, `onChatStart`, `onTurnStart`) or `run()`.
511
+ */
512
+ declare function setChatPrompt(resolved: ResolvedPrompt | string): void;
513
+ /**
514
+ * Read the stored prompt. Throws if `chat.prompt.set()` has not been called.
515
+ */
516
+ declare function getChatPrompt(): ChatPromptValue;
517
+ /**
518
+ * Options for {@link toStreamTextOptions}.
519
+ */
520
+ export type ToStreamTextOptionsOptions = {
521
+ /** Additional telemetry metadata merged into `experimental_telemetry.metadata`. */
522
+ telemetry?: Record<string, string>;
523
+ /**
524
+ * An AI SDK provider registry (from `createProviderRegistry`) or any object
525
+ * with a `languageModel(id)` method. When provided and the stored prompt has
526
+ * a `model` string, the resolved `LanguageModel` is included in the returned
527
+ * options so `streamText` uses it directly.
528
+ *
529
+ * The model string should use the `"provider:model-id"` format
530
+ * (e.g. `"openai:gpt-4o"`, `"anthropic:claude-sonnet-4-6"`).
531
+ */
532
+ registry?: {
533
+ languageModel(modelId: string): unknown;
534
+ };
535
+ };
536
+ /**
537
+ * Returns an options object ready to spread into `streamText()`.
538
+ *
539
+ * Includes `system`, `experimental_telemetry`, and any config fields
540
+ * (temperature, maxTokens, etc.) from the stored prompt.
541
+ *
542
+ * When a `registry` is provided and the prompt has a `model` string,
543
+ * the resolved `LanguageModel` is included as `model`.
544
+ *
545
+ * If no prompt has been set, returns `{}` (no-op spread).
546
+ */
547
+ declare function toStreamTextOptions(options?: ToStreamTextOptionsOptions): Record<string, unknown>;
163
548
  /**
164
549
  * Options for `pipeChat`.
165
550
  */
@@ -179,6 +564,19 @@ export type PipeChatOptions = {
179
564
  /** Override the default span name for this operation. */
180
565
  spanName?: string;
181
566
  };
567
+ /**
568
+ * Options for customizing the `toUIMessageStream()` call used when piping
569
+ * `streamText` results to the frontend.
570
+ *
571
+ * Set static defaults via `uiMessageStreamOptions` on `chat.task()`, or
572
+ * override per-turn via `chat.setUIMessageStreamOptions()`.
573
+ *
574
+ * `onFinish`, `originalMessages`, and `generateMessageId` are omitted because
575
+ * they are managed internally for response capture and message accumulation.
576
+ * Use `streamText`'s `onFinish` for custom finish handling, or drop down to
577
+ * raw task mode with `chat.pipe()` for full control.
578
+ */
579
+ export type ChatUIMessageStreamOptions = Omit<UIMessageStreamOptions<UIMessage>, "onFinish" | "originalMessages" | "generateMessageId">;
182
580
  /**
183
581
  * An object with a `toUIMessageStream()` method (e.g. `StreamTextResult` from `streamText()`).
184
582
  */
@@ -297,6 +695,10 @@ export type TurnStartEvent<TClientData = unknown> = {
297
695
  previousRunId?: string;
298
696
  /** Whether this run was preloaded before the first message. */
299
697
  preloaded: boolean;
698
+ /** Token usage from the previous turn. Undefined on turn 0. */
699
+ previousTurnUsage?: LanguageModelUsage;
700
+ /** Cumulative token usage across all completed turns so far. */
701
+ totalUsage: LanguageModelUsage;
300
702
  };
301
703
  /**
302
704
  * Event passed to the `onTurnComplete` callback.
@@ -347,6 +749,10 @@ export type TurnCompleteEvent<TClientData = unknown> = {
347
749
  previousRunId?: string;
348
750
  /** Whether this run was preloaded before the first message. */
349
751
  preloaded: boolean;
752
+ /** Token usage for this turn. Undefined if usage couldn't be captured (e.g. manual pipeChat). */
753
+ usage?: LanguageModelUsage;
754
+ /** Cumulative token usage across all turns in this run (including this turn). */
755
+ totalUsage: LanguageModelUsage;
350
756
  };
351
757
  export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extends TaskSchema | undefined = undefined> = Omit<TaskOptions<TIdentifier, ChatTaskWirePayload, unknown>, "run"> & {
352
758
  /**
@@ -422,10 +828,68 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
422
828
  */
423
829
  onTurnStart?: (event: TurnStartEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void;
424
830
  /**
425
- * Called after each turn completes (after the response is captured, before waiting
426
- * for the next message). Also fires on the final turn.
831
+ * Called after the response is captured but before the stream closes.
832
+ * The stream is still open, so you can write custom chunks to the frontend
833
+ * (e.g. compaction progress). Use this for compaction, post-processing,
834
+ * or any work where the user should see real-time status updates.
835
+ *
836
+ * @example
837
+ * ```ts
838
+ * onBeforeTurnComplete: async ({ messages, uiMessages, usage }) => {
839
+ * if (usage?.inputTokens && usage.inputTokens > 5000) {
840
+ * await chat.stream.append({ type: "data-compaction", id: generateId(), data: { status: "compacting" } });
841
+ * // ... compact messages ...
842
+ * chat.setMessages(compactedMessages);
843
+ * await chat.stream.append({ type: "data-compaction", id: generateId(), data: { status: "complete" } });
844
+ * }
845
+ * }
846
+ * ```
847
+ */
848
+ onBeforeTurnComplete?: (event: TurnCompleteEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void;
849
+ /**
850
+ * Called when conversation compaction occurs (via `chat.compact()` or
851
+ * `chat.compactionStep()`). Use for logging, billing, or persisting the summary.
852
+ *
853
+ * @example
854
+ * ```ts
855
+ * onCompacted: async ({ summary, totalTokens, chatId }) => {
856
+ * logger.info("Compacted", { totalTokens, chatId });
857
+ * await db.compactionLog.create({ data: { chatId, summary } });
858
+ * }
859
+ * ```
860
+ */
861
+ onCompacted?: (event: CompactedEvent) => Promise<void> | void;
862
+ /**
863
+ * Automatic context compaction. When provided, compaction runs automatically
864
+ * in both the inner loop (prepareStep, between tool-call steps) and the
865
+ * outer loop (between turns, for single-step responses where prepareStep
866
+ * never fires).
427
867
  *
428
- * Use this to persist the conversation to your database after each assistant response.
868
+ * The `shouldCompact` callback decides when to compact, and `summarize`
869
+ * generates the summary. The prepareStep is auto-injected into
870
+ * `chat.toStreamTextOptions()` — if you provide your own `prepareStep`
871
+ * after spreading, it overrides the auto-injected one.
872
+ *
873
+ * @example
874
+ * ```ts
875
+ * chat.task({
876
+ * id: "my-chat",
877
+ * compaction: {
878
+ * shouldCompact: ({ totalTokens }) => (totalTokens ?? 0) > 80_000,
879
+ * summarize: async (messages) =>
880
+ * generateText({ model, messages: [...messages, { role: "user", content: "Summarize." }] })
881
+ * .then((r) => r.text),
882
+ * },
883
+ * run: async ({ messages, signal }) => {
884
+ * return streamText({ ...chat.toStreamTextOptions({ registry }), messages });
885
+ * },
886
+ * });
887
+ * ```
888
+ */
889
+ compaction?: ChatTaskCompactionOptions;
890
+ /**
891
+ * Called after the stream closes for this turn. Use this to persist the
892
+ * conversation to your database after each assistant response.
429
893
  *
430
894
  * @example
431
895
  * ```ts
@@ -451,16 +915,16 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
451
915
  */
452
916
  turnTimeout?: string;
453
917
  /**
454
- * How long (in seconds) to keep the run warm after each turn before suspending.
455
- * During this window the run stays active and can respond instantly to the
456
- * next message. After this timeout, the run suspends (frees compute) and waits
457
- * via `inputStream.wait()`.
918
+ * How long (in seconds) the run stays idle (active, using compute) after each
919
+ * turn, waiting for the next message. During this window responses are instant.
920
+ * After this timeout the run suspends (frees compute) and waits via
921
+ * `inputStream.wait()`.
458
922
  *
459
923
  * Set to `0` to suspend immediately after each turn.
460
924
  *
461
925
  * @default 30
462
926
  */
463
- warmTimeoutInSeconds?: number;
927
+ idleTimeoutInSeconds?: number;
464
928
  /**
465
929
  * How long the `chatAccessToken` (scoped to this run) remains valid.
466
930
  * A fresh token is minted after each turn, so this only needs to cover
@@ -472,14 +936,14 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
472
936
  */
473
937
  chatAccessTokenTTL?: string;
474
938
  /**
475
- * How long (in seconds) to keep the run warm after `onPreload` fires,
939
+ * How long (in seconds) the run stays idle after `onPreload` fires,
476
940
  * waiting for the first message before suspending.
477
941
  *
478
942
  * Only applies to preloaded runs (triggered via `transport.preload()`).
479
943
  *
480
- * @default Same as `warmTimeoutInSeconds`
944
+ * @default Same as `idleTimeoutInSeconds`
481
945
  */
482
- preloadWarmTimeoutInSeconds?: number;
946
+ preloadIdleTimeoutInSeconds?: number;
483
947
  /**
484
948
  * How long to wait (suspended) for the first message after a preloaded run starts.
485
949
  * If no message arrives within this time, the run ends.
@@ -489,6 +953,55 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
489
953
  * @default Same as `turnTimeout`
490
954
  */
491
955
  preloadTimeout?: string;
956
+ /**
957
+ * Transform model messages before they're used anywhere — in `run()`,
958
+ * in compaction rebuilds, and in compaction results.
959
+ *
960
+ * Define once, applied everywhere. Use for Anthropic cache breaks,
961
+ * injecting system context, stripping PII, etc.
962
+ *
963
+ * @example
964
+ * ```ts
965
+ * prepareMessages: async ({ messages, reason }) => {
966
+ * // Add Anthropic cache breaks to the last message
967
+ * if (messages.length === 0) return messages;
968
+ * const last = messages[messages.length - 1];
969
+ * return [...messages.slice(0, -1), {
970
+ * ...last,
971
+ * providerOptions: { ...last.providerOptions, anthropic: { cacheControl: { type: "ephemeral" } } },
972
+ * }];
973
+ * }
974
+ * ```
975
+ */
976
+ prepareMessages?: (event: PrepareMessagesEvent<inferSchemaOut<TClientDataSchema>>) => ModelMessage[] | Promise<ModelMessage[]>;
977
+ /**
978
+ * Default options for `toUIMessageStream()` when auto-piping or using
979
+ * `turn.complete()` / `chat.pipeAndCapture()`.
980
+ *
981
+ * Controls how the `StreamTextResult` is converted to a `UIMessageChunk`
982
+ * stream — error handling, reasoning/source visibility, metadata, etc.
983
+ *
984
+ * Can be overridden per-turn by calling `chat.setUIMessageStreamOptions()`
985
+ * inside `run()` or lifecycle hooks. Per-turn values are merged on top
986
+ * of these defaults (per-turn wins on conflicts).
987
+ *
988
+ * `onFinish`, `originalMessages`, and `generateMessageId` are managed
989
+ * internally and cannot be overridden here. Use `streamText`'s `onFinish`
990
+ * for custom finish handling, or drop to raw task mode for full control.
991
+ *
992
+ * @example
993
+ * ```ts
994
+ * chat.task({
995
+ * id: "my-chat",
996
+ * uiMessageStreamOptions: {
997
+ * sendReasoning: true,
998
+ * onError: (error) => error instanceof Error ? error.message : "An error occurred.",
999
+ * },
1000
+ * run: async ({ messages, signal }) => { ... },
1001
+ * });
1002
+ * ```
1003
+ */
1004
+ uiMessageStreamOptions?: ChatUIMessageStreamOptions;
492
1005
  };
493
1006
  /**
494
1007
  * Creates a Trigger.dev task pre-configured for AI SDK chat.
@@ -553,23 +1066,45 @@ declare function setTurnTimeout(duration: string): void;
553
1066
  */
554
1067
  declare function setTurnTimeoutInSeconds(seconds: number): void;
555
1068
  /**
556
- * Override the warm timeout for subsequent turns in the current run.
1069
+ * Override the idle timeout for subsequent turns in the current run.
557
1070
  *
558
- * The warm timeout controls how long the run stays active (using compute)
1071
+ * The idle timeout controls how long the run stays active (using compute)
559
1072
  * after each turn, waiting for the next message. During this window,
560
1073
  * responses are instant. After it expires, the run suspends.
561
1074
  *
562
- * @param seconds - Number of seconds to stay warm (0 to suspend immediately)
1075
+ * @param seconds - Number of seconds to stay idle (0 to suspend immediately)
1076
+ *
1077
+ * @example
1078
+ * ```ts
1079
+ * run: async ({ messages, signal }) => {
1080
+ * chat.setIdleTimeoutInSeconds(60);
1081
+ * return streamText({ model, messages, abortSignal: signal });
1082
+ * }
1083
+ * ```
1084
+ */
1085
+ declare function setIdleTimeoutInSeconds(seconds: number): void;
1086
+ /**
1087
+ * Override the `toUIMessageStream()` options for the current turn.
1088
+ *
1089
+ * These options control how the `StreamTextResult` is converted to a
1090
+ * `UIMessageChunk` stream — error handling, reasoning/source visibility,
1091
+ * message metadata, etc.
1092
+ *
1093
+ * Per-turn options are merged on top of the static `uiMessageStreamOptions`
1094
+ * set on `chat.task()`. Per-turn values win on conflicts.
563
1095
  *
564
1096
  * @example
565
1097
  * ```ts
566
1098
  * run: async ({ messages, signal }) => {
567
- * chat.setWarmTimeoutInSeconds(60);
1099
+ * chat.setUIMessageStreamOptions({
1100
+ * sendReasoning: true,
1101
+ * onError: (error) => error instanceof Error ? error.message : "An error occurred.",
1102
+ * });
568
1103
  * return streamText({ model, messages, abortSignal: signal });
569
1104
  * }
570
1105
  * ```
571
1106
  */
572
- declare function setWarmTimeoutInSeconds(seconds: number): void;
1107
+ declare function setUIMessageStreamOptions(options: ChatUIMessageStreamOptions): void;
573
1108
  /**
574
1109
  * Check whether the user stopped generation during the current turn.
575
1110
  *
@@ -635,6 +1170,217 @@ declare function chatDefer(promise: Promise<unknown>): void;
635
1170
  * ```
636
1171
  */
637
1172
  declare function cleanupAbortedParts(message: UIMessage): UIMessage;
1173
+ /**
1174
+ * Create a managed stop signal wired to the chat stop input stream.
1175
+ *
1176
+ * Call once at the start of your run. Use `signal` as the abort signal for
1177
+ * `streamText`. Call `reset()` at the start of each turn to get a fresh
1178
+ * per-turn signal. Call `cleanup()` when the run ends.
1179
+ *
1180
+ * @example
1181
+ * ```ts
1182
+ * const stop = chat.createStopSignal();
1183
+ * for (let turn = 0; turn < 100; turn++) {
1184
+ * stop.reset();
1185
+ * const result = streamText({ model, messages, abortSignal: stop.signal });
1186
+ * await chat.pipe(result);
1187
+ * // ...
1188
+ * }
1189
+ * stop.cleanup();
1190
+ * ```
1191
+ */
1192
+ declare function createStopSignal(): {
1193
+ readonly signal: AbortSignal;
1194
+ reset: () => void;
1195
+ cleanup: () => void;
1196
+ };
1197
+ /**
1198
+ * Signal the frontend that the current turn is complete.
1199
+ *
1200
+ * The `TriggerChatTransport` intercepts this to close the ReadableStream
1201
+ * for the current turn. Call after piping the response stream.
1202
+ *
1203
+ * @example
1204
+ * ```ts
1205
+ * await chat.pipe(result);
1206
+ * await chat.writeTurnComplete();
1207
+ * ```
1208
+ */
1209
+ declare function chatWriteTurnComplete(options?: {
1210
+ publicAccessToken?: string;
1211
+ }): Promise<void>;
1212
+ /**
1213
+ * Pipe a `StreamTextResult` (or similar) to the chat stream and capture
1214
+ * the assistant's response message via `onFinish`.
1215
+ *
1216
+ * Combines `toUIMessageStream()` + `onFinish` callback + `chat.pipe()`.
1217
+ * Returns the captured `UIMessage`, or `undefined` if capture failed.
1218
+ *
1219
+ * @example
1220
+ * ```ts
1221
+ * const result = streamText({ model, messages, abortSignal: signal });
1222
+ * const response = await chat.pipeAndCapture(result, { signal });
1223
+ * if (response) conversation.addResponse(response);
1224
+ * ```
1225
+ */
1226
+ declare function pipeChatAndCapture(source: UIMessageStreamable, options?: {
1227
+ signal?: AbortSignal;
1228
+ spanName?: string;
1229
+ }): Promise<UIMessage | undefined>;
1230
+ /**
1231
+ * Accumulates conversation messages across turns.
1232
+ *
1233
+ * Handles the transport protocol: turn 0 sends full history (replace),
1234
+ * subsequent turns send only new messages (append), regenerate sends
1235
+ * full history minus last assistant message (replace).
1236
+ *
1237
+ * @example
1238
+ * ```ts
1239
+ * const conversation = new chat.MessageAccumulator();
1240
+ * for (let turn = 0; turn < 100; turn++) {
1241
+ * const messages = await conversation.addIncoming(payload.messages, payload.trigger, turn);
1242
+ * const result = streamText({ model, messages });
1243
+ * const response = await chat.pipeAndCapture(result);
1244
+ * if (response) await conversation.addResponse(response);
1245
+ * }
1246
+ * ```
1247
+ */
1248
+ declare class ChatMessageAccumulator {
1249
+ modelMessages: ModelMessage[];
1250
+ uiMessages: UIMessage[];
1251
+ private _compaction?;
1252
+ constructor(options?: {
1253
+ compaction?: ChatTaskCompactionOptions;
1254
+ });
1255
+ /**
1256
+ * Add incoming messages from the transport payload.
1257
+ * Returns the full accumulated model messages for `streamText`.
1258
+ */
1259
+ addIncoming(messages: UIMessage[], trigger: string, turn: number): Promise<ModelMessage[]>;
1260
+ /**
1261
+ * Add the assistant's response to the accumulator.
1262
+ * Call after `pipeAndCapture` with the captured response.
1263
+ */
1264
+ /**
1265
+ * Replace all accumulated messages (for compaction).
1266
+ * Converts UIMessages to ModelMessages internally.
1267
+ */
1268
+ setMessages(uiMessages: UIMessage[]): Promise<void>;
1269
+ addResponse(response: UIMessage): Promise<void>;
1270
+ /**
1271
+ * Returns a `prepareStep` function for inner-loop compaction.
1272
+ * Only available when `compaction` was provided to the constructor.
1273
+ * Pass the result to `streamText({ prepareStep: conversation.prepareStep() })`.
1274
+ */
1275
+ prepareStep(): ((args: {
1276
+ messages: ModelMessage[];
1277
+ steps: CompactionStep[];
1278
+ }) => Promise<{
1279
+ messages: ModelMessage[];
1280
+ } | undefined>) | undefined;
1281
+ /**
1282
+ * Run outer-loop compaction if needed. Call after adding the response
1283
+ * and capturing usage. Applies `compactModelMessages` and `compactUIMessages`
1284
+ * callbacks if configured.
1285
+ *
1286
+ * @returns `true` if compaction was performed, `false` otherwise.
1287
+ */
1288
+ compactIfNeeded(usage: LanguageModelUsage | undefined, context?: {
1289
+ chatId?: string;
1290
+ turn?: number;
1291
+ clientData?: unknown;
1292
+ totalUsage?: LanguageModelUsage;
1293
+ }): Promise<boolean>;
1294
+ }
1295
+ export type ChatSessionOptions = {
1296
+ /** Run-level cancel signal (from task context). */
1297
+ signal: AbortSignal;
1298
+ /** Seconds to stay idle between turns before suspending. @default 30 */
1299
+ idleTimeoutInSeconds?: number;
1300
+ /** Duration string for suspend timeout. @default "1h" */
1301
+ timeout?: string;
1302
+ /** Max turns before ending. @default 100 */
1303
+ maxTurns?: number;
1304
+ /** Automatic context compaction — same options as `chat.task({ compaction })`. */
1305
+ compaction?: ChatTaskCompactionOptions;
1306
+ };
1307
+ export type ChatTurn = {
1308
+ /** Turn number (0-indexed). */
1309
+ number: number;
1310
+ /** Chat session ID. */
1311
+ chatId: string;
1312
+ /** What triggered this turn. */
1313
+ trigger: string;
1314
+ /** Client data from the transport (`metadata` field on the wire payload). */
1315
+ clientData: unknown;
1316
+ /** Full accumulated model messages — pass directly to `streamText`. */
1317
+ readonly messages: ModelMessage[];
1318
+ /** Full accumulated UI messages — use for persistence. */
1319
+ readonly uiMessages: UIMessage[];
1320
+ /** Combined stop+cancel AbortSignal (fresh each turn). */
1321
+ signal: AbortSignal;
1322
+ /** Whether the user stopped generation this turn. */
1323
+ readonly stopped: boolean;
1324
+ /** Whether this is a continuation run. */
1325
+ continuation: boolean;
1326
+ /** Token usage from the previous turn. Undefined on turn 0. */
1327
+ previousTurnUsage?: LanguageModelUsage;
1328
+ /** Cumulative token usage across all completed turns so far. */
1329
+ totalUsage: LanguageModelUsage;
1330
+ /**
1331
+ * Replace accumulated messages (for compaction). Takes UIMessages and
1332
+ * converts to ModelMessages internally. After calling this, `turn.messages`
1333
+ * reflects the compacted history.
1334
+ */
1335
+ setMessages(uiMessages: UIMessage[]): Promise<void>;
1336
+ /**
1337
+ * Easy path: pipe stream, capture response, accumulate it,
1338
+ * clean up aborted parts if stopped, and write turn-complete chunk.
1339
+ */
1340
+ complete(source: UIMessageStreamable): Promise<UIMessage | undefined>;
1341
+ /**
1342
+ * Manual path: just write turn-complete chunk.
1343
+ * Use when you've already piped and accumulated manually.
1344
+ */
1345
+ done(): Promise<void>;
1346
+ /**
1347
+ * Add the response to the accumulator manually.
1348
+ * Use with `chat.pipeAndCapture` when you need control between pipe and done.
1349
+ */
1350
+ addResponse(response: UIMessage): Promise<void>;
1351
+ };
1352
+ /**
1353
+ * Create a chat session that yields turns as an async iterator.
1354
+ *
1355
+ * Handles: preload wait, stop signals, message accumulation, turn-complete
1356
+ * signaling, and idle/suspend between turns. You control: initialization,
1357
+ * model/tool selection, persistence, and any custom per-turn logic.
1358
+ *
1359
+ * @example
1360
+ * ```ts
1361
+ * import { task } from "@trigger.dev/sdk";
1362
+ * import { chat, type ChatTaskWirePayload } from "@trigger.dev/sdk/ai";
1363
+ * import { streamText } from "ai";
1364
+ * import { openai } from "@ai-sdk/openai";
1365
+ *
1366
+ * export const myChat = task({
1367
+ * id: "my-chat",
1368
+ * run: async (payload: ChatTaskWirePayload, { signal }) => {
1369
+ * const session = chat.createSession(payload, { signal });
1370
+ *
1371
+ * for await (const turn of session) {
1372
+ * const result = streamText({
1373
+ * model: openai("gpt-4o"),
1374
+ * messages: turn.messages,
1375
+ * abortSignal: turn.signal,
1376
+ * });
1377
+ * await turn.complete(result);
1378
+ * }
1379
+ * },
1380
+ * });
1381
+ * ```
1382
+ */
1383
+ declare function createChatSession(payload: ChatTaskWirePayload, options: ChatSessionOptions): AsyncIterable<ChatTurn>;
638
1384
  /**
639
1385
  * A Proxy-backed, run-scoped data object that appears as `T` to users.
640
1386
  * Includes helper methods for initialization, dirty tracking, and serialization.
@@ -722,8 +1468,10 @@ export declare const chat: {
722
1468
  setTurnTimeout: typeof setTurnTimeout;
723
1469
  /** Override the turn timeout at runtime (seconds). See {@link setTurnTimeoutInSeconds}. */
724
1470
  setTurnTimeoutInSeconds: typeof setTurnTimeoutInSeconds;
725
- /** Override the warm timeout at runtime. See {@link setWarmTimeoutInSeconds}. */
726
- setWarmTimeoutInSeconds: typeof setWarmTimeoutInSeconds;
1471
+ /** Override the idle timeout at runtime. See {@link setIdleTimeoutInSeconds}. */
1472
+ setIdleTimeoutInSeconds: typeof setIdleTimeoutInSeconds;
1473
+ /** Override toUIMessageStream() options for the current turn. See {@link setUIMessageStreamOptions}. */
1474
+ setUIMessageStreamOptions: typeof setUIMessageStreamOptions;
727
1475
  /** Check if the current turn was stopped by the user. See {@link isStopped}. */
728
1476
  isStopped: typeof isStopped;
729
1477
  /** Clean up aborted parts from a UIMessage. See {@link cleanupAbortedParts}. */
@@ -732,4 +1480,45 @@ export declare const chat: {
732
1480
  defer: typeof chatDefer;
733
1481
  /** Typed chat output stream for writing custom chunks or piping from subtasks. */
734
1482
  stream: import("@trigger.dev/core/v3").RealtimeDefinedStream<UIMessageChunk>;
1483
+ /** Pre-built input stream for receiving messages from the transport. */
1484
+ messages: import("@trigger.dev/core/v3").RealtimeDefinedInputStream<ChatTaskWirePayload<UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>, unknown>>;
1485
+ /** Create a managed stop signal wired to the stop input stream. See {@link createStopSignal}. */
1486
+ createStopSignal: typeof createStopSignal;
1487
+ /** Signal the frontend that the current turn is complete. See {@link chatWriteTurnComplete}. */
1488
+ writeTurnComplete: typeof chatWriteTurnComplete;
1489
+ /** Pipe a stream and capture the response message. See {@link pipeChatAndCapture}. */
1490
+ pipeAndCapture: typeof pipeChatAndCapture;
1491
+ /** Message accumulator class for raw task chat. See {@link ChatMessageAccumulator}. */
1492
+ MessageAccumulator: typeof ChatMessageAccumulator;
1493
+ /** Create a chat session (async iterator). See {@link createChatSession}. */
1494
+ createSession: typeof createChatSession;
1495
+ /**
1496
+ * Store and retrieve a resolved prompt for the current run.
1497
+ *
1498
+ * - `chat.prompt.set(resolved)` — store a `ResolvedPrompt` or plain string
1499
+ * - `chat.prompt()` — read the stored prompt (throws if not set)
1500
+ */
1501
+ prompt: typeof getChatPrompt & {
1502
+ set: typeof setChatPrompt;
1503
+ };
1504
+ /**
1505
+ * Returns an options object ready to spread into `streamText()`.
1506
+ * Reads the stored prompt and returns `{ system, experimental_telemetry, ...config }`.
1507
+ * Returns `{}` if no prompt has been set.
1508
+ */
1509
+ toStreamTextOptions: typeof toStreamTextOptions;
1510
+ /**
1511
+ * Replace the accumulated conversation messages for compaction.
1512
+ * Call from `onTurnStart` or `onTurnComplete`. Takes `UIMessage[]` and
1513
+ * converts to `ModelMessage[]` internally.
1514
+ */
1515
+ setMessages: typeof setChatMessages;
1516
+ /** Check if it's safe to compact messages (no in-flight tool calls). */
1517
+ isCompactionSafe: typeof isCompactionSafe;
1518
+ /** Returns a `prepareStep` function that handles context compaction automatically. */
1519
+ compactionStep: typeof chatCompactionStep;
1520
+ /** Low-level compaction for use inside a custom `prepareStep`. */
1521
+ compact: typeof chatCompact;
1522
+ /** Read the current compaction state (summary + base message count). */
1523
+ getCompactionState: typeof getCompactionState;
735
1524
  };