feat(ai-psychosis): add user-info detector (yes_people/yes_digital/no)
This commit is contained in:
parent
39ea46441c
commit
ca6567b501
3 changed files with 259 additions and 0 deletions
|
|
@ -183,6 +183,55 @@ const domainPersonalDevPatterns = [
|
|||
/\b(?:improve|grow|level up) (?:myself|my (?:self-discipline|focus|productivity))\b/i,
|
||||
];
|
||||
|
||||
// v1.2: User-information dimension (paper page 11). Three classes — yes_people,
|
||||
// yes_digital, no. Priority: yes_people > yes_digital > no. Sticky for session.
|
||||
//
|
||||
// "yes_people" — user has access to humans for advice (therapist, friend,
|
||||
// mentor, partner, support group, family).
|
||||
const userInfoPeoplePatterns = [
|
||||
/\bmy (?:therapist|counselor|psychologist|psychiatrist)\b/i,
|
||||
/\bmy (?:doctor|gp|physician|specialist)\b/i,
|
||||
/\bmy (?:friend|best friend|close friend)\b/i,
|
||||
/\bmy (?:partner|spouse|wife|husband|girlfriend|boyfriend)\b/i,
|
||||
/\bmy (?:mom|dad|mother|father|parent|sibling|sister|brother)\b/i,
|
||||
/\bmy (?:mentor|coach|advisor|sponsor)\b/i,
|
||||
/\bmy support group\b/i,
|
||||
/\bI (?:asked|talked to|spoke with|consulted) (?:my|a) (?:friend|therapist|doctor|mentor)\b/i,
|
||||
/\bI (?:told|confided in) (?:my|a) (?:friend|therapist|partner|family)\b/i,
|
||||
/\bmy (?:family|relatives) (?:said|told|think|suggest)\b/i,
|
||||
/\bmy (?:lawyer|attorney|legal counsel)\b/i,
|
||||
/\bmy (?:pastor|priest|rabbi|imam|spiritual (?:teacher|guide))\b/i,
|
||||
/\bmy (?:teacher|professor|tutor)\b/i,
|
||||
/\bmy (?:colleague|coworker|boss|manager)\b/i,
|
||||
/\bI (?:reached out|called) (?:to )?(?:my|a) (?:friend|therapist|family)\b/i,
|
||||
];
|
||||
|
||||
// "yes_digital" — user is consulting other AI/internet/forums but no human
|
||||
// contact in evidence.
|
||||
const userInfoDigitalPatterns = [
|
||||
/\bI (?:googled|searched|looked (?:it|this) up online)\b/i,
|
||||
/\bI read (?:online|on the internet|on a forum|on reddit|on stack overflow)\b/i,
|
||||
/\b(?:chatgpt|gpt|gemini|copilot|another ai|the other ai) (?:said|told|suggested|recommended)\b/i,
|
||||
/\b(?:I |we )?(?:found|saw) (?:an? |the )?(?:forum post|reddit thread|article|blog post)\b/i,
|
||||
/\b(?:youtube|tiktok|twitter|x\.com|instagram) (?:video|post|thread)\b/i,
|
||||
/\baccording to (?:wikipedia|google|the internet|the article)\b/i,
|
||||
/\b(?:I asked|asked) (?:chatgpt|gpt|gemini|claude|another ai|copilot)\b/i,
|
||||
/\b(?:online|the internet) (?:says|claims|suggests)\b/i,
|
||||
/\bsearched (?:for|on) (?:google|stackoverflow|github)\b/i,
|
||||
/\bi watched (?:a youtube|videos? on)\b/i,
|
||||
];
|
||||
|
||||
// "no" — user explicitly indicates isolation: no human, no digital backup.
|
||||
const userInfoNoPatterns = [
|
||||
/\b(?:nobody|no one) knows\b/i,
|
||||
/\bI haven'?t told (?:anyone|anybody|anything to anyone)\b/i,
|
||||
/\bdealing with this alone\b/i,
|
||||
/\bI (?:can'?t|cannot) tell (?:anyone|anybody|my (?:family|friends|therapist))\b/i,
|
||||
/\b(?:I|we) keep (?:this|it) (?:to myself|secret|hidden)\b/i,
|
||||
/\bnobody (?:in my life|around me) (?:would understand|gets it)\b/i,
|
||||
/\bjust me (?:and|with) (?:my|the) (?:thoughts|head|computer|claude)\b/i,
|
||||
];
|
||||
|
||||
for (const p of depPatterns) { if (p.test(prompt)) { depHit = 1; break; } }
|
||||
for (const p of escPatterns) { if (p.test(prompt)) { escHit = 1; break; } }
|
||||
for (const p of fatPatterns) { if (p.test(prompt)) { fatHit = 1; break; } }
|
||||
|
|
@ -202,6 +251,11 @@ let domainSpiritualityHit = 0; for (const p of domainSpiritualityPatterns) { if
|
|||
let domainConsumerHit = 0; for (const p of domainConsumerPatterns) { if (p.test(prompt)) { domainConsumerHit = 1; break; } }
|
||||
let domainPersonalDevHit = 0; for (const p of domainPersonalDevPatterns) { if (p.test(prompt)) { domainPersonalDevHit = 1; break; } }
|
||||
|
||||
// v1.2: User-info detection — three classes with priority yes_people > yes_digital > no.
|
||||
let userInfoPeopleHit = 0; for (const p of userInfoPeoplePatterns) { if (p.test(prompt)) { userInfoPeopleHit = 1; break; } }
|
||||
let userInfoDigitalHit = 0; for (const p of userInfoDigitalPatterns) { if (p.test(prompt)) { userInfoDigitalHit = 1; break; } }
|
||||
let userInfoNoHit = 0; for (const p of userInfoNoPatterns) { if (p.test(prompt)) { userInfoNoHit = 1; break; } }
|
||||
|
||||
// Clear prompt from memory
|
||||
prompt = '';
|
||||
|
||||
|
|
@ -214,6 +268,11 @@ if (fatHit === 1 || escHit === 1) {
|
|||
|
||||
// Update state with new flag counts
|
||||
const state = readState();
|
||||
|
||||
// v1.2: turn_count drives tier-1 user-info alert (Step 9). Defaults to 0 for
|
||||
// pre-v1.2 state files; session-start.mjs seeds it for fresh v1.2 sessions.
|
||||
state.turn_count = (Number(state.turn_count) || 0) + 1;
|
||||
|
||||
const newDep = (Number(state.dep_flags) || 0) + depHit;
|
||||
const newEsc = (Number(state.esc_flags) || 0) + escHit;
|
||||
const newFat = (Number(state.fatigue_flags) || 0) + fatHit;
|
||||
|
|
@ -225,6 +284,30 @@ state.fatigue_flags = newFat;
|
|||
state.val_flags = newVal;
|
||||
state.pushback_count = (Number(state.pushback_count) || 0) + pbReactiveHit + pbPreemptiveHit;
|
||||
|
||||
// v1.2: user-info classification (paper page 11). Priority yes_people > yes_digital > no.
|
||||
// Class is sticky for the session — once set to a "stronger" signal, never
|
||||
// downgrades. Counters always accumulate regardless of class transitions.
|
||||
if (!state.user_info_flags || typeof state.user_info_flags !== 'object') {
|
||||
state.user_info_flags = { yes_people: 0, yes_digital: 0, no: 0 };
|
||||
}
|
||||
if (userInfoPeopleHit) state.user_info_flags.yes_people = (state.user_info_flags.yes_people || 0) + 1;
|
||||
if (userInfoDigitalHit) state.user_info_flags.yes_digital = (state.user_info_flags.yes_digital || 0) + 1;
|
||||
if (userInfoNoHit) state.user_info_flags.no = (state.user_info_flags.no || 0) + 1;
|
||||
|
||||
// Class priority: people > digital > no. Sticky upward, never downward.
|
||||
const RANK = { yes_people: 3, yes_digital: 2, no: 1 };
|
||||
let nextClass = state.user_info_class || null;
|
||||
const candidate = userInfoPeopleHit ? 'yes_people'
|
||||
: userInfoDigitalHit ? 'yes_digital'
|
||||
: userInfoNoHit ? 'no'
|
||||
: null;
|
||||
if (candidate) {
|
||||
const currentRank = nextClass ? (RANK[nextClass] || 0) : 0;
|
||||
const candidateRank = RANK[candidate] || 0;
|
||||
if (candidateRank > currentRank) nextClass = candidate;
|
||||
}
|
||||
state.user_info_class = nextClass;
|
||||
|
||||
// v1.2: domain_context is always an array. Coerce v1.1.0 string shape on read.
|
||||
const anyDomainHit = domainHit
|
||||
|| domainLegalHit || domainParentingHit || domainHealthHit
|
||||
|
|
|
|||
|
|
@ -40,6 +40,13 @@ const state = {
|
|||
val_flags: 0,
|
||||
pushback_count: 0,
|
||||
domain_context: null,
|
||||
// v1.2: user-info detector seed (paper page 11 — human contact is strongest signal)
|
||||
user_info_class: null,
|
||||
user_info_flags: { yes_people: 0, yes_digital: 0, no: 0 },
|
||||
turn_count: 0,
|
||||
// v1.2: validation-seeking detector seed
|
||||
valseek_count: 0,
|
||||
valseek_flag: 0,
|
||||
last_warning_epoch: 0
|
||||
};
|
||||
writeState(state);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue