@trigger.dev/sdk 4.4.6 → 4.5.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/v3/agentSkillsRuntime.d.ts +28 -0
- package/dist/commonjs/v3/agentSkillsRuntime.js +163 -0
- package/dist/commonjs/v3/agentSkillsRuntime.js.map +1 -0
- package/dist/commonjs/v3/ai-shared.d.ts +173 -0
- package/dist/commonjs/v3/ai-shared.js +25 -0
- package/dist/commonjs/v3/ai-shared.js.map +1 -0
- package/dist/commonjs/v3/ai.d.ts +2823 -5
- package/dist/commonjs/v3/ai.js +6197 -13
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/auth.d.ts +9 -0
- package/dist/commonjs/v3/auth.js.map +1 -1
- package/dist/commonjs/v3/chat-client.d.ts +301 -0
- package/dist/commonjs/v3/chat-client.js +624 -0
- package/dist/commonjs/v3/chat-client.js.map +1 -0
- package/dist/commonjs/v3/chat-react.d.ts +155 -0
- package/dist/commonjs/v3/chat-react.js +330 -0
- package/dist/commonjs/v3/chat-react.js.map +1 -0
- package/dist/commonjs/v3/chat-server.d.ts +206 -0
- package/dist/commonjs/v3/chat-server.js +737 -0
- package/dist/commonjs/v3/chat-server.js.map +1 -0
- package/dist/commonjs/v3/chat-server.test.d.ts +1 -0
- package/dist/commonjs/v3/chat-server.test.js +518 -0
- package/dist/commonjs/v3/chat-server.test.js.map +1 -0
- package/dist/commonjs/v3/chat-tab-coordinator.d.ts +65 -0
- package/dist/commonjs/v3/chat-tab-coordinator.js +235 -0
- package/dist/commonjs/v3/chat-tab-coordinator.js.map +1 -0
- package/dist/commonjs/v3/chat-tab-coordinator.test.d.ts +1 -0
- package/dist/commonjs/v3/chat-tab-coordinator.test.js +140 -0
- package/dist/commonjs/v3/chat-tab-coordinator.test.js.map +1 -0
- package/dist/commonjs/v3/chat.d.ts +437 -0
- package/dist/commonjs/v3/chat.js +968 -0
- package/dist/commonjs/v3/chat.js.map +1 -0
- package/dist/commonjs/v3/chat.test.d.ts +1 -0
- package/dist/commonjs/v3/chat.test.js +1180 -0
- package/dist/commonjs/v3/chat.test.js.map +1 -0
- package/dist/commonjs/v3/createStartSessionAction.test.d.ts +1 -0
- package/dist/commonjs/v3/createStartSessionAction.test.js +113 -0
- package/dist/commonjs/v3/createStartSessionAction.test.js.map +1 -0
- package/dist/commonjs/v3/deployments.d.ts +26 -0
- package/dist/commonjs/v3/deployments.js +37 -0
- package/dist/commonjs/v3/deployments.js.map +1 -0
- package/dist/commonjs/v3/index.d.ts +6 -3
- package/dist/commonjs/v3/index.js +7 -1
- package/dist/commonjs/v3/index.js.map +1 -1
- package/dist/commonjs/v3/runs.d.ts +22 -7
- package/dist/commonjs/v3/runs.js +1 -0
- package/dist/commonjs/v3/runs.js.map +1 -1
- package/dist/commonjs/v3/sessions.d.ts +228 -0
- package/dist/commonjs/v3/sessions.js +664 -0
- package/dist/commonjs/v3/sessions.js.map +1 -0
- package/dist/commonjs/v3/sessions.test.d.ts +1 -0
- package/dist/commonjs/v3/sessions.test.js +154 -0
- package/dist/commonjs/v3/sessions.test.js.map +1 -0
- package/dist/commonjs/v3/shared.d.ts +24 -2
- package/dist/commonjs/v3/shared.js +189 -1
- package/dist/commonjs/v3/shared.js.map +1 -1
- package/dist/commonjs/v3/skill.d.ts +99 -0
- package/dist/commonjs/v3/skill.js +155 -0
- package/dist/commonjs/v3/skill.js.map +1 -0
- package/dist/commonjs/v3/skills.d.ts +2 -0
- package/dist/commonjs/v3/skills.js +6 -0
- package/dist/commonjs/v3/skills.js.map +1 -0
- package/dist/commonjs/v3/streams.js +127 -19
- package/dist/commonjs/v3/streams.js.map +1 -1
- package/dist/commonjs/v3/tasks.d.ts +2 -1
- package/dist/commonjs/v3/tasks.js +1 -0
- package/dist/commonjs/v3/tasks.js.map +1 -1
- package/dist/commonjs/v3/test/index.d.ts +3 -0
- package/dist/commonjs/v3/test/index.js +18 -0
- package/dist/commonjs/v3/test/index.js.map +1 -0
- package/dist/commonjs/v3/test/mock-chat-agent.d.ts +259 -0
- package/dist/commonjs/v3/test/mock-chat-agent.js +468 -0
- package/dist/commonjs/v3/test/mock-chat-agent.js.map +1 -0
- package/dist/commonjs/v3/test/setup-catalog.d.ts +1 -0
- package/dist/commonjs/v3/test/setup-catalog.js +18 -0
- package/dist/commonjs/v3/test/setup-catalog.js.map +1 -0
- package/dist/commonjs/v3/test/test-session-handle.d.ts +53 -0
- package/dist/commonjs/v3/test/test-session-handle.js +256 -0
- package/dist/commonjs/v3/test/test-session-handle.js.map +1 -0
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/agentSkillsRuntime.d.ts +28 -0
- package/dist/esm/v3/agentSkillsRuntime.js +136 -0
- package/dist/esm/v3/agentSkillsRuntime.js.map +1 -0
- package/dist/esm/v3/ai-shared.d.ts +173 -0
- package/dist/esm/v3/ai-shared.js +22 -0
- package/dist/esm/v3/ai-shared.js.map +1 -0
- package/dist/esm/v3/ai.d.ts +2823 -5
- package/dist/esm/v3/ai.js +6187 -14
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/auth.d.ts +9 -0
- package/dist/esm/v3/auth.js.map +1 -1
- package/dist/esm/v3/chat-client.d.ts +301 -0
- package/dist/esm/v3/chat-client.js +619 -0
- package/dist/esm/v3/chat-client.js.map +1 -0
- package/dist/esm/v3/chat-react.d.ts +155 -0
- package/dist/esm/v3/chat-react.js +325 -0
- package/dist/esm/v3/chat-react.js.map +1 -0
- package/dist/esm/v3/chat-server.d.ts +206 -0
- package/dist/esm/v3/chat-server.js +734 -0
- package/dist/esm/v3/chat-server.js.map +1 -0
- package/dist/esm/v3/chat-server.test.d.ts +1 -0
- package/dist/esm/v3/chat-server.test.js +516 -0
- package/dist/esm/v3/chat-server.test.js.map +1 -0
- package/dist/esm/v3/chat-tab-coordinator.d.ts +65 -0
- package/dist/esm/v3/chat-tab-coordinator.js +231 -0
- package/dist/esm/v3/chat-tab-coordinator.js.map +1 -0
- package/dist/esm/v3/chat-tab-coordinator.test.d.ts +1 -0
- package/dist/esm/v3/chat-tab-coordinator.test.js +138 -0
- package/dist/esm/v3/chat-tab-coordinator.test.js.map +1 -0
- package/dist/esm/v3/chat.d.ts +437 -0
- package/dist/esm/v3/chat.js +961 -0
- package/dist/esm/v3/chat.js.map +1 -0
- package/dist/esm/v3/chat.test.d.ts +1 -0
- package/dist/esm/v3/chat.test.js +1178 -0
- package/dist/esm/v3/chat.test.js.map +1 -0
- package/dist/esm/v3/createStartSessionAction.test.d.ts +1 -0
- package/dist/esm/v3/createStartSessionAction.test.js +111 -0
- package/dist/esm/v3/createStartSessionAction.test.js.map +1 -0
- package/dist/esm/v3/deployments.d.ts +26 -0
- package/dist/esm/v3/deployments.js +34 -0
- package/dist/esm/v3/deployments.js.map +1 -0
- package/dist/esm/v3/index.d.ts +6 -3
- package/dist/esm/v3/index.js +4 -1
- package/dist/esm/v3/index.js.map +1 -1
- package/dist/esm/v3/runs.d.ts +15 -0
- package/dist/esm/v3/runs.js +1 -0
- package/dist/esm/v3/runs.js.map +1 -1
- package/dist/esm/v3/sessions.d.ts +228 -0
- package/dist/esm/v3/sessions.js +656 -0
- package/dist/esm/v3/sessions.js.map +1 -0
- package/dist/esm/v3/sessions.test.d.ts +1 -0
- package/dist/esm/v3/sessions.test.js +152 -0
- package/dist/esm/v3/sessions.test.js.map +1 -0
- package/dist/esm/v3/shared.d.ts +24 -2
- package/dist/esm/v3/shared.js +188 -1
- package/dist/esm/v3/shared.js.map +1 -1
- package/dist/esm/v3/skill.d.ts +99 -0
- package/dist/esm/v3/skill.js +128 -0
- package/dist/esm/v3/skill.js.map +1 -0
- package/dist/esm/v3/skills.d.ts +2 -0
- package/dist/esm/v3/skills.js +2 -0
- package/dist/esm/v3/skills.js.map +1 -0
- package/dist/esm/v3/streams.js +127 -20
- package/dist/esm/v3/streams.js.map +1 -1
- package/dist/esm/v3/tasks.d.ts +2 -1
- package/dist/esm/v3/tasks.js +2 -1
- package/dist/esm/v3/tasks.js.map +1 -1
- package/dist/esm/v3/test/index.d.ts +3 -0
- package/dist/esm/v3/test/index.js +13 -0
- package/dist/esm/v3/test/index.js.map +1 -0
- package/dist/esm/v3/test/mock-chat-agent.d.ts +259 -0
- package/dist/esm/v3/test/mock-chat-agent.js +465 -0
- package/dist/esm/v3/test/mock-chat-agent.js.map +1 -0
- package/dist/esm/v3/test/setup-catalog.d.ts +1 -0
- package/dist/esm/v3/test/setup-catalog.js +16 -0
- package/dist/esm/v3/test/setup-catalog.js.map +1 -0
- package/dist/esm/v3/test/test-session-handle.d.ts +53 -0
- package/dist/esm/v3/test/test-session-handle.js +251 -0
- package/dist/esm/v3/test/test-session-handle.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/package.json +87 -6
package/dist/commonjs/v3/ai.d.ts
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
|
-
import { Task, type inferSchemaIn, type TaskSchema, type TaskWithSchema } from "@trigger.dev/core/v3";
|
|
1
|
+
import { AnyTask, type MachinePresetName, type RealtimeDefinedInputStream, type RealtimeDefinedStream, Task, type inferSchemaIn, type inferSchemaOut, type TaskIdentifier, type TaskOptions, type TaskSchema, type TaskRunContext, type TaskWithSchema } from "@trigger.dev/core/v3";
|
|
2
|
+
import type { FinishReason, ModelMessage, ToolSet, UIMessage, UIMessageChunk, UIMessageStreamOptions, LanguageModelUsage } from "ai";
|
|
2
3
|
import { Tool, ToolCallOptions } from "ai";
|
|
3
|
-
|
|
4
|
+
import { locals } from "./locals.js";
|
|
5
|
+
import type { ResolvedPrompt } from "./prompt.js";
|
|
6
|
+
import type { ResolvedSkill } from "./skill.js";
|
|
7
|
+
import { type SessionTriggerConfig } from "@trigger.dev/core/v3";
|
|
8
|
+
/** Re-export for typing `ctx` in `chat.agent` hooks without importing `@trigger.dev/core`. */
|
|
9
|
+
export type { TaskRunContext } from "@trigger.dev/core/v3";
|
|
10
|
+
export type ToolCallExecutionOptions = {
|
|
11
|
+
toolCallId: string;
|
|
12
|
+
experimental_context?: unknown;
|
|
13
|
+
/** Chat context — only present when the tool runs inside a chat.agent turn. */
|
|
14
|
+
chatId?: string;
|
|
15
|
+
turn?: number;
|
|
16
|
+
continuation?: boolean;
|
|
17
|
+
clientData?: unknown;
|
|
18
|
+
};
|
|
19
|
+
/** Chat context stored in locals during each chat.agent turn for auto-detection. */
|
|
20
|
+
type ChatTurnContext<TClientData = unknown> = {
|
|
21
|
+
chatId: string;
|
|
22
|
+
turn: number;
|
|
23
|
+
continuation: boolean;
|
|
24
|
+
clientData?: TClientData;
|
|
25
|
+
};
|
|
26
|
+
export declare function __setReadChatSnapshotImplForTests(impl: ReadChatSnapshotImpl | undefined): void;
|
|
27
|
+
export declare function __setWriteChatSnapshotImplForTests(impl: WriteChatSnapshotImpl | undefined): void;
|
|
28
|
+
type ReplaySessionOutTailImpl = <TUIMessage extends UIMessage>(sessionId: string, options?: {
|
|
29
|
+
lastEventId?: string;
|
|
30
|
+
}) => Promise<ReplaySessionOutTailResult<TUIMessage>>;
|
|
31
|
+
export declare function __setReplaySessionOutTailImplForTests(impl: ReplaySessionOutTailImpl | undefined): void;
|
|
32
|
+
export declare function __setReplaySessionInTailImplForTests(impl: ReplaySessionInTailImpl | undefined): void;
|
|
4
33
|
type ToolResultContent = Array<{
|
|
5
34
|
type: "text";
|
|
6
35
|
text: string;
|
|
@@ -12,11 +41,2800 @@ type ToolResultContent = Array<{
|
|
|
12
41
|
export type ToolOptions<TResult> = {
|
|
13
42
|
experimental_toToolResultContent?: (result: TResult) => ToolResultContent;
|
|
14
43
|
};
|
|
15
|
-
|
|
16
|
-
|
|
44
|
+
/** Satisfies AI SDK `ToolSet` index signature alongside concrete `Tool` input/output types. */
|
|
45
|
+
type ToolSetCompatible<T extends Tool<any, any>> = T & NonNullable<ToolSet[string]>;
|
|
46
|
+
/**
|
|
47
|
+
* Returns an `execute` function for the AI SDK `tool()` helper (or any compatible tool definition).
|
|
48
|
+
* Preferred API for task-backed tools: the same Trigger wiring as the deprecated `ai.tool()`
|
|
49
|
+
* (`triggerAndSubscribe`, tool-call metadata, chat context, `chat.local` serialization) without
|
|
50
|
+
* building the tool object. You supply `description`, `inputSchema`, and any AI-SDK-only options
|
|
51
|
+
* (e.g. `experimental_toToolResultContent`) on `tool()` yourself.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* import { tool } from "ai";
|
|
56
|
+
* import { z } from "zod";
|
|
57
|
+
* import { ai } from "@trigger.dev/sdk/ai";
|
|
58
|
+
* import { myTask } from "./trigger/myTask";
|
|
59
|
+
*
|
|
60
|
+
* export const myTool = tool({
|
|
61
|
+
* description: myTask.description ?? "",
|
|
62
|
+
* inputSchema: z.object({ id: z.string() }),
|
|
63
|
+
* execute: ai.toolExecute(myTask),
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
declare function toolExecute<TIdentifier extends string, TInput = void, TOutput = unknown>(task: Task<TIdentifier, TInput, TOutput>): (input: TInput, toolOpts: ToolCallOptions) => Promise<TOutput>;
|
|
68
|
+
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>;
|
|
69
|
+
/**
|
|
70
|
+
* @deprecated Use `tool()` from the `ai` package with `execute: ai.toolExecute(task)` instead.
|
|
71
|
+
* This helper may be removed in a future major release.
|
|
72
|
+
*/
|
|
73
|
+
declare function toolFromTask<TIdentifier extends string, TInput = void, TOutput = unknown>(task: Task<TIdentifier, TInput, TOutput>, options?: ToolOptions<TOutput>): ToolSetCompatible<Tool<TInput, TOutput>>;
|
|
74
|
+
/** @deprecated Use `tool()` from `ai` with `execute: ai.toolExecute(task)`. */
|
|
75
|
+
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>>;
|
|
17
76
|
declare function getToolOptionsFromMetadata(): ToolCallExecutionOptions | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Get the current tool call ID from inside a subtask invoked via `ai.toolExecute()` (or legacy `ai.tool()`).
|
|
79
|
+
* Returns `undefined` if not running as a tool subtask.
|
|
80
|
+
*/
|
|
81
|
+
declare function getToolCallId(): string | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Get the chat context from inside a subtask invoked via `ai.toolExecute()` (or legacy `ai.tool()`) within a `chat.agent`.
|
|
84
|
+
* Pass `typeof yourChatTask` as the type parameter to get typed `clientData`.
|
|
85
|
+
* Returns `undefined` if the parent is not a chat task.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* const ctx = ai.chatContext<typeof myChat>();
|
|
90
|
+
* // ctx?.clientData is typed based on myChat's clientDataSchema
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
declare function getToolChatContext<TChatTask extends AnyTask = AnyTask>(): ChatTurnContext<InferChatClientData<TChatTask>> | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* Get the chat context from inside a subtask, throwing if not in a chat context.
|
|
96
|
+
* Pass `typeof yourChatTask` as the type parameter to get typed `clientData`.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* const ctx = ai.chatContextOrThrow<typeof myChat>();
|
|
101
|
+
* // ctx.chatId, ctx.clientData are guaranteed non-null
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
declare function getToolChatContextOrThrow<TChatTask extends AnyTask = AnyTask>(): ChatTurnContext<InferChatClientData<TChatTask>>;
|
|
18
105
|
export declare const ai: {
|
|
106
|
+
/**
|
|
107
|
+
* @deprecated Use `tool()` from the `ai` package with `execute: ai.toolExecute(task)` instead.
|
|
108
|
+
*/
|
|
19
109
|
tool: typeof toolFromTask;
|
|
110
|
+
/**
|
|
111
|
+
* Preferred: return value for the `execute` field of AI SDK `tool()`. Keeps Trigger subtask and
|
|
112
|
+
* metadata behavior without coupling to a specific `ai` version’s `Tool` / `ToolSet` types.
|
|
113
|
+
*/
|
|
114
|
+
toolExecute: typeof toolExecute;
|
|
20
115
|
currentToolOptions: typeof getToolOptionsFromMetadata;
|
|
116
|
+
/** Get the tool call ID from inside a subtask invoked via `ai.toolExecute()` (or legacy `ai.tool()`). */
|
|
117
|
+
toolCallId: typeof getToolCallId;
|
|
118
|
+
/** Get chat context (chatId, turn, clientData, etc.) from inside a subtask of a `chat.agent`. Returns undefined if not in a chat context. */
|
|
119
|
+
chatContext: typeof getToolChatContext;
|
|
120
|
+
/** Get chat context or throw if not in a chat context. Pass `typeof yourChatTask` for typed clientData. */
|
|
121
|
+
chatContextOrThrow: typeof getToolChatContextOrThrow;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Creates a public access token for a chat task.
|
|
125
|
+
*
|
|
126
|
+
* This is a convenience helper that creates a multi-use trigger public token
|
|
127
|
+
* scoped to the given task. Use it in a server action to provide the frontend
|
|
128
|
+
* `TriggerChatTransport` with an `accessToken`.
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* // actions.ts
|
|
133
|
+
* "use server";
|
|
134
|
+
* import { chat } from "@trigger.dev/sdk/ai";
|
|
135
|
+
* import type { myChat } from "@/trigger/chat";
|
|
136
|
+
*
|
|
137
|
+
* export const getChatToken = () => chat.createAccessToken<typeof myChat>("my-chat");
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
declare function createChatAccessToken<TTask extends AnyTask>(taskId: TaskIdentifier<TTask>): Promise<string>;
|
|
141
|
+
/**
|
|
142
|
+
* A stream writer passed to chat lifecycle callbacks (`onPreload`, `onChatStart`,
|
|
143
|
+
* `onTurnStart`, `onTurnComplete`, `onCompacted`).
|
|
144
|
+
*
|
|
145
|
+
* Write custom `UIMessageChunk` parts (e.g. `data-*` parts) directly to the chat
|
|
146
|
+
* stream without the ceremony of `chat.stream.writer({ execute })`.
|
|
147
|
+
*
|
|
148
|
+
* The writer is lazy — no stream overhead if you don't call `write()` or `merge()`.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* onTurnStart: async ({ writer }) => {
|
|
153
|
+
* writer.write({ type: "data-status", data: { loading: true } });
|
|
154
|
+
* },
|
|
155
|
+
* onTurnComplete: async ({ writer, uiMessages }) => {
|
|
156
|
+
* writer.write({ type: "data-analytics", data: { messageCount: uiMessages.length } });
|
|
157
|
+
* },
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export type ChatWriter = {
|
|
161
|
+
/** Write a single UIMessageChunk to the chat stream. */
|
|
162
|
+
write(part: UIMessageChunk): void;
|
|
163
|
+
/** Merge another stream's chunks into the chat stream. */
|
|
164
|
+
merge(stream: ReadableStream<UIMessageChunk>): void;
|
|
165
|
+
};
|
|
166
|
+
import type { ChatTaskWirePayload } from "./ai-shared.js";
|
|
167
|
+
export type { ChatTaskWirePayload, ChatInputChunk } from "./ai-shared.js";
|
|
168
|
+
/**
|
|
169
|
+
* The payload shape passed to the `chatAgent` run function.
|
|
170
|
+
*
|
|
171
|
+
* - `messages` contains model-ready messages (converted via `convertToModelMessages`) —
|
|
172
|
+
* pass these directly to `streamText`.
|
|
173
|
+
* - `clientData` contains custom data from the frontend (the `metadata` field from `sendMessage()`).
|
|
174
|
+
*
|
|
175
|
+
* The backend accumulates the full conversation history across turns, so the frontend
|
|
176
|
+
* only needs to send new messages after the first turn.
|
|
177
|
+
*/
|
|
178
|
+
export type ChatTaskPayload<TClientData = unknown> = {
|
|
179
|
+
/** Model-ready messages — pass directly to `streamText({ messages })`. */
|
|
180
|
+
messages: ModelMessage[];
|
|
181
|
+
/** The unique identifier for the chat session */
|
|
182
|
+
chatId: string;
|
|
183
|
+
/**
|
|
184
|
+
* The trigger type:
|
|
185
|
+
* - `"submit-message"`: A new user message
|
|
186
|
+
* - `"regenerate-message"`: Regenerate the last assistant response
|
|
187
|
+
* - `"preload"`: Run was preloaded before the first message (only on turn 0)
|
|
188
|
+
* - `"action"`: A typed action from the frontend (see `actionSchema` + `onAction`).
|
|
189
|
+
* The action has already been applied before `run()` fires — check `trigger === "action"`
|
|
190
|
+
* to short-circuit the LLM call when an action doesn't need a response.
|
|
191
|
+
* - `"close"`: The chat session is being closed (internal; `run()` is not called).
|
|
192
|
+
*/
|
|
193
|
+
trigger: "submit-message" | "regenerate-message" | "preload" | "action" | "close";
|
|
194
|
+
/** The ID of the message to regenerate (only for `"regenerate-message"`) */
|
|
195
|
+
messageId?: string;
|
|
196
|
+
/** Custom data from the frontend (passed via `metadata` on `sendMessage()` or the transport). */
|
|
197
|
+
clientData?: TClientData;
|
|
198
|
+
/** Whether this run is continuing an existing chat (previous run timed out or was cancelled). False for brand new chats. */
|
|
199
|
+
continuation: boolean;
|
|
200
|
+
/** The run ID of the previous run (only set when `continuation` is true). */
|
|
201
|
+
previousRunId?: string;
|
|
202
|
+
/** Whether this run was preloaded before the first message. */
|
|
203
|
+
preloaded: boolean;
|
|
204
|
+
/**
|
|
205
|
+
* The friendlyId of the Session primitive backing this chat. Use with
|
|
206
|
+
* `sessions.open(sessionId)` when you need direct access to the session's
|
|
207
|
+
* `.in` / `.out` channels outside the hooks the agent already wires for
|
|
208
|
+
* you. Undefined only for legacy transports that predate the sessions
|
|
209
|
+
* migration.
|
|
210
|
+
*/
|
|
211
|
+
sessionId?: string;
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* Abort signals provided to the `chatAgent` run function.
|
|
215
|
+
*/
|
|
216
|
+
export type ChatTaskSignals = {
|
|
217
|
+
/** Combined signal — fires on run cancel OR stop generation. Pass to `streamText`. */
|
|
218
|
+
signal: AbortSignal;
|
|
219
|
+
/** Fires only when the run is cancelled, expired, or exceeds maxDuration. */
|
|
220
|
+
cancelSignal: AbortSignal;
|
|
221
|
+
/** Fires only when the frontend stops generation for this turn (per-turn, reset each turn). */
|
|
222
|
+
stopSignal: AbortSignal;
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* The full payload passed to a `chatAgent` run function.
|
|
226
|
+
* Extends `ChatTaskPayload` (the wire payload) with abort signals.
|
|
227
|
+
*/
|
|
228
|
+
export type ChatTaskRunPayload<TClientData = unknown> = ChatTaskPayload<TClientData> & ChatTaskSignals & {
|
|
229
|
+
/**
|
|
230
|
+
* Task run context — same object as the `ctx` passed to a standard `task({ run })` handler’s second argument.
|
|
231
|
+
* Use for tags, metadata, parent run links, or any API that needs the full run record.
|
|
232
|
+
*/
|
|
233
|
+
ctx: TaskRunContext;
|
|
234
|
+
/** Token usage from the previous turn. Undefined on turn 0. */
|
|
235
|
+
previousTurnUsage?: LanguageModelUsage;
|
|
236
|
+
/** Cumulative token usage across all completed turns so far. */
|
|
237
|
+
totalUsage: LanguageModelUsage;
|
|
238
|
+
};
|
|
239
|
+
/** Convenience re-export of the AI SDK's `LanguageModelUsage` type. */
|
|
240
|
+
export type ChatTurnUsage = LanguageModelUsage;
|
|
241
|
+
/**
|
|
242
|
+
* Replace the accumulated conversation messages for the current run.
|
|
243
|
+
*
|
|
244
|
+
* Call from `onTurnStart` to compact before `run()` executes, or from
|
|
245
|
+
* `onTurnComplete` to compact before the next turn. Takes `UIMessage[]`
|
|
246
|
+
* and converts to `ModelMessage[]` internally.
|
|
247
|
+
*/
|
|
248
|
+
declare function setChatMessages<TUIM extends UIMessage = UIMessage>(uiMessages: TUIM[]): void;
|
|
249
|
+
/**
|
|
250
|
+
* A tool call surfaced by `chat.history.getPendingToolCalls()` /
|
|
251
|
+
* `getResolvedToolCalls()`. Identifies the call by its `toolCallId` plus
|
|
252
|
+
* the `messageId` of the assistant message that hosts it, so callers can
|
|
253
|
+
* locate the part precisely without re-walking the chain.
|
|
254
|
+
*/
|
|
255
|
+
export type ChatToolCallRef = {
|
|
256
|
+
toolCallId: string;
|
|
257
|
+
toolName: string;
|
|
258
|
+
messageId: string;
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* A new tool result surfaced by `chat.history.extractNewToolResults()`.
|
|
262
|
+
* `errorText` is set iff the part is in `output-error` state; otherwise
|
|
263
|
+
* `output` carries the resolved value.
|
|
264
|
+
*/
|
|
265
|
+
export type ChatNewToolResult = {
|
|
266
|
+
toolCallId: string;
|
|
267
|
+
toolName: string;
|
|
268
|
+
output: unknown;
|
|
269
|
+
errorText?: string;
|
|
270
|
+
};
|
|
271
|
+
/** State stored in locals during prepareStep compaction. */
|
|
272
|
+
interface CompactionState {
|
|
273
|
+
summary: string;
|
|
274
|
+
baseResponseMessageCount: number;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Event passed to `summarize` callbacks.
|
|
278
|
+
*/
|
|
279
|
+
export type SummarizeEvent = {
|
|
280
|
+
/** The current model messages to summarize. */
|
|
281
|
+
messages: ModelMessage[];
|
|
282
|
+
/** Full usage object from the triggering step/turn. */
|
|
283
|
+
usage?: LanguageModelUsage;
|
|
284
|
+
/** Cumulative token usage across all completed turns. Present in chat.agent contexts. */
|
|
285
|
+
totalUsage?: LanguageModelUsage;
|
|
286
|
+
/** The chat session ID (if running inside a chat.agent). */
|
|
287
|
+
chatId?: string;
|
|
288
|
+
/** The current turn number (0-indexed, if inside a chat.agent). */
|
|
289
|
+
turn?: number;
|
|
290
|
+
/** Custom data from the frontend (if inside a chat.agent). */
|
|
291
|
+
clientData?: unknown;
|
|
292
|
+
/**
|
|
293
|
+
* Where compaction is running:
|
|
294
|
+
* - `"inner"` — between tool-call steps (prepareStep)
|
|
295
|
+
* - `"outer"` — between turns
|
|
296
|
+
*/
|
|
297
|
+
source?: "inner" | "outer";
|
|
298
|
+
/** The step number (0-indexed). Only present when `source` is `"inner"`. */
|
|
299
|
+
stepNumber?: number;
|
|
300
|
+
};
|
|
301
|
+
/**
|
|
302
|
+
* Event passed to `compactUIMessages` and `compactModelMessages` callbacks.
|
|
303
|
+
*/
|
|
304
|
+
export type CompactMessagesEvent<TUIM extends UIMessage = UIMessage> = {
|
|
305
|
+
/** The generated summary text. */
|
|
306
|
+
summary: string;
|
|
307
|
+
/** The current UI messages (full conversation). */
|
|
308
|
+
uiMessages: TUIM[];
|
|
309
|
+
/** The current model messages (full conversation). */
|
|
310
|
+
modelMessages: ModelMessage[];
|
|
311
|
+
/** The chat session ID. */
|
|
312
|
+
chatId: string;
|
|
313
|
+
/** The current turn number (0-indexed). */
|
|
314
|
+
turn: number;
|
|
315
|
+
/** Custom data from the frontend. */
|
|
316
|
+
clientData?: unknown;
|
|
317
|
+
/**
|
|
318
|
+
* Where compaction is running:
|
|
319
|
+
* - `"inner"` — between tool-call steps (prepareStep)
|
|
320
|
+
* - `"outer"` — between turns
|
|
321
|
+
*/
|
|
322
|
+
source: "inner" | "outer";
|
|
323
|
+
};
|
|
324
|
+
/**
|
|
325
|
+
* Options for the `compaction` field on `chat.agent()`.
|
|
326
|
+
*
|
|
327
|
+
* Handles compaction automatically in both the inner loop (prepareStep, between
|
|
328
|
+
* tool-call steps) and the outer loop (between turns, for single-step responses
|
|
329
|
+
* where prepareStep never fires).
|
|
330
|
+
*/
|
|
331
|
+
export type ChatAgentCompactionOptions<TUIM extends UIMessage = UIMessage> = {
|
|
332
|
+
/** Decide whether to compact. Return true to trigger compaction. */
|
|
333
|
+
shouldCompact: (event: ShouldCompactEvent) => boolean | Promise<boolean>;
|
|
334
|
+
/** Generate a summary from the current messages. Return the summary text. */
|
|
335
|
+
summarize: (event: SummarizeEvent) => Promise<string>;
|
|
336
|
+
/**
|
|
337
|
+
* Transform UI messages after compaction (what gets persisted and displayed).
|
|
338
|
+
* Default: preserve all UI messages unchanged.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* // Flatten to summary
|
|
343
|
+
* compactUIMessages: ({ summary }) => [{
|
|
344
|
+
* id: generateId(), role: "assistant",
|
|
345
|
+
* parts: [{ type: "text", text: `[Summary]\n\n${summary}` }],
|
|
346
|
+
* }],
|
|
347
|
+
*
|
|
348
|
+
* // Summary + keep last 4 messages
|
|
349
|
+
* compactUIMessages: ({ uiMessages, summary }) => [
|
|
350
|
+
* { id: generateId(), role: "assistant",
|
|
351
|
+
* parts: [{ type: "text", text: `[Summary]\n\n${summary}` }] },
|
|
352
|
+
* ...uiMessages.slice(-4),
|
|
353
|
+
* ],
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
compactUIMessages?: (event: CompactMessagesEvent<TUIM>) => TUIM[] | Promise<TUIM[]>;
|
|
357
|
+
/**
|
|
358
|
+
* Transform model messages after compaction (what gets sent to the LLM).
|
|
359
|
+
* Default: replace all with a single summary message.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* // Summary + keep last 2 model messages
|
|
364
|
+
* compactModelMessages: ({ modelMessages, summary }) => [
|
|
365
|
+
* { role: "user", content: summary },
|
|
366
|
+
* ...modelMessages.slice(-2),
|
|
367
|
+
* ],
|
|
368
|
+
* ```
|
|
369
|
+
*/
|
|
370
|
+
compactModelMessages?: (event: CompactMessagesEvent<TUIM>) => ModelMessage[] | Promise<ModelMessage[]>;
|
|
371
|
+
};
|
|
372
|
+
/**
|
|
373
|
+
* Event passed to `shouldInject` and `prepareMessages` callbacks.
|
|
374
|
+
*/
|
|
375
|
+
export type PendingMessagesBatchEvent<TUIM extends UIMessage = UIMessage> = {
|
|
376
|
+
/** All pending UI messages that arrived during streaming (batch). */
|
|
377
|
+
messages: TUIM[];
|
|
378
|
+
/** Current model messages in the conversation. */
|
|
379
|
+
modelMessages: ModelMessage[];
|
|
380
|
+
/** Completed steps so far. */
|
|
381
|
+
steps: CompactionStep[];
|
|
382
|
+
/** Current step number (0-indexed). */
|
|
383
|
+
stepNumber: number;
|
|
384
|
+
/** Chat session ID. */
|
|
385
|
+
chatId: string;
|
|
386
|
+
/** Current turn number (0-indexed). */
|
|
387
|
+
turn: number;
|
|
388
|
+
/** Custom data from the frontend. */
|
|
389
|
+
clientData?: unknown;
|
|
390
|
+
};
|
|
391
|
+
/**
|
|
392
|
+
* Event passed to `onReceived` callback (per-message, as they arrive).
|
|
393
|
+
*/
|
|
394
|
+
export type PendingMessageReceivedEvent<TUIM extends UIMessage = UIMessage> = {
|
|
395
|
+
/** The UI message that arrived during streaming. */
|
|
396
|
+
message: TUIM;
|
|
397
|
+
/** Chat session ID. */
|
|
398
|
+
chatId: string;
|
|
399
|
+
/** Current turn number (0-indexed). */
|
|
400
|
+
turn: number;
|
|
401
|
+
};
|
|
402
|
+
/**
|
|
403
|
+
* Event passed to `onInjected` callback (batch, after injection).
|
|
404
|
+
*/
|
|
405
|
+
export type PendingMessagesInjectedEvent<TUIM extends UIMessage = UIMessage> = {
|
|
406
|
+
/** All UI messages that were injected. */
|
|
407
|
+
messages: TUIM[];
|
|
408
|
+
/** The model messages that were injected. */
|
|
409
|
+
injectedModelMessages: ModelMessage[];
|
|
410
|
+
/** Chat session ID. */
|
|
411
|
+
chatId: string;
|
|
412
|
+
/** Current turn number (0-indexed). */
|
|
413
|
+
turn: number;
|
|
414
|
+
/** Step number where injection occurred. */
|
|
415
|
+
stepNumber: number;
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* Options for the `pendingMessages` field on `chat.agent()`, `chat.createSession()`,
|
|
419
|
+
* or `ChatMessageAccumulator`.
|
|
420
|
+
*
|
|
421
|
+
* Configures how messages that arrive during streaming are handled. When
|
|
422
|
+
* `shouldInject` is provided and returns `true`, the full batch of pending
|
|
423
|
+
* messages is injected between tool-call steps via `prepareStep`.
|
|
424
|
+
* Otherwise, messages queue for the next turn.
|
|
425
|
+
*/
|
|
426
|
+
export type PendingMessagesOptions<TUIM extends UIMessage = UIMessage> = {
|
|
427
|
+
/**
|
|
428
|
+
* Decide whether to inject pending messages between tool-call steps.
|
|
429
|
+
* Called once per step boundary with the full batch of pending messages.
|
|
430
|
+
* If absent, no injection happens — messages only queue for the next turn.
|
|
431
|
+
*/
|
|
432
|
+
shouldInject?: (event: PendingMessagesBatchEvent<TUIM>) => boolean | Promise<boolean>;
|
|
433
|
+
/**
|
|
434
|
+
* Transform the batch of pending messages before injection.
|
|
435
|
+
* Return the model messages to inject.
|
|
436
|
+
* Default: convert each UI message via `convertToModelMessages`.
|
|
437
|
+
*/
|
|
438
|
+
prepare?: (event: PendingMessagesBatchEvent<TUIM>) => ModelMessage[] | Promise<ModelMessage[]>;
|
|
439
|
+
/** Called when a message arrives during streaming (per-message). */
|
|
440
|
+
onReceived?: (event: PendingMessageReceivedEvent<TUIM>) => void | Promise<void>;
|
|
441
|
+
/** Called after a batch of messages is injected via `prepareStep`. */
|
|
442
|
+
onInjected?: (event: PendingMessagesInjectedEvent<TUIM>) => void | Promise<void>;
|
|
443
|
+
};
|
|
444
|
+
/**
|
|
445
|
+
* The data part type used to signal that pending messages were injected
|
|
446
|
+
* between tool-call steps. The frontend can match on this to render
|
|
447
|
+
* injection points inline in the assistant response.
|
|
448
|
+
*/
|
|
449
|
+
export { PENDING_MESSAGE_INJECTED_TYPE } from "./ai-shared.js";
|
|
450
|
+
/**
|
|
451
|
+
* Event passed to the `prepareMessages` hook.
|
|
452
|
+
*/
|
|
453
|
+
export type PrepareMessagesEvent<TClientData = unknown> = {
|
|
454
|
+
/** The messages to transform. Return the transformed array. */
|
|
455
|
+
messages: ModelMessage[];
|
|
456
|
+
/** Why messages are being prepared. */
|
|
457
|
+
reason: "run" | "compaction-rebuild" | "compaction-result";
|
|
458
|
+
/** The chat session ID. */
|
|
459
|
+
chatId: string;
|
|
460
|
+
/** The current turn number (0-indexed). */
|
|
461
|
+
turn: number;
|
|
462
|
+
/** Custom data from the frontend. */
|
|
463
|
+
clientData?: TClientData;
|
|
464
|
+
};
|
|
465
|
+
/**
|
|
466
|
+
* Data shape for `data-compaction` stream chunks emitted during compaction.
|
|
467
|
+
* Use to type the `data` field when rendering compaction parts in the frontend.
|
|
468
|
+
*/
|
|
469
|
+
export type CompactionChunkData = {
|
|
470
|
+
status: "compacting" | "complete";
|
|
471
|
+
totalTokens: number | undefined;
|
|
472
|
+
};
|
|
473
|
+
/**
|
|
474
|
+
* Event passed to the `onCompacted` callback.
|
|
475
|
+
*/
|
|
476
|
+
export type CompactedEvent = {
|
|
477
|
+
/** Task run context — same as `task` lifecycle hooks and `chat.agent` `run({ ctx })`. */
|
|
478
|
+
ctx: TaskRunContext;
|
|
479
|
+
/** The generated summary text. */
|
|
480
|
+
summary: string;
|
|
481
|
+
/** The messages that were compacted (pre-compaction). */
|
|
482
|
+
messages: ModelMessage[];
|
|
483
|
+
/** Number of messages before compaction. */
|
|
484
|
+
messageCount: number;
|
|
485
|
+
/** Token usage from the step that triggered compaction. */
|
|
486
|
+
usage: LanguageModelUsage;
|
|
487
|
+
/** Total token count that triggered compaction. */
|
|
488
|
+
totalTokens: number | undefined;
|
|
489
|
+
/** Input token count from the triggering step. */
|
|
490
|
+
inputTokens: number | undefined;
|
|
491
|
+
/** Output token count from the triggering step. */
|
|
492
|
+
outputTokens: number | undefined;
|
|
493
|
+
/** The step number where compaction occurred (0-indexed). */
|
|
494
|
+
stepNumber: number;
|
|
495
|
+
/** The chat session ID (if running inside a chat.agent). */
|
|
496
|
+
chatId?: string;
|
|
497
|
+
/** The current turn number (if running inside a chat.agent). */
|
|
498
|
+
turn?: number;
|
|
499
|
+
/** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
|
|
500
|
+
writer: ChatWriter;
|
|
501
|
+
};
|
|
502
|
+
/**
|
|
503
|
+
* Event passed to `shouldCompact` callbacks.
|
|
504
|
+
*/
|
|
505
|
+
export type ShouldCompactEvent = {
|
|
506
|
+
/** The current model messages (full conversation). */
|
|
507
|
+
messages: ModelMessage[];
|
|
508
|
+
/** Total token count from the triggering step/turn. */
|
|
509
|
+
totalTokens: number | undefined;
|
|
510
|
+
/** Input token count from the triggering step/turn. */
|
|
511
|
+
inputTokens: number | undefined;
|
|
512
|
+
/** Output token count from the triggering step/turn. */
|
|
513
|
+
outputTokens: number | undefined;
|
|
514
|
+
/** Full usage object from the triggering step/turn. */
|
|
515
|
+
usage?: LanguageModelUsage;
|
|
516
|
+
/** Cumulative token usage across all completed turns. Present in chat.agent contexts. */
|
|
517
|
+
totalUsage?: LanguageModelUsage;
|
|
518
|
+
/** The chat session ID (if running inside a chat.agent). */
|
|
519
|
+
chatId?: string;
|
|
520
|
+
/** The current turn number (0-indexed, if inside a chat.agent). */
|
|
521
|
+
turn?: number;
|
|
522
|
+
/** Custom data from the frontend (if inside a chat.agent). */
|
|
523
|
+
clientData?: unknown;
|
|
524
|
+
/**
|
|
525
|
+
* Where this check is running:
|
|
526
|
+
* - `"inner"` — between tool-call steps (prepareStep)
|
|
527
|
+
* - `"outer"` — between turns (after response, before onBeforeTurnComplete)
|
|
528
|
+
*/
|
|
529
|
+
source?: "inner" | "outer";
|
|
530
|
+
/** The step number (0-indexed). Only present when `source` is `"inner"`. */
|
|
531
|
+
stepNumber?: number;
|
|
532
|
+
/** The steps array from prepareStep. Only present when `source` is `"inner"`. */
|
|
533
|
+
steps?: CompactionStep[];
|
|
534
|
+
};
|
|
535
|
+
/**
|
|
536
|
+
* Options for `chat.compaction()` — the high-level prepareStep factory.
|
|
537
|
+
*/
|
|
538
|
+
export type CompactionOptions = {
|
|
539
|
+
/** Generate a summary from the current messages. Return the summary text. */
|
|
540
|
+
summarize: (messages: ModelMessage[]) => Promise<string>;
|
|
541
|
+
/** Token threshold — compact when totalTokens exceeds this. Ignored if `shouldCompact` is provided. */
|
|
542
|
+
threshold?: number;
|
|
543
|
+
/** Custom compaction trigger. When provided, used instead of `threshold`. */
|
|
544
|
+
shouldCompact?: (event: ShouldCompactEvent) => boolean | Promise<boolean>;
|
|
545
|
+
};
|
|
546
|
+
/** A step object as received in prepareStep's `steps` array. */
|
|
547
|
+
export type CompactionStep = {
|
|
548
|
+
usage: LanguageModelUsage;
|
|
549
|
+
finishReason: string;
|
|
550
|
+
content: Array<{
|
|
551
|
+
type: string;
|
|
552
|
+
toolCallId?: string;
|
|
553
|
+
}>;
|
|
554
|
+
response: {
|
|
555
|
+
messages: Array<any>;
|
|
556
|
+
};
|
|
557
|
+
};
|
|
558
|
+
/**
|
|
559
|
+
* Result of `chat.compact()`. Discriminated union so you can inspect
|
|
560
|
+
* what happened, but also directly compatible with prepareStep's return type.
|
|
561
|
+
*
|
|
562
|
+
* - `"skipped"` — no compaction needed (first step, boundary unsafe, or under threshold). Return `undefined` to prepareStep.
|
|
563
|
+
* - `"rebuilt"` — previous compaction exists, messages rebuilt from summary + new response messages.
|
|
564
|
+
* - `"compacted"` — compaction just happened, includes the generated summary.
|
|
565
|
+
*/
|
|
566
|
+
export type CompactResult = {
|
|
567
|
+
type: "skipped";
|
|
568
|
+
} | {
|
|
569
|
+
type: "rebuilt";
|
|
570
|
+
messages: ModelMessage[];
|
|
571
|
+
} | {
|
|
572
|
+
type: "compacted";
|
|
573
|
+
messages: ModelMessage[];
|
|
574
|
+
summary: string;
|
|
575
|
+
};
|
|
576
|
+
/**
|
|
577
|
+
* Options for `chat.compact()` — the low-level compaction function.
|
|
578
|
+
*/
|
|
579
|
+
export type CompactOptions = {
|
|
580
|
+
/** Generate a summary from the current messages. Return the summary text. */
|
|
581
|
+
summarize: (messages: ModelMessage[]) => Promise<string>;
|
|
582
|
+
/** Token threshold — compact when totalTokens exceeds this. Ignored if `shouldCompact` is provided. */
|
|
583
|
+
threshold?: number;
|
|
584
|
+
/** Custom compaction trigger. When provided, used instead of `threshold`. */
|
|
585
|
+
shouldCompact?: (event: ShouldCompactEvent) => boolean | Promise<boolean>;
|
|
586
|
+
};
|
|
587
|
+
/**
|
|
588
|
+
* Read the current compaction state. Returns the summary and base message count
|
|
589
|
+
* if compaction has occurred in this turn, or `undefined` if not.
|
|
590
|
+
*
|
|
591
|
+
* Use in a custom `prepareStep` to rebuild from a previous compaction:
|
|
592
|
+
* ```ts
|
|
593
|
+
* const state = chat.getCompactionState();
|
|
594
|
+
* if (state) {
|
|
595
|
+
* return { messages: [{ role: "user", content: state.summary }, ...newMsgs] };
|
|
596
|
+
* }
|
|
597
|
+
* ```
|
|
598
|
+
*/
|
|
599
|
+
declare function getCompactionState(): CompactionState | undefined;
|
|
600
|
+
/**
|
|
601
|
+
* Low-level compaction for use inside a custom `prepareStep`.
|
|
602
|
+
*
|
|
603
|
+
* Handles the full decision tree: first step, already-compacted rebuild,
|
|
604
|
+
* boundary safety, threshold check, summarization, stream chunks, state
|
|
605
|
+
* storage, and accumulator update.
|
|
606
|
+
*
|
|
607
|
+
* Returns a `CompactResult` — inspect `result.type` to see what happened,
|
|
608
|
+
* or convert to a prepareStep return with `result.type === "skipped" ? undefined : result`.
|
|
609
|
+
*
|
|
610
|
+
* @example
|
|
611
|
+
* ```ts
|
|
612
|
+
* prepareStep: async ({ messages, steps }) => {
|
|
613
|
+
* // your custom logic here...
|
|
614
|
+
* const result = await chat.compact(messages, steps, {
|
|
615
|
+
* threshold: 80_000,
|
|
616
|
+
* summarize: async (msgs) => generateText({ model, messages: msgs }).then(r => r.text),
|
|
617
|
+
* });
|
|
618
|
+
* if (result.type === "compacted") {
|
|
619
|
+
* logger.info("Compacted!", { summary: result.summary });
|
|
620
|
+
* }
|
|
621
|
+
* return result.type === "skipped" ? undefined : result;
|
|
622
|
+
* },
|
|
623
|
+
* ```
|
|
624
|
+
*/
|
|
625
|
+
declare function chatCompact(messages: ModelMessage[], steps: CompactionStep[], options: CompactOptions): Promise<CompactResult>;
|
|
626
|
+
/**
|
|
627
|
+
* Returns a `prepareStep` function that handles context compaction automatically.
|
|
628
|
+
*
|
|
629
|
+
* Monitors token usage between tool-call steps. When `totalTokens` exceeds
|
|
630
|
+
* the threshold, generates a summary via `summarize()`, replaces the message
|
|
631
|
+
* history, and emits `data-compaction` stream chunks for the frontend.
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* ```ts
|
|
635
|
+
* return streamText({
|
|
636
|
+
* ...chat.toStreamTextOptions({ registry }),
|
|
637
|
+
* messages: chat.addCacheBreaks(messages),
|
|
638
|
+
* prepareStep: chat.compactionStep({
|
|
639
|
+
* threshold: 80_000,
|
|
640
|
+
* summarize: async (messages) => {
|
|
641
|
+
* return generateText({ model, messages: [...messages, { role: "user", content: "Summarize." }] })
|
|
642
|
+
* .then((r) => r.text);
|
|
643
|
+
* },
|
|
644
|
+
* }),
|
|
645
|
+
* tools: { ... },
|
|
646
|
+
* });
|
|
647
|
+
* ```
|
|
648
|
+
*/
|
|
649
|
+
declare function chatCompactionStep(options: CompactionOptions): (args: {
|
|
650
|
+
messages: ModelMessage[];
|
|
651
|
+
steps: CompactionStep[];
|
|
652
|
+
}) => Promise<{
|
|
653
|
+
messages: ModelMessage[];
|
|
654
|
+
} | undefined>;
|
|
655
|
+
/**
|
|
656
|
+
* Checks whether it's safe to compact the message history. Returns `false`
|
|
657
|
+
* if any tool calls are in-flight (incomplete tool invocations without results).
|
|
658
|
+
*
|
|
659
|
+
* Call before `chat.setMessages()` to avoid corrupting tool-call state.
|
|
660
|
+
*/
|
|
661
|
+
declare function isCompactionSafe(messages: UIMessage[]): boolean;
|
|
662
|
+
/**
|
|
663
|
+
* A resolved prompt stored via `chat.prompt.set()`. Either a full `ResolvedPrompt`
|
|
664
|
+
* from `prompts.define().resolve()`, or a lightweight wrapper around a plain string.
|
|
665
|
+
*/
|
|
666
|
+
export type ChatPromptValue = ResolvedPrompt | {
|
|
667
|
+
text: string;
|
|
668
|
+
model: undefined;
|
|
669
|
+
config: undefined;
|
|
670
|
+
promptId: string;
|
|
671
|
+
version: number;
|
|
672
|
+
labels: string[];
|
|
673
|
+
toAISDKTelemetry: (additionalMetadata?: Record<string, string>) => {
|
|
674
|
+
experimental_telemetry: {
|
|
675
|
+
isEnabled: true;
|
|
676
|
+
metadata: Record<string, string>;
|
|
677
|
+
};
|
|
678
|
+
};
|
|
679
|
+
};
|
|
680
|
+
/**
|
|
681
|
+
* Store a resolved prompt (or plain string) for the current run.
|
|
682
|
+
* Call from any hook (`onPreload`, `onChatStart`, `onTurnStart`) or `run()`.
|
|
683
|
+
*/
|
|
684
|
+
declare function setChatPrompt(resolved: ResolvedPrompt | string): void;
|
|
685
|
+
/**
|
|
686
|
+
* Read the stored prompt. Throws if `chat.prompt.set()` has not been called.
|
|
687
|
+
*/
|
|
688
|
+
declare function getChatPrompt(): ChatPromptValue;
|
|
689
|
+
/**
|
|
690
|
+
* Store resolved skills for the current run. Call from any hook
|
|
691
|
+
* (`onPreload`, `onChatStart`, `onTurnStart`) or `run()`.
|
|
692
|
+
*/
|
|
693
|
+
declare function setChatSkills(skills: ResolvedSkill[]): void;
|
|
694
|
+
/** Read the stored skills. Returns `undefined` if none set. */
|
|
695
|
+
declare function getChatSkills(): ResolvedSkill[] | undefined;
|
|
696
|
+
/**
|
|
697
|
+
* Build the three tools we auto-inject into `streamText` when skills are
|
|
698
|
+
* set: `loadSkill`, `readFile`, `bash`. Scoped per-skill by name.
|
|
699
|
+
*
|
|
700
|
+
* Exported so callers can use the same tools outside the auto-wired path
|
|
701
|
+
* (e.g. in a `chat.createSession` loop with custom streamText).
|
|
702
|
+
*/
|
|
703
|
+
export declare function buildSkillTools(skills: ResolvedSkill[]): Record<string, Tool>;
|
|
704
|
+
/**
|
|
705
|
+
* Options for {@link toStreamTextOptions}.
|
|
706
|
+
*/
|
|
707
|
+
export type ToStreamTextOptionsOptions = {
|
|
708
|
+
/** Additional telemetry metadata merged into `experimental_telemetry.metadata`. */
|
|
709
|
+
telemetry?: Record<string, string>;
|
|
710
|
+
/**
|
|
711
|
+
* An AI SDK provider registry (from `createProviderRegistry`) or any object
|
|
712
|
+
* with a `languageModel(id)` method. When provided and the stored prompt has
|
|
713
|
+
* a `model` string, the resolved `LanguageModel` is included in the returned
|
|
714
|
+
* options so `streamText` uses it directly.
|
|
715
|
+
*
|
|
716
|
+
* The model string should use the `"provider:model-id"` format
|
|
717
|
+
* (e.g. `"openai:gpt-4o"`, `"anthropic:claude-sonnet-4-6"`).
|
|
718
|
+
*/
|
|
719
|
+
registry?: {
|
|
720
|
+
languageModel(modelId: string): unknown;
|
|
721
|
+
};
|
|
722
|
+
/**
|
|
723
|
+
* User-defined tools to merge alongside the auto-injected skill tools
|
|
724
|
+
* (`loadSkill`, `readFile`, `bash`). User tools win on name conflicts.
|
|
725
|
+
*
|
|
726
|
+
* If you don't pass `tools` here and skills are set, the returned options
|
|
727
|
+
* will include just the skill tools — spread after any `tools` you pass
|
|
728
|
+
* directly to `streamText` and they'll be replaced. Easiest: pass all
|
|
729
|
+
* your tools here.
|
|
730
|
+
*/
|
|
731
|
+
tools?: Record<string, Tool>;
|
|
732
|
+
};
|
|
733
|
+
/**
|
|
734
|
+
* Returns an options object ready to spread into `streamText()`.
|
|
735
|
+
*
|
|
736
|
+
* Includes `system`, `experimental_telemetry`, and any config fields
|
|
737
|
+
* (temperature, maxTokens, etc.) from the stored prompt.
|
|
738
|
+
*
|
|
739
|
+
* When a `registry` is provided and the prompt has a `model` string,
|
|
740
|
+
* the resolved `LanguageModel` is included as `model`.
|
|
741
|
+
*
|
|
742
|
+
* If no prompt has been set, returns `{}` (no-op spread).
|
|
743
|
+
*/
|
|
744
|
+
declare function toStreamTextOptions(options?: ToStreamTextOptionsOptions): Record<string, unknown>;
|
|
745
|
+
/**
|
|
746
|
+
* Options for `pipeChat`.
|
|
747
|
+
*/
|
|
748
|
+
export type PipeChatOptions = {
|
|
749
|
+
/**
|
|
750
|
+
* Override the stream key. Must match the `streamKey` on `TriggerChatTransport`.
|
|
751
|
+
* @default "chat"
|
|
752
|
+
*/
|
|
753
|
+
streamKey?: string;
|
|
754
|
+
/** An AbortSignal to cancel the stream. */
|
|
755
|
+
signal?: AbortSignal;
|
|
756
|
+
/**
|
|
757
|
+
* The target run ID to pipe to.
|
|
758
|
+
* @default "self" (current run)
|
|
759
|
+
*/
|
|
760
|
+
target?: string;
|
|
761
|
+
/** Override the default span name for this operation. */
|
|
762
|
+
spanName?: string;
|
|
763
|
+
};
|
|
764
|
+
/**
|
|
765
|
+
* Options for customizing the `toUIMessageStream()` call used when piping
|
|
766
|
+
* `streamText` results to the frontend.
|
|
767
|
+
*
|
|
768
|
+
* Set static defaults via `uiMessageStreamOptions` on `chat.agent()`, or
|
|
769
|
+
* override per-turn via `chat.setUIMessageStreamOptions()`.
|
|
770
|
+
*
|
|
771
|
+
* `onFinish` is omitted because it is managed internally for response capture.
|
|
772
|
+
* Use `streamText`'s `onFinish` for custom finish handling, or drop down to
|
|
773
|
+
* raw task mode with `chat.pipe()` for full control.
|
|
774
|
+
*
|
|
775
|
+
* `originalMessages` is omitted because it is automatically set from the
|
|
776
|
+
* accumulated conversation history, ensuring message IDs are reused across
|
|
777
|
+
* turns (e.g. for tool approval continuations).
|
|
778
|
+
*
|
|
779
|
+
* `generateMessageId` can be set to control ID generation for response
|
|
780
|
+
* messages (e.g. UUID-v7). If not set, the AI SDK's default `generateId` is used.
|
|
781
|
+
*/
|
|
782
|
+
export type ChatUIMessageStreamOptions<TUIM extends UIMessage = UIMessage> = Omit<UIMessageStreamOptions<TUIM>, "onFinish" | "originalMessages">;
|
|
783
|
+
/**
|
|
784
|
+
* An object with a `toUIMessageStream()` method (e.g. `StreamTextResult` from `streamText()`).
|
|
785
|
+
*/
|
|
786
|
+
type UIMessageStreamable = {
|
|
787
|
+
toUIMessageStream: (...args: any[]) => AsyncIterable<unknown> | ReadableStream<unknown>;
|
|
788
|
+
};
|
|
789
|
+
/**
|
|
790
|
+
* Pipes a chat stream to the realtime stream, making it available to the
|
|
791
|
+
* `TriggerChatTransport` on the frontend.
|
|
792
|
+
*
|
|
793
|
+
* Accepts:
|
|
794
|
+
* - A `StreamTextResult` from `streamText()` (has `.toUIMessageStream()`)
|
|
795
|
+
* - An `AsyncIterable` of `UIMessageChunk`s
|
|
796
|
+
* - A `ReadableStream` of `UIMessageChunk`s
|
|
797
|
+
*
|
|
798
|
+
* Must be called from inside a Trigger.dev task's `run` function.
|
|
799
|
+
*
|
|
800
|
+
* @example
|
|
801
|
+
* ```ts
|
|
802
|
+
* import { task } from "@trigger.dev/sdk";
|
|
803
|
+
* import { chat, type ChatTaskPayload } from "@trigger.dev/sdk/ai";
|
|
804
|
+
* import { streamText, convertToModelMessages } from "ai";
|
|
805
|
+
*
|
|
806
|
+
* export const myChatTask = task({
|
|
807
|
+
* id: "my-chat-task",
|
|
808
|
+
* run: async (payload: ChatTaskPayload) => {
|
|
809
|
+
* const result = streamText({
|
|
810
|
+
* model: openai("gpt-4o"),
|
|
811
|
+
* messages: payload.messages,
|
|
812
|
+
* });
|
|
813
|
+
*
|
|
814
|
+
* await chat.pipe(result);
|
|
815
|
+
* },
|
|
816
|
+
* });
|
|
817
|
+
* ```
|
|
818
|
+
*
|
|
819
|
+
* @example
|
|
820
|
+
* ```ts
|
|
821
|
+
* // Works from anywhere inside a task — even deep in your agent code
|
|
822
|
+
* async function runAgentLoop(messages: CoreMessage[]) {
|
|
823
|
+
* const result = streamText({ model, messages });
|
|
824
|
+
* await chat.pipe(result);
|
|
825
|
+
* }
|
|
826
|
+
* ```
|
|
827
|
+
*/
|
|
828
|
+
declare function pipeChat(source: UIMessageStreamable | AsyncIterable<unknown> | ReadableStream<unknown>, options?: PipeChatOptions): Promise<void>;
|
|
829
|
+
/**
|
|
830
|
+
* Options for defining a chat task.
|
|
831
|
+
*
|
|
832
|
+
* Extends the standard `TaskOptions` but pre-types the payload as `ChatTaskPayload`
|
|
833
|
+
* and overrides `run` to accept `ChatTaskRunPayload` (with abort signals).
|
|
834
|
+
*
|
|
835
|
+
* **Auto-piping:** If the `run` function returns a value with `.toUIMessageStream()`
|
|
836
|
+
* (like a `StreamTextResult`), the stream is automatically piped to the frontend.
|
|
837
|
+
*
|
|
838
|
+
* **Single-run mode:** By default, the task uses input streams so that the
|
|
839
|
+
* entire conversation lives inside one run. After each AI response, the task
|
|
840
|
+
* emits a control chunk and suspends via `messagesInput.wait()`. The frontend
|
|
841
|
+
* transport resumes the same run by sending the next message via input streams.
|
|
842
|
+
*/
|
|
843
|
+
/**
|
|
844
|
+
* Event passed to the `onPreload` callback.
|
|
845
|
+
*/
|
|
846
|
+
export type PreloadEvent<TClientData = unknown> = {
|
|
847
|
+
/** Task run context — same as `task({ run })` second-argument `ctx`. */
|
|
848
|
+
ctx: TaskRunContext;
|
|
849
|
+
/** The unique identifier for the chat session. */
|
|
850
|
+
chatId: string;
|
|
851
|
+
/** The Trigger.dev run ID for this conversation. */
|
|
852
|
+
runId: string;
|
|
853
|
+
/** A scoped access token for this chat run. */
|
|
854
|
+
chatAccessToken: string;
|
|
855
|
+
/** Custom data from the frontend. */
|
|
856
|
+
clientData?: TClientData;
|
|
857
|
+
/** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
|
|
858
|
+
writer: ChatWriter;
|
|
859
|
+
};
|
|
860
|
+
/**
|
|
861
|
+
* Event passed to the `onBoot` callback.
|
|
862
|
+
*
|
|
863
|
+
* Fires once at the start of every run boot — for the initial first-message
|
|
864
|
+
* run, for preloaded runs, AND for reactive continuation runs (post-cancel,
|
|
865
|
+
* post-crash, post-`endRun`, `chat.requestUpgrade`, OOM-retry attempts).
|
|
866
|
+
* Does NOT fire when the SAME run resumes from snapshot via the
|
|
867
|
+
* idle-window suspend/resume path — use `onChatResume` for that.
|
|
868
|
+
*
|
|
869
|
+
* Use this for per-process setup that needs to run every time a fresh
|
|
870
|
+
* worker picks up the chat: initialize `chat.local` state, open
|
|
871
|
+
* per-process resources (DB connections, sandboxes, etc.), or
|
|
872
|
+
* re-hydrate customer state from your DB on continuation.
|
|
873
|
+
*
|
|
874
|
+
* Ordering:
|
|
875
|
+
* - First message of a chat: `onBoot` → `onChatStart` → `onTurnStart` → `run()`
|
|
876
|
+
* - Preloaded run: `onBoot` → `onPreload` → (wait) → `onChatStart` → ...
|
|
877
|
+
* - Continuation run: `onBoot` → (wait for first message) → `onTurnStart` → `run()`
|
|
878
|
+
* (`onChatStart` does NOT fire on continuation runs)
|
|
879
|
+
*/
|
|
880
|
+
export type BootEvent<TClientData = unknown> = {
|
|
881
|
+
/** Task run context — same as `task({ run })` second-argument `ctx`. */
|
|
882
|
+
ctx: TaskRunContext;
|
|
883
|
+
/** The unique identifier for the chat session. */
|
|
884
|
+
chatId: string;
|
|
885
|
+
/** The Trigger.dev run ID for this run boot. */
|
|
886
|
+
runId: string;
|
|
887
|
+
/** A scoped access token for this chat run. Persist this for frontend reconnection. */
|
|
888
|
+
chatAccessToken: string;
|
|
889
|
+
/** Custom data from the frontend (passed via `metadata` on `sendMessage()` or the transport). */
|
|
890
|
+
clientData: TClientData;
|
|
891
|
+
/**
|
|
892
|
+
* True when this run is a reactive continuation — the prior run died
|
|
893
|
+
* (cancel / crash / `endRun` / `requestUpgrade` / OOM retry) and this
|
|
894
|
+
* fresh worker is taking over the chat. Branch on this to re-load
|
|
895
|
+
* customer-owned state from your DB.
|
|
896
|
+
*/
|
|
897
|
+
continuation: boolean;
|
|
898
|
+
/** Public id of the prior run when `continuation` is true. */
|
|
899
|
+
previousRunId?: string;
|
|
900
|
+
/** Whether this run was triggered as a preload. */
|
|
901
|
+
preloaded: boolean;
|
|
902
|
+
};
|
|
903
|
+
/**
|
|
904
|
+
* A tool call extracted from the partial assistant message of a dead run.
|
|
905
|
+
* Surfaced on `RecoveryBootEvent.pendingToolCalls` so the customer can
|
|
906
|
+
* decide how to repair the chain (synthesize a result, drop the partial,
|
|
907
|
+
* etc.).
|
|
908
|
+
*/
|
|
909
|
+
export type RecoveryPendingToolCall = {
|
|
910
|
+
/** The AI SDK tool call id. */
|
|
911
|
+
toolCallId: string;
|
|
912
|
+
/** The tool name (the `tool-${name}` suffix). */
|
|
913
|
+
toolName: string;
|
|
914
|
+
/** The input the model produced for the tool call. */
|
|
915
|
+
input: unknown;
|
|
916
|
+
/** The part index inside `partialAssistant.parts` for in-place edits. */
|
|
917
|
+
partIndex: number;
|
|
918
|
+
};
|
|
919
|
+
/**
|
|
920
|
+
* Event passed to the `onRecoveryBoot` callback.
|
|
921
|
+
*
|
|
922
|
+
* Fires once at boot when a continuation run inherits in-flight state from
|
|
923
|
+
* a dead predecessor (cancel / crash / OOM / deploy eviction / graceful
|
|
924
|
+
* `chat.requestUpgrade`). The runtime reads both `session.in` and
|
|
925
|
+
* `session.out` past the last `turn-complete` cursor and surfaces the
|
|
926
|
+
* recovered pieces here so the customer can shape the conversational
|
|
927
|
+
* chain before the first turn fires.
|
|
928
|
+
*
|
|
929
|
+
* Does NOT fire when there's nothing to recover (clean continuation after
|
|
930
|
+
* `chat.endRun()` with no buffered user messages, fresh chat, OOM retry
|
|
931
|
+
* after a successful turn-complete with no in-flight tail).
|
|
932
|
+
*
|
|
933
|
+
* Does NOT fire when `hydrateMessages` is registered (the customer owns
|
|
934
|
+
* persistence; recovery decisions live in their own DB query).
|
|
935
|
+
*/
|
|
936
|
+
export type RecoveryBootEvent<TUIM extends UIMessage = UIMessage> = {
|
|
937
|
+
/** Task run context — same as `task({ run })` second-argument `ctx`. */
|
|
938
|
+
ctx: TaskRunContext;
|
|
939
|
+
/** The unique identifier for the chat session. */
|
|
940
|
+
chatId: string;
|
|
941
|
+
/** The Trigger.dev run ID for this run boot. */
|
|
942
|
+
runId: string;
|
|
943
|
+
/** Public id of the prior run that died. */
|
|
944
|
+
previousRunId: string;
|
|
945
|
+
/**
|
|
946
|
+
* Best-effort cause of the predecessor's death. Currently always
|
|
947
|
+
* `"unknown"` — the run engine doesn't yet plumb the real reason
|
|
948
|
+
* into the continuation payload. Future SDK versions will narrow
|
|
949
|
+
* this. Don't branch behavior on it yet.
|
|
950
|
+
*/
|
|
951
|
+
cause: "cancelled" | "crashed" | "unknown";
|
|
952
|
+
/**
|
|
953
|
+
* The conversation chain that was successfully persisted by the
|
|
954
|
+
* predecessor's last `onTurnComplete`. Empty if the predecessor died
|
|
955
|
+
* before turn 1 ever completed.
|
|
956
|
+
*/
|
|
957
|
+
settledMessages: TUIM[];
|
|
958
|
+
/**
|
|
959
|
+
* User messages that arrived on `session.in` past the cursor — i.e.
|
|
960
|
+
* the message(s) the predecessor was processing or had queued when
|
|
961
|
+
* it died. The runtime's default is to re-dispatch each as a fresh
|
|
962
|
+
* turn after the chain is restored. Return a different list via
|
|
963
|
+
* `recoveredTurns` to skip / reorder / collapse them.
|
|
964
|
+
*/
|
|
965
|
+
inFlightUsers: TUIM[];
|
|
966
|
+
/**
|
|
967
|
+
* The trailing assistant message the predecessor was streaming when
|
|
968
|
+
* it died — the orphan whose `turn-complete` never fired. Undefined
|
|
969
|
+
* if the predecessor died before any assistant output reached
|
|
970
|
+
* `session.out` (cancel-before-first-token, snapshot-only path).
|
|
971
|
+
*/
|
|
972
|
+
partialAssistant: TUIM | undefined;
|
|
973
|
+
/**
|
|
974
|
+
* Tool calls extracted from `partialAssistant.parts` that the model
|
|
975
|
+
* had started but the tool runtime never resolved. Empty when
|
|
976
|
+
* `partialAssistant` is undefined or carries no `input-available`
|
|
977
|
+
* tool parts.
|
|
978
|
+
*/
|
|
979
|
+
pendingToolCalls: RecoveryPendingToolCall[];
|
|
980
|
+
/**
|
|
981
|
+
* Lazy session.out writer — identical to the `writer` passed to
|
|
982
|
+
* `onTurnStart` / `onTurnComplete` / `onChatStart`. Use this to emit
|
|
983
|
+
* a recovery signal (e.g. a `data-chat-recovery` UIMessage chunk)
|
|
984
|
+
* BEFORE the first recovered turn fires so the bridge can render a
|
|
985
|
+
* "recovering..." banner. Lazy: no overhead if unused.
|
|
986
|
+
*/
|
|
987
|
+
writer: ChatWriter;
|
|
988
|
+
};
|
|
989
|
+
/**
|
|
990
|
+
* Return shape for the `onRecoveryBoot` callback. Every field is optional —
|
|
991
|
+
* omit one to accept the default.
|
|
992
|
+
*/
|
|
993
|
+
export type RecoveryBootResult<TUIM extends UIMessage = UIMessage> = {
|
|
994
|
+
/**
|
|
995
|
+
* The chain the new run boots with. Replaces the default
|
|
996
|
+
* (`settledMessages`). Use this to keep the partial assistant in
|
|
997
|
+
* context, mutate its tool parts to inject synthesized results,
|
|
998
|
+
* collapse history, etc.
|
|
999
|
+
*
|
|
1000
|
+
* Ignored when `hydrateMessages` is registered (the hydrate hook
|
|
1001
|
+
* runs per-turn and overwrites the chain).
|
|
1002
|
+
*/
|
|
1003
|
+
chain?: TUIM[];
|
|
1004
|
+
/**
|
|
1005
|
+
* The user messages to re-dispatch as fresh turns after the chain is
|
|
1006
|
+
* restored. Default: `inFlightUsers` (re-process every in-flight
|
|
1007
|
+
* user). Return `[]` to suppress all of them; return a filtered /
|
|
1008
|
+
* reordered subset to skip specific ones.
|
|
1009
|
+
*/
|
|
1010
|
+
recoveredTurns?: TUIM[];
|
|
1011
|
+
/**
|
|
1012
|
+
* Awaitable run AFTER the writer flushes and BEFORE the first
|
|
1013
|
+
* recovered turn fires. Use for blocking persistence (e.g. write the
|
|
1014
|
+
* partial assistant to your DB so a follow-up turn can reference
|
|
1015
|
+
* it). Errors bubble — wrap your own try/catch if you want to soft-
|
|
1016
|
+
* fail.
|
|
1017
|
+
*/
|
|
1018
|
+
beforeBoot?: () => Promise<void>;
|
|
1019
|
+
};
|
|
1020
|
+
/**
|
|
1021
|
+
* Event passed to the `onChatStart` callback.
|
|
1022
|
+
*
|
|
1023
|
+
* Fires exactly once per chat, on the very first user message of the chat's
|
|
1024
|
+
* lifetime. Does NOT fire on continuation runs (post-`endRun`,
|
|
1025
|
+
* post-waitpoint-timeout, `chat.requestUpgrade`) or on OOM-retry attempts —
|
|
1026
|
+
* those are runs of an already-started chat.
|
|
1027
|
+
*/
|
|
1028
|
+
export type ChatStartEvent<TClientData = unknown> = {
|
|
1029
|
+
/** Task run context — same as `task({ run })` second-argument `ctx`. */
|
|
1030
|
+
ctx: TaskRunContext;
|
|
1031
|
+
/** The unique identifier for the chat session. */
|
|
1032
|
+
chatId: string;
|
|
1033
|
+
/**
|
|
1034
|
+
* The initial model-ready messages for this conversation. Typically just
|
|
1035
|
+
* the first user message (or empty if `chat.headStart` is in play and the
|
|
1036
|
+
* seed message is supplied elsewhere). Since this hook only fires for the
|
|
1037
|
+
* chat's very first message, there's no prior history to load here.
|
|
1038
|
+
*/
|
|
1039
|
+
messages: ModelMessage[];
|
|
1040
|
+
/** Custom data from the frontend (passed via `metadata` on `sendMessage()` or the transport). */
|
|
1041
|
+
clientData: TClientData;
|
|
1042
|
+
/** The Trigger.dev run ID for this conversation. */
|
|
1043
|
+
runId: string;
|
|
1044
|
+
/** A scoped access token for this chat run. Persist this for frontend reconnection. */
|
|
1045
|
+
chatAccessToken: string;
|
|
1046
|
+
/**
|
|
1047
|
+
* @deprecated Always `false` — `onChatStart` no longer fires on continuation
|
|
1048
|
+
* runs. Kept for backward compatibility; remove your `continuation` checks
|
|
1049
|
+
* from `onChatStart` and rely on the contract (this hook fires exactly once
|
|
1050
|
+
* per chat, on the very first message).
|
|
1051
|
+
*/
|
|
1052
|
+
continuation: boolean;
|
|
1053
|
+
/**
|
|
1054
|
+
* @deprecated Always `undefined` — `onChatStart` no longer fires on
|
|
1055
|
+
* continuation runs.
|
|
1056
|
+
*/
|
|
1057
|
+
previousRunId?: string;
|
|
1058
|
+
/** Whether this run was preloaded before the first message. */
|
|
1059
|
+
preloaded: boolean;
|
|
1060
|
+
/** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
|
|
1061
|
+
writer: ChatWriter;
|
|
1062
|
+
};
|
|
1063
|
+
/**
|
|
1064
|
+
* Event passed to the `hydrateMessages` callback.
|
|
1065
|
+
*/
|
|
1066
|
+
export type HydrateMessagesEvent<TClientData = unknown, TUIM extends UIMessage = UIMessage> = {
|
|
1067
|
+
/** The unique identifier for the chat session. */
|
|
1068
|
+
chatId: string;
|
|
1069
|
+
/** The turn number (0-indexed). */
|
|
1070
|
+
turn: number;
|
|
1071
|
+
/** The trigger type for this turn. */
|
|
1072
|
+
trigger: "submit-message" | "regenerate-message" | "action";
|
|
1073
|
+
/** Validated incoming UI messages from the wire payload (what the frontend sent). Empty for actions. */
|
|
1074
|
+
incomingMessages: TUIM[];
|
|
1075
|
+
/** The accumulated UI messages before this turn (empty on turn 0). */
|
|
1076
|
+
previousMessages: TUIM[];
|
|
1077
|
+
/** Parsed client data from the transport metadata. */
|
|
1078
|
+
clientData?: TClientData;
|
|
1079
|
+
/** Whether this run is continuing from a previous run. */
|
|
1080
|
+
continuation: boolean;
|
|
1081
|
+
/** The ID of the previous run (if continuation). */
|
|
1082
|
+
previousRunId?: string;
|
|
1083
|
+
};
|
|
1084
|
+
/**
|
|
1085
|
+
* Event passed to the `onValidateMessages` callback.
|
|
1086
|
+
*/
|
|
1087
|
+
export type ValidateMessagesEvent<TUIM extends UIMessage = UIMessage> = {
|
|
1088
|
+
/** The incoming UI messages for this turn (after cleanup of aborted tool parts). */
|
|
1089
|
+
messages: TUIM[];
|
|
1090
|
+
/** The unique identifier for the chat session. */
|
|
1091
|
+
chatId: string;
|
|
1092
|
+
/** The turn number (0-indexed). */
|
|
1093
|
+
turn: number;
|
|
1094
|
+
/** The trigger type for this turn. */
|
|
1095
|
+
trigger: "submit-message" | "regenerate-message" | "preload" | "close";
|
|
1096
|
+
};
|
|
1097
|
+
/**
|
|
1098
|
+
* Event passed to the `onAction` callback.
|
|
1099
|
+
*/
|
|
1100
|
+
export type ActionEvent<TAction = unknown, TClientData = unknown, TUIM extends UIMessage = UIMessage> = {
|
|
1101
|
+
/** The parsed and validated action payload. */
|
|
1102
|
+
action: TAction;
|
|
1103
|
+
/** The unique identifier for the chat session. */
|
|
1104
|
+
chatId: string;
|
|
1105
|
+
/** The turn number (0-indexed). */
|
|
1106
|
+
turn: number;
|
|
1107
|
+
/** Parsed client data from the transport metadata. */
|
|
1108
|
+
clientData?: TClientData;
|
|
1109
|
+
/** The accumulated UI messages (after hydration, if set). */
|
|
1110
|
+
uiMessages: TUIM[];
|
|
1111
|
+
/** The accumulated model messages (after hydration, if set). */
|
|
1112
|
+
messages: ModelMessage[];
|
|
1113
|
+
};
|
|
1114
|
+
/**
|
|
1115
|
+
* Event passed to the `onTurnStart` callback.
|
|
1116
|
+
*/
|
|
1117
|
+
export type TurnStartEvent<TClientData = unknown, TUIM extends UIMessage = UIMessage> = {
|
|
1118
|
+
/** Task run context — same as `task({ run })` second-argument `ctx`. */
|
|
1119
|
+
ctx: TaskRunContext;
|
|
1120
|
+
/** The unique identifier for the chat session. */
|
|
1121
|
+
chatId: string;
|
|
1122
|
+
/** The accumulated model-ready messages (all turns so far, including new user message). */
|
|
1123
|
+
messages: ModelMessage[];
|
|
1124
|
+
/** The accumulated UI messages (all turns so far, including new user message). */
|
|
1125
|
+
uiMessages: TUIM[];
|
|
1126
|
+
/** The turn number (0-indexed). */
|
|
1127
|
+
turn: number;
|
|
1128
|
+
/** The Trigger.dev run ID for this conversation. */
|
|
1129
|
+
runId: string;
|
|
1130
|
+
/** A scoped access token for this chat run. */
|
|
1131
|
+
chatAccessToken: string;
|
|
1132
|
+
/** Custom data from the frontend. */
|
|
1133
|
+
clientData?: TClientData;
|
|
1134
|
+
/** Whether this run is continuing an existing chat (previous run timed out or was cancelled). False for brand new chats. */
|
|
1135
|
+
continuation: boolean;
|
|
1136
|
+
/** The run ID of the previous run (only set when `continuation` is true). */
|
|
1137
|
+
previousRunId?: string;
|
|
1138
|
+
/** Whether this run was preloaded before the first message. */
|
|
1139
|
+
preloaded: boolean;
|
|
1140
|
+
/** Token usage from the previous turn. Undefined on turn 0. */
|
|
1141
|
+
previousTurnUsage?: LanguageModelUsage;
|
|
1142
|
+
/** Cumulative token usage across all completed turns so far. */
|
|
1143
|
+
totalUsage: LanguageModelUsage;
|
|
1144
|
+
/** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
|
|
1145
|
+
writer: ChatWriter;
|
|
1146
|
+
};
|
|
1147
|
+
/**
|
|
1148
|
+
* Event passed to the `onTurnComplete` callback.
|
|
1149
|
+
*/
|
|
1150
|
+
export type TurnCompleteEvent<TClientData = unknown, TUIM extends UIMessage = UIMessage> = {
|
|
1151
|
+
/** Task run context — same as `task({ run })` second-argument `ctx`. */
|
|
1152
|
+
ctx: TaskRunContext;
|
|
1153
|
+
/** The unique identifier for the chat session. */
|
|
1154
|
+
chatId: string;
|
|
1155
|
+
/** The full accumulated conversation in model format (all turns so far). */
|
|
1156
|
+
messages: ModelMessage[];
|
|
1157
|
+
/**
|
|
1158
|
+
* The full accumulated conversation in UI format (all turns so far).
|
|
1159
|
+
* This is the format expected by `useChat` — store this for persistence.
|
|
1160
|
+
*/
|
|
1161
|
+
uiMessages: TUIM[];
|
|
1162
|
+
/**
|
|
1163
|
+
* Only the new model messages from this turn (user message(s) + assistant response).
|
|
1164
|
+
* Useful for appending to an existing conversation record.
|
|
1165
|
+
*/
|
|
1166
|
+
newMessages: ModelMessage[];
|
|
1167
|
+
/**
|
|
1168
|
+
* Only the new UI messages from this turn (user message(s) + assistant response).
|
|
1169
|
+
* Useful for inserting individual message records instead of overwriting the full history.
|
|
1170
|
+
*/
|
|
1171
|
+
newUIMessages: TUIM[];
|
|
1172
|
+
/** The assistant's response for this turn, with aborted parts cleaned up when `stopped` is true. Undefined if `pipeChat` was used manually. */
|
|
1173
|
+
responseMessage: TUIM | undefined;
|
|
1174
|
+
/**
|
|
1175
|
+
* The raw assistant response before abort cleanup. Includes incomplete tool parts
|
|
1176
|
+
* (`input-available`, `partial-call`) and streaming reasoning/text parts.
|
|
1177
|
+
* Use this if you need custom cleanup logic. Same as `responseMessage` when not stopped.
|
|
1178
|
+
*/
|
|
1179
|
+
rawResponseMessage: TUIM | undefined;
|
|
1180
|
+
/** The turn number (0-indexed). */
|
|
1181
|
+
turn: number;
|
|
1182
|
+
/** The Trigger.dev run ID for this conversation. */
|
|
1183
|
+
runId: string;
|
|
1184
|
+
/** A fresh scoped access token for this chat run (renewed each turn). Persist this for frontend reconnection. */
|
|
1185
|
+
chatAccessToken: string;
|
|
1186
|
+
/** The last event ID from the stream writer. Use this with `resume: true` to avoid replaying events after refresh. */
|
|
1187
|
+
lastEventId?: string;
|
|
1188
|
+
/** Custom data from the frontend. */
|
|
1189
|
+
clientData?: TClientData;
|
|
1190
|
+
/** Whether the user stopped generation during this turn. */
|
|
1191
|
+
stopped: boolean;
|
|
1192
|
+
/** Whether this run is continuing an existing chat (previous run timed out or was cancelled). False for brand new chats. */
|
|
1193
|
+
continuation: boolean;
|
|
1194
|
+
/** The run ID of the previous run (only set when `continuation` is true). */
|
|
1195
|
+
previousRunId?: string;
|
|
1196
|
+
/** Whether this run was preloaded before the first message. */
|
|
1197
|
+
preloaded: boolean;
|
|
1198
|
+
/** Token usage for this turn. Undefined if usage couldn't be captured (e.g. manual pipeChat). */
|
|
1199
|
+
usage?: LanguageModelUsage;
|
|
1200
|
+
/** Cumulative token usage across all turns in this run (including this turn). */
|
|
1201
|
+
totalUsage: LanguageModelUsage;
|
|
1202
|
+
/**
|
|
1203
|
+
* Why the LLM stopped generating this turn:
|
|
1204
|
+
* - `"stop"` — model generated a stop sequence (normal completion)
|
|
1205
|
+
* - `"tool-calls"` — model stopped on one or more tool calls. If any tool
|
|
1206
|
+
* has no `execute` function (e.g. an `ask_user` HITL tool), the turn is
|
|
1207
|
+
* paused awaiting user input; inspect `responseMessage.parts` for tool
|
|
1208
|
+
* parts in `input-available` state to distinguish.
|
|
1209
|
+
* - `"length"` — max tokens reached
|
|
1210
|
+
* - `"content-filter"` — content filter stopped the model
|
|
1211
|
+
* - `"error"` — model errored
|
|
1212
|
+
* - `"other"` — provider-specific reason
|
|
1213
|
+
*
|
|
1214
|
+
* Undefined if the underlying stream didn't provide a finish reason (e.g.
|
|
1215
|
+
* manual `pipeChat()` or an aborted stream).
|
|
1216
|
+
*/
|
|
1217
|
+
finishReason?: FinishReason;
|
|
1218
|
+
};
|
|
1219
|
+
/**
|
|
1220
|
+
* Event passed to the `onBeforeTurnComplete` callback.
|
|
1221
|
+
* Same as `TurnCompleteEvent` but includes a `writer` since the stream is still open.
|
|
1222
|
+
*/
|
|
1223
|
+
export type BeforeTurnCompleteEvent<TClientData = unknown, TUIM extends UIMessage = UIMessage> = TurnCompleteEvent<TClientData, TUIM> & {
|
|
1224
|
+
/** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
|
|
1225
|
+
writer: ChatWriter;
|
|
1226
|
+
};
|
|
1227
|
+
/**
|
|
1228
|
+
* Discriminated event passed to the `onChatSuspend` callback.
|
|
1229
|
+
* Use `phase` to distinguish preload vs turn suspension.
|
|
1230
|
+
*/
|
|
1231
|
+
export type ChatSuspendEvent<TClientData = unknown, TUIM extends UIMessage = UIMessage> = {
|
|
1232
|
+
/** Suspend is happening after onPreload, before the first message. */
|
|
1233
|
+
phase: "preload";
|
|
1234
|
+
/** Task run context. */
|
|
1235
|
+
ctx: TaskRunContext;
|
|
1236
|
+
/** The chat session ID. */
|
|
1237
|
+
chatId: string;
|
|
1238
|
+
/** The Trigger.dev run ID. */
|
|
1239
|
+
runId: string;
|
|
1240
|
+
/** Custom data from the frontend. */
|
|
1241
|
+
clientData?: TClientData;
|
|
1242
|
+
} | {
|
|
1243
|
+
/**
|
|
1244
|
+
* Suspend is happening on a continuation run that booted with no incoming
|
|
1245
|
+
* message (post-`endRun`, post-waitpoint-timeout, etc.) and is waiting
|
|
1246
|
+
* for the next session.in record before running any turn. Distinct from
|
|
1247
|
+
* `phase: "preload"` — the chat already started; `onPreload` has not
|
|
1248
|
+
* fired and will not fire on this run.
|
|
1249
|
+
*/
|
|
1250
|
+
phase: "continuation";
|
|
1251
|
+
/** Task run context. */
|
|
1252
|
+
ctx: TaskRunContext;
|
|
1253
|
+
/** The chat session ID. */
|
|
1254
|
+
chatId: string;
|
|
1255
|
+
/** The Trigger.dev run ID. */
|
|
1256
|
+
runId: string;
|
|
1257
|
+
/** Custom data from the frontend. */
|
|
1258
|
+
clientData?: TClientData;
|
|
1259
|
+
} | {
|
|
1260
|
+
/** Suspend is happening after a completed turn, waiting for the next message. */
|
|
1261
|
+
phase: "turn";
|
|
1262
|
+
/** Task run context. */
|
|
1263
|
+
ctx: TaskRunContext;
|
|
1264
|
+
/** The chat session ID. */
|
|
1265
|
+
chatId: string;
|
|
1266
|
+
/** The Trigger.dev run ID. */
|
|
1267
|
+
runId: string;
|
|
1268
|
+
/** The turn number (0-indexed) that just completed. */
|
|
1269
|
+
turn: number;
|
|
1270
|
+
/** The accumulated model messages after the completed turn. */
|
|
1271
|
+
messages: ModelMessage[];
|
|
1272
|
+
/** The accumulated UI messages after the completed turn. */
|
|
1273
|
+
uiMessages: TUIM[];
|
|
1274
|
+
/** Custom data from the frontend. */
|
|
1275
|
+
clientData?: TClientData;
|
|
1276
|
+
};
|
|
1277
|
+
/**
|
|
1278
|
+
* Discriminated event passed to the `onChatResume` callback.
|
|
1279
|
+
* Use `phase` to distinguish preload vs turn resumption.
|
|
1280
|
+
*/
|
|
1281
|
+
export type ChatResumeEvent<TClientData = unknown, TUIM extends UIMessage = UIMessage> = {
|
|
1282
|
+
/** First message arrived after preload suspension. */
|
|
1283
|
+
phase: "preload";
|
|
1284
|
+
/** Task run context. */
|
|
1285
|
+
ctx: TaskRunContext;
|
|
1286
|
+
/** The chat session ID. */
|
|
1287
|
+
chatId: string;
|
|
1288
|
+
/** The Trigger.dev run ID. */
|
|
1289
|
+
runId: string;
|
|
1290
|
+
/** Custom data from the frontend. */
|
|
1291
|
+
clientData?: TClientData;
|
|
1292
|
+
} | {
|
|
1293
|
+
/**
|
|
1294
|
+
* First message arrived after continuation-wait suspension. Distinct
|
|
1295
|
+
* from `phase: "preload"` — the chat already started; this is a new
|
|
1296
|
+
* run picking up after a prior run ended (`endRun`, waitpoint timeout,
|
|
1297
|
+
* etc.).
|
|
1298
|
+
*/
|
|
1299
|
+
phase: "continuation";
|
|
1300
|
+
/** Task run context. */
|
|
1301
|
+
ctx: TaskRunContext;
|
|
1302
|
+
/** The chat session ID. */
|
|
1303
|
+
chatId: string;
|
|
1304
|
+
/** The Trigger.dev run ID. */
|
|
1305
|
+
runId: string;
|
|
1306
|
+
/** Custom data from the frontend. */
|
|
1307
|
+
clientData?: TClientData;
|
|
1308
|
+
} | {
|
|
1309
|
+
/** Next message arrived after turn suspension. */
|
|
1310
|
+
phase: "turn";
|
|
1311
|
+
/** Task run context. */
|
|
1312
|
+
ctx: TaskRunContext;
|
|
1313
|
+
/** The chat session ID. */
|
|
1314
|
+
chatId: string;
|
|
1315
|
+
/** The Trigger.dev run ID. */
|
|
1316
|
+
runId: string;
|
|
1317
|
+
/** The turn number that was completed before suspension. */
|
|
1318
|
+
turn: number;
|
|
1319
|
+
/** The accumulated model messages (from before suspension). */
|
|
1320
|
+
messages: ModelMessage[];
|
|
1321
|
+
/** The accumulated UI messages (from before suspension). */
|
|
1322
|
+
uiMessages: TUIM[];
|
|
1323
|
+
/** Custom data from the frontend. */
|
|
1324
|
+
clientData?: TClientData;
|
|
1325
|
+
};
|
|
1326
|
+
export type ChatAgentOptions<TIdentifier extends string, TClientDataSchema extends TaskSchema | undefined = undefined, TUIMessage extends UIMessage = UIMessage, TActionSchema extends TaskSchema | undefined = undefined> = Omit<TaskOptions<TIdentifier, ChatTaskWirePayload<TUIMessage, inferSchemaIn<TClientDataSchema>>, unknown>, "run" | "retry"> & {
|
|
1327
|
+
/**
|
|
1328
|
+
* Fallback machine preset to use when an attempt fails with an
|
|
1329
|
+
* out-of-memory (OOM) error. Setting this enables a single OOM retry:
|
|
1330
|
+
* the next attempt boots on the larger machine, and the chat picks
|
|
1331
|
+
* up via the standard continuation path (same `chatId` / Session,
|
|
1332
|
+
* accumulator rebuilds via `hydrateMessages` or post-`onTurnStart`
|
|
1333
|
+
* persisted state).
|
|
1334
|
+
*
|
|
1335
|
+
* Set `machine` (top-level `TaskOptions`) to control the *default*
|
|
1336
|
+
* machine the agent runs on. `oomMachine` is the *retry-only* swap.
|
|
1337
|
+
*
|
|
1338
|
+
* Note: an OOM retry restarts the entire turn from the top — the
|
|
1339
|
+
* model call and any in-flight tool executes re-run on the larger
|
|
1340
|
+
* machine. Make tool executes idempotent or persist results before
|
|
1341
|
+
* returning if you can't tolerate re-execution.
|
|
1342
|
+
*
|
|
1343
|
+
* Generic `retry` options are not exposed on `chat.agent` because
|
|
1344
|
+
* arbitrary retries against an LLM-driven loop tend to be expensive
|
|
1345
|
+
* and side-effecting. If you need richer retry semantics, drop down
|
|
1346
|
+
* to `chat.task` (the raw primitive).
|
|
1347
|
+
*
|
|
1348
|
+
* @example
|
|
1349
|
+
* ```ts
|
|
1350
|
+
* chat.agent({
|
|
1351
|
+
* id: "my-chat",
|
|
1352
|
+
* machine: "small-1x",
|
|
1353
|
+
* oomMachine: "medium-2x",
|
|
1354
|
+
* run: async ({ messages, signal }) =>
|
|
1355
|
+
* streamText({ model, messages, abortSignal: signal }),
|
|
1356
|
+
* });
|
|
1357
|
+
* ```
|
|
1358
|
+
*/
|
|
1359
|
+
oomMachine?: MachinePresetName;
|
|
1360
|
+
/**
|
|
1361
|
+
* Schema for validating `clientData` from the frontend.
|
|
1362
|
+
* Accepts Zod, ArkType, Valibot, or any supported schema library.
|
|
1363
|
+
* When provided, `clientData` is parsed and typed in all hooks and `run`.
|
|
1364
|
+
*
|
|
1365
|
+
* @example
|
|
1366
|
+
* ```ts
|
|
1367
|
+
* import { z } from "zod";
|
|
1368
|
+
*
|
|
1369
|
+
* chat.agent({
|
|
1370
|
+
* id: "my-chat",
|
|
1371
|
+
* clientDataSchema: z.object({ model: z.string().optional(), userId: z.string() }),
|
|
1372
|
+
* run: async ({ messages, clientData, ctx, signal }) => {
|
|
1373
|
+
* // clientData is typed as { model?: string; userId: string }
|
|
1374
|
+
* // ctx is the same TaskRunContext as in task({ run: (payload, { ctx }) => ... })
|
|
1375
|
+
* },
|
|
1376
|
+
* });
|
|
1377
|
+
* ```
|
|
1378
|
+
*/
|
|
1379
|
+
clientDataSchema?: TClientDataSchema;
|
|
1380
|
+
/**
|
|
1381
|
+
* Schema for validating custom actions sent via `transport.sendAction()`.
|
|
1382
|
+
*
|
|
1383
|
+
* When the frontend sends `trigger: "action"`, the `action` payload is
|
|
1384
|
+
* parsed against this schema before reaching `onAction`. Invalid actions
|
|
1385
|
+
* throw and abort the turn.
|
|
1386
|
+
*
|
|
1387
|
+
* @example
|
|
1388
|
+
* ```ts
|
|
1389
|
+
* import { z } from "zod";
|
|
1390
|
+
*
|
|
1391
|
+
* chat.agent({
|
|
1392
|
+
* id: "my-chat",
|
|
1393
|
+
* actionSchema: z.discriminatedUnion("type", [
|
|
1394
|
+
* z.object({ type: z.literal("undo") }),
|
|
1395
|
+
* z.object({ type: z.literal("rollback"), targetMessageId: z.string() }),
|
|
1396
|
+
* ]),
|
|
1397
|
+
* onAction: async ({ action }) => {
|
|
1398
|
+
* if (action.type === "undo") chat.history.slice(0, -2);
|
|
1399
|
+
* if (action.type === "rollback") chat.history.rollbackTo(action.targetMessageId);
|
|
1400
|
+
* },
|
|
1401
|
+
* run: async ({ messages, signal }) => { ... },
|
|
1402
|
+
* });
|
|
1403
|
+
* ```
|
|
1404
|
+
*/
|
|
1405
|
+
actionSchema?: TActionSchema;
|
|
1406
|
+
/**
|
|
1407
|
+
* Called when the frontend sends a custom action via `transport.sendAction()`.
|
|
1408
|
+
*
|
|
1409
|
+
* Actions are not turns. They fire `hydrateMessages` (if configured) and
|
|
1410
|
+
* `onAction` only — no `onTurnStart` / `prepareMessages` /
|
|
1411
|
+
* `onBeforeTurnComplete` / `onTurnComplete`, no `run()`. Use
|
|
1412
|
+
* `chat.history.*` inside `onAction` to mutate state.
|
|
1413
|
+
*
|
|
1414
|
+
* To produce a model response from an action, return a
|
|
1415
|
+
* `StreamTextResult` (auto-piped), `string`, or `UIMessage`. Returning
|
|
1416
|
+
* `void` or nothing is the side-effect-only default.
|
|
1417
|
+
*/
|
|
1418
|
+
onAction?: (event: ActionEvent<[
|
|
1419
|
+
TActionSchema
|
|
1420
|
+
] extends [TaskSchema] ? inferSchemaOut<TActionSchema> : unknown, inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<unknown> | unknown;
|
|
1421
|
+
/**
|
|
1422
|
+
* The run function for the chat task.
|
|
1423
|
+
*
|
|
1424
|
+
* Receives a `ChatTaskRunPayload` with the conversation messages, chat session ID,
|
|
1425
|
+
* trigger type, task `ctx` (same as `task({ run })`’s second argument), and abort signals
|
|
1426
|
+
* (`signal`, `cancelSignal`, `stopSignal`).
|
|
1427
|
+
*
|
|
1428
|
+
* **Auto-piping:** If this function returns a value with `.toUIMessageStream()`,
|
|
1429
|
+
* the stream is automatically piped to the frontend.
|
|
1430
|
+
*/
|
|
1431
|
+
run: (payload: ChatTaskRunPayload<inferSchemaOut<TClientDataSchema>>) => Promise<unknown>;
|
|
1432
|
+
/**
|
|
1433
|
+
* Called once at the start of every run boot — for the initial run, for
|
|
1434
|
+
* preloaded runs, AND for reactive continuation runs (post-cancel /
|
|
1435
|
+
* crash / `endRun` / `requestUpgrade` / OOM retry).
|
|
1436
|
+
*
|
|
1437
|
+
* Use this for per-process setup that needs to run every time a fresh
|
|
1438
|
+
* worker picks up the chat: initialize `chat.local` state, open
|
|
1439
|
+
* per-process resources, or re-hydrate customer state from your DB
|
|
1440
|
+
* on continuation. Branch on `continuation` to decide whether to load
|
|
1441
|
+
* existing state vs. start fresh.
|
|
1442
|
+
*
|
|
1443
|
+
* Fires BEFORE `onPreload` (preloaded path) and `onChatStart`
|
|
1444
|
+
* (first-message path), so `chat.local` is safe to read in those hooks.
|
|
1445
|
+
*
|
|
1446
|
+
* Does NOT fire when the same run resumes from snapshot via the
|
|
1447
|
+
* idle-window suspend/resume path — use `onChatResume` for that.
|
|
1448
|
+
*
|
|
1449
|
+
* @example
|
|
1450
|
+
* ```ts
|
|
1451
|
+
* onBoot: async ({ chatId, clientData, continuation }) => {
|
|
1452
|
+
* const user = await db.user.findFirst({ where: { id: clientData.userId } });
|
|
1453
|
+
* userContext.init({ name: user.name, plan: user.plan });
|
|
1454
|
+
* if (continuation) {
|
|
1455
|
+
* // re-hydrate any per-chat in-memory state from your DB
|
|
1456
|
+
* }
|
|
1457
|
+
* }
|
|
1458
|
+
* ```
|
|
1459
|
+
*/
|
|
1460
|
+
onBoot?: (event: BootEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void;
|
|
1461
|
+
/**
|
|
1462
|
+
* Recovery boot hook — fires once on a continuation run that inherited
|
|
1463
|
+
* in-flight state from a dead predecessor (cancel / crash / OOM /
|
|
1464
|
+
* deploy eviction / `chat.requestUpgrade()`). The runtime reads both
|
|
1465
|
+
* stream tails past the last `turn-complete` cursor and hands the
|
|
1466
|
+
* customer the recovered pieces (settled chain, in-flight users,
|
|
1467
|
+
* partial assistant, pending tool calls) so the chain can be shaped
|
|
1468
|
+
* before the first recovered turn fires.
|
|
1469
|
+
*
|
|
1470
|
+
* Does NOT fire when there's nothing to recover — e.g. a clean
|
|
1471
|
+
* continuation after `chat.endRun()` with no buffered user, a fresh
|
|
1472
|
+
* chat, or an OOM retry on top of a complete snapshot.
|
|
1473
|
+
*
|
|
1474
|
+
* Does NOT fire when `hydrateMessages` is registered — that hook owns
|
|
1475
|
+
* the per-turn chain and overlapping recovery decisions belong in the
|
|
1476
|
+
* customer's DB.
|
|
1477
|
+
*
|
|
1478
|
+
* Defaults (returned when the hook is omitted or returns no field):
|
|
1479
|
+
* - `chain` = `settledMessages` (drop the orphan partial)
|
|
1480
|
+
* - `recoveredTurns` = `inFlightUsers` (re-dispatch every user)
|
|
1481
|
+
*
|
|
1482
|
+
* @example
|
|
1483
|
+
* ```ts
|
|
1484
|
+
* onRecoveryBoot: async ({ partialAssistant, inFlightUsers, writer, cause }) => {
|
|
1485
|
+
* writer.write({
|
|
1486
|
+
* type: "data-chat-recovery",
|
|
1487
|
+
* id: generateId(),
|
|
1488
|
+
* data: { cause, partial: partialAssistant?.id },
|
|
1489
|
+
* });
|
|
1490
|
+
* return {}; // accept defaults: drop partial, re-dispatch users
|
|
1491
|
+
* }
|
|
1492
|
+
* ```
|
|
1493
|
+
*/
|
|
1494
|
+
onRecoveryBoot?: (event: RecoveryBootEvent<TUIMessage>) => Promise<RecoveryBootResult<TUIMessage> | void> | RecoveryBootResult<TUIMessage> | void;
|
|
1495
|
+
/**
|
|
1496
|
+
* Called when a preloaded run starts, before the first message arrives.
|
|
1497
|
+
*
|
|
1498
|
+
* Use this to initialize state, create DB records, and load context early —
|
|
1499
|
+
* so everything is ready when the user's first message comes through.
|
|
1500
|
+
*
|
|
1501
|
+
* @example
|
|
1502
|
+
* ```ts
|
|
1503
|
+
* onPreload: async ({ ctx, chatId, clientData }) => {
|
|
1504
|
+
* await db.chat.create({ data: { id: chatId } });
|
|
1505
|
+
* userContext.init(await loadUser(clientData.userId));
|
|
1506
|
+
* }
|
|
1507
|
+
* ```
|
|
1508
|
+
*/
|
|
1509
|
+
onPreload?: (event: PreloadEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void;
|
|
1510
|
+
/**
|
|
1511
|
+
* Called exactly once per chat, on the very first user message of the
|
|
1512
|
+
* chat's lifetime. Does NOT fire on continuation runs (post-`endRun`,
|
|
1513
|
+
* post-waitpoint-timeout, `chat.requestUpgrade`) or on OOM-retry attempts —
|
|
1514
|
+
* those are runs of an already-started chat.
|
|
1515
|
+
*
|
|
1516
|
+
* Use this for one-time chat-setup work — creating the Chat DB row,
|
|
1517
|
+
* initializing per-chat in-memory state, minting resources tied to the
|
|
1518
|
+
* chat's lifetime. Safe to assume no prior history exists here.
|
|
1519
|
+
*
|
|
1520
|
+
* For per-turn work, use `onTurnStart`.
|
|
1521
|
+
*
|
|
1522
|
+
* @example
|
|
1523
|
+
* ```ts
|
|
1524
|
+
* onChatStart: async ({ ctx, chatId, messages, clientData }) => {
|
|
1525
|
+
* await db.chat.create({ data: { id: chatId, userId: clientData.userId } });
|
|
1526
|
+
* }
|
|
1527
|
+
* ```
|
|
1528
|
+
*/
|
|
1529
|
+
onChatStart?: (event: ChatStartEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void;
|
|
1530
|
+
/**
|
|
1531
|
+
* Validate or transform incoming UI messages before they are converted to model
|
|
1532
|
+
* messages and accumulated. Fires once per turn with the raw `UIMessage[]` from
|
|
1533
|
+
* the wire payload (after cleanup of aborted tool parts).
|
|
1534
|
+
*
|
|
1535
|
+
* Return the validated messages array. Throw to abort the turn with an error.
|
|
1536
|
+
*
|
|
1537
|
+
* This is the right place to call the AI SDK's `validateUIMessages` to catch
|
|
1538
|
+
* malformed messages from storage or untrusted input before they reach the model.
|
|
1539
|
+
*
|
|
1540
|
+
* @example
|
|
1541
|
+
* ```ts
|
|
1542
|
+
* import { validateUIMessages } from "ai";
|
|
1543
|
+
*
|
|
1544
|
+
* chat.agent({
|
|
1545
|
+
* id: "my-chat",
|
|
1546
|
+
* onValidateMessages: async ({ messages }) => {
|
|
1547
|
+
* return validateUIMessages({ messages, tools: chatTools });
|
|
1548
|
+
* },
|
|
1549
|
+
* run: async ({ messages }) => {
|
|
1550
|
+
* return streamText({ model, messages, tools: chatTools });
|
|
1551
|
+
* },
|
|
1552
|
+
* });
|
|
1553
|
+
* ```
|
|
1554
|
+
*/
|
|
1555
|
+
onValidateMessages?: (event: ValidateMessagesEvent<TUIMessage>) => TUIMessage[] | Promise<TUIMessage[]>;
|
|
1556
|
+
/**
|
|
1557
|
+
* Load the full message history from your backend on every turn,
|
|
1558
|
+
* replacing the built-in linear accumulator.
|
|
1559
|
+
*
|
|
1560
|
+
* When set, the returned messages become the accumulated state for this turn.
|
|
1561
|
+
* The normal accumulation logic (append for submit, replace for regenerate)
|
|
1562
|
+
* is skipped entirely — the hook is the source of truth.
|
|
1563
|
+
*
|
|
1564
|
+
* After the hook returns, any incoming wire messages with matching IDs are
|
|
1565
|
+
* auto-merged (handles tool approval responses transparently).
|
|
1566
|
+
*
|
|
1567
|
+
* Use cases:
|
|
1568
|
+
* - Backend trust: prevent clients from injecting fabricated history
|
|
1569
|
+
* - Branching conversations (DAGs): load only the active branch
|
|
1570
|
+
* - Rollback/undo: exclude undone messages from history
|
|
1571
|
+
*
|
|
1572
|
+
* @example
|
|
1573
|
+
* ```ts
|
|
1574
|
+
* chat.agent({
|
|
1575
|
+
* id: "my-chat",
|
|
1576
|
+
* hydrateMessages: async ({ chatId, trigger, incomingMessages }) => {
|
|
1577
|
+
* // Persist the new message
|
|
1578
|
+
* const newMsg = incomingMessages[incomingMessages.length - 1];
|
|
1579
|
+
* if (newMsg && trigger === "submit-message") {
|
|
1580
|
+
* await db.chatMessages.create({ chatId, message: newMsg });
|
|
1581
|
+
* }
|
|
1582
|
+
* // Return the full authoritative history
|
|
1583
|
+
* return db.chatMessages.findMany({ where: { chatId } });
|
|
1584
|
+
* },
|
|
1585
|
+
* run: async ({ messages, signal }) => {
|
|
1586
|
+
* return streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
|
|
1587
|
+
* },
|
|
1588
|
+
* });
|
|
1589
|
+
* ```
|
|
1590
|
+
*/
|
|
1591
|
+
hydrateMessages?: (event: HydrateMessagesEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => TUIMessage[] | Promise<TUIMessage[]>;
|
|
1592
|
+
/**
|
|
1593
|
+
* Called at the start of every turn, after message accumulation and `onChatStart` (turn 0),
|
|
1594
|
+
* but before the `run` function executes.
|
|
1595
|
+
*
|
|
1596
|
+
* Use this to persist messages before streaming begins, so a mid-stream page refresh
|
|
1597
|
+
* still shows the user's message.
|
|
1598
|
+
*
|
|
1599
|
+
* @example
|
|
1600
|
+
* ```ts
|
|
1601
|
+
* onTurnStart: async ({ ctx, chatId, uiMessages }) => {
|
|
1602
|
+
* await db.chat.update({ where: { id: chatId }, data: { messages: uiMessages } });
|
|
1603
|
+
* }
|
|
1604
|
+
* ```
|
|
1605
|
+
*/
|
|
1606
|
+
onTurnStart?: (event: TurnStartEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void;
|
|
1607
|
+
/**
|
|
1608
|
+
* Called after the response is captured but before the stream closes.
|
|
1609
|
+
* The stream is still open, so you can write custom chunks to the frontend
|
|
1610
|
+
* (e.g. compaction progress). Use this for compaction, post-processing,
|
|
1611
|
+
* or any work where the user should see real-time status updates.
|
|
1612
|
+
*
|
|
1613
|
+
* @example
|
|
1614
|
+
* ```ts
|
|
1615
|
+
* onBeforeTurnComplete: async ({ ctx, writer, usage }) => {
|
|
1616
|
+
* if (usage?.inputTokens && usage.inputTokens > 5000) {
|
|
1617
|
+
* writer.write({ type: "data-compaction", id: generateId(), data: { status: "compacting" } });
|
|
1618
|
+
* // ... compact messages ...
|
|
1619
|
+
* chat.setMessages(compactedMessages);
|
|
1620
|
+
* writer.write({ type: "data-compaction", id: generateId(), data: { status: "complete" } });
|
|
1621
|
+
* }
|
|
1622
|
+
* }
|
|
1623
|
+
* ```
|
|
1624
|
+
*/
|
|
1625
|
+
onBeforeTurnComplete?: (event: BeforeTurnCompleteEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void;
|
|
1626
|
+
/**
|
|
1627
|
+
* Called when conversation compaction occurs (via `chat.compact()` or
|
|
1628
|
+
* `chat.compactionStep()`). Use for logging, billing, or persisting the summary.
|
|
1629
|
+
*
|
|
1630
|
+
* @example
|
|
1631
|
+
* ```ts
|
|
1632
|
+
* onCompacted: async ({ ctx, summary, totalTokens, chatId }) => {
|
|
1633
|
+
* logger.info("Compacted", { totalTokens, chatId });
|
|
1634
|
+
* await db.compactionLog.create({ data: { chatId, summary } });
|
|
1635
|
+
* }
|
|
1636
|
+
* ```
|
|
1637
|
+
*/
|
|
1638
|
+
onCompacted?: (event: CompactedEvent) => Promise<void> | void;
|
|
1639
|
+
/**
|
|
1640
|
+
* Automatic context compaction. When provided, compaction runs automatically
|
|
1641
|
+
* in both the inner loop (prepareStep, between tool-call steps) and the
|
|
1642
|
+
* outer loop (between turns, for single-step responses where prepareStep
|
|
1643
|
+
* never fires).
|
|
1644
|
+
*
|
|
1645
|
+
* The `shouldCompact` callback decides when to compact, and `summarize`
|
|
1646
|
+
* generates the summary. The prepareStep is auto-injected into
|
|
1647
|
+
* `chat.toStreamTextOptions()` — if you provide your own `prepareStep`
|
|
1648
|
+
* after spreading, it overrides the auto-injected one.
|
|
1649
|
+
*
|
|
1650
|
+
* @example
|
|
1651
|
+
* ```ts
|
|
1652
|
+
* chat.agent({
|
|
1653
|
+
* id: "my-chat",
|
|
1654
|
+
* compaction: {
|
|
1655
|
+
* shouldCompact: ({ totalTokens }) => (totalTokens ?? 0) > 80_000,
|
|
1656
|
+
* summarize: async (messages) =>
|
|
1657
|
+
* generateText({ model, messages: [...messages, { role: "user", content: "Summarize." }] })
|
|
1658
|
+
* .then((r) => r.text),
|
|
1659
|
+
* },
|
|
1660
|
+
* run: async ({ messages, signal }) => {
|
|
1661
|
+
* return streamText({ ...chat.toStreamTextOptions({ registry }), messages });
|
|
1662
|
+
* },
|
|
1663
|
+
* });
|
|
1664
|
+
* ```
|
|
1665
|
+
*/
|
|
1666
|
+
compaction?: ChatAgentCompactionOptions<TUIMessage>;
|
|
1667
|
+
/**
|
|
1668
|
+
* Configure how messages that arrive during streaming are handled.
|
|
1669
|
+
*
|
|
1670
|
+
* By default, messages queue for the next turn. When `shouldInject` is provided
|
|
1671
|
+
* and returns `true`, messages are injected between tool-call steps via
|
|
1672
|
+
* `prepareStep` — allowing users to steer the agent mid-execution.
|
|
1673
|
+
*
|
|
1674
|
+
* @example
|
|
1675
|
+
* ```ts
|
|
1676
|
+
* pendingMessages: {
|
|
1677
|
+
* shouldInject: ({ steps }) => steps.length > 0,
|
|
1678
|
+
* onReceived: ({ message }) => logger.info("Steering message received"),
|
|
1679
|
+
* },
|
|
1680
|
+
* ```
|
|
1681
|
+
*/
|
|
1682
|
+
pendingMessages?: PendingMessagesOptions<TUIMessage>;
|
|
1683
|
+
/**
|
|
1684
|
+
* Called after each assistant response completes. Use to persist the
|
|
1685
|
+
* conversation to your database after each assistant response.
|
|
1686
|
+
*
|
|
1687
|
+
* @example
|
|
1688
|
+
* ```ts
|
|
1689
|
+
* onTurnComplete: async ({ ctx, chatId, messages }) => {
|
|
1690
|
+
* await db.chat.update({ where: { id: chatId }, data: { messages } });
|
|
1691
|
+
* }
|
|
1692
|
+
* ```
|
|
1693
|
+
*/
|
|
1694
|
+
onTurnComplete?: (event: TurnCompleteEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void;
|
|
1695
|
+
/**
|
|
1696
|
+
* Maximum number of conversational turns (message round-trips) a single run
|
|
1697
|
+
* will handle before ending. After this many turns the run completes
|
|
1698
|
+
* normally and the next message will start a fresh run.
|
|
1699
|
+
*
|
|
1700
|
+
* @default 100
|
|
1701
|
+
*/
|
|
1702
|
+
maxTurns?: number;
|
|
1703
|
+
/**
|
|
1704
|
+
* How long to wait for the next message before timing out and ending the run.
|
|
1705
|
+
* Accepts any duration string (e.g. `"1h"`, `"30m"`).
|
|
1706
|
+
*
|
|
1707
|
+
* @default "1h"
|
|
1708
|
+
*/
|
|
1709
|
+
turnTimeout?: string;
|
|
1710
|
+
/**
|
|
1711
|
+
* How long (in seconds) the run stays idle (active, using compute) after each
|
|
1712
|
+
* turn, waiting for the next message. During this window responses are instant.
|
|
1713
|
+
* After this timeout the run suspends (frees compute) and waits via
|
|
1714
|
+
* `inputStream.wait()`.
|
|
1715
|
+
*
|
|
1716
|
+
* Set to `0` to suspend immediately after each turn.
|
|
1717
|
+
*
|
|
1718
|
+
* @default 30
|
|
1719
|
+
*/
|
|
1720
|
+
idleTimeoutInSeconds?: number;
|
|
1721
|
+
/**
|
|
1722
|
+
* How long the `chatAccessToken` (scoped to this run) remains valid.
|
|
1723
|
+
* A fresh token is minted after each turn, so this only needs to cover
|
|
1724
|
+
* the gap between turns.
|
|
1725
|
+
*
|
|
1726
|
+
* Accepts a duration string (e.g. `"1h"`, `"30m"`, `"2h"`).
|
|
1727
|
+
*
|
|
1728
|
+
* @default "1h"
|
|
1729
|
+
*/
|
|
1730
|
+
chatAccessTokenTTL?: string;
|
|
1731
|
+
/**
|
|
1732
|
+
* How long (in seconds) the run stays idle after `onPreload` fires,
|
|
1733
|
+
* waiting for the first message before suspending.
|
|
1734
|
+
*
|
|
1735
|
+
* Only applies to preloaded runs (triggered via `transport.preload()`).
|
|
1736
|
+
* Takes precedence over `transport.preload(..., { idleTimeoutInSeconds })`
|
|
1737
|
+
* and over {@link ChatAgentOptions.idleTimeoutInSeconds}.
|
|
1738
|
+
*
|
|
1739
|
+
* @default Same as `idleTimeoutInSeconds`
|
|
1740
|
+
*/
|
|
1741
|
+
preloadIdleTimeoutInSeconds?: number;
|
|
1742
|
+
/**
|
|
1743
|
+
* How long to wait (suspended) for the first message after a preloaded run starts.
|
|
1744
|
+
* If no message arrives within this time, the run ends.
|
|
1745
|
+
*
|
|
1746
|
+
* Only applies to preloaded runs.
|
|
1747
|
+
*
|
|
1748
|
+
* @default Same as `turnTimeout`
|
|
1749
|
+
*/
|
|
1750
|
+
preloadTimeout?: string;
|
|
1751
|
+
/**
|
|
1752
|
+
* Transform model messages before they're used anywhere — in `run()`,
|
|
1753
|
+
* in compaction rebuilds, and in compaction results.
|
|
1754
|
+
*
|
|
1755
|
+
* Define once, applied everywhere. Use for Anthropic cache breaks,
|
|
1756
|
+
* injecting system context, stripping PII, etc.
|
|
1757
|
+
*
|
|
1758
|
+
* @example
|
|
1759
|
+
* ```ts
|
|
1760
|
+
* prepareMessages: async ({ messages, reason }) => {
|
|
1761
|
+
* // Add Anthropic cache breaks to the last message
|
|
1762
|
+
* if (messages.length === 0) return messages;
|
|
1763
|
+
* const last = messages[messages.length - 1];
|
|
1764
|
+
* return [...messages.slice(0, -1), {
|
|
1765
|
+
* ...last,
|
|
1766
|
+
* providerOptions: { ...last.providerOptions, anthropic: { cacheControl: { type: "ephemeral" } } },
|
|
1767
|
+
* }];
|
|
1768
|
+
* }
|
|
1769
|
+
* ```
|
|
1770
|
+
*/
|
|
1771
|
+
prepareMessages?: (event: PrepareMessagesEvent<inferSchemaOut<TClientDataSchema>>) => ModelMessage[] | Promise<ModelMessage[]>;
|
|
1772
|
+
/**
|
|
1773
|
+
* Default options for `toUIMessageStream()` when auto-piping or using
|
|
1774
|
+
* `turn.complete()` / `chat.pipeAndCapture()`.
|
|
1775
|
+
*
|
|
1776
|
+
* Controls how the `StreamTextResult` is converted to a `UIMessageChunk`
|
|
1777
|
+
* stream — error handling, reasoning/source visibility, metadata, etc.
|
|
1778
|
+
*
|
|
1779
|
+
* Can be overridden per-turn by calling `chat.setUIMessageStreamOptions()`
|
|
1780
|
+
* inside `run()` or lifecycle hooks. Per-turn values are merged on top
|
|
1781
|
+
* of these defaults (per-turn wins on conflicts).
|
|
1782
|
+
*
|
|
1783
|
+
* `onFinish` and `originalMessages` are managed internally and cannot be
|
|
1784
|
+
* overridden here. Use `streamText`'s `onFinish` for custom finish
|
|
1785
|
+
* handling. `generateMessageId` can be set to control response message
|
|
1786
|
+
* ID generation (e.g. UUID-v7).
|
|
1787
|
+
*
|
|
1788
|
+
* @example
|
|
1789
|
+
* ```ts
|
|
1790
|
+
* chat.agent({
|
|
1791
|
+
* id: "my-chat",
|
|
1792
|
+
* uiMessageStreamOptions: {
|
|
1793
|
+
* sendReasoning: true,
|
|
1794
|
+
* onError: (error) => error instanceof Error ? error.message : "An error occurred.",
|
|
1795
|
+
* },
|
|
1796
|
+
* run: async ({ messages, signal }) => { ... },
|
|
1797
|
+
* });
|
|
1798
|
+
* ```
|
|
1799
|
+
*/
|
|
1800
|
+
uiMessageStreamOptions?: ChatUIMessageStreamOptions<TUIMessage>;
|
|
1801
|
+
/**
|
|
1802
|
+
* Called right before the run suspends to wait for a message.
|
|
1803
|
+
*
|
|
1804
|
+
* The `phase` discriminator tells you when the suspend happened:
|
|
1805
|
+
* - `"preload"`: after `onPreload`, waiting for the first message
|
|
1806
|
+
* - `"turn"`: after `onTurnComplete`, waiting for the next message
|
|
1807
|
+
*
|
|
1808
|
+
* Use this for cleanup before suspension (e.g. disposing sandboxes, closing connections).
|
|
1809
|
+
*
|
|
1810
|
+
* @example
|
|
1811
|
+
* ```ts
|
|
1812
|
+
* onChatSuspend: async (event) => {
|
|
1813
|
+
* await disposeExpensiveResources(event.ctx.run.id);
|
|
1814
|
+
* if (event.phase === "turn") {
|
|
1815
|
+
* logger.info("Suspending after turn", { turn: event.turn });
|
|
1816
|
+
* }
|
|
1817
|
+
* }
|
|
1818
|
+
* ```
|
|
1819
|
+
*/
|
|
1820
|
+
onChatSuspend?: (event: ChatSuspendEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void;
|
|
1821
|
+
/**
|
|
1822
|
+
* Called right after the run resumes from suspension with a new message.
|
|
1823
|
+
*
|
|
1824
|
+
* The `phase` discriminator tells you when the resume happened:
|
|
1825
|
+
* - `"preload"`: first message arrived after preload suspension
|
|
1826
|
+
* - `"turn"`: next message arrived after turn suspension
|
|
1827
|
+
*
|
|
1828
|
+
* Use this for re-initialization after wake (e.g. warming caches, reconnecting).
|
|
1829
|
+
*
|
|
1830
|
+
* @example
|
|
1831
|
+
* ```ts
|
|
1832
|
+
* onChatResume: async (event) => {
|
|
1833
|
+
* warmCache(event.ctx.run.id);
|
|
1834
|
+
* if (event.phase === "turn") {
|
|
1835
|
+
* logger.info("Resumed after turn", { turn: event.turn });
|
|
1836
|
+
* }
|
|
1837
|
+
* }
|
|
1838
|
+
* ```
|
|
1839
|
+
*/
|
|
1840
|
+
onChatResume?: (event: ChatResumeEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void;
|
|
1841
|
+
/**
|
|
1842
|
+
* When `true`, the run exits successfully after the preload idle timeout
|
|
1843
|
+
* instead of suspending and waiting. The run completes with no turn executed.
|
|
1844
|
+
*
|
|
1845
|
+
* Use this for "fire and forget" preloads where you only want to do eager
|
|
1846
|
+
* initialization. If the user doesn't send a message during the idle window,
|
|
1847
|
+
* the run ends cleanly.
|
|
1848
|
+
*
|
|
1849
|
+
* Only applies to preloaded runs (triggered via `transport.preload()`).
|
|
1850
|
+
*
|
|
1851
|
+
* @default false
|
|
1852
|
+
*/
|
|
1853
|
+
exitAfterPreloadIdle?: boolean;
|
|
1854
|
+
};
|
|
1855
|
+
/**
|
|
1856
|
+
* Creates a Trigger.dev task pre-configured for AI SDK chat.
|
|
1857
|
+
*
|
|
1858
|
+
* - **Pre-types the payload** as `ChatTaskRunPayload` — includes abort signals
|
|
1859
|
+
* - **Auto-pipes the stream** if `run` returns a `StreamTextResult`
|
|
1860
|
+
* - **Multi-turn**: keeps the conversation in a single run using input streams
|
|
1861
|
+
* - **Stop support**: frontend can stop generation mid-stream via the stop input stream
|
|
1862
|
+
* - For complex flows, use `pipeChat()` from anywhere inside your task code
|
|
1863
|
+
*
|
|
1864
|
+
* @example
|
|
1865
|
+
* ```ts
|
|
1866
|
+
* import { chat } from "@trigger.dev/sdk/ai";
|
|
1867
|
+
* import { streamText, convertToModelMessages } from "ai";
|
|
1868
|
+
* import { openai } from "@ai-sdk/openai";
|
|
1869
|
+
*
|
|
1870
|
+
* export const myChat = chat.agent({
|
|
1871
|
+
* id: "my-chat",
|
|
1872
|
+
* run: async ({ messages, signal }) => {
|
|
1873
|
+
* return streamText({
|
|
1874
|
+
* model: openai("gpt-4o"),
|
|
1875
|
+
* messages, // already converted via convertToModelMessages
|
|
1876
|
+
* abortSignal: signal,
|
|
1877
|
+
* });
|
|
1878
|
+
* },
|
|
1879
|
+
* });
|
|
1880
|
+
* ```
|
|
1881
|
+
*/
|
|
1882
|
+
type ChatCustomAgentOptions<TIdentifier extends string, TClientDataSchema extends TaskSchema | undefined = undefined, TUIMessage extends UIMessage = UIMessage> = Omit<TaskOptions<TIdentifier, ChatTaskWirePayload<TUIMessage, inferSchemaIn<TClientDataSchema>>, unknown>, "triggerSource" | "agentConfig"> & {
|
|
1883
|
+
clientDataSchema?: TClientDataSchema;
|
|
1884
|
+
};
|
|
1885
|
+
declare function chatCustomAgent<TIdentifier extends string, TClientDataSchema extends TaskSchema | undefined = undefined, TUIMessage extends UIMessage = UIMessage>(options: ChatCustomAgentOptions<TIdentifier, TClientDataSchema, TUIMessage>): Task<TIdentifier, ChatTaskWirePayload<TUIMessage, inferSchemaIn<TClientDataSchema>>, unknown>;
|
|
1886
|
+
declare function chatAgent<TIdentifier extends string, TClientDataSchema extends TaskSchema | undefined = undefined, TUIMessage extends UIMessage = UIMessage, TActionSchema extends TaskSchema | undefined = undefined>(options: ChatAgentOptions<TIdentifier, TClientDataSchema, TUIMessage, TActionSchema>): Task<TIdentifier, ChatTaskWirePayload<TUIMessage, inferSchemaIn<TClientDataSchema>>, unknown>;
|
|
1887
|
+
/**
|
|
1888
|
+
* Optional config for {@link chat.withUIMessage}. `streamOptions` become default
|
|
1889
|
+
* static `toUIMessageStream()` settings; inner `chat.agent({ uiMessageStreamOptions })`
|
|
1890
|
+
* shallow-merges on top (task wins on conflicts).
|
|
1891
|
+
*/
|
|
1892
|
+
export type ChatWithUIMessageConfig<TUIM extends UIMessage = UIMessage> = {
|
|
1893
|
+
streamOptions?: ChatUIMessageStreamOptions<TUIM>;
|
|
1894
|
+
};
|
|
1895
|
+
/**
|
|
1896
|
+
* A chainable builder for configuring chat tasks with fixed UI message types,
|
|
1897
|
+
* client data schemas, and builder-level hooks that compose with task-level hooks.
|
|
1898
|
+
*
|
|
1899
|
+
* Obtain a builder via {@link chat.withUIMessage} or {@link chat.withClientData}.
|
|
1900
|
+
*
|
|
1901
|
+
* @example
|
|
1902
|
+
* ```ts
|
|
1903
|
+
* export const myChat = chat
|
|
1904
|
+
* .withUIMessage<AgentUiMessage>({ streamOptions: { sendReasoning: true } })
|
|
1905
|
+
* .withClientData({ schema: z.object({ userId: z.string() }) })
|
|
1906
|
+
* .onChatSuspend(async ({ ctx }) => { await disposeResources(ctx.run.id) })
|
|
1907
|
+
* .task({
|
|
1908
|
+
* id: "my-chat",
|
|
1909
|
+
* run: async ({ messages, signal }) => streamText({ model, messages, abortSignal: signal }),
|
|
1910
|
+
* });
|
|
1911
|
+
* ```
|
|
1912
|
+
*/
|
|
1913
|
+
export interface ChatBuilder<TUIMessage extends UIMessage = UIMessage, TClientDataSchema extends TaskSchema | undefined = undefined> {
|
|
1914
|
+
/** Fix the UI message type. Returns a new builder preserving all accumulated state. */
|
|
1915
|
+
withUIMessage<TUIM extends UIMessage = UIMessage>(config?: ChatWithUIMessageConfig<TUIM>): ChatBuilder<TUIM, TClientDataSchema>;
|
|
1916
|
+
/** Fix the client data schema. Returns a new builder preserving all accumulated state. */
|
|
1917
|
+
withClientData<TSchema extends TaskSchema>(config: {
|
|
1918
|
+
schema: TSchema;
|
|
1919
|
+
}): ChatBuilder<TUIMessage, TSchema>;
|
|
1920
|
+
/** Register a builder-level `onBoot` hook. Runs before the task-level hook if both are set. */
|
|
1921
|
+
onBoot(fn: (event: BootEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void): ChatBuilder<TUIMessage, TClientDataSchema>;
|
|
1922
|
+
/** Register a builder-level `onPreload` hook. Runs before the task-level hook if both are set. */
|
|
1923
|
+
onPreload(fn: (event: PreloadEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void): ChatBuilder<TUIMessage, TClientDataSchema>;
|
|
1924
|
+
/** Register a builder-level `onChatStart` hook. Runs before the task-level hook if both are set. */
|
|
1925
|
+
onChatStart(fn: (event: ChatStartEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void): ChatBuilder<TUIMessage, TClientDataSchema>;
|
|
1926
|
+
/** Register a builder-level `onTurnStart` hook. Runs before the task-level hook if both are set. */
|
|
1927
|
+
onTurnStart(fn: (event: TurnStartEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void): ChatBuilder<TUIMessage, TClientDataSchema>;
|
|
1928
|
+
/** Register a builder-level `onBeforeTurnComplete` hook. Runs before the task-level hook if both are set. */
|
|
1929
|
+
onBeforeTurnComplete(fn: (event: BeforeTurnCompleteEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void): ChatBuilder<TUIMessage, TClientDataSchema>;
|
|
1930
|
+
/** Register a builder-level `onTurnComplete` hook. Runs before the task-level hook if both are set. */
|
|
1931
|
+
onTurnComplete(fn: (event: TurnCompleteEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void): ChatBuilder<TUIMessage, TClientDataSchema>;
|
|
1932
|
+
/** Register a builder-level `onCompacted` hook. Runs before the task-level hook if both are set. */
|
|
1933
|
+
onCompacted(fn: (event: CompactedEvent) => Promise<void> | void): ChatBuilder<TUIMessage, TClientDataSchema>;
|
|
1934
|
+
/** Register a builder-level `onChatSuspend` hook. Runs before the task-level hook if both are set. */
|
|
1935
|
+
onChatSuspend(fn: (event: ChatSuspendEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void): ChatBuilder<TUIMessage, TClientDataSchema>;
|
|
1936
|
+
/** Register a builder-level `onChatResume` hook. Runs before the task-level hook if both are set. */
|
|
1937
|
+
onChatResume(fn: (event: ChatResumeEvent<inferSchemaOut<TClientDataSchema>, TUIMessage>) => Promise<void> | void): ChatBuilder<TUIMessage, TClientDataSchema>;
|
|
1938
|
+
/**
|
|
1939
|
+
* Create the chat agent with the accumulated builder configuration.
|
|
1940
|
+
*
|
|
1941
|
+
* When `withClientData` was called, `clientDataSchema` is injected automatically
|
|
1942
|
+
* and omitted from options. Otherwise, it can still be set directly in options
|
|
1943
|
+
* (backwards compatible).
|
|
1944
|
+
*/
|
|
1945
|
+
agent: [TClientDataSchema] extends [undefined] ? <TId extends string, TInfer extends TaskSchema | undefined = undefined, TAction extends TaskSchema | undefined = undefined>(options: ChatAgentOptions<TId, TInfer, TUIMessage, TAction>) => Task<TId, ChatTaskWirePayload<TUIMessage, inferSchemaIn<TInfer>>, unknown> : <TId extends string, TAction extends TaskSchema | undefined = undefined>(options: Omit<ChatAgentOptions<TId, TClientDataSchema, TUIMessage, TAction>, "clientDataSchema">) => Task<TId, ChatTaskWirePayload<TUIMessage, inferSchemaIn<TClientDataSchema>>, unknown>;
|
|
1946
|
+
/**
|
|
1947
|
+
* Create a custom agent with manual lifecycle control.
|
|
1948
|
+
*
|
|
1949
|
+
* The agent appears in the playground but you manage the turn loop,
|
|
1950
|
+
* message waiting, and streaming yourself using composable primitives
|
|
1951
|
+
* (`chat.messages`, `chat.MessageAccumulator`, `chat.pipeAndCapture`, etc.).
|
|
1952
|
+
*
|
|
1953
|
+
* Builder hooks (`onPreload`, `onChatStart`, etc.) are not applied —
|
|
1954
|
+
* those are managed-lifecycle concepts handled by `.agent()`.
|
|
1955
|
+
*/
|
|
1956
|
+
customAgent: [TClientDataSchema] extends [undefined] ? <TId extends string>(options: ChatCustomAgentOptions<TId, undefined, TUIMessage>) => Task<TId, ChatTaskWirePayload<TUIMessage, undefined>, unknown> : <TId extends string>(options: ChatCustomAgentOptions<TId, TClientDataSchema, TUIMessage>) => Task<TId, ChatTaskWirePayload<TUIMessage, inferSchemaIn<TClientDataSchema>>, unknown>;
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* Fix the UI message type for a chat task (AI SDK `UIMessage` generics) while
|
|
1960
|
+
* keeping `id` and `clientDataSchema` inference on the inner {@link chat.agent} call.
|
|
1961
|
+
*
|
|
1962
|
+
* Returns a {@link ChatBuilder} that supports chaining `.withClientData()`,
|
|
1963
|
+
* hook methods (`.onPreload()`, `.onChatSuspend()`, etc.), and `.task()`.
|
|
1964
|
+
*
|
|
1965
|
+
* @example
|
|
1966
|
+
* ```ts
|
|
1967
|
+
* type AgentUiMessage = UIMessage<unknown, UIDataTypes, UITools>;
|
|
1968
|
+
*
|
|
1969
|
+
* export const myChat = chat.withUIMessage<AgentUiMessage>({
|
|
1970
|
+
* streamOptions: { sendReasoning: true },
|
|
1971
|
+
* }).task({
|
|
1972
|
+
* id: "my-chat",
|
|
1973
|
+
* run: async ({ messages, signal }) => { ... },
|
|
1974
|
+
* });
|
|
1975
|
+
* ```
|
|
1976
|
+
*/
|
|
1977
|
+
declare function withUIMessage<TUIM extends UIMessage = UIMessage>(config?: ChatWithUIMessageConfig<TUIM>): ChatBuilder<TUIM, undefined>;
|
|
1978
|
+
/**
|
|
1979
|
+
* Fix the client data schema for a chat task, providing typed `clientData`
|
|
1980
|
+
* in all hooks and the `run` function.
|
|
1981
|
+
*
|
|
1982
|
+
* Returns a {@link ChatBuilder} that supports chaining `.withUIMessage()`,
|
|
1983
|
+
* hook methods (`.onPreload()`, `.onChatSuspend()`, etc.), and `.task()`.
|
|
1984
|
+
*
|
|
1985
|
+
* @example
|
|
1986
|
+
* ```ts
|
|
1987
|
+
* export const myChat = chat
|
|
1988
|
+
* .withClientData({ schema: z.object({ userId: z.string() }) })
|
|
1989
|
+
* .task({
|
|
1990
|
+
* id: "my-chat",
|
|
1991
|
+
* onPreload: async ({ clientData }) => {
|
|
1992
|
+
* // clientData is typed as { userId: string }
|
|
1993
|
+
* },
|
|
1994
|
+
* run: async ({ messages, signal }) => { ... },
|
|
1995
|
+
* });
|
|
1996
|
+
* ```
|
|
1997
|
+
*/
|
|
1998
|
+
declare function withClientData<TSchema extends TaskSchema>(config: {
|
|
1999
|
+
schema: TSchema;
|
|
2000
|
+
}): ChatBuilder<UIMessage, TSchema>;
|
|
2001
|
+
/**
|
|
2002
|
+
* Override the turn timeout for subsequent turns in the current run.
|
|
2003
|
+
*
|
|
2004
|
+
* The turn timeout controls how long the run stays suspended (freeing compute)
|
|
2005
|
+
* waiting for the next user message. When it expires, the run completes
|
|
2006
|
+
* gracefully and the next message starts a fresh run.
|
|
2007
|
+
*
|
|
2008
|
+
* Call from inside a `chatAgent` run function to adjust based on context.
|
|
2009
|
+
*
|
|
2010
|
+
* @param duration - A duration string (e.g. `"5m"`, `"1h"`, `"30s"`)
|
|
2011
|
+
*
|
|
2012
|
+
* @example
|
|
2013
|
+
* ```ts
|
|
2014
|
+
* run: async ({ messages, signal }) => {
|
|
2015
|
+
* chat.setTurnTimeout("2h");
|
|
2016
|
+
* return streamText({ model, messages, abortSignal: signal });
|
|
2017
|
+
* }
|
|
2018
|
+
* ```
|
|
2019
|
+
*/
|
|
2020
|
+
declare function setTurnTimeout(duration: string): void;
|
|
2021
|
+
/**
|
|
2022
|
+
* Override the turn timeout in seconds for subsequent turns in the current run.
|
|
2023
|
+
*
|
|
2024
|
+
* @param seconds - Number of seconds to wait for the next message before ending the run
|
|
2025
|
+
*
|
|
2026
|
+
* @example
|
|
2027
|
+
* ```ts
|
|
2028
|
+
* run: async ({ messages, signal }) => {
|
|
2029
|
+
* chat.setTurnTimeoutInSeconds(3600); // 1 hour
|
|
2030
|
+
* return streamText({ model, messages, abortSignal: signal });
|
|
2031
|
+
* }
|
|
2032
|
+
* ```
|
|
2033
|
+
*/
|
|
2034
|
+
declare function setTurnTimeoutInSeconds(seconds: number): void;
|
|
2035
|
+
/**
|
|
2036
|
+
* Override the idle timeout for subsequent turns in the current run.
|
|
2037
|
+
*
|
|
2038
|
+
* The idle timeout controls how long the run stays active (using compute)
|
|
2039
|
+
* after each turn, waiting for the next message. During this window,
|
|
2040
|
+
* responses are instant. After it expires, the run suspends.
|
|
2041
|
+
*
|
|
2042
|
+
* @param seconds - Number of seconds to stay idle (0 to suspend immediately)
|
|
2043
|
+
*
|
|
2044
|
+
* @example
|
|
2045
|
+
* ```ts
|
|
2046
|
+
* run: async ({ messages, signal }) => {
|
|
2047
|
+
* chat.setIdleTimeoutInSeconds(60);
|
|
2048
|
+
* return streamText({ model, messages, abortSignal: signal });
|
|
2049
|
+
* }
|
|
2050
|
+
* ```
|
|
2051
|
+
*/
|
|
2052
|
+
declare function setIdleTimeoutInSeconds(seconds: number): void;
|
|
2053
|
+
/**
|
|
2054
|
+
* Override the `toUIMessageStream()` options for the current turn.
|
|
2055
|
+
*
|
|
2056
|
+
* These options control how the `StreamTextResult` is converted to a
|
|
2057
|
+
* `UIMessageChunk` stream — error handling, reasoning/source visibility,
|
|
2058
|
+
* message metadata, etc.
|
|
2059
|
+
*
|
|
2060
|
+
* Per-turn options are merged on top of the static `uiMessageStreamOptions`
|
|
2061
|
+
* set on `chat.agent()`. Per-turn values win on conflicts.
|
|
2062
|
+
*
|
|
2063
|
+
* @example
|
|
2064
|
+
* ```ts
|
|
2065
|
+
* run: async ({ messages, signal }) => {
|
|
2066
|
+
* chat.setUIMessageStreamOptions({
|
|
2067
|
+
* sendReasoning: true,
|
|
2068
|
+
* onError: (error) => error instanceof Error ? error.message : "An error occurred.",
|
|
2069
|
+
* });
|
|
2070
|
+
* return streamText({ model, messages, abortSignal: signal });
|
|
2071
|
+
* }
|
|
2072
|
+
* ```
|
|
2073
|
+
*/
|
|
2074
|
+
declare function setUIMessageStreamOptions(options: ChatUIMessageStreamOptions<UIMessage>): void;
|
|
2075
|
+
/**
|
|
2076
|
+
* Check whether the user stopped generation during the current turn.
|
|
2077
|
+
*
|
|
2078
|
+
* Works from **anywhere** inside a `chat.agent` run — including inside
|
|
2079
|
+
* `streamText`'s `onFinish` callback — without needing to thread the
|
|
2080
|
+
* `stopSignal` through closures.
|
|
2081
|
+
*
|
|
2082
|
+
* This is especially useful when the AI SDK's `isAborted` flag is unreliable
|
|
2083
|
+
* (e.g. when using `createUIMessageStream` + `writer.merge()`).
|
|
2084
|
+
*
|
|
2085
|
+
* @example
|
|
2086
|
+
* ```ts
|
|
2087
|
+
* onFinish: ({ isAborted }) => {
|
|
2088
|
+
* const wasStopped = isAborted || chat.isStopped();
|
|
2089
|
+
* if (wasStopped) {
|
|
2090
|
+
* // handle stop
|
|
2091
|
+
* }
|
|
2092
|
+
* }
|
|
2093
|
+
* ```
|
|
2094
|
+
*/
|
|
2095
|
+
declare function isStopped(): boolean;
|
|
2096
|
+
/**
|
|
2097
|
+
* Request that the current run exits so the next message starts on the latest
|
|
2098
|
+
* deployed version (via the standard continuation mechanism).
|
|
2099
|
+
*
|
|
2100
|
+
* When called from `onTurnStart` or `onValidateMessages`, `run()` is skipped
|
|
2101
|
+
* entirely — the run exits immediately and the transport re-triggers the
|
|
2102
|
+
* same message on the new version.
|
|
2103
|
+
*
|
|
2104
|
+
* When called from `run()` or `chat.defer()`, the current turn completes
|
|
2105
|
+
* normally and the run exits afterward instead of waiting for the next message.
|
|
2106
|
+
*
|
|
2107
|
+
* Call from `onTurnStart`, `onValidateMessages`, `onChatResume`, `run()`,
|
|
2108
|
+
* or inside `chat.defer()`.
|
|
2109
|
+
*
|
|
2110
|
+
* @example
|
|
2111
|
+
* ```ts
|
|
2112
|
+
* const SUPPORTED_VERSIONS = new Set(["v2", "v3"]);
|
|
2113
|
+
*
|
|
2114
|
+
* chat.agent({
|
|
2115
|
+
* id: "my-chat",
|
|
2116
|
+
* onTurnStart: async ({ clientData }) => {
|
|
2117
|
+
* if (clientData?.protocolVersion && !SUPPORTED_VERSIONS.has(clientData.protocolVersion)) {
|
|
2118
|
+
* chat.requestUpgrade();
|
|
2119
|
+
* }
|
|
2120
|
+
* },
|
|
2121
|
+
* run: async ({ messages }) => { ... },
|
|
2122
|
+
* });
|
|
2123
|
+
* ```
|
|
2124
|
+
*/
|
|
2125
|
+
declare function requestUpgrade(): void;
|
|
2126
|
+
/**
|
|
2127
|
+
* Exit the run after the current turn completes, without waiting for the
|
|
2128
|
+
* next message. Unlike {@link requestUpgrade}, no upgrade-required signal
|
|
2129
|
+
* is sent to the client — the turn finishes normally, `onTurnComplete`
|
|
2130
|
+
* fires, and the loop exits instead of going idle.
|
|
2131
|
+
*
|
|
2132
|
+
* Call from `run()`, `chat.defer()`, `onBeforeTurnComplete`, or
|
|
2133
|
+
* `onTurnComplete` to end the run on your own terms (budget exhausted,
|
|
2134
|
+
* task complete, goal achieved, etc.).
|
|
2135
|
+
*
|
|
2136
|
+
* The next user message on the same `chatId` starts a fresh run via the
|
|
2137
|
+
* normal continuation mechanism.
|
|
2138
|
+
*
|
|
2139
|
+
* @example
|
|
2140
|
+
* ```ts
|
|
2141
|
+
* chat.agent({
|
|
2142
|
+
* id: "one-shot-agent",
|
|
2143
|
+
* run: async ({ messages, signal }) => {
|
|
2144
|
+
* const result = streamText({ model: openai("gpt-4o"), messages, abortSignal: signal });
|
|
2145
|
+
* // Single-response agent — exit after this turn.
|
|
2146
|
+
* chat.endRun();
|
|
2147
|
+
* return result;
|
|
2148
|
+
* },
|
|
2149
|
+
* });
|
|
2150
|
+
* ```
|
|
2151
|
+
*/
|
|
2152
|
+
declare function endRun(): void;
|
|
2153
|
+
/**
|
|
2154
|
+
* Register a promise that runs in the background during the current turn.
|
|
2155
|
+
*
|
|
2156
|
+
* Use this to move non-blocking work (DB writes, analytics, etc.) out of
|
|
2157
|
+
* the critical path. The promise runs in parallel with streaming and is
|
|
2158
|
+
* awaited (with a 5 s timeout) before `onTurnComplete` fires.
|
|
2159
|
+
*
|
|
2160
|
+
* @example
|
|
2161
|
+
* ```ts
|
|
2162
|
+
* onTurnStart: async ({ chatId, uiMessages }) => {
|
|
2163
|
+
* // Pass a promise directly
|
|
2164
|
+
* chat.defer(db.chat.update({ where: { id: chatId }, data: { messages: uiMessages } }));
|
|
2165
|
+
*
|
|
2166
|
+
* // Or pass an async function — cleaner for multi-step work
|
|
2167
|
+
* chat.defer(async () => {
|
|
2168
|
+
* const flags = await getFeatureFlags();
|
|
2169
|
+
* if (flags.forceUpgrade) chat.requestUpgrade();
|
|
2170
|
+
* });
|
|
2171
|
+
* },
|
|
2172
|
+
* ```
|
|
2173
|
+
*/
|
|
2174
|
+
declare function chatDefer(promiseOrFn: Promise<unknown> | (() => Promise<unknown>)): void;
|
|
2175
|
+
/**
|
|
2176
|
+
* Queue model messages for injection at the next `prepareStep` boundary.
|
|
2177
|
+
*
|
|
2178
|
+
* Use this to inject context from background work into the agent's conversation.
|
|
2179
|
+
* Messages are appended to the model messages before the next LLM inference call.
|
|
2180
|
+
*
|
|
2181
|
+
* Combine with `chat.defer()` to run background analysis and inject results:
|
|
2182
|
+
*
|
|
2183
|
+
* @example
|
|
2184
|
+
* ```ts
|
|
2185
|
+
* onTurnComplete: async ({ messages }) => {
|
|
2186
|
+
* chat.defer((async () => {
|
|
2187
|
+
* const review = await generateObject({
|
|
2188
|
+
* model: openai("gpt-4o-mini"),
|
|
2189
|
+
* messages: [...messages, { role: "user", content: "Review the last response." }],
|
|
2190
|
+
* schema: z.object({ suggestions: z.array(z.string()) }),
|
|
2191
|
+
* });
|
|
2192
|
+
* if (review.object.suggestions.length > 0) {
|
|
2193
|
+
* chat.inject([{
|
|
2194
|
+
* role: "system",
|
|
2195
|
+
* content: `Improvements for next response:\n${review.object.suggestions.join("\n")}`,
|
|
2196
|
+
* }]);
|
|
2197
|
+
* }
|
|
2198
|
+
* })());
|
|
2199
|
+
* },
|
|
2200
|
+
* ```
|
|
2201
|
+
*/
|
|
2202
|
+
declare function injectBackgroundContext(messages: ModelMessage[]): void;
|
|
2203
|
+
/**
|
|
2204
|
+
* Clean up a UIMessage that was captured during an aborted/stopped turn.
|
|
2205
|
+
*
|
|
2206
|
+
* When generation is stopped mid-stream, the captured message may contain:
|
|
2207
|
+
* - Tool parts stuck in incomplete states (`partial-call`, `input-available`,
|
|
2208
|
+
* `input-streaming`) that cause permanent UI spinners
|
|
2209
|
+
* - Reasoning parts with `state: "streaming"` instead of `"done"`
|
|
2210
|
+
* - Text parts with `state: "streaming"` instead of `"done"`
|
|
2211
|
+
*
|
|
2212
|
+
* This function returns a cleaned copy with:
|
|
2213
|
+
* - Incomplete tool parts removed entirely
|
|
2214
|
+
* - Reasoning and text parts marked as `"done"`
|
|
2215
|
+
*
|
|
2216
|
+
* `chat.agent` calls this automatically when stop is detected before passing
|
|
2217
|
+
* the response to `onTurnComplete`. Use this manually when calling `pipeChat`
|
|
2218
|
+
* directly and capturing response messages yourself.
|
|
2219
|
+
*
|
|
2220
|
+
* @example
|
|
2221
|
+
* ```ts
|
|
2222
|
+
* onTurnComplete: async ({ responseMessage, stopped }) => {
|
|
2223
|
+
* // Already cleaned automatically by chat.agent — but if you captured
|
|
2224
|
+
* // your own message via pipeChat, clean it manually:
|
|
2225
|
+
* const cleaned = chat.cleanupAbortedParts(myMessage);
|
|
2226
|
+
* await db.messages.save(cleaned);
|
|
2227
|
+
* }
|
|
2228
|
+
* ```
|
|
2229
|
+
*/
|
|
2230
|
+
declare function cleanupAbortedParts<TUIM extends UIMessage>(message: TUIM): TUIM;
|
|
2231
|
+
/**
|
|
2232
|
+
* Create a managed stop signal wired to the chat stop input stream.
|
|
2233
|
+
*
|
|
2234
|
+
* Call once at the start of your run. Use `signal` as the abort signal for
|
|
2235
|
+
* `streamText`. Call `reset()` at the start of each turn to get a fresh
|
|
2236
|
+
* per-turn signal. Call `cleanup()` when the run ends.
|
|
2237
|
+
*
|
|
2238
|
+
* @example
|
|
2239
|
+
* ```ts
|
|
2240
|
+
* const stop = chat.createStopSignal();
|
|
2241
|
+
* for (let turn = 0; turn < 100; turn++) {
|
|
2242
|
+
* stop.reset();
|
|
2243
|
+
* const result = streamText({ model, messages, abortSignal: stop.signal });
|
|
2244
|
+
* await chat.pipe(result);
|
|
2245
|
+
* // ...
|
|
2246
|
+
* }
|
|
2247
|
+
* stop.cleanup();
|
|
2248
|
+
* ```
|
|
2249
|
+
*/
|
|
2250
|
+
declare function createStopSignal(): {
|
|
2251
|
+
readonly signal: AbortSignal;
|
|
2252
|
+
reset: () => void;
|
|
2253
|
+
cleanup: () => void;
|
|
2254
|
+
};
|
|
2255
|
+
/**
|
|
2256
|
+
* Signal the frontend that the current turn is complete.
|
|
2257
|
+
*
|
|
2258
|
+
* The `TriggerChatTransport` intercepts this to close the ReadableStream
|
|
2259
|
+
* for the current turn. Call after piping the response stream.
|
|
2260
|
+
*
|
|
2261
|
+
* @example
|
|
2262
|
+
* ```ts
|
|
2263
|
+
* await chat.pipe(result);
|
|
2264
|
+
* await chat.writeTurnComplete();
|
|
2265
|
+
* ```
|
|
2266
|
+
*/
|
|
2267
|
+
declare function chatWriteTurnComplete(options?: {
|
|
2268
|
+
publicAccessToken?: string;
|
|
2269
|
+
}): Promise<void>;
|
|
2270
|
+
/**
|
|
2271
|
+
* Pipe a `StreamTextResult` (or similar) to the chat stream and capture
|
|
2272
|
+
* the assistant's response message via `onFinish`.
|
|
2273
|
+
*
|
|
2274
|
+
* Combines `toUIMessageStream()` + `onFinish` callback + `chat.pipe()`.
|
|
2275
|
+
* Returns the captured `UIMessage`, or `undefined` if capture failed.
|
|
2276
|
+
*
|
|
2277
|
+
* @example
|
|
2278
|
+
* ```ts
|
|
2279
|
+
* const result = streamText({ model, messages, abortSignal: signal });
|
|
2280
|
+
* const response = await chat.pipeAndCapture(result, { signal });
|
|
2281
|
+
* if (response) conversation.addResponse(response);
|
|
2282
|
+
* ```
|
|
2283
|
+
*/
|
|
2284
|
+
declare function pipeChatAndCapture(source: UIMessageStreamable, options?: {
|
|
2285
|
+
signal?: AbortSignal;
|
|
2286
|
+
spanName?: string;
|
|
2287
|
+
}): Promise<UIMessage | undefined>;
|
|
2288
|
+
/**
|
|
2289
|
+
* Accumulates conversation messages across turns.
|
|
2290
|
+
*
|
|
2291
|
+
* Handles the transport protocol: turn 0 sends full history (replace),
|
|
2292
|
+
* subsequent turns send only new messages (append), regenerate sends
|
|
2293
|
+
* full history minus last assistant message (replace).
|
|
2294
|
+
*
|
|
2295
|
+
* @example
|
|
2296
|
+
* ```ts
|
|
2297
|
+
* const conversation = new chat.MessageAccumulator();
|
|
2298
|
+
* for (let turn = 0; turn < 100; turn++) {
|
|
2299
|
+
* const messages = await conversation.addIncoming(payload.messages, payload.trigger, turn);
|
|
2300
|
+
* const result = streamText({ model, messages });
|
|
2301
|
+
* const response = await chat.pipeAndCapture(result);
|
|
2302
|
+
* if (response) await conversation.addResponse(response);
|
|
2303
|
+
* }
|
|
2304
|
+
* ```
|
|
2305
|
+
*/
|
|
2306
|
+
declare class ChatMessageAccumulator {
|
|
2307
|
+
modelMessages: ModelMessage[];
|
|
2308
|
+
uiMessages: UIMessage[];
|
|
2309
|
+
private _compaction?;
|
|
2310
|
+
private _pendingMessages?;
|
|
2311
|
+
private _steeringQueue;
|
|
2312
|
+
constructor(options?: {
|
|
2313
|
+
compaction?: ChatAgentCompactionOptions;
|
|
2314
|
+
pendingMessages?: PendingMessagesOptions;
|
|
2315
|
+
});
|
|
2316
|
+
/**
|
|
2317
|
+
* Add incoming messages from the transport payload.
|
|
2318
|
+
* Returns the full accumulated model messages for `streamText`.
|
|
2319
|
+
*/
|
|
2320
|
+
addIncoming(messages: UIMessage[], trigger: string, turn: number): Promise<ModelMessage[]>;
|
|
2321
|
+
/**
|
|
2322
|
+
* Add the assistant's response to the accumulator.
|
|
2323
|
+
* Call after `pipeAndCapture` with the captured response.
|
|
2324
|
+
*/
|
|
2325
|
+
/**
|
|
2326
|
+
* Replace all accumulated messages (for compaction).
|
|
2327
|
+
* Converts UIMessages to ModelMessages internally.
|
|
2328
|
+
*/
|
|
2329
|
+
setMessages(uiMessages: UIMessage[]): Promise<void>;
|
|
2330
|
+
addResponse(response: UIMessage): Promise<void>;
|
|
2331
|
+
/**
|
|
2332
|
+
* Queue a message for injection via `prepareStep`. Call from a
|
|
2333
|
+
* `messagesInput.on()` listener when a message arrives during streaming.
|
|
2334
|
+
*/
|
|
2335
|
+
steer(message: UIMessage, modelMessages?: ModelMessage[]): void;
|
|
2336
|
+
/**
|
|
2337
|
+
* Queue a message for injection, converting to model messages automatically.
|
|
2338
|
+
*/
|
|
2339
|
+
steerAsync(message: UIMessage): Promise<void>;
|
|
2340
|
+
/**
|
|
2341
|
+
* Get and clear unconsumed steering messages.
|
|
2342
|
+
*/
|
|
2343
|
+
drainSteering(): UIMessage[];
|
|
2344
|
+
/**
|
|
2345
|
+
* Returns a `prepareStep` function that handles both compaction and
|
|
2346
|
+
* pending message injection. Pass to `streamText({ prepareStep: conversation.prepareStep() })`.
|
|
2347
|
+
*/
|
|
2348
|
+
prepareStep(): ((args: {
|
|
2349
|
+
messages: ModelMessage[];
|
|
2350
|
+
steps: CompactionStep[];
|
|
2351
|
+
}) => Promise<{
|
|
2352
|
+
messages: ModelMessage[];
|
|
2353
|
+
} | undefined>) | undefined;
|
|
2354
|
+
/**
|
|
2355
|
+
* Run outer-loop compaction if needed. Call after adding the response
|
|
2356
|
+
* and capturing usage. Applies `compactModelMessages` and `compactUIMessages`
|
|
2357
|
+
* callbacks if configured.
|
|
2358
|
+
*
|
|
2359
|
+
* @returns `true` if compaction was performed, `false` otherwise.
|
|
2360
|
+
*/
|
|
2361
|
+
compactIfNeeded(usage: LanguageModelUsage | undefined, context?: {
|
|
2362
|
+
chatId?: string;
|
|
2363
|
+
turn?: number;
|
|
2364
|
+
clientData?: unknown;
|
|
2365
|
+
totalUsage?: LanguageModelUsage;
|
|
2366
|
+
}): Promise<boolean>;
|
|
2367
|
+
}
|
|
2368
|
+
export type ChatSessionOptions = {
|
|
2369
|
+
/** Run-level cancel signal (from task context). */
|
|
2370
|
+
signal: AbortSignal;
|
|
2371
|
+
/** Seconds to stay idle between turns before suspending. @default 30 */
|
|
2372
|
+
idleTimeoutInSeconds?: number;
|
|
2373
|
+
/** Duration string for suspend timeout. @default "1h" */
|
|
2374
|
+
timeout?: string;
|
|
2375
|
+
/** Max turns before ending. @default 100 */
|
|
2376
|
+
maxTurns?: number;
|
|
2377
|
+
/** Automatic context compaction — same options as `chat.agent({ compaction })`. */
|
|
2378
|
+
compaction?: ChatAgentCompactionOptions;
|
|
2379
|
+
/** Configure mid-execution message injection — same options as `chat.agent({ pendingMessages })`. */
|
|
2380
|
+
pendingMessages?: PendingMessagesOptions;
|
|
2381
|
+
};
|
|
2382
|
+
export type ChatTurn = {
|
|
2383
|
+
/** Turn number (0-indexed). */
|
|
2384
|
+
number: number;
|
|
2385
|
+
/** Chat session ID. */
|
|
2386
|
+
chatId: string;
|
|
2387
|
+
/** What triggered this turn. */
|
|
2388
|
+
trigger: string;
|
|
2389
|
+
/** Client data from the transport (`metadata` field on the wire payload). */
|
|
2390
|
+
clientData: unknown;
|
|
2391
|
+
/** Full accumulated model messages — pass directly to `streamText`. */
|
|
2392
|
+
readonly messages: ModelMessage[];
|
|
2393
|
+
/** Full accumulated UI messages — use for persistence. */
|
|
2394
|
+
readonly uiMessages: UIMessage[];
|
|
2395
|
+
/** Combined stop+cancel AbortSignal (fresh each turn). */
|
|
2396
|
+
signal: AbortSignal;
|
|
2397
|
+
/** Whether the user stopped generation this turn. */
|
|
2398
|
+
readonly stopped: boolean;
|
|
2399
|
+
/** Whether this is a continuation run. */
|
|
2400
|
+
continuation: boolean;
|
|
2401
|
+
/** Token usage from the previous turn. Undefined on turn 0. */
|
|
2402
|
+
previousTurnUsage?: LanguageModelUsage;
|
|
2403
|
+
/** Cumulative token usage across all completed turns so far. */
|
|
2404
|
+
totalUsage: LanguageModelUsage;
|
|
2405
|
+
/**
|
|
2406
|
+
* Replace accumulated messages (for compaction). Takes UIMessages and
|
|
2407
|
+
* converts to ModelMessages internally. After calling this, `turn.messages`
|
|
2408
|
+
* reflects the compacted history.
|
|
2409
|
+
*/
|
|
2410
|
+
setMessages(uiMessages: UIMessage[]): Promise<void>;
|
|
2411
|
+
/**
|
|
2412
|
+
* Easy path: pipe stream, capture response, accumulate it,
|
|
2413
|
+
* clean up aborted parts if stopped, and write turn-complete chunk.
|
|
2414
|
+
*/
|
|
2415
|
+
complete(source: UIMessageStreamable): Promise<UIMessage | undefined>;
|
|
2416
|
+
/**
|
|
2417
|
+
* Manual path: just write turn-complete chunk.
|
|
2418
|
+
* Use when you've already piped and accumulated manually.
|
|
2419
|
+
*/
|
|
2420
|
+
done(): Promise<void>;
|
|
2421
|
+
/**
|
|
2422
|
+
* Add the response to the accumulator manually.
|
|
2423
|
+
* Use with `chat.pipeAndCapture` when you need control between pipe and done.
|
|
2424
|
+
*/
|
|
2425
|
+
addResponse(response: UIMessage): Promise<void>;
|
|
2426
|
+
/**
|
|
2427
|
+
* Returns a `prepareStep` function that handles both compaction and
|
|
2428
|
+
* pending message injection. Pass to `streamText({ prepareStep: turn.prepareStep() })`.
|
|
2429
|
+
* Only needed when not using `chat.toStreamTextOptions()` (which auto-injects it).
|
|
2430
|
+
*/
|
|
2431
|
+
prepareStep(): ((args: {
|
|
2432
|
+
messages: ModelMessage[];
|
|
2433
|
+
steps: CompactionStep[];
|
|
2434
|
+
}) => Promise<{
|
|
2435
|
+
messages: ModelMessage[];
|
|
2436
|
+
} | undefined>) | undefined;
|
|
2437
|
+
};
|
|
2438
|
+
/**
|
|
2439
|
+
* Create a chat session that yields turns as an async iterator.
|
|
2440
|
+
*
|
|
2441
|
+
* Handles: preload wait, stop signals, message accumulation, turn-complete
|
|
2442
|
+
* signaling, and idle/suspend between turns. You control: initialization,
|
|
2443
|
+
* model/tool selection, persistence, and any custom per-turn logic.
|
|
2444
|
+
*
|
|
2445
|
+
* @example
|
|
2446
|
+
* ```ts
|
|
2447
|
+
* import { task } from "@trigger.dev/sdk";
|
|
2448
|
+
* import { chat, type ChatTaskWirePayload } from "@trigger.dev/sdk/ai";
|
|
2449
|
+
* import { streamText } from "ai";
|
|
2450
|
+
* import { openai } from "@ai-sdk/openai";
|
|
2451
|
+
*
|
|
2452
|
+
* export const myChat = task({
|
|
2453
|
+
* id: "my-chat",
|
|
2454
|
+
* run: async (payload: ChatTaskWirePayload, { signal }) => {
|
|
2455
|
+
* const session = chat.createSession(payload, { signal });
|
|
2456
|
+
*
|
|
2457
|
+
* for await (const turn of session) {
|
|
2458
|
+
* const result = streamText({
|
|
2459
|
+
* model: openai("gpt-4o"),
|
|
2460
|
+
* messages: turn.messages,
|
|
2461
|
+
* abortSignal: turn.signal,
|
|
2462
|
+
* });
|
|
2463
|
+
* await turn.complete(result);
|
|
2464
|
+
* }
|
|
2465
|
+
* },
|
|
2466
|
+
* });
|
|
2467
|
+
* ```
|
|
2468
|
+
*/
|
|
2469
|
+
declare function createChatSession(payload: ChatTaskWirePayload, options: ChatSessionOptions): AsyncIterable<ChatTurn>;
|
|
2470
|
+
/**
|
|
2471
|
+
* A Proxy-backed, run-scoped data object that appears as `T` to users.
|
|
2472
|
+
* Includes helper methods for initialization, dirty tracking, and serialization.
|
|
2473
|
+
* Internal metadata is stored behind Symbols and invisible to
|
|
2474
|
+
* `Object.keys()`, `JSON.stringify()`, and spread.
|
|
2475
|
+
*/
|
|
2476
|
+
export type ChatLocal<T extends Record<string, unknown>> = T & {
|
|
2477
|
+
/** Initialize the local with a value. Call in `onChatStart` or `run()`. */
|
|
2478
|
+
init(value: T): void;
|
|
2479
|
+
/** Returns `true` if any property was set since the last check. Resets the dirty flag. */
|
|
2480
|
+
hasChanged(): boolean;
|
|
2481
|
+
/** Returns a plain object copy of the current value. Useful for persistence. */
|
|
2482
|
+
get(): T;
|
|
2483
|
+
readonly [CHAT_LOCAL_KEY]: ReturnType<typeof locals.create<T>>;
|
|
2484
|
+
readonly [CHAT_LOCAL_DIRTY_KEY]: ReturnType<typeof locals.create<boolean>>;
|
|
2485
|
+
};
|
|
2486
|
+
/**
|
|
2487
|
+
* Creates a per-run typed data object accessible from anywhere during task execution.
|
|
2488
|
+
*
|
|
2489
|
+
* Declare at module level, then initialize inside `onBoot` (recommended — fires
|
|
2490
|
+
* on every fresh worker including continuation runs). Do NOT initialize in
|
|
2491
|
+
* `onChatStart` alone: `onChatStart` only fires on the chat's very first
|
|
2492
|
+
* message, so `chat.local` would be uninitialized on continuation runs and
|
|
2493
|
+
* `run()` would throw.
|
|
2494
|
+
*
|
|
2495
|
+
* Multiple locals can coexist — each gets its own isolated run-scoped storage.
|
|
2496
|
+
*
|
|
2497
|
+
* The `id` is required and must be unique across all `chat.local()` calls in
|
|
2498
|
+
* your project. It's used to serialize values into subtask metadata so that
|
|
2499
|
+
* `ai.toolExecute()` (or legacy `ai.tool()`) subtasks can auto-hydrate parent locals (read-only).
|
|
2500
|
+
*
|
|
2501
|
+
* @example
|
|
2502
|
+
* ```ts
|
|
2503
|
+
* import { chat } from "@trigger.dev/sdk/ai";
|
|
2504
|
+
*
|
|
2505
|
+
* const userPrefs = chat.local<{ theme: string; language: string }>({ id: "userPrefs" });
|
|
2506
|
+
* const gameState = chat.local<{ score: number; streak: number }>({ id: "gameState" });
|
|
2507
|
+
*
|
|
2508
|
+
* export const myChat = chat.agent({
|
|
2509
|
+
* id: "my-chat",
|
|
2510
|
+
* onBoot: async ({ clientData }) => {
|
|
2511
|
+
* const prefs = await db.prefs.findUnique({ where: { userId: clientData.userId } });
|
|
2512
|
+
* userPrefs.init(prefs ?? { theme: "dark", language: "en" });
|
|
2513
|
+
* gameState.init({ score: 0, streak: 0 });
|
|
2514
|
+
* },
|
|
2515
|
+
* onTurnComplete: async ({ chatId }) => {
|
|
2516
|
+
* if (gameState.hasChanged()) {
|
|
2517
|
+
* await db.save({ where: { chatId }, data: gameState.get() });
|
|
2518
|
+
* }
|
|
2519
|
+
* },
|
|
2520
|
+
* run: async ({ messages }) => {
|
|
2521
|
+
* gameState.score++;
|
|
2522
|
+
* return streamText({
|
|
2523
|
+
* system: `User prefers ${userPrefs.theme} theme. Score: ${gameState.score}`,
|
|
2524
|
+
* messages,
|
|
2525
|
+
* });
|
|
2526
|
+
* },
|
|
2527
|
+
* });
|
|
2528
|
+
* ```
|
|
2529
|
+
*/
|
|
2530
|
+
declare function chatLocal<T extends Record<string, unknown>>(options: {
|
|
2531
|
+
id: string;
|
|
2532
|
+
}): ChatLocal<T>;
|
|
2533
|
+
/**
|
|
2534
|
+
* Extracts the client data (metadata) type from a chat task.
|
|
2535
|
+
* Use this to type the `metadata` option on the transport.
|
|
2536
|
+
*
|
|
2537
|
+
* @example
|
|
2538
|
+
* ```ts
|
|
2539
|
+
* import type { InferChatClientData } from "@trigger.dev/sdk/ai";
|
|
2540
|
+
* import type { myChat } from "@/trigger/chat";
|
|
2541
|
+
*
|
|
2542
|
+
* type MyClientData = InferChatClientData<typeof myChat>;
|
|
2543
|
+
* // { model?: string; userId: string }
|
|
2544
|
+
* ```
|
|
2545
|
+
*/
|
|
2546
|
+
import type { InferChatClientData } from "./ai-shared.js";
|
|
2547
|
+
export type { InferChatClientData, InferChatUIMessage } from "./ai-shared.js";
|
|
2548
|
+
/**
|
|
2549
|
+
* Options for {@link createChatStartSessionAction}.
|
|
2550
|
+
*/
|
|
2551
|
+
/**
|
|
2552
|
+
* Discriminator for per-endpoint `baseURL` / `fetch` callbacks on
|
|
2553
|
+
* `createChatStartSessionAction`.
|
|
2554
|
+
*
|
|
2555
|
+
* - `"sessions"` — `POST /api/v1/sessions` (session create + first run trigger).
|
|
2556
|
+
* - `"auth"` — `POST /api/v1/auth/jwt/claims` (only fired when
|
|
2557
|
+
* `tokenTTL` is set; otherwise the publicAccessToken from session create
|
|
2558
|
+
* is reused as-is).
|
|
2559
|
+
*/
|
|
2560
|
+
export type ChatStartSessionEndpoint = "sessions" | "auth";
|
|
2561
|
+
export type ChatStartSessionEndpointContext = {
|
|
2562
|
+
endpoint: ChatStartSessionEndpoint;
|
|
2563
|
+
chatId: string;
|
|
2564
|
+
};
|
|
2565
|
+
export type ChatStartSessionBaseURLResolver = (ctx: ChatStartSessionEndpointContext) => string;
|
|
2566
|
+
export type ChatStartSessionFetchOverride = (url: string, init: RequestInit, ctx: ChatStartSessionEndpointContext) => Promise<Response>;
|
|
2567
|
+
export type CreateChatStartSessionActionOptions = {
|
|
2568
|
+
/** TTL for the session-scoped public access token. @default "1h" */
|
|
2569
|
+
tokenTTL?: string | number | Date;
|
|
2570
|
+
/**
|
|
2571
|
+
* Default trigger config used when starting a new session for a chat.
|
|
2572
|
+
* Per-call `params.triggerConfig` shallow-merges on top.
|
|
2573
|
+
*/
|
|
2574
|
+
triggerConfig?: Partial<SessionTriggerConfig>;
|
|
2575
|
+
/**
|
|
2576
|
+
* Override the Trigger.dev API base URL. String applies to both
|
|
2577
|
+
* `/api/v1/sessions` and `/api/v1/auth/jwt/claims`; function picks per
|
|
2578
|
+
* endpoint. When unset, falls back to `apiClientManager.baseURL`
|
|
2579
|
+
* (typically the `TRIGGER_API_URL` env var). Set this to route session
|
|
2580
|
+
* create through a trusted edge proxy that injects server-side signal
|
|
2581
|
+
* into `basePayload.metadata` before forwarding upstream.
|
|
2582
|
+
*/
|
|
2583
|
+
baseURL?: string | ChatStartSessionBaseURLResolver;
|
|
2584
|
+
/**
|
|
2585
|
+
* Per-request fetch override. Receives the resolved URL, RequestInit,
|
|
2586
|
+
* and endpoint context. Use for header injection, proxy routing, or
|
|
2587
|
+
* custom retry. Applies to both session-create and JWT-claims POSTs.
|
|
2588
|
+
*/
|
|
2589
|
+
fetch?: ChatStartSessionFetchOverride;
|
|
2590
|
+
};
|
|
2591
|
+
/**
|
|
2592
|
+
* Params for the function returned by {@link createChatStartSessionAction}.
|
|
2593
|
+
*/
|
|
2594
|
+
export type ChatStartSessionParams<TChat extends AnyTask = AnyTask> = {
|
|
2595
|
+
/** Conversation id (mapped to the Session's `externalId`). */
|
|
2596
|
+
chatId: string;
|
|
2597
|
+
/**
|
|
2598
|
+
* Typed client data — folded into the first run's `payload.metadata` so
|
|
2599
|
+
* `onPreload`, `onChatStart`, etc. see the same `clientData` shape on the
|
|
2600
|
+
* first turn as subsequent turns get via the transport's `clientData`
|
|
2601
|
+
* option. Typed via the agent's `clientDataSchema` when the action is
|
|
2602
|
+
* parameterised with `createStartSessionAction<typeof myChat>(...)`.
|
|
2603
|
+
*/
|
|
2604
|
+
clientData?: InferChatClientData<TChat>;
|
|
2605
|
+
/**
|
|
2606
|
+
* Per-call trigger config. Shallow-merged over the action's default
|
|
2607
|
+
* `triggerConfig`. `basePayload` is the customer's wire payload (for
|
|
2608
|
+
* `chat.agent`: anything beyond `chatId`/`messages`/`trigger`/`metadata`,
|
|
2609
|
+
* which the runtime injects automatically).
|
|
2610
|
+
*/
|
|
2611
|
+
triggerConfig?: Partial<SessionTriggerConfig>;
|
|
2612
|
+
/**
|
|
2613
|
+
* Opaque session-level metadata stored on the Session row. Separate from
|
|
2614
|
+
* the per-turn `clientData` above. Use this when you want to attach
|
|
2615
|
+
* server-side metadata that doesn't go through the agent's `clientDataSchema`.
|
|
2616
|
+
*/
|
|
2617
|
+
metadata?: Record<string, unknown>;
|
|
2618
|
+
};
|
|
2619
|
+
/**
|
|
2620
|
+
* Result from {@link createChatStartSessionAction}'s returned function.
|
|
2621
|
+
*/
|
|
2622
|
+
export type ChatStartSessionResult = {
|
|
2623
|
+
/**
|
|
2624
|
+
* Session-scoped public access token (`read:sessions:{chatId} +
|
|
2625
|
+
* write:sessions:{chatId}`). Pass this to the browser; the transport
|
|
2626
|
+
* uses it to call `.in/append`, `.out`, `end-and-continue`.
|
|
2627
|
+
*/
|
|
2628
|
+
publicAccessToken: string;
|
|
2629
|
+
/** Friendly id of the run triggered alongside session create. */
|
|
2630
|
+
runId: string;
|
|
2631
|
+
/** Session friendlyId — informational. */
|
|
2632
|
+
sessionId: string;
|
|
2633
|
+
};
|
|
2634
|
+
/**
|
|
2635
|
+
* Creates a server-side helper that starts (or resumes) a Session for a
|
|
2636
|
+
* given chatId — atomically creating the row, triggering the first run,
|
|
2637
|
+
* and returning a session-scoped PAT for the browser to use.
|
|
2638
|
+
*
|
|
2639
|
+
* Wrap in a Next.js server action (or any server-side handler) so the
|
|
2640
|
+
* customer's secret key never crosses to the browser.
|
|
2641
|
+
*
|
|
2642
|
+
* Parameterise the action with `<typeof yourChatAgent>` to type the
|
|
2643
|
+
* `clientData` field against your agent's `clientDataSchema`.
|
|
2644
|
+
*
|
|
2645
|
+
* @example
|
|
2646
|
+
* ```ts
|
|
2647
|
+
* // actions.ts
|
|
2648
|
+
* "use server";
|
|
2649
|
+
* import { chat } from "@trigger.dev/sdk/ai";
|
|
2650
|
+
* import type { myChat } from "@/trigger/chat";
|
|
2651
|
+
*
|
|
2652
|
+
* export const startChatSession = chat.createStartSessionAction<typeof myChat>(
|
|
2653
|
+
* "my-chat",
|
|
2654
|
+
* { triggerConfig: { machine: "small-1x" } }
|
|
2655
|
+
* );
|
|
2656
|
+
* ```
|
|
2657
|
+
*
|
|
2658
|
+
* Then in the browser, threading the typed `clientData` from the transport:
|
|
2659
|
+
* ```tsx
|
|
2660
|
+
* const transport = useTriggerChatTransport<typeof myChat>({
|
|
2661
|
+
* task: "my-chat",
|
|
2662
|
+
* accessToken: ({ chatId }) => mintChatAccessToken(chatId),
|
|
2663
|
+
* startSession: ({ chatId, clientData }) =>
|
|
2664
|
+
* startChatSession({ chatId, clientData }),
|
|
2665
|
+
* });
|
|
2666
|
+
* ```
|
|
2667
|
+
*/
|
|
2668
|
+
declare function createChatStartSessionAction<TChat extends AnyTask = AnyTask>(taskId: string, options?: CreateChatStartSessionActionOptions): (params: ChatStartSessionParams<TChat>) => Promise<ChatStartSessionResult>;
|
|
2669
|
+
export declare const chat: {
|
|
2670
|
+
/** Create a chat agent. See {@link chatAgent}. */
|
|
2671
|
+
agent: typeof chatAgent;
|
|
2672
|
+
/** Create a custom agent with manual lifecycle control. See {@link chatCustomAgent}. */
|
|
2673
|
+
customAgent: typeof chatCustomAgent;
|
|
2674
|
+
/** Create a chat task with a fixed {@link UIMessage} subtype and optional default stream options. See {@link withUIMessage}. */
|
|
2675
|
+
withUIMessage: typeof withUIMessage;
|
|
2676
|
+
/** Create a chat task with a fixed client data schema. See {@link withClientData}. */
|
|
2677
|
+
withClientData: typeof withClientData;
|
|
2678
|
+
/** Create a server-side helper for starting (or resuming) a Session for a chatId. See {@link createChatStartSessionAction}. */
|
|
2679
|
+
createStartSessionAction: typeof createChatStartSessionAction;
|
|
2680
|
+
/** Pipe a stream to the chat transport. See {@link pipeChat}. */
|
|
2681
|
+
pipe: typeof pipeChat;
|
|
2682
|
+
/** Create a per-run typed local. See {@link chatLocal}. */
|
|
2683
|
+
local: typeof chatLocal;
|
|
2684
|
+
/** Create a public access token for a chat task. See {@link createChatAccessToken}. */
|
|
2685
|
+
createAccessToken: typeof createChatAccessToken;
|
|
2686
|
+
/** Override the turn timeout at runtime (duration string). See {@link setTurnTimeout}. */
|
|
2687
|
+
setTurnTimeout: typeof setTurnTimeout;
|
|
2688
|
+
/** Override the turn timeout at runtime (seconds). See {@link setTurnTimeoutInSeconds}. */
|
|
2689
|
+
setTurnTimeoutInSeconds: typeof setTurnTimeoutInSeconds;
|
|
2690
|
+
/** Override the idle timeout at runtime. See {@link setIdleTimeoutInSeconds}. */
|
|
2691
|
+
setIdleTimeoutInSeconds: typeof setIdleTimeoutInSeconds;
|
|
2692
|
+
/** Override toUIMessageStream() options for the current turn. See {@link setUIMessageStreamOptions}. */
|
|
2693
|
+
setUIMessageStreamOptions: typeof setUIMessageStreamOptions;
|
|
2694
|
+
/** Check if the current turn was stopped by the user. See {@link isStopped}. */
|
|
2695
|
+
isStopped: typeof isStopped;
|
|
2696
|
+
/** Request that the run exits after the current turn so the next message starts on the latest version. See {@link requestUpgrade}. */
|
|
2697
|
+
requestUpgrade: typeof requestUpgrade;
|
|
2698
|
+
/** Exit the run after the current turn completes, without any upgrade signal. See {@link endRun}. */
|
|
2699
|
+
endRun: typeof endRun;
|
|
2700
|
+
/** Clean up aborted parts from a UIMessage. See {@link cleanupAbortedParts}. */
|
|
2701
|
+
cleanupAbortedParts: typeof cleanupAbortedParts;
|
|
2702
|
+
/** Register background work that runs in parallel with streaming. See {@link chatDefer}. */
|
|
2703
|
+
defer: typeof chatDefer;
|
|
2704
|
+
/** Queue model messages for injection at the next `prepareStep` boundary. See {@link injectBackgroundContext}. */
|
|
2705
|
+
inject: typeof injectBackgroundContext;
|
|
2706
|
+
/** Typed chat output stream for writing custom chunks or piping from subtasks. */
|
|
2707
|
+
stream: RealtimeDefinedStream<UIMessageChunk>;
|
|
2708
|
+
/** Write data parts that persist to the response message. See {@link chatResponse}. */
|
|
2709
|
+
response: {
|
|
2710
|
+
/**
|
|
2711
|
+
* Write a single chunk. Non-transient data parts are accumulated into the
|
|
2712
|
+
* response message; everything else is stream-only.
|
|
2713
|
+
*/
|
|
2714
|
+
write(part: UIMessageChunk): void;
|
|
2715
|
+
};
|
|
2716
|
+
/** Pre-built input stream for receiving messages from the transport. */
|
|
2717
|
+
messages: RealtimeDefinedInputStream<ChatTaskWirePayload>;
|
|
2718
|
+
/** Create a managed stop signal wired to the stop input stream. See {@link createStopSignal}. */
|
|
2719
|
+
createStopSignal: typeof createStopSignal;
|
|
2720
|
+
/** Signal the frontend that the current turn is complete. See {@link chatWriteTurnComplete}. */
|
|
2721
|
+
writeTurnComplete: typeof chatWriteTurnComplete;
|
|
2722
|
+
/** Pipe a stream and capture the response message. See {@link pipeChatAndCapture}. */
|
|
2723
|
+
pipeAndCapture: typeof pipeChatAndCapture;
|
|
2724
|
+
/** Message accumulator class for raw task chat. See {@link ChatMessageAccumulator}. */
|
|
2725
|
+
MessageAccumulator: typeof ChatMessageAccumulator;
|
|
2726
|
+
/** Create a chat session (async iterator). See {@link createChatSession}. */
|
|
2727
|
+
createSession: typeof createChatSession;
|
|
2728
|
+
/**
|
|
2729
|
+
* Store and retrieve a resolved prompt for the current run.
|
|
2730
|
+
*
|
|
2731
|
+
* - `chat.prompt.set(resolved)` — store a `ResolvedPrompt` or plain string
|
|
2732
|
+
* - `chat.prompt()` — read the stored prompt (throws if not set)
|
|
2733
|
+
*/
|
|
2734
|
+
prompt: typeof getChatPrompt & {
|
|
2735
|
+
set: typeof setChatPrompt;
|
|
2736
|
+
};
|
|
2737
|
+
/**
|
|
2738
|
+
* Store and retrieve resolved agent skills for the current run.
|
|
2739
|
+
*
|
|
2740
|
+
* - `chat.skills.set([...])` — store an array of `ResolvedSkill`s
|
|
2741
|
+
* - `chat.skills()` — read the stored skills (returns undefined if none)
|
|
2742
|
+
*
|
|
2743
|
+
* Skills set here are automatically injected into `streamText` by
|
|
2744
|
+
* `chat.toStreamTextOptions()`: skill descriptions land in the system
|
|
2745
|
+
* prompt and `loadSkill` / `readFile` / `bash` tools are added to the
|
|
2746
|
+
* tool set.
|
|
2747
|
+
*/
|
|
2748
|
+
skills: typeof getChatSkills & {
|
|
2749
|
+
set: typeof setChatSkills;
|
|
2750
|
+
};
|
|
2751
|
+
/**
|
|
2752
|
+
* Returns an options object ready to spread into `streamText()`.
|
|
2753
|
+
* Reads the stored prompt and returns `{ system, experimental_telemetry, ...config }`.
|
|
2754
|
+
* Returns `{}` if no prompt has been set.
|
|
2755
|
+
*/
|
|
2756
|
+
toStreamTextOptions: typeof toStreamTextOptions;
|
|
2757
|
+
/**
|
|
2758
|
+
* Replace the accumulated conversation messages for compaction.
|
|
2759
|
+
* Call from `onTurnStart` or `onTurnComplete`. Takes `UIMessage[]` and
|
|
2760
|
+
* converts to `ModelMessage[]` internally.
|
|
2761
|
+
*/
|
|
2762
|
+
setMessages: typeof setChatMessages;
|
|
2763
|
+
/**
|
|
2764
|
+
* Imperative API for modifying the accumulated message history.
|
|
2765
|
+
* Supports rollback, remove, replace, slice, and full replacement.
|
|
2766
|
+
* Can be called from any hook or `run()`.
|
|
2767
|
+
*/
|
|
2768
|
+
history: {
|
|
2769
|
+
/** Read the current accumulated UI messages (copy). */
|
|
2770
|
+
all(): UIMessage[];
|
|
2771
|
+
/**
|
|
2772
|
+
* Read the current chain as an ordered `UIMessage[]`. Identical to
|
|
2773
|
+
* `all()`; use whichever name reads better in context.
|
|
2774
|
+
*/
|
|
2775
|
+
getChain(): UIMessage[];
|
|
2776
|
+
/**
|
|
2777
|
+
* Find a message by id. Returns `undefined` if no message with that id
|
|
2778
|
+
* is present in the current chain.
|
|
2779
|
+
*/
|
|
2780
|
+
findMessage(messageId: string): UIMessage | undefined;
|
|
2781
|
+
/**
|
|
2782
|
+
* Tool calls on the *most recent* assistant message that are still in
|
|
2783
|
+
* `input-available` state (waiting on an `addToolOutput` answer). The
|
|
2784
|
+
* scan walks back from the tail and stops at the first assistant
|
|
2785
|
+
* message it finds, so a trailing user message does not change the
|
|
2786
|
+
* result — pending tool calls remain pending until they're resolved
|
|
2787
|
+
* on that assistant or the assistant is removed.
|
|
2788
|
+
*
|
|
2789
|
+
* Use this to gate fresh user turns or actions during HITL flows: if
|
|
2790
|
+
* `getPendingToolCalls().length > 0`, an `addToolOutput` is expected.
|
|
2791
|
+
*
|
|
2792
|
+
* Returns `[]` if there is no assistant message yet, or if the most
|
|
2793
|
+
* recent assistant has no pending tool calls.
|
|
2794
|
+
*
|
|
2795
|
+
* Approval flows (`approval-requested` / `approval-responded` states)
|
|
2796
|
+
* are not surfaced here. Those are about the user authorizing a tool
|
|
2797
|
+
* to run; "pending" is about the user *answering* a tool call.
|
|
2798
|
+
*/
|
|
2799
|
+
getPendingToolCalls(): ChatToolCallRef[];
|
|
2800
|
+
/**
|
|
2801
|
+
* Tool calls across the chain with a final result (`output-available`
|
|
2802
|
+
* or `output-error`). Use this to dedup re-saves when the AI SDK
|
|
2803
|
+
* resends an assistant message with progressively more answered parts.
|
|
2804
|
+
*/
|
|
2805
|
+
getResolvedToolCalls(): ChatToolCallRef[];
|
|
2806
|
+
/**
|
|
2807
|
+
* Pure helper: returns the tool parts in `message` whose results are
|
|
2808
|
+
* not already represented in the current chain. Use this when
|
|
2809
|
+
* persisting tool results to your own store: each call surfaces only
|
|
2810
|
+
* the *new* answers, so writes stay idempotent across re-streams.
|
|
2811
|
+
* Duplicate `toolCallId`s within `message` itself are also collapsed
|
|
2812
|
+
* to a single entry.
|
|
2813
|
+
*/
|
|
2814
|
+
extractNewToolResults(message: UIMessage): ChatNewToolResult[];
|
|
2815
|
+
/** Replace all accumulated messages. Same as `chat.setMessages()`. */
|
|
2816
|
+
set(messages: UIMessage[]): void;
|
|
2817
|
+
/** Remove a specific message by ID. */
|
|
2818
|
+
remove(messageId: string): void;
|
|
2819
|
+
/** Keep messages up to and including the given ID (undo/rollback). */
|
|
2820
|
+
rollbackTo(messageId: string): void;
|
|
2821
|
+
/** Replace a specific message by ID (edit). */
|
|
2822
|
+
replace(messageId: string, message: UIMessage): void;
|
|
2823
|
+
/** Keep only messages in the given range. */
|
|
2824
|
+
slice(start: number, end?: number): void;
|
|
2825
|
+
};
|
|
2826
|
+
/** Check if it's safe to compact messages (no in-flight tool calls). */
|
|
2827
|
+
isCompactionSafe: typeof isCompactionSafe;
|
|
2828
|
+
/** Returns a `prepareStep` function that handles context compaction automatically. */
|
|
2829
|
+
compactionStep: typeof chatCompactionStep;
|
|
2830
|
+
/** Low-level compaction for use inside a custom `prepareStep`. */
|
|
2831
|
+
compact: typeof chatCompact;
|
|
2832
|
+
/** Read the current compaction state (summary + base message count). */
|
|
2833
|
+
getCompactionState: typeof getCompactionState;
|
|
2834
|
+
/**
|
|
2835
|
+
* The friendlyId (`session_*`) of the backing Session for the current chat.agent run.
|
|
2836
|
+
* Useful for persisting alongside `runId` so reloads can resume the same session.
|
|
2837
|
+
* Throws if called outside a chat.agent `run()` or hook.
|
|
2838
|
+
*/
|
|
2839
|
+
readonly sessionId: string;
|
|
21
2840
|
};
|
|
22
|
-
export {};
|