@minded-ai/mindedjs 2.0.2 → 2.0.4-beta-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/agent.ts CHANGED
@@ -489,8 +489,8 @@ export class Agent {
489
489
  if (handlerResult) {
490
490
  if (!handlerResult.isQualified) {
491
491
  logger.debug({ message: '[Trigger] Disqualified', triggerName, triggerBody, sessionId });
492
- await this.interruptSessionManager.release(sessionId);
493
- return;
492
+ // Process any queued messages even when disqualified
493
+ return await this.processQueuedMessages(sessionId);
494
494
  }
495
495
  }
496
496
 
@@ -541,26 +541,10 @@ export class Agent {
541
541
  } else {
542
542
  res = await this.compiledGraph.invoke(state, langgraphConfig);
543
543
  }
544
- const nextMessage = await this.interruptSessionManager.dequeue(sessionId);
545
- if (nextMessage) {
546
- // Dequeue the first message and process it recursively
547
- // Recursively process the next message with bypass flag
548
- const invokeParams: AgentInvokeParams = {
549
- triggerBody: nextMessage.triggerBody,
550
- triggerName: nextMessage.triggerName,
551
- bypassSessionCheck: true,
552
- };
553
- if (nextMessage.appName) {
554
- invokeParams.appName = nextMessage.appName;
555
- }
556
- logger.debug({ msg: 'Invoking next message in the queue', invokeParams });
557
- return await this.invoke(invokeParams);
558
- }
559
-
560
- // Release the session lock
561
- await this.interruptSessionManager.release(sessionId);
562
544
 
563
- return res;
545
+ // Process any queued messages before returning
546
+ const queueResult = await this.processQueuedMessages(sessionId);
547
+ return queueResult !== undefined ? queueResult : res;
564
548
  } catch (err) {
565
549
  logger.error({ msg: '[Trigger] Error', err, sessionId });
566
550
 
@@ -946,4 +930,65 @@ export class Agent {
946
930
  public onEnd(): void {
947
931
  mindedConnection.disconnect();
948
932
  }
933
+
934
+ /**
935
+ * Process any queued messages for a session.
936
+ * If a message is found, it will be processed recursively.
937
+ * If no messages are found, checks if the graph was interrupted and resumes if needed.
938
+ * Only releases the lock when there's nothing left to process.
939
+ *
940
+ * @param sessionId - The session ID to check for queued messages
941
+ * @returns The result of processing the next message or resuming the graph, or undefined if nothing to process
942
+ */
943
+ private async processQueuedMessages(sessionId: string): Promise<any> {
944
+ const nextMessage = await this.interruptSessionManager.dequeue(sessionId);
945
+ if (nextMessage) {
946
+ // Dequeue the first message and process it recursively
947
+ const invokeParams: AgentInvokeParams = {
948
+ triggerBody: nextMessage.triggerBody,
949
+ triggerName: nextMessage.triggerName,
950
+ bypassSessionCheck: true,
951
+ };
952
+ if (nextMessage.appName) {
953
+ invokeParams.appName = nextMessage.appName;
954
+ }
955
+ logger.debug({
956
+ msg: 'Invoking next message in the queue',
957
+ invokeParams,
958
+ });
959
+
960
+ return await this.invoke(invokeParams);
961
+ }
962
+
963
+ // No more messages in queue - check if we need to resume an interrupted graph
964
+ const langgraphConfig = this.getLangraphConfig(sessionId);
965
+ const langgraphState = await this.compiledGraph.getState(langgraphConfig);
966
+ const lastTask = langgraphState.tasks[langgraphState.tasks.length - 1];
967
+ const hasInterrupt = lastTask?.interrupts?.length > 0;
968
+
969
+ if (hasInterrupt) {
970
+ const interruptValue = lastTask!.interrupts[0].value as InterruptPayload;
971
+ if (interruptValue?.type === InterruptType.NEW_TRIGGERS) {
972
+ // Graph was interrupted by new triggers - resume it
973
+ logger.debug({ msg: 'Resuming interrupted graph after processing queued messages', sessionId });
974
+
975
+ const state = langgraphState.values as State<any>;
976
+ const resumeResult = await this.compiledGraph.invoke(
977
+ new Command({
978
+ update: state,
979
+ resume: '',
980
+ }),
981
+ langgraphConfig,
982
+ );
983
+
984
+ // After resuming, check again for queued messages (recursive)
985
+ const queueResult = await this.processQueuedMessages(sessionId);
986
+ return queueResult !== undefined ? queueResult : resumeResult;
987
+ }
988
+ }
989
+
990
+ // No more messages and no interrupt to resume - release the session lock
991
+ await this.interruptSessionManager.release(sessionId);
992
+ return undefined;
993
+ }
949
994
  }
@@ -81,13 +81,16 @@ const getSdkVersion = (): string => {
81
81
  }
82
82
  };
83
83
 
84
- const connect = async (token: string): Promise<void> => {
84
+ const connect = async (token: string, retryCount: number = 0): Promise<void> => {
85
85
  const { isDeployed, baseUrl, env } = getConfig();
86
- logger.info(`Connecting to Minded platform: ${baseUrl}`);
86
+ const maxRetries = isDeployed ? 3 : 0;
87
+
88
+ logger.info(`Connecting to Minded platform: ${baseUrl}${retryCount > 0 ? ` (retry ${retryCount}/${maxRetries})` : ''}`);
89
+
87
90
  return new Promise<void>((resolve, reject) => {
88
91
  socket = io(baseUrl, {
89
92
  path: '/minded-connect',
90
- transports: ["websocket"],
93
+ transports: ['websocket'],
91
94
  query: {
92
95
  isDeployedAgent: isDeployed,
93
96
  token,
@@ -121,9 +124,32 @@ const connect = async (token: string): Promise<void> => {
121
124
  checkReady();
122
125
  });
123
126
 
124
- socket.on('connect_error', (err) => {
125
- logger.error({ msg: 'Failed to connect to minded platform', err: err.message });
126
- reject(new Error('Failed to connect to minded platform'));
127
+ socket.on('connect_error', async (err) => {
128
+ // If we're in cloud mode and haven't exhausted retries, try again
129
+ if (isDeployed && retryCount < maxRetries) {
130
+ logger.info({ msg: `Retrying connection in ${(retryCount + 1) * 2} seconds...` });
131
+
132
+ // Disconnect current socket
133
+ if (socket?.connected) {
134
+ socket.disconnect();
135
+ }
136
+ socket = null;
137
+
138
+ // Wait with exponential backoff
139
+ await wait((retryCount + 1) * 2000);
140
+
141
+ // Retry connection
142
+ try {
143
+ await connect(token, retryCount + 1);
144
+ resolve();
145
+ } catch (retryErr) {
146
+ reject(retryErr);
147
+ }
148
+ } else {
149
+ // No more retries or not in cloud mode
150
+ logger.error({ msg: 'Failed to connect to minded platform', err });
151
+ reject(new Error('Failed to connect to minded platform'));
152
+ }
127
153
  });
128
154
 
129
155
  socket.on('disconnect', () => {
@@ -144,8 +170,8 @@ const connect = async (token: string): Promise<void> => {
144
170
  socket.disconnect();
145
171
  }
146
172
 
147
- // Get new token and reconnect
148
- await connect(token);
173
+ // Get new token and reconnect (start fresh with retry count 0)
174
+ await connect(token, 0);
149
175
  }
150
176
  });
151
177