UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

382 lines 13.5 kB
/** * Memory Write Gating System * * Adds authority scope, TTL, decay, and contradiction tracking * to memory operations. Ensures that only authorized agents can * write to specific namespaces, enforces rate limits, and detects * contradictions between memory entries. * * @module @claude-flow/guidance/memory-gate */ import { createHash } from 'node:crypto'; // ============================================================================ // Role Hierarchy // ============================================================================ /** Role hierarchy levels (higher = more authority) */ const ROLE_HIERARCHY = { queen: 4, coordinator: 3, worker: 2, observer: 1, }; /** * Minimum role required to write to any namespace */ const MINIMUM_WRITE_ROLE = 'worker'; // ============================================================================ // Contradiction Detection Patterns // ============================================================================ const CONTRADICTION_PATTERNS = [ { positive: /\bmust\b/i, negative: /\bnever\b|\bdo not\b|\bavoid\b/i, description: 'Conflicting obligation: "must" vs negation', }, { positive: /\balways\b/i, negative: /\bnever\b|\bdon't\b|\bdo not\b/i, description: 'Conflicting frequency: "always" vs "never"', }, { positive: /\brequire\b/i, negative: /\bforbid\b|\bprohibit\b/i, description: 'Conflicting policy: "require" vs "forbid"', }, { positive: /\benable\b/i, negative: /\bdisable\b/i, description: 'Conflicting state: "enable" vs "disable"', }, { positive: /\btrue\b/i, negative: /\bfalse\b/i, description: 'Conflicting boolean: "true" vs "false"', }, ]; // ============================================================================ // Utility Functions // ============================================================================ /** * Compute SHA-256 hash of a value */ function computeValueHash(value) { const serialized = JSON.stringify(value); return createHash('sha256').update(serialized).digest('hex'); } /** * Stringify a value for contradiction detection */ function stringifyForComparison(value) { if (typeof value === 'string') return value; if (typeof value === 'number' || typeof value === 'boolean') return String(value); try { return JSON.stringify(value); } catch { return String(value); } } // ============================================================================ // MemoryWriteGate // ============================================================================ /** * Memory Write Gate * * Controls write access to the memory system by enforcing: * - Authority checks (namespace access, role hierarchy) * - Rate limiting (sliding window per agent) * - Overwrite permissions * - Contradiction detection against existing entries * - TTL and confidence decay tracking */ export class MemoryWriteGate { authorities = new Map(); writeTimestamps = new Map(); contradictionThreshold; defaultTtlMs; defaultDecayRate; enableContradictionTracking; contradictionResolutions = new Map(); constructor(config = {}) { this.contradictionThreshold = config.contradictionThreshold ?? 0.5; this.defaultTtlMs = config.defaultTtlMs ?? null; this.defaultDecayRate = config.defaultDecayRate ?? 0; this.enableContradictionTracking = config.enableContradictionTracking ?? true; if (config.authorities) { for (const authority of config.authorities) { this.authorities.set(authority.agentId, authority); } } } /** * Evaluate whether a write operation should be allowed. * * Steps: * 1. Check authority (namespace allowed, role sufficient) * 2. Check rate limit * 3. Check overwrite permission * 4. Detect contradictions against existing entries * 5. Return decision */ evaluateWrite(authority, key, namespace, value, existingEntries) { const reasons = []; let allowed = true; // Step 1: Authority check const authorityCheck = this.checkAuthority(authority, namespace); if (!authorityCheck.passed) { allowed = false; reasons.push(`Authority check failed: role "${authority.role}" insufficient or namespace "${namespace}" not allowed`); } // Step 2: Rate limit check const rateCheck = this.checkRateLimit(authority); if (!rateCheck.passed) { allowed = false; reasons.push(`Rate limit exceeded: ${rateCheck.writesInWindow}/${rateCheck.limit} writes in window`); } // Step 3: Overwrite check const isOverwrite = existingEntries ? existingEntries.some((e) => e.key === key && e.namespace === namespace) : false; const overwriteCheck = { isOverwrite, allowed: isOverwrite ? authority.canOverwrite : true, }; if (isOverwrite && !authority.canOverwrite) { allowed = false; reasons.push('Overwrite not permitted for this authority'); } // Step 4: Contradiction detection let contradictions = []; if (this.enableContradictionTracking && existingEntries && existingEntries.length > 0) { const raw = this.detectContradictions(value, existingEntries); contradictions = raw.map((c) => ({ existingKey: c.entryKey, description: c.description, })); } // Step 5: Record write timestamp if allowed if (allowed) { this.recordWrite(authority.agentId); } const reason = allowed ? 'Write allowed' : reasons.join('; '); return { allowed, reason, contradictions, authorityCheck, rateCheck, overwriteCheck, }; } /** * Register a new authority or update an existing one */ registerAuthority(authority) { this.authorities.set(authority.agentId, authority); } /** * Compute the current confidence for an entry based on decay over time. * * Uses exponential decay: confidence = initialConfidence * e^(-decayRate * ageHours) * where ageHours is (now - updatedAt) / 3600000 */ computeConfidence(entry) { if (entry.decayRate === 0) return entry.confidence; if (entry.decayRate >= 1) return 0; const now = Date.now(); const ageMs = now - entry.updatedAt; const ageHours = ageMs / 3_600_000; const decayed = entry.confidence * Math.exp(-entry.decayRate * ageHours); return Math.max(0, Math.min(1, decayed)); } /** * Get all entries whose TTL has been exceeded */ getExpiredEntries(entries) { const now = Date.now(); return entries.filter((entry) => { if (entry.ttlMs === null) return false; return now - entry.createdAt > entry.ttlMs; }); } /** * Get entries whose decayed confidence has dropped below a threshold */ getDecayedEntries(entries, threshold) { return entries.filter((entry) => { const currentConfidence = this.computeConfidence(entry); return currentConfidence < threshold; }); } /** * Detect contradictions between a new value and existing entries. * * Uses string-based pattern matching to find conflicting statements * (must vs never, always vs never, require vs forbid, etc.) */ detectContradictions(newValue, existingEntries) { const newText = stringifyForComparison(newValue); const contradictions = []; for (const entry of existingEntries) { const existingText = stringifyForComparison(entry.value); for (const pattern of CONTRADICTION_PATTERNS) { const newMatchesPositive = pattern.positive.test(newText) && pattern.negative.test(existingText); const newMatchesNegative = pattern.negative.test(newText) && pattern.positive.test(existingText); if (newMatchesPositive || newMatchesNegative) { contradictions.push({ entryKey: entry.key, description: pattern.description, }); break; // Only report the first contradiction per entry } } } return contradictions; } /** * Mark a contradiction as resolved */ resolveContradiction(entryKey, resolution) { this.contradictionResolutions.set(entryKey, resolution); } /** * Get the authority for an agent by ID */ getAuthorityFor(agentId) { return this.authorities.get(agentId); } /** * Get the current rate limit status for an agent */ getRateLimitStatus(agentId) { const authority = this.authorities.get(agentId); const limit = authority?.maxWritesPerMinute ?? 0; const now = Date.now(); const windowMs = 60_000; const windowStart = now - windowMs; const timestamps = this.writeTimestamps.get(agentId) ?? []; const recentWrites = timestamps.filter((t) => t > windowStart); // Find the earliest write in the window to compute reset time const resetAt = recentWrites.length > 0 ? recentWrites[0] + windowMs : now; return { writesInWindow: recentWrites.length, limit, resetAt, }; } // ===== Accessors ===== /** Get the default TTL in ms */ getDefaultTtlMs() { return this.defaultTtlMs; } /** Get the default decay rate */ getDefaultDecayRate() { return this.defaultDecayRate; } /** Check if contradiction tracking is enabled */ isContradictionTrackingEnabled() { return this.enableContradictionTracking; } /** Get the contradiction resolution for an entry key */ getContradictionResolution(entryKey) { return this.contradictionResolutions.get(entryKey); } // ===== Private Methods ===== /** * Check whether an authority is allowed to write to a namespace */ checkAuthority(authority, namespace) { const roleLevel = ROLE_HIERARCHY[authority.role]; const minimumLevel = ROLE_HIERARCHY[MINIMUM_WRITE_ROLE]; // Role check: must be at least 'worker' level if (roleLevel < minimumLevel) { return { passed: false, requiredRole: MINIMUM_WRITE_ROLE, actualRole: authority.role, }; } // Namespace check: must be in allowed list, or queen can write anywhere if (authority.role !== 'queen' && !authority.namespaces.includes(namespace)) { return { passed: false, requiredRole: MINIMUM_WRITE_ROLE, actualRole: authority.role, }; } return { passed: true, requiredRole: MINIMUM_WRITE_ROLE, actualRole: authority.role, }; } /** * Check rate limit using a sliding window of write timestamps */ checkRateLimit(authority) { const now = Date.now(); const windowMs = 60_000; const windowStart = now - windowMs; const timestamps = this.writeTimestamps.get(authority.agentId) ?? []; // Prune old timestamps outside the window const recentWrites = timestamps.filter((t) => t > windowStart); this.writeTimestamps.set(authority.agentId, recentWrites); return { passed: recentWrites.length < authority.maxWritesPerMinute, writesInWindow: recentWrites.length, limit: authority.maxWritesPerMinute, }; } /** * Record a write timestamp for an agent */ recordWrite(agentId) { const timestamps = this.writeTimestamps.get(agentId) ?? []; timestamps.push(Date.now()); this.writeTimestamps.set(agentId, timestamps); } } // ============================================================================ // Factory // ============================================================================ /** * Create a MemoryWriteGate instance with optional configuration */ export function createMemoryWriteGate(config) { return new MemoryWriteGate(config); } // ============================================================================ // Helper: Create a MemoryEntry // ============================================================================ /** * Create a new MemoryEntry with defaults applied */ export function createMemoryEntry(key, namespace, value, authority, options = {}) { const now = Date.now(); return { key, namespace, value, valueHash: computeValueHash(value), authority, createdAt: now, updatedAt: now, ttlMs: options.ttlMs ?? null, decayRate: options.decayRate ?? 0, confidence: options.confidence ?? 1, lineage: options.lineage ?? { operation: 'create' }, contradictions: [], }; } //# sourceMappingURL=memory-gate.js.map