fix: recognize past-tense vomiting in chat safety
All checks were successful
Quality gates / quality (pull_request) Successful in 1m20s

This commit is contained in:
Timmy 2026-08-20 16:57:09 +00:00
parent cc1fc6ee36
commit 1935ecc345
5 changed files with 5 additions and 3 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 KiB

After

Width:  |  Height:  |  Size: 311 KiB

View File

@ -4,7 +4,7 @@ const URGENT_TEXT_PATTERNS = Object.freeze([
['blood', /\b(?:rectal bleeding|bleeding from (?:the )?(?:rectum|bottom)|blood(?:y)? (?:in|on|with) (?:my |the )?(?:stool|poop|bowel movement)|(?:stool|poop) (?:has|contains|with) blood)\b/i], ['blood', /\b(?:rectal bleeding|bleeding from (?:the )?(?:rectum|bottom)|blood(?:y)? (?:in|on|with) (?:my |the )?(?:stool|poop|bowel movement)|(?:stool|poop) (?:has|contains|with) blood)\b/i],
['blackOrDarkRed', /\b(?:(?:black|dark[- ]?red) (?:stool|poop|bowel movement)|(?:stool|poop|bowel movement) (?:is|looks?) (?:black|dark[- ]?red))s?\b/i], ['blackOrDarkRed', /\b(?:(?:black|dark[- ]?red) (?:stool|poop|bowel movement)|(?:stool|poop|bowel movement) (?:is|looks?) (?:black|dark[- ]?red))s?\b/i],
['severePain', /\b(?:severe|constant|unrelenting) (?:abdominal|stomach|belly) pain\b/i], ['severePain', /\b(?:severe|constant|unrelenting) (?:abdominal|stomach|belly) pain\b/i],
['vomiting', /\b(?:vomit(?:ing|ed)?|throwing up)\b/i], ['vomiting', /\b(?:vomit(?:ing|ed|s)?|throw(?:ing|s)? up|threw up|thrown up)\b/i],
['fever', /\bfever(?:ish)?\b/i], ['fever', /\bfever(?:ish)?\b/i],
['cannotPassGas', /\b(?:cannot|can[']?t|cant|unable to|not able to) pass gas\b/i], ['cannotPassGas', /\b(?:cannot|can[']?t|cant|unable to|not able to) pass gas\b/i],
]); ]);

View File

@ -50,6 +50,7 @@ test('detects common urgent symptom language without matching unrelated blood wo
'My stool is black', 'My stool is black',
'I have severe stomach pain', 'I have severe stomach pain',
'I am throwing up and have a fever', 'I am throwing up and have a fever',
'I threw up',
'I am unable to pass gas', 'I am unable to pass gas',
]) assert.equal(detectUrgentText(message).urgent, true, message); ]) assert.equal(detectUrgentText(message).urgent, true, message);
assert.equal(detectUrgentText('My blood pressure was checked').urgent, false); assert.equal(detectUrgentText('My blood pressure was checked').urgent, false);

View File

@ -128,6 +128,7 @@ test('authoritative chat service intercepts urgent message and ledger symptoms b
await service.unlock({ origin, accessCode: 'test-agent-access-code-2026' }); await service.unlock({ origin, accessCode: 'test-agent-access-code-2026' });
const messageResult = await service.chat({ origin, cookieToken: 'safety-cookie', payload: { message: 'I have rectal bleeding', ledger: [] } }); const messageResult = await service.chat({ origin, cookieToken: 'safety-cookie', payload: { message: 'I have rectal bleeding', ledger: [] } });
const pastTenseVomitingResult = await service.chat({ origin, cookieToken: 'safety-cookie', payload: { message: 'I threw up', ledger: [] } });
const ledgerResult = await service.chat({ origin, cookieToken: 'safety-cookie', payload: { const ledgerResult = await service.chat({ origin, cookieToken: 'safety-cookie', payload: {
message: 'What does my journal show?', message: 'What does my journal show?',
ledger: [{ bristolType: 4, color: 'brown', note: '', symptoms: { cannotPassGas: true } }], ledger: [{ bristolType: 4, color: 'brown', note: '', symptoms: { cannotPassGas: true } }],
@ -138,7 +139,7 @@ test('authoritative chat service intercepts urgent message and ledger symptoms b
} }); } });
assert.equal(calls.length, 0); assert.equal(calls.length, 0);
for (const result of [messageResult, ledgerResult, noteResult]) { for (const result of [messageResult, pastTenseVomitingResult, ledgerResult, noteResult]) {
assert.equal(result.safetyOverride, true); assert.equal(result.safetyOverride, true);
assert.match(result.reply, /medical help/i); assert.match(result.reply, /medical help/i);
assert.doesNotMatch(result.reply, /unsafe upstream/i); assert.doesNotMatch(result.reply, /unsafe upstream/i);

View File

@ -38,7 +38,7 @@ assert.equal(chatRequest.message, 'What pattern do you see?');
assert.ok(Array.isArray(chatRequest.ledger)); assert.ok(Array.isArray(chatRequest.ledger));
assert.equal(JSON.stringify(chatRequest).includes('photoDataUrl'), false, 'photos never enter chat context'); assert.equal(JSON.stringify(chatRequest).includes('photoDataUrl'), false, 'photos never enter chat context');
const previousRequest = chatRequest; const previousRequest = chatRequest;
await page.locator('#chat-message').fill('I am unable to pass gas'); await page.locator('#chat-message').fill('I threw up');
await page.locator('#send-chat').click(); await page.locator('#send-chat').click();
await page.waitForSelector('.bubble.timmy >> text=Pause and get medical help'); await page.waitForSelector('.bubble.timmy >> text=Pause and get medical help');
assert.equal(chatRequest, previousRequest, 'urgent language must be intercepted before the Hermes request'); assert.equal(chatRequest, previousRequest, 'urgent language must be intercepted before the Hermes request');