import test from 'node:test'; import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; const packagePath = new URL('../package.json', import.meta.url); const domainPath = new URL('../src/domain.js', import.meta.url); const servicePath = new URL('../src/hermes-agent-service.js', import.meta.url); const appPath = new URL('../app.js', import.meta.url); test('expanded escalation suites are first-class gates in the default npm test run', async () => { const packageJson = JSON.parse(await readFile(packagePath, 'utf8')); const script = packageJson.scripts.test; assert.match(script, /tests\/symptom-escalation\.regression\.test\.js/, 'domain regression matrix must run in CI'); assert.match(script, /tests\/escalation-boundary\.test\.js/, 'authoritative boundary suite must run in CI'); }); test('the authoritative zero-Hermes-call invariant stays wired at the single chat gate', async () => { const [service, app] = await Promise.all([ readFile(servicePath, 'utf8'), readFile(appPath, 'utf8'), ]); // Server: urgent detection happens before any Hermes turn is spawned. const chatIndex = service.indexOf('async chat('); assert.ok(chatIndex > 0, 'chat entry point exists'); const chatBody = service.slice(chatIndex); const urgentGate = chatBody.indexOf('detectUrgentText(message).urgent || hasUrgentLedgerContext(ledger)'); const runTurnCall = chatBody.indexOf('await runTurn('); assert.ok(urgentGate > 0, 'service must screen message and ledger urgency'); assert.ok(runTurnCall > 0, 'service must call the agent adapter'); assert.ok(urgentGate < runTurnCall, 'urgent override must execute before the Hermes turn'); assert.match(chatBody, /safetyOverride: true/, 'override response is explicit'); // Browser: the same deterministic rules intercept before the network call. assert.match(app, /detectUrgentText\(message\)\.urgent\|\|hasUrgentLedgerContext\(ledgerForAgent\(\)\)/); assert.match(app, /urgentChatMessage/); }); test('detection logic stays centralized in the shared domain module', async () => { const domain = await readFile(domainPath, 'utf8'); // One frozen pattern table drives text detection; no parallel detector copies exist. assert.match(domain, /const URGENT_TEXT_PATTERNS = Object\.freeze\(/); assert.match(domain, /const URGENT_KEYS = /); assert.equal([...domain.matchAll(/URGENT_MESSAGE/g)].length >= 3, true, 'flags, ledger, and chat paths share one urgent copy constant'); });