@trigger.dev/sdk 0.0.0-prerelease-20260304181730 → 0.0.0-prerelease-20260305142821
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 +118 -16
- package/dist/commonjs/v3/ai.js +212 -33
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/chat-react.d.ts +3 -0
- package/dist/commonjs/v3/chat-react.js +8 -0
- package/dist/commonjs/v3/chat-react.js.map +1 -1
- package/dist/commonjs/v3/chat.d.ts +85 -0
- package/dist/commonjs/v3/chat.js +76 -3
- package/dist/commonjs/v3/chat.js.map +1 -1
- package/dist/commonjs/v3/chat.test.js +325 -72
- package/dist/commonjs/v3/chat.test.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/ai.d.ts +118 -16
- package/dist/esm/v3/ai.js +212 -33
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/chat-react.d.ts +3 -0
- package/dist/esm/v3/chat-react.js +9 -1
- package/dist/esm/v3/chat-react.js.map +1 -1
- package/dist/esm/v3/chat.d.ts +85 -0
- package/dist/esm/v3/chat.js +76 -3
- package/dist/esm/v3/chat.js.map +1 -1
- package/dist/esm/v3/chat.test.js +325 -72
- package/dist/esm/v3/chat.test.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +2 -2
|
@@ -516,6 +516,7 @@ const sampleChunks = [
|
|
|
516
516
|
(0, vitest_1.describe)("multiple sessions", () => {
|
|
517
517
|
(0, vitest_1.it)("should track multiple chat sessions independently", async () => {
|
|
518
518
|
let callCount = 0;
|
|
519
|
+
const turnCompleteChunk = { type: "__trigger_turn_complete" };
|
|
519
520
|
global.fetch = vitest_1.vi.fn().mockImplementation(async (url) => {
|
|
520
521
|
const urlStr = typeof url === "string" ? url : url.toString();
|
|
521
522
|
if (urlStr.includes("/trigger")) {
|
|
@@ -529,7 +530,9 @@ const sampleChunks = [
|
|
|
529
530
|
});
|
|
530
531
|
}
|
|
531
532
|
if (urlStr.includes("/realtime/v1/streams/")) {
|
|
532
|
-
|
|
533
|
+
// Include turn-complete chunk so the session is preserved
|
|
534
|
+
const chunks = [...sampleChunks, turnCompleteChunk];
|
|
535
|
+
return new Response(createSSEStream(sseEncode(chunks)), {
|
|
533
536
|
status: 200,
|
|
534
537
|
headers: {
|
|
535
538
|
"content-type": "text/event-stream",
|
|
@@ -544,21 +547,25 @@ const sampleChunks = [
|
|
|
544
547
|
accessToken: "token",
|
|
545
548
|
baseURL: "https://api.test.trigger.dev",
|
|
546
549
|
});
|
|
547
|
-
// Start two independent chat sessions
|
|
548
|
-
await transport.sendMessages({
|
|
550
|
+
// Start two independent chat sessions and consume the streams
|
|
551
|
+
const s1 = await transport.sendMessages({
|
|
549
552
|
trigger: "submit-message",
|
|
550
553
|
chatId: "session-a",
|
|
551
554
|
messageId: undefined,
|
|
552
555
|
messages: [createUserMessage("Hello A")],
|
|
553
556
|
abortSignal: undefined,
|
|
554
557
|
});
|
|
555
|
-
|
|
558
|
+
const r1 = s1.getReader();
|
|
559
|
+
while (!(await r1.read()).done) { }
|
|
560
|
+
const s2 = await transport.sendMessages({
|
|
556
561
|
trigger: "submit-message",
|
|
557
562
|
chatId: "session-b",
|
|
558
563
|
messageId: undefined,
|
|
559
564
|
messages: [createUserMessage("Hello B")],
|
|
560
565
|
abortSignal: undefined,
|
|
561
566
|
});
|
|
567
|
+
const r2 = s2.getReader();
|
|
568
|
+
while (!(await r2.read()).done) { }
|
|
562
569
|
// Both sessions should be independently reconnectable
|
|
563
570
|
const streamA = await transport.reconnectToStream({ chatId: "session-a" });
|
|
564
571
|
const streamB = await transport.reconnectToStream({ chatId: "session-b" });
|
|
@@ -727,11 +734,7 @@ const sampleChunks = [
|
|
|
727
734
|
});
|
|
728
735
|
(0, vitest_1.describe)("lastEventId tracking", () => {
|
|
729
736
|
(0, vitest_1.it)("should pass lastEventId to SSE subscription on subsequent turns", async () => {
|
|
730
|
-
const
|
|
731
|
-
type: "__trigger_waitpoint_ready",
|
|
732
|
-
tokenId: "wp_token_eid",
|
|
733
|
-
publicAccessToken: "wp_access_eid",
|
|
734
|
-
};
|
|
737
|
+
const turnCompleteChunk = { type: "__trigger_turn_complete" };
|
|
735
738
|
let triggerCallCount = 0;
|
|
736
739
|
const streamFetchCalls = [];
|
|
737
740
|
global.fetch = vitest_1.vi.fn().mockImplementation(async (url, init) => {
|
|
@@ -746,8 +749,9 @@ const sampleChunks = [
|
|
|
746
749
|
},
|
|
747
750
|
});
|
|
748
751
|
}
|
|
749
|
-
|
|
750
|
-
|
|
752
|
+
// Handle input stream sends (for second message)
|
|
753
|
+
if (urlStr.includes("/realtime/v1/streams/") && urlStr.includes("/input/")) {
|
|
754
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
751
755
|
status: 200,
|
|
752
756
|
headers: { "content-type": "application/json" },
|
|
753
757
|
});
|
|
@@ -760,7 +764,7 @@ const sampleChunks = [
|
|
|
760
764
|
const chunks = [
|
|
761
765
|
...sampleChunks,
|
|
762
766
|
{ type: "finish", id: "part-1" },
|
|
763
|
-
|
|
767
|
+
turnCompleteChunk,
|
|
764
768
|
];
|
|
765
769
|
return new Response(createSSEStream(sseEncode(chunks)), {
|
|
766
770
|
status: 200,
|
|
@@ -791,7 +795,7 @@ const sampleChunks = [
|
|
|
791
795
|
if (done)
|
|
792
796
|
break;
|
|
793
797
|
}
|
|
794
|
-
// Second message —
|
|
798
|
+
// Second message — sends via input stream
|
|
795
799
|
const stream2 = await transport.sendMessages({
|
|
796
800
|
trigger: "submit-message",
|
|
797
801
|
chatId: "chat-eid",
|
|
@@ -812,13 +816,125 @@ const sampleChunks = [
|
|
|
812
816
|
(0, vitest_1.expect)(secondStreamHeaders["Last-Event-ID"]).toBeDefined();
|
|
813
817
|
});
|
|
814
818
|
});
|
|
819
|
+
(0, vitest_1.describe)("minimal wire payloads", () => {
|
|
820
|
+
(0, vitest_1.it)("should send only new messages via input stream on turn 2+", async () => {
|
|
821
|
+
const turnCompleteChunk = { type: "__trigger_turn_complete" };
|
|
822
|
+
const inputStreamPayloads = [];
|
|
823
|
+
global.fetch = vitest_1.vi.fn().mockImplementation(async (url, init) => {
|
|
824
|
+
const urlStr = typeof url === "string" ? url : url.toString();
|
|
825
|
+
if (urlStr.includes("/api/v1/tasks/") && urlStr.includes("/trigger")) {
|
|
826
|
+
return new Response(JSON.stringify({ id: "run_minimal" }), {
|
|
827
|
+
status: 200,
|
|
828
|
+
headers: {
|
|
829
|
+
"content-type": "application/json",
|
|
830
|
+
"x-trigger-jwt": "pub_token_minimal",
|
|
831
|
+
},
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
// Capture input stream payloads (ApiClient wraps in { data: ... })
|
|
835
|
+
if (urlStr.includes("/realtime/v1/streams/") && urlStr.includes("/input/")) {
|
|
836
|
+
const body = JSON.parse(init?.body);
|
|
837
|
+
inputStreamPayloads.push(body.data);
|
|
838
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
839
|
+
status: 200,
|
|
840
|
+
headers: { "content-type": "application/json" },
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
if (urlStr.includes("/realtime/v1/streams/")) {
|
|
844
|
+
const chunks = [
|
|
845
|
+
...sampleChunks,
|
|
846
|
+
turnCompleteChunk,
|
|
847
|
+
];
|
|
848
|
+
return new Response(createSSEStream(sseEncode(chunks)), {
|
|
849
|
+
status: 200,
|
|
850
|
+
headers: {
|
|
851
|
+
"content-type": "text/event-stream",
|
|
852
|
+
"X-Stream-Version": "v1",
|
|
853
|
+
},
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
throw new Error(`Unexpected fetch URL: ${urlStr}`);
|
|
857
|
+
});
|
|
858
|
+
const transport = new chat_js_1.TriggerChatTransport({
|
|
859
|
+
task: "my-task",
|
|
860
|
+
accessToken: "token",
|
|
861
|
+
baseURL: "https://api.test.trigger.dev",
|
|
862
|
+
});
|
|
863
|
+
const userMsg1 = createUserMessage("Hello");
|
|
864
|
+
const assistantMsg = createAssistantMessage("Hi there!");
|
|
865
|
+
const userMsg2 = createUserMessage("What's up?");
|
|
866
|
+
// Turn 1 — triggers a new run with full history
|
|
867
|
+
const stream1 = await transport.sendMessages({
|
|
868
|
+
trigger: "submit-message",
|
|
869
|
+
chatId: "chat-minimal",
|
|
870
|
+
messageId: undefined,
|
|
871
|
+
messages: [userMsg1],
|
|
872
|
+
abortSignal: undefined,
|
|
873
|
+
});
|
|
874
|
+
const r1 = stream1.getReader();
|
|
875
|
+
while (!(await r1.read()).done) { }
|
|
876
|
+
// Turn 2 — sends via input stream, should only include NEW messages
|
|
877
|
+
const stream2 = await transport.sendMessages({
|
|
878
|
+
trigger: "submit-message",
|
|
879
|
+
chatId: "chat-minimal",
|
|
880
|
+
messageId: undefined,
|
|
881
|
+
messages: [userMsg1, assistantMsg, userMsg2],
|
|
882
|
+
abortSignal: undefined,
|
|
883
|
+
});
|
|
884
|
+
const r2 = stream2.getReader();
|
|
885
|
+
while (!(await r2.read()).done) { }
|
|
886
|
+
// Verify: the input stream payload should only contain the new user message
|
|
887
|
+
(0, vitest_1.expect)(inputStreamPayloads).toHaveLength(1);
|
|
888
|
+
const sentPayload = inputStreamPayloads[0];
|
|
889
|
+
// Only the new user message should be sent (backend already has the assistant response)
|
|
890
|
+
(0, vitest_1.expect)(sentPayload.messages).toHaveLength(1);
|
|
891
|
+
(0, vitest_1.expect)(sentPayload.messages[0]).toEqual(userMsg2);
|
|
892
|
+
});
|
|
893
|
+
(0, vitest_1.it)("should send full history on first message (trigger)", async () => {
|
|
894
|
+
let triggerPayload;
|
|
895
|
+
global.fetch = vitest_1.vi.fn().mockImplementation(async (url, init) => {
|
|
896
|
+
const urlStr = typeof url === "string" ? url : url.toString();
|
|
897
|
+
if (urlStr.includes("/api/v1/tasks/") && urlStr.includes("/trigger")) {
|
|
898
|
+
triggerPayload = JSON.parse(init?.body);
|
|
899
|
+
return new Response(JSON.stringify({ id: "run_full" }), {
|
|
900
|
+
status: 200,
|
|
901
|
+
headers: {
|
|
902
|
+
"content-type": "application/json",
|
|
903
|
+
"x-trigger-jwt": "pub_token_full",
|
|
904
|
+
},
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
if (urlStr.includes("/realtime/v1/streams/")) {
|
|
908
|
+
return new Response(createSSEStream(sseEncode(sampleChunks)), {
|
|
909
|
+
status: 200,
|
|
910
|
+
headers: {
|
|
911
|
+
"content-type": "text/event-stream",
|
|
912
|
+
"X-Stream-Version": "v1",
|
|
913
|
+
},
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
throw new Error(`Unexpected fetch URL: ${urlStr}`);
|
|
917
|
+
});
|
|
918
|
+
const transport = new chat_js_1.TriggerChatTransport({
|
|
919
|
+
task: "my-task",
|
|
920
|
+
accessToken: "token",
|
|
921
|
+
baseURL: "https://api.test.trigger.dev",
|
|
922
|
+
});
|
|
923
|
+
const messages = [createUserMessage("Hello"), createAssistantMessage("Hi!"), createUserMessage("More")];
|
|
924
|
+
await transport.sendMessages({
|
|
925
|
+
trigger: "submit-message",
|
|
926
|
+
chatId: "chat-full",
|
|
927
|
+
messageId: undefined,
|
|
928
|
+
messages,
|
|
929
|
+
abortSignal: undefined,
|
|
930
|
+
});
|
|
931
|
+
// First message always sends full history via trigger
|
|
932
|
+
(0, vitest_1.expect)(triggerPayload.payload.messages).toHaveLength(3);
|
|
933
|
+
});
|
|
934
|
+
});
|
|
815
935
|
(0, vitest_1.describe)("AbortController cleanup", () => {
|
|
816
936
|
(0, vitest_1.it)("should terminate SSE connection after intercepting control chunk", async () => {
|
|
817
|
-
const controlChunk = {
|
|
818
|
-
type: "__trigger_waitpoint_ready",
|
|
819
|
-
tokenId: "wp_token_abort",
|
|
820
|
-
publicAccessToken: "wp_access_abort",
|
|
821
|
-
};
|
|
937
|
+
const controlChunk = { type: "__trigger_turn_complete" };
|
|
822
938
|
let streamAborted = false;
|
|
823
939
|
global.fetch = vitest_1.vi.fn().mockImplementation(async (url, init) => {
|
|
824
940
|
const urlStr = typeof url === "string" ? url : url.toString();
|
|
@@ -925,14 +1041,10 @@ const sampleChunks = [
|
|
|
925
1041
|
});
|
|
926
1042
|
(0, vitest_1.expect)(tokenCallCount).toBe(1);
|
|
927
1043
|
});
|
|
928
|
-
(0, vitest_1.it)("should resolve async token for
|
|
929
|
-
const
|
|
930
|
-
type: "__trigger_waitpoint_ready",
|
|
931
|
-
tokenId: "wp_token_async",
|
|
932
|
-
publicAccessToken: "wp_access_async",
|
|
933
|
-
};
|
|
1044
|
+
(0, vitest_1.it)("should not resolve async token for input stream send flow", async () => {
|
|
1045
|
+
const turnCompleteChunk = { type: "__trigger_turn_complete" };
|
|
934
1046
|
let tokenCallCount = 0;
|
|
935
|
-
let
|
|
1047
|
+
let inputStreamSendCalled = false;
|
|
936
1048
|
global.fetch = vitest_1.vi.fn().mockImplementation(async (url) => {
|
|
937
1049
|
const urlStr = typeof url === "string" ? url : url.toString();
|
|
938
1050
|
if (urlStr.includes("/api/v1/tasks/") && urlStr.includes("/trigger")) {
|
|
@@ -944,9 +1056,10 @@ const sampleChunks = [
|
|
|
944
1056
|
},
|
|
945
1057
|
});
|
|
946
1058
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1059
|
+
// Handle input stream sends
|
|
1060
|
+
if (urlStr.includes("/realtime/v1/streams/") && urlStr.includes("/input/")) {
|
|
1061
|
+
inputStreamSendCalled = true;
|
|
1062
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
950
1063
|
status: 200,
|
|
951
1064
|
headers: { "content-type": "application/json" },
|
|
952
1065
|
});
|
|
@@ -955,7 +1068,7 @@ const sampleChunks = [
|
|
|
955
1068
|
const chunks = [
|
|
956
1069
|
...sampleChunks,
|
|
957
1070
|
{ type: "finish", id: "part-1" },
|
|
958
|
-
|
|
1071
|
+
turnCompleteChunk,
|
|
959
1072
|
];
|
|
960
1073
|
return new Response(createSSEStream(sseEncode(chunks)), {
|
|
961
1074
|
status: 200,
|
|
@@ -991,7 +1104,7 @@ const sampleChunks = [
|
|
|
991
1104
|
break;
|
|
992
1105
|
}
|
|
993
1106
|
const firstTokenCount = tokenCallCount;
|
|
994
|
-
// Second message — should
|
|
1107
|
+
// Second message — should send via input stream (does NOT call async token)
|
|
995
1108
|
const stream2 = await transport.sendMessages({
|
|
996
1109
|
trigger: "submit-message",
|
|
997
1110
|
chatId: "chat-async-wp",
|
|
@@ -1005,18 +1118,14 @@ const sampleChunks = [
|
|
|
1005
1118
|
if (done)
|
|
1006
1119
|
break;
|
|
1007
1120
|
}
|
|
1008
|
-
// Token function should NOT have been called again for the
|
|
1121
|
+
// Token function should NOT have been called again for the input stream path
|
|
1009
1122
|
(0, vitest_1.expect)(tokenCallCount).toBe(firstTokenCount);
|
|
1010
|
-
(0, vitest_1.expect)(
|
|
1123
|
+
(0, vitest_1.expect)(inputStreamSendCalled).toBe(true);
|
|
1011
1124
|
});
|
|
1012
1125
|
});
|
|
1013
|
-
(0, vitest_1.describe)("single-run mode (
|
|
1014
|
-
(0, vitest_1.it)("should
|
|
1015
|
-
const
|
|
1016
|
-
type: "__trigger_waitpoint_ready",
|
|
1017
|
-
tokenId: "wp_token_123",
|
|
1018
|
-
publicAccessToken: "wp_access_abc",
|
|
1019
|
-
};
|
|
1126
|
+
(0, vitest_1.describe)("single-run mode (input stream loop)", () => {
|
|
1127
|
+
(0, vitest_1.it)("should not forward turn-complete control chunk to consumer", async () => {
|
|
1128
|
+
const turnCompleteChunk = { type: "__trigger_turn_complete" };
|
|
1020
1129
|
global.fetch = vitest_1.vi.fn().mockImplementation(async (url) => {
|
|
1021
1130
|
const urlStr = typeof url === "string" ? url : url.toString();
|
|
1022
1131
|
if (urlStr.includes("/trigger")) {
|
|
@@ -1032,7 +1141,7 @@ const sampleChunks = [
|
|
|
1032
1141
|
const chunks = [
|
|
1033
1142
|
...sampleChunks,
|
|
1034
1143
|
{ type: "finish", id: "part-1" },
|
|
1035
|
-
|
|
1144
|
+
turnCompleteChunk,
|
|
1036
1145
|
];
|
|
1037
1146
|
return new Response(createSSEStream(sseEncode(chunks)), {
|
|
1038
1147
|
status: 200,
|
|
@@ -1068,16 +1177,12 @@ const sampleChunks = [
|
|
|
1068
1177
|
// All AI SDK chunks should be forwarded
|
|
1069
1178
|
(0, vitest_1.expect)(receivedChunks.length).toBe(sampleChunks.length + 1); // +1 for the finish chunk
|
|
1070
1179
|
// Control chunk should not be in the output
|
|
1071
|
-
(0, vitest_1.expect)(receivedChunks.every((c) => c.type !== "
|
|
1180
|
+
(0, vitest_1.expect)(receivedChunks.every((c) => c.type !== "__trigger_turn_complete")).toBe(true);
|
|
1072
1181
|
});
|
|
1073
|
-
(0, vitest_1.it)("should
|
|
1074
|
-
const
|
|
1075
|
-
type: "__trigger_waitpoint_ready",
|
|
1076
|
-
tokenId: "wp_token_456",
|
|
1077
|
-
publicAccessToken: "wp_access_def",
|
|
1078
|
-
};
|
|
1182
|
+
(0, vitest_1.it)("should send via input stream on second message instead of triggering a new run", async () => {
|
|
1183
|
+
const turnCompleteChunk = { type: "__trigger_turn_complete" };
|
|
1079
1184
|
let triggerCallCount = 0;
|
|
1080
|
-
let
|
|
1185
|
+
let inputStreamSendCalled = false;
|
|
1081
1186
|
global.fetch = vitest_1.vi.fn().mockImplementation(async (url, init) => {
|
|
1082
1187
|
const urlStr = typeof url === "string" ? url : url.toString();
|
|
1083
1188
|
if (urlStr.includes("/api/v1/tasks/") && urlStr.includes("/trigger")) {
|
|
@@ -1090,10 +1195,10 @@ const sampleChunks = [
|
|
|
1090
1195
|
},
|
|
1091
1196
|
});
|
|
1092
1197
|
}
|
|
1093
|
-
// Handle
|
|
1094
|
-
if (urlStr.includes("/
|
|
1095
|
-
|
|
1096
|
-
return new Response(JSON.stringify({
|
|
1198
|
+
// Handle input stream sends
|
|
1199
|
+
if (urlStr.includes("/realtime/v1/streams/") && urlStr.includes("/input/")) {
|
|
1200
|
+
inputStreamSendCalled = true;
|
|
1201
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
1097
1202
|
status: 200,
|
|
1098
1203
|
headers: { "content-type": "application/json" },
|
|
1099
1204
|
});
|
|
@@ -1102,7 +1207,7 @@ const sampleChunks = [
|
|
|
1102
1207
|
const chunks = [
|
|
1103
1208
|
...sampleChunks,
|
|
1104
1209
|
{ type: "finish", id: "part-1" },
|
|
1105
|
-
|
|
1210
|
+
turnCompleteChunk,
|
|
1106
1211
|
];
|
|
1107
1212
|
return new Response(createSSEStream(sseEncode(chunks)), {
|
|
1108
1213
|
status: 200,
|
|
@@ -1127,7 +1232,7 @@ const sampleChunks = [
|
|
|
1127
1232
|
messages: [createUserMessage("Hello")],
|
|
1128
1233
|
abortSignal: undefined,
|
|
1129
1234
|
});
|
|
1130
|
-
// Consume stream
|
|
1235
|
+
// Consume stream
|
|
1131
1236
|
const reader1 = stream1.getReader();
|
|
1132
1237
|
while (true) {
|
|
1133
1238
|
const { done } = await reader1.read();
|
|
@@ -1135,7 +1240,7 @@ const sampleChunks = [
|
|
|
1135
1240
|
break;
|
|
1136
1241
|
}
|
|
1137
1242
|
(0, vitest_1.expect)(triggerCallCount).toBe(1);
|
|
1138
|
-
// Second message — should
|
|
1243
|
+
// Second message — should send via input stream instead of triggering
|
|
1139
1244
|
const stream2 = await transport.sendMessages({
|
|
1140
1245
|
trigger: "submit-message",
|
|
1141
1246
|
chatId: "chat-resume",
|
|
@@ -1152,8 +1257,8 @@ const sampleChunks = [
|
|
|
1152
1257
|
}
|
|
1153
1258
|
// Should NOT have triggered a second run
|
|
1154
1259
|
(0, vitest_1.expect)(triggerCallCount).toBe(1);
|
|
1155
|
-
// Should have
|
|
1156
|
-
(0, vitest_1.expect)(
|
|
1260
|
+
// Should have sent via input stream
|
|
1261
|
+
(0, vitest_1.expect)(inputStreamSendCalled).toBe(true);
|
|
1157
1262
|
});
|
|
1158
1263
|
(0, vitest_1.it)("should fall back to triggering a new run if stream closes without control chunk", async () => {
|
|
1159
1264
|
let triggerCallCount = 0;
|
|
@@ -1217,12 +1322,8 @@ const sampleChunks = [
|
|
|
1217
1322
|
// Should have triggered a second run
|
|
1218
1323
|
(0, vitest_1.expect)(triggerCallCount).toBe(2);
|
|
1219
1324
|
});
|
|
1220
|
-
(0, vitest_1.it)("should fall back to new run when
|
|
1221
|
-
const
|
|
1222
|
-
type: "__trigger_waitpoint_ready",
|
|
1223
|
-
tokenId: "wp_token_fail",
|
|
1224
|
-
publicAccessToken: "wp_access_fail",
|
|
1225
|
-
};
|
|
1325
|
+
(0, vitest_1.it)("should fall back to new run when sendInputStream fails", async () => {
|
|
1326
|
+
const turnCompleteChunk = { type: "__trigger_turn_complete" };
|
|
1226
1327
|
let triggerCallCount = 0;
|
|
1227
1328
|
global.fetch = vitest_1.vi.fn().mockImplementation(async (url) => {
|
|
1228
1329
|
const urlStr = typeof url === "string" ? url : url.toString();
|
|
@@ -1236,22 +1337,19 @@ const sampleChunks = [
|
|
|
1236
1337
|
},
|
|
1237
1338
|
});
|
|
1238
1339
|
}
|
|
1239
|
-
//
|
|
1240
|
-
if (urlStr.includes("/
|
|
1241
|
-
return new Response(JSON.stringify({ error: "
|
|
1242
|
-
status:
|
|
1340
|
+
// Input stream send fails
|
|
1341
|
+
if (urlStr.includes("/realtime/v1/streams/") && urlStr.includes("/input/")) {
|
|
1342
|
+
return new Response(JSON.stringify({ error: "Run not found" }), {
|
|
1343
|
+
status: 404,
|
|
1243
1344
|
headers: { "content-type": "application/json" },
|
|
1244
1345
|
});
|
|
1245
1346
|
}
|
|
1246
1347
|
if (urlStr.includes("/realtime/v1/streams/")) {
|
|
1247
|
-
// First call has control chunk, subsequent calls don't
|
|
1248
1348
|
const chunks = [
|
|
1249
1349
|
...sampleChunks,
|
|
1250
1350
|
{ type: "finish", id: "part-1" },
|
|
1351
|
+
turnCompleteChunk,
|
|
1251
1352
|
];
|
|
1252
|
-
if (triggerCallCount <= 1) {
|
|
1253
|
-
chunks.push(controlChunk);
|
|
1254
|
-
}
|
|
1255
1353
|
return new Response(createSSEStream(sseEncode(chunks)), {
|
|
1256
1354
|
status: 200,
|
|
1257
1355
|
headers: {
|
|
@@ -1282,7 +1380,7 @@ const sampleChunks = [
|
|
|
1282
1380
|
break;
|
|
1283
1381
|
}
|
|
1284
1382
|
(0, vitest_1.expect)(triggerCallCount).toBe(1);
|
|
1285
|
-
// Second message —
|
|
1383
|
+
// Second message — sendInputStream will fail, should fall back to new run
|
|
1286
1384
|
const stream2 = await transport.sendMessages({
|
|
1287
1385
|
trigger: "submit-message",
|
|
1288
1386
|
chatId: "chat-fail",
|
|
@@ -1300,5 +1398,160 @@ const sampleChunks = [
|
|
|
1300
1398
|
(0, vitest_1.expect)(triggerCallCount).toBe(2);
|
|
1301
1399
|
});
|
|
1302
1400
|
});
|
|
1401
|
+
(0, vitest_1.describe)("onSessionChange", () => {
|
|
1402
|
+
(0, vitest_1.it)("should fire when a new session is created", async () => {
|
|
1403
|
+
const onSessionChange = vitest_1.vi.fn();
|
|
1404
|
+
const triggerRunId = "run_session_new";
|
|
1405
|
+
const publicToken = "pub_session_new";
|
|
1406
|
+
global.fetch = vitest_1.vi.fn().mockImplementation(async (url) => {
|
|
1407
|
+
const urlStr = typeof url === "string" ? url : url.toString();
|
|
1408
|
+
if (urlStr.includes("/trigger")) {
|
|
1409
|
+
return new Response(JSON.stringify({ id: triggerRunId }), {
|
|
1410
|
+
status: 200,
|
|
1411
|
+
headers: {
|
|
1412
|
+
"content-type": "application/json",
|
|
1413
|
+
"x-trigger-jwt": publicToken,
|
|
1414
|
+
},
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
if (urlStr.includes("/realtime/v1/streams/")) {
|
|
1418
|
+
const chunks = [
|
|
1419
|
+
...sampleChunks,
|
|
1420
|
+
{ type: "__trigger_turn_complete" },
|
|
1421
|
+
];
|
|
1422
|
+
return new Response(createSSEStream(sseEncode(chunks)), {
|
|
1423
|
+
status: 200,
|
|
1424
|
+
headers: {
|
|
1425
|
+
"content-type": "text/event-stream",
|
|
1426
|
+
"X-Stream-Version": "v1",
|
|
1427
|
+
},
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
throw new Error(`Unexpected fetch URL: ${urlStr}`);
|
|
1431
|
+
});
|
|
1432
|
+
const transport = new chat_js_1.TriggerChatTransport({
|
|
1433
|
+
task: "my-task",
|
|
1434
|
+
accessToken: "token",
|
|
1435
|
+
baseURL: "https://api.test.trigger.dev",
|
|
1436
|
+
onSessionChange,
|
|
1437
|
+
});
|
|
1438
|
+
const stream = await transport.sendMessages({
|
|
1439
|
+
trigger: "submit-message",
|
|
1440
|
+
chatId: "chat-1",
|
|
1441
|
+
messageId: undefined,
|
|
1442
|
+
messages: [createUserMessage("Hello")],
|
|
1443
|
+
abortSignal: undefined,
|
|
1444
|
+
});
|
|
1445
|
+
// Session created notification should have fired
|
|
1446
|
+
(0, vitest_1.expect)(onSessionChange).toHaveBeenCalledWith("chat-1", {
|
|
1447
|
+
runId: triggerRunId,
|
|
1448
|
+
publicAccessToken: publicToken,
|
|
1449
|
+
lastEventId: undefined,
|
|
1450
|
+
});
|
|
1451
|
+
// Consume stream
|
|
1452
|
+
const reader = stream.getReader();
|
|
1453
|
+
while (!(await reader.read()).done) { }
|
|
1454
|
+
// Should also fire with updated lastEventId on turn complete
|
|
1455
|
+
const lastCall = onSessionChange.mock.calls[onSessionChange.mock.calls.length - 1];
|
|
1456
|
+
(0, vitest_1.expect)(lastCall[0]).toBe("chat-1");
|
|
1457
|
+
(0, vitest_1.expect)(lastCall[1]).not.toBeNull();
|
|
1458
|
+
(0, vitest_1.expect)(lastCall[1].lastEventId).toBeDefined();
|
|
1459
|
+
});
|
|
1460
|
+
(0, vitest_1.it)("should fire with null when session is deleted (stream ends naturally)", async () => {
|
|
1461
|
+
const onSessionChange = vitest_1.vi.fn();
|
|
1462
|
+
global.fetch = vitest_1.vi.fn().mockImplementation(async (url) => {
|
|
1463
|
+
const urlStr = typeof url === "string" ? url : url.toString();
|
|
1464
|
+
if (urlStr.includes("/trigger")) {
|
|
1465
|
+
return new Response(JSON.stringify({ id: "run_end" }), {
|
|
1466
|
+
status: 200,
|
|
1467
|
+
headers: {
|
|
1468
|
+
"content-type": "application/json",
|
|
1469
|
+
"x-trigger-jwt": "pub_end",
|
|
1470
|
+
},
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
if (urlStr.includes("/realtime/v1/streams/")) {
|
|
1474
|
+
// No turn-complete chunk — stream ends naturally (run completed)
|
|
1475
|
+
return new Response(createSSEStream(sseEncode(sampleChunks)), {
|
|
1476
|
+
status: 200,
|
|
1477
|
+
headers: {
|
|
1478
|
+
"content-type": "text/event-stream",
|
|
1479
|
+
"X-Stream-Version": "v1",
|
|
1480
|
+
},
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
throw new Error(`Unexpected fetch URL: ${urlStr}`);
|
|
1484
|
+
});
|
|
1485
|
+
const transport = new chat_js_1.TriggerChatTransport({
|
|
1486
|
+
task: "my-task",
|
|
1487
|
+
accessToken: "token",
|
|
1488
|
+
baseURL: "https://api.test.trigger.dev",
|
|
1489
|
+
onSessionChange,
|
|
1490
|
+
});
|
|
1491
|
+
const stream = await transport.sendMessages({
|
|
1492
|
+
trigger: "submit-message",
|
|
1493
|
+
chatId: "chat-end",
|
|
1494
|
+
messageId: undefined,
|
|
1495
|
+
messages: [createUserMessage("Hello")],
|
|
1496
|
+
abortSignal: undefined,
|
|
1497
|
+
});
|
|
1498
|
+
// Consume the stream fully
|
|
1499
|
+
const reader = stream.getReader();
|
|
1500
|
+
while (!(await reader.read()).done) { }
|
|
1501
|
+
// Session should have been created then deleted
|
|
1502
|
+
(0, vitest_1.expect)(onSessionChange).toHaveBeenCalledWith("chat-end", vitest_1.expect.objectContaining({
|
|
1503
|
+
runId: "run_end",
|
|
1504
|
+
}));
|
|
1505
|
+
(0, vitest_1.expect)(onSessionChange).toHaveBeenCalledWith("chat-end", null);
|
|
1506
|
+
});
|
|
1507
|
+
(0, vitest_1.it)("should be updatable via setOnSessionChange", async () => {
|
|
1508
|
+
const onSessionChange1 = vitest_1.vi.fn();
|
|
1509
|
+
const onSessionChange2 = vitest_1.vi.fn();
|
|
1510
|
+
global.fetch = vitest_1.vi.fn().mockImplementation(async (url) => {
|
|
1511
|
+
const urlStr = typeof url === "string" ? url : url.toString();
|
|
1512
|
+
if (urlStr.includes("/trigger")) {
|
|
1513
|
+
return new Response(JSON.stringify({ id: "run_update" }), {
|
|
1514
|
+
status: 200,
|
|
1515
|
+
headers: {
|
|
1516
|
+
"content-type": "application/json",
|
|
1517
|
+
"x-trigger-jwt": "pub_update",
|
|
1518
|
+
},
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
if (urlStr.includes("/realtime/v1/streams/")) {
|
|
1522
|
+
const chunks = [
|
|
1523
|
+
...sampleChunks,
|
|
1524
|
+
{ type: "__trigger_turn_complete" },
|
|
1525
|
+
];
|
|
1526
|
+
return new Response(createSSEStream(sseEncode(chunks)), {
|
|
1527
|
+
status: 200,
|
|
1528
|
+
headers: {
|
|
1529
|
+
"content-type": "text/event-stream",
|
|
1530
|
+
"X-Stream-Version": "v1",
|
|
1531
|
+
},
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
throw new Error(`Unexpected fetch URL: ${urlStr}`);
|
|
1535
|
+
});
|
|
1536
|
+
const transport = new chat_js_1.TriggerChatTransport({
|
|
1537
|
+
task: "my-task",
|
|
1538
|
+
accessToken: "token",
|
|
1539
|
+
baseURL: "https://api.test.trigger.dev",
|
|
1540
|
+
onSessionChange: onSessionChange1,
|
|
1541
|
+
});
|
|
1542
|
+
// Update the callback before sending
|
|
1543
|
+
transport.setOnSessionChange(onSessionChange2);
|
|
1544
|
+
await transport.sendMessages({
|
|
1545
|
+
trigger: "submit-message",
|
|
1546
|
+
chatId: "chat-update",
|
|
1547
|
+
messageId: undefined,
|
|
1548
|
+
messages: [createUserMessage("Hello")],
|
|
1549
|
+
abortSignal: undefined,
|
|
1550
|
+
});
|
|
1551
|
+
// Only onSessionChange2 should have been called
|
|
1552
|
+
(0, vitest_1.expect)(onSessionChange1).not.toHaveBeenCalled();
|
|
1553
|
+
(0, vitest_1.expect)(onSessionChange2).toHaveBeenCalled();
|
|
1554
|
+
});
|
|
1555
|
+
});
|
|
1303
1556
|
});
|
|
1304
1557
|
//# sourceMappingURL=chat.test.js.map
|