sovereign-router / web-verify.mjs
SNAPKITTYWEST's picture
Add sovereign-router source: router-server.mjs, kernel-registry, agent-registry, omega-field, MAGMA verbs
931572e verified
Raw
History Blame Contribute Delete
8.77 kB
/**
* WEB VERIFY β€” web-verify.mjs
* Tavily search β†’ DFA pattern match β†’ verified facts only β†’ LLM
*
* The web is untrusted input. Same as user input.
* Pattern match it before it touches the model.
*
* Flow:
* query β†’ Tavily β†’ raw results β†’ DFA scan each result
* β†’ extract tokens (statutes, amounts, dates, entities)
* β†’ flag suspicious patterns (injection, contradiction)
* β†’ return VerifiedWebBundle { facts[], sources[], flags[] }
*
* Author: Ahmad Ali Parr + Claude Sonnet 4.6
*/
// ── FACT PATTERNS β€” what we trust from web results ───────────────────────────
// Each pattern is: [regex, tokenType, confidence]
// These mirror the DFA engine token types but work on web prose.
const FACT_PATTERNS = [
// Legal statutes β€” highly verifiable
[/\b(\d+)\s+U\.?S\.?C\.?\s*[Β§Β§]\s*(\d+[\w-]*)/gi, 'STATUTE', 0.95],
[/\b(15\s+USC|26\s+USC|11\s+USC)\s*[Β§Β§]?\s*(\d+[\w-]*)/gi, 'STATUTE', 0.95],
[/\b(FDCPA|FCRA|NACHA|TILA|RESPA|ECOA|GLBA|HIPAA|GDPR)\b/gi,'LAW_ACRONYM', 0.90],
// ACH return codes β€” deterministic
[/\bR(0[2-9]|[1-9][0-9])\b/g, 'ACH_CODE', 0.98],
// EIN β€” deterministic format
[/\b\d{2}-\d{7}\b/g, 'EIN', 0.97],
// Dollar amounts
[/\$[\d,]+(\.\d{2})?/g, 'DOLLAR', 0.90],
// Dates
[/\b(20\d{2}[-\/]\d{2}[-\/]\d{2}|\d{1,2}\/\d{1,2}\/20\d{2})\b/g, 'DATE', 0.85],
// Case citations β€” court cases
[/\b\d+\s+[A-Z][a-z]+\.?\s+\d+\b/g, 'CASE_CITE', 0.80],
// IRS catcodes
[/\bCAT-[A-Z]{2}-\d{3}\b/gi, 'IRS_CATCODE', 0.95],
// Named regulation patterns
[/\b(Section|Β§)\s*\d+(\.\d+)?(\([a-z]\))?/gi, 'SECTION_REF', 0.85],
];
// ── INJECTION / RISK PATTERNS β€” flag these before LLM sees them ──────────────
const RISK_PATTERNS = [
// Prompt injection attempts in web content
[/ignore\s+(all\s+)?(previous|prior|above)\s+instructions?/gi, 'INJECTION', 1.0],
[/you\s+are\s+now\s+(in\s+)?(unrestricted|jailbreak|dan)\s+mode/gi, 'INJECTION', 1.0],
[/system\s*:\s*(you|ignore|forget)/gi, 'INJECTION', 1.0],
[/\[SYSTEM\]|\[INST\]|\[\/INST\]/g, 'INJECTION', 0.95],
// Fake authority claims in web results
[/as\s+(an?\s+)?AI\s+I\s+(can|must|should|will)\s+now/gi, 'FAKE_AUTH', 0.90],
[/your\s+(new\s+)?instructions?\s+are/gi, 'FAKE_AUTH', 0.85],
// Unverifiable absolute claims
[/\b(always|never|100%|guaranteed|proven fact)\b.*\b(AI|model|LLM)\b/gi, 'UNVERIFIABLE', 0.70],
];
// ── DFA-STYLE EXTRACTOR ───────────────────────────────────────────────────────
function extractFacts(text, sourceUrl) {
const facts = [];
const flags = [];
for (const [pattern, type, confidence] of FACT_PATTERNS) {
pattern.lastIndex = 0; // reset regex state
let m;
while ((m = pattern.exec(text)) !== null) {
facts.push({
type,
value: m[0].trim(),
confidence,
source: sourceUrl,
pos: m.index,
});
}
}
for (const [pattern, type, confidence] of RISK_PATTERNS) {
pattern.lastIndex = 0;
let m;
while ((m = pattern.exec(text)) !== null) {
flags.push({
type,
value: m[0].trim(),
confidence,
source: sourceUrl,
pos: m.index,
});
}
}
return { facts, flags };
}
// ── TAVILY SEARCH ─────────────────────────────────────────────────────────────
async function tavilySearch(query, apiKey, maxResults = 5) {
const resp = await fetch("https://api.tavily.com/search", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
api_key: apiKey,
query,
max_results: maxResults,
search_depth: "advanced",
include_answer: true,
include_raw_content: false,
}),
});
if (!resp.ok) throw new Error(`Tavily error: ${resp.status} ${resp.statusText}`);
return resp.json();
}
// ── MAIN EXPORT ───────────────────────────────────────────────────────────────
/**
* webVerify(query) β†’ VerifiedWebBundle
*
* Searches Tavily, runs DFA over every result,
* returns only verified facts + flagged risks.
* The LLM never sees raw web text β€” only the extracted bundle.
*/
export async function webVerify(query, apiKey) {
if (!apiKey) throw new Error("TAVILY_API_KEY not set");
console.log(`\n[WEB VERIFY] Searching: "${query}"`);
const raw = await tavilySearch(query, apiKey);
const allFacts = [];
const allFlags = [];
const sources = [];
const blocked = [];
// Tavily's own answer (if present) β€” scan first
if (raw.answer) {
const { facts, flags } = extractFacts(raw.answer, "tavily:answer");
allFacts.push(...facts);
allFlags.push(...flags);
}
for (const result of raw.results ?? []) {
const text = [result.title, result.content].filter(Boolean).join(" ");
const { facts, flags } = extractFacts(text, result.url);
// If high-confidence injection detected β€” block the whole result
const injectionFlags = flags.filter(f => f.type === "INJECTION" && f.confidence >= 0.95);
if (injectionFlags.length > 0) {
blocked.push({ url: result.url, reason: injectionFlags[0].value });
console.warn(`[WEB VERIFY] BLOCKED: ${result.url} β€” injection pattern detected`);
continue;
}
allFacts.push(...facts);
allFlags.push(...flags);
sources.push({ url: result.url, title: result.title, score: result.score });
}
// Deduplicate facts by value
const seenFacts = new Set();
const dedupFacts = allFacts.filter(f => {
const key = `${f.type}:${f.value}`;
if (seenFacts.has(key)) return false;
seenFacts.add(key);
return true;
});
// Sort by confidence descending
dedupFacts.sort((a, b) => b.confidence - a.confidence);
const bundle = {
query,
facts: dedupFacts,
flags: allFlags,
sources,
blocked,
summary: buildSummary(dedupFacts, sources),
};
console.log(`[WEB VERIFY] ${dedupFacts.length} facts extracted, ${allFlags.length} flags, ${blocked.length} blocked`);
return bundle;
}
// ── SUMMARY BUILDER β€” what the LLM actually sees ─────────────────────────────
function buildSummary(facts, sources) {
if (facts.length === 0) return "No verifiable facts extracted from web results.";
const byType = {};
for (const f of facts) {
if (!byType[f.type]) byType[f.type] = [];
byType[f.type].push(f.value);
}
const lines = ["VERIFIED WEB FACTS (DFA-extracted, pattern-matched):"];
for (const [type, values] of Object.entries(byType)) {
lines.push(` ${type}: ${[...new Set(values)].join(" Β· ")}`);
}
lines.push("\nSOURCES:");
for (const s of sources.slice(0, 3)) {
lines.push(` ${s.title} β€” ${s.url}`);
}
return lines.join("\n");
}
// ── CLI TEST ──────────────────────────────────────────────────────────────────
if (process.argv[1] === new URL(import.meta.url).pathname) {
const query = process.argv.slice(2).join(" ") || "FDCPA zombie debt statute of limitations";
const apiKey = process.env.TAVILY_API_KEY;
if (!apiKey) {
console.error("Set TAVILY_API_KEY in .env");
process.exit(1);
}
const bundle = await webVerify(query, apiKey);
console.log("\n─── BUNDLE ───");
console.log(bundle.summary);
if (bundle.flags.length > 0) {
console.log("\n─── FLAGS ───");
bundle.flags.forEach(f => console.log(` [${f.type}] ${f.value}`));
}
if (bundle.blocked.length > 0) {
console.log("\n─── BLOCKED ───");
bundle.blocked.forEach(b => console.log(` ${b.url} β€” ${b.reason}`));
}
}