@ulrichc1/sparn 1.0.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,948 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ DEFAULT_CONFIG: () => DEFAULT_CONFIG,
34
+ createBTSPEmbedder: () => createBTSPEmbedder,
35
+ createClaudeCodeAdapter: () => createClaudeCodeAdapter,
36
+ createConfidenceStates: () => createConfidenceStates,
37
+ createEngramScorer: () => createEngramScorer,
38
+ createGenericAdapter: () => createGenericAdapter,
39
+ createKVMemory: () => createKVMemory,
40
+ createLogger: () => createLogger,
41
+ createSleepCompressor: () => createSleepCompressor,
42
+ createSparsePruner: () => createSparsePruner,
43
+ estimateTokens: () => estimateTokens,
44
+ hashContent: () => hashContent
45
+ });
46
+ module.exports = __toCommonJS(src_exports);
47
+
48
+ // src/adapters/claude-code.ts
49
+ var import_node_crypto3 = require("crypto");
50
+
51
+ // src/core/btsp-embedder.ts
52
+ var import_node_crypto2 = require("crypto");
53
+
54
+ // src/utils/hash.ts
55
+ var import_node_crypto = require("crypto");
56
+ function hashContent(content) {
57
+ return (0, import_node_crypto.createHash)("sha256").update(content, "utf8").digest("hex");
58
+ }
59
+
60
+ // src/core/btsp-embedder.ts
61
+ function createBTSPEmbedder() {
62
+ const BTSP_PATTERNS = [
63
+ // Error patterns
64
+ /\b(error|exception|failure|fatal|critical|panic)\b/i,
65
+ /\b(TypeError|ReferenceError|SyntaxError|RangeError|URIError)\b/,
66
+ /\bENOENT|EACCES|ECONNREFUSED|ETIMEDOUT\b/,
67
+ // Stack trace patterns
68
+ /^\s+at\s+.*\(.*:\d+:\d+\)/m,
69
+ // JavaScript stack trace
70
+ /^\s+at\s+.*\.[a-zA-Z]+:\d+/m,
71
+ // Python/Ruby stack trace
72
+ // Git diff new files
73
+ /^new file mode \d+$/m,
74
+ /^--- \/dev\/null$/m,
75
+ // Merge conflict markers
76
+ /^<<<<<<< /m,
77
+ /^=======/m,
78
+ /^>>>>>>> /m
79
+ ];
80
+ function detectBTSP(content) {
81
+ return BTSP_PATTERNS.some((pattern) => pattern.test(content));
82
+ }
83
+ function createBTSPEntry(content, tags = [], metadata = {}) {
84
+ return {
85
+ id: (0, import_node_crypto2.randomUUID)(),
86
+ content,
87
+ hash: hashContent(content),
88
+ timestamp: Date.now(),
89
+ score: 1,
90
+ // Maximum initial score
91
+ ttl: 365 * 24 * 3600,
92
+ // 1 year in seconds (long retention)
93
+ state: "active",
94
+ // Always active
95
+ accessCount: 0,
96
+ tags: [...tags, "btsp"],
97
+ metadata,
98
+ isBTSP: true
99
+ };
100
+ }
101
+ return {
102
+ detectBTSP,
103
+ createBTSPEntry
104
+ };
105
+ }
106
+
107
+ // src/core/confidence-states.ts
108
+ function createConfidenceStates(config) {
109
+ const { activeThreshold, readyThreshold } = config;
110
+ function calculateState(entry) {
111
+ if (entry.isBTSP) {
112
+ return "active";
113
+ }
114
+ if (entry.score > activeThreshold) {
115
+ return "active";
116
+ }
117
+ if (entry.score >= readyThreshold) {
118
+ return "ready";
119
+ }
120
+ return "silent";
121
+ }
122
+ function transition(entry) {
123
+ const newState = calculateState(entry);
124
+ return {
125
+ ...entry,
126
+ state: newState
127
+ };
128
+ }
129
+ function getDistribution(entries) {
130
+ const distribution = {
131
+ silent: 0,
132
+ ready: 0,
133
+ active: 0,
134
+ total: entries.length
135
+ };
136
+ for (const entry of entries) {
137
+ const state = calculateState(entry);
138
+ distribution[state]++;
139
+ }
140
+ return distribution;
141
+ }
142
+ return {
143
+ calculateState,
144
+ transition,
145
+ getDistribution
146
+ };
147
+ }
148
+
149
+ // src/core/engram-scorer.ts
150
+ function createEngramScorer(config) {
151
+ const { defaultTTL } = config;
152
+ function calculateDecay(ageInSeconds, ttlInSeconds) {
153
+ if (ttlInSeconds === 0) return 1;
154
+ if (ageInSeconds <= 0) return 0;
155
+ const ratio = ageInSeconds / ttlInSeconds;
156
+ const decay = 1 - Math.exp(-ratio);
157
+ return Math.max(0, Math.min(1, decay));
158
+ }
159
+ function calculateScore(entry, currentTime = Date.now()) {
160
+ const ageInMilliseconds = currentTime - entry.timestamp;
161
+ const ageInSeconds = Math.max(0, ageInMilliseconds / 1e3);
162
+ const decay = calculateDecay(ageInSeconds, entry.ttl);
163
+ let score = entry.score * (1 - decay);
164
+ if (entry.accessCount > 0) {
165
+ const accessBonus = Math.log(entry.accessCount + 1) * 0.1;
166
+ score = Math.min(1, score + accessBonus);
167
+ }
168
+ if (entry.isBTSP) {
169
+ score = Math.max(score, 0.9);
170
+ }
171
+ return Math.max(0, Math.min(1, score));
172
+ }
173
+ function refreshTTL(entry) {
174
+ return {
175
+ ...entry,
176
+ ttl: defaultTTL * 3600,
177
+ // Convert hours to seconds
178
+ timestamp: Date.now()
179
+ };
180
+ }
181
+ return {
182
+ calculateScore,
183
+ refreshTTL,
184
+ calculateDecay
185
+ };
186
+ }
187
+
188
+ // src/utils/tokenizer.ts
189
+ function estimateTokens(text) {
190
+ if (!text || text.length === 0) {
191
+ return 0;
192
+ }
193
+ const words = text.split(/\s+/).filter((w) => w.length > 0);
194
+ const wordCount = words.length;
195
+ const charCount = text.length;
196
+ const charEstimate = Math.ceil(charCount / 4);
197
+ const wordEstimate = Math.ceil(wordCount * 0.75);
198
+ return Math.max(wordEstimate, charEstimate);
199
+ }
200
+
201
+ // src/core/sparse-pruner.ts
202
+ function createSparsePruner(config) {
203
+ const { threshold } = config;
204
+ function tokenize(text) {
205
+ return text.toLowerCase().split(/\s+/).filter((word) => word.length > 0);
206
+ }
207
+ function calculateTF(term, tokens) {
208
+ const count = tokens.filter((t) => t === term).length;
209
+ return Math.sqrt(count);
210
+ }
211
+ function calculateIDF(term, allEntries) {
212
+ const totalDocs = allEntries.length;
213
+ const docsWithTerm = allEntries.filter((entry) => {
214
+ const tokens = tokenize(entry.content);
215
+ return tokens.includes(term);
216
+ }).length;
217
+ if (docsWithTerm === 0) return 0;
218
+ return Math.log(totalDocs / docsWithTerm);
219
+ }
220
+ function scoreEntry(entry, allEntries) {
221
+ const tokens = tokenize(entry.content);
222
+ if (tokens.length === 0) return 0;
223
+ const uniqueTerms = [...new Set(tokens)];
224
+ let totalScore = 0;
225
+ for (const term of uniqueTerms) {
226
+ const tf = calculateTF(term, tokens);
227
+ const idf = calculateIDF(term, allEntries);
228
+ totalScore += tf * idf;
229
+ }
230
+ return totalScore / tokens.length;
231
+ }
232
+ function prune(entries) {
233
+ if (entries.length === 0) {
234
+ return {
235
+ kept: [],
236
+ removed: [],
237
+ originalTokens: 0,
238
+ prunedTokens: 0
239
+ };
240
+ }
241
+ const originalTokens = entries.reduce((sum, e) => sum + estimateTokens(e.content), 0);
242
+ const scored = entries.map((entry) => ({
243
+ entry,
244
+ score: scoreEntry(entry, entries)
245
+ }));
246
+ scored.sort((a, b) => b.score - a.score);
247
+ const keepCount = Math.max(1, Math.ceil(entries.length * (threshold / 100)));
248
+ const kept = scored.slice(0, keepCount).map((s) => s.entry);
249
+ const removed = scored.slice(keepCount).map((s) => s.entry);
250
+ const prunedTokens = kept.reduce((sum, e) => sum + estimateTokens(e.content), 0);
251
+ return {
252
+ kept,
253
+ removed,
254
+ originalTokens,
255
+ prunedTokens
256
+ };
257
+ }
258
+ return {
259
+ prune,
260
+ scoreEntry
261
+ };
262
+ }
263
+
264
+ // src/adapters/claude-code.ts
265
+ var CLAUDE_CODE_PROFILE = {
266
+ // More aggressive pruning for tool results (they can be verbose)
267
+ toolResultThreshold: 3,
268
+ // Keep top 3% of tool results
269
+ // Preserve conversation turns more aggressively
270
+ conversationBoost: 1.5,
271
+ // 50% boost for User/Assistant exchanges
272
+ // Prioritize recent context (Claude Code sessions are typically focused)
273
+ recentContextWindow: 10 * 60,
274
+ // Last 10 minutes gets priority
275
+ // BTSP patterns specific to Claude Code
276
+ btspPatterns: [
277
+ // Error patterns
278
+ /\b(error|exception|failure|fatal|critical|panic)\b/i,
279
+ /^\s+at\s+.*\(.*:\d+:\d+\)/m,
280
+ // Stack traces
281
+ /^Error:/m,
282
+ // Git conflict markers
283
+ /^<<<<<<< /m,
284
+ /^=======/m,
285
+ /^>>>>>>> /m,
286
+ // Tool use patterns (important for context)
287
+ /<function_calls>/,
288
+ /<invoke>/,
289
+ /<tool_use>/,
290
+ // File operation results (often critical)
291
+ /ENOENT|EACCES|EISDIR|EEXIST/
292
+ ]
293
+ };
294
+ function createClaudeCodeAdapter(memory, config) {
295
+ const pruner = createSparsePruner({
296
+ threshold: config.pruning.threshold
297
+ });
298
+ const scorer = createEngramScorer(config.decay);
299
+ const states = createConfidenceStates(config.states);
300
+ const btsp = createBTSPEmbedder();
301
+ async function optimize(context, options = {}) {
302
+ const startTime = Date.now();
303
+ const entries = parseClaudeCodeContext(context);
304
+ const entriesWithBTSP = entries.map((entry) => {
305
+ const isBTSP = CLAUDE_CODE_PROFILE.btspPatterns.some(
306
+ (pattern) => pattern.test(entry.content)
307
+ );
308
+ if (isBTSP) {
309
+ const btspEntry = btsp.createBTSPEntry(entry.content, [...entry.tags, "claude-code"], {
310
+ originalTimestamp: entry.timestamp
311
+ });
312
+ return {
313
+ ...btspEntry,
314
+ timestamp: entry.timestamp
315
+ };
316
+ }
317
+ return entry;
318
+ });
319
+ const boostedEntries = entriesWithBTSP.map((entry) => {
320
+ const isConversationTurn = entry.content.trim().startsWith("User:") || entry.content.trim().startsWith("Assistant:");
321
+ if (isConversationTurn) {
322
+ return {
323
+ ...entry,
324
+ score: entry.score * CLAUDE_CODE_PROFILE.conversationBoost
325
+ };
326
+ }
327
+ return entry;
328
+ });
329
+ const scoredEntries = boostedEntries.map((entry) => {
330
+ const decayScore = scorer.calculateScore(entry);
331
+ return {
332
+ ...entry,
333
+ score: decayScore
334
+ };
335
+ });
336
+ const entriesWithStates = scoredEntries.map((entry) => {
337
+ const state = states.calculateState(entry);
338
+ return {
339
+ ...entry,
340
+ state
341
+ };
342
+ });
343
+ const pruneResult = pruner.prune(entriesWithStates);
344
+ if (!options.dryRun) {
345
+ for (const entry of pruneResult.kept) {
346
+ await memory.put(entry);
347
+ }
348
+ await memory.recordOptimization({
349
+ timestamp: Date.now(),
350
+ tokens_before: pruneResult.originalTokens,
351
+ tokens_after: pruneResult.prunedTokens,
352
+ entries_pruned: pruneResult.removed.length,
353
+ duration_ms: Date.now() - startTime
354
+ });
355
+ }
356
+ const optimizedContext = pruneResult.kept.map((entry) => entry.content).join("\n");
357
+ const stateDistribution = states.getDistribution(pruneResult.kept);
358
+ const result = {
359
+ optimizedContext,
360
+ tokensBefore: pruneResult.originalTokens,
361
+ tokensAfter: pruneResult.prunedTokens,
362
+ reduction: pruneResult.originalTokens > 0 ? (pruneResult.originalTokens - pruneResult.prunedTokens) / pruneResult.originalTokens : 0,
363
+ entriesProcessed: entries.length,
364
+ entriesKept: pruneResult.kept.length,
365
+ durationMs: Date.now() - startTime,
366
+ stateDistribution
367
+ };
368
+ if (options.verbose) {
369
+ result.details = pruneResult.kept.map((entry) => ({
370
+ id: entry.id,
371
+ score: entry.score,
372
+ state: entry.state || "unknown",
373
+ isBTSP: entry.tags.includes("btsp"),
374
+ tokens: estimateTokens(entry.content)
375
+ }));
376
+ }
377
+ return result;
378
+ }
379
+ return {
380
+ optimize
381
+ };
382
+ }
383
+ function parseClaudeCodeContext(context) {
384
+ const entries = [];
385
+ const now = Date.now();
386
+ const lines = context.split("\n");
387
+ let currentBlock = [];
388
+ let blockType = "other";
389
+ for (const line of lines) {
390
+ const trimmed = line.trim();
391
+ if (trimmed.startsWith("User:") || trimmed.startsWith("Assistant:")) {
392
+ if (currentBlock.length > 0) {
393
+ entries.push(createEntry(currentBlock.join("\n"), blockType, now));
394
+ currentBlock = [];
395
+ }
396
+ blockType = "conversation";
397
+ currentBlock.push(line);
398
+ } else if (trimmed.includes("<function_calls>") || trimmed.includes("<invoke>") || trimmed.includes("<tool_use>")) {
399
+ if (currentBlock.length > 0) {
400
+ entries.push(createEntry(currentBlock.join("\n"), blockType, now));
401
+ currentBlock = [];
402
+ }
403
+ blockType = "tool";
404
+ currentBlock.push(line);
405
+ } else if (trimmed.includes("<function_results>") || trimmed.includes("</function_results>")) {
406
+ if (currentBlock.length > 0 && blockType !== "result") {
407
+ entries.push(createEntry(currentBlock.join("\n"), blockType, now));
408
+ currentBlock = [];
409
+ }
410
+ blockType = "result";
411
+ currentBlock.push(line);
412
+ } else if (currentBlock.length > 0) {
413
+ currentBlock.push(line);
414
+ } else if (trimmed.length > 0) {
415
+ currentBlock.push(line);
416
+ blockType = "other";
417
+ }
418
+ }
419
+ if (currentBlock.length > 0) {
420
+ entries.push(createEntry(currentBlock.join("\n"), blockType, now));
421
+ }
422
+ return entries.filter((e) => e.content.trim().length > 0);
423
+ }
424
+ function createEntry(content, type, baseTime) {
425
+ const tags = [type];
426
+ let initialScore = 0.5;
427
+ if (type === "conversation") initialScore = 0.8;
428
+ if (type === "tool") initialScore = 0.7;
429
+ if (type === "result") initialScore = 0.4;
430
+ return {
431
+ id: (0, import_node_crypto3.randomUUID)(),
432
+ content,
433
+ hash: hashContent(content),
434
+ timestamp: baseTime,
435
+ score: initialScore,
436
+ state: initialScore > 0.7 ? "active" : initialScore > 0.3 ? "ready" : "silent",
437
+ ttl: 24 * 3600,
438
+ // 24 hours default
439
+ accessCount: 0,
440
+ tags,
441
+ metadata: { type },
442
+ isBTSP: false
443
+ };
444
+ }
445
+
446
+ // src/adapters/generic.ts
447
+ var import_node_crypto4 = require("crypto");
448
+ function createGenericAdapter(memory, config) {
449
+ const pruner = createSparsePruner(config.pruning);
450
+ const scorer = createEngramScorer(config.decay);
451
+ const states = createConfidenceStates(config.states);
452
+ const btsp = createBTSPEmbedder();
453
+ async function optimize(context, options = {}) {
454
+ const startTime = Date.now();
455
+ const lines = context.split("\n").filter((line) => line.trim().length > 0);
456
+ const entries = lines.map((content) => ({
457
+ id: (0, import_node_crypto4.randomUUID)(),
458
+ content,
459
+ hash: hashContent(content),
460
+ timestamp: Date.now(),
461
+ score: btsp.detectBTSP(content) ? 1 : 0.5,
462
+ // BTSP gets high initial score
463
+ ttl: config.decay.defaultTTL * 3600,
464
+ // Convert hours to seconds
465
+ state: "ready",
466
+ accessCount: 0,
467
+ tags: [],
468
+ metadata: {},
469
+ isBTSP: btsp.detectBTSP(content)
470
+ }));
471
+ const tokensBefore = entries.reduce((sum, e) => sum + estimateTokens(e.content), 0);
472
+ const scoredEntries = entries.map((entry) => ({
473
+ ...entry,
474
+ score: scorer.calculateScore(entry)
475
+ }));
476
+ const statedEntries = scoredEntries.map((entry) => states.transition(entry));
477
+ const pruneResult = pruner.prune(statedEntries);
478
+ const optimizedEntries = pruneResult.kept.filter(
479
+ (e) => e.state === "active" || e.state === "ready"
480
+ );
481
+ const tokensAfter = optimizedEntries.reduce((sum, e) => sum + estimateTokens(e.content), 0);
482
+ const optimizedContext = optimizedEntries.map((e) => e.content).join("\n");
483
+ if (!options.dryRun) {
484
+ for (const entry of optimizedEntries) {
485
+ await memory.put(entry);
486
+ }
487
+ await memory.recordOptimization({
488
+ timestamp: Date.now(),
489
+ tokens_before: tokensBefore,
490
+ tokens_after: tokensAfter,
491
+ entries_pruned: entries.length - optimizedEntries.length,
492
+ duration_ms: Date.now() - startTime
493
+ });
494
+ }
495
+ const distribution = states.getDistribution(optimizedEntries);
496
+ const result = {
497
+ optimizedContext,
498
+ tokensBefore,
499
+ tokensAfter,
500
+ reduction: tokensBefore > 0 ? (tokensBefore - tokensAfter) / tokensBefore : 0,
501
+ entriesProcessed: entries.length,
502
+ entriesKept: optimizedEntries.length,
503
+ stateDistribution: distribution,
504
+ durationMs: Date.now() - startTime
505
+ };
506
+ if (options.verbose) {
507
+ result.details = optimizedEntries.map((e) => ({
508
+ id: e.id,
509
+ score: e.score,
510
+ state: e.state,
511
+ isBTSP: e.isBTSP,
512
+ tokens: estimateTokens(e.content)
513
+ }));
514
+ }
515
+ return result;
516
+ }
517
+ return {
518
+ optimize
519
+ };
520
+ }
521
+
522
+ // src/core/kv-memory.ts
523
+ var import_node_fs = require("fs");
524
+ var import_better_sqlite3 = __toESM(require("better-sqlite3"), 1);
525
+ function createBackup(dbPath) {
526
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
527
+ const backupPath = `${dbPath}.backup-${timestamp}`;
528
+ try {
529
+ (0, import_node_fs.copyFileSync)(dbPath, backupPath);
530
+ console.log(`\u2713 Database backed up to: ${backupPath}`);
531
+ return backupPath;
532
+ } catch (error) {
533
+ console.error(`Warning: Could not create backup: ${error}`);
534
+ return "";
535
+ }
536
+ }
537
+ async function createKVMemory(dbPath) {
538
+ let db;
539
+ try {
540
+ db = new import_better_sqlite3.default(dbPath);
541
+ const integrityCheck = db.pragma("quick_check", { simple: true });
542
+ if (integrityCheck !== "ok") {
543
+ console.error("\u26A0 Database corruption detected!");
544
+ if ((0, import_node_fs.existsSync)(dbPath)) {
545
+ const backupPath = createBackup(dbPath);
546
+ if (backupPath) {
547
+ console.log(`Backup created at: ${backupPath}`);
548
+ }
549
+ }
550
+ console.log("Attempting database recovery...");
551
+ db.close();
552
+ db = new import_better_sqlite3.default(dbPath);
553
+ }
554
+ } catch (error) {
555
+ console.error("\u26A0 Database error detected:", error);
556
+ if ((0, import_node_fs.existsSync)(dbPath)) {
557
+ createBackup(dbPath);
558
+ console.log("Creating new database...");
559
+ }
560
+ db = new import_better_sqlite3.default(dbPath);
561
+ }
562
+ db.pragma("journal_mode = WAL");
563
+ db.exec(`
564
+ CREATE TABLE IF NOT EXISTS entries_index (
565
+ id TEXT PRIMARY KEY NOT NULL,
566
+ hash TEXT UNIQUE NOT NULL,
567
+ timestamp INTEGER NOT NULL,
568
+ score REAL NOT NULL DEFAULT 0.0 CHECK(score >= 0.0 AND score <= 1.0),
569
+ ttl INTEGER NOT NULL CHECK(ttl >= 0),
570
+ state TEXT NOT NULL CHECK(state IN ('silent', 'ready', 'active')),
571
+ accessCount INTEGER NOT NULL DEFAULT 0 CHECK(accessCount >= 0),
572
+ isBTSP INTEGER NOT NULL DEFAULT 0 CHECK(isBTSP IN (0, 1)),
573
+ created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
574
+ );
575
+ `);
576
+ db.exec(`
577
+ CREATE TABLE IF NOT EXISTS entries_value (
578
+ id TEXT PRIMARY KEY NOT NULL,
579
+ content TEXT NOT NULL,
580
+ tags TEXT,
581
+ metadata TEXT,
582
+ FOREIGN KEY (id) REFERENCES entries_index(id) ON DELETE CASCADE
583
+ );
584
+ `);
585
+ db.exec(`
586
+ CREATE TABLE IF NOT EXISTS optimization_stats (
587
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
588
+ timestamp INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
589
+ tokens_before INTEGER NOT NULL,
590
+ tokens_after INTEGER NOT NULL,
591
+ entries_pruned INTEGER NOT NULL,
592
+ duration_ms INTEGER NOT NULL
593
+ );
594
+ `);
595
+ db.exec(`
596
+ CREATE INDEX IF NOT EXISTS idx_entries_state ON entries_index(state);
597
+ CREATE INDEX IF NOT EXISTS idx_entries_score ON entries_index(score DESC);
598
+ CREATE INDEX IF NOT EXISTS idx_entries_hash ON entries_index(hash);
599
+ CREATE INDEX IF NOT EXISTS idx_entries_timestamp ON entries_index(timestamp DESC);
600
+ CREATE INDEX IF NOT EXISTS idx_stats_timestamp ON optimization_stats(timestamp DESC);
601
+ `);
602
+ const putIndexStmt = db.prepare(`
603
+ INSERT OR REPLACE INTO entries_index
604
+ (id, hash, timestamp, score, ttl, state, accessCount, isBTSP)
605
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
606
+ `);
607
+ const putValueStmt = db.prepare(`
608
+ INSERT OR REPLACE INTO entries_value
609
+ (id, content, tags, metadata)
610
+ VALUES (?, ?, ?, ?)
611
+ `);
612
+ const getStmt = db.prepare(`
613
+ SELECT
614
+ i.id, i.hash, i.timestamp, i.score, i.ttl, i.state, i.accessCount, i.isBTSP,
615
+ v.content, v.tags, v.metadata
616
+ FROM entries_index i
617
+ JOIN entries_value v ON i.id = v.id
618
+ WHERE i.id = ?
619
+ `);
620
+ const deleteIndexStmt = db.prepare("DELETE FROM entries_index WHERE id = ?");
621
+ const deleteValueStmt = db.prepare("DELETE FROM entries_value WHERE id = ?");
622
+ return {
623
+ async put(entry) {
624
+ const transaction = db.transaction(() => {
625
+ putIndexStmt.run(
626
+ entry.id,
627
+ entry.hash,
628
+ entry.timestamp,
629
+ entry.score,
630
+ entry.ttl,
631
+ entry.state,
632
+ entry.accessCount,
633
+ entry.isBTSP ? 1 : 0
634
+ );
635
+ putValueStmt.run(
636
+ entry.id,
637
+ entry.content,
638
+ JSON.stringify(entry.tags),
639
+ JSON.stringify(entry.metadata)
640
+ );
641
+ });
642
+ transaction();
643
+ },
644
+ async get(id) {
645
+ const row = getStmt.get(id);
646
+ if (!row) {
647
+ return null;
648
+ }
649
+ const r = row;
650
+ return {
651
+ id: r.id,
652
+ content: r.content,
653
+ hash: r.hash,
654
+ timestamp: r.timestamp,
655
+ score: r.score,
656
+ ttl: r.ttl,
657
+ state: r.state,
658
+ accessCount: r.accessCount,
659
+ tags: r.tags ? JSON.parse(r.tags) : [],
660
+ metadata: r.metadata ? JSON.parse(r.metadata) : {},
661
+ isBTSP: r.isBTSP === 1
662
+ };
663
+ },
664
+ async query(filters) {
665
+ let sql = `
666
+ SELECT
667
+ i.id, i.hash, i.timestamp, i.score, i.ttl, i.state, i.accessCount, i.isBTSP,
668
+ v.content, v.tags, v.metadata
669
+ FROM entries_index i
670
+ JOIN entries_value v ON i.id = v.id
671
+ WHERE 1=1
672
+ `;
673
+ const params = [];
674
+ if (filters.state) {
675
+ sql += " AND i.state = ?";
676
+ params.push(filters.state);
677
+ }
678
+ if (filters.minScore !== void 0) {
679
+ sql += " AND i.score >= ?";
680
+ params.push(filters.minScore);
681
+ }
682
+ if (filters.maxScore !== void 0) {
683
+ sql += " AND i.score <= ?";
684
+ params.push(filters.maxScore);
685
+ }
686
+ if (filters.isBTSP !== void 0) {
687
+ sql += " AND i.isBTSP = ?";
688
+ params.push(filters.isBTSP ? 1 : 0);
689
+ }
690
+ sql += " ORDER BY i.score DESC";
691
+ if (filters.limit) {
692
+ sql += " LIMIT ?";
693
+ params.push(filters.limit);
694
+ }
695
+ if (filters.offset) {
696
+ sql += " OFFSET ?";
697
+ params.push(filters.offset);
698
+ }
699
+ const stmt = db.prepare(sql);
700
+ const rows = stmt.all(...params);
701
+ return rows.map((row) => {
702
+ const r = row;
703
+ return {
704
+ id: r.id,
705
+ content: r.content,
706
+ hash: r.hash,
707
+ timestamp: r.timestamp,
708
+ score: r.score,
709
+ ttl: r.ttl,
710
+ state: r.state,
711
+ accessCount: r.accessCount,
712
+ tags: r.tags ? JSON.parse(r.tags) : [],
713
+ metadata: r.metadata ? JSON.parse(r.metadata) : {},
714
+ isBTSP: r.isBTSP === 1
715
+ };
716
+ });
717
+ },
718
+ async delete(id) {
719
+ const transaction = db.transaction(() => {
720
+ deleteIndexStmt.run(id);
721
+ deleteValueStmt.run(id);
722
+ });
723
+ transaction();
724
+ },
725
+ async list() {
726
+ const stmt = db.prepare("SELECT id FROM entries_index");
727
+ const rows = stmt.all();
728
+ return rows.map((r) => r.id);
729
+ },
730
+ async compact() {
731
+ const before = db.prepare("SELECT COUNT(*) as count FROM entries_index").get();
732
+ db.exec("DELETE FROM entries_index WHERE ttl <= 0");
733
+ db.exec("VACUUM");
734
+ const after = db.prepare("SELECT COUNT(*) as count FROM entries_index").get();
735
+ return before.count - after.count;
736
+ },
737
+ async close() {
738
+ db.close();
739
+ },
740
+ async recordOptimization(stats) {
741
+ const stmt = db.prepare(`
742
+ INSERT INTO optimization_stats (timestamp, tokens_before, tokens_after, entries_pruned, duration_ms)
743
+ VALUES (?, ?, ?, ?, ?)
744
+ `);
745
+ stmt.run(
746
+ stats.timestamp,
747
+ stats.tokens_before,
748
+ stats.tokens_after,
749
+ stats.entries_pruned,
750
+ stats.duration_ms
751
+ );
752
+ },
753
+ async getOptimizationStats() {
754
+ const stmt = db.prepare(`
755
+ SELECT id, timestamp, tokens_before, tokens_after, entries_pruned, duration_ms
756
+ FROM optimization_stats
757
+ ORDER BY timestamp DESC
758
+ `);
759
+ const rows = stmt.all();
760
+ return rows;
761
+ },
762
+ async clearOptimizationStats() {
763
+ db.exec("DELETE FROM optimization_stats");
764
+ }
765
+ };
766
+ }
767
+
768
+ // src/core/sleep-compressor.ts
769
+ function createSleepCompressor() {
770
+ const scorer = createEngramScorer({ defaultTTL: 24, decayThreshold: 0.95 });
771
+ function consolidate(entries) {
772
+ const startTime = Date.now();
773
+ const originalCount = entries.length;
774
+ const now = Date.now();
775
+ const nonDecayed = entries.filter((entry) => {
776
+ const ageInSeconds = (now - entry.timestamp) / 1e3;
777
+ const decay = scorer.calculateDecay(ageInSeconds, entry.ttl);
778
+ return decay < 0.95;
779
+ });
780
+ const decayedRemoved = originalCount - nonDecayed.length;
781
+ const duplicateGroups = findDuplicates(nonDecayed);
782
+ const merged = mergeDuplicates(duplicateGroups);
783
+ const duplicateIds = new Set(duplicateGroups.flatMap((g) => g.entries.map((e) => e.id)));
784
+ const nonDuplicates = nonDecayed.filter((e) => !duplicateIds.has(e.id));
785
+ const kept = [...merged, ...nonDuplicates];
786
+ const removed = entries.filter((e) => !kept.some((k) => k.id === e.id));
787
+ const duplicatesRemoved = duplicateGroups.reduce((sum, g) => sum + (g.entries.length - 1), 0);
788
+ return {
789
+ kept,
790
+ removed,
791
+ entriesBefore: originalCount,
792
+ entriesAfter: kept.length,
793
+ decayedRemoved,
794
+ duplicatesRemoved,
795
+ compressionRatio: originalCount > 0 ? kept.length / originalCount : 0,
796
+ durationMs: Date.now() - startTime
797
+ };
798
+ }
799
+ function findDuplicates(entries) {
800
+ const groups = [];
801
+ const processed = /* @__PURE__ */ new Set();
802
+ for (let i = 0; i < entries.length; i++) {
803
+ const entry = entries[i];
804
+ if (!entry || processed.has(entry.id)) continue;
805
+ const duplicates = entries.filter((e, idx) => idx !== i && e.hash === entry.hash);
806
+ if (duplicates.length > 0) {
807
+ const group = {
808
+ entries: [entry, ...duplicates],
809
+ similarity: 1
810
+ // Exact match
811
+ };
812
+ groups.push(group);
813
+ processed.add(entry.id);
814
+ for (const dup of duplicates) {
815
+ processed.add(dup.id);
816
+ }
817
+ }
818
+ }
819
+ for (let i = 0; i < entries.length; i++) {
820
+ const entryI = entries[i];
821
+ if (!entryI || processed.has(entryI.id)) continue;
822
+ for (let j = i + 1; j < entries.length; j++) {
823
+ const entryJ = entries[j];
824
+ if (!entryJ || processed.has(entryJ.id)) continue;
825
+ const similarity = cosineSimilarity(entryI.content, entryJ.content);
826
+ if (similarity >= 0.85) {
827
+ const group = {
828
+ entries: [entryI, entryJ],
829
+ similarity
830
+ };
831
+ groups.push(group);
832
+ processed.add(entryI.id);
833
+ processed.add(entryJ.id);
834
+ break;
835
+ }
836
+ }
837
+ }
838
+ return groups;
839
+ }
840
+ function mergeDuplicates(groups) {
841
+ const merged = [];
842
+ for (const group of groups) {
843
+ const sorted = [...group.entries].sort((a, b) => b.score - a.score);
844
+ const best = sorted[0];
845
+ if (!best) continue;
846
+ const totalAccessCount = group.entries.reduce((sum, e) => sum + e.accessCount, 0);
847
+ const allTags = new Set(group.entries.flatMap((e) => e.tags));
848
+ merged.push({
849
+ ...best,
850
+ accessCount: totalAccessCount,
851
+ tags: Array.from(allTags)
852
+ });
853
+ }
854
+ return merged;
855
+ }
856
+ function cosineSimilarity(text1, text2) {
857
+ const words1 = tokenize(text1);
858
+ const words2 = tokenize(text2);
859
+ const vocab = /* @__PURE__ */ new Set([...words1, ...words2]);
860
+ const vec1 = {};
861
+ const vec2 = {};
862
+ for (const word of vocab) {
863
+ vec1[word] = words1.filter((w) => w === word).length;
864
+ vec2[word] = words2.filter((w) => w === word).length;
865
+ }
866
+ let dotProduct = 0;
867
+ let mag1 = 0;
868
+ let mag2 = 0;
869
+ for (const word of vocab) {
870
+ const count1 = vec1[word] ?? 0;
871
+ const count2 = vec2[word] ?? 0;
872
+ dotProduct += count1 * count2;
873
+ mag1 += count1 * count1;
874
+ mag2 += count2 * count2;
875
+ }
876
+ mag1 = Math.sqrt(mag1);
877
+ mag2 = Math.sqrt(mag2);
878
+ if (mag1 === 0 || mag2 === 0) return 0;
879
+ return dotProduct / (mag1 * mag2);
880
+ }
881
+ function tokenize(text) {
882
+ return text.toLowerCase().split(/\s+/).filter((word) => word.length > 0);
883
+ }
884
+ return {
885
+ consolidate,
886
+ findDuplicates,
887
+ mergeDuplicates
888
+ };
889
+ }
890
+
891
+ // src/types/config.ts
892
+ var DEFAULT_CONFIG = {
893
+ pruning: {
894
+ threshold: 5,
895
+ aggressiveness: 50
896
+ },
897
+ decay: {
898
+ defaultTTL: 24,
899
+ decayThreshold: 0.95
900
+ },
901
+ states: {
902
+ activeThreshold: 0.7,
903
+ readyThreshold: 0.3
904
+ },
905
+ agent: "generic",
906
+ ui: {
907
+ colors: true,
908
+ sounds: false,
909
+ verbose: false
910
+ },
911
+ autoConsolidate: null
912
+ };
913
+
914
+ // src/utils/logger.ts
915
+ function createLogger(verbose = false) {
916
+ return {
917
+ debug(message, ...args) {
918
+ if (verbose) {
919
+ console.debug(`[DEBUG] ${message}`, ...args);
920
+ }
921
+ },
922
+ info(message, ...args) {
923
+ console.info(`[INFO] ${message}`, ...args);
924
+ },
925
+ warn(message, ...args) {
926
+ console.warn(`[WARN] ${message}`, ...args);
927
+ },
928
+ error(message, ...args) {
929
+ console.error(`[ERROR] ${message}`, ...args);
930
+ }
931
+ };
932
+ }
933
+ // Annotate the CommonJS export names for ESM import in node:
934
+ 0 && (module.exports = {
935
+ DEFAULT_CONFIG,
936
+ createBTSPEmbedder,
937
+ createClaudeCodeAdapter,
938
+ createConfidenceStates,
939
+ createEngramScorer,
940
+ createGenericAdapter,
941
+ createKVMemory,
942
+ createLogger,
943
+ createSleepCompressor,
944
+ createSparsePruner,
945
+ estimateTokens,
946
+ hashContent
947
+ });
948
+ //# sourceMappingURL=index.cjs.map