@mxf-dev/core 2.0.1 → 2.0.3

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.
@@ -552,7 +552,14 @@ export class MemoryService {
552
552
  // Handle agent memory UPDATE
553
553
  if (typeof memoryEventData.id === 'string' && memoryEventData.data) {
554
554
  // CRITICAL: Must subscribe to Observable to trigger execution!
555
- this.updateAgentMemory(memoryEventData.id, memoryEventData.data).subscribe({
555
+ // The request context routes the UPDATE_RESULT back to the
556
+ // requesting agent with its own operationId — without it the
557
+ // SDK's pending save can never observe its result.
558
+ this.updateAgentMemory(memoryEventData.id, memoryEventData.data, {
559
+ operationId: memoryEventData.operationId,
560
+ requesterAgentId: event.agentId,
561
+ requesterChannelId: event.channelId
562
+ }).subscribe({
556
563
  next: () => null,
557
564
  error: (err) => this.logger.error(`Agent memory update failed for ${memoryEventData.id}: ${err}`)
558
565
  });
@@ -702,11 +709,27 @@ export class MemoryService {
702
709
 
703
710
  /**
704
711
  * Update agent memory
712
+ *
713
+ * When the update was requested over the wire (SDK → Events.Memory.UPDATE),
714
+ * requestContext carries the requester's operationId and identity. The
715
+ * UPDATE_RESULT emitted here must echo that operationId and be addressed to
716
+ * the requesting agent: the SDK matches results by operationId, and event
717
+ * forwarding routes them by payload.agentId. Before this parameter existed,
718
+ * agent-scope results were emitted with a freshly generated operationId under
719
+ * SYSTEM_AGENT — no SDK save round-trip could ever observe its own result, so
720
+ * every awaited save hung forever and every fire-and-forget save leaked its
721
+ * result listener.
722
+ *
705
723
  * @param pAgentId Agent ID
706
724
  * @param updates Memory updates
725
+ * @param requestContext Requester identity for result routing (wire requests only)
707
726
  * @returns Observable of updated agent memory
708
727
  */
709
- public updateAgentMemory(pAgentId: string, updates: Partial<IEnhancedAgentMemory>): Observable<IEnhancedAgentMemory> {
728
+ public updateAgentMemory(
729
+ pAgentId: string,
730
+ updates: Partial<IEnhancedAgentMemory>,
731
+ requestContext?: { operationId: string; requesterAgentId: AgentId; requesterChannelId: ChannelId }
732
+ ): Observable<IEnhancedAgentMemory> {
710
733
  this.validator.assertIsNonEmptyString(pAgentId, 'Agent ID must be a non-empty string');
711
734
  this.validator.assertIsObject(updates, 'Updates must be an object');
712
735
 
@@ -777,8 +800,11 @@ export class MemoryService {
777
800
  }
778
801
  }
779
802
 
780
- // Emit update event through EventBus
781
- const operationId = uuidv4(); // Generate operationId for the event data
803
+ // Emit update event through EventBus. Echo the requester's
804
+ // operationId and address the result to the requesting agent so
805
+ // the SDK's pending save resolves; internal callers (no
806
+ // requestContext) keep the system-level identity.
807
+ const operationId = requestContext?.operationId ?? uuidv4();
782
808
  const updateResultData: MemoryUpdateResultEventData = {
783
809
  operationId,
784
810
  scope: MemoryScope.AGENT,
@@ -786,14 +812,13 @@ export class MemoryService {
786
812
  memory: updatedMemory
787
813
  };
788
814
 
789
- // Define standard agentId and channelId for system-level memory events
790
- const systemAgentId: AgentId = 'SYSTEM_AGENT'; // Or a more specific system agent ID
791
- const noChannelId: ChannelId = 'NO_CHANNEL'; // Or a more specific system channel ID or null if appropriate
815
+ const resultAgentId: AgentId = requestContext?.requesterAgentId ?? 'SYSTEM_AGENT';
816
+ const resultChannelId: ChannelId = requestContext?.requesterChannelId ?? 'NO_CHANNEL';
792
817
 
793
818
  const payload = createMemoryUpdateResultEventPayload(
794
819
  Events.Memory.UPDATE_RESULT,
795
- systemAgentId,
796
- noChannelId,
820
+ resultAgentId,
821
+ resultChannelId,
797
822
  updateResultData
798
823
  );
799
824
  EventBus.server.emit(Events.Memory.UPDATE_RESULT, payload);
@@ -801,22 +826,21 @@ export class MemoryService {
801
826
  observer.next(updatedMemory);
802
827
  observer.complete();
803
828
  }, error => {
804
- const operationId_agent_update_error = uuidv4();
829
+ // Errors must reach the requester too — a save whose failure is
830
+ // only logged server-side leaves the requesting SDK waiting forever.
805
831
  const updateResultData_agent_update_error: MemoryUpdateResultEventData = {
806
- operationId: operationId_agent_update_error,
832
+ operationId: requestContext?.operationId ?? uuidv4(),
807
833
  scope: MemoryScope.AGENT,
808
834
  id: pAgentId,
809
835
  memory: null,
810
836
  error: (error as Error).message
811
837
  };
812
- const systemAgentId_agent_update_error: AgentId = 'SYSTEM_AGENT'; // Or a more specific system agent ID
813
- const noChannelId_agent_update_error: ChannelId = 'NO_CHANNEL'; // Or a more specific system channel ID or null if appropriate
814
838
  EventBus.server.emit(
815
- Events.Memory.UPDATE_RESULT,
839
+ Events.Memory.UPDATE_RESULT,
816
840
  createMemoryUpdateResultEventPayload(
817
841
  Events.Memory.UPDATE_RESULT,
818
- systemAgentId_agent_update_error,
819
- noChannelId_agent_update_error,
842
+ requestContext?.requesterAgentId ?? 'SYSTEM_AGENT',
843
+ requestContext?.requesterChannelId ?? 'NO_CHANNEL',
820
844
  updateResultData_agent_update_error
821
845
  )
822
846
  );
@@ -34,6 +34,13 @@ export enum NetworkErrorType {
34
34
  NETWORK_CONNECTION_REFUSED = 'NETWORK_CONNECTION_REFUSED',
35
35
  NETWORK_DNS_RESOLUTION = 'NETWORK_DNS_RESOLUTION',
36
36
  NETWORK_SOCKET_ERROR = 'NETWORK_SOCKET_ERROR',
37
+
38
+ // Hard per-request timeout: the request ran for the full configured bound
39
+ // (requestTimeoutMs / a stream idle bound) without completing. Deliberately
40
+ // NOT retryable — retrying a request that just consumed the entire timeout
41
+ // budget would multiply the silence the timeout exists to end. The caller
42
+ // gets the failure immediately.
43
+ REQUEST_TIMEOUT = 'REQUEST_TIMEOUT',
37
44
 
38
45
  // API service issues
39
46
  API_BAD_GATEWAY = 'API_BAD_GATEWAY', // 502
@@ -205,9 +212,18 @@ export function classifyNetworkError(
205
212
  }
206
213
  }
207
214
 
215
+ // Hard request timeouts are marked explicitly: either by AbortSignal.timeout()
216
+ // (DOMException named 'TimeoutError' in both Node and Bun) or by the
217
+ // isRequestTimeout flag set where the timeout is enforced. Checked before the
218
+ // message heuristics below so a hard timeout is never misclassified as a
219
+ // retryable NETWORK_TIMEOUT.
220
+ if (error?.isRequestTimeout === true || error?.name === 'TimeoutError') {
221
+ return NetworkErrorType.REQUEST_TIMEOUT;
222
+ }
223
+
208
224
  // Check error message for network issues
209
225
  const errorMessage = error?.message?.toLowerCase() || '';
210
-
226
+
211
227
  if (errorMessage.includes('timeout')) {
212
228
  return NetworkErrorType.NETWORK_TIMEOUT;
213
229
  }