@aurora-foundation/obsidian-next 0.4.7 → 0.4.9
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/CHANGELOG.md +23 -0
- package/LICENSE +628 -190
- package/README.md +23 -9
- package/dist/auditLog-6WDBDNYL.js +8 -0
- package/dist/auditLog-HGPVDSDC.js +8 -0
- package/dist/auditLog-TDIKFBM4.js +8 -0
- package/dist/auditLog-XC2KY3ZZ.js +8 -0
- package/dist/chunk-2I235WNB.js +133 -0
- package/dist/chunk-2JWDGXTR.js +42 -0
- package/dist/chunk-2NOB6W2B.js +133 -0
- package/dist/chunk-3LFKVKKL.js +7199 -0
- package/dist/chunk-3U6WHPDX.js +4695 -0
- package/dist/chunk-3UCL6RYE.js +7272 -0
- package/dist/chunk-4GN2UQLI.js +130 -0
- package/dist/chunk-4MW33MZD.js +516 -0
- package/dist/chunk-4PUJBUKZ.js +4716 -0
- package/dist/chunk-4QHK6H6O.js +130 -0
- package/dist/chunk-55CQIHCO.js +133 -0
- package/dist/chunk-5LWINFWI.js +676 -0
- package/dist/chunk-5OKGLNQW.js +439 -0
- package/dist/chunk-5T6ETZEO.js +6183 -0
- package/dist/chunk-5WGIFUVL.js +4234 -0
- package/dist/chunk-66EW47T3.js +4237 -0
- package/dist/chunk-6TXUOTT2.js +581 -0
- package/dist/chunk-6YUYSYDA.js +130 -0
- package/dist/chunk-74VPNFMX.js +133 -0
- package/dist/chunk-77CGJRGV.js +6188 -0
- package/dist/chunk-7DS3VT4C.js +7135 -0
- package/dist/chunk-7FHX3VBT.js +133 -0
- package/dist/chunk-7MHF56YU.js +6178 -0
- package/dist/chunk-ABLPMV7G.js +133 -0
- package/dist/chunk-B77K6OQZ.js +687 -0
- package/dist/chunk-BKOXH66O.js +133 -0
- package/dist/chunk-BPP76UN2.js +130 -0
- package/dist/chunk-C4D56GRC.js +5936 -0
- package/dist/chunk-CCDPY4WE.js +370 -0
- package/dist/chunk-CHNVBJN3.js +7272 -0
- package/dist/chunk-CKBZI576.js +7229 -0
- package/dist/chunk-CW5HBSJ2.js +7198 -0
- package/dist/chunk-DGHDJEY7.js +133 -0
- package/dist/chunk-DPNIQWKZ.js +439 -0
- package/dist/chunk-DU4T3V2T.js +214 -0
- package/dist/chunk-DV3WFKNB.js +4679 -0
- package/dist/chunk-DZI2OVN2.js +516 -0
- package/dist/chunk-E45VILML.js +7198 -0
- package/dist/chunk-ECEUUYXC.js +7199 -0
- package/dist/chunk-EJRRSHPW.js +685 -0
- package/dist/chunk-EMBMLZFE.js +370 -0
- package/dist/chunk-EPG5V5OO.js +285 -0
- package/dist/chunk-F2R4HXXW.js +130 -0
- package/dist/chunk-FK6N66ES.js +581 -0
- package/dist/chunk-G3CZKGYA.js +197 -0
- package/dist/chunk-GUUPG4A7.js +7111 -0
- package/dist/chunk-HBAAUGUN.js +7230 -0
- package/dist/chunk-HHFJMK2Q.js +6177 -0
- package/dist/chunk-HINRQTCZ.js +196 -0
- package/dist/chunk-HRKJ3R2U.js +288 -0
- package/dist/chunk-HWVK4CVE.js +439 -0
- package/dist/chunk-JEYSADNZ.js +581 -0
- package/dist/chunk-JNEIL7UN.js +4252 -0
- package/dist/chunk-JTWSK277.js +676 -0
- package/dist/chunk-K4CHTTCJ.js +942 -0
- package/dist/chunk-K7R5KUDS.js +4695 -0
- package/dist/chunk-KNJFOURE.js +7151 -0
- package/dist/chunk-KY22FIT3.js +7256 -0
- package/dist/chunk-L2OTIJSF.js +4228 -0
- package/dist/chunk-LEEBUHP6.js +4655 -0
- package/dist/chunk-LK7UP2T7.js +130 -0
- package/dist/chunk-LPGNO3PK.js +284 -0
- package/dist/chunk-LYQYJMWS.js +133 -0
- package/dist/chunk-MBYFJXR3.js +130 -0
- package/dist/chunk-N3WX44L3.js +130 -0
- package/dist/chunk-N6AQWES3.js +6197 -0
- package/dist/chunk-NW4XSTQZ.js +130 -0
- package/dist/chunk-NWG2XURH.js +130 -0
- package/dist/chunk-O3GF3LJD.js +6142 -0
- package/dist/chunk-OHP5LD3Y.js +6188 -0
- package/dist/chunk-P5PQSFZT.js +6182 -0
- package/dist/chunk-PAADOWNP.js +130 -0
- package/dist/chunk-PERGND7L.js +7213 -0
- package/dist/chunk-PWA7V4XX.js +179 -0
- package/dist/chunk-QGCWEP6L.js +7111 -0
- package/dist/chunk-QVT2IHNJ.js +175 -0
- package/dist/chunk-QZNGYPMS.js +6161 -0
- package/dist/chunk-R6P2E2ZQ.js +207 -0
- package/dist/chunk-ROSDMGIL.js +4679 -0
- package/dist/chunk-RQZP7IKG.js +196 -0
- package/dist/chunk-RUQSPX3U.js +133 -0
- package/dist/chunk-S3BYHP5M.js +130 -0
- package/dist/chunk-S6GNETVE.js +438 -0
- package/dist/chunk-SDT2ZE2R.js +133 -0
- package/dist/chunk-SHQBXJFC.js +6166 -0
- package/dist/chunk-TJNISYTE.js +42 -0
- package/dist/chunk-TJW74HFF.js +130 -0
- package/dist/chunk-TPP72DTK.js +7096 -0
- package/dist/chunk-UOESII6R.js +42 -0
- package/dist/chunk-UWEDGLYJ.js +6142 -0
- package/dist/chunk-V5FYNAFX.js +133 -0
- package/dist/chunk-VPURF6UT.js +7198 -0
- package/dist/chunk-VQH6LWIZ.js +6184 -0
- package/dist/chunk-VS22YVX6.js +7111 -0
- package/dist/chunk-VSF5KBW7.js +367 -0
- package/dist/chunk-VV3JMCKY.js +214 -0
- package/dist/chunk-W5L7HOE3.js +133 -0
- package/dist/chunk-WFEVQISK.js +676 -0
- package/dist/chunk-WJZPSCEP.js +516 -0
- package/dist/chunk-WLV4MKEF.js +16 -0
- package/dist/chunk-WSEVQFFI.js +5428 -0
- package/dist/chunk-X7N2RNR3.js +5428 -0
- package/dist/chunk-XKZNMRNO.js +133 -0
- package/dist/chunk-Y7BVEC36.js +130 -0
- package/dist/chunk-YG7YSNNU.js +4226 -0
- package/dist/chunk-YHM62466.js +261 -0
- package/dist/chunk-YLTYJLDZ.js +7208 -0
- package/dist/chunk-YPMJD4YE.js +56 -0
- package/dist/chunk-YTX3FU2A.js +7199 -0
- package/dist/chunk-ZEQ3EBBN.js +214 -0
- package/dist/chunk-ZIWLQSLK.js +42 -0
- package/dist/chunk-ZJELNTEO.js +516 -0
- package/dist/chunk-ZOSSVNGK.js +370 -0
- package/dist/config-EYK32F2E.js +10 -0
- package/dist/config-FJPPPYTY.js +10 -0
- package/dist/config-VAHPVILX.js +10 -0
- package/dist/context-2YGE4U75.js +10 -0
- package/dist/context-5UFVYKES.js +9 -0
- package/dist/context-ANZF4J72.js +10 -0
- package/dist/context-GLUNCUBQ.js +10 -0
- package/dist/context-M5ULPZKQ.js +10 -0
- package/dist/context-NYOIRZKV.js +10 -0
- package/dist/context-YP2REI6A.js +10 -0
- package/dist/database-MP2JBLMF.js +8 -0
- package/dist/index.js +6177 -4688
- package/dist/keyManager-P2SZONKE.js +8 -0
- package/dist/mcp/index.js +127 -47
- package/dist/memory-BPGJAL4J.js +13 -0
- package/dist/memory-FRQOUI6W.js +13 -0
- package/dist/memory-OAMK27IZ.js +13 -0
- package/dist/memory-OZ734ALL.js +13 -0
- package/dist/memory-PQ2EWRMU.js +13 -0
- package/dist/memory-PXL45M6W.js +13 -0
- package/dist/memory-QZTBTYPH.js +13 -0
- package/dist/migrations-TLJ3WRVW.js +188 -0
- package/dist/resume-2NHDK6EI.js +17 -0
- package/dist/resume-44L2PDB2.js +17 -0
- package/dist/resume-4MXIWUJO.js +7 -0
- package/dist/resume-72VJX66I.js +17 -0
- package/dist/resume-7ZW4XM3B.js +15 -0
- package/dist/resume-AZHYQ657.js +17 -0
- package/dist/resume-CNLXSYHV.js +17 -0
- package/dist/resume-DPN4Q777.js +16 -0
- package/dist/resume-DSFHVNPI.js +15 -0
- package/dist/resume-GHLQJJTO.js +17 -0
- package/dist/resume-HJ6SBWTF.js +17 -0
- package/dist/resume-HRLYHY2L.js +17 -0
- package/dist/resume-ISIQFKO6.js +17 -0
- package/dist/resume-JQDEA6PS.js +15 -0
- package/dist/resume-KP3Y3Y7P.js +17 -0
- package/dist/resume-M4KHR5OI.js +17 -0
- package/dist/resume-N62OAMBG.js +17 -0
- package/dist/resume-NNMA5POT.js +17 -0
- package/dist/resume-ODYT3J4H.js +17 -0
- package/dist/resume-PCFJXA5O.js +17 -0
- package/dist/resume-PLF4XGBD.js +15 -0
- package/dist/resume-Q2PFX57V.js +17 -0
- package/dist/resume-QB4XI2J5.js +17 -0
- package/dist/resume-R5MFTUPF.js +17 -0
- package/dist/resume-SVA7223Z.js +17 -0
- package/dist/resume-TYKKDJZI.js +17 -0
- package/dist/resume-XIS45HKV.js +17 -0
- package/dist/resume-YCSEJTU7.js +17 -0
- package/dist/resume-YD76GI2J.js +15 -0
- package/dist/resume-YDN7EL77.js +17 -0
- package/dist/resume-YE7DB4ZA.js +17 -0
- package/dist/resume-YKAKOXWV.js +15 -0
- package/dist/resume-ZHBCVFDY.js +17 -0
- package/dist/scheduler-2CK24A2Q.js +14 -0
- package/dist/scheduler-7OAF2XKX.js +14 -0
- package/dist/scheduler-AS23AAB5.js +14 -0
- package/dist/scheduler-PCOYQJA5.js +14 -0
- package/dist/scheduler-V2ECBQPK.js +14 -0
- package/dist/scheduler-VEWZ6L7V.js +13 -0
- package/dist/scheduler-W37QMGDQ.js +14 -0
- package/dist/session-2E2JKPD7.js +15 -0
- package/dist/session-2VSF257B.js +14 -0
- package/dist/session-4APFTDJU.js +14 -0
- package/dist/session-5YS5LNNL.js +16 -0
- package/dist/session-6MO5ZPOB.js +16 -0
- package/dist/session-6XMGPRTQ.js +14 -0
- package/dist/session-7DJR77R7.js +16 -0
- package/dist/session-7DQHPWTR.js +14 -0
- package/dist/session-ADKIQCR5.js +16 -0
- package/dist/session-AOGH2GGI.js +16 -0
- package/dist/session-C4W6GDYG.js +16 -0
- package/dist/session-DCGNGGMV.js +14 -0
- package/dist/session-F5JKZAN2.js +16 -0
- package/dist/session-G6F3O2FQ.js +16 -0
- package/dist/session-GFBSARRO.js +16 -0
- package/dist/session-H5IWAIUI.js +16 -0
- package/dist/session-IPFA6AHC.js +14 -0
- package/dist/session-IWG2UOAX.js +14 -0
- package/dist/session-KJ2K4Y4M.js +14 -0
- package/dist/session-KPXFBW6Q.js +14 -0
- package/dist/session-KR256UL5.js +16 -0
- package/dist/session-M72LJXPR.js +16 -0
- package/dist/session-MBK3FODN.js +14 -0
- package/dist/session-MOUFAU7G.js +16 -0
- package/dist/session-NRC6ZXFQ.js +16 -0
- package/dist/session-NRPQMV4K.js +16 -0
- package/dist/session-O5IFFJZQ.js +14 -0
- package/dist/session-OF5BGKDE.js +16 -0
- package/dist/session-OGRZMIM7.js +14 -0
- package/dist/session-OJOFAJG3.js +16 -0
- package/dist/session-OKU4N3SP.js +16 -0
- package/dist/session-P2VAOSFB.js +14 -0
- package/dist/session-PKOVZD4M.js +16 -0
- package/dist/session-POAIMUVN.js +16 -0
- package/dist/session-PSHFONFE.js +16 -0
- package/dist/session-QKYVVZFV.js +16 -0
- package/dist/session-QPWGBMUS.js +14 -0
- package/dist/session-R5UG5PZR.js +14 -0
- package/dist/session-RAY6BZRQ.js +16 -0
- package/dist/session-S3VATHMU.js +16 -0
- package/dist/session-SYTD7RHW.js +14 -0
- package/dist/session-UHMMVO4J.js +16 -0
- package/dist/session-WEX5K3ZY.js +14 -0
- package/dist/session-XFLOXGU3.js +14 -0
- package/dist/session-XV2A4HHG.js +14 -0
- package/dist/settings-3VPJYD4D.js +8 -0
- package/dist/settings-GZTJJTBK.js +8 -0
- package/dist/settings-YKJFSKMO.js +8 -0
- package/dist/shell-FM34624T.js +8 -0
- package/package.json +14 -4
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
import {
|
|
2
|
+
context
|
|
3
|
+
} from "./chunk-EMBMLZFE.js";
|
|
4
|
+
import {
|
|
5
|
+
bus
|
|
6
|
+
} from "./chunk-WQM6FFSD.js";
|
|
7
|
+
import {
|
|
8
|
+
db
|
|
9
|
+
} from "./chunk-FNLWB54Z.js";
|
|
10
|
+
|
|
11
|
+
// src/core/memory.ts
|
|
12
|
+
import fs from "fs/promises";
|
|
13
|
+
import path from "path";
|
|
14
|
+
import os from "os";
|
|
15
|
+
import { pipeline } from "@xenova/transformers";
|
|
16
|
+
import chokidar from "chokidar";
|
|
17
|
+
var MemoryManager = class {
|
|
18
|
+
initialized = false;
|
|
19
|
+
extractor = null;
|
|
20
|
+
watcher = null;
|
|
21
|
+
async init() {
|
|
22
|
+
if (this.initialized) return;
|
|
23
|
+
try {
|
|
24
|
+
this.extractor = await pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");
|
|
25
|
+
} catch (e) {
|
|
26
|
+
bus.emitAgent({ type: "error", message: `[Memory] Embedding pipeline failed to initialize: ${e instanceof Error ? e.message : String(e)}` });
|
|
27
|
+
}
|
|
28
|
+
this.initialized = true;
|
|
29
|
+
this.startWatcher();
|
|
30
|
+
}
|
|
31
|
+
startWatcher() {
|
|
32
|
+
const memoryPath = path.join(os.homedir(), ".obsidian-next", "MEMORY.md");
|
|
33
|
+
if (this.watcher) return;
|
|
34
|
+
this.watcher = chokidar.watch(memoryPath, {
|
|
35
|
+
persistent: true,
|
|
36
|
+
ignoreInitial: true
|
|
37
|
+
});
|
|
38
|
+
this.watcher.on("change", async () => {
|
|
39
|
+
try {
|
|
40
|
+
await this.importFromMarkdown(memoryPath);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async importFromMarkdown(filePath) {
|
|
46
|
+
try {
|
|
47
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
48
|
+
const sections = content.split("## ").slice(1);
|
|
49
|
+
const typeMap = {
|
|
50
|
+
"User Preferences": "user_preference",
|
|
51
|
+
"Project Facts": "project_fact",
|
|
52
|
+
"Decision Log": "decision_log",
|
|
53
|
+
"Learned Patterns": "learned_pattern",
|
|
54
|
+
"Daily Summaries": "daily_summary"
|
|
55
|
+
};
|
|
56
|
+
for (const section of sections) {
|
|
57
|
+
const lines = section.split("\n");
|
|
58
|
+
const typeHeader = lines[0].trim();
|
|
59
|
+
const type = typeMap[typeHeader];
|
|
60
|
+
if (!type) continue;
|
|
61
|
+
const memoBlocks = section.split("### ").slice(1);
|
|
62
|
+
for (const block of memoBlocks) {
|
|
63
|
+
const blockLines = block.split("\n");
|
|
64
|
+
const key = blockLines[0].trim();
|
|
65
|
+
const memoContent = blockLines.slice(1).filter((l) => !l.trim().startsWith("*Last updated:")).join("\n").trim();
|
|
66
|
+
if (key && memoContent) {
|
|
67
|
+
await this.store(type, key, memoContent);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async getEmbedding(text) {
|
|
75
|
+
await this.init();
|
|
76
|
+
if (!this.extractor) throw new Error("Embedding pipeline not initialized");
|
|
77
|
+
const output = await this.extractor(text, { pooling: "mean", normalize: true });
|
|
78
|
+
return Float32Array.from(output.data);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Store a memory/fact
|
|
82
|
+
*/
|
|
83
|
+
async store(type, key, content) {
|
|
84
|
+
await this.init();
|
|
85
|
+
const sessionId = context.get().session_id;
|
|
86
|
+
try {
|
|
87
|
+
const embeddingArray = await this.getEmbedding(`${key}: ${content}`);
|
|
88
|
+
const embedding = Buffer.from(embeddingArray.buffer);
|
|
89
|
+
const transaction = db.getDb().transaction(() => {
|
|
90
|
+
const existing = db.getDb().prepare(`
|
|
91
|
+
SELECT id FROM memos WHERE type = ? AND key = ?
|
|
92
|
+
`).get(type, key);
|
|
93
|
+
let memoId;
|
|
94
|
+
if (existing) {
|
|
95
|
+
db.getDb().prepare(`
|
|
96
|
+
UPDATE memos SET content = ?, updated_at = strftime('%s', 'now'), session_id = ?
|
|
97
|
+
WHERE id = ?
|
|
98
|
+
`).run(content, sessionId, existing.id);
|
|
99
|
+
memoId = existing.id;
|
|
100
|
+
} else {
|
|
101
|
+
const result = db.getDb().prepare(`
|
|
102
|
+
INSERT INTO memos (session_id, type, key, content)
|
|
103
|
+
VALUES (?, ?, ?, ?)
|
|
104
|
+
`).run(sessionId, type, key, content);
|
|
105
|
+
memoId = Number(result.lastInsertRowid);
|
|
106
|
+
}
|
|
107
|
+
db.getDb().prepare("DELETE FROM vec_memos WHERE rowid = ?").run(BigInt(memoId));
|
|
108
|
+
db.getDb().prepare(`
|
|
109
|
+
INSERT INTO vec_memos (rowid, embedding)
|
|
110
|
+
VALUES (?, ?)
|
|
111
|
+
`).run(BigInt(memoId), embedding);
|
|
112
|
+
});
|
|
113
|
+
transaction();
|
|
114
|
+
return true;
|
|
115
|
+
} catch (e) {
|
|
116
|
+
console.error("Failed to store memory:", e);
|
|
117
|
+
bus.emitAgent({ type: "error", message: `Memory Store Error: ${e instanceof Error ? e.message : String(e)}` });
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Recall a specific memory by key
|
|
123
|
+
*/
|
|
124
|
+
async recall(key) {
|
|
125
|
+
await this.init();
|
|
126
|
+
try {
|
|
127
|
+
const row = db.getDb().prepare(`
|
|
128
|
+
SELECT id, type, key, content, created_at, updated_at, access_count, last_accessed
|
|
129
|
+
FROM memos
|
|
130
|
+
WHERE key = ?
|
|
131
|
+
ORDER BY updated_at DESC
|
|
132
|
+
LIMIT 1
|
|
133
|
+
`).get(key);
|
|
134
|
+
if (!row) return null;
|
|
135
|
+
return {
|
|
136
|
+
id: row.id,
|
|
137
|
+
type: row.type,
|
|
138
|
+
key: row.key,
|
|
139
|
+
content: row.content,
|
|
140
|
+
created_at: new Date(row.created_at * 1e3).toISOString(),
|
|
141
|
+
updated_at: new Date(row.updated_at * 1e3).toISOString(),
|
|
142
|
+
access_count: row.access_count || 0,
|
|
143
|
+
last_accessed: row.last_accessed ? new Date(row.last_accessed * 1e3).toISOString() : void 0
|
|
144
|
+
};
|
|
145
|
+
} catch (e) {
|
|
146
|
+
bus.emitAgent({ type: "error", message: `[Memory] Recall failed for key "${key}": ${e instanceof Error ? e.message : String(e)}` });
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Search memories by type or content (Semantic Search)
|
|
152
|
+
*/
|
|
153
|
+
async search(query, type) {
|
|
154
|
+
await this.init();
|
|
155
|
+
try {
|
|
156
|
+
const embeddingArray = await this.getEmbedding(query);
|
|
157
|
+
const embedding = Buffer.from(embeddingArray.buffer);
|
|
158
|
+
let sql = `
|
|
159
|
+
SELECT m.id, m.type, m.key, m.content, m.created_at, m.updated_at,
|
|
160
|
+
v.distance
|
|
161
|
+
FROM vec_memos v
|
|
162
|
+
JOIN memos m ON v.rowid = m.id
|
|
163
|
+
WHERE v.embedding MATCH ?
|
|
164
|
+
AND k = 20
|
|
165
|
+
`;
|
|
166
|
+
const params = [embedding];
|
|
167
|
+
if (type) {
|
|
168
|
+
sql = sql.replace("AND k = 20", "AND m.type = ? AND k = 20");
|
|
169
|
+
params.push(type);
|
|
170
|
+
}
|
|
171
|
+
const rows = db.getDb().prepare(sql).all(...params);
|
|
172
|
+
if (rows.length === 0) {
|
|
173
|
+
return this.keywordSearch(query, type);
|
|
174
|
+
}
|
|
175
|
+
return rows.map((row) => ({
|
|
176
|
+
id: row.id,
|
|
177
|
+
type: row.type,
|
|
178
|
+
key: row.key,
|
|
179
|
+
content: row.content,
|
|
180
|
+
created_at: new Date(row.created_at * 1e3).toISOString(),
|
|
181
|
+
updated_at: new Date(row.updated_at * 1e3).toISOString()
|
|
182
|
+
}));
|
|
183
|
+
} catch (e) {
|
|
184
|
+
bus.emitAgent({ type: "thought", content: `[Memory] Semantic search failed, falling back to keyword search.`, hidden: true });
|
|
185
|
+
return this.keywordSearch(query, type);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async keywordSearch(query, type) {
|
|
189
|
+
try {
|
|
190
|
+
let sql = `
|
|
191
|
+
SELECT id, type, key, content, created_at, updated_at
|
|
192
|
+
FROM memos
|
|
193
|
+
WHERE (key LIKE ? OR content LIKE ?)
|
|
194
|
+
`;
|
|
195
|
+
const params = [`%${query}%`, `%${query}%`];
|
|
196
|
+
if (type) {
|
|
197
|
+
sql += " AND type = ?";
|
|
198
|
+
params.push(type);
|
|
199
|
+
}
|
|
200
|
+
sql += " ORDER BY updated_at DESC LIMIT 20";
|
|
201
|
+
const rows = db.getDb().prepare(sql).all(...params);
|
|
202
|
+
return rows.map((row) => ({
|
|
203
|
+
id: row.id,
|
|
204
|
+
type: row.type,
|
|
205
|
+
key: row.key,
|
|
206
|
+
content: row.content,
|
|
207
|
+
created_at: new Date(row.created_at * 1e3).toISOString(),
|
|
208
|
+
updated_at: new Date(row.updated_at * 1e3).toISOString()
|
|
209
|
+
}));
|
|
210
|
+
} catch (e) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Get all memories of a specific type
|
|
216
|
+
*/
|
|
217
|
+
async getByType(type) {
|
|
218
|
+
await this.init();
|
|
219
|
+
try {
|
|
220
|
+
const rows = db.getDb().prepare(`
|
|
221
|
+
SELECT id, type, key, content, created_at, updated_at
|
|
222
|
+
FROM memos
|
|
223
|
+
WHERE type = ?
|
|
224
|
+
ORDER BY updated_at DESC
|
|
225
|
+
`).all(type);
|
|
226
|
+
return rows.map((row) => ({
|
|
227
|
+
id: row.id,
|
|
228
|
+
type: row.type,
|
|
229
|
+
key: row.key,
|
|
230
|
+
content: row.content,
|
|
231
|
+
created_at: new Date(row.created_at * 1e3).toISOString(),
|
|
232
|
+
updated_at: new Date(row.updated_at * 1e3).toISOString()
|
|
233
|
+
}));
|
|
234
|
+
} catch (e) {
|
|
235
|
+
bus.emitAgent({ type: "error", message: `[Memory] Failed to get memories by type "${type}": ${e instanceof Error ? e.message : String(e)}` });
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Get user preferences summary for context injection
|
|
241
|
+
*/
|
|
242
|
+
async getUserContext() {
|
|
243
|
+
await this.init();
|
|
244
|
+
try {
|
|
245
|
+
const prefs = await this.getByType("user_preference");
|
|
246
|
+
const facts = await this.getByType("project_fact");
|
|
247
|
+
if (prefs.length === 0 && facts.length === 0) {
|
|
248
|
+
return "";
|
|
249
|
+
}
|
|
250
|
+
const lines = ["[RECALL]"];
|
|
251
|
+
if (prefs.length > 0) {
|
|
252
|
+
for (const p of prefs.slice(0, 5)) {
|
|
253
|
+
lines.push(`${p.key}: ${p.content}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (facts.length > 0) {
|
|
257
|
+
for (const f of facts.slice(0, 10)) {
|
|
258
|
+
lines.push(`${f.key}: ${f.content}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (lines.length === 1) return "";
|
|
262
|
+
return lines.join("\n");
|
|
263
|
+
} catch (e) {
|
|
264
|
+
return "";
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Delete a memory
|
|
269
|
+
*/
|
|
270
|
+
async forget(key) {
|
|
271
|
+
await this.init();
|
|
272
|
+
try {
|
|
273
|
+
db.getDb().prepare("DELETE FROM memos WHERE key = ?").run(key);
|
|
274
|
+
return true;
|
|
275
|
+
} catch (e) {
|
|
276
|
+
bus.emitAgent({ type: "error", message: `[Memory] Failed to forget key "${key}": ${e instanceof Error ? e.message : String(e)}` });
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Clear all memories (use with caution)
|
|
282
|
+
*/
|
|
283
|
+
async clearAll() {
|
|
284
|
+
await this.init();
|
|
285
|
+
try {
|
|
286
|
+
db.getDb().prepare("DELETE FROM memos").run();
|
|
287
|
+
return true;
|
|
288
|
+
} catch (e) {
|
|
289
|
+
bus.emitAgent({ type: "error", message: `[Memory] Failed to clear all memories: ${e instanceof Error ? e.message : String(e)}` });
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Get memory statistics
|
|
295
|
+
*/
|
|
296
|
+
async getStats() {
|
|
297
|
+
await this.init();
|
|
298
|
+
try {
|
|
299
|
+
const total = db.getDb().prepare("SELECT COUNT(*) as count FROM memos").get();
|
|
300
|
+
const byTypeRows = db.getDb().prepare(`
|
|
301
|
+
SELECT type, COUNT(*) as count FROM memos GROUP BY type
|
|
302
|
+
`).all();
|
|
303
|
+
const byType = {};
|
|
304
|
+
for (const row of byTypeRows) {
|
|
305
|
+
byType[row.type] = row.count;
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
total: total.count,
|
|
309
|
+
byType
|
|
310
|
+
};
|
|
311
|
+
} catch (e) {
|
|
312
|
+
return { total: 0, byType: {} };
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Export all memories to a Markdown file for human readability
|
|
317
|
+
*/
|
|
318
|
+
async exportToMarkdown() {
|
|
319
|
+
await this.init();
|
|
320
|
+
try {
|
|
321
|
+
const rows = db.getDb().prepare(`
|
|
322
|
+
SELECT type, key, content, updated_at
|
|
323
|
+
FROM memos
|
|
324
|
+
ORDER BY type, key
|
|
325
|
+
`).all();
|
|
326
|
+
if (rows.length === 0) {
|
|
327
|
+
return "No memories to export.";
|
|
328
|
+
}
|
|
329
|
+
let markdown = "# Obsidian Memory Bank\n\n";
|
|
330
|
+
markdown += `Generated on: ${(/* @__PURE__ */ new Date()).toLocaleString()}
|
|
331
|
+
|
|
332
|
+
`;
|
|
333
|
+
const grouped = {};
|
|
334
|
+
for (const row of rows) {
|
|
335
|
+
if (!grouped[row.type]) grouped[row.type] = [];
|
|
336
|
+
grouped[row.type].push(row);
|
|
337
|
+
}
|
|
338
|
+
const typeNames = {
|
|
339
|
+
"user_preference": "User Preferences",
|
|
340
|
+
"project_fact": "Project Facts",
|
|
341
|
+
"decision_log": "Decision Log",
|
|
342
|
+
"learned_pattern": "Learned Patterns",
|
|
343
|
+
"daily_summary": "Daily Summaries"
|
|
344
|
+
};
|
|
345
|
+
for (const type of Object.keys(grouped)) {
|
|
346
|
+
markdown += `## ${typeNames[type] || type}
|
|
347
|
+
|
|
348
|
+
`;
|
|
349
|
+
for (const memo of grouped[type]) {
|
|
350
|
+
markdown += `### ${memo.key}
|
|
351
|
+
`;
|
|
352
|
+
markdown += `${memo.content}
|
|
353
|
+
|
|
354
|
+
`;
|
|
355
|
+
markdown += `*Last updated: ${new Date(memo.updated_at * 1e3).toLocaleString()}*
|
|
356
|
+
|
|
357
|
+
`;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const { config } = await import("./config-FJPPPYTY.js");
|
|
361
|
+
const cfg = await config.load();
|
|
362
|
+
const exportDir = path.join(os.homedir(), ".obsidian-next");
|
|
363
|
+
const exportPath = path.join(exportDir, "MEMORY.md");
|
|
364
|
+
await fs.mkdir(exportDir, { recursive: true });
|
|
365
|
+
await fs.writeFile(exportPath, markdown, "utf-8");
|
|
366
|
+
return exportPath;
|
|
367
|
+
} catch (error) {
|
|
368
|
+
bus.emitAgent({ type: "error", message: `[Memory] Export failed: ${error.message}` });
|
|
369
|
+
throw new Error(`Failed to export memory: ${error.message}`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// ==================== Memory Graph Methods ====================
|
|
373
|
+
/**
|
|
374
|
+
* Add a relationship between two memos
|
|
375
|
+
*/
|
|
376
|
+
async addRelation(sourceKey, targetKey, relationType, strength = 1) {
|
|
377
|
+
await this.init();
|
|
378
|
+
try {
|
|
379
|
+
const source = await this.recall(sourceKey);
|
|
380
|
+
const target = await this.recall(targetKey);
|
|
381
|
+
if (!source || !target) {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
db.getDb().prepare(`
|
|
385
|
+
INSERT OR REPLACE INTO memo_relations (source_id, target_id, relation_type, strength)
|
|
386
|
+
VALUES (?, ?, ?, ?)
|
|
387
|
+
`).run(source.id, target.id, relationType, Math.max(0, Math.min(1, strength)));
|
|
388
|
+
return true;
|
|
389
|
+
} catch (e) {
|
|
390
|
+
bus.emitAgent({ type: "error", message: `[Memory] Failed to add relation: ${e instanceof Error ? e.message : String(e)}` });
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Get all relationships for a memo
|
|
396
|
+
*/
|
|
397
|
+
async getRelations(key) {
|
|
398
|
+
await this.init();
|
|
399
|
+
try {
|
|
400
|
+
const memo = await this.recall(key);
|
|
401
|
+
if (!memo) return [];
|
|
402
|
+
const rows = db.getDb().prepare(`
|
|
403
|
+
SELECT id, source_id, target_id, relation_type, strength, created_at
|
|
404
|
+
FROM memo_relations
|
|
405
|
+
WHERE source_id = ? OR target_id = ?
|
|
406
|
+
`).all(memo.id, memo.id);
|
|
407
|
+
return rows.map((row) => ({
|
|
408
|
+
id: row.id,
|
|
409
|
+
sourceId: row.source_id,
|
|
410
|
+
targetId: row.target_id,
|
|
411
|
+
relationType: row.relation_type,
|
|
412
|
+
strength: row.strength,
|
|
413
|
+
createdAt: new Date(row.created_at * 1e3).toISOString()
|
|
414
|
+
}));
|
|
415
|
+
} catch (e) {
|
|
416
|
+
return [];
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Get related memos with their relationships
|
|
421
|
+
*/
|
|
422
|
+
async getRelatedMemos(key) {
|
|
423
|
+
await this.init();
|
|
424
|
+
try {
|
|
425
|
+
const memo = await this.recall(key);
|
|
426
|
+
if (!memo) return [];
|
|
427
|
+
const rows = db.getDb().prepare(`
|
|
428
|
+
SELECT
|
|
429
|
+
m.id, m.type, m.key, m.content, m.created_at, m.updated_at,
|
|
430
|
+
m.access_count, m.last_accessed,
|
|
431
|
+
r.id as rel_id, r.source_id, r.target_id, r.relation_type, r.strength, r.created_at as rel_created
|
|
432
|
+
FROM memo_relations r
|
|
433
|
+
JOIN memos m ON (
|
|
434
|
+
(r.source_id = ? AND r.target_id = m.id) OR
|
|
435
|
+
(r.target_id = ? AND r.source_id = m.id)
|
|
436
|
+
)
|
|
437
|
+
WHERE (r.source_id = ? OR r.target_id = ?)
|
|
438
|
+
ORDER BY r.strength DESC
|
|
439
|
+
`).all(memo.id, memo.id, memo.id, memo.id);
|
|
440
|
+
return rows.map((row) => ({
|
|
441
|
+
memo: {
|
|
442
|
+
id: row.id,
|
|
443
|
+
type: row.type,
|
|
444
|
+
key: row.key,
|
|
445
|
+
content: row.content,
|
|
446
|
+
created_at: new Date(row.created_at * 1e3).toISOString(),
|
|
447
|
+
updated_at: new Date(row.updated_at * 1e3).toISOString(),
|
|
448
|
+
access_count: row.access_count || 0,
|
|
449
|
+
last_accessed: row.last_accessed ? new Date(row.last_accessed * 1e3).toISOString() : void 0
|
|
450
|
+
},
|
|
451
|
+
relation: {
|
|
452
|
+
id: row.rel_id,
|
|
453
|
+
sourceId: row.source_id,
|
|
454
|
+
targetId: row.target_id,
|
|
455
|
+
relationType: row.relation_type,
|
|
456
|
+
strength: row.strength,
|
|
457
|
+
createdAt: new Date(row.rel_created * 1e3).toISOString()
|
|
458
|
+
}
|
|
459
|
+
}));
|
|
460
|
+
} catch (e) {
|
|
461
|
+
return [];
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Calculate relevance score with temporal decay
|
|
466
|
+
* Score decays exponentially based on time since last access and access frequency
|
|
467
|
+
*/
|
|
468
|
+
getRelevanceScore(memo) {
|
|
469
|
+
const now = Date.now();
|
|
470
|
+
const lastAccessed = memo.last_accessed ? new Date(memo.last_accessed).getTime() : now;
|
|
471
|
+
const accessCount = memo.access_count || 1;
|
|
472
|
+
const halfLife = 7 * 24 * 60 * 60 * 1e3;
|
|
473
|
+
const timeSinceAccess = now - lastAccessed;
|
|
474
|
+
const decayFactor = Math.pow(0.5, timeSinceAccess / halfLife);
|
|
475
|
+
const frequencyBoost = 1 + Math.log10(accessCount + 1) * 0.2;
|
|
476
|
+
return decayFactor * frequencyBoost;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Update access stats for a memo (call when memo is retrieved/used)
|
|
480
|
+
*/
|
|
481
|
+
async updateAccessStats(key) {
|
|
482
|
+
await this.init();
|
|
483
|
+
try {
|
|
484
|
+
db.getDb().prepare(`
|
|
485
|
+
UPDATE memos
|
|
486
|
+
SET access_count = COALESCE(access_count, 0) + 1,
|
|
487
|
+
last_accessed = strftime('%s', 'now')
|
|
488
|
+
WHERE key = ?
|
|
489
|
+
`).run(key);
|
|
490
|
+
} catch (e) {
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Search with relationship awareness - includes related memos in results
|
|
495
|
+
*/
|
|
496
|
+
async searchWithRelations(query, type) {
|
|
497
|
+
await this.init();
|
|
498
|
+
const baseResults = await this.search(query, type);
|
|
499
|
+
const enriched = await Promise.all(baseResults.map(async (memo) => {
|
|
500
|
+
await this.updateAccessStats(memo.key);
|
|
501
|
+
const freshMemo = await this.recall(memo.key);
|
|
502
|
+
const memoWithStats = freshMemo || memo;
|
|
503
|
+
const score = this.getRelevanceScore(memoWithStats);
|
|
504
|
+
const relatedResults = await this.getRelatedMemos(memo.key);
|
|
505
|
+
const related = relatedResults.map((r) => r.memo);
|
|
506
|
+
return { memo: memoWithStats, score, related };
|
|
507
|
+
}));
|
|
508
|
+
return enriched.sort((a, b) => b.score - a.score);
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
var memory = new MemoryManager();
|
|
512
|
+
|
|
513
|
+
export {
|
|
514
|
+
MemoryManager,
|
|
515
|
+
memory
|
|
516
|
+
};
|