@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.js CHANGED
@@ -35,89 +35,11 @@ __export(src_exports, {
35
35
  RunEvent: () => import_agno_types3.RunEvent
36
36
  });
37
37
  module.exports = __toCommonJS(src_exports);
38
+ var import_agno_types3 = require("@antipopp/agno-types");
38
39
 
39
40
  // src/client.ts
40
- var import_eventemitter3 = __toESM(require("eventemitter3"));
41
41
  var import_agno_types2 = require("@antipopp/agno-types");
42
-
43
- // src/stores/message-store.ts
44
- var MessageStore = class {
45
- constructor() {
46
- this.messages = [];
47
- }
48
- /**
49
- * Get all messages
50
- */
51
- getMessages() {
52
- return [...this.messages];
53
- }
54
- /**
55
- * Set messages (replaces all)
56
- */
57
- setMessages(messages) {
58
- this.messages = [...messages];
59
- }
60
- /**
61
- * Add a message
62
- */
63
- addMessage(message) {
64
- this.messages = [...this.messages, message];
65
- }
66
- /**
67
- * Update the last message
68
- */
69
- updateLastMessage(updater) {
70
- if (this.messages.length === 0)
71
- return void 0;
72
- const lastMessage = this.messages[this.messages.length - 1];
73
- const updatedMessage = updater(lastMessage);
74
- this.messages = [
75
- ...this.messages.slice(0, -1),
76
- updatedMessage
77
- ];
78
- return updatedMessage;
79
- }
80
- /**
81
- * Update a specific message by index
82
- */
83
- updateMessage(index, updater) {
84
- if (index < 0 || index >= this.messages.length)
85
- return void 0;
86
- const message = this.messages[index];
87
- const updatedMessage = updater(message);
88
- this.messages = [
89
- ...this.messages.slice(0, index),
90
- updatedMessage,
91
- ...this.messages.slice(index + 1)
92
- ];
93
- return updatedMessage;
94
- }
95
- /**
96
- * Remove last N messages
97
- */
98
- removeLastMessages(count) {
99
- this.messages = this.messages.slice(0, -count);
100
- }
101
- /**
102
- * Clear all messages
103
- */
104
- clear() {
105
- this.messages = [];
106
- }
107
- /**
108
- * Get the last message
109
- */
110
- getLastMessage() {
111
- return this.messages.length > 0 ? this.messages[this.messages.length - 1] : void 0;
112
- }
113
- /**
114
- * Check if last message has streaming error
115
- */
116
- hasLastMessageError() {
117
- const lastMessage = this.getLastMessage();
118
- return lastMessage?.streamingError === true;
119
- }
120
- };
42
+ var import_eventemitter3 = __toESM(require("eventemitter3"));
121
43
 
122
44
  // src/managers/config-manager.ts
123
45
  var ConfigManager = class {
@@ -262,6 +184,18 @@ var ConfigManager = class {
262
184
  setParams(params) {
263
185
  this.updateField("params", params);
264
186
  }
187
+ /**
188
+ * Get global dependencies
189
+ */
190
+ getDependencies() {
191
+ return this.config.dependencies;
192
+ }
193
+ /**
194
+ * Set global dependencies
195
+ */
196
+ setDependencies(dependencies) {
197
+ this.updateField("dependencies", dependencies);
198
+ }
265
199
  /**
266
200
  * Get current entity ID (agent or team based on mode)
267
201
  */
@@ -275,14 +209,14 @@ var ConfigManager = class {
275
209
  const mode = this.getMode();
276
210
  const endpoint = this.getEndpoint();
277
211
  const entityId = this.getCurrentEntityId();
278
- if (!entityId)
212
+ if (!entityId) {
279
213
  return null;
214
+ }
280
215
  const encodedEntityId = encodeURIComponent(entityId);
281
216
  if (mode === "team") {
282
217
  return `${endpoint}/teams/${encodedEntityId}/runs`;
283
- } else {
284
- return `${endpoint}/agents/${encodedEntityId}/runs`;
285
218
  }
219
+ return `${endpoint}/agents/${encodedEntityId}/runs`;
286
220
  }
287
221
  /**
288
222
  * Build request headers by merging global headers, per-request headers, and auth token.
@@ -305,7 +239,7 @@ var ConfigManager = class {
305
239
  }
306
240
  const authToken = this.getAuthToken();
307
241
  if (authToken) {
308
- headers["Authorization"] = `Bearer ${authToken}`;
242
+ headers.Authorization = `Bearer ${authToken}`;
309
243
  }
310
244
  return headers;
311
245
  }
@@ -329,6 +263,29 @@ var ConfigManager = class {
329
263
  }
330
264
  return new URLSearchParams(params);
331
265
  }
266
+ /**
267
+ * Build dependencies by merging global dependencies and per-request dependencies.
268
+ * Merge order (lowest to highest precedence):
269
+ * 1. Global dependencies from config
270
+ * 2. Per-request dependencies (overrides global)
271
+ *
272
+ * @param perRequestDependencies - Optional dependencies for this specific request
273
+ * @returns Merged dependencies object, or undefined if no dependencies are configured
274
+ */
275
+ buildDependencies(perRequestDependencies) {
276
+ const dependencies = {};
277
+ const globalDependencies = this.getDependencies();
278
+ if (globalDependencies) {
279
+ Object.assign(dependencies, globalDependencies);
280
+ }
281
+ if (perRequestDependencies) {
282
+ Object.assign(dependencies, perRequestDependencies);
283
+ }
284
+ if (Object.keys(dependencies).length === 0) {
285
+ return void 0;
286
+ }
287
+ return dependencies;
288
+ }
332
289
  };
333
290
 
334
291
  // src/managers/session-manager.ts
@@ -415,75 +372,470 @@ var SessionManager = class {
415
372
  convertRunsToMessages(runs) {
416
373
  const messages = [];
417
374
  for (const run of runs) {
418
- const timestamp = run.created_at ? new Date(run.created_at).getTime() / 1e3 : Math.floor(Date.now() / 1e3);
419
- if (run.run_input) {
420
- messages.push({
421
- role: "user",
422
- content: run.run_input,
423
- created_at: timestamp
424
- });
425
- }
426
- const toolCalls = [];
427
- if (run.tools && Array.isArray(run.tools)) {
428
- for (const tool of run.tools) {
429
- const toolObj = tool;
430
- const toolCall = {
431
- role: "tool",
432
- content: toolObj.content ?? "",
433
- tool_call_id: toolObj.tool_call_id ?? "",
434
- tool_name: toolObj.tool_name ?? "",
435
- tool_args: toolObj.tool_args ?? {},
436
- tool_call_error: toolObj.tool_call_error ?? false,
437
- metrics: toolObj.metrics ?? { time: 0 },
438
- created_at: timestamp
439
- };
440
- toolCalls.push(toolCall);
441
- }
442
- }
443
- if (run.reasoning_messages && Array.isArray(run.reasoning_messages)) {
444
- for (const msg of run.reasoning_messages) {
445
- const reasoningMsg = msg;
446
- if (reasoningMsg.role === "tool") {
447
- toolCalls.push({
448
- role: "tool",
449
- content: reasoningMsg.content ?? "",
450
- tool_call_id: reasoningMsg.tool_call_id ?? "",
451
- tool_name: reasoningMsg.tool_name ?? "",
452
- tool_args: reasoningMsg.tool_args ?? {},
453
- tool_call_error: reasoningMsg.tool_call_error ?? false,
454
- metrics: reasoningMsg.metrics ?? { time: 0 },
455
- created_at: reasoningMsg.created_at ?? timestamp
456
- });
457
- }
458
- }
459
- }
460
- let contentStr = "";
461
- if (typeof run.content === "string") {
462
- contentStr = run.content;
463
- } else if (run.content && typeof run.content === "object") {
464
- contentStr = JSON.stringify(run.content);
375
+ const timestamp = this.getRunTimestamp(run);
376
+ const inputMedia = this.mergeInputMedia(
377
+ this.extractInputMedia(run.input_media),
378
+ this.extractInputMediaFromMessages(run.messages)
379
+ );
380
+ if (run.run_input || this.hasAnyMedia(inputMedia)) {
381
+ messages.push(
382
+ this.buildUserMessage(run.run_input ?? "", timestamp, inputMedia)
383
+ );
465
384
  }
466
- const extraData = run.reasoning_messages || run.reasoning_steps || run.references ? {
467
- reasoning_messages: run.reasoning_messages,
468
- reasoning_steps: run.reasoning_steps,
469
- references: run.references
470
- } : void 0;
385
+ const toolCalls = this.extractToolCalls(run, timestamp);
471
386
  messages.push({
472
387
  role: "agent",
473
- content: contentStr,
388
+ content: this.normalizeRunContent(run.content),
474
389
  tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
475
- extra_data: extraData,
476
- images: run.images,
477
- videos: run.videos,
478
- audio: run.audio,
479
- response_audio: run.response_audio,
390
+ extra_data: this.buildExtraData(run),
391
+ images: this.normalizeImages(run.images),
392
+ videos: this.normalizeVideos(run.videos),
393
+ audio: this.normalizeAudio(run.audio),
394
+ files: this.normalizeFiles(run.files),
395
+ response_audio: run.response_audio ?? void 0,
480
396
  created_at: timestamp + 1
481
397
  // Agent response is slightly after user message
482
398
  });
483
399
  }
484
400
  return messages;
485
401
  }
402
+ getRunTimestamp(run) {
403
+ return run.created_at ? new Date(run.created_at).getTime() / 1e3 : Math.floor(Date.now() / 1e3);
404
+ }
405
+ buildUserMessage(content, createdAt, media) {
406
+ return {
407
+ role: "user",
408
+ content,
409
+ images: media?.images,
410
+ videos: media?.videos,
411
+ audio: media?.audio,
412
+ files: media?.files,
413
+ created_at: createdAt
414
+ };
415
+ }
416
+ hasAnyMedia(media) {
417
+ return Boolean(
418
+ media.images && media.images.length > 0 || media.videos && media.videos.length > 0 || media.audio && media.audio.length > 0 || media.files && media.files.length > 0
419
+ );
420
+ }
421
+ isRecord(value) {
422
+ return typeof value === "object" && value !== null;
423
+ }
424
+ getStringValue(record, key) {
425
+ const value = record[key];
426
+ return typeof value === "string" && value.length > 0 ? value : void 0;
427
+ }
428
+ getNumberOrStringValue(record, key) {
429
+ const value = record[key];
430
+ return typeof value === "number" || typeof value === "string" ? value : void 0;
431
+ }
432
+ buildDataUrl(content, mimeType, fallbackMimeType) {
433
+ const safeMimeType = mimeType || fallbackMimeType;
434
+ return `data:${safeMimeType};base64,${content}`;
435
+ }
436
+ normalizeImage(item) {
437
+ if (!this.isRecord(item)) {
438
+ return void 0;
439
+ }
440
+ const mimeType = this.getStringValue(item, "mime_type");
441
+ const content = this.getStringValue(item, "content");
442
+ const url = this.getStringValue(item, "url") || (content ? this.buildDataUrl(content, mimeType, "image/png") : void 0);
443
+ if (!url) {
444
+ return void 0;
445
+ }
446
+ return {
447
+ url,
448
+ revised_prompt: this.getStringValue(item, "revised_prompt"),
449
+ original_prompt: this.getStringValue(item, "original_prompt"),
450
+ alt_text: this.getStringValue(item, "alt_text"),
451
+ id: this.getStringValue(item, "id"),
452
+ mime_type: mimeType,
453
+ format: this.getStringValue(item, "format")
454
+ };
455
+ }
456
+ normalizeVideo(item) {
457
+ if (!this.isRecord(item)) {
458
+ return void 0;
459
+ }
460
+ const mimeType = this.getStringValue(item, "mime_type");
461
+ const content = this.getStringValue(item, "content");
462
+ const url = this.getStringValue(item, "url") || (content ? this.buildDataUrl(content, mimeType, "video/mp4") : void 0);
463
+ const normalized = {
464
+ url,
465
+ id: this.getNumberOrStringValue(item, "id"),
466
+ eta: this.getNumberOrStringValue(item, "eta"),
467
+ mime_type: mimeType,
468
+ format: this.getStringValue(item, "format"),
469
+ original_prompt: this.getStringValue(item, "original_prompt"),
470
+ revised_prompt: this.getStringValue(item, "revised_prompt"),
471
+ width: typeof item.width === "number" ? item.width : void 0,
472
+ height: typeof item.height === "number" ? item.height : void 0,
473
+ fps: typeof item.fps === "number" ? item.fps : void 0,
474
+ duration: typeof item.duration === "number" ? item.duration : void 0
475
+ };
476
+ if (normalized.url || normalized.id !== void 0 || normalized.eta !== void 0) {
477
+ return normalized;
478
+ }
479
+ return void 0;
480
+ }
481
+ normalizeAudioEntry(item) {
482
+ if (!this.isRecord(item)) {
483
+ return void 0;
484
+ }
485
+ const mimeType = this.getStringValue(item, "mime_type");
486
+ const content = this.getStringValue(item, "content");
487
+ const url = this.getStringValue(item, "url") || (content ? this.buildDataUrl(content, mimeType, "audio/wav") : void 0);
488
+ const normalized = {
489
+ base64_audio: this.getStringValue(item, "base64_audio"),
490
+ mime_type: mimeType,
491
+ url,
492
+ id: this.getNumberOrStringValue(item, "id"),
493
+ content,
494
+ channels: typeof item.channels === "number" ? item.channels : void 0,
495
+ sample_rate: typeof item.sample_rate === "number" ? item.sample_rate : void 0,
496
+ format: this.getStringValue(item, "format"),
497
+ duration: typeof item.duration === "number" ? item.duration : void 0
498
+ };
499
+ if (normalized.url || normalized.base64_audio || normalized.content || normalized.id !== void 0) {
500
+ return normalized;
501
+ }
502
+ return void 0;
503
+ }
504
+ normalizeFileEntry(item) {
505
+ if (!this.isRecord(item)) {
506
+ return void 0;
507
+ }
508
+ const normalized = {
509
+ id: this.getStringValue(item, "id"),
510
+ url: this.getStringValue(item, "url"),
511
+ filename: this.getStringValue(item, "filename"),
512
+ name: this.getStringValue(item, "name"),
513
+ mime_type: this.getStringValue(item, "mime_type"),
514
+ format: this.getStringValue(item, "format"),
515
+ size: typeof item.size === "number" ? item.size : void 0
516
+ };
517
+ if (normalized.url || normalized.filename || normalized.name || normalized.id) {
518
+ return normalized;
519
+ }
520
+ return void 0;
521
+ }
522
+ normalizeArray(value, normalizer) {
523
+ if (!Array.isArray(value)) {
524
+ return void 0;
525
+ }
526
+ const items = value.map((item) => normalizer(item)).filter((item) => item !== void 0);
527
+ return items.length > 0 ? items : void 0;
528
+ }
529
+ normalizeImages(value) {
530
+ return this.normalizeArray(value, (item) => this.normalizeImage(item));
531
+ }
532
+ normalizeVideos(value) {
533
+ return this.normalizeArray(value, (item) => this.normalizeVideo(item));
534
+ }
535
+ normalizeAudio(value) {
536
+ return this.normalizeArray(value, (item) => this.normalizeAudioEntry(item));
537
+ }
538
+ normalizeFiles(value) {
539
+ return this.normalizeArray(value, (item) => this.normalizeFileEntry(item));
540
+ }
541
+ extractInputMedia(inputMedia) {
542
+ if (!this.isRecord(inputMedia)) {
543
+ return {};
544
+ }
545
+ return {
546
+ images: this.normalizeImages(inputMedia.images),
547
+ videos: this.normalizeVideos(inputMedia.videos),
548
+ audio: this.normalizeAudio(inputMedia.audios ?? inputMedia.audio),
549
+ files: this.normalizeFiles(inputMedia.files)
550
+ };
551
+ }
552
+ extractInputMediaFromMessages(messages) {
553
+ if (!Array.isArray(messages)) {
554
+ return {};
555
+ }
556
+ for (const message of messages) {
557
+ if (!this.isRecord(message)) {
558
+ continue;
559
+ }
560
+ if (this.getStringValue(message, "role") !== "user") {
561
+ continue;
562
+ }
563
+ const media = {
564
+ images: this.normalizeImages(message.images ?? message.image),
565
+ videos: this.normalizeVideos(message.videos ?? message.video),
566
+ audio: this.normalizeAudio(message.audios ?? message.audio),
567
+ files: this.normalizeFiles(message.files)
568
+ };
569
+ if (this.hasAnyMedia(media)) {
570
+ return media;
571
+ }
572
+ }
573
+ return {};
574
+ }
575
+ mergeInputMedia(primary, fallback) {
576
+ return {
577
+ images: primary.images ?? fallback.images,
578
+ videos: primary.videos ?? fallback.videos,
579
+ audio: primary.audio ?? fallback.audio,
580
+ files: primary.files ?? fallback.files
581
+ };
582
+ }
583
+ normalizeRunContent(content) {
584
+ if (typeof content === "string") {
585
+ return content;
586
+ }
587
+ if (content && typeof content === "object") {
588
+ return JSON.stringify(content);
589
+ }
590
+ return "";
591
+ }
592
+ buildToolCall(rawTool, fallbackTimestamp) {
593
+ return {
594
+ role: "tool",
595
+ content: rawTool.content ?? "",
596
+ tool_call_id: rawTool.tool_call_id ?? "",
597
+ tool_name: rawTool.tool_name ?? "",
598
+ tool_args: rawTool.tool_args ?? {},
599
+ tool_call_error: rawTool.tool_call_error ?? false,
600
+ metrics: rawTool.metrics ?? { time: 0 },
601
+ created_at: rawTool.created_at ?? fallbackTimestamp
602
+ };
603
+ }
604
+ extractToolCalls(run, timestamp) {
605
+ const toolCalls = [];
606
+ if (run.tools && Array.isArray(run.tools)) {
607
+ for (const tool of run.tools) {
608
+ toolCalls.push(this.buildToolCall(tool, timestamp));
609
+ }
610
+ }
611
+ if (run.reasoning_messages && Array.isArray(run.reasoning_messages)) {
612
+ for (const message of run.reasoning_messages) {
613
+ if (message.role === "tool") {
614
+ toolCalls.push(this.buildToolCall(message, timestamp));
615
+ }
616
+ }
617
+ }
618
+ return toolCalls;
619
+ }
620
+ buildExtraData(run) {
621
+ if (!(run.reasoning_messages || run.reasoning_steps || run.references)) {
622
+ return void 0;
623
+ }
624
+ return {
625
+ reasoning_messages: run.reasoning_messages,
626
+ reasoning_steps: run.reasoning_steps,
627
+ references: run.references
628
+ };
629
+ }
630
+ };
631
+
632
+ // src/parsers/stream-parser.ts
633
+ var StreamResponseHttpError = class extends Error {
634
+ constructor(status, message) {
635
+ super(message);
636
+ this.name = "StreamResponseHttpError";
637
+ this.status = status;
638
+ }
486
639
  };
640
+ function isLegacyFormat(data) {
641
+ return typeof data === "object" && data !== null && "event" in data && !("data" in data) && typeof data.event === "string";
642
+ }
643
+ function convertNewFormatToLegacy(newFormatData) {
644
+ const { event, data } = newFormatData;
645
+ let parsedData;
646
+ if (typeof data === "string") {
647
+ try {
648
+ parsedData = JSON.parse(data);
649
+ } catch {
650
+ parsedData = {};
651
+ }
652
+ } else {
653
+ parsedData = data;
654
+ }
655
+ return {
656
+ event,
657
+ ...parsedData
658
+ };
659
+ }
660
+ function processChunk(chunk, onChunk) {
661
+ onChunk(chunk);
662
+ }
663
+ function updateJsonParserState(char, state) {
664
+ if (state.inString) {
665
+ if (state.escapeNext) {
666
+ state.escapeNext = false;
667
+ return false;
668
+ }
669
+ if (char === "\\") {
670
+ state.escapeNext = true;
671
+ return false;
672
+ }
673
+ if (char === '"') {
674
+ state.inString = false;
675
+ }
676
+ return false;
677
+ }
678
+ if (char === '"') {
679
+ state.inString = true;
680
+ return false;
681
+ }
682
+ if (char === "{") {
683
+ state.braceCount++;
684
+ return false;
685
+ }
686
+ if (char !== "}") {
687
+ return false;
688
+ }
689
+ state.braceCount--;
690
+ return state.braceCount === 0;
691
+ }
692
+ function findJsonSlice(buffer, fromIndex) {
693
+ const startIndex = buffer.indexOf("{", fromIndex);
694
+ if (startIndex === -1) {
695
+ return void 0;
696
+ }
697
+ const parserState = {
698
+ braceCount: 0,
699
+ inString: false,
700
+ escapeNext: false
701
+ };
702
+ for (let i = startIndex; i < buffer.length; i++) {
703
+ const isJsonComplete = updateJsonParserState(buffer[i], parserState);
704
+ if (isJsonComplete) {
705
+ return { startIndex, endIndex: i };
706
+ }
707
+ }
708
+ return void 0;
709
+ }
710
+ function logChunkParseError(error, jsonString, position) {
711
+ if (typeof process === "undefined" || process.env?.NODE_ENV !== "development") {
712
+ return;
713
+ }
714
+ console.error("Failed to parse JSON chunk:", {
715
+ error,
716
+ chunk: jsonString.substring(0, 100) + (jsonString.length > 100 ? "..." : ""),
717
+ position
718
+ });
719
+ }
720
+ function processJsonSlice(jsonString, startIndex, onChunk) {
721
+ try {
722
+ const parsed = JSON.parse(jsonString);
723
+ if (isLegacyFormat(parsed)) {
724
+ processChunk(parsed, onChunk);
725
+ } else {
726
+ processChunk(convertNewFormatToLegacy(parsed), onChunk);
727
+ }
728
+ return true;
729
+ } catch (error) {
730
+ logChunkParseError(error, jsonString, startIndex);
731
+ if (jsonString.length > 1e4) {
732
+ throw new Error(
733
+ `Failed to parse large JSON chunk at position ${startIndex}`
734
+ );
735
+ }
736
+ return false;
737
+ }
738
+ }
739
+ function parseBuffer(buffer, onChunk) {
740
+ let searchIndex = 0;
741
+ let remainingBuffer = buffer;
742
+ while (true) {
743
+ const jsonSlice = findJsonSlice(remainingBuffer, searchIndex);
744
+ if (!jsonSlice) {
745
+ break;
746
+ }
747
+ const jsonString = remainingBuffer.slice(
748
+ jsonSlice.startIndex,
749
+ jsonSlice.endIndex + 1
750
+ );
751
+ const parsed = processJsonSlice(jsonString, jsonSlice.startIndex, onChunk);
752
+ if (!parsed) {
753
+ searchIndex = jsonSlice.startIndex + 1;
754
+ continue;
755
+ }
756
+ remainingBuffer = remainingBuffer.slice(jsonSlice.endIndex + 1).trim();
757
+ searchIndex = 0;
758
+ }
759
+ return remainingBuffer;
760
+ }
761
+ function buildRequestHeaders(headers, requestBody) {
762
+ return {
763
+ ...!(requestBody instanceof FormData) && {
764
+ "Content-Type": "application/json"
765
+ },
766
+ ...headers
767
+ };
768
+ }
769
+ async function buildErrorMessage(response) {
770
+ const defaultMessage = `HTTP ${response.status}: ${response.statusText}`;
771
+ const contentType = response.headers.get("content-type");
772
+ if (!contentType?.includes("application/json")) {
773
+ return defaultMessage;
774
+ }
775
+ try {
776
+ const errorData = await response.json();
777
+ return errorData.detail || errorData.message || defaultMessage;
778
+ } catch {
779
+ return defaultMessage;
780
+ }
781
+ }
782
+ async function consumeResponseStream(body, onChunk, onComplete) {
783
+ const reader = body.getReader();
784
+ const decoder = new TextDecoder();
785
+ let buffer = "";
786
+ while (true) {
787
+ const { done, value } = await reader.read();
788
+ if (done) {
789
+ parseBuffer(buffer, onChunk);
790
+ onComplete();
791
+ return;
792
+ }
793
+ buffer += decoder.decode(value, { stream: true });
794
+ buffer = parseBuffer(buffer, onChunk);
795
+ }
796
+ }
797
+ async function streamResponse(options) {
798
+ const {
799
+ apiUrl,
800
+ headers = {},
801
+ params,
802
+ requestBody,
803
+ onChunk,
804
+ onError,
805
+ onComplete,
806
+ signal
807
+ } = options;
808
+ const finalUrl = params?.toString() ? `${apiUrl}?${params.toString()}` : apiUrl;
809
+ try {
810
+ const response = await fetch(finalUrl, {
811
+ method: "POST",
812
+ headers: buildRequestHeaders(headers, requestBody),
813
+ body: requestBody instanceof FormData ? requestBody : JSON.stringify(requestBody),
814
+ signal
815
+ });
816
+ if (!response.ok) {
817
+ const errorMessage = await buildErrorMessage(response);
818
+ throw new StreamResponseHttpError(response.status, errorMessage);
819
+ }
820
+ if (!response.body) {
821
+ throw new Error("No response body");
822
+ }
823
+ await consumeResponseStream(response.body, onChunk, onComplete);
824
+ } catch (error) {
825
+ if (error instanceof Error && error.name === "AbortError") {
826
+ return;
827
+ }
828
+ if (error instanceof Error) {
829
+ onError(error);
830
+ return;
831
+ }
832
+ if (typeof error === "object" && error !== null && "detail" in error) {
833
+ onError(new Error(String(error.detail)));
834
+ } else {
835
+ onError(new Error(String(error)));
836
+ }
837
+ }
838
+ }
487
839
 
488
840
  // src/processors/event-processor.ts
489
841
  var import_agno_types = require("@antipopp/agno-types");
@@ -508,14 +860,18 @@ function processToolCall(toolCall, prevToolCalls = []) {
508
860
  );
509
861
  if (existingToolCallIndex >= 0) {
510
862
  const updatedToolCalls = [...prevToolCalls];
511
- updatedToolCalls[existingToolCallIndex] = {
512
- ...updatedToolCalls[existingToolCallIndex],
513
- ...toolCall
514
- };
863
+ const existing = updatedToolCalls[existingToolCallIndex];
864
+ const merged = { ...existing, ...toolCall };
865
+ if (existing.external_execution && existing.result !== void 0) {
866
+ merged.result = existing.result;
867
+ }
868
+ if (existing.ui_component !== void 0 && !toolCall.ui_component) {
869
+ merged.ui_component = existing.ui_component;
870
+ }
871
+ updatedToolCalls[existingToolCallIndex] = merged;
515
872
  return updatedToolCalls;
516
- } else {
517
- return [...prevToolCalls, toolCall];
518
873
  }
874
+ return [...prevToolCalls, toolCall];
519
875
  }
520
876
  function processChunkToolCalls(chunk, existingToolCalls = []) {
521
877
  let updatedToolCalls = [...existingToolCalls];
@@ -533,6 +889,105 @@ var EventProcessor = class {
533
889
  constructor() {
534
890
  this.lastContent = "";
535
891
  }
892
+ appendRunContent(chunk, updatedMessage) {
893
+ if (typeof chunk.content === "string") {
894
+ const uniqueContent = chunk.content.replace(this.lastContent, "");
895
+ updatedMessage.content = updatedMessage.content + uniqueContent;
896
+ this.lastContent = chunk.content;
897
+ return;
898
+ }
899
+ if (typeof chunk.content !== "string" && chunk.content !== null) {
900
+ const jsonBlock = getJsonMarkdown(chunk.content);
901
+ updatedMessage.content = updatedMessage.content + jsonBlock;
902
+ this.lastContent = jsonBlock;
903
+ }
904
+ }
905
+ applyRunContentFields(chunk, lastMessage, updatedMessage) {
906
+ this.appendRunContent(chunk, updatedMessage);
907
+ updatedMessage.tool_calls = processChunkToolCalls(
908
+ chunk,
909
+ lastMessage.tool_calls
910
+ );
911
+ if (chunk.extra_data?.reasoning_steps) {
912
+ updatedMessage.extra_data = {
913
+ ...updatedMessage.extra_data,
914
+ reasoning_steps: chunk.extra_data.reasoning_steps
915
+ };
916
+ }
917
+ if (chunk.extra_data?.references) {
918
+ updatedMessage.extra_data = {
919
+ ...updatedMessage.extra_data,
920
+ references: chunk.extra_data.references
921
+ };
922
+ }
923
+ updatedMessage.created_at = chunk.created_at ?? lastMessage.created_at;
924
+ if (chunk.images) {
925
+ updatedMessage.images = chunk.images;
926
+ }
927
+ if (chunk.image) {
928
+ const existingImages = updatedMessage.images ?? lastMessage.images ?? [];
929
+ const hasImage = existingImages.some((image) => {
930
+ if (image.id && chunk.image?.id) {
931
+ return image.id === chunk.image.id;
932
+ }
933
+ return image.url === chunk.image?.url;
934
+ });
935
+ if (!hasImage) {
936
+ updatedMessage.images = [...existingImages, chunk.image];
937
+ }
938
+ }
939
+ if (chunk.videos) {
940
+ updatedMessage.videos = chunk.videos;
941
+ }
942
+ if (chunk.audio) {
943
+ updatedMessage.audio = chunk.audio;
944
+ }
945
+ if (chunk.files) {
946
+ updatedMessage.files = chunk.files;
947
+ }
948
+ if (chunk.response_audio?.transcript && typeof chunk.response_audio.transcript === "string") {
949
+ updatedMessage.response_audio = {
950
+ ...updatedMessage.response_audio,
951
+ transcript: (updatedMessage.response_audio?.transcript || "") + chunk.response_audio.transcript
952
+ };
953
+ }
954
+ }
955
+ applyCompletedFields(chunk, lastMessage, updatedMessage) {
956
+ let updatedContent;
957
+ if (typeof chunk.content === "string") {
958
+ updatedContent = chunk.content;
959
+ } else {
960
+ try {
961
+ updatedContent = JSON.stringify(chunk.content);
962
+ } catch {
963
+ updatedContent = "Error parsing response";
964
+ }
965
+ }
966
+ updatedMessage.content = updatedContent;
967
+ updatedMessage.tool_calls = processChunkToolCalls(
968
+ chunk,
969
+ lastMessage.tool_calls
970
+ );
971
+ const completedImages = chunk.images ?? (chunk.image ? [chunk.image] : void 0);
972
+ updatedMessage.images = completedImages ?? lastMessage.images;
973
+ updatedMessage.videos = chunk.videos ?? lastMessage.videos;
974
+ updatedMessage.audio = chunk.audio ?? lastMessage.audio;
975
+ updatedMessage.files = chunk.files ?? lastMessage.files;
976
+ updatedMessage.response_audio = chunk.response_audio;
977
+ updatedMessage.created_at = chunk.created_at ?? lastMessage.created_at;
978
+ updatedMessage.extra_data = {
979
+ reasoning_steps: chunk.extra_data?.reasoning_steps ?? lastMessage.extra_data?.reasoning_steps,
980
+ references: chunk.extra_data?.references ?? lastMessage.extra_data?.references
981
+ };
982
+ }
983
+ appendReasoningSteps(chunk, lastMessage, updatedMessage) {
984
+ const existingSteps = lastMessage.extra_data?.reasoning_steps ?? [];
985
+ const incomingSteps = chunk.extra_data?.reasoning_steps ?? [];
986
+ updatedMessage.extra_data = {
987
+ ...updatedMessage.extra_data,
988
+ reasoning_steps: [...existingSteps, ...incomingSteps]
989
+ };
990
+ }
536
991
  /**
537
992
  * Process a chunk and update the last message
538
993
  */
@@ -559,58 +1014,13 @@ var EventProcessor = class {
559
1014
  break;
560
1015
  case import_agno_types.RunEvent.RunContent:
561
1016
  case import_agno_types.RunEvent.TeamRunContent:
562
- if (typeof chunk.content === "string") {
563
- const uniqueContent = chunk.content.replace(this.lastContent, "");
564
- updatedMessage.content = updatedMessage.content + uniqueContent;
565
- this.lastContent = chunk.content;
566
- } else if (typeof chunk.content !== "string" && chunk.content !== null) {
567
- const jsonBlock = getJsonMarkdown(chunk.content);
568
- updatedMessage.content = updatedMessage.content + jsonBlock;
569
- this.lastContent = jsonBlock;
570
- }
571
- updatedMessage.tool_calls = processChunkToolCalls(
572
- chunk,
573
- lastMessage.tool_calls
574
- );
575
- if (chunk.extra_data?.reasoning_steps) {
576
- updatedMessage.extra_data = {
577
- ...updatedMessage.extra_data,
578
- reasoning_steps: chunk.extra_data.reasoning_steps
579
- };
580
- }
581
- if (chunk.extra_data?.references) {
582
- updatedMessage.extra_data = {
583
- ...updatedMessage.extra_data,
584
- references: chunk.extra_data.references
585
- };
586
- }
587
- updatedMessage.created_at = chunk.created_at ?? lastMessage.created_at;
588
- if (chunk.images) {
589
- updatedMessage.images = chunk.images;
590
- }
591
- if (chunk.videos) {
592
- updatedMessage.videos = chunk.videos;
593
- }
594
- if (chunk.audio) {
595
- updatedMessage.audio = chunk.audio;
596
- }
597
- if (chunk.response_audio?.transcript && typeof chunk.response_audio.transcript === "string") {
598
- const transcript = chunk.response_audio.transcript;
599
- updatedMessage.response_audio = {
600
- ...updatedMessage.response_audio,
601
- transcript: (updatedMessage.response_audio?.transcript || "") + transcript
602
- };
603
- }
1017
+ this.applyRunContentFields(chunk, lastMessage, updatedMessage);
604
1018
  break;
605
1019
  case import_agno_types.RunEvent.ReasoningStep:
606
- case import_agno_types.RunEvent.TeamReasoningStep:
607
- const existingSteps = lastMessage.extra_data?.reasoning_steps ?? [];
608
- const incomingSteps = chunk.extra_data?.reasoning_steps ?? [];
609
- updatedMessage.extra_data = {
610
- ...updatedMessage.extra_data,
611
- reasoning_steps: [...existingSteps, ...incomingSteps]
612
- };
1020
+ case import_agno_types.RunEvent.TeamReasoningStep: {
1021
+ this.appendReasoningSteps(chunk, lastMessage, updatedMessage);
613
1022
  break;
1023
+ }
614
1024
  case import_agno_types.RunEvent.ReasoningCompleted:
615
1025
  case import_agno_types.RunEvent.TeamReasoningCompleted:
616
1026
  if (chunk.extra_data?.reasoning_steps) {
@@ -621,31 +1031,10 @@ var EventProcessor = class {
621
1031
  }
622
1032
  break;
623
1033
  case import_agno_types.RunEvent.RunCompleted:
624
- case import_agno_types.RunEvent.TeamRunCompleted:
625
- let updatedContent;
626
- if (typeof chunk.content === "string") {
627
- updatedContent = chunk.content;
628
- } else {
629
- try {
630
- updatedContent = JSON.stringify(chunk.content);
631
- } catch {
632
- updatedContent = "Error parsing response";
633
- }
634
- }
635
- updatedMessage.content = updatedContent;
636
- updatedMessage.tool_calls = processChunkToolCalls(
637
- chunk,
638
- lastMessage.tool_calls
639
- );
640
- updatedMessage.images = chunk.images ?? lastMessage.images;
641
- updatedMessage.videos = chunk.videos ?? lastMessage.videos;
642
- updatedMessage.response_audio = chunk.response_audio;
643
- updatedMessage.created_at = chunk.created_at ?? lastMessage.created_at;
644
- updatedMessage.extra_data = {
645
- reasoning_steps: chunk.extra_data?.reasoning_steps ?? lastMessage.extra_data?.reasoning_steps,
646
- references: chunk.extra_data?.references ?? lastMessage.extra_data?.references
647
- };
1034
+ case import_agno_types.RunEvent.TeamRunCompleted: {
1035
+ this.applyCompletedFields(chunk, lastMessage, updatedMessage);
648
1036
  break;
1037
+ }
649
1038
  case import_agno_types.RunEvent.UpdatingMemory:
650
1039
  case import_agno_types.RunEvent.TeamMemoryUpdateStarted:
651
1040
  case import_agno_types.RunEvent.TeamMemoryUpdateCompleted:
@@ -657,6 +1046,8 @@ var EventProcessor = class {
657
1046
  case import_agno_types.RunEvent.TeamRunCancelled:
658
1047
  updatedMessage.streamingError = true;
659
1048
  break;
1049
+ default:
1050
+ break;
660
1051
  }
661
1052
  return updatedMessage;
662
1053
  }
@@ -668,166 +1059,95 @@ var EventProcessor = class {
668
1059
  }
669
1060
  };
670
1061
 
671
- // src/parsers/stream-parser.ts
672
- function isLegacyFormat(data) {
673
- return typeof data === "object" && data !== null && "event" in data && !("data" in data) && typeof data.event === "string";
674
- }
675
- function convertNewFormatToLegacy(newFormatData) {
676
- const { event, data } = newFormatData;
677
- let parsedData;
678
- if (typeof data === "string") {
679
- try {
680
- parsedData = JSON.parse(data);
681
- } catch {
682
- parsedData = {};
683
- }
684
- } else {
685
- parsedData = data;
686
- }
687
- return {
688
- event,
689
- ...parsedData
690
- };
691
- }
692
- function processChunk(chunk, onChunk) {
693
- onChunk(chunk);
694
- }
695
- function parseBuffer(buffer, onChunk) {
696
- let currentIndex = 0;
697
- let jsonStartIndex = buffer.indexOf("{", currentIndex);
698
- while (jsonStartIndex !== -1 && jsonStartIndex < buffer.length) {
699
- let braceCount = 0;
700
- let inString = false;
701
- let escapeNext = false;
702
- let jsonEndIndex = -1;
703
- let i = jsonStartIndex;
704
- for (; i < buffer.length; i++) {
705
- const char = buffer[i];
706
- if (inString) {
707
- if (escapeNext) {
708
- escapeNext = false;
709
- } else if (char === "\\") {
710
- escapeNext = true;
711
- } else if (char === '"') {
712
- inString = false;
713
- }
714
- } else {
715
- if (char === '"') {
716
- inString = true;
717
- } else if (char === "{") {
718
- braceCount++;
719
- } else if (char === "}") {
720
- braceCount--;
721
- if (braceCount === 0) {
722
- jsonEndIndex = i;
723
- break;
724
- }
725
- }
726
- }
727
- }
728
- if (jsonEndIndex !== -1) {
729
- const jsonString = buffer.slice(jsonStartIndex, jsonEndIndex + 1);
730
- try {
731
- const parsed = JSON.parse(jsonString);
732
- if (isLegacyFormat(parsed)) {
733
- processChunk(parsed, onChunk);
734
- } else {
735
- const legacyChunk = convertNewFormatToLegacy(parsed);
736
- processChunk(legacyChunk, onChunk);
737
- }
738
- } catch (error) {
739
- if (typeof process !== "undefined" && process.env?.NODE_ENV === "development") {
740
- console.error("Failed to parse JSON chunk:", {
741
- error,
742
- chunk: jsonString.substring(0, 100) + (jsonString.length > 100 ? "..." : ""),
743
- position: jsonStartIndex
744
- });
745
- }
746
- if (jsonString.length > 1e4) {
747
- throw new Error(`Failed to parse large JSON chunk at position ${jsonStartIndex}`);
748
- }
749
- jsonStartIndex = buffer.indexOf("{", jsonStartIndex + 1);
750
- continue;
751
- }
752
- currentIndex = jsonEndIndex + 1;
753
- buffer = buffer.slice(currentIndex).trim();
754
- currentIndex = 0;
755
- jsonStartIndex = buffer.indexOf("{", currentIndex);
756
- } else {
757
- break;
758
- }
759
- }
760
- return buffer;
761
- }
762
- async function streamResponse(options) {
763
- const {
764
- apiUrl,
765
- headers = {},
766
- params,
767
- requestBody,
768
- onChunk,
769
- onError,
770
- onComplete,
771
- signal
772
- } = options;
773
- let buffer = "";
774
- const finalUrl = params && params.toString() ? `${apiUrl}?${params.toString()}` : apiUrl;
775
- try {
776
- const response = await fetch(finalUrl, {
777
- method: "POST",
778
- headers: {
779
- ...!(requestBody instanceof FormData) && {
780
- "Content-Type": "application/json"
781
- },
782
- ...headers
783
- },
784
- body: requestBody instanceof FormData ? requestBody : JSON.stringify(requestBody),
785
- signal
786
- });
787
- if (!response.ok) {
788
- let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
789
- const contentType = response.headers.get("content-type");
790
- if (contentType?.includes("application/json")) {
791
- try {
792
- const errorData = await response.json();
793
- errorMessage = errorData.detail || errorData.message || errorMessage;
794
- } catch {
795
- }
796
- }
797
- throw new Error(errorMessage);
798
- }
799
- if (!response.body) {
800
- throw new Error("No response body");
1062
+ // src/stores/message-store.ts
1063
+ var MessageStore = class {
1064
+ constructor() {
1065
+ this.messages = [];
1066
+ }
1067
+ /**
1068
+ * Get all messages
1069
+ */
1070
+ getMessages() {
1071
+ return [...this.messages];
1072
+ }
1073
+ /**
1074
+ * Set messages (replaces all)
1075
+ */
1076
+ setMessages(messages) {
1077
+ this.messages = [...messages];
1078
+ }
1079
+ /**
1080
+ * Add a message
1081
+ */
1082
+ addMessage(message) {
1083
+ this.messages = [...this.messages, message];
1084
+ }
1085
+ /**
1086
+ * Update the last message
1087
+ */
1088
+ updateLastMessage(updater) {
1089
+ if (this.messages.length === 0) {
1090
+ return void 0;
801
1091
  }
802
- const reader = response.body.getReader();
803
- const decoder = new TextDecoder();
804
- const processStream = async () => {
805
- while (true) {
806
- const { done, value } = await reader.read();
807
- if (done) {
808
- buffer = parseBuffer(buffer, onChunk);
809
- onComplete();
810
- return;
811
- }
812
- buffer += decoder.decode(value, { stream: true });
813
- buffer = parseBuffer(buffer, onChunk);
814
- }
815
- };
816
- await processStream();
817
- } catch (error) {
818
- if (error instanceof Error && error.name === "AbortError") {
819
- return;
1092
+ const lastMessage = this.messages.at(-1);
1093
+ if (!lastMessage) {
1094
+ return void 0;
820
1095
  }
821
- if (typeof error === "object" && error !== null && "detail" in error) {
822
- onError(new Error(String(error.detail)));
823
- } else {
824
- onError(new Error(String(error)));
1096
+ const updatedMessage = updater(lastMessage);
1097
+ this.messages = [...this.messages.slice(0, -1), updatedMessage];
1098
+ return updatedMessage;
1099
+ }
1100
+ /**
1101
+ * Update a specific message by index
1102
+ */
1103
+ updateMessage(index, updater) {
1104
+ if (index < 0 || index >= this.messages.length) {
1105
+ return void 0;
825
1106
  }
1107
+ const message = this.messages[index];
1108
+ const updatedMessage = updater(message);
1109
+ this.messages = [
1110
+ ...this.messages.slice(0, index),
1111
+ updatedMessage,
1112
+ ...this.messages.slice(index + 1)
1113
+ ];
1114
+ return updatedMessage;
826
1115
  }
827
- }
1116
+ /**
1117
+ * Remove last N messages
1118
+ */
1119
+ removeLastMessages(count) {
1120
+ this.messages = this.messages.slice(0, -count);
1121
+ }
1122
+ /**
1123
+ * Clear all messages
1124
+ */
1125
+ clear() {
1126
+ this.messages = [];
1127
+ }
1128
+ /**
1129
+ * Get the last message
1130
+ */
1131
+ getLastMessage() {
1132
+ return this.messages.length > 0 ? this.messages.at(-1) : void 0;
1133
+ }
1134
+ /**
1135
+ * Check if last message has streaming error
1136
+ */
1137
+ hasLastMessageError() {
1138
+ const lastMessage = this.getLastMessage();
1139
+ return lastMessage?.streamingError === true;
1140
+ }
1141
+ };
828
1142
 
829
1143
  // src/utils/logger.ts
830
- var SENSITIVE_KEYS = ["authToken", "Authorization", "token", "password", "apiKey"];
1144
+ var SENSITIVE_KEYS = [
1145
+ "authToken",
1146
+ "Authorization",
1147
+ "token",
1148
+ "password",
1149
+ "apiKey"
1150
+ ];
831
1151
  function sanitizeObject(obj) {
832
1152
  if (obj === null || obj === void 0) {
833
1153
  return obj;
@@ -855,36 +1175,36 @@ function sanitizeObject(obj) {
855
1175
  function isDevelopment() {
856
1176
  return typeof process !== "undefined" && process.env?.NODE_ENV === "development";
857
1177
  }
858
- var Logger = class {
1178
+ var Logger = {
859
1179
  /**
860
1180
  * Log debug information (only in development)
861
1181
  */
862
- static debug(message, data) {
1182
+ debug(message, data) {
863
1183
  if (isDevelopment()) {
864
1184
  const sanitized = data ? sanitizeObject(data) : void 0;
865
1185
  console.debug(`[DEBUG] ${message}`, sanitized || "");
866
1186
  }
867
- }
1187
+ },
868
1188
  /**
869
1189
  * Log informational messages (only in development)
870
1190
  */
871
- static info(message, data) {
1191
+ info(message, data) {
872
1192
  if (isDevelopment()) {
873
1193
  const sanitized = data ? sanitizeObject(data) : void 0;
874
1194
  console.info(`[INFO] ${message}`, sanitized || "");
875
1195
  }
876
- }
1196
+ },
877
1197
  /**
878
1198
  * Log warnings (always logs)
879
1199
  */
880
- static warn(message, data) {
1200
+ warn(message, data) {
881
1201
  const sanitized = data ? sanitizeObject(data) : void 0;
882
1202
  console.warn(`[WARN] ${message}`, sanitized || "");
883
- }
1203
+ },
884
1204
  /**
885
1205
  * Log errors (always logs)
886
1206
  */
887
- static error(message, data) {
1207
+ error(message, data) {
888
1208
  const sanitized = data ? sanitizeObject(data) : void 0;
889
1209
  console.error(`[ERROR] ${message}`, sanitized || "");
890
1210
  }
@@ -902,19 +1222,104 @@ function toSafeISOString(timestamp) {
902
1222
  }
903
1223
  return new Date(ts).toISOString();
904
1224
  }
1225
+ function getFileName(file, index) {
1226
+ if ("name" in file && typeof file.name === "string" && file.name) {
1227
+ return file.name;
1228
+ }
1229
+ return `file-${index}`;
1230
+ }
1231
+ function getFileFormat(mimeType) {
1232
+ if (!mimeType) {
1233
+ return void 0;
1234
+ }
1235
+ const [, subtype] = mimeType.split("/");
1236
+ if (!subtype) {
1237
+ return void 0;
1238
+ }
1239
+ return subtype.split(";")[0]?.trim().toLowerCase() || void 0;
1240
+ }
1241
+ function createPreviewUrl(file) {
1242
+ if (typeof URL === "undefined" || typeof URL.createObjectURL !== "function") {
1243
+ return void 0;
1244
+ }
1245
+ try {
1246
+ return URL.createObjectURL(file);
1247
+ } catch {
1248
+ return void 0;
1249
+ }
1250
+ }
1251
+ function buildMessageMediaPayload(files) {
1252
+ const images = [];
1253
+ const videos = [];
1254
+ const audio = [];
1255
+ const fileAttachments = [];
1256
+ const objectUrls = [];
1257
+ files.forEach((file, index) => {
1258
+ const filename = getFileName(file, index);
1259
+ const mimeType = file.type || "application/octet-stream";
1260
+ const format = getFileFormat(mimeType);
1261
+ const previewUrl = createPreviewUrl(file);
1262
+ if (previewUrl) {
1263
+ objectUrls.push(previewUrl);
1264
+ }
1265
+ if (mimeType.startsWith("image/") && previewUrl) {
1266
+ images.push({
1267
+ url: previewUrl,
1268
+ mime_type: mimeType,
1269
+ format
1270
+ });
1271
+ return;
1272
+ }
1273
+ if (mimeType.startsWith("video/")) {
1274
+ videos.push({
1275
+ url: previewUrl,
1276
+ id: filename,
1277
+ mime_type: mimeType,
1278
+ format
1279
+ });
1280
+ return;
1281
+ }
1282
+ if (mimeType.startsWith("audio/")) {
1283
+ audio.push({
1284
+ url: previewUrl,
1285
+ id: filename,
1286
+ mime_type: mimeType,
1287
+ format
1288
+ });
1289
+ return;
1290
+ }
1291
+ fileAttachments.push({
1292
+ filename,
1293
+ name: filename,
1294
+ mime_type: mimeType,
1295
+ format,
1296
+ size: file.size,
1297
+ url: previewUrl
1298
+ });
1299
+ });
1300
+ return {
1301
+ images: images.length > 0 ? images : void 0,
1302
+ videos: videos.length > 0 ? videos : void 0,
1303
+ audio: audio.length > 0 ? audio : void 0,
1304
+ files: fileAttachments.length > 0 ? fileAttachments : void 0,
1305
+ objectUrls
1306
+ };
1307
+ }
905
1308
  var AgnoClient = class extends import_eventemitter3.default {
906
1309
  constructor(config) {
907
1310
  super();
908
- // toolCallId -> UIComponentSpec
909
1311
  this.runCompletedSuccessfully = false;
1312
+ this.currentAbortController = null;
910
1313
  this.messageStore = new MessageStore();
911
1314
  this.configManager = new ConfigManager(config);
912
1315
  this.sessionManager = new SessionManager();
913
1316
  this.eventProcessor = new EventProcessor();
914
1317
  this.pendingUISpecs = /* @__PURE__ */ new Map();
1318
+ this.localAttachmentUrls = /* @__PURE__ */ new Set();
915
1319
  this.state = {
916
1320
  isStreaming: false,
917
1321
  isRefreshing: false,
1322
+ isCancelling: false,
918
1323
  isEndpointActive: false,
919
1324
  agents: [],
920
1325
  teams: [],
@@ -953,12 +1358,99 @@ var AgnoClient = class extends import_eventemitter3.default {
953
1358
  * Clear all messages
954
1359
  */
955
1360
  clearMessages() {
1361
+ this.revokeAttachmentUrlsFromMessages(this.messageStore.getMessages());
1362
+ this.localAttachmentUrls.clear();
956
1363
  this.messageStore.clear();
957
1364
  this.configManager.setSessionId(void 0);
958
1365
  this.pendingUISpecs.clear();
959
1366
  this.emit("message:update", this.messageStore.getMessages());
960
1367
  this.emit("state:change", this.getState());
961
1368
  }
1369
+ /**
1370
+ * Cancel an active or paused run
1371
+ */
1372
+ async cancelRun() {
1373
+ if (!(this.state.isStreaming || this.state.isPaused)) {
1374
+ throw new Error("No active or paused run to cancel");
1375
+ }
1376
+ const runId = this.state.pausedRunId || this.currentRunId;
1377
+ if (this.currentAbortController) {
1378
+ this.currentAbortController.abort();
1379
+ this.currentAbortController = null;
1380
+ }
1381
+ this.state.isCancelling = true;
1382
+ this.emit("state:change", this.getState());
1383
+ const cancelErrorMessage = runId ? await this.requestBackendCancel(runId) : this.logMissingRunIdForCancel();
1384
+ this.state.isStreaming = false;
1385
+ this.state.isPaused = false;
1386
+ this.state.isCancelling = false;
1387
+ this.state.pausedRunId = void 0;
1388
+ this.state.toolsAwaitingExecution = void 0;
1389
+ this.currentRunId = void 0;
1390
+ if (cancelErrorMessage) {
1391
+ this.state.errorMessage = cancelErrorMessage;
1392
+ this.emit("message:error", cancelErrorMessage);
1393
+ }
1394
+ this.emit("run:cancelled", { runId });
1395
+ this.emit("state:change", this.getState());
1396
+ }
1397
+ logMissingRunIdForCancel() {
1398
+ Logger.warn(
1399
+ "[AgnoClient] No run ID available, skipping backend cancel request"
1400
+ );
1401
+ return void 0;
1402
+ }
1403
+ async requestBackendCancel(runId) {
1404
+ const runUrl = this.configManager.getRunUrl();
1405
+ if (!runUrl) {
1406
+ const message = "Run cancelled locally, but backend cancel could not be sent: no agent or team selected";
1407
+ Logger.warn(`[AgnoClient] ${message}`);
1408
+ return message;
1409
+ }
1410
+ const cancelUrl = `${runUrl}/${runId}/cancel`;
1411
+ const headers = this.configManager.buildRequestHeaders();
1412
+ try {
1413
+ const response = await fetch(cancelUrl, {
1414
+ method: "POST",
1415
+ headers
1416
+ });
1417
+ if (response.ok) {
1418
+ return void 0;
1419
+ }
1420
+ if (response.status === 401 || response.status === 403) {
1421
+ const message = `Run cancelled locally, but backend cancel was rejected (${response.status})`;
1422
+ Logger.warn(`[AgnoClient] ${message}`);
1423
+ return message;
1424
+ }
1425
+ Logger.warn(
1426
+ `[AgnoClient] Backend cancel returned ${response.status} \u2014 run may have already completed`
1427
+ );
1428
+ return void 0;
1429
+ } catch (error) {
1430
+ const reason = error instanceof Error ? error.message : String(error);
1431
+ const message = `Run cancelled locally, but backend cancel failed: ${reason}`;
1432
+ Logger.warn(`[AgnoClient] ${message}`);
1433
+ return message;
1434
+ }
1435
+ }
1436
+ /**
1437
+ * Abort the active stream without calling the backend cancel endpoint.
1438
+ * Since streamResponse handles AbortError by returning silently
1439
+ * (no onComplete/onError called), state cleanup is done here.
1440
+ */
1441
+ abortStream() {
1442
+ if (!this.state.isStreaming) {
1443
+ return;
1444
+ }
1445
+ if (this.currentAbortController) {
1446
+ this.currentAbortController.abort();
1447
+ this.currentAbortController = null;
1448
+ }
1449
+ this.state.isStreaming = false;
1450
+ this.currentRunId = void 0;
1451
+ this.emit("stream:end");
1452
+ this.emit("state:change", this.getState());
1453
+ }
962
1454
  /**
963
1455
  * Send a message to the agent/team (streaming)
964
1456
  */
@@ -979,16 +1471,30 @@ var AgnoClient = class extends import_eventemitter3.default {
979
1471
  if (typeof message === "string") {
980
1472
  formData.append("message", message);
981
1473
  }
1474
+ if (options?.files) {
1475
+ options.files.forEach((file, index) => {
1476
+ formData.append("files", file, getFileName(file, index));
1477
+ });
1478
+ }
1479
+ const requestFiles = formData.getAll("files").filter((entry) => typeof entry !== "string");
1480
+ const userMessageMedia = buildMessageMediaPayload(requestFiles);
1481
+ this.trackAttachmentUrls(userMessageMedia.objectUrls);
1482
+ const userMessageContent = String(formData.get("message") ?? "");
982
1483
  const lastMessage = this.messageStore.getLastMessage();
983
1484
  if (lastMessage?.streamingError) {
984
1485
  const secondLast = this.messageStore.getMessages()[this.messageStore.getMessages().length - 2];
985
1486
  if (secondLast?.role === "user") {
1487
+ this.revokeAttachmentUrlsFromMessages([secondLast, lastMessage]);
986
1488
  this.messageStore.removeLastMessages(2);
987
1489
  }
988
1490
  }
989
1491
  this.messageStore.addMessage({
990
1492
  role: "user",
991
- content: formData.get("message"),
1493
+ content: userMessageContent,
1494
+ images: userMessageMedia.images,
1495
+ videos: userMessageMedia.videos,
1496
+ audio: userMessageMedia.audio,
1497
+ files: userMessageMedia.files,
992
1498
  created_at: Math.floor(Date.now() / 1e3)
993
1499
  });
994
1500
  this.messageStore.addMessage({
@@ -1002,33 +1508,42 @@ var AgnoClient = class extends import_eventemitter3.default {
1002
1508
  this.eventProcessor.reset();
1003
1509
  let newSessionId = this.configManager.getSessionId();
1004
1510
  try {
1005
- formData.append("stream", "true");
1006
- formData.append("session_id", newSessionId ?? "");
1511
+ formData.set("stream", "true");
1512
+ formData.set("session_id", newSessionId ?? "");
1007
1513
  const userId = this.configManager.getUserId();
1008
1514
  if (userId) {
1009
- formData.append("user_id", userId);
1515
+ formData.set("user_id", userId);
1516
+ }
1517
+ const dependencies = this.configManager.buildDependencies(
1518
+ options?.dependencies
1519
+ );
1520
+ if (dependencies) {
1521
+ formData.set("dependencies", JSON.stringify(dependencies));
1010
1522
  }
1011
1523
  const headers = this.configManager.buildRequestHeaders(options?.headers);
1012
1524
  const params = this.configManager.buildQueryString(options?.params);
1525
+ this.currentAbortController = new AbortController();
1013
1526
  await streamResponse({
1014
1527
  apiUrl: runUrl,
1015
1528
  headers,
1016
1529
  params,
1017
1530
  requestBody: formData,
1531
+ signal: this.currentAbortController.signal,
1018
1532
  onChunk: (chunk) => {
1019
- this.handleChunk(chunk, newSessionId, formData.get("message"));
1020
- if (chunk.event === import_agno_types2.RunEvent.RunStarted || chunk.event === import_agno_types2.RunEvent.TeamRunStarted || chunk.event === import_agno_types2.RunEvent.ReasoningStarted || chunk.event === import_agno_types2.RunEvent.TeamReasoningStarted) {
1021
- if (chunk.session_id) {
1022
- newSessionId = chunk.session_id;
1023
- this.configManager.setSessionId(chunk.session_id);
1024
- }
1533
+ this.handleChunk(chunk, newSessionId, userMessageContent);
1534
+ if ((chunk.event === import_agno_types2.RunEvent.RunStarted || chunk.event === import_agno_types2.RunEvent.TeamRunStarted || chunk.event === import_agno_types2.RunEvent.ReasoningStarted || chunk.event === import_agno_types2.RunEvent.TeamReasoningStarted) && chunk.session_id) {
1535
+ newSessionId = chunk.session_id;
1536
+ this.configManager.setSessionId(chunk.session_id);
1025
1537
  }
1026
1538
  },
1027
1539
  onError: (error) => {
1540
+ this.currentAbortController = null;
1028
1541
  this.handleError(error, newSessionId);
1029
1542
  },
1030
1543
  onComplete: async () => {
1544
+ this.currentAbortController = null;
1031
1545
  this.state.isStreaming = false;
1546
+ this.currentRunId = void 0;
1032
1547
  this.emit("stream:end");
1033
1548
  this.emit("message:complete", this.messageStore.getMessages());
1034
1549
  this.emit("state:change", this.getState());
@@ -1039,6 +1554,7 @@ var AgnoClient = class extends import_eventemitter3.default {
1039
1554
  }
1040
1555
  });
1041
1556
  } catch (error) {
1557
+ this.currentAbortController = null;
1042
1558
  this.handleError(
1043
1559
  error instanceof Error ? error : new Error(String(error)),
1044
1560
  newSessionId
@@ -1050,20 +1566,21 @@ var AgnoClient = class extends import_eventemitter3.default {
1050
1566
  */
1051
1567
  handleChunk(chunk, currentSessionId, messageContent) {
1052
1568
  const event = chunk.event;
1053
- if (event === import_agno_types2.RunEvent.RunStarted || event === import_agno_types2.RunEvent.TeamRunStarted || event === import_agno_types2.RunEvent.ReasoningStarted || event === import_agno_types2.RunEvent.TeamReasoningStarted) {
1054
- if (chunk.session_id && (!currentSessionId || currentSessionId !== chunk.session_id)) {
1055
- const sessionData = {
1056
- session_id: chunk.session_id,
1057
- session_name: messageContent,
1058
- created_at: toSafeISOString(chunk.created_at)
1059
- };
1060
- const sessionExists = this.state.sessions.some(
1061
- (s) => s.session_id === chunk.session_id
1062
- );
1063
- if (!sessionExists) {
1064
- this.state.sessions = [sessionData, ...this.state.sessions];
1065
- this.emit("session:created", sessionData);
1066
- }
1569
+ if ((event === import_agno_types2.RunEvent.RunStarted || event === import_agno_types2.RunEvent.TeamRunStarted) && chunk.run_id) {
1570
+ this.currentRunId = chunk.run_id;
1571
+ }
1572
+ if ((event === import_agno_types2.RunEvent.RunStarted || event === import_agno_types2.RunEvent.TeamRunStarted || event === import_agno_types2.RunEvent.ReasoningStarted || event === import_agno_types2.RunEvent.TeamReasoningStarted) && chunk.session_id && (!currentSessionId || currentSessionId !== chunk.session_id)) {
1573
+ const sessionData = {
1574
+ session_id: chunk.session_id,
1575
+ session_name: messageContent,
1576
+ created_at: toSafeISOString(chunk.created_at)
1577
+ };
1578
+ const sessionExists = this.state.sessions.some(
1579
+ (s) => s.session_id === chunk.session_id
1580
+ );
1581
+ if (!sessionExists) {
1582
+ this.state.sessions = [sessionData, ...this.state.sessions];
1583
+ this.emit("session:created", sessionData);
1067
1584
  }
1068
1585
  }
1069
1586
  if (event === import_agno_types2.RunEvent.RunPaused) {
@@ -1123,6 +1640,73 @@ var AgnoClient = class extends import_eventemitter3.default {
1123
1640
  this.emit("stream:end");
1124
1641
  this.emit("state:change", this.getState());
1125
1642
  }
1643
+ trackAttachmentUrls(urls) {
1644
+ for (const url of urls) {
1645
+ this.localAttachmentUrls.add(url);
1646
+ }
1647
+ }
1648
+ collectAttachmentUrls(message) {
1649
+ const imageUrls = message.images?.map((image) => image.url) ?? [];
1650
+ const videoUrls = message.videos?.map((video) => video.url).filter((url) => Boolean(url)) ?? [];
1651
+ const audioUrls = message.audio?.map((audio) => audio.url).filter((url) => Boolean(url)) ?? [];
1652
+ const fileUrls = message.files?.map((file) => file.url).filter((url) => Boolean(url)) ?? [];
1653
+ return [...imageUrls, ...videoUrls, ...audioUrls, ...fileUrls];
1654
+ }
1655
+ revokeAttachmentUrls(urls) {
1656
+ if (typeof URL === "undefined" || typeof URL.revokeObjectURL !== "function") {
1657
+ return;
1658
+ }
1659
+ for (const url of urls) {
1660
+ if (!this.localAttachmentUrls.has(url)) {
1661
+ continue;
1662
+ }
1663
+ URL.revokeObjectURL(url);
1664
+ this.localAttachmentUrls.delete(url);
1665
+ }
1666
+ }
1667
+ revokeAttachmentUrlsFromMessages(messages) {
1668
+ const urls = [];
1669
+ for (const message of messages) {
1670
+ if (!message) {
1671
+ continue;
1672
+ }
1673
+ urls.push(...this.collectAttachmentUrls(message));
1674
+ }
1675
+ this.revokeAttachmentUrls(urls);
1676
+ }
1677
+ collectExistingUIComponents() {
1678
+ const existingUIComponents = /* @__PURE__ */ new Map();
1679
+ for (const message of this.messageStore.getMessages()) {
1680
+ if (!message.tool_calls) {
1681
+ continue;
1682
+ }
1683
+ for (const toolCall of message.tool_calls) {
1684
+ if (toolCall.ui_component) {
1685
+ existingUIComponents.set(
1686
+ toolCall.tool_call_id,
1687
+ toolCall.ui_component
1688
+ );
1689
+ }
1690
+ }
1691
+ }
1692
+ return existingUIComponents;
1693
+ }
1694
+ restoreUIComponents(messages, uiComponents) {
1695
+ if (uiComponents.size === 0) {
1696
+ return;
1697
+ }
1698
+ for (const message of messages) {
1699
+ if (!message.tool_calls) {
1700
+ continue;
1701
+ }
1702
+ for (const toolCall of message.tool_calls) {
1703
+ const uiComponent = uiComponents.get(toolCall.tool_call_id);
1704
+ if (uiComponent) {
1705
+ toolCall.ui_component = uiComponent;
1706
+ }
1707
+ }
1708
+ }
1709
+ }
1126
1710
  /**
1127
1711
  * Refresh messages from the session API after run completion.
1128
1712
  * Replaces streamed messages with authoritative session data.
@@ -1135,19 +1719,14 @@ var AgnoClient = class extends import_eventemitter3.default {
1135
1719
  Logger.debug("[AgnoClient] Cannot refresh: no session ID");
1136
1720
  return;
1137
1721
  }
1722
+ if (this.state.isStreaming) {
1723
+ Logger.debug("[AgnoClient] Skipping refresh: stream is active");
1724
+ return;
1725
+ }
1138
1726
  this.state.isRefreshing = true;
1139
1727
  this.emit("state:change", this.getState());
1140
1728
  try {
1141
- const existingUIComponents = /* @__PURE__ */ new Map();
1142
- for (const message of this.messageStore.getMessages()) {
1143
- if (message.tool_calls) {
1144
- for (const toolCall of message.tool_calls) {
1145
- if (toolCall.ui_component) {
1146
- existingUIComponents.set(toolCall.tool_call_id, toolCall.ui_component);
1147
- }
1148
- }
1149
- }
1150
- }
1729
+ const existingUIComponents = this.collectExistingUIComponents();
1151
1730
  const config = this.configManager.getConfig();
1152
1731
  const entityType = this.configManager.getMode();
1153
1732
  const dbId = this.configManager.getDbId() || "";
@@ -1163,27 +1742,28 @@ var AgnoClient = class extends import_eventemitter3.default {
1163
1742
  userId,
1164
1743
  params
1165
1744
  );
1166
- const messages = this.sessionManager.convertSessionToMessages(response);
1167
- if (existingUIComponents.size > 0) {
1168
- for (const message of messages) {
1169
- if (message.tool_calls) {
1170
- for (let i = 0; i < message.tool_calls.length; i++) {
1171
- const toolCall = message.tool_calls[i];
1172
- const uiComponent = existingUIComponents.get(toolCall.tool_call_id);
1173
- if (uiComponent) {
1174
- message.tool_calls[i].ui_component = uiComponent;
1175
- }
1176
- }
1177
- }
1178
- }
1745
+ if (this.state.isStreaming) {
1746
+ Logger.debug(
1747
+ "[AgnoClient] Aborting refresh: stream started during fetch"
1748
+ );
1749
+ return;
1179
1750
  }
1751
+ const messages = this.sessionManager.convertSessionToMessages(response);
1752
+ this.restoreUIComponents(messages, existingUIComponents);
1753
+ this.revokeAttachmentUrlsFromMessages(this.messageStore.getMessages());
1180
1754
  this.messageStore.setMessages(messages);
1181
- Logger.debug("[AgnoClient] Session refreshed:", `${messages.length} messages`);
1755
+ Logger.debug(
1756
+ "[AgnoClient] Session refreshed:",
1757
+ `${messages.length} messages`
1758
+ );
1182
1759
  this.emit("message:refreshed", messages);
1183
1760
  this.emit("message:update", messages);
1184
1761
  } catch (error) {
1185
1762
  Logger.error("[AgnoClient] Failed to refresh session:", error);
1186
- this.emit("message:error", `Session refresh failed: ${error instanceof Error ? error.message : String(error)}`);
1763
+ this.emit(
1764
+ "message:error",
1765
+ `Session refresh failed: ${error instanceof Error ? error.message : String(error)}`
1766
+ );
1187
1767
  } finally {
1188
1768
  this.state.isRefreshing = false;
1189
1769
  this.emit("state:change", this.getState());
@@ -1198,7 +1778,11 @@ var AgnoClient = class extends import_eventemitter3.default {
1198
1778
  const entityType = this.configManager.getMode();
1199
1779
  const dbId = this.configManager.getDbId() || "";
1200
1780
  const userId = this.configManager.getUserId();
1201
- Logger.debug("[AgnoClient] Loading session with:", { entityType, dbId, userId });
1781
+ Logger.debug("[AgnoClient] Loading session with:", {
1782
+ entityType,
1783
+ dbId,
1784
+ userId
1785
+ });
1202
1786
  const headers = this.configManager.buildRequestHeaders();
1203
1787
  const params = this.configManager.buildQueryString(options?.params);
1204
1788
  const response = await this.sessionManager.fetchSession(
@@ -1211,7 +1795,11 @@ var AgnoClient = class extends import_eventemitter3.default {
1211
1795
  params
1212
1796
  );
1213
1797
  const messages = this.sessionManager.convertSessionToMessages(response);
1214
- Logger.debug("[AgnoClient] Setting messages to store:", `${messages.length} messages`);
1798
+ Logger.debug(
1799
+ "[AgnoClient] Setting messages to store:",
1800
+ `${messages.length} messages`
1801
+ );
1802
+ this.revokeAttachmentUrlsFromMessages(this.messageStore.getMessages());
1215
1803
  this.messageStore.setMessages(messages);
1216
1804
  this.configManager.setSessionId(sessionId);
1217
1805
  Logger.debug("[AgnoClient] Emitting events...");
@@ -1280,7 +1868,9 @@ var AgnoClient = class extends import_eventemitter3.default {
1280
1868
  }
1281
1869
  const existingToolCalls = lastMessage.tool_calls || [];
1282
1870
  const existingIds = new Set(existingToolCalls.map((t) => t.tool_call_id));
1283
- const newToolCalls = toolCalls.filter((t) => !existingIds.has(t.tool_call_id));
1871
+ const newToolCalls = toolCalls.filter(
1872
+ (t) => !existingIds.has(t.tool_call_id)
1873
+ );
1284
1874
  if (newToolCalls.length > 0) {
1285
1875
  this.messageStore.updateLastMessage((msg) => ({
1286
1876
  ...msg,
@@ -1327,8 +1917,9 @@ var AgnoClient = class extends import_eventemitter3.default {
1327
1917
  * Batches all updates to emit only one message:update event
1328
1918
  */
1329
1919
  applyPendingUISpecs() {
1330
- if (this.pendingUISpecs.size === 0)
1920
+ if (this.pendingUISpecs.size === 0) {
1331
1921
  return;
1922
+ }
1332
1923
  const messages = this.messageStore.getMessages();
1333
1924
  const updatedMessages = [];
1334
1925
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -1360,9 +1951,9 @@ var AgnoClient = class extends import_eventemitter3.default {
1360
1951
  }
1361
1952
  }
1362
1953
  if (updatedMessages.length > 0) {
1363
- updatedMessages.forEach(({ index, message }) => {
1954
+ for (const { index, message } of updatedMessages) {
1364
1955
  this.messageStore.updateMessage(index, () => message);
1365
- });
1956
+ }
1366
1957
  this.emit("message:update", this.messageStore.getMessages());
1367
1958
  }
1368
1959
  }
@@ -1383,22 +1974,55 @@ var AgnoClient = class extends import_eventemitter3.default {
1383
1974
  "HITL (Human-in-the-Loop) frontend tool execution is not supported for teams. Only agents support the continue endpoint."
1384
1975
  );
1385
1976
  }
1386
- if (!this.state.isPaused || !this.state.pausedRunId) {
1977
+ if (!(this.state.isPaused && this.state.pausedRunId)) {
1387
1978
  throw new Error("No paused run to continue");
1388
1979
  }
1389
1980
  const runUrl = this.configManager.getRunUrl();
1390
1981
  if (!runUrl) {
1391
1982
  throw new Error("No agent or team selected");
1392
1983
  }
1393
- const continueUrl = `${runUrl}/${this.state.pausedRunId}/continue`;
1394
- this.state.isPaused = false;
1984
+ const pausedRunId = this.state.pausedRunId;
1985
+ const continueUrl = `${runUrl}/${pausedRunId}/continue`;
1395
1986
  this.state.isStreaming = true;
1396
- this.emit("run:continued", { runId: this.state.pausedRunId });
1987
+ this.state.errorMessage = void 0;
1397
1988
  this.emit("state:change", this.getState());
1398
- const cleanedTools = tools.map((tool) => {
1399
- const { ui_component, ...backendTool } = tool;
1400
- return backendTool;
1401
- });
1989
+ let hasContinued = false;
1990
+ let streamError;
1991
+ const markRunContinued = (runId) => {
1992
+ if (hasContinued) {
1993
+ return;
1994
+ }
1995
+ hasContinued = true;
1996
+ this.state.isPaused = false;
1997
+ this.state.toolsAwaitingExecution = void 0;
1998
+ this.emit("run:continued", { runId: runId || pausedRunId });
1999
+ this.emit("state:change", this.getState());
2000
+ };
2001
+ const handleContinueError = (error) => {
2002
+ streamError = error;
2003
+ this.state.isStreaming = false;
2004
+ this.state.errorMessage = error.message;
2005
+ const isConflictError = error instanceof StreamResponseHttpError && error.status === 409;
2006
+ if (isConflictError || !hasContinued) {
2007
+ this.state.isPaused = true;
2008
+ } else {
2009
+ this.state.isPaused = false;
2010
+ this.state.pausedRunId = void 0;
2011
+ this.state.toolsAwaitingExecution = void 0;
2012
+ this.messageStore.updateLastMessage((msg) => ({
2013
+ ...msg,
2014
+ streamingError: true
2015
+ }));
2016
+ }
2017
+ this.emit("message:error", error.message);
2018
+ this.emit("stream:end");
2019
+ this.emit("state:change", this.getState());
2020
+ };
2021
+ const cleanedTools = tools.map(
2022
+ ({ ui_component: _uiComponent, ...tool }) => {
2023
+ return tool;
2024
+ }
2025
+ );
1402
2026
  const formData = new FormData();
1403
2027
  formData.append("tools", JSON.stringify(cleanedTools));
1404
2028
  formData.append("stream", "true");
@@ -1412,36 +2036,45 @@ var AgnoClient = class extends import_eventemitter3.default {
1412
2036
  }
1413
2037
  const headers = this.configManager.buildRequestHeaders(options?.headers);
1414
2038
  const params = this.configManager.buildQueryString(options?.params);
1415
- try {
1416
- await streamResponse({
1417
- apiUrl: continueUrl,
1418
- headers,
1419
- params,
1420
- requestBody: formData,
1421
- onChunk: (chunk) => {
1422
- this.handleChunk(chunk, currentSessionId, "");
1423
- },
1424
- onError: (error) => {
1425
- this.handleError(error, currentSessionId);
1426
- },
1427
- onComplete: async () => {
1428
- this.state.isStreaming = false;
2039
+ this.currentAbortController = new AbortController();
2040
+ await streamResponse({
2041
+ apiUrl: continueUrl,
2042
+ headers,
2043
+ params,
2044
+ requestBody: formData,
2045
+ signal: this.currentAbortController.signal,
2046
+ onChunk: (chunk) => {
2047
+ const event = chunk.event;
2048
+ if (!hasContinued && event !== import_agno_types2.RunEvent.RunPaused) {
2049
+ markRunContinued(chunk.run_id);
2050
+ }
2051
+ this.handleChunk(chunk, currentSessionId, "");
2052
+ },
2053
+ onError: (error) => {
2054
+ this.currentAbortController = null;
2055
+ handleContinueError(error);
2056
+ },
2057
+ onComplete: async () => {
2058
+ this.currentAbortController = null;
2059
+ if (!(hasContinued || this.state.isPaused)) {
2060
+ markRunContinued(pausedRunId);
2061
+ }
2062
+ this.state.isStreaming = false;
2063
+ if (!this.state.isPaused) {
1429
2064
  this.state.pausedRunId = void 0;
1430
2065
  this.state.toolsAwaitingExecution = void 0;
1431
- this.emit("stream:end");
1432
- this.emit("message:complete", this.messageStore.getMessages());
1433
- this.emit("state:change", this.getState());
1434
- if (this.runCompletedSuccessfully) {
1435
- this.runCompletedSuccessfully = false;
1436
- await this.refreshSessionMessages();
1437
- }
1438
2066
  }
1439
- });
1440
- } catch (error) {
1441
- this.handleError(
1442
- error instanceof Error ? error : new Error(String(error)),
1443
- currentSessionId
1444
- );
2067
+ this.emit("stream:end");
2068
+ this.emit("message:complete", this.messageStore.getMessages());
2069
+ this.emit("state:change", this.getState());
2070
+ if (this.runCompletedSuccessfully) {
2071
+ this.runCompletedSuccessfully = false;
2072
+ await this.refreshSessionMessages();
2073
+ }
2074
+ }
2075
+ });
2076
+ if (streamError) {
2077
+ throw streamError;
1445
2078
  }
1446
2079
  }
1447
2080
  /**
@@ -1526,7 +2159,7 @@ var AgnoClient = class extends import_eventemitter3.default {
1526
2159
  const currentConfig = this.configManager.getConfig();
1527
2160
  const hasAgentConfigured = currentConfig.agentId;
1528
2161
  const hasTeamConfigured = currentConfig.teamId;
1529
- if (!hasAgentConfigured && !hasTeamConfigured) {
2162
+ if (!(hasAgentConfigured || hasTeamConfigured)) {
1530
2163
  if (agents.length > 0) {
1531
2164
  const firstAgent = agents[0];
1532
2165
  this.configManager.updateConfig({
@@ -1547,10 +2180,34 @@ var AgnoClient = class extends import_eventemitter3.default {
1547
2180
  }
1548
2181
  return { agents, teams };
1549
2182
  }
2183
+ /**
2184
+ * Dispose of the client and clean up all resources.
2185
+ * Call this method when the client is no longer needed to prevent memory leaks.
2186
+ * After calling dispose(), the client instance should not be reused.
2187
+ */
2188
+ dispose() {
2189
+ if (this.currentAbortController) {
2190
+ this.currentAbortController.abort();
2191
+ this.currentAbortController = null;
2192
+ }
2193
+ this.removeAllListeners();
2194
+ this.revokeAttachmentUrlsFromMessages(this.messageStore.getMessages());
2195
+ this.localAttachmentUrls.clear();
2196
+ this.messageStore.clear();
2197
+ this.pendingUISpecs.clear();
2198
+ this.eventProcessor.reset();
2199
+ this.state.isStreaming = false;
2200
+ this.state.isRefreshing = false;
2201
+ this.state.isEndpointActive = false;
2202
+ this.state.agents = [];
2203
+ this.state.teams = [];
2204
+ this.state.sessions = [];
2205
+ this.state.isPaused = false;
2206
+ this.state.pausedRunId = void 0;
2207
+ this.state.toolsAwaitingExecution = void 0;
2208
+ this.state.errorMessage = void 0;
2209
+ }
1550
2210
  };
1551
-
1552
- // src/index.ts
1553
- var import_agno_types3 = require("@antipopp/agno-types");
1554
2211
  // Annotate the CommonJS export names for ESM import in node:
1555
2212
  0 && (module.exports = {
1556
2213
  AgnoClient,