@trigger.dev/sdk 0.0.0-chat-prerelease-20260413144407 → 0.0.0-chat-prerelease-20260414181032
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 +21 -6
- package/dist/commonjs/v3/ai.js +181 -12
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/chat.d.ts +1 -0
- package/dist/commonjs/v3/chat.js +29 -16
- package/dist/commonjs/v3/chat.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/ai.d.ts +21 -6
- package/dist/esm/v3/ai.js +181 -12
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/chat.d.ts +1 -0
- package/dist/esm/v3/chat.js +29 -16
- package/dist/esm/v3/chat.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +2 -2
package/dist/commonjs/v3/ai.d.ts
CHANGED
|
@@ -724,12 +724,18 @@ export type PipeChatOptions = {
|
|
|
724
724
|
* Set static defaults via `uiMessageStreamOptions` on `chat.agent()`, or
|
|
725
725
|
* override per-turn via `chat.setUIMessageStreamOptions()`.
|
|
726
726
|
*
|
|
727
|
-
* `onFinish
|
|
728
|
-
* they are managed internally for response capture and message accumulation.
|
|
727
|
+
* `onFinish` is omitted because it is managed internally for response capture.
|
|
729
728
|
* Use `streamText`'s `onFinish` for custom finish handling, or drop down to
|
|
730
729
|
* raw task mode with `chat.pipe()` for full control.
|
|
730
|
+
*
|
|
731
|
+
* `originalMessages` is omitted because it is automatically set from the
|
|
732
|
+
* accumulated conversation history, ensuring message IDs are reused across
|
|
733
|
+
* turns (e.g. for tool approval continuations).
|
|
734
|
+
*
|
|
735
|
+
* `generateMessageId` can be set to control ID generation for response
|
|
736
|
+
* messages (e.g. UUID-v7). If not set, the AI SDK's default `generateId` is used.
|
|
731
737
|
*/
|
|
732
|
-
export type ChatUIMessageStreamOptions<TUIM extends UIMessage = UIMessage> = Omit<UIMessageStreamOptions<TUIM>, "onFinish" | "originalMessages"
|
|
738
|
+
export type ChatUIMessageStreamOptions<TUIM extends UIMessage = UIMessage> = Omit<UIMessageStreamOptions<TUIM>, "onFinish" | "originalMessages">;
|
|
733
739
|
/**
|
|
734
740
|
* An object with a `toUIMessageStream()` method (e.g. `StreamTextResult` from `streamText()`).
|
|
735
741
|
*/
|
|
@@ -1285,9 +1291,10 @@ export type ChatAgentOptions<TIdentifier extends string, TClientDataSchema exten
|
|
|
1285
1291
|
* inside `run()` or lifecycle hooks. Per-turn values are merged on top
|
|
1286
1292
|
* of these defaults (per-turn wins on conflicts).
|
|
1287
1293
|
*
|
|
1288
|
-
* `onFinish
|
|
1289
|
-
*
|
|
1290
|
-
*
|
|
1294
|
+
* `onFinish` and `originalMessages` are managed internally and cannot be
|
|
1295
|
+
* overridden here. Use `streamText`'s `onFinish` for custom finish
|
|
1296
|
+
* handling. `generateMessageId` can be set to control response message
|
|
1297
|
+
* ID generation (e.g. UUID-v7).
|
|
1291
1298
|
*
|
|
1292
1299
|
* @example
|
|
1293
1300
|
* ```ts
|
|
@@ -2097,6 +2104,14 @@ export declare const chat: {
|
|
|
2097
2104
|
inject: typeof injectBackgroundContext;
|
|
2098
2105
|
/** Typed chat output stream for writing custom chunks or piping from subtasks. */
|
|
2099
2106
|
stream: import("@trigger.dev/core/v3").RealtimeDefinedStream<UIMessageChunk>;
|
|
2107
|
+
/** Write data parts that persist to the response message. See {@link chatResponse}. */
|
|
2108
|
+
response: {
|
|
2109
|
+
/**
|
|
2110
|
+
* Write a single chunk. Non-transient data parts are accumulated into the
|
|
2111
|
+
* response message; everything else is stream-only.
|
|
2112
|
+
*/
|
|
2113
|
+
write(part: UIMessageChunk): void;
|
|
2114
|
+
};
|
|
2100
2115
|
/** Pre-built input stream for receiving messages from the transport. */
|
|
2101
2116
|
messages: import("@trigger.dev/core/v3").RealtimeDefinedInputStream<ChatTaskWirePayload<UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>, unknown>>;
|
|
2102
2117
|
/** Create a managed stop signal wired to the stop input stream. See {@link createStopSignal}. */
|
package/dist/commonjs/v3/ai.js
CHANGED
|
@@ -243,6 +243,43 @@ exports.CHAT_STREAM_KEY = chat_constants_js_1.CHAT_STREAM_KEY;
|
|
|
243
243
|
* ```
|
|
244
244
|
*/
|
|
245
245
|
const chatStream = streams_js_1.streams.define({ id: chat_constants_js_1.CHAT_STREAM_KEY });
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
// chat.response — write data parts that persist to the response message
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
/**
|
|
250
|
+
* Write data parts that both stream to the frontend AND persist in
|
|
251
|
+
* `onTurnComplete`'s `responseMessage` and `uiMessages`.
|
|
252
|
+
*
|
|
253
|
+
* Non-transient data chunks (`type` starts with `data-`, no `transient: true`)
|
|
254
|
+
* are queued for accumulation into the assistant response message.
|
|
255
|
+
* Transient or non-data chunks are streamed only (same as `chat.stream`).
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```ts
|
|
259
|
+
* // Persists to responseMessage.parts
|
|
260
|
+
* chat.response.write({ type: "data-handover", data: { context: summary } });
|
|
261
|
+
*
|
|
262
|
+
* // Transient — streams only, not in responseMessage
|
|
263
|
+
* chat.response.write({ type: "data-progress", data: { percent: 50 }, transient: true });
|
|
264
|
+
* ```
|
|
265
|
+
*/
|
|
266
|
+
const chatResponse = {
|
|
267
|
+
/**
|
|
268
|
+
* Write a single chunk. Non-transient data parts are accumulated into the
|
|
269
|
+
* response message; everything else is stream-only.
|
|
270
|
+
*/
|
|
271
|
+
write(part) {
|
|
272
|
+
queueResponsePart(part);
|
|
273
|
+
const { waitUntilComplete } = streams_js_1.streams.writer(exports.CHAT_STREAM_KEY, {
|
|
274
|
+
spanName: "chat.response.write",
|
|
275
|
+
collapsed: true,
|
|
276
|
+
execute: ({ write }) => {
|
|
277
|
+
write(part);
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
waitUntilComplete().catch(() => { });
|
|
281
|
+
},
|
|
282
|
+
};
|
|
246
283
|
/**
|
|
247
284
|
* Creates a lazy ChatWriter that only opens a realtime stream on first use.
|
|
248
285
|
* Call `flush()` after the callback returns to await stream completion.
|
|
@@ -274,6 +311,7 @@ function createLazyChatWriter() {
|
|
|
274
311
|
writer: {
|
|
275
312
|
write(part) {
|
|
276
313
|
ensureInitialized();
|
|
314
|
+
queueResponsePart(part);
|
|
277
315
|
writeImpl(part);
|
|
278
316
|
},
|
|
279
317
|
merge(stream) {
|
|
@@ -400,6 +438,30 @@ const chatPendingMessagesKey = locals_js_1.locals.create("chat.pendingMessages")
|
|
|
400
438
|
const chatSteeringQueueKey = locals_js_1.locals.create("chat.steeringQueue");
|
|
401
439
|
/** @internal — IDs of messages that were successfully injected via prepareStep */
|
|
402
440
|
const chatInjectedMessageIdsKey = locals_js_1.locals.create("chat.injectedMessageIds");
|
|
441
|
+
/** @internal — non-transient data parts queued via chat.response or writer.write() for accumulation into the response message */
|
|
442
|
+
const chatResponsePartsKey = locals_js_1.locals.create("chat.responseParts");
|
|
443
|
+
/**
|
|
444
|
+
* Check if a chunk is a non-transient data part that should persist to the response message.
|
|
445
|
+
* @internal
|
|
446
|
+
*/
|
|
447
|
+
function isNonTransientDataPart(part) {
|
|
448
|
+
if (typeof part !== "object" || part === null)
|
|
449
|
+
return false;
|
|
450
|
+
const p = part;
|
|
451
|
+
return typeof p.type === "string" && p.type.startsWith("data-") && p.transient !== true;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Queue a chunk for accumulation into the response message (if it's a non-transient data part).
|
|
455
|
+
* Called by `chat.response.write()` and `ChatWriter.write()`.
|
|
456
|
+
* @internal
|
|
457
|
+
*/
|
|
458
|
+
function queueResponsePart(part) {
|
|
459
|
+
if (!isNonTransientDataPart(part))
|
|
460
|
+
return;
|
|
461
|
+
const parts = locals_js_1.locals.get(chatResponsePartsKey) ?? [];
|
|
462
|
+
parts.push(part);
|
|
463
|
+
locals_js_1.locals.set(chatResponsePartsKey, parts);
|
|
464
|
+
}
|
|
403
465
|
/**
|
|
404
466
|
* Check that no tool calls are in-flight in a step's content.
|
|
405
467
|
* Used before compaction to avoid losing tool state mid-execution.
|
|
@@ -536,6 +598,7 @@ async function chatCompact(messages, steps, options) {
|
|
|
536
598
|
type: "data-compaction",
|
|
537
599
|
id: compactionId,
|
|
538
600
|
data: { status: "compacting", totalTokens },
|
|
601
|
+
transient: true,
|
|
539
602
|
});
|
|
540
603
|
// Generate summary
|
|
541
604
|
summary = await options.summarize(messages);
|
|
@@ -576,6 +639,7 @@ async function chatCompact(messages, steps, options) {
|
|
|
576
639
|
type: "data-compaction",
|
|
577
640
|
id: compactionId,
|
|
578
641
|
data: { status: "complete", totalTokens },
|
|
642
|
+
transient: true,
|
|
579
643
|
});
|
|
580
644
|
write({ type: "finish-step" });
|
|
581
645
|
},
|
|
@@ -1174,6 +1238,7 @@ function chatAgent(options) {
|
|
|
1174
1238
|
locals_js_1.locals.set(chatDeferKey, new Set());
|
|
1175
1239
|
locals_js_1.locals.set(chatCompactionStateKey, undefined);
|
|
1176
1240
|
locals_js_1.locals.set(chatSteeringQueueKey, []);
|
|
1241
|
+
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
1177
1242
|
// NOTE: chatBackgroundQueueKey is NOT reset here — messages injected
|
|
1178
1243
|
// by deferred work from the previous turn's onTurnComplete need to
|
|
1179
1244
|
// survive into the next turn. The queue is drained before run().
|
|
@@ -1291,11 +1356,34 @@ function chatAgent(options) {
|
|
|
1291
1356
|
// No new user messages for regenerate — just the response (added below)
|
|
1292
1357
|
}
|
|
1293
1358
|
else {
|
|
1294
|
-
// Submit:
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1359
|
+
// Submit: check if any incoming message updates an existing one (by ID).
|
|
1360
|
+
// This handles tool approval responses, where the frontend resends the
|
|
1361
|
+
// assistant message with updated tool parts (approval-responded).
|
|
1362
|
+
// IDs match because we always pass generateMessageId + originalMessages
|
|
1363
|
+
// to toUIMessageStream, so the backend's start chunk carries the same
|
|
1364
|
+
// messageId that the frontend uses.
|
|
1365
|
+
let replaced = false;
|
|
1366
|
+
for (const incoming of cleanedUIMessages) {
|
|
1367
|
+
const idx = accumulatedUIMessages.findIndex((m) => m.id === incoming.id);
|
|
1368
|
+
if (idx !== -1) {
|
|
1369
|
+
accumulatedUIMessages[idx] = incoming;
|
|
1370
|
+
replaced = true;
|
|
1371
|
+
}
|
|
1372
|
+
else {
|
|
1373
|
+
accumulatedUIMessages.push(incoming);
|
|
1374
|
+
turnNewUIMessages.push(incoming);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
if (replaced) {
|
|
1378
|
+
// Reconvert all model messages since a replacement changes the structure
|
|
1379
|
+
accumulatedMessages = await toModelMessages(accumulatedUIMessages);
|
|
1380
|
+
}
|
|
1381
|
+
else {
|
|
1382
|
+
accumulatedMessages.push(...incomingModelMessages);
|
|
1383
|
+
}
|
|
1384
|
+
if (turnNewUIMessages.length > 0) {
|
|
1385
|
+
turnNewModelMessages.push(...(await toModelMessages(turnNewUIMessages)));
|
|
1386
|
+
}
|
|
1299
1387
|
}
|
|
1300
1388
|
// Mint a scoped public access token once per turn, reused for
|
|
1301
1389
|
// onChatStart, onTurnStart, onTurnComplete, and the turn-complete chunk.
|
|
@@ -1408,9 +1496,14 @@ function chatAgent(options) {
|
|
|
1408
1496
|
let onFinishAttached = false;
|
|
1409
1497
|
let runResult;
|
|
1410
1498
|
try {
|
|
1411
|
-
// Drain any messages injected by background work (e.g. self-review from previous turn)
|
|
1499
|
+
// Drain any messages injected by background work (e.g. self-review from previous turn).
|
|
1500
|
+
// Skip if the last message is a tool message — appending after it would
|
|
1501
|
+
// prevent streamText from finding pending tool approvals (it checks
|
|
1502
|
+
// the last message). The queued messages will be picked up by prepareStep
|
|
1503
|
+
// at the next step boundary instead.
|
|
1504
|
+
const lastAccumulated = accumulatedMessages[accumulatedMessages.length - 1];
|
|
1412
1505
|
const bgQueue = locals_js_1.locals.get(chatBackgroundQueueKey);
|
|
1413
|
-
if (bgQueue && bgQueue.length > 0) {
|
|
1506
|
+
if (bgQueue && bgQueue.length > 0 && lastAccumulated?.role !== "tool") {
|
|
1414
1507
|
accumulatedMessages.push(...bgQueue.splice(0));
|
|
1415
1508
|
}
|
|
1416
1509
|
runResult = await userRun({
|
|
@@ -1430,10 +1523,20 @@ function chatAgent(options) {
|
|
|
1430
1523
|
// Auto-pipe if the run function returned a StreamTextResult or similar,
|
|
1431
1524
|
// but only if pipeChat() wasn't already called manually during this turn.
|
|
1432
1525
|
// We call toUIMessageStream ourselves to attach onFinish for response capture.
|
|
1526
|
+
// Pass originalMessages so the AI SDK reuses message IDs across turns
|
|
1527
|
+
// (e.g. for tool approval continuations / HITL flows).
|
|
1433
1528
|
if ((locals_js_1.locals.get(chatPipeCountKey) ?? 0) === 0 && isUIMessageStreamable(runResult)) {
|
|
1434
1529
|
onFinishAttached = true;
|
|
1530
|
+
const resolvedOptions = resolveUIMessageStreamOptions();
|
|
1435
1531
|
const uiStream = runResult.toUIMessageStream({
|
|
1436
|
-
...
|
|
1532
|
+
...resolvedOptions,
|
|
1533
|
+
// Pass originalMessages so the AI SDK reuses message IDs across
|
|
1534
|
+
// turns (e.g. for tool approval continuations / HITL flows).
|
|
1535
|
+
originalMessages: accumulatedUIMessages,
|
|
1536
|
+
// Always provide generateMessageId so the start chunk carries a
|
|
1537
|
+
// messageId. Without this, the frontend and backend generate IDs
|
|
1538
|
+
// independently and they won't match for ID-based dedup.
|
|
1539
|
+
generateMessageId: resolvedOptions.generateMessageId ?? ai_1.generateId,
|
|
1437
1540
|
onFinish: ({ responseMessage }) => {
|
|
1438
1541
|
capturedResponseMessage = responseMessage;
|
|
1439
1542
|
resolveOnFinish();
|
|
@@ -1554,6 +1657,15 @@ function chatAgent(options) {
|
|
|
1554
1657
|
if (!capturedResponseMessage.id) {
|
|
1555
1658
|
capturedResponseMessage = { ...capturedResponseMessage, id: (0, ai_1.generateId)() };
|
|
1556
1659
|
}
|
|
1660
|
+
// Append any non-transient data parts queued via chat.response or writer.write()
|
|
1661
|
+
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
1662
|
+
if (queuedParts && queuedParts.length > 0) {
|
|
1663
|
+
capturedResponseMessage = {
|
|
1664
|
+
...capturedResponseMessage,
|
|
1665
|
+
parts: [...capturedResponseMessage.parts, ...queuedParts],
|
|
1666
|
+
};
|
|
1667
|
+
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
1668
|
+
}
|
|
1557
1669
|
accumulatedUIMessages.push(capturedResponseMessage);
|
|
1558
1670
|
turnNewUIMessages.push(capturedResponseMessage);
|
|
1559
1671
|
try {
|
|
@@ -1567,10 +1679,21 @@ function chatAgent(options) {
|
|
|
1567
1679
|
// Conversion failed — skip accumulation for this turn
|
|
1568
1680
|
}
|
|
1569
1681
|
}
|
|
1570
|
-
//
|
|
1571
|
-
//
|
|
1572
|
-
|
|
1573
|
-
|
|
1682
|
+
// If there's no captured response (manual pipe mode) but there are
|
|
1683
|
+
// queued data parts, create a minimal response message to hold them.
|
|
1684
|
+
if (!capturedResponseMessage) {
|
|
1685
|
+
const remainingParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
1686
|
+
if (remainingParts && remainingParts.length > 0) {
|
|
1687
|
+
capturedResponseMessage = {
|
|
1688
|
+
id: (0, ai_1.generateId)(),
|
|
1689
|
+
role: "assistant",
|
|
1690
|
+
parts: [...remainingParts],
|
|
1691
|
+
};
|
|
1692
|
+
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
1693
|
+
accumulatedUIMessages.push(capturedResponseMessage);
|
|
1694
|
+
turnNewUIMessages.push(capturedResponseMessage);
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1574
1697
|
if (runSignal.aborted)
|
|
1575
1698
|
return "exit";
|
|
1576
1699
|
// Await deferred background work (e.g. DB writes from onTurnStart)
|
|
@@ -1612,6 +1735,7 @@ function chatAgent(options) {
|
|
|
1612
1735
|
type: "data-compaction",
|
|
1613
1736
|
id: compactionId,
|
|
1614
1737
|
data: { status: "compacting", totalTokens: turnUsage.totalTokens },
|
|
1738
|
+
transient: true,
|
|
1615
1739
|
});
|
|
1616
1740
|
const summary = await outerCompaction.summarize({
|
|
1617
1741
|
messages: accumulatedMessages,
|
|
@@ -1673,6 +1797,7 @@ function chatAgent(options) {
|
|
|
1673
1797
|
type: "data-compaction",
|
|
1674
1798
|
id: compactionId,
|
|
1675
1799
|
data: { status: "complete", totalTokens: turnUsage.totalTokens },
|
|
1800
|
+
transient: true,
|
|
1676
1801
|
});
|
|
1677
1802
|
},
|
|
1678
1803
|
});
|
|
@@ -1746,6 +1871,22 @@ function chatAgent(options) {
|
|
|
1746
1871
|
},
|
|
1747
1872
|
});
|
|
1748
1873
|
}
|
|
1874
|
+
// Drain any late response parts added during onBeforeTurnComplete
|
|
1875
|
+
const lateParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
1876
|
+
if (lateParts && lateParts.length > 0 && capturedResponseMessage) {
|
|
1877
|
+
const idx = accumulatedUIMessages.findIndex((m) => m.id === capturedResponseMessage.id);
|
|
1878
|
+
if (idx !== -1) {
|
|
1879
|
+
const msg = accumulatedUIMessages[idx];
|
|
1880
|
+
accumulatedUIMessages[idx] = {
|
|
1881
|
+
...msg,
|
|
1882
|
+
parts: [...(msg.parts ?? []), ...lateParts],
|
|
1883
|
+
};
|
|
1884
|
+
capturedResponseMessage = accumulatedUIMessages[idx];
|
|
1885
|
+
turnCompleteEvent.responseMessage = capturedResponseMessage;
|
|
1886
|
+
turnCompleteEvent.uiMessages = accumulatedUIMessages;
|
|
1887
|
+
}
|
|
1888
|
+
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
1889
|
+
}
|
|
1749
1890
|
// Write turn-complete control chunk — closes the frontend stream.
|
|
1750
1891
|
const turnCompleteResult = await writeTurnCompleteChunk(currentWirePayload.chatId, turnAccessToken);
|
|
1751
1892
|
// Fire onTurnComplete — stream is closed, use for persistence.
|
|
@@ -2753,6 +2894,8 @@ function createChatSession(payload, options) {
|
|
|
2753
2894
|
}
|
|
2754
2895
|
// Reset stop signal for this turn
|
|
2755
2896
|
stop.reset();
|
|
2897
|
+
// Reset per-turn state
|
|
2898
|
+
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
2756
2899
|
// Set up steering queue and pending messages config in locals
|
|
2757
2900
|
// so toStreamTextOptions() auto-injects prepareStep for steering
|
|
2758
2901
|
const turnSteeringQueue = [];
|
|
@@ -2849,8 +2992,26 @@ function createChatSession(payload, options) {
|
|
|
2849
2992
|
const cleaned = stop.signal.aborted && !runSignal.aborted
|
|
2850
2993
|
? cleanupAbortedParts(response)
|
|
2851
2994
|
: response;
|
|
2995
|
+
// Append any non-transient data parts queued via chat.response or writer.write()
|
|
2996
|
+
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
2997
|
+
if (queuedParts && queuedParts.length > 0) {
|
|
2998
|
+
cleaned.parts = [...(cleaned.parts ?? []), ...queuedParts];
|
|
2999
|
+
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
3000
|
+
}
|
|
2852
3001
|
await accumulator.addResponse(cleaned);
|
|
2853
3002
|
}
|
|
3003
|
+
else {
|
|
3004
|
+
// No response (manual pipe mode) but there are queued data parts
|
|
3005
|
+
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
3006
|
+
if (queuedParts && queuedParts.length > 0) {
|
|
3007
|
+
await accumulator.addResponse({
|
|
3008
|
+
id: (0, ai_1.generateId)(),
|
|
3009
|
+
role: "assistant",
|
|
3010
|
+
parts: queuedParts,
|
|
3011
|
+
});
|
|
3012
|
+
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
2854
3015
|
// Capture token usage from the streamText result
|
|
2855
3016
|
let turnUsage;
|
|
2856
3017
|
if (typeof source.totalUsage?.then === "function") {
|
|
@@ -2917,6 +3078,12 @@ function createChatSession(payload, options) {
|
|
|
2917
3078
|
return response;
|
|
2918
3079
|
},
|
|
2919
3080
|
async addResponse(response) {
|
|
3081
|
+
// Append any non-transient data parts queued via chat.response or writer.write()
|
|
3082
|
+
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
3083
|
+
if (queuedParts && queuedParts.length > 0) {
|
|
3084
|
+
response = { ...response, parts: [...(response.parts ?? []), ...queuedParts] };
|
|
3085
|
+
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
3086
|
+
}
|
|
2920
3087
|
await accumulator.addResponse(response);
|
|
2921
3088
|
},
|
|
2922
3089
|
async done() {
|
|
@@ -3219,6 +3386,8 @@ exports.chat = {
|
|
|
3219
3386
|
inject: injectBackgroundContext,
|
|
3220
3387
|
/** Typed chat output stream for writing custom chunks or piping from subtasks. */
|
|
3221
3388
|
stream: chatStream,
|
|
3389
|
+
/** Write data parts that persist to the response message. See {@link chatResponse}. */
|
|
3390
|
+
response: chatResponse,
|
|
3222
3391
|
/** Pre-built input stream for receiving messages from the transport. */
|
|
3223
3392
|
messages: messagesInput,
|
|
3224
3393
|
/** Create a managed stop signal wired to the stop input stream. See {@link createStopSignal}. */
|