@super-pocock-ai/memory-core 2.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.js +576 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +533 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +35 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,576 @@
|
|
|
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 index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
34
|
+
MemoryCorePlugin: () => MemoryCorePlugin,
|
|
35
|
+
MemoryStore: () => MemoryStore,
|
|
36
|
+
createMemoryTool: () => createMemoryTool,
|
|
37
|
+
default: () => index_default,
|
|
38
|
+
getGlobalMemoryDir: () => getGlobalMemoryDir,
|
|
39
|
+
getMemoryRoot: () => getMemoryRoot,
|
|
40
|
+
getProjectMemoryDir: () => getProjectMemoryDir,
|
|
41
|
+
loadConfig: () => loadConfig,
|
|
42
|
+
resolveProjectId: () => resolveProjectId
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(index_exports);
|
|
45
|
+
var import_fs7 = require("fs");
|
|
46
|
+
var import_path6 = require("path");
|
|
47
|
+
|
|
48
|
+
// src/tool.ts
|
|
49
|
+
var import_plugin = require("@opencode-ai/plugin");
|
|
50
|
+
var import_zod = require("zod");
|
|
51
|
+
|
|
52
|
+
// src/store.ts
|
|
53
|
+
var import_better_sqlite3 = __toESM(require("better-sqlite3"));
|
|
54
|
+
var MemoryStore = class {
|
|
55
|
+
db;
|
|
56
|
+
constructor(dbPath) {
|
|
57
|
+
this.db = new import_better_sqlite3.default(dbPath);
|
|
58
|
+
this.db.pragma("journal_mode = WAL");
|
|
59
|
+
this.init();
|
|
60
|
+
}
|
|
61
|
+
init() {
|
|
62
|
+
this.db.exec(`
|
|
63
|
+
CREATE TABLE IF NOT EXISTS memory_files (
|
|
64
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
65
|
+
path TEXT NOT NULL UNIQUE,
|
|
66
|
+
scope TEXT NOT NULL,
|
|
67
|
+
scope_id TEXT NOT NULL DEFAULT '',
|
|
68
|
+
type TEXT NOT NULL,
|
|
69
|
+
fingerprint TEXT NOT NULL,
|
|
70
|
+
last_indexed_at INTEGER NOT NULL
|
|
71
|
+
)
|
|
72
|
+
`);
|
|
73
|
+
this.db.exec(`
|
|
74
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts
|
|
75
|
+
USING fts5(path, body, tokenize='unicode61')
|
|
76
|
+
`);
|
|
77
|
+
}
|
|
78
|
+
search(query, filters = {}) {
|
|
79
|
+
const tokens = query.split(/\s+/).filter(Boolean).map((t) => `"${t}"`);
|
|
80
|
+
if (tokens.length === 0) return [];
|
|
81
|
+
const ftsQuery = tokens.join(" OR ");
|
|
82
|
+
const limit = filters.limit ?? 10;
|
|
83
|
+
let sql = `
|
|
84
|
+
SELECT f.path, m.scope, m.scope_id, m.type,
|
|
85
|
+
snippet(memory_fts, 1, '<<', '>>', '...', 32) AS snippet,
|
|
86
|
+
bm25(memory_fts) AS score
|
|
87
|
+
FROM memory_fts f
|
|
88
|
+
JOIN memory_files m ON m.path = f.path
|
|
89
|
+
WHERE memory_fts MATCH ?
|
|
90
|
+
`;
|
|
91
|
+
const params = [ftsQuery];
|
|
92
|
+
if (filters.scope) {
|
|
93
|
+
sql += ` AND m.scope = ?`;
|
|
94
|
+
params.push(filters.scope);
|
|
95
|
+
}
|
|
96
|
+
if (filters.scope_id) {
|
|
97
|
+
sql += ` AND m.scope_id = ?`;
|
|
98
|
+
params.push(filters.scope_id);
|
|
99
|
+
}
|
|
100
|
+
if (filters.type) {
|
|
101
|
+
sql += ` AND m.type = ?`;
|
|
102
|
+
params.push(filters.type);
|
|
103
|
+
}
|
|
104
|
+
sql += ` ORDER BY score LIMIT ?`;
|
|
105
|
+
params.push(limit);
|
|
106
|
+
return this.db.prepare(sql).all(...params);
|
|
107
|
+
}
|
|
108
|
+
write(path, scope, scope_id, type, content) {
|
|
109
|
+
const fingerprint = `${content.length}-${Date.now()}`;
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
this.db.prepare(`
|
|
112
|
+
INSERT OR REPLACE INTO memory_files (path, scope, scope_id, type, fingerprint, last_indexed_at)
|
|
113
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
114
|
+
`).run(path, scope, scope_id, type, fingerprint, now);
|
|
115
|
+
this.db.prepare(`DELETE FROM memory_fts WHERE path = ?`).run(path);
|
|
116
|
+
this.db.prepare(`INSERT INTO memory_fts (path, body) VALUES (?, ?)`).run(path, content);
|
|
117
|
+
}
|
|
118
|
+
forget(memoryId) {
|
|
119
|
+
this.db.prepare("DELETE FROM memory_files WHERE path = ?").run(memoryId);
|
|
120
|
+
this.db.prepare("DELETE FROM memory_fts WHERE path = ?").run(memoryId);
|
|
121
|
+
}
|
|
122
|
+
close() {
|
|
123
|
+
this.db.close();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/reconcile.ts
|
|
128
|
+
var import_fs = require("fs");
|
|
129
|
+
var import_path = require("path");
|
|
130
|
+
function parseScopeAndId(memoryRoot, filePath) {
|
|
131
|
+
const rel = (0, import_path.relative)(memoryRoot, filePath).split(import_path.sep);
|
|
132
|
+
if (rel[0] === "global") return { scope: "global", scope_id: "" };
|
|
133
|
+
if (rel[0] === "projects" && rel.length >= 3) return { scope: "projects", scope_id: rel[1] };
|
|
134
|
+
if (rel[0] === "sessions" && rel.length >= 3) return { scope: "sessions", scope_id: rel[1] };
|
|
135
|
+
return { scope: "unknown", scope_id: "" };
|
|
136
|
+
}
|
|
137
|
+
function walkMdFiles(dir) {
|
|
138
|
+
const results = [];
|
|
139
|
+
for (const entry of (0, import_fs.readdirSync)(dir, { withFileTypes: true })) {
|
|
140
|
+
const full = (0, import_path.join)(dir, entry.name);
|
|
141
|
+
if (entry.isDirectory()) {
|
|
142
|
+
results.push(...walkMdFiles(full));
|
|
143
|
+
} else if (entry.name.endsWith(".md")) {
|
|
144
|
+
results.push(full);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return results;
|
|
148
|
+
}
|
|
149
|
+
function reconcileMemoryDir(store, memoryRoot) {
|
|
150
|
+
const filesOnDisk = walkMdFiles(memoryRoot);
|
|
151
|
+
const diskPaths = /* @__PURE__ */ new Set();
|
|
152
|
+
for (const file of filesOnDisk) {
|
|
153
|
+
diskPaths.add(file);
|
|
154
|
+
const content = (0, import_fs.readFileSync)(file, "utf-8");
|
|
155
|
+
const { scope, scope_id } = parseScopeAndId(memoryRoot, file);
|
|
156
|
+
store.write(file, scope, scope_id, "snapshot", content);
|
|
157
|
+
}
|
|
158
|
+
const indexed = store.db.prepare("SELECT path FROM memory_files").all();
|
|
159
|
+
for (const row of indexed) {
|
|
160
|
+
if (!diskPaths.has(row.path)) {
|
|
161
|
+
store.forget(row.path);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/tool.ts
|
|
167
|
+
var import_fs2 = require("fs");
|
|
168
|
+
function createMemoryTool(ctx) {
|
|
169
|
+
let store = null;
|
|
170
|
+
function ensureStore() {
|
|
171
|
+
if (!store) {
|
|
172
|
+
(0, import_fs2.mkdirSync)(ctx.memoryDir, { recursive: true });
|
|
173
|
+
store = new MemoryStore(ctx.dbPath);
|
|
174
|
+
reconcileMemoryDir(store, ctx.memoryDir);
|
|
175
|
+
}
|
|
176
|
+
return store;
|
|
177
|
+
}
|
|
178
|
+
return (0, import_plugin.tool)({
|
|
179
|
+
description: "Search or write to the persistent memory store. Search returns ranked results across memory files. Write saves content to a memory file.",
|
|
180
|
+
args: {
|
|
181
|
+
operation: import_zod.z.enum(["search", "write", "list", "forget"]),
|
|
182
|
+
query: import_zod.z.string().optional(),
|
|
183
|
+
scope: import_zod.z.string().optional(),
|
|
184
|
+
scope_id: import_zod.z.string().optional(),
|
|
185
|
+
type: import_zod.z.string().optional(),
|
|
186
|
+
limit: import_zod.z.number().optional(),
|
|
187
|
+
path: import_zod.z.string().optional(),
|
|
188
|
+
content: import_zod.z.string().optional(),
|
|
189
|
+
memory_id: import_zod.z.string().optional()
|
|
190
|
+
},
|
|
191
|
+
execute(args) {
|
|
192
|
+
const s = ensureStore();
|
|
193
|
+
if (args.operation === "search") {
|
|
194
|
+
if (!args.query) return "query is required for search";
|
|
195
|
+
const results = s.search(args.query, {
|
|
196
|
+
scope: args.scope,
|
|
197
|
+
scope_id: args.scope_id,
|
|
198
|
+
type: args.type,
|
|
199
|
+
limit: args.limit
|
|
200
|
+
});
|
|
201
|
+
if (results.length === 0) return "No results found";
|
|
202
|
+
return JSON.stringify(results, null, 2);
|
|
203
|
+
}
|
|
204
|
+
if (args.operation === "write") {
|
|
205
|
+
if (!args.path) return "path is required for write";
|
|
206
|
+
if (!args.content) return "content is required for write";
|
|
207
|
+
s.write(
|
|
208
|
+
args.path,
|
|
209
|
+
args.scope ?? "unknown",
|
|
210
|
+
args.scope_id ?? "",
|
|
211
|
+
args.type ?? "snapshot",
|
|
212
|
+
args.content
|
|
213
|
+
);
|
|
214
|
+
return `Wrote to ${args.path}`;
|
|
215
|
+
}
|
|
216
|
+
if (args.operation === "list") {
|
|
217
|
+
const results = s.search("*", {
|
|
218
|
+
scope: args.scope,
|
|
219
|
+
scope_id: args.scope_id,
|
|
220
|
+
type: args.type,
|
|
221
|
+
limit: args.limit ?? 50
|
|
222
|
+
});
|
|
223
|
+
return JSON.stringify(results, null, 2);
|
|
224
|
+
}
|
|
225
|
+
if (args.operation === "forget") {
|
|
226
|
+
if (!args.memory_id) return "memory_id is required for forget";
|
|
227
|
+
s.forget(args.memory_id);
|
|
228
|
+
return `Forgot ${args.memory_id}`;
|
|
229
|
+
}
|
|
230
|
+
return "Invalid operation";
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/config.ts
|
|
236
|
+
var import_fs3 = require("fs");
|
|
237
|
+
var import_path2 = require("path");
|
|
238
|
+
var DEFAULT_CONFIG = {
|
|
239
|
+
memory: {
|
|
240
|
+
injection: {
|
|
241
|
+
total_budget: 2e3,
|
|
242
|
+
allocation: {
|
|
243
|
+
checkpoint: 0.3,
|
|
244
|
+
memory: 0.25,
|
|
245
|
+
notes: 0.2,
|
|
246
|
+
progress: 0.15,
|
|
247
|
+
buffer: 0.1
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
checkpoint: {
|
|
251
|
+
thresholds: ["40%", "60%", "80%"],
|
|
252
|
+
drain_timeout_ms: 12e4,
|
|
253
|
+
idle_timeout_ms: 3e5
|
|
254
|
+
},
|
|
255
|
+
dream: {
|
|
256
|
+
auto: true,
|
|
257
|
+
interval_days: 7
|
|
258
|
+
},
|
|
259
|
+
distill: {
|
|
260
|
+
auto: true,
|
|
261
|
+
interval_days: 30
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
function getConfigPath(projectRoot) {
|
|
266
|
+
return (0, import_path2.join)(projectRoot, ".super-pocock", "config.json");
|
|
267
|
+
}
|
|
268
|
+
function loadConfig(projectRoot) {
|
|
269
|
+
const configPath = getConfigPath(projectRoot);
|
|
270
|
+
if ((0, import_fs3.existsSync)(configPath)) {
|
|
271
|
+
const content = (0, import_fs3.readFileSync)(configPath, "utf-8");
|
|
272
|
+
return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
|
|
273
|
+
}
|
|
274
|
+
return DEFAULT_CONFIG;
|
|
275
|
+
}
|
|
276
|
+
function ensureConfig(projectRoot) {
|
|
277
|
+
const configPath = getConfigPath(projectRoot);
|
|
278
|
+
if (!(0, import_fs3.existsSync)(configPath)) {
|
|
279
|
+
(0, import_fs3.mkdirSync)((0, import_path2.dirname)(configPath), { recursive: true });
|
|
280
|
+
(0, import_fs3.writeFileSync)(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/paths.ts
|
|
285
|
+
var import_crypto = require("crypto");
|
|
286
|
+
var import_path3 = require("path");
|
|
287
|
+
function resolveProjectId(absRepoPath) {
|
|
288
|
+
return (0, import_crypto.createHash)("sha256").update(absRepoPath).digest("hex").slice(0, 12);
|
|
289
|
+
}
|
|
290
|
+
function getMemoryRoot(projectRoot) {
|
|
291
|
+
return (0, import_path3.join)(projectRoot, ".super-pocock", "memory");
|
|
292
|
+
}
|
|
293
|
+
function getProjectMemoryDir(projectRoot) {
|
|
294
|
+
const projectId = resolveProjectId(projectRoot);
|
|
295
|
+
return (0, import_path3.join)(getMemoryRoot(projectRoot), "projects", projectId);
|
|
296
|
+
}
|
|
297
|
+
function getGlobalMemoryDir(projectRoot) {
|
|
298
|
+
return (0, import_path3.join)(getMemoryRoot(projectRoot), "global");
|
|
299
|
+
}
|
|
300
|
+
function getDbPath(projectRoot) {
|
|
301
|
+
return (0, import_path3.join)(getMemoryRoot(projectRoot), "memory.db");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/checkpoint-plugin.ts
|
|
305
|
+
var import_fs4 = require("fs");
|
|
306
|
+
var CheckpointPlugin = async ({ client, directory }) => {
|
|
307
|
+
const config = loadConfig(directory);
|
|
308
|
+
const memoryDir = getProjectMemoryDir(directory);
|
|
309
|
+
(0, import_fs4.mkdirSync)(memoryDir, { recursive: true });
|
|
310
|
+
let lastCheckpointRatio = 0;
|
|
311
|
+
const THRESHOLDS = config.memory.checkpoint.thresholds.map((t) => parseFloat(t) / 100);
|
|
312
|
+
return {
|
|
313
|
+
event: async ({ event }) => {
|
|
314
|
+
if (event.type === "session.next.step.ended") {
|
|
315
|
+
const ratio = event.properties.tokens / event.properties.contextLimit;
|
|
316
|
+
const nextThreshold = THRESHOLDS.find((t) => t > lastCheckpointRatio);
|
|
317
|
+
if (nextThreshold && ratio >= nextThreshold) {
|
|
318
|
+
lastCheckpointRatio = ratio;
|
|
319
|
+
await triggerCheckpoint(client, event.properties.sessionID, directory);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (event.type === "session.status" && event.properties?.status?.type === "idle") {
|
|
323
|
+
setTimeout(async () => {
|
|
324
|
+
await triggerCheckpoint(client, event.properties.sessionID, directory);
|
|
325
|
+
}, config.memory.checkpoint.idle_timeout_ms);
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
"experimental.session.compacting": async (input, output) => {
|
|
329
|
+
output.context.push(`
|
|
330
|
+
## Checkpoint Instructions
|
|
331
|
+
Extract and preserve these 11 fields:
|
|
332
|
+
1. Intent (\u7528\u6237\u7684\u6838\u5FC3\u76EE\u6807)
|
|
333
|
+
2. Actions (\u5DF2\u6267\u884C\u7684\u64CD\u4F5C)
|
|
334
|
+
3. Task Tree (\u5B50\u4EFB\u52A1\u5C42\u7EA7)
|
|
335
|
+
4. Errors & Workarounds (\u9047\u5230\u7684\u95EE\u9898\u53CA\u89E3\u51B3)
|
|
336
|
+
5. Design Decisions (\u5173\u952E\u6280\u672F\u9009\u62E9)
|
|
337
|
+
6. Constraints (\u9650\u5236\u6761\u4EF6)
|
|
338
|
+
7. Current State (\u4EFB\u52A1\u8FDB\u5EA6)
|
|
339
|
+
8. TODOs (\u5269\u4F59\u5DE5\u4F5C)
|
|
340
|
+
9. Code Changes Summary (Git diff \u6982\u8981)
|
|
341
|
+
10. Key File Paths (\u6D89\u53CA\u7684\u6838\u5FC3\u6587\u4EF6)
|
|
342
|
+
11. Timestamp (\u68C0\u67E5\u70B9\u521B\u5EFA\u65F6\u95F4)
|
|
343
|
+
`);
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
async function triggerCheckpoint(client, sessionID, directory) {
|
|
348
|
+
const messages = await client.session.messages({ path: { id: sessionID } });
|
|
349
|
+
const context = constructCheckpointContext(messages);
|
|
350
|
+
await client.session.prompt({
|
|
351
|
+
path: { id: sessionID },
|
|
352
|
+
body: {
|
|
353
|
+
agent: "checkpoint-writer",
|
|
354
|
+
parts: [{
|
|
355
|
+
type: "text",
|
|
356
|
+
text: `Extract checkpoint from this context:
|
|
357
|
+
${context}`
|
|
358
|
+
}]
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
function constructCheckpointContext(messages) {
|
|
363
|
+
return messages.map((m) => `[${m.role}]: ${m.content}`).join("\n");
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// src/injection-plugin.ts
|
|
367
|
+
var import_fs5 = require("fs");
|
|
368
|
+
var import_path4 = require("path");
|
|
369
|
+
var MemoryInjectionPlugin = async ({ directory }) => {
|
|
370
|
+
const config = loadConfig(directory);
|
|
371
|
+
const memoryCache = /* @__PURE__ */ new Map();
|
|
372
|
+
function loadMemoryFiles() {
|
|
373
|
+
const projectDir = getProjectMemoryDir(directory);
|
|
374
|
+
const globalDir = getGlobalMemoryDir(directory);
|
|
375
|
+
const memories = {};
|
|
376
|
+
const files = [
|
|
377
|
+
{ key: "checkpoint", path: (0, import_path4.join)(projectDir, "checkpoint.md") },
|
|
378
|
+
{ key: "memory", path: (0, import_path4.join)(projectDir, "MEMORY.md") },
|
|
379
|
+
{ key: "notes", path: (0, import_path4.join)(projectDir, "notes.md") },
|
|
380
|
+
{ key: "progress", path: (0, import_path4.join)(projectDir, "tasks", "progress.md") },
|
|
381
|
+
{ key: "global_memory", path: (0, import_path4.join)(globalDir, "MEMORY.md") }
|
|
382
|
+
];
|
|
383
|
+
for (const file of files) {
|
|
384
|
+
if ((0, import_fs5.existsSync)(file.path)) {
|
|
385
|
+
memories[file.key] = (0, import_fs5.readFileSync)(file.path, "utf-8");
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return memories;
|
|
389
|
+
}
|
|
390
|
+
function estimateTokens(text) {
|
|
391
|
+
return Math.ceil(text.length / 4);
|
|
392
|
+
}
|
|
393
|
+
function smartTruncate(text, maxTokens) {
|
|
394
|
+
const estimatedTokens = estimateTokens(text);
|
|
395
|
+
if (estimatedTokens <= maxTokens) return text;
|
|
396
|
+
const keepChars = maxTokens * 4 * 0.3;
|
|
397
|
+
const start = text.substring(0, keepChars);
|
|
398
|
+
const end = text.substring(text.length - keepChars);
|
|
399
|
+
return `${start}
|
|
400
|
+
|
|
401
|
+
... [\u8BB0\u5FC6\u5DF2\u538B\u7F29\uFF0C\u539F ${estimatedTokens} tokens] ...
|
|
402
|
+
|
|
403
|
+
${end}`;
|
|
404
|
+
}
|
|
405
|
+
function formatMemory(memories, budget) {
|
|
406
|
+
const sections = [];
|
|
407
|
+
if (memories.checkpoint && budget.checkpoint > 0) {
|
|
408
|
+
sections.push(`## \u68C0\u67E5\u70B9\u72B6\u6001
|
|
409
|
+
${smartTruncate(memories.checkpoint, budget.checkpoint)}`);
|
|
410
|
+
}
|
|
411
|
+
if (memories.memory && budget.memory > 0) {
|
|
412
|
+
sections.push(`## \u9879\u76EE\u8BB0\u5FC6
|
|
413
|
+
${smartTruncate(memories.memory, budget.memory)}`);
|
|
414
|
+
}
|
|
415
|
+
if (memories.global_memory && budget.memory > 0) {
|
|
416
|
+
sections.push(`## \u5168\u5C40\u8BB0\u5FC6
|
|
417
|
+
${smartTruncate(memories.global_memory, budget.memory * 0.5)}`);
|
|
418
|
+
}
|
|
419
|
+
if (memories.notes && budget.notes > 0) {
|
|
420
|
+
sections.push(`## \u5F53\u524D\u7B14\u8BB0
|
|
421
|
+
${smartTruncate(memories.notes, budget.notes)}`);
|
|
422
|
+
}
|
|
423
|
+
if (memories.progress && budget.progress > 0) {
|
|
424
|
+
sections.push(`## \u4EFB\u52A1\u8FDB\u5EA6
|
|
425
|
+
${smartTruncate(memories.progress, budget.progress)}`);
|
|
426
|
+
}
|
|
427
|
+
return sections.join("\n\n");
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
event: async ({ event }) => {
|
|
431
|
+
if (event.type === "session.created") {
|
|
432
|
+
const memories = loadMemoryFiles();
|
|
433
|
+
memoryCache.set(event.properties.sessionID, memories);
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
437
|
+
const memories = memoryCache.get(input.sessionID);
|
|
438
|
+
if (!memories) return;
|
|
439
|
+
const totalBudget = config.memory.injection.total_budget;
|
|
440
|
+
const allocation = config.memory.injection.allocation;
|
|
441
|
+
const budget = {
|
|
442
|
+
checkpoint: Math.floor(totalBudget * allocation.checkpoint),
|
|
443
|
+
memory: Math.floor(totalBudget * allocation.memory),
|
|
444
|
+
notes: Math.floor(totalBudget * allocation.notes),
|
|
445
|
+
progress: Math.floor(totalBudget * allocation.progress)
|
|
446
|
+
};
|
|
447
|
+
const memoryContext = formatMemory(memories, budget);
|
|
448
|
+
if (memoryContext) {
|
|
449
|
+
output.system.push(`
|
|
450
|
+
|
|
451
|
+
# Injected Memory Context
|
|
452
|
+
${memoryContext}`);
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
"experimental.session.compacting": async (input, output) => {
|
|
456
|
+
const memories = memoryCache.get(input.sessionID);
|
|
457
|
+
if (!memories) return;
|
|
458
|
+
const criticalMemory = [];
|
|
459
|
+
if (memories.checkpoint) {
|
|
460
|
+
criticalMemory.push(`\u5F53\u524D\u68C0\u67E5\u70B9: ${memories.checkpoint.substring(0, 500)}`);
|
|
461
|
+
}
|
|
462
|
+
if (memories.progress) {
|
|
463
|
+
criticalMemory.push(`\u4EFB\u52A1\u8FDB\u5EA6\u6458\u8981: ${memories.progress.substring(0, 300)}`);
|
|
464
|
+
}
|
|
465
|
+
output.context.push(`## \u4FDD\u7559\u7684\u5173\u952E\u8BB0\u5FC6
|
|
466
|
+
${criticalMemory.join("\n\n")}`);
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
// src/dream-plugin.ts
|
|
472
|
+
var import_fs6 = require("fs");
|
|
473
|
+
var import_path5 = require("path");
|
|
474
|
+
var DreamPlugin = async ({ client, directory }) => {
|
|
475
|
+
const config = loadConfig(directory);
|
|
476
|
+
const memoryDir = getProjectMemoryDir(directory);
|
|
477
|
+
(0, import_fs6.mkdirSync)(memoryDir, { recursive: true });
|
|
478
|
+
function getLastTimestamp(metaFile) {
|
|
479
|
+
const metaPath = (0, import_path5.join)(memoryDir, metaFile);
|
|
480
|
+
if ((0, import_fs6.existsSync)(metaPath)) {
|
|
481
|
+
const meta = JSON.parse((0, import_fs6.readFileSync)(metaPath, "utf-8"));
|
|
482
|
+
return meta.timestamp ?? 0;
|
|
483
|
+
}
|
|
484
|
+
return 0;
|
|
485
|
+
}
|
|
486
|
+
function setLastTimestamp(metaFile) {
|
|
487
|
+
const metaPath = (0, import_path5.join)(memoryDir, metaFile);
|
|
488
|
+
(0, import_fs6.writeFileSync)(metaPath, JSON.stringify({ timestamp: Date.now() }));
|
|
489
|
+
}
|
|
490
|
+
function shouldAutoRun(metaFile, intervalDays) {
|
|
491
|
+
const lastRun = getLastTimestamp(metaFile);
|
|
492
|
+
const intervalMs = intervalDays * 24 * 60 * 60 * 1e3;
|
|
493
|
+
return Date.now() - lastRun > intervalMs;
|
|
494
|
+
}
|
|
495
|
+
return {
|
|
496
|
+
event: async ({ event }) => {
|
|
497
|
+
if (event.type === "session.created") {
|
|
498
|
+
if (config.memory.dream.auto && shouldAutoRun(".dream-meta.json", config.memory.dream.interval_days)) {
|
|
499
|
+
await client.session.prompt({
|
|
500
|
+
path: { id: event.properties.sessionID },
|
|
501
|
+
body: {
|
|
502
|
+
agent: "dream",
|
|
503
|
+
parts: [{
|
|
504
|
+
type: "text",
|
|
505
|
+
text: "Run automatic dream memory consolidation pass."
|
|
506
|
+
}]
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
setLastTimestamp(".dream-meta.json");
|
|
510
|
+
}
|
|
511
|
+
if (config.memory.distill.auto && shouldAutoRun(".distill-meta.json", config.memory.distill.interval_days)) {
|
|
512
|
+
await client.session.prompt({
|
|
513
|
+
path: { id: event.properties.sessionID },
|
|
514
|
+
body: {
|
|
515
|
+
agent: "distill",
|
|
516
|
+
parts: [{
|
|
517
|
+
type: "text",
|
|
518
|
+
text: "Run automatic distill pass."
|
|
519
|
+
}]
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
setLastTimestamp(".distill-meta.json");
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
// src/index.ts
|
|
530
|
+
var MemoryCorePlugin = async (ctx) => {
|
|
531
|
+
ensureConfig(ctx.directory);
|
|
532
|
+
const memoryRoot = getMemoryRoot(ctx.directory);
|
|
533
|
+
const projectDir = getProjectMemoryDir(ctx.directory);
|
|
534
|
+
const globalDir = getGlobalMemoryDir(ctx.directory);
|
|
535
|
+
(0, import_fs7.mkdirSync)((0, import_path6.join)(memoryRoot, "global"), { recursive: true });
|
|
536
|
+
(0, import_fs7.mkdirSync)((0, import_path6.join)(projectDir, "sessions"), { recursive: true });
|
|
537
|
+
(0, import_fs7.mkdirSync)((0, import_path6.join)(projectDir, "tasks"), { recursive: true });
|
|
538
|
+
const memoryTool = createMemoryTool({
|
|
539
|
+
memoryDir: memoryRoot,
|
|
540
|
+
dbPath: getDbPath(ctx.directory)
|
|
541
|
+
});
|
|
542
|
+
const checkpointPlugin = await CheckpointPlugin(ctx);
|
|
543
|
+
const injectionPlugin = await MemoryInjectionPlugin(ctx);
|
|
544
|
+
const dreamPlugin = await DreamPlugin(ctx);
|
|
545
|
+
return {
|
|
546
|
+
tool: {
|
|
547
|
+
memory: memoryTool
|
|
548
|
+
},
|
|
549
|
+
event: async (input) => {
|
|
550
|
+
await checkpointPlugin.event?.(input);
|
|
551
|
+
await injectionPlugin.event?.(input);
|
|
552
|
+
await dreamPlugin.event?.(input);
|
|
553
|
+
},
|
|
554
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
555
|
+
await injectionPlugin["experimental.chat.system.transform"]?.(input, output);
|
|
556
|
+
},
|
|
557
|
+
"experimental.session.compacting": async (input, output) => {
|
|
558
|
+
await checkpointPlugin["experimental.session.compacting"]?.(input, output);
|
|
559
|
+
await injectionPlugin["experimental.session.compacting"]?.(input, output);
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
};
|
|
563
|
+
var index_default = MemoryCorePlugin;
|
|
564
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
565
|
+
0 && (module.exports = {
|
|
566
|
+
DEFAULT_CONFIG,
|
|
567
|
+
MemoryCorePlugin,
|
|
568
|
+
MemoryStore,
|
|
569
|
+
createMemoryTool,
|
|
570
|
+
getGlobalMemoryDir,
|
|
571
|
+
getMemoryRoot,
|
|
572
|
+
getProjectMemoryDir,
|
|
573
|
+
loadConfig,
|
|
574
|
+
resolveProjectId
|
|
575
|
+
});
|
|
576
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/tool.ts","../src/store.ts","../src/reconcile.ts","../src/config.ts","../src/paths.ts","../src/checkpoint-plugin.ts","../src/injection-plugin.ts","../src/dream-plugin.ts"],"sourcesContent":["import type { Plugin } from \"@opencode-ai/plugin\"\nimport { mkdirSync } from \"fs\"\nimport { join } from \"path\"\nimport { createMemoryTool } from \"./tool\"\nimport { CheckpointPlugin } from \"./checkpoint-plugin\"\nimport { MemoryInjectionPlugin } from \"./injection-plugin\"\nimport { DreamPlugin } from \"./dream-plugin\"\nimport { getMemoryRoot, getProjectMemoryDir, getGlobalMemoryDir, getDbPath } from \"./paths\"\nimport { ensureConfig } from \"./config\"\n\nexport const MemoryCorePlugin: Plugin = async (ctx) => {\n // 确保配置文件存在\n ensureConfig(ctx.directory)\n\n const memoryRoot = getMemoryRoot(ctx.directory)\n const projectDir = getProjectMemoryDir(ctx.directory)\n const globalDir = getGlobalMemoryDir(ctx.directory)\n\n // 创建目录结构\n mkdirSync(join(memoryRoot, \"global\"), { recursive: true })\n mkdirSync(join(projectDir, \"sessions\"), { recursive: true })\n mkdirSync(join(projectDir, \"tasks\"), { recursive: true })\n\n // 创建 memory 工具\n const memoryTool = createMemoryTool({\n memoryDir: memoryRoot,\n dbPath: getDbPath(ctx.directory),\n })\n\n // 初始化子插件\n const checkpointPlugin = await CheckpointPlugin(ctx)\n const injectionPlugin = await MemoryInjectionPlugin(ctx)\n const dreamPlugin = await DreamPlugin(ctx)\n\n return {\n tool: {\n memory: memoryTool,\n },\n event: async (input) => {\n await checkpointPlugin.event?.(input)\n await injectionPlugin.event?.(input)\n await dreamPlugin.event?.(input)\n },\n \"experimental.chat.system.transform\": async (input, output) => {\n await injectionPlugin[\"experimental.chat.system.transform\"]?.(input, output)\n },\n \"experimental.session.compacting\": async (input, output) => {\n await checkpointPlugin[\"experimental.session.compacting\"]?.(input, output)\n await injectionPlugin[\"experimental.session.compacting\"]?.(input, output)\n },\n }\n}\n\nexport default MemoryCorePlugin\n\n// 导出子模块\nexport { MemoryStore } from \"./store\"\nexport { createMemoryTool } from \"./tool\"\nexport { resolveProjectId, getMemoryRoot, getProjectMemoryDir, getGlobalMemoryDir } from \"./paths\"\nexport { loadConfig, DEFAULT_CONFIG } from \"./config\"\nexport type { MemoryConfig } from \"./config\"\nexport type { SearchResult, SearchFilters } from \"./store\"\n","import { tool } from \"@opencode-ai/plugin\"\nimport { z } from \"zod\"\nimport { MemoryStore } from \"./store\"\nimport { reconcileMemoryDir } from \"./reconcile\"\nimport { mkdirSync } from \"fs\"\n\nexport function createMemoryTool(ctx: { memoryDir: string; dbPath: string }) {\n let store: MemoryStore | null = null\n\n function ensureStore() {\n if (!store) {\n mkdirSync(ctx.memoryDir, { recursive: true })\n store = new MemoryStore(ctx.dbPath)\n reconcileMemoryDir(store, ctx.memoryDir)\n }\n return store\n }\n\n return tool({\n description: \"Search or write to the persistent memory store. Search returns ranked results across memory files. Write saves content to a memory file.\",\n args: {\n operation: z.enum([\"search\", \"write\", \"list\", \"forget\"]),\n query: z.string().optional(),\n scope: z.string().optional(),\n scope_id: z.string().optional(),\n type: z.string().optional(),\n limit: z.number().optional(),\n path: z.string().optional(),\n content: z.string().optional(),\n memory_id: z.string().optional(),\n },\n execute(args: any) {\n const s = ensureStore()\n\n if (args.operation === \"search\") {\n if (!args.query) return \"query is required for search\"\n const results = s.search(args.query, {\n scope: args.scope,\n scope_id: args.scope_id,\n type: args.type,\n limit: args.limit,\n })\n if (results.length === 0) return \"No results found\"\n return JSON.stringify(results, null, 2)\n }\n\n if (args.operation === \"write\") {\n if (!args.path) return \"path is required for write\"\n if (!args.content) return \"content is required for write\"\n s.write(\n args.path,\n args.scope ?? \"unknown\",\n args.scope_id ?? \"\",\n args.type ?? \"snapshot\",\n args.content,\n )\n return `Wrote to ${args.path}`\n }\n\n if (args.operation === \"list\") {\n const results = s.search(\"*\", {\n scope: args.scope,\n scope_id: args.scope_id,\n type: args.type,\n limit: args.limit ?? 50,\n })\n return JSON.stringify(results, null, 2)\n }\n\n if (args.operation === \"forget\") {\n if (!args.memory_id) return \"memory_id is required for forget\"\n s.forget(args.memory_id)\n return `Forgot ${args.memory_id}`\n }\n\n return \"Invalid operation\"\n },\n })\n}\n","import Database from \"better-sqlite3\"\n\nexport interface SearchFilters {\n scope?: string\n scope_id?: string\n type?: string\n limit?: number\n}\n\nexport interface SearchResult {\n path: string\n scope: string\n scope_id: string\n type: string\n snippet: string\n score: number\n}\n\nexport class MemoryStore {\n public db: Database.Database\n\n constructor(dbPath: string) {\n this.db = new Database(dbPath)\n this.db.pragma(\"journal_mode = WAL\")\n this.init()\n }\n\n private init() {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS memory_files (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n path TEXT NOT NULL UNIQUE,\n scope TEXT NOT NULL,\n scope_id TEXT NOT NULL DEFAULT '',\n type TEXT NOT NULL,\n fingerprint TEXT NOT NULL,\n last_indexed_at INTEGER NOT NULL\n )\n `)\n this.db.exec(`\n CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts\n USING fts5(path, body, tokenize='unicode61')\n `)\n }\n\n search(query: string, filters: SearchFilters = {}): SearchResult[] {\n const tokens = query.split(/\\s+/).filter(Boolean).map(t => `\"${t}\"`)\n if (tokens.length === 0) return []\n const ftsQuery = tokens.join(\" OR \")\n const limit = filters.limit ?? 10\n\n let sql = `\n SELECT f.path, m.scope, m.scope_id, m.type,\n snippet(memory_fts, 1, '<<', '>>', '...', 32) AS snippet,\n bm25(memory_fts) AS score\n FROM memory_fts f\n JOIN memory_files m ON m.path = f.path\n WHERE memory_fts MATCH ?\n `\n const params: any[] = [ftsQuery]\n if (filters.scope) { sql += ` AND m.scope = ?`; params.push(filters.scope) }\n if (filters.scope_id) { sql += ` AND m.scope_id = ?`; params.push(filters.scope_id) }\n if (filters.type) { sql += ` AND m.type = ?`; params.push(filters.type) }\n sql += ` ORDER BY score LIMIT ?`\n params.push(limit)\n return this.db.prepare(sql).all(...params) as SearchResult[]\n }\n\n write(path: string, scope: string, scope_id: string, type: string, content: string) {\n const fingerprint = `${content.length}-${Date.now()}`\n const now = Date.now()\n\n this.db.prepare(`\n INSERT OR REPLACE INTO memory_files (path, scope, scope_id, type, fingerprint, last_indexed_at)\n VALUES (?, ?, ?, ?, ?, ?)\n `).run(path, scope, scope_id, type, fingerprint, now)\n\n this.db.prepare(`DELETE FROM memory_fts WHERE path = ?`).run(path)\n this.db.prepare(`INSERT INTO memory_fts (path, body) VALUES (?, ?)`).run(path, content)\n }\n\n forget(memoryId: string) {\n this.db.prepare(\"DELETE FROM memory_files WHERE path = ?\").run(memoryId)\n this.db.prepare(\"DELETE FROM memory_fts WHERE path = ?\").run(memoryId)\n }\n\n close() {\n this.db.close()\n }\n}\n","import { readdirSync, readFileSync } from \"fs\"\nimport { join, relative, sep } from \"path\"\nimport type { MemoryStore } from \"./store\"\n\nfunction parseScopeAndId(memoryRoot: string, filePath: string): { scope: string; scope_id: string } {\n const rel = relative(memoryRoot, filePath).split(sep)\n if (rel[0] === \"global\") return { scope: \"global\", scope_id: \"\" }\n if (rel[0] === \"projects\" && rel.length >= 3) return { scope: \"projects\", scope_id: rel[1] }\n if (rel[0] === \"sessions\" && rel.length >= 3) return { scope: \"sessions\", scope_id: rel[1] }\n return { scope: \"unknown\", scope_id: \"\" }\n}\n\nfunction walkMdFiles(dir: string): string[] {\n const results: string[] = []\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) {\n results.push(...walkMdFiles(full))\n } else if (entry.name.endsWith(\".md\")) {\n results.push(full)\n }\n }\n return results\n}\n\nexport function reconcileMemoryDir(store: MemoryStore, memoryRoot: string) {\n const filesOnDisk = walkMdFiles(memoryRoot)\n const diskPaths = new Set<string>()\n\n for (const file of filesOnDisk) {\n diskPaths.add(file)\n const content = readFileSync(file, \"utf-8\")\n const { scope, scope_id } = parseScopeAndId(memoryRoot, file)\n store.write(file, scope, scope_id, \"snapshot\", content)\n }\n\n const indexed = store.db.prepare(\"SELECT path FROM memory_files\").all() as { path: string }[]\n for (const row of indexed) {\n if (!diskPaths.has(row.path)) {\n store.forget(row.path)\n }\n }\n}\n","import { readFileSync, existsSync, writeFileSync, mkdirSync } from \"fs\"\nimport { join, dirname } from \"path\"\n\nexport interface MemoryConfig {\n memory: {\n injection: {\n total_budget: number\n allocation: {\n checkpoint: number\n memory: number\n notes: number\n progress: number\n buffer: number\n }\n }\n checkpoint: {\n thresholds: string[]\n drain_timeout_ms: number\n idle_timeout_ms: number\n }\n dream: {\n auto: boolean\n interval_days: number\n }\n distill: {\n auto: boolean\n interval_days: number\n }\n }\n}\n\nexport const DEFAULT_CONFIG: MemoryConfig = {\n memory: {\n injection: {\n total_budget: 2000,\n allocation: {\n checkpoint: 0.30,\n memory: 0.25,\n notes: 0.20,\n progress: 0.15,\n buffer: 0.10\n }\n },\n checkpoint: {\n thresholds: [\"40%\", \"60%\", \"80%\"],\n drain_timeout_ms: 120000,\n idle_timeout_ms: 300000\n },\n dream: {\n auto: true,\n interval_days: 7\n },\n distill: {\n auto: true,\n interval_days: 30\n }\n }\n}\n\nexport function getConfigPath(projectRoot: string): string {\n return join(projectRoot, \".super-pocock\", \"config.json\")\n}\n\nexport function loadConfig(projectRoot: string): MemoryConfig {\n const configPath = getConfigPath(projectRoot)\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, \"utf-8\")\n return { ...DEFAULT_CONFIG, ...JSON.parse(content) }\n }\n return DEFAULT_CONFIG\n}\n\nexport function ensureConfig(projectRoot: string): void {\n const configPath = getConfigPath(projectRoot)\n if (!existsSync(configPath)) {\n mkdirSync(dirname(configPath), { recursive: true })\n writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2))\n }\n}\n","import { createHash } from \"crypto\"\nimport { join } from \"path\"\n\nexport type Scope = \"global\" | \"projects\" | \"sessions\"\n\nexport function resolveProjectId(absRepoPath: string): string {\n return createHash(\"sha256\").update(absRepoPath).digest(\"hex\").slice(0, 12)\n}\n\nexport function getMemoryRoot(projectRoot: string): string {\n return join(projectRoot, \".super-pocock\", \"memory\")\n}\n\nexport function getProjectMemoryDir(projectRoot: string): string {\n const projectId = resolveProjectId(projectRoot)\n return join(getMemoryRoot(projectRoot), \"projects\", projectId)\n}\n\nexport function getGlobalMemoryDir(projectRoot: string): string {\n return join(getMemoryRoot(projectRoot), \"global\")\n}\n\nexport function getSessionMemoryDir(projectRoot: string, sessionId: string): string {\n return join(getProjectMemoryDir(projectRoot), \"sessions\", sessionId)\n}\n\nexport function getDbPath(projectRoot: string): string {\n return join(getMemoryRoot(projectRoot), \"memory.db\")\n}\n","import type { Plugin } from \"@opencode-ai/plugin\"\nimport { loadConfig } from \"./config\"\nimport { getProjectMemoryDir } from \"./paths\"\nimport { mkdirSync } from \"fs\"\nimport { join } from \"path\"\n\nexport const CheckpointPlugin: Plugin = async ({ client, directory }) => {\n const config = loadConfig(directory)\n const memoryDir = getProjectMemoryDir(directory)\n mkdirSync(memoryDir, { recursive: true })\n\n let lastCheckpointRatio = 0\n const THRESHOLDS = config.memory.checkpoint.thresholds.map(t => parseFloat(t) / 100)\n\n return {\n event: async ({ event }) => {\n // 触发 1: Token 比例阈值\n if (event.type === \"session.next.step.ended\") {\n const ratio = event.properties.tokens / event.properties.contextLimit\n const nextThreshold = THRESHOLDS.find(t => t > lastCheckpointRatio)\n if (nextThreshold && ratio >= nextThreshold) {\n lastCheckpointRatio = ratio\n await triggerCheckpoint(client, event.properties.sessionID, directory)\n }\n }\n\n // 触发 2: 会话结束(idle 超时)\n if (event.type === \"session.status\" && event.properties?.status?.type === \"idle\") {\n setTimeout(async () => {\n await triggerCheckpoint(client, event.properties.sessionID, directory)\n }, config.memory.checkpoint.idle_timeout_ms)\n }\n },\n\n \"experimental.session.compacting\": async (input, output) => {\n output.context.push(`\n## Checkpoint Instructions\nExtract and preserve these 11 fields:\n1. Intent (用户的核心目标)\n2. Actions (已执行的操作)\n3. Task Tree (子任务层级)\n4. Errors & Workarounds (遇到的问题及解决)\n5. Design Decisions (关键技术选择)\n6. Constraints (限制条件)\n7. Current State (任务进度)\n8. TODOs (剩余工作)\n9. Code Changes Summary (Git diff 概要)\n10. Key File Paths (涉及的核心文件)\n11. Timestamp (检查点创建时间)\n `)\n },\n }\n}\n\nasync function triggerCheckpoint(client: any, sessionID: string, directory: string) {\n const messages = await client.session.messages({ path: { id: sessionID } })\n const context = constructCheckpointContext(messages)\n\n await client.session.prompt({\n path: { id: sessionID },\n body: {\n agent: \"checkpoint-writer\",\n parts: [{\n type: \"text\",\n text: `Extract checkpoint from this context:\\n${context}`\n }],\n },\n })\n}\n\nfunction constructCheckpointContext(messages: any[]): string {\n return messages.map(m => `[${m.role}]: ${m.content}`).join(\"\\n\")\n}\n","import type { Plugin } from \"@opencode-ai/plugin\"\nimport { loadConfig } from \"./config\"\nimport { getProjectMemoryDir, getGlobalMemoryDir } from \"./paths\"\nimport { existsSync, readFileSync } from \"fs\"\nimport { join } from \"path\"\n\nexport const MemoryInjectionPlugin: Plugin = async ({ directory }) => {\n const config = loadConfig(directory)\n const memoryCache = new Map<string, Record<string, string>>()\n\n function loadMemoryFiles() {\n const projectDir = getProjectMemoryDir(directory)\n const globalDir = getGlobalMemoryDir(directory)\n\n const memories: Record<string, string> = {}\n\n const files = [\n { key: \"checkpoint\", path: join(projectDir, \"checkpoint.md\") },\n { key: \"memory\", path: join(projectDir, \"MEMORY.md\") },\n { key: \"notes\", path: join(projectDir, \"notes.md\") },\n { key: \"progress\", path: join(projectDir, \"tasks\", \"progress.md\") },\n { key: \"global_memory\", path: join(globalDir, \"MEMORY.md\") },\n ]\n\n for (const file of files) {\n if (existsSync(file.path)) {\n memories[file.key] = readFileSync(file.path, \"utf-8\")\n }\n }\n\n return memories\n }\n\n function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4)\n }\n\n function smartTruncate(text: string, maxTokens: number): string {\n const estimatedTokens = estimateTokens(text)\n if (estimatedTokens <= maxTokens) return text\n\n const keepChars = maxTokens * 4 * 0.3\n const start = text.substring(0, keepChars)\n const end = text.substring(text.length - keepChars)\n\n return `${start}\\n\\n... [记忆已压缩,原 ${estimatedTokens} tokens] ...\\n\\n${end}`\n }\n\n function formatMemory(memories: Record<string, string>, budget: Record<string, number>): string {\n const sections = []\n\n if (memories.checkpoint && budget.checkpoint > 0) {\n sections.push(`## 检查点状态\\n${smartTruncate(memories.checkpoint, budget.checkpoint)}`)\n }\n if (memories.memory && budget.memory > 0) {\n sections.push(`## 项目记忆\\n${smartTruncate(memories.memory, budget.memory)}`)\n }\n if (memories.global_memory && budget.memory > 0) {\n sections.push(`## 全局记忆\\n${smartTruncate(memories.global_memory, budget.memory * 0.5)}`)\n }\n if (memories.notes && budget.notes > 0) {\n sections.push(`## 当前笔记\\n${smartTruncate(memories.notes, budget.notes)}`)\n }\n if (memories.progress && budget.progress > 0) {\n sections.push(`## 任务进度\\n${smartTruncate(memories.progress, budget.progress)}`)\n }\n\n return sections.join(\"\\n\\n\")\n }\n\n return {\n event: async ({ event }) => {\n if (event.type === \"session.created\") {\n const memories = loadMemoryFiles()\n memoryCache.set(event.properties.sessionID, memories)\n }\n },\n\n \"experimental.chat.system.transform\": async (input, output) => {\n const memories = memoryCache.get(input.sessionID)\n if (!memories) return\n\n const totalBudget = config.memory.injection.total_budget\n const allocation = config.memory.injection.allocation\n\n const budget = {\n checkpoint: Math.floor(totalBudget * allocation.checkpoint),\n memory: Math.floor(totalBudget * allocation.memory),\n notes: Math.floor(totalBudget * allocation.notes),\n progress: Math.floor(totalBudget * allocation.progress),\n }\n\n const memoryContext = formatMemory(memories, budget)\n\n if (memoryContext) {\n output.system.push(`\\n\\n# Injected Memory Context\\n${memoryContext}`)\n }\n },\n\n \"experimental.session.compacting\": async (input, output) => {\n const memories = memoryCache.get(input.sessionID)\n if (!memories) return\n\n const criticalMemory = []\n if (memories.checkpoint) {\n criticalMemory.push(`当前检查点: ${memories.checkpoint.substring(0, 500)}`)\n }\n if (memories.progress) {\n criticalMemory.push(`任务进度摘要: ${memories.progress.substring(0, 300)}`)\n }\n\n output.context.push(`## 保留的关键记忆\\n${criticalMemory.join(\"\\n\\n\")}`)\n },\n }\n}\n","import type { Plugin } from \"@opencode-ai/plugin\"\nimport { loadConfig } from \"./config\"\nimport { getProjectMemoryDir } from \"./paths\"\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"fs\"\nimport { join } from \"path\"\n\nexport const DreamPlugin: Plugin = async ({ client, directory }) => {\n const config = loadConfig(directory)\n const memoryDir = getProjectMemoryDir(directory)\n mkdirSync(memoryDir, { recursive: true })\n\n function getLastTimestamp(metaFile: string): number {\n const metaPath = join(memoryDir, metaFile)\n if (existsSync(metaPath)) {\n const meta = JSON.parse(readFileSync(metaPath, \"utf-8\"))\n return meta.timestamp ?? 0\n }\n return 0\n }\n\n function setLastTimestamp(metaFile: string) {\n const metaPath = join(memoryDir, metaFile)\n writeFileSync(metaPath, JSON.stringify({ timestamp: Date.now() }))\n }\n\n function shouldAutoRun(metaFile: string, intervalDays: number): boolean {\n const lastRun = getLastTimestamp(metaFile)\n const intervalMs = intervalDays * 24 * 60 * 60 * 1000\n return Date.now() - lastRun > intervalMs\n }\n\n return {\n event: async ({ event }) => {\n if (event.type === \"session.created\") {\n // 自动触发 dream\n if (config.memory.dream.auto && shouldAutoRun(\".dream-meta.json\", config.memory.dream.interval_days)) {\n await client.session.prompt({\n path: { id: event.properties.sessionID },\n body: {\n agent: \"dream\",\n parts: [{\n type: \"text\",\n text: \"Run automatic dream memory consolidation pass.\"\n }],\n },\n })\n setLastTimestamp(\".dream-meta.json\")\n }\n\n // 自动触发 distill\n if (config.memory.distill.auto && shouldAutoRun(\".distill-meta.json\", config.memory.distill.interval_days)) {\n await client.session.prompt({\n path: { id: event.properties.sessionID },\n body: {\n agent: \"distill\",\n parts: [{\n type: \"text\",\n text: \"Run automatic distill pass.\"\n }],\n },\n })\n setLastTimestamp(\".distill-meta.json\")\n }\n }\n },\n }\n}\n"],"mappings":"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","names":["import_fs","import_path","Database","import_fs","import_fs","import_path","import_path","import_fs","import_fs","import_path","import_fs","import_path"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
3
|
+
import { join as join6 } from "path";
|
|
4
|
+
|
|
5
|
+
// src/tool.ts
|
|
6
|
+
import { tool } from "@opencode-ai/plugin";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
// src/store.ts
|
|
10
|
+
import Database from "better-sqlite3";
|
|
11
|
+
var MemoryStore = class {
|
|
12
|
+
db;
|
|
13
|
+
constructor(dbPath) {
|
|
14
|
+
this.db = new Database(dbPath);
|
|
15
|
+
this.db.pragma("journal_mode = WAL");
|
|
16
|
+
this.init();
|
|
17
|
+
}
|
|
18
|
+
init() {
|
|
19
|
+
this.db.exec(`
|
|
20
|
+
CREATE TABLE IF NOT EXISTS memory_files (
|
|
21
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
22
|
+
path TEXT NOT NULL UNIQUE,
|
|
23
|
+
scope TEXT NOT NULL,
|
|
24
|
+
scope_id TEXT NOT NULL DEFAULT '',
|
|
25
|
+
type TEXT NOT NULL,
|
|
26
|
+
fingerprint TEXT NOT NULL,
|
|
27
|
+
last_indexed_at INTEGER NOT NULL
|
|
28
|
+
)
|
|
29
|
+
`);
|
|
30
|
+
this.db.exec(`
|
|
31
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts
|
|
32
|
+
USING fts5(path, body, tokenize='unicode61')
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
search(query, filters = {}) {
|
|
36
|
+
const tokens = query.split(/\s+/).filter(Boolean).map((t) => `"${t}"`);
|
|
37
|
+
if (tokens.length === 0) return [];
|
|
38
|
+
const ftsQuery = tokens.join(" OR ");
|
|
39
|
+
const limit = filters.limit ?? 10;
|
|
40
|
+
let sql = `
|
|
41
|
+
SELECT f.path, m.scope, m.scope_id, m.type,
|
|
42
|
+
snippet(memory_fts, 1, '<<', '>>', '...', 32) AS snippet,
|
|
43
|
+
bm25(memory_fts) AS score
|
|
44
|
+
FROM memory_fts f
|
|
45
|
+
JOIN memory_files m ON m.path = f.path
|
|
46
|
+
WHERE memory_fts MATCH ?
|
|
47
|
+
`;
|
|
48
|
+
const params = [ftsQuery];
|
|
49
|
+
if (filters.scope) {
|
|
50
|
+
sql += ` AND m.scope = ?`;
|
|
51
|
+
params.push(filters.scope);
|
|
52
|
+
}
|
|
53
|
+
if (filters.scope_id) {
|
|
54
|
+
sql += ` AND m.scope_id = ?`;
|
|
55
|
+
params.push(filters.scope_id);
|
|
56
|
+
}
|
|
57
|
+
if (filters.type) {
|
|
58
|
+
sql += ` AND m.type = ?`;
|
|
59
|
+
params.push(filters.type);
|
|
60
|
+
}
|
|
61
|
+
sql += ` ORDER BY score LIMIT ?`;
|
|
62
|
+
params.push(limit);
|
|
63
|
+
return this.db.prepare(sql).all(...params);
|
|
64
|
+
}
|
|
65
|
+
write(path, scope, scope_id, type, content) {
|
|
66
|
+
const fingerprint = `${content.length}-${Date.now()}`;
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
this.db.prepare(`
|
|
69
|
+
INSERT OR REPLACE INTO memory_files (path, scope, scope_id, type, fingerprint, last_indexed_at)
|
|
70
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
71
|
+
`).run(path, scope, scope_id, type, fingerprint, now);
|
|
72
|
+
this.db.prepare(`DELETE FROM memory_fts WHERE path = ?`).run(path);
|
|
73
|
+
this.db.prepare(`INSERT INTO memory_fts (path, body) VALUES (?, ?)`).run(path, content);
|
|
74
|
+
}
|
|
75
|
+
forget(memoryId) {
|
|
76
|
+
this.db.prepare("DELETE FROM memory_files WHERE path = ?").run(memoryId);
|
|
77
|
+
this.db.prepare("DELETE FROM memory_fts WHERE path = ?").run(memoryId);
|
|
78
|
+
}
|
|
79
|
+
close() {
|
|
80
|
+
this.db.close();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/reconcile.ts
|
|
85
|
+
import { readdirSync, readFileSync } from "fs";
|
|
86
|
+
import { join, relative, sep } from "path";
|
|
87
|
+
function parseScopeAndId(memoryRoot, filePath) {
|
|
88
|
+
const rel = relative(memoryRoot, filePath).split(sep);
|
|
89
|
+
if (rel[0] === "global") return { scope: "global", scope_id: "" };
|
|
90
|
+
if (rel[0] === "projects" && rel.length >= 3) return { scope: "projects", scope_id: rel[1] };
|
|
91
|
+
if (rel[0] === "sessions" && rel.length >= 3) return { scope: "sessions", scope_id: rel[1] };
|
|
92
|
+
return { scope: "unknown", scope_id: "" };
|
|
93
|
+
}
|
|
94
|
+
function walkMdFiles(dir) {
|
|
95
|
+
const results = [];
|
|
96
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
97
|
+
const full = join(dir, entry.name);
|
|
98
|
+
if (entry.isDirectory()) {
|
|
99
|
+
results.push(...walkMdFiles(full));
|
|
100
|
+
} else if (entry.name.endsWith(".md")) {
|
|
101
|
+
results.push(full);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return results;
|
|
105
|
+
}
|
|
106
|
+
function reconcileMemoryDir(store, memoryRoot) {
|
|
107
|
+
const filesOnDisk = walkMdFiles(memoryRoot);
|
|
108
|
+
const diskPaths = /* @__PURE__ */ new Set();
|
|
109
|
+
for (const file of filesOnDisk) {
|
|
110
|
+
diskPaths.add(file);
|
|
111
|
+
const content = readFileSync(file, "utf-8");
|
|
112
|
+
const { scope, scope_id } = parseScopeAndId(memoryRoot, file);
|
|
113
|
+
store.write(file, scope, scope_id, "snapshot", content);
|
|
114
|
+
}
|
|
115
|
+
const indexed = store.db.prepare("SELECT path FROM memory_files").all();
|
|
116
|
+
for (const row of indexed) {
|
|
117
|
+
if (!diskPaths.has(row.path)) {
|
|
118
|
+
store.forget(row.path);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/tool.ts
|
|
124
|
+
import { mkdirSync } from "fs";
|
|
125
|
+
function createMemoryTool(ctx) {
|
|
126
|
+
let store = null;
|
|
127
|
+
function ensureStore() {
|
|
128
|
+
if (!store) {
|
|
129
|
+
mkdirSync(ctx.memoryDir, { recursive: true });
|
|
130
|
+
store = new MemoryStore(ctx.dbPath);
|
|
131
|
+
reconcileMemoryDir(store, ctx.memoryDir);
|
|
132
|
+
}
|
|
133
|
+
return store;
|
|
134
|
+
}
|
|
135
|
+
return tool({
|
|
136
|
+
description: "Search or write to the persistent memory store. Search returns ranked results across memory files. Write saves content to a memory file.",
|
|
137
|
+
args: {
|
|
138
|
+
operation: z.enum(["search", "write", "list", "forget"]),
|
|
139
|
+
query: z.string().optional(),
|
|
140
|
+
scope: z.string().optional(),
|
|
141
|
+
scope_id: z.string().optional(),
|
|
142
|
+
type: z.string().optional(),
|
|
143
|
+
limit: z.number().optional(),
|
|
144
|
+
path: z.string().optional(),
|
|
145
|
+
content: z.string().optional(),
|
|
146
|
+
memory_id: z.string().optional()
|
|
147
|
+
},
|
|
148
|
+
execute(args) {
|
|
149
|
+
const s = ensureStore();
|
|
150
|
+
if (args.operation === "search") {
|
|
151
|
+
if (!args.query) return "query is required for search";
|
|
152
|
+
const results = s.search(args.query, {
|
|
153
|
+
scope: args.scope,
|
|
154
|
+
scope_id: args.scope_id,
|
|
155
|
+
type: args.type,
|
|
156
|
+
limit: args.limit
|
|
157
|
+
});
|
|
158
|
+
if (results.length === 0) return "No results found";
|
|
159
|
+
return JSON.stringify(results, null, 2);
|
|
160
|
+
}
|
|
161
|
+
if (args.operation === "write") {
|
|
162
|
+
if (!args.path) return "path is required for write";
|
|
163
|
+
if (!args.content) return "content is required for write";
|
|
164
|
+
s.write(
|
|
165
|
+
args.path,
|
|
166
|
+
args.scope ?? "unknown",
|
|
167
|
+
args.scope_id ?? "",
|
|
168
|
+
args.type ?? "snapshot",
|
|
169
|
+
args.content
|
|
170
|
+
);
|
|
171
|
+
return `Wrote to ${args.path}`;
|
|
172
|
+
}
|
|
173
|
+
if (args.operation === "list") {
|
|
174
|
+
const results = s.search("*", {
|
|
175
|
+
scope: args.scope,
|
|
176
|
+
scope_id: args.scope_id,
|
|
177
|
+
type: args.type,
|
|
178
|
+
limit: args.limit ?? 50
|
|
179
|
+
});
|
|
180
|
+
return JSON.stringify(results, null, 2);
|
|
181
|
+
}
|
|
182
|
+
if (args.operation === "forget") {
|
|
183
|
+
if (!args.memory_id) return "memory_id is required for forget";
|
|
184
|
+
s.forget(args.memory_id);
|
|
185
|
+
return `Forgot ${args.memory_id}`;
|
|
186
|
+
}
|
|
187
|
+
return "Invalid operation";
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/config.ts
|
|
193
|
+
import { readFileSync as readFileSync2, existsSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
194
|
+
import { join as join2, dirname } from "path";
|
|
195
|
+
var DEFAULT_CONFIG = {
|
|
196
|
+
memory: {
|
|
197
|
+
injection: {
|
|
198
|
+
total_budget: 2e3,
|
|
199
|
+
allocation: {
|
|
200
|
+
checkpoint: 0.3,
|
|
201
|
+
memory: 0.25,
|
|
202
|
+
notes: 0.2,
|
|
203
|
+
progress: 0.15,
|
|
204
|
+
buffer: 0.1
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
checkpoint: {
|
|
208
|
+
thresholds: ["40%", "60%", "80%"],
|
|
209
|
+
drain_timeout_ms: 12e4,
|
|
210
|
+
idle_timeout_ms: 3e5
|
|
211
|
+
},
|
|
212
|
+
dream: {
|
|
213
|
+
auto: true,
|
|
214
|
+
interval_days: 7
|
|
215
|
+
},
|
|
216
|
+
distill: {
|
|
217
|
+
auto: true,
|
|
218
|
+
interval_days: 30
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
function getConfigPath(projectRoot) {
|
|
223
|
+
return join2(projectRoot, ".super-pocock", "config.json");
|
|
224
|
+
}
|
|
225
|
+
function loadConfig(projectRoot) {
|
|
226
|
+
const configPath = getConfigPath(projectRoot);
|
|
227
|
+
if (existsSync(configPath)) {
|
|
228
|
+
const content = readFileSync2(configPath, "utf-8");
|
|
229
|
+
return { ...DEFAULT_CONFIG, ...JSON.parse(content) };
|
|
230
|
+
}
|
|
231
|
+
return DEFAULT_CONFIG;
|
|
232
|
+
}
|
|
233
|
+
function ensureConfig(projectRoot) {
|
|
234
|
+
const configPath = getConfigPath(projectRoot);
|
|
235
|
+
if (!existsSync(configPath)) {
|
|
236
|
+
mkdirSync2(dirname(configPath), { recursive: true });
|
|
237
|
+
writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/paths.ts
|
|
242
|
+
import { createHash } from "crypto";
|
|
243
|
+
import { join as join3 } from "path";
|
|
244
|
+
function resolveProjectId(absRepoPath) {
|
|
245
|
+
return createHash("sha256").update(absRepoPath).digest("hex").slice(0, 12);
|
|
246
|
+
}
|
|
247
|
+
function getMemoryRoot(projectRoot) {
|
|
248
|
+
return join3(projectRoot, ".super-pocock", "memory");
|
|
249
|
+
}
|
|
250
|
+
function getProjectMemoryDir(projectRoot) {
|
|
251
|
+
const projectId = resolveProjectId(projectRoot);
|
|
252
|
+
return join3(getMemoryRoot(projectRoot), "projects", projectId);
|
|
253
|
+
}
|
|
254
|
+
function getGlobalMemoryDir(projectRoot) {
|
|
255
|
+
return join3(getMemoryRoot(projectRoot), "global");
|
|
256
|
+
}
|
|
257
|
+
function getDbPath(projectRoot) {
|
|
258
|
+
return join3(getMemoryRoot(projectRoot), "memory.db");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// src/checkpoint-plugin.ts
|
|
262
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
263
|
+
var CheckpointPlugin = async ({ client, directory }) => {
|
|
264
|
+
const config = loadConfig(directory);
|
|
265
|
+
const memoryDir = getProjectMemoryDir(directory);
|
|
266
|
+
mkdirSync3(memoryDir, { recursive: true });
|
|
267
|
+
let lastCheckpointRatio = 0;
|
|
268
|
+
const THRESHOLDS = config.memory.checkpoint.thresholds.map((t) => parseFloat(t) / 100);
|
|
269
|
+
return {
|
|
270
|
+
event: async ({ event }) => {
|
|
271
|
+
if (event.type === "session.next.step.ended") {
|
|
272
|
+
const ratio = event.properties.tokens / event.properties.contextLimit;
|
|
273
|
+
const nextThreshold = THRESHOLDS.find((t) => t > lastCheckpointRatio);
|
|
274
|
+
if (nextThreshold && ratio >= nextThreshold) {
|
|
275
|
+
lastCheckpointRatio = ratio;
|
|
276
|
+
await triggerCheckpoint(client, event.properties.sessionID, directory);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (event.type === "session.status" && event.properties?.status?.type === "idle") {
|
|
280
|
+
setTimeout(async () => {
|
|
281
|
+
await triggerCheckpoint(client, event.properties.sessionID, directory);
|
|
282
|
+
}, config.memory.checkpoint.idle_timeout_ms);
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
"experimental.session.compacting": async (input, output) => {
|
|
286
|
+
output.context.push(`
|
|
287
|
+
## Checkpoint Instructions
|
|
288
|
+
Extract and preserve these 11 fields:
|
|
289
|
+
1. Intent (\u7528\u6237\u7684\u6838\u5FC3\u76EE\u6807)
|
|
290
|
+
2. Actions (\u5DF2\u6267\u884C\u7684\u64CD\u4F5C)
|
|
291
|
+
3. Task Tree (\u5B50\u4EFB\u52A1\u5C42\u7EA7)
|
|
292
|
+
4. Errors & Workarounds (\u9047\u5230\u7684\u95EE\u9898\u53CA\u89E3\u51B3)
|
|
293
|
+
5. Design Decisions (\u5173\u952E\u6280\u672F\u9009\u62E9)
|
|
294
|
+
6. Constraints (\u9650\u5236\u6761\u4EF6)
|
|
295
|
+
7. Current State (\u4EFB\u52A1\u8FDB\u5EA6)
|
|
296
|
+
8. TODOs (\u5269\u4F59\u5DE5\u4F5C)
|
|
297
|
+
9. Code Changes Summary (Git diff \u6982\u8981)
|
|
298
|
+
10. Key File Paths (\u6D89\u53CA\u7684\u6838\u5FC3\u6587\u4EF6)
|
|
299
|
+
11. Timestamp (\u68C0\u67E5\u70B9\u521B\u5EFA\u65F6\u95F4)
|
|
300
|
+
`);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
async function triggerCheckpoint(client, sessionID, directory) {
|
|
305
|
+
const messages = await client.session.messages({ path: { id: sessionID } });
|
|
306
|
+
const context = constructCheckpointContext(messages);
|
|
307
|
+
await client.session.prompt({
|
|
308
|
+
path: { id: sessionID },
|
|
309
|
+
body: {
|
|
310
|
+
agent: "checkpoint-writer",
|
|
311
|
+
parts: [{
|
|
312
|
+
type: "text",
|
|
313
|
+
text: `Extract checkpoint from this context:
|
|
314
|
+
${context}`
|
|
315
|
+
}]
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
function constructCheckpointContext(messages) {
|
|
320
|
+
return messages.map((m) => `[${m.role}]: ${m.content}`).join("\n");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// src/injection-plugin.ts
|
|
324
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
325
|
+
import { join as join4 } from "path";
|
|
326
|
+
var MemoryInjectionPlugin = async ({ directory }) => {
|
|
327
|
+
const config = loadConfig(directory);
|
|
328
|
+
const memoryCache = /* @__PURE__ */ new Map();
|
|
329
|
+
function loadMemoryFiles() {
|
|
330
|
+
const projectDir = getProjectMemoryDir(directory);
|
|
331
|
+
const globalDir = getGlobalMemoryDir(directory);
|
|
332
|
+
const memories = {};
|
|
333
|
+
const files = [
|
|
334
|
+
{ key: "checkpoint", path: join4(projectDir, "checkpoint.md") },
|
|
335
|
+
{ key: "memory", path: join4(projectDir, "MEMORY.md") },
|
|
336
|
+
{ key: "notes", path: join4(projectDir, "notes.md") },
|
|
337
|
+
{ key: "progress", path: join4(projectDir, "tasks", "progress.md") },
|
|
338
|
+
{ key: "global_memory", path: join4(globalDir, "MEMORY.md") }
|
|
339
|
+
];
|
|
340
|
+
for (const file of files) {
|
|
341
|
+
if (existsSync2(file.path)) {
|
|
342
|
+
memories[file.key] = readFileSync3(file.path, "utf-8");
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return memories;
|
|
346
|
+
}
|
|
347
|
+
function estimateTokens(text) {
|
|
348
|
+
return Math.ceil(text.length / 4);
|
|
349
|
+
}
|
|
350
|
+
function smartTruncate(text, maxTokens) {
|
|
351
|
+
const estimatedTokens = estimateTokens(text);
|
|
352
|
+
if (estimatedTokens <= maxTokens) return text;
|
|
353
|
+
const keepChars = maxTokens * 4 * 0.3;
|
|
354
|
+
const start = text.substring(0, keepChars);
|
|
355
|
+
const end = text.substring(text.length - keepChars);
|
|
356
|
+
return `${start}
|
|
357
|
+
|
|
358
|
+
... [\u8BB0\u5FC6\u5DF2\u538B\u7F29\uFF0C\u539F ${estimatedTokens} tokens] ...
|
|
359
|
+
|
|
360
|
+
${end}`;
|
|
361
|
+
}
|
|
362
|
+
function formatMemory(memories, budget) {
|
|
363
|
+
const sections = [];
|
|
364
|
+
if (memories.checkpoint && budget.checkpoint > 0) {
|
|
365
|
+
sections.push(`## \u68C0\u67E5\u70B9\u72B6\u6001
|
|
366
|
+
${smartTruncate(memories.checkpoint, budget.checkpoint)}`);
|
|
367
|
+
}
|
|
368
|
+
if (memories.memory && budget.memory > 0) {
|
|
369
|
+
sections.push(`## \u9879\u76EE\u8BB0\u5FC6
|
|
370
|
+
${smartTruncate(memories.memory, budget.memory)}`);
|
|
371
|
+
}
|
|
372
|
+
if (memories.global_memory && budget.memory > 0) {
|
|
373
|
+
sections.push(`## \u5168\u5C40\u8BB0\u5FC6
|
|
374
|
+
${smartTruncate(memories.global_memory, budget.memory * 0.5)}`);
|
|
375
|
+
}
|
|
376
|
+
if (memories.notes && budget.notes > 0) {
|
|
377
|
+
sections.push(`## \u5F53\u524D\u7B14\u8BB0
|
|
378
|
+
${smartTruncate(memories.notes, budget.notes)}`);
|
|
379
|
+
}
|
|
380
|
+
if (memories.progress && budget.progress > 0) {
|
|
381
|
+
sections.push(`## \u4EFB\u52A1\u8FDB\u5EA6
|
|
382
|
+
${smartTruncate(memories.progress, budget.progress)}`);
|
|
383
|
+
}
|
|
384
|
+
return sections.join("\n\n");
|
|
385
|
+
}
|
|
386
|
+
return {
|
|
387
|
+
event: async ({ event }) => {
|
|
388
|
+
if (event.type === "session.created") {
|
|
389
|
+
const memories = loadMemoryFiles();
|
|
390
|
+
memoryCache.set(event.properties.sessionID, memories);
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
394
|
+
const memories = memoryCache.get(input.sessionID);
|
|
395
|
+
if (!memories) return;
|
|
396
|
+
const totalBudget = config.memory.injection.total_budget;
|
|
397
|
+
const allocation = config.memory.injection.allocation;
|
|
398
|
+
const budget = {
|
|
399
|
+
checkpoint: Math.floor(totalBudget * allocation.checkpoint),
|
|
400
|
+
memory: Math.floor(totalBudget * allocation.memory),
|
|
401
|
+
notes: Math.floor(totalBudget * allocation.notes),
|
|
402
|
+
progress: Math.floor(totalBudget * allocation.progress)
|
|
403
|
+
};
|
|
404
|
+
const memoryContext = formatMemory(memories, budget);
|
|
405
|
+
if (memoryContext) {
|
|
406
|
+
output.system.push(`
|
|
407
|
+
|
|
408
|
+
# Injected Memory Context
|
|
409
|
+
${memoryContext}`);
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
"experimental.session.compacting": async (input, output) => {
|
|
413
|
+
const memories = memoryCache.get(input.sessionID);
|
|
414
|
+
if (!memories) return;
|
|
415
|
+
const criticalMemory = [];
|
|
416
|
+
if (memories.checkpoint) {
|
|
417
|
+
criticalMemory.push(`\u5F53\u524D\u68C0\u67E5\u70B9: ${memories.checkpoint.substring(0, 500)}`);
|
|
418
|
+
}
|
|
419
|
+
if (memories.progress) {
|
|
420
|
+
criticalMemory.push(`\u4EFB\u52A1\u8FDB\u5EA6\u6458\u8981: ${memories.progress.substring(0, 300)}`);
|
|
421
|
+
}
|
|
422
|
+
output.context.push(`## \u4FDD\u7559\u7684\u5173\u952E\u8BB0\u5FC6
|
|
423
|
+
${criticalMemory.join("\n\n")}`);
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
// src/dream-plugin.ts
|
|
429
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync4 } from "fs";
|
|
430
|
+
import { join as join5 } from "path";
|
|
431
|
+
var DreamPlugin = async ({ client, directory }) => {
|
|
432
|
+
const config = loadConfig(directory);
|
|
433
|
+
const memoryDir = getProjectMemoryDir(directory);
|
|
434
|
+
mkdirSync4(memoryDir, { recursive: true });
|
|
435
|
+
function getLastTimestamp(metaFile) {
|
|
436
|
+
const metaPath = join5(memoryDir, metaFile);
|
|
437
|
+
if (existsSync3(metaPath)) {
|
|
438
|
+
const meta = JSON.parse(readFileSync4(metaPath, "utf-8"));
|
|
439
|
+
return meta.timestamp ?? 0;
|
|
440
|
+
}
|
|
441
|
+
return 0;
|
|
442
|
+
}
|
|
443
|
+
function setLastTimestamp(metaFile) {
|
|
444
|
+
const metaPath = join5(memoryDir, metaFile);
|
|
445
|
+
writeFileSync2(metaPath, JSON.stringify({ timestamp: Date.now() }));
|
|
446
|
+
}
|
|
447
|
+
function shouldAutoRun(metaFile, intervalDays) {
|
|
448
|
+
const lastRun = getLastTimestamp(metaFile);
|
|
449
|
+
const intervalMs = intervalDays * 24 * 60 * 60 * 1e3;
|
|
450
|
+
return Date.now() - lastRun > intervalMs;
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
event: async ({ event }) => {
|
|
454
|
+
if (event.type === "session.created") {
|
|
455
|
+
if (config.memory.dream.auto && shouldAutoRun(".dream-meta.json", config.memory.dream.interval_days)) {
|
|
456
|
+
await client.session.prompt({
|
|
457
|
+
path: { id: event.properties.sessionID },
|
|
458
|
+
body: {
|
|
459
|
+
agent: "dream",
|
|
460
|
+
parts: [{
|
|
461
|
+
type: "text",
|
|
462
|
+
text: "Run automatic dream memory consolidation pass."
|
|
463
|
+
}]
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
setLastTimestamp(".dream-meta.json");
|
|
467
|
+
}
|
|
468
|
+
if (config.memory.distill.auto && shouldAutoRun(".distill-meta.json", config.memory.distill.interval_days)) {
|
|
469
|
+
await client.session.prompt({
|
|
470
|
+
path: { id: event.properties.sessionID },
|
|
471
|
+
body: {
|
|
472
|
+
agent: "distill",
|
|
473
|
+
parts: [{
|
|
474
|
+
type: "text",
|
|
475
|
+
text: "Run automatic distill pass."
|
|
476
|
+
}]
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
setLastTimestamp(".distill-meta.json");
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// src/index.ts
|
|
487
|
+
var MemoryCorePlugin = async (ctx) => {
|
|
488
|
+
ensureConfig(ctx.directory);
|
|
489
|
+
const memoryRoot = getMemoryRoot(ctx.directory);
|
|
490
|
+
const projectDir = getProjectMemoryDir(ctx.directory);
|
|
491
|
+
const globalDir = getGlobalMemoryDir(ctx.directory);
|
|
492
|
+
mkdirSync5(join6(memoryRoot, "global"), { recursive: true });
|
|
493
|
+
mkdirSync5(join6(projectDir, "sessions"), { recursive: true });
|
|
494
|
+
mkdirSync5(join6(projectDir, "tasks"), { recursive: true });
|
|
495
|
+
const memoryTool = createMemoryTool({
|
|
496
|
+
memoryDir: memoryRoot,
|
|
497
|
+
dbPath: getDbPath(ctx.directory)
|
|
498
|
+
});
|
|
499
|
+
const checkpointPlugin = await CheckpointPlugin(ctx);
|
|
500
|
+
const injectionPlugin = await MemoryInjectionPlugin(ctx);
|
|
501
|
+
const dreamPlugin = await DreamPlugin(ctx);
|
|
502
|
+
return {
|
|
503
|
+
tool: {
|
|
504
|
+
memory: memoryTool
|
|
505
|
+
},
|
|
506
|
+
event: async (input) => {
|
|
507
|
+
await checkpointPlugin.event?.(input);
|
|
508
|
+
await injectionPlugin.event?.(input);
|
|
509
|
+
await dreamPlugin.event?.(input);
|
|
510
|
+
},
|
|
511
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
512
|
+
await injectionPlugin["experimental.chat.system.transform"]?.(input, output);
|
|
513
|
+
},
|
|
514
|
+
"experimental.session.compacting": async (input, output) => {
|
|
515
|
+
await checkpointPlugin["experimental.session.compacting"]?.(input, output);
|
|
516
|
+
await injectionPlugin["experimental.session.compacting"]?.(input, output);
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
};
|
|
520
|
+
var index_default = MemoryCorePlugin;
|
|
521
|
+
export {
|
|
522
|
+
DEFAULT_CONFIG,
|
|
523
|
+
MemoryCorePlugin,
|
|
524
|
+
MemoryStore,
|
|
525
|
+
createMemoryTool,
|
|
526
|
+
index_default as default,
|
|
527
|
+
getGlobalMemoryDir,
|
|
528
|
+
getMemoryRoot,
|
|
529
|
+
getProjectMemoryDir,
|
|
530
|
+
loadConfig,
|
|
531
|
+
resolveProjectId
|
|
532
|
+
};
|
|
533
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/tool.ts","../src/store.ts","../src/reconcile.ts","../src/config.ts","../src/paths.ts","../src/checkpoint-plugin.ts","../src/injection-plugin.ts","../src/dream-plugin.ts"],"sourcesContent":["import type { Plugin } from \"@opencode-ai/plugin\"\nimport { mkdirSync } from \"fs\"\nimport { join } from \"path\"\nimport { createMemoryTool } from \"./tool\"\nimport { CheckpointPlugin } from \"./checkpoint-plugin\"\nimport { MemoryInjectionPlugin } from \"./injection-plugin\"\nimport { DreamPlugin } from \"./dream-plugin\"\nimport { getMemoryRoot, getProjectMemoryDir, getGlobalMemoryDir, getDbPath } from \"./paths\"\nimport { ensureConfig } from \"./config\"\n\nexport const MemoryCorePlugin: Plugin = async (ctx) => {\n // 确保配置文件存在\n ensureConfig(ctx.directory)\n\n const memoryRoot = getMemoryRoot(ctx.directory)\n const projectDir = getProjectMemoryDir(ctx.directory)\n const globalDir = getGlobalMemoryDir(ctx.directory)\n\n // 创建目录结构\n mkdirSync(join(memoryRoot, \"global\"), { recursive: true })\n mkdirSync(join(projectDir, \"sessions\"), { recursive: true })\n mkdirSync(join(projectDir, \"tasks\"), { recursive: true })\n\n // 创建 memory 工具\n const memoryTool = createMemoryTool({\n memoryDir: memoryRoot,\n dbPath: getDbPath(ctx.directory),\n })\n\n // 初始化子插件\n const checkpointPlugin = await CheckpointPlugin(ctx)\n const injectionPlugin = await MemoryInjectionPlugin(ctx)\n const dreamPlugin = await DreamPlugin(ctx)\n\n return {\n tool: {\n memory: memoryTool,\n },\n event: async (input) => {\n await checkpointPlugin.event?.(input)\n await injectionPlugin.event?.(input)\n await dreamPlugin.event?.(input)\n },\n \"experimental.chat.system.transform\": async (input, output) => {\n await injectionPlugin[\"experimental.chat.system.transform\"]?.(input, output)\n },\n \"experimental.session.compacting\": async (input, output) => {\n await checkpointPlugin[\"experimental.session.compacting\"]?.(input, output)\n await injectionPlugin[\"experimental.session.compacting\"]?.(input, output)\n },\n }\n}\n\nexport default MemoryCorePlugin\n\n// 导出子模块\nexport { MemoryStore } from \"./store\"\nexport { createMemoryTool } from \"./tool\"\nexport { resolveProjectId, getMemoryRoot, getProjectMemoryDir, getGlobalMemoryDir } from \"./paths\"\nexport { loadConfig, DEFAULT_CONFIG } from \"./config\"\nexport type { MemoryConfig } from \"./config\"\nexport type { SearchResult, SearchFilters } from \"./store\"\n","import { tool } from \"@opencode-ai/plugin\"\nimport { z } from \"zod\"\nimport { MemoryStore } from \"./store\"\nimport { reconcileMemoryDir } from \"./reconcile\"\nimport { mkdirSync } from \"fs\"\n\nexport function createMemoryTool(ctx: { memoryDir: string; dbPath: string }) {\n let store: MemoryStore | null = null\n\n function ensureStore() {\n if (!store) {\n mkdirSync(ctx.memoryDir, { recursive: true })\n store = new MemoryStore(ctx.dbPath)\n reconcileMemoryDir(store, ctx.memoryDir)\n }\n return store\n }\n\n return tool({\n description: \"Search or write to the persistent memory store. Search returns ranked results across memory files. Write saves content to a memory file.\",\n args: {\n operation: z.enum([\"search\", \"write\", \"list\", \"forget\"]),\n query: z.string().optional(),\n scope: z.string().optional(),\n scope_id: z.string().optional(),\n type: z.string().optional(),\n limit: z.number().optional(),\n path: z.string().optional(),\n content: z.string().optional(),\n memory_id: z.string().optional(),\n },\n execute(args: any) {\n const s = ensureStore()\n\n if (args.operation === \"search\") {\n if (!args.query) return \"query is required for search\"\n const results = s.search(args.query, {\n scope: args.scope,\n scope_id: args.scope_id,\n type: args.type,\n limit: args.limit,\n })\n if (results.length === 0) return \"No results found\"\n return JSON.stringify(results, null, 2)\n }\n\n if (args.operation === \"write\") {\n if (!args.path) return \"path is required for write\"\n if (!args.content) return \"content is required for write\"\n s.write(\n args.path,\n args.scope ?? \"unknown\",\n args.scope_id ?? \"\",\n args.type ?? \"snapshot\",\n args.content,\n )\n return `Wrote to ${args.path}`\n }\n\n if (args.operation === \"list\") {\n const results = s.search(\"*\", {\n scope: args.scope,\n scope_id: args.scope_id,\n type: args.type,\n limit: args.limit ?? 50,\n })\n return JSON.stringify(results, null, 2)\n }\n\n if (args.operation === \"forget\") {\n if (!args.memory_id) return \"memory_id is required for forget\"\n s.forget(args.memory_id)\n return `Forgot ${args.memory_id}`\n }\n\n return \"Invalid operation\"\n },\n })\n}\n","import Database from \"better-sqlite3\"\n\nexport interface SearchFilters {\n scope?: string\n scope_id?: string\n type?: string\n limit?: number\n}\n\nexport interface SearchResult {\n path: string\n scope: string\n scope_id: string\n type: string\n snippet: string\n score: number\n}\n\nexport class MemoryStore {\n public db: Database.Database\n\n constructor(dbPath: string) {\n this.db = new Database(dbPath)\n this.db.pragma(\"journal_mode = WAL\")\n this.init()\n }\n\n private init() {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS memory_files (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n path TEXT NOT NULL UNIQUE,\n scope TEXT NOT NULL,\n scope_id TEXT NOT NULL DEFAULT '',\n type TEXT NOT NULL,\n fingerprint TEXT NOT NULL,\n last_indexed_at INTEGER NOT NULL\n )\n `)\n this.db.exec(`\n CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts\n USING fts5(path, body, tokenize='unicode61')\n `)\n }\n\n search(query: string, filters: SearchFilters = {}): SearchResult[] {\n const tokens = query.split(/\\s+/).filter(Boolean).map(t => `\"${t}\"`)\n if (tokens.length === 0) return []\n const ftsQuery = tokens.join(\" OR \")\n const limit = filters.limit ?? 10\n\n let sql = `\n SELECT f.path, m.scope, m.scope_id, m.type,\n snippet(memory_fts, 1, '<<', '>>', '...', 32) AS snippet,\n bm25(memory_fts) AS score\n FROM memory_fts f\n JOIN memory_files m ON m.path = f.path\n WHERE memory_fts MATCH ?\n `\n const params: any[] = [ftsQuery]\n if (filters.scope) { sql += ` AND m.scope = ?`; params.push(filters.scope) }\n if (filters.scope_id) { sql += ` AND m.scope_id = ?`; params.push(filters.scope_id) }\n if (filters.type) { sql += ` AND m.type = ?`; params.push(filters.type) }\n sql += ` ORDER BY score LIMIT ?`\n params.push(limit)\n return this.db.prepare(sql).all(...params) as SearchResult[]\n }\n\n write(path: string, scope: string, scope_id: string, type: string, content: string) {\n const fingerprint = `${content.length}-${Date.now()}`\n const now = Date.now()\n\n this.db.prepare(`\n INSERT OR REPLACE INTO memory_files (path, scope, scope_id, type, fingerprint, last_indexed_at)\n VALUES (?, ?, ?, ?, ?, ?)\n `).run(path, scope, scope_id, type, fingerprint, now)\n\n this.db.prepare(`DELETE FROM memory_fts WHERE path = ?`).run(path)\n this.db.prepare(`INSERT INTO memory_fts (path, body) VALUES (?, ?)`).run(path, content)\n }\n\n forget(memoryId: string) {\n this.db.prepare(\"DELETE FROM memory_files WHERE path = ?\").run(memoryId)\n this.db.prepare(\"DELETE FROM memory_fts WHERE path = ?\").run(memoryId)\n }\n\n close() {\n this.db.close()\n }\n}\n","import { readdirSync, readFileSync } from \"fs\"\nimport { join, relative, sep } from \"path\"\nimport type { MemoryStore } from \"./store\"\n\nfunction parseScopeAndId(memoryRoot: string, filePath: string): { scope: string; scope_id: string } {\n const rel = relative(memoryRoot, filePath).split(sep)\n if (rel[0] === \"global\") return { scope: \"global\", scope_id: \"\" }\n if (rel[0] === \"projects\" && rel.length >= 3) return { scope: \"projects\", scope_id: rel[1] }\n if (rel[0] === \"sessions\" && rel.length >= 3) return { scope: \"sessions\", scope_id: rel[1] }\n return { scope: \"unknown\", scope_id: \"\" }\n}\n\nfunction walkMdFiles(dir: string): string[] {\n const results: string[] = []\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) {\n results.push(...walkMdFiles(full))\n } else if (entry.name.endsWith(\".md\")) {\n results.push(full)\n }\n }\n return results\n}\n\nexport function reconcileMemoryDir(store: MemoryStore, memoryRoot: string) {\n const filesOnDisk = walkMdFiles(memoryRoot)\n const diskPaths = new Set<string>()\n\n for (const file of filesOnDisk) {\n diskPaths.add(file)\n const content = readFileSync(file, \"utf-8\")\n const { scope, scope_id } = parseScopeAndId(memoryRoot, file)\n store.write(file, scope, scope_id, \"snapshot\", content)\n }\n\n const indexed = store.db.prepare(\"SELECT path FROM memory_files\").all() as { path: string }[]\n for (const row of indexed) {\n if (!diskPaths.has(row.path)) {\n store.forget(row.path)\n }\n }\n}\n","import { readFileSync, existsSync, writeFileSync, mkdirSync } from \"fs\"\nimport { join, dirname } from \"path\"\n\nexport interface MemoryConfig {\n memory: {\n injection: {\n total_budget: number\n allocation: {\n checkpoint: number\n memory: number\n notes: number\n progress: number\n buffer: number\n }\n }\n checkpoint: {\n thresholds: string[]\n drain_timeout_ms: number\n idle_timeout_ms: number\n }\n dream: {\n auto: boolean\n interval_days: number\n }\n distill: {\n auto: boolean\n interval_days: number\n }\n }\n}\n\nexport const DEFAULT_CONFIG: MemoryConfig = {\n memory: {\n injection: {\n total_budget: 2000,\n allocation: {\n checkpoint: 0.30,\n memory: 0.25,\n notes: 0.20,\n progress: 0.15,\n buffer: 0.10\n }\n },\n checkpoint: {\n thresholds: [\"40%\", \"60%\", \"80%\"],\n drain_timeout_ms: 120000,\n idle_timeout_ms: 300000\n },\n dream: {\n auto: true,\n interval_days: 7\n },\n distill: {\n auto: true,\n interval_days: 30\n }\n }\n}\n\nexport function getConfigPath(projectRoot: string): string {\n return join(projectRoot, \".super-pocock\", \"config.json\")\n}\n\nexport function loadConfig(projectRoot: string): MemoryConfig {\n const configPath = getConfigPath(projectRoot)\n if (existsSync(configPath)) {\n const content = readFileSync(configPath, \"utf-8\")\n return { ...DEFAULT_CONFIG, ...JSON.parse(content) }\n }\n return DEFAULT_CONFIG\n}\n\nexport function ensureConfig(projectRoot: string): void {\n const configPath = getConfigPath(projectRoot)\n if (!existsSync(configPath)) {\n mkdirSync(dirname(configPath), { recursive: true })\n writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2))\n }\n}\n","import { createHash } from \"crypto\"\nimport { join } from \"path\"\n\nexport type Scope = \"global\" | \"projects\" | \"sessions\"\n\nexport function resolveProjectId(absRepoPath: string): string {\n return createHash(\"sha256\").update(absRepoPath).digest(\"hex\").slice(0, 12)\n}\n\nexport function getMemoryRoot(projectRoot: string): string {\n return join(projectRoot, \".super-pocock\", \"memory\")\n}\n\nexport function getProjectMemoryDir(projectRoot: string): string {\n const projectId = resolveProjectId(projectRoot)\n return join(getMemoryRoot(projectRoot), \"projects\", projectId)\n}\n\nexport function getGlobalMemoryDir(projectRoot: string): string {\n return join(getMemoryRoot(projectRoot), \"global\")\n}\n\nexport function getSessionMemoryDir(projectRoot: string, sessionId: string): string {\n return join(getProjectMemoryDir(projectRoot), \"sessions\", sessionId)\n}\n\nexport function getDbPath(projectRoot: string): string {\n return join(getMemoryRoot(projectRoot), \"memory.db\")\n}\n","import type { Plugin } from \"@opencode-ai/plugin\"\nimport { loadConfig } from \"./config\"\nimport { getProjectMemoryDir } from \"./paths\"\nimport { mkdirSync } from \"fs\"\nimport { join } from \"path\"\n\nexport const CheckpointPlugin: Plugin = async ({ client, directory }) => {\n const config = loadConfig(directory)\n const memoryDir = getProjectMemoryDir(directory)\n mkdirSync(memoryDir, { recursive: true })\n\n let lastCheckpointRatio = 0\n const THRESHOLDS = config.memory.checkpoint.thresholds.map(t => parseFloat(t) / 100)\n\n return {\n event: async ({ event }) => {\n // 触发 1: Token 比例阈值\n if (event.type === \"session.next.step.ended\") {\n const ratio = event.properties.tokens / event.properties.contextLimit\n const nextThreshold = THRESHOLDS.find(t => t > lastCheckpointRatio)\n if (nextThreshold && ratio >= nextThreshold) {\n lastCheckpointRatio = ratio\n await triggerCheckpoint(client, event.properties.sessionID, directory)\n }\n }\n\n // 触发 2: 会话结束(idle 超时)\n if (event.type === \"session.status\" && event.properties?.status?.type === \"idle\") {\n setTimeout(async () => {\n await triggerCheckpoint(client, event.properties.sessionID, directory)\n }, config.memory.checkpoint.idle_timeout_ms)\n }\n },\n\n \"experimental.session.compacting\": async (input, output) => {\n output.context.push(`\n## Checkpoint Instructions\nExtract and preserve these 11 fields:\n1. Intent (用户的核心目标)\n2. Actions (已执行的操作)\n3. Task Tree (子任务层级)\n4. Errors & Workarounds (遇到的问题及解决)\n5. Design Decisions (关键技术选择)\n6. Constraints (限制条件)\n7. Current State (任务进度)\n8. TODOs (剩余工作)\n9. Code Changes Summary (Git diff 概要)\n10. Key File Paths (涉及的核心文件)\n11. Timestamp (检查点创建时间)\n `)\n },\n }\n}\n\nasync function triggerCheckpoint(client: any, sessionID: string, directory: string) {\n const messages = await client.session.messages({ path: { id: sessionID } })\n const context = constructCheckpointContext(messages)\n\n await client.session.prompt({\n path: { id: sessionID },\n body: {\n agent: \"checkpoint-writer\",\n parts: [{\n type: \"text\",\n text: `Extract checkpoint from this context:\\n${context}`\n }],\n },\n })\n}\n\nfunction constructCheckpointContext(messages: any[]): string {\n return messages.map(m => `[${m.role}]: ${m.content}`).join(\"\\n\")\n}\n","import type { Plugin } from \"@opencode-ai/plugin\"\nimport { loadConfig } from \"./config\"\nimport { getProjectMemoryDir, getGlobalMemoryDir } from \"./paths\"\nimport { existsSync, readFileSync } from \"fs\"\nimport { join } from \"path\"\n\nexport const MemoryInjectionPlugin: Plugin = async ({ directory }) => {\n const config = loadConfig(directory)\n const memoryCache = new Map<string, Record<string, string>>()\n\n function loadMemoryFiles() {\n const projectDir = getProjectMemoryDir(directory)\n const globalDir = getGlobalMemoryDir(directory)\n\n const memories: Record<string, string> = {}\n\n const files = [\n { key: \"checkpoint\", path: join(projectDir, \"checkpoint.md\") },\n { key: \"memory\", path: join(projectDir, \"MEMORY.md\") },\n { key: \"notes\", path: join(projectDir, \"notes.md\") },\n { key: \"progress\", path: join(projectDir, \"tasks\", \"progress.md\") },\n { key: \"global_memory\", path: join(globalDir, \"MEMORY.md\") },\n ]\n\n for (const file of files) {\n if (existsSync(file.path)) {\n memories[file.key] = readFileSync(file.path, \"utf-8\")\n }\n }\n\n return memories\n }\n\n function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4)\n }\n\n function smartTruncate(text: string, maxTokens: number): string {\n const estimatedTokens = estimateTokens(text)\n if (estimatedTokens <= maxTokens) return text\n\n const keepChars = maxTokens * 4 * 0.3\n const start = text.substring(0, keepChars)\n const end = text.substring(text.length - keepChars)\n\n return `${start}\\n\\n... [记忆已压缩,原 ${estimatedTokens} tokens] ...\\n\\n${end}`\n }\n\n function formatMemory(memories: Record<string, string>, budget: Record<string, number>): string {\n const sections = []\n\n if (memories.checkpoint && budget.checkpoint > 0) {\n sections.push(`## 检查点状态\\n${smartTruncate(memories.checkpoint, budget.checkpoint)}`)\n }\n if (memories.memory && budget.memory > 0) {\n sections.push(`## 项目记忆\\n${smartTruncate(memories.memory, budget.memory)}`)\n }\n if (memories.global_memory && budget.memory > 0) {\n sections.push(`## 全局记忆\\n${smartTruncate(memories.global_memory, budget.memory * 0.5)}`)\n }\n if (memories.notes && budget.notes > 0) {\n sections.push(`## 当前笔记\\n${smartTruncate(memories.notes, budget.notes)}`)\n }\n if (memories.progress && budget.progress > 0) {\n sections.push(`## 任务进度\\n${smartTruncate(memories.progress, budget.progress)}`)\n }\n\n return sections.join(\"\\n\\n\")\n }\n\n return {\n event: async ({ event }) => {\n if (event.type === \"session.created\") {\n const memories = loadMemoryFiles()\n memoryCache.set(event.properties.sessionID, memories)\n }\n },\n\n \"experimental.chat.system.transform\": async (input, output) => {\n const memories = memoryCache.get(input.sessionID)\n if (!memories) return\n\n const totalBudget = config.memory.injection.total_budget\n const allocation = config.memory.injection.allocation\n\n const budget = {\n checkpoint: Math.floor(totalBudget * allocation.checkpoint),\n memory: Math.floor(totalBudget * allocation.memory),\n notes: Math.floor(totalBudget * allocation.notes),\n progress: Math.floor(totalBudget * allocation.progress),\n }\n\n const memoryContext = formatMemory(memories, budget)\n\n if (memoryContext) {\n output.system.push(`\\n\\n# Injected Memory Context\\n${memoryContext}`)\n }\n },\n\n \"experimental.session.compacting\": async (input, output) => {\n const memories = memoryCache.get(input.sessionID)\n if (!memories) return\n\n const criticalMemory = []\n if (memories.checkpoint) {\n criticalMemory.push(`当前检查点: ${memories.checkpoint.substring(0, 500)}`)\n }\n if (memories.progress) {\n criticalMemory.push(`任务进度摘要: ${memories.progress.substring(0, 300)}`)\n }\n\n output.context.push(`## 保留的关键记忆\\n${criticalMemory.join(\"\\n\\n\")}`)\n },\n }\n}\n","import type { Plugin } from \"@opencode-ai/plugin\"\nimport { loadConfig } from \"./config\"\nimport { getProjectMemoryDir } from \"./paths\"\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from \"fs\"\nimport { join } from \"path\"\n\nexport const DreamPlugin: Plugin = async ({ client, directory }) => {\n const config = loadConfig(directory)\n const memoryDir = getProjectMemoryDir(directory)\n mkdirSync(memoryDir, { recursive: true })\n\n function getLastTimestamp(metaFile: string): number {\n const metaPath = join(memoryDir, metaFile)\n if (existsSync(metaPath)) {\n const meta = JSON.parse(readFileSync(metaPath, \"utf-8\"))\n return meta.timestamp ?? 0\n }\n return 0\n }\n\n function setLastTimestamp(metaFile: string) {\n const metaPath = join(memoryDir, metaFile)\n writeFileSync(metaPath, JSON.stringify({ timestamp: Date.now() }))\n }\n\n function shouldAutoRun(metaFile: string, intervalDays: number): boolean {\n const lastRun = getLastTimestamp(metaFile)\n const intervalMs = intervalDays * 24 * 60 * 60 * 1000\n return Date.now() - lastRun > intervalMs\n }\n\n return {\n event: async ({ event }) => {\n if (event.type === \"session.created\") {\n // 自动触发 dream\n if (config.memory.dream.auto && shouldAutoRun(\".dream-meta.json\", config.memory.dream.interval_days)) {\n await client.session.prompt({\n path: { id: event.properties.sessionID },\n body: {\n agent: \"dream\",\n parts: [{\n type: \"text\",\n text: \"Run automatic dream memory consolidation pass.\"\n }],\n },\n })\n setLastTimestamp(\".dream-meta.json\")\n }\n\n // 自动触发 distill\n if (config.memory.distill.auto && shouldAutoRun(\".distill-meta.json\", config.memory.distill.interval_days)) {\n await client.session.prompt({\n path: { id: event.properties.sessionID },\n body: {\n agent: \"distill\",\n parts: [{\n type: \"text\",\n text: \"Run automatic distill pass.\"\n }],\n },\n })\n setLastTimestamp(\".distill-meta.json\")\n }\n }\n },\n }\n}\n"],"mappings":"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","names":["mkdirSync","join","readFileSync","mkdirSync","join","join","join","mkdirSync","existsSync","readFileSync","join","existsSync","readFileSync","writeFileSync","mkdirSync","join","mkdirSync","join"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@super-pocock-ai/memory-core",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "持久化记忆系统,基于 SQLite FTS5 和 BM25 搜索",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup src/index.ts --no-dts --format esm,cjs",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"typecheck": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@opencode-ai/plugin": "latest",
|
|
24
|
+
"better-sqlite3": "^11.0.0",
|
|
25
|
+
"zod": "^3.23.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"typescript": "^5.5.0",
|
|
30
|
+
"tsup": "^8.0.0",
|
|
31
|
+
"vitest": "^2.0.0"
|
|
32
|
+
},
|
|
33
|
+
"keywords": ["opencode", "memory", "sqlite", "fts5", "bm25", "checkpoint"],
|
|
34
|
+
"license": "MIT"
|
|
35
|
+
}
|