@trigger.dev/sdk 0.0.0-chat-prerelease-20260506093419 → 0.0.0-chat-prerelease-20260507122230
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 +56 -0
- package/dist/commonjs/v3/ai.js +217 -5
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/chat-server.js +10 -1
- package/dist/commonjs/v3/chat-server.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/ai.d.ts +56 -0
- package/dist/esm/v3/ai.js +218 -6
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/chat-server.js +10 -1
- package/dist/esm/v3/chat-server.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +2 -2
package/dist/commonjs/v3/ai.d.ts
CHANGED
|
@@ -263,6 +263,28 @@ export type ChatTurnUsage = LanguageModelUsage;
|
|
|
263
263
|
* and converts to `ModelMessage[]` internally.
|
|
264
264
|
*/
|
|
265
265
|
declare function setChatMessages<TUIM extends UIMessage = UIMessage>(uiMessages: TUIM[]): void;
|
|
266
|
+
/**
|
|
267
|
+
* A tool call surfaced by `chat.history.getPendingToolCalls()` /
|
|
268
|
+
* `getResolvedToolCalls()`. Identifies the call by its `toolCallId` plus
|
|
269
|
+
* the `messageId` of the assistant message that hosts it, so callers can
|
|
270
|
+
* locate the part precisely without re-walking the chain.
|
|
271
|
+
*/
|
|
272
|
+
export type ChatToolCallRef = {
|
|
273
|
+
toolCallId: string;
|
|
274
|
+
toolName: string;
|
|
275
|
+
messageId: string;
|
|
276
|
+
};
|
|
277
|
+
/**
|
|
278
|
+
* A new tool result surfaced by `chat.history.extractNewToolResults()`.
|
|
279
|
+
* `errorText` is set iff the part is in `output-error` state; otherwise
|
|
280
|
+
* `output` carries the resolved value.
|
|
281
|
+
*/
|
|
282
|
+
export type ChatNewToolResult = {
|
|
283
|
+
toolCallId: string;
|
|
284
|
+
toolName: string;
|
|
285
|
+
output: unknown;
|
|
286
|
+
errorText?: string;
|
|
287
|
+
};
|
|
266
288
|
/** State stored in locals during prepareStep compaction. */
|
|
267
289
|
interface CompactionState {
|
|
268
290
|
summary: string;
|
|
@@ -2493,6 +2515,40 @@ export declare const chat: {
|
|
|
2493
2515
|
history: {
|
|
2494
2516
|
/** Read the current accumulated UI messages (copy). */
|
|
2495
2517
|
all(): UIMessage[];
|
|
2518
|
+
/**
|
|
2519
|
+
* Read the current chain as an ordered `UIMessage[]`. Alias for `all()` —
|
|
2520
|
+
* use this when working alongside parent-aware APIs (TRI-9120) where
|
|
2521
|
+
* "chain" disambiguates from "graph".
|
|
2522
|
+
*/
|
|
2523
|
+
getChain(): UIMessage[];
|
|
2524
|
+
/**
|
|
2525
|
+
* Find a message by id. Returns `undefined` if no message with that id
|
|
2526
|
+
* is present in the current chain.
|
|
2527
|
+
*/
|
|
2528
|
+
findMessage(messageId: string): UIMessage | undefined;
|
|
2529
|
+
/**
|
|
2530
|
+
* Tool calls on the leaf assistant message still waiting on an answer
|
|
2531
|
+
* (`input-available` state). Use this to gate fresh user turns during
|
|
2532
|
+
* HITL flows: if `getPendingToolCalls().length > 0`, an `addToolOutput`
|
|
2533
|
+
* is expected before any new user message.
|
|
2534
|
+
*
|
|
2535
|
+
* Returns `[]` if there is no assistant message yet, or if the leaf
|
|
2536
|
+
* assistant has no pending tool calls.
|
|
2537
|
+
*/
|
|
2538
|
+
getPendingToolCalls(): ChatToolCallRef[];
|
|
2539
|
+
/**
|
|
2540
|
+
* Tool calls across the chain with a final result (`output-available`
|
|
2541
|
+
* or `output-error`). Use this to dedup re-saves when the AI SDK
|
|
2542
|
+
* resends an assistant message with progressively more answered parts.
|
|
2543
|
+
*/
|
|
2544
|
+
getResolvedToolCalls(): ChatToolCallRef[];
|
|
2545
|
+
/**
|
|
2546
|
+
* Pure helper: returns the tool parts in `message` whose results are
|
|
2547
|
+
* not already represented in the current chain. Use this when
|
|
2548
|
+
* persisting tool results to your own store: each call surfaces only
|
|
2549
|
+
* the *new* answers, so writes stay idempotent across re-streams.
|
|
2550
|
+
*/
|
|
2551
|
+
extractNewToolResults(message: UIMessage): ChatNewToolResult[];
|
|
2496
2552
|
/** Replace all accumulated messages. Same as `chat.setMessages()`. */
|
|
2497
2553
|
set(messages: UIMessage[]): void;
|
|
2498
2554
|
/** Remove a specific message by ID. */
|
package/dist/commonjs/v3/ai.js
CHANGED
|
@@ -807,6 +807,54 @@ const chatStopControllerKey = locals_js_1.locals.create("chat.stopController");
|
|
|
807
807
|
const chatUIStreamStaticKey = locals_js_1.locals.create("chat.uiMessageStreamOptions.static");
|
|
808
808
|
/** Per-turn UIMessageStream options, set via chat.setUIMessageStreamOptions(). @internal */
|
|
809
809
|
const chatUIStreamPerTurnKey = locals_js_1.locals.create("chat.uiMessageStreamOptions.perTurn");
|
|
810
|
+
/**
|
|
811
|
+
* Run-scoped `toolCallId → assistant messageId` map. Records the head
|
|
812
|
+
* assistant id whenever the accumulator absorbs an assistant message
|
|
813
|
+
* containing tool parts. Used as a fallback in the id-merge for
|
|
814
|
+
* incoming tool-answer messages — if the AI SDK regenerates the
|
|
815
|
+
* assistant id on a HITL `addToolOutput` resume, we look up the
|
|
816
|
+
* original head id by `toolCallId` and rewrite it before the merge.
|
|
817
|
+
*
|
|
818
|
+
* Customer-side workaround for the same case is documented in Arena
|
|
819
|
+
* AI's chat-agent task; lifting it into the SDK so customers don't
|
|
820
|
+
* have to. See TRI-9137.
|
|
821
|
+
* @internal
|
|
822
|
+
*/
|
|
823
|
+
const chatToolCallToMessageIdKey = locals_js_1.locals.create("chat.toolCallToMessageId");
|
|
824
|
+
function recordToolCallIdsFromMessage(message) {
|
|
825
|
+
if (!message || message.role !== "assistant" || !message.id)
|
|
826
|
+
return;
|
|
827
|
+
let map = locals_js_1.locals.get(chatToolCallToMessageIdKey);
|
|
828
|
+
if (!map) {
|
|
829
|
+
map = new Map();
|
|
830
|
+
locals_js_1.locals.set(chatToolCallToMessageIdKey, map);
|
|
831
|
+
}
|
|
832
|
+
for (const part of message.parts ?? []) {
|
|
833
|
+
if (typeof part !== "object" || part == null)
|
|
834
|
+
continue;
|
|
835
|
+
const toolCallId = part.toolCallId;
|
|
836
|
+
if (typeof toolCallId === "string" && toolCallId.length > 0) {
|
|
837
|
+
map.set(toolCallId, message.id);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
function rewriteIncomingIdViaToolCallMap(incoming) {
|
|
842
|
+
const map = locals_js_1.locals.get(chatToolCallToMessageIdKey);
|
|
843
|
+
if (!map || map.size === 0)
|
|
844
|
+
return incoming;
|
|
845
|
+
for (const part of incoming.parts ?? []) {
|
|
846
|
+
if (typeof part !== "object" || part == null)
|
|
847
|
+
continue;
|
|
848
|
+
const toolCallId = part.toolCallId;
|
|
849
|
+
if (typeof toolCallId !== "string" || toolCallId.length === 0)
|
|
850
|
+
continue;
|
|
851
|
+
const headId = map.get(toolCallId);
|
|
852
|
+
if (headId && headId !== incoming.id) {
|
|
853
|
+
return { ...incoming, id: headId };
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
return incoming;
|
|
857
|
+
}
|
|
810
858
|
function emptyUsage() {
|
|
811
859
|
return {
|
|
812
860
|
inputTokens: undefined,
|
|
@@ -872,19 +920,161 @@ function getChatHistoryState() {
|
|
|
872
920
|
return locals_js_1.locals.get(chatCurrentUIMessagesKey) ?? [];
|
|
873
921
|
}
|
|
874
922
|
/**
|
|
875
|
-
*
|
|
923
|
+
* Tool parts that are "done" — either succeeded with a value or failed
|
|
924
|
+
* with an error. Excludes pending (`input-streaming`/`input-available`)
|
|
925
|
+
* and approval (`approval-requested`/`approval-responded`) states.
|
|
926
|
+
* @internal
|
|
927
|
+
*/
|
|
928
|
+
function isResolvedToolState(state) {
|
|
929
|
+
return state === "output-available" || state === "output-error";
|
|
930
|
+
}
|
|
931
|
+
/** @internal */
|
|
932
|
+
function isPendingToolState(state) {
|
|
933
|
+
return state === "input-available";
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Walk an assistant message and yield each tool part with its callId,
|
|
937
|
+
* name, and state. Skips non-assistant messages and non-tool parts.
|
|
938
|
+
* @internal
|
|
939
|
+
*/
|
|
940
|
+
function* iterateToolParts(message) {
|
|
941
|
+
if (message.role !== "assistant")
|
|
942
|
+
return;
|
|
943
|
+
for (const part of (message.parts ?? [])) {
|
|
944
|
+
if (!(0, ai_1.isToolUIPart)(part))
|
|
945
|
+
continue;
|
|
946
|
+
const toolCallId = part.toolCallId;
|
|
947
|
+
if (typeof toolCallId !== "string" || toolCallId.length === 0)
|
|
948
|
+
continue;
|
|
949
|
+
yield {
|
|
950
|
+
part,
|
|
951
|
+
toolCallId,
|
|
952
|
+
toolName: (0, ai_1.getToolName)(part),
|
|
953
|
+
state: part.state,
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Tool parts on the *leaf* assistant message that are still waiting on
|
|
959
|
+
* an answer (`input-available` state). Used to gate fresh user turns
|
|
960
|
+
* during HITL flows.
|
|
961
|
+
* @internal
|
|
962
|
+
*/
|
|
963
|
+
function getPendingToolCallsFromHistory(messages) {
|
|
964
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
965
|
+
const msg = messages[i];
|
|
966
|
+
if (msg.role !== "assistant")
|
|
967
|
+
continue;
|
|
968
|
+
const pending = [];
|
|
969
|
+
for (const { toolCallId, toolName, state } of iterateToolParts(msg)) {
|
|
970
|
+
if (isPendingToolState(state)) {
|
|
971
|
+
pending.push({ toolCallId, toolName, messageId: msg.id });
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
return pending;
|
|
975
|
+
}
|
|
976
|
+
return [];
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* All tool parts across the chain that have already produced an output
|
|
980
|
+
* (`output-available` or `output-error`). Used to dedup re-saves when
|
|
981
|
+
* the AI SDK resends an assistant with progressively more answered
|
|
982
|
+
* parts.
|
|
983
|
+
* @internal
|
|
984
|
+
*/
|
|
985
|
+
function getResolvedToolCallsFromHistory(messages) {
|
|
986
|
+
const out = [];
|
|
987
|
+
for (const msg of messages) {
|
|
988
|
+
for (const { toolCallId, toolName, state } of iterateToolParts(msg)) {
|
|
989
|
+
if (isResolvedToolState(state)) {
|
|
990
|
+
out.push({ toolCallId, toolName, messageId: msg.id });
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
return out;
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Pure helper: tool parts in `message` that have a fresh result not
|
|
998
|
+
* already represented by the resolved toolCallIds in `messages`. The
|
|
999
|
+
* `errorText` field is present only for `output-error` parts.
|
|
1000
|
+
* @internal
|
|
1001
|
+
*/
|
|
1002
|
+
function extractNewToolResultsFromHistory(message, messages) {
|
|
1003
|
+
const resolved = new Set(getResolvedToolCallsFromHistory(messages).map((r) => r.toolCallId));
|
|
1004
|
+
const out = [];
|
|
1005
|
+
for (const { part, toolCallId, toolName, state } of iterateToolParts(message)) {
|
|
1006
|
+
if (!isResolvedToolState(state))
|
|
1007
|
+
continue;
|
|
1008
|
+
if (resolved.has(toolCallId))
|
|
1009
|
+
continue;
|
|
1010
|
+
if (state === "output-error") {
|
|
1011
|
+
out.push({ toolCallId, toolName, output: undefined, errorText: part.errorText });
|
|
1012
|
+
}
|
|
1013
|
+
else {
|
|
1014
|
+
out.push({ toolCallId, toolName, output: part.output });
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return out;
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Imperative API for reading and modifying the accumulated message history.
|
|
876
1021
|
*
|
|
877
1022
|
* Mutations use the same deferred override mechanism as `chat.setMessages()`:
|
|
878
|
-
* they are applied at lifecycle checkpoints (after hooks return).
|
|
1023
|
+
* they are applied at lifecycle checkpoints (after hooks return). Reads are
|
|
1024
|
+
* synchronous against the current accumulator state.
|
|
879
1025
|
*
|
|
880
1026
|
* Can be called from `onTurnStart`, `onBeforeTurnComplete`, `onTurnComplete`,
|
|
881
|
-
* `run()`, or AI SDK tools.
|
|
1027
|
+
* `run()`, `onAction`, or AI SDK tools.
|
|
882
1028
|
*/
|
|
883
1029
|
const chatHistory = {
|
|
884
1030
|
/** Read the current accumulated UI messages (copy). */
|
|
885
1031
|
all() {
|
|
886
1032
|
return [...getChatHistoryState()];
|
|
887
1033
|
},
|
|
1034
|
+
/**
|
|
1035
|
+
* Read the current chain as an ordered `UIMessage[]`. Alias for `all()` —
|
|
1036
|
+
* use this when working alongside parent-aware APIs (TRI-9120) where
|
|
1037
|
+
* "chain" disambiguates from "graph".
|
|
1038
|
+
*/
|
|
1039
|
+
getChain() {
|
|
1040
|
+
return [...getChatHistoryState()];
|
|
1041
|
+
},
|
|
1042
|
+
/**
|
|
1043
|
+
* Find a message by id. Returns `undefined` if no message with that id
|
|
1044
|
+
* is present in the current chain.
|
|
1045
|
+
*/
|
|
1046
|
+
findMessage(messageId) {
|
|
1047
|
+
return getChatHistoryState().find((m) => m.id === messageId);
|
|
1048
|
+
},
|
|
1049
|
+
/**
|
|
1050
|
+
* Tool calls on the leaf assistant message still waiting on an answer
|
|
1051
|
+
* (`input-available` state). Use this to gate fresh user turns during
|
|
1052
|
+
* HITL flows: if `getPendingToolCalls().length > 0`, an `addToolOutput`
|
|
1053
|
+
* is expected before any new user message.
|
|
1054
|
+
*
|
|
1055
|
+
* Returns `[]` if there is no assistant message yet, or if the leaf
|
|
1056
|
+
* assistant has no pending tool calls.
|
|
1057
|
+
*/
|
|
1058
|
+
getPendingToolCalls() {
|
|
1059
|
+
return getPendingToolCallsFromHistory(getChatHistoryState());
|
|
1060
|
+
},
|
|
1061
|
+
/**
|
|
1062
|
+
* Tool calls across the chain with a final result (`output-available`
|
|
1063
|
+
* or `output-error`). Use this to dedup re-saves when the AI SDK
|
|
1064
|
+
* resends an assistant message with progressively more answered parts.
|
|
1065
|
+
*/
|
|
1066
|
+
getResolvedToolCalls() {
|
|
1067
|
+
return getResolvedToolCallsFromHistory(getChatHistoryState());
|
|
1068
|
+
},
|
|
1069
|
+
/**
|
|
1070
|
+
* Pure helper: returns the tool parts in `message` whose results are
|
|
1071
|
+
* not already represented in the current chain. Use this when
|
|
1072
|
+
* persisting tool results to your own store: each call surfaces only
|
|
1073
|
+
* the *new* answers, so writes stay idempotent across re-streams.
|
|
1074
|
+
*/
|
|
1075
|
+
extractNewToolResults(message) {
|
|
1076
|
+
return extractNewToolResultsFromHistory(message, getChatHistoryState());
|
|
1077
|
+
},
|
|
888
1078
|
/** Replace all accumulated messages. Same as `chat.setMessages()`. */
|
|
889
1079
|
set(messages) {
|
|
890
1080
|
locals_js_1.locals.set(chatOverrideMessagesKey, messages);
|
|
@@ -2361,9 +2551,24 @@ function chatAgent(options) {
|
|
|
2361
2551
|
// IDs match because we always pass generateMessageId + originalMessages
|
|
2362
2552
|
// to toUIMessageStream, so the backend's start chunk carries the same
|
|
2363
2553
|
// messageId that the frontend uses.
|
|
2554
|
+
//
|
|
2555
|
+
// Fallback for HITL `addToolOutput` continuations where the AI SDK
|
|
2556
|
+
// regenerates the assistant id (Arena AI report, TRI-9137): if the
|
|
2557
|
+
// id-match fails, look up the head messageId via toolCallId and
|
|
2558
|
+
// rewrite the incoming id before retrying. The mapping is
|
|
2559
|
+
// populated whenever an assistant containing tool parts lands in
|
|
2560
|
+
// the accumulator.
|
|
2364
2561
|
let replaced = false;
|
|
2365
|
-
for (const
|
|
2366
|
-
|
|
2562
|
+
for (const raw of cleanedUIMessages) {
|
|
2563
|
+
let incoming = raw;
|
|
2564
|
+
let idx = accumulatedUIMessages.findIndex((m) => m.id === incoming.id);
|
|
2565
|
+
if (idx === -1) {
|
|
2566
|
+
const rewritten = rewriteIncomingIdViaToolCallMap(incoming);
|
|
2567
|
+
if (rewritten.id !== incoming.id) {
|
|
2568
|
+
incoming = rewritten;
|
|
2569
|
+
idx = accumulatedUIMessages.findIndex((m) => m.id === incoming.id);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2367
2572
|
if (idx !== -1) {
|
|
2368
2573
|
accumulatedUIMessages[idx] = incoming;
|
|
2369
2574
|
replaced = true;
|
|
@@ -2372,6 +2577,7 @@ function chatAgent(options) {
|
|
|
2372
2577
|
accumulatedUIMessages.push(incoming);
|
|
2373
2578
|
turnNewUIMessages.push(incoming);
|
|
2374
2579
|
}
|
|
2580
|
+
recordToolCallIdsFromMessage(incoming);
|
|
2375
2581
|
}
|
|
2376
2582
|
if (replaced) {
|
|
2377
2583
|
// Reconvert all model messages since a replacement changes the structure
|
|
@@ -2773,6 +2979,12 @@ function chatAgent(options) {
|
|
|
2773
2979
|
}
|
|
2774
2980
|
turnNewUIMessages.push(capturedResponseMessage);
|
|
2775
2981
|
locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
|
|
2982
|
+
// Record toolCallId → head messageId so a HITL
|
|
2983
|
+
// continuation next turn can recover the head id
|
|
2984
|
+
// even if the AI SDK regenerates it. See
|
|
2985
|
+
// `chatToolCallToMessageIdKey` for the full
|
|
2986
|
+
// rationale (TRI-9137).
|
|
2987
|
+
recordToolCallIdsFromMessage(capturedResponseMessage);
|
|
2776
2988
|
try {
|
|
2777
2989
|
const responseModelMessages = await toModelMessages([
|
|
2778
2990
|
stripProviderMetadata(capturedResponseMessage),
|