@truefoundry/assistant-ui-runtime 0.1.5 → 0.1.6-rc.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/README.md +374 -190
- package/dist/index.d.ts +32 -29
- package/dist/index.js +330 -236
- package/dist/index.js.map +1 -1
- package/dist/plugins/truefoundry-agent-server-adapter/index.d.ts +30 -0
- package/dist/plugins/truefoundry-agent-server-adapter/index.js +198 -0
- package/dist/plugins/truefoundry-agent-server-adapter/index.js.map +1 -0
- package/dist/types-VUBzoJT2.d.ts +462 -0
- package/package.json +10 -2
- package/src/askUserQuestion.ts +3 -3
- package/src/buildEditedUserMessageContent.test.ts +2 -2
- package/src/collectPending.ts +1 -1
- package/src/convertTurnMessages.test.ts +141 -196
- package/src/convertTurnMessages.ts +130 -76
- package/src/createSubAgent.ts +1 -1
- package/src/draftAgentConfig.test.ts +26 -29
- package/src/extractTurnUserText.ts +1 -1
- package/src/foldPeerThreads.test.ts +1 -1
- package/src/foldPeerThreads.ts +3 -2
- package/src/index.ts +39 -4
- package/src/listPages.ts +21 -0
- package/src/loadSessionSnapshot.test.ts +9 -8
- package/src/loadSessionSnapshot.ts +9 -14
- package/src/mcpAuth.ts +6 -3
- package/src/messageCustomMetadata.ts +1 -1
- package/src/modelMessageContent.ts +1 -1
- package/src/modelMessageImageContent.test.ts +1 -1
- package/src/modelMessageImageContent.ts +7 -6
- package/src/plugins/truefoundry-agent-server-adapter/index.ts +285 -0
- package/src/private/agentSpec.ts +8 -3
- package/src/private/draftSessionBridge.ts +14 -13
- package/src/private/truefoundryDraftThreadListAdapter.test.ts +44 -49
- package/src/private/truefoundryDraftThreadListAdapter.ts +22 -16
- package/src/requiredActionInputs.ts +1 -1
- package/src/requiredActionsFromActiveUpdate.test.ts +1 -1
- package/src/server/eventUtils.ts +120 -0
- package/src/server/events.ts +246 -0
- package/src/server/index.ts +66 -0
- package/src/server/types.ts +313 -0
- package/src/sessionSnapshot.ts +1 -1
- package/src/sessions.ts +5 -21
- package/src/streamTurn.test.ts +172 -155
- package/src/streamTurn.ts +51 -48
- package/src/toolApproval.ts +4 -4
- package/src/toolResponse.ts +4 -4
- package/src/truefoundryExtras.ts +1 -1
- package/src/truefoundryOwnedSessionsThreadListAdapter.test.ts +26 -29
- package/src/truefoundryOwnedSessionsThreadListAdapter.ts +18 -23
- package/src/truefoundryThreadListAdapter.test.ts +16 -18
- package/src/truefoundryThreadListAdapter.ts +7 -7
- package/src/turnEventHelpers.ts +1 -1
- package/src/types.ts +2 -16
- package/src/useTrueFoundryAgentMessages.test.tsx +38 -70
- package/src/useTrueFoundryAgentMessages.ts +32 -44
- package/src/useTrueFoundryAgentRuntime.ts +11 -28
- package/src/private/bindDraftAgentSession.test.ts +0 -54
- package/src/private/bindDraftAgentSession.ts +0 -28
- package/src/private/getGatewayFromPrivateClient.ts +0 -13
package/src/streamTurn.test.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import type {
|
|
3
|
-
AgentSession,
|
|
4
|
-
Turn,
|
|
5
|
-
TurnStreamData,
|
|
6
|
-
} from "truefoundry-gateway-sdk/agents";
|
|
2
|
+
import type { AgentChatServer, TurnStreamData } from "./server/index.js";
|
|
7
3
|
|
|
8
4
|
import { ROOT_THREAD_ID } from "./constants.js";
|
|
9
5
|
import { PeerThreadFoldState } from "./foldPeerThreads.js";
|
|
10
6
|
import { resumeTurnStream, streamTurnContent } from "./streamTurn.js";
|
|
11
7
|
|
|
12
8
|
const createdAt = new Date().toISOString();
|
|
9
|
+
const SESSION_ID = "session-1";
|
|
13
10
|
|
|
14
11
|
function streamData(
|
|
15
12
|
sequenceNumber: number,
|
|
@@ -18,6 +15,10 @@ function streamData(
|
|
|
18
15
|
return { sequenceNumber, event: event as TurnStreamData["event"] };
|
|
19
16
|
}
|
|
20
17
|
|
|
18
|
+
function mockServer(partial: Record<string, unknown>): AgentChatServer {
|
|
19
|
+
return partial as unknown as AgentChatServer;
|
|
20
|
+
}
|
|
21
|
+
|
|
21
22
|
async function collectUpdates(
|
|
22
23
|
generator: AsyncGenerator<{ content: unknown[] }>,
|
|
23
24
|
): Promise<{ content: unknown[] }[]> {
|
|
@@ -32,42 +33,42 @@ describe("streamTurn", () => {
|
|
|
32
33
|
describe("streamTurnContent", () => {
|
|
33
34
|
it("prepares a user turn and yields folded stream updates", async () => {
|
|
34
35
|
const foldState = new PeerThreadFoldState();
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
prepareTurn,
|
|
49
|
-
cancel: vi.fn().mockResolvedValue(undefined),
|
|
50
|
-
} as unknown as AgentSession;
|
|
36
|
+
const prepareAndExecuteTurn = vi.fn(async function* () {
|
|
37
|
+
yield streamData(1, {
|
|
38
|
+
type: "model.message",
|
|
39
|
+
createdAt,
|
|
40
|
+
id: "m1",
|
|
41
|
+
threadId: ROOT_THREAD_ID,
|
|
42
|
+
content: "hello from stream",
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
const server = mockServer({
|
|
46
|
+
prepareAndExecuteTurn,
|
|
47
|
+
cancelSession: vi.fn().mockResolvedValue(undefined),
|
|
48
|
+
});
|
|
51
49
|
|
|
52
50
|
const updates = await collectUpdates(
|
|
53
51
|
streamTurnContent(
|
|
54
|
-
|
|
52
|
+
server,
|
|
53
|
+
SESSION_ID,
|
|
55
54
|
foldState,
|
|
56
55
|
{ userMessage: "hello" },
|
|
57
56
|
new AbortController().signal,
|
|
58
57
|
),
|
|
59
58
|
);
|
|
60
59
|
|
|
61
|
-
expect(
|
|
60
|
+
expect(prepareAndExecuteTurn).toHaveBeenCalledWith({
|
|
61
|
+
sessionId: SESSION_ID,
|
|
62
62
|
input: [{ type: "user.message", content: "hello" }],
|
|
63
63
|
previousTurnId: "auto",
|
|
64
|
+
abortSignal: expect.any(AbortSignal),
|
|
64
65
|
});
|
|
65
66
|
expect(updates).toEqual([
|
|
66
67
|
{ content: [{ type: "text", text: "hello from stream" }] },
|
|
67
68
|
]);
|
|
68
69
|
});
|
|
69
70
|
|
|
70
|
-
it("passes required-action inputs through
|
|
71
|
+
it("passes required-action inputs through prepareAndExecuteTurn", async () => {
|
|
71
72
|
const inputs = [
|
|
72
73
|
{
|
|
73
74
|
type: "user.tool_approval" as const,
|
|
@@ -82,131 +83,139 @@ describe("streamTurn", () => {
|
|
|
82
83
|
content: "A",
|
|
83
84
|
},
|
|
84
85
|
];
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
} as unknown as AgentSession;
|
|
86
|
+
const prepareAndExecuteTurn = vi.fn(async function* () {});
|
|
87
|
+
const server = mockServer({
|
|
88
|
+
prepareAndExecuteTurn,
|
|
89
|
+
cancelSession: vi.fn().mockResolvedValue(undefined),
|
|
90
|
+
});
|
|
91
91
|
|
|
92
92
|
await collectUpdates(
|
|
93
93
|
streamTurnContent(
|
|
94
|
-
|
|
94
|
+
server,
|
|
95
|
+
SESSION_ID,
|
|
95
96
|
new PeerThreadFoldState(),
|
|
96
97
|
{ inputs },
|
|
97
98
|
new AbortController().signal,
|
|
98
99
|
),
|
|
99
100
|
);
|
|
100
101
|
|
|
101
|
-
expect(
|
|
102
|
+
expect(prepareAndExecuteTurn).toHaveBeenCalledWith({
|
|
103
|
+
sessionId: SESSION_ID,
|
|
102
104
|
input: inputs,
|
|
103
105
|
previousTurnId: "auto",
|
|
106
|
+
abortSignal: expect.any(AbortSignal),
|
|
104
107
|
});
|
|
105
108
|
});
|
|
106
109
|
|
|
107
110
|
it("uses empty input when resuming after MCP auth", async () => {
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
} as unknown as AgentSession;
|
|
111
|
+
const prepareAndExecuteTurn = vi.fn(async function* () {});
|
|
112
|
+
const server = mockServer({
|
|
113
|
+
prepareAndExecuteTurn,
|
|
114
|
+
cancelSession: vi.fn().mockResolvedValue(undefined),
|
|
115
|
+
});
|
|
114
116
|
|
|
115
117
|
await collectUpdates(
|
|
116
118
|
streamTurnContent(
|
|
117
|
-
|
|
119
|
+
server,
|
|
120
|
+
SESSION_ID,
|
|
118
121
|
new PeerThreadFoldState(),
|
|
119
122
|
{ resumeMcpAuth: true },
|
|
120
123
|
new AbortController().signal,
|
|
121
124
|
),
|
|
122
125
|
);
|
|
123
126
|
|
|
124
|
-
expect(
|
|
127
|
+
expect(prepareAndExecuteTurn).toHaveBeenCalledWith({
|
|
128
|
+
sessionId: SESSION_ID,
|
|
125
129
|
input: [],
|
|
126
130
|
previousTurnId: "auto",
|
|
131
|
+
abortSignal: expect.any(AbortSignal),
|
|
127
132
|
});
|
|
128
133
|
});
|
|
129
134
|
|
|
130
135
|
it("forwards an explicit previousTurnId when branching", async () => {
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
} as unknown as AgentSession;
|
|
136
|
+
const prepareAndExecuteTurn = vi.fn(async function* () {});
|
|
137
|
+
const server = mockServer({
|
|
138
|
+
prepareAndExecuteTurn,
|
|
139
|
+
cancelSession: vi.fn().mockResolvedValue(undefined),
|
|
140
|
+
});
|
|
137
141
|
|
|
138
142
|
await collectUpdates(
|
|
139
143
|
streamTurnContent(
|
|
140
|
-
|
|
144
|
+
server,
|
|
145
|
+
SESSION_ID,
|
|
141
146
|
new PeerThreadFoldState(),
|
|
142
147
|
{ userMessage: "edited", previousTurnId: "turn-a" },
|
|
143
148
|
new AbortController().signal,
|
|
144
149
|
),
|
|
145
150
|
);
|
|
146
151
|
|
|
147
|
-
expect(
|
|
152
|
+
expect(prepareAndExecuteTurn).toHaveBeenCalledWith({
|
|
153
|
+
sessionId: SESSION_ID,
|
|
148
154
|
input: [{ type: "user.message", content: "edited" }],
|
|
149
155
|
previousTurnId: "turn-a",
|
|
156
|
+
abortSignal: expect.any(AbortSignal),
|
|
150
157
|
});
|
|
151
158
|
});
|
|
152
159
|
|
|
153
160
|
it("forwards previousTurnId \"none\" when branching from root", async () => {
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
} as unknown as AgentSession;
|
|
161
|
+
const prepareAndExecuteTurn = vi.fn(async function* () {});
|
|
162
|
+
const server = mockServer({
|
|
163
|
+
prepareAndExecuteTurn,
|
|
164
|
+
cancelSession: vi.fn().mockResolvedValue(undefined),
|
|
165
|
+
});
|
|
160
166
|
|
|
161
167
|
await collectUpdates(
|
|
162
168
|
streamTurnContent(
|
|
163
|
-
|
|
169
|
+
server,
|
|
170
|
+
SESSION_ID,
|
|
164
171
|
new PeerThreadFoldState(),
|
|
165
172
|
{ userMessage: "first", previousTurnId: "none" },
|
|
166
173
|
new AbortController().signal,
|
|
167
174
|
),
|
|
168
175
|
);
|
|
169
176
|
|
|
170
|
-
expect(
|
|
177
|
+
expect(prepareAndExecuteTurn).toHaveBeenCalledWith({
|
|
178
|
+
sessionId: SESSION_ID,
|
|
171
179
|
input: [{ type: "user.message", content: "first" }],
|
|
172
180
|
previousTurnId: "none",
|
|
181
|
+
abortSignal: expect.any(AbortSignal),
|
|
173
182
|
});
|
|
174
183
|
});
|
|
175
184
|
|
|
176
185
|
it("returns early and cancels the session when already aborted", async () => {
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
const
|
|
180
|
-
const session = { prepareTurn, cancel } as unknown as AgentSession;
|
|
186
|
+
const prepareAndExecuteTurn = vi.fn(async function* () {});
|
|
187
|
+
const cancelSession = vi.fn().mockResolvedValue(undefined);
|
|
188
|
+
const server = mockServer({ prepareAndExecuteTurn, cancelSession });
|
|
181
189
|
const abortController = new AbortController();
|
|
182
190
|
abortController.abort();
|
|
183
191
|
|
|
184
192
|
const updates = await collectUpdates(
|
|
185
193
|
streamTurnContent(
|
|
186
|
-
|
|
194
|
+
server,
|
|
195
|
+
SESSION_ID,
|
|
187
196
|
new PeerThreadFoldState(),
|
|
188
197
|
{ userMessage: "hello" },
|
|
189
198
|
abortController.signal,
|
|
190
199
|
),
|
|
191
200
|
);
|
|
192
201
|
|
|
193
|
-
expect(
|
|
194
|
-
expect(
|
|
202
|
+
expect(cancelSession).toHaveBeenCalledWith({ sessionId: SESSION_ID });
|
|
203
|
+
expect(prepareAndExecuteTurn).not.toHaveBeenCalled();
|
|
195
204
|
expect(updates).toEqual([]);
|
|
196
205
|
});
|
|
197
206
|
|
|
198
|
-
it("forwards headers to
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
} as unknown as AgentSession;
|
|
207
|
+
it("forwards headers to prepareAndExecuteTurn", async () => {
|
|
208
|
+
const prepareAndExecuteTurn = vi.fn(async function* () {});
|
|
209
|
+
const server = mockServer({
|
|
210
|
+
prepareAndExecuteTurn,
|
|
211
|
+
cancelSession: vi.fn().mockResolvedValue(undefined),
|
|
212
|
+
});
|
|
205
213
|
const abortSignal = new AbortController().signal;
|
|
206
214
|
|
|
207
215
|
await collectUpdates(
|
|
208
216
|
streamTurnContent(
|
|
209
|
-
|
|
217
|
+
server,
|
|
218
|
+
SESSION_ID,
|
|
210
219
|
new PeerThreadFoldState(),
|
|
211
220
|
{
|
|
212
221
|
userMessage: "hello",
|
|
@@ -218,54 +227,50 @@ describe("streamTurn", () => {
|
|
|
218
227
|
),
|
|
219
228
|
);
|
|
220
229
|
|
|
221
|
-
expect(
|
|
222
|
-
|
|
223
|
-
{
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
230
|
+
expect(prepareAndExecuteTurn).toHaveBeenCalledWith({
|
|
231
|
+
sessionId: SESSION_ID,
|
|
232
|
+
input: [{ type: "user.message", content: "hello" }],
|
|
233
|
+
previousTurnId: "auto",
|
|
234
|
+
abortSignal,
|
|
235
|
+
headers: {
|
|
236
|
+
"x-tfy-session-last-updated-at": "2026-06-30T10:00:00.000Z",
|
|
228
237
|
},
|
|
229
|
-
);
|
|
238
|
+
});
|
|
230
239
|
});
|
|
231
240
|
|
|
232
241
|
it("notifies gateway turn id when turn.done errors with no content yields", async () => {
|
|
233
242
|
const gatewayTurnId = "01ky6mqzmczwt6ssyd5r02gjjc";
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
),
|
|
257
|
-
};
|
|
258
|
-
const prepareTurn = vi.fn(() => turn);
|
|
259
|
-
const session = {
|
|
260
|
-
prepareTurn,
|
|
261
|
-
cancel: vi.fn().mockResolvedValue(undefined),
|
|
262
|
-
} as unknown as AgentSession;
|
|
243
|
+
const prepareAndExecuteTurn = vi.fn(async function* () {
|
|
244
|
+
yield streamData(1, {
|
|
245
|
+
type: "turn.created",
|
|
246
|
+
createdAt,
|
|
247
|
+
id: "created-1",
|
|
248
|
+
turnId: gatewayTurnId,
|
|
249
|
+
input: [{ type: "user.message", content: "hello" }],
|
|
250
|
+
});
|
|
251
|
+
yield streamData(2, {
|
|
252
|
+
type: "turn.done",
|
|
253
|
+
createdAt,
|
|
254
|
+
id: "done-1",
|
|
255
|
+
state: {
|
|
256
|
+
status: "error",
|
|
257
|
+
message:
|
|
258
|
+
"Publisher Model is not servable in region us-central1.",
|
|
259
|
+
completedAt: createdAt,
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
const server = mockServer({
|
|
264
|
+
prepareAndExecuteTurn,
|
|
265
|
+
cancelSession: vi.fn().mockResolvedValue(undefined),
|
|
266
|
+
});
|
|
263
267
|
const onTurnIdAvailable = vi.fn();
|
|
264
268
|
|
|
265
269
|
await expect(
|
|
266
270
|
collectUpdates(
|
|
267
271
|
streamTurnContent(
|
|
268
|
-
|
|
272
|
+
server,
|
|
273
|
+
SESSION_ID,
|
|
269
274
|
new PeerThreadFoldState(),
|
|
270
275
|
{ userMessage: "hello" },
|
|
271
276
|
new AbortController().signal,
|
|
@@ -279,31 +284,30 @@ describe("streamTurn", () => {
|
|
|
279
284
|
expect(onTurnIdAvailable).toHaveBeenCalledWith(gatewayTurnId);
|
|
280
285
|
});
|
|
281
286
|
|
|
282
|
-
it("does not notify when an error stream never
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
cancel: vi.fn().mockResolvedValue(undefined),
|
|
300
|
-
} as unknown as AgentSession;
|
|
287
|
+
it("does not notify when an error stream never emits turn.created", async () => {
|
|
288
|
+
const prepareAndExecuteTurn = vi.fn(async function* () {
|
|
289
|
+
yield streamData(1, {
|
|
290
|
+
type: "turn.done",
|
|
291
|
+
createdAt,
|
|
292
|
+
id: "done-1",
|
|
293
|
+
state: {
|
|
294
|
+
status: "error",
|
|
295
|
+
completedAt: createdAt,
|
|
296
|
+
message: "boom",
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
const server = mockServer({
|
|
301
|
+
prepareAndExecuteTurn,
|
|
302
|
+
cancelSession: vi.fn().mockResolvedValue(undefined),
|
|
303
|
+
});
|
|
301
304
|
const onTurnIdAvailable = vi.fn();
|
|
302
305
|
|
|
303
306
|
await expect(
|
|
304
307
|
collectUpdates(
|
|
305
308
|
streamTurnContent(
|
|
306
|
-
|
|
309
|
+
server,
|
|
310
|
+
SESSION_ID,
|
|
307
311
|
new PeerThreadFoldState(),
|
|
308
312
|
{ userMessage: "hello" },
|
|
309
313
|
new AbortController().signal,
|
|
@@ -320,45 +324,58 @@ describe("streamTurn", () => {
|
|
|
320
324
|
describe("resumeTurnStream", () => {
|
|
321
325
|
it("reconnects with afterSequenceNumber and yields updates", async () => {
|
|
322
326
|
const foldState = new PeerThreadFoldState();
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
session: { cancel: vi.fn().mockResolvedValue(undefined) },
|
|
337
|
-
} as unknown as Turn;
|
|
327
|
+
const subscribeToTurn = vi.fn(async function* () {
|
|
328
|
+
yield streamData(2, {
|
|
329
|
+
type: "model.message",
|
|
330
|
+
createdAt,
|
|
331
|
+
id: "m2",
|
|
332
|
+
threadId: ROOT_THREAD_ID,
|
|
333
|
+
content: "resumed",
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
const server = mockServer({
|
|
337
|
+
subscribeToTurn,
|
|
338
|
+
cancelSession: vi.fn().mockResolvedValue(undefined),
|
|
339
|
+
});
|
|
338
340
|
|
|
339
341
|
const updates = await collectUpdates(
|
|
340
|
-
resumeTurnStream(
|
|
342
|
+
resumeTurnStream(
|
|
343
|
+
server,
|
|
344
|
+
SESSION_ID,
|
|
345
|
+
"turn-1",
|
|
346
|
+
foldState,
|
|
347
|
+
new AbortController().signal,
|
|
348
|
+
1,
|
|
349
|
+
),
|
|
341
350
|
);
|
|
342
|
-
expect(
|
|
351
|
+
expect(subscribeToTurn).toHaveBeenCalledWith({
|
|
352
|
+
sessionId: SESSION_ID,
|
|
353
|
+
turnId: "turn-1",
|
|
354
|
+
afterSequenceNumber: 1,
|
|
355
|
+
abortSignal: expect.any(AbortSignal),
|
|
356
|
+
});
|
|
343
357
|
expect(updates).toEqual([{ content: [{ type: "text", text: "resumed" }] }]);
|
|
344
358
|
});
|
|
345
359
|
|
|
346
360
|
it("returns early when aborted before streaming starts", async () => {
|
|
347
|
-
const
|
|
348
|
-
const
|
|
349
|
-
const
|
|
350
|
-
stream,
|
|
351
|
-
session: { cancel },
|
|
352
|
-
} as unknown as Turn;
|
|
361
|
+
const subscribeToTurn = vi.fn(async function* () {});
|
|
362
|
+
const cancelSession = vi.fn().mockResolvedValue(undefined);
|
|
363
|
+
const server = mockServer({ subscribeToTurn, cancelSession });
|
|
353
364
|
const abortController = new AbortController();
|
|
354
365
|
abortController.abort();
|
|
355
366
|
|
|
356
367
|
const updates = await collectUpdates(
|
|
357
|
-
resumeTurnStream(
|
|
368
|
+
resumeTurnStream(
|
|
369
|
+
server,
|
|
370
|
+
SESSION_ID,
|
|
371
|
+
"turn-1",
|
|
372
|
+
new PeerThreadFoldState(),
|
|
373
|
+
abortController.signal,
|
|
374
|
+
),
|
|
358
375
|
);
|
|
359
376
|
|
|
360
|
-
expect(
|
|
361
|
-
expect(
|
|
377
|
+
expect(cancelSession).toHaveBeenCalledWith({ sessionId: SESSION_ID });
|
|
378
|
+
expect(subscribeToTurn).not.toHaveBeenCalled();
|
|
362
379
|
expect(updates).toEqual([]);
|
|
363
380
|
});
|
|
364
381
|
});
|