@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/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,45 @@ 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", {
|
|
159
|
+
error: error instanceof Error ? error.message : String(error),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
120
163
|
let readChatSnapshotImpl;
|
|
121
164
|
function __setReadChatSnapshotImplForTests(impl) {
|
|
122
165
|
readChatSnapshotImpl = impl;
|
|
@@ -668,6 +711,16 @@ function createTaskToolExecuteHandler(task) {
|
|
|
668
711
|
toolMeta.continuation = chatCtx.continuation;
|
|
669
712
|
toolMeta.clientData = chatCtx.clientData;
|
|
670
713
|
}
|
|
714
|
+
else {
|
|
715
|
+
// Hand-rolled chat.customAgent loops never set per-turn context, but
|
|
716
|
+
// the wrapper records the boot payload's external chatId at run boot
|
|
717
|
+
// — thread it so subtask chat helpers (`chat.stream.writer` with
|
|
718
|
+
// target "root") can open the parent's session.
|
|
719
|
+
const chatExternalId = locals_js_1.locals.get(chatExternalIdKey);
|
|
720
|
+
if (chatExternalId) {
|
|
721
|
+
toolMeta.chatId = chatExternalId;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
671
724
|
const chatLocals = {};
|
|
672
725
|
for (const entry of chatLocalRegistry) {
|
|
673
726
|
const value = locals_js_1.locals.get(entry.key);
|
|
@@ -1190,8 +1243,7 @@ const handoverInput = {
|
|
|
1190
1243
|
const result = await getChatSession().in.waitWithIdleTimeout(options);
|
|
1191
1244
|
if (!result.ok)
|
|
1192
1245
|
return result;
|
|
1193
|
-
if (result.output.kind === "handover" ||
|
|
1194
|
-
result.output.kind === "handover-skip") {
|
|
1246
|
+
if (result.output.kind === "handover" || result.output.kind === "handover-skip") {
|
|
1195
1247
|
return { ok: true, output: result.output };
|
|
1196
1248
|
}
|
|
1197
1249
|
// Other kinds (message, stop) are not expected during handover-prepare.
|
|
@@ -1200,6 +1252,36 @@ const handoverInput = {
|
|
|
1200
1252
|
}
|
|
1201
1253
|
},
|
|
1202
1254
|
};
|
|
1255
|
+
/**
|
|
1256
|
+
* Wait for a `chat.headStart` handover signal inside a custom-agent loop or
|
|
1257
|
+
* `chat.createSession`. Returns:
|
|
1258
|
+
* - `null` — this run is not a `handover-prepare` boot, or the wait idled out /
|
|
1259
|
+
* the warm handler crashed before signaling. Treat as "no handover".
|
|
1260
|
+
* - `{ kind: "handover-skip" }` — the warm handler aborted; exit without a turn.
|
|
1261
|
+
* - `{ kind: "handover", partialAssistantMessage, messageId?, isFinal }` — splice
|
|
1262
|
+
* the partial (`chat.MessageAccumulator.applyHandover`) and, when `isFinal` is
|
|
1263
|
+
* false, fall through to `streamText` to run the handed-over tool round.
|
|
1264
|
+
*
|
|
1265
|
+
* For the common case prefer `accumulator.consumeHandover()`, which also seeds
|
|
1266
|
+
* `payload.headStartMessages` and applies the partial for you.
|
|
1267
|
+
*
|
|
1268
|
+
* Must be called at turn 0 before any `chat.messages.waitWithIdleTimeout` —
|
|
1269
|
+
* that facade consumes and discards non-message chunks, which would swallow the
|
|
1270
|
+
* handover signal.
|
|
1271
|
+
*/
|
|
1272
|
+
async function waitForHandover(options) {
|
|
1273
|
+
if (options.payload.trigger !== "handover-prepare")
|
|
1274
|
+
return null;
|
|
1275
|
+
const result = await handoverInput.waitWithIdleTimeout({
|
|
1276
|
+
idleTimeoutInSeconds: options.idleTimeoutInSeconds ?? options.payload.idleTimeoutInSeconds ?? 60,
|
|
1277
|
+
timeout: options.timeout,
|
|
1278
|
+
spanName: options.spanName ?? "waiting for handover signal",
|
|
1279
|
+
});
|
|
1280
|
+
// Non-ok = idle timeout or the warm handler crashed without signaling.
|
|
1281
|
+
if (!result.ok)
|
|
1282
|
+
return null;
|
|
1283
|
+
return result.output;
|
|
1284
|
+
}
|
|
1203
1285
|
/**
|
|
1204
1286
|
* Per-turn deferred promises. Registered via `chat.defer()`, awaited
|
|
1205
1287
|
* before `onTurnComplete` fires. Reset each turn.
|
|
@@ -1296,6 +1378,27 @@ function synthesizeHandoverUIMessage(partial, messageId) {
|
|
|
1296
1378
|
parts,
|
|
1297
1379
|
};
|
|
1298
1380
|
}
|
|
1381
|
+
/**
|
|
1382
|
+
* Splice a head-start handover partial into an accumulating message pair
|
|
1383
|
+
* (model + UI). Dedups by `messageId` against the UI chain (so a hydrated
|
|
1384
|
+
* history that already persisted the partial isn't doubled), then pushes the
|
|
1385
|
+
* partial into `modelMessages` and the synthesized UIMessage into `uiMessages`.
|
|
1386
|
+
* Shared by the `chat.agent` turn-0 splice and `ChatMessageAccumulator.applyHandover`.
|
|
1387
|
+
* @internal
|
|
1388
|
+
*/
|
|
1389
|
+
function spliceHandoverPartial(modelMessages, uiMessages, signal) {
|
|
1390
|
+
if (!signal.partialAssistantMessage || signal.partialAssistantMessage.length === 0) {
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
// Skip if the hydrated chain already persisted the partial under this id.
|
|
1394
|
+
const alreadyInChain = signal.messageId !== undefined && uiMessages.some((m) => m.id === signal.messageId);
|
|
1395
|
+
if (alreadyInChain)
|
|
1396
|
+
return;
|
|
1397
|
+
modelMessages.push(...signal.partialAssistantMessage);
|
|
1398
|
+
const partialUI = synthesizeHandoverUIMessage(signal.partialAssistantMessage, signal.messageId);
|
|
1399
|
+
if (partialUI)
|
|
1400
|
+
uiMessages.push(partialUI);
|
|
1401
|
+
}
|
|
1299
1402
|
/**
|
|
1300
1403
|
* Per-turn background context queue. Messages added via `chat.backgroundWork.inject()`
|
|
1301
1404
|
* are drained at the next `prepareStep` boundary and appended to the model messages.
|
|
@@ -1853,6 +1956,55 @@ function isStepBoundarySafe(step) {
|
|
|
1853
1956
|
.map((p) => p.toolCallId));
|
|
1854
1957
|
return ![...callIds].some((id) => !settledIds.has(id));
|
|
1855
1958
|
}
|
|
1959
|
+
/**
|
|
1960
|
+
* True when a model message is a `tool` message carrying a
|
|
1961
|
+
* `tool-approval-response` part — the trailing row a head-start handover
|
|
1962
|
+
* reshapes a pending first-turn tool call into. AI SDK's `collectToolApprovals`
|
|
1963
|
+
* only inspects the conversation's last message, so this row must survive to
|
|
1964
|
+
* `streamText` intact for the agent to execute the handed-over call.
|
|
1965
|
+
* @internal
|
|
1966
|
+
*/
|
|
1967
|
+
function hasToolApprovalResponse(message) {
|
|
1968
|
+
return (message?.role === "tool" &&
|
|
1969
|
+
Array.isArray(message.content) &&
|
|
1970
|
+
message.content.some((part) => part != null &&
|
|
1971
|
+
typeof part === "object" &&
|
|
1972
|
+
part.type === "tool-approval-response"));
|
|
1973
|
+
}
|
|
1974
|
+
/**
|
|
1975
|
+
* Keep a head-start handover's tool-approval tail intact across `prepareMessages`.
|
|
1976
|
+
*
|
|
1977
|
+
* The handover reshapes the warm step-1's pending tool call into AI SDK's
|
|
1978
|
+
* tool-approval round: a `tool-approval-request` on the assistant plus a
|
|
1979
|
+
* trailing `tool` message with `tool-approval-response { approved: true }`. The
|
|
1980
|
+
* agent's next `streamText` runs `collectToolApprovals`, which ONLY looks at the
|
|
1981
|
+
* last message — so that tool row must stay last and unmodified for the pending
|
|
1982
|
+
* call to execute. A user `prepareMessages` hook that rewrites or drops the last
|
|
1983
|
+
* message (e.g. rolling a provider cache breakpoint onto it) silently breaks the
|
|
1984
|
+
* resume: the agent sends a bare `tool_use` and the turn dies with
|
|
1985
|
+
* "tool_use ids were found without tool_result". If the hook's input ended with
|
|
1986
|
+
* that approval tail, re-assert the original tail as the last message.
|
|
1987
|
+
*
|
|
1988
|
+
* No-op for every normal turn — only fires when the input genuinely ended with a
|
|
1989
|
+
* pending tool-approval response (i.e. a head-start resume).
|
|
1990
|
+
* @internal
|
|
1991
|
+
*/
|
|
1992
|
+
function preserveToolApprovalTail(original, prepared) {
|
|
1993
|
+
const originalTail = original[original.length - 1];
|
|
1994
|
+
if (!hasToolApprovalResponse(originalTail))
|
|
1995
|
+
return prepared;
|
|
1996
|
+
// Hook left the exact tail object in place — nothing to do.
|
|
1997
|
+
if (prepared[prepared.length - 1] === originalTail)
|
|
1998
|
+
return prepared;
|
|
1999
|
+
// Otherwise drop only the trailing approval tail the hook produced (the
|
|
2000
|
+
// original moved, or a rewritten copy) and re-append the original so it is
|
|
2001
|
+
// last and intact. Older approval rounds deeper in history must survive.
|
|
2002
|
+
const withoutMovedOriginal = prepared.filter((m) => m !== originalTail);
|
|
2003
|
+
while (hasToolApprovalResponse(withoutMovedOriginal[withoutMovedOriginal.length - 1])) {
|
|
2004
|
+
withoutMovedOriginal.pop();
|
|
2005
|
+
}
|
|
2006
|
+
return [...withoutMovedOriginal, originalTail];
|
|
2007
|
+
}
|
|
1856
2008
|
/**
|
|
1857
2009
|
* Apply the prepareMessages hook if one is set in locals.
|
|
1858
2010
|
* @internal
|
|
@@ -1862,7 +2014,7 @@ async function applyPrepareMessages(messages, reason) {
|
|
|
1862
2014
|
if (!hook)
|
|
1863
2015
|
return messages;
|
|
1864
2016
|
const turnCtx = locals_js_1.locals.get(chatTurnContextKey);
|
|
1865
|
-
|
|
2017
|
+
const prepared = await tracer_js_1.tracer.startActiveSpan("prepareMessages()", async () => {
|
|
1866
2018
|
return hook({
|
|
1867
2019
|
messages,
|
|
1868
2020
|
reason,
|
|
@@ -1878,6 +2030,9 @@ async function applyPrepareMessages(messages, reason) {
|
|
|
1878
2030
|
"chat.prepareMessages.messageCount": messages.length,
|
|
1879
2031
|
},
|
|
1880
2032
|
});
|
|
2033
|
+
// A user hook must never be able to break the head-start handover resume by
|
|
2034
|
+
// disturbing the trailing tool-approval row (see preserveToolApprovalTail).
|
|
2035
|
+
return preserveToolApprovalTail(messages, prepared);
|
|
1881
2036
|
}
|
|
1882
2037
|
/**
|
|
1883
2038
|
* Resolve the `tools` option into a concrete `ToolSet` and cache it in locals so
|
|
@@ -2250,11 +2405,18 @@ function isCompactionSafe(messages) {
|
|
|
2250
2405
|
}
|
|
2251
2406
|
/** @internal */
|
|
2252
2407
|
const chatPromptKey = locals_js_1.locals.create("chat.prompt");
|
|
2408
|
+
/**
|
|
2409
|
+
* @internal Provider options attached to the system message that
|
|
2410
|
+
* `toStreamTextOptions()` builds from the stored prompt — lets a provider cache
|
|
2411
|
+
* the system block. Stored separately so it works for both the `ResolvedPrompt`
|
|
2412
|
+
* and plain-string forms without mutating the prompt object.
|
|
2413
|
+
*/
|
|
2414
|
+
const chatPromptProviderOptionsKey = locals_js_1.locals.create("chat.prompt.providerOptions");
|
|
2253
2415
|
/**
|
|
2254
2416
|
* Store a resolved prompt (or plain string) for the current run.
|
|
2255
2417
|
* Call from any hook (`onPreload`, `onChatStart`, `onTurnStart`) or `run()`.
|
|
2256
2418
|
*/
|
|
2257
|
-
function setChatPrompt(resolved) {
|
|
2419
|
+
function setChatPrompt(resolved, options) {
|
|
2258
2420
|
if (typeof resolved === "string") {
|
|
2259
2421
|
locals_js_1.locals.set(chatPromptKey, {
|
|
2260
2422
|
text: resolved,
|
|
@@ -2271,6 +2433,9 @@ function setChatPrompt(resolved) {
|
|
|
2271
2433
|
else {
|
|
2272
2434
|
locals_js_1.locals.set(chatPromptKey, resolved);
|
|
2273
2435
|
}
|
|
2436
|
+
// Always overwrite the slot (even with undefined) so a later prompt.set with
|
|
2437
|
+
// no options clears a previous prompt's cache opt-in rather than leaking it.
|
|
2438
|
+
locals_js_1.locals.set(chatPromptProviderOptionsKey, options?.providerOptions);
|
|
2274
2439
|
}
|
|
2275
2440
|
/**
|
|
2276
2441
|
* Read the stored prompt. Throws if `chat.prompt.set()` has not been called.
|
|
@@ -2436,7 +2601,21 @@ function toStreamTextOptions(options) {
|
|
|
2436
2601
|
const promptText = prompt?.text ?? "";
|
|
2437
2602
|
const skillsText = skills && skills.length > 0 ? buildSkillsSystemPrompt(skills) : "";
|
|
2438
2603
|
if (promptText || skillsText) {
|
|
2439
|
-
|
|
2604
|
+
const systemText = [promptText, skillsText].filter(Boolean).join("\n\n");
|
|
2605
|
+
// Resolve system-prompt provider options for caching. Precedence (most
|
|
2606
|
+
// specific wins, no deep merge): explicit `systemProviderOptions` →
|
|
2607
|
+
// `cacheControl` sugar → `providerOptions` stored on `chat.prompt.set()`.
|
|
2608
|
+
const systemProviderOptions = options?.systemProviderOptions ??
|
|
2609
|
+
(options?.cacheControl
|
|
2610
|
+
? { anthropic: { cacheControl: options.cacheControl } }
|
|
2611
|
+
: undefined) ??
|
|
2612
|
+
locals_js_1.locals.get(chatPromptProviderOptionsKey);
|
|
2613
|
+
// A bare string stays a bare string (the unchanged default). With provider
|
|
2614
|
+
// options, emit a structured `SystemModelMessage` so the provider can cache
|
|
2615
|
+
// the system block — `streamText`'s `system` accepts string | message.
|
|
2616
|
+
result.system = systemProviderOptions
|
|
2617
|
+
? { role: "system", content: systemText, providerOptions: systemProviderOptions }
|
|
2618
|
+
: systemText;
|
|
2440
2619
|
}
|
|
2441
2620
|
// Prompt-related options (only if chat.prompt.set() was called)
|
|
2442
2621
|
if (prompt) {
|
|
@@ -2618,6 +2797,7 @@ function chatCustomAgent(options) {
|
|
|
2618
2797
|
// `chat.createStartSessionAction`) before this run is triggered.
|
|
2619
2798
|
// No client-side upsert needed.
|
|
2620
2799
|
locals_js_1.locals.set(chatSessionHandleKey, sessions_js_1.sessions.open(payload.chatId));
|
|
2800
|
+
locals_js_1.locals.set(chatExternalIdKey, payload.chatId);
|
|
2621
2801
|
locals_js_1.locals.set(chatAgentRunContextKey, runOptions.ctx);
|
|
2622
2802
|
// Initialize the turn-complete trim slot so `chat.writeTurnComplete`
|
|
2623
2803
|
// trims `session.out` back to the previous turn boundary. Without
|
|
@@ -2627,6 +2807,10 @@ function chatCustomAgent(options) {
|
|
|
2627
2807
|
(0, streams_js_1.markChatAgentRunForStreamsWarning)();
|
|
2628
2808
|
v3_1.taskContext.setConversationId(payload.chatId);
|
|
2629
2809
|
stampConversationIdOnActiveSpan(payload.chatId);
|
|
2810
|
+
// Seed the `.in` resume cursor before user code attaches any `.in`
|
|
2811
|
+
// listener — otherwise a continuation boot replays already-answered
|
|
2812
|
+
// messages into the loop's first wait.
|
|
2813
|
+
await seedSessionInResumeCursorForCustomLoop(payload);
|
|
2630
2814
|
return userRun(payload, runOptions);
|
|
2631
2815
|
},
|
|
2632
2816
|
});
|
|
@@ -2673,6 +2857,7 @@ function chatAgent(options) {
|
|
|
2673
2857
|
// `chat.createStartSessionAction` or browser-direct) before this
|
|
2674
2858
|
// run is triggered — no client-side upsert needed here.
|
|
2675
2859
|
locals_js_1.locals.set(chatSessionHandleKey, sessions_js_1.sessions.open(payload.chatId));
|
|
2860
|
+
locals_js_1.locals.set(chatExternalIdKey, payload.chatId);
|
|
2676
2861
|
// Mutable holder; advances in `writeTurnCompleteChunk` after each turn
|
|
2677
2862
|
// and is the trim target for the NEXT turn's trim record.
|
|
2678
2863
|
locals_js_1.locals.set(lastTurnCompleteSeqNumKey, { value: undefined });
|
|
@@ -2796,7 +2981,9 @@ function chatAgent(options) {
|
|
|
2796
2981
|
const replayOutPhase = async () => {
|
|
2797
2982
|
const replayOutStart = Date.now();
|
|
2798
2983
|
try {
|
|
2799
|
-
const replayResult = await replaySessionOutTail(sessionIdForSnapshot, {
|
|
2984
|
+
const replayResult = await replaySessionOutTail(sessionIdForSnapshot, {
|
|
2985
|
+
lastEventId: bootSnapshot?.lastOutEventId,
|
|
2986
|
+
});
|
|
2800
2987
|
replayedSettled = replayResult.settled;
|
|
2801
2988
|
replayedPartial = replayResult.partial;
|
|
2802
2989
|
replayedPartialRaw = replayResult.partialRaw;
|
|
@@ -2893,9 +3080,7 @@ function chatAgent(options) {
|
|
|
2893
3080
|
// crashed / was canceled / requested upgrade
|
|
2894
3081
|
// - Snapshot exists at all (catches edge cases where the wire
|
|
2895
3082
|
// didn't set `continuation` but a snapshot indicates prior turns)
|
|
2896
|
-
const needsResumeCursor = ctx.attempt.number > 1 ||
|
|
2897
|
-
payload.continuation === true ||
|
|
2898
|
-
bootSnapshot !== undefined;
|
|
3083
|
+
const needsResumeCursor = ctx.attempt.number > 1 || payload.continuation === true || bootSnapshot !== undefined;
|
|
2899
3084
|
if (needsResumeCursor) {
|
|
2900
3085
|
try {
|
|
2901
3086
|
// Reuse the cursor the boot block already resolved (snapshot
|
|
@@ -3242,9 +3427,7 @@ function chatAgent(options) {
|
|
|
3242
3427
|
// Wait for the first real message — task-level idle settings win over
|
|
3243
3428
|
// `transport.preload(..., { idleTimeoutInSeconds })` / wire payload so
|
|
3244
3429
|
// `chat.agent({ idleTimeoutInSeconds, preloadIdleTimeoutInSeconds })` is authoritative.
|
|
3245
|
-
const effectivePreloadIdleTimeout = preloadIdleTimeoutInSeconds ??
|
|
3246
|
-
idleTimeoutInSeconds ??
|
|
3247
|
-
payload.idleTimeoutInSeconds;
|
|
3430
|
+
const effectivePreloadIdleTimeout = preloadIdleTimeoutInSeconds ?? idleTimeoutInSeconds ?? payload.idleTimeoutInSeconds;
|
|
3248
3431
|
const effectivePreloadTimeout = metadata_js_1.metadata.get(TURN_TIMEOUT_METADATA_KEY) ??
|
|
3249
3432
|
preloadTimeout ??
|
|
3250
3433
|
turnTimeout;
|
|
@@ -3489,7 +3672,9 @@ function chatAgent(options) {
|
|
|
3489
3672
|
if (lastUserMessage) {
|
|
3490
3673
|
turnAttributes["chat.user_message"] = lastUserMessage;
|
|
3491
3674
|
// Show a truncated preview of the user message as an accessory
|
|
3492
|
-
const preview = lastUserMessage.length > 80
|
|
3675
|
+
const preview = lastUserMessage.length > 80
|
|
3676
|
+
? lastUserMessage.slice(0, 80) + "..."
|
|
3677
|
+
: lastUserMessage;
|
|
3493
3678
|
Object.assign(turnAttributes, (0, v3_1.accessoryAttributes)({
|
|
3494
3679
|
items: [{ text: preview, variant: "normal" }],
|
|
3495
3680
|
style: "codepath",
|
|
@@ -3776,8 +3961,7 @@ function chatAgent(options) {
|
|
|
3776
3961
|
if (currentWirePayload.trigger === "submit-message" &&
|
|
3777
3962
|
cleanedUIMessages.length > 0) {
|
|
3778
3963
|
const lastUI = cleanedUIMessages[cleanedUIMessages.length - 1];
|
|
3779
|
-
const matchedExisting = lastUI.id !== undefined &&
|
|
3780
|
-
previouslyKnownMessageIds.has(lastUI.id);
|
|
3964
|
+
const matchedExisting = lastUI.id !== undefined && previouslyKnownMessageIds.has(lastUI.id);
|
|
3781
3965
|
if (!matchedExisting) {
|
|
3782
3966
|
turnNewUIMessages.push(lastUI);
|
|
3783
3967
|
const lastModel = (await toModelMessages([lastUI]))[0];
|
|
@@ -3882,18 +4066,10 @@ function chatAgent(options) {
|
|
|
3882
4066
|
// `UIMessageStreamError: No tool invocation found`.
|
|
3883
4067
|
const pendingHandoverPartial = locals_js_1.locals.get(chatHandoverPartialKey);
|
|
3884
4068
|
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
|
-
}
|
|
4069
|
+
spliceHandoverPartial(accumulatedMessages, accumulatedUIMessages, {
|
|
4070
|
+
partialAssistantMessage: pendingHandoverPartial,
|
|
4071
|
+
messageId: locals_js_1.locals.get(chatHandoverMessageIdKey),
|
|
4072
|
+
});
|
|
3897
4073
|
locals_js_1.locals.set(chatHandoverPartialKey, []); // consume once
|
|
3898
4074
|
}
|
|
3899
4075
|
}
|
|
@@ -4120,7 +4296,8 @@ function chatAgent(options) {
|
|
|
4120
4296
|
// We call toUIMessageStream ourselves to attach onFinish for response capture.
|
|
4121
4297
|
// Pass originalMessages so the AI SDK reuses message IDs across turns
|
|
4122
4298
|
// (e.g. for tool approval continuations / HITL flows).
|
|
4123
|
-
if ((locals_js_1.locals.get(chatPipeCountKey) ?? 0) === 0 &&
|
|
4299
|
+
if ((locals_js_1.locals.get(chatPipeCountKey) ?? 0) === 0 &&
|
|
4300
|
+
isUIMessageStreamable(runResult)) {
|
|
4124
4301
|
onFinishAttached = true;
|
|
4125
4302
|
const resolvedOptions = resolveUIMessageStreamOptions();
|
|
4126
4303
|
// For action turns, don't pass originalMessages: the response
|
|
@@ -4133,9 +4310,7 @@ function chatAgent(options) {
|
|
|
4133
4310
|
// Pass originalMessages so the AI SDK reuses message IDs across
|
|
4134
4311
|
// turns (e.g. for tool approval continuations / HITL flows).
|
|
4135
4312
|
// Omit for action turns to force a fresh response ID.
|
|
4136
|
-
...(isActionTurn
|
|
4137
|
-
? {}
|
|
4138
|
-
: { originalMessages: accumulatedUIMessages }),
|
|
4313
|
+
...(isActionTurn ? {} : { originalMessages: accumulatedUIMessages }),
|
|
4139
4314
|
// Always provide generateMessageId so the start chunk carries a
|
|
4140
4315
|
// messageId. Without this, the frontend and backend generate IDs
|
|
4141
4316
|
// independently and they won't match for ID-based dedup.
|
|
@@ -4146,7 +4321,10 @@ function chatAgent(options) {
|
|
|
4146
4321
|
resolveOnFinish();
|
|
4147
4322
|
},
|
|
4148
4323
|
});
|
|
4149
|
-
await pipeChat(uiStream, {
|
|
4324
|
+
await pipeChat(uiStream, {
|
|
4325
|
+
signal: combinedSignal,
|
|
4326
|
+
spanName: "stream response",
|
|
4327
|
+
});
|
|
4150
4328
|
}
|
|
4151
4329
|
}
|
|
4152
4330
|
catch (error) {
|
|
@@ -4181,7 +4359,8 @@ function chatAgent(options) {
|
|
|
4181
4359
|
// never reports final usage), which would block the turn loop
|
|
4182
4360
|
// from ever firing onTurnComplete / writeTurnComplete.
|
|
4183
4361
|
let turnUsage;
|
|
4184
|
-
if (runResult != null &&
|
|
4362
|
+
if (runResult != null &&
|
|
4363
|
+
typeof runResult.totalUsage?.then === "function") {
|
|
4185
4364
|
try {
|
|
4186
4365
|
turnUsage = (await Promise.race([
|
|
4187
4366
|
runResult.totalUsage,
|
|
@@ -4267,7 +4446,10 @@ function chatAgent(options) {
|
|
|
4267
4446
|
// may produce a message with an empty ID since IDs are normally
|
|
4268
4447
|
// assigned by the frontend's useChat).
|
|
4269
4448
|
if (!capturedResponseMessage.id) {
|
|
4270
|
-
capturedResponseMessage = {
|
|
4449
|
+
capturedResponseMessage = {
|
|
4450
|
+
...capturedResponseMessage,
|
|
4451
|
+
id: (0, ai_runtime_js_1.generateId)(),
|
|
4452
|
+
};
|
|
4271
4453
|
}
|
|
4272
4454
|
// Append any non-transient data parts queued via chat.response or writer.write()
|
|
4273
4455
|
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
@@ -4568,7 +4750,9 @@ function chatAgent(options) {
|
|
|
4568
4750
|
? { "gen_ai.usage.total_tokens": turnUsage.totalTokens }
|
|
4569
4751
|
: {}),
|
|
4570
4752
|
...(cumulativeUsage.totalTokens != null
|
|
4571
|
-
? {
|
|
4753
|
+
? {
|
|
4754
|
+
"gen_ai.usage.cumulative_total_tokens": cumulativeUsage.totalTokens,
|
|
4755
|
+
}
|
|
4572
4756
|
: {}),
|
|
4573
4757
|
},
|
|
4574
4758
|
});
|
|
@@ -4600,9 +4784,7 @@ function chatAgent(options) {
|
|
|
4600
4784
|
savedAt: Date.now(),
|
|
4601
4785
|
messages: accumulatedUIMessages,
|
|
4602
4786
|
lastOutEventId: turnCompleteResult?.lastEventId,
|
|
4603
|
-
lastInEventId: snapshotInCursor !== undefined
|
|
4604
|
-
? String(snapshotInCursor)
|
|
4605
|
-
: undefined,
|
|
4787
|
+
lastInEventId: snapshotInCursor !== undefined ? String(snapshotInCursor) : undefined,
|
|
4606
4788
|
});
|
|
4607
4789
|
}, {
|
|
4608
4790
|
attributes: {
|
|
@@ -4644,8 +4826,7 @@ function chatAgent(options) {
|
|
|
4644
4826
|
// chat.requestUpgrade() was called — exit the loop so the
|
|
4645
4827
|
// transport triggers a new run on the latest version.
|
|
4646
4828
|
// chat.endRun() — same exit, no upgrade semantics.
|
|
4647
|
-
if (locals_js_1.locals.get(chatUpgradeRequestedKey) ||
|
|
4648
|
-
locals_js_1.locals.get(chatEndRunRequestedKey)) {
|
|
4829
|
+
if (locals_js_1.locals.get(chatUpgradeRequestedKey) || locals_js_1.locals.get(chatEndRunRequestedKey)) {
|
|
4649
4830
|
return "exit";
|
|
4650
4831
|
}
|
|
4651
4832
|
// Wait for the next message — stay idle briefly, then suspend
|
|
@@ -4725,7 +4906,9 @@ function chatAgent(options) {
|
|
|
4725
4906
|
// Turn error handler: write an error chunk + turn-complete to the stream
|
|
4726
4907
|
// so the client sees the error, then wait for the next message instead
|
|
4727
4908
|
// of killing the entire run. This keeps the conversation alive.
|
|
4728
|
-
if (turnError instanceof Error &&
|
|
4909
|
+
if (turnError instanceof Error &&
|
|
4910
|
+
turnError.name === "AbortError" &&
|
|
4911
|
+
runSignal.aborted) {
|
|
4729
4912
|
// Full run cancellation — exit immediately
|
|
4730
4913
|
throw turnError;
|
|
4731
4914
|
}
|
|
@@ -4821,9 +5004,7 @@ function chatAgent(options) {
|
|
|
4821
5004
|
savedAt: Date.now(),
|
|
4822
5005
|
messages: erroredUIMessages,
|
|
4823
5006
|
lastOutEventId: errorTurnCompleteResult?.lastEventId,
|
|
4824
|
-
lastInEventId: errorSnapshotInCursor !== undefined
|
|
4825
|
-
? String(errorSnapshotInCursor)
|
|
4826
|
-
: undefined,
|
|
5007
|
+
lastInEventId: errorSnapshotInCursor !== undefined ? String(errorSnapshotInCursor) : undefined,
|
|
4827
5008
|
});
|
|
4828
5009
|
}
|
|
4829
5010
|
catch (error) {
|
|
@@ -4834,8 +5015,7 @@ function chatAgent(options) {
|
|
|
4834
5015
|
}
|
|
4835
5016
|
}
|
|
4836
5017
|
// chat.requestUpgrade() / chat.endRun() — exit after error turn too
|
|
4837
|
-
if (locals_js_1.locals.get(chatUpgradeRequestedKey) ||
|
|
4838
|
-
locals_js_1.locals.get(chatEndRunRequestedKey)) {
|
|
5018
|
+
if (locals_js_1.locals.get(chatUpgradeRequestedKey) || locals_js_1.locals.get(chatEndRunRequestedKey)) {
|
|
4839
5019
|
return;
|
|
4840
5020
|
}
|
|
4841
5021
|
// Drain remaining recovered turns before idling — a thrown
|
|
@@ -4868,7 +5048,7 @@ function chatAgent(options) {
|
|
|
4868
5048
|
// cleanup so a missing subscription doesn't throw.
|
|
4869
5049
|
stopSub?.off();
|
|
4870
5050
|
}
|
|
4871
|
-
}
|
|
5051
|
+
},
|
|
4872
5052
|
});
|
|
4873
5053
|
// Register clientDataSchema so the CLI converts it to JSONSchema
|
|
4874
5054
|
// and stores it as payloadSchema — used by the Playground UI
|
|
@@ -4960,7 +5140,7 @@ function createChatBuilder(config) {
|
|
|
4960
5140
|
agent(options) {
|
|
4961
5141
|
const mergedUiStream = config.uiStreamOptions && options.uiMessageStreamOptions
|
|
4962
5142
|
? { ...config.uiStreamOptions, ...options.uiMessageStreamOptions }
|
|
4963
|
-
: options.uiMessageStreamOptions ?? config.uiStreamOptions;
|
|
5143
|
+
: (options.uiMessageStreamOptions ?? config.uiStreamOptions);
|
|
4964
5144
|
return chatAgent({
|
|
4965
5145
|
...options,
|
|
4966
5146
|
...(config.clientDataSchema ? { clientDataSchema: config.clientDataSchema } : {}),
|
|
@@ -5453,8 +5633,19 @@ async function pipeChatAndCapture(source, options) {
|
|
|
5453
5633
|
const onFinishPromise = new Promise((r) => {
|
|
5454
5634
|
resolveOnFinish = r;
|
|
5455
5635
|
});
|
|
5636
|
+
const resolvedOptions = resolveUIMessageStreamOptions();
|
|
5456
5637
|
const uiStream = source.toUIMessageStream({
|
|
5457
|
-
...
|
|
5638
|
+
...resolvedOptions,
|
|
5639
|
+
// Thread the prior chain (incl. a spliced handover partial) so a resumed
|
|
5640
|
+
// tool round's tool-output chunks merge into the originating tool-call
|
|
5641
|
+
// instead of throwing "No tool invocation found".
|
|
5642
|
+
...(options?.originalMessages ? { originalMessages: options.originalMessages } : {}),
|
|
5643
|
+
// Stamp a server-generated id on the start chunk, same as chat.agent's
|
|
5644
|
+
// pipe. Without it the AI SDK regenerates the assistant id when a
|
|
5645
|
+
// prepareStep injection (steering) starts a new step mid-stream, and
|
|
5646
|
+
// the frontend replaces the partial message — wiping the
|
|
5647
|
+
// pre-injection text from the UI and the captured response.
|
|
5648
|
+
generateMessageId: resolvedOptions.generateMessageId ?? ai_runtime_js_1.generateId,
|
|
5458
5649
|
onFinish: ({ responseMessage }) => {
|
|
5459
5650
|
captured = responseMessage;
|
|
5460
5651
|
resolveOnFinish();
|
|
@@ -5524,10 +5715,65 @@ class ChatMessageAccumulator {
|
|
|
5524
5715
|
this.uiMessages = [...uiMessages];
|
|
5525
5716
|
this.modelMessages = await toModelMessages(uiMessages);
|
|
5526
5717
|
}
|
|
5718
|
+
/**
|
|
5719
|
+
* Splice a `chat.headStart` handover partial into the accumulator (the warm
|
|
5720
|
+
* step-1 response). Dedups by `messageId` so a seeded/hydrated history that
|
|
5721
|
+
* already carries the partial isn't doubled. Seed any prior history first
|
|
5722
|
+
* (e.g. `setMessages(payload.headStartMessages)`). Low-level — see
|
|
5723
|
+
* `consumeHandover` for the wait+seed+apply convenience.
|
|
5724
|
+
*/
|
|
5725
|
+
applyHandover(signal) {
|
|
5726
|
+
spliceHandoverPartial(this.modelMessages, this.uiMessages, signal);
|
|
5727
|
+
}
|
|
5728
|
+
/**
|
|
5729
|
+
* One-call `chat.headStart` handover for a custom-agent loop: waits for the
|
|
5730
|
+
* handover signal, seeds prior history from `payload.headStartMessages`,
|
|
5731
|
+
* applies the warm step-1 partial, and reports what to do next.
|
|
5732
|
+
*
|
|
5733
|
+
* Returns `{ isFinal, skipped }`:
|
|
5734
|
+
* - `skipped: true` — not a `handover-prepare` run, the wait idled out, or the
|
|
5735
|
+
* warm handler aborted. Exit the run without a turn.
|
|
5736
|
+
* - `isFinal: true` — step 1 IS the response (pure text). Write turn-complete
|
|
5737
|
+
* and continue; do not call `streamText`.
|
|
5738
|
+
* - `isFinal: false` — fall through to `streamText`, which runs the pending
|
|
5739
|
+
* tool round handed over from step 1.
|
|
5740
|
+
*/
|
|
5741
|
+
async consumeHandover(options) {
|
|
5742
|
+
const signal = await waitForHandover({
|
|
5743
|
+
payload: options.payload,
|
|
5744
|
+
idleTimeoutInSeconds: options.idleTimeoutInSeconds,
|
|
5745
|
+
timeout: options.timeout,
|
|
5746
|
+
});
|
|
5747
|
+
if (!signal || signal.kind === "handover-skip") {
|
|
5748
|
+
return { isFinal: false, skipped: true };
|
|
5749
|
+
}
|
|
5750
|
+
if (options.payload.headStartMessages && options.payload.headStartMessages.length > 0) {
|
|
5751
|
+
await this.setMessages(options.payload.headStartMessages);
|
|
5752
|
+
}
|
|
5753
|
+
this.applyHandover(signal);
|
|
5754
|
+
return { isFinal: signal.isFinal, skipped: false };
|
|
5755
|
+
}
|
|
5527
5756
|
async addResponse(response) {
|
|
5528
5757
|
if (!response.id) {
|
|
5529
5758
|
response = { ...response, id: (0, ai_runtime_js_1.generateId)() };
|
|
5530
5759
|
}
|
|
5760
|
+
// Tool-approval and handover-resume continuations reuse the trailing
|
|
5761
|
+
// assistant's ID (via originalMessages on the pipe), so the captured
|
|
5762
|
+
// response can carry the same ID as a message already in the chain
|
|
5763
|
+
// (e.g. a spliced handover partial). Replace in place instead of pushing
|
|
5764
|
+
// a duplicate, mirroring the chat.agent accumulator.
|
|
5765
|
+
const existingIdx = this.uiMessages.findIndex((m) => m.id === response.id);
|
|
5766
|
+
if (existingIdx !== -1) {
|
|
5767
|
+
this.uiMessages[existingIdx] = response;
|
|
5768
|
+
try {
|
|
5769
|
+
// Reconvert all model messages since we replaced rather than appended.
|
|
5770
|
+
this.modelMessages = await toModelMessages(this.uiMessages.map((m) => stripProviderMetadata(m)));
|
|
5771
|
+
}
|
|
5772
|
+
catch {
|
|
5773
|
+
// Conversion failed — leave the existing model messages in place
|
|
5774
|
+
}
|
|
5775
|
+
return;
|
|
5776
|
+
}
|
|
5531
5777
|
this.uiMessages.push(response);
|
|
5532
5778
|
try {
|
|
5533
5779
|
const msgs = await toModelMessages([stripProviderMetadata(response)]);
|
|
@@ -5660,14 +5906,18 @@ class ChatMessageAccumulator {
|
|
|
5660
5906
|
* signaling, and idle/suspend between turns. You control: initialization,
|
|
5661
5907
|
* model/tool selection, persistence, and any custom per-turn logic.
|
|
5662
5908
|
*
|
|
5909
|
+
* Call from inside a `chat.customAgent()` run — the wrapper binds the
|
|
5910
|
+
* backing Session that the iterator's stop signal and message channels
|
|
5911
|
+
* resolve to. (A plain `task()` does not bind it, so `createSession`
|
|
5912
|
+
* would throw "session handle is not initialized".)
|
|
5913
|
+
*
|
|
5663
5914
|
* @example
|
|
5664
5915
|
* ```ts
|
|
5665
|
-
* import { task } from "@trigger.dev/sdk";
|
|
5666
5916
|
* import { chat, type ChatTaskWirePayload } from "@trigger.dev/sdk/ai";
|
|
5667
5917
|
* import { streamText } from "ai";
|
|
5668
5918
|
* import { openai } from "@ai-sdk/openai";
|
|
5669
5919
|
*
|
|
5670
|
-
* export const myChat =
|
|
5920
|
+
* export const myChat = chat.customAgent({
|
|
5671
5921
|
* id: "my-chat",
|
|
5672
5922
|
* run: async (payload: ChatTaskWirePayload, { signal }) => {
|
|
5673
5923
|
* const session = chat.createSession(payload, { signal });
|
|
@@ -5691,20 +5941,56 @@ function createChatSession(payload, options) {
|
|
|
5691
5941
|
[Symbol.asyncIterator]() {
|
|
5692
5942
|
let currentPayload = payload;
|
|
5693
5943
|
let turn = -1;
|
|
5694
|
-
|
|
5944
|
+
// Created on the first next() call, AFTER the resume-cursor seed —
|
|
5945
|
+
// createStopSignal attaches the `.in` SSE tail, and attaching
|
|
5946
|
+
// before the seed replays every record from seq 0 (the seed is a
|
|
5947
|
+
// no-op when the chatCustomAgent wrapper already ran it).
|
|
5948
|
+
let stop;
|
|
5949
|
+
let booted = false;
|
|
5695
5950
|
const accumulator = new ChatMessageAccumulator();
|
|
5696
5951
|
let previousTurnUsage;
|
|
5697
5952
|
let cumulativeUsage = emptyUsage();
|
|
5698
5953
|
return {
|
|
5699
5954
|
async next() {
|
|
5955
|
+
if (!booted) {
|
|
5956
|
+
booted = true;
|
|
5957
|
+
await seedSessionInResumeCursorForCustomLoop(currentPayload);
|
|
5958
|
+
stop = createStopSignal();
|
|
5959
|
+
}
|
|
5700
5960
|
turn++;
|
|
5961
|
+
// Head-start handover: the server triggered this run with
|
|
5962
|
+
// `trigger: "handover-prepare"` and signals the warm step-1 partial on
|
|
5963
|
+
// `session.in`. Wait for it BEFORE any `messagesInput.waitWithIdleTimeout`
|
|
5964
|
+
// (that facade consumes-and-discards non-message chunks and would swallow
|
|
5965
|
+
// the signal). Turn-0 only — continuation boots never carry this trigger.
|
|
5966
|
+
let handoverThisTurn = null;
|
|
5967
|
+
let pendingHandoverSignal = null;
|
|
5968
|
+
if (turn === 0 && currentPayload.trigger === "handover-prepare") {
|
|
5969
|
+
const signal = await waitForHandover({
|
|
5970
|
+
payload: currentPayload,
|
|
5971
|
+
idleTimeoutInSeconds: sessionIdleTimeoutOpt ??
|
|
5972
|
+
currentPayload.idleTimeoutInSeconds ??
|
|
5973
|
+
idleTimeoutInSeconds,
|
|
5974
|
+
timeout,
|
|
5975
|
+
});
|
|
5976
|
+
if (!signal || signal.kind === "handover-skip" || runSignal.aborted) {
|
|
5977
|
+
stop.cleanup();
|
|
5978
|
+
return { done: true, value: undefined };
|
|
5979
|
+
}
|
|
5980
|
+
pendingHandoverSignal = signal;
|
|
5981
|
+
handoverThisTurn = { isFinal: signal.isFinal };
|
|
5982
|
+
// Rewrite to a normal first-turn message turn so the rest of the loop
|
|
5983
|
+
// (steering setup, addIncoming, turnObj) runs unchanged.
|
|
5984
|
+
currentPayload = { ...currentPayload, trigger: "submit-message", message: undefined };
|
|
5985
|
+
}
|
|
5701
5986
|
// First turn: wait when the boot payload carries no message.
|
|
5702
5987
|
// Preload boots wait for the first real message; continuation
|
|
5703
5988
|
// boots (fresh run via `ensureRunForSession` / end-and-continue)
|
|
5704
5989
|
// arrive with the sticky boot-payload fields stripped, so running
|
|
5705
5990
|
// a turn immediately would invoke the model with no user input.
|
|
5706
5991
|
const isMessagelessContinuationBoot = currentPayload.continuation === true && !currentPayload.message;
|
|
5707
|
-
if (turn === 0 &&
|
|
5992
|
+
if (turn === 0 &&
|
|
5993
|
+
(currentPayload.trigger === "preload" || isMessagelessContinuationBoot)) {
|
|
5708
5994
|
const result = await messagesInput.waitWithIdleTimeout({
|
|
5709
5995
|
idleTimeoutInSeconds: sessionIdleTimeoutOpt ?? currentPayload.idleTimeoutInSeconds ?? 30,
|
|
5710
5996
|
timeout,
|
|
@@ -5728,8 +6014,7 @@ function createChatSession(payload, options) {
|
|
|
5728
6014
|
// Subsequent turns: wait for the next message
|
|
5729
6015
|
if (turn > 0) {
|
|
5730
6016
|
// chat.requestUpgrade() / chat.endRun() — exit before waiting
|
|
5731
|
-
if (locals_js_1.locals.get(chatUpgradeRequestedKey) ||
|
|
5732
|
-
locals_js_1.locals.get(chatEndRunRequestedKey)) {
|
|
6017
|
+
if (locals_js_1.locals.get(chatUpgradeRequestedKey) || locals_js_1.locals.get(chatEndRunRequestedKey)) {
|
|
5733
6018
|
stop.cleanup();
|
|
5734
6019
|
return { done: true, value: undefined };
|
|
5735
6020
|
}
|
|
@@ -5804,6 +6089,16 @@ function createChatSession(payload, options) {
|
|
|
5804
6089
|
? [currentPayload.message]
|
|
5805
6090
|
: [];
|
|
5806
6091
|
const messages = await accumulator.addIncoming(incomingForAccumulator, currentPayload.trigger, turn);
|
|
6092
|
+
// Apply the head-start handover AFTER addIncoming — turn-0 addIncoming
|
|
6093
|
+
// replaces accumulator state, which would wipe a pre-applied splice.
|
|
6094
|
+
// Seed prior history first, then splice the warm step-1 partial.
|
|
6095
|
+
if (pendingHandoverSignal) {
|
|
6096
|
+
const priorHistory = currentPayload.headStartMessages;
|
|
6097
|
+
if (priorHistory && priorHistory.length > 0) {
|
|
6098
|
+
await accumulator.setMessages(priorHistory);
|
|
6099
|
+
}
|
|
6100
|
+
accumulator.applyHandover(pendingHandoverSignal);
|
|
6101
|
+
}
|
|
5807
6102
|
// chat.requestUpgrade() called before this turn — signal transport and exit
|
|
5808
6103
|
if (locals_js_1.locals.get(chatUpgradeRequestedKey)) {
|
|
5809
6104
|
await writeUpgradeRequiredChunk();
|
|
@@ -5830,13 +6125,38 @@ function createChatSession(payload, options) {
|
|
|
5830
6125
|
continuation: currentPayload.continuation ?? false,
|
|
5831
6126
|
previousTurnUsage,
|
|
5832
6127
|
totalUsage: cumulativeUsage,
|
|
6128
|
+
handover: handoverThisTurn,
|
|
5833
6129
|
async setMessages(uiMessages) {
|
|
5834
6130
|
await accumulator.setMessages(uiMessages);
|
|
5835
6131
|
},
|
|
5836
6132
|
async complete(source) {
|
|
6133
|
+
// Head-start final turn: the warm step-1 partial is already spliced
|
|
6134
|
+
// into the accumulator and IS the response — nothing to pipe. Only
|
|
6135
|
+
// valid on a final handover; a missing source on any other turn is a
|
|
6136
|
+
// mistake (it would silently finalize without an assistant response).
|
|
6137
|
+
if (!source) {
|
|
6138
|
+
if (!handoverThisTurn?.isFinal) {
|
|
6139
|
+
throw new Error("turn.complete() requires a stream source unless turn.handover.isFinal is true");
|
|
6140
|
+
}
|
|
6141
|
+
const response = accumulator.uiMessages.at(-1);
|
|
6142
|
+
if (!response || response.role !== "assistant") {
|
|
6143
|
+
throw new Error("turn.complete() could not find the spliced handover response");
|
|
6144
|
+
}
|
|
6145
|
+
sessionMsgSub.off();
|
|
6146
|
+
await chatWriteTurnComplete();
|
|
6147
|
+
return response;
|
|
6148
|
+
}
|
|
5837
6149
|
let response;
|
|
5838
6150
|
try {
|
|
5839
|
-
response = await pipeChatAndCapture(source, {
|
|
6151
|
+
response = await pipeChatAndCapture(source, {
|
|
6152
|
+
signal: combinedSignal,
|
|
6153
|
+
// On a non-final handover turn, thread the spliced partial so a
|
|
6154
|
+
// resumed tool round's tool-output chunks merge into the
|
|
6155
|
+
// handed-over tool-call. Gated on the handover turn only — a
|
|
6156
|
+
// normal turn must not pass originalMessages (it would merge the
|
|
6157
|
+
// fresh response into the prior assistant message).
|
|
6158
|
+
...(handoverThisTurn ? { originalMessages: accumulator.uiMessages } : {}),
|
|
6159
|
+
});
|
|
5840
6160
|
}
|
|
5841
6161
|
catch (error) {
|
|
5842
6162
|
if (error instanceof Error && error.name === "AbortError") {
|
|
@@ -5941,7 +6261,8 @@ function createChatSession(payload, options) {
|
|
|
5941
6261
|
},
|
|
5942
6262
|
];
|
|
5943
6263
|
if (sessionCompaction.compactUIMessages) {
|
|
5944
|
-
accumulator.uiMessages =
|
|
6264
|
+
accumulator.uiMessages =
|
|
6265
|
+
await sessionCompaction.compactUIMessages(compactEvent);
|
|
5945
6266
|
}
|
|
5946
6267
|
}
|
|
5947
6268
|
}
|
|
@@ -5953,7 +6274,10 @@ function createChatSession(payload, options) {
|
|
|
5953
6274
|
// Append any non-transient data parts queued via chat.response or writer.write()
|
|
5954
6275
|
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
5955
6276
|
if (queuedParts && queuedParts.length > 0) {
|
|
5956
|
-
response = {
|
|
6277
|
+
response = {
|
|
6278
|
+
...response,
|
|
6279
|
+
parts: [...(response.parts ?? []), ...queuedParts],
|
|
6280
|
+
};
|
|
5957
6281
|
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
5958
6282
|
}
|
|
5959
6283
|
await accumulator.addResponse(response);
|
|
@@ -5991,7 +6315,8 @@ function createChatSession(payload, options) {
|
|
|
5991
6315
|
return { done: false, value: turnObj };
|
|
5992
6316
|
},
|
|
5993
6317
|
async return() {
|
|
5994
|
-
stop
|
|
6318
|
+
// `stop` only exists once next() has booted the iterator.
|
|
6319
|
+
stop?.cleanup();
|
|
5995
6320
|
return { done: true, value: undefined };
|
|
5996
6321
|
},
|
|
5997
6322
|
};
|
|
@@ -6221,6 +6546,12 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
6221
6546
|
if (!params.chatId) {
|
|
6222
6547
|
throw new Error("chat.createStartSessionAction: params.chatId is required — used as the session externalId.");
|
|
6223
6548
|
}
|
|
6549
|
+
// Scope the action to `apiClient`'s env: re-enter without it so the body
|
|
6550
|
+
// runs once, under that config (read via apiClientManager.accessToken/.baseURL).
|
|
6551
|
+
if (options?.apiClient) {
|
|
6552
|
+
const { apiClient, ...rest } = options;
|
|
6553
|
+
return v3_1.apiClientManager.runWithConfig(apiClient, () => createChatStartSessionAction(taskId, rest)(params));
|
|
6554
|
+
}
|
|
6224
6555
|
// The first run boots before the user's first message lands on
|
|
6225
6556
|
// `.in/append`, so it sees an empty `messages` array and `trigger:
|
|
6226
6557
|
// "preload"`. This matches the pre-Sessions preload semantics:
|
|
@@ -6234,8 +6565,8 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
6234
6565
|
// run-list filter by chat works without the customer having to wire it
|
|
6235
6566
|
// up. Mirrors the browser-mediated `TriggerChatTransport.doStart` path.
|
|
6236
6567
|
const userTags = params.triggerConfig?.tags ?? options?.triggerConfig?.tags ?? [];
|
|
6237
|
-
//
|
|
6238
|
-
const tags = [`chat:${params.chatId}`, ...userTags].slice(0,
|
|
6568
|
+
// SessionTriggerConfig.tags allows at most 5; the auto chat tag takes one slot.
|
|
6569
|
+
const tags = [`chat:${params.chatId}`, ...userTags].slice(0, 5);
|
|
6239
6570
|
const clientDataMetadata = params.clientData !== undefined ? { metadata: params.clientData } : {};
|
|
6240
6571
|
const triggerConfig = {
|
|
6241
6572
|
basePayload: {
|
|
@@ -6259,6 +6590,20 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
6259
6590
|
maxAttempts: params.triggerConfig?.maxAttempts ?? options?.triggerConfig?.maxAttempts,
|
|
6260
6591
|
}
|
|
6261
6592
|
: {}),
|
|
6593
|
+
...(options?.triggerConfig?.maxDuration !== undefined ||
|
|
6594
|
+
params.triggerConfig?.maxDuration !== undefined
|
|
6595
|
+
? {
|
|
6596
|
+
maxDuration: params.triggerConfig?.maxDuration ?? options?.triggerConfig?.maxDuration,
|
|
6597
|
+
}
|
|
6598
|
+
: {}),
|
|
6599
|
+
...(options?.triggerConfig?.region || params.triggerConfig?.region
|
|
6600
|
+
? { region: params.triggerConfig?.region ?? options?.triggerConfig?.region }
|
|
6601
|
+
: {}),
|
|
6602
|
+
...(options?.triggerConfig?.lockToVersion || params.triggerConfig?.lockToVersion
|
|
6603
|
+
? {
|
|
6604
|
+
lockToVersion: params.triggerConfig?.lockToVersion ?? options?.triggerConfig?.lockToVersion,
|
|
6605
|
+
}
|
|
6606
|
+
: {}),
|
|
6262
6607
|
...(options?.triggerConfig?.idleTimeoutInSeconds !== undefined ||
|
|
6263
6608
|
params.triggerConfig?.idleTimeoutInSeconds !== undefined
|
|
6264
6609
|
? {
|
|
@@ -6314,9 +6659,7 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
6314
6659
|
}
|
|
6315
6660
|
function resolveChatStartBaseURL(endpoint, chatId, option) {
|
|
6316
6661
|
const fallback = v3_1.apiClientManager.baseURL ?? "https://api.trigger.dev";
|
|
6317
|
-
const raw = typeof option === "function"
|
|
6318
|
-
? option({ endpoint, chatId })
|
|
6319
|
-
: option ?? fallback;
|
|
6662
|
+
const raw = typeof option === "function" ? option({ endpoint, chatId }) : (option ?? fallback);
|
|
6320
6663
|
return raw.replace(/\/$/, "");
|
|
6321
6664
|
}
|
|
6322
6665
|
function overrideRequestHeaders(accessToken) {
|
|
@@ -6437,10 +6780,20 @@ exports.chat = {
|
|
|
6437
6780
|
MessageAccumulator: ChatMessageAccumulator,
|
|
6438
6781
|
/** Create a chat session (async iterator). See {@link createChatSession}. */
|
|
6439
6782
|
createSession: createChatSession,
|
|
6783
|
+
/**
|
|
6784
|
+
* Wait for a `chat.headStart` handover signal inside a `chat.customAgent`
|
|
6785
|
+
* loop (turn 0). See {@link waitForHandover}. For most loops prefer the
|
|
6786
|
+
* `chat.MessageAccumulator.consumeHandover()` convenience, which also seeds
|
|
6787
|
+
* `payload.headStartMessages` and applies the partial.
|
|
6788
|
+
*/
|
|
6789
|
+
waitForHandover,
|
|
6440
6790
|
/**
|
|
6441
6791
|
* Store and retrieve a resolved prompt for the current run.
|
|
6442
6792
|
*
|
|
6443
6793
|
* - `chat.prompt.set(resolved)` — store a `ResolvedPrompt` or plain string
|
|
6794
|
+
* - `chat.prompt.set(resolved, { providerOptions })` — also attach provider
|
|
6795
|
+
* options to the system block so a provider can cache it (e.g. Anthropic
|
|
6796
|
+
* prompt caching). See the prompt-caching guide.
|
|
6444
6797
|
* - `chat.prompt()` — read the stored prompt (throws if not set)
|
|
6445
6798
|
*/
|
|
6446
6799
|
prompt: Object.assign(getChatPrompt, { set: setChatPrompt }),
|