@qwen-code/arena-agent 0.0.1

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.
Files changed (64) hide show
  1. package/.env.example +52 -0
  2. package/README.md +436 -0
  3. package/dist/agent.d.ts +33 -0
  4. package/dist/agent.js +366 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/attachments.d.ts +29 -0
  7. package/dist/attachments.js +222 -0
  8. package/dist/attachments.js.map +1 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.js +256 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/compaction.d.ts +15 -0
  13. package/dist/compaction.js +176 -0
  14. package/dist/compaction.js.map +1 -0
  15. package/dist/config.d.ts +23 -0
  16. package/dist/config.js +61 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/document-reader.d.ts +18 -0
  19. package/dist/document-reader.js +423 -0
  20. package/dist/document-reader.js.map +1 -0
  21. package/dist/fetch-page.d.ts +25 -0
  22. package/dist/fetch-page.js +428 -0
  23. package/dist/fetch-page.js.map +1 -0
  24. package/dist/history.d.ts +5 -0
  25. package/dist/history.js +31 -0
  26. package/dist/history.js.map +1 -0
  27. package/dist/mock.d.ts +4 -0
  28. package/dist/mock.js +151 -0
  29. package/dist/mock.js.map +1 -0
  30. package/dist/protocol.d.ts +23 -0
  31. package/dist/protocol.js +84 -0
  32. package/dist/protocol.js.map +1 -0
  33. package/dist/provider.d.ts +31 -0
  34. package/dist/provider.js +308 -0
  35. package/dist/provider.js.map +1 -0
  36. package/dist/sdk.d.ts +181 -0
  37. package/dist/sdk.js +674 -0
  38. package/dist/sdk.js.map +1 -0
  39. package/dist/server.d.ts +15 -0
  40. package/dist/server.js +1433 -0
  41. package/dist/server.js.map +1 -0
  42. package/dist/spec.d.ts +11 -0
  43. package/dist/spec.js +198 -0
  44. package/dist/spec.js.map +1 -0
  45. package/dist/tools.d.ts +38 -0
  46. package/dist/tools.js +992 -0
  47. package/dist/tools.js.map +1 -0
  48. package/dist/tui.d.ts +10 -0
  49. package/dist/tui.js +350 -0
  50. package/dist/tui.js.map +1 -0
  51. package/dist/types.d.ts +146 -0
  52. package/dist/types.js +2 -0
  53. package/dist/types.js.map +1 -0
  54. package/dist/web/app.d.ts +1 -0
  55. package/dist/web/app.js +1082 -0
  56. package/dist/web/app.js.map +1 -0
  57. package/docs/SDK.md +521 -0
  58. package/package.json +59 -0
  59. package/spec/message.json +150 -0
  60. package/spec/tools.json +418 -0
  61. package/web/arena-logo.svg +11 -0
  62. package/web/fonts.css +67 -0
  63. package/web/index.html +285 -0
  64. package/web/styles.css +1798 -0
package/dist/sdk.js ADDED
@@ -0,0 +1,674 @@
1
+ class AsyncQueue {
2
+ values = [];
3
+ pending = [];
4
+ ended = false;
5
+ failure;
6
+ push(value) {
7
+ if (this.ended)
8
+ return;
9
+ const pending = this.pending.shift();
10
+ if (pending) {
11
+ pending.resolve({ value, done: false });
12
+ }
13
+ else {
14
+ this.values.push(value);
15
+ }
16
+ }
17
+ close(error) {
18
+ if (this.ended)
19
+ return;
20
+ this.ended = true;
21
+ this.failure = error;
22
+ for (const pending of this.pending.splice(0)) {
23
+ if (error)
24
+ pending.reject(error);
25
+ else
26
+ pending.resolve({ value: undefined, done: true });
27
+ }
28
+ }
29
+ [Symbol.asyncIterator]() {
30
+ return {
31
+ next: async () => {
32
+ const value = this.values.shift();
33
+ if (value !== undefined)
34
+ return { value, done: false };
35
+ if (this.ended) {
36
+ if (this.failure)
37
+ throw this.failure;
38
+ return { value: undefined, done: true };
39
+ }
40
+ return new Promise((resolve, reject) => {
41
+ this.pending.push({ resolve, reject });
42
+ });
43
+ },
44
+ };
45
+ }
46
+ }
47
+ export class ArenaApiError extends Error {
48
+ status;
49
+ body;
50
+ constructor(message, status, body) {
51
+ super(message);
52
+ this.status = status;
53
+ this.body = body;
54
+ this.name = "ArenaApiError";
55
+ }
56
+ }
57
+ function withoutTrailingSlash(value) {
58
+ return value.replace(/\/+$/, "");
59
+ }
60
+ function errorMessage(error) {
61
+ return error instanceof Error ? error.message : String(error);
62
+ }
63
+ function abortError() {
64
+ const error = new Error("operation cancelled");
65
+ error.name = "AbortError";
66
+ return error;
67
+ }
68
+ function runtimeFetch() {
69
+ if (typeof globalThis.fetch !== "function") {
70
+ throw new Error("Arena SDK requires a Fetch API implementation");
71
+ }
72
+ return globalThis.fetch.bind(globalThis);
73
+ }
74
+ function runtimeOriginHost() {
75
+ return typeof globalThis.location === "object"
76
+ ? globalThis.location.host
77
+ : "";
78
+ }
79
+ function randomId() {
80
+ if (typeof globalThis.crypto?.randomUUID === "function") {
81
+ return globalThis.crypto.randomUUID();
82
+ }
83
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (value) => {
84
+ const random = Math.floor(Math.random() * 16);
85
+ const nibble = value === "x" ? random : (random & 0x3) | 0x8;
86
+ return nibble.toString(16);
87
+ });
88
+ }
89
+ function dateInTimezone(timezone) {
90
+ const parts = new Intl.DateTimeFormat("en-CA", {
91
+ timeZone: timezone,
92
+ year: "numeric",
93
+ month: "2-digit",
94
+ day: "2-digit",
95
+ }).formatToParts(new Date());
96
+ const value = (type) => parts.find((part) => part.type === type)?.value || "";
97
+ return `${value("year")}-${value("month")}-${value("day")}`;
98
+ }
99
+ export function defaultArenaClientContext() {
100
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
101
+ return {
102
+ currentDate: dateInTimezone(timezone),
103
+ timezone,
104
+ };
105
+ }
106
+ async function responseBody(response) {
107
+ const text = await response.text();
108
+ if (!text)
109
+ return {};
110
+ try {
111
+ return JSON.parse(text);
112
+ }
113
+ catch {
114
+ return text;
115
+ }
116
+ }
117
+ async function expectJson(response) {
118
+ const body = await responseBody(response);
119
+ if (!response.ok) {
120
+ const record = body && typeof body === "object"
121
+ ? body
122
+ : {};
123
+ throw new ArenaApiError(String(record.error || record.message || `Request failed (${response.status})`), response.status, body);
124
+ }
125
+ return body;
126
+ }
127
+ async function* sseFrames(body) {
128
+ const reader = body.getReader();
129
+ const decoder = new TextDecoder();
130
+ let buffer = "";
131
+ const parseFrame = (frame) => {
132
+ let id;
133
+ const data = [];
134
+ for (const line of frame.split("\n")) {
135
+ if (line.startsWith("id:"))
136
+ id = line.slice(3).trim();
137
+ if (line.startsWith("data:"))
138
+ data.push(line.slice(5).trimStart());
139
+ }
140
+ return data.length ? { ...(id ? { id } : {}), data: data.join("\n") } : undefined;
141
+ };
142
+ while (true) {
143
+ const { done, value } = await reader.read();
144
+ buffer += decoder.decode(value, { stream: !done }).replace(/\r\n/g, "\n");
145
+ let boundary = buffer.indexOf("\n\n");
146
+ while (boundary !== -1) {
147
+ const parsed = parseFrame(buffer.slice(0, boundary));
148
+ buffer = buffer.slice(boundary + 2);
149
+ if (parsed)
150
+ yield parsed;
151
+ boundary = buffer.indexOf("\n\n");
152
+ }
153
+ if (done)
154
+ break;
155
+ }
156
+ const parsed = parseFrame(buffer);
157
+ if (parsed)
158
+ yield parsed;
159
+ }
160
+ async function abortableDelay(milliseconds, signal) {
161
+ if (signal.aborted)
162
+ throw signal.reason || abortError();
163
+ await new Promise((resolve, reject) => {
164
+ const complete = () => {
165
+ signal.removeEventListener("abort", abort);
166
+ resolve();
167
+ };
168
+ const timeout = setTimeout(complete, milliseconds);
169
+ const abort = () => {
170
+ clearTimeout(timeout);
171
+ signal.removeEventListener("abort", abort);
172
+ reject(signal.reason || abortError());
173
+ };
174
+ signal.addEventListener("abort", abort, { once: true });
175
+ });
176
+ }
177
+ function uploadValue(input) {
178
+ if (input instanceof Blob) {
179
+ return {
180
+ name: typeof input.name === "string"
181
+ ? String(input.name)
182
+ : "upload.bin",
183
+ body: input,
184
+ type: input.type || "application/octet-stream",
185
+ };
186
+ }
187
+ let body;
188
+ if (input.data instanceof Blob) {
189
+ body = input.data;
190
+ }
191
+ else if (input.data instanceof Uint8Array) {
192
+ const bytes = new Uint8Array(input.data.byteLength);
193
+ bytes.set(input.data);
194
+ body = new Blob([bytes.buffer], {
195
+ type: input.type || "application/octet-stream",
196
+ });
197
+ }
198
+ else {
199
+ body = new Blob([input.data], {
200
+ type: input.type || "application/octet-stream",
201
+ });
202
+ }
203
+ return {
204
+ name: input.name,
205
+ body,
206
+ type: input.type || body.type || "application/octet-stream",
207
+ };
208
+ }
209
+ function inputRequest(event) {
210
+ if (event.type !== "data-user-input-request")
211
+ return undefined;
212
+ const data = event.data && typeof event.data === "object"
213
+ ? event.data
214
+ : {};
215
+ if (typeof data.requestId !== "string" ||
216
+ typeof data.toolCallId !== "string" ||
217
+ !Array.isArray(data.questions)) {
218
+ return undefined;
219
+ }
220
+ return {
221
+ requestId: data.requestId,
222
+ toolCallId: data.toolCallId,
223
+ questions: data.questions,
224
+ };
225
+ }
226
+ export class ArenaClient {
227
+ sessions;
228
+ baseUrl;
229
+ fetcher;
230
+ requestCredentials;
231
+ onCredentials;
232
+ constructor(options = {}) {
233
+ this.baseUrl = withoutTrailingSlash(options.baseUrl || "");
234
+ this.fetcher = options.fetch || runtimeFetch();
235
+ this.requestCredentials = options.credentials || "same-origin";
236
+ this.onCredentials = options.onCredentials;
237
+ this.sessions = {
238
+ create: (createOptions) => this.createSession(createOptions),
239
+ resume: (credentials) => this.resumeSession(credentials),
240
+ };
241
+ }
242
+ async getConfig(signal) {
243
+ return expectJson(await this.request("/api/config", { signal }));
244
+ }
245
+ async createSession(options = {}) {
246
+ const defaults = defaultArenaClientContext();
247
+ const value = await expectJson(await this.request("/api/chat/trigger-session", {
248
+ method: "POST",
249
+ headers: { "content-type": "application/json" },
250
+ body: JSON.stringify({
251
+ currentDate: options.currentDate || defaults.currentDate,
252
+ timezone: options.timezone || defaults.timezone,
253
+ ...(options.location ? { location: options.location } : {}),
254
+ }),
255
+ signal: options.signal,
256
+ }));
257
+ return this.resumeSession({
258
+ sessionId: value.sessionId,
259
+ token: value.publicAccessToken,
260
+ expiresAt: value.expiresAt,
261
+ });
262
+ }
263
+ resumeSession(credentials) {
264
+ if (!credentials.sessionId || !credentials.token) {
265
+ throw new Error("resumeSession requires a sessionId and token");
266
+ }
267
+ this.onCredentials?.({ ...credentials });
268
+ return new ArenaSession(this, credentials);
269
+ }
270
+ async run(options, createOptions = {}) {
271
+ const session = await this.createSession(createOptions);
272
+ const result = await session.run(options);
273
+ return { ...result, session };
274
+ }
275
+ async request(pathname, init = {}) {
276
+ return this.fetcher(this.resolveUrl(pathname), {
277
+ ...init,
278
+ credentials: init.credentials || this.requestCredentials,
279
+ });
280
+ }
281
+ resolveUrl(pathname) {
282
+ return `${this.baseUrl}${pathname}`;
283
+ }
284
+ credentialsChanged(credentials) {
285
+ this.onCredentials?.({ ...credentials });
286
+ }
287
+ }
288
+ export class ArenaSession {
289
+ client;
290
+ id;
291
+ lastEventId;
292
+ tokenValue;
293
+ expiresAtValue;
294
+ constructor(client, credentials) {
295
+ this.client = client;
296
+ this.id = credentials.sessionId;
297
+ this.tokenValue = credentials.token;
298
+ this.expiresAtValue = credentials.expiresAt;
299
+ this.lastEventId = "0";
300
+ }
301
+ get credentials() {
302
+ return {
303
+ sessionId: this.id,
304
+ token: this.tokenValue,
305
+ ...(this.expiresAtValue ? { expiresAt: this.expiresAtValue } : {}),
306
+ };
307
+ }
308
+ setLastEventId(value) {
309
+ this.lastEventId = value || "0";
310
+ }
311
+ async refreshToken(signal) {
312
+ const value = await expectJson(await this.client.request("/api/chat/trigger-token", {
313
+ method: "POST",
314
+ headers: {
315
+ authorization: `Bearer ${this.tokenValue}`,
316
+ "content-type": "application/json",
317
+ },
318
+ body: JSON.stringify({ sessionId: this.id }),
319
+ signal,
320
+ }));
321
+ this.tokenValue = value.token;
322
+ this.expiresAtValue = value.expiresAt;
323
+ const credentials = this.credentials;
324
+ this.client.credentialsChanged(credentials);
325
+ return credentials;
326
+ }
327
+ async getHistory(signal) {
328
+ const value = await expectJson(await this.authorizedRequest(`/api/chat/sessions/${this.id}/messages`, { signal }));
329
+ this.lastEventId = value.lastEventId || "0";
330
+ return {
331
+ messages: value.messages || [],
332
+ lastEventId: this.lastEventId,
333
+ activePrompt: value.activePrompt || null,
334
+ };
335
+ }
336
+ async upload(input, signal) {
337
+ const upload = uploadValue(input);
338
+ return expectJson(await this.authorizedRequest(`/api/chat/sessions/${this.id}/uploads`, {
339
+ method: "POST",
340
+ headers: {
341
+ "content-type": upload.type,
342
+ "x-file-name": encodeURIComponent(upload.name),
343
+ },
344
+ body: upload.body,
345
+ signal,
346
+ })).then((value) => value.attachment);
347
+ }
348
+ async send(options) {
349
+ const input = options.input?.trim() || "";
350
+ const uploaded = await Promise.all((options.files || []).map((file) => this.upload(file, options.signal)));
351
+ const attachments = [...(options.attachments || []), ...uploaded];
352
+ if (!input && !attachments.length) {
353
+ throw new Error("send requires input or at least one file");
354
+ }
355
+ const messageId = options.messageId || randomId();
356
+ const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
357
+ const parts = [
358
+ ...(input ? [{ type: "text", text: input }] : []),
359
+ ...attachments.map((attachment) => ({
360
+ type: "file",
361
+ path: attachment.path,
362
+ })),
363
+ ];
364
+ const value = await expectJson(await this.authorizedRequest(`/ai-proxy/realtime/v1/sessions/${this.id}/in/append`, {
365
+ method: "POST",
366
+ headers: {
367
+ "content-type": "application/json",
368
+ "x-trigger-source": "sdk",
369
+ "x-part-id": randomId(),
370
+ },
371
+ body: JSON.stringify({
372
+ kind: "message",
373
+ payload: {
374
+ message: {
375
+ id: messageId,
376
+ role: "user",
377
+ parts,
378
+ },
379
+ chatId: this.id,
380
+ trigger: "submit-message",
381
+ messageId,
382
+ metadata: {
383
+ originHost: runtimeOriginHost(),
384
+ timezone,
385
+ submissionSource: "sdk",
386
+ ...(options.metadata || {}),
387
+ },
388
+ },
389
+ }),
390
+ signal: options.signal,
391
+ }));
392
+ return {
393
+ accepted: value.accepted,
394
+ sessionId: value.sessionId,
395
+ messageId: value.messageId,
396
+ attachments: value.attachments || attachments,
397
+ };
398
+ }
399
+ connect(options = {}) {
400
+ if (options.lastEventId !== undefined) {
401
+ this.lastEventId = options.lastEventId;
402
+ }
403
+ return new ArenaEventStream(this, options);
404
+ }
405
+ async *stream(options) {
406
+ const messageId = options.messageId || randomId();
407
+ const stream = this.connect({
408
+ reconnect: options.reconnect,
409
+ reconnectDelayMs: options.reconnectDelayMs,
410
+ signal: options.signal,
411
+ });
412
+ let started = false;
413
+ try {
414
+ await stream.ready;
415
+ await this.send({
416
+ input: options.input,
417
+ files: options.files,
418
+ attachments: options.attachments,
419
+ messageId,
420
+ metadata: options.metadata,
421
+ signal: options.signal,
422
+ });
423
+ for await (const event of stream) {
424
+ if (!started) {
425
+ started =
426
+ event.type === "start" && event.messageId === messageId;
427
+ if (!started)
428
+ continue;
429
+ }
430
+ const request = inputRequest(event);
431
+ const answers = request && options.onInputRequired
432
+ ? Promise.resolve(options.onInputRequired(request, this))
433
+ : undefined;
434
+ yield event;
435
+ if (request && answers) {
436
+ try {
437
+ await this.respondToInput(request.requestId, await answers, options.signal);
438
+ }
439
+ catch (error) {
440
+ await this.cancel().catch(() => undefined);
441
+ throw error;
442
+ }
443
+ }
444
+ if (event.type === "trigger:turn-complete")
445
+ break;
446
+ }
447
+ }
448
+ finally {
449
+ stream.close();
450
+ if (options.signal?.aborted && options.cancelOnAbort !== false) {
451
+ await this.cancel().catch(() => undefined);
452
+ }
453
+ }
454
+ }
455
+ async run(options) {
456
+ const events = [];
457
+ const files = new Map();
458
+ let text = "";
459
+ let reasoning = "";
460
+ let messages = [];
461
+ let status = "unknown";
462
+ let error;
463
+ for await (const event of this.stream(options)) {
464
+ events.push(event);
465
+ if (event.type === "text-delta")
466
+ text += String(event.delta || "");
467
+ if (event.type === "reasoning-delta") {
468
+ reasoning += String(event.delta || "");
469
+ }
470
+ if (event.type === "data-messages") {
471
+ const data = event.data && typeof event.data === "object"
472
+ ? event.data
473
+ : {};
474
+ if (Array.isArray(data.messages))
475
+ messages = data.messages;
476
+ }
477
+ if (["data-workspace", "data-present-file"].includes(event.type)) {
478
+ const data = event.data && typeof event.data === "object"
479
+ ? event.data
480
+ : {};
481
+ const path = typeof data.path === "string" ? data.path : "";
482
+ if (path)
483
+ files.set(path, data);
484
+ }
485
+ if (event.type === "error") {
486
+ error = String(event.errorText || "Agent error");
487
+ }
488
+ if (event.type === "trigger:turn-complete") {
489
+ status = event.status === "success" ? "success" : "error";
490
+ if (event.error)
491
+ error = String(event.error);
492
+ }
493
+ }
494
+ return {
495
+ status,
496
+ text,
497
+ reasoning,
498
+ messages,
499
+ files: [...files.values()],
500
+ events,
501
+ ...(error ? { error } : {}),
502
+ };
503
+ }
504
+ async respondToInput(requestId, answers, signal) {
505
+ await expectJson(await this.authorizedRequest(`/api/chat/sessions/${this.id}/input-response`, {
506
+ method: "POST",
507
+ headers: { "content-type": "application/json" },
508
+ body: JSON.stringify({ requestId, answers }),
509
+ signal,
510
+ }));
511
+ }
512
+ async cancel(signal) {
513
+ const value = await expectJson(await this.authorizedRequest(`/api/chat/sessions/${this.id}/cancel`, { method: "POST", signal }));
514
+ return value.cancelled === true;
515
+ }
516
+ workspaceUrl(filePath) {
517
+ return this.client.resolveUrl(this.workspacePath(filePath));
518
+ }
519
+ async download(filePath, signal) {
520
+ const response = await this.authorizedRequest(this.workspacePath(filePath), { signal });
521
+ if (!response.ok)
522
+ await expectJson(response);
523
+ return response.blob();
524
+ }
525
+ workspacePath(filePath) {
526
+ const encoded = filePath
527
+ .replace(/^\/home\/user\/?/, "")
528
+ .split("/")
529
+ .map(encodeURIComponent)
530
+ .join("/");
531
+ return `/api/chat/sessions/${this.id}/workspace/${encoded}`;
532
+ }
533
+ async openEventResponse(signal) {
534
+ const response = await this.authorizedRequest(`/ai-proxy/realtime/v1/sessions/${this.id}/out`, {
535
+ headers: {
536
+ accept: "text/event-stream",
537
+ "last-event-id": this.lastEventId,
538
+ },
539
+ signal,
540
+ });
541
+ if (!response.ok)
542
+ await expectJson(response);
543
+ if (!response.body) {
544
+ throw new Error("Arena event stream returned no response body");
545
+ }
546
+ return response;
547
+ }
548
+ async authorizedRequest(pathname, init = {}, retry = true) {
549
+ const headers = new Headers(init.headers);
550
+ headers.set("authorization", `Bearer ${this.tokenValue}`);
551
+ const response = await this.client.request(pathname, {
552
+ ...init,
553
+ headers,
554
+ });
555
+ if (response.status === 401 && retry) {
556
+ await response.body?.cancel().catch(() => undefined);
557
+ await this.refreshToken(init.signal || undefined);
558
+ return this.authorizedRequest(pathname, init, false);
559
+ }
560
+ return response;
561
+ }
562
+ }
563
+ export class ArenaEventStream {
564
+ session;
565
+ options;
566
+ ready;
567
+ closed;
568
+ controller = new AbortController();
569
+ events = new AsyncQueue();
570
+ reconnect;
571
+ reconnectDelayMs;
572
+ readyResolve;
573
+ readyReject;
574
+ closedResolve;
575
+ readySettled = false;
576
+ connectedOnce = false;
577
+ intentionalClose = false;
578
+ removeExternalAbort;
579
+ constructor(session, options) {
580
+ this.session = session;
581
+ this.options = options;
582
+ this.reconnect = options.reconnect !== false;
583
+ this.reconnectDelayMs = options.reconnectDelayMs ?? 800;
584
+ this.ready = new Promise((resolve, reject) => {
585
+ this.readyResolve = resolve;
586
+ this.readyReject = reject;
587
+ });
588
+ this.closed = new Promise((resolve) => {
589
+ this.closedResolve = resolve;
590
+ });
591
+ if (options.signal) {
592
+ const abort = () => this.close();
593
+ options.signal.addEventListener("abort", abort, { once: true });
594
+ this.removeExternalAbort = () => options.signal?.removeEventListener("abort", abort);
595
+ if (options.signal.aborted)
596
+ this.close();
597
+ }
598
+ queueMicrotask(() => void this.pump());
599
+ }
600
+ close() {
601
+ if (this.intentionalClose)
602
+ return;
603
+ this.intentionalClose = true;
604
+ this.controller.abort(abortError());
605
+ }
606
+ [Symbol.asyncIterator]() {
607
+ return this.events[Symbol.asyncIterator]();
608
+ }
609
+ state(value) {
610
+ this.options.onStateChange?.(value);
611
+ }
612
+ async pump() {
613
+ try {
614
+ while (!this.controller.signal.aborted) {
615
+ this.state(this.connectedOnce ? "reconnecting" : "connecting");
616
+ try {
617
+ const response = await this.session.openEventResponse(this.controller.signal);
618
+ if (!this.connectedOnce) {
619
+ this.connectedOnce = true;
620
+ this.readySettled = true;
621
+ this.readyResolve();
622
+ }
623
+ this.state("connected");
624
+ for await (const frame of sseFrames(response.body)) {
625
+ if (frame.id)
626
+ this.session.lastEventId = frame.id;
627
+ const value = JSON.parse(frame.data);
628
+ const event = {
629
+ ...value,
630
+ ...(frame.id ? { eventId: frame.id } : {}),
631
+ };
632
+ this.events.push(event);
633
+ this.options.onEvent?.(event);
634
+ }
635
+ if (!this.reconnect)
636
+ break;
637
+ }
638
+ catch (error) {
639
+ if (this.controller.signal.aborted)
640
+ break;
641
+ this.options.onError?.(error instanceof Error ? error : new Error(String(error)));
642
+ if (!this.reconnect) {
643
+ if (!this.readySettled) {
644
+ this.readySettled = true;
645
+ this.readyReject(error);
646
+ }
647
+ this.events.close(error);
648
+ return;
649
+ }
650
+ }
651
+ await abortableDelay(this.reconnectDelayMs, this.controller.signal).catch(() => undefined);
652
+ }
653
+ this.events.close();
654
+ }
655
+ catch (error) {
656
+ if (this.controller.signal.aborted)
657
+ this.events.close();
658
+ else {
659
+ this.options.onError?.(new Error(errorMessage(error)));
660
+ this.events.close(error);
661
+ }
662
+ }
663
+ finally {
664
+ if (!this.readySettled) {
665
+ this.readySettled = true;
666
+ this.readyReject(this.controller.signal.reason || new Error("event stream closed"));
667
+ }
668
+ this.removeExternalAbort?.();
669
+ this.state("closed");
670
+ this.closedResolve();
671
+ }
672
+ }
673
+ }
674
+ //# sourceMappingURL=sdk.js.map