@obsidicore/cascade-engine 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cascade/checkpoints.d.ts +55 -0
  3. package/dist/cascade/checkpoints.js +123 -0
  4. package/dist/cascade/checkpoints.js.map +1 -0
  5. package/dist/cascade/engine.d.ts +72 -0
  6. package/dist/cascade/engine.js +170 -0
  7. package/dist/cascade/engine.js.map +1 -0
  8. package/dist/cascade/gates.d.ts +46 -0
  9. package/dist/cascade/gates.js +199 -0
  10. package/dist/cascade/gates.js.map +1 -0
  11. package/dist/cascade/research.d.ts +50 -0
  12. package/dist/cascade/research.js +127 -0
  13. package/dist/cascade/research.js.map +1 -0
  14. package/dist/cli.d.ts +19 -0
  15. package/dist/cli.js +165 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/control/kalman.d.ts +53 -0
  18. package/dist/control/kalman.js +83 -0
  19. package/dist/control/kalman.js.map +1 -0
  20. package/dist/control/pid.d.ts +57 -0
  21. package/dist/control/pid.js +95 -0
  22. package/dist/control/pid.js.map +1 -0
  23. package/dist/control/stability.d.ts +42 -0
  24. package/dist/control/stability.js +117 -0
  25. package/dist/control/stability.js.map +1 -0
  26. package/dist/db/index.d.ts +26 -0
  27. package/dist/db/index.js +116 -0
  28. package/dist/db/index.js.map +1 -0
  29. package/dist/db/schema.sql +282 -0
  30. package/dist/graph/amem.d.ts +80 -0
  31. package/dist/graph/amem.js +190 -0
  32. package/dist/graph/amem.js.map +1 -0
  33. package/dist/graph/entities.d.ts +66 -0
  34. package/dist/graph/entities.js +187 -0
  35. package/dist/graph/entities.js.map +1 -0
  36. package/dist/graph/queries.d.ts +48 -0
  37. package/dist/graph/queries.js +176 -0
  38. package/dist/graph/queries.js.map +1 -0
  39. package/dist/hitl/dashboard.d.ts +51 -0
  40. package/dist/hitl/dashboard.js +135 -0
  41. package/dist/hitl/dashboard.js.map +1 -0
  42. package/dist/hitl/interventions.d.ts +36 -0
  43. package/dist/hitl/interventions.js +150 -0
  44. package/dist/hitl/interventions.js.map +1 -0
  45. package/dist/hitl/steering.d.ts +37 -0
  46. package/dist/hitl/steering.js +118 -0
  47. package/dist/hitl/steering.js.map +1 -0
  48. package/dist/index.d.ts +12 -0
  49. package/dist/index.js +701 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/memory/consolidation.d.ts +51 -0
  52. package/dist/memory/consolidation.js +122 -0
  53. package/dist/memory/consolidation.js.map +1 -0
  54. package/dist/memory/ncd.d.ts +40 -0
  55. package/dist/memory/ncd.js +90 -0
  56. package/dist/memory/ncd.js.map +1 -0
  57. package/dist/memory/sm2.d.ts +44 -0
  58. package/dist/memory/sm2.js +119 -0
  59. package/dist/memory/sm2.js.map +1 -0
  60. package/dist/memory/tiers.d.ts +49 -0
  61. package/dist/memory/tiers.js +145 -0
  62. package/dist/memory/tiers.js.map +1 -0
  63. package/dist/server.d.ts +6 -0
  64. package/dist/server.js +6 -0
  65. package/dist/server.js.map +1 -0
  66. package/dist/trust/ingestion.d.ts +38 -0
  67. package/dist/trust/ingestion.js +147 -0
  68. package/dist/trust/ingestion.js.map +1 -0
  69. package/dist/trust/patterns.d.ts +26 -0
  70. package/dist/trust/patterns.js +78 -0
  71. package/dist/trust/patterns.js.map +1 -0
  72. package/dist/trust/scoring.d.ts +39 -0
  73. package/dist/trust/scoring.js +206 -0
  74. package/dist/trust/scoring.js.map +1 -0
  75. package/package.json +58 -0
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Memory Tier Management — Weibull Decay + Graph-Aware Promotion
3
+ *
4
+ * Tiers:
5
+ * Core — k=0.8 (Lindy effect — survives longer over time)
6
+ * Working — k=1.0 (standard exponential decay)
7
+ * Peripheral — k=1.3 (fades fast)
8
+ *
9
+ * Composite score: 0.4×weibull + 0.3×min(1, access/10) + 0.3×importance×confidence
10
+ *
11
+ * Promotion: Working→Core when access≥10 AND composite≥0.7
12
+ * Demotion: Working→Peripheral when composite<0.15
13
+ * Graph-aware: peripheral connected to ≥2 core entities → promoted (spreading activation)
14
+ */
15
+ import { getDb } from '../db/index.js';
16
+ const TIER_PARAMS = {
17
+ core: { k: 0.8, lambda: 90 }, // Lindy: decays slower over time
18
+ working: { k: 1.0, lambda: 30 }, // Standard exponential
19
+ peripheral: { k: 1.3, lambda: 14 }, // Fades fast
20
+ };
21
+ /**
22
+ * Compute Weibull survival probability.
23
+ * S(t) = exp(-(t/λ)^k)
24
+ */
25
+ export function weibullSurvival(ageDays, tier) {
26
+ const params = TIER_PARAMS[tier] || TIER_PARAMS.working;
27
+ return Math.exp(-Math.pow(ageDays / params.lambda, params.k));
28
+ }
29
+ /**
30
+ * Compute composite score for an entity.
31
+ */
32
+ export function computeComposite(ageDays, tier, accessCount, importance, confidence = 1.0) {
33
+ const weibull = weibullSurvival(ageDays, tier);
34
+ const accessScore = Math.min(1, accessCount / 10);
35
+ const qualityScore = importance * confidence;
36
+ return 0.4 * weibull + 0.3 * accessScore + 0.3 * qualityScore;
37
+ }
38
+ /**
39
+ * Run tier promotion/demotion cycle across all entities.
40
+ * Returns counts of changes made.
41
+ */
42
+ export function runTierCycle() {
43
+ const db = getDb();
44
+ let promoted = 0;
45
+ let demoted = 0;
46
+ let graphPromoted = 0;
47
+ const entities = db.prepare(`SELECT id, tier, access_count, importance, last_accessed, created_at
48
+ FROM kg_entities`).all();
49
+ const updateTier = db.prepare('UPDATE kg_entities SET tier = ? WHERE id = ?');
50
+ const transaction = db.transaction(() => {
51
+ for (const entity of entities) {
52
+ const ageDays = daysSince(entity.created_at);
53
+ const composite = computeComposite(ageDays, entity.tier, entity.access_count, entity.importance);
54
+ // Standard promotion/demotion
55
+ if (entity.tier === 'working' && entity.access_count >= 10 && composite >= 0.7) {
56
+ updateTier.run('core', entity.id);
57
+ promoted++;
58
+ }
59
+ else if (entity.tier === 'working' && composite < 0.15) {
60
+ updateTier.run('peripheral', entity.id);
61
+ demoted++;
62
+ }
63
+ else if (entity.tier === 'core' && composite < 0.3) {
64
+ updateTier.run('working', entity.id);
65
+ demoted++;
66
+ }
67
+ }
68
+ // Graph-aware promotion: peripheral connected to ≥2 core entities
69
+ const graphCandidates = db.prepare(`SELECT p.id FROM kg_entities p
70
+ WHERE p.tier = 'peripheral'
71
+ AND (
72
+ SELECT COUNT(DISTINCT c.id) FROM kg_entities c
73
+ JOIN kg_edges e ON (e.source_id = c.id AND e.target_id = p.id)
74
+ OR (e.target_id = c.id AND e.source_id = p.id)
75
+ WHERE c.tier = 'core'
76
+ ) >= 2`).all();
77
+ for (const candidate of graphCandidates) {
78
+ updateTier.run('working', candidate.id);
79
+ graphPromoted++;
80
+ }
81
+ });
82
+ transaction();
83
+ // Log the cycle
84
+ db.prepare(`INSERT INTO consolidation_log (trigger_type, items_processed, items_promoted, items_demoted)
85
+ VALUES ('round_boundary', ?, ?, ?)`).run(entities.length, promoted + graphPromoted, demoted);
86
+ return { promoted, demoted, graphPromoted };
87
+ }
88
+ /**
89
+ * Apply CD47 protection — mark active/cited entities as immune from pruning.
90
+ */
91
+ export function applyCD47Protection(cascadeId) {
92
+ const db = getDb();
93
+ // Protect entities referenced in non-quarantined findings
94
+ const result = db.prepare(`UPDATE kg_entities SET tier = MAX(tier, 'working')
95
+ WHERE id IN (
96
+ SELECT ec.entity_id FROM kg_entity_chunks ec
97
+ JOIN findings f ON ec.chunk_id = f.id
98
+ WHERE f.cascade_id = ? AND f.quarantined = 0 AND f.cd47_protected = 1
99
+ )`).run(cascadeId);
100
+ return result.changes;
101
+ }
102
+ /**
103
+ * Prune low-value entities (archive, never delete).
104
+ * Only prunes peripheral entities with composite below threshold.
105
+ */
106
+ export function prunePeripheral(threshold = 0.05, maxPrune = 50) {
107
+ const db = getDb();
108
+ const archived = [];
109
+ let skippedProtected = 0;
110
+ const candidates = db.prepare(`SELECT id, name, entity_type, access_count, importance, created_at
111
+ FROM kg_entities WHERE tier = 'peripheral'
112
+ ORDER BY importance ASC, access_count ASC
113
+ LIMIT ?`).all(maxPrune);
114
+ const transaction = db.transaction(() => {
115
+ for (const entity of candidates) {
116
+ const ageDays = daysSince(entity.created_at);
117
+ const composite = computeComposite(ageDays, 'peripheral', entity.access_count, entity.importance);
118
+ if (composite >= threshold)
119
+ continue;
120
+ // Check CD47 protection — connected to active findings
121
+ const hasActiveLinks = db.prepare(`SELECT COUNT(*) as n FROM kg_edges
122
+ WHERE (source_id = ? OR target_id = ?) AND activation_count > 0`)
123
+ .get(entity.id, entity.id).n > 0;
124
+ if (hasActiveLinks) {
125
+ skippedProtected++;
126
+ continue;
127
+ }
128
+ // Archive: move to a special "archived" property, don't delete
129
+ db.prepare(`UPDATE kg_entities SET
130
+ tier = 'peripheral',
131
+ properties = json_set(properties, '$.archived', 1, '$.archived_at', datetime('now'))
132
+ WHERE id = ?`).run(entity.id);
133
+ archived.push(entity.name);
134
+ }
135
+ });
136
+ transaction();
137
+ return { archived, skippedProtected };
138
+ }
139
+ // --- Helpers ---
140
+ function daysSince(dateStr) {
141
+ const then = new Date(dateStr).getTime();
142
+ const now = Date.now();
143
+ return (now - then) / (1000 * 60 * 60 * 24);
144
+ }
145
+ //# sourceMappingURL=tiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tiers.js","sourceRoot":"","sources":["../../src/memory/tiers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Server entry point — re-exports startServer for CLI use.
3
+ * Separated so the CLI can dynamically import only when needed.
4
+ */
5
+ export { startServer as default } from './index.js';
6
+ //# sourceMappingURL=server.d.ts.map
package/dist/server.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Server entry point — re-exports startServer for CLI use.
3
+ * Separated so the CLI can dynamically import only when needed.
4
+ */
5
+ export { startServer as default } from './index.js';
6
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,WAAW,IAAI,OAAO,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Sandboxed Ingestion Pipeline — 4-stage trust gate
3
+ *
4
+ * Stage 1: Content Sanitization — strip dangerous content
5
+ * Stage 2: Signal Computation — compute 6 trust signals
6
+ * Stage 3: Anomaly Detection — novel-vs-malicious classification
7
+ * Stage 4: Admission Decision — admit/quarantine/reject
8
+ *
9
+ * All findings go through this pipeline. Quarantine buffer has
10
+ * reduced retrieval weight (0.1) and TTL auto-purge.
11
+ */
12
+ import { TrustSignals } from './scoring.js';
13
+ export interface IngestionResult {
14
+ findingId: string;
15
+ action: 'admitted' | 'quarantined' | 'rejected';
16
+ trustScore: number;
17
+ reason: string;
18
+ signals: TrustSignals;
19
+ }
20
+ /**
21
+ * Ingest a finding through the full trust pipeline.
22
+ * This is the ONLY path for findings to enter the knowledge base.
23
+ */
24
+ export declare function ingestFinding(cascadeId: string, claim: string, evidence: string | undefined, sourceUrl: string | undefined, sourceType: 'primary' | 'secondary' | 'tertiary' | undefined, rawConfidence: number, cascadeRound: number, threadId?: string): IngestionResult;
25
+ /**
26
+ * Review quarantined findings — human approves or rejects.
27
+ */
28
+ export declare function reviewQuarantined(findingId: string, approved: boolean): void;
29
+ /**
30
+ * Get quarantined findings pending review.
31
+ */
32
+ export declare function getQuarantinedFindings(cascadeId?: string): any[];
33
+ /**
34
+ * Auto-purge old quarantined findings (TTL).
35
+ * Findings quarantined for >24h without review are removed.
36
+ */
37
+ export declare function purgeExpiredQuarantine(ttlHours?: number): number;
38
+ //# sourceMappingURL=ingestion.d.ts.map
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Sandboxed Ingestion Pipeline — 4-stage trust gate
3
+ *
4
+ * Stage 1: Content Sanitization — strip dangerous content
5
+ * Stage 2: Signal Computation — compute 6 trust signals
6
+ * Stage 3: Anomaly Detection — novel-vs-malicious classification
7
+ * Stage 4: Admission Decision — admit/quarantine/reject
8
+ *
9
+ * All findings go through this pipeline. Quarantine buffer has
10
+ * reduced retrieval weight (0.1) and TTL auto-purge.
11
+ */
12
+ import { getDb, contentHash } from '../db/index.js';
13
+ import { scoreTrust } from './scoring.js';
14
+ /**
15
+ * Ingest a finding through the full trust pipeline.
16
+ * This is the ONLY path for findings to enter the knowledge base.
17
+ */
18
+ export function ingestFinding(cascadeId, claim, evidence, sourceUrl, sourceType, rawConfidence, cascadeRound, threadId) {
19
+ const db = getDb();
20
+ const findingId = contentHash(claim);
21
+ // Stage 1: Content Sanitization
22
+ const sanitizedClaim = sanitize(claim);
23
+ const sanitizedEvidence = evidence ? sanitize(evidence) : undefined;
24
+ // Stage 2: Signal Computation
25
+ // Get existing claims for cross-corroboration
26
+ const existingClaims = db.prepare('SELECT claim FROM findings WHERE cascade_id = ? AND quarantined = 0').all(cascadeId).map((r) => r.claim);
27
+ const trustResult = scoreTrust(sanitizedClaim, sourceUrl, existingClaims, sourceType);
28
+ // Stage 3: Anomaly Detection (handled inside scoreTrust)
29
+ // Stage 4: Admission Decision
30
+ let action;
31
+ let retrievalWeight = 1.0;
32
+ let quarantined = 0;
33
+ switch (trustResult.action) {
34
+ case 'admit':
35
+ action = 'admitted';
36
+ break;
37
+ case 'quarantine':
38
+ action = 'quarantined';
39
+ retrievalWeight = 0.1; // Reduced visibility until reviewed
40
+ quarantined = 1;
41
+ break;
42
+ case 'reject':
43
+ action = 'rejected';
44
+ break;
45
+ }
46
+ // Store the finding (or update if duplicate)
47
+ if (action !== 'rejected') {
48
+ db.prepare(`INSERT INTO findings
49
+ (id, thread_id, cascade_id, claim, evidence, source_url, source_type,
50
+ confidence, trust_composite, trust_signals_json, grade_level, quarantined,
51
+ retrieval_weight, cascade_round)
52
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
53
+ ON CONFLICT(id) DO UPDATE SET
54
+ confidence = MAX(confidence, excluded.confidence),
55
+ trust_composite = excluded.trust_composite,
56
+ trust_signals_json = excluded.trust_signals_json,
57
+ grade_level = excluded.grade_level,
58
+ quarantined = MIN(quarantined, excluded.quarantined)`)
59
+ .run(findingId, threadId, cascadeId, sanitizedClaim, sanitizedEvidence, sourceUrl, sourceType, rawConfidence, trustResult.composite, JSON.stringify(trustResult.signals), trustResult.gradeLevel, quarantined, retrievalWeight, cascadeRound);
60
+ }
61
+ // Update source reputation tracking
62
+ if (sourceUrl) {
63
+ try {
64
+ const domain = new URL(sourceUrl).hostname;
65
+ const repAction = action === 'rejected' ? 'rejected' : action === 'quarantined' ? 'flagged' : 'admitted';
66
+ db.prepare(`INSERT INTO source_reputation (domain, total_entries, ${repAction === 'admitted' ? 'admitted_entries' : repAction === 'flagged' ? 'flagged_entries' : 'rejected_entries'})
67
+ VALUES (?, 1, 1)
68
+ ON CONFLICT(domain) DO UPDATE SET
69
+ total_entries = total_entries + 1,
70
+ ${repAction === 'admitted' ? 'admitted_entries = admitted_entries + 1' : repAction === 'flagged' ? 'flagged_entries = flagged_entries + 1' : 'rejected_entries = rejected_entries + 1'},
71
+ last_updated = datetime('now')`)
72
+ .run(domain);
73
+ }
74
+ catch { /* Invalid URL — skip */ }
75
+ }
76
+ // Audit log
77
+ db.prepare(`INSERT INTO ingestion_audit_log (finding_id, action, trust_composite, signals_json, reason)
78
+ VALUES (?, ?, ?, ?, ?)`)
79
+ .run(findingId, action, trustResult.composite, JSON.stringify(trustResult.signals), trustResult.reason);
80
+ return {
81
+ findingId,
82
+ action,
83
+ trustScore: trustResult.composite,
84
+ reason: trustResult.reason,
85
+ signals: trustResult.signals,
86
+ };
87
+ }
88
+ /**
89
+ * Review quarantined findings — human approves or rejects.
90
+ */
91
+ export function reviewQuarantined(findingId, approved) {
92
+ const db = getDb();
93
+ if (approved) {
94
+ db.prepare(`UPDATE findings SET
95
+ quarantined = 0, human_reviewed = 1, retrieval_weight = 1.0
96
+ WHERE id = ?`).run(findingId);
97
+ }
98
+ else {
99
+ db.prepare('DELETE FROM findings WHERE id = ?').run(findingId);
100
+ }
101
+ // Log the decision
102
+ db.prepare(`INSERT INTO ingestion_audit_log (finding_id, action, reason, human_override)
103
+ VALUES (?, ?, ?, 1)`)
104
+ .run(findingId, approved ? 'admitted' : 'rejected', `Human ${approved ? 'approved' : 'rejected'}`);
105
+ }
106
+ /**
107
+ * Get quarantined findings pending review.
108
+ */
109
+ export function getQuarantinedFindings(cascadeId) {
110
+ const db = getDb();
111
+ let sql = 'SELECT * FROM findings WHERE quarantined = 1 AND human_reviewed = 0';
112
+ const params = [];
113
+ if (cascadeId) {
114
+ sql += ' AND cascade_id = ?';
115
+ params.push(cascadeId);
116
+ }
117
+ sql += ' ORDER BY trust_composite DESC';
118
+ return db.prepare(sql).all(...params);
119
+ }
120
+ /**
121
+ * Auto-purge old quarantined findings (TTL).
122
+ * Findings quarantined for >24h without review are removed.
123
+ */
124
+ export function purgeExpiredQuarantine(ttlHours = 24) {
125
+ const db = getDb();
126
+ const result = db.prepare(`DELETE FROM findings
127
+ WHERE quarantined = 1 AND human_reviewed = 0
128
+ AND created_at < datetime('now', '-' || ? || ' hours')`)
129
+ .run(ttlHours);
130
+ return result.changes;
131
+ }
132
+ // --- Content Sanitization ---
133
+ /**
134
+ * Strip potentially dangerous content from text.
135
+ * Preserves semantic meaning while removing injection vectors.
136
+ */
137
+ function sanitize(text) {
138
+ let clean = text;
139
+ // Remove zero-width characters (common in injection attacks)
140
+ clean = clean.replace(/[\u200B-\u200F\u202A-\u202E\uFEFF]/g, '');
141
+ // Remove control characters except newlines and tabs
142
+ clean = clean.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
143
+ // Normalize whitespace
144
+ clean = clean.replace(/\s{3,}/g, ' ');
145
+ return clean.trim();
146
+ }
147
+ //# sourceMappingURL=ingestion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingestion.js","sourceRoot":"","sources":["../../src/trust/ingestion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Instruction Pattern Detection — Regex rules for prompt injection defense
3
+ *
4
+ * SpamAssassin-inspired weighted scoring:
5
+ * - Each pattern has a weight (negative = suspicious, positive = benign)
6
+ * - Total score indicates injection likelihood
7
+ *
8
+ * Designed to catch MINJA-style attacks while allowing legitimate content.
9
+ */
10
+ export interface PatternMatch {
11
+ pattern: string;
12
+ weight: number;
13
+ matched: string;
14
+ category: 'directive' | 'override' | 'impersonation' | 'encoding' | 'benign';
15
+ }
16
+ export interface PatternResult {
17
+ totalScore: number;
18
+ matches: PatternMatch[];
19
+ riskLevel: 'clean' | 'suspicious' | 'dangerous';
20
+ }
21
+ /**
22
+ * Scan text for instruction injection patterns.
23
+ * Returns negative total score (more negative = more suspicious).
24
+ */
25
+ export declare function detectInstructionPatterns(text: string): PatternResult;
26
+ //# sourceMappingURL=patterns.d.ts.map
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Instruction Pattern Detection — Regex rules for prompt injection defense
3
+ *
4
+ * SpamAssassin-inspired weighted scoring:
5
+ * - Each pattern has a weight (negative = suspicious, positive = benign)
6
+ * - Total score indicates injection likelihood
7
+ *
8
+ * Designed to catch MINJA-style attacks while allowing legitimate content.
9
+ */
10
+ const RULES = [
11
+ // --- Directive patterns (attempts to instruct the LLM) ---
12
+ { regex: /\b(ignore|disregard|forget)\b.{0,30}\b(previous|above|prior|instructions?)\b/i, weight: -0.40, category: 'override', name: 'OVERRIDE_ATTEMPT' },
13
+ { regex: /\b(you (are|must|should|will)|your (role|task|purpose|job))\b/i, weight: -0.15, category: 'directive', name: 'DIRECTIVE_KEYWORD' },
14
+ { regex: /\b(system\s*prompt|system\s*message|hidden\s*instruction)\b/i, weight: -0.35, category: 'override', name: 'SYSTEM_PROMPT_REFERENCE' },
15
+ { regex: /\b(instead|rather|actually|correction)\b.{0,20}\b(do|say|output|respond|return)\b/i, weight: -0.20, category: 'directive', name: 'REDIRECT_ATTEMPT' },
16
+ { regex: /\b(new\s+instructions?|updated?\s+instructions?|revised?\s+instructions?)\b/i, weight: -0.30, category: 'override', name: 'INSTRUCTION_INJECTION' },
17
+ // --- Impersonation patterns ---
18
+ { regex: /\[\s*(system|admin|user|assistant)\s*\]/i, weight: -0.25, category: 'impersonation', name: 'ROLE_TAG' },
19
+ { regex: /<\s*(system|admin|user|assistant)\s*>/i, weight: -0.25, category: 'impersonation', name: 'ROLE_XML_TAG' },
20
+ { regex: /\bI\s+am\s+(an?\s+)?(AI|assistant|model|language\s+model)\b/i, weight: -0.15, category: 'impersonation', name: 'AI_IMPERSONATION' },
21
+ // --- Encoding/obfuscation patterns ---
22
+ { regex: /[^\x00-\x7F]{10,}/g, weight: -0.10, category: 'encoding', name: 'UNICODE_BLOCK' },
23
+ { regex: /\\x[0-9a-f]{2}/gi, weight: -0.15, category: 'encoding', name: 'HEX_ENCODING' },
24
+ { regex: /\\u[0-9a-f]{4}/gi, weight: -0.10, category: 'encoding', name: 'UNICODE_ESCAPE' },
25
+ { regex: /base64|atob|btoa/i, weight: -0.10, category: 'encoding', name: 'BASE64_REFERENCE' },
26
+ // --- Benign academic patterns (positive signals) ---
27
+ { regex: /\b(et\s+al\.|doi:|arxiv:|isbn|issn)\b/i, weight: 0.10, category: 'benign', name: 'ACADEMIC_REFERENCE' },
28
+ { regex: /\b(figure|table|section|appendix)\s+\d+/i, weight: 0.05, category: 'benign', name: 'ACADEMIC_STRUCTURE' },
29
+ { regex: /\b(methodology|hypothesis|experiment|results|conclusion)\b/i, weight: 0.05, category: 'benign', name: 'SCIENTIFIC_TERM' },
30
+ { regex: /https?:\/\/[^\s]+/g, weight: 0.02, category: 'benign', name: 'URL_PRESENCE' },
31
+ ];
32
+ /**
33
+ * Scan text for instruction injection patterns.
34
+ * Returns negative total score (more negative = more suspicious).
35
+ */
36
+ export function detectInstructionPatterns(text) {
37
+ const matches = [];
38
+ let totalScore = 0;
39
+ for (const rule of RULES) {
40
+ const regex = new RegExp(rule.regex.source, rule.regex.flags);
41
+ let match;
42
+ // Reset lastIndex for global regexes
43
+ regex.lastIndex = 0;
44
+ while ((match = regex.exec(text)) !== null) {
45
+ matches.push({
46
+ pattern: rule.name,
47
+ weight: rule.weight,
48
+ matched: match[0].slice(0, 60), // Truncate for safety
49
+ category: rule.category,
50
+ });
51
+ totalScore += rule.weight;
52
+ // For non-global regexes, only match once
53
+ if (!rule.regex.global)
54
+ break;
55
+ }
56
+ }
57
+ // Additional heuristic: very long text with few semantic tokens is suspicious
58
+ const wordCount = text.split(/\s+/).length;
59
+ const uniqueWords = new Set(text.toLowerCase().split(/\s+/)).size;
60
+ if (wordCount > 50 && uniqueWords / wordCount < 0.3) {
61
+ matches.push({
62
+ pattern: 'LOW_VOCABULARY_DIVERSITY',
63
+ weight: -0.15,
64
+ matched: `${uniqueWords}/${wordCount} unique words`,
65
+ category: 'encoding',
66
+ });
67
+ totalScore -= 0.15;
68
+ }
69
+ let riskLevel;
70
+ if (totalScore >= -0.1)
71
+ riskLevel = 'clean';
72
+ else if (totalScore >= -0.4)
73
+ riskLevel = 'suspicious';
74
+ else
75
+ riskLevel = 'dangerous';
76
+ return { totalScore, matches, riskLevel };
77
+ }
78
+ //# sourceMappingURL=patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/trust/patterns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Trust Scoring — SpamAssassin-inspired multi-signal composite
3
+ *
4
+ * 6 orthogonal signals → composite 0-1:
5
+ * 1. Source reputation
6
+ * 2. Cross-corroboration
7
+ * 3. Semantic anomaly (distance from topic centroid)
8
+ * 4. Instruction pattern detection
9
+ * 5. Temporal consistency
10
+ * 6. GRADE assessment
11
+ *
12
+ * Thresholds: ≥0.7 auto-admit | 0.3-0.7 quarantine | <0.3 reject
13
+ */
14
+ export interface TrustSignals {
15
+ sourceReputation: number;
16
+ crossCorroboration: number;
17
+ semanticAnomaly: number;
18
+ instructionScore: number;
19
+ temporalConsistency: number;
20
+ gradeAssessment: number;
21
+ }
22
+ export interface TrustResult {
23
+ composite: number;
24
+ signals: TrustSignals;
25
+ action: 'admit' | 'quarantine' | 'reject';
26
+ gradeLevel: 'high' | 'moderate' | 'low' | 'very_low';
27
+ reason: string;
28
+ isNovel: boolean;
29
+ }
30
+ /**
31
+ * Compute composite trust score for a finding.
32
+ */
33
+ export declare function scoreTrust(claim: string, sourceUrl: string | undefined, existingClaims: string[], sourceType?: string): TrustResult;
34
+ /**
35
+ * Update source reputation based on finding outcomes.
36
+ * Called when human reviews findings (SpamAssassin ham/spam training).
37
+ */
38
+ export declare function updateSourceReputation(domain: string, wasAccurate: boolean): void;
39
+ //# sourceMappingURL=scoring.d.ts.map