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

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 (45) hide show
  1. package/dist/commonjs/v3/ai.d.ts +337 -46
  2. package/dist/commonjs/v3/ai.js +726 -167
  3. package/dist/commonjs/v3/ai.js.map +1 -1
  4. package/dist/commonjs/v3/chat-react.d.ts +91 -2
  5. package/dist/commonjs/v3/chat-react.js +180 -3
  6. package/dist/commonjs/v3/chat-react.js.map +1 -1
  7. package/dist/commonjs/v3/chat.d.ts +63 -5
  8. package/dist/commonjs/v3/chat.js +237 -48
  9. package/dist/commonjs/v3/chat.js.map +1 -1
  10. package/dist/commonjs/v3/chat.test.js +315 -18
  11. package/dist/commonjs/v3/chat.test.js.map +1 -1
  12. package/dist/commonjs/v3/index.d.ts +2 -2
  13. package/dist/commonjs/v3/runs.d.ts +7 -0
  14. package/dist/commonjs/v3/runs.js +1 -0
  15. package/dist/commonjs/v3/runs.js.map +1 -1
  16. package/dist/commonjs/v3/shared.d.ts +24 -2
  17. package/dist/commonjs/v3/shared.js +160 -1
  18. package/dist/commonjs/v3/shared.js.map +1 -1
  19. package/dist/commonjs/v3/tasks.d.ts +2 -1
  20. package/dist/commonjs/v3/tasks.js +1 -0
  21. package/dist/commonjs/v3/tasks.js.map +1 -1
  22. package/dist/commonjs/version.js +1 -1
  23. package/dist/esm/v3/ai.d.ts +337 -46
  24. package/dist/esm/v3/ai.js +727 -168
  25. package/dist/esm/v3/ai.js.map +1 -1
  26. package/dist/esm/v3/chat-react.d.ts +91 -2
  27. package/dist/esm/v3/chat-react.js +181 -5
  28. package/dist/esm/v3/chat-react.js.map +1 -1
  29. package/dist/esm/v3/chat.d.ts +63 -5
  30. package/dist/esm/v3/chat.js +237 -48
  31. package/dist/esm/v3/chat.js.map +1 -1
  32. package/dist/esm/v3/chat.test.js +315 -18
  33. package/dist/esm/v3/chat.test.js.map +1 -1
  34. package/dist/esm/v3/index.d.ts +2 -2
  35. package/dist/esm/v3/runs.d.ts +7 -0
  36. package/dist/esm/v3/runs.js +1 -0
  37. package/dist/esm/v3/runs.js.map +1 -1
  38. package/dist/esm/v3/shared.d.ts +24 -2
  39. package/dist/esm/v3/shared.js +159 -1
  40. package/dist/esm/v3/shared.js.map +1 -1
  41. package/dist/esm/v3/tasks.d.ts +2 -1
  42. package/dist/esm/v3/tasks.js +2 -1
  43. package/dist/esm/v3/tasks.js.map +1 -1
  44. package/dist/esm/version.js +1 -1
  45. package/package.json +3 -3
@@ -1,8 +1,10 @@
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, UIMessageStreamOptions, LanguageModelUsage } from "ai";
3
- import { Tool } from "ai";
1
+ import { AnyTask, Task, type inferSchemaIn, type inferSchemaOut, type TaskIdentifier, type TaskOptions, type TaskSchema, type TaskRunContext, type TaskWithSchema } from "@trigger.dev/core/v3";
2
+ import type { ModelMessage, ToolSet, UIMessage, UIMessageChunk, UIMessageStreamOptions, LanguageModelUsage } from "ai";
3
+ import { Tool, ToolCallOptions } from "ai";
4
4
  import { locals } from "./locals.js";
5
5
  import type { ResolvedPrompt } from "./prompt.js";
6
+ /** Re-export for typing `ctx` in `chat.task` hooks without importing `@trigger.dev/core`. */
7
+ export type { TaskRunContext } from "@trigger.dev/core/v3";
6
8
  import { CHAT_MESSAGES_STREAM_ID, CHAT_STOP_STREAM_ID } from "./chat-constants.js";
7
9
  export type ToolCallExecutionOptions = {
8
10
  toolCallId: string;
@@ -31,16 +33,46 @@ type ToolResultContent = Array<{
31
33
  export type ToolOptions<TResult> = {
32
34
  experimental_toToolResultContent?: (result: TResult) => ToolResultContent;
33
35
  };
34
- declare function toolFromTask<TIdentifier extends string, TInput = void, TOutput = unknown>(task: Task<TIdentifier, TInput, TOutput>, options?: ToolOptions<TOutput>): Tool<TInput, TOutput>;
35
- declare function toolFromTask<TIdentifier extends string, TTaskSchema extends TaskSchema | undefined = undefined, TOutput = unknown>(task: TaskWithSchema<TIdentifier, TTaskSchema, TOutput>, options?: ToolOptions<TOutput>): Tool<inferSchemaIn<TTaskSchema>, TOutput>;
36
+ /** Satisfies AI SDK `ToolSet` index signature alongside concrete `Tool` input/output types. */
37
+ type ToolSetCompatible<T extends Tool<any, any>> = T & NonNullable<ToolSet[string]>;
38
+ /**
39
+ * Returns an `execute` function for the AI SDK `tool()` helper (or any compatible tool definition).
40
+ * Preferred API for task-backed tools: the same Trigger wiring as the deprecated `ai.tool()`
41
+ * (`triggerAndSubscribe`, tool-call metadata, chat context, `chat.local` serialization) without
42
+ * building the tool object. You supply `description`, `inputSchema`, and any AI-SDK-only options
43
+ * (e.g. `experimental_toToolResultContent`) on `tool()` yourself.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import { tool } from "ai";
48
+ * import { z } from "zod";
49
+ * import { ai } from "@trigger.dev/sdk/ai";
50
+ * import { myTask } from "./trigger/myTask";
51
+ *
52
+ * export const myTool = tool({
53
+ * description: myTask.description ?? "",
54
+ * inputSchema: z.object({ id: z.string() }),
55
+ * execute: ai.toolExecute(myTask),
56
+ * });
57
+ * ```
58
+ */
59
+ declare function toolExecute<TIdentifier extends string, TInput = void, TOutput = unknown>(task: Task<TIdentifier, TInput, TOutput>): (input: TInput, toolOpts: ToolCallOptions) => Promise<TOutput>;
60
+ declare function toolExecute<TIdentifier extends string, TTaskSchema extends TaskSchema | undefined = undefined, TOutput = unknown>(task: TaskWithSchema<TIdentifier, TTaskSchema, TOutput>): (input: inferSchemaIn<TTaskSchema>, toolOpts: ToolCallOptions) => Promise<TOutput>;
61
+ /**
62
+ * @deprecated Use `tool()` from the `ai` package with `execute: ai.toolExecute(task)` instead.
63
+ * This helper may be removed in a future major release.
64
+ */
65
+ declare function toolFromTask<TIdentifier extends string, TInput = void, TOutput = unknown>(task: Task<TIdentifier, TInput, TOutput>, options?: ToolOptions<TOutput>): ToolSetCompatible<Tool<TInput, TOutput>>;
66
+ /** @deprecated Use `tool()` from `ai` with `execute: ai.toolExecute(task)`. */
67
+ declare function toolFromTask<TIdentifier extends string, TTaskSchema extends TaskSchema | undefined = undefined, TOutput = unknown>(task: TaskWithSchema<TIdentifier, TTaskSchema, TOutput>, options?: ToolOptions<TOutput>): ToolSetCompatible<Tool<inferSchemaIn<TTaskSchema>, TOutput>>;
36
68
  declare function getToolOptionsFromMetadata(): ToolCallExecutionOptions | undefined;
37
69
  /**
38
- * Get the current tool call ID from inside a subtask invoked via `ai.tool()`.
70
+ * Get the current tool call ID from inside a subtask invoked via `ai.toolExecute()` (or legacy `ai.tool()`).
39
71
  * Returns `undefined` if not running as a tool subtask.
40
72
  */
41
73
  declare function getToolCallId(): string | undefined;
42
74
  /**
43
- * Get the chat context from inside a subtask invoked via `ai.tool()` within a `chat.task`.
75
+ * Get the chat context from inside a subtask invoked via `ai.toolExecute()` (or legacy `ai.tool()`) within a `chat.task`.
44
76
  * Pass `typeof yourChatTask` as the type parameter to get typed `clientData`.
45
77
  * Returns `undefined` if the parent is not a chat task.
46
78
  *
@@ -63,9 +95,17 @@ declare function getToolChatContext<TChatTask extends AnyTask = AnyTask>(): Chat
63
95
  */
64
96
  declare function getToolChatContextOrThrow<TChatTask extends AnyTask = AnyTask>(): ChatTurnContext<InferChatClientData<TChatTask>>;
65
97
  export declare const ai: {
98
+ /**
99
+ * @deprecated Use `tool()` from the `ai` package with `execute: ai.toolExecute(task)` instead.
100
+ */
66
101
  tool: typeof toolFromTask;
102
+ /**
103
+ * Preferred: return value for the `execute` field of AI SDK `tool()`. Keeps Trigger subtask and
104
+ * metadata behavior without coupling to a specific `ai` version’s `Tool` / `ToolSet` types.
105
+ */
106
+ toolExecute: typeof toolExecute;
67
107
  currentToolOptions: typeof getToolOptionsFromMetadata;
68
- /** Get the tool call ID from inside a subtask invoked via `ai.tool()`. */
108
+ /** Get the tool call ID from inside a subtask invoked via `ai.toolExecute()` (or legacy `ai.tool()`). */
69
109
  toolCallId: typeof getToolCallId;
70
110
  /** Get chat context (chatId, turn, clientData, etc.) from inside a subtask of a `chat.task`. Returns undefined if not in a chat context. */
71
111
  chatContext: typeof getToolChatContext;
@@ -97,6 +137,31 @@ declare function createChatAccessToken<TTask extends AnyTask>(taskId: TaskIdenti
97
137
  */
98
138
  export declare const CHAT_STREAM_KEY = "chat";
99
139
  export { CHAT_MESSAGES_STREAM_ID, CHAT_STOP_STREAM_ID };
140
+ /**
141
+ * A stream writer passed to chat lifecycle callbacks (`onPreload`, `onChatStart`,
142
+ * `onTurnStart`, `onTurnComplete`, `onCompacted`).
143
+ *
144
+ * Write custom `UIMessageChunk` parts (e.g. `data-*` parts) directly to the chat
145
+ * stream without the ceremony of `chat.stream.writer({ execute })`.
146
+ *
147
+ * The writer is lazy — no stream overhead if you don't call `write()` or `merge()`.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * onTurnStart: async ({ writer }) => {
152
+ * writer.write({ type: "data-status", data: { loading: true } });
153
+ * },
154
+ * onTurnComplete: async ({ writer, uiMessages }) => {
155
+ * writer.write({ type: "data-analytics", data: { messageCount: uiMessages.length } });
156
+ * },
157
+ * ```
158
+ */
159
+ export type ChatWriter = {
160
+ /** Write a single UIMessageChunk to the chat stream. */
161
+ write(part: UIMessageChunk): void;
162
+ /** Merge another stream's chunks into the chat stream. */
163
+ merge(stream: ReadableStream<UIMessageChunk>): void;
164
+ };
100
165
  /**
101
166
  * The wire payload shape sent by `TriggerChatTransport`.
102
167
  * Uses `metadata` to match the AI SDK's `ChatRequestOptions` field name.
@@ -163,6 +228,11 @@ export type ChatTaskSignals = {
163
228
  * Extends `ChatTaskPayload` (the wire payload) with abort signals.
164
229
  */
165
230
  export type ChatTaskRunPayload<TClientData = unknown> = ChatTaskPayload<TClientData> & ChatTaskSignals & {
231
+ /**
232
+ * Task run context — same object as the `ctx` passed to a standard `task({ run })` handler’s second argument.
233
+ * Use for tags, metadata, parent run links, or any API that needs the full run record.
234
+ */
235
+ ctx: TaskRunContext;
166
236
  /** Token usage from the previous turn. Undefined on turn 0. */
167
237
  previousTurnUsage?: LanguageModelUsage;
168
238
  /** Cumulative token usage across all completed turns so far. */
@@ -177,7 +247,7 @@ export type ChatTurnUsage = LanguageModelUsage;
177
247
  * `onTurnComplete` to compact before the next turn. Takes `UIMessage[]`
178
248
  * and converts to `ModelMessage[]` internally.
179
249
  */
180
- declare function setChatMessages(uiMessages: UIMessage[]): void;
250
+ declare function setChatMessages<TUIM extends UIMessage = UIMessage>(uiMessages: TUIM[]): void;
181
251
  /** State stored in locals during prepareStep compaction. */
182
252
  interface CompactionState {
183
253
  summary: string;
@@ -211,11 +281,11 @@ export type SummarizeEvent = {
211
281
  /**
212
282
  * Event passed to `compactUIMessages` and `compactModelMessages` callbacks.
213
283
  */
214
- export type CompactMessagesEvent = {
284
+ export type CompactMessagesEvent<TUIM extends UIMessage = UIMessage> = {
215
285
  /** The generated summary text. */
216
286
  summary: string;
217
287
  /** The current UI messages (full conversation). */
218
- uiMessages: UIMessage[];
288
+ uiMessages: TUIM[];
219
289
  /** The current model messages (full conversation). */
220
290
  modelMessages: ModelMessage[];
221
291
  /** The chat session ID. */
@@ -238,7 +308,7 @@ export type CompactMessagesEvent = {
238
308
  * tool-call steps) and the outer loop (between turns, for single-step responses
239
309
  * where prepareStep never fires).
240
310
  */
241
- export type ChatTaskCompactionOptions = {
311
+ export type ChatTaskCompactionOptions<TUIM extends UIMessage = UIMessage> = {
242
312
  /** Decide whether to compact. Return true to trigger compaction. */
243
313
  shouldCompact: (event: ShouldCompactEvent) => boolean | Promise<boolean>;
244
314
  /** Generate a summary from the current messages. Return the summary text. */
@@ -263,7 +333,7 @@ export type ChatTaskCompactionOptions = {
263
333
  * ],
264
334
  * ```
265
335
  */
266
- compactUIMessages?: (event: CompactMessagesEvent) => UIMessage[] | Promise<UIMessage[]>;
336
+ compactUIMessages?: (event: CompactMessagesEvent<TUIM>) => TUIM[] | Promise<TUIM[]>;
267
337
  /**
268
338
  * Transform model messages after compaction (what gets sent to the LLM).
269
339
  * Default: replace all with a single summary message.
@@ -277,8 +347,86 @@ export type ChatTaskCompactionOptions = {
277
347
  * ],
278
348
  * ```
279
349
  */
280
- compactModelMessages?: (event: CompactMessagesEvent) => ModelMessage[] | Promise<ModelMessage[]>;
350
+ compactModelMessages?: (event: CompactMessagesEvent<TUIM>) => ModelMessage[] | Promise<ModelMessage[]>;
351
+ };
352
+ /**
353
+ * Event passed to `shouldInject` and `prepareMessages` callbacks.
354
+ */
355
+ export type PendingMessagesBatchEvent<TUIM extends UIMessage = UIMessage> = {
356
+ /** All pending UI messages that arrived during streaming (batch). */
357
+ messages: TUIM[];
358
+ /** Current model messages in the conversation. */
359
+ modelMessages: ModelMessage[];
360
+ /** Completed steps so far. */
361
+ steps: CompactionStep[];
362
+ /** Current step number (0-indexed). */
363
+ stepNumber: number;
364
+ /** Chat session ID. */
365
+ chatId: string;
366
+ /** Current turn number (0-indexed). */
367
+ turn: number;
368
+ /** Custom data from the frontend. */
369
+ clientData?: unknown;
370
+ };
371
+ /**
372
+ * Event passed to `onReceived` callback (per-message, as they arrive).
373
+ */
374
+ export type PendingMessageReceivedEvent<TUIM extends UIMessage = UIMessage> = {
375
+ /** The UI message that arrived during streaming. */
376
+ message: TUIM;
377
+ /** Chat session ID. */
378
+ chatId: string;
379
+ /** Current turn number (0-indexed). */
380
+ turn: number;
381
+ };
382
+ /**
383
+ * Event passed to `onInjected` callback (batch, after injection).
384
+ */
385
+ export type PendingMessagesInjectedEvent<TUIM extends UIMessage = UIMessage> = {
386
+ /** All UI messages that were injected. */
387
+ messages: TUIM[];
388
+ /** The model messages that were injected. */
389
+ injectedModelMessages: ModelMessage[];
390
+ /** Chat session ID. */
391
+ chatId: string;
392
+ /** Current turn number (0-indexed). */
393
+ turn: number;
394
+ /** Step number where injection occurred. */
395
+ stepNumber: number;
396
+ };
397
+ /**
398
+ * Options for the `pendingMessages` field on `chat.task()`, `chat.createSession()`,
399
+ * or `ChatMessageAccumulator`.
400
+ *
401
+ * Configures how messages that arrive during streaming are handled. When
402
+ * `shouldInject` is provided and returns `true`, the full batch of pending
403
+ * messages is injected between tool-call steps via `prepareStep`.
404
+ * Otherwise, messages queue for the next turn.
405
+ */
406
+ export type PendingMessagesOptions<TUIM extends UIMessage = UIMessage> = {
407
+ /**
408
+ * Decide whether to inject pending messages between tool-call steps.
409
+ * Called once per step boundary with the full batch of pending messages.
410
+ * If absent, no injection happens — messages only queue for the next turn.
411
+ */
412
+ shouldInject?: (event: PendingMessagesBatchEvent<TUIM>) => boolean | Promise<boolean>;
413
+ /**
414
+ * Transform the batch of pending messages before injection.
415
+ * Return the model messages to inject.
416
+ * Default: convert each UI message via `convertToModelMessages`.
417
+ */
418
+ prepare?: (event: PendingMessagesBatchEvent<TUIM>) => ModelMessage[] | Promise<ModelMessage[]>;
419
+ /** Called when a message arrives during streaming (per-message). */
420
+ onReceived?: (event: PendingMessageReceivedEvent<TUIM>) => void | Promise<void>;
421
+ /** Called after a batch of messages is injected via `prepareStep`. */
422
+ onInjected?: (event: PendingMessagesInjectedEvent<TUIM>) => void | Promise<void>;
281
423
  };
424
+ /**
425
+ * The data part type used to signal that pending messages were injected
426
+ * between tool-call steps. The frontend can match on this to render
427
+ * injection points inline in the assistant response.
428
+ */
429
+ export declare const PENDING_MESSAGE_INJECTED_TYPE: "data-pending-message-injected";
282
430
  /**
283
431
  * Event passed to the `prepareMessages` hook.
284
432
  */
@@ -306,6 +454,8 @@ export type CompactionChunkData = {
306
454
  * Event passed to the `onCompacted` callback.
307
455
  */
308
456
  export type CompactedEvent = {
457
+ /** Task run context — same as `task` lifecycle hooks and `chat.task` `run({ ctx })`. */
458
+ ctx: TaskRunContext;
309
459
  /** The generated summary text. */
310
460
  summary: string;
311
461
  /** The messages that were compacted (pre-compaction). */
@@ -326,6 +476,8 @@ export type CompactedEvent = {
326
476
  chatId?: string;
327
477
  /** The current turn number (if running inside a chat.task). */
328
478
  turn?: number;
479
+ /** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
480
+ writer: ChatWriter;
329
481
  };
330
482
  /**
331
483
  * Event passed to `shouldCompact` callbacks.
@@ -576,7 +728,7 @@ export type PipeChatOptions = {
576
728
  * Use `streamText`'s `onFinish` for custom finish handling, or drop down to
577
729
  * raw task mode with `chat.pipe()` for full control.
578
730
  */
579
- export type ChatUIMessageStreamOptions = Omit<UIMessageStreamOptions<UIMessage>, "onFinish" | "originalMessages" | "generateMessageId">;
731
+ export type ChatUIMessageStreamOptions<TUIM extends UIMessage = UIMessage> = Omit<UIMessageStreamOptions<TUIM>, "onFinish" | "originalMessages" | "generateMessageId">;
580
732
  /**
581
733
  * An object with a `toUIMessageStream()` method (e.g. `StreamTextResult` from `streamText()`).
582
734
  */
@@ -641,6 +793,8 @@ declare function pipeChat(source: UIMessageStreamable | AsyncIterable<unknown> |
641
793
  * Event passed to the `onPreload` callback.
642
794
  */
643
795
  export type PreloadEvent<TClientData = unknown> = {
796
+ /** Task run context — same as `task({ run })` second-argument `ctx`. */
797
+ ctx: TaskRunContext;
644
798
  /** The unique identifier for the chat session. */
645
799
  chatId: string;
646
800
  /** The Trigger.dev run ID for this conversation. */
@@ -649,11 +803,15 @@ export type PreloadEvent<TClientData = unknown> = {
649
803
  chatAccessToken: string;
650
804
  /** Custom data from the frontend. */
651
805
  clientData?: TClientData;
806
+ /** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
807
+ writer: ChatWriter;
652
808
  };
653
809
  /**
654
810
  * Event passed to the `onChatStart` callback.
655
811
  */
656
812
  export type ChatStartEvent<TClientData = unknown> = {
813
+ /** Task run context — same as `task({ run })` second-argument `ctx`. */
814
+ ctx: TaskRunContext;
657
815
  /** The unique identifier for the chat session. */
658
816
  chatId: string;
659
817
  /** The initial model-ready messages for this conversation. */
@@ -670,17 +828,21 @@ export type ChatStartEvent<TClientData = unknown> = {
670
828
  previousRunId?: string;
671
829
  /** Whether this run was preloaded before the first message. */
672
830
  preloaded: boolean;
831
+ /** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
832
+ writer: ChatWriter;
673
833
  };
674
834
  /**
675
835
  * Event passed to the `onTurnStart` callback.
676
836
  */
677
- export type TurnStartEvent<TClientData = unknown> = {
837
+ export type TurnStartEvent<TClientData = unknown, TUIM extends UIMessage = UIMessage> = {
838
+ /** Task run context — same as `task({ run })` second-argument `ctx`. */
839
+ ctx: TaskRunContext;
678
840
  /** The unique identifier for the chat session. */
679
841
  chatId: string;
680
842
  /** The accumulated model-ready messages (all turns so far, including new user message). */
681
843
  messages: ModelMessage[];
682
844
  /** The accumulated UI messages (all turns so far, including new user message). */
683
- uiMessages: UIMessage[];
845
+ uiMessages: TUIM[];
684
846
  /** The turn number (0-indexed). */
685
847
  turn: number;
686
848
  /** The Trigger.dev run ID for this conversation. */
@@ -699,11 +861,15 @@ export type TurnStartEvent<TClientData = unknown> = {
699
861
  previousTurnUsage?: LanguageModelUsage;
700
862
  /** Cumulative token usage across all completed turns so far. */
701
863
  totalUsage: LanguageModelUsage;
864
+ /** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
865
+ writer: ChatWriter;
702
866
  };
703
867
  /**
704
868
  * Event passed to the `onTurnComplete` callback.
705
869
  */
706
- export type TurnCompleteEvent<TClientData = unknown> = {
870
+ export type TurnCompleteEvent<TClientData = unknown, TUIM extends UIMessage = UIMessage> = {
871
+ /** Task run context — same as `task({ run })` second-argument `ctx`. */
872
+ ctx: TaskRunContext;
707
873
  /** The unique identifier for the chat session. */
708
874
  chatId: string;
709
875
  /** The full accumulated conversation in model format (all turns so far). */
@@ -712,7 +878,7 @@ export type TurnCompleteEvent<TClientData = unknown> = {
712
878
  * The full accumulated conversation in UI format (all turns so far).
713
879
  * This is the format expected by `useChat` — store this for persistence.
714
880
  */
715
- uiMessages: UIMessage[];
881
+ uiMessages: TUIM[];
716
882
  /**
717
883
  * Only the new model messages from this turn (user message(s) + assistant response).
718
884
  * Useful for appending to an existing conversation record.
@@ -722,15 +888,15 @@ export type TurnCompleteEvent<TClientData = unknown> = {
722
888
  * Only the new UI messages from this turn (user message(s) + assistant response).
723
889
  * Useful for inserting individual message records instead of overwriting the full history.
724
890
  */
725
- newUIMessages: UIMessage[];
891
+ newUIMessages: TUIM[];
726
892
  /** The assistant's response for this turn, with aborted parts cleaned up when `stopped` is true. Undefined if `pipeChat` was used manually. */
727
- responseMessage: UIMessage | undefined;
893
+ responseMessage: TUIM | undefined;
728
894
  /**
729
895
  * The raw assistant response before abort cleanup. Includes incomplete tool parts
730
896
  * (`input-available`, `partial-call`) and streaming reasoning/text parts.
731
897
  * Use this if you need custom cleanup logic. Same as `responseMessage` when not stopped.
732
898
  */
733
- rawResponseMessage: UIMessage | undefined;
899
+ rawResponseMessage: TUIM | undefined;
734
900
  /** The turn number (0-indexed). */
735
901
  turn: number;
736
902
  /** The Trigger.dev run ID for this conversation. */
@@ -754,7 +920,15 @@ export type TurnCompleteEvent<TClientData = unknown> = {
754
920
  /** Cumulative token usage across all turns in this run (including this turn). */
755
921
  totalUsage: LanguageModelUsage;
756
922
  };
757
- export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extends TaskSchema | undefined = undefined> = Omit<TaskOptions<TIdentifier, ChatTaskWirePayload, unknown>, "run"> & {
923
+ /**
924
+ * Event passed to the `onBeforeTurnComplete` callback.
925
+ * Same as `TurnCompleteEvent` but includes a `writer` since the stream is still open.
926
+ */
927
+ export type BeforeTurnCompleteEvent<TClientData = unknown, TUIM extends UIMessage = UIMessage> = TurnCompleteEvent<TClientData, TUIM> & {
928
+ /** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
929
+ writer: ChatWriter;
930
+ };
931
+ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extends TaskSchema | undefined = undefined, TUIMessage extends UIMessage = UIMessage> = Omit<TaskOptions<TIdentifier, ChatTaskWirePayload<TUIMessage, inferSchemaIn<TClientDataSchema>>, unknown>, "run"> & {
758
932
  /**
759
933
  * Schema for validating `clientData` from the frontend.
760
934
  * Accepts Zod, ArkType, Valibot, or any supported schema library.
@@ -767,8 +941,9 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
767
941
  * chat.task({
768
942
  * id: "my-chat",
769
943
  * clientDataSchema: z.object({ model: z.string().optional(), userId: z.string() }),
770
- * run: async ({ messages, clientData, signal }) => {
944
+ * run: async ({ messages, clientData, ctx, signal }) => {
771
945
  * // clientData is typed as { model?: string; userId: string }
946
+ * // ctx is the same TaskRunContext as in task({ run: (payload, { ctx }) => ... })
772
947
  * },
773
948
  * });
774
949
  * ```
@@ -778,7 +953,8 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
778
953
  * The run function for the chat task.
779
954
  *
780
955
  * Receives a `ChatTaskRunPayload` with the conversation messages, chat session ID,
781
- * trigger type, and abort signals (`signal`, `cancelSignal`, `stopSignal`).
956
+ * trigger type, task `ctx` (same as `task({ run })`’s second argument), and abort signals
957
+ * (`signal`, `cancelSignal`, `stopSignal`).
782
958
  *
783
959
  * **Auto-piping:** If this function returns a value with `.toUIMessageStream()`,
784
960
  * the stream is automatically piped to the frontend.
@@ -792,7 +968,7 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
792
968
  *
793
969
  * @example
794
970
  * ```ts
795
- * onPreload: async ({ chatId, clientData }) => {
971
+ * onPreload: async ({ ctx, chatId, clientData }) => {
796
972
  * await db.chat.create({ data: { id: chatId } });
797
973
  * userContext.init(await loadUser(clientData.userId));
798
974
  * }
@@ -806,7 +982,7 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
806
982
  *
807
983
  * @example
808
984
  * ```ts
809
- * onChatStart: async ({ chatId, messages, clientData }) => {
985
+ * onChatStart: async ({ ctx, chatId, messages, clientData }) => {
810
986
  * await db.chat.create({ data: { id: chatId, userId: clientData.userId } });
811
987
  * }
812
988
  * ```
@@ -821,12 +997,12 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
821
997
  *
822
998
  * @example
823
999
  * ```ts
824
- * onTurnStart: async ({ chatId, uiMessages }) => {
1000
+ * onTurnStart: async ({ ctx, chatId, uiMessages }) => {
825
1001
  * await db.chat.update({ where: { id: chatId }, data: { messages: uiMessages } });
826
1002
  * }
827
1003
  * ```
828
1004
  */
829
- onTurnStart?: (event: TurnStartEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void;
1005
+ onTurnStart?: (event: TurnStartEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void;
830
1006
  /**
831
1007
  * Called after the response is captured but before the stream closes.
832
1008
  * The stream is still open, so you can write custom chunks to the frontend
@@ -835,24 +1011,24 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
835
1011
  *
836
1012
  * @example
837
1013
  * ```ts
838
- * onBeforeTurnComplete: async ({ messages, uiMessages, usage }) => {
1014
+ * onBeforeTurnComplete: async ({ ctx, writer, usage }) => {
839
1015
  * if (usage?.inputTokens && usage.inputTokens > 5000) {
840
- * await chat.stream.append({ type: "data-compaction", id: generateId(), data: { status: "compacting" } });
1016
+ * writer.write({ type: "data-compaction", id: generateId(), data: { status: "compacting" } });
841
1017
  * // ... compact messages ...
842
1018
  * chat.setMessages(compactedMessages);
843
- * await chat.stream.append({ type: "data-compaction", id: generateId(), data: { status: "complete" } });
1019
+ * writer.write({ type: "data-compaction", id: generateId(), data: { status: "complete" } });
844
1020
  * }
845
1021
  * }
846
1022
  * ```
847
1023
  */
848
- onBeforeTurnComplete?: (event: TurnCompleteEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void;
1024
+ onBeforeTurnComplete?: (event: BeforeTurnCompleteEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void;
849
1025
  /**
850
1026
  * Called when conversation compaction occurs (via `chat.compact()` or
851
1027
  * `chat.compactionStep()`). Use for logging, billing, or persisting the summary.
852
1028
  *
853
1029
  * @example
854
1030
  * ```ts
855
- * onCompacted: async ({ summary, totalTokens, chatId }) => {
1031
+ * onCompacted: async ({ ctx, summary, totalTokens, chatId }) => {
856
1032
  * logger.info("Compacted", { totalTokens, chatId });
857
1033
  * await db.compactionLog.create({ data: { chatId, summary } });
858
1034
  * }
@@ -886,19 +1062,35 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
886
1062
  * });
887
1063
  * ```
888
1064
  */
889
- compaction?: ChatTaskCompactionOptions;
1065
+ compaction?: ChatTaskCompactionOptions<TUIMessage>;
890
1066
  /**
891
- * Called after the stream closes for this turn. Use this to persist the
1067
+ * Configure how messages that arrive during streaming are handled.
1068
+ *
1069
+ * By default, messages queue for the next turn. When `shouldInject` is provided
1070
+ * and returns `true`, messages are injected between tool-call steps via
1071
+ * `prepareStep` — allowing users to steer the agent mid-execution.
1072
+ *
1073
+ * @example
1074
+ * ```ts
1075
+ * pendingMessages: {
1076
+ * shouldInject: ({ steps }) => steps.length > 0,
1077
+ * onReceived: ({ message }) => logger.info("Steering message received"),
1078
+ * },
1079
+ * ```
1080
+ */
1081
+ pendingMessages?: PendingMessagesOptions<TUIMessage>;
1082
+ /**
1083
+ * Called after each assistant response completes. Use to persist the
892
1084
  * conversation to your database after each assistant response.
893
1085
  *
894
1086
  * @example
895
1087
  * ```ts
896
- * onTurnComplete: async ({ chatId, messages }) => {
1088
+ * onTurnComplete: async ({ ctx, chatId, messages }) => {
897
1089
  * await db.chat.update({ where: { id: chatId }, data: { messages } });
898
1090
  * }
899
1091
  * ```
900
1092
  */
901
- onTurnComplete?: (event: TurnCompleteEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void;
1093
+ onTurnComplete?: (event: TurnCompleteEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void;
902
1094
  /**
903
1095
  * Maximum number of conversational turns (message round-trips) a single run
904
1096
  * will handle before ending. After this many turns the run completes
@@ -1001,7 +1193,7 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
1001
1193
  * });
1002
1194
  * ```
1003
1195
  */
1004
- uiMessageStreamOptions?: ChatUIMessageStreamOptions;
1196
+ uiMessageStreamOptions?: ChatUIMessageStreamOptions<TUIMessage>;
1005
1197
  };
1006
1198
  /**
1007
1199
  * Creates a Trigger.dev task pre-configured for AI SDK chat.
@@ -1030,7 +1222,34 @@ export type ChatTaskOptions<TIdentifier extends string, TClientDataSchema extend
1030
1222
  * });
1031
1223
  * ```
1032
1224
  */
1033
- declare function chatTask<TIdentifier extends string, TClientDataSchema extends TaskSchema | undefined = undefined>(options: ChatTaskOptions<TIdentifier, TClientDataSchema>): Task<TIdentifier, ChatTaskWirePayload<UIMessage, inferSchemaIn<TClientDataSchema>>, unknown>;
1225
+ declare function chatTask<TIdentifier extends string, TClientDataSchema extends TaskSchema | undefined = undefined, TUIMessage extends UIMessage = UIMessage>(options: ChatTaskOptions<TIdentifier, TClientDataSchema, TUIMessage>): Task<TIdentifier, ChatTaskWirePayload<TUIMessage, inferSchemaIn<TClientDataSchema>>, unknown>;
1226
+ /**
1227
+ * Optional config for {@link chat.withUIMessage}. `streamOptions` become default
1228
+ * static `toUIMessageStream()` settings; inner `chat.task({ uiMessageStreamOptions })`
1229
+ * shallow-merges on top (task wins on conflicts).
1230
+ */
1231
+ export type ChatWithUIMessageConfig<TUIM extends UIMessage = UIMessage> = {
1232
+ streamOptions?: ChatUIMessageStreamOptions<TUIM>;
1233
+ };
1234
+ /**
1235
+ * Fix the UI message type for a chat task (AI SDK `UIMessage` generics) while
1236
+ * keeping `id` and `clientDataSchema` inference on the inner {@link chat.task} call.
1237
+ *
1238
+ * @example
1239
+ * ```ts
1240
+ * type AgentUiMessage = UIMessage<unknown, UIDataTypes, UITools>;
1241
+ *
1242
+ * export const myChat = chat.withUIMessage<AgentUiMessage>({
1243
+ * streamOptions: { sendReasoning: true },
1244
+ * }).task({
1245
+ * id: "my-chat",
1246
+ * run: async ({ messages, signal }) => { ... },
1247
+ * });
1248
+ * ```
1249
+ */
1250
+ declare function withUIMessage<TUIM extends UIMessage = UIMessage>(config?: ChatWithUIMessageConfig<TUIM>): {
1251
+ task: <TIdentifier extends string, TClientDataSchema extends TaskSchema | undefined = undefined>(options: ChatTaskOptions<TIdentifier, TClientDataSchema, TUIM>) => Task<TIdentifier, ChatTaskWirePayload<TUIM, inferSchemaIn<TClientDataSchema>>, unknown>;
1252
+ };
1034
1253
  /**
1035
1254
  * Override the turn timeout for subsequent turns in the current run.
1036
1255
  *
@@ -1104,7 +1323,7 @@ declare function setIdleTimeoutInSeconds(seconds: number): void;
1104
1323
  * }
1105
1324
  * ```
1106
1325
  */
1107
- declare function setUIMessageStreamOptions(options: ChatUIMessageStreamOptions): void;
1326
+ declare function setUIMessageStreamOptions(options: ChatUIMessageStreamOptions<UIMessage>): void;
1108
1327
  /**
1109
1328
  * Check whether the user stopped generation during the current turn.
1110
1329
  *
@@ -1142,6 +1361,34 @@ declare function isStopped(): boolean;
1142
1361
  * ```
1143
1362
  */
1144
1363
  declare function chatDefer(promise: Promise<unknown>): void;
1364
+ /**
1365
+ * Queue model messages for injection at the next `prepareStep` boundary.
1366
+ *
1367
+ * Use this to inject context from background work into the agent's conversation.
1368
+ * Messages are appended to the model messages before the next LLM inference call.
1369
+ *
1370
+ * Combine with `chat.defer()` to run background analysis and inject results:
1371
+ *
1372
+ * @example
1373
+ * ```ts
1374
+ * onTurnComplete: async ({ messages }) => {
1375
+ * chat.defer((async () => {
1376
+ * const review = await generateObject({
1377
+ * model: openai("gpt-4o-mini"),
1378
+ * messages: [...messages, { role: "user", content: "Review the last response." }],
1379
+ * schema: z.object({ suggestions: z.array(z.string()) }),
1380
+ * });
1381
+ * if (review.object.suggestions.length > 0) {
1382
+ * chat.inject([{
1383
+ * role: "system",
1384
+ * content: `Improvements for next response:\n${review.object.suggestions.join("\n")}`,
1385
+ * }]);
1386
+ * }
1387
+ * })());
1388
+ * },
1389
+ * ```
1390
+ */
1391
+ declare function injectBackgroundContext(messages: ModelMessage[]): void;
1145
1392
  /**
1146
1393
  * Clean up a UIMessage that was captured during an aborted/stopped turn.
1147
1394
  *
@@ -1169,7 +1416,7 @@ declare function chatDefer(promise: Promise<unknown>): void;
1169
1416
  * }
1170
1417
  * ```
1171
1418
  */
1172
- declare function cleanupAbortedParts(message: UIMessage): UIMessage;
1419
+ declare function cleanupAbortedParts<TUIM extends UIMessage>(message: TUIM): TUIM;
1173
1420
  /**
1174
1421
  * Create a managed stop signal wired to the chat stop input stream.
1175
1422
  *
@@ -1249,8 +1496,11 @@ declare class ChatMessageAccumulator {
1249
1496
  modelMessages: ModelMessage[];
1250
1497
  uiMessages: UIMessage[];
1251
1498
  private _compaction?;
1499
+ private _pendingMessages?;
1500
+ private _steeringQueue;
1252
1501
  constructor(options?: {
1253
1502
  compaction?: ChatTaskCompactionOptions;
1503
+ pendingMessages?: PendingMessagesOptions;
1254
1504
  });
1255
1505
  /**
1256
1506
  * Add incoming messages from the transport payload.
@@ -1268,9 +1518,21 @@ declare class ChatMessageAccumulator {
1268
1518
  setMessages(uiMessages: UIMessage[]): Promise<void>;
1269
1519
  addResponse(response: UIMessage): Promise<void>;
1270
1520
  /**
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() })`.
1521
+ * Queue a message for injection via `prepareStep`. Call from a
1522
+ * `messagesInput.on()` listener when a message arrives during streaming.
1523
+ */
1524
+ steer(message: UIMessage, modelMessages?: ModelMessage[]): void;
1525
+ /**
1526
+ * Queue a message for injection, converting to model messages automatically.
1527
+ */
1528
+ steerAsync(message: UIMessage): Promise<void>;
1529
+ /**
1530
+ * Get and clear unconsumed steering messages.
1531
+ */
1532
+ drainSteering(): UIMessage[];
1533
+ /**
1534
+ * Returns a `prepareStep` function that handles both compaction and
1535
+ * pending message injection. Pass to `streamText({ prepareStep: conversation.prepareStep() })`.
1274
1536
  */
1275
1537
  prepareStep(): ((args: {
1276
1538
  messages: ModelMessage[];
@@ -1303,6 +1565,8 @@ export type ChatSessionOptions = {
1303
1565
  maxTurns?: number;
1304
1566
  /** Automatic context compaction — same options as `chat.task({ compaction })`. */
1305
1567
  compaction?: ChatTaskCompactionOptions;
1568
+ /** Configure mid-execution message injection — same options as `chat.task({ pendingMessages })`. */
1569
+ pendingMessages?: PendingMessagesOptions;
1306
1570
  };
1307
1571
  export type ChatTurn = {
1308
1572
  /** Turn number (0-indexed). */
@@ -1348,6 +1612,17 @@ export type ChatTurn = {
1348
1612
  * Use with `chat.pipeAndCapture` when you need control between pipe and done.
1349
1613
  */
1350
1614
  addResponse(response: UIMessage): Promise<void>;
1615
+ /**
1616
+ * Returns a `prepareStep` function that handles both compaction and
1617
+ * pending message injection. Pass to `streamText({ prepareStep: turn.prepareStep() })`.
1618
+ * Only needed when not using `chat.toStreamTextOptions()` (which auto-injects it).
1619
+ */
1620
+ prepareStep(): ((args: {
1621
+ messages: ModelMessage[];
1622
+ steps: CompactionStep[];
1623
+ }) => Promise<{
1624
+ messages: ModelMessage[];
1625
+ } | undefined>) | undefined;
1351
1626
  };
1352
1627
  /**
1353
1628
  * Create a chat session that yields turns as an async iterator.
@@ -1407,7 +1682,7 @@ export type ChatLocal<T extends Record<string, unknown>> = T & {
1407
1682
  *
1408
1683
  * The `id` is required and must be unique across all `chat.local()` calls in
1409
1684
  * your project. It's used to serialize values into subtask metadata so that
1410
- * `ai.tool()` subtasks can auto-hydrate parent locals (read-only).
1685
+ * `ai.toolExecute()` (or legacy `ai.tool()`) subtasks can auto-hydrate parent locals (read-only).
1411
1686
  *
1412
1687
  * @example
1413
1688
  * ```ts
@@ -1455,9 +1730,23 @@ declare function chatLocal<T extends Record<string, unknown>>(options: {
1455
1730
  * ```
1456
1731
  */
1457
1732
  export type InferChatClientData<TTask extends AnyTask> = TTask extends Task<string, ChatTaskWirePayload<any, infer TMetadata>, any> ? TMetadata : unknown;
1733
+ /**
1734
+ * Extracts the UI message type from a chat task (wire payload `messages` items).
1735
+ *
1736
+ * @example
1737
+ * ```ts
1738
+ * import type { InferChatUIMessage } from "@trigger.dev/sdk/ai";
1739
+ * import type { myChat } from "@/trigger/chat";
1740
+ *
1741
+ * type Msg = InferChatUIMessage<typeof myChat>;
1742
+ * ```
1743
+ */
1744
+ export type InferChatUIMessage<TTask extends AnyTask> = TTask extends Task<string, ChatTaskWirePayload<infer TUIM extends UIMessage, any>, any> ? TUIM : UIMessage;
1458
1745
  export declare const chat: {
1459
1746
  /** Create a chat task. See {@link chatTask}. */
1460
1747
  task: typeof chatTask;
1748
+ /** Create a chat task with a fixed {@link UIMessage} subtype and optional default stream options. See {@link withUIMessage}. */
1749
+ withUIMessage: typeof withUIMessage;
1461
1750
  /** Pipe a stream to the chat transport. See {@link pipeChat}. */
1462
1751
  pipe: typeof pipeChat;
1463
1752
  /** Create a per-run typed local. See {@link chatLocal}. */
@@ -1478,6 +1767,8 @@ export declare const chat: {
1478
1767
  cleanupAbortedParts: typeof cleanupAbortedParts;
1479
1768
  /** Register background work that runs in parallel with streaming. See {@link chatDefer}. */
1480
1769
  defer: typeof chatDefer;
1770
+ /** Queue model messages for injection at the next `prepareStep` boundary. See {@link injectBackgroundContext}. */
1771
+ inject: typeof injectBackgroundContext;
1481
1772
  /** Typed chat output stream for writing custom chunks or piping from subtasks. */
1482
1773
  stream: import("@trigger.dev/core/v3").RealtimeDefinedStream<UIMessageChunk>;
1483
1774
  /** Pre-built input stream for receiving messages from the transport. */