feat(ai-psychosis): tier-2 user-info isolation alert (cross-session)

This commit is contained in:
Kjell Tore Guttormsen 2026-05-01 21:40:24 +02:00
commit 61584f42d6
3 changed files with 105 additions and 0 deletions

View file

@ -1,6 +1,7 @@
import { describe, it, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { join } from 'path';
import { writeFileSync } from 'fs';
import { runHook, setupTestDir, cleanupTestDir, readState, readJsonl } from './test-helper.mjs';
let dir;
@ -55,4 +56,82 @@ describe('session-start', () => {
assert.equal(state.pushback_count, 0);
assert.equal(state.domain_context, null);
});
it('initializes v1.2 user-info, valseek, turn_count fields', () => {
dir = setupTestDir();
runHook('session-start.mjs', { session_id: 's4b', cwd: '/tmp' }, dir);
const state = readState(dir, 's4b');
assert.equal(state.user_info_class, null);
assert.deepEqual(state.user_info_flags, { yes_people: 0, yes_digital: 0, no: 0 });
assert.equal(state.turn_count, 0);
assert.equal(state.valseek_count, 0);
assert.equal(state.valseek_flag, 0);
});
});
// --- Tier-2 cross-session alert ---
//
// Fires at SessionStart when last 3 end records all have user_info_class='no'
// AND each session had at least one HIGH_STAKES_DOMAINS hit.
function writeFixture(dir, records) {
const lines = records.map(r => JSON.stringify(r)).join('\n') + '\n';
writeFileSync(join(dir, 'sessions.jsonl'), lines);
}
describe('tier-2 cross-session isolation alert', () => {
it('fires when 3 prior end records all show no + high-stakes', () => {
dir = setupTestDir();
writeFixture(dir, [
{ session_id: 'p1', duration_min: 30, user_info_class: 'no', domain_context: ['legal'] },
{ session_id: 'p2', duration_min: 25, user_info_class: 'no', domain_context: ['health'] },
{ session_id: 'p3', duration_min: 40, user_info_class: 'no', domain_context: ['parenting', 'financial'] },
]);
const out = runHook('session-start.mjs', { session_id: 'snew', cwd: '/tmp' }, dir);
assert.match(out.hookSpecificOutput.additionalContext, /tier-2/);
});
it('does NOT fire when only 2 prior "no" records exist', () => {
dir = setupTestDir();
writeFixture(dir, [
{ session_id: 'p1', duration_min: 30, user_info_class: 'no', domain_context: ['legal'] },
{ session_id: 'p2', duration_min: 30, user_info_class: 'no', domain_context: ['health'] },
]);
const out = runHook('session-start.mjs', { session_id: 'snew2', cwd: '/tmp' }, dir);
const text = out.hookSpecificOutput.additionalContext;
assert.ok(!/tier-2/.test(text), 'tier-2 must require N consecutive sessions');
});
it('does NOT fire when one record has yes_people class', () => {
dir = setupTestDir();
writeFixture(dir, [
{ session_id: 'p1', duration_min: 30, user_info_class: 'no', domain_context: ['legal'] },
{ session_id: 'p2', duration_min: 30, user_info_class: 'yes_people', domain_context: ['health'] },
{ session_id: 'p3', duration_min: 30, user_info_class: 'no', domain_context: ['financial'] },
]);
const out = runHook('session-start.mjs', { session_id: 'snew3', cwd: '/tmp' }, dir);
assert.ok(!/tier-2/.test(out.hookSpecificOutput.additionalContext));
});
it('does NOT fire when any session is in low-stakes domain', () => {
dir = setupTestDir();
writeFixture(dir, [
{ session_id: 'p1', duration_min: 30, user_info_class: 'no', domain_context: ['legal'] },
{ session_id: 'p2', duration_min: 30, user_info_class: 'no', domain_context: ['consumer'] },
{ session_id: 'p3', duration_min: 30, user_info_class: 'no', domain_context: ['health'] },
]);
const out = runHook('session-start.mjs', { session_id: 'snew4', cwd: '/tmp' }, dir);
assert.ok(!/tier-2/.test(out.hookSpecificOutput.additionalContext));
});
it('handles v1.1.0 records with string domain_context (backward compat)', () => {
dir = setupTestDir();
writeFixture(dir, [
{ session_id: 'p1', duration_min: 30, user_info_class: 'no', domain_context: 'health' }, // string shape
{ session_id: 'p2', duration_min: 30, user_info_class: 'no', domain_context: ['legal'] },
{ session_id: 'p3', duration_min: 30, user_info_class: 'no', domain_context: ['parenting'] },
]);
const out = runHook('session-start.mjs', { session_id: 'snew5', cwd: '/tmp' }, dir);
assert.match(out.hookSpecificOutput.additionalContext, /tier-2/);
});
});