Skip to content

Releases: Tkingxiao/dsh-memu-memory

v1.2.4

Choose a tag to compare

@github-actions github-actions released this 02 Nov 11:55

一整版「量具 + 适配」:注入实测台补上离线可复现的那一半并修掉真机抓出的 §3.24 缺口;本地向量从 1 个模型扩到 5 个(新增 MiniLM、e5、Qwen3 int8/q4f16 两张卡),补上非对称前缀需要的角色通道与下载来源选择;整理超时改为按转录规模推预算;总日志里的失败行终于像失败(红色只在 WebUI 总日志页;侧栏整理记录只留正常流水,失败由「整理提醒」负责)。不加依赖、不加构建步骤;bge-small-zh-v1.5-int8 的 manifest 一个字节未改。

两条目录条目从 pending 转为可用

  • all-minilm-l6-v2-int8(23.9 MB,384 维,mean + L2)与 multilingual-e5-small(135.4 MB,384 维,mean + L2)现在带着固定 commit、逐文件 bytes/sha256、图描述符(opset 11 / 3 个 int64 输入 / last_hidden_state float32[batch,seq,384] / 自包含)、量化事实与许可证来源。这三条仍只固定 ModelScope 的 revision(2026-10-30 的核实口径:当时 huggingface.co 超时、hf-mirror 没取到可核对的固定 commit,所以其余来源保持「不生成 URL」)。2026-11-02 复查发现 hf-mirror 其实可用(它重定向到自己的 resolve-cache,不经过 huggingface.co,两条 Qwen3 卡片就走它),但没有回填到这三条:换源不是免费的,要重新逐字节核对一遍 hash,而那三条的 ModelScope 源本来就在正常工作。
  • 每条事实都有出处,不写推测值:pooling 取自官方仓库的 1_Pooling/config.json(作为 origin 固定的见证制品,不冒充导出仓库),mean_tokens=true 且 word_embedding_dimension=384;量化方案对两个图都做了结构核验——MatMulInteger + DynamicQuantizeLinear,且 MiniLM 39/39、e5 75/75 个 int8/uint8 二维权重都存在长度=输出通道数的一维 float32 scale(标量 scale=0),即 per-channel 权重 + 动态激活量化;e5 另有仓库自带的 quant_config.json 佐证 reduce_range=true。
  • maxTokens 按制品本身取 512(config.max_position_embeddings、tokenizer_config.model_max_length、图输出维度三处一致),不按 sentence-transformers wrapper 的 max_seq_length=256 收窄——那只是 SB 的窗口,写进目录会把分窗口径变成模型属性。参数量同样来自图内 initializer 元素计数(22,565,376 / 117,505,920),不是模型页上的约数。
  • 三条 runtime 事实抽出成共享的 PINNED_ONNX_RUNTIME:三个模型 pin 的是同一棵受管 ORT 树,复制三遍只会让哪天改漏一份。改动前后 bge-small-zh-v1.5-int8 的 profileFingerprint 与 manifestDigest 逐字节相同(47511436… / c18f4719…),已装好的 BGE 库不会因此对不上号。

非对称前缀需要角色通道,而不是猜(§14.4 / §14.8)

  • worker 此前完全不施加 manifest.prefixes,所以 e5 这类模型即使装了也会用错读法。现在前缀由 worker 按请求角色施加,角色走 model 字符串后缀(:query / :passage):MemU 的信封只有 {model, input, encoding_format},没有 role 字段,而知道这条文本是查询还是被存进的条目的一方只有启动子进程的插件,所以 retrieve 注入 :query、commit / list-files 注入 :passage。
  • 裸 model id 对非对称 profile 一律 400 LOCAL_VECTOR_ROLE_REQUIRED,不"默认成文档":默认错一边就得到一个指纹声称不是这个空间的向量空间,而 HTTP 还是 200。是否要角色由 prefixesRequireRole(entry)(即 prefixes.query !== prefixes.document)派生,前缀本身在指纹里,因此不引入第二个可配置开关。BGE/MiniLM 两侧同前缀,行为与请求字节完全不变。
  • prefixesRequireRole 只从前缀派生;/health 现在报告 rolesRequired,worker endpoint 也把它带回给调用方,healthCheckLocalVector 用文档角色探测。
  • 实测过再写进目录:query: 与 passage: 下同一句话的余弦 0.9811(确实是两个向量),前缀策略 A/B(同一份草稿)asym R@1 0.6667 / MRR 0.8182 / 分离度 0.1230,sym-passage 0.6364 / 0.8030 / 0.0867,sym-query 0.6667 / 0.8157 / 0.1155,无前缀 0.6364 / 0.8030 / 0.1139。差异只有一个样本量级,所以结论是"对称前缀不是不能活,而是没有理由用一个更差的",不是"非对称被证明显著更好"。
  • 真机端到端(隔离 home + junction 复用已装引擎与制品,不碰真实记忆库):MiniLM(g1) → e5(g2) → BGE(g3) 三次切换各 6/6 条重建、0 失败,库文件名带各自指纹+代际(global.<fp8>.g<n>.sqlite3),切换后中英查询均召回,回滚回到上一空间。这一跑同时坐实了 MemU 会把带后缀的 model 原样回传(memu/embedding/backends/openai.py 发 {"model": embed_model, "input": inputs})。

守卫与界面

  • tests/embedding-catalog.test.mjs:三条目的指纹钉死(改目录必须是有意识地来这里更新);新增"两条已核实条目"与"只有非对称前缀需要角色通道"两组断言;pending 的口径改用合成条目验证。
  • 新增 tests/local-embedding-env.test.mjs(3 条):角色→model 字符串的映射、对称模型逐字节不变、strict-local 信封未被角色改动带走。tests/local-embedding-worker.test.mjs 加 1 条:前缀确实到达推理、裸 id 被拒且没有进推理、/health 报告 rolesRequired。tests/local-embedding-protocol.test.mjs 加 1 条角色解析与拒绝。tests/embedding-runtime.test.mjs 的"完整运行时"断言从只看 BGE 扩成遍历三条目 × 五个平台。
  • 设置页模型卡多一个语言 Tag(en / zh · en · multilingual),因为现在有三条可选,"这只在英文上强"是选择时必须看得见的信息;getLocalVectorState().catalog 随之透出 languages 与 rolesRequired。
  • 文档:docs/env-and-commands.md 新增 §9.5.1(三条模型的制品事实、实测横向对照、角色通道的为什么与命令)。

Qwen3-Embedding-0.6B 两张卡片(int8 默认 + q4f16)

  • 目录新增 qwen3-embedding-0.6b-int8(onnx/model_quantized.onnx,600.2 MB,默认下载的那张)与 qwen3-embedding-0.6b-q4f16(541.8 MB)。逐文件 bytes/sha256 与 HF 文件树的 lfs.oid 逐字节对上;图头是 opset 21、只有 input_ids + attention_mask(因果 LM 没有 token_type_ids)、输出 last_hidden_state[1, sequence_length, 1024]——batch 维是导出时写死的 1,所以一次只能喂一行。
  • 两种量化 = 两个向量空间 = 两张卡片:int8 是 quantize_dynamic,图侧核实 196/196 个 MatMulInteger 的 scale/zero_point 都是 rank-0 标量、per-channel scale 向量为 0 → per-tensor,与 BGE/MiniLM/e5 的 per-channel 不是同一个空间;q4f16 是 com.microsoft.MatMulNBits(int4、block_size=128)+ float16 激活、图输出 dtype 也是 float16。quantization 进指纹,所以分条目、分 DB,不许一个条目挂两个图文件让用户以为能混用。
  • 新增 last-token pooling(pooling: 'last'):因果 LM 按 attention_mask 累加和定位最后一个非 pad token,与官方 1_Pooling(pooling_mode_lasttoken=true) 同口径。校验器的 pooling 白名单加 last(max 仍是「可以声明、执行侧没实现」,所以没有任何 available 条目用它)。
  • 窗口与上限:maxTokens 合法范围从 8192 放宽到 32768(Qwen3 制品的 max_position_embeddings),但卡片按实测延迟取 4096——1520 字实测已 4.29 s,按 32k 跑一条长记忆会直接顶到 worker 的 120 s 请求超时。verifyStagedArtifacts 因此从「窗口必须等于制品上限」改成「不得超过制品上限」:超过必炸,小于是指纹里写明的策略。单文件下载上限 512 MiB → 1 GiB(否则装不下这条已逐字节核实的 600 MB 制品)。
  • 查询侧用官方 Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery: 前缀、document 侧官方明确不加指令 → 非对称,复用上一节那条角色通道,无需新概念。实测同一句话加/不加前缀余弦只有 0.8284(q4f16 0.8203),比 e5 的 0.9811 挪动得多——前缀在这款模型上是真的换空间。
  • 实测(同一批文本、同一棵受管 ORT 树,五行可以直接比):加载 int8 2.06 s / q4f16 11.4 s;一次编码 29 / 380 / 1520 字 = int8 91 / 437 / 4286 ms,q4f16 110 / 870 / 5863 ms,而 BGE 是 5 / 17 / 22 ms。草稿集(33 条可答)int8 带前缀 R@1 0.6970 / MRR 0.8359、裸文本 0.6667 / 0.8081;q4f16 带前缀 0.7273 / 0.8510。q4f16 没有比 int8 差,差一个样本不足以排质量;能当依据的只有加载慢 5.5 倍与贡献算子依赖,所以默认仍是 int8。
  • 真机端到端:五路互切 MiniLM(g1) → Qwen3-int8(g2) → Qwen3-q4f16(g3) → e5(g4) → BGE(g5) 各 6/6 条重建、0 失败,每个代际一份指纹命名的库,切换后中英查询均召回,回滚回到上一空间。

整理超时不再是必然事件:预算随转录规模走

  • 真实读数(2026-11-02,935 条事件的会话):整理 fork 开始后 125 s 才写出第一条记忆,180 s 到点只写完 3 条 → 超时;退避重试 131 s 完成。不是卡死,也没丢东西——那 3 条在 index/lib_256e5cb7.json 与向量库里都核到了。根因是 fork 的种子是整段会话,worker 读完整段才开始写,而预算是写死的 180 s。
  • 现在按范围推:consolidationWaitBudget(eventCount) = 120 s + 0.35 s/事件,夹在 [180 s, 600 s](斜率取自实测的 0.14–0.2 s/事件,留约 2 倍余量)。小会话仍是 3 分钟,935 条拿到 447 s,再大也不会超过 10 分钟——卡死的 fork 仍然会被收掉。显式 maxWaitMs 的调用方行为完全不变(既有 30 ms 超时用例照旧过)。
  • 可见性:整理已开始 日志带上「预算 Ns」,超时文案带上「over N events」,失败行用上一节的红色 + 失败 标签——下次看到就能判断是不是范围太大。
  • 明确没做的事:超时仍然不推进 lastSleepSeq。没有回执就不能证明覆盖了哪一段,重试必须重读同一段;为了省时间改成部分推进等于可能漏事件,不做。
  • 守卫:tests/consolidation-fork.test.mjs 新增预算缩放用例(0/100/200/935/5000/非法值)。

总日志里的失败行终于像失败了

  • 现象:WebUI ›「全部日志」里,整理超时/失败与正常流水长得一模一样。判据写的是 level === 'error' 才上色,而活动日志的超时路径记的是 level: 'warn' + ok: false(lib/private.js),归一化器又只在没显式给 level 时才从 ok===false 推成 warn——于是这条真实失败永远走不到那个分支。
  • 改成三档判定 logRowState:失败(ok === false || level === 'error',红左边条 + 浅红底 + 失败 Tag)/ 降级(level === 'warn' 且没失败,例如「向量未激活,写入已降级为索引+引擎」「自动关联跳过」,橙边 + 降级 Tag)/ 常规(不标)。失败与降级不同色是刻意的:都标红会让真失败被降级噪声淹没。
  • 标红只发生在总日志页。侧栏「整理记录」不再显示失败行(过滤掉,空态按过滤后算)——失败已经由它上方跨会话的「整理提醒」负责(可清除、可跳转),5 行摘要里再挤一遍红字只会把正常流水挤走。
  • 不只靠颜色(配色对色觉障碍与灰度截图无效),Tag 带 hover 解释;样式走作用域 CSS .dshm-logrow.is-failed / .is-degraded,没有新增内联样式。
  • 守卫:tests/client-render.test.mjs 新增一条行为用例(总日志页三行 fixture 分别断言 class 与文字标签,hover 必须存在);静态断言钉住「侧栏先过滤失败行」「只有总日志页用 logRowClass」「旧的 level === 'error' ? 'warn' 不许回来」。做过变异验证:把判定改回只看 level === 'error' 时行为用例立刻红。

下载来源可选:默认 hf-mirror,源站可切

  • download(id, { provider }):不传就取目录顺序里第一个已固定的来源(Qwen3 卡片因此默认 hf-mirror),传了就只用那一个、解析不出可用来源时如实报错而不回退到别的源。RPC downloadLocalVectorModel 透传 provider;设置页只在「有多于一个已固定来源」的卡片上给一行 is-row 选择,只有一个来源就不出现「选」这件事。换源不改向量空间——provider 不进指纹,每个文件落盘前都要过目录 pin 的同一个 sha256。
  • 见证制品(基座仓库的 1_Pooling/config.json)原本绑死它被核实的那个 provider,换源会被这条规则整条封掉;新增 origin.providers 显式声明合法的镜像集合(默认仍是单 provider,三条老条目行为不变)。
  • 第一次真装 Qwen3 抓到的真 bug:hf-mirror 对小文件用 chunked 传输、不发 content-length,而下载器把 Number(null) = 0 当成「声明了 0 字节」,于是每个小文件都被误判成 EMBEDDING_SIZE_MISMATCH。现在头缺失等于「未知」,判断交给落盘的字节数与 sha256。第二个由测试抓到:embedding-download.js 用了 PROVIDERS 却没 import。
  • 目录构建器顺手去掉一条自找麻烦的断言:totalBytes 只在与文件之和不符时报错——它本来就是同一份事实的两种写法,旧规则逼着手写清单里那条要么删掉要么预先算对。
  • 文档:docs/env-and-commands.md §9.5.1 改为五行对照(含延迟列的同一批文本口径)并新增「下载来源」一段;docs/troubleshooting.md §3.25 覆盖 Qwen3 专属坑(固定 batch=1、贡献算子域、延迟、选源与 chunked 头)。
  • 守卫:tests/embedding-catalog.test.mjs(五指纹钉死、两张 Qwen3 卡片是两个空间、默认来源必须是 hf-mirror 且源站同 commit 同 sha256)、tests/embedding-download.test.mjs(缺失 content-length 不误判、显式选源不回退、两种错码分开、last-pooling/float16/窗口上限的装机校验)、tests/local-embedding-worker.test.mjs(cls / mean / last 在同一份 hidden state 上各算各的,padding 不参与 last 的定位)。

注入离线模拟器

  • 新增 scripts/simulate-injection.mjs(npm run simulate:injection,EVALUATION ONLY):挂真实的 registerInjection('agent/pre-step'),在临时 DSH_MEMU_HOME 里播种一份「每种可注入块各有一条」的库——明天/今天/已过期的带日期未了之事、年度纪念日、含时效词的条目、四个月前的旧条目、近似重复的主题对——跑 N 轮,把每次组装好的请求打给只监听 127.0.0.1 的伪端点,产出 report.md / mock-llm.jsonl / settings-snapshot.json 与一行 JSON 摘要。
  • 转录是累积的(已发过的插件消息留在上下文里,和宿主一样)。这不是省事:ensureUnifiedGuide 会检查 decision.messages 里有没有当前规则的 guide,历史里没有就每轮重投,guide 计数会变成 50/50 而不是 1/50——那种数只证明驱动写错了。
  • 默认 randomCueChance=0(出货默认 0.02),让跑数可复现;--cue 可还原。--turns/--home/--out/--port/--json;退出码 0 / 2 用法错 / 3 注入链路自己报了 warn(模拟器把 warn 当失败,不当噪声)。
  • 覆盖范围写死在文档与脚本头里:它量的是插件决定了什么,不含宿主的折叠与真实模型的 tool 轮;与真机(scripts/drive-session.mjs)对不上时以真机为准。
  • 新增 tests/simulate-injection.test.mjs(3 条):钉的是量具属性而非某次计数——guide 只在首轮一次、锚点只在首轮一次、标题卡 body lines 必须为 0、JSONL 里不许出现记忆正文或标题词、设置快照带 C/D 的读数口径、用法错误退码 2。
  • 第一批离线基线(2026-11-01,50 轮,设置见快照:autoInject=smart、dedupeTurns=16、turnTokens=800、injectionTotalTokens=2000、freshnessHint=on、freshnessDays=30、openLoopRemindDays=7、salientWake=off):注入 18/50 轮(36%);按块的 Δ 计数 guide 1 / anchor 1 / due 1 / anniversary 1 / texture 1 / openLoop 1 / context 18 / freshness 11,media / lexicon / cue / replay 为 0(无附件、词典为空、cue 概率置 0、没有压缩事件);标题卡 body lines 全部 0;0 warn;整场只推过 16 个不同 id。32 个 0 注入轮归因到 dedupeTurns=16 的窗口,这与报告的“本轮没有新块”一致,但离线报告本身证明不了这一点——真机要靠 session log 对账坐实。
  • docs/env-and-commands.md §9.6 改为「三件工具各量什么、覆盖不到什么」的分工表,并记下真机前提:provider 配置在 ~/.dsh/profiles/<profile>/cordis.patch.yml 的 llm-pi-ai,- id/config 是整份替换不是合并,改完要重启该 profile 的宿主。
  • 真机 50 轮跑了(2026-11-01,隔离实例):DSH_HOME 用 launcher 自己的 home,profile 只加一个 dsh-memu-memory junction,provider 指向 8090 的伪端点,dsh --profile … --port 0 打印的 token 换 cookie 后由 drive-session 灌轮。读数(51 请求 / 2 会话指纹):guide、anchor、due、`annive...
Read more

v1.2.3

Choose a tag to compare

@github-actions github-actions released this 01 Nov 11:20

todolist.txt 六条设想的施工版本(工单 A–F)。行为变化集中在注入与整理两侧,都不删记忆;工单 E 默认关闭,等真机验收。

情绪四轴上卡片(工单 A)

  • 记忆卡片与详情抽屉现在显示 arousal(激活)与 dominance(掌控)以及情绪来源(模型标注 / 会话心情 / 正文识别 / 未知);旧条目缺这两个字段就整行不出现,不从 valence 反推。
  • 卡片的情绪 Tag 加悬停提示,展开四轴构成与来源。仍是只读:情绪不是编辑项(docs/emotion-model-spec.md §1.2 边界不变),全局条目不带情绪(§1.1 非目标)。

未了之事与日期联动(工单 C)

  • 过期即复提:带 YYYY-MM-DD 且已过期的 open_loop 条目不再受「静置 3 天」门槛限制,进入 [open loop] 候选并标注 date=… 已经过了,让模型说得清晚了多久。
  • 节奏由自己的提醒时钟决定:binding 记 openLoopRemindedAt,同一条每隔 openLoopRemindDays(默认 7 天,[1,365])复提一次,同日不重复;90 天未命中的记录自动清理。常规腿(无日期承诺)行为完全不变:3 天门槛 + 提过即静默。每回合仍最多 1 条。
  • 同回合不重复念:纪念日块(<memory-anniversary> / <memory-due>)真正进入文本时,同一条目从两条腿都剔除;预算不够没注入时不会连带沉默未了之事。到期当天的措辞在 <memory-due> 内升级,不新开块。
  • 闭环仍只有用户能做:不自动检测承诺、不自动关闭,关闭走 memu_private_update。

条件式时效提示(工单 D)

  • 召回块尾部按需追加一条 <memory-freshness>:当至少一条进入文本的条目带时间敏感信号——已过期的 YYYY-MM-DD、标题/正文命中固定词表(最新/最近/现在/目前/今年/版本/价格/天气/新闻/比分/汇率)、或条目年龄 ≥ freshnessDays(默认 30 天)——提示模型「若会话里有可用的搜索工具,回答当前状态前先核实;没有就如实说明可能过时」。
  • 判定是新增 lib/freshness.js 的只读纯函数,不加字段、不改存储;owner=about_project 的工程事实不按年龄算陈旧;年度 MM-DD 永不过期。
  • 与召回共用 turnTokens 预算,装不下就丢提示、召回照常(fail-open,异常只 warn);anchor-only 模式与 autoInject=off 不出。措辞刻意是条件式:插件无法确知本会话的工具面(docs/host-api.md §6.8)。
  • 设置:freshnessHint(默认开)、freshnessDays(默认 30,[1,3650])。不改 4 份 system-guide,不动 rulesKey。

纪念日到点唤醒(工单 E,实验特性,默认关闭)

  • salientWake='schedule' 时,整理周期新增 lib/salient-wake.js pass:按投递时区扫今天/明天命中的 salient date,用宿主公开的 ctx.schedule 给绑定会话排一次性任务(timing.at = {date,time,time_zone},target={kind:'session',sessionId},时刻 salientWakeHour 默认 09:00,salientWakeTimeZone 留空跟随系统)。
  • 到点是 agent.followup,新开一整回合;而本插件的注入只认真实用户消息,所以那一回合拿不到锚点/召回/纪念日块——唤醒 prompt 自带日期、措辞与 memu_private_recall 指引,并声明「已说过不要重复」。
  • 幂等靠 schedule.list 对账:同名 active 跳过、paused 不动、已 ended 删除、过期槽位不补建;subagent 与归档会话只跳过并记信号;宿主没有 schedule 服务时 warn 一次并降级为被动注入。失败留 kind:'wake' 活动日志,不让周期失败。日历日与「这一刻是否已过」按投递时区计算(dayInZone / instantInZone):早先按机器墙钟算,配置时区与系统时区不一致时会排错一天,或把那个时区里还没到的时刻当成已过而静默不排。
  • 边界(写进 docs/):宿主进程必须活着;at-least-once 可能重复投递;用户手删任务下轮重建;任务在宿主 schedule UI 可见。真机验收前默认 off。

实体主节点合并(工单 F)

  • lib/graph.js 新增 mergeEntities({libraryId,targetId,sourceIds}):实体文件锁内一次完成——别名取并集(含来源名字)cap 30 并报告丢弃项、私有索引 links[].to 全量改指主节点(同 (to,kind) 去重)、关系快照按端点对重键(与主节点侧冲突时保留主节点侧并计入回执)、删除来源实体并 prune 孤立项。只改指向,不删记忆;活动日志记 op:'entity-merge'。kind 不同、自指、未知 id 一律拒绝且不留半成品。
  • suggestEntityMerges({libraryId}) 是纯读候选:同 kind 且「归一化后互相包含(短方 ≥2 字)」或「编辑距离 ≤2(短方 ≥3 字)」,稳定排序、上限 20,永不自动合并。
  • WebUI 节点图页新增「合并候选」区:显示双方名字与理由,两次点击确认,合并后回执写明改指多少条链接、并入多少别名、丢了多少。RPC 新增 mergeEntities / suggestEntityMerges(方法数 93 → 95,走同一库级授权)。
  • 不做跨库与全局↔私有归并;没有 undo,回退走既有备份。

注入实测台(工单 B,仅评测用)

  • 新增 scripts/mock-llm.mjs:只监听 127.0.0.1 的伪 OpenAI 兼容端点,把每次模型请求的块标记命中(guide / anchor / due / anniversary / cue / texture / media / lexicon / context / open loop / freshness / replay)记成 JSONL,--report 输出「每轮 × 标记矩阵 + 汇总」。日志只有长度、哈希与计数,永不落正文。
  • 新增 scripts/drive-session.mjs:走宿主公开的 sessionController.prompt 灌轮数,只认持久 session log 里本轮 turn/end 的 reason.kind==='completed';没有 --allow-api-route 明确授权时拒绝发送并打印等价手工请求(退出码 3)。
  • 标记表 pin 到本版本(MARKER_SET_VERSION='1.2.3'),改 lib/inject.js 的块标记必须同处更新。两个脚本都不被 lib/ import,属测量工具而非运行时能力。
  • 用法与退出码见 docs/env-and-commands.md §9.6;50 轮基线对比留给后续调参(C/D/E 的行为变化以它作度量)。

验证与迁移

  • 帮助页 FAQ 从 10 条增至 11 条(中英同步):新增「对话里为什么会主动提『那个日子已经过了』『这条可能过时了』」,把 C/D/E 三处会话侧可见行为写成人能读懂的原因,并说明三者都只是提示、不自动写入或删除;docs/client-conventions.md §7.3 记下「候选只显示 + 二次确认 + 回执数字取实值」的约定。
  • 全量 npm test(2026-11-01 实测):1277 tests / 1275 pass / 0 fail / 2 skipped(两条按设计跳过:curator 真实引擎用例、备份 venv/WAL 用例,均需 opt-in 真机环境);scripts/acceptance-local.mjs 50/50;npm run check 的 i18n / package / rpc-method-count(95) / test-tracking / loader-stubs 全部通过。
  • 新增 tests/entity-merge.test.mjs、tests/open-loop-date.test.mjs、tests/freshness.test.mjs、tests/salient-wake.test.mjs、tests/mock-llm.test.mjs、tests/settings-plumbing.test.mjs 与 tests/stubs/dsh-schedule.js;扩展 inject / injection-chain / prompts / emotion / client-render / webui-auth / activity-log 等套件。
  • 顺手修掉两处会在别人机器上闪红的地方:tests/sidebar.test.mjs 的大库首屏预算 500ms → 1500ms(守的是秒级病理形状,空载实测 378–448ms、并行整包曾到 1808ms);tests/client-render.test.mjs 的短条目过期时刻改为相对时间(曾 pin 在当天 09:30Z,下午必红)。
  • 修复一处静默失败面:openLoopRemindDays / freshnessHint / freshnessDays / salientWake / salientWakeHour / salientWakeTimeZone 必须进 setSettings allowlist 与 resetSettings key 表,否则设置页写了不生效(tests/settings-plumbing.test.mjs 拦回归)。
  • 活动日志新增 wake kind;词典生命周期等处的过期计数表述改为引用 rpc-method-count 标记,不再散写数字。
  • 无数据迁移:新设置项走默认值,唤醒默认关闭。依赖、构建步骤与网络要求均未增加。

v1.2.2

Choose a tag to compare

@github-actions github-actions released this 31 Oct 20:22

本版本把此前未单独发布的内部变更合并为正式版 1.2.2;下方「同版基线」保留原详细记录。

注入标题卡与标题提示词

  • 锚点/召回改为标题卡:formatPrivateTitle / formatGlobalTitle 只输出 [id]、标题、来源、owner、layer、日期/年龄、marginalized/open_loop、accuracy/uncertainty/disclosure 与附件计数;正文和附件 caption 不再进入 prompt。完整标签保留,imagined 不会被当成事实,never_say 不会丢失。
  • 标题就是目录:rule 移到锚点头部、所有记忆之前,并追加「标题即目录:要原文用 memu_private_recall 按标题关键词查,或直接引用方括号里的 id」。工具与抽屉仍返回全文。
  • 预算不变,容量自然扩增:anchorTokens/turnTokens/injectionTotalTokens 默认值不动,inbound 的 floor(turnBudget/2) 保留;移除 core≤5 / global≤8 的预先切片,由 packToBudget 决定条数。标题缺失回退正文前 40 字。
  • 提示词:memu_private_remember / memu_private_update 的 title、global curator 与四份私有整理稿统一要求 8–40 字、具体人名/物名/事件/结论关键词、禁止空标题;吸收/合并稿标题覆盖全部来源关键词。远程整理 JSON 契约的 title 上限同步为 8–40;四份领域正文不再包含 memu_* 工具工作流(远程整理没有工具),词典/关联的工具调用流程只留在本地 worker 任务书,领域稿仅保留“可抽什么词”的差异;修正“锚点/召唤”错字并在 <memory-context> 开头补“条目是标题卡,正文用 recall 取”。

词典自动入库与卡片管理

  • 公开生命周期改为 active / archived:模型提案与 curator 抽取普通词条直接 active,不再进人工审核;sensitive trigger 的新建/修改仍被硬拒,只有用户能管理敏感词;模型修改敏感字段不再生成 pending revision,而是 typed 拒绝。
  • legacy 迁移:启动/周期巡检把 legacy pending 新提案转 active(敏感转 archived)、pending revision 转 archived;rejected 只保留内部墓碑与 fingerprint,UI/注入/计数均隐藏。迁移只写有变化的文件。
  • RPC:删除 lexiconReview,新增 lexiconArchive / lexiconRestore;lexiconList 默认 active、支持 archived 页签与稳定 statusCounts,lexiconSearch 支持 includeArchived;方法计数 92 → 93。
  • 词典页改为卡片流:使用中/已归档 两页签、kind 筛选、搜索、新建、多选;卡片含 term 大字标题、kind/sensitive Tag、120 字释义、usage/aliases、hitCount 与 strength Bar、来源记忆跳转,以及编辑/归档或恢复/两段删除。批量归档/恢复/删除,部分成功分开显示。
  • 词典注入仍保留 term + 短义:lexiconTurnLine 短义从 120 字收紧到 80 字;锚点摘要仍只收 active top8,敏感词单列。模型提案工具描述与回执去掉 pending/人审,worker 增加 memu_lexicon_search / memu_lexicon_propose。

私有自动关联与实体枢纽

  • 算法先筛,0 token:整理结算后,对 created/updated 条目做同库、同 context/track、可见非 secret 的候选;共享稀有 name-like token、pairScore < 0.82 且无冲突时直接建立 memory↔memory relation,写 source:'auto'。
  • 实体枢纽:≥2 条记忆共享稀有 name-like token 时复用/创建实体;人物信号明确建 person,拉丁名无人物信号保守建 thing;归一化名称比较避免 Yuike/yuike 双实体。类型拿不准或已有实体 kind 冲突时转不确定候选。
  • 不确定候选交本次 worker:新增工具 memu_private_link_suggest({ids}),本地先落高置信链接/枢纽,只返回 ≤8 对候选卡;worker 用 memu_private_link 带证据裁决,不另起整理、不重读转录;每轮最多确认 5 条。lib/private.js 结算后兜底调用同一算法,失败只 warn,不影响整理成功。
  • 图元数据:linkEntries 持久化 source:'auto',getGraph 边带 source;关系图显示「自动」Tag 与移除按钮。自动关联不改正文、不写向量、不删除。worker 写工具由 2 件扩到 5 件(remember/update/link/link_suggest/lexicon_propose),读工具 3 件;探测仍要求 remember/update 才算有写能力。

帮助页 v2

  • 快速开始 5 步补「在哪里 / 成功的样子」;新增私有与全局判断链、标题卡与召回、归档/删除/撤回、向量库、私有自动关联/实体枢纽、侧边栏地图、九行标注速查表。
  • FAQ 从 6 条扩到 10 条:标题卡正文、词典自动生效/归档、自动关联撤销、备份 vs 导出 ZIP、删除对话不再整理、向量未激活回退等;中英同步。

WebUI 布局与选择器优化

  • 记忆页:记忆库选择并入「范围」行,搜索独占一行,多选/批量操作独占一行;多选按钮点击后原地变为「退出多选」,批量按钮排列在右侧。
  • 库选择器显示「库名 · 文件夹名」,同名库才追加短 id,完整路径放入悬停提示;所有 select 统一箭头、高度、圆角与焦点样式,修复 standalone 主题覆盖导致箭头消失。
  • 侧栏作用域卡改为全宽、两列等宽、不可被 flex 压缩;整理记录默认显示最新 5 条,点击标题打开 WebUI 日志页。

验证与迁移

  • 全量 npm test:1210 tests / 1208 pass / 2 skipped;scripts/acceptance-local.mjs 50/50;npm run check 静态/i18n/RPC 计数与 test-tracking 全部通过。
  • 新增 tests/private-links.test.mjs;inject/lexicon/worker/RPC/UI/help 套件同步改写;B-4/H-5 验收改为标题卡口径。
  • 依赖、构建步骤与网络要求均未增加。

同版基线:curator 三段式、全局关联与词典库

curator 三段式流水线(主动关联 · 综合整理 · 集合记忆)

  • 候选检索:倒排阻塞 + BM25 + 字符 Dice + token/entity Jaccard + 加权 RRF(k=60,权重 0.5/0.3/0.2);N>300 走倒排、>2000 截断最近并标记;候选 ≤20/提案,簇 ≤12。
  • 候选簇:全对贪心团(禁传递闭包)、medoid 代表、簇 <3 条不建议 absorb、medoid 平均 <0.55 丢簇;候选簇随 prompt 给模型(索引 4000 + 候选 2000 token 预算,超预算丢低分)。
  • 全局关联 link:curator 可输出 related/refines/supports 三种边(对称双写同 opId、有向 DFS 防环 ≤3、每端 ≤20、跨域只 related 且 rp↔coding 禁止);note 必须含会话转录逐字引用(证据落地校验,伪造/未落地不落边);幂等 opId;移除只删边不删记忆。
  • absorb 集合记忆:多条碎片合成一条集合记忆——target superseded、sources archived(不删除,可还原)、absorbSnapshot 支持一键撤销;合并稿句级去重 + 抽取/摘要双模 + 覆盖率三段(不足 skip/pending);opId 幂等;簇外重复 ≥0.90 转 pending。
  • 审核模式:curatorReviewMode 设置(默认 auto=提炼自动通过,review-all=全部进人审);硬门恒人审(conflict/无证据);rewrite 操作风险 ≥0.70 在 auto 下也暂扣;风险分与原因写到全部条目供审计。
  • 证据落地:quote 必须是所策划会话转录的规范化子串(省略号分段校验、先脱敏);无证据/未落地恒 pending 或不落库;修复"非法 merge target 继续创建"与"未知 op 默认 create"两个输入 bug。
  • 全局页/复核队列 UI:关联 Tag + 跳转 + 移除;「集合记忆 · N 条来源」Tag + 一键撤销合并;复核队列显示风险分与原因、pendingAbsorb 来源清单。

词典库(Lexicon)

  • 独立存储(data/lexicon/<libraryId>.json,CAS + 原子写 + withFileLock,容量 5000)与第 12 页卡片流管理界面(active/archived 两页签、搜索、编辑、归档/恢复、两段删除、来源记忆跳转)。
  • 注入:首轮锚点词典摘要(top 8 + 敏感触发词单列警示)+ 用户消息词面命中 B 通道(30 分钟冷却、单回合 2 条);fail-open 不阻断记忆召回。
  • 生命周期:模型/curator 普通词条直接 active;sensitive trigger 仅用户可管;归档可恢复,rejected 只作内部墓碑;legacy pending 自动迁移。
  • 备份/导出/导入含词典;RPC 方法 93 个(archive/restore),模型工具 13 个(含 lexicon search/propose 与 link_suggest);Wilson 频率先验参与排序(0.85–1.00 因子不压过匹配层级)。

修复

  • 事实级去重:写入近似门(≥0.90 拒绝 / 0.55–0.90 打标 / allow_duplicate 逃生口)+ 召回按簇折叠 + duplicateOf 降权;update 与评审接受走同一道门;长度剪枝带补探针防绕过。
  • 库级 HTTP 路由授权:31 个库寻址端点按 session 绑定精确比较或宿主 capability 回调(fail-closed),ZIP 导入先授权后读 body;修复悬空 sessionId 可写全局审核队列的真实洞。
  • 「当前已入向量库」计数跟随真实增减;四条漏戳写入路径补齐。
  • PTC(run_code)会话整理可用;挂载子集结构化上报;远程整理写入计数只数真实落库。
  • 修复"非法 merge target 继续创建"、"未知 op 默认 create"、register disposer 泄漏三个输入 bug。

界面与可观测性

  • 帮助页(第 13 页):快速上手 5 步、分区详解、FAQ 10 条、反馈三入口;无库时记忆总览页显示首次安装卡。
  • 实体删除文字化 + 孤立实体折叠区(启动扫描自动回收老版本孤立节点)。
  • 成功提示全通道补齐(保存/归档/恢复/删除/批量/导出)。

v1.2.1

Choose a tag to compare

@github-actions github-actions released this 30 Oct 20:43

事实级去重

  • 写入侧近似门:与库内既有记忆 dice ≥ 0.90 的改写直接拒绝并返回 existingId,0.55–0.90 打 duplicateOf 标签;allow_duplicate 为"同一天两次独立观测"留逃生口。门在 redactSecrets 与矛盾检测之后,同题矛盾句不会被吞。
  • 召回侧止血(对已污染的库立即生效):候选收集按簇限额折叠、跨腿融合折叠(保留 rrf 最高、落 mergedTogether)、mmrSelect 对 ≥0.82 的重合硬跳过(0.55–0.82 保留软罚);cutWeakScores 支持绝对下限 retrievalFloor(默认 0 关闭)。
  • 观测:召回活动日志记录 collapsed / clusterCount,阈值误伤事后可查。
  • 降权:带 duplicateOf 的条目召回 ×0.7,打断"重复簇越用越活"的自我强化。
  • update / 评审接受走同一道门:≥0.90 拒绝(update 零 backend commit、review 保持待复核),0.55–0.90 接受并打标;排除自身与既有 duplicateOf 宿主。
  • 长度剪枝防绕过:剪枝带内补包含与核心词全含双探针(0.92 / 0.90),"短改长"不再整对跳过;部分覆盖一律 0,宁漏勿误拒。
  • 写入计数说真话:远程整理的 count 只数真实落库,去重拦下与待复核分列进日志。

PTC(run_code)会话整理

  • header 工具表只有 run_code 不再被当作"没有写工具":PTC 的可写面在注册表里,fork 时把读写四件挂进 worker 自己的 scope(仅对该一次性 worker 可见,结束即卸载),任务书写明 await tools.memu_private_remember({...}) 调用形态。
  • 挂载元数据结构化:活动日志记 workerToolsAttached(实际子集)/ workerToolsRequested / workerToolsPartial;部分挂载不再被误报为完整挂载。
  • 修掉一个真实泄漏:register 单件抛错会丢弃已注册的 disposer,在 worker scope 留下卸不掉的常驻注册(改为逐件 fail-open)。
  • 混合工具面(原生 memu_* 与 run_code 同现)有测试钉住:任务书既列原生名单也带 SDK 调用形态。

整理提醒与日志

  • 侧边栏新增跨会话的「整理提醒」(记忆设置卡之下、整理记录之上):默认收起只显示最新一条,点击展开失败/成功两列(各最新 6 条),失败行带会话标题、原因与跳转按钮(直达对应库)。
  • 失败记录可一键清除:只删 consolidate 且 ok=false 的行,成功流水与其它日志不动;落盘成功才更新缓存。
  • WebUI 清空日志通过共享通道即时广播,侧边栏提醒同帧清空,不再等下一轮 20s 轮询。
  • 日志页整理 / 提炼行带会话标题与文件夹名,裸 id 只作回退。

记忆页与全局记忆

  • 「MemU 记忆」页按私有 / 全局分组(默认私有、私有靠前),计数分块;私有块支持多选批量归档 / 恢复 / 彻底删除(两段确认,逐条走既有 RPC,部分失败如实上报)。
  • 「全局记忆」页每条记忆新增「查看此记忆」,一键跳到 MemU 记忆页对应条目并直接打开抽屉。

关系图

  • 永久删除记忆级联清理:同事务剔除指向它的入边,删除后回收孤儿实体;归档(软删)不级联,还原后边还在——边界有测试钉住。
  • 新增 deleteEntity:仍被引用时默认拒绝并说明引用数,force 连同入边与关系快照一起删(先清索引后删行);图谱实体行带删除入口。
  • 悬空边策略统一:getGraph 占位与 recallRelations 都标 missing: true,不再一边占位一边静默丢弃。
  • 恢复备份后自动回收孤儿实体;插件启动延迟 4 秒全库扫描老版本遗留的孤立节点(幂等、健康数据零写入、失败只 warn),新安装为 no-op。

本地向量

  • 「当前已入向量库 N 条」跟随真实增减:迁移账本(扣除已删除)∪ 当前代际写入戳合成,切换后新写入实时计入、永久删除即扣、回滚代际不串空间。
  • 四条漏戳的写入路径补齐(批量导入 / 评审接受 / 编辑重提交 / 全局整理),此后"进没进向量库"每条都有凭证。

修复

  • held-clone 虚假向量戳:整理冲突产生的 pending_review 克隆不再继承从未属于它的 entry.vector,membership 不再虚计。
  • curator 提交成功后的索引失败现在逐条 forget 补偿(尽力而为且逐条可见:compensated / failed 进活动日志);标注为补偿设计而非事务。
  • registerTools 单件抛错不再丢弃已注册的 disposer(worker scope 常驻注册泄漏)。
  • 整理 worker 的工具挂载在宿主不暴露 ctx 时如实上报(attached=null + partial),不再误报为已挂全。
  • 事实级去重同口径盖住 update / review 后,远程整理写入计数同步修正(见上)。

v1.2.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 14:33

情绪模型(四项构成 + 双辅助轴)

  • readMood 从单一 valence 扩展为四项构成(joy/anger/sorrow/fear,和为 1)+ arousal(强度)+ dominance(掌控感);返回值继续带 valence,旧调用不受影响。
  • 复合情绪不再是新维度,而是单纯形上的具名点:委屈/尴尬/嫉妒/心疼/不甘各有明确词条与默认掌控感。
  • 召回新增 overlap 一致性路径(±30%)与 dominance 乘数(±10%),旧条目无 emotion 时回落原 valence 路径,不反推四轴。
  • 门控改为三条独立条件的 OR:主导项 share ≥ 0.35、|arousal − 0.35| ≥ 0.1、|dominance| ≥ 0.25。
  • 写入区分三种语义:省略=继承会话心情,null=明确中性,对象=模型读数;emotionSource 记录来源(model/session/content/unknown)。
  • 后台整理 fork 显式不继承父会话心境,四份整理提示词要求按事件所在回合还原情绪。
  • 修掉派生 valence 把 anger 记为正、emotion: null 被继承覆盖、dominanceBias(null) 返回 1.05 三处偏差。

主动记忆 A/B 数据集

  • 新增 4 个规模梯度 case(98 / 998 / 4998 / 19998 事件),用既有 padding 生成器产出,零手写事件;数据集 10 → 14 个 case。
  • 梯度暴露了一个此前被 fixture 规模掩盖的事实:guide-weighted 的覆盖在阈值以上低于 legacy(11/20 vs 14/20),守卫在 2000 事件处把薄回合的覆盖换成 seed 成本(19,998 事件一次 fork 约 32k token → 0)。
  • 新增 guide-weighted:structural 行,用 lib/consolidation-receipt.js 的 parseReceipt 把 C 包回执映射成凭证;14 个 case 与 oracle 行逐条一致。
  • 真实模型运行器的 dry-run 计划从 20 个请求变为 28 个(14 case × 2 臂)。

v1.1.3

Choose a tag to compare

@github-actions github-actions released this 29 Oct 21:49

本地向量(受管)

  • 重建提交改用 MemU 的 recall_files 信封,修复"每条都 VECTOR_READBACK_FAILED、整次重建作废"的根因。
  • 重建进度独立成块:明确区分准备中 / 写入向量 / 已切换 / 失败 / 已停止;失败时列出 itemId · code · message。
  • 切换成功后清空"待切换"标记;旧标记 {status:'active'} 兼容读取,不再同时显示"当前生效 + 待切换"。
  • 记忆详情显示向量归属:新写入带模型与代际,迁移条目按账本显示"已入向量库(迁移)"。
  • 列表接口在向量启用后直接读索引:归档 / 还原与记忆墙刷新从秒级降到毫秒级。
  • 未激活向量时,写入 / 列表 / 全局列表降级为索引 + 引擎,不再抛 VECTOR_NOT_ACTIVE 卡死整理。

记忆

  • 模型把日期写进正文但漏填 salient_date 时,从唯一的 YYYY-MM-DD 兜底;出现多个日期则不猜。
  • 默认 privateTopK / globalTopK 调整为 5 / 5。

可观测性与界面

  • 新增活动日志(召回 / 写入 / 整理 / 提炼 / 向量)与「日志」页:筛选、复制、下载、定位记忆。
  • 侧边栏整理结果改为按钮下方的「整理记录」,不再在顶部显示 fork 提示;作用域卡片下移。
  • 「连接与向量」重做为分步卡片:运行环境 → 模型 → 切换并重建,主操作更醒目。
  • 修复日志页 hook 顺序导致的设置 / WebUI 白屏(React #310)。

工程

  • Windows 子进程补齐 SystemDrive / SystemRoot / windir,避免字面量 %SystemDrive% 目录。
  • manifestDigest 只覆盖制品与向量空间字段,改说明文字不再判废已激活的向量空间。
  • 新增链路矩阵 npm run test:chain(3 后端 × 3 触发 + 降级,10/10);测试总数 702 → 761。

v1.1.2

Choose a tag to compare

@github-actions github-actions released this 28 Oct 22:07

Full Changelog: v1.1.1...v1.1.2

v1.1.1

Choose a tag to compare

@github-actions github-actions released this 28 Oct 21:03

Full Changelog: v1.1.0...v1.1.1

1.1.0 — injection chain, date semantics, docs split

Choose a tag to compare

@Tkingxiao Tkingxiao released this 28 Oct 16:56

1.1.0

The injection chain — one dead binding no longer blinds the conversation

  • A conversation created without a memory library used to trigger the private index load anyway, the throw was swallowed, and the whole injection was lost for that turn — the model saw only the anchor and the sidebar recorded injection failed: invalid library id. The load is now skipped when there is no library to read.
  • Recall now guards on the library id too, and says this conversation has no memory library instead of the misleading no bound session.
  • A new conversation now adopts the folder's existing library instead of creating a second one, so the memories written in one conversation are visible in the next. Archived libraries stay archived; adoption is per folder.

Dates — "tomorrow" stops becoming a yearly reminder

  • A relative day (今天 / 明天 / 后天 / today / tomorrow) is resolved to a dated salient_date at write time, unless the text pins an absolute date. Before this, 明天要去考驾照 was stored as 10-29 and came back every year.
  • The same resolution now covers memory update and import, not just remember — so fixing a bad date no longer re-creates it.
  • The consolidation prompts finally document salient_date, and the memu_private_remember schema warns that a bare MM-DD recurs yearly.
  • Existing entries are untouched. A stored MM-DD still means "anniversary" — that is the documented rule, and it is what makes birthdays work.

Docs — the manual moved, the index got small

  • AGENTS.md went from 58 KB to 6 KB and is now an index that answers "which file". The manual moved to docs/ with every section number unchanged, so external references like §8.4 still resolve.
  • The host silently truncates the instruction file at 65536 bytes, so npm run check now fails above 60000. docs/ is outside that budget.
  • Acceptance G-1 now asserts that every doc pointer in the index resolves, that nothing points at a retired document, and that the ten hard rules match between the index and the appendix that carries them in full.

Upgrading

No migration: existing ~/.dsh/dsh-memu-memory data keeps working. Back up the directory first if you store memories you cannot re-create.

Published from this Release by the repository's trusted-publishing workflow, so the npm tarball carries a provenance attestation.