diff --git a/README.md b/README.md index a9e8c3f..b3998e2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ license CI node - tests + tests coverage Awesome

@@ -31,15 +31,9 @@ | 日期 | 版本 | 头条 | |------|------|------| -| 2026-10-17 | **v0.9.0** | **M.O. beta** — 你的工作流不再住在记忆库的一行文字里,而是住进项目仓库的 `.mneme/pipelines/.json`:能提交、能 diff、能 PR、能被 CI 读。面板画的图和 agent 读的是同一份数据。配套「候选分层」:草稿先在 `/candidates/` 攒着,走过两次**且**写得出反事实才提议升格——**升格不是降低标准,是补齐标准**。 | -| 2026-10-17 | **v0.9.0** | **设置面板重排**([#427](https://github.com/slow-stack/mneme/issues/427))— 折叠线从「冷门」挪到「新手之外」:首屏只留 3 个总开关 + 作用域组,其余整组收进「全部设置」;22 个条目名从 `bm25SearchEnabled` 改说人话,并标注「默认 / 已改」。 | -| 2026-10-17 | 加固 | **安全审计第一批**([#462](https://github.com/slow-stack/mneme/pull/462))— 外部访问 API 的 39 条路由全部改成**声明式鉴权**:漏写策略在装配期直接抛错,而不是运行时静默放行;`/export` 与 `GET /external-api` 补上鉴权门(此前未鉴权即可导出含归档/已遗忘的整库、并就地铸造 token);请求体加 4 MiB 上限。 | -| 2026-10-17 | 加固 | **路由策略重构**([#464](https://github.com/slow-stack/mneme/pull/464))— 鉴权从「每个 handler 自己记得写」变成「路由注册的必填字段」,23 处手写校验归零。供应链侧同步收紧:OSV 扫描改为有漏洞即失败、npm 发布带 `--provenance` 签名、secret scanning 与 Dependabot 打开。 | -| 2026-10-17 | 结案 | **一条 secret scanning 告警**([#465](https://github.com/slow-stack/mneme/pull/465))— 报告说「存在明文 token 泄露」,核实是测试语料里刻意构造的形状样本(`sk-proj-` + 顺序字母数字)。修法不是删告警,是**给语料加一道锁**:`test/helpers/` 下任何完整形状的凭据字面量都会让测试变红,且与生产扫描器的规则双向对齐。 | -| 2026-10-18 | **v0.9.1** | **M.O. 从「看得见」跑到「跑得出账」** — 工作流不再只有一张图:`/mo-run start ` 与 `/mo-run stop` 由人划出执行窗口,一次 run 落到 `<项目>/.mneme/pipelines/.runs.jsonl`;归属只出候选(单条命令唯一归属实测 14.2%,所以「认不出来」是合法答案);P1 回执列业务动作、P2 人确认才写账、P3 把「人说做了」与「观察里一次都没出现」的矛盾摆出来让人判。**机器从不替你下结论。** | -| 2026-10-18 | **v0.9.1** | **人格定义页 v1.2**(beta,默认关)— 从一张卡变成按 agent 寻址的多张卡:设置键是 `{default, byAgent}`,v1 的单对象读作 `default`、零迁移;注入按会话身份寻址对应 agent 的卡,身份缺失落 `default`。 | -| 2026-10-19 | **v0.9.2** | **让「看不见的失败」显形** — 这一版修的大多不是「缺功能」,是「坏了却不报」:功能开关一直是启动期生效,面板「已改」只说明值进了库,于是新增第三态「待重启」([#481](https://github.com/slow-stack/mneme/pull/481));巩固的 `dreamMaxTokens` 默认值被原样当 `max_tokens` 发出去,撞上服务商上限后 180 次巩固 0 次成功,现在面板能改、超上限还会按服务商自己声明的上限自动降级([#478](https://github.com/slow-stack/mneme/issues/478));自动蒸馏写入的行 100% 拿不到向量,注释承诺的「提交后补跑」从未存在([#477](https://github.com/slow-stack/mneme/pull/477));CLI 把 `--` 开头的值当成新旗标——这个缺陷只在 token 前两位恰好掷中 `-`(约 1/4096)时现形,潜伏了整整一个发布周期。另新增作用域级可逆闸门([#471](https://github.com/slow-stack/mneme/issues/471),默认关):让一路记忆能被单独按下与松开,不改配置、不重启。 | +| 2026-10-21 | **v0.9.4** | **三条链各自补上「最后一环」** — 这一版的三件事同一种形状:东西早就造好了,只差没人能叫它。① **agent 主动整理**([#231](https://github.com/slow-stack/mneme/issues/231)):引擎在 v0.8 就落在 `src/organize.js`(先 dryRun 出比对报告 → agent 判断 → apply 落库,筛除只归档不删,全程复用 `dream_runs` 回执),可它**没有任何调用入口**——工具表里没有它、两张 API 脸 0 命中、全仓 `.organize(` 零调用点,只有测试能调到。现在它有工具了:`memory_organize`,`organizeEnabled` 默认关(关 = 工具根本不注册,模型看不到就不会调)。② **画像能被反向提取了**([#497](https://github.com/slow-stack/mneme/issues/497)):面板「用户画像」加一个按钮,读活跃记忆、花一次宿主模型调用,产一段「助手眼中的用户」**草稿**——**只出草稿**:采用是你在面板上再点一次,你手写的原文不会被模型改写;空库和模型不配合都直接报错,绝不回一张空草稿冒充「这次没提炼出东西」(`profileExtractEnabled` 默认关)。③ **人格不再无条件进别人的会话**:`default` 卡以前会落给所有没建卡的身份,于是宿主里自带身份的外部 bot 每一帧都收到两句「你是谁」撞在一起,而界面上「关掉这张卡」实际不生效——新增 `personaStrictAddressing`(默认关,历史行为逐字节不变)把「身份没建卡」与「卡被显式关掉」都收回到不注入。同版补上蒸馏的**转录围栏 + 解析失败护栏**([#494](https://github.com/slow-stack/mneme/pull/494):一条会话被同一个窗口卡死、重试 20 次越试越贵的根因;失败行现在恒带原始输出形状,事后能回答「模型到底吐了什么」),并把面板里那段自称「注入生效文本」的只读区改成由后端渲染——它以前既没有人格三行也没有表头和前缀,标题却承诺了模型实际看到的东西。 | | 2026-10-20 | **v0.9.3** | **M.O. 从「挖得出来」跑到「会被叫去挖」** — 沉淀链的算法自 v0.9.1 起就写完了(多帧 zstd → 重复序列 → 缺席对照 → draft 草稿),可它在运行时**没有任何调用点**:全仓引用只出现在测试里。机器挖得出来、却永远不会被叫去挖——缺的从来不是算法,是**归属**与**触发**。这一版把三处接上:按会话头里的 `cwd` 反查项目桶(桶名是路径的**有损**变形,规则不在本仓,查不到就整跑跳过,而不是降级成全局普查——后者会把 A 项目的动作算到 B 头上,且结果看起来完全正常);新增 `nudgeForProject` 把「归属 → 挖掘 → 落盘」收成一个入口;挂 `turn/end` 按项目跑,冷却 10 分钟、单次预算 4 秒、延到宏任务、一次一个项目、抛错只 `warn`。**它只产提议,不产草稿**([#489](https://github.com/slow-stack/mneme/pull/489)):升格仍然要人批,面板也多了「现在沉淀一次」,与自动跑同一条入口、区别只有不改仓库。同版修掉三处「坏了却不报」:作用域反查自 v0.8.0 起每次都抛错走降级、canonical 归一与成员校验静默失效([#487](https://github.com/slow-stack/mneme/issues/487));`/nudge` 的写盘口只拦 `PUT`/`POST`,带 token 的 `PATCH`/`DELETE` 会静默写盘、归属解析不看预算、桶名边收边截把「没看全」报成「确实没有」;多帧 zstd 的反证断言绑了 Node 的整块解语义,24.22.0 起过期([#490](https://github.com/slow-stack/mneme/pull/490))。 | +| 2026-10-19 | **v0.9.2** | **让「看不见的失败」显形** — 这一版修的大多不是「缺功能」,是「坏了却不报」:功能开关一直是启动期生效,面板「已改」只说明值进了库,于是新增第三态「待重启」([#481](https://github.com/slow-stack/mneme/pull/481));巩固的 `dreamMaxTokens` 默认值被原样当 `max_tokens` 发出去,撞上服务商上限后 180 次巩固 0 次成功,现在面板能改、超上限还会按服务商自己声明的上限自动降级([#478](https://github.com/slow-stack/mneme/issues/478));自动蒸馏写入的行 100% 拿不到向量,注释承诺的「提交后补跑」从未存在([#477](https://github.com/slow-stack/mneme/pull/477));CLI 把 `--` 开头的值当成新旗标——这个缺陷只在 token 前两位恰好掷中 `-`(约 1/4096)时现形,潜伏了整整一个发布周期。另新增作用域级可逆闸门([#471](https://github.com/slow-stack/mneme/issues/471),默认关):让一路记忆能被单独按下与松开,不改配置、不重启。 | > 只记大改动,逐条细节见 [CHANGELOG](dsh-mneme/CHANGELOG.md)。 @@ -265,7 +259,7 @@ dsh-mneme-serve # 默认 ~/.dsh/memory + 127.0.0.1:8790 | **v0.6** | 会话生命周期:删对话 ≠ 删记忆 | ✅ | | **v0.7** | 自进化记忆:热度衰减 + 睡眠双保护 + 桌面端工作台/功能开关 | ✅ | | **v0.8** | 作用域隔离(agent/workspace 双维隔离 + 检索加权 + opt-in 硬过滤)+ 冲突队列人工裁决 + 归属显式声明 + 生态化(stdio MCP server / 图召回轴 / 冷启动 / 注入截断与状态条 / 蒸馏可靠性 / 注入形态与 agent 主动整理接口 / 配置说明与 issue 模板) | ✅ 已发布(至 v0.8.16) | -| **v0.9** | M.O.(Modus Operandi)beta:项目级工作流本体 + 候选分层与升格判据 + 面板清单/图同屏;设置面板按「新手之外」重划折叠线(#427);安全审计与供应链加固(声明式路由鉴权、OSV 阻断、发布签名);**v0.9.1 补上写侧**:执行窗口、运行账、归属候选、观察旁挂与脱节检出、步骤 `assert`/`optional`、沉淀链草稿挖掘、人格定义页(按 agent 多卡);**v0.9.2 让看不见的失败显形**:功能开关第三态「待重启」、作用域级可逆闸门(#471)、巩固 `max_tokens` 超上限自动降级(#478);**v0.9.3 接通自动沉淀**:按会话头 `cwd` 反查项目桶、挂 `turn/end`、冷却 10 分钟、单次预算 4 秒,只产提议不产草稿(#489),并修作用域反查自 v0.8.0 起静默降级(#487)、`/nudge` 写盘口漏拦 `PATCH`/`DELETE`、多帧 zstd 反证断言绑 Node 整块解语义(#490) | ✅ 已发布(v0.9.3) | +| **v0.9** | M.O.(Modus Operandi)beta:项目级工作流本体 + 候选分层与升格判据 + 面板清单/图同屏;设置面板按「新手之外」重划折叠线(#427);安全审计与供应链加固(声明式路由鉴权、OSV 阻断、发布签名);**v0.9.1 补上写侧**:执行窗口、运行账、归属候选、观察旁挂与脱节检出、步骤 `assert`/`optional`、沉淀链草稿挖掘、人格定义页(按 agent 多卡);**v0.9.2 让看不见的失败显形**:功能开关第三态「待重启」、作用域级可逆闸门(#471)、巩固 `max_tokens` 超上限自动降级(#478);**v0.9.3 接通自动沉淀**:按会话头 `cwd` 反查项目桶、挂 `turn/end`、冷却 10 分钟、单次预算 4 秒,只产提议不产草稿(#489),并修作用域反查自 v0.8.0 起静默降级(#487)、`/nudge` 写盘口漏拦 `PATCH`/`DELETE`、多帧 zstd 反证断言绑 Node 整块解语义(#490);**v0.9.4 补上三条链的最后一环**:agent 主动整理接口有了工具(`memory_organize`,`organizeEnabled` 默认关,关时模型根本看不到它)、画像可以从记忆提取**草稿**(`profileExtractEnabled` 默认关,只出草稿、采用是用户再点一次,手写的原文不被模型改写)、人格 `default` 卡不再无条件进「没建卡」的身份(#497,`personaStrictAddressing` 默认关),面板那段「注入生效文本」改成由后端单源渲染,另补蒸馏转录围栏与解析失败护栏(#494) | ✅ 已发布(v0.9.4) | > 完整逐小版本说明见 [CHANGELOG](dsh-mneme/CHANGELOG.md)。 @@ -273,7 +267,7 @@ dsh-mneme-serve # 默认 ~/.dsh/memory + 127.0.0.1:8790 ```bash cd dsh-mneme && npm install -npm test # 2257 个测试 +npm test # 2302 个测试 npm run stress # 三轴线压测 npm run sync # src → lib 同步 ``` @@ -314,15 +308,9 @@ dsh-mneme 的机制设计与下列公开研究相互印证(以下引用均逐 | Date | Version | Headline | |------|---------|----------| -| 2026-10-17 | **v0.9.0** | **M.O. (modus operandi) beta** — how you get things done no longer lives as a line of prose in the memory store; it lives in the project repo as `.mneme/pipelines/.json`: commit it, diff it, review it, let CI read it. The panel draws from the same data the agent reads. Paired with **candidate tiering**: drafts sit in `/candidates/` until they've been walked twice **and** can be stated as a counterfactual — **promotion raises the bar, it does not lower it**. | -| 2026-10-17 | **v0.9.0** | **Settings panel rework** ([#427](https://github.com/slow-stack/mneme/issues/427)) — the fold line moved from "rarely used" to "beyond the newcomer": the first screen keeps 3 master switches plus the scope group, everything else collapses into "all settings"; 22 labels now say what they do instead of naming a config key, tagged default / changed. | -| 2026-10-17 | Hardening | **Security audit, batch one** ([#462](https://github.com/slow-stack/mneme/pull/462)) — all 39 routes on the external API now carry **declarative auth**: a missing policy throws at wiring time instead of failing open at runtime; `/export` and `GET /external-api` got their missing gate (they previously let an unauthenticated caller dump the whole store including archived and forgotten rows, and mint a token on the spot); request bodies capped at 4 MiB. | -| 2026-10-17 | Hardening | **Route policy refactor** ([#464](https://github.com/slow-stack/mneme/pull/464)) — auth moved from "every handler remembers to check" to "a required field on route registration", retiring 23 hand-written checks. Supply chain tightened alongside: OSV now fails the build on findings, npm publishes carry `--provenance`, secret scanning and Dependabot are on. | -| 2026-10-17 | Closed | **One secret-scanning alert** ([#465](https://github.com/slow-stack/mneme/pull/465)) — reported as "a plaintext token leak", verified as a deliberately shaped test sample (`sk-proj-` plus sequential letters and digits). The fix was not to silence the alert but to **lock the corpus**: any complete credential shape under `test/helpers/` now turns the suite red, and the guard is cross-checked against the production scanner in both directions. | -| 2026-10-18 | **v0.9.1** | **M.O. goes from “visible” to “accounted for”** — workflows are more than a diagram now: `/mo-run start ` and `/mo-run stop` let a person draw the execution window, and a run is journaled to `/.mneme/pipelines/.runs.jsonl`. Attribution only produces candidates (exact single-command attribution measured at 14.2%, so “no idea” is a legitimate answer); P1 reports the business actions seen, P2 writes the ledger only after a human confirms, and P3 puts “the human said it ran” against “the observation never saw it” for the human to settle. **The machine never concludes for you.** | -| 2026-10-18 | **v0.9.1** | **Persona page v1.2** (beta, off by default) — one card becomes per-agent cards: the setting is `{default, byAgent}`, a v1 single object reads as `default` with zero migration, and injection resolves the card by session identity, falling back to `default`. | -| 2026-10-19 | **v0.9.2** | **Making silent failures visible** — most of what this release fixes isn't a missing feature, it's something broken that never said so. Feature flags have always taken effect at startup, so a lit "changed" badge only meant the value reached the database; there's now a third state, **pending restart** ([#481](https://github.com/slow-stack/mneme/pull/481)). The default `dreamMaxTokens` was sent verbatim as `max_tokens` and blew past provider caps: **180 consolidations, 0 successes** over eight days — the panel can now edit it, and an over-cap request retries once at the limit the provider itself declares ([#478](https://github.com/slow-stack/mneme/issues/478)). Rows written by auto-distillation got no vector at all; the "deferred to commit" the comment promised never existed ([#477](https://github.com/slow-stack/mneme/pull/477)). And the CLI treated values starting with `--` as new flags — a defect that only surfaces when a random token happens to begin with `-` (about 1/4096), which is why it hid for a whole release cycle. Also new: a **scope-level reversible gate** ([#471](https://github.com/slow-stack/mneme/issues/471), off by default) that pauses one scope without editing config or restarting. | +| 2026-10-21 | **v0.9.4** | **Three chains each get their missing last link** — the three items in this release share one shape: the thing was already built, nothing could call it. ① **Agent-driven organizing** ([#231](https://github.com/slow-stack/mneme/issues/231)): the engine landed in `src/organize.js` back in v0.8 (dryRun comparison report → the agent decides → apply persists, screening out means archive rather than delete, every step leaving a `dream_runs` receipt), but it had **no entry point at all** — absent from the tool table, zero hits on both API surfaces, zero `.organize(` call sites in the repo; only its own tests could reach it. Now it has a tool: `memory_organize`, behind `organizeEnabled`, off by default (off = the tool is not even registered, so a model that cannot see it never calls it). ② **The profile can finally be derived** ([#497](https://github.com/slow-stack/mneme/issues/497)): the panel's profile card gained a button that reads the active memories, spends one host model call and returns an "as the assistant sees them" **draft** — **a draft only**: applying it is another click you make, and your hand-written text is never rewritten by the model; an empty store or a misbehaving model is an error, never an empty draft masquerading as "nothing to extract this time" (`profileExtractEnabled`, off by default). ③ **Personas no longer leak into someone else's session**: the `default` card used to be handed to every identity without a card, so a third-party bot running in the same host — one that carries its own identity — received two "you are ..." claims in every turn, while "switch this card off" in the panel changed nothing; the new `personaStrictAddressing` (off by default, existing behaviour byte-identical) reclaims both "no card for this identity" and "card explicitly switched off" as inject-nothing. The same release adds the **transcript fence and the parse-failure guard** for distillation ([#494](https://github.com/slow-stack/mneme/pull/494): the root cause of one session being stuck on the same window, retried 20 times and growing more expensive each time; failed rows now always carry the raw output shape, so "what did the model actually print" is answerable afterwards), and the panel's read-only card that called itself "injected text" is now rendered by the backend — it used to show neither the persona lines nor the header and prefix it claimed to mirror. | | 2026-10-20 | **v0.9.3** | **M.O. goes from “diggable” to “actually summoned to dig”** — the distillation chain has been fully implemented since v0.9.1 (multi-frame zstd → repeated sequences → absence contrast → draft workflows), yet it had **no call site at runtime**: every reference in the repo lived in tests. The machine could dig, but nothing ever called it. What was missing was never the algorithm — it was **attribution** and **triggering**. This release wires up three things: resolving a project's session bucket from the `cwd` in the session header (bucket names are a **lossy** transform of paths whose rule lives outside this repo; when the lookup fails the whole run is skipped rather than degraded into a global sweep — the latter attributes project A's actions to project B, and the result looks perfectly normal); a new `nudgeForProject` that folds “attribute → mine → persist” into one entry point; and a `turn/end` hook that runs per project with a 10-minute cooldown, a 4-second budget, deferred to a macrotask, one project at a time, errors only warned. **It produces proposals, never drafts** ([#489](https://github.com/slow-stack/mneme/pull/489)): promotion still needs a human, and the panel gained a "distill now" button on the same entry point, differing only in that it does not touch the repo. The same version fixes three “broken but silent” defects: the scope reverse-lookup that had been throwing and silently degrading on every call since v0.8.0, disabling canonical normalization and membership checks ([#487](https://github.com/slow-stack/mneme/issues/487)); `/nudge`'s write gate, which only intercepted `PUT`/`POST` so a token-bearing `PATCH`/`DELETE` persisted proposals silently, plus attribution resolution ignoring the budget and bucket names being truncated while being collected — reporting “did not look at everything” as “there is genuinely nothing”; and a multi-frame zstd counter-assertion bound to Node's whole-buffer decode semantics, expired from 24.22.0 ([#490](https://github.com/slow-stack/mneme/pull/490)). | +| 2026-10-19 | **v0.9.2** | **Making silent failures visible** — most of what this release fixes isn't a missing feature, it's something broken that never said so. Feature flags have always taken effect at startup, so a lit "changed" badge only meant the value reached the database; there's now a third state, **pending restart** ([#481](https://github.com/slow-stack/mneme/pull/481)). The default `dreamMaxTokens` was sent verbatim as `max_tokens` and blew past provider caps: **180 consolidations, 0 successes** over eight days — the panel can now edit it, and an over-cap request retries once at the limit the provider itself declares ([#478](https://github.com/slow-stack/mneme/issues/478)). Rows written by auto-distillation got no vector at all; the "deferred to commit" the comment promised never existed ([#477](https://github.com/slow-stack/mneme/pull/477)). And the CLI treated values starting with `--` as new flags — a defect that only surfaces when a random token happens to begin with `-` (about 1/4096), which is why it hid for a whole release cycle. Also new: a **scope-level reversible gate** ([#471](https://github.com/slow-stack/mneme/issues/471), off by default) that pauses one scope without editing config or restarting. | > Headlines only; see the [CHANGELOG](dsh-mneme/CHANGELOG.md) for the full list. @@ -548,7 +536,7 @@ Community projects that wire mneme into more platforms — not part of this repo | **v0.6** | Session lifecycle: delete session ≠ delete memory | ✅ | | **v0.7** | Self-evolving memory: heat decay + sleep dual-protection + desktop workbench/feature toggles | ✅ | | **v0.8** | Scope isolation (agent/workspace stamping + retrieval weighting + opt-in hard filter) + conflict review queue + explicit attribution + ecosystem (stdio MCP server / graph recall axis / cold-start bootstrap / injection truncation & status bar / distill reliability / injection shaping & agent-driven organize / configuration reference & issue templates) | ✅ Released (up to v0.8.16) | -| **v0.9** | M.O. (Modus Operandi) beta: project-level workflows + candidate tiering & promotion criteria + panel list-and-graph view; settings panel re-folded at "beyond the newcomer" (#427); security & supply-chain hardening (declarative route auth, OSV gate, signed releases); **v0.9.1 adds the write side**: execution windows, run ledger, attribution candidates, observation sidecar with drift detection, step `assert`/`optional`, draft mining from session logs, persona page (per-agent cards); **v0.9.2 makes silent failures visible**: feature-flag third state "pending restart", scope-level reversible gate (#471), automatic max_tokens downgrade for consolidation (#478); **v0.9.3 wires up auto-distillation**: resolves the project bucket from the session header's `cwd`, hooks `turn/end`, 10-minute cooldown, 4-second budget, proposals only and never drafts (#489), and fixes the scope reverse-lookup that had been silently degrading since v0.8.0 (#487), the `/nudge` write gate missing `PATCH`/`DELETE`, and a multi-frame zstd counter-assertion bound to Node's whole-buffer decode semantics (#490) | ✅ Released (v0.9.3) | +| **v0.9** | M.O. (Modus Operandi) beta: project-level workflows + candidate tiering & promotion criteria + panel list-and-graph view; settings panel re-folded at "beyond the newcomer" (#427); security & supply-chain hardening (declarative route auth, OSV gate, signed releases); **v0.9.1 adds the write side**: execution windows, run ledger, attribution candidates, observation sidecar with drift detection, step `assert`/`optional`, draft mining from session logs, persona page (per-agent cards); **v0.9.2 makes silent failures visible**: feature-flag third state "pending restart", scope-level reversible gate (#471), automatic max_tokens downgrade for consolidation (#478); **v0.9.3 wires up auto-distillation**: resolves the project bucket from the session header's `cwd`, hooks `turn/end`, 10-minute cooldown, 4-second budget, proposals only and never drafts (#489), and fixes the scope reverse-lookup that had been silently degrading since v0.8.0 (#487), the `/nudge` write gate missing `PATCH`/`DELETE`, and a multi-frame zstd counter-assertion bound to Node's whole-buffer decode semantics (#490); **v0.9.4 supplies the missing last link on three chains**: the agent-driven organizing interface finally gets a tool (`memory_organize`, behind `organizeEnabled`, off by default — off means the model cannot even see it), the profile can be drafted from memory (`profileExtractEnabled`, off by default; a draft only — applying it is another user action, and hand-written text is never rewritten by the model), and the persona `default` card is no longer handed to identities that simply have no card (#497, `personaStrictAddressing`, off by default); the panel's "injected text" card is now rendered by a single backend source, plus the distillation transcript fence and parse-failure guard (#494) | ✅ Released (v0.9.4) | > Full per-minor-version changelog in [CHANGELOG](dsh-mneme/CHANGELOG.md). @@ -556,7 +544,7 @@ Community projects that wire mneme into more platforms — not part of this repo ```bash cd dsh-mneme && npm install -npm test # 2257 tests +npm test # 2302 tests npm run stress # three-axis stress test npm run sync # src → lib sync ``` diff --git a/dsh-mneme/CHANGELOG.md b/dsh-mneme/CHANGELOG.md index eb25ef8..c0a62a5 100644 --- a/dsh-mneme/CHANGELOG.md +++ b/dsh-mneme/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased] +## [0.9.4] - 2026-10-21 + ## 🆕 新增 - **agent 主动整理接口接上调用入口:`memory_organize` 工具 + `organizeEnabled`(#231,默认关)**:引擎在 v0.8 就造好了(`src/organize.js`:dryRun 出比对报告 → agent 判断 → apply 落库,筛除只归档不删,全程复用 `dream_runs` 的回执语义),但当时刻意「不进工具列表、不加独立开关」,于是它一直是**只有测试能调用**的功能——`tools.js` 的十一个工具里没有它,两张 API 脸 0 命中,全仓 `.organize(` 零调用点。这一批接的是油门:新工具 `memory_organize`(`mode: dryRun|apply`,缺省 dryRun)+ 顶层扁平开关 `organizeEnabled`(默认 `false`)。**默认关与「关 = 不注册」是同一件事的两面**:开关关掉时工具根本不进注册表,模型看不到就不会调——沿用 v0.8.5 工具暴露闸的定性(比在描述里劝模型别用更可靠);轻量档预设也关,但理由不是成本而是协议:dryRun→apply 要求模型拿住上一轮的 `run_id` 再回来,弱模型漏这一环就是一次被拒的调用。预设不剥夺显式开启的权利(合并顺序「用户开关 > 轻量预设 > bundle」,同 `continuityRescueEnabled`)。**补上 `opts.hiddenIds` 这个零生产者的可信上下文**:`organize.apply` 收它、却从来没人传(工具没接,自然没有生产者)——不接就等于让整理接口成为第二条绕过 scope 治理的改库通道。现在由工具层按 #170 复核项 4 的口径算好传入(`register_document` 的 `hiddenEvidenceIds` 同形):他 scope 的 explicit 行按「不存在」处理,回执文案维持「unknown, archived or out of scope」那一句,不泄漏存在性。**同一件事还有报告那一半**:比对只看三列**值**(不看 `*_scope_source`、不调 `isVisibleInScope`),而 `agent_scope` 是模型自己给的参数,所以把报告原样回给模型就等于开了一个探测口——「那一档有没有这个标题」,拿到 `id` 与 `title` 就是答案。现在落点先过一遍调用方可见性:看不见的行按「不存在」说(`verdict: new`、不带 `match`),`counts` 跟着重算——留着引擎那份计数,`exact: 1` 配一个不带落点的条目本身就是存在性 oracle,等于把泄露从 id 挪到计数器。审计行仍存原始落点,那是给操作者看的证据面,不是给模型的读数。经整理落库的候选与 `memory_save` 同源盖章(会话身份 `auto`、显式参数改盖 `explicit`),否则这条路写出来的行会全部躺在未标注(全局可见)那一档。测试 +13(`test/organize-tool.test.js`):默认不注册 / 开启后注册、lightMode 预设值、dryRun 一行不写只留回执、apply 三态计数、拒绝没有报告的 apply、拒绝缺 `run_id`、未知 mode、scope 盖章两例、存在性泄漏的**正反两向**(`strictScope` 关时照常给出落点——修安全问题不许顺带改成少召回)、落地侧 out-of-scope 一例。两条新守卫各做过变异自证:摘掉可见性过滤 → 只有泄露那条红;`counts` 换回引擎那份 → 只有计数断言红(`actual 0, expected 1`)。`hiddenIds` 那条**第一版是错的**:最初拿「别人 scope 的行」测,可那行永远不会成为报告的命中行,把守卫整段摘掉测试照样绿;换成「同比对键但会话看不见」那一格才咬得住——而写错的那一版还把泄露本身钉成了期望值。#195 的工具矩阵两个分支都补了(清单比对与 C 层结构断言改用暴露闸全开的配置——opt-in 工具在默认配置下不在注册表里,用默认配置跑那条护栏会静默失效)、`tools.test.js` 的 schema 子集加走一遍暴露配置、`api.test.js` 旗标计数锁 103→104、`docs/CONFIGURATION.md` 补键与轻量档清单。**本批刻意不含**:注入侧的「能力说明」段落(属 #249 剩下的另一半,本批唯一表面是工具描述),以及面板上 `run_type='organize'` 回执的展示口径。 @@ -10,7 +12,7 @@ ## 🐛 修复 -- **人格的 `default` 卡会进所有没建卡的身份,面板的「注入生效文本」又不是生效文本(#497)**:两件事同源——寻址与渲染各有一份实现。① **寻址**:命中不了 `byAgent` 卡(没建卡,或卡被显式关掉)时一律落 `default`。于是 `default` 卡开着时,宿主里跑的外部 bot(自带身份与设定)每一帧都会收到一份「你的人格名字:…」,两句「你是谁」撞在同一轮;而 `agent_scope` 是模型可给的参数,不构成边界。这条也让界面上「关掉这张卡」实际不生效——关掉的卡照样从 `default` 拿回一份人格。新增 **opt-in** 键 `personaStrictAddressing`(默认 `false`,历史行为逐字节不变):打开后这两种情况都不注入人格,`default` 退回它的本意——只兜**这一帧解析不到会话身份**的无名会话。不默认改是因为它是口径变更,老用户升级后不许有人格静默消失。② **同源**:`lib/client.js` 那段「Agent 眼中的你(注入生效文本)」原本是前端自己拼的(只有 profile + rules 两条列表,没有 `[用户设置]` 表头、没有「- 用户画像:」前缀、**完全没有人格三行**,注释还写着「零后端新请求」),标题却声称是模型实际看到的读数。渲染整体搬到 `src/user-settings.js`(`escapePromptVars` / `resolvePersonaCard` / `renderUserSettingsBlock`),注入器与预览共用这一份;`inject.js` 的 `renderUserSettings` 只剩一行委托,转义闭包改为委托同一实现(其余调用点不动)。`GET /api/dsh-mneme/persona` 因此多出 `preview`(`text` / `source` / `agent` / `configGate`),并支持 `?agent=<身份>` 逐张卡预览;面板只显示后端给的文本,读失败时 fail-closed 显示「未知」,绝不回退到前端拼的版本——预览里也不出现实际不会注入的人格行。 预览请求带序号,过期响应直接丢弃(先切卡 A 再切卡 B 时 A 的 GET 可能后回来,而这张卡自称「生效文本」,显示另一张卡的文本比不显示更糟);`source: default` 还按 `agent` 分成两句话:「身份解析到了但没有可用的卡(落回默认卡)」与「这一帧解析不到身份」——前者正是「关掉这张卡为什么不生效」的答案。`settings.getPersonaFor` 改完后生产代码零调用点,一并删除,寻址只留一处。回归锁:strict 四例 + 一条现状锁、预览与注入器**逐字节同源**例、总闸关闭如实标注例、面板不许回到前端拼接例;变异自证两条(摘掉 strict 分支 → 4 红;预览忽略 `agent` 参数 → 3 红,含逐字节同源那条)。**刻意不做**:画像/规则的独立开关(比 persona 早,加闸影响所有用户)、「从记忆提取画像」(本仓无任何提取管道,那是新功能)、人格卡单选互斥(运行时每轮本只用一张卡,互斥会牺牲 v1.2 多卡的意义)。 +- **人格的 `default` 卡会进所有没建卡的身份,面板的「注入生效文本」又不是生效文本(#497)**:两件事同源——寻址与渲染各有一份实现。① **寻址**:命中不了 `byAgent` 卡(没建卡,或卡被显式关掉)时一律落 `default`。于是 `default` 卡开着时,宿主里跑的外部 bot(自带身份与设定)每一帧都会收到一份「你的人格名字:…」,两句「你是谁」撞在同一轮;而 `agent_scope` 是模型可给的参数,不构成边界。这条也让界面上「关掉这张卡」实际不生效——关掉的卡照样从 `default` 拿回一份人格。新增 **opt-in** 键 `personaStrictAddressing`(默认 `false`,历史行为逐字节不变):打开后这两种情况都不注入人格,`default` 退回它的本意——只兜**这一帧解析不到会话身份**的无名会话。不默认改是因为它是口径变更,老用户升级后不许有人格静默消失。② **同源**:`lib/client.js` 那段「Agent 眼中的你(注入生效文本)」原本是前端自己拼的(只有 profile + rules 两条列表,没有 `[用户设置]` 表头、没有「- 用户画像:」前缀、**完全没有人格三行**,注释还写着「零后端新请求」),标题却声称是模型实际看到的读数。渲染整体搬到 `src/user-settings.js`(`escapePromptVars` / `resolvePersonaCard` / `renderUserSettingsBlock`),注入器与预览共用这一份;`inject.js` 的 `renderUserSettings` 只剩一行委托,转义闭包改为委托同一实现(其余调用点不动)。`GET /api/dsh-mneme/persona` 因此多出 `preview`(`text` / `source` / `agent` / `configGate`),并支持 `?agent=<身份>` 逐张卡预览;面板只显示后端给的文本,读失败时 fail-closed 显示「未知」,绝不回退到前端拼的版本——预览里也不出现实际不会注入的人格行。 预览请求带序号,过期响应直接丢弃(先切卡 A 再切卡 B 时 A 的 GET 可能后回来,而这张卡自称「生效文本」,显示另一张卡的文本比不显示更糟);`source: default` 还按 `agent` 分成两句话:「身份解析到了但没有可用的卡(落回默认卡)」与「这一帧解析不到身份」——前者正是「关掉这张卡为什么不生效」的答案。`settings.getPersonaFor` 改完后生产代码零调用点,一并删除,寻址只留一处。回归锁:strict 四例 + 一条现状锁、预览与注入器**逐字节同源**例、总闸关闭如实标注例、面板不许回到前端拼接例;变异自证两条(摘掉 strict 分支 → 4 红;预览忽略 `agent` 参数 → 3 红,含逐字节同源那条)。**刻意不做**:画像/规则的独立开关(比 persona 早,加闸影响所有用户)、人格卡单选互斥(运行时每轮本只用一张卡,互斥会牺牲 v1.2 多卡的意义——要的是「未命中就不注入」那一格,已由 `personaStrictAddressing` 给出)。「从记忆提取画像」提出本条时确实还没有管道、当时列为不做,维护者随后指定要做,**已作为本版另一段落地**(见上方 `profileExtractEnabled`)。 - **蒸馏的转录被模型当成「该我回话了」:同一窗口确定性失败,重试 20 次、越试越贵(#493)**:转录以「用户:…」开头、**裸放**在一条 user 消息里(`src/summarize.js` 的 `collectMessages`),格式指令只在 system 里。模型于是把它当成当前发言接着往下说/点评,输出里连 `[` 都没有,`parseSummaryJsonResult` 在括号早退处直接判 `invalid summary JSON`;而解析失败不推进游标(语义是「这段还没提炼完,下次接着来」),下一个 `turn/end` 重试**同文同温**——同输入同错,窗口还随新事件继续变大。报告者的 20 条失败 input tokens 8194 → 11118 单调递增、`finish_reason = stop`,正是这条路径;本机注入恒定返回中文点评的假 stream 复现出 19 次额外调用、落库 0 条、游标不动。改法两条:①**围栏 + 角色重申**——user 消息现在以「你是第三方提炼员,不是下面这段会话的参与者」开头,转录包进 `<<<会话转录开始>>>` / `<<<会话转录结束>>>`;围栏是确定性的,重试收到的转录与首次逐字节相同(既有回归用例依赖这条);system 保持原样,因为模型对「这段文本是什么」的判断由它所在的 user 消息决定,只写进 system 救不回来(实测同段转录只加 system 约束仍 0/2 出 JSON)。②**失败护栏**(新键 `summarizeParseFailureMaxRetries`,默认 `0` = 关):连续失败到阈值后强制消费游标跳过该窗口,避免一条会话被同一窗口卡死。默认关是因为放弃内容本身有代价——短窗口偶发失败仍值得重试。 - **解析失败行的审计 `metadata` 恒为 `null`,事后无法回答「模型到底吐了什么形状」**:成功路径才给 `metadata` 赋值,失败早退分支从不赋值——90 条历史 `invalid summary JSON` 一条原始输出都没留。现在失败行恒带 `distillChars` / `parse_failure` / `raw_len` / `raw_head`(截断 300 字符)/ `raw_has_bracket` / `streak`。`raw_has_bracket` 是区分「模型没按格式写」与「格式对但内容救不回来」的唯一低成本判据,也正是 #493 现场的关键特征。只加计量、不加跳过决策(同 #239 的 `distillChars` 取舍)。**刻意不改的两处**:`llm_audit_logs.session_key` 仍为 NULL(`test/pre-inject-gate.test.js:155` 显式锁着「LLM 调用行不写会话键」,补上会改动 #254 写入准入的话题回填行为);不显式传 `temperature`(温度由宿主/路由决定,插件覆盖会改变所有用户的输出分布)。 diff --git a/dsh-mneme/README.md b/dsh-mneme/README.md index 3204058..d11f8d9 100644 --- a/dsh-mneme/README.md +++ b/dsh-mneme/README.md @@ -5,7 +5,7 @@ [![npm version](https://img.shields.io/npm/v/@modusensus/dsh-mneme?color=blue&label=npm)](https://www.npmjs.com/package/@modusensus/dsh-mneme) [![license](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![Awesome](https://awesome-dsh-plugin.com/badge.svg)](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) -[![tests](https://img.shields.io/badge/tests-2257%20passed-success)](https://github.com/slow-stack/mneme) +[![tests](https://img.shields.io/badge/tests-2302%20passed-success)](https://github.com/slow-stack/mneme) [![CI](https://img.shields.io/github/actions/workflow/status/slow-stack/mneme/ci.yml)](https://github.com/slow-stack/mneme/actions) [![node](https://img.shields.io/badge/node-22%2B-blue)](https://nodejs.org) [![npm downloads](https://img.shields.io/npm/d18m/@modusensus/dsh-mneme.svg?color=blue&label=downloads)](https://www.npmjs.com/package/@modusensus/dsh-mneme) @@ -275,6 +275,8 @@ v0.3.0 起新增**记忆基因**层:从记忆里抽取**命名实体**、**带 | **v0.9.1**(2026-10-18) | M.O. 从「看得见」跑到「跑得出账」:执行窗口由 `/mo-run start` / `/mo-run stop` 显式划开、运行账落 `<项目>/.mneme/pipelines/.runs.jsonl`(账本坏了与从没跑过严格分开)、归属只出候选、观察旁挂与脱节检出、步骤 `assert` / `optional`、R1/R2 提议;沉淀链从真实会话日志挖 draft 工作流(判据恒 `null`,答案只能由人补);人格定义页 v1 → v1.2(按 agent 寻址多卡,默认关);面板、向量与检查更新的一圈修复(#448 / #450 / #460);Release 正文改分层渲染 | | **v0.9.2**(2026-10-19) | 让「看不见的失败」显形:功能开关的第三态「待重启」把「面板显示已改 ≠ 运行时已改」摆到界面上(#481);作用域级可逆闸门让一路记忆能被单独按下与松开、不必改配置重启(#471,默认关);巩固用的 `dreamMaxTokens` 默认值撞服务商上限、八天 180 次全失败,面板现在能改、超上限还会自动降级重试(#478);CLI 把 `--` 开头的值当新旗标的真实缺陷(一次 1/4096 概率的 CI 红揪出来的)、人格卡删除 × 永远不可见、自动蒸馏写入的行 100% 拿不到向量(#477) | | **v0.9.3**(2026-10-20) | M.O. 从「挖得出来」到「会被叫去挖」:沉淀链算法自 v0.9.1 起就完整实现(多帧 zstd → 重复序列 → 缺席对照 → draft 草稿),运行时却没有任何调用点——机器挖得出来、却永远不会被叫去挖。这一版把归属与触发接上:按会话头 `cwd` 反查项目桶(桶名是路径的有损变形,查不到整跑跳过而不是降级成全局普查)、挂 `turn/end`、冷却 10 分钟、单次预算 4 秒、延到宏任务、一次一个项目、抛错只 `warn`,**只产提议不产草稿**(`actionNudgeEnabled`,beta,默认关);面板加「现在沉淀一次」,与自动跑同一条入口,区别只有它不改仓库;另修作用域反查自 v0.8.0 起每次都抛错走降级(#487)、`/nudge` 三处「没看全」被报成「确实没有」(#489 评审)、多帧 zstd 反证断言绑了 Node 整块解语义(#490),并把整个 M.O. 的「手法」改回「工作流」 | +| **v0.9.4**(2026-10-21) | 三条链各自补上「最后一环」——东西早就造好了,只差没人能叫它:**agent 主动整理**(`memory_organize`,先 dryRun 出比对报告 → agent 判断 → apply 落库,筛除只归档不删、全程留审计回执;`organizeEnabled` 默认关,关时工具根本不注册);**画像提取草稿**(面板按钮读活跃记忆 + 一次模型调用,产一段「助手眼中的用户」草稿,`profileExtractEnabled` 默认关)——**只出草稿**,采用是用户再点一次,手写的画像原文不会被模型改写,空库与解析失败都直接报错、绝不回空草稿冒充「没提炼出东西」;**人格寻址收口**(`default` 卡以前无条件落给所有没建卡的身份,宿主里自带身份的 bot 每帧收到两句「你是谁」;`personaStrictAddressing` 默认关,把「没建卡」与「卡被显式关掉」都收回为不注入)。同版补蒸馏的**转录围栏 + 解析失败护栏**(#494:一条会话被同一窗口卡死、重试 20 次越试越贵的根因;失败行现在恒带原始输出形状),把面板自称「注入生效文本」的只读区改成后端单源渲染(并修它从不显示人格行),评审另收掉两处「坏了却不报」:整理报告侧的存在性探测口(`strictScope` 下按调用方可见性过滤落点并重算计数)、画像保存不判 `res.ok` 却提示已保存 | + | **v0.8.x** | 作用域隔离(agent / workspace 双维 + opt-in 硬过滤)、冲突队列人工裁决、生态化(stdio MCP server / 图召回轴 / 冷启动 / 注入截断) | ## 📦 安装 @@ -677,7 +679,7 @@ src/ ├── api.js # HTTP 路由(Web 面板数据通道,含 /conflicts 冲突队列) └── index.js # 插件接线 lib/ # src 的同步分发产物(npm run sync,prepack 钩子在发布时自动跑;一致性由 scripts/check-sync.js 在 CI 与 release.yml 校验——它只查不同步;唯一手写例外 lib/client.js——Web 面板 bundle,sync 不覆盖) -test/ # 2257 个 node:test 测试(审计与三轴线压测不变量;src↔lib 一致性由 scripts/check-sync.js 发布闸门校验) +test/ # 2302 个 node:test 测试(审计与三轴线压测不变量;src↔lib 一致性由 scripts/check-sync.js 发布闸门校验) scripts/ # e2e-dsh.js 端到端演示 · stress-dsh.js 三轴线压测 · sync-lib.js 同步 · check-sync.js 发布闸门 · benchmark-recall.js / benchmark-embed.js / benchmark-rerank.js 基准 · sync-test-badge.mjs 测试徽章 · build-runtime-manifest.mjs 运行时清单 ``` @@ -686,7 +688,7 @@ scripts/ # e2e-dsh.js 端到端演示 · stress-dsh.js 三轴线压 ```bash cd dsh-mneme npm install # 安装 peer 依赖(以 devDependencies 形式,用于本地测试) -npm test # 运行 2257 个测试 +npm test # 运行 2302 个测试 npm run stress # 三轴线压测:长会话检索 / 冲突仲裁 / 多 Agent 并发(离线 mock LLM) npm run sync # 把 src/ 同步到 lib/(发布时由 prepack 钩子自动执行) ``` diff --git a/dsh-mneme/package-lock.json b/dsh-mneme/package-lock.json index 4f7d8ad..0f35743 100644 --- a/dsh-mneme/package-lock.json +++ b/dsh-mneme/package-lock.json @@ -1,12 +1,12 @@ { "name": "@modusensus/dsh-mneme", - "version": "0.9.3", + "version": "0.9.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@modusensus/dsh-mneme", - "version": "0.9.3", + "version": "0.9.4", "license": "MIT", "dependencies": { "morphicons": "^1.7.1" diff --git a/dsh-mneme/package.json b/dsh-mneme/package.json index 8cf72d4..c0e3c2b 100644 --- a/dsh-mneme/package.json +++ b/dsh-mneme/package.json @@ -1,7 +1,7 @@ { "name": "@modusensus/dsh-mneme", "description": "Cross-session memory plugin for DeepSeek Harness with autoDream consolidation: SQLite store, Markdown mirrors, 10 model tools, automatic injection, session summarization, user profile/rules, custom slash commands, vector (semantic) search, and a Web GUI panel", - "version": "0.9.3", + "version": "0.9.4", "license": "MIT", "repository": { "type": "git",