v0.13.0: 记忆系统重构 — MEMORY.md 文件化 + 路径保护 + 自动提取优化

核心变更:
- 删除 SQLite memories 表 + FTS5 + IVF 向量存储引擎 (~1300行)
- 4 个记忆工具合并为 1 个 memory 工具 (5 action)
- 记忆存储改为工作空间 MEMORY.md 单文件,严格格式校验
- 路径保护: checkPathAllowed 拦截所有工具,仅 memory 专用 IPC 通道可访问
- 应用启动/工作空间切换时自动校验并初始化 MEMORY.md(格式错误自动备份重建)
- 自动记忆提取重建: 对话结束时触发,多层质量过滤(内容/泛化/去重/安全/importance门槛)
- 工具总数: 42→40,UI 全面更新(帮助面板、工具面板、设置面板、README)
- 版本号更新: 0.12.11 → 0.13.0
This commit is contained in:
紫影233
2026-06-24 14:49:09 +08:00
parent dcaf5982fc
commit 0903d740da
26 changed files with 1207 additions and 2364 deletions
+1 -137
View File
@@ -3,7 +3,7 @@
* v4.0: 桌面端走 IPC → SQLiteWeb 端仍用 IndexedDB
*/
import type { ChatSession, MemoryEntry } from '../types.js';
import type { ChatSession } from '../types.js';
/** 检查是否在桌面环境 */
function isDesktop(): boolean {
@@ -217,89 +217,8 @@ export class ChatDB {
return this._idbGetSetting(key, defaultValue);
}
// ── Memories ──
async saveMemory(entry: MemoryEntry): Promise<string> {
if (isDesktop()) {
const row = {
id: entry.id,
type: entry.type,
content: entry.content,
importance: entry.importance,
tags: entry.tags?.length ? JSON.stringify(entry.tags) : null,
source: entry.source || null,
session_id: entry.sessionId || null,
use_count: entry.useCount,
embedding: entry.embedding ? JSON.stringify(entry.embedding) : null,
created_at: entry.createdAt,
updated_at: entry.updatedAt,
last_used_at: entry.lastUsedAt
};
await dbBridge().saveMemory(row);
return entry.id;
}
return this._idbSaveMemory(entry);
}
async getMemory(id: string): Promise<MemoryEntry | null> {
if (isDesktop()) {
const row = await dbBridge().getMemory(id);
return row ? this._rowToMemory(row) : null;
}
return this._idbGetMemory(id);
}
async getAllMemories(): Promise<MemoryEntry[]> {
if (isDesktop()) {
const rows = await dbBridge().getAllMemories();
return rows.map((r: any) => this._rowToMemory(r));
}
return this._idbGetAllMemories();
}
async getMemoriesByType(type: string): Promise<MemoryEntry[]> {
if (isDesktop()) {
const rows = await dbBridge().getMemoriesByType(type);
return rows.map((r: any) => this._rowToMemory(r));
}
return this._idbGetMemoriesByType(type);
}
async deleteMemory(id: string): Promise<void> {
if (isDesktop()) {
await dbBridge().deleteMemory(id);
return;
}
return this._idbDeleteMemory(id);
}
async clearAllMemories(): Promise<void> {
if (isDesktop()) {
await dbBridge().clearAllMemories();
return;
}
return this._idbClearAllMemories();
}
// ── Helpers ──
private _rowToMemory(row: any): MemoryEntry {
return {
id: row.id,
type: row.type,
content: row.content,
importance: row.importance,
tags: row.tags ? JSON.parse(row.tags) : [],
source: row.source || undefined,
sessionId: row.session_id || undefined,
useCount: row.use_count,
embedding: row.embedding ? JSON.parse(row.embedding) : undefined,
createdAt: row.created_at,
updatedAt: row.updated_at,
lastUsedAt: row.last_used_at
};
}
// ═══════════════════════════════════════════
// IndexedDB fallback methods (Web 端兼容)
// ═══════════════════════════════════════════
@@ -393,61 +312,6 @@ export class ChatDB {
});
}
private async _idbSaveMemory(entry: MemoryEntry): Promise<string> {
return new Promise((resolve, reject) => {
const store = this._idbTx('memories', 'readwrite');
const req = store.put(entry);
req.onsuccess = () => resolve(entry.id);
req.onerror = () => reject(req.error);
});
}
private async _idbGetMemory(id: string): Promise<MemoryEntry | null> {
return new Promise((resolve, reject) => {
const store = this._idbTx('memories');
const req = store.get(id);
req.onsuccess = () => resolve(req.result || null);
req.onerror = () => reject(req.error);
});
}
private async _idbGetAllMemories(): Promise<MemoryEntry[]> {
return new Promise((resolve, reject) => {
const store = this._idbTx('memories');
const req = store.getAll();
req.onsuccess = () => resolve(req.result || []);
req.onerror = () => reject(req.error);
});
}
private async _idbGetMemoriesByType(type: string): Promise<MemoryEntry[]> {
return new Promise((resolve, reject) => {
const store = this._idbTx('memories');
const index = store.index('type');
const req = index.getAll(type);
req.onsuccess = () => resolve(req.result || []);
req.onerror = () => reject(req.error);
});
}
private async _idbDeleteMemory(id: string): Promise<void> {
return new Promise((resolve, reject) => {
const store = this._idbTx('memories', 'readwrite');
const req = store.delete(id);
req.onsuccess = () => resolve();
req.onerror = () => reject(req.error);
});
}
private async _idbClearAllMemories(): Promise<void> {
return new Promise((resolve, reject) => {
const store = this._idbTx('memories', 'readwrite');
const req = store.clear();
req.onsuccess = () => resolve();
req.onerror = () => reject(req.error);
});
}
/** 获取原始 IndexedDB 实例(用于数据迁移) */
getRawIDB(): IDBDatabase | null {
return this.idb;