claude-launchpad 0.8.1 → 0.8.4-dev.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/{chunk-NNVJCKEP.js → chunk-JPVLFY2R.js} +36 -1
- package/dist/chunk-JPVLFY2R.js.map +1 -0
- package/dist/chunk-MQJA7TGY.js +60 -0
- package/dist/chunk-MQJA7TGY.js.map +1 -0
- package/dist/{chunk-EUAVDA7W.js → chunk-X7ZY2Y2Z.js} +2 -2
- package/dist/cli.js +8 -7
- package/dist/cli.js.map +1 -1
- package/dist/commands/memory/server.js +252 -3
- package/dist/commands/memory/server.js.map +1 -1
- package/dist/{context-QU2QFVS3.js → context-HX5BOXYM.js} +158 -70
- package/dist/context-HX5BOXYM.js.map +1 -0
- package/dist/{extract-ADZYHMUP.js → extract-7D2EEXYD.js} +3 -3
- package/dist/{install-3IW2PDOS.js → install-ULZUZI7T.js} +2 -2
- package/dist/{stats-XLVVS3JA.js → stats-Y5ZFAZVF.js} +3 -3
- package/dist/{tui-BXRHLYAS.js → tui-DTIXPD2V.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-NNVJCKEP.js.map +0 -1
- package/dist/chunk-OYSKBXBB.js +0 -311
- package/dist/chunk-OYSKBXBB.js.map +0 -1
- package/dist/context-QU2QFVS3.js.map +0 -1
- /package/dist/{chunk-EUAVDA7W.js.map → chunk-X7ZY2Y2Z.js.map} +0 -0
- /package/dist/{extract-ADZYHMUP.js.map → extract-7D2EEXYD.js.map} +0 -0
- /package/dist/{install-3IW2PDOS.js.map → install-ULZUZI7T.js.map} +0 -0
- /package/dist/{stats-XLVVS3JA.js.map → stats-Y5ZFAZVF.js.map} +0 -0
- /package/dist/{tui-BXRHLYAS.js.map → tui-DTIXPD2V.js.map} +0 -0
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
computeContextScore,
|
|
4
|
+
getGitContext
|
|
5
|
+
} from "./chunk-MQJA7TGY.js";
|
|
5
6
|
import {
|
|
6
7
|
detectProject
|
|
7
8
|
} from "./chunk-NAW47BYA.js";
|
|
8
9
|
import {
|
|
9
10
|
initStorage
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-X7ZY2Y2Z.js";
|
|
11
12
|
import "./chunk-TALTTAMW.js";
|
|
12
13
|
import {
|
|
13
|
-
DEFAULT_DECAY_PARAMS
|
|
14
|
-
|
|
14
|
+
DEFAULT_DECAY_PARAMS,
|
|
15
|
+
INJECTION_COLD_START_THRESHOLD,
|
|
16
|
+
INJECTION_HEADER_TOKENS,
|
|
17
|
+
INJECTION_MIN_SCORE,
|
|
18
|
+
INJECTION_WEIGHTS,
|
|
19
|
+
RECENCY_HALF_LIFE,
|
|
20
|
+
TYPE_INJECTION_BONUS,
|
|
21
|
+
estimateTokens
|
|
22
|
+
} from "./chunk-JPVLFY2R.js";
|
|
15
23
|
import "./chunk-UN2XVQ5K.js";
|
|
16
24
|
import "./chunk-FL3JGYDM.js";
|
|
17
25
|
|
|
@@ -199,8 +207,142 @@ function normalizeText(text) {
|
|
|
199
207
|
return text.toLowerCase().replace(/\s+/g, " ").trim();
|
|
200
208
|
}
|
|
201
209
|
|
|
210
|
+
// src/commands/memory/services/injection-service.ts
|
|
211
|
+
var InjectionService = class {
|
|
212
|
+
#deps;
|
|
213
|
+
constructor(deps) {
|
|
214
|
+
this.#deps = deps;
|
|
215
|
+
}
|
|
216
|
+
selectForInjection(tokenBudget, project) {
|
|
217
|
+
const allMemories = this.#deps.memoryRepo.getAll(project);
|
|
218
|
+
const totalCount = allMemories.length;
|
|
219
|
+
const candidates = allMemories.filter(
|
|
220
|
+
(m) => m.type !== "working" && m.importance >= 0.05
|
|
221
|
+
);
|
|
222
|
+
if (candidates.length === 0) {
|
|
223
|
+
return { memories: [], tokensUsed: 0, tokenBudget, totalCount };
|
|
224
|
+
}
|
|
225
|
+
const minScore = candidates.length <= INJECTION_COLD_START_THRESHOLD ? 0.1 : INJECTION_MIN_SCORE;
|
|
226
|
+
const scored = candidates.map((m) => ({ memory: m, score: this.#scoreMemory(m) })).filter((s) => s.score >= minScore).sort((a, b) => b.score - a.score);
|
|
227
|
+
return this.#packBudget(scored, tokenBudget, totalCount);
|
|
228
|
+
}
|
|
229
|
+
formatInjection(result) {
|
|
230
|
+
const { memories, tokensUsed, tokenBudget, totalCount } = result;
|
|
231
|
+
if (memories.length === 0) {
|
|
232
|
+
return "No memories stored for this project. Use memory_store to save knowledge across sessions.";
|
|
233
|
+
}
|
|
234
|
+
const lines = [];
|
|
235
|
+
lines.push(`# Agentic Memory (${memories.length} of ${totalCount} memories, ${tokensUsed}/${tokenBudget} tokens)`);
|
|
236
|
+
lines.push("Use memory_search to retrieve full content for any memory listed here.\n");
|
|
237
|
+
const full = memories.filter((m) => m.tier === "full");
|
|
238
|
+
const summary = memories.filter((m) => m.tier === "summary");
|
|
239
|
+
const index = memories.filter((m) => m.tier === "index");
|
|
240
|
+
if (full.length > 0) {
|
|
241
|
+
lines.push("## Key Memories");
|
|
242
|
+
for (const { memory: m } of full) {
|
|
243
|
+
lines.push(`### ${m.title ?? "(untitled)"} [${m.type}]`);
|
|
244
|
+
lines.push(m.content.slice(0, 500));
|
|
245
|
+
if (m.tags.length > 0) lines.push(`Tags: ${m.tags.join(", ")}`);
|
|
246
|
+
lines.push("");
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (summary.length > 0) {
|
|
250
|
+
lines.push("## Related Memories");
|
|
251
|
+
for (const { memory: m } of summary) {
|
|
252
|
+
const snippet = m.content.slice(0, 150);
|
|
253
|
+
const ellipsis = m.content.length > 150 ? "..." : "";
|
|
254
|
+
lines.push(`- **${m.title ?? "(untitled)"}** [${m.type}]: ${snippet}${ellipsis}`);
|
|
255
|
+
}
|
|
256
|
+
lines.push("");
|
|
257
|
+
}
|
|
258
|
+
if (index.length > 0) {
|
|
259
|
+
lines.push("## Also Available (use memory_search)");
|
|
260
|
+
for (const { memory: m } of index) {
|
|
261
|
+
lines.push(`- ${m.id.slice(0, 8)} ${m.title ?? "(untitled)"} [${m.type}]`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return lines.join("\n");
|
|
265
|
+
}
|
|
266
|
+
// ── Scoring ────────────────────────────────────────────────
|
|
267
|
+
#scoreMemory(memory) {
|
|
268
|
+
const ctx = this.#contextRelevance(memory);
|
|
269
|
+
const val = this.#valueSignal(memory);
|
|
270
|
+
const imp = memory.importance;
|
|
271
|
+
const rec = this.#recencyScore(memory);
|
|
272
|
+
const typ = TYPE_INJECTION_BONUS[memory.type] ?? 0.5;
|
|
273
|
+
const noise = this.#noisePenalty(memory);
|
|
274
|
+
return ctx * INJECTION_WEIGHTS.context + val * INJECTION_WEIGHTS.value + imp * INJECTION_WEIGHTS.importance + rec * INJECTION_WEIGHTS.recency + typ * INJECTION_WEIGHTS.typeBonus + noise * INJECTION_WEIGHTS.noise;
|
|
275
|
+
}
|
|
276
|
+
#contextRelevance(memory) {
|
|
277
|
+
if (!this.#deps.gitContext) return 0;
|
|
278
|
+
return computeContextScore(memory.context, this.#deps.gitContext, "");
|
|
279
|
+
}
|
|
280
|
+
#valueSignal(memory) {
|
|
281
|
+
const { accessCount, injectionCount } = memory;
|
|
282
|
+
if (accessCount === 0 && injectionCount === 0) return 0.5;
|
|
283
|
+
if (injectionCount > 0 && accessCount === 0) {
|
|
284
|
+
return Math.max(0, 0.5 - Math.min(1, injectionCount / 10) * 0.5);
|
|
285
|
+
}
|
|
286
|
+
const ratio = Math.min(1, accessCount / Math.max(1, injectionCount));
|
|
287
|
+
return 0.4 + ratio * 0.6;
|
|
288
|
+
}
|
|
289
|
+
#recencyScore(memory) {
|
|
290
|
+
const halfLife = RECENCY_HALF_LIFE[memory.type] ?? 30;
|
|
291
|
+
const ageDays = (Date.now() - new Date(memory.updatedAt).getTime()) / 864e5;
|
|
292
|
+
return Math.exp(-ageDays * Math.LN2 / halfLife);
|
|
293
|
+
}
|
|
294
|
+
#noisePenalty(memory) {
|
|
295
|
+
if (memory.injectionCount <= 3) return 1;
|
|
296
|
+
if (memory.accessCount > 0) return 1;
|
|
297
|
+
return Math.max(0.2, 1 - Math.log2(memory.injectionCount - 2) * 0.15);
|
|
298
|
+
}
|
|
299
|
+
// ── Token Budget Packing ───────────────────────────────────
|
|
300
|
+
#packBudget(scored, tokenBudget, totalCount) {
|
|
301
|
+
const available = tokenBudget - INJECTION_HEADER_TOKENS;
|
|
302
|
+
const selected = [];
|
|
303
|
+
let tokensUsed = INJECTION_HEADER_TOKENS;
|
|
304
|
+
for (const { memory, score } of scored) {
|
|
305
|
+
if (tokensUsed >= tokenBudget) break;
|
|
306
|
+
const tier = this.#assignTier(score, selected.length, available - (tokensUsed - INJECTION_HEADER_TOKENS));
|
|
307
|
+
const cost = this.#estimateTierTokens(memory, tier);
|
|
308
|
+
if (tokensUsed + cost <= tokenBudget) {
|
|
309
|
+
selected.push({ memory, score, tier, tokenCost: cost });
|
|
310
|
+
tokensUsed += cost;
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
const demoted = tier === "full" ? "summary" : tier === "summary" ? "index" : null;
|
|
314
|
+
if (demoted) {
|
|
315
|
+
const demotedCost = this.#estimateTierTokens(memory, demoted);
|
|
316
|
+
if (tokensUsed + demotedCost <= tokenBudget) {
|
|
317
|
+
selected.push({ memory, score, tier: demoted, tokenCost: demotedCost });
|
|
318
|
+
tokensUsed += demotedCost;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
for (const entry of selected) {
|
|
323
|
+
this.#deps.memoryRepo.incrementInjection(entry.memory.id);
|
|
324
|
+
}
|
|
325
|
+
return { memories: selected, tokensUsed, tokenBudget, totalCount };
|
|
326
|
+
}
|
|
327
|
+
#assignTier(score, position, remainingBudget) {
|
|
328
|
+
if (position < 3 && score >= 0.6 && remainingBudget > 200) return "full";
|
|
329
|
+
if (position < 8 && score >= 0.35 && remainingBudget > 80) return "summary";
|
|
330
|
+
return "index";
|
|
331
|
+
}
|
|
332
|
+
#estimateTierTokens(memory, tier) {
|
|
333
|
+
const meta = `[${memory.type}] ${memory.title ?? ""} (${memory.tags.join(", ")})`;
|
|
334
|
+
switch (tier) {
|
|
335
|
+
case "full":
|
|
336
|
+
return estimateTokens(memory.content.slice(0, 500)) + estimateTokens(meta) + 10;
|
|
337
|
+
case "summary":
|
|
338
|
+
return estimateTokens(memory.content.slice(0, 150)) + estimateTokens(meta) + 8;
|
|
339
|
+
case "index":
|
|
340
|
+
return estimateTokens(`${memory.id.slice(0, 8)} [${memory.type}] ${memory.title ?? "(untitled)"}`) + 4;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
202
345
|
// src/commands/memory/subcommands/context.ts
|
|
203
|
-
var FULL_INJECT_THRESHOLD = 10;
|
|
204
346
|
var CONSOLIDATION_FILE = ".last-consolidation";
|
|
205
347
|
function shouldConsolidate(dataDir, intervalDays) {
|
|
206
348
|
const checkpointPath = join(dataDir, CONSOLIDATION_FILE);
|
|
@@ -241,77 +383,23 @@ async function runContext(opts) {
|
|
|
241
383
|
process.stderr.write(`[agentic-memory] maintenance error: ${err instanceof Error ? err.message : err}
|
|
242
384
|
`);
|
|
243
385
|
}
|
|
244
|
-
const
|
|
386
|
+
const project = detectProject(process.cwd());
|
|
387
|
+
const gitContext = getGitContext();
|
|
388
|
+
const injectionService = new InjectionService({
|
|
245
389
|
memoryRepo: ctx.memoryRepo,
|
|
246
390
|
relationRepo: ctx.relationRepo,
|
|
247
|
-
|
|
391
|
+
gitContext: gitContext ?? void 0
|
|
248
392
|
});
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
type: opts.type
|
|
255
|
-
});
|
|
256
|
-
if (results.length === 0) {
|
|
257
|
-
write("No memories found for this project.");
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
const useGraph = totalCount > FULL_INJECT_THRESHOLD;
|
|
261
|
-
if (opts.json) {
|
|
262
|
-
write("# Agentic Memory - Session Context");
|
|
263
|
-
if (useGraph) {
|
|
264
|
-
write(`${totalCount} memories stored. Showing index only - use memory_search to get full content.
|
|
265
|
-
`);
|
|
266
|
-
const graph = results.map((r) => formatGraphEntry(r));
|
|
267
|
-
write(JSON.stringify({ mode: "graph", totalMemories: totalCount, memories: graph }, null, 2));
|
|
268
|
-
} else {
|
|
269
|
-
write("The following memories were loaded from previous sessions. Treat these as known facts.");
|
|
270
|
-
write("When the user asks about something covered here, answer from these memories directly.\n");
|
|
271
|
-
const sections = {
|
|
272
|
-
contextMatched: results.filter((r) => r.section === "context").map(formatFullEntry),
|
|
273
|
-
recent: results.filter((r) => r.section === "recent").map(formatFullEntry),
|
|
274
|
-
related: results.filter((r) => r.section === "related").map(formatFullEntry)
|
|
275
|
-
};
|
|
276
|
-
write(JSON.stringify({ mode: "full", ...sections }, null, 2));
|
|
277
|
-
}
|
|
278
|
-
} else {
|
|
279
|
-
write(`agentic-memory - Session context (${results.length}/${totalCount} memories)
|
|
280
|
-
`);
|
|
281
|
-
for (const r of results) {
|
|
282
|
-
const m = r.result.memory;
|
|
283
|
-
write(` ${m.title ?? "(untitled)"} [${m.type}] - ${m.content.slice(0, 100)}${m.content.length > 100 ? "..." : ""}`);
|
|
284
|
-
}
|
|
285
|
-
}
|
|
393
|
+
const result = injectionService.selectForInjection(
|
|
394
|
+
ctx.config.injectionBudget,
|
|
395
|
+
project ?? void 0
|
|
396
|
+
);
|
|
397
|
+
write(injectionService.formatInjection(result));
|
|
286
398
|
} finally {
|
|
287
399
|
ctx.close();
|
|
288
400
|
}
|
|
289
401
|
}
|
|
290
|
-
function formatFullEntry(entry) {
|
|
291
|
-
const m = entry.result.memory;
|
|
292
|
-
return {
|
|
293
|
-
id: m.id,
|
|
294
|
-
type: m.type,
|
|
295
|
-
title: m.title,
|
|
296
|
-
content: m.content.slice(0, 500),
|
|
297
|
-
importance: m.importance,
|
|
298
|
-
tags: m.tags,
|
|
299
|
-
score: Math.round(entry.result.score * 100) / 100,
|
|
300
|
-
createdAt: m.createdAt
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
function formatGraphEntry(entry) {
|
|
304
|
-
const m = entry.result.memory;
|
|
305
|
-
return {
|
|
306
|
-
id: m.id,
|
|
307
|
-
type: m.type,
|
|
308
|
-
title: m.title,
|
|
309
|
-
importance: m.importance,
|
|
310
|
-
tags: m.tags,
|
|
311
|
-
section: entry.section
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
402
|
export {
|
|
315
403
|
runContext
|
|
316
404
|
};
|
|
317
|
-
//# sourceMappingURL=context-
|
|
405
|
+
//# sourceMappingURL=context-HX5BOXYM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/memory/subcommands/context.ts","../src/commands/memory/services/decay-service.ts","../src/commands/memory/services/consolidation-service.ts","../src/commands/memory/services/injection-service.ts"],"sourcesContent":["import { readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { DecayService } from '../services/decay-service.js';\nimport { ConsolidationService } from '../services/consolidation-service.js';\nimport { InjectionService } from '../services/injection-service.js';\nimport { detectProject } from '../utils/project.js';\nimport { getGitContext } from '../utils/git-context.js';\nimport { initStorage } from './init-storage.js';\n\ninterface ContextOpts {\n readonly json?: boolean;\n readonly dbPath?: string;\n}\n\nconst CONSOLIDATION_FILE = '.last-consolidation';\n\nfunction shouldConsolidate(dataDir: string, intervalDays: number): boolean {\n const checkpointPath = join(dataDir, CONSOLIDATION_FILE);\n try {\n const raw = readFileSync(checkpointPath, 'utf-8').trim();\n const lastRun = parseInt(raw, 10);\n if (isNaN(lastRun)) return true;\n const daysSince = (Date.now() - lastRun) / 86_400_000;\n return daysSince >= intervalDays;\n } catch {\n return true;\n }\n}\n\nfunction markConsolidated(dataDir: string): void {\n writeFileSync(join(dataDir, CONSOLIDATION_FILE), String(Date.now()), 'utf-8');\n}\n\nfunction write(msg: string): void {\n process.stdout.write(msg + '\\n');\n}\n\nexport async function runContext(opts: ContextOpts): Promise<void> {\n const ctx = initStorage(opts.dbPath);\n\n try {\n // Run maintenance (decay + consolidation) before loading context\n try {\n const decayService = new DecayService({\n memoryRepo: ctx.memoryRepo,\n relationRepo: ctx.relationRepo,\n });\n decayService.run();\n\n if (shouldConsolidate(ctx.dataDir, ctx.config.consolidationInterval)) {\n const consolidationService = new ConsolidationService({\n memoryRepo: ctx.memoryRepo,\n relationRepo: ctx.relationRepo,\n });\n await consolidationService.consolidate();\n markConsolidated(ctx.dataDir);\n }\n } catch (err) {\n process.stderr.write(`[agentic-memory] maintenance error: ${err instanceof Error ? err.message : err}\\n`);\n }\n\n const project = detectProject(process.cwd());\n const gitContext = getGitContext();\n\n const injectionService = new InjectionService({\n memoryRepo: ctx.memoryRepo,\n relationRepo: ctx.relationRepo,\n gitContext: gitContext ?? undefined,\n });\n\n const result = injectionService.selectForInjection(\n ctx.config.injectionBudget,\n project ?? undefined,\n );\n\n write(injectionService.formatInjection(result));\n } finally {\n ctx.close();\n }\n}\n","import type { Memory, DecayParams } from '../types.js';\nimport type { MemoryRepo } from '../storage/memory-repo.js';\nimport type { RelationRepo } from '../storage/relation-repo.js';\nimport { DEFAULT_DECAY_PARAMS } from '../config.js';\n\n// ── Types ────────────────────────────────────────────────────\n\nexport interface DecayServiceDeps {\n readonly memoryRepo: MemoryRepo;\n readonly relationRepo: RelationRepo;\n readonly params?: DecayParams;\n}\n\nexport interface DecayReport {\n readonly decayed: number;\n readonly pruned: number;\n readonly workingCleared: number;\n}\n\n// ── Decay Service ────────────────────────────────────────────\n\nexport class DecayService {\n readonly #memoryRepo: MemoryRepo;\n readonly #relationRepo: RelationRepo;\n readonly #params: DecayParams;\n\n constructor(deps: DecayServiceDeps) {\n this.#memoryRepo = deps.memoryRepo;\n this.#relationRepo = deps.relationRepo;\n this.#params = deps.params ?? DEFAULT_DECAY_PARAMS;\n }\n\n /**\n * Run full decay cycle: clear working memories, apply decay, prune dead memories.\n */\n run(): DecayReport {\n const workingCleared = this.clearWorkingMemories();\n const decayed = this.decayAll();\n const pruned = this.prune();\n return { decayed, pruned, workingCleared };\n }\n\n clearWorkingMemories(): number {\n return this.#memoryRepo.deleteByType('working');\n }\n\n /**\n * Apply decay formula to all non-working memories.\n */\n decayAll(): number {\n const memories = this.#memoryRepo.getAll();\n let updated = 0;\n\n for (const memory of memories) {\n if (memory.type === 'working') continue;\n\n const newImportance = this.computeDecayedImportance(memory);\n if (Math.abs(newImportance - memory.importance) > 0.001) {\n this.#memoryRepo.updateImportanceOnly(memory.id, newImportance);\n updated++;\n }\n }\n\n return updated;\n }\n\n /**\n * Hard-delete memories below prune threshold.\n */\n prune(): number {\n const memories = this.#memoryRepo.getAll();\n const now = Date.now();\n let pruned = 0;\n\n for (const memory of memories) {\n if (memory.type === 'working') continue;\n\n const ageDays = (now - new Date(memory.createdAt).getTime()) / (1000 * 60 * 60 * 24);\n if (\n ageDays > this.#params.pruneMinAgeDays &&\n memory.importance < this.#params.pruneThreshold &&\n memory.accessCount === 0\n ) {\n this.#memoryRepo.hardDelete(memory.id);\n pruned++;\n }\n }\n\n return pruned;\n }\n\n /**\n * Compute decayed importance as a pure function of age from creation.\n * Uses createdAt (immutable) to avoid compounding decay across sessions.\n */\n computeDecayedImportance(memory: Memory): number {\n const tau = this.#params.tauByType[memory.type];\n if (tau === 0) return memory.importance;\n\n const ageDays = (Date.now() - new Date(memory.createdAt).getTime()) / (1000 * 60 * 60 * 24);\n if (ageDays < 0) return memory.importance;\n\n // Synaptic consolidation window (days 0-7)\n if (ageDays <= 7) {\n if (memory.type === 'episodic') {\n const ebbinghaus = Math.exp(-ageDays * 0.4);\n return Math.max(this.#params.importanceFloor, memory.importance * ebbinghaus);\n }\n return memory.importance;\n }\n\n // Access modifier: higher access count = larger tau = slower decay\n const accessModifier = this.getAccessModifier(memory.accessCount);\n\n // Relation modifier: connected memories decay slower\n const relationCount = this.#relationRepo.countByMemory(memory.id);\n const relationModifier = relationCount >= this.#params.relationModifier.connectedThreshold\n ? this.#params.relationModifier.connectedMultiplier\n : this.#params.relationModifier.isolatedMultiplier;\n\n let effectiveTau = tau * accessModifier * relationModifier;\n\n // Injection penalty: surfaced but never used = noise\n if (memory.injectionCount > 5 && memory.accessCount === 0) {\n effectiveTau /= 1.5;\n }\n\n const decayFactor = Math.exp(-(ageDays - 7) / effectiveTau);\n const newImportance = memory.importance * decayFactor;\n\n return Math.max(this.#params.importanceFloor, newImportance);\n }\n\n private getAccessModifier(accessCount: number): number {\n for (const tier of this.#params.accessModifiers) {\n if (accessCount <= tier.maxCount) return tier.multiplier;\n }\n return 1.0;\n }\n}\n","import type { MemoryRepo } from '../storage/memory-repo.js';\nimport type { RelationRepo } from '../storage/relation-repo.js';\n\n// ── Types ────────────────────────────────────────────────────\n\nexport interface ConsolidationDeps {\n readonly memoryRepo: MemoryRepo;\n readonly relationRepo: RelationRepo;\n}\n\nexport interface ConsolidationReport {\n readonly deduplicated: number;\n readonly episodicsCompressed: number;\n readonly pruned: number;\n}\n\n// ── Consolidation Service ────────────────────────────────────\n\nexport class ConsolidationService {\n readonly #deps: ConsolidationDeps;\n\n constructor(deps: ConsolidationDeps) {\n this.#deps = deps;\n }\n\n /**\n * Run consolidation pipeline.\n * Phase 1: Deduplicate exact/near-exact content matches\n * Phase 2: Compress old consolidated episodics\n * Phase 3: Prune dead memories\n */\n async consolidate(): Promise<ConsolidationReport> {\n const deduplicated = this.deduplicateMemories();\n const episodicsCompressed = this.compressEpisodics();\n const pruned = this.prune();\n return { deduplicated, episodicsCompressed, pruned };\n }\n\n /**\n * Phase 1: Deduplication via content similarity.\n */\n deduplicateMemories(): number {\n const memories = this.#deps.memoryRepo.getAll();\n const seen = new Set<string>();\n let merged = 0;\n\n for (const memory of memories) {\n if (seen.has(memory.id)) continue;\n\n const normalizedContent = normalizeText(memory.content);\n\n for (const other of memories) {\n if (other.id === memory.id) continue;\n if (seen.has(other.id)) continue;\n\n const otherNormalized = normalizeText(other.content);\n if (normalizedContent !== otherNormalized) continue;\n\n const [keeper, discard] = memory.importance >= other.importance\n ? [memory, other] : [other, memory];\n\n const mergedTags = [...new Set([...keeper.tags, ...discard.tags])];\n this.#deps.memoryRepo.updateContent(keeper.id, {\n tags: mergedTags,\n importance: Math.max(keeper.importance, discard.importance),\n });\n\n this.#deps.memoryRepo.hardDelete(discard.id);\n seen.add(discard.id);\n merged++;\n\n if (discard.id === memory.id) break;\n }\n\n seen.add(memory.id);\n }\n\n return merged;\n }\n\n /**\n * Phase 2: Compress old episodic memories that have been consolidated.\n */\n compressEpisodics(): number {\n const episodics = this.#deps.memoryRepo.getByType('episodic');\n const now = Date.now();\n let compressed = 0;\n\n for (const memory of episodics) {\n const ageDays = (now - new Date(memory.createdAt).getTime()) / (1000 * 60 * 60 * 24);\n if (ageDays <= 60) continue;\n if (memory.accessCount > 0) continue;\n if (memory.importance >= 0.2) continue;\n\n const relations = this.#deps.relationRepo.getBySource(memory.id);\n const isConsolidated = relations.some(r => r.relationType === 'derived_from');\n if (!isConsolidated) continue;\n\n this.#deps.memoryRepo.hardDelete(memory.id);\n compressed++;\n }\n\n return compressed;\n }\n\n /**\n * Phase 3: Prune dead memories.\n */\n prune(): number {\n const memories = this.#deps.memoryRepo.getAll();\n const now = Date.now();\n let pruned = 0;\n\n for (const memory of memories) {\n if (memory.type === 'working') continue;\n const ageDays = (now - new Date(memory.createdAt).getTime()) / (1000 * 60 * 60 * 24);\n if (ageDays > 90 && memory.importance < 0.1 && memory.accessCount === 0) {\n this.#deps.memoryRepo.hardDelete(memory.id);\n pruned++;\n }\n }\n\n return pruned;\n }\n}\n\n// ── Helpers ──────────────────────────────────────────────────\n\nfunction normalizeText(text: string): string {\n return text.toLowerCase().replace(/\\s+/g, ' ').trim();\n}\n","import type { MemoryRepo } from \"../storage/memory-repo.js\";\nimport type { RelationRepo } from \"../storage/relation-repo.js\";\nimport type { Memory } from \"../types.js\";\nimport {\n estimateTokens,\n INJECTION_WEIGHTS as W,\n TYPE_INJECTION_BONUS,\n RECENCY_HALF_LIFE,\n INJECTION_MIN_SCORE,\n INJECTION_COLD_START_THRESHOLD,\n INJECTION_HEADER_TOKENS,\n} from \"../config.js\";\nimport { computeContextScore, type GitContext } from \"../utils/git-context.js\";\n\n// ── Types ──────────────────────────────────────────────────────\n\nexport type InjectionTier = \"full\" | \"summary\" | \"index\";\n\nexport interface ScoredMemory {\n readonly memory: Memory;\n readonly score: number;\n readonly tier: InjectionTier;\n readonly tokenCost: number;\n}\n\nexport interface InjectionResult {\n readonly memories: readonly ScoredMemory[];\n readonly tokensUsed: number;\n readonly tokenBudget: number;\n readonly totalCount: number;\n}\n\ninterface InjectionDeps {\n readonly memoryRepo: MemoryRepo;\n readonly relationRepo: RelationRepo;\n readonly gitContext?: GitContext;\n}\n\n// ── Service ────────────────────────────────────────────────────\n\nexport class InjectionService {\n readonly #deps: InjectionDeps;\n\n constructor(deps: InjectionDeps) {\n this.#deps = deps;\n }\n\n selectForInjection(tokenBudget: number, project?: string): InjectionResult {\n const allMemories = this.#deps.memoryRepo.getAll(project);\n const totalCount = allMemories.length;\n\n // Gate: skip working memories and below-floor importance\n const candidates = allMemories.filter(\n (m) => m.type !== \"working\" && m.importance >= 0.05,\n );\n\n if (candidates.length === 0) {\n return { memories: [], tokensUsed: 0, tokenBudget, totalCount };\n }\n\n // Cold start: inject all if few memories\n const minScore = candidates.length <= INJECTION_COLD_START_THRESHOLD\n ? 0.10\n : INJECTION_MIN_SCORE;\n\n // Score all candidates\n const scored = candidates\n .map((m) => ({ memory: m, score: this.#scoreMemory(m) }))\n .filter((s) => s.score >= minScore)\n .sort((a, b) => b.score - a.score);\n\n // Greedy token-budget packing with tier assignment\n return this.#packBudget(scored, tokenBudget, totalCount);\n }\n\n formatInjection(result: InjectionResult): string {\n const { memories, tokensUsed, tokenBudget, totalCount } = result;\n\n if (memories.length === 0) {\n return \"No memories stored for this project. Use memory_store to save knowledge across sessions.\";\n }\n\n const lines: string[] = [];\n lines.push(`# Agentic Memory (${memories.length} of ${totalCount} memories, ${tokensUsed}/${tokenBudget} tokens)`);\n lines.push(\"Use memory_search to retrieve full content for any memory listed here.\\n\");\n\n const full = memories.filter((m) => m.tier === \"full\");\n const summary = memories.filter((m) => m.tier === \"summary\");\n const index = memories.filter((m) => m.tier === \"index\");\n\n if (full.length > 0) {\n lines.push(\"## Key Memories\");\n for (const { memory: m } of full) {\n lines.push(`### ${m.title ?? \"(untitled)\"} [${m.type}]`);\n lines.push(m.content.slice(0, 500));\n if (m.tags.length > 0) lines.push(`Tags: ${m.tags.join(\", \")}`);\n lines.push(\"\");\n }\n }\n\n if (summary.length > 0) {\n lines.push(\"## Related Memories\");\n for (const { memory: m } of summary) {\n const snippet = m.content.slice(0, 150);\n const ellipsis = m.content.length > 150 ? \"...\" : \"\";\n lines.push(`- **${m.title ?? \"(untitled)\"}** [${m.type}]: ${snippet}${ellipsis}`);\n }\n lines.push(\"\");\n }\n\n if (index.length > 0) {\n lines.push(\"## Also Available (use memory_search)\");\n for (const { memory: m } of index) {\n lines.push(`- ${m.id.slice(0, 8)} ${m.title ?? \"(untitled)\"} [${m.type}]`);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n // ── Scoring ────────────────────────────────────────────────\n\n #scoreMemory(memory: Memory): number {\n const ctx = this.#contextRelevance(memory);\n const val = this.#valueSignal(memory);\n const imp = memory.importance;\n const rec = this.#recencyScore(memory);\n const typ = TYPE_INJECTION_BONUS[memory.type] ?? 0.5;\n const noise = this.#noisePenalty(memory);\n\n return (\n ctx * W.context +\n val * W.value +\n imp * W.importance +\n rec * W.recency +\n typ * W.typeBonus +\n noise * W.noise\n );\n }\n\n #contextRelevance(memory: Memory): number {\n if (!this.#deps.gitContext) return 0;\n return computeContextScore(memory.context, this.#deps.gitContext, \"\");\n }\n\n #valueSignal(memory: Memory): number {\n const { accessCount, injectionCount } = memory;\n\n // Never injected or accessed: neutral — give it a chance\n if (accessCount === 0 && injectionCount === 0) return 0.5;\n\n // Injected but never accessed: noise\n if (injectionCount > 0 && accessCount === 0) {\n return Math.max(0.0, 0.5 - Math.min(1.0, injectionCount / 10) * 0.5);\n }\n\n // Access-to-injection ratio\n const ratio = Math.min(1.0, accessCount / Math.max(1, injectionCount));\n return 0.4 + ratio * 0.6;\n }\n\n #recencyScore(memory: Memory): number {\n const halfLife = RECENCY_HALF_LIFE[memory.type] ?? 30;\n const ageDays = (Date.now() - new Date(memory.updatedAt).getTime()) / 86_400_000;\n return Math.exp(-ageDays * Math.LN2 / halfLife);\n }\n\n #noisePenalty(memory: Memory): number {\n if (memory.injectionCount <= 3) return 1.0; // no penalty for first 3\n if (memory.accessCount > 0) return 1.0; // any access = useful\n return Math.max(0.2, 1.0 - Math.log2(memory.injectionCount - 2) * 0.15);\n }\n\n // ── Token Budget Packing ───────────────────────────────────\n\n #packBudget(\n scored: readonly { readonly memory: Memory; readonly score: number }[],\n tokenBudget: number,\n totalCount: number,\n ): InjectionResult {\n const available = tokenBudget - INJECTION_HEADER_TOKENS;\n const selected: ScoredMemory[] = [];\n let tokensUsed = INJECTION_HEADER_TOKENS;\n\n for (const { memory, score } of scored) {\n if (tokensUsed >= tokenBudget) break;\n\n const tier = this.#assignTier(score, selected.length, available - (tokensUsed - INJECTION_HEADER_TOKENS));\n const cost = this.#estimateTierTokens(memory, tier);\n\n if (tokensUsed + cost <= tokenBudget) {\n selected.push({ memory, score, tier, tokenCost: cost });\n tokensUsed += cost;\n continue;\n }\n\n // Try cheaper tier\n const demoted = tier === \"full\" ? \"summary\" as const : tier === \"summary\" ? \"index\" as const : null;\n if (demoted) {\n const demotedCost = this.#estimateTierTokens(memory, demoted);\n if (tokensUsed + demotedCost <= tokenBudget) {\n selected.push({ memory, score, tier: demoted, tokenCost: demotedCost });\n tokensUsed += demotedCost;\n }\n }\n }\n\n // Track injections\n for (const entry of selected) {\n this.#deps.memoryRepo.incrementInjection(entry.memory.id);\n }\n\n return { memories: selected, tokensUsed, tokenBudget, totalCount };\n }\n\n #assignTier(score: number, position: number, remainingBudget: number): InjectionTier {\n if (position < 3 && score >= 0.60 && remainingBudget > 200) return \"full\";\n if (position < 8 && score >= 0.35 && remainingBudget > 80) return \"summary\";\n return \"index\";\n }\n\n #estimateTierTokens(memory: Memory, tier: InjectionTier): number {\n const meta = `[${memory.type}] ${memory.title ?? \"\"} (${memory.tags.join(\", \")})`;\n switch (tier) {\n case \"full\":\n return estimateTokens(memory.content.slice(0, 500)) + estimateTokens(meta) + 10;\n case \"summary\":\n return estimateTokens(memory.content.slice(0, 150)) + estimateTokens(meta) + 8;\n case \"index\":\n return estimateTokens(`${memory.id.slice(0, 8)} [${memory.type}] ${memory.title ?? \"(untitled)\"}`) + 4;\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
} from "./chunk-NAW47BYA.js";
|
|
5
5
|
import {
|
|
6
6
|
initStorage
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-X7ZY2Y2Z.js";
|
|
8
8
|
import "./chunk-TALTTAMW.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-JPVLFY2R.js";
|
|
10
10
|
import "./chunk-UN2XVQ5K.js";
|
|
11
11
|
import "./chunk-FL3JGYDM.js";
|
|
12
12
|
|
|
@@ -215,4 +215,4 @@ function readStdin(timeoutMs) {
|
|
|
215
215
|
export {
|
|
216
216
|
runExtract
|
|
217
217
|
};
|
|
218
|
-
//# sourceMappingURL=extract-
|
|
218
|
+
//# sourceMappingURL=extract-7D2EEXYD.js.map
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
loadConfig,
|
|
10
10
|
migrate,
|
|
11
11
|
resolveDataDir
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-JPVLFY2R.js";
|
|
13
13
|
import "./chunk-UN2XVQ5K.js";
|
|
14
14
|
import {
|
|
15
15
|
log
|
|
@@ -250,4 +250,4 @@ function installSkills(projectDir) {
|
|
|
250
250
|
export {
|
|
251
251
|
runInstall
|
|
252
252
|
};
|
|
253
|
-
//# sourceMappingURL=install-
|
|
253
|
+
//# sourceMappingURL=install-ULZUZI7T.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
initStorage
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-X7ZY2Y2Z.js";
|
|
5
5
|
import "./chunk-TALTTAMW.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-JPVLFY2R.js";
|
|
7
7
|
import "./chunk-UN2XVQ5K.js";
|
|
8
8
|
import {
|
|
9
9
|
log
|
|
@@ -72,4 +72,4 @@ async function runStats(opts) {
|
|
|
72
72
|
export {
|
|
73
73
|
runStats
|
|
74
74
|
};
|
|
75
|
-
//# sourceMappingURL=stats-
|
|
75
|
+
//# sourceMappingURL=stats-Y5ZFAZVF.js.map
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
loadConfig,
|
|
12
12
|
migrate,
|
|
13
13
|
resolveDataDir
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-JPVLFY2R.js";
|
|
15
15
|
import "./chunk-UN2XVQ5K.js";
|
|
16
16
|
import "./chunk-FL3JGYDM.js";
|
|
17
17
|
|
|
@@ -1098,4 +1098,4 @@ async function startTui(options) {
|
|
|
1098
1098
|
export {
|
|
1099
1099
|
startTui
|
|
1100
1100
|
};
|
|
1101
|
-
//# sourceMappingURL=tui-
|
|
1101
|
+
//# sourceMappingURL=tui-DTIXPD2V.js.map
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/memory/config.ts","../src/commands/memory/storage/database.ts","../src/commands/memory/storage/migrations/001-initial.ts","../src/commands/memory/storage/migrations/002-add-project.ts","../src/commands/memory/storage/migrator.ts"],"sourcesContent":["import { z } from 'zod';\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { DecayParams } from './types.js';\n\n// ── Config Schema ─────────────────────────────────────────────\n\nconst ConfigSchema = z.object({\n dataDir: z.string().default('~/.agentic-memory'),\n injectionBudget: z.number().int().min(100).max(20000).default(2000),\n consolidationInterval: z.number().int().min(1).default(10),\n enableReranker: z.boolean().default(true),\n logLevel: z.enum(['debug', 'info', 'warn', 'error']).default('warn'),\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\n// ── Defaults ──────────────────────────────────────────────────\n\nexport const DEFAULT_CONFIG: Config = {\n dataDir: '~/.agentic-memory',\n injectionBudget: 2000,\n consolidationInterval: 10,\n enableReranker: true,\n logLevel: 'warn',\n};\n\nexport const DEFAULT_DECAY_PARAMS: DecayParams = {\n tauByType: {\n working: 0, // cleared each session, tau irrelevant\n episodic: 60, // fast decay\n semantic: 365, // slow decay\n procedural: 730, // near-permanent\n pattern: 180, // medium decay\n },\n accessModifiers: [\n { maxCount: 3, multiplier: 1.0 },\n { maxCount: 10, multiplier: 2.0 },\n { maxCount: Infinity, multiplier: 4.0 },\n ],\n relationModifier: {\n connectedThreshold: 3,\n connectedMultiplier: 0.7,\n isolatedMultiplier: 1.3,\n },\n importanceFloor: 0.05,\n pruneThreshold: 0.1,\n pruneMinAgeDays: 90,\n};\n\nexport const SCORING_WEIGHTS = {\n text: 0.35,\n importance: 0.20,\n recency: 0.20,\n access: 0.10,\n context: 0.15,\n} as const;\n\n// ── Config Loader ─────────────────────────────────────────────\n\nexport function resolveDataDir(dataDir: string): string {\n if (dataDir.startsWith('~')) {\n return join(homedir(), dataDir.slice(1));\n }\n return dataDir;\n}\n\nexport function loadConfig(overrides?: Partial<Config>): Config {\n const envOverrides: Record<string, unknown> = {};\n\n const envBudget = process.env['AGENTIC_MEMORY_INJECTION_BUDGET'];\n if (envBudget !== undefined) {\n envOverrides['injectionBudget'] = parseInt(envBudget, 10);\n }\n\n const envLogLevel = process.env['AGENTIC_MEMORY_LOG_LEVEL'];\n if (envLogLevel !== undefined) {\n envOverrides['logLevel'] = envLogLevel;\n }\n\n const envDataDir = process.env['AGENTIC_MEMORY_DATA_DIR'];\n if (envDataDir !== undefined) {\n envOverrides['dataDir'] = envDataDir;\n }\n\n // Try loading config.json from data dir\n let fileConfig: Record<string, unknown> = {};\n const baseDir = resolveDataDir(overrides?.dataDir ?? envOverrides['dataDir'] as string ?? DEFAULT_CONFIG.dataDir);\n try {\n const raw = readFileSync(join(baseDir, 'config.json'), 'utf-8');\n fileConfig = JSON.parse(raw) as Record<string, unknown>;\n } catch (err) {\n const isNotFound = err instanceof Error && 'code' in err && (err as NodeJS.ErrnoException).code === 'ENOENT';\n if (!isNotFound) {\n // Malformed JSON or permissions error - warn, don't silently ignore\n console.error('[agentic-memory] Failed to load config.json:', err instanceof Error ? err.message : err);\n }\n }\n\n const merged = { ...DEFAULT_CONFIG, ...fileConfig, ...envOverrides, ...overrides };\n return ConfigSchema.parse(merged);\n}\n\n// ── Token Estimation ──────────────────────────────────────────\n\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n","import type DatabaseConstructor from 'better-sqlite3';\nimport { resolveDataDir } from '../config.js';\nimport { mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { cwdRequire } from '../utils/require-deps.js';\n\nexport interface DatabaseOptions {\n readonly dbPath?: string; // full path override (e.g. ':memory:' for tests)\n readonly dataDir?: string; // resolved data dir (default ~/.agentic-memory)\n}\n\nexport function createDatabase(options: DatabaseOptions = {}): DatabaseConstructor.Database {\n const dbPath = options.dbPath ?? resolveDbPath(options.dataDir);\n\n if (dbPath !== ':memory:') {\n mkdirSync(dirname(dbPath), { recursive: true });\n }\n\n const Database = cwdRequire('better-sqlite3') as typeof DatabaseConstructor;\n const sqliteVec = cwdRequire('sqlite-vec') as { load: (db: DatabaseConstructor.Database) => void };\n\n const db = new Database(dbPath);\n\n // Load sqlite-vec extension\n sqliteVec.load(db);\n\n // Configure PRAGMAs (order matters: foreign_keys before any ops, journal_mode is persistent)\n db.pragma('journal_mode = WAL');\n db.pragma('busy_timeout = 5000');\n db.pragma('foreign_keys = ON');\n db.pragma('cache_size = -64000');\n db.pragma('mmap_size = 268435456');\n db.pragma('synchronous = NORMAL');\n db.pragma('temp_store = MEMORY');\n db.pragma('journal_size_limit = 33554432');\n\n return db;\n}\n\nexport function closeDatabase(db: DatabaseConstructor.Database): void {\n try {\n db.pragma('wal_checkpoint(TRUNCATE)');\n } catch {\n // Checkpoint may fail on :memory: - that's fine\n }\n db.close();\n}\n\nfunction resolveDbPath(dataDir?: string): string {\n const dir = resolveDataDir(dataDir ?? '~/.agentic-memory');\n return join(dir, 'memory.db');\n}\n","import type Database from 'better-sqlite3';\n\nexport const version = 1;\n\nexport function up(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS meta (\n key TEXT PRIMARY KEY,\n value TEXT\n );\n\n CREATE TABLE IF NOT EXISTS memories (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL CHECK(type IN ('episodic','semantic','procedural','working','pattern')),\n title TEXT,\n content TEXT NOT NULL,\n context TEXT,\n source TEXT CHECK(source IN ('manual','session_end','consolidation','hook','import')),\n tags TEXT NOT NULL DEFAULT '[]',\n importance REAL NOT NULL DEFAULT 0.5 CHECK(importance >= 0.0 AND importance <= 1.0),\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now')),\n access_count INTEGER NOT NULL DEFAULT 0 CHECK(access_count >= 0),\n last_accessed TEXT,\n injection_count INTEGER NOT NULL DEFAULT 0 CHECK(injection_count >= 0),\n embedding BLOB\n );\n\n CREATE TABLE IF NOT EXISTS relations (\n source_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n target_id TEXT NOT NULL REFERENCES memories(id) ON DELETE CASCADE,\n relation_type TEXT NOT NULL CHECK(relation_type IN (\n 'relates_to','depends_on','contradicts','extends','implements','derived_from'\n )),\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n PRIMARY KEY (source_id, target_id, relation_type)\n );\n\n -- FTS5 external content (no data duplication)\n CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(\n title, content, tags,\n content=memories,\n content_rowid=rowid,\n tokenize='porter unicode61'\n );\n\n -- FTS5 sync triggers\n CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN\n INSERT INTO memories_fts(rowid, title, content, tags)\n VALUES (new.rowid, new.title, new.content, new.tags);\n END;\n\n CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN\n INSERT INTO memories_fts(memories_fts, rowid, title, content, tags)\n VALUES ('delete', old.rowid, old.title, old.content, old.tags);\n END;\n\n CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN\n INSERT INTO memories_fts(memories_fts, rowid, title, content, tags)\n VALUES ('delete', old.rowid, old.title, old.content, old.tags);\n INSERT INTO memories_fts(rowid, title, content, tags)\n VALUES (new.rowid, new.title, new.content, new.tags);\n END;\n\n -- Vector search (synced manually in application code)\n CREATE VIRTUAL TABLE IF NOT EXISTS memories_vec USING vec0(\n memory_id TEXT PRIMARY KEY,\n embedding float[384] distance_metric=cosine\n );\n\n -- Indexes\n CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);\n CREATE INDEX IF NOT EXISTS idx_memories_importance ON memories(importance);\n CREATE INDEX IF NOT EXISTS idx_memories_created_at ON memories(created_at);\n CREATE INDEX IF NOT EXISTS idx_relations_target ON relations(target_id);\n `);\n}\n","import type Database from 'better-sqlite3';\n\nexport const version = 2;\n\nexport function up(db: Database.Database): void {\n db.exec(`\n ALTER TABLE memories ADD COLUMN project TEXT;\n CREATE INDEX IF NOT EXISTS idx_memories_project ON memories(project);\n `);\n}\n","import type Database from 'better-sqlite3';\nimport * as migration001 from './migrations/001-initial.js';\nimport * as migration002 from './migrations/002-add-project.js';\n\ninterface Migration {\n readonly version: number;\n readonly up: (db: Database.Database) => void;\n}\n\nconst migrations: readonly Migration[] = [\n migration001,\n migration002,\n];\n\nexport function getSchemaVersion(db: Database.Database): number {\n try {\n const row = db.prepare(\"SELECT value FROM meta WHERE key = 'schema_version'\").get() as\n { value: string } | undefined;\n return row ? parseInt(row.value, 10) : 0;\n } catch {\n return 0;\n }\n}\n\nexport function migrate(db: Database.Database): void {\n const current = getSchemaVersion(db);\n const pending = migrations.filter(m => m.version > current);\n\n if (pending.length === 0) return;\n\n const runMigrations = db.transaction(() => {\n for (const m of pending) {\n m.up(db);\n db.prepare(\"INSERT OR REPLACE INTO meta (key, value) VALUES ('schema_version', ?)\")\n .run(String(m.version));\n }\n });\n\n runMigrations();\n}\n"],"mappings":"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","names":["join","join","up","version"]}
|