@peopl-health/nexus 3.2.0 → 3.2.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.
@@ -695,7 +695,9 @@ class NexusMessaging {
695
695
  async _processWithLock(chatId, existingTypingInterval = null) {
696
696
  this.processingLocks.set(chatId, true);
697
697
  let typingInterval = existingTypingInterval;
698
- let runId = null;
698
+
699
+ const runId = `run_${Date.now()}_${Math.random().toString(36).substring(7)}`;
700
+ this.activeRequests.set(chatId, runId);
699
701
 
700
702
  try {
701
703
  if (!typingInterval) {
@@ -707,6 +709,12 @@ class NexusMessaging {
707
709
  let lastCount = messageCount;
708
710
 
709
711
  while (Date.now() - startTime < this.batchingConfig.batchWindowMs) {
712
+ if (this.abandonedRuns.has(runId)) {
713
+ logger.info(`[Batching] Run ${runId} abandoned during batching for ${chatId}`);
714
+ this.abandonedRuns.delete(runId);
715
+ return;
716
+ }
717
+
710
718
  await new Promise(resolve => setTimeout(resolve, 500));
711
719
  const newCount = await this._getUnprocessedMessageCount(chatId);
712
720
 
@@ -721,11 +729,14 @@ class NexusMessaging {
721
729
  }
722
730
  }
723
731
 
732
+ if (this.abandonedRuns.has(runId)) {
733
+ logger.info(`[CheckAfter] Run ${runId} abandoned before AI call for ${chatId}`);
734
+ this.abandonedRuns.delete(runId);
735
+ return;
736
+ }
737
+
724
738
  logger.info(`[CheckAfter] Processing ${lastCount} messages for ${chatId} after batching`);
725
739
 
726
- runId = `run_${Date.now()}_${Math.random().toString(36).substring(7)}`;
727
- this.activeRequests.set(chatId, runId);
728
-
729
740
  const result = await this._processMessages(chatId, () => replyAssistant(chatId, null, null, { runId }));
730
741
 
731
742
  if (this.abandonedRuns.has(runId)) {
@@ -16,6 +16,7 @@ function getCurRow(baseID, code) {
16
16
  const runAssistantAndWait = async ({
17
17
  thread,
18
18
  assistant,
19
+ message = null,
19
20
  runConfig = {}
20
21
  }) => {
21
22
  if (!thread || !thread.getConversationId()) {
@@ -34,6 +35,7 @@ const runAssistantAndWait = async ({
34
35
  async (currentThread = thread) => {
35
36
  return await provider.executeRun({
36
37
  thread: currentThread,
38
+ message,
37
39
  assistant,
38
40
  tools,
39
41
  config,
@@ -58,7 +60,7 @@ const runAssistantWithRetries = async (thread, assistant, runConfig, patientRepl
58
60
  'thread.id': thread.getConversationId(),
59
61
  'assistant.id': thread.getAssistantId()
60
62
  })
61
- )({ thread, assistant, runConfig });
63
+ )({ thread, assistant, runConfig, message: patientReply });
62
64
 
63
65
  const predictionTimeMs = Date.now() - startTime;
64
66
 
@@ -11,26 +11,26 @@ class DefaultMemoryManager extends MemoryManager {
11
11
  this.maxHistoricalMessages = parseInt(process.env.MAX_HISTORICAL_MESSAGES || '50', 10);
12
12
  }
13
13
 
14
- async buildContext({ thread, config = {} }) {
14
+ async buildContext({ thread, message = null, config = {} }) {
15
15
  this._logActivity('Building context', { threadCode: thread.code });
16
16
 
17
17
  try {
18
- const allMessages = await getLastNMessages(thread.code, this.maxHistoricalMessages);
19
- const additionalMessages = config.additionalMessages || [];
18
+ const beforeCheckpoint = message ? message.createdAt : null;
19
+ const allMessages = await getLastNMessages(thread.code, this.maxHistoricalMessages, beforeCheckpoint);
20
20
 
21
21
  if (!allMessages?.length) {
22
- return additionalMessages;
22
+ return [];
23
23
  }
24
24
 
25
25
  const messageContext = allMessages.reverse().flatMap(msg => {
26
26
  const formattedContents = formatMessage(msg);
27
27
  return formattedContents.map(content => ({
28
- role: msg.origin === 'patient' ? 'user' : 'assistant',
28
+ role: msg.origin === 'instruction' ? 'developer' : msg.origin === 'patient' ? 'user' : 'assistant',
29
29
  content: content || msg.body || msg.content || ''
30
30
  }));
31
31
  }).filter(msg => msg.content);
32
32
 
33
- return [...additionalMessages, ...messageContext];
33
+ return messageContext;
34
34
  } catch (error) {
35
35
  logger.error('[DefaultMemoryManager] Context building failed', {
36
36
  threadCode: thread.code,
@@ -173,7 +173,7 @@ class OpenAIResponsesProvider {
173
173
  /**
174
174
  * Main entry point for running assistant
175
175
  */
176
- async executeRun({ thread, assistant, tools = [], config = {} }) {
176
+ async executeRun({ thread, assistant, message = null, tools = [], config = {} }) {
177
177
  const { conversationId, assistantId } = this._normalizeThread(thread);
178
178
  const promptVersion = thread?.version || null;
179
179
 
@@ -187,6 +187,7 @@ class OpenAIResponsesProvider {
187
187
  // Delegate context building to conversation manager
188
188
  const context = await this.conversationManager.buildContext({
189
189
  thread,
190
+ message,
190
191
  config: {
191
192
  ...config,
192
193
  threadId: conversationId,
@@ -112,14 +112,8 @@ const addInstructionCore = async (code, instruction, role = 'system') => {
112
112
 
113
113
  try {
114
114
  const assistant = getAssistantById(thread.getAssistantId(), thread);
115
- const runResult = await runAssistantWithRetries(thread, assistant, {
116
- additionalInstructions: instruction,
117
- additionalMessages: [
118
- { role: role, content: instruction }
119
- ]
120
- });
121
115
 
122
- // Save instruction message to database for frontend visibility
116
+ // Save instruction message to database
123
117
  try {
124
118
  const message_id = `instruction_${Date.now()}_${Math.random().toString(36).substring(7)}`;
125
119
  await insertMessage({
@@ -137,6 +131,13 @@ const addInstructionCore = async (code, instruction, role = 'system') => {
137
131
  logger.error('[addInstructionCore] Error saving instruction message', { err });
138
132
  }
139
133
 
134
+ const runResult = await runAssistantWithRetries(thread, assistant, {
135
+ additionalInstructions: instruction,
136
+ additionalMessages: [
137
+ { role: role, content: instruction }
138
+ ]
139
+ });
140
+
140
141
  logger.info('[addInstructionCore] Run response', { output: runResult?.output });
141
142
  return runResult?.output || null;
142
143
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",