Skip to main content
Glama
ForgettingAuditSystem.ts15.3 kB
/** * Forgetting Audit System Implementation * * Provides comprehensive logging and tracking of all forgetting decisions, * user interactions, and system impacts for transparency and compliance. */ import { ForgettingActualImpact, ForgettingAuditEntry, ForgettingAuditQuery, ForgettingAuditSummary, ForgettingUserOverride, IForgettingAuditSystem, RecoveryAttemptRecord, } from "../../interfaces/audit.js"; import { ForgettingDecision, ForgettingEvaluation, } from "../../interfaces/forgetting.js"; export class ForgettingAuditSystem implements IForgettingAuditSystem { private auditEntries: Map<string, ForgettingAuditEntry> = new Map(); private nextAuditId: number = 1; constructor() { // Initialization complete } async logForgettingDecision( memory_id: string, memory_type: "episodic" | "semantic", memory_content_summary: string, decision: ForgettingDecision, evaluation: ForgettingEvaluation, execution_method: "automatic" | "manual" | "user_requested", privacy_level: | "public" | "private" | "confidential" | "restricted" = "private" ): Promise<string> { const audit_id = `audit_${this.nextAuditId++}_${Date.now()}`; const timestamp = Date.now(); const auditEntry: ForgettingAuditEntry = { audit_id, timestamp, memory_id, memory_type, memory_content_summary, decision, evaluation, execution_status: "pending", execution_method, user_consent_requested: decision.user_consent_required ?? false, privacy_level, secure_deletion_applied: false, audit_trail_encrypted: privacy_level === "confidential" || privacy_level === "restricted", }; this.auditEntries.set(audit_id, auditEntry); console.error(`Logged forgetting decision for memory ${memory_id}`, { audit_id, memory_type, execution_method, privacy_level, user_consent_required: decision.user_consent_required, }); return audit_id; } async updateExecutionStatus( audit_id: string, status: "executed" | "cancelled" | "failed", actual_impact?: ForgettingActualImpact ): Promise<void> { const entry = this.auditEntries.get(audit_id); if (!entry) { throw new Error(`Audit entry not found: ${audit_id}`); } entry.execution_status = status; entry.execution_timestamp = Date.now(); if (actual_impact) { entry.actual_impact = actual_impact; } // Mark secure deletion as applied if execution was successful if (status === "executed" && entry.privacy_level === "confidential") { entry.secure_deletion_applied = true; } console.error(`Updated execution status for ${audit_id}`, { audit_id, status, memory_id: entry.memory_id, actual_impact: actual_impact ? "provided" : "not_provided", }); } async recordUserConsent( audit_id: string, consent_granted: boolean, user_feedback?: string ): Promise<void> { const entry = this.auditEntries.get(audit_id); if (!entry) { throw new Error(`Audit entry not found: ${audit_id}`); } entry.user_consent_granted = consent_granted; if (user_feedback) { entry.user_feedback = user_feedback; } console.error(`Recorded user consent for ${audit_id}`, { audit_id, consent_granted, memory_id: entry.memory_id, has_feedback: !!user_feedback, }); } async recordUserOverride( audit_id: string, override: ForgettingUserOverride ): Promise<void> { const entry = this.auditEntries.get(audit_id); if (!entry) { throw new Error(`Audit entry not found: ${audit_id}`); } entry.user_override = override; console.error(`Recorded user override for ${audit_id}`, { audit_id, override_type: override.override_type, memory_id: entry.memory_id, override_reason: override.override_reason, }); } async recordRecoveryAttempt( audit_id: string, recovery_record: RecoveryAttemptRecord ): Promise<void> { const entry = this.auditEntries.get(audit_id); if (!entry) { throw new Error(`Audit entry not found: ${audit_id}`); } if (!entry.recovery_attempts) { entry.recovery_attempts = []; } entry.recovery_attempts.push(recovery_record); console.error(`Recorded recovery attempt for ${audit_id}`, { audit_id, attempt_id: recovery_record.attempt_id, recovery_success: recovery_record.recovery_success, memory_id: entry.memory_id, user_initiated: recovery_record.user_initiated, }); } async queryAuditEntries( query: ForgettingAuditQuery ): Promise<ForgettingAuditEntry[]> { let entries = Array.from(this.auditEntries.values()); // Apply filters if (query.start_timestamp) { entries = entries.filter( (entry) => entry.timestamp >= query.start_timestamp! ); } if (query.end_timestamp) { entries = entries.filter( (entry) => entry.timestamp <= query.end_timestamp! ); } if (query.memory_ids && query.memory_ids.length > 0) { entries = entries.filter((entry) => query.memory_ids!.includes(entry.memory_id) ); } if (query.execution_status && query.execution_status.length > 0) { entries = entries.filter((entry) => query.execution_status!.includes(entry.execution_status) ); } if (query.execution_method && query.execution_method.length > 0) { entries = entries.filter((entry) => query.execution_method!.includes(entry.execution_method) ); } if (query.user_consent_granted !== undefined) { entries = entries.filter( (entry) => entry.user_consent_granted === query.user_consent_granted ); } if (query.privacy_level && query.privacy_level.length > 0) { entries = entries.filter((entry) => query.privacy_level!.includes(entry.privacy_level) ); } // Sort by timestamp (newest first) entries.sort((a, b) => b.timestamp - a.timestamp); // Apply pagination const offset = query.offset ?? 0; const limit = query.limit ?? 100; entries = entries.slice(offset, offset + limit); console.error(`Query returned ${entries.length} audit entries`, { total_entries: this.auditEntries.size, query_filters: Object.keys(query).length, }); return entries; } async getAuditSummary( start_timestamp?: number, end_timestamp?: number ): Promise<ForgettingAuditSummary> { let entries = Array.from(this.auditEntries.values()); // Apply time filters if (start_timestamp) { entries = entries.filter((entry) => entry.timestamp >= start_timestamp); } if (end_timestamp) { entries = entries.filter((entry) => entry.timestamp <= end_timestamp); } // Calculate summary statistics const total_entries = entries.length; const entries_by_status: Record<string, number> = {}; const entries_by_method: Record<string, number> = {}; let total_memory_freed_bytes = 0; let total_processing_improvement_ms = 0; let processing_improvement_count = 0; let consent_requested_count = 0; let consent_granted_count = 0; let recovery_attempt_count = 0; let recovery_success_count = 0; for (const entry of entries) { // Count by status entries_by_status[entry.execution_status] = (entries_by_status[entry.execution_status] || 0) + 1; // Count by method entries_by_method[entry.execution_method] = (entries_by_method[entry.execution_method] || 0) + 1; // Aggregate impact metrics if (entry.actual_impact) { total_memory_freed_bytes += entry.actual_impact.memory_space_freed_bytes ?? 0; if (entry.actual_impact.processing_speed_improvement_ms) { total_processing_improvement_ms += entry.actual_impact.processing_speed_improvement_ms; processing_improvement_count++; } } // Count consent metrics if (entry.user_consent_requested) { consent_requested_count++; if (entry.user_consent_granted) { consent_granted_count++; } } // Count recovery metrics if (entry.recovery_attempts) { recovery_attempt_count += entry.recovery_attempts.length; recovery_success_count += entry.recovery_attempts.filter( (attempt) => attempt.recovery_success ).length; } } const average_processing_improvement_ms = processing_improvement_count > 0 ? total_processing_improvement_ms / processing_improvement_count : 0; const user_consent_rate = consent_requested_count > 0 ? consent_granted_count / consent_requested_count : 0; const recovery_attempt_rate = total_entries > 0 ? recovery_attempt_count / total_entries : 0; const recovery_success_rate = recovery_attempt_count > 0 ? recovery_success_count / recovery_attempt_count : 0; const time_period = { start_timestamp: start_timestamp || (entries.length > 0 ? Math.min(...entries.map((e) => e.timestamp)) : Date.now()), end_timestamp: end_timestamp || (entries.length > 0 ? Math.max(...entries.map((e) => e.timestamp)) : Date.now()), }; return { total_entries, entries_by_status, entries_by_method, total_memory_freed_bytes, average_processing_improvement_ms, user_consent_rate, recovery_attempt_rate, recovery_success_rate, time_period, }; } async exportAuditData( query: ForgettingAuditQuery, format: "json" | "csv" | "xml" ): Promise<string> { const entries = await this.queryAuditEntries(query); switch (format) { case "json": return JSON.stringify(entries, null, 2); case "csv": return this.exportToCsv(entries); case "xml": return this.exportToXml(entries); default: throw new Error(`Unsupported export format: ${format}`); } } private exportToCsv(entries: ForgettingAuditEntry[]): string { if (entries.length === 0) { return "No data to export"; } const headers = [ "audit_id", "timestamp", "memory_id", "memory_type", "execution_status", "execution_method", "user_consent_requested", "user_consent_granted", "privacy_level", "secure_deletion_applied", ]; const csvRows = [headers.join(",")]; for (const entry of entries) { const row = [ entry.audit_id, new Date(entry.timestamp).toISOString(), entry.memory_id, entry.memory_type, entry.execution_status, entry.execution_method, entry.user_consent_requested.toString(), entry.user_consent_granted?.toString() ?? "", entry.privacy_level, entry.secure_deletion_applied.toString(), ]; csvRows.push(row.join(",")); } return csvRows.join("\n"); } private exportToXml(entries: ForgettingAuditEntry[]): string { let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<audit_entries>\n'; for (const entry of entries) { xml += " <entry>\n"; xml += ` <audit_id>${entry.audit_id}</audit_id>\n`; xml += ` <timestamp>${new Date( entry.timestamp ).toISOString()}</timestamp>\n`; xml += ` <memory_id>${entry.memory_id}</memory_id>\n`; xml += ` <memory_type>${entry.memory_type}</memory_type>\n`; xml += ` <execution_status>${entry.execution_status}</execution_status>\n`; xml += ` <execution_method>${entry.execution_method}</execution_method>\n`; xml += ` <user_consent_requested>${entry.user_consent_requested}</user_consent_requested>\n`; xml += ` <privacy_level>${entry.privacy_level}</privacy_level>\n`; xml += ` <secure_deletion_applied>${entry.secure_deletion_applied}</secure_deletion_applied>\n`; xml += " </entry>\n"; } xml += "</audit_entries>"; return xml; } async purgeOldEntries( retention_period_days: number, preserve_important: boolean ): Promise<number> { const cutoff_timestamp = Date.now() - retention_period_days * 24 * 60 * 60 * 1000; let purged_count = 0; for (const [audit_id, entry] of this.auditEntries.entries()) { if (entry.timestamp < cutoff_timestamp) { // Check if we should preserve important entries if (preserve_important) { const is_important = entry.privacy_level === "confidential" || entry.privacy_level === "restricted" || entry.user_override || (entry.recovery_attempts?.length ?? 0) > 0; if (is_important) { continue; } } this.auditEntries.delete(audit_id); purged_count++; } } console.error(`Purged ${purged_count} old audit entries`, { retention_period_days, preserve_important, remaining_entries: this.auditEntries.size, }); return purged_count; } // Additional utility methods /** * Get audit entry by ID */ async getAuditEntry(audit_id: string): Promise<ForgettingAuditEntry | null> { return this.auditEntries.get(audit_id) ?? null; } /** * Get all audit entries for a specific memory */ async getMemoryAuditHistory( memory_id: string ): Promise<ForgettingAuditEntry[]> { return Array.from(this.auditEntries.values()) .filter((entry) => entry.memory_id === memory_id) .sort((a, b) => b.timestamp - a.timestamp); } /** * Get system health metrics */ async getSystemHealth(): Promise<{ total_entries: number; recent_entries_24h: number; pending_consent_requests: number; failed_executions_24h: number; average_processing_time_ms: number; }> { const now = Date.now(); const twentyFourHoursAgo = now - 24 * 60 * 60 * 1000; const all_entries = Array.from(this.auditEntries.values()); const recent_entries = all_entries.filter( (entry) => entry.timestamp >= twentyFourHoursAgo ); const pending_consent_requests = all_entries.filter( (entry) => entry.user_consent_requested && entry.user_consent_granted === undefined ).length; const failed_executions_24h = recent_entries.filter( (entry) => entry.execution_status === "failed" ).length; // Calculate average processing time for executed entries const executed_entries = all_entries.filter( (entry) => entry.execution_status === "executed" && entry.execution_timestamp ); const average_processing_time_ms = executed_entries.length > 0 ? executed_entries.reduce( (sum, entry) => sum + (entry.execution_timestamp! - entry.timestamp), 0 ) / executed_entries.length : 0; return { total_entries: all_entries.length, recent_entries_24h: recent_entries.length, pending_consent_requests, failed_executions_24h, average_processing_time_ms, }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ThoughtMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server