@antipopp/agno-client 0.9.0 → 0.11.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/dist/index.mjs CHANGED
@@ -1,85 +1,9 @@
1
+ // src/index.ts
2
+ import { RunEvent as RunEvent2 } from "@antipopp/agno-types";
3
+
1
4
  // src/client.ts
2
- import EventEmitter from "eventemitter3";
3
5
  import { RunEvent } from "@antipopp/agno-types";
4
-
5
- // src/stores/message-store.ts
6
- var MessageStore = class {
7
- constructor() {
8
- this.messages = [];
9
- }
10
- /**
11
- * Get all messages
12
- */
13
- getMessages() {
14
- return [...this.messages];
15
- }
16
- /**
17
- * Set messages (replaces all)
18
- */
19
- setMessages(messages) {
20
- this.messages = [...messages];
21
- }
22
- /**
23
- * Add a message
24
- */
25
- addMessage(message) {
26
- this.messages = [...this.messages, message];
27
- }
28
- /**
29
- * Update the last message
30
- */
31
- updateLastMessage(updater) {
32
- if (this.messages.length === 0)
33
- return void 0;
34
- const lastMessage = this.messages[this.messages.length - 1];
35
- const updatedMessage = updater(lastMessage);
36
- this.messages = [
37
- ...this.messages.slice(0, -1),
38
- updatedMessage
39
- ];
40
- return updatedMessage;
41
- }
42
- /**
43
- * Update a specific message by index
44
- */
45
- updateMessage(index, updater) {
46
- if (index < 0 || index >= this.messages.length)
47
- return void 0;
48
- const message = this.messages[index];
49
- const updatedMessage = updater(message);
50
- this.messages = [
51
- ...this.messages.slice(0, index),
52
- updatedMessage,
53
- ...this.messages.slice(index + 1)
54
- ];
55
- return updatedMessage;
56
- }
57
- /**
58
- * Remove last N messages
59
- */
60
- removeLastMessages(count) {
61
- this.messages = this.messages.slice(0, -count);
62
- }
63
- /**
64
- * Clear all messages
65
- */
66
- clear() {
67
- this.messages = [];
68
- }
69
- /**
70
- * Get the last message
71
- */
72
- getLastMessage() {
73
- return this.messages.length > 0 ? this.messages[this.messages.length - 1] : void 0;
74
- }
75
- /**
76
- * Check if last message has streaming error
77
- */
78
- hasLastMessageError() {
79
- const lastMessage = this.getLastMessage();
80
- return lastMessage?.streamingError === true;
81
- }
82
- };
6
+ import EventEmitter from "eventemitter3";
83
7
 
84
8
  // src/managers/config-manager.ts
85
9
  var ConfigManager = class {
@@ -224,6 +148,18 @@ var ConfigManager = class {
224
148
  setParams(params) {
225
149
  this.updateField("params", params);
226
150
  }
151
+ /**
152
+ * Get global dependencies
153
+ */
154
+ getDependencies() {
155
+ return this.config.dependencies;
156
+ }
157
+ /**
158
+ * Set global dependencies
159
+ */
160
+ setDependencies(dependencies) {
161
+ this.updateField("dependencies", dependencies);
162
+ }
227
163
  /**
228
164
  * Get current entity ID (agent or team based on mode)
229
165
  */
@@ -237,14 +173,14 @@ var ConfigManager = class {
237
173
  const mode = this.getMode();
238
174
  const endpoint = this.getEndpoint();
239
175
  const entityId = this.getCurrentEntityId();
240
- if (!entityId)
176
+ if (!entityId) {
241
177
  return null;
178
+ }
242
179
  const encodedEntityId = encodeURIComponent(entityId);
243
180
  if (mode === "team") {
244
181
  return `${endpoint}/teams/${encodedEntityId}/runs`;
245
- } else {
246
- return `${endpoint}/agents/${encodedEntityId}/runs`;
247
182
  }
183
+ return `${endpoint}/agents/${encodedEntityId}/runs`;
248
184
  }
249
185
  /**
250
186
  * Build request headers by merging global headers, per-request headers, and auth token.
@@ -267,7 +203,7 @@ var ConfigManager = class {
267
203
  }
268
204
  const authToken = this.getAuthToken();
269
205
  if (authToken) {
270
- headers["Authorization"] = `Bearer ${authToken}`;
206
+ headers.Authorization = `Bearer ${authToken}`;
271
207
  }
272
208
  return headers;
273
209
  }
@@ -291,6 +227,29 @@ var ConfigManager = class {
291
227
  }
292
228
  return new URLSearchParams(params);
293
229
  }
230
+ /**
231
+ * Build dependencies by merging global dependencies and per-request dependencies.
232
+ * Merge order (lowest to highest precedence):
233
+ * 1. Global dependencies from config
234
+ * 2. Per-request dependencies (overrides global)
235
+ *
236
+ * @param perRequestDependencies - Optional dependencies for this specific request
237
+ * @returns Merged dependencies object, or undefined if no dependencies are configured
238
+ */
239
+ buildDependencies(perRequestDependencies) {
240
+ const dependencies = {};
241
+ const globalDependencies = this.getDependencies();
242
+ if (globalDependencies) {
243
+ Object.assign(dependencies, globalDependencies);
244
+ }
245
+ if (perRequestDependencies) {
246
+ Object.assign(dependencies, perRequestDependencies);
247
+ }
248
+ if (Object.keys(dependencies).length === 0) {
249
+ return void 0;
250
+ }
251
+ return dependencies;
252
+ }
294
253
  };
295
254
 
296
255
  // src/managers/session-manager.ts
@@ -377,75 +336,470 @@ var SessionManager = class {
377
336
  convertRunsToMessages(runs) {
378
337
  const messages = [];
379
338
  for (const run of runs) {
380
- const timestamp = run.created_at ? new Date(run.created_at).getTime() / 1e3 : Math.floor(Date.now() / 1e3);
381
- if (run.run_input) {
382
- messages.push({
383
- role: "user",
384
- content: run.run_input,
385
- created_at: timestamp
386
- });
387
- }
388
- const toolCalls = [];
389
- if (run.tools && Array.isArray(run.tools)) {
390
- for (const tool of run.tools) {
391
- const toolObj = tool;
392
- const toolCall = {
393
- role: "tool",
394
- content: toolObj.content ?? "",
395
- tool_call_id: toolObj.tool_call_id ?? "",
396
- tool_name: toolObj.tool_name ?? "",
397
- tool_args: toolObj.tool_args ?? {},
398
- tool_call_error: toolObj.tool_call_error ?? false,
399
- metrics: toolObj.metrics ?? { time: 0 },
400
- created_at: timestamp
401
- };
402
- toolCalls.push(toolCall);
403
- }
404
- }
405
- if (run.reasoning_messages && Array.isArray(run.reasoning_messages)) {
406
- for (const msg of run.reasoning_messages) {
407
- const reasoningMsg = msg;
408
- if (reasoningMsg.role === "tool") {
409
- toolCalls.push({
410
- role: "tool",
411
- content: reasoningMsg.content ?? "",
412
- tool_call_id: reasoningMsg.tool_call_id ?? "",
413
- tool_name: reasoningMsg.tool_name ?? "",
414
- tool_args: reasoningMsg.tool_args ?? {},
415
- tool_call_error: reasoningMsg.tool_call_error ?? false,
416
- metrics: reasoningMsg.metrics ?? { time: 0 },
417
- created_at: reasoningMsg.created_at ?? timestamp
418
- });
419
- }
420
- }
421
- }
422
- let contentStr = "";
423
- if (typeof run.content === "string") {
424
- contentStr = run.content;
425
- } else if (run.content && typeof run.content === "object") {
426
- contentStr = JSON.stringify(run.content);
339
+ const timestamp = this.getRunTimestamp(run);
340
+ const inputMedia = this.mergeInputMedia(
341
+ this.extractInputMedia(run.input_media),
342
+ this.extractInputMediaFromMessages(run.messages)
343
+ );
344
+ if (run.run_input || this.hasAnyMedia(inputMedia)) {
345
+ messages.push(
346
+ this.buildUserMessage(run.run_input ?? "", timestamp, inputMedia)
347
+ );
427
348
  }
428
- const extraData = run.reasoning_messages || run.reasoning_steps || run.references ? {
429
- reasoning_messages: run.reasoning_messages,
430
- reasoning_steps: run.reasoning_steps,
431
- references: run.references
432
- } : void 0;
349
+ const toolCalls = this.extractToolCalls(run, timestamp);
433
350
  messages.push({
434
351
  role: "agent",
435
- content: contentStr,
352
+ content: this.normalizeRunContent(run.content),
436
353
  tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
437
- extra_data: extraData,
438
- images: run.images,
439
- videos: run.videos,
440
- audio: run.audio,
441
- response_audio: run.response_audio,
354
+ extra_data: this.buildExtraData(run),
355
+ images: this.normalizeImages(run.images),
356
+ videos: this.normalizeVideos(run.videos),
357
+ audio: this.normalizeAudio(run.audio),
358
+ files: this.normalizeFiles(run.files),
359
+ response_audio: run.response_audio ?? void 0,
442
360
  created_at: timestamp + 1
443
361
  // Agent response is slightly after user message
444
362
  });
445
363
  }
446
364
  return messages;
447
365
  }
366
+ getRunTimestamp(run) {
367
+ return run.created_at ? new Date(run.created_at).getTime() / 1e3 : Math.floor(Date.now() / 1e3);
368
+ }
369
+ buildUserMessage(content, createdAt, media) {
370
+ return {
371
+ role: "user",
372
+ content,
373
+ images: media?.images,
374
+ videos: media?.videos,
375
+ audio: media?.audio,
376
+ files: media?.files,
377
+ created_at: createdAt
378
+ };
379
+ }
380
+ hasAnyMedia(media) {
381
+ return Boolean(
382
+ media.images && media.images.length > 0 || media.videos && media.videos.length > 0 || media.audio && media.audio.length > 0 || media.files && media.files.length > 0
383
+ );
384
+ }
385
+ isRecord(value) {
386
+ return typeof value === "object" && value !== null;
387
+ }
388
+ getStringValue(record, key) {
389
+ const value = record[key];
390
+ return typeof value === "string" && value.length > 0 ? value : void 0;
391
+ }
392
+ getNumberOrStringValue(record, key) {
393
+ const value = record[key];
394
+ return typeof value === "number" || typeof value === "string" ? value : void 0;
395
+ }
396
+ buildDataUrl(content, mimeType, fallbackMimeType) {
397
+ const safeMimeType = mimeType || fallbackMimeType;
398
+ return `data:${safeMimeType};base64,${content}`;
399
+ }
400
+ normalizeImage(item) {
401
+ if (!this.isRecord(item)) {
402
+ return void 0;
403
+ }
404
+ const mimeType = this.getStringValue(item, "mime_type");
405
+ const content = this.getStringValue(item, "content");
406
+ const url = this.getStringValue(item, "url") || (content ? this.buildDataUrl(content, mimeType, "image/png") : void 0);
407
+ if (!url) {
408
+ return void 0;
409
+ }
410
+ return {
411
+ url,
412
+ revised_prompt: this.getStringValue(item, "revised_prompt"),
413
+ original_prompt: this.getStringValue(item, "original_prompt"),
414
+ alt_text: this.getStringValue(item, "alt_text"),
415
+ id: this.getStringValue(item, "id"),
416
+ mime_type: mimeType,
417
+ format: this.getStringValue(item, "format")
418
+ };
419
+ }
420
+ normalizeVideo(item) {
421
+ if (!this.isRecord(item)) {
422
+ return void 0;
423
+ }
424
+ const mimeType = this.getStringValue(item, "mime_type");
425
+ const content = this.getStringValue(item, "content");
426
+ const url = this.getStringValue(item, "url") || (content ? this.buildDataUrl(content, mimeType, "video/mp4") : void 0);
427
+ const normalized = {
428
+ url,
429
+ id: this.getNumberOrStringValue(item, "id"),
430
+ eta: this.getNumberOrStringValue(item, "eta"),
431
+ mime_type: mimeType,
432
+ format: this.getStringValue(item, "format"),
433
+ original_prompt: this.getStringValue(item, "original_prompt"),
434
+ revised_prompt: this.getStringValue(item, "revised_prompt"),
435
+ width: typeof item.width === "number" ? item.width : void 0,
436
+ height: typeof item.height === "number" ? item.height : void 0,
437
+ fps: typeof item.fps === "number" ? item.fps : void 0,
438
+ duration: typeof item.duration === "number" ? item.duration : void 0
439
+ };
440
+ if (normalized.url || normalized.id !== void 0 || normalized.eta !== void 0) {
441
+ return normalized;
442
+ }
443
+ return void 0;
444
+ }
445
+ normalizeAudioEntry(item) {
446
+ if (!this.isRecord(item)) {
447
+ return void 0;
448
+ }
449
+ const mimeType = this.getStringValue(item, "mime_type");
450
+ const content = this.getStringValue(item, "content");
451
+ const url = this.getStringValue(item, "url") || (content ? this.buildDataUrl(content, mimeType, "audio/wav") : void 0);
452
+ const normalized = {
453
+ base64_audio: this.getStringValue(item, "base64_audio"),
454
+ mime_type: mimeType,
455
+ url,
456
+ id: this.getNumberOrStringValue(item, "id"),
457
+ content,
458
+ channels: typeof item.channels === "number" ? item.channels : void 0,
459
+ sample_rate: typeof item.sample_rate === "number" ? item.sample_rate : void 0,
460
+ format: this.getStringValue(item, "format"),
461
+ duration: typeof item.duration === "number" ? item.duration : void 0
462
+ };
463
+ if (normalized.url || normalized.base64_audio || normalized.content || normalized.id !== void 0) {
464
+ return normalized;
465
+ }
466
+ return void 0;
467
+ }
468
+ normalizeFileEntry(item) {
469
+ if (!this.isRecord(item)) {
470
+ return void 0;
471
+ }
472
+ const normalized = {
473
+ id: this.getStringValue(item, "id"),
474
+ url: this.getStringValue(item, "url"),
475
+ filename: this.getStringValue(item, "filename"),
476
+ name: this.getStringValue(item, "name"),
477
+ mime_type: this.getStringValue(item, "mime_type"),
478
+ format: this.getStringValue(item, "format"),
479
+ size: typeof item.size === "number" ? item.size : void 0
480
+ };
481
+ if (normalized.url || normalized.filename || normalized.name || normalized.id) {
482
+ return normalized;
483
+ }
484
+ return void 0;
485
+ }
486
+ normalizeArray(value, normalizer) {
487
+ if (!Array.isArray(value)) {
488
+ return void 0;
489
+ }
490
+ const items = value.map((item) => normalizer(item)).filter((item) => item !== void 0);
491
+ return items.length > 0 ? items : void 0;
492
+ }
493
+ normalizeImages(value) {
494
+ return this.normalizeArray(value, (item) => this.normalizeImage(item));
495
+ }
496
+ normalizeVideos(value) {
497
+ return this.normalizeArray(value, (item) => this.normalizeVideo(item));
498
+ }
499
+ normalizeAudio(value) {
500
+ return this.normalizeArray(value, (item) => this.normalizeAudioEntry(item));
501
+ }
502
+ normalizeFiles(value) {
503
+ return this.normalizeArray(value, (item) => this.normalizeFileEntry(item));
504
+ }
505
+ extractInputMedia(inputMedia) {
506
+ if (!this.isRecord(inputMedia)) {
507
+ return {};
508
+ }
509
+ return {
510
+ images: this.normalizeImages(inputMedia.images),
511
+ videos: this.normalizeVideos(inputMedia.videos),
512
+ audio: this.normalizeAudio(inputMedia.audios ?? inputMedia.audio),
513
+ files: this.normalizeFiles(inputMedia.files)
514
+ };
515
+ }
516
+ extractInputMediaFromMessages(messages) {
517
+ if (!Array.isArray(messages)) {
518
+ return {};
519
+ }
520
+ for (const message of messages) {
521
+ if (!this.isRecord(message)) {
522
+ continue;
523
+ }
524
+ if (this.getStringValue(message, "role") !== "user") {
525
+ continue;
526
+ }
527
+ const media = {
528
+ images: this.normalizeImages(message.images ?? message.image),
529
+ videos: this.normalizeVideos(message.videos ?? message.video),
530
+ audio: this.normalizeAudio(message.audios ?? message.audio),
531
+ files: this.normalizeFiles(message.files)
532
+ };
533
+ if (this.hasAnyMedia(media)) {
534
+ return media;
535
+ }
536
+ }
537
+ return {};
538
+ }
539
+ mergeInputMedia(primary, fallback) {
540
+ return {
541
+ images: primary.images ?? fallback.images,
542
+ videos: primary.videos ?? fallback.videos,
543
+ audio: primary.audio ?? fallback.audio,
544
+ files: primary.files ?? fallback.files
545
+ };
546
+ }
547
+ normalizeRunContent(content) {
548
+ if (typeof content === "string") {
549
+ return content;
550
+ }
551
+ if (content && typeof content === "object") {
552
+ return JSON.stringify(content);
553
+ }
554
+ return "";
555
+ }
556
+ buildToolCall(rawTool, fallbackTimestamp) {
557
+ return {
558
+ role: "tool",
559
+ content: rawTool.content ?? "",
560
+ tool_call_id: rawTool.tool_call_id ?? "",
561
+ tool_name: rawTool.tool_name ?? "",
562
+ tool_args: rawTool.tool_args ?? {},
563
+ tool_call_error: rawTool.tool_call_error ?? false,
564
+ metrics: rawTool.metrics ?? { time: 0 },
565
+ created_at: rawTool.created_at ?? fallbackTimestamp
566
+ };
567
+ }
568
+ extractToolCalls(run, timestamp) {
569
+ const toolCalls = [];
570
+ if (run.tools && Array.isArray(run.tools)) {
571
+ for (const tool of run.tools) {
572
+ toolCalls.push(this.buildToolCall(tool, timestamp));
573
+ }
574
+ }
575
+ if (run.reasoning_messages && Array.isArray(run.reasoning_messages)) {
576
+ for (const message of run.reasoning_messages) {
577
+ if (message.role === "tool") {
578
+ toolCalls.push(this.buildToolCall(message, timestamp));
579
+ }
580
+ }
581
+ }
582
+ return toolCalls;
583
+ }
584
+ buildExtraData(run) {
585
+ if (!(run.reasoning_messages || run.reasoning_steps || run.references)) {
586
+ return void 0;
587
+ }
588
+ return {
589
+ reasoning_messages: run.reasoning_messages,
590
+ reasoning_steps: run.reasoning_steps,
591
+ references: run.references
592
+ };
593
+ }
594
+ };
595
+
596
+ // src/parsers/stream-parser.ts
597
+ var StreamResponseHttpError = class extends Error {
598
+ constructor(status, message) {
599
+ super(message);
600
+ this.name = "StreamResponseHttpError";
601
+ this.status = status;
602
+ }
448
603
  };
604
+ function isLegacyFormat(data) {
605
+ return typeof data === "object" && data !== null && "event" in data && !("data" in data) && typeof data.event === "string";
606
+ }
607
+ function convertNewFormatToLegacy(newFormatData) {
608
+ const { event, data } = newFormatData;
609
+ let parsedData;
610
+ if (typeof data === "string") {
611
+ try {
612
+ parsedData = JSON.parse(data);
613
+ } catch {
614
+ parsedData = {};
615
+ }
616
+ } else {
617
+ parsedData = data;
618
+ }
619
+ return {
620
+ event,
621
+ ...parsedData
622
+ };
623
+ }
624
+ function processChunk(chunk, onChunk) {
625
+ onChunk(chunk);
626
+ }
627
+ function updateJsonParserState(char, state) {
628
+ if (state.inString) {
629
+ if (state.escapeNext) {
630
+ state.escapeNext = false;
631
+ return false;
632
+ }
633
+ if (char === "\\") {
634
+ state.escapeNext = true;
635
+ return false;
636
+ }
637
+ if (char === '"') {
638
+ state.inString = false;
639
+ }
640
+ return false;
641
+ }
642
+ if (char === '"') {
643
+ state.inString = true;
644
+ return false;
645
+ }
646
+ if (char === "{") {
647
+ state.braceCount++;
648
+ return false;
649
+ }
650
+ if (char !== "}") {
651
+ return false;
652
+ }
653
+ state.braceCount--;
654
+ return state.braceCount === 0;
655
+ }
656
+ function findJsonSlice(buffer, fromIndex) {
657
+ const startIndex = buffer.indexOf("{", fromIndex);
658
+ if (startIndex === -1) {
659
+ return void 0;
660
+ }
661
+ const parserState = {
662
+ braceCount: 0,
663
+ inString: false,
664
+ escapeNext: false
665
+ };
666
+ for (let i = startIndex; i < buffer.length; i++) {
667
+ const isJsonComplete = updateJsonParserState(buffer[i], parserState);
668
+ if (isJsonComplete) {
669
+ return { startIndex, endIndex: i };
670
+ }
671
+ }
672
+ return void 0;
673
+ }
674
+ function logChunkParseError(error, jsonString, position) {
675
+ if (typeof process === "undefined" || process.env?.NODE_ENV !== "development") {
676
+ return;
677
+ }
678
+ console.error("Failed to parse JSON chunk:", {
679
+ error,
680
+ chunk: jsonString.substring(0, 100) + (jsonString.length > 100 ? "..." : ""),
681
+ position
682
+ });
683
+ }
684
+ function processJsonSlice(jsonString, startIndex, onChunk) {
685
+ try {
686
+ const parsed = JSON.parse(jsonString);
687
+ if (isLegacyFormat(parsed)) {
688
+ processChunk(parsed, onChunk);
689
+ } else {
690
+ processChunk(convertNewFormatToLegacy(parsed), onChunk);
691
+ }
692
+ return true;
693
+ } catch (error) {
694
+ logChunkParseError(error, jsonString, startIndex);
695
+ if (jsonString.length > 1e4) {
696
+ throw new Error(
697
+ `Failed to parse large JSON chunk at position ${startIndex}`
698
+ );
699
+ }
700
+ return false;
701
+ }
702
+ }
703
+ function parseBuffer(buffer, onChunk) {
704
+ let searchIndex = 0;
705
+ let remainingBuffer = buffer;
706
+ while (true) {
707
+ const jsonSlice = findJsonSlice(remainingBuffer, searchIndex);
708
+ if (!jsonSlice) {
709
+ break;
710
+ }
711
+ const jsonString = remainingBuffer.slice(
712
+ jsonSlice.startIndex,
713
+ jsonSlice.endIndex + 1
714
+ );
715
+ const parsed = processJsonSlice(jsonString, jsonSlice.startIndex, onChunk);
716
+ if (!parsed) {
717
+ searchIndex = jsonSlice.startIndex + 1;
718
+ continue;
719
+ }
720
+ remainingBuffer = remainingBuffer.slice(jsonSlice.endIndex + 1).trim();
721
+ searchIndex = 0;
722
+ }
723
+ return remainingBuffer;
724
+ }
725
+ function buildRequestHeaders(headers, requestBody) {
726
+ return {
727
+ ...!(requestBody instanceof FormData) && {
728
+ "Content-Type": "application/json"
729
+ },
730
+ ...headers
731
+ };
732
+ }
733
+ async function buildErrorMessage(response) {
734
+ const defaultMessage = `HTTP ${response.status}: ${response.statusText}`;
735
+ const contentType = response.headers.get("content-type");
736
+ if (!contentType?.includes("application/json")) {
737
+ return defaultMessage;
738
+ }
739
+ try {
740
+ const errorData = await response.json();
741
+ return errorData.detail || errorData.message || defaultMessage;
742
+ } catch {
743
+ return defaultMessage;
744
+ }
745
+ }
746
+ async function consumeResponseStream(body, onChunk, onComplete) {
747
+ const reader = body.getReader();
748
+ const decoder = new TextDecoder();
749
+ let buffer = "";
750
+ while (true) {
751
+ const { done, value } = await reader.read();
752
+ if (done) {
753
+ parseBuffer(buffer, onChunk);
754
+ onComplete();
755
+ return;
756
+ }
757
+ buffer += decoder.decode(value, { stream: true });
758
+ buffer = parseBuffer(buffer, onChunk);
759
+ }
760
+ }
761
+ async function streamResponse(options) {
762
+ const {
763
+ apiUrl,
764
+ headers = {},
765
+ params,
766
+ requestBody,
767
+ onChunk,
768
+ onError,
769
+ onComplete,
770
+ signal
771
+ } = options;
772
+ const finalUrl = params?.toString() ? `${apiUrl}?${params.toString()}` : apiUrl;
773
+ try {
774
+ const response = await fetch(finalUrl, {
775
+ method: "POST",
776
+ headers: buildRequestHeaders(headers, requestBody),
777
+ body: requestBody instanceof FormData ? requestBody : JSON.stringify(requestBody),
778
+ signal
779
+ });
780
+ if (!response.ok) {
781
+ const errorMessage = await buildErrorMessage(response);
782
+ throw new StreamResponseHttpError(response.status, errorMessage);
783
+ }
784
+ if (!response.body) {
785
+ throw new Error("No response body");
786
+ }
787
+ await consumeResponseStream(response.body, onChunk, onComplete);
788
+ } catch (error) {
789
+ if (error instanceof Error && error.name === "AbortError") {
790
+ return;
791
+ }
792
+ if (error instanceof Error) {
793
+ onError(error);
794
+ return;
795
+ }
796
+ if (typeof error === "object" && error !== null && "detail" in error) {
797
+ onError(new Error(String(error.detail)));
798
+ } else {
799
+ onError(new Error(String(error)));
800
+ }
801
+ }
802
+ }
449
803
 
450
804
  // src/processors/event-processor.ts
451
805
  import { RunEvent as RunEventEnum } from "@antipopp/agno-types";
@@ -470,14 +824,18 @@ function processToolCall(toolCall, prevToolCalls = []) {
470
824
  );
471
825
  if (existingToolCallIndex >= 0) {
472
826
  const updatedToolCalls = [...prevToolCalls];
473
- updatedToolCalls[existingToolCallIndex] = {
474
- ...updatedToolCalls[existingToolCallIndex],
475
- ...toolCall
476
- };
827
+ const existing = updatedToolCalls[existingToolCallIndex];
828
+ const merged = { ...existing, ...toolCall };
829
+ if (existing.external_execution && existing.result !== void 0) {
830
+ merged.result = existing.result;
831
+ }
832
+ if (existing.ui_component !== void 0 && !toolCall.ui_component) {
833
+ merged.ui_component = existing.ui_component;
834
+ }
835
+ updatedToolCalls[existingToolCallIndex] = merged;
477
836
  return updatedToolCalls;
478
- } else {
479
- return [...prevToolCalls, toolCall];
480
837
  }
838
+ return [...prevToolCalls, toolCall];
481
839
  }
482
840
  function processChunkToolCalls(chunk, existingToolCalls = []) {
483
841
  let updatedToolCalls = [...existingToolCalls];
@@ -495,6 +853,105 @@ var EventProcessor = class {
495
853
  constructor() {
496
854
  this.lastContent = "";
497
855
  }
856
+ appendRunContent(chunk, updatedMessage) {
857
+ if (typeof chunk.content === "string") {
858
+ const uniqueContent = chunk.content.replace(this.lastContent, "");
859
+ updatedMessage.content = updatedMessage.content + uniqueContent;
860
+ this.lastContent = chunk.content;
861
+ return;
862
+ }
863
+ if (typeof chunk.content !== "string" && chunk.content !== null) {
864
+ const jsonBlock = getJsonMarkdown(chunk.content);
865
+ updatedMessage.content = updatedMessage.content + jsonBlock;
866
+ this.lastContent = jsonBlock;
867
+ }
868
+ }
869
+ applyRunContentFields(chunk, lastMessage, updatedMessage) {
870
+ this.appendRunContent(chunk, updatedMessage);
871
+ updatedMessage.tool_calls = processChunkToolCalls(
872
+ chunk,
873
+ lastMessage.tool_calls
874
+ );
875
+ if (chunk.extra_data?.reasoning_steps) {
876
+ updatedMessage.extra_data = {
877
+ ...updatedMessage.extra_data,
878
+ reasoning_steps: chunk.extra_data.reasoning_steps
879
+ };
880
+ }
881
+ if (chunk.extra_data?.references) {
882
+ updatedMessage.extra_data = {
883
+ ...updatedMessage.extra_data,
884
+ references: chunk.extra_data.references
885
+ };
886
+ }
887
+ updatedMessage.created_at = chunk.created_at ?? lastMessage.created_at;
888
+ if (chunk.images) {
889
+ updatedMessage.images = chunk.images;
890
+ }
891
+ if (chunk.image) {
892
+ const existingImages = updatedMessage.images ?? lastMessage.images ?? [];
893
+ const hasImage = existingImages.some((image) => {
894
+ if (image.id && chunk.image?.id) {
895
+ return image.id === chunk.image.id;
896
+ }
897
+ return image.url === chunk.image?.url;
898
+ });
899
+ if (!hasImage) {
900
+ updatedMessage.images = [...existingImages, chunk.image];
901
+ }
902
+ }
903
+ if (chunk.videos) {
904
+ updatedMessage.videos = chunk.videos;
905
+ }
906
+ if (chunk.audio) {
907
+ updatedMessage.audio = chunk.audio;
908
+ }
909
+ if (chunk.files) {
910
+ updatedMessage.files = chunk.files;
911
+ }
912
+ if (chunk.response_audio?.transcript && typeof chunk.response_audio.transcript === "string") {
913
+ updatedMessage.response_audio = {
914
+ ...updatedMessage.response_audio,
915
+ transcript: (updatedMessage.response_audio?.transcript || "") + chunk.response_audio.transcript
916
+ };
917
+ }
918
+ }
919
+ applyCompletedFields(chunk, lastMessage, updatedMessage) {
920
+ let updatedContent;
921
+ if (typeof chunk.content === "string") {
922
+ updatedContent = chunk.content;
923
+ } else {
924
+ try {
925
+ updatedContent = JSON.stringify(chunk.content);
926
+ } catch {
927
+ updatedContent = "Error parsing response";
928
+ }
929
+ }
930
+ updatedMessage.content = updatedContent;
931
+ updatedMessage.tool_calls = processChunkToolCalls(
932
+ chunk,
933
+ lastMessage.tool_calls
934
+ );
935
+ const completedImages = chunk.images ?? (chunk.image ? [chunk.image] : void 0);
936
+ updatedMessage.images = completedImages ?? lastMessage.images;
937
+ updatedMessage.videos = chunk.videos ?? lastMessage.videos;
938
+ updatedMessage.audio = chunk.audio ?? lastMessage.audio;
939
+ updatedMessage.files = chunk.files ?? lastMessage.files;
940
+ updatedMessage.response_audio = chunk.response_audio;
941
+ updatedMessage.created_at = chunk.created_at ?? lastMessage.created_at;
942
+ updatedMessage.extra_data = {
943
+ reasoning_steps: chunk.extra_data?.reasoning_steps ?? lastMessage.extra_data?.reasoning_steps,
944
+ references: chunk.extra_data?.references ?? lastMessage.extra_data?.references
945
+ };
946
+ }
947
+ appendReasoningSteps(chunk, lastMessage, updatedMessage) {
948
+ const existingSteps = lastMessage.extra_data?.reasoning_steps ?? [];
949
+ const incomingSteps = chunk.extra_data?.reasoning_steps ?? [];
950
+ updatedMessage.extra_data = {
951
+ ...updatedMessage.extra_data,
952
+ reasoning_steps: [...existingSteps, ...incomingSteps]
953
+ };
954
+ }
498
955
  /**
499
956
  * Process a chunk and update the last message
500
957
  */
@@ -521,58 +978,13 @@ var EventProcessor = class {
521
978
  break;
522
979
  case RunEventEnum.RunContent:
523
980
  case RunEventEnum.TeamRunContent:
524
- if (typeof chunk.content === "string") {
525
- const uniqueContent = chunk.content.replace(this.lastContent, "");
526
- updatedMessage.content = updatedMessage.content + uniqueContent;
527
- this.lastContent = chunk.content;
528
- } else if (typeof chunk.content !== "string" && chunk.content !== null) {
529
- const jsonBlock = getJsonMarkdown(chunk.content);
530
- updatedMessage.content = updatedMessage.content + jsonBlock;
531
- this.lastContent = jsonBlock;
532
- }
533
- updatedMessage.tool_calls = processChunkToolCalls(
534
- chunk,
535
- lastMessage.tool_calls
536
- );
537
- if (chunk.extra_data?.reasoning_steps) {
538
- updatedMessage.extra_data = {
539
- ...updatedMessage.extra_data,
540
- reasoning_steps: chunk.extra_data.reasoning_steps
541
- };
542
- }
543
- if (chunk.extra_data?.references) {
544
- updatedMessage.extra_data = {
545
- ...updatedMessage.extra_data,
546
- references: chunk.extra_data.references
547
- };
548
- }
549
- updatedMessage.created_at = chunk.created_at ?? lastMessage.created_at;
550
- if (chunk.images) {
551
- updatedMessage.images = chunk.images;
552
- }
553
- if (chunk.videos) {
554
- updatedMessage.videos = chunk.videos;
555
- }
556
- if (chunk.audio) {
557
- updatedMessage.audio = chunk.audio;
558
- }
559
- if (chunk.response_audio?.transcript && typeof chunk.response_audio.transcript === "string") {
560
- const transcript = chunk.response_audio.transcript;
561
- updatedMessage.response_audio = {
562
- ...updatedMessage.response_audio,
563
- transcript: (updatedMessage.response_audio?.transcript || "") + transcript
564
- };
565
- }
981
+ this.applyRunContentFields(chunk, lastMessage, updatedMessage);
566
982
  break;
567
983
  case RunEventEnum.ReasoningStep:
568
- case RunEventEnum.TeamReasoningStep:
569
- const existingSteps = lastMessage.extra_data?.reasoning_steps ?? [];
570
- const incomingSteps = chunk.extra_data?.reasoning_steps ?? [];
571
- updatedMessage.extra_data = {
572
- ...updatedMessage.extra_data,
573
- reasoning_steps: [...existingSteps, ...incomingSteps]
574
- };
984
+ case RunEventEnum.TeamReasoningStep: {
985
+ this.appendReasoningSteps(chunk, lastMessage, updatedMessage);
575
986
  break;
987
+ }
576
988
  case RunEventEnum.ReasoningCompleted:
577
989
  case RunEventEnum.TeamReasoningCompleted:
578
990
  if (chunk.extra_data?.reasoning_steps) {
@@ -583,31 +995,10 @@ var EventProcessor = class {
583
995
  }
584
996
  break;
585
997
  case RunEventEnum.RunCompleted:
586
- case RunEventEnum.TeamRunCompleted:
587
- let updatedContent;
588
- if (typeof chunk.content === "string") {
589
- updatedContent = chunk.content;
590
- } else {
591
- try {
592
- updatedContent = JSON.stringify(chunk.content);
593
- } catch {
594
- updatedContent = "Error parsing response";
595
- }
596
- }
597
- updatedMessage.content = updatedContent;
598
- updatedMessage.tool_calls = processChunkToolCalls(
599
- chunk,
600
- lastMessage.tool_calls
601
- );
602
- updatedMessage.images = chunk.images ?? lastMessage.images;
603
- updatedMessage.videos = chunk.videos ?? lastMessage.videos;
604
- updatedMessage.response_audio = chunk.response_audio;
605
- updatedMessage.created_at = chunk.created_at ?? lastMessage.created_at;
606
- updatedMessage.extra_data = {
607
- reasoning_steps: chunk.extra_data?.reasoning_steps ?? lastMessage.extra_data?.reasoning_steps,
608
- references: chunk.extra_data?.references ?? lastMessage.extra_data?.references
609
- };
998
+ case RunEventEnum.TeamRunCompleted: {
999
+ this.applyCompletedFields(chunk, lastMessage, updatedMessage);
610
1000
  break;
1001
+ }
611
1002
  case RunEventEnum.UpdatingMemory:
612
1003
  case RunEventEnum.TeamMemoryUpdateStarted:
613
1004
  case RunEventEnum.TeamMemoryUpdateCompleted:
@@ -619,6 +1010,8 @@ var EventProcessor = class {
619
1010
  case RunEventEnum.TeamRunCancelled:
620
1011
  updatedMessage.streamingError = true;
621
1012
  break;
1013
+ default:
1014
+ break;
622
1015
  }
623
1016
  return updatedMessage;
624
1017
  }
@@ -630,166 +1023,95 @@ var EventProcessor = class {
630
1023
  }
631
1024
  };
632
1025
 
633
- // src/parsers/stream-parser.ts
634
- function isLegacyFormat(data) {
635
- return typeof data === "object" && data !== null && "event" in data && !("data" in data) && typeof data.event === "string";
636
- }
637
- function convertNewFormatToLegacy(newFormatData) {
638
- const { event, data } = newFormatData;
639
- let parsedData;
640
- if (typeof data === "string") {
641
- try {
642
- parsedData = JSON.parse(data);
643
- } catch {
644
- parsedData = {};
645
- }
646
- } else {
647
- parsedData = data;
648
- }
649
- return {
650
- event,
651
- ...parsedData
652
- };
653
- }
654
- function processChunk(chunk, onChunk) {
655
- onChunk(chunk);
656
- }
657
- function parseBuffer(buffer, onChunk) {
658
- let currentIndex = 0;
659
- let jsonStartIndex = buffer.indexOf("{", currentIndex);
660
- while (jsonStartIndex !== -1 && jsonStartIndex < buffer.length) {
661
- let braceCount = 0;
662
- let inString = false;
663
- let escapeNext = false;
664
- let jsonEndIndex = -1;
665
- let i = jsonStartIndex;
666
- for (; i < buffer.length; i++) {
667
- const char = buffer[i];
668
- if (inString) {
669
- if (escapeNext) {
670
- escapeNext = false;
671
- } else if (char === "\\") {
672
- escapeNext = true;
673
- } else if (char === '"') {
674
- inString = false;
675
- }
676
- } else {
677
- if (char === '"') {
678
- inString = true;
679
- } else if (char === "{") {
680
- braceCount++;
681
- } else if (char === "}") {
682
- braceCount--;
683
- if (braceCount === 0) {
684
- jsonEndIndex = i;
685
- break;
686
- }
687
- }
688
- }
689
- }
690
- if (jsonEndIndex !== -1) {
691
- const jsonString = buffer.slice(jsonStartIndex, jsonEndIndex + 1);
692
- try {
693
- const parsed = JSON.parse(jsonString);
694
- if (isLegacyFormat(parsed)) {
695
- processChunk(parsed, onChunk);
696
- } else {
697
- const legacyChunk = convertNewFormatToLegacy(parsed);
698
- processChunk(legacyChunk, onChunk);
699
- }
700
- } catch (error) {
701
- if (typeof process !== "undefined" && process.env?.NODE_ENV === "development") {
702
- console.error("Failed to parse JSON chunk:", {
703
- error,
704
- chunk: jsonString.substring(0, 100) + (jsonString.length > 100 ? "..." : ""),
705
- position: jsonStartIndex
706
- });
707
- }
708
- if (jsonString.length > 1e4) {
709
- throw new Error(`Failed to parse large JSON chunk at position ${jsonStartIndex}`);
710
- }
711
- jsonStartIndex = buffer.indexOf("{", jsonStartIndex + 1);
712
- continue;
713
- }
714
- currentIndex = jsonEndIndex + 1;
715
- buffer = buffer.slice(currentIndex).trim();
716
- currentIndex = 0;
717
- jsonStartIndex = buffer.indexOf("{", currentIndex);
718
- } else {
719
- break;
720
- }
721
- }
722
- return buffer;
723
- }
724
- async function streamResponse(options) {
725
- const {
726
- apiUrl,
727
- headers = {},
728
- params,
729
- requestBody,
730
- onChunk,
731
- onError,
732
- onComplete,
733
- signal
734
- } = options;
735
- let buffer = "";
736
- const finalUrl = params && params.toString() ? `${apiUrl}?${params.toString()}` : apiUrl;
737
- try {
738
- const response = await fetch(finalUrl, {
739
- method: "POST",
740
- headers: {
741
- ...!(requestBody instanceof FormData) && {
742
- "Content-Type": "application/json"
743
- },
744
- ...headers
745
- },
746
- body: requestBody instanceof FormData ? requestBody : JSON.stringify(requestBody),
747
- signal
748
- });
749
- if (!response.ok) {
750
- let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
751
- const contentType = response.headers.get("content-type");
752
- if (contentType?.includes("application/json")) {
753
- try {
754
- const errorData = await response.json();
755
- errorMessage = errorData.detail || errorData.message || errorMessage;
756
- } catch {
757
- }
758
- }
759
- throw new Error(errorMessage);
760
- }
761
- if (!response.body) {
762
- throw new Error("No response body");
1026
+ // src/stores/message-store.ts
1027
+ var MessageStore = class {
1028
+ constructor() {
1029
+ this.messages = [];
1030
+ }
1031
+ /**
1032
+ * Get all messages
1033
+ */
1034
+ getMessages() {
1035
+ return [...this.messages];
1036
+ }
1037
+ /**
1038
+ * Set messages (replaces all)
1039
+ */
1040
+ setMessages(messages) {
1041
+ this.messages = [...messages];
1042
+ }
1043
+ /**
1044
+ * Add a message
1045
+ */
1046
+ addMessage(message) {
1047
+ this.messages = [...this.messages, message];
1048
+ }
1049
+ /**
1050
+ * Update the last message
1051
+ */
1052
+ updateLastMessage(updater) {
1053
+ if (this.messages.length === 0) {
1054
+ return void 0;
763
1055
  }
764
- const reader = response.body.getReader();
765
- const decoder = new TextDecoder();
766
- const processStream = async () => {
767
- while (true) {
768
- const { done, value } = await reader.read();
769
- if (done) {
770
- buffer = parseBuffer(buffer, onChunk);
771
- onComplete();
772
- return;
773
- }
774
- buffer += decoder.decode(value, { stream: true });
775
- buffer = parseBuffer(buffer, onChunk);
776
- }
777
- };
778
- await processStream();
779
- } catch (error) {
780
- if (error instanceof Error && error.name === "AbortError") {
781
- return;
1056
+ const lastMessage = this.messages.at(-1);
1057
+ if (!lastMessage) {
1058
+ return void 0;
782
1059
  }
783
- if (typeof error === "object" && error !== null && "detail" in error) {
784
- onError(new Error(String(error.detail)));
785
- } else {
786
- onError(new Error(String(error)));
1060
+ const updatedMessage = updater(lastMessage);
1061
+ this.messages = [...this.messages.slice(0, -1), updatedMessage];
1062
+ return updatedMessage;
1063
+ }
1064
+ /**
1065
+ * Update a specific message by index
1066
+ */
1067
+ updateMessage(index, updater) {
1068
+ if (index < 0 || index >= this.messages.length) {
1069
+ return void 0;
787
1070
  }
1071
+ const message = this.messages[index];
1072
+ const updatedMessage = updater(message);
1073
+ this.messages = [
1074
+ ...this.messages.slice(0, index),
1075
+ updatedMessage,
1076
+ ...this.messages.slice(index + 1)
1077
+ ];
1078
+ return updatedMessage;
788
1079
  }
789
- }
1080
+ /**
1081
+ * Remove last N messages
1082
+ */
1083
+ removeLastMessages(count) {
1084
+ this.messages = this.messages.slice(0, -count);
1085
+ }
1086
+ /**
1087
+ * Clear all messages
1088
+ */
1089
+ clear() {
1090
+ this.messages = [];
1091
+ }
1092
+ /**
1093
+ * Get the last message
1094
+ */
1095
+ getLastMessage() {
1096
+ return this.messages.length > 0 ? this.messages.at(-1) : void 0;
1097
+ }
1098
+ /**
1099
+ * Check if last message has streaming error
1100
+ */
1101
+ hasLastMessageError() {
1102
+ const lastMessage = this.getLastMessage();
1103
+ return lastMessage?.streamingError === true;
1104
+ }
1105
+ };
790
1106
 
791
1107
  // src/utils/logger.ts
792
- var SENSITIVE_KEYS = ["authToken", "Authorization", "token", "password", "apiKey"];
1108
+ var SENSITIVE_KEYS = [
1109
+ "authToken",
1110
+ "Authorization",
1111
+ "token",
1112
+ "password",
1113
+ "apiKey"
1114
+ ];
793
1115
  function sanitizeObject(obj) {
794
1116
  if (obj === null || obj === void 0) {
795
1117
  return obj;
@@ -817,36 +1139,36 @@ function sanitizeObject(obj) {
817
1139
  function isDevelopment() {
818
1140
  return typeof process !== "undefined" && process.env?.NODE_ENV === "development";
819
1141
  }
820
- var Logger = class {
1142
+ var Logger = {
821
1143
  /**
822
1144
  * Log debug information (only in development)
823
1145
  */
824
- static debug(message, data) {
1146
+ debug(message, data) {
825
1147
  if (isDevelopment()) {
826
1148
  const sanitized = data ? sanitizeObject(data) : void 0;
827
1149
  console.debug(`[DEBUG] ${message}`, sanitized || "");
828
1150
  }
829
- }
1151
+ },
830
1152
  /**
831
1153
  * Log informational messages (only in development)
832
1154
  */
833
- static info(message, data) {
1155
+ info(message, data) {
834
1156
  if (isDevelopment()) {
835
1157
  const sanitized = data ? sanitizeObject(data) : void 0;
836
1158
  console.info(`[INFO] ${message}`, sanitized || "");
837
1159
  }
838
- }
1160
+ },
839
1161
  /**
840
1162
  * Log warnings (always logs)
841
1163
  */
842
- static warn(message, data) {
1164
+ warn(message, data) {
843
1165
  const sanitized = data ? sanitizeObject(data) : void 0;
844
1166
  console.warn(`[WARN] ${message}`, sanitized || "");
845
- }
1167
+ },
846
1168
  /**
847
1169
  * Log errors (always logs)
848
1170
  */
849
- static error(message, data) {
1171
+ error(message, data) {
850
1172
  const sanitized = data ? sanitizeObject(data) : void 0;
851
1173
  console.error(`[ERROR] ${message}`, sanitized || "");
852
1174
  }
@@ -864,19 +1186,104 @@ function toSafeISOString(timestamp) {
864
1186
  }
865
1187
  return new Date(ts).toISOString();
866
1188
  }
1189
+ function getFileName(file, index) {
1190
+ if ("name" in file && typeof file.name === "string" && file.name) {
1191
+ return file.name;
1192
+ }
1193
+ return `file-${index}`;
1194
+ }
1195
+ function getFileFormat(mimeType) {
1196
+ if (!mimeType) {
1197
+ return void 0;
1198
+ }
1199
+ const [, subtype] = mimeType.split("/");
1200
+ if (!subtype) {
1201
+ return void 0;
1202
+ }
1203
+ return subtype.split(";")[0]?.trim().toLowerCase() || void 0;
1204
+ }
1205
+ function createPreviewUrl(file) {
1206
+ if (typeof URL === "undefined" || typeof URL.createObjectURL !== "function") {
1207
+ return void 0;
1208
+ }
1209
+ try {
1210
+ return URL.createObjectURL(file);
1211
+ } catch {
1212
+ return void 0;
1213
+ }
1214
+ }
1215
+ function buildMessageMediaPayload(files) {
1216
+ const images = [];
1217
+ const videos = [];
1218
+ const audio = [];
1219
+ const fileAttachments = [];
1220
+ const objectUrls = [];
1221
+ files.forEach((file, index) => {
1222
+ const filename = getFileName(file, index);
1223
+ const mimeType = file.type || "application/octet-stream";
1224
+ const format = getFileFormat(mimeType);
1225
+ const previewUrl = createPreviewUrl(file);
1226
+ if (previewUrl) {
1227
+ objectUrls.push(previewUrl);
1228
+ }
1229
+ if (mimeType.startsWith("image/") && previewUrl) {
1230
+ images.push({
1231
+ url: previewUrl,
1232
+ mime_type: mimeType,
1233
+ format
1234
+ });
1235
+ return;
1236
+ }
1237
+ if (mimeType.startsWith("video/")) {
1238
+ videos.push({
1239
+ url: previewUrl,
1240
+ id: filename,
1241
+ mime_type: mimeType,
1242
+ format
1243
+ });
1244
+ return;
1245
+ }
1246
+ if (mimeType.startsWith("audio/")) {
1247
+ audio.push({
1248
+ url: previewUrl,
1249
+ id: filename,
1250
+ mime_type: mimeType,
1251
+ format
1252
+ });
1253
+ return;
1254
+ }
1255
+ fileAttachments.push({
1256
+ filename,
1257
+ name: filename,
1258
+ mime_type: mimeType,
1259
+ format,
1260
+ size: file.size,
1261
+ url: previewUrl
1262
+ });
1263
+ });
1264
+ return {
1265
+ images: images.length > 0 ? images : void 0,
1266
+ videos: videos.length > 0 ? videos : void 0,
1267
+ audio: audio.length > 0 ? audio : void 0,
1268
+ files: fileAttachments.length > 0 ? fileAttachments : void 0,
1269
+ objectUrls
1270
+ };
1271
+ }
867
1272
  var AgnoClient = class extends EventEmitter {
868
1273
  constructor(config) {
869
1274
  super();
870
- // toolCallId -> UIComponentSpec
871
1275
  this.runCompletedSuccessfully = false;
1276
+ this.currentAbortController = null;
872
1277
  this.messageStore = new MessageStore();
873
1278
  this.configManager = new ConfigManager(config);
874
1279
  this.sessionManager = new SessionManager();
875
1280
  this.eventProcessor = new EventProcessor();
876
1281
  this.pendingUISpecs = /* @__PURE__ */ new Map();
1282
+ this.localAttachmentUrls = /* @__PURE__ */ new Set();
877
1283
  this.state = {
878
1284
  isStreaming: false,
879
1285
  isRefreshing: false,
1286
+ isCancelling: false,
880
1287
  isEndpointActive: false,
881
1288
  agents: [],
882
1289
  teams: [],
@@ -915,12 +1322,99 @@ var AgnoClient = class extends EventEmitter {
915
1322
  * Clear all messages
916
1323
  */
917
1324
  clearMessages() {
1325
+ this.revokeAttachmentUrlsFromMessages(this.messageStore.getMessages());
1326
+ this.localAttachmentUrls.clear();
918
1327
  this.messageStore.clear();
919
1328
  this.configManager.setSessionId(void 0);
920
1329
  this.pendingUISpecs.clear();
921
1330
  this.emit("message:update", this.messageStore.getMessages());
922
1331
  this.emit("state:change", this.getState());
923
1332
  }
1333
+ /**
1334
+ * Cancel an active or paused run
1335
+ */
1336
+ async cancelRun() {
1337
+ if (!(this.state.isStreaming || this.state.isPaused)) {
1338
+ throw new Error("No active or paused run to cancel");
1339
+ }
1340
+ const runId = this.state.pausedRunId || this.currentRunId;
1341
+ if (this.currentAbortController) {
1342
+ this.currentAbortController.abort();
1343
+ this.currentAbortController = null;
1344
+ }
1345
+ this.state.isCancelling = true;
1346
+ this.emit("state:change", this.getState());
1347
+ const cancelErrorMessage = runId ? await this.requestBackendCancel(runId) : this.logMissingRunIdForCancel();
1348
+ this.state.isStreaming = false;
1349
+ this.state.isPaused = false;
1350
+ this.state.isCancelling = false;
1351
+ this.state.pausedRunId = void 0;
1352
+ this.state.toolsAwaitingExecution = void 0;
1353
+ this.currentRunId = void 0;
1354
+ if (cancelErrorMessage) {
1355
+ this.state.errorMessage = cancelErrorMessage;
1356
+ this.emit("message:error", cancelErrorMessage);
1357
+ }
1358
+ this.emit("run:cancelled", { runId });
1359
+ this.emit("state:change", this.getState());
1360
+ }
1361
+ logMissingRunIdForCancel() {
1362
+ Logger.warn(
1363
+ "[AgnoClient] No run ID available, skipping backend cancel request"
1364
+ );
1365
+ return void 0;
1366
+ }
1367
+ async requestBackendCancel(runId) {
1368
+ const runUrl = this.configManager.getRunUrl();
1369
+ if (!runUrl) {
1370
+ const message = "Run cancelled locally, but backend cancel could not be sent: no agent or team selected";
1371
+ Logger.warn(`[AgnoClient] ${message}`);
1372
+ return message;
1373
+ }
1374
+ const cancelUrl = `${runUrl}/${runId}/cancel`;
1375
+ const headers = this.configManager.buildRequestHeaders();
1376
+ try {
1377
+ const response = await fetch(cancelUrl, {
1378
+ method: "POST",
1379
+ headers
1380
+ });
1381
+ if (response.ok) {
1382
+ return void 0;
1383
+ }
1384
+ if (response.status === 401 || response.status === 403) {
1385
+ const message = `Run cancelled locally, but backend cancel was rejected (${response.status})`;
1386
+ Logger.warn(`[AgnoClient] ${message}`);
1387
+ return message;
1388
+ }
1389
+ Logger.warn(
1390
+ `[AgnoClient] Backend cancel returned ${response.status} \u2014 run may have already completed`
1391
+ );
1392
+ return void 0;
1393
+ } catch (error) {
1394
+ const reason = error instanceof Error ? error.message : String(error);
1395
+ const message = `Run cancelled locally, but backend cancel failed: ${reason}`;
1396
+ Logger.warn(`[AgnoClient] ${message}`);
1397
+ return message;
1398
+ }
1399
+ }
1400
+ /**
1401
+ * Abort the active stream without calling the backend cancel endpoint.
1402
+ * Since streamResponse handles AbortError by returning silently
1403
+ * (no onComplete/onError called), state cleanup is done here.
1404
+ */
1405
+ abortStream() {
1406
+ if (!this.state.isStreaming) {
1407
+ return;
1408
+ }
1409
+ if (this.currentAbortController) {
1410
+ this.currentAbortController.abort();
1411
+ this.currentAbortController = null;
1412
+ }
1413
+ this.state.isStreaming = false;
1414
+ this.currentRunId = void 0;
1415
+ this.emit("stream:end");
1416
+ this.emit("state:change", this.getState());
1417
+ }
924
1418
  /**
925
1419
  * Send a message to the agent/team (streaming)
926
1420
  */
@@ -941,16 +1435,30 @@ var AgnoClient = class extends EventEmitter {
941
1435
  if (typeof message === "string") {
942
1436
  formData.append("message", message);
943
1437
  }
1438
+ if (options?.files) {
1439
+ options.files.forEach((file, index) => {
1440
+ formData.append("files", file, getFileName(file, index));
1441
+ });
1442
+ }
1443
+ const requestFiles = formData.getAll("files").filter((entry) => typeof entry !== "string");
1444
+ const userMessageMedia = buildMessageMediaPayload(requestFiles);
1445
+ this.trackAttachmentUrls(userMessageMedia.objectUrls);
1446
+ const userMessageContent = String(formData.get("message") ?? "");
944
1447
  const lastMessage = this.messageStore.getLastMessage();
945
1448
  if (lastMessage?.streamingError) {
946
1449
  const secondLast = this.messageStore.getMessages()[this.messageStore.getMessages().length - 2];
947
1450
  if (secondLast?.role === "user") {
1451
+ this.revokeAttachmentUrlsFromMessages([secondLast, lastMessage]);
948
1452
  this.messageStore.removeLastMessages(2);
949
1453
  }
950
1454
  }
951
1455
  this.messageStore.addMessage({
952
1456
  role: "user",
953
- content: formData.get("message"),
1457
+ content: userMessageContent,
1458
+ images: userMessageMedia.images,
1459
+ videos: userMessageMedia.videos,
1460
+ audio: userMessageMedia.audio,
1461
+ files: userMessageMedia.files,
954
1462
  created_at: Math.floor(Date.now() / 1e3)
955
1463
  });
956
1464
  this.messageStore.addMessage({
@@ -964,33 +1472,42 @@ var AgnoClient = class extends EventEmitter {
964
1472
  this.eventProcessor.reset();
965
1473
  let newSessionId = this.configManager.getSessionId();
966
1474
  try {
967
- formData.append("stream", "true");
968
- formData.append("session_id", newSessionId ?? "");
1475
+ formData.set("stream", "true");
1476
+ formData.set("session_id", newSessionId ?? "");
969
1477
  const userId = this.configManager.getUserId();
970
1478
  if (userId) {
971
- formData.append("user_id", userId);
1479
+ formData.set("user_id", userId);
1480
+ }
1481
+ const dependencies = this.configManager.buildDependencies(
1482
+ options?.dependencies
1483
+ );
1484
+ if (dependencies) {
1485
+ formData.set("dependencies", JSON.stringify(dependencies));
972
1486
  }
973
1487
  const headers = this.configManager.buildRequestHeaders(options?.headers);
974
1488
  const params = this.configManager.buildQueryString(options?.params);
1489
+ this.currentAbortController = new AbortController();
975
1490
  await streamResponse({
976
1491
  apiUrl: runUrl,
977
1492
  headers,
978
1493
  params,
979
1494
  requestBody: formData,
1495
+ signal: this.currentAbortController.signal,
980
1496
  onChunk: (chunk) => {
981
- this.handleChunk(chunk, newSessionId, formData.get("message"));
982
- if (chunk.event === RunEvent.RunStarted || chunk.event === RunEvent.TeamRunStarted || chunk.event === RunEvent.ReasoningStarted || chunk.event === RunEvent.TeamReasoningStarted) {
983
- if (chunk.session_id) {
984
- newSessionId = chunk.session_id;
985
- this.configManager.setSessionId(chunk.session_id);
986
- }
1497
+ this.handleChunk(chunk, newSessionId, userMessageContent);
1498
+ if ((chunk.event === RunEvent.RunStarted || chunk.event === RunEvent.TeamRunStarted || chunk.event === RunEvent.ReasoningStarted || chunk.event === RunEvent.TeamReasoningStarted) && chunk.session_id) {
1499
+ newSessionId = chunk.session_id;
1500
+ this.configManager.setSessionId(chunk.session_id);
987
1501
  }
988
1502
  },
989
1503
  onError: (error) => {
1504
+ this.currentAbortController = null;
990
1505
  this.handleError(error, newSessionId);
991
1506
  },
992
1507
  onComplete: async () => {
1508
+ this.currentAbortController = null;
993
1509
  this.state.isStreaming = false;
1510
+ this.currentRunId = void 0;
994
1511
  this.emit("stream:end");
995
1512
  this.emit("message:complete", this.messageStore.getMessages());
996
1513
  this.emit("state:change", this.getState());
@@ -1001,6 +1518,7 @@ var AgnoClient = class extends EventEmitter {
1001
1518
  }
1002
1519
  });
1003
1520
  } catch (error) {
1521
+ this.currentAbortController = null;
1004
1522
  this.handleError(
1005
1523
  error instanceof Error ? error : new Error(String(error)),
1006
1524
  newSessionId
@@ -1012,20 +1530,21 @@ var AgnoClient = class extends EventEmitter {
1012
1530
  */
1013
1531
  handleChunk(chunk, currentSessionId, messageContent) {
1014
1532
  const event = chunk.event;
1015
- if (event === RunEvent.RunStarted || event === RunEvent.TeamRunStarted || event === RunEvent.ReasoningStarted || event === RunEvent.TeamReasoningStarted) {
1016
- if (chunk.session_id && (!currentSessionId || currentSessionId !== chunk.session_id)) {
1017
- const sessionData = {
1018
- session_id: chunk.session_id,
1019
- session_name: messageContent,
1020
- created_at: toSafeISOString(chunk.created_at)
1021
- };
1022
- const sessionExists = this.state.sessions.some(
1023
- (s) => s.session_id === chunk.session_id
1024
- );
1025
- if (!sessionExists) {
1026
- this.state.sessions = [sessionData, ...this.state.sessions];
1027
- this.emit("session:created", sessionData);
1028
- }
1533
+ if ((event === RunEvent.RunStarted || event === RunEvent.TeamRunStarted) && chunk.run_id) {
1534
+ this.currentRunId = chunk.run_id;
1535
+ }
1536
+ if ((event === RunEvent.RunStarted || event === RunEvent.TeamRunStarted || event === RunEvent.ReasoningStarted || event === RunEvent.TeamReasoningStarted) && chunk.session_id && (!currentSessionId || currentSessionId !== chunk.session_id)) {
1537
+ const sessionData = {
1538
+ session_id: chunk.session_id,
1539
+ session_name: messageContent,
1540
+ created_at: toSafeISOString(chunk.created_at)
1541
+ };
1542
+ const sessionExists = this.state.sessions.some(
1543
+ (s) => s.session_id === chunk.session_id
1544
+ );
1545
+ if (!sessionExists) {
1546
+ this.state.sessions = [sessionData, ...this.state.sessions];
1547
+ this.emit("session:created", sessionData);
1029
1548
  }
1030
1549
  }
1031
1550
  if (event === RunEvent.RunPaused) {
@@ -1085,6 +1604,73 @@ var AgnoClient = class extends EventEmitter {
1085
1604
  this.emit("stream:end");
1086
1605
  this.emit("state:change", this.getState());
1087
1606
  }
1607
+ trackAttachmentUrls(urls) {
1608
+ for (const url of urls) {
1609
+ this.localAttachmentUrls.add(url);
1610
+ }
1611
+ }
1612
+ collectAttachmentUrls(message) {
1613
+ const imageUrls = message.images?.map((image) => image.url) ?? [];
1614
+ const videoUrls = message.videos?.map((video) => video.url).filter((url) => Boolean(url)) ?? [];
1615
+ const audioUrls = message.audio?.map((audio) => audio.url).filter((url) => Boolean(url)) ?? [];
1616
+ const fileUrls = message.files?.map((file) => file.url).filter((url) => Boolean(url)) ?? [];
1617
+ return [...imageUrls, ...videoUrls, ...audioUrls, ...fileUrls];
1618
+ }
1619
+ revokeAttachmentUrls(urls) {
1620
+ if (typeof URL === "undefined" || typeof URL.revokeObjectURL !== "function") {
1621
+ return;
1622
+ }
1623
+ for (const url of urls) {
1624
+ if (!this.localAttachmentUrls.has(url)) {
1625
+ continue;
1626
+ }
1627
+ URL.revokeObjectURL(url);
1628
+ this.localAttachmentUrls.delete(url);
1629
+ }
1630
+ }
1631
+ revokeAttachmentUrlsFromMessages(messages) {
1632
+ const urls = [];
1633
+ for (const message of messages) {
1634
+ if (!message) {
1635
+ continue;
1636
+ }
1637
+ urls.push(...this.collectAttachmentUrls(message));
1638
+ }
1639
+ this.revokeAttachmentUrls(urls);
1640
+ }
1641
+ collectExistingUIComponents() {
1642
+ const existingUIComponents = /* @__PURE__ */ new Map();
1643
+ for (const message of this.messageStore.getMessages()) {
1644
+ if (!message.tool_calls) {
1645
+ continue;
1646
+ }
1647
+ for (const toolCall of message.tool_calls) {
1648
+ if (toolCall.ui_component) {
1649
+ existingUIComponents.set(
1650
+ toolCall.tool_call_id,
1651
+ toolCall.ui_component
1652
+ );
1653
+ }
1654
+ }
1655
+ }
1656
+ return existingUIComponents;
1657
+ }
1658
+ restoreUIComponents(messages, uiComponents) {
1659
+ if (uiComponents.size === 0) {
1660
+ return;
1661
+ }
1662
+ for (const message of messages) {
1663
+ if (!message.tool_calls) {
1664
+ continue;
1665
+ }
1666
+ for (const toolCall of message.tool_calls) {
1667
+ const uiComponent = uiComponents.get(toolCall.tool_call_id);
1668
+ if (uiComponent) {
1669
+ toolCall.ui_component = uiComponent;
1670
+ }
1671
+ }
1672
+ }
1673
+ }
1088
1674
  /**
1089
1675
  * Refresh messages from the session API after run completion.
1090
1676
  * Replaces streamed messages with authoritative session data.
@@ -1097,19 +1683,14 @@ var AgnoClient = class extends EventEmitter {
1097
1683
  Logger.debug("[AgnoClient] Cannot refresh: no session ID");
1098
1684
  return;
1099
1685
  }
1686
+ if (this.state.isStreaming) {
1687
+ Logger.debug("[AgnoClient] Skipping refresh: stream is active");
1688
+ return;
1689
+ }
1100
1690
  this.state.isRefreshing = true;
1101
1691
  this.emit("state:change", this.getState());
1102
1692
  try {
1103
- const existingUIComponents = /* @__PURE__ */ new Map();
1104
- for (const message of this.messageStore.getMessages()) {
1105
- if (message.tool_calls) {
1106
- for (const toolCall of message.tool_calls) {
1107
- if (toolCall.ui_component) {
1108
- existingUIComponents.set(toolCall.tool_call_id, toolCall.ui_component);
1109
- }
1110
- }
1111
- }
1112
- }
1693
+ const existingUIComponents = this.collectExistingUIComponents();
1113
1694
  const config = this.configManager.getConfig();
1114
1695
  const entityType = this.configManager.getMode();
1115
1696
  const dbId = this.configManager.getDbId() || "";
@@ -1125,27 +1706,28 @@ var AgnoClient = class extends EventEmitter {
1125
1706
  userId,
1126
1707
  params
1127
1708
  );
1128
- const messages = this.sessionManager.convertSessionToMessages(response);
1129
- if (existingUIComponents.size > 0) {
1130
- for (const message of messages) {
1131
- if (message.tool_calls) {
1132
- for (let i = 0; i < message.tool_calls.length; i++) {
1133
- const toolCall = message.tool_calls[i];
1134
- const uiComponent = existingUIComponents.get(toolCall.tool_call_id);
1135
- if (uiComponent) {
1136
- message.tool_calls[i].ui_component = uiComponent;
1137
- }
1138
- }
1139
- }
1140
- }
1709
+ if (this.state.isStreaming) {
1710
+ Logger.debug(
1711
+ "[AgnoClient] Aborting refresh: stream started during fetch"
1712
+ );
1713
+ return;
1141
1714
  }
1715
+ const messages = this.sessionManager.convertSessionToMessages(response);
1716
+ this.restoreUIComponents(messages, existingUIComponents);
1717
+ this.revokeAttachmentUrlsFromMessages(this.messageStore.getMessages());
1142
1718
  this.messageStore.setMessages(messages);
1143
- Logger.debug("[AgnoClient] Session refreshed:", `${messages.length} messages`);
1719
+ Logger.debug(
1720
+ "[AgnoClient] Session refreshed:",
1721
+ `${messages.length} messages`
1722
+ );
1144
1723
  this.emit("message:refreshed", messages);
1145
1724
  this.emit("message:update", messages);
1146
1725
  } catch (error) {
1147
1726
  Logger.error("[AgnoClient] Failed to refresh session:", error);
1148
- this.emit("message:error", `Session refresh failed: ${error instanceof Error ? error.message : String(error)}`);
1727
+ this.emit(
1728
+ "message:error",
1729
+ `Session refresh failed: ${error instanceof Error ? error.message : String(error)}`
1730
+ );
1149
1731
  } finally {
1150
1732
  this.state.isRefreshing = false;
1151
1733
  this.emit("state:change", this.getState());
@@ -1160,7 +1742,11 @@ var AgnoClient = class extends EventEmitter {
1160
1742
  const entityType = this.configManager.getMode();
1161
1743
  const dbId = this.configManager.getDbId() || "";
1162
1744
  const userId = this.configManager.getUserId();
1163
- Logger.debug("[AgnoClient] Loading session with:", { entityType, dbId, userId });
1745
+ Logger.debug("[AgnoClient] Loading session with:", {
1746
+ entityType,
1747
+ dbId,
1748
+ userId
1749
+ });
1164
1750
  const headers = this.configManager.buildRequestHeaders();
1165
1751
  const params = this.configManager.buildQueryString(options?.params);
1166
1752
  const response = await this.sessionManager.fetchSession(
@@ -1173,7 +1759,11 @@ var AgnoClient = class extends EventEmitter {
1173
1759
  params
1174
1760
  );
1175
1761
  const messages = this.sessionManager.convertSessionToMessages(response);
1176
- Logger.debug("[AgnoClient] Setting messages to store:", `${messages.length} messages`);
1762
+ Logger.debug(
1763
+ "[AgnoClient] Setting messages to store:",
1764
+ `${messages.length} messages`
1765
+ );
1766
+ this.revokeAttachmentUrlsFromMessages(this.messageStore.getMessages());
1177
1767
  this.messageStore.setMessages(messages);
1178
1768
  this.configManager.setSessionId(sessionId);
1179
1769
  Logger.debug("[AgnoClient] Emitting events...");
@@ -1242,7 +1832,9 @@ var AgnoClient = class extends EventEmitter {
1242
1832
  }
1243
1833
  const existingToolCalls = lastMessage.tool_calls || [];
1244
1834
  const existingIds = new Set(existingToolCalls.map((t) => t.tool_call_id));
1245
- const newToolCalls = toolCalls.filter((t) => !existingIds.has(t.tool_call_id));
1835
+ const newToolCalls = toolCalls.filter(
1836
+ (t) => !existingIds.has(t.tool_call_id)
1837
+ );
1246
1838
  if (newToolCalls.length > 0) {
1247
1839
  this.messageStore.updateLastMessage((msg) => ({
1248
1840
  ...msg,
@@ -1289,8 +1881,9 @@ var AgnoClient = class extends EventEmitter {
1289
1881
  * Batches all updates to emit only one message:update event
1290
1882
  */
1291
1883
  applyPendingUISpecs() {
1292
- if (this.pendingUISpecs.size === 0)
1884
+ if (this.pendingUISpecs.size === 0) {
1293
1885
  return;
1886
+ }
1294
1887
  const messages = this.messageStore.getMessages();
1295
1888
  const updatedMessages = [];
1296
1889
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -1322,9 +1915,9 @@ var AgnoClient = class extends EventEmitter {
1322
1915
  }
1323
1916
  }
1324
1917
  if (updatedMessages.length > 0) {
1325
- updatedMessages.forEach(({ index, message }) => {
1918
+ for (const { index, message } of updatedMessages) {
1326
1919
  this.messageStore.updateMessage(index, () => message);
1327
- });
1920
+ }
1328
1921
  this.emit("message:update", this.messageStore.getMessages());
1329
1922
  }
1330
1923
  }
@@ -1345,22 +1938,55 @@ var AgnoClient = class extends EventEmitter {
1345
1938
  "HITL (Human-in-the-Loop) frontend tool execution is not supported for teams. Only agents support the continue endpoint."
1346
1939
  );
1347
1940
  }
1348
- if (!this.state.isPaused || !this.state.pausedRunId) {
1941
+ if (!(this.state.isPaused && this.state.pausedRunId)) {
1349
1942
  throw new Error("No paused run to continue");
1350
1943
  }
1351
1944
  const runUrl = this.configManager.getRunUrl();
1352
1945
  if (!runUrl) {
1353
1946
  throw new Error("No agent or team selected");
1354
1947
  }
1355
- const continueUrl = `${runUrl}/${this.state.pausedRunId}/continue`;
1356
- this.state.isPaused = false;
1948
+ const pausedRunId = this.state.pausedRunId;
1949
+ const continueUrl = `${runUrl}/${pausedRunId}/continue`;
1357
1950
  this.state.isStreaming = true;
1358
- this.emit("run:continued", { runId: this.state.pausedRunId });
1951
+ this.state.errorMessage = void 0;
1359
1952
  this.emit("state:change", this.getState());
1360
- const cleanedTools = tools.map((tool) => {
1361
- const { ui_component, ...backendTool } = tool;
1362
- return backendTool;
1363
- });
1953
+ let hasContinued = false;
1954
+ let streamError;
1955
+ const markRunContinued = (runId) => {
1956
+ if (hasContinued) {
1957
+ return;
1958
+ }
1959
+ hasContinued = true;
1960
+ this.state.isPaused = false;
1961
+ this.state.toolsAwaitingExecution = void 0;
1962
+ this.emit("run:continued", { runId: runId || pausedRunId });
1963
+ this.emit("state:change", this.getState());
1964
+ };
1965
+ const handleContinueError = (error) => {
1966
+ streamError = error;
1967
+ this.state.isStreaming = false;
1968
+ this.state.errorMessage = error.message;
1969
+ const isConflictError = error instanceof StreamResponseHttpError && error.status === 409;
1970
+ if (isConflictError || !hasContinued) {
1971
+ this.state.isPaused = true;
1972
+ } else {
1973
+ this.state.isPaused = false;
1974
+ this.state.pausedRunId = void 0;
1975
+ this.state.toolsAwaitingExecution = void 0;
1976
+ this.messageStore.updateLastMessage((msg) => ({
1977
+ ...msg,
1978
+ streamingError: true
1979
+ }));
1980
+ }
1981
+ this.emit("message:error", error.message);
1982
+ this.emit("stream:end");
1983
+ this.emit("state:change", this.getState());
1984
+ };
1985
+ const cleanedTools = tools.map(
1986
+ ({ ui_component: _uiComponent, ...tool }) => {
1987
+ return tool;
1988
+ }
1989
+ );
1364
1990
  const formData = new FormData();
1365
1991
  formData.append("tools", JSON.stringify(cleanedTools));
1366
1992
  formData.append("stream", "true");
@@ -1374,36 +2000,45 @@ var AgnoClient = class extends EventEmitter {
1374
2000
  }
1375
2001
  const headers = this.configManager.buildRequestHeaders(options?.headers);
1376
2002
  const params = this.configManager.buildQueryString(options?.params);
1377
- try {
1378
- await streamResponse({
1379
- apiUrl: continueUrl,
1380
- headers,
1381
- params,
1382
- requestBody: formData,
1383
- onChunk: (chunk) => {
1384
- this.handleChunk(chunk, currentSessionId, "");
1385
- },
1386
- onError: (error) => {
1387
- this.handleError(error, currentSessionId);
1388
- },
1389
- onComplete: async () => {
1390
- this.state.isStreaming = false;
2003
+ this.currentAbortController = new AbortController();
2004
+ await streamResponse({
2005
+ apiUrl: continueUrl,
2006
+ headers,
2007
+ params,
2008
+ requestBody: formData,
2009
+ signal: this.currentAbortController.signal,
2010
+ onChunk: (chunk) => {
2011
+ const event = chunk.event;
2012
+ if (!hasContinued && event !== RunEvent.RunPaused) {
2013
+ markRunContinued(chunk.run_id);
2014
+ }
2015
+ this.handleChunk(chunk, currentSessionId, "");
2016
+ },
2017
+ onError: (error) => {
2018
+ this.currentAbortController = null;
2019
+ handleContinueError(error);
2020
+ },
2021
+ onComplete: async () => {
2022
+ this.currentAbortController = null;
2023
+ if (!(hasContinued || this.state.isPaused)) {
2024
+ markRunContinued(pausedRunId);
2025
+ }
2026
+ this.state.isStreaming = false;
2027
+ if (!this.state.isPaused) {
1391
2028
  this.state.pausedRunId = void 0;
1392
2029
  this.state.toolsAwaitingExecution = void 0;
1393
- this.emit("stream:end");
1394
- this.emit("message:complete", this.messageStore.getMessages());
1395
- this.emit("state:change", this.getState());
1396
- if (this.runCompletedSuccessfully) {
1397
- this.runCompletedSuccessfully = false;
1398
- await this.refreshSessionMessages();
1399
- }
1400
2030
  }
1401
- });
1402
- } catch (error) {
1403
- this.handleError(
1404
- error instanceof Error ? error : new Error(String(error)),
1405
- currentSessionId
1406
- );
2031
+ this.emit("stream:end");
2032
+ this.emit("message:complete", this.messageStore.getMessages());
2033
+ this.emit("state:change", this.getState());
2034
+ if (this.runCompletedSuccessfully) {
2035
+ this.runCompletedSuccessfully = false;
2036
+ await this.refreshSessionMessages();
2037
+ }
2038
+ }
2039
+ });
2040
+ if (streamError) {
2041
+ throw streamError;
1407
2042
  }
1408
2043
  }
1409
2044
  /**
@@ -1488,7 +2123,7 @@ var AgnoClient = class extends EventEmitter {
1488
2123
  const currentConfig = this.configManager.getConfig();
1489
2124
  const hasAgentConfigured = currentConfig.agentId;
1490
2125
  const hasTeamConfigured = currentConfig.teamId;
1491
- if (!hasAgentConfigured && !hasTeamConfigured) {
2126
+ if (!(hasAgentConfigured || hasTeamConfigured)) {
1492
2127
  if (agents.length > 0) {
1493
2128
  const firstAgent = agents[0];
1494
2129
  this.configManager.updateConfig({
@@ -1509,10 +2144,34 @@ var AgnoClient = class extends EventEmitter {
1509
2144
  }
1510
2145
  return { agents, teams };
1511
2146
  }
2147
+ /**
2148
+ * Dispose of the client and clean up all resources.
2149
+ * Call this method when the client is no longer needed to prevent memory leaks.
2150
+ * After calling dispose(), the client instance should not be reused.
2151
+ */
2152
+ dispose() {
2153
+ if (this.currentAbortController) {
2154
+ this.currentAbortController.abort();
2155
+ this.currentAbortController = null;
2156
+ }
2157
+ this.removeAllListeners();
2158
+ this.revokeAttachmentUrlsFromMessages(this.messageStore.getMessages());
2159
+ this.localAttachmentUrls.clear();
2160
+ this.messageStore.clear();
2161
+ this.pendingUISpecs.clear();
2162
+ this.eventProcessor.reset();
2163
+ this.state.isStreaming = false;
2164
+ this.state.isRefreshing = false;
2165
+ this.state.isEndpointActive = false;
2166
+ this.state.agents = [];
2167
+ this.state.teams = [];
2168
+ this.state.sessions = [];
2169
+ this.state.isPaused = false;
2170
+ this.state.pausedRunId = void 0;
2171
+ this.state.toolsAwaitingExecution = void 0;
2172
+ this.state.errorMessage = void 0;
2173
+ }
1512
2174
  };
1513
-
1514
- // src/index.ts
1515
- import { RunEvent as RunEvent2 } from "@antipopp/agno-types";
1516
2175
  export {
1517
2176
  AgnoClient,
1518
2177
  Logger,