Skip to content
32 changes: 32 additions & 0 deletions .github/i18n/README.ar.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,38 @@ memory({ mode: "import", inputPath: "./memories.json" });
`chatMessage.maxMemories` على `0` إلى تعطيل حقن ذكريات المشروع، مع استمرار إمكانية
حقن ملف المستخدم عند تفعيله.

### Update-on-capture (اختياري)

مسار اختياري لإبقاء الذكريات حديثة عند تغيّر الحقائق. عند
`updateOnCaptureEnabled: true` يضمّن الالتقاط التلقائي الملخص الجديد، ويبحث عن
ذكريات مشابهة فوق `updateOnCaptureSimilarityThreshold` (الافتراضي `0.75`)، ويترك
لنموذج الالتقاط الاختيار بين `add` أو `update` (إعادة كتابة في المكان) أو
`supersede` (حفظ الجديد وحذف القديم soft-delete إلى سلة المهملات). تُتخطى الذكريات
المثبّتة. الافتراضي معطّل ليبقى السلوك السابق add-only.

```jsonc
"updateOnCaptureEnabled": true,
"updateOnCaptureSimilarityThreshold": 0.75,
"updateOnCaptureMaxCandidates": 5,
```

ملاحظة: مع `Xenova/nomic-embed-text-v1` غالبًا ما يحسّن
`embeddingUseTaskPrefixes: true` (وخفض `similarityThreshold` للاستدعاء عند الحاجة)
جودة المطابقة للذكريات الأطول. تبقى البادئات opt-in افتراضيًا.

### Dreaming (توحيد ذاكرة اختياري)

تمريرة خلفية اختيارية تجد الذكريات **ذات الصلة** عبر
`dreaming.relatedThreshold` (الافتراضي `0.75`، مستقل عن
`deduplicationSimilarityThreshold`)، وتطلب من `opencodeProvider` /
`opencodeModel` تنفيذ `merge` / `update` / `supersede` / `keep` / `flag`
(تناقضات)، وتحذف الصفوف المستبدلة soft-delete إلى سلة قابلة للاستعادة. الذكريات
المثبّتة لا تُمس. مع `dreaming.crossProject: true` (أو
`memory.defaultScope: "all-projects"`) تُجمَّع عبر وسوم المشاريع؛ نطاق المستخدم
يبقى منفصلًا. الإعداد تحت `dreaming` في `opencode-mem.jsonc` (`enabled` افتراضي
`false`). واجهة الويب تعرض **Trash** و**Dream**. تُحذف الصفوف soft-deleted نهائيًا
بعد `dreaming.trashRetentionDays` أثناء التنظيف.

### اختيار / إعداد التضمينات

تشغّل التضمينات البحث بالتشابه للذكريات وملف المستخدم. أعدها في نفس الملف (`~/.config/opencode/opencode-mem.jsonc`). **لا توجد خلفية MLX** — التضمينات المحلية تستخدم `@huggingface/transformers` مع ONNX، وليس Apple MLX.
Expand Down
34 changes: 28 additions & 6 deletions .github/i18n/README.de.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,36 @@ Memories verfügbar wären. Mit `chatMessage.maxMemories` auf `0` wird die Injek
von Projekt-Memories deaktiviert; das Nutzerprofil kann weiterhin injiziert werden,
wenn es aktiviert ist.

### Update-on-capture (optional)

Opt-in-Pfad, der Memories aktuell hält, wenn sich Fakten ändern. Bei
`updateOnCaptureEnabled: true` bettet Auto-Capture die neue Summary ein, sucht ähnliche
Memories oberhalb von `updateOnCaptureSimilarityThreshold` (Standard `0.75`) und lässt das
Capture-Modell zwischen `add`, `update` (in-place überschreiben) und `supersede` (neu
speichern + alt soft-deleten in den Papierkorb) wählen. Gepinnte Memories werden übersprungen.
Standard ist aus, damit bestehende Installationen add-only bleiben.

```jsonc
"updateOnCaptureEnabled": true,
"updateOnCaptureSimilarityThreshold": 0.75,
"updateOnCaptureMaxCandidates": 5,
```

Tipp: Bei `Xenova/nomic-embed-text-v1` verbessert `embeddingUseTaskPrefixes: true` (und ggf.
ein niedrigeres Recall-`similarityThreshold`) oft die Trefferqualität bei längeren Memories.
Prefixes bleiben standardmäßig opt-in.

### Dreaming (optionale Memory-Konsolidierung)

Opt-in-Hintergrundlauf, der Near-Duplikate findet (gleicher Similarity-Schwellwert wie
Deduplizierung), sie über `opencodeProvider`/`opencodeModel` zusammenführt und ersetzte
Einträge soft-deleten (wiederherstellbarer Papierkorb). Gepinnte Memories bleiben unberührt.
Konfiguration unter `dreaming` in `opencode-mem.jsonc` (`enabled` Standard `false`). Die
Web-UI bietet **Papierkorb** und **Dreaming**. Soft-gelöschte Zeilen werden nach
`dreaming.trashRetentionDays` beim Cleanup hart bereinigt.
Opt-in-Hintergrundlauf, der **verwandte** Memories findet (über `dreaming.relatedThreshold`,
Standard `0.75`, unabhängig von `deduplicationSimilarityThreshold`), sie über
`opencodeProvider`/`opencodeModel` mit `merge` / `update` / `supersede` / `keep` / `flag`
(Widersprüche) konsolidiert und ersetzte Einträge soft-deleten (wiederherstellbarer
Papierkorb). Gepinnte Memories bleiben unberührt. Mit `dreaming.crossProject: true` (oder
`memory.defaultScope: "all-projects"`) gruppiert Dreaming über Projekt-Tags; User-Scope
bleibt getrennt. Konfiguration unter `dreaming` in `opencode-mem.jsonc` (`enabled` Standard
`false`). Die Web-UI bietet **Papierkorb** und **Dreaming**. Soft-gelöschte Zeilen werden
nach `dreaming.trashRetentionDays` beim Cleanup hart bereinigt.

### Embeddings wählen / konfigurieren

Expand Down
34 changes: 34 additions & 0 deletions .github/i18n/README.nl.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,40 @@ in die voor geschikte herinneringen beschikbaar zijn. Met `chatMessage.maxMemori
op `0` wordt het injecteren van projectherinneringen uitgeschakeld; het
gebruikersprofiel kan nog steeds worden geïnjecteerd als dit is ingeschakeld.

### Update-on-capture (optioneel)

Optioneel pad om herinneringen actueel te houden wanneer feiten veranderen. Met
`updateOnCaptureEnabled: true` embedt auto-capture de nieuwe samenvatting, zoekt
vergelijkbare herinneringen boven `updateOnCaptureSimilarityThreshold` (standaard
`0.75`) en laat het capture-model kiezen tussen `add`, `update` (in-place
herschrijven) of `supersede` (nieuw opslaan + oud soft-deleten naar de prullenbak).
Vastgezette (pinned) herinneringen worden overgeslagen. Standaard uit, zodat
bestaande installaties add-only blijven.

```jsonc
"updateOnCaptureEnabled": true,
"updateOnCaptureSimilarityThreshold": 0.75,
"updateOnCaptureMaxCandidates": 5,
```

Tip: bij `Xenova/nomic-embed-text-v1` verbetert `embeddingUseTaskPrefixes: true`
(en eventueel een lagere recall-`similarityThreshold`) vaak de matchkwaliteit voor
langere herinneringen. Prefixes blijven standaard opt-in.

### Dreaming (optionele geheugenconsolidatie)

Optionele achtergrondpass die **gerelateerde** herinneringen vindt via
`dreaming.relatedThreshold` (standaard `0.75`, onafhankelijk van
`deduplicationSimilarityThreshold`), ze via `opencodeProvider` / `opencodeModel`
met `merge` / `update` / `supersede` / `keep` / `flag` (tegenstrijdigheden)
consolideert, en vervangen rijen soft-deletet naar herstelbare prullenbak.
Vastgezette herinneringen blijven onaangeroerd. Met `dreaming.crossProject: true`
(of `memory.defaultScope: "all-projects"`) groepeert dreaming over projecttags;
user-scope blijft gescheiden. Configureer onder `dreaming` in
`opencode-mem.jsonc` (`enabled` standaard `false`). De web-UI toont **Trash** en
**Dream**. Soft-verwijderde rijen worden na `dreaming.trashRetentionDays` hard
opgeschoond.

### Embeddings kiezen / configureren

Embeddings voeden similarity search voor herinneringen en het gebruikersprofiel. Configureer ze in hetzelfde bestand (`~/.config/opencode/opencode-mem.jsonc`). Er is **geen MLX-backend** — lokale embeddings gebruiken `@huggingface/transformers` met ONNX, niet Apple MLX.
Expand Down
33 changes: 33 additions & 0 deletions .github/i18n/README.tr.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,39 @@ Oturum ve yaşa göre hariç tutma kuralları, enjekte edilen bellek sayısı s
doldurmaz. `chatMessage.maxMemories` değerini `0` yapmak proje belleği enjeksiyonunu
devre dışı bırakır; kullanıcı profili etkinleştirilmişse yine de enjekte edilebilir.

### Update-on-capture (isteğe bağlı)

Gerçekler değiştikçe bellekleri güncel tutmak için isteğe bağlı yol.
`updateOnCaptureEnabled: true` iken otomatik yakalama yeni özeti gömer,
`updateOnCaptureSimilarityThreshold` (varsayılan `0.75`) üzerindeki benzer
bellekleri arar ve yakalama modelinin `add`, `update` (yerinde yeniden yazma)
veya `supersede` (yeniyi kaydet + eskisini çöp kutusuna soft-delete) seçmesini
sağlar. Sabitlenmiş (pinned) bellekler atlanır. Varsayılan kapalıdır; mevcut
kurulumlar add-only kalır.

```jsonc
"updateOnCaptureEnabled": true,
"updateOnCaptureSimilarityThreshold": 0.75,
"updateOnCaptureMaxCandidates": 5,
```

İpucu: `Xenova/nomic-embed-text-v1` için `embeddingUseTaskPrefixes: true`
(ve isteğe bağlı olarak daha düşük recall `similarityThreshold`) uzun belleklerde
eşleşme kalitesini çoğu zaman iyileştirir. Önekler varsayılan olarak opt-in kalır.

### Dreaming (isteğe bağlı bellek birleştirme)

`dreaming.relatedThreshold` (varsayılan `0.75`, `deduplicationSimilarityThreshold`
ile bağımsız) ile **ilgili** bellekleri bulan isteğe bağlı arka plan geçişi;
yapılandırılmış `opencodeProvider` / `opencodeModel` ile `merge` / `update` /
`supersede` / `keep` / `flag` (çelişkiler) uygular ve geçersiz kılınan satırları
geri yüklenebilir çöp kutusuna soft-delete eder. Sabitlenmiş bellekler dokunulmaz.
`dreaming.crossProject: true` (veya `memory.defaultScope: "all-projects"`) ile
proje etiketleri arasında gruplanır; kullanıcı kapsamı ayrı kalır.
`opencode-mem.jsonc` içinde `dreaming` altında yapılandırın (`enabled` varsayılan
`false`). Web arayüzü **Trash** ve **Dream** sunar. Soft-silinen satırlar
`dreaming.trashRetentionDays` sonra temizlikte kalıcı silinir.

### Embedding seçimi / yapılandırması

Embedding’ler bellekler ve kullanıcı profili için benzerlik aramasını güçlendirir. Bunları aynı dosyada yapılandırın (`~/.config/opencode/opencode-mem.jsonc`). **MLX backend yoktur** — yerel embedding’ler Apple MLX değil, ONNX ile `@huggingface/transformers` kullanır.
Expand Down
16 changes: 16 additions & 0 deletions .github/i18n/README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,22 @@ memory({ mode: "import", inputPath: "./memories.json" });
不会占用原本可供符合条件的记忆使用的名额。将 `chatMessage.maxMemories` 设为 `0`
会禁用项目记忆注入,但启用用户画像后仍可注入用户画像。

### Update-on-capture(可选)

可选路径,用于在事实变化时保持记忆最新。当 `updateOnCaptureEnabled: true` 时,自动捕获会嵌入新摘要,搜索高于 `updateOnCaptureSimilarityThreshold`(默认 `0.75`)的相似记忆,并由捕获模型选择 `add`、`update`(原地改写)或 `supersede`(保存新条目并将旧条目软删除到回收站)。已固定(pinned)的记忆会被跳过。默认关闭,以便现有安装保持仅新增行为。

```jsonc
"updateOnCaptureEnabled": true,
"updateOnCaptureSimilarityThreshold": 0.75,
"updateOnCaptureMaxCandidates": 5,
```

提示:对 `Xenova/nomic-embed-text-v1`,启用 `embeddingUseTaskPrefixes: true`(并可视情况降低召回 `similarityThreshold`)通常能改善较长记忆的匹配质量。前缀默认仍为可选。

### Dreaming(可选记忆整合)

可选后台流程,通过 `dreaming.relatedThreshold`(默认 `0.75`,独立于 `deduplicationSimilarityThreshold`)查找**相关**记忆,并由配置的 `opencodeProvider` / `opencodeModel` 执行 `merge` / `update` / `supersede` / `keep` / `flag`(矛盾标记),将过时条目软删除到可恢复回收站。已固定记忆不会被改动。设置 `dreaming.crossProject: true`(或 `memory.defaultScope: "all-projects"`)可跨项目标签分组;用户范围保持分离。在 `opencode-mem.jsonc` 的 `dreaming` 下配置(`enabled` 默认 `false`)。Web UI 提供 **Trash** 与 **Dream**。软删除行在常规清理时于 `dreaming.trashRetentionDays` 后硬清除。

### 选择 / 配置嵌入模型

嵌入为记忆与用户画像提供相似性搜索能力。在同一文件中配置(`~/.config/opencode/opencode-mem.jsonc`)。**没有 MLX 后端** — 本地嵌入使用带 ONNX 的 `@huggingface/transformers`,而非 Apple MLX。
Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,12 +300,15 @@ memories. Prefixes stay opt-in by default.

### Dreaming (optional memory consolidation)

Opt-in background pass that finds near-duplicate memories (same similarity threshold as
deduplication), asks the configured `opencodeProvider` / `opencodeModel` to merge them,
and soft-deletes superseded rows into recoverable trash. Pinned memories are never
touched. Configure under `dreaming` in `opencode-mem.jsonc` (`enabled` default `false`).
The web UI exposes **Trash** (restore) and **Dream** (manual run). Soft-deleted rows are
hard-purged after `dreaming.trashRetentionDays` during normal cleanup.
Opt-in background pass that finds **related** memories (via `dreaming.relatedThreshold`,
default `0.75`, independent of `deduplicationSimilarityThreshold`), asks the configured
`opencodeProvider` / `opencodeModel` to `merge`, `update`, `supersede`, `keep`, or `flag`
contradictions, and soft-deletes superseded rows into recoverable trash. Pinned memories
are never touched. Set `dreaming.crossProject: true` (or `memory.defaultScope:
"all-projects"`) to group across project tags; user-scope stays separate. Configure under
`dreaming` in `opencode-mem.jsonc` (`enabled` default `false`). The web UI exposes
**Trash** (restore) and **Dream** (manual run). Soft-deleted rows are hard-purged after
`dreaming.trashRetentionDays` during normal cleanup.

Deeper related-threshold dreaming (update/supersede/flag, cross-project) lands in the
follow-up PR for #464.
Expand Down
13 changes: 12 additions & 1 deletion src/services/api-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ export async function handleListTrash(limit = 200): Promise<
content: string;
deletedAt: number;
mergedInto?: string;
supersededBy?: string;
projectName?: string;
containerTag: string;
}>
Expand All @@ -518,6 +519,7 @@ export async function handleListTrash(limit = 200): Promise<
content: string;
deletedAt: number;
mergedInto?: string;
supersededBy?: string;
projectName?: string;
containerTag: string;
}> = [];
Expand All @@ -531,6 +533,7 @@ export async function handleListTrash(limit = 200): Promise<
content: String(row.content),
deletedAt: Number(row.deleted_at),
mergedInto: typeof meta.mergedInto === "string" ? meta.mergedInto : undefined,
supersededBy: typeof meta.supersededBy === "string" ? meta.supersededBy : undefined,
projectName: row.project_name ? String(row.project_name) : undefined,
containerTag: String(row.container_tag),
});
Expand All @@ -554,10 +557,18 @@ export async function handleRestoreMemory(id: string): Promise<ApiResponse<{ res
if (!memory || memory.deleted_at == null) continue;
const ok = await tursoVectorSearch.restoreVector(db, id);
if (!ok) return { success: false, error: "Failed to restore memory" };
// Clear merge pointer so the restored row stands alone again.
// Clear merge/supersede pointers so the restored row stands alone again.
const meta = safeJSONParse(memory.metadata) || {};
let metaChanged = false;
if (meta.mergedInto) {
delete meta.mergedInto;
metaChanged = true;
}
if (meta.supersededBy) {
delete meta.supersededBy;
metaChanged = true;
}
if (metaChanged) {
await db.run(`UPDATE memories SET metadata = ? WHERE id = ?`, [JSON.stringify(meta), id]);
}
return { success: true, data: { restored: true } };
Expand Down
27 changes: 24 additions & 3 deletions src/services/auto-capture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,27 @@ async function capturePrompt(
});

if (CONFIG.showAutoCaptureToasts) {
const action = result.action || "add";
const toastByAction: Record<string, { title: string; message: string }> = {
add: {
title: "Memory Captured",
message: "Project memory saved from conversation",
},
update: {
title: "Memory Updated",
message: "Similar existing memory was rewritten with current facts",
},
supersede: {
title: "Memory Superseded",
message: "New memory saved; outdated entry moved to trash",
},
};
const toast = toastByAction[action] ?? toastByAction.add!;
await ctx.client?.tui
.showToast({
body: {
title: "Memory Captured",
message: "Project memory saved from conversation",
title: toast.title,
message: toast.message,
variant: "success",
duration: 3000,
},
Expand Down Expand Up @@ -752,9 +768,14 @@ async function persistAutoCaptureMemory(
}

const scope = CONFIG.memory.defaultScope === "all-projects" ? "all-projects" : "project";
// Over-fetch before pin-filtering so pinned hits don't starve the candidate list.
const searchLimit = Math.min(
20,
Math.max(CONFIG.updateOnCaptureMaxCandidates * 3, CONFIG.updateOnCaptureMaxCandidates + 5)
);
const search = await memoryClient.searchMemories(summaryWithTags, containerTag, scope, {
similarityThreshold: CONFIG.updateOnCaptureSimilarityThreshold,
limit: CONFIG.updateOnCaptureMaxCandidates,
limit: searchLimit,
});

const rawCandidates: UpdateOnCaptureCandidate[] = (search.success ? search.results : []).map(
Expand Down
Loading
Loading