@trigger.dev/sdk 4.6.3 → 4.7.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.d.ts +5 -2
- package/dist/commonjs/v3/ai.js +189 -266
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/chat-client.js +7 -0
- package/dist/commonjs/v3/chat-client.js.map +1 -1
- package/dist/commonjs/v3/chat-server.d.ts +1 -0
- package/dist/commonjs/v3/chat-server.js +8 -0
- package/dist/commonjs/v3/chat-server.js.map +1 -1
- package/dist/commonjs/v3/chat.d.ts +28 -4
- package/dist/commonjs/v3/chat.js +41 -9
- package/dist/commonjs/v3/chat.js.map +1 -1
- package/dist/commonjs/v3/chatRouteWait.d.ts +21 -0
- package/dist/commonjs/v3/chatRouteWait.js +43 -0
- package/dist/commonjs/v3/chatRouteWait.js.map +1 -0
- package/dist/commonjs/v3/compactionResponse.js +5 -0
- package/dist/commonjs/v3/compactionResponse.js.map +1 -1
- package/dist/commonjs/v3/concurrency-shared.d.ts +13 -0
- package/dist/commonjs/v3/concurrency-shared.js +35 -0
- package/dist/commonjs/v3/concurrency-shared.js.map +1 -0
- package/dist/commonjs/v3/concurrencyLimits.d.ts +73 -0
- package/dist/commonjs/v3/concurrencyLimits.js +166 -0
- package/dist/commonjs/v3/concurrencyLimits.js.map +1 -0
- package/dist/commonjs/v3/index.d.ts +2 -1
- package/dist/commonjs/v3/index.js +3 -1
- package/dist/commonjs/v3/index.js.map +1 -1
- package/dist/commonjs/v3/managedChatResponse.d.ts +44 -0
- package/dist/commonjs/v3/managedChatResponse.js +233 -0
- package/dist/commonjs/v3/managedChatResponse.js.map +1 -0
- package/dist/commonjs/v3/queues.d.ts +31 -0
- package/dist/commonjs/v3/queues.js +31 -0
- package/dist/commonjs/v3/queues.js.map +1 -1
- package/dist/commonjs/v3/shared.d.ts +18 -1
- package/dist/commonjs/v3/shared.js +137 -47
- package/dist/commonjs/v3/shared.js.map +1 -1
- package/dist/commonjs/v3/steeringContext.d.ts +41 -0
- package/dist/commonjs/v3/steeringContext.js +118 -0
- package/dist/commonjs/v3/steeringContext.js.map +1 -0
- package/dist/commonjs/v3/transcriptStorage.d.ts +4 -1
- package/dist/commonjs/v3/transcriptStorage.js +51 -4
- package/dist/commonjs/v3/transcriptStorage.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/ai.d.ts +5 -2
- package/dist/esm/v3/ai.js +189 -266
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/chat-client.js +7 -0
- package/dist/esm/v3/chat-client.js.map +1 -1
- package/dist/esm/v3/chat-server.d.ts +1 -0
- package/dist/esm/v3/chat-server.js +8 -0
- package/dist/esm/v3/chat-server.js.map +1 -1
- package/dist/esm/v3/chat.d.ts +28 -4
- package/dist/esm/v3/chat.js +41 -9
- package/dist/esm/v3/chat.js.map +1 -1
- package/dist/esm/v3/chatRouteWait.d.ts +21 -0
- package/dist/esm/v3/chatRouteWait.js +40 -0
- package/dist/esm/v3/chatRouteWait.js.map +1 -0
- package/dist/esm/v3/compactionResponse.js +5 -0
- package/dist/esm/v3/compactionResponse.js.map +1 -1
- package/dist/esm/v3/concurrency-shared.d.ts +13 -0
- package/dist/esm/v3/concurrency-shared.js +31 -0
- package/dist/esm/v3/concurrency-shared.js.map +1 -0
- package/dist/esm/v3/concurrencyLimits.d.ts +73 -0
- package/dist/esm/v3/concurrencyLimits.js +158 -0
- package/dist/esm/v3/concurrencyLimits.js.map +1 -0
- package/dist/esm/v3/index.d.ts +2 -1
- package/dist/esm/v3/index.js +2 -1
- package/dist/esm/v3/index.js.map +1 -1
- package/dist/esm/v3/managedChatResponse.d.ts +44 -0
- package/dist/esm/v3/managedChatResponse.js +228 -0
- package/dist/esm/v3/managedChatResponse.js.map +1 -0
- package/dist/esm/v3/queues.d.ts +31 -0
- package/dist/esm/v3/queues.js +31 -0
- package/dist/esm/v3/queues.js.map +1 -1
- package/dist/esm/v3/shared.d.ts +18 -1
- package/dist/esm/v3/shared.js +136 -47
- package/dist/esm/v3/shared.js.map +1 -1
- package/dist/esm/v3/steeringContext.d.ts +41 -0
- package/dist/esm/v3/steeringContext.js +113 -0
- package/dist/esm/v3/steeringContext.js.map +1 -0
- package/dist/esm/v3/transcriptStorage.d.ts +4 -1
- package/dist/esm/v3/transcriptStorage.js +51 -4
- package/dist/esm/v3/transcriptStorage.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/docs/ai-chat/client-protocol.mdx +3 -1
- package/docs/ai-chat/error-handling.mdx +44 -76
- package/docs/ai-chat/fast-starts.mdx +1 -1
- package/docs/ai-chat/frontend.mdx +27 -21
- package/docs/ai-chat/patterns/branching-conversations.mdx +95 -230
- package/docs/ai-chat/patterns/human-in-the-loop.mdx +166 -164
- package/docs/ai-chat/patterns/tool-result-auditing.mdx +28 -27
- package/docs/ai-chat/patterns/version-upgrades.mdx +4 -4
- package/docs/ai-chat/pending-messages.mdx +19 -5
- package/docs/ai-chat/quick-start.mdx +26 -20
- package/docs/ai-chat/reference.mdx +21 -3
- package/docs/ai-chat/sessions.mdx +1 -1
- package/docs/ai-chat/testing.mdx +16 -4
- package/docs/concurrency.mdx +384 -0
- package/docs/database-connections.mdx +3 -3
- package/docs/deploy-environment-variables.mdx +6 -0
- package/docs/deployment/atomic-deployment.mdx +416 -132
- package/docs/deployment/overview.mdx +2 -2
- package/docs/github-actions.mdx +2 -2
- package/docs/github-integration.mdx +2 -2
- package/docs/idempotency.mdx +43 -5
- package/docs/introduction.mdx +1 -1
- package/docs/limits.mdx +16 -6
- package/docs/observability/query.mdx +25 -0
- package/docs/queues.mdx +271 -0
- package/docs/reports.mdx +1 -1
- package/docs/runs/priority.mdx +2 -25
- package/docs/self-hosting/env/webapp.mdx +7 -0
- package/docs/tasks/overview.mdx +3 -5
- package/docs/troubleshooting-alerts.mdx +124 -1
- package/docs/troubleshooting.mdx +12 -0
- package/docs/vercel-integration.mdx +6 -7
- package/docs/versioning.mdx +1 -1
- package/docs/writing-tasks-introduction.mdx +2 -1
- package/package.json +2 -2
- package/docs/deployment/version-skew-protection.mdx +0 -492
- package/docs/queue-concurrency.mdx +0 -358
package/dist/commonjs/v3/ai.js
CHANGED
|
@@ -16,9 +16,12 @@ const v3_1 = require("@trigger.dev/core/v3");
|
|
|
16
16
|
// ESM-only `ai@7` (see ../imports/ai-runtime.ts).
|
|
17
17
|
const api_1 = require("@opentelemetry/api");
|
|
18
18
|
const sessionTracing_js_1 = require("./sessionTracing.js");
|
|
19
|
+
const chatRouteWait_js_1 = require("./chatRouteWait.js");
|
|
19
20
|
const ai_runtime_js_1 = require("../imports/ai-runtime.js");
|
|
20
21
|
const transcriptStorage_js_1 = require("./transcriptStorage.js");
|
|
21
22
|
const compactionResponse_js_1 = require("./compactionResponse.js");
|
|
23
|
+
const managedChatResponse_js_1 = require("./managedChatResponse.js");
|
|
24
|
+
const steeringContext_js_1 = require("./steeringContext.js");
|
|
22
25
|
let transcriptStorageOverride;
|
|
23
26
|
/**
|
|
24
27
|
* Test-only override for the storage `chat.agent` persists through, so a
|
|
@@ -48,6 +51,7 @@ const externalDeploymentId_js_1 = require("./externalDeploymentId.js");
|
|
|
48
51
|
const chatVersionSkew_js_1 = require("./chatVersionSkew.js");
|
|
49
52
|
const sessions_js_1 = require("./sessions.js");
|
|
50
53
|
const shared_js_1 = require("./shared.js");
|
|
54
|
+
const concurrency_shared_js_1 = require("./concurrency-shared.js");
|
|
51
55
|
const streams_js_1 = require("./streams.js");
|
|
52
56
|
const tracer_js_1 = require("./tracer.js");
|
|
53
57
|
const METADATA_KEY = "tool.execute.options";
|
|
@@ -56,17 +60,17 @@ const METADATA_KEY = "tool.execute.options";
|
|
|
56
60
|
* `ignoreIncompleteToolCalls: true` to prevent failures from
|
|
57
61
|
* stopped/aborted conversations with partial tool parts.
|
|
58
62
|
*/
|
|
59
|
-
function toModelMessages(messages) {
|
|
63
|
+
function toModelMessages(messages, context) {
|
|
60
64
|
// Pass the resolved per-turn `tools` (if any) so the AI SDK can look up each
|
|
61
65
|
// tool's `toModelOutput` and re-apply it to prior-turn tool results. Without
|
|
62
66
|
// `tools` it falls back to JSON-stringifying the raw output (TRI-10149). The
|
|
63
67
|
// conditional spread keeps the options object byte-identical to the no-tools
|
|
64
68
|
// path when nothing was declared.
|
|
65
69
|
const tools = locals_js_1.locals.get(chatResolvedToolsKey);
|
|
66
|
-
return (0, ai_runtime_js_1.convertToModelMessages)(
|
|
70
|
+
return (0, steeringContext_js_1.convertSteeredMessages)(messages, async (batch) => (0, ai_runtime_js_1.convertToModelMessages)(batch, {
|
|
67
71
|
ignoreIncompleteToolCalls: true,
|
|
68
72
|
...(tools ? { tools } : {}),
|
|
69
|
-
});
|
|
73
|
+
}), locals_js_1.locals.get(chatSteeringInjectionsKey) ?? new Map(), context ?? locals_js_1.locals.get(chatCurrentUIMessagesKey) ?? messages, context !== undefined || locals_js_1.locals.get(chatCurrentUIMessagesKey) !== undefined);
|
|
70
74
|
}
|
|
71
75
|
const chatTurnContextKey = locals_js_1.locals.create("chat.turnContext");
|
|
72
76
|
/**
|
|
@@ -871,7 +875,7 @@ const chatStream = {
|
|
|
871
875
|
* `onTurnComplete`'s `responseMessage` and `uiMessages`.
|
|
872
876
|
*
|
|
873
877
|
* Non-transient data chunks (`type` starts with `data-`, no `transient: true`)
|
|
874
|
-
* are
|
|
878
|
+
* are accumulated in emission order into the assistant response message.
|
|
875
879
|
* Transient or non-data chunks are streamed only (same as `chat.stream`).
|
|
876
880
|
*
|
|
877
881
|
* @example
|
|
@@ -889,15 +893,7 @@ const chatResponse = {
|
|
|
889
893
|
* response message; everything else is stream-only.
|
|
890
894
|
*/
|
|
891
895
|
write(part) {
|
|
892
|
-
|
|
893
|
-
const { waitUntilComplete } = chatStream.writer({
|
|
894
|
-
spanName: "chat.response.write",
|
|
895
|
-
collapsed: true,
|
|
896
|
-
execute: ({ write }) => {
|
|
897
|
-
write(part);
|
|
898
|
-
},
|
|
899
|
-
});
|
|
900
|
-
waitUntilComplete().catch(() => { });
|
|
896
|
+
managedResponse().writeData(part);
|
|
901
897
|
},
|
|
902
898
|
};
|
|
903
899
|
/**
|
|
@@ -906,60 +902,13 @@ const chatResponse = {
|
|
|
906
902
|
* @internal
|
|
907
903
|
*/
|
|
908
904
|
function createLazyChatWriter() {
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
let waitPromise = null;
|
|
912
|
-
let resolveExecute = null;
|
|
913
|
-
let started = false;
|
|
914
|
-
const bufferedParts = [];
|
|
915
|
-
const bufferedStreams = [];
|
|
916
|
-
function ensureInitialized() {
|
|
917
|
-
if (started)
|
|
918
|
-
return;
|
|
919
|
-
started = true;
|
|
920
|
-
const executePromise = new Promise((resolve) => {
|
|
921
|
-
resolveExecute = resolve;
|
|
922
|
-
});
|
|
923
|
-
const { waitUntilComplete } = chatStream.writer({
|
|
905
|
+
return (0, managedChatResponse_js_1.createOrderedChatWriter)(() => (locals_js_1.locals.get(chatManagedResponseActiveKey) ? managedResponse() : undefined), async (stream) => {
|
|
906
|
+
const { waitUntilComplete } = chatStream.pipe(stream, {
|
|
924
907
|
collapsed: true,
|
|
925
908
|
spanName: "callback writer",
|
|
926
|
-
execute: ({ write, merge }) => {
|
|
927
|
-
writeImpl = write;
|
|
928
|
-
mergeImpl = merge;
|
|
929
|
-
for (const part of bufferedParts.splice(0))
|
|
930
|
-
write(part);
|
|
931
|
-
for (const stream of bufferedStreams.splice(0))
|
|
932
|
-
merge(stream);
|
|
933
|
-
return executePromise;
|
|
934
|
-
},
|
|
935
909
|
});
|
|
936
|
-
|
|
937
|
-
}
|
|
938
|
-
return {
|
|
939
|
-
writer: {
|
|
940
|
-
write(part) {
|
|
941
|
-
ensureInitialized();
|
|
942
|
-
queueResponsePart(part);
|
|
943
|
-
if (writeImpl)
|
|
944
|
-
writeImpl(part);
|
|
945
|
-
else
|
|
946
|
-
bufferedParts.push(part);
|
|
947
|
-
},
|
|
948
|
-
merge(stream) {
|
|
949
|
-
ensureInitialized();
|
|
950
|
-
if (mergeImpl)
|
|
951
|
-
mergeImpl(stream);
|
|
952
|
-
else
|
|
953
|
-
bufferedStreams.push(stream);
|
|
954
|
-
},
|
|
955
|
-
},
|
|
956
|
-
async flush() {
|
|
957
|
-
if (resolveExecute) {
|
|
958
|
-
resolveExecute(); // Signal execute to complete
|
|
959
|
-
await waitPromise(); // Wait for stream to finish piping
|
|
960
|
-
}
|
|
961
|
-
},
|
|
962
|
-
};
|
|
910
|
+
await waitUntilComplete();
|
|
911
|
+
});
|
|
963
912
|
}
|
|
964
913
|
/**
|
|
965
914
|
* Runs a callback with a lazy ChatWriter, flushing the stream after completion.
|
|
@@ -1135,31 +1084,20 @@ async function waitOnChatRoute(route, options) {
|
|
|
1135
1084
|
if (options.onSuspend)
|
|
1136
1085
|
await options.onSuspend();
|
|
1137
1086
|
span.setAttribute("wait.resolved", "suspended");
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
const wake = await session.in.awaitWake({
|
|
1149
|
-
timeout: options.timeout,
|
|
1150
|
-
lastSeqNum: wakeFrom,
|
|
1151
|
-
});
|
|
1152
|
-
if (!wake.ok) {
|
|
1153
|
-
span.recordException(wake.error);
|
|
1154
|
-
return { ok: false, error: wake.error };
|
|
1155
|
-
}
|
|
1156
|
-
const record = await router.next(route);
|
|
1157
|
-
if (!record)
|
|
1158
|
-
continue;
|
|
1159
|
-
if (options.onResume)
|
|
1160
|
-
await options.onResume();
|
|
1161
|
-
return { ok: true, output: record.data, record };
|
|
1087
|
+
const result = await (0, chatRouteWait_js_1.waitForChatRouteAfterIdle)(router, route, {
|
|
1088
|
+
timeout: options.timeout,
|
|
1089
|
+
wake: async (timeout, lastSeqNum) => {
|
|
1090
|
+
span.setAttribute("wait.lastSeqNum", lastSeqNum ?? -1);
|
|
1091
|
+
return session.in.awaitWake({ timeout, lastSeqNum });
|
|
1092
|
+
},
|
|
1093
|
+
});
|
|
1094
|
+
if (!result.ok) {
|
|
1095
|
+
span.recordException(result.error);
|
|
1096
|
+
return result;
|
|
1162
1097
|
}
|
|
1098
|
+
if (options.onResume)
|
|
1099
|
+
await options.onResume();
|
|
1100
|
+
return { ok: true, output: result.record.data, record: result.record };
|
|
1163
1101
|
}, {
|
|
1164
1102
|
attributes: {
|
|
1165
1103
|
[v3_1.SemanticInternalAttributes.STYLE_ICON]: "sessions",
|
|
@@ -2550,29 +2488,19 @@ const chatTurnNewUIMessagesKey = locals_js_1.locals.create("chat.turnNewUIMessag
|
|
|
2550
2488
|
const chatPendingSteerKey = locals_js_1.locals.create("chat.pendingSteer");
|
|
2551
2489
|
/** @internal — IDs of messages that were successfully injected via prepareStep */
|
|
2552
2490
|
const chatInjectedMessageIdsKey = locals_js_1.locals.create("chat.injectedMessageIds");
|
|
2553
|
-
|
|
2554
|
-
const
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
}
|
|
2565
|
-
|
|
2566
|
-
* Queue a chunk for accumulation into the response message (if it's a non-transient data part).
|
|
2567
|
-
* Called by `chat.response.write()` and `ChatWriter.write()`.
|
|
2568
|
-
* @internal
|
|
2569
|
-
*/
|
|
2570
|
-
function queueResponsePart(part) {
|
|
2571
|
-
if (!isNonTransientDataPart(part))
|
|
2572
|
-
return;
|
|
2573
|
-
const parts = locals_js_1.locals.get(chatResponsePartsKey) ?? [];
|
|
2574
|
-
parts.push(part);
|
|
2575
|
-
locals_js_1.locals.set(chatResponsePartsKey, parts);
|
|
2491
|
+
const chatManagedResponseKey = locals_js_1.locals.create("chat.managedResponse");
|
|
2492
|
+
const chatManagedResponseActiveKey = locals_js_1.locals.create("chat.managedResponseActive");
|
|
2493
|
+
const chatSteeringInjectionsKey = locals_js_1.locals.create("chat.steeringInjections");
|
|
2494
|
+
function managedResponse() {
|
|
2495
|
+
let response = locals_js_1.locals.get(chatManagedResponseKey);
|
|
2496
|
+
if (!response || response.isClosed) {
|
|
2497
|
+
response = new managedChatResponse_js_1.ManagedChatResponse(async (stream) => {
|
|
2498
|
+
const { waitUntilComplete } = chatStream.pipe(stream, { spanName: "managed chat response" });
|
|
2499
|
+
await waitUntilComplete();
|
|
2500
|
+
});
|
|
2501
|
+
locals_js_1.locals.set(chatManagedResponseKey, response);
|
|
2502
|
+
}
|
|
2503
|
+
return response;
|
|
2576
2504
|
}
|
|
2577
2505
|
/**
|
|
2578
2506
|
* Check that no tool calls are in-flight in a step's content.
|
|
@@ -2924,6 +2852,8 @@ function modelFormOf(m, batch, injected) {
|
|
|
2924
2852
|
* @internal
|
|
2925
2853
|
*/
|
|
2926
2854
|
async function drainSteeringQueue(config, messages, steps, queueOverride) {
|
|
2855
|
+
if (steps.length === 0)
|
|
2856
|
+
managedResponse().beginGeneration();
|
|
2927
2857
|
const queue = queueOverride ?? locals_js_1.locals.get(chatSteeringQueueKey);
|
|
2928
2858
|
if (!queue || queue.length === 0)
|
|
2929
2859
|
return EMPTY_DRAIN;
|
|
@@ -3056,31 +2986,24 @@ async function drainSteeringQueue(config, messages, steps, queueOverride) {
|
|
|
3056
2986
|
}
|
|
3057
2987
|
locals_js_1.locals.set(chatPendingSteerKey, pendingSteer);
|
|
3058
2988
|
}
|
|
3059
|
-
// Write injection confirmation chunk to the stream so the frontend
|
|
3060
|
-
// knows which messages were injected and where in the response.
|
|
3061
2989
|
if (injected.length > 0) {
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
await waitUntilComplete();
|
|
3080
|
-
}
|
|
3081
|
-
catch {
|
|
3082
|
-
/* non-fatal — stream write failed */
|
|
3083
|
-
}
|
|
2990
|
+
const id = (0, ai_runtime_js_1.generateId)();
|
|
2991
|
+
const messageIds = claimedUIMessages.map((m) => m.id);
|
|
2992
|
+
const injections = locals_js_1.locals.get(chatSteeringInjectionsKey) ?? new Map();
|
|
2993
|
+
injections.set(id, { id, messageIds, messages: injected });
|
|
2994
|
+
locals_js_1.locals.set(chatSteeringInjectionsKey, injections);
|
|
2995
|
+
const response = managedResponse();
|
|
2996
|
+
// prepareStep can run ahead of the UI consumer. Admit the marker only
|
|
2997
|
+
// once the actual preceding finish-step chunk has entered this stream.
|
|
2998
|
+
response.afterStep(steps.length);
|
|
2999
|
+
response.writeData({
|
|
3000
|
+
type: ai_shared_js_1.PENDING_MESSAGE_INJECTED_TYPE,
|
|
3001
|
+
id,
|
|
3002
|
+
data: {
|
|
3003
|
+
messageIds,
|
|
3004
|
+
messages: claimedUIMessages.map((m) => ({ id: m.id, text: textOfUIMessage(m) })),
|
|
3005
|
+
},
|
|
3006
|
+
});
|
|
3084
3007
|
}
|
|
3085
3008
|
// Fire onInjected callback
|
|
3086
3009
|
if (config.onInjected && injected.length > 0) {
|
|
@@ -3381,7 +3304,7 @@ function buildManagedStreamTextOptions(options, config) {
|
|
|
3381
3304
|
* regenerate needs: without it a regenerated answer can call nothing.
|
|
3382
3305
|
*/
|
|
3383
3306
|
tools: (tools ?? agentTools),
|
|
3384
|
-
});
|
|
3307
|
+
}, false);
|
|
3385
3308
|
const promptSystem = locals_js_1.locals.get(chatPromptKey)?.text;
|
|
3386
3309
|
/**
|
|
3387
3310
|
* Two managed sources conflict too, not only a caller against a managed one.
|
|
@@ -3408,6 +3331,9 @@ function buildManagedStreamTextOptions(options, config) {
|
|
|
3408
3331
|
return { ...(first ?? {}), ...(second ?? {}) };
|
|
3409
3332
|
};
|
|
3410
3333
|
}
|
|
3334
|
+
if (typeof managed.prepareStep === "function") {
|
|
3335
|
+
managed.prepareStep = (0, steeringContext_js_1.retainStepMessages)(managed.prepareStep);
|
|
3336
|
+
}
|
|
3411
3337
|
return { ...managed, ...rest };
|
|
3412
3338
|
}
|
|
3413
3339
|
/** @internal Test hook for {@link buildManagedStreamTextOptions}. */
|
|
@@ -3423,7 +3349,7 @@ function createBoundStreamText(registry, agentSystem, agentCacheControl, agentSy
|
|
|
3423
3349
|
}));
|
|
3424
3350
|
return bound;
|
|
3425
3351
|
}
|
|
3426
|
-
function toStreamTextOptions(options) {
|
|
3352
|
+
function toStreamTextOptions(options, retain = true) {
|
|
3427
3353
|
const agentDefaults = locals_js_1.locals.get(chatAgentManagedConfigKey);
|
|
3428
3354
|
if (agentDefaults) {
|
|
3429
3355
|
options = {
|
|
@@ -3630,6 +3556,9 @@ function toStreamTextOptions(options) {
|
|
|
3630
3556
|
return resultMessages ? { messages: resultMessages } : undefined;
|
|
3631
3557
|
};
|
|
3632
3558
|
}
|
|
3559
|
+
if (retain && typeof result.prepareStep === "function") {
|
|
3560
|
+
result.prepareStep = (0, steeringContext_js_1.retainStepMessages)(result.prepareStep);
|
|
3561
|
+
}
|
|
3633
3562
|
return result;
|
|
3634
3563
|
}
|
|
3635
3564
|
const actionTurnBrand = Symbol.for("trigger.dev/chat/actionTurn");
|
|
@@ -3764,7 +3693,7 @@ function isReadableStream(value) {
|
|
|
3764
3693
|
* }
|
|
3765
3694
|
* ```
|
|
3766
3695
|
*/
|
|
3767
|
-
async function pipeChat(source, options) {
|
|
3696
|
+
async function pipeChat(source, options, capture) {
|
|
3768
3697
|
locals_js_1.locals.set(chatPipeCountKey, (locals_js_1.locals.get(chatPipeCountKey) ?? 0) + 1);
|
|
3769
3698
|
let stream;
|
|
3770
3699
|
if (isUIMessageStreamable(source)) {
|
|
@@ -3793,8 +3722,18 @@ async function pipeChat(source, options) {
|
|
|
3793
3722
|
// accepts opaque UIMessageStreamable / raw iterables whose element
|
|
3794
3723
|
// type we don't know at compile time. Cast — runtime behaviour is
|
|
3795
3724
|
// identical (bytes go to session.out either way).
|
|
3796
|
-
|
|
3797
|
-
|
|
3725
|
+
if (capture) {
|
|
3726
|
+
const response = managedResponse();
|
|
3727
|
+
response.seed(capture.originalMessages);
|
|
3728
|
+
await response.pipe(stream, options?.signal);
|
|
3729
|
+
}
|
|
3730
|
+
else {
|
|
3731
|
+
// Raw/manual pipes intentionally do not feed the managed capture. Never
|
|
3732
|
+
// leave injection events waiting for finish-step chunks it cannot see.
|
|
3733
|
+
managedResponse().useRawPipe();
|
|
3734
|
+
const { waitUntilComplete } = chatStream.pipe(stream, pipeOptions);
|
|
3735
|
+
await waitUntilComplete();
|
|
3736
|
+
}
|
|
3798
3737
|
}
|
|
3799
3738
|
function chatCustomAgent(options) {
|
|
3800
3739
|
const { clientDataSchema, onClientDataValidationError, clientDataReportErrorAt, run: userRun, ...restOptions } = options;
|
|
@@ -4047,11 +3986,13 @@ function chatAgent(options) {
|
|
|
4047
3986
|
if (!pending || pending.length === 0)
|
|
4048
3987
|
return [];
|
|
4049
3988
|
locals_js_1.locals.set(chatPendingSteerKey, []);
|
|
4050
|
-
|
|
3989
|
+
const inlineIds = new Set((0, steeringContext_js_1.steeringMarkers)(options?.response ? [options.response] : []).flatMap((m) => m.messageIds));
|
|
3990
|
+
const standalone = pending.filter((entry) => !inlineIds.has(entry.ui.id));
|
|
3991
|
+
for (const entry of standalone) {
|
|
4051
3992
|
accumulatedMessages.push(...entry.model);
|
|
4052
3993
|
options?.turnNew?.push(...entry.model);
|
|
4053
3994
|
}
|
|
4054
|
-
return
|
|
3995
|
+
return standalone;
|
|
4055
3996
|
};
|
|
4056
3997
|
// Accumulated UI messages for persistence. Mirrors the model accumulator
|
|
4057
3998
|
// but in frontend-friendly UIMessage format (with parts, id, etc.).
|
|
@@ -4145,7 +4086,10 @@ function chatAgent(options) {
|
|
|
4145
4086
|
});
|
|
4146
4087
|
const throughId = opts.messages.at(-1)?.id ?? "";
|
|
4147
4088
|
const queued = locals_js_1.locals.get(chatBackgroundQueueKey) ?? [];
|
|
4148
|
-
const
|
|
4089
|
+
const transcriptIds = new Set(opts.messages.map((message) => message.id));
|
|
4090
|
+
const markerIds = new Set((0, steeringContext_js_1.steeringMarkers)(opts.messages).map((marker) => marker.id));
|
|
4091
|
+
const steering = [...(locals_js_1.locals.get(chatSteeringInjectionsKey)?.values() ?? [])].filter((entry) => markerIds.has(entry.id) || entry.messageIds.some((id) => transcriptIds.has(id)));
|
|
4092
|
+
const runtimeState = laneCompacted || laneInjections.length > 0 || queued.length > 0 || steering.length > 0
|
|
4149
4093
|
? {
|
|
4150
4094
|
v: 1,
|
|
4151
4095
|
...(laneCompacted
|
|
@@ -4158,6 +4102,7 @@ function chatAgent(options) {
|
|
|
4158
4102
|
: {}),
|
|
4159
4103
|
...(laneInjections.length > 0 ? { injections: laneInjections } : {}),
|
|
4160
4104
|
...(queued.length > 0 ? { queued: [...queued] } : {}),
|
|
4105
|
+
...(steering.length > 0 ? { steering } : {}),
|
|
4161
4106
|
}
|
|
4162
4107
|
: null;
|
|
4163
4108
|
if (runtimeState !== null || persistedStateSet) {
|
|
@@ -4627,6 +4572,8 @@ function chatAgent(options) {
|
|
|
4627
4572
|
}
|
|
4628
4573
|
try {
|
|
4629
4574
|
const bootRuntimeState = (0, transcriptStorage_js_1.parseTranscriptRuntimeState)(bootTranscriptState);
|
|
4575
|
+
locals_js_1.locals.set(chatSteeringInjectionsKey, new Map((bootRuntimeState?.steering ?? []).map((entry) => [entry.id, entry])));
|
|
4576
|
+
locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
|
|
4630
4577
|
const restored = await (0, transcriptStorage_js_1.restoreModelLane)(accumulatedUIMessages, bootRuntimeState, (messages) => toModelMessages(messages));
|
|
4631
4578
|
accumulatedMessages = restored.messages;
|
|
4632
4579
|
laneCompacted = restored.compacted;
|
|
@@ -5074,7 +5021,9 @@ function chatAgent(options) {
|
|
|
5074
5021
|
locals_js_1.locals.set(chatCompactionStateKey, undefined);
|
|
5075
5022
|
locals_js_1.locals.set(chatSteeringQueueKey, []);
|
|
5076
5023
|
locals_js_1.locals.set(chatPendingBackgroundKey, []);
|
|
5077
|
-
locals_js_1.locals.
|
|
5024
|
+
await locals_js_1.locals.get(chatManagedResponseKey)?.close();
|
|
5025
|
+
locals_js_1.locals.set(chatManagedResponseKey, undefined);
|
|
5026
|
+
locals_js_1.locals.set(chatManagedResponseActiveKey, true);
|
|
5078
5027
|
// NOTE: chatBackgroundQueueKey is NOT reset here — messages injected
|
|
5079
5028
|
// by deferred work from the previous turn's onTurnComplete need to
|
|
5080
5029
|
// survive into the next turn. The queue is drained before run().
|
|
@@ -5238,7 +5187,7 @@ function chatAgent(options) {
|
|
|
5238
5187
|
if (actionOverride) {
|
|
5239
5188
|
locals_js_1.locals.set(chatOverrideMessagesKey, undefined);
|
|
5240
5189
|
accumulatedUIMessages = [...actionOverride];
|
|
5241
|
-
accumulatedMessages = await toModelMessages(actionOverride);
|
|
5190
|
+
accumulatedMessages = await toModelMessages(actionOverride, actionOverride);
|
|
5242
5191
|
laneCompacted = false;
|
|
5243
5192
|
laneInjections = [];
|
|
5244
5193
|
locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
|
|
@@ -5396,7 +5345,7 @@ function chatAgent(options) {
|
|
|
5396
5345
|
accumulatedUIMessages[accumulatedUIMessages.length - 1].role !== "user") {
|
|
5397
5346
|
accumulatedUIMessages.pop();
|
|
5398
5347
|
}
|
|
5399
|
-
accumulatedMessages = await toModelMessages(accumulatedUIMessages);
|
|
5348
|
+
accumulatedMessages = await toModelMessages(accumulatedUIMessages, accumulatedUIMessages);
|
|
5400
5349
|
laneCompacted = false;
|
|
5401
5350
|
laneInjections = [];
|
|
5402
5351
|
}
|
|
@@ -5448,7 +5397,7 @@ function chatAgent(options) {
|
|
|
5448
5397
|
}
|
|
5449
5398
|
if (!inPlace) {
|
|
5450
5399
|
v3_1.logger.warn("chat.agent: replaced message not found at the model lane tail; reconverting the lane");
|
|
5451
|
-
accumulatedMessages = await toModelMessages(accumulatedUIMessages);
|
|
5400
|
+
accumulatedMessages = await toModelMessages(accumulatedUIMessages, accumulatedUIMessages);
|
|
5452
5401
|
laneCompacted = false;
|
|
5453
5402
|
laneInjections = [];
|
|
5454
5403
|
}
|
|
@@ -5665,7 +5614,7 @@ function chatAgent(options) {
|
|
|
5665
5614
|
if (turnStartOverride) {
|
|
5666
5615
|
locals_js_1.locals.set(chatOverrideMessagesKey, undefined);
|
|
5667
5616
|
accumulatedUIMessages = [...turnStartOverride];
|
|
5668
|
-
accumulatedMessages = await toModelMessages(turnStartOverride);
|
|
5617
|
+
accumulatedMessages = await toModelMessages(turnStartOverride, turnStartOverride);
|
|
5669
5618
|
laneCompacted = false;
|
|
5670
5619
|
laneInjections = [];
|
|
5671
5620
|
locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
|
|
@@ -5745,6 +5694,7 @@ function chatAgent(options) {
|
|
|
5745
5694
|
capturedResponseMessage = lastUI;
|
|
5746
5695
|
capturedFinishReason = "stop";
|
|
5747
5696
|
}
|
|
5697
|
+
managedResponse().seed(accumulatedUIMessages);
|
|
5748
5698
|
// Don't call userRun. Don't pipe. Skip directly
|
|
5749
5699
|
// to the post-turn flow below.
|
|
5750
5700
|
}
|
|
@@ -5804,7 +5754,7 @@ function chatAgent(options) {
|
|
|
5804
5754
|
await pipeChat(tapUIMessageChunks(uiStream, turnBufferedChunks), {
|
|
5805
5755
|
signal: combinedSignal,
|
|
5806
5756
|
spanName: "stream response",
|
|
5807
|
-
});
|
|
5757
|
+
}, { originalMessages: isActionTurn ? undefined : accumulatedUIMessages });
|
|
5808
5758
|
}
|
|
5809
5759
|
}
|
|
5810
5760
|
catch (error) {
|
|
@@ -5832,6 +5782,10 @@ function chatAgent(options) {
|
|
|
5832
5782
|
new Promise((r) => setTimeout(r, 2_000)),
|
|
5833
5783
|
]);
|
|
5834
5784
|
}
|
|
5785
|
+
capturedResponseMessage =
|
|
5786
|
+
(await locals_js_1.locals.get(chatManagedResponseKey)?.snapshot()) ?? capturedResponseMessage;
|
|
5787
|
+
if (capturedResponseMessage)
|
|
5788
|
+
capturedPartialResponse = capturedResponseMessage;
|
|
5835
5789
|
// Capture token usage from the streamText result (if available).
|
|
5836
5790
|
// totalUsage is a PromiseLike that resolves after the stream is consumed.
|
|
5837
5791
|
// Race with a 2s timeout — on stop-abort the AI SDK's totalUsage
|
|
@@ -5955,6 +5909,7 @@ function chatAgent(options) {
|
|
|
5955
5909
|
// branches below, so a turn that captured no response is covered.
|
|
5956
5910
|
const steerTailThisTurn = reconcilePendingSteer({
|
|
5957
5911
|
turnNew: turnNewModelMessages,
|
|
5912
|
+
response: capturedResponseMessage,
|
|
5958
5913
|
}).reduce((n, e) => n + e.model.length, 0) + reconcilePendingBackground();
|
|
5959
5914
|
// Append the assistant's response (partial or complete) to the accumulator.
|
|
5960
5915
|
// The onFinish callback fires even on abort/stop, so partial responses
|
|
@@ -5977,15 +5932,6 @@ function chatAgent(options) {
|
|
|
5977
5932
|
id: (0, ai_runtime_js_1.generateId)(),
|
|
5978
5933
|
};
|
|
5979
5934
|
}
|
|
5980
|
-
// Append any non-transient data parts queued via chat.response or writer.write()
|
|
5981
|
-
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
5982
|
-
if (queuedParts && queuedParts.length > 0) {
|
|
5983
|
-
capturedResponseMessage = {
|
|
5984
|
-
...capturedResponseMessage,
|
|
5985
|
-
parts: [...capturedResponseMessage.parts, ...queuedParts],
|
|
5986
|
-
};
|
|
5987
|
-
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
5988
|
-
}
|
|
5989
5935
|
const responseHasContent = capturedResponseMessage.parts.some((part) => part.type !== "step-start");
|
|
5990
5936
|
if (responseHasContent) {
|
|
5991
5937
|
// Tool-approval continuations: the AI SDK reuses the trailing
|
|
@@ -6047,7 +5993,7 @@ function chatAgent(options) {
|
|
|
6047
5993
|
locals_js_1.locals.set(chatHandoverSplicedRunKey, undefined);
|
|
6048
5994
|
if (!ok) {
|
|
6049
5995
|
v3_1.logger.warn("chat.agent: replaced response not found at the model lane tail; reconverting the lane");
|
|
6050
|
-
accumulatedMessages = await toModelMessages(accumulatedUIMessages);
|
|
5996
|
+
accumulatedMessages = await toModelMessages(accumulatedUIMessages, accumulatedUIMessages);
|
|
6051
5997
|
laneCompacted = false;
|
|
6052
5998
|
laneInjections = [];
|
|
6053
5999
|
}
|
|
@@ -6065,22 +6011,6 @@ function chatAgent(options) {
|
|
|
6065
6011
|
responseWasSkipped = true;
|
|
6066
6012
|
}
|
|
6067
6013
|
}
|
|
6068
|
-
// If there's no captured response (manual pipe mode) but there are
|
|
6069
|
-
// queued data parts, create a minimal response message to hold them.
|
|
6070
|
-
if (!capturedResponseMessage) {
|
|
6071
|
-
const remainingParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
6072
|
-
if (remainingParts && remainingParts.length > 0) {
|
|
6073
|
-
capturedResponseMessage = {
|
|
6074
|
-
id: (0, ai_runtime_js_1.generateId)(),
|
|
6075
|
-
role: "assistant",
|
|
6076
|
-
parts: [...remainingParts],
|
|
6077
|
-
};
|
|
6078
|
-
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
6079
|
-
accumulatedUIMessages.push(capturedResponseMessage);
|
|
6080
|
-
turnNewUIMessages.push(capturedResponseMessage);
|
|
6081
|
-
locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
|
|
6082
|
-
}
|
|
6083
|
-
}
|
|
6084
6014
|
if (capturedResponseMessage) {
|
|
6085
6015
|
responseCommitted = true;
|
|
6086
6016
|
capturedPartialResponse = capturedResponseMessage;
|
|
@@ -6242,6 +6172,8 @@ function chatAgent(options) {
|
|
|
6242
6172
|
totalUsage: cumulativeUsage,
|
|
6243
6173
|
finishReason: capturedFinishReason,
|
|
6244
6174
|
};
|
|
6175
|
+
const beforeHookRevision = locals_js_1.locals.get(chatManagedResponseKey)?.revision ?? 0;
|
|
6176
|
+
let beforeHookHistoryEdited = false;
|
|
6245
6177
|
// Fire onBeforeTurnComplete — stream is still open so the hook
|
|
6246
6178
|
// can write custom chunks to the frontend (e.g. compaction progress).
|
|
6247
6179
|
if (onBeforeTurnComplete) {
|
|
@@ -6252,9 +6184,10 @@ function chatAgent(options) {
|
|
|
6252
6184
|
// Check if the hook replaced messages (compaction or chat.history)
|
|
6253
6185
|
const override = locals_js_1.locals.get(chatOverrideMessagesKey);
|
|
6254
6186
|
if (override) {
|
|
6187
|
+
beforeHookHistoryEdited = true;
|
|
6255
6188
|
locals_js_1.locals.set(chatOverrideMessagesKey, undefined);
|
|
6256
6189
|
accumulatedUIMessages = [...override];
|
|
6257
|
-
accumulatedMessages = await toModelMessages(override);
|
|
6190
|
+
accumulatedMessages = await toModelMessages(override, override);
|
|
6258
6191
|
laneCompacted = false;
|
|
6259
6192
|
laneInjections = [];
|
|
6260
6193
|
locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
|
|
@@ -6271,35 +6204,33 @@ function chatAgent(options) {
|
|
|
6271
6204
|
},
|
|
6272
6205
|
});
|
|
6273
6206
|
}
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
turnCompleteEvent.responseMessage = capturedResponseMessage;
|
|
6207
|
+
const editedResponse = beforeHookHistoryEdited && capturedResponseMessage
|
|
6208
|
+
? accumulatedUIMessages.find((message) => message.id === capturedResponseMessage?.id)
|
|
6209
|
+
: undefined;
|
|
6210
|
+
const finalManagedResponse = await locals_js_1.locals
|
|
6211
|
+
.get(chatManagedResponseKey)
|
|
6212
|
+
?.snapshot(editedResponse
|
|
6213
|
+
? { message: editedResponse, from: beforeHookRevision }
|
|
6214
|
+
: undefined);
|
|
6215
|
+
if (finalManagedResponse?.parts.some((part) => part.type !== "step-start")) {
|
|
6216
|
+
const idx = accumulatedUIMessages.findIndex((m) => m.id === finalManagedResponse.id);
|
|
6217
|
+
const finalized = (wasStopped ? cleanupAbortedParts(finalManagedResponse) : finalManagedResponse);
|
|
6218
|
+
if (idx !== -1 || responseWasSkipped || !capturedResponseMessage) {
|
|
6219
|
+
if (idx !== -1)
|
|
6220
|
+
accumulatedUIMessages[idx] = finalized;
|
|
6221
|
+
else
|
|
6222
|
+
accumulatedUIMessages.push(finalized);
|
|
6223
|
+
const deltaIdx = turnNewUIMessages.findIndex((m) => m.id === finalized.id);
|
|
6224
|
+
if (deltaIdx !== -1)
|
|
6225
|
+
turnNewUIMessages[deltaIdx] = finalized;
|
|
6226
|
+
else
|
|
6227
|
+
turnNewUIMessages.push(finalized);
|
|
6228
|
+
capturedResponseMessage = finalized;
|
|
6229
|
+
capturedPartialResponse = finalized;
|
|
6230
|
+
turnCompleteEvent.responseMessage = finalized;
|
|
6299
6231
|
turnCompleteEvent.uiMessages = accumulatedUIMessages;
|
|
6300
6232
|
locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
|
|
6301
6233
|
}
|
|
6302
|
-
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
6303
6234
|
}
|
|
6304
6235
|
settleRecoveredTurn(currentWirePayload);
|
|
6305
6236
|
// Write turn-complete control chunk — closes the frontend stream.
|
|
@@ -6316,7 +6247,7 @@ function chatAgent(options) {
|
|
|
6316
6247
|
if (turnCompleteOverride) {
|
|
6317
6248
|
locals_js_1.locals.set(chatOverrideMessagesKey, undefined);
|
|
6318
6249
|
accumulatedUIMessages = [...turnCompleteOverride];
|
|
6319
|
-
accumulatedMessages = await toModelMessages(turnCompleteOverride);
|
|
6250
|
+
accumulatedMessages = await toModelMessages(turnCompleteOverride, turnCompleteOverride);
|
|
6320
6251
|
laneCompacted = false;
|
|
6321
6252
|
laneInjections = [];
|
|
6322
6253
|
locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
|
|
@@ -6552,7 +6483,8 @@ function chatAgent(options) {
|
|
|
6552
6483
|
!accumulatedUIMessages.some((m) => m.id === erroredWireMessage.id)
|
|
6553
6484
|
? [...accumulatedUIMessages, erroredWireMessage]
|
|
6554
6485
|
: accumulatedUIMessages;
|
|
6555
|
-
let partialResponse =
|
|
6486
|
+
let partialResponse = (await locals_js_1.locals.get(chatManagedResponseKey)?.snapshot()) ??
|
|
6487
|
+
capturedPartialResponse ??
|
|
6556
6488
|
(await assemblePartialFromChunks(turnBufferedChunks));
|
|
6557
6489
|
if (partialResponse) {
|
|
6558
6490
|
partialResponse = cleanupAbortedParts(partialResponse);
|
|
@@ -6560,23 +6492,16 @@ function chatAgent(options) {
|
|
|
6560
6492
|
let partialIdx = partialResponse?.id
|
|
6561
6493
|
? erroredUIMessages.findIndex((m) => m.id === partialResponse.id)
|
|
6562
6494
|
: -1;
|
|
6563
|
-
if (partialResponse &&
|
|
6495
|
+
if (partialResponse &&
|
|
6496
|
+
capturedPartialResponse === undefined &&
|
|
6497
|
+
!locals_js_1.locals.get(chatManagedResponseKey)?.continues(partialResponse.id) &&
|
|
6498
|
+
partialIdx !== -1) {
|
|
6564
6499
|
partialResponse = undefined;
|
|
6565
6500
|
partialIdx = -1;
|
|
6566
6501
|
}
|
|
6567
6502
|
if (partialResponse && !partialResponse.id) {
|
|
6568
6503
|
partialResponse = { ...partialResponse, id: (0, ai_runtime_js_1.generateId)() };
|
|
6569
6504
|
}
|
|
6570
|
-
if (partialResponse && !responseCommitted) {
|
|
6571
|
-
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
6572
|
-
if (queuedParts && queuedParts.length > 0) {
|
|
6573
|
-
partialResponse = {
|
|
6574
|
-
...partialResponse,
|
|
6575
|
-
parts: [...partialResponse.parts, ...queuedParts],
|
|
6576
|
-
};
|
|
6577
|
-
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
6578
|
-
}
|
|
6579
|
-
}
|
|
6580
6505
|
const includePartial = partialResponse != null && !responseCommitted;
|
|
6581
6506
|
// What the stream left behind, by content. After `onTurnComplete` the
|
|
6582
6507
|
// partial is still unfinished only if the message under its id is
|
|
@@ -6609,7 +6534,7 @@ function chatAgent(options) {
|
|
|
6609
6534
|
};
|
|
6610
6535
|
let erroredNewUIMessages = buildErroredNew();
|
|
6611
6536
|
let erroredNewModelMessages = [];
|
|
6612
|
-
const reconciledSteer = reconcilePendingSteer();
|
|
6537
|
+
const reconciledSteer = reconcilePendingSteer({ response: partialResponse });
|
|
6613
6538
|
const backgroundTailThisTurn = reconcilePendingBackground();
|
|
6614
6539
|
if (!responseCommitted) {
|
|
6615
6540
|
try {
|
|
@@ -6620,14 +6545,7 @@ function chatAgent(options) {
|
|
|
6620
6545
|
* the model received it (what `prepare` produced), matching the
|
|
6621
6546
|
* lane. The wire message and partial are converted as before.
|
|
6622
6547
|
*/
|
|
6623
|
-
|
|
6624
|
-
for (const m of erroredNewUIMessages) {
|
|
6625
|
-
const recorded = steerModelById.get(m.id);
|
|
6626
|
-
if (recorded)
|
|
6627
|
-
erroredNewModelMessages.push(...recorded);
|
|
6628
|
-
else
|
|
6629
|
-
erroredNewModelMessages.push(...(await toModelMessages([stripProviderMetadata(m)])));
|
|
6630
|
-
}
|
|
6548
|
+
erroredNewModelMessages = await toModelMessages(erroredNewUIMessages.map(stripProviderMetadata));
|
|
6631
6549
|
}
|
|
6632
6550
|
if (erroredUIMessagesWithPartial !== accumulatedUIMessages) {
|
|
6633
6551
|
if (partialIdx === -1) {
|
|
@@ -6639,7 +6557,7 @@ function chatAgent(options) {
|
|
|
6639
6557
|
backgroundTailThisTurn);
|
|
6640
6558
|
if (!ok) {
|
|
6641
6559
|
v3_1.logger.warn("chat.agent: replaced partial not found at the model lane tail; reconverting the lane");
|
|
6642
|
-
accumulatedMessages = await toModelMessages(erroredUIMessagesWithPartial);
|
|
6560
|
+
accumulatedMessages = await toModelMessages(erroredUIMessagesWithPartial, erroredUIMessagesWithPartial);
|
|
6643
6561
|
laneCompacted = false;
|
|
6644
6562
|
laneInjections = [];
|
|
6645
6563
|
}
|
|
@@ -6696,7 +6614,7 @@ function chatAgent(options) {
|
|
|
6696
6614
|
// Convert first: a rejected conversion (a tool's `toModelOutput`
|
|
6697
6615
|
// can throw) must leave every lane on the history it had.
|
|
6698
6616
|
const overrideUIMessages = [...errorTurnOverride];
|
|
6699
|
-
const overrideModelMessages = await toModelMessages(errorTurnOverride);
|
|
6617
|
+
const overrideModelMessages = await toModelMessages(errorTurnOverride, errorTurnOverride);
|
|
6700
6618
|
erroredUIMessagesWithPartial = overrideUIMessages;
|
|
6701
6619
|
accumulatedUIMessages = overrideUIMessages;
|
|
6702
6620
|
accumulatedMessages = overrideModelMessages;
|
|
@@ -6789,6 +6707,11 @@ function chatAgent(options) {
|
|
|
6789
6707
|
}
|
|
6790
6708
|
finally {
|
|
6791
6709
|
turnMsgSub?.off();
|
|
6710
|
+
locals_js_1.locals.set(chatManagedResponseActiveKey, false);
|
|
6711
|
+
await locals_js_1.locals
|
|
6712
|
+
.get(chatManagedResponseKey)
|
|
6713
|
+
?.close()
|
|
6714
|
+
.catch(() => { });
|
|
6792
6715
|
}
|
|
6793
6716
|
}
|
|
6794
6717
|
}
|
|
@@ -7758,7 +7681,7 @@ async function pipeChatAndCapture(source, options) {
|
|
|
7758
7681
|
await pipeChat(tappedStream, {
|
|
7759
7682
|
signal: options?.signal,
|
|
7760
7683
|
spanName: options?.spanName ?? "stream response",
|
|
7761
|
-
});
|
|
7684
|
+
}, { originalMessages: options?.originalMessages });
|
|
7762
7685
|
// The pipe can drain cleanly on a stop — the source stream just ends
|
|
7763
7686
|
// early — so classify by the signal rather than relying on a throw.
|
|
7764
7687
|
if (options?.signal?.aborted) {
|
|
@@ -7784,6 +7707,9 @@ async function pipeChatAndCapture(source, options) {
|
|
|
7784
7707
|
if (!captured && bufferedChunks.length > 0) {
|
|
7785
7708
|
captured = await assemblePartialFromChunks(bufferedChunks);
|
|
7786
7709
|
}
|
|
7710
|
+
captured = (await locals_js_1.locals.get(chatManagedResponseKey)?.snapshot()) ?? captured;
|
|
7711
|
+
if (!locals_js_1.locals.get(chatTurnContextKey))
|
|
7712
|
+
await locals_js_1.locals.get(chatManagedResponseKey)?.close();
|
|
7787
7713
|
return {
|
|
7788
7714
|
message: captured,
|
|
7789
7715
|
status,
|
|
@@ -7817,6 +7743,7 @@ class ChatMessageAccumulator {
|
|
|
7817
7743
|
_handoverRun;
|
|
7818
7744
|
_pendingMessages;
|
|
7819
7745
|
_steeringQueue = [];
|
|
7746
|
+
_pendingSteer = [];
|
|
7820
7747
|
constructor(options) {
|
|
7821
7748
|
this._compaction = options?.compaction;
|
|
7822
7749
|
this._pendingMessages = options?.pendingMessages;
|
|
@@ -7889,6 +7816,19 @@ class ChatMessageAccumulator {
|
|
|
7889
7816
|
return { isFinal: signal.isFinal, skipped: false };
|
|
7890
7817
|
}
|
|
7891
7818
|
async addResponse(response) {
|
|
7819
|
+
const inlineIds = new Set((0, steeringContext_js_1.steeringMarkers)([response]).flatMap((m) => m.messageIds));
|
|
7820
|
+
for (const entry of this._pendingSteer.splice(0)) {
|
|
7821
|
+
if (!inlineIds.has(entry.ui.id))
|
|
7822
|
+
continue;
|
|
7823
|
+
// absorbSteering is public and updates context immediately. Move just
|
|
7824
|
+
// those exact appended objects into the response's chronological run;
|
|
7825
|
+
// never rebuild a possibly compacted lane from the UI transcript.
|
|
7826
|
+
for (const message of entry.model) {
|
|
7827
|
+
const index = this.modelMessages.lastIndexOf(message);
|
|
7828
|
+
if (index !== -1)
|
|
7829
|
+
this.modelMessages.splice(index, 1);
|
|
7830
|
+
}
|
|
7831
|
+
}
|
|
7892
7832
|
if (!response.id) {
|
|
7893
7833
|
response = { ...response, id: (0, ai_runtime_js_1.generateId)() };
|
|
7894
7834
|
}
|
|
@@ -7965,7 +7905,10 @@ class ChatMessageAccumulator {
|
|
|
7965
7905
|
this.uiMessages.push(...fresh);
|
|
7966
7906
|
// Record what the model received. Only when the whole batch is new is
|
|
7967
7907
|
// `injected` known to describe exactly these messages.
|
|
7968
|
-
|
|
7908
|
+
const model = injected && fresh.length === claimed.length ? injected : await toModelMessages(fresh);
|
|
7909
|
+
this.modelMessages.push(...model);
|
|
7910
|
+
for (const ui of fresh)
|
|
7911
|
+
this._pendingSteer.push({ ui, model: modelFormOf(ui, fresh, model) });
|
|
7969
7912
|
}
|
|
7970
7913
|
/**
|
|
7971
7914
|
* Get and clear unconsumed steering messages.
|
|
@@ -7985,7 +7928,7 @@ class ChatMessageAccumulator {
|
|
|
7985
7928
|
const comp = this._compaction;
|
|
7986
7929
|
const pm = this._pendingMessages;
|
|
7987
7930
|
const queue = this._steeringQueue;
|
|
7988
|
-
return async ({ messages, steps }) => {
|
|
7931
|
+
return (0, steeringContext_js_1.retainStepMessages)(async ({ messages, steps }) => {
|
|
7989
7932
|
let resultMessages;
|
|
7990
7933
|
// 1. Compaction
|
|
7991
7934
|
if (comp) {
|
|
@@ -7998,7 +7941,7 @@ class ChatMessageAccumulator {
|
|
|
7998
7941
|
}
|
|
7999
7942
|
}
|
|
8000
7943
|
// 2. Pending message injection
|
|
8001
|
-
if (pm
|
|
7944
|
+
if (pm) {
|
|
8002
7945
|
const { injected, claimed } = await drainSteeringQueue(pm, resultMessages ?? messages, steps, queue);
|
|
8003
7946
|
await this.absorbSteering(claimed, injected);
|
|
8004
7947
|
if (injected.length > 0) {
|
|
@@ -8006,7 +7949,7 @@ class ChatMessageAccumulator {
|
|
|
8006
7949
|
}
|
|
8007
7950
|
}
|
|
8008
7951
|
return resultMessages ? { messages: resultMessages } : undefined;
|
|
8009
|
-
};
|
|
7952
|
+
});
|
|
8010
7953
|
}
|
|
8011
7954
|
/**
|
|
8012
7955
|
* Run outer-loop compaction if needed. Call after adding the response
|
|
@@ -8318,7 +8261,9 @@ function createChatSession(payload, options) {
|
|
|
8318
8261
|
// Reset stop signal for this turn
|
|
8319
8262
|
stop.reset();
|
|
8320
8263
|
// Reset per-turn state
|
|
8321
|
-
locals_js_1.locals.
|
|
8264
|
+
await locals_js_1.locals.get(chatManagedResponseKey)?.close();
|
|
8265
|
+
locals_js_1.locals.set(chatManagedResponseKey, undefined);
|
|
8266
|
+
locals_js_1.locals.set(chatManagedResponseActiveKey, true);
|
|
8322
8267
|
// Set up steering queue and pending messages config in locals
|
|
8323
8268
|
// so toStreamTextOptions() auto-injects prepareStep for steering
|
|
8324
8269
|
const turnSteeringQueue = [];
|
|
@@ -8463,11 +8408,6 @@ function createChatSession(payload, options) {
|
|
|
8463
8408
|
if (captured.status === "error") {
|
|
8464
8409
|
if (captured.message) {
|
|
8465
8410
|
const partial = cleanupAbortedParts(captured.message);
|
|
8466
|
-
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
8467
|
-
if (queuedParts && queuedParts.length > 0) {
|
|
8468
|
-
partial.parts = [...(partial.parts ?? []), ...queuedParts];
|
|
8469
|
-
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
8470
|
-
}
|
|
8471
8411
|
await accumulator.addResponse(partial);
|
|
8472
8412
|
}
|
|
8473
8413
|
throw captured.error;
|
|
@@ -8483,26 +8423,8 @@ function createChatSession(payload, options) {
|
|
|
8483
8423
|
const cleaned = stop.signal.aborted && !runSignal.aborted
|
|
8484
8424
|
? cleanupAbortedParts(response)
|
|
8485
8425
|
: response;
|
|
8486
|
-
// Append any non-transient data parts queued via chat.response or writer.write()
|
|
8487
|
-
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
8488
|
-
if (queuedParts && queuedParts.length > 0) {
|
|
8489
|
-
cleaned.parts = [...(cleaned.parts ?? []), ...queuedParts];
|
|
8490
|
-
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
8491
|
-
}
|
|
8492
8426
|
await accumulator.addResponse(cleaned);
|
|
8493
8427
|
}
|
|
8494
|
-
else {
|
|
8495
|
-
// No response (manual pipe mode) but there are queued data parts
|
|
8496
|
-
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
8497
|
-
if (queuedParts && queuedParts.length > 0) {
|
|
8498
|
-
await accumulator.addResponse({
|
|
8499
|
-
id: (0, ai_runtime_js_1.generateId)(),
|
|
8500
|
-
role: "assistant",
|
|
8501
|
-
parts: queuedParts,
|
|
8502
|
-
});
|
|
8503
|
-
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
8504
|
-
}
|
|
8505
|
-
}
|
|
8506
8428
|
// Capture token usage from the streamText result. Race with a 2s
|
|
8507
8429
|
// timeout — on stop-abort the AI SDK's totalUsage promise can hang
|
|
8508
8430
|
// indefinitely, which would wedge the turn loop (same guard as
|
|
@@ -8593,15 +8515,6 @@ function createChatSession(payload, options) {
|
|
|
8593
8515
|
return response;
|
|
8594
8516
|
},
|
|
8595
8517
|
async addResponse(response) {
|
|
8596
|
-
// Append any non-transient data parts queued via chat.response or writer.write()
|
|
8597
|
-
const queuedParts = locals_js_1.locals.get(chatResponsePartsKey);
|
|
8598
|
-
if (queuedParts && queuedParts.length > 0) {
|
|
8599
|
-
response = {
|
|
8600
|
-
...response,
|
|
8601
|
-
parts: [...(response.parts ?? []), ...queuedParts],
|
|
8602
|
-
};
|
|
8603
|
-
locals_js_1.locals.set(chatResponsePartsKey, []);
|
|
8604
|
-
}
|
|
8605
8518
|
await accumulator.addResponse(response);
|
|
8606
8519
|
},
|
|
8607
8520
|
async done() {
|
|
@@ -8613,7 +8526,7 @@ function createChatSession(payload, options) {
|
|
|
8613
8526
|
const hasPending = !!sessionPendingMessages;
|
|
8614
8527
|
if (!hasCompaction && !hasPending)
|
|
8615
8528
|
return undefined;
|
|
8616
|
-
return async ({ messages: stepMsgs, steps, }) => {
|
|
8529
|
+
return (0, steeringContext_js_1.retainStepMessages)(async ({ messages: stepMsgs, steps, }) => {
|
|
8617
8530
|
let resultMessages;
|
|
8618
8531
|
if (sessionCompaction) {
|
|
8619
8532
|
const compactResult = await chatCompact(stepMsgs, steps, {
|
|
@@ -8632,7 +8545,7 @@ function createChatSession(payload, options) {
|
|
|
8632
8545
|
}
|
|
8633
8546
|
}
|
|
8634
8547
|
return resultMessages ? { messages: resultMessages } : undefined;
|
|
8635
|
-
};
|
|
8548
|
+
});
|
|
8636
8549
|
},
|
|
8637
8550
|
};
|
|
8638
8551
|
return { done: false, value: turnObj };
|
|
@@ -8900,6 +8813,10 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
8900
8813
|
const clientDataMetadata = params.clientData !== undefined ? { metadata: params.clientData } : {};
|
|
8901
8814
|
const maxAttempts = params.triggerConfig?.maxAttempts ?? options?.triggerConfig?.maxAttempts;
|
|
8902
8815
|
const maxDuration = params.triggerConfig?.maxDuration ?? options?.triggerConfig?.maxDuration;
|
|
8816
|
+
const concurrency = params.triggerConfig?.concurrency !== undefined
|
|
8817
|
+
? params.triggerConfig.concurrency
|
|
8818
|
+
: options?.triggerConfig?.concurrency;
|
|
8819
|
+
const concurrencyKey = params.triggerConfig?.concurrencyKey ?? options?.triggerConfig?.concurrencyKey;
|
|
8903
8820
|
const idleTimeoutInSeconds = params.triggerConfig?.idleTimeoutInSeconds ?? options?.triggerConfig?.idleTimeoutInSeconds;
|
|
8904
8821
|
// Only `undefined` means "not supplied": a per-call `null` (opt out) has to beat a pinning
|
|
8905
8822
|
// action default, which neither truthiness nor `??` would allow.
|
|
@@ -8921,6 +8838,8 @@ function createChatStartSessionAction(taskId, options) {
|
|
|
8921
8838
|
...(options?.triggerConfig?.queue || params.triggerConfig?.queue
|
|
8922
8839
|
? { queue: params.triggerConfig?.queue ?? options?.triggerConfig?.queue }
|
|
8923
8840
|
: {}),
|
|
8841
|
+
...(concurrency !== undefined ? (0, concurrency_shared_js_1.triggerConcurrencyBody)(concurrency) : {}),
|
|
8842
|
+
...(concurrencyKey !== undefined ? { concurrencyKey } : {}),
|
|
8924
8843
|
tags,
|
|
8925
8844
|
...(maxAttempts !== undefined ? { maxAttempts } : {}),
|
|
8926
8845
|
...(maxDuration !== undefined ? { maxDuration } : {}),
|
|
@@ -9268,6 +9187,10 @@ exports.chat = {
|
|
|
9268
9187
|
* @internal
|
|
9269
9188
|
*/
|
|
9270
9189
|
async function writeTurnCompleteChunk(_chatId, publicAccessToken) {
|
|
9190
|
+
locals_js_1.locals.set(chatManagedResponseActiveKey, false);
|
|
9191
|
+
const response = locals_js_1.locals.get(chatManagedResponseKey);
|
|
9192
|
+
if (response)
|
|
9193
|
+
await response.close();
|
|
9271
9194
|
const session = getChatSession();
|
|
9272
9195
|
// A handover-prepare boot claims the handover kinds so a signal arriving
|
|
9273
9196
|
// before `waitForHandover` attaches is not drained. Released here rather than
|