opencode-mempalace-persistence 1.1.1 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -2
- package/dist/index.d.ts +6 -0
- package/dist/index.js +79 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# opencode-mempalace-persistence
|
|
2
2
|
|
|
3
|
-
> **Community plugin** — not officially maintained by the MemPalace team. Fully open source, ~
|
|
3
|
+
> **Community plugin** — not officially maintained by the MemPalace team. Fully open source, ~200 lines of TypeScript.
|
|
4
4
|
|
|
5
5
|
An OpenCode plugin that automatically saves every conversation to MemPalace and uses stored memory to provide better, context-aware responses. Real-time, zero cron, zero external scripts.
|
|
6
6
|
|
|
@@ -104,7 +104,47 @@ This is loaded automatically at session start via `instructions` in opencode.jso
|
|
|
104
104
|
}
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
### 5.
|
|
107
|
+
### 5. (Optional) Auto-inject memory context
|
|
108
|
+
|
|
109
|
+
By default, the model must search MemPalace on its own via AGENTS.md instructions. For models with poor tool-use discipline, enable auto-injection — the plugin injects identity + relevant memories directly into the prompt:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"plugin": ["opencode-mempalace-persistence"],
|
|
114
|
+
"mempalace": {
|
|
115
|
+
"autoInjectContext": true
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
When enabled, on every user message:
|
|
121
|
+
- **First message**: Injects your identity from `~/.mempalace/identity.txt`
|
|
122
|
+
- **Every message**: Runs `mempalace search` and injects relevant results
|
|
123
|
+
|
|
124
|
+
The context is guaranteed regardless of model discipline. Since the plugin handles search and identity injection, you can simplify AGENTS.md — keep only the Knowledge Graph management steps:
|
|
125
|
+
|
|
126
|
+
```markdown
|
|
127
|
+
# Memory & Knowledge instructions
|
|
128
|
+
|
|
129
|
+
### Step 1 — Query Knowledge Graph
|
|
130
|
+
Call `mempalace_mempalace_kg_query` for entity "user" to retrieve relevant facts.
|
|
131
|
+
|
|
132
|
+
### Step 2 — Record Knowledge Graph facts
|
|
133
|
+
After responding, call `mempalace_mempalace_kg_add` for any new facts found.
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
And remove `"~/.mempalace/identity.txt"` from `instructions` in your opencode.json — the plugin injects it automatically.
|
|
137
|
+
|
|
138
|
+
**Summary: with vs without autoInjectContext**
|
|
139
|
+
|
|
140
|
+
| Feature | Without (default) | With `autoInjectContext: true` |
|
|
141
|
+
|---------|:-:|:-:|
|
|
142
|
+
| Memory search | Model calls `mempalace_search` (AGENTS.md) | Plugin injects automatically |
|
|
143
|
+
| Identity | `instructions: ["identity.txt"]` | Plugin injects automatically |
|
|
144
|
+
| AGENTS.md needed | Full (search + KG + identity) | Minimal (KG management only) |
|
|
145
|
+
| Depends on model discipline | Yes | No |
|
|
146
|
+
|
|
147
|
+
### 6. MemPalace (if not already installed)
|
|
108
148
|
|
|
109
149
|
```bash
|
|
110
150
|
# Install (requires mempalace>=3.3.5 for HNSW corruption fix)
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,12 @@ declare const _default: () => Promise<{
|
|
|
12
12
|
message: import("@opencode-ai/sdk").UserMessage;
|
|
13
13
|
parts: import("@opencode-ai/sdk").Part[];
|
|
14
14
|
}) => Promise<void>;
|
|
15
|
+
"experimental.chat.messages.transform": (_input: {}, output: {
|
|
16
|
+
messages: {
|
|
17
|
+
info: import("@opencode-ai/sdk").Message;
|
|
18
|
+
parts: import("@opencode-ai/sdk").Part[];
|
|
19
|
+
}[];
|
|
20
|
+
}) => Promise<void>;
|
|
15
21
|
event: ({ event }: any) => Promise<void>;
|
|
16
22
|
}>;
|
|
17
23
|
export default _default;
|
package/dist/index.js
CHANGED
|
@@ -8,10 +8,14 @@ const VENV_PYTHON = join(HOME, ".local/share/pipx/venvs/mempalace/bin/python3");
|
|
|
8
8
|
const MEMPALACE_BIN = join(HOME, ".local/bin/mempalace");
|
|
9
9
|
const OPENCODE_DB = join(HOME, ".local/share/opencode/opencode.db");
|
|
10
10
|
const STATE_FILE = join(HOME, ".mempalace/sync_state.json");
|
|
11
|
+
const OPENCODE_CONFIG = join(HOME, ".config/opencode/opencode.json");
|
|
12
|
+
const IDENTITY_FILE = join(HOME, ".mempalace/identity.txt");
|
|
11
13
|
const OUT_DIR = "/tmp/oc-sessions";
|
|
12
14
|
const TMP_SCRIPT = "/tmp/oc-plugin-query.py";
|
|
13
15
|
const DEBUG = !!process.env.OPENCODE_MEMPALACE_DEBUG;
|
|
14
16
|
const LOG_FILE = "/tmp/opencode-mempalace.log";
|
|
17
|
+
const MAX_INJECT_CHARS = 900;
|
|
18
|
+
const MAX_SEARCH_RESULTS = 3;
|
|
15
19
|
function log(msg) {
|
|
16
20
|
if (!DEBUG)
|
|
17
21
|
return;
|
|
@@ -23,6 +27,7 @@ function log(msg) {
|
|
|
23
27
|
}
|
|
24
28
|
let miningLock = false;
|
|
25
29
|
let lastSyncTs = 0;
|
|
30
|
+
let wakeupDone = false;
|
|
26
31
|
function runPython(code) {
|
|
27
32
|
writeFileSync(TMP_SCRIPT, code);
|
|
28
33
|
try {
|
|
@@ -38,6 +43,40 @@ function hasText(parts) {
|
|
|
38
43
|
.map((p) => p.text.trim())
|
|
39
44
|
.join("\n");
|
|
40
45
|
}
|
|
46
|
+
function isAutoInjectEnabled() {
|
|
47
|
+
try {
|
|
48
|
+
const raw = readFileSync(OPENCODE_CONFIG, "utf-8");
|
|
49
|
+
const cfg = JSON.parse(raw);
|
|
50
|
+
return !!cfg?.mempalace?.autoInjectContext;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function readIdentity() {
|
|
57
|
+
if (!existsSync(IDENTITY_FILE))
|
|
58
|
+
return "";
|
|
59
|
+
try {
|
|
60
|
+
return readFileSync(IDENTITY_FILE, "utf-8").trim();
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function mempalaceSearch(query) {
|
|
67
|
+
try {
|
|
68
|
+
const out = execSync(`${MEMPALACE_BIN} search "${query.replace(/"/g, '\\"')}" --results ${MAX_SEARCH_RESULTS}`, {
|
|
69
|
+
encoding: "utf-8",
|
|
70
|
+
timeout: 15000,
|
|
71
|
+
}).trim();
|
|
72
|
+
if (!out || out.includes("No results"))
|
|
73
|
+
return "";
|
|
74
|
+
return out.slice(0, MAX_INJECT_CHARS);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return "";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
41
80
|
function getLastSync() {
|
|
42
81
|
if (!existsSync(STATE_FILE))
|
|
43
82
|
return 0;
|
|
@@ -174,7 +213,9 @@ print(json.dumps(texts))
|
|
|
174
213
|
}
|
|
175
214
|
export default (async () => {
|
|
176
215
|
mkdirSync(OUT_DIR, { recursive: true });
|
|
177
|
-
|
|
216
|
+
const autoInject = isAutoInjectEnabled();
|
|
217
|
+
const identity = readIdentity();
|
|
218
|
+
log(`loaded (autoInjectContext: ${autoInject})`);
|
|
178
219
|
return {
|
|
179
220
|
"chat.message": async (_input, output) => {
|
|
180
221
|
const role = output.message.role;
|
|
@@ -186,6 +227,43 @@ export default (async () => {
|
|
|
186
227
|
log("user msg - queue sync");
|
|
187
228
|
setTimeout(() => dbSync(), 500);
|
|
188
229
|
},
|
|
230
|
+
"experimental.chat.messages.transform": async (_input, output) => {
|
|
231
|
+
if (!autoInject)
|
|
232
|
+
return;
|
|
233
|
+
if (!output?.messages?.length)
|
|
234
|
+
return;
|
|
235
|
+
const lastUser = [...output.messages].reverse().find((m) => m.info?.role === "user");
|
|
236
|
+
if (!lastUser)
|
|
237
|
+
return;
|
|
238
|
+
const query = hasText(lastUser.parts || []);
|
|
239
|
+
if (!query)
|
|
240
|
+
return;
|
|
241
|
+
const injectParts = [];
|
|
242
|
+
if (!wakeupDone) {
|
|
243
|
+
wakeupDone = true;
|
|
244
|
+
if (identity) {
|
|
245
|
+
injectParts.push({
|
|
246
|
+
id: `mp-identity-${Date.now()}`,
|
|
247
|
+
type: "text",
|
|
248
|
+
synthetic: true,
|
|
249
|
+
text: `[MemPalace Identity]\n${identity}\n[/MemPalace Identity]`,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const memories = mempalaceSearch(query);
|
|
254
|
+
if (memories) {
|
|
255
|
+
injectParts.push({
|
|
256
|
+
id: `mp-recall-${Date.now()}`,
|
|
257
|
+
type: "text",
|
|
258
|
+
synthetic: true,
|
|
259
|
+
text: `[MemPalace Recall]\n${memories}\n[/MemPalace Recall]`,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (injectParts.length > 0) {
|
|
263
|
+
lastUser.parts.push(...injectParts);
|
|
264
|
+
log(`injected ${injectParts.length} context blocks`);
|
|
265
|
+
}
|
|
266
|
+
},
|
|
189
267
|
event: async ({ event }) => {
|
|
190
268
|
if (event?.type !== "session.idle")
|
|
191
269
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-mempalace-persistence",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "OpenCode plugin — auto-sync conversations to MemPalace memory in real-time. No forced wings, KG extraction via MCP tools.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|