@trigger.dev/sdk 4.5.0-rc.6 → 4.5.0-rc.7
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/ai.d.ts +171 -5
- package/dist/commonjs/v3/ai.js +309 -22
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/chat-server.d.ts +8 -0
- package/dist/commonjs/v3/chat-server.js +32 -10
- package/dist/commonjs/v3/chat-server.js.map +1 -1
- package/dist/commonjs/v3/chat-server.test.js +51 -0
- package/dist/commonjs/v3/chat-server.test.js.map +1 -1
- package/dist/commonjs/v3/createStartSessionAction.test.js +30 -0
- package/dist/commonjs/v3/createStartSessionAction.test.js.map +1 -1
- package/dist/commonjs/v3/sessions.d.ts +3 -2
- package/dist/commonjs/v3/sessions.js +3 -2
- package/dist/commonjs/v3/sessions.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/ai.d.ts +171 -5
- package/dist/esm/v3/ai.js +309 -22
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/chat-server.d.ts +8 -0
- package/dist/esm/v3/chat-server.js +32 -10
- package/dist/esm/v3/chat-server.js.map +1 -1
- package/dist/esm/v3/chat-server.test.js +51 -0
- package/dist/esm/v3/chat-server.test.js.map +1 -1
- package/dist/esm/v3/createStartSessionAction.test.js +30 -0
- package/dist/esm/v3/createStartSessionAction.test.js.map +1 -1
- package/dist/esm/v3/sessions.d.ts +3 -2
- package/dist/esm/v3/sessions.js +3 -2
- package/dist/esm/v3/sessions.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/docs/ai/prompts.mdx +430 -0
- package/docs/ai-chat/actions.mdx +115 -0
- package/docs/ai-chat/anatomy.mdx +71 -0
- package/docs/ai-chat/backend.mdx +817 -0
- package/docs/ai-chat/background-injection.mdx +221 -0
- package/docs/ai-chat/changelog.mdx +850 -0
- package/docs/ai-chat/chat-local.mdx +174 -0
- package/docs/ai-chat/client-protocol.mdx +1081 -0
- package/docs/ai-chat/compaction.mdx +411 -0
- package/docs/ai-chat/custom-agents.mdx +364 -0
- package/docs/ai-chat/error-handling.mdx +415 -0
- package/docs/ai-chat/fast-starts.mdx +672 -0
- package/docs/ai-chat/frontend.mdx +580 -0
- package/docs/ai-chat/how-it-works.mdx +230 -0
- package/docs/ai-chat/lifecycle-hooks.mdx +530 -0
- package/docs/ai-chat/mcp.mdx +101 -0
- package/docs/ai-chat/overview.mdx +90 -0
- package/docs/ai-chat/patterns/branching-conversations.mdx +284 -0
- package/docs/ai-chat/patterns/code-sandbox.mdx +126 -0
- package/docs/ai-chat/patterns/database-persistence.mdx +414 -0
- package/docs/ai-chat/patterns/human-in-the-loop.mdx +275 -0
- package/docs/ai-chat/patterns/large-payloads.mdx +169 -0
- package/docs/ai-chat/patterns/oom-resilience.mdx +120 -0
- package/docs/ai-chat/patterns/persistence-and-replay.mdx +211 -0
- package/docs/ai-chat/patterns/recovery-boot.mdx +230 -0
- package/docs/ai-chat/patterns/skills.mdx +221 -0
- package/docs/ai-chat/patterns/sub-agents.mdx +383 -0
- package/docs/ai-chat/patterns/tool-result-auditing.mdx +148 -0
- package/docs/ai-chat/patterns/trusted-edge-signals.mdx +337 -0
- package/docs/ai-chat/patterns/version-upgrades.mdx +172 -0
- package/docs/ai-chat/pending-messages.mdx +343 -0
- package/docs/ai-chat/prompt-caching.mdx +206 -0
- package/docs/ai-chat/quick-start.mdx +161 -0
- package/docs/ai-chat/reference.mdx +909 -0
- package/docs/ai-chat/server-chat.mdx +263 -0
- package/docs/ai-chat/sessions.mdx +333 -0
- package/docs/ai-chat/testing.mdx +682 -0
- package/docs/ai-chat/tools.mdx +191 -0
- package/docs/ai-chat/types.mdx +242 -0
- package/docs/ai-chat/upgrade-guide.mdx +515 -0
- package/docs/apikeys.mdx +54 -0
- package/docs/building-with-ai.mdx +261 -0
- package/docs/bulk-actions.mdx +49 -0
- package/docs/changelog.mdx +6 -0
- package/docs/cli-deploy-commands.mdx +9 -0
- package/docs/cli-dev-commands.mdx +9 -0
- package/docs/cli-dev.mdx +8 -0
- package/docs/cli-init-commands.mdx +58 -0
- package/docs/cli-introduction.mdx +25 -0
- package/docs/cli-list-profiles-commands.mdx +42 -0
- package/docs/cli-login-commands.mdx +33 -0
- package/docs/cli-logout-commands.mdx +33 -0
- package/docs/cli-preview-archive.mdx +59 -0
- package/docs/cli-promote-commands.mdx +9 -0
- package/docs/cli-switch.mdx +43 -0
- package/docs/cli-update-commands.mdx +42 -0
- package/docs/cli-whoami-commands.mdx +33 -0
- package/docs/community.mdx +6 -0
- package/docs/config/config-file.mdx +602 -0
- package/docs/config/extensions/additionalFiles.mdx +38 -0
- package/docs/config/extensions/additionalPackages.mdx +40 -0
- package/docs/config/extensions/aptGet.mdx +34 -0
- package/docs/config/extensions/audioWaveform.mdx +20 -0
- package/docs/config/extensions/custom.mdx +380 -0
- package/docs/config/extensions/emitDecoratorMetadata.mdx +29 -0
- package/docs/config/extensions/esbuildPlugin.mdx +31 -0
- package/docs/config/extensions/ffmpeg.mdx +45 -0
- package/docs/config/extensions/lightpanda.mdx +56 -0
- package/docs/config/extensions/overview.mdx +67 -0
- package/docs/config/extensions/playwright.mdx +195 -0
- package/docs/config/extensions/prismaExtension.mdx +1014 -0
- package/docs/config/extensions/puppeteer.mdx +30 -0
- package/docs/config/extensions/pythonExtension.mdx +182 -0
- package/docs/config/extensions/syncEnvVars.mdx +291 -0
- package/docs/context.mdx +235 -0
- package/docs/database-connections.mdx +213 -0
- package/docs/deploy-environment-variables.mdx +435 -0
- package/docs/deployment/atomic-deployment.mdx +172 -0
- package/docs/deployment/overview.mdx +257 -0
- package/docs/deployment/preview-branches.mdx +224 -0
- package/docs/errors-retrying.mdx +379 -0
- package/docs/github-actions.mdx +222 -0
- package/docs/github-integration.mdx +136 -0
- package/docs/github-repo.mdx +8 -0
- package/docs/help-email.mdx +6 -0
- package/docs/help-slack.mdx +11 -0
- package/docs/hidden-tasks.mdx +56 -0
- package/docs/how-it-works.mdx +454 -0
- package/docs/how-to-reduce-your-spend.mdx +217 -0
- package/docs/idempotency.mdx +504 -0
- package/docs/introduction.mdx +223 -0
- package/docs/limits.mdx +241 -0
- package/docs/logging.mdx +195 -0
- package/docs/machines.mdx +952 -0
- package/docs/manual-setup.mdx +632 -0
- package/docs/mcp-agent-rules.mdx +41 -0
- package/docs/mcp-introduction.mdx +385 -0
- package/docs/mcp-tools.mdx +273 -0
- package/docs/migrating-from-v3.mdx +334 -0
- package/docs/observability/dashboards.mdx +102 -0
- package/docs/observability/query.mdx +585 -0
- package/docs/open-source-contributing.mdx +16 -0
- package/docs/open-source-self-hosting.mdx +541 -0
- package/docs/private-networking/aws-console-setup.mdx +304 -0
- package/docs/private-networking/overview.mdx +144 -0
- package/docs/private-networking/troubleshooting.mdx +78 -0
- package/docs/queue-concurrency.mdx +354 -0
- package/docs/quick-start.mdx +97 -0
- package/docs/realtime/auth.mdx +208 -0
- package/docs/realtime/backend/overview.mdx +45 -0
- package/docs/realtime/backend/streams.mdx +418 -0
- package/docs/realtime/backend/subscribe.mdx +225 -0
- package/docs/realtime/how-it-works.mdx +94 -0
- package/docs/realtime/overview.mdx +63 -0
- package/docs/realtime/react-hooks/overview.mdx +73 -0
- package/docs/realtime/react-hooks/streams.mdx +449 -0
- package/docs/realtime/react-hooks/subscribe.mdx +674 -0
- package/docs/realtime/react-hooks/swr.mdx +87 -0
- package/docs/realtime/react-hooks/triggering.mdx +194 -0
- package/docs/realtime/react-hooks/use-wait-token.mdx +34 -0
- package/docs/realtime/run-object.mdx +174 -0
- package/docs/replaying.mdx +72 -0
- package/docs/request-feature.mdx +6 -0
- package/docs/roadmap.mdx +6 -0
- package/docs/run-tests.mdx +20 -0
- package/docs/run-usage.mdx +113 -0
- package/docs/runs/heartbeats.mdx +38 -0
- package/docs/runs/max-duration.mdx +139 -0
- package/docs/runs/metadata.mdx +734 -0
- package/docs/runs/priority.mdx +31 -0
- package/docs/runs.mdx +396 -0
- package/docs/self-hosting/docker.mdx +458 -0
- package/docs/self-hosting/env/supervisor.mdx +74 -0
- package/docs/self-hosting/env/webapp.mdx +276 -0
- package/docs/self-hosting/kubernetes.mdx +601 -0
- package/docs/self-hosting/overview.mdx +108 -0
- package/docs/skills.mdx +85 -0
- package/docs/tags.mdx +120 -0
- package/docs/tasks/overview.mdx +697 -0
- package/docs/tasks/scheduled.mdx +382 -0
- package/docs/tasks/schemaTask.mdx +413 -0
- package/docs/tasks/streams.mdx +884 -0
- package/docs/triggering.mdx +1320 -0
- package/docs/troubleshooting-alerts.mdx +385 -0
- package/docs/troubleshooting-debugging-in-vscode.mdx +8 -0
- package/docs/troubleshooting-github-issues.mdx +6 -0
- package/docs/troubleshooting-uptime-status.mdx +6 -0
- package/docs/troubleshooting.mdx +398 -0
- package/docs/upgrading-packages.mdx +80 -0
- package/docs/vercel-integration.mdx +207 -0
- package/docs/versioning.mdx +56 -0
- package/docs/video-walkthrough.mdx +23 -0
- package/docs/wait-for-token.mdx +540 -0
- package/docs/wait-for.mdx +42 -0
- package/docs/wait-until.mdx +53 -0
- package/docs/wait.mdx +18 -0
- package/docs/writing-tasks-introduction.mdx +33 -0
- package/package.json +8 -5
- package/skills/trigger-authoring-chat-agent/SKILL.md +296 -0
- package/skills/trigger-authoring-tasks/SKILL.md +254 -0
- package/skills/trigger-chat-agent-advanced/SKILL.md +368 -0
- package/skills/trigger-cost-savings/SKILL.md +116 -0
- package/skills/trigger-realtime-and-frontend/SKILL.md +276 -0
package/dist/commonjs/v3/ai.js
CHANGED
|
@@ -63,6 +63,10 @@ const chatTurnContextKey = locals_js_1.locals.create("chat.turnContext");
|
|
|
63
63
|
* @internal
|
|
64
64
|
*/
|
|
65
65
|
const chatSessionHandleKey = locals_js_1.locals.create("chat.sessionHandle");
|
|
66
|
+
// The external `chatId` from the boot payload — the value `ToolCallExecutionOptions.chatId`
|
|
67
|
+
// is documented to carry. Custom-agent loops never set per-turn context, so subtask tool
|
|
68
|
+
// metadata reads this directly rather than the Session handle id.
|
|
69
|
+
const chatExternalIdKey = locals_js_1.locals.create("chat.externalId");
|
|
66
70
|
/**
|
|
67
71
|
* S2 seq_num of the most recent `turn-complete` control record written by
|
|
68
72
|
* this worker. Read by `writeTurnCompleteChunk` to know what to trim back
|
|
@@ -117,6 +121,43 @@ async function findLatestSessionInCursor(chatId) {
|
|
|
117
121
|
async function __findLatestSessionInCursorForTests(chatId) {
|
|
118
122
|
return findLatestSessionInCursor(chatId);
|
|
119
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Seed the `.in` resume cursor for custom-agent loops (`chat.customAgent`
|
|
126
|
+
* raw loops and `chat.createSession`) the way `chat.agent`'s boot does.
|
|
127
|
+
*
|
|
128
|
+
* MUST run before anything attaches a `.in` listener (`createStopSignal`,
|
|
129
|
+
* `chat.messages.on`, the first wait): attaching opens the SSE tail with
|
|
130
|
+
* `Last-Event-ID` from the seeded cursor, so attach-then-seed replays
|
|
131
|
+
* every record from seq 0 — already-answered user messages get delivered
|
|
132
|
+
* into the new run's first wait and the loop re-answers them.
|
|
133
|
+
*
|
|
134
|
+
* Seeds both cursors: `setLastSeqNum` controls the SSE `Last-Event-ID`,
|
|
135
|
+
* `setLastDispatchedSeqNum` gates waiter dispatch — seeding only the
|
|
136
|
+
* former still re-delivers records the manager buffered before the seed.
|
|
137
|
+
*
|
|
138
|
+
* No-ops on fresh boots and when a cursor is already seeded (e.g. the
|
|
139
|
+
* `chatCustomAgent` wrapper ran before a nested `createChatSession`).
|
|
140
|
+
* @internal
|
|
141
|
+
*/
|
|
142
|
+
async function seedSessionInResumeCursorForCustomLoop(payload) {
|
|
143
|
+
if (v3_1.sessionStreams.lastSeqNum(payload.chatId, "in") !== undefined)
|
|
144
|
+
return;
|
|
145
|
+
// No continuation/attempt gate: the wire may omit `continuation` on a
|
|
146
|
+
// run that still has prior turns (chat.agent covers that case via its
|
|
147
|
+
// snapshot). The scan doubles as the prior-state probe — a fresh
|
|
148
|
+
// session has no turn-complete on `.out`, returns no cursor, and
|
|
149
|
+
// seeds nothing. Cost on fresh boots is one non-blocking records read.
|
|
150
|
+
try {
|
|
151
|
+
const cursor = await findLatestSessionInCursor(payload.chatId);
|
|
152
|
+
if (cursor !== undefined) {
|
|
153
|
+
v3_1.sessionStreams.setLastSeqNum(payload.chatId, "in", cursor);
|
|
154
|
+
v3_1.sessionStreams.setLastDispatchedSeqNum(payload.chatId, "in", cursor);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
v3_1.logger.warn("chat session: session.in resume cursor lookup failed; old messages may replay", { error: error instanceof Error ? error.message : String(error) });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
120
161
|
let readChatSnapshotImpl;
|
|
121
162
|
function __setReadChatSnapshotImplForTests(impl) {
|
|
122
163
|
readChatSnapshotImpl = impl;
|
|
@@ -668,6 +709,16 @@ function createTaskToolExecuteHandler(task) {
|
|
|
668
709
|
toolMeta.continuation = chatCtx.continuation;
|
|
669
710
|
toolMeta.clientData = chatCtx.clientData;
|
|
670
711
|
}
|
|
712
|
+
else {
|
|
713
|
+
// Hand-rolled chat.customAgent loops never set per-turn context, but
|
|
714
|
+
// the wrapper records the boot payload's external chatId at run boot
|
|
715
|
+
// — thread it so subtask chat helpers (`chat.stream.writer` with
|
|
716
|
+
// target "root") can open the parent's session.
|
|
717
|
+
const chatExternalId = locals_js_1.locals.get(chatExternalIdKey);
|
|
718
|
+
if (chatExternalId) {
|
|
719
|
+
toolMeta.chatId = chatExternalId;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
671
722
|
const chatLocals = {};
|
|
672
723
|
for (const entry of chatLocalRegistry) {
|
|
673
724
|
const value = locals_js_1.locals.get(entry.key);
|
|
@@ -1200,6 +1251,36 @@ const handoverInput = {
|
|
|
1200
1251
|
}
|
|
1201
1252
|
},
|
|
1202
1253
|
};
|
|
1254
|
+
/**
|
|
1255
|
+
* Wait for a `chat.headStart` handover signal inside a custom-agent loop or
|
|
1256
|
+
* `chat.createSession`. Returns:
|
|
1257
|
+
* - `null` — this run is not a `handover-prepare` boot, or the wait idled out /
|
|
1258
|
+
* the warm handler crashed before signaling. Treat as "no handover".
|
|
1259
|
+
* - `{ kind: "handover-skip" }` — the warm handler aborted; exit without a turn.
|
|
1260
|
+
* - `{ kind: "handover", partialAssistantMessage, messageId?, isFinal }` — splice
|
|
1261
|
+
* the partial (`chat.MessageAccumulator.applyHandover`) and, when `isFinal` is
|
|
1262
|
+
* false, fall through to `streamText` to run the handed-over tool round.
|
|
1263
|
+
*
|
|
1264
|
+
* For the common case prefer `accumulator.consumeHandover()`, which also seeds
|
|
1265
|
+
* `payload.headStartMessages` and applies the partial for you.
|
|
1266
|
+
*
|
|
1267
|
+
* Must be called at turn 0 before any `chat.messages.waitWithIdleTimeout` —
|
|
1268
|
+
* that facade consumes and discards non-message chunks, which would swallow the
|
|
1269
|
+
* handover signal.
|
|
1270
|
+
*/
|
|
1271
|
+
async function waitForHandover(options) {
|
|
1272
|
+
if (options.payload.trigger !== "handover-prepare")
|
|
1273
|
+
return null;
|
|
1274
|
+
const result = await handoverInput.waitWithIdleTimeout({
|
|
1275
|
+
idleTimeoutInSeconds: options.idleTimeoutInSeconds ?? options.payload.idleTimeoutInSeconds ?? 60,
|
|
1276
|
+
timeout: options.timeout,
|
|
1277
|
+
spanName: options.spanName ?? "waiting for handover signal",
|
|
1278
|
+
});
|
|
1279
|
+
// Non-ok = idle timeout or the warm handler crashed without signaling.
|
|
1280
|
+
if (!result.ok)
|
|
1281
|
+
return null;
|
|
1282
|
+
return result.output;
|
|
1283
|
+
}
|
|
1203
1284
|
/**
|
|
1204
1285
|
* Per-turn deferred promises. Registered via `chat.defer()`, awaited
|
|
1205
1286
|
* before `onTurnComplete` fires. Reset each turn.
|
|
@@ -1296,6 +1377,27 @@ function synthesizeHandoverUIMessage(partial, messageId) {
|
|
|
1296
1377
|
parts,
|
|
1297
1378
|
};
|
|
1298
1379
|
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Splice a head-start handover partial into an accumulating message pair
|
|
1382
|
+
* (model + UI). Dedups by `messageId` against the UI chain (so a hydrated
|
|
1383
|
+
* history that already persisted the partial isn't doubled), then pushes the
|
|
1384
|
+
* partial into `modelMessages` and the synthesized UIMessage into `uiMessages`.
|
|
1385
|
+
* Shared by the `chat.agent` turn-0 splice and `ChatMessageAccumulator.applyHandover`.
|
|
1386
|
+
* @internal
|
|
1387
|
+
*/
|
|
1388
|
+
function spliceHandoverPartial(modelMessages, uiMessages, signal) {
|
|
1389
|
+
if (!signal.partialAssistantMessage || signal.partialAssistantMessage.length === 0) {
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
// Skip if the hydrated chain already persisted the partial under this id.
|
|
1393
|
+
const alreadyInChain = signal.messageId !== undefined && uiMessages.some((m) => m.id === signal.messageId);
|
|
1394
|
+
if (alreadyInChain)
|
|
1395
|
+
return;
|
|
1396
|
+
modelMessages.push(...signal.partialAssistantMessage);
|
|
1397
|
+
const partialUI = synthesizeHandoverUIMessage(signal.partialAssistantMessage, signal.messageId);
|
|
1398
|
+
if (partialUI)
|
|
1399
|
+
uiMessages.push(partialUI);
|
|
1400
|
+
}
|
|
1299
1401
|
/**
|
|
1300
1402
|
* Per-turn background context queue. Messages added via `chat.backgroundWork.inject()`
|
|
1301
1403
|
* are drained at the next `prepareStep` boundary and appended to the model messages.
|
|
@@ -2250,11 +2352,18 @@ function isCompactionSafe(messages) {
|
|
|
2250
2352
|
}
|
|
2251
2353
|
/** @internal */
|
|
2252
2354
|
const chatPromptKey = locals_js_1.locals.create("chat.prompt");
|
|
2355
|
+
/**
|
|
2356
|
+
* @internal Provider options attached to the system message that
|
|
2357
|
+
* `toStreamTextOptions()` builds from the stored prompt — lets a provider cache
|
|
2358
|
+
* the system block. Stored separately so it works for both the `ResolvedPrompt`
|
|
2359
|
+
* and plain-string forms without mutating the prompt object.
|
|
2360
|
+
*/
|
|
2361
|
+
const chatPromptProviderOptionsKey = locals_js_1.locals.create("chat.prompt.providerOptions");
|
|
2253
2362
|
/**
|
|
2254
2363
|
* Store a resolved prompt (or plain string) for the current run.
|
|
2255
2364
|
* Call from any hook (`onPreload`, `onChatStart`, `onTurnStart`) or `run()`.
|
|
2256
2365
|
*/
|
|
2257
|
-
function setChatPrompt(resolved) {
|
|
2366
|
+
function setChatPrompt(resolved, options) {
|
|
2258
2367
|
if (typeof resolved === "string") {
|
|
2259
2368
|
locals_js_1.locals.set(chatPromptKey, {
|
|
2260
2369
|
text: resolved,
|
|
@@ -2271,6 +2380,9 @@ function setChatPrompt(resolved) {
|
|
|
2271
2380
|
else {
|
|
2272
2381
|
locals_js_1.locals.set(chatPromptKey, resolved);
|
|
2273
2382
|
}
|
|
2383
|
+
// Always overwrite the slot (even with undefined) so a later prompt.set with
|
|
2384
|
+
// no options clears a previous prompt's cache opt-in rather than leaking it.
|
|
2385
|
+
locals_js_1.locals.set(chatPromptProviderOptionsKey, options?.providerOptions);
|
|
2274
2386
|
}
|
|
2275
2387
|
/**
|
|
2276
2388
|
* Read the stored prompt. Throws if `chat.prompt.set()` has not been called.
|
|
@@ -2436,7 +2548,21 @@ function toStreamTextOptions(options) {
|
|
|
2436
2548
|
const promptText = prompt?.text ?? "";
|
|
2437
2549
|
const skillsText = skills && skills.length > 0 ? buildSkillsSystemPrompt(skills) : "";
|
|
2438
2550
|
if (promptText || skillsText) {
|
|
2439
|
-
|
|
2551
|
+
const systemText = [promptText, skillsText].filter(Boolean).join("\n\n");
|
|
2552
|
+
// Resolve system-prompt provider options for caching. Precedence (most
|
|
2553
|
+
// specific wins, no deep merge): explicit `systemProviderOptions` →
|
|
2554
|
+
// `cacheControl` sugar → `providerOptions` stored on `chat.prompt.set()`.
|
|
2555
|
+
const systemProviderOptions = options?.systemProviderOptions ??
|
|
2556
|
+
(options?.cacheControl
|
|
2557
|
+
? { anthropic: { cacheControl: options.cacheControl } }
|
|
2558
|
+
: undefined) ??
|
|
2559
|
+
locals_js_1.locals.get(chatPromptProviderOptionsKey);
|
|
2560
|
+
// A bare string stays a bare string (the unchanged default). With provider
|
|
2561
|
+
// options, emit a structured `SystemModelMessage` so the provider can cache
|
|
2562
|
+
// the system block — `streamText`'s `system` accepts string | message.
|
|
2563
|
+
result.system = systemProviderOptions
|
|
2564
|
+
? { role: "system", content: systemText, providerOptions: systemProviderOptions }
|
|
2565
|
+
: systemText;
|
|
2440
2566
|
}
|
|
2441
2567
|
// Prompt-related options (only if chat.prompt.set() was called)
|
|
2442
2568
|
if (prompt) {
|
|
@@ -2618,6 +2744,7 @@ function chatCustomAgent(options) {
|
|
|
2618
2744
|
// `chat.createStartSessionAction`) before this run is triggered.
|
|
2619
2745
|
// No client-side upsert needed.
|
|
2620
2746
|
locals_js_1.locals.set(chatSessionHandleKey, sessions_js_1.sessions.open(payload.chatId));
|
|
2747
|
+
locals_js_1.locals.set(chatExternalIdKey, payload.chatId);
|
|
2621
2748
|
locals_js_1.locals.set(chatAgentRunContextKey, runOptions.ctx);
|
|
2622
2749
|
// Initialize the turn-complete trim slot so `chat.writeTurnComplete`
|
|
2623
2750
|
// trims `session.out` back to the previous turn boundary. Without
|
|
@@ -2627,6 +2754,10 @@ function chatCustomAgent(options) {
|
|
|
2627
2754
|
(0, streams_js_1.markChatAgentRunForStreamsWarning)();
|
|
2628
2755
|
v3_1.taskContext.setConversationId(payload.chatId);
|
|
2629
2756
|
stampConversationIdOnActiveSpan(payload.chatId);
|
|
2757
|
+
// Seed the `.in` resume cursor before user code attaches any `.in`
|
|
2758
|
+
// listener — otherwise a continuation boot replays already-answered
|
|
2759
|
+
// messages into the loop's first wait.
|
|
2760
|
+
await seedSessionInResumeCursorForCustomLoop(payload);
|
|
2630
2761
|
return userRun(payload, runOptions);
|
|
2631
2762
|
},
|
|
2632
2763
|
});
|
|
@@ -2673,6 +2804,7 @@ function chatAgent(options) {
|
|
|
2673
2804
|
// `chat.createStartSessionAction` or browser-direct) before this
|
|
2674
2805
|
// run is triggered — no client-side upsert needed here.
|
|
2675
2806
|
locals_js_1.locals.set(chatSessionHandleKey, sessions_js_1.sessions.open(payload.chatId));
|
|
2807
|
+
locals_js_1.locals.set(chatExternalIdKey, payload.chatId);
|
|
2676
2808
|
// Mutable holder; advances in `writeTurnCompleteChunk` after each turn
|
|
2677
2809
|
// and is the trim target for the NEXT turn's trim record.
|
|
2678
2810
|
locals_js_1.locals.set(lastTurnCompleteSeqNumKey, { value: undefined });
|
|
@@ -3882,18 +4014,10 @@ function chatAgent(options) {
|
|
|
3882
4014
|
// `UIMessageStreamError: No tool invocation found`.
|
|
3883
4015
|
const pendingHandoverPartial = locals_js_1.locals.get(chatHandoverPartialKey);
|
|
3884
4016
|
if (pendingHandoverPartial && pendingHandoverPartial.length > 0) {
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
accumulatedUIMessages.some((m) => m.id === handoverMessageId);
|
|
3890
|
-
if (!alreadyInChain) {
|
|
3891
|
-
accumulatedMessages.push(...pendingHandoverPartial);
|
|
3892
|
-
const partialUI = synthesizeHandoverUIMessage(pendingHandoverPartial, handoverMessageId);
|
|
3893
|
-
if (partialUI) {
|
|
3894
|
-
accumulatedUIMessages.push(partialUI);
|
|
3895
|
-
}
|
|
3896
|
-
}
|
|
4017
|
+
spliceHandoverPartial(accumulatedMessages, accumulatedUIMessages, {
|
|
4018
|
+
partialAssistantMessage: pendingHandoverPartial,
|
|
4019
|
+
messageId: locals_js_1.locals.get(chatHandoverMessageIdKey),
|
|
4020
|
+
});
|
|
3897
4021
|
locals_js_1.locals.set(chatHandoverPartialKey, []); // consume once
|
|
3898
4022
|
}
|
|
3899
4023
|
}
|
|
@@ -5453,8 +5577,19 @@ async function pipeChatAndCapture(source, options) {
|
|
|
5453
5577
|
const onFinishPromise = new Promise((r) => {
|
|
5454
5578
|
resolveOnFinish = r;
|
|
5455
5579
|
});
|
|
5580
|
+
const resolvedOptions = resolveUIMessageStreamOptions();
|
|
5456
5581
|
const uiStream = source.toUIMessageStream({
|
|
5457
|
-
...
|
|
5582
|
+
...resolvedOptions,
|
|
5583
|
+
// Thread the prior chain (incl. a spliced handover partial) so a resumed
|
|
5584
|
+
// tool round's tool-output chunks merge into the originating tool-call
|
|
5585
|
+
// instead of throwing "No tool invocation found".
|
|
5586
|
+
...(options?.originalMessages ? { originalMessages: options.originalMessages } : {}),
|
|
5587
|
+
// Stamp a server-generated id on the start chunk, same as chat.agent's
|
|
5588
|
+
// pipe. Without it the AI SDK regenerates the assistant id when a
|
|
5589
|
+
// prepareStep injection (steering) starts a new step mid-stream, and
|
|
5590
|
+
// the frontend replaces the partial message — wiping the
|
|
5591
|
+
// pre-injection text from the UI and the captured response.
|
|
5592
|
+
generateMessageId: resolvedOptions.generateMessageId ?? ai_runtime_js_1.generateId,
|
|
5458
5593
|
onFinish: ({ responseMessage }) => {
|
|
5459
5594
|
captured = responseMessage;
|
|
5460
5595
|
resolveOnFinish();
|
|
@@ -5524,10 +5659,65 @@ class ChatMessageAccumulator {
|
|
|
5524
5659
|
this.uiMessages = [...uiMessages];
|
|
5525
5660
|
this.modelMessages = await toModelMessages(uiMessages);
|
|
5526
5661
|
}
|
|
5662
|
+
/**
|
|
5663
|
+
* Splice a `chat.headStart` handover partial into the accumulator (the warm
|
|
5664
|
+
* step-1 response). Dedups by `messageId` so a seeded/hydrated history that
|
|
5665
|
+
* already carries the partial isn't doubled. Seed any prior history first
|
|
5666
|
+
* (e.g. `setMessages(payload.headStartMessages)`). Low-level — see
|
|
5667
|
+
* `consumeHandover` for the wait+seed+apply convenience.
|
|
5668
|
+
*/
|
|
5669
|
+
applyHandover(signal) {
|
|
5670
|
+
spliceHandoverPartial(this.modelMessages, this.uiMessages, signal);
|
|
5671
|
+
}
|
|
5672
|
+
/**
|
|
5673
|
+
* One-call `chat.headStart` handover for a custom-agent loop: waits for the
|
|
5674
|
+
* handover signal, seeds prior history from `payload.headStartMessages`,
|
|
5675
|
+
* applies the warm step-1 partial, and reports what to do next.
|
|
5676
|
+
*
|
|
5677
|
+
* Returns `{ isFinal, skipped }`:
|
|
5678
|
+
* - `skipped: true` — not a `handover-prepare` run, the wait idled out, or the
|
|
5679
|
+
* warm handler aborted. Exit the run without a turn.
|
|
5680
|
+
* - `isFinal: true` — step 1 IS the response (pure text). Write turn-complete
|
|
5681
|
+
* and continue; do not call `streamText`.
|
|
5682
|
+
* - `isFinal: false` — fall through to `streamText`, which runs the pending
|
|
5683
|
+
* tool round handed over from step 1.
|
|
5684
|
+
*/
|
|
5685
|
+
async consumeHandover(options) {
|
|
5686
|
+
const signal = await waitForHandover({
|
|
5687
|
+
payload: options.payload,
|
|
5688
|
+
idleTimeoutInSeconds: options.idleTimeoutInSeconds,
|
|
5689
|
+
timeout: options.timeout,
|
|
5690
|
+
});
|
|
5691
|
+
if (!signal || signal.kind === "handover-skip") {
|
|
5692
|
+
return { isFinal: false, skipped: true };
|
|
5693
|
+
}
|
|
5694
|
+
if (options.payload.headStartMessages && options.payload.headStartMessages.length > 0) {
|
|
5695
|
+
await this.setMessages(options.payload.headStartMessages);
|
|
5696
|
+
}
|
|
5697
|
+
this.applyHandover(signal);
|
|
5698
|
+
return { isFinal: signal.isFinal, skipped: false };
|
|
5699
|
+
}
|
|
5527
5700
|
async addResponse(response) {
|
|
5528
5701
|
if (!response.id) {
|
|
5529
5702
|
response = { ...response, id: (0, ai_runtime_js_1.generateId)() };
|
|
5530
5703
|
}
|
|
5704
|
+
// Tool-approval and handover-resume continuations reuse the trailing
|
|
5705
|
+
// assistant's ID (via originalMessages on the pipe), so the captured
|
|
5706
|
+
// response can carry the same ID as a message already in the chain
|
|
5707
|
+
// (e.g. a spliced handover partial). Replace in place instead of pushing
|
|
5708
|
+
// a duplicate, mirroring the chat.agent accumulator.
|
|
5709
|
+
const existingIdx = this.uiMessages.findIndex((m) => m.id === response.id);
|
|
5710
|
+
if (existingIdx !== -1) {
|
|
5711
|
+
this.uiMessages[existingIdx] = response;
|
|
5712
|
+
try {
|
|
5713
|
+
// Reconvert all model messages since we replaced rather than appended.
|
|
5714
|
+
this.modelMessages = await toModelMessages(this.uiMessages.map((m) => stripProviderMetadata(m)));
|
|
5715
|
+
}
|
|
5716
|
+
catch {
|
|
5717
|
+
// Conversion failed — leave the existing model messages in place
|
|
5718
|
+
}
|
|
5719
|
+
return;
|
|
5720
|
+
}
|
|
5531
5721
|
this.uiMessages.push(response);
|
|
5532
5722
|
try {
|
|
5533
5723
|
const msgs = await toModelMessages([stripProviderMetadata(response)]);
|
|
@@ -5660,14 +5850,18 @@ class ChatMessageAccumulator {
|
|
|
5660
5850
|
* signaling, and idle/suspend between turns. You control: initialization,
|
|
5661
5851
|
* model/tool selection, persistence, and any custom per-turn logic.
|
|
5662
5852
|
*
|
|
5853
|
+
* Call from inside a `chat.customAgent()` run — the wrapper binds the
|
|
5854
|
+
* backing Session that the iterator's stop signal and message channels
|
|
5855
|
+
* resolve to. (A plain `task()` does not bind it, so `createSession`
|
|
5856
|
+
* would throw "session handle is not initialized".)
|
|
5857
|
+
*
|
|
5663
5858
|
* @example
|
|
5664
5859
|
* ```ts
|
|
5665
|
-
* import { task } from "@trigger.dev/sdk";
|
|
5666
5860
|
* import { chat, type ChatTaskWirePayload } from "@trigger.dev/sdk/ai";
|
|
5667
5861
|
* import { streamText } from "ai";
|
|
5668
5862
|
* import { openai } from "@ai-sdk/openai";
|
|
5669
5863
|
*
|
|
5670
|
-
* export const myChat =
|
|
5864
|
+
* export const myChat = chat.customAgent({
|
|
5671
5865
|
* id: "my-chat",
|
|
5672
5866
|
* run: async (payload: ChatTaskWirePayload, { signal }) => {
|
|
5673
5867
|
* const session = chat.createSession(payload, { signal });
|
|
@@ -5691,13 +5885,46 @@ function createChatSession(payload, options) {
|
|
|
5691
5885
|
[Symbol.asyncIterator]() {
|
|
5692
5886
|
let currentPayload = payload;
|
|
5693
5887
|
let turn = -1;
|
|
5694
|
-
|
|
5888
|
+
// Created on the first next() call, AFTER the resume-cursor seed —
|
|
5889
|
+
// createStopSignal attaches the `.in` SSE tail, and attaching
|
|
5890
|
+
// before the seed replays every record from seq 0 (the seed is a
|
|
5891
|
+
// no-op when the chatCustomAgent wrapper already ran it).
|
|
5892
|
+
let stop;
|
|
5893
|
+
let booted = false;
|
|
5695
5894
|
const accumulator = new ChatMessageAccumulator();
|
|
5696
5895
|
let previousTurnUsage;
|
|
5697
5896
|
let cumulativeUsage = emptyUsage();
|
|
5698
5897
|
return {
|
|
5699
5898
|
async next() {
|
|
5899
|
+
if (!booted) {
|
|
5900
|
+
booted = true;
|
|
5901
|
+
await seedSessionInResumeCursorForCustomLoop(currentPayload);
|
|
5902
|
+
stop = createStopSignal();
|
|
5903
|
+
}
|
|
5700
5904
|
turn++;
|
|
5905
|
+
// Head-start handover: the server triggered this run with
|
|
5906
|
+
// `trigger: "handover-prepare"` and signals the warm step-1 partial on
|
|
5907
|
+
// `session.in`. Wait for it BEFORE any `messagesInput.waitWithIdleTimeout`
|
|
5908
|
+
// (that facade consumes-and-discards non-message chunks and would swallow
|
|
5909
|
+
// the signal). Turn-0 only — continuation boots never carry this trigger.
|
|
5910
|
+
let handoverThisTurn = null;
|
|
5911
|
+
let pendingHandoverSignal = null;
|
|
5912
|
+
if (turn === 0 && currentPayload.trigger === "handover-prepare") {
|
|
5913
|
+
const signal = await waitForHandover({
|
|
5914
|
+
payload: currentPayload,
|
|
5915
|
+
idleTimeoutInSeconds: sessionIdleTimeoutOpt ?? currentPayload.idleTimeoutInSeconds ?? idleTimeoutInSeconds,
|
|
5916
|
+
timeout,
|
|
5917
|
+
});
|
|
5918
|
+
if (!signal || signal.kind === "handover-skip" || runSignal.aborted) {
|
|
5919
|
+
stop.cleanup();
|
|
5920
|
+
return { done: true, value: undefined };
|
|
5921
|
+
}
|
|
5922
|
+
pendingHandoverSignal = signal;
|
|
5923
|
+
handoverThisTurn = { isFinal: signal.isFinal };
|
|
5924
|
+
// Rewrite to a normal first-turn message turn so the rest of the loop
|
|
5925
|
+
// (steering setup, addIncoming, turnObj) runs unchanged.
|
|
5926
|
+
currentPayload = { ...currentPayload, trigger: "submit-message", message: undefined };
|
|
5927
|
+
}
|
|
5701
5928
|
// First turn: wait when the boot payload carries no message.
|
|
5702
5929
|
// Preload boots wait for the first real message; continuation
|
|
5703
5930
|
// boots (fresh run via `ensureRunForSession` / end-and-continue)
|
|
@@ -5804,6 +6031,16 @@ function createChatSession(payload, options) {
|
|
|
5804
6031
|
? [currentPayload.message]
|
|
5805
6032
|
: [];
|
|
5806
6033
|
const messages = await accumulator.addIncoming(incomingForAccumulator, currentPayload.trigger, turn);
|
|
6034
|
+
// Apply the head-start handover AFTER addIncoming — turn-0 addIncoming
|
|
6035
|
+
// replaces accumulator state, which would wipe a pre-applied splice.
|
|
6036
|
+
// Seed prior history first, then splice the warm step-1 partial.
|
|
6037
|
+
if (pendingHandoverSignal) {
|
|
6038
|
+
const priorHistory = currentPayload.headStartMessages;
|
|
6039
|
+
if (priorHistory && priorHistory.length > 0) {
|
|
6040
|
+
await accumulator.setMessages(priorHistory);
|
|
6041
|
+
}
|
|
6042
|
+
accumulator.applyHandover(pendingHandoverSignal);
|
|
6043
|
+
}
|
|
5807
6044
|
// chat.requestUpgrade() called before this turn — signal transport and exit
|
|
5808
6045
|
if (locals_js_1.locals.get(chatUpgradeRequestedKey)) {
|
|
5809
6046
|
await writeUpgradeRequiredChunk();
|
|
@@ -5830,13 +6067,38 @@ function createChatSession(payload, options) {
|
|
|
5830
6067
|
continuation: currentPayload.continuation ?? false,
|
|
5831
6068
|
previousTurnUsage,
|
|
5832
6069
|
totalUsage: cumulativeUsage,
|
|
6070
|
+
handover: handoverThisTurn,
|
|
5833
6071
|
async setMessages(uiMessages) {
|
|
5834
6072
|
await accumulator.setMessages(uiMessages);
|
|
5835
6073
|
},
|
|
5836
6074
|
async complete(source) {
|
|
6075
|
+
// Head-start final turn: the warm step-1 partial is already spliced
|
|
6076
|
+
// into the accumulator and IS the response — nothing to pipe. Only
|
|
6077
|
+
// valid on a final handover; a missing source on any other turn is a
|
|
6078
|
+
// mistake (it would silently finalize without an assistant response).
|
|
6079
|
+
if (!source) {
|
|
6080
|
+
if (!handoverThisTurn?.isFinal) {
|
|
6081
|
+
throw new Error("turn.complete() requires a stream source unless turn.handover.isFinal is true");
|
|
6082
|
+
}
|
|
6083
|
+
const response = accumulator.uiMessages.at(-1);
|
|
6084
|
+
if (!response || response.role !== "assistant") {
|
|
6085
|
+
throw new Error("turn.complete() could not find the spliced handover response");
|
|
6086
|
+
}
|
|
6087
|
+
sessionMsgSub.off();
|
|
6088
|
+
await chatWriteTurnComplete();
|
|
6089
|
+
return response;
|
|
6090
|
+
}
|
|
5837
6091
|
let response;
|
|
5838
6092
|
try {
|
|
5839
|
-
response = await pipeChatAndCapture(source, {
|
|
6093
|
+
response = await pipeChatAndCapture(source, {
|
|
6094
|
+
signal: combinedSignal,
|
|
6095
|
+
// On a non-final handover turn, thread the spliced partial so a
|
|
6096
|
+
// resumed tool round's tool-output chunks merge into the
|
|
6097
|
+
// handed-over tool-call. Gated on the handover turn only — a
|
|
6098
|
+
// normal turn must not pass originalMessages (it would merge the
|
|
6099
|
+
// fresh response into the prior assistant message).
|
|
6100
|
+
...(handoverThisTurn ? { originalMessages: accumulator.uiMessages } : {}),
|
|
6101
|
+
});
|
|
5840
6102
|
}
|
|
5841
6103
|
catch (error) {
|
|
5842
6104
|
if (error instanceof Error && error.name === "AbortError") {
|
|
@@ -5991,7 +6253,8 @@ function createChatSession(payload, options) {
|
|
|
5991
6253
|
return { done: false, value: turnObj };
|
|
5992
6254
|
},
|
|
5993
6255
|
async return() {
|
|
5994
|
-
stop
|
|
6256
|
+
// `stop` only exists once next() has booted the iterator.
|
|
6257
|
+
stop?.cleanup();
|
|
5995
6258
|
return { done: true, value: undefined };
|
|
5996
6259
|
},
|
|
5997
6260
|
};
|
|
@@ -6234,8 +6497,8 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
6234
6497
|
// run-list filter by chat works without the customer having to wire it
|
|
6235
6498
|
// up. Mirrors the browser-mediated `TriggerChatTransport.doStart` path.
|
|
6236
6499
|
const userTags = params.triggerConfig?.tags ?? options?.triggerConfig?.tags ?? [];
|
|
6237
|
-
//
|
|
6238
|
-
const tags = [`chat:${params.chatId}`, ...userTags].slice(0,
|
|
6500
|
+
// SessionTriggerConfig.tags allows at most 5; the auto chat tag takes one slot.
|
|
6501
|
+
const tags = [`chat:${params.chatId}`, ...userTags].slice(0, 5);
|
|
6239
6502
|
const clientDataMetadata = params.clientData !== undefined ? { metadata: params.clientData } : {};
|
|
6240
6503
|
const triggerConfig = {
|
|
6241
6504
|
basePayload: {
|
|
@@ -6259,6 +6522,20 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
6259
6522
|
maxAttempts: params.triggerConfig?.maxAttempts ?? options?.triggerConfig?.maxAttempts,
|
|
6260
6523
|
}
|
|
6261
6524
|
: {}),
|
|
6525
|
+
...(options?.triggerConfig?.maxDuration !== undefined ||
|
|
6526
|
+
params.triggerConfig?.maxDuration !== undefined
|
|
6527
|
+
? {
|
|
6528
|
+
maxDuration: params.triggerConfig?.maxDuration ?? options?.triggerConfig?.maxDuration,
|
|
6529
|
+
}
|
|
6530
|
+
: {}),
|
|
6531
|
+
...(options?.triggerConfig?.region || params.triggerConfig?.region
|
|
6532
|
+
? { region: params.triggerConfig?.region ?? options?.triggerConfig?.region }
|
|
6533
|
+
: {}),
|
|
6534
|
+
...(options?.triggerConfig?.lockToVersion || params.triggerConfig?.lockToVersion
|
|
6535
|
+
? {
|
|
6536
|
+
lockToVersion: params.triggerConfig?.lockToVersion ?? options?.triggerConfig?.lockToVersion,
|
|
6537
|
+
}
|
|
6538
|
+
: {}),
|
|
6262
6539
|
...(options?.triggerConfig?.idleTimeoutInSeconds !== undefined ||
|
|
6263
6540
|
params.triggerConfig?.idleTimeoutInSeconds !== undefined
|
|
6264
6541
|
? {
|
|
@@ -6437,10 +6714,20 @@ exports.chat = {
|
|
|
6437
6714
|
MessageAccumulator: ChatMessageAccumulator,
|
|
6438
6715
|
/** Create a chat session (async iterator). See {@link createChatSession}. */
|
|
6439
6716
|
createSession: createChatSession,
|
|
6717
|
+
/**
|
|
6718
|
+
* Wait for a `chat.headStart` handover signal inside a `chat.customAgent`
|
|
6719
|
+
* loop (turn 0). See {@link waitForHandover}. For most loops prefer the
|
|
6720
|
+
* `chat.MessageAccumulator.consumeHandover()` convenience, which also seeds
|
|
6721
|
+
* `payload.headStartMessages` and applies the partial.
|
|
6722
|
+
*/
|
|
6723
|
+
waitForHandover,
|
|
6440
6724
|
/**
|
|
6441
6725
|
* Store and retrieve a resolved prompt for the current run.
|
|
6442
6726
|
*
|
|
6443
6727
|
* - `chat.prompt.set(resolved)` — store a `ResolvedPrompt` or plain string
|
|
6728
|
+
* - `chat.prompt.set(resolved, { providerOptions })` — also attach provider
|
|
6729
|
+
* options to the system block so a provider can cache it (e.g. Anthropic
|
|
6730
|
+
* prompt caching). See the prompt-caching guide.
|
|
6444
6731
|
* - `chat.prompt()` — read the stored prompt (throws if not set)
|
|
6445
6732
|
*/
|
|
6446
6733
|
prompt: Object.assign(getChatPrompt, { set: setChatPrompt }),
|