@peopl-health/nexus 5.11.0-dev.1125 → 5.11.0-dev.1126
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/lib/clinical/AssistantProcessor.js +5 -2
- package/lib/clinical/context/ToolRuntimeContext.js +10 -4
- package/lib/clinical/services/clinicalAirtableService.js +19 -11
- package/lib/clinical/services/shadowService.js +1 -1
- package/lib/clinical/tools/extractClinicalInfoTool.js +1 -0
- package/lib/clinical/tools/schedulePatientReminderTool.js +12 -4
- package/package.json +1 -1
|
@@ -45,7 +45,11 @@ class AssistantProcessor {
|
|
|
45
45
|
|
|
46
46
|
if (thread.isInCohort && !runOptions.toolRuntimeContext && runOptions.runId && thread.code) {
|
|
47
47
|
try {
|
|
48
|
-
runOptions.toolRuntimeContext = new ToolRuntimeContext({
|
|
48
|
+
runOptions.toolRuntimeContext = new ToolRuntimeContext({
|
|
49
|
+
patientCode: thread.code,
|
|
50
|
+
turnId: runOptions.runId,
|
|
51
|
+
shouldContinue: runOptions.shouldContinue,
|
|
52
|
+
});
|
|
49
53
|
} catch (error) {
|
|
50
54
|
logger.warn('[AssistantProcessor] turn will run untraced', { code: thread.code, error: error.message });
|
|
51
55
|
}
|
|
@@ -83,7 +87,6 @@ class AssistantProcessor {
|
|
|
83
87
|
outbound: output,
|
|
84
88
|
completed: runResult?.completed,
|
|
85
89
|
predictionTimeMs,
|
|
86
|
-
shouldContinue: runOptions.shouldContinue,
|
|
87
90
|
});
|
|
88
91
|
} catch (error) {
|
|
89
92
|
logger.warn('[AssistantProcessor] trace not stored', {
|
|
@@ -8,6 +8,7 @@ class ToolRuntimeContext {
|
|
|
8
8
|
repos = {},
|
|
9
9
|
contextSource = null,
|
|
10
10
|
shadow = false,
|
|
11
|
+
shouldContinue = null,
|
|
11
12
|
} = {}) {
|
|
12
13
|
if (typeof patientCode !== 'string' || !patientCode) {
|
|
13
14
|
throw new Error('ToolRuntimeContext requires a non-empty patientCode');
|
|
@@ -22,6 +23,7 @@ class ToolRuntimeContext {
|
|
|
22
23
|
this.patientCode = patientCode;
|
|
23
24
|
this.turnId = turnId;
|
|
24
25
|
this.sessionId = sessionId;
|
|
26
|
+
this.shouldContinue = shouldContinue;
|
|
25
27
|
this.repos = repos;
|
|
26
28
|
this.contextSource = contextSource;
|
|
27
29
|
this.enabledSkills = [];
|
|
@@ -42,13 +44,17 @@ class ToolRuntimeContext {
|
|
|
42
44
|
return toolData;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
isLive() {
|
|
48
|
+
return typeof this.shouldContinue !== 'function' || this.shouldContinue();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async store({ outbound = null, completed = false, predictionTimeMs = null } = {}) {
|
|
46
52
|
this.trace.setSignals({ activatedSkills: [...this.activatedSkills].sort() });
|
|
47
53
|
this.trace.setOutbound(outbound);
|
|
48
|
-
this.
|
|
54
|
+
const superseded = !this.isLive();
|
|
55
|
+
this.trace.setStatus(superseded ? 'superseded' : (completed ? 'completed' : 'incomplete'));
|
|
49
56
|
this.trace.setTimings({ predictionTimeMs });
|
|
50
|
-
|
|
51
|
-
if (!superseded) await this.trace.save();
|
|
57
|
+
await this.trace.save();
|
|
52
58
|
}
|
|
53
59
|
|
|
54
60
|
setSafetyArtifact(artifact) {
|
|
@@ -21,16 +21,21 @@ const INTAKE_SOURCE_TO_AIRTABLE = {
|
|
|
21
21
|
team_relay: 'doctor',
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
async function auditedWrite({ trace, code, table, write }) {
|
|
24
|
+
async function auditedWrite({ trace, code, table, write, isLive = null }) {
|
|
25
25
|
const entry = { table, at: new Date().toISOString(), ok: false };
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
if (isLive && !isLive()) {
|
|
27
|
+
entry.skipped = 'superseded';
|
|
28
|
+
logger.info('[clinicalAirtableService] Airtable write skipped — turn superseded', { table, code });
|
|
29
|
+
} else {
|
|
30
|
+
try {
|
|
31
|
+
const result = await write();
|
|
32
|
+
entry.ok = true;
|
|
33
|
+
const record = Array.isArray(result) ? result[0] : result;
|
|
34
|
+
if (record?.id) entry.recordId = record.id;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
entry.error = err?.message || String(err);
|
|
37
|
+
logger.error('[clinicalAirtableService] Airtable write failed', { table, code, error: entry.error });
|
|
38
|
+
}
|
|
34
39
|
}
|
|
35
40
|
if (trace?.setSignals) {
|
|
36
41
|
const existing = Array.isArray(trace.signals?.airtableWrites) ? trace.signals.airtableWrites : [];
|
|
@@ -92,10 +97,11 @@ async function recordEmergency({ code, trace, symptom, suspectedCondition = '',
|
|
|
92
97
|
});
|
|
93
98
|
}
|
|
94
99
|
|
|
95
|
-
async function recordClinicalIntake({ code, trace, messageRaw, source = 'self' }) {
|
|
100
|
+
async function recordClinicalIntake({ code, trace, messageRaw, source = 'self', isLive = null }) {
|
|
96
101
|
return auditedWrite({
|
|
97
102
|
trace,
|
|
98
103
|
code,
|
|
104
|
+
isLive,
|
|
99
105
|
table: 'intake',
|
|
100
106
|
write: async () => {
|
|
101
107
|
const patientId = await findIntakePatientRecordId(code);
|
|
@@ -110,12 +116,13 @@ async function recordClinicalIntake({ code, trace, messageRaw, source = 'self' }
|
|
|
110
116
|
});
|
|
111
117
|
}
|
|
112
118
|
|
|
113
|
-
async function recordReminder({ code, trace, actionType, reminderId, reminderDateUtc, reminderType, description }) {
|
|
119
|
+
async function recordReminder({ code, trace, actionType, reminderId, reminderDateUtc, reminderType, description, isLive = null }) {
|
|
114
120
|
const type = REMINDER_TYPE_TO_AIRTABLE[reminderType] || reminderType;
|
|
115
121
|
if (actionType === 'update') {
|
|
116
122
|
return auditedWrite({
|
|
117
123
|
trace,
|
|
118
124
|
code,
|
|
125
|
+
isLive,
|
|
119
126
|
table: 'reminder',
|
|
120
127
|
write: async () => {
|
|
121
128
|
const updated = await airtableService.updateRecordByFilter(
|
|
@@ -133,6 +140,7 @@ async function recordReminder({ code, trace, actionType, reminderId, reminderDat
|
|
|
133
140
|
return auditedWrite({
|
|
134
141
|
trace,
|
|
135
142
|
code,
|
|
143
|
+
isLive,
|
|
136
144
|
table: 'reminder',
|
|
137
145
|
write: () => airtableService.addLinkedRecord(
|
|
138
146
|
Monitoreo_ID,
|
|
@@ -27,7 +27,7 @@ async function runShadowTurn({
|
|
|
27
27
|
const shadowAssistant = resolveAssistant(shadowThread);
|
|
28
28
|
|
|
29
29
|
const context = new ToolRuntimeContext({
|
|
30
|
-
patientCode: code, turnId: runId, sessionId, shadow: true,
|
|
30
|
+
patientCode: code, turnId: runId, sessionId, shadow: true, shouldContinue,
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
const runOptions = {
|
|
@@ -107,11 +107,18 @@ async function handler(args = {}, context = {}) {
|
|
|
107
107
|
};
|
|
108
108
|
trace.setSignals({ reminderIntents: [...existing, intent] });
|
|
109
109
|
|
|
110
|
+
const isLive = runtime.isLive?.bind(runtime);
|
|
111
|
+
const superseded = Boolean(isLive) && !isLive();
|
|
112
|
+
|
|
110
113
|
if (runtime.patientCode) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
114
|
+
if (superseded) {
|
|
115
|
+
logger.info('[schedulePatientReminder] reminder projection skipped — turn superseded', { turnId });
|
|
116
|
+
} else {
|
|
117
|
+
try {
|
|
118
|
+
await dispatchReminder({ intent, patientId: runtime.patientCode });
|
|
119
|
+
} catch (err) {
|
|
120
|
+
logger.warn('[schedulePatientReminder] reminder FHIR projection failed; ack unaffected', { turnId, error: err?.message });
|
|
121
|
+
}
|
|
115
122
|
}
|
|
116
123
|
}
|
|
117
124
|
|
|
@@ -124,6 +131,7 @@ async function handler(args = {}, context = {}) {
|
|
|
124
131
|
reminderDateUtc: fireMoment.toISOString(),
|
|
125
132
|
reminderType,
|
|
126
133
|
description: storedDescription,
|
|
134
|
+
isLive,
|
|
127
135
|
});
|
|
128
136
|
}
|
|
129
137
|
|