@aipack-ai/memory 0.0.1 → 1.0.2
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.map +1 -1
- package/package.json +4 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store/file-memory-store.ts","../src/retrieval/tokenizer.ts","../src/retrieval/bm25.ts","../src/retrieval/vector-index.ts","../src/store/memory-index.ts","../src/utils/keyed-mutex.ts","../src/store/in-memory-store.ts","../src/retrieval/embedder.ts","../src/retrieval/hybrid-retriever.ts","../../agent/core/types.ts","../../agent/core/context-resource.ts","../../agent/core/transformer.ts","../../agent/core/extension.ts","../src/capture/extractor.ts","../src/capture/capture-extension.ts","../src/injection/sentinels.ts","../src/injection/injection-transformer.ts","../src/consolidation/consolidator.ts","../src/tools/memory-tools.ts","../src/plugin.ts"],"sourcesContent":["/**\n * 文件记忆存储(默认实现)。\n *\n * 每条记忆一个 JSON 文件:<baseDir>/<encodeURIComponent(id)>.json。\n * 写入采用 temp + rename 原子替换(镜像 aipack FileSessionStorage)。\n * 内存缓存 MemoryIndex + BM25 增量索引;首次访问时懒加载并按 maxAge/expiresAt 惰性清理。\n *\n * 并发安全:同 id 的写操作(save/delete/touchRecall)经 keyed mutex 串行,\n * 避免 read-modify-write 竞态(如 embedding 计算期间丢失另一路更新)。\n *\n * 加载优化:懒加载时并发批量读文件(默认 64 并发),避免逐文件串行 IO;\n * 检索要求条目常驻内存(BM25 倒排 + 向量索引),内存占用与记忆规模成正比\n * (详见 README 限制说明)。\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { MemoryIndex } from './memory-index';\nimport { finalizeEntry } from './in-memory-store';\nimport { KeyedMutex } from '../utils/keyed-mutex';\nimport type {\n ConsolidateOptions,\n ConsolidatorLike,\n Embedder,\n FileMemoryStoreOptions,\n MemoryEntry,\n MemoryEventSink,\n MemorySaveInput,\n MemorySearchResult,\n MemoryStats,\n MemoryStore,\n} from '../types';\n\nfunction resolveBaseDir(baseDir?: string): string {\n if (baseDir) {\n if (baseDir === '~') return os.homedir();\n if (baseDir.startsWith('~/')) return path.join(os.homedir(), baseDir.slice(2));\n if (path.isAbsolute(baseDir)) return baseDir;\n return path.join(process.cwd(), baseDir);\n }\n return path.join(process.cwd(), '.aipack', 'memory');\n}\n\nfunction encodeId(id: string): string {\n return encodeURIComponent(id);\n}\n\n/** 懒加载并发读的批大小 */\nconst LOAD_CONCURRENCY = 64;\n\n/** 并发执行 tasks(每批 LOAD_CONCURRENCY 个),返回完成数量(含失败) */\nasync function runPool<T>(tasks: (() => Promise<T>)[]): Promise<number> {\n let done = 0;\n for (let i = 0; i < tasks.length; i += LOAD_CONCURRENCY) {\n const batch = tasks.slice(i, i + LOAD_CONCURRENCY);\n await Promise.all(\n batch.map(async (t) => {\n try {\n await t();\n } finally {\n done++;\n }\n }),\n );\n }\n return done;\n}\n\nexport class FileMemoryStore implements MemoryStore {\n private baseDir: string;\n private maxAge?: number;\n private idx: MemoryIndex;\n private loaded = false;\n private loading: Promise<void> | null = null;\n private consolidator?: ConsolidatorLike;\n private embedder?: Embedder;\n private onEvent?: MemoryEventSink;\n /** 同 id 写互斥(save/delete/touchRecall) */\n private writeLocks = new KeyedMutex();\n private lastConsolidatedAt?: number;\n\n constructor(\n options: FileMemoryStoreOptions & {\n index?: MemoryIndex;\n embedder?: Embedder;\n onEvent?: MemoryEventSink;\n } = {},\n ) {\n this.baseDir = resolveBaseDir(options.baseDir);\n this.maxAge = options.maxAge;\n this.idx = options.index ?? new MemoryIndex();\n this.embedder = options.embedder;\n this.onEvent = options.onEvent;\n }\n\n get dir(): string {\n return this.baseDir;\n }\n\n setConsolidator(consolidator: ConsolidatorLike): void {\n this.consolidator = consolidator;\n }\n\n // ─── 加载 ───────────────────────────────────────────────────────\n\n /** 懒加载:读取目录所有 JSON → 内存索引。并发安全。 */\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return;\n if (this.loading) return this.loading;\n\n this.loading = (async () => {\n const start = Date.now();\n let loaded = 0;\n let skipped = 0;\n try {\n await fs.mkdir(this.baseDir, { recursive: true });\n const files = await fs.readdir(this.baseDir);\n const now = Date.now();\n const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.endsWith('.tmp'));\n await runPool(\n jsonFiles.map((f) => async () => {\n const filePath = path.join(this.baseDir, f);\n try {\n const raw = await fs.readFile(filePath, 'utf-8');\n const entry = JSON.parse(raw) as MemoryEntry;\n if (!entry?.id) {\n skipped++;\n return;\n }\n // 惰性过期清理(maxAge 按 updatedAt / expiresAt)\n const expiredByAge =\n this.maxAge != null && now - entry.updatedAt > this.maxAge;\n const expiredByTtl = entry.expiresAt != null && now > entry.expiresAt;\n if (expiredByAge || expiredByTtl) {\n await fs.unlink(filePath).catch(() => {});\n skipped++;\n return;\n }\n this.idx.add(entry);\n loaded++;\n } catch {\n skipped++;\n this.onEvent?.({ type: 'store:corrupt', file: f });\n }\n }),\n );\n } catch (err) {\n // 目录不存在等:记录并继续(空库可用)\n this.onEvent?.({ type: 'store:corrupt', file: `<baseDir> ${(err as Error).message}` });\n }\n this.loaded = true;\n this.loading = null;\n if (loaded > 0 || skipped > 0) {\n this.onEvent?.({\n type: 'store:load',\n loaded,\n skipped,\n ms: Date.now() - start,\n });\n }\n })();\n return this.loading;\n }\n\n private entryPath(id: string): string {\n return path.join(this.baseDir, `${encodeId(id)}.json`);\n }\n\n private async writeEntry(entry: MemoryEntry): Promise<void> {\n await fs.mkdir(this.baseDir, { recursive: true });\n const target = this.entryPath(entry.id);\n const tmp = `${target}.${process.pid}.tmp`;\n await fs.writeFile(tmp, JSON.stringify(entry, null, 2), 'utf-8');\n await fs.rename(tmp, target); // 原子替换\n }\n\n // ─── MemoryStore ────────────────────────────────────────────────\n\n async save(entry: MemorySaveInput): Promise<MemoryEntry> {\n await this.ensureLoaded();\n return this.writeLocks.withLock(entry.id ?? '*', async () => {\n const prev = entry.id ? this.idx.get(entry.id) : null;\n const finalized = finalizeEntry(\n {\n ...entry,\n // 保留 embedding(若已存在且新 entry 未提供)\n embedding: entry.embedding ?? prev?.embedding,\n // 显式 createdAt 优先(如合并器保留最早创建时间);未提供时沿用已有条目的\n createdAt: entry.createdAt ?? prev?.createdAt,\n },\n Date.now(),\n );\n // 配置了 embedder 且无既有向量时,计算 embedding\n if (this.embedder && !finalized.embedding) {\n try {\n finalized.embedding = await this.embedder.embed(finalized.content);\n } catch (err) {\n this.onEvent?.({\n type: 'embedding:error',\n id: finalized.id,\n error: (err as Error).message,\n });\n // embedding 失败不阻断保存(退化为纯 BM25 检索)\n }\n }\n await this.writeEntry(finalized);\n this.idx.add(finalized);\n return finalized;\n });\n }\n\n async get(id: string): Promise<MemoryEntry | null> {\n await this.ensureLoaded();\n return this.idx.get(id);\n }\n\n async delete(id: string): Promise<boolean> {\n await this.ensureLoaded();\n return this.writeLocks.withLock(id, async () => {\n const existed = this.idx.remove(id);\n if (existed) {\n await fs.unlink(this.entryPath(id)).catch(() => {});\n }\n return existed;\n });\n }\n\n async list(limit?: number): Promise<MemoryEntry[]> {\n await this.ensureLoaded();\n return this.idx.list(limit);\n }\n\n async search(query: string, limit?: number): Promise<MemorySearchResult[]> {\n await this.ensureLoaded();\n return this.idx.search(query, limit);\n }\n\n async searchVectors(queryVec: number[], limit?: number): Promise<MemorySearchResult[]> {\n await this.ensureLoaded();\n return this.idx.searchVectors(queryVec, limit);\n }\n\n async touchRecall(id: string, at: number = Date.now()): Promise<void> {\n await this.ensureLoaded();\n return this.writeLocks.withLock(id, async () => {\n const e = this.idx.get(id);\n if (!e) return;\n // 仅更新检索统计;不更新 updatedAt(updatedAt = 内容修改时间,\n // 驱动增量合并候选窗口与过期语义,不应被一次检索刷新)\n e.lastRecalledAt = at;\n e.recallCount += 1;\n await this.writeEntry(e);\n this.idx.add(e);\n });\n }\n\n async consolidate(\n options?: ConsolidateOptions,\n ): Promise<{ merged: number; pruned: number }> {\n if (!this.consolidator) return { merged: 0, pruned: 0 };\n await this.ensureLoaded();\n // 不持全局锁:consolidator 内部经 store.save/delete(逐 id 锁)原子化,\n // 跨 id 的交错为 best-effort(新写入条目未被本次合并处理,下一轮再合并)\n const result = await this.consolidator.run(options);\n this.lastConsolidatedAt = Date.now();\n return result;\n }\n\n async prune(options?: {\n maxAgeMs?: number;\n minConfidence?: number;\n }): Promise<number> {\n await this.ensureLoaded();\n const now = Date.now();\n const maxAge = options?.maxAgeMs;\n const minConf = options?.minConfidence ?? 0;\n let removed = 0;\n for (const e of this.idx.all()) {\n const expiredByAge = maxAge != null && now - e.updatedAt > maxAge;\n const expiredByTtl = e.expiresAt != null && now > e.expiresAt;\n const lowConf = e.confidence < minConf;\n if (expiredByAge || expiredByTtl || lowConf) {\n if (await this.delete(e.id)) removed++;\n }\n }\n if (removed > 0) this.onEvent?.({ type: 'prune', removed });\n return removed;\n }\n\n async count(): Promise<number> {\n await this.ensureLoaded();\n return this.idx.count();\n }\n\n markConsolidated(at: number = Date.now()): void {\n this.lastConsolidatedAt = at;\n }\n\n async stats(): Promise<MemoryStats> {\n await this.ensureLoaded();\n const base = this.idx.stats();\n return { ...base, lastConsolidatedAt: this.lastConsolidatedAt };\n }\n\n dispose(): void {\n // 所有写操作均 await 完成,无后台句柄需释放(占位,保持接口一致)\n }\n}\n\nexport function createFileMemoryStore(options?: FileMemoryStoreOptions): MemoryStore {\n return new FileMemoryStore(options);\n}\n","/**\n * 分词器:支持 latin(小写化 + 按非字母数字分割)与 CJK(逐字符)。\n * 零依赖,适配中英文混合文本的 BM25 索引。\n */\n\n/** 判断字符是否为 CJK 文字:汉字(含扩展/兼容区)、日本假名、韩国谚文 */\nexport function isCJK(ch: string): boolean {\n if (!ch) return false;\n const code = ch.codePointAt(0) ?? 0;\n return (\n (code >= 0x4e00 && code <= 0x9fff) || // CJK 统一表意文字\n (code >= 0x3400 && code <= 0x4dbf) || // CJK 扩展 A\n (code >= 0xf900 && code <= 0xfaff) || // CJK 兼容表意\n (code >= 0x3040 && code <= 0x30ff) || // 日文假名(平假名 + 片假名)\n (code >= 0xac00 && code <= 0xd7af) // 韩文谚文\n );\n}\n\n/** 判断字符是否为 CJK 兼容标点 / 全角符号(粗略范围,常见中文标点) */\nfunction isCJKSymbol(ch: string): boolean {\n const code = ch.charCodeAt(0);\n // CJK 标点 \\u3000-\\u303f、全角 ascii \\uff00-\\uffef\n return (code >= 0x3000 && code <= 0x303f) || (code >= 0xff00 && code <= 0xffef);\n}\n\n/**\n * 常见停用词(中英文小集合),用于概念抽取去停用词。\n * BM25 索引本身不去停用词(保留其 IDF 权重),仅概念抽取时过滤。\n */\nexport const STOPWORDS = new Set<string>([\n // 英文\n 'the', 'a', 'an', 'and', 'or', 'but', 'if', 'then', 'else', 'when', 'of', 'to', 'in',\n 'on', 'at', 'by', 'for', 'with', 'without', 'is', 'are', 'was', 'were', 'be', 'been',\n 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'can', 'could',\n 'should', 'shall', 'may', 'might', 'must', 'i', 'you', 'he', 'she', 'it', 'we', 'they',\n 'this', 'that', 'these', 'those', 'my', 'your', 'his', 'her', 'its', 'our', 'their',\n 'me', 'him', 'us', 'them', 'as', 'from', 'so', 'not', 'no', 'yes',\n // 中文高频虚词\n '的', '了', '是', '在', '我', '你', '他', '她', '它', '们', '这', '那', '和', '与', '或',\n '也', '都', '就', '把', '被', '让', '给', '对', '为', '从', '到', '向', '着', '过', '吧',\n '吗', '呢', '啊', '哦', '嗯', '个', '些', '上', '下', '里', '中',\n]);\n\n/**\n * 分词器:支持 latin(小写化 + 按非字母数字分割)与 CJK(字符 bigram)。\n * 零依赖,适配中日韩英混合文本的 BM25 索引。\n *\n * CJK 采用「相邻两字 bigram」而非逐字 unigram:\n * - bigram 区分度远高于单字(「数据库」vs「数据科学」共享「数据」但不再全靠\n * 「数/据」这种高 df 低 idf 的单字强匹配);\n * - 奇数长度 CJK 串尾部遗留单字,保证单字查询仍可命中。\n * 支持汉字(含扩展/兼容)、日文假名、韩文谚文。\n */\n\n/**\n * 将文本切分为 token 数组。\n * - latin 部分:小写化后按非字母数字字符分割。\n * - CJK 部分:相邻两字 bigram(奇数串尾部补单字)。\n * - 标点 / 空白忽略。\n */\nexport function tokenize(text: string): string[] {\n if (!text) return [];\n const tokens: string[] = [];\n let latinBuf = '';\n let cjkBuf = '';\n\n const flushLatin = () => {\n if (latinBuf) {\n // 小写化 + 按非字母数字分割\n const parts = latinBuf.toLowerCase().match(/[a-z0-9]+/g);\n if (parts) tokens.push(...parts);\n latinBuf = '';\n }\n };\n\n const flushCJK = () => {\n if (!cjkBuf) return;\n const chars = [...cjkBuf];\n if (chars.length === 1) {\n tokens.push(chars[0]);\n } else {\n for (let i = 0; i + 1 < chars.length; i++) {\n tokens.push(chars[i] + chars[i + 1]);\n }\n // 奇数长度:最后一个字无配对,补单字(保证单字查询可命中)\n if (chars.length % 2 === 1) {\n tokens.push(chars[chars.length - 1]);\n }\n }\n cjkBuf = '';\n };\n\n for (const ch of text) {\n if (isCJK(ch)) {\n flushLatin();\n cjkBuf += ch;\n } else if (isCJKSymbol(ch)) {\n flushLatin();\n flushCJK();\n } else {\n flushCJK();\n latinBuf += ch;\n }\n }\n flushLatin();\n flushCJK();\n\n return tokens;\n}\n\n/** 概念抽取:返回非停用词 token 的频次 top-N(用于记忆 concepts 字段) */\nexport function extractConcepts(text: string, maxConcepts = 8): string[] {\n const tokens = tokenize(text);\n const freq = new Map<string, number>();\n for (const t of tokens) {\n if (STOPWORDS.has(t)) continue;\n // 单字符英文跳过;CJK 单字(奇数串尾部)保留,bigram 正常保留\n if (t.length < 2 && !isCJK(t)) continue;\n freq.set(t, (freq.get(t) ?? 0) + 1);\n }\n return [...freq.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, maxConcepts)\n .map(([t]) => t);\n}\n","/**\n * BM25 倒排索引与检索器(零依赖)。\n *\n * 经典 BM25 公式:\n * score(q, d) = Σ_t idf(t) * (tf(t,d) * (k1+1)) / (tf(t,d) + k1*(1 - b + b*|d|/avgdl))\n * idf(t) = ln((N - df(t) + 0.5) / (df(t) + 0.5) + 1)\n *\n * 分数不归一化(由 HybridRetriever 负责 min-max 归一化)。\n */\n\nimport { tokenize } from './tokenizer';\nimport type { MemoryEntry, MemorySearchResult } from '../types';\n\nexport interface BM25Options {\n /** 词频饱和参数,默认 1.5 */\n k1?: number;\n /** 文档长度归一化参数,默认 0.75 */\n b?: number;\n}\n\ninterface DocEntry {\n id: string;\n tokens: string[];\n length: number;\n tf: Map<string, number>;\n}\n\nexport class BM25Index {\n private k1: number;\n private b: number;\n /** 文档集合 */\n private docs = new Map<string, DocEntry>();\n /** 倒排表:token -> 文档 id 集合 */\n private inverted = new Map<string, Set<string>>();\n private totalLength = 0;\n\n constructor(options: BM25Options = {}) {\n this.k1 = options.k1 ?? 1.5;\n this.b = options.b ?? 0.75;\n }\n\n /** 平均文档长度 */\n private get avgdl(): number {\n return this.docs.size === 0 ? 0 : this.totalLength / this.docs.size;\n }\n\n /** 文档总数 */\n get size(): number {\n return this.docs.size;\n }\n\n /** 添加或替换文档(同 id 覆盖) */\n add(id: string, tokens: string[]): void {\n // 若已存在,先移除旧的倒排\n if (this.docs.has(id)) this.remove(id);\n\n const tf = new Map<string, number>();\n for (const t of tokens) tf.set(t, (tf.get(t) ?? 0) + 1);\n\n this.docs.set(id, { id, tokens, length: tokens.length, tf });\n this.totalLength += tokens.length;\n\n for (const t of tf.keys()) {\n let set = this.inverted.get(t);\n if (!set) {\n set = new Set();\n this.inverted.set(t, set);\n }\n set.add(id);\n }\n }\n\n /** 移除文档 */\n remove(id: string): void {\n const doc = this.docs.get(id);\n if (!doc) return;\n this.docs.delete(id);\n this.totalLength -= doc.length;\n for (const t of doc.tf.keys()) {\n const set = this.inverted.get(t);\n if (!set) continue;\n set.delete(id);\n if (set.size === 0) this.inverted.delete(t);\n }\n }\n\n /** 清空索引 */\n clear(): void {\n this.docs.clear();\n this.inverted.clear();\n this.totalLength = 0;\n }\n\n /**\n * 检索:返回 top-N 的 {id, score}(按分数降序)。\n * 只扫描 query token 命中的文档,避免全量计算。\n */\n search(queryTokens: string[], limit = 10): Array<{ id: string; score: number }> {\n if (this.docs.size === 0 || queryTokens.length === 0) return [];\n\n const N = this.docs.size;\n const avgdl = this.avgdl;\n const scores = new Map<string, number>();\n\n for (const t of queryTokens) {\n const set = this.inverted.get(t);\n if (!set || set.size === 0) continue;\n\n const df = set.size;\n const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);\n\n for (const docId of set) {\n const doc = this.docs.get(docId);\n if (!doc) continue;\n const tf = doc.tf.get(t) ?? 0;\n if (tf === 0) continue;\n\n const denom = tf + this.k1 * (1 - this.b + (this.b * doc.length) / (avgdl || 1));\n const contrib = (idf * (tf * (this.k1 + 1))) / (denom || 1);\n scores.set(docId, (scores.get(docId) ?? 0) + contrib);\n }\n }\n\n return [...scores.entries()]\n .map(([id, score]) => ({ id, score }))\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n }\n\n /**\n * 单个 token 的 idf(未出现返回 0)。\n * 供上层计算查询的理论满分(完全同文、tf=1、len≈avgdl 时的分数),\n * 把无界 BM25 原始分规范化为 [0,1] 相似度。\n */\n idf(token: string): number {\n const df = this.inverted.get(token)?.size ?? 0;\n if (df === 0) return 0;\n const N = this.docs.size;\n return Math.log((N - df + 0.5) / (df + 0.5) + 1);\n }\n}\n\n/**\n * 基于 BM25Index 的检索器,包装为 MemorySearchResult。\n * entries 与 index 由外部维护(store 持有并增量同步)。\n *\n * 分数语义:BM25 原始分无界(取决于词频/idf 与库规模),与 embedding 的\n * cosine 相似度(0..1)量纲不匹配 —— 若直接作为绝对相似度与\n * similarityThreshold(如 0.85)比较,合并几乎永远不会触发。这里除以\n * 查询的理论满分(Σidf,即完全同文时的分数)并截断到 [0,1]:\n * 完全同文 ≈ 1,部分命中按比例衰减,使绝对阈值对 BM25 / cosine 统一成立。\n * 该变换是单调的,对普通检索路径的 min-max 归一化幂等(排序不变)。\n */\nexport class BM25Retriever {\n constructor(\n private index: BM25Index,\n private entries: Map<string, MemoryEntry>,\n ) {}\n\n async search(query: string, limit = 5): Promise<MemorySearchResult[]> {\n const queryTokens = tokenize(query);\n const hits = this.index.search(queryTokens, limit);\n\n // 查询的理论满分:所有 query token 的 idf 之和。\n // 完全同文(tf=1、len≈avgdl)时 BM25 分数 ≈ Σidf,因此 score/Σidf ≈ 1;\n // 词频高或文档更短的命中可能 >1,截断到 1。\n const maxPossible = queryTokens.reduce((acc, t) => acc + this.index.idf(t), 0);\n\n return hits.map(({ id, score }) => ({\n entry: this.entries.get(id)!,\n score: maxPossible > 0 ? Math.min(1, score / maxPossible) : 0,\n matchedBy: 'bm25' as const,\n })).filter((r) => r.entry != null);\n }\n}\n","/**\n * VectorIndex —— 零依赖简化 ANN(近似最近邻)向量索引。\n *\n * 用途:为混合检索提供「独立于 BM25 的向量召回」。\n * 之前向量检索被 BM25 top-K 候选池封顶,本索引保证向量路可独立召回语义相似\n * 而关键词不重叠的记忆。\n *\n * 策略(按规模取舍):\n * - 默认:精确 brute-force cosine(预计算范数),中小规模(< 5 万条)足够快;\n * - 可选 `ivfBuckets`:按主导维度分桶的简化 IVF,查询时探测邻近分桶,\n * 降低大库扫描量(近似召回,用于更大规模)。\n *\n * 共享 entry.embedding 引用(不复制向量),内存开销仅为索引结构本身。\n */\n\n/** 向量检索命中 */\nexport interface VectorSearchResult {\n id: string;\n score: number; // cosine 0..1\n}\n\ninterface VecEntry {\n id: string;\n vec: number[];\n norm: number;\n bucket: number;\n}\n\nexport interface VectorIndexOptions {\n /** IVF 分桶数(0 = 纯 brute-force 精确检索,默认 0) */\n ivfBuckets?: number;\n}\n\nfunction normOf(v: number[]): number {\n let s = 0;\n for (let i = 0; i < v.length; i++) s += v[i] * v[i];\n return Math.sqrt(s);\n}\n\nfunction dot(a: number[], b: number[]): number {\n let s = 0;\n for (let i = 0; i < a.length; i++) s += a[i] * b[i];\n return s;\n}\n\nexport class VectorIndex {\n private entries = new Map<string, VecEntry>();\n /** bucket -> id 列表(仅 ivfBuckets > 0 时使用) */\n private buckets = new Map<number, string[]>();\n private dim = 0;\n private bucketCount: number;\n\n constructor(options: VectorIndexOptions = {}) {\n this.bucketCount = options.ivfBuckets ?? 0;\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n /** 添加或替换向量(同 id 覆盖)。维度不一致时忽略,返回是否生效 */\n add(id: string, vector: number[]): boolean {\n if (!vector || vector.length === 0) return false;\n if (this.dim === 0) this.dim = vector.length;\n if (vector.length !== this.dim) return false;\n\n const prev = this.entries.get(id);\n if (prev && this.bucketCount > 0) this.removeFromBucket(id, prev.bucket);\n\n const entry: VecEntry = {\n id,\n vec: vector,\n norm: normOf(vector),\n bucket: this.bucketOf(vector),\n };\n this.entries.set(id, entry);\n\n if (this.bucketCount > 0) {\n let list = this.buckets.get(entry.bucket);\n if (!list) {\n list = [];\n this.buckets.set(entry.bucket, list);\n }\n if (!list.includes(id)) list.push(id);\n }\n return true;\n }\n\n remove(id: string): boolean {\n const e = this.entries.get(id);\n if (!e) return false;\n this.entries.delete(id);\n if (this.bucketCount > 0) this.removeFromBucket(id, e.bucket);\n return true;\n }\n\n clear(): void {\n this.entries.clear();\n this.buckets.clear();\n this.dim = 0;\n }\n\n /** 检索 top-k(cosine 降序,score > 0) */\n search(query: number[], k: number): VectorSearchResult[] {\n if (this.entries.size === 0 || k <= 0 || !query || query.length === 0) return [];\n if (query.length !== this.dim) return [];\n\n const qNorm = normOf(query);\n if (qNorm === 0) return [];\n\n const pool =\n this.bucketCount > 0\n ? this.probeCandidates(query, k * 4)\n : [...this.entries.values()];\n\n const results: VectorSearchResult[] = [];\n for (const e of pool) {\n const score = dot(query, e.vec) / (qNorm * e.norm || 1);\n if (!(score > 0)) continue;\n results.push({ id: e.id, score });\n }\n\n results.sort((a, b) => b.score - a.score);\n return results.slice(0, k);\n }\n\n /** 主导维度(argmax |v|)分桶 */\n private bucketOf(vec: number[]): number {\n if (this.bucketCount <= 0) return 0;\n let best = 0;\n let bestAbs = -1;\n for (let i = 0; i < vec.length; i++) {\n const a = Math.abs(vec[i]);\n if (a > bestAbs) {\n bestAbs = a;\n best = i;\n }\n }\n return best % this.bucketCount;\n }\n\n /** 从查询桶开始探测邻近分桶,直到凑够 minCandidates */\n private probeCandidates(query: number[], minCandidates: number): VecEntry[] {\n const qBucket = this.bucketOf(query);\n const out: VecEntry[] = [];\n for (let r = 0; r < this.bucketCount && out.length < minCandidates; r++) {\n const list = this.buckets.get((qBucket + r) % this.bucketCount);\n if (!list) continue;\n for (const id of list) {\n const e = this.entries.get(id);\n if (e) out.push(e);\n if (out.length >= minCandidates) break;\n }\n }\n return out;\n }\n\n private removeFromBucket(id: string, bucket: number): void {\n const list = this.buckets.get(bucket);\n if (!list) return;\n const i = list.indexOf(id);\n if (i !== -1) list.splice(i, 1);\n if (list.length === 0) this.buckets.delete(bucket);\n }\n}\n","/**\n * 内存索引:维护 entries 集合 + BM25 倒排索引,供两个 store 复用。\n *\n * 职责:get / list / count / search / add / remove。\n * 不负责持久化(由具体 store 处理)。\n */\n\nimport { BM25Index, BM25Retriever } from '../retrieval/bm25';\nimport { VectorIndex } from '../retrieval/vector-index';\nimport { tokenize } from '../retrieval/tokenizer';\nimport type { MemoryEntry, MemorySearchResult, MemoryStats } from '../types';\n\n/** updatedAt 降序比较器 */\nfunction byUpdatedAtDesc(a: MemoryEntry, b: MemoryEntry): number {\n return b.updatedAt - a.updatedAt;\n}\n\nexport class MemoryIndex {\n private entries = new Map<string, MemoryEntry>();\n private index = new BM25Index();\n /** 独立向量索引:保证向量召回不被 BM25 候选池封顶 */\n private vectors = new VectorIndex();\n private retriever = new BM25Retriever(this.index, this.entries);\n\n /** 共享底层索引与条目表,返回一个 BM25Retriever(与 store 实时同步) */\n getRetriever(): BM25Retriever {\n return this.retriever;\n }\n\n get(id: string): MemoryEntry | null {\n return this.entries.get(id) ?? null;\n }\n\n /** 全部条目(updatedAt 降序) */\n all(): MemoryEntry[] {\n return [...this.entries.values()].sort(byUpdatedAtDesc);\n }\n\n list(limit?: number): MemoryEntry[] {\n const all = this.all();\n return limit != null ? all.slice(0, limit) : all;\n }\n\n count(): number {\n return this.entries.size;\n }\n\n /** 索引内容 = content + concepts(提升概念命中) */\n add(entry: MemoryEntry): void {\n this.entries.set(entry.id, entry);\n const tokens = tokenize(`${entry.content} ${entry.concepts.join(' ')}`);\n this.index.add(entry.id, tokens);\n // 向量索引同步:有 embedding 则加入,否则移除(避免残留旧向量)\n if (entry.embedding && entry.embedding.length > 0) {\n this.vectors.add(entry.id, entry.embedding);\n } else {\n this.vectors.remove(entry.id);\n }\n }\n\n remove(id: string): boolean {\n if (!this.entries.has(id)) return false;\n this.index.remove(id);\n this.vectors.remove(id);\n this.entries.delete(id);\n return true;\n }\n\n clear(): void {\n this.entries.clear();\n this.index.clear();\n this.vectors.clear();\n }\n\n async search(query: string, limit?: number): Promise<MemorySearchResult[]> {\n return this.retriever.search(query, limit ?? 5);\n }\n\n async searchVectors(queryVec: number[], limit?: number): Promise<MemorySearchResult[]> {\n const hits = this.vectors.search(queryVec, limit ?? 5);\n const results: MemorySearchResult[] = [];\n for (const { id, score } of hits) {\n const entry = this.entries.get(id);\n if (entry) results.push({ entry, score, matchedBy: 'embedding' as const });\n }\n return results;\n }\n\n /** 统计快照 */\n stats(): MemoryStats {\n const bySource = { capture: 0, tool: 0, consolidation: 0 } as MemoryStats['bySource'];\n let avgConfidence = 0;\n let recallTotal = 0;\n let lastRecalledAt: number | undefined;\n for (const e of this.entries.values()) {\n if (bySource[e.source] == null) bySource[e.source] = 0;\n bySource[e.source] += 1;\n avgConfidence += e.confidence;\n recallTotal += e.recallCount;\n if (e.lastRecalledAt != null && (lastRecalledAt == null || e.lastRecalledAt > lastRecalledAt)) {\n lastRecalledAt = e.lastRecalledAt;\n }\n }\n const count = this.entries.size;\n return {\n count,\n embeddingCount: this.vectors.size,\n bySource,\n avgConfidence: count > 0 ? avgConfidence / count : 0,\n recallTotal,\n lastRecalledAt,\n };\n }\n}\n","/**\n * 零依赖 keyed mutex(promise 链实现)。\n *\n * 用于串行化同一 key 上的 read-modify-write 操作(如 FileMemoryStore 的同 id 更新),\n * 以及全局写锁(consolidate / prune 与 save 互斥)。\n * 任一批量 fn 抛错不会污染链,后续调用者不受影响。\n */\n\nexport class KeyedMutex {\n /** key -> 当前链尾 promise */\n private tails = new Map<string, Promise<void>>();\n\n /**\n * 在 key 上串行执行 fn:同一 key 的并发调用按调用顺序排队,逐个执行。\n * 不同 key 之间互不阻塞(`withLock('*', ...)` 可作全局锁)。\n */\n withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const prev = this.tails.get(key) ?? Promise.resolve();\n let release!: () => void;\n const gate = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = prev.then(() => gate);\n this.tails.set(key, tail);\n\n return prev.then(async () => {\n try {\n return await fn();\n } finally {\n release();\n // 若链尾仍是我们这条,清掉避免 Map 泄漏\n if (this.tails.get(key) === tail) {\n this.tails.delete(key);\n }\n }\n });\n }\n\n /** 当前持锁 key 数量(调试用) */\n get pending(): number {\n return this.tails.size;\n }\n}\n\n/** 全局共享实例(各 store 复用;互不冲突) */\nexport const globalMutex = new KeyedMutex();\n","/**\n * 内存记忆存储(测试与临时场景)。\n * 进程退出即丢失,无持久化。API 与 FileMemoryStore 完全一致。\n *\n * 并发安全:同 id 的写操作(save/delete/touchRecall)经 keyed mutex 串行,\n * 避免 embedder 异步计算期间的 read-modify-write 竞态;跨 id 互不阻塞。\n */\n\nimport { MemoryIndex } from './memory-index';\nimport { KeyedMutex } from '../utils/keyed-mutex';\nimport type {\n ConsolidateOptions,\n ConsolidatorLike,\n Embedder,\n MemoryEntry,\n MemoryEventSink,\n MemorySaveInput,\n MemorySearchResult,\n MemoryStats,\n MemoryStore,\n} from '../types';\n\n/** 生成短随机 id */\nfunction genId(): string {\n return `mem_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/** 填充默认字段(id / createdAt / updatedAt / recallCount / expiresAt) */\nexport function finalizeEntry(entry: MemorySaveInput, now: number = Date.now()): MemoryEntry {\n const createdAt = entry.createdAt ?? now;\n const { ttlMs, ...rest } = entry;\n return {\n ...rest,\n id: entry.id ?? genId(),\n createdAt,\n updatedAt: now,\n recallCount: entry.recallCount ?? 0,\n expiresAt: ttlMs != null ? createdAt + ttlMs : entry.expiresAt,\n };\n}\n\nexport interface InMemoryStoreOptions {\n /** 可选向量化器:配置后 save 时自动计算 embedding(供混合检索) */\n embedder?: Embedder;\n /** 事件接收器(失败/整理等关键节点) */\n onEvent?: MemoryEventSink;\n}\n\nexport class InMemoryStore implements MemoryStore {\n private idx = new MemoryIndex();\n private consolidator?: ConsolidatorLike;\n private embedder?: Embedder;\n private onEvent?: MemoryEventSink;\n private writeLock = new KeyedMutex();\n private lastConsolidatedAt?: number;\n\n constructor(options: InMemoryStoreOptions = {}) {\n this.embedder = options.embedder;\n this.onEvent = options.onEvent;\n }\n\n setConsolidator(consolidator: ConsolidatorLike): void {\n this.consolidator = consolidator;\n }\n\n async save(entry: MemorySaveInput): Promise<MemoryEntry> {\n return this.writeLock.withLock(entry.id ?? '*', async () => {\n // 若已存在同 id,保留 createdAt 与 embedding\n const prev = entry.id ? this.idx.get(entry.id) : null;\n const finalized = finalizeEntry(\n {\n ...entry,\n embedding: entry.embedding ?? prev?.embedding,\n // 显式 createdAt 优先(如合并器保留最早创建时间);未提供时沿用已有条目的\n createdAt: entry.createdAt ?? prev?.createdAt,\n },\n Date.now(),\n );\n // 配置了 embedder 且无既有向量时,计算 embedding\n if (this.embedder && !finalized.embedding) {\n try {\n finalized.embedding = await this.embedder.embed(finalized.content);\n } catch (err) {\n this.onEvent?.({ type: 'embedding:error', id: finalized.id, error: (err as Error).message });\n // embedding 失败不阻断保存(退化为纯 BM25 检索)\n }\n }\n this.idx.add(finalized);\n return finalized;\n });\n }\n\n async get(id: string): Promise<MemoryEntry | null> {\n return this.idx.get(id);\n }\n\n async delete(id: string): Promise<boolean> {\n return this.writeLock.withLock(id, async () => this.idx.remove(id));\n }\n\n async list(limit?: number): Promise<MemoryEntry[]> {\n return this.idx.list(limit);\n }\n\n async search(query: string, limit?: number): Promise<MemorySearchResult[]> {\n return this.idx.search(query, limit);\n }\n\n async searchVectors(queryVec: number[], limit?: number): Promise<MemorySearchResult[]> {\n return this.idx.searchVectors(queryVec, limit);\n }\n\n async touchRecall(id: string, at: number = Date.now()): Promise<void> {\n return this.writeLock.withLock(id, async () => {\n const e = this.idx.get(id);\n if (!e) return;\n // 仅更新检索统计;不更新 updatedAt(updatedAt 表示内容修改时间,\n // 驱动增量合并候选窗口与过期语义,不应被一次检索刷新)\n e.lastRecalledAt = at;\n e.recallCount += 1;\n this.idx.add(e);\n });\n }\n\n async consolidate(\n options?: ConsolidateOptions,\n ): Promise<{ merged: number; pruned: number }> {\n if (!this.consolidator) return { merged: 0, pruned: 0 };\n // 不持全局锁:consolidator 内部经 store.save/delete(逐 id 锁)原子化,\n // 跨 id 的交错为 best-effort(新写入条目未被本次合并处理,下一轮再合并)\n const result = await this.consolidator.run(options);\n this.lastConsolidatedAt = Date.now();\n return result;\n }\n\n async prune(options?: {\n maxAgeMs?: number;\n minConfidence?: number;\n }): Promise<number> {\n const now = Date.now();\n const maxAge = options?.maxAgeMs;\n const minConf = options?.minConfidence ?? 0;\n let removed = 0;\n for (const e of this.idx.all()) {\n const expiredByAge = maxAge != null && now - e.updatedAt > maxAge;\n const expiredByTtl = e.expiresAt != null && now > e.expiresAt;\n const lowConf = e.confidence < minConf;\n if (expiredByAge || expiredByTtl || lowConf) {\n if (await this.delete(e.id)) removed++;\n }\n }\n if (removed > 0) this.onEvent?.({ type: 'prune', removed });\n return removed;\n }\n\n async count(): Promise<number> {\n return this.idx.count();\n }\n\n markConsolidated(at: number = Date.now()): void {\n this.lastConsolidatedAt = at;\n }\n\n async stats(): Promise<MemoryStats> {\n const base = this.idx.stats();\n return { ...base, lastConsolidatedAt: this.lastConsolidatedAt };\n }\n\n dispose(): void {\n // 内存存储无可释放资源(占位,保持接口一致)\n }\n}\n\nexport function createInMemoryStore(): MemoryStore {\n return new InMemoryStore();\n}\n","/**\n * Embedder 接口与余弦相似度工具。\n *\n * 默认不提供任何 embedder 实现(零依赖、零 API Key)。\n * 用户可自行实现 Embedder 接入 ollama / @huggingface/transformers / OpenAI 等。\n */\n\nimport type { Embedder } from '../types';\n\nexport type { Embedder };\n\n/** 余弦相似度。任一为零向量返回 0。 */\nexport function cosine(a: number[], b: number[]): number {\n if (!a || !b || a.length === 0 || a.length !== b.length) return 0;\n let dot = 0;\n let na = 0;\n let nb = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n na += a[i] * a[i];\n nb += b[i] * b[i];\n }\n if (na === 0 || nb === 0) return 0;\n return dot / (Math.sqrt(na) * Math.sqrt(nb));\n}\n\n/**\n * 将一组分数 min-max 归一化到 0..1。\n * 单元素或全相同时返回等分(避免除零)。\n */\nexport function minMaxNormalize(scores: number[]): number[] {\n if (scores.length === 0) return [];\n const min = Math.min(...scores);\n const max = Math.max(...scores);\n if (max - min === 0) return scores.map(() => 1);\n return scores.map((s) => (s - min) / (max - min));\n}\n","/**\n * 混合检索器:BM25(必选)+ 可选 Embedding(向量)双路独立召回后加权融合。\n *\n * 默认无 embedder 时退化为纯 BM25。\n * 有 embedder 时:\n * - BM25 路独立召回 top-`limit*3`\n * - 向量路独立召回 top-`limit*3`(依赖 store 的 searchVectors / VectorIndex,\n * 不受 BM25 候选池封顶 —— 修复「向量召回被 BM25 top-K 限制」问题)\n * - 两路各自 min-max 归一化后按权重融合:final = w_bm25 * bm25_norm + w_embed * cos_norm\n * - 过滤 < minScore,截 limit\n *\n * 兼容路径:\n * - 配置了 embedder 但 store 不支持向量检索(无 searchVectors 能力):\n * 退化为「BM25 候选 + 向量重排」(旧行为),并在 minScore 处诚实标注。\n */\n\nimport { cosine, minMaxNormalize } from './embedder';\nimport type { Embedder, MemorySearchResult } from '../types';\n\n/**\n * 检索器最小契约:任何具备 search(query, limit) 的对象均可作为 BM25 候选源。\n * BM25Retriever 天然满足此接口;插件层也可用 StoreBackedRetriever 包装自定义 store。\n */\nexport interface RetrieverLike {\n search(query: string, limit?: number): Promise<MemorySearchResult[]>;\n}\n\n/** 向量检索源契约(由 store 实现,或插件层包装) */\nexport interface VectorSearchLike {\n searchVectors(queryVec: number[], limit?: number): Promise<MemorySearchResult[]>;\n}\n\nexport interface HybridRetrieverOptions {\n /** BM25 候选源(BM25Retriever 或任何 RetrieverLike 实现) */\n bm25: RetrieverLike;\n /** 可选向量源(store.searchVectors);有 embedder 但无此源时退化为候选重排 */\n vector?: VectorSearchLike;\n embedder?: Embedder;\n /** BM25 权重,默认 0.5 */\n bm25Weight?: number;\n /** embedding 权重,默认 0.5 */\n embedWeight?: number;\n /** 注入阶段最低分数阈值,默认 0.1 */\n minScore?: number;\n}\n\nexport interface HybridSearchOptions {\n /** 覆盖默认 minScore(如注入转换器携带自己的阈值,不再篡改共享 retriever) */\n minScore?: number;\n /**\n * 原始分数模式:不做 min-max 归一化,保留 BM25 / cosine 原始分数。\n * 供合并器等需要「绝对相似度阈值」的场景使用 —— min-max 会把同一查询内\n * 的次优分数压到 0,使绝对阈值(如 0.85)失去意义。\n */\n raw?: boolean;\n}\n\nexport class HybridRetriever {\n bm25: RetrieverLike;\n vector?: VectorSearchLike;\n embedder?: Embedder;\n bm25Weight: number;\n embedWeight: number;\n minScore: number;\n\n constructor(options: HybridRetrieverOptions) {\n this.bm25 = options.bm25;\n this.vector = options.vector;\n this.embedder = options.embedder;\n this.bm25Weight = options.bm25Weight ?? 0.5;\n this.embedWeight = options.embedWeight ?? 0.5;\n this.minScore = options.minScore ?? 0.1;\n }\n\n /** 是否启用了向量检索 */\n get hasEmbedder(): boolean {\n return !!this.embedder;\n }\n\n async search(\n query: string,\n limit = 5,\n opts?: HybridSearchOptions,\n ): Promise<MemorySearchResult[]> {\n const minScore = opts?.minScore ?? this.minScore;\n const raw = opts?.raw ?? false;\n if (limit <= 0) return [];\n\n // BM25 路:有向量能力时放大候选池以提升融合质量\n const bm25Limit = this.embedder ? Math.max(limit * 3, limit) : limit;\n const bm25Results = await this.bm25.search(query, bm25Limit);\n\n // 纯 BM25 模式:分数 min-max 归一化后过滤(或 raw 模式保留原始分数)\n if (!this.embedder) {\n if (bm25Results.length === 0) return [];\n return raw\n ? this.fuseRaw(bm25Results, [], minScore, limit)\n : this.fuseSingle(bm25Results, minScore, limit);\n }\n\n // 尝试向量查询\n let queryVec: number[] | null = null;\n try {\n queryVec = await this.embedder.embed(query);\n } catch {\n queryVec = null; // 向量失败退化为纯 BM25\n }\n\n // 无向量源(自定义 store 不支持)或向量查询失败:BM25 候选 + 向量重排(旧行为)\n if (!this.vector || !queryVec || queryVec.length === 0) {\n if (bm25Results.length === 0) return [];\n if (raw) return this.fuseRaw(bm25Results, [], minScore, limit);\n return queryVec && this.vector\n ? this.rerank(bm25Results, queryVec, minScore, limit)\n : this.fuseSingle(bm25Results, minScore, limit);\n }\n\n // 双路独立召回 + 融合\n let vecResults: MemorySearchResult[] = [];\n try {\n vecResults = await this.vector.searchVectors(queryVec, bm25Limit);\n } catch {\n vecResults = [];\n }\n if (bm25Results.length === 0 && vecResults.length === 0) return [];\n return raw\n ? this.fuseRaw(bm25Results, vecResults, minScore, limit)\n : this.fuseDual(bm25Results, vecResults, minScore, limit);\n }\n\n // ─── 融合策略 ───────────────────────────────────────────────────\n\n /** 单路(纯 BM25):归一化 + 过滤 + 截断 */\n private fuseSingle(\n results: MemorySearchResult[],\n minScore: number,\n limit: number,\n ): MemorySearchResult[] {\n const normed = minMaxNormalize(results.map((r) => r.score));\n return results\n .map((r, i) => ({ entry: r.entry, score: normed[i], matchedBy: 'bm25' as const }))\n .filter((r) => r.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n }\n\n /** 候选重排(兼容路径):BM25 候选 + 向量 cosine 加权融合 */\n private rerank(\n bm25Results: MemorySearchResult[],\n queryVec: number[],\n minScore: number,\n limit: number,\n ): MemorySearchResult[] {\n const cosScores = bm25Results.map((r) => {\n const ev = r.entry.embedding;\n return ev && ev.length === queryVec.length ? cosine(queryVec, ev) : 0;\n });\n const bm25Normed = minMaxNormalize(bm25Results.map((r) => r.score));\n const cosNormed = minMaxNormalize(cosScores);\n const wSum = this.bm25Weight + this.embedWeight || 1;\n\n return bm25Results\n .map((r, i) => ({\n entry: r.entry,\n score: (this.bm25Weight * bm25Normed[i] + this.embedWeight * cosNormed[i]) / wSum,\n matchedBy: (cosNormed[i] >= bm25Normed[i] ? 'embedding' : 'hybrid') as\n | 'embedding'\n | 'hybrid',\n }))\n .filter((r) => r.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n }\n\n /**\n * 原始分数融合(raw 模式):保留检索源原始分数,不做 min-max 归一化,\n * 供合并器按「绝对相似度阈值」判定可合并项。双路命中时取 max\n * (任一来源判定相似即相似),避免归一化把次优分数抹平。\n *\n * 量纲说明:BM25 分数在 BM25Retriever 层已按 Σidf 归一化到 [0,1]\n * (完全同文≈1),cosine 本身在 [0,1],因此这里的绝对阈值(如 0.85)\n * 对两路统一成立。\n */\n private fuseRaw(\n bm25Results: MemorySearchResult[],\n vecResults: MemorySearchResult[],\n minScore: number,\n limit: number,\n ): MemorySearchResult[] {\n const union = new Map<\n string,\n { entry: MemorySearchResult['entry']; score: number; matchedBy: 'bm25' | 'embedding' | 'hybrid' }\n >();\n for (const r of bm25Results) {\n union.set(r.entry.id, { entry: r.entry, score: r.score, matchedBy: 'bm25' });\n }\n for (const r of vecResults) {\n const prev = union.get(r.entry.id);\n if (!prev) {\n union.set(r.entry.id, { entry: r.entry, score: r.score, matchedBy: 'embedding' });\n } else {\n prev.score = Math.max(prev.score, r.score);\n if (prev.matchedBy === 'bm25') prev.matchedBy = 'hybrid';\n }\n }\n return [...union.values()]\n .filter((x) => x.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n }\n\n /** 双路独立召回融合:按 id 取并集,加权求和,再按权重和归一化 */\n private fuseDual(\n bm25Results: MemorySearchResult[],\n vecResults: MemorySearchResult[],\n minScore: number,\n limit: number,\n ): MemorySearchResult[] {\n const bm25Normed = minMaxNormalize(bm25Results.map((r) => r.score));\n const vecNormed = minMaxNormalize(vecResults.map((r) => r.score));\n const wB = this.bm25Weight;\n const wE = this.embedWeight;\n const wSum = wB + wE || 1;\n\n const union = new Map<\n string,\n { entry: MemorySearchResult['entry']; score: number; matchedBy: 'bm25' | 'embedding' | 'hybrid' }\n >();\n bm25Results.forEach((r, i) => {\n union.set(r.entry.id, { entry: r.entry, score: bm25Normed[i] * wB, matchedBy: 'bm25' });\n });\n vecResults.forEach((r, i) => {\n const prev = union.get(r.entry.id);\n const s = vecNormed[i] * wE;\n if (!prev) {\n union.set(r.entry.id, { entry: r.entry, score: s, matchedBy: 'embedding' });\n } else {\n prev.score += s;\n if (prev.matchedBy === 'bm25') prev.matchedBy = 'hybrid';\n }\n });\n\n return [...union.values()]\n .map((x) => ({ entry: x.entry, score: x.score / wSum, matchedBy: x.matchedBy }))\n .filter((r) => r.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n }\n}\n","/**\n * packages/core/types.ts - 核心类型定义\n *\n * 这是整个框架的类型基础,不依赖任何外部实现。\n * 所有消息、内容块、模型、工具等类型都在此定义。\n */\n\n// ─── 内容块类型 ───────────────────────────────────────────────────\n\nexport interface TextContent {\n type: 'text';\n text: string;\n}\n\nexport interface ImageContent {\n type: 'image';\n data: string; // base64 或 URL\n mimeType: string;\n}\n\nexport interface ToolCallContent {\n type: 'toolCall';\n id: string;\n name: string;\n arguments: unknown;\n}\n\nexport interface ThinkingContent {\n type: 'thinking';\n text: string;\n}\n\nexport type ContentBlock =\n | TextContent\n | ImageContent\n | ToolCallContent\n | ThinkingContent;\n\n// ─── 消息类型 ─────────────────────────────────────────────────────\n\nexport interface BaseMessage {\n role: string;\n content: string | ContentBlock[];\n timestamp: number;\n [key: string]: unknown;\n}\n\nexport interface UserMessage extends BaseMessage {\n role: 'user';\n}\n\nexport interface AssistantMessage extends BaseMessage {\n role: 'assistant';\n model?: string;\n provider?: string;\n stopReason?: string;\n usage?: Usage;\n errorMessage?: string;\n responseId?: string;\n}\n\nexport interface ToolResultMessage extends BaseMessage {\n role: 'toolResult';\n toolCallId: string;\n toolName: string;\n isError: boolean;\n}\n\nexport interface SystemMessage extends BaseMessage {\n role: 'system';\n}\n\nexport type Message = UserMessage | AssistantMessage | ToolResultMessage | SystemMessage;\n\n// ─── Token 用量 ───────────────────────────────────────────────────\n\n/**\n * 用量与费用。与 aipack/ai 的 Usage 结构对齐:\n * cacheRead/cacheWrite/reasoning 为可选字段,旧消费者无需感知。\n */\nexport interface Usage {\n input: number;\n output: number;\n total: number;\n /** 命中缓存读取的 token 数(不计入 input) */\n cacheRead?: number;\n /** 写入缓存的 token 数 */\n cacheWrite?: number;\n /** 推理 token 数(completion 的子集) */\n reasoning?: number;\n /** 还原后的原始 token 总数(input + output + cacheRead + cacheWrite) */\n totalTokens?: number;\n}\n\n// ─── 模型定义 ─────────────────────────────────────────────────────\n\nexport interface Model {\n id: string;\n name: string;\n provider: string;\n contextWindow: number;\n maxTokens: number;\n reasoning: boolean;\n [key: string]: unknown;\n}\n\n// ─── 工具定义 ─────────────────────────────────────────────────────\n\nexport interface ToolResult {\n content: ContentBlock[];\n details: unknown;\n usage?: Usage;\n}\n\nexport interface Tool {\n name: string;\n description: string;\n parameters: unknown; // JSON Schema 或 TypeBox schema\n /**\n * 所需权限能力声明(如 'shell:exec' / 'fs:write' / 'memory:write' / 'network:fetch')。\n * 供框架级 PermissionPolicy 裁决使用;未声明视为安全工具。\n */\n permissions?: string[];\n execute: (\n toolCallId: string,\n args: unknown,\n signal?: AbortSignal,\n ) => Promise<ToolResult>;\n prepareArguments?: (args: unknown) => unknown;\n}\n\n// ─── 上下文 ───────────────────────────────────────────────────────\n\nexport interface Context {\n systemPrompt: string;\n messages: Message[];\n tools?: Tool[];\n}\n\n// ─── 流式事件 ─────────────────────────────────────────────────────\n\nexport interface StreamOptions {\n signal?: AbortSignal;\n reasoning?: string;\n sessionId?: string;\n /** 重试回调:provider 内 retry() 真正退避重试时调用(可观测/埋点用) */\n onRetryAttempt?: (info: { attempt: number; error: unknown; delayMs: number }) => void;\n}\n\nexport type StreamEvent =\n | { type: 'start'; partial: { content: ContentBlock[] } }\n | { type: 'text_delta'; delta: string }\n | { type: 'thinking_delta'; delta: string }\n | { type: 'tool_call_start'; id: string; name: string }\n | { type: 'tool_call_delta'; id: string; delta: string }\n | { type: 'tool_call_end'; id: string }\n | { type: 'done'; message: AssistantMessage }\n | { type: 'error'; message: AssistantMessage };\n\nexport type StreamResult = AsyncIterable<StreamEvent>;\n\n/**\n * 流式函数类型 - 模型提供者需实现此接口\n *\n * 是一个可替换的核心能力。\n * 用户通过提供不同的 streamFn 来接入不同的 LLM 提供商。\n */\nexport type StreamFn = (\n model: Model,\n context: Context,\n options?: StreamOptions,\n) => StreamResult;\n\n// ─── Agent 状态 ───────────────────────────────────────────────────\n\nexport type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'max';\n\nexport interface AgentState {\n systemPrompt: string;\n model: Model;\n thinkingLevel: ThinkingLevel;\n tools: Tool[];\n messages: Message[];\n isStreaming: boolean;\n streamingMessage?: Message;\n pendingToolCalls: Set<string>;\n errorMessage?: string;\n}\n\n// ─── 工具函数 ─────────────────────────────────────────────────────\n\nexport function extractText(content: string | ContentBlock[]): string {\n if (typeof content === 'string') return content;\n return content\n .filter((c): c is TextContent => c.type === 'text')\n .map(c => c.text)\n .join('');\n}\n\nexport function extractToolCalls(content: string | ContentBlock[]): ToolCallContent[] {\n if (typeof content === 'string') return [];\n return content.filter((c): c is ToolCallContent => c.type === 'toolCall');\n}\n\nexport function createTextContent(text: string): TextContent {\n return { type: 'text', text };\n}\n\nexport function createEmptyUsage(): Usage {\n return { input: 0, output: 0, total: 0 };\n}\n","/**\n * ContextResource - 上下文资源\n *\n * 代表上下文中的单个资源单元(消息、工具调用、工具结果、状态快照等)。\n * 每个 Resource 拥有唯一标识,并可声明对其他 Resource 的依赖。\n */\n\n// ─── 资源类型 ─────────────────────────────────────────────────────\n\nexport type ResourceType =\n | 'user_message'\n | 'assistant_message'\n | 'tool_call'\n | 'tool_result'\n | 'system_message'\n | 'state_snapshot'\n | 'compaction_summary'\n | 'custom';\n\nexport type ResourceRole = 'user' | 'assistant' | 'toolResult' | 'system' | string;\n\n// ─── 资源接口 ─────────────────────────────────────────────────────\n\nexport interface ContextResource {\n /** 唯一标识 */\n readonly id: string;\n /** 资源类型 */\n readonly type: ResourceType;\n /** 消息角色 */\n readonly role: ResourceRole;\n /** 资源内容(消息体或工具数据) */\n readonly content: unknown;\n /** 时间戳 */\n readonly timestamp: number;\n /** 依赖的其他资源 ID(如 tool_result 依赖 tool_call) */\n readonly dependencies: string[];\n /** 资源元数据 */\n readonly meta: Record<string, unknown>;\n /** 是否为关键资源(压缩时不可移除) */\n readonly pinned: boolean;\n}\n\n// ─── 资源构建器 ───────────────────────────────────────────────────\n\nexport class ContextResourceBuilder {\n private _id: string;\n private _type: ResourceType = 'custom';\n private _role: ResourceRole = 'user';\n private _content: unknown = '';\n private _timestamp: number = Date.now();\n private _dependencies: string[] = [];\n private _meta: Record<string, unknown> = {};\n private _pinned: boolean = false;\n\n constructor(id?: string) {\n this._id = id || `res_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n }\n\n id(id: string): this {\n this._id = id;\n return this;\n }\n\n type(t: ResourceType): this {\n this._type = t;\n return this;\n }\n\n role(r: ResourceRole): this {\n this._role = r;\n return this;\n }\n\n content(c: unknown): this {\n this._content = c;\n return this;\n }\n\n timestamp(ts: number): this {\n this._timestamp = ts;\n return this;\n }\n\n dependsOn(...ids: string[]): this {\n this._dependencies.push(...ids);\n return this;\n }\n\n meta(key: string, value: unknown): this {\n this._meta[key] = value;\n return this;\n }\n\n pinned(p: boolean = true): this {\n this._pinned = p;\n return this;\n }\n\n build(): ContextResource {\n return {\n id: this._id,\n type: this._type,\n role: this._role,\n content: this._content,\n timestamp: this._timestamp,\n dependencies: this._dependencies,\n meta: this._meta,\n pinned: this._pinned,\n };\n }\n}\n\n// ─── 工厂函数 ─────────────────────────────────────────────────────\n\nexport function createMessageResource(\n role: ResourceRole,\n content: unknown,\n options?: Partial<ContextResource>,\n): ContextResource {\n const type: ResourceType =\n role === 'user' ? 'user_message'\n : role === 'assistant' ? 'assistant_message'\n : role === 'system' ? 'system_message'\n : 'custom';\n\n return new ContextResourceBuilder()\n .type(type)\n .role(role)\n .content(content)\n .timestamp(options?.timestamp ?? Date.now())\n .build();\n}\n\nexport function createToolCallResource(\n toolCallId: string,\n toolName: string,\n args: unknown,\n options?: Partial<ContextResource>,\n): ContextResource {\n return new ContextResourceBuilder()\n .id(toolCallId)\n .type('tool_call')\n .role('assistant')\n .content({ toolName, args })\n .meta('toolName', toolName)\n .meta('toolCallId', toolCallId)\n .timestamp(options?.timestamp ?? Date.now())\n .build();\n}\n\nexport function createToolResultResource(\n toolCallId: string,\n toolName: string,\n result: unknown,\n isError: boolean,\n dependsOn: string,\n options?: Partial<ContextResource>,\n): ContextResource {\n return new ContextResourceBuilder()\n .id(`${toolCallId}_result`)\n .type('tool_result')\n .role('toolResult')\n .content(result)\n .dependsOn(dependsOn)\n .meta('toolName', toolName)\n .meta('toolCallId', toolCallId)\n .meta('isError', isError)\n .timestamp(options?.timestamp ?? Date.now())\n .build();\n}\n","/**\n * Transformer - 上下文转换器\n *\n * Transformer 负责对 ContextResource 集合进行转换,\n * 例如压缩、清理、配对修复、状态快照注入等。\n * Runtime 按数组顺序链式执行转换器:上一个转换器的输出作为下一个的输入。\n */\n\nimport type { ContextResource } from './context-resource';\nimport type { TaskGraph } from './task-graph';\n\n// ─── 转换上下文 ───────────────────────────────────────────────────\n\nexport interface TransformContext {\n /** 当前任务图 */\n graph: TaskGraph;\n /** 运行时提供的额外上下文 */\n runtime: TransformRuntime;\n /** 中止信号 */\n signal?: AbortSignal;\n}\n\nexport interface TransformRuntime {\n /** 会话标识 */\n sessionKey: string;\n /** 当前回合数 */\n turn: number;\n /** 工作区路径 */\n workspace?: string;\n /** 运行时配置 */\n config?: Record<string, unknown>;\n /** 当前模型的上下文窗口(token),供 token 级截断转换器使用 */\n contextWindow?: number;\n /** 当前模型最大输出 token */\n maxTokens?: number;\n /** token 预算占 contextWindow 的比例(默认 0.8) */\n contextBudgetRatio?: number;\n}\n\n// ─── 转换器接口 ───────────────────────────────────────────────────\n\nexport interface ContextTransformer {\n /** 转换器名称 */\n readonly name: string;\n /**\n * 执行转换\n * @param resources 输入资源列表\n * @param context 转换上下文\n * @returns 转换后的资源列表\n */\n transform(\n resources: ContextResource[],\n context: TransformContext,\n ): Promise<ContextResource[]>;\n}\n\n// ─── 转换器选项 ───────────────────────────────────────────────────\n\nexport interface TransformerOptions {\n /** 额外配置 */\n config?: Record<string, unknown>;\n}\n\n// ─── 抽象基类 ─────────────────────────────────────────────────────\n\nexport abstract class BaseTransformer implements ContextTransformer {\n abstract readonly name: string;\n\n protected config: Record<string, unknown>;\n\n constructor(options: TransformerOptions = {}) {\n this.config = options.config ?? {};\n }\n\n async transform(\n resources: ContextResource[],\n context: TransformContext,\n ): Promise<ContextResource[]> {\n return this.run(resources, context);\n }\n\n protected abstract run(\n resources: ContextResource[],\n context: TransformContext,\n ): Promise<ContextResource[]>;\n}\n","/**\n * Extension - 扩展插件系统\n *\n * Extension 通过 Tapable 钩子在 Runtime 生命周期的关键节点注入逻辑,\n * 无需修改 Runtime 核心,实现开闭原则。\n */\n\nimport { AsyncSeriesHook, AsyncSeriesWaterfallHook } from './tapable';\nimport type { Request } from './request';\nimport type { Result } from './result';\nimport type { ContextResource } from './context-resource';\nimport type { BeforeToolCallDecision, AfterToolCallDecision } from './tool-hooks';\n\n// ─── Runtime 钩子集合 ─────────────────────────────────────────────\n\nexport interface RuntimeHooks {\n /** Runtime 初始化前 */\n beforeInitialize: AsyncSeriesHook<[Request]>;\n /** Runtime 初始化后 */\n afterInitialize: AsyncSeriesHook<[Request]>;\n /** 请求处理前(可修改请求) */\n beforeRun: AsyncSeriesWaterfallHook<Request>;\n /** 资源构建后、转换前(可修改资源) */\n beforeTransform: AsyncSeriesWaterfallHook<ContextResource[]>;\n /** 资源转换后、模型调用前(可修改资源) */\n afterTransform: AsyncSeriesWaterfallHook<ContextResource[]>;\n /** 模型调用后、结果构建前 */\n beforeEmit: AsyncSeriesHook<[Result]>;\n /** 结果构建后 */\n afterEmit: AsyncSeriesHook<[Result]>;\n /**\n * 运行结束。\n * 第二参数为最终 Request(可选,向后兼容)。\n * 单会话模式下会话标识由 Runtime 持有;多会话场景请创建独立 Runtime 实例。\n */\n done: AsyncSeriesHook<[Result, Request?]>;\n /** 运行失败 */\n failed: AsyncSeriesHook<[Error, Request]>;\n /**\n * 工具执行前(参数校验后)。流转 BeforeToolCallDecision:\n * 任一 tap 设 block 则该工具不执行(生成拒绝结果);\n * 设 terminate 则终止整个 run(本轮工具结束后停止 runLoop);\n * 可改写 args。第二参数为 ToolCallContext。\n */\n beforeToolCall: AsyncSeriesWaterfallHook<BeforeToolCallDecision>;\n /**\n * 工具执行后、最终事件发出前。流转 AfterToolCallDecision:\n * 可改写 result(waterfall),设 terminate 终止整个 run。\n * 第二参数为 ToolCallContext。\n */\n afterToolCall: AsyncSeriesWaterfallHook<AfterToolCallDecision>;\n}\n\n// ─── Extension 接口 ───────────────────────────────────────────────\n\nexport interface Extension {\n /** 扩展名称 */\n readonly name: string;\n /**\n * 应用扩展到 Runtime\n * 通过 hooks.tap 注册回调\n */\n apply(hooks: RuntimeHooks, context: ExtensionContext): void;\n}\n\n// ─── 扩展上下文 ───────────────────────────────────────────────────\n\nexport interface ExtensionContext {\n /** Runtime 配置 */\n readonly config: Record<string, unknown>;\n /** 工作区路径 */\n readonly workspace: string;\n /** 会话标识 */\n readonly sessionKey: string;\n /** 共享状态(Extension 间通信) */\n readonly shared: Map<string, unknown>;\n}\n\n// ─── Extension 管理器 ─────────────────────────────────────────────\n\nexport class ExtensionManager {\n private extensions: Extension[] = [];\n private hooks: RuntimeHooks;\n\n constructor() {\n this.hooks = this.createHooks();\n }\n\n private createHooks(): RuntimeHooks {\n return {\n beforeInitialize: new AsyncSeriesHook('beforeInitialize'),\n afterInitialize: new AsyncSeriesHook('afterInitialize'),\n beforeRun: new AsyncSeriesWaterfallHook('beforeRun'),\n beforeTransform: new AsyncSeriesWaterfallHook('beforeTransform'),\n afterTransform: new AsyncSeriesWaterfallHook('afterTransform'),\n beforeEmit: new AsyncSeriesHook('beforeEmit'),\n afterEmit: new AsyncSeriesHook('afterEmit'),\n done: new AsyncSeriesHook('done'),\n failed: new AsyncSeriesHook('failed'),\n beforeToolCall: new AsyncSeriesWaterfallHook('beforeToolCall'),\n afterToolCall: new AsyncSeriesWaterfallHook('afterToolCall'),\n };\n }\n\n /** 注册扩展 */\n register(extension: Extension): this {\n this.extensions.push(extension);\n return this;\n }\n\n /** 批量注册扩展 */\n registerAll(extensions: Extension[]): this {\n for (const ext of extensions) {\n this.register(ext);\n }\n return this;\n }\n\n /** 应用所有扩展到钩子 */\n applyAll(context: ExtensionContext): void {\n for (const ext of this.extensions) {\n try {\n ext.apply(this.hooks, context);\n } catch (err) {\n // 单个 Extension 失败不影响其他 Extension,但需可观测\n console.warn(`[Extension] \"${ext.name}\" apply 失败:`, (err as Error)?.message ?? err);\n }\n }\n }\n\n /** 获取钩子集合 */\n getHooks(): RuntimeHooks {\n return this.hooks;\n }\n\n /** 移除扩展 */\n unregister(name: string): boolean {\n const idx = this.extensions.findIndex(e => e.name === name);\n if (idx === -1) return false;\n this.extensions.splice(idx, 1);\n return true;\n }\n\n /** 获取所有已注册的扩展 */\n getExtensions(): Extension[] {\n return [...this.extensions];\n }\n\n /** 清空所有扩展 */\n clear(): void {\n this.extensions = [];\n }\n}\n\n// ─── 抽象基类 ─────────────────────────────────────────────────────\n\nexport abstract class BaseExtension implements Extension {\n abstract readonly name: string;\n\n apply(hooks: RuntimeHooks, context: ExtensionContext): void {\n this.setup(hooks, context);\n }\n\n protected abstract setup(hooks: RuntimeHooks, context: ExtensionContext): void;\n}\n","/**\n * 捕获抽取器:把一轮对话(用户消息 + 助手回答 + 工具)压成一条记忆。\n *\n * - 零-LLM 模式(默认):抽取关键词概念,content 截断为 `Q: ...\\nA: ...`。\n * - LLM 模式(可选 summarizeFn):调用外部摘要函数,失败回退到零-LLM。\n */\n\nimport { extractConcepts } from '../retrieval/tokenizer';\nimport type { SummarizeFn } from '../types';\n\nexport interface ExtractResult {\n content: string;\n concepts: string[];\n /** 是否经过 LLM 摘要 */\n summarized: boolean;\n}\n\nexport interface ExtractorOptions {\n /** 概念数上限,默认 8 */\n maxConcepts?: number;\n /** content 最大字符数,默认 2000 */\n maxChars?: number;\n}\n\n/** 截断文本到 maxChars(保留尾部省略号) */\nfunction truncate(text: string, max: number): string {\n if (text.length <= max) return text;\n return `${text.slice(0, max - 1)}…`;\n}\n\n/** 取首句(按中英文句末标点切分),并截断到 max */\nfunction firstSentence(text: string, max: number): string {\n const trimmed = text.trim();\n if (!trimmed) return '';\n const match = trimmed.match(/^[^。!?!?.…\\n]+[。!?!?.…]?/);\n const first = match ? match[0].trim() : trimmed;\n return truncate(first, max);\n}\n\n/**\n * 零-LLM 抽取(要点压缩):content = 用户首句 + 助手首句 + 工具(截断)。\n *\n * 相比直接转储整轮对话:\n * - 只保留「提问主体 + 回答主旨」,避免整段原文进索引(防止索引膨胀、\n * 问题文本干扰检索命中);\n * - concepts = 关键词 top-N(供 BM25 与展示)。\n */\nexport function extractFromTurn(\n userMessage: string,\n assistantContent: string,\n toolsUsed: string[],\n options: ExtractorOptions = {},\n): ExtractResult {\n const maxChars = options.maxChars ?? 2000;\n const maxConcepts = options.maxConcepts ?? 8;\n\n const parts: string[] = [];\n if (userMessage) parts.push(`Q: ${firstSentence(userMessage, 160)}`);\n if (assistantContent) parts.push(`A: ${firstSentence(assistantContent, 320)}`);\n if (toolsUsed.length > 0) parts.push(`tools: ${toolsUsed.slice(0, 5).join(', ')}`);\n const content = truncate(parts.join('\\n'), maxChars);\n\n const conceptSource = `${userMessage} ${assistantContent} ${toolsUsed.join(' ')}`;\n const concepts = extractConcepts(conceptSource, maxConcepts);\n\n return { content, concepts, summarized: false };\n}\n\n/**\n * 运行抽取:若提供 summarizeFn 则先尝试 LLM 摘要,失败/返回 null 回退到零-LLM。\n */\nexport async function runCaptureExtractor(\n input: {\n userMessage: string;\n assistantContent: string;\n toolsUsed: string[];\n summarizeFn?: SummarizeFn;\n },\n options: ExtractorOptions = {},\n): Promise<ExtractResult> {\n const { userMessage, assistantContent, toolsUsed, summarizeFn } = input;\n\n if (summarizeFn) {\n try {\n const result = await summarizeFn({\n userMessage,\n assistantContent,\n toolsUsed,\n });\n if (result && result.summary && result.summary.trim()) {\n const maxChars = options.maxChars ?? 2000;\n const concepts =\n result.concepts && result.concepts.length > 0\n ? result.concepts\n : extractConcepts(\n `${userMessage} ${result.summary}`,\n options.maxConcepts ?? 8,\n );\n return {\n content: truncate(result.summary.trim(), maxChars),\n concepts,\n summarized: true,\n };\n }\n } catch {\n // 摘要失败,回退零-LLM\n }\n }\n\n return extractFromTurn(userMessage, assistantContent, toolsUsed, options);\n}\n","/**\n * 记忆捕获扩展。\n *\n * 在 Runtime 生命周期中「静默」捕获每轮对话要点并存为可检索记忆。\n * 参考 agentmemory 的 capture 阶段。\n *\n * 机制:\n * - beforeRun:暂存本轮用户消息(单会话,Runtime 持有唯一 sessionKey)。\n * - done:与本轮结果配对捕获。sessionKey 来自 ExtensionContext(Runtime 级),\n * 与结果配对写入记忆库。框架保证同一 Runtime 的 run 串行执行,无并发错配。\n * - failed:本轮失败不捕获(仅成功回合入库),无残留状态需清理。\n *\n * 每 consolidateEvery 次捕获触发一次合并。\n */\n\nimport { BaseExtension } from '@aipack-ai/agent';\nimport type { RuntimeHooks, ExtensionContext, Request, Result } from '@aipack-ai/agent';\nimport type { MemoryEventSink, MemoryStore } from '../types';\nimport type { SummarizeFn } from '../types';\nimport { runCaptureExtractor } from './extractor';\n\nexport interface CaptureOptions {\n enabled?: boolean;\n /** 可选 LLM 摘要函数(默认关闭,零 token) */\n summarizeFn?: SummarizeFn;\n /** 最小用户消息长度(小于则跳过捕获),默认 12 */\n minLength?: number;\n /** 概念数上限,默认 8 */\n maxConcepts?: number;\n /** content 最大字符数,默认 2000 */\n maxContentChars?: number;\n /** 每 N 次捕获触发一次 consolidate(0=不自动,默认 0) */\n consolidateEvery?: number;\n /** 捕获记忆 TTL(ms),过期后 prune 时清理 */\n ttlMs?: number;\n /** 事件接收器(捕获失败等) */\n onEvent?: MemoryEventSink;\n}\n\nexport class MemoryCaptureExtension extends BaseExtension {\n readonly name = 'memory-capture';\n\n private store: MemoryStore;\n private summarizeFn?: SummarizeFn;\n private minLength: number;\n private maxConcepts: number;\n private maxContentChars: number;\n private consolidateEvery: number;\n private ttlMs?: number;\n private onEvent?: MemoryEventSink;\n\n /** 本轮待捕获的用户消息(sessionKey -> 请求信息) */\n private pending = new Map<string, { message: string }>();\n\n /** 捕获计数(用于触发 consolidate) */\n private captureCount = 0;\n\n constructor(store: MemoryStore, options: CaptureOptions = {}) {\n super();\n this.store = store;\n this.summarizeFn = options.summarizeFn;\n this.minLength = options.minLength ?? 12;\n this.maxConcepts = options.maxConcepts ?? 8;\n this.maxContentChars = options.maxContentChars ?? 2000;\n this.consolidateEvery = options.consolidateEvery ?? 0;\n this.ttlMs = options.ttlMs;\n this.onEvent = options.onEvent;\n }\n\n protected setup(hooks: RuntimeHooks, context: ExtensionContext): void {\n const sessionKey = context.sessionKey;\n // beforeRun:暂存用户消息(不改请求)\n hooks.beforeRun.tapPromise('memory-capture', async (request: Request) => {\n try {\n this.pending.set(sessionKey, { message: request.message });\n } catch {\n // 忽略 stash 失败\n }\n return request;\n });\n\n // done:与本轮结果配对捕获(单会话模式,sessionKey 由 Runtime 持有)\n hooks.done.tapPromise('memory-capture', async (result: Result, _request?: Request) => {\n await this.captureFromResult(result, sessionKey);\n });\n }\n\n private async captureFromResult(result: Result, sessionKey?: string): Promise<void> {\n if (!sessionKey) return;\n\n // 先取后删(避免 delete 后 get 返回 undefined)\n const stashed = this.pending.get(sessionKey);\n this.pending.delete(sessionKey);\n if (!stashed) return;\n\n // 仅捕获成功回合\n if (!result.success) return;\n const userMessage = stashed.message;\n const assistantContent = result.content ?? '';\n if (!userMessage || userMessage.trim().length < this.minLength) return;\n if (!assistantContent || !assistantContent.trim()) return;\n\n try {\n const extracted = await runCaptureExtractor(\n {\n userMessage,\n assistantContent,\n toolsUsed: result.toolsUsed ?? [],\n summarizeFn: this.summarizeFn,\n },\n { maxConcepts: this.maxConcepts, maxChars: this.maxContentChars },\n );\n\n await this.store.save({\n content: extracted.content,\n concepts: extracted.concepts,\n confidence: extracted.summarized ? 0.8 : 0.6,\n source: 'capture',\n sessionKey,\n ttlMs: this.ttlMs,\n });\n\n this.captureCount += 1;\n\n // 周期性合并\n if (this.consolidateEvery > 0 && this.captureCount % this.consolidateEvery === 0) {\n try {\n await this.store.consolidate();\n } catch (err) {\n this.onEvent?.({\n type: 'consolidate:failed',\n error: (err as Error).message,\n });\n // 合并失败不影响捕获\n }\n }\n } catch (err) {\n // 捕获失败不影响运行结果\n this.onEvent?.({ type: 'capture:failed', sessionKey, error: (err as Error).message });\n }\n }\n}\n","/**\n * 注入哨兵(sentinel):在 user 消息内容中包裹记忆块。\n *\n * 为何用 sentinel 而非 resource.meta:\n * aipack 的 messageToResource/resourceToMessage 对 user 消息不保留 meta\n * (context-resource/index.ts:30-38, 112-119)。sentinel 是 content 的一部分,\n * 随消息持久化,下一轮 messagesToResources 重建后仍可在 content 文本中识别并剥离。\n */\n\nimport type { MemorySearchResult } from '../types';\n\nexport const MEMORY_BLOCK_START = '<<<AIPACK_MEMORY>>>';\nexport const MEMORY_BLOCK_END = '<<</AIPACK_MEMORY>>>';\n\n/** 剥离 sentinel 包裹块(含其后的多余空行),返回干净原文 */\nexport function stripMemoryBlock(text: string): string {\n if (!text) return text;\n // 匹配 START ... END(非贪婪)及紧随其后的空行\n const re = new RegExp(\n `${escapeRegex(MEMORY_BLOCK_START)}[\\\\s\\\\S]*?${escapeRegex(MEMORY_BLOCK_END)}\\\\s*`,\n 'g',\n );\n return text.replace(re, '').trim();\n}\n\n/** 判断文本是否包含 sentinel 块 */\nexport function hasMemoryBlock(text: string): boolean {\n if (!text) return false;\n return text.includes(MEMORY_BLOCK_START);\n}\n\n/** 将若干行用 sentinel 包裹(含头尾换行,便于前插进 content) */\nexport function wrapMemoryBlock(lines: string[]): string {\n const body = lines.join('\\n').trimEnd();\n return `${MEMORY_BLOCK_START}\\n${body}\\n${MEMORY_BLOCK_END}`;\n}\n\n/** 由检索结果构造可读的记忆块文本 */\nexport function buildMemoryBlock(results: MemorySearchResult[]): string {\n if (results.length === 0) return '';\n const lines = ['[Relevant memories]'];\n for (const r of results) {\n const score = r.score.toFixed(2);\n const content = r.entry.content.replace(/\\s+/g, ' ').trim().slice(0, 500);\n lines.push(`- ${content} (score=${score}, id=${r.entry.id})`);\n }\n return wrapMemoryBlock(lines);\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n","/**\n * 记忆注入转换器。\n *\n * 需放在 transformers 数组最前(先剥后注),每轮:\n * 1. 剥除所有 user_message 资源内容中已存在的 sentinel 记忆块(清上轮注入,\n * 含已持久化进 session 的)。\n * 2. 取最新 user_message 的纯文本作为检索 query。\n * 3. HybridRetriever 取 top-K,过滤 minScore。\n * 4. 非空则把记忆块前插进最新 user 消息内容(string / ContentBlock[] 两分支)。\n *\n * 为何合并进最新 user 消息而非新增独立资源或 system 资源:\n * - aipack buildContext 会过滤 role==='system' 消息(runtime/index.ts:585),\n * 故 system 注入不会到达模型;\n * - 新增独立 user 资源会产生「连续两条 user 消息」,部分 provider 解析异常;\n * - 合并进最新 user 消息语义自然,且 content 中的 sentinel 可跨轮识别剥离。\n */\n\nimport { BaseTransformer } from '@aipack-ai/agent';\nimport type { ContextResource, ContentBlock, TextContent } from '@aipack-ai/agent';\nimport { ContextResourceBuilder } from '@aipack-ai/agent';\nimport type { HybridRetriever } from '../retrieval/hybrid-retriever';\nimport {\n buildMemoryBlock,\n hasMemoryBlock,\n stripMemoryBlock,\n} from './sentinels';\n\nexport interface InjectionOptions {\n /** 注入 top-K,默认 5 */\n maxMemories?: number;\n /** 最低分数阈值,默认 0.1 */\n minScore?: number;\n /** 可选:对检索 query 做变换(如抽取提问主体) */\n queryTransform?: (latestUserText: string) => string;\n /** 可选:命中记忆后的回调(插件层装配为 store.touchRecall,更新检索统计) */\n onRecall?: (ids: string[]) => void | Promise<void>;\n}\n\nexport class MemoryInjectionTransformer extends BaseTransformer {\n readonly name = 'memory-injection';\n\n private retriever: HybridRetriever;\n private maxMemories: number;\n private minScore: number;\n private queryTransform?: (text: string) => string;\n private onRecall?: (ids: string[]) => void | Promise<void>;\n\n constructor(retriever: HybridRetriever, options: InjectionOptions = {}) {\n super();\n this.retriever = retriever;\n this.maxMemories = options.maxMemories ?? 5;\n this.minScore = options.minScore ?? 0.1;\n this.queryTransform = options.queryTransform;\n this.onRecall = options.onRecall;\n // 注意:不再写回共享 retriever.minScore(构造函数不应篡改他人持有的对象),\n // 阈值在每次检索时以参数覆盖传入。\n }\n\n protected async run(\n resources: ContextResource[],\n _context: import('@aipack-ai/agent').TransformContext,\n ): Promise<ContextResource[]> {\n if (resources.length === 0) return resources;\n\n // 1. 剥除所有 user_message 资源中的旧 sentinel 块(不可变:重建新资源)\n let cleaned = resources.map((r) => this.stripResource(r));\n\n // 2. 找最新 user_message(数组中最后一个 user_message)\n let latestIdx = -1;\n for (let i = cleaned.length - 1; i >= 0; i--) {\n if (cleaned[i].type === 'user_message') {\n latestIdx = i;\n break;\n }\n }\n if (latestIdx === -1) return cleaned;\n\n const latest = cleaned[latestIdx];\n const queryText = this.extractUserText(latest);\n if (!queryText || !queryText.trim()) return cleaned;\n\n const query = this.queryTransform ? this.queryTransform(queryText) : queryText;\n\n // 3. 检索 top-K(携带本转换器阈值,不影响共享 retriever 的默认值)\n const results = await this.retriever.search(query, this.maxMemories, {\n minScore: this.minScore,\n });\n if (results.length === 0) return cleaned;\n\n // 4. 更新检索统计(fire-and-forget,不阻塞注入首 token 延迟)\n if (this.onRecall) {\n const ids = results.map((r) => r.entry.id);\n Promise.resolve()\n .then(() => this.onRecall!(ids))\n .catch(() => {\n // 统计失败忽略\n });\n }\n\n // 5. 构造记忆块并前插进最新 user 消息内容\n const block = buildMemoryBlock(results);\n const injected = this.injectIntoResource(latest, block);\n\n cleaned = [...cleaned];\n cleaned[latestIdx] = injected;\n return cleaned;\n }\n\n // ─── 工具方法 ───────────────────────────────────────────────────\n\n /** 从资源中抽取纯文本(兼容 string 与 ContentBlock[]) */\n private extractUserText(resource: ContextResource): string {\n const content = resource.content;\n if (typeof content === 'string') return stripMemoryBlock(content);\n if (Array.isArray(content)) {\n // 拼接所有文本块(已剥除 sentinel)\n return content\n .map((b) => (this.isTextBlock(b) ? stripMemoryBlock(b.text) : ''))\n .join('')\n .trim();\n }\n return '';\n }\n\n private isTextBlock(b: ContentBlock): b is TextContent {\n return (b as TextContent).type === 'text';\n }\n\n /** 剥除单个资源中的 sentinel 块,返回新资源(若无需剥除返回原资源) */\n private stripResource(resource: ContextResource): ContextResource {\n if (resource.type !== 'user_message') return resource;\n const content = resource.content;\n\n if (typeof content === 'string') {\n if (!hasMemoryBlock(content)) return resource;\n return this.rebuildResource(resource, stripMemoryBlock(content));\n }\n\n if (Array.isArray(content)) {\n let changed = false;\n const newBlocks: ContentBlock[] = [];\n for (const b of content) {\n if (this.isTextBlock(b)) {\n if (!hasMemoryBlock(b.text)) {\n newBlocks.push(b);\n continue;\n }\n const stripped = stripMemoryBlock(b.text);\n if (stripped) {\n newBlocks.push({ type: 'text', text: stripped } as TextContent);\n }\n changed = true;\n } else {\n newBlocks.push(b);\n }\n }\n if (!changed) return resource;\n return this.rebuildResource(resource, newBlocks);\n }\n\n return resource;\n }\n\n /** 把记忆块前插进资源内容 */\n private injectIntoResource(resource: ContextResource, block: string): ContextResource {\n const content = resource.content;\n\n if (typeof content === 'string') {\n const original = stripMemoryBlock(content);\n const injected = `${block}\\n\\n${original}`;\n return this.rebuildResource(resource, injected);\n }\n\n if (Array.isArray(content)) {\n // 过滤掉旧 sentinel 文本块,再前插新 sentinel 文本块(保留图片等非文本块)\n const kept: ContentBlock[] = content.filter(\n (b) => !(this.isTextBlock(b) && hasMemoryBlock(b.text)),\n );\n const injected: ContentBlock[] = [\n { type: 'text', text: `${block}\\n\\n` } as TextContent,\n ...kept,\n ];\n return this.rebuildResource(resource, injected);\n }\n\n return resource;\n }\n\n /** 重建资源(保持 id/type/role/timestamp/dependencies/meta/pinned,替换 content) */\n private rebuildResource(\n resource: ContextResource,\n content: string | ContentBlock[],\n ): ContextResource {\n return new ContextResourceBuilder()\n .id(resource.id)\n .type(resource.type)\n .role(resource.role)\n .content(content)\n .timestamp(resource.timestamp)\n .pinned(resource.pinned)\n .build();\n }\n}\n","/**\n * 合并器:去重 / 合并相似记忆 + 修剪过期与低置信度。\n *\n * 参考 agentmemory 的 consolidate 阶段:\n * - 候选窗口 = 自上次合并以来内容有更新的条目(增量合并,避免每次对全库 N 条\n * 逐一检索的 O(N²) 塌陷;首次合并或跨进程重启后为全量)。\n * - 对每个候选:以其 content 作为 query 检索相似记忆,相似度 >= 阈值则合并。\n * - 合并:content 取较长、concepts 并集、置信度取 max + 小奖励(避免旧「累加」\n * 使置信度快速饱和到 1.0 而丧失排序/修剪信号)。\n * - 修剪过期 / 低置信度;超过 maxMemories 时淘汰置信度最低的。\n */\n\nimport type { HybridRetriever } from '../retrieval/hybrid-retriever';\nimport type {\n ConsolidateOptions,\n ConsolidatorLike,\n MemoryEntry,\n MemoryEventSink,\n MemoryStore,\n} from '../types';\n\n/** 合并两条记忆为一条(保留 newer 的 id) */\nfunction mergeTwo(a: MemoryEntry, b: MemoryEntry): MemoryEntry {\n const newer = a.updatedAt >= b.updatedAt ? a : b;\n const older = newer === a ? b : a;\n // content 取较长者(信息量更大),若相近则取较新\n const content =\n newer.content.length >= older.content.length ? newer.content : older.content;\n const concepts = [...new Set([...a.concepts, ...b.concepts])];\n // 置信度取 max + 0.05 小奖励(截断到 1):累加会让记忆快速顶到 1.0,\n // 使 confidence 作为 pruning/排序信号失效\n const confidence = Math.min(1, Math.max(a.confidence, b.confidence) + 0.05);\n const lastRecalledAt = [a.lastRecalledAt, b.lastRecalledAt]\n .filter((t): t is number => t != null)\n .sort()\n .pop();\n\n return {\n id: newer.id,\n content,\n concepts,\n confidence,\n source: 'consolidation',\n sessionKey: newer.sessionKey,\n createdAt: Math.min(a.createdAt, b.createdAt),\n updatedAt: Date.now(),\n lastRecalledAt,\n recallCount: a.recallCount + b.recallCount,\n embedding: newer.embedding ?? older.embedding,\n expiresAt: newer.expiresAt ?? older.expiresAt,\n meta: { ...(older.meta ?? {}), ...(newer.meta ?? {}) },\n };\n}\n\nexport interface ConsolidatorOptions {\n similarityThreshold?: number;\n /** 事件接收器(合并结果/失败) */\n onEvent?: MemoryEventSink;\n}\n\nexport class Consolidator implements ConsolidatorLike {\n private store: MemoryStore;\n private retriever: HybridRetriever;\n private similarityThreshold: number;\n private onEvent?: MemoryEventSink;\n\n constructor(\n store: MemoryStore,\n retriever: HybridRetriever,\n options: ConsolidatorOptions = {},\n ) {\n this.store = store;\n this.retriever = retriever;\n this.similarityThreshold = options.similarityThreshold ?? 0.85;\n this.onEvent = options.onEvent;\n }\n\n async run(\n options: ConsolidateOptions = {},\n ): Promise<{ merged: number; pruned: number }> {\n const start = Date.now();\n const threshold = options.similarityThreshold ?? this.similarityThreshold;\n const all = await this.store.list();\n\n // 增量候选:仅处理上次合并后内容有更新的条目(touchRecall 不再更新 updatedAt,\n // 因此 updatedAt 稳定表示内容修改时间;首次/跨进程重启 lastConsolidatedAt 为空 → 全量)\n const stats = await this.store.stats();\n const since = stats.lastConsolidatedAt;\n const candidates = (since == null ? all : all.filter((e) => e.updatedAt >= since)).sort(\n // 显式按 updatedAt 降序(新条目先处理,可吸收旧的相似条目);\n // 同毫秒写入时以 createdAt 决胜,保证处理顺序确定\n (a, b) => b.updatedAt - a.updatedAt || b.createdAt - a.createdAt,\n );\n\n const processed = new Set<string>();\n let merged = 0;\n\n // 按 updatedAt 降序处理(list 已是降序),优先保留较新的幸存者\n for (const entry of candidates) {\n if (processed.has(entry.id)) continue;\n\n // 用该记忆 content 作为 query 检索相似记忆。\n // raw 模式:保留原始 BM25/cosine 分数,使 similarityThreshold 按「绝对\n // 相似度」判定 —— min-max 归一化会把次优分数压到 0,导致阈值形同虚设。\n let results: Awaited<ReturnType<HybridRetriever['search']>> = [];\n try {\n results = await this.retriever.search(entry.content, 10, { raw: true });\n } catch {\n continue;\n }\n\n const similar = results.filter(\n (r) =>\n r.entry.id !== entry.id &&\n !processed.has(r.entry.id) &&\n r.score >= threshold,\n );\n\n if (similar.length === 0) {\n processed.add(entry.id);\n continue;\n }\n\n // 原子性修复:先合并内存中的幸存者并 save(原子写入),成功后再 delete\n // 相似项。旧实现「先逐条 delete 再 save」,中途失败即丢失已删记忆。\n let survivor = entry;\n for (const r of similar) {\n survivor = mergeTwo(survivor, r.entry);\n processed.add(r.entry.id);\n }\n await this.store.save({\n id: survivor.id,\n content: survivor.content,\n concepts: survivor.concepts,\n confidence: survivor.confidence,\n source: 'consolidation',\n sessionKey: survivor.sessionKey,\n createdAt: survivor.createdAt,\n embedding: survivor.embedding,\n expiresAt: survivor.expiresAt,\n recallCount: survivor.recallCount,\n });\n merged += similar.length;\n for (const r of similar) {\n try {\n await this.store.delete(r.entry.id);\n } catch (err) {\n // delete 失败留下重复条目,可被下一轮合并吸收;不中断整体\n this.onEvent?.({ type: 'consolidate:failed', error: `delete:${(err as Error).message}` });\n }\n }\n processed.add(survivor.id);\n }\n\n // 修剪过期 / 低置信度\n let pruned = 0;\n try {\n pruned += await this.store.prune({\n maxAgeMs: options.maxAgeMs,\n minConfidence: options.minConfidence ?? 0.1,\n });\n } catch (err) {\n this.onEvent?.({ type: 'consolidate:failed', error: (err as Error).message });\n // 修剪失败忽略\n }\n\n // 数量上限:淘汰置信度最低、最旧的\n if (options.maxMemories != null) {\n const after = await this.store.list();\n if (after.length > options.maxMemories) {\n const sorted = [...after].sort(\n (a, b) => b.confidence - a.confidence || b.updatedAt - a.updatedAt,\n );\n const toRemove = sorted.slice(options.maxMemories);\n for (const e of toRemove) {\n const ok = await this.store.delete(e.id);\n if (ok) pruned++;\n }\n }\n }\n\n // 记录合并完成时间(驱动下一轮增量窗口)\n this.store.markConsolidated();\n\n const ms = Date.now() - start;\n this.onEvent?.({ type: 'consolidate', merged, pruned, ms });\n return { merged, pruned };\n }\n}\n","/**\n * Agent 可调用的记忆工具。\n *\n * 参考 agentmemory 的 MCP 工具(save / recall / sessions 等),\n * 但以 aipack 原生 Tool 形式提供,parameters 用纯 JSON Schema(不依赖 TypeBox)。\n *\n * 工具列表:\n * - save_memory(content, concepts?)\n * - search_memory(query, limit?)\n * - list_memories(limit?)\n * - delete_memory(id)\n */\n\nimport type { Tool, ToolResult } from '@aipack-ai/agent';\nimport { createTextContent } from '@aipack-ai/agent';\nimport type { MemoryStore } from '../types';\n\nexport interface MemoryToolsOptions {\n /** list_memories 默认返回上限,默认 20 */\n listLimit?: number;\n /** search_memory 默认返回上限,默认 5 */\n searchLimit?: number;\n /** save_memory 保存的记忆 TTL(ms),过期后 prune 时清理 */\n saveTtlMs?: number;\n}\n\n/** 工具输入硬上限:防止 LLM 误调用写入超长内容 / 超大集合 */\nconst MAX_CONTENT_CHARS = 2000;\nconst MAX_CONCEPTS = 20;\nconst MAX_LIST_LIMIT = 200;\nconst MAX_SEARCH_LIMIT = 50;\n\nfunction clampInt(v: unknown, fallback: number, min: number, max: number): number {\n const n = typeof v === 'number' && Number.isFinite(v) ? Math.floor(v) : fallback;\n return Math.min(max, Math.max(min, n));\n}\n\nexport function createMemoryTools(\n store: MemoryStore,\n options: MemoryToolsOptions = {},\n): Tool[] {\n const listLimit = clampInt(options.listLimit, 20, 1, MAX_LIST_LIMIT);\n const searchLimit = clampInt(options.searchLimit, 5, 1, MAX_SEARCH_LIMIT);\n const saveTtlMs = options.saveTtlMs;\n\n const saveMemory: Tool = {\n name: 'save_memory',\n description:\n '保存一条长期记忆(跨会话保留)。用于记录用户偏好、关键决策、事实约束、架构约定等,' +\n '以便后续会话自动检索注入。content 为记忆正文,concepts 为可选关键词标签。',\n parameters: {\n type: 'object',\n properties: {\n content: { type: 'string', description: '记忆正文' },\n concepts: {\n type: 'array',\n items: { type: 'string' },\n description: '可选关键词 / 概念标签',\n },\n },\n required: ['content'],\n },\n async execute(_toolCallId, args): Promise<ToolResult> {\n const a = (args ?? {}) as { content?: string; concepts?: string[] };\n if (!a.content || !a.content.trim()) {\n return {\n content: [createTextContent('save_memory 失败:content 不能为空')],\n details: { error: 'empty content' },\n };\n }\n const content = a.content.trim().slice(0, MAX_CONTENT_CHARS);\n const concepts = (a.concepts ?? [])\n .filter((c): c is string => typeof c === 'string' && c.length > 0)\n .map((c) => c.trim().slice(0, 50))\n .slice(0, MAX_CONCEPTS);\n return store.save({\n content,\n concepts,\n confidence: 0.7,\n source: 'tool',\n ttlMs: saveTtlMs,\n }).then((entry) => ({\n content: [createTextContent(`已保存记忆(id=${entry.id})`)],\n details: { id: entry.id, saved: true },\n }));\n },\n };\n\n const searchMemory: Tool = {\n name: 'search_memory',\n description:\n '检索与查询相关的长期记忆(BM25 关键词 + 可选向量混合检索)。返回按相关度排序的记忆列表。',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: '检索查询' },\n limit: { type: 'integer', description: '返回上限(默认 5)' },\n },\n required: ['query'],\n },\n async execute(_toolCallId, args): Promise<ToolResult> {\n const a = (args ?? {}) as { query?: string; limit?: number };\n if (!a.query || !a.query.trim()) {\n return {\n content: [createTextContent('search_memory 失败:query 不能为空')],\n details: { error: 'empty query' },\n };\n }\n const limit = clampInt(a.limit, searchLimit, 1, MAX_SEARCH_LIMIT);\n const results = await store.search(a.query.trim(), limit);\n if (results.length === 0) {\n return {\n content: [createTextContent('未找到相关记忆。')],\n details: { count: 0 },\n };\n }\n const lines = results.map(\n (r, i) =>\n `${i + 1}. [score=${r.score.toFixed(2)} id=${r.entry.id}] ${r.entry.content.replace(/\\s+/g, ' ').slice(0, 200)}`,\n );\n return {\n content: [createTextContent(`找到 ${results.length} 条相关记忆:\\n${lines.join('\\n')}`)],\n details: { count: results.length, ids: results.map((r) => r.entry.id) },\n };\n },\n };\n\n const listMemories: Tool = {\n name: 'list_memories',\n description: '列出最近的长期记忆(按更新时间倒序)。',\n parameters: {\n type: 'object',\n properties: {\n limit: { type: 'integer', description: '返回上限(默认 20)' },\n },\n },\n async execute(_toolCallId, args): Promise<ToolResult> {\n const a = (args ?? {}) as { limit?: number };\n const limit = clampInt(a.limit, listLimit, 1, MAX_LIST_LIMIT);\n const entries = await store.list(limit);\n if (entries.length === 0) {\n return {\n content: [createTextContent('当前无任何记忆。')],\n details: { count: 0 },\n };\n }\n const lines = entries.map(\n (e, i) =>\n `${i + 1}. [id=${e.id} conf=${e.confidence.toFixed(2)}] ${e.content.replace(/\\s+/g, ' ').slice(0, 200)}`,\n );\n return {\n content: [\n createTextContent(`共 ${entries.length} 条记忆:\\n${lines.join('\\n')}`),\n ],\n details: { count: entries.length, ids: entries.map((e) => e.id) },\n };\n },\n };\n\n const deleteMemory: Tool = {\n name: 'delete_memory',\n description: '按 id 删除一条长期记忆。',\n parameters: {\n type: 'object',\n properties: {\n id: { type: 'string', description: '记忆 id' },\n },\n required: ['id'],\n },\n async execute(_toolCallId, args): Promise<ToolResult> {\n const a = (args ?? {}) as { id?: string };\n if (!a.id) {\n return {\n content: [createTextContent('delete_memory 失败:id 不能为空')],\n details: { error: 'empty id' },\n };\n }\n const ok = await store.delete(a.id);\n return {\n content: [\n createTextContent(ok ? `已删除记忆(id=${a.id})` : `未找到记忆(id=${a.id})`),\n ],\n details: { id: a.id, deleted: ok },\n };\n },\n };\n\n return [saveMemory, searchMemory, listMemories, deleteMemory];\n}\n","/**\n * 插件聚合入口:createMemoryPlugin(options) → { store, retriever, extensions, transformers, tools, install() }\n *\n * 把 MemoryStore + HybridRetriever + MemoryCaptureExtension + MemoryInjectionTransformer\n * + Consolidator + memory tools 装配成一个开箱即用的插件。\n *\n * 用法(aipack.config.js):\n * import { createMemoryPlugin } from '@aipack-ai/memory';\n * const mem = createMemoryPlugin({ baseDir: '~/.aipack/memory' });\n * const r = mem.install();\n * export default {\n * ..., // provider, model, systemPrompt, sessions...\n * extensions: r.extensions,\n * transformers: r.transformers,\n * tools: r.tools,\n * };\n *\n * 默认配置:FileMemoryStore(持久化)+ 纯 BM25 检索(零依赖)+ 自动捕获 + 自动注入 + 4 个记忆工具。\n * 提供 embedder 后自动升级为 BM25 + 向量混合检索(双路独立召回);提供 summarizeFn 后 capture 走 LLM 摘要。\n */\n\nimport type { Extension, ContextTransformer, Tool } from '@aipack-ai/agent';\nimport { FileMemoryStore } from './store/file-memory-store';\nimport { HybridRetriever } from './retrieval/hybrid-retriever';\nimport type { RetrieverLike, VectorSearchLike } from './retrieval/hybrid-retriever';\nimport { MemoryCaptureExtension } from './capture/capture-extension';\nimport type { CaptureOptions } from './capture/capture-extension';\nimport { MemoryInjectionTransformer } from './injection/injection-transformer';\nimport type { InjectionOptions } from './injection/injection-transformer';\nimport { Consolidator } from './consolidation/consolidator';\nimport { createMemoryTools } from './tools/memory-tools';\nimport type {\n Embedder,\n MemoryEvent,\n MemoryEventSink,\n MemorySearchResult,\n MemoryStore,\n SummarizeFn,\n} from './types';\n\n// ─── StoreBackedRetriever:把任意 MemoryStore 包装成 RetrieverLike ──────────\n\n/**\n * 适配器:将 MemoryStore.search() 包装为 RetrieverLike,供 HybridRetriever 使用。\n *\n * 默认 store(FileMemoryStore / InMemoryStore)的 search() 已内置 BM25,\n * 此适配器直接委托,保证检索与存储数据实时同步。\n * 对于自带检索能力的自定义 store 同样适用。\n */\nclass StoreBackedRetriever implements RetrieverLike {\n constructor(private store: MemoryStore) {}\n\n async search(query: string, limit = 5): Promise<MemorySearchResult[]> {\n return this.store.search(query, limit);\n }\n}\n\n// ─── StoreVectorSource:把 MemoryStore.searchVectors() 包装成向量检索源 ─────\n\n/**\n * 适配器:将 store.searchVectors() 暴露为 VectorSearchLike,供 HybridRetriever\n * 做「独立于 BM25 的向量召回」。\n */\nclass StoreVectorSource implements VectorSearchLike {\n constructor(private store: MemoryStore) {}\n\n async searchVectors(queryVec: number[], limit = 5): Promise<MemorySearchResult[]> {\n return this.store.searchVectors(queryVec, limit);\n }\n}\n\n// ─── 默认事件接收器:失败类事件打印告警 ───────────────────────────────\n\nconst FAILURE_EVENTS: ReadonlySet<MemoryEvent['type']> = new Set([\n 'store:corrupt',\n 'embedding:error',\n 'capture:failed',\n 'consolidate:failed',\n]);\n\nfunction defaultEventSink(event: MemoryEvent): void {\n if (FAILURE_EVENTS.has(event.type)) {\n console.warn(`[aipack-memory] ${event.type}: ${(event as { error?: string }).error ?? ''}`);\n }\n}\n\n// ─── 插件选项与返回类型 ──────────────────────────────────────────────\n\nexport interface MemoryPluginOptions {\n /** FileMemoryStore 存储目录(支持 ~ 开头),默认 <cwd>/.aipack/memory */\n baseDir?: string;\n /** 自定义 store(覆盖默认 FileMemoryStore;需自行保证 search 能力) */\n store?: MemoryStore;\n /** 注入 top-K 上限,默认 5 */\n maxMemories?: number;\n /** 最低相关度阈值,默认 0.1 */\n minScore?: number;\n /** 捕获开关 / 选项,默认 true */\n capture?: boolean | CaptureOptions;\n /** 注入开关 / 选项,默认 true */\n inject?: boolean | InjectionOptions;\n /** 记忆工具开关,默认 true */\n tools?: boolean;\n /** 可选向量化器,配置后启用 BM25 + 向量混合检索 */\n embedder?: Embedder;\n /** 可选 LLM 摘要函数,配置后 capture 走 LLM 摘要(默认零-LLM 要点抽取) */\n summarizeFn?: SummarizeFn;\n /** 每 N 次捕获自动触发一次 consolidate(0=不自动,默认 0) */\n consolidateEvery?: number;\n /** 捕获记忆 TTL(ms),过期后 prune 清理 */\n captureTtlMs?: number;\n /** save_memory 工具保存的记忆 TTL(ms),过期后 prune 清理 */\n toolTtlMs?: number;\n /** 事件接收器(失败/整理/统计等关键节点;默认打印失败告警) */\n onEvent?: MemoryEventSink;\n}\n\nexport interface MemoryPlugin {\n /** 装配好的 store(可直接编程式调用 save/search/consolidate 等) */\n store: MemoryStore;\n /** 装配好的混合检索器 */\n retriever: HybridRetriever;\n /** 扩展列表(capture) */\n extensions: Extension[];\n /** 转换器列表(injection) */\n transformers: ContextTransformer[];\n /** 工具列表(save/search/list/delete) */\n tools: Tool[];\n /** 返回 { extensions, transformers, tools },供 aipack.config.js 展开 */\n install(): {\n extensions: Extension[];\n transformers: ContextTransformer[];\n tools: Tool[];\n };\n /** 释放资源(热重载场景) */\n dispose(): void;\n}\n\n// ─── 辅助:解析 boolean | object 选项 ─────────────────────────────────\n\nfunction isCaptureEnabled(v: boolean | CaptureOptions | undefined): boolean {\n if (v === false) return false;\n return true; // true / undefined / object → 启用\n}\n\nfunction isInjectEnabled(v: boolean | InjectionOptions | undefined): boolean {\n if (v === false) return false;\n return true;\n}\n\nfunction resolveCaptureOptions(\n v: boolean | CaptureOptions | undefined,\n fallback: {\n summarizeFn?: SummarizeFn;\n consolidateEvery?: number;\n ttlMs?: number;\n onEvent?: MemoryEventSink;\n },\n): CaptureOptions {\n if (typeof v === 'object' && v !== null) {\n return {\n ...v,\n summarizeFn: v.summarizeFn ?? fallback.summarizeFn,\n consolidateEvery: v.consolidateEvery ?? fallback.consolidateEvery,\n ttlMs: v.ttlMs ?? fallback.ttlMs,\n onEvent: v.onEvent ?? fallback.onEvent,\n };\n }\n return {\n summarizeFn: fallback.summarizeFn,\n consolidateEvery: fallback.consolidateEvery,\n ttlMs: fallback.ttlMs,\n onEvent: fallback.onEvent,\n };\n}\n\nfunction resolveInjectOptions(\n v: boolean | InjectionOptions | undefined,\n fallback: { maxMemories?: number; minScore?: number },\n): InjectionOptions {\n if (typeof v === 'object' && v !== null) {\n return {\n ...v,\n maxMemories: v.maxMemories ?? fallback.maxMemories,\n minScore: v.minScore ?? fallback.minScore,\n };\n }\n return {\n maxMemories: fallback.maxMemories,\n minScore: fallback.minScore,\n };\n}\n\n// ─── createMemoryPlugin ──────────────────────────────────────────────\n\nexport function createMemoryPlugin(options: MemoryPluginOptions = {}): MemoryPlugin {\n const embedder = options.embedder;\n const onEvent = options.onEvent ?? defaultEventSink;\n\n // 1. Store:自定义优先,否则 FileMemoryStore(带 embedder)\n const store: MemoryStore =\n options.store ??\n new FileMemoryStore({\n baseDir: options.baseDir,\n embedder,\n onEvent,\n });\n\n // 2. Retriever:BM25 + 可选向量双路独立召回\n const bm25Source: RetrieverLike = new StoreBackedRetriever(store);\n const retriever = new HybridRetriever({\n bm25: bm25Source,\n vector: embedder ? new StoreVectorSource(store) : undefined,\n embedder,\n minScore: options.minScore ?? 0.1,\n });\n\n // 3. Consolidator:注入 store,使 store.consolidate() 可用(增量合并)\n const consolidator = new Consolidator(store, retriever, {\n similarityThreshold: 0.85,\n onEvent,\n });\n store.setConsolidator(consolidator);\n\n // 4. Extensions:capture\n const extensions: Extension[] = [];\n if (isCaptureEnabled(options.capture)) {\n const captureOpts = resolveCaptureOptions(options.capture, {\n summarizeFn: options.summarizeFn,\n consolidateEvery: options.consolidateEvery,\n ttlMs: options.captureTtlMs,\n onEvent,\n });\n extensions.push(new MemoryCaptureExtension(store, captureOpts));\n }\n\n // 5. Transformers:injection\n const transformers: ContextTransformer[] = [];\n if (isInjectEnabled(options.inject)) {\n const injectOpts = resolveInjectOptions(options.inject, {\n maxMemories: options.maxMemories,\n minScore: options.minScore,\n });\n transformers.push(\n new MemoryInjectionTransformer(retriever, {\n ...injectOpts,\n // 命中后更新检索统计(fire-and-forget,不阻塞注入);\n // 失败不静默:告警日志便于排障(检索统计丢失不致命)\n onRecall: async (ids: string[]) => {\n await Promise.allSettled(ids.map(async (id) => {\n try {\n await store.touchRecall(id);\n } catch (err) {\n console.warn(`[memory] touchRecall 失败(id=${id}): ${(err as Error).message}`);\n }\n }));\n },\n }),\n );\n }\n\n // 6. Tools\n const tools: Tool[] = [];\n if (options.tools !== false) {\n tools.push(...createMemoryTools(store, { saveTtlMs: options.toolTtlMs }));\n }\n\n return {\n store,\n retriever,\n extensions,\n transformers,\n tools,\n install() {\n return { extensions, transformers, tools };\n },\n dispose() {\n store.dispose();\n },\n };\n}\n"],"mappings":"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","names":["dot"]}
|
|
1
|
+
{"version":3,"sources":["../src/store/file-memory-store.ts","../src/retrieval/tokenizer.ts","../src/retrieval/bm25.ts","../src/retrieval/vector-index.ts","../src/store/memory-index.ts","../src/utils/keyed-mutex.ts","../src/store/in-memory-store.ts","../src/retrieval/embedder.ts","../src/retrieval/hybrid-retriever.ts","../../agent/core/types.ts","../../agent/core/context-resource.ts","../../agent/core/transformer.ts","../../agent/core/extension.ts","../src/capture/extractor.ts","../src/capture/capture-extension.ts","../src/injection/sentinels.ts","../src/injection/injection-transformer.ts","../src/consolidation/consolidator.ts","../src/tools/memory-tools.ts","../src/plugin.ts"],"sourcesContent":["/**\n * 文件记忆存储(默认实现)。\n *\n * 每条记忆一个 JSON 文件:<baseDir>/<encodeURIComponent(id)>.json。\n * 写入采用 temp + rename 原子替换(镜像 aipack FileSessionStorage)。\n * 内存缓存 MemoryIndex + BM25 增量索引;首次访问时懒加载并按 maxAge/expiresAt 惰性清理。\n *\n * 并发安全:同 id 的写操作(save/delete/touchRecall)经 keyed mutex 串行,\n * 避免 read-modify-write 竞态(如 embedding 计算期间丢失另一路更新)。\n *\n * 加载优化:懒加载时并发批量读文件(默认 64 并发),避免逐文件串行 IO;\n * 检索要求条目常驻内存(BM25 倒排 + 向量索引),内存占用与记忆规模成正比\n * (详见 README 限制说明)。\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { MemoryIndex } from './memory-index';\nimport { finalizeEntry } from './in-memory-store';\nimport { KeyedMutex } from '../utils/keyed-mutex';\nimport type {\n ConsolidateOptions,\n ConsolidatorLike,\n Embedder,\n FileMemoryStoreOptions,\n MemoryEntry,\n MemoryEventSink,\n MemorySaveInput,\n MemorySearchResult,\n MemoryStats,\n MemoryStore,\n} from '../types';\n\nfunction resolveBaseDir(baseDir?: string): string {\n if (baseDir) {\n if (baseDir === '~') return os.homedir();\n if (baseDir.startsWith('~/')) return path.join(os.homedir(), baseDir.slice(2));\n if (path.isAbsolute(baseDir)) return baseDir;\n return path.join(process.cwd(), baseDir);\n }\n return path.join(process.cwd(), '.aipack', 'memory');\n}\n\nfunction encodeId(id: string): string {\n return encodeURIComponent(id);\n}\n\n/** 懒加载并发读的批大小 */\nconst LOAD_CONCURRENCY = 64;\n\n/** 并发执行 tasks(每批 LOAD_CONCURRENCY 个),返回完成数量(含失败) */\nasync function runPool<T>(tasks: (() => Promise<T>)[]): Promise<number> {\n let done = 0;\n for (let i = 0; i < tasks.length; i += LOAD_CONCURRENCY) {\n const batch = tasks.slice(i, i + LOAD_CONCURRENCY);\n await Promise.all(\n batch.map(async (t) => {\n try {\n await t();\n } finally {\n done++;\n }\n }),\n );\n }\n return done;\n}\n\nexport class FileMemoryStore implements MemoryStore {\n private baseDir: string;\n private maxAge?: number;\n private idx: MemoryIndex;\n private loaded = false;\n private loading: Promise<void> | null = null;\n private consolidator?: ConsolidatorLike;\n private embedder?: Embedder;\n private onEvent?: MemoryEventSink;\n /** 同 id 写互斥(save/delete/touchRecall) */\n private writeLocks = new KeyedMutex();\n private lastConsolidatedAt?: number;\n\n constructor(\n options: FileMemoryStoreOptions & {\n index?: MemoryIndex;\n embedder?: Embedder;\n onEvent?: MemoryEventSink;\n } = {},\n ) {\n this.baseDir = resolveBaseDir(options.baseDir);\n this.maxAge = options.maxAge;\n this.idx = options.index ?? new MemoryIndex();\n this.embedder = options.embedder;\n this.onEvent = options.onEvent;\n }\n\n get dir(): string {\n return this.baseDir;\n }\n\n setConsolidator(consolidator: ConsolidatorLike): void {\n this.consolidator = consolidator;\n }\n\n // ─── 加载 ───────────────────────────────────────────────────────\n\n /** 懒加载:读取目录所有 JSON → 内存索引。并发安全。 */\n private async ensureLoaded(): Promise<void> {\n if (this.loaded) return;\n if (this.loading) return this.loading;\n\n this.loading = (async () => {\n const start = Date.now();\n let loaded = 0;\n let skipped = 0;\n try {\n await fs.mkdir(this.baseDir, { recursive: true });\n const files = await fs.readdir(this.baseDir);\n const now = Date.now();\n const jsonFiles = files.filter((f) => f.endsWith('.json') && !f.endsWith('.tmp'));\n await runPool(\n jsonFiles.map((f) => async () => {\n const filePath = path.join(this.baseDir, f);\n try {\n const raw = await fs.readFile(filePath, 'utf-8');\n const entry = JSON.parse(raw) as MemoryEntry;\n if (!entry?.id) {\n skipped++;\n return;\n }\n // 惰性过期清理(maxAge 按 updatedAt / expiresAt)\n const expiredByAge =\n this.maxAge != null && now - entry.updatedAt > this.maxAge;\n const expiredByTtl = entry.expiresAt != null && now > entry.expiresAt;\n if (expiredByAge || expiredByTtl) {\n await fs.unlink(filePath).catch(() => {});\n skipped++;\n return;\n }\n this.idx.add(entry);\n loaded++;\n } catch {\n skipped++;\n this.onEvent?.({ type: 'store:corrupt', file: f });\n }\n }),\n );\n } catch (err) {\n // 目录不存在等:记录并继续(空库可用)\n this.onEvent?.({ type: 'store:corrupt', file: `<baseDir> ${(err as Error).message}` });\n }\n this.loaded = true;\n this.loading = null;\n if (loaded > 0 || skipped > 0) {\n this.onEvent?.({\n type: 'store:load',\n loaded,\n skipped,\n ms: Date.now() - start,\n });\n }\n })();\n return this.loading;\n }\n\n private entryPath(id: string): string {\n return path.join(this.baseDir, `${encodeId(id)}.json`);\n }\n\n private async writeEntry(entry: MemoryEntry): Promise<void> {\n await fs.mkdir(this.baseDir, { recursive: true });\n const target = this.entryPath(entry.id);\n const tmp = `${target}.${process.pid}.tmp`;\n await fs.writeFile(tmp, JSON.stringify(entry, null, 2), 'utf-8');\n await fs.rename(tmp, target); // 原子替换\n }\n\n // ─── MemoryStore ────────────────────────────────────────────────\n\n async save(entry: MemorySaveInput): Promise<MemoryEntry> {\n await this.ensureLoaded();\n return this.writeLocks.withLock(entry.id ?? '*', async () => {\n const prev = entry.id ? this.idx.get(entry.id) : null;\n const finalized = finalizeEntry(\n {\n ...entry,\n // 保留 embedding(若已存在且新 entry 未提供)\n embedding: entry.embedding ?? prev?.embedding,\n // 显式 createdAt 优先(如合并器保留最早创建时间);未提供时沿用已有条目的\n createdAt: entry.createdAt ?? prev?.createdAt,\n },\n Date.now(),\n );\n // 配置了 embedder 且无既有向量时,计算 embedding\n if (this.embedder && !finalized.embedding) {\n try {\n finalized.embedding = await this.embedder.embed(finalized.content);\n } catch (err) {\n this.onEvent?.({\n type: 'embedding:error',\n id: finalized.id,\n error: (err as Error).message,\n });\n // embedding 失败不阻断保存(退化为纯 BM25 检索)\n }\n }\n await this.writeEntry(finalized);\n this.idx.add(finalized);\n return finalized;\n });\n }\n\n async get(id: string): Promise<MemoryEntry | null> {\n await this.ensureLoaded();\n return this.idx.get(id);\n }\n\n async delete(id: string): Promise<boolean> {\n await this.ensureLoaded();\n return this.writeLocks.withLock(id, async () => {\n const existed = this.idx.remove(id);\n if (existed) {\n await fs.unlink(this.entryPath(id)).catch(() => {});\n }\n return existed;\n });\n }\n\n async list(limit?: number): Promise<MemoryEntry[]> {\n await this.ensureLoaded();\n return this.idx.list(limit);\n }\n\n async search(query: string, limit?: number): Promise<MemorySearchResult[]> {\n await this.ensureLoaded();\n return this.idx.search(query, limit);\n }\n\n async searchVectors(queryVec: number[], limit?: number): Promise<MemorySearchResult[]> {\n await this.ensureLoaded();\n return this.idx.searchVectors(queryVec, limit);\n }\n\n async touchRecall(id: string, at: number = Date.now()): Promise<void> {\n await this.ensureLoaded();\n return this.writeLocks.withLock(id, async () => {\n const e = this.idx.get(id);\n if (!e) return;\n // 仅更新检索统计;不更新 updatedAt(updatedAt = 内容修改时间,\n // 驱动增量合并候选窗口与过期语义,不应被一次检索刷新)\n e.lastRecalledAt = at;\n e.recallCount += 1;\n await this.writeEntry(e);\n this.idx.add(e);\n });\n }\n\n async consolidate(\n options?: ConsolidateOptions,\n ): Promise<{ merged: number; pruned: number }> {\n if (!this.consolidator) return { merged: 0, pruned: 0 };\n await this.ensureLoaded();\n // 不持全局锁:consolidator 内部经 store.save/delete(逐 id 锁)原子化,\n // 跨 id 的交错为 best-effort(新写入条目未被本次合并处理,下一轮再合并)\n const result = await this.consolidator.run(options);\n this.lastConsolidatedAt = Date.now();\n return result;\n }\n\n async prune(options?: {\n maxAgeMs?: number;\n minConfidence?: number;\n }): Promise<number> {\n await this.ensureLoaded();\n const now = Date.now();\n const maxAge = options?.maxAgeMs;\n const minConf = options?.minConfidence ?? 0;\n let removed = 0;\n for (const e of this.idx.all()) {\n const expiredByAge = maxAge != null && now - e.updatedAt > maxAge;\n const expiredByTtl = e.expiresAt != null && now > e.expiresAt;\n const lowConf = e.confidence < minConf;\n if (expiredByAge || expiredByTtl || lowConf) {\n if (await this.delete(e.id)) removed++;\n }\n }\n if (removed > 0) this.onEvent?.({ type: 'prune', removed });\n return removed;\n }\n\n async count(): Promise<number> {\n await this.ensureLoaded();\n return this.idx.count();\n }\n\n markConsolidated(at: number = Date.now()): void {\n this.lastConsolidatedAt = at;\n }\n\n async stats(): Promise<MemoryStats> {\n await this.ensureLoaded();\n const base = this.idx.stats();\n return { ...base, lastConsolidatedAt: this.lastConsolidatedAt };\n }\n\n dispose(): void {\n // 所有写操作均 await 完成,无后台句柄需释放(占位,保持接口一致)\n }\n}\n\nexport function createFileMemoryStore(options?: FileMemoryStoreOptions): MemoryStore {\n return new FileMemoryStore(options);\n}\n","/**\n * 分词器:支持 latin(小写化 + 按非字母数字分割)与 CJK(逐字符)。\n * 零依赖,适配中英文混合文本的 BM25 索引。\n */\n\n/** 判断字符是否为 CJK 文字:汉字(含扩展/兼容区)、日本假名、韩国谚文 */\nexport function isCJK(ch: string): boolean {\n if (!ch) return false;\n const code = ch.codePointAt(0) ?? 0;\n return (\n (code >= 0x4e00 && code <= 0x9fff) || // CJK 统一表意文字\n (code >= 0x3400 && code <= 0x4dbf) || // CJK 扩展 A\n (code >= 0xf900 && code <= 0xfaff) || // CJK 兼容表意\n (code >= 0x3040 && code <= 0x30ff) || // 日文假名(平假名 + 片假名)\n (code >= 0xac00 && code <= 0xd7af) // 韩文谚文\n );\n}\n\n/** 判断字符是否为 CJK 兼容标点 / 全角符号(粗略范围,常见中文标点) */\nfunction isCJKSymbol(ch: string): boolean {\n const code = ch.charCodeAt(0);\n // CJK 标点 \\u3000-\\u303f、全角 ascii \\uff00-\\uffef\n return (code >= 0x3000 && code <= 0x303f) || (code >= 0xff00 && code <= 0xffef);\n}\n\n/**\n * 常见停用词(中英文小集合),用于概念抽取去停用词。\n * BM25 索引本身不去停用词(保留其 IDF 权重),仅概念抽取时过滤。\n */\nexport const STOPWORDS = new Set<string>([\n // 英文\n 'the', 'a', 'an', 'and', 'or', 'but', 'if', 'then', 'else', 'when', 'of', 'to', 'in',\n 'on', 'at', 'by', 'for', 'with', 'without', 'is', 'are', 'was', 'were', 'be', 'been',\n 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'can', 'could',\n 'should', 'shall', 'may', 'might', 'must', 'i', 'you', 'he', 'she', 'it', 'we', 'they',\n 'this', 'that', 'these', 'those', 'my', 'your', 'his', 'her', 'its', 'our', 'their',\n 'me', 'him', 'us', 'them', 'as', 'from', 'so', 'not', 'no', 'yes',\n // 中文高频虚词\n '的', '了', '是', '在', '我', '你', '他', '她', '它', '们', '这', '那', '和', '与', '或',\n '也', '都', '就', '把', '被', '让', '给', '对', '为', '从', '到', '向', '着', '过', '吧',\n '吗', '呢', '啊', '哦', '嗯', '个', '些', '上', '下', '里', '中',\n]);\n\n/**\n * 分词器:支持 latin(小写化 + 按非字母数字分割)与 CJK(字符 bigram)。\n * 零依赖,适配中日韩英混合文本的 BM25 索引。\n *\n * CJK 采用「相邻两字 bigram」而非逐字 unigram:\n * - bigram 区分度远高于单字(「数据库」vs「数据科学」共享「数据」但不再全靠\n * 「数/据」这种高 df 低 idf 的单字强匹配);\n * - 奇数长度 CJK 串尾部遗留单字,保证单字查询仍可命中。\n * 支持汉字(含扩展/兼容)、日文假名、韩文谚文。\n */\n\n/**\n * 将文本切分为 token 数组。\n * - latin 部分:小写化后按非字母数字字符分割。\n * - CJK 部分:相邻两字 bigram(奇数串尾部补单字)。\n * - 标点 / 空白忽略。\n */\nexport function tokenize(text: string): string[] {\n if (!text) return [];\n const tokens: string[] = [];\n let latinBuf = '';\n let cjkBuf = '';\n\n const flushLatin = () => {\n if (latinBuf) {\n // 小写化 + 按非字母数字分割\n const parts = latinBuf.toLowerCase().match(/[a-z0-9]+/g);\n if (parts) tokens.push(...parts);\n latinBuf = '';\n }\n };\n\n const flushCJK = () => {\n if (!cjkBuf) return;\n const chars = [...cjkBuf];\n if (chars.length === 1) {\n tokens.push(chars[0]);\n } else {\n for (let i = 0; i + 1 < chars.length; i++) {\n tokens.push(chars[i] + chars[i + 1]);\n }\n // 奇数长度:最后一个字无配对,补单字(保证单字查询可命中)\n if (chars.length % 2 === 1) {\n tokens.push(chars[chars.length - 1]);\n }\n }\n cjkBuf = '';\n };\n\n for (const ch of text) {\n if (isCJK(ch)) {\n flushLatin();\n cjkBuf += ch;\n } else if (isCJKSymbol(ch)) {\n flushLatin();\n flushCJK();\n } else {\n flushCJK();\n latinBuf += ch;\n }\n }\n flushLatin();\n flushCJK();\n\n return tokens;\n}\n\n/** 概念抽取:返回非停用词 token 的频次 top-N(用于记忆 concepts 字段) */\nexport function extractConcepts(text: string, maxConcepts = 8): string[] {\n const tokens = tokenize(text);\n const freq = new Map<string, number>();\n for (const t of tokens) {\n if (STOPWORDS.has(t)) continue;\n // 单字符英文跳过;CJK 单字(奇数串尾部)保留,bigram 正常保留\n if (t.length < 2 && !isCJK(t)) continue;\n freq.set(t, (freq.get(t) ?? 0) + 1);\n }\n return [...freq.entries()]\n .sort((a, b) => b[1] - a[1])\n .slice(0, maxConcepts)\n .map(([t]) => t);\n}\n","/**\n * BM25 倒排索引与检索器(零依赖)。\n *\n * 经典 BM25 公式:\n * score(q, d) = Σ_t idf(t) * (tf(t,d) * (k1+1)) / (tf(t,d) + k1*(1 - b + b*|d|/avgdl))\n * idf(t) = ln((N - df(t) + 0.5) / (df(t) + 0.5) + 1)\n *\n * 分数不归一化(由 HybridRetriever 负责 min-max 归一化)。\n */\n\nimport { tokenize } from './tokenizer';\nimport type { MemoryEntry, MemorySearchResult } from '../types';\n\nexport interface BM25Options {\n /** 词频饱和参数,默认 1.5 */\n k1?: number;\n /** 文档长度归一化参数,默认 0.75 */\n b?: number;\n}\n\ninterface DocEntry {\n id: string;\n tokens: string[];\n length: number;\n tf: Map<string, number>;\n}\n\nexport class BM25Index {\n private k1: number;\n private b: number;\n /** 文档集合 */\n private docs = new Map<string, DocEntry>();\n /** 倒排表:token -> 文档 id 集合 */\n private inverted = new Map<string, Set<string>>();\n private totalLength = 0;\n\n constructor(options: BM25Options = {}) {\n this.k1 = options.k1 ?? 1.5;\n this.b = options.b ?? 0.75;\n }\n\n /** 平均文档长度 */\n private get avgdl(): number {\n return this.docs.size === 0 ? 0 : this.totalLength / this.docs.size;\n }\n\n /** 文档总数 */\n get size(): number {\n return this.docs.size;\n }\n\n /** 添加或替换文档(同 id 覆盖) */\n add(id: string, tokens: string[]): void {\n // 若已存在,先移除旧的倒排\n if (this.docs.has(id)) this.remove(id);\n\n const tf = new Map<string, number>();\n for (const t of tokens) tf.set(t, (tf.get(t) ?? 0) + 1);\n\n this.docs.set(id, { id, tokens, length: tokens.length, tf });\n this.totalLength += tokens.length;\n\n for (const t of tf.keys()) {\n let set = this.inverted.get(t);\n if (!set) {\n set = new Set();\n this.inverted.set(t, set);\n }\n set.add(id);\n }\n }\n\n /** 移除文档 */\n remove(id: string): void {\n const doc = this.docs.get(id);\n if (!doc) return;\n this.docs.delete(id);\n this.totalLength -= doc.length;\n for (const t of doc.tf.keys()) {\n const set = this.inverted.get(t);\n if (!set) continue;\n set.delete(id);\n if (set.size === 0) this.inverted.delete(t);\n }\n }\n\n /** 清空索引 */\n clear(): void {\n this.docs.clear();\n this.inverted.clear();\n this.totalLength = 0;\n }\n\n /**\n * 检索:返回 top-N 的 {id, score}(按分数降序)。\n * 只扫描 query token 命中的文档,避免全量计算。\n */\n search(queryTokens: string[], limit = 10): Array<{ id: string; score: number }> {\n if (this.docs.size === 0 || queryTokens.length === 0) return [];\n\n const N = this.docs.size;\n const avgdl = this.avgdl;\n const scores = new Map<string, number>();\n\n for (const t of queryTokens) {\n const set = this.inverted.get(t);\n if (!set || set.size === 0) continue;\n\n const df = set.size;\n const idf = Math.log((N - df + 0.5) / (df + 0.5) + 1);\n\n for (const docId of set) {\n const doc = this.docs.get(docId);\n if (!doc) continue;\n const tf = doc.tf.get(t) ?? 0;\n if (tf === 0) continue;\n\n const denom = tf + this.k1 * (1 - this.b + (this.b * doc.length) / (avgdl || 1));\n const contrib = (idf * (tf * (this.k1 + 1))) / (denom || 1);\n scores.set(docId, (scores.get(docId) ?? 0) + contrib);\n }\n }\n\n return [...scores.entries()]\n .map(([id, score]) => ({ id, score }))\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n }\n\n /**\n * 单个 token 的 idf(未出现返回 0)。\n * 供上层计算查询的理论满分(完全同文、tf=1、len≈avgdl 时的分数),\n * 把无界 BM25 原始分规范化为 [0,1] 相似度。\n */\n idf(token: string): number {\n const df = this.inverted.get(token)?.size ?? 0;\n if (df === 0) return 0;\n const N = this.docs.size;\n return Math.log((N - df + 0.5) / (df + 0.5) + 1);\n }\n}\n\n/**\n * 基于 BM25Index 的检索器,包装为 MemorySearchResult。\n * entries 与 index 由外部维护(store 持有并增量同步)。\n *\n * 分数语义:BM25 原始分无界(取决于词频/idf 与库规模),与 embedding 的\n * cosine 相似度(0..1)量纲不匹配 —— 若直接作为绝对相似度与\n * similarityThreshold(如 0.85)比较,合并几乎永远不会触发。这里除以\n * 查询的理论满分(Σidf,即完全同文时的分数)并截断到 [0,1]:\n * 完全同文 ≈ 1,部分命中按比例衰减,使绝对阈值对 BM25 / cosine 统一成立。\n * 该变换是单调的,对普通检索路径的 min-max 归一化幂等(排序不变)。\n */\nexport class BM25Retriever {\n constructor(\n private index: BM25Index,\n private entries: Map<string, MemoryEntry>,\n ) {}\n\n async search(query: string, limit = 5): Promise<MemorySearchResult[]> {\n const queryTokens = tokenize(query);\n const hits = this.index.search(queryTokens, limit);\n\n // 查询的理论满分:所有 query token 的 idf 之和。\n // 完全同文(tf=1、len≈avgdl)时 BM25 分数 ≈ Σidf,因此 score/Σidf ≈ 1;\n // 词频高或文档更短的命中可能 >1,截断到 1。\n const maxPossible = queryTokens.reduce((acc, t) => acc + this.index.idf(t), 0);\n\n return hits.map(({ id, score }) => ({\n entry: this.entries.get(id)!,\n score: maxPossible > 0 ? Math.min(1, score / maxPossible) : 0,\n matchedBy: 'bm25' as const,\n })).filter((r) => r.entry != null);\n }\n}\n","/**\n * VectorIndex —— 零依赖简化 ANN(近似最近邻)向量索引。\n *\n * 用途:为混合检索提供「独立于 BM25 的向量召回」。\n * 之前向量检索被 BM25 top-K 候选池封顶,本索引保证向量路可独立召回语义相似\n * 而关键词不重叠的记忆。\n *\n * 策略(按规模取舍):\n * - 默认:精确 brute-force cosine(预计算范数),中小规模(< 5 万条)足够快;\n * - 可选 `ivfBuckets`:按主导维度分桶的简化 IVF,查询时探测邻近分桶,\n * 降低大库扫描量(近似召回,用于更大规模)。\n *\n * 共享 entry.embedding 引用(不复制向量),内存开销仅为索引结构本身。\n */\n\n/** 向量检索命中 */\nexport interface VectorSearchResult {\n id: string;\n score: number; // cosine 0..1\n}\n\ninterface VecEntry {\n id: string;\n vec: number[];\n norm: number;\n bucket: number;\n}\n\nexport interface VectorIndexOptions {\n /** IVF 分桶数(0 = 纯 brute-force 精确检索,默认 0) */\n ivfBuckets?: number;\n}\n\nfunction normOf(v: number[]): number {\n let s = 0;\n for (let i = 0; i < v.length; i++) s += v[i] * v[i];\n return Math.sqrt(s);\n}\n\nfunction dot(a: number[], b: number[]): number {\n let s = 0;\n for (let i = 0; i < a.length; i++) s += a[i] * b[i];\n return s;\n}\n\nexport class VectorIndex {\n private entries = new Map<string, VecEntry>();\n /** bucket -> id 列表(仅 ivfBuckets > 0 时使用) */\n private buckets = new Map<number, string[]>();\n private dim = 0;\n private bucketCount: number;\n\n constructor(options: VectorIndexOptions = {}) {\n this.bucketCount = options.ivfBuckets ?? 0;\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n /** 添加或替换向量(同 id 覆盖)。维度不一致时忽略,返回是否生效 */\n add(id: string, vector: number[]): boolean {\n if (!vector || vector.length === 0) return false;\n if (this.dim === 0) this.dim = vector.length;\n if (vector.length !== this.dim) return false;\n\n const prev = this.entries.get(id);\n if (prev && this.bucketCount > 0) this.removeFromBucket(id, prev.bucket);\n\n const entry: VecEntry = {\n id,\n vec: vector,\n norm: normOf(vector),\n bucket: this.bucketOf(vector),\n };\n this.entries.set(id, entry);\n\n if (this.bucketCount > 0) {\n let list = this.buckets.get(entry.bucket);\n if (!list) {\n list = [];\n this.buckets.set(entry.bucket, list);\n }\n if (!list.includes(id)) list.push(id);\n }\n return true;\n }\n\n remove(id: string): boolean {\n const e = this.entries.get(id);\n if (!e) return false;\n this.entries.delete(id);\n if (this.bucketCount > 0) this.removeFromBucket(id, e.bucket);\n return true;\n }\n\n clear(): void {\n this.entries.clear();\n this.buckets.clear();\n this.dim = 0;\n }\n\n /** 检索 top-k(cosine 降序,score > 0) */\n search(query: number[], k: number): VectorSearchResult[] {\n if (this.entries.size === 0 || k <= 0 || !query || query.length === 0) return [];\n if (query.length !== this.dim) return [];\n\n const qNorm = normOf(query);\n if (qNorm === 0) return [];\n\n const pool =\n this.bucketCount > 0\n ? this.probeCandidates(query, k * 4)\n : [...this.entries.values()];\n\n const results: VectorSearchResult[] = [];\n for (const e of pool) {\n const score = dot(query, e.vec) / (qNorm * e.norm || 1);\n if (!(score > 0)) continue;\n results.push({ id: e.id, score });\n }\n\n results.sort((a, b) => b.score - a.score);\n return results.slice(0, k);\n }\n\n /** 主导维度(argmax |v|)分桶 */\n private bucketOf(vec: number[]): number {\n if (this.bucketCount <= 0) return 0;\n let best = 0;\n let bestAbs = -1;\n for (let i = 0; i < vec.length; i++) {\n const a = Math.abs(vec[i]);\n if (a > bestAbs) {\n bestAbs = a;\n best = i;\n }\n }\n return best % this.bucketCount;\n }\n\n /** 从查询桶开始探测邻近分桶,直到凑够 minCandidates */\n private probeCandidates(query: number[], minCandidates: number): VecEntry[] {\n const qBucket = this.bucketOf(query);\n const out: VecEntry[] = [];\n for (let r = 0; r < this.bucketCount && out.length < minCandidates; r++) {\n const list = this.buckets.get((qBucket + r) % this.bucketCount);\n if (!list) continue;\n for (const id of list) {\n const e = this.entries.get(id);\n if (e) out.push(e);\n if (out.length >= minCandidates) break;\n }\n }\n return out;\n }\n\n private removeFromBucket(id: string, bucket: number): void {\n const list = this.buckets.get(bucket);\n if (!list) return;\n const i = list.indexOf(id);\n if (i !== -1) list.splice(i, 1);\n if (list.length === 0) this.buckets.delete(bucket);\n }\n}\n","/**\n * 内存索引:维护 entries 集合 + BM25 倒排索引,供两个 store 复用。\n *\n * 职责:get / list / count / search / add / remove。\n * 不负责持久化(由具体 store 处理)。\n */\n\nimport { BM25Index, BM25Retriever } from '../retrieval/bm25';\nimport { VectorIndex } from '../retrieval/vector-index';\nimport { tokenize } from '../retrieval/tokenizer';\nimport type { MemoryEntry, MemorySearchResult, MemoryStats } from '../types';\n\n/** updatedAt 降序比较器 */\nfunction byUpdatedAtDesc(a: MemoryEntry, b: MemoryEntry): number {\n return b.updatedAt - a.updatedAt;\n}\n\nexport class MemoryIndex {\n private entries = new Map<string, MemoryEntry>();\n private index = new BM25Index();\n /** 独立向量索引:保证向量召回不被 BM25 候选池封顶 */\n private vectors = new VectorIndex();\n private retriever = new BM25Retriever(this.index, this.entries);\n\n /** 共享底层索引与条目表,返回一个 BM25Retriever(与 store 实时同步) */\n getRetriever(): BM25Retriever {\n return this.retriever;\n }\n\n get(id: string): MemoryEntry | null {\n return this.entries.get(id) ?? null;\n }\n\n /** 全部条目(updatedAt 降序) */\n all(): MemoryEntry[] {\n return [...this.entries.values()].sort(byUpdatedAtDesc);\n }\n\n list(limit?: number): MemoryEntry[] {\n const all = this.all();\n return limit != null ? all.slice(0, limit) : all;\n }\n\n count(): number {\n return this.entries.size;\n }\n\n /** 索引内容 = content + concepts(提升概念命中) */\n add(entry: MemoryEntry): void {\n this.entries.set(entry.id, entry);\n const tokens = tokenize(`${entry.content} ${entry.concepts.join(' ')}`);\n this.index.add(entry.id, tokens);\n // 向量索引同步:有 embedding 则加入,否则移除(避免残留旧向量)\n if (entry.embedding && entry.embedding.length > 0) {\n this.vectors.add(entry.id, entry.embedding);\n } else {\n this.vectors.remove(entry.id);\n }\n }\n\n remove(id: string): boolean {\n if (!this.entries.has(id)) return false;\n this.index.remove(id);\n this.vectors.remove(id);\n this.entries.delete(id);\n return true;\n }\n\n clear(): void {\n this.entries.clear();\n this.index.clear();\n this.vectors.clear();\n }\n\n async search(query: string, limit?: number): Promise<MemorySearchResult[]> {\n return this.retriever.search(query, limit ?? 5);\n }\n\n async searchVectors(queryVec: number[], limit?: number): Promise<MemorySearchResult[]> {\n const hits = this.vectors.search(queryVec, limit ?? 5);\n const results: MemorySearchResult[] = [];\n for (const { id, score } of hits) {\n const entry = this.entries.get(id);\n if (entry) results.push({ entry, score, matchedBy: 'embedding' as const });\n }\n return results;\n }\n\n /** 统计快照 */\n stats(): MemoryStats {\n const bySource = { capture: 0, tool: 0, consolidation: 0 } as MemoryStats['bySource'];\n let avgConfidence = 0;\n let recallTotal = 0;\n let lastRecalledAt: number | undefined;\n for (const e of this.entries.values()) {\n if (bySource[e.source] == null) bySource[e.source] = 0;\n bySource[e.source] += 1;\n avgConfidence += e.confidence;\n recallTotal += e.recallCount;\n if (e.lastRecalledAt != null && (lastRecalledAt == null || e.lastRecalledAt > lastRecalledAt)) {\n lastRecalledAt = e.lastRecalledAt;\n }\n }\n const count = this.entries.size;\n return {\n count,\n embeddingCount: this.vectors.size,\n bySource,\n avgConfidence: count > 0 ? avgConfidence / count : 0,\n recallTotal,\n lastRecalledAt,\n };\n }\n}\n","/**\n * 零依赖 keyed mutex(promise 链实现)。\n *\n * 用于串行化同一 key 上的 read-modify-write 操作(如 FileMemoryStore 的同 id 更新),\n * 以及全局写锁(consolidate / prune 与 save 互斥)。\n * 任一批量 fn 抛错不会污染链,后续调用者不受影响。\n */\n\nexport class KeyedMutex {\n /** key -> 当前链尾 promise */\n private tails = new Map<string, Promise<void>>();\n\n /**\n * 在 key 上串行执行 fn:同一 key 的并发调用按调用顺序排队,逐个执行。\n * 不同 key 之间互不阻塞(`withLock('*', ...)` 可作全局锁)。\n */\n withLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const prev = this.tails.get(key) ?? Promise.resolve();\n let release!: () => void;\n const gate = new Promise<void>((resolve) => {\n release = resolve;\n });\n const tail = prev.then(() => gate);\n this.tails.set(key, tail);\n\n return prev.then(async () => {\n try {\n return await fn();\n } finally {\n release();\n // 若链尾仍是我们这条,清掉避免 Map 泄漏\n if (this.tails.get(key) === tail) {\n this.tails.delete(key);\n }\n }\n });\n }\n\n /** 当前持锁 key 数量(调试用) */\n get pending(): number {\n return this.tails.size;\n }\n}\n\n/** 全局共享实例(各 store 复用;互不冲突) */\nexport const globalMutex = new KeyedMutex();\n","/**\n * 内存记忆存储(测试与临时场景)。\n * 进程退出即丢失,无持久化。API 与 FileMemoryStore 完全一致。\n *\n * 并发安全:同 id 的写操作(save/delete/touchRecall)经 keyed mutex 串行,\n * 避免 embedder 异步计算期间的 read-modify-write 竞态;跨 id 互不阻塞。\n */\n\nimport { MemoryIndex } from './memory-index';\nimport { KeyedMutex } from '../utils/keyed-mutex';\nimport type {\n ConsolidateOptions,\n ConsolidatorLike,\n Embedder,\n MemoryEntry,\n MemoryEventSink,\n MemorySaveInput,\n MemorySearchResult,\n MemoryStats,\n MemoryStore,\n} from '../types';\n\n/** 生成短随机 id */\nfunction genId(): string {\n return `mem_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/** 填充默认字段(id / createdAt / updatedAt / recallCount / expiresAt) */\nexport function finalizeEntry(entry: MemorySaveInput, now: number = Date.now()): MemoryEntry {\n const createdAt = entry.createdAt ?? now;\n const { ttlMs, ...rest } = entry;\n return {\n ...rest,\n id: entry.id ?? genId(),\n createdAt,\n updatedAt: now,\n recallCount: entry.recallCount ?? 0,\n expiresAt: ttlMs != null ? createdAt + ttlMs : entry.expiresAt,\n };\n}\n\nexport interface InMemoryStoreOptions {\n /** 可选向量化器:配置后 save 时自动计算 embedding(供混合检索) */\n embedder?: Embedder;\n /** 事件接收器(失败/整理等关键节点) */\n onEvent?: MemoryEventSink;\n}\n\nexport class InMemoryStore implements MemoryStore {\n private idx = new MemoryIndex();\n private consolidator?: ConsolidatorLike;\n private embedder?: Embedder;\n private onEvent?: MemoryEventSink;\n private writeLock = new KeyedMutex();\n private lastConsolidatedAt?: number;\n\n constructor(options: InMemoryStoreOptions = {}) {\n this.embedder = options.embedder;\n this.onEvent = options.onEvent;\n }\n\n setConsolidator(consolidator: ConsolidatorLike): void {\n this.consolidator = consolidator;\n }\n\n async save(entry: MemorySaveInput): Promise<MemoryEntry> {\n return this.writeLock.withLock(entry.id ?? '*', async () => {\n // 若已存在同 id,保留 createdAt 与 embedding\n const prev = entry.id ? this.idx.get(entry.id) : null;\n const finalized = finalizeEntry(\n {\n ...entry,\n embedding: entry.embedding ?? prev?.embedding,\n // 显式 createdAt 优先(如合并器保留最早创建时间);未提供时沿用已有条目的\n createdAt: entry.createdAt ?? prev?.createdAt,\n },\n Date.now(),\n );\n // 配置了 embedder 且无既有向量时,计算 embedding\n if (this.embedder && !finalized.embedding) {\n try {\n finalized.embedding = await this.embedder.embed(finalized.content);\n } catch (err) {\n this.onEvent?.({ type: 'embedding:error', id: finalized.id, error: (err as Error).message });\n // embedding 失败不阻断保存(退化为纯 BM25 检索)\n }\n }\n this.idx.add(finalized);\n return finalized;\n });\n }\n\n async get(id: string): Promise<MemoryEntry | null> {\n return this.idx.get(id);\n }\n\n async delete(id: string): Promise<boolean> {\n return this.writeLock.withLock(id, async () => this.idx.remove(id));\n }\n\n async list(limit?: number): Promise<MemoryEntry[]> {\n return this.idx.list(limit);\n }\n\n async search(query: string, limit?: number): Promise<MemorySearchResult[]> {\n return this.idx.search(query, limit);\n }\n\n async searchVectors(queryVec: number[], limit?: number): Promise<MemorySearchResult[]> {\n return this.idx.searchVectors(queryVec, limit);\n }\n\n async touchRecall(id: string, at: number = Date.now()): Promise<void> {\n return this.writeLock.withLock(id, async () => {\n const e = this.idx.get(id);\n if (!e) return;\n // 仅更新检索统计;不更新 updatedAt(updatedAt 表示内容修改时间,\n // 驱动增量合并候选窗口与过期语义,不应被一次检索刷新)\n e.lastRecalledAt = at;\n e.recallCount += 1;\n this.idx.add(e);\n });\n }\n\n async consolidate(\n options?: ConsolidateOptions,\n ): Promise<{ merged: number; pruned: number }> {\n if (!this.consolidator) return { merged: 0, pruned: 0 };\n // 不持全局锁:consolidator 内部经 store.save/delete(逐 id 锁)原子化,\n // 跨 id 的交错为 best-effort(新写入条目未被本次合并处理,下一轮再合并)\n const result = await this.consolidator.run(options);\n this.lastConsolidatedAt = Date.now();\n return result;\n }\n\n async prune(options?: {\n maxAgeMs?: number;\n minConfidence?: number;\n }): Promise<number> {\n const now = Date.now();\n const maxAge = options?.maxAgeMs;\n const minConf = options?.minConfidence ?? 0;\n let removed = 0;\n for (const e of this.idx.all()) {\n const expiredByAge = maxAge != null && now - e.updatedAt > maxAge;\n const expiredByTtl = e.expiresAt != null && now > e.expiresAt;\n const lowConf = e.confidence < minConf;\n if (expiredByAge || expiredByTtl || lowConf) {\n if (await this.delete(e.id)) removed++;\n }\n }\n if (removed > 0) this.onEvent?.({ type: 'prune', removed });\n return removed;\n }\n\n async count(): Promise<number> {\n return this.idx.count();\n }\n\n markConsolidated(at: number = Date.now()): void {\n this.lastConsolidatedAt = at;\n }\n\n async stats(): Promise<MemoryStats> {\n const base = this.idx.stats();\n return { ...base, lastConsolidatedAt: this.lastConsolidatedAt };\n }\n\n dispose(): void {\n // 内存存储无可释放资源(占位,保持接口一致)\n }\n}\n\nexport function createInMemoryStore(): MemoryStore {\n return new InMemoryStore();\n}\n","/**\n * Embedder 接口与余弦相似度工具。\n *\n * 默认不提供任何 embedder 实现(零依赖、零 API Key)。\n * 用户可自行实现 Embedder 接入 ollama / @huggingface/transformers / OpenAI 等。\n */\n\nimport type { Embedder } from '../types';\n\nexport type { Embedder };\n\n/** 余弦相似度。任一为零向量返回 0。 */\nexport function cosine(a: number[], b: number[]): number {\n if (!a || !b || a.length === 0 || a.length !== b.length) return 0;\n let dot = 0;\n let na = 0;\n let nb = 0;\n for (let i = 0; i < a.length; i++) {\n dot += a[i] * b[i];\n na += a[i] * a[i];\n nb += b[i] * b[i];\n }\n if (na === 0 || nb === 0) return 0;\n return dot / (Math.sqrt(na) * Math.sqrt(nb));\n}\n\n/**\n * 将一组分数 min-max 归一化到 0..1。\n * 单元素或全相同时返回等分(避免除零)。\n */\nexport function minMaxNormalize(scores: number[]): number[] {\n if (scores.length === 0) return [];\n const min = Math.min(...scores);\n const max = Math.max(...scores);\n if (max - min === 0) return scores.map(() => 1);\n return scores.map((s) => (s - min) / (max - min));\n}\n","/**\n * 混合检索器:BM25(必选)+ 可选 Embedding(向量)双路独立召回后加权融合。\n *\n * 默认无 embedder 时退化为纯 BM25。\n * 有 embedder 时:\n * - BM25 路独立召回 top-`limit*3`\n * - 向量路独立召回 top-`limit*3`(依赖 store 的 searchVectors / VectorIndex,\n * 不受 BM25 候选池封顶 —— 修复「向量召回被 BM25 top-K 限制」问题)\n * - 两路各自 min-max 归一化后按权重融合:final = w_bm25 * bm25_norm + w_embed * cos_norm\n * - 过滤 < minScore,截 limit\n *\n * 兼容路径:\n * - 配置了 embedder 但 store 不支持向量检索(无 searchVectors 能力):\n * 退化为「BM25 候选 + 向量重排」(旧行为),并在 minScore 处诚实标注。\n */\n\nimport { cosine, minMaxNormalize } from './embedder';\nimport type { Embedder, MemorySearchResult } from '../types';\n\n/**\n * 检索器最小契约:任何具备 search(query, limit) 的对象均可作为 BM25 候选源。\n * BM25Retriever 天然满足此接口;插件层也可用 StoreBackedRetriever 包装自定义 store。\n */\nexport interface RetrieverLike {\n search(query: string, limit?: number): Promise<MemorySearchResult[]>;\n}\n\n/** 向量检索源契约(由 store 实现,或插件层包装) */\nexport interface VectorSearchLike {\n searchVectors(queryVec: number[], limit?: number): Promise<MemorySearchResult[]>;\n}\n\nexport interface HybridRetrieverOptions {\n /** BM25 候选源(BM25Retriever 或任何 RetrieverLike 实现) */\n bm25: RetrieverLike;\n /** 可选向量源(store.searchVectors);有 embedder 但无此源时退化为候选重排 */\n vector?: VectorSearchLike;\n embedder?: Embedder;\n /** BM25 权重,默认 0.5 */\n bm25Weight?: number;\n /** embedding 权重,默认 0.5 */\n embedWeight?: number;\n /** 注入阶段最低分数阈值,默认 0.1 */\n minScore?: number;\n}\n\nexport interface HybridSearchOptions {\n /** 覆盖默认 minScore(如注入转换器携带自己的阈值,不再篡改共享 retriever) */\n minScore?: number;\n /**\n * 原始分数模式:不做 min-max 归一化,保留 BM25 / cosine 原始分数。\n * 供合并器等需要「绝对相似度阈值」的场景使用 —— min-max 会把同一查询内\n * 的次优分数压到 0,使绝对阈值(如 0.85)失去意义。\n */\n raw?: boolean;\n}\n\nexport class HybridRetriever {\n bm25: RetrieverLike;\n vector?: VectorSearchLike;\n embedder?: Embedder;\n bm25Weight: number;\n embedWeight: number;\n minScore: number;\n\n constructor(options: HybridRetrieverOptions) {\n this.bm25 = options.bm25;\n this.vector = options.vector;\n this.embedder = options.embedder;\n this.bm25Weight = options.bm25Weight ?? 0.5;\n this.embedWeight = options.embedWeight ?? 0.5;\n this.minScore = options.minScore ?? 0.1;\n }\n\n /** 是否启用了向量检索 */\n get hasEmbedder(): boolean {\n return !!this.embedder;\n }\n\n async search(\n query: string,\n limit = 5,\n opts?: HybridSearchOptions,\n ): Promise<MemorySearchResult[]> {\n const minScore = opts?.minScore ?? this.minScore;\n const raw = opts?.raw ?? false;\n if (limit <= 0) return [];\n\n // BM25 路:有向量能力时放大候选池以提升融合质量\n const bm25Limit = this.embedder ? Math.max(limit * 3, limit) : limit;\n const bm25Results = await this.bm25.search(query, bm25Limit);\n\n // 纯 BM25 模式:分数 min-max 归一化后过滤(或 raw 模式保留原始分数)\n if (!this.embedder) {\n if (bm25Results.length === 0) return [];\n return raw\n ? this.fuseRaw(bm25Results, [], minScore, limit)\n : this.fuseSingle(bm25Results, minScore, limit);\n }\n\n // 尝试向量查询\n let queryVec: number[] | null = null;\n try {\n queryVec = await this.embedder.embed(query);\n } catch {\n queryVec = null; // 向量失败退化为纯 BM25\n }\n\n // 无向量源(自定义 store 不支持)或向量查询失败:BM25 候选 + 向量重排(旧行为)\n if (!this.vector || !queryVec || queryVec.length === 0) {\n if (bm25Results.length === 0) return [];\n if (raw) return this.fuseRaw(bm25Results, [], minScore, limit);\n return queryVec && this.vector\n ? this.rerank(bm25Results, queryVec, minScore, limit)\n : this.fuseSingle(bm25Results, minScore, limit);\n }\n\n // 双路独立召回 + 融合\n let vecResults: MemorySearchResult[] = [];\n try {\n vecResults = await this.vector.searchVectors(queryVec, bm25Limit);\n } catch {\n vecResults = [];\n }\n if (bm25Results.length === 0 && vecResults.length === 0) return [];\n return raw\n ? this.fuseRaw(bm25Results, vecResults, minScore, limit)\n : this.fuseDual(bm25Results, vecResults, minScore, limit);\n }\n\n // ─── 融合策略 ───────────────────────────────────────────────────\n\n /** 单路(纯 BM25):归一化 + 过滤 + 截断 */\n private fuseSingle(\n results: MemorySearchResult[],\n minScore: number,\n limit: number,\n ): MemorySearchResult[] {\n const normed = minMaxNormalize(results.map((r) => r.score));\n return results\n .map((r, i) => ({ entry: r.entry, score: normed[i], matchedBy: 'bm25' as const }))\n .filter((r) => r.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n }\n\n /** 候选重排(兼容路径):BM25 候选 + 向量 cosine 加权融合 */\n private rerank(\n bm25Results: MemorySearchResult[],\n queryVec: number[],\n minScore: number,\n limit: number,\n ): MemorySearchResult[] {\n const cosScores = bm25Results.map((r) => {\n const ev = r.entry.embedding;\n return ev && ev.length === queryVec.length ? cosine(queryVec, ev) : 0;\n });\n const bm25Normed = minMaxNormalize(bm25Results.map((r) => r.score));\n const cosNormed = minMaxNormalize(cosScores);\n const wSum = this.bm25Weight + this.embedWeight || 1;\n\n return bm25Results\n .map((r, i) => ({\n entry: r.entry,\n score: (this.bm25Weight * bm25Normed[i] + this.embedWeight * cosNormed[i]) / wSum,\n matchedBy: (cosNormed[i] >= bm25Normed[i] ? 'embedding' : 'hybrid') as\n | 'embedding'\n | 'hybrid',\n }))\n .filter((r) => r.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n }\n\n /**\n * 原始分数融合(raw 模式):保留检索源原始分数,不做 min-max 归一化,\n * 供合并器按「绝对相似度阈值」判定可合并项。双路命中时取 max\n * (任一来源判定相似即相似),避免归一化把次优分数抹平。\n *\n * 量纲说明:BM25 分数在 BM25Retriever 层已按 Σidf 归一化到 [0,1]\n * (完全同文≈1),cosine 本身在 [0,1],因此这里的绝对阈值(如 0.85)\n * 对两路统一成立。\n */\n private fuseRaw(\n bm25Results: MemorySearchResult[],\n vecResults: MemorySearchResult[],\n minScore: number,\n limit: number,\n ): MemorySearchResult[] {\n const union = new Map<\n string,\n { entry: MemorySearchResult['entry']; score: number; matchedBy: 'bm25' | 'embedding' | 'hybrid' }\n >();\n for (const r of bm25Results) {\n union.set(r.entry.id, { entry: r.entry, score: r.score, matchedBy: 'bm25' });\n }\n for (const r of vecResults) {\n const prev = union.get(r.entry.id);\n if (!prev) {\n union.set(r.entry.id, { entry: r.entry, score: r.score, matchedBy: 'embedding' });\n } else {\n prev.score = Math.max(prev.score, r.score);\n if (prev.matchedBy === 'bm25') prev.matchedBy = 'hybrid';\n }\n }\n return [...union.values()]\n .filter((x) => x.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n }\n\n /** 双路独立召回融合:按 id 取并集,加权求和,再按权重和归一化 */\n private fuseDual(\n bm25Results: MemorySearchResult[],\n vecResults: MemorySearchResult[],\n minScore: number,\n limit: number,\n ): MemorySearchResult[] {\n const bm25Normed = minMaxNormalize(bm25Results.map((r) => r.score));\n const vecNormed = minMaxNormalize(vecResults.map((r) => r.score));\n const wB = this.bm25Weight;\n const wE = this.embedWeight;\n const wSum = wB + wE || 1;\n\n const union = new Map<\n string,\n { entry: MemorySearchResult['entry']; score: number; matchedBy: 'bm25' | 'embedding' | 'hybrid' }\n >();\n bm25Results.forEach((r, i) => {\n union.set(r.entry.id, { entry: r.entry, score: bm25Normed[i] * wB, matchedBy: 'bm25' });\n });\n vecResults.forEach((r, i) => {\n const prev = union.get(r.entry.id);\n const s = vecNormed[i] * wE;\n if (!prev) {\n union.set(r.entry.id, { entry: r.entry, score: s, matchedBy: 'embedding' });\n } else {\n prev.score += s;\n if (prev.matchedBy === 'bm25') prev.matchedBy = 'hybrid';\n }\n });\n\n return [...union.values()]\n .map((x) => ({ entry: x.entry, score: x.score / wSum, matchedBy: x.matchedBy }))\n .filter((r) => r.score >= minScore)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit);\n }\n}\n","/**\n * packages/core/types.ts - 核心类型定义\n *\n * 这是整个框架的类型基础,不依赖任何外部实现。\n * 所有消息、内容块、模型、工具等类型都在此定义。\n */\n\n// ─── 内容块类型 ───────────────────────────────────────────────────\n\nexport interface TextContent {\n type: 'text';\n text: string;\n}\n\nexport interface ImageContent {\n type: 'image';\n data: string; // base64 或 URL\n mimeType: string;\n}\n\nexport interface ToolCallContent {\n type: 'toolCall';\n id: string;\n name: string;\n arguments: unknown;\n}\n\nexport interface ThinkingContent {\n type: 'thinking';\n text: string;\n}\n\nexport type ContentBlock =\n | TextContent\n | ImageContent\n | ToolCallContent\n | ThinkingContent;\n\n// ─── 消息类型 ─────────────────────────────────────────────────────\n\nexport interface BaseMessage {\n role: string;\n content: string | ContentBlock[];\n timestamp: number;\n [key: string]: unknown;\n}\n\nexport interface UserMessage extends BaseMessage {\n role: 'user';\n}\n\nexport interface AssistantMessage extends BaseMessage {\n role: 'assistant';\n model?: string;\n provider?: string;\n stopReason?: string;\n usage?: Usage;\n errorMessage?: string;\n responseId?: string;\n}\n\nexport interface ToolResultMessage extends BaseMessage {\n role: 'toolResult';\n toolCallId: string;\n toolName: string;\n isError: boolean;\n}\n\nexport interface SystemMessage extends BaseMessage {\n role: 'system';\n}\n\nexport type Message = UserMessage | AssistantMessage | ToolResultMessage | SystemMessage;\n\n// ─── Token 用量 ───────────────────────────────────────────────────\n\n/**\n * 用量与费用。与 aipack/ai 的 Usage 结构对齐:\n * cacheRead/cacheWrite/reasoning 为可选字段,旧消费者无需感知。\n */\nexport interface Usage {\n input: number;\n output: number;\n total: number;\n /** 命中缓存读取的 token 数(不计入 input) */\n cacheRead?: number;\n /** 写入缓存的 token 数 */\n cacheWrite?: number;\n /** 推理 token 数(completion 的子集) */\n reasoning?: number;\n /** 还原后的原始 token 总数(input + output + cacheRead + cacheWrite) */\n totalTokens?: number;\n}\n\n// ─── 模型定义 ─────────────────────────────────────────────────────\n\nexport interface Model {\n id: string;\n name: string;\n provider: string;\n contextWindow: number;\n maxTokens: number;\n reasoning: boolean;\n [key: string]: unknown;\n}\n\n// ─── 工具定义 ─────────────────────────────────────────────────────\n\nexport interface ToolResult {\n content: ContentBlock[];\n details: unknown;\n usage?: Usage;\n}\n\nexport interface Tool {\n name: string;\n description: string;\n parameters: unknown; // JSON Schema 或 TypeBox schema\n /**\n * 所需权限能力声明(如 'shell:exec' / 'fs:write' / 'memory:write' / 'network:fetch')。\n * 供框架级 PermissionPolicy 裁决使用;未声明视为安全工具。\n */\n permissions?: string[];\n execute: (\n toolCallId: string,\n args: unknown,\n signal?: AbortSignal,\n ) => Promise<ToolResult>;\n prepareArguments?: (args: unknown) => unknown;\n}\n\n// ─── 上下文 ───────────────────────────────────────────────────────\n\nexport interface Context {\n systemPrompt: string;\n messages: Message[];\n tools?: Tool[];\n}\n\n// ─── 流式事件 ─────────────────────────────────────────────────────\n\nexport interface StreamOptions {\n signal?: AbortSignal;\n reasoning?: string;\n sessionId?: string;\n /** 重试回调:provider 内 retry() 真正退避重试时调用(可观测/埋点用) */\n onRetryAttempt?: (info: { attempt: number; error: unknown; delayMs: number }) => void;\n}\n\nexport type StreamEvent =\n | { type: 'start'; partial: { content: ContentBlock[] } }\n | { type: 'text_delta'; delta: string }\n | { type: 'thinking_delta'; delta: string }\n | { type: 'tool_call_start'; id: string; name: string }\n | { type: 'tool_call_delta'; id: string; delta: string }\n | { type: 'tool_call_end'; id: string }\n | { type: 'done'; message: AssistantMessage }\n | { type: 'error'; message: AssistantMessage };\n\nexport type StreamResult = AsyncIterable<StreamEvent>;\n\n/**\n * 流式函数类型 - 模型提供者需实现此接口\n *\n * 是一个可替换的核心能力。\n * 用户通过提供不同的 streamFn 来接入不同的 LLM 提供商。\n */\nexport type StreamFn = (\n model: Model,\n context: Context,\n options?: StreamOptions,\n) => StreamResult;\n\n// ─── Agent 状态 ───────────────────────────────────────────────────\n\nexport type ThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'max';\n\nexport interface AgentState {\n systemPrompt: string;\n model: Model;\n thinkingLevel: ThinkingLevel;\n tools: Tool[];\n messages: Message[];\n isStreaming: boolean;\n streamingMessage?: Message;\n pendingToolCalls: Set<string>;\n errorMessage?: string;\n}\n\n// ─── 工具函数 ─────────────────────────────────────────────────────\n\nexport function extractText(content: string | ContentBlock[]): string {\n if (typeof content === 'string') return content;\n return content\n .filter((c): c is TextContent => c.type === 'text')\n .map(c => c.text)\n .join('');\n}\n\nexport function extractToolCalls(content: string | ContentBlock[]): ToolCallContent[] {\n if (typeof content === 'string') return [];\n return content.filter((c): c is ToolCallContent => c.type === 'toolCall');\n}\n\nexport function createTextContent(text: string): TextContent {\n return { type: 'text', text };\n}\n\nexport function createEmptyUsage(): Usage {\n return { input: 0, output: 0, total: 0 };\n}\n","/**\n * ContextResource - 上下文资源\n *\n * 代表上下文中的单个资源单元(消息、工具调用、工具结果、状态快照等)。\n * 每个 Resource 拥有唯一标识,并可声明对其他 Resource 的依赖。\n */\n\n// ─── 资源类型 ─────────────────────────────────────────────────────\n\nexport type ResourceType =\n | 'user_message'\n | 'assistant_message'\n | 'tool_call'\n | 'tool_result'\n | 'system_message'\n | 'state_snapshot'\n | 'compaction_summary'\n | 'custom';\n\nexport type ResourceRole = 'user' | 'assistant' | 'toolResult' | 'system' | string;\n\n// ─── 资源接口 ─────────────────────────────────────────────────────\n\nexport interface ContextResource {\n /** 唯一标识 */\n readonly id: string;\n /** 资源类型 */\n readonly type: ResourceType;\n /** 消息角色 */\n readonly role: ResourceRole;\n /** 资源内容(消息体或工具数据) */\n readonly content: unknown;\n /** 时间戳 */\n readonly timestamp: number;\n /** 依赖的其他资源 ID(如 tool_result 依赖 tool_call) */\n readonly dependencies: string[];\n /** 资源元数据 */\n readonly meta: Record<string, unknown>;\n /** 是否为关键资源(压缩时不可移除) */\n readonly pinned: boolean;\n}\n\n// ─── 资源构建器 ───────────────────────────────────────────────────\n\nexport class ContextResourceBuilder {\n private _id: string;\n private _type: ResourceType = 'custom';\n private _role: ResourceRole = 'user';\n private _content: unknown = '';\n private _timestamp: number = Date.now();\n private _dependencies: string[] = [];\n private _meta: Record<string, unknown> = {};\n private _pinned: boolean = false;\n\n constructor(id?: string) {\n this._id = id || `res_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n }\n\n id(id: string): this {\n this._id = id;\n return this;\n }\n\n type(t: ResourceType): this {\n this._type = t;\n return this;\n }\n\n role(r: ResourceRole): this {\n this._role = r;\n return this;\n }\n\n content(c: unknown): this {\n this._content = c;\n return this;\n }\n\n timestamp(ts: number): this {\n this._timestamp = ts;\n return this;\n }\n\n dependsOn(...ids: string[]): this {\n this._dependencies.push(...ids);\n return this;\n }\n\n meta(key: string, value: unknown): this {\n this._meta[key] = value;\n return this;\n }\n\n pinned(p: boolean = true): this {\n this._pinned = p;\n return this;\n }\n\n build(): ContextResource {\n return {\n id: this._id,\n type: this._type,\n role: this._role,\n content: this._content,\n timestamp: this._timestamp,\n dependencies: this._dependencies,\n meta: this._meta,\n pinned: this._pinned,\n };\n }\n}\n\n// ─── 工厂函数 ─────────────────────────────────────────────────────\n\nexport function createMessageResource(\n role: ResourceRole,\n content: unknown,\n options?: Partial<ContextResource>,\n): ContextResource {\n const type: ResourceType =\n role === 'user' ? 'user_message'\n : role === 'assistant' ? 'assistant_message'\n : role === 'system' ? 'system_message'\n : 'custom';\n\n return new ContextResourceBuilder()\n .type(type)\n .role(role)\n .content(content)\n .timestamp(options?.timestamp ?? Date.now())\n .build();\n}\n\nexport function createToolCallResource(\n toolCallId: string,\n toolName: string,\n args: unknown,\n options?: Partial<ContextResource>,\n): ContextResource {\n return new ContextResourceBuilder()\n .id(toolCallId)\n .type('tool_call')\n .role('assistant')\n .content({ toolName, args })\n .meta('toolName', toolName)\n .meta('toolCallId', toolCallId)\n .timestamp(options?.timestamp ?? Date.now())\n .build();\n}\n\nexport function createToolResultResource(\n toolCallId: string,\n toolName: string,\n result: unknown,\n isError: boolean,\n dependsOn: string,\n options?: Partial<ContextResource>,\n): ContextResource {\n return new ContextResourceBuilder()\n .id(`${toolCallId}_result`)\n .type('tool_result')\n .role('toolResult')\n .content(result)\n .dependsOn(dependsOn)\n .meta('toolName', toolName)\n .meta('toolCallId', toolCallId)\n .meta('isError', isError)\n .timestamp(options?.timestamp ?? Date.now())\n .build();\n}\n","/**\n * Transformer - 上下文转换器\n *\n * Transformer 负责对 ContextResource 集合进行转换,\n * 例如压缩、清理、配对修复、状态快照注入等。\n * Runtime 按数组顺序链式执行转换器:上一个转换器的输出作为下一个的输入。\n */\n\nimport type { ContextResource } from './context-resource';\nimport type { TaskGraph } from './task-graph';\n\n// ─── 转换上下文 ───────────────────────────────────────────────────\n\nexport interface TransformContext {\n /** 当前任务图 */\n graph: TaskGraph;\n /** 运行时提供的额外上下文 */\n runtime: TransformRuntime;\n /** 中止信号 */\n signal?: AbortSignal;\n}\n\nexport interface TransformRuntime {\n /** 会话标识 */\n sessionKey: string;\n /** 当前回合数 */\n turn: number;\n /** 工作区路径 */\n workspace?: string;\n /** 运行时配置 */\n config?: Record<string, unknown>;\n /** 当前模型的上下文窗口(token),供 token 级截断转换器使用 */\n contextWindow?: number;\n /** 当前模型最大输出 token */\n maxTokens?: number;\n /** token 预算占 contextWindow 的比例(默认 0.8) */\n contextBudgetRatio?: number;\n}\n\n// ─── 转换器接口 ───────────────────────────────────────────────────\n\nexport interface ContextTransformer {\n /** 转换器名称 */\n readonly name: string;\n /**\n * 执行转换\n * @param resources 输入资源列表\n * @param context 转换上下文\n * @returns 转换后的资源列表\n */\n transform(\n resources: ContextResource[],\n context: TransformContext,\n ): Promise<ContextResource[]>;\n}\n\n// ─── 转换器选项 ───────────────────────────────────────────────────\n\nexport interface TransformerOptions {\n /** 额外配置 */\n config?: Record<string, unknown>;\n}\n\n// ─── 抽象基类 ─────────────────────────────────────────────────────\n\nexport abstract class BaseTransformer implements ContextTransformer {\n abstract readonly name: string;\n\n protected config: Record<string, unknown>;\n\n constructor(options: TransformerOptions = {}) {\n this.config = options.config ?? {};\n }\n\n async transform(\n resources: ContextResource[],\n context: TransformContext,\n ): Promise<ContextResource[]> {\n return this.run(resources, context);\n }\n\n protected abstract run(\n resources: ContextResource[],\n context: TransformContext,\n ): Promise<ContextResource[]>;\n}\n","/**\n * Extension - 扩展插件系统\n *\n * Extension 通过 Tapable 钩子在 Runtime 生命周期的关键节点注入逻辑,\n * 无需修改 Runtime 核心,实现开闭原则。\n */\n\nimport { AsyncSeriesHook, AsyncSeriesWaterfallHook } from './tapable';\nimport type { Request } from './request';\nimport type { Result } from './result';\nimport type { Context } from './types';\nimport type { ContextResource } from './context-resource';\nimport type { BeforeToolCallDecision, AfterToolCallDecision } from './tool-hooks';\nimport type { Runtime } from './runtime';\n\n// ─── Runtime 钩子集合 ─────────────────────────────────────────────\n\nexport interface RuntimeHooks {\n /** Runtime 初始化前 */\n beforeInitialize: AsyncSeriesHook<[Request]>;\n /** Runtime 初始化后 */\n afterInitialize: AsyncSeriesHook<[Request]>;\n /** 请求处理前(可修改请求) */\n beforeRun: AsyncSeriesWaterfallHook<Request>;\n /** 资源构建后、转换前(可修改资源) */\n beforeTransform: AsyncSeriesWaterfallHook<ContextResource[]>;\n /**\n * 资源转换后、模型调用前(可修改资源)\n */\n afterTransform: AsyncSeriesWaterfallHook<ContextResource[]>;\n /**\n * 模型调用前(waterfall,可修改最终发给模型的 Context:\n * systemPrompt / messages / tools)。\n * 典型用途:插件注入 system prompt 片段或动态附加工具(如 skills 目录注入)。\n * 每次模型调用触发一次;systemPrompt 以 Runtime 持有的基准值为起点,改动不累积。\n */\n beforeModelCall: AsyncSeriesWaterfallHook<Context>;\n /** 模型调用后、结果构建前 */\n beforeEmit: AsyncSeriesHook<[Result]>;\n /** 结果构建后 */\n afterEmit: AsyncSeriesHook<[Result]>;\n /**\n * 运行结束。\n * 第二参数为最终 Request(可选,向后兼容)。\n * 单会话模式下会话标识由 Runtime 持有;多会话场景请创建独立 Runtime 实例。\n */\n done: AsyncSeriesHook<[Result, Request?]>;\n /** 运行失败 */\n failed: AsyncSeriesHook<[Error, Request]>;\n /**\n * 工具执行前(参数校验后)。流转 BeforeToolCallDecision:\n * 任一 tap 设 block 则该工具不执行(生成拒绝结果);\n * 设 terminate 则终止整个 run(本轮工具结束后停止 runLoop);\n * 可改写 args。第二参数为 ToolCallContext。\n */\n beforeToolCall: AsyncSeriesWaterfallHook<BeforeToolCallDecision>;\n /**\n * 工具执行后、最终事件发出前。流转 AfterToolCallDecision:\n * 可改写 result(waterfall),设 terminate 终止整个 run。\n * 第二参数为 ToolCallContext。\n */\n afterToolCall: AsyncSeriesWaterfallHook<AfterToolCallDecision>;\n}\n\n// ─── Extension 接口 ───────────────────────────────────────────────\n\nexport interface Extension {\n /** 扩展名称 */\n readonly name: string;\n /**\n * 应用扩展到 Runtime\n * 通过 hooks.tap 注册回调\n */\n apply(hooks: RuntimeHooks, context: ExtensionContext): void;\n}\n\n// ─── 扩展上下文 ───────────────────────────────────────────────────\n\nexport interface ExtensionContext {\n /** Runtime 配置 */\n readonly config: Record<string, unknown>;\n /** 工作区路径 */\n readonly workspace: string;\n /** 会话标识 */\n readonly sessionKey: string;\n /** 共享状态(Extension 间通信) */\n readonly shared: Map<string, unknown>;\n /**\n * 所属 Runtime 引用(可选)。\n * 供插件在 apply 阶段注册工具等(如 SkillsExtension 注册内置 skill 工具)。\n * 由 AgentRuntime.create 注入;自建 ExtensionContext 时可缺省。\n */\n readonly runtime?: Runtime;\n}\n\n// ─── Extension 管理器 ─────────────────────────────────────────────\n\nexport class ExtensionManager {\n private extensions: Extension[] = [];\n private hooks: RuntimeHooks;\n\n constructor() {\n this.hooks = this.createHooks();\n }\n\n private createHooks(): RuntimeHooks {\n return {\n beforeInitialize: new AsyncSeriesHook('beforeInitialize'),\n afterInitialize: new AsyncSeriesHook('afterInitialize'),\n beforeRun: new AsyncSeriesWaterfallHook('beforeRun'),\n beforeTransform: new AsyncSeriesWaterfallHook('beforeTransform'),\n afterTransform: new AsyncSeriesWaterfallHook('afterTransform'),\n beforeModelCall: new AsyncSeriesWaterfallHook('beforeModelCall'),\n beforeEmit: new AsyncSeriesHook('beforeEmit'),\n afterEmit: new AsyncSeriesHook('afterEmit'),\n done: new AsyncSeriesHook('done'),\n failed: new AsyncSeriesHook('failed'),\n beforeToolCall: new AsyncSeriesWaterfallHook('beforeToolCall'),\n afterToolCall: new AsyncSeriesWaterfallHook('afterToolCall'),\n };\n }\n\n /** 注册扩展 */\n register(extension: Extension): this {\n this.extensions.push(extension);\n return this;\n }\n\n /** 批量注册扩展 */\n registerAll(extensions: Extension[]): this {\n for (const ext of extensions) {\n this.register(ext);\n }\n return this;\n }\n\n /** 应用所有扩展到钩子 */\n applyAll(context: ExtensionContext): void {\n for (const ext of this.extensions) {\n try {\n ext.apply(this.hooks, context);\n } catch (err) {\n // 单个 Extension 失败不影响其他 Extension,但需可观测\n console.warn(`[Extension] \"${ext.name}\" apply 失败:`, (err as Error)?.message ?? err);\n }\n }\n }\n\n /** 获取钩子集合 */\n getHooks(): RuntimeHooks {\n return this.hooks;\n }\n\n /** 移除扩展 */\n unregister(name: string): boolean {\n const idx = this.extensions.findIndex(e => e.name === name);\n if (idx === -1) return false;\n this.extensions.splice(idx, 1);\n return true;\n }\n\n /** 获取所有已注册的扩展 */\n getExtensions(): Extension[] {\n return [...this.extensions];\n }\n\n /** 清空所有扩展 */\n clear(): void {\n this.extensions = [];\n }\n}\n\n// ─── 抽象基类 ─────────────────────────────────────────────────────\n\nexport abstract class BaseExtension implements Extension {\n abstract readonly name: string;\n\n apply(hooks: RuntimeHooks, context: ExtensionContext): void {\n this.setup(hooks, context);\n }\n\n protected abstract setup(hooks: RuntimeHooks, context: ExtensionContext): void;\n}\n","/**\n * 捕获抽取器:把一轮对话(用户消息 + 助手回答 + 工具)压成一条记忆。\n *\n * - 零-LLM 模式(默认):抽取关键词概念,content 截断为 `Q: ...\\nA: ...`。\n * - LLM 模式(可选 summarizeFn):调用外部摘要函数,失败回退到零-LLM。\n */\n\nimport { extractConcepts } from '../retrieval/tokenizer';\nimport type { SummarizeFn } from '../types';\n\nexport interface ExtractResult {\n content: string;\n concepts: string[];\n /** 是否经过 LLM 摘要 */\n summarized: boolean;\n}\n\nexport interface ExtractorOptions {\n /** 概念数上限,默认 8 */\n maxConcepts?: number;\n /** content 最大字符数,默认 2000 */\n maxChars?: number;\n}\n\n/** 截断文本到 maxChars(保留尾部省略号) */\nfunction truncate(text: string, max: number): string {\n if (text.length <= max) return text;\n return `${text.slice(0, max - 1)}…`;\n}\n\n/** 取首句(按中英文句末标点切分),并截断到 max */\nfunction firstSentence(text: string, max: number): string {\n const trimmed = text.trim();\n if (!trimmed) return '';\n const match = trimmed.match(/^[^。!?!?.…\\n]+[。!?!?.…]?/);\n const first = match ? match[0].trim() : trimmed;\n return truncate(first, max);\n}\n\n/**\n * 零-LLM 抽取(要点压缩):content = 用户首句 + 助手首句 + 工具(截断)。\n *\n * 相比直接转储整轮对话:\n * - 只保留「提问主体 + 回答主旨」,避免整段原文进索引(防止索引膨胀、\n * 问题文本干扰检索命中);\n * - concepts = 关键词 top-N(供 BM25 与展示)。\n */\nexport function extractFromTurn(\n userMessage: string,\n assistantContent: string,\n toolsUsed: string[],\n options: ExtractorOptions = {},\n): ExtractResult {\n const maxChars = options.maxChars ?? 2000;\n const maxConcepts = options.maxConcepts ?? 8;\n\n const parts: string[] = [];\n if (userMessage) parts.push(`Q: ${firstSentence(userMessage, 160)}`);\n if (assistantContent) parts.push(`A: ${firstSentence(assistantContent, 320)}`);\n if (toolsUsed.length > 0) parts.push(`tools: ${toolsUsed.slice(0, 5).join(', ')}`);\n const content = truncate(parts.join('\\n'), maxChars);\n\n const conceptSource = `${userMessage} ${assistantContent} ${toolsUsed.join(' ')}`;\n const concepts = extractConcepts(conceptSource, maxConcepts);\n\n return { content, concepts, summarized: false };\n}\n\n/**\n * 运行抽取:若提供 summarizeFn 则先尝试 LLM 摘要,失败/返回 null 回退到零-LLM。\n */\nexport async function runCaptureExtractor(\n input: {\n userMessage: string;\n assistantContent: string;\n toolsUsed: string[];\n summarizeFn?: SummarizeFn;\n },\n options: ExtractorOptions = {},\n): Promise<ExtractResult> {\n const { userMessage, assistantContent, toolsUsed, summarizeFn } = input;\n\n if (summarizeFn) {\n try {\n const result = await summarizeFn({\n userMessage,\n assistantContent,\n toolsUsed,\n });\n if (result && result.summary && result.summary.trim()) {\n const maxChars = options.maxChars ?? 2000;\n const concepts =\n result.concepts && result.concepts.length > 0\n ? result.concepts\n : extractConcepts(\n `${userMessage} ${result.summary}`,\n options.maxConcepts ?? 8,\n );\n return {\n content: truncate(result.summary.trim(), maxChars),\n concepts,\n summarized: true,\n };\n }\n } catch {\n // 摘要失败,回退零-LLM\n }\n }\n\n return extractFromTurn(userMessage, assistantContent, toolsUsed, options);\n}\n","/**\n * 记忆捕获扩展。\n *\n * 在 Runtime 生命周期中「静默」捕获每轮对话要点并存为可检索记忆。\n * 参考 agentmemory 的 capture 阶段。\n *\n * 机制:\n * - beforeRun:暂存本轮用户消息(单会话,Runtime 持有唯一 sessionKey)。\n * - done:与本轮结果配对捕获。sessionKey 来自 ExtensionContext(Runtime 级),\n * 与结果配对写入记忆库。框架保证同一 Runtime 的 run 串行执行,无并发错配。\n * - failed:本轮失败不捕获(仅成功回合入库),无残留状态需清理。\n *\n * 每 consolidateEvery 次捕获触发一次合并。\n */\n\nimport { BaseExtension } from '@aipack-ai/agent';\nimport type { RuntimeHooks, ExtensionContext, Request, Result } from '@aipack-ai/agent';\nimport type { MemoryEventSink, MemoryStore } from '../types';\nimport type { SummarizeFn } from '../types';\nimport { runCaptureExtractor } from './extractor';\n\nexport interface CaptureOptions {\n enabled?: boolean;\n /** 可选 LLM 摘要函数(默认关闭,零 token) */\n summarizeFn?: SummarizeFn;\n /** 最小用户消息长度(小于则跳过捕获),默认 12 */\n minLength?: number;\n /** 概念数上限,默认 8 */\n maxConcepts?: number;\n /** content 最大字符数,默认 2000 */\n maxContentChars?: number;\n /** 每 N 次捕获触发一次 consolidate(0=不自动,默认 0) */\n consolidateEvery?: number;\n /** 捕获记忆 TTL(ms),过期后 prune 时清理 */\n ttlMs?: number;\n /** 事件接收器(捕获失败等) */\n onEvent?: MemoryEventSink;\n}\n\nexport class MemoryCaptureExtension extends BaseExtension {\n readonly name = 'memory-capture';\n\n private store: MemoryStore;\n private summarizeFn?: SummarizeFn;\n private minLength: number;\n private maxConcepts: number;\n private maxContentChars: number;\n private consolidateEvery: number;\n private ttlMs?: number;\n private onEvent?: MemoryEventSink;\n\n /** 本轮待捕获的用户消息(sessionKey -> 请求信息) */\n private pending = new Map<string, { message: string }>();\n\n /** 捕获计数(用于触发 consolidate) */\n private captureCount = 0;\n\n constructor(store: MemoryStore, options: CaptureOptions = {}) {\n super();\n this.store = store;\n this.summarizeFn = options.summarizeFn;\n this.minLength = options.minLength ?? 12;\n this.maxConcepts = options.maxConcepts ?? 8;\n this.maxContentChars = options.maxContentChars ?? 2000;\n this.consolidateEvery = options.consolidateEvery ?? 0;\n this.ttlMs = options.ttlMs;\n this.onEvent = options.onEvent;\n }\n\n protected setup(hooks: RuntimeHooks, context: ExtensionContext): void {\n const sessionKey = context.sessionKey;\n // beforeRun:暂存用户消息(不改请求)\n hooks.beforeRun.tapPromise('memory-capture', async (request: Request) => {\n try {\n this.pending.set(sessionKey, { message: request.message });\n } catch {\n // 忽略 stash 失败\n }\n return request;\n });\n\n // done:与本轮结果配对捕获(单会话模式,sessionKey 由 Runtime 持有)\n hooks.done.tapPromise('memory-capture', async (result: Result, _request?: Request) => {\n await this.captureFromResult(result, sessionKey);\n });\n }\n\n private async captureFromResult(result: Result, sessionKey?: string): Promise<void> {\n if (!sessionKey) return;\n\n // 先取后删(避免 delete 后 get 返回 undefined)\n const stashed = this.pending.get(sessionKey);\n this.pending.delete(sessionKey);\n if (!stashed) return;\n\n // 仅捕获成功回合\n if (!result.success) return;\n const userMessage = stashed.message;\n const assistantContent = result.content ?? '';\n if (!userMessage || userMessage.trim().length < this.minLength) return;\n if (!assistantContent || !assistantContent.trim()) return;\n\n try {\n const extracted = await runCaptureExtractor(\n {\n userMessage,\n assistantContent,\n toolsUsed: result.toolsUsed ?? [],\n summarizeFn: this.summarizeFn,\n },\n { maxConcepts: this.maxConcepts, maxChars: this.maxContentChars },\n );\n\n await this.store.save({\n content: extracted.content,\n concepts: extracted.concepts,\n confidence: extracted.summarized ? 0.8 : 0.6,\n source: 'capture',\n sessionKey,\n ttlMs: this.ttlMs,\n });\n\n this.captureCount += 1;\n\n // 周期性合并\n if (this.consolidateEvery > 0 && this.captureCount % this.consolidateEvery === 0) {\n try {\n await this.store.consolidate();\n } catch (err) {\n this.onEvent?.({\n type: 'consolidate:failed',\n error: (err as Error).message,\n });\n // 合并失败不影响捕获\n }\n }\n } catch (err) {\n // 捕获失败不影响运行结果\n this.onEvent?.({ type: 'capture:failed', sessionKey, error: (err as Error).message });\n }\n }\n}\n","/**\n * 注入哨兵(sentinel):在 user 消息内容中包裹记忆块。\n *\n * 为何用 sentinel 而非 resource.meta:\n * aipack 的 messageToResource/resourceToMessage 对 user 消息不保留 meta\n * (context-resource/index.ts:30-38, 112-119)。sentinel 是 content 的一部分,\n * 随消息持久化,下一轮 messagesToResources 重建后仍可在 content 文本中识别并剥离。\n */\n\nimport type { MemorySearchResult } from '../types';\n\nexport const MEMORY_BLOCK_START = '<<<AIPACK_MEMORY>>>';\nexport const MEMORY_BLOCK_END = '<<</AIPACK_MEMORY>>>';\n\n/** 剥离 sentinel 包裹块(含其后的多余空行),返回干净原文 */\nexport function stripMemoryBlock(text: string): string {\n if (!text) return text;\n // 匹配 START ... END(非贪婪)及紧随其后的空行\n const re = new RegExp(\n `${escapeRegex(MEMORY_BLOCK_START)}[\\\\s\\\\S]*?${escapeRegex(MEMORY_BLOCK_END)}\\\\s*`,\n 'g',\n );\n return text.replace(re, '').trim();\n}\n\n/** 判断文本是否包含 sentinel 块 */\nexport function hasMemoryBlock(text: string): boolean {\n if (!text) return false;\n return text.includes(MEMORY_BLOCK_START);\n}\n\n/** 将若干行用 sentinel 包裹(含头尾换行,便于前插进 content) */\nexport function wrapMemoryBlock(lines: string[]): string {\n const body = lines.join('\\n').trimEnd();\n return `${MEMORY_BLOCK_START}\\n${body}\\n${MEMORY_BLOCK_END}`;\n}\n\n/** 由检索结果构造可读的记忆块文本 */\nexport function buildMemoryBlock(results: MemorySearchResult[]): string {\n if (results.length === 0) return '';\n const lines = ['[Relevant memories]'];\n for (const r of results) {\n const score = r.score.toFixed(2);\n const content = r.entry.content.replace(/\\s+/g, ' ').trim().slice(0, 500);\n lines.push(`- ${content} (score=${score}, id=${r.entry.id})`);\n }\n return wrapMemoryBlock(lines);\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n","/**\n * 记忆注入转换器。\n *\n * 需放在 transformers 数组最前(先剥后注),每轮:\n * 1. 剥除所有 user_message 资源内容中已存在的 sentinel 记忆块(清上轮注入,\n * 含已持久化进 session 的)。\n * 2. 取最新 user_message 的纯文本作为检索 query。\n * 3. HybridRetriever 取 top-K,过滤 minScore。\n * 4. 非空则把记忆块前插进最新 user 消息内容(string / ContentBlock[] 两分支)。\n *\n * 为何合并进最新 user 消息而非新增独立资源或 system 资源:\n * - aipack buildContext 会过滤 role==='system' 消息(runtime/index.ts:585),\n * 故 system 注入不会到达模型;\n * - 新增独立 user 资源会产生「连续两条 user 消息」,部分 provider 解析异常;\n * - 合并进最新 user 消息语义自然,且 content 中的 sentinel 可跨轮识别剥离。\n */\n\nimport { BaseTransformer } from '@aipack-ai/agent';\nimport type { ContextResource, ContentBlock, TextContent } from '@aipack-ai/agent';\nimport { ContextResourceBuilder } from '@aipack-ai/agent';\nimport type { HybridRetriever } from '../retrieval/hybrid-retriever';\nimport {\n buildMemoryBlock,\n hasMemoryBlock,\n stripMemoryBlock,\n} from './sentinels';\n\nexport interface InjectionOptions {\n /** 注入 top-K,默认 5 */\n maxMemories?: number;\n /** 最低分数阈值,默认 0.1 */\n minScore?: number;\n /** 可选:对检索 query 做变换(如抽取提问主体) */\n queryTransform?: (latestUserText: string) => string;\n /** 可选:命中记忆后的回调(插件层装配为 store.touchRecall,更新检索统计) */\n onRecall?: (ids: string[]) => void | Promise<void>;\n}\n\nexport class MemoryInjectionTransformer extends BaseTransformer {\n readonly name = 'memory-injection';\n\n private retriever: HybridRetriever;\n private maxMemories: number;\n private minScore: number;\n private queryTransform?: (text: string) => string;\n private onRecall?: (ids: string[]) => void | Promise<void>;\n\n constructor(retriever: HybridRetriever, options: InjectionOptions = {}) {\n super();\n this.retriever = retriever;\n this.maxMemories = options.maxMemories ?? 5;\n this.minScore = options.minScore ?? 0.1;\n this.queryTransform = options.queryTransform;\n this.onRecall = options.onRecall;\n // 注意:不再写回共享 retriever.minScore(构造函数不应篡改他人持有的对象),\n // 阈值在每次检索时以参数覆盖传入。\n }\n\n protected async run(\n resources: ContextResource[],\n _context: import('@aipack-ai/agent').TransformContext,\n ): Promise<ContextResource[]> {\n if (resources.length === 0) return resources;\n\n // 1. 剥除所有 user_message 资源中的旧 sentinel 块(不可变:重建新资源)\n let cleaned = resources.map((r) => this.stripResource(r));\n\n // 2. 找最新 user_message(数组中最后一个 user_message)\n let latestIdx = -1;\n for (let i = cleaned.length - 1; i >= 0; i--) {\n if (cleaned[i].type === 'user_message') {\n latestIdx = i;\n break;\n }\n }\n if (latestIdx === -1) return cleaned;\n\n const latest = cleaned[latestIdx];\n const queryText = this.extractUserText(latest);\n if (!queryText || !queryText.trim()) return cleaned;\n\n const query = this.queryTransform ? this.queryTransform(queryText) : queryText;\n\n // 3. 检索 top-K(携带本转换器阈值,不影响共享 retriever 的默认值)\n const results = await this.retriever.search(query, this.maxMemories, {\n minScore: this.minScore,\n });\n if (results.length === 0) return cleaned;\n\n // 4. 更新检索统计(fire-and-forget,不阻塞注入首 token 延迟)\n if (this.onRecall) {\n const ids = results.map((r) => r.entry.id);\n Promise.resolve()\n .then(() => this.onRecall!(ids))\n .catch(() => {\n // 统计失败忽略\n });\n }\n\n // 5. 构造记忆块并前插进最新 user 消息内容\n const block = buildMemoryBlock(results);\n const injected = this.injectIntoResource(latest, block);\n\n cleaned = [...cleaned];\n cleaned[latestIdx] = injected;\n return cleaned;\n }\n\n // ─── 工具方法 ───────────────────────────────────────────────────\n\n /** 从资源中抽取纯文本(兼容 string 与 ContentBlock[]) */\n private extractUserText(resource: ContextResource): string {\n const content = resource.content;\n if (typeof content === 'string') return stripMemoryBlock(content);\n if (Array.isArray(content)) {\n // 拼接所有文本块(已剥除 sentinel)\n return content\n .map((b) => (this.isTextBlock(b) ? stripMemoryBlock(b.text) : ''))\n .join('')\n .trim();\n }\n return '';\n }\n\n private isTextBlock(b: ContentBlock): b is TextContent {\n return (b as TextContent).type === 'text';\n }\n\n /** 剥除单个资源中的 sentinel 块,返回新资源(若无需剥除返回原资源) */\n private stripResource(resource: ContextResource): ContextResource {\n if (resource.type !== 'user_message') return resource;\n const content = resource.content;\n\n if (typeof content === 'string') {\n if (!hasMemoryBlock(content)) return resource;\n return this.rebuildResource(resource, stripMemoryBlock(content));\n }\n\n if (Array.isArray(content)) {\n let changed = false;\n const newBlocks: ContentBlock[] = [];\n for (const b of content) {\n if (this.isTextBlock(b)) {\n if (!hasMemoryBlock(b.text)) {\n newBlocks.push(b);\n continue;\n }\n const stripped = stripMemoryBlock(b.text);\n if (stripped) {\n newBlocks.push({ type: 'text', text: stripped } as TextContent);\n }\n changed = true;\n } else {\n newBlocks.push(b);\n }\n }\n if (!changed) return resource;\n return this.rebuildResource(resource, newBlocks);\n }\n\n return resource;\n }\n\n /** 把记忆块前插进资源内容 */\n private injectIntoResource(resource: ContextResource, block: string): ContextResource {\n const content = resource.content;\n\n if (typeof content === 'string') {\n const original = stripMemoryBlock(content);\n const injected = `${block}\\n\\n${original}`;\n return this.rebuildResource(resource, injected);\n }\n\n if (Array.isArray(content)) {\n // 过滤掉旧 sentinel 文本块,再前插新 sentinel 文本块(保留图片等非文本块)\n const kept: ContentBlock[] = content.filter(\n (b) => !(this.isTextBlock(b) && hasMemoryBlock(b.text)),\n );\n const injected: ContentBlock[] = [\n { type: 'text', text: `${block}\\n\\n` } as TextContent,\n ...kept,\n ];\n return this.rebuildResource(resource, injected);\n }\n\n return resource;\n }\n\n /** 重建资源(保持 id/type/role/timestamp/dependencies/meta/pinned,替换 content) */\n private rebuildResource(\n resource: ContextResource,\n content: string | ContentBlock[],\n ): ContextResource {\n return new ContextResourceBuilder()\n .id(resource.id)\n .type(resource.type)\n .role(resource.role)\n .content(content)\n .timestamp(resource.timestamp)\n .pinned(resource.pinned)\n .build();\n }\n}\n","/**\n * 合并器:去重 / 合并相似记忆 + 修剪过期与低置信度。\n *\n * 参考 agentmemory 的 consolidate 阶段:\n * - 候选窗口 = 自上次合并以来内容有更新的条目(增量合并,避免每次对全库 N 条\n * 逐一检索的 O(N²) 塌陷;首次合并或跨进程重启后为全量)。\n * - 对每个候选:以其 content 作为 query 检索相似记忆,相似度 >= 阈值则合并。\n * - 合并:content 取较长、concepts 并集、置信度取 max + 小奖励(避免旧「累加」\n * 使置信度快速饱和到 1.0 而丧失排序/修剪信号)。\n * - 修剪过期 / 低置信度;超过 maxMemories 时淘汰置信度最低的。\n */\n\nimport type { HybridRetriever } from '../retrieval/hybrid-retriever';\nimport type {\n ConsolidateOptions,\n ConsolidatorLike,\n MemoryEntry,\n MemoryEventSink,\n MemoryStore,\n} from '../types';\n\n/** 合并两条记忆为一条(保留 newer 的 id) */\nfunction mergeTwo(a: MemoryEntry, b: MemoryEntry): MemoryEntry {\n const newer = a.updatedAt >= b.updatedAt ? a : b;\n const older = newer === a ? b : a;\n // content 取较长者(信息量更大),若相近则取较新\n const content =\n newer.content.length >= older.content.length ? newer.content : older.content;\n const concepts = [...new Set([...a.concepts, ...b.concepts])];\n // 置信度取 max + 0.05 小奖励(截断到 1):累加会让记忆快速顶到 1.0,\n // 使 confidence 作为 pruning/排序信号失效\n const confidence = Math.min(1, Math.max(a.confidence, b.confidence) + 0.05);\n const lastRecalledAt = [a.lastRecalledAt, b.lastRecalledAt]\n .filter((t): t is number => t != null)\n .sort()\n .pop();\n\n return {\n id: newer.id,\n content,\n concepts,\n confidence,\n source: 'consolidation',\n sessionKey: newer.sessionKey,\n createdAt: Math.min(a.createdAt, b.createdAt),\n updatedAt: Date.now(),\n lastRecalledAt,\n recallCount: a.recallCount + b.recallCount,\n embedding: newer.embedding ?? older.embedding,\n expiresAt: newer.expiresAt ?? older.expiresAt,\n meta: { ...(older.meta ?? {}), ...(newer.meta ?? {}) },\n };\n}\n\nexport interface ConsolidatorOptions {\n similarityThreshold?: number;\n /** 事件接收器(合并结果/失败) */\n onEvent?: MemoryEventSink;\n}\n\nexport class Consolidator implements ConsolidatorLike {\n private store: MemoryStore;\n private retriever: HybridRetriever;\n private similarityThreshold: number;\n private onEvent?: MemoryEventSink;\n\n constructor(\n store: MemoryStore,\n retriever: HybridRetriever,\n options: ConsolidatorOptions = {},\n ) {\n this.store = store;\n this.retriever = retriever;\n this.similarityThreshold = options.similarityThreshold ?? 0.85;\n this.onEvent = options.onEvent;\n }\n\n async run(\n options: ConsolidateOptions = {},\n ): Promise<{ merged: number; pruned: number }> {\n const start = Date.now();\n const threshold = options.similarityThreshold ?? this.similarityThreshold;\n const all = await this.store.list();\n\n // 增量候选:仅处理上次合并后内容有更新的条目(touchRecall 不再更新 updatedAt,\n // 因此 updatedAt 稳定表示内容修改时间;首次/跨进程重启 lastConsolidatedAt 为空 → 全量)\n const stats = await this.store.stats();\n const since = stats.lastConsolidatedAt;\n const candidates = (since == null ? all : all.filter((e) => e.updatedAt >= since)).sort(\n // 显式按 updatedAt 降序(新条目先处理,可吸收旧的相似条目);\n // 同毫秒写入时以 createdAt 决胜,保证处理顺序确定\n (a, b) => b.updatedAt - a.updatedAt || b.createdAt - a.createdAt,\n );\n\n const processed = new Set<string>();\n let merged = 0;\n\n // 按 updatedAt 降序处理(list 已是降序),优先保留较新的幸存者\n for (const entry of candidates) {\n if (processed.has(entry.id)) continue;\n\n // 用该记忆 content 作为 query 检索相似记忆。\n // raw 模式:保留原始 BM25/cosine 分数,使 similarityThreshold 按「绝对\n // 相似度」判定 —— min-max 归一化会把次优分数压到 0,导致阈值形同虚设。\n let results: Awaited<ReturnType<HybridRetriever['search']>> = [];\n try {\n results = await this.retriever.search(entry.content, 10, { raw: true });\n } catch {\n continue;\n }\n\n const similar = results.filter(\n (r) =>\n r.entry.id !== entry.id &&\n !processed.has(r.entry.id) &&\n r.score >= threshold,\n );\n\n if (similar.length === 0) {\n processed.add(entry.id);\n continue;\n }\n\n // 原子性修复:先合并内存中的幸存者并 save(原子写入),成功后再 delete\n // 相似项。旧实现「先逐条 delete 再 save」,中途失败即丢失已删记忆。\n let survivor = entry;\n for (const r of similar) {\n survivor = mergeTwo(survivor, r.entry);\n processed.add(r.entry.id);\n }\n await this.store.save({\n id: survivor.id,\n content: survivor.content,\n concepts: survivor.concepts,\n confidence: survivor.confidence,\n source: 'consolidation',\n sessionKey: survivor.sessionKey,\n createdAt: survivor.createdAt,\n embedding: survivor.embedding,\n expiresAt: survivor.expiresAt,\n recallCount: survivor.recallCount,\n });\n merged += similar.length;\n for (const r of similar) {\n try {\n await this.store.delete(r.entry.id);\n } catch (err) {\n // delete 失败留下重复条目,可被下一轮合并吸收;不中断整体\n this.onEvent?.({ type: 'consolidate:failed', error: `delete:${(err as Error).message}` });\n }\n }\n processed.add(survivor.id);\n }\n\n // 修剪过期 / 低置信度\n let pruned = 0;\n try {\n pruned += await this.store.prune({\n maxAgeMs: options.maxAgeMs,\n minConfidence: options.minConfidence ?? 0.1,\n });\n } catch (err) {\n this.onEvent?.({ type: 'consolidate:failed', error: (err as Error).message });\n // 修剪失败忽略\n }\n\n // 数量上限:淘汰置信度最低、最旧的\n if (options.maxMemories != null) {\n const after = await this.store.list();\n if (after.length > options.maxMemories) {\n const sorted = [...after].sort(\n (a, b) => b.confidence - a.confidence || b.updatedAt - a.updatedAt,\n );\n const toRemove = sorted.slice(options.maxMemories);\n for (const e of toRemove) {\n const ok = await this.store.delete(e.id);\n if (ok) pruned++;\n }\n }\n }\n\n // 记录合并完成时间(驱动下一轮增量窗口)\n this.store.markConsolidated();\n\n const ms = Date.now() - start;\n this.onEvent?.({ type: 'consolidate', merged, pruned, ms });\n return { merged, pruned };\n }\n}\n","/**\n * Agent 可调用的记忆工具。\n *\n * 参考 agentmemory 的 MCP 工具(save / recall / sessions 等),\n * 但以 aipack 原生 Tool 形式提供,parameters 用纯 JSON Schema(不依赖 TypeBox)。\n *\n * 工具列表:\n * - save_memory(content, concepts?)\n * - search_memory(query, limit?)\n * - list_memories(limit?)\n * - delete_memory(id)\n */\n\nimport type { Tool, ToolResult } from '@aipack-ai/agent';\nimport { createTextContent } from '@aipack-ai/agent';\nimport type { MemoryStore } from '../types';\n\nexport interface MemoryToolsOptions {\n /** list_memories 默认返回上限,默认 20 */\n listLimit?: number;\n /** search_memory 默认返回上限,默认 5 */\n searchLimit?: number;\n /** save_memory 保存的记忆 TTL(ms),过期后 prune 时清理 */\n saveTtlMs?: number;\n}\n\n/** 工具输入硬上限:防止 LLM 误调用写入超长内容 / 超大集合 */\nconst MAX_CONTENT_CHARS = 2000;\nconst MAX_CONCEPTS = 20;\nconst MAX_LIST_LIMIT = 200;\nconst MAX_SEARCH_LIMIT = 50;\n\nfunction clampInt(v: unknown, fallback: number, min: number, max: number): number {\n const n = typeof v === 'number' && Number.isFinite(v) ? Math.floor(v) : fallback;\n return Math.min(max, Math.max(min, n));\n}\n\nexport function createMemoryTools(\n store: MemoryStore,\n options: MemoryToolsOptions = {},\n): Tool[] {\n const listLimit = clampInt(options.listLimit, 20, 1, MAX_LIST_LIMIT);\n const searchLimit = clampInt(options.searchLimit, 5, 1, MAX_SEARCH_LIMIT);\n const saveTtlMs = options.saveTtlMs;\n\n const saveMemory: Tool = {\n name: 'save_memory',\n description:\n '保存一条长期记忆(跨会话保留)。用于记录用户偏好、关键决策、事实约束、架构约定等,' +\n '以便后续会话自动检索注入。content 为记忆正文,concepts 为可选关键词标签。',\n parameters: {\n type: 'object',\n properties: {\n content: { type: 'string', description: '记忆正文' },\n concepts: {\n type: 'array',\n items: { type: 'string' },\n description: '可选关键词 / 概念标签',\n },\n },\n required: ['content'],\n },\n async execute(_toolCallId, args): Promise<ToolResult> {\n const a = (args ?? {}) as { content?: string; concepts?: string[] };\n if (!a.content || !a.content.trim()) {\n return {\n content: [createTextContent('save_memory 失败:content 不能为空')],\n details: { error: 'empty content' },\n };\n }\n const content = a.content.trim().slice(0, MAX_CONTENT_CHARS);\n const concepts = (a.concepts ?? [])\n .filter((c): c is string => typeof c === 'string' && c.length > 0)\n .map((c) => c.trim().slice(0, 50))\n .slice(0, MAX_CONCEPTS);\n return store.save({\n content,\n concepts,\n confidence: 0.7,\n source: 'tool',\n ttlMs: saveTtlMs,\n }).then((entry) => ({\n content: [createTextContent(`已保存记忆(id=${entry.id})`)],\n details: { id: entry.id, saved: true },\n }));\n },\n };\n\n const searchMemory: Tool = {\n name: 'search_memory',\n description:\n '检索与查询相关的长期记忆(BM25 关键词 + 可选向量混合检索)。返回按相关度排序的记忆列表。',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: '检索查询' },\n limit: { type: 'integer', description: '返回上限(默认 5)' },\n },\n required: ['query'],\n },\n async execute(_toolCallId, args): Promise<ToolResult> {\n const a = (args ?? {}) as { query?: string; limit?: number };\n if (!a.query || !a.query.trim()) {\n return {\n content: [createTextContent('search_memory 失败:query 不能为空')],\n details: { error: 'empty query' },\n };\n }\n const limit = clampInt(a.limit, searchLimit, 1, MAX_SEARCH_LIMIT);\n const results = await store.search(a.query.trim(), limit);\n if (results.length === 0) {\n return {\n content: [createTextContent('未找到相关记忆。')],\n details: { count: 0 },\n };\n }\n const lines = results.map(\n (r, i) =>\n `${i + 1}. [score=${r.score.toFixed(2)} id=${r.entry.id}] ${r.entry.content.replace(/\\s+/g, ' ').slice(0, 200)}`,\n );\n return {\n content: [createTextContent(`找到 ${results.length} 条相关记忆:\\n${lines.join('\\n')}`)],\n details: { count: results.length, ids: results.map((r) => r.entry.id) },\n };\n },\n };\n\n const listMemories: Tool = {\n name: 'list_memories',\n description: '列出最近的长期记忆(按更新时间倒序)。',\n parameters: {\n type: 'object',\n properties: {\n limit: { type: 'integer', description: '返回上限(默认 20)' },\n },\n },\n async execute(_toolCallId, args): Promise<ToolResult> {\n const a = (args ?? {}) as { limit?: number };\n const limit = clampInt(a.limit, listLimit, 1, MAX_LIST_LIMIT);\n const entries = await store.list(limit);\n if (entries.length === 0) {\n return {\n content: [createTextContent('当前无任何记忆。')],\n details: { count: 0 },\n };\n }\n const lines = entries.map(\n (e, i) =>\n `${i + 1}. [id=${e.id} conf=${e.confidence.toFixed(2)}] ${e.content.replace(/\\s+/g, ' ').slice(0, 200)}`,\n );\n return {\n content: [\n createTextContent(`共 ${entries.length} 条记忆:\\n${lines.join('\\n')}`),\n ],\n details: { count: entries.length, ids: entries.map((e) => e.id) },\n };\n },\n };\n\n const deleteMemory: Tool = {\n name: 'delete_memory',\n description: '按 id 删除一条长期记忆。',\n parameters: {\n type: 'object',\n properties: {\n id: { type: 'string', description: '记忆 id' },\n },\n required: ['id'],\n },\n async execute(_toolCallId, args): Promise<ToolResult> {\n const a = (args ?? {}) as { id?: string };\n if (!a.id) {\n return {\n content: [createTextContent('delete_memory 失败:id 不能为空')],\n details: { error: 'empty id' },\n };\n }\n const ok = await store.delete(a.id);\n return {\n content: [\n createTextContent(ok ? `已删除记忆(id=${a.id})` : `未找到记忆(id=${a.id})`),\n ],\n details: { id: a.id, deleted: ok },\n };\n },\n };\n\n return [saveMemory, searchMemory, listMemories, deleteMemory];\n}\n","/**\n * 插件聚合入口:createMemoryPlugin(options) → { store, retriever, extensions, transformers, tools, install() }\n *\n * 把 MemoryStore + HybridRetriever + MemoryCaptureExtension + MemoryInjectionTransformer\n * + Consolidator + memory tools 装配成一个开箱即用的插件。\n *\n * 用法(aipack.config.js):\n * import { createMemoryPlugin } from '@aipack-ai/memory';\n * const mem = createMemoryPlugin({ baseDir: '~/.aipack/memory' });\n * const r = mem.install();\n * export default {\n * ..., // provider, model, systemPrompt, sessions...\n * extensions: r.extensions,\n * transformers: r.transformers,\n * tools: r.tools,\n * };\n *\n * 默认配置:FileMemoryStore(持久化)+ 纯 BM25 检索(零依赖)+ 自动捕获 + 自动注入 + 4 个记忆工具。\n * 提供 embedder 后自动升级为 BM25 + 向量混合检索(双路独立召回);提供 summarizeFn 后 capture 走 LLM 摘要。\n */\n\nimport type { Extension, ContextTransformer, Tool } from '@aipack-ai/agent';\nimport { FileMemoryStore } from './store/file-memory-store';\nimport { HybridRetriever } from './retrieval/hybrid-retriever';\nimport type { RetrieverLike, VectorSearchLike } from './retrieval/hybrid-retriever';\nimport { MemoryCaptureExtension } from './capture/capture-extension';\nimport type { CaptureOptions } from './capture/capture-extension';\nimport { MemoryInjectionTransformer } from './injection/injection-transformer';\nimport type { InjectionOptions } from './injection/injection-transformer';\nimport { Consolidator } from './consolidation/consolidator';\nimport { createMemoryTools } from './tools/memory-tools';\nimport type {\n Embedder,\n MemoryEvent,\n MemoryEventSink,\n MemorySearchResult,\n MemoryStore,\n SummarizeFn,\n} from './types';\n\n// ─── StoreBackedRetriever:把任意 MemoryStore 包装成 RetrieverLike ──────────\n\n/**\n * 适配器:将 MemoryStore.search() 包装为 RetrieverLike,供 HybridRetriever 使用。\n *\n * 默认 store(FileMemoryStore / InMemoryStore)的 search() 已内置 BM25,\n * 此适配器直接委托,保证检索与存储数据实时同步。\n * 对于自带检索能力的自定义 store 同样适用。\n */\nclass StoreBackedRetriever implements RetrieverLike {\n constructor(private store: MemoryStore) {}\n\n async search(query: string, limit = 5): Promise<MemorySearchResult[]> {\n return this.store.search(query, limit);\n }\n}\n\n// ─── StoreVectorSource:把 MemoryStore.searchVectors() 包装成向量检索源 ─────\n\n/**\n * 适配器:将 store.searchVectors() 暴露为 VectorSearchLike,供 HybridRetriever\n * 做「独立于 BM25 的向量召回」。\n */\nclass StoreVectorSource implements VectorSearchLike {\n constructor(private store: MemoryStore) {}\n\n async searchVectors(queryVec: number[], limit = 5): Promise<MemorySearchResult[]> {\n return this.store.searchVectors(queryVec, limit);\n }\n}\n\n// ─── 默认事件接收器:失败类事件打印告警 ───────────────────────────────\n\nconst FAILURE_EVENTS: ReadonlySet<MemoryEvent['type']> = new Set([\n 'store:corrupt',\n 'embedding:error',\n 'capture:failed',\n 'consolidate:failed',\n]);\n\nfunction defaultEventSink(event: MemoryEvent): void {\n if (FAILURE_EVENTS.has(event.type)) {\n console.warn(`[aipack-memory] ${event.type}: ${(event as { error?: string }).error ?? ''}`);\n }\n}\n\n// ─── 插件选项与返回类型 ──────────────────────────────────────────────\n\nexport interface MemoryPluginOptions {\n /** FileMemoryStore 存储目录(支持 ~ 开头),默认 <cwd>/.aipack/memory */\n baseDir?: string;\n /** 自定义 store(覆盖默认 FileMemoryStore;需自行保证 search 能力) */\n store?: MemoryStore;\n /** 注入 top-K 上限,默认 5 */\n maxMemories?: number;\n /** 最低相关度阈值,默认 0.1 */\n minScore?: number;\n /** 捕获开关 / 选项,默认 true */\n capture?: boolean | CaptureOptions;\n /** 注入开关 / 选项,默认 true */\n inject?: boolean | InjectionOptions;\n /** 记忆工具开关,默认 true */\n tools?: boolean;\n /** 可选向量化器,配置后启用 BM25 + 向量混合检索 */\n embedder?: Embedder;\n /** 可选 LLM 摘要函数,配置后 capture 走 LLM 摘要(默认零-LLM 要点抽取) */\n summarizeFn?: SummarizeFn;\n /** 每 N 次捕获自动触发一次 consolidate(0=不自动,默认 0) */\n consolidateEvery?: number;\n /** 捕获记忆 TTL(ms),过期后 prune 清理 */\n captureTtlMs?: number;\n /** save_memory 工具保存的记忆 TTL(ms),过期后 prune 清理 */\n toolTtlMs?: number;\n /** 事件接收器(失败/整理/统计等关键节点;默认打印失败告警) */\n onEvent?: MemoryEventSink;\n}\n\nexport interface MemoryPlugin {\n /** 装配好的 store(可直接编程式调用 save/search/consolidate 等) */\n store: MemoryStore;\n /** 装配好的混合检索器 */\n retriever: HybridRetriever;\n /** 扩展列表(capture) */\n extensions: Extension[];\n /** 转换器列表(injection) */\n transformers: ContextTransformer[];\n /** 工具列表(save/search/list/delete) */\n tools: Tool[];\n /** 返回 { extensions, transformers, tools },供 aipack.config.js 展开 */\n install(): {\n extensions: Extension[];\n transformers: ContextTransformer[];\n tools: Tool[];\n };\n /** 释放资源(热重载场景) */\n dispose(): void;\n}\n\n// ─── 辅助:解析 boolean | object 选项 ─────────────────────────────────\n\nfunction isCaptureEnabled(v: boolean | CaptureOptions | undefined): boolean {\n if (v === false) return false;\n return true; // true / undefined / object → 启用\n}\n\nfunction isInjectEnabled(v: boolean | InjectionOptions | undefined): boolean {\n if (v === false) return false;\n return true;\n}\n\nfunction resolveCaptureOptions(\n v: boolean | CaptureOptions | undefined,\n fallback: {\n summarizeFn?: SummarizeFn;\n consolidateEvery?: number;\n ttlMs?: number;\n onEvent?: MemoryEventSink;\n },\n): CaptureOptions {\n if (typeof v === 'object' && v !== null) {\n return {\n ...v,\n summarizeFn: v.summarizeFn ?? fallback.summarizeFn,\n consolidateEvery: v.consolidateEvery ?? fallback.consolidateEvery,\n ttlMs: v.ttlMs ?? fallback.ttlMs,\n onEvent: v.onEvent ?? fallback.onEvent,\n };\n }\n return {\n summarizeFn: fallback.summarizeFn,\n consolidateEvery: fallback.consolidateEvery,\n ttlMs: fallback.ttlMs,\n onEvent: fallback.onEvent,\n };\n}\n\nfunction resolveInjectOptions(\n v: boolean | InjectionOptions | undefined,\n fallback: { maxMemories?: number; minScore?: number },\n): InjectionOptions {\n if (typeof v === 'object' && v !== null) {\n return {\n ...v,\n maxMemories: v.maxMemories ?? fallback.maxMemories,\n minScore: v.minScore ?? fallback.minScore,\n };\n }\n return {\n maxMemories: fallback.maxMemories,\n minScore: fallback.minScore,\n };\n}\n\n// ─── createMemoryPlugin ──────────────────────────────────────────────\n\nexport function createMemoryPlugin(options: MemoryPluginOptions = {}): MemoryPlugin {\n const embedder = options.embedder;\n const onEvent = options.onEvent ?? defaultEventSink;\n\n // 1. Store:自定义优先,否则 FileMemoryStore(带 embedder)\n const store: MemoryStore =\n options.store ??\n new FileMemoryStore({\n baseDir: options.baseDir,\n embedder,\n onEvent,\n });\n\n // 2. Retriever:BM25 + 可选向量双路独立召回\n const bm25Source: RetrieverLike = new StoreBackedRetriever(store);\n const retriever = new HybridRetriever({\n bm25: bm25Source,\n vector: embedder ? new StoreVectorSource(store) : undefined,\n embedder,\n minScore: options.minScore ?? 0.1,\n });\n\n // 3. Consolidator:注入 store,使 store.consolidate() 可用(增量合并)\n const consolidator = new Consolidator(store, retriever, {\n similarityThreshold: 0.85,\n onEvent,\n });\n store.setConsolidator(consolidator);\n\n // 4. Extensions:capture\n const extensions: Extension[] = [];\n if (isCaptureEnabled(options.capture)) {\n const captureOpts = resolveCaptureOptions(options.capture, {\n summarizeFn: options.summarizeFn,\n consolidateEvery: options.consolidateEvery,\n ttlMs: options.captureTtlMs,\n onEvent,\n });\n extensions.push(new MemoryCaptureExtension(store, captureOpts));\n }\n\n // 5. Transformers:injection\n const transformers: ContextTransformer[] = [];\n if (isInjectEnabled(options.inject)) {\n const injectOpts = resolveInjectOptions(options.inject, {\n maxMemories: options.maxMemories,\n minScore: options.minScore,\n });\n transformers.push(\n new MemoryInjectionTransformer(retriever, {\n ...injectOpts,\n // 命中后更新检索统计(fire-and-forget,不阻塞注入);\n // 失败不静默:告警日志便于排障(检索统计丢失不致命)\n onRecall: async (ids: string[]) => {\n await Promise.allSettled(ids.map(async (id) => {\n try {\n await store.touchRecall(id);\n } catch (err) {\n console.warn(`[memory] touchRecall 失败(id=${id}): ${(err as Error).message}`);\n }\n }));\n },\n }),\n );\n }\n\n // 6. Tools\n const tools: Tool[] = [];\n if (options.tools !== false) {\n tools.push(...createMemoryTools(store, { saveTtlMs: options.toolTtlMs }));\n }\n\n return {\n store,\n retriever,\n extensions,\n transformers,\n tools,\n install() {\n return { extensions, transformers, tools };\n },\n dispose() {\n store.dispose();\n },\n };\n}\n"],"mappings":"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","names":["dot"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aipack-ai/memory",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "aipack 持久化记忆插件:capture → compress → index → recall/inject → consolidate",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,18 +29,18 @@
|
|
|
29
29
|
"node": ">=18.0.0"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@aipack-ai/agent": "
|
|
32
|
+
"@aipack-ai/agent": "1.0.2"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^20.12.0",
|
|
36
36
|
"tsup": "^8.5.1",
|
|
37
37
|
"typescript": "^5.5.0",
|
|
38
38
|
"tsx": "^4.16.0",
|
|
39
|
-
"@aipack-ai/agent": "
|
|
39
|
+
"@aipack-ai/agent": "1.0.2"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsup",
|
|
43
|
-
"prebuild": "pnpm --filter @aipack-ai/agent build",
|
|
43
|
+
"prebuild": "[ -f ../agent/dist/index.d.ts ] || pnpm --filter @aipack-ai/agent build",
|
|
44
44
|
"typecheck": "tsc --noEmit",
|
|
45
45
|
"test": "node --import tsx --test tests/*.test.ts",
|
|
46
46
|
"example": "node --import tsx examples/round-trip.ts"
|