@peopl-health/nexus 5.9.0-dev.921 → 6.0.0-dev.611

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.
Files changed (126) hide show
  1. package/lib/clinical/AssistantProcessor.js +5 -34
  2. package/lib/clinical/config/divergenceConfig.js +1 -18
  3. package/lib/clinical/config/llmConfig.js +1 -9
  4. package/lib/clinical/context/ToolRuntimeContext.js +2 -1
  5. package/lib/clinical/helpers/divergenceHelper.js +0 -12
  6. package/lib/clinical/index.js +0 -2
  7. package/lib/clinical/memory/DefaultMemoryManager.js +9 -19
  8. package/lib/clinical/models/turnTraceModel.js +5 -18
  9. package/lib/clinical/providers/AnthropicProvider.js +29 -291
  10. package/lib/clinical/providers/BaseLLMProvider.js +24 -113
  11. package/lib/clinical/providers/GatewayProvider.js +3 -3
  12. package/lib/clinical/providers/OpenAIResponsesProvider.js +4 -46
  13. package/lib/clinical/services/divergenceService.js +5 -22
  14. package/lib/clinical/services/promptComposerService.js +15 -46
  15. package/lib/clinical/services/shadowMessageService.js +12 -28
  16. package/lib/clinical/services/shadowService.js +3 -12
  17. package/lib/clinical/stores/CtcaeCatalog.js +0 -20
  18. package/lib/config/airtableConfig.js +0 -3
  19. package/lib/core/BatchingManager.js +3 -10
  20. package/lib/core/NexusMessaging.js +13 -31
  21. package/lib/eval/EvalProvider.js +2 -3
  22. package/lib/fhir/config/fhirConfig.js +3 -34
  23. package/lib/fhir/constants/projectionSlugs.js +1 -41
  24. package/lib/fhir/helpers/elementHelper.js +0 -21
  25. package/lib/fhir/helpers/fhirHelper.js +2 -5
  26. package/lib/fhir/index.js +0 -24
  27. package/lib/fhir/models/envelopeModel.js +0 -5
  28. package/lib/fhir/projections/registerProjectors.js +0 -16
  29. package/lib/fhir/resources/Condition.js +2 -107
  30. package/lib/fhir/resources/Observation.js +1 -73
  31. package/lib/fhir/resources/Procedure.js +2 -34
  32. package/lib/fhir/resources/Provenance.js +2 -93
  33. package/lib/fhir/services/fhirService.js +2 -2
  34. package/lib/helpers/twilioHelper.js +1 -9
  35. package/lib/index.d.ts +0 -16
  36. package/lib/index.js +0 -9
  37. package/lib/models/messageModel.js +0 -6
  38. package/lib/routes/index.js +1 -21
  39. package/lib/services/airtableService.js +1 -2
  40. package/lib/shared/dtos/ContingencySafetyNet.js +3 -10
  41. package/lib/shared/dtos/DispatchedEscalation.js +8 -3
  42. package/lib/shared/dtos/Reminder.js +2 -2
  43. package/lib/storage/MongoStorage.js +0 -2
  44. package/package.json +1 -1
  45. package/lib/clinical/config/cohortConfig.js +0 -68
  46. package/lib/clinical/config/composerConfig.js +0 -42
  47. package/lib/clinical/config/subAgentsConfig.js +0 -44
  48. package/lib/clinical/flags/bridgeConfig.js +0 -14
  49. package/lib/clinical/flags/composerFlags.js +0 -14
  50. package/lib/clinical/helpers/clinicalFactsHelper.js +0 -108
  51. package/lib/clinical/helpers/clinicalMentionHelper.js +0 -68
  52. package/lib/clinical/helpers/cohortHelper.js +0 -22
  53. package/lib/clinical/helpers/gradeEstimateHelper.js +0 -29
  54. package/lib/clinical/helpers/resultsHelper.js +0 -34
  55. package/lib/clinical/helpers/safetyFlagsHelper.js +0 -10
  56. package/lib/clinical/services/bridgeService.js +0 -55
  57. package/lib/clinical/services/clinicalAirtableService.js +0 -162
  58. package/lib/clinical/services/clinicalExtractionService.js +0 -112
  59. package/lib/clinical/services/composerService.js +0 -59
  60. package/lib/clinical/services/dispatcherService.js +0 -137
  61. package/lib/clinical/services/reminderDispatchService.js +0 -36
  62. package/lib/clinical/services/skillService.js +0 -44
  63. package/lib/clinical/tools/analyzeSymptomPatternsTool.js +0 -279
  64. package/lib/clinical/tools/deliverPatientMessageTool.js +0 -202
  65. package/lib/clinical/tools/extractClinicalInfoTool.js +0 -167
  66. package/lib/clinical/tools/getActiveSymptomLandscapeTool.js +0 -141
  67. package/lib/clinical/tools/getPatientHistoryTool.js +0 -235
  68. package/lib/clinical/tools/getPatientRiskProfileTool.js +0 -71
  69. package/lib/clinical/tools/getRouterContextBundleTool.js +0 -93
  70. package/lib/clinical/tools/lookupCtcaeEntryTool.js +0 -35
  71. package/lib/clinical/tools/openClusterHypothesisTool.js +0 -160
  72. package/lib/clinical/tools/openConditionTool.js +0 -136
  73. package/lib/clinical/tools/recordClinicalImpressionTool.js +0 -251
  74. package/lib/clinical/tools/recordInterventionTool.js +0 -140
  75. package/lib/clinical/tools/registerClinicalTools.js +0 -39
  76. package/lib/clinical/tools/reportCrisisTool.js +0 -211
  77. package/lib/clinical/tools/reportMedicalEscalationTool.js +0 -187
  78. package/lib/clinical/tools/reportResultsReceivedTool.js +0 -153
  79. package/lib/clinical/tools/reportUnresolvedRequestv2Tool.js +0 -177
  80. package/lib/clinical/tools/schedulePatientReminderTool.js +0 -149
  81. package/lib/clinical/tools/setContingencyPlanTool.js +0 -176
  82. package/lib/clinical/tools/skillTool.js +0 -67
  83. package/lib/clinical/tools/submitRoutingDecisionTool.js +0 -291
  84. package/lib/clinical/tools/updateClusterHypothesisTool.js +0 -159
  85. package/lib/controllers/cohortController.js +0 -16
  86. package/lib/controllers/traceController.js +0 -26
  87. package/lib/core/workflowRunner.js +0 -35
  88. package/lib/fhir/config/fhirTablesConfig.js +0 -9
  89. package/lib/fhir/constants/severityPriority.js +0 -8
  90. package/lib/fhir/helpers/compositeHelper.js +0 -88
  91. package/lib/fhir/helpers/fhirReadHelper.js +0 -42
  92. package/lib/fhir/helpers/terminologyHelper.js +0 -260
  93. package/lib/fhir/projections/clusterProjection.js +0 -24
  94. package/lib/fhir/projections/contingencyProjection.js +0 -36
  95. package/lib/fhir/projections/dispatchedEscalationProjection.js +0 -25
  96. package/lib/fhir/projections/interventionProjection.js +0 -42
  97. package/lib/fhir/projections/proctcaeProjection.js +0 -179
  98. package/lib/fhir/projections/reminderProjection.js +0 -20
  99. package/lib/fhir/projections/riskProjection.js +0 -16
  100. package/lib/fhir/projections/routingProjection.js +0 -48
  101. package/lib/fhir/projections/symptomCaseProjection.js +0 -65
  102. package/lib/fhir/resources/CarePlan.js +0 -93
  103. package/lib/fhir/resources/ClinicalImpression.js +0 -118
  104. package/lib/fhir/resources/Communication.js +0 -65
  105. package/lib/fhir/resources/CommunicationRequest.js +0 -182
  106. package/lib/fhir/resources/Device.js +0 -24
  107. package/lib/fhir/resources/Patient.js +0 -18
  108. package/lib/fhir/resources/Questionnaire.js +0 -23
  109. package/lib/fhir/resources/QuestionnaireResponse.js +0 -29
  110. package/lib/fhir/resources/RequestOrchestration.js +0 -46
  111. package/lib/fhir/resources/RiskAssessment.js +0 -99
  112. package/lib/fhir/resources/Task.js +0 -106
  113. package/lib/fhir/resources/interventionExtensions.js +0 -28
  114. package/lib/fhir/services/clusterService.js +0 -77
  115. package/lib/fhir/services/contingencyService.js +0 -117
  116. package/lib/fhir/services/interventionService.js +0 -149
  117. package/lib/fhir/services/resultObservationService.js +0 -52
  118. package/lib/fhir/services/riskService.js +0 -101
  119. package/lib/fhir/services/routingService.js +0 -147
  120. package/lib/fhir/services/snapshotService.js +0 -28
  121. package/lib/fhir/services/symptomCaseService.js +0 -139
  122. package/lib/fhir/services/terminologyService.js +0 -58
  123. package/lib/fhir/services/triageService.js +0 -57
  124. package/lib/fhir/utils/normalizeUtils.js +0 -33
  125. package/lib/shared/dtos/GradedSymptom.js +0 -23
  126. package/lib/shared/dtos/TriageResponse.js +0 -35
@@ -1,7 +1,6 @@
1
1
  const { getSystems } = require('../config/fhirConfig');
2
2
  const { fhirId } = require('../helpers/fhirHelper');
3
- const { identifier, reference, patientReference, codeableConcept, extension, toIso } = require('../helpers/elementHelper');
4
- const { SYMPTOM_GRADE_SLUG } = require('../constants/projectionSlugs');
3
+ const { identifier, patientReference, codeableConcept } = require('../helpers/elementHelper');
5
4
 
6
5
  const OBSERVED_FIELD_SYSTEM_KEY = 'observedField';
7
6
 
@@ -46,77 +45,6 @@ class Observation {
46
45
  if (components.length) payload.component = components;
47
46
  return new Observation(payload);
48
47
  }
49
-
50
- static fromTriageProjection({ sym, code, aeCategoryConcept, components, gradeConcept, method, deviceId }) {
51
- const sid = String(sym.symptomId);
52
- const payload = {
53
- resourceType: 'Observation',
54
- id: fhirId(sid),
55
- identifier: [identifier('pro-ctcae-symptom', sid)],
56
- status: 'preliminary',
57
- category: [observationCategory('survey'), ...(aeCategoryConcept ? [aeCategoryConcept] : [])],
58
- code,
59
- subject: patientReference(sym.patientId),
60
- performer: [patientReference(sym.patientId)],
61
- valueCodeableConcept: gradeConcept,
62
- method,
63
- extension: [
64
- extension('grade-verification', { valueCode: 'provisional' }),
65
- extension('grade-derivation', { valueCode: 'pro-ctcae-composite' }),
66
- extension('symptom-source', { valueString: String(sym.source || 'triage') }),
67
- ],
68
- derivedFrom: [reference('QuestionnaireResponse', `triage-${sym.triageId}`)],
69
- device: reference('Device', deviceId),
70
- };
71
- if (sym.effective) payload.effectiveDateTime = toIso(sym.effective);
72
- if (components.length) payload.component = components;
73
- return new Observation(payload);
74
- }
75
-
76
- static fromSymptomGrade({ assessment }) {
77
- const grade = assessment.grade;
78
- const gradeId = `${assessment.assessmentId}-grade`;
79
- const extensions = [];
80
- if (grade.confidence !== null) extensions.push(extension('grade-confidence', { valueString: grade.confidence }));
81
- if (grade.possibleRange !== null) extensions.push(extension('grade-range', { valueString: `${grade.possibleRange[0]}-${grade.possibleRange[1]}` }));
82
- if (grade.reasoning) extensions.push(extension('grade-reasoning', { valueString: grade.reasoning }));
83
- if (assessment.gradeVerification) extensions.push(extension('grade-verification', { valueString: assessment.gradeVerification }));
84
- if (assessment.gradeThresholdClause) extensions.push(extension('grade-threshold-clause', { valueString: assessment.gradeThresholdClause }));
85
- const payload = {
86
- resourceType: 'Observation',
87
- id: fhirId(gradeId),
88
- identifier: [identifier(SYMPTOM_GRADE_SLUG, gradeId)],
89
- status: 'final',
90
- category: [observationCategory('survey')],
91
- code: codeableConcept('CTCAE severity grade'),
92
- subject: patientReference(assessment.patientId),
93
- performer: [patientReference(assessment.patientId)],
94
- effectiveDateTime: toIso(assessment.performedAt),
95
- };
96
- if (grade.bestEstimate !== null) payload.valueInteger = grade.bestEstimate;
97
- if (extensions.length) payload.extension = extensions;
98
- return new Observation(payload);
99
- }
100
-
101
- static fromResultObservation({ patientId, obsId, code, value, unit, category, effective, now, slug }) {
102
- const payload = {
103
- resourceType: 'Observation',
104
- id: fhirId(obsId),
105
- identifier: [identifier(slug, obsId)],
106
- status: 'final',
107
- category: [observationCategory(category || 'laboratory')],
108
- code: codeableConcept(code),
109
- subject: patientReference(patientId),
110
- effectiveDateTime: effective || now,
111
- issued: now,
112
- };
113
- if (typeof value === 'number') {
114
- if (Number.isFinite(value)) payload.valueQuantity = unit ? { value, unit } : { value };
115
- } else if (value !== null && value !== undefined && value !== '') {
116
- payload.valueString = String(value);
117
- }
118
- return new Observation(payload);
119
- }
120
48
  }
121
49
 
122
50
  function observationCategory(code) {
@@ -1,43 +1,11 @@
1
- const { fhirId, codeSystemUrl } = require('../helpers/fhirHelper');
2
- const {
3
- identifier,
4
- patientReference,
5
- reference,
6
- codeableConcept,
7
- notes,
8
- toIso,
9
- } = require('../helpers/elementHelper');
10
- const {
11
- INTERVENTION_SLUG,
12
- INTERVENTION_KIND_SLUG,
13
- INTERVENTION_OUTCOME_SLUG,
14
- } = require('../constants/projectionSlugs');
15
- const { interventionExtensions } = require('./interventionExtensions');
1
+ const { fhirId } = require('../helpers/fhirHelper');
2
+ const { identifier, patientReference } = require('../helpers/elementHelper');
16
3
 
17
4
  class Procedure {
18
5
  constructor(payload) {
19
6
  Object.assign(this, payload);
20
7
  }
21
8
 
22
- static fromIntervention({ intervention }) {
23
- const payload = {
24
- resourceType: 'Procedure',
25
- id: fhirId(intervention.interventionId),
26
- identifier: [identifier(INTERVENTION_SLUG, intervention.interventionId)],
27
- status: intervention.outcome === 'pending' ? 'in-progress' : 'completed',
28
- code: codeableConcept(intervention.interventionLabel),
29
- subject: patientReference(intervention.patientId),
30
- reason: [{ reference: reference('Condition', intervention.caseId) }],
31
- occurrenceDateTime: toIso(intervention.deliveredAt),
32
- outcome: codeableConcept(intervention.outcome, { code: intervention.outcome, system: codeSystemUrl(INTERVENTION_OUTCOME_SLUG) }),
33
- category: [codeableConcept(intervention.kind, { code: intervention.kind, system: codeSystemUrl(INTERVENTION_KIND_SLUG) })],
34
- extension: interventionExtensions(intervention, { withRecipient: true }),
35
- };
36
- const note = notes(intervention.rationale, intervention.outcomeNotes);
37
- if (note) payload.note = note;
38
- return new Procedure(payload);
39
- }
40
-
41
9
  static fromMentionProjection({ fact, provenance, code, extensions, slug }) {
42
10
  const assertion = fact.assertion;
43
11
  const notes = provenance.verbatimQuotes && provenance.verbatimQuotes.length
@@ -1,33 +1,12 @@
1
1
  const { getDeviceId, getOrganizationId } = require('../config/fhirConfig');
2
- const { fhirId, codeSystemUrl } = require('../helpers/fhirHelper');
3
- const { extension, reference, codeableConcept, toIso } = require('../helpers/elementHelper');
4
-
5
- const CLUSTER_ACTIVITY_CODESYSTEM = 'clinical-activity';
6
- const CLUSTER_ACTIVITY_CODES = {
7
- opened: 'cluster-hypothesis-opened',
8
- updated: 'cluster-hypothesis-updated',
9
- dissolved: 'cluster-hypothesis-dissolved',
10
- };
2
+ const { fhirId } = require('../helpers/fhirHelper');
3
+ const { extension, reference, codeableConcept } = require('../helpers/elementHelper');
11
4
 
12
5
  class Provenance {
13
6
  constructor(payload) {
14
7
  Object.assign(this, payload);
15
8
  }
16
9
 
17
- static fromClusterImpression({ clusterId, turnId, action, recordedAt, transitionReason }) {
18
- const activityCode = CLUSTER_ACTIVITY_CODES[action] || 'cluster-hypothesis';
19
- const payload = {
20
- resourceType: 'Provenance',
21
- id: fhirId(`${clusterId}-prov-${turnId}-${action}`),
22
- target: [{ reference: `ClinicalImpression/${fhirId(clusterId)}` }],
23
- recorded: toIso(recordedAt),
24
- agent: [{ who: reference('Device', getDeviceId()), onBehalfOf: reference('Organization', getOrganizationId()) }],
25
- activity: codeableConcept(activityCode, { code: activityCode, system: codeSystemUrl(CLUSTER_ACTIVITY_CODESYSTEM) }),
26
- };
27
- if (transitionReason) payload.extension = [extension('transition-reason', { valueString: transitionReason })];
28
- return new Provenance(payload);
29
- }
30
-
31
10
  static fromMentionProjection({ target, provenance, activityText }) {
32
11
  const payload = {
33
12
  resourceType: 'Provenance',
@@ -44,76 +23,6 @@ class Provenance {
44
23
  };
45
24
  return new Provenance(payload);
46
25
  }
47
-
48
- static fromDispatchedEscalation({ target, recordedAt, activityText }) {
49
- return new Provenance({
50
- resourceType: 'Provenance',
51
- id: fhirId(`${target.id}-prov`),
52
- target: [{ reference: `${target.resourceType}/${target.id}` }],
53
- recorded: toIso(recordedAt),
54
- agent: [{ who: reference('Device', getDeviceId()), onBehalfOf: reference('Organization', getOrganizationId()) }],
55
- activity: codeableConcept(activityText),
56
- });
57
- }
58
-
59
- static fromRoutingDecision({ provenanceId, targetRefs, provenance, activityText }) {
60
- return new Provenance({
61
- resourceType: 'Provenance',
62
- id: fhirId(provenanceId),
63
- target: targetRefs,
64
- recorded: toIso(provenance.recordedAt),
65
- agent: [{ who: reference('Device', provenance.agentDevice), onBehalfOf: reference('Organization', provenance.onBehalfOf) }],
66
- activity: codeableConcept(activityText),
67
- });
68
- }
69
-
70
- static fromSymptomCase({ provenanceId, targetRefs, recordedAt, activityText }) {
71
- return new Provenance({
72
- resourceType: 'Provenance',
73
- id: fhirId(provenanceId),
74
- target: targetRefs,
75
- recorded: toIso(recordedAt),
76
- agent: [{ who: reference('Device', getDeviceId()), onBehalfOf: reference('Organization', getOrganizationId()) }],
77
- activity: codeableConcept(activityText),
78
- });
79
- }
80
-
81
- static fromPatientRisk({ assessment }) {
82
- return new Provenance({
83
- resourceType: 'Provenance',
84
- id: fhirId(`${assessment.assessmentId}-prov`),
85
- target: [{ reference: `RiskAssessment/${fhirId(assessment.assessmentId)}` }],
86
- recorded: toIso(assessment.assessedAt),
87
- agent: [{ who: reference('Device', getDeviceId()), onBehalfOf: reference('Organization', getOrganizationId()) }],
88
- activity: codeableConcept('patient-risk-assessment'),
89
- });
90
- }
91
-
92
- static fromContingency({ planId, recipients, recordedAt, activityText }) {
93
- const targets = [{ reference: `CarePlan/${fhirId(planId)}` }];
94
- for (const recipient of recipients) {
95
- targets.push({ reference: `CommunicationRequest/${fhirId(`${planId}-${recipient}`)}` });
96
- }
97
- return new Provenance({
98
- resourceType: 'Provenance',
99
- id: fhirId(`${planId}-prov`),
100
- target: targets,
101
- recorded: toIso(recordedAt),
102
- agent: [{ who: reference('Device', getDeviceId()), onBehalfOf: reference('Organization', getOrganizationId()) }],
103
- activity: codeableConcept(activityText),
104
- });
105
- }
106
-
107
- static fromResultObservations({ targetIds, provenanceId, activityText, now }) {
108
- return new Provenance({
109
- resourceType: 'Provenance',
110
- id: fhirId(provenanceId),
111
- target: targetIds.map((id) => ({ reference: `Observation/${fhirId(id)}` })),
112
- recorded: now,
113
- agent: [{ who: reference('Device', getDeviceId()), onBehalfOf: reference('Organization', getOrganizationId()) }],
114
- activity: codeableConcept(activityText),
115
- });
116
- }
117
26
  }
118
27
 
119
28
  module.exports = { Provenance };
@@ -15,11 +15,11 @@ function project(items) {
15
15
  return registry.project(items);
16
16
  }
17
17
 
18
- async function storeResources(resources, { store = null, merge = null } = {}) {
18
+ async function storeResources(resources, { store = null } = {}) {
19
19
  const resourceStore = store || getFhirStore();
20
20
  const stored = [];
21
21
  for (const resource of [resources].flat(Infinity).filter((entry) => entry && entry.resourceType)) {
22
- stored.push(await resourceStore.upsert(resource, { merge }));
22
+ stored.push(await resourceStore.upsert(resource));
23
23
  }
24
24
  return { stored };
25
25
  }
@@ -121,18 +121,10 @@ const ensureWhatsAppFormat = (code) => {
121
121
  return trimmed;
122
122
  };
123
123
 
124
- const stripWhatsAppFormat = (code) => {
125
- const raw = String(code).trim();
126
- const unprefixed = raw.startsWith('whatsapp:') ? raw.slice('whatsapp:'.length) : raw;
127
- const beforeAt = unprefixed.includes('@') ? unprefixed.slice(0, unprefixed.indexOf('@')) : unprefixed;
128
- return beforeAt.replace(/^\+/, '');
129
- };
130
-
131
124
  module.exports = {
132
125
  convertTwilioToInternalFormat,
133
126
  downloadMediaFromTwilio,
134
127
  getMediaTypeFromContentType,
135
128
  extractTitle,
136
- ensureWhatsAppFormat,
137
- stripWhatsAppFormat
129
+ ensureWhatsAppFormat
138
130
  };
package/lib/index.d.ts CHANGED
@@ -416,20 +416,6 @@ declare module '@peopl-health/nexus' {
416
416
  export function createQueueAdapter(type: 'local' | 'redis' | string, config?: any): QueueAdapter;
417
417
  export function registerQueueAdapter(name: string, AdapterClass: typeof QueueAdapter): void;
418
418
 
419
- // Workflows
420
- export interface Workflow {
421
- kind: string;
422
- dedupeKey: (trigger: any) => string;
423
- prepare: (trigger: any) => Promise<any>;
424
- }
425
-
426
- export interface WorkflowRunner {
427
- register(workflow: Workflow): Promise<Workflow>;
428
- enqueue(kind: string, trigger: any, options?: QueueJobOptions): Promise<string>;
429
- }
430
-
431
- export function createWorkflowRunner(options: { queueAdapter: QueueAdapter }): WorkflowRunner;
432
-
433
419
  // Memory System
434
420
  export interface PatientMemoryDocument {
435
421
  _id: any;
@@ -631,8 +617,6 @@ declare module '@peopl-health/nexus' {
631
617
  prompt?: string | null;
632
618
  preset?: string | null;
633
619
  response_id?: string | null;
634
- turnId?: string | null;
635
- traceId?: string | null;
636
620
  run?: any;
637
621
  predictionTimeMs?: number;
638
622
  retries?: number;
package/lib/index.js CHANGED
@@ -16,7 +16,6 @@ const {
16
16
  DefaultMemoryManager,
17
17
  EnhancedMemoryManager,
18
18
  initialize: initializeClinical,
19
- registerClinicalTools,
20
19
  } = require('./clinical');
21
20
  const { initialize: initializeFhir } = require('./fhir');
22
21
  const { logger } = require('./utils/logger');
@@ -27,7 +26,6 @@ const { BaileysProvider } = require('./adapters/BaileysProvider');
27
26
  const { setPreprocessingHandler, hasPreprocessingHandler, invokePreprocessingHandler } = require('./services/preprocessingService');
28
27
  const { requestIdMiddleware, getRequestId } = require('./middleware/requestId');
29
28
  const { QueueAdapter, LocalQueueAdapter, RedisQueueAdapter, createQueueAdapter, registerQueueAdapter } = require('./queue');
30
- const { createWorkflowRunner } = require('./core/workflowRunner');
31
29
  const routes = require('./routes');
32
30
  const { resetAll } = require('./config/lifecycle');
33
31
  const { EvalProvider } = require('./eval/EvalProvider');
@@ -159,12 +157,6 @@ class Nexus {
159
157
  logger.warn('[Nexus] clinical init skipped', { error: e?.message || e });
160
158
  }
161
159
 
162
- try {
163
- registerClinicalTools();
164
- } catch (e) {
165
- logger.warn('Warning: failed to register clinical tools:', e?.message || e);
166
- }
167
-
168
160
  this.isInitialized = true;
169
161
  }
170
162
 
@@ -215,7 +207,6 @@ class Nexus {
215
207
  }
216
208
 
217
209
  module.exports = {
218
- createWorkflowRunner,
219
210
  Nexus,
220
211
  TwilioProvider,
221
212
  BaileysProvider,
@@ -78,8 +78,6 @@ const messageSchema = new mongoose.Schema({
78
78
  prompt: { type: Object, default: null },
79
79
  preset: { type: Object, default: null },
80
80
  response_id: { type: String, default: null },
81
- turnId: { type: String },
82
- traceId: { type: String },
83
81
  triggeredBy: { type: String, default: null }
84
82
  }, { timestamps: true });
85
83
 
@@ -97,10 +95,6 @@ messageSchema.index({ createdAt: -1 }, { name: 'global_sort_idx' });
97
95
 
98
96
  messageSchema.index({ message_id: 1 }, { name: 'message_id_idx', sparse: true });
99
97
 
100
- messageSchema.index({ turnId: 1 }, { name: 'turn_id_idx', sparse: true });
101
-
102
- messageSchema.index({ traceId: 1 }, { name: 'trace_id_idx', sparse: true });
103
-
104
98
  messageSchema.index({ triggeredBy: 1, createdAt: -1 }, { name: 'triggered_by_idx', sparse: true });
105
99
 
106
100
  const Message = mongoose.model('Message', messageSchema);
@@ -90,14 +90,6 @@ const presetRouteDefinitions = {
90
90
  'POST /cache/clear': 'clearPresetCacheController'
91
91
  };
92
92
 
93
- const cohortRouteDefinitions = {
94
- 'POST /cache/clear': 'clearCohortCacheController'
95
- };
96
-
97
- const traceRouteDefinitions = {
98
- 'GET /:traceId': 'getTraceController'
99
- };
100
-
101
93
  const createRouter = (routeDefinitions, controllers) => {
102
94
  const router = express.Router();
103
95
 
@@ -123,7 +115,6 @@ const messageController = require('../controllers/messageController');
123
115
  const messageStatusController = require('../controllers/messageStatusController');
124
116
  const patientController = require('../controllers/patientController');
125
117
  const presetController = require('../controllers/presetController');
126
- const cohortController = require('../controllers/cohortController');
127
118
  const qualityMessageController = require('../controllers/qualityMessageController');
128
119
  const templateController = require('../controllers/templateController');
129
120
  const templateFlowController = require('../controllers/templateFlowController');
@@ -131,7 +122,6 @@ const flowDataExchangeController = require('../controllers/flowDataExchangeContr
131
122
  const uploadController = require('../controllers/uploadController');
132
123
  const dashboardController = require('../controllers/dashboardController');
133
124
  const prescriptionController = require('../controllers/prescriptionController');
134
- const traceController = require('../controllers/traceController');
135
125
 
136
126
  const builtInControllers = {
137
127
  // Assistant controllers
@@ -191,9 +181,6 @@ const builtInControllers = {
191
181
  // Preset controllers
192
182
  clearPresetCacheController: presetController.clearPresetCacheController,
193
183
 
194
- // Cohort controllers
195
- clearCohortCacheController: cohortController.clearCohortCacheController,
196
-
197
184
  // Template controllers
198
185
  createTemplate: templateController.createTemplate,
199
186
  listTemplates: templateController.listTemplates,
@@ -221,10 +208,7 @@ const builtInControllers = {
221
208
  getDashboardController: dashboardController.getDashboardController,
222
209
  getDashboardStatsControllerById: dashboardController.getDashboardStatsControllerById,
223
210
  getDashboardTrendController: dashboardController.getDashboardTrendController,
224
- updateDashboardControllerById: dashboardController.updateDashboardControllerById,
225
-
226
- // Trace controllers
227
- getTraceController: traceController.getTraceController
211
+ updateDashboardControllerById: dashboardController.updateDashboardControllerById
228
212
  };
229
213
 
230
214
  const setupDefaultRoutes = (app) => {
@@ -239,11 +223,9 @@ const setupDefaultRoutes = (app) => {
239
223
  app.use('/api/message', createRouter(messageRouteDefinitions, builtInControllers));
240
224
  app.use('/api/patient', createRouter(patientRouteDefinitions, builtInControllers));
241
225
  app.use('/api/preset', createRouter(presetRouteDefinitions, builtInControllers));
242
- app.use('/api/cohort', createRouter(cohortRouteDefinitions, builtInControllers));
243
226
  app.use('/api/template', createRouter(templateRouteDefinitions, builtInControllers));
244
227
  app.use('/api/prescription', createRouter(prescriptionRouteDefinitions, builtInControllers));
245
228
  app.use('/api/dashboard', createRouter(dashboardRouteDefinitions, builtInControllers));
246
- app.use('/api/trace', createRouter(traceRouteDefinitions, builtInControllers));
247
229
  };
248
230
 
249
231
  module.exports = {
@@ -253,11 +235,9 @@ module.exports = {
253
235
  messageRoutes: messageRouteDefinitions,
254
236
  patientRoutes: patientRouteDefinitions,
255
237
  presetRoutes: presetRouteDefinitions,
256
- cohortRoutes: cohortRouteDefinitions,
257
238
  templateRoutes: templateRouteDefinitions,
258
239
  prescriptionRoutes: prescriptionRouteDefinitions,
259
240
  dashboardRoutes: dashboardRouteDefinitions,
260
- traceRoutes: traceRouteDefinitions,
261
241
 
262
242
  createRouter,
263
243
  setupDefaultRoutes
@@ -78,8 +78,7 @@ async function getRecords(baseID, tableName) {
78
78
 
79
79
  async function getRecordByFilter(baseID, tableName, filter, view = 'Grid view', fields) {
80
80
  try {
81
- const selectOptions = { filterByFormula: filter };
82
- if (view) selectOptions.view = view;
81
+ const selectOptions = { filterByFormula: filter, view };
83
82
  if (fields && fields.length) selectOptions.fields = fields;
84
83
  return await withAirtableRetry(
85
84
  () => collectRecords(getBase(baseID)(tableName).select(selectOptions)),
@@ -4,13 +4,10 @@ const { BaseDto } = require('./BaseDto');
4
4
 
5
5
  const dateTime = z.iso.datetime({ offset: true });
6
6
 
7
- const ESCALATION_PRIORITIES = ['critical', 'high', 'medium', 'low', 'routine', 'urgent', 'asap', 'stat'];
8
- const ACTIVE_CARE_PLAN_STATUSES = ['armed', 'fired_reminder', 'fired_team'];
9
-
10
7
  const escalationStepSchema = z.strictObject({
11
8
  recipient: z.enum(['patient', 'care_team']),
12
9
  message: z.string(),
13
- priority: z.enum(ESCALATION_PRIORITIES).nullable().default(null),
10
+ priority: z.enum(['critical', 'high', 'medium', 'low', 'routine', 'urgent', 'asap', 'stat']).nullable().default(null),
14
11
  actionRequested: z.string().nullable().default(null),
15
12
  firesAfterSilenceMinutes: z.number().int().nonnegative().nullable().default(null),
16
13
  });
@@ -24,7 +21,7 @@ const carePlanSchema = z.strictObject({
24
21
  awaitingCharacterization: z.string(),
25
22
  whyThisMatters: z.string(),
26
23
  creationReasoning: z.string(),
27
- status: z.enum([...ACTIVE_CARE_PLAN_STATUSES, 'auto_resolved', 'cancelled']).default('armed'),
24
+ status: z.enum(['armed', 'fired_reminder', 'fired_team', 'auto_resolved', 'cancelled']).default('armed'),
28
25
  createdAt: dateTime,
29
26
  resolvedAt: dateTime.nullable().default(null),
30
27
  resolvedReason: z.string().nullable().default(null),
@@ -47,8 +44,4 @@ class ContingencySafetyNet extends BaseDto {}
47
44
 
48
45
  ContingencySafetyNet.schema = schema;
49
46
 
50
- module.exports = {
51
- ContingencySafetyNet,
52
- ESCALATION_PRIORITIES,
53
- ACTIVE_CARE_PLAN_STATUSES,
54
- };
47
+ module.exports = { ContingencySafetyNet };
@@ -16,10 +16,15 @@ const baseFields = {
16
16
 
17
17
  const notifyEscalation = z.strictObject({
18
18
  ...baseFields,
19
- kind: z.enum(['page_navigator', 'page_oncologist', 'notify_911_guidance', 'schedule_followup']),
19
+ kind: z.enum(['page_navigator', 'page_oncologist', 'notify_911_guidance']),
20
20
  recipient: z.string().nullable().default(null),
21
21
  requiresAck: z.boolean().default(false),
22
- fireAt: dateTime.nullable().default(null),
22
+ });
23
+
24
+ const followupEscalation = z.strictObject({
25
+ ...baseFields,
26
+ kind: z.literal('schedule_followup'),
27
+ fireAt: dateTime,
23
28
  followupPurpose: z.string().nullable().default(null),
24
29
  });
25
30
 
@@ -30,7 +35,7 @@ const ticketEscalation = z.strictObject({
30
35
  slaMinutes: z.number().int().nullable().default(null),
31
36
  });
32
37
 
33
- const schema = z.discriminatedUnion('kind', [notifyEscalation, ticketEscalation]);
38
+ const schema = z.discriminatedUnion('kind', [notifyEscalation, followupEscalation, ticketEscalation]);
34
39
 
35
40
  class DispatchedEscalation extends BaseDto {}
36
41
 
@@ -21,8 +21,8 @@ const schema = z.strictObject({
21
21
  .refine((d) => (d.status === 'cancelled') === (d.cancelReason !== null), {
22
22
  message: 'cancelReason is set iff status is cancelled',
23
23
  })
24
- .refine((d) => d.recipientKind !== 'related_person' || (typeof d.recipientRefId === 'string' && d.recipientRefId.length > 0), {
25
- message: 'a related_person recipient requires a non-empty recipientRefId',
24
+ .refine((d) => d.recipientKind !== 'related_person' || d.recipientRefId !== null, {
25
+ message: 'a related_person recipient requires recipientRefId',
26
26
  });
27
27
 
28
28
  class Reminder extends BaseDto {}
@@ -123,8 +123,6 @@ class MongoStorage {
123
123
  prompt: messageData.prompt || null,
124
124
  preset: messageData.preset || null,
125
125
  response_id: messageData.response_id || null,
126
- turnId: messageData.turnId || undefined,
127
- traceId: messageData.traceId || undefined,
128
126
  statusInfo: messageData.statusInfo,
129
127
  clinical_context: messageData.clinicalContext || null,
130
128
  triggeredBy: messageData.triggeredBy || null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "5.9.0-dev.921",
3
+ "version": "6.0.0-dev.611",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",
@@ -1,68 +0,0 @@
1
- const { Config_ID } = require('../../config/airtableConfig');
2
- const runtimeConfig = require('../../config/runtimeConfig');
3
-
4
- const { stripWhatsAppFormat } = require('../../helpers/twilioHelper');
5
- const { getRecordByFilter } = require('../../services/airtableService');
6
-
7
- const { logger } = require('../../utils/logger');
8
- const MapCache = require('../../utils/MapCache');
9
-
10
- const COHORT_TABLE = 'clinical_cohort';
11
- const CACHE_TTL = 5 * 60 * 1000;
12
- const CACHE_KEY = 'cohort';
13
- const LANES = ['preset', 'bridge', 'fhir_write', 'fhir_read'];
14
-
15
- const cache = new MapCache({ maxSize: 1, ttl: CACHE_TTL });
16
- let inFlightRead = null;
17
- let generation = 0;
18
-
19
- function isCohortEnabled() {
20
- return runtimeConfig.get('COHORT_ENABLED', 'false') === 'true';
21
- }
22
-
23
- async function readRegistry() {
24
- const records = await getRecordByFilter(Config_ID, COHORT_TABLE, 'TRUE()');
25
- if (!records) throw new Error('cohort registry could not be read from the Config base');
26
-
27
- const lanesByCode = new Map();
28
- for (const row of records) {
29
- if (!row.whatsapp_id || row.status !== 'active') continue;
30
- lanesByCode.set(stripWhatsAppFormat(row.whatsapp_id), new Set(row.lanes || []));
31
- }
32
- return lanesByCode;
33
- }
34
-
35
- async function load() {
36
- const cached = cache.get(CACHE_KEY);
37
- if (cached) return cached;
38
- if (!inFlightRead) {
39
- const readGeneration = generation;
40
- inFlightRead = readRegistry()
41
- .then((lanesByCode) => {
42
- if (readGeneration === generation) cache.set(CACHE_KEY, lanesByCode);
43
- return lanesByCode;
44
- })
45
- .finally(() => { inFlightRead = null; });
46
- }
47
- return inFlightRead;
48
- }
49
-
50
- async function isInCohort(code, lane) {
51
- if (!LANES.includes(lane)) throw new Error(`unknown cohort lane: ${lane}`);
52
- if (!code || !isCohortEnabled()) return false;
53
- try {
54
- const lanes = (await load()).get(stripWhatsAppFormat(code));
55
- return Boolean(lanes && lanes.has(lane));
56
- } catch (error) {
57
- logger.error('[cohortConfig] registry read failed; gate fails closed', { code, lane, error: error.message });
58
- return false;
59
- }
60
- }
61
-
62
- function clearCohortCache() {
63
- generation += 1;
64
- cache.clear();
65
- inFlightRead = null;
66
- }
67
-
68
- module.exports = { isInCohort, clearCohortCache };
@@ -1,42 +0,0 @@
1
- const { Config_ID } = require('../../config/airtableConfig');
2
-
3
- const { getRecordByFilter } = require('../../services/airtableService');
4
-
5
- const { logger } = require('../../utils/logger');
6
- const MapCache = require('../../utils/MapCache');
7
-
8
- const CONFIG_TABLE = 'registry';
9
- const CACHE_TTL = 5 * 60 * 1000;
10
- const CACHE_KEY = 'composerConfig';
11
-
12
- const cache = new MapCache({ maxSize: 1, ttl: CACHE_TTL });
13
-
14
- async function load() {
15
- const cached = cache.get(CACHE_KEY);
16
- if (cached) return cached;
17
-
18
- const records = await getRecordByFilter(Config_ID, CONFIG_TABLE, 'TRUE()');
19
- if (!records) {
20
- logger.warn('[composerConfig] could not read composer prompt blocks; composer will fall back to legacy render');
21
- const fallback = { skillPrompt: '', constitution: '' };
22
- cache.set(CACHE_KEY, fallback);
23
- return fallback;
24
- }
25
- const byKey = Object.fromEntries(records.map((r) => [r.config, r.value]));
26
- const config = {
27
- skillPrompt: byKey.COMPOSER_SKILL_PROMPT || '',
28
- constitution: byKey.COMPOSER_CONSTITUTION || '',
29
- };
30
- cache.set(CACHE_KEY, config);
31
- return config;
32
- }
33
-
34
- async function getPromptBlocks() {
35
- const { skillPrompt, constitution } = await load();
36
- if (!skillPrompt || !constitution) return null;
37
- return { skillPrompt, constitution };
38
- }
39
-
40
- module.exports = {
41
- getPromptBlocks,
42
- };