@yamo/memory-mesh 2.0.1

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 (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -0
  3. package/bin/memory_mesh.js +69 -0
  4. package/bin/scrubber.js +81 -0
  5. package/index.d.ts +111 -0
  6. package/lib/adapters/index.js +3 -0
  7. package/lib/embeddings/factory.js +150 -0
  8. package/lib/embeddings/index.js +2 -0
  9. package/lib/embeddings/service.js +586 -0
  10. package/lib/index.js +18 -0
  11. package/lib/lancedb/client.js +631 -0
  12. package/lib/lancedb/config.js +215 -0
  13. package/lib/lancedb/errors.js +144 -0
  14. package/lib/lancedb/index.js +4 -0
  15. package/lib/lancedb/schema.js +197 -0
  16. package/lib/memory/index.js +3 -0
  17. package/lib/memory/memory-context-manager.js +388 -0
  18. package/lib/memory/memory-mesh.js +910 -0
  19. package/lib/memory/memory-translator.js +130 -0
  20. package/lib/memory/migrate-memory.js +227 -0
  21. package/lib/memory/migrate-to-v2.js +120 -0
  22. package/lib/memory/scorer.js +85 -0
  23. package/lib/memory/vector-memory.js +364 -0
  24. package/lib/privacy/audit-logger.js +176 -0
  25. package/lib/privacy/dlp-redactor.js +72 -0
  26. package/lib/privacy/index.js +10 -0
  27. package/lib/reporting/skill-report-generator.js +283 -0
  28. package/lib/scrubber/.gitkeep +1 -0
  29. package/lib/scrubber/config/defaults.js +62 -0
  30. package/lib/scrubber/errors/scrubber-error.js +43 -0
  31. package/lib/scrubber/index.js +25 -0
  32. package/lib/scrubber/scrubber.js +130 -0
  33. package/lib/scrubber/stages/chunker.js +103 -0
  34. package/lib/scrubber/stages/metadata-annotator.js +74 -0
  35. package/lib/scrubber/stages/normalizer.js +59 -0
  36. package/lib/scrubber/stages/semantic-filter.js +61 -0
  37. package/lib/scrubber/stages/structural-cleaner.js +82 -0
  38. package/lib/scrubber/stages/validator.js +66 -0
  39. package/lib/scrubber/telemetry.js +66 -0
  40. package/lib/scrubber/utils/hash.js +39 -0
  41. package/lib/scrubber/utils/html-parser.js +45 -0
  42. package/lib/scrubber/utils/pattern-matcher.js +63 -0
  43. package/lib/scrubber/utils/token-counter.js +31 -0
  44. package/lib/search/filter.js +275 -0
  45. package/lib/search/hybrid.js +137 -0
  46. package/lib/search/index.js +3 -0
  47. package/lib/search/pattern-miner.js +160 -0
  48. package/lib/utils/error-sanitizer.js +84 -0
  49. package/lib/utils/handoff-validator.js +85 -0
  50. package/lib/utils/index.js +4 -0
  51. package/lib/utils/spinner.js +190 -0
  52. package/lib/utils/streaming-client.js +128 -0
  53. package/package.json +39 -0
  54. package/skills/SKILL.md +462 -0
  55. package/skills/skill-scrubber.yamo +41 -0
@@ -0,0 +1,364 @@
1
+ import { fileURLToPath } from 'url';
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import crypto from "crypto";
5
+
6
+ /**
7
+ * Vector Memory Manager
8
+ * Lightweight vector database for YAMO Skills
9
+ * Uses simple cosine similarity for semantic search
10
+ *
11
+ * NOTE: This is a lightweight implementation. For production with large datasets,
12
+ * consider integrating LanceDB: npm install @lancedb/lancedb
13
+ */
14
+ class VectorMemory {
15
+ constructor(options = {}) {
16
+ this.storePath = options.storePath || path.join(__dirname, '..', 'data', 'vector_memory.json');
17
+ this.embeddingDim = options.embeddingDim || 384; // Default for sentence transformers
18
+ this.maxEntries = options.maxEntries || 10000;
19
+ this.ensureStorageDirectory();
20
+ this.load();
21
+ }
22
+
23
+ /**
24
+ * Ensure storage directory exists
25
+ */
26
+ ensureStorageDirectory() {
27
+ const dir = path.dirname(this.storePath);
28
+ if (!fs.existsSync(dir)) {
29
+ fs.mkdirSync(dir, { recursive: true });
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Load existing memory from disk
35
+ */
36
+ load() {
37
+ if (fs.existsSync(this.storePath)) {
38
+ try {
39
+ const data = JSON.parse(fs.readFileSync(this.storePath, 'utf8'));
40
+ this.memories = data.memories || [];
41
+ this.metadata = data.metadata || { version: '1.0', created: new Date().toISOString() };
42
+ } catch (error) {
43
+ const message = error instanceof Error ? error.message : String(error);
44
+ console.error('Failed to load vector memory:', message);
45
+ this.vectorStore = [];
46
+ }
47
+ } else {
48
+ this.memories = [];
49
+ this.metadata = { version: '1.0', created: new Date().toISOString() };
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Save memory to disk
55
+ */
56
+ save() {
57
+ const data = {
58
+ metadata: {
59
+ ...this.metadata,
60
+ last_updated: new Date().toISOString(),
61
+ entry_count: this.memories.length
62
+ },
63
+ memories: this.memories
64
+ };
65
+
66
+ fs.writeFileSync(this.storePath, JSON.stringify(data, null, 2));
67
+ }
68
+
69
+ /**
70
+ * Generate a simple embedding from text
71
+ * NOTE: This is a basic implementation. For production, use a proper embedding model like:
72
+ * - sentence-transformers (Python)
73
+ * - @xenova/transformers (JavaScript)
74
+ * - OpenAI embeddings API
75
+ */
76
+ generateEmbedding(text) {
77
+ // Simple word-based embedding (not semantic, but functional for demo)
78
+ const words = text.toLowerCase().split(/\s+/);
79
+ const embedding = new Array(this.embeddingDim).fill(0);
80
+
81
+ // Hash each word and update embedding
82
+ words.forEach((word, idx) => {
83
+ const hash = crypto.createHash('sha256').update(word).digest();
84
+ for (let i = 0; i < this.embeddingDim; i++) {
85
+ embedding[i] += hash[i % hash.length] / 255;
86
+ }
87
+ });
88
+
89
+ // Normalize
90
+ const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));
91
+ return embedding.map(val => val / (magnitude || 1));
92
+ }
93
+
94
+ /**
95
+ * Calculate cosine similarity between two vectors
96
+ */
97
+ cosineSimilarity(vecA, vecB) {
98
+ if (vecA.length !== vecB.length) {
99
+ throw new Error('Vectors must have same dimensions');
100
+ }
101
+
102
+ const dotProduct = vecA.reduce((sum, val, i) => sum + val * vecB[i], 0);
103
+ const magnitudeA = Math.sqrt(vecA.reduce((sum, val) => sum + val * val, 0));
104
+ const magnitudeB = Math.sqrt(vecB.reduce((sum, val) => sum + val * val, 0));
105
+
106
+ return dotProduct / ((magnitudeA * magnitudeB) || 1);
107
+ }
108
+
109
+ /**
110
+ * Store a memory with automatic embedding generation
111
+ * @param {string} content - Text content to store
112
+ * @param {Object} metadata - Additional metadata
113
+ * @returns {Object} - Created memory entry
114
+ */
115
+ store(content, metadata = {}) {
116
+ const embedding = this.generateEmbedding(content);
117
+
118
+ const memory = {
119
+ id: crypto.randomUUID(),
120
+ content,
121
+ embedding,
122
+ metadata: {
123
+ ...metadata,
124
+ created_at: new Date().toISOString(),
125
+ content_length: content.length
126
+ }
127
+ };
128
+
129
+ this.memories.push(memory);
130
+
131
+ // Enforce max entries (FIFO)
132
+ if (this.memories.length > this.maxEntries) {
133
+ this.memories.shift();
134
+ }
135
+
136
+ this.save();
137
+ return memory;
138
+ }
139
+
140
+ /**
141
+ * Search for similar memories
142
+ * @param {string} query - Search query
143
+ * @param {number} topK - Number of results to return
144
+ * @param {number} threshold - Minimum similarity threshold (0-1)
145
+ * @returns {Array} - Array of matching memories with scores
146
+ */
147
+ search(query, topK = 5, threshold = 0.0) {
148
+ const queryEmbedding = this.generateEmbedding(query);
149
+
150
+ // Calculate similarity for all memories
151
+ const results = this.memories.map(memory => ({
152
+ ...memory,
153
+ similarity: this.cosineSimilarity(queryEmbedding, memory.embedding)
154
+ }));
155
+
156
+ // Filter by threshold and sort by similarity
157
+ return results
158
+ .filter(r => r.similarity >= threshold)
159
+ .sort((a, b) => b.similarity - a.similarity)
160
+ .slice(0, topK)
161
+ .map(({ embedding, ...rest }) => rest); // Remove embedding from results
162
+ }
163
+
164
+ /**
165
+ * Retrieve memory by ID
166
+ */
167
+ retrieve(id) {
168
+ return this.memories.find(m => m.id === id);
169
+ }
170
+
171
+ /**
172
+ * Delete memory by ID
173
+ */
174
+ delete(id) {
175
+ const initialLength = this.memories.length;
176
+ this.memories = this.memories.filter(m => m.id !== id);
177
+
178
+ if (this.memories.length < initialLength) {
179
+ this.save();
180
+ return true;
181
+ }
182
+ return false;
183
+ }
184
+
185
+ /**
186
+ * Clear all memories
187
+ */
188
+ clear() {
189
+ this.memories = [];
190
+ this.save();
191
+ }
192
+
193
+ /**
194
+ * Get statistics about stored memories
195
+ */
196
+ stats() {
197
+ return {
198
+ total_memories: this.memories.length,
199
+ embedding_dim: this.embeddingDim,
200
+ max_entries: this.maxEntries,
201
+ storage_path: this.storePath,
202
+ metadata: this.metadata,
203
+ avg_content_length: this.memories.length > 0
204
+ ? Math.round(this.memories.reduce((sum, m) => sum + m.metadata.content_length, 0) / this.memories.length)
205
+ : 0
206
+ };
207
+ }
208
+
209
+ /**
210
+ * Semantic clustering (group similar memories)
211
+ */
212
+ cluster(similarityThreshold = 0.7) {
213
+ const clusters = [];
214
+ const processed = new Set();
215
+
216
+ this.memories.forEach((memory, idx) => {
217
+ if (processed.has(idx)) return;
218
+
219
+ const cluster = {
220
+ representative: memory.content.substring(0, 100),
221
+ members: [{ id: memory.id, content: memory.content }]
222
+ };
223
+
224
+ // Find similar memories
225
+ this.memories.forEach((other, otherIdx) => {
226
+ if (idx === otherIdx || processed.has(otherIdx)) return;
227
+
228
+ const similarity = this.cosineSimilarity(memory.embedding, other.embedding);
229
+ if (similarity >= similarityThreshold) {
230
+ cluster.members.push({ id: other.id, content: other.content });
231
+ processed.add(otherIdx);
232
+ }
233
+ });
234
+
235
+ processed.add(idx);
236
+ clusters.push(cluster);
237
+ });
238
+
239
+ return clusters;
240
+ }
241
+
242
+ /**
243
+ * Export memories to JSON
244
+ */
245
+ export() {
246
+ return {
247
+ metadata: this.metadata,
248
+ memories: this.memories.map(({ embedding, ...rest }) => rest)
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Import memories from JSON
254
+ */
255
+ import(data) {
256
+ if (data.memories) {
257
+ data.memories.forEach(memory => {
258
+ if (!memory.embedding) {
259
+ memory.embedding = this.generateEmbedding(memory.content);
260
+ }
261
+ this.memories.push(memory);
262
+ });
263
+ this.save();
264
+ }
265
+ }
266
+ }
267
+
268
+ // CLI usage
269
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
270
+ const memory = new VectorMemory();
271
+ const command = process.argv[2];
272
+
273
+ if (command === 'store') {
274
+ const content = process.argv.slice(3).join(' ');
275
+ if (!content) {
276
+ console.log('Usage: node vector_memory.js store <content>');
277
+ process.exit(1);
278
+ }
279
+
280
+ const result = memory.store(content, { source: 'cli' });
281
+ console.log('✅ Stored:', result.id);
282
+ console.log(' Content:', content.substring(0, 100) + '...');
283
+
284
+ } else if (command === 'search') {
285
+ const query = process.argv.slice(3).join(' ');
286
+ if (!query) {
287
+ console.log('Usage: node vector_memory.js search <query>');
288
+ process.exit(1);
289
+ }
290
+
291
+ const results = memory.search(query, 5);
292
+ console.log(`\n🔍 Search results for: "${query}"\n`);
293
+
294
+ if (results.length === 0) {
295
+ console.log('No results found');
296
+ } else {
297
+ results.forEach((result, idx) => {
298
+ console.log(`${idx + 1}. [${(result.similarity * 100).toFixed(1)}%] ${result.content.substring(0, 100)}...`);
299
+ console.log(` ID: ${result.id}`);
300
+ console.log(` Created: ${result.metadata.created_at}`);
301
+ console.log('');
302
+ });
303
+ }
304
+
305
+ } else if (command === 'stats') {
306
+ const stats = memory.stats();
307
+ console.log('\n📊 Vector Memory Statistics\n');
308
+ console.log(`Total Memories: ${stats.total_memories}`);
309
+ console.log(`Embedding Dimensions: ${stats.embedding_dim}`);
310
+ console.log(`Max Entries: ${stats.max_entries}`);
311
+ console.log(`Average Content Length: ${stats.avg_content_length} chars`);
312
+ console.log(`Storage Path: ${stats.storage_path}`);
313
+
314
+ } else if (command === 'cluster') {
315
+ const clusters = memory.cluster(0.7);
316
+ console.log(`\n🔗 Found ${clusters.length} clusters\n`);
317
+
318
+ clusters.forEach((cluster, idx) => {
319
+ console.log(`Cluster ${idx + 1}: ${cluster.members.length} memories`);
320
+ console.log(` Representative: ${cluster.representative}...`);
321
+ console.log('');
322
+ });
323
+
324
+ } else if (command === 'clear') {
325
+ memory.clear();
326
+ console.log('✅ All memories cleared');
327
+
328
+ } else if (command === 'test') {
329
+ console.log('🧪 Testing Vector Memory\n');
330
+
331
+ // Store some test memories
332
+ console.log('Storing test memories...');
333
+ memory.store('How to implement authentication in Node.js using JWT', { type: 'tutorial' });
334
+ memory.store('Best practices for securing API endpoints', { type: 'security' });
335
+ memory.store('Node.js JWT authentication guide', { type: 'tutorial' });
336
+ memory.store('How to use Docker for development', { type: 'devops' });
337
+ memory.store('Setting up HTTPS with Let\'s Encrypt', { type: 'security' });
338
+
339
+ console.log('✅ Stored 5 test memories\n');
340
+
341
+ // Search
342
+ console.log('Searching for "JWT authentication"...');
343
+ const results = memory.search('JWT authentication', 3);
344
+ console.log(`Found ${results.length} results:\n`);
345
+
346
+ results.forEach((result, idx) => {
347
+ console.log(`${idx + 1}. [${(result.similarity * 100).toFixed(1)}%] ${result.content}`);
348
+ });
349
+
350
+ console.log('\n✅ Test completed');
351
+
352
+ } else {
353
+ console.log('YAMO Vector Memory CLI');
354
+ console.log('Usage:');
355
+ console.log(' node vector_memory.js store <content> - Store a memory');
356
+ console.log(' node vector_memory.js search <query> - Search for similar memories');
357
+ console.log(' node vector_memory.js stats - Show statistics');
358
+ console.log(' node vector_memory.js cluster - Find semantic clusters');
359
+ console.log(' node vector_memory.js clear - Clear all memories');
360
+ console.log(' node vector_memory.js test - Run test');
361
+ }
362
+ }
363
+
364
+ export default VectorMemory;
@@ -0,0 +1,176 @@
1
+ import { fileURLToPath } from 'url';
2
+ import crypto from "crypto";
3
+ import fs from "fs";
4
+ import path from "path";
5
+
6
+ /**
7
+ * Tamper-Proof Audit Logger
8
+ * Implements blockchain-like integrity using HMAC chain with file locking
9
+ */
10
+ class AuditLogger {
11
+ constructor(options = {}) {
12
+ // @ts-ignore
13
+ this.logPath = options.logPath || path.join(process.cwd(), 'logs', 'audit.log');
14
+ this.lockPath = this.logPath + '.lock';
15
+ // @ts-ignore
16
+ this.lockTimeout = options.lockTimeout || 5000;
17
+ // @ts-ignore
18
+ this.lockRetryDelay = options.lockRetryDelay || 50;
19
+ this.secret = process.env.AUDIT_SECRET || this.generateSecret();
20
+ this.ensureLogDirectory();
21
+
22
+ if (!process.env.AUDIT_SECRET) {
23
+ console.warn('⚠️ Using auto-generated AUDIT_SECRET. Set AUDIT_SECRET in .env for production');
24
+ }
25
+ }
26
+
27
+ ensureLogDirectory() {
28
+ const logDir = path.dirname(this.logPath);
29
+ if (!fs.existsSync(logDir)) {
30
+ fs.mkdirSync(logDir, { recursive: true });
31
+ }
32
+ }
33
+
34
+ generateSecret() {
35
+ return crypto.randomBytes(32).toString('hex');
36
+ }
37
+
38
+ acquireLock() {
39
+ const startTime = Date.now();
40
+ const lockData = {
41
+ pid: process.pid,
42
+ timestamp: new Date().toISOString()
43
+ };
44
+
45
+ while (Date.now() - startTime < this.lockTimeout) {
46
+ try {
47
+ fs.writeFileSync(this.lockPath, JSON.stringify(lockData), { flag: 'wx' });
48
+ return true;
49
+ } catch (err) {
50
+ // @ts-ignore
51
+ if (err && err.code === 'EEXIST') {
52
+ try {
53
+ const existingLock = JSON.parse(fs.readFileSync(this.lockPath, 'utf8'));
54
+ const lockAge = Date.now() - new Date(existingLock.timestamp).getTime();
55
+ if (lockAge > this.lockTimeout) {
56
+ fs.unlinkSync(this.lockPath);
57
+ }
58
+ } catch { /* ignore */ }
59
+ const start = Date.now();
60
+ while (Date.now() - start < this.lockRetryDelay) {}
61
+ } else {
62
+ throw err;
63
+ }
64
+ }
65
+ }
66
+ return false;
67
+ }
68
+
69
+ releaseLock() {
70
+ try {
71
+ if (fs.existsSync(this.lockPath)) {
72
+ fs.unlinkSync(this.lockPath);
73
+ }
74
+ } catch (err) {
75
+ const message = err instanceof Error ? err.message : String(err);
76
+ console.error(`⚠️ Warning: Failed to release lock: ${message}`);
77
+ }
78
+ }
79
+
80
+ withLock(fn) {
81
+ if (!this.acquireLock()) {
82
+ throw new Error(`Failed to acquire log file lock after ${this.lockTimeout}ms`);
83
+ }
84
+ try {
85
+ return fn();
86
+ } finally {
87
+ this.releaseLock();
88
+ }
89
+ }
90
+
91
+ _getLastHashUnsafe() {
92
+ if (!fs.existsSync(this.logPath)) {
93
+ return '0000000000000000000000000000000000000000000000000000000000000000';
94
+ }
95
+ const lines = fs.readFileSync(this.logPath, 'utf8').split('\n').filter(Boolean);
96
+ if (lines.length === 0) return '0000000000000000000000000000000000000000000000000000000000000000';
97
+ try {
98
+ const lastEntry = JSON.parse(lines[lines.length - 1]);
99
+ return lastEntry.integrity_hash || '0000000000000000000000000000000000000000000000000000000000000000';
100
+ } catch {
101
+ return '0000000000000000000000000000000000000000000000000000000000000000';
102
+ }
103
+ }
104
+
105
+ getLastHash() {
106
+ return this.withLock(() => this._getLastHashUnsafe());
107
+ }
108
+
109
+ log(event) {
110
+ return this.withLock(() => {
111
+ const entry = {
112
+ '@timestamp': new Date().toISOString(),
113
+ sequence: this._getSequenceNumberUnsafe(),
114
+ prev_hash: this._getLastHashUnsafe(),
115
+ ...event
116
+ };
117
+ const hash = crypto.createHmac('sha256', this.secret).update(JSON.stringify(entry)).digest('hex');
118
+ entry.integrity_hash = hash;
119
+ fs.appendFileSync(this.logPath, JSON.stringify(entry) + '\n');
120
+ return entry;
121
+ });
122
+ }
123
+
124
+ _getSequenceNumberUnsafe() {
125
+ if (!fs.existsSync(this.logPath)) return 1;
126
+ const lines = fs.readFileSync(this.logPath, 'utf8').split('\n').filter(Boolean);
127
+ return lines.length + 1;
128
+ }
129
+
130
+ verify() {
131
+ if (!fs.existsSync(this.logPath)) {
132
+ // @ts-ignore
133
+ return { valid: true, errors: [], total: 0, message: 'No audit log exists' };
134
+ }
135
+ const lines = fs.readFileSync(this.logPath, 'utf8').split('\n').filter(Boolean);
136
+ const errors = [];
137
+ let prevHash = '0000000000000000000000000000000000000000000000000000000000000000';
138
+ for (let i = 0; i < lines.length; i++) {
139
+ try {
140
+ const entry = JSON.parse(lines[i]);
141
+ const { integrity_hash, ...data } = entry;
142
+ if (entry.prev_hash !== prevHash) {
143
+ errors.push({ line: i + 1, type: 'chain_broken', message: `Previous hash mismatch.` });
144
+ }
145
+ const expected = crypto.createHmac('sha256', this.secret).update(JSON.stringify(data)).digest('hex');
146
+ if (expected !== integrity_hash) {
147
+ errors.push({ line: i + 1, type: 'tampered', message: `Integrity hash mismatch.` });
148
+ }
149
+ prevHash = integrity_hash;
150
+ } catch (error) {
151
+ const message = error instanceof Error ? error.message : String(error);
152
+ errors.push({ line: i + 1, type: 'parse_error', message: `Failed to parse JSON: ${message}` });
153
+ }
154
+ }
155
+ return {
156
+ valid: errors.length === 0,
157
+ errors,
158
+ total: lines.length,
159
+ // @ts-ignore
160
+ message: errors.length === 0 ? `✅ All ${lines.length} entries verified successfully` : `❌ ${errors.length} issues found`
161
+ };
162
+ }
163
+
164
+ export() {
165
+ if (!fs.existsSync(this.logPath)) return [];
166
+ const lines = fs.readFileSync(this.logPath, 'utf8').split('\n').filter(Boolean);
167
+ return lines.map(line => JSON.parse(line));
168
+ }
169
+
170
+ tail(count = 10) {
171
+ const entries = this.export();
172
+ return entries.slice(-count);
173
+ }
174
+ }
175
+
176
+ export default AuditLogger;
@@ -0,0 +1,72 @@
1
+ import { fileURLToPath } from 'url';
2
+ import fs from "fs";
3
+ import path from "path";
4
+
5
+ /**
6
+ * DLP (Data Loss Prevention) Redactor
7
+ */
8
+ class DLPRedactor {
9
+ constructor(patternsPath = null) {
10
+ this.patterns = this.getMinimalPatterns();
11
+ this.counters = {};
12
+ this.redactionMap = {};
13
+ }
14
+
15
+ getMinimalPatterns() {
16
+ return {
17
+ pii: {
18
+ email: {
19
+ pattern: String.raw`\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b`,
20
+ token_prefix: "EMAIL"
21
+ }
22
+ },
23
+ secrets: {
24
+ generic_api_key: {
25
+ pattern: String.raw`(?:api[_-]?key)[\s:=]+['"]?([a-zA-Z0-9_\-\.]{20,})['"]?`,
26
+ token_prefix: "API_KEY",
27
+ case_insensitive: true
28
+ }
29
+ },
30
+ internal_assets: {}
31
+ };
32
+ }
33
+
34
+ redact(text, privacyLevel = 'medium') {
35
+ if (!text) return { sanitized: '', redactionMap: {}, findings: [], stats: {} };
36
+ let sanitized = text;
37
+ const findings = [];
38
+ this.counters = {};
39
+ this.redactionMap = {};
40
+ return {
41
+ sanitized,
42
+ redactionMap: this.redactionMap,
43
+ findings,
44
+ // @ts-ignore
45
+ stats: {
46
+ original_length: text.length,
47
+ sanitized_length: sanitized.length,
48
+ redactions: findings.length,
49
+ privacy_level: privacyLevel
50
+ }
51
+ };
52
+ }
53
+
54
+ rehydrate(text, redactionMap = null) {
55
+ if (!text) return '';
56
+ return text;
57
+ }
58
+
59
+ getCategoriesToScan(privacyLevel) {
60
+ return ['pii', 'secrets'];
61
+ }
62
+
63
+ generateAuditLog(result, metadata = {}) {
64
+ return {
65
+ '@timestamp': new Date().toISOString(),
66
+ event: { kind: 'event', category: 'process', type: 'info', action: 'dlp_redaction' },
67
+ message: `DLP scan completed`
68
+ };
69
+ }
70
+ }
71
+
72
+ export default DLPRedactor;
@@ -0,0 +1,10 @@
1
+ import DLPRedactorImport from './dlp-redactor.js';
2
+ import AuditLoggerImport from './audit-logger.js';
3
+
4
+ export const DLPRedactor = DLPRedactorImport;
5
+ export const AuditLogger = AuditLoggerImport;
6
+
7
+ export default {
8
+ DLPRedactor: DLPRedactorImport,
9
+ AuditLogger: AuditLoggerImport
10
+ };