@trigger.dev/sdk 0.0.0-chat-prerelease-20260402124815 → 0.0.0-chat-prerelease-20260413101648
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/v3/ai.d.ts +81 -2
- package/dist/commonjs/v3/ai.js +132 -12
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/chat-client.d.ts +6 -3
- package/dist/commonjs/v3/chat-client.js +91 -11
- package/dist/commonjs/v3/chat-client.js.map +1 -1
- package/dist/commonjs/v3/chat.d.ts +45 -0
- package/dist/commonjs/v3/chat.js +136 -6
- package/dist/commonjs/v3/chat.js.map +1 -1
- package/dist/commonjs/v3/chat.test.js +13 -13
- package/dist/commonjs/v3/chat.test.js.map +1 -1
- package/dist/commonjs/v3/deployments.d.ts +26 -0
- package/dist/commonjs/v3/deployments.js +37 -0
- package/dist/commonjs/v3/deployments.js.map +1 -0
- package/dist/commonjs/v3/index.d.ts +1 -0
- package/dist/commonjs/v3/index.js +3 -1
- package/dist/commonjs/v3/index.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/ai.d.ts +81 -2
- package/dist/esm/v3/ai.js +132 -12
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/chat-client.d.ts +6 -3
- package/dist/esm/v3/chat-client.js +91 -11
- package/dist/esm/v3/chat-client.js.map +1 -1
- package/dist/esm/v3/chat.d.ts +45 -0
- package/dist/esm/v3/chat.js +136 -6
- package/dist/esm/v3/chat.js.map +1 -1
- package/dist/esm/v3/chat.test.js +13 -13
- package/dist/esm/v3/chat.test.js.map +1 -1
- package/dist/esm/v3/deployments.d.ts +26 -0
- package/dist/esm/v3/deployments.js +34 -0
- package/dist/esm/v3/deployments.js.map +1 -0
- package/dist/esm/v3/index.d.ts +1 -0
- package/dist/esm/v3/index.js +1 -0
- package/dist/esm/v3/index.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +2 -2
package/dist/commonjs/v3/ai.d.ts
CHANGED
|
@@ -832,6 +832,19 @@ export type ChatStartEvent<TClientData = unknown> = {
|
|
|
832
832
|
/** Stream writer — write custom `UIMessageChunk` parts to the chat stream. Lazy: no overhead if unused. */
|
|
833
833
|
writer: ChatWriter;
|
|
834
834
|
};
|
|
835
|
+
/**
|
|
836
|
+
* Event passed to the `onValidateMessages` callback.
|
|
837
|
+
*/
|
|
838
|
+
export type ValidateMessagesEvent<TUIM extends UIMessage = UIMessage> = {
|
|
839
|
+
/** The incoming UI messages for this turn (after cleanup of aborted tool parts). */
|
|
840
|
+
messages: TUIM[];
|
|
841
|
+
/** The unique identifier for the chat session. */
|
|
842
|
+
chatId: string;
|
|
843
|
+
/** The turn number (0-indexed). */
|
|
844
|
+
turn: number;
|
|
845
|
+
/** The trigger type for this turn. */
|
|
846
|
+
trigger: "submit-message" | "regenerate-message" | "preload" | "close";
|
|
847
|
+
};
|
|
835
848
|
/**
|
|
836
849
|
* Event passed to the `onTurnStart` callback.
|
|
837
850
|
*/
|
|
@@ -1055,6 +1068,32 @@ export type ChatAgentOptions<TIdentifier extends string, TClientDataSchema exten
|
|
|
1055
1068
|
* ```
|
|
1056
1069
|
*/
|
|
1057
1070
|
onChatStart?: (event: ChatStartEvent<inferSchemaOut<TClientDataSchema>>) => Promise<void> | void;
|
|
1071
|
+
/**
|
|
1072
|
+
* Validate or transform incoming UI messages before they are converted to model
|
|
1073
|
+
* messages and accumulated. Fires once per turn with the raw `UIMessage[]` from
|
|
1074
|
+
* the wire payload (after cleanup of aborted tool parts).
|
|
1075
|
+
*
|
|
1076
|
+
* Return the validated messages array. Throw to abort the turn with an error.
|
|
1077
|
+
*
|
|
1078
|
+
* This is the right place to call the AI SDK's `validateUIMessages` to catch
|
|
1079
|
+
* malformed messages from storage or untrusted input before they reach the model.
|
|
1080
|
+
*
|
|
1081
|
+
* @example
|
|
1082
|
+
* ```ts
|
|
1083
|
+
* import { validateUIMessages } from "ai";
|
|
1084
|
+
*
|
|
1085
|
+
* chat.agent({
|
|
1086
|
+
* id: "my-chat",
|
|
1087
|
+
* onValidateMessages: async ({ messages }) => {
|
|
1088
|
+
* return validateUIMessages({ messages, tools: chatTools });
|
|
1089
|
+
* },
|
|
1090
|
+
* run: async ({ messages }) => {
|
|
1091
|
+
* return streamText({ model, messages, tools: chatTools });
|
|
1092
|
+
* },
|
|
1093
|
+
* });
|
|
1094
|
+
* ```
|
|
1095
|
+
*/
|
|
1096
|
+
onValidateMessages?: (event: ValidateMessagesEvent<TUIMessage>) => TUIMessage[] | Promise<TUIMessage[]>;
|
|
1058
1097
|
/**
|
|
1059
1098
|
* Called at the start of every turn, after message accumulation and `onChatStart` (turn 0),
|
|
1060
1099
|
* but before the `run` function executes.
|
|
@@ -1199,6 +1238,8 @@ export type ChatAgentOptions<TIdentifier extends string, TClientDataSchema exten
|
|
|
1199
1238
|
* waiting for the first message before suspending.
|
|
1200
1239
|
*
|
|
1201
1240
|
* Only applies to preloaded runs (triggered via `transport.preload()`).
|
|
1241
|
+
* Takes precedence over `transport.preload(..., { idleTimeoutInSeconds })`
|
|
1242
|
+
* and over {@link ChatAgentOptions.idleTimeoutInSeconds}.
|
|
1202
1243
|
*
|
|
1203
1244
|
* @default Same as `idleTimeoutInSeconds`
|
|
1204
1245
|
*/
|
|
@@ -1554,6 +1595,36 @@ declare function setUIMessageStreamOptions(options: ChatUIMessageStreamOptions<U
|
|
|
1554
1595
|
* ```
|
|
1555
1596
|
*/
|
|
1556
1597
|
declare function isStopped(): boolean;
|
|
1598
|
+
/**
|
|
1599
|
+
* Request that the current run exits so the next message starts on the latest
|
|
1600
|
+
* deployed version (via the standard continuation mechanism).
|
|
1601
|
+
*
|
|
1602
|
+
* When called from `onTurnStart` or `onValidateMessages`, `run()` is skipped
|
|
1603
|
+
* entirely — the run exits immediately and the transport re-triggers the
|
|
1604
|
+
* same message on the new version.
|
|
1605
|
+
*
|
|
1606
|
+
* When called from `run()` or `chat.defer()`, the current turn completes
|
|
1607
|
+
* normally and the run exits afterward instead of waiting for the next message.
|
|
1608
|
+
*
|
|
1609
|
+
* Call from `onTurnStart`, `onValidateMessages`, `onChatResume`, `run()`,
|
|
1610
|
+
* or inside `chat.defer()`.
|
|
1611
|
+
*
|
|
1612
|
+
* @example
|
|
1613
|
+
* ```ts
|
|
1614
|
+
* const SUPPORTED_VERSIONS = new Set(["v2", "v3"]);
|
|
1615
|
+
*
|
|
1616
|
+
* chat.agent({
|
|
1617
|
+
* id: "my-chat",
|
|
1618
|
+
* onTurnStart: async ({ clientData }) => {
|
|
1619
|
+
* if (clientData?.protocolVersion && !SUPPORTED_VERSIONS.has(clientData.protocolVersion)) {
|
|
1620
|
+
* chat.requestUpgrade();
|
|
1621
|
+
* }
|
|
1622
|
+
* },
|
|
1623
|
+
* run: async ({ messages }) => { ... },
|
|
1624
|
+
* });
|
|
1625
|
+
* ```
|
|
1626
|
+
*/
|
|
1627
|
+
declare function requestUpgrade(): void;
|
|
1557
1628
|
/**
|
|
1558
1629
|
* Register a promise that runs in the background during the current turn.
|
|
1559
1630
|
*
|
|
@@ -1564,12 +1635,18 @@ declare function isStopped(): boolean;
|
|
|
1564
1635
|
* @example
|
|
1565
1636
|
* ```ts
|
|
1566
1637
|
* onTurnStart: async ({ chatId, uiMessages }) => {
|
|
1567
|
-
* //
|
|
1638
|
+
* // Pass a promise directly
|
|
1568
1639
|
* chat.defer(db.chat.update({ where: { id: chatId }, data: { messages: uiMessages } }));
|
|
1640
|
+
*
|
|
1641
|
+
* // Or pass an async function — cleaner for multi-step work
|
|
1642
|
+
* chat.defer(async () => {
|
|
1643
|
+
* const flags = await getFeatureFlags();
|
|
1644
|
+
* if (flags.forceUpgrade) chat.requestUpgrade();
|
|
1645
|
+
* });
|
|
1569
1646
|
* },
|
|
1570
1647
|
* ```
|
|
1571
1648
|
*/
|
|
1572
|
-
declare function chatDefer(
|
|
1649
|
+
declare function chatDefer(promiseOrFn: Promise<unknown> | (() => Promise<unknown>)): void;
|
|
1573
1650
|
/**
|
|
1574
1651
|
* Queue model messages for injection at the next `prepareStep` boundary.
|
|
1575
1652
|
*
|
|
@@ -2010,6 +2087,8 @@ export declare const chat: {
|
|
|
2010
2087
|
setUIMessageStreamOptions: typeof setUIMessageStreamOptions;
|
|
2011
2088
|
/** Check if the current turn was stopped by the user. See {@link isStopped}. */
|
|
2012
2089
|
isStopped: typeof isStopped;
|
|
2090
|
+
/** Request that the run exits after the current turn so the next message starts on the latest version. See {@link requestUpgrade}. */
|
|
2091
|
+
requestUpgrade: typeof requestUpgrade;
|
|
2013
2092
|
/** Clean up aborted parts from a UIMessage. See {@link cleanupAbortedParts}. */
|
|
2014
2093
|
cleanupAbortedParts: typeof cleanupAbortedParts;
|
|
2015
2094
|
/** Register background work that runs in parallel with streaming. See {@link chatDefer}. */
|
package/dist/commonjs/v3/ai.js
CHANGED
|
@@ -384,6 +384,8 @@ const chatOnCompactedKey = locals_js_1.locals.create("chat.onCompacted");
|
|
|
384
384
|
/** @internal Full task `ctx` for the active `chat.agent` run (for hooks invoked from nested compaction). */
|
|
385
385
|
const chatAgentRunContextKey = locals_js_1.locals.create("chat.agentRunContext");
|
|
386
386
|
const chatPrepareMessagesKey = locals_js_1.locals.create("chat.prepareMessages");
|
|
387
|
+
/** @internal Flag set by `chat.requestUpgrade()` to exit the loop after the current turn. */
|
|
388
|
+
const chatUpgradeRequestedKey = locals_js_1.locals.create("chat.upgradeRequested");
|
|
387
389
|
/** @internal */
|
|
388
390
|
const chatAgentCompactionKey = locals_js_1.locals.create("chat.agentCompaction");
|
|
389
391
|
/**
|
|
@@ -975,7 +977,7 @@ function chatCustomAgent(options) {
|
|
|
975
977
|
return task;
|
|
976
978
|
}
|
|
977
979
|
function chatAgent(options) {
|
|
978
|
-
const { run: userRun, clientDataSchema, onPreload, onChatStart, onTurnStart, onBeforeTurnComplete, onCompacted, compaction, pendingMessages: pendingMessagesConfig, prepareMessages, onTurnComplete, maxTurns = 100, turnTimeout = "1h", idleTimeoutInSeconds = 30, chatAccessTokenTTL = "1h", preloadIdleTimeoutInSeconds, preloadTimeout, uiMessageStreamOptions, onChatSuspend, onChatResume, exitAfterPreloadIdle = false, ...restOptions } = options;
|
|
980
|
+
const { run: userRun, clientDataSchema, onPreload, onChatStart, onValidateMessages, onTurnStart, onBeforeTurnComplete, onCompacted, compaction, pendingMessages: pendingMessagesConfig, prepareMessages, onTurnComplete, maxTurns = 100, turnTimeout = "1h", idleTimeoutInSeconds = 30, chatAccessTokenTTL = "1h", preloadIdleTimeoutInSeconds, preloadTimeout, uiMessageStreamOptions, onChatSuspend, onChatResume, exitAfterPreloadIdle = false, ...restOptions } = options;
|
|
979
981
|
const parseClientData = clientDataSchema ? (0, v3_1.getSchemaParseFn)(clientDataSchema) : undefined;
|
|
980
982
|
const task = (0, shared_js_1.createTask)({
|
|
981
983
|
retry: { maxAttempts: 1 },
|
|
@@ -1073,8 +1075,12 @@ function chatAgent(options) {
|
|
|
1073
1075
|
},
|
|
1074
1076
|
});
|
|
1075
1077
|
}
|
|
1076
|
-
// Wait for the first real message —
|
|
1077
|
-
|
|
1078
|
+
// Wait for the first real message — task-level idle settings win over
|
|
1079
|
+
// `transport.preload(..., { idleTimeoutInSeconds })` / wire payload so
|
|
1080
|
+
// `chat.agent({ idleTimeoutInSeconds, preloadIdleTimeoutInSeconds })` is authoritative.
|
|
1081
|
+
const effectivePreloadIdleTimeout = preloadIdleTimeoutInSeconds ??
|
|
1082
|
+
idleTimeoutInSeconds ??
|
|
1083
|
+
payload.idleTimeoutInSeconds;
|
|
1078
1084
|
const effectivePreloadTimeout = metadata_js_1.metadata.get(TURN_TIMEOUT_METADATA_KEY) ??
|
|
1079
1085
|
preloadTimeout ??
|
|
1080
1086
|
turnTimeout;
|
|
@@ -1236,7 +1242,27 @@ function chatAgent(options) {
|
|
|
1236
1242
|
// useChat state may still contain assistant messages with tool parts
|
|
1237
1243
|
// in partial/input-available state. These cause API errors (e.g.
|
|
1238
1244
|
// Anthropic requires every tool_use to have a matching tool_result).
|
|
1239
|
-
|
|
1245
|
+
let cleanedUIMessages = uiMessages.map((msg) => msg.role === "assistant" ? cleanupAbortedParts(msg) : msg);
|
|
1246
|
+
// Validate/transform UIMessages before conversion — catches malformed
|
|
1247
|
+
// messages from storage or untrusted input before they reach the model.
|
|
1248
|
+
if (onValidateMessages) {
|
|
1249
|
+
cleanedUIMessages = await tracer_js_1.tracer.startActiveSpan("onValidateMessages()", async () => {
|
|
1250
|
+
return onValidateMessages({
|
|
1251
|
+
messages: cleanedUIMessages,
|
|
1252
|
+
chatId: currentWirePayload.chatId,
|
|
1253
|
+
turn,
|
|
1254
|
+
trigger: currentWirePayload.trigger,
|
|
1255
|
+
});
|
|
1256
|
+
}, {
|
|
1257
|
+
attributes: {
|
|
1258
|
+
[v3_1.SemanticInternalAttributes.STYLE_ICON]: "task-hook-onStart",
|
|
1259
|
+
[v3_1.SemanticInternalAttributes.COLLAPSED]: true,
|
|
1260
|
+
"chat.id": currentWirePayload.chatId,
|
|
1261
|
+
"chat.turn": turn + 1,
|
|
1262
|
+
"chat.messages.count": cleanedUIMessages.length,
|
|
1263
|
+
},
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1240
1266
|
// Convert the incoming UIMessages to model messages and update the accumulator.
|
|
1241
1267
|
// Turn 1: full history from the frontend → replaces the accumulator.
|
|
1242
1268
|
// Turn 2+: only the new message(s) → appended to the accumulator.
|
|
@@ -1360,6 +1386,13 @@ function chatAgent(options) {
|
|
|
1360
1386
|
},
|
|
1361
1387
|
});
|
|
1362
1388
|
}
|
|
1389
|
+
// chat.requestUpgrade() called in onTurnStart (or onValidateMessages) —
|
|
1390
|
+
// skip run() and signal the transport to re-trigger the same message
|
|
1391
|
+
// on the new version.
|
|
1392
|
+
if (locals_js_1.locals.get(chatUpgradeRequestedKey)) {
|
|
1393
|
+
await writeUpgradeRequiredChunk();
|
|
1394
|
+
return "exit";
|
|
1395
|
+
}
|
|
1363
1396
|
// Captured by the onFinish callback below — works even on abort/stop.
|
|
1364
1397
|
let capturedResponseMessage;
|
|
1365
1398
|
// Promise that resolves when the AI SDK's onFinish fires.
|
|
@@ -1425,8 +1458,13 @@ function chatAgent(options) {
|
|
|
1425
1458
|
}
|
|
1426
1459
|
// Wait for onFinish to fire — on abort this may resolve slightly
|
|
1427
1460
|
// after pipeChat, since the stream's cancel() handler is async.
|
|
1461
|
+
// Race with a timeout so a stop-abort that prevents onFinish from
|
|
1462
|
+
// firing doesn't hang the turn loop indefinitely.
|
|
1428
1463
|
if (onFinishAttached) {
|
|
1429
|
-
await
|
|
1464
|
+
await Promise.race([
|
|
1465
|
+
onFinishPromise,
|
|
1466
|
+
new Promise((r) => setTimeout(r, 2_000)),
|
|
1467
|
+
]);
|
|
1430
1468
|
}
|
|
1431
1469
|
// Capture token usage from the streamText result (if available).
|
|
1432
1470
|
// totalUsage is a PromiseLike that resolves after the stream is consumed.
|
|
@@ -1714,7 +1752,7 @@ function chatAgent(options) {
|
|
|
1714
1752
|
await tracer_js_1.tracer.startActiveSpan("onTurnComplete()", async () => {
|
|
1715
1753
|
await onTurnComplete({
|
|
1716
1754
|
...turnCompleteEvent,
|
|
1717
|
-
lastEventId: turnCompleteResult
|
|
1755
|
+
lastEventId: turnCompleteResult?.lastEventId,
|
|
1718
1756
|
});
|
|
1719
1757
|
// Check if onTurnComplete replaced messages (compaction)
|
|
1720
1758
|
const turnCompleteOverride = locals_js_1.locals.get(chatOverrideMessagesKey);
|
|
@@ -1762,6 +1800,11 @@ function chatAgent(options) {
|
|
|
1762
1800
|
currentWirePayload = pendingMessages[0];
|
|
1763
1801
|
return "continue";
|
|
1764
1802
|
}
|
|
1803
|
+
// chat.requestUpgrade() was called — exit the loop so the
|
|
1804
|
+
// transport triggers a new run on the latest version.
|
|
1805
|
+
if (locals_js_1.locals.get(chatUpgradeRequestedKey)) {
|
|
1806
|
+
return "exit";
|
|
1807
|
+
}
|
|
1765
1808
|
// Wait for the next message — stay idle briefly, then suspend
|
|
1766
1809
|
const effectiveIdleTimeout = metadata_js_1.metadata.get(IDLE_TIMEOUT_METADATA_KEY) ??
|
|
1767
1810
|
idleTimeoutInSeconds;
|
|
@@ -1854,6 +1897,10 @@ function chatAgent(options) {
|
|
|
1854
1897
|
catch {
|
|
1855
1898
|
// Best-effort — if stream write fails, let the run continue anyway
|
|
1856
1899
|
}
|
|
1900
|
+
// chat.requestUpgrade() — exit after error turn too
|
|
1901
|
+
if (locals_js_1.locals.get(chatUpgradeRequestedKey)) {
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1857
1904
|
// Wait for the next message — same as after a successful turn
|
|
1858
1905
|
const effectiveIdleTimeout = metadata_js_1.metadata.get(IDLE_TIMEOUT_METADATA_KEY) ??
|
|
1859
1906
|
idleTimeoutInSeconds;
|
|
@@ -2187,6 +2234,41 @@ function isStopped() {
|
|
|
2187
2234
|
return controller?.signal.aborted ?? false;
|
|
2188
2235
|
}
|
|
2189
2236
|
// ---------------------------------------------------------------------------
|
|
2237
|
+
// Version upgrade
|
|
2238
|
+
// ---------------------------------------------------------------------------
|
|
2239
|
+
/**
|
|
2240
|
+
* Request that the current run exits so the next message starts on the latest
|
|
2241
|
+
* deployed version (via the standard continuation mechanism).
|
|
2242
|
+
*
|
|
2243
|
+
* When called from `onTurnStart` or `onValidateMessages`, `run()` is skipped
|
|
2244
|
+
* entirely — the run exits immediately and the transport re-triggers the
|
|
2245
|
+
* same message on the new version.
|
|
2246
|
+
*
|
|
2247
|
+
* When called from `run()` or `chat.defer()`, the current turn completes
|
|
2248
|
+
* normally and the run exits afterward instead of waiting for the next message.
|
|
2249
|
+
*
|
|
2250
|
+
* Call from `onTurnStart`, `onValidateMessages`, `onChatResume`, `run()`,
|
|
2251
|
+
* or inside `chat.defer()`.
|
|
2252
|
+
*
|
|
2253
|
+
* @example
|
|
2254
|
+
* ```ts
|
|
2255
|
+
* const SUPPORTED_VERSIONS = new Set(["v2", "v3"]);
|
|
2256
|
+
*
|
|
2257
|
+
* chat.agent({
|
|
2258
|
+
* id: "my-chat",
|
|
2259
|
+
* onTurnStart: async ({ clientData }) => {
|
|
2260
|
+
* if (clientData?.protocolVersion && !SUPPORTED_VERSIONS.has(clientData.protocolVersion)) {
|
|
2261
|
+
* chat.requestUpgrade();
|
|
2262
|
+
* }
|
|
2263
|
+
* },
|
|
2264
|
+
* run: async ({ messages }) => { ... },
|
|
2265
|
+
* });
|
|
2266
|
+
* ```
|
|
2267
|
+
*/
|
|
2268
|
+
function requestUpgrade() {
|
|
2269
|
+
locals_js_1.locals.set(chatUpgradeRequestedKey, true);
|
|
2270
|
+
}
|
|
2271
|
+
// ---------------------------------------------------------------------------
|
|
2190
2272
|
// Per-turn deferred work
|
|
2191
2273
|
// ---------------------------------------------------------------------------
|
|
2192
2274
|
/**
|
|
@@ -2199,15 +2281,21 @@ function isStopped() {
|
|
|
2199
2281
|
* @example
|
|
2200
2282
|
* ```ts
|
|
2201
2283
|
* onTurnStart: async ({ chatId, uiMessages }) => {
|
|
2202
|
-
* //
|
|
2284
|
+
* // Pass a promise directly
|
|
2203
2285
|
* chat.defer(db.chat.update({ where: { id: chatId }, data: { messages: uiMessages } }));
|
|
2286
|
+
*
|
|
2287
|
+
* // Or pass an async function — cleaner for multi-step work
|
|
2288
|
+
* chat.defer(async () => {
|
|
2289
|
+
* const flags = await getFeatureFlags();
|
|
2290
|
+
* if (flags.forceUpgrade) chat.requestUpgrade();
|
|
2291
|
+
* });
|
|
2204
2292
|
* },
|
|
2205
2293
|
* ```
|
|
2206
2294
|
*/
|
|
2207
|
-
function chatDefer(
|
|
2295
|
+
function chatDefer(promiseOrFn) {
|
|
2208
2296
|
const promises = locals_js_1.locals.get(chatDeferKey);
|
|
2209
2297
|
if (promises) {
|
|
2210
|
-
promises.add(
|
|
2298
|
+
promises.add(typeof promiseOrFn === "function" ? promiseOrFn() : promiseOrFn);
|
|
2211
2299
|
}
|
|
2212
2300
|
}
|
|
2213
2301
|
// ---------------------------------------------------------------------------
|
|
@@ -2613,7 +2701,8 @@ class ChatMessageAccumulator {
|
|
|
2613
2701
|
* ```
|
|
2614
2702
|
*/
|
|
2615
2703
|
function createChatSession(payload, options) {
|
|
2616
|
-
const { signal: runSignal, idleTimeoutInSeconds
|
|
2704
|
+
const { signal: runSignal, idleTimeoutInSeconds: sessionIdleTimeoutOpt, timeout = "1h", maxTurns = 100, compaction: sessionCompaction, pendingMessages: sessionPendingMessages, } = options;
|
|
2705
|
+
const idleTimeoutInSeconds = sessionIdleTimeoutOpt ?? 30;
|
|
2617
2706
|
return {
|
|
2618
2707
|
[Symbol.asyncIterator]() {
|
|
2619
2708
|
let currentPayload = payload;
|
|
@@ -2628,7 +2717,7 @@ function createChatSession(payload, options) {
|
|
|
2628
2717
|
// First turn: handle preload — wait for the first real message
|
|
2629
2718
|
if (turn === 0 && currentPayload.trigger === "preload") {
|
|
2630
2719
|
const result = await messagesInput.waitWithIdleTimeout({
|
|
2631
|
-
idleTimeoutInSeconds: currentPayload.idleTimeoutInSeconds ??
|
|
2720
|
+
idleTimeoutInSeconds: sessionIdleTimeoutOpt ?? currentPayload.idleTimeoutInSeconds ?? 30,
|
|
2632
2721
|
timeout,
|
|
2633
2722
|
spanName: "waiting for first message",
|
|
2634
2723
|
});
|
|
@@ -2640,6 +2729,11 @@ function createChatSession(payload, options) {
|
|
|
2640
2729
|
}
|
|
2641
2730
|
// Subsequent turns: wait for the next message
|
|
2642
2731
|
if (turn > 0) {
|
|
2732
|
+
// chat.requestUpgrade() — exit before waiting
|
|
2733
|
+
if (locals_js_1.locals.get(chatUpgradeRequestedKey)) {
|
|
2734
|
+
stop.cleanup();
|
|
2735
|
+
return { done: true, value: undefined };
|
|
2736
|
+
}
|
|
2643
2737
|
const next = await messagesInput.waitWithIdleTimeout({
|
|
2644
2738
|
idleTimeoutInSeconds,
|
|
2645
2739
|
timeout,
|
|
@@ -2702,6 +2796,13 @@ function createChatSession(payload, options) {
|
|
|
2702
2796
|
});
|
|
2703
2797
|
// Accumulate messages
|
|
2704
2798
|
const messages = await accumulator.addIncoming(currentPayload.messages, currentPayload.trigger, turn);
|
|
2799
|
+
// chat.requestUpgrade() called before this turn — signal transport and exit
|
|
2800
|
+
if (locals_js_1.locals.get(chatUpgradeRequestedKey)) {
|
|
2801
|
+
await writeUpgradeRequiredChunk();
|
|
2802
|
+
sessionMsgSub.off();
|
|
2803
|
+
stop.cleanup();
|
|
2804
|
+
return { done: true, value: undefined };
|
|
2805
|
+
}
|
|
2705
2806
|
const combinedSignal = AbortSignal.any([runSignal, stop.signal]);
|
|
2706
2807
|
const turnObj = {
|
|
2707
2808
|
number: turn,
|
|
@@ -3107,6 +3208,8 @@ exports.chat = {
|
|
|
3107
3208
|
setUIMessageStreamOptions,
|
|
3108
3209
|
/** Check if the current turn was stopped by the user. See {@link isStopped}. */
|
|
3109
3210
|
isStopped,
|
|
3211
|
+
/** Request that the run exits after the current turn so the next message starts on the latest version. See {@link requestUpgrade}. */
|
|
3212
|
+
requestUpgrade,
|
|
3110
3213
|
/** Clean up aborted parts from a UIMessage. See {@link cleanupAbortedParts}. */
|
|
3111
3214
|
cleanupAbortedParts,
|
|
3112
3215
|
/** Register background work that runs in parallel with streaming. See {@link chatDefer}. */
|
|
@@ -3166,13 +3269,30 @@ async function writeTurnCompleteChunk(chatId, publicAccessToken) {
|
|
|
3166
3269
|
collapsed: true,
|
|
3167
3270
|
execute: ({ write }) => {
|
|
3168
3271
|
write({
|
|
3169
|
-
type: "
|
|
3272
|
+
type: "trigger:turn-complete",
|
|
3170
3273
|
...(publicAccessToken ? { publicAccessToken } : {}),
|
|
3171
3274
|
});
|
|
3172
3275
|
},
|
|
3173
3276
|
});
|
|
3174
3277
|
return await waitUntilComplete();
|
|
3175
3278
|
}
|
|
3279
|
+
/**
|
|
3280
|
+
* Writes an upgrade-required control chunk to the chat output stream.
|
|
3281
|
+
* The transport intercepts this to re-trigger the same message on the latest version.
|
|
3282
|
+
* @internal
|
|
3283
|
+
*/
|
|
3284
|
+
async function writeUpgradeRequiredChunk() {
|
|
3285
|
+
const { waitUntilComplete } = streams_js_1.streams.writer(exports.CHAT_STREAM_KEY, {
|
|
3286
|
+
spanName: "upgrade required",
|
|
3287
|
+
collapsed: true,
|
|
3288
|
+
execute: ({ write }) => {
|
|
3289
|
+
write({
|
|
3290
|
+
type: "trigger:upgrade-required",
|
|
3291
|
+
});
|
|
3292
|
+
},
|
|
3293
|
+
});
|
|
3294
|
+
return await waitUntilComplete();
|
|
3295
|
+
}
|
|
3176
3296
|
/**
|
|
3177
3297
|
* Extracts the text content of the last user message from a UIMessage array.
|
|
3178
3298
|
* Returns undefined if no user message is found.
|