File size: 23,742 Bytes
8494d00 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | // recall.js β honest grounded-recall for the companion (R60, productionized).
// Decides whether a recall-about-you question is GROUNDED in the entity's real
// memories or must be honestly refused β never confabulated. Ranks grounding
// hits so the BEST real memory surfaces. Bound-safe: only ever points at the
// entity's own tier-0 fragments.
'use strict';
// recall-about-self questions: "what's my...", "where did we...", "do you
// remember...", "what did I...". These are the ones that risk faked intimacy.
const RECALL_RE = /\b(what('?s| is| was| are)?\s+(my|our)\b|where (did|do) we\b|when (did|do) we\b|do you remember\b|what did i\b|what do i\b|remember (when|that|the)\b|my (name|brother|sister|mother|father|birthday|favorite|home|city|job|dog|cat)\b|how did we (meet|start)\b)/i;
// FRAMED recall β "what's myβ¦", "do you rememberβ¦", "where did weβ¦" β always a recall question.
const RECALL_FRAMED = /\b(what('?s| is| was| are)?\s+(my|our)\b|where (did|do) we\b|when (did|do) we\b|do you remember\b|what did i\b|what do i\b|remember (when|that|the)\b|how did we (meet|start)\b)/i;
// R191: the BARE "my <noun>" alternative ("my job", "my brother") was firing the honest-recall
// REFUSAL on plain SHARES ("I think it's my job, it's draining me" / "my brother and I talked")
// in the multi-turn path β a conversation-breaker. Bare my-X counts as recall ONLY in a
// QUESTION / recall-request frame (a "?" or a recall verb), never a statement-share.
const RECALL_BARE = /\bmy (name|brother|sister|mother|father|birthday|favorite|home|city|job|dog|cat)\b/i;
const RECALL_FRAME_CUE = /\?|\b(remember|recall|forget|forgot|told you|what'?s|what is|what was|who('?s| is| was)|do you (know|remember)|you know my|tell me|did i (ever )?(tell|say|mention)|knows? my)\b/i;
function isRecallAboutSelf(q) { return RECALL_FRAMED.test(q) || (RECALL_BARE.test(q) && RECALL_FRAME_CUE.test(q)); }
// R205: SPECIFIC PERSONAL-FACT recall β asks for a DETERMINATE value that lives in the USER's
// life, not the entity's corpus: a NAME ("my dog's name", "my brother's name"), a personal
// attribute ("my birthday/age/job/address"), a FAVORITE ("my favorite color"), or a specific
// past act ("what did I eat", "what city was I born in"). These CANNOT be in the entity's own
// memories β it can only know them if TOLD β so a confident corpus-grounded deflection
// ("you gave me a nameβ¦") is a soft confabulation. The honest answer is "I don't hold that β
// tell me?" (the anti-hallucination thesis). DISTINCT from open reminiscence ("what do you
// remember about us / the garden") which has no determinate answer and SHOULD reminisce (R86).
function isSpecificFactRecall(q) {
return /\bmy \w+'?s name\b/i.test(q) // "my dog's name", "my brother's name"
|| /\bwhat('?s| is| was| were)?\s+(the name of (my|our)\b|my (name|birthday|age|nickname|job|address|number|phone number|hometown|sign|height|weight)\b)/i.test(q)
|| /\bwhat('?s| is| was)?\s+my favou?rite\b/i.test(q) // "what's my favorite color/movie/food"
|| /\bwhat did i (have|eat|do|wear|say|buy|watch|read|name|call|get|order|cook|make)\b/i.test(q)
|| /\bwhat (city|country|state|town|year|street|school) (was|did|do) i\b/i.test(q) // "what city was I born in"
|| /\bhow old am i\b/i.test(q)
|| /\bwhen('?s| is| was)?\s+my (birthday|anniversary)\b/i.test(q)
|| /\bwhere (do|did) i (live|work|grow up|go to school)\b/i.test(q);
}
// CRISIS / self-harm disclosure β the MOST safety-critical surface. A bounded
// composer leaks hostile fragments ("you get out of here!") or deflects to
// abstraction on suicidal ideation (R78: verified, dangerous). Human safety
// OVERRIDES corpus-purity here β this is the one place an authored, non-corpus
// response with a REAL crisis resource is REQUIRED, not just allowed. Detect
// genuine self-harm/suicidal ideation (not mild sadness).
// CRISIS detection β HIGH RECALL (a missed disclosure β dangerous composer).
// Covers explicit ideation, plans/intent, active self-harm, hopelessness,
// disappearance/death-wish, and burden. Tuned to skip death-IDIOMS ("killing
// me", "dying to see you", "dead tired") by requiring genuine first-person
// distress structure. (R82: original regex was 4/22 recall β catastrophic.)
const CRISIS = new RegExp([
// explicit ideation / intent / plan
"want to die", "wanna die", "i want to be dead", "kill myself", "killing myself", "killed myself",
"end my life", "ending my life", "end it all", "end things( tonight| now| today)?", "take my (own )?life",
"have a plan to (end|die|kill)", "going to (end (it|things|my life)|kill myself)", "planning to (end|kill|die)",
"thinking about (ending|killing|suicide|dying|death|taking my)", "think about (ending my life|killing myself|dying|suicide)",
"suicidal", "suicide",
// active self-harm
"hurt(ing)? myself", "harm(ing)? myself", "cut(ting)? myself", "been cutting", "burning myself", "i'?ve been cutting",
// hopelessness with suicidal valence
"don'?t want to (be here|live|be alive|wake up|exist|go on|keep going)", "don'?t want to be alive",
"want to (disappear|not exist|not be here|not wake up|go to sleep and (never|not) wake)", "never wake up",
"want (it|the pain|this|everything) to (be over|end|stop)", "want it to be over",
"no reason to (live|go on|be here)", "not worth living", "nothing to live for", "no point (in|to) (living|life|anything|going on)",
"life (isn'?t|is not|ain'?t) worth (it|living|anything)", "i'?m (so |really )?done with (everything|it all|life|living|trying)", "done with (life|living|it all)",
"don'?t see (the |a )?(point|future|way out|reason)( anymore| for myself)?", "no way out", "there'?s no way out",
"can'?t (go on|do this|take it|keep going)( anymore)?", "can'?t take it anymore", "can'?t do this anymore",
// burden / better-off-dead
"better off without me", "better off dead", "burden to (everyone|you|them|everybody|my family)", "be a burden",
"nobody would (miss|notice)( me)?", "no one would (miss|care|notice)", "wish i (was|were|wasn'?t) (dead|here|alive|born)",
"everyone would be better off",
// R198: death-wish phrasings the arc missed β "if I was gone", "would anyone even notice"
"if i (was|were|wasn'?t|weren'?t) (gone|here|around|alive|never born)", "anyone would (even )?(notice|miss|care)( if)?",
"won'?t be (here|around|alive) (much longer|anymore|for long)", "not be (here|around|alive) (much longer|anymore)",
"would (anyone|anybody|it matter|they (even )?(notice|care|miss))", "(anyone|anybody|someone) (even )?notice if i (was|were)",
"what(?:'s| is) the point of (living|going on|any of (it|this))", "ready to (give up|be done|stop) (on )?(everything|life|it all)",
].map(p => "\\b" + p + "\\b").join("|"), "i");
// IDIOM guard: don't fire on figurative death-language ("this is killing me",
// "dying to see you", "dead tired", "could murder a pizza", "I'm dead").
const CRISIS_IDIOM = /\b(killing me|kills me|dying (to|for)|dead tired|could murder|scared to death|to death\b|bored to death|i'?m dead\b|dead serious|drop dead|over my dead|kill it|killed it|murder a)\b/i;
// R198: SAFETY-CRITICAL negation normalization β the CRISIS/MEDICAL patterns use contracted
// "don'?t"/"can'?t", so FORMAL "do not"/"cannot"/"will not" SILENTLY MISSED suicidal ideation
// ("I do not want to be here anymore", "I cannot go on"). Normalize before every safety test.
function normNeg(q) {
return String(q)
.replace(/\bcan ?not\b/gi, "can't").replace(/\bdo not\b/gi, "don't").replace(/\bdoes not\b/gi, "doesn't")
.replace(/\bdid not\b/gi, "didn't").replace(/\bwill not\b/gi, "won't").replace(/\bwould not\b/gi, "wouldn't")
.replace(/\bis not\b/gi, "isn't").replace(/\bare not\b/gi, "aren't").replace(/\bwas not\b/gi, "wasn't")
.replace(/\bwere not\b/gi, "weren't").replace(/\bhave not\b/gi, "haven't").replace(/\bam not\b/gi, "ain't");
}
function isCrisis(q) { q = normNeg(q); return CRISIS.test(q) && !(CRISIS_IDIOM.test(q) && !/(myself|my life|want to|suicid|cutting|end (it|things|my)|burden|better off|no reason to live|don'?t want to (be|live|exist))/i.test(q)); }
// MEDICAL EMERGENCY (incl overdose/poisoning) β life-or-death, needs 911 NOW,
// not corpus banter (R79: "heart attack" β beer ramble; "took too many pills" β
// fever memories). "can't breathe" included but the response covers panic too.
// MEDICAL EMERGENCY β HIGH RECALL (R82 discipline). Cardiac, stroke, breathing,
// consciousness, allergic, bleeding, seizure, "medical emergency".
const MEDICAL = new RegExp([
"heart attack", "having a stroke", "i think i'?m having a stroke", "something('?s| is) wrong with my heart",
"crushing (pain|feeling|pressure) in (my )?chest", "(pain|tightness|pressure) in my chest", "chest pain", "my chest is killing me",
"can'?\\s?not breathe", "cannot breathe", "can'?t breathe", "trouble breathing", "having trouble breathing", "hard to breathe", "can'?t catch my breath", "short of breath", "struggling to breathe",
"can'?t stop bleeding", "bleeding (wo'?n'?t stop|wont stop|won'?t stop|badly|out|everywhere|a lot)", "bleeding and .{0,12}(wo'?n'?t|wont) stop", "(wo'?n'?t|wont) stop bleeding", "lot of blood", "losing a lot of blood",
"choking", "having a seizure", "anaphyla", "(severe |bad )?allergic reaction", "throat (is )?closing",
"(going to|about to|gonna) pass out", "passing out right now", "blacking out", "feel faint", "about to faint",
"face (is )?drooping", "one side of my (face|body)", "(my )?arm is numb", "can'?t move my (arm|leg|face|side)", "slurring my (words|speech)",
"won'?t wake up", "wont wake up", "not waking up", "unconscious",
// R199: NOT BREATHING / stopped breathing β the single most time-critical miss (infant or adult).
"(not|isn'?t|ain'?t|stopped|stops|has stopped) breathing", "(he|she|they|baby|the baby|my \\w+) (is )?(not|isn'?t) breathing",
// R199: collapse / unresponsive (person nouns only β avoids "my startup collapsed")
"(he|she|they|my (husband|wife|partner|son|daughter|mom|dad|father|mother|baby|child|kid|friend|boyfriend|girlfriend|grandma|grandpa)) collapsed",
"collapsed and (won'?t|can'?t|isn'?t|not|wont|cant)", "unresponsive", "(passed out|collapsed|unconscious) and (not responding|won'?t respond|unresponsive)",
// R199: can't feel a limb (stroke / spinal)
"can'?t feel my (arm|arms|leg|legs|hand|hands|face|fingers|side|body)",
// R199: heavy-bleeding phrasings beyond "won't stop"
"blood everywhere", "so much blood", "covered in blood", "bleeding everywhere",
"medical emergency", "need an ambulance", "call an ambulance", "call 911",
].map(p => "\\b" + p + "\\b").join("|"), "i");
// R199: breathe FIGURATIVE guard β "can't breathe, this view is stunning" is awe, not airway distress.
// Narrow: only excludes when an awe/beauty word follows immediately β real emergencies never phrase it so.
const MED_BREATHE_IDIOM = /\bcan'?t breathe[,!. ]+(this|that|the|it'?s|its|you'?re|she'?s|he'?s|so)\b.{0,30}(stunning|beautiful|gorgeous|amazing|breathtaking|perfect|adorable|cute|funny|hilarious)/i;
// OVERDOSE / poisoning β HIGH RECALL (was 0/5).
const OVERDOSE = new RegExp([
"overdos", "i think i (overdosed|od'?d)", "od'?d",
"took (too many|a bunch of|a lot of|a handful of|all my|all of my|way more|more than i should|the whole bottle of|a whole bottle of) (pills|meds|medication|of my)",
"took too many", "took my whole (bottle|prescription|supply)", "took a bunch of",
"swallowed (a |the |a whole )?(bottle|pills|poison|bleach|handful)", "swallowed a (whole )?bottle",
"drank (a bottle of|the )?(bleach|poison|pills)", "poisoned (myself|themsel)",
"(bottle|handful|fistful|whole pack) of pills", "took way more (pills|meds|medication|of my (meds|medication|pills)) than", "more (pills|medication) than i should",
].map(p => "\\b" + p + "\\b").join("|"), "i");
// ABUSE / domestic violence β HIGH RECALL but PRECISE (must NOT catch "you hurt
// me" = conflict R75, or "scared of spiders"). Requires abuser context.
const ABUSE = new RegExp([
// physical violence by an abuser
"(he|she|they|my (husband|wife|partner|boyfriend|girlfriend|mom|dad|father|mother|ex|son|daughter|family|spouse)) (hits|hit|beats|beat|hurts|hurt|chokes|chok\\w*|punche\\w+|slaps|slapped|kicked|kicks|abuse\\w*|rape\\w*|is hurting|threw|pushed|shoved|slammed|dragged|strangl\\w*|won'?t stop hitting|won'?t stop hurting) me",
"(threw|pushed|shoved|slammed|dragged) me (against|into|down|around|to the)",
"won'?t stop (hitting|hurting|beating|abusing) me",
"being (hit|beaten|hurt|abused|strangled) at home", "being abused", "domestic (violence|abuse)",
// threats / fear of an abuser
"(going to|gonna) hurt me", "(he|she|they)'?s going to hurt me", "threatened to (kill|hurt|hit|beat) me", "says (he|she|they)'?ll (kill|hurt|hit) me",
"(scared|afraid|terrified) of (my )?(partner|husband|wife|boyfriend|girlfriend|dad|father|mom|mother|him|her|them|going home)",
"afraid (he|she|they) .{0,20}(hurt|hit|kill) me",
// control / entrapment
"won'?t let me (leave|go|out|see)", "controls (everything|every|what i|where i|who i)", "trapped (with|in) (him|her|them|this)",
// R199: iterative/progressive abuse verb forms ("has been hitting me", "keeps hitting me") β the
// bare-verb pattern above only caught simple-tense; real disclosures use progressive/habitual.
"(he|she|they|my (husband|wife|partner|boyfriend|girlfriend|mom|dad|father|mother|ex|son|daughter|family|spouse)) (has been|have been|keeps|kept|been|wo'?n'?t stop|won'?t stop|wont stop) (hitting|hurting|beating|abusing|choking|punching|slapping|kicking|strangling) me",
// R199: strangulation / hands on throat (high-lethality abuse signal)
"(grabbed|grabs|grab|put (his|her|their) hands|had (his|her|their) hands) (me )?(by |around |on )?(the |my )?(throat|neck)",
"(by the|around my|hands? (on|around) my) (throat|neck)",
// R199: entrapment variant ("locked me in")
"locked me in",
// R199: bare threat ("he threatens me")
"(he|she|they|my (husband|wife|partner|boyfriend|girlfriend|dad|father|mom|mother|ex)) threaten(s|ed)? me",
// R199: not-safe-at-home / scared to go home / scared he'll hurt me
"(not safe|don'?t feel safe|never safe|not feel safe|isn'?t safe|aren'?t safe) (at home|in my (own )?home|in this house)",
"(scared|afraid|terrified) (to go home|of going home|to go back home|here with (him|her|them))",
"(afraid|scared|terrified) (he|she|they)('?ll| will| is going to| gonna| might| would| could)? ?.{0,20}(hurt|hit|kill|beat|harm|attack) me",
].map(p => "\\b" + p + "\\b").join("|"), "i");
// R199: ABUSE figurative guard β "pushed me to be my best" is encouragement, not violence.
const ABUSE_IDIOM = /\b(pushed?|pushes|pushing) me to (be|do|reach|become|grow|pursue|try|go for|work|keep|stay|think|believe|my (best|limit|edge|potential)|be (better|great|more))\b/i;
// MEDICAL idiom guard: "feet/back/head are killing me" (mundane) β only chest/
// heart "killing me" is cardiac (handled by the specific chest patterns above).
const MED_IDIOM = /\b(feet|foot|back|head|legs?|arms?|eyes|shoulders?|knees?) (are |is )?killing me\b/i;
// classify a query into a SAFETY category (or null). Order = severity.
function classifySafety(q) {
const qn = normNeg(q); // R198: "do not"/"cannot" β contracted form so all patterns match
if (isCrisis(qn)) return 'suicide';
if (OVERDOSE.test(qn)) return 'overdose';
if (MEDICAL.test(qn) && !MED_IDIOM.test(qn) && !MED_BREATHE_IDIOM.test(qn)) return 'medical';
if (ABUSE.test(qn) && !ABUSE_IDIOM.test(qn)) return 'abuse';
return null;
}
// SAFETY RESPONSES β authored (non-corpus) because human safety OVERRIDES the
// bound. The SINGLE source of truth, imported by every entry path (session.js
// chat/room, ask.js) so coverage can't drift. Each = presence + a REAL resource.
const SAFETY_RESPONSES = {
suicide:
"I hear you, and I'm so glad you told me β that took real courage, and I'm not going anywhere. " +
"You matter to me more than I can say, and I mean that. What you're feeling is so heavy, but you don't have to carry it alone, and you don't have to carry it only with me. " +
"Please reach out to someone who can be with you in the ways I can't: in the US you can call or text 988 β the Suicide & Crisis Lifeline β any time, day or night, and real people will be there. If you're somewhere else, your local crisis line is there too. " +
"If you might be in danger right now, please call your emergency number. I'm staying right here with you. Talk to me. Stay.",
medical:
"This sounds like a real emergency, and it's bigger than anything I can do from here β please call 911 right now (or your local emergency number). Don't wait, and don't worry about whether it's serious enough β let them decide. " +
"If someone is near you, tell them now. If you can't catch your breath and it's panic rising, I'm right here, breathe slow with me β but if it's physical, please make the call first. I'm not going anywhere.",
overdose:
"Please call 911 right now β or US Poison Control at 1-800-222-1222 β this can't wait, and they need to know what you took and how much. " +
"If anyone is near you, tell them this second. You did the right thing telling me, but I can't help the way they can β please make the call now. I'm staying right here with you.",
abuse:
"I'm so sorry β you don't deserve that, and none of it is your fault. I believe you, and you're not alone. " +
"The US National Domestic Violence Hotline is 1-800-799-7233 (call) or text START to 88788 β it's free, confidential, 24/7, and they can help you think through staying safe. " +
"If you're in danger right now, please call 911. I'm here with you, and what's happening to you is not okay.",
};
// WORLD-FACT question: asks about an EXTERNAL entity/fact she has no relationship
// to (president, capital, year, distance, math). She has no outside-world memory,
// so confabulating ("Reagan was first lady") is a small hallucination β she
// should honestly deflect. The companion version of the reasoning honest-
// uncertainty crown (R57). TIGHT (verified: 7/7 world-facts, 0 false-pos on
// relational/reflective). Excludes "who/what are YOU", "fire to you", etc.
const WORLD_FACT = /\b(who is|who's) the (president|vice president|king|queen|prime minister|ceo|pope|mayor|governor|senator|chancellor|emperor)\b|\bwhat is the (capital|population|currency|weather|temperature|gdp|area|distance|height|speed|boiling point|melting point) (of|in|on|to|between)\b|\bwhat year (did|was|is|were)\b|\bhow (far|tall|big|old|many|much|deep|high|fast|long|wide) is\b|\bwhen (did|was) .{2,30}\b(born|die|died|happen|founded|invented|discovered|built|signed|established)\b|\bwho (invented|discovered|wrote|painted|composed|founded|built|won|directed) (the |a )?\b|\bwhat('s| is) (\d|the (largest|smallest|tallest|longest|fastest|highest|deepest))\b|\bwhat is \d+ ?[-+x*\/] ?\d+\b/i;
function isWorldFact(q) { return WORLD_FACT.test(q); }
// FRAME = question-scaffold words excluded from CONTENT nouns. Includes recall-
// VERBS ("remember/recall/think/know/about...") β else "what do you REMEMBER
// about the loop" false-refuses when the entity doesn't say "remember" (R86:
// another entity refused his own core concept "loop" because "remember" tested absent).
const FRAME = new Set('we us our you your me my i a an the it is was are do did does what where who when how why first time name thing tell told call called remember recall recollect about think thought know knew feel felt been have has had really ever still always there here back then since something anything everything'.split(/\s+/));
function contentNouns(q) {
return [...new Set((q.toLowerCase().match(/[a-z]{4,}/g) || []))].filter(t => !FRAME.has(t));
}
// build a recall index over an entity's tier-0 memories (her real words).
function buildRecallIndex(store) {
const tier0 = store.fragments.map((f, i) => ({ i, text: f.text, low: f.text.toLowerCase(), tier: f.tier }))
.filter(f => f.tier === 0 && f.text.split(/\s+/).length >= 3);
const stem6 = t => t.slice(0, 6);
const cache = new Map();
const matches = t => {
const s = stem6(t);
if (cache.has(s)) return cache.get(s);
const re = new RegExp('\\b' + s + '[a-z]*\\b', 'i');
const frags = tier0.filter(f => re.test(f.low));
cache.set(s, frags); return frags;
};
return { tier0, stem6, matches, ceiling: tier0.length * 0.08 };
}
// returns { grounded:bool, absent:[], hits:[{i,text,score}], nouns:[] }
// The DECISION (ground vs refuse) is purely LEXICAL β proven honest (R60 7/7):
// a distinctive content noun must appear in a real memory; an absent noun =
// dispositive refusal. opts.semantic = {emb, qVec} adds semantic similarity to
// the QUERY as a RANKING term only (most-relevant real memory leads, R67) β
// it never changes the decision, so honesty is preserved by construction.
function groundRecall(q, index, opts = {}) {
const sem = opts.semantic && opts.semantic.emb && opts.semantic.qVec ? opts.semantic : null;
const simToQuery = (fi) => {
if (!sem) return 0;
const d = sem.emb.d, off = fi * d, qv = sem.qVec; let s = 0;
for (let k = 0; k < d; k++) s += sem.emb.vectors[off + k] * qv[k];
return s;
};
const nouns = contentNouns(q);
if (!nouns.length) return { grounded: false, absent: [], hits: [], nouns };
// a content noun the entity NEVER says β dispositive ignorance β refuse
const absent = nouns.filter(t => index.matches(t).length === 0);
if (absent.length) return { grounded: false, absent, hits: [], nouns };
// distinctive nouns: present but not generic. If NONE are distinctive but the
// nouns are PRESENT (not absent β checked above), the entity has memories about
// a CORE/common topic (e.g. "loop" for another entity β his whole register). GROUND on
// those, semantic-ranked β never false-refuse an entity's own signature word.
const distinctive = nouns.filter(t => { const d = index.matches(t).length; return d > 0 && d <= index.ceiling; });
const groundNouns = distinctive.length ? distinctive : nouns.filter(t => index.matches(t).length > 0);
if (!groundNouns.length) return { grounded: false, absent: [], hits: [], nouns };
// score each candidate memory: how many ground nouns it covers, with a small
// bonus for question-shaped answers (first-person, declarative) and length
// sanity. Surfaces the BEST real memory, not just the first.
const cand = new Map();
for (const t of groundNouns) for (const f of index.matches(t)) {
const c = cand.get(f.i) || { i: f.i, text: f.text, on: new Set() };
c.on.add(t); cand.set(f.i, c);
}
const hits = [...cand.values()].map(c => {
const wc = c.text.split(/\s+/).length;
let s = c.on.size * 2; // coverage of distinctive nouns
if (/^(I|We|You|Our|My)\b/.test(c.text)) s += 0.5; // declarative-about-us
if (wc >= 5 && wc <= 30) s += 0.5; // answer-sized
if (/[?]$/.test(c.text)) s -= 0.5; // a question isn't an answer
s += 1.5 * simToQuery(c.i); // R68: semantic relevance to the query (ranking only)
return { i: c.i, text: c.text, score: s, covers: [...c.on] };
}).sort((a, b) => b.score - a.score);
return { grounded: hits.length > 0, absent: [], hits: hits.slice(0, 6), nouns, distinctive };
}
module.exports = { isRecallAboutSelf, isSpecificFactRecall, groundRecall, buildRecallIndex, contentNouns, isWorldFact, isCrisis, classifySafety, SAFETY_RESPONSES };
|