@agentuity/runtime 0.0.86 → 0.0.88

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.
@@ -45,7 +45,13 @@ export class LocalKeyValueStorage implements KeyValueStorage {
45
45
  // Deserialize based on content type
46
46
  let data: T;
47
47
  if (row.content_type === 'application/json') {
48
- data = JSON.parse(row.value.toString('utf-8'));
48
+ try {
49
+ const text = row.value.toString('utf-8');
50
+ data = JSON.parse(text);
51
+ } catch {
52
+ // If JSON parse fails, return the raw buffer as Uint8Array
53
+ data = new Uint8Array(row.value) as T;
54
+ }
49
55
  } else if (row.content_type.startsWith('text/')) {
50
56
  data = row.value.toString('utf-8') as T;
51
57
  } else {
@@ -173,9 +173,6 @@ export class LocalVectorStorage implements VectorStorage {
173
173
  throw new Error('Query is required');
174
174
  }
175
175
 
176
- // Generate query embedding
177
- const queryEmbedding = simpleEmbedding(params.query);
178
-
179
176
  // Fetch all vectors for this name
180
177
  const query = this.#db.query(`
181
178
  SELECT id, key, embedding, metadata
@@ -190,6 +187,18 @@ export class LocalVectorStorage implements VectorStorage {
190
187
  metadata: string | null;
191
188
  }>;
192
189
 
190
+ // If no vectors exist, return empty results
191
+ if (rows.length === 0) {
192
+ return [];
193
+ }
194
+
195
+ // Detect dimensionality from first stored vector
196
+ const firstEmbedding = JSON.parse(rows[0].embedding);
197
+ const dimensions = firstEmbedding.length;
198
+
199
+ // Generate query embedding with matching dimensions
200
+ const queryEmbedding = simpleEmbedding(params.query, dimensions);
201
+
193
202
  // Calculate similarities
194
203
  const results: Array<VectorSearchResult<T> & { similarity: number }> = [];
195
204
 
package/src/session.ts CHANGED
@@ -615,10 +615,15 @@ export class DefaultSession implements Session {
615
615
  }
616
616
 
617
617
  /**
618
- * WebSocket client for thread state persistence
618
+ * WebSocket client for thread state persistence.
619
+ *
620
+ * **WARNING: This class is exported for testing purposes only and is subject to change
621
+ * without notice. Do not use this class directly in production code.**
622
+ *
619
623
  * @internal
624
+ * @experimental
620
625
  */
621
- class ThreadWebSocketClient {
626
+ export class ThreadWebSocketClient {
622
627
  private ws: WebSocket | null = null;
623
628
  private authenticated = false;
624
629
  private pendingRequests = new Map<
@@ -630,6 +635,10 @@ class ThreadWebSocketClient {
630
635
  private apiKey: string;
631
636
  private wsUrl: string;
632
637
  private wsConnecting: Promise<void> | null = null;
638
+ private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
639
+ private isDisposed = false;
640
+ private initialConnectResolve: (() => void) | null = null;
641
+ private initialConnectReject: ((err: Error) => void) | null = null;
633
642
 
634
643
  constructor(apiKey: string, wsUrl: string) {
635
644
  this.apiKey = apiKey;
@@ -638,10 +647,19 @@ class ThreadWebSocketClient {
638
647
 
639
648
  async connect(): Promise<void> {
640
649
  return new Promise((resolve, reject) => {
650
+ // Store the initial connect promise callbacks if this is the first attempt
651
+ if (this.reconnectAttempts === 0) {
652
+ this.initialConnectResolve = resolve;
653
+ this.initialConnectReject = reject;
654
+ }
655
+
641
656
  // Set connection timeout
642
657
  const connectionTimeout = setTimeout(() => {
643
658
  this.cleanup();
644
- reject(new Error('WebSocket connection timeout (10s)'));
659
+ const rejectFn = this.initialConnectReject || reject;
660
+ this.initialConnectResolve = null;
661
+ this.initialConnectReject = null;
662
+ rejectFn(new Error('WebSocket connection timeout (10s)'));
645
663
  }, 10_000);
646
664
 
647
665
  try {
@@ -662,13 +680,21 @@ class ThreadWebSocketClient {
662
680
  if (message.success) {
663
681
  this.authenticated = true;
664
682
  this.reconnectAttempts = 0;
665
- resolve();
683
+
684
+ // Resolve both the current promise and the initial connect promise
685
+ const resolveFn = this.initialConnectResolve || resolve;
686
+ this.initialConnectResolve = null;
687
+ this.initialConnectReject = null;
688
+ resolveFn();
666
689
  } else {
667
690
  const err = new Error(
668
691
  `WebSocket authentication failed: ${message.error || 'Unknown error'}`
669
692
  );
670
693
  this.cleanup();
671
- reject(err);
694
+ const rejectFn = this.initialConnectReject || reject;
695
+ this.initialConnectResolve = null;
696
+ this.initialConnectReject = null;
697
+ rejectFn(err);
672
698
  }
673
699
  return;
674
700
  }
@@ -692,7 +718,13 @@ class ThreadWebSocketClient {
692
718
  this.ws.on('error', (err: Error) => {
693
719
  clearTimeout(connectionTimeout);
694
720
  if (!this.authenticated) {
695
- reject(new Error(`WebSocket error: ${err.message}`));
721
+ // Don't reject immediately if we'll attempt reconnection
722
+ if (this.reconnectAttempts >= this.maxReconnectAttempts || this.isDisposed) {
723
+ const rejectFn = this.initialConnectReject || reject;
724
+ this.initialConnectResolve = null;
725
+ this.initialConnectReject = null;
726
+ rejectFn(new Error(`WebSocket error: ${err.message}`));
727
+ }
696
728
  }
697
729
  });
698
730
 
@@ -707,29 +739,59 @@ class ThreadWebSocketClient {
707
739
  this.pendingRequests.delete(id);
708
740
  }
709
741
 
710
- // Reject connecting promise if still pending
711
- if (!wasAuthenticated) {
712
- reject(new Error('WebSocket closed before authentication'));
742
+ // Don't attempt reconnection if disposed
743
+ if (this.isDisposed) {
744
+ // Reject initial connect if still pending
745
+ if (!wasAuthenticated && this.initialConnectReject) {
746
+ this.initialConnectReject(new Error('WebSocket closed before authentication'));
747
+ this.initialConnectResolve = null;
748
+ this.initialConnectReject = null;
749
+ }
750
+ return;
713
751
  }
714
752
 
715
- // Attempt reconnection only if we were previously authenticated
716
- if (wasAuthenticated && this.reconnectAttempts < this.maxReconnectAttempts) {
753
+ // Attempt reconnection if within retry limits (even if auth didn't complete)
754
+ // This handles server rollouts where connection closes before auth finishes
755
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
717
756
  this.reconnectAttempts++;
718
757
  const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30_000);
719
758
 
759
+ internal.info(
760
+ `WebSocket disconnected, attempting reconnection ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`
761
+ );
762
+
720
763
  // Schedule reconnection with backoff delay
721
- setTimeout(() => {
764
+ this.reconnectTimer = setTimeout(() => {
765
+ this.reconnectTimer = null;
722
766
  // Create new connection promise for reconnection
723
767
  this.wsConnecting = this.connect().catch(() => {
724
768
  // Reconnection failed, reset
725
769
  this.wsConnecting = null;
726
770
  });
727
771
  }, delay);
772
+ } else {
773
+ internal.error(
774
+ `WebSocket disconnected after ${this.reconnectAttempts} attempts, giving up`
775
+ );
776
+
777
+ // Reject initial connect if still pending (all attempts exhausted)
778
+ if (!wasAuthenticated && this.initialConnectReject) {
779
+ this.initialConnectReject(
780
+ new Error(
781
+ `WebSocket closed before authentication after ${this.reconnectAttempts} attempts`
782
+ )
783
+ );
784
+ this.initialConnectResolve = null;
785
+ this.initialConnectReject = null;
786
+ }
728
787
  }
729
788
  });
730
789
  } catch (err) {
731
790
  clearTimeout(connectionTimeout);
732
- reject(err);
791
+ const rejectFn = this.initialConnectReject || reject;
792
+ this.initialConnectResolve = null;
793
+ this.initialConnectReject = null;
794
+ rejectFn(err as Error);
733
795
  }
734
796
  });
735
797
  }
@@ -846,12 +908,25 @@ class ThreadWebSocketClient {
846
908
  }
847
909
 
848
910
  cleanup(): void {
911
+ // Mark as disposed to prevent new reconnection attempts
912
+ this.isDisposed = true;
913
+
914
+ // Cancel any pending reconnection timer
915
+ if (this.reconnectTimer) {
916
+ clearTimeout(this.reconnectTimer);
917
+ this.reconnectTimer = null;
918
+ }
919
+
849
920
  if (this.ws) {
850
921
  this.ws.close();
851
922
  this.ws = null;
852
923
  }
853
924
  this.authenticated = false;
854
925
  this.pendingRequests.clear();
926
+ this.reconnectAttempts = 0;
927
+ this.wsConnecting = null;
928
+ this.initialConnectResolve = null;
929
+ this.initialConnectReject = null;
855
930
  }
856
931
  }
857
932