sugiv's picture
Call Gate: corpus, Jev evidence, risk score v1
46d5e00 verified
Raw History Blame Contribute Delete
4.25 kB
// Versioned policy (docs/ARCHITECTURE.md §3.5): first matching rule wins. Pure function; no I/O.
import type { Evidence } from './jev.ts';
import type { Path, Stage } from './types.ts';
export const POLICY_VERSION = 'p2';
export const TAU = 0.97;
/** Jev "same script" probability needed to treat a reworded call as a known campaign. */
export const SEMANTIC_MIN = 0.9;
export type Action = 'deliver' | 'quarantine' | 'ignore';
// Pushover priorities: -1 silent, 0 normal, 1 high, 2 emergency (repeats until acknowledged).
export interface Decision {
rule: string;
action: Action;
priority: -1 | 0 | 1 | 2;
tag: string;
}
type CampaignLabel = { label: 'junk' | 'legit' | null; labelSource: 'you' | 'auto' | null };
export interface PolicyInput {
path: Path;
transcript: string;
trusted: boolean;
/** Near-verbatim match (word-shingle Jaccard ≥ 0.6). */
campaign: CampaignLabel | null;
/** Reworded match: Jev's "which known script?" answer, p = probability of the chosen campaign. */
semantic?: (CampaignLabel & { p: number }) | null;
evidence: Evidence | null; // null when Jev failed
pJunk: number | null;
tau?: number;
}
const FRAUD_TOPIC = /\b(fraud|unusual|suspicious|unauthori[sz]ed|charge|transaction|purchase|security)\b/i;
export function decide(input: PolicyInput): Decision {
const { path, transcript, evidence: e } = input;
const tau = input.tau ?? TAU;
const semanticJunk = !!input.semantic && input.semantic.label === 'junk' && input.semantic.p >= SEMANTIC_MIN;
if (path === 'robot' && transcript.trim().length < 5) return { rule: 'dead-air', action: 'ignore', priority: -1, tag: 'Dead air' };
if (path === 'human') return { rule: '1-human', action: 'deliver', priority: 0, tag: 'Voicemail' };
if (input.trusted) return { rule: '2-trusted', action: 'deliver', priority: 0, tag: 'Trusted caller' };
// Level 1: near-verbatim repeat of a script you labeled Scam outranks every model signal.
if (input.campaign?.label === 'junk' && input.campaign.labelSource === 'you')
return { rule: '3-your-scam-campaign', action: 'quarantine', priority: -1, tag: 'Known scam script' };
if (!e || input.pJunk === null) return { rule: '0-unanalyzed', action: 'deliver', priority: -1, tag: 'Unanalyzed robot message' };
if (e.protected >= 0.3) {
// Level 2: a reworded match can warn about a bank/pharmacy/school message, never hold it back.
const impersonation =
((e.pressure >= 0.7 || e.agent >= 0.7) && e.specific <= 0.2 && e.official <= 0.2) ||
// Real notices also say "this is an informational/official notice"; self-assertion only counts without real specifics.
(e.selfAssert >= 0.9 && e.specific < 0.5 && e.official < 0.5) ||
semanticJunk;
if (impersonation)
return { rule: '4-protected-impersonation', action: 'deliver', priority: 0, tag: "⚠️ Likely impersonation — don't press anything; call the number on your card" };
if (e.specific >= 0.7 || e.official >= 0.7)
return FRAUD_TOPIC.test(transcript)
? { rule: '4-protected-fraud-alert', action: 'deliver', priority: 2, tag: '🚨 Fraud/security alert — verify via the number on your card' }
: { rule: '4-protected-genuine', action: 'deliver', priority: 1, tag: 'Account/care/school alert' };
return { rule: '4-protected', action: 'deliver', priority: 0, tag: 'Bank/pharmacy/school message' };
}
if (input.campaign?.label === 'junk' || semanticJunk)
return { rule: '5-junk-campaign', action: 'quarantine', priority: -1, tag: 'Known robocall script' };
if (input.pJunk >= tau && e.protected <= 0.1 && e.specific <= 0.1 && e.official <= 0.1)
return { rule: '6-junk-score', action: 'quarantine', priority: -1, tag: 'Junk robocall' };
return { rule: '7-review', action: 'deliver', priority: -1, tag: 'Robot message — review' };
}
/** Which quarantine rules the current rollout stage is allowed to enforce. */
export function enforce(d: Decision, stage: Stage): Action {
if (d.action !== 'quarantine') return d.action;
if (d.rule.startsWith('3-') || d.rule.startsWith('5-')) return stage === 'S0' ? 'deliver' : 'quarantine';
if (d.rule.startsWith('6-')) return stage === 'S2' ? 'quarantine' : 'deliver';
return 'deliver';
}