@trigger.dev/sdk 4.5.0-rc.6 → 4.5.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/ai-shared.js +1 -4
- package/dist/commonjs/v3/ai-shared.js.map +1 -1
- package/dist/commonjs/v3/ai.d.ts +178 -6
- package/dist/commonjs/v3/ai.js +417 -64
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/chat-client.js +18 -14
- package/dist/commonjs/v3/chat-client.js.map +1 -1
- package/dist/commonjs/v3/chat-react.js +1 -3
- package/dist/commonjs/v3/chat-react.js.map +1 -1
- package/dist/commonjs/v3/chat-server.d.ts +76 -0
- package/dist/commonjs/v3/chat-server.js +194 -24
- package/dist/commonjs/v3/chat-server.js.map +1 -1
- package/dist/commonjs/v3/chat-server.test.js +201 -6
- package/dist/commonjs/v3/chat-server.test.js.map +1 -1
- package/dist/commonjs/v3/chat.js +4 -3
- package/dist/commonjs/v3/chat.js.map +1 -1
- package/dist/commonjs/v3/chat.test.js +6 -18
- package/dist/commonjs/v3/chat.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/idempotencyKeys.js.map +1 -1
- package/dist/commonjs/v3/prompt.js +2 -6
- package/dist/commonjs/v3/prompt.js.map +1 -1
- package/dist/commonjs/v3/promptManagement.js.map +1 -1
- package/dist/commonjs/v3/retry.js.map +1 -1
- package/dist/commonjs/v3/runs.d.ts +1 -1
- package/dist/commonjs/v3/sessions.d.ts +3 -2
- package/dist/commonjs/v3/sessions.js +4 -2
- package/dist/commonjs/v3/sessions.js.map +1 -1
- package/dist/commonjs/v3/shared.js.map +1 -1
- package/dist/commonjs/v3/skill.js.map +1 -1
- package/dist/commonjs/v3/streams.js +2 -3
- package/dist/commonjs/v3/streams.js.map +1 -1
- package/dist/commonjs/v3/streams.test.js.map +1 -1
- package/dist/commonjs/v3/test/mock-chat-agent.js +1 -4
- package/dist/commonjs/v3/test/mock-chat-agent.js.map +1 -1
- package/dist/commonjs/v3/test/test-session-handle.js.map +1 -1
- package/dist/commonjs/v3/triggerClient.test.js +1 -1
- package/dist/commonjs/v3/triggerClient.test.js.map +1 -1
- package/dist/commonjs/v3/triggerClient.types.test.js +11 -11
- package/dist/commonjs/v3/triggerClient.types.test.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/ai-shared.js +1 -4
- package/dist/esm/v3/ai-shared.js.map +1 -1
- package/dist/esm/v3/ai.d.ts +178 -6
- package/dist/esm/v3/ai.js +417 -64
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/chat-client.js +18 -14
- package/dist/esm/v3/chat-client.js.map +1 -1
- package/dist/esm/v3/chat-react.js +1 -3
- package/dist/esm/v3/chat-react.js.map +1 -1
- package/dist/esm/v3/chat-server.d.ts +76 -0
- package/dist/esm/v3/chat-server.js +194 -24
- package/dist/esm/v3/chat-server.js.map +1 -1
- package/dist/esm/v3/chat-server.test.js +201 -6
- package/dist/esm/v3/chat-server.test.js.map +1 -1
- package/dist/esm/v3/chat.js +4 -3
- package/dist/esm/v3/chat.js.map +1 -1
- package/dist/esm/v3/chat.test.js +6 -18
- package/dist/esm/v3/chat.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/idempotencyKeys.js +1 -1
- package/dist/esm/v3/idempotencyKeys.js.map +1 -1
- package/dist/esm/v3/prompt.js +2 -6
- package/dist/esm/v3/prompt.js.map +1 -1
- package/dist/esm/v3/promptManagement.js.map +1 -1
- package/dist/esm/v3/retry.js.map +1 -1
- package/dist/esm/v3/sessions.d.ts +3 -2
- package/dist/esm/v3/sessions.js +4 -2
- package/dist/esm/v3/sessions.js.map +1 -1
- package/dist/esm/v3/shared.js.map +1 -1
- package/dist/esm/v3/skill.js.map +1 -1
- package/dist/esm/v3/streams.js +2 -3
- package/dist/esm/v3/streams.js.map +1 -1
- package/dist/esm/v3/streams.test.js.map +1 -1
- package/dist/esm/v3/test/mock-chat-agent.js +4 -7
- package/dist/esm/v3/test/mock-chat-agent.js.map +1 -1
- package/dist/esm/v3/test/test-session-handle.js.map +1 -1
- package/dist/esm/v3/triggerClient.test.js +1 -1
- package/dist/esm/v3/triggerClient.test.js.map +1 -1
- package/dist/esm/v3/triggerClient.types.test.js +11 -11
- package/dist/esm/v3/triggerClient.types.test.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/docs/ai/prompts.mdx +426 -0
- package/docs/ai-chat/actions.mdx +111 -0
- package/docs/ai-chat/anatomy.mdx +67 -0
- package/docs/ai-chat/backend.mdx +813 -0
- package/docs/ai-chat/background-injection.mdx +217 -0
- package/docs/ai-chat/changelog.mdx +958 -0
- package/docs/ai-chat/chat-local.mdx +170 -0
- package/docs/ai-chat/client-protocol.mdx +1077 -0
- package/docs/ai-chat/compaction.mdx +407 -0
- package/docs/ai-chat/custom-agents.mdx +392 -0
- package/docs/ai-chat/error-handling.mdx +411 -0
- package/docs/ai-chat/fast-starts.mdx +754 -0
- package/docs/ai-chat/frontend.mdx +576 -0
- package/docs/ai-chat/how-it-works.mdx +226 -0
- package/docs/ai-chat/lifecycle-hooks.mdx +526 -0
- package/docs/ai-chat/mcp.mdx +97 -0
- package/docs/ai-chat/overview.mdx +86 -0
- package/docs/ai-chat/patterns/branching-conversations.mdx +280 -0
- package/docs/ai-chat/patterns/code-sandbox.mdx +122 -0
- package/docs/ai-chat/patterns/database-persistence.mdx +410 -0
- package/docs/ai-chat/patterns/human-in-the-loop.mdx +279 -0
- package/docs/ai-chat/patterns/large-payloads.mdx +165 -0
- package/docs/ai-chat/patterns/oom-resilience.mdx +116 -0
- package/docs/ai-chat/patterns/persistence-and-replay.mdx +207 -0
- package/docs/ai-chat/patterns/recovery-boot.mdx +226 -0
- package/docs/ai-chat/patterns/skills.mdx +217 -0
- package/docs/ai-chat/patterns/sub-agents.mdx +379 -0
- package/docs/ai-chat/patterns/tool-result-auditing.mdx +144 -0
- package/docs/ai-chat/patterns/trusted-edge-signals.mdx +333 -0
- package/docs/ai-chat/patterns/version-upgrades.mdx +168 -0
- package/docs/ai-chat/pending-messages.mdx +339 -0
- package/docs/ai-chat/prompt-caching.mdx +206 -0
- package/docs/ai-chat/quick-start.mdx +157 -0
- package/docs/ai-chat/reference.mdx +905 -0
- package/docs/ai-chat/server-chat.mdx +259 -0
- package/docs/ai-chat/sessions.mdx +329 -0
- package/docs/ai-chat/testing.mdx +678 -0
- package/docs/ai-chat/tools.mdx +187 -0
- package/docs/ai-chat/types.mdx +238 -0
- package/docs/ai-chat/upgrade-guide.mdx +511 -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-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/dev-branches.mdx +92 -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 +12 -0
- package/docs/hidden-tasks.mdx +56 -0
- package/docs/how-it-works.mdx +454 -0
- package/docs/how-to-reduce-your-spend.mdx +224 -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 +109 -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 +9 -0
- package/docs/troubleshooting-github-issues.mdx +6 -0
- package/docs/troubleshooting-uptime-status.mdx +7 -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/esm/v3/ai.js
CHANGED
|
@@ -49,6 +49,10 @@ const chatTurnContextKey = locals.create("chat.turnContext");
|
|
|
49
49
|
* @internal
|
|
50
50
|
*/
|
|
51
51
|
const chatSessionHandleKey = locals.create("chat.sessionHandle");
|
|
52
|
+
// The external `chatId` from the boot payload — the value `ToolCallExecutionOptions.chatId`
|
|
53
|
+
// is documented to carry. Custom-agent loops never set per-turn context, so subtask tool
|
|
54
|
+
// metadata reads this directly rather than the Session handle id.
|
|
55
|
+
const chatExternalIdKey = locals.create("chat.externalId");
|
|
52
56
|
/**
|
|
53
57
|
* S2 seq_num of the most recent `turn-complete` control record written by
|
|
54
58
|
* this worker. Read by `writeTurnCompleteChunk` to know what to trim back
|
|
@@ -103,6 +107,45 @@ async function findLatestSessionInCursor(chatId) {
|
|
|
103
107
|
export async function __findLatestSessionInCursorForTests(chatId) {
|
|
104
108
|
return findLatestSessionInCursor(chatId);
|
|
105
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Seed the `.in` resume cursor for custom-agent loops (`chat.customAgent`
|
|
112
|
+
* raw loops and `chat.createSession`) the way `chat.agent`'s boot does.
|
|
113
|
+
*
|
|
114
|
+
* MUST run before anything attaches a `.in` listener (`createStopSignal`,
|
|
115
|
+
* `chat.messages.on`, the first wait): attaching opens the SSE tail with
|
|
116
|
+
* `Last-Event-ID` from the seeded cursor, so attach-then-seed replays
|
|
117
|
+
* every record from seq 0 — already-answered user messages get delivered
|
|
118
|
+
* into the new run's first wait and the loop re-answers them.
|
|
119
|
+
*
|
|
120
|
+
* Seeds both cursors: `setLastSeqNum` controls the SSE `Last-Event-ID`,
|
|
121
|
+
* `setLastDispatchedSeqNum` gates waiter dispatch — seeding only the
|
|
122
|
+
* former still re-delivers records the manager buffered before the seed.
|
|
123
|
+
*
|
|
124
|
+
* No-ops on fresh boots and when a cursor is already seeded (e.g. the
|
|
125
|
+
* `chatCustomAgent` wrapper ran before a nested `createChatSession`).
|
|
126
|
+
* @internal
|
|
127
|
+
*/
|
|
128
|
+
async function seedSessionInResumeCursorForCustomLoop(payload) {
|
|
129
|
+
if (sessionStreams.lastSeqNum(payload.chatId, "in") !== undefined)
|
|
130
|
+
return;
|
|
131
|
+
// No continuation/attempt gate: the wire may omit `continuation` on a
|
|
132
|
+
// run that still has prior turns (chat.agent covers that case via its
|
|
133
|
+
// snapshot). The scan doubles as the prior-state probe — a fresh
|
|
134
|
+
// session has no turn-complete on `.out`, returns no cursor, and
|
|
135
|
+
// seeds nothing. Cost on fresh boots is one non-blocking records read.
|
|
136
|
+
try {
|
|
137
|
+
const cursor = await findLatestSessionInCursor(payload.chatId);
|
|
138
|
+
if (cursor !== undefined) {
|
|
139
|
+
sessionStreams.setLastSeqNum(payload.chatId, "in", cursor);
|
|
140
|
+
sessionStreams.setLastDispatchedSeqNum(payload.chatId, "in", cursor);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
logger.warn("chat session: session.in resume cursor lookup failed; old messages may replay", {
|
|
145
|
+
error: error instanceof Error ? error.message : String(error),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
106
149
|
let readChatSnapshotImpl;
|
|
107
150
|
export function __setReadChatSnapshotImplForTests(impl) {
|
|
108
151
|
readChatSnapshotImpl = impl;
|
|
@@ -654,6 +697,16 @@ function createTaskToolExecuteHandler(task) {
|
|
|
654
697
|
toolMeta.continuation = chatCtx.continuation;
|
|
655
698
|
toolMeta.clientData = chatCtx.clientData;
|
|
656
699
|
}
|
|
700
|
+
else {
|
|
701
|
+
// Hand-rolled chat.customAgent loops never set per-turn context, but
|
|
702
|
+
// the wrapper records the boot payload's external chatId at run boot
|
|
703
|
+
// — thread it so subtask chat helpers (`chat.stream.writer` with
|
|
704
|
+
// target "root") can open the parent's session.
|
|
705
|
+
const chatExternalId = locals.get(chatExternalIdKey);
|
|
706
|
+
if (chatExternalId) {
|
|
707
|
+
toolMeta.chatId = chatExternalId;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
657
710
|
const chatLocals = {};
|
|
658
711
|
for (const entry of chatLocalRegistry) {
|
|
659
712
|
const value = locals.get(entry.key);
|
|
@@ -1176,8 +1229,7 @@ const handoverInput = {
|
|
|
1176
1229
|
const result = await getChatSession().in.waitWithIdleTimeout(options);
|
|
1177
1230
|
if (!result.ok)
|
|
1178
1231
|
return result;
|
|
1179
|
-
if (result.output.kind === "handover" ||
|
|
1180
|
-
result.output.kind === "handover-skip") {
|
|
1232
|
+
if (result.output.kind === "handover" || result.output.kind === "handover-skip") {
|
|
1181
1233
|
return { ok: true, output: result.output };
|
|
1182
1234
|
}
|
|
1183
1235
|
// Other kinds (message, stop) are not expected during handover-prepare.
|
|
@@ -1186,6 +1238,36 @@ const handoverInput = {
|
|
|
1186
1238
|
}
|
|
1187
1239
|
},
|
|
1188
1240
|
};
|
|
1241
|
+
/**
|
|
1242
|
+
* Wait for a `chat.headStart` handover signal inside a custom-agent loop or
|
|
1243
|
+
* `chat.createSession`. Returns:
|
|
1244
|
+
* - `null` — this run is not a `handover-prepare` boot, or the wait idled out /
|
|
1245
|
+
* the warm handler crashed before signaling. Treat as "no handover".
|
|
1246
|
+
* - `{ kind: "handover-skip" }` — the warm handler aborted; exit without a turn.
|
|
1247
|
+
* - `{ kind: "handover", partialAssistantMessage, messageId?, isFinal }` — splice
|
|
1248
|
+
* the partial (`chat.MessageAccumulator.applyHandover`) and, when `isFinal` is
|
|
1249
|
+
* false, fall through to `streamText` to run the handed-over tool round.
|
|
1250
|
+
*
|
|
1251
|
+
* For the common case prefer `accumulator.consumeHandover()`, which also seeds
|
|
1252
|
+
* `payload.headStartMessages` and applies the partial for you.
|
|
1253
|
+
*
|
|
1254
|
+
* Must be called at turn 0 before any `chat.messages.waitWithIdleTimeout` —
|
|
1255
|
+
* that facade consumes and discards non-message chunks, which would swallow the
|
|
1256
|
+
* handover signal.
|
|
1257
|
+
*/
|
|
1258
|
+
async function waitForHandover(options) {
|
|
1259
|
+
if (options.payload.trigger !== "handover-prepare")
|
|
1260
|
+
return null;
|
|
1261
|
+
const result = await handoverInput.waitWithIdleTimeout({
|
|
1262
|
+
idleTimeoutInSeconds: options.idleTimeoutInSeconds ?? options.payload.idleTimeoutInSeconds ?? 60,
|
|
1263
|
+
timeout: options.timeout,
|
|
1264
|
+
spanName: options.spanName ?? "waiting for handover signal",
|
|
1265
|
+
});
|
|
1266
|
+
// Non-ok = idle timeout or the warm handler crashed without signaling.
|
|
1267
|
+
if (!result.ok)
|
|
1268
|
+
return null;
|
|
1269
|
+
return result.output;
|
|
1270
|
+
}
|
|
1189
1271
|
/**
|
|
1190
1272
|
* Per-turn deferred promises. Registered via `chat.defer()`, awaited
|
|
1191
1273
|
* before `onTurnComplete` fires. Reset each turn.
|
|
@@ -1282,6 +1364,27 @@ function synthesizeHandoverUIMessage(partial, messageId) {
|
|
|
1282
1364
|
parts,
|
|
1283
1365
|
};
|
|
1284
1366
|
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Splice a head-start handover partial into an accumulating message pair
|
|
1369
|
+
* (model + UI). Dedups by `messageId` against the UI chain (so a hydrated
|
|
1370
|
+
* history that already persisted the partial isn't doubled), then pushes the
|
|
1371
|
+
* partial into `modelMessages` and the synthesized UIMessage into `uiMessages`.
|
|
1372
|
+
* Shared by the `chat.agent` turn-0 splice and `ChatMessageAccumulator.applyHandover`.
|
|
1373
|
+
* @internal
|
|
1374
|
+
*/
|
|
1375
|
+
function spliceHandoverPartial(modelMessages, uiMessages, signal) {
|
|
1376
|
+
if (!signal.partialAssistantMessage || signal.partialAssistantMessage.length === 0) {
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
// Skip if the hydrated chain already persisted the partial under this id.
|
|
1380
|
+
const alreadyInChain = signal.messageId !== undefined && uiMessages.some((m) => m.id === signal.messageId);
|
|
1381
|
+
if (alreadyInChain)
|
|
1382
|
+
return;
|
|
1383
|
+
modelMessages.push(...signal.partialAssistantMessage);
|
|
1384
|
+
const partialUI = synthesizeHandoverUIMessage(signal.partialAssistantMessage, signal.messageId);
|
|
1385
|
+
if (partialUI)
|
|
1386
|
+
uiMessages.push(partialUI);
|
|
1387
|
+
}
|
|
1285
1388
|
/**
|
|
1286
1389
|
* Per-turn background context queue. Messages added via `chat.backgroundWork.inject()`
|
|
1287
1390
|
* are drained at the next `prepareStep` boundary and appended to the model messages.
|
|
@@ -1837,6 +1940,55 @@ function isStepBoundarySafe(step) {
|
|
|
1837
1940
|
.map((p) => p.toolCallId));
|
|
1838
1941
|
return ![...callIds].some((id) => !settledIds.has(id));
|
|
1839
1942
|
}
|
|
1943
|
+
/**
|
|
1944
|
+
* True when a model message is a `tool` message carrying a
|
|
1945
|
+
* `tool-approval-response` part — the trailing row a head-start handover
|
|
1946
|
+
* reshapes a pending first-turn tool call into. AI SDK's `collectToolApprovals`
|
|
1947
|
+
* only inspects the conversation's last message, so this row must survive to
|
|
1948
|
+
* `streamText` intact for the agent to execute the handed-over call.
|
|
1949
|
+
* @internal
|
|
1950
|
+
*/
|
|
1951
|
+
function hasToolApprovalResponse(message) {
|
|
1952
|
+
return (message?.role === "tool" &&
|
|
1953
|
+
Array.isArray(message.content) &&
|
|
1954
|
+
message.content.some((part) => part != null &&
|
|
1955
|
+
typeof part === "object" &&
|
|
1956
|
+
part.type === "tool-approval-response"));
|
|
1957
|
+
}
|
|
1958
|
+
/**
|
|
1959
|
+
* Keep a head-start handover's tool-approval tail intact across `prepareMessages`.
|
|
1960
|
+
*
|
|
1961
|
+
* The handover reshapes the warm step-1's pending tool call into AI SDK's
|
|
1962
|
+
* tool-approval round: a `tool-approval-request` on the assistant plus a
|
|
1963
|
+
* trailing `tool` message with `tool-approval-response { approved: true }`. The
|
|
1964
|
+
* agent's next `streamText` runs `collectToolApprovals`, which ONLY looks at the
|
|
1965
|
+
* last message — so that tool row must stay last and unmodified for the pending
|
|
1966
|
+
* call to execute. A user `prepareMessages` hook that rewrites or drops the last
|
|
1967
|
+
* message (e.g. rolling a provider cache breakpoint onto it) silently breaks the
|
|
1968
|
+
* resume: the agent sends a bare `tool_use` and the turn dies with
|
|
1969
|
+
* "tool_use ids were found without tool_result". If the hook's input ended with
|
|
1970
|
+
* that approval tail, re-assert the original tail as the last message.
|
|
1971
|
+
*
|
|
1972
|
+
* No-op for every normal turn — only fires when the input genuinely ended with a
|
|
1973
|
+
* pending tool-approval response (i.e. a head-start resume).
|
|
1974
|
+
* @internal
|
|
1975
|
+
*/
|
|
1976
|
+
function preserveToolApprovalTail(original, prepared) {
|
|
1977
|
+
const originalTail = original[original.length - 1];
|
|
1978
|
+
if (!hasToolApprovalResponse(originalTail))
|
|
1979
|
+
return prepared;
|
|
1980
|
+
// Hook left the exact tail object in place — nothing to do.
|
|
1981
|
+
if (prepared[prepared.length - 1] === originalTail)
|
|
1982
|
+
return prepared;
|
|
1983
|
+
// Otherwise drop only the trailing approval tail the hook produced (the
|
|
1984
|
+
// original moved, or a rewritten copy) and re-append the original so it is
|
|
1985
|
+
// last and intact. Older approval rounds deeper in history must survive.
|
|
1986
|
+
const withoutMovedOriginal = prepared.filter((m) => m !== originalTail);
|
|
1987
|
+
while (hasToolApprovalResponse(withoutMovedOriginal[withoutMovedOriginal.length - 1])) {
|
|
1988
|
+
withoutMovedOriginal.pop();
|
|
1989
|
+
}
|
|
1990
|
+
return [...withoutMovedOriginal, originalTail];
|
|
1991
|
+
}
|
|
1840
1992
|
/**
|
|
1841
1993
|
* Apply the prepareMessages hook if one is set in locals.
|
|
1842
1994
|
* @internal
|
|
@@ -1846,7 +1998,7 @@ async function applyPrepareMessages(messages, reason) {
|
|
|
1846
1998
|
if (!hook)
|
|
1847
1999
|
return messages;
|
|
1848
2000
|
const turnCtx = locals.get(chatTurnContextKey);
|
|
1849
|
-
|
|
2001
|
+
const prepared = await tracer.startActiveSpan("prepareMessages()", async () => {
|
|
1850
2002
|
return hook({
|
|
1851
2003
|
messages,
|
|
1852
2004
|
reason,
|
|
@@ -1862,6 +2014,9 @@ async function applyPrepareMessages(messages, reason) {
|
|
|
1862
2014
|
"chat.prepareMessages.messageCount": messages.length,
|
|
1863
2015
|
},
|
|
1864
2016
|
});
|
|
2017
|
+
// A user hook must never be able to break the head-start handover resume by
|
|
2018
|
+
// disturbing the trailing tool-approval row (see preserveToolApprovalTail).
|
|
2019
|
+
return preserveToolApprovalTail(messages, prepared);
|
|
1865
2020
|
}
|
|
1866
2021
|
/**
|
|
1867
2022
|
* Resolve the `tools` option into a concrete `ToolSet` and cache it in locals so
|
|
@@ -2234,11 +2389,18 @@ function isCompactionSafe(messages) {
|
|
|
2234
2389
|
}
|
|
2235
2390
|
/** @internal */
|
|
2236
2391
|
const chatPromptKey = locals.create("chat.prompt");
|
|
2392
|
+
/**
|
|
2393
|
+
* @internal Provider options attached to the system message that
|
|
2394
|
+
* `toStreamTextOptions()` builds from the stored prompt — lets a provider cache
|
|
2395
|
+
* the system block. Stored separately so it works for both the `ResolvedPrompt`
|
|
2396
|
+
* and plain-string forms without mutating the prompt object.
|
|
2397
|
+
*/
|
|
2398
|
+
const chatPromptProviderOptionsKey = locals.create("chat.prompt.providerOptions");
|
|
2237
2399
|
/**
|
|
2238
2400
|
* Store a resolved prompt (or plain string) for the current run.
|
|
2239
2401
|
* Call from any hook (`onPreload`, `onChatStart`, `onTurnStart`) or `run()`.
|
|
2240
2402
|
*/
|
|
2241
|
-
function setChatPrompt(resolved) {
|
|
2403
|
+
function setChatPrompt(resolved, options) {
|
|
2242
2404
|
if (typeof resolved === "string") {
|
|
2243
2405
|
locals.set(chatPromptKey, {
|
|
2244
2406
|
text: resolved,
|
|
@@ -2255,6 +2417,9 @@ function setChatPrompt(resolved) {
|
|
|
2255
2417
|
else {
|
|
2256
2418
|
locals.set(chatPromptKey, resolved);
|
|
2257
2419
|
}
|
|
2420
|
+
// Always overwrite the slot (even with undefined) so a later prompt.set with
|
|
2421
|
+
// no options clears a previous prompt's cache opt-in rather than leaking it.
|
|
2422
|
+
locals.set(chatPromptProviderOptionsKey, options?.providerOptions);
|
|
2258
2423
|
}
|
|
2259
2424
|
/**
|
|
2260
2425
|
* Read the stored prompt. Throws if `chat.prompt.set()` has not been called.
|
|
@@ -2420,7 +2585,21 @@ function toStreamTextOptions(options) {
|
|
|
2420
2585
|
const promptText = prompt?.text ?? "";
|
|
2421
2586
|
const skillsText = skills && skills.length > 0 ? buildSkillsSystemPrompt(skills) : "";
|
|
2422
2587
|
if (promptText || skillsText) {
|
|
2423
|
-
|
|
2588
|
+
const systemText = [promptText, skillsText].filter(Boolean).join("\n\n");
|
|
2589
|
+
// Resolve system-prompt provider options for caching. Precedence (most
|
|
2590
|
+
// specific wins, no deep merge): explicit `systemProviderOptions` →
|
|
2591
|
+
// `cacheControl` sugar → `providerOptions` stored on `chat.prompt.set()`.
|
|
2592
|
+
const systemProviderOptions = options?.systemProviderOptions ??
|
|
2593
|
+
(options?.cacheControl
|
|
2594
|
+
? { anthropic: { cacheControl: options.cacheControl } }
|
|
2595
|
+
: undefined) ??
|
|
2596
|
+
locals.get(chatPromptProviderOptionsKey);
|
|
2597
|
+
// A bare string stays a bare string (the unchanged default). With provider
|
|
2598
|
+
// options, emit a structured `SystemModelMessage` so the provider can cache
|
|
2599
|
+
// the system block — `streamText`'s `system` accepts string | message.
|
|
2600
|
+
result.system = systemProviderOptions
|
|
2601
|
+
? { role: "system", content: systemText, providerOptions: systemProviderOptions }
|
|
2602
|
+
: systemText;
|
|
2424
2603
|
}
|
|
2425
2604
|
// Prompt-related options (only if chat.prompt.set() was called)
|
|
2426
2605
|
if (prompt) {
|
|
@@ -2602,6 +2781,7 @@ function chatCustomAgent(options) {
|
|
|
2602
2781
|
// `chat.createStartSessionAction`) before this run is triggered.
|
|
2603
2782
|
// No client-side upsert needed.
|
|
2604
2783
|
locals.set(chatSessionHandleKey, sessions.open(payload.chatId));
|
|
2784
|
+
locals.set(chatExternalIdKey, payload.chatId);
|
|
2605
2785
|
locals.set(chatAgentRunContextKey, runOptions.ctx);
|
|
2606
2786
|
// Initialize the turn-complete trim slot so `chat.writeTurnComplete`
|
|
2607
2787
|
// trims `session.out` back to the previous turn boundary. Without
|
|
@@ -2611,6 +2791,10 @@ function chatCustomAgent(options) {
|
|
|
2611
2791
|
markChatAgentRunForStreamsWarning();
|
|
2612
2792
|
taskContext.setConversationId(payload.chatId);
|
|
2613
2793
|
stampConversationIdOnActiveSpan(payload.chatId);
|
|
2794
|
+
// Seed the `.in` resume cursor before user code attaches any `.in`
|
|
2795
|
+
// listener — otherwise a continuation boot replays already-answered
|
|
2796
|
+
// messages into the loop's first wait.
|
|
2797
|
+
await seedSessionInResumeCursorForCustomLoop(payload);
|
|
2614
2798
|
return userRun(payload, runOptions);
|
|
2615
2799
|
},
|
|
2616
2800
|
});
|
|
@@ -2657,6 +2841,7 @@ function chatAgent(options) {
|
|
|
2657
2841
|
// `chat.createStartSessionAction` or browser-direct) before this
|
|
2658
2842
|
// run is triggered — no client-side upsert needed here.
|
|
2659
2843
|
locals.set(chatSessionHandleKey, sessions.open(payload.chatId));
|
|
2844
|
+
locals.set(chatExternalIdKey, payload.chatId);
|
|
2660
2845
|
// Mutable holder; advances in `writeTurnCompleteChunk` after each turn
|
|
2661
2846
|
// and is the trim target for the NEXT turn's trim record.
|
|
2662
2847
|
locals.set(lastTurnCompleteSeqNumKey, { value: undefined });
|
|
@@ -2780,7 +2965,9 @@ function chatAgent(options) {
|
|
|
2780
2965
|
const replayOutPhase = async () => {
|
|
2781
2966
|
const replayOutStart = Date.now();
|
|
2782
2967
|
try {
|
|
2783
|
-
const replayResult = await replaySessionOutTail(sessionIdForSnapshot, {
|
|
2968
|
+
const replayResult = await replaySessionOutTail(sessionIdForSnapshot, {
|
|
2969
|
+
lastEventId: bootSnapshot?.lastOutEventId,
|
|
2970
|
+
});
|
|
2784
2971
|
replayedSettled = replayResult.settled;
|
|
2785
2972
|
replayedPartial = replayResult.partial;
|
|
2786
2973
|
replayedPartialRaw = replayResult.partialRaw;
|
|
@@ -2877,9 +3064,7 @@ function chatAgent(options) {
|
|
|
2877
3064
|
// crashed / was canceled / requested upgrade
|
|
2878
3065
|
// - Snapshot exists at all (catches edge cases where the wire
|
|
2879
3066
|
// didn't set `continuation` but a snapshot indicates prior turns)
|
|
2880
|
-
const needsResumeCursor = ctx.attempt.number > 1 ||
|
|
2881
|
-
payload.continuation === true ||
|
|
2882
|
-
bootSnapshot !== undefined;
|
|
3067
|
+
const needsResumeCursor = ctx.attempt.number > 1 || payload.continuation === true || bootSnapshot !== undefined;
|
|
2883
3068
|
if (needsResumeCursor) {
|
|
2884
3069
|
try {
|
|
2885
3070
|
// Reuse the cursor the boot block already resolved (snapshot
|
|
@@ -3226,9 +3411,7 @@ function chatAgent(options) {
|
|
|
3226
3411
|
// Wait for the first real message — task-level idle settings win over
|
|
3227
3412
|
// `transport.preload(..., { idleTimeoutInSeconds })` / wire payload so
|
|
3228
3413
|
// `chat.agent({ idleTimeoutInSeconds, preloadIdleTimeoutInSeconds })` is authoritative.
|
|
3229
|
-
const effectivePreloadIdleTimeout = preloadIdleTimeoutInSeconds ??
|
|
3230
|
-
idleTimeoutInSeconds ??
|
|
3231
|
-
payload.idleTimeoutInSeconds;
|
|
3414
|
+
const effectivePreloadIdleTimeout = preloadIdleTimeoutInSeconds ?? idleTimeoutInSeconds ?? payload.idleTimeoutInSeconds;
|
|
3232
3415
|
const effectivePreloadTimeout = metadata.get(TURN_TIMEOUT_METADATA_KEY) ??
|
|
3233
3416
|
preloadTimeout ??
|
|
3234
3417
|
turnTimeout;
|
|
@@ -3473,7 +3656,9 @@ function chatAgent(options) {
|
|
|
3473
3656
|
if (lastUserMessage) {
|
|
3474
3657
|
turnAttributes["chat.user_message"] = lastUserMessage;
|
|
3475
3658
|
// Show a truncated preview of the user message as an accessory
|
|
3476
|
-
const preview = lastUserMessage.length > 80
|
|
3659
|
+
const preview = lastUserMessage.length > 80
|
|
3660
|
+
? lastUserMessage.slice(0, 80) + "..."
|
|
3661
|
+
: lastUserMessage;
|
|
3477
3662
|
Object.assign(turnAttributes, accessoryAttributes({
|
|
3478
3663
|
items: [{ text: preview, variant: "normal" }],
|
|
3479
3664
|
style: "codepath",
|
|
@@ -3760,8 +3945,7 @@ function chatAgent(options) {
|
|
|
3760
3945
|
if (currentWirePayload.trigger === "submit-message" &&
|
|
3761
3946
|
cleanedUIMessages.length > 0) {
|
|
3762
3947
|
const lastUI = cleanedUIMessages[cleanedUIMessages.length - 1];
|
|
3763
|
-
const matchedExisting = lastUI.id !== undefined &&
|
|
3764
|
-
previouslyKnownMessageIds.has(lastUI.id);
|
|
3948
|
+
const matchedExisting = lastUI.id !== undefined && previouslyKnownMessageIds.has(lastUI.id);
|
|
3765
3949
|
if (!matchedExisting) {
|
|
3766
3950
|
turnNewUIMessages.push(lastUI);
|
|
3767
3951
|
const lastModel = (await toModelMessages([lastUI]))[0];
|
|
@@ -3866,18 +4050,10 @@ function chatAgent(options) {
|
|
|
3866
4050
|
// `UIMessageStreamError: No tool invocation found`.
|
|
3867
4051
|
const pendingHandoverPartial = locals.get(chatHandoverPartialKey);
|
|
3868
4052
|
if (pendingHandoverPartial && pendingHandoverPartial.length > 0) {
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
accumulatedUIMessages.some((m) => m.id === handoverMessageId);
|
|
3874
|
-
if (!alreadyInChain) {
|
|
3875
|
-
accumulatedMessages.push(...pendingHandoverPartial);
|
|
3876
|
-
const partialUI = synthesizeHandoverUIMessage(pendingHandoverPartial, handoverMessageId);
|
|
3877
|
-
if (partialUI) {
|
|
3878
|
-
accumulatedUIMessages.push(partialUI);
|
|
3879
|
-
}
|
|
3880
|
-
}
|
|
4053
|
+
spliceHandoverPartial(accumulatedMessages, accumulatedUIMessages, {
|
|
4054
|
+
partialAssistantMessage: pendingHandoverPartial,
|
|
4055
|
+
messageId: locals.get(chatHandoverMessageIdKey),
|
|
4056
|
+
});
|
|
3881
4057
|
locals.set(chatHandoverPartialKey, []); // consume once
|
|
3882
4058
|
}
|
|
3883
4059
|
}
|
|
@@ -4104,7 +4280,8 @@ function chatAgent(options) {
|
|
|
4104
4280
|
// We call toUIMessageStream ourselves to attach onFinish for response capture.
|
|
4105
4281
|
// Pass originalMessages so the AI SDK reuses message IDs across turns
|
|
4106
4282
|
// (e.g. for tool approval continuations / HITL flows).
|
|
4107
|
-
if ((locals.get(chatPipeCountKey) ?? 0) === 0 &&
|
|
4283
|
+
if ((locals.get(chatPipeCountKey) ?? 0) === 0 &&
|
|
4284
|
+
isUIMessageStreamable(runResult)) {
|
|
4108
4285
|
onFinishAttached = true;
|
|
4109
4286
|
const resolvedOptions = resolveUIMessageStreamOptions();
|
|
4110
4287
|
// For action turns, don't pass originalMessages: the response
|
|
@@ -4117,9 +4294,7 @@ function chatAgent(options) {
|
|
|
4117
4294
|
// Pass originalMessages so the AI SDK reuses message IDs across
|
|
4118
4295
|
// turns (e.g. for tool approval continuations / HITL flows).
|
|
4119
4296
|
// Omit for action turns to force a fresh response ID.
|
|
4120
|
-
...(isActionTurn
|
|
4121
|
-
? {}
|
|
4122
|
-
: { originalMessages: accumulatedUIMessages }),
|
|
4297
|
+
...(isActionTurn ? {} : { originalMessages: accumulatedUIMessages }),
|
|
4123
4298
|
// Always provide generateMessageId so the start chunk carries a
|
|
4124
4299
|
// messageId. Without this, the frontend and backend generate IDs
|
|
4125
4300
|
// independently and they won't match for ID-based dedup.
|
|
@@ -4130,7 +4305,10 @@ function chatAgent(options) {
|
|
|
4130
4305
|
resolveOnFinish();
|
|
4131
4306
|
},
|
|
4132
4307
|
});
|
|
4133
|
-
await pipeChat(uiStream, {
|
|
4308
|
+
await pipeChat(uiStream, {
|
|
4309
|
+
signal: combinedSignal,
|
|
4310
|
+
spanName: "stream response",
|
|
4311
|
+
});
|
|
4134
4312
|
}
|
|
4135
4313
|
}
|
|
4136
4314
|
catch (error) {
|
|
@@ -4165,7 +4343,8 @@ function chatAgent(options) {
|
|
|
4165
4343
|
// never reports final usage), which would block the turn loop
|
|
4166
4344
|
// from ever firing onTurnComplete / writeTurnComplete.
|
|
4167
4345
|
let turnUsage;
|
|
4168
|
-
if (runResult != null &&
|
|
4346
|
+
if (runResult != null &&
|
|
4347
|
+
typeof runResult.totalUsage?.then === "function") {
|
|
4169
4348
|
try {
|
|
4170
4349
|
turnUsage = (await Promise.race([
|
|
4171
4350
|
runResult.totalUsage,
|
|
@@ -4251,7 +4430,10 @@ function chatAgent(options) {
|
|
|
4251
4430
|
// may produce a message with an empty ID since IDs are normally
|
|
4252
4431
|
// assigned by the frontend's useChat).
|
|
4253
4432
|
if (!capturedResponseMessage.id) {
|
|
4254
|
-
capturedResponseMessage = {
|
|
4433
|
+
capturedResponseMessage = {
|
|
4434
|
+
...capturedResponseMessage,
|
|
4435
|
+
id: generateMessageId(),
|
|
4436
|
+
};
|
|
4255
4437
|
}
|
|
4256
4438
|
// Append any non-transient data parts queued via chat.response or writer.write()
|
|
4257
4439
|
const queuedParts = locals.get(chatResponsePartsKey);
|
|
@@ -4552,7 +4734,9 @@ function chatAgent(options) {
|
|
|
4552
4734
|
? { "gen_ai.usage.total_tokens": turnUsage.totalTokens }
|
|
4553
4735
|
: {}),
|
|
4554
4736
|
...(cumulativeUsage.totalTokens != null
|
|
4555
|
-
? {
|
|
4737
|
+
? {
|
|
4738
|
+
"gen_ai.usage.cumulative_total_tokens": cumulativeUsage.totalTokens,
|
|
4739
|
+
}
|
|
4556
4740
|
: {}),
|
|
4557
4741
|
},
|
|
4558
4742
|
});
|
|
@@ -4584,9 +4768,7 @@ function chatAgent(options) {
|
|
|
4584
4768
|
savedAt: Date.now(),
|
|
4585
4769
|
messages: accumulatedUIMessages,
|
|
4586
4770
|
lastOutEventId: turnCompleteResult?.lastEventId,
|
|
4587
|
-
lastInEventId: snapshotInCursor !== undefined
|
|
4588
|
-
? String(snapshotInCursor)
|
|
4589
|
-
: undefined,
|
|
4771
|
+
lastInEventId: snapshotInCursor !== undefined ? String(snapshotInCursor) : undefined,
|
|
4590
4772
|
});
|
|
4591
4773
|
}, {
|
|
4592
4774
|
attributes: {
|
|
@@ -4628,8 +4810,7 @@ function chatAgent(options) {
|
|
|
4628
4810
|
// chat.requestUpgrade() was called — exit the loop so the
|
|
4629
4811
|
// transport triggers a new run on the latest version.
|
|
4630
4812
|
// chat.endRun() — same exit, no upgrade semantics.
|
|
4631
|
-
if (locals.get(chatUpgradeRequestedKey) ||
|
|
4632
|
-
locals.get(chatEndRunRequestedKey)) {
|
|
4813
|
+
if (locals.get(chatUpgradeRequestedKey) || locals.get(chatEndRunRequestedKey)) {
|
|
4633
4814
|
return "exit";
|
|
4634
4815
|
}
|
|
4635
4816
|
// Wait for the next message — stay idle briefly, then suspend
|
|
@@ -4709,7 +4890,9 @@ function chatAgent(options) {
|
|
|
4709
4890
|
// Turn error handler: write an error chunk + turn-complete to the stream
|
|
4710
4891
|
// so the client sees the error, then wait for the next message instead
|
|
4711
4892
|
// of killing the entire run. This keeps the conversation alive.
|
|
4712
|
-
if (turnError instanceof Error &&
|
|
4893
|
+
if (turnError instanceof Error &&
|
|
4894
|
+
turnError.name === "AbortError" &&
|
|
4895
|
+
runSignal.aborted) {
|
|
4713
4896
|
// Full run cancellation — exit immediately
|
|
4714
4897
|
throw turnError;
|
|
4715
4898
|
}
|
|
@@ -4805,9 +4988,7 @@ function chatAgent(options) {
|
|
|
4805
4988
|
savedAt: Date.now(),
|
|
4806
4989
|
messages: erroredUIMessages,
|
|
4807
4990
|
lastOutEventId: errorTurnCompleteResult?.lastEventId,
|
|
4808
|
-
lastInEventId: errorSnapshotInCursor !== undefined
|
|
4809
|
-
? String(errorSnapshotInCursor)
|
|
4810
|
-
: undefined,
|
|
4991
|
+
lastInEventId: errorSnapshotInCursor !== undefined ? String(errorSnapshotInCursor) : undefined,
|
|
4811
4992
|
});
|
|
4812
4993
|
}
|
|
4813
4994
|
catch (error) {
|
|
@@ -4818,8 +4999,7 @@ function chatAgent(options) {
|
|
|
4818
4999
|
}
|
|
4819
5000
|
}
|
|
4820
5001
|
// chat.requestUpgrade() / chat.endRun() — exit after error turn too
|
|
4821
|
-
if (locals.get(chatUpgradeRequestedKey) ||
|
|
4822
|
-
locals.get(chatEndRunRequestedKey)) {
|
|
5002
|
+
if (locals.get(chatUpgradeRequestedKey) || locals.get(chatEndRunRequestedKey)) {
|
|
4823
5003
|
return;
|
|
4824
5004
|
}
|
|
4825
5005
|
// Drain remaining recovered turns before idling — a thrown
|
|
@@ -4852,7 +5032,7 @@ function chatAgent(options) {
|
|
|
4852
5032
|
// cleanup so a missing subscription doesn't throw.
|
|
4853
5033
|
stopSub?.off();
|
|
4854
5034
|
}
|
|
4855
|
-
}
|
|
5035
|
+
},
|
|
4856
5036
|
});
|
|
4857
5037
|
// Register clientDataSchema so the CLI converts it to JSONSchema
|
|
4858
5038
|
// and stores it as payloadSchema — used by the Playground UI
|
|
@@ -4944,7 +5124,7 @@ function createChatBuilder(config) {
|
|
|
4944
5124
|
agent(options) {
|
|
4945
5125
|
const mergedUiStream = config.uiStreamOptions && options.uiMessageStreamOptions
|
|
4946
5126
|
? { ...config.uiStreamOptions, ...options.uiMessageStreamOptions }
|
|
4947
|
-
: options.uiMessageStreamOptions ?? config.uiStreamOptions;
|
|
5127
|
+
: (options.uiMessageStreamOptions ?? config.uiStreamOptions);
|
|
4948
5128
|
return chatAgent({
|
|
4949
5129
|
...options,
|
|
4950
5130
|
...(config.clientDataSchema ? { clientDataSchema: config.clientDataSchema } : {}),
|
|
@@ -5437,8 +5617,19 @@ async function pipeChatAndCapture(source, options) {
|
|
|
5437
5617
|
const onFinishPromise = new Promise((r) => {
|
|
5438
5618
|
resolveOnFinish = r;
|
|
5439
5619
|
});
|
|
5620
|
+
const resolvedOptions = resolveUIMessageStreamOptions();
|
|
5440
5621
|
const uiStream = source.toUIMessageStream({
|
|
5441
|
-
...
|
|
5622
|
+
...resolvedOptions,
|
|
5623
|
+
// Thread the prior chain (incl. a spliced handover partial) so a resumed
|
|
5624
|
+
// tool round's tool-output chunks merge into the originating tool-call
|
|
5625
|
+
// instead of throwing "No tool invocation found".
|
|
5626
|
+
...(options?.originalMessages ? { originalMessages: options.originalMessages } : {}),
|
|
5627
|
+
// Stamp a server-generated id on the start chunk, same as chat.agent's
|
|
5628
|
+
// pipe. Without it the AI SDK regenerates the assistant id when a
|
|
5629
|
+
// prepareStep injection (steering) starts a new step mid-stream, and
|
|
5630
|
+
// the frontend replaces the partial message — wiping the
|
|
5631
|
+
// pre-injection text from the UI and the captured response.
|
|
5632
|
+
generateMessageId: resolvedOptions.generateMessageId ?? generateMessageId,
|
|
5442
5633
|
onFinish: ({ responseMessage }) => {
|
|
5443
5634
|
captured = responseMessage;
|
|
5444
5635
|
resolveOnFinish();
|
|
@@ -5508,10 +5699,65 @@ class ChatMessageAccumulator {
|
|
|
5508
5699
|
this.uiMessages = [...uiMessages];
|
|
5509
5700
|
this.modelMessages = await toModelMessages(uiMessages);
|
|
5510
5701
|
}
|
|
5702
|
+
/**
|
|
5703
|
+
* Splice a `chat.headStart` handover partial into the accumulator (the warm
|
|
5704
|
+
* step-1 response). Dedups by `messageId` so a seeded/hydrated history that
|
|
5705
|
+
* already carries the partial isn't doubled. Seed any prior history first
|
|
5706
|
+
* (e.g. `setMessages(payload.headStartMessages)`). Low-level — see
|
|
5707
|
+
* `consumeHandover` for the wait+seed+apply convenience.
|
|
5708
|
+
*/
|
|
5709
|
+
applyHandover(signal) {
|
|
5710
|
+
spliceHandoverPartial(this.modelMessages, this.uiMessages, signal);
|
|
5711
|
+
}
|
|
5712
|
+
/**
|
|
5713
|
+
* One-call `chat.headStart` handover for a custom-agent loop: waits for the
|
|
5714
|
+
* handover signal, seeds prior history from `payload.headStartMessages`,
|
|
5715
|
+
* applies the warm step-1 partial, and reports what to do next.
|
|
5716
|
+
*
|
|
5717
|
+
* Returns `{ isFinal, skipped }`:
|
|
5718
|
+
* - `skipped: true` — not a `handover-prepare` run, the wait idled out, or the
|
|
5719
|
+
* warm handler aborted. Exit the run without a turn.
|
|
5720
|
+
* - `isFinal: true` — step 1 IS the response (pure text). Write turn-complete
|
|
5721
|
+
* and continue; do not call `streamText`.
|
|
5722
|
+
* - `isFinal: false` — fall through to `streamText`, which runs the pending
|
|
5723
|
+
* tool round handed over from step 1.
|
|
5724
|
+
*/
|
|
5725
|
+
async consumeHandover(options) {
|
|
5726
|
+
const signal = await waitForHandover({
|
|
5727
|
+
payload: options.payload,
|
|
5728
|
+
idleTimeoutInSeconds: options.idleTimeoutInSeconds,
|
|
5729
|
+
timeout: options.timeout,
|
|
5730
|
+
});
|
|
5731
|
+
if (!signal || signal.kind === "handover-skip") {
|
|
5732
|
+
return { isFinal: false, skipped: true };
|
|
5733
|
+
}
|
|
5734
|
+
if (options.payload.headStartMessages && options.payload.headStartMessages.length > 0) {
|
|
5735
|
+
await this.setMessages(options.payload.headStartMessages);
|
|
5736
|
+
}
|
|
5737
|
+
this.applyHandover(signal);
|
|
5738
|
+
return { isFinal: signal.isFinal, skipped: false };
|
|
5739
|
+
}
|
|
5511
5740
|
async addResponse(response) {
|
|
5512
5741
|
if (!response.id) {
|
|
5513
5742
|
response = { ...response, id: generateMessageId() };
|
|
5514
5743
|
}
|
|
5744
|
+
// Tool-approval and handover-resume continuations reuse the trailing
|
|
5745
|
+
// assistant's ID (via originalMessages on the pipe), so the captured
|
|
5746
|
+
// response can carry the same ID as a message already in the chain
|
|
5747
|
+
// (e.g. a spliced handover partial). Replace in place instead of pushing
|
|
5748
|
+
// a duplicate, mirroring the chat.agent accumulator.
|
|
5749
|
+
const existingIdx = this.uiMessages.findIndex((m) => m.id === response.id);
|
|
5750
|
+
if (existingIdx !== -1) {
|
|
5751
|
+
this.uiMessages[existingIdx] = response;
|
|
5752
|
+
try {
|
|
5753
|
+
// Reconvert all model messages since we replaced rather than appended.
|
|
5754
|
+
this.modelMessages = await toModelMessages(this.uiMessages.map((m) => stripProviderMetadata(m)));
|
|
5755
|
+
}
|
|
5756
|
+
catch {
|
|
5757
|
+
// Conversion failed — leave the existing model messages in place
|
|
5758
|
+
}
|
|
5759
|
+
return;
|
|
5760
|
+
}
|
|
5515
5761
|
this.uiMessages.push(response);
|
|
5516
5762
|
try {
|
|
5517
5763
|
const msgs = await toModelMessages([stripProviderMetadata(response)]);
|
|
@@ -5644,14 +5890,18 @@ class ChatMessageAccumulator {
|
|
|
5644
5890
|
* signaling, and idle/suspend between turns. You control: initialization,
|
|
5645
5891
|
* model/tool selection, persistence, and any custom per-turn logic.
|
|
5646
5892
|
*
|
|
5893
|
+
* Call from inside a `chat.customAgent()` run — the wrapper binds the
|
|
5894
|
+
* backing Session that the iterator's stop signal and message channels
|
|
5895
|
+
* resolve to. (A plain `task()` does not bind it, so `createSession`
|
|
5896
|
+
* would throw "session handle is not initialized".)
|
|
5897
|
+
*
|
|
5647
5898
|
* @example
|
|
5648
5899
|
* ```ts
|
|
5649
|
-
* import { task } from "@trigger.dev/sdk";
|
|
5650
5900
|
* import { chat, type ChatTaskWirePayload } from "@trigger.dev/sdk/ai";
|
|
5651
5901
|
* import { streamText } from "ai";
|
|
5652
5902
|
* import { openai } from "@ai-sdk/openai";
|
|
5653
5903
|
*
|
|
5654
|
-
* export const myChat =
|
|
5904
|
+
* export const myChat = chat.customAgent({
|
|
5655
5905
|
* id: "my-chat",
|
|
5656
5906
|
* run: async (payload: ChatTaskWirePayload, { signal }) => {
|
|
5657
5907
|
* const session = chat.createSession(payload, { signal });
|
|
@@ -5675,20 +5925,56 @@ function createChatSession(payload, options) {
|
|
|
5675
5925
|
[Symbol.asyncIterator]() {
|
|
5676
5926
|
let currentPayload = payload;
|
|
5677
5927
|
let turn = -1;
|
|
5678
|
-
|
|
5928
|
+
// Created on the first next() call, AFTER the resume-cursor seed —
|
|
5929
|
+
// createStopSignal attaches the `.in` SSE tail, and attaching
|
|
5930
|
+
// before the seed replays every record from seq 0 (the seed is a
|
|
5931
|
+
// no-op when the chatCustomAgent wrapper already ran it).
|
|
5932
|
+
let stop;
|
|
5933
|
+
let booted = false;
|
|
5679
5934
|
const accumulator = new ChatMessageAccumulator();
|
|
5680
5935
|
let previousTurnUsage;
|
|
5681
5936
|
let cumulativeUsage = emptyUsage();
|
|
5682
5937
|
return {
|
|
5683
5938
|
async next() {
|
|
5939
|
+
if (!booted) {
|
|
5940
|
+
booted = true;
|
|
5941
|
+
await seedSessionInResumeCursorForCustomLoop(currentPayload);
|
|
5942
|
+
stop = createStopSignal();
|
|
5943
|
+
}
|
|
5684
5944
|
turn++;
|
|
5945
|
+
// Head-start handover: the server triggered this run with
|
|
5946
|
+
// `trigger: "handover-prepare"` and signals the warm step-1 partial on
|
|
5947
|
+
// `session.in`. Wait for it BEFORE any `messagesInput.waitWithIdleTimeout`
|
|
5948
|
+
// (that facade consumes-and-discards non-message chunks and would swallow
|
|
5949
|
+
// the signal). Turn-0 only — continuation boots never carry this trigger.
|
|
5950
|
+
let handoverThisTurn = null;
|
|
5951
|
+
let pendingHandoverSignal = null;
|
|
5952
|
+
if (turn === 0 && currentPayload.trigger === "handover-prepare") {
|
|
5953
|
+
const signal = await waitForHandover({
|
|
5954
|
+
payload: currentPayload,
|
|
5955
|
+
idleTimeoutInSeconds: sessionIdleTimeoutOpt ??
|
|
5956
|
+
currentPayload.idleTimeoutInSeconds ??
|
|
5957
|
+
idleTimeoutInSeconds,
|
|
5958
|
+
timeout,
|
|
5959
|
+
});
|
|
5960
|
+
if (!signal || signal.kind === "handover-skip" || runSignal.aborted) {
|
|
5961
|
+
stop.cleanup();
|
|
5962
|
+
return { done: true, value: undefined };
|
|
5963
|
+
}
|
|
5964
|
+
pendingHandoverSignal = signal;
|
|
5965
|
+
handoverThisTurn = { isFinal: signal.isFinal };
|
|
5966
|
+
// Rewrite to a normal first-turn message turn so the rest of the loop
|
|
5967
|
+
// (steering setup, addIncoming, turnObj) runs unchanged.
|
|
5968
|
+
currentPayload = { ...currentPayload, trigger: "submit-message", message: undefined };
|
|
5969
|
+
}
|
|
5685
5970
|
// First turn: wait when the boot payload carries no message.
|
|
5686
5971
|
// Preload boots wait for the first real message; continuation
|
|
5687
5972
|
// boots (fresh run via `ensureRunForSession` / end-and-continue)
|
|
5688
5973
|
// arrive with the sticky boot-payload fields stripped, so running
|
|
5689
5974
|
// a turn immediately would invoke the model with no user input.
|
|
5690
5975
|
const isMessagelessContinuationBoot = currentPayload.continuation === true && !currentPayload.message;
|
|
5691
|
-
if (turn === 0 &&
|
|
5976
|
+
if (turn === 0 &&
|
|
5977
|
+
(currentPayload.trigger === "preload" || isMessagelessContinuationBoot)) {
|
|
5692
5978
|
const result = await messagesInput.waitWithIdleTimeout({
|
|
5693
5979
|
idleTimeoutInSeconds: sessionIdleTimeoutOpt ?? currentPayload.idleTimeoutInSeconds ?? 30,
|
|
5694
5980
|
timeout,
|
|
@@ -5712,8 +5998,7 @@ function createChatSession(payload, options) {
|
|
|
5712
5998
|
// Subsequent turns: wait for the next message
|
|
5713
5999
|
if (turn > 0) {
|
|
5714
6000
|
// chat.requestUpgrade() / chat.endRun() — exit before waiting
|
|
5715
|
-
if (locals.get(chatUpgradeRequestedKey) ||
|
|
5716
|
-
locals.get(chatEndRunRequestedKey)) {
|
|
6001
|
+
if (locals.get(chatUpgradeRequestedKey) || locals.get(chatEndRunRequestedKey)) {
|
|
5717
6002
|
stop.cleanup();
|
|
5718
6003
|
return { done: true, value: undefined };
|
|
5719
6004
|
}
|
|
@@ -5788,6 +6073,16 @@ function createChatSession(payload, options) {
|
|
|
5788
6073
|
? [currentPayload.message]
|
|
5789
6074
|
: [];
|
|
5790
6075
|
const messages = await accumulator.addIncoming(incomingForAccumulator, currentPayload.trigger, turn);
|
|
6076
|
+
// Apply the head-start handover AFTER addIncoming — turn-0 addIncoming
|
|
6077
|
+
// replaces accumulator state, which would wipe a pre-applied splice.
|
|
6078
|
+
// Seed prior history first, then splice the warm step-1 partial.
|
|
6079
|
+
if (pendingHandoverSignal) {
|
|
6080
|
+
const priorHistory = currentPayload.headStartMessages;
|
|
6081
|
+
if (priorHistory && priorHistory.length > 0) {
|
|
6082
|
+
await accumulator.setMessages(priorHistory);
|
|
6083
|
+
}
|
|
6084
|
+
accumulator.applyHandover(pendingHandoverSignal);
|
|
6085
|
+
}
|
|
5791
6086
|
// chat.requestUpgrade() called before this turn — signal transport and exit
|
|
5792
6087
|
if (locals.get(chatUpgradeRequestedKey)) {
|
|
5793
6088
|
await writeUpgradeRequiredChunk();
|
|
@@ -5814,13 +6109,38 @@ function createChatSession(payload, options) {
|
|
|
5814
6109
|
continuation: currentPayload.continuation ?? false,
|
|
5815
6110
|
previousTurnUsage,
|
|
5816
6111
|
totalUsage: cumulativeUsage,
|
|
6112
|
+
handover: handoverThisTurn,
|
|
5817
6113
|
async setMessages(uiMessages) {
|
|
5818
6114
|
await accumulator.setMessages(uiMessages);
|
|
5819
6115
|
},
|
|
5820
6116
|
async complete(source) {
|
|
6117
|
+
// Head-start final turn: the warm step-1 partial is already spliced
|
|
6118
|
+
// into the accumulator and IS the response — nothing to pipe. Only
|
|
6119
|
+
// valid on a final handover; a missing source on any other turn is a
|
|
6120
|
+
// mistake (it would silently finalize without an assistant response).
|
|
6121
|
+
if (!source) {
|
|
6122
|
+
if (!handoverThisTurn?.isFinal) {
|
|
6123
|
+
throw new Error("turn.complete() requires a stream source unless turn.handover.isFinal is true");
|
|
6124
|
+
}
|
|
6125
|
+
const response = accumulator.uiMessages.at(-1);
|
|
6126
|
+
if (!response || response.role !== "assistant") {
|
|
6127
|
+
throw new Error("turn.complete() could not find the spliced handover response");
|
|
6128
|
+
}
|
|
6129
|
+
sessionMsgSub.off();
|
|
6130
|
+
await chatWriteTurnComplete();
|
|
6131
|
+
return response;
|
|
6132
|
+
}
|
|
5821
6133
|
let response;
|
|
5822
6134
|
try {
|
|
5823
|
-
response = await pipeChatAndCapture(source, {
|
|
6135
|
+
response = await pipeChatAndCapture(source, {
|
|
6136
|
+
signal: combinedSignal,
|
|
6137
|
+
// On a non-final handover turn, thread the spliced partial so a
|
|
6138
|
+
// resumed tool round's tool-output chunks merge into the
|
|
6139
|
+
// handed-over tool-call. Gated on the handover turn only — a
|
|
6140
|
+
// normal turn must not pass originalMessages (it would merge the
|
|
6141
|
+
// fresh response into the prior assistant message).
|
|
6142
|
+
...(handoverThisTurn ? { originalMessages: accumulator.uiMessages } : {}),
|
|
6143
|
+
});
|
|
5824
6144
|
}
|
|
5825
6145
|
catch (error) {
|
|
5826
6146
|
if (error instanceof Error && error.name === "AbortError") {
|
|
@@ -5925,7 +6245,8 @@ function createChatSession(payload, options) {
|
|
|
5925
6245
|
},
|
|
5926
6246
|
];
|
|
5927
6247
|
if (sessionCompaction.compactUIMessages) {
|
|
5928
|
-
accumulator.uiMessages =
|
|
6248
|
+
accumulator.uiMessages =
|
|
6249
|
+
await sessionCompaction.compactUIMessages(compactEvent);
|
|
5929
6250
|
}
|
|
5930
6251
|
}
|
|
5931
6252
|
}
|
|
@@ -5937,7 +6258,10 @@ function createChatSession(payload, options) {
|
|
|
5937
6258
|
// Append any non-transient data parts queued via chat.response or writer.write()
|
|
5938
6259
|
const queuedParts = locals.get(chatResponsePartsKey);
|
|
5939
6260
|
if (queuedParts && queuedParts.length > 0) {
|
|
5940
|
-
response = {
|
|
6261
|
+
response = {
|
|
6262
|
+
...response,
|
|
6263
|
+
parts: [...(response.parts ?? []), ...queuedParts],
|
|
6264
|
+
};
|
|
5941
6265
|
locals.set(chatResponsePartsKey, []);
|
|
5942
6266
|
}
|
|
5943
6267
|
await accumulator.addResponse(response);
|
|
@@ -5975,7 +6299,8 @@ function createChatSession(payload, options) {
|
|
|
5975
6299
|
return { done: false, value: turnObj };
|
|
5976
6300
|
},
|
|
5977
6301
|
async return() {
|
|
5978
|
-
stop
|
|
6302
|
+
// `stop` only exists once next() has booted the iterator.
|
|
6303
|
+
stop?.cleanup();
|
|
5979
6304
|
return { done: true, value: undefined };
|
|
5980
6305
|
},
|
|
5981
6306
|
};
|
|
@@ -6205,6 +6530,12 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
6205
6530
|
if (!params.chatId) {
|
|
6206
6531
|
throw new Error("chat.createStartSessionAction: params.chatId is required — used as the session externalId.");
|
|
6207
6532
|
}
|
|
6533
|
+
// Scope the action to `apiClient`'s env: re-enter without it so the body
|
|
6534
|
+
// runs once, under that config (read via apiClientManager.accessToken/.baseURL).
|
|
6535
|
+
if (options?.apiClient) {
|
|
6536
|
+
const { apiClient, ...rest } = options;
|
|
6537
|
+
return apiClientManager.runWithConfig(apiClient, () => createChatStartSessionAction(taskId, rest)(params));
|
|
6538
|
+
}
|
|
6208
6539
|
// The first run boots before the user's first message lands on
|
|
6209
6540
|
// `.in/append`, so it sees an empty `messages` array and `trigger:
|
|
6210
6541
|
// "preload"`. This matches the pre-Sessions preload semantics:
|
|
@@ -6218,8 +6549,8 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
6218
6549
|
// run-list filter by chat works without the customer having to wire it
|
|
6219
6550
|
// up. Mirrors the browser-mediated `TriggerChatTransport.doStart` path.
|
|
6220
6551
|
const userTags = params.triggerConfig?.tags ?? options?.triggerConfig?.tags ?? [];
|
|
6221
|
-
//
|
|
6222
|
-
const tags = [`chat:${params.chatId}`, ...userTags].slice(0,
|
|
6552
|
+
// SessionTriggerConfig.tags allows at most 5; the auto chat tag takes one slot.
|
|
6553
|
+
const tags = [`chat:${params.chatId}`, ...userTags].slice(0, 5);
|
|
6223
6554
|
const clientDataMetadata = params.clientData !== undefined ? { metadata: params.clientData } : {};
|
|
6224
6555
|
const triggerConfig = {
|
|
6225
6556
|
basePayload: {
|
|
@@ -6243,6 +6574,20 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
6243
6574
|
maxAttempts: params.triggerConfig?.maxAttempts ?? options?.triggerConfig?.maxAttempts,
|
|
6244
6575
|
}
|
|
6245
6576
|
: {}),
|
|
6577
|
+
...(options?.triggerConfig?.maxDuration !== undefined ||
|
|
6578
|
+
params.triggerConfig?.maxDuration !== undefined
|
|
6579
|
+
? {
|
|
6580
|
+
maxDuration: params.triggerConfig?.maxDuration ?? options?.triggerConfig?.maxDuration,
|
|
6581
|
+
}
|
|
6582
|
+
: {}),
|
|
6583
|
+
...(options?.triggerConfig?.region || params.triggerConfig?.region
|
|
6584
|
+
? { region: params.triggerConfig?.region ?? options?.triggerConfig?.region }
|
|
6585
|
+
: {}),
|
|
6586
|
+
...(options?.triggerConfig?.lockToVersion || params.triggerConfig?.lockToVersion
|
|
6587
|
+
? {
|
|
6588
|
+
lockToVersion: params.triggerConfig?.lockToVersion ?? options?.triggerConfig?.lockToVersion,
|
|
6589
|
+
}
|
|
6590
|
+
: {}),
|
|
6246
6591
|
...(options?.triggerConfig?.idleTimeoutInSeconds !== undefined ||
|
|
6247
6592
|
params.triggerConfig?.idleTimeoutInSeconds !== undefined
|
|
6248
6593
|
? {
|
|
@@ -6298,9 +6643,7 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
6298
6643
|
}
|
|
6299
6644
|
function resolveChatStartBaseURL(endpoint, chatId, option) {
|
|
6300
6645
|
const fallback = apiClientManager.baseURL ?? "https://api.trigger.dev";
|
|
6301
|
-
const raw = typeof option === "function"
|
|
6302
|
-
? option({ endpoint, chatId })
|
|
6303
|
-
: option ?? fallback;
|
|
6646
|
+
const raw = typeof option === "function" ? option({ endpoint, chatId }) : (option ?? fallback);
|
|
6304
6647
|
return raw.replace(/\/$/, "");
|
|
6305
6648
|
}
|
|
6306
6649
|
function overrideRequestHeaders(accessToken) {
|
|
@@ -6421,10 +6764,20 @@ export const chat = {
|
|
|
6421
6764
|
MessageAccumulator: ChatMessageAccumulator,
|
|
6422
6765
|
/** Create a chat session (async iterator). See {@link createChatSession}. */
|
|
6423
6766
|
createSession: createChatSession,
|
|
6767
|
+
/**
|
|
6768
|
+
* Wait for a `chat.headStart` handover signal inside a `chat.customAgent`
|
|
6769
|
+
* loop (turn 0). See {@link waitForHandover}. For most loops prefer the
|
|
6770
|
+
* `chat.MessageAccumulator.consumeHandover()` convenience, which also seeds
|
|
6771
|
+
* `payload.headStartMessages` and applies the partial.
|
|
6772
|
+
*/
|
|
6773
|
+
waitForHandover,
|
|
6424
6774
|
/**
|
|
6425
6775
|
* Store and retrieve a resolved prompt for the current run.
|
|
6426
6776
|
*
|
|
6427
6777
|
* - `chat.prompt.set(resolved)` — store a `ResolvedPrompt` or plain string
|
|
6778
|
+
* - `chat.prompt.set(resolved, { providerOptions })` — also attach provider
|
|
6779
|
+
* options to the system block so a provider can cache it (e.g. Anthropic
|
|
6780
|
+
* prompt caching). See the prompt-caching guide.
|
|
6428
6781
|
* - `chat.prompt()` — read the stored prompt (throws if not set)
|
|
6429
6782
|
*/
|
|
6430
6783
|
prompt: Object.assign(getChatPrompt, { set: setChatPrompt }),
|