@rvboris/opencode-mempalace 0.4.0 → 0.5.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/README.md CHANGED
@@ -14,7 +14,7 @@ Before every reply, the plugin searches your memory for relevant context. After
14
14
 
15
15
  ```
16
16
  You: "What build tool does this project use?"
17
- AI: [searches memory] → "Bun. You decided that on April 10."
17
+ AI: [searches memory] → "Bun. This project uses Bun."
18
18
  ```
19
19
 
20
20
  ### The result
@@ -43,6 +43,57 @@ Add to `opencode.json`:
43
43
 
44
44
  That's it. Memory search, autosave, and both tools are active immediately.
45
45
 
46
+ ## How it works
47
+
48
+ The plugin runs inside OpenCode as hooks + tools. A thin Python bridge calls the local `mempalace` package, which stores everything in ChromaDB with on-device `embeddinggemma-300m` embeddings. No cloud, no API keys.
49
+
50
+ ```mermaid
51
+ flowchart TD
52
+ subgraph OC["OpenCode"]
53
+ U["User"]
54
+ M["AI Model"]
55
+ end
56
+
57
+ subgraph PL["TypeScript plugin (this repo)"]
58
+ H1["system.transform hook — injects: search memory first"]
59
+ H2["event hook — autosave on idle / compact / close"]
60
+ T["mempalace_memory (9 modes) + mempalace_status tools"]
61
+ HUD["TUI HUD — session stats badge"]
62
+ end
63
+
64
+ subgraph BR["Python bridge"]
65
+ A["mempalace_adapter.py — spawned per call"]
66
+ end
67
+
68
+ subgraph MP["mempalace package (local)"]
69
+ SRV["mcp_server / convo_miner"]
70
+ DB[("ChromaDB")]
71
+ EMB["embeddinggemma-300m ONNX"]
72
+ end
73
+
74
+ SF[("opencode_status.json")]
75
+
76
+ U -->|"message"| H1
77
+ H1 -->|"retrieval nudge"| M
78
+ M -->|"mempalace_memory search"| T
79
+ T --> A --> SRV --> EMB
80
+ SRV --> DB
81
+ A --> T --> M
82
+ M -->|"answers with context"| U
83
+ T -.->|"counters"| SF
84
+ HUD -.->|"reads"| SF
85
+ M -.->|"session idle"| H2
86
+ H2 -.->|"mine_messages"| A
87
+ ```
88
+
89
+ **Retrieval** — before each reply, the `system.transform` hook nudges the model to search memory first. The model calls `mempalace_memory [search]`, the bridge forwards it to `mempalace`, and results (vector + BM25) come back as context for the answer.
90
+
91
+ **Autosave** — on session idle, compaction, or close, the `event` hook mines the transcript for durable facts and saves them to the right memory area via `mine_messages`. Counters are written to `opencode_status.json`, which the TUI HUD reads.
92
+
93
+ **Keyword save** — when the user says "remember this" / "note that", the plugin arms a save instruction so the model persists the fact immediately via `mempalace_memory [save]`.
94
+
95
+ The plugin talks to `mempalace` through a local Python bridge (`bridge/mempalace_adapter.py`), spawned per call — it does **not** require the MemPalace MCP server.
96
+
46
97
  ## Features
47
98
 
48
99
  ### Hidden retrieval
@@ -51,18 +102,27 @@ Before each answer, the plugin injects a search instruction so the model checks
51
102
 
52
103
  ### Background autosave
53
104
 
54
- On session idle, compaction, or close, the plugin mines the conversation transcript for durable facts and saves them to the right memory area automatically.
105
+ On session idle, compaction, or close, the plugin mines the conversation transcript for durable facts and saves them to the right memory area automatically. Low-signal fragments are filtered before mining, so prompt leftovers like `re.`, `ls>`, or mostly punctuation are skipped instead of becoming junk memory.
106
+
107
+ ### Reliable local bridge
108
+
109
+ Write-like operations (`save`, autosave mining, diary writes, graph writes, checkpoints, deletes) are serialized through the adapter and retried on MemPalace palace-lock contention (`held by PID`). Search calls still run without that write queue.
55
110
 
56
111
  ### `mempalace_memory` — the one tool
57
112
 
58
- Four modes, one interface:
113
+ Nine modes, one interface:
59
114
 
60
115
  | Mode | Purpose |
61
116
  |---|---|
62
117
  | `save` | Store a preference, fact, or decision |
63
- | `search` | Find relevant memory by query |
118
+ | `search` | Find relevant memory by query (optional `source_file` filter) |
64
119
  | `kg_add` | Add a structured fact to the knowledge graph |
65
120
  | `diary_write` | Save a short work note |
121
+ | `checkpoint` | Batch-save multiple items + optional diary in one call |
122
+ | `delete` | Remove a memory by drawer ID |
123
+ | `delete_by_source` | Bulk-remove memories by source file (dry-run by default) |
124
+ | `kg_query` | Search the knowledge graph for an entity's relationships |
125
+ | `diary_read` | Read recent diary entries |
66
126
 
67
127
  Examples:
68
128
 
@@ -78,6 +138,14 @@ mempalace_memory mode: search scope: project room: decisions query: build to
78
138
  mempalace_memory mode: kg_add subject: my-repo predicate: uses object: bun
79
139
  ```
80
140
 
141
+ ```text
142
+ mempalace_memory mode: checkpoint items: [{"wing":"wing_user","room":"preferences","content":"likes dark mode"},{"wing":"wing_project","room":"decisions","content":"uses bun"}]
143
+ ```
144
+
145
+ ```text
146
+ mempalace_memory mode: delete_by_source source_file: /data/import.jsonl dry_run: true
147
+ ```
148
+
81
149
  ### `mempalace_status` — visible proof
82
150
 
83
151
  Check whether the plugin is actually helping:
@@ -93,10 +161,26 @@ Shows retrieval hit rate, last autosave outcome, memory previews, and cumulative
93
161
  A compact session stats line appears in the OpenCode prompt area:
94
162
 
95
163
  ```
96
- MEM hits 3 · saved 2 · failed 0 · writes 1
164
+ MEM helps 3
165
+ MEM cited 2
166
+ MEM found 5
167
+ MEM no hits
168
+ MEM searched
169
+ MEM quiet
170
+ MEM helps 1 · fail 1
97
171
  ```
98
172
 
99
- Color-coded indicators for `SKIPPED` and `FAILED` states. Requires a `tui.json` entry (see below).
173
+ - `MEM helps N` memory improved or saved time (the most useful verdicts)
174
+ - `MEM cited N` — memory was mentioned but did not change the answer
175
+ - `MEM no help` — retrieval happened but had no effect
176
+ - `MEM unknown` — model omitted the verdict tag
177
+ - `MEM found N` — retrieval returned N memories and no judge verdict is recorded yet
178
+ - `MEM no hits` — retrieval ran and returned no memories
179
+ - `MEM searched` — retrieval ran, but result count is unavailable
180
+ - `MEM quiet` — no retrieval activity yet
181
+ - `· fail N` / `· skip N` — shown only when autosave has errors
182
+
183
+ The HUD combines retrieval evidence with a **judge signal**. When the model reports `[memory: verdict]`, the plugin parses it after each turn and strips it before saving. If no verdict is available, retrieval results still show as `found`, `no hits`, or `searched`. Requires a `tui.json` entry (see below).
100
184
 
101
185
  ## Memory areas
102
186
 
@@ -154,7 +238,7 @@ To enable the prompt-area stats display, add a `tui.json` in your OpenCode confi
154
238
  {
155
239
  "$schema": "https://opencode.ai/tui.json",
156
240
  "plugin": [
157
- "file:///path/to/mempalace-autosave/plugin/tui/index.tsx"
241
+ "file:///path/to/opencode-mempalace/plugin/tui/index.tsx"
158
242
  ]
159
243
  }
160
244
  ```
@@ -168,21 +252,6 @@ Or when installed from npm, use the package entry:
168
252
  }
169
253
  ```
170
254
 
171
- ## How it works
172
-
173
- ```
174
- User message
175
- → system hook injects "search memory first" instruction
176
- → model calls mempalace_memory [search]
177
- → results inform the answer
178
-
179
- Session ends / idles
180
- → event hook mines transcript
181
- → Python adapter saves durable facts via MemPalace
182
- ```
183
-
184
- The plugin uses a local Python bridge (`bridge/mempalace_adapter.py`) to communicate with MemPalace. It does **not** require the MemPalace MCP server.
185
-
186
255
  ## Compatibility
187
256
 
188
257
  | Requirement | Version |
@@ -194,14 +263,14 @@ The plugin uses a local Python bridge (`bridge/mempalace_adapter.py`) to communi
194
263
 
195
264
  ## Project docs
196
265
 
197
- - [Changelog](./CHANGELOG.md) — release history
266
+ - [Changelog](./CHANGELOG.md) — release notes
198
267
  - [Contributing](./CONTRIBUTING.md) — changelog rules
199
268
 
200
269
  ## Local development
201
270
 
202
271
  ```bash
203
272
  git clone https://github.com/rvboris/opencode-mempalace.git
204
- cd opencode-mempalace/mempalace-autosave
273
+ cd opencode-mempalace
205
274
  npm install
206
275
  npm run build
207
276
  ```
@@ -210,7 +279,7 @@ Load from source in `opencode.json`:
210
279
 
211
280
  ```jsonc
212
281
  {
213
- "plugin": ["file:///ABSOLUTE/PATH/TO/mempalace-autosave/plugin/index.ts"]
282
+ "plugin": ["file:///ABSOLUTE/PATH/TO/opencode-mempalace/plugin/index.ts"]
214
283
  }
215
284
  ```
216
285
 
package/README.ru.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  ```
16
16
  Ты: "Какую систему сборки мы используем?"
17
- ИИ: [ищет в памяти] → "Bun. Это решение приняли 10 апреля."
17
+ ИИ: [ищет в памяти] → "Bun. Этот проект использует Bun."
18
18
  ```
19
19
 
20
20
  ### Результат
@@ -43,6 +43,57 @@ mempalace init ~/.mempalace/palace
43
43
 
44
44
  Готово. Поиск по памяти, автосохранение и оба инструмента активны сразу.
45
45
 
46
+ ## Как это работает
47
+
48
+ Плагин работает внутри OpenCode как набор хуков и тулов. Тонкий Python-мост вызывает локальный пакет `mempalace`, который хранит всё в ChromaDB с локальными эмбеддингами `embeddinggemma-300m` на устройстве. Без облака, без API-ключей.
49
+
50
+ ```mermaid
51
+ flowchart TD
52
+ subgraph OC["OpenCode"]
53
+ U["Пользователь"]
54
+ M["ИИ-модель"]
55
+ end
56
+
57
+ subgraph PL["TypeScript-плагин (этот репо)"]
58
+ H1["system.transform хук — подставляет: сначала ищи в памяти"]
59
+ H2["event хук — автосохранение при простое / компактизации / закрытии"]
60
+ T["тулы mempalace_memory (9 режимов) + mempalace_status"]
61
+ HUD["TUI HUD — бейдж статистики сессии"]
62
+ end
63
+
64
+ subgraph BR["Python-мост"]
65
+ A["mempalace_adapter.py — запускается на каждый вызов"]
66
+ end
67
+
68
+ subgraph MP["пакет mempalace (локально)"]
69
+ SRV["mcp_server / convo_miner"]
70
+ DB[("ChromaDB")]
71
+ EMB["embeddinggemma-300m ONNX"]
72
+ end
73
+
74
+ SF[("opencode_status.json")]
75
+
76
+ U -->|"сообщение"| H1
77
+ H1 -->|"подсказка поиска"| M
78
+ M -->|"mempalace_memory search"| T
79
+ T --> A --> SRV --> EMB
80
+ SRV --> DB
81
+ A --> T --> M
82
+ M -->|"ответ с контекстом"| U
83
+ T -.->|"счётчики"| SF
84
+ HUD -.->|"читает"| SF
85
+ M -.->|"простой сессии"| H2
86
+ H2 -.->|"mine_messages"| A
87
+ ```
88
+
89
+ **Поиск (retrieval)** — перед каждым ответом хук `system.transform` подталкивает модель сначала проверить память. Модель вызывает `mempalace_memory [search]`, мост прокидывает вызов в `mempalace`, результаты (векторный + BM25-поиск) возвращаются как контекст для ответа.
90
+
91
+ **Автосохранение** — при простое сессии, компактизации или закрытии хук `event` анализирует транскрипт, извлекает устойчивые факты и сохраняет их в нужную область памяти через `mine_messages`. Счётчики пишутся в `opencode_status.json`, который читает TUI HUD.
92
+
93
+ **Сохранение по ключевому слову** — когда пользователь говорит «запомни это» / «отметь», плагин активирует инструкцию сохранения, чтобы модель сразу сохранила факт через `mempalace_memory [save]`.
94
+
95
+ Плагин обращается к `mempalace` через локальный Python-мост (`bridge/mempalace_adapter.py`), запускаемый на каждый вызов, — MCP-сервер MemPalace ему **не нужен**.
96
+
46
97
  ## Возможности
47
98
 
48
99
  ### Скрытый поиск
@@ -51,18 +102,27 @@ mempalace init ~/.mempalace/palace
51
102
 
52
103
  ### Фоновое автосохранение
53
104
 
54
- При простое сессии, компактизации или завершении — плагин анализирует транскрипт, извлекает устойчивые факты и сохраняет их в нужную область памяти.
105
+ При простое сессии, компактизации или завершении — плагин анализирует транскрипт, извлекает устойчивые факты и сохраняет их в нужную область памяти. Низкосигнальные фрагменты фильтруются до майнинга, поэтому остатки вроде `re.`, `ls>` или почти одна пунктуация не становятся мусорной памятью.
106
+
107
+ ### Надёжный локальный мост
108
+
109
+ Операции записи (`save`, автосохранение, diary, граф, checkpoints, удаления) проходят через очередь адаптера и повторяются при конфликте palace-lock (`held by PID`). Поиск выполняется без этой очереди.
55
110
 
56
111
  ### `mempalace_memory` — единый инструмент
57
112
 
58
- Четыре режима, один интерфейс:
113
+ Девять режимов, один интерфейс:
59
114
 
60
115
  | Режим | Назначение |
61
116
  |---|---|
62
117
  | `save` | Сохранить предпочтение, факт или решение |
63
- | `search` | Найти релевантную память по запросу |
118
+ | `search` | Найти релевантную память по запросу (опциональный фильтр `source_file`) |
64
119
  | `kg_add` | Добавить структурированный факт в граф знаний |
65
120
  | `diary_write` | Записать короткую рабочую заметку |
121
+ | `checkpoint` | Пакетное сохранение нескольких записей + опциональной diary за один вызов |
122
+ | `delete` | Удалить память по ID записи |
123
+ | `delete_by_source` | Массовое удаление памяти по файлу-источнику (dry-run по умолчанию) |
124
+ | `kg_query` | Поиск по графу знаний — связи сущности |
125
+ | `diary_read` | Чтение недавних записей diary |
66
126
 
67
127
  Примеры:
68
128
 
@@ -78,6 +138,14 @@ mempalace_memory mode: search scope: project room: decisions query: сист
78
138
  mempalace_memory mode: kg_add subject: my-repo predicate: uses object: bun
79
139
  ```
80
140
 
141
+ ```text
142
+ mempalace_memory mode: checkpoint items: [{"wing":"wing_user","room":"preferences","content":"любит тёмную тему"},{"wing":"wing_project","room":"decisions","content":"использует bun"}]
143
+ ```
144
+
145
+ ```text
146
+ mempalace_memory mode: delete_by_source source_file: /data/import.jsonl dry_run: true
147
+ ```
148
+
81
149
  ### `mempalace_status` — видимое доказательство
82
150
 
83
151
  Проверь, реально ли плагин помогает:
@@ -93,10 +161,26 @@ mempalace_status
93
161
  Компактная строка статистики текущей сессии в области ввода OpenCode:
94
162
 
95
163
  ```
96
- MEM hits 3 · saved 2 · failed 0 · writes 1
164
+ MEM helps 3
165
+ MEM cited 2
166
+ MEM found 5
167
+ MEM no hits
168
+ MEM searched
169
+ MEM quiet
170
+ MEM helps 1 · fail 1
97
171
  ```
98
172
 
99
- Цветовые индикаторы для `SKIPPED` и `FAILED`. Требует записи в `tui.json` (см. ниже).
173
+ - `MEM helps N` память улучшила ответ или сэкономила время (самые полезные вердикты)
174
+ - `MEM cited N` — память была упомянута, но ответ не изменился
175
+ - `MEM no help` — поиск был, но без эффекта
176
+ - `MEM unknown` — модель не поставила тег с вердиктом
177
+ - `MEM found N` — поиск вернул N записей, но judge-вердикт ещё не записан
178
+ - `MEM no hits` — поиск был, но ничего не нашёл
179
+ - `MEM searched` — поиск был, но количество результатов недоступно
180
+ - `MEM quiet` — поиск по памяти ещё не выполнялся
181
+ - `· fail N` / `· skip N` — показываются только при ошибках автосохранения
182
+
183
+ HUD совмещает факт поиска и **judge-сигнал**. Когда модель сообщает `[memory: verdict]`, плагин парсит тег после хода и вырезает перед сохранением. Если вердикта нет, результаты поиска всё равно видны как `found`, `no hits` или `searched`. Требует записи в `tui.json` (см. ниже).
100
184
 
101
185
  ## Области памяти
102
186
 
@@ -154,7 +238,7 @@ MEM hits 3 · saved 2 · failed 0 · writes 1
154
238
  {
155
239
  "$schema": "https://opencode.ai/tui.json",
156
240
  "plugin": [
157
- "file:///путь/до/mempalace-autosave/plugin/tui/index.tsx"
241
+ "file:///путь/до/opencode-mempalace/plugin/tui/index.tsx"
158
242
  ]
159
243
  }
160
244
  ```
@@ -168,21 +252,6 @@ MEM hits 3 · saved 2 · failed 0 · writes 1
168
252
  }
169
253
  ```
170
254
 
171
- ## Как это работает
172
-
173
- ```
174
- Сообщение пользователя
175
- → системный хук подставляет «сначала проверь память»
176
- → модель вызывает mempalace_memory [search]
177
- → результаты влияют на ответ
178
-
179
- Сессия завершается / простаивает
180
- → хук событий анализирует транскрипт
181
- → Python-адаптер сохраняет устойчивые факты через MemPalace
182
- ```
183
-
184
- Плагин использует локальную Python-прослойку (`bridge/mempalace_adapter.py`) для работы с MemPalace. Ему **не нужен** MCP-сервер MemPalace.
185
-
186
255
  ## Совместимость
187
256
 
188
257
  | Требование | Версия |
@@ -194,14 +263,14 @@ MEM hits 3 · saved 2 · failed 0 · writes 1
194
263
 
195
264
  ## Документация проекта
196
265
 
197
- - [Changelog](./CHANGELOG.md) — история изменений
266
+ - [Changelog](./CHANGELOG.md) — заметки к релизам
198
267
  - [Contributing](./CONTRIBUTING.md) — правила ведения changelog
199
268
 
200
269
  ## Локальная разработка
201
270
 
202
271
  ```bash
203
272
  git clone https://github.com/rvboris/opencode-mempalace.git
204
- cd opencode-mempalace/mempalace-autosave
273
+ cd opencode-mempalace
205
274
  npm install
206
275
  npm run build
207
276
  ```
@@ -210,7 +279,7 @@ npm run build
210
279
 
211
280
  ```jsonc
212
281
  {
213
- "plugin": ["file:///ABSOLUTE/PATH/TO/mempalace-autosave/plugin/index.ts"]
282
+ "plugin": ["file:///ABSOLUTE/PATH/TO/opencode-mempalace/plugin/index.ts"]
214
283
  }
215
284
  ```
216
285
 
@@ -37,6 +37,7 @@ def main() -> int:
37
37
  limit=payload.get("limit", 5),
38
38
  wing=payload.get("wing"),
39
39
  room=payload.get("room"),
40
+ source_file=payload.get("source_file"),
40
41
  )
41
42
  elif mode == "save":
42
43
  result = mcp_server.tool_add_drawer(
@@ -78,6 +79,31 @@ def main() -> int:
78
79
  "mode": "mine_messages",
79
80
  "wing": payload.get("wing"),
80
81
  }
82
+ elif mode == "delete":
83
+ result = mcp_server.tool_delete_drawer(drawer_id=payload["drawer_id"])
84
+ elif mode == "delete_by_source":
85
+ result = mcp_server.tool_delete_by_source(
86
+ source_file=payload["source_file"],
87
+ dry_run=payload.get("dry_run", True),
88
+ )
89
+ elif mode == "kg_query":
90
+ result = mcp_server.tool_kg_query(
91
+ entity=payload["entity"],
92
+ as_of=payload.get("as_of"),
93
+ direction=payload.get("direction", "both"),
94
+ )
95
+ elif mode == "diary_read":
96
+ result = mcp_server.tool_diary_read(
97
+ agent_name=payload.get("agent_name", "opencode"),
98
+ last_n=payload.get("last_n", 10),
99
+ wing=payload.get("wing", ""),
100
+ )
101
+ elif mode == "checkpoint":
102
+ result = mcp_server.tool_checkpoint(
103
+ items=payload["items"],
104
+ diary=payload.get("diary"),
105
+ dedup_threshold=payload.get("dedup_threshold", 0.9),
106
+ )
81
107
  else:
82
108
  result = {"success": False, "error": f"Unknown mode: {mode}"}
83
109
  except Exception as error:
@@ -1,12 +1,12 @@
1
- import { AutosaveReason, AutosaveStatus, buildMessageSnapshot, getMessageSnapshot, getSessionState, markAutosaveComplete, markKeywordSavePending, markFailed, markRetrievalPending, setMessageSnapshot, shouldScheduleAutosave, } from "../lib/autosave";
1
+ import { AutosaveReason, AutosaveStatus, buildAutosaveMiningTranscript, buildMessageSnapshot, getMessageSnapshot, getSessionState, markAutosaveComplete, markKeywordSavePending, markFailed, markRetrievalPending, setMessageSnapshot, shouldScheduleAutosave, } from "../lib/autosave";
2
2
  import { executeAdapter } from "../lib/adapter";
3
3
  import { loadConfig } from "../lib/config";
4
4
  import { COMPACTION_CONTEXT_MESSAGE, DEFAULT_AGENT_NAME, LOG_MESSAGES } from "../lib/constants";
5
- import { sanitizeText } from "../lib/derive";
5
+ import { sanitizeText, stripJudgeTag, parseJudgeTag } from "../lib/derive";
6
6
  import { getProjectName, loadSessionMessages } from "../lib/opencode";
7
7
  import { redactSecrets } from "../lib/privacy";
8
8
  import { getProjectScope } from "../lib/scope";
9
- import { recordAutosave } from "../lib/status";
9
+ import { recordAutosave, recordRetrievalJudge } from "../lib/status";
10
10
  import { writeLog } from "../lib/log";
11
11
  import { SESSION_EVENT_TYPES } from "../lib/types";
12
12
  const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -61,6 +61,16 @@ export const eventHooks = (ctx) => {
61
61
  }
62
62
  if (!isAutosaveTriggerEventType(event.type))
63
63
  return;
64
+ // Parse judge tag from last assistant message before anything else.
65
+ // Only when a retrieval happened this turn (retrievalPending was set).
66
+ const state = getSessionState(sessionId);
67
+ if (state.retrievalPending) {
68
+ const snapshot = getMessageSnapshot(sessionId);
69
+ if (snapshot) {
70
+ const verdict = (parseJudgeTag(snapshot.transcript) ?? "unknown");
71
+ await recordRetrievalJudge({ sessionId, verdict });
72
+ }
73
+ }
64
74
  await writeLog("INFO", LOG_MESSAGES.autosaveTriggerReceived, {
65
75
  eventType: event.type,
66
76
  sessionId,
@@ -103,8 +113,9 @@ export const eventHooks = (ctx) => {
103
113
  });
104
114
  return;
105
115
  }
106
- const sanitizedTranscript = sanitizeText(redactSecrets(transcript));
107
- if (!sanitizedTranscript.trim()) {
116
+ const sanitizedTranscript = sanitizeText(redactSecrets(stripJudgeTag(transcript)));
117
+ const miningTranscript = buildAutosaveMiningTranscript(sanitizedTranscript);
118
+ if (!miningTranscript) {
108
119
  markAutosaveComplete(sessionId, userDigest, transcriptDigest, AutosaveStatus.Noop);
109
120
  await recordAutosave({
110
121
  sessionId,
@@ -118,7 +129,7 @@ export const eventHooks = (ctx) => {
118
129
  const wing = getProjectScope(getProjectName(ctx.project), config.projectWingPrefix).wing;
119
130
  const result = await executeAdapter(ctx.$, {
120
131
  mode: "mine_messages",
121
- transcript: sanitizedTranscript,
132
+ transcript: miningTranscript,
122
133
  wing,
123
134
  extract_mode: config.autoMineExtractMode,
124
135
  agent: DEFAULT_AGENT_NAME,
@@ -1,2 +1,23 @@
1
+ import { spawn as nodeSpawn } from "node:child_process";
1
2
  import type { AdapterRequest, AdapterResponse } from "./types";
3
+ /**
4
+ * Resolve the Python interpreter that owns the `mempalace` package.
5
+ *
6
+ * Resolution order:
7
+ * 1. `MEMPALACE_ADAPTER_PYTHON` env (explicit override).
8
+ * 2. The shebang of the `mempalace` CLI on $PATH — a console_script always
9
+ * points at the interpreter that installed the package (pipx, uv-tool,
10
+ * pip --user, or a venv), so this is the most reliable auto-detection.
11
+ * 3. `python3` then `python` on $PATH (best-effort; may still lack mempalace).
12
+ *
13
+ * Returns null if nothing usable was found.
14
+ */
15
+ export declare const resolvePython: () => string | null;
16
+ /** Test-only: clear the cached interpreter so env/PATH changes are re-read. */
17
+ export declare const resetPythonResolver: () => void;
18
+ declare let adapterDelay: (ms: number) => Promise<void>;
19
+ export declare const setAdapterDelayForTests: (delay: typeof adapterDelay) => void;
20
+ export declare const setAdapterSpawnForTests: (spawn: typeof nodeSpawn) => void;
21
+ export declare const resetAdapterTestHooks: () => void;
2
22
  export declare const executeAdapter: (_shell: unknown, payload: AdapterRequest, retries?: number) => Promise<AdapterResponse>;
23
+ export {};