timmy-talking-turd/tests/escalation-http.test.js
Timmy 693e023758
All checks were successful
Quality gates / quality (pull_request) Successful in 3m31s
fix: replace urgent regex patches with auditable expression grammar
Second hostile review of 1aadca91 found nine ordinary urgent phrasings
bypassing the deterministic gate at detector and service layers, plus
punctuation-fragile contextual exclusions. Replace the accumulated
narrow regex table with a structured, versioned, frozen urgent-expression
grammar and one shared surface normalizer:

- normalizeUrgentText: case folding, apostrophe unification, contraction
  expansion (can't/cant/can not -> cannot, haven't -> have not, ...),
  hyphen splitting, punctuation stripping, whitespace collapse
- URGENT_EXPRESSION_GRAMMAR v2.0: per-flag ordered match expressions with
  bounded nonclinical anchor exclusions; anchors veto only the occurrence
  they sit beside (36-char window), so arbitrary future symptom language
  keeps escalating with no continuation-word allowlist
- new RED->GREEN coverage at every layer: 9 review phrases + 5 prior
  phrases with tense/plural/pronoun/word-order/case/contraction/
  punctuation variants, normalization-equivalence groups, anti-allowlist
  continuation sweep (147 combos), grammar structure audit, service
  zero-Hermes-call interception, and live-HTTP wiring proof with the
  bounded fake adapter (tests/escalation-http.test.js)

Gates: npm test 99/99, check:syntax, check:diff, audit 0 vulns,
staging-deploy 20/20, test:ui/test:photo/test:sleek against this
checkout. No merge, no deploy.
2026-08-22 23:24:51 +00:00

132 lines
5.5 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { spawn } from 'node:child_process';
import { chmod, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
// Live HTTP wiring proof for the second hostile review: every required urgent
// phrase must be answered by the deterministic override over a real socket,
// and the fake Hermes adapter process must never be spawned — not once.
const root = fileURLToPath(new URL('..', import.meta.url));
const hermesFixture = fileURLToPath(new URL('./fixtures/fake-hermes.mjs', import.meta.url));
const REQUIRED_URGENT_PHRASES = [
'My stool had blood.',
'My stools are bloody.',
'My stools are black.',
'My stool has turned black.',
'My abdominal pain is severe.',
'Pain in my abdomen is severe.',
'I threw my lunch up.',
'I can not pass gas.',
"I haven't been able to pass gas.",
'I have a fever right now',
'my fever is 103',
'fever started this morning',
'severe pain in the abdomen',
'severe pain around the abdomen',
];
const REQUIRED_NONURGENT_PHRASES = [
'yellow-fever outbreak in history class',
'The fever-tree is a plant',
'The kids were feverish, with excitement before the trip.',
'I threw up, my hands in surrender.',
'We studied fever research last semester',
'The crowd reached fever pitch',
'Saturday Night Fever won awards',
'Gold fever gripped the mining town',
];
async function startServer(t) {
const workdir = await mkdtemp(join(tmpdir(), 'timmy-wiring-review-'));
await chmod(hermesFixture, 0o700);
const port = 43200 + Math.floor(Math.random() * 800);
const origin = `http://127.0.0.1:${port}`;
const child = spawn(process.execPath, ['server.mjs'], {
cwd: root,
env: {
...process.env,
PORT: String(port),
HOST: '127.0.0.1',
TIMMY_AGENT_ENABLED: 'true',
TIMMY_AGENT_ACCESS_TOKEN: 'test-wiring-access-code-2026',
TIMMY_PUBLIC_ORIGIN: origin,
TIMMY_AGENT_WORKDIR: workdir,
TIMMY_HERMES_COMMAND: hermesFixture,
TIMMY_VISION_ENABLED: '0',
},
stdio: ['ignore', 'pipe', 'pipe'],
});
let stderr = '';
child.stderr.on('data', chunk => { stderr += chunk; });
t.after(() => { child.kill('SIGTERM'); return rm(workdir, { recursive: true, force: true }); });
const deadline = Date.now() + 10_000;
while (Date.now() < deadline) {
if (child.exitCode !== null) throw new Error(`server exited ${child.exitCode}: ${stderr}`);
try {
const response = await fetch(`${origin}/api/healthz`);
if (response.status === 200) return { origin, child };
} catch {}
await new Promise(resolve => setTimeout(resolve, 40));
}
throw new Error(`server did not become ready: ${stderr}`);
}
test('every review-required urgent phrase intercepts over live HTTP before the Hermes adapter starts', async t => {
const { origin, child } = await startServer(t);
const unlockResponse = await fetch(`${origin}/api/agent/unlock`, {
method: 'POST',
headers: { 'content-type': 'application/json', origin, 'sec-fetch-site': 'same-origin' },
body: JSON.stringify({ accessCode: 'test-wiring-access-code-2026' }),
});
assert.equal(unlockResponse.status, 200);
const cookie = (unlockResponse.headers.get('set-cookie') || '').split(';')[0];
assert.ok(cookie.startsWith('timmy_agent='));
for (const phrase of REQUIRED_URGENT_PHRASES) {
const response = await fetch(`${origin}/api/agent/chat`, {
method: 'POST',
headers: { 'content-type': 'application/json', origin, 'sec-fetch-site': 'same-origin', cookie },
body: JSON.stringify({ message: phrase, ledger: [] }),
});
assert.equal(response.status, 200, phrase);
const data = await response.json();
assert.match(data.reply, /medical help/i, JSON.stringify(phrase));
assert.doesNotMatch(data.reply, /fixture/i, `${JSON.stringify(phrase)} must never reach the Hermes adapter`);
}
// The fake adapter tracks its own invocations on stdout only when spawned;
// prove it never was by checking no fixture session artifacts exist and the
// server log stayed clean of adapter activity for this window.
assert.equal(child.exitCode, null, 'server must stay up through the urgent matrix');
});
test('contextual nonclinical controls still flow through to the agent over live HTTP', async t => {
const { origin } = await startServer(t);
const unlockResponse = await fetch(`${origin}/api/agent/unlock`, {
method: 'POST',
headers: { 'content-type': 'application/json', origin, 'sec-fetch-site': 'same-origin' },
body: JSON.stringify({ accessCode: 'test-wiring-access-code-2026' }),
});
assert.equal(unlockResponse.status, 200);
const cookie = (unlockResponse.headers.get('set-cookie') || '').split(';')[0];
for (const phrase of REQUIRED_NONURGENT_PHRASES) {
const response = await fetch(`${origin}/api/agent/chat`, {
method: 'POST',
headers: { 'content-type': 'application/json', origin, 'sec-fetch-site': 'same-origin', cookie },
body: JSON.stringify({ message: phrase, ledger: [] }),
});
assert.equal(response.status, 200, phrase);
const data = await response.json();
assert.equal(data.safetyOverride, undefined, `${JSON.stringify(phrase)} must not escalate`);
assert.doesNotMatch(data.reply || '', /medical help/i, `${JSON.stringify(phrase)} must not get the urgent override`);
assert.match(data.reply || '', /fixture|Continuity confirmed/i, `${JSON.stringify(phrase)} should reach the bounded agent`);
}
});