@trigger.dev/sdk 0.0.0-chat-prerelease-20260506093419 → 0.0.0-chat-prerelease-20260507131256
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 +66 -0
- package/dist/commonjs/v3/ai.js +235 -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 +66 -0
- package/dist/esm/v3/ai.js +236 -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,50 @@ 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[]`. Identical to
|
|
2520
|
+
* `all()`; use whichever name reads better in context.
|
|
2521
|
+
*/
|
|
2522
|
+
getChain(): UIMessage[];
|
|
2523
|
+
/**
|
|
2524
|
+
* Find a message by id. Returns `undefined` if no message with that id
|
|
2525
|
+
* is present in the current chain.
|
|
2526
|
+
*/
|
|
2527
|
+
findMessage(messageId: string): UIMessage | undefined;
|
|
2528
|
+
/**
|
|
2529
|
+
* Tool calls on the *most recent* assistant message that are still in
|
|
2530
|
+
* `input-available` state (waiting on an `addToolOutput` answer). The
|
|
2531
|
+
* scan walks back from the tail and stops at the first assistant
|
|
2532
|
+
* message it finds, so a trailing user message does not change the
|
|
2533
|
+
* result — pending tool calls remain pending until they're resolved
|
|
2534
|
+
* on that assistant or the assistant is removed.
|
|
2535
|
+
*
|
|
2536
|
+
* Use this to gate fresh user turns or actions during HITL flows: if
|
|
2537
|
+
* `getPendingToolCalls().length > 0`, an `addToolOutput` is expected.
|
|
2538
|
+
*
|
|
2539
|
+
* Returns `[]` if there is no assistant message yet, or if the most
|
|
2540
|
+
* recent assistant has no pending tool calls.
|
|
2541
|
+
*
|
|
2542
|
+
* Approval flows (`approval-requested` / `approval-responded` states)
|
|
2543
|
+
* are not surfaced here. Those are about the user authorizing a tool
|
|
2544
|
+
* to run; "pending" is about the user *answering* a tool call.
|
|
2545
|
+
*/
|
|
2546
|
+
getPendingToolCalls(): ChatToolCallRef[];
|
|
2547
|
+
/**
|
|
2548
|
+
* Tool calls across the chain with a final result (`output-available`
|
|
2549
|
+
* or `output-error`). Use this to dedup re-saves when the AI SDK
|
|
2550
|
+
* resends an assistant message with progressively more answered parts.
|
|
2551
|
+
*/
|
|
2552
|
+
getResolvedToolCalls(): ChatToolCallRef[];
|
|
2553
|
+
/**
|
|
2554
|
+
* Pure helper: returns the tool parts in `message` whose results are
|
|
2555
|
+
* not already represented in the current chain. Use this when
|
|
2556
|
+
* persisting tool results to your own store: each call surfaces only
|
|
2557
|
+
* the *new* answers, so writes stay idempotent across re-streams.
|
|
2558
|
+
* Duplicate `toolCallId`s within `message` itself are also collapsed
|
|
2559
|
+
* to a single entry.
|
|
2560
|
+
*/
|
|
2561
|
+
extractNewToolResults(message: UIMessage): ChatNewToolResult[];
|
|
2496
2562
|
/** Replace all accumulated messages. Same as `chat.setMessages()`. */
|
|
2497
2563
|
set(messages: UIMessage[]): void;
|
|
2498
2564
|
/** 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,179 @@ 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
|
+
*
|
|
1001
|
+
* Within a single `message`, duplicate `toolCallId`s emit only once
|
|
1002
|
+
* (first occurrence wins). This guards against malformed assistants
|
|
1003
|
+
* with repeated tool parts.
|
|
1004
|
+
* @internal
|
|
1005
|
+
*/
|
|
1006
|
+
function extractNewToolResultsFromHistory(message, messages) {
|
|
1007
|
+
const resolved = new Set(getResolvedToolCallsFromHistory(messages).map((r) => r.toolCallId));
|
|
1008
|
+
const seen = new Set();
|
|
1009
|
+
const out = [];
|
|
1010
|
+
for (const { part, toolCallId, toolName, state } of iterateToolParts(message)) {
|
|
1011
|
+
if (!isResolvedToolState(state))
|
|
1012
|
+
continue;
|
|
1013
|
+
if (resolved.has(toolCallId))
|
|
1014
|
+
continue;
|
|
1015
|
+
if (seen.has(toolCallId))
|
|
1016
|
+
continue;
|
|
1017
|
+
seen.add(toolCallId);
|
|
1018
|
+
if (state === "output-error") {
|
|
1019
|
+
out.push({ toolCallId, toolName, output: undefined, errorText: part.errorText });
|
|
1020
|
+
}
|
|
1021
|
+
else {
|
|
1022
|
+
out.push({ toolCallId, toolName, output: part.output });
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
return out;
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Imperative API for reading and modifying the accumulated message history.
|
|
876
1029
|
*
|
|
877
1030
|
* Mutations use the same deferred override mechanism as `chat.setMessages()`:
|
|
878
|
-
* they are applied at lifecycle checkpoints (after hooks return).
|
|
1031
|
+
* they are applied at lifecycle checkpoints (after hooks return). Reads are
|
|
1032
|
+
* synchronous against the current accumulator state.
|
|
879
1033
|
*
|
|
880
1034
|
* Can be called from `onTurnStart`, `onBeforeTurnComplete`, `onTurnComplete`,
|
|
881
|
-
* `run()`, or AI SDK tools.
|
|
1035
|
+
* `run()`, `onAction`, or AI SDK tools.
|
|
882
1036
|
*/
|
|
883
1037
|
const chatHistory = {
|
|
884
1038
|
/** Read the current accumulated UI messages (copy). */
|
|
885
1039
|
all() {
|
|
886
1040
|
return [...getChatHistoryState()];
|
|
887
1041
|
},
|
|
1042
|
+
/**
|
|
1043
|
+
* Read the current chain as an ordered `UIMessage[]`. Identical to
|
|
1044
|
+
* `all()`; use whichever name reads better in context.
|
|
1045
|
+
*/
|
|
1046
|
+
getChain() {
|
|
1047
|
+
return chatHistory.all();
|
|
1048
|
+
},
|
|
1049
|
+
/**
|
|
1050
|
+
* Find a message by id. Returns `undefined` if no message with that id
|
|
1051
|
+
* is present in the current chain.
|
|
1052
|
+
*/
|
|
1053
|
+
findMessage(messageId) {
|
|
1054
|
+
return getChatHistoryState().find((m) => m.id === messageId);
|
|
1055
|
+
},
|
|
1056
|
+
/**
|
|
1057
|
+
* Tool calls on the *most recent* assistant message that are still in
|
|
1058
|
+
* `input-available` state (waiting on an `addToolOutput` answer). The
|
|
1059
|
+
* scan walks back from the tail and stops at the first assistant
|
|
1060
|
+
* message it finds, so a trailing user message does not change the
|
|
1061
|
+
* result — pending tool calls remain pending until they're resolved
|
|
1062
|
+
* on that assistant or the assistant is removed.
|
|
1063
|
+
*
|
|
1064
|
+
* Use this to gate fresh user turns or actions during HITL flows: if
|
|
1065
|
+
* `getPendingToolCalls().length > 0`, an `addToolOutput` is expected.
|
|
1066
|
+
*
|
|
1067
|
+
* Returns `[]` if there is no assistant message yet, or if the most
|
|
1068
|
+
* recent assistant has no pending tool calls.
|
|
1069
|
+
*
|
|
1070
|
+
* Approval flows (`approval-requested` / `approval-responded` states)
|
|
1071
|
+
* are not surfaced here. Those are about the user authorizing a tool
|
|
1072
|
+
* to run; "pending" is about the user *answering* a tool call.
|
|
1073
|
+
*/
|
|
1074
|
+
getPendingToolCalls() {
|
|
1075
|
+
return getPendingToolCallsFromHistory(getChatHistoryState());
|
|
1076
|
+
},
|
|
1077
|
+
/**
|
|
1078
|
+
* Tool calls across the chain with a final result (`output-available`
|
|
1079
|
+
* or `output-error`). Use this to dedup re-saves when the AI SDK
|
|
1080
|
+
* resends an assistant message with progressively more answered parts.
|
|
1081
|
+
*/
|
|
1082
|
+
getResolvedToolCalls() {
|
|
1083
|
+
return getResolvedToolCallsFromHistory(getChatHistoryState());
|
|
1084
|
+
},
|
|
1085
|
+
/**
|
|
1086
|
+
* Pure helper: returns the tool parts in `message` whose results are
|
|
1087
|
+
* not already represented in the current chain. Use this when
|
|
1088
|
+
* persisting tool results to your own store: each call surfaces only
|
|
1089
|
+
* the *new* answers, so writes stay idempotent across re-streams.
|
|
1090
|
+
* Duplicate `toolCallId`s within `message` itself are also collapsed
|
|
1091
|
+
* to a single entry.
|
|
1092
|
+
*/
|
|
1093
|
+
extractNewToolResults(message) {
|
|
1094
|
+
return extractNewToolResultsFromHistory(message, getChatHistoryState());
|
|
1095
|
+
},
|
|
888
1096
|
/** Replace all accumulated messages. Same as `chat.setMessages()`. */
|
|
889
1097
|
set(messages) {
|
|
890
1098
|
locals_js_1.locals.set(chatOverrideMessagesKey, messages);
|
|
@@ -2361,9 +2569,24 @@ function chatAgent(options) {
|
|
|
2361
2569
|
// IDs match because we always pass generateMessageId + originalMessages
|
|
2362
2570
|
// to toUIMessageStream, so the backend's start chunk carries the same
|
|
2363
2571
|
// messageId that the frontend uses.
|
|
2572
|
+
//
|
|
2573
|
+
// Fallback for HITL `addToolOutput` continuations where the AI SDK
|
|
2574
|
+
// regenerates the assistant id (Arena AI report, TRI-9137): if the
|
|
2575
|
+
// id-match fails, look up the head messageId via toolCallId and
|
|
2576
|
+
// rewrite the incoming id before retrying. The mapping is
|
|
2577
|
+
// populated whenever an assistant containing tool parts lands in
|
|
2578
|
+
// the accumulator.
|
|
2364
2579
|
let replaced = false;
|
|
2365
|
-
for (const
|
|
2366
|
-
|
|
2580
|
+
for (const raw of cleanedUIMessages) {
|
|
2581
|
+
let incoming = raw;
|
|
2582
|
+
let idx = accumulatedUIMessages.findIndex((m) => m.id === incoming.id);
|
|
2583
|
+
if (idx === -1) {
|
|
2584
|
+
const rewritten = rewriteIncomingIdViaToolCallMap(incoming);
|
|
2585
|
+
if (rewritten.id !== incoming.id) {
|
|
2586
|
+
incoming = rewritten;
|
|
2587
|
+
idx = accumulatedUIMessages.findIndex((m) => m.id === incoming.id);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2367
2590
|
if (idx !== -1) {
|
|
2368
2591
|
accumulatedUIMessages[idx] = incoming;
|
|
2369
2592
|
replaced = true;
|
|
@@ -2372,6 +2595,7 @@ function chatAgent(options) {
|
|
|
2372
2595
|
accumulatedUIMessages.push(incoming);
|
|
2373
2596
|
turnNewUIMessages.push(incoming);
|
|
2374
2597
|
}
|
|
2598
|
+
recordToolCallIdsFromMessage(incoming);
|
|
2375
2599
|
}
|
|
2376
2600
|
if (replaced) {
|
|
2377
2601
|
// Reconvert all model messages since a replacement changes the structure
|
|
@@ -2773,6 +2997,12 @@ function chatAgent(options) {
|
|
|
2773
2997
|
}
|
|
2774
2998
|
turnNewUIMessages.push(capturedResponseMessage);
|
|
2775
2999
|
locals_js_1.locals.set(chatCurrentUIMessagesKey, accumulatedUIMessages);
|
|
3000
|
+
// Record toolCallId → head messageId so a HITL
|
|
3001
|
+
// continuation next turn can recover the head id
|
|
3002
|
+
// even if the AI SDK regenerates it. See
|
|
3003
|
+
// `chatToolCallToMessageIdKey` for the full
|
|
3004
|
+
// rationale (TRI-9137).
|
|
3005
|
+
recordToolCallIdsFromMessage(capturedResponseMessage);
|
|
2776
3006
|
try {
|
|
2777
3007
|
const responseModelMessages = await toModelMessages([
|
|
2778
3008
|
stripProviderMetadata(capturedResponseMessage),
|