@peopl-health/nexus 5.71.6 → 5.71.7

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.
@@ -77,7 +77,7 @@ function gradeEstimateSchema(description) {
77
77
  source: {
78
78
  type: 'string',
79
79
  enum: GRADE_SOURCES,
80
- description: 'Where this grade came from. Required whenever you set best_estimate. `proctcae_composite`: you are carrying over the number the PRO-CTCAE triage already computed — say this even when you agree with it. When what the patient tells you this turn contradicts that number (they can do today what the questionnaire said they could not), what they tell you now wins: grade it yourself as `agent_adjudicated`. `agent_adjudicated`: you read the grade_scale clauses yourself against what the patient said. `carried_forward`: the case\'s previous grade, unchanged, with no new evidence this turn.',
80
+ description: 'Where this grade came from. Required whenever you set best_estimate. `proctcae_composite`: you are carrying over the number the PRO-CTCAE triage already computed — say this even when you agree with it. When what the patient tells you this turn contradicts that number (they can do today what the questionnaire said they could not), what they tell you now wins: when the term has a grade_scale, grade it yourself as `agent_adjudicated`. `agent_adjudicated`: you read the grade_scale clauses yourself against what the patient said. `carried_forward`: the case\'s previous grade, unchanged, with no new evidence this turn.',
81
81
  },
82
82
  matched_grade: {
83
83
  type: ['integer', 'null'],
@@ -70,7 +70,7 @@ function gradeAnchorNotice({ ctcaeTerm, grade, anchor }) {
70
70
  const named = leaned === 'self_care' ? 'self-care' : 'instrumental';
71
71
  if (rulesOutFunctionalPart(leaned, anchor)) {
72
72
  return `Grade ${grade} of ${ctcaeTerm} can only stand on the non-functional part of its clause: the patient is not limited in ${named} ADL. `
73
- + '`reasoning` must name that criterion; if it is not met, record the grade below.';
73
+ + '`reasoning` must name that criterion; if it is not met, record the grade below and name in `improvement_basis` what rules this grade out.';
74
74
  }
75
75
  return `Grade ${grade} of ${ctcaeTerm} was recorded without the ${named} ADL limitation its clause mentions. `
76
76
  + 'That is fine when another part of the clause is met or the whole picture makes it the most likely grade — `reasoning` must name which. '
@@ -52,9 +52,9 @@ const GUARDS = [
52
52
  }),
53
53
  ];
54
54
 
55
- function gradeClaimError({ ctcaeTerm, grade, stated = null, previous = null, restatesPriorEvidence = true, continuesEpisode = false, recordsOngoingRead = false, trace = null }) {
55
+ function gradeClaimError({ ctcaeTerm, grade, stated = null, previous = null, restatesPriorEvidence = true, recordsOngoingRead = false, trace = null }) {
56
56
  const resolved = grade || {};
57
- const claim = { ctcaeTerm, grade: resolved, stated: stated || resolved, previous, restatesPriorEvidence, continuesEpisode, recordsOngoingRead, trace };
57
+ const claim = { ctcaeTerm, grade: resolved, stated: stated || resolved, previous, restatesPriorEvidence, recordsOngoingRead, trace };
58
58
  for (const guard of GUARDS) {
59
59
  const error = guard(claim);
60
60
  if (error) return error;
@@ -121,7 +121,6 @@ function buildAssessment(condition, previous, args, runtime, restatesPriorEviden
121
121
  stated: parseGradeEstimate(args.grade_estimate),
122
122
  previous: previous.grade,
123
123
  restatesPriorEvidence,
124
- continuesEpisode,
125
124
  recordsOngoingRead: ONGOING_READ_EVENTS.includes(normalisedEvent(args)),
126
125
  trace: runtime?.trace || null,
127
126
  });
@@ -175,6 +174,10 @@ function requireCeilingGap(args, event) {
175
174
  return error ? fail(error) : null;
176
175
  }
177
176
 
177
+ function sameGradeAndAnchor(a, b) {
178
+ return !!a && !!b && a.bestEstimate === b.bestEstimate && a.functionalAnchor === b.functionalAnchor;
179
+ }
180
+
178
181
  function sameGrade(a, b) {
179
182
  return JSON.stringify(projectGradeRead(a)) === JSON.stringify(projectGradeRead(b));
180
183
  }
@@ -351,7 +354,9 @@ async function handler(args = {}, context = {}) {
351
354
 
352
355
  const { condition, latestAssessment } = result;
353
356
  const consistency = inputConsistency(args);
354
- const freshRead = ONGOING_READ_EVENTS.includes(event) && hasGradeSignal(args?.grade_estimate) && !isCarriedForward(args.grade_estimate);
357
+ // A hold of the same grade on the same anchor was already advised; repeating it re-asks the patient every turn.
358
+ const heldAsAdvised = event === 'add_evidence' && sameGradeAndAnchor(existing.latestAssessment?.grade, latestAssessment.grade);
359
+ const freshRead = ONGOING_READ_EVENTS.includes(event) && hasGradeSignal(args?.grade_estimate) && !isCarriedForward(args.grade_estimate) && !heldAsAdvised;
355
360
  const gradeNotice = freshRead ? gradeClaimNotice({ ctcaeTerm: condition.ctcaeTerm, grade: latestAssessment.grade }) : null;
356
361
  return JSON.stringify({
357
362
  success: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peopl-health/nexus",
3
- "version": "5.71.6",
3
+ "version": "5.71.7",
4
4
  "description": "Core messaging and assistant library for WhatsApp communication platforms",
5
5
  "keywords": [
6
6
  "whatsapp",