Files
brain/tools/observer-routing-detector.mjs
T

76 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Routing-gate method-direction detector (brain governance, observer
* factor-analysis spec §5.1). Pure — given a user-prompt text and a list of
* known node names, decides whether the user *dictated* a specific method.
* Conservative-broad: a directive verb within a 40-char window before a node
* name, or a /slash-command form.
*
* Security Guidance #40: pure string ops — no exec/execSync.
*/
import { readFileSync, existsSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
/** Repo root = parent of the tools/ directory where this module lives. */
const REPO_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
const KNOWN_NODES_PATH = 'tools/observer-known-nodes.txt';
const DIRECTIVE_VERBS = [
'запусти', 'запускай', 'используй', 'вызови', 'вызывай', 'прогони',
'применяй', 'применить', 'через', 'run', 'use', 'invoke', 'via',
];
/** Load the directable node names from the data file (# comments / blanks skipped). */
export function loadKnownNodes(path = KNOWN_NODES_PATH) {
const absPath = resolve(REPO_ROOT, path);
if (!existsSync(absPath)) return [];
const out = [];
for (const line of readFileSync(absPath, 'utf-8').split('\n')) {
const t = line.trim();
if (!t || t.startsWith('#')) continue;
out.push(t);
}
return out;
}
/**
* Снять ТОЛЬКО PASTED-контекст: fenced-блоки и markdown-blockquote-строки.
* НАМЕРЕННО НЕ трогает inline-backticks/кавычки/guillemets — в отличие от
* stripQuotedContext (tools/enforce-rationalization-audit.mjs), у которого
* ПРОТИВОПОЛОЖНОЕ безопасное направление (FN-safe). Здесь гейт routingGate
* FP-safe: inline-форма `/x` должна оставаться директивой (over-detection
* дешевле тихой потери governance-сигнала; sharp-edges SE-1).
*/
export function stripPastedContext(text) {
if (typeof text !== 'string') return '';
let s = text;
s = s.replace(/```[\s\S]*?```/g, ' '); // fenced blocks
s = s.replace(/^[ \t]*(?:>|>).*$/gm, ' '); // markdown blockquote lines
return s;
}
/**
* @returns {{directed: boolean, node: string|null}}
*/
export function detectMethodDirected(promptText, knownNodes) {
const text = stripPastedContext(String(promptText || '')).toLowerCase();
for (const node of knownNodes || []) {
const n = String(node).toLowerCase();
if (!n) continue;
if (text.includes('/' + n)) return { directed: true, node };
const idx = text.indexOf(n);
if (idx === -1) continue;
const before = text.slice(Math.max(0, idx - 40), idx);
if (DIRECTIVE_VERBS.some((v) => before.includes(v))) return { directed: true, node };
}
return { directed: false, node: null };
}
if (process.argv[1] && process.argv[1].replace(/\\/g, '/').endsWith('/observer-routing-detector.mjs')) {
const det = detectMethodDirected(process.argv.slice(2).join(' '), loadKnownNodes());
console.log(JSON.stringify(det));
process.exit(0);
}