@peopl-health/nexus 4.3.2 → 4.4.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.
@@ -10,6 +10,7 @@ const { sanitizeMediaFilename } = require('../utils/sanitizerUtils');
10
10
  const { validateMedia, getMediaType, MEDIA_LIMITS, STICKER_DIMENSIONS } = require('../utils/mediaValidator');
11
11
  const { logger } = require('../utils/logger');
12
12
  const { calculateDelay } = require('../utils/scheduleUtils');
13
+ const { isBenchMode } = require('../utils/benchModeHelper');
13
14
 
14
15
  const { ScheduledMessage } = require('../models/agendaMessageModel');
15
16
 
@@ -49,6 +50,24 @@ class TwilioProvider extends MessageProvider {
49
50
  throw new Error('Twilio provider not initialized');
50
51
  }
51
52
 
53
+ if (isBenchMode(messageData?.code)) {
54
+ const benchSid = `bench_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
55
+ logger.info('[TwilioProvider] bench.suppressed_send', {
56
+ event: 'bench.suppressed_send',
57
+ code: messageData.code,
58
+ body: messageData.body || null,
59
+ contentSid: messageData.contentSid || null
60
+ });
61
+ return {
62
+ success: true,
63
+ messageId: benchSid,
64
+ provider: 'twilio',
65
+ status: 'bench_suppressed',
66
+ result: { sid: benchSid, status: 'bench_suppressed' },
67
+ finalize: { sid: benchSid, status: 'bench_suppressed' }
68
+ };
69
+ }
70
+
52
71
  const { code, body, fileUrl, fileType, variables, contentSid } = messageData;
53
72
 
54
73
  const formattedFrom = ensureWhatsAppFormat(this.whatsappNumber);
@@ -236,7 +255,8 @@ class TwilioProvider extends MessageProvider {
236
255
  });
237
256
  const response = await sender(payload);
238
257
  const messageId = response?.result?.sid || null;
239
- await updateStatus('sent', messageId);
258
+ const persistedStatus = response?.status === 'bench_suppressed' ? 'bench_suppressed' : 'sent';
259
+ await updateStatus(persistedStatus, messageId);
240
260
  } catch (error) {
241
261
  await updateStatus('failed', null, error);
242
262
  logger.error(`Scheduled message failed: ${error.message}`);
@@ -4,7 +4,7 @@ const runtimeConfig = require('../config/runtimeConfig');
4
4
  const { logger } = require('../utils/logger');
5
5
 
6
6
  const { Message } = require('../models/messageModel');
7
- const { getBug, VALID_SEVERITIES } = require('../models/bugModel');
7
+ const { getBug, VALID_SEVERITIES, UPDATABLE_FIELDS } = require('../models/bugModel');
8
8
 
9
9
  const { addRecord, getRecordByFilter } = require('../services/airtableService');
10
10
 
@@ -99,14 +99,27 @@ const updateBugController = async (req, res) => {
99
99
  return res.status(400).json({ success: false, error: 'No fields provided for update' });
100
100
  }
101
101
 
102
+ const disallowed = Object.keys(fields).filter(k => !UPDATABLE_FIELDS.includes(k));
103
+ if (disallowed.length > 0) {
104
+ return res.status(400).json({ success: false, error: `Fields not allowed for update: ${disallowed.join(', ')}` });
105
+ }
106
+
107
+ if (fields.severity != null && !VALID_SEVERITIES.includes(fields.severity)) {
108
+ return res.status(400).json({ success: false, error: `Severity must be one of: ${VALID_SEVERITIES.join(', ')}` });
109
+ }
110
+
111
+ if (fields.project !== undefined && (typeof fields.project !== 'object' || Array.isArray(fields.project))) {
112
+ return res.status(400).json({ success: false, error: 'project must be an object' });
113
+ }
114
+
102
115
  const Bug = getBug();
103
- const updatedBug = await Bug.findOneAndUpdate({ recordId }, fields, { new: true });
116
+ const updatedBug = await Bug.findOneAndUpdate({ recordId }, { $set: fields }, { new: true, runValidators: true });
104
117
  if (!updatedBug) return res.status(404).json({ success: false, error: 'Bug not found' });
105
118
 
106
119
  res.status(200).json({ success: true, bug: updatedBug });
107
120
  } catch (error) {
108
121
  logger.error('Error updating bug report:', { error: error.message, recordId: req.params?.recordId });
109
- res.status(500).json({ success: false, error: error.message });
122
+ res.status(500).json({ success: false, error: 'Internal server error' });
110
123
  }
111
124
  };
112
125
 
@@ -441,7 +441,7 @@ const markMessagesAsReadController = async (req, res) => {
441
441
  .catch(err => logger.error('[markMessagesAsRead] Failed to reset thread unreadCount', { phoneNumber, error: err.message }));
442
442
 
443
443
  if (modifiedCount > 0) {
444
- updateRecordByFilter(Monitoreo_ID, 'message_monitor', `{whatsapp_id} = "${phoneNumber}"`, { read: true })
444
+ updateRecordByFilter(Monitoreo_ID, 'message_monitor', `{whatsapp_id} = "${phoneNumber}"`, { read: true }, phoneNumber)
445
445
  .catch(err => logger.error('[markMessagesAsRead] Failed to update message_monitor', { phoneNumber, error: err.message }));
446
446
  }
447
447
 
@@ -29,7 +29,8 @@ const createPrescriptionController = async (req, res) => {
29
29
  referenceTable: 'estado_general',
30
30
  referenceFilter: `{whatsapp_id}='${whatsappId}'`,
31
31
  linkFieldName: 'patient_id'
32
- }
32
+ },
33
+ whatsappId
33
34
  );
34
35
 
35
36
  logger.info('[PrescriptionController] Prescription created', { code: whatsappId });
@@ -61,14 +61,16 @@ async function updateMessageStatus(messageSid, status, errorCode = null, errorMe
61
61
  referenceTable: 'message_monitor',
62
62
  referenceFilter: `{whatsapp_id}="${updated.numero}"`,
63
63
  linkFieldName: 'message_monitor'
64
- }
64
+ },
65
+ updated.numero
65
66
  ).catch(err => logger.error('[MessageStatus] Failed to create undelivered_messages record', { messageSid, error: err.message }));
66
67
  } else {
67
68
  updateRecordByFilter(
68
69
  Monitoreo_ID,
69
70
  'undelivered_messages',
70
71
  `{message_id}="${updated.message_id}"`,
71
- { status }
72
+ { status },
73
+ updated.numero
72
74
  ).catch(err => logger.error('[MessageStatus] Failed to update undelivered_messages record', { messageSid, error: err.message }));
73
75
  }
74
76
  }
@@ -36,7 +36,7 @@ const getThreadInfo = async (code) => {
36
36
  const switchThreadStoppedStatus = async (code, stopped) => {
37
37
  await Thread.updateOne({ code }, { active: true, stopped });
38
38
 
39
- updateRecordByFilter(Monitoreo_ID, 'message_monitor', `{whatsapp_id} = "${code}"`, { stopped })
39
+ updateRecordByFilter(Monitoreo_ID, 'message_monitor', `{whatsapp_id} = "${code}"`, { stopped }, code)
40
40
  .catch(err => logger.error('[switchThreadStoppedStatus] Failed to update message_monitor', { code, error: err.message }));
41
41
  };
42
42
 
@@ -45,7 +45,7 @@ const setThreadPromptId = async (code, promptId) => {
45
45
  Thread.setAssistantId(updateFields, promptId);
46
46
  await Thread.updateOne({ code: code }, { $set: updateFields });
47
47
 
48
- updateRecordByFilter(Monitoreo_ID, 'message_monitor', `{whatsapp_id} = "${code}"`, { prompt_id: promptId })
48
+ updateRecordByFilter(Monitoreo_ID, 'message_monitor', `{whatsapp_id} = "${code}"`, { prompt_id: promptId }, code)
49
49
  .catch(err => logger.error('[setThreadPromptId] Failed to update message_monitor', { code, error: err.message }));
50
50
  };
51
51
 
@@ -31,7 +31,7 @@ async function uploadMediaToAirtable(whatsappId, mediaUrl, baseID, tableName) {
31
31
  referenceTable: 'estado_general',
32
32
  referenceFilter: `whatsapp_id = "${whatsappId}"`,
33
33
  linkFieldName: 'patient_id'
34
- });
34
+ }, whatsappId);
35
35
  } catch (error) {
36
36
  logger.warn('[uploadMediaToAirtable] Failed', { whatsappId, error: error.message });
37
37
  throw error;
@@ -2,6 +2,7 @@ const { OpenAI } = require('openai');
2
2
 
3
3
  const { logger } = require('../utils/logger');
4
4
  const { retryWithBackoff } = require('../utils/retryUtils');
5
+ const { isBenchMode } = require('../utils/benchModeHelper');
5
6
 
6
7
  const { getPatientMemory } = require('../models/patientMemoryModel');
7
8
  const { getConversationSummary } = require('../models/conversationSummaryModel');
@@ -69,6 +70,10 @@ class MemoryExtractor {
69
70
  }
70
71
 
71
72
  async processSessionEnd(numero, sessionMessages, sessionId) {
73
+ if (isBenchMode(numero)) {
74
+ logger.info('[MemoryExtractor] bench.suppressed_extraction', { event: 'bench.suppressed_extraction', numero, sessionId });
75
+ return { summary: null, memoriesCreated: 0, memoriesReinforced: 0 };
76
+ }
72
77
  if (!sessionMessages?.length) {
73
78
  logger.info('[MemoryExtractor] No messages to process', { numero, sessionId });
74
79
  return { summary: null, memoriesCreated: 0, memoriesReinforced: 0 };
@@ -1,4 +1,5 @@
1
1
  const { logger } = require('../utils/logger');
2
+ const { isBenchMode } = require('../utils/benchModeHelper');
2
3
 
3
4
  const { Message } = require('../models/messageModel');
4
5
 
@@ -13,6 +14,7 @@ class SessionManager {
13
14
  }
14
15
 
15
16
  recordActivity(numero) {
17
+ if (isBenchMode(numero)) return;
16
18
  const now = Date.now();
17
19
  const existing = this.activeSessions.get(numero);
18
20
 
@@ -58,6 +60,7 @@ class SessionManager {
58
60
  }
59
61
 
60
62
  async processSessionEndForNumero(numero) {
63
+ if (isBenchMode(numero)) return null;
61
64
  const session = this.activeSessions.get(numero);
62
65
  const sessionId = session?.sessionId || this._generateSessionId(numero);
63
66
  const sessionStart = session?.sessionStart || Date.now() - this.idleTimeoutMs;
@@ -4,6 +4,7 @@ const { getDb, getModelDatabase } = require('../config/mongoConfig');
4
4
  const { logger } = require('../utils/logger');
5
5
 
6
6
  const VALID_SEVERITIES = ['low', 'medium', 'high', 'critical'];
7
+ const UPDATABLE_FIELDS = ['status', 'repository', 'issue', 'assignees', 'pullRequests', 'resolution', 'immediatePatch', 'severity', 'server', 'bugType', 'project'];
7
8
 
8
9
  const bugSchema = new mongoose.Schema({
9
10
  reporter: { type: String, required: true, index: true },
@@ -19,7 +20,7 @@ const bugSchema = new mongoose.Schema({
19
20
  assignees: [{ type: String, default: null }],
20
21
  pullRequests: [{ type: String, default: null }],
21
22
  resolution: { type: String, default: null },
22
- inmediatePatch: { type: String, default: null },
23
+ immediatePatch: { type: String, default: null },
23
24
  recordId: { type: String, default: null },
24
25
  project: {
25
26
  projectId: { type: String, default: null },
@@ -34,4 +35,4 @@ const getBug = () => {
34
35
  return db.models.Bug || db.model('Bug', bugSchema);
35
36
  };
36
37
 
37
- module.exports = { getBug, bugSchema, VALID_SEVERITIES };
38
+ module.exports = { getBug, bugSchema, VALID_SEVERITIES, UPDATABLE_FIELDS };
@@ -169,7 +169,7 @@ async function insertMessage(values) {
169
169
  updateRecordByFilter(Monitoreo_ID, 'message_monitor', `{whatsapp_id} = "${values.numero}"`, {
170
170
  ...(values.from_me ? { last_message_bot: values.body } : { last_message_patient: values.body, read: false }),
171
171
  ...(values.from_me ? { last_message_bot_time: values.timestamp } : { last_message_patient_time: values.timestamp })
172
- }).catch(err => logger.error('[MongoStorage] Failed to update message_monitor table', { numero: values.numero, error: err.message }));
172
+ }, values.numero).catch(err => logger.error('[MongoStorage] Failed to update message_monitor table', { numero: values.numero, error: err.message }));
173
173
 
174
174
  logger.info('[MongoStorage] Message inserted or updated successfully');
175
175
  return { isNew, doc };
@@ -1,5 +1,6 @@
1
1
  const { trace, metrics } = require('@opentelemetry/api');
2
2
 
3
+ const { hasBenchAttribute } = require('../utils/benchModeHelper');
3
4
  const { initTelemetry, shutdownTelemetry } = require('../observability/telemetry');
4
5
 
5
6
  const tracer = trace.getTracer('nexus-assistant');
@@ -51,33 +52,39 @@ const s3UploadDuration = meter.createHistogram('nexus_s3_upload_duration', {
51
52
  });
52
53
 
53
54
  async function traceOperation(name, operation, attributes = {}) {
54
- const span = tracer.startSpan(name, { attributes });
55
+ const isBench = hasBenchAttribute(attributes);
56
+ const spanAttributes = isBench ? { ...attributes, bench: true } : attributes;
57
+ const span = tracer.startSpan(name, { attributes: spanAttributes });
55
58
  const startTime = Date.now();
56
-
59
+
57
60
  try {
58
- activeOperationsGauge.add(1, attributes);
59
-
61
+ if (!isBench) activeOperationsGauge.add(1, attributes);
62
+
60
63
  const result = await operation(span);
61
-
64
+
62
65
  const duration = Date.now() - startTime;
63
-
64
- responseTimeHistogram.record(duration, attributes);
65
- operationCounter.add(1, { ...attributes, success: true });
66
-
66
+
67
+ if (!isBench) {
68
+ responseTimeHistogram.record(duration, attributes);
69
+ operationCounter.add(1, { ...attributes, success: true });
70
+ }
71
+
67
72
  span.setAttributes({
68
73
  'operation.duration_ms': duration,
69
74
  'operation.success': true,
70
75
  });
71
-
76
+
72
77
  span.setStatus({ code: 1 });
73
78
  return result;
74
-
79
+
75
80
  } catch (error) {
76
81
  const duration = Date.now() - startTime;
77
-
78
- operationCounter.add(1, { ...attributes, success: false });
79
- responseTimeHistogram.record(duration, { ...attributes, error: true });
80
-
82
+
83
+ if (!isBench) {
84
+ operationCounter.add(1, { ...attributes, success: false });
85
+ responseTimeHistogram.record(duration, { ...attributes, error: true });
86
+ }
87
+
81
88
  span.recordException(error);
82
89
  span.setStatus({ code: 2, message: error.message });
83
90
  span.setAttributes({
@@ -85,16 +92,18 @@ async function traceOperation(name, operation, attributes = {}) {
85
92
  'operation.success': false,
86
93
  'error.message': error.message,
87
94
  });
88
-
95
+
89
96
  throw error;
90
97
  } finally {
91
- activeOperationsGauge.add(-1, attributes);
98
+ if (!isBench) activeOperationsGauge.add(-1, attributes);
92
99
  span.end();
93
100
  }
94
101
  }
95
102
 
96
103
  function createSpan(name, attributes = {}) {
97
- return tracer.startSpan(name, { attributes });
104
+ const isBench = hasBenchAttribute(attributes);
105
+ const spanAttributes = isBench ? { ...attributes, bench: true } : attributes;
106
+ return tracer.startSpan(name, { attributes: spanAttributes });
98
107
  }
99
108
 
100
109
  function init(config = {}) {
@@ -103,46 +112,51 @@ function init(config = {}) {
103
112
  }
104
113
 
105
114
  function traceAssistantReply(operation, attributes = {}) {
115
+ const isBench = hasBenchAttribute(attributes);
106
116
  return traceOperation('assistant_reply', async (span) => {
107
117
  const startTime = Date.now();
108
118
  try {
109
119
  const result = await operation(span);
110
120
  const duration = Date.now() - startTime;
111
- assistantReplyDuration.record(duration, attributes);
121
+ if (!isBench) assistantReplyDuration.record(duration, attributes);
112
122
  return result;
113
123
  } catch (error) {
114
124
  const duration = Date.now() - startTime;
115
- assistantReplyDuration.record(duration, { ...attributes, error: true });
125
+ if (!isBench) assistantReplyDuration.record(duration, { ...attributes, error: true });
116
126
  throw error;
117
127
  }
118
128
  }, attributes);
119
129
  }
120
130
 
121
131
  function traceAssistantInstruction(operation, attributes = {}) {
132
+ const isBench = hasBenchAttribute(attributes);
122
133
  return traceOperation('assistant_instruction', async (span) => {
123
134
  const startTime = Date.now();
124
135
  try {
125
136
  const result = await operation(span);
126
137
  const duration = Date.now() - startTime;
127
- assistantInstructionDuration.record(duration, attributes);
138
+ if (!isBench) assistantInstructionDuration.record(duration, attributes);
128
139
  return result;
129
140
  } catch (error) {
130
141
  const duration = Date.now() - startTime;
131
- assistantInstructionDuration.record(duration, { ...attributes, error: true });
142
+ if (!isBench) assistantInstructionDuration.record(duration, { ...attributes, error: true });
132
143
  throw error;
133
144
  }
134
145
  }, attributes);
135
146
  }
136
147
 
137
148
  function recordAssistantRetry(attributes = {}) {
149
+ if (hasBenchAttribute(attributes)) return;
138
150
  assistantRetryCounter.add(1, attributes);
139
151
  }
140
152
 
141
153
  function recordThreadOperation(operationType, attributes = {}) {
154
+ if (hasBenchAttribute(attributes)) return;
142
155
  threadOperationsCounter.add(1, { ...attributes, operation_type: operationType });
143
156
  }
144
157
 
145
158
  function recordFileOperation(operationType, attributes = {}) {
159
+ if (hasBenchAttribute(attributes)) return;
146
160
  fileOperationsCounter.add(1, { ...attributes, operation_type: operationType });
147
161
  }
148
162
 
@@ -147,6 +147,7 @@ const builtInControllers = {
147
147
  markMessagesAsReadController: conversationController.markMessagesAsReadController,
148
148
  searchMessagesByNumberController: conversationController.searchMessagesByNumberController,
149
149
  reportBugController: bugReportController.reportBugController,
150
+ updateBugController: bugReportController.updateBugController,
150
151
  getBugByWhatsappIdController: bugReportController.getBugByWhatsappIdController,
151
152
  caseDocumentationController: caseDocumentationController.caseDocumentationController,
152
153
 
@@ -1,6 +1,7 @@
1
1
  const { airtable } = require('../config/airtableConfig');
2
2
 
3
3
  const { logger } = require('../utils/logger');
4
+ const { isBenchMode } = require('../utils/benchModeHelper');
4
5
 
5
6
  let evalMode = false;
6
7
 
@@ -23,7 +24,11 @@ async function collectRecords(query, mapper = r => r.fields) {
23
24
  return records;
24
25
  }
25
26
 
26
- async function addRecord(baseID, tableName, fields) {
27
+ async function addRecord(baseID, tableName, fields, context = null) {
28
+ if (isBenchMode(context)) {
29
+ logger.info('[addRecord:bench] Suppressed', { tableName, code: context });
30
+ return { id: 'bench_mock_record', fields: Array.isArray(fields) ? fields[0]?.fields || {} : fields };
31
+ }
27
32
  if (evalMode) {
28
33
  logger.info('[addRecord:eval] Muted', { tableName });
29
34
  return { id: 'eval_mock_record', fields: Array.isArray(fields) ? fields[0]?.fields || {} : fields };
@@ -60,7 +65,11 @@ async function getRecordByFilter(baseID, tableName, filter, view = 'Grid view',
60
65
  }
61
66
  }
62
67
 
63
- async function updateRecordByFilter(baseID, tableName, filter, updateFields) {
68
+ async function updateRecordByFilter(baseID, tableName, filter, updateFields, context = null) {
69
+ if (isBenchMode(context)) {
70
+ logger.info('[updateRecordByFilter:bench] Suppressed', { tableName, filter, code: context });
71
+ return [{ id: 'bench_mock_record', fields: updateFields }];
72
+ }
64
73
  if (evalMode) {
65
74
  logger.info('[updateRecordByFilter:eval] Muted', { tableName, filter });
66
75
  return [{ id: 'eval_mock_record', fields: updateFields }];
@@ -85,9 +94,14 @@ async function updateRecordByFilter(baseID, tableName, filter, updateFields) {
85
94
  }
86
95
  }
87
96
 
88
- async function addLinkedRecord(baseID, targetTable, fields, linkConfig) {
97
+ async function addLinkedRecord(baseID, targetTable, fields, linkConfig, context = null) {
89
98
  if (!baseID) throw new Error('[addLinkedRecord] Base ID is required');
90
99
 
100
+ if (isBenchMode(context)) {
101
+ logger.info('[addLinkedRecord:bench] Suppressed', { targetTable, code: context });
102
+ return { id: 'bench_mock_record', fields };
103
+ }
104
+
91
105
  try {
92
106
  if (linkConfig) {
93
107
  const { referenceTable, referenceFilter, linkFieldName } = linkConfig;
@@ -55,13 +55,13 @@ const updatePatientInformation = async (code, fields, auditContext = {}) => {
55
55
  .map(([key, value]) => [key.replace(/^updt_/, ''), value])
56
56
  );
57
57
 
58
- const result = await updateRecordByFilter(Estado_General_ID, 'estado_general', filter, fields);
58
+ const result = await updateRecordByFilter(Estado_General_ID, 'estado_general', filter, fields, code);
59
59
 
60
60
  let failedFields = [];
61
61
  if (!result) {
62
62
  logger.warn('[updatePatientInformation] Bulk update failed, retrying fields individually', { code });
63
63
  for (const [key, value] of Object.entries(fields)) {
64
- const fieldResult = await updateRecordByFilter(Estado_General_ID, 'estado_general', filter, { [key]: value });
64
+ const fieldResult = await updateRecordByFilter(Estado_General_ID, 'estado_general', filter, { [key]: value }, code);
65
65
  if (!fieldResult) {
66
66
  failedFields.push({ key, value });
67
67
  const cleanKey = key.replace(/^updt_/, '');
@@ -0,0 +1,31 @@
1
+ const BENCH_CODE_PREFIX = 'bench:';
2
+
3
+ function extractCode(codeOrThread) {
4
+ if (!codeOrThread) return null;
5
+ if (typeof codeOrThread === 'string') return codeOrThread;
6
+ if (typeof codeOrThread === 'object') {
7
+ return codeOrThread.code || codeOrThread.numero || codeOrThread.thread_code || null;
8
+ }
9
+ return null;
10
+ }
11
+
12
+ function isBenchMode(codeOrThread) {
13
+ const code = extractCode(codeOrThread);
14
+ return typeof code === 'string' && code.startsWith(BENCH_CODE_PREFIX);
15
+ }
16
+
17
+ function hasBenchAttribute(attributes) {
18
+ if (!attributes || typeof attributes !== 'object') return false;
19
+ if (attributes.bench === true) return true;
20
+ for (const key of Object.keys(attributes)) {
21
+ const value = attributes[key];
22
+ if (typeof value === 'string' && value.startsWith(BENCH_CODE_PREFIX)) return true;
23
+ }
24
+ return false;
25
+ }
26
+
27
+ module.exports = {
28
+ BENCH_CODE_PREFIX,
29
+ isBenchMode,
30
+ hasBenchAttribute
31
+ };
@@ -1,6 +1,7 @@
1
1
  const { SpanStatusCode } = require('@opentelemetry/api');
2
2
 
3
3
  const { createSpan } = require('../observability');
4
+ const { hasBenchAttribute } = require('./benchModeHelper');
4
5
 
5
6
  const withTracing = (fn, spanName, attributeMapper = null, options = {}) => {
6
7
  return async function (...args) {
@@ -9,7 +10,11 @@ const withTracing = (fn, spanName, attributeMapper = null, options = {}) => {
9
10
 
10
11
  try {
11
12
  if (typeof attributeMapper === 'function') {
12
- span.setAttributes(attributeMapper(...args));
13
+ const attributes = attributeMapper(...args);
14
+ span.setAttributes(attributes);
15
+ if (hasBenchAttribute(attributes)) {
16
+ span.setAttribute('bench', true);
17
+ }
13
18
  }
14
19
  const result = await fn.apply(this, args);
15
20
  span.setStatus({ code: SpanStatusCode.OK });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "4.3.2",
3
+ "version": "4.4.0",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",